KiCad PCB EDA Suite
dialog_fp_lib_table.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) 2013 CERN
6  * Copyright (C) 2012-2016 KiCad Developers, see change_log.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 
27 /* TODO:
28 
29 *) After any change to uri, reparse the environment variables.
30 
31 */
32 
33 
34 #include <set>
35 #include <wx/regex.h>
36 
37 #include <fctsys.h>
38 #include <project.h>
39 #include <3d_viewer.h> // for KISYS3DMOD
41 #include <fp_lib_table.h>
42 #include <lib_table_lexer.h>
43 #include <invoke_pcb_dialog.h>
44 #include <grid_tricks.h>
45 #include <confirm.h>
46 #include <wizard_add_fplib.h>
47 
48 
51 {
57  COL_COUNT // keep as last
58 };
59 
60 
66 class FP_TBL_MODEL : public wxGridTableBase, public FP_LIB_TABLE
67 {
68  friend class FP_GRID_TRICKS;
69 
70 public:
71 
77  FP_TBL_MODEL( const FP_LIB_TABLE& aTableToEdit )
78  {
79  rows = aTableToEdit.rows;
80  }
81 
82  //-----<wxGridTableBase overloads>-------------------------------------------
83 
84  int GetNumberRows() override { return rows.size(); }
85  int GetNumberCols() override { return COL_COUNT; }
86 
87  wxString GetValue( int aRow, int aCol ) override
88  {
89  if( unsigned( aRow ) < rows.size() )
90  {
91  const LIB_TABLE_ROW* r = &rows[aRow];
92 
93  switch( aCol )
94  {
95  case COL_NICKNAME: return r->GetNickName();
96  case COL_URI: return r->GetFullURI();
97  case COL_TYPE: return r->GetType();
98  case COL_OPTIONS: return r->GetOptions();
99  case COL_DESCR: return r->GetDescr();
100  default:
101  ; // fall thru to wxEmptyString
102  }
103  }
104 
105  return wxEmptyString;
106  }
107 
108  void SetValue( int aRow, int aCol, const wxString &aValue ) override
109  {
110  if( unsigned( aRow ) < rows.size() )
111  {
112  LIB_TABLE_ROW* r = &rows[aRow];
113 
114  switch( aCol )
115  {
116  case COL_NICKNAME: r->SetNickName( aValue ); break;
117  case COL_URI: r->SetFullURI( aValue ); break;
118  case COL_TYPE: r->SetType( aValue ); break;
119  case COL_OPTIONS: r->SetOptions( aValue ); break;
120  case COL_DESCR: r->SetDescr( aValue ); break;
121  }
122  }
123  }
124 
125  bool IsEmptyCell( int aRow, int aCol ) override
126  {
127  return !GetValue( aRow, aCol );
128  }
129 
130  bool InsertRows( size_t aPos = 0, size_t aNumRows = 1 ) override
131  {
132  if( aPos < rows.size() )
133  {
134  for( size_t i = 0; i < aNumRows; i++ )
135  {
136  rows.insert( rows.begin() + i,
137  dynamic_cast< LIB_TABLE_ROW* >( new FP_LIB_TABLE_ROW ) );
138  }
139 
140  // use the (wxGridStringTable) source Luke.
141  if( GetView() )
142  {
143  wxGridTableMessage msg( this,
144  wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
145  aPos,
146  aNumRows );
147 
148  GetView()->ProcessTableMessage( msg );
149  }
150 
151  return true;
152  }
153 
154  return false;
155  }
156 
157  bool AppendRows( size_t aNumRows = 1 ) override
158  {
159  // do not modify aNumRows, original value needed for wxGridTableMessage below
160  for( int i = aNumRows; i; --i )
161  rows.push_back( new FP_LIB_TABLE_ROW );
162 
163  if( GetView() )
164  {
165  wxGridTableMessage msg( this,
166  wxGRIDTABLE_NOTIFY_ROWS_APPENDED,
167  aNumRows );
168 
169  GetView()->ProcessTableMessage( msg );
170  }
171 
172  return true;
173  }
174 
175  bool DeleteRows( size_t aPos, size_t aNumRows ) override
176  {
177  // aPos may be a large positive, e.g. size_t(-1), and the sum of
178  // aPos+aNumRows may wrap here, so both ends of the range are tested.
179  if( aPos < rows.size() && aPos + aNumRows <= rows.size() )
180  {
181  LIB_TABLE_ROWS_ITER start = rows.begin() + aPos;
182  rows.erase( start, start + aNumRows );
183 
184  if( GetView() )
185  {
186  wxGridTableMessage msg( this,
187  wxGRIDTABLE_NOTIFY_ROWS_DELETED,
188  aPos,
189  aNumRows );
190 
191  GetView()->ProcessTableMessage( msg );
192  }
193 
194  return true;
195  }
196 
197  return false;
198  }
199 
200  wxString GetColLabelValue( int aCol ) override
201  {
202  switch( aCol )
203  {
204  case COL_NICKNAME: return _( "Nickname" );
205  case COL_URI: return _( "Library Path" );
206 
207  // keep this "Plugin Type" text fairly long so column is sized wide enough
208  case COL_TYPE: return _( "Plugin Type" );
209  case COL_OPTIONS: return _( "Options" );
210  case COL_DESCR: return _( "Description" );
211  default: return wxEmptyString;
212  }
213  }
214 
215  //-----</wxGridTableBase overloads>------------------------------------------
216 };
217 
218 
220 {
221 public:
222  FP_GRID_TRICKS( wxGrid* aGrid ) :
223  GRID_TRICKS( aGrid )
224  {
225  }
226 
227 protected:
228 
231  virtual void paste_text( const wxString& cb_text ) override
232  {
233  FP_TBL_MODEL* tbl = (FP_TBL_MODEL*) m_grid->GetTable();
234 
235  size_t ndx = cb_text.find( "(fp_lib_table" );
236 
237  if( ndx != std::string::npos )
238  {
239  // paste the FP_LIB_TABLE_ROWs of s-expression (fp_lib_table), starting
240  // at column 0 regardless of current cursor column.
241 
242  STRING_LINE_READER slr( TO_UTF8( cb_text ), "Clipboard" );
243  LIB_TABLE_LEXER lexer( &slr );
244  FP_LIB_TABLE tmp_tbl;
245  bool parsed = true;
246 
247  try
248  {
249  tmp_tbl.Parse( &lexer );
250  }
251  catch( PARSE_ERROR& pe )
252  {
253  DisplayError( NULL, pe.What() );
254  parsed = false;
255  }
256 
257  if( parsed )
258  {
259  const int cur_row = std::max( getCursorRow(), 0 );
260 
261  // if clipboard rows would extend past end of current table size...
262  if( tmp_tbl.GetCount() > tbl->GetNumberRows() - cur_row )
263  {
264  int newRowsNeeded = tmp_tbl.GetCount() - ( tbl->GetNumberRows() - cur_row );
265  tbl->AppendRows( newRowsNeeded );
266  }
267 
268  for( int i = 0; i < tmp_tbl.GetCount(); ++i )
269  {
270  tbl->rows.replace( cur_row+i, tmp_tbl.At( i ) );
271  }
272  }
273 
274  m_grid->AutoSizeColumns( false );
275  }
276  else
277  {
278  // paste spreadsheet formatted text.
279  GRID_TRICKS::paste_text( cb_text );
280  }
281  }
282 };
283 
284 
291 {
292 
293 public:
294  DIALOG_FP_LIB_TABLE( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ) :
295  DIALOG_FP_LIB_TABLE_BASE( aParent ),
296  m_global( aGlobal ),
297  m_project( aProject )
298  {
299  // For user info, shows the table filenames:
300  m_PrjTableFilename->SetLabel( Prj().FootprintLibTblName() );
302 
303  // wxGrid only supports user owned tables if they exist past end of ~wxGrid(),
304  // so make it a grid owned table.
305  m_global_grid->SetTable( new FP_TBL_MODEL( *aGlobal ), true );
306  m_project_grid->SetTable( new FP_TBL_MODEL( *aProject ), true );
307 
308  // add Cut, Copy, and Paste to wxGrids
309  m_global_grid->PushEventHandler( new FP_GRID_TRICKS( m_global_grid ) );
310  m_project_grid->PushEventHandler( new FP_GRID_TRICKS( m_project_grid ) );
311 
312  m_global_grid->AutoSizeColumns( false );
313  m_project_grid->AutoSizeColumns( false );
314 
315  wxArrayString choices;
316 
317  choices.Add( IO_MGR::ShowType( IO_MGR::KICAD ) );
318  choices.Add( IO_MGR::ShowType( IO_MGR::GITHUB ) );
319  choices.Add( IO_MGR::ShowType( IO_MGR::LEGACY ) );
320  choices.Add( IO_MGR::ShowType( IO_MGR::EAGLE ) );
321  choices.Add( IO_MGR::ShowType( IO_MGR::GEDA_PCB ) );
322 
323  /* PCAD_PLUGIN does not support Footprint*() functions
324  choices.Add( IO_MGR::ShowType( IO_MGR::GITHUB ) );
325  */
326 
327  wxGridCellAttr* attr;
328 
329  attr = new wxGridCellAttr;
330  attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
331  m_project_grid->SetColAttr( COL_TYPE, attr );
332 
333  attr = new wxGridCellAttr;
334  attr->SetEditor( new wxGridCellChoiceEditor( choices ) );
335  m_global_grid->SetColAttr( COL_TYPE, attr );
336 
338 
339  for( int i=0; i<2; ++i )
340  {
341  wxGrid* g = i==0 ? m_global_grid : m_project_grid;
342 
343  // all but COL_OPTIONS, which is edited with Option Editor anyways.
344  g->AutoSizeColumn( COL_NICKNAME, false );
345  g->AutoSizeColumn( COL_TYPE, false );
346  g->AutoSizeColumn( COL_URI, false );
347  g->AutoSizeColumn( COL_DESCR, false );
348 
349  // would set this to width of title, if it was easily known.
350  g->SetColSize( COL_OPTIONS, 80 );
351  }
352 
353  // This scrunches the dialog hideously, probably due to wxAUI container.
354  // Fit();
355  // We derive from DIALOG_SHIM so prior size will be used anyways.
356 
357  // select the last selected page
358  m_auinotebook->SetSelection( m_pageNdx );
359 
360  // fire pageChangedHandler() so m_cur_grid gets set
361  // m_auinotebook->SetSelection will generate a pageChangedHandler()
362  // event call later, but too late.
363  wxAuiNotebookEvent uneventful;
364  pageChangedHandler( uneventful );
365 
366  // Gives a selection for each grid, mainly for delete lib button.
367  // Without that, we do not see what lib will be deleted
368  m_global_grid->SelectRow( 0 );
369  m_project_grid->SelectRow( 0 );
370 
371  // for ALT+A handling, we want the initial focus to be on the first selected grid.
372  m_cur_grid->SetFocus();
373 
374  // On some windows manager (Unity, XFCE), this dialog is
375  // not always raised, depending on this dialog is run.
376  // Force it to be raised
377  Raise();
378  }
379 
381  {
382  // Delete the GRID_TRICKS.
383  // Any additional event handlers should be popped before the window is deleted.
384  m_global_grid->PopEventHandler( true );
385  m_project_grid->PopEventHandler( true );
386  }
387 
388 
389 private:
391 
394  int getCursorCol() const
395  {
396  return m_cur_grid->GetGridCursorCol();
397  }
398 
401  int getCursorRow() const
402  {
403  return m_cur_grid->GetGridCursorRow();
404  }
405 
412  {
413  for( int t=0; t<2; ++t )
414  {
415  FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model();
416 
417  for( int r = 0; r < model.GetNumberRows(); )
418  {
419  wxString nick = model.GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
420  wxString uri = model.GetValue( r, COL_URI ).Trim( false ).Trim();
421 
422  if( !nick || !uri )
423  {
424  // Delete the "empty" row, where empty means missing nick or uri.
425  // This also updates the UI which could be slow, but there should only be a few
426  // rows to delete, unless the user fell asleep on the Add Row
427  // button.
428  model.DeleteRows( r, 1 );
429  }
430  else if( nick.find(':') != size_t(-1) )
431  {
432  wxString msg = wxString::Format(
433  _( "Illegal character '%s' found in Nickname: '%s' in row %d" ),
434  ":", GetChars( nick ), r );
435 
436  // show the tabbed panel holding the grid we have flunked:
437  if( &model != cur_model() )
438  {
439  m_auinotebook->SetSelection( &model == global_model() ? 0 : 1 );
440  }
441 
442  // go to the problematic row
443  m_cur_grid->SetGridCursor( r, 0 );
444  m_cur_grid->SelectBlock( r, 0, r, 0 );
445  m_cur_grid->MakeCellVisible( r, 0 );
446 
447  wxMessageDialog errdlg( this, msg, _( "No Colon in Nicknames" ) );
448  errdlg.ShowModal();
449  return false;
450  }
451  else
452  {
453  // set the trimmed values back into the table so they get saved to disk.
454  model.SetValue( r, COL_NICKNAME, nick );
455  model.SetValue( r, COL_URI, uri );
456  ++r; // this row was OK.
457  }
458  }
459  }
460 
461  // check for duplicate nickNames, separately in each table.
462  for( int t=0; t<2; ++t )
463  {
464  FP_TBL_MODEL& model = t==0 ? *global_model() : *project_model();
465 
466  for( int r1 = 0; r1 < model.GetNumberRows() - 1; ++r1 )
467  {
468  wxString nick1 = model.GetValue( r1, COL_NICKNAME );
469 
470  for( int r2=r1+1; r2 < model.GetNumberRows(); ++r2 )
471  {
472  wxString nick2 = model.GetValue( r2, COL_NICKNAME );
473 
474  if( nick1 == nick2 )
475  {
476  wxString msg = wxString::Format(
477  _( "Duplicate Nickname: '%s' in rows %d and %d" ),
478  GetChars( nick1 ), r1+1, r2+1
479  );
480 
481  // show the tabbed panel holding the grid we have flunked:
482  if( &model != cur_model() )
483  {
484  m_auinotebook->SetSelection( &model == global_model() ? 0 : 1 );
485  }
486 
487  // go to the lower of the two rows, it is technically the duplicate:
488  m_cur_grid->SetGridCursor( r2, 0 );
489  m_cur_grid->SelectBlock( r2, 0, r2, 0 );
490  m_cur_grid->MakeCellVisible( r2, 0 );
491 
492  wxMessageDialog errdlg( this, msg, _( "Please Delete or Modify One" ) );
493  errdlg.ShowModal();
494  return false;
495  }
496  }
497  }
498  }
499 
500  return true;
501  }
502 
503  //-----<event handlers>----------------------------------
504 
505  void onKeyDown( wxKeyEvent& ev ) override
506  {
507 #if 0
508  // send the key to the current grid
509  ((wxEvtHandler*)m_cur_grid)->ProcessEvent( ev );
510 #else
511  // or no:
512  // m_cur_grid has the focus most of the time anyways, so above not needed.
513  ev.Skip();
514 #endif
515  }
516 
517  void pageChangedHandler( wxAuiNotebookEvent& event ) override
518  {
519  m_pageNdx = m_auinotebook->GetSelection();
521  }
522 
523  void appendRowHandler( wxCommandEvent& event ) override
524  {
525  if( m_cur_grid->AppendRows( 1 ) )
526  {
527  int last_row = m_cur_grid->GetNumberRows() - 1;
528 
529  // wx documentation is wrong, SetGridCursor does not make visible.
530  m_cur_grid->MakeCellVisible( last_row, 0 );
531  m_cur_grid->SetGridCursor( last_row, 0 );
532  m_cur_grid->SelectRow( m_cur_grid->GetGridCursorRow() );
533  }
534  }
535 
536  void deleteRowHandler( wxCommandEvent& event ) override
537  {
538  int currRow = getCursorRow();
539 
540  // In a wxGrid, collect rows that have a selected cell, or are selected
541  // is not so easy: it depend on the way the selection was made.
542  // Here, we collect row selected by clicking on a row label, and
543  // row that contain a cell previously selected.
544  // If no candidate, just delete the row with the grid cursor.
545  wxArrayInt selectedRows = m_cur_grid->GetSelectedRows();
546  wxGridCellCoordsArray cells = m_cur_grid->GetSelectedCells();
547 
548  // Add all row having cell selected to list:
549  for( unsigned ii = 0; ii < cells.GetCount(); ii++ )
550  selectedRows.Add( cells[ii].GetRow() );
551 
552  // Use the row having the grid cursor only if we have no candidate:
553  if( selectedRows.size() == 0 && getCursorRow() >= 0 )
554  selectedRows.Add( getCursorRow() );
555 
556  std::sort( selectedRows.begin(), selectedRows.end() );
557 
558  // Remove selected rows (note: a row can be stored more than once in list)
559  int last_row = -1;
560  for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
561  {
562  int row = selectedRows[ii];
563 
564  if( row != last_row )
565  {
566  last_row = row;
567  m_cur_grid->DeleteRows( row, 1 );
568  }
569  }
570 
571  if( currRow >= m_cur_grid->GetNumberRows() )
572  m_cur_grid->SetGridCursor(m_cur_grid->GetNumberRows()-1, getCursorCol() );
573 
574  m_cur_grid->SelectRow( m_cur_grid->GetGridCursorRow() );
575  }
576 
577  void moveUpHandler( wxCommandEvent& event ) override
578  {
579  wxArrayInt rowsSelected = m_cur_grid->GetSelectedRows();
580 
581  if( rowsSelected.GetCount() == 0 )
582  return;
583 
584  // @todo: add multiple selection moves.
585  int curRow = rowsSelected[0];
586 
587  if( curRow >= 1 )
588  {
589  int curCol = getCursorCol();
590 
591  FP_TBL_MODEL* tbl = cur_model();
592 
593  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
594  tbl->rows.release( tbl->rows.begin() + curRow );
595 
596  --curRow;
597  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
598 
599  if( tbl->GetView() )
600  {
601  // fire a msg to cause redrawing
602  wxGridTableMessage msg( tbl,
603  wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
604  curRow,
605  0 );
606 
607  tbl->GetView()->ProcessTableMessage( msg );
608  }
609 
610  m_cur_grid->MakeCellVisible( curRow, curCol );
611  m_cur_grid->SetGridCursor( curRow, curCol );
612  m_cur_grid->SelectRow( getCursorRow() );
613  }
614  }
615 
616  void moveDownHandler( wxCommandEvent& event ) override
617  {
618  wxArrayInt rowsSelected = m_cur_grid->GetSelectedRows();
619 
620  if( rowsSelected.GetCount() == 0 )
621  return;
622 
623  FP_TBL_MODEL* tbl = cur_model();
624 
625  // @todo: add multiple selection moves.
626  int curRow = rowsSelected[0];
627 
628  if( unsigned( curRow + 1 ) < tbl->rows.size() )
629  {
630  int curCol = getCursorCol();
631 
632  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
633  tbl->rows.release( tbl->rows.begin() + curRow );
634 
635  ++curRow;
636  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
637 
638  if( tbl->GetView() )
639  {
640  // fire a msg to cause redrawing
641  wxGridTableMessage msg( tbl,
642  wxGRIDTABLE_NOTIFY_ROWS_INSERTED,
643  curRow - 1,
644  0 );
645 
646  tbl->GetView()->ProcessTableMessage( msg );
647  }
648 
649  m_cur_grid->MakeCellVisible( curRow, curCol );
650  m_cur_grid->SetGridCursor( curRow, curCol );
651  m_cur_grid->SelectRow( getCursorRow() );
652  }
653  }
654 
655  void optionsEditor( wxCommandEvent& event ) override
656  {
657  FP_TBL_MODEL* tbl = cur_model();
658 
659  if( tbl->GetNumberRows() )
660  {
661  int curRow = getCursorRow();
662  LIB_TABLE_ROW* row = &tbl->rows[curRow];
663 
664  wxString result;
665  const wxString& options = row->GetOptions();
666 
667  InvokePluginOptionsEditor( this, row->GetNickName(), row->GetType(), options, &result );
668 
669  if( options != result )
670  {
671  row->SetOptions( result );
672 
673  // all but options:
674  m_cur_grid->AutoSizeColumn( COL_NICKNAME, false );
675  m_cur_grid->AutoSizeColumn( COL_URI, false );
676  m_cur_grid->AutoSizeColumn( COL_TYPE, false );
677 
678  // On Windows, the grid is not refresh,
679  // so force resfresh after a change
680 #ifdef __WINDOWS__
681  Refresh();
682 #endif
683  }
684  }
685  }
686 
687  void OnClickLibraryWizard( wxCommandEvent& event ) override;
688 
689  void onCancelButtonClick( wxCommandEvent& event ) override
690  {
691  EndModal( 0 );
692  }
693 
694  void onCancelCaptionButtonClick( wxCloseEvent& event ) override
695  {
696  EndModal( 0 );
697  }
698 
699  void onOKButtonClick( wxCommandEvent& event ) override
700  {
701  int dialogRet = 0;
702 
703  // stuff any pending cell editor text into the table.
704  m_cur_grid->SaveEditControlValue();
705 
706  if( verifyTables() )
707  {
708  if( *global_model() != *m_global )
709  {
710  dialogRet |= 1;
711 
712  m_global->Clear();
713  m_global->rows.transfer( m_global->rows.end(), global_model()->rows.begin(),
714  global_model()->rows.end(), global_model()->rows );
715  m_global->reindex();
716  }
717 
718  if( *project_model() != *m_project )
719  {
720  dialogRet |= 2;
721 
722  m_project->Clear();
723  m_project->rows.transfer( m_project->rows.end(), project_model()->rows.begin(),
724  project_model()->rows.end(), project_model()->rows );
725  m_project->reindex();
726  }
727 
728  EndModal( dialogRet );
729  }
730  }
731 
735  {
736  wxRegEx re( ".*?\\$\\{(.+?)\\}.*?", wxRE_ADVANCED );
737  wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
738 
739  std::set< wxString > unique;
740  typedef std::set<wxString>::const_iterator SET_CITER;
741 
742  // clear the table
743  m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() );
744 
745  FP_TBL_MODEL* gbl = global_model();
746  FP_TBL_MODEL* prj = project_model();
747 
748  int gblRowCount = gbl->GetNumberRows();
749  int prjRowCount = prj->GetNumberRows();
750  int row;
751 
752  for( row = 0; row < gblRowCount; ++row )
753  {
754  wxString uri = gbl->GetValue( row, COL_URI );
755 
756  while( re.Matches( uri ) )
757  {
758  wxString envvar = re.GetMatch( uri, 1 );
759 
760  // ignore duplicates
761  unique.insert( envvar );
762 
763  // delete the last match and search again
764  uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
765  }
766  }
767 
768  for( row = 0; row < prjRowCount; ++row )
769  {
770  wxString uri = prj->GetValue( row, COL_URI );
771 
772  while( re.Matches( uri ) )
773  {
774  wxString envvar = re.GetMatch( uri, 1 );
775 
776  // ignore duplicates
777  unique.insert( envvar );
778 
779  // delete the last match and search again
780  uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
781  }
782  }
783 
784  // Make sure this special environment variable shows up even if it was
785  // not used yet. It is automatically set by KiCad to the directory holding
786  // the current project.
787  unique.insert( PROJECT_VAR_NAME );
788  unique.insert( FP_LIB_TABLE::GlobalPathEnvVariableName() );
789  // This special environment variable is used to locate 3d shapes
790  unique.insert( KISYS3DMOD );
791 
792  m_path_subs_grid->AppendRows( unique.size() );
793 
794  row = 0;
795 
796  for( SET_CITER it = unique.begin(); it != unique.end(); ++it, ++row )
797  {
798  wxString evName = *it;
799  wxString evValue;
800 
801  m_path_subs_grid->SetCellValue( row, 0, evName );
802 
803  if( wxGetEnv( evName, &evValue ) )
804  m_path_subs_grid->SetCellValue( row, 1, evValue );
805  }
806 
807  m_path_subs_grid->AutoSizeColumns();
808  }
809 
810  //-----</event handlers>---------------------------------
811 
812  // caller's tables are modified only on OK button and successful verification.
815 
816  FP_TBL_MODEL* global_model() const { return (FP_TBL_MODEL*) m_global_grid->GetTable(); }
817  FP_TBL_MODEL* project_model() const { return (FP_TBL_MODEL*) m_project_grid->GetTable(); }
818  FP_TBL_MODEL* cur_model() const { return (FP_TBL_MODEL*) m_cur_grid->GetTable(); }
819 
820  wxGrid* m_cur_grid;
821  static int m_pageNdx;
822 };
823 
824 
826 
827 
828 void DIALOG_FP_LIB_TABLE::OnClickLibraryWizard( wxCommandEvent& event )
829 {
830  WIZARD_FPLIB_TABLE dlg( this );
831 
832  if( !dlg.RunWizard( dlg.GetFirstPage() ) )
833  return; // Aborted by user
834 
835  const std::vector<WIZARD_FPLIB_TABLE::LIBRARY>& libs = dlg.GetLibraries();
836  bool global_scope = dlg.GetLibScope() == WIZARD_FPLIB_TABLE::GLOBAL;
837  wxGrid* libgrid = global_scope ? m_global_grid : m_project_grid;
838  FP_TBL_MODEL* tbl = (FP_TBL_MODEL*) libgrid->GetTable();
839 
840  for( std::vector<WIZARD_FPLIB_TABLE::LIBRARY>::const_iterator it = libs.begin();
841  it != libs.end(); ++it )
842  {
843  if( it->GetStatus() == WIZARD_FPLIB_TABLE::LIBRARY::INVALID )
844  continue;
845 
846  if( libgrid->AppendRows( 1 ) )
847  {
848  int last_row = libgrid->GetNumberRows() - 1;
849 
850  // Add the nickname: currently make it from filename
851  tbl->SetValue( last_row, COL_NICKNAME, it->GetDescription() );
852 
853  // Add the path:
854  tbl->SetValue( last_row, COL_URI, it->GetAutoPath( dlg.GetLibScope() ) );
855 
856  // Add the plugin name:
857  tbl->SetValue( last_row, COL_TYPE, it->GetPluginName() );
858 
859  libgrid->MakeCellVisible( last_row, 0 );
860  libgrid->SetGridCursor( last_row, 0 );
861  }
862  }
863 
864  // Switch to the current scope tab
865  if( global_scope )
866  m_auinotebook->SetSelection( 0 );
867  else
868  m_auinotebook->SetSelection( 1 );
869 
870  libgrid->SelectRow( libgrid->GetGridCursorRow() );
871 }
872 
873 
874 int InvokePcbLibTableEditor( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal,
875  FP_LIB_TABLE* aProject )
876 {
877  DIALOG_FP_LIB_TABLE dlg( aParent, aGlobal, aProject );
878 
879  int dialogRet = dlg.ShowModal(); // returns value passed to EndModal() above
880 
881  return dialogRet;
882 }
883 
884 
885 int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal,
886  FP_LIB_TABLE* aProject )
887 {
888  WIZARD_FPLIB_TABLE dlg( aParent );
889 
890  if( !dlg.RunWizard( dlg.GetFirstPage() ) )
891  return 0; // Aborted by user
892 
893  const std::vector<WIZARD_FPLIB_TABLE::LIBRARY>& libs = dlg.GetLibraries();
895  FP_LIB_TABLE* fp_tbl = ( scope == WIZARD_FPLIB_TABLE::GLOBAL ? aGlobal : aProject );
896 
897  if( fp_tbl )
898  {
899  for( std::vector<WIZARD_FPLIB_TABLE::LIBRARY>::const_iterator it = libs.begin();
900  it != libs.end(); ++it )
901  {
902  if( it->GetStatus() == WIZARD_FPLIB_TABLE::LIBRARY::INVALID )
903  continue;
904 
905  FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( it->GetDescription(),
906  it->GetAutoPath( scope ),
907  it->GetPluginName(),
908  wxEmptyString ); // options
909  fp_tbl->InsertRow( row );
910  }
911  }
912 
913  return scope;
914 }
int GetCount()
COL_ORDER
grid column order is established by this sequence
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.
FP_TBL_MODEL * global_model() const
wxGrid * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:42
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.
Class LIB_TABLE_ROW.
void Clear()
Delete all rows.
LIB_SCOPE GetLibScope() const
Function GetLibScope Returns the scope for the added libraries (global / project specific).
void SetValue(int aRow, int aCol, const wxString &aValue) override
void OnClickLibraryWizard(wxCommandEvent &event) override
void appendRowHandler(wxCommandEvent &event) override
Class FP_LIB_TABLE_ROW.
Definition: fp_lib_table.h:41
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Function InsertRow.
This file is part of the common library.
Class FP_TBL_MODEL mixes in FP_LIB_TABLE into wxGridTableBase so the result can be used as a table wi...
void reindex()
FP_TBL_MODEL * project_model() const
void onCancelCaptionButtonClick(wxCloseEvent &event) override
FP_TBL_MODEL * cur_model() const
void InvokePluginOptionsEditor(wxTopLevelWindow *aCaller, const wxString &aNickname, const wxString &aPluginType, const wxString &aOptions, wxString *aResult)
Function InvokePluginOptionsEditor calls DIALOG_FP_PLUGIN_OPTIONS dialog so that plugin options set c...
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:33
Class GRID_TRICKS is used to add cut, copy, and paste to an otherwise unmodied wxGrid instance...
Definition: grid_tricks.h:34
void deleteRowHandler(wxCommandEvent &event) override
virtual void SetType(const wxString &aType)=0
Function SetType.
Class DIALOG_FP_LIB_TABLE shows and edits the PCB library tables.
const wxString & GetOptions() const
Function GetOptions.
LIB_TABLE_ROW * At(int aIndex)
bool AppendRows(size_t aNumRows=1) override
LIB_TABLE_ROWS rows
void moveUpHandler(wxCommandEvent &event) override
static const wxString ShowType(PCB_FILE_T aFileType)
Function ShowType returns a brief name for a plugin, given aFileType enum.
Definition: io_mgr.cpp:105
virtual const wxString GetType() const =0
Function GetType.
int InvokeFootprintWizard(wxTopLevelWindow *aParent, FP_LIB_TABLE *aGlobal, FP_LIB_TABLE *aProject)
Function InvokeFootprintWizard Runs the footprint library wizard for easy library addition...
void moveDownHandler(wxCommandEvent &event) override
Geda PCB file formats.
Definition: io_mgr.h:57
const wxString GetFullURI(bool aSubstituted=false) const
Function GetFullURI.
void populateEnvironReadOnlyTable()
Populate the readonly environment variable table with names and values by examining all the full_uri ...
int GetNumberRows() override
virtual void paste_text(const wxString &cb_text)
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes...
Definition: macros.h:47
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
bool DeleteRows(size_t aPos, size_t aNumRows) override
static const wxString GlobalPathEnvVariableName()
Function GlobalPathEnvVarVariableName.
virtual void paste_text(const wxString &cb_text) override
handle specialized clipboard text, with leading "(fp_lib_table", OR spreadsheet formatted text...
virtual void Parse(LIB_TABLE_LEXER *aLexer) override
Function Parse.
int InvokePcbLibTableEditor(wxTopLevelWindow *aParent, FP_LIB_TABLE *aGlobal, FP_LIB_TABLE *aProject)
Function InvokePcbLibTableEditor shows the modal DIALOG_FP_LIB_TABLE for purposes of editing two lib ...
void Refresh()
LIB_SCOPE
Scope (global/project)
S-expression Pcbnew file format.
Definition: io_mgr.h:54
#define KISYS3DMOD
A variable name whose value holds the path of 3D shape files.
Definition: 3d_viewer.h:38
wxString GetValue(int aRow, int aCol) override
wxString GetColLabelValue(int aCol) override
void pageChangedHandler(wxAuiNotebookEvent &event) override
const std::vector< LIBRARY > & GetLibraries() const
Function GetLibraries Returns libraries selected by the user.
const wxString & GetDescr() const
Function GetDescr.
DIALOG_FP_LIB_TABLE(wxTopLevelWindow *aParent, FP_LIB_TABLE *aGlobal, FP_LIB_TABLE *aProject)
void onKeyDown(wxKeyEvent &ev) override
Class DIALOG_FP_LIB_TABLE_BASE.
bool IsEmptyCell(int aRow, int aCol) override
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 const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
const wxString & GetNickName() const
Function GetNickName.
Struct PARSE_ERROR contains a filename or source description, a problem input line, a line number, a byte offset, and an error message which contains the the caller's report and his call site information: CPP source file, function, and line number.
Definition: ki_exception.h:94
void SetFullURI(const wxString &aFullURI)
Function SetFullURI.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
Legacy Pcbnew file formats prior to s-expression.
Definition: io_mgr.h:53
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
#define max(a, b)
Definition: auxiliary.h:86
void SetNickName(const wxString &aNickName)
Function SetNickName.
wxWizardPage * GetFirstPage() const
Function GetFirstPage Returns the welcoming page for the wizard.
LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER
bool InsertRows(size_t aPos=0, size_t aNumRows=1) override
static int m_pageNdx
Remember the last notebook page selected during a session.
FP_GRID_TRICKS(wxGrid *aGrid)
wxGrid * m_cur_grid
changed based on tab choice
Class LIB_TABLE_LEXER is an automatically generated class using the TokenList2DnsLexer.cmake technology, based on keywords provided by file: /home/kicad/workspace/kicad-doxygen/common/lib_table.keywords.
void SetOptions(const wxString &aOptions)
Function SetOptions.
void onOKButtonClick(wxCommandEvent &event) override
void onCancelButtonClick(wxCommandEvent &event) override
bool verifyTables()
Function verifyTables trims important fields, removes blank row entries, and checks for duplicates...
Class STRING_LINE_READER is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:254
void SetDescr(const wxString &aDescr)
Function SetDescr.
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:69
int GetNumberCols() override
FP_TBL_MODEL(const FP_LIB_TABLE &aTableToEdit)
Constructor FP_TBL_MODEL is a copy constructor that builds a wxGridTableBase (table model) by wrappin...
Read only http://github.com repo holding pretty footprints.
Definition: io_mgr.h:58
Declaration of the eda_3d_viewer class.
static wxString GetGlobalTableFileName()
Function GetGlobalTableFileName.
void optionsEditor(wxCommandEvent &event) override