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