KiCad PCB EDA Suite
Loading...
Searching...
No Matches
grid_tricks.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <grid_tricks.h>
26#include <wx/defs.h>
27#include <wx/event.h>
28#include <wx/tokenzr.h>
29#include <wx/clipbrd.h>
30#include <wx/log.h>
31#include <wx/stc/stc.h>
33
34
35// It works for table data on clipboard for an Excel spreadsheet,
36// why not us too for now.
37#define COL_SEP wxT( '\t' )
38#define ROW_SEP wxT( '\n' )
39#define ROW_SEP_R wxT( '\r' )
40
41
43 m_grid( aGrid ),
44 m_addHandler( []( wxCommandEvent& ) {} ),
45 m_enableSingleClickEdit( true ),
46 m_multiCellEditEnabled( true )
47{
48 init();
49}
50
51
52GRID_TRICKS::GRID_TRICKS( WX_GRID* aGrid, std::function<void( wxCommandEvent& )> aAddHandler ) :
53 m_grid( aGrid ),
54 m_addHandler( aAddHandler ),
55 m_enableSingleClickEdit( true ),
56 m_multiCellEditEnabled( true )
57{
58 init();
59}
60
61
63{
68
69 m_grid->Connect( wxEVT_GRID_CELL_LEFT_CLICK,
70 wxGridEventHandler( GRID_TRICKS::onGridCellLeftClick ), nullptr, this );
71 m_grid->Connect( wxEVT_GRID_CELL_LEFT_DCLICK,
72 wxGridEventHandler( GRID_TRICKS::onGridCellLeftDClick ), nullptr, this );
73 m_grid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK,
74 wxGridEventHandler( GRID_TRICKS::onGridCellRightClick ), nullptr, this );
75 m_grid->Connect( wxEVT_GRID_LABEL_RIGHT_CLICK,
76 wxGridEventHandler( GRID_TRICKS::onGridLabelRightClick ), nullptr, this );
77 m_grid->Connect( wxEVT_GRID_LABEL_LEFT_CLICK,
78 wxGridEventHandler( GRID_TRICKS::onGridLabelLeftClick ), nullptr, this );
79 m_grid->Connect( GRIDTRICKS_FIRST_ID, GRIDTRICKS_LAST_ID, wxEVT_COMMAND_MENU_SELECTED,
80 wxCommandEventHandler( GRID_TRICKS::onPopupSelection ), nullptr, this );
81 m_grid->Connect( wxEVT_CHAR_HOOK,
82 wxCharEventHandler( GRID_TRICKS::onCharHook ), nullptr, this );
83 m_grid->Connect( wxEVT_KEY_DOWN,
84 wxKeyEventHandler( GRID_TRICKS::onKeyDown ), nullptr, this );
85 m_grid->Connect( wxEVT_UPDATE_UI,
86 wxUpdateUIEventHandler( GRID_TRICKS::onUpdateUI ), nullptr, this );
87
88 // The handlers that control the tooltips must be on the actual grid window, not the grid
89 m_grid->GetGridWindow()->Connect( wxEVT_MOTION,
90 wxMouseEventHandler( GRID_TRICKS::onGridMotion ), nullptr,
91 this );
92}
93
94
95bool GRID_TRICKS::isTextEntry( int aRow, int aCol )
96{
97 wxGridCellEditor* editor = m_grid->GetCellEditor( aRow, aCol );
98 bool retval = ( dynamic_cast<wxTextEntry*>( editor )
99 || dynamic_cast<GRID_CELL_STC_EDITOR*>( editor ) );
100
101 editor->DecRef();
102 return retval;
103}
104
105
106bool GRID_TRICKS::isCheckbox( int aRow, int aCol )
107{
108 wxGridCellRenderer* renderer = m_grid->GetCellRenderer( aRow, aCol );
109 bool retval = ( dynamic_cast<wxGridCellBoolRenderer*>( renderer ) );
110
111 renderer->DecRef();
112 return retval;
113}
114
115
116bool GRID_TRICKS::isReadOnly( int aRow, int aCol )
117{
118 return !m_grid->IsEditable() || m_grid->IsReadOnly( aRow, aCol );
119}
120
121
122bool GRID_TRICKS::toggleCell( int aRow, int aCol, bool aPreserveSelection )
123{
124 if( isCheckbox( aRow, aCol ) )
125 {
126 if( !aPreserveSelection )
127 {
128 m_grid->ClearSelection();
129 m_grid->SetGridCursor( aRow, aCol );
130 }
131
132 wxGridTableBase* model = m_grid->GetTable();
133
134 if( model->CanGetValueAs( aRow, aCol, wxGRID_VALUE_BOOL )
135 && model->CanSetValueAs( aRow, aCol, wxGRID_VALUE_BOOL ) )
136 {
137 model->SetValueAsBool( aRow, aCol, !model->GetValueAsBool( aRow, aCol ) );
138 }
139 else // fall back to string processing
140 {
141 if( model->GetValue( aRow, aCol ) == wxT( "1" ) )
142 model->SetValue( aRow, aCol, wxT( "0" ) );
143 else
144 model->SetValue( aRow, aCol, wxT( "1" ) );
145 }
146
147 // Mac needs this for the keyboard events; Linux appears to always need it.
148 m_grid->ForceRefresh();
149
150 // Let any clients know
151 wxGridEvent event( m_grid->GetId(), wxEVT_GRID_CELL_CHANGED, m_grid, aRow, aCol );
152 event.SetString( model->GetValue( aRow, aCol ) );
153 m_grid->GetEventHandler()->ProcessEvent( event );
154
155 return true;
156 }
157
158 return false;
159}
160
161
162bool GRID_TRICKS::showEditor( int aRow, int aCol )
163{
164 if( m_grid->GetGridCursorRow() != aRow || m_grid->GetGridCursorCol() != aCol )
165 m_grid->SetGridCursor( aRow, aCol );
166
167 if( !isReadOnly( aRow, aCol ) )
168 {
169 m_grid->ClearSelection();
170
171 m_sel_row_start = aRow;
172 m_sel_col_start = aCol;
173 m_sel_row_count = 1;
174 m_sel_col_count = 1;
175
176 if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows )
177 {
178 wxArrayInt rows = m_grid->GetSelectedRows();
179
180 if( rows.size() != 1 || rows.Item( 0 ) != aRow )
181 m_grid->SelectRow( aRow );
182 }
183
184 // For several reasons we can't enable the control here. There's the whole
185 // SetInSetFocus() issue/hack in wxWidgets, and there's also wxGrid's MouseUp
186 // handler which doesn't notice it's processing a MouseUp until after it has
187 // disabled the editor yet again. So we re-use wxWidgets' slow-click hack,
188 // which is processed later in the MouseUp handler.
189 //
190 // It should be pointed out that the fact that it's wxWidgets' hack doesn't
191 // make it any less of a hack. Be extra careful with any modifications here.
192 // See, in particular, https://bugs.launchpad.net/kicad/+bug/1817965.
194
195 return true;
196 }
197
198 return false;
199}
200
201
202void GRID_TRICKS::onGridCellLeftClick( wxGridEvent& aEvent )
203{
204 int row = aEvent.GetRow();
205 int col = aEvent.GetCol();
206
207 // Don't make users click twice to toggle a checkbox or edit a text cell
208 if( !aEvent.GetModifiers() )
209 {
210 bool toggled = false;
211
212 if( toggleCell( row, col, true ) )
213 toggled = true;
214 else if( m_enableSingleClickEdit && showEditor( row, col ) )
215 return;
216
217 // Apply checkbox changes to multi-selection.
218 // Non-checkbox changes handled elsewhere
219 if( toggled )
220 {
222
223 // We only want to apply this to whole rows. If the grid allows selecting individual
224 // cells, and the selection contains dijoint cells, skip this logic.
225 if( !m_grid->GetSelectedCells().IsEmpty() || m_sel_row_count < 2 )
226 {
227 // We preserved the selection in toggleCell above; so clear it now that we know
228 // we aren't doing a multi-select edit
229 m_grid->ClearSelection();
230 return;
231 }
232
233 wxString newVal = m_grid->GetCellValue( row, col );
234
235 for( int otherRow = m_sel_row_start; otherRow < m_sel_row_start + m_sel_row_count; ++otherRow )
236 {
237 if( otherRow == row )
238 continue;
239
240 m_grid->SetCellValue( otherRow, col, newVal );
241 }
242
243 return;
244 }
245 }
246
247 aEvent.Skip();
248}
249
250
251void GRID_TRICKS::onGridCellLeftDClick( wxGridEvent& aEvent )
252{
253 if( !handleDoubleClick( aEvent ) )
254 onGridCellLeftClick( aEvent );
255}
256
257
258void GRID_TRICKS::onGridMotion( wxMouseEvent& aEvent )
259{
260 // Always skip the event
261 aEvent.Skip();
262
263 wxPoint pt = aEvent.GetPosition();
264 wxPoint pos = m_grid->CalcScrolledPosition( wxPoint( pt.x, pt.y ) );
265
266 int col = m_grid->XToCol( pos.x );
267 int row = m_grid->YToRow( pos.y );
268
269 // Empty tooltip if the cell doesn't exist or the column doesn't have tooltips
270 if( ( col == wxNOT_FOUND ) || ( row == wxNOT_FOUND ) || !m_tooltipEnabled[col] )
271 {
272 m_grid->GetGridWindow()->SetToolTip( wxS( "" ) );
273 return;
274 }
275
276 // Set the tooltip to the string contained in the cell
277 m_grid->GetGridWindow()->SetToolTip( m_grid->GetCellValue( row, col ) );
278}
279
280
281bool GRID_TRICKS::handleDoubleClick( wxGridEvent& aEvent )
282{
283 // Double-click processing must be handled by specific sub-classes
284 return false;
285}
286
287
289{
290 wxGridCellCoordsArray topLeft = m_grid->GetSelectionBlockTopLeft();
291 wxGridCellCoordsArray botRight = m_grid->GetSelectionBlockBottomRight();
292
293 wxArrayInt cols = m_grid->GetSelectedCols();
294 wxArrayInt rows = m_grid->GetSelectedRows();
295
296 if( topLeft.Count() && botRight.Count() )
297 {
298 m_sel_row_start = topLeft[0].GetRow();
299 m_sel_col_start = topLeft[0].GetCol();
300
301 m_sel_row_count = botRight[0].GetRow() - m_sel_row_start + 1;
302 m_sel_col_count = botRight[0].GetCol() - m_sel_col_start + 1;
303 }
304 else if( cols.Count() )
305 {
306 m_sel_col_start = cols[0];
307 m_sel_col_count = cols.Count();
308 m_sel_row_start = 0;
309 m_sel_row_count = m_grid->GetNumberRows();
310 }
311 else if( rows.Count() )
312 {
313 m_sel_col_start = 0;
314 m_sel_col_count = m_grid->GetNumberCols();
315 m_sel_row_start = rows[0];
316 m_sel_row_count = rows.Count();
317 }
318 else
319 {
320 m_sel_row_start = m_grid->GetGridCursorRow();
321 m_sel_col_start = m_grid->GetGridCursorCol();
322 m_sel_row_count = m_sel_row_start >= 0 ? 1 : 0;
323 m_sel_col_count = m_sel_col_start >= 0 ? 1 : 0;
324 }
325}
326
327
328void GRID_TRICKS::onGridCellRightClick( wxGridEvent& aEvent )
329{
330 wxMenu menu;
331
332 showPopupMenu( menu, aEvent );
333}
334
335
336void GRID_TRICKS::onGridLabelLeftClick( wxGridEvent& aEvent )
337{
339
340 aEvent.Skip();
341}
342
343
345{
346 wxMenu menu;
347
348 for( int i = 0; i < m_grid->GetNumberCols(); ++i )
349 {
350 int id = GRIDTRICKS_FIRST_SHOWHIDE + i;
351 menu.AppendCheckItem( id, m_grid->GetColLabelValue( i ) );
352 menu.Check( id, m_grid->IsColShown( i ) );
353 }
354
355 m_grid->PopupMenu( &menu );
356}
357
358
359void GRID_TRICKS::showPopupMenu( wxMenu& menu, wxGridEvent& aEvent )
360{
361 menu.Append( GRIDTRICKS_ID_CUT, _( "Cut" ) + "\tCtrl+X",
362 _( "Clear selected cells placing original contents on clipboard" ) );
363 menu.Append( GRIDTRICKS_ID_COPY, _( "Copy" ) + "\tCtrl+C",
364 _( "Copy selected cells to clipboard" ) );
365
367 {
368 menu.Append( GRIDTRICKS_ID_PASTE, _( "Paste" ) + "\tCtrl+V",
369 _( "Paste clipboard cells to matrix at current cell" ) );
370 menu.Append( GRIDTRICKS_ID_DELETE, _( "Delete" ) + "\tDel",
371 _( "Clear contents of selected cells" ) );
372 }
373
374 menu.Append( GRIDTRICKS_ID_SELECT, _( "Select All" ) + "\tCtrl+A",
375 _( "Select all cells" ) );
376
377 menu.Enable( GRIDTRICKS_ID_CUT, false );
378 menu.Enable( GRIDTRICKS_ID_DELETE, false );
379 menu.Enable( GRIDTRICKS_ID_PASTE, false );
380
382
383 auto anyCellsWritable =
384 [&]()
385 {
386 for( int row = m_sel_row_start; row < m_sel_row_start + m_sel_row_count; ++row )
387 {
388 for( int col = m_sel_col_start; col < m_sel_col_start + m_sel_col_count; ++col )
389 {
390 if( !isReadOnly( row, col ) && isTextEntry( row, col ) )
391 return true;
392 }
393 }
394
395 return false;
396 };
397
398 if( anyCellsWritable() )
399 {
400 menu.Enable( GRIDTRICKS_ID_CUT, true );
401 menu.Enable( GRIDTRICKS_ID_DELETE, true );
402 }
403
404 // Paste can overflow the selection, so don't depend on the particular cell being writeable.
405
406 wxLogNull doNotLog; // disable logging of failed clipboard actions
407
408 if( wxTheClipboard->Open() )
409 {
410 if( wxTheClipboard->IsSupported( wxDF_TEXT )
411 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
412 {
413 if( m_grid->IsEditable() )
414 menu.Enable( GRIDTRICKS_ID_PASTE, true );
415 }
416
417 wxTheClipboard->Close();
418 }
419
420 m_grid->PopupMenu( &menu );
421}
422
423
424void GRID_TRICKS::onPopupSelection( wxCommandEvent& event )
425{
426 doPopupSelection( event );
427}
428
429
430void GRID_TRICKS::doPopupSelection( wxCommandEvent& event )
431{
432 int menu_id = event.GetId();
433
434 // assume getSelectedArea() was called by rightClickPopupMenu() and there's
435 // no way to have gotten here without that having been called.
436
437 switch( menu_id )
438 {
440 cutcopy( true, true );
441 break;
442
444 cutcopy( true, false );
445 break;
446
448 cutcopy( false, true );
449 break;
450
453 break;
454
456 m_grid->SelectAll();
457 break;
458
459 default:
460 if( menu_id >= GRIDTRICKS_FIRST_SHOWHIDE && m_grid->CommitPendingChanges( false ) )
461 {
462 int col = menu_id - GRIDTRICKS_FIRST_SHOWHIDE;
463
464 if( m_grid->IsColShown( col ) )
465 m_grid->HideCol( col );
466 else
467 m_grid->ShowCol( col );
468 }
469 }
470}
471
472
473void GRID_TRICKS::onCharHook( wxKeyEvent& ev )
474{
475 bool handled = false;
476
477 if( ( ev.GetKeyCode() == WXK_RETURN || ev.GetKeyCode() == WXK_NUMPAD_ENTER )
478 && ev.GetModifiers() == wxMOD_NONE
479 && m_grid->GetGridCursorRow() == m_grid->GetNumberRows() - 1 )
480 {
481 if( m_grid->IsCellEditControlShown() )
482 {
484 handled = true;
485 }
486 else
487 {
488 wxCommandEvent dummy;
490 handled = true;
491 }
492 }
493 else if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'V' )
494 {
495 if( m_grid->IsCellEditControlShown() && wxTheClipboard->Open() )
496 {
497 if( wxTheClipboard->IsSupported( wxDF_TEXT )
498 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
499 {
500 wxTextDataObject data;
501 wxTheClipboard->GetData( data );
502
503 if( data.GetText().Contains( COL_SEP ) || data.GetText().Contains( ROW_SEP ) )
504 {
505 wxString stripped( data.GetText() );
506 stripped.Replace( ROW_SEP, " " );
507 stripped.Replace( ROW_SEP_R, " " );
508 stripped.Replace( COL_SEP, " " );
509
510 // Write to the CellEditControl if we can
511 wxTextEntry* te = dynamic_cast<wxTextEntry*>( ev.GetEventObject() );
512
513 if( te && te->IsEditable() )
514 te->WriteText( stripped );
515 else
516 paste_text( stripped );
517
518 handled = true;
519 }
520 }
521
522 wxTheClipboard->Close();
523 m_grid->ForceRefresh();
524 }
525 }
526 else if( ev.GetKeyCode() == WXK_ESCAPE )
527 {
528 if( m_grid->IsCellEditControlShown() )
529 {
531 handled = true;
532 }
533 }
534
535 if( !handled )
536 ev.Skip( true );
537}
538
539
540void GRID_TRICKS::onKeyDown( wxKeyEvent& ev )
541{
542 if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'A' )
543 {
544 m_grid->SelectAll();
545 return;
546 }
547 else if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'C' )
548 {
550 cutcopy( true, false );
551 return;
552 }
553 else if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'V' )
554 {
557 return;
558 }
559 else if( ev.GetModifiers() == wxMOD_CONTROL && ev.GetKeyCode() == 'X' )
560 {
562 cutcopy( true, true );
563 return;
564 }
565 else if( !ev.GetModifiers() && ev.GetKeyCode() == WXK_DELETE )
566 {
568 cutcopy( false, true );
569 return;
570 }
571
572 // space-bar toggling of checkboxes
573 if( m_grid->IsEditable() && ev.GetKeyCode() == ' ' )
574 {
575 bool retVal = false;
576
577 // If only rows can be selected, only toggle the first cell in a row
578 if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows )
579 {
580 wxArrayInt rowSel = m_grid->GetSelectedRows();
581
582 for( unsigned int rowInd = 0; rowInd < rowSel.GetCount(); rowInd++ )
583 retVal |= toggleCell( rowSel[rowInd], 0, true );
584 }
585
586 // If only columns can be selected, only toggle the first cell in a column
587 else if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectColumns )
588 {
589 wxArrayInt colSel = m_grid->GetSelectedCols();
590
591 for( unsigned int colInd = 0; colInd < colSel.GetCount(); colInd++ )
592 retVal |= toggleCell( 0, colSel[colInd], true );
593 }
594
595 // If the user can select the individual cells, toggle each cell selected
596 else if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectCells )
597 {
598 wxArrayInt rowSel = m_grid->GetSelectedRows();
599 wxArrayInt colSel = m_grid->GetSelectedCols();
600 wxGridCellCoordsArray cellSel = m_grid->GetSelectedCells();
601 wxGridCellCoordsArray topLeft = m_grid->GetSelectionBlockTopLeft();
602 wxGridCellCoordsArray botRight = m_grid->GetSelectionBlockBottomRight();
603
604 // Iterate over every individually selected cell and try to toggle it
605 for( unsigned int cellInd = 0; cellInd < cellSel.GetCount(); cellInd++ )
606 {
607 retVal |= toggleCell( cellSel[cellInd].GetRow(), cellSel[cellInd].GetCol(), true );
608 }
609
610 // Iterate over every column and try to toggle each cell in it
611 for( unsigned int colInd = 0; colInd < colSel.GetCount(); colInd++ )
612 {
613 for( int row = 0; row < m_grid->GetNumberRows(); row++ )
614 retVal |= toggleCell( row, colSel[colInd], true );
615 }
616
617 // Iterate over every row and try to toggle each cell in it
618 for( unsigned int rowInd = 0; rowInd < rowSel.GetCount(); rowInd++ )
619 {
620 for( int col = 0; col < m_grid->GetNumberCols(); col++ )
621 retVal |= toggleCell( rowSel[rowInd], col, true );
622 }
623
624 // Iterate over the selection blocks
625 for( unsigned int blockInd = 0; blockInd < topLeft.GetCount(); blockInd++ )
626 {
627 wxGridCellCoords start = topLeft[blockInd];
628 wxGridCellCoords end = botRight[blockInd];
629
630 for( int row = start.GetRow(); row <= end.GetRow(); row++ )
631 {
632 for( int col = start.GetCol(); col <= end.GetCol(); col++ )
633 retVal |= toggleCell( row, col, true );
634 }
635 }
636 }
637
638 // Return if there were any cells toggled
639 if( retVal )
640 return;
641 }
642
643 // ctrl-tab for exit grid
644#ifdef __APPLE__
645 bool ctrl = ev.RawControlDown();
646#else
647 bool ctrl = ev.ControlDown();
648#endif
649
650 if( ctrl && ev.GetKeyCode() == WXK_TAB )
651 {
652 wxWindow* test = m_grid->GetNextSibling();
653
654 if( !test )
655 test = m_grid->GetParent()->GetNextSibling();
656
657 while( test && !test->IsTopLevel() )
658 {
659 test->SetFocus();
660
661 if( test->HasFocus() )
662 break;
663
664 if( !test->GetChildren().empty() )
665 {
666 test = test->GetChildren().front();
667 }
668 else if( test->GetNextSibling() )
669 {
670 test = test->GetNextSibling();
671 }
672 else
673 {
674 while( test )
675 {
676 test = test->GetParent();
677
678 if( test && test->IsTopLevel() )
679 {
680 break;
681 }
682 else if( test && test->GetNextSibling() )
683 {
684 test = test->GetNextSibling();
685 break;
686 }
687 }
688 }
689 }
690
691 return;
692 }
693
694 ev.Skip( true );
695}
696
697
699{
700 wxLogNull doNotLog; // disable logging of failed clipboard actions
701
702 if( m_grid->IsEditable() && wxTheClipboard->Open() )
703 {
704 if( wxTheClipboard->IsSupported( wxDF_TEXT )
705 || wxTheClipboard->IsSupported( wxDF_UNICODETEXT ) )
706 {
707 wxTextDataObject data;
708
709 wxTheClipboard->GetData( data );
710
711 wxString text = data.GetText();
712
713#ifdef __WXMAC__
714 // Some editors use windows linefeeds (\r\n), which wx re-writes to \n\n
715 text.Replace( "\n\n", "\n" );
716#endif
718 paste_text( text );
719 }
720
721 wxTheClipboard->Close();
722 m_grid->ForceRefresh();
723 }
724}
725
726
727void GRID_TRICKS::paste_text( const wxString& cb_text )
728{
730 return;
731
732 wxGridTableBase* tbl = m_grid->GetTable();
733
734 const int cur_row = m_grid->GetGridCursorRow();
735 const int cur_col = m_grid->GetGridCursorCol();
736 int start_row;
737 int end_row;
738 int start_col;
739 int end_col;
740 bool is_selection = false;
741
742 if( cur_row < 0 || cur_col < 0 )
743 {
744 wxBell();
745 return;
746 }
747
748 if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows )
749 {
750 if( m_sel_row_count > 1 )
751 is_selection = true;
752 }
753 else if( m_sel_col_count > 1 || m_sel_row_count > 1 )
754 {
755 is_selection = true;
756 }
757
758 wxStringTokenizer rows( cb_text, ROW_SEP, wxTOKEN_RET_EMPTY );
759
760 // If selection of cells is present
761 // then a clipboard pastes to selected cells only.
762 if( is_selection )
763 {
764 start_row = m_sel_row_start;
766 start_col = m_sel_col_start;
768 }
769 // Otherwise, paste whole clipboard
770 // starting from cell with cursor.
771 else
772 {
773 start_row = cur_row;
774 end_row = cur_row + rows.CountTokens();
775
776 if( end_row > tbl->GetNumberRows() )
777 {
778 if( m_addHandler )
779 {
780 for( int ii = end_row - tbl->GetNumberRows(); ii > 0; --ii )
781 {
782 wxCommandEvent dummy;
784 }
785 }
786
787 end_row = tbl->GetNumberRows();
788 }
789
790 start_col = cur_col;
791 end_col = start_col; // end_col actual value calculates later
792 }
793
794 for( int row = start_row; row < end_row; ++row )
795 {
796 // If number of selected rows is larger than the count of rows on the clipboard, paste
797 // again and again until the end of the selection is reached.
798 if( !rows.HasMoreTokens() )
799 rows.SetString( cb_text, ROW_SEP, wxTOKEN_RET_EMPTY );
800
801 wxString rowTxt = rows.GetNextToken();
802
803 wxStringTokenizer cols( rowTxt, COL_SEP, wxTOKEN_RET_EMPTY );
804
805 if( !is_selection )
806 end_col = cur_col + cols.CountTokens();
807
808 for( int col = start_col; col < end_col && col < tbl->GetNumberCols(); ++col )
809 {
810 // Skip hidden columns
811 if( !m_grid->IsColShown( col ) )
812 {
813 end_col++;
814 continue;
815 }
816
817 // If number of selected cols is larger than the count of cols on the clipboard,
818 // paste again and again until the end of the selection is reached.
819 if( !cols.HasMoreTokens() )
820 cols.SetString( rowTxt, COL_SEP, wxTOKEN_RET_EMPTY );
821
822 wxString cellTxt = cols.GetNextToken();
823
824 // Allow paste to anything that can take a string, including things like color
825 // swatches and checkboxes
826 if( tbl->CanSetValueAs( row, col, wxGRID_VALUE_STRING ) && !isReadOnly( row, col ) )
827 {
828 tbl->SetValue( row, col, cellTxt );
829
830 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_CHANGED, m_grid, row, col );
831 m_grid->GetEventHandler()->ProcessEvent( evt );
832 }
833 // Allow paste to any cell that can accept a boolean value
834 else if( tbl->CanSetValueAs( row, col, wxGRID_VALUE_BOOL ) )
835 {
836 tbl->SetValueAsBool( row, col, cellTxt == wxT( "1" ) );
837
838 wxGridEvent evt( m_grid->GetId(), wxEVT_GRID_CELL_CHANGED, m_grid, row, col );
839 m_grid->GetEventHandler()->ProcessEvent( evt );
840 }
841 }
842 }
843}
844
845
846void GRID_TRICKS::cutcopy( bool doCopy, bool doDelete )
847{
848 wxLogNull doNotLog; // disable logging of failed clipboard actions
849
850 if( doCopy && !wxTheClipboard->Open() )
851 return;
852
853 wxGridTableBase* tbl = m_grid->GetTable();
854 wxString txt;
855
856 // fill txt with a format that is compatible with most spreadsheets
857 for( int row = m_sel_row_start; row < m_sel_row_start + m_sel_row_count; ++row )
858 {
859 if( !txt.IsEmpty() )
860 txt += ROW_SEP;
861
862 for( int col = m_sel_col_start; col < m_sel_col_start + m_sel_col_count; ++col )
863 {
864 if( !m_grid->IsColShown( col ) )
865 continue;
866
867 txt += tbl->GetValue( row, col );
868
869 if( col < m_sel_col_start + m_sel_col_count - 1 ) // that was not last column
870 txt += COL_SEP;
871
872 if( doDelete )
873 {
874 // Do NOT allow clear of things that can take strings but aren't textEntries
875 // (ie: color swatches, textboxes, etc.).
876 if( isTextEntry( row, col ) && !isReadOnly( row, col ) )
877 tbl->SetValue( row, col, wxEmptyString );
878 }
879 }
880 }
881
882 if( doCopy )
883 {
884 wxTheClipboard->SetData( new wxTextDataObject( txt ) );
885 wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
886 wxTheClipboard->Close();
887 }
888
889 if( doDelete )
890 m_grid->ForceRefresh();
891}
892
893
894void GRID_TRICKS::onUpdateUI( wxUpdateUIEvent& event )
895{
896 // Respect ROW selectionMode when moving cursor
897
898 if( m_grid->GetSelectionMode() == wxGrid::wxGridSelectRows )
899 {
900 int cursorRow = m_grid->GetGridCursorRow();
901 bool cursorInSelectedRow = false;
902
903 for( int row : m_grid->GetSelectedRows() )
904 {
905 if( row == cursorRow )
906 {
907 cursorInSelectedRow = true;
908 break;
909 }
910 }
911
912 if( !cursorInSelectedRow && cursorRow >= 0 )
913 m_grid->SelectRow( cursorRow );
914 }
915}
bool isReadOnly(int aRow, int aCol)
void onGridMotion(wxMouseEvent &event)
void onGridLabelLeftClick(wxGridEvent &event)
virtual void paste_text(const wxString &cb_text)
void init()
Shared initialization for various ctors.
Definition: grid_tricks.cpp:62
void getSelectedArea()
Puts the selected area into a sensible rectangle of m_sel_{row,col}_{start,count} above.
bool m_multiCellEditEnabled
Definition: grid_tricks.h:142
GRID_TRICKS(WX_GRID *aGrid)
Definition: grid_tricks.cpp:42
void onKeyDown(wxKeyEvent &event)
void onPopupSelection(wxCommandEvent &event)
virtual void cutcopy(bool doCopy, bool doDelete)
bool m_enableSingleClickEdit
Definition: grid_tricks.h:141
virtual void doPopupSelection(wxCommandEvent &event)
bool isCheckbox(int aRow, int aCol)
std::function< void(wxCommandEvent &)> m_addHandler
Definition: grid_tricks.h:137
virtual void showPopupMenu(wxMenu &menu, wxGridEvent &aEvent)
int m_sel_row_start
Definition: grid_tricks.h:132
std::bitset< GRIDTRICKS_MAX_COL > m_tooltipEnabled
Definition: grid_tricks.h:139
void onUpdateUI(wxUpdateUIEvent &event)
void onGridCellRightClick(wxGridEvent &event)
WX_GRID * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:128
int m_sel_row_count
Definition: grid_tricks.h:134
bool isTextEntry(int aRow, int aCol)
Definition: grid_tricks.cpp:95
void onGridCellLeftDClick(wxGridEvent &event)
void onGridCellLeftClick(wxGridEvent &event)
virtual bool handleDoubleClick(wxGridEvent &aEvent)
int m_sel_col_count
Definition: grid_tricks.h:135
void onCharHook(wxKeyEvent &event)
virtual void paste_clipboard()
bool showEditor(int aRow, int aCol)
int m_sel_col_start
Definition: grid_tricks.h:133
void onGridLabelRightClick(wxGridEvent &event)
virtual bool toggleCell(int aRow, int aCol, bool aPreserveSelection=false)
bool CancelPendingChanges()
Definition: wx_grid.cpp:604
void ShowEditorOnMouseUp()
WxWidgets has a bunch of bugs in its handling of wxGrid mouse events which close cell editors right a...
Definition: wx_grid.h:212
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:632
#define _(s)
#define ROW_SEP_R
Definition: grid_tricks.cpp:39
#define COL_SEP
Definition: grid_tricks.cpp:37
#define ROW_SEP
Definition: grid_tricks.cpp:38
@ GRIDTRICKS_ID_PASTE
Definition: grid_tricks.h:45
@ GRIDTRICKS_FIRST_ID
Definition: grid_tricks.h:41
@ GRIDTRICKS_FIRST_SHOWHIDE
Definition: grid_tricks.h:51
@ GRIDTRICKS_ID_SELECT
Definition: grid_tricks.h:46
@ GRIDTRICKS_ID_CUT
Definition: grid_tricks.h:42
@ GRIDTRICKS_LAST_ID
Definition: grid_tricks.h:53
@ GRIDTRICKS_ID_COPY
Definition: grid_tricks.h:43
@ GRIDTRICKS_ID_DELETE
Definition: grid_tricks.h:44
std::vector< FAB_LAYER_COLOR > dummy
VECTOR2I end