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