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