KiCad PCB EDA Suite
panel_sym_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) 2017 Wayne Stambaugh <stambaughw@gmail.com>
5  * Copyright (C) 2017-2020 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <set>
22 #include <wx/regex.h>
23 
24 #include <fctsys.h>
25 #include <project.h>
26 #include <panel_sym_lib_table.h>
27 #include <lib_id.h>
28 #include <symbol_lib_table.h>
29 #include <lib_table_lexer.h>
30 #include <grid_tricks.h>
31 #include <widgets/wx_grid.h>
32 #include <confirm.h>
33 #include <bitmaps.h>
34 #include <lib_table_grid.h>
36 #include <env_paths.h>
37 #include <eeschema_id.h>
38 #include <lib_edit_frame.h>
39 #include <lib_view_frame.h>
40 #include <sch_edit_frame.h>
41 #include <kiway.h>
42 #include <sch_screen.h>
43 
46 
47 
48 // clang-format off
49 
54 {
55  wxString m_Description;
56  wxString m_FileFilter;
58  bool m_IsFile;
59  SCH_IO_MGR::SCH_FILE_T m_Plugin;
60 };
61 
62 
66 enum {
69 };
70 
71 
76 static const std::map<int, supportedFileType>& fileTypes()
77 {
78  static const std::map<int, supportedFileType> fileTypes =
79  {
81  {
82  "KiCad legacy symbol library file (*.lib)",
84  "",
85  true,
86  SCH_IO_MGR::SCH_LEGACY
87  }
88  },
89  {
91  {
92  "KiCad s-expression symbol library file (*.kicad_sym)",
94  "",
95  true,
96  SCH_IO_MGR::SCH_KICAD
97  }
98  }
99  };
100 
101  return fileTypes;
102 }
103 
104 
109 {
110  friend class SYMBOL_GRID_TRICKS;
111 
112 protected:
113  LIB_TABLE_ROW* at( size_t aIndex ) override { return &rows.at( aIndex ); }
114 
115  size_t size() const override { return rows.size(); }
116 
118  {
119  return dynamic_cast< LIB_TABLE_ROW* >( new SYMBOL_LIB_TABLE_ROW );
120  }
121 
122  LIB_TABLE_ROWS_ITER begin() override { return rows.begin(); }
123 
125  {
126  return rows.insert( aIterator, aRow );
127  }
128 
129  void push_back( LIB_TABLE_ROW* aRow ) override { rows.push_back( aRow ); }
130 
132  {
133  return rows.erase( aFirst, aLast );
134  }
135 
136 public:
137 
139  {
140  rows = aTableToEdit.rows;
141  }
142 };
143 
144 
146 {
147 public:
149  GRID_TRICKS( aGrid ),
150  m_dialog( aParent )
151  {
152  }
153 
154 protected:
156 
159  virtual void paste_text( const wxString& cb_text ) override
160  {
161  SYMBOL_LIB_TABLE_GRID* tbl = (SYMBOL_LIB_TABLE_GRID*) m_grid->GetTable();
162  size_t ndx = cb_text.find( "(sym_lib_table" );
163 
164  if( ndx != std::string::npos )
165  {
166  // paste the SYMBOL_LIB_TABLE_ROWs of s-expression (sym_lib_table), starting
167  // at column 0 regardless of current cursor column.
168 
169  STRING_LINE_READER slr( TO_UTF8( cb_text ), "Clipboard" );
170  LIB_TABLE_LEXER lexer( &slr );
171  SYMBOL_LIB_TABLE tmp_tbl;
172  bool parsed = true;
173 
174  try
175  {
176  tmp_tbl.Parse( &lexer );
177  }
178  catch( PARSE_ERROR& pe )
179  {
180  DisplayError( m_dialog, pe.What() );
181  parsed = false;
182  }
183 
184  if( parsed )
185  {
186  // make sure the table is big enough...
187  if( tmp_tbl.GetCount() > (unsigned) tbl->GetNumberRows() )
188  tbl->AppendRows( tmp_tbl.GetCount() - tbl->GetNumberRows() );
189 
190  for( unsigned i = 0; i < tmp_tbl.GetCount(); ++i )
191  tbl->rows.replace( i, tmp_tbl.At( i ).clone() );
192  }
193 
194  m_grid->AutoSizeColumns( false );
195  }
196  else
197  {
198  // paste spreadsheet formatted text.
199  GRID_TRICKS::paste_text( cb_text );
200 
201  m_grid->AutoSizeColumns( false );
202  }
203  }
204 };
205 
206 
208  SYMBOL_LIB_TABLE* aGlobal,
209  const wxString& aGlobalTablePath,
210  SYMBOL_LIB_TABLE* aProject,
211  const wxString& aProjectTablePath,
212  const wxString& aProjectBasePath ) :
213  PANEL_SYM_LIB_TABLE_BASE( aParent ),
214  m_globalTable( aGlobal ),
215  m_projectTable( aProject ),
216  m_projectBasePath( aProjectBasePath ),
217  m_parent( aParent ),
218  m_lastBrowseDir( aProjectBasePath )
219 {
220  // For user info, shows the table filenames:
221  m_GblTableFilename->SetLabel( aGlobalTablePath );
222  m_PrjTableFilename->SetLabel( aProjectTablePath );
223 
224  // wxGrid only supports user owned tables if they exist past end of ~wxGrid(),
225  // so make it a grid owned table.
226  m_global_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aGlobal ), true );
227  m_project_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aProject ), true );
228 
229  // Give a bit more room for combobox editors
230  m_global_grid->SetDefaultRowSize( m_global_grid->GetDefaultRowSize() + 4 );
231  m_project_grid->SetDefaultRowSize( m_project_grid->GetDefaultRowSize() + 4 );
232 
233  // add Cut, Copy, and Paste to wxGrids
234  m_global_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_global_grid ) );
235  m_project_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_project_grid ) );
236  m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
237 
238  m_global_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
239  m_project_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
240 
241  m_global_grid->AutoSizeColumns( false );
242  m_project_grid->AutoSizeColumns( false );
243 
244  wxArrayString pluginChoices;
245 
246  pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) );
247  pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
248 
250 
251  for( wxGrid* g : { m_global_grid, m_project_grid } )
252  {
253  // Set special attributes
254  wxGridCellAttr* attr;
255 
256  attr = new wxGridCellAttr;
257  attr->SetEditor( new GRID_CELL_SYMLIB_EDITOR( m_parent, &m_lastBrowseDir,
259  g->SetColAttr( COL_URI, attr );
260 
261  attr = new wxGridCellAttr;
262  attr->SetEditor( new wxGridCellChoiceEditor( pluginChoices ) );
263  g->SetColAttr( COL_TYPE, attr );
264 
265  attr = new wxGridCellAttr;
266  attr->SetRenderer( new wxGridCellBoolRenderer() );
267  attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
268  g->SetColAttr( COL_ENABLED, attr );
269 
270  // all but COL_OPTIONS, which is edited with Option Editor anyways.
271  g->AutoSizeColumn( COL_NICKNAME, false );
272  g->AutoSizeColumn( COL_TYPE, false );
273  g->AutoSizeColumn( COL_URI, false );
274  g->AutoSizeColumn( COL_DESCR, false );
275  g->AutoSizeColumn( COL_ENABLED, false );
276 
277  // would set this to width of title, if it was easily known.
278  g->SetColSize( COL_OPTIONS, 80 );
279  }
280 
281  // select the last selected page
282  m_auinotebook->SetSelection( m_pageNdx );
284 
285  m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
286  m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
287 
288  // for ALT+A handling, we want the initial focus to be on the first selected grid.
290 
291  // Configure button logos
292  m_append_button->SetBitmap( KiBitmap( small_plus_xpm ) );
293  m_delete_button->SetBitmap( KiBitmap( trash_xpm ) );
294  m_move_up_button->SetBitmap( KiBitmap( small_up_xpm ) );
295  m_move_down_button->SetBitmap( KiBitmap( small_down_xpm ) );
296  m_browse_button->SetBitmap( KiBitmap( folder_xpm ) );
297 
298  // Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
299  // a currentCell which is sometimes not highlighted.
300  if( m_global_grid->GetNumberRows() > 0 )
301  m_global_grid->SelectRow( 0 );
302 
303  if( m_project_grid->GetNumberRows() > 0 )
304  m_project_grid->SelectRow( 0 );
305 }
306 
307 
309 {
310  // When the dialog is closed it will hide the current notebook page first, which will
311  // in turn select the other one. We then end up saving its index as the "current page".
312  // So flip them back again:
313  m_pageNdx = m_pageNdx == 1 ? 0 : 1;
314 
315  // Delete the GRID_TRICKS.
316  // Any additional event handlers should be popped before the window is deleted.
317  m_global_grid->PopEventHandler( true );
318  m_project_grid->PopEventHandler( true );
319  m_path_subs_grid->PopEventHandler( true );
320 }
321 
322 
324 {
325  for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
326  {
327  for( int r = 0; r < model->GetNumberRows(); )
328  {
329  wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
330  wxString uri = model->GetValue( r, COL_URI ).Trim( false ).Trim();
331  unsigned illegalCh = 0;
332 
333  if( !nick || !uri )
334  {
335  // Delete the "empty" row, where empty means missing nick or uri.
336  // This also updates the UI which could be slow, but there should only be a few
337  // rows to delete, unless the user fell asleep on the Add Row
338  // button.
339  model->DeleteRows( r, 1 );
340  }
341  else if( ( illegalCh = LIB_ID::FindIllegalLibNicknameChar( nick, LIB_ID::ID_SCH ) ) )
342  {
343  wxString msg = wxString::Format( _( "Illegal character '%c' in Nickname: \"%s\"" ),
344  illegalCh,
345  nick );
346 
347  // show the tabbed panel holding the grid we have flunked:
348  if( model != cur_model() )
349  m_auinotebook->SetSelection( model == global_model() ? 0 : 1 );
350 
351  m_cur_grid->MakeCellVisible( r, 0 );
352  m_cur_grid->SetGridCursor( r, 1 );
353 
354  wxMessageDialog errdlg( this, msg, _( "No Colon in Nicknames" ) );
355  errdlg.ShowModal();
356  return false;
357  }
358  else
359  {
360  // set the trimmed values back into the table so they get saved to disk.
361  model->SetValue( r, COL_NICKNAME, nick );
362  model->SetValue( r, COL_URI, uri );
363  ++r; // this row was OK.
364  }
365  }
366  }
367 
368  // check for duplicate nickNames, separately in each table.
369  for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
370  {
371  for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 )
372  {
373  wxString nick1 = model->GetValue( r1, COL_NICKNAME );
374 
375  for( int r2=r1+1; r2 < model->GetNumberRows(); ++r2 )
376  {
377  wxString nick2 = model->GetValue( r2, COL_NICKNAME );
378 
379  if( nick1 == nick2 )
380  {
381  wxString msg = wxString::Format( _( "Duplicate Nickname: \"%s\"." ), nick1 );
382 
383  // show the tabbed panel holding the grid we have flunked:
384  if( model != cur_model() )
385  m_auinotebook->SetSelection( model == global_model() ? 0 : 1 );
386 
387  // go to the lower of the two rows, it is technically the duplicate:
388  m_cur_grid->MakeCellVisible( r2, 0 );
389  m_cur_grid->SetGridCursor( r2, 1 );
390 
391  wxMessageDialog errdlg( this, msg, _( "Please Delete or Modify One" ) );
392  errdlg.ShowModal();
393 
394  return false;
395  }
396  }
397  }
398  }
399 
400  return true;
401 }
402 
403 
404 void PANEL_SYM_LIB_TABLE::pageChangedHandler( wxAuiNotebookEvent& event )
405 {
406  m_pageNdx = (unsigned) std::max( 0, m_auinotebook->GetSelection() );
408 }
409 
410 
411 void PANEL_SYM_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event )
412 {
413  wxString wildcards = KiCadSymbolLibFileWildcard();
414 
415  wildcards += "|" + LegacySymbolLibFileWildcard();
416 
417  wxFileDialog dlg( this, _( "Select Library" ), m_lastBrowseDir,
418  wxEmptyString, wildcards,
419  wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
420 
421  auto result = dlg.ShowModal();
422 
423  if( result == wxID_CANCEL )
424  return;
425 
426  m_lastBrowseDir = dlg.GetDirectory();
427 
428  const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
429  bool addDuplicates = false;
430  bool applyToAll = false;
431  wxString warning = _( "Warning: Duplicate Nickname" );
432  wxString msg = _( "A library nicknamed \"%s\" already exists." );
433  wxString detailedMsg = _( "Please change the library nickname after adding this library." );
434 
435  wxArrayString files;
436  dlg.GetFilenames( files );
437 
438  for( const auto& file : files )
439  {
440  wxString filePath = dlg.GetDirectory() + wxFileName::GetPathSeparator() + file;
441  wxFileName fn( filePath );
442  wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), LIB_ID::ID_SCH );
443  bool doAdd = true;
444 
445  if( cur_model()->ContainsNickname( nickname ) )
446  {
447  if( !applyToAll )
448  {
449  // The cancel button adds the library to the table anyway
450  addDuplicates = ( OKOrCancelDialog( this, warning, wxString::Format( msg, nickname ),
451  detailedMsg, _( "Skip" ), _( "Add Anyway" ), &applyToAll ) == wxID_CANCEL );
452  }
453 
454  doAdd = addDuplicates;
455  }
456 
457  if( doAdd && m_cur_grid->AppendRows( 1 ) )
458  {
459  int last_row = m_cur_grid->GetNumberRows() - 1;
460 
461  m_cur_grid->SetCellValue( last_row, COL_NICKNAME, nickname );
462 
463  // TODO the following code can detect only schematic types, not libs
464  // SCH_IO_MGR needs to provide file extension information for libraries too
465 
466  // auto detect the plugin type
467  for( auto pluginType : SCH_IO_MGR::SCH_FILE_T_vector )
468  {
469  if( SCH_IO_MGR::GetLibraryFileExtension( pluginType ).Lower() == fn.GetExt().Lower() )
470  {
471  m_cur_grid->SetCellValue( last_row, COL_TYPE,
472  SCH_IO_MGR::ShowType( pluginType ) );
473  break;
474  }
475  }
476 
477  // try to use path normalized to an environmental variable or project path
478  wxString path = NormalizePath( filePath, &envVars, m_projectBasePath );
479 
480  // Do not use the project path in the global library table. This will almost
481  // assuredly be wrong for a different project.
482  if( path.IsEmpty() || (m_pageNdx == 0 && path.Contains( "${KIPRJMOD}" )) )
483  path = fn.GetFullPath();
484 
485  m_cur_grid->SetCellValue( last_row, COL_URI, path );
486  }
487  }
488 
489  if( !files.IsEmpty() )
490  {
491  m_cur_grid->MakeCellVisible( m_cur_grid->GetNumberRows() - 1, 0 );
492  m_cur_grid->SetGridCursor( m_cur_grid->GetNumberRows() - 1, 1 );
493  }
494 }
495 
496 
497 void PANEL_SYM_LIB_TABLE::appendRowHandler( wxCommandEvent& event )
498 {
500  return;
501 
502  if( m_cur_grid->AppendRows( 1 ) )
503  {
504  int row = m_cur_grid->GetNumberRows() - 1;
505 
506  // wx documentation is wrong, SetGridCursor does not make visible.
507  m_cur_grid->MakeCellVisible( row, 0 );
508  m_cur_grid->SetGridCursor( row, 1 );
509  m_cur_grid->EnableCellEditControl( true );
510  m_cur_grid->ShowCellEditControl();
511  }
512 }
513 
514 
515 void PANEL_SYM_LIB_TABLE::deleteRowHandler( wxCommandEvent& event )
516 {
518  return;
519 
520  int curRow = m_cur_grid->GetGridCursorRow();
521  int curCol = m_cur_grid->GetGridCursorCol();
522 
523  // In a wxGrid, collect rows that have a selected cell, or are selected
524  // It is not so easy: it depends on the way the selection was made.
525  // Here, we collect rows selected by clicking on a row label, and rows that contain
526  // previously-selected cells.
527  // If no candidate, just delete the row with the grid cursor.
528  wxArrayInt selectedRows = m_cur_grid->GetSelectedRows();
529  wxGridCellCoordsArray cells = m_cur_grid->GetSelectedCells();
530  wxGridCellCoordsArray blockTopLeft = m_cur_grid->GetSelectionBlockTopLeft();
531  wxGridCellCoordsArray blockBotRight = m_cur_grid->GetSelectionBlockBottomRight();
532 
533  // Add all row having cell selected to list:
534  for( unsigned ii = 0; ii < cells.GetCount(); ii++ )
535  selectedRows.Add( cells[ii].GetRow() );
536 
537  // Handle block selection
538  if( !blockTopLeft.IsEmpty() && !blockBotRight.IsEmpty() )
539  {
540  for( int i = blockTopLeft[0].GetRow(); i <= blockBotRight[0].GetRow(); ++i )
541  selectedRows.Add( i );
542  }
543 
544  // Use the row having the grid cursor only if we have no candidate:
545  if( selectedRows.size() == 0 && m_cur_grid->GetGridCursorRow() >= 0 )
546  selectedRows.Add( m_cur_grid->GetGridCursorRow() );
547 
548  if( selectedRows.size() == 0 )
549  {
550  wxBell();
551  return;
552  }
553 
554  std::sort( selectedRows.begin(), selectedRows.end() );
555 
556  // Remove selected rows (note: a row can be stored more than once in list)
557  int last_row = -1;
558 
559  for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
560  {
561  int row = selectedRows[ii];
562 
563  if( row != last_row )
564  {
565  last_row = row;
566  m_cur_grid->DeleteRows( row, 1 );
567  }
568  }
569 
570  if( m_cur_grid->GetNumberRows() > 0 && curRow >= 0 )
571  m_cur_grid->SetGridCursor( std::min( curRow, m_cur_grid->GetNumberRows() - 1 ), curCol );
572 }
573 
574 
575 void PANEL_SYM_LIB_TABLE::moveUpHandler( wxCommandEvent& event )
576 {
578  return;
579 
581  int curRow = m_cur_grid->GetGridCursorRow();
582 
583  // @todo: add multiple selection moves.
584  if( curRow >= 1 )
585  {
586  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
587  tbl->rows.release( tbl->rows.begin() + curRow );
588 
589  --curRow;
590  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
591 
592  if( tbl->GetView() )
593  {
594  // Update the wxGrid
595  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow, 0 );
596  tbl->GetView()->ProcessTableMessage( msg );
597  }
598 
599  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
600  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
601  }
602 }
603 
604 
605 void PANEL_SYM_LIB_TABLE::moveDownHandler( wxCommandEvent& event )
606 {
608  return;
609 
611  int curRow = m_cur_grid->GetGridCursorRow();
612 
613  // @todo: add multiple selection moves.
614  if( unsigned( curRow + 1 ) < tbl->rows.size() )
615  {
616  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
617  tbl->rows.release( tbl->rows.begin() + curRow );
618 
619  ++curRow;
620  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
621 
622  if( tbl->GetView() )
623  {
624  // Update the wxGrid
625  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow - 1, 0 );
626  tbl->GetView()->ProcessTableMessage( msg );
627  }
628 
629  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
630  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
631  }
632 }
633 
634 
636 {
638  return false;
639 
640  if( !verifyTables() )
641  return false;
642 
643  if( *global_model() != *m_globalTable )
644  {
646 
647  m_globalTable->Clear();
648  m_globalTable->rows.transfer( m_globalTable->rows.end(), global_model()->rows.begin(),
649  global_model()->rows.end(), global_model()->rows );
651  }
652 
653  if( *project_model() != *m_projectTable )
654  {
656 
658  m_projectTable->rows.transfer( m_projectTable->rows.end(), project_model()->rows.begin(),
659  project_model()->rows.end(), project_model()->rows );
661  }
662 
663  return true;
664 }
665 
666 
668 {
669  wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
670  wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
671 
672  std::set< wxString > unique;
673 
674  // clear the table
675  m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() );
676 
677  for( SYMBOL_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
678  {
679  for( int row = 0; row < tbl->GetNumberRows(); ++row )
680  {
681  wxString uri = tbl->GetValue( row, COL_URI );
682 
683  while( re.Matches( uri ) )
684  {
685  wxString envvar = re.GetMatch( uri, 2 );
686 
687  // if not ${...} form then must be $(...)
688  if( envvar.IsEmpty() )
689  envvar = re.GetMatch( uri, 4 );
690 
691  // ignore duplicates
692  unique.insert( envvar );
693 
694  // delete the last match and search again
695  uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
696  }
697  }
698  }
699 
700  // Make sure this special environment variable shows up even if it was
701  // not used yet. It is automatically set by KiCad to the directory holding
702  // the current project.
703  unique.insert( PROJECT_VAR_NAME );
705 
706  for( const wxString& evName : unique )
707  {
708  int row = m_path_subs_grid->GetNumberRows();
709  m_path_subs_grid->AppendRows( 1 );
710 
711  m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
712  m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
713 
714  wxString evValue;
715  wxGetEnv( evName, &evValue );
716  m_path_subs_grid->SetCellValue( row, 1, evValue );
717  m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
718  }
719 
720  // No combobox editors here, but it looks better if its consistent with the other
721  // grids in the dialog.
722  m_path_subs_grid->SetDefaultRowSize( m_path_subs_grid->GetDefaultRowSize() + 2 );
723 
724  adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
725 }
726 
727 
729 {
730  // Account for scroll bars
731  aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
732 
733  m_path_subs_grid->AutoSizeColumn( 0 );
734  m_path_subs_grid->SetColSize( 1, aWidth - m_path_subs_grid->GetColSize( 0 ) );
735 }
736 
737 
738 void PANEL_SYM_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
739 {
740  adjustPathSubsGridColumns( event.GetSize().GetX() );
741 
742  event.Skip();
743 }
744 
745 
747 {
748  return (SYMBOL_LIB_TABLE_GRID*) m_global_grid->GetTable();
749 }
750 
751 
753 {
754  return (SYMBOL_LIB_TABLE_GRID*) m_project_grid->GetTable();
755 }
756 
757 
759 {
760  return (SYMBOL_LIB_TABLE_GRID*) m_cur_grid->GetTable();
761 }
762 
763 
765 
766 
767 void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
768 {
769  SCH_EDIT_FRAME* schEditor = (SCH_EDIT_FRAME*) aKiway->Player( FRAME_SCH, false );
770  LIB_EDIT_FRAME* libEditor = (LIB_EDIT_FRAME*) aKiway->Player( FRAME_SCH_LIB_EDITOR, false );
771  LIB_VIEW_FRAME* libViewer = (LIB_VIEW_FRAME*) aKiway->Player( FRAME_SCH_VIEWER, false );
772 
774  wxString globalTablePath = SYMBOL_LIB_TABLE::GetGlobalTableFileName();
775  SYMBOL_LIB_TABLE* projectTable = aKiway->Prj().SchSymbolLibTable();
776  wxString projectPath = aKiway->Prj().GetProjectPath();
777  wxFileName projectTableFn( projectPath, SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
778  wxString msg;
779  wxString currentLib;
780 
781  if( libEditor )
782  {
783  currentLib = libEditor->GetCurLib();
784 
785  // This prevents an ugly crash on OSX (https://bugs.launchpad.net/kicad/+bug/1765286)
786  libEditor->FreezeSearchTree();
787 
788  if( libEditor->HasLibModifications() )
789  {
790  msg = _( "Modifications have been made to one or more symbol libraries.\n"
791  "Changes must be saved or discarded before the symbol library "
792  "table can be modified." );
793 
794  switch( UnsavedChangesDialog( aParent, msg ) )
795  {
796  case wxID_YES: libEditor->SaveAll(); break;
797  case wxID_NO: libEditor->RevertAll(); break;
798  default:
799  case wxID_CANCEL: libEditor->ThawSearchTree(); return;
800  }
801  }
802  }
803 
804  DIALOG_EDIT_LIBRARY_TABLES dlg( aParent, _( "Symbol Libraries" ) );
805 
806  dlg.InstallPanel( new PANEL_SYM_LIB_TABLE( &dlg, globalTable, globalTablePath,
807  projectTable, projectTableFn.GetFullPath(),
808  aKiway->Prj().GetProjectPath() ) );
809 
810  if( dlg.ShowModal() == wxID_CANCEL )
811  {
812  if( libEditor )
813  libEditor->ThawSearchTree();
814 
815  return;
816  }
817 
818  if( dlg.m_GlobalTableChanged )
819  {
820  try
821  {
822  globalTable->Save( globalTablePath );
823  }
824  catch( const IO_ERROR& ioe )
825  {
826  msg.Printf( _( "Error saving global library table:\n\n%s" ), ioe.What() );
827  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
828  }
829  }
830 
831  if( dlg.m_ProjectTableChanged )
832  {
833  try
834  {
835  projectTable->Save( projectTableFn.GetFullPath() );
836  }
837  catch( const IO_ERROR& ioe )
838  {
839  msg.Printf( _( "Error saving project-specific library table:\n\n%s" ), ioe.What() );
840  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
841  }
842  }
843 
844  if( schEditor )
845  schEditor->SyncView();
846 
847  if( libEditor )
848  {
849  // Check if the currently selected symbol library been removed or disabled.
850  if( !currentLib.empty()
851  && !projectTable->HasLibrary( currentLib, true ) )
852  {
853  libEditor->SetCurLib( wxEmptyString );
854  libEditor->emptyScreen();
855  }
856 
857  libEditor->SyncLibraries( true );
858  libEditor->ThawSearchTree();
859  }
860 
861  if( libViewer )
862  libViewer->ReCreateListLib();
863 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:239
const BITMAP_OPAQUE trash_xpm[1]
Definition: trash.cpp:46
wxString m_FileFilter
Filter used for file pickers if m_IsFile is true.
static const wxString & GetSymbolLibTableFileName()
const BITMAP_OPAQUE folder_xpm[1]
Definition: folder.cpp:20
static SYMBOL_LIB_TABLE & GetGlobalLibTable()
virtual void paste_text(const wxString &cb_text) override
handle specialized clipboard text, with leading "(sym_lib_table" or spreadsheet formatted text.
bool m_IsFile
Whether the library is a folder or a file.
void moveUpHandler(wxCommandEvent &event) override
void pageChangedHandler(wxAuiNotebookEvent &event) override
SCH_IO_MGR::SCH_FILE_T m_Plugin
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
void Clear()
Delete all rows.
PANEL_SYM_LIB_TABLE(DIALOG_EDIT_LIBRARY_TABLES *aParent, SYMBOL_LIB_TABLE *aGlobal, const wxString &aGlobalTablePath, SYMBOL_LIB_TABLE *aProject, const wxString &aProjectTablePath, const wxString &m_projectBasePath)
This file is part of the common library.
void reindex()
bool ContainsNickname(const wxString &aNickname)
VTBL_ENTRY PROJECT & Prj() const
Function Prj returns the PROJECT associated with this KIWAY.
Definition: kiway.cpp:172
unsigned GetCount() const
Get the number of rows contained in the table.
static const wxString ShowType(SCH_FILE_T aFileType)
Return a brief name for a plugin, given aFileType enum.
Definition: sch_io_mgr.cpp:81
LIB_TABLE_ROW * at(size_t aIndex) override
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
void InvokeSchEditSymbolLibTable(KIWAY *aKiway, wxWindow *aParent)
WX_GRID * m_cur_grid
changed based on tab choice
GRID_TRICKS is used to add mouse and command handling (such as cut, copy, and paste) to a WX_GRID ins...
Definition: grid_tricks.h:51
static const wxString GetLibraryFileExtension(SCH_FILE_T aFileType)
Return the symbol library file extension (if any) for aFileType.
Definition: sch_io_mgr.cpp:138
static unsigned FindIllegalLibNicknameChar(const UTF8 &aNickname, LIB_ID_TYPE aType)
Looks for characters that are illegal in library nicknames.
Definition: lib_id.cpp:404
SYMBOL_LIB_TABLE_GRID * global_model() const
static const wxString GlobalPathEnvVariableName()
Return the name of the environment variable used to hold the directory of locally installed "KiCad sp...
static size_t m_pageNdx
Remember the last notebook page selected during a session.
Schematic editor (Eeschema) main window.
LIB_TABLE_ROWS rows
Container that describes file type info for the add a library options.
void SetTable(wxGridTableBase *table, bool aTakeOwnership=false)
Hide wxGrid's SetTable() method with one which doesn't mess up the grid column widths when setting th...
Definition: wx_grid.cpp:59
int GetNumberRows() override
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:122
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:114
Symbol library viewer main window.
void adjustPathSubsGridColumns(int aWidth)
wxString GetCurLib() const
The nickname of the current library being edited and empty string if none.
This abstract base class mixes any object derived from LIB_TABLE into wxGridTableBase so the result c...
WX_GRID * m_grid
I don't own the grid, but he owns me.
Definition: grid_tricks.h:58
virtual void paste_text(const wxString &cb_text)
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:80
void populateEnvironReadOnlyTable()
Populate the readonly environment variable table with names and values by examining all the full_uri ...
LIB_TABLE_ROW * clone() const
Build a wxGridTableBase by wrapping an SYMBOL_LIB_TABLE object.
VTBL_ENTRY KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=NULL)
Function Player returns the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:342
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
Definition: pgm_base.h:117
void SaveAll()
Saves all modified parts and libraries.
SYMBOL_LIB_TABLE_GRID * cur_model() const
wxString m_FolderSearchExtension
In case of folders it stands for extensions of files stored inside.
void SyncView()
Mark all items for refresh.
SYMBOL_LIB_TABLE * m_projectTable
bool TransferDataFromWindow() override
const BITMAP_OPAQUE small_down_xpm[1]
Definition: small_down.cpp:25
Class PANEL_SYM_LIB_TABLE_BASE.
Definition of file extensions used in Kicad.
DIALOG_EDIT_LIBRARY_TABLES * m_dialog
KIWAY is a minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the s...
Definition: kiway.h:273
void push_back(LIB_TABLE_ROW *aRow) override
wxString KiCadSymbolLibFileWildcard()
Dialog to show and edit symbol library tables.
void deleteRowHandler(wxCommandEvent &event) override
bool CommitPendingChanges(bool aQuietMode=false)
Close any open cell edit controls.
Definition: wx_grid.cpp:174
bool AppendRows(size_t aNumRows=1) override
int UnsavedChangesDialog(wxWindow *parent, wxString aMessage, bool *aApplyToAll)
A specialized version of HandleUnsavedChanges which handles an apply-to-all checkbox.
Definition: confirm.cpp:148
wxString LegacySymbolLibFileWildcard()
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Displays a warning dialog with aMessage and returns the user response.
Definition: confirm.cpp:214
void appendRowHandler(wxCommandEvent &event) override
const BITMAP_OPAQUE small_up_xpm[1]
Definition: small_up.cpp:26
SYMBOL_LIB_TABLE_GRID(const SYMBOL_LIB_TABLE &aTableToEdit)
void emptyScreen()
Restores the empty editor screen, without any part or library selected.
DIALOG_EDIT_LIBRARY_TABLES * m_parent
static const std::map< int, supportedFileType > & fileTypes()
Map with event id as the key to supported file types that will be listed for the add a library option...
void SyncLibraries(bool aShowProgress)
Synchronize the library manager to the symbol library table, and then the symbol tree to the library ...
SYMBOL_LIB_TABLE_GRID * project_model() const
Struct PARSE_ERROR contains a filename or source description, a problem input line,...
Definition: ki_exception.h:123
The symbol library editor main window.
wxString m_lastBrowseDir
last browsed directory
bool HasLibModifications() const
Check if any pending libraries have been modified.
LIB_TABLE_ROW & At(unsigned aIndex)
Get the 'n'th LIB_TABLE_ROW object.
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 _(s)
Definition: 3d_actions.cpp:33
LIB_TABLE_ROWS_ITER erase(LIB_TABLE_ROWS_ITER aFirst, LIB_TABLE_ROWS_ITER aLast) override
virtual void Parse(LIB_TABLE_LEXER *aLexer) override
Parse the #LIB_TABLE_LEXER s-expression library table format into the appropriate LIB_TABLE_ROW objec...
LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
#define TO_UTF8(wxstring)
bool verifyTables()
Trim important fields, removes blank row entries, and checks for duplicates.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, LIB_ID_TYPE aType, bool aLib=false)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:352
wxString SetCurLib(const wxString &aLibNickname)
Sets the current library nickname and returns the old library nickname.
void onSizeGrid(wxSizeEvent &event) override
LIB_TABLE_ROWS_ITER insert(LIB_TABLE_ROWS_ITER aIterator, LIB_TABLE_ROW *aRow) override
SYMBOL_LIB_TABLE * m_globalTable
bool ReCreateListLib()
Creates or recreates a sorted list of currently loaded libraries.
LIB_TABLE_ROW * makeNewRow() override
STRING_LINE_READER is a LINE_READER that reads from a multiline 8 bit wide std::string.
Definition: richio.h:254
LIB_TABLE_ROWS_ITER begin() override
wxString m_Description
Description shown in the file picker dialog.
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
static wxString GetGlobalTableFileName()
Fetch the global symbol library table file name.
size_t size() const override
const BITMAP_OPAQUE small_plus_xpm[1]
Definition: small_plus.cpp:20
void moveDownHandler(wxCommandEvent &event) override
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalizes a file path to an environmental variable, if possible.
Definition: env_paths.cpp:68
SYMBOL_GRID_TRICKS(DIALOG_EDIT_LIBRARY_TABLES *aParent, WX_GRID *aGrid)
void browseLibrariesHandler(wxCommandEvent &event) override