/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /** * @file librairi.cpp * @brief Manage module (footprint) libraries. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // unique, "file local" translations: #define FMT_OK_DELETE _( "OK to delete footprint \"%s\" in library \"%s\"" ) #define FMT_IMPORT_MODULE _( "Import Footprint" ) #define FMT_FILE_NOT_FOUND _( "File \"%s\" not found" ) #define FMT_NOT_MODULE _( "Not a footprint file" ) #define FMT_MOD_NOT_FOUND _( "Unable to find or load footprint \"%s\" from lib path \"%s\"" ) #define FMT_LIB_READ_ONLY _( "Library \"%s\" is read only, not writable" ) #define FMT_EXPORT_MODULE _( "Export Footprint" ) #define FMT_SAVE_MODULE _( "Save Footprint" ) #define FMT_MOD_REF _( "Enter footprint name:" ) #define FMT_EXPORTED _( "Footprint exported to file \"%s\"" ) #define FMT_MOD_DELETED _( "Footprint \"%s\" deleted from library \"%s\"" ) #define FMT_MOD_CREATE _( "New Footprint" ) #define FMT_NO_REF_ABORTED _( "No footprint name defined." ) #define FMT_SELECT_LIB _( "Select Library" ) static const wxString INFO_LEGACY_LIB_WARN_EDIT( _( "Writing/modifying legacy libraries (.mod files) is not allowed\n"\ "Please save the current library to the new .pretty format\n"\ "and update your footprint lib table\n"\ "to save your footprint (a .kicad_mod file) in the .pretty library folder" ) ); static const wxString INFO_LEGACY_LIB_WARN_DELETE( _( "Modifying legacy libraries (.mod files) is not allowed\n"\ "Please save the current library under the new .pretty format\n"\ "and update your footprint lib table\n"\ "before deleting a footprint" ) ); #define EXPORT_IMPORT_LASTPATH_KEY wxT( "import_last_path" ) /** * Prompt the user for a module file to open. * @param aParent - parent window for the dialog * @param aLastPath - last opened path */ static wxFileName getFootprintFilenameFromUser( wxWindow* aParent, const wxString& aLastPath ) { static int lastFilterIndex = 0; // To store the last choice during a session. wxString wildCard; wildCard << KiCadFootprintLibFileWildcard() << wxChar( '|' ) << ModLegacyExportFileWildcard() << wxChar( '|' ) << GedaPcbFootprintLibFileWildcard(); wxFileDialog dlg( aParent, FMT_IMPORT_MODULE, aLastPath, wxEmptyString, wildCard, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); dlg.SetFilterIndex( lastFilterIndex ); if( dlg.ShowModal() == wxID_CANCEL ) return wxFileName(); lastFilterIndex = dlg.GetFilterIndex(); return wxFileName( dlg.GetPath() ); } /** * Read a file to detect the type. * @param aFile - open file to be read. File pointer will be closed. * @param aFileName - file name to be read * @param aName - wxString to receive the module name iff type is LEGACY */ static IO_MGR::PCB_FILE_T detect_file_type( FILE* aFile, const wxFileName& aFileName, wxString* aName ) { FILE_LINE_READER freader( aFile, aFileName.GetFullPath() ); WHITESPACE_FILTER_READER reader( freader ); IO_MGR::PCB_FILE_T file_type; wxASSERT( aName ); reader.ReadLine(); char* line = reader.Line(); if( !strncasecmp( line, "(module", strlen( "(module" ) ) ) { file_type = IO_MGR::KICAD_SEXP; *aName = aFileName.GetName(); } else if( !strncasecmp( line, FOOTPRINT_LIBRARY_HEADER, FOOTPRINT_LIBRARY_HEADER_CNT ) ) { file_type = IO_MGR::LEGACY; while( reader.ReadLine() ) { if( !strncasecmp( line, "$MODULE", strlen( "$MODULE" ) ) ) { *aName = FROM_UTF8( StrPurge( line + strlen( "$MODULE" ) ) ); break; } } } else if( !strncasecmp( line, "Element", strlen( "Element" ) ) ) { file_type = IO_MGR::GEDA_PCB; *aName = aFileName.GetName(); } else { file_type = IO_MGR::FILE_TYPE_NONE; } return file_type; } /** * Parse a footprint using a PLUGIN. * @param aFileName - file name to parse * @param aFileType - type of the file * @param aName - name of the footprint */ static MODULE* parse_module_with_plugin( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType, const wxString& aName ) { wxString path; switch( aFileType ) { case IO_MGR::GEDA_PCB: path = aFileName.GetPath(); break; case IO_MGR::LEGACY: path = aFileName.GetFullPath(); break; default: wxFAIL_MSG( wxT( "unexpected IO_MGR::PCB_FILE_T" ) ); } PLUGIN::RELEASER pi( IO_MGR::PluginFind( aFileType ) ); return pi->FootprintLoad( path, aName ); } /** * Parse a KICAD footprint. * @param aFileName - file name to parse */ static MODULE* parse_module_kicad( const wxFileName& aFileName ) { wxString fcontents; PCB_IO pcb_io; wxFFile f( aFileName.GetFullPath() ); if( !f.IsOpened() ) return NULL; f.ReadAll( &fcontents ); return dynamic_cast( pcb_io.Parse( fcontents ) ); } /** * Try to load a footprint, returning NULL if the file couldn't be accessed. * @param aFileName - file name to load * @param aFileType - type of the file to load * @param aName - footprint name */ MODULE* try_load_footprint( const wxFileName& aFileName, IO_MGR::PCB_FILE_T aFileType, const wxString& aName ) { MODULE* module; switch( aFileType ) { case IO_MGR::GEDA_PCB: case IO_MGR::LEGACY: module = parse_module_with_plugin( aFileName, aFileType, aName ); break; case IO_MGR::KICAD_SEXP: module = parse_module_kicad( aFileName ); break; default: wxFAIL_MSG( wxT( "unexpected IO_MGR::PCB_FILE_T" ) ); module = NULL; } return module; } MODULE* FOOTPRINT_EDIT_FRAME::Import_Module( const wxString& aName ) { wxString lastOpenedPathForLoading = m_mruPath; wxConfigBase* cfg = Kiface().KifaceSettings(); if( cfg ) cfg->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading ); wxFileName fn; if( aName != wxT("") ) fn = aName; else fn = getFootprintFilenameFromUser( this, lastOpenedPathForLoading ); if( !fn.IsOk() ) return NULL; FILE* fp = wxFopen( fn.GetFullPath(), wxT( "rt" ) ); if( !fp ) { wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( fn.GetFullPath() ) ); DisplayError( this, msg ); return NULL; } if( cfg ) // Save file path { lastOpenedPathForLoading = fn.GetPath(); cfg->Write( EXPORT_IMPORT_LASTPATH_KEY, lastOpenedPathForLoading ); } wxString moduleName; IO_MGR::PCB_FILE_T fileType = detect_file_type( fp, fn.GetFullPath(), &moduleName ); if( fileType == IO_MGR::FILE_TYPE_NONE ) { DisplayError( this, FMT_NOT_MODULE ); return NULL; } MODULE* module = NULL; try { module = try_load_footprint( fn, fileType, moduleName ); if( !module ) { wxString msg = wxString::Format( FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetFullPath() ) ); DisplayError( this, msg ); return NULL; } } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); // if the footprint is not loaded, exit. // However, even if an error happens, it can be loaded, because in KICAD and GPCB format, // a fp library is a set of separate files, and the error(s) are not necessary when // reading the selected file if( !module ) return NULL; } LIB_ID fpid; fpid.SetLibItemName( module->GetFPID().GetLibItemName() ); module->SetFPID( fpid ); // Insert footprint in list GetBoard()->Add( module ); // Display info : SetMsgPanel( module ); PlaceModule( module, NULL ); if( IsGalCanvasActive() ) module->SetPosition( wxPoint( 0, 0 ) ); GetBoard()->m_Status_Pcb = 0; GetBoard()->BuildListOfNets(); updateView(); return module; } void FOOTPRINT_EDIT_FRAME::Export_Module( MODULE* aModule ) { wxFileName fn; wxConfigBase* cfg = Kiface().KifaceSettings(); if( !aModule ) return; fn.SetName( aModule->GetFPID().GetLibItemName() ); wxString wildcard = KiCadFootprintLibFileWildcard(); fn.SetExt( KiCadFootprintFileExtension ); if( cfg ) { wxString path; cfg->Read( EXPORT_IMPORT_LASTPATH_KEY, &path, m_mruPath ); fn.SetPath( path ); } wxFileDialog dlg( this, FMT_EXPORT_MODULE, fn.GetPath(), fn.GetFullName(), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if( dlg.ShowModal() == wxID_CANCEL ) return; fn = dlg.GetPath(); if( cfg ) // Save file path { cfg->Write( EXPORT_IMPORT_LASTPATH_KEY, fn.GetPath() ); } try { // Export as *.kicad_pcb format, using a strategy which is specifically chosen // as an example on how it could also be used to send it to the system clipboard. PCB_IO pcb_io( CTL_FOR_LIBRARY ); /* This module should *already* be "normalized" in a way such that orientation is zero, etc., since it came from module editor. module->SetTimeStamp( 0 ); module->SetParent( 0 ); module->SetOrientation( 0 ); */ pcb_io.Format( aModule ); FILE* fp = wxFopen( dlg.GetPath(), wxT( "wt" ) ); if( fp == NULL ) { wxMessageBox( wxString::Format( _( "Unable to create or write file \"%s\"" ), GetChars( dlg.GetPath() ) ) ); return; } fprintf( fp, "%s", pcb_io.GetStringOutput( false ).c_str() ); fclose( fp ); } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return; } wxString msg = wxString::Format( FMT_EXPORTED, GetChars( dlg.GetPath() ) ); DisplayInfoMessage( this, msg ); } wxString PCB_BASE_EDIT_FRAME::CreateNewLibrary(const wxString& aLibName ) { // Kicad cannot write legacy format libraries, only .pretty new format // because the legacy format cannot handle current features. // The footprint library is actually a directory wxString initialPath = wxPathOnly( Prj().GetProjectFullName() ); wxFileName fn; bool saveInGlobalTable = false, saveInProjectTable = false; if( aLibName.IsEmpty() ) { fn = initialPath; if( !LibraryFileBrowser( false, fn, KiCadFootprintLibPathWildcard(), KiCadFootprintLibPathExtension ) ) return wxEmptyString; wxArrayString libTableNames; libTableNames.Add( _( "Global" ) ); libTableNames.Add( _( "Project" ) ); switch( SelectSingleOption( this, _( "Select Library Table" ), _( "Choose the Library Table to add the library to:" ), libTableNames ) ) { case 0: saveInGlobalTable = true; break; case 1: saveInProjectTable = true; break; default: return wxEmptyString; } } else { fn = aLibName; if( !fn.IsAbsolute() ) { fn.SetName( aLibName ); fn.MakeAbsolute( initialPath ); } // Enforce the .pretty extension: fn.SetExt( KiCadFootprintLibPathExtension ); } // We can save fp libs only using IO_MGR::KICAD_SEXP format (.pretty libraries) IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD_SEXP; wxString libPath = fn.GetFullPath(); try { PLUGIN::RELEASER pi( IO_MGR::PluginFind( piType ) ); bool writable = false; bool exists = false; try { writable = pi->IsFootprintLibWritable( libPath ); exists = true; // no exception was thrown, lib must exist. } catch( const IO_ERROR& ) { } if( exists ) { if( !writable ) { wxString msg = wxString::Format( FMT_LIB_READ_ONLY, libPath ); DisplayError( this, msg ); return wxEmptyString; } else { wxString msg = wxString::Format( _( "Library %s already exists." ), libPath ); KIDIALOG dlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); dlg.SetOKLabel( _( "Overwrite" ) ); dlg.DoNotShowCheckbox( __FILE__, __LINE__ ); if( dlg.ShowModal() == wxID_CANCEL ) return wxEmptyString; pi->FootprintLibDelete( libPath ); } } pi->FootprintLibCreate( libPath ); if( saveInGlobalTable ) { auto row = new FP_LIB_TABLE_ROW( fn.GetName(), libPath, wxT( "KiCad" ), wxEmptyString ); GFootprintTable.InsertRow( row ); GFootprintTable.Save( FP_LIB_TABLE::GetGlobalTableFileName() ); } else if( saveInProjectTable ) { auto row = new FP_LIB_TABLE_ROW( fn.GetName(), libPath, wxT( "KiCad" ), wxEmptyString ); Prj().PcbFootprintLibs()->InsertRow( row ); Prj().PcbFootprintLibs()->Save( Prj().FootprintLibTblName() ); } } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return wxEmptyString; } return libPath; } bool FOOTPRINT_EDIT_FRAME::DeleteModuleFromLibrary( MODULE* aModule ) { if( !aModule ) return false; LIB_ID fpid = aModule->GetFPID(); if( !fpid.IsValid() ) return false; wxString nickname = fpid.GetLibNickname(); wxString fpname = fpid.GetLibItemName(); // Legacy libraries are readable, but modifying legacy format is not allowed // So prompt the user if he try to delete a footprint from a legacy lib wxString libfullname = Prj().PcbFootprintLibs()->FindRow( nickname )->GetFullURI(); if( IO_MGR::GuessPluginTypeFromLibPath( libfullname ) == IO_MGR::LEGACY ) { DisplayInfoMessage( this, INFO_LEGACY_LIB_WARN_DELETE ); return false; } if( !Prj().PcbFootprintLibs()->IsFootprintLibWritable( nickname ) ) { wxString msg = wxString::Format( _( "Library \"%s\" is read only" ), nickname ); DisplayError( this, msg ); return false; } // Confirmation wxString msg = wxString::Format( FMT_OK_DELETE, fpname.GetData(), nickname.GetData() ); if( !IsOK( this, msg ) ) return false; try { Prj().PcbFootprintLibs()->FootprintDelete( nickname, fpname ); } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return false; } msg.Printf( FMT_MOD_DELETED, fpname.GetData(), nickname.GetData() ); SetStatusText( msg ); return true; } void PCB_EDIT_FRAME::ArchiveModulesOnBoard( bool aStoreInNewLib, const wxString& aLibName, wxString* aLibPath ) { if( GetBoard()->m_Modules == NULL ) { DisplayInfoMessage( this, _( "No footprints to archive!" ) ); return; } wxString footprintName; if( !aStoreInNewLib ) { // The footprints are saved in an existing .pretty library in the fp lib table PROJECT& prj = Prj(); wxString last_nickname = prj.GetRString( PROJECT::PCB_LIB_NICKNAME ); wxString nickname = SelectLibrary( last_nickname ); if( !nickname ) // Aborted return; prj.SetRString( PROJECT::PCB_LIB_NICKNAME, nickname ); try { FP_LIB_TABLE* tbl = prj.PcbFootprintLibs(); for( MODULE* curr_fp = GetBoard()->m_Modules; curr_fp; curr_fp = curr_fp->Next() ) { if( !curr_fp->GetFPID().GetLibItemName().empty() ) // Can happen with old boards. tbl->FootprintSave( nickname, curr_fp, false ); } } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); } } else { // The footprints are saved in a new .pretty library. // If this library already exists, all previous footprints will be deleted wxString libPath = CreateNewLibrary( aLibName ); if( libPath.IsEmpty() ) // Aborted return; if( aLibPath ) *aLibPath = libPath; IO_MGR::PCB_FILE_T piType = IO_MGR::KICAD_SEXP; PLUGIN::RELEASER pi( IO_MGR::PluginFind( piType ) ); for( MODULE* curr_fp = GetBoard()->m_Modules; curr_fp; curr_fp = curr_fp->Next() ) { try { if( !curr_fp->GetFPID().GetLibItemName().empty() ) // Can happen with old boards. pi->FootprintSave( libPath, curr_fp ); } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); } } } } class LIBRARY_NAME_CLEARER { MODULE* m_module; LIB_ID m_savedFPID; public: LIBRARY_NAME_CLEARER( MODULE* aModule ) { m_module = aModule; m_savedFPID = aModule->GetFPID(); m_module->SetFPID( LIB_ID( wxEmptyString, m_savedFPID.GetLibItemName() ) ); } ~LIBRARY_NAME_CLEARER() { m_module->SetFPID( m_savedFPID ); } }; bool FOOTPRINT_EDIT_FRAME::SaveFootprint( MODULE* aModule ) { wxString libraryName = aModule->GetFPID().GetLibNickname(); wxString footprintName = aModule->GetFPID().GetLibItemName(); if( aModule->GetLink() ) { return SaveFootprintToBoard( false ); } if( libraryName.IsEmpty() || footprintName.IsEmpty() ) return SaveFootprintAs( aModule ); FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs(); // Legacy libraries are readable, but modifying legacy format is not allowed // So prompt the user if he try to add/replace a footprint in a legacy lib wxString libfullname = tbl->FindRow( libraryName )->GetFullURI(); if( IO_MGR::GuessPluginTypeFromLibPath( libfullname ) == IO_MGR::LEGACY ) { DisplayInfoMessage( this, INFO_LEGACY_LIB_WARN_EDIT ); return false; } try { MODULE* m = tbl->FootprintLoad( libraryName, footprintName ); delete m; LIBRARY_NAME_CLEARER temp( aModule ); // this always overwrites any existing footprint, but should yell on its // own if the library or footprint is not writable. tbl->FootprintSave( libraryName, aModule ); } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return false; } return true; } bool FOOTPRINT_EDIT_FRAME::SaveFootprintToBoard( bool aAddNew ) { // update module in the current board, // not just add it to the board with total disregard for the netlist... PCB_EDIT_FRAME* pcbframe = (PCB_EDIT_FRAME*) Kiway().Player( FRAME_PCB, false ); if( pcbframe == NULL ) // happens when the board editor is not active (or closed) { DisplayErrorMessage( this, _("No board currently open." ) ); return false; } BOARD* mainpcb = pcbframe->GetBoard(); MODULE* source_module = NULL; MODULE* module_in_edit = GetBoard()->m_Modules; // Search the old module (source) if exists // Because this source could be deleted when editing the main board... if( module_in_edit->GetLink() ) // this is not a new module ... { source_module = mainpcb->m_Modules; for( ; source_module != NULL; source_module = source_module->Next() ) { if( module_in_edit->GetLink() == source_module->GetTimeStamp() ) break; } } if( !aAddNew && source_module == NULL ) // source not found { DisplayError( this, _( "Unable to find the footprint on the main board.\nCannot save." ) ); return false; } if( aAddNew && source_module != NULL ) { DisplayError( this, _( "Footprint already exists on board." ) ); return false; } m_toolManager->RunAction( PCB_ACTIONS::selectionClear, true ); pcbframe->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true ); BOARD_COMMIT commit( pcbframe ); // Create the "new" module MODULE* newmodule = new MODULE( *module_in_edit ); newmodule->SetParent( mainpcb ); newmodule->SetLink( 0 ); if( source_module ) // this is an update command { // In the main board, // the new module replace the old module (pos, orient, ref, value // and connexions are kept) // and the source_module (old module) is deleted pcbframe->Exchange_Module( source_module, newmodule, commit ); newmodule->SetTimeStamp( module_in_edit->GetLink() ); commit.Push( wxT( "Update module" ) ); } else // This is an insert command { wxPoint cursor_pos = pcbframe->GetCrossHairPosition(); commit.Add( newmodule ); pcbframe->SetCrossHairPosition( wxPoint( 0, 0 ) ); pcbframe->PlaceModule( newmodule, NULL ); newmodule->SetPosition( wxPoint( 0, 0 ) ); pcbframe->SetCrossHairPosition( cursor_pos ); newmodule->SetTimeStamp( GetNewTimeStamp() ); commit.Push( wxT( "Insert module" ) ); } newmodule->ClearFlags(); pcbframe->SetCurItem( NULL ); // @todo LEGACY should be unnecessary mainpcb->m_Status_Pcb = 0; return true; } bool FOOTPRINT_EDIT_FRAME::SaveFootprintAs( MODULE* aModule ) { if( aModule == NULL ) return false; FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs(); SetMsgPanel( aModule ); wxString libraryName = aModule->GetFPID().GetLibNickname(); wxString footprintName = aModule->GetFPID().GetLibItemName(); bool updateValue = ( aModule->GetValue() == footprintName ); wxArrayString headers; std::vector itemsToDisplay; std::vector nicknames = tbl->GetLogicalLibs(); headers.Add( _( "Nickname" ) ); headers.Add( _( "Description" ) ); for( unsigned i = 0; i < nicknames.size(); i++ ) { wxArrayString item; item.Add( nicknames[i] ); item.Add( tbl->GetDescription( nicknames[i] ) ); itemsToDisplay.push_back( item ); } EDA_LIST_DIALOG dlg( this, FMT_SAVE_MODULE, headers, itemsToDisplay, libraryName, nullptr, nullptr, /* sort */ false, /* show headers */ false ); dlg.SetListLabel( _( "Save in library:" ) ); dlg.SetOKLabel( _( "Save" ) ); wxBoxSizer* bNameSizer = new wxBoxSizer( wxHORIZONTAL ); wxStaticText* label = new wxStaticText( &dlg, wxID_ANY, _( "Name:" ), wxDefaultPosition, wxDefaultSize, 0 ); bNameSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 ); wxTextCtrl* nameTextCtrl = new wxTextCtrl( &dlg, wxID_ANY, footprintName, wxDefaultPosition, wxDefaultSize, 0 ); bNameSizer->Add( nameTextCtrl, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); wxTextValidator nameValidator( wxFILTER_EXCLUDE_CHAR_LIST ); nameValidator.SetCharExcludes( MODULE::StringLibNameInvalidChars( false ) ); nameTextCtrl->SetValidator( nameValidator ); wxSizer* mainSizer = dlg.GetSizer(); mainSizer->Prepend( bNameSizer, 0, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5 ); // Move nameTextCtrl to the head of the tab-order if( dlg.GetChildren().DeleteObject( nameTextCtrl ) ) dlg.GetChildren().Insert( nameTextCtrl ); dlg.SetInitialFocus( nameTextCtrl ); dlg.Layout(); mainSizer->Fit( &dlg ); if( dlg.ShowModal() != wxID_OK ) return false; // canceled by user libraryName = dlg.GetTextSelection(); if( libraryName.IsEmpty() ) { DisplayError( NULL, _( "No library specified. Footprint could not be saved." ) ); return false; } footprintName = nameTextCtrl->GetValue(); footprintName.Trim( true ); footprintName.Trim( false ); if( footprintName.IsEmpty() ) { DisplayError( NULL, _( "No footprint name specified. Footprint could not be saved." ) ); return false; } aModule->SetFPID( LIB_ID( libraryName, footprintName ) ); if( updateValue ) aModule->SetValue( footprintName ); // Legacy libraries are readable, but modifying legacy format is not allowed // So prompt the user if he try to add/replace a footprint in a legacy lib wxString libfullname = Prj().PcbFootprintLibs()->FindRow( libraryName )->GetFullURI(); IO_MGR::PCB_FILE_T piType = IO_MGR::GuessPluginTypeFromLibPath( libfullname ); if( piType == IO_MGR::LEGACY ) { DisplayInfoMessage( this, INFO_LEGACY_LIB_WARN_EDIT ); return false; } bool module_exists = false; try { MODULE* m = tbl->FootprintLoad( libraryName, footprintName ); module_exists = m != nullptr; delete m; LIBRARY_NAME_CLEARER temp( aModule ); // this always overwrites any existing footprint, but should yell on its // own if the library or footprint is not writable. tbl->FootprintSave( libraryName, aModule ); } catch( const IO_ERROR& ioe ) { DisplayError( this, ioe.What() ); return false; } wxString fmt = module_exists ? _( "Component \"%s\" replaced in \"%s\"" ) : _( "Component \"%s\" added in \"%s\"" ); wxString msg = wxString::Format( fmt, footprintName.GetData(), libraryName.GetData() ); SetStatusText( msg ); return true; } MODULE* PCB_BASE_FRAME::CreateNewModule( const wxString& aModuleName ) { // Creates a new footprint at position 0,0 which contains the minimal items: // the reference and the value. // Value : initialized to the footprint name. // put on fab layer (front side) // Reference : initialized to a default value (REF**). // put on silkscreen layer (front side) wxString moduleName = aModuleName; // Ask for the new module name if( moduleName.IsEmpty() ) { WX_TEXT_ENTRY_DIALOG dlg( this, FMT_MOD_REF, FMT_MOD_CREATE, moduleName ); dlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &moduleName ) ); if( dlg.ShowModal() != wxID_OK ) return NULL; //Aborted by user } moduleName.Trim( true ); moduleName.Trim( false ); if( moduleName.IsEmpty() ) { DisplayInfoMessage( this, FMT_NO_REF_ABORTED ); return NULL; } // Creates the new module and add it to the head of the linked list of modules MODULE* module = new MODULE( GetBoard() ); // Update parameters: timestamp ... module->SetLastEditTime(); // Update its name in lib module->SetFPID( LIB_ID( wxEmptyString, moduleName ) ); wxPoint default_pos; BOARD_DESIGN_SETTINGS& settings = GetDesignSettings(); // Update reference: if( settings.m_RefDefaultText.IsEmpty() ) module->SetReference( moduleName ); else module->SetReference( settings.m_RefDefaultText ); PCB_LAYER_ID layer = ToLAYER_ID( settings.m_RefDefaultlayer ); module->Reference().SetThickness( settings.GetTextThickness( layer ) ); module->Reference().SetTextSize( settings.GetTextSize( layer ) ); module->Reference().SetItalic( settings.GetTextItalic( layer ) ); module->Reference().SetKeepUpright( settings.GetTextUpright( layer ) ); default_pos.y = GetDesignSettings().GetTextSize( layer ).y / 2; module->Reference().SetPosition( default_pos ); module->Reference().SetLayer( layer ); module->Reference().SetVisible( settings.m_RefDefaultVisibility ); // Set the value field to a default value if( settings.m_ValueDefaultText.IsEmpty() ) module->SetValue( moduleName ); else module->SetValue( settings.m_ValueDefaultText ); layer = ToLAYER_ID( settings.m_ValueDefaultlayer ); module->Value().SetThickness( settings.GetTextThickness( layer ) ); module->Value().SetTextSize( settings.GetTextSize( layer ) ); module->Value().SetItalic( settings.GetTextItalic( layer ) ); module->Value().SetKeepUpright( settings.GetTextUpright( layer ) ); default_pos.y = -default_pos.y; module->Value().SetPosition( default_pos ); module->Value().SetLayer( layer ); module->Value().SetVisible( settings.m_ValueDefaultVisibility ); SetMsgPanel( module ); return module; } wxString PCB_BASE_FRAME::SelectLibrary( const wxString& aNicknameExisting ) { wxArrayString headers; headers.Add( _( "Nickname" ) ); headers.Add( _( "Description" ) ); FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs(); std::vector< wxArrayString > itemsToDisplay; std::vector< wxString > nicknames = fptbl->GetLogicalLibs(); for( unsigned i = 0; i < nicknames.size(); i++ ) { wxArrayString item; item.Add( nicknames[i] ); item.Add( fptbl->GetDescription( nicknames[i] ) ); itemsToDisplay.push_back( item ); } EDA_LIST_DIALOG dlg( this, FMT_SELECT_LIB, headers, itemsToDisplay, aNicknameExisting ); if( dlg.ShowModal() != wxID_OK ) return wxEmptyString; wxString nickname = dlg.GetTextSelection(); wxLogDebug( wxT( "Chose footprint library \"%s\"." ), GetChars( nickname ) ); return nickname; }