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