KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_fp_lib_table.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright (C) 2013-2021 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26
27#include <set>
28#include <wx/regex.h>
29#include <wx/dirdlg.h>
30#include <wx/filedlg.h>
31#include <wx/msgdlg.h>
32#include <functional>
33
34#include <project.h>
35#include <env_vars.h>
37#include <panel_fp_lib_table.h>
38#include <lib_id.h>
40#include <lib_table_lexer.h>
41#include <invoke_pcb_dialog.h>
42#include <bitmaps.h>
44#include <widgets/wx_grid.h>
49#include <confirm.h>
52#include <pgm_base.h>
53#include <pcb_edit_frame.h>
54#include <env_paths.h>
58#include <kiway.h>
59#include <kiway_express.h>
60#include <pcbnew_id.h> // For ID_PCBNEW_END_LIST
62#include <paths.h>
63#include <macros.h>
64#include <project_pcb.h>
65#include <common.h>
69
70
75{
76public:
77 FP_LIB_TABLE_GRID_DATA_MODEL( DIALOG_SHIM* aParent, WX_GRID* aGrid, const LIBRARY_TABLE& aTableToEdit,
78 FOOTPRINT_LIBRARY_ADAPTER* aAdapter, const wxArrayString& aPluginChoices,
79 wxString* aMRUDirectory, const wxString& aProjectPath,
80 const std::map<PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC>& aSupportedFiles ) :
81 LIB_TABLE_GRID_DATA_MODEL( aParent, aGrid, aTableToEdit, aAdapter, aPluginChoices, aMRUDirectory,
82 aProjectPath ),
83 m_supportedFpFiles( aSupportedFiles )
84 {
85 }
86
87 void SetValue( int aRow, int aCol, const wxString &aValue ) override
88 {
89 wxCHECK( aRow < (int) size(), /* void */ );
90
91 LIB_TABLE_GRID_DATA_MODEL::SetValue( aRow, aCol, aValue );
92
93 // If setting a filepath, attempt to auto-detect the format
94 if( aCol == COL_URI )
95 {
96 LIBRARY_TABLE_ROW& row = at( (size_t) aRow );
97 wxString uri = LIBRARY_MANAGER::ExpandURI( row.URI(), Pgm().GetSettingsManager().Prj() );
99
100 if( pluginType != PCB_IO_MGR::FILE_TYPE_NONE )
101 SetValue( aRow, COL_TYPE, PCB_IO_MGR::ShowType( pluginType ) );
102 }
103 }
104
105protected:
106 wxString getFileTypes( WX_GRID* aGrid, int aRow ) override
107 {
108 FP_LIB_TABLE_GRID_DATA_MODEL* table = static_cast<FP_LIB_TABLE_GRID_DATA_MODEL*>( aGrid->GetTable() );
109 LIBRARY_TABLE_ROW& tableRow = table->at( aRow );
111 const IO_BASE::IO_FILE_DESC& pluginDesc = m_supportedFpFiles.at( fileType );
112
113 if( pluginDesc.m_IsFile )
114 return pluginDesc.FileFilter();
115
116 return wxEmptyString;
117 }
118
119private:
120 const std::map<PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC>& m_supportedFpFiles;
121};
122
123
124
126{
127public:
128 FP_GRID_TRICKS( PANEL_FP_LIB_TABLE* aPanel, WX_GRID* aGrid, std::function<void( wxCommandEvent& )> aAddHandler ) :
129 LIB_TABLE_GRID_TRICKS( aGrid, aAddHandler ),
130 m_panel( aPanel )
131 {
133 }
134
135protected:
136 void optionsEditor( int aRow ) override
137 {
138 LIB_TABLE_GRID_DATA_MODEL* tbl = static_cast<LIB_TABLE_GRID_DATA_MODEL*>( m_grid->GetTable() );
139
140 if( tbl->GetNumberRows() > aRow )
141 {
142 LIBRARY_TABLE_ROW& row = tbl->At( static_cast<size_t>( aRow ) );
143 const wxString& options = row.Options();
144 wxString result = options;
145 std::map<std::string, UTF8> choices;
146
149 pi->GetLibraryOptions( &choices );
150
151 DIALOG_PLUGIN_OPTIONS dlg( wxGetTopLevelParent( m_grid ), row.Nickname(), choices, options, &result );
152 dlg.ShowModal();
153
154 if( options != result )
155 {
156 row.SetOptions( result );
157 m_grid->Refresh();
158 }
159 }
160 }
161
162 void openTable( const LIBRARY_TABLE_ROW& aRow ) override
163 {
164 wxFileName uri = LIBRARY_MANAGER::ExpandURI( aRow.URI(), Pgm().GetSettingsManager().Prj() );
165 auto nestedTable = std::make_unique<LIBRARY_TABLE>( uri, LIBRARY_TABLE_SCOPE::GLOBAL );
166
167 m_panel->AddTable( nestedTable.get(), aRow.Nickname(), true );
168 }
169
170 wxString getTablePreamble() override
171 {
172 return wxT( "(fp_lib_table" );
173 }
174
175protected:
177};
178
179
180void PANEL_FP_LIB_TABLE::AddTable( LIBRARY_TABLE* aTable, const wxString& aTitle, bool aClosable )
181{
183 wxString projectPath = m_project->GetProjectPath();
184
186
187 WX_GRID* grid = get_grid( (int) m_notebook->GetPageCount() - 1 );
188
189 if( aTable->Path().StartsWith( projectPath ) )
190 {
191 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *aTable, adapter, m_pluginChoices,
193 true /* take ownership */ );
194 }
195 else
196 {
197 wxString* lastGlobalLibDir = nullptr;
198
199 if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
200 {
201 if( cfg->m_LastFootprintLibDir.IsEmpty() )
202 cfg->m_LastFootprintLibDir = PATHS::GetDefaultUserFootprintsPath();
203
204 lastGlobalLibDir = &cfg->m_LastFootprintLibDir;
205 }
206
207 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *aTable, adapter, m_pluginChoices,
208 lastGlobalLibDir, wxEmptyString, m_supportedFpFiles ),
209 true /* take ownership */ );
210 }
211
212 // add Cut, Copy, and Paste to wxGrids
213 grid->PushEventHandler( new FP_GRID_TRICKS( this, grid,
214 [this]( wxCommandEvent& event )
215 {
216 appendRowHandler( event );
217 } ) );
218
219 auto autoSizeCol =
220 [&]( int aCol )
221 {
222 int prevWidth = grid->GetColSize( aCol );
223
224 grid->AutoSizeColumn( aCol, false );
225 grid->SetColSize( aCol, std::max( prevWidth, grid->GetColSize( aCol ) ) );
226 };
227
228 // all but COL_OPTIONS, which is edited with Option Editor anyways.
229 autoSizeCol( COL_NICKNAME );
230 autoSizeCol( COL_TYPE );
231 autoSizeCol( COL_URI );
232 autoSizeCol( COL_DESCR );
233
234 if( grid->GetNumberRows() > 0 )
235 {
236 grid->SetGridCursor( 0, COL_NICKNAME );
237 grid->SelectRow( 0 );
238 }
239}
240
241
243 PANEL_FP_LIB_TABLE_BASE( aParent ),
244 m_project( aProject ),
245 m_parent( aParent )
246{
247 m_lastProjectLibDir = m_project->GetProjectPath();
248
250
251 for( auto& [fileType, desc] : m_supportedFpFiles )
253
254 std::optional<LIBRARY_TABLE*> table = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
256 wxASSERT( table.has_value() );
257
258 AddTable( table.value(), _( "Global Libraries" ), false /* closable */ );
259
260 std::optional<LIBRARY_TABLE*> projectTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
262
263 if( projectTable.has_value() )
264 AddTable( projectTable.value(), _( "Project Specific Libraries" ), false /* closable */ );
265
266 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
267
268 // add Cut, Copy, and Paste to wxGrids
269 m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
270
272
273 m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
274 m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
275
276 // Configure button logos
282
283 // For aesthetic reasons, we must set the size of m_browseButton to match the other bitmaps
284 // manually (for instance m_append_button)
285 Layout(); // Needed at least on MSW to compute the actual buttons sizes, after initializing
286 // their bitmaps
287 wxSize buttonSize = m_append_button->GetSize();
288
289 m_browseButton->SetWidthPadding( 4 );
290 m_browseButton->SetMinSize( buttonSize );
291
292 // Populate the browse library options
293 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
294
295 for( auto& [type, desc] : m_supportedFpFiles )
296 {
297 wxString entryStr = PCB_IO_MGR::ShowType( type );
298 wxString midPart;
299
300 if( desc.m_IsFile && !desc.m_FileExtensions.empty() )
301 {
302 entryStr << wxString::Format( wxS( " (%s)" ), JoinExtensions( desc.m_FileExtensions ) );
303 }
304 else if( !desc.m_IsFile && !desc.m_ExtensionsInDir.empty() )
305 {
306 midPart = wxString::Format( _( "folder with %s files" ), JoinExtensions( desc.m_ExtensionsInDir ) );
307 entryStr << wxString::Format( wxS( " (%s)" ), midPart );
308 }
309
310 browseMenu->Append( type, entryStr );
311 browseMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this, type );
312 }
313
314 Layout();
315
316 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &PANEL_FP_LIB_TABLE::onNotebookPageCloseRequest, this );
317 // This is the button only press for the browse button instead of the menu
319}
320
321
323{
324 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
325
326 for( auto& [type, desc] : m_supportedFpFiles )
327 browseMenu->Unbind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this, type );
328
329 m_browseButton->Unbind( wxEVT_BUTTON, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this );
330
331 // Delete the GRID_TRICKS.
332 // (Notebook page GRID_TRICKS are deleted by LIB_TABLE_NOTEBOOK_PANEL.)
333 m_path_subs_grid->PopEventHandler( true );
334}
335
336
338{
339 return static_cast<FP_LIB_TABLE_GRID_DATA_MODEL*>( get_grid( aPage )->GetTable() );
340}
341
342
344{
345 return static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( aPage ) )->GetGrid();
346}
347
348
350{
351 // No visibility control for footprint libraries yet; this feature is primarily
352 // useful for database libraries and it's only implemented for schematic symbols
353 // at the moment.
354 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
355 {
356 WX_GRID* grid = get_grid( page );
357 grid->HideCol( COL_VISIBLE );
358 }
359
360 // for ALT+A handling, we want the initial focus to be on the first selected grid.
361 m_parent->SetInitialFocus( cur_grid() );
362
363 return true;
364}
365
366
368{
369 for( const auto& plugin : PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins() )
370 {
371 IO_RELEASER<PCB_IO> pi( plugin.m_createFunc() );
372
373 if( !pi )
374 continue;
375
376 if( const IO_BASE::IO_FILE_DESC& desc = pi->GetLibraryDesc() )
377 m_supportedFpFiles.emplace( plugin.m_type, desc );
378 }
379
381 IO_BASE::IO_FILE_DESC( _( "Table (nested library table)" ), {} ) );
382}
383
384
386{
387 wxString msg;
388
389 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
390 {
391 WX_GRID* grid = get_grid( page );
392
394 [&]( int aRow, int aCol )
395 {
396 // show the tabbed panel holding the grid we have flunked:
397 if( m_notebook->GetSelection() != page )
398 m_notebook->SetSelection( page );
399
400 grid->MakeCellVisible( aRow, 0 );
401 grid->SetGridCursor( aRow, aCol );
402 } ) )
403 {
404 return false;
405 }
406 }
407
408 return true;
409}
410
411
416
417
422
423
428
429
434
435
436// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE
437void PANEL_FP_LIB_TABLE::onMigrateLibraries( wxCommandEvent& event )
438{
439 if( !cur_grid()->CommitPendingChanges() )
440 return;
441
442 wxArrayInt selectedRows = cur_grid()->GetSelectedRows();
443
444 if( selectedRows.empty() && cur_grid()->GetGridCursorRow() >= 0 )
445 selectedRows.push_back( cur_grid()->GetGridCursorRow() );
446
447 wxArrayInt rowsToMigrate;
448 wxString kicadType = PCB_IO_MGR::ShowType( PCB_IO_MGR::KICAD_SEXP );
449 wxString msg;
450 DIALOG_HTML_REPORTER errorReporter( this );
451
452 for( int row : selectedRows )
453 {
454 if( cur_grid()->GetCellValue( row, COL_TYPE ) != kicadType )
455 rowsToMigrate.push_back( row );
456 }
457
458 if( rowsToMigrate.size() <= 0 )
459 {
460 wxMessageBox( _( "Select one or more rows containing libraries to save as current KiCad format." ) );
461 return;
462 }
463 else
464 {
465 if( rowsToMigrate.size() == 1 )
466 {
467 msg.Printf( _( "Save '%s' as current KiCad format and replace entry in table?" ),
468 cur_grid()->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) );
469 }
470 else
471 {
472 msg.Printf( _( "Save %d libraries as current KiCad format and replace entries in table?" ),
473 (int) rowsToMigrate.size() );
474 }
475
476 if( !IsOK( m_parent, msg ) )
477 return;
478 }
479
480 for( int row : rowsToMigrate )
481 {
482 wxString relPath = cur_grid()->GetCellValue( row, COL_URI );
483 wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project );
484 wxFileName legacyLib( resolvedPath );
485
486 if( !legacyLib.Exists() )
487 {
488 msg.Printf( _( "Library '%s' not found." ), relPath );
489 DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
490 continue;
491 }
492
493 wxFileName newLib( resolvedPath );
494 newLib.AppendDir( newLib.GetName() + "." + FILEEXT::KiCadFootprintLibPathExtension );
495 newLib.SetName( "" );
496 newLib.ClearExt();
497
498 if( newLib.DirExists() )
499 {
500 msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing footprints?" ),
501 newLib.GetFullPath() );
502
503 switch( wxMessageBox( msg, _( "Migrate Library" ), wxYES_NO|wxCANCEL|wxICON_QUESTION, m_parent ) )
504 {
505 case wxYES: break;
506 case wxNO: continue;
507 case wxCANCEL: return;
508 }
509 }
510
511 wxString options = cur_grid()->GetCellValue( row, COL_OPTIONS );
512 std::map<std::string, UTF8> props( LIBRARY_TABLE::ParseOptions( options.ToStdString() ) );
513
514 if( PCB_IO_MGR::ConvertLibrary( props, legacyLib.GetFullPath(), newLib.GetFullPath(),
515 errorReporter.m_Reporter ) )
516 {
517 relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(), m_project );
518
519 cur_grid()->SetCellValue( row, COL_URI, relPath );
520 cur_grid()->SetCellValue( row, COL_TYPE, kicadType );
521 }
522 else
523 {
524 DisplayErrorMessage( m_parent, wxString::Format( _( "Failed to save footprint library file '%s'." ),
525 newLib.GetFullPath() ) );
526 }
527 }
528
529 if( errorReporter.m_Reporter->HasMessage() )
530 {
531 errorReporter.m_Reporter->Flush(); // Build HTML messages
532 errorReporter.ShowModal();
533 }
534}
535
536
538{
539 if( !cur_grid()->CommitPendingChanges() )
540 return;
541
543
544 // We are bound both to the menu and button with this one handler
545 // So we must set the file type based on it
546 if( event.GetEventType() == wxEVT_BUTTON )
547 {
548 // Let's default to adding a kicad footprint file for just the footprint
550 }
551 else
552 {
553 fileType = static_cast<PCB_IO_MGR::PCB_FILE_T>( event.GetId() );
554 }
555
557 return;
558
559 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
560 const IO_BASE::IO_FILE_DESC& fileDesc = m_supportedFpFiles.at( fileType );
562
563 wxString title;
564 wxString dummy;
565 wxString* lastDir;
566
568 title = _( "Select Library Table" );
569 else
570 title = wxString::Format( _( "Select %s Library" ), PCB_IO_MGR::ShowType( fileType ) );
571
572 if( m_notebook->GetSelection() == 0 )
573 lastDir = cfg ? &cfg->m_LastFootprintLibDir : &dummy;
574 else
575 lastDir = &m_lastProjectLibDir;
576
577 wxArrayString files;
578
579 if( fileDesc.m_IsFile )
580 {
581 wxFileDialog dlg( m_parent, title, *lastDir, wxEmptyString, fileDesc.FileFilter(),
582 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
583
584 if( dlg.ShowModal() == wxID_CANCEL )
585 return;
586
587 dlg.GetPaths( files );
588 *lastDir = dlg.GetDirectory();
589 }
590 else
591 {
592 wxDirDialog dlg( m_parent, title, *lastDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE );
593
594 if( dlg.ShowModal() == wxID_CANCEL )
595 return;
596
597 dlg.GetPaths( files );
598
599 if( !files.IsEmpty() )
600 {
601 wxFileName first( files.front() );
602 *lastDir = first.GetPath();
603 }
604 }
605
606 // Drop the last directory if the path is a .pretty folder
608 cfg->m_LastFootprintLibDir = cfg->m_LastFootprintLibDir.BeforeLast( wxFileName::GetPathSeparator() );
609
610
611 bool addDuplicates = false;
612 bool applyToAll = false;
613 wxString warning = _( "Warning: Duplicate Nicknames" );
614 wxString msg = _( "An item nicknamed '%s' already exists." );
615 wxString detailedMsg = _( "One of the nicknames will need to be changed." );
616
617 for( const wxString& filePath : files )
618 {
619 wxFileName fn( filePath );
620 wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), true );
621 bool doAdd = true;
622
625 {
626 nickname = LIB_ID::FixIllegalChars( fn.GetFullName(), true ).wx_str();
627 }
628
629 if( cur_model()->ContainsNickname( nickname ) )
630 {
631 if( !applyToAll )
632 {
633 // The cancel button adds the library to the table anyway
634 addDuplicates = OKOrCancelDialog( m_parent, warning, wxString::Format( msg, nickname ), detailedMsg,
635 _( "Skip" ), _( "Add Anyway" ), &applyToAll ) == wxID_CANCEL;
636 }
637
638 doAdd = addDuplicates;
639 }
640
641 if( doAdd && cur_grid()->AppendRows( 1 ) )
642 {
643 int last_row = cur_grid()->GetNumberRows() - 1;
644
645 cur_grid()->SetCellValue( last_row, COL_NICKNAME, nickname );
646 cur_grid()->SetCellValue( last_row, COL_TYPE, PCB_IO_MGR::ShowType( fileType ) );
647
648 // try to use path normalized to an environmental variable or project path
649 wxString path = NormalizePath( filePath, &envVars, m_project->GetProjectPath() );
650
651 // Do not use the project path in the global library table. This will almost
652 // assuredly be wrong for a different project.
653 if( m_notebook->GetSelection() == 0 && path.Contains( wxT( "${KIPRJMOD}" ) ) )
654 path = fn.GetFullPath();
655
656 cur_grid()->SetCellValue( last_row, COL_URI, path );
657 }
658 }
659
660 if( !files.IsEmpty() )
661 {
662 cur_grid()->MakeCellVisible( cur_grid()->GetNumberRows() - 1, COL_ENABLED );
663 cur_grid()->SetGridCursor( cur_grid()->GetNumberRows() - 1, COL_NICKNAME );
664 }
665}
666
667
668void PANEL_FP_LIB_TABLE::onNotebookPageCloseRequest( wxAuiNotebookEvent& aEvent )
669{
670 wxAuiNotebook* notebook = (wxAuiNotebook*) aEvent.GetEventObject();
671 wxWindow* page = notebook->GetPage( aEvent.GetSelection() );
672
673 if( LIB_TABLE_NOTEBOOK_PANEL* panel = dynamic_cast<LIB_TABLE_NOTEBOOK_PANEL*>( page ) )
674 {
675 if( panel->GetClosable() )
676 {
677 if( !panel->GetCanClose() )
678 aEvent.Veto();
679 }
680 else
681 {
682 aEvent.Veto();
683 }
684 }
685}
686
687
689{
690 // Account for scroll bars
691 aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
692
693 m_path_subs_grid->AutoSizeColumn( 0 );
694 m_path_subs_grid->SetColSize( 0, std::max( 72, m_path_subs_grid->GetColSize( 0 ) ) );
695 m_path_subs_grid->SetColSize( 1, std::max( 120, aWidth - m_path_subs_grid->GetColSize( 0 ) ) );
696}
697
698
699void PANEL_FP_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
700{
701 adjustPathSubsGridColumns( event.GetSize().GetX() );
702
703 event.Skip();
704}
705
706
707void PANEL_FP_LIB_TABLE::onReset( wxCommandEvent& event )
708{
709 if( !cur_grid()->CommitPendingChanges() )
710 return;
711
712 WX_GRID* grid = get_grid( 0 );
713
714 // No need to prompt to preserve an empty table
715 if( grid->GetNumberRows() > 0 && !IsOK( this, wxString::Format( _( "This action will reset your global library "
716 "table on disk and cannot be undone." ) ) ) )
717 {
718 return;
719 }
720
721 wxString* lastGlobalLibDir = nullptr;
722
723 if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
724 {
725 if( cfg->m_LastFootprintLibDir.IsEmpty() )
726 cfg->m_LastFootprintLibDir = PATHS::GetDefaultUserFootprintsPath();
727
728 lastGlobalLibDir = &cfg->m_LastFootprintLibDir;
729 }
730
732
733 // Go ahead and reload here because this action takes place even if the dialog is canceled
735
736 if( KIFACE *face = m_parent->Kiway().KiFACE( KIWAY::FACE_PCB ) )
737 face->PreloadLibraries( &m_parent->Kiway() );
738
739 grid->Freeze();
740
741 wxGridTableBase* table = grid->GetTable();
742 grid->DestroyTable( table );
743
744 std::optional<LIBRARY_TABLE*> newTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
746 wxASSERT( newTable );
747
749
750 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *newTable.value(), adapter, m_pluginChoices,
751 lastGlobalLibDir, wxEmptyString, m_supportedFpFiles ),
752 true /* take ownership */ );
753
754 m_parent->m_GlobalTableChanged = true;
755
756 grid->Thaw();
757
758 if( grid->GetNumberRows() > 0 )
759 {
760 grid->SetGridCursor( 0, COL_NICKNAME );
761 grid->SelectRow( 0 );
762 }
763}
764
765
766void PANEL_FP_LIB_TABLE::onPageChange( wxAuiNotebookEvent& event )
767{
768 m_resetGlobal->Enable( m_notebook->GetSelection() == 0 );
769}
770
771
773{
774 if( !cur_grid()->CommitPendingChanges() )
775 return false;
776
777 if( !verifyTables() )
778 return false;
779
780 std::optional<LIBRARY_TABLE*> optTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
782 wxCHECK( optTable, false );
783 LIBRARY_TABLE* globalTable = *optTable;
784
785 if( get_model( 0 )->Table() != *globalTable )
786 {
787 m_parent->m_GlobalTableChanged = true;
788 *globalTable = get_model( 0 )->Table();
789
790 globalTable->Save().map_error(
791 []( const LIBRARY_ERROR& aError )
792 {
793 wxMessageBox( _( "Error saving global library table:\n\n" ) + aError.message,
794 _( "File Save Error" ), wxOK | wxICON_ERROR );
795 } );
796 }
797
799
800 if( optTable.has_value() && get_model( 1 )->Table().Path() == optTable.value()->Path() )
801 {
802 LIBRARY_TABLE* projectTable = *optTable;
803
804 if( get_model( 1 )->Table() != *projectTable )
805 {
806 m_parent->m_ProjectTableChanged = true;
807 *projectTable = get_model( 1 )->Table();
808
809 projectTable->Save().map_error(
810 []( const LIBRARY_ERROR& aError )
811 {
812 wxMessageBox( _( "Error saving project-specific library table:\n\n" ) + aError.message,
813 _( "File Save Error" ), wxOK | wxICON_ERROR );
814 } );
815 }
816 }
817
818 for( int ii = 0; ii < (int) m_notebook->GetPageCount(); ++ii )
819 {
820 LIB_TABLE_NOTEBOOK_PANEL* panel = static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( ii ) );
821
822 if( panel->GetClosable() && panel->TableModified() )
823 {
824 panel->SaveTable();
825 m_parent->m_GlobalTableChanged = true;
826 m_parent->m_ProjectTableChanged = true;
827 }
828 }
829
830 return true;
831}
832
833
837{
838 wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
839 wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
840
841 std::set< wxString > unique;
842
843 // clear the table
844 m_path_subs_grid->ClearRows();
845
846 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
847 {
848 LIB_TABLE_GRID_DATA_MODEL* model = get_model( page );
849
850 for( int row = 0; row < model->GetNumberRows(); ++row )
851 {
852 wxString uri = model->GetValue( row, COL_URI );
853
854 while( re.Matches( uri ) )
855 {
856 wxString envvar = re.GetMatch( uri, 2 );
857
858 // if not ${...} form then must be $(...)
859 if( envvar.IsEmpty() )
860 envvar = re.GetMatch( uri, 4 );
861
862 // ignore duplicates
863 unique.insert( envvar );
864
865 // delete the last match and search again
866 uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
867 }
868 }
869 }
870
871 // Make sure this special environment variable shows up even if it was not used yet. It is
872 // automatically set by KiCad to the directory holding the current project.
873 unique.insert( PROJECT_VAR_NAME );
874 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "FOOTPRINT_DIR" ) ) );
875
876 // This special environment variable is used to locate 3d shapes
877 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
878
879 for( const wxString& evName : unique )
880 {
881 int row = m_path_subs_grid->GetNumberRows();
882 m_path_subs_grid->AppendRows( 1 );
883
884 m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
885 m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
886
887 wxString evValue;
888 wxGetEnv( evName, &evValue );
889 m_path_subs_grid->SetCellValue( row, 1, evValue );
890 m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
891 }
892
893 adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
894}
895
896
897//-----</event handlers>---------------------------------
898
899
900
901void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller )
902{
903 DIALOG_EDIT_LIBRARY_TABLES dlg( aCaller, _( "Footprint Libraries" ) );
904 dlg.SetKiway( &dlg, aKiway );
905
906 dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, &aKiway->Prj() ) );
907
908 if( dlg.ShowModal() == wxID_CANCEL )
909 return;
910
911 if( dlg.m_GlobalTableChanged )
913
914 if( dlg.m_ProjectTableChanged )
915 {
916 // Trigger a reload of the table and cancel an in-progress background load
918 }
919
920 // Trigger a reload in case any libraries have been added or removed
921 if( KIFACE *face = aKiway->KiFACE( KIWAY::FACE_PCB ) )
922 face->PreloadLibraries( aKiway );
923
924 std::string payload = "";
927 aKiway->ExpressMail( FRAME_CVPCB, MAIL_RELOAD_LIB, payload );
928}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
Class DIALOG_HTML_REPORTER.
WX_HTML_REPORT_BOX * m_Reporter
An options editor in the form of a two column name/value spreadsheet like (table) UI.
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
int ShowModal() override
An interface to the global shared library manager that is schematic-specific and linked to one projec...
void openTable(const LIBRARY_TABLE_ROW &aRow) override
FP_GRID_TRICKS(PANEL_FP_LIB_TABLE *aPanel, WX_GRID *aGrid, std::function< void(wxCommandEvent &)> aAddHandler)
wxString getTablePreamble() override
void optionsEditor(int aRow) override
PANEL_FP_LIB_TABLE * m_panel
This class builds a wxGridTableBase by wrapping an #FP_LIB_TABLE object.
FP_LIB_TABLE_GRID_DATA_MODEL(DIALOG_SHIM *aParent, WX_GRID *aGrid, const LIBRARY_TABLE &aTableToEdit, FOOTPRINT_LIBRARY_ADAPTER *aAdapter, const wxArrayString &aPluginChoices, wxString *aMRUDirectory, const wxString &aProjectPath, const std::map< PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC > &aSupportedFiles)
wxString getFileTypes(WX_GRID *aGrid, int aRow) override
void SetValue(int aRow, int aCol, const wxString &aValue) override
const std::map< PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC > & m_supportedFpFiles
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:61
WX_GRID * m_grid
I don't own the grid, but he owns me.
void SetTooltipEnable(int aCol, bool aEnable=true)
Enable the tooltip for a column.
Definition grid_tricks.h:75
void SetKiway(wxWindow *aDest, KIWAY *aKiway)
It is only used for debugging, since "this" is not a wxWindow*.
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:292
virtual void ExpressMail(FRAME_T aDestination, MAIL_T aCommand, std::string &aPayload, wxWindow *aSource=nullptr, bool aFromOtherThread=false)
Send aPayload to aDestination from aSource.
Definition kiway.cpp:507
virtual KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Return the KIFACE* given a FACE_T.
Definition kiway.cpp:206
@ FACE_PCB
pcbnew DSO
Definition kiway.h:300
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:200
static wxString ExpandURI(const wxString &aShortURI, const PROJECT &aProject)
std::optional< LIBRARY_TABLE * > Table(LIBRARY_TABLE_TYPE aType, LIBRARY_TABLE_SCOPE aScope)
Retrieves a given table; creating a new empty project table if a valid project is loaded and the give...
static bool CreateGlobalTable(LIBRARY_TABLE_TYPE aType, bool aPopulateDefaultLibraries)
void LoadGlobalTables(std::initializer_list< LIBRARY_TABLE_TYPE > aTablesToLoad={})
(Re)loads the global library tables in the given list, or all tables if no list is given
void ProjectChanged()
Notify all adapters that the project has changed.
void SetOptions(const wxString &aOptions)
const wxString & Type() const
const wxString & URI() const
const wxString & Nickname() const
const wxString & Options() const
LIBRARY_RESULT< void > Save()
static std::map< std::string, UTF8 > ParseOptions(const std::string &aOptionsList)
const wxString & Path() const
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:192
This abstract base class mixes any object derived from #LIB_TABLE into wxGridTableBase so the result ...
virtual LIBRARY_TABLE_ROW & at(size_t aIndex)
wxString GetValue(int aRow, int aCol) override
LIBRARY_TABLE_ROW & At(size_t aIndex)
void SetValue(int aRow, int aCol, const wxString &aValue) override
LIB_TABLE_GRID_DATA_MODEL(DIALOG_SHIM *aParent, WX_GRID *aGrid, const LIBRARY_TABLE &aTableToEdit, LIBRARY_MANAGER_ADAPTER *aAdapter, const wxArrayString &aPluginChoices, wxString *aMRUDirectory, const wxString &aProjectPath)
static void MoveUpHandler(WX_GRID *aGrid)
LIB_TABLE_GRID_TRICKS(WX_GRID *aGrid)
static void DeleteRowHandler(WX_GRID *aGrid)
static bool VerifyTable(WX_GRID *aGrid, std::function< void(int aRow, int aCol)> aErrorHandler)
static void AppendRowHandler(WX_GRID *aGrid)
static void MoveDownHandler(WX_GRID *aGrid)
static void AddTable(wxAuiNotebook *aNotebook, const wxString &aTitle, bool aClosable)
STD_BITMAP_BUTTON * m_move_up_button
STD_BITMAP_BUTTON * m_append_button
STD_BITMAP_BUTTON * m_move_down_button
STD_BITMAP_BUTTON * m_delete_button
PANEL_FP_LIB_TABLE_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxTAB_TRAVERSAL, const wxString &name=wxEmptyString)
Dialog to show and edit symbol library tables.
bool verifyTables()
Trim important fields, removes blank row entries, and checks for duplicates.
std::map< PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC > m_supportedFpFiles
void onSizeGrid(wxSizeEvent &event) override
void moveUpHandler(wxCommandEvent &event) override
bool TransferDataToWindow() override
WX_GRID * cur_grid() const
void adjustPathSubsGridColumns(int aWidth)
void moveDownHandler(wxCommandEvent &event) override
wxArrayString m_pluginChoices
PANEL_FP_LIB_TABLE(DIALOG_EDIT_LIBRARY_TABLES *aParent, PROJECT *aProject)
void populateEnvironReadOnlyTable()
Populate the readonly environment variable table with names and values by examining all the full_uri ...
void deleteRowHandler(wxCommandEvent &event) override
void onNotebookPageCloseRequest(wxAuiNotebookEvent &aEvent)
FP_LIB_TABLE_GRID_DATA_MODEL * get_model(int aPage) const
FP_LIB_TABLE_GRID_DATA_MODEL * cur_model() const
void onReset(wxCommandEvent &event) override
void onMigrateLibraries(wxCommandEvent &event) override
void browseLibrariesHandler(wxCommandEvent &event)
void onPageChange(wxAuiNotebookEvent &event) override
bool TransferDataFromWindow() override
void AddTable(LIBRARY_TABLE *table, const wxString &aTitle, bool aClosable)
WX_GRID * get_grid(int aPage) const
DIALOG_EDIT_LIBRARY_TABLES * m_parent
void appendRowHandler(wxCommandEvent &event) override
static wxString GetDefaultUserFootprintsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:92
wxString m_LastFootprintLibDir
static PLUGIN_REGISTRY * Instance()
Definition pcb_io_mgr.h:95
static bool ConvertLibrary(const std::map< std::string, UTF8 > &aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilePath, REPORTER *aReporter)
Convert a schematic symbol library to the latest KiCad format.
static PCB_FILE_T EnumFromStr(const wxString &aFileType)
Return the PCB_FILE_T from the corresponding plugin type name: "kicad", "legacy", etc.
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:56
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:58
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a #PLUGIN which the caller can use to import, export, save, or load design documents.
static PCB_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a footprint library's libPath.
static const wxString ShowType(PCB_FILE_T aFileType)
Return a brief name for a plugin given aFileType enum.
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:783
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:131
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:65
wxString wx_str() const
Definition utf8.cpp:45
void Flush()
Build the HTML messages page.
bool HasMessage() const override
Returns true if any messages were reported.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:365
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:458
The common library.
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:150
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:259
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
This file is part of the common library.
#define _(s)
Declaration of the eda_3d_viewer class.
wxString NormalizePath(const wxFileName &aFilePath, const ENV_VAR_MAP *aEnvVars, const wxString &aProjectPath)
Normalize a file path to an environmental variable, if possible.
Definition env_paths.cpp:73
Helper functions to substitute paths with environmental variables.
Functions related to environment variables, including help functions.
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:45
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:43
@ FRAME_CVPCB
Definition frame_type.h:52
static const std::string KiCadFootprintLibPathExtension
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:629
This file contains miscellaneous commonly used macros and functions.
@ MAIL_RELOAD_LIB
Definition mail_type.h:57
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
void InvokePcbLibTableEditor(KIWAY *aKiway, wxWindow *aCaller)
Function InvokePcbLibTableEditor shows the modal DIALOG_FP_LIB_TABLE for purposes of editing the glob...
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:946
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:41
T * GetAppSettings(const char *aFilename)
std::vector< FAB_LAYER_COLOR > dummy
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
Container that describes file type info.
Definition io_base.h:45
bool m_IsFile
Whether the library is a folder or a file.
Definition io_base.h:53
wxString FileFilter() const
Definition io_base.cpp:40
Implement a participant in the KIWAY alchemy.
Definition kiway.h:155
wxString message
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.