From afa4ebcc846facae098762ecb5af4e5ee6a97f1c Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 3 Mar 2025 13:36:57 +0000 Subject: [PATCH] Handle exporting symbols into libraries already under LIB_MGR. Fixes https://gitlab.com/kicad/code/kicad/-/issues/17840 --- eeschema/symbol_editor/symbol_editor.cpp | 144 +++++++++++++++++- .../symbol_editor_import_export.cpp | 96 ------------ 2 files changed, 142 insertions(+), 98 deletions(-) diff --git a/eeschema/symbol_editor/symbol_editor.cpp b/eeschema/symbol_editor/symbol_editor.cpp index 00061ea6b0..8fe1c2b174 100644 --- a/eeschema/symbol_editor/symbol_editor.cpp +++ b/eeschema/symbol_editor/symbol_editor.cpp @@ -692,7 +692,9 @@ public: SYMBOL_SAVE_AS_HANDLER( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr, CONFLICT_STRATEGY aStrategy, bool aValueFollowsName ) : - m_libMgr( aLibMgr ), m_strategy( aStrategy ), m_valueFollowsName( aValueFollowsName ) + m_libMgr( aLibMgr ), + m_strategy( aStrategy ), + m_valueFollowsName( aValueFollowsName ) { } @@ -990,7 +992,7 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) if( conflicts.size() == 1 && conflicts.front() == newName ) { // The simplest case is when the symbol itself has a conflict - msg = wxString::Format( _( "Symbol '%s' already exists in library '%s'." + msg = wxString::Format( _( "Symbol '%s' already exists in library '%s'. " "Do you want to overwrite it?" ), newName, newLib ); @@ -1082,6 +1084,144 @@ void SYMBOL_EDIT_FRAME::saveSymbolCopyAs( bool aOpenCopy ) } +void SYMBOL_EDIT_FRAME::ExportSymbol() +{ + wxString msg; + LIB_SYMBOL* symbol = getTargetSymbol(); + + if( !symbol ) + { + ShowInfoBarError( _( "There is no symbol selected to save." ) ); + return; + } + + wxFileName fn; + + fn.SetName( symbol->GetName().Lower() ); + fn.SetExt( FILEEXT::KiCadSymbolLibFileExtension ); + + wxFileDialog dlg( this, _( "Export Symbol" ), m_mruPath, fn.GetFullName(), + FILEEXT::KiCadSymbolLibFileWildcard(), wxFD_SAVE ); + + if( dlg.ShowModal() == wxID_CANCEL ) + return; + + auto strategy = SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY::OVERWRITE; + + fn = dlg.GetPath(); + fn.MakeAbsolute(); + + wxString libraryName; + std::unique_ptr flattenedSymbol = symbol->Flatten(); + + for( const wxString& candidate : m_libMgr->GetLibraryNames() ) + { + if( m_libMgr->GetLibrary( candidate )->GetFullURI( true ) == fn.GetFullPath() ) + libraryName = candidate; + } + + if( !libraryName.IsEmpty() ) + { + SYMBOL_SAVE_AS_HANDLER saver( *m_libMgr, strategy, false ); + + if( m_libMgr->IsLibraryReadOnly( libraryName ) ) + { + msg = wxString::Format( _( "Library '%s' is read-only." ), libraryName ); + DisplayError( this, msg ); + return; + } + + if( m_libMgr->SymbolNameInUse( symbol->GetName(), libraryName ) ) + { + msg = wxString::Format( _( "Symbol '%s' already exists in library '%s'." ), + symbol->GetName(), libraryName ); + + KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); + errorDlg.SetOKLabel( _( "Overwrite" ) ); + errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ ); + + if( errorDlg.ShowModal() == wxID_CANCEL ) + return; + } + + saver.DoSave( *flattenedSymbol, symbol->GetName(), libraryName ); + + SyncLibraries( false ); + return; + } + + LIB_SYMBOL* old_symbol = nullptr; + SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() ); + + if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN ) + pluginType = SCH_IO_MGR::SCH_KICAD; + + IO_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) ); + + if( fn.FileExists() ) + { + try + { + old_symbol = pi->LoadSymbol( fn.GetFullPath(), symbol->GetName() ); + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Error occurred attempting to load symbol library file '%s'." ), + fn.GetFullPath() ); + DisplayErrorMessage( this, msg, ioe.What() ); + return; + } + + if( old_symbol ) + { + msg.Printf( _( "Symbol %s already exists in library '%s'." ), + UnescapeString( symbol->GetName() ), + fn.GetFullName() ); + + KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); + errorDlg.SetOKLabel( _( "Overwrite" ) ); + errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ ); + + if( errorDlg.ShowModal() == wxID_CANCEL ) + return; + } + } + + if( !fn.IsDirWritable() ) + { + msg.Printf( _( "Insufficient permissions to save library '%s'." ), fn.GetFullPath() ); + DisplayError( this, msg ); + return; + } + + try + { + if( !fn.FileExists() ) + pi->CreateLibrary( fn.GetFullPath() ); + + // The flattened symbol is most likely what the user would want. As some point in + // the future as more of the symbol library inheritance is implemented, this may have + // to be changes to save symbols of inherited symbols. + pi->SaveSymbol( fn.GetFullPath(), flattenedSymbol.release() ); + } + catch( const IO_ERROR& ioe ) + { + msg.Printf( _( "Failed to create symbol library file '%s'." ), fn.GetFullPath() ); + DisplayErrorMessage( this, msg, ioe.What() ); + msg.Printf( _( "Error creating symbol library '%s'." ), fn.GetFullName() ); + SetStatusText( msg ); + return; + } + + m_mruPath = fn.GetPath(); + + msg.Printf( _( "Symbol %s saved to library '%s'." ), + UnescapeString( symbol->GetName() ), + fn.GetFullPath() ); + SetStatusText( msg ); +} + + void SYMBOL_EDIT_FRAME::UpdateAfterSymbolProperties( wxString* aOldName ) { wxCHECK( m_symbol, /* void */ ); diff --git a/eeschema/symbol_editor/symbol_editor_import_export.cpp b/eeschema/symbol_editor/symbol_editor_import_export.cpp index 9c604c47ab..bb42404a86 100644 --- a/eeschema/symbol_editor/symbol_editor_import_export.cpp +++ b/eeschema/symbol_editor/symbol_editor_import_export.cpp @@ -150,99 +150,3 @@ void SYMBOL_EDIT_FRAME::ImportSymbol() } -void SYMBOL_EDIT_FRAME::ExportSymbol() -{ - wxString msg; - LIB_SYMBOL* symbol = getTargetSymbol(); - - if( !symbol ) - { - ShowInfoBarError( _( "There is no symbol selected to save." ) ); - return; - } - - wxFileName fn; - - fn.SetName( symbol->GetName().Lower() ); - fn.SetExt( FILEEXT::KiCadSymbolLibFileExtension ); - - wxFileDialog dlg( this, _( "Export Symbol" ), m_mruPath, fn.GetFullName(), - FILEEXT::KiCadSymbolLibFileWildcard(), wxFD_SAVE ); - - if( dlg.ShowModal() == wxID_CANCEL ) - return; - - fn = dlg.GetPath(); - fn.MakeAbsolute(); - - LIB_SYMBOL* old_symbol = nullptr; - SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() ); - - if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN ) - pluginType = SCH_IO_MGR::SCH_KICAD; - - IO_RELEASER pi( SCH_IO_MGR::FindPlugin( pluginType ) ); - - if( fn.FileExists() ) - { - try - { - old_symbol = pi->LoadSymbol( fn.GetFullPath(), symbol->GetName() ); - } - catch( const IO_ERROR& ioe ) - { - msg.Printf( _( "Error occurred attempting to load symbol library file '%s'." ), - fn.GetFullPath() ); - DisplayErrorMessage( this, msg, ioe.What() ); - return; - } - - if( old_symbol ) - { - msg.Printf( _( "Symbol %s already exists in library '%s'." ), - UnescapeString( symbol->GetName() ), - fn.GetFullName() ); - - KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); - errorDlg.SetOKLabel( _( "Overwrite" ) ); - errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ ); - - if( errorDlg.ShowModal() == wxID_CANCEL ) - return; - } - } - - if( fn.Exists() && !fn.IsDirWritable() ) - { - msg.Printf( _( "Insufficient permissions to save library '%s'." ), - fn.GetFullPath() ); - DisplayError( this, msg ); - return; - } - - try - { - if( !fn.FileExists() ) - pi->CreateLibrary( fn.GetFullPath() ); - - // The flattened symbol is most likely what the user would want. As some point in - // the future as more of the symbol library inheritance is implemented, this may have - // to be changes to save symbols of inherited symbols. - pi->SaveSymbol( fn.GetFullPath(), symbol->Flatten().release() ); - } - catch( const IO_ERROR& ioe ) - { - msg.Printf( _( "Failed to create symbol library file '%s'." ), fn.GetFullPath() ); - DisplayErrorMessage( this, msg, ioe.What() ); - msg.Printf( _( "Error creating symbol library '%s'." ), fn.GetFullName() ); - SetStatusText( msg ); - return; - } - - m_mruPath = fn.GetPath(); - - msg.Printf( _( "Symbol %s saved to library '%s'." ), - UnescapeString( symbol->GetName() ), - fn.GetFullPath() ); - SetStatusText( msg ); -}