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