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 <lib_edit_frame.h>
38 #include <lib_view_frame.h>
39 #include <sch_edit_frame.h>
40 #include <kiway.h>
41 #include <sch_screen.h>
42 
45 
50 {
51  friend class SYMBOL_GRID_TRICKS;
52 
53 protected:
54  LIB_TABLE_ROW* at( size_t aIndex ) override { return &rows.at( aIndex ); }
55 
56  size_t size() const override { return rows.size(); }
57 
59  {
60  return dynamic_cast< LIB_TABLE_ROW* >( new SYMBOL_LIB_TABLE_ROW );
61  }
62 
63  LIB_TABLE_ROWS_ITER begin() override { return rows.begin(); }
64 
66  {
67  return rows.insert( aIterator, aRow );
68  }
69 
70  void push_back( LIB_TABLE_ROW* aRow ) override { rows.push_back( aRow ); }
71 
73  {
74  return rows.erase( aFirst, aLast );
75  }
76 
77 public:
78 
79  SYMBOL_LIB_TABLE_GRID( const SYMBOL_LIB_TABLE& aTableToEdit )
80  {
81  rows = aTableToEdit.rows;
82  }
83 };
84 
85 
87 {
88 public:
90  GRID_TRICKS( aGrid ),
91  m_dialog( aParent )
92  {
93  }
94 
95 protected:
97 
100  virtual void paste_text( const wxString& cb_text ) override
101  {
102  SYMBOL_LIB_TABLE_GRID* tbl = (SYMBOL_LIB_TABLE_GRID*) m_grid->GetTable();
103  size_t ndx = cb_text.find( "(sym_lib_table" );
104 
105  if( ndx != std::string::npos )
106  {
107  // paste the SYMBOL_LIB_TABLE_ROWs of s-expression (sym_lib_table), starting
108  // at column 0 regardless of current cursor column.
109 
110  STRING_LINE_READER slr( TO_UTF8( cb_text ), "Clipboard" );
111  LIB_TABLE_LEXER lexer( &slr );
112  SYMBOL_LIB_TABLE tmp_tbl;
113  bool parsed = true;
114 
115  try
116  {
117  tmp_tbl.Parse( &lexer );
118  }
119  catch( PARSE_ERROR& pe )
120  {
121  DisplayError( m_dialog, pe.What() );
122  parsed = false;
123  }
124 
125  if( parsed )
126  {
127  // make sure the table is big enough...
128  if( tmp_tbl.GetCount() > (unsigned) tbl->GetNumberRows() )
129  tbl->AppendRows( tmp_tbl.GetCount() - tbl->GetNumberRows() );
130 
131  for( unsigned i = 0; i < tmp_tbl.GetCount(); ++i )
132  tbl->rows.replace( i, tmp_tbl.At( i ).clone() );
133  }
134 
135  m_grid->AutoSizeColumns( false );
136  }
137  else
138  {
139  // paste spreadsheet formatted text.
140  GRID_TRICKS::paste_text( cb_text );
141 
142  m_grid->AutoSizeColumns( false );
143  }
144  }
145 };
146 
147 
149  SYMBOL_LIB_TABLE* aGlobal,
150  const wxString& aGlobalTablePath,
151  SYMBOL_LIB_TABLE* aProject,
152  const wxString& aProjectTablePath,
153  const wxString& aProjectBasePath ) :
154  PANEL_SYM_LIB_TABLE_BASE( aParent ),
155  m_globalTable( aGlobal ),
156  m_projectTable( aProject ),
157  m_projectBasePath( aProjectBasePath ),
158  m_parent( aParent ),
159  m_lastBrowseDir( aProjectBasePath )
160 {
161  // For user info, shows the table filenames:
162  m_GblTableFilename->SetLabel( aGlobalTablePath );
163  m_PrjTableFilename->SetLabel( aProjectTablePath );
164 
165  // wxGrid only supports user owned tables if they exist past end of ~wxGrid(),
166  // so make it a grid owned table.
167  m_global_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aGlobal ), true );
168  m_project_grid->SetTable( new SYMBOL_LIB_TABLE_GRID( *aProject ), true );
169 
170  // Give a bit more room for combobox editors
171  m_global_grid->SetDefaultRowSize( m_global_grid->GetDefaultRowSize() + 4 );
172  m_project_grid->SetDefaultRowSize( m_project_grid->GetDefaultRowSize() + 4 );
173 
174  // add Cut, Copy, and Paste to wxGrids
175  m_global_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_global_grid ) );
176  m_project_grid->PushEventHandler( new SYMBOL_GRID_TRICKS( m_parent, m_project_grid ) );
177  m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
178 
179  m_global_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
180  m_project_grid->SetSelectionMode( wxGrid::wxGridSelectRows );
181 
182  m_global_grid->AutoSizeColumns( false );
183  m_project_grid->AutoSizeColumns( false );
184 
185  wxArrayString pluginChoices;
186 
187 // pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) );
188  pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
189 
191 
192  for( wxGrid* g : { m_global_grid, m_project_grid } )
193  {
194  // Set special attributes
195  wxGridCellAttr* attr;
196 
197  attr = new wxGridCellAttr;
198  attr->SetEditor( new GRID_CELL_SYMLIB_EDITOR( m_parent, &m_lastBrowseDir,
200  g->SetColAttr( COL_URI, attr );
201 
202  attr = new wxGridCellAttr;
203  attr->SetEditor( new wxGridCellChoiceEditor( pluginChoices ) );
204  g->SetColAttr( COL_TYPE, attr );
205 
206  attr = new wxGridCellAttr;
207  attr->SetRenderer( new wxGridCellBoolRenderer() );
208  attr->SetReadOnly(); // not really; we delegate interactivity to GRID_TRICKS
209  g->SetColAttr( COL_ENABLED, attr );
210 
211  // all but COL_OPTIONS, which is edited with Option Editor anyways.
212  g->AutoSizeColumn( COL_NICKNAME, false );
213  g->AutoSizeColumn( COL_TYPE, false );
214  g->AutoSizeColumn( COL_URI, false );
215  g->AutoSizeColumn( COL_DESCR, false );
216  g->AutoSizeColumn( COL_ENABLED, false );
217 
218  // would set this to width of title, if it was easily known.
219  g->SetColSize( COL_OPTIONS, 80 );
220  }
221 
222  // select the last selected page
223  m_auinotebook->SetSelection( m_pageNdx );
225 
226  m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
227  m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
228 
229  // for ALT+A handling, we want the initial focus to be on the first selected grid.
231 
232  // Configure button logos
233  m_append_button->SetBitmap( KiBitmap( small_plus_xpm ) );
234  m_delete_button->SetBitmap( KiBitmap( trash_xpm ) );
235  m_move_up_button->SetBitmap( KiBitmap( small_up_xpm ) );
236  m_move_down_button->SetBitmap( KiBitmap( small_down_xpm ) );
237  m_browse_button->SetBitmap( KiBitmap( folder_xpm ) );
238 
239  // Gives a selection to each grid, mainly for delete button. wxGrid's wake up with
240  // a currentCell which is sometimes not highlighted.
241  if( m_global_grid->GetNumberRows() > 0 )
242  m_global_grid->SelectRow( 0 );
243 
244  if( m_project_grid->GetNumberRows() > 0 )
245  m_project_grid->SelectRow( 0 );
246 }
247 
248 
250 {
251  // When the dialog is closed it will hide the current notebook page first, which will
252  // in turn select the other one. We then end up saving its index as the "current page".
253  // So flip them back again:
254  m_pageNdx = m_pageNdx == 1 ? 0 : 1;
255 
256  // Delete the GRID_TRICKS.
257  // Any additional event handlers should be popped before the window is deleted.
258  m_global_grid->PopEventHandler( true );
259  m_project_grid->PopEventHandler( true );
260  m_path_subs_grid->PopEventHandler( true );
261 }
262 
263 
265 {
266  for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
267  {
268  for( int r = 0; r < model->GetNumberRows(); )
269  {
270  wxString nick = model->GetValue( r, COL_NICKNAME ).Trim( false ).Trim();
271  wxString uri = model->GetValue( r, COL_URI ).Trim( false ).Trim();
272  unsigned illegalCh = 0;
273 
274  if( !nick || !uri )
275  {
276  // Delete the "empty" row, where empty means missing nick or uri.
277  // This also updates the UI which could be slow, but there should only be a few
278  // rows to delete, unless the user fell asleep on the Add Row
279  // button.
280  model->DeleteRows( r, 1 );
281  }
282  else if( ( illegalCh = LIB_ID::FindIllegalLibNicknameChar( nick, LIB_ID::ID_SCH ) ) )
283  {
284  wxString msg = wxString::Format( _( "Illegal character '%c' in Nickname: \"%s\"" ),
285  illegalCh,
286  nick );
287 
288  // show the tabbed panel holding the grid we have flunked:
289  if( model != cur_model() )
290  m_auinotebook->SetSelection( model == global_model() ? 0 : 1 );
291 
292  m_cur_grid->MakeCellVisible( r, 0 );
293  m_cur_grid->SetGridCursor( r, 1 );
294 
295  wxMessageDialog errdlg( this, msg, _( "No Colon in Nicknames" ) );
296  errdlg.ShowModal();
297  return false;
298  }
299  else
300  {
301  // set the trimmed values back into the table so they get saved to disk.
302  model->SetValue( r, COL_NICKNAME, nick );
303  model->SetValue( r, COL_URI, uri );
304  ++r; // this row was OK.
305  }
306  }
307  }
308 
309  // check for duplicate nickNames, separately in each table.
310  for( SYMBOL_LIB_TABLE_GRID* model : { global_model(), project_model() } )
311  {
312  for( int r1 = 0; r1 < model->GetNumberRows() - 1; ++r1 )
313  {
314  wxString nick1 = model->GetValue( r1, COL_NICKNAME );
315 
316  for( int r2=r1+1; r2 < model->GetNumberRows(); ++r2 )
317  {
318  wxString nick2 = model->GetValue( r2, COL_NICKNAME );
319 
320  if( nick1 == nick2 )
321  {
322  wxString msg = wxString::Format( _( "Duplicate Nickname: \"%s\"." ), nick1 );
323 
324  // show the tabbed panel holding the grid we have flunked:
325  if( model != cur_model() )
326  m_auinotebook->SetSelection( model == global_model() ? 0 : 1 );
327 
328  // go to the lower of the two rows, it is technically the duplicate:
329  m_cur_grid->MakeCellVisible( r2, 0 );
330  m_cur_grid->SetGridCursor( r2, 1 );
331 
332  wxMessageDialog errdlg( this, msg, _( "Please Delete or Modify One" ) );
333  errdlg.ShowModal();
334 
335  return false;
336  }
337  }
338  }
339  }
340 
341  return true;
342 }
343 
344 
345 void PANEL_SYM_LIB_TABLE::pageChangedHandler( wxAuiNotebookEvent& event )
346 {
347  m_pageNdx = (unsigned) std::max( 0, m_auinotebook->GetSelection() );
349 }
350 
351 
352 void PANEL_SYM_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event )
353 {
354  wxFileDialog dlg( this, _( "Select Library" ), m_lastBrowseDir,
355  wxEmptyString, SchematicLibraryFileWildcard(),
356  wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
357 
358  auto result = dlg.ShowModal();
359 
360  if( result == wxID_CANCEL )
361  return;
362 
363  m_lastBrowseDir = dlg.GetDirectory();
364 
365  const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
366  bool addDuplicates = false;
367  bool applyToAll = false;
368  wxString warning = _( "Warning: Duplicate Nickname" );
369  wxString msg = _( "A library nicknamed \"%s\" already exists." );
370  wxArrayString files;
371  dlg.GetFilenames( files );
372 
373  for( const auto& file : files )
374  {
375  wxString filePath = dlg.GetDirectory() + wxFileName::GetPathSeparator() + file;
376  wxFileName fn( filePath );
377  wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), LIB_ID::ID_SCH );
378  bool doAdd = true;
379 
380  if( cur_model()->ContainsNickname( nickname ) )
381  {
382  if( !applyToAll )
383  {
384  int ret = OKOrCancelDialog( this, warning, wxString::Format( msg, nickname ),
385  _( "Skip" ), _( "Add Anyway" ), &applyToAll );
386  addDuplicates = (ret == wxID_CANCEL );
387  }
388 
389  doAdd = addDuplicates;
390  }
391 
392  if( doAdd && m_cur_grid->AppendRows( 1 ) )
393  {
394  int last_row = m_cur_grid->GetNumberRows() - 1;
395 
396  m_cur_grid->SetCellValue( last_row, COL_NICKNAME, nickname );
397 
398  // TODO the following code can detect only schematic types, not libs
399  // SCH_IO_MGR needs to provide file extension information for libraries too
400 
401  // auto detect the plugin type
402  /*for( auto pluginType : SCH_IO_MGR::SCH_FILE_T_vector )
403  {
404  if( SCH_IO_MGR::GetFileExtension( pluginType ).Lower() == fn.GetExt().Lower() )
405  {
406  m_cur_grid->SetCellValue( last_row, COL_TYPE,
407  SCH_IO_MGR::ShowType( pluginType ) );
408  break;
409  }
410  }*/
411  m_cur_grid->SetCellValue( last_row, COL_TYPE,
412  SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
413 
414  // try to use path normalized to an environmental variable or project path
415  wxString path = NormalizePath( filePath, &envVars, m_projectBasePath );
416 
417  // Do not use the project path in the global library table. This will almost
418  // assuredly be wrong for a different project.
419  if( path.IsEmpty() || (m_pageNdx == 0 && path.Contains( "${KIPRJMOD}" )) )
420  path = fn.GetFullPath();
421 
422  m_cur_grid->SetCellValue( last_row, COL_URI, path );
423  }
424  }
425 
426  if( !files.IsEmpty() )
427  {
428  m_cur_grid->MakeCellVisible( m_cur_grid->GetNumberRows() - 1, 0 );
429  m_cur_grid->SetGridCursor( m_cur_grid->GetNumberRows() - 1, 1 );
430  }
431 }
432 
433 
434 void PANEL_SYM_LIB_TABLE::appendRowHandler( wxCommandEvent& event )
435 {
437  return;
438 
439  if( m_cur_grid->AppendRows( 1 ) )
440  {
441  int row = m_cur_grid->GetNumberRows() - 1;
442  // Gives a default type (currently, only one type exists):
443  m_cur_grid->SetCellValue( row, COL_TYPE, SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
444 
445  // wx documentation is wrong, SetGridCursor does not make visible.
446  m_cur_grid->MakeCellVisible( row, 0 );
447  m_cur_grid->SetGridCursor( row, 1 );
448 
449  m_cur_grid->EnableCellEditControl( true );
450  m_cur_grid->ShowCellEditControl();
451  }
452 }
453 
454 
455 void PANEL_SYM_LIB_TABLE::deleteRowHandler( wxCommandEvent& event )
456 {
458  return;
459 
460  int curRow = m_cur_grid->GetGridCursorRow();
461  int curCol = m_cur_grid->GetGridCursorCol();
462 
463  // In a wxGrid, collect rows that have a selected cell, or are selected
464  // It is not so easy: it depends on the way the selection was made.
465  // Here, we collect rows selected by clicking on a row label, and rows that contain
466  // previously-selected cells.
467  // If no candidate, just delete the row with the grid cursor.
468  wxArrayInt selectedRows = m_cur_grid->GetSelectedRows();
469  wxGridCellCoordsArray cells = m_cur_grid->GetSelectedCells();
470  wxGridCellCoordsArray blockTopLeft = m_cur_grid->GetSelectionBlockTopLeft();
471  wxGridCellCoordsArray blockBotRight = m_cur_grid->GetSelectionBlockBottomRight();
472 
473  // Add all row having cell selected to list:
474  for( unsigned ii = 0; ii < cells.GetCount(); ii++ )
475  selectedRows.Add( cells[ii].GetRow() );
476 
477  // Handle block selection
478  if( !blockTopLeft.IsEmpty() && !blockBotRight.IsEmpty() )
479  {
480  for( int i = blockTopLeft[0].GetRow(); i <= blockBotRight[0].GetRow(); ++i )
481  selectedRows.Add( i );
482  }
483 
484  // Use the row having the grid cursor only if we have no candidate:
485  if( selectedRows.size() == 0 && m_cur_grid->GetGridCursorRow() >= 0 )
486  selectedRows.Add( m_cur_grid->GetGridCursorRow() );
487 
488  if( selectedRows.size() == 0 )
489  {
490  wxBell();
491  return;
492  }
493 
494  std::sort( selectedRows.begin(), selectedRows.end() );
495 
496  // Remove selected rows (note: a row can be stored more than once in list)
497  int last_row = -1;
498 
499  for( int ii = selectedRows.GetCount()-1; ii >= 0; ii-- )
500  {
501  int row = selectedRows[ii];
502 
503  if( row != last_row )
504  {
505  last_row = row;
506  m_cur_grid->DeleteRows( row, 1 );
507  }
508  }
509 
510  m_cur_grid->SetGridCursor( std::min( curRow, m_cur_grid->GetNumberRows() - 1 ), curCol );
511 }
512 
513 
514 void PANEL_SYM_LIB_TABLE::moveUpHandler( wxCommandEvent& event )
515 {
517  return;
518 
520  int curRow = m_cur_grid->GetGridCursorRow();
521 
522  // @todo: add multiple selection moves.
523  if( curRow >= 1 )
524  {
525  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
526  tbl->rows.release( tbl->rows.begin() + curRow );
527 
528  --curRow;
529  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
530 
531  if( tbl->GetView() )
532  {
533  // Update the wxGrid
534  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow, 0 );
535  tbl->GetView()->ProcessTableMessage( msg );
536  }
537 
538  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
539  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
540  }
541 }
542 
543 
544 void PANEL_SYM_LIB_TABLE::moveDownHandler( wxCommandEvent& event )
545 {
547  return;
548 
550  int curRow = m_cur_grid->GetGridCursorRow();
551 
552  // @todo: add multiple selection moves.
553  if( unsigned( curRow + 1 ) < tbl->rows.size() )
554  {
555  boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me =
556  tbl->rows.release( tbl->rows.begin() + curRow );
557 
558  ++curRow;
559  tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() );
560 
561  if( tbl->GetView() )
562  {
563  // Update the wxGrid
564  wxGridTableMessage msg( tbl, wxGRIDTABLE_NOTIFY_ROWS_INSERTED, curRow - 1, 0 );
565  tbl->GetView()->ProcessTableMessage( msg );
566  }
567 
568  m_cur_grid->MakeCellVisible( curRow, m_cur_grid->GetGridCursorCol() );
569  m_cur_grid->SetGridCursor( curRow, m_cur_grid->GetGridCursorCol() );
570  }
571 }
572 
573 
575 {
577  return false;
578 
579  if( !verifyTables() )
580  return false;
581 
582  if( *global_model() != *m_globalTable )
583  {
585 
586  m_globalTable->Clear();
587  m_globalTable->rows.transfer( m_globalTable->rows.end(), global_model()->rows.begin(),
588  global_model()->rows.end(), global_model()->rows );
590  }
591 
592  if( *project_model() != *m_projectTable )
593  {
595 
597  m_projectTable->rows.transfer( m_projectTable->rows.end(), project_model()->rows.begin(),
598  project_model()->rows.end(), project_model()->rows );
600  }
601 
602  return true;
603 }
604 
605 
607 {
608  wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
609  wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
610 
611  std::set< wxString > unique;
612 
613  // clear the table
614  m_path_subs_grid->DeleteRows( 0, m_path_subs_grid->GetNumberRows() );
615 
616  for( SYMBOL_LIB_TABLE_GRID* tbl : { global_model(), project_model() } )
617  {
618  for( int row = 0; row < tbl->GetNumberRows(); ++row )
619  {
620  wxString uri = tbl->GetValue( row, COL_URI );
621 
622  while( re.Matches( uri ) )
623  {
624  wxString envvar = re.GetMatch( uri, 2 );
625 
626  // if not ${...} form then must be $(...)
627  if( envvar.IsEmpty() )
628  envvar = re.GetMatch( uri, 4 );
629 
630  // ignore duplicates
631  unique.insert( envvar );
632 
633  // delete the last match and search again
634  uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
635  }
636  }
637  }
638 
639  // Make sure this special environment variable shows up even if it was
640  // not used yet. It is automatically set by KiCad to the directory holding
641  // the current project.
642  unique.insert( PROJECT_VAR_NAME );
644 
645  for( const wxString& evName : unique )
646  {
647  int row = m_path_subs_grid->GetNumberRows();
648  m_path_subs_grid->AppendRows( 1 );
649 
650  m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
651  m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
652 
653  wxString evValue;
654  wxGetEnv( evName, &evValue );
655  m_path_subs_grid->SetCellValue( row, 1, evValue );
656  m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
657  }
658 
659  // No combobox editors here, but it looks better if its consistent with the other
660  // grids in the dialog.
661  m_path_subs_grid->SetDefaultRowSize( m_path_subs_grid->GetDefaultRowSize() + 2 );
662 
663  adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
664 }
665 
666 
668 {
669  // Account for scroll bars
670  aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
671 
672  m_path_subs_grid->AutoSizeColumn( 0 );
673  m_path_subs_grid->SetColSize( 1, aWidth - m_path_subs_grid->GetColSize( 0 ) );
674 }
675 
676 
677 void PANEL_SYM_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
678 {
679  adjustPathSubsGridColumns( event.GetSize().GetX() );
680 
681  event.Skip();
682 }
683 
684 
686 {
687  return (SYMBOL_LIB_TABLE_GRID*) m_global_grid->GetTable();
688 }
689 
690 
692 {
693  return (SYMBOL_LIB_TABLE_GRID*) m_project_grid->GetTable();
694 }
695 
696 
698 {
699  return (SYMBOL_LIB_TABLE_GRID*) m_cur_grid->GetTable();
700 }
701 
702 
704 
705 
706 void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
707 {
708  SCH_EDIT_FRAME* schEditor = (SCH_EDIT_FRAME*) aKiway->Player( FRAME_SCH, false );
709  LIB_EDIT_FRAME* libEditor = (LIB_EDIT_FRAME*) aKiway->Player( FRAME_SCH_LIB_EDITOR, false );
710  LIB_VIEW_FRAME* libViewer = (LIB_VIEW_FRAME*) aKiway->Player( FRAME_SCH_VIEWER, false );
711 
713  wxString globalTablePath = SYMBOL_LIB_TABLE::GetGlobalTableFileName();
714  SYMBOL_LIB_TABLE* projectTable = aKiway->Prj().SchSymbolLibTable();
715  wxString projectPath = aKiway->Prj().GetProjectPath();
716  wxFileName projectTableFn( projectPath, SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
717  wxString msg;
718  wxString currentLib;
719 
720  if( libEditor )
721  {
722  currentLib = libEditor->GetCurLib();
723 
724  // This prevents an ugly crash on OSX (https://bugs.launchpad.net/kicad/+bug/1765286)
725  libEditor->FreezeSearchTree();
726 
727  if( libEditor->HasLibModifications() )
728  {
729  msg = _( "Modifications have been made to one or more symbol libraries.\n"
730  "Changes must be saved or discarded before the symbol library "
731  "table can be modified." );
732 
733  switch( UnsavedChangesDialog( aParent, msg ) )
734  {
735  case wxID_YES: libEditor->SaveAll(); break;
736  case wxID_NO: libEditor->RevertAll(); break;
737  default:
738  case wxID_CANCEL: libEditor->ThawSearchTree(); return;
739  }
740  }
741  }
742 
743  DIALOG_EDIT_LIBRARY_TABLES dlg( aParent, _( "Symbol Libraries" ) );
744 
745  dlg.InstallPanel( new PANEL_SYM_LIB_TABLE( &dlg, globalTable, globalTablePath,
746  projectTable, projectTableFn.GetFullPath(),
747  aKiway->Prj().GetProjectPath() ) );
748 
749  if( dlg.ShowModal() == wxID_CANCEL )
750  {
751  if( libEditor )
752  libEditor->ThawSearchTree();
753 
754  return;
755  }
756 
757  if( dlg.m_GlobalTableChanged )
758  {
759  try
760  {
761  globalTable->Save( globalTablePath );
762  }
763  catch( const IO_ERROR& ioe )
764  {
765  msg.Printf( _( "Error saving global library table:\n\n%s" ), ioe.What() );
766  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
767  }
768  }
769 
770  if( dlg.m_ProjectTableChanged )
771  {
772  try
773  {
774  projectTable->Save( projectTableFn.GetFullPath() );
775  }
776  catch( const IO_ERROR& ioe )
777  {
778  msg.Printf( _( "Error saving project-specific library table:\n\n%s" ), ioe.What() );
779  wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
780  }
781  }
782 
783  SCH_SCREENS schematic;
784 
785  schematic.UpdateSymbolLinks( true ); // Update all symbol library links for all sheets.
786 
787  if( schEditor )
788  schEditor->SyncView();
789 
790  if( libEditor )
791  {
792  // Check if the currently selected symbol library been removed or disabled.
793  if( !currentLib.empty()
794  && !projectTable->HasLibrary( currentLib, true ) )
795  {
796  libEditor->SetCurLib( wxEmptyString );
797  libEditor->emptyScreen();
798  }
799 
800  libEditor->SyncLibraries( true );
801  libEditor->ThawSearchTree();
802  }
803 
804  if( libViewer )
805  libViewer->ReCreateListLib();
806 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:236
const BITMAP_OPAQUE trash_xpm[1]
Definition: trash.cpp:46
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.
void moveUpHandler(wxCommandEvent &event) override
void pageChangedHandler(wxAuiNotebookEvent &event) override
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:98
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:76
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:37
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 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()
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Displays a warning dialog with aMessage and returns the user response.
Definition: confirm.cpp:214
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
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:79
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:136
void SaveAll()
Saves all modified parts and libraries.
SYMBOL_LIB_TABLE_GRID * cur_model() const
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:274
void push_back(LIB_TABLE_ROW *aRow) override
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
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
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:31
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
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:67
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:498
SYMBOL_GRID_TRICKS(DIALOG_EDIT_LIBRARY_TABLES *aParent, WX_GRID *aGrid)
void browseLibrariesHandler(wxCommandEvent &event) override