]> git.saurik.com Git - wxWidgets.git/blame - src/generic/gridsel.cpp
Mention wxEVT_COMMAND_TOGGLEBUTTON_CLICKED explicitly in the documentation.
[wxWidgets.git] / src / generic / gridsel.cpp
CommitLineData
294f6bcb 1///////////////////////////////////////////////////////////////////////////
93763ad5 2// Name: src/generic/gridsel.cpp
294f6bcb
SN
3// Purpose: wxGridSelection
4// Author: Stefan Neis
5// Modified by:
6// Created: 20/02/1999
2b5f62a0 7// RCS-ID: $Id$
294f6bcb 8// Copyright: (c) Stefan Neis (Stefan.Neis@t-online.de)
65571936 9// Licence: wxWindows licence
294f6bcb
SN
10/////////////////////////////////////////////////////////////////////////////
11
93763ad5
WS
12// For compilers that support precompilation, includes "wx/wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16 #pragma hdrstop
17#endif
18
294f6bcb
SN
19// ============================================================================
20// declarations
21// ============================================================================
22
23// ----------------------------------------------------------------------------
24// headers
25// ----------------------------------------------------------------------------
26
f7556ff0 27#if wxUSE_GRID
f1567cdd 28
294f6bcb
SN
29#include "wx/generic/gridsel.h"
30
a0bd3147 31
f1567cdd
SN
32// Some explanation for the members of the class:
33// m_cellSelection stores individual selected cells
34// -- this is only used if m_selectionMode == wxGridSelectCells
35// m_blockSelectionTopLeft and m_blockSelectionBottomRight
36// store the upper left and lower right corner of selected Blocks
37// m_rowSelection and m_colSelection store individual selected
38// rows and columns; maybe those are superfluous and should be
39// treated as blocks?
40
294f6bcb 41wxGridSelection::wxGridSelection( wxGrid * grid,
b5808881 42 wxGrid::wxGridSelectionModes sel )
294f6bcb
SN
43{
44 m_grid = grid;
45 m_selectionMode = sel;
46}
47
48bool wxGridSelection::IsSelection()
49{
50 return ( m_cellSelection.GetCount() || m_blockSelectionTopLeft.GetCount() ||
b5808881 51 m_rowSelection.GetCount() || m_colSelection.GetCount() );
294f6bcb
SN
52}
53
a0bd3147 54bool wxGridSelection::IsInSelection( int row, int col )
294f6bcb 55{
f1567cdd
SN
56 size_t count;
57
58 // First check whether the given cell is individually selected
59 // (if m_selectionMode is wxGridSelectCells).
60 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb
SN
61 {
62 count = m_cellSelection.GetCount();
b5808881
SN
63 for ( size_t n = 0; n < count; n++ )
64 {
65 wxGridCellCoords& coords = m_cellSelection[n];
66 if ( row == coords.GetRow() && col == coords.GetCol() )
ca65c044 67 return true;
b5808881 68 }
294f6bcb 69 }
f1567cdd
SN
70
71 // Now check whether the given cell is
72 // contained in one of the selected blocks.
294f6bcb
SN
73 count = m_blockSelectionTopLeft.GetCount();
74 for ( size_t n = 0; n < count; n++ )
75 {
b5808881
SN
76 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
77 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
78 if ( BlockContainsCell(coords1.GetRow(), coords1.GetCol(),
79 coords2.GetRow(), coords2.GetCol(),
80 row, col ) )
ca65c044 81 return true;
b5808881 82 }
f1567cdd
SN
83
84 // Now check whether the given cell is
85 // contained in one of the selected rows
86 // (unless we are in column selection mode).
b5808881 87 if ( m_selectionMode != wxGrid::wxGridSelectColumns )
294f6bcb 88 {
4e115ed2 89 count = m_rowSelection.GetCount();
b5808881
SN
90 for ( size_t n = 0; n < count; n++ )
91 {
92 if ( row == m_rowSelection[n] )
ca65c044 93 return true;
b5808881
SN
94 }
95 }
f1567cdd
SN
96
97 // Now check whether the given cell is
98 // contained in one of the selected columns
99 // (unless we are in row selection mode).
b5808881 100 if ( m_selectionMode != wxGrid::wxGridSelectRows )
294f6bcb 101 {
4e115ed2 102 count = m_colSelection.GetCount();
b5808881
SN
103 for ( size_t n = 0; n < count; n++ )
104 {
105 if ( col == m_colSelection[n] )
ca65c044 106 return true;
b5808881 107 }
294f6bcb 108 }
a0bd3147 109
ca65c044 110 return false;
294f6bcb
SN
111}
112
f1567cdd 113// Change the selection mode
a0bd3147 114void wxGridSelection::SetSelectionMode( wxGrid::wxGridSelectionModes selmode )
f1567cdd
SN
115{
116 // if selection mode is unchanged return immediately
117 if (selmode == m_selectionMode)
118 return;
119
120 if ( m_selectionMode != wxGrid::wxGridSelectCells )
121 {
122 // if changing form row to column selection
123 // or vice versa, clear the selection.
124 if ( selmode != wxGrid::wxGridSelectCells )
125 ClearSelection();
126
127 m_selectionMode = selmode;
128 }
129 else
130 {
131 // if changing from cell selection to something else,
132 // promote selected cells/blocks to whole rows/columns.
133 size_t n;
134 while ( ( n = m_cellSelection.GetCount() ) > 0 )
135 {
136 n--;
137 wxGridCellCoords& coords = m_cellSelection[n];
138 int row = coords.GetRow();
139 int col = coords.GetCol();
140 m_cellSelection.RemoveAt(n);
141 if (selmode == wxGrid::wxGridSelectRows)
142 SelectRow( row );
143 else // selmode == wxGridSelectColumns)
144 SelectCol( col );
145 }
043d16b2 146
043d16b2 147 // Note that m_blockSelectionTopLeft's size may be changing!
a0bd3147 148 for (n = 0; n < m_blockSelectionTopLeft.GetCount(); n++)
f1567cdd 149 {
f1567cdd
SN
150 wxGridCellCoords& coords = m_blockSelectionTopLeft[n];
151 int topRow = coords.GetRow();
152 int leftCol = coords.GetCol();
153 coords = m_blockSelectionBottomRight[n];
154 int bottomRow = coords.GetRow();
155 int rightCol = coords.GetCol();
a0bd3147 156
f1567cdd
SN
157 if (selmode == wxGrid::wxGridSelectRows)
158 {
159 if (leftCol != 0 || rightCol != m_grid->GetNumberCols() - 1 )
160 {
161 m_blockSelectionTopLeft.RemoveAt(n);
162 m_blockSelectionBottomRight.RemoveAt(n);
8b5f6d9d
VZ
163 SelectBlockNoEvent( topRow, 0,
164 bottomRow, m_grid->GetNumberCols() - 1);
f1567cdd
SN
165 }
166 }
167 else // selmode == wxGridSelectColumns)
168 {
169 if (topRow != 0 || bottomRow != m_grid->GetNumberRows() - 1 )
170 {
171 m_blockSelectionTopLeft.RemoveAt(n);
172 m_blockSelectionBottomRight.RemoveAt(n);
8b5f6d9d
VZ
173 SelectBlockNoEvent(0, leftCol,
174 m_grid->GetNumberRows() - 1, rightCol);
f1567cdd
SN
175 }
176 }
177 }
a0bd3147 178
043d16b2 179 m_selectionMode = selmode;
f1567cdd
SN
180 }
181}
182
8b5f6d9d 183void wxGridSelection::SelectRow(int row, const wxKeyboardState& kbd)
294f6bcb 184{
b5808881 185 if ( m_selectionMode == wxGrid::wxGridSelectColumns )
294f6bcb 186 return;
a0bd3147 187
f1567cdd 188 size_t count, n;
294f6bcb
SN
189
190 // Remove single cells contained in newly selected block.
f1567cdd 191 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb
SN
192 {
193 count = m_cellSelection.GetCount();
f1567cdd 194 for ( n = 0; n < count; n++ )
b5808881
SN
195 {
196 wxGridCellCoords& coords = m_cellSelection[n];
197 if ( BlockContainsCell( row, 0, row, m_grid->GetNumberCols() - 1,
198 coords.GetRow(), coords.GetCol() ) )
199 {
200 m_cellSelection.RemoveAt(n);
a0bd3147
DS
201 n--;
202 count--;
b5808881
SN
203 }
204 }
294f6bcb
SN
205 }
206
f1567cdd 207 // Simplify list of selected blocks (if possible)
294f6bcb 208 count = m_blockSelectionTopLeft.GetCount();
ca65c044 209 bool done = false;
a0bd3147 210
b14159f7 211 for ( n = 0; n < count; n++ )
294f6bcb 212 {
b5808881
SN
213 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
214 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
f1567cdd
SN
215
216 // Remove block if it is a subset of the row
b5808881
SN
217 if ( coords1.GetRow() == row && row == coords2.GetRow() )
218 {
219 m_blockSelectionTopLeft.RemoveAt(n);
220 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
221 n--;
222 count--;
f1e26920 223 }
b5808881
SN
224 else if ( coords1.GetCol() == 0 &&
225 coords2.GetCol() == m_grid->GetNumberCols() - 1 )
226 {
f1567cdd 227 // silently return, if row is contained in block
b5808881
SN
228 if ( coords1.GetRow() <= row && row <= coords2.GetRow() )
229 return;
f1567cdd 230 // expand block, if it touched row
b5808881
SN
231 else if ( coords1.GetRow() == row + 1)
232 {
233 coords1.SetRow(row);
ca65c044 234 done = true;
b5808881
SN
235 }
236 else if ( coords2.GetRow() == row - 1)
237 {
238 coords2.SetRow(row);
ca65c044 239 done = true;
f1e26920 240 }
b5808881 241 }
294f6bcb
SN
242 }
243
f1567cdd
SN
244 // Unless we successfully handled the row,
245 // check whether row is already selected.
246 if ( !done )
294f6bcb 247 {
f1567cdd
SN
248 count = m_rowSelection.GetCount();
249 for ( n = 0; n < count; n++ )
250 {
251 if ( row == m_rowSelection[n] )
252 return;
253 }
294f6bcb 254
f1567cdd
SN
255 // Add row to selection
256 m_rowSelection.Add(row);
257 }
b5808881
SN
258
259 // Update View:
b5808881 260 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
261 {
262 wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( row, 0 ),
263 wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ) );
ca65c044 264 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
3665f7d0 265 }
f1567cdd 266
5c8fc7c1
SN
267 // Send Event
268 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
269 wxEVT_GRID_RANGE_SELECT,
270 m_grid,
271 wxGridCellCoords( row, 0 ),
d95b0c2b 272 wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ),
ca65c044 273 true,
8b5f6d9d 274 kbd);
5c8fc7c1 275
a0bd3147 276 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
294f6bcb
SN
277}
278
8b5f6d9d 279void wxGridSelection::SelectCol(int col, const wxKeyboardState& kbd)
294f6bcb 280{
b5808881 281 if ( m_selectionMode == wxGrid::wxGridSelectRows )
294f6bcb 282 return;
f1567cdd 283 size_t count, n;
294f6bcb
SN
284
285 // Remove single cells contained in newly selected block.
f1567cdd 286 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb
SN
287 {
288 count = m_cellSelection.GetCount();
f1567cdd 289 for ( n = 0; n < count; n++ )
b5808881
SN
290 {
291 wxGridCellCoords& coords = m_cellSelection[n];
292 if ( BlockContainsCell( 0, col, m_grid->GetNumberRows() - 1, col,
293 coords.GetRow(), coords.GetCol() ) )
294 {
295 m_cellSelection.RemoveAt(n);
a0bd3147
DS
296 n--;
297 count--;
b5808881
SN
298 }
299 }
294f6bcb
SN
300 }
301
f1567cdd 302 // Simplify list of selected blocks (if possible)
294f6bcb 303 count = m_blockSelectionTopLeft.GetCount();
ca65c044 304 bool done = false;
b14159f7 305 for ( n = 0; n < count; n++ )
294f6bcb 306 {
b5808881
SN
307 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
308 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
f1567cdd
SN
309
310 // Remove block if it is a subset of the column
b5808881
SN
311 if ( coords1.GetCol() == col && col == coords2.GetCol() )
312 {
313 m_blockSelectionTopLeft.RemoveAt(n);
314 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
315 n--;
316 count--;
b5808881
SN
317 }
318 else if ( coords1.GetRow() == 0 &&
319 coords2.GetRow() == m_grid->GetNumberRows() - 1 )
320 {
f1567cdd 321 // silently return, if row is contained in block
b5808881
SN
322 if ( coords1.GetCol() <= col && col <= coords2.GetCol() )
323 return;
f1567cdd 324 // expand block, if it touched col
b5808881
SN
325 else if ( coords1.GetCol() == col + 1)
326 {
327 coords1.SetCol(col);
ca65c044 328 done = true;
b5808881
SN
329 }
330 else if ( coords2.GetCol() == col - 1)
331 {
332 coords2.SetCol(col);
ca65c044 333 done = true;
f1e26920 334 }
b5808881 335 }
294f6bcb
SN
336 }
337
f1567cdd 338 // Unless we successfully handled the column,
294f6bcb 339 // Check whether col is already selected.
f1567cdd 340 if ( !done )
294f6bcb 341 {
f1567cdd
SN
342 count = m_colSelection.GetCount();
343 for ( n = 0; n < count; n++ )
344 {
345 if ( col == m_colSelection[n] )
346 return;
347 }
294f6bcb 348
f1567cdd
SN
349 // Add col to selection
350 m_colSelection.Add(col);
351 }
b5808881
SN
352
353 // Update View:
b5808881 354 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
355 {
356 wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( 0, col ),
357 wxGridCellCoords( m_grid->GetNumberRows() - 1, col ) );
ca65c044 358 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
3665f7d0 359 }
f1567cdd 360
5c8fc7c1
SN
361 // Send Event
362 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
363 wxEVT_GRID_RANGE_SELECT,
364 m_grid,
365 wxGridCellCoords( 0, col ),
d95b0c2b 366 wxGridCellCoords( m_grid->GetNumberRows() - 1, col ),
ca65c044 367 true,
8b5f6d9d 368 kbd );
5c8fc7c1 369
a0bd3147 370 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
294f6bcb
SN
371}
372
5c8fc7c1
SN
373void wxGridSelection::SelectBlock( int topRow, int leftCol,
374 int bottomRow, int rightCol,
8b5f6d9d 375 const wxKeyboardState& kbd,
d95b0c2b 376 bool sendEvent )
294f6bcb 377{
5c8fc7c1 378 // Fix the coordinates of the block if needed.
8a3e536c 379 switch ( m_selectionMode )
294f6bcb 380 {
8a3e536c
VZ
381 default:
382 wxFAIL_MSG( "unknown selection mode" );
383 // fall through
384
385 case wxGrid::wxGridSelectCells:
386 // nothing to do -- in this mode arbitrary blocks can be selected
387 break;
388
389 case wxGrid::wxGridSelectRows:
390 leftCol = 0;
391 rightCol = m_grid->GetNumberCols() - 1;
392 break;
393
394 case wxGrid::wxGridSelectColumns:
395 topRow = 0;
396 bottomRow = m_grid->GetNumberRows() - 1;
397 break;
398
399 case wxGrid::wxGridSelectRowsOrColumns:
400 // block selection doesn't make sense for this mode, we could only
401 // select the entire grid but this wouldn't be useful
402 return;
294f6bcb 403 }
a0bd3147 404
5c8fc7c1
SN
405 if ( topRow > bottomRow )
406 {
407 int temp = topRow;
408 topRow = bottomRow;
409 bottomRow = temp;
410 }
411
412 if ( leftCol > rightCol )
413 {
414 int temp = leftCol;
415 leftCol = rightCol;
416 rightCol = temp;
417 }
294f6bcb
SN
418
419 // Handle single cell selection in SelectCell.
695a3263
MB
420 // (MB: added check for selection mode here to prevent
421 // crashes if, for example, we are select rows and the
422 // grid only has 1 col)
423 if ( m_selectionMode == wxGrid::wxGridSelectCells &&
424 topRow == bottomRow && leftCol == rightCol )
a0bd3147 425 {
8b5f6d9d 426 SelectCell( topRow, leftCol, kbd, sendEvent );
a0bd3147 427 }
294f6bcb 428
f1567cdd 429 size_t count, n;
a0bd3147 430
294f6bcb 431 // Remove single cells contained in newly selected block.
f1567cdd 432 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb
SN
433 {
434 count = m_cellSelection.GetCount();
f1567cdd 435 for ( n = 0; n < count; n++ )
b5808881
SN
436 {
437 wxGridCellCoords& coords = m_cellSelection[n];
438 if ( BlockContainsCell( topRow, leftCol, bottomRow, rightCol,
439 coords.GetRow(), coords.GetCol() ) )
440 {
441 m_cellSelection.RemoveAt(n);
a0bd3147
DS
442 n--;
443 count--;
b5808881
SN
444 }
445 }
294f6bcb
SN
446 }
447
448 // If a block containing the selection is already selected, return,
449 // if a block contained in the selection is found, remove it.
450
451 count = m_blockSelectionTopLeft.GetCount();
f1567cdd 452 for ( n = 0; n < count; n++ )
294f6bcb 453 {
b5808881
SN
454 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
455 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
a0bd3147 456
b5808881
SN
457 switch ( BlockContain( coords1.GetRow(), coords1.GetCol(),
458 coords2.GetRow(), coords2.GetCol(),
459 topRow, leftCol, bottomRow, rightCol ) )
460 {
902725ee
WS
461 case 1:
462 return;
463
464 case -1:
465 m_blockSelectionTopLeft.RemoveAt(n);
466 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
467 n--;
468 count--;
902725ee
WS
469 break;
470
471 default:
472 break;
b5808881 473 }
294f6bcb
SN
474 }
475
476 // If a row containing the selection is already selected, return,
477 // if a row contained in newly selected block is found, remove it.
b5808881 478 if ( m_selectionMode != wxGrid::wxGridSelectColumns )
294f6bcb 479 {
f1567cdd
SN
480 count = m_rowSelection.GetCount();
481 for ( n = 0; n < count; n++ )
b5808881
SN
482 {
483 switch ( BlockContain( m_rowSelection[n], 0,
a0bd3147 484 m_rowSelection[n], m_grid->GetNumberCols() - 1,
b5808881
SN
485 topRow, leftCol, bottomRow, rightCol ) )
486 {
902725ee
WS
487 case 1:
488 return;
489
490 case -1:
491 m_rowSelection.RemoveAt(n);
a0bd3147
DS
492 n--;
493 count--;
902725ee
WS
494 break;
495
496 default:
497 break;
b5808881
SN
498 }
499 }
500 }
a0bd3147 501
b5808881 502 if ( m_selectionMode != wxGrid::wxGridSelectRows )
294f6bcb 503 {
f1567cdd
SN
504 count = m_colSelection.GetCount();
505 for ( n = 0; n < count; n++ )
b5808881
SN
506 {
507 switch ( BlockContain( 0, m_colSelection[n],
a0bd3147 508 m_grid->GetNumberRows() - 1, m_colSelection[n],
b5808881
SN
509 topRow, leftCol, bottomRow, rightCol ) )
510 {
902725ee
WS
511 case 1:
512 return;
513
514 case -1:
515 m_colSelection.RemoveAt(n);
a0bd3147
DS
516 n--;
517 count--;
902725ee
WS
518 break;
519
520 default:
521 break;
b5808881
SN
522 }
523 }
294f6bcb 524 }
a0bd3147 525
294f6bcb
SN
526 m_blockSelectionTopLeft.Add( wxGridCellCoords( topRow, leftCol ) );
527 m_blockSelectionBottomRight.Add( wxGridCellCoords( bottomRow, rightCol ) );
b5808881
SN
528
529 // Update View:
b5808881 530 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
531 {
532 wxRect r = m_grid->BlockToDeviceRect( wxGridCellCoords( topRow, leftCol ),
533 wxGridCellCoords( bottomRow, rightCol ) );
ca65c044 534 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
3665f7d0 535 }
f1567cdd 536
5c8fc7c1
SN
537 // Send Event, if not disabled.
538 if ( sendEvent )
539 {
d95b0c2b 540 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
a0bd3147
DS
541 wxEVT_GRID_RANGE_SELECT,
542 m_grid,
543 wxGridCellCoords( topRow, leftCol ),
544 wxGridCellCoords( bottomRow, rightCol ),
545 true,
8b5f6d9d 546 kbd);
a0bd3147 547 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
5c8fc7c1 548 }
294f6bcb
SN
549}
550
d95b0c2b 551void wxGridSelection::SelectCell( int row, int col,
8b5f6d9d 552 const wxKeyboardState& kbd,
d95b0c2b 553 bool sendEvent )
294f6bcb 554{
b5808881 555 if ( m_selectionMode == wxGrid::wxGridSelectRows )
043d16b2 556 {
8b5f6d9d 557 SelectBlock(row, 0, row, m_grid->GetNumberCols() - 1, kbd, sendEvent);
a0bd3147 558
043d16b2
SN
559 return;
560 }
b5808881 561 else if ( m_selectionMode == wxGrid::wxGridSelectColumns )
043d16b2 562 {
8b5f6d9d 563 SelectBlock(0, col, m_grid->GetNumberRows() - 1, col, kbd, sendEvent);
a0bd3147 564
043d16b2
SN
565 return;
566 }
294f6bcb
SN
567 else if ( IsInSelection ( row, col ) )
568 return;
a0bd3147 569
294f6bcb 570 m_cellSelection.Add( wxGridCellCoords( row, col ) );
b5808881 571
f1567cdd 572 // Update View:
b5808881 573 if ( !m_grid->GetBatchCount() )
3665f7d0 574 {
a0bd3147
DS
575 wxRect r = m_grid->BlockToDeviceRect(
576 wxGridCellCoords( row, col ),
577 wxGridCellCoords( row, col ) );
ca65c044 578 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
3665f7d0 579 }
f1567cdd 580
5c8fc7c1 581 // Send event
d95b0c2b
SN
582 if (sendEvent)
583 {
f6bcfd97 584 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
a0bd3147
DS
585 wxEVT_GRID_RANGE_SELECT,
586 m_grid,
587 wxGridCellCoords( row, col ),
588 wxGridCellCoords( row, col ),
589 true,
8b5f6d9d 590 kbd);
a0bd3147 591 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
d95b0c2b 592 }
294f6bcb
SN
593}
594
8b5f6d9d
VZ
595void
596wxGridSelection::ToggleCellSelection(int row, int col,
597 const wxKeyboardState& kbd)
294f6bcb 598{
f1567cdd 599 // if the cell is not selected, select it
294f6bcb 600 if ( !IsInSelection ( row, col ) )
b5808881 601 {
8b5f6d9d 602 SelectCell(row, col, kbd);
a0bd3147 603
b5808881
SN
604 return;
605 }
294f6bcb 606
f1567cdd
SN
607 // otherwise deselect it. This can be simple or more or
608 // less difficult, depending on how the cell is selected.
609 size_t count, n;
610
611 // The simplest case: The cell is contained in m_cellSelection
612 // Then it can't be contained in rows/cols/block (since those
613 // would remove the cell from m_cellSelection on creation), so
614 // we just have to remove it from m_cellSelection.
294f6bcb 615
f1567cdd 616 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb
SN
617 {
618 count = m_cellSelection.GetCount();
f1567cdd 619 for ( n = 0; n < count; n++ )
b5808881 620 {
4e115ed2
VZ
621 const wxGridCellCoords& sel = m_cellSelection[n];
622 if ( row == sel.GetRow() && col == sel.GetCol() )
b5808881 623 {
3665f7d0 624 wxGridCellCoords coords = m_cellSelection[n];
b5808881 625 m_cellSelection.RemoveAt(n);
b5808881 626 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
627 {
628 wxRect r = m_grid->BlockToDeviceRect( coords, coords );
ca65c044 629 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
3665f7d0 630 }
5c8fc7c1
SN
631
632 // Send event
f6bcfd97
BP
633 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
634 wxEVT_GRID_RANGE_SELECT,
635 m_grid,
636 wxGridCellCoords( row, col ),
637 wxGridCellCoords( row, col ),
ca65c044 638 false,
8b5f6d9d 639 kbd );
a0bd3147
DS
640 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
641
723d1b1d 642 return;
b5808881
SN
643 }
644 }
294f6bcb
SN
645 }
646
f1567cdd
SN
647 // The most difficult case: The cell is member of one or even several
648 // blocks. Split each such block in up to 4 new parts, that don't
649 // contain the cell to be selected, like this:
650 // |---------------------------|
651 // | |
652 // | part 1 |
653 // | |
654 // |---------------------------|
655 // | part 3 |x| part 4 |
656 // |---------------------------|
657 // | |
658 // | part 2 |
659 // | |
660 // |---------------------------|
661 // (The x marks the newly deselected cell).
662 // Note: in row selection mode, we only need part1 and part2;
663 // in column selection mode, we only need part 3 and part4,
664 // which are expanded to whole columns automatically!
665
294f6bcb 666 count = m_blockSelectionTopLeft.GetCount();
f1567cdd 667 for ( n = 0; n < count; n++ )
a0bd3147 668 {
b5808881
SN
669 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
670 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
671 int topRow = coords1.GetRow();
672 int leftCol = coords1.GetCol();
673 int bottomRow = coords2.GetRow();
674 int rightCol = coords2.GetCol();
a0bd3147
DS
675
676 if ( BlockContainsCell( topRow, leftCol, bottomRow, rightCol, row, col ) )
b5808881
SN
677 {
678 // remove the block
679 m_blockSelectionTopLeft.RemoveAt(n);
680 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
681 n--;
682 count--;
683
b5808881
SN
684 // add up to 4 smaller blocks and set update region
685 if ( m_selectionMode != wxGrid::wxGridSelectColumns )
686 {
687 if ( topRow < row )
8b5f6d9d 688 SelectBlockNoEvent(topRow, leftCol, row - 1, rightCol);
b5808881 689 if ( bottomRow > row )
8b5f6d9d 690 SelectBlockNoEvent(row + 1, leftCol, bottomRow, rightCol);
b5808881 691 }
a0bd3147 692
b5808881
SN
693 if ( m_selectionMode != wxGrid::wxGridSelectRows )
694 {
695 if ( leftCol < col )
8b5f6d9d 696 SelectBlockNoEvent(row, leftCol, row, col - 1);
b5808881 697 if ( rightCol > col )
8b5f6d9d 698 SelectBlockNoEvent(row, col + 1, row, rightCol);
b5808881
SN
699 }
700 }
294f6bcb
SN
701 }
702
475be9ce 703 bool rowSelectionWasChanged = false;
294f6bcb 704 // remove a cell from a row, adding up to two new blocks
b5808881 705 if ( m_selectionMode != wxGrid::wxGridSelectColumns )
294f6bcb 706 {
f1567cdd
SN
707 count = m_rowSelection.GetCount();
708 for ( n = 0; n < count; n++ )
b5808881
SN
709 {
710 if ( m_rowSelection[n] == row )
711 {
712 m_rowSelection.RemoveAt(n);
a0bd3147
DS
713 n--;
714 count--;
715
475be9ce
VZ
716 rowSelectionWasChanged = true;
717
b5808881
SN
718 if (m_selectionMode == wxGrid::wxGridSelectCells)
719 {
720 if ( col > 0 )
8b5f6d9d 721 SelectBlockNoEvent(row, 0, row, col - 1);
b5808881 722 if ( col < m_grid->GetNumberCols() - 1 )
8b5f6d9d
VZ
723 SelectBlockNoEvent( row, col + 1,
724 row, m_grid->GetNumberCols() - 1);
b5808881
SN
725 }
726 }
727 }
294f6bcb
SN
728 }
729
475be9ce 730 bool colSelectionWasChanged = false;
294f6bcb 731 // remove a cell from a column, adding up to two new blocks
b5808881 732 if ( m_selectionMode != wxGrid::wxGridSelectRows )
294f6bcb 733 {
f1567cdd
SN
734 count = m_colSelection.GetCount();
735 for ( n = 0; n < count; n++ )
b5808881
SN
736 {
737 if ( m_colSelection[n] == col )
738 {
739 m_colSelection.RemoveAt(n);
a0bd3147
DS
740 n--;
741 count--;
742
475be9ce
VZ
743 colSelectionWasChanged = true;
744
b5808881
SN
745 if (m_selectionMode == wxGrid::wxGridSelectCells)
746 {
747 if ( row > 0 )
8b5f6d9d 748 SelectBlockNoEvent(0, col, row - 1, col);
b5808881 749 if ( row < m_grid->GetNumberRows() - 1 )
8b5f6d9d
VZ
750 SelectBlockNoEvent(row + 1, col,
751 m_grid->GetNumberRows() - 1, col);
b5808881
SN
752 }
753 }
754 }
294f6bcb 755 }
f1567cdd 756
5c8fc7c1
SN
757 // Refresh the screen and send the event; according to m_selectionMode,
758 // we need to either update only the cell, or the whole row/column.
294f6bcb 759 wxRect r;
475be9ce 760 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb 761 {
475be9ce 762 if ( !m_grid->GetBatchCount() )
a0bd3147 763 {
475be9ce 764 r = m_grid->BlockToDeviceRect(
a0bd3147 765 wxGridCellCoords( row, col ),
475be9ce
VZ
766 wxGridCellCoords( row, col ) );
767 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
a0bd3147 768 }
3665f7d0 769
475be9ce
VZ
770 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
771 wxEVT_GRID_RANGE_SELECT,
772 m_grid,
773 wxGridCellCoords( row, col ),
774 wxGridCellCoords( row, col ),
775 false,
776 kbd );
777 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
778 }
779 else // rows/columns selection mode
780 {
781 if ( m_selectionMode != wxGrid::wxGridSelectColumns &&
782 rowSelectionWasChanged )
a0bd3147 783 {
475be9ce
VZ
784 int numCols = m_grid->GetNumberCols();
785 for ( int colFrom = 0, colTo = 0; colTo <= numCols; ++colTo )
a0bd3147 786 {
475be9ce
VZ
787 if ( m_colSelection.Index(colTo) >= 0 || colTo == numCols )
788 {
789 if ( colFrom < colTo )
790 {
791 if ( !m_grid->GetBatchCount() )
792 {
793 r = m_grid->BlockToDeviceRect(
794 wxGridCellCoords( row, colFrom ),
795 wxGridCellCoords( row, colTo-1 ) );
796 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
797 }
798
799 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
800 wxEVT_GRID_RANGE_SELECT,
801 m_grid,
802 wxGridCellCoords( row, colFrom ),
803 wxGridCellCoords( row, colTo - 1 ),
804 false,
805 kbd );
806 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
807 }
808
809 colFrom = colTo + 1;
810 }
a0bd3147 811 }
a0bd3147 812 }
a0bd3147 813
475be9ce
VZ
814 if ( m_selectionMode != wxGrid::wxGridSelectRows &&
815 colSelectionWasChanged )
a0bd3147 816 {
475be9ce
VZ
817 int numRows = m_grid->GetNumberRows();
818 for ( int rowFrom = 0, rowTo = 0; rowTo <= numRows; ++rowTo )
a0bd3147 819 {
475be9ce
VZ
820 if ( m_rowSelection.Index(rowTo) >= 0 || rowTo == numRows )
821 {
822 if (rowFrom < rowTo)
823 {
824 if ( !m_grid->GetBatchCount() )
825 {
826 r = m_grid->BlockToDeviceRect(
827 wxGridCellCoords( rowFrom, col ),
828 wxGridCellCoords( rowTo - 1, col ) );
829 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
830 }
831
832 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
833 wxEVT_GRID_RANGE_SELECT,
834 m_grid,
835 wxGridCellCoords( rowFrom, col ),
836 wxGridCellCoords( rowTo - 1, col ),
837 false,
838 kbd );
839 m_grid->GetEventHandler()->ProcessEvent( gridEvt );
840 }
841
842 rowFrom = rowTo + 1;
843 }
a0bd3147 844 }
a0bd3147 845 }
294f6bcb 846 }
294f6bcb
SN
847}
848
849void wxGridSelection::ClearSelection()
850{
851 size_t n;
3665f7d0
SN
852 wxRect r;
853 wxGridCellCoords coords1, coords2;
f1567cdd 854
8862315b 855 // deselect all individual cells and update the screen
f1567cdd 856 if ( m_selectionMode == wxGrid::wxGridSelectCells )
294f6bcb 857 {
8862315b 858 while ( ( n = m_cellSelection.GetCount() ) > 0)
b5808881 859 {
b5808881 860 n--;
3665f7d0 861 coords1 = m_cellSelection[n];
b5808881
SN
862 m_cellSelection.RemoveAt(n);
863 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
864 {
865 r = m_grid->BlockToDeviceRect( coords1, coords1 );
ca65c044 866 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
a0bd3147 867
7d75e6c6
RD
868#ifdef __WXMAC__
869 ((wxWindow *)m_grid->m_gridWin)->Update();
870#endif
3665f7d0 871 }
b5808881 872 }
294f6bcb 873 }
f1567cdd
SN
874
875 // deselect all blocks and update the screen
8862315b 876 while ( ( n = m_blockSelectionTopLeft.GetCount() ) > 0)
294f6bcb 877 {
b5808881 878 n--;
3665f7d0
SN
879 coords1 = m_blockSelectionTopLeft[n];
880 coords2 = m_blockSelectionBottomRight[n];
b5808881
SN
881 m_blockSelectionTopLeft.RemoveAt(n);
882 m_blockSelectionBottomRight.RemoveAt(n);
883 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
884 {
885 r = m_grid->BlockToDeviceRect( coords1, coords2 );
ca65c044 886 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
a0bd3147 887
7d75e6c6
RD
888#ifdef __WXMAC__
889 ((wxWindow *)m_grid->m_gridWin)->Update();
890#endif
3665f7d0 891 }
b5808881 892 }
f1567cdd
SN
893
894 // deselect all rows and update the screen
b5808881 895 if ( m_selectionMode != wxGrid::wxGridSelectColumns )
294f6bcb 896 {
8862315b 897 while ( ( n = m_rowSelection.GetCount() ) > 0)
b5808881
SN
898 {
899 n--;
3665f7d0 900 int row = m_rowSelection[n];
b5808881
SN
901 m_rowSelection.RemoveAt(n);
902 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
903 {
904 r = m_grid->BlockToDeviceRect( wxGridCellCoords( row, 0 ),
905 wxGridCellCoords( row, m_grid->GetNumberCols() - 1 ) );
ca65c044 906 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
a0bd3147 907
7d75e6c6
RD
908#ifdef __WXMAC__
909 ((wxWindow *)m_grid->m_gridWin)->Update();
910#endif
3665f7d0 911 }
b5808881
SN
912 }
913 }
f1567cdd
SN
914
915 // deselect all columns and update the screen
b5808881 916 if ( m_selectionMode != wxGrid::wxGridSelectRows )
294f6bcb 917 {
8862315b 918 while ( ( n = m_colSelection.GetCount() ) > 0)
b5808881
SN
919 {
920 n--;
3665f7d0 921 int col = m_colSelection[n];
b5808881
SN
922 m_colSelection.RemoveAt(n);
923 if ( !m_grid->GetBatchCount() )
3665f7d0
SN
924 {
925 r = m_grid->BlockToDeviceRect( wxGridCellCoords( 0, col ),
926 wxGridCellCoords( m_grid->GetNumberRows() - 1, col ) );
ca65c044 927 ((wxWindow *)m_grid->m_gridWin)->Refresh( false, &r );
a0bd3147 928
7d75e6c6
RD
929#ifdef __WXMAC__
930 ((wxWindow *)m_grid->m_gridWin)->Update();
931#endif
3665f7d0 932 }
b5808881 933 }
294f6bcb 934 }
f6bcfd97
BP
935
936 // One deselection event, indicating deselection of _all_ cells.
937 // (No finer grained events for each of the smaller regions
938 // deselected above!)
939 wxGridRangeSelectEvent gridEvt( m_grid->GetId(),
940 wxEVT_GRID_RANGE_SELECT,
941 m_grid,
942 wxGridCellCoords( 0, 0 ),
a0bd3147
DS
943 wxGridCellCoords(
944 m_grid->GetNumberRows() - 1,
945 m_grid->GetNumberCols() - 1 ),
ca65c044 946 false );
f6bcfd97
BP
947
948 m_grid->GetEventHandler()->ProcessEvent(gridEvt);
294f6bcb
SN
949}
950
951
952void wxGridSelection::UpdateRows( size_t pos, int numRows )
953{
954 size_t count = m_cellSelection.GetCount();
b14159f7
JS
955 size_t n;
956 for ( n = 0; n < count; n++ )
294f6bcb
SN
957 {
958 wxGridCellCoords& coords = m_cellSelection[n];
959 wxCoord row = coords.GetRow();
960 if ((size_t)row >= pos)
961 {
962 if (numRows > 0)
963 {
964 // If rows inserted, increase row counter where necessary
965 coords.SetRow(row + numRows);
966 }
967 else if (numRows < 0)
968 {
969 // If rows deleted ...
970 if ((size_t)row >= pos - numRows)
971 {
972 // ...either decrement row counter (if row still exists)...
973 coords.SetRow(row + numRows);
974 }
975 else
976 {
977 // ...or remove the attribute
978 m_cellSelection.RemoveAt(n);
a0bd3147
DS
979 n--;
980 count--;
294f6bcb
SN
981 }
982 }
983 }
984 }
985
986 count = m_blockSelectionTopLeft.GetCount();
b14159f7 987 for ( n = 0; n < count; n++ )
294f6bcb
SN
988 {
989 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
990 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
991 wxCoord row1 = coords1.GetRow();
992 wxCoord row2 = coords2.GetRow();
a0bd3147 993
294f6bcb
SN
994 if ((size_t)row2 >= pos)
995 {
996 if (numRows > 0)
997 {
998 // If rows inserted, increase row counter where necessary
a0bd3147
DS
999 coords2.SetRow( row2 + numRows );
1000 if ((size_t)row1 >= pos)
1001 coords1.SetRow( row1 + numRows );
294f6bcb
SN
1002 }
1003 else if (numRows < 0)
1004 {
1005 // If rows deleted ...
1006 if ((size_t)row2 >= pos - numRows)
1007 {
1008 // ...either decrement row counter (if row still exists)...
a0bd3147
DS
1009 coords2.SetRow( row2 + numRows );
1010 if ((size_t)row1 >= pos)
1011 coords1.SetRow( wxMax(row1 + numRows, (int)pos) );
f1e26920 1012
294f6bcb
SN
1013 }
1014 else
1015 {
a0bd3147 1016 if ((size_t)row1 >= pos)
b5808881
SN
1017 {
1018 // ...or remove the attribute
1019 m_blockSelectionTopLeft.RemoveAt(n);
1020 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
1021 n--;
1022 count--;
b5808881
SN
1023 }
1024 else
a0bd3147 1025 coords2.SetRow( pos );
294f6bcb
SN
1026 }
1027 }
1028 }
1029 }
1030
1031 count = m_rowSelection.GetCount();
b14159f7 1032 for ( n = 0; n < count; n++ )
294f6bcb 1033 {
a0bd3147 1034 int rowOrCol_ = m_rowSelection[n];
f1e26920 1035
a0bd3147 1036 if ((size_t) rowOrCol_ >= pos)
f1e26920
CE
1037 {
1038 if ( numRows > 0 )
1039 {
a0bd3147 1040 m_rowSelection[n] += numRows;
f1e26920
CE
1041 }
1042 else if ( numRows < 0 )
1043 {
a0bd3147
DS
1044 if ((size_t)rowOrCol_ >= (pos - numRows))
1045 m_rowSelection[n] += numRows;
f1e26920
CE
1046 else
1047 {
a0bd3147 1048 m_rowSelection.RemoveAt( n );
f1e26920
CE
1049 n--;
1050 count--;
1051 }
1052 }
1053 }
294f6bcb 1054 }
f6bcfd97
BP
1055 // No need to touch selected columns, unless we removed _all_
1056 // rows, in this case, we remove all columns from the selection.
f1e26920 1057
f6bcfd97
BP
1058 if ( !m_grid->GetNumberRows() )
1059 m_colSelection.Clear();
294f6bcb
SN
1060}
1061
f1e26920 1062
294f6bcb
SN
1063void wxGridSelection::UpdateCols( size_t pos, int numCols )
1064{
1065 size_t count = m_cellSelection.GetCount();
b14159f7 1066 size_t n;
a0bd3147 1067
b14159f7 1068 for ( n = 0; n < count; n++ )
294f6bcb
SN
1069 {
1070 wxGridCellCoords& coords = m_cellSelection[n];
1071 wxCoord col = coords.GetCol();
1072 if ((size_t)col >= pos)
1073 {
1074 if (numCols > 0)
1075 {
1076 // If rows inserted, increase row counter where necessary
1077 coords.SetCol(col + numCols);
1078 }
1079 else if (numCols < 0)
1080 {
1081 // If rows deleted ...
1082 if ((size_t)col >= pos - numCols)
1083 {
1084 // ...either decrement row counter (if row still exists)...
1085 coords.SetCol(col + numCols);
1086 }
1087 else
1088 {
1089 // ...or remove the attribute
1090 m_cellSelection.RemoveAt(n);
a0bd3147
DS
1091 n--;
1092 count--;
294f6bcb
SN
1093 }
1094 }
1095 }
1096 }
1097
1098 count = m_blockSelectionTopLeft.GetCount();
b14159f7 1099 for ( n = 0; n < count; n++ )
294f6bcb
SN
1100 {
1101 wxGridCellCoords& coords1 = m_blockSelectionTopLeft[n];
1102 wxGridCellCoords& coords2 = m_blockSelectionBottomRight[n];
1103 wxCoord col1 = coords1.GetCol();
1104 wxCoord col2 = coords2.GetCol();
a0bd3147 1105
294f6bcb
SN
1106 if ((size_t)col2 >= pos)
1107 {
1108 if (numCols > 0)
1109 {
1110 // If rows inserted, increase row counter where necessary
1111 coords2.SetCol(col2 + numCols);
a0bd3147 1112 if ((size_t)col1 >= pos)
b5808881 1113 coords1.SetCol(col1 + numCols);
294f6bcb
SN
1114 }
1115 else if (numCols < 0)
1116 {
1117 // If cols deleted ...
1118 if ((size_t)col2 >= pos - numCols)
1119 {
1120 // ...either decrement col counter (if col still exists)...
1121 coords2.SetCol(col2 + numCols);
b5808881 1122 if ( (size_t) col1 >= pos)
a0bd3147 1123 coords1.SetCol( wxMax(col1 + numCols, (int)pos) );
f1e26920 1124
294f6bcb
SN
1125 }
1126 else
1127 {
a0bd3147 1128 if ((size_t)col1 >= pos)
b5808881
SN
1129 {
1130 // ...or remove the attribute
1131 m_blockSelectionTopLeft.RemoveAt(n);
1132 m_blockSelectionBottomRight.RemoveAt(n);
a0bd3147
DS
1133 n--;
1134 count--;
b5808881
SN
1135 }
1136 else
1137 coords2.SetCol(pos);
294f6bcb
SN
1138 }
1139 }
1140 }
1141 }
1142
1143 count = m_colSelection.GetCount();
b14159f7 1144 for ( n = 0; n < count; n++ )
294f6bcb 1145 {
a0bd3147 1146 int rowOrCol = m_colSelection[n];
f1e26920 1147
a0bd3147 1148 if ((size_t)rowOrCol >= pos)
294f6bcb
SN
1149 {
1150 if ( numCols > 0 )
a0bd3147 1151 m_colSelection[n] += numCols;
f1e26920 1152 else if ( numCols < 0 )
294f6bcb 1153 {
a0bd3147
DS
1154 if ((size_t)rowOrCol >= (pos - numCols))
1155 m_colSelection[n] += numCols;
294f6bcb
SN
1156 else
1157 {
a0bd3147 1158 m_colSelection.RemoveAt( n );
f1e26920
CE
1159 n--;
1160 count--;
294f6bcb
SN
1161 }
1162 }
1163 }
1164 }
f6bcfd97
BP
1165
1166 // No need to touch selected rows, unless we removed _all_
1167 // columns, in this case, we remove all rows from the selection.
1168 if ( !m_grid->GetNumberCols() )
1169 m_rowSelection.Clear();
294f6bcb
SN
1170}
1171
1172int wxGridSelection::BlockContain( int topRow1, int leftCol1,
b5808881
SN
1173 int bottomRow1, int rightCol1,
1174 int topRow2, int leftCol2,
1175 int bottomRow2, int rightCol2 )
294f6bcb
SN
1176// returns 1, if Block1 contains Block2,
1177// -1, if Block2 contains Block1,
1178// 0, otherwise
1179{
1180 if ( topRow1 <= topRow2 && bottomRow2 <= bottomRow1 &&
b5808881 1181 leftCol1 <= leftCol2 && rightCol2 <= rightCol1 )
294f6bcb
SN
1182 return 1;
1183 else if ( topRow2 <= topRow1 && bottomRow1 <= bottomRow2 &&
b5808881 1184 leftCol2 <= leftCol1 && rightCol1 <= rightCol2 )
294f6bcb 1185 return -1;
a0bd3147 1186
294f6bcb
SN
1187 return 0;
1188}
f1567cdd
SN
1189
1190#endif