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