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