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-2019 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_LEGACY ) );
247  pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) );
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 = SchematicLibraryFileWildcard();
414 
415  wildcards += "|" + KiCadSymbolLibFileWildcard();
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  m_cur_grid->SetGridCursor( std::min( curRow, m_cur_grid->GetNumberRows() - 1 ), curCol );
571 }
572 
573 
574 void PANEL_SYM_LIB_TABLE::moveUpHandler( wxCommandEvent& event )
575 {
577  return;
578 
580  int curRow = m_cur_grid->GetGridCursorRow();
581 
582  // @todo: add multiple selection moves.
583  if( curRow >= 1 )
584  {
585  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
586  tbl->rows.release( tbl->rows.begin() + curRow );
587 
588  --curRow;
589  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
590 
591  if( tbl->GetView() )
592  {
593  // Update the wxGrid
594  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow, 0 );
595  tbl->GetView()->ProcessTableMessage( msg );
596  }
597 
598  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
599  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
600  }
601 }
602 
603 
604 void PANEL_SYM_LIB_TABLE::moveDownHandler( wxCommandEvent& event )
605 {
607  return;
608 
610  int curRow = m_cur_grid->GetGridCursorRow();
611 
612  // @todo: add multiple selection moves.
613  if( unsigned( curRow + 1 ) < tbl->rows.size() )
614  {
615  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
616  tbl->rows.release( tbl->rows.begin() + curRow );
617 
618  ++curRow;
619  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
620 
621  if( tbl->GetView() )
622  {
623  // Update the wxGrid
624  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow - 1, 0 );
625  tbl->GetView()->ProcessTableMessage( msg );
626  }
627 
628  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
629  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
630  }
631 }
632 
633 
635 {
637  return false;
638 
639  if( !verifyTables() )
640  return false;
641 
642  if( *global_model() != *m_globalTable )
643  {
645 
646  m_globalTable->Clear();
647  m_globalTable->rows.transfer( m_globalTable->rows.end(), global_model()->rows.begin(),
648  global_model()->rows.end(), global_model()->rows );
650  }
651 
652  if( *project_model() != *m_projectTable )
653  {
655 
657  m_projectTable->rows.transfer( m_projectTable->rows.end(), project_model()->rows.begin(),
658  project_model()->rows.end(), project_model()->rows );
660  }
661 
662  return true;
663 }
664 
665 
667 {
668  wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
669  wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
670 
671  std::set< wxString > unique;
672 
673  // clear the table
674  m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() );
675 
676  for( SYMBOL_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
677  {
678  for( int row = 0; row < tbl->GetNumberRows(); ++row )
679  {
680  wxString uri = tbl->GetValue( row, COL_URI );
681 
682  while( re.Matches( uri ) )
683  {
684  wxString envvar = re.GetMatch( uri, 2 );
685 
686  // if not ${...} form then must be $(...)
687  if( envvar.IsEmpty() )
688  envvar = re.GetMatch( uri, 4 );
689 
690  // ignore duplicates
691  unique.insert( envvar );
692 
693  // delete the last match and search again
694  uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
695  }
696  }
697  }
698 
699  // Make sure this special environment variable shows up even if it was
700  // not used yet. It is automatically set by KiCad to the directory holding
701  // the current project.
702  unique.insert( PROJECT_VAR_NAME );
704 
705  for( const wxString& evName : unique )
706  {
707  int row = m_path_subs_grid->GetNumberRows();
708  m_path_subs_grid->AppendRows( 1 );
709 
710  m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
711  m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
712 
713  wxString evValue;
714  wxGetEnv( evName, &evValue );
715  m_path_subs_grid->SetCellValue( row, 1, evValue );
716  m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
717  }
718 
719  // No combobox editors here, but it looks better if its consistent with the other
720  // grids in the dialog.
721  m_path_subs_grid->SetDefaultRowSize( m_path_subs_grid->GetDefaultRowSize() + 2 );
722 
723  adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
724 }
725 
726 
728 {
729  // Account for scroll bars
730  aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
731 
732  m_path_subs_grid->AutoSizeColumn( 0 );
733  m_path_subs_grid->SetColSize( 1, aWidth - m_path_subs_grid->GetColSize( 0 ) );
734 }
735 
736 
737 void PANEL_SYM_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
738 {
739  adjustPathSubsGridColumns( event.GetSize().GetX() );
740 
741  event.Skip();
742 }
743 
744 
746 {
747  return (SYMBOL_LIB_TABLE_GRID*) m_global_grid->GetTable();
748 }
749 
750 
752 {
753  return (SYMBOL_LIB_TABLE_GRID*) m_project_grid->GetTable();
754 }
755 
756 
758 {
759  return (SYMBOL_LIB_TABLE_GRID*) m_cur_grid->GetTable();
760 }
761 
762 
764 
765 
766 void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
767 {
768  SCH_EDIT_FRAME* schEditor = (SCH_EDIT_FRAME*) aKiway->Player( FRAME_SCH, false );
769  LIB_EDIT_FRAME* libEditor = (LIB_EDIT_FRAME*) aKiway->Player( FRAME_SCH_LIB_EDITOR, false );
770  LIB_VIEW_FRAME* libViewer = (LIB_VIEW_FRAME*) aKiway->Player( FRAME_SCH_VIEWER, false );
771 
773  wxString globalTablePath = SYMBOL_LIB_TABLE::GetGlobalTableFileName();
774  SYMBOL_LIB_TABLE* projectTable = aKiway->Prj().SchSymbolLibTable();
775  wxString projectPath = aKiway->Prj().GetProjectPath();
776  wxFileName projectTableFn( projectPath, SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
777  wxString msg;
778  wxString currentLib;
779 
780  if( libEditor )
781  {
782  currentLib = libEditor->GetCurLib();
783 
784  // This prevents an ugly crash on OSX (https://bugs.launchpad.net/kicad/+bug/1765286)
785  libEditor->FreezeSearchTree();
786 
787  if( libEditor->HasLibModifications() )
788  {
789  msg = _( "Modifications have been made to one or more symbol libraries.\n"
790  "Changes must be saved or discarded before the symbol library "
791  "table can be modified." );
792 
793  switch( UnsavedChangesDialog( aParent, msg ) )
794  {
795  case wxID_YES: libEditor->SaveAll(); break;
796  case wxID_NO: libEditor->RevertAll(); break;
797  default:
798  case wxID_CANCEL: libEditor->ThawSearchTree(); return;
799  }
800  }
801  }
802 
803  DIALOG_EDIT_LIBRARY_TABLES dlg( aParent, _( "Symbol Libraries" ) );
804 
805  dlg.InstallPanel( new PANEL_SYM_LIB_TABLE( &dlg, globalTable, globalTablePath,
806  projectTable, projectTableFn.GetFullPath(),
807  aKiway->Prj().GetProjectPath() ) );
808 
809  if( dlg.ShowModal() == wxID_CANCEL )
810  {
811  if( libEditor )
812  libEditor->ThawSearchTree();
813 
814  return;
815  }
816 
817  if( dlg.m_GlobalTableChanged )
818  {
819  try
820  {
821  globalTable->Save( globalTablePath );
822  }
823  catch( const IO_ERROR& ioe )
824  {
825  msg.Printf( _( "Error saving global library table:\n\n%s" ), ioe.What() );
826  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
827  }
828  }
829 
830  if( dlg.m_ProjectTableChanged )
831  {
832  try
833  {
834  projectTable->Save( projectTableFn.GetFullPath() );
835  }
836  catch( const IO_ERROR& ioe )
837  {
838  msg.Printf( _( "Error saving project-specific library table:\n\n%s" ), ioe.What() );
839  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
840  }
841  }
842 
843  SCH_SCREENS schematic;
844 
845  schematic.UpdateSymbolLinks( true ); // Update all symbol library links for all sheets.
846 
847  if( schEditor )
848  schEditor->SyncView();
849 
850  if( libEditor )
851  {
852  // Check if the currently selected symbol library been removed or disabled.
853  if( !currentLib.empty()
854  && !projectTable->HasLibrary( currentLib, true ) )
855  {
856  libEditor->SetCurLib( wxEmptyString );
857  libEditor->emptyScreen();
858  }
859 
860  libEditor->SyncLibraries( true );
861  libEditor->ThawSearchTree();
862  }
863 
864  if( libViewer )
865  libViewer->ReCreateListLib();
866 }
wxString SchematicSymbolFileWildcard()
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:103
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:171
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:79
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:136
static unsigned FindIllegalLibNicknameChar(const UTF8 &aNickname, LIB_ID_TYPE aType)
Looks for characters that are illegal in library nicknames.
Definition: lib_id.cpp:404
wxString SchematicLibraryFileWildcard()
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:57
int GetNumberRows() override
VTBL_ENTRY const wxString GetProjectPath() const
Function GetProjectPath returns the full path of the project.
Definition: project.cpp:102
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition: dialog_shim.h:115
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)
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:48
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:341
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:172
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
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.
void UpdateSymbolLinks(bool aForce=false)
Initialize or reinitialize the weak reference to the LIB_PART for each SCH_COMPONENT found in the ful...
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.
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
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:491
SYMBOL_GRID_TRICKS(DIALOG_EDIT_LIBRARY_TABLES *aParent, WX_GRID *aGrid)
void browseLibrariesHandler(wxCommandEvent &event) override