From 50b171fa3364e305c5c9823646231b08e01477f4 Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Tue, 29 Dec 2020 13:59:48 -0500 Subject: [PATCH] Symbol editor: improve symbol library save as behavior. ADDED: Radio button controls to symbol library editor save as file dialog to allow the user to choose the appropriate symbol library table changes to be made after the library is saved. Fixes https://gitlab.com/kicad/code/kicad/issues/4911 --- eeschema/symbol_editor/symbol_edit_frame.cpp | 121 ++++++++++++++++++ eeschema/symbol_editor/symbol_edit_frame.h | 29 +++++ eeschema/symbol_editor/symbol_editor.cpp | 127 +++++++++++++++++++ 3 files changed, 277 insertions(+) diff --git a/eeschema/symbol_editor/symbol_edit_frame.cpp b/eeschema/symbol_editor/symbol_edit_frame.cpp index 5a5d8ffc80..a7704c1d35 100644 --- a/eeschema/symbol_editor/symbol_edit_frame.cpp +++ b/eeschema/symbol_editor/symbol_edit_frame.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1307,3 +1308,123 @@ void SYMBOL_EDIT_FRAME::LoadSymbolFromSchematic( const std::unique_ptr DisplaySymbolDatasheet(); Refresh(); } + + +bool SYMBOL_EDIT_FRAME::addLibTableEntry( const wxString& aLibFile, TABLE_SCOPE aScope ) +{ + wxFileName fn = aLibFile; + wxFileName libTableFileName( Prj().GetProjectPath(), + SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); + wxString libNickname = fn.GetName(); + SYMBOL_LIB_TABLE* libTable = Prj().SchSymbolLibTable(); + const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables(); + + if( libTable->HasLibrary( libNickname ) ) + { + wxString tmp; + int suffix = 1; + + while( libTable->HasLibrary( libNickname ) ) + { + tmp.Printf( "%s%d", fn.GetName(), suffix ); + libNickname = tmp; + suffix += 1; + } + } + + SYMBOL_LIB_TABLE_ROW* row = new SYMBOL_LIB_TABLE_ROW(); + row->SetNickName( libNickname ); + + wxString normalizedPath = NormalizePath( aLibFile, &envVars, Prj().GetProjectPath() ); + + if( aScope == GLOBAL_LIB_TABLE ) + { + libTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable(); + libTableFileName = SYMBOL_LIB_TABLE::GetGlobalTableFileName(); + + // We cannot normalize against the current project path when saving to global table. + normalizedPath = NormalizePath( aLibFile, &envVars, wxEmptyString ); + } + + if( normalizedPath.IsEmpty() ) + normalizedPath = aLibFile; + + row->SetFullURI( normalizedPath ); + + wxCHECK( libTable->InsertRow( row ), false ); + + try + { + libTable->Save( libTableFileName.GetFullPath() ); + } + catch( const IO_ERROR& ioe ) + { + wxString msg; + msg.Printf( _( "Error saving %s symbol library table." ), + ( aScope == GLOBAL_LIB_TABLE ) ? _( "global" ) : _( "project" ) ); + + wxMessageDialog dlg( this, msg, _( "File Save Error" ), wxOK | wxICON_ERROR ); + dlg.SetExtendedMessage( ioe.What() ); + dlg.ShowModal(); + + return false; + } + + return true; +} + + +bool SYMBOL_EDIT_FRAME::replaceLibTableEntry( const wxString& aLibNickname, + const wxString& aLibFile ) +{ + // Check the global library table first because checking the project library table + // checks the global library table as well due to library chaining. + bool isGlobalTable = true; + wxFileName libTableFileName = SYMBOL_LIB_TABLE::GetGlobalTableFileName();; + const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables(); + SYMBOL_LIB_TABLE* libTable = &SYMBOL_LIB_TABLE::GetGlobalLibTable(); + SYMBOL_LIB_TABLE_ROW* row = libTable->FindRow( aLibNickname ); + + if( !row ) + { + libTableFileName.SetPath( Prj().GetProjectPath() ); + libTableFileName.SetName( SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() ); + libTable = Prj().SchSymbolLibTable(); + isGlobalTable = false; + row = libTable->FindRow( aLibNickname ); + } + + wxCHECK( row, false ); + + wxString projectPath; + + if( !isGlobalTable ) + projectPath = Prj().GetProjectPath(); + + wxString normalizedPath = NormalizePath( aLibFile, &envVars, projectPath ); + + if( normalizedPath.IsEmpty() ) + normalizedPath = aLibFile; + + row->SetFullURI( normalizedPath ); + row->SetType( "KiCad" ); + + try + { + libTable->Save( libTableFileName.GetFullPath() ); + } + catch( const IO_ERROR& ioe ) + { + wxString msg; + msg.Printf( _( "Error saving %s symbol library table." ), + ( isGlobalTable ) ? _( "global" ) : _( "project" ) ); + + wxMessageDialog dlg( this, msg, _( "File Save Error" ), wxOK | wxICON_ERROR ); + dlg.SetExtendedMessage( ioe.What() ); + dlg.ShowModal(); + + return false; + } + + return true; +} diff --git a/eeschema/symbol_editor/symbol_edit_frame.h b/eeschema/symbol_editor/symbol_edit_frame.h index 6df2b34bea..6fd47f1227 100644 --- a/eeschema/symbol_editor/symbol_edit_frame.h +++ b/eeschema/symbol_editor/symbol_edit_frame.h @@ -469,6 +469,35 @@ private: ///< Rename LIB_PART aliases to avoid conflicts before adding a symbol to a library. void ensureUniqueName( LIB_PART* aPart, const wxString& aLibrary ); + enum TABLE_SCOPE + { + GLOBAL_LIB_TABLE, + PROJECT_LIB_TABLE + }; + + /** + * Add \a aLibFile to the symbol library table defined by \a aScope. + * + * @note The library defined by \a aLibFile must be a KiCad (s-expression) library. + * + * @param aLibFile is the full path and file name of the symbol library to add to the table. + * @param aScope defines if \a aLibFile is added to the global or project library table. + * @return true if successful or false if a failure occurs. + */ + bool addLibTableEntry( const wxString& aLibFile, TABLE_SCOPE aScope = GLOBAL_LIB_TABLE ); + + /** + * Replace the file path of the symbol library table entry \a aLibNickname with \a aLibFile. + * + * @note The library defined by \a aLibFile must be a KiCad (s-expression) library. + * + * @param aLibNickmane is the nickname of an existing library table entry. + * @param aLibFile is the full path and file name of the symbol library to replace in the + * table. + * @return true if successful or false if a failure occurs. + */ + bool replaceLibTableEntry( const wxString& aLibNickname, const wxString& aLibFile ); + DECLARE_EVENT_TABLE() public: diff --git a/eeschema/symbol_editor/symbol_editor.cpp b/eeschema/symbol_editor/symbol_editor.cpp index 356a2c9057..b518eb81e3 100644 --- a/eeschema/symbol_editor/symbol_editor.cpp +++ b/eeschema/symbol_editor/symbol_editor.cpp @@ -45,6 +45,92 @@ #include +/** + * Helper control to inquire user what to do on library save as operation. + */ +class SAVE_AS_HELPER : public wxPanel +{ +public: + SAVE_AS_HELPER( wxWindow* aParent ) : + wxPanel( aParent ) + { + m_simpleSaveAs = new wxRadioButton( this, wxID_ANY, _( "Normal save as operation" ), + wxDefaultPosition, wxDefaultSize, wxRB_GROUP ); + m_simpleSaveAs->SetToolTip( _( "Do not perform any additional operations after saving " + "library." ) ); + m_replaceTableEntry = new wxRadioButton( this, wxID_ANY, + _( "Replace library table entry" ) ); + m_replaceTableEntry->SetToolTip( _( "Replacy symbol library table entry with new library." + "\n\nThe original library will no longer be avaliable " + "for use." ) ); + m_addGlobalTableEntry = new wxRadioButton( this, wxID_ANY, + _( "Add new global library table entry" ) ); + m_addGlobalTableEntry->SetToolTip( _( "Add new entry to the global symbol library table." + "\n\nThe symbol library table nickname is suffixed " + "with\nan integer to ensure no duplicate table " + "entries." ) ); + m_addProjectTableEntry = new wxRadioButton( this, wxID_ANY, + _( "Add new project library table entry" ) ); + m_addProjectTableEntry->SetToolTip( _( "Add new entry to the project symbol library table." + "\n\nThe symbol library table nickname is suffixed " + "with\nan integer to ensure no duplicate table " + "entries." ) ); + + wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL ); + sizer->Add( m_simpleSaveAs, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, 5 ); + sizer->Add( m_replaceTableEntry, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5 ); + sizer->Add( m_addGlobalTableEntry, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5 ); + sizer->Add( m_addProjectTableEntry, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5 ); + + SetSizerAndFit( sizer ); + } + + enum SAH_TYPE + { + UNDEFINED = -1, + NORMAL_SAVE_AS, + REPLACE_TABLE_ENTRY, + ADD_GLOBAL_TABLE_ENTRY, + ADD_PROJECT_TABLE_ENTRY + }; + + SAH_TYPE GetOption() const + { + if( m_simpleSaveAs->GetValue() ) + return SAH_TYPE::NORMAL_SAVE_AS; + else if( m_replaceTableEntry->GetValue() ) + return SAH_TYPE::REPLACE_TABLE_ENTRY; + else if( m_addGlobalTableEntry->GetValue() ) + return ADD_GLOBAL_TABLE_ENTRY; + else if( m_addProjectTableEntry->GetValue() ) + return ADD_PROJECT_TABLE_ENTRY; + else + return UNDEFINED; + } + + /** + * Create a new panel to add to a wxFileDialog object. + * + * The caller owns the created object and is responsible for deleting it. + * + * @param aParent is the parent window that will own the created object. + * @return the newly created panel to add to the wxFileDialog. + */ + static wxWindow* Create( wxWindow* aParent ) + { + wxCHECK( aParent, nullptr ); + + return new SAVE_AS_HELPER( aParent ); + } + +private: + wxRadioButton* m_simpleSaveAs; + wxRadioButton* m_replaceTableEntry; + wxRadioButton* m_addGlobalTableEntry; + wxRadioButton* m_addProjectTableEntry; +}; + + void SYMBOL_EDIT_FRAME::updateTitle() { wxString lib = GetCurLib(); @@ -1026,6 +1112,7 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) { wxFileName fn; wxString msg; + SAVE_AS_HELPER::SAH_TYPE type = SAVE_AS_HELPER::SAH_TYPE::UNDEFINED; SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD; PROJECT& prj = Prj(); @@ -1056,6 +1143,8 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) default_path, fn.GetFullName(), wildcards, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); + dlg.SetExtraControlCreator( &SAVE_AS_HELPER::Create ); + if( dlg.ShowModal() == wxID_CANCEL ) return false; @@ -1065,6 +1154,11 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) if( fn.GetExt().IsEmpty() ) fn.SetExt( KiCadSymbolLibFileExtension ); + + const SAVE_AS_HELPER* sah = dynamic_cast( dlg.GetExtraControl() ); + wxCHECK( sah, false ); + + type = sah->GetOption(); } else { @@ -1091,7 +1185,40 @@ bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) } if( !aNewFile ) + { m_libMgr->ClearLibraryModified( aLibrary ); + } + else + { + bool resyncLibTree = false; + wxString originalLibNickname = getTargetLib(); + + switch( type ) + { + case SAVE_AS_HELPER::SAH_TYPE::REPLACE_TABLE_ENTRY: + resyncLibTree = replaceLibTableEntry( originalLibNickname, fn.GetFullPath() ); + break; + + case SAVE_AS_HELPER::SAH_TYPE::ADD_GLOBAL_TABLE_ENTRY: + resyncLibTree = addLibTableEntry( fn.GetFullPath() ); + break; + + case SAVE_AS_HELPER::SAH_TYPE::ADD_PROJECT_TABLE_ENTRY: + resyncLibTree = addLibTableEntry( fn.GetFullPath(), PROJECT_LIB_TABLE ); + break; + + case SAVE_AS_HELPER::SAH_TYPE::NORMAL_SAVE_AS: + default: + break; + } + + if( resyncLibTree ) + { + FreezeSearchTree(); + SyncLibraries( true ); + ThawSearchTree(); + } + } ClearMsgPanel(); msg.Printf( _( "Symbol library file \"%s\" saved" ), fn.GetFullPath() );