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 <dick@softplc.com>
5  * Copyright (C) 2012 KiCad Developers, see change_log.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 
26 #include <fctsys.h>
27 #include <grid_tricks.h>
28 #include <wx/tokenzr.h>
29 #include <wx/arrstr.h>
30 #include <wx/clipbrd.h>
31 
32 #include <algorithm>
33 
34 
35  // It works for table data on clipboard for an Excell spreadsheet,
36 // why not us too for now.
37 #define COL_SEP wxT( '\t' )
38 #define ROW_SEP wxT( '\n' )
39 
40 
41 enum
42 {
43  MYID_FIRST = -1,
49 };
50 
51 
52 GRID_TRICKS::GRID_TRICKS( wxGrid* aGrid ):
53  m_grid( aGrid )
54 {
55  m_sel_row_start = 0;
56  m_sel_col_start = 0;
57  m_sel_row_count = 0;
58  m_sel_col_count = 0;
59 
60  aGrid->Connect( wxEVT_GRID_CELL_RIGHT_CLICK, wxGridEventHandler( GRID_TRICKS::onGridCellRightClick ), NULL, this );
61  aGrid->Connect( MYID_FIRST, MYID_LAST, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( GRID_TRICKS::onPopupSelection ), NULL, this );
62  aGrid->Connect( wxEVT_KEY_DOWN, wxKeyEventHandler( GRID_TRICKS::onKeyDown ), NULL, this );
63  aGrid->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( GRID_TRICKS::onRightDown ), NULL, this );
64 }
65 
66 
68 {
69  wxGridCellCoordsArray topLeft = m_grid->GetSelectionBlockTopLeft();
70  wxGridCellCoordsArray botRight = m_grid->GetSelectionBlockBottomRight();
71 
72  wxArrayInt cols = m_grid->GetSelectedCols();
73  wxArrayInt rows = m_grid->GetSelectedRows();
74 
75  DBG(printf("topLeft.Count():%d botRight:Count():%d\n", int( topLeft.Count() ), int( botRight.Count() ) );)
76 
77  if( topLeft.Count() && botRight.Count() )
78  {
79  m_sel_row_start = topLeft[0].GetRow();
80  m_sel_col_start = topLeft[0].GetCol();
81 
82  m_sel_row_count = botRight[0].GetRow() - m_sel_row_start + 1;
83  m_sel_col_count = botRight[0].GetCol() - m_sel_col_start + 1;
84  }
85  else if( cols.Count() )
86  {
87  m_sel_col_start = cols[0];
88  m_sel_col_count = cols.Count();
89  m_sel_row_start = 0;
90  m_sel_row_count = m_grid->GetNumberRows();
91  }
92  else if( rows.Count() )
93  {
94  m_sel_col_start = 0;
95  m_sel_col_count = m_grid->GetNumberCols();
96  m_sel_row_start = rows[0];
97  m_sel_row_count = rows.Count();
98  }
99  else
100  {
101  m_sel_row_start = -1;
102  m_sel_col_start = -1;
103  m_sel_row_count = 0;
104  m_sel_col_count = 0;
105  }
106 
107  //DBG(printf("m_sel_row_start:%d m_sel_col_start:%d m_sel_row_count:%d m_sel_col_count:%d\n", m_sel_row_start, m_sel_col_start, m_sel_row_count, m_sel_col_count );)
108 }
109 
110 
112 {
113  wxMenu menu;
114 
115  menu.Append( MYID_CUT, _( "Cut\tCTRL+X" ), _( "Clear selected cells pasting original contents to clipboard" ) );
116  menu.Append( MYID_COPY, _( "Copy\tCTRL+C" ), _( "Copy selected cells to clipboard" ) );
117  menu.Append( MYID_PASTE, _( "Paste\tCTRL+V" ), _( "Paste clipboard cells to matrix at current cell" ) );
118  menu.Append( MYID_SELECT, _( "Select All\tCTRL+A" ), _( "Select all cells" ) );
119 
120  getSelectedArea();
121 
122  // if nothing is selected, disable cut and copy.
124  {
125  menu.Enable( MYID_CUT, false );
126  menu.Enable( MYID_COPY, false );
127  }
128 
129  bool have_cb_text = false;
130  if( wxTheClipboard->Open() )
131  {
132  if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
133  have_cb_text = true;
134 
135  wxTheClipboard->Close();
136  }
137 
138  if( !have_cb_text )
139  {
140  // if nothing on clipboard, disable paste.
141  menu.Enable( MYID_PASTE, false );
142  }
143 
144  m_grid->PopupMenu( &menu );
145 }
146 
147 
148 void GRID_TRICKS::onPopupSelection( wxCommandEvent& event )
149 {
150  int menu_id = event.GetId();
151 
152  // assume getSelectedArea() was called by rightClickPopupMenu() and there's
153  // no way to have gotten here without that having been called.
154 
155  switch( menu_id )
156  {
157  case MYID_CUT:
158  case MYID_COPY:
159  cutcopy( menu_id == MYID_CUT );
160  break;
161 
162  case MYID_PASTE:
163  paste_clipboard();
164  break;
165 
166  case MYID_SELECT:
167  m_grid->SelectAll();
168  break;
169 
170  default:
171  ;
172  }
173 }
174 
175 
176 void GRID_TRICKS::onKeyDown( wxKeyEvent& ev )
177 {
178  if( isCtl( 'A', ev ) )
179  {
180  m_grid->SelectAll();
181  }
182  else if( isCtl( 'C', ev ) )
183  {
184  getSelectedArea();
185  cutcopy( false );
186  }
187  else if( isCtl( 'V', ev ) )
188  {
189  getSelectedArea();
190  paste_clipboard();
191  }
192  else if( isCtl( 'X', ev ) )
193  {
194  getSelectedArea();
195  cutcopy( true );
196  }
197  else
198  {
199  ev.Skip( true );
200  }
201 }
202 
203 
205 {
206  if( wxTheClipboard->Open() )
207  {
208  if( wxTheClipboard->IsSupported( wxDF_TEXT ) )
209  {
210  wxTextDataObject data;
211 
212  wxTheClipboard->GetData( data );
213 
214  wxString cb_text = data.GetText();
215 
216  paste_text( cb_text );
217  }
218 
219  wxTheClipboard->Close();
220  m_grid->ForceRefresh();
221  }
222 }
223 
224 
225 void GRID_TRICKS::paste_text( const wxString& cb_text )
226 {
227  wxGridTableBase* tbl = m_grid->GetTable();
228 
229  const int cur_row = std::max( getCursorRow(), 0 ); // no -1
230  const int cur_col = std::max( getCursorCol(), 0 );
231 
232  wxStringTokenizer rows( cb_text, ROW_SEP, wxTOKEN_RET_EMPTY );
233 
234  // if clipboard rows would extend past end of current table size...
235  if( int( rows.CountTokens() ) > tbl->GetNumberRows() - cur_row )
236  {
237  int newRowsNeeded = rows.CountTokens() - ( tbl->GetNumberRows() - cur_row );
238 
239  tbl->AppendRows( newRowsNeeded );
240  }
241 
242  for( int row = cur_row; rows.HasMoreTokens(); ++row )
243  {
244  wxString rowTxt = rows.GetNextToken();
245 
246  wxStringTokenizer cols( rowTxt, COL_SEP, wxTOKEN_RET_EMPTY );
247 
248  for( int col = cur_col; cols.HasMoreTokens(); ++col )
249  {
250  wxString cellTxt = cols.GetNextToken();
251  tbl->SetValue( row, col, cellTxt );
252  }
253  }
254  m_grid->AutoSizeColumns( false );
255 }
256 
257 
258 void GRID_TRICKS::cutcopy( bool doCut )
259 {
260  if( wxTheClipboard->Open() )
261  {
262  wxGridTableBase* tbl = m_grid->GetTable();
263  wxString txt;
264 
265  // fill txt with a format that is compatible with most spreadsheets
266  for( int row = m_sel_row_start; row < m_sel_row_start + m_sel_row_count; ++row )
267  {
268  for( int col = m_sel_col_start; col < m_sel_col_start + m_sel_col_count; ++col )
269  {
270  txt += tbl->GetValue( row, col );
271 
272  if( col < m_sel_col_start + m_sel_col_count - 1 ) // that was not last column
273  txt += COL_SEP;
274 
275  if( doCut )
276  tbl->SetValue( row, col, wxEmptyString );
277  }
278  txt += ROW_SEP;
279  }
280 
281  wxTheClipboard->SetData( new wxTextDataObject( txt ) );
282  wxTheClipboard->Close();
283 
284  if( doCut )
285  {
286  m_grid->AutoSizeColumns( false );
287  m_grid->ForceRefresh();
288  }
289  }
290 }
291 
wxGrid * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:42
int getCursorCol() const
If the cursor is not on a valid cell, because there are no rows at all, return -1, else return a 0 based column index.
Definition: grid_tricks.h:53
void getSelectedArea()
Puts the selected area into a sensible rectangle of m_sel_{row,col}_{start,count} above...
Definition: grid_tricks.cpp:67
int m_sel_row_count
Definition: grid_tricks.h:48
GRID_TRICKS(wxGrid *aGrid)
Definition: grid_tricks.cpp:52
void onRightDown(wxMouseEvent &event)
Definition: grid_tricks.h:78
void onGridCellRightClick(wxGridEvent &event)
Definition: grid_tricks.h:73
int m_sel_col_start
Definition: grid_tricks.h:47
static bool isCtl(int aChar, const wxKeyEvent &e)
Definition: grid_tricks.h:68
virtual void paste_text(const wxString &cb_text)
void onPopupSelection(wxCommandEvent &event)
int m_sel_col_count
Definition: grid_tricks.h:49
int m_sel_row_start
Definition: grid_tricks.h:46
virtual void paste_clipboard()
#define ROW_SEP
Definition: grid_tricks.cpp:38
int getCursorRow() const
If the cursor is not on a valid cell, because there are no rows at all, return -1, else return a 0 based row index.
Definition: grid_tricks.h:60
virtual void showPopupMenu()
#define max(a, b)
Definition: auxiliary.h:86
#define COL_SEP
Definition: grid_tricks.cpp:37
#define DBG(x)
Definition: fctsys.h:33
virtual void cutcopy(bool doCut)
void onKeyDown(wxKeyEvent &ev)