From 32b6deb8b1aa43f9c758729079e04b3df728d5a6 Mon Sep 17 00:00:00 2001 From: Roberto Fernandez Bautista Date: Mon, 28 Aug 2023 22:44:10 +0200 Subject: [PATCH] ADDED Migrate libraries button in Footprint Library Table Allows easy migration of legacy and non-KiCad footprint libraries --- common/wildcards_and_files_ext.cpp | 1 + include/wildcards_and_files_ext.h | 1 + pcbnew/dialogs/panel_fp_lib_table.cpp | 172 +++++++++++++++++++-- pcbnew/dialogs/panel_fp_lib_table.h | 16 +- pcbnew/dialogs/panel_fp_lib_table_base.cpp | 10 +- pcbnew/dialogs/panel_fp_lib_table_base.fbp | 84 ++++++++++ pcbnew/dialogs/panel_fp_lib_table_base.h | 4 +- pcbnew/io_mgr.cpp | 4 + 8 files changed, 271 insertions(+), 21 deletions(-) diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index b999f834c5..9b84c32484 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -175,6 +175,7 @@ const std::string FootprintPlaceFileExtension( "pos" ); const std::string KiCadFootprintLibPathExtension( "pretty" ); // this is a directory const std::string LegacyFootprintLibPathExtension( "mod" ); // this is a file const std::string AltiumFootprintLibPathExtension( "PcbLib" ); // this is a file +const std::string CadstarFootprintLibPathExtension( "cpa" ); // this is a file const std::string EagleFootprintLibPathExtension( "lbr" ); // this is a file const std::string GedaPcbFootprintLibFileExtension( "fp" ); // this is a file diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index 9b2154633a..6ba1df43f7 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -160,6 +160,7 @@ extern const std::string FootprintPlaceFileExtension; extern const std::string KiCadFootprintFileExtension; extern const std::string KiCadFootprintLibPathExtension; extern const std::string AltiumFootprintLibPathExtension; +extern const std::string CadstarFootprintLibPathExtension; extern const std::string LtspiceSchematicExtension; extern const std::string LtspiceSymbolExtension; extern const std::string GedaPcbFootprintLibFileExtension; diff --git a/pcbnew/dialogs/panel_fp_lib_table.cpp b/pcbnew/dialogs/panel_fp_lib_table.cpp index da06cd8a5b..53493fb235 100644 --- a/pcbnew/dialogs/panel_fp_lib_table.cpp +++ b/pcbnew/dialogs/panel_fp_lib_table.cpp @@ -272,17 +272,18 @@ protected: }; -PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, - FP_LIB_TABLE* aGlobal, const wxString& aGlobalTblPath, - FP_LIB_TABLE* aProject, const wxString& aProjectTblPath, +PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, PROJECT* aProject, + FP_LIB_TABLE* aGlobalTable, const wxString& aGlobalTblPath, + FP_LIB_TABLE* aProjectTable, const wxString& aProjectTblPath, const wxString& aProjectBasePath ) : PANEL_FP_LIB_TABLE_BASE( aParent ), - m_global( aGlobal ), + m_globalTable( aGlobalTable ), + m_projectTable( aProjectTable ), m_project( aProject ), m_projectBasePath( aProjectBasePath ), m_parent( aParent ) { - m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobal ), true ); + m_global_grid->SetTable( new FP_LIB_TABLE_GRID( *aGlobalTable ), true ); // add Cut, Copy, and Paste to wxGrids m_path_subs_grid->PushEventHandler( new GRID_TRICKS( m_path_subs_grid ) ); @@ -361,9 +362,9 @@ PANEL_FP_LIB_TABLE::PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, populateEnvironReadOnlyTable(); - if( aProject ) + if( aProjectTable ) { - m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProject ), true ); + m_project_grid->SetTable( new FP_LIB_TABLE_GRID( *aProjectTable ), true ); setupGrid( m_project_grid ); } else @@ -733,6 +734,149 @@ void PANEL_FP_LIB_TABLE::moveDownHandler( wxCommandEvent& event ) } +// @todo refactor this function into single location shared with PANEL_SYM_LIB_TABLE +void PANEL_FP_LIB_TABLE::onMigrateLibraries( wxCommandEvent& event ) +{ + if( !m_cur_grid->CommitPendingChanges() ) + return; + + wxArrayInt selectedRows = m_cur_grid->GetSelectedRows(); + + if( selectedRows.empty() && m_cur_grid->GetGridCursorRow() >= 0 ) + selectedRows.push_back( m_cur_grid->GetGridCursorRow() ); + + wxArrayInt rowsToMigrate; + wxString kicadType = IO_MGR::ShowType( IO_MGR::KICAD_SEXP ); + wxString msg; + + for( int row : selectedRows ) + { + if( m_cur_grid->GetCellValue( row, COL_TYPE ) != kicadType ) + rowsToMigrate.push_back( row ); + } + + if( rowsToMigrate.size() <= 0 ) + { + wxMessageBox( wxString::Format( _( "Select one or more rows containing libraries " + "to save as current KiCad format." ) ) ); + return; + } + else + { + if( rowsToMigrate.size() == 1 ) + { + msg.Printf( _( "Save '%s' as current KiCad format " + "and replace entry in table?" ), + m_cur_grid->GetCellValue( rowsToMigrate[0], COL_NICKNAME ) ); + } + else + { + msg.Printf( _( "Save %d libraries as current KiCad format " + "and replace entries in table?" ), + (int) rowsToMigrate.size() ); + } + + if( !IsOK( m_parent, msg ) ) + return; + } + + for( int row : rowsToMigrate ) + { + wxString libName = m_cur_grid->GetCellValue( row, COL_NICKNAME ); + wxString relPath = m_cur_grid->GetCellValue( row, COL_URI ); + wxString resolvedPath = ExpandEnvVarSubstitutions( relPath, m_project ); + wxFileName legacyLib( resolvedPath ); + + if( !legacyLib.Exists() ) + { + msg.Printf( _( "Library '%s' not found." ), relPath ); + DisplayErrorMessage( this, msg ); + continue; + } + + wxFileName newLib( resolvedPath ); + newLib.AppendDir( newLib.GetName() + "." + KiCadFootprintLibPathExtension ); + newLib.SetName( "" ); + newLib.ClearExt(); + + if( newLib.DirExists() ) + { + msg.Printf( _( "Folder '%s' already exists. Do you want overwrite any existing footprints?" ), + newLib.GetFullPath() ); + + switch( wxMessageBox( msg, _( "Migrate Library" ), + wxYES_NO | wxCANCEL | wxICON_QUESTION, m_parent ) ) + { + case wxYES: break; + case wxNO: continue; + case wxCANCEL: return; + } + } + + wxString options = m_cur_grid->GetCellValue( row, COL_OPTIONS ); + std::unique_ptr props( LIB_TABLE::ParseOptions( options.ToStdString() ) ); + + if( convertLibrary( props.get(), legacyLib.GetFullPath(), newLib.GetFullPath() ) ) + { + relPath = NormalizePath( newLib.GetFullPath(), &Pgm().GetLocalEnvVariables(), + m_project ); + + // Do not use the project path in the global library table. This will almost + // assuredly be wrong for a different project. + if( m_cur_grid == m_global_grid && relPath.Contains( "${KIPRJMOD}" ) ) + relPath = newLib.GetFullPath(); + + m_cur_grid->SetCellValue( row, COL_URI, relPath ); + m_cur_grid->SetCellValue( row, COL_TYPE, kicadType ); + } + else + { + msg.Printf( _( "Failed to save footprint library file '%s'." ), newLib.GetFullPath() ); + DisplayErrorMessage( this, msg ); + } + } +} + + +bool PANEL_FP_LIB_TABLE::convertLibrary( STRING_UTF8_MAP* aOldFileProps, + const wxString& aOldFilePath, + const wxString& aNewFilePath) +{ + IO_MGR::PCB_FILE_T oldFileType = IO_MGR::GuessPluginTypeFromLibPath( aOldFilePath ); + + if( oldFileType == IO_MGR::FILE_TYPE_NONE ) + return false; + + + PLUGIN::RELEASER oldFilePI( IO_MGR::PluginFind( oldFileType ) ); + PLUGIN::RELEASER kicadPI( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) ); + wxArrayString fpNames; + wxFileName newFileName( aNewFilePath ); + + try + { + if( !newFileName.DirExists() ) + wxMkDir( aNewFilePath ); + + bool bestEfforts = false; // throw on first error + oldFilePI->FootprintEnumerate( fpNames, aOldFilePath, bestEfforts, aOldFileProps ); + + for ( const wxString& fpName : fpNames ) + { + std::unique_ptr fp( oldFilePI->GetEnumeratedFootprint( aOldFilePath, fpName, aOldFileProps ) ); + kicadPI->FootprintSave( aNewFilePath, fp.get() ); + } + + } + catch( ... ) + { + return false; + } + + return true; +} + + void PANEL_FP_LIB_TABLE::browseLibrariesHandler( wxCommandEvent& event ) { if( !m_cur_grid->CommitPendingChanges() ) @@ -899,20 +1043,20 @@ bool PANEL_FP_LIB_TABLE::TransferDataFromWindow() if( verifyTables() ) { - if( *global_model() != *m_global ) + if( *global_model() != *m_globalTable ) { m_parent->m_GlobalTableChanged = true; - m_global->Clear(); - m_global->TransferRows( global_model()->m_rows ); + m_globalTable->Clear(); + m_globalTable->TransferRows( global_model()->m_rows ); } - if( project_model() && *project_model() != *m_project ) + if( project_model() && *project_model() != *m_projectTable ) { m_parent->m_ProjectTableChanged = true; - m_project->Clear(); - m_project->TransferRows( project_model()->m_rows ); + m_projectTable->Clear(); + m_projectTable->TransferRows( project_model()->m_rows ); } return true; @@ -1010,7 +1154,7 @@ void InvokePcbLibTableEditor( KIWAY* aKiway, wxWindow* aCaller ) if( aKiway->Prj().IsNullProject() ) projectTable = nullptr; - dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, globalTable, globalTablePath, + dlg.InstallPanel( new PANEL_FP_LIB_TABLE( &dlg, &aKiway->Prj(), globalTable, globalTablePath, projectTable, projectTablePath, aKiway->Prj().GetProjectPath() ) ); diff --git a/pcbnew/dialogs/panel_fp_lib_table.h b/pcbnew/dialogs/panel_fp_lib_table.h index e2439c60fd..272b3d88e6 100644 --- a/pcbnew/dialogs/panel_fp_lib_table.h +++ b/pcbnew/dialogs/panel_fp_lib_table.h @@ -26,6 +26,7 @@ class FP_LIB_TABLE; class FP_LIB_TABLE_GRID; +class PROJECT; /** @@ -35,9 +36,9 @@ class PANEL_FP_LIB_TABLE : public PANEL_FP_LIB_TABLE_BASE { public: - PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, - FP_LIB_TABLE* aGlobal, const wxString& aGlobalTblPath, - FP_LIB_TABLE* aProject, const wxString& aProjectTblPath, + PANEL_FP_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, PROJECT* aProject, + FP_LIB_TABLE* aGlobalTable, const wxString& aGlobalTblPath, + FP_LIB_TABLE* aProjectTable, const wxString& aProjectTblPath, const wxString& aProjectBasePath ); ~PANEL_FP_LIB_TABLE() override; @@ -57,6 +58,7 @@ private: void deleteRowHandler( wxCommandEvent& event ) override; void moveUpHandler( wxCommandEvent& event ) override; void moveDownHandler( wxCommandEvent& event ) override; + void onMigrateLibraries( wxCommandEvent& event ) override; void onSizeGrid( wxSizeEvent& event ) override; void adjustPathSubsGridColumns( int aWidth ); @@ -65,6 +67,9 @@ private: /// by examining all the full_uri columns. void populateEnvironReadOnlyTable(); void populatePluginList(); + + bool convertLibrary( STRING_UTF8_MAP* aOldFileProps, const wxString& aOldFilePath, + const wxString& aNewFilePath ); FP_LIB_TABLE_GRID* global_model() const { @@ -82,8 +87,9 @@ private: } // caller's tables are modified only on OK button and successful verification. - FP_LIB_TABLE* m_global; - FP_LIB_TABLE* m_project; + FP_LIB_TABLE* m_globalTable; + FP_LIB_TABLE* m_projectTable; + PROJECT* m_project; wxString m_projectBasePath; DIALOG_EDIT_LIBRARY_TABLES* m_parent; diff --git a/pcbnew/dialogs/panel_fp_lib_table_base.cpp b/pcbnew/dialogs/panel_fp_lib_table_base.cpp index 0bb00847bd..49ab432352 100644 --- a/pcbnew/dialogs/panel_fp_lib_table_base.cpp +++ b/pcbnew/dialogs/panel_fp_lib_table_base.cpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.0-4761b0c) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -151,6 +151,12 @@ PANEL_FP_LIB_TABLE_BASE::PANEL_FP_LIB_TABLE_BASE( wxWindow* parent, wxWindowID i bButtonsSizer->Add( m_delete_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 ); + bButtonsSizer->Add( 20, 0, 1, wxEXPAND, 5 ); + + m_migrate_libs_button = new wxButton( this, wxID_ANY, _("Migrate Libraries"), wxDefaultPosition, wxDefaultSize, 0 ); + bButtonsSizer->Add( m_migrate_libs_button, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + bMainSizer->Add( bButtonsSizer, 0, wxEXPAND|wxALL, 8 ); wxStaticText* stPathsLabel; @@ -203,6 +209,7 @@ PANEL_FP_LIB_TABLE_BASE::PANEL_FP_LIB_TABLE_BASE( wxWindow* parent, wxWindowID i m_move_up_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::moveUpHandler ), NULL, this ); m_move_down_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::moveDownHandler ), NULL, this ); m_delete_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::deleteRowHandler ), NULL, this ); + m_migrate_libs_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::onMigrateLibraries ), NULL, this ); m_path_subs_grid->Connect( wxEVT_SIZE, wxSizeEventHandler( PANEL_FP_LIB_TABLE_BASE::onSizeGrid ), NULL, this ); } @@ -214,6 +221,7 @@ PANEL_FP_LIB_TABLE_BASE::~PANEL_FP_LIB_TABLE_BASE() m_move_up_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::moveUpHandler ), NULL, this ); m_move_down_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::moveDownHandler ), NULL, this ); m_delete_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::deleteRowHandler ), NULL, this ); + m_migrate_libs_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_FP_LIB_TABLE_BASE::onMigrateLibraries ), NULL, this ); m_path_subs_grid->Disconnect( wxEVT_SIZE, wxSizeEventHandler( PANEL_FP_LIB_TABLE_BASE::onSizeGrid ), NULL, this ); } diff --git a/pcbnew/dialogs/panel_fp_lib_table_base.fbp b/pcbnew/dialogs/panel_fp_lib_table_base.fbp index 85747fcdd8..7bc472c4ec 100644 --- a/pcbnew/dialogs/panel_fp_lib_table_base.fbp +++ b/pcbnew/dialogs/panel_fp_lib_table_base.fbp @@ -796,6 +796,90 @@ deleteRowHandler + + 5 + wxEXPAND + 1 + + 0 + protected + 20 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Migrate Libraries + + 0 + + 0 + + + 0 + + 1 + m_migrate_libs_button + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onMigrateLibraries + + diff --git a/pcbnew/dialogs/panel_fp_lib_table_base.h b/pcbnew/dialogs/panel_fp_lib_table_base.h index 5f8859d4cc..57a0f7b545 100644 --- a/pcbnew/dialogs/panel_fp_lib_table_base.h +++ b/pcbnew/dialogs/panel_fp_lib_table_base.h @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b) +// C++ code generated with wxFormBuilder (version 3.10.0-4761b0c) // http://www.wxformbuilder.org/ // // PLEASE DO *NOT* EDIT THIS FILE! @@ -51,6 +51,7 @@ class PANEL_FP_LIB_TABLE_BASE : public wxPanel STD_BITMAP_BUTTON* m_move_up_button; STD_BITMAP_BUTTON* m_move_down_button; STD_BITMAP_BUTTON* m_delete_button; + wxButton* m_migrate_libs_button; WX_GRID* m_path_subs_grid; // Virtual event handlers, override them in your derived class @@ -59,6 +60,7 @@ class PANEL_FP_LIB_TABLE_BASE : public wxPanel virtual void moveUpHandler( wxCommandEvent& event ) { event.Skip(); } virtual void moveDownHandler( wxCommandEvent& event ) { event.Skip(); } virtual void deleteRowHandler( wxCommandEvent& event ) { event.Skip(); } + virtual void onMigrateLibraries( wxCommandEvent& event ) { event.Skip(); } virtual void onSizeGrid( wxSizeEvent& event ) { event.Skip(); } diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp index f6c0a13ac8..389a8518ac 100644 --- a/pcbnew/io_mgr.cpp +++ b/pcbnew/io_mgr.cpp @@ -162,6 +162,10 @@ IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath { ret = ALTIUM_DESIGNER; } + else if( fn.GetExt() == CadstarFootprintLibPathExtension ) + { + ret = CADSTAR_PCB_ARCHIVE; + } // Test this one anyways, even though it's the default guess, to avoid // the wxURI instantiation below.