KiCad PCB EDA Suite
Loading...
Searching...
No Matches
design_block_library_adapter.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 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include "design_block.h"
23
25#include <design_block_io.h>
26#include <env_vars.h>
27#include <ki_exception.h>
28#include <trace_helpers.h>
29
30#include <magic_enum.hpp>
31#include <wx/log.h>
32
33
35
37
38
43
44
49
50
52{
53 return ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) );
54}
55
56
58{
59 DESIGN_BLOCK_IO* ret = dynamic_cast<DESIGN_BLOCK_IO*>( aRow->plugin.get() );
60 wxCHECK( aRow->plugin && ret, nullptr );
61 return ret;
62}
63
64
66{
67 // TODO(JE) do we maintain a precondition that URIs are absolute paths after expansion?
69
71 {
72 wxString msg;
73 wxFileName fileName( m_manager.GetFullURI( row, true ) );
74
75 if( fileName.FileExists() )
76 return tl::unexpected( LIBRARY_TABLE_OK() );
77 else
78 msg = wxString::Format( _( "Nested table '%s' not found." ), row->URI() );
79
80 return tl::unexpected( LIBRARY_ERROR( msg ) );
81 }
83 {
84 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
85 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
86 return tl::unexpected( LIBRARY_ERROR( msg ) );
87 }
88
90 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
91
92 return plugin;
93}
94
95
97{
98 return dbplugin( aRow );
99}
100
101
103{
104 wxArrayString dummyList;
105 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
106 dbplugin( aLib )->DesignBlockEnumerate( dummyList, aUri, false, &options );
107}
108
109
110std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
111{
113
114 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
115
116 try
117 {
118 wxArrayString dummyList;
119 dbplugin( aLib )->DesignBlockEnumerate( dummyList, getUri( aLib->row ), false, &options );
120 wxLogTrace( traceLibraries, "DB: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
122 }
123 catch( IO_ERROR& e )
124 {
126 aLib->status.error = LIBRARY_ERROR( e.Problem(), e.Where() );
127 wxLogTrace( traceLibraries, "DB: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
128 }
129
130 return aLib->status;
131}
132
133
134std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
135{
137
138 if( result.has_value() )
139 return LoadOne( *result );
140
141 return LIB_STATUS{
142 .load_status = LOAD_STATUS::LOAD_ERROR,
143 .error = LIBRARY_ERROR( { result.error() } )
144 };
145}
146
147
148std::vector<DESIGN_BLOCK*> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlocks( const wxString& aNickname )
149{
150 std::vector<DESIGN_BLOCK*> blocks;
151
152 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
153
154 if( !maybeLib )
155 return blocks;
156
157 const LIB_DATA* lib = *maybeLib;
158 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
159 wxArrayString blockNames;
160
161 try
162 {
163 dbplugin( lib )->DesignBlockEnumerate( blockNames, getUri( lib->row ), false, &options );
164 }
165 catch( IO_ERROR& e )
166 {
167 wxLogTrace( traceLibraries, "DB: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
168 }
169
170 for( const wxString& blockName : blockNames )
171 {
172 try
173 {
174 blocks.emplace_back( dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), blockName, false, &options ) );
175 }
176 catch( IO_ERROR& e )
177 {
178 wxLogTrace( traceLibraries, "DB: Exception enumerating design block %s: %s", blockName, e.What() );
179 }
180 }
181
182 return blocks;
183}
184
185
186std::vector<wxString> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlockNames( const wxString& aNickname )
187{
188 // TODO(JE) can we kill wxArrayString in internal API?
189 wxArrayString namesAS;
190 std::vector<wxString> names;
191
192 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
193 {
194 const LIB_DATA* lib = *maybeLib;
195 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
196
197 dbplugin( lib )->DesignBlockEnumerate( namesAS, getUri( lib->row ), true, &options );
198 }
199
200 for( const wxString& name : namesAS )
201 names.emplace_back( name );
202
203 return names;
204}
205
206
207static wxString describeMissingLibrary( const wxString& aNickname, const std::vector<LIBRARY_TABLE_ROW*>& aRows )
208{
209 wxString message = wxString::Format( _( "Design block library '%s' is not in the design block "
210 "library table." ),
211 aNickname );
212
213 // If one name sits inside the other, it can be the same library after a rename.
214 if( !aNickname.IsEmpty() )
215 {
216 const wxString wanted = aNickname.Lower();
217
218 for( const LIBRARY_TABLE_ROW* row : aRows )
219 {
220 const wxString candidate = row->Nickname().Lower();
221
222 if( candidate.IsEmpty() || ( !candidate.Contains( wanted ) && !wanted.Contains( candidate ) ) )
223 continue;
224
225 message += wxString::Format( _( " Did you mean '%s'?" ), row->Nickname() );
226 break;
227 }
228 }
229
230 if( aRows.empty() )
231 return message + wxS( "\n\n" ) + _( "No design block libraries are configured." );
232
233 message += wxS( "\n\n" ) + _( "Configured design block libraries:" );
234
235 const size_t maxListed = 20;
236
237 for( size_t ii = 0; ii < aRows.size(); ii++ )
238 {
239 if( ii == maxListed )
240 {
241 message += wxT( "\n" ) + wxString::Format( _( "and %d more" ), (int) ( aRows.size() - maxListed ) );
242 break;
243 }
244
245 message +=
246 wxString::Format( wxT( "\n %s (%s)" ), aRows[ii]->Nickname(),
247 aRows[ii]->Scope() == LIBRARY_TABLE_SCOPE::PROJECT ? _( "project" ) : _( "global" ) );
248 }
249
250 return message;
251}
252
253
254wxString DESIGN_BLOCK_LIBRARY_ADAPTER::libraryUnavailableMessage( const wxString& aNickname ) const
255{
256 std::optional<LIBRARY_TABLE_ROW*> row = GetRow( aNickname );
257
258 if( !row )
259 return describeMissingLibrary( aNickname, Rows() );
260
261 if( ( *row )->Disabled() )
262 return wxString::Format( _( "Design block library '%s' is disabled in the library table." ), aNickname );
263
264 std::optional<LIB_STATUS> status = GetLibraryStatus( aNickname );
265
266 if( status && status->load_status == LOAD_STATUS::LOAD_ERROR )
267 {
268 return wxString::Format( _( "Design block library '%s' could not be opened." ) + wxS( "\n%s" ), aNickname,
269 status->error ? status->error->message : wxString() );
270 }
271
272 return wxString::Format( _( "Design block library '%s' is still loading." ), aNickname );
273}
274
275
277 const wxString& aDesignBlockName, bool aKeepUUID,
278 wxString* aErrorMsg )
279{
280 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
281 {
282 const LIB_DATA* lib = *maybeLib;
283 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
284
285 try
286 {
287 if( DESIGN_BLOCK* db =
288 dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), aDesignBlockName, aKeepUUID, &options ) )
289 {
290 db->GetLibId().SetLibNickname( aNickname );
291 return db;
292 }
293 }
294 catch( const IO_ERROR& ioe )
295 {
296 wxLogTrace( traceLibraries, "LoadDesignBlock: error loading %s:%s: %s", aNickname, aDesignBlockName,
297 ioe.What() );
298
299 if( aErrorMsg )
300 *aErrorMsg = ioe.What();
301 }
302 }
303 else
304 {
305 wxLogTrace( traceLibraries, "LoadDesignBlock: requested library %s not loaded", aNickname );
306
307 if( aErrorMsg )
308 *aErrorMsg = libraryUnavailableMessage( aNickname );
309 }
310
311 return nullptr;
312}
313
314
315bool DESIGN_BLOCK_LIBRARY_ADAPTER::DesignBlockExists( const wxString& aNickname, const wxString& aDesignBlockName )
316{
317 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
318 {
319 const LIB_DATA* lib = *maybeLib;
320 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
321
322 return dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlockName, &options );
323 }
324
325 return false;
326}
327
328
330 const wxString& aDesignBlockName )
331{
332 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
333 {
334 const LIB_DATA* lib = *maybeLib;
335 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
336
337 return dbplugin( lib )->GetEnumeratedDesignBlock( getUri( lib->row ), aDesignBlockName, &options );
338 }
339
340 return nullptr;
341}
342
343
345 const DESIGN_BLOCK* aDesignBlock,
346 bool aOverwrite )
347{
348 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
349 {
350 const LIB_DATA* lib = *maybeLib;
351 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
352
353 if( !aOverwrite && dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlock->GetName(), &options ) )
354 return SAVE_SKIPPED;
355
356 dbplugin( lib )->DesignBlockSave( getUri( lib->row ), aDesignBlock, &options );
357 }
358
359 return SAVE_OK;
360}
361
362
364 const wxString& aDesignBlockName )
365{
366 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
367 {
368 const LIB_DATA* lib = *maybeLib;
369 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
370 return dbplugin( lib )->DesignBlockDelete( getUri( lib->row ), aDesignBlockName, &options );
371 }
372}
373
374
376{
377 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
378 {
379 const LIB_DATA* lib = *maybeLib;
380 return plugin( lib )->IsLibraryWritable( getUri( lib->row ) );
381 }
382
383 return false;
384}
385
386
388 bool aKeepUUID, wxString* aErrorMsg )
389{
390 wxString nickname = aDesignBlockId.GetLibNickname();
391 wxString DesignBlockname = aDesignBlockId.GetLibItemName();
392
393 if( nickname.size() )
394 return LoadDesignBlock( nickname, DesignBlockname, aKeepUUID, aErrorMsg );
395
396 // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
397 for( const wxString& library : GetLibraryNames() )
398 {
399 // DesignBlockLoad() returns NULL on not found, does not throw exception
400 // unless there's an IO_ERROR.
401 if( DESIGN_BLOCK* ret = LoadDesignBlock( library, DesignBlockname, aKeepUUID ) )
402 return ret;
403 }
404
405 if( aErrorMsg )
406 *aErrorMsg = wxString::Format( _( "Design block '%s' not found in any library." ), DesignBlockname );
407
408 return nullptr;
409}
const char * name
@ DESIGN_BLOCK_FILE_UNKNOWN
0 is not a legal menu id on Mac
static DESIGN_BLOCK_FILE_T EnumFromStr(const wxString &aFileType)
static DESIGN_BLOCK_IO * FindPlugin(DESIGN_BLOCK_FILE_T aFileType)
bool DesignBlockExists(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockDelete(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockSave(const wxString &aLibraryPath, const DESIGN_BLOCK *aDesignBlock, const std::map< std::string, UTF8 > *aProperties=nullptr)
const DESIGN_BLOCK * GetEnumeratedDesignBlock(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockEnumerate(wxArrayString &aDesignBlockNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr)
std::vector< wxString > GetDesignBlockNames(const wxString &aNickname)
wxString libraryUnavailableMessage(const wxString &aNickname) const
SAVE_T
The set of return values from DesignBlockSave() below.
std::vector< DESIGN_BLOCK * > GetDesignBlocks(const wxString &aNickname)
IO_BASE * plugin(const LIB_DATA *aRow) override
static LEAK_AT_EXIT< std::shared_mutex > GlobalLibraryMutex
static DESIGN_BLOCK_IO * dbplugin(const LIB_DATA *aRow)
Helper to cast the ABC plugin in the LIB_DATA* to a concrete plugin.
void DeleteDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName)
Delete the aDesignBlockName from the library given by aNickname.
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
static LEAK_AT_EXIT< std::map< wxString, LIB_DATA > > GlobalLibraries
SAVE_T SaveDesignBlock(const wxString &aNickname, const DESIGN_BLOCK *aDesignBlock, bool aOverwrite=true)
Write aDesignBlock to an existing library given by aNickname.
DESIGN_BLOCK * DesignBlockLoadWithOptionalNickname(const LIB_ID &aDesignBlockId, bool aKeepUUID=false, wxString *aErrorMsg=nullptr)
Load a design block having aDesignBlockId with possibly an empty nickname.
DESIGN_BLOCK_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
bool IsDesignBlockLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
const DESIGN_BLOCK * GetEnumeratedDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName)
A version of #DesignBlockLoad() for use after #DesignBlockEnumerate() for more efficient cache manage...
DESIGN_BLOCK * LoadDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName, bool aKeepUUID=false, wxString *aErrorMsg=nullptr)
Load a design block having aDesignBlockName from the library given by aNickname.
bool DesignBlockExists(const wxString &aNickname, const wxString &aDesignBlockName)
Indicates whether or not the given design block already exists in the given library.
void enumerateLibrary(LIB_DATA *aLib, const wxString &aUri) override
Override in derived class to perform library-specific enumeration.
wxString GetName() const override
virtual bool IsLibraryWritable(const wxString &aLibraryPath)
Return true if the library at aLibraryPath is writable.
Definition io_base.cpp:60
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()
virtual const wxString Problem() const
what was the problem?
virtual const wxString Where() const
where did the Problem() occur?
A wrapper for static data that should not be destroyed at program exit.
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const
Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
void evictOwnedGlobalEntries()
Erases this adapter's own entries (LIB_DATA::global_owner == this) from the process-wide globalLibs()...
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
std::vector< LIBRARY_TABLE_ROW * > Rows(LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH, bool aIncludeInvalid=false) const
Like LIBRARY_MANAGER::Rows but filtered to the LIBRARY_TABLE_TYPE of this adapter.
wxString getUri(const LIBRARY_TABLE_ROW *aRow) const
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::vector< wxString > GetLibraryNames() const
Returns a list of library nicknames that are available (skips any that failed to load)
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Type() const
const wxString & URI() const
const wxString & Nickname() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
static wxString describeMissingLibrary(const wxString &aNickname, const std::vector< LIBRARY_TABLE_ROW * > &aRows)
#define _(s)
Functions related to environment variables, including help functions.
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
Storage for an actual loaded library (including library content owned by the plugin)
LIB_STATUS status
std::unique_ptr< IO_BASE > plugin
const LIBRARY_TABLE_ROW * row
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.