KiCad PCB EDA Suite
Loading...
Searching...
No Matches
remote_symbol_import_utils.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <common.h>
23#include <eeschema_settings.h>
24#include <io/io_mgr.h>
25#include <lib_symbol.h>
28#include <pgm_base.h>
30#include <sch_edit_frame.h>
31#include <sch_io/sch_io.h>
32#include <sch_io/sch_io_mgr.h>
33#include <sch_symbol.h>
35#include <tool/tool_manager.h>
36#include <tools/sch_actions.h>
37
38#include <wx/ffile.h>
39#include <wx/filefn.h>
40#include <wx/intl.h>
41
42
43wxString SanitizeRemoteFileComponent( const wxString& aValue, const wxString& aDefault, bool aLower )
44{
45 wxString result = aValue;
46 result.Trim( true ).Trim( false );
47
48 if( result.IsEmpty() )
49 result = aDefault;
50
51 for( size_t i = 0; i < result.length(); ++i )
52 {
53 const wxUniChar ch = result[i];
54
55 if( !( wxIsalnum( ch ) || ch == '_' || ch == '-' || ch == '.' ) )
56 result[i] = '_';
57 }
58
59 return aLower ? result.Lower() : result;
60}
61
62
64{
66 wxString prefix = settings ? settings->m_RemoteSymbol.library_prefix : wxString();
67
68 if( prefix.IsEmpty() )
70
71 return SanitizeRemoteFileComponent( prefix, wxS( "remote" ), true );
72}
73
74
75bool WriteRemoteBinaryFile( const wxFileName& aOutput, const std::vector<uint8_t>& aPayload, wxString& aError )
76{
77 if( aPayload.empty() )
78 {
79 aError = _( "Payload was empty." );
80 return false;
81 }
82
83 wxFileName targetDir = aOutput;
84 targetDir.SetFullName( wxEmptyString );
85
86 if( !targetDir.DirExists() && !targetDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
87 {
88 aError = wxString::Format( _( "Unable to create '%s'." ), targetDir.GetFullPath() );
89 return false;
90 }
91
92 wxFFile file( aOutput.GetFullPath(), wxS( "wb" ) );
93
94 if( !file.IsOpened() )
95 {
96 aError = wxString::Format( _( "Unable to open '%s' for writing." ), aOutput.GetFullPath() );
97 return false;
98 }
99
100 if( file.Write( aPayload.data(), aPayload.size() ) != aPayload.size() )
101 {
102 aError = wxString::Format( _( "Failed to write '%s'." ), aOutput.GetFullPath() );
103 return false;
104 }
105
106 file.Close();
107 return true;
108}
109
110
111bool EnsureRemoteDestinationRoot( wxFileName& aOutDir, wxString& aError )
112{
114
115 if( !settings )
116 {
117 aError = _( "Unable to load schematic settings." );
118 return false;
119 }
120
121 wxString destination = settings->m_RemoteSymbol.destination_dir;
122
123 if( destination.IsEmpty() )
125
126 destination = ExpandEnvVarSubstitutions( destination, &Pgm().GetSettingsManager().Prj() );
127 destination.Trim( true ).Trim( false );
128
129 if( destination.IsEmpty() )
130 {
131 aError = _( "Destination directory is not configured." );
132 return false;
133 }
134
135 wxFileName dir = wxFileName::DirName( destination );
136 dir.Normalize( FN_NORMALIZE_FLAGS );
137
138 if( !dir.DirExists() && !dir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
139 {
140 aError = wxString::Format( _( "Unable to create directory '%s'." ), dir.GetFullPath() );
141 return false;
142 }
143
144 aOutDir = dir;
145 return true;
146}
147
148
149bool EnsureRemoteLibraryEntry( LIBRARY_TABLE_TYPE aTableType, const wxFileName& aLibraryPath,
150 const wxString& aNickname, bool aGlobalTable, bool aStrict, wxString& aError )
151{
153 auto tableOpt = manager.Table( aTableType, aGlobalTable ? LIBRARY_TABLE_SCOPE::GLOBAL
155
156 if( !tableOpt )
157 {
158 if( aStrict )
159 aError = _( "Unable to access the library table." );
160
161 return !aStrict;
162 }
163
164 LIBRARY_TABLE* table = *tableOpt;
165 const wxString fullPath = aLibraryPath.GetFullPath();
166
167 if( table->HasRow( aNickname ) )
168 {
169 if( std::optional<LIBRARY_TABLE_ROW*> rowOpt = table->Row( aNickname ); rowOpt )
170 {
171 LIBRARY_TABLE_ROW* row = *rowOpt;
172
173 if( row->URI() != fullPath )
174 {
175 row->SetURI( fullPath );
176
177 if( !table->Save() )
178 {
179 aError = _( "Failed to update the library table." );
180 return false;
181 }
182 }
183 }
184
185 return true;
186 }
187
188 LIBRARY_TABLE_ROW& row = table->InsertRow();
189 row.SetNickname( aNickname );
190 row.SetURI( fullPath );
191 row.SetType( wxS( "KiCad" ) );
192 row.SetOptions( wxString() );
193 row.SetDescription( _( "Remote download" ) );
194 row.SetOk( true );
195
196 if( !table->Save() )
197 {
198 aError = _( "Failed to save the library table." );
199 return false;
200 }
201
202 return true;
203}
204
205
206bool SaveRemoteSymbolToLibrary( SYMBOL_LIBRARY_ADAPTER& aAdapter, const wxFileName& aLibraryFile,
207 const wxString& aNickname, bool aGlobalTable,
208 std::unique_ptr<LIB_SYMBOL> aSymbol, wxString& aError )
209{
210 if( !EnsureRemoteLibraryEntry( LIBRARY_TABLE_TYPE::SYMBOL, aLibraryFile, aNickname, aGlobalTable, true,
211 aError ) )
212 {
213 return false;
214 }
215
216 // The sexpr plugin refuses to save into a library file that does not exist yet
217 if( !aLibraryFile.FileExists() && !aAdapter.CreateLibrary( aNickname ) )
218 {
219 aError = wxString::Format( _( "Unable to create '%s'." ), aLibraryFile.GetFullPath() );
220 return false;
221 }
222
223 if( aAdapter.SaveSymbol( aNickname, std::move( aSymbol ), true ) != SYMBOL_LIBRARY_ADAPTER::SAVE_OK )
224 {
225 aError = _( "Unable to save the downloaded symbol." );
226 return false;
227 }
228
230
232
233 return true;
234}
235
236
237bool PlaceRemoteDownloadedSymbol( SCH_EDIT_FRAME* aFrame, const wxString& aNickname, const wxString& aLibItemName,
238 wxString& aError )
239{
240 if( !aFrame )
241 {
242 aError = _( "No schematic editor is available for placement." );
243 return false;
244 }
245
246 LIB_ID libId;
247 libId.SetLibNickname( aNickname );
248 libId.SetLibItemName( aLibItemName );
249
250 LIB_SYMBOL* libSymbol = aFrame->GetLibSymbol( libId );
251
252 if( !libSymbol )
253 {
254 aError = _( "Unable to load the downloaded symbol for placement." );
255 return false;
256 }
257
258 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &aFrame->GetCurrentSheet(), 1 );
259 symbol->SetParent( aFrame->GetScreen() );
260
261 if( EESCHEMA_SETTINGS* cfg = aFrame->eeconfig(); cfg && cfg->m_AutoplaceFields.enable )
262 symbol->AutoplaceFields( nullptr, AUTOPLACE_AUTO );
263
264 TOOL_MANAGER* toolMgr = aFrame->GetToolManager();
265
266 if( !toolMgr )
267 {
268 delete symbol;
269 aError = _( "Unable to access the schematic placement tools." );
270 return false;
271 }
272
273 aFrame->Raise();
275 true,
276 symbol->IsMultiUnit() } );
277 return true;
278}
279
280
281std::unique_ptr<LIB_SYMBOL> LoadRemoteSymbolFromPayload( const std::vector<uint8_t>& aPayload,
282 const wxString& aLibItemName, wxString& aError )
283{
284 if( aPayload.empty() )
285 {
286 aError = _( "Symbol payload was empty." );
287 return nullptr;
288 }
289
290 wxString tempPath = wxFileName::CreateTempFileName( wxS( "remote_symbol" ) );
291
292 if( tempPath.IsEmpty() )
293 {
294 aError = _( "Unable to create a temporary file for the symbol payload." );
295 return nullptr;
296 }
297
298 wxFileName tempFile( tempPath );
299 wxFFile file( tempFile.GetFullPath(), wxS( "wb" ) );
300
301 if( !file.IsOpened() )
302 {
303 aError = _( "Unable to create a temporary file for the symbol payload." );
304 wxRemoveFile( tempFile.GetFullPath() );
305 return nullptr;
306 }
307
308 if( file.Write( aPayload.data(), aPayload.size() ) != aPayload.size() )
309 {
310 aError = _( "Failed to write the temporary symbol payload." );
311 file.Close();
312 wxRemoveFile( tempFile.GetFullPath() );
313 return nullptr;
314 }
315
316 file.Close();
317
318 IO_RELEASER<SCH_IO> plugin( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
319
320 if( !plugin )
321 {
322 aError = _( "Unable to access the KiCad symbol plugin." );
323 wxRemoveFile( tempFile.GetFullPath() );
324 return nullptr;
325 }
326
327 std::unique_ptr<LIB_SYMBOL> symbol;
328
329 try
330 {
331 LIB_SYMBOL* loaded = plugin->LoadSymbol( tempFile.GetFullPath(), aLibItemName );
332
333 if( loaded )
334 symbol = std::make_unique<LIB_SYMBOL>( *loaded );
335 else
336 aError = _( "Symbol payload did not include the expected symbol." );
337 }
338 catch( const IO_ERROR& e )
339 {
340 aError = wxString::Format( _( "Unable to decode the symbol payload: %s" ), e.What() );
341 }
342
343 wxRemoveFile( tempFile.GetFullPath() );
344 return symbol;
345}
346
347
348LIB_ID BuildRemoteLibId( const wxString& aResolvedLibrary, const wxString& aResolvedItemName )
349{
350 wxString nickname = RemoteLibraryPrefix()
351 + wxS( "_" )
352 + SanitizeRemoteFileComponent( aResolvedLibrary, wxS( "footprints" ), true );
353
354 if( nickname.EndsWith( wxT( ".pretty" ) ) )
355 nickname = nickname.BeforeLast( '.' );
356
357 wxString itemName = SanitizeRemoteFileComponent( aResolvedItemName, wxS( "footprint" ) );
358
359 if( itemName.EndsWith( wxT( ".kicad_mod" ) ) )
360 itemName = itemName.BeforeLast( '.' );
361
362 LIB_ID id;
363 id.SetLibNickname( nickname );
364 id.SetLibItemName( itemName );
365 return id;
366}
367
368
369void ApplyFootprintLinks( LIB_SYMBOL& aSymbol, const std::vector<LIB_ID>& aLinks )
370{
371 if( aLinks.empty() )
372 return;
373
374 aSymbol.SetFootprintProp( aLinks.front().GetUniStringLibId() );
375
376 if( aLinks.size() == 1 )
377 return;
378
379 wxArrayString filters = aSymbol.GetFPFilters();
380
381 for( size_t i = 1; i < aLinks.size(); ++i )
382 filters.Add( aLinks[i].GetUniStringLibItemName() );
383
384 aSymbol.SetFPFilters( filters );
385}
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
REMOTE_PROVIDER_SETTINGS m_RemoteSymbol
AUTOPLACE_FIELDS m_AutoplaceFields
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
bool CreateLibrary(const wxString &aNickname)
Creates the library (i.e. saves to disk) for the given row if it exists.
void ReloadLibraryEntry(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH)
Synchronously recreates the plugin for and reloads the given library.
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 SetOptions(const wxString &aOptions)
void SetNickname(const wxString &aNickname)
void SetOk(bool aOk=true)
void SetType(const wxString &aType)
void SetDescription(const wxString &aDescription)
void SetURI(const wxString &aUri)
const wxString & URI() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:124
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
Define a library symbol object.
Definition lib_symbol.h:114
wxArrayString GetFPFilters() const
Definition lib_symbol.h:252
void SetFootprintProp(const wxString &aFootprint)
Definition lib_symbol.h:509
void SetFPFilters(const wxArrayString &aFilters)
Definition lib_symbol.h:250
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
static TOOL_ACTION placeSymbol
Definition sch_actions.h:72
EESCHEMA_SETTINGS * eeconfig() const
LIB_SYMBOL * GetLibSymbol(const LIB_ID &aLibId, bool aUseCacheLib=false, bool aShowErrorMsg=false)
Load symbol from symbol library table.
Schematic editor (Eeschema) main window.
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_SHEET_PATH & GetCurrentSheet() const
Schematic symbol object.
Definition sch_symbol.h:75
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
bool IsMultiUnit() const override
Definition sch_symbol.h:257
An interface to the global shared library manager that is schematic-specific and linked to one projec...
SAVE_T SaveSymbol(const wxString &aNickname, std::unique_ptr< LIB_SYMBOL > aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Master controller class:
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject, bool aAllowEscapes)
Replace any environment variable & text variable references with their values.
Definition common.cpp:777
#define _(s)
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:737
LIBRARY_TABLE_TYPE
LIBRARY_TABLE_SCOPE
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
static const std::vector< std::string > filters
Definition plugin.cpp:64
void ApplyFootprintLinks(LIB_SYMBOL &aSymbol, const std::vector< LIB_ID > &aLinks)
Apply a list of footprint LIB_IDs to a symbol about to be saved.
std::unique_ptr< LIB_SYMBOL > LoadRemoteSymbolFromPayload(const std::vector< uint8_t > &aPayload, const wxString &aLibItemName, wxString &aError)
Deserialize a remote-downloaded symbol payload into a LIB_SYMBOL.
LIB_ID BuildRemoteLibId(const wxString &aResolvedLibrary, const wxString &aResolvedItemName)
Build the local LIB_ID for a remote-downloaded library item.
bool PlaceRemoteDownloadedSymbol(SCH_EDIT_FRAME *aFrame, const wxString &aNickname, const wxString &aLibItemName, wxString &aError)
Place a symbol from a remote download into the schematic editor.
bool EnsureRemoteLibraryEntry(LIBRARY_TABLE_TYPE aTableType, const wxFileName &aLibraryPath, const wxString &aNickname, bool aGlobalTable, bool aStrict, wxString &aError)
Add or update a library table entry for a remote download library.
bool WriteRemoteBinaryFile(const wxFileName &aOutput, const std::vector< uint8_t > &aPayload, wxString &aError)
Write binary data to a file, creating parent directories as needed.
bool SaveRemoteSymbolToLibrary(SYMBOL_LIBRARY_ADAPTER &aAdapter, const wxFileName &aLibraryFile, const wxString &aNickname, bool aGlobalTable, std::unique_ptr< LIB_SYMBOL > aSymbol, wxString &aError)
Save a downloaded symbol into the remote symbol library aNickname backed by aLibraryFile.
bool EnsureRemoteDestinationRoot(wxFileName &aOutDir, wxString &aError)
Resolve and create the configured destination root directory for remote symbol downloads.
wxString RemoteLibraryPrefix()
Return the configured (or default) library prefix for remote downloads, sanitized for use as a filena...
wxString SanitizeRemoteFileComponent(const wxString &aValue, const wxString &aDefault, bool aLower)
Replace non-alphanumeric characters (other than _ - .) with underscores.
@ AUTOPLACE_AUTO
Definition sch_item.h:75
T * GetAppSettings(const char *aFilename)
wxString result
Test unit parsing edge cases and error handling.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:35