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