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