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