Don't show hidden wxGrid rows/columns when they're auto-sized.
[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 int i;
2881 for ( i = 0; i < m_numCols - numCols; i++ )
2882 {
2883 if ( m_colAt[i] >= (int)pos )
2884 m_colAt[i] += numCols;
2885 }
2886
2887 m_colAt.Insert( pos, pos, numCols );
2888
2889 //Set the new columns' positions
2890 for ( i = pos + 1; i < (int)pos + numCols; i++ )
2891 {
2892 m_colAt[i] = i;
2893 }
2894 }
2895
2896 if ( !m_colWidths.IsEmpty() )
2897 {
2898 m_colWidths.Insert( m_defaultColWidth, pos, numCols );
2899 m_colRights.Insert( 0, pos, numCols );
2900
2901 int right = 0;
2902 if ( pos > 0 )
2903 right = m_colRights[GetColAt( pos - 1 )];
2904
2905 int colPos;
2906 for ( colPos = pos; colPos < m_numCols; colPos++ )
2907 {
2908 i = GetColAt( colPos );
2909
2910 right += m_colWidths[i];
2911 m_colRights[i] = right;
2912 }
2913 }
2914
2915 if ( m_currentCellCoords == wxGridNoCellCoords )
2916 {
2917 // if we have just inserted cols into an empty grid the current
2918 // cell will be undefined...
2919 //
2920 SetCurrentCell( 0, 0 );
2921 }
2922
2923 if ( m_selection )
2924 m_selection->UpdateCols( pos, numCols );
2925 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
2926 if (attrProvider)
2927 attrProvider->UpdateAttrCols( pos, numCols );
2928 if ( !GetBatchCount() )
2929 {
2930 CalcDimensions();
2931 m_colWindow->Refresh();
2932 }
2933 }
2934 result = true;
2935 break;
2936
2937 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
2938 {
2939 int numCols = msg.GetCommandInt();
2940 int oldNumCols = m_numCols;
2941 m_numCols += numCols;
2942 if ( m_useNativeHeader )
2943 GetGridColHeader()->SetColumnCount(m_numCols);
2944
2945 if ( !m_colAt.IsEmpty() )
2946 {
2947 m_colAt.Add( 0, numCols );
2948
2949 //Set the new columns' positions
2950 int i;
2951 for ( i = oldNumCols; i < m_numCols; i++ )
2952 {
2953 m_colAt[i] = i;
2954 }
2955 }
2956
2957 if ( !m_colWidths.IsEmpty() )
2958 {
2959 m_colWidths.Add( m_defaultColWidth, numCols );
2960 m_colRights.Add( 0, numCols );
2961
2962 int right = 0;
2963 if ( oldNumCols > 0 )
2964 right = m_colRights[GetColAt( oldNumCols - 1 )];
2965
2966 int colPos;
2967 for ( colPos = oldNumCols; colPos < m_numCols; colPos++ )
2968 {
2969 i = GetColAt( colPos );
2970
2971 right += m_colWidths[i];
2972 m_colRights[i] = right;
2973 }
2974 }
2975
2976 if ( m_currentCellCoords == wxGridNoCellCoords )
2977 {
2978 // if we have just inserted cols into an empty grid the current
2979 // cell will be undefined...
2980 //
2981 SetCurrentCell( 0, 0 );
2982 }
2983 if ( !GetBatchCount() )
2984 {
2985 CalcDimensions();
2986 m_colWindow->Refresh();
2987 }
2988 }
2989 result = true;
2990 break;
2991
2992 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
2993 {
2994 size_t pos = msg.GetCommandInt();
2995 int numCols = msg.GetCommandInt2();
2996 m_numCols -= numCols;
2997 if ( m_useNativeHeader )
2998 GetGridColHeader()->SetColumnCount(m_numCols);
2999
3000 if ( !m_colAt.IsEmpty() )
3001 {
3002 int colID = GetColAt( pos );
3003
3004 m_colAt.RemoveAt( pos, numCols );
3005
3006 //Shift the column IDs
3007 int colPos;
3008 for ( colPos = 0; colPos < m_numCols; colPos++ )
3009 {
3010 if ( m_colAt[colPos] > colID )
3011 m_colAt[colPos] -= numCols;
3012 }
3013 }
3014
3015 if ( !m_colWidths.IsEmpty() )
3016 {
3017 m_colWidths.RemoveAt( pos, numCols );
3018 m_colRights.RemoveAt( pos, numCols );
3019
3020 int w = 0;
3021 int colPos;
3022 for ( colPos = 0; colPos < m_numCols; colPos++ )
3023 {
3024 i = GetColAt( colPos );
3025
3026 w += m_colWidths[i];
3027 m_colRights[i] = w;
3028 }
3029 }
3030
3031 if ( !m_numCols )
3032 {
3033 m_currentCellCoords = wxGridNoCellCoords;
3034 }
3035 else
3036 {
3037 if ( m_currentCellCoords.GetCol() >= m_numCols )
3038 m_currentCellCoords.Set( 0, 0 );
3039 }
3040
3041 if ( m_selection )
3042 m_selection->UpdateCols( pos, -((int)numCols) );
3043 wxGridCellAttrProvider * attrProvider = m_table->GetAttrProvider();
3044 if (attrProvider)
3045 {
3046 attrProvider->UpdateAttrCols( pos, -((int)numCols) );
3047
3048 // ifdef'd out following patch from Paul Gammans
3049 #if 0
3050 // No need to touch row attributes, unless we
3051 // removed _all_ columns, in this case, we remove
3052 // all row attributes.
3053 // I hate to do this here, but the
3054 // needed data is not available inside UpdateAttrCols.
3055 if ( !GetNumberCols() )
3056 attrProvider->UpdateAttrRows( 0, -GetNumberRows() );
3057 #endif
3058 }
3059
3060 if ( !GetBatchCount() )
3061 {
3062 CalcDimensions();
3063 m_colWindow->Refresh();
3064 }
3065 }
3066 result = true;
3067 break;
3068 }
3069
3070 if (result && !GetBatchCount() )
3071 m_gridWin->Refresh();
3072
3073 return result;
3074 }
3075
3076 wxArrayInt wxGrid::CalcRowLabelsExposed( const wxRegion& reg ) const
3077 {
3078 wxRegionIterator iter( reg );
3079 wxRect r;
3080
3081 wxArrayInt rowlabels;
3082
3083 int top, bottom;
3084 while ( iter )
3085 {
3086 r = iter.GetRect();
3087
3088 // TODO: remove this when we can...
3089 // There is a bug in wxMotif that gives garbage update
3090 // rectangles if you jump-scroll a long way by clicking the
3091 // scrollbar with middle button. This is a work-around
3092 //
3093 #if defined(__WXMOTIF__)
3094 int cw, ch;
3095 m_gridWin->GetClientSize( &cw, &ch );
3096 if ( r.GetTop() > ch )
3097 r.SetTop( 0 );
3098 r.SetBottom( wxMin( r.GetBottom(), ch ) );
3099 #endif
3100
3101 // logical bounds of update region
3102 //
3103 int dummy;
3104 CalcUnscrolledPosition( 0, r.GetTop(), &dummy, &top );
3105 CalcUnscrolledPosition( 0, r.GetBottom(), &dummy, &bottom );
3106
3107 // find the row labels within these bounds
3108 //
3109 int row;
3110 for ( row = internalYToRow(top); row < m_numRows; row++ )
3111 {
3112 if ( GetRowBottom(row) < top )
3113 continue;
3114
3115 if ( GetRowTop(row) > bottom )
3116 break;
3117
3118 rowlabels.Add( row );
3119 }
3120
3121 ++iter;
3122 }
3123
3124 return rowlabels;
3125 }
3126
3127 wxArrayInt wxGrid::CalcColLabelsExposed( const wxRegion& reg ) const
3128 {
3129 wxRegionIterator iter( reg );
3130 wxRect r;
3131
3132 wxArrayInt colLabels;
3133
3134 int left, right;
3135 while ( iter )
3136 {
3137 r = iter.GetRect();
3138
3139 // TODO: remove this when we can...
3140 // There is a bug in wxMotif that gives garbage update
3141 // rectangles if you jump-scroll a long way by clicking the
3142 // scrollbar with middle button. This is a work-around
3143 //
3144 #if defined(__WXMOTIF__)
3145 int cw, ch;
3146 m_gridWin->GetClientSize( &cw, &ch );
3147 if ( r.GetLeft() > cw )
3148 r.SetLeft( 0 );
3149 r.SetRight( wxMin( r.GetRight(), cw ) );
3150 #endif
3151
3152 // logical bounds of update region
3153 //
3154 int dummy;
3155 CalcUnscrolledPosition( r.GetLeft(), 0, &left, &dummy );
3156 CalcUnscrolledPosition( r.GetRight(), 0, &right, &dummy );
3157
3158 // find the cells within these bounds
3159 //
3160 int col;
3161 int colPos;
3162 for ( colPos = GetColPos( internalXToCol(left) ); colPos < m_numCols; colPos++ )
3163 {
3164 col = GetColAt( colPos );
3165
3166 if ( GetColRight(col) < left )
3167 continue;
3168
3169 if ( GetColLeft(col) > right )
3170 break;
3171
3172 colLabels.Add( col );
3173 }
3174
3175 ++iter;
3176 }
3177
3178 return colLabels;
3179 }
3180
3181 wxGridCellCoordsArray wxGrid::CalcCellsExposed( const wxRegion& reg ) const
3182 {
3183 wxRect r;
3184
3185 wxGridCellCoordsArray cellsExposed;
3186
3187 int left, top, right, bottom;
3188 for ( wxRegionIterator iter(reg); iter; ++iter )
3189 {
3190 r = iter.GetRect();
3191
3192 // Skip 0-height cells, they're invisible anyhow, don't waste time
3193 // getting their rectangles and so on.
3194 if ( !r.GetHeight() )
3195 continue;
3196
3197 // TODO: remove this when we can...
3198 // There is a bug in wxMotif that gives garbage update
3199 // rectangles if you jump-scroll a long way by clicking the
3200 // scrollbar with middle button. This is a work-around
3201 //
3202 #if defined(__WXMOTIF__)
3203 int cw, ch;
3204 m_gridWin->GetClientSize( &cw, &ch );
3205 if ( r.GetTop() > ch ) r.SetTop( 0 );
3206 if ( r.GetLeft() > cw ) r.SetLeft( 0 );
3207 r.SetRight( wxMin( r.GetRight(), cw ) );
3208 r.SetBottom( wxMin( r.GetBottom(), ch ) );
3209 #endif
3210
3211 // logical bounds of update region
3212 //
3213 CalcUnscrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
3214 CalcUnscrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
3215
3216 // find the cells within these bounds
3217 wxArrayInt cols;
3218 for ( int row = internalYToRow(top); row < m_numRows; row++ )
3219 {
3220 if ( GetRowBottom(row) <= top )
3221 continue;
3222
3223 if ( GetRowTop(row) > bottom )
3224 break;
3225
3226 // add all dirty cells in this row: notice that the columns which
3227 // are dirty don't depend on the row so we compute them only once
3228 // for the first dirty row and then reuse for all the next ones
3229 if ( cols.empty() )
3230 {
3231 // do determine the dirty columns
3232 for ( int pos = XToPos(left); pos <= XToPos(right); pos++ )
3233 cols.push_back(GetColAt(pos));
3234
3235 // if there are no dirty columns at all, nothing to do
3236 if ( cols.empty() )
3237 break;
3238 }
3239
3240 const size_t count = cols.size();
3241 for ( size_t n = 0; n < count; n++ )
3242 cellsExposed.Add(wxGridCellCoords(row, cols[n]));
3243 }
3244 }
3245
3246 return cellsExposed;
3247 }
3248
3249
3250 void wxGrid::ProcessRowLabelMouseEvent( wxMouseEvent& event )
3251 {
3252 int x, y, row;
3253 wxPoint pos( event.GetPosition() );
3254 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
3255
3256 if ( event.Dragging() )
3257 {
3258 if (!m_isDragging)
3259 {
3260 m_isDragging = true;
3261 m_rowLabelWin->CaptureMouse();
3262 }
3263
3264 if ( event.LeftIsDown() )
3265 {
3266 switch ( m_cursorMode )
3267 {
3268 case WXGRID_CURSOR_RESIZE_ROW:
3269 {
3270 int cw, ch, left, dummy;
3271 m_gridWin->GetClientSize( &cw, &ch );
3272 CalcUnscrolledPosition( 0, 0, &left, &dummy );
3273
3274 wxClientDC dc( m_gridWin );
3275 PrepareDC( dc );
3276 y = wxMax( y,
3277 GetRowTop(m_dragRowOrCol) +
3278 GetRowMinimalHeight(m_dragRowOrCol) );
3279 dc.SetLogicalFunction(wxINVERT);
3280 if ( m_dragLastPos >= 0 )
3281 {
3282 dc.DrawLine( left, m_dragLastPos, left+cw, m_dragLastPos );
3283 }
3284 dc.DrawLine( left, y, left+cw, y );
3285 m_dragLastPos = y;
3286 }
3287 break;
3288
3289 case WXGRID_CURSOR_SELECT_ROW:
3290 {
3291 if ( (row = YToRow( y )) >= 0 )
3292 {
3293 if ( m_selection )
3294 m_selection->SelectRow(row, event);
3295 }
3296 }
3297 break;
3298
3299 // default label to suppress warnings about "enumeration value
3300 // 'xxx' not handled in switch
3301 default:
3302 break;
3303 }
3304 }
3305 return;
3306 }
3307
3308 if ( m_isDragging && (event.Entering() || event.Leaving()) )
3309 return;
3310
3311 if (m_isDragging)
3312 {
3313 if (m_rowLabelWin->HasCapture())
3314 m_rowLabelWin->ReleaseMouse();
3315 m_isDragging = false;
3316 }
3317
3318 // ------------ Entering or leaving the window
3319 //
3320 if ( event.Entering() || event.Leaving() )
3321 {
3322 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
3323 }
3324
3325 // ------------ Left button pressed
3326 //
3327 else if ( event.LeftDown() )
3328 {
3329 row = YToEdgeOfRow(y);
3330 if ( row != wxNOT_FOUND && CanDragRowSize(row) )
3331 {
3332 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin);
3333 }
3334 else // not a request to start resizing
3335 {
3336 row = YToRow(y);
3337 if ( row >= 0 &&
3338 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, row, -1, event ) )
3339 {
3340 if ( !event.ShiftDown() && !event.CmdDown() )
3341 ClearSelection();
3342 if ( m_selection )
3343 {
3344 if ( event.ShiftDown() )
3345 {
3346 m_selection->SelectBlock
3347 (
3348 m_currentCellCoords.GetRow(), 0,
3349 row, GetNumberCols() - 1,
3350 event
3351 );
3352 }
3353 else
3354 {
3355 m_selection->SelectRow(row, event);
3356 }
3357 }
3358
3359 ChangeCursorMode(WXGRID_CURSOR_SELECT_ROW, m_rowLabelWin);
3360 }
3361 }
3362 }
3363
3364 // ------------ Left double click
3365 //
3366 else if (event.LeftDClick() )
3367 {
3368 row = YToEdgeOfRow(y);
3369 if ( row != wxNOT_FOUND && CanDragRowSize(row) )
3370 {
3371 // adjust row height depending on label text
3372 //
3373 // TODO: generate RESIZING event, see #10754
3374 AutoSizeRowLabelSize( row );
3375
3376 SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, row, -1, event);
3377
3378 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3379 m_dragLastPos = -1;
3380 }
3381 else // not on row separator or it's not resizable
3382 {
3383 row = YToRow(y);
3384 if ( row >=0 &&
3385 !SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, row, -1, event ) )
3386 {
3387 // no default action at the moment
3388 }
3389 }
3390 }
3391
3392 // ------------ Left button released
3393 //
3394 else if ( event.LeftUp() )
3395 {
3396 if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
3397 DoEndDragResizeRow(event);
3398
3399 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin);
3400 m_dragLastPos = -1;
3401 }
3402
3403 // ------------ Right button down
3404 //
3405 else if ( event.RightDown() )
3406 {
3407 row = YToRow(y);
3408 if ( row >=0 &&
3409 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, row, -1, event ) )
3410 {
3411 // no default action at the moment
3412 }
3413 }
3414
3415 // ------------ Right double click
3416 //
3417 else if ( event.RightDClick() )
3418 {
3419 row = YToRow(y);
3420 if ( row >= 0 &&
3421 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, row, -1, event ) )
3422 {
3423 // no default action at the moment
3424 }
3425 }
3426
3427 // ------------ No buttons down and mouse moving
3428 //
3429 else if ( event.Moving() )
3430 {
3431 m_dragRowOrCol = YToEdgeOfRow( y );
3432 if ( m_dragRowOrCol != wxNOT_FOUND )
3433 {
3434 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3435 {
3436 if ( CanDragRowSize(m_dragRowOrCol) )
3437 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, m_rowLabelWin, false);
3438 }
3439 }
3440 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3441 {
3442 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, m_rowLabelWin, false);
3443 }
3444 }
3445 }
3446
3447 void wxGrid::UpdateColumnSortingIndicator(int col)
3448 {
3449 wxCHECK_RET( col != wxNOT_FOUND, "invalid column index" );
3450
3451 if ( m_useNativeHeader )
3452 GetGridColHeader()->UpdateColumn(col);
3453 else if ( m_nativeColumnLabels )
3454 m_colWindow->Refresh();
3455 //else: sorting indicator display not yet implemented in grid version
3456 }
3457
3458 void wxGrid::SetSortingColumn(int col, bool ascending)
3459 {
3460 if ( col == m_sortCol )
3461 {
3462 // we are already using this column for sorting (or not sorting at all)
3463 // but we might still change the sorting order, check for it
3464 if ( m_sortCol != wxNOT_FOUND && ascending != m_sortIsAscending )
3465 {
3466 m_sortIsAscending = ascending;
3467
3468 UpdateColumnSortingIndicator(m_sortCol);
3469 }
3470 }
3471 else // we're changing the column used for sorting
3472 {
3473 const int sortColOld = m_sortCol;
3474
3475 // change it before updating the column as we want GetSortingColumn()
3476 // to return the correct new value
3477 m_sortCol = col;
3478
3479 if ( sortColOld != wxNOT_FOUND )
3480 UpdateColumnSortingIndicator(sortColOld);
3481
3482 if ( m_sortCol != wxNOT_FOUND )
3483 {
3484 m_sortIsAscending = ascending;
3485 UpdateColumnSortingIndicator(m_sortCol);
3486 }
3487 }
3488 }
3489
3490 void wxGrid::DoColHeaderClick(int col)
3491 {
3492 // we consider that the grid was resorted if this event is processed and
3493 // not vetoed
3494 if ( SendEvent(wxEVT_GRID_COL_SORT, -1, col) == 1 )
3495 {
3496 SetSortingColumn(col, IsSortingBy(col) ? !m_sortIsAscending : true);
3497 Refresh();
3498 }
3499 }
3500
3501 void wxGrid::DoStartResizeCol(int col)
3502 {
3503 m_dragRowOrCol = col;
3504 m_dragLastPos = -1;
3505 DoUpdateResizeColWidth(GetColWidth(m_dragRowOrCol));
3506 }
3507
3508 void wxGrid::DoUpdateResizeCol(int x)
3509 {
3510 int cw, ch, dummy, top;
3511 m_gridWin->GetClientSize( &cw, &ch );
3512 CalcUnscrolledPosition( 0, 0, &dummy, &top );
3513
3514 wxClientDC dc( m_gridWin );
3515 PrepareDC( dc );
3516
3517 x = wxMax( x, GetColLeft(m_dragRowOrCol) + GetColMinimalWidth(m_dragRowOrCol));
3518 dc.SetLogicalFunction(wxINVERT);
3519 if ( m_dragLastPos >= 0 )
3520 {
3521 dc.DrawLine( m_dragLastPos, top, m_dragLastPos, top + ch );
3522 }
3523 dc.DrawLine( x, top, x, top + ch );
3524 m_dragLastPos = x;
3525 }
3526
3527 void wxGrid::DoUpdateResizeColWidth(int w)
3528 {
3529 DoUpdateResizeCol(GetColLeft(m_dragRowOrCol) + w);
3530 }
3531
3532 void wxGrid::ProcessColLabelMouseEvent( wxMouseEvent& event )
3533 {
3534 int x, y;
3535 wxPoint pos( event.GetPosition() );
3536 CalcUnscrolledPosition( pos.x, pos.y, &x, &y );
3537
3538 int col = XToCol(x);
3539 if ( event.Dragging() )
3540 {
3541 if (!m_isDragging)
3542 {
3543 m_isDragging = true;
3544 GetColLabelWindow()->CaptureMouse();
3545
3546 if ( m_cursorMode == WXGRID_CURSOR_MOVE_COL && col != -1 )
3547 DoStartMoveCol(col);
3548 }
3549
3550 if ( event.LeftIsDown() )
3551 {
3552 switch ( m_cursorMode )
3553 {
3554 case WXGRID_CURSOR_RESIZE_COL:
3555 DoUpdateResizeCol(x);
3556 break;
3557
3558 case WXGRID_CURSOR_SELECT_COL:
3559 {
3560 if ( col != -1 )
3561 {
3562 if ( m_selection )
3563 m_selection->SelectCol(col, event);
3564 }
3565 }
3566 break;
3567
3568 case WXGRID_CURSOR_MOVE_COL:
3569 {
3570 int posNew = XToPos(x);
3571 int colNew = GetColAt(posNew);
3572
3573 // determine the position of the drop marker
3574 int markerX;
3575 if ( x >= GetColLeft(colNew) + (GetColWidth(colNew) / 2) )
3576 markerX = GetColRight(colNew);
3577 else
3578 markerX = GetColLeft(colNew);
3579
3580 if ( markerX != m_dragLastPos )
3581 {
3582 wxClientDC dc( GetColLabelWindow() );
3583 DoPrepareDC(dc);
3584
3585 int cw, ch;
3586 GetColLabelWindow()->GetClientSize( &cw, &ch );
3587
3588 markerX++;
3589
3590 //Clean up the last indicator
3591 if ( m_dragLastPos >= 0 )
3592 {
3593 wxPen pen( GetColLabelWindow()->GetBackgroundColour(), 2 );
3594 dc.SetPen(pen);
3595 dc.DrawLine( m_dragLastPos + 1, 0, m_dragLastPos + 1, ch );
3596 dc.SetPen(wxNullPen);
3597
3598 if ( XToCol( m_dragLastPos ) != -1 )
3599 DrawColLabel( dc, XToCol( m_dragLastPos ) );
3600 }
3601
3602 const wxColour *color;
3603 //Moving to the same place? Don't draw a marker
3604 if ( colNew == m_dragRowOrCol )
3605 color = wxLIGHT_GREY;
3606 else
3607 color = wxBLUE;
3608
3609 //Draw the marker
3610 wxPen pen( *color, 2 );
3611 dc.SetPen(pen);
3612
3613 dc.DrawLine( markerX, 0, markerX, ch );
3614
3615 dc.SetPen(wxNullPen);
3616
3617 m_dragLastPos = markerX - 1;
3618 }
3619 }
3620 break;
3621
3622 // default label to suppress warnings about "enumeration value
3623 // 'xxx' not handled in switch
3624 default:
3625 break;
3626 }
3627 }
3628 return;
3629 }
3630
3631 if ( m_isDragging && (event.Entering() || event.Leaving()) )
3632 return;
3633
3634 if (m_isDragging)
3635 {
3636 if (GetColLabelWindow()->HasCapture())
3637 GetColLabelWindow()->ReleaseMouse();
3638 m_isDragging = false;
3639 }
3640
3641 // ------------ Entering or leaving the window
3642 //
3643 if ( event.Entering() || event.Leaving() )
3644 {
3645 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3646 }
3647
3648 // ------------ Left button pressed
3649 //
3650 else if ( event.LeftDown() )
3651 {
3652 int col = XToEdgeOfCol(x);
3653 if ( col != wxNOT_FOUND && CanDragColSize(col) )
3654 {
3655 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow());
3656 }
3657 else // not a request to start resizing
3658 {
3659 col = XToCol(x);
3660 if ( col >= 0 &&
3661 !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, col, event ) )
3662 {
3663 if ( m_canDragColMove )
3664 {
3665 //Show button as pressed
3666 wxClientDC dc( GetColLabelWindow() );
3667 int colLeft = GetColLeft( col );
3668 int colRight = GetColRight( col ) - 1;
3669 dc.SetPen( wxPen( GetColLabelWindow()->GetBackgroundColour(), 1 ) );
3670 dc.DrawLine( colLeft, 1, colLeft, m_colLabelHeight-1 );
3671 dc.DrawLine( colLeft, 1, colRight, 1 );
3672
3673 ChangeCursorMode(WXGRID_CURSOR_MOVE_COL, GetColLabelWindow());
3674 }
3675 else
3676 {
3677 if ( !event.ShiftDown() && !event.CmdDown() )
3678 ClearSelection();
3679 if ( m_selection )
3680 {
3681 if ( event.ShiftDown() )
3682 {
3683 m_selection->SelectBlock
3684 (
3685 0, m_currentCellCoords.GetCol(),
3686 GetNumberRows() - 1, col,
3687 event
3688 );
3689 }
3690 else
3691 {
3692 m_selection->SelectCol(col, event);
3693 }
3694 }
3695
3696 ChangeCursorMode(WXGRID_CURSOR_SELECT_COL, GetColLabelWindow());
3697 }
3698 }
3699 }
3700 }
3701
3702 // ------------ Left double click
3703 //
3704 if ( event.LeftDClick() )
3705 {
3706 const int colEdge = XToEdgeOfCol(x);
3707 if ( colEdge == -1 )
3708 {
3709 if ( col >= 0 &&
3710 ! SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, col, event ) )
3711 {
3712 // no default action at the moment
3713 }
3714 }
3715 else
3716 {
3717 // adjust column width depending on label text
3718 //
3719 // TODO: generate RESIZING event, see #10754
3720 AutoSizeColLabelSize( colEdge );
3721
3722 SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, colEdge, event);
3723
3724 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3725 m_dragLastPos = -1;
3726 }
3727 }
3728
3729 // ------------ Left button released
3730 //
3731 else if ( event.LeftUp() )
3732 {
3733 switch ( m_cursorMode )
3734 {
3735 case WXGRID_CURSOR_RESIZE_COL:
3736 DoEndDragResizeCol(event);
3737 break;
3738
3739 case WXGRID_CURSOR_MOVE_COL:
3740 if ( m_dragLastPos == -1 || col == m_dragRowOrCol )
3741 {
3742 // the column didn't actually move anywhere
3743 if ( col != -1 )
3744 DoColHeaderClick(col);
3745 m_colWindow->Refresh(); // "unpress" the column
3746 }
3747 else
3748 {
3749 // get the position of the column we're over
3750 int pos = XToPos(x);
3751
3752 // we may need to adjust the drop position but don't bother
3753 // checking for it if we can't anyhow
3754 if ( pos > 1 )
3755 {
3756 // also find the index of the column we're over: notice
3757 // that the existing "col" variable may be invalid but
3758 // we need a valid one here
3759 const int colValid = GetColAt(pos);
3760
3761 // if we're on the "near" (usually left but right in
3762 // RTL case) part of the column, the actual position we
3763 // should be placed in is actually the one before it
3764 bool onNearPart;
3765 const int middle = GetColLeft(colValid) +
3766 GetColWidth(colValid)/2;
3767 if ( GetLayoutDirection() == wxLayout_LeftToRight )
3768 onNearPart = (x <= middle);
3769 else // wxLayout_RightToLeft
3770 onNearPart = (x > middle);
3771
3772 if ( onNearPart )
3773 pos--;
3774 }
3775
3776 DoEndMoveCol(pos);
3777 }
3778 break;
3779
3780 case WXGRID_CURSOR_SELECT_COL:
3781 case WXGRID_CURSOR_SELECT_CELL:
3782 case WXGRID_CURSOR_RESIZE_ROW:
3783 case WXGRID_CURSOR_SELECT_ROW:
3784 if ( col != -1 )
3785 DoColHeaderClick(col);
3786 break;
3787 }
3788
3789 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow());
3790 m_dragLastPos = -1;
3791 }
3792
3793 // ------------ Right button down
3794 //
3795 else if ( event.RightDown() )
3796 {
3797 if ( col >= 0 &&
3798 !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, col, event ) )
3799 {
3800 // no default action at the moment
3801 }
3802 }
3803
3804 // ------------ Right double click
3805 //
3806 else if ( event.RightDClick() )
3807 {
3808 if ( col >= 0 &&
3809 !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, col, event ) )
3810 {
3811 // no default action at the moment
3812 }
3813 }
3814
3815 // ------------ No buttons down and mouse moving
3816 //
3817 else if ( event.Moving() )
3818 {
3819 m_dragRowOrCol = XToEdgeOfCol( x );
3820 if ( m_dragRowOrCol >= 0 )
3821 {
3822 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
3823 {
3824 if ( CanDragColSize(m_dragRowOrCol) )
3825 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, GetColLabelWindow(), false);
3826 }
3827 }
3828 else if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
3829 {
3830 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL, GetColLabelWindow(), false);
3831 }
3832 }
3833 }
3834
3835 void wxGrid::ProcessCornerLabelMouseEvent( wxMouseEvent& event )
3836 {
3837 if ( event.LeftDown() )
3838 {
3839 // indicate corner label by having both row and
3840 // col args == -1
3841 //
3842 if ( !SendEvent( wxEVT_GRID_LABEL_LEFT_CLICK, -1, -1, event ) )
3843 {
3844 SelectAll();
3845 }
3846 }
3847 else if ( event.LeftDClick() )
3848 {
3849 SendEvent( wxEVT_GRID_LABEL_LEFT_DCLICK, -1, -1, event );
3850 }
3851 else if ( event.RightDown() )
3852 {
3853 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_CLICK, -1, -1, event ) )
3854 {
3855 // no default action at the moment
3856 }
3857 }
3858 else if ( event.RightDClick() )
3859 {
3860 if ( !SendEvent( wxEVT_GRID_LABEL_RIGHT_DCLICK, -1, -1, event ) )
3861 {
3862 // no default action at the moment
3863 }
3864 }
3865 }
3866
3867 void wxGrid::CancelMouseCapture()
3868 {
3869 // cancel operation currently in progress, whatever it is
3870 if ( m_winCapture )
3871 {
3872 m_isDragging = false;
3873 m_startDragPos = wxDefaultPosition;
3874
3875 m_cursorMode = WXGRID_CURSOR_SELECT_CELL;
3876 m_winCapture->SetCursor( *wxSTANDARD_CURSOR );
3877 m_winCapture = NULL;
3878
3879 // remove traces of whatever we drew on screen
3880 Refresh();
3881 }
3882 }
3883
3884 void wxGrid::ChangeCursorMode(CursorMode mode,
3885 wxWindow *win,
3886 bool captureMouse)
3887 {
3888 #if wxUSE_LOG_TRACE
3889 static const wxChar *const cursorModes[] =
3890 {
3891 wxT("SELECT_CELL"),
3892 wxT("RESIZE_ROW"),
3893 wxT("RESIZE_COL"),
3894 wxT("SELECT_ROW"),
3895 wxT("SELECT_COL"),
3896 wxT("MOVE_COL"),
3897 };
3898
3899 wxLogTrace(wxT("grid"),
3900 wxT("wxGrid cursor mode (mouse capture for %s): %s -> %s"),
3901 win == m_colWindow ? wxT("colLabelWin")
3902 : win ? wxT("rowLabelWin")
3903 : wxT("gridWin"),
3904 cursorModes[m_cursorMode], cursorModes[mode]);
3905 #endif // wxUSE_LOG_TRACE
3906
3907 if ( mode == m_cursorMode &&
3908 win == m_winCapture &&
3909 captureMouse == (m_winCapture != NULL))
3910 return;
3911
3912 if ( !win )
3913 {
3914 // by default use the grid itself
3915 win = m_gridWin;
3916 }
3917
3918 if ( m_winCapture )
3919 {
3920 m_winCapture->ReleaseMouse();
3921 m_winCapture = NULL;
3922 }
3923
3924 m_cursorMode = mode;
3925
3926 switch ( m_cursorMode )
3927 {
3928 case WXGRID_CURSOR_RESIZE_ROW:
3929 win->SetCursor( m_rowResizeCursor );
3930 break;
3931
3932 case WXGRID_CURSOR_RESIZE_COL:
3933 win->SetCursor( m_colResizeCursor );
3934 break;
3935
3936 case WXGRID_CURSOR_MOVE_COL:
3937 win->SetCursor( wxCursor(wxCURSOR_HAND) );
3938 break;
3939
3940 default:
3941 win->SetCursor( *wxSTANDARD_CURSOR );
3942 break;
3943 }
3944
3945 // we need to capture mouse when resizing
3946 bool resize = m_cursorMode == WXGRID_CURSOR_RESIZE_ROW ||
3947 m_cursorMode == WXGRID_CURSOR_RESIZE_COL;
3948
3949 if ( captureMouse && resize )
3950 {
3951 win->CaptureMouse();
3952 m_winCapture = win;
3953 }
3954 }
3955
3956 // ----------------------------------------------------------------------------
3957 // grid mouse event processing
3958 // ----------------------------------------------------------------------------
3959
3960 bool
3961 wxGrid::DoGridCellDrag(wxMouseEvent& event,
3962 const wxGridCellCoords& coords,
3963 bool isFirstDrag)
3964 {
3965 bool performDefault = true ;
3966
3967 if ( coords == wxGridNoCellCoords )
3968 return performDefault; // we're outside any valid cell
3969
3970 // Hide the edit control, so it won't interfere with drag-shrinking.
3971 if ( IsCellEditControlShown() )
3972 {
3973 HideCellEditControl();
3974 SaveEditControlValue();
3975 }
3976
3977 switch ( event.GetModifiers() )
3978 {
3979 case wxMOD_CONTROL:
3980 if ( m_selectedBlockCorner == wxGridNoCellCoords)
3981 m_selectedBlockCorner = coords;
3982 UpdateBlockBeingSelected(m_selectedBlockCorner, coords);
3983 break;
3984
3985 case wxMOD_NONE:
3986 if ( CanDragCell() )
3987 {
3988 if ( isFirstDrag )
3989 {
3990 if ( m_selectedBlockCorner == wxGridNoCellCoords)
3991 m_selectedBlockCorner = coords;
3992
3993 // if event is handled by user code, no further processing
3994 if ( SendEvent(wxEVT_GRID_CELL_BEGIN_DRAG, coords, event) != 0 )
3995 performDefault = false;
3996
3997 return performDefault;
3998 }
3999 }
4000
4001 UpdateBlockBeingSelected(m_currentCellCoords, coords);
4002 break;
4003
4004 default:
4005 // we don't handle the other key modifiers
4006 event.Skip();
4007 }
4008
4009 return performDefault;
4010 }
4011
4012 void wxGrid::DoGridLineDrag(wxMouseEvent& event, const wxGridOperations& oper)
4013 {
4014 wxClientDC dc(m_gridWin);
4015 PrepareDC(dc);
4016 dc.SetLogicalFunction(wxINVERT);
4017
4018 const wxRect rectWin(CalcUnscrolledPosition(wxPoint(0, 0)),
4019 m_gridWin->GetClientSize());
4020
4021 // erase the previously drawn line, if any
4022 if ( m_dragLastPos >= 0 )
4023 oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos);
4024
4025 // we need the vertical position for rows and horizontal for columns here
4026 m_dragLastPos = oper.Dual().Select(CalcUnscrolledPosition(event.GetPosition()));
4027
4028 // don't allow resizing beneath the minimal size
4029 const int posMin = oper.GetLineStartPos(this, m_dragRowOrCol) +
4030 oper.GetMinimalLineSize(this, m_dragRowOrCol);
4031 if ( m_dragLastPos < posMin )
4032 m_dragLastPos = posMin;
4033
4034 // and draw it at the new position
4035 oper.DrawParallelLineInRect(dc, rectWin, m_dragLastPos);
4036 }
4037
4038 void wxGrid::DoGridDragEvent(wxMouseEvent& event, const wxGridCellCoords& coords)
4039 {
4040 if ( !m_isDragging )
4041 {
4042 // Don't start doing anything until the mouse has been dragged far
4043 // enough
4044 const wxPoint& pt = event.GetPosition();
4045 if ( m_startDragPos == wxDefaultPosition )
4046 {
4047 m_startDragPos = pt;
4048 return;
4049 }
4050
4051 if ( abs(m_startDragPos.x - pt.x) <= DRAG_SENSITIVITY &&
4052 abs(m_startDragPos.y - pt.y) <= DRAG_SENSITIVITY )
4053 return;
4054 }
4055
4056 const bool isFirstDrag = !m_isDragging;
4057 m_isDragging = true;
4058
4059 switch ( m_cursorMode )
4060 {
4061 case WXGRID_CURSOR_SELECT_CELL:
4062 // no further handling if handled by user
4063 if ( DoGridCellDrag(event, coords, isFirstDrag) == false )
4064 return;
4065 break;
4066
4067 case WXGRID_CURSOR_RESIZE_ROW:
4068 DoGridLineDrag(event, wxGridRowOperations());
4069 break;
4070
4071 case WXGRID_CURSOR_RESIZE_COL:
4072 DoGridLineDrag(event, wxGridColumnOperations());
4073 break;
4074
4075 default:
4076 event.Skip();
4077 }
4078
4079 if ( isFirstDrag )
4080 {
4081 wxASSERT_MSG( !m_winCapture, "shouldn't capture the mouse twice" );
4082
4083 m_winCapture = m_gridWin;
4084 m_winCapture->CaptureMouse();
4085 }
4086 }
4087
4088 void
4089 wxGrid::DoGridCellLeftDown(wxMouseEvent& event,
4090 const wxGridCellCoords& coords,
4091 const wxPoint& pos)
4092 {
4093 if ( SendEvent(wxEVT_GRID_CELL_LEFT_CLICK, coords, event) )
4094 {
4095 // event handled by user code, no need to do anything here
4096 return;
4097 }
4098
4099 if ( !event.CmdDown() )
4100 ClearSelection();
4101
4102 if ( event.ShiftDown() )
4103 {
4104 if ( m_selection )
4105 {
4106 m_selection->SelectBlock(m_currentCellCoords, coords, event);
4107 m_selectedBlockCorner = coords;
4108 }
4109 }
4110 else if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 )
4111 {
4112 DisableCellEditControl();
4113 MakeCellVisible( coords );
4114
4115 if ( event.CmdDown() )
4116 {
4117 if ( m_selection )
4118 {
4119 m_selection->ToggleCellSelection(coords, event);
4120 }
4121
4122 m_selectedBlockTopLeft = wxGridNoCellCoords;
4123 m_selectedBlockBottomRight = wxGridNoCellCoords;
4124 m_selectedBlockCorner = coords;
4125 }
4126 else
4127 {
4128 if ( m_selection )
4129 {
4130 // In row or column selection mode just clicking on the cell
4131 // should select the row or column containing it: this is more
4132 // convenient for the kinds of controls that use such selection
4133 // mode and is compatible with 2.8 behaviour (see #12062).
4134 switch ( m_selection->GetSelectionMode() )
4135 {
4136 case wxGridSelectCells:
4137 case wxGridSelectRowsOrColumns:
4138 // nothing to do in these cases
4139 break;
4140
4141 case wxGridSelectRows:
4142 m_selection->SelectRow(coords.GetRow());
4143 break;
4144
4145 case wxGridSelectColumns:
4146 m_selection->SelectCol(coords.GetCol());
4147 break;
4148 }
4149 }
4150
4151 m_waitForSlowClick = m_currentCellCoords == coords &&
4152 coords != wxGridNoCellCoords;
4153 SetCurrentCell( coords );
4154 }
4155 }
4156 }
4157
4158 void
4159 wxGrid::DoGridCellLeftDClick(wxMouseEvent& event,
4160 const wxGridCellCoords& coords,
4161 const wxPoint& pos)
4162 {
4163 if ( XToEdgeOfCol(pos.x) < 0 && YToEdgeOfRow(pos.y) < 0 )
4164 {
4165 if ( !SendEvent(wxEVT_GRID_CELL_LEFT_DCLICK, coords, event) )
4166 {
4167 // we want double click to select a cell and start editing
4168 // (i.e. to behave in same way as sequence of two slow clicks):
4169 m_waitForSlowClick = true;
4170 }
4171 }
4172 }
4173
4174 void
4175 wxGrid::DoGridCellLeftUp(wxMouseEvent& event, const wxGridCellCoords& coords)
4176 {
4177 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
4178 {
4179 if (m_winCapture)
4180 {
4181 m_winCapture->ReleaseMouse();
4182 m_winCapture = NULL;
4183 }
4184
4185 if ( coords == m_currentCellCoords && m_waitForSlowClick && CanEnableCellControl() )
4186 {
4187 ClearSelection();
4188 EnableCellEditControl();
4189
4190 wxGridCellAttr *attr = GetCellAttr(coords);
4191 wxGridCellEditor *editor = attr->GetEditor(this, coords.GetRow(), coords.GetCol());
4192 editor->StartingClick();
4193 editor->DecRef();
4194 attr->DecRef();
4195
4196 m_waitForSlowClick = false;
4197 }
4198 else if ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
4199 m_selectedBlockBottomRight != wxGridNoCellCoords )
4200 {
4201 if ( m_selection )
4202 {
4203 m_selection->SelectBlock( m_selectedBlockTopLeft,
4204 m_selectedBlockBottomRight,
4205 event );
4206 }
4207
4208 m_selectedBlockTopLeft = wxGridNoCellCoords;
4209 m_selectedBlockBottomRight = wxGridNoCellCoords;
4210
4211 // Show the edit control, if it has been hidden for
4212 // drag-shrinking.
4213 ShowCellEditControl();
4214 }
4215 }
4216 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_ROW )
4217 {
4218 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
4219 DoEndDragResizeRow(event);
4220 }
4221 else if ( m_cursorMode == WXGRID_CURSOR_RESIZE_COL )
4222 {
4223 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
4224 DoEndDragResizeCol(event);
4225 }
4226
4227 m_dragLastPos = -1;
4228 }
4229
4230 void
4231 wxGrid::DoGridMouseMoveEvent(wxMouseEvent& WXUNUSED(event),
4232 const wxGridCellCoords& coords,
4233 const wxPoint& pos)
4234 {
4235 if ( coords.GetRow() < 0 || coords.GetCol() < 0 )
4236 {
4237 // out of grid cell area
4238 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
4239 return;
4240 }
4241
4242 int dragRow = YToEdgeOfRow( pos.y );
4243 int dragCol = XToEdgeOfCol( pos.x );
4244
4245 // Dragging on the corner of a cell to resize in both
4246 // directions is not implemented yet...
4247 //
4248 if ( dragRow >= 0 && dragCol >= 0 )
4249 {
4250 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
4251 return;
4252 }
4253
4254 if ( dragRow >= 0 && CanDragGridSize() && CanDragRowSize(dragRow) )
4255 {
4256 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
4257 {
4258 m_dragRowOrCol = dragRow;
4259 ChangeCursorMode(WXGRID_CURSOR_RESIZE_ROW, NULL, false);
4260 }
4261 }
4262 // When using the native header window we can only resize the columns by
4263 // dragging the dividers in it because we can't make it enter into the
4264 // column resizing mode programmatically
4265 else if ( dragCol >= 0 && !m_useNativeHeader &&
4266 CanDragGridSize() && CanDragColSize(dragCol) )
4267 {
4268 if ( m_cursorMode == WXGRID_CURSOR_SELECT_CELL )
4269 {
4270 m_dragRowOrCol = dragCol;
4271 ChangeCursorMode(WXGRID_CURSOR_RESIZE_COL, NULL, false);
4272 }
4273 }
4274 else // Neither on a row or col edge
4275 {
4276 if ( m_cursorMode != WXGRID_CURSOR_SELECT_CELL )
4277 {
4278 ChangeCursorMode(WXGRID_CURSOR_SELECT_CELL);
4279 }
4280 }
4281 }
4282
4283 void wxGrid::ProcessGridCellMouseEvent(wxMouseEvent& event)
4284 {
4285 if ( event.Entering() || event.Leaving() )
4286 {
4287 // we don't care about these events but we must not reset m_isDragging
4288 // if they happen so return before anything else is done
4289 event.Skip();
4290 return;
4291 }
4292
4293 const wxPoint pos = CalcUnscrolledPosition(event.GetPosition());
4294
4295 // coordinates of the cell under mouse
4296 wxGridCellCoords coords = XYToCell(pos);
4297
4298 int cell_rows, cell_cols;
4299 GetCellSize( coords.GetRow(), coords.GetCol(), &cell_rows, &cell_cols );
4300 if ( (cell_rows < 0) || (cell_cols < 0) )
4301 {
4302 coords.SetRow(coords.GetRow() + cell_rows);
4303 coords.SetCol(coords.GetCol() + cell_cols);
4304 }
4305
4306 if ( event.Dragging() )
4307 {
4308 if ( event.LeftIsDown() )
4309 DoGridDragEvent(event, coords);
4310 else
4311 event.Skip();
4312 return;
4313 }
4314
4315 m_isDragging = false;
4316 m_startDragPos = wxDefaultPosition;
4317
4318 // deal with various button presses
4319 if ( event.IsButton() )
4320 {
4321 if ( coords != wxGridNoCellCoords )
4322 {
4323 DisableCellEditControl();
4324
4325 if ( event.LeftDown() )
4326 DoGridCellLeftDown(event, coords, pos);
4327 else if ( event.LeftDClick() )
4328 DoGridCellLeftDClick(event, coords, pos);
4329 else if ( event.RightDown() )
4330 SendEvent(wxEVT_GRID_CELL_RIGHT_CLICK, coords, event);
4331 else if ( event.RightDClick() )
4332 SendEvent(wxEVT_GRID_CELL_RIGHT_DCLICK, coords, event);
4333 }
4334
4335 // this one should be called even if we're not over any cell
4336 if ( event.LeftUp() )
4337 {
4338 DoGridCellLeftUp(event, coords);
4339 }
4340 }
4341 else if ( event.Moving() )
4342 {
4343 DoGridMouseMoveEvent(event, coords, pos);
4344 }
4345 else // unknown mouse event?
4346 {
4347 event.Skip();
4348 }
4349 }
4350
4351 // this function returns true only if the size really changed
4352 bool wxGrid::DoEndDragResizeLine(const wxGridOperations& oper)
4353 {
4354 if ( m_dragLastPos == -1 )
4355 return false;
4356
4357 const wxGridOperations& doper = oper.Dual();
4358
4359 const wxSize size = m_gridWin->GetClientSize();
4360
4361 const wxPoint ptOrigin = CalcUnscrolledPosition(wxPoint(0, 0));
4362
4363 // erase the last line we drew
4364 wxClientDC dc(m_gridWin);
4365 PrepareDC(dc);
4366 dc.SetLogicalFunction(wxINVERT);
4367
4368 const int posLineStart = oper.Select(ptOrigin);
4369 const int posLineEnd = oper.Select(ptOrigin) + oper.Select(size);
4370
4371 oper.DrawParallelLine(dc, posLineStart, posLineEnd, m_dragLastPos);
4372
4373 // temporarily hide the edit control before resizing
4374 HideCellEditControl();
4375 SaveEditControlValue();
4376
4377 // do resize the line
4378 const int lineStart = oper.GetLineStartPos(this, m_dragRowOrCol);
4379 const int lineSizeOld = oper.GetLineSize(this, m_dragRowOrCol);
4380 oper.SetLineSize(this, m_dragRowOrCol,
4381 wxMax(m_dragLastPos - lineStart,
4382 oper.GetMinimalLineSize(this, m_dragRowOrCol)));
4383 const bool
4384 sizeChanged = oper.GetLineSize(this, m_dragRowOrCol) != lineSizeOld;
4385
4386 m_dragLastPos = -1;
4387
4388 // refresh now if we're not frozen
4389 if ( !GetBatchCount() )
4390 {
4391 // we need to refresh everything beyond the resized line in the header
4392 // window
4393
4394 // get the position from which to refresh in the other direction
4395 wxRect rect(CellToRect(oper.MakeCoords(m_dragRowOrCol, 0)));
4396 rect.SetPosition(CalcScrolledPosition(rect.GetPosition()));
4397
4398 // we only need the ordinate (for rows) or abscissa (for columns) here,
4399 // and need to cover the entire window in the other direction
4400 oper.Select(rect) = 0;
4401
4402 wxRect rectHeader(rect.GetPosition(),
4403 oper.MakeSize
4404 (
4405 oper.GetHeaderWindowSize(this),
4406 doper.Select(size) - doper.Select(rect)
4407 ));
4408
4409 oper.GetHeaderWindow(this)->Refresh(true, &rectHeader);
4410
4411
4412 // also refresh the grid window: extend the rectangle
4413 if ( m_table )
4414 {
4415 oper.SelectSize(rect) = oper.Select(size);
4416
4417 int subtractLines = 0;
4418 const int lineStart = doper.PosToLine(this, posLineStart);
4419 if ( lineStart >= 0 )
4420 {
4421 // ensure that if we have a multi-cell block we redraw all of
4422 // it by increasing the refresh area to cover it entirely if a
4423 // part of it is affected
4424 const int lineEnd = doper.PosToLine(this, posLineEnd, true);
4425 for ( int line = lineStart; line < lineEnd; line++ )
4426 {
4427 int cellLines = oper.Select(
4428 GetCellSize(oper.MakeCoords(m_dragRowOrCol, line)));
4429 if ( cellLines < subtractLines )
4430 subtractLines = cellLines;
4431 }
4432 }
4433
4434 int startPos =
4435 oper.GetLineStartPos(this, m_dragRowOrCol + subtractLines);
4436 startPos = doper.CalcScrolledPosition(this, startPos);
4437
4438 doper.Select(rect) = startPos;
4439 doper.SelectSize(rect) = doper.Select(size) - startPos;
4440
4441 m_gridWin->Refresh(false, &rect);
4442 }
4443 }
4444
4445 // show the edit control back again
4446 ShowCellEditControl();
4447
4448 return sizeChanged;
4449 }
4450
4451 void wxGrid::DoEndDragResizeRow(const wxMouseEvent& event)
4452 {
4453 // TODO: generate RESIZING event, see #10754
4454
4455 if ( DoEndDragResizeLine(wxGridRowOperations()) )
4456 SendGridSizeEvent(wxEVT_GRID_ROW_SIZE, m_dragRowOrCol, -1, event);
4457 }
4458
4459 void wxGrid::DoEndDragResizeCol(const wxMouseEvent& event)
4460 {
4461 // TODO: generate RESIZING event, see #10754
4462
4463 if ( DoEndDragResizeLine(wxGridColumnOperations()) )
4464 SendGridSizeEvent(wxEVT_GRID_COL_SIZE, -1, m_dragRowOrCol, event);
4465 }
4466
4467 void wxGrid::DoStartMoveCol(int col)
4468 {
4469 m_dragRowOrCol = col;
4470 }
4471
4472 void wxGrid::DoEndMoveCol(int pos)
4473 {
4474 wxASSERT_MSG( m_dragRowOrCol != -1, "no matching DoStartMoveCol?" );
4475
4476 if ( SendEvent(wxEVT_GRID_COL_MOVE, -1, m_dragRowOrCol) != -1 )
4477 SetColPos(m_dragRowOrCol, pos);
4478 //else: vetoed by user
4479
4480 m_dragRowOrCol = -1;
4481 }
4482
4483 void wxGrid::RefreshAfterColPosChange()
4484 {
4485 // recalculate the column rights as the column positions have changed,
4486 // unless we calculate them dynamically because all columns widths are the
4487 // same and it's easy to do
4488 if ( !m_colWidths.empty() )
4489 {
4490 int colRight = 0;
4491 for ( int colPos = 0; colPos < m_numCols; colPos++ )
4492 {
4493 int colID = GetColAt( colPos );
4494
4495 colRight += m_colWidths[colID];
4496 m_colRights[colID] = colRight;
4497 }
4498 }
4499
4500 // and make the changes visible
4501 if ( m_useNativeHeader )
4502 {
4503 if ( m_colAt.empty() )
4504 GetGridColHeader()->ResetColumnsOrder();
4505 else
4506 GetGridColHeader()->SetColumnsOrder(m_colAt);
4507 }
4508 else
4509 {
4510 m_colWindow->Refresh();
4511 }
4512 m_gridWin->Refresh();
4513 }
4514
4515 void wxGrid::SetColumnsOrder(const wxArrayInt& order)
4516 {
4517 m_colAt = order;
4518
4519 RefreshAfterColPosChange();
4520 }
4521
4522 void wxGrid::SetColPos(int idx, int pos)
4523 {
4524 // we're going to need m_colAt now, initialize it if needed
4525 if ( m_colAt.empty() )
4526 {
4527 m_colAt.reserve(m_numCols);
4528 for ( int i = 0; i < m_numCols; i++ )
4529 m_colAt.push_back(i);
4530 }
4531
4532 wxHeaderCtrl::MoveColumnInOrderArray(m_colAt, idx, pos);
4533
4534 RefreshAfterColPosChange();
4535 }
4536
4537 void wxGrid::ResetColPos()
4538 {
4539 m_colAt.clear();
4540
4541 RefreshAfterColPosChange();
4542 }
4543
4544 void wxGrid::EnableDragColMove( bool enable )
4545 {
4546 if ( m_canDragColMove == enable )
4547 return;
4548
4549 if ( m_useNativeHeader )
4550 {
4551 // update all columns to make them [not] reorderable
4552 GetGridColHeader()->SetColumnCount(m_numCols);
4553 }
4554
4555 m_canDragColMove = enable;
4556
4557 // we use to call ResetColPos() from here if !enable but this doesn't seem
4558 // right as it would mean there would be no way to "freeze" the current
4559 // columns order by disabling moving them after putting them in the desired
4560 // order, whereas now you can always call ResetColPos() manually if needed
4561 }
4562
4563
4564 //
4565 // ------ interaction with data model
4566 //
4567 bool wxGrid::ProcessTableMessage( wxGridTableMessage& msg )
4568 {
4569 switch ( msg.GetId() )
4570 {
4571 case wxGRIDTABLE_REQUEST_VIEW_GET_VALUES:
4572 return GetModelValues();
4573
4574 case wxGRIDTABLE_REQUEST_VIEW_SEND_VALUES:
4575 return SetModelValues();
4576
4577 case wxGRIDTABLE_NOTIFY_ROWS_INSERTED:
4578 case wxGRIDTABLE_NOTIFY_ROWS_APPENDED:
4579 case wxGRIDTABLE_NOTIFY_ROWS_DELETED:
4580 case wxGRIDTABLE_NOTIFY_COLS_INSERTED:
4581 case wxGRIDTABLE_NOTIFY_COLS_APPENDED:
4582 case wxGRIDTABLE_NOTIFY_COLS_DELETED:
4583 return Redimension( msg );
4584
4585 default:
4586 return false;
4587 }
4588 }
4589
4590 // The behaviour of this function depends on the grid table class
4591 // Clear() function. For the default wxGridStringTable class the
4592 // behaviour is to replace all cell contents with wxEmptyString but
4593 // not to change the number of rows or cols.
4594 //
4595 void wxGrid::ClearGrid()
4596 {
4597 if ( m_table )
4598 {
4599 if (IsCellEditControlEnabled())
4600 DisableCellEditControl();
4601
4602 m_table->Clear();
4603 if (!GetBatchCount())
4604 m_gridWin->Refresh();
4605 }
4606 }
4607
4608 bool
4609 wxGrid::DoModifyLines(bool (wxGridTableBase::*funcModify)(size_t, size_t),
4610 int pos, int num, bool WXUNUSED(updateLabels) )
4611 {
4612 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
4613
4614 if ( !m_table )
4615 return false;
4616
4617 if ( IsCellEditControlEnabled() )
4618 DisableCellEditControl();
4619
4620 return (m_table->*funcModify)(pos, num);
4621
4622 // the table will have sent the results of the insert row
4623 // operation to this view object as a grid table message
4624 }
4625
4626 bool
4627 wxGrid::DoAppendLines(bool (wxGridTableBase::*funcAppend)(size_t),
4628 int num, bool WXUNUSED(updateLabels))
4629 {
4630 wxCHECK_MSG( m_created, false, "must finish creating the grid first" );
4631
4632 if ( !m_table )
4633 return false;
4634
4635 return (m_table->*funcAppend)(num);
4636 }
4637
4638 // ----------------------------------------------------------------------------
4639 // event generation helpers
4640 // ----------------------------------------------------------------------------
4641
4642 void
4643 wxGrid::SendGridSizeEvent(wxEventType type,
4644 int row, int col,
4645 const wxMouseEvent& mouseEv)
4646 {
4647 int rowOrCol = row == -1 ? col : row;
4648
4649 wxGridSizeEvent gridEvt( GetId(),
4650 type,
4651 this,
4652 rowOrCol,
4653 mouseEv.GetX() + GetRowLabelSize(),
4654 mouseEv.GetY() + GetColLabelSize(),
4655 mouseEv);
4656
4657 GetEventHandler()->ProcessEvent(gridEvt);
4658 }
4659
4660 // Generate a grid event based on a mouse event and return:
4661 // -1 if the event was vetoed
4662 // +1 if the event was processed (but not vetoed)
4663 // 0 if the event wasn't handled
4664 int
4665 wxGrid::SendEvent(const wxEventType type,
4666 int row, int col,
4667 const wxMouseEvent& mouseEv)
4668 {
4669 bool claimed, vetoed;
4670
4671 if ( type == wxEVT_GRID_RANGE_SELECT )
4672 {
4673 // Right now, it should _never_ end up here!
4674 wxGridRangeSelectEvent gridEvt( GetId(),
4675 type,
4676 this,
4677 m_selectedBlockTopLeft,
4678 m_selectedBlockBottomRight,
4679 true,
4680 mouseEv);
4681
4682 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4683 vetoed = !gridEvt.IsAllowed();
4684 }
4685 else if ( type == wxEVT_GRID_LABEL_LEFT_CLICK ||
4686 type == wxEVT_GRID_LABEL_LEFT_DCLICK ||
4687 type == wxEVT_GRID_LABEL_RIGHT_CLICK ||
4688 type == wxEVT_GRID_LABEL_RIGHT_DCLICK )
4689 {
4690 wxPoint pos = mouseEv.GetPosition();
4691
4692 if ( mouseEv.GetEventObject() == GetGridRowLabelWindow() )
4693 pos.y += GetColLabelSize();
4694 if ( mouseEv.GetEventObject() == GetGridColLabelWindow() )
4695 pos.x += GetRowLabelSize();
4696
4697 wxGridEvent gridEvt( GetId(),
4698 type,
4699 this,
4700 row, col,
4701 pos.x,
4702 pos.y,
4703 false,
4704 mouseEv);
4705 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4706 vetoed = !gridEvt.IsAllowed();
4707 }
4708 else
4709 {
4710 wxGridEvent gridEvt( GetId(),
4711 type,
4712 this,
4713 row, col,
4714 mouseEv.GetX() + GetRowLabelSize(),
4715 mouseEv.GetY() + GetColLabelSize(),
4716 false,
4717 mouseEv);
4718
4719 if ( type == wxEVT_GRID_CELL_BEGIN_DRAG )
4720 {
4721 // by default the dragging is not supported, the user code must
4722 // explicitly allow the event for it to take place
4723 gridEvt.Veto();
4724 }
4725
4726 claimed = GetEventHandler()->ProcessEvent(gridEvt);
4727 vetoed = !gridEvt.IsAllowed();
4728 }
4729
4730 // A Veto'd event may not be `claimed' so test this first
4731 if (vetoed)
4732 return -1;
4733
4734 return claimed ? 1 : 0;
4735 }
4736
4737 // Generate a grid event of specified type, return value same as above
4738 //
4739 int
4740 wxGrid::SendEvent(const wxEventType type, int row, int col, const wxString& s)
4741 {
4742 wxGridEvent gridEvt( GetId(), type, this, row, col );
4743 gridEvt.SetString(s);
4744
4745 const bool claimed = GetEventHandler()->ProcessEvent(gridEvt);
4746
4747 // A Veto'd event may not be `claimed' so test this first
4748 if ( !gridEvt.IsAllowed() )
4749 return -1;
4750
4751 return claimed ? 1 : 0;
4752 }
4753
4754 void wxGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
4755 {
4756 // needed to prevent zillions of paint events on MSW
4757 wxPaintDC dc(this);
4758 }
4759
4760 void wxGrid::Refresh(bool eraseb, const wxRect* rect)
4761 {
4762 // Don't do anything if between Begin/EndBatch...
4763 // EndBatch() will do all this on the last nested one anyway.
4764 if ( m_created && !GetBatchCount() )
4765 {
4766 // Refresh to get correct scrolled position:
4767 wxScrolledWindow::Refresh(eraseb, rect);
4768
4769 if (rect)
4770 {
4771 int rect_x, rect_y, rectWidth, rectHeight;
4772 int width_label, width_cell, height_label, height_cell;
4773 int x, y;
4774
4775 // Copy rectangle can get scroll offsets..
4776 rect_x = rect->GetX();
4777 rect_y = rect->GetY();
4778 rectWidth = rect->GetWidth();
4779 rectHeight = rect->GetHeight();
4780
4781 width_label = m_rowLabelWidth - rect_x;
4782 if (width_label > rectWidth)
4783 width_label = rectWidth;
4784
4785 height_label = m_colLabelHeight - rect_y;
4786 if (height_label > rectHeight)
4787 height_label = rectHeight;
4788
4789 if (rect_x > m_rowLabelWidth)
4790 {
4791 x = rect_x - m_rowLabelWidth;
4792 width_cell = rectWidth;
4793 }
4794 else
4795 {
4796 x = 0;
4797 width_cell = rectWidth - (m_rowLabelWidth - rect_x);
4798 }
4799
4800 if (rect_y > m_colLabelHeight)
4801 {
4802 y = rect_y - m_colLabelHeight;
4803 height_cell = rectHeight;
4804 }
4805 else
4806 {
4807 y = 0;
4808 height_cell = rectHeight - (m_colLabelHeight - rect_y);
4809 }
4810
4811 // Paint corner label part intersecting rect.
4812 if ( width_label > 0 && height_label > 0 )
4813 {
4814 wxRect anotherrect(rect_x, rect_y, width_label, height_label);
4815 m_cornerLabelWin->Refresh(eraseb, &anotherrect);
4816 }
4817
4818 // Paint col labels part intersecting rect.
4819 if ( width_cell > 0 && height_label > 0 )
4820 {
4821 wxRect anotherrect(x, rect_y, width_cell, height_label);
4822 m_colWindow->Refresh(eraseb, &anotherrect);
4823 }
4824
4825 // Paint row labels part intersecting rect.
4826 if ( width_label > 0 && height_cell > 0 )
4827 {
4828 wxRect anotherrect(rect_x, y, width_label, height_cell);
4829 m_rowLabelWin->Refresh(eraseb, &anotherrect);
4830 }
4831
4832 // Paint cell area part intersecting rect.
4833 if ( width_cell > 0 && height_cell > 0 )
4834 {
4835 wxRect anotherrect(x, y, width_cell, height_cell);
4836 m_gridWin->Refresh(eraseb, &anotherrect);
4837 }
4838 }
4839 else
4840 {
4841 m_cornerLabelWin->Refresh(eraseb, NULL);
4842 m_colWindow->Refresh(eraseb, NULL);
4843 m_rowLabelWin->Refresh(eraseb, NULL);
4844 m_gridWin->Refresh(eraseb, NULL);
4845 }
4846 }
4847 }
4848
4849 void wxGrid::OnSize(wxSizeEvent& WXUNUSED(event))
4850 {
4851 if (m_targetWindow != this) // check whether initialisation has been done
4852 {
4853 // reposition our children windows
4854 CalcWindowSizes();
4855 }
4856 }
4857
4858 void wxGrid::OnKeyDown( wxKeyEvent& event )
4859 {
4860 if ( m_inOnKeyDown )
4861 {
4862 // shouldn't be here - we are going round in circles...
4863 //
4864 wxFAIL_MSG( wxT("wxGrid::OnKeyDown called while already active") );
4865 }
4866
4867 m_inOnKeyDown = true;
4868
4869 // propagate the event up and see if it gets processed
4870 wxWindow *parent = GetParent();
4871 wxKeyEvent keyEvt( event );
4872 keyEvt.SetEventObject( parent );
4873
4874 if ( !parent->GetEventHandler()->ProcessEvent( keyEvt ) )
4875 {
4876 if (GetLayoutDirection() == wxLayout_RightToLeft)
4877 {
4878 if (event.GetKeyCode() == WXK_RIGHT)
4879 event.m_keyCode = WXK_LEFT;
4880 else if (event.GetKeyCode() == WXK_LEFT)
4881 event.m_keyCode = WXK_RIGHT;
4882 }
4883
4884 // try local handlers
4885 switch ( event.GetKeyCode() )
4886 {
4887 case WXK_UP:
4888 if ( event.ControlDown() )
4889 MoveCursorUpBlock( event.ShiftDown() );
4890 else
4891 MoveCursorUp( event.ShiftDown() );
4892 break;
4893
4894 case WXK_DOWN:
4895 if ( event.ControlDown() )
4896 MoveCursorDownBlock( event.ShiftDown() );
4897 else
4898 MoveCursorDown( event.ShiftDown() );
4899 break;
4900
4901 case WXK_LEFT:
4902 if ( event.ControlDown() )
4903 MoveCursorLeftBlock( event.ShiftDown() );
4904 else
4905 MoveCursorLeft( event.ShiftDown() );
4906 break;
4907
4908 case WXK_RIGHT:
4909 if ( event.ControlDown() )
4910 MoveCursorRightBlock( event.ShiftDown() );
4911 else
4912 MoveCursorRight( event.ShiftDown() );
4913 break;
4914
4915 case WXK_RETURN:
4916 case WXK_NUMPAD_ENTER:
4917 if ( event.ControlDown() )
4918 {
4919 event.Skip(); // to let the edit control have the return
4920 }
4921 else
4922 {
4923 if ( GetGridCursorRow() < GetNumberRows()-1 )
4924 {
4925 MoveCursorDown( event.ShiftDown() );
4926 }
4927 else
4928 {
4929 // at the bottom of a column
4930 DisableCellEditControl();
4931 }
4932 }
4933 break;
4934
4935 case WXK_ESCAPE:
4936 ClearSelection();
4937 break;
4938
4939 case WXK_TAB:
4940 if (event.ShiftDown())
4941 {
4942 if ( GetGridCursorCol() > 0 )
4943 {
4944 MoveCursorLeft( false );
4945 }
4946 else
4947 {
4948 // at left of grid
4949 DisableCellEditControl();
4950 }
4951 }
4952 else
4953 {
4954 if ( GetGridCursorCol() < GetNumberCols() - 1 )
4955 {
4956 MoveCursorRight( false );
4957 }
4958 else
4959 {
4960 // at right of grid
4961 DisableCellEditControl();
4962 }
4963 }
4964 break;
4965
4966 case WXK_HOME:
4967 GoToCell(event.ControlDown() ? 0
4968 : m_currentCellCoords.GetRow(),
4969 0);
4970 break;
4971
4972 case WXK_END:
4973 GoToCell(event.ControlDown() ? m_numRows - 1
4974 : m_currentCellCoords.GetRow(),
4975 m_numCols - 1);
4976 break;
4977
4978 case WXK_PAGEUP:
4979 MovePageUp();
4980 break;
4981
4982 case WXK_PAGEDOWN:
4983 MovePageDown();
4984 break;
4985
4986 case WXK_SPACE:
4987 // Ctrl-Space selects the current column, Shift-Space -- the
4988 // current row and Ctrl-Shift-Space -- everything
4989 switch ( m_selection ? event.GetModifiers() : wxMOD_NONE )
4990 {
4991 case wxMOD_CONTROL:
4992 m_selection->SelectCol(m_currentCellCoords.GetCol());
4993 break;
4994
4995 case wxMOD_SHIFT:
4996 m_selection->SelectRow(m_currentCellCoords.GetRow());
4997 break;
4998
4999 case wxMOD_CONTROL | wxMOD_SHIFT:
5000 m_selection->SelectBlock(0, 0,
5001 m_numRows - 1, m_numCols - 1);
5002 break;
5003
5004 case wxMOD_NONE:
5005 if ( !IsEditable() )
5006 {
5007 MoveCursorRight(false);
5008 break;
5009 }
5010 //else: fall through
5011
5012 default:
5013 event.Skip();
5014 }
5015 break;
5016
5017 default:
5018 event.Skip();
5019 break;
5020 }
5021 }
5022
5023 m_inOnKeyDown = false;
5024 }
5025
5026 void wxGrid::OnKeyUp( wxKeyEvent& event )
5027 {
5028 // try local handlers
5029 //
5030 if ( event.GetKeyCode() == WXK_SHIFT )
5031 {
5032 if ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
5033 m_selectedBlockBottomRight != wxGridNoCellCoords )
5034 {
5035 if ( m_selection )
5036 {
5037 m_selection->SelectBlock(
5038 m_selectedBlockTopLeft,
5039 m_selectedBlockBottomRight,
5040 event);
5041 }
5042 }
5043
5044 m_selectedBlockTopLeft = wxGridNoCellCoords;
5045 m_selectedBlockBottomRight = wxGridNoCellCoords;
5046 m_selectedBlockCorner = wxGridNoCellCoords;
5047 }
5048 }
5049
5050 void wxGrid::OnChar( wxKeyEvent& event )
5051 {
5052 // is it possible to edit the current cell at all?
5053 if ( !IsCellEditControlEnabled() && CanEnableCellControl() )
5054 {
5055 // yes, now check whether the cells editor accepts the key
5056 int row = m_currentCellCoords.GetRow();
5057 int col = m_currentCellCoords.GetCol();
5058 wxGridCellAttr *attr = GetCellAttr(row, col);
5059 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
5060
5061 // <F2> is special and will always start editing, for
5062 // other keys - ask the editor itself
5063 if ( (event.GetKeyCode() == WXK_F2 && !event.HasModifiers())
5064 || editor->IsAcceptedKey(event) )
5065 {
5066 // ensure cell is visble
5067 MakeCellVisible(row, col);
5068 EnableCellEditControl();
5069
5070 // a problem can arise if the cell is not completely
5071 // visible (even after calling MakeCellVisible the
5072 // control is not created and calling StartingKey will
5073 // crash the app
5074 if ( event.GetKeyCode() != WXK_F2 && editor->IsCreated() && m_cellEditCtrlEnabled )
5075 editor->StartingKey(event);
5076 }
5077 else
5078 {
5079 event.Skip();
5080 }
5081
5082 editor->DecRef();
5083 attr->DecRef();
5084 }
5085 else
5086 {
5087 event.Skip();
5088 }
5089 }
5090
5091 void wxGrid::OnEraseBackground(wxEraseEvent&)
5092 {
5093 }
5094
5095 bool wxGrid::SetCurrentCell( const wxGridCellCoords& coords )
5096 {
5097 if ( SendEvent(wxEVT_GRID_SELECT_CELL, coords) == -1 )
5098 {
5099 // the event has been vetoed - do nothing
5100 return false;
5101 }
5102
5103 #if !defined(__WXMAC__)
5104 wxClientDC dc( m_gridWin );
5105 PrepareDC( dc );
5106 #endif
5107
5108 if ( m_currentCellCoords != wxGridNoCellCoords )
5109 {
5110 DisableCellEditControl();
5111
5112 if ( IsVisible( m_currentCellCoords, false ) )
5113 {
5114 wxRect r;
5115 r = BlockToDeviceRect( m_currentCellCoords, m_currentCellCoords );
5116 if ( !m_gridLinesEnabled )
5117 {
5118 r.x--;
5119 r.y--;
5120 r.width++;
5121 r.height++;
5122 }
5123
5124 wxGridCellCoordsArray cells = CalcCellsExposed( r );
5125
5126 // Otherwise refresh redraws the highlight!
5127 m_currentCellCoords = coords;
5128
5129 #if defined(__WXMAC__)
5130 m_gridWin->Refresh(true /*, & r */);
5131 #else
5132 DrawGridCellArea( dc, cells );
5133 DrawAllGridLines( dc, r );
5134 #endif
5135 }
5136 }
5137
5138 m_currentCellCoords = coords;
5139
5140 wxGridCellAttr *attr = GetCellAttr( coords );
5141 #if !defined(__WXMAC__)
5142 DrawCellHighlight( dc, attr );
5143 #endif
5144 attr->DecRef();
5145
5146 return true;
5147 }
5148
5149 void
5150 wxGrid::UpdateBlockBeingSelected(int topRow, int leftCol,
5151 int bottomRow, int rightCol)
5152 {
5153 MakeCellVisible(m_selectedBlockCorner);
5154 m_selectedBlockCorner = wxGridCellCoords(bottomRow, rightCol);
5155
5156 if ( m_selection )
5157 {
5158 switch ( m_selection->GetSelectionMode() )
5159 {
5160 default:
5161 wxFAIL_MSG( "unknown selection mode" );
5162 // fall through
5163
5164 case wxGridSelectCells:
5165 // arbitrary blocks selection allowed so just use the cell
5166 // coordinates as is
5167 break;
5168
5169 case wxGridSelectRows:
5170 // only full rows selection allowd, ensure that we do select
5171 // full rows
5172 leftCol = 0;
5173 rightCol = GetNumberCols() - 1;
5174 break;
5175
5176 case wxGridSelectColumns:
5177 // same as above but for columns
5178 topRow = 0;
5179 bottomRow = GetNumberRows() - 1;
5180 break;
5181
5182 case wxGridSelectRowsOrColumns:
5183 // in this mode we can select only full rows or full columns so
5184 // it doesn't make sense to select blocks at all (and we can't
5185 // extend the block because there is no preferred direction, we
5186 // could only extend it to cover the entire grid but this is
5187 // not useful)
5188 return;
5189 }
5190 }
5191
5192 EnsureFirstLessThanSecond(topRow, bottomRow);
5193 EnsureFirstLessThanSecond(leftCol, rightCol);
5194
5195 wxGridCellCoords updateTopLeft = wxGridCellCoords(topRow, leftCol),
5196 updateBottomRight = wxGridCellCoords(bottomRow, rightCol);
5197
5198 // First the case that we selected a completely new area
5199 if ( m_selectedBlockTopLeft == wxGridNoCellCoords ||
5200 m_selectedBlockBottomRight == wxGridNoCellCoords )
5201 {
5202 wxRect rect;
5203 rect = BlockToDeviceRect( wxGridCellCoords ( topRow, leftCol ),
5204 wxGridCellCoords ( bottomRow, rightCol ) );
5205 m_gridWin->Refresh( false, &rect );
5206 }
5207
5208 // Now handle changing an existing selection area.
5209 else if ( m_selectedBlockTopLeft != updateTopLeft ||
5210 m_selectedBlockBottomRight != updateBottomRight )
5211 {
5212 // Compute two optimal update rectangles:
5213 // Either one rectangle is a real subset of the
5214 // other, or they are (almost) disjoint!
5215 wxRect rect[4];
5216 bool need_refresh[4];
5217 need_refresh[0] =
5218 need_refresh[1] =
5219 need_refresh[2] =
5220 need_refresh[3] = false;
5221 int i;
5222
5223 // Store intermediate values
5224 wxCoord oldLeft = m_selectedBlockTopLeft.GetCol();
5225 wxCoord oldTop = m_selectedBlockTopLeft.GetRow();
5226 wxCoord oldRight = m_selectedBlockBottomRight.GetCol();
5227 wxCoord oldBottom = m_selectedBlockBottomRight.GetRow();
5228
5229 // Determine the outer/inner coordinates.
5230 EnsureFirstLessThanSecond(oldLeft, leftCol);
5231 EnsureFirstLessThanSecond(oldTop, topRow);
5232 EnsureFirstLessThanSecond(rightCol, oldRight);
5233 EnsureFirstLessThanSecond(bottomRow, oldBottom);
5234
5235 // Now, either the stuff marked old is the outer
5236 // rectangle or we don't have a situation where one
5237 // is contained in the other.
5238
5239 if ( oldLeft < leftCol )
5240 {
5241 // Refresh the newly selected or deselected
5242 // area to the left of the old or new selection.
5243 need_refresh[0] = true;
5244 rect[0] = BlockToDeviceRect(
5245 wxGridCellCoords( oldTop, oldLeft ),
5246 wxGridCellCoords( oldBottom, leftCol - 1 ) );
5247 }
5248
5249 if ( oldTop < topRow )
5250 {
5251 // Refresh the newly selected or deselected
5252 // area above the old or new selection.
5253 need_refresh[1] = true;
5254 rect[1] = BlockToDeviceRect(
5255 wxGridCellCoords( oldTop, leftCol ),
5256 wxGridCellCoords( topRow - 1, rightCol ) );
5257 }
5258
5259 if ( oldRight > rightCol )
5260 {
5261 // Refresh the newly selected or deselected
5262 // area to the right of the old or new selection.
5263 need_refresh[2] = true;
5264 rect[2] = BlockToDeviceRect(
5265 wxGridCellCoords( oldTop, rightCol + 1 ),
5266 wxGridCellCoords( oldBottom, oldRight ) );
5267 }
5268
5269 if ( oldBottom > bottomRow )
5270 {
5271 // Refresh the newly selected or deselected
5272 // area below the old or new selection.
5273 need_refresh[3] = true;
5274 rect[3] = BlockToDeviceRect(
5275 wxGridCellCoords( bottomRow + 1, leftCol ),
5276 wxGridCellCoords( oldBottom, rightCol ) );
5277 }
5278
5279 // various Refresh() calls
5280 for (i = 0; i < 4; i++ )
5281 if ( need_refresh[i] && rect[i] != wxGridNoCellRect )
5282 m_gridWin->Refresh( false, &(rect[i]) );
5283 }
5284
5285 // change selection
5286 m_selectedBlockTopLeft = updateTopLeft;
5287 m_selectedBlockBottomRight = updateBottomRight;
5288 }
5289
5290 //
5291 // ------ functions to get/send data (see also public functions)
5292 //
5293
5294 bool wxGrid::GetModelValues()
5295 {
5296 // Hide the editor, so it won't hide a changed value.
5297 HideCellEditControl();
5298
5299 if ( m_table )
5300 {
5301 // all we need to do is repaint the grid
5302 //
5303 m_gridWin->Refresh();
5304 return true;
5305 }
5306
5307 return false;
5308 }
5309
5310 bool wxGrid::SetModelValues()
5311 {
5312 int row, col;
5313
5314 // Disable the editor, so it won't hide a changed value.
5315 // Do we also want to save the current value of the editor first?
5316 // I think so ...
5317 DisableCellEditControl();
5318
5319 if ( m_table )
5320 {
5321 for ( row = 0; row < m_numRows; row++ )
5322 {
5323 for ( col = 0; col < m_numCols; col++ )
5324 {
5325 m_table->SetValue( row, col, GetCellValue(row, col) );
5326 }
5327 }
5328
5329 return true;
5330 }
5331
5332 return false;
5333 }
5334
5335 // Note - this function only draws cells that are in the list of
5336 // exposed cells (usually set from the update region by
5337 // CalcExposedCells)
5338 //
5339 void wxGrid::DrawGridCellArea( wxDC& dc, const wxGridCellCoordsArray& cells )
5340 {
5341 if ( !m_numRows || !m_numCols )
5342 return;
5343
5344 int i, numCells = cells.GetCount();
5345 int row, col, cell_rows, cell_cols;
5346 wxGridCellCoordsArray redrawCells;
5347
5348 for ( i = numCells - 1; i >= 0; i-- )
5349 {
5350 row = cells[i].GetRow();
5351 col = cells[i].GetCol();
5352 GetCellSize( row, col, &cell_rows, &cell_cols );
5353
5354 // If this cell is part of a multicell block, find owner for repaint
5355 if ( cell_rows <= 0 || cell_cols <= 0 )
5356 {
5357 wxGridCellCoords cell( row + cell_rows, col + cell_cols );
5358 bool marked = false;
5359 for ( int j = 0; j < numCells; j++ )
5360 {
5361 if ( cell == cells[j] )
5362 {
5363 marked = true;
5364 break;
5365 }
5366 }
5367
5368 if (!marked)
5369 {
5370 int count = redrawCells.GetCount();
5371 for (int j = 0; j < count; j++)
5372 {
5373 if ( cell == redrawCells[j] )
5374 {
5375 marked = true;
5376 break;
5377 }
5378 }
5379
5380 if (!marked)
5381 redrawCells.Add( cell );
5382 }
5383
5384 // don't bother drawing this cell
5385 continue;
5386 }
5387
5388 // If this cell is empty, find cell to left that might want to overflow
5389 if (m_table && m_table->IsEmptyCell(row, col))
5390 {
5391 for ( int l = 0; l < cell_rows; l++ )
5392 {
5393 // find a cell in this row to leave already marked for repaint
5394 int left = col;
5395 for (int k = 0; k < int(redrawCells.GetCount()); k++)
5396 if ((redrawCells[k].GetCol() < left) &&
5397 (redrawCells[k].GetRow() == row))
5398 {
5399 left = redrawCells[k].GetCol();
5400 }
5401
5402 if (left == col)
5403 left = 0; // oh well
5404
5405 for (int j = col - 1; j >= left; j--)
5406 {
5407 if (!m_table->IsEmptyCell(row + l, j))
5408 {
5409 if (GetCellOverflow(row + l, j))
5410 {
5411 wxGridCellCoords cell(row + l, j);
5412 bool marked = false;
5413
5414 for (int k = 0; k < numCells; k++)
5415 {
5416 if ( cell == cells[k] )
5417 {
5418 marked = true;
5419 break;
5420 }
5421 }
5422
5423 if (!marked)
5424 {
5425 int count = redrawCells.GetCount();
5426 for (int k = 0; k < count; k++)
5427 {
5428 if ( cell == redrawCells[k] )
5429 {
5430 marked = true;
5431 break;
5432 }
5433 }
5434 if (!marked)
5435 redrawCells.Add( cell );
5436 }
5437 }
5438 break;
5439 }
5440 }
5441 }
5442 }
5443
5444 DrawCell( dc, cells[i] );
5445 }
5446
5447 numCells = redrawCells.GetCount();
5448
5449 for ( i = numCells - 1; i >= 0; i-- )
5450 {
5451 DrawCell( dc, redrawCells[i] );
5452 }
5453 }
5454
5455 void wxGrid::DrawGridSpace( wxDC& dc )
5456 {
5457 int cw, ch;
5458 m_gridWin->GetClientSize( &cw, &ch );
5459
5460 int right, bottom;
5461 CalcUnscrolledPosition( cw, ch, &right, &bottom );
5462
5463 int rightCol = m_numCols > 0 ? GetColRight(GetColAt( m_numCols - 1 )) : 0;
5464 int bottomRow = m_numRows > 0 ? GetRowBottom(m_numRows - 1) : 0;
5465
5466 if ( right > rightCol || bottom > bottomRow )
5467 {
5468 int left, top;
5469 CalcUnscrolledPosition( 0, 0, &left, &top );
5470
5471 dc.SetBrush(GetDefaultCellBackgroundColour());
5472 dc.SetPen( *wxTRANSPARENT_PEN );
5473
5474 if ( right > rightCol )
5475 {
5476 dc.DrawRectangle( rightCol, top, right - rightCol, ch );
5477 }
5478
5479 if ( bottom > bottomRow )
5480 {
5481 dc.DrawRectangle( left, bottomRow, cw, bottom - bottomRow );
5482 }
5483 }
5484 }
5485
5486 void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
5487 {
5488 int row = coords.GetRow();
5489 int col = coords.GetCol();
5490
5491 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5492 return;
5493
5494 // we draw the cell border ourselves
5495 wxGridCellAttr* attr = GetCellAttr(row, col);
5496
5497 bool isCurrent = coords == m_currentCellCoords;
5498
5499 wxRect rect = CellToRect( row, col );
5500
5501 // if the editor is shown, we should use it and not the renderer
5502 // Note: However, only if it is really _shown_, i.e. not hidden!
5503 if ( isCurrent && IsCellEditControlShown() )
5504 {
5505 // NB: this "#if..." is temporary and fixes a problem where the
5506 // edit control is erased by this code after being rendered.
5507 // On wxMac (QD build only), the cell editor is a wxTextCntl and is rendered
5508 // implicitly, causing this out-of order render.
5509 #if !defined(__WXMAC__)
5510 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
5511 editor->PaintBackground(rect, attr);
5512 editor->DecRef();
5513 #endif
5514 }
5515 else
5516 {
5517 // but all the rest is drawn by the cell renderer and hence may be customized
5518 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
5519 renderer->Draw(*this, *attr, dc, rect, row, col, IsInSelection(coords));
5520 renderer->DecRef();
5521 }
5522
5523 attr->DecRef();
5524 }
5525
5526 void wxGrid::DrawCellHighlight( wxDC& dc, const wxGridCellAttr *attr )
5527 {
5528 // don't show highlight when the grid doesn't have focus
5529 if ( !HasFocus() )
5530 return;
5531
5532 int row = m_currentCellCoords.GetRow();
5533 int col = m_currentCellCoords.GetCol();
5534
5535 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5536 return;
5537
5538 wxRect rect = CellToRect(row, col);
5539
5540 // hmmm... what could we do here to show that the cell is disabled?
5541 // for now, I just draw a thinner border than for the other ones, but
5542 // it doesn't look really good
5543
5544 int penWidth = attr->IsReadOnly() ? m_cellHighlightROPenWidth : m_cellHighlightPenWidth;
5545
5546 if (penWidth > 0)
5547 {
5548 // The center of the drawn line is where the position/width/height of
5549 // the rectangle is actually at (on wxMSW at least), so the
5550 // size of the rectangle is reduced to compensate for the thickness of
5551 // the line. If this is too strange on non-wxMSW platforms then
5552 // please #ifdef this appropriately.
5553 rect.x += penWidth / 2;
5554 rect.y += penWidth / 2;
5555 rect.width -= penWidth - 1;
5556 rect.height -= penWidth - 1;
5557
5558 // Now draw the rectangle
5559 // use the cellHighlightColour if the cell is inside a selection, this
5560 // will ensure the cell is always visible.
5561 dc.SetPen(wxPen(IsInSelection(row,col) ? m_selectionForeground
5562 : m_cellHighlightColour,
5563 penWidth));
5564 dc.SetBrush(*wxTRANSPARENT_BRUSH);
5565 dc.DrawRectangle(rect);
5566 }
5567 }
5568
5569 wxPen wxGrid::GetDefaultGridLinePen()
5570 {
5571 return wxPen(GetGridLineColour());
5572 }
5573
5574 wxPen wxGrid::GetRowGridLinePen(int WXUNUSED(row))
5575 {
5576 return GetDefaultGridLinePen();
5577 }
5578
5579 wxPen wxGrid::GetColGridLinePen(int WXUNUSED(col))
5580 {
5581 return GetDefaultGridLinePen();
5582 }
5583
5584 void wxGrid::DrawCellBorder( wxDC& dc, const wxGridCellCoords& coords )
5585 {
5586 int row = coords.GetRow();
5587 int col = coords.GetCol();
5588 if ( GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
5589 return;
5590
5591
5592 wxRect rect = CellToRect( row, col );
5593
5594 // right hand border
5595 dc.SetPen( GetColGridLinePen(col) );
5596 dc.DrawLine( rect.x + rect.width, rect.y,
5597 rect.x + rect.width, rect.y + rect.height + 1 );
5598
5599 // bottom border
5600 dc.SetPen( GetRowGridLinePen(row) );
5601 dc.DrawLine( rect.x, rect.y + rect.height,
5602 rect.x + rect.width, rect.y + rect.height);
5603 }
5604
5605 void wxGrid::DrawHighlight(wxDC& dc, const wxGridCellCoordsArray& cells)
5606 {
5607 // This if block was previously in wxGrid::OnPaint but that doesn't
5608 // seem to get called under wxGTK - MB
5609 //
5610 if ( m_currentCellCoords == wxGridNoCellCoords &&
5611 m_numRows && m_numCols )
5612 {
5613 m_currentCellCoords.Set(0, 0);
5614 }
5615
5616 if ( IsCellEditControlShown() )
5617 {
5618 // don't show highlight when the edit control is shown
5619 return;
5620 }
5621
5622 // if the active cell was repainted, repaint its highlight too because it
5623 // might have been damaged by the grid lines
5624 size_t count = cells.GetCount();
5625 for ( size_t n = 0; n < count; n++ )
5626 {
5627 wxGridCellCoords cell = cells[n];
5628
5629 // If we are using attributes, then we may have just exposed another
5630 // cell in a partially-visible merged cluster of cells. If the "anchor"
5631 // (upper left) cell of this merged cluster is the cell indicated by
5632 // m_currentCellCoords, then we need to refresh the cell highlight even
5633 // though the "anchor" itself is not part of our update segment.
5634 if ( CanHaveAttributes() )
5635 {
5636 int rows = 0,
5637 cols = 0;
5638 GetCellSize(cell.GetRow(), cell.GetCol(), &rows, &cols);
5639
5640 if ( rows < 0 )
5641 cell.SetRow(cell.GetRow() + rows);
5642
5643 if ( cols < 0 )
5644 cell.SetCol(cell.GetCol() + cols);
5645 }
5646
5647 if ( cell == m_currentCellCoords )
5648 {
5649 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
5650 DrawCellHighlight(dc, attr);
5651 attr->DecRef();
5652
5653 break;
5654 }
5655 }
5656 }
5657
5658 // Used by wxGrid::Render() to draw the grid lines only for the cells in the
5659 // specified range.
5660 void
5661 wxGrid::DrawRangeGridLines(wxDC& dc,
5662 const wxRegion& reg,
5663 const wxGridCellCoords& topLeft,
5664 const wxGridCellCoords& bottomRight)
5665 {
5666 if ( !m_gridLinesEnabled )
5667 return;
5668
5669 int top, left, width, height;
5670 reg.GetBox( left, top, width, height );
5671
5672 // create a clipping region
5673 wxRegion clippedcells( dc.LogicalToDeviceX( left ),
5674 dc.LogicalToDeviceY( top ),
5675 dc.LogicalToDeviceXRel( width ),
5676 dc.LogicalToDeviceYRel( height ) );
5677
5678 // subtract multi cell span area from clipping region for lines
5679 wxRect rect;
5680 for ( int row = topLeft.GetRow(); row <= bottomRight.GetRow(); row++ )
5681 {
5682 for ( int col = topLeft.GetCol(); col <= bottomRight.GetCol(); col++ )
5683 {
5684 int cell_rows, cell_cols;
5685 GetCellSize( row, col, &cell_rows, &cell_cols );
5686 if ( cell_rows > 1 || cell_cols > 1 ) // multi cell
5687 {
5688 rect = CellToRect( row, col );
5689 // cater for scaling
5690 // device origin already set in ::Render() for x, y
5691 rect.x = dc.LogicalToDeviceX( rect.x );
5692 rect.y = dc.LogicalToDeviceY( rect.y );
5693 rect.width = dc.LogicalToDeviceXRel( rect.width );
5694 rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
5695 clippedcells.Subtract( rect );
5696 }
5697 else if ( cell_rows < 0 || cell_cols < 0 ) // part of multicell
5698 {
5699 rect = CellToRect( row + cell_rows, col + cell_cols );
5700 rect.x = dc.LogicalToDeviceX( rect.x );
5701 rect.y = dc.LogicalToDeviceY( rect.y );
5702 rect.width = dc.LogicalToDeviceXRel( rect.width );
5703 rect.height = dc.LogicalToDeviceYRel( rect.height ) - 1;
5704 clippedcells.Subtract( rect );
5705 }
5706 }
5707 }
5708
5709 dc.SetDeviceClippingRegion( clippedcells );
5710
5711 DoDrawGridLines(dc,
5712 top, left, top + height, left + width,
5713 topLeft.GetRow(), topLeft.GetCol(),
5714 bottomRight.GetRow(), bottomRight.GetCol());
5715
5716 dc.DestroyClippingRegion();
5717 }
5718
5719 // This is used to redraw all grid lines e.g. when the grid line colour
5720 // has been changed
5721 //
5722 void wxGrid::DrawAllGridLines( wxDC& dc, const wxRegion & WXUNUSED(reg) )
5723 {
5724 if ( !m_gridLinesEnabled )
5725 return;
5726
5727 int top, bottom, left, right;
5728
5729 int cw, ch;
5730 m_gridWin->GetClientSize(&cw, &ch);
5731 CalcUnscrolledPosition( 0, 0, &left, &top );
5732 CalcUnscrolledPosition( cw, ch, &right, &bottom );
5733
5734 // avoid drawing grid lines past the last row and col
5735 if ( m_gridLinesClipHorz )
5736 {
5737 if ( !m_numCols )
5738 return;
5739
5740 const int lastColRight = GetColRight(GetColAt(m_numCols - 1));
5741 if ( right > lastColRight )
5742 right = lastColRight;
5743 }
5744
5745 if ( m_gridLinesClipVert )
5746 {
5747 if ( !m_numRows )
5748 return;
5749
5750 const int lastRowBottom = GetRowBottom(m_numRows - 1);
5751 if ( bottom > lastRowBottom )
5752 bottom = lastRowBottom;
5753 }
5754
5755 // no gridlines inside multicells, clip them out
5756 int leftCol = GetColPos( internalXToCol(left) );
5757 int topRow = internalYToRow(top);
5758 int rightCol = GetColPos( internalXToCol(right) );
5759 int bottomRow = internalYToRow(bottom);
5760
5761 wxRegion clippedcells(0, 0, cw, ch);
5762
5763 int cell_rows, cell_cols;
5764 wxRect rect;
5765
5766 for ( int j = topRow; j <= bottomRow; j++ )
5767 {
5768 for ( int colPos = leftCol; colPos <= rightCol; colPos++ )
5769 {
5770 int i = GetColAt( colPos );
5771
5772 GetCellSize( j, i, &cell_rows, &cell_cols );
5773 if ((cell_rows > 1) || (cell_cols > 1))
5774 {
5775 rect = CellToRect(j,i);
5776 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
5777 clippedcells.Subtract(rect);
5778 }
5779 else if ((cell_rows < 0) || (cell_cols < 0))
5780 {
5781 rect = CellToRect(j + cell_rows, i + cell_cols);
5782 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
5783 clippedcells.Subtract(rect);
5784 }
5785 }
5786 }
5787
5788 dc.SetDeviceClippingRegion( clippedcells );
5789
5790 DoDrawGridLines(dc,
5791 top, left, bottom, right,
5792 topRow, leftCol, m_numRows, m_numCols);
5793
5794 dc.DestroyClippingRegion();
5795 }
5796
5797 void
5798 wxGrid::DoDrawGridLines(wxDC& dc,
5799 int top, int left,
5800 int bottom, int right,
5801 int topRow, int leftCol,
5802 int bottomRow, int rightCol)
5803 {
5804 // horizontal grid lines
5805 for ( int i = topRow; i < bottomRow; i++ )
5806 {
5807 int bot = GetRowBottom(i) - 1;
5808
5809 if ( bot > bottom )
5810 break;
5811
5812 if ( bot >= top )
5813 {
5814 dc.SetPen( GetRowGridLinePen(i) );
5815 dc.DrawLine( left, bot, right, bot );
5816 }
5817 }
5818
5819 // vertical grid lines
5820 for ( int colPos = leftCol; colPos < rightCol; colPos++ )
5821 {
5822 int i = GetColAt( colPos );
5823
5824 int colRight = GetColRight(i);
5825 #ifdef __WXGTK__
5826 if (GetLayoutDirection() != wxLayout_RightToLeft)
5827 #endif
5828 colRight--;
5829
5830 if ( colRight > right )
5831 break;
5832
5833 if ( colRight >= left )
5834 {
5835 dc.SetPen( GetColGridLinePen(i) );
5836 dc.DrawLine( colRight, top, colRight, bottom );
5837 }
5838 }
5839 }
5840
5841 void wxGrid::DrawRowLabels( wxDC& dc, const wxArrayInt& rows)
5842 {
5843 if ( !m_numRows )
5844 return;
5845
5846 const size_t numLabels = rows.GetCount();
5847 for ( size_t i = 0; i < numLabels; i++ )
5848 {
5849 DrawRowLabel( dc, rows[i] );
5850 }
5851 }
5852
5853 void wxGrid::DrawRowLabel( wxDC& dc, int row )
5854 {
5855 if ( GetRowHeight(row) <= 0 || m_rowLabelWidth <= 0 )
5856 return;
5857
5858 wxGridCellAttrProvider * const
5859 attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
5860
5861 // notice that an explicit static_cast is needed to avoid a compilation
5862 // error with VC7.1 which, for some reason, tries to instantiate (abstract)
5863 // wxGridRowHeaderRenderer class without it
5864 const wxGridRowHeaderRenderer&
5865 rend = attrProvider ? attrProvider->GetRowHeaderRenderer(row)
5866 : static_cast<const wxGridRowHeaderRenderer&>
5867 (gs_defaultHeaderRenderers.rowRenderer);
5868
5869 wxRect rect(0, GetRowTop(row), m_rowLabelWidth, GetRowHeight(row));
5870 rend.DrawBorder(*this, dc, rect);
5871
5872 int hAlign, vAlign;
5873 GetRowLabelAlignment(&hAlign, &vAlign);
5874
5875 rend.DrawLabel(*this, dc, GetRowLabelValue(row),
5876 rect, hAlign, vAlign, wxHORIZONTAL);
5877 }
5878
5879 void wxGrid::UseNativeColHeader(bool native)
5880 {
5881 if ( native == m_useNativeHeader )
5882 return;
5883
5884 delete m_colWindow;
5885 m_useNativeHeader = native;
5886
5887 CreateColumnWindow();
5888
5889 if ( m_useNativeHeader )
5890 GetGridColHeader()->SetColumnCount(m_numCols);
5891 CalcWindowSizes();
5892 }
5893
5894 void wxGrid::SetUseNativeColLabels( bool native )
5895 {
5896 wxASSERT_MSG( !m_useNativeHeader,
5897 "doesn't make sense when using native header" );
5898
5899 m_nativeColumnLabels = native;
5900 if (native)
5901 {
5902 int height = wxRendererNative::Get().GetHeaderButtonHeight( this );
5903 SetColLabelSize( height );
5904 }
5905
5906 GetColLabelWindow()->Refresh();
5907 m_cornerLabelWin->Refresh();
5908 }
5909
5910 void wxGrid::DrawColLabels( wxDC& dc,const wxArrayInt& cols )
5911 {
5912 if ( !m_numCols )
5913 return;
5914
5915 const size_t numLabels = cols.GetCount();
5916 for ( size_t i = 0; i < numLabels; i++ )
5917 {
5918 DrawColLabel( dc, cols[i] );
5919 }
5920 }
5921
5922 void wxGrid::DrawCornerLabel(wxDC& dc)
5923 {
5924 wxRect rect(wxSize(m_rowLabelWidth, m_colLabelHeight));
5925
5926 if ( m_nativeColumnLabels )
5927 {
5928 rect.Deflate(1);
5929
5930 wxRendererNative::Get().DrawHeaderButton(m_cornerLabelWin, dc, rect, 0);
5931 }
5932 else
5933 {
5934 rect.width++;
5935 rect.height++;
5936
5937 wxGridCellAttrProvider * const
5938 attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
5939 const wxGridCornerHeaderRenderer&
5940 rend = attrProvider ? attrProvider->GetCornerRenderer()
5941 : static_cast<wxGridCornerHeaderRenderer&>
5942 (gs_defaultHeaderRenderers.cornerRenderer);
5943
5944 rend.DrawBorder(*this, dc, rect);
5945 }
5946 }
5947
5948 void wxGrid::DrawColLabel(wxDC& dc, int col)
5949 {
5950 if ( GetColWidth(col) <= 0 || m_colLabelHeight <= 0 )
5951 return;
5952
5953 int colLeft = GetColLeft(col);
5954
5955 wxRect rect(colLeft, 0, GetColWidth(col), m_colLabelHeight);
5956 wxGridCellAttrProvider * const
5957 attrProvider = m_table ? m_table->GetAttrProvider() : NULL;
5958 const wxGridColumnHeaderRenderer&
5959 rend = attrProvider ? attrProvider->GetColumnHeaderRenderer(col)
5960 : static_cast<wxGridColumnHeaderRenderer&>
5961 (gs_defaultHeaderRenderers.colRenderer);
5962
5963 if ( m_nativeColumnLabels )
5964 {
5965 wxRendererNative::Get().DrawHeaderButton
5966 (
5967 GetColLabelWindow(),
5968 dc,
5969 rect,
5970 0,
5971 IsSortingBy(col)
5972 ? IsSortOrderAscending()
5973 ? wxHDR_SORT_ICON_UP
5974 : wxHDR_SORT_ICON_DOWN
5975 : wxHDR_SORT_ICON_NONE
5976 );
5977 rect.Deflate(2);
5978 }
5979 else
5980 {
5981 // It is reported that we need to erase the background to avoid display
5982 // artefacts, see #12055.
5983 wxDCBrushChanger setBrush(dc, m_colWindow->GetBackgroundColour());
5984 dc.DrawRectangle(rect);
5985
5986 rend.DrawBorder(*this, dc, rect);
5987 }
5988
5989 int hAlign, vAlign;
5990 GetColLabelAlignment(&hAlign, &vAlign);
5991 const int orient = GetColLabelTextOrientation();
5992
5993 rend.DrawLabel(*this, dc, GetColLabelValue(col), rect, hAlign, vAlign, orient);
5994 }
5995
5996 // TODO: these 2 functions should be replaced with wxDC::DrawLabel() to which
5997 // we just have to add textOrientation support
5998 void wxGrid::DrawTextRectangle( wxDC& dc,
5999 const wxString& value,
6000 const wxRect& rect,
6001 int horizAlign,
6002 int vertAlign,
6003 int textOrientation ) const
6004 {
6005 wxArrayString lines;
6006
6007 StringToLines( value, lines );
6008
6009 DrawTextRectangle(dc, lines, rect, horizAlign, vertAlign, textOrientation);
6010 }
6011
6012 void wxGrid::DrawTextRectangle(wxDC& dc,
6013 const wxArrayString& lines,
6014 const wxRect& rect,
6015 int horizAlign,
6016 int vertAlign,
6017 int textOrientation) const
6018 {
6019 if ( lines.empty() )
6020 return;
6021
6022 wxDCClipper clip(dc, rect);
6023
6024 long textWidth,
6025 textHeight;
6026
6027 if ( textOrientation == wxHORIZONTAL )
6028 GetTextBoxSize( dc, lines, &textWidth, &textHeight );
6029 else
6030 GetTextBoxSize( dc, lines, &textHeight, &textWidth );
6031
6032 int x = 0,
6033 y = 0;
6034 switch ( vertAlign )
6035 {
6036 case wxALIGN_BOTTOM:
6037 if ( textOrientation == wxHORIZONTAL )
6038 y = rect.y + (rect.height - textHeight - 1);
6039 else
6040 x = rect.x + rect.width - textWidth;
6041 break;
6042
6043 case wxALIGN_CENTRE:
6044 if ( textOrientation == wxHORIZONTAL )
6045 y = rect.y + ((rect.height - textHeight) / 2);
6046 else
6047 x = rect.x + ((rect.width - textWidth) / 2);
6048 break;
6049
6050 case wxALIGN_TOP:
6051 default:
6052 if ( textOrientation == wxHORIZONTAL )
6053 y = rect.y + 1;
6054 else
6055 x = rect.x + 1;
6056 break;
6057 }
6058
6059 // Align each line of a multi-line label
6060 size_t nLines = lines.GetCount();
6061 for ( size_t l = 0; l < nLines; l++ )
6062 {
6063 const wxString& line = lines[l];
6064
6065 if ( line.empty() )
6066 {
6067 *(textOrientation == wxHORIZONTAL ? &y : &x) += dc.GetCharHeight();
6068 continue;
6069 }
6070
6071 wxCoord lineWidth = 0,
6072 lineHeight = 0;
6073 dc.GetTextExtent(line, &lineWidth, &lineHeight);
6074
6075 switch ( horizAlign )
6076 {
6077 case wxALIGN_RIGHT:
6078 if ( textOrientation == wxHORIZONTAL )
6079 x = rect.x + (rect.width - lineWidth - 1);
6080 else
6081 y = rect.y + lineWidth + 1;
6082 break;
6083
6084 case wxALIGN_CENTRE:
6085 if ( textOrientation == wxHORIZONTAL )
6086 x = rect.x + ((rect.width - lineWidth) / 2);
6087 else
6088 y = rect.y + rect.height - ((rect.height - lineWidth) / 2);
6089 break;
6090
6091 case wxALIGN_LEFT:
6092 default:
6093 if ( textOrientation == wxHORIZONTAL )
6094 x = rect.x + 1;
6095 else
6096 y = rect.y + rect.height - 1;
6097 break;
6098 }
6099
6100 if ( textOrientation == wxHORIZONTAL )
6101 {
6102 dc.DrawText( line, x, y );
6103 y += lineHeight;
6104 }
6105 else
6106 {
6107 dc.DrawRotatedText( line, x, y, 90.0 );
6108 x += lineHeight;
6109 }
6110 }
6111 }
6112
6113 // Split multi-line text up into an array of strings.
6114 // Any existing contents of the string array are preserved.
6115 //
6116 // TODO: refactor wxTextFile::Read() and reuse the same code from here
6117 void wxGrid::StringToLines( const wxString& value, wxArrayString& lines ) const
6118 {
6119 int startPos = 0;
6120 int pos;
6121 wxString eol = wxTextFile::GetEOL( wxTextFileType_Unix );
6122 wxString tVal = wxTextFile::Translate( value, wxTextFileType_Unix );
6123
6124 while ( startPos < (int)tVal.length() )
6125 {
6126 pos = tVal.Mid(startPos).Find( eol );
6127 if ( pos < 0 )
6128 {
6129 break;
6130 }
6131 else if ( pos == 0 )
6132 {
6133 lines.Add( wxEmptyString );
6134 }
6135 else
6136 {
6137 lines.Add( tVal.Mid(startPos, pos) );
6138 }
6139
6140 startPos += pos + 1;
6141 }
6142
6143 if ( startPos < (int)tVal.length() )
6144 {
6145 lines.Add( tVal.Mid( startPos ) );
6146 }
6147 }
6148
6149 void wxGrid::GetTextBoxSize( const wxDC& dc,
6150 const wxArrayString& lines,
6151 long *width, long *height ) const
6152 {
6153 wxCoord w = 0;
6154 wxCoord h = 0;
6155 wxCoord lineW = 0, lineH = 0;
6156
6157 size_t i;
6158 for ( i = 0; i < lines.GetCount(); i++ )
6159 {
6160 dc.GetTextExtent( lines[i], &lineW, &lineH );
6161 w = wxMax( w, lineW );
6162 h += lineH;
6163 }
6164
6165 *width = w;
6166 *height = h;
6167 }
6168
6169 //
6170 // ------ Batch processing.
6171 //
6172 void wxGrid::EndBatch()
6173 {
6174 if ( m_batchCount > 0 )
6175 {
6176 m_batchCount--;
6177 if ( !m_batchCount )
6178 {
6179 CalcDimensions();
6180 m_rowLabelWin->Refresh();
6181 m_colWindow->Refresh();
6182 m_cornerLabelWin->Refresh();
6183 m_gridWin->Refresh();
6184 }
6185 }
6186 }
6187
6188 // Use this, rather than wxWindow::Refresh(), to force an immediate
6189 // repainting of the grid. Has no effect if you are already inside a
6190 // BeginBatch / EndBatch block.
6191 //
6192 void wxGrid::ForceRefresh()
6193 {
6194 BeginBatch();
6195 EndBatch();
6196 }
6197
6198 bool wxGrid::Enable(bool enable)
6199 {
6200 if ( !wxScrolledWindow::Enable(enable) )
6201 return false;
6202
6203 // redraw in the new state
6204 m_gridWin->Refresh();
6205
6206 return true;
6207 }
6208
6209 //
6210 // ------ Edit control functions
6211 //
6212
6213 void wxGrid::EnableEditing( bool edit )
6214 {
6215 if ( edit != m_editable )
6216 {
6217 if (!edit)
6218 EnableCellEditControl(edit);
6219 m_editable = edit;
6220 }
6221 }
6222
6223 void wxGrid::EnableCellEditControl( bool enable )
6224 {
6225 if (! m_editable)
6226 return;
6227
6228 if ( enable != m_cellEditCtrlEnabled )
6229 {
6230 if ( enable )
6231 {
6232 if ( SendEvent(wxEVT_GRID_EDITOR_SHOWN) == -1 )
6233 return;
6234
6235 // this should be checked by the caller!
6236 wxASSERT_MSG( CanEnableCellControl(), wxT("can't enable editing for this cell!") );
6237
6238 // do it before ShowCellEditControl()
6239 m_cellEditCtrlEnabled = enable;
6240
6241 ShowCellEditControl();
6242 }
6243 else
6244 {
6245 SendEvent(wxEVT_GRID_EDITOR_HIDDEN);
6246
6247 HideCellEditControl();
6248 SaveEditControlValue();
6249
6250 // do it after HideCellEditControl()
6251 m_cellEditCtrlEnabled = enable;
6252 }
6253 }
6254 }
6255
6256 bool wxGrid::IsCurrentCellReadOnly() const
6257 {
6258 wxGridCellAttr*
6259 attr = const_cast<wxGrid *>(this)->GetCellAttr(m_currentCellCoords);
6260 bool readonly = attr->IsReadOnly();
6261 attr->DecRef();
6262
6263 return readonly;
6264 }
6265
6266 bool wxGrid::CanEnableCellControl() const
6267 {
6268 return m_editable && (m_currentCellCoords != wxGridNoCellCoords) &&
6269 !IsCurrentCellReadOnly();
6270 }
6271
6272 bool wxGrid::IsCellEditControlEnabled() const
6273 {
6274 // the cell edit control might be disable for all cells or just for the
6275 // current one if it's read only
6276 return m_cellEditCtrlEnabled ? !IsCurrentCellReadOnly() : false;
6277 }
6278
6279 bool wxGrid::IsCellEditControlShown() const
6280 {
6281 bool isShown = false;
6282
6283 if ( m_cellEditCtrlEnabled )
6284 {
6285 int row = m_currentCellCoords.GetRow();
6286 int col = m_currentCellCoords.GetCol();
6287 wxGridCellAttr* attr = GetCellAttr(row, col);
6288 wxGridCellEditor* editor = attr->GetEditor((wxGrid*) this, row, col);
6289 attr->DecRef();
6290
6291 if ( editor )
6292 {
6293 if ( editor->IsCreated() )
6294 {
6295 isShown = editor->GetControl()->IsShown();
6296 }
6297
6298 editor->DecRef();
6299 }
6300 }
6301
6302 return isShown;
6303 }
6304
6305 void wxGrid::ShowCellEditControl()
6306 {
6307 if ( IsCellEditControlEnabled() )
6308 {
6309 if ( !IsVisible( m_currentCellCoords, false ) )
6310 {
6311 m_cellEditCtrlEnabled = false;
6312 return;
6313 }
6314 else
6315 {
6316 wxRect rect = CellToRect( m_currentCellCoords );
6317 int row = m_currentCellCoords.GetRow();
6318 int col = m_currentCellCoords.GetCol();
6319
6320 // if this is part of a multicell, find owner (topleft)
6321 int cell_rows, cell_cols;
6322 GetCellSize( row, col, &cell_rows, &cell_cols );
6323 if ( cell_rows <= 0 || cell_cols <= 0 )
6324 {
6325 row += cell_rows;
6326 col += cell_cols;
6327 m_currentCellCoords.SetRow( row );
6328 m_currentCellCoords.SetCol( col );
6329 }
6330
6331 // erase the highlight and the cell contents because the editor
6332 // might not cover the entire cell
6333 wxClientDC dc( m_gridWin );
6334 PrepareDC( dc );
6335 wxGridCellAttr* attr = GetCellAttr(row, col);
6336 dc.SetBrush(wxBrush(attr->GetBackgroundColour()));
6337 dc.SetPen(*wxTRANSPARENT_PEN);
6338 dc.DrawRectangle(rect);
6339
6340 // convert to scrolled coords
6341 CalcScrolledPosition( rect.x, rect.y, &rect.x, &rect.y );
6342
6343 int nXMove = 0;
6344 if (rect.x < 0)
6345 nXMove = rect.x;
6346
6347 // cell is shifted by one pixel
6348 // However, don't allow x or y to become negative
6349 // since the SetSize() method interprets that as
6350 // "don't change."
6351 if (rect.x > 0)
6352 rect.x--;
6353 if (rect.y > 0)
6354 rect.y--;
6355
6356 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
6357 if ( !editor->IsCreated() )
6358 {
6359 editor->Create(m_gridWin, wxID_ANY,
6360 new wxGridCellEditorEvtHandler(this, editor));
6361
6362 wxGridEditorCreatedEvent evt(GetId(),
6363 wxEVT_GRID_EDITOR_CREATED,
6364 this,
6365 row,
6366 col,
6367 editor->GetControl());
6368 GetEventHandler()->ProcessEvent(evt);
6369 }
6370
6371 // resize editor to overflow into righthand cells if allowed
6372 int maxWidth = rect.width;
6373 wxString value = GetCellValue(row, col);
6374 if ( (value != wxEmptyString) && (attr->GetOverflow()) )
6375 {
6376 int y;
6377 GetTextExtent(value, &maxWidth, &y, NULL, NULL, &attr->GetFont());
6378 if (maxWidth < rect.width)
6379 maxWidth = rect.width;
6380 }
6381
6382 int client_right = m_gridWin->GetClientSize().GetWidth();
6383 if (rect.x + maxWidth > client_right)
6384 maxWidth = client_right - rect.x;
6385
6386 if ((maxWidth > rect.width) && (col < m_numCols) && m_table)
6387 {
6388 GetCellSize( row, col, &cell_rows, &cell_cols );
6389 // may have changed earlier
6390 for (int i = col + cell_cols; i < m_numCols; i++)
6391 {
6392 int c_rows, c_cols;
6393 GetCellSize( row, i, &c_rows, &c_cols );
6394
6395 // looks weird going over a multicell
6396 if (m_table->IsEmptyCell( row, i ) &&
6397 (rect.width < maxWidth) && (c_rows == 1))
6398 {
6399 rect.width += GetColWidth( i );
6400 }
6401 else
6402 break;
6403 }
6404
6405 if (rect.GetRight() > client_right)
6406 rect.SetRight( client_right - 1 );
6407 }
6408
6409 editor->SetCellAttr( attr );
6410 editor->SetSize( rect );
6411 if (nXMove != 0)
6412 editor->GetControl()->Move(
6413 editor->GetControl()->GetPosition().x + nXMove,
6414 editor->GetControl()->GetPosition().y );
6415 editor->Show( true, attr );
6416
6417 // recalc dimensions in case we need to
6418 // expand the scrolled window to account for editor
6419 CalcDimensions();
6420
6421 editor->BeginEdit(row, col, this);
6422 editor->SetCellAttr(NULL);
6423
6424 editor->DecRef();
6425 attr->DecRef();
6426 }
6427 }
6428 }
6429
6430 void wxGrid::HideCellEditControl()
6431 {
6432 if ( IsCellEditControlEnabled() )
6433 {
6434 int row = m_currentCellCoords.GetRow();
6435 int col = m_currentCellCoords.GetCol();
6436
6437 wxGridCellAttr *attr = GetCellAttr(row, col);
6438 wxGridCellEditor *editor = attr->GetEditor(this, row, col);
6439 const bool editorHadFocus = editor->GetControl()->HasFocus();
6440 editor->Show( false );
6441 editor->DecRef();
6442 attr->DecRef();
6443
6444 // return the focus to the grid itself if the editor had it
6445 //
6446 // note that we must not do this unconditionally to avoid stealing
6447 // focus from the window which just received it if we are hiding the
6448 // editor precisely because we lost focus
6449 if ( editorHadFocus )
6450 m_gridWin->SetFocus();
6451
6452 // refresh whole row to the right
6453 wxRect rect( CellToRect(row, col) );
6454 CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y );
6455 rect.width = m_gridWin->GetClientSize().GetWidth() - rect.x;
6456
6457 #ifdef __WXMAC__
6458 // ensure that the pixels under the focus ring get refreshed as well
6459 rect.Inflate(10, 10);
6460 #endif
6461
6462 m_gridWin->Refresh( false, &rect );
6463 }
6464 }
6465
6466 void wxGrid::SaveEditControlValue()
6467 {
6468 if ( IsCellEditControlEnabled() )
6469 {
6470 int row = m_currentCellCoords.GetRow();
6471 int col = m_currentCellCoords.GetCol();
6472
6473 wxString oldval = GetCellValue(row, col);
6474
6475 wxGridCellAttr* attr = GetCellAttr(row, col);
6476 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
6477
6478 wxString newval;
6479 bool changed = editor->EndEdit(row, col, this, oldval, &newval);
6480
6481 if ( changed && SendEvent(wxEVT_GRID_CELL_CHANGING, newval) != -1 )
6482 {
6483 editor->ApplyEdit(row, col, this);
6484
6485 // for compatibility reasons dating back to wx 2.8 when this event
6486 // was called wxEVT_GRID_CELL_CHANGE and wxEVT_GRID_CELL_CHANGING
6487 // didn't exist we allow vetoing this one too
6488 if ( SendEvent(wxEVT_GRID_CELL_CHANGED, oldval) == -1 )
6489 {
6490 // Event has been vetoed, set the data back.
6491 SetCellValue(row, col, oldval);
6492 }
6493 }
6494
6495 editor->DecRef();
6496 attr->DecRef();
6497 }
6498 }
6499
6500 //
6501 // ------ Grid location functions
6502 // Note that all of these functions work with the logical coordinates of
6503 // grid cells and labels so you will need to convert from device
6504 // coordinates for mouse events etc.
6505 //
6506
6507 wxGridCellCoords wxGrid::XYToCell(int x, int y) const
6508 {
6509 int row = YToRow(y);
6510 int col = XToCol(x);
6511
6512 return row == -1 || col == -1 ? wxGridNoCellCoords
6513 : wxGridCellCoords(row, col);
6514 }
6515
6516 // compute row or column from some (unscrolled) coordinate value, using either
6517 // m_defaultRowHeight/m_defaultColWidth or binary search on array of
6518 // m_rowBottoms/m_colRights to do it quickly in O(log n) time.
6519 // NOTE: This may not work correctly for reordered columns.
6520 int wxGrid::PosToLinePos(int coord,
6521 bool clipToMinMax,
6522 const wxGridOperations& oper) const
6523 {
6524 const int numLines = oper.GetNumberOfLines(this);
6525
6526 if ( coord < 0 )
6527 return clipToMinMax && numLines > 0 ? 0 : wxNOT_FOUND;
6528
6529 const int defaultLineSize = oper.GetDefaultLineSize(this);
6530 wxCHECK_MSG( defaultLineSize, -1, "can't have 0 default line size" );
6531
6532 int maxPos = coord / defaultLineSize,
6533 minPos = 0;
6534
6535 // check for the simplest case: if we have no explicit line sizes
6536 // configured, then we already know the line this position falls in
6537 const wxArrayInt& lineEnds = oper.GetLineEnds(this);
6538 if ( lineEnds.empty() )
6539 {
6540 if ( maxPos < numLines )
6541 return maxPos;
6542
6543 return clipToMinMax ? numLines - 1 : -1;
6544 }
6545
6546
6547 // binary search is quite efficient and we can't really make any assumptions
6548 // on where to start here since row and columns could be of size 0 if they are
6549 // hidden. While this could be made more efficient, some profiling will be
6550 // necessary to determine if it really is a performance bottleneck
6551 maxPos = numLines - 1;
6552
6553 // check if the position is beyond the last column
6554 const int lineAtMaxPos = oper.GetLineAt(this, maxPos);
6555 if ( coord >= lineEnds[lineAtMaxPos] )
6556 return clipToMinMax ? maxPos : -1;
6557
6558 // or before the first one
6559 const int lineAt0 = oper.GetLineAt(this, 0);
6560 if ( coord < lineEnds[lineAt0] )
6561 return 0;
6562
6563
6564 // finally do perform the binary search
6565 while ( minPos < maxPos )
6566 {
6567 wxCHECK_MSG( lineEnds[oper.GetLineAt(this, minPos)] <= coord &&
6568 coord < lineEnds[oper.GetLineAt(this, maxPos)],
6569 -1,
6570 "wxGrid: internal error in PosToLinePos()" );
6571
6572 if ( coord >= lineEnds[oper.GetLineAt(this, maxPos - 1)] )
6573 return maxPos;
6574 else
6575 maxPos--;
6576
6577 const int median = minPos + (maxPos - minPos + 1) / 2;
6578 if ( coord < lineEnds[oper.GetLineAt(this, median)] )
6579 maxPos = median;
6580 else
6581 minPos = median;
6582 }
6583
6584 return maxPos;
6585 }
6586
6587 int
6588 wxGrid::PosToLine(int coord,
6589 bool clipToMinMax,
6590 const wxGridOperations& oper) const
6591 {
6592 int pos = PosToLinePos(coord, clipToMinMax, oper);
6593
6594 return pos == wxNOT_FOUND ? wxNOT_FOUND : oper.GetLineAt(this, pos);
6595 }
6596
6597 int wxGrid::YToRow(int y, bool clipToMinMax) const
6598 {
6599 return PosToLine(y, clipToMinMax, wxGridRowOperations());
6600 }
6601
6602 int wxGrid::XToCol(int x, bool clipToMinMax) const
6603 {
6604 return PosToLine(x, clipToMinMax, wxGridColumnOperations());
6605 }
6606
6607 int wxGrid::XToPos(int x) const
6608 {
6609 return PosToLinePos(x, true /* clip */, wxGridColumnOperations());
6610 }
6611
6612 // return the row number such that the y coord is near the edge of, or -1 if
6613 // not near an edge.
6614 //
6615 // notice that position can only possibly be near an edge if the row/column is
6616 // large enough to still allow for an "inner" area that is _not_ near the edge
6617 // (i.e., if the height/width is smaller than WXGRID_LABEL_EDGE_ZONE, pos will
6618 // _never_ be considered to be near the edge).
6619 int wxGrid::PosToEdgeOfLine(int pos, const wxGridOperations& oper) const
6620 {
6621 const int line = oper.PosToLine(this, pos, true);
6622
6623 if ( oper.GetLineSize(this, line) > WXGRID_LABEL_EDGE_ZONE )
6624 {
6625 // We know that we are in this line, test whether we are close enough
6626 // to start or end border, respectively.
6627 if ( abs(oper.GetLineEndPos(this, line) - pos) < WXGRID_LABEL_EDGE_ZONE )
6628 return line;
6629 else if ( line > 0 &&
6630 pos - oper.GetLineStartPos(this,
6631 line) < WXGRID_LABEL_EDGE_ZONE )
6632 {
6633 return oper.GetLineBefore(this, line);
6634 }
6635 }
6636
6637 return -1;
6638 }
6639
6640 int wxGrid::YToEdgeOfRow(int y) const
6641 {
6642 return PosToEdgeOfLine(y, wxGridRowOperations());
6643 }
6644
6645 int wxGrid::XToEdgeOfCol(int x) const
6646 {
6647 return PosToEdgeOfLine(x, wxGridColumnOperations());
6648 }
6649
6650 wxRect wxGrid::CellToRect( int row, int col ) const
6651 {
6652 wxRect rect( -1, -1, -1, -1 );
6653
6654 if ( row >= 0 && row < m_numRows &&
6655 col >= 0 && col < m_numCols )
6656 {
6657 int i, cell_rows, cell_cols;
6658 rect.width = rect.height = 0;
6659 GetCellSize( row, col, &cell_rows, &cell_cols );
6660 // if negative then find multicell owner
6661 if (cell_rows < 0)
6662 row += cell_rows;
6663 if (cell_cols < 0)
6664 col += cell_cols;
6665 GetCellSize( row, col, &cell_rows, &cell_cols );
6666
6667 rect.x = GetColLeft(col);
6668 rect.y = GetRowTop(row);
6669 for (i=col; i < col + cell_cols; i++)
6670 rect.width += GetColWidth(i);
6671 for (i=row; i < row + cell_rows; i++)
6672 rect.height += GetRowHeight(i);
6673
6674 // if grid lines are enabled, then the area of the cell is a bit smaller
6675 if (m_gridLinesEnabled)
6676 {
6677 rect.width -= 1;
6678 rect.height -= 1;
6679 }
6680 }
6681
6682 return rect;
6683 }
6684
6685 bool wxGrid::IsVisible( int row, int col, bool wholeCellVisible ) const
6686 {
6687 // get the cell rectangle in logical coords
6688 //
6689 wxRect r( CellToRect( row, col ) );
6690
6691 // convert to device coords
6692 //
6693 int left, top, right, bottom;
6694 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
6695 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
6696
6697 // check against the client area of the grid window
6698 int cw, ch;
6699 m_gridWin->GetClientSize( &cw, &ch );
6700
6701 if ( wholeCellVisible )
6702 {
6703 // is the cell wholly visible ?
6704 return ( left >= 0 && right <= cw &&
6705 top >= 0 && bottom <= ch );
6706 }
6707 else
6708 {
6709 // is the cell partly visible ?
6710 //
6711 return ( ((left >= 0 && left < cw) || (right > 0 && right <= cw)) &&
6712 ((top >= 0 && top < ch) || (bottom > 0 && bottom <= ch)) );
6713 }
6714 }
6715
6716 // make the specified cell location visible by doing a minimal amount
6717 // of scrolling
6718 //
6719 void wxGrid::MakeCellVisible( int row, int col )
6720 {
6721 int i;
6722 int xpos = -1, ypos = -1;
6723
6724 if ( row >= 0 && row < m_numRows &&
6725 col >= 0 && col < m_numCols )
6726 {
6727 // get the cell rectangle in logical coords
6728 wxRect r( CellToRect( row, col ) );
6729
6730 // convert to device coords
6731 int left, top, right, bottom;
6732 CalcScrolledPosition( r.GetLeft(), r.GetTop(), &left, &top );
6733 CalcScrolledPosition( r.GetRight(), r.GetBottom(), &right, &bottom );
6734
6735 int cw, ch;
6736 m_gridWin->GetClientSize( &cw, &ch );
6737
6738 if ( top < 0 )
6739 {
6740 ypos = r.GetTop();
6741 }
6742 else if ( bottom > ch )
6743 {
6744 int h = r.GetHeight();
6745 ypos = r.GetTop();
6746 for ( i = row - 1; i >= 0; i-- )
6747 {
6748 int rowHeight = GetRowHeight(i);
6749 if ( h + rowHeight > ch )
6750 break;
6751
6752 h += rowHeight;
6753 ypos -= rowHeight;
6754 }
6755
6756 // we divide it later by GRID_SCROLL_LINE, make sure that we don't
6757 // have rounding errors (this is important, because if we do,
6758 // we might not scroll at all and some cells won't be redrawn)
6759 //
6760 // Sometimes GRID_SCROLL_LINE / 2 is not enough,
6761 // so just add a full scroll unit...
6762 ypos += m_yScrollPixelsPerLine;
6763 }
6764
6765 // special handling for wide cells - show always left part of the cell!
6766 // Otherwise, e.g. when stepping from row to row, it would jump between
6767 // left and right part of the cell on every step!
6768 // if ( left < 0 )
6769 if ( left < 0 || (right - left) >= cw )
6770 {
6771 xpos = r.GetLeft();
6772 }
6773 else if ( right > cw )
6774 {
6775 // position the view so that the cell is on the right
6776 int x0, y0;
6777 CalcUnscrolledPosition(0, 0, &x0, &y0);
6778 xpos = x0 + (right - cw);
6779
6780 // see comment for ypos above
6781 xpos += m_xScrollPixelsPerLine;
6782 }
6783
6784 if ( xpos != -1 || ypos != -1 )
6785 {
6786 if ( xpos != -1 )
6787 xpos /= m_xScrollPixelsPerLine;
6788 if ( ypos != -1 )
6789 ypos /= m_yScrollPixelsPerLine;
6790 Scroll( xpos, ypos );
6791 AdjustScrollbars();
6792 }
6793 }
6794 }
6795
6796 //
6797 // ------ Grid cursor movement functions
6798 //
6799
6800 bool
6801 wxGrid::DoMoveCursor(bool expandSelection,
6802 const wxGridDirectionOperations& diroper)
6803 {
6804 if ( m_currentCellCoords == wxGridNoCellCoords )
6805 return false;
6806
6807 if ( expandSelection )
6808 {
6809 wxGridCellCoords coords = m_selectedBlockCorner;
6810 if ( coords == wxGridNoCellCoords )
6811 coords = m_currentCellCoords;
6812
6813 if ( diroper.IsAtBoundary(coords) )
6814 return false;
6815
6816 diroper.Advance(coords);
6817
6818 UpdateBlockBeingSelected(m_currentCellCoords, coords);
6819 }
6820 else // don't expand selection
6821 {
6822 ClearSelection();
6823
6824 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6825 return false;
6826
6827 wxGridCellCoords coords = m_currentCellCoords;
6828 diroper.Advance(coords);
6829
6830 GoToCell(coords);
6831 }
6832
6833 return true;
6834 }
6835
6836 bool wxGrid::MoveCursorUp(bool expandSelection)
6837 {
6838 return DoMoveCursor(expandSelection,
6839 wxGridBackwardOperations(this, wxGridRowOperations()));
6840 }
6841
6842 bool wxGrid::MoveCursorDown(bool expandSelection)
6843 {
6844 return DoMoveCursor(expandSelection,
6845 wxGridForwardOperations(this, wxGridRowOperations()));
6846 }
6847
6848 bool wxGrid::MoveCursorLeft(bool expandSelection)
6849 {
6850 return DoMoveCursor(expandSelection,
6851 wxGridBackwardOperations(this, wxGridColumnOperations()));
6852 }
6853
6854 bool wxGrid::MoveCursorRight(bool expandSelection)
6855 {
6856 return DoMoveCursor(expandSelection,
6857 wxGridForwardOperations(this, wxGridColumnOperations()));
6858 }
6859
6860 bool wxGrid::DoMoveCursorByPage(const wxGridDirectionOperations& diroper)
6861 {
6862 if ( m_currentCellCoords == wxGridNoCellCoords )
6863 return false;
6864
6865 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6866 return false;
6867
6868 const int oldRow = m_currentCellCoords.GetRow();
6869 int newRow = diroper.MoveByPixelDistance(oldRow, m_gridWin->GetClientSize().y);
6870 if ( newRow == oldRow )
6871 {
6872 wxGridCellCoords coords(m_currentCellCoords);
6873 diroper.Advance(coords);
6874 newRow = coords.GetRow();
6875 }
6876
6877 GoToCell(newRow, m_currentCellCoords.GetCol());
6878
6879 return true;
6880 }
6881
6882 bool wxGrid::MovePageUp()
6883 {
6884 return DoMoveCursorByPage(
6885 wxGridBackwardOperations(this, wxGridRowOperations()));
6886 }
6887
6888 bool wxGrid::MovePageDown()
6889 {
6890 return DoMoveCursorByPage(
6891 wxGridForwardOperations(this, wxGridRowOperations()));
6892 }
6893
6894 // helper of DoMoveCursorByBlock(): advance the cell coordinates using diroper
6895 // until we find a non-empty cell or reach the grid end
6896 void
6897 wxGrid::AdvanceToNextNonEmpty(wxGridCellCoords& coords,
6898 const wxGridDirectionOperations& diroper)
6899 {
6900 while ( !diroper.IsAtBoundary(coords) )
6901 {
6902 diroper.Advance(coords);
6903 if ( !m_table->IsEmpty(coords) )
6904 break;
6905 }
6906 }
6907
6908 bool
6909 wxGrid::DoMoveCursorByBlock(bool expandSelection,
6910 const wxGridDirectionOperations& diroper)
6911 {
6912 if ( !m_table || m_currentCellCoords == wxGridNoCellCoords )
6913 return false;
6914
6915 if ( diroper.IsAtBoundary(m_currentCellCoords) )
6916 return false;
6917
6918 wxGridCellCoords coords(m_currentCellCoords);
6919 if ( m_table->IsEmpty(coords) )
6920 {
6921 // we are in an empty cell: find the next block of non-empty cells
6922 AdvanceToNextNonEmpty(coords, diroper);
6923 }
6924 else // current cell is not empty
6925 {
6926 diroper.Advance(coords);
6927 if ( m_table->IsEmpty(coords) )
6928 {
6929 // we started at the end of a block, find the next one
6930 AdvanceToNextNonEmpty(coords, diroper);
6931 }
6932 else // we're in a middle of a block
6933 {
6934 // go to the end of it, i.e. find the last cell before the next
6935 // empty one
6936 while ( !diroper.IsAtBoundary(coords) )
6937 {
6938 wxGridCellCoords coordsNext(coords);
6939 diroper.Advance(coordsNext);
6940 if ( m_table->IsEmpty(coordsNext) )
6941 break;
6942
6943 coords = coordsNext;
6944 }
6945 }
6946 }
6947
6948 if ( expandSelection )
6949 {
6950 UpdateBlockBeingSelected(m_currentCellCoords, coords);
6951 }
6952 else
6953 {
6954 ClearSelection();
6955 GoToCell(coords);
6956 }
6957
6958 return true;
6959 }
6960
6961 bool wxGrid::MoveCursorUpBlock(bool expandSelection)
6962 {
6963 return DoMoveCursorByBlock(
6964 expandSelection,
6965 wxGridBackwardOperations(this, wxGridRowOperations())
6966 );
6967 }
6968
6969 bool wxGrid::MoveCursorDownBlock( bool expandSelection )
6970 {
6971 return DoMoveCursorByBlock(
6972 expandSelection,
6973 wxGridForwardOperations(this, wxGridRowOperations())
6974 );
6975 }
6976
6977 bool wxGrid::MoveCursorLeftBlock( bool expandSelection )
6978 {
6979 return DoMoveCursorByBlock(
6980 expandSelection,
6981 wxGridBackwardOperations(this, wxGridColumnOperations())
6982 );
6983 }
6984
6985 bool wxGrid::MoveCursorRightBlock( bool expandSelection )
6986 {
6987 return DoMoveCursorByBlock(
6988 expandSelection,
6989 wxGridForwardOperations(this, wxGridColumnOperations())
6990 );
6991 }
6992
6993 //
6994 // ------ Label values and formatting
6995 //
6996
6997 void wxGrid::GetRowLabelAlignment( int *horiz, int *vert ) const
6998 {
6999 if ( horiz )
7000 *horiz = m_rowLabelHorizAlign;
7001 if ( vert )
7002 *vert = m_rowLabelVertAlign;
7003 }
7004
7005 void wxGrid::GetColLabelAlignment( int *horiz, int *vert ) const
7006 {
7007 if ( horiz )
7008 *horiz = m_colLabelHorizAlign;
7009 if ( vert )
7010 *vert = m_colLabelVertAlign;
7011 }
7012
7013 int wxGrid::GetColLabelTextOrientation() const
7014 {
7015 return m_colLabelTextOrientation;
7016 }
7017
7018 wxString wxGrid::GetRowLabelValue( int row ) const
7019 {
7020 if ( m_table )
7021 {
7022 return m_table->GetRowLabelValue( row );
7023 }
7024 else
7025 {
7026 wxString s;
7027 s << row;
7028 return s;
7029 }
7030 }
7031
7032 wxString wxGrid::GetColLabelValue( int col ) const
7033 {
7034 if ( m_table )
7035 {
7036 return m_table->GetColLabelValue( col );
7037 }
7038 else
7039 {
7040 wxString s;
7041 s << col;
7042 return s;
7043 }
7044 }
7045
7046 void wxGrid::SetRowLabelSize( int width )
7047 {
7048 wxASSERT( width >= 0 || width == wxGRID_AUTOSIZE );
7049
7050 if ( width == wxGRID_AUTOSIZE )
7051 {
7052 width = CalcColOrRowLabelAreaMinSize(wxGRID_ROW);
7053 }
7054
7055 if ( width != m_rowLabelWidth )
7056 {
7057 if ( width == 0 )
7058 {
7059 m_rowLabelWin->Show( false );
7060 m_cornerLabelWin->Show( false );
7061 }
7062 else if ( m_rowLabelWidth == 0 )
7063 {
7064 m_rowLabelWin->Show( true );
7065 if ( m_colLabelHeight > 0 )
7066 m_cornerLabelWin->Show( true );
7067 }
7068
7069 m_rowLabelWidth = width;
7070 CalcWindowSizes();
7071 wxScrolledWindow::Refresh( true );
7072 }
7073 }
7074
7075 void wxGrid::SetColLabelSize( int height )
7076 {
7077 wxASSERT( height >=0 || height == wxGRID_AUTOSIZE );
7078
7079 if ( height == wxGRID_AUTOSIZE )
7080 {
7081 height = CalcColOrRowLabelAreaMinSize(wxGRID_COLUMN);
7082 }
7083
7084 if ( height != m_colLabelHeight )
7085 {
7086 if ( height == 0 )
7087 {
7088 m_colWindow->Show( false );
7089 m_cornerLabelWin->Show( false );
7090 }
7091 else if ( m_colLabelHeight == 0 )
7092 {
7093 m_colWindow->Show( true );
7094 if ( m_rowLabelWidth > 0 )
7095 m_cornerLabelWin->Show( true );
7096 }
7097
7098 m_colLabelHeight = height;
7099 CalcWindowSizes();
7100 wxScrolledWindow::Refresh( true );
7101 }
7102 }
7103
7104 void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
7105 {
7106 if ( m_labelBackgroundColour != colour )
7107 {
7108 m_labelBackgroundColour = colour;
7109 m_rowLabelWin->SetBackgroundColour( colour );
7110 m_colWindow->SetBackgroundColour( colour );
7111 m_cornerLabelWin->SetBackgroundColour( colour );
7112
7113 if ( !GetBatchCount() )
7114 {
7115 m_rowLabelWin->Refresh();
7116 m_colWindow->Refresh();
7117 m_cornerLabelWin->Refresh();
7118 }
7119 }
7120 }
7121
7122 void wxGrid::SetLabelTextColour( const wxColour& colour )
7123 {
7124 if ( m_labelTextColour != colour )
7125 {
7126 m_labelTextColour = colour;
7127 if ( !GetBatchCount() )
7128 {
7129 m_rowLabelWin->Refresh();
7130 m_colWindow->Refresh();
7131 }
7132 }
7133 }
7134
7135 void wxGrid::SetLabelFont( const wxFont& font )
7136 {
7137 m_labelFont = font;
7138 if ( !GetBatchCount() )
7139 {
7140 m_rowLabelWin->Refresh();
7141 m_colWindow->Refresh();
7142 }
7143 }
7144
7145 void wxGrid::SetRowLabelAlignment( int horiz, int vert )
7146 {
7147 // allow old (incorrect) defs to be used
7148 switch ( horiz )
7149 {
7150 case wxLEFT: horiz = wxALIGN_LEFT; break;
7151 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
7152 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
7153 }
7154
7155 switch ( vert )
7156 {
7157 case wxTOP: vert = wxALIGN_TOP; break;
7158 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
7159 case wxCENTRE: vert = wxALIGN_CENTRE; break;
7160 }
7161
7162 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
7163 {
7164 m_rowLabelHorizAlign = horiz;
7165 }
7166
7167 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
7168 {
7169 m_rowLabelVertAlign = vert;
7170 }
7171
7172 if ( !GetBatchCount() )
7173 {
7174 m_rowLabelWin->Refresh();
7175 }
7176 }
7177
7178 void wxGrid::SetColLabelAlignment( int horiz, int vert )
7179 {
7180 // allow old (incorrect) defs to be used
7181 switch ( horiz )
7182 {
7183 case wxLEFT: horiz = wxALIGN_LEFT; break;
7184 case wxRIGHT: horiz = wxALIGN_RIGHT; break;
7185 case wxCENTRE: horiz = wxALIGN_CENTRE; break;
7186 }
7187
7188 switch ( vert )
7189 {
7190 case wxTOP: vert = wxALIGN_TOP; break;
7191 case wxBOTTOM: vert = wxALIGN_BOTTOM; break;
7192 case wxCENTRE: vert = wxALIGN_CENTRE; break;
7193 }
7194
7195 if ( horiz == wxALIGN_LEFT || horiz == wxALIGN_CENTRE || horiz == wxALIGN_RIGHT )
7196 {
7197 m_colLabelHorizAlign = horiz;
7198 }
7199
7200 if ( vert == wxALIGN_TOP || vert == wxALIGN_CENTRE || vert == wxALIGN_BOTTOM )
7201 {
7202 m_colLabelVertAlign = vert;
7203 }
7204
7205 if ( !GetBatchCount() )
7206 {
7207 m_colWindow->Refresh();
7208 }
7209 }
7210
7211 // Note: under MSW, the default column label font must be changed because it
7212 // does not support vertical printing
7213 //
7214 // Example: wxFont font(9, wxSWISS, wxNORMAL, wxBOLD);
7215 // pGrid->SetLabelFont(font);
7216 // pGrid->SetColLabelTextOrientation(wxVERTICAL);
7217 //
7218 void wxGrid::SetColLabelTextOrientation( int textOrientation )
7219 {
7220 if ( textOrientation == wxHORIZONTAL || textOrientation == wxVERTICAL )
7221 m_colLabelTextOrientation = textOrientation;
7222
7223 if ( !GetBatchCount() )
7224 m_colWindow->Refresh();
7225 }
7226
7227 void wxGrid::SetRowLabelValue( int row, const wxString& s )
7228 {
7229 if ( m_table )
7230 {
7231 m_table->SetRowLabelValue( row, s );
7232 if ( !GetBatchCount() )
7233 {
7234 wxRect rect = CellToRect( row, 0 );
7235 if ( rect.height > 0 )
7236 {
7237 CalcScrolledPosition(0, rect.y, &rect.x, &rect.y);
7238 rect.x = 0;
7239 rect.width = m_rowLabelWidth;
7240 m_rowLabelWin->Refresh( true, &rect );
7241 }
7242 }
7243 }
7244 }
7245
7246 void wxGrid::SetColLabelValue( int col, const wxString& s )
7247 {
7248 if ( m_table )
7249 {
7250 m_table->SetColLabelValue( col, s );
7251 if ( !GetBatchCount() )
7252 {
7253 if ( m_useNativeHeader )
7254 {
7255 GetGridColHeader()->UpdateColumn(col);
7256 }
7257 else
7258 {
7259 wxRect rect = CellToRect( 0, col );
7260 if ( rect.width > 0 )
7261 {
7262 CalcScrolledPosition(rect.x, 0, &rect.x, &rect.y);
7263 rect.y = 0;
7264 rect.height = m_colLabelHeight;
7265 GetColLabelWindow()->Refresh( true, &rect );
7266 }
7267 }
7268 }
7269 }
7270 }
7271
7272 void wxGrid::SetGridLineColour( const wxColour& colour )
7273 {
7274 if ( m_gridLineColour != colour )
7275 {
7276 m_gridLineColour = colour;
7277
7278 if ( GridLinesEnabled() )
7279 RedrawGridLines();
7280 }
7281 }
7282
7283 void wxGrid::SetCellHighlightColour( const wxColour& colour )
7284 {
7285 if ( m_cellHighlightColour != colour )
7286 {
7287 m_cellHighlightColour = colour;
7288
7289 wxClientDC dc( m_gridWin );
7290 PrepareDC( dc );
7291 wxGridCellAttr* attr = GetCellAttr(m_currentCellCoords);
7292 DrawCellHighlight(dc, attr);
7293 attr->DecRef();
7294 }
7295 }
7296
7297 void wxGrid::SetCellHighlightPenWidth(int width)
7298 {
7299 if (m_cellHighlightPenWidth != width)
7300 {
7301 m_cellHighlightPenWidth = width;
7302
7303 // Just redrawing the cell highlight is not enough since that won't
7304 // make any visible change if the thickness is getting smaller.
7305 int row = m_currentCellCoords.GetRow();
7306 int col = m_currentCellCoords.GetCol();
7307 if ( row == -1 || col == -1 || GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
7308 return;
7309
7310 wxRect rect = CellToRect(row, col);
7311 m_gridWin->Refresh(true, &rect);
7312 }
7313 }
7314
7315 void wxGrid::SetCellHighlightROPenWidth(int width)
7316 {
7317 if (m_cellHighlightROPenWidth != width)
7318 {
7319 m_cellHighlightROPenWidth = width;
7320
7321 // Just redrawing the cell highlight is not enough since that won't
7322 // make any visible change if the thickness is getting smaller.
7323 int row = m_currentCellCoords.GetRow();
7324 int col = m_currentCellCoords.GetCol();
7325 if ( row == -1 || col == -1 ||
7326 GetColWidth(col) <= 0 || GetRowHeight(row) <= 0 )
7327 return;
7328
7329 wxRect rect = CellToRect(row, col);
7330 m_gridWin->Refresh(true, &rect);
7331 }
7332 }
7333
7334 void wxGrid::RedrawGridLines()
7335 {
7336 // the lines will be redrawn when the window is thawn
7337 if ( GetBatchCount() )
7338 return;
7339
7340 if ( GridLinesEnabled() )
7341 {
7342 wxClientDC dc( m_gridWin );
7343 PrepareDC( dc );
7344 DrawAllGridLines( dc, wxRegion() );
7345 }
7346 else // remove the grid lines
7347 {
7348 m_gridWin->Refresh();
7349 }
7350 }
7351
7352 void wxGrid::EnableGridLines( bool enable )
7353 {
7354 if ( enable != m_gridLinesEnabled )
7355 {
7356 m_gridLinesEnabled = enable;
7357
7358 RedrawGridLines();
7359 }
7360 }
7361
7362 void wxGrid::DoClipGridLines(bool& var, bool clip)
7363 {
7364 if ( clip != var )
7365 {
7366 var = clip;
7367
7368 if ( GridLinesEnabled() )
7369 RedrawGridLines();
7370 }
7371 }
7372
7373 int wxGrid::GetDefaultRowSize() const
7374 {
7375 return m_defaultRowHeight;
7376 }
7377
7378 int wxGrid::GetRowSize( int row ) const
7379 {
7380 wxCHECK_MSG( row >= 0 && row < m_numRows, 0, wxT("invalid row index") );
7381
7382 return GetRowHeight(row);
7383 }
7384
7385 int wxGrid::GetDefaultColSize() const
7386 {
7387 return m_defaultColWidth;
7388 }
7389
7390 int wxGrid::GetColSize( int col ) const
7391 {
7392 wxCHECK_MSG( col >= 0 && col < m_numCols, 0, wxT("invalid column index") );
7393
7394 return GetColWidth(col);
7395 }
7396
7397 // ============================================================================
7398 // access to the grid attributes: each of them has a default value in the grid
7399 // itself and may be overidden on a per-cell basis
7400 // ============================================================================
7401
7402 // ----------------------------------------------------------------------------
7403 // setting default attributes
7404 // ----------------------------------------------------------------------------
7405
7406 void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
7407 {
7408 m_defaultCellAttr->SetBackgroundColour(col);
7409 #ifdef __WXGTK__
7410 m_gridWin->SetBackgroundColour(col);
7411 #endif
7412 }
7413
7414 void wxGrid::SetDefaultCellTextColour( const wxColour& col )
7415 {
7416 m_defaultCellAttr->SetTextColour(col);
7417 }
7418
7419 void wxGrid::SetDefaultCellAlignment( int horiz, int vert )
7420 {
7421 m_defaultCellAttr->SetAlignment(horiz, vert);
7422 }
7423
7424 void wxGrid::SetDefaultCellOverflow( bool allow )
7425 {
7426 m_defaultCellAttr->SetOverflow(allow);
7427 }
7428
7429 void wxGrid::SetDefaultCellFont( const wxFont& font )
7430 {
7431 m_defaultCellAttr->SetFont(font);
7432 }
7433
7434 // For editors and renderers the type registry takes precedence over the
7435 // default attr, so we need to register the new editor/renderer for the string
7436 // data type in order to make setting a default editor/renderer appear to
7437 // work correctly.
7438
7439 void wxGrid::SetDefaultRenderer(wxGridCellRenderer *renderer)
7440 {
7441 RegisterDataType(wxGRID_VALUE_STRING,
7442 renderer,
7443 GetDefaultEditorForType(wxGRID_VALUE_STRING));
7444 }
7445
7446 void wxGrid::SetDefaultEditor(wxGridCellEditor *editor)
7447 {
7448 RegisterDataType(wxGRID_VALUE_STRING,
7449 GetDefaultRendererForType(wxGRID_VALUE_STRING),
7450 editor);
7451 }
7452
7453 // ----------------------------------------------------------------------------
7454 // access to the default attributes
7455 // ----------------------------------------------------------------------------
7456
7457 wxColour wxGrid::GetDefaultCellBackgroundColour() const
7458 {
7459 return m_defaultCellAttr->GetBackgroundColour();
7460 }
7461
7462 wxColour wxGrid::GetDefaultCellTextColour() const
7463 {
7464 return m_defaultCellAttr->GetTextColour();
7465 }
7466
7467 wxFont wxGrid::GetDefaultCellFont() const
7468 {
7469 return m_defaultCellAttr->GetFont();
7470 }
7471
7472 void wxGrid::GetDefaultCellAlignment( int *horiz, int *vert ) const
7473 {
7474 m_defaultCellAttr->GetAlignment(horiz, vert);
7475 }
7476
7477 bool wxGrid::GetDefaultCellOverflow() const
7478 {
7479 return m_defaultCellAttr->GetOverflow();
7480 }
7481
7482 wxGridCellRenderer *wxGrid::GetDefaultRenderer() const
7483 {
7484 return m_defaultCellAttr->GetRenderer(NULL, 0, 0);
7485 }
7486
7487 wxGridCellEditor *wxGrid::GetDefaultEditor() const
7488 {
7489 return m_defaultCellAttr->GetEditor(NULL, 0, 0);
7490 }
7491
7492 // ----------------------------------------------------------------------------
7493 // access to cell attributes
7494 // ----------------------------------------------------------------------------
7495
7496 wxColour wxGrid::GetCellBackgroundColour(int row, int col) const
7497 {
7498 wxGridCellAttr *attr = GetCellAttr(row, col);
7499 wxColour colour = attr->GetBackgroundColour();
7500 attr->DecRef();
7501
7502 return colour;
7503 }
7504
7505 wxColour wxGrid::GetCellTextColour( int row, int col ) const
7506 {
7507 wxGridCellAttr *attr = GetCellAttr(row, col);
7508 wxColour colour = attr->GetTextColour();
7509 attr->DecRef();
7510
7511 return colour;
7512 }
7513
7514 wxFont wxGrid::GetCellFont( int row, int col ) const
7515 {
7516 wxGridCellAttr *attr = GetCellAttr(row, col);
7517 wxFont font = attr->GetFont();
7518 attr->DecRef();
7519
7520 return font;
7521 }
7522
7523 void wxGrid::GetCellAlignment( int row, int col, int *horiz, int *vert ) const
7524 {
7525 wxGridCellAttr *attr = GetCellAttr(row, col);
7526 attr->GetAlignment(horiz, vert);
7527 attr->DecRef();
7528 }
7529
7530 bool wxGrid::GetCellOverflow( int row, int col ) const
7531 {
7532 wxGridCellAttr *attr = GetCellAttr(row, col);
7533 bool allow = attr->GetOverflow();
7534 attr->DecRef();
7535
7536 return allow;
7537 }
7538
7539 wxGrid::CellSpan
7540 wxGrid::GetCellSize( int row, int col, int *num_rows, int *num_cols ) const
7541 {
7542 wxGridCellAttr *attr = GetCellAttr(row, col);
7543 attr->GetSize( num_rows, num_cols );
7544 attr->DecRef();
7545
7546 if ( *num_rows == 1 && *num_cols == 1 )
7547 return CellSpan_None; // just a normal cell
7548
7549 if ( *num_rows < 0 || *num_cols < 0 )
7550 return CellSpan_Inside; // covered by a multi-span cell
7551
7552 // this cell spans multiple cells to its right/bottom
7553 return CellSpan_Main;
7554 }
7555
7556 wxGridCellRenderer* wxGrid::GetCellRenderer(int row, int col) const
7557 {
7558 wxGridCellAttr* attr = GetCellAttr(row, col);
7559 wxGridCellRenderer* renderer = attr->GetRenderer(this, row, col);
7560 attr->DecRef();
7561
7562 return renderer;
7563 }
7564
7565 wxGridCellEditor* wxGrid::GetCellEditor(int row, int col) const
7566 {
7567 wxGridCellAttr* attr = GetCellAttr(row, col);
7568 wxGridCellEditor* editor = attr->GetEditor(this, row, col);
7569 attr->DecRef();
7570
7571 return editor;
7572 }
7573
7574 bool wxGrid::IsReadOnly(int row, int col) const
7575 {
7576 wxGridCellAttr* attr = GetCellAttr(row, col);
7577 bool isReadOnly = attr->IsReadOnly();
7578 attr->DecRef();
7579
7580 return isReadOnly;
7581 }
7582
7583 // ----------------------------------------------------------------------------
7584 // attribute support: cache, automatic provider creation, ...
7585 // ----------------------------------------------------------------------------
7586
7587 bool wxGrid::CanHaveAttributes() const
7588 {
7589 if ( !m_table )
7590 {
7591 return false;
7592 }
7593
7594 return m_table->CanHaveAttributes();
7595 }
7596
7597 void wxGrid::ClearAttrCache()
7598 {
7599 if ( m_attrCache.row != -1 )
7600 {
7601 wxGridCellAttr *oldAttr = m_attrCache.attr;
7602 m_attrCache.attr = NULL;
7603 m_attrCache.row = -1;
7604 // wxSafeDecRec(...) might cause event processing that accesses
7605 // the cached attribute, if one exists (e.g. by deleting the
7606 // editor stored within the attribute). Therefore it is important
7607 // to invalidate the cache before calling wxSafeDecRef!
7608 wxSafeDecRef(oldAttr);
7609 }
7610 }
7611
7612 void wxGrid::RefreshAttr(int row, int col)
7613 {
7614 if ( m_attrCache.row == row && m_attrCache.col == col )
7615 ClearAttrCache();
7616 }
7617
7618
7619 void wxGrid::CacheAttr(int row, int col, wxGridCellAttr *attr) const
7620 {
7621 if ( attr != NULL )
7622 {
7623 wxGrid * const self = const_cast<wxGrid *>(this);
7624
7625 self->ClearAttrCache();
7626 self->m_attrCache.row = row;
7627 self->m_attrCache.col = col;
7628 self->m_attrCache.attr = attr;
7629 wxSafeIncRef(attr);
7630 }
7631 }
7632
7633 bool wxGrid::LookupAttr(int row, int col, wxGridCellAttr **attr) const
7634 {
7635 if ( row == m_attrCache.row && col == m_attrCache.col )
7636 {
7637 *attr = m_attrCache.attr;
7638 wxSafeIncRef(m_attrCache.attr);
7639
7640 #ifdef DEBUG_ATTR_CACHE
7641 gs_nAttrCacheHits++;
7642 #endif
7643
7644 return true;
7645 }
7646 else
7647 {
7648 #ifdef DEBUG_ATTR_CACHE
7649 gs_nAttrCacheMisses++;
7650 #endif
7651
7652 return false;
7653 }
7654 }
7655
7656 wxGridCellAttr *wxGrid::GetCellAttr(int row, int col) const
7657 {
7658 wxGridCellAttr *attr = NULL;
7659 // Additional test to avoid looking at the cache e.g. for
7660 // wxNoCellCoords, as this will confuse memory management.
7661 if ( row >= 0 )
7662 {
7663 if ( !LookupAttr(row, col, &attr) )
7664 {
7665 attr = m_table ? m_table->GetAttr(row, col, wxGridCellAttr::Any)
7666 : NULL;
7667 CacheAttr(row, col, attr);
7668 }
7669 }
7670
7671 if (attr)
7672 {
7673 attr->SetDefAttr(m_defaultCellAttr);
7674 }
7675 else
7676 {
7677 attr = m_defaultCellAttr;
7678 attr->IncRef();
7679 }
7680
7681 return attr;
7682 }
7683
7684 wxGridCellAttr *wxGrid::GetOrCreateCellAttr(int row, int col) const
7685 {
7686 wxGridCellAttr *attr = NULL;
7687 bool canHave = ((wxGrid*)this)->CanHaveAttributes();
7688
7689 wxCHECK_MSG( canHave, attr, wxT("Cell attributes not allowed"));
7690 wxCHECK_MSG( m_table, attr, wxT("must have a table") );
7691
7692 attr = m_table->GetAttr(row, col, wxGridCellAttr::Cell);
7693 if ( !attr )
7694 {
7695 attr = new wxGridCellAttr(m_defaultCellAttr);
7696
7697 // artificially inc the ref count to match DecRef() in caller
7698 attr->IncRef();
7699 m_table->SetAttr(attr, row, col);
7700 }
7701
7702 return attr;
7703 }
7704
7705 // ----------------------------------------------------------------------------
7706 // setting column attributes (wrappers around SetColAttr)
7707 // ----------------------------------------------------------------------------
7708
7709 void wxGrid::SetColFormatBool(int col)
7710 {
7711 SetColFormatCustom(col, wxGRID_VALUE_BOOL);
7712 }
7713
7714 void wxGrid::SetColFormatNumber(int col)
7715 {
7716 SetColFormatCustom(col, wxGRID_VALUE_NUMBER);
7717 }
7718
7719 void wxGrid::SetColFormatFloat(int col, int width, int precision)
7720 {
7721 wxString typeName = wxGRID_VALUE_FLOAT;
7722 if ( (width != -1) || (precision != -1) )
7723 {
7724 typeName << wxT(':') << width << wxT(',') << precision;
7725 }
7726
7727 SetColFormatCustom(col, typeName);
7728 }
7729
7730 void wxGrid::SetColFormatCustom(int col, const wxString& typeName)
7731 {
7732 wxGridCellAttr *attr = m_table->GetAttr(-1, col, wxGridCellAttr::Col );
7733 if (!attr)
7734 attr = new wxGridCellAttr;
7735 wxGridCellRenderer *renderer = GetDefaultRendererForType(typeName);
7736 attr->SetRenderer(renderer);
7737 wxGridCellEditor *editor = GetDefaultEditorForType(typeName);
7738 attr->SetEditor(editor);
7739
7740 SetColAttr(col, attr);
7741
7742 }
7743
7744 // ----------------------------------------------------------------------------
7745 // setting cell attributes: this is forwarded to the table
7746 // ----------------------------------------------------------------------------
7747
7748 void wxGrid::SetAttr(int row, int col, wxGridCellAttr *attr)
7749 {
7750 if ( CanHaveAttributes() )
7751 {
7752 m_table->SetAttr(attr, row, col);
7753 ClearAttrCache();
7754 }
7755 else
7756 {
7757 wxSafeDecRef(attr);
7758 }
7759 }
7760
7761 void wxGrid::SetRowAttr(int row, wxGridCellAttr *attr)
7762 {
7763 if ( CanHaveAttributes() )
7764 {
7765 m_table->SetRowAttr(attr, row);
7766 ClearAttrCache();
7767 }
7768 else
7769 {
7770 wxSafeDecRef(attr);
7771 }
7772 }
7773
7774 void wxGrid::SetColAttr(int col, wxGridCellAttr *attr)
7775 {
7776 if ( CanHaveAttributes() )
7777 {
7778 m_table->SetColAttr(attr, col);
7779 ClearAttrCache();
7780 }
7781 else
7782 {
7783 wxSafeDecRef(attr);
7784 }
7785 }
7786
7787 void wxGrid::SetCellBackgroundColour( int row, int col, const wxColour& colour )
7788 {
7789 if ( CanHaveAttributes() )
7790 {
7791 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7792 attr->SetBackgroundColour(colour);
7793 attr->DecRef();
7794 }
7795 }
7796
7797 void wxGrid::SetCellTextColour( int row, int col, const wxColour& colour )
7798 {
7799 if ( CanHaveAttributes() )
7800 {
7801 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7802 attr->SetTextColour(colour);
7803 attr->DecRef();
7804 }
7805 }
7806
7807 void wxGrid::SetCellFont( int row, int col, const wxFont& font )
7808 {
7809 if ( CanHaveAttributes() )
7810 {
7811 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7812 attr->SetFont(font);
7813 attr->DecRef();
7814 }
7815 }
7816
7817 void wxGrid::SetCellAlignment( int row, int col, int horiz, int vert )
7818 {
7819 if ( CanHaveAttributes() )
7820 {
7821 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7822 attr->SetAlignment(horiz, vert);
7823 attr->DecRef();
7824 }
7825 }
7826
7827 void wxGrid::SetCellOverflow( int row, int col, bool allow )
7828 {
7829 if ( CanHaveAttributes() )
7830 {
7831 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7832 attr->SetOverflow(allow);
7833 attr->DecRef();
7834 }
7835 }
7836
7837 void wxGrid::SetCellSize( int row, int col, int num_rows, int num_cols )
7838 {
7839 if ( CanHaveAttributes() )
7840 {
7841 int cell_rows, cell_cols;
7842
7843 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7844 attr->GetSize(&cell_rows, &cell_cols);
7845 attr->SetSize(num_rows, num_cols);
7846 attr->DecRef();
7847
7848 // Cannot set the size of a cell to 0 or negative values
7849 // While it is perfectly legal to do that, this function cannot
7850 // handle all the possibilies, do it by hand by getting the CellAttr.
7851 // You can only set the size of a cell to 1,1 or greater with this fn
7852 wxASSERT_MSG( !((cell_rows < 1) || (cell_cols < 1)),
7853 wxT("wxGrid::SetCellSize setting cell size that is already part of another cell"));
7854 wxASSERT_MSG( !((num_rows < 1) || (num_cols < 1)),
7855 wxT("wxGrid::SetCellSize setting cell size to < 1"));
7856
7857 // if this was already a multicell then "turn off" the other cells first
7858 if ((cell_rows > 1) || (cell_cols > 1))
7859 {
7860 int i, j;
7861 for (j=row; j < row + cell_rows; j++)
7862 {
7863 for (i=col; i < col + cell_cols; i++)
7864 {
7865 if ((i != col) || (j != row))
7866 {
7867 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
7868 attr_stub->SetSize( 1, 1 );
7869 attr_stub->DecRef();
7870 }
7871 }
7872 }
7873 }
7874
7875 // mark the cells that will be covered by this cell to
7876 // negative or zero values to point back at this cell
7877 if (((num_rows > 1) || (num_cols > 1)) && (num_rows >= 1) && (num_cols >= 1))
7878 {
7879 int i, j;
7880 for (j=row; j < row + num_rows; j++)
7881 {
7882 for (i=col; i < col + num_cols; i++)
7883 {
7884 if ((i != col) || (j != row))
7885 {
7886 wxGridCellAttr *attr_stub = GetOrCreateCellAttr(j, i);
7887 attr_stub->SetSize( row - j, col - i );
7888 attr_stub->DecRef();
7889 }
7890 }
7891 }
7892 }
7893 }
7894 }
7895
7896 void wxGrid::SetCellRenderer(int row, int col, wxGridCellRenderer *renderer)
7897 {
7898 if ( CanHaveAttributes() )
7899 {
7900 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7901 attr->SetRenderer(renderer);
7902 attr->DecRef();
7903 }
7904 }
7905
7906 void wxGrid::SetCellEditor(int row, int col, wxGridCellEditor* editor)
7907 {
7908 if ( CanHaveAttributes() )
7909 {
7910 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7911 attr->SetEditor(editor);
7912 attr->DecRef();
7913 }
7914 }
7915
7916 void wxGrid::SetReadOnly(int row, int col, bool isReadOnly)
7917 {
7918 if ( CanHaveAttributes() )
7919 {
7920 wxGridCellAttr *attr = GetOrCreateCellAttr(row, col);
7921 attr->SetReadOnly(isReadOnly);
7922 attr->DecRef();
7923 }
7924 }
7925
7926 // ----------------------------------------------------------------------------
7927 // Data type registration
7928 // ----------------------------------------------------------------------------
7929
7930 void wxGrid::RegisterDataType(const wxString& typeName,
7931 wxGridCellRenderer* renderer,
7932 wxGridCellEditor* editor)
7933 {
7934 m_typeRegistry->RegisterDataType(typeName, renderer, editor);
7935 }
7936
7937
7938 wxGridCellEditor * wxGrid::GetDefaultEditorForCell(int row, int col) const
7939 {
7940 wxString typeName = m_table->GetTypeName(row, col);
7941 return GetDefaultEditorForType(typeName);
7942 }
7943
7944 wxGridCellRenderer * wxGrid::GetDefaultRendererForCell(int row, int col) const
7945 {
7946 wxString typeName = m_table->GetTypeName(row, col);
7947 return GetDefaultRendererForType(typeName);
7948 }
7949
7950 wxGridCellEditor * wxGrid::GetDefaultEditorForType(const wxString& typeName) const
7951 {
7952 int index = m_typeRegistry->FindOrCloneDataType(typeName);
7953 if ( index == wxNOT_FOUND )
7954 {
7955 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
7956
7957 return NULL;
7958 }
7959
7960 return m_typeRegistry->GetEditor(index);
7961 }
7962
7963 wxGridCellRenderer * wxGrid::GetDefaultRendererForType(const wxString& typeName) const
7964 {
7965 int index = m_typeRegistry->FindOrCloneDataType(typeName);
7966 if ( index == wxNOT_FOUND )
7967 {
7968 wxFAIL_MSG(wxString::Format(wxT("Unknown data type name [%s]"), typeName.c_str()));
7969
7970 return NULL;
7971 }
7972
7973 return m_typeRegistry->GetRenderer(index);
7974 }
7975
7976 // ----------------------------------------------------------------------------
7977 // row/col size
7978 // ----------------------------------------------------------------------------
7979
7980 void wxGrid::DoDisableLineResize(int line, wxGridFixedIndicesSet *& setFixed)
7981 {
7982 if ( !setFixed )
7983 {
7984 setFixed = new wxGridFixedIndicesSet;
7985 }
7986
7987 setFixed->insert(line);
7988 }
7989
7990 bool
7991 wxGrid::DoCanResizeLine(int line, const wxGridFixedIndicesSet *setFixed) const
7992 {
7993 return !setFixed || !setFixed->count(line);
7994 }
7995
7996 void wxGrid::EnableDragRowSize( bool enable )
7997 {
7998 m_canDragRowSize = enable;
7999 }
8000
8001 void wxGrid::EnableDragColSize( bool enable )
8002 {
8003 m_canDragColSize = enable;
8004 }
8005
8006 void wxGrid::EnableDragGridSize( bool enable )
8007 {
8008 m_canDragGridSize = enable;
8009 }
8010
8011 void wxGrid::EnableDragCell( bool enable )
8012 {
8013 m_canDragCell = enable;
8014 }
8015
8016 void wxGrid::SetDefaultRowSize( int height, bool resizeExistingRows )
8017 {
8018 m_defaultRowHeight = wxMax( height, m_minAcceptableRowHeight );
8019
8020 if ( resizeExistingRows )
8021 {
8022 // since we are resizing all rows to the default row size,
8023 // we can simply clear the row heights and row bottoms
8024 // arrays (which also allows us to take advantage of
8025 // some speed optimisations)
8026 m_rowHeights.Empty();
8027 m_rowBottoms.Empty();
8028 if ( !GetBatchCount() )
8029 CalcDimensions();
8030 }
8031 }
8032
8033 namespace
8034 {
8035
8036 // This is a common part of SetRowSize() and SetColSize() which takes care of
8037 // updating the height/width of a row/column depending on its current value and
8038 // the new one.
8039 //
8040 // Returns the difference between the new and the old size.
8041 int UpdateRowOrColSize(int& sizeCurrent, int sizeNew)
8042 {
8043 // On input here sizeCurrent can be negative if it's currently hidden (the
8044 // real size is its absolute value then). And sizeNew can be 0 to indicate
8045 // that the row/column should be hidden or -1 to indicate that it should be
8046 // shown again.
8047
8048 if ( sizeNew < 0 )
8049 {
8050 // We're showing back a previously hidden row/column.
8051 wxASSERT_MSG( sizeNew == -1, wxS("New size must be positive or -1.") );
8052
8053 wxASSERT_MSG( sizeCurrent < 0, wxS("May only show back if hidden.") );
8054
8055 sizeCurrent = -sizeCurrent;
8056
8057 // This is positive which is correct.
8058 return sizeCurrent;
8059 }
8060 else if ( sizeNew == 0 )
8061 {
8062 // We're hiding a row/column.
8063 wxASSERT_MSG( sizeCurrent > 0, wxS("Can't hide if already hidden.") );
8064
8065 sizeCurrent = -sizeCurrent;
8066
8067 // This is negative which is correct.
8068 return sizeCurrent;
8069 }
8070 else // We're just changing the row/column size.
8071 {
8072 // Here it could have been hidden or not previously.
8073 const int sizeOld = sizeCurrent < 0 ? 0 : sizeCurrent;
8074
8075 sizeCurrent = sizeNew;
8076
8077 return sizeCurrent - sizeOld;
8078 }
8079 }
8080
8081 } // anonymous namespace
8082
8083 void wxGrid::SetRowSize( int row, int height )
8084 {
8085 // See comment in SetColSize
8086 if ( height > 0 && height < GetRowMinimalAcceptableHeight())
8087 return;
8088
8089 // The value of -1 is special and means to fit the height to the row label.
8090 if ( height == -1 )
8091 {
8092 // As with the columns, ignore attempts to auto-size the hidden rows.
8093 if ( GetRowHeight(row) == 0 )
8094 return;
8095
8096 long w, h;
8097 wxArrayString lines;
8098 wxClientDC dc(m_rowLabelWin);
8099 dc.SetFont(GetLabelFont());
8100 StringToLines(GetRowLabelValue( row ), lines);
8101 GetTextBoxSize( dc, lines, &w, &h );
8102 //check that it is not less than the minimal height
8103 height = wxMax(h, GetRowMinimalAcceptableHeight());
8104 }
8105
8106 DoSetRowSize(row, height);
8107 }
8108
8109 void wxGrid::DoSetRowSize( int row, int height )
8110 {
8111 wxCHECK_RET( row >= 0 && row < m_numRows, wxT("invalid row index") );
8112
8113 if ( m_rowHeights.IsEmpty() )
8114 {
8115 // need to really create the array
8116 InitRowHeights();
8117 }
8118
8119 const int diff = UpdateRowOrColSize(m_rowHeights[row], height);
8120 if ( !diff )
8121 return;
8122
8123 for ( int i = row; i < m_numRows; i++ )
8124 {
8125 m_rowBottoms[i] += diff;
8126 }
8127
8128 if ( !GetBatchCount() )
8129 {
8130 CalcDimensions();
8131 Refresh();
8132 }
8133 }
8134
8135 void wxGrid::SetDefaultColSize( int width, bool resizeExistingCols )
8136 {
8137 // we dont allow zero default column width
8138 m_defaultColWidth = wxMax( wxMax( width, m_minAcceptableColWidth ), 1 );
8139
8140 if ( resizeExistingCols )
8141 {
8142 // since we are resizing all columns to the default column size,
8143 // we can simply clear the col widths and col rights
8144 // arrays (which also allows us to take advantage of
8145 // some speed optimisations)
8146 m_colWidths.Empty();
8147 m_colRights.Empty();
8148 if ( !GetBatchCount() )
8149 CalcDimensions();
8150 }
8151 }
8152
8153 void wxGrid::SetColSize( int col, int width )
8154 {
8155 // we intentionally don't test whether the width is less than
8156 // GetColMinimalWidth() here but we do compare it with
8157 // GetColMinimalAcceptableWidth() as otherwise things currently break (see
8158 // #651) -- and we also always allow the width of 0 as it has the special
8159 // sense of hiding the column
8160 if ( width > 0 && width < GetColMinimalAcceptableWidth() )
8161 return;
8162
8163 // The value of -1 is special and means to fit the width to the column label.
8164 if ( width == -1 )
8165 {
8166 // We currently don't support auto-sizing hidden columns. We could, but
8167 // it's not clear whether this is really needed and it would make the
8168 // code more complex.
8169 if ( GetColWidth(col) == 0 )
8170 return;
8171
8172 long w, h;
8173 wxArrayString lines;
8174 wxClientDC dc(m_colWindow);
8175 dc.SetFont(GetLabelFont());
8176 StringToLines(GetColLabelValue(col), lines);
8177 if ( GetColLabelTextOrientation() == wxHORIZONTAL )
8178 GetTextBoxSize( dc, lines, &w, &h );
8179 else
8180 GetTextBoxSize( dc, lines, &h, &w );
8181 width = w + 6;
8182 //check that it is not less than the minimal width
8183 width = wxMax(width, GetColMinimalAcceptableWidth());
8184 }
8185
8186 DoSetColSize(col, width);
8187 }
8188
8189 void wxGrid::DoSetColSize( int col, int width )
8190 {
8191 wxCHECK_RET( col >= 0 && col < m_numCols, wxT("invalid column index") );
8192
8193 if ( m_colWidths.IsEmpty() )
8194 {
8195 // need to really create the array
8196 InitColWidths();
8197 }
8198
8199 const int diff = UpdateRowOrColSize(m_colWidths[col], width);
8200 if ( !diff )
8201 return;
8202
8203 if ( m_useNativeHeader )
8204 GetGridColHeader()->UpdateColumn(col);
8205 //else: will be refreshed when the header is redrawn
8206
8207 for ( int colPos = GetColPos(col); colPos < m_numCols; colPos++ )
8208 {
8209 m_colRights[GetColAt(colPos)] += diff;
8210 }
8211
8212 if ( !GetBatchCount() )
8213 {
8214 CalcDimensions();
8215 Refresh();
8216 }
8217 }
8218
8219 void wxGrid::SetColMinimalWidth( int col, int width )
8220 {
8221 if (width > GetColMinimalAcceptableWidth())
8222 {
8223 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
8224 m_colMinWidths[key] = width;
8225 }
8226 }
8227
8228 void wxGrid::SetRowMinimalHeight( int row, int width )
8229 {
8230 if (width > GetRowMinimalAcceptableHeight())
8231 {
8232 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
8233 m_rowMinHeights[key] = width;
8234 }
8235 }
8236
8237 int wxGrid::GetColMinimalWidth(int col) const
8238 {
8239 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)col;
8240 wxLongToLongHashMap::const_iterator it = m_colMinWidths.find(key);
8241
8242 return it != m_colMinWidths.end() ? (int)it->second : m_minAcceptableColWidth;
8243 }
8244
8245 int wxGrid::GetRowMinimalHeight(int row) const
8246 {
8247 wxLongToLongHashMap::key_type key = (wxLongToLongHashMap::key_type)row;
8248 wxLongToLongHashMap::const_iterator it = m_rowMinHeights.find(key);
8249
8250 return it != m_rowMinHeights.end() ? (int)it->second : m_minAcceptableRowHeight;
8251 }
8252
8253 void wxGrid::SetColMinimalAcceptableWidth( int width )
8254 {
8255 // We do allow a width of 0 since this gives us
8256 // an easy way to temporarily hiding columns.
8257 if ( width >= 0 )
8258 m_minAcceptableColWidth = width;
8259 }
8260
8261 void wxGrid::SetRowMinimalAcceptableHeight( int height )
8262 {
8263 // We do allow a height of 0 since this gives us
8264 // an easy way to temporarily hiding rows.
8265 if ( height >= 0 )
8266 m_minAcceptableRowHeight = height;
8267 }
8268
8269 int wxGrid::GetColMinimalAcceptableWidth() const
8270 {
8271 return m_minAcceptableColWidth;
8272 }
8273
8274 int wxGrid::GetRowMinimalAcceptableHeight() const
8275 {
8276 return m_minAcceptableRowHeight;
8277 }
8278
8279 // ----------------------------------------------------------------------------
8280 // auto sizing
8281 // ----------------------------------------------------------------------------
8282
8283 void
8284 wxGrid::AutoSizeColOrRow(int colOrRow, bool setAsMin, wxGridDirection direction)
8285 {
8286 const bool column = direction == wxGRID_COLUMN;
8287
8288 // We don't support auto-sizing hidden rows or columns, this doesn't seem
8289 // to make much sense.
8290 if ( column )
8291 {
8292 if ( GetColWidth(colOrRow) == 0 )
8293 return;
8294 }
8295 else
8296 {
8297 if ( GetRowHeight(colOrRow) == 0 )
8298 return;
8299 }
8300
8301 wxClientDC dc(m_gridWin);
8302
8303 // cancel editing of cell
8304 HideCellEditControl();
8305 SaveEditControlValue();
8306
8307 // initialize both of them just to avoid compiler warnings even if only
8308 // really needs to be initialized here
8309 int row,
8310 col;
8311 if ( column )
8312 {
8313 row = -1;
8314 col = colOrRow;
8315 }
8316 else
8317 {
8318 row = colOrRow;
8319 col = -1;
8320 }
8321
8322 wxCoord extent, extentMax = 0;
8323 int max = column ? m_numRows : m_numCols;
8324 for ( int rowOrCol = 0; rowOrCol < max; rowOrCol++ )
8325 {
8326 if ( column )
8327 {
8328 row = rowOrCol;
8329 col = colOrRow;
8330 }
8331 else
8332 {
8333 row = colOrRow;
8334 col = rowOrCol;
8335 }
8336
8337 // we need to account for the cells spanning multiple columns/rows:
8338 // while they may need a lot of space, they don't need all of it in
8339 // this column/row
8340 int numRows, numCols;
8341 const CellSpan span = GetCellSize(row, col, &numRows, &numCols);
8342 if ( span == CellSpan_Inside )
8343 {
8344 // we need to get the size of the main cell, not of a cell hidden
8345 // by it
8346 row += numRows;
8347 col += numCols;
8348
8349 // get the size of the main cell too
8350 GetCellSize(row, col, &numRows, &numCols);
8351 }
8352
8353 // get cell ( main cell if CellSpan_Inside ) renderer best size
8354 wxGridCellAttr *attr = GetCellAttr(row, col);
8355 wxGridCellRenderer *renderer = attr->GetRenderer(this, row, col);
8356 if ( renderer )
8357 {
8358 wxSize size = renderer->GetBestSize(*this, *attr, dc, row, col);
8359 extent = column ? size.x : size.y;
8360
8361 if ( span != CellSpan_None )
8362 {
8363 // we spread the size of a spanning cell over all the cells it
8364 // covers evenly -- this is probably not ideal but we can't
8365 // really do much better here
8366 //
8367 // notice that numCols and numRows are never 0 as they
8368 // correspond to the size of the main cell of the span and not
8369 // of the cell inside it
8370 extent /= column ? numCols : numRows;
8371 }
8372
8373 if ( extent > extentMax )
8374 extentMax = extent;
8375
8376 renderer->DecRef();
8377 }
8378
8379 attr->DecRef();
8380 }
8381
8382 // now also compare with the column label extent
8383 wxCoord w, h;
8384 dc.SetFont( GetLabelFont() );
8385
8386 if ( column )
8387 {
8388 dc.GetMultiLineTextExtent( GetColLabelValue(colOrRow), &w, &h );
8389 if ( GetColLabelTextOrientation() == wxVERTICAL )
8390 w = h;
8391 }
8392 else
8393 dc.GetMultiLineTextExtent( GetRowLabelValue(colOrRow), &w, &h );
8394
8395 extent = column ? w : h;
8396 if ( extent > extentMax )
8397 extentMax = extent;
8398
8399 if ( !extentMax )
8400 {
8401 // empty column - give default extent (notice that if extentMax is less
8402 // than default extent but != 0, it's OK)
8403 extentMax = column ? m_defaultColWidth : m_defaultRowHeight;
8404 }
8405 else
8406 {
8407 if ( column )
8408 // leave some space around text
8409 extentMax += 10;
8410 else
8411 extentMax += 6;
8412 }
8413
8414 if ( column )
8415 {
8416 // Ensure automatic width is not less than minimal width. See the
8417 // comment in SetColSize() for explanation of why this isn't done
8418 // in SetColSize().
8419 if ( !setAsMin )
8420 extentMax = wxMax(extentMax, GetColMinimalWidth(colOrRow));
8421
8422 SetColSize( colOrRow, extentMax );
8423 if ( !GetBatchCount() )
8424 {
8425 if ( m_useNativeHeader )
8426 {
8427 GetGridColHeader()->UpdateColumn(colOrRow);
8428 }
8429 else
8430 {
8431 int cw, ch, dummy;
8432 m_gridWin->GetClientSize( &cw, &ch );
8433 wxRect rect ( CellToRect( 0, colOrRow ) );
8434 rect.y = 0;
8435 CalcScrolledPosition(rect.x, 0, &rect.x, &dummy);
8436 rect.width = cw - rect.x;
8437 rect.height = m_colLabelHeight;
8438 GetColLabelWindow()->Refresh( true, &rect );
8439 }
8440 }
8441 }
8442 else
8443 {
8444 // Ensure automatic width is not less than minimal height. See the
8445 // comment in SetColSize() for explanation of why this isn't done
8446 // in SetRowSize().
8447 if ( !setAsMin )
8448 extentMax = wxMax(extentMax, GetRowMinimalHeight(colOrRow));
8449
8450 SetRowSize(colOrRow, extentMax);
8451 if ( !GetBatchCount() )
8452 {
8453 int cw, ch, dummy;
8454 m_gridWin->GetClientSize( &cw, &ch );
8455 wxRect rect( CellToRect( colOrRow, 0 ) );
8456 rect.x = 0;
8457 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
8458 rect.width = m_rowLabelWidth;
8459 rect.height = ch - rect.y;
8460 m_rowLabelWin->Refresh( true, &rect );
8461 }
8462 }
8463
8464 if ( setAsMin )
8465 {
8466 if ( column )
8467 SetColMinimalWidth(colOrRow, extentMax);
8468 else
8469 SetRowMinimalHeight(colOrRow, extentMax);
8470 }
8471 }
8472
8473 wxCoord wxGrid::CalcColOrRowLabelAreaMinSize(wxGridDirection direction)
8474 {
8475 // calculate size for the rows or columns?
8476 const bool calcRows = direction == wxGRID_ROW;
8477
8478 wxClientDC dc(calcRows ? GetGridRowLabelWindow()
8479 : GetGridColLabelWindow());
8480 dc.SetFont(GetLabelFont());
8481
8482 // which dimension should we take into account for calculations?
8483 //
8484 // for columns, the text can be only horizontal so it's easy but for rows
8485 // we also have to take into account the text orientation
8486 const bool
8487 useWidth = calcRows || (GetColLabelTextOrientation() == wxVERTICAL);
8488
8489 wxArrayString lines;
8490 wxCoord extentMax = 0;
8491
8492 const int numRowsOrCols = calcRows ? m_numRows : m_numCols;
8493 for ( int rowOrCol = 0; rowOrCol < numRowsOrCols; rowOrCol++ )
8494 {
8495 lines.Clear();
8496
8497 wxString label = calcRows ? GetRowLabelValue(rowOrCol)
8498 : GetColLabelValue(rowOrCol);
8499 StringToLines(label, lines);
8500
8501 long w, h;
8502 GetTextBoxSize(dc, lines, &w, &h);
8503
8504 const wxCoord extent = useWidth ? w : h;
8505 if ( extent > extentMax )
8506 extentMax = extent;
8507 }
8508
8509 if ( !extentMax )
8510 {
8511 // empty column - give default extent (notice that if extentMax is less
8512 // than default extent but != 0, it's OK)
8513 extentMax = calcRows ? GetDefaultRowLabelSize()
8514 : GetDefaultColLabelSize();
8515 }
8516
8517 // leave some space around text (taken from AutoSizeColOrRow)
8518 if ( calcRows )
8519 extentMax += 10;
8520 else
8521 extentMax += 6;
8522
8523 return extentMax;
8524 }
8525
8526 int wxGrid::SetOrCalcColumnSizes(bool calcOnly, bool setAsMin)
8527 {
8528 int width = m_rowLabelWidth;
8529
8530 wxGridUpdateLocker locker;
8531 if(!calcOnly)
8532 locker.Create(this);
8533
8534 for ( int col = 0; col < m_numCols; col++ )
8535 {
8536 if ( !calcOnly )
8537 AutoSizeColumn(col, setAsMin);
8538
8539 width += GetColWidth(col);
8540 }
8541
8542 return width;
8543 }
8544
8545 int wxGrid::SetOrCalcRowSizes(bool calcOnly, bool setAsMin)
8546 {
8547 int height = m_colLabelHeight;
8548
8549 wxGridUpdateLocker locker;
8550 if(!calcOnly)
8551 locker.Create(this);
8552
8553 for ( int row = 0; row < m_numRows; row++ )
8554 {
8555 if ( !calcOnly )
8556 AutoSizeRow(row, setAsMin);
8557
8558 height += GetRowHeight(row);
8559 }
8560
8561 return height;
8562 }
8563
8564 void wxGrid::AutoSize()
8565 {
8566 wxGridUpdateLocker locker(this);
8567
8568 wxSize size(SetOrCalcColumnSizes(false) - m_rowLabelWidth + m_extraWidth,
8569 SetOrCalcRowSizes(false) - m_colLabelHeight + m_extraHeight);
8570
8571 // we know that we're not going to have scrollbars so disable them now to
8572 // avoid trouble in SetClientSize() which can otherwise set the correct
8573 // client size but also leave space for (not needed any more) scrollbars
8574 SetScrollbars(m_xScrollPixelsPerLine, m_yScrollPixelsPerLine,
8575 0, 0, 0, 0, true);
8576
8577 SetClientSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight);
8578 }
8579
8580 void wxGrid::AutoSizeRowLabelSize( int row )
8581 {
8582 // Hide the edit control, so it
8583 // won't interfere with drag-shrinking.
8584 if ( IsCellEditControlShown() )
8585 {
8586 HideCellEditControl();
8587 SaveEditControlValue();
8588 }
8589
8590 // autosize row height depending on label text
8591 SetRowSize(row, -1);
8592
8593 ForceRefresh();
8594 }
8595
8596 void wxGrid::AutoSizeColLabelSize( int col )
8597 {
8598 // Hide the edit control, so it
8599 // won't interfere with drag-shrinking.
8600 if ( IsCellEditControlShown() )
8601 {
8602 HideCellEditControl();
8603 SaveEditControlValue();
8604 }
8605
8606 // autosize column width depending on label text
8607 SetColSize(col, -1);
8608
8609 ForceRefresh();
8610 }
8611
8612 wxSize wxGrid::DoGetBestSize() const
8613 {
8614 wxGrid * const self = const_cast<wxGrid *>(this);
8615
8616 // we do the same as in AutoSize() here with the exception that we don't
8617 // change the column/row sizes, only calculate them
8618 wxSize size(self->SetOrCalcColumnSizes(true) - m_rowLabelWidth + m_extraWidth,
8619 self->SetOrCalcRowSizes(true) - m_colLabelHeight + m_extraHeight);
8620
8621 // NOTE: This size should be cached, but first we need to add calls to
8622 // InvalidateBestSize everywhere that could change the results of this
8623 // calculation.
8624 // CacheBestSize(size);
8625
8626 return wxSize(size.x + m_rowLabelWidth, size.y + m_colLabelHeight)
8627 + GetWindowBorderSize();
8628 }
8629
8630 void wxGrid::Fit()
8631 {
8632 AutoSize();
8633 }
8634
8635 #if WXWIN_COMPATIBILITY_2_8
8636 wxPen& wxGrid::GetDividerPen() const
8637 {
8638 return wxNullPen;
8639 }
8640 #endif // WXWIN_COMPATIBILITY_2_8
8641
8642 // ----------------------------------------------------------------------------
8643 // cell value accessor functions
8644 // ----------------------------------------------------------------------------
8645
8646 void wxGrid::SetCellValue( int row, int col, const wxString& s )
8647 {
8648 if ( m_table )
8649 {
8650 m_table->SetValue( row, col, s );
8651 if ( !GetBatchCount() )
8652 {
8653 int dummy;
8654 wxRect rect( CellToRect( row, col ) );
8655 rect.x = 0;
8656 rect.width = m_gridWin->GetClientSize().GetWidth();
8657 CalcScrolledPosition(0, rect.y, &dummy, &rect.y);
8658 m_gridWin->Refresh( false, &rect );
8659 }
8660
8661 if ( m_currentCellCoords.GetRow() == row &&
8662 m_currentCellCoords.GetCol() == col &&
8663 IsCellEditControlShown())
8664 // Note: If we are using IsCellEditControlEnabled,
8665 // this interacts badly with calling SetCellValue from
8666 // an EVT_GRID_CELL_CHANGE handler.
8667 {
8668 HideCellEditControl();
8669 ShowCellEditControl(); // will reread data from table
8670 }
8671 }
8672 }
8673
8674 // ----------------------------------------------------------------------------
8675 // block, row and column selection
8676 // ----------------------------------------------------------------------------
8677
8678 void wxGrid::SelectRow( int row, bool addToSelected )
8679 {
8680 if ( !m_selection )
8681 return;
8682
8683 if ( !addToSelected )
8684 ClearSelection();
8685
8686 m_selection->SelectRow(row);
8687 }
8688
8689 void wxGrid::SelectCol( int col, bool addToSelected )
8690 {
8691 if ( !m_selection )
8692 return;
8693
8694 if ( !addToSelected )
8695 ClearSelection();
8696
8697 m_selection->SelectCol(col);
8698 }
8699
8700 void wxGrid::SelectBlock(int topRow, int leftCol, int bottomRow, int rightCol,
8701 bool addToSelected)
8702 {
8703 if ( !m_selection )
8704 return;
8705
8706 if ( !addToSelected )
8707 ClearSelection();
8708
8709 m_selection->SelectBlock(topRow, leftCol, bottomRow, rightCol);
8710 }
8711
8712 void wxGrid::SelectAll()
8713 {
8714 if ( m_numRows > 0 && m_numCols > 0 )
8715 {
8716 if ( m_selection )
8717 m_selection->SelectBlock( 0, 0, m_numRows - 1, m_numCols - 1 );
8718 }
8719 }
8720
8721 // ----------------------------------------------------------------------------
8722 // cell, row and col deselection
8723 // ----------------------------------------------------------------------------
8724
8725 void wxGrid::DeselectLine(int line, const wxGridOperations& oper)
8726 {
8727 if ( !m_selection )
8728 return;
8729
8730 const wxGridSelectionModes mode = m_selection->GetSelectionMode();
8731 if ( mode == oper.GetSelectionMode() ||
8732 mode == wxGrid::wxGridSelectRowsOrColumns )
8733 {
8734 const wxGridCellCoords c(oper.MakeCoords(line, 0));
8735 if ( m_selection->IsInSelection(c) )
8736 m_selection->ToggleCellSelection(c);
8737 }
8738 else if ( mode != oper.Dual().GetSelectionMode() )
8739 {
8740 const int nOther = oper.Dual().GetNumberOfLines(this);
8741 for ( int i = 0; i < nOther; i++ )
8742 {
8743 const wxGridCellCoords c(oper.MakeCoords(line, i));
8744 if ( m_selection->IsInSelection(c) )
8745 m_selection->ToggleCellSelection(c);
8746 }
8747 }
8748 //else: can only select orthogonal lines so no lines in this direction
8749 // could have been selected anyhow
8750 }
8751
8752 void wxGrid::DeselectRow(int row)
8753 {
8754 DeselectLine(row, wxGridRowOperations());
8755 }
8756
8757 void wxGrid::DeselectCol(int col)
8758 {
8759 DeselectLine(col, wxGridColumnOperations());
8760 }
8761
8762 void wxGrid::DeselectCell( int row, int col )
8763 {
8764 if ( m_selection && m_selection->IsInSelection(row, col) )
8765 m_selection->ToggleCellSelection(row, col);
8766 }
8767
8768 bool wxGrid::IsSelection() const
8769 {
8770 return ( m_selection && (m_selection->IsSelection() ||
8771 ( m_selectedBlockTopLeft != wxGridNoCellCoords &&
8772 m_selectedBlockBottomRight != wxGridNoCellCoords) ) );
8773 }
8774
8775 bool wxGrid::IsInSelection( int row, int col ) const
8776 {
8777 return ( m_selection && (m_selection->IsInSelection( row, col ) ||
8778 ( row >= m_selectedBlockTopLeft.GetRow() &&
8779 col >= m_selectedBlockTopLeft.GetCol() &&
8780 row <= m_selectedBlockBottomRight.GetRow() &&
8781 col <= m_selectedBlockBottomRight.GetCol() )) );
8782 }
8783
8784 wxGridCellCoordsArray wxGrid::GetSelectedCells() const
8785 {
8786 if (!m_selection)
8787 {
8788 wxGridCellCoordsArray a;
8789 return a;
8790 }
8791
8792 return m_selection->m_cellSelection;
8793 }
8794
8795 wxGridCellCoordsArray wxGrid::GetSelectionBlockTopLeft() const
8796 {
8797 if (!m_selection)
8798 {
8799 wxGridCellCoordsArray a;
8800 return a;
8801 }
8802
8803 return m_selection->m_blockSelectionTopLeft;
8804 }
8805
8806 wxGridCellCoordsArray wxGrid::GetSelectionBlockBottomRight() const
8807 {
8808 if (!m_selection)
8809 {
8810 wxGridCellCoordsArray a;
8811 return a;
8812 }
8813
8814 return m_selection->m_blockSelectionBottomRight;
8815 }
8816
8817 wxArrayInt wxGrid::GetSelectedRows() const
8818 {
8819 if (!m_selection)
8820 {
8821 wxArrayInt a;
8822 return a;
8823 }
8824
8825 return m_selection->m_rowSelection;
8826 }
8827
8828 wxArrayInt wxGrid::GetSelectedCols() const
8829 {
8830 if (!m_selection)
8831 {
8832 wxArrayInt a;
8833 return a;
8834 }
8835
8836 return m_selection->m_colSelection;
8837 }
8838
8839 void wxGrid::ClearSelection()
8840 {
8841 wxRect r1 = BlockToDeviceRect(m_selectedBlockTopLeft,
8842 m_selectedBlockBottomRight);
8843 wxRect r2 = BlockToDeviceRect(m_currentCellCoords,
8844 m_selectedBlockCorner);
8845
8846 m_selectedBlockTopLeft =
8847 m_selectedBlockBottomRight =
8848 m_selectedBlockCorner = wxGridNoCellCoords;
8849
8850 if ( !r1.IsEmpty() )
8851 RefreshRect(r1, false);
8852 if ( !r2.IsEmpty() )
8853 RefreshRect(r2, false);
8854
8855 if ( m_selection )
8856 m_selection->ClearSelection();
8857 }
8858
8859 // This function returns the rectangle that encloses the given block
8860 // in device coords clipped to the client size of the grid window.
8861 //
8862 wxRect wxGrid::BlockToDeviceRect( const wxGridCellCoords& topLeft,
8863 const wxGridCellCoords& bottomRight ) const
8864 {
8865 wxRect resultRect;
8866 wxRect tempCellRect = CellToRect(topLeft);
8867 if ( tempCellRect != wxGridNoCellRect )
8868 {
8869 resultRect = tempCellRect;
8870 }
8871 else
8872 {
8873 resultRect = wxRect(0, 0, 0, 0);
8874 }
8875
8876 tempCellRect = CellToRect(bottomRight);
8877 if ( tempCellRect != wxGridNoCellRect )
8878 {
8879 resultRect += tempCellRect;
8880 }
8881 else
8882 {
8883 // If both inputs were "wxGridNoCellRect," then there's nothing to do.
8884 return wxGridNoCellRect;
8885 }
8886
8887 // Ensure that left/right and top/bottom pairs are in order.
8888 int left = resultRect.GetLeft();
8889 int top = resultRect.GetTop();
8890 int right = resultRect.GetRight();
8891 int bottom = resultRect.GetBottom();
8892
8893 int leftCol = topLeft.GetCol();
8894 int topRow = topLeft.GetRow();
8895 int rightCol = bottomRight.GetCol();
8896 int bottomRow = bottomRight.GetRow();
8897
8898 if (left > right)
8899 {
8900 int tmp = left;
8901 left = right;
8902 right = tmp;
8903
8904 tmp = leftCol;
8905 leftCol = rightCol;
8906 rightCol = tmp;
8907 }
8908
8909 if (top > bottom)
8910 {
8911 int tmp = top;
8912 top = bottom;
8913 bottom = tmp;
8914
8915 tmp = topRow;
8916 topRow = bottomRow;
8917 bottomRow = tmp;
8918 }
8919
8920 // The following loop is ONLY necessary to detect and handle merged cells.
8921 int cw, ch;
8922 m_gridWin->GetClientSize( &cw, &ch );
8923
8924 // Get the origin coordinates: notice that they will be negative if the
8925 // grid is scrolled downwards/to the right.
8926 int gridOriginX = 0;
8927 int gridOriginY = 0;
8928 CalcScrolledPosition(gridOriginX, gridOriginY, &gridOriginX, &gridOriginY);
8929
8930 int onScreenLeftmostCol = internalXToCol(-gridOriginX);
8931 int onScreenUppermostRow = internalYToRow(-gridOriginY);
8932
8933 int onScreenRightmostCol = internalXToCol(-gridOriginX + cw);
8934 int onScreenBottommostRow = internalYToRow(-gridOriginY + ch);
8935
8936 // Bound our loop so that we only examine the portion of the selected block
8937 // that is shown on screen. Therefore, we compare the Top-Left block values
8938 // to the Top-Left screen values, and the Bottom-Right block values to the
8939 // Bottom-Right screen values, choosing appropriately.
8940 const int visibleTopRow = wxMax(topRow, onScreenUppermostRow);
8941 const int visibleBottomRow = wxMin(bottomRow, onScreenBottommostRow);
8942 const int visibleLeftCol = wxMax(leftCol, onScreenLeftmostCol);
8943 const int visibleRightCol = wxMin(rightCol, onScreenRightmostCol);
8944
8945 for ( int j = visibleTopRow; j <= visibleBottomRow; j++ )
8946 {
8947 for ( int i = visibleLeftCol; i <= visibleRightCol; i++ )
8948 {
8949 if ( (j == visibleTopRow) || (j == visibleBottomRow) ||
8950 (i == visibleLeftCol) || (i == visibleRightCol) )
8951 {
8952 tempCellRect = CellToRect( j, i );
8953
8954 if (tempCellRect.x < left)
8955 left = tempCellRect.x;
8956 if (tempCellRect.y < top)
8957 top = tempCellRect.y;
8958 if (tempCellRect.x + tempCellRect.width > right)
8959 right = tempCellRect.x + tempCellRect.width;
8960 if (tempCellRect.y + tempCellRect.height > bottom)
8961 bottom = tempCellRect.y + tempCellRect.height;
8962 }
8963 else
8964 {
8965 i = visibleRightCol; // jump over inner cells.
8966 }
8967 }
8968 }
8969
8970 // Convert to scrolled coords
8971 CalcScrolledPosition( left, top, &left, &top );
8972 CalcScrolledPosition( right, bottom, &right, &bottom );
8973
8974 if (right < 0 || bottom < 0 || left > cw || top > ch)
8975 return wxRect(0,0,0,0);
8976
8977 resultRect.SetLeft( wxMax(0, left) );
8978 resultRect.SetTop( wxMax(0, top) );
8979 resultRect.SetRight( wxMin(cw, right) );
8980 resultRect.SetBottom( wxMin(ch, bottom) );
8981
8982 return resultRect;
8983 }
8984
8985 void wxGrid::DoSetSizes(const wxGridSizesInfo& sizeInfo,
8986 const wxGridOperations& oper)
8987 {
8988 BeginBatch();
8989 oper.SetDefaultLineSize(this, sizeInfo.m_sizeDefault, true);
8990 const int numLines = oper.GetNumberOfLines(this);
8991 for ( int i = 0; i < numLines; i++ )
8992 {
8993 int size = sizeInfo.GetSize(i);
8994 if ( size != sizeInfo.m_sizeDefault)
8995 oper.SetLineSize(this, i, size);
8996 }
8997 EndBatch();
8998 }
8999
9000 void wxGrid::SetColSizes(const wxGridSizesInfo& sizeInfo)
9001 {
9002 DoSetSizes(sizeInfo, wxGridColumnOperations());
9003 }
9004
9005 void wxGrid::SetRowSizes(const wxGridSizesInfo& sizeInfo)
9006 {
9007 DoSetSizes(sizeInfo, wxGridRowOperations());
9008 }
9009
9010 wxGridSizesInfo::wxGridSizesInfo(int defSize, const wxArrayInt& allSizes)
9011 {
9012 m_sizeDefault = defSize;
9013 for ( size_t i = 0; i < allSizes.size(); i++ )
9014 {
9015 if ( allSizes[i] != defSize )
9016 m_customSizes[i] = allSizes[i];
9017 }
9018 }
9019
9020 int wxGridSizesInfo::GetSize(unsigned pos) const
9021 {
9022 wxUnsignedToIntHashMap::const_iterator it = m_customSizes.find(pos);
9023
9024 // if it's not found return the default
9025 if ( it == m_customSizes.end() )
9026 return m_sizeDefault;
9027
9028 // otherwise return 0 if it's hidden, currently there is no way to get
9029 // its size before it had been hidden
9030 if ( it->second < 0 )
9031 return 0;
9032
9033 return it->second;
9034 }
9035
9036 // ----------------------------------------------------------------------------
9037 // drop target
9038 // ----------------------------------------------------------------------------
9039
9040 #if wxUSE_DRAG_AND_DROP
9041
9042 // this allow setting drop target directly on wxGrid
9043 void wxGrid::SetDropTarget(wxDropTarget *dropTarget)
9044 {
9045 GetGridWindow()->SetDropTarget(dropTarget);
9046 }
9047
9048 #endif // wxUSE_DRAG_AND_DROP
9049
9050 // ----------------------------------------------------------------------------
9051 // grid event classes
9052 // ----------------------------------------------------------------------------
9053
9054 IMPLEMENT_DYNAMIC_CLASS( wxGridEvent, wxNotifyEvent )
9055
9056 wxGridEvent::wxGridEvent( int id, wxEventType type, wxObject* obj,
9057 int row, int col, int x, int y, bool sel,
9058 bool control, bool shift, bool alt, bool meta )
9059 : wxNotifyEvent( type, id ),
9060 wxKeyboardState(control, shift, alt, meta)
9061 {
9062 Init(row, col, x, y, sel);
9063
9064 SetEventObject(obj);
9065 }
9066
9067 IMPLEMENT_DYNAMIC_CLASS( wxGridSizeEvent, wxNotifyEvent )
9068
9069 wxGridSizeEvent::wxGridSizeEvent( int id, wxEventType type, wxObject* obj,
9070 int rowOrCol, int x, int y,
9071 bool control, bool shift, bool alt, bool meta )
9072 : wxNotifyEvent( type, id ),
9073 wxKeyboardState(control, shift, alt, meta)
9074 {
9075 Init(rowOrCol, x, y);
9076
9077 SetEventObject(obj);
9078 }
9079
9080
9081 IMPLEMENT_DYNAMIC_CLASS( wxGridRangeSelectEvent, wxNotifyEvent )
9082
9083 wxGridRangeSelectEvent::wxGridRangeSelectEvent(int id, wxEventType type, wxObject* obj,
9084 const wxGridCellCoords& topLeft,
9085 const wxGridCellCoords& bottomRight,
9086 bool sel, bool control,
9087 bool shift, bool alt, bool meta )
9088 : wxNotifyEvent( type, id ),
9089 wxKeyboardState(control, shift, alt, meta)
9090 {
9091 Init(topLeft, bottomRight, sel);
9092
9093 SetEventObject(obj);
9094 }
9095
9096
9097 IMPLEMENT_DYNAMIC_CLASS(wxGridEditorCreatedEvent, wxCommandEvent)
9098
9099 wxGridEditorCreatedEvent::wxGridEditorCreatedEvent(int id, wxEventType type,
9100 wxObject* obj, int row,
9101 int col, wxControl* ctrl)
9102 : wxCommandEvent(type, id)
9103 {
9104 SetEventObject(obj);
9105 m_row = row;
9106 m_col = col;
9107 m_ctrl = ctrl;
9108 }
9109
9110
9111 // ----------------------------------------------------------------------------
9112 // wxGridTypeRegistry
9113 // ----------------------------------------------------------------------------
9114
9115 wxGridTypeRegistry::~wxGridTypeRegistry()
9116 {
9117 size_t count = m_typeinfo.GetCount();
9118 for ( size_t i = 0; i < count; i++ )
9119 delete m_typeinfo[i];
9120 }
9121
9122 void wxGridTypeRegistry::RegisterDataType(const wxString& typeName,
9123 wxGridCellRenderer* renderer,
9124 wxGridCellEditor* editor)
9125 {
9126 wxGridDataTypeInfo* info = new wxGridDataTypeInfo(typeName, renderer, editor);
9127
9128 // is it already registered?
9129 int loc = FindRegisteredDataType(typeName);
9130 if ( loc != wxNOT_FOUND )
9131 {
9132 delete m_typeinfo[loc];
9133 m_typeinfo[loc] = info;
9134 }
9135 else
9136 {
9137 m_typeinfo.Add(info);
9138 }
9139 }
9140
9141 int wxGridTypeRegistry::FindRegisteredDataType(const wxString& typeName)
9142 {
9143 size_t count = m_typeinfo.GetCount();
9144 for ( size_t i = 0; i < count; i++ )
9145 {
9146 if ( typeName == m_typeinfo[i]->m_typeName )
9147 {
9148 return i;
9149 }
9150 }
9151
9152 return wxNOT_FOUND;
9153 }
9154
9155 int wxGridTypeRegistry::FindDataType(const wxString& typeName)
9156 {
9157 int index = FindRegisteredDataType(typeName);
9158 if ( index == wxNOT_FOUND )
9159 {
9160 // check whether this is one of the standard ones, in which case
9161 // register it "on the fly"
9162 #if wxUSE_TEXTCTRL
9163 if ( typeName == wxGRID_VALUE_STRING )
9164 {
9165 RegisterDataType(wxGRID_VALUE_STRING,
9166 new wxGridCellStringRenderer,
9167 new wxGridCellTextEditor);
9168 }
9169 else
9170 #endif // wxUSE_TEXTCTRL
9171 #if wxUSE_CHECKBOX
9172 if ( typeName == wxGRID_VALUE_BOOL )
9173 {
9174 RegisterDataType(wxGRID_VALUE_BOOL,
9175 new wxGridCellBoolRenderer,
9176 new wxGridCellBoolEditor);
9177 }
9178 else
9179 #endif // wxUSE_CHECKBOX
9180 #if wxUSE_TEXTCTRL
9181 if ( typeName == wxGRID_VALUE_NUMBER )
9182 {
9183 RegisterDataType(wxGRID_VALUE_NUMBER,
9184 new wxGridCellNumberRenderer,
9185 new wxGridCellNumberEditor);
9186 }
9187 else if ( typeName == wxGRID_VALUE_FLOAT )
9188 {
9189 RegisterDataType(wxGRID_VALUE_FLOAT,
9190 new wxGridCellFloatRenderer,
9191 new wxGridCellFloatEditor);
9192 }
9193 else
9194 #endif // wxUSE_TEXTCTRL
9195 #if wxUSE_COMBOBOX
9196 if ( typeName == wxGRID_VALUE_CHOICE )
9197 {
9198 RegisterDataType(wxGRID_VALUE_CHOICE,
9199 new wxGridCellStringRenderer,
9200 new wxGridCellChoiceEditor);
9201 }
9202 else
9203 #endif // wxUSE_COMBOBOX
9204 {
9205 return wxNOT_FOUND;
9206 }
9207
9208 // we get here only if just added the entry for this type, so return
9209 // the last index
9210 index = m_typeinfo.GetCount() - 1;
9211 }
9212
9213 return index;
9214 }
9215
9216 int wxGridTypeRegistry::FindOrCloneDataType(const wxString& typeName)
9217 {
9218 int index = FindDataType(typeName);
9219 if ( index == wxNOT_FOUND )
9220 {
9221 // the first part of the typename is the "real" type, anything after ':'
9222 // are the parameters for the renderer
9223 index = FindDataType(typeName.BeforeFirst(wxT(':')));
9224 if ( index == wxNOT_FOUND )
9225 {
9226 return wxNOT_FOUND;
9227 }
9228
9229 wxGridCellRenderer *renderer = GetRenderer(index);
9230 wxGridCellRenderer *rendererOld = renderer;
9231 renderer = renderer->Clone();
9232 rendererOld->DecRef();
9233
9234 wxGridCellEditor *editor = GetEditor(index);
9235 wxGridCellEditor *editorOld = editor;
9236 editor = editor->Clone();
9237 editorOld->DecRef();
9238
9239 // do it even if there are no parameters to reset them to defaults
9240 wxString params = typeName.AfterFirst(wxT(':'));
9241 renderer->SetParameters(params);
9242 editor->SetParameters(params);
9243
9244 // register the new typename
9245 RegisterDataType(typeName, renderer, editor);
9246
9247 // we just registered it, it's the last one
9248 index = m_typeinfo.GetCount() - 1;
9249 }
9250
9251 return index;
9252 }
9253
9254 wxGridCellRenderer* wxGridTypeRegistry::GetRenderer(int index)
9255 {
9256 wxGridCellRenderer* renderer = m_typeinfo[index]->m_renderer;
9257 if (renderer)
9258 renderer->IncRef();
9259
9260 return renderer;
9261 }
9262
9263 wxGridCellEditor* wxGridTypeRegistry::GetEditor(int index)
9264 {
9265 wxGridCellEditor* editor = m_typeinfo[index]->m_editor;
9266 if (editor)
9267 editor->IncRef();
9268
9269 return editor;
9270 }
9271
9272 #endif // wxUSE_GRID