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