Added support for corner, row and column headers renderers to wxGrid.
[wxWidgets.git] / src / generic / grid.cpp
1 ///////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/grid.cpp
3 // Purpose: wxGrid and related classes
4 // Author: Michael Bedward (based on code by Julian Smart, Robin Dunn)
5 // Modified by: Robin Dunn, Vadim Zeitlin, Santiago Palacios
6 // Created: 1/08/1999
7 // RCS-ID: $Id$
8 // Copyright: (c) Michael Bedward (mbedward@ozemail.com.au)
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 TODO:
14
15 - Replace use of wxINVERT with wxOverlay
16 - Make Begin/EndBatch() the same as the generic Freeze/Thaw()
17 - Review the column reordering code, it's a mess.
18 - Implement row reordering after dealing with the columns.
19 */
20
21 // For compilers that support precompilation, includes "wx/wx.h".
22 #include "wx/wxprec.h"
23
24 #ifdef __BORLANDC__
25 #pragma hdrstop
26 #endif
27
28 #if wxUSE_GRID
29
30 #include "wx/grid.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/utils.h"
34 #include "wx/dcclient.h"
35 #include "wx/settings.h"
36 #include "wx/log.h"
37 #include "wx/textctrl.h"
38 #include "wx/checkbox.h"
39 #include "wx/combobox.h"
40 #include "wx/valtext.h"
41 #include "wx/intl.h"
42 #include "wx/math.h"
43 #include "wx/listbox.h"
44 #endif
45
46 #include "wx/textfile.h"
47 #include "wx/spinctrl.h"
48 #include "wx/tokenzr.h"
49 #include "wx/renderer.h"
50 #include "wx/headerctrl.h"
51 #include "wx/hashset.h"
52
53 #include "wx/generic/gridsel.h"
54 #include "wx/generic/gridctrl.h"
55 #include "wx/generic/grideditors.h"
56 #include "wx/generic/private/grid.h"
57
58 const char wxGridNameStr[] = "grid";
59
60 #if defined(__WXMOTIF__)
61 #define WXUNUSED_MOTIF(identifier) WXUNUSED(identifier)
62 #else
63 #define WXUNUSED_MOTIF(identifier) identifier
64 #endif
65
66 #if defined(__WXGTK__)
67 #define WXUNUSED_GTK(identifier) WXUNUSED(identifier)
68 #else
69 #define WXUNUSED_GTK(identifier) identifier
70 #endif
71
72 // Required for wxIs... functions
73 #include <ctype.h>
74
75 WX_DECLARE_HASH_SET_WITH_DECL(int, wxIntegerHash, wxIntegerEqual,
76 wxGridFixedIndicesSet, class WXDLLIMPEXP_ADV);
77
78
79 // ----------------------------------------------------------------------------
80 // globals
81 // ----------------------------------------------------------------------------
82
83 //#define DEBUG_ATTR_CACHE
84 #ifdef DEBUG_ATTR_CACHE
85 static size_t gs_nAttrCacheHits = 0;
86 static size_t gs_nAttrCacheMisses = 0;
87 #endif
88
89 // ----------------------------------------------------------------------------
90 // constants
91 // ----------------------------------------------------------------------------
92
93 wxGridCellCoords wxGridNoCellCoords( -1, -1 );
94 wxRect wxGridNoCellRect( -1, -1, -1, -1 );
95
96 namespace
97 {
98
99 // scroll line size
100 const size_t GRID_SCROLL_LINE_X = 15;
101 const size_t GRID_SCROLL_LINE_Y = GRID_SCROLL_LINE_X;
102
103 // the size of hash tables used a bit everywhere (the max number of elements
104 // in these hash tables is the number of rows/columns)
105 const int GRID_HASH_SIZE = 100;
106
107 // the minimal distance in pixels the mouse needs to move to start a drag
108 // operation
109 const int DRAG_SENSITIVITY = 3;
110
111 } // anonymous namespace
112
113 #include "wx/arrimpl.cpp"
114
115 WX_DEFINE_OBJARRAY(wxGridCellCoordsArray)
116 WX_DEFINE_OBJARRAY(wxGridCellWithAttrArray)
117
118 // ----------------------------------------------------------------------------
119 // events
120 // ----------------------------------------------------------------------------
121
122 wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_CLICK, wxGridEvent );
123 wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEvent );
124 wxDEFINE_EVENT( wxEVT_GRID_CELL_LEFT_DCLICK, wxGridEvent );
125 wxDEFINE_EVENT( wxEVT_GRID_CELL_RIGHT_DCLICK, wxGridEvent );
126 wxDEFINE_EVENT( wxEVT_GRID_CELL_BEGIN_DRAG, wxGridEvent );
127 wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_CLICK, wxGridEvent );
128 wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_CLICK, wxGridEvent );
129 wxDEFINE_EVENT( wxEVT_GRID_LABEL_LEFT_DCLICK, wxGridEvent );
130 wxDEFINE_EVENT( wxEVT_GRID_LABEL_RIGHT_DCLICK, wxGridEvent );
131 wxDEFINE_EVENT( wxEVT_GRID_ROW_SIZE, wxGridSizeEvent );
132 wxDEFINE_EVENT( wxEVT_GRID_COL_SIZE, wxGridSizeEvent );
133 wxDEFINE_EVENT( wxEVT_GRID_COL_MOVE, wxGridEvent );
134 wxDEFINE_EVENT( wxEVT_GRID_COL_SORT, wxGridEvent );
135 wxDEFINE_EVENT( wxEVT_GRID_RANGE_SELECT, wxGridRangeSelectEvent );
136 wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGING, wxGridEvent );
137 wxDEFINE_EVENT( wxEVT_GRID_CELL_CHANGED, wxGridEvent );
138 wxDEFINE_EVENT( wxEVT_GRID_SELECT_CELL, wxGridEvent );
139 wxDEFINE_EVENT( wxEVT_GRID_EDITOR_SHOWN, wxGridEvent );
140 wxDEFINE_EVENT( wxEVT_GRID_EDITOR_HIDDEN, wxGridEvent );
141 wxDEFINE_EVENT( wxEVT_GRID_EDITOR_CREATED, wxGridEditorCreatedEvent );
142
143 // ============================================================================
144 // implementation
145 // ============================================================================
146
147 IMPLEMENT_ABSTRACT_CLASS(wxGridCellEditorEvtHandler, wxEvtHandler)
148
149 BEGIN_EVENT_TABLE( wxGridCellEditorEvtHandler, wxEvtHandler )
150 EVT_KILL_FOCUS( wxGridCellEditorEvtHandler::OnKillFocus )
151 EVT_KEY_DOWN( wxGridCellEditorEvtHandler::OnKeyDown )
152 EVT_CHAR( wxGridCellEditorEvtHandler::OnChar )
153 END_EVENT_TABLE()
154
155 BEGIN_EVENT_TABLE(wxGridHeaderCtrl, wxHeaderCtrl)
156 EVT_HEADER_CLICK(wxID_ANY, wxGridHeaderCtrl::OnClick)
157
158 EVT_HEADER_BEGIN_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnBeginResize)
159 EVT_HEADER_RESIZING(wxID_ANY, wxGridHeaderCtrl::OnResizing)
160 EVT_HEADER_END_RESIZE(wxID_ANY, wxGridHeaderCtrl::OnEndResize)
161
162 EVT_HEADER_BEGIN_REORDER(wxID_ANY, wxGridHeaderCtrl::OnBeginReorder)
163 EVT_HEADER_END_REORDER(wxID_ANY, wxGridHeaderCtrl::OnEndReorder)
164 END_EVENT_TABLE()
165
166 wxGridOperations& wxGridRowOperations::Dual() const
167 {
168 static wxGridColumnOperations s_colOper;
169
170 return s_colOper;
171 }
172
173 wxGridOperations& wxGridColumnOperations::Dual() const
174 {
175 static wxGridRowOperations s_rowOper;
176
177 return s_rowOper;
178 }
179
180 // ----------------------------------------------------------------------------
181 // wxGridCellWorker is an (almost) empty common base class for
182 // wxGridCellRenderer and wxGridCellEditor managing ref counting
183 // ----------------------------------------------------------------------------
184
185 void wxGridCellWorker::SetParameters(const wxString& WXUNUSED(params))
186 {
187 // nothing to do
188 }
189
190 wxGridCellWorker::~wxGridCellWorker()
191 {
192 }
193
194 // ----------------------------------------------------------------------------
195 // wxGridHeaderLabelsRenderer and related classes
196 // ----------------------------------------------------------------------------
197
198 void wxGridHeaderLabelsRenderer::DrawLabel(const wxGrid& grid,
199 wxDC& dc,
200 const wxString& value,
201 const wxRect& rect,
202 int horizAlign,
203 int vertAlign,
204 int textOrientation) const
205 {
206 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
207 dc.SetTextForeground(grid.GetLabelTextColour());
208 dc.SetFont(grid.GetLabelFont());
209 grid.DrawTextRectangle(dc, value, rect, horizAlign, vertAlign, textOrientation);
210 }
211
212
213 void wxGridRowHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid),
214 wxDC& dc,
215 wxRect& rect) const
216 {
217 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
218 dc.DrawLine(rect.GetRight(), rect.GetTop(),
219 rect.GetRight(), rect.GetBottom());
220 dc.DrawLine(rect.GetLeft(), rect.GetTop(),
221 rect.GetLeft(), rect.GetBottom());
222 dc.DrawLine(rect.GetLeft(), rect.GetBottom(),
223 rect.GetRight() + 1, rect.GetBottom());
224
225 dc.SetPen(*wxWHITE_PEN);
226 dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(),
227 rect.GetLeft() + 1, rect.GetBottom());
228 dc.DrawLine(rect.GetLeft() + 1, rect.GetTop(),
229 rect.GetRight(), rect.GetTop());
230
231 rect.Deflate(2);
232 }
233
234 void wxGridColumnHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid),
235 wxDC& dc,
236 wxRect& rect) const
237 {
238 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
239 dc.DrawLine(rect.GetRight(), rect.GetTop(),
240 rect.GetRight(), rect.GetBottom());
241 dc.DrawLine(rect.GetLeft(), rect.GetTop(),
242 rect.GetRight(), rect.GetTop());
243 dc.DrawLine(rect.GetLeft(), rect.GetBottom(),
244 rect.GetRight() + 1, rect.GetBottom());
245
246 dc.SetPen(*wxWHITE_PEN);
247 dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1,
248 rect.GetLeft(), rect.GetBottom());
249 dc.DrawLine(rect.GetLeft(), rect.GetTop() + 1,
250 rect.GetRight(), rect.GetTop() + 1);
251
252 rect.Deflate(2);
253 }
254
255 void wxGridCornerHeaderRendererDefault::DrawBorder(const wxGrid& WXUNUSED(grid),
256 wxDC& dc,
257 wxRect& rect) const
258 {
259 dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW)));
260 dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1,
261 rect.GetRight() - 1, rect.GetTop());
262 dc.DrawLine(rect.GetRight() - 1, rect.GetBottom() - 1,
263 rect.GetLeft(), rect.GetBottom() - 1);
264 dc.DrawLine(rect.GetLeft(), rect.GetTop(),
265 rect.GetRight(), rect.GetTop());
266 dc.DrawLine(rect.GetLeft(), rect.GetTop(),
267 rect.GetLeft(), rect.GetBottom());
268
269 dc.SetPen(*wxWHITE_PEN);
270 dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1,
271 rect.GetRight() - 1, rect.GetTop() + 1);
272 dc.DrawLine(rect.GetLeft() + 1, rect.GetTop() + 1,
273 rect.GetLeft() + 1, rect.GetBottom() - 1);
274
275 rect.Deflate(2);
276 }
277
278 // ----------------------------------------------------------------------------
279 // wxGridCellAttr
280 // ----------------------------------------------------------------------------
281
282 void wxGridCellAttr::Init(wxGridCellAttr *attrDefault)
283 {
284 m_isReadOnly = Unset;
285
286 m_renderer = NULL;
287 m_editor = NULL;
288
289 m_attrkind = wxGridCellAttr::Cell;
290
291 m_sizeRows = m_sizeCols = 1;
292 m_overflow = UnsetOverflow;
293
294 SetDefAttr(attrDefault);
295 }
296
297 wxGridCellAttr *wxGridCellAttr::Clone() const
298 {
299 wxGridCellAttr *attr = new wxGridCellAttr(m_defGridAttr);
300
301 if ( HasTextColour() )
302 attr->SetTextColour(GetTextColour());
303 if ( HasBackgroundColour() )
304 attr->SetBackgroundColour(GetBackgroundColour());
305 if ( HasFont() )
306 attr->SetFont(GetFont());
307 if ( HasAlignment() )
308 attr->SetAlignment(m_hAlign, m_vAlign);
309
310 attr->SetSize( m_sizeRows, m_sizeCols );
311
312 if ( m_renderer )
313 {
314 attr->SetRenderer(m_renderer);
315 m_renderer->IncRef();
316 }
317 if ( m_editor )
318 {
319 attr->SetEditor(m_editor);
320 m_editor->IncRef();
321 }
322
323 if ( IsReadOnly() )
324 attr->SetReadOnly();
325
326 attr->SetOverflow( m_overflow == Overflow );
327 attr->SetKind( m_attrkind );
328
329 return attr;
330 }
331
332 void wxGridCellAttr::MergeWith(wxGridCellAttr *mergefrom)
333 {
334 if ( !HasTextColour() && mergefrom->HasTextColour() )
335 SetTextColour(mergefrom->GetTextColour());
336 if ( !HasBackgroundColour() && mergefrom->HasBackgroundColour() )
337 SetBackgroundColour(mergefrom->GetBackgroundColour());
338 if ( !HasFont() && mergefrom->HasFont() )
339 SetFont(mergefrom->GetFont());
340 if ( !HasAlignment() && mergefrom->HasAlignment() )
341 {
342 int hAlign, vAlign;
343 mergefrom->GetAlignment( &hAlign, &vAlign);
344 SetAlignment(hAlign, vAlign);
345 }
346 if ( !HasSize() && mergefrom->HasSize() )
347 mergefrom->GetSize( &m_sizeRows, &m_sizeCols );
348
349 // Directly access member functions as GetRender/Editor don't just return
350 // m_renderer/m_editor
351 //
352 // Maybe add support for merge of Render and Editor?
353 if (!HasRenderer() && mergefrom->HasRenderer() )
354 {
355 m_renderer = mergefrom->m_renderer;
356 m_renderer->IncRef();
357 }
358 if ( !HasEditor() && mergefrom->HasEditor() )
359 {
360 m_editor = mergefrom->m_editor;
361 m_editor->IncRef();
362 }
363 if ( !HasReadWriteMode() && mergefrom->HasReadWriteMode() )
364 SetReadOnly(mergefrom->IsReadOnly());
365
366 if (!HasOverflowMode() && mergefrom->HasOverflowMode() )
367 SetOverflow(mergefrom->GetOverflow());
368
369 SetDefAttr(mergefrom->m_defGridAttr);
370 }
371
372 void wxGridCellAttr::SetSize(int num_rows, int num_cols)
373 {
374 // The size of a cell is normally 1,1
375
376 // If this cell is larger (2,2) then this is the top left cell
377 // the other cells that will be covered (lower right cells) must be
378 // set to negative or zero values such that
379 // row + num_rows of the covered cell points to the larger cell (this cell)
380 // same goes for the col + num_cols.
381
382 // Size of 0,0 is NOT valid, neither is <=0 and any positive value
383
384 wxASSERT_MSG( (!((num_rows > 0) && (num_cols <= 0)) ||
385 !((num_rows <= 0) && (num_cols > 0)) ||
386 !((num_rows == 0) && (num_cols == 0))),
387 wxT("wxGridCellAttr::SetSize only takes two postive values or negative/zero values"));
388
389 m_sizeRows = num_rows;
390 m_sizeCols = num_cols;
391 }
392
393 const wxColour& wxGridCellAttr::GetTextColour() const
394 {
395 if (HasTextColour())
396 {
397 return m_colText;
398 }
399 else if (m_defGridAttr && m_defGridAttr != this)
400 {
401 return m_defGridAttr->GetTextColour();
402 }
403 else
404 {
405 wxFAIL_MSG(wxT("Missing default cell attribute"));
406 return wxNullColour;
407 }
408 }
409
410 const wxColour& wxGridCellAttr::GetBackgroundColour() const
411 {
412 if (HasBackgroundColour())
413 {
414 return m_colBack;
415 }
416 else if (m_defGridAttr && m_defGridAttr != this)
417 {
418 return m_defGridAttr->GetBackgroundColour();
419 }
420 else
421 {
422 wxFAIL_MSG(wxT("Missing default cell attribute"));
423 return wxNullColour;
424 }
425 }
426
427 const wxFont& wxGridCellAttr::GetFont() const
428 {
429 if (HasFont())
430 {
431 return m_font;
432 }
433 else if (m_defGridAttr && m_defGridAttr != this)
434 {
435 return m_defGridAttr->GetFont();
436 }
437 else
438 {
439 wxFAIL_MSG(wxT("Missing default cell attribute"));
440 return wxNullFont;
441 }
442 }
443
444 void wxGridCellAttr::GetAlignment(int *hAlign, int *vAlign) const
445 {
446 if (HasAlignment())
447 {
448 if ( hAlign )
449 *hAlign = m_hAlign;
450 if ( vAlign )
451 *vAlign = m_vAlign;
452 }
453 else if (m_defGridAttr && m_defGridAttr != this)
454 {
455 m_defGridAttr->GetAlignment(hAlign, vAlign);
456 }
457 else
458 {
459 wxFAIL_MSG(wxT("Missing default cell attribute"));
460 }
461 }
462
463 void wxGridCellAttr::GetSize( int *num_rows, int *num_cols ) const
464 {
465 if ( num_rows )
466 *num_rows = m_sizeRows;
467 if ( num_cols )
468 *num_cols = m_sizeCols;
469 }
470
471 // GetRenderer and GetEditor use a slightly different decision path about
472 // which attribute to use. If a non-default attr object has one then it is
473 // used, otherwise the default editor or renderer is fetched from the grid and
474 // used. It should be the default for the data type of the cell. If it is
475 // NULL (because the table has a type that the grid does not have in its
476 // registry), then the grid's default editor or renderer is used.
477
478 wxGridCellRenderer* wxGridCellAttr::GetRenderer(const wxGrid* grid, int row, int col) const
479 {
480 wxGridCellRenderer *renderer = NULL;
481
482 if ( m_renderer && this != m_defGridAttr )
483 {
484 // use the cells renderer if it has one
485 renderer = m_renderer;
486 renderer->IncRef();
487 }
488 else // no non-default cell renderer
489 {
490 // get default renderer for the data type
491 if ( grid )
492 {
493 // GetDefaultRendererForCell() will do IncRef() for us
494 renderer = grid->GetDefaultRendererForCell(row, col);
495 }
496
497 if ( renderer == NULL )
498 {
499 if ( (m_defGridAttr != NULL) && (m_defGridAttr != this) )
500 {
501 // if we still don't have one then use the grid default
502 // (no need for IncRef() here neither)
503 renderer = m_defGridAttr->GetRenderer(NULL, 0, 0);
504 }
505 else // default grid attr
506 {
507 // use m_renderer which we had decided not to use initially
508 renderer = m_renderer;
509 if ( renderer )
510 renderer->IncRef();
511 }
512 }
513 }
514
515 // we're supposed to always find something
516 wxASSERT_MSG(renderer, wxT("Missing default cell renderer"));
517
518 return renderer;
519 }
520
521 // same as above, except for s/renderer/editor/g
522 wxGridCellEditor* wxGridCellAttr::GetEditor(const wxGrid* grid, int row, int col) const
523 {
524 wxGridCellEditor *editor = NULL;
525
526 if ( m_editor && this != m_defGridAttr )
527 {
528 // use the cells editor if it has one
529 editor = m_editor;
530 editor->IncRef();
531 }
532 else // no non default cell editor
533 {
534 // get default editor for the data type
535 if ( grid )
536 {
537 // GetDefaultEditorForCell() will do IncRef() for us
538 editor = grid->GetDefaultEditorForCell(row, col);
539 }
540
541 if ( editor == NULL )
542 {
543 if ( (m_defGridAttr != NULL) && (m_defGridAttr != this) )
544 {
545 // if we still don't have one then use the grid default
546 // (no need for IncRef() here neither)
547 editor = m_defGridAttr->GetEditor(NULL, 0, 0);
548 }
549 else // default grid attr
550 {
551 // use m_editor which we had decided not to use initially
552 editor = m_editor;
553 if ( editor )
554 editor->IncRef();
555 }
556 }
557 }
558
559 // we're supposed to always find something
560 wxASSERT_MSG(editor, wxT("Missing default cell editor"));
561
562 return editor;
563 }
564
565 // ----------------------------------------------------------------------------
566 // wxGridCellAttrData
567 // ----------------------------------------------------------------------------
568
569 void wxGridCellAttrData::SetAttr(wxGridCellAttr *attr, int row, int col)
570 {
571 // Note: contrary to wxGridRowOrColAttrData::SetAttr, we must not
572 // touch attribute's reference counting explicitly, since this
573 // is managed by class wxGridCellWithAttr
574 int n = FindIndex(row, col);
575 if ( n == wxNOT_FOUND )
576 {
577 if ( attr )
578 {
579 // add the attribute
580 m_attrs.Add(new wxGridCellWithAttr(row, col, attr));
581 }
582 //else: nothing to do
583 }
584 else // we already have an attribute for this cell
585 {
586 if ( attr )
587 {
588 // change the attribute
589 m_attrs[(size_t)n].ChangeAttr(attr);
590 }
591 else
592 {
593 // remove this attribute
594 m_attrs.RemoveAt((size_t)n);
595 }
596 }
597 }
598
599 wxGridCellAttr *wxGridCellAttrData::GetAttr(int row, int col) const
600 {
601 wxGridCellAttr *attr = NULL;
602
603 int n = FindIndex(row, col);
604 if ( n != wxNOT_FOUND )
605 {
606 attr = m_attrs[(size_t)n].attr;
607 attr->IncRef();
608 }
609
610 return attr;
611 }
612
613 void wxGridCellAttrData::UpdateAttrRows( size_t pos, int numRows )
614 {
615 size_t count = m_attrs.GetCount();
616 for ( size_t n = 0; n < count; n++ )
617 {
618 wxGridCellCoords& coords = m_attrs[n].coords;
619 wxCoord row = coords.GetRow();
620 if ((size_t)row >= pos)
621 {
622 if (numRows > 0)
623 {
624 // If rows inserted, include row counter where necessary
625 coords.SetRow(row + numRows);
626 }
627 else if (numRows < 0)
628 {
629 // If rows deleted ...
630 if ((size_t)row >= pos - numRows)
631 {
632 // ...either decrement row counter (if row still exists)...
633 coords.SetRow(row + numRows);
634 }
635 else
636 {
637 // ...or remove the attribute
638 m_attrs.RemoveAt(n);
639 n--;
640 count--;
641 }
642 }
643 }
644 }
645 }
646
647 void wxGridCellAttrData::UpdateAttrCols( size_t pos, int numCols )
648 {
649 size_t count = m_attrs.GetCount();
650 for ( size_t n = 0; n < count; n++ )
651 {
652 wxGridCellCoords& coords = m_attrs[n].coords;
653 wxCoord col = coords.GetCol();
654 if ( (size_t)col >= pos )
655 {
656 if ( numCols > 0 )
657 {
658 // If rows inserted, include row counter where necessary
659 coords.SetCol(col + numCols);
660 }
661 else if (numCols < 0)
662 {
663 // If rows deleted ...
664 if ((size_t)col >= pos - numCols)
665 {
666 // ...either decrement row counter (if row still exists)...
667 coords.SetCol(col + numCols);
668 }
669 else
670 {
671 // ...or remove the attribute
672 m_attrs.RemoveAt(n);
673 n--;
674 count--;
675 }
676 }
677 }
678 }
679 }
680
681 int wxGridCellAttrData::FindIndex(int row, int col) const
682 {
683 size_t count = m_attrs.GetCount();
684 for ( size_t n = 0; n < count; n++ )
685 {
686 const wxGridCellCoords& coords = m_attrs[n].coords;
687 if ( (coords.GetRow() == row) && (coords.GetCol() == col) )
688 {
689 return n;
690 }
691 }
692
693 return wxNOT_FOUND;
694 }
695
696 // ----------------------------------------------------------------------------
697 // wxGridRowOrColAttrData
698 // ----------------------------------------------------------------------------
699
700 wxGridRowOrColAttrData::~wxGridRowOrColAttrData()
701 {
702 size_t count = m_attrs.GetCount();
703 for ( size_t n = 0; n < count; n++ )
704 {
705 m_attrs[n]->DecRef();
706 }
707 }
708
709 wxGridCellAttr *wxGridRowOrColAttrData::GetAttr(int rowOrCol) const
710 {
711 wxGridCellAttr *attr = NULL;
712
713 int n = m_rowsOrCols.Index(rowOrCol);
714 if ( n != wxNOT_FOUND )
715 {
716 attr = m_attrs[(size_t)n];
717 attr->IncRef();
718 }
719
720 return attr;
721 }
722
723 void wxGridRowOrColAttrData::SetAttr(wxGridCellAttr *attr, int rowOrCol)
724 {
725 int i = m_rowsOrCols.Index(rowOrCol);
726 if ( i == wxNOT_FOUND )
727 {
728 if ( attr )
729 {
730 // store the new attribute, taking its ownership
731 m_rowsOrCols.Add(rowOrCol);
732 m_attrs.Add(attr);
733 }
734 // nothing to remove
735 }
736 else // we have an attribute for this row or column
737 {
738 size_t n = (size_t)i;
739
740 // notice that this code works correctly even when the old attribute is
741 // the same as the new one: as we own of it, we must call DecRef() on
742 // it in any case and this won't result in destruction of the new
743 // attribute if it's the same as old one because it must have ref count
744 // of at least 2 to be passed to us while we keep a reference to it too
745 m_attrs[n]->DecRef();
746
747 if ( attr )
748 {
749 // replace the attribute with the new one
750 m_attrs[n] = attr;
751 }
752 else // remove the attribute
753 {
754 m_rowsOrCols.RemoveAt(n);
755 m_attrs.RemoveAt(n);
756 }
757 }
758 }
759
760 void wxGridRowOrColAttrData::UpdateAttrRowsOrCols( size_t pos, int numRowsOrCols )
761 {
762 size_t count = m_attrs.GetCount();
763 for ( size_t n = 0; n < count; n++ )
764 {
765 int & rowOrCol = m_rowsOrCols[n];
766 if ( (size_t)rowOrCol >= pos )
767 {
768 if ( numRowsOrCols > 0 )
769 {
770 // If rows inserted, include row counter where necessary
771 rowOrCol += numRowsOrCols;
772 }
773 else if ( numRowsOrCols < 0)
774 {
775 // If rows deleted, either decrement row counter (if row still exists)
776 if ((size_t)rowOrCol >= pos - numRowsOrCols)
777 rowOrCol += numRowsOrCols;
778 else
779 {
780 m_rowsOrCols.RemoveAt(n);
781 m_attrs[n]->DecRef();
782 m_attrs.RemoveAt(n);
783 n--;
784 count--;
785 }
786 }
787 }
788 }
789 }
790
791 // ----------------------------------------------------------------------------
792 // wxGridCellAttrProvider
793 // ----------------------------------------------------------------------------
794
795 wxGridCellAttrProvider::wxGridCellAttrProvider()
796 {
797 m_data = NULL;
798 }
799
800 wxGridCellAttrProvider::~wxGridCellAttrProvider()
801 {
802 delete m_data;
803 }
804
805 void wxGridCellAttrProvider::InitData()
806 {
807 m_data = new wxGridCellAttrProviderData;
808 }
809
810 wxGridCellAttr *wxGridCellAttrProvider::GetAttr(int row, int col,
811 wxGridCellAttr::wxAttrKind kind ) const
812 {
813 wxGridCellAttr *attr = NULL;
814 if ( m_data )
815 {
816 switch (kind)
817 {
818 case (wxGridCellAttr::Any):
819 // Get cached merge attributes.
820 // Currently not used as no cache implemented as not mutable
821 // attr = m_data->m_mergeAttr.GetAttr(row, col);
822 if (!attr)
823 {
824 // Basically implement old version.
825 // Also check merge cache, so we don't have to re-merge every time..
826 wxGridCellAttr *attrcell = m_data->m_cellAttrs.GetAttr(row, col);
827 wxGridCellAttr *attrrow = m_data->m_rowAttrs.GetAttr(row);
828 wxGridCellAttr *attrcol = m_data->m_colAttrs.GetAttr(col);
829
830 if ((attrcell != attrrow) && (attrrow != attrcol) && (attrcell != attrcol))
831 {
832 // Two or more are non NULL
833 attr = new wxGridCellAttr;
834 attr->SetKind(wxGridCellAttr::Merged);
835
836 // Order is important..
837 if (attrcell)
838 {
839 attr->MergeWith(attrcell);
840 attrcell->DecRef();
841 }
842 if (attrcol)
843 {
844 attr->MergeWith(attrcol);
845 attrcol->DecRef();
846 }
847 if (attrrow)
848 {
849 attr->MergeWith(attrrow);
850 attrrow->DecRef();
851 }
852
853 // store merge attr if cache implemented
854 //attr->IncRef();
855 //m_data->m_mergeAttr.SetAttr(attr, row, col);
856 }
857 else
858 {
859 // one or none is non null return it or null.
860 if (attrrow)
861 attr = attrrow;
862 if (attrcol)
863 {
864 if (attr)
865 attr->DecRef();
866 attr = attrcol;
867 }
868 if (attrcell)
869 {
870 if (attr)
871 attr->DecRef();
872 attr = attrcell;
873 }
874 }
875 }
876 break;
877
878 case (wxGridCellAttr::Cell):
879 attr = m_data->m_cellAttrs.GetAttr(row, col);
880 break;
881
882 case (wxGridCellAttr::Col):
883 attr = m_data->m_colAttrs.GetAttr(col);
884 break;
885
886 case (wxGridCellAttr::Row):
887 attr = m_data->m_rowAttrs.GetAttr(row);
888 break;
889
890 default:
891 // unused as yet...
892 // (wxGridCellAttr::Default):
893 // (wxGridCellAttr::Merged):
894 break;
895 }
896 }
897
898 return attr;
899 }
900
901 void wxGridCellAttrProvider::SetAttr(wxGridCellAttr *attr,
902 int row, int col)
903 {
904 if ( !m_data )
905 InitData();
906
907 m_data->m_cellAttrs.SetAttr(attr, row, col);
908 }
909
910 void wxGridCellAttrProvider::SetRowAttr(wxGridCellAttr *attr, int row)
911 {
912 if ( !m_data )
913 InitData();
914
915 m_data->m_rowAttrs.SetAttr(attr, row);
916 }
917
918 void wxGridCellAttrProvider::SetColAttr(wxGridCellAttr *attr, int col)
919 {
920 if ( !m_data )
921 InitData();
922
923 m_data->m_colAttrs.SetAttr(attr, col);
924 }
925
926 void wxGridCellAttrProvider::UpdateAttrRows( size_t pos, int numRows )
927 {
928 if ( m_data )
929 {
930 m_data->m_cellAttrs.UpdateAttrRows( pos, numRows );
931
932 m_data->m_rowAttrs.UpdateAttrRowsOrCols( pos, numRows );
933 }
934 }
935
936 void wxGridCellAttrProvider::UpdateAttrCols( size_t pos, int numCols )
937 {
938 if ( m_data )
939 {
940 m_data->m_cellAttrs.UpdateAttrCols( pos, numCols );
941
942 m_data->m_colAttrs.UpdateAttrRowsOrCols( pos, numCols );
943 }
944 }
945
946 // ----------------------------------------------------------------------------
947 // wxGridTableBase
948 // ----------------------------------------------------------------------------
949
950 IMPLEMENT_ABSTRACT_CLASS( wxGridTableBase, wxObject )
951
952 wxGridTableBase::wxGridTableBase()
953 {
954 m_view = NULL;
955 m_attrProvider = NULL;
956 }
957
958 wxGridTableBase::~wxGridTableBase()
959 {
960 delete m_attrProvider;
961 }
962
963 void wxGridTableBase::SetAttrProvider(wxGridCellAttrProvider *attrProvider)
964 {
965 delete m_attrProvider;
966 m_attrProvider = attrProvider;
967 }
968
969 bool wxGridTableBase::CanHaveAttributes()
970 {
971 if ( ! GetAttrProvider() )
972 {
973 // use the default attr provider by default
974 SetAttrProvider(new wxGridCellAttrProvider);
975 }
976
977 return true;
978 }
979
980 wxGridCellAttr *wxGridTableBase::GetAttr(int row, int col, wxGridCellAttr::wxAttrKind kind)
981 {
982 if ( m_attrProvider )
983 return m_attrProvider->GetAttr(row, col, kind);
984 else
985 return NULL;
986 }
987
988 void wxGridTableBase::SetAttr(wxGridCellAttr* attr, int row, int col)
989 {
990 if ( m_attrProvider )
991 {
992 if ( attr )
993 attr->SetKind(wxGridCellAttr::Cell);
994 m_attrProvider->SetAttr(attr, row, col);
995 }
996 else
997 {
998 // as we take ownership of the pointer and don't store it, we must
999 // free it now
1000 wxSafeDecRef(attr);
1001 }
1002 }
1003
1004 void wxGridTableBase::SetRowAttr(wxGridCellAttr *attr, int row)
1005 {
1006 if ( m_attrProvider )
1007 {
1008 attr->SetKind(wxGridCellAttr::Row);
1009 m_attrProvider->SetRowAttr(attr, row);
1010 }
1011 else
1012 {
1013 // as we take ownership of the pointer and don't store it, we must
1014 // free it now
1015 wxSafeDecRef(attr);
1016 }
1017 }
1018
1019 void wxGridTableBase::SetColAttr(wxGridCellAttr *attr, int col)
1020 {
1021 if ( m_attrProvider )
1022 {
1023 attr->SetKind(wxGridCellAttr::Col);
1024 m_attrProvider->SetColAttr(attr, col);
1025 }
1026 else
1027 {
1028 // as we take ownership of the pointer and don't store it, we must
1029 // free it now
1030 wxSafeDecRef(attr);
1031 }
1032 }
1033
1034 bool wxGridTableBase::InsertRows( size_t WXUNUSED(pos),
1035 size_t WXUNUSED(numRows) )
1036 {
1037 wxFAIL_MSG( wxT("Called grid table class function InsertRows\nbut your derived table class does not override this function") );
1038
1039 return false;
1040 }
1041
1042 bool wxGridTableBase::AppendRows( size_t WXUNUSED(numRows) )
1043 {
1044 wxFAIL_MSG( wxT("Called grid table class function AppendRows\nbut your derived table class does not override this function"));
1045
1046 return false;
1047 }
1048
1049 bool wxGridTableBase::DeleteRows( size_t WXUNUSED(pos),
1050 size_t WXUNUSED(numRows) )
1051 {
1052 wxFAIL_MSG( wxT("Called grid table class function DeleteRows\nbut your derived table class does not override this function"));
1053
1054 return false;
1055 }
1056
1057 bool wxGridTableBase::InsertCols( size_t WXUNUSED(pos),
1058 size_t WXUNUSED(numCols) )
1059 {
1060 wxFAIL_MSG( wxT("Called grid table class function InsertCols\nbut your derived table class does not override this function"));
1061
1062 return false;
1063 }
1064
1065 bool wxGridTableBase::AppendCols( size_t WXUNUSED(numCols) )
1066 {
1067 wxFAIL_MSG(wxT("Called grid table class function AppendCols\nbut your derived table class does not override this function"));
1068
1069 return false;
1070 }
1071
1072 bool wxGridTableBase::DeleteCols( size_t WXUNUSED(pos),
1073 size_t WXUNUSED(numCols) )
1074 {
1075 wxFAIL_MSG( wxT("Called grid table class function DeleteCols\nbut your derived table class does not override this function"));
1076
1077 return false;
1078 }
1079
1080 wxString wxGridTableBase::GetRowLabelValue( int row )
1081 {
1082 wxString s;
1083
1084 // RD: Starting the rows at zero confuses users,
1085 // no matter how much it makes sense to us geeks.
1086 s << row + 1;
1087
1088 return s;
1089 }
1090
1091 wxString wxGridTableBase::GetColLabelValue( int col )
1092 {
1093 // default col labels are:
1094 // cols 0 to 25 : A-Z
1095 // cols 26 to 675 : AA-ZZ
1096 // etc.
1097
1098 wxString s;
1099 unsigned int i, n;
1100 for ( n = 1; ; n++ )
1101 {
1102 s += (wxChar) (wxT('A') + (wxChar)(col % 26));
1103 col = col / 26 - 1;
1104 if ( col < 0 )
1105 break;
1106 }
1107
1108 // reverse the string...
1109 wxString s2;
1110 for ( i = 0; i < n; i++ )
1111 {
1112 s2 += s[n - i - 1];
1113 }
1114
1115 return s2;
1116 }
1117
1118 wxString wxGridTableBase::GetTypeName( int WXUNUSED(row), int WXUNUSED(col) )
1119 {
1120 return wxGRID_VALUE_STRING;
1121 }
1122
1123 bool wxGridTableBase::CanGetValueAs( int WXUNUSED(row), int WXUNUSED(col),
1124 const wxString& typeName )
1125 {
1126 return typeName == wxGRID_VALUE_STRING;
1127 }
1128
1129 bool wxGridTableBase::CanSetValueAs( int row, int col, const wxString& typeName )
1130 {
1131 return CanGetValueAs(row, col, typeName);
1132 }
1133
1134 long wxGridTableBase::GetValueAsLong( int WXUNUSED(row), int WXUNUSED(col) )
1135 {
1136 return 0;
1137 }
1138
1139 double wxGridTableBase::GetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col) )
1140 {
1141 return 0.0;
1142 }
1143
1144 bool wxGridTableBase::GetValueAsBool( int WXUNUSED(row), int WXUNUSED(col) )
1145 {
1146 return false;
1147 }
1148
1149 void wxGridTableBase::SetValueAsLong( int WXUNUSED(row), int WXUNUSED(col),
1150 long WXUNUSED(value) )
1151 {
1152 }
1153
1154 void wxGridTableBase::SetValueAsDouble( int WXUNUSED(row), int WXUNUSED(col),
1155 double WXUNUSED(value) )
1156 {
1157 }
1158
1159 void wxGridTableBase::SetValueAsBool( int WXUNUSED(row), int WXUNUSED(col),
1160 bool WXUNUSED(value) )
1161 {
1162 }
1163
1164 void* wxGridTableBase::GetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
1165 const wxString& WXUNUSED(typeName) )
1166 {
1167 return NULL;
1168 }
1169
1170 void wxGridTableBase::SetValueAsCustom( int WXUNUSED(row), int WXUNUSED(col),
1171 const wxString& WXUNUSED(typeName),
1172 void* WXUNUSED(value) )
1173 {
1174 }
1175
1176 //////////////////////////////////////////////////////////////////////
1177 //
1178 // Message class for the grid table to send requests and notifications
1179 // to the grid view
1180 //
1181
1182 wxGridTableMessage::wxGridTableMessage()
1183 {
1184 m_table = NULL;
1185 m_id = -1;
1186 m_comInt1 = -1;
1187 m_comInt2 = -1;
1188 }
1189
1190 wxGridTableMessage::wxGridTableMessage( wxGridTableBase *table, int id,
1191 int commandInt1, int commandInt2 )
1192 {
1193 m_table = table;
1194 m_id = id;
1195 m_comInt1 = commandInt1;
1196 m_comInt2 = commandInt2;
1197 }
1198
1199 //////////////////////////////////////////////////////////////////////
1200 //
1201 // A basic grid table for string data. An object of this class will
1202 // created by wxGrid if you don't specify an alternative table class.
1203 //
1204
1205 WX_DEFINE_OBJARRAY(wxGridStringArray)
1206
1207 IMPLEMENT_DYNAMIC_CLASS( wxGridStringTable, wxGridTableBase )
1208
1209 wxGridStringTable::wxGridStringTable()
1210 : wxGridTableBase()
1211 {
1212 m_numCols = 0;
1213 }
1214
1215 wxGridStringTable::wxGridStringTable( int numRows, int numCols )
1216 : wxGridTableBase()
1217 {
1218 m_numCols = numCols;
1219
1220 m_data.Alloc( numRows );
1221
1222 wxArrayString sa;
1223 sa.Alloc( numCols );
1224 sa.Add( wxEmptyString, numCols );
1225
1226 m_data.Add( sa, numRows );
1227 }
1228
1229 wxString wxGridStringTable::GetValue( int row, int col )
1230 {
1231 wxCHECK_MSG( (row >= 0 && row < GetNumberRows()) &&
1232 (col >= 0 && col < GetNumberCols()),
1233 wxEmptyString,
1234 wxT("invalid row or column index in wxGridStringTable") );
1235
1236 return m_data[row][col];
1237 }
1238
1239 void wxGridStringTable::SetValue( int row, int col, const wxString& value )
1240 {
1241 wxCHECK_RET( (row >= 0 && row < GetNumberRows()) &&
1242 (col >= 0 && col < GetNumberCols()),
1243 wxT("invalid row or column index in wxGridStringTable") );
1244
1245 m_data[row][col] = value;
1246 }
1247
1248 void wxGridStringTable::Clear()
1249 {
1250 int row, col;
1251 int numRows, numCols;
1252
1253 numRows = m_data.GetCount();
1254 if ( numRows > 0 )
1255 {
1256 numCols = m_data[0].GetCount();
1257
1258 for ( row = 0; row < numRows; row++ )
1259 {
1260 for ( col = 0; col < numCols; col++ )
1261 {
1262 m_data[row][col] = wxEmptyString;
1263 }
1264 }
1265 }
1266 }
1267
1268 bool wxGridStringTable::InsertRows( size_t pos, size_t numRows )
1269 {
1270 size_t curNumRows = m_data.GetCount();
1271 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
1272 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
1273
1274 if ( pos >= curNumRows )
1275 {
1276 return AppendRows( numRows );
1277 }
1278
1279 wxArrayString sa;
1280 sa.Alloc( curNumCols );
1281 sa.Add( wxEmptyString, curNumCols );
1282 m_data.Insert( sa, pos, numRows );
1283
1284 if ( GetView() )
1285 {
1286 wxGridTableMessage msg( this,
1287 wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
1288 pos,
1289 numRows );
1290
1291 GetView()->ProcessTableMessage( msg );
1292 }
1293
1294 return true;
1295 }
1296
1297 bool wxGridStringTable::AppendRows( size_t numRows )
1298 {
1299 size_t curNumRows = m_data.GetCount();
1300 size_t curNumCols = ( curNumRows > 0
1301 ? m_data[0].GetCount()
1302 : ( GetView() ? GetView()->GetNumberCols() : 0 ) );
1303
1304 wxArrayString sa;
1305 if ( curNumCols > 0 )
1306 {
1307 sa.Alloc( curNumCols );
1308 sa.Add( wxEmptyString, curNumCols );
1309 }
1310
1311 m_data.Add( sa, numRows );
1312
1313 if ( GetView() )
1314 {
1315 wxGridTableMessage msg( this,
1316 wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
1317 numRows );
1318
1319 GetView()->ProcessTableMessage( msg );
1320 }
1321
1322 return true;
1323 }
1324
1325 bool wxGridStringTable::DeleteRows( size_t pos, size_t numRows )
1326 {
1327 size_t curNumRows = m_data.GetCount();
1328
1329 if ( pos >= curNumRows )
1330 {
1331 wxFAIL_MSG( wxString::Format
1332 (
1333 wxT("Called wxGridStringTable::DeleteRows(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu rows"),
1334 (unsigned long)pos,
1335 (unsigned long)numRows,
1336 (unsigned long)curNumRows
1337 ) );
1338
1339 return false;
1340 }
1341
1342 if ( numRows > curNumRows - pos )
1343 {
1344 numRows = curNumRows - pos;
1345 }
1346
1347 if ( numRows >= curNumRows )
1348 {
1349 m_data.Clear();
1350 }
1351 else
1352 {
1353 m_data.RemoveAt( pos, numRows );
1354 }
1355
1356 if ( GetView() )
1357 {
1358 wxGridTableMessage msg( this,
1359 wxGRIDTABLE_NOTIFY_ROWS_DELETED,
1360 pos,
1361 numRows );
1362
1363 GetView()->ProcessTableMessage( msg );
1364 }
1365
1366 return true;
1367 }
1368
1369 bool wxGridStringTable::InsertCols( size_t pos, size_t numCols )
1370 {
1371 size_t row, col;
1372
1373 size_t curNumRows = m_data.GetCount();
1374 size_t curNumCols = ( curNumRows > 0
1375 ? m_data[0].GetCount()
1376 : ( GetView() ? GetView()->GetNumberCols() : 0 ) );
1377
1378 if ( pos >= curNumCols )
1379 {
1380 return AppendCols( numCols );
1381 }
1382
1383 if ( !m_colLabels.IsEmpty() )
1384 {
1385 m_colLabels.Insert( wxEmptyString, pos, numCols );
1386
1387 size_t i;
1388 for ( i = pos; i < pos + numCols; i++ )
1389 m_colLabels[i] = wxGridTableBase::GetColLabelValue( i );
1390 }
1391
1392 for ( row = 0; row < curNumRows; row++ )
1393 {
1394 for ( col = pos; col < pos + numCols; col++ )
1395 {
1396 m_data[row].Insert( wxEmptyString, col );
1397 }
1398 }
1399
1400 m_numCols += numCols;
1401
1402 if ( GetView() )
1403 {
1404 wxGridTableMessage msg( this,
1405 wxGRIDTABLE_NOTIFY_COLS_INSERTED,
1406 pos,
1407 numCols );
1408
1409 GetView()->ProcessTableMessage( msg );
1410 }
1411
1412 return true;
1413 }
1414
1415 bool wxGridStringTable::AppendCols( size_t numCols )
1416 {
1417 size_t row;
1418
1419 size_t curNumRows = m_data.GetCount();
1420
1421 for ( row = 0; row < curNumRows; row++ )
1422 {
1423 m_data[row].Add( wxEmptyString, numCols );
1424 }
1425
1426 m_numCols += numCols;
1427
1428 if ( GetView() )
1429 {
1430 wxGridTableMessage msg( this,
1431 wxGRIDTABLE_NOTIFY_COLS_APPENDED,
1432 numCols );
1433
1434 GetView()->ProcessTableMessage( msg );
1435 }
1436
1437 return true;
1438 }
1439
1440 bool wxGridStringTable::DeleteCols( size_t pos, size_t numCols )
1441 {
1442 size_t row;
1443
1444 size_t curNumRows = m_data.GetCount();
1445 size_t curNumCols = ( curNumRows > 0 ? m_data[0].GetCount() :
1446 ( GetView() ? GetView()->GetNumberCols() : 0 ) );
1447
1448 if ( pos >= curNumCols )
1449 {
1450 wxFAIL_MSG( wxString::Format
1451 (
1452 wxT("Called wxGridStringTable::DeleteCols(pos=%lu, N=%lu)\nPos value is invalid for present table with %lu cols"),
1453 (unsigned long)pos,
1454 (unsigned long)numCols,
1455 (unsigned long)curNumCols
1456 ) );
1457 return false;
1458 }
1459
1460 int colID;
1461 if ( GetView() )
1462 colID = GetView()->GetColAt( pos );
1463 else
1464 colID = pos;
1465
1466 if ( numCols > curNumCols - colID )
1467 {
1468 numCols = curNumCols - colID;
1469 }
1470
1471 if ( !m_colLabels.IsEmpty() )
1472 {
1473 // m_colLabels stores just as many elements as it needs, e.g. if only
1474 // the label of the first column had been set it would have only one
1475 // element and not numCols, so account for it
1476 int nToRm = m_colLabels.size() - colID;
1477 if ( nToRm > 0 )
1478 m_colLabels.RemoveAt( colID, nToRm );
1479 }
1480
1481 if ( numCols >= curNumCols )
1482 {
1483 for ( row = 0; row < curNumRows; row++ )
1484 {
1485 m_data[row].Clear();
1486 }
1487
1488 m_numCols = 0;
1489 }
1490 else // something will be left
1491 {
1492 for ( row = 0; row < curNumRows; row++ )
1493 {
1494 m_data[row].RemoveAt( colID, numCols );
1495 }
1496
1497 m_numCols -= numCols;
1498 }
1499
1500 if ( GetView() )
1501 {
1502 wxGridTableMessage msg( this,
1503 wxGRIDTABLE_NOTIFY_COLS_DELETED,
1504 pos,
1505 numCols );
1506
1507 GetView()->ProcessTableMessage( msg );
1508 }
1509
1510 return true;
1511 }
1512
1513 wxString wxGridStringTable::GetRowLabelValue( int row )
1514 {
1515 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
1516 {
1517 // using default label
1518 //
1519 return wxGridTableBase::GetRowLabelValue( row );
1520 }
1521 else
1522 {
1523 return m_rowLabels[row];
1524 }
1525 }
1526
1527 wxString wxGridStringTable::GetColLabelValue( int col )
1528 {
1529 if ( col > (int)(m_colLabels.GetCount()) - 1 )
1530 {
1531 // using default label
1532 //
1533 return wxGridTableBase::GetColLabelValue( col );
1534 }
1535 else
1536 {
1537 return m_colLabels[col];
1538 }
1539 }
1540
1541 void wxGridStringTable::SetRowLabelValue( int row, const wxString& value )
1542 {
1543 if ( row > (int)(m_rowLabels.GetCount()) - 1 )
1544 {
1545 int n = m_rowLabels.GetCount();
1546 int i;
1547
1548 for ( i = n; i <= row; i++ )
1549 {
1550 m_rowLabels.Add( wxGridTableBase::GetRowLabelValue(i) );
1551 }
1552 }
1553
1554 m_rowLabels[row] = value;
1555 }
1556
1557 void wxGridStringTable::SetColLabelValue( int col, const wxString& value )
1558 {
1559 if ( col > (int)(m_colLabels.GetCount()) - 1 )
1560 {
1561 int n = m_colLabels.GetCount();
1562 int i;
1563
1564 for ( i = n; i <= col; i++ )
1565 {
1566 m_colLabels.Add( wxGridTableBase::GetColLabelValue(i) );
1567 }
1568 }
1569
1570 m_colLabels[col] = value;
1571 }
1572
1573
1574 //////////////////////////////////////////////////////////////////////
1575 //////////////////////////////////////////////////////////////////////
1576
1577 BEGIN_EVENT_TABLE(wxGridSubwindow, wxWindow)
1578 EVT_MOUSE_CAPTURE_LOST(wxGridSubwindow::OnMouseCaptureLost)
1579 END_EVENT_TABLE()
1580
1581 void wxGridSubwindow::OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
1582 {
1583 m_owner->CancelMouseCapture();
1584 }
1585
1586 BEGIN_EVENT_TABLE( wxGridRowLabelWindow, wxGridSubwindow )
1587 EVT_PAINT( wxGridRowLabelWindow::OnPaint )
1588 EVT_MOUSEWHEEL( wxGridRowLabelWindow::OnMouseWheel )
1589 EVT_MOUSE_EVENTS( wxGridRowLabelWindow::OnMouseEvent )
1590 END_EVENT_TABLE()
1591
1592 void wxGridRowLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
1593 {
1594 wxPaintDC dc(this);
1595
1596 // NO - don't do this because it will set both the x and y origin
1597 // coords to match the parent scrolled window and we just want to
1598 // set the y coord - MB
1599 //
1600 // m_owner->PrepareDC( dc );
1601
1602 int x, y;
1603 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
1604 wxPoint pt = dc.GetDeviceOrigin();
1605 dc.SetDeviceOrigin( pt.x, pt.y-y );
1606
1607 wxArrayInt rows = m_owner->CalcRowLabelsExposed( GetUpdateRegion() );
1608 m_owner->DrawRowLabels( dc, rows );
1609 }
1610
1611 void wxGridRowLabelWindow::OnMouseEvent( wxMouseEvent& event )
1612 {
1613 m_owner->ProcessRowLabelMouseEvent( event );
1614 }
1615
1616 void wxGridRowLabelWindow::OnMouseWheel( wxMouseEvent& event )
1617 {
1618 if (!m_owner->GetEventHandler()->ProcessEvent( event ))
1619 event.Skip();
1620 }
1621
1622 //////////////////////////////////////////////////////////////////////
1623
1624 BEGIN_EVENT_TABLE( wxGridColLabelWindow, wxGridSubwindow )
1625 EVT_PAINT( wxGridColLabelWindow::OnPaint )
1626 EVT_MOUSEWHEEL( wxGridColLabelWindow::OnMouseWheel )
1627 EVT_MOUSE_EVENTS( wxGridColLabelWindow::OnMouseEvent )
1628 END_EVENT_TABLE()
1629
1630 void wxGridColLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
1631 {
1632 wxPaintDC dc(this);
1633
1634 // NO - don't do this because it will set both the x and y origin
1635 // coords to match the parent scrolled window and we just want to
1636 // set the x coord - MB
1637 //
1638 // m_owner->PrepareDC( dc );
1639
1640 int x, y;
1641 m_owner->CalcUnscrolledPosition( 0, 0, &x, &y );
1642 wxPoint pt = dc.GetDeviceOrigin();
1643 if (GetLayoutDirection() == wxLayout_RightToLeft)
1644 dc.SetDeviceOrigin( pt.x+x, pt.y );
1645 else
1646 dc.SetDeviceOrigin( pt.x-x, pt.y );
1647
1648 wxArrayInt cols = m_owner->CalcColLabelsExposed( GetUpdateRegion() );
1649 m_owner->DrawColLabels( dc, cols );
1650 }
1651
1652 void wxGridColLabelWindow::OnMouseEvent( wxMouseEvent& event )
1653 {
1654 m_owner->ProcessColLabelMouseEvent( event );
1655 }
1656
1657 void wxGridColLabelWindow::OnMouseWheel( wxMouseEvent& event )
1658 {
1659 if (!m_owner->GetEventHandler()->ProcessEvent( event ))
1660 event.Skip();
1661 }
1662
1663 //////////////////////////////////////////////////////////////////////
1664
1665 BEGIN_EVENT_TABLE( wxGridCornerLabelWindow, wxGridSubwindow )
1666 EVT_MOUSEWHEEL( wxGridCornerLabelWindow::OnMouseWheel )
1667 EVT_MOUSE_EVENTS( wxGridCornerLabelWindow::OnMouseEvent )
1668 EVT_PAINT( wxGridCornerLabelWindow::OnPaint )
1669 END_EVENT_TABLE()
1670
1671 void wxGridCornerLabelWindow::OnPaint( wxPaintEvent& WXUNUSED(event) )
1672 {
1673 wxPaintDC dc(this);
1674
1675 m_owner->DrawCornerLabel(dc);
1676 }
1677
1678 void wxGridCornerLabelWindow::OnMouseEvent( wxMouseEvent& event )
1679 {
1680 m_owner->ProcessCornerLabelMouseEvent( event );
1681 }
1682
1683 void wxGridCornerLabelWindow::OnMouseWheel( wxMouseEvent& event )
1684 {
1685 if (!m_owner->GetEventHandler()->ProcessEvent(event))
1686 event.Skip();
1687 }
1688
1689 //////////////////////////////////////////////////////////////////////
1690
1691 BEGIN_EVENT_TABLE( wxGridWindow, wxGridSubwindow )
1692 EVT_PAINT( wxGridWindow::OnPaint )
1693 EVT_MOUSEWHEEL( wxGridWindow::OnMouseWheel )
1694 EVT_MOUSE_EVENTS( wxGridWindow::OnMouseEvent )
1695 EVT_KEY_DOWN( wxGridWindow::OnKeyDown )
1696 EVT_KEY_UP( wxGridWindow::OnKeyUp )
1697 EVT_CHAR( wxGridWindow::OnChar )
1698 EVT_SET_FOCUS( wxGridWindow::OnFocus )
1699 EVT_KILL_FOCUS( wxGridWindow::OnFocus )
1700 EVT_ERASE_BACKGROUND( wxGridWindow::OnEraseBackground )
1701 END_EVENT_TABLE()
1702
1703 void wxGridWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
1704 {
1705 wxPaintDC dc( this );
1706 m_owner->PrepareDC( dc );
1707 wxRegion reg = GetUpdateRegion();
1708 wxGridCellCoordsArray dirtyCells = m_owner->CalcCellsExposed( reg );
1709 m_owner->DrawGridCellArea( dc, dirtyCells );
1710
1711 m_owner->DrawGridSpace( dc );
1712
1713 m_owner->DrawAllGridLines( dc, reg );
1714
1715 m_owner->DrawHighlight( dc, dirtyCells );
1716 }
1717
1718 void wxGridWindow::ScrollWindow( int dx, int dy, const wxRect *rect )
1719 {
1720 wxWindow::ScrollWindow( dx, dy, rect );
1721 m_owner->GetGridRowLabelWindow()->ScrollWindow( 0, dy, rect );
1722 m_owner->GetGridColLabelWindow()->ScrollWindow( dx, 0, rect );
1723 }
1724
1725 void wxGridWindow::OnMouseEvent( wxMouseEvent& event )
1726 {
1727 if (event.ButtonDown(wxMOUSE_BTN_LEFT) && FindFocus() != this)
1728 SetFocus();
1729
1730 m_owner->ProcessGridCellMouseEvent( event );
1731 }
1732
1733 void wxGridWindow::OnMouseWheel( wxMouseEvent& event )
1734 {
1735 if (!m_owner->GetEventHandler()->ProcessEvent( event ))
1736 event.Skip();
1737 }
1738
1739 // This seems to be required for wxMotif/wxGTK otherwise the mouse
1740 // cursor must be in the cell edit control to get key events
1741 //
1742 void wxGridWindow::OnKeyDown( wxKeyEvent& event )
1743 {
1744 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) )
1745 event.Skip();
1746 }
1747
1748 void wxGridWindow::OnKeyUp( wxKeyEvent& event )
1749 {
1750 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) )
1751 event.Skip();
1752 }
1753
1754 void wxGridWindow::OnChar( wxKeyEvent& event )
1755 {
1756 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) )
1757 event.Skip();
1758 }
1759
1760 void wxGridWindow::OnEraseBackground( wxEraseEvent& WXUNUSED(event) )
1761 {
1762 }
1763
1764 void wxGridWindow::OnFocus(wxFocusEvent& event)
1765 {
1766 // and if we have any selection, it has to be repainted, because it
1767 // uses different colour when the grid is not focused:
1768 if ( m_owner->IsSelection() )
1769 {
1770 Refresh();
1771 }
1772 else
1773 {
1774 // NB: Note that this code is in "else" branch only because the other
1775 // branch refreshes everything and so there's no point in calling
1776 // Refresh() again, *not* because it should only be done if
1777 // !IsSelection(). If the above code is ever optimized to refresh
1778 // only selected area, this needs to be moved out of the "else"
1779 // branch so that it's always executed.
1780
1781 // current cell cursor {dis,re}appears on focus change:
1782 const wxGridCellCoords cursorCoords(m_owner->GetGridCursorRow(),
1783 m_owner->GetGridCursorCol());
1784 const wxRect cursor =
1785 m_owner->BlockToDeviceRect(cursorCoords, cursorCoords);
1786 Refresh(true, &cursor);
1787 }
1788
1789 if ( !m_owner->GetEventHandler()->ProcessEvent( event ) )
1790 event.Skip();
1791 }
1792
1793 #define internalXToCol(x) XToCol(x, true)
1794 #define internalYToRow(y) YToRow(y, true)
1795
1796 /////////////////////////////////////////////////////////////////////
1797
1798 #if wxUSE_EXTENDED_RTTI
1799 WX_DEFINE_FLAGS( wxGridStyle )
1800
1801 wxBEGIN_FLAGS( wxGridStyle )
1802 // new style border flags, we put them first to
1803 // use them for streaming out
1804 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
1805 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
1806 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
1807 wxFLAGS_MEMBER(wxBORDER_RAISED)
1808 wxFLAGS_MEMBER(wxBORDER_STATIC)
1809 wxFLAGS_MEMBER(wxBORDER_NONE)
1810
1811 // old style border flags
1812 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
1813 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
1814 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
1815 wxFLAGS_MEMBER(wxRAISED_BORDER)
1816 wxFLAGS_MEMBER(wxSTATIC_BORDER)
1817 wxFLAGS_MEMBER(wxBORDER)
1818
1819 // standard window styles
1820 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
1821 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
1822 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
1823 wxFLAGS_MEMBER(wxWANTS_CHARS)
1824 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
1825 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB)
1826 wxFLAGS_MEMBER(wxVSCROLL)
1827 wxFLAGS_MEMBER(wxHSCROLL)
1828
1829 wxEND_FLAGS( wxGridStyle )
1830
1831 IMPLEMENT_DYNAMIC_CLASS_XTI(wxGrid, wxScrolledWindow,"wx/grid.h")
1832
1833 wxBEGIN_PROPERTIES_TABLE(wxGrid)
1834 wxHIDE_PROPERTY( Children )
1835 wxPROPERTY_FLAGS( WindowStyle , wxGridStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
1836 wxEND_PROPERTIES_TABLE()
1837
1838 wxBEGIN_HANDLERS_TABLE(wxGrid)
1839 wxEND_HANDLERS_TABLE()
1840
1841 wxCONSTRUCTOR_5( wxGrid , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
1842
1843 /*
1844 TODO : Expose more information of a list's layout, etc. via appropriate objects (e.g., NotebookPageInfo)
1845 */
1846 #else
1847 IMPLEMENT_DYNAMIC_CLASS( wxGrid, wxScrolledWindow )
1848 #endif
1849
1850 BEGIN_EVENT_TABLE( wxGrid, wxScrolledWindow )
1851 EVT_PAINT( wxGrid::OnPaint )
1852 EVT_SIZE( wxGrid::OnSize )
1853 EVT_KEY_DOWN( wxGrid::OnKeyDown )
1854 EVT_KEY_UP( wxGrid::OnKeyUp )
1855 EVT_CHAR ( wxGrid::OnChar )
1856 EVT_ERASE_BACKGROUND( wxGrid::OnEraseBackground )
1857 END_EVENT_TABLE()
1858
1859 bool wxGrid::Create(wxWindow *parent, wxWindowID id,
1860 const wxPoint& pos, const wxSize& size,
1861 long style, const wxString& name)
1862 {
1863 if (!wxScrolledWindow::Create(parent, id, pos, size,
1864 style | wxWANTS_CHARS, name))
1865 return false;
1866
1867 m_colMinWidths = wxLongToLongHashMap(GRID_HASH_SIZE);
1868 m_rowMinHeights = wxLongToLongHashMap(GRID_HASH_SIZE);
1869
1870 Create();
1871 SetInitialSize(size);
1872 SetScrollRate(m_scrollLineX, m_scrollLineY);
1873 CalcDimensions();
1874
1875 return true;
1876 }
1877
1878 wxGrid::~wxGrid()
1879 {
1880 if ( m_winCapture )
1881 m_winCapture->ReleaseMouse();
1882
1883 // Ensure that the editor control is destroyed before the grid is,
1884 // otherwise we crash later when the editor tries to do something with the
1885 // half destroyed grid
1886 HideCellEditControl();
1887
1888 // Must do this or ~wxScrollHelper will pop the wrong event handler
1889 SetTargetWindow(this);
1890 ClearAttrCache();
1891 wxSafeDecRef(m_defaultCellAttr);
1892
1893 #ifdef DEBUG_ATTR_CACHE
1894 size_t total = gs_nAttrCacheHits + gs_nAttrCacheMisses;
1895 wxPrintf(wxT("wxGrid attribute cache statistics: "
1896 "total: %u, hits: %u (%u%%)\n"),
1897 total, gs_nAttrCacheHits,
1898 total ? (gs_nAttrCacheHits*100) / total : 0);
1899 #endif
1900
1901 // if we own the table, just delete it, otherwise at least don't leave it
1902 // with dangling view pointer
1903 if ( m_ownTable )
1904 delete m_table;
1905 else if ( m_table && m_table->GetView() == this )
1906 m_table->SetView(NULL);
1907
1908 delete m_typeRegistry;
1909 delete m_selection;
1910
1911 delete m_setFixedRows;
1912 delete m_setFixedCols;
1913 }
1914
1915 //
1916 // ----- internal init and update functions
1917 //
1918
1919 // NOTE: If using the default visual attributes works everywhere then this can
1920 // be removed as well as the #else cases below.
1921 #define _USE_VISATTR 0
1922
1923 void wxGrid::Create()
1924 {
1925 // create the type registry
1926 m_typeRegistry = new wxGridTypeRegistry;
1927
1928 m_cellEditCtrlEnabled = false;
1929
1930 m_defaultCellAttr = new wxGridCellAttr();
1931
1932 // Set default cell attributes
1933 m_defaultCellAttr->SetDefAttr(m_defaultCellAttr);
1934 m_defaultCellAttr->SetKind(wxGridCellAttr::Default);
1935 m_defaultCellAttr->SetFont(GetFont());
1936 m_defaultCellAttr->SetAlignment(wxALIGN_LEFT, wxALIGN_TOP);
1937 m_defaultCellAttr->SetRenderer(new wxGridCellStringRenderer);
1938 m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
1939
1940 #if _USE_VISATTR
1941 wxVisualAttributes gva = wxListBox::GetClassDefaultAttributes();
1942 wxVisualAttributes lva = wxPanel::GetClassDefaultAttributes();
1943
1944 m_defaultCellAttr->SetTextColour(gva.colFg);
1945 m_defaultCellAttr->SetBackgroundColour(gva.colBg);
1946
1947 #else
1948 m_defaultCellAttr->SetTextColour(
1949 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
1950 m_defaultCellAttr->SetBackgroundColour(
1951 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
1952 #endif
1953
1954 m_numRows = 0;
1955 m_numCols = 0;
1956 m_currentCellCoords = wxGridNoCellCoords;
1957
1958 // subwindow components that make up the wxGrid
1959 m_rowLabelWin = new wxGridRowLabelWindow(this);
1960 CreateColumnWindow();
1961 m_cornerLabelWin = new wxGridCornerLabelWindow(this);
1962 m_gridWin = new wxGridWindow( this );
1963
1964 SetTargetWindow( m_gridWin );
1965
1966 #if _USE_VISATTR
1967 wxColour gfg = gva.colFg;
1968 wxColour gbg = gva.colBg;
1969 wxColour lfg = lva.colFg;
1970 wxColour lbg = lva.colBg;
1971 #else
1972 wxColour gfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
1973 wxColour gbg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
1974 wxColour lfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
1975 wxColour lbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
1976 #endif
1977
1978 m_cornerLabelWin->SetOwnForegroundColour(lfg);
1979 m_cornerLabelWin->SetOwnBackgroundColour(lbg);
1980 m_rowLabelWin->SetOwnForegroundColour(lfg);
1981 m_rowLabelWin->SetOwnBackgroundColour(lbg);
1982 m_colWindow->SetOwnForegroundColour(lfg);
1983 m_colWindow->SetOwnBackgroundColour(lbg);
1984
1985 m_gridWin->SetOwnForegroundColour(gfg);
1986 m_gridWin->SetOwnBackgroundColour(gbg);
1987
1988 m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
1989 m_labelTextColour = m_rowLabelWin->GetForegroundColour();
1990
1991 // now that we have the grid window, use its font to compute the default
1992 // row height
1993 m_defaultRowHeight = m_gridWin->GetCharHeight();
1994 #if defined(__WXMOTIF__) || defined(__WXGTK__) // see also text ctrl sizing in ShowCellEditControl()
1995 m_defaultRowHeight += 8;
1996 #else
1997 m_defaultRowHeight += 4;
1998 #endif
1999
2000 }
2001
2002 void wxGrid::CreateColumnWindow()
2003 {
2004 if ( m_useNativeHeader )
2005 {
2006 m_colWindow = new wxGridHeaderCtrl(this);
2007 m_colLabelHeight = m_colWindow->GetBestSize().y;
2008 }
2009 else // draw labels ourselves
2010 {
2011 m_colWindow = new wxGridColLabelWindow(this);
2012 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
2013 }
2014 }
2015
2016 bool wxGrid::CreateGrid( int numRows, int numCols,
2017 wxGridSelectionModes selmode )
2018 {
2019 wxCHECK_MSG( !m_created,
2020 false,
2021 wxT("wxGrid::CreateGrid or wxGrid::SetTable called more than once") );
2022
2023 return SetTable(new wxGridStringTable(numRows, numCols), true, selmode);
2024 }
2025
2026 void wxGrid::SetSelectionMode(wxGridSelectionModes selmode)
2027 {
2028 wxCHECK_RET( m_created,
2029 wxT("Called wxGrid::SetSelectionMode() before calling CreateGrid()") );
2030
2031 m_selection->SetSelectionMode( selmode );
2032 }
2033
2034 wxGrid::wxGridSelectionModes wxGrid::GetSelectionMode() const
2035 {
2036 wxCHECK_MSG( m_created, wxGridSelectCells,
2037 wxT("Called wxGrid::GetSelectionMode() before calling CreateGrid()") );
2038
2039 return m_selection->GetSelectionMode();
2040 }
2041
2042 bool
2043 wxGrid::SetTable(wxGridTableBase *table,
2044 bool takeOwnership,
2045 wxGrid::wxGridSelectionModes selmode )
2046 {
2047 bool checkSelection = false;
2048 if ( m_created )
2049 {
2050 // stop all processing
2051 m_created = false;
2052
2053 if (m_table)
2054 {
2055 m_table->SetView(0);
2056 if( m_ownTable )
2057 delete m_table;
2058 m_table = NULL;
2059 }
2060
2061 delete m_selection;
2062 m_selection = NULL;
2063
2064 m_ownTable = false;
2065 m_numRows = 0;
2066 m_numCols = 0;
2067 checkSelection = true;
2068
2069 // kill row and column size arrays
2070 m_colWidths.Empty();
2071 m_colRights.Empty();
2072 m_rowHeights.Empty();
2073 m_rowBottoms.Empty();
2074 }
2075
2076 if (table)
2077 {
2078 m_numRows = table->GetNumberRows();
2079 m_numCols = table->GetNumberCols();
2080
2081 if ( m_useNativeHeader )
2082 GetGridColHeader()->SetColumnCount(m_numCols);
2083
2084 m_table = table;
2085 m_table->SetView( this );
2086 m_ownTable = takeOwnership;
2087 m_selection = new wxGridSelection( this, selmode );
2088 if (checkSelection)
2089 {
2090 // If the newly set table is smaller than the
2091 // original one current cell and selection regions
2092 // might be invalid,
2093 m_selectedBlockCorner = wxGridNoCellCoords;
2094 m_currentCellCoords =
2095 wxGridCellCoords(wxMin(m_numRows, m_currentCellCoords.GetRow()),
2096 wxMin(m_numCols, m_currentCellCoords.GetCol()));
2097 if (m_selectedBlockTopLeft.GetRow() >= m_numRows ||
2098 m_selectedBlockTopLeft.GetCol() >= m_numCols)
2099 {
2100 m_selectedBlockTopLeft = wxGridNoCellCoords;
2101 m_selectedBlockBottomRight = wxGridNoCellCoords;
2102 }
2103 else
2104 m_selectedBlockBottomRight =
2105 wxGridCellCoords(wxMin(m_numRows,
2106 m_selectedBlockBottomRight.GetRow()),
2107 wxMin(m_numCols,
2108 m_selectedBlockBottomRight.GetCol()));
2109 }
2110 CalcDimensions();
2111
2112 m_created = true;
2113 }
2114
2115 return m_created;
2116 }
2117
2118 void wxGrid::Init()
2119 {
2120 m_created = false;
2121
2122 m_cornerLabelWin = NULL;
2123 m_rowLabelWin = NULL;
2124 m_colWindow = NULL;
2125 m_gridWin = NULL;
2126
2127 m_table = NULL;
2128 m_ownTable = false;
2129
2130 m_selection = NULL;
2131 m_defaultCellAttr = NULL;
2132 m_typeRegistry = NULL;
2133 m_winCapture = NULL;
2134
2135 m_rowLabelWidth = WXGRID_DEFAULT_ROW_LABEL_WIDTH;
2136 m_colLabelHeight = WXGRID_DEFAULT_COL_LABEL_HEIGHT;
2137
2138 m_setFixedRows =
2139 m_setFixedCols = NULL;
2140
2141 // init attr cache
2142 m_attrCache.row = -1;
2143 m_attrCache.col = -1;
2144 m_attrCache.attr = NULL;
2145
2146 m_labelFont = GetFont();
2147 m_labelFont.SetWeight( wxBOLD );
2148
2149 m_rowLabelHorizAlign = wxALIGN_CENTRE;
2150 m_rowLabelVertAlign = wxALIGN_CENTRE;
2151
2152 m_colLabelHorizAlign = wxALIGN_CENTRE;
2153 m_colLabelVertAlign = wxALIGN_CENTRE;
2154 m_colLabelTextOrientation = wxHORIZONTAL;
2155
2156 m_defaultColWidth = WXGRID_DEFAULT_COL_WIDTH;
2157 m_defaultRowHeight = 0; // this will be initialized after creation
2158
2159 m_minAcceptableColWidth = WXGRID_MIN_COL_WIDTH;
2160 m_minAcceptableRowHeight = WXGRID_MIN_ROW_HEIGHT;
2161
2162 m_gridLineColour = wxColour( 192,192,192 );
2163 m_gridLinesEnabled = true;
2164 m_gridLinesClipHorz =
2165 m_gridLinesClipVert = true;
2166 m_cellHighlightColour = *wxBLACK;
2167 m_cellHighlightPenWidth = 2;
2168 m_cellHighlightROPenWidth = 1;
2169
2170 m_canDragColMove = false;
2171
2172 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
2173 m_winCapture = NULL;
2174 m_canDragRowSize = true;
2175 m_canDragColSize = true;
2176 m_canDragGridSize = true;
2177 m_canDragCell = false;
2178 m_dragLastPos = -1;
2179 m_dragRowOrCol = -1;
2180 m_isDragging = false;
2181 m_startDragPos = wxDefaultPosition;
2182
2183 m_sortCol = wxNOT_FOUND;
2184 m_sortIsAscending = true;
2185
2186 m_useNativeHeader =
2187 m_nativeColumnLabels = false;
2188
2189 m_waitForSlowClick = false;
2190
2191 m_rowResizeCursor = wxCursor( wxCURSOR_SIZENS );
2192 m_colResizeCursor = wxCursor( wxCURSOR_SIZEWE );
2193
2194 m_currentCellCoords = wxGridNoCellCoords;
2195
2196 m_selectedBlockTopLeft =
2197 m_selectedBlockBottomRight =
2198 m_selectedBlockCorner = wxGridNoCellCoords;
2199
2200 m_selectionBackground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
2201 m_selectionForeground = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
2202
2203 m_editable = true; // default for whole grid
2204
2205 m_inOnKeyDown = false;
2206 m_batchCount = 0;
2207
2208 m_extraWidth =
2209 m_extraHeight = 0;
2210
2211 m_scrollLineX = GRID_SCROLL_LINE_X;
2212 m_scrollLineY = GRID_SCROLL_LINE_Y;
2213 }
2214
2215 // ----------------------------------------------------------------------------
2216 // the idea is to call these functions only when necessary because they create
2217 // quite big arrays which eat memory mostly unnecessary - in particular, if
2218 // default widths/heights are used for all rows/columns, we may not use these
2219 // arrays at all
2220 //
2221 // with some extra code, it should be possible to only store the widths/heights
2222 // different from default ones (resulting in space savings for huge grids) but
2223 // this is not done currently
2224 // ----------------------------------------------------------------------------
2225
2226 void wxGrid::InitRowHeights()
2227 {
2228 m_rowHeights.Empty();
2229 m_rowBottoms.Empty();
2230
2231 m_rowHeights.Alloc( m_numRows );
2232 m_rowBottoms.Alloc( m_numRows );
2233
2234 m_rowHeights.Add( m_defaultRowHeight, m_numRows );
2235
2236 int rowBottom = 0;
2237 for ( int i = 0; i < m_numRows; i++ )
2238 {
2239 rowBottom += m_defaultRowHeight;
2240 m_rowBottoms.Add( rowBottom );
2241 }
2242 }
2243
2244 void wxGrid::InitColWidths()
2245 {
2246 m_colWidths.Empty();
2247 m_colRights.Empty();
2248
2249 m_colWidths.Alloc( m_numCols );
2250 m_colRights.Alloc( m_numCols );
2251
2252 m_colWidths.Add( m_defaultColWidth, m_numCols );
2253
2254 for ( int i = 0; i < m_numCols; i++ )
2255 {
2256 int colRight = ( GetColPos( i ) + 1 ) * m_defaultColWidth;
2257 m_colRights.Add( colRight );
2258 }
2259 }
2260
2261 int wxGrid::GetColWidth(int col) const
2262 {
2263 return m_colWidths.IsEmpty() ? m_defaultColWidth : m_colWidths[col];
2264 }
2265
2266 int wxGrid::GetColLeft(int col) const
2267 {
2268 return m_colRights.IsEmpty() ? GetColPos( col ) * m_defaultColWidth
2269 : m_colRights[col] - m_colWidths[col];
2270 }
2271
2272 int wxGrid::GetColRight(int col) const
2273 {
2274 return m_colRights.IsEmpty() ? (GetColPos( col ) + 1) * m_defaultColWidth
2275 : m_colRights[col];
2276 }
2277
2278 int wxGrid::GetRowHeight(int row) const
2279 {
2280 return m_rowHeights.IsEmpty() ? m_defaultRowHeight : m_rowHeights[row];
2281 }
2282
2283 int wxGrid::GetRowTop(int row) const
2284 {
2285 return m_rowBottoms.IsEmpty() ? row * m_defaultRowHeight
2286 : m_rowBottoms[row] - m_rowHeights[row];
2287 }
2288
2289 int wxGrid::GetRowBottom(int row) const
2290 {
2291 return m_rowBottoms.IsEmpty() ? (row + 1) * m_defaultRowHeight
2292 : m_rowBottoms[row];
2293 }
2294
2295 void wxGrid::CalcDimensions()
2296 {
2297 // compute the size of the scrollable area
2298 int w = m_numCols > 0 ? GetColRight(GetColAt(m_numCols - 1)) : 0;
2299 int h = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0;
2300
2301 w += m_extraWidth;
2302 h += m_extraHeight;
2303
2304 // take into account editor if shown
2305 if ( IsCellEditControlShown() )
2306 {
2307 int w2, h2;
2308 int r = m_currentCellCoords.GetRow();
2309 int c = m_currentCellCoords.GetCol();
2310 int x = GetColLeft(c);
2311 int y = GetRowTop(r);
2312
2313 // how big is the editor
2314 wxGridCellAttr* attr = GetCellAttr(r, c);
2315 wxGridCellEditor* editor = attr->GetEditor(this, r, c);
2316 editor->GetControl()->GetSize(&w2, &h2);
2317 w2 += x;
2318 h2 += y;
2319 if ( w2 > w )
2320 w = w2;
2321 if ( h2 > h )
2322 h = h2;
2323 editor->DecRef();
2324 attr->DecRef();
2325 }
2326
2327 // preserve (more or less) the previous position
2328 int x, y;
2329 GetViewStart( &x, &y );
2330
2331 // ensure the position is valid for the new scroll ranges
2332 if ( x >= w )
2333 x = wxMax( w - 1, 0 );
2334 if ( y >= h )
2335 y = wxMax( h - 1, 0 );
2336
2337 // update the virtual size and refresh the scrollbars to reflect it
2338 m_gridWin->SetVirtualSize(w, h);
2339 Scroll(x, y);
2340 AdjustScrollbars();
2341
2342 // if our OnSize() hadn't been called (it would if we have scrollbars), we
2343 // still must reposition the children
2344 CalcWindowSizes();
2345 }
2346
2347 wxSize wxGrid::GetSizeAvailableForScrollTarget(const wxSize& size)
2348 {
2349 wxSize sizeGridWin(size);
2350 sizeGridWin.x -= m_rowLabelWidth;
2351 sizeGridWin.y -= m_colLabelHeight;
2352
2353 return sizeGridWin;
2354 }
2355
2356 void wxGrid::CalcWindowSizes()
2357 {
2358 // escape if the window is has not been fully created yet
2359
2360 if ( m_cornerLabelWin == NULL )
2361 return;
2362
2363 int cw, ch;
2364 GetClientSize( &cw, &ch );
2365
2366 // the grid may be too small to have enough space for the labels yet, don't
2367 // size the windows to negative sizes in this case
2368 int gw = cw - m_rowLabelWidth;
2369 int gh = ch - m_colLabelHeight;
2370 if (gw < 0)
2371 gw = 0;
2372 if (gh < 0)
2373 gh = 0;
2374
2375 if ( m_cornerLabelWin && m_cornerLabelWin->IsShown() )
2376 m_cornerLabelWin->SetSize( 0, 0, m_rowLabelWidth, m_colLabelHeight );
2377
2378 if ( m_colWindow && m_colWindow->IsShown() )
2379 m_colWindow->SetSize( m_rowLabelWidth, 0, gw, m_colLabelHeight );
2380
2381 if ( m_rowLabelWin && m_rowLabelWin->IsShown() )
2382 m_rowLabelWin->SetSize( 0, m_colLabelHeight, m_rowLabelWidth, gh );
2383
2384 if ( m_gridWin && m_gridWin->IsShown() )
2385 m_gridWin->SetSize( m_rowLabelWidth, m_colLabelHeight, gw, gh );
2386 }
2387
2388 // this is called when the grid table sends a message
2389 // to indicate that it has been redimensioned
2390 //
2391 bool wxGrid::Redimension( wxGridTableMessage& msg )
2392 {
2393 int i;
2394 bool result = false;
2395
2396 // Clear the attribute cache as the attribute might refer to a different
2397 // cell than stored in the cache after adding/removing rows/columns.
2398 ClearAttrCache();
2399
2400 // By the same reasoning, the editor should be dismissed if columns are
2401 // added or removed. And for consistency, it should IMHO always be
2402 // removed, not only if the cell "underneath" it actually changes.
2403 // For now, I intentionally do not save the editor's content as the
2404 // cell it might want to save that stuff to might no longer exist.
2405 HideCellEditControl();
2406
2407 switch ( msg.GetId() )
2408 {
2409 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
2410 {
2411 size_t pos = msg.GetCommandInt();
2412 int numRows = msg.GetCommandInt2();
2413
2414 m_numRows += numRows;
2415
2416 if ( !m_rowHeights.IsEmpty() )
2417 {
2418 m_rowHeights.Insert( m_defaultRowHeight, pos, numRows );
2419 m_rowBottoms.Insert( 0, pos, numRows );
2420
2421 int bottom = 0;
2422 if ( pos > 0 )
2423 bottom = m_rowBottoms[pos - 1];
2424
2425 for ( i = pos; i < m_numRows; i++ )
2426 {
2427 bottom += m_rowHeights[i];
2428 m_rowBottoms[i] = bottom;
2429 }
2430 }
2431
2432 if ( m_currentCellCoords == wxGridNoCellCoords )
2433 {
2434 // if we have just inserted cols into an empty grid the current
2435 // cell will be undefined...
2436 //
2437 SetCurrentCell( 0, 0 );
2438 }
2439
2440 if ( m_selection )
2441 m_selection->UpdateRows( pos, numRows );
2442 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
2443 if (attrProvider)
2444 attrProvider->UpdateAttrRows( pos, numRows );
2445
2446 if ( !GetBatchCount() )
2447 {
2448 CalcDimensions();
2449 m_rowLabelWin->Refresh();
2450 }
2451 }
2452 result = true;
2453 break;
2454
2455 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
2456 {
2457 int numRows = msg.GetCommandInt();
2458 int oldNumRows = m_numRows;
2459 m_numRows += numRows;
2460
2461 if ( !m_rowHeights.IsEmpty() )
2462 {
2463 m_rowHeights.Add( m_defaultRowHeight, numRows );
2464 m_rowBottoms.Add( 0, numRows );
2465
2466 int bottom = 0;
2467 if ( oldNumRows > 0 )
2468 bottom = m_rowBottoms[oldNumRows - 1];
2469
2470 for ( i = oldNumRows; i < m_numRows; i++ )
2471 {
2472 bottom += m_rowHeights[i];
2473 m_rowBottoms[i] = bottom;
2474 }
2475 }
2476
2477 if ( m_currentCellCoords == wxGridNoCellCoords )
2478 {
2479 // if we have just inserted cols into an empty grid the current
2480 // cell will be undefined...
2481 //
2482 SetCurrentCell( 0, 0 );
2483 }
2484
2485 if ( !GetBatchCount() )
2486 {
2487 CalcDimensions();
2488 m_rowLabelWin->Refresh();
2489 }
2490 }
2491 result = true;
2492 break;
2493
2494 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
2495 {
2496 size_t pos = msg.GetCommandInt();
2497 int numRows = msg.GetCommandInt2();
2498 m_numRows -= numRows;
2499
2500 if ( !m_rowHeights.IsEmpty() )
2501 {
2502 m_rowHeights.RemoveAt( pos, numRows );
2503 m_rowBottoms.RemoveAt( pos, numRows );
2504
2505 int h = 0;
2506 for ( i = 0; i < m_numRows; i++ )
2507 {
2508 h += m_rowHeights[i];
2509 m_rowBottoms[i] = h;
2510 }
2511 }
2512
2513 if ( !m_numRows )
2514 {
2515 m_currentCellCoords = wxGridNoCellCoords;
2516 }
2517 else
2518 {
2519 if ( m_currentCellCoords.GetRow() >= m_numRows )
2520 m_currentCellCoords.Set( 0, 0 );
2521 }
2522
2523 if ( m_selection )
2524 m_selection->UpdateRows( pos, -((int)numRows) );
2525 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
2526 if (attrProvider)
2527 {
2528 attrProvider->UpdateAttrRows( pos, -((int)numRows) );
2529
2530 // ifdef'd out following patch from Paul Gammans
2531 #if 0
2532 // No need to touch column attributes, unless we
2533 // removed _all_ rows, in this case, we remove
2534 // all column attributes.
2535 // I hate to do this here, but the
2536 // needed data is not available inside UpdateAttrRows.
2537 if ( !GetNumberRows() )
2538 attrProvider->UpdateAttrCols( 0, -GetNumberCols() );
2539 #endif
2540 }
2541
2542 if ( !GetBatchCount() )
2543 {
2544 CalcDimensions();
2545 m_rowLabelWin->Refresh();
2546 }
2547 }
2548 result = true;
2549 break;
2550
2551 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
2552 {
2553 size_t pos = msg.GetCommandInt();
2554 int numCols = msg.GetCommandInt2();
2555 m_numCols += numCols;
2556
2557 if ( m_useNativeHeader )
2558 GetGridColHeader()->SetColumnCount(m_numCols);
2559
2560 if ( !m_colAt.IsEmpty() )
2561 {
2562 //Shift the column IDs
2563 int i;
2564 for ( i = 0; i < m_numCols - numCols; i++ )
2565 {
2566 if ( m_colAt[i] >= (int)pos )
2567 m_colAt[i] += numCols;
2568 }
2569
2570 m_colAt.Insert( pos, pos, numCols );
2571
2572 //Set the new columns' positions
2573 for ( i = pos + 1; i < (int)pos + numCols; i++ )
2574 {
2575 m_colAt[i] = i;
2576 }
2577 }
2578
2579 if ( !m_colWidths.IsEmpty() )
2580 {
2581 m_colWidths.Insert( m_defaultColWidth, pos, numCols );
2582 m_colRights.Insert( 0, pos, numCols );
2583
2584 int right = 0;
2585 if ( pos > 0 )
2586 right = m_colRights[GetColAt( pos - 1 )];
2587
2588 int colPos;
2589 for ( colPos = pos; colPos < m_numCols; colPos++ )
2590 {
2591 i = GetColAt( colPos );
2592
2593 right += m_colWidths[i];
2594 m_colRights[i] = right;
2595 }
2596 }
2597
2598 if ( m_currentCellCoords == wxGridNoCellCoords )
2599 {
2600 // if we have just inserted cols into an empty grid the current
2601 // cell will be undefined...
2602 //
2603 SetCurrentCell( 0, 0 );
2604 }
2605
2606 if ( m_selection )
2607 m_selection->UpdateCols( pos, numCols );
2608 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
2609 if (attrProvider)
2610 attrProvider->UpdateAttrCols( pos, numCols );
2611 if ( !GetBatchCount() )
2612 {
2613 CalcDimensions();
2614 m_colWindow->Refresh();
2615 }
2616 }
2617 result = true;
2618 break;
2619
2620 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
2621 {
2622 int numCols = msg.GetCommandInt();
2623 int oldNumCols = m_numCols;
2624 m_numCols += numCols;
2625 if ( m_useNativeHeader )
2626 GetGridColHeader()->SetColumnCount(m_numCols);
2627
2628 if ( !m_colAt.IsEmpty() )
2629 {
2630 m_colAt.Add( 0, numCols );
2631
2632 //Set the new columns' positions
2633 int i;
2634 for ( i = oldNumCols; i < m_numCols; i++ )
2635 {
2636 m_colAt[i] = i;
2637 }
2638 }
2639
2640 if ( !m_colWidths.IsEmpty() )
2641 {
2642 m_colWidths.Add( m_defaultColWidth, numCols );
2643 m_colRights.Add( 0, numCols );
2644
2645 int right = 0;
2646 if ( oldNumCols > 0 )
2647 right = m_colRights[GetColAt( oldNumCols - 1 )];
2648
2649 int colPos;
2650 for ( colPos = oldNumCols; colPos < m_numCols; colPos++ )
2651 {
2652 i = GetColAt( colPos );
2653
2654 right += m_colWidths[i];
2655 m_colRights[i] = right;
2656 }
2657 }
2658
2659 if ( m_currentCellCoords == wxGridNoCellCoords )
2660 {
2661 // if we have just inserted cols into an empty grid the current
2662 // cell will be undefined...
2663 //
2664 SetCurrentCell( 0, 0 );
2665 }
2666 if ( !GetBatchCount() )
2667 {
2668 CalcDimensions();
2669 m_colWindow->Refresh();
2670 }
2671 }
2672 result = true;
2673 break;
2674
2675 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
2676 {
2677 size_t pos = msg.GetCommandInt();
2678 int numCols = msg.GetCommandInt2();
2679 m_numCols -= numCols;
2680 if ( m_useNativeHeader )
2681 GetGridColHeader()->SetColumnCount(m_numCols);
2682
2683 if ( !m_colAt.IsEmpty() )
2684 {
2685 int colID = GetColAt( pos );
2686
2687 m_colAt.RemoveAt( pos, numCols );
2688
2689 //Shift the column IDs
2690 int colPos;
2691 for ( colPos = 0; colPos < m_numCols; colPos++ )
2692 {
2693 if ( m_colAt[colPos] > colID )
2694 m_colAt[colPos] -= numCols;
2695 }
2696 }
2697
2698 if ( !m_colWidths.IsEmpty() )
2699 {
2700 m_colWidths.RemoveAt( pos, numCols );
2701 m_colRights.RemoveAt( pos, numCols );
2702
2703 int w = 0;
2704 int colPos;
2705 for ( colPos = 0; colPos < m_numCols; colPos++ )
2706 {
2707 i = GetColAt( colPos );
2708
2709 w += m_colWidths[i];
2710 m_colRights[i] = w;
2711 }
2712 }
2713
2714 if ( !m_numCols )
2715 {
2716 m_currentCellCoords = wxGridNoCellCoords;
2717 }
2718 else
2719 {
2720 if ( m_currentCellCoords.GetCol() >= m_numCols )
2721 m_currentCellCoords.Set( 0, 0 );
2722 }
2723
2724 if ( m_selection )
2725 m_selection->UpdateCols( pos, -((int)numCols) );
2726 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
2727 if (attrProvider)
2728 {
2729 attrProvider->UpdateAttrCols( pos, -((int)numCols) );
2730
2731 // ifdef'd out following patch from Paul Gammans
2732 #if 0
2733 // No need to touch row attributes, unless we
2734 // removed _all_ columns, in this case, we remove
2735 // all row attributes.
2736 // I hate to do this here, but the
2737 // needed data is not available inside UpdateAttrCols.
2738 if ( !GetNumberCols() )
2739 attrProvider->UpdateAttrRows( 0, -GetNumberRows() );
2740 #endif
2741 }
2742
2743 if ( !GetBatchCount() )
2744 {
2745 CalcDimensions();
2746 m_colWindow->Refresh();
2747 }
2748 }
2749 result = true;
2750 break;
2751 }
2752
2753 if (result && !GetBatchCount() )
2754 m_gridWin->Refresh();
2755
2756 return result;
2757 }
2758
2759 wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const
2760 {
2761 wxRegionIterator iter( reg );
2762 wxRect r;
2763
2764 wxArrayInt rowlabels;
2765
2766 int top, bottom;
2767 while ( iter )
2768 {
2769 r = iter.GetRect();
2770
2771 // TODO: remove this when we can...
2772 // There is a bug in wxMotif that gives garbage update
2773 // rectangles if you jump-scroll a long way by clicking the
2774 // scrollbar with middle button. This is a work-around
2775 //
2776 #if defined(__WXMOTIF__)
2777 int cw, ch;
2778 m_gridWin->GetClientSize( &cw, &ch );
2779 if ( r.GetTop() > ch )
2780 r.SetTop( 0 );
2781 r.SetBottom( wxMin( r.GetBottom(), ch ) );
2782 #endif
2783
2784 // logical bounds of update region
2785 //
2786 int dummy;
2787 CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
2788 CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
2789
2790 // find the row labels within these bounds
2791 //
2792 int row;
2793 for ( row = internalYToRow(top); row < m_numRows; row++ )
2794 {
2795 if ( GetRowBottom(row) < top )
2796 continue;
2797
2798 if ( GetRowTop(row) > bottom )
2799 break;
2800
2801 rowlabels.Add( row );
2802 }
2803
2804 ++iter;
2805 }
2806
2807 return rowlabels;
2808 }
2809
2810 wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const
2811 {
2812 wxRegionIterator iter( reg );
2813 wxRect r;
2814
2815 wxArrayInt colLabels;
2816
2817 int left, right;
2818 while ( iter )
2819 {
2820 r = iter.GetRect();
2821
2822 // TODO: remove this when we can...
2823 // There is a bug in wxMotif that gives garbage update
2824 // rectangles if you jump-scroll a long way by clicking the
2825 // scrollbar with middle button. This is a work-around
2826 //
2827 #if defined(__WXMOTIF__)
2828 int cw, ch;
2829 m_gridWin->GetClientSize( &cw, &ch );
2830 if ( r.GetLeft() > cw )
2831 r.SetLeft( 0 );
2832 r.SetRight( wxMin( r.GetRight(), cw ) );
2833 #endif
2834
2835 // logical bounds of update region
2836 //
2837 int dummy;
2838 CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
2839 CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
2840
2841 // find the cells within these bounds
2842 //
2843 int col;
2844 int colPos;
2845 for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ )
2846 {
2847 col = GetColAt( colPos );
2848
2849 if ( GetColRight(col) < left )
2850 continue;
2851
2852 if ( GetColLeft(col) > right )
2853 break;
2854
2855 colLabels.Add( col );
2856 }
2857
2858 ++iter;
2859 }
2860
2861 return colLabels;
2862 }
2863
2864 wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const
2865 {
2866 wxRegionIterator iter( reg );
2867 wxRect r;
2868
2869 wxGridCellCoordsArray cellsExposed;
2870
2871 int left, top, right, bottom;
2872 while ( iter )
2873 {
2874 r = iter.GetRect();
2875
2876 // TODO: remove this when we can...
2877 // There is a bug in wxMotif that gives garbage update
2878 // rectangles if you jump-scroll a long way by clicking the
2879 // scrollbar with middle button. This is a work-around
2880 //
2881 #if defined(__WXMOTIF__)
2882 int cw, ch;
2883 m_gridWin->GetClientSize( &cw, &ch );
2884 if ( r.GetTop() > ch ) r.SetTop( 0 );
2885 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
2886 r.SetRight( wxMin( r.GetRight(), cw ) );
2887 r.SetBottom( wxMin( r.GetBottom(), ch ) );
2888 #endif
2889
2890 // logical bounds of update region
2891 //
2892 CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
2893 CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
2894
2895 // find the cells within these bounds
2896 wxArrayInt cols;
2897 for ( int row = internalYToRow(top); row < m_numRows; row++ )
2898 {
2899 if ( GetRowBottom(row) <= top )
2900 continue;
2901
2902 if ( GetRowTop(row) > bottom )
2903 break;
2904
2905 // add all dirty cells in this row: notice that the columns which
2906 // are dirty don't depend on the row so we compute them only once
2907 // for the first dirty row and then reuse for all the next ones
2908 if ( cols.empty() )
2909 {
2910 // do determine the dirty columns
2911 for ( int pos = XToPos(left); pos <= XToPos(right); pos++ )
2912 cols.push_back(GetColAt(pos));
2913
2914 // if there are no dirty columns at all, nothing to do
2915 if ( cols.empty() )
2916 break;
2917 }
2918
2919 const size_t count = cols.size();
2920 for ( size_t n = 0; n < count; n++ )
2921 cellsExposed.Add(wxGridCellCoords(row, cols[n]));
2922 }
2923
2924 ++iter;
2925 }
2926
2927 return cellsExposed;
2928 }
2929
2930
2931 void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
2932 {
2933 int x, y, row;
2934 wxPoint pos( event.GetPosition() );
2935 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
2936
2937 if ( event.Dragging() )
2938 {
2939 if (!m_isDragging)
2940 {
2941 m_isDragging = true;
2942 m_rowLabelWin->CaptureMouse();
2943 }
2944
2945 if ( event.LeftIsDown() )
2946 {
2947 switch ( m_cursorMode )
2948 {
2949 case WXGRID_CURSOR_RESIZE_ROW:
2950 {
2951 int cw, ch, left, dummy;
2952 m_gridWin->GetClientSize( &cw, &ch );
2953 CalcUnscrolledPosition( 0, 0, &left, &dummy );
2954
2955 wxClientDC dc( m_gridWin );
2956 PrepareDC( dc );
2957 y = wxMax( y,
2958 GetRowTop(m_dragRowOrCol) +
2959 GetRowMinimalHeight(m_dragRowOrCol) );
2960 dc.SetLogicalFunction(wxINVERT);
2961 if ( m_dragLastPos >= 0 )
2962 {
2963 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
2964 }
2965 dc.DrawLine( left, y, left+cw, y );
2966 m_dragLastPos = y;
2967 }
2968 break;
2969
2970 case WXGRID_CURSOR_SELECT_ROW:
2971 {
2972 if ( (row = YToRow( y )) >= 0 )
2973 {
2974 if ( m_selection )
2975 m_selection->SelectRow(row, event);
2976 }
2977 }
2978 break;
2979
2980 // default label to suppress warnings about "enumeration value
2981 // 'xxx' not handled in switch
2982 default:
2983 break;
2984 }
2985 }
2986 return;
2987 }
2988
2989 if ( m_isDragging && (event.Entering() || event.Leaving()) )
2990 return;
2991
2992 if (m_isDragging)
2993 {
2994 if (m_rowLabelWin->HasCapture())
2995 m_rowLabelWin->ReleaseMouse();
2996 m_isDragging = false;
2997 }
2998
2999 // ------------ Entering or leaving the window
3000 //
3001 if ( event.Entering() || event.Leaving() )
3002 {
3003 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
3004 }
3005
3006 // ------------ Left button pressed
3007 //
3008 else if ( event.LeftDown() )
3009 {
3010 row = YToEdgeOfRow(y);
3011 if ( row != wxNOT_FOUND && CanDragRowSize(row) )
3012 {
3013 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
3014 }
3015 else // not a request to start resizing
3016 {
3017 row = YToRow(y);
3018 if ( row >= 0 &&
3019 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
3020 {
3021 if ( !event.ShiftDown() && !event.CmdDown() )
3022 ClearSelection();
3023 if ( m_selection )
3024 {
3025 if ( event.ShiftDown() )
3026 {
3027 m_selection->SelectBlock
3028 (
3029 m_currentCellCoords.GetRow(), 0,
3030 row, GetNumberCols() - 1,
3031 event
3032 );
3033 }
3034 else
3035 {
3036 m_selection->SelectRow(row, event);
3037 }
3038 }
3039
3040 ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
3041 }
3042 }
3043 }
3044
3045 // ------------ Left double click
3046 //
3047 else if (event.LeftDClick() )
3048 {
3049 row = YToEdgeOfRow(y);
3050 if ( row != wxNOT_FOUND && CanDragRowSize(row) )
3051 {
3052 // adjust row height depending on label text
3053 //
3054 // TODO: generate RESIZING event, see #10754
3055 AutoSizeRowLabelSize( row );
3056
3057 SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, row, -1, event);
3058
3059 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3060 m_dragLastPos = -1;
3061 }
3062 else // not on row separator or it's not resizeable
3063 {
3064 row = YToRow(y);
3065 if ( row >=0 &&
3066 !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
3067 {
3068 // no default action at the moment
3069 }
3070 }
3071 }
3072
3073 // ------------ Left button released
3074 //
3075 else if ( event.LeftUp() )
3076 {
3077 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3078 DoEndDragResizeRow(event);
3079
3080 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
3081 m_dragLastPos = -1;
3082 }
3083
3084 // ------------ Right button down
3085 //
3086 else if ( event.RightDown() )
3087 {
3088 row = YToRow(y);
3089 if ( row >=0 &&
3090 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
3091 {
3092 // no default action at the moment
3093 }
3094 }
3095
3096 // ------------ Right double click
3097 //
3098 else if ( event.RightDClick() )
3099 {
3100 row = YToRow(y);
3101 if ( row >= 0 &&
3102 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
3103 {
3104 // no default action at the moment
3105 }
3106 }
3107
3108 // ------------ No buttons down and mouse moving
3109 //
3110 else if ( event.Moving() )
3111 {
3112 m_dragRowOrCol = YToEdgeOfRow( y );
3113 if ( m_dragRowOrCol != wxNOT_FOUND )
3114 {
3115 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3116 {
3117 if ( CanDragRowSize(m_dragRowOrCol) )
3118 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false);
3119 }
3120 }
3121 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3122 {
3123 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false);
3124 }
3125 }
3126 }
3127
3128 void wxGrid::UpdateColumnSortingIndicator(int col)
3129 {
3130 wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" );
3131
3132 if ( m_useNativeHeader )
3133 GetGridColHeader()->UpdateColumn(col);
3134 else if ( m_nativeColumnLabels )
3135 m_colWindow->Refresh();
3136 //else: sorting indicator display not yet implemented in grid version
3137 }
3138
3139 void wxGrid::SetSortingColumn(int col, bool ascending)
3140 {
3141 if ( col == m_sortCol )
3142 {
3143 // we are already using this column for sorting (or not sorting at all)
3144 // but we might still change the sorting order, check for it
3145 if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending )
3146 {
3147 m_sortIsAscending = ascending;
3148
3149 UpdateColumnSortingIndicator(m_sortCol);
3150 }
3151 }
3152 else // we're changing the column used for sorting
3153 {
3154 const int sortColOld = m_sortCol;
3155
3156 // change it before updating the column as we want GetSortingColumn()
3157 // to return the correct new value
3158 m_sortCol = col;
3159
3160 if ( sortColOld != wxNOT_FOUND )
3161 UpdateColumnSortingIndicator(sortColOld);
3162
3163 if ( m_sortCol != wxNOT_FOUND )
3164 {
3165 m_sortIsAscending = ascending;
3166 UpdateColumnSortingIndicator(m_sortCol);
3167 }
3168 }
3169 }
3170
3171 void wxGrid::DoColHeaderClick(int col)
3172 {
3173 // we consider that the grid was resorted if this event is processed and
3174 // not vetoed
3175 if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 )
3176 {
3177 SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true);
3178 Refresh();
3179 }
3180 }
3181
3182 void wxGrid::DoStartResizeCol(int col)
3183 {
3184 m_dragRowOrCol = col;
3185 m_dragLastPos = -1;
3186 DoUpdateResizeColWidth(GetColWidth(m_dragRowOrCol));
3187 }
3188
3189 void wxGrid::DoUpdateResizeCol(int x)
3190 {
3191 int cw, ch, dummy, top;
3192 m_gridWin->GetClientSize( &cw, &ch );
3193 CalcUnscrolledPosition( 0, 0, &dummy, &top );
3194
3195 wxClientDC dc( m_gridWin );
3196 PrepareDC( dc );
3197
3198 x = wxMax( x, GetColLeft(m_dragRowOrCol) + GetColMinimalWidth(m_dragRowOrCol));
3199 dc.SetLogicalFunction(wxINVERT);
3200 if ( m_dragLastPos >= 0 )
3201 {
3202 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch );
3203 }
3204 dc.DrawLine( x, top, x, top + ch );
3205 m_dragLastPos = x;
3206 }
3207
3208 void wxGrid::DoUpdateResizeColWidth(int w)
3209 {
3210 DoUpdateResizeCol(GetColLeft(m_dragRowOrCol) + w);
3211 }
3212
3213 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
3214 {
3215 int x, y;
3216 wxPoint pos( event.GetPosition() );
3217 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
3218
3219 int col = XToCol(x);
3220 if ( event.Dragging() )
3221 {
3222 if (!m_isDragging)
3223 {
3224 m_isDragging = true;
3225 GetColLabelWindow()->CaptureMouse();
3226
3227 if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 )
3228 DoStartMoveCol(col);
3229 }
3230
3231 if ( event.LeftIsDown() )
3232 {
3233 switch ( m_cursorMode )
3234 {
3235 case WXGRID_CURSOR_RESIZE_COL:
3236 DoUpdateResizeCol(x);
3237 break;
3238
3239 case WXGRID_CURSOR_SELECT_COL:
3240 {
3241 if ( col != -1 )
3242 {
3243 if ( m_selection )
3244 m_selection->SelectCol(col, event);
3245 }
3246 }
3247 break;
3248
3249 case WXGRID_CURSOR_MOVE_COL:
3250 {
3251 int posNew = XToPos(x);
3252 int colNew = GetColAt(posNew);
3253
3254 // determine the position of the drop marker
3255 int markerX;
3256 if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) )
3257 markerX = GetColRight(colNew);
3258 else
3259 markerX = GetColLeft(colNew);
3260
3261 if ( markerX != m_dragLastPos )
3262 {
3263 wxClientDC dc( GetColLabelWindow() );
3264 DoPrepareDC(dc);
3265
3266 int cw, ch;
3267 GetColLabelWindow()->GetClientSize( &cw, &ch );
3268
3269 markerX++;
3270
3271 //Clean up the last indicator
3272 if ( m_dragLastPos >= 0 )
3273 {
3274 wxPen pen( GetColLabelWindow()->GetBackgroundColour(), 2 );
3275 dc.SetPen(pen);
3276 dc.DrawLine( m_dragLastPos + 1, 0, m_dragLastPos + 1, ch );
3277 dc.SetPen(wxNullPen);
3278
3279 if ( XToCol( m_dragLastPos ) != -1 )
3280 DrawColLabel( dc, XToCol( m_dragLastPos ) );
3281 }
3282
3283 const wxColour *color;
3284 //Moving to the same place? Don't draw a marker
3285 if ( colNew == m_dragRowOrCol )
3286 color = wxLIGHT_GREY;
3287 else
3288 color = wxBLUE;
3289
3290 //Draw the marker
3291 wxPen pen( *color, 2 );
3292 dc.SetPen(pen);
3293
3294 dc.DrawLine( markerX, 0, markerX, ch );
3295
3296 dc.SetPen(wxNullPen);
3297
3298 m_dragLastPos = markerX - 1;
3299 }
3300 }
3301 break;
3302
3303 // default label to suppress warnings about "enumeration value
3304 // 'xxx' not handled in switch
3305 default:
3306 break;
3307 }
3308 }
3309 return;
3310 }
3311
3312 if ( m_isDragging && (event.Entering() || event.Leaving()) )
3313 return;
3314
3315 if (m_isDragging)
3316 {
3317 if (GetColLabelWindow()->HasCapture())
3318 GetColLabelWindow()->ReleaseMouse();
3319 m_isDragging = false;
3320 }
3321
3322 // ------------ Entering or leaving the window
3323 //
3324 if ( event.Entering() || event.Leaving() )
3325 {
3326 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3327 }
3328
3329 // ------------ Left button pressed
3330 //
3331 else if ( event.LeftDown() )
3332 {
3333 int col = XToEdgeOfCol(x);
3334 if ( col != wxNOT_FOUND && CanDragColSize(col) )
3335 {
3336 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow());
3337 }
3338 else // not a request to start resizing
3339 {
3340 col = XToCol(x);
3341 if ( col >= 0 &&
3342 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
3343 {
3344 if ( m_canDragColMove )
3345 {
3346 //Show button as pressed
3347 wxClientDC dc( GetColLabelWindow() );
3348 int colLeft = GetColLeft( col );
3349 int colRight = GetColRight( col ) - 1;
3350 dc.SetPen( wxPen( GetColLabelWindow()->GetBackgroundColour(), 1 ) );
3351 dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 );
3352 dc.DrawLine( colLeft, 1, colRight, 1 );
3353
3354 ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, GetColLabelWindow());
3355 }
3356 else
3357 {
3358 if ( !event.ShiftDown() && !event.CmdDown() )
3359 ClearSelection();
3360 if ( m_selection )
3361 {
3362 if ( event.ShiftDown() )
3363 {
3364 m_selection->SelectBlock
3365 (
3366 0, m_currentCellCoords.GetCol(),
3367 GetNumberRows() - 1, col,
3368 event
3369 );
3370 }
3371 else
3372 {
3373 m_selection->SelectCol(col, event);
3374 }
3375 }
3376
3377 ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, GetColLabelWindow());
3378 }
3379 }
3380 }
3381 }
3382
3383 // ------------ Left double click
3384 //
3385 if ( event.LeftDClick() )
3386 {
3387 const int colEdge = XToEdgeOfCol(x);
3388 if ( colEdge == -1 )
3389 {
3390 if ( col >= 0 &&
3391 ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
3392 {
3393 // no default action at the moment
3394 }
3395 }
3396 else
3397 {
3398 // adjust column width depending on label text
3399 //
3400 // TODO: generate RESIZING event, see #10754
3401 AutoSizeColLabelSize( colEdge );
3402
3403 SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, colEdge, event);
3404
3405 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3406 m_dragLastPos = -1;
3407 }
3408 }
3409
3410 // ------------ Left button released
3411 //
3412 else if ( event.LeftUp() )
3413 {
3414 switch ( m_cursorMode )
3415 {
3416 case WXGRID_CURSOR_RESIZE_COL:
3417 DoEndDragResizeCol(event);
3418 break;
3419
3420 case WXGRID_CURSOR_MOVE_COL:
3421 if ( m_dragLastPos == -1 || col == m_dragRowOrCol )
3422 {
3423 // the column didn't actually move anywhere
3424 if ( col != -1 )
3425 DoColHeaderClick(col);
3426 m_colWindow->Refresh(); // "unpress" the column
3427 }
3428 else
3429 {
3430 DoEndMoveCol(XToPos(x));
3431 }
3432 break;
3433
3434 case WXGRID_CURSOR_SELECT_COL:
3435 case WXGRID_CURSOR_SELECT_CELL:
3436 case WXGRID_CURSOR_RESIZE_ROW:
3437 case WXGRID_CURSOR_SELECT_ROW:
3438 if ( col != -1 )
3439 DoColHeaderClick(col);
3440 break;
3441 }
3442
3443 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3444 m_dragLastPos = -1;
3445 }
3446
3447 // ------------ Right button down
3448 //
3449 else if ( event.RightDown() )
3450 {
3451 if ( col >= 0 &&
3452 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
3453 {
3454 // no default action at the moment
3455 }
3456 }
3457
3458 // ------------ Right double click
3459 //
3460 else if ( event.RightDClick() )
3461 {
3462 if ( col >= 0 &&
3463 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
3464 {
3465 // no default action at the moment
3466 }
3467 }
3468
3469 // ------------ No buttons down and mouse moving
3470 //
3471 else if ( event.Moving() )
3472 {
3473 m_dragRowOrCol = XToEdgeOfCol( x );
3474 if ( m_dragRowOrCol >= 0 )
3475 {
3476 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3477 {
3478 if ( CanDragColSize(m_dragRowOrCol) )
3479 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false);
3480 }
3481 }
3482 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3483 {
3484 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow(), false);
3485 }
3486 }
3487 }
3488
3489 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
3490 {
3491 if ( event.LeftDown() )
3492 {
3493 // indicate corner label by having both row and
3494 // col args == -1
3495 //
3496 if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
3497 {
3498 SelectAll();
3499 }
3500 }
3501 else if ( event.LeftDClick() )
3502 {
3503 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
3504 }
3505 else if ( event.RightDown() )
3506 {
3507 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
3508 {
3509 // no default action at the moment
3510 }
3511 }
3512 else if ( event.RightDClick() )
3513 {
3514 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
3515 {
3516 // no default action at the moment
3517 }
3518 }
3519 }
3520
3521 void wxGrid::CancelMouseCapture()
3522 {
3523 // cancel operation currently in progress, whatever it is
3524 if ( m_winCapture )
3525 {
3526 m_isDragging = false;
3527 m_startDragPos = wxDefaultPosition;
3528
3529 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
3530 m_winCapture->SetCursor( *wxSTANDARD_CURSOR );
3531 m_winCapture = NULL;
3532
3533 // remove traces of whatever we drew on screen
3534 Refresh();
3535 }
3536 }
3537
3538 void wxGrid::ChangeCursorMode(CursorMode mode,
3539 wxWindow *win,
3540 bool captureMouse)
3541 {
3542 #if wxUSE_LOG_TRACE
3543 static const wxChar *cursorModes[] =
3544 {
3545 wxT("SELECT_CELL"),
3546 wxT("RESIZE_ROW"),
3547 wxT("RESIZE_COL"),
3548 wxT("SELECT_ROW"),
3549 wxT("SELECT_COL"),
3550 wxT("MOVE_COL"),
3551 };
3552
3553 wxLogTrace(wxT("grid"),
3554 wxT("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
3555 win == m_colWindow ? wxT("colLabelWin")
3556 : win ? wxT("rowLabelWin")
3557 : wxT("gridWin"),
3558 cursorModes[m_cursorMode], cursorModes[mode]);
3559 #endif // wxUSE_LOG_TRACE
3560
3561 if ( mode == m_cursorMode &&
3562 win == m_winCapture &&
3563 captureMouse == (m_winCapture != NULL))
3564 return;
3565
3566 if ( !win )
3567 {
3568 // by default use the grid itself
3569 win = m_gridWin;
3570 }
3571
3572 if ( m_winCapture )
3573 {
3574 m_winCapture->ReleaseMouse();
3575 m_winCapture = NULL;
3576 }
3577
3578 m_cursorMode = mode;
3579
3580 switch ( m_cursorMode )
3581 {
3582 case WXGRID_CURSOR_RESIZE_ROW:
3583 win->SetCursor( m_rowResizeCursor );
3584 break;
3585
3586 case WXGRID_CURSOR_RESIZE_COL:
3587 win->SetCursor( m_colResizeCursor );
3588 break;
3589
3590 case WXGRID_CURSOR_MOVE_COL:
3591 win->SetCursor( wxCursor(wxCURSOR_HAND) );
3592 break;
3593
3594 default:
3595 win->SetCursor( *wxSTANDARD_CURSOR );
3596 break;
3597 }
3598
3599 // we need to capture mouse when resizing
3600 bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
3601 m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
3602
3603 if ( captureMouse && resize )
3604 {
3605 win->CaptureMouse();
3606 m_winCapture = win;
3607 }
3608 }
3609
3610 // ----------------------------------------------------------------------------
3611 // grid mouse event processing
3612 // ----------------------------------------------------------------------------
3613
3614 void
3615 wxGrid::DoGridCellDrag(wxMouseEvent& event,
3616 const wxGridCellCoords& coords,
3617 bool isFirstDrag)
3618 {
3619 if ( coords == wxGridNoCellCoords )
3620 return; // we're outside any valid cell
3621
3622 // Hide the edit control, so it won't interfere with drag-shrinking.
3623 if ( IsCellEditControlShown() )
3624 {
3625 HideCellEditControl();
3626 SaveEditControlValue();
3627 }
3628
3629 switch ( event.GetModifiers() )
3630 {
3631 case wxMOD_CMD:
3632 if ( m_selectedBlockCorner == wxGridNoCellCoords)
3633 m_selectedBlockCorner = coords;
3634 UpdateBlockBeingSelected(m_selectedBlockCorner, coords);
3635 break;
3636
3637 case wxMOD_NONE:
3638 if ( CanDragCell() )
3639 {
3640 if ( isFirstDrag )
3641 {
3642 if ( m_selectedBlockCorner == wxGridNoCellCoords)
3643 m_selectedBlockCorner = coords;
3644
3645 SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event);
3646 return;
3647 }
3648 }
3649
3650 UpdateBlockBeingSelected(m_currentCellCoords, coords);
3651 break;
3652
3653 default:
3654 // we don't handle the other key modifiers
3655 event.Skip();
3656 }
3657 }
3658
3659 void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper)
3660 {
3661 wxClientDC dc(m_gridWin);
3662 PrepareDC(dc);
3663 dc.SetLogicalFunction(wxINVERT);
3664
3665 const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)),
3666 m_gridWin->GetClientSize());
3667
3668 // erase the previously drawn line, if any
3669 if ( m_dragLastPos >= 0 )
3670 oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos);
3671
3672 // we need the vertical position for rows and horizontal for columns here
3673 m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition()));
3674
3675 // don't allow resizing beneath the minimal size
3676 const int posMin = oper.GetLineStartPos(this, m_dragRowOrCol) +
3677 oper.GetMinimalLineSize(this, m_dragRowOrCol);
3678 if ( m_dragLastPos < posMin )
3679 m_dragLastPos = posMin;
3680
3681 // and draw it at the new position
3682 oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos);
3683 }
3684
3685 void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords)
3686 {
3687 if ( !m_isDragging )
3688 {
3689 // Don't start doing anything until the mouse has been dragged far
3690 // enough
3691 const wxPoint& pt = event.GetPosition();
3692 if ( m_startDragPos == wxDefaultPosition )
3693 {
3694 m_startDragPos = pt;
3695 return;
3696 }
3697
3698 if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY &&
3699 abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY )
3700 return;
3701 }
3702
3703 const bool isFirstDrag = !m_isDragging;
3704 m_isDragging = true;
3705
3706 switch ( m_cursorMode )
3707 {
3708 case WXGRID_CURSOR_SELECT_CELL:
3709 DoGridCellDrag(event, coords, isFirstDrag);
3710 break;
3711
3712 case WXGRID_CURSOR_RESIZE_ROW:
3713 DoGridLineDrag(event, wxGridRowOperations());
3714 break;
3715
3716 case WXGRID_CURSOR_RESIZE_COL:
3717 DoGridLineDrag(event, wxGridColumnOperations());
3718 break;
3719
3720 default:
3721 event.Skip();
3722 }
3723
3724 if ( isFirstDrag )
3725 {
3726 m_winCapture = m_gridWin;
3727 m_winCapture->CaptureMouse();
3728 }
3729 }
3730
3731 void
3732 wxGrid::DoGridCellLeftDown(wxMouseEvent& event,
3733 const wxGridCellCoords& coords,
3734 const wxPoint& pos)
3735 {
3736 if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) )
3737 {
3738 // event handled by user code, no need to do anything here
3739 return;
3740 }
3741
3742 if ( !event.CmdDown() )
3743 ClearSelection();
3744
3745 if ( event.ShiftDown() )
3746 {
3747 if ( m_selection )
3748 {
3749 m_selection->SelectBlock(m_currentCellCoords, coords, event);
3750 m_selectedBlockCorner = coords;
3751 }
3752 }
3753 else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 )
3754 {
3755 DisableCellEditControl();
3756 MakeCellVisible( coords );
3757
3758 if ( event.CmdDown() )
3759 {
3760 if ( m_selection )
3761 {
3762 m_selection->ToggleCellSelection(coords, event);
3763 }
3764
3765 m_selectedBlockTopLeft = wxGridNoCellCoords;
3766 m_selectedBlockBottomRight = wxGridNoCellCoords;
3767 m_selectedBlockCorner = coords;
3768 }
3769 else
3770 {
3771 m_waitForSlowClick = m_currentCellCoords == coords &&
3772 coords != wxGridNoCellCoords;
3773 SetCurrentCell( coords );
3774 }
3775 }
3776 }
3777
3778 void
3779 wxGrid::DoGridCellLeftDClick(wxMouseEvent& event,
3780 const wxGridCellCoords& coords,
3781 const wxPoint& pos)
3782 {
3783 if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 )
3784 {
3785 if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) )
3786 {
3787 // we want double click to select a cell and start editing
3788 // (i.e. to behave in same way as sequence of two slow clicks):
3789 m_waitForSlowClick = true;
3790 }
3791 }
3792 }
3793
3794 void
3795 wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords)
3796 {
3797 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3798 {
3799 if (m_winCapture)
3800 {
3801 m_winCapture->ReleaseMouse();
3802 m_winCapture = NULL;
3803 }
3804
3805 if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() )
3806 {
3807 ClearSelection();
3808 EnableCellEditControl();
3809
3810 wxGridCellAttr *attr = GetCellAttr(coords);
3811 wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol());
3812 editor->StartingClick();
3813 editor->DecRef();
3814 attr->DecRef();
3815
3816 m_waitForSlowClick = false;
3817 }
3818 else if ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
3819 m_selectedBlockBottomRight != wxGridNoCellCoords )
3820 {
3821 if ( m_selection )
3822 {
3823 m_selection->SelectBlock( m_selectedBlockTopLeft,
3824 m_selectedBlockBottomRight,
3825 event );
3826 }
3827
3828 m_selectedBlockTopLeft = wxGridNoCellCoords;
3829 m_selectedBlockBottomRight = wxGridNoCellCoords;
3830
3831 // Show the edit control, if it has been hidden for
3832 // drag-shrinking.
3833 ShowCellEditControl();
3834 }
3835 }
3836 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3837 {
3838 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3839 DoEndDragResizeRow(event);
3840 }
3841 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
3842 {
3843 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3844 DoEndDragResizeCol(event);
3845 }
3846
3847 m_dragLastPos = -1;
3848 }
3849
3850 void
3851 wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event),
3852 const wxGridCellCoords& coords,
3853 const wxPoint& pos)
3854 {
3855 if ( coords.GetRow() < 0 || coords.GetCol() < 0 )
3856 {
3857 // out of grid cell area
3858 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3859 return;
3860 }
3861
3862 int dragRow = YToEdgeOfRow( pos.y );
3863 int dragCol = XToEdgeOfCol( pos.x );
3864
3865 // Dragging on the corner of a cell to resize in both
3866 // directions is not implemented yet...
3867 //
3868 if ( dragRow >= 0 && dragCol >= 0 )
3869 {
3870 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3871 return;
3872 }
3873
3874 if ( dragRow >= 0 && CanDragGridSize() && CanDragRowSize(dragRow) )
3875 {
3876 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3877 {
3878 m_dragRowOrCol = dragRow;
3879 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false);
3880 }
3881 }
3882 // When using the native header window we can only resize the columns by
3883 // dragging the dividers in it because we can't make it enter into the
3884 // column resizing mode programmatically
3885 else if ( dragCol >= 0 && !m_useNativeHeader &&
3886 CanDragGridSize() && CanDragColSize(dragCol) )
3887 {
3888 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3889 {
3890 m_dragRowOrCol = dragCol;
3891 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false);
3892 }
3893 }
3894 else // Neither on a row or col edge
3895 {
3896 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3897 {
3898 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3899 }
3900 }
3901 }
3902
3903 void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
3904 {
3905 const wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
3906
3907 // coordinates of the cell under mouse
3908 wxGridCellCoords coords = XYToCell(pos);
3909
3910 int cell_rows, cell_cols;
3911 GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols );
3912 if ( (cell_rows < 0) || (cell_cols < 0) )
3913 {
3914 coords.SetRow(coords.GetRow() + cell_rows);
3915 coords.SetCol(coords.GetCol() + cell_cols);
3916 }
3917
3918 if ( event.Dragging() )
3919 {
3920 if ( event.LeftIsDown() )
3921 DoGridDragEvent(event, coords);
3922 else
3923 event.Skip();
3924 return;
3925 }
3926
3927 m_isDragging = false;
3928 m_startDragPos = wxDefaultPosition;
3929
3930 // VZ: if we do this, the mode is reset to WXGRID_CURSOR_SELECT_CELL
3931 // immediately after it becomes WXGRID_CURSOR_RESIZE_ROW/COL under
3932 // wxGTK
3933 #if 0
3934 if ( event.Entering() || event.Leaving() )
3935 {
3936 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
3937 m_gridWin->SetCursor( *wxSTANDARD_CURSOR );
3938 }
3939 #endif // 0
3940
3941 // deal with various button presses
3942 if ( event.IsButton() )
3943 {
3944 if ( coords != wxGridNoCellCoords )
3945 {
3946 DisableCellEditControl();
3947
3948 if ( event.LeftDown() )
3949 DoGridCellLeftDown(event, coords, pos);
3950 else if ( event.LeftDClick() )
3951 DoGridCellLeftDClick(event, coords, pos);
3952 else if ( event.RightDown() )
3953 SendEvent(wxEVT_GRID_CELL_RIGHT_CLICK, coords, event);
3954 else if ( event.RightDClick() )
3955 SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event);
3956 }
3957
3958 // this one should be called even if we're not over any cell
3959 if ( event.LeftUp() )
3960 {
3961 DoGridCellLeftUp(event, coords);
3962 }
3963 }
3964 else if ( event.Moving() )
3965 {
3966 DoGridMouseMoveEvent(event, coords, pos);
3967 }
3968 else // unknown mouse event?
3969 {
3970 event.Skip();
3971 }
3972 }
3973
3974 // this function returns true only if the size really changed
3975 bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper)
3976 {
3977 if ( m_dragLastPos == -1 )
3978 return false;
3979
3980 const wxGridOperations& doper = oper.Dual();
3981
3982 const wxSize size = m_gridWin->GetClientSize();
3983
3984 const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0));
3985
3986 // erase the last line we drew
3987 wxClientDC dc(m_gridWin);
3988 PrepareDC(dc);
3989 dc.SetLogicalFunction(wxINVERT);
3990
3991 const int posLineStart = oper.Select(ptOrigin);
3992 const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size);
3993
3994 oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos);
3995
3996 // temporarily hide the edit control before resizing
3997 HideCellEditControl();
3998 SaveEditControlValue();
3999
4000 // do resize the line
4001 const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol);
4002 const int lineSizeOld = oper.GetLineSize(this, m_dragRowOrCol);
4003 oper.SetLineSize(this, m_dragRowOrCol,
4004 wxMax(m_dragLastPos - lineStart,
4005 oper.GetMinimalLineSize(this, m_dragRowOrCol)));
4006 const bool
4007 sizeChanged = oper.GetLineSize(this, m_dragRowOrCol) != lineSizeOld;
4008
4009 m_dragLastPos = -1;
4010
4011 // refresh now if we're not frozen
4012 if ( !GetBatchCount() )
4013 {
4014 // we need to refresh everything beyond the resized line in the header
4015 // window
4016
4017 // get the position from which to refresh in the other direction
4018 wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0)));
4019 rect.SetPosition(CalcScrolledPosition(rect.GetPosition()));
4020
4021 // we only need the ordinate (for rows) or abscissa (for columns) here,
4022 // and need to cover the entire window in the other direction
4023 oper.Select(rect) = 0;
4024
4025 wxRect rectHeader(rect.GetPosition(),
4026 oper.MakeSize
4027 (
4028 oper.GetHeaderWindowSize(this),
4029 doper.Select(size) - doper.Select(rect)
4030 ));
4031
4032 oper.GetHeaderWindow(this)->Refresh(true, &rectHeader);
4033
4034
4035 // also refresh the grid window: extend the rectangle
4036 if ( m_table )
4037 {
4038 oper.SelectSize(rect) = oper.Select(size);
4039
4040 int subtractLines = 0;
4041 const int lineStart = oper.PosToLine(this, posLineStart);
4042 if ( lineStart >= 0 )
4043 {
4044 // ensure that if we have a multi-cell block we redraw all of
4045 // it by increasing the refresh area to cover it entirely if a
4046 // part of it is affected
4047 const int lineEnd = oper.PosToLine(this, posLineEnd, true);
4048 for ( int line = lineStart; line < lineEnd; line++ )
4049 {
4050 int cellLines = oper.Select(
4051 GetCellSize(oper.MakeCoords(m_dragRowOrCol, line)));
4052 if ( cellLines < subtractLines )
4053 subtractLines = cellLines;
4054 }
4055 }
4056
4057 int startPos =
4058 oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines);
4059 startPos = doper.CalcScrolledPosition(this, startPos);
4060
4061 doper.Select(rect) = startPos;
4062 doper.SelectSize(rect) = doper.Select(size) - startPos;
4063
4064 m_gridWin->Refresh(false, &rect);
4065 }
4066 }
4067
4068 // show the edit control back again
4069 ShowCellEditControl();
4070
4071 return sizeChanged;
4072 }
4073
4074 void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event)
4075 {
4076 // TODO: generate RESIZING event, see #10754
4077
4078 if ( DoEndDragResizeLine(wxGridRowOperations()) )
4079 SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event);
4080 }
4081
4082 void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event)
4083 {
4084 // TODO: generate RESIZING event, see #10754
4085
4086 if ( DoEndDragResizeLine(wxGridColumnOperations()) )
4087 SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event);
4088 }
4089
4090 void wxGrid::DoStartMoveCol(int col)
4091 {
4092 m_dragRowOrCol = col;
4093 }
4094
4095 void wxGrid::DoEndMoveCol(int pos)
4096 {
4097 wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" );
4098
4099 if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 )
4100 SetColPos(m_dragRowOrCol, pos);
4101 //else: vetoed by user
4102
4103 m_dragRowOrCol = -1;
4104 }
4105
4106 void wxGrid::RefreshAfterColPosChange()
4107 {
4108 // recalculate the column rights as the column positions have changed,
4109 // unless we calculate them dynamically because all columns widths are the
4110 // same and it's easy to do
4111 if ( !m_colWidths.empty() )
4112 {
4113 int colRight = 0;
4114 for ( int colPos = 0; colPos < m_numCols; colPos++ )
4115 {
4116 int colID = GetColAt( colPos );
4117
4118 colRight += m_colWidths[colID];
4119 m_colRights[colID] = colRight;
4120 }
4121 }
4122
4123 // and make the changes visible
4124 if ( m_useNativeHeader )
4125 {
4126 if ( m_colAt.empty() )
4127 GetGridColHeader()->ResetColumnsOrder();
4128 else
4129 GetGridColHeader()->SetColumnsOrder(m_colAt);
4130 }
4131 else
4132 {
4133 m_colWindow->Refresh();
4134 }
4135 m_gridWin->Refresh();
4136 }
4137
4138 void wxGrid::SetColumnsOrder(const wxArrayInt& order)
4139 {
4140 m_colAt = order;
4141
4142 RefreshAfterColPosChange();
4143 }
4144
4145 void wxGrid::SetColPos(int idx, int pos)
4146 {
4147 // we're going to need m_colAt now, initialize it if needed
4148 if ( m_colAt.empty() )
4149 {
4150 m_colAt.reserve(m_numCols);
4151 for ( int i = 0; i < m_numCols; i++ )
4152 m_colAt.push_back(i);
4153 }
4154
4155 wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos);
4156
4157 RefreshAfterColPosChange();
4158 }
4159
4160 void wxGrid::ResetColPos()
4161 {
4162 m_colAt.clear();
4163
4164 RefreshAfterColPosChange();
4165 }
4166
4167 void wxGrid::EnableDragColMove( bool enable )
4168 {
4169 if ( m_canDragColMove == enable )
4170 return;
4171
4172 if ( m_useNativeHeader )
4173 {
4174 // update all columns to make them [not] reorderable
4175 GetGridColHeader()->SetColumnCount(m_numCols);
4176 }
4177
4178 m_canDragColMove = enable;
4179
4180 // we use to call ResetColPos() from here if !enable but this doesn't seem
4181 // right as it would mean there would be no way to "freeze" the current
4182 // columns order by disabling moving them after putting them in the desired
4183 // order, whereas now you can always call ResetColPos() manually if needed
4184 }
4185
4186
4187 //
4188 // ------ interaction with data model
4189 //
4190 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
4191 {
4192 switch ( msg.GetId() )
4193 {
4194 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
4195 return GetModelValues();
4196
4197 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
4198 return SetModelValues();
4199
4200 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
4201 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
4202 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
4203 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
4204 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
4205 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
4206 return Redimension( msg );
4207
4208 default:
4209 return false;
4210 }
4211 }
4212
4213 // The behaviour of this function depends on the grid table class
4214 // Clear() function. For the default wxGridStringTable class the
4215 // behaviour is to replace all cell contents with wxEmptyString but
4216 // not to change the number of rows or cols.
4217 //
4218 void wxGrid::ClearGrid()
4219 {
4220 if ( m_table )
4221 {
4222 if (IsCellEditControlEnabled())
4223 DisableCellEditControl();
4224
4225 m_table->Clear();
4226 if (!GetBatchCount())
4227 m_gridWin->Refresh();
4228 }
4229 }
4230
4231 bool
4232 wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t),
4233 int pos, int num, bool WXUNUSED(updateLabels) )
4234 {
4235 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
4236
4237 if ( !m_table )
4238 return false;
4239
4240 if ( IsCellEditControlEnabled() )
4241 DisableCellEditControl();
4242
4243 return (m_table->*funcModify)(pos, num);
4244
4245 // the table will have sent the results of the insert row
4246 // operation to this view object as a grid table message
4247 }
4248
4249 bool
4250 wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t),
4251 int num, bool WXUNUSED(updateLabels))
4252 {
4253 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
4254
4255 if ( !m_table )
4256 return false;
4257
4258 return (m_table->*funcAppend)(num);
4259 }
4260
4261 // ----------------------------------------------------------------------------
4262 // event generation helpers
4263 // ----------------------------------------------------------------------------
4264
4265 void
4266 wxGrid::SendGridSizeEvent(wxEventType type,
4267 int row, int col,
4268 const wxMouseEvent& mouseEv)
4269 {
4270 int rowOrCol = row == -1 ? col : row;
4271
4272 wxGridSizeEvent gridEvt( GetId(),
4273 type,
4274 this,
4275 rowOrCol,
4276 mouseEv.GetX() + GetRowLabelSize(),
4277 mouseEv.GetY() + GetColLabelSize(),
4278 mouseEv);
4279
4280 GetEventHandler()->ProcessEvent(gridEvt);
4281 }
4282
4283 // Generate a grid event based on a mouse event and return:
4284 // -1 if the event was vetoed
4285 // +1 if the event was processed (but not vetoed)
4286 // 0 if the event wasn't handled
4287 int
4288 wxGrid::SendEvent(const wxEventType type,
4289 int row, int col,
4290 const wxMouseEvent& mouseEv)
4291 {
4292 bool claimed, vetoed;
4293
4294 if ( type == wxEVT_GRID_RANGE_SELECT )
4295 {
4296 // Right now, it should _never_ end up here!
4297 wxGridRangeSelectEvent gridEvt( GetId(),
4298 type,
4299 this,
4300 m_selectedBlockTopLeft,
4301 m_selectedBlockBottomRight,
4302 true,
4303 mouseEv);
4304
4305 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4306 vetoed = !gridEvt.IsAllowed();
4307 }
4308 else if ( type == wxEVT_GRID_LABEL_LEFT_CLICK ||
4309 type == wxEVT_GRID_LABEL_LEFT_DCLICK ||
4310 type == wxEVT_GRID_LABEL_RIGHT_CLICK ||
4311 type == wxEVT_GRID_LABEL_RIGHT_DCLICK )
4312 {
4313 wxPoint pos = mouseEv.GetPosition();
4314
4315 if ( mouseEv.GetEventObject() == GetGridRowLabelWindow() )
4316 pos.y += GetColLabelSize();
4317 if ( mouseEv.GetEventObject() == GetGridColLabelWindow() )
4318 pos.x += GetRowLabelSize();
4319
4320 wxGridEvent gridEvt( GetId(),
4321 type,
4322 this,
4323 row, col,
4324 pos.x,
4325 pos.y,
4326 false,
4327 mouseEv);
4328 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4329 vetoed = !gridEvt.IsAllowed();
4330 }
4331 else
4332 {
4333 wxGridEvent gridEvt( GetId(),
4334 type,
4335 this,
4336 row, col,
4337 mouseEv.GetX() + GetRowLabelSize(),
4338 mouseEv.GetY() + GetColLabelSize(),
4339 false,
4340 mouseEv);
4341 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4342 vetoed = !gridEvt.IsAllowed();
4343 }
4344
4345 // A Veto'd event may not be `claimed' so test this first
4346 if (vetoed)
4347 return -1;
4348
4349 return claimed ? 1 : 0;
4350 }
4351
4352 // Generate a grid event of specified type, return value same as above
4353 //
4354 int
4355 wxGrid::SendEvent(const wxEventType type, int row, int col, const wxString& s)
4356 {
4357 wxGridEvent gridEvt( GetId(), type, this, row, col );
4358 gridEvt.SetString(s);
4359
4360 const bool claimed = GetEventHandler()->ProcessEvent(gridEvt);
4361
4362 // A Veto'd event may not be `claimed' so test this first
4363 if ( !gridEvt.IsAllowed() )
4364 return -1;
4365
4366 return claimed ? 1 : 0;
4367 }
4368
4369 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
4370 {
4371 // needed to prevent zillions of paint events on MSW
4372 wxPaintDC dc(this);
4373 }
4374
4375 void wxGrid::Refresh(bool eraseb, const wxRect* rect)
4376 {
4377 // Don't do anything if between Begin/EndBatch...
4378 // EndBatch() will do all this on the last nested one anyway.
4379 if ( m_created && !GetBatchCount() )
4380 {
4381 // Refresh to get correct scrolled position:
4382 wxScrolledWindow::Refresh(eraseb, rect);
4383
4384 if (rect)
4385 {
4386 int rect_x, rect_y, rectWidth, rectHeight;
4387 int width_label, width_cell, height_label, height_cell;
4388 int x, y;
4389
4390 // Copy rectangle can get scroll offsets..
4391 rect_x = rect->GetX();
4392 rect_y = rect->GetY();
4393 rectWidth = rect->GetWidth();
4394 rectHeight = rect->GetHeight();
4395
4396 width_label = m_rowLabelWidth - rect_x;
4397 if (width_label > rectWidth)
4398 width_label = rectWidth;
4399
4400 height_label = m_colLabelHeight - rect_y;
4401 if (height_label > rectHeight)
4402 height_label = rectHeight;
4403
4404 if (rect_x > m_rowLabelWidth)
4405 {
4406 x = rect_x - m_rowLabelWidth;
4407 width_cell = rectWidth;
4408 }
4409 else
4410 {
4411 x = 0;
4412 width_cell = rectWidth - (m_rowLabelWidth - rect_x);
4413 }
4414
4415 if (rect_y > m_colLabelHeight)
4416 {
4417 y = rect_y - m_colLabelHeight;
4418 height_cell = rectHeight;
4419 }
4420 else
4421 {
4422 y = 0;
4423 height_cell = rectHeight - (m_colLabelHeight - rect_y);
4424 }
4425
4426 // Paint corner label part intersecting rect.
4427 if ( width_label > 0 && height_label > 0 )
4428 {
4429 wxRect anotherrect(rect_x, rect_y, width_label, height_label);
4430 m_cornerLabelWin->Refresh(eraseb, &anotherrect);
4431 }
4432
4433 // Paint col labels part intersecting rect.
4434 if ( width_cell > 0 && height_label > 0 )
4435 {
4436 wxRect anotherrect(x, rect_y, width_cell, height_label);
4437 m_colWindow->Refresh(eraseb, &anotherrect);
4438 }
4439
4440 // Paint row labels part intersecting rect.
4441 if ( width_label > 0 && height_cell > 0 )
4442 {
4443 wxRect anotherrect(rect_x, y, width_label, height_cell);
4444 m_rowLabelWin->Refresh(eraseb, &anotherrect);
4445 }
4446
4447 // Paint cell area part intersecting rect.
4448 if ( width_cell > 0 && height_cell > 0 )
4449 {
4450 wxRect anotherrect(x, y, width_cell, height_cell);
4451 m_gridWin->Refresh(eraseb, &anotherrect);
4452 }
4453 }
4454 else
4455 {
4456 m_cornerLabelWin->Refresh(eraseb, NULL);
4457 m_colWindow->Refresh(eraseb, NULL);
4458 m_rowLabelWin->Refresh(eraseb, NULL);
4459 m_gridWin->Refresh(eraseb, NULL);
4460 }
4461 }
4462 }
4463
4464 void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event))
4465 {
4466 if (m_targetWindow != this) // check whether initialisation has been done
4467 {
4468 // reposition our children windows
4469 CalcWindowSizes();
4470 }
4471 }
4472
4473 void wxGrid::OnKeyDown( wxKeyEvent& event )
4474 {
4475 if ( m_inOnKeyDown )
4476 {
4477 // shouldn't be here - we are going round in circles...
4478 //
4479 wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
4480 }
4481
4482 m_inOnKeyDown = true;
4483
4484 // propagate the event up and see if it gets processed
4485 wxWindow *parent = GetParent();
4486 wxKeyEvent keyEvt( event );
4487 keyEvt.SetEventObject( parent );
4488
4489 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
4490 {
4491 if (GetLayoutDirection() == wxLayout_RightToLeft)
4492 {
4493 if (event.GetKeyCode() == WXK_RIGHT)
4494 event.m_keyCode = WXK_LEFT;
4495 else if (event.GetKeyCode() == WXK_LEFT)
4496 event.m_keyCode = WXK_RIGHT;
4497 }
4498
4499 // try local handlers
4500 switch ( event.GetKeyCode() )
4501 {
4502 case WXK_UP:
4503 if ( event.ControlDown() )
4504 MoveCursorUpBlock( event.ShiftDown() );
4505 else
4506 MoveCursorUp( event.ShiftDown() );
4507 break;
4508
4509 case WXK_DOWN:
4510 if ( event.ControlDown() )
4511 MoveCursorDownBlock( event.ShiftDown() );
4512 else
4513 MoveCursorDown( event.ShiftDown() );
4514 break;
4515
4516 case WXK_LEFT:
4517 if ( event.ControlDown() )
4518 MoveCursorLeftBlock( event.ShiftDown() );
4519 else
4520 MoveCursorLeft( event.ShiftDown() );
4521 break;
4522
4523 case WXK_RIGHT:
4524 if ( event.ControlDown() )
4525 MoveCursorRightBlock( event.ShiftDown() );
4526 else
4527 MoveCursorRight( event.ShiftDown() );
4528 break;
4529
4530 case WXK_RETURN:
4531 case WXK_NUMPAD_ENTER:
4532 if ( event.ControlDown() )
4533 {
4534 event.Skip(); // to let the edit control have the return
4535 }
4536 else
4537 {
4538 if ( GetGridCursorRow() < GetNumberRows()-1 )
4539 {
4540 MoveCursorDown( event.ShiftDown() );
4541 }
4542 else
4543 {
4544 // at the bottom of a column
4545 DisableCellEditControl();
4546 }
4547 }
4548 break;
4549
4550 case WXK_ESCAPE:
4551 ClearSelection();
4552 break;
4553
4554 case WXK_TAB:
4555 if (event.ShiftDown())
4556 {
4557 if ( GetGridCursorCol() > 0 )
4558 {
4559 MoveCursorLeft( false );
4560 }
4561 else
4562 {
4563 // at left of grid
4564 DisableCellEditControl();
4565 }
4566 }
4567 else
4568 {
4569 if ( GetGridCursorCol() < GetNumberCols() - 1 )
4570 {
4571 MoveCursorRight( false );
4572 }
4573 else
4574 {
4575 // at right of grid
4576 DisableCellEditControl();
4577 }
4578 }
4579 break;
4580
4581 case WXK_HOME:
4582 if ( event.ControlDown() )
4583 {
4584 GoToCell(0, 0);
4585 }
4586 else
4587 {
4588 event.Skip();
4589 }
4590 break;
4591
4592 case WXK_END:
4593 if ( event.ControlDown() )
4594 {
4595 GoToCell(m_numRows - 1, m_numCols - 1);
4596 }
4597 else
4598 {
4599 event.Skip();
4600 }
4601 break;
4602
4603 case WXK_PAGEUP:
4604 MovePageUp();
4605 break;
4606
4607 case WXK_PAGEDOWN:
4608 MovePageDown();
4609 break;
4610
4611 case WXK_SPACE:
4612 // Ctrl-Space selects the current column, Shift-Space -- the
4613 // current row and Ctrl-Shift-Space -- everything
4614 switch ( m_selection ? event.GetModifiers() : wxMOD_NONE )
4615 {
4616 case wxMOD_CONTROL:
4617 m_selection->SelectCol(m_currentCellCoords.GetCol());
4618 break;
4619
4620 case wxMOD_SHIFT:
4621 m_selection->SelectRow(m_currentCellCoords.GetRow());
4622 break;
4623
4624 case wxMOD_CONTROL | wxMOD_SHIFT:
4625 m_selection->SelectBlock(0, 0,
4626 m_numRows - 1, m_numCols - 1);
4627 break;
4628
4629 case wxMOD_NONE:
4630 if ( !IsEditable() )
4631 {
4632 MoveCursorRight(false);
4633 break;
4634 }
4635 //else: fall through
4636
4637 default:
4638 event.Skip();
4639 }
4640 break;
4641
4642 default:
4643 event.Skip();
4644 break;
4645 }
4646 }
4647
4648 m_inOnKeyDown = false;
4649 }
4650
4651 void wxGrid::OnKeyUp( wxKeyEvent& event )
4652 {
4653 // try local handlers
4654 //
4655 if ( event.GetKeyCode() == WXK_SHIFT )
4656 {
4657 if ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
4658 m_selectedBlockBottomRight != wxGridNoCellCoords )
4659 {
4660 if ( m_selection )
4661 {
4662 m_selection->SelectBlock(
4663 m_selectedBlockTopLeft,
4664 m_selectedBlockBottomRight,
4665 event);
4666 }
4667 }
4668
4669 m_selectedBlockTopLeft = wxGridNoCellCoords;
4670 m_selectedBlockBottomRight = wxGridNoCellCoords;
4671 m_selectedBlockCorner = wxGridNoCellCoords;
4672 }
4673 }
4674
4675 void wxGrid::OnChar( wxKeyEvent& event )
4676 {
4677 // is it possible to edit the current cell at all?
4678 if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
4679 {
4680 // yes, now check whether the cells editor accepts the key
4681 int row = m_currentCellCoords.GetRow();
4682 int col = m_currentCellCoords.GetCol();
4683 wxGridCellAttr *attr = GetCellAttr(row, col);
4684 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
4685
4686 // <F2> is special and will always start editing, for
4687 // other keys - ask the editor itself
4688 if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
4689 || editor->IsAcceptedKey(event) )
4690 {
4691 // ensure cell is visble
4692 MakeCellVisible(row, col);
4693 EnableCellEditControl();
4694
4695 // a problem can arise if the cell is not completely
4696 // visible (even after calling MakeCellVisible the
4697 // control is not created and calling StartingKey will
4698 // crash the app
4699 if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
4700 editor->StartingKey(event);
4701 }
4702 else
4703 {
4704 event.Skip();
4705 }
4706
4707 editor->DecRef();
4708 attr->DecRef();
4709 }
4710 else
4711 {
4712 event.Skip();
4713 }
4714 }
4715
4716 void wxGrid::OnEraseBackground(wxEraseEvent&)
4717 {
4718 }
4719
4720 bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
4721 {
4722 if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )
4723 {
4724 // the event has been vetoed - do nothing
4725 return false;
4726 }
4727
4728 #if !defined(__WXMAC__)
4729 wxClientDC dc( m_gridWin );
4730 PrepareDC( dc );
4731 #endif
4732
4733 if ( m_currentCellCoords != wxGridNoCellCoords )
4734 {
4735 DisableCellEditControl();
4736
4737 if ( IsVisible( m_currentCellCoords, false ) )
4738 {
4739 wxRect r;
4740 r = BlockToDeviceRect( m_currentCellCoords, m_currentCellCoords );
4741 if ( !m_gridLinesEnabled )
4742 {
4743 r.x--;
4744 r.y--;
4745 r.width++;
4746 r.height++;
4747 }
4748
4749 wxGridCellCoordsArray cells = CalcCellsExposed( r );
4750
4751 // Otherwise refresh redraws the highlight!
4752 m_currentCellCoords = coords;
4753
4754 #if defined(__WXMAC__)
4755 m_gridWin->Refresh(true /*, & r */);
4756 #else
4757 DrawGridCellArea( dc, cells );
4758 DrawAllGridLines( dc, r );
4759 #endif
4760 }
4761 }
4762
4763 m_currentCellCoords = coords;
4764
4765 wxGridCellAttr *attr = GetCellAttr( coords );
4766 #if !defined(__WXMAC__)
4767 DrawCellHighlight( dc, attr );
4768 #endif
4769 attr->DecRef();
4770
4771 return true;
4772 }
4773
4774 void
4775 wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol,
4776 int bottomRow, int rightCol)
4777 {
4778 if ( m_selection )
4779 {
4780 switch ( m_selection->GetSelectionMode() )
4781 {
4782 default:
4783 wxFAIL_MSG( "unknown selection mode" );
4784 // fall through
4785
4786 case wxGridSelectCells:
4787 // arbitrary blocks selection allowed so just use the cell
4788 // coordinates as is
4789 break;
4790
4791 case wxGridSelectRows:
4792 // only full rows selection allowd, ensure that we do select
4793 // full rows
4794 leftCol = 0;
4795 rightCol = GetNumberCols() - 1;
4796 break;
4797
4798 case wxGridSelectColumns:
4799 // same as above but for columns
4800 topRow = 0;
4801 bottomRow = GetNumberRows() - 1;
4802 break;
4803
4804 case wxGridSelectRowsOrColumns:
4805 // in this mode we can select only full rows or full columns so
4806 // it doesn't make sense to select blocks at all (and we can't
4807 // extend the block because there is no preferred direction, we
4808 // could only extend it to cover the entire grid but this is
4809 // not useful)
4810 return;
4811 }
4812 }
4813
4814 m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
4815 MakeCellVisible(m_selectedBlockCorner);
4816
4817 EnsureFirstLessThanSecond(topRow, bottomRow);
4818 EnsureFirstLessThanSecond(leftCol, rightCol);
4819
4820 wxGridCellCoords updateTopLeft = wxGridCellCoords(topRow, leftCol),
4821 updateBottomRight = wxGridCellCoords(bottomRow, rightCol);
4822
4823 // First the case that we selected a completely new area
4824 if ( m_selectedBlockTopLeft == wxGridNoCellCoords ||
4825 m_selectedBlockBottomRight == wxGridNoCellCoords )
4826 {
4827 wxRect rect;
4828 rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
4829 wxGridCellCoords ( bottomRow, rightCol ) );
4830 m_gridWin->Refresh( false, &rect );
4831 }
4832
4833 // Now handle changing an existing selection area.
4834 else if ( m_selectedBlockTopLeft != updateTopLeft ||
4835 m_selectedBlockBottomRight != updateBottomRight )
4836 {
4837 // Compute two optimal update rectangles:
4838 // Either one rectangle is a real subset of the
4839 // other, or they are (almost) disjoint!
4840 wxRect rect[4];
4841 bool need_refresh[4];
4842 need_refresh[0] =
4843 need_refresh[1] =
4844 need_refresh[2] =
4845 need_refresh[3] = false;
4846 int i;
4847
4848 // Store intermediate values
4849 wxCoord oldLeft = m_selectedBlockTopLeft.GetCol();
4850 wxCoord oldTop = m_selectedBlockTopLeft.GetRow();
4851 wxCoord oldRight = m_selectedBlockBottomRight.GetCol();
4852 wxCoord oldBottom = m_selectedBlockBottomRight.GetRow();
4853
4854 // Determine the outer/inner coordinates.
4855 EnsureFirstLessThanSecond(oldLeft, leftCol);
4856 EnsureFirstLessThanSecond(oldTop, topRow);
4857 EnsureFirstLessThanSecond(rightCol, oldRight);
4858 EnsureFirstLessThanSecond(bottomRow, oldBottom);
4859
4860 // Now, either the stuff marked old is the outer
4861 // rectangle or we don't have a situation where one
4862 // is contained in the other.
4863
4864 if ( oldLeft < leftCol )
4865 {
4866 // Refresh the newly selected or deselected
4867 // area to the left of the old or new selection.
4868 need_refresh[0] = true;
4869 rect[0] = BlockToDeviceRect(
4870 wxGridCellCoords( oldTop, oldLeft ),
4871 wxGridCellCoords( oldBottom, leftCol - 1 ) );
4872 }
4873
4874 if ( oldTop < topRow )
4875 {
4876 // Refresh the newly selected or deselected
4877 // area above the old or new selection.
4878 need_refresh[1] = true;
4879 rect[1] = BlockToDeviceRect(
4880 wxGridCellCoords( oldTop, leftCol ),
4881 wxGridCellCoords( topRow - 1, rightCol ) );
4882 }
4883
4884 if ( oldRight > rightCol )
4885 {
4886 // Refresh the newly selected or deselected
4887 // area to the right of the old or new selection.
4888 need_refresh[2] = true;
4889 rect[2] = BlockToDeviceRect(
4890 wxGridCellCoords( oldTop, rightCol + 1 ),
4891 wxGridCellCoords( oldBottom, oldRight ) );
4892 }
4893
4894 if ( oldBottom > bottomRow )
4895 {
4896 // Refresh the newly selected or deselected
4897 // area below the old or new selection.
4898 need_refresh[3] = true;
4899 rect[3] = BlockToDeviceRect(
4900 wxGridCellCoords( bottomRow + 1, leftCol ),
4901 wxGridCellCoords( oldBottom, rightCol ) );
4902 }
4903
4904 // various Refresh() calls
4905 for (i = 0; i < 4; i++ )
4906 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
4907 m_gridWin->Refresh( false, &(rect[i]) );
4908 }
4909
4910 // change selection
4911 m_selectedBlockTopLeft = updateTopLeft;
4912 m_selectedBlockBottomRight = updateBottomRight;
4913 }
4914
4915 //
4916 // ------ functions to get/send data (see also public functions)
4917 //
4918
4919 bool wxGrid::GetModelValues()
4920 {
4921 // Hide the editor, so it won't hide a changed value.
4922 HideCellEditControl();
4923
4924 if ( m_table )
4925 {
4926 // all we need to do is repaint the grid
4927 //
4928 m_gridWin->Refresh();
4929 return true;
4930 }
4931
4932 return false;
4933 }
4934
4935 bool wxGrid::SetModelValues()
4936 {
4937 int row, col;
4938
4939 // Disable the editor, so it won't hide a changed value.
4940 // Do we also want to save the current value of the editor first?
4941 // I think so ...
4942 DisableCellEditControl();
4943
4944 if ( m_table )
4945 {
4946 for ( row = 0; row < m_numRows; row++ )
4947 {
4948 for ( col = 0; col < m_numCols; col++ )
4949 {
4950 m_table->SetValue( row, col, GetCellValue(row, col) );
4951 }
4952 }
4953
4954 return true;
4955 }
4956
4957 return false;
4958 }
4959
4960 // Note - this function only draws cells that are in the list of
4961 // exposed cells (usually set from the update region by
4962 // CalcExposedCells)
4963 //
4964 void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
4965 {
4966 if ( !m_numRows || !m_numCols )
4967 return;
4968
4969 int i, numCells = cells.GetCount();
4970 int row, col, cell_rows, cell_cols;
4971 wxGridCellCoordsArray redrawCells;
4972
4973 for ( i = numCells - 1; i >= 0; i-- )
4974 {
4975 row = cells[i].GetRow();
4976 col = cells[i].GetCol();
4977 GetCellSize( row, col, &cell_rows, &cell_cols );
4978
4979 // If this cell is part of a multicell block, find owner for repaint
4980 if ( cell_rows <= 0 || cell_cols <= 0 )
4981 {
4982 wxGridCellCoords cell( row + cell_rows, col + cell_cols );
4983 bool marked = false;
4984 for ( int j = 0; j < numCells; j++ )
4985 {
4986 if ( cell == cells[j] )
4987 {
4988 marked = true;
4989 break;
4990 }
4991 }
4992
4993 if (!marked)
4994 {
4995 int count = redrawCells.GetCount();
4996 for (int j = 0; j < count; j++)
4997 {
4998 if ( cell == redrawCells[j] )
4999 {
5000 marked = true;
5001 break;
5002 }
5003 }
5004
5005 if (!marked)
5006 redrawCells.Add( cell );
5007 }
5008
5009 // don't bother drawing this cell
5010 continue;
5011 }
5012
5013 // If this cell is empty, find cell to left that might want to overflow
5014 if (m_table && m_table->IsEmptyCell(row, col))
5015 {
5016 for ( int l = 0; l < cell_rows; l++ )
5017 {
5018 // find a cell in this row to leave already marked for repaint
5019 int left = col;
5020 for (int k = 0; k < int(redrawCells.GetCount()); k++)
5021 if ((redrawCells[k].GetCol() < left) &&
5022 (redrawCells[k].GetRow() == row))
5023 {
5024 left = redrawCells[k].GetCol();
5025 }
5026
5027 if (left == col)
5028 left = 0; // oh well
5029
5030 for (int j = col - 1; j >= left; j--)
5031 {
5032 if (!m_table->IsEmptyCell(row + l, j))
5033 {
5034 if (GetCellOverflow(row + l, j))
5035 {
5036 wxGridCellCoords cell(row + l, j);
5037 bool marked = false;
5038
5039 for (int k = 0; k < numCells; k++)
5040 {
5041 if ( cell == cells[k] )
5042 {
5043 marked = true;
5044 break;
5045 }
5046 }
5047
5048 if (!marked)
5049 {
5050 int count = redrawCells.GetCount();
5051 for (int k = 0; k < count; k++)
5052 {
5053 if ( cell == redrawCells[k] )
5054 {
5055 marked = true;
5056 break;
5057 }
5058 }
5059 if (!marked)
5060 redrawCells.Add( cell );
5061 }
5062 }
5063 break;
5064 }
5065 }
5066 }
5067 }
5068
5069 DrawCell( dc, cells[i] );
5070 }
5071
5072 numCells = redrawCells.GetCount();
5073
5074 for ( i = numCells - 1; i >= 0; i-- )
5075 {
5076 DrawCell( dc, redrawCells[i] );
5077 }
5078 }
5079
5080 void wxGrid::DrawGridSpace( wxDC& dc )
5081 {
5082 int cw, ch;
5083 m_gridWin->GetClientSize( &cw, &ch );
5084
5085 int right, bottom;
5086 CalcUnscrolledPosition( cw, ch, &right, &bottom );
5087
5088 int rightCol = m_numCols > 0 ? GetColRight(GetColAt( m_numCols - 1 )) : 0;
5089 int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0;
5090
5091 if ( right > rightCol || bottom > bottomRow )
5092 {
5093 int left, top;
5094 CalcUnscrolledPosition( 0, 0, &left, &top );
5095
5096 dc.SetBrush(GetDefaultCellBackgroundColour());
5097 dc.SetPen( *wxTRANSPARENT_PEN );
5098
5099 if ( right > rightCol )
5100 {
5101 dc.DrawRectangle( rightCol, top, right - rightCol, ch );
5102 }
5103
5104 if ( bottom > bottomRow )
5105 {
5106 dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow );
5107 }
5108 }
5109 }
5110
5111 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
5112 {
5113 int row = coords.GetRow();
5114 int col = coords.GetCol();
5115
5116 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5117 return;
5118
5119 // we draw the cell border ourselves
5120 wxGridCellAttr* attr = GetCellAttr(row, col);
5121
5122 bool isCurrent = coords == m_currentCellCoords;
5123
5124 wxRect rect = CellToRect( row, col );
5125
5126 // if the editor is shown, we should use it and not the renderer
5127 // Note: However, only if it is really _shown_, i.e. not hidden!
5128 if ( isCurrent && IsCellEditControlShown() )
5129 {
5130 // NB: this "#if..." is temporary and fixes a problem where the
5131 // edit control is erased by this code after being rendered.
5132 // On wxMac (QD build only), the cell editor is a wxTextCntl and is rendered
5133 // implicitly, causing this out-of order render.
5134 #if !defined(__WXMAC__)
5135 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
5136 editor->PaintBackground(rect, attr);
5137 editor->DecRef();
5138 #endif
5139 }
5140 else
5141 {
5142 // but all the rest is drawn by the cell renderer and hence may be customized
5143 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
5144 renderer->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
5145 renderer->DecRef();
5146 }
5147
5148 attr->DecRef();
5149 }
5150
5151 void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
5152 {
5153 // don't show highlight when the grid doesn't have focus
5154 if ( !HasFocus() )
5155 return;
5156
5157 int row = m_currentCellCoords.GetRow();
5158 int col = m_currentCellCoords.GetCol();
5159
5160 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5161 return;
5162
5163 wxRect rect = CellToRect(row, col);
5164
5165 // hmmm... what could we do here to show that the cell is disabled?
5166 // for now, I just draw a thinner border than for the other ones, but
5167 // it doesn't look really good
5168
5169 int penWidth = attr->IsReadOnly() ? m_cellHighlightROPenWidth : m_cellHighlightPenWidth;
5170
5171 if (penWidth > 0)
5172 {
5173 // The center of the drawn line is where the position/width/height of
5174 // the rectangle is actually at (on wxMSW at least), so the
5175 // size of the rectangle is reduced to compensate for the thickness of
5176 // the line. If this is too strange on non-wxMSW platforms then
5177 // please #ifdef this appropriately.
5178 rect.x += penWidth / 2;
5179 rect.y += penWidth / 2;
5180 rect.width -= penWidth - 1;
5181 rect.height -= penWidth - 1;
5182
5183 // Now draw the rectangle
5184 // use the cellHighlightColour if the cell is inside a selection, this
5185 // will ensure the cell is always visible.
5186 dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground
5187 : m_cellHighlightColour,
5188 penWidth));
5189 dc.SetBrush(*wxTRANSPARENT_BRUSH);
5190 dc.DrawRectangle(rect);
5191 }
5192 }
5193
5194 wxPen wxGrid::GetDefaultGridLinePen()
5195 {
5196 return wxPen(GetGridLineColour());
5197 }
5198
5199 wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row))
5200 {
5201 return GetDefaultGridLinePen();
5202 }
5203
5204 wxPen wxGrid::GetColGridLinePen(int WXUNUSED(col))
5205 {
5206 return GetDefaultGridLinePen();
5207 }
5208
5209 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
5210 {
5211 int row = coords.GetRow();
5212 int col = coords.GetCol();
5213 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5214 return;
5215
5216
5217 wxRect rect = CellToRect( row, col );
5218
5219 // right hand border
5220 dc.SetPen( GetColGridLinePen(col) );
5221 dc.DrawLine( rect.x + rect.width, rect.y,
5222 rect.x + rect.width, rect.y + rect.height + 1 );
5223
5224 // bottom border
5225 dc.SetPen( GetRowGridLinePen(row) );
5226 dc.DrawLine( rect.x, rect.y + rect.height,
5227 rect.x + rect.width, rect.y + rect.height);
5228 }
5229
5230 void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells)
5231 {
5232 // This if block was previously in wxGrid::OnPaint but that doesn't
5233 // seem to get called under wxGTK - MB
5234 //
5235 if ( m_currentCellCoords == wxGridNoCellCoords &&
5236 m_numRows && m_numCols )
5237 {
5238 m_currentCellCoords.Set(0, 0);
5239 }
5240
5241 if ( IsCellEditControlShown() )
5242 {
5243 // don't show highlight when the edit control is shown
5244 return;
5245 }
5246
5247 // if the active cell was repainted, repaint its highlight too because it
5248 // might have been damaged by the grid lines
5249 size_t count = cells.GetCount();
5250 for ( size_t n = 0; n < count; n++ )
5251 {
5252 wxGridCellCoords cell = cells[n];
5253
5254 // If we are using attributes, then we may have just exposed another
5255 // cell in a partially-visible merged cluster of cells. If the "anchor"
5256 // (upper left) cell of this merged cluster is the cell indicated by
5257 // m_currentCellCoords, then we need to refresh the cell highlight even
5258 // though the "anchor" itself is not part of our update segment.
5259 if ( CanHaveAttributes() )
5260 {
5261 int rows = 0,
5262 cols = 0;
5263 GetCellSize(cell.GetRow(), cell.GetCol(), &rows, &cols);
5264
5265 if ( rows < 0 )
5266 cell.SetRow(cell.GetRow() + rows);
5267
5268 if ( cols < 0 )
5269 cell.SetCol(cell.GetCol() + cols);
5270 }
5271
5272 if ( cell == m_currentCellCoords )
5273 {
5274 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
5275 DrawCellHighlight(dc, attr);
5276 attr->DecRef();
5277
5278 break;
5279 }
5280 }
5281 }
5282
5283 // This is used to redraw all grid lines e.g. when the grid line colour
5284 // has been changed
5285 //
5286 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
5287 {
5288 if ( !m_gridLinesEnabled )
5289 return;
5290
5291 int top, bottom, left, right;
5292
5293 int cw, ch;
5294 m_gridWin->GetClientSize(&cw, &ch);
5295 CalcUnscrolledPosition( 0, 0, &left, &top );
5296 CalcUnscrolledPosition( cw, ch, &right, &bottom );
5297
5298 // avoid drawing grid lines past the last row and col
5299 if ( m_gridLinesClipHorz )
5300 {
5301 if ( !m_numCols )
5302 return;
5303
5304 const int lastColRight = GetColRight(GetColAt(m_numCols - 1));
5305 if ( right > lastColRight )
5306 right = lastColRight;
5307 }
5308
5309 if ( m_gridLinesClipVert )
5310 {
5311 if ( !m_numRows )
5312 return;
5313
5314 const int lastRowBottom = GetRowBottom(m_numRows - 1);
5315 if ( bottom > lastRowBottom )
5316 bottom = lastRowBottom;
5317 }
5318
5319 // no gridlines inside multicells, clip them out
5320 int leftCol = GetColPos( internalXToCol(left) );
5321 int topRow = internalYToRow(top);
5322 int rightCol = GetColPos( internalXToCol(right) );
5323 int bottomRow = internalYToRow(bottom);
5324
5325 wxRegion clippedcells(0, 0, cw, ch);
5326
5327 int cell_rows, cell_cols;
5328 wxRect rect;
5329
5330 for ( int j = topRow; j <= bottomRow; j++ )
5331 {
5332 for ( int colPos = leftCol; colPos <= rightCol; colPos++ )
5333 {
5334 int i = GetColAt( colPos );
5335
5336 GetCellSize( j, i, &cell_rows, &cell_cols );
5337 if ((cell_rows > 1) || (cell_cols > 1))
5338 {
5339 rect = CellToRect(j,i);
5340 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
5341 clippedcells.Subtract(rect);
5342 }
5343 else if ((cell_rows < 0) || (cell_cols < 0))
5344 {
5345 rect = CellToRect(j + cell_rows, i + cell_cols);
5346 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
5347 clippedcells.Subtract(rect);
5348 }
5349 }
5350 }
5351
5352 dc.SetDeviceClippingRegion( clippedcells );
5353
5354
5355 // horizontal grid lines
5356 for ( int i = internalYToRow(top); i < m_numRows; i++ )
5357 {
5358 int bot = GetRowBottom(i) - 1;
5359
5360 if ( bot > bottom )
5361 break;
5362
5363 if ( bot >= top )
5364 {
5365 dc.SetPen( GetRowGridLinePen(i) );
5366 dc.DrawLine( left, bot, right, bot );
5367 }
5368 }
5369
5370 // vertical grid lines
5371 for ( int colPos = leftCol; colPos < m_numCols; colPos++ )
5372 {
5373 int i = GetColAt( colPos );
5374
5375 int colRight = GetColRight(i);
5376 #ifdef __WXGTK__
5377 if (GetLayoutDirection() != wxLayout_RightToLeft)
5378 #endif
5379 colRight--;
5380
5381 if ( colRight > right )
5382 break;
5383
5384 if ( colRight >= left )
5385 {
5386 dc.SetPen( GetColGridLinePen(i) );
5387 dc.DrawLine( colRight, top, colRight, bottom );
5388 }
5389 }
5390
5391 dc.DestroyClippingRegion();
5392 }
5393
5394 void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows)
5395 {
5396 if ( !m_numRows )
5397 return;
5398
5399 const size_t numLabels = rows.GetCount();
5400 for ( size_t i = 0; i < numLabels; i++ )
5401 {
5402 DrawRowLabel( dc, rows[i] );
5403 }
5404 }
5405
5406 void wxGrid::DrawRowLabel( wxDC& dc, int row )
5407 {
5408 if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
5409 return;
5410
5411 const wxGridRowHeaderRenderer&
5412 rend = m_table->GetAttrProvider()->GetRowHeaderRenderer(row);
5413 wxRect rect(0, GetRowTop(row), m_rowLabelWidth, GetRowHeight(row));
5414 rend.DrawBorder(*this, dc, rect);
5415
5416 int hAlign, vAlign;
5417 GetRowLabelAlignment(&hAlign, &vAlign);
5418
5419 rend.DrawLabel(*this, dc, GetRowLabelValue(row),
5420 rect, hAlign, vAlign, wxHORIZONTAL);
5421 }
5422
5423 void wxGrid::UseNativeColHeader(bool native)
5424 {
5425 if ( native == m_useNativeHeader )
5426 return;
5427
5428 delete m_colWindow;
5429 m_useNativeHeader = native;
5430
5431 CreateColumnWindow();
5432
5433 if ( m_useNativeHeader )
5434 GetGridColHeader()->SetColumnCount(m_numCols);
5435 CalcWindowSizes();
5436 }
5437
5438 void wxGrid::SetUseNativeColLabels( bool native )
5439 {
5440 wxASSERT_MSG( !m_useNativeHeader,
5441 "doesn't make sense when using native header" );
5442
5443 m_nativeColumnLabels = native;
5444 if (native)
5445 {
5446 int height = wxRendererNative::Get().GetHeaderButtonHeight( this );
5447 SetColLabelSize( height );
5448 }
5449
5450 GetColLabelWindow()->Refresh();
5451 m_cornerLabelWin->Refresh();
5452 }
5453
5454 void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols )
5455 {
5456 if ( !m_numCols )
5457 return;
5458
5459 const size_t numLabels = cols.GetCount();
5460 for ( size_t i = 0; i < numLabels; i++ )
5461 {
5462 DrawColLabel( dc, cols[i] );
5463 }
5464 }
5465
5466 void wxGrid::DrawCornerLabel(wxDC& dc)
5467 {
5468 wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight));
5469
5470 if ( m_nativeColumnLabels )
5471 {
5472 rect.Deflate(1);
5473
5474 wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0);
5475 }
5476 else
5477 {
5478 rect.width++;
5479 rect.height++;
5480
5481 const wxGridCornerHeaderRenderer&
5482 rend = m_table->GetAttrProvider()->GetCornerRenderer();
5483
5484 rend.DrawBorder(*this, dc, rect);
5485 }
5486 }
5487
5488 void wxGrid::DrawColLabel(wxDC& dc, int col)
5489 {
5490 if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 )
5491 return;
5492
5493 int colLeft = GetColLeft(col);
5494
5495 wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight);
5496 const wxGridColumnHeaderRenderer&
5497 rend = m_table->GetAttrProvider()->GetColumnHeaderRenderer(col);
5498
5499 if ( m_nativeColumnLabels )
5500 {
5501 wxRendererNative::Get().DrawHeaderButton
5502 (
5503 GetColLabelWindow(),
5504 dc,
5505 rect,
5506 0,
5507 IsSortingBy(col)
5508 ? IsSortOrderAscending()
5509 ? wxHDR_SORT_ICON_UP
5510 : wxHDR_SORT_ICON_DOWN
5511 : wxHDR_SORT_ICON_NONE
5512 );
5513 rect.Deflate(2);
5514 }
5515 else
5516 {
5517 rend.DrawBorder(*this, dc, rect);
5518 }
5519
5520 int hAlign, vAlign;
5521 GetColLabelAlignment(&hAlign, &vAlign);
5522 const int orient = GetColLabelTextOrientation();
5523
5524 rend.DrawLabel(*this, dc, GetColLabelValue(col), rect, hAlign, vAlign, orient);
5525 }
5526
5527 // TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which
5528 // we just have to add textOrientation support
5529 void wxGrid::DrawTextRectangle( wxDC& dc,
5530 const wxString& value,
5531 const wxRect& rect,
5532 int horizAlign,
5533 int vertAlign,
5534 int textOrientation ) const
5535 {
5536 wxArrayString lines;
5537
5538 StringToLines( value, lines );
5539
5540 DrawTextRectangle(dc, lines, rect, horizAlign, vertAlign, textOrientation);
5541 }
5542
5543 void wxGrid::DrawTextRectangle(wxDC& dc,
5544 const wxArrayString& lines,
5545 const wxRect& rect,
5546 int horizAlign,
5547 int vertAlign,
5548 int textOrientation) const
5549 {
5550 if ( lines.empty() )
5551 return;
5552
5553 wxDCClipper clip(dc, rect);
5554
5555 long textWidth,
5556 textHeight;
5557
5558 if ( textOrientation == wxHORIZONTAL )
5559 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
5560 else
5561 GetTextBoxSize( dc, lines, &textHeight, &textWidth );
5562
5563 int x = 0,
5564 y = 0;
5565 switch ( vertAlign )
5566 {
5567 case wxALIGN_BOTTOM:
5568 if ( textOrientation == wxHORIZONTAL )
5569 y = rect.y + (rect.height - textHeight - 1);
5570 else
5571 x = rect.x + rect.width - textWidth;
5572 break;
5573
5574 case wxALIGN_CENTRE:
5575 if ( textOrientation == wxHORIZONTAL )
5576 y = rect.y + ((rect.height - textHeight) / 2);
5577 else
5578 x = rect.x + ((rect.width - textWidth) / 2);
5579 break;
5580
5581 case wxALIGN_TOP:
5582 default:
5583 if ( textOrientation == wxHORIZONTAL )
5584 y = rect.y + 1;
5585 else
5586 x = rect.x + 1;
5587 break;
5588 }
5589
5590 // Align each line of a multi-line label
5591 size_t nLines = lines.GetCount();
5592 for ( size_t l = 0; l < nLines; l++ )
5593 {
5594 const wxString& line = lines[l];
5595
5596 if ( line.empty() )
5597 {
5598 *(textOrientation == wxHORIZONTAL ? &y : &x) += dc.GetCharHeight();
5599 continue;
5600 }
5601
5602 wxCoord lineWidth = 0,
5603 lineHeight = 0;
5604 dc.GetTextExtent(line, &lineWidth, &lineHeight);
5605
5606 switch ( horizAlign )
5607 {
5608 case wxALIGN_RIGHT:
5609 if ( textOrientation == wxHORIZONTAL )
5610 x = rect.x + (rect.width - lineWidth - 1);
5611 else
5612 y = rect.y + lineWidth + 1;
5613 break;
5614
5615 case wxALIGN_CENTRE:
5616 if ( textOrientation == wxHORIZONTAL )
5617 x = rect.x + ((rect.width - lineWidth) / 2);
5618 else
5619 y = rect.y + rect.height - ((rect.height - lineWidth) / 2);
5620 break;
5621
5622 case wxALIGN_LEFT:
5623 default:
5624 if ( textOrientation == wxHORIZONTAL )
5625 x = rect.x + 1;
5626 else
5627 y = rect.y + rect.height - 1;
5628 break;
5629 }
5630
5631 if ( textOrientation == wxHORIZONTAL )
5632 {
5633 dc.DrawText( line, x, y );
5634 y += lineHeight;
5635 }
5636 else
5637 {
5638 dc.DrawRotatedText( line, x, y, 90.0 );
5639 x += lineHeight;
5640 }
5641 }
5642 }
5643
5644 // Split multi-line text up into an array of strings.
5645 // Any existing contents of the string array are preserved.
5646 //
5647 // TODO: refactor wxTextFile::Read() and reuse the same code from here
5648 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const
5649 {
5650 int startPos = 0;
5651 int pos;
5652 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
5653 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
5654
5655 while ( startPos < (int)tVal.length() )
5656 {
5657 pos = tVal.Mid(startPos).Find( eol );
5658 if ( pos < 0 )
5659 {
5660 break;
5661 }
5662 else if ( pos == 0 )
5663 {
5664 lines.Add( wxEmptyString );
5665 }
5666 else
5667 {
5668 lines.Add( tVal.Mid(startPos, pos) );
5669 }
5670
5671 startPos += pos + 1;
5672 }
5673
5674 if ( startPos < (int)tVal.length() )
5675 {
5676 lines.Add( tVal.Mid( startPos ) );
5677 }
5678 }
5679
5680 void wxGrid::GetTextBoxSize( const wxDC& dc,
5681 const wxArrayString& lines,
5682 long *width, long *height ) const
5683 {
5684 wxCoord w = 0;
5685 wxCoord h = 0;
5686 wxCoord lineW = 0, lineH = 0;
5687
5688 size_t i;
5689 for ( i = 0; i < lines.GetCount(); i++ )
5690 {
5691 dc.GetTextExtent( lines[i], &lineW, &lineH );
5692 w = wxMax( w, lineW );
5693 h += lineH;
5694 }
5695
5696 *width = w;
5697 *height = h;
5698 }
5699
5700 //
5701 // ------ Batch processing.
5702 //
5703 void wxGrid::EndBatch()
5704 {
5705 if ( m_batchCount > 0 )
5706 {
5707 m_batchCount--;
5708 if ( !m_batchCount )
5709 {
5710 CalcDimensions();
5711 m_rowLabelWin->Refresh();
5712 m_colWindow->Refresh();
5713 m_cornerLabelWin->Refresh();
5714 m_gridWin->Refresh();
5715 }
5716 }
5717 }
5718
5719 // Use this, rather than wxWindow::Refresh(), to force an immediate
5720 // repainting of the grid. Has no effect if you are already inside a
5721 // BeginBatch / EndBatch block.
5722 //
5723 void wxGrid::ForceRefresh()
5724 {
5725 BeginBatch();
5726 EndBatch();
5727 }
5728
5729 bool wxGrid::Enable(bool enable)
5730 {
5731 if ( !wxScrolledWindow::Enable(enable) )
5732 return false;
5733
5734 // redraw in the new state
5735 m_gridWin->Refresh();
5736
5737 return true;
5738 }
5739
5740 //
5741 // ------ Edit control functions
5742 //
5743
5744 void wxGrid::EnableEditing( bool edit )
5745 {
5746 if ( edit != m_editable )
5747 {
5748 if (!edit)
5749 EnableCellEditControl(edit);
5750 m_editable = edit;
5751 }
5752 }
5753
5754 void wxGrid::EnableCellEditControl( bool enable )
5755 {
5756 if (! m_editable)
5757 return;
5758
5759 if ( enable != m_cellEditCtrlEnabled )
5760 {
5761 if ( enable )
5762 {
5763 if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
5764 return;
5765
5766 // this should be checked by the caller!
5767 wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
5768
5769 // do it before ShowCellEditControl()
5770 m_cellEditCtrlEnabled = enable;
5771
5772 ShowCellEditControl();
5773 }
5774 else
5775 {
5776 SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
5777
5778 HideCellEditControl();
5779 SaveEditControlValue();
5780
5781 // do it after HideCellEditControl()
5782 m_cellEditCtrlEnabled = enable;
5783 }
5784 }
5785 }
5786
5787 bool wxGrid::IsCurrentCellReadOnly() const
5788 {
5789 // const_cast
5790 wxGridCellAttr* attr = ((wxGrid *)this)->GetCellAttr(m_currentCellCoords);
5791 bool readonly = attr->IsReadOnly();
5792 attr->DecRef();
5793
5794 return readonly;
5795 }
5796
5797 bool wxGrid::CanEnableCellControl() const
5798 {
5799 return m_editable && (m_currentCellCoords != wxGridNoCellCoords) &&
5800 !IsCurrentCellReadOnly();
5801 }
5802
5803 bool wxGrid::IsCellEditControlEnabled() const
5804 {
5805 // the cell edit control might be disable for all cells or just for the
5806 // current one if it's read only
5807 return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : false;
5808 }
5809
5810 bool wxGrid::IsCellEditControlShown() const
5811 {
5812 bool isShown = false;
5813
5814 if ( m_cellEditCtrlEnabled )
5815 {
5816 int row = m_currentCellCoords.GetRow();
5817 int col = m_currentCellCoords.GetCol();
5818 wxGridCellAttr* attr = GetCellAttr(row, col);
5819 wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
5820 attr->DecRef();
5821
5822 if ( editor )
5823 {
5824 if ( editor->IsCreated() )
5825 {
5826 isShown = editor->GetControl()->IsShown();
5827 }
5828
5829 editor->DecRef();
5830 }
5831 }
5832
5833 return isShown;
5834 }
5835
5836 void wxGrid::ShowCellEditControl()
5837 {
5838 if ( IsCellEditControlEnabled() )
5839 {
5840 if ( !IsVisible( m_currentCellCoords, false ) )
5841 {
5842 m_cellEditCtrlEnabled = false;
5843 return;
5844 }
5845 else
5846 {
5847 wxRect rect = CellToRect( m_currentCellCoords );
5848 int row = m_currentCellCoords.GetRow();
5849 int col = m_currentCellCoords.GetCol();
5850
5851 // if this is part of a multicell, find owner (topleft)
5852 int cell_rows, cell_cols;
5853 GetCellSize( row, col, &cell_rows, &cell_cols );
5854 if ( cell_rows <= 0 || cell_cols <= 0 )
5855 {
5856 row += cell_rows;
5857 col += cell_cols;
5858 m_currentCellCoords.SetRow( row );
5859 m_currentCellCoords.SetCol( col );
5860 }
5861
5862 // erase the highlight and the cell contents because the editor
5863 // might not cover the entire cell
5864 wxClientDC dc( m_gridWin );
5865 PrepareDC( dc );
5866 wxGridCellAttr* attr = GetCellAttr(row, col);
5867 dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
5868 dc.SetPen(*wxTRANSPARENT_PEN);
5869 dc.DrawRectangle(rect);
5870
5871 // convert to scrolled coords
5872 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
5873
5874 int nXMove = 0;
5875 if (rect.x < 0)
5876 nXMove = rect.x;
5877
5878 // cell is shifted by one pixel
5879 // However, don't allow x or y to become negative
5880 // since the SetSize() method interprets that as
5881 // "don't change."
5882 if (rect.x > 0)
5883 rect.x--;
5884 if (rect.y > 0)
5885 rect.y--;
5886
5887 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
5888 if ( !editor->IsCreated() )
5889 {
5890 editor->Create(m_gridWin, wxID_ANY,
5891 new wxGridCellEditorEvtHandler(this, editor));
5892
5893 wxGridEditorCreatedEvent evt(GetId(),
5894 wxEVT_GRID_EDITOR_CREATED,
5895 this,
5896 row,
5897 col,
5898 editor->GetControl());
5899 GetEventHandler()->ProcessEvent(evt);
5900 }
5901
5902 // resize editor to overflow into righthand cells if allowed
5903 int maxWidth = rect.width;
5904 wxString value = GetCellValue(row, col);
5905 if ( (value != wxEmptyString) && (attr->GetOverflow()) )
5906 {
5907 int y;
5908 GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
5909 if (maxWidth < rect.width)
5910 maxWidth = rect.width;
5911 }
5912
5913 int client_right = m_gridWin->GetClientSize().GetWidth();
5914 if (rect.x + maxWidth > client_right)
5915 maxWidth = client_right - rect.x;
5916
5917 if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
5918 {
5919 GetCellSize( row, col, &cell_rows, &cell_cols );
5920 // may have changed earlier
5921 for (int i = col + cell_cols; i < m_numCols; i++)
5922 {
5923 int c_rows, c_cols;
5924 GetCellSize( row, i, &c_rows, &c_cols );
5925
5926 // looks weird going over a multicell
5927 if (m_table->IsEmptyCell( row, i ) &&
5928 (rect.width < maxWidth) && (c_rows == 1))
5929 {
5930 rect.width += GetColWidth( i );
5931 }
5932 else
5933 break;
5934 }
5935
5936 if (rect.GetRight() > client_right)
5937 rect.SetRight( client_right - 1 );
5938 }
5939
5940 editor->SetCellAttr( attr );
5941 editor->SetSize( rect );
5942 if (nXMove != 0)
5943 editor->GetControl()->Move(
5944 editor->GetControl()->GetPosition().x + nXMove,
5945 editor->GetControl()->GetPosition().y );
5946 editor->Show( true, attr );
5947
5948 // recalc dimensions in case we need to
5949 // expand the scrolled window to account for editor
5950 CalcDimensions();
5951
5952 editor->BeginEdit(row, col, this);
5953 editor->SetCellAttr(NULL);
5954
5955 editor->DecRef();
5956 attr->DecRef();
5957 }
5958 }
5959 }
5960
5961 void wxGrid::HideCellEditControl()
5962 {
5963 if ( IsCellEditControlEnabled() )
5964 {
5965 int row = m_currentCellCoords.GetRow();
5966 int col = m_currentCellCoords.GetCol();
5967
5968 wxGridCellAttr *attr = GetCellAttr(row, col);
5969 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
5970 const bool editorHadFocus = editor->GetControl()->HasFocus();
5971 editor->Show( false );
5972 editor->DecRef();
5973 attr->DecRef();
5974
5975 // return the focus to the grid itself if the editor had it
5976 //
5977 // note that we must not do this unconditionally to avoid stealing
5978 // focus from the window which just received it if we are hiding the
5979 // editor precisely because we lost focus
5980 if ( editorHadFocus )
5981 m_gridWin->SetFocus();
5982
5983 // refresh whole row to the right
5984 wxRect rect( CellToRect(row, col) );
5985 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
5986 rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
5987
5988 #ifdef __WXMAC__
5989 // ensure that the pixels under the focus ring get refreshed as well
5990 rect.Inflate(10, 10);
5991 #endif
5992
5993 m_gridWin->Refresh( false, &rect );
5994 }
5995 }
5996
5997 void wxGrid::SaveEditControlValue()
5998 {
5999 if ( IsCellEditControlEnabled() )
6000 {
6001 int row = m_currentCellCoords.GetRow();
6002 int col = m_currentCellCoords.GetCol();
6003
6004 wxString oldval = GetCellValue(row, col);
6005
6006 wxGridCellAttr* attr = GetCellAttr(row, col);
6007 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
6008
6009 wxString newval;
6010 bool changed = editor->EndEdit(row, col, this, oldval, &newval);
6011
6012 if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
6013 {
6014 editor->ApplyEdit(row, col, this);
6015
6016 // for compatibility reasons dating back to wx 2.8 when this event
6017 // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
6018 // didn't exist we allow vetoing this one too
6019 if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
6020 {
6021 // Event has been vetoed, set the data back.
6022 SetCellValue(row, col, oldval);
6023 }
6024 }
6025
6026 editor->DecRef();
6027 attr->DecRef();
6028 }
6029 }
6030
6031 //
6032 // ------ Grid location functions
6033 // Note that all of these functions work with the logical coordinates of
6034 // grid cells and labels so you will need to convert from device
6035 // coordinates for mouse events etc.
6036 //
6037
6038 wxGridCellCoords wxGrid::XYToCell(int x, int y) const
6039 {
6040 int row = YToRow(y);
6041 int col = XToCol(x);
6042
6043 return row == -1 || col == -1 ? wxGridNoCellCoords
6044 : wxGridCellCoords(row, col);
6045 }
6046
6047 // compute row or column from some (unscrolled) coordinate value, using either
6048 // m_defaultRowHeight/m_defaultColWidth or binary search on array of
6049 // m_rowBottoms/m_colRights to do it quickly (linear search shouldn't be used
6050 // for large grids)
6051 int wxGrid::PosToLinePos(int coord,
6052 bool clipToMinMax,
6053 const wxGridOperations& oper) const
6054 {
6055 const int numLines = oper.GetNumberOfLines(this);
6056
6057 if ( coord < 0 )
6058 return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND;
6059
6060 const int defaultLineSize = oper.GetDefaultLineSize(this);
6061 wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
6062
6063 int maxPos = coord / defaultLineSize,
6064 minPos = 0;
6065
6066 // check for the simplest case: if we have no explicit line sizes
6067 // configured, then we already know the line this position falls in
6068 const wxArrayInt& lineEnds = oper.GetLineEnds(this);
6069 if ( lineEnds.empty() )
6070 {
6071 if ( maxPos < numLines )
6072 return maxPos;
6073
6074 return clipToMinMax ? numLines - 1 : -1;
6075 }
6076
6077
6078 // adjust maxPos before starting the binary search
6079 if ( maxPos >= numLines )
6080 {
6081 maxPos = numLines - 1;
6082 }
6083 else
6084 {
6085 if ( coord >= lineEnds[oper.GetLineAt(this, maxPos)])
6086 {
6087 minPos = maxPos;
6088 const int minDist = oper.GetMinimalAcceptableLineSize(this);
6089 if ( minDist )
6090 maxPos = coord / minDist;
6091 else
6092 maxPos = numLines - 1;
6093 }
6094
6095 if ( maxPos >= numLines )
6096 maxPos = numLines - 1;
6097 }
6098
6099 // check if the position is beyond the last column
6100 const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
6101 if ( coord >= lineEnds[lineAtMaxPos] )
6102 return clipToMinMax ? maxPos : -1;
6103
6104 // or before the first one
6105 const int lineAt0 = oper.GetLineAt(this, 0);
6106 if ( coord < lineEnds[lineAt0] )
6107 return 0;
6108
6109
6110 // finally do perform the binary search
6111 while ( minPos < maxPos )
6112 {
6113 wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
6114 coord < lineEnds[oper.GetLineAt(this, maxPos)],
6115 -1,
6116 "wxGrid: internal error in PosToLinePos()" );
6117
6118 if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
6119 return maxPos;
6120 else
6121 maxPos--;
6122
6123 const int median = minPos + (maxPos - minPos + 1) / 2;
6124 if ( coord < lineEnds[oper.GetLineAt(this, median)] )
6125 maxPos = median;
6126 else
6127 minPos = median;
6128 }
6129
6130 return maxPos;
6131 }
6132
6133 int
6134 wxGrid::PosToLine(int coord,
6135 bool clipToMinMax,
6136 const wxGridOperations& oper) const
6137 {
6138 int pos = PosToLinePos(coord, clipToMinMax, oper);
6139
6140 return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos);
6141 }
6142
6143 int wxGrid::YToRow(int y, bool clipToMinMax) const
6144 {
6145 return PosToLine(y, clipToMinMax, wxGridRowOperations());
6146 }
6147
6148 int wxGrid::XToCol(int x, bool clipToMinMax) const
6149 {
6150 return PosToLine(x, clipToMinMax, wxGridColumnOperations());
6151 }
6152
6153 int wxGrid::XToPos(int x) const
6154 {
6155 return PosToLinePos(x, true /* clip */, wxGridColumnOperations());
6156 }
6157
6158 // return the row number such that the y coord is near the edge of, or -1 if
6159 // not near an edge.
6160 //
6161 // notice that position can only possibly be near an edge if the row/column is
6162 // large enough to still allow for an "inner" area that is _not_ near the edge
6163 // (i.e., if the height/width is smaller than WXGRID_LABEL_EDGE_ZONE, pos will
6164 // _never_ be considered to be near the edge).
6165 int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
6166 {
6167 const int line = oper.PosToLine(this, pos, true);
6168
6169 if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE )
6170 {
6171 // We know that we are in this line, test whether we are close enough
6172 // to start or end border, respectively.
6173 if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE )
6174 return line;
6175 else if ( line > 0 &&
6176 pos - oper.GetLineStartPos(this,
6177 line) < WXGRID_LABEL_EDGE_ZONE )
6178 return line - 1;
6179 }
6180
6181 return -1;
6182 }
6183
6184 int wxGrid::YToEdgeOfRow(int y) const
6185 {
6186 return PosToEdgeOfLine(y, wxGridRowOperations());
6187 }
6188
6189 int wxGrid::XToEdgeOfCol(int x) const
6190 {
6191 return PosToEdgeOfLine(x, wxGridColumnOperations());
6192 }
6193
6194 wxRect wxGrid::CellToRect( int row, int col ) const
6195 {
6196 wxRect rect( -1, -1, -1, -1 );
6197
6198 if ( row >= 0 && row < m_numRows &&
6199 col >= 0 && col < m_numCols )
6200 {
6201 int i, cell_rows, cell_cols;
6202 rect.width = rect.height = 0;
6203 GetCellSize( row, col, &cell_rows, &cell_cols );
6204 // if negative then find multicell owner
6205 if (cell_rows < 0)
6206 row += cell_rows;
6207 if (cell_cols < 0)
6208 col += cell_cols;
6209 GetCellSize( row, col, &cell_rows, &cell_cols );
6210
6211 rect.x = GetColLeft(col);
6212 rect.y = GetRowTop(row);
6213 for (i=col; i < col + cell_cols; i++)
6214 rect.width += GetColWidth(i);
6215 for (i=row; i < row + cell_rows; i++)
6216 rect.height += GetRowHeight(i);
6217
6218 // if grid lines are enabled, then the area of the cell is a bit smaller
6219 if (m_gridLinesEnabled)
6220 {
6221 rect.width -= 1;
6222 rect.height -= 1;
6223 }
6224 }
6225
6226 return rect;
6227 }
6228
6229 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const
6230 {
6231 // get the cell rectangle in logical coords
6232 //
6233 wxRect r( CellToRect( row, col ) );
6234
6235 // convert to device coords
6236 //
6237 int left, top, right, bottom;
6238 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
6239 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
6240
6241 // check against the client area of the grid window
6242 int cw, ch;
6243 m_gridWin->GetClientSize( &cw, &ch );
6244
6245 if ( wholeCellVisible )
6246 {
6247 // is the cell wholly visible ?
6248 return ( left >= 0 && right <= cw &&
6249 top >= 0 && bottom <= ch );
6250 }
6251 else
6252 {
6253 // is the cell partly visible ?
6254 //
6255 return ( ((left >= 0 && left < cw) || (right > 0 && right <= cw)) &&
6256 ((top >= 0 && top < ch) || (bottom > 0 && bottom <= ch)) );
6257 }
6258 }
6259
6260 // make the specified cell location visible by doing a minimal amount
6261 // of scrolling
6262 //
6263 void wxGrid::MakeCellVisible( int row, int col )
6264 {
6265 int i;
6266 int xpos = -1, ypos = -1;
6267
6268 if ( row >= 0 && row < m_numRows &&
6269 col >= 0 && col < m_numCols )
6270 {
6271 // get the cell rectangle in logical coords
6272 wxRect r( CellToRect( row, col ) );
6273
6274 // convert to device coords
6275 int left, top, right, bottom;
6276 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
6277 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
6278
6279 int cw, ch;
6280 m_gridWin->GetClientSize( &cw, &ch );
6281
6282 if ( top < 0 )
6283 {
6284 ypos = r.GetTop();
6285 }
6286 else if ( bottom > ch )
6287 {
6288 int h = r.GetHeight();
6289 ypos = r.GetTop();
6290 for ( i = row - 1; i >= 0; i-- )
6291 {
6292 int rowHeight = GetRowHeight(i);
6293 if ( h + rowHeight > ch )
6294 break;
6295
6296 h += rowHeight;
6297 ypos -= rowHeight;
6298 }
6299
6300 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
6301 // have rounding errors (this is important, because if we do,
6302 // we might not scroll at all and some cells won't be redrawn)
6303 //
6304 // Sometimes GRID_SCROLL_LINE / 2 is not enough,
6305 // so just add a full scroll unit...
6306 ypos += m_scrollLineY;
6307 }
6308
6309 // special handling for wide cells - show always left part of the cell!
6310 // Otherwise, e.g. when stepping from row to row, it would jump between
6311 // left and right part of the cell on every step!
6312 // if ( left < 0 )
6313 if ( left < 0 || (right - left) >= cw )
6314 {
6315 xpos = r.GetLeft();
6316 }
6317 else if ( right > cw )
6318 {
6319 // position the view so that the cell is on the right
6320 int x0, y0;
6321 CalcUnscrolledPosition(0, 0, &x0, &y0);
6322 xpos = x0 + (right - cw);
6323
6324 // see comment for ypos above
6325 xpos += m_scrollLineX;
6326 }
6327
6328 if ( xpos != -1 || ypos != -1 )
6329 {
6330 if ( xpos != -1 )
6331 xpos /= m_scrollLineX;
6332 if ( ypos != -1 )
6333 ypos /= m_scrollLineY;
6334 Scroll( xpos, ypos );
6335 AdjustScrollbars();
6336 }
6337 }
6338 }
6339
6340 //
6341 // ------ Grid cursor movement functions
6342 //
6343
6344 bool
6345 wxGrid::DoMoveCursor(bool expandSelection,
6346 const wxGridDirectionOperations& diroper)
6347 {
6348 if ( m_currentCellCoords == wxGridNoCellCoords )
6349 return false;
6350
6351 if ( expandSelection )
6352 {
6353 wxGridCellCoords coords = m_selectedBlockCorner;
6354 if ( coords == wxGridNoCellCoords )
6355 coords = m_currentCellCoords;
6356
6357 if ( diroper.IsAtBoundary(coords) )
6358 return false;
6359
6360 diroper.Advance(coords);
6361
6362 UpdateBlockBeingSelected(m_currentCellCoords, coords);
6363 }
6364 else // don't expand selection
6365 {
6366 ClearSelection();
6367
6368 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6369 return false;
6370
6371 wxGridCellCoords coords = m_currentCellCoords;
6372 diroper.Advance(coords);
6373
6374 GoToCell(coords);
6375 }
6376
6377 return true;
6378 }
6379
6380 bool wxGrid::MoveCursorUp(bool expandSelection)
6381 {
6382 return DoMoveCursor(expandSelection,
6383 wxGridBackwardOperations(this, wxGridRowOperations()));
6384 }
6385
6386 bool wxGrid::MoveCursorDown(bool expandSelection)
6387 {
6388 return DoMoveCursor(expandSelection,
6389 wxGridForwardOperations(this, wxGridRowOperations()));
6390 }
6391
6392 bool wxGrid::MoveCursorLeft(bool expandSelection)
6393 {
6394 return DoMoveCursor(expandSelection,
6395 wxGridBackwardOperations(this, wxGridColumnOperations()));
6396 }
6397
6398 bool wxGrid::MoveCursorRight(bool expandSelection)
6399 {
6400 return DoMoveCursor(expandSelection,
6401 wxGridForwardOperations(this, wxGridColumnOperations()));
6402 }
6403
6404 bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper)
6405 {
6406 if ( m_currentCellCoords == wxGridNoCellCoords )
6407 return false;
6408
6409 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6410 return false;
6411
6412 const int oldRow = m_currentCellCoords.GetRow();
6413 int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y);
6414 if ( newRow == oldRow )
6415 {
6416 wxGridCellCoords coords(m_currentCellCoords);
6417 diroper.Advance(coords);
6418 newRow = coords.GetRow();
6419 }
6420
6421 GoToCell(newRow, m_currentCellCoords.GetCol());
6422
6423 return true;
6424 }
6425
6426 bool wxGrid::MovePageUp()
6427 {
6428 return DoMoveCursorByPage(
6429 wxGridBackwardOperations(this, wxGridRowOperations()));
6430 }
6431
6432 bool wxGrid::MovePageDown()
6433 {
6434 return DoMoveCursorByPage(
6435 wxGridForwardOperations(this, wxGridRowOperations()));
6436 }
6437
6438 // helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper
6439 // until we find a non-empty cell or reach the grid end
6440 void
6441 wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords,
6442 const wxGridDirectionOperations& diroper)
6443 {
6444 while ( !diroper.IsAtBoundary(coords) )
6445 {
6446 diroper.Advance(coords);
6447 if ( !m_table->IsEmpty(coords) )
6448 break;
6449 }
6450 }
6451
6452 bool
6453 wxGrid::DoMoveCursorByBlock(bool expandSelection,
6454 const wxGridDirectionOperations& diroper)
6455 {
6456 if ( !m_table || m_currentCellCoords == wxGridNoCellCoords )
6457 return false;
6458
6459 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6460 return false;
6461
6462 wxGridCellCoords coords(m_currentCellCoords);
6463 if ( m_table->IsEmpty(coords) )
6464 {
6465 // we are in an empty cell: find the next block of non-empty cells
6466 AdvanceToNextNonEmpty(coords, diroper);
6467 }
6468 else // current cell is not empty
6469 {
6470 diroper.Advance(coords);
6471 if ( m_table->IsEmpty(coords) )
6472 {
6473 // we started at the end of a block, find the next one
6474 AdvanceToNextNonEmpty(coords, diroper);
6475 }
6476 else // we're in a middle of a block
6477 {
6478 // go to the end of it, i.e. find the last cell before the next
6479 // empty one
6480 while ( !diroper.IsAtBoundary(coords) )
6481 {
6482 wxGridCellCoords coordsNext(coords);
6483 diroper.Advance(coordsNext);
6484 if ( m_table->IsEmpty(coordsNext) )
6485 break;
6486
6487 coords = coordsNext;
6488 }
6489 }
6490 }
6491
6492 if ( expandSelection )
6493 {
6494 UpdateBlockBeingSelected(m_currentCellCoords, coords);
6495 }
6496 else
6497 {
6498 ClearSelection();
6499 GoToCell(coords);
6500 }
6501
6502 return true;
6503 }
6504
6505 bool wxGrid::MoveCursorUpBlock(bool expandSelection)
6506 {
6507 return DoMoveCursorByBlock(
6508 expandSelection,
6509 wxGridBackwardOperations(this, wxGridRowOperations())
6510 );
6511 }
6512
6513 bool wxGrid::MoveCursorDownBlock( bool expandSelection )
6514 {
6515 return DoMoveCursorByBlock(
6516 expandSelection,
6517 wxGridForwardOperations(this, wxGridRowOperations())
6518 );
6519 }
6520
6521 bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
6522 {
6523 return DoMoveCursorByBlock(
6524 expandSelection,
6525 wxGridBackwardOperations(this, wxGridColumnOperations())
6526 );
6527 }
6528
6529 bool wxGrid::MoveCursorRightBlock( bool expandSelection )
6530 {
6531 return DoMoveCursorByBlock(
6532 expandSelection,
6533 wxGridForwardOperations(this, wxGridColumnOperations())
6534 );
6535 }
6536
6537 //
6538 // ------ Label values and formatting
6539 //
6540
6541 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) const
6542 {
6543 if ( horiz )
6544 *horiz = m_rowLabelHorizAlign;
6545 if ( vert )
6546 *vert = m_rowLabelVertAlign;
6547 }
6548
6549 void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) const
6550 {
6551 if ( horiz )
6552 *horiz = m_colLabelHorizAlign;
6553 if ( vert )
6554 *vert = m_colLabelVertAlign;
6555 }
6556
6557 int wxGrid::GetColLabelTextOrientation() const
6558 {
6559 return m_colLabelTextOrientation;
6560 }
6561
6562 wxString wxGrid::GetRowLabelValue( int row ) const
6563 {
6564 if ( m_table )
6565 {
6566 return m_table->GetRowLabelValue( row );
6567 }
6568 else
6569 {
6570 wxString s;
6571 s << row;
6572 return s;
6573 }
6574 }
6575
6576 wxString wxGrid::GetColLabelValue( int col ) const
6577 {
6578 if ( m_table )
6579 {
6580 return m_table->GetColLabelValue( col );
6581 }
6582 else
6583 {
6584 wxString s;
6585 s << col;
6586 return s;
6587 }
6588 }
6589
6590 void wxGrid::SetRowLabelSize( int width )
6591 {
6592 wxASSERT( width >= 0 || width == wxGRID_AUTOSIZE );
6593
6594 if ( width == wxGRID_AUTOSIZE )
6595 {
6596 width = CalcColOrRowLabelAreaMinSize(wxGRID_ROW);
6597 }
6598
6599 if ( width != m_rowLabelWidth )
6600 {
6601 if ( width == 0 )
6602 {
6603 m_rowLabelWin->Show( false );
6604 m_cornerLabelWin->Show( false );
6605 }
6606 else if ( m_rowLabelWidth == 0 )
6607 {
6608 m_rowLabelWin->Show( true );
6609 if ( m_colLabelHeight > 0 )
6610 m_cornerLabelWin->Show( true );
6611 }
6612
6613 m_rowLabelWidth = width;
6614 CalcWindowSizes();
6615 wxScrolledWindow::Refresh( true );
6616 }
6617 }
6618
6619 void wxGrid::SetColLabelSize( int height )
6620 {
6621 wxASSERT( height >=0 || height == wxGRID_AUTOSIZE );
6622
6623 if ( height == wxGRID_AUTOSIZE )
6624 {
6625 height = CalcColOrRowLabelAreaMinSize(wxGRID_COLUMN);
6626 }
6627
6628 if ( height != m_colLabelHeight )
6629 {
6630 if ( height == 0 )
6631 {
6632 m_colWindow->Show( false );
6633 m_cornerLabelWin->Show( false );
6634 }
6635 else if ( m_colLabelHeight == 0 )
6636 {
6637 m_colWindow->Show( true );
6638 if ( m_rowLabelWidth > 0 )
6639 m_cornerLabelWin->Show( true );
6640 }
6641
6642 m_colLabelHeight = height;
6643 CalcWindowSizes();
6644 wxScrolledWindow::Refresh( true );
6645 }
6646 }
6647
6648 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
6649 {
6650 if ( m_labelBackgroundColour != colour )
6651 {
6652 m_labelBackgroundColour = colour;
6653 m_rowLabelWin->SetBackgroundColour( colour );
6654 m_colWindow->SetBackgroundColour( colour );
6655 m_cornerLabelWin->SetBackgroundColour( colour );
6656
6657 if ( !GetBatchCount() )
6658 {
6659 m_rowLabelWin->Refresh();
6660 m_colWindow->Refresh();
6661 m_cornerLabelWin->Refresh();
6662 }
6663 }
6664 }
6665
6666 void wxGrid::SetLabelTextColour( const wxColour& colour )
6667 {
6668 if ( m_labelTextColour != colour )
6669 {
6670 m_labelTextColour = colour;
6671 if ( !GetBatchCount() )
6672 {
6673 m_rowLabelWin->Refresh();
6674 m_colWindow->Refresh();
6675 }
6676 }
6677 }
6678
6679 void wxGrid::SetLabelFont( const wxFont& font )
6680 {
6681 m_labelFont = font;
6682 if ( !GetBatchCount() )
6683 {
6684 m_rowLabelWin->Refresh();
6685 m_colWindow->Refresh();
6686 }
6687 }
6688
6689 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
6690 {
6691 // allow old (incorrect) defs to be used
6692 switch ( horiz )
6693 {
6694 case wxLEFT: horiz = wxALIGN_LEFT; break;
6695 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
6696 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
6697 }
6698
6699 switch ( vert )
6700 {
6701 case wxTOP: vert = wxALIGN_TOP; break;
6702 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
6703 case wxCENTRE: vert = wxALIGN_CENTRE; break;
6704 }
6705
6706 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
6707 {
6708 m_rowLabelHorizAlign = horiz;
6709 }
6710
6711 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
6712 {
6713 m_rowLabelVertAlign = vert;
6714 }
6715
6716 if ( !GetBatchCount() )
6717 {
6718 m_rowLabelWin->Refresh();
6719 }
6720 }
6721
6722 void wxGrid::SetColLabelAlignment( int horiz, int vert )
6723 {
6724 // allow old (incorrect) defs to be used
6725 switch ( horiz )
6726 {
6727 case wxLEFT: horiz = wxALIGN_LEFT; break;
6728 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
6729 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
6730 }
6731
6732 switch ( vert )
6733 {
6734 case wxTOP: vert = wxALIGN_TOP; break;
6735 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
6736 case wxCENTRE: vert = wxALIGN_CENTRE; break;
6737 }
6738
6739 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
6740 {
6741 m_colLabelHorizAlign = horiz;
6742 }
6743
6744 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
6745 {
6746 m_colLabelVertAlign = vert;
6747 }
6748
6749 if ( !GetBatchCount() )
6750 {
6751 m_colWindow->Refresh();
6752 }
6753 }
6754
6755 // Note: under MSW, the default column label font must be changed because it
6756 // does not support vertical printing
6757 //
6758 // Example: wxFont font(9, wxSWISS, wxNORMAL, wxBOLD);
6759 // pGrid->SetLabelFont(font);
6760 // pGrid->SetColLabelTextOrientation(wxVERTICAL);
6761 //
6762 void wxGrid::SetColLabelTextOrientation( int textOrientation )
6763 {
6764 if ( textOrientation == wxHORIZONTAL || textOrientation == wxVERTICAL )
6765 m_colLabelTextOrientation = textOrientation;
6766
6767 if ( !GetBatchCount() )
6768 m_colWindow->Refresh();
6769 }
6770
6771 void wxGrid::SetRowLabelValue( int row, const wxString& s )
6772 {
6773 if ( m_table )
6774 {
6775 m_table->SetRowLabelValue( row, s );
6776 if ( !GetBatchCount() )
6777 {
6778 wxRect rect = CellToRect( row, 0 );
6779 if ( rect.height > 0 )
6780 {
6781 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
6782 rect.x = 0;
6783 rect.width = m_rowLabelWidth;
6784 m_rowLabelWin->Refresh( true, &rect );
6785 }
6786 }
6787 }
6788 }
6789
6790 void wxGrid::SetColLabelValue( int col, const wxString& s )
6791 {
6792 if ( m_table )
6793 {
6794 m_table->SetColLabelValue( col, s );
6795 if ( !GetBatchCount() )
6796 {
6797 if ( m_useNativeHeader )
6798 {
6799 GetGridColHeader()->UpdateColumn(col);
6800 }
6801 else
6802 {
6803 wxRect rect = CellToRect( 0, col );
6804 if ( rect.width > 0 )
6805 {
6806 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
6807 rect.y = 0;
6808 rect.height = m_colLabelHeight;
6809 GetColLabelWindow()->Refresh( true, &rect );
6810 }
6811 }
6812 }
6813 }
6814 }
6815
6816 void wxGrid::SetGridLineColour( const wxColour& colour )
6817 {
6818 if ( m_gridLineColour != colour )
6819 {
6820 m_gridLineColour = colour;
6821
6822 if ( GridLinesEnabled() )
6823 RedrawGridLines();
6824 }
6825 }
6826
6827 void wxGrid::SetCellHighlightColour( const wxColour& colour )
6828 {
6829 if ( m_cellHighlightColour != colour )
6830 {
6831 m_cellHighlightColour = colour;
6832
6833 wxClientDC dc( m_gridWin );
6834 PrepareDC( dc );
6835 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
6836 DrawCellHighlight(dc, attr);
6837 attr->DecRef();
6838 }
6839 }
6840
6841 void wxGrid::SetCellHighlightPenWidth(int width)
6842 {
6843 if (m_cellHighlightPenWidth != width)
6844 {
6845 m_cellHighlightPenWidth = width;
6846
6847 // Just redrawing the cell highlight is not enough since that won't
6848 // make any visible change if the the thickness is getting smaller.
6849 int row = m_currentCellCoords.GetRow();
6850 int col = m_currentCellCoords.GetCol();
6851 if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
6852 return;
6853
6854 wxRect rect = CellToRect(row, col);
6855 m_gridWin->Refresh(true, &rect);
6856 }
6857 }
6858
6859 void wxGrid::SetCellHighlightROPenWidth(int width)
6860 {
6861 if (m_cellHighlightROPenWidth != width)
6862 {
6863 m_cellHighlightROPenWidth = width;
6864
6865 // Just redrawing the cell highlight is not enough since that won't
6866 // make any visible change if the the thickness is getting smaller.
6867 int row = m_currentCellCoords.GetRow();
6868 int col = m_currentCellCoords.GetCol();
6869 if ( row == -1 || col == -1 ||
6870 GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
6871 return;
6872
6873 wxRect rect = CellToRect(row, col);
6874 m_gridWin->Refresh(true, &rect);
6875 }
6876 }
6877
6878 void wxGrid::RedrawGridLines()
6879 {
6880 // the lines will be redrawn when the window is thawn
6881 if ( GetBatchCount() )
6882 return;
6883
6884 if ( GridLinesEnabled() )
6885 {
6886 wxClientDC dc( m_gridWin );
6887 PrepareDC( dc );
6888 DrawAllGridLines( dc, wxRegion() );
6889 }
6890 else // remove the grid lines
6891 {
6892 m_gridWin->Refresh();
6893 }
6894 }
6895
6896 void wxGrid::EnableGridLines( bool enable )
6897 {
6898 if ( enable != m_gridLinesEnabled )
6899 {
6900 m_gridLinesEnabled = enable;
6901
6902 RedrawGridLines();
6903 }
6904 }
6905
6906 void wxGrid::DoClipGridLines(bool& var, bool clip)
6907 {
6908 if ( clip != var )
6909 {
6910 var = clip;
6911
6912 if ( GridLinesEnabled() )
6913 RedrawGridLines();
6914 }
6915 }
6916
6917 int wxGrid::GetDefaultRowSize() const
6918 {
6919 return m_defaultRowHeight;
6920 }
6921
6922 int wxGrid::GetRowSize( int row ) const
6923 {
6924 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, wxT("invalid row index") );
6925
6926 return GetRowHeight(row);
6927 }
6928
6929 int wxGrid::GetDefaultColSize() const
6930 {
6931 return m_defaultColWidth;
6932 }
6933
6934 int wxGrid::GetColSize( int col ) const
6935 {
6936 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, wxT("invalid column index") );
6937
6938 return GetColWidth(col);
6939 }
6940
6941 // ============================================================================
6942 // access to the grid attributes: each of them has a default value in the grid
6943 // itself and may be overidden on a per-cell basis
6944 // ============================================================================
6945
6946 // ----------------------------------------------------------------------------
6947 // setting default attributes
6948 // ----------------------------------------------------------------------------
6949
6950 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
6951 {
6952 m_defaultCellAttr->SetBackgroundColour(col);
6953 #ifdef __WXGTK__
6954 m_gridWin->SetBackgroundColour(col);
6955 #endif
6956 }
6957
6958 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
6959 {
6960 m_defaultCellAttr->SetTextColour(col);
6961 }
6962
6963 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
6964 {
6965 m_defaultCellAttr->SetAlignment(horiz, vert);
6966 }
6967
6968 void wxGrid::SetDefaultCellOverflow( bool allow )
6969 {
6970 m_defaultCellAttr->SetOverflow(allow);
6971 }
6972
6973 void wxGrid::SetDefaultCellFont( const wxFont& font )
6974 {
6975 m_defaultCellAttr->SetFont(font);
6976 }
6977
6978 // For editors and renderers the type registry takes precedence over the
6979 // default attr, so we need to register the new editor/renderer for the string
6980 // data type in order to make setting a default editor/renderer appear to
6981 // work correctly.
6982
6983 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
6984 {
6985 RegisterDataType(wxGRID_VALUE_STRING,
6986 renderer,
6987 GetDefaultEditorForType(wxGRID_VALUE_STRING));
6988 }
6989
6990 void wxGrid::SetDefaultEditor(wxGridCellEditor *editor)
6991 {
6992 RegisterDataType(wxGRID_VALUE_STRING,
6993 GetDefaultRendererForType(wxGRID_VALUE_STRING),
6994 editor);
6995 }
6996
6997 // ----------------------------------------------------------------------------
6998 // access to the default attributes
6999 // ----------------------------------------------------------------------------
7000
7001 wxColour wxGrid::GetDefaultCellBackgroundColour() const
7002 {
7003 return m_defaultCellAttr->GetBackgroundColour();
7004 }
7005
7006 wxColour wxGrid::GetDefaultCellTextColour() const
7007 {
7008 return m_defaultCellAttr->GetTextColour();
7009 }
7010
7011 wxFont wxGrid::GetDefaultCellFont() const
7012 {
7013 return m_defaultCellAttr->GetFont();
7014 }
7015
7016 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) const
7017 {
7018 m_defaultCellAttr->GetAlignment(horiz, vert);
7019 }
7020
7021 bool wxGrid::GetDefaultCellOverflow() const
7022 {
7023 return m_defaultCellAttr->GetOverflow();
7024 }
7025
7026 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
7027 {
7028 return m_defaultCellAttr->GetRenderer(NULL, 0, 0);
7029 }
7030
7031 wxGridCellEditor *wxGrid::GetDefaultEditor() const
7032 {
7033 return m_defaultCellAttr->GetEditor(NULL, 0, 0);
7034 }
7035
7036 // ----------------------------------------------------------------------------
7037 // access to cell attributes
7038 // ----------------------------------------------------------------------------
7039
7040 wxColour wxGrid::GetCellBackgroundColour(int row, int col) const
7041 {
7042 wxGridCellAttr *attr = GetCellAttr(row, col);
7043 wxColour colour = attr->GetBackgroundColour();
7044 attr->DecRef();
7045
7046 return colour;
7047 }
7048
7049 wxColour wxGrid::GetCellTextColour( int row, int col ) const
7050 {
7051 wxGridCellAttr *attr = GetCellAttr(row, col);
7052 wxColour colour = attr->GetTextColour();
7053 attr->DecRef();
7054
7055 return colour;
7056 }
7057
7058 wxFont wxGrid::GetCellFont( int row, int col ) const
7059 {
7060 wxGridCellAttr *attr = GetCellAttr(row, col);
7061 wxFont font = attr->GetFont();
7062 attr->DecRef();
7063
7064 return font;
7065 }
7066
7067 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) const
7068 {
7069 wxGridCellAttr *attr = GetCellAttr(row, col);
7070 attr->GetAlignment(horiz, vert);
7071 attr->DecRef();
7072 }
7073
7074 bool wxGrid::GetCellOverflow( int row, int col ) const
7075 {
7076 wxGridCellAttr *attr = GetCellAttr(row, col);
7077 bool allow = attr->GetOverflow();
7078 attr->DecRef();
7079
7080 return allow;
7081 }
7082
7083 void wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const
7084 {
7085 wxGridCellAttr *attr = GetCellAttr(row, col);
7086 attr->GetSize( num_rows, num_cols );
7087 attr->DecRef();
7088 }
7089
7090 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const
7091 {
7092 wxGridCellAttr* attr = GetCellAttr(row, col);
7093 wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
7094 attr->DecRef();
7095
7096 return renderer;
7097 }
7098
7099 wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) const
7100 {
7101 wxGridCellAttr* attr = GetCellAttr(row, col);
7102 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
7103 attr->DecRef();
7104
7105 return editor;
7106 }
7107
7108 bool wxGrid::IsReadOnly(int row, int col) const
7109 {
7110 wxGridCellAttr* attr = GetCellAttr(row, col);
7111 bool isReadOnly = attr->IsReadOnly();
7112 attr->DecRef();
7113
7114 return isReadOnly;
7115 }
7116
7117 // ----------------------------------------------------------------------------
7118 // attribute support: cache, automatic provider creation, ...
7119 // ----------------------------------------------------------------------------
7120
7121 bool wxGrid::CanHaveAttributes() const
7122 {
7123 if ( !m_table )
7124 {
7125 return false;
7126 }
7127
7128 return m_table->CanHaveAttributes();
7129 }
7130
7131 void wxGrid::ClearAttrCache()
7132 {
7133 if ( m_attrCache.row != -1 )
7134 {
7135 wxGridCellAttr *oldAttr = m_attrCache.attr;
7136 m_attrCache.attr = NULL;
7137 m_attrCache.row = -1;
7138 // wxSafeDecRec(...) might cause event processing that accesses
7139 // the cached attribute, if one exists (e.g. by deleting the
7140 // editor stored within the attribute). Therefore it is important
7141 // to invalidate the cache before calling wxSafeDecRef!
7142 wxSafeDecRef(oldAttr);
7143 }
7144 }
7145
7146 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
7147 {
7148 if ( attr != NULL )
7149 {
7150 wxGrid *self = (wxGrid *)this; // const_cast
7151
7152 self->ClearAttrCache();
7153 self->m_attrCache.row = row;
7154 self->m_attrCache.col = col;
7155 self->m_attrCache.attr = attr;
7156 wxSafeIncRef(attr);
7157 }
7158 }
7159
7160 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
7161 {
7162 if ( row == m_attrCache.row && col == m_attrCache.col )
7163 {
7164 *attr = m_attrCache.attr;
7165 wxSafeIncRef(m_attrCache.attr);
7166
7167 #ifdef DEBUG_ATTR_CACHE
7168 gs_nAttrCacheHits++;
7169 #endif
7170
7171 return true;
7172 }
7173 else
7174 {
7175 #ifdef DEBUG_ATTR_CACHE
7176 gs_nAttrCacheMisses++;
7177 #endif
7178
7179 return false;
7180 }
7181 }
7182
7183 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
7184 {
7185 wxGridCellAttr *attr = NULL;
7186 // Additional test to avoid looking at the cache e.g. for
7187 // wxNoCellCoords, as this will confuse memory management.
7188 if ( row >= 0 )
7189 {
7190 if ( !LookupAttr(row, col, &attr) )
7191 {
7192 attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any)
7193 : NULL;
7194 CacheAttr(row, col, attr);
7195 }
7196 }
7197
7198 if (attr)
7199 {
7200 attr->SetDefAttr(m_defaultCellAttr);
7201 }
7202 else
7203 {
7204 attr = m_defaultCellAttr;
7205 attr->IncRef();
7206 }
7207
7208 return attr;
7209 }
7210
7211 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
7212 {
7213 wxGridCellAttr *attr = NULL;
7214 bool canHave = ((wxGrid*)this)->CanHaveAttributes();
7215
7216 wxCHECK_MSG( canHave, attr, wxT("Cell attributes not allowed"));
7217 wxCHECK_MSG( m_table, attr, wxT("must have a table") );
7218
7219 attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell);
7220 if ( !attr )
7221 {
7222 attr = new wxGridCellAttr(m_defaultCellAttr);
7223
7224 // artificially inc the ref count to match DecRef() in caller
7225 attr->IncRef();
7226 m_table->SetAttr(attr, row, col);
7227 }
7228
7229 return attr;
7230 }
7231
7232 // ----------------------------------------------------------------------------
7233 // setting column attributes (wrappers around SetColAttr)
7234 // ----------------------------------------------------------------------------
7235
7236 void wxGrid::SetColFormatBool(int col)
7237 {
7238 SetColFormatCustom(col, wxGRID_VALUE_BOOL);
7239 }
7240
7241 void wxGrid::SetColFormatNumber(int col)
7242 {
7243 SetColFormatCustom(col, wxGRID_VALUE_NUMBER);
7244 }
7245
7246 void wxGrid::SetColFormatFloat(int col, int width, int precision)
7247 {
7248 wxString typeName = wxGRID_VALUE_FLOAT;
7249 if ( (width != -1) || (precision != -1) )
7250 {
7251 typeName << wxT(':') << width << wxT(',') << precision;
7252 }
7253
7254 SetColFormatCustom(col, typeName);
7255 }
7256
7257 void wxGrid::SetColFormatCustom(int col, const wxString& typeName)
7258 {
7259 wxGridCellAttr *attr = m_table->GetAttr(-1, col, wxGridCellAttr::Col );
7260 if (!attr)
7261 attr = new wxGridCellAttr;
7262 wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName);
7263 attr->SetRenderer(renderer);
7264 wxGridCellEditor *editor = GetDefaultEditorForType(typeName);
7265 attr->SetEditor(editor);
7266
7267 SetColAttr(col, attr);
7268
7269 }
7270
7271 // ----------------------------------------------------------------------------
7272 // setting cell attributes: this is forwarded to the table
7273 // ----------------------------------------------------------------------------
7274
7275 void wxGrid::SetAttr(int row, int col, wxGridCellAttr *attr)
7276 {
7277 if ( CanHaveAttributes() )
7278 {
7279 m_table->SetAttr(attr, row, col);
7280 ClearAttrCache();
7281 }
7282 else
7283 {
7284 wxSafeDecRef(attr);
7285 }
7286 }
7287
7288 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
7289 {
7290 if ( CanHaveAttributes() )
7291 {
7292 m_table->SetRowAttr(attr, row);
7293 ClearAttrCache();
7294 }
7295 else
7296 {
7297 wxSafeDecRef(attr);
7298 }
7299 }
7300
7301 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
7302 {
7303 if ( CanHaveAttributes() )
7304 {
7305 m_table->SetColAttr(attr, col);
7306 ClearAttrCache();
7307 }
7308 else
7309 {
7310 wxSafeDecRef(attr);
7311 }
7312 }
7313
7314 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
7315 {
7316 if ( CanHaveAttributes() )
7317 {
7318 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7319 attr->SetBackgroundColour(colour);
7320 attr->DecRef();
7321 }
7322 }
7323
7324 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
7325 {
7326 if ( CanHaveAttributes() )
7327 {
7328 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7329 attr->SetTextColour(colour);
7330 attr->DecRef();
7331 }
7332 }
7333
7334 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
7335 {
7336 if ( CanHaveAttributes() )
7337 {
7338 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7339 attr->SetFont(font);
7340 attr->DecRef();
7341 }
7342 }
7343
7344 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
7345 {
7346 if ( CanHaveAttributes() )
7347 {
7348 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7349 attr->SetAlignment(horiz, vert);
7350 attr->DecRef();
7351 }
7352 }
7353
7354 void wxGrid::SetCellOverflow( int row, int col, bool allow )
7355 {
7356 if ( CanHaveAttributes() )
7357 {
7358 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7359 attr->SetOverflow(allow);
7360 attr->DecRef();
7361 }
7362 }
7363
7364 void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols )
7365 {
7366 if ( CanHaveAttributes() )
7367 {
7368 int cell_rows, cell_cols;
7369
7370 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7371 attr->GetSize(&cell_rows, &cell_cols);
7372 attr->SetSize(num_rows, num_cols);
7373 attr->DecRef();
7374
7375 // Cannot set the size of a cell to 0 or negative values
7376 // While it is perfectly legal to do that, this function cannot
7377 // handle all the possibilies, do it by hand by getting the CellAttr.
7378 // You can only set the size of a cell to 1,1 or greater with this fn
7379 wxASSERT_MSG( !((cell_rows < 1) || (cell_cols < 1)),
7380 wxT("wxGrid::SetCellSize setting cell size that is already part of another cell"));
7381 wxASSERT_MSG( !((num_rows < 1) || (num_cols < 1)),
7382 wxT("wxGrid::SetCellSize setting cell size to < 1"));
7383
7384 // if this was already a multicell then "turn off" the other cells first
7385 if ((cell_rows > 1) || (cell_cols > 1))
7386 {
7387 int i, j;
7388 for (j=row; j < row + cell_rows; j++)
7389 {
7390 for (i=col; i < col + cell_cols; i++)
7391 {
7392 if ((i != col) || (j != row))
7393 {
7394 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
7395 attr_stub->SetSize( 1, 1 );
7396 attr_stub->DecRef();
7397 }
7398 }
7399 }
7400 }
7401
7402 // mark the cells that will be covered by this cell to
7403 // negative or zero values to point back at this cell
7404 if (((num_rows > 1) || (num_cols > 1)) && (num_rows >= 1) && (num_cols >= 1))
7405 {
7406 int i, j;
7407 for (j=row; j < row + num_rows; j++)
7408 {
7409 for (i=col; i < col + num_cols; i++)
7410 {
7411 if ((i != col) || (j != row))
7412 {
7413 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
7414 attr_stub->SetSize( row - j, col - i );
7415 attr_stub->DecRef();
7416 }
7417 }
7418 }
7419 }
7420 }
7421 }
7422
7423 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
7424 {
7425 if ( CanHaveAttributes() )
7426 {
7427 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7428 attr->SetRenderer(renderer);
7429 attr->DecRef();
7430 }
7431 }
7432
7433 void wxGrid::SetCellEditor(int row, int col, wxGridCellEditor* editor)
7434 {
7435 if ( CanHaveAttributes() )
7436 {
7437 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7438 attr->SetEditor(editor);
7439 attr->DecRef();
7440 }
7441 }
7442
7443 void wxGrid::SetReadOnly(int row, int col, bool isReadOnly)
7444 {
7445 if ( CanHaveAttributes() )
7446 {
7447 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7448 attr->SetReadOnly(isReadOnly);
7449 attr->DecRef();
7450 }
7451 }
7452
7453 // ----------------------------------------------------------------------------
7454 // Data type registration
7455 // ----------------------------------------------------------------------------
7456
7457 void wxGrid::RegisterDataType(const wxString& typeName,
7458 wxGridCellRenderer* renderer,
7459 wxGridCellEditor* editor)
7460 {
7461 m_typeRegistry->RegisterDataType(typeName, renderer, editor);
7462 }
7463
7464
7465 wxGridCellEditor * wxGrid::GetDefaultEditorForCell(int row, int col) const
7466 {
7467 wxString typeName = m_table->GetTypeName(row, col);
7468 return GetDefaultEditorForType(typeName);
7469 }
7470
7471 wxGridCellRenderer * wxGrid::GetDefaultRendererForCell(int row, int col) const
7472 {
7473 wxString typeName = m_table->GetTypeName(row, col);
7474 return GetDefaultRendererForType(typeName);
7475 }
7476
7477 wxGridCellEditor * wxGrid::GetDefaultEditorForType(const wxString& typeName) const
7478 {
7479 int index = m_typeRegistry->FindOrCloneDataType(typeName);
7480 if ( index == wxNOT_FOUND )
7481 {
7482 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
7483
7484 return NULL;
7485 }
7486
7487 return m_typeRegistry->GetEditor(index);
7488 }
7489
7490 wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) const
7491 {
7492 int index = m_typeRegistry->FindOrCloneDataType(typeName);
7493 if ( index == wxNOT_FOUND )
7494 {
7495 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
7496
7497 return NULL;
7498 }
7499
7500 return m_typeRegistry->GetRenderer(index);
7501 }
7502
7503 // ----------------------------------------------------------------------------
7504 // row/col size
7505 // ----------------------------------------------------------------------------
7506
7507 void wxGrid::DoDisableLineResize(int line, wxGridFixedIndicesSet *& setFixed)
7508 {
7509 if ( !setFixed )
7510 {
7511 setFixed = new wxGridFixedIndicesSet;
7512 }
7513
7514 setFixed->insert(line);
7515 }
7516
7517 bool
7518 wxGrid::DoCanResizeLine(int line, const wxGridFixedIndicesSet *setFixed) const
7519 {
7520 return !setFixed || !setFixed->count(line);
7521 }
7522
7523 void wxGrid::EnableDragRowSize( bool enable )
7524 {
7525 m_canDragRowSize = enable;
7526 }
7527
7528 void wxGrid::EnableDragColSize( bool enable )
7529 {
7530 m_canDragColSize = enable;
7531 }
7532
7533 void wxGrid::EnableDragGridSize( bool enable )
7534 {
7535 m_canDragGridSize = enable;
7536 }
7537
7538 void wxGrid::EnableDragCell( bool enable )
7539 {
7540 m_canDragCell = enable;
7541 }
7542
7543 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
7544 {
7545 m_defaultRowHeight = wxMax( height, m_minAcceptableRowHeight );
7546
7547 if ( resizeExistingRows )
7548 {
7549 // since we are resizing all rows to the default row size,
7550 // we can simply clear the row heights and row bottoms
7551 // arrays (which also allows us to take advantage of
7552 // some speed optimisations)
7553 m_rowHeights.Empty();
7554 m_rowBottoms.Empty();
7555 if ( !GetBatchCount() )
7556 CalcDimensions();
7557 }
7558 }
7559
7560 void wxGrid::SetRowSize( int row, int height )
7561 {
7562 wxCHECK_RET( row >= 0 && row < m_numRows, wxT("invalid row index") );
7563
7564 // if < 0 then calculate new height from label
7565 if ( height < 0 )
7566 {
7567 long w, h;
7568 wxArrayString lines;
7569 wxClientDC dc(m_rowLabelWin);
7570 dc.SetFont(GetLabelFont());
7571 StringToLines(GetRowLabelValue( row ), lines);
7572 GetTextBoxSize( dc, lines, &w, &h );
7573 //check that it is not less than the minimal height
7574 height = wxMax(h, GetRowMinimalAcceptableHeight());
7575 }
7576
7577 // See comment in SetColSize
7578 if ( height < GetRowMinimalAcceptableHeight())
7579 return;
7580
7581 if ( m_rowHeights.IsEmpty() )
7582 {
7583 // need to really create the array
7584 InitRowHeights();
7585 }
7586
7587 int h = wxMax( 0, height );
7588 int diff = h - m_rowHeights[row];
7589
7590 m_rowHeights[row] = h;
7591 for ( int i = row; i < m_numRows; i++ )
7592 {
7593 m_rowBottoms[i] += diff;
7594 }
7595
7596 if ( !GetBatchCount() )
7597 CalcDimensions();
7598 }
7599
7600 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
7601 {
7602 // we dont allow zero default column width
7603 m_defaultColWidth = wxMax( wxMax( width, m_minAcceptableColWidth ), 1 );
7604
7605 if ( resizeExistingCols )
7606 {
7607 // since we are resizing all columns to the default column size,
7608 // we can simply clear the col widths and col rights
7609 // arrays (which also allows us to take advantage of
7610 // some speed optimisations)
7611 m_colWidths.Empty();
7612 m_colRights.Empty();
7613 if ( !GetBatchCount() )
7614 CalcDimensions();
7615 }
7616 }
7617
7618 void wxGrid::SetColSize( int col, int width )
7619 {
7620 wxCHECK_RET( col >= 0 && col < m_numCols, wxT("invalid column index") );
7621
7622 // if < 0 then calculate new width from label
7623 if ( width < 0 )
7624 {
7625 long w, h;
7626 wxArrayString lines;
7627 wxClientDC dc(m_colWindow);
7628 dc.SetFont(GetLabelFont());
7629 StringToLines(GetColLabelValue(col), lines);
7630 if ( GetColLabelTextOrientation() == wxHORIZONTAL )
7631 GetTextBoxSize( dc, lines, &w, &h );
7632 else
7633 GetTextBoxSize( dc, lines, &h, &w );
7634 width = w + 6;
7635 //check that it is not less than the minimal width
7636 width = wxMax(width, GetColMinimalAcceptableWidth());
7637 }
7638
7639 // we intentionally don't test whether the width is less than
7640 // GetColMinimalWidth() here but we do compare it with
7641 // GetColMinimalAcceptableWidth() as otherwise things currently break (see
7642 // #651) -- and we also always allow the width of 0 as it has the special
7643 // sense of hiding the column
7644 if ( width > 0 && width < GetColMinimalAcceptableWidth() )
7645 return;
7646
7647 if ( m_colWidths.IsEmpty() )
7648 {
7649 // need to really create the array
7650 InitColWidths();
7651 }
7652
7653 const int diff = width - m_colWidths[col];
7654 m_colWidths[col] = width;
7655 if ( m_useNativeHeader )
7656 GetGridColHeader()->UpdateColumn(col);
7657 //else: will be refreshed when the header is redrawn
7658
7659 for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ )
7660 {
7661 m_colRights[GetColAt(colPos)] += diff;
7662 }
7663
7664 if ( !GetBatchCount() )
7665 {
7666 CalcDimensions();
7667 Refresh();
7668 }
7669 }
7670
7671 void wxGrid::SetColMinimalWidth( int col, int width )
7672 {
7673 if (width > GetColMinimalAcceptableWidth())
7674 {
7675 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
7676 m_colMinWidths[key] = width;
7677 }
7678 }
7679
7680 void wxGrid::SetRowMinimalHeight( int row, int width )
7681 {
7682 if (width > GetRowMinimalAcceptableHeight())
7683 {
7684 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
7685 m_rowMinHeights[key] = width;
7686 }
7687 }
7688
7689 int wxGrid::GetColMinimalWidth(int col) const
7690 {
7691 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
7692 wxLongToLongHashMap::const_iterator it = m_colMinWidths.find(key);
7693
7694 return it != m_colMinWidths.end() ? (int)it->second : m_minAcceptableColWidth;
7695 }
7696
7697 int wxGrid::GetRowMinimalHeight(int row) const
7698 {
7699 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
7700 wxLongToLongHashMap::const_iterator it = m_rowMinHeights.find(key);
7701
7702 return it != m_rowMinHeights.end() ? (int)it->second : m_minAcceptableRowHeight;
7703 }
7704
7705 void wxGrid::SetColMinimalAcceptableWidth( int width )
7706 {
7707 // We do allow a width of 0 since this gives us
7708 // an easy way to temporarily hiding columns.
7709 if ( width >= 0 )
7710 m_minAcceptableColWidth = width;
7711 }
7712
7713 void wxGrid::SetRowMinimalAcceptableHeight( int height )
7714 {
7715 // We do allow a height of 0 since this gives us
7716 // an easy way to temporarily hiding rows.
7717 if ( height >= 0 )
7718 m_minAcceptableRowHeight = height;
7719 }
7720
7721 int wxGrid::GetColMinimalAcceptableWidth() const
7722 {
7723 return m_minAcceptableColWidth;
7724 }
7725
7726 int wxGrid::GetRowMinimalAcceptableHeight() const
7727 {
7728 return m_minAcceptableRowHeight;
7729 }
7730
7731 // ----------------------------------------------------------------------------
7732 // auto sizing
7733 // ----------------------------------------------------------------------------
7734
7735 void
7736 wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction)
7737 {
7738 const bool column = direction == wxGRID_COLUMN;
7739
7740 wxClientDC dc(m_gridWin);
7741
7742 // cancel editing of cell
7743 HideCellEditControl();
7744 SaveEditControlValue();
7745
7746 // init both of them to avoid compiler warnings, even if we only need one
7747 int row = -1,
7748 col = -1;
7749 if ( column )
7750 col = colOrRow;
7751 else
7752 row = colOrRow;
7753
7754 wxCoord extent, extentMax = 0;
7755 int max = column ? m_numRows : m_numCols;
7756 for ( int rowOrCol = 0; rowOrCol < max; rowOrCol++ )
7757 {
7758 if ( column )
7759 row = rowOrCol;
7760 else
7761 col = rowOrCol;
7762
7763 wxGridCellAttr *attr = GetCellAttr(row, col);
7764 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
7765 if ( renderer )
7766 {
7767 wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
7768 extent = column ? size.x : size.y;
7769 if ( extent > extentMax )
7770 extentMax = extent;
7771
7772 renderer->DecRef();
7773 }
7774
7775 attr->DecRef();
7776 }
7777
7778 // now also compare with the column label extent
7779 wxCoord w, h;
7780 dc.SetFont( GetLabelFont() );
7781
7782 if ( column )
7783 {
7784 dc.GetMultiLineTextExtent( GetColLabelValue(col), &w, &h );
7785 if ( GetColLabelTextOrientation() == wxVERTICAL )
7786 w = h;
7787 }
7788 else
7789 dc.GetMultiLineTextExtent( GetRowLabelValue(row), &w, &h );
7790
7791 extent = column ? w : h;
7792 if ( extent > extentMax )
7793 extentMax = extent;
7794
7795 if ( !extentMax )
7796 {
7797 // empty column - give default extent (notice that if extentMax is less
7798 // than default extent but != 0, it's OK)
7799 extentMax = column ? m_defaultColWidth : m_defaultRowHeight;
7800 }
7801 else
7802 {
7803 if ( column )
7804 // leave some space around text
7805 extentMax += 10;
7806 else
7807 extentMax += 6;
7808 }
7809
7810 if ( column )
7811 {
7812 // Ensure automatic width is not less than minimal width. See the
7813 // comment in SetColSize() for explanation of why this isn't done
7814 // in SetColSize().
7815 if ( !setAsMin )
7816 extentMax = wxMax(extentMax, GetColMinimalWidth(col));
7817
7818 SetColSize( col, extentMax );
7819 if ( !GetBatchCount() )
7820 {
7821 if ( m_useNativeHeader )
7822 {
7823 GetGridColHeader()->UpdateColumn(col);
7824 }
7825 else
7826 {
7827 int cw, ch, dummy;
7828 m_gridWin->GetClientSize( &cw, &ch );
7829 wxRect rect ( CellToRect( 0, col ) );
7830 rect.y = 0;
7831 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
7832 rect.width = cw - rect.x;
7833 rect.height = m_colLabelHeight;
7834 GetColLabelWindow()->Refresh( true, &rect );
7835 }
7836 }
7837 }
7838 else
7839 {
7840 // Ensure automatic width is not less than minimal height. See the
7841 // comment in SetColSize() for explanation of why this isn't done
7842 // in SetRowSize().
7843 if ( !setAsMin )
7844 extentMax = wxMax(extentMax, GetRowMinimalHeight(row));
7845
7846 SetRowSize(row, extentMax);
7847 if ( !GetBatchCount() )
7848 {
7849 int cw, ch, dummy;
7850 m_gridWin->GetClientSize( &cw, &ch );
7851 wxRect rect( CellToRect( row, 0 ) );
7852 rect.x = 0;
7853 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
7854 rect.width = m_rowLabelWidth;
7855 rect.height = ch - rect.y;
7856 m_rowLabelWin->Refresh( true, &rect );
7857 }
7858 }
7859
7860 if ( setAsMin )
7861 {
7862 if ( column )
7863 SetColMinimalWidth(col, extentMax);
7864 else
7865 SetRowMinimalHeight(row, extentMax);
7866 }
7867 }
7868
7869 wxCoord wxGrid::CalcColOrRowLabelAreaMinSize(wxGridDirection direction)
7870 {
7871 // calculate size for the rows or columns?
7872 const bool calcRows = direction == wxGRID_ROW;
7873
7874 wxClientDC dc(calcRows ? GetGridRowLabelWindow()
7875 : GetGridColLabelWindow());
7876 dc.SetFont(GetLabelFont());
7877
7878 // which dimension should we take into account for calculations?
7879 //
7880 // for columns, the text can be only horizontal so it's easy but for rows
7881 // we also have to take into account the text orientation
7882 const bool
7883 useWidth = calcRows || (GetColLabelTextOrientation() == wxVERTICAL);
7884
7885 wxArrayString lines;
7886 wxCoord extentMax = 0;
7887
7888 const int numRowsOrCols = calcRows ? m_numRows : m_numCols;
7889 for ( int rowOrCol = 0; rowOrCol < numRowsOrCols; rowOrCol++ )
7890 {
7891 lines.Clear();
7892
7893 wxString label = calcRows ? GetRowLabelValue(rowOrCol)
7894 : GetColLabelValue(rowOrCol);
7895 StringToLines(label, lines);
7896
7897 long w, h;
7898 GetTextBoxSize(dc, lines, &w, &h);
7899
7900 const wxCoord extent = useWidth ? w : h;
7901 if ( extent > extentMax )
7902 extentMax = extent;
7903 }
7904
7905 if ( !extentMax )
7906 {
7907 // empty column - give default extent (notice that if extentMax is less
7908 // than default extent but != 0, it's OK)
7909 extentMax = calcRows ? GetDefaultRowLabelSize()
7910 : GetDefaultColLabelSize();
7911 }
7912
7913 // leave some space around text (taken from AutoSizeColOrRow)
7914 if ( calcRows )
7915 extentMax += 10;
7916 else
7917 extentMax += 6;
7918
7919 return extentMax;
7920 }
7921
7922 int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin)
7923 {
7924 int width = m_rowLabelWidth;
7925
7926 wxGridUpdateLocker locker;
7927 if(!calcOnly)
7928 locker.Create(this);
7929
7930 for ( int col = 0; col < m_numCols; col++ )
7931 {
7932 if ( !calcOnly )
7933 AutoSizeColumn(col, setAsMin);
7934
7935 width += GetColWidth(col);
7936 }
7937
7938 return width;
7939 }
7940
7941 int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin)
7942 {
7943 int height = m_colLabelHeight;
7944
7945 wxGridUpdateLocker locker;
7946 if(!calcOnly)
7947 locker.Create(this);
7948
7949 for ( int row = 0; row < m_numRows; row++ )
7950 {
7951 if ( !calcOnly )
7952 AutoSizeRow(row, setAsMin);
7953
7954 height += GetRowHeight(row);
7955 }
7956
7957 return height;
7958 }
7959
7960 void wxGrid::AutoSize()
7961 {
7962 wxGridUpdateLocker locker(this);
7963
7964 wxSize size(SetOrCalcColumnSizes(false) - m_rowLabelWidth + m_extraWidth,
7965 SetOrCalcRowSizes(false) - m_colLabelHeight + m_extraHeight);
7966
7967 // we know that we're not going to have scrollbars so disable them now to
7968 // avoid trouble in SetClientSize() which can otherwise set the correct
7969 // client size but also leave space for (not needed any more) scrollbars
7970 SetScrollbars(0, 0, 0, 0, 0, 0, true);
7971
7972 // restore the scroll rate parameters overwritten by SetScrollbars()
7973 SetScrollRate(m_scrollLineX, m_scrollLineY);
7974
7975 SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight);
7976 }
7977
7978 void wxGrid::AutoSizeRowLabelSize( int row )
7979 {
7980 // Hide the edit control, so it
7981 // won't interfere with drag-shrinking.
7982 if ( IsCellEditControlShown() )
7983 {
7984 HideCellEditControl();
7985 SaveEditControlValue();
7986 }
7987
7988 // autosize row height depending on label text
7989 SetRowSize(row, -1);
7990 ForceRefresh();
7991 }
7992
7993 void wxGrid::AutoSizeColLabelSize( int col )
7994 {
7995 // Hide the edit control, so it
7996 // won't interfere with drag-shrinking.
7997 if ( IsCellEditControlShown() )
7998 {
7999 HideCellEditControl();
8000 SaveEditControlValue();
8001 }
8002
8003 // autosize column width depending on label text
8004 SetColSize(col, -1);
8005 ForceRefresh();
8006 }
8007
8008 wxSize wxGrid::DoGetBestSize() const
8009 {
8010 wxGrid *self = (wxGrid *)this; // const_cast
8011
8012 // we do the same as in AutoSize() here with the exception that we don't
8013 // change the column/row sizes, only calculate them
8014 wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth,
8015 self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight);
8016
8017 // NOTE: This size should be cached, but first we need to add calls to
8018 // InvalidateBestSize everywhere that could change the results of this
8019 // calculation.
8020 // CacheBestSize(size);
8021
8022 return wxSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight)
8023 + GetWindowBorderSize();
8024 }
8025
8026 void wxGrid::Fit()
8027 {
8028 AutoSize();
8029 }
8030
8031 wxPen& wxGrid::GetDividerPen() const
8032 {
8033 return wxNullPen;
8034 }
8035
8036 // ----------------------------------------------------------------------------
8037 // cell value accessor functions
8038 // ----------------------------------------------------------------------------
8039
8040 void wxGrid::SetCellValue( int row, int col, const wxString& s )
8041 {
8042 if ( m_table )
8043 {
8044 m_table->SetValue( row, col, s );
8045 if ( !GetBatchCount() )
8046 {
8047 int dummy;
8048 wxRect rect( CellToRect( row, col ) );
8049 rect.x = 0;
8050 rect.width = m_gridWin->GetClientSize().GetWidth();
8051 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
8052 m_gridWin->Refresh( false, &rect );
8053 }
8054
8055 if ( m_currentCellCoords.GetRow() == row &&
8056 m_currentCellCoords.GetCol() == col &&
8057 IsCellEditControlShown())
8058 // Note: If we are using IsCellEditControlEnabled,
8059 // this interacts badly with calling SetCellValue from
8060 // an EVT_GRID_CELL_CHANGE handler.
8061 {
8062 HideCellEditControl();
8063 ShowCellEditControl(); // will reread data from table
8064 }
8065 }
8066 }
8067
8068 // ----------------------------------------------------------------------------
8069 // block, row and column selection
8070 // ----------------------------------------------------------------------------
8071
8072 void wxGrid::SelectRow( int row, bool addToSelected )
8073 {
8074 if ( !m_selection )
8075 return;
8076
8077 if ( !addToSelected )
8078 ClearSelection();
8079
8080 m_selection->SelectRow(row);
8081 }
8082
8083 void wxGrid::SelectCol( int col, bool addToSelected )
8084 {
8085 if ( !m_selection )
8086 return;
8087
8088 if ( !addToSelected )
8089 ClearSelection();
8090
8091 m_selection->SelectCol(col);
8092 }
8093
8094 void wxGrid::SelectBlock(int topRow, int leftCol, int bottomRow, int rightCol,
8095 bool addToSelected)
8096 {
8097 if ( !m_selection )
8098 return;
8099
8100 if ( !addToSelected )
8101 ClearSelection();
8102
8103 m_selection->SelectBlock(topRow, leftCol, bottomRow, rightCol);
8104 }
8105
8106 void wxGrid::SelectAll()
8107 {
8108 if ( m_numRows > 0 && m_numCols > 0 )
8109 {
8110 if ( m_selection )
8111 m_selection->SelectBlock( 0, 0, m_numRows - 1, m_numCols - 1 );
8112 }
8113 }
8114
8115 // ----------------------------------------------------------------------------
8116 // cell, row and col deselection
8117 // ----------------------------------------------------------------------------
8118
8119 void wxGrid::DeselectLine(int line, const wxGridOperations& oper)
8120 {
8121 if ( !m_selection )
8122 return;
8123
8124 const wxGridSelectionModes mode = m_selection->GetSelectionMode();
8125 if ( mode == oper.GetSelectionMode() ||
8126 mode == wxGrid::wxGridSelectRowsOrColumns )
8127 {
8128 const wxGridCellCoords c(oper.MakeCoords(line, 0));
8129 if ( m_selection->IsInSelection(c) )
8130 m_selection->ToggleCellSelection(c);
8131 }
8132 else if ( mode != oper.Dual().GetSelectionMode() )
8133 {
8134 const int nOther = oper.Dual().GetNumberOfLines(this);
8135 for ( int i = 0; i < nOther; i++ )
8136 {
8137 const wxGridCellCoords c(oper.MakeCoords(line, i));
8138 if ( m_selection->IsInSelection(c) )
8139 m_selection->ToggleCellSelection(c);
8140 }
8141 }
8142 //else: can only select orthogonal lines so no lines in this direction
8143 // could have been selected anyhow
8144 }
8145
8146 void wxGrid::DeselectRow(int row)
8147 {
8148 DeselectLine(row, wxGridRowOperations());
8149 }
8150
8151 void wxGrid::DeselectCol(int col)
8152 {
8153 DeselectLine(col, wxGridColumnOperations());
8154 }
8155
8156 void wxGrid::DeselectCell( int row, int col )
8157 {
8158 if ( m_selection && m_selection->IsInSelection(row, col) )
8159 m_selection->ToggleCellSelection(row, col);
8160 }
8161
8162 bool wxGrid::IsSelection() const
8163 {
8164 return ( m_selection && (m_selection->IsSelection() ||
8165 ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
8166 m_selectedBlockBottomRight != wxGridNoCellCoords) ) );
8167 }
8168
8169 bool wxGrid::IsInSelection( int row, int col ) const
8170 {
8171 return ( m_selection && (m_selection->IsInSelection( row, col ) ||
8172 ( row >= m_selectedBlockTopLeft.GetRow() &&
8173 col >= m_selectedBlockTopLeft.GetCol() &&
8174 row <= m_selectedBlockBottomRight.GetRow() &&
8175 col <= m_selectedBlockBottomRight.GetCol() )) );
8176 }
8177
8178 wxGridCellCoordsArray wxGrid::GetSelectedCells() const
8179 {
8180 if (!m_selection)
8181 {
8182 wxGridCellCoordsArray a;
8183 return a;
8184 }
8185
8186 return m_selection->m_cellSelection;
8187 }
8188
8189 wxGridCellCoordsArray wxGrid::GetSelectionBlockTopLeft() const
8190 {
8191 if (!m_selection)
8192 {
8193 wxGridCellCoordsArray a;
8194 return a;
8195 }
8196
8197 return m_selection->m_blockSelectionTopLeft;
8198 }
8199
8200 wxGridCellCoordsArray wxGrid::GetSelectionBlockBottomRight() const
8201 {
8202 if (!m_selection)
8203 {
8204 wxGridCellCoordsArray a;
8205 return a;
8206 }
8207
8208 return m_selection->m_blockSelectionBottomRight;
8209 }
8210
8211 wxArrayInt wxGrid::GetSelectedRows() const
8212 {
8213 if (!m_selection)
8214 {
8215 wxArrayInt a;
8216 return a;
8217 }
8218
8219 return m_selection->m_rowSelection;
8220 }
8221
8222 wxArrayInt wxGrid::GetSelectedCols() const
8223 {
8224 if (!m_selection)
8225 {
8226 wxArrayInt a;
8227 return a;
8228 }
8229
8230 return m_selection->m_colSelection;
8231 }
8232
8233 void wxGrid::ClearSelection()
8234 {
8235 wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft,
8236 m_selectedBlockBottomRight);
8237 wxRect r2 = BlockToDeviceRect(m_currentCellCoords,
8238 m_selectedBlockCorner);
8239
8240 m_selectedBlockTopLeft =
8241 m_selectedBlockBottomRight =
8242 m_selectedBlockCorner = wxGridNoCellCoords;
8243
8244 if ( !r1.IsEmpty() )
8245 RefreshRect(r1, false);
8246 if ( !r2.IsEmpty() )
8247 RefreshRect(r2, false);
8248
8249 if ( m_selection )
8250 m_selection->ClearSelection();
8251 }
8252
8253 // This function returns the rectangle that encloses the given block
8254 // in device coords clipped to the client size of the grid window.
8255 //
8256 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft,
8257 const wxGridCellCoords& bottomRight ) const
8258 {
8259 wxRect resultRect;
8260 wxRect tempCellRect = CellToRect(topLeft);
8261 if ( tempCellRect != wxGridNoCellRect )
8262 {
8263 resultRect = tempCellRect;
8264 }
8265 else
8266 {
8267 resultRect = wxRect(0, 0, 0, 0);
8268 }
8269
8270 tempCellRect = CellToRect(bottomRight);
8271 if ( tempCellRect != wxGridNoCellRect )
8272 {
8273 resultRect += tempCellRect;
8274 }
8275 else
8276 {
8277 // If both inputs were "wxGridNoCellRect," then there's nothing to do.
8278 return wxGridNoCellRect;
8279 }
8280
8281 // Ensure that left/right and top/bottom pairs are in order.
8282 int left = resultRect.GetLeft();
8283 int top = resultRect.GetTop();
8284 int right = resultRect.GetRight();
8285 int bottom = resultRect.GetBottom();
8286
8287 int leftCol = topLeft.GetCol();
8288 int topRow = topLeft.GetRow();
8289 int rightCol = bottomRight.GetCol();
8290 int bottomRow = bottomRight.GetRow();
8291
8292 if (left > right)
8293 {
8294 int tmp = left;
8295 left = right;
8296 right = tmp;
8297
8298 tmp = leftCol;
8299 leftCol = rightCol;
8300 rightCol = tmp;
8301 }
8302
8303 if (top > bottom)
8304 {
8305 int tmp = top;
8306 top = bottom;
8307 bottom = tmp;
8308
8309 tmp = topRow;
8310 topRow = bottomRow;
8311 bottomRow = tmp;
8312 }
8313
8314 // The following loop is ONLY necessary to detect and handle merged cells.
8315 int cw, ch;
8316 m_gridWin->GetClientSize( &cw, &ch );
8317
8318 // Get the origin coordinates: notice that they will be negative if the
8319 // grid is scrolled downwards/to the right.
8320 int gridOriginX = 0;
8321 int gridOriginY = 0;
8322 CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY);
8323
8324 int onScreenLeftmostCol = internalXToCol(-gridOriginX);
8325 int onScreenUppermostRow = internalYToRow(-gridOriginY);
8326
8327 int onScreenRightmostCol = internalXToCol(-gridOriginX + cw);
8328 int onScreenBottommostRow = internalYToRow(-gridOriginY + ch);
8329
8330 // Bound our loop so that we only examine the portion of the selected block
8331 // that is shown on screen. Therefore, we compare the Top-Left block values
8332 // to the Top-Left screen values, and the Bottom-Right block values to the
8333 // Bottom-Right screen values, choosing appropriately.
8334 const int visibleTopRow = wxMax(topRow, onScreenUppermostRow);
8335 const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow);
8336 const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol);
8337 const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol);
8338
8339 for ( int j = visibleTopRow; j <= visibleBottomRow; j++ )
8340 {
8341 for ( int i = visibleLeftCol; i <= visibleRightCol; i++ )
8342 {
8343 if ( (j == visibleTopRow) || (j == visibleBottomRow) ||
8344 (i == visibleLeftCol) || (i == visibleRightCol) )
8345 {
8346 tempCellRect = CellToRect( j, i );
8347
8348 if (tempCellRect.x < left)
8349 left = tempCellRect.x;
8350 if (tempCellRect.y < top)
8351 top = tempCellRect.y;
8352 if (tempCellRect.x + tempCellRect.width > right)
8353 right = tempCellRect.x + tempCellRect.width;
8354 if (tempCellRect.y + tempCellRect.height > bottom)
8355 bottom = tempCellRect.y + tempCellRect.height;
8356 }
8357 else
8358 {
8359 i = visibleRightCol; // jump over inner cells.
8360 }
8361 }
8362 }
8363
8364 // Convert to scrolled coords
8365 CalcScrolledPosition( left, top, &left, &top );
8366 CalcScrolledPosition( right, bottom, &right, &bottom );
8367
8368 if (right < 0 || bottom < 0 || left > cw || top > ch)
8369 return wxRect(0,0,0,0);
8370
8371 resultRect.SetLeft( wxMax(0, left) );
8372 resultRect.SetTop( wxMax(0, top) );
8373 resultRect.SetRight( wxMin(cw, right) );
8374 resultRect.SetBottom( wxMin(ch, bottom) );
8375
8376 return resultRect;
8377 }
8378
8379 void wxGrid::DoSetSizes(const wxGridSizesInfo& sizeInfo,
8380 const wxGridOperations& oper)
8381 {
8382 BeginBatch();
8383 oper.SetDefaultLineSize(this, sizeInfo.m_sizeDefault, true);
8384 const int numLines = oper.GetNumberOfLines(this);
8385 for ( int i = 0; i < numLines; i++ )
8386 {
8387 int size = sizeInfo.GetSize(i);
8388 if ( size != sizeInfo.m_sizeDefault)
8389 oper.SetLineSize(this, i, size);
8390 }
8391 EndBatch();
8392 }
8393
8394 void wxGrid::SetColSizes(const wxGridSizesInfo& sizeInfo)
8395 {
8396 DoSetSizes(sizeInfo, wxGridColumnOperations());
8397 }
8398
8399 void wxGrid::SetRowSizes(const wxGridSizesInfo& sizeInfo)
8400 {
8401 DoSetSizes(sizeInfo, wxGridRowOperations());
8402 }
8403
8404 wxGridSizesInfo::wxGridSizesInfo(int defSize, const wxArrayInt& allSizes)
8405 {
8406 m_sizeDefault = defSize;
8407 for ( size_t i = 0; i < allSizes.size(); i++ )
8408 {
8409 if ( allSizes[i] != defSize )
8410 m_customSizes[i] = allSizes[i];
8411 }
8412 }
8413
8414 int wxGridSizesInfo::GetSize(unsigned pos) const
8415 {
8416 wxUnsignedToIntHashMap::const_iterator it = m_customSizes.find(pos);
8417
8418 return it == m_customSizes.end() ? m_sizeDefault : it->second;
8419 }
8420
8421 // ----------------------------------------------------------------------------
8422 // drop target
8423 // ----------------------------------------------------------------------------
8424
8425 #if wxUSE_DRAG_AND_DROP
8426
8427 // this allow setting drop target directly on wxGrid
8428 void wxGrid::SetDropTarget(wxDropTarget *dropTarget)
8429 {
8430 GetGridWindow()->SetDropTarget(dropTarget);
8431 }
8432
8433 #endif // wxUSE_DRAG_AND_DROP
8434
8435 // ----------------------------------------------------------------------------
8436 // grid event classes
8437 // ----------------------------------------------------------------------------
8438
8439 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent )
8440
8441 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
8442 int row, int col, int x, int y, bool sel,
8443 bool control, bool shift, bool alt, bool meta )
8444 : wxNotifyEvent( type, id ),
8445 wxKeyboardState(control, shift, alt, meta)
8446 {
8447 Init(row, col, x, y, sel);
8448
8449 SetEventObject(obj);
8450 }
8451
8452 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent )
8453
8454 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
8455 int rowOrCol, int x, int y,
8456 bool control, bool shift, bool alt, bool meta )
8457 : wxNotifyEvent( type, id ),
8458 wxKeyboardState(control, shift, alt, meta)
8459 {
8460 Init(rowOrCol, x, y);
8461
8462 SetEventObject(obj);
8463 }
8464
8465
8466 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxNotifyEvent )
8467
8468 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
8469 const wxGridCellCoords& topLeft,
8470 const wxGridCellCoords& bottomRight,
8471 bool sel, bool control,
8472 bool shift, bool alt, bool meta )
8473 : wxNotifyEvent( type, id ),
8474 wxKeyboardState(control, shift, alt, meta)
8475 {
8476 Init(topLeft, bottomRight, sel);
8477
8478 SetEventObject(obj);
8479 }
8480
8481
8482 IMPLEMENT_DYNAMIC_CLASS(wxGridEditorCreatedEvent, wxCommandEvent)
8483
8484 wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type,
8485 wxObject* obj, int row,
8486 int col, wxControl* ctrl)
8487 : wxCommandEvent(type, id)
8488 {
8489 SetEventObject(obj);
8490 m_row = row;
8491 m_col = col;
8492 m_ctrl = ctrl;
8493 }
8494
8495
8496 // ----------------------------------------------------------------------------
8497 // wxGridTypeRegistry
8498 // ----------------------------------------------------------------------------
8499
8500 wxGridTypeRegistry::~wxGridTypeRegistry()
8501 {
8502 size_t count = m_typeinfo.GetCount();
8503 for ( size_t i = 0; i < count; i++ )
8504 delete m_typeinfo[i];
8505 }
8506
8507 void wxGridTypeRegistry::RegisterDataType(const wxString& typeName,
8508 wxGridCellRenderer* renderer,
8509 wxGridCellEditor* editor)
8510 {
8511 wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor);
8512
8513 // is it already registered?
8514 int loc = FindRegisteredDataType(typeName);
8515 if ( loc != wxNOT_FOUND )
8516 {
8517 delete m_typeinfo[loc];
8518 m_typeinfo[loc] = info;
8519 }
8520 else
8521 {
8522 m_typeinfo.Add(info);
8523 }
8524 }
8525
8526 int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName)
8527 {
8528 size_t count = m_typeinfo.GetCount();
8529 for ( size_t i = 0; i < count; i++ )
8530 {
8531 if ( typeName == m_typeinfo[i]->m_typeName )
8532 {
8533 return i;
8534 }
8535 }
8536
8537 return wxNOT_FOUND;
8538 }
8539
8540 int wxGridTypeRegistry::FindDataType(const wxString& typeName)
8541 {
8542 int index = FindRegisteredDataType(typeName);
8543 if ( index == wxNOT_FOUND )
8544 {
8545 // check whether this is one of the standard ones, in which case
8546 // register it "on the fly"
8547 #if wxUSE_TEXTCTRL
8548 if ( typeName == wxGRID_VALUE_STRING )
8549 {
8550 RegisterDataType(wxGRID_VALUE_STRING,
8551 new wxGridCellStringRenderer,
8552 new wxGridCellTextEditor);
8553 }
8554 else
8555 #endif // wxUSE_TEXTCTRL
8556 #if wxUSE_CHECKBOX
8557 if ( typeName == wxGRID_VALUE_BOOL )
8558 {
8559 RegisterDataType(wxGRID_VALUE_BOOL,
8560 new wxGridCellBoolRenderer,
8561 new wxGridCellBoolEditor);
8562 }
8563 else
8564 #endif // wxUSE_CHECKBOX
8565 #if wxUSE_TEXTCTRL
8566 if ( typeName == wxGRID_VALUE_NUMBER )
8567 {
8568 RegisterDataType(wxGRID_VALUE_NUMBER,
8569 new wxGridCellNumberRenderer,
8570 new wxGridCellNumberEditor);
8571 }
8572 else if ( typeName == wxGRID_VALUE_FLOAT )
8573 {
8574 RegisterDataType(wxGRID_VALUE_FLOAT,
8575 new wxGridCellFloatRenderer,
8576 new wxGridCellFloatEditor);
8577 }
8578 else
8579 #endif // wxUSE_TEXTCTRL
8580 #if wxUSE_COMBOBOX
8581 if ( typeName == wxGRID_VALUE_CHOICE )
8582 {
8583 RegisterDataType(wxGRID_VALUE_CHOICE,
8584 new wxGridCellStringRenderer,
8585 new wxGridCellChoiceEditor);
8586 }
8587 else
8588 #endif // wxUSE_COMBOBOX
8589 {
8590 return wxNOT_FOUND;
8591 }
8592
8593 // we get here only if just added the entry for this type, so return
8594 // the last index
8595 index = m_typeinfo.GetCount() - 1;
8596 }
8597
8598 return index;
8599 }
8600
8601 int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName)
8602 {
8603 int index = FindDataType(typeName);
8604 if ( index == wxNOT_FOUND )
8605 {
8606 // the first part of the typename is the "real" type, anything after ':'
8607 // are the parameters for the renderer
8608 index = FindDataType(typeName.BeforeFirst(wxT(':')));
8609 if ( index == wxNOT_FOUND )
8610 {
8611 return wxNOT_FOUND;
8612 }
8613
8614 wxGridCellRenderer *renderer = GetRenderer(index);
8615 wxGridCellRenderer *rendererOld = renderer;
8616 renderer = renderer->Clone();
8617 rendererOld->DecRef();
8618
8619 wxGridCellEditor *editor = GetEditor(index);
8620 wxGridCellEditor *editorOld = editor;
8621 editor = editor->Clone();
8622 editorOld->DecRef();
8623
8624 // do it even if there are no parameters to reset them to defaults
8625 wxString params = typeName.AfterFirst(wxT(':'));
8626 renderer->SetParameters(params);
8627 editor->SetParameters(params);
8628
8629 // register the new typename
8630 RegisterDataType(typeName, renderer, editor);
8631
8632 // we just registered it, it's the last one
8633 index = m_typeinfo.GetCount() - 1;
8634 }
8635
8636 return index;
8637 }
8638
8639 wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index)
8640 {
8641 wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer;
8642 if (renderer)
8643 renderer->IncRef();
8644
8645 return renderer;
8646 }
8647
8648 wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index)
8649 {
8650 wxGridCellEditor* editor = m_typeinfo[index]->m_editor;
8651 if (editor)
8652 editor->IncRef();
8653
8654 return editor;
8655 }
8656
8657 #endif // wxUSE_GRID