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