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