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, see <https://www.gnu.org/licenses/>.
20 */
21
22
23#include <set>
24#include <wx/regex.h>
25#include <wx/dirdlg.h>
26#include <wx/filedlg.h>
27#include <wx/msgdlg.h>
28#include <functional>
29
30#include <project.h>
31#include <env_vars.h>
33#include <panel_fp_lib_table.h>
34#include <lib_id.h>
36#include <lib_table_lexer.h>
37#include <invoke_pcb_dialog.h>
38#include <bitmaps.h>
40#include <widgets/wx_grid.h>
45#include <confirm.h>
47#include <kiplatform/ui.h>
49#include <pgm_base.h>
50#include <pcb_edit_frame.h>
51#include <env_paths.h>
55#include <kiway.h>
56#include <kiway_mail.h>
57#include <pcbnew_id.h> // For ID_PCBNEW_END_LIST
59#include <paths.h>
60#include <macros.h>
61#include <project_pcb.h>
62#include <common.h>
66
67
72{
73public:
74 FP_LIB_TABLE_GRID_DATA_MODEL( DIALOG_SHIM* aParent, WX_GRID* aGrid, const LIBRARY_TABLE& aTableToEdit,
75 FOOTPRINT_LIBRARY_ADAPTER* aAdapter, const wxArrayString& aPluginChoices,
76 wxString* aMRUDirectory, const wxString& aProjectPath,
77 const std::map<PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC>& aSupportedFiles ) :
78 LIB_TABLE_GRID_DATA_MODEL( aParent, aGrid, aTableToEdit, aAdapter, aPluginChoices, aMRUDirectory,
79 aProjectPath ),
80 m_supportedFpFiles( aSupportedFiles )
81 {
82 }
83
84 void SetValue( int aRow, int aCol, const wxString &aValue ) override
85 {
86 wxCHECK( aRow < (int) size(), /* void */ );
87
88 LIB_TABLE_GRID_DATA_MODEL::SetValue( aRow, aCol, aValue );
89
90 // If setting a filepath, attempt to auto-detect the format
91 if( aCol == COL_URI )
92 {
93 LIBRARY_TABLE_ROW& row = at( (size_t) aRow );
94 wxString uri = LIBRARY_MANAGER::ExpandURI( row.URI(), Pgm().GetSettingsManager().Prj() );
96
97 if( pluginType != PCB_IO_MGR::FILE_TYPE_NONE )
98 SetValue( aRow, COL_TYPE, PCB_IO_MGR::ShowType( pluginType ) );
99 }
100 }
101
102protected:
103 wxString getFileTypes( WX_GRID* aGrid, int aRow ) override
104 {
105 FP_LIB_TABLE_GRID_DATA_MODEL* table = static_cast<FP_LIB_TABLE_GRID_DATA_MODEL*>( aGrid->GetTable() );
106 LIBRARY_TABLE_ROW& tableRow = table->at( aRow );
107
108 if( tableRow.Type() == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
109 {
110 wxString filter = _( "Footprint Library Tables" );
111#ifndef __WXOSX__
112 filter << wxString::Format( _( " (%s)|%s" ), FILEEXT::FootprintLibraryTableFileName,
114#else
115 filter << wxString::Format( _( " (%s)|%s" ), wxFileSelectorDefaultWildcardStr,
116 wxFileSelectorDefaultWildcardStr );
117#endif
118 return filter;
119 }
120
122
124 return wxEmptyString;
125
126 const IO_BASE::IO_FILE_DESC& pluginDesc = m_supportedFpFiles.at( fileType );
127
128 if( pluginDesc.m_IsFile )
129 return pluginDesc.FileFilter();
130
131 return wxEmptyString;
132 }
133
134private:
135 const std::map<PCB_IO_MGR::PCB_FILE_T, IO_BASE::IO_FILE_DESC>& m_supportedFpFiles;
136};
137
138
139
141{
142public:
143 FP_GRID_TRICKS( PANEL_FP_LIB_TABLE* aPanel, WX_GRID* aGrid, std::function<void( wxCommandEvent& )> aAddHandler ) :
144 LIB_TABLE_GRID_TRICKS( aGrid, aAddHandler ),
145 m_panel( aPanel )
146 {
148 }
149
151 {
152 return false;
153 }
154
155protected:
156 void optionsEditor( int aRow ) override
157 {
158 LIB_TABLE_GRID_DATA_MODEL* tbl = static_cast<LIB_TABLE_GRID_DATA_MODEL*>( m_grid->GetTable() );
159
160 if( aRow < tbl->GetNumberRows() )
161 {
162 LIBRARY_TABLE_ROW& row = tbl->At( static_cast<size_t>( aRow ) );
163 const wxString& options = row.Options();
164 wxString result = options;
165 std::map<std::string, UTF8> choices;
166
169
170 if( pi )
171 pi->GetLibraryOptions( &choices );
172
173 DIALOG_PLUGIN_OPTIONS dlg( wxGetTopLevelParent( m_grid ), row.Nickname(), choices, options, &result );
174 dlg.ShowModal();
175
176 if( options != result )
177 {
178 row.SetOptions( result );
179 m_grid->Refresh();
180 }
181 }
182 }
183
184 void openTable( const LIBRARY_TABLE_ROW& aRow ) override
185 {
186 wxFileName fn( LIBRARY_MANAGER::ExpandURI( aRow.URI(), Pgm().GetSettingsManager().Prj() ) );
187 std::shared_ptr<LIBRARY_TABLE> child = std::make_shared<LIBRARY_TABLE>( fn, LIBRARY_TABLE_SCOPE::GLOBAL,
189
190 if( !child->IsOk() )
191 {
192 wxMessageBox( _( "Unable to load library table." ) );
193 }
194 else
195 {
197
198 m_panel->OpenTable( child, aRow.Nickname() );
199 }
200 }
201
202 wxString getTablePreamble() override
203 {
204 return wxT( "(fp_lib_table" );
205 }
206
211
212protected:
214};
215
216
217void PANEL_FP_LIB_TABLE::OpenTable( const std::shared_ptr<LIBRARY_TABLE>& aTable, const wxString& aTitle )
218{
219 wxString tabTitle = aTitle;
220
221 if( aTable->IsReadOnly() )
222 tabTitle += wxS( " " ) + _( "(read-only)" );
223
224 for( int ii = 2; ii < (int) m_notebook->GetPageCount(); ++ii )
225 {
226 wxString candidate = m_notebook->GetPageText( ii );
227
228 if( candidate.EndsWith( " *" ) )
229 candidate = candidate.Left( candidate.Length() - 2 );
230
231 if( candidate == tabTitle )
232 {
233 // Something is pretty fishy with wxAuiNotebook::ChangeSelection(); on Mac at least it
234 // results in a re-entrant call where the second call is one page behind.
235 for( int attempts = 0; attempts < 3; ++attempts )
236 m_notebook->ChangeSelection( ii );
237
238 return;
239 }
240 }
241
242 m_nestedTables.push_back( aTable );
243 AddTable( aTable.get(), tabTitle, true );
244
245 // Something is pretty fishy with wxAuiNotebook::ChangeSelection(); on Mac at least it
246 // results in a re-entrant call where the second call is one page behind.
247 for( int attempts = 0; attempts < 3; ++attempts )
248 m_notebook->ChangeSelection( m_notebook->GetPageCount() - 1 );
249}
250
251
252void PANEL_FP_LIB_TABLE::AddTable( LIBRARY_TABLE* aTable, const wxString& aTitle, bool aClosable )
253{
255 wxString projectPath = m_project->GetProjectPath();
256
258
259 WX_GRID* grid = get_grid( (int) m_notebook->GetPageCount() - 1 );
260
261 if( aTable->Path().StartsWith( projectPath ) )
262 {
263 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *aTable, adapter, m_pluginChoices,
265 true /* take ownership */ );
266 }
267 else
268 {
269 wxString* lastGlobalLibDir = nullptr;
270
271 if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
272 {
273 if( cfg->m_LastFootprintLibDir.IsEmpty() )
274 cfg->m_LastFootprintLibDir = PATHS::GetDefaultUserFootprintsPath();
275
276 lastGlobalLibDir = &cfg->m_LastFootprintLibDir;
277 }
278
279 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *aTable, adapter, m_pluginChoices,
280 lastGlobalLibDir, wxEmptyString, m_supportedFpFiles ),
281 true /* take ownership */ );
282 }
283
284 static_cast<LIB_TABLE_GRID_DATA_MODEL*>( grid->GetTable() )->RecheckRows();
285
286 LIB_TABLE_NOTEBOOK_PANEL* notebookPanel =
287 static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( m_notebook->GetPageCount() - 1 ) );
288
289 static_cast<LIB_TABLE_GRID_DATA_MODEL*>( grid->GetTable() )->SetChangeCallback(
290 [notebookPanel]()
291 {
292 notebookPanel->MarkDirty();
293 } );
294
295 // add Cut, Copy, and Paste to wxGrids
296 grid->PushEventHandler( new FP_GRID_TRICKS( this, grid,
297 [this]( wxCommandEvent& event )
298 {
299 appendRowHandler( event );
300 } ) );
301
302 auto autoSizeCol =
303 [&]( int aCol )
304 {
305 int prevWidth = grid->GetColSize( aCol );
306
307 grid->AutoSizeColumn( aCol, false );
308 grid->SetColSize( aCol, std::max( prevWidth, grid->GetColSize( aCol ) ) );
309 };
310
311 // all but COL_OPTIONS, which is edited with Option Editor anyways.
312 autoSizeCol( COL_NICKNAME );
313 autoSizeCol( COL_TYPE );
314 autoSizeCol( COL_URI );
315 autoSizeCol( COL_DESCR );
316
317 if( grid->GetNumberRows() > 0 )
318 {
319 grid->SetGridCursor( 0, COL_NICKNAME );
320 grid->SelectRow( 0 );
321 }
322}
323
324
326 PANEL_FP_LIB_TABLE_BASE( aParent ),
327 m_project( aProject ),
328 m_parent( aParent ),
330{
331 m_notebook->SetArtProvider( new WX_AUI_TAB_ART() );
332
333 m_lastProjectLibDir = m_project->GetProjectPath();
334
336
337 for( auto& [fileType, desc] : m_supportedFpFiles )
339
340 std::optional<LIBRARY_TABLE*> table = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
342 wxASSERT( table.has_value() );
343
344 AddTable( table.value(), _( "Global Libraries" ), false /* closable */ );
345
346 std::optional<LIBRARY_TABLE*> projectTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
348
349 if( projectTable.has_value() )
350 AddTable( projectTable.value(), _( "Project Specific Libraries" ), false /* closable */ );
351
352 // add Cut, Copy, and Paste to wxGrids
353 m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) );
354
356
357 m_path_subs_grid->SetColLabelValue( 0, _( "Name" ) );
358 m_path_subs_grid->SetColLabelValue( 1, _( "Value" ) );
359
360 // Configure button logos
366
367 // For aesthetic reasons, we must set the size of m_browseButton to match the other bitmaps
368 // manually (for instance m_append_button)
369 Layout(); // Needed at least on MSW to compute the actual buttons sizes, after initializing
370 // their bitmaps
371 wxSize buttonSize = m_append_button->GetSize();
372
373 m_browseButton->SetWidthPadding( 4 );
374 m_browseButton->SetMinSize( buttonSize );
375
376 // Populate the browse library options
377 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
378
379 for( auto& [type, desc] : m_supportedFpFiles )
380 {
381 wxString entryStr = PCB_IO_MGR::ShowType( type );
382 wxString midPart;
383
384 if( desc.m_IsFile && !desc.m_FileExtensions.empty() )
385 {
386 entryStr << wxString::Format( wxS( " (%s)" ), JoinExtensions( desc.m_FileExtensions ) );
387 }
388 else if( !desc.m_IsFile && !desc.m_ExtensionsInDir.empty() )
389 {
390 midPart = wxString::Format( _( "folder with %s files" ), JoinExtensions( desc.m_ExtensionsInDir ) );
391 entryStr << wxString::Format( wxS( " (%s)" ), midPart );
392 }
393
394 browseMenu->Append( type, entryStr );
395 browseMenu->Bind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this, type );
396 }
397
398 Layout();
399
400 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CLOSE, &PANEL_FP_LIB_TABLE::onNotebookPageCloseRequest, this );
401 m_notebook->Bind( wxEVT_AUINOTEBOOK_PAGE_CHANGING, &PANEL_FP_LIB_TABLE::onNotebookPageChangeRequest, this );
402 // This is the button only press for the browse button instead of the menu
404}
405
406
408{
409 wxMenu* browseMenu = m_browseButton->GetSplitButtonMenu();
410
411 for( auto& [type, desc] : m_supportedFpFiles )
412 browseMenu->Unbind( wxEVT_COMMAND_MENU_SELECTED, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this, type );
413
414 m_browseButton->Unbind( wxEVT_BUTTON, &PANEL_FP_LIB_TABLE::browseLibrariesHandler, this );
415
416 // Delete the GRID_TRICKS.
417 // (Notebook page GRID_TRICKS are deleted by LIB_TABLE_NOTEBOOK_PANEL.)
418 m_path_subs_grid->PopEventHandler( true );
419}
420
421
423{
424 return static_cast<FP_LIB_TABLE_GRID_DATA_MODEL*>( get_grid( aPage )->GetTable() );
425}
426
427
429{
430 return static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( aPage ) )->GetGrid();
431}
432
433
435{
436 // No visibility control for footprint libraries yet; this feature is primarily
437 // useful for database libraries and it's only implemented for schematic symbols
438 // at the moment.
439 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
440 {
441 WX_GRID* grid = get_grid( page );
442 grid->HideCol( COL_VISIBLE );
443 }
444
445 // for ALT+A handling, we want the initial focus to be on the first selected grid.
446 m_parent->SetInitialFocus( cur_grid() );
447
448 return true;
449}
450
451
453{
454 for( const auto& plugin : PCB_IO_MGR::PLUGIN_REGISTRY::Instance()->AllPlugins() )
455 {
456 IO_RELEASER<PCB_IO> pi( plugin.m_createFunc() );
457
458 if( !pi )
459 continue;
460
461 if( const IO_BASE::IO_FILE_DESC& desc = pi->GetLibraryDesc() )
462 m_supportedFpFiles.emplace( plugin.m_type, desc );
463 }
464
466 IO_BASE::IO_FILE_DESC( _( "Table (nested library table)" ), {} ) );
467}
468
469
471{
472 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
473 {
474 WX_GRID* grid = get_grid( page );
475
477 [&]( int aRow, int aCol )
478 {
479 // show the tabbed panel holding the grid we have flunked:
480 if( m_notebook->GetSelection() != page )
481 m_notebook->SetSelection( page );
482
483 grid->MakeCellVisible( aRow, 0 );
484 grid->SetGridCursor( aRow, aCol );
485 } ) )
486 {
487 return false;
488 }
489 }
490
491 return true;
492}
493
494
499
500
505
506
511
512
517
518
519// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE
520void PANEL_FP_LIB_TABLE::onMigrateLibraries( wxCommandEvent& event )
521{
522 if( !cur_grid()->CommitPendingChanges() )
523 return;
524
525 wxArrayInt selectedRows = cur_grid()->GetSelectedRows();
526
527 if( selectedRows.empty() && cur_grid()->GetGridCursorRow() >= 0 )
528 selectedRows.push_back( cur_grid()->GetGridCursorRow() );
529
530 wxArrayInt rowsToMigrate;
531 wxString kicadType = PCB_IO_MGR::ShowType( PCB_IO_MGR::KICAD_SEXP );
532 wxString nestedTableType = LIBRARY_TABLE_ROW::TABLE_TYPE_NAME;
533 wxString msg;
534 DIALOG_HTML_REPORTER errorReporter( this );
535
536 for( int row : selectedRows )
537 {
538 const wxString& type = cur_grid()->GetCellValue( row, COL_TYPE );
539
540 // Nested library tables are not footprint libraries and cannot be migrated.
541 if( type != kicadType && type != nestedTableType )
542 rowsToMigrate.push_back( row );
543 }
544
545 if( rowsToMigrate.size() <= 0 )
546 {
547 wxMessageBox( _( "Select one or more rows containing libraries to save as current KiCad format." ) );
548 return;
549 }
550 else
551 {
552 if( rowsToMigrate.size() == 1 )
553 {
554 msg.Printf( _( "Save '%s' as current KiCad format and replace entry in table?" ),
555 cur_grid()->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) );
556 }
557 else
558 {
559 msg.Printf( _( "Save %d libraries as current KiCad format and replace entries in table?" ),
560 (int) rowsToMigrate.size() );
561 }
562
563 if( !IsOK( m_parent, msg ) )
564 return;
565 }
566
567 for( int row : rowsToMigrate )
568 {
569 wxString relPath = cur_grid()->GetCellValue( row, COL_URI );
570 wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project );
571 wxFileName legacyLib( resolvedPath );
572
573 if( !legacyLib.Exists() )
574 {
575 msg.Printf( _( "Library '%s' not found." ), relPath );
576 DisplayErrorMessage( wxGetTopLevelParent( this ), msg );
577 continue;
578 }
579
580 wxFileName newLib( resolvedPath );
581 newLib.AppendDir( newLib.GetName() + "." + FILEEXT::KiCadFootprintLibPathExtension );
582 newLib.SetName( "" );
583 newLib.ClearExt();
584
585 if( newLib.DirExists() )
586 {
587 msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing footprints?" ),
588 newLib.GetFullPath() );
589
590 switch( wxMessageBox( msg, _( "Migrate Library" ), wxYES_NO|wxCANCEL|wxICON_QUESTION, m_parent ) )
591 {
592 case wxYES: break;
593 case wxNO: continue;
594 case wxCANCEL: return;
595 }
596 }
597
598 wxString options = cur_grid()->GetCellValue( row, COL_OPTIONS );
599 std::map<std::string, UTF8> props( LIBRARY_TABLE::ParseOptions( options.ToStdString() ) );
600
601 if( PCB_IO_MGR::ConvertLibrary( props, legacyLib.GetFullPath(), newLib.GetFullPath(),
602 errorReporter.m_Reporter ) )
603 {
604 relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(), m_project );
605
606 cur_grid()->SetCellValue( row, COL_URI, relPath );
607 cur_grid()->SetCellValue( row, COL_TYPE, kicadType );
608 }
609 else
610 {
611 DisplayErrorMessage( m_parent, wxString::Format( _( "Failed to save footprint library file '%s'." ),
612 newLib.GetFullPath() ) );
613 }
614 }
615
616 if( errorReporter.m_Reporter->HasMessage() )
617 {
618 errorReporter.m_Reporter->Flush(); // Build HTML messages
619 errorReporter.ShowModal();
620 }
621}
622
623
625{
626 if( !cur_grid()->CommitPendingChanges() )
627 return;
628
630
631 // We are bound both to the menu and button with this one handler
632 // So we must set the file type based on it
633 if( event.GetEventType() == wxEVT_BUTTON )
634 {
635 // Let's default to adding a kicad footprint file for just the footprint
637 }
638 else
639 {
640 fileType = static_cast<PCB_IO_MGR::PCB_FILE_T>( event.GetId() );
641 }
642
644 return;
645
646 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
647 const IO_BASE::IO_FILE_DESC& fileDesc = m_supportedFpFiles.at( fileType );
649
650 wxString title;
651 wxString dummy;
652 wxString* lastDir;
653
655 title = _( "Select Library Table" );
656 else
657 title = wxString::Format( _( "Select %s Library" ), PCB_IO_MGR::ShowType( fileType ) );
658
659 if( m_notebook->GetSelection() == 0 )
660 lastDir = cfg ? &cfg->m_LastFootprintLibDir : &dummy;
661 else
662 lastDir = &m_lastProjectLibDir;
663
664 wxArrayString files;
665
666 if( fileDesc.m_IsFile )
667 {
668 wxFileDialog dlg( m_parent, title, *lastDir, wxEmptyString, fileDesc.FileFilter(),
669 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_MULTIPLE );
670
672
673 if( dlg.ShowModal() == wxID_CANCEL )
674 return;
675
676 dlg.GetPaths( files );
677 *lastDir = dlg.GetDirectory();
678 }
679 else
680 {
681 wxDirDialog dlg( m_parent, title, *lastDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST | wxDD_MULTIPLE );
682
683 if( dlg.ShowModal() == wxID_CANCEL )
684 return;
685
686 dlg.GetPaths( files );
687
688 if( !files.IsEmpty() )
689 {
690 wxFileName first( files.front() );
691 *lastDir = first.GetPath();
692 }
693 }
694
695 // Drop the last directory if the path is a .pretty folder
697 cfg->m_LastFootprintLibDir = cfg->m_LastFootprintLibDir.BeforeLast( wxFileName::GetPathSeparator() );
698
699
700 bool addDuplicates = false;
701 bool applyToAll = false;
702 wxString warning = _( "Warning: Duplicate Nicknames" );
703 wxString msg = _( "An item nicknamed '%s' already exists." );
704 wxString detailedMsg = _( "One of the nicknames will need to be changed." );
705
706 for( const wxString& filePath : files )
707 {
708 wxFileName fn( filePath );
709 wxString nickname = LIB_ID::FixIllegalChars( fn.GetName(), true );
710 bool doAdd = true;
711
714 {
715 nickname = LIB_ID::FixIllegalChars( fn.GetFullName(), true ).wx_str();
716 }
717
718 if( cur_model()->ContainsNickname( nickname ) )
719 {
720 if( !applyToAll )
721 {
722 // The cancel button adds the library to the table anyway
723 addDuplicates = OKOrCancelDialog( m_parent, warning, wxString::Format( msg, nickname ), detailedMsg,
724 _( "Skip" ), _( "Add Anyway" ), &applyToAll ) == wxID_CANCEL;
725 }
726
727 doAdd = addDuplicates;
728 }
729
730 if( doAdd && cur_grid()->AppendRows( 1 ) )
731 {
732 int last_row = cur_grid()->GetNumberRows() - 1;
733
734 cur_grid()->SetCellValue( last_row, COL_NICKNAME, nickname );
735 cur_grid()->SetCellValue( last_row, COL_TYPE, PCB_IO_MGR::ShowType( fileType ) );
736
737 // try to use path normalized to an environmental variable or project path
738 wxString path = NormalizePath( filePath, &envVars, m_project->GetProjectPath() );
739
740 // Do not use the project path in the global library table. This will almost
741 // assuredly be wrong for a different project.
742 if( m_notebook->GetSelection() == 0 && path.Contains( wxT( "${KIPRJMOD}" ) ) )
743 path = fn.GetFullPath();
744
745 cur_grid()->SetCellValue( last_row, COL_URI, path );
746 }
747 }
748
749 if( !files.IsEmpty() )
750 {
751 cur_grid()->MakeCellVisible( cur_grid()->GetNumberRows() - 1, COL_ENABLED );
752 cur_grid()->SetGridCursor( cur_grid()->GetNumberRows() - 1, COL_NICKNAME );
753 }
754}
755
756
757void PANEL_FP_LIB_TABLE::onNotebookPageCloseRequest( wxAuiNotebookEvent& aEvent )
758{
759 wxAuiNotebook* notebook = (wxAuiNotebook*) aEvent.GetEventObject();
760 wxWindow* page = notebook->GetPage( aEvent.GetSelection() );
761
762 if( LIB_TABLE_NOTEBOOK_PANEL* panel = dynamic_cast<LIB_TABLE_NOTEBOOK_PANEL*>( page ) )
763 {
764 if( panel->GetClosable() )
765 {
766 if( !panel->GetCanClose() )
767 aEvent.Veto();
768 }
769 else
770 {
771 aEvent.Veto();
772 }
773 }
774}
775
776
778{
779 // Account for scroll bars
780 aWidth -= ( m_path_subs_grid->GetSize().x - m_path_subs_grid->GetClientSize().x );
781
782 m_path_subs_grid->AutoSizeColumn( 0 );
783 m_path_subs_grid->SetColSize( 0, std::max( 72, m_path_subs_grid->GetColSize( 0 ) ) );
784 m_path_subs_grid->SetColSize( 1, std::max( 120, aWidth - m_path_subs_grid->GetColSize( 0 ) ) );
785}
786
787
788void PANEL_FP_LIB_TABLE::onSizeGrid( wxSizeEvent& event )
789{
790 adjustPathSubsGridColumns( event.GetSize().GetX() );
791
792 event.Skip();
793}
794
795
796void PANEL_FP_LIB_TABLE::onReset( wxCommandEvent& event )
797{
798 if( !cur_grid()->CommitPendingChanges() )
799 return;
800
801 WX_GRID* grid = get_grid( 0 );
802
803 // No need to prompt to preserve an empty table
804 if( grid->GetNumberRows() > 0 && !IsOK( this, wxString::Format( _( "This action will reset your global library "
805 "table on disk and cannot be undone." ) ) ) )
806 {
807 return;
808 }
809
810 wxString* lastGlobalLibDir = nullptr;
811
812 if( PCBNEW_SETTINGS* cfg = GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" ) )
813 {
814 if( cfg->m_LastFootprintLibDir.IsEmpty() )
815 cfg->m_LastFootprintLibDir = PATHS::GetDefaultUserFootprintsPath();
816
817 lastGlobalLibDir = &cfg->m_LastFootprintLibDir;
818 }
819
821
822 // Go ahead and reload here because this action takes place even if the dialog is canceled
824
825 if( KIFACE *face = m_parent->Kiway().KiFACE( KIWAY::FACE_PCB ) )
826 face->PreloadLibraries( &m_parent->Kiway() );
827
828 grid->Freeze();
829
830 wxGridTableBase* table = grid->GetTable();
831 grid->DestroyTable( table );
832
833 std::optional<LIBRARY_TABLE*> newTable = Pgm().GetLibraryManager().Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
835 wxASSERT( newTable );
836
838
839 grid->SetTable( new FP_LIB_TABLE_GRID_DATA_MODEL( m_parent, grid, *newTable.value(), adapter, m_pluginChoices,
840 lastGlobalLibDir, wxEmptyString, m_supportedFpFiles ),
841 true /* take ownership */ );
842
843 LIB_TABLE_NOTEBOOK_PANEL* panel0 = static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( 0 ) );
844 panel0->ClearDirty();
845
846 static_cast<LIB_TABLE_GRID_DATA_MODEL*>( grid->GetTable() )->SetChangeCallback(
847 [panel0]()
848 {
849 panel0->MarkDirty();
850 } );
851
852 m_parent->m_GlobalTableChanged = true;
853
854 grid->Thaw();
855
856 if( grid->GetNumberRows() > 0 )
857 {
858 grid->SetGridCursor( 0, COL_NICKNAME );
859 grid->SelectRow( 0 );
860 }
861}
862
863
864void PANEL_FP_LIB_TABLE::onNotebookPageChangeRequest( wxAuiNotebookEvent& aEvent )
865{
867 aEvent.Veto();
868 else
869 aEvent.Skip();
870}
871
872
873void PANEL_FP_LIB_TABLE::onPageChange( wxAuiNotebookEvent& event )
874{
875 m_resetGlobal->Enable( m_notebook->GetSelection() == 0 );
876}
877
878
880{
881 if( !cur_grid()->CommitPendingChanges() )
882 return false;
883
884 if( !verifyTables() )
885 return false;
886
887 bool success = true;
889 int firstNestedTable = 1;
890 std::optional<LIBRARY_TABLE*> globalTable = manager.Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
892
893 if( globalTable.has_value() && get_model( 0 )->Table() != *globalTable.value() )
894 {
895 m_parent->m_GlobalTableChanged = true;
896 *globalTable.value() = get_model( 0 )->Table();
897
898 globalTable.value()->Save().map_error(
899 [&success]( const LIBRARY_ERROR& aError )
900 {
901 wxMessageBox( _( "Error saving global library table:\n\n" ) + aError.message,
902 _( "File Save Error" ), wxOK | wxICON_ERROR );
903 success = false;
904 } );
905 }
906
907 std::optional<LIBRARY_TABLE*> projectTable = manager.Table( LIBRARY_TABLE_TYPE::FOOTPRINT,
909
910 if( projectTable.has_value() && get_model( 1 )->Table().Path() == projectTable.value()->Path() )
911 {
912 firstNestedTable = 2;
913
914 if( get_model( 1 )->Table() != *projectTable.value() )
915 {
916 m_parent->m_ProjectTableChanged = true;
917 *projectTable.value() = get_model( 1 )->Table();
918
919 projectTable.value()->Save().map_error(
920 [&success]( const LIBRARY_ERROR& aError )
921 {
922 wxMessageBox( _( "Error saving project library table:\n\n" ) + aError.message,
923 _( "File Save Error" ), wxOK | wxICON_ERROR );
924 success = false;
925 } );
926 }
927 }
928
929 for( int ii = firstNestedTable; ii < (int) m_notebook->GetPageCount(); ++ii )
930 {
931 LIB_TABLE_NOTEBOOK_PANEL* panel = static_cast<LIB_TABLE_NOTEBOOK_PANEL*>( m_notebook->GetPage( ii ) );
932
933 if( panel->TableModified() )
934 success &= panel->SaveTable();
935 }
936
938 return success;
939}
940
941
945{
946 wxRegEx re( ".*?(\\$\\{(.+?)\\})|(\\$\\((.+?)\\)).*?", wxRE_ADVANCED );
947 wxASSERT( re.IsValid() ); // wxRE_ADVANCED is required.
948
949 std::set< wxString > unique;
950
951 // clear the table
952 m_path_subs_grid->ClearRows();
953
954 for( int page = 0 ; page < (int) m_notebook->GetPageCount(); ++page )
955 {
957
958 for( int row = 0; row < model->GetNumberRows(); ++row )
959 {
960 wxString uri = model->GetValue( row, COL_URI );
961
962 while( re.Matches( uri ) )
963 {
964 wxString envvar = re.GetMatch( uri, 2 );
965
966 // if not ${...} form then must be $(...)
967 if( envvar.IsEmpty() )
968 envvar = re.GetMatch( uri, 4 );
969
970 // ignore duplicates
971 unique.insert( envvar );
972
973 // delete the last match and search again
974 uri.Replace( re.GetMatch( uri, 0 ), wxEmptyString );
975 }
976 }
977 }
978
979 // Make sure this special environment variable shows up even if it was not used yet. It is
980 // automatically set by KiCad to the directory holding the current project.
981 unique.insert( PROJECT_VAR_NAME );
982 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "FOOTPRINT_DIR" ) ) );
983
984 // This special environment variable is used to locate 3d shapes
985 unique.insert( ENV_VAR::GetVersionedEnvVarName( wxS( "3DMODEL_DIR" ) ) );
986
987 for( const wxString& evName : unique )
988 {
989 int row = m_path_subs_grid->GetNumberRows();
990 m_path_subs_grid->AppendRows( 1 );
991
992 m_path_subs_grid->SetCellValue( row, 0, wxT( "${" ) + evName + wxT( "}" ) );
993 m_path_subs_grid->SetCellEditor( row, 0, new GRID_CELL_READONLY_TEXT_EDITOR() );
994
995 wxString evValue;
996 wxGetEnv( evName, &evValue );
997 m_path_subs_grid->SetCellValue( row, 1, evValue );
998 m_path_subs_grid->SetCellEditor( row, 1, new GRID_CELL_READONLY_TEXT_EDITOR() );
999 }
1000
1001 adjustPathSubsGridColumns( m_path_subs_grid->GetRect().GetWidth() );
1002}
1003
1004
1005//-----</event handlers>---------------------------------
1006
1007
1008
1009void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller )
1010{
1011 DIALOG_EDIT_LIBRARY_TABLES dlg( aCaller, _( "Footprint Libraries" ) );
1012 dlg.SetKiway( &dlg, aKiway );
1013
1014 PANEL_FP_LIB_TABLE* panel = new PANEL_FP_LIB_TABLE( &dlg, &aKiway->Prj() );
1015 dlg.InstallPanel( panel, panel->GetNotebook() );
1016
1017 // User can choose to save changes on a Cancel, so don't exit on wxID_CANCEL.
1018 dlg.ShowModal();
1019
1020 if( dlg.m_GlobalTableChanged )
1022
1023 if( dlg.m_ProjectTableChanged )
1024 {
1025 // Trigger a reload of the table and cancel an in-progress background load
1027 }
1028
1029 // Trigger a reload in case any libraries have been added or removed
1030 if( KIFACE *face = aKiway->KiFACE( KIWAY::FACE_PCB ) )
1031 face->PreloadLibraries( aKiway );
1032
1033 std::string payload = "";
1036 aKiway->ExpressMail( FRAME_CVPCB, MAIL_RELOAD_LIB, payload );
1037}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
void InstallPanel(wxPanel *aPanel, wxAuiNotebook *aNotebook)
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:80
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)
static bool SupportsVisibilityColumn()
wxString getTablePreamble() override
void optionsEditor(int aRow) override
PANEL_FP_LIB_TABLE * m_panel
bool supportsVisibilityColumn() override
This class builds a wxGridTableBase by wrapping an LIBRARY_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:57
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:71
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:348
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:486
virtual KIFACE * KiFACE(FACE_T aFaceId, bool doLoad=true)
Return the KIFACE* given a FACE_T.
Definition kiway.cpp:207
@ FACE_PCB
pcbnew DSO
Definition kiway.h:356
virtual PROJECT & Prj() const
Return the PROJECT associated with this KIWAY.
Definition kiway.cpp:201
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...
void ApplyLibOverrides(LIBRARY_TABLE &aTable)
Applies stored user overrides (disabled/hidden) to rows of a read-only table.
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
static const wxString TABLE_TYPE_NAME
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:205
This abstract base class mixes any object derived from LIBRARY_TABLE into wxGridTableBase so the resu...
virtual LIBRARY_TABLE_ROW & at(size_t aIndex)
void SetChangeCallback(std::function< void()> aCallback)
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 void AppendRowHandler(WX_GRID *aGrid, const wxString &aType)
static bool VerifyTable(WX_GRID *aGrid, bool aSupportsVisibilityColumn, std::function< void(int aRow, int aCol)> aErrorHandler)
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.
void OpenTable(const std::shared_ptr< LIBRARY_TABLE > &table, const wxString &aTitle)
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
std::vector< std::shared_ptr< LIBRARY_TABLE > > m_nestedTables
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
void onNotebookPageChangeRequest(wxAuiNotebookEvent &aEvent)
DIALOG_EDIT_LIBRARY_TABLES * m_parent
void appendRowHandler(wxCommandEvent &event) override
wxAuiNotebook * GetNotebook() const
static wxString GetDefaultUserFootprintsPath()
Gets the default path we point users to create projects.
Definition paths.cpp:93
wxString m_LastFootprintLibDir
static PLUGIN_REGISTRY * Instance()
Definition pcb_io_mgr.h:99
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:52
@ KICAD_SEXP
S-expression Pcbnew file format.
Definition pcb_io_mgr.h:54
@ PCB_FILE_UNKNOWN
0 is not a legal menu id on Mac
Definition pcb_io_mgr.h:53
static PCB_IO * FindPlugin(PCB_FILE_T aFileType)
Return a PCB_IO 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:792
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:63
wxString wx_str() const
Definition utf8.cpp:41
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:776
wxString JoinExtensions(const std::vector< std::string > &aExts)
Join a list of file extensions for use in a file dialog.
Definition common.cpp:867
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:165
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:274
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
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:41
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
@ FRAME_CVPCB
Definition frame_type.h:48
static const std::string FootprintLibraryTableFileName
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:733
This file contains miscellaneous commonly used macros and functions.
@ MAIL_RELOAD_LIB
Definition mail_type.h:54
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:533
void InvokePcbLibTableEditor(KIWAY *aKiway, wxWindow *aCaller)
Show the modal DIALOG_EDIT_LIBRARY_TABLES for purposes of editing the global and project library tabl...
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition project.h:38
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:43
bool m_IsFile
Whether the library is a folder or a file.
Definition io_base.h:51
wxString FileFilter() const
Definition io_base.cpp:40
Implement a participant in the KIWAY alchemy.
Definition kiway.h:153
wxString message
std::string path
KIBIS_MODEL * model
wxString result
Test unit parsing edge cases and error handling.
Definition of file extensions used in Kicad.