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