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