KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_database.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 (C) 2022 Jon Evans <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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 along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <iostream>
22#include <unordered_set>
23#include <wx/datetime.h>
24#include <wx/log.h>
25
26#include <boost/algorithm/string.hpp>
27
30#include <fmt.h>
31#include <lib_symbol.h>
32#include <symbol_lib_table.h>
33
34#include "sch_io_database.h"
35
36
37SCH_IO_DATABASE::SCH_IO_DATABASE() : SCH_IO( wxS( "Database library" ) ),
38 m_libTable( nullptr ),
39 m_settings(),
40 m_conn()
41{
44}
45
46
50
51
52void SCH_IO_DATABASE::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
53 const wxString& aLibraryPath,
54 const std::map<std::string, UTF8>* aProperties )
55{
56 std::vector<LIB_SYMBOL*> symbols;
57 EnumerateSymbolLib( symbols, aLibraryPath, aProperties );
58
59 for( LIB_SYMBOL* symbol : symbols )
60 aSymbolNameList.Add( symbol->GetName() );
61}
62
63
64void SCH_IO_DATABASE::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
65 const wxString& aLibraryPath,
66 const std::map<std::string, UTF8>* aProperties )
67{
68 wxCHECK_RET( m_libTable, "Database plugin missing library table handle!" );
69 ensureSettings( aLibraryPath );
71 cacheLib();
72
73 if( !m_conn )
75
76 bool powerSymbolsOnly = ( aProperties && aProperties->contains( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) );
77
78 for( auto const& pair : m_nameToSymbolcache )
79 {
80 LIB_SYMBOL* symbol = pair.second.get();
81
82 if( !powerSymbolsOnly || symbol->IsPower() )
83 aSymbolList.emplace_back( symbol );
84 }
85}
86
87
88LIB_SYMBOL* SCH_IO_DATABASE::LoadSymbol( const wxString& aLibraryPath,
89 const wxString& aAliasName,
90 const std::map<std::string, UTF8>* aProperties )
91{
92 wxCHECK( m_libTable, nullptr );
93 ensureSettings( aLibraryPath );
95
96 if( !m_conn )
98
99 /*
100 * Table names are tricky, in order to allow maximum flexibility to the user.
101 * The slash character is used as a separator between a table name and symbol name, but symbol
102 * names may also contain slashes and table names may now also be empty (which results in the
103 * slash being dropped in the symbol name when placing a new symbol). So, if a slash is found,
104 * we check if the string before the slash is a valid table name. If not, we assume the table
105 * name is blank if our config has an entry for the null table.
106 */
107
108 std::string tableName = "";
109 std::string symbolName( aAliasName.ToUTF8() );
110
111 if( aAliasName.Contains( '/' ) )
112 {
113 tableName = std::string( aAliasName.BeforeFirst( '/' ).ToUTF8() );
114 symbolName = std::string( aAliasName.AfterFirst( '/' ).ToUTF8() );
115 }
116
117 std::vector<const DATABASE_LIB_TABLE*> tablesToTry;
118
119 for( const DATABASE_LIB_TABLE& tableIter : m_settings->m_Tables )
120 {
121 if( tableIter.name == tableName )
122 tablesToTry.emplace_back( &tableIter );
123 }
124
125 if( tablesToTry.empty() )
126 {
127 wxLogTrace( traceDatabase, wxT( "LoadSymbol: table '%s' not found in config" ), tableName );
128 return nullptr;
129 }
130
131 const DATABASE_LIB_TABLE* foundTable = nullptr;
133
134 for( const DATABASE_LIB_TABLE* table : tablesToTry )
135 {
136 if( m_conn->SelectOne( table->table, std::make_pair( table->key_col, symbolName ),
137 result ) )
138 {
139 foundTable = table;
140 wxLogTrace( traceDatabase, wxT( "LoadSymbol: SelectOne (%s, %s) found in %s" ),
141 table->key_col, symbolName, table->table );
142 }
143 else
144 {
145 wxLogTrace( traceDatabase, wxT( "LoadSymbol: SelectOne (%s, %s) failed for table %s" ),
146 table->key_col, symbolName, table->table );
147 }
148 }
149
150 wxCHECK( foundTable, nullptr );
151
152 return loadSymbolFromRow( aAliasName, *foundTable, result ).release();
153}
154
155
156void SCH_IO_DATABASE::GetSubLibraryNames( std::vector<wxString>& aNames )
157{
158 ensureSettings( wxEmptyString );
159
160 aNames.clear();
161
162 std::set<wxString> tableNames;
163
164 for( const DATABASE_LIB_TABLE& tableIter : m_settings->m_Tables )
165 {
166 if( tableNames.count( tableIter.name ) )
167 continue;
168
169 aNames.emplace_back( tableIter.name );
170 tableNames.insert( tableIter.name );
171 }
172}
173
174
175void SCH_IO_DATABASE::GetAvailableSymbolFields( std::vector<wxString>& aNames )
176{
177 std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) );
178}
179
180
181void SCH_IO_DATABASE::GetDefaultSymbolFields( std::vector<wxString>& aNames )
182{
183 std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(),
184 std::back_inserter( aNames ) );
185}
186
187
188bool SCH_IO_DATABASE::TestConnection( wxString* aErrorMsg )
189{
190 if( m_conn && m_conn->IsConnected() )
191 return true;
192
193 connect();
194
195 if( aErrorMsg && ( !m_conn || !m_conn->IsConnected() ) )
196 *aErrorMsg = m_lastError;
197
198 return m_conn && m_conn->IsConnected();
199}
200
201
203{
204 long long currentTimestampSeconds = wxDateTime::Now().GetValue().GetValue() / 1000;
205
206 if( m_libTable->GetModifyHash() == m_cacheModifyHash
207 && ( currentTimestampSeconds - m_cacheTimestamp ) < m_settings->m_Cache.max_age )
208 {
209 return;
210 }
211
212 for( const DATABASE_LIB_TABLE& table : m_settings->m_Tables )
213 {
214 std::vector<DATABASE_CONNECTION::ROW> results;
215
216 if( !m_conn->SelectAll( table.table, table.key_col, results ) )
217 {
218 if( !m_conn->GetLastError().empty() )
219 {
220 wxString msg = wxString::Format( _( "Error reading database table %s: %s" ),
221 table.table, m_conn->GetLastError() );
222 THROW_IO_ERROR( msg );
223 }
224
225 continue;
226 }
227
228 for( DATABASE_CONNECTION::ROW& result : results )
229 {
230 if( !result.count( table.key_col ) )
231 continue;
232
233 std::string prefix = table.name.empty() ? "" : fmt::format( "{}/", table.name );
234 wxString name( fmt::format( "{}{}", prefix,
235 std::any_cast<std::string>( result[table.key_col] ) ) );
236
237 std::unique_ptr<LIB_SYMBOL> symbol = loadSymbolFromRow( name, table, result );
238
239 if( symbol )
240 m_nameToSymbolcache[symbol->GetName()] = std::move( symbol );
241 }
242 }
243
244 m_cacheTimestamp = currentTimestampSeconds;
245 m_cacheModifyHash = m_libTable->GetModifyHash();
246}
247
248void SCH_IO_DATABASE::ensureSettings( const wxString& aSettingsPath )
249{
250 auto tryLoad =
251 [&]()
252 {
253 if( !m_settings->LoadFromFile() )
254 {
255 wxString msg = wxString::Format(
256 _( "Could not load database library: settings file %s missing or invalid" ),
257 aSettingsPath );
258
259 THROW_IO_ERROR( msg );
260 }
261 };
262
263 if( !m_settings && !aSettingsPath.IsEmpty() )
264 {
265 std::string path( aSettingsPath.ToUTF8() );
266 m_settings = std::make_unique<DATABASE_LIB_SETTINGS>( path );
267 m_settings->SetReadOnly( true );
268
269 tryLoad();
270 }
271 else if( !m_conn && m_settings )
272 {
273 // If we have valid settings but no connection yet; reload settings in case user is editing
274 tryLoad();
275 }
276 else if( m_conn && m_settings && !aSettingsPath.IsEmpty() )
277 {
278 wxASSERT_MSG( aSettingsPath == m_settings->GetFilename(),
279 "Path changed for database library without re-initializing plugin!" );
280 }
281 else if( !m_settings )
282 {
283 wxLogTrace( traceDatabase, wxT( "ensureSettings: no settings but no valid path!" ) );
284 }
285}
286
287
289{
290 wxCHECK_RET( m_settings, "Call ensureSettings before ensureConnection!" );
291
292 connect();
293
294 if( !m_conn || !m_conn->IsConnected() )
295 {
296 wxString msg = wxString::Format(
297 _( "Could not load database library: could not connect to database %s (%s)" ),
298 m_settings->m_Source.dsn, m_lastError );
299
300 THROW_IO_ERROR( msg );
301 }
302}
303
304
306{
307 wxCHECK_RET( m_settings, "Call ensureSettings before connect()!" );
308
309 if( m_conn && !m_conn->IsConnected() )
310 m_conn.reset();
311
312 if( !m_conn )
313 {
314 if( m_settings->m_Source.connection_string.empty() )
315 {
316 m_conn = std::make_unique<DATABASE_CONNECTION>( m_settings->m_Source.dsn,
317 m_settings->m_Source.username,
318 m_settings->m_Source.password,
319 m_settings->m_Source.timeout );
320 }
321 else
322 {
323 std::string cs = m_settings->m_Source.connection_string;
324 std::string basePath( wxFileName( m_settings->GetFilename() ).GetPath().ToUTF8() );
325
326 // Database drivers that use files operate on absolute paths, so provide a mechanism
327 // for specifying on-disk databases that live next to the kicad_dbl file
328 boost::replace_all( cs, "${CWD}", basePath );
329
330 m_conn = std::make_unique<DATABASE_CONNECTION>( cs, m_settings->m_Source.timeout );
331 }
332
333 if( !m_conn->IsConnected() )
334 {
335 m_lastError = m_conn->GetLastError();
336 m_conn.reset();
337 return;
338 }
339
340 for( const DATABASE_LIB_TABLE& tableIter : m_settings->m_Tables )
341 {
342 std::set<std::string> columns;
343
344 columns.insert( boost::to_lower_copy( tableIter.key_col ) );
345 columns.insert( boost::to_lower_copy( tableIter.footprints_col ) );
346 columns.insert( boost::to_lower_copy( tableIter.symbols_col ) );
347
348 columns.insert( boost::to_lower_copy( tableIter.properties.description ) );
349 columns.insert( boost::to_lower_copy( tableIter.properties.footprint_filters ) );
350 columns.insert( boost::to_lower_copy( tableIter.properties.keywords ) );
351 columns.insert( boost::to_lower_copy( tableIter.properties.exclude_from_sim ) );
352 columns.insert( boost::to_lower_copy( tableIter.properties.exclude_from_bom ) );
353 columns.insert( boost::to_lower_copy( tableIter.properties.exclude_from_board ) );
354
355 for( const DATABASE_FIELD_MAPPING& field : tableIter.fields )
356 columns.insert( boost::to_lower_copy( field.column ) );
357
358 m_conn->CacheTableInfo( tableIter.table, columns );
359 }
360
361 m_conn->SetCacheParams( m_settings->m_Cache.max_size, m_settings->m_Cache.max_age );
362 }
363}
364
365
366std::optional<bool> SCH_IO_DATABASE::boolFromAny( const std::any& aVal )
367{
368 try
369 {
370 bool val = std::any_cast<bool>( aVal );
371 return val;
372 }
373 catch( const std::bad_any_cast& )
374 {
375 }
376
377 try
378 {
379 int val = std::any_cast<int>( aVal );
380 return static_cast<bool>( val );
381 }
382 catch( const std::bad_any_cast& )
383 {
384 }
385
386 try
387 {
388 wxString strval( std::any_cast<std::string>( aVal ).c_str(), wxConvUTF8 );
389
390 if( strval.IsEmpty() )
391 return std::nullopt;
392
393 strval.MakeLower();
394
395 for( const auto& trueVal : { wxS( "true" ), wxS( "yes" ), wxS( "y" ), wxS( "1" ) } )
396 {
397 if( strval.Matches( trueVal ) )
398 return true;
399 }
400
401 for( const auto& falseVal : { wxS( "false" ), wxS( "no" ), wxS( "n" ), wxS( "0" ) } )
402 {
403 if( strval.Matches( falseVal ) )
404 return false;
405 }
406 }
407 catch( const std::bad_any_cast& )
408 {
409 }
410
411 return std::nullopt;
412}
413
414
415std::unique_ptr<LIB_SYMBOL> SCH_IO_DATABASE::loadSymbolFromRow( const wxString& aSymbolName,
416 const DATABASE_LIB_TABLE& aTable,
417 const DATABASE_CONNECTION::ROW& aRow )
418{
419 std::unique_ptr<LIB_SYMBOL> symbol = nullptr;
420
421 if( aRow.count( aTable.symbols_col ) )
422 {
423 LIB_SYMBOL* originalSymbol = nullptr;
424
425 // TODO: Support multiple options for symbol
426 std::string symbolIdStr = std::any_cast<std::string>( aRow.at( aTable.symbols_col ) );
427 LIB_ID symbolId;
428 symbolId.Parse( std::any_cast<std::string>( aRow.at( aTable.symbols_col ) ) );
429
430 if( symbolId.IsValid() )
431 originalSymbol = m_libTable->LoadSymbol( symbolId );
432
433 if( originalSymbol )
434 {
435 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: found original symbol '%s'" ),
436 symbolIdStr );
437 symbol.reset( originalSymbol->Duplicate() );
438 symbol->SetSourceLibId( symbolId );
439 }
440 else if( !symbolId.IsValid() )
441 {
442 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: source symbol id '%s' is invalid, "
443 "will create empty symbol" ), symbolIdStr );
444 }
445 else
446 {
447 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: source symbol '%s' not found, "
448 "will create empty symbol" ), symbolIdStr );
449 }
450 }
451
452 if( !symbol )
453 {
454 // Actual symbol not found: return metadata only; error will be indicated in the
455 // symbol chooser
456 symbol.reset( new LIB_SYMBOL( aSymbolName ) );
457 }
458 else
459 {
460 symbol->SetName( aSymbolName );
461 }
462
463 LIB_ID libId = symbol->GetLibId();
464 libId.SetSubLibraryName( aTable.name );;
465 symbol->SetLibId( libId );
466 wxArrayString footprintsList;
467
468 if( aRow.count( aTable.footprints_col ) )
469 {
470 std::string footprints = std::any_cast<std::string>( aRow.at( aTable.footprints_col ) );
471
472 wxString footprintsStr = wxString( footprints.c_str(), wxConvUTF8 );
473 wxStringTokenizer tokenizer( footprintsStr, ";\t\r\n", wxTOKEN_STRTOK );
474
475 while( tokenizer.HasMoreTokens() )
476 footprintsList.Add( tokenizer.GetNextToken() );
477
478 if( footprintsList.size() > 0 )
479 symbol->GetFootprintField().SetText( footprintsList[0] );
480 }
481 else
482 {
483 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: footprint field %s not found." ),
484 aTable.footprints_col );
485 }
486
487 if( !aTable.properties.description.empty() && aRow.count( aTable.properties.description ) )
488 {
489 wxString value(
490 std::any_cast<std::string>( aRow.at( aTable.properties.description ) ).c_str(),
491 wxConvUTF8 );
492 symbol->SetDescription( value );
493 }
494
495 if( !aTable.properties.keywords.empty() && aRow.count( aTable.properties.keywords ) )
496 {
497 wxString value( std::any_cast<std::string>( aRow.at( aTable.properties.keywords ) ).c_str(),
498 wxConvUTF8 );
499 symbol->SetKeyWords( value );
500 }
501
502 if( !aTable.properties.footprint_filters.empty()
503 && aRow.count( aTable.properties.footprint_filters ) )
504 {
505 wxString value( std::any_cast<std::string>( aRow.at( aTable.properties.footprint_filters ) )
506 .c_str(),
507 wxConvUTF8 );
508 footprintsList.push_back( value );
509 }
510
511 symbol->SetFPFilters( footprintsList );
512
513 if( !aTable.properties.exclude_from_sim.empty()
514 && aRow.count( aTable.properties.exclude_from_sim ) )
515 {
516 std::optional<bool> val = boolFromAny( aRow.at( aTable.properties.exclude_from_sim ) );
517
518 if( val )
519 {
520 symbol->SetExcludedFromSim( *val );
521 }
522 else
523 {
524 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: exclude_from_sim value for %s "
525 "could not be cast to a boolean" ), aSymbolName );
526 }
527 }
528
529 if( !aTable.properties.exclude_from_board.empty()
530 && aRow.count( aTable.properties.exclude_from_board ) )
531 {
532 std::optional<bool> val = boolFromAny( aRow.at( aTable.properties.exclude_from_board ) );
533
534 if( val )
535 {
536 symbol->SetExcludedFromBoard( *val );
537 }
538 else
539 {
540 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: exclude_from_board value for %s "
541 "could not be cast to a boolean" ), aSymbolName );
542 }
543 }
544
545 if( !aTable.properties.exclude_from_bom.empty()
546 && aRow.count( aTable.properties.exclude_from_bom ) )
547 {
548 std::optional<bool> val = boolFromAny( aRow.at( aTable.properties.exclude_from_bom ) );
549
550 if( val )
551 {
552 symbol->SetExcludedFromBOM( *val );
553 }
554 else
555 {
556 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: exclude_from_bom value for %s "
557 "could not be cast to a boolean" ), aSymbolName );
558 }
559 }
560
561 std::vector<SCH_FIELD*> fields;
562 symbol->GetFields( fields );
563
564 std::unordered_map<wxString, SCH_FIELD*> fieldsMap;
565
566 for( SCH_FIELD* field : fields )
567 fieldsMap[field->GetName()] = field;
568
569 static const wxString c_valueFieldName( wxS( "Value" ) );
570 static const wxString c_datasheetFieldName( wxS( "Datasheet" ) );
571
572 for( const DATABASE_FIELD_MAPPING& mapping : aTable.fields )
573 {
574 if( !aRow.count( mapping.column ) )
575 {
576 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: field %s not found in result" ),
577 mapping.column );
578 continue;
579 }
580
581 std::string strValue;
582
583 try
584 {
585 strValue = std::any_cast<std::string>( aRow.at( mapping.column ) );
586 }
587 catch( std::bad_any_cast& )
588 {
589 }
590
591 wxString value( strValue.c_str(), wxConvUTF8 );
592
593 if( mapping.name_wx == c_valueFieldName )
594 {
595 SCH_FIELD& field = symbol->GetValueField();
596 field.SetText( value );
597
598 if( !mapping.inherit_properties )
599 {
600 field.SetVisible( mapping.visible_on_add );
601 field.SetNameShown( mapping.show_name );
602 }
603 continue;
604 }
605 else if( mapping.name_wx == c_datasheetFieldName )
606 {
607 SCH_FIELD& field = symbol->GetDatasheetField();
608 field.SetText( value );
609
610 if( !mapping.inherit_properties )
611 {
612 field.SetVisible( mapping.visible_on_add );
613 field.SetNameShown( mapping.show_name );
614
615 if( mapping.visible_on_add )
616 field.SetAutoAdded( true );
617 }
618
619 continue;
620 }
621
622 SCH_FIELD* field;
623 bool isNew = false;
624
625 if( fieldsMap.count( mapping.name_wx ) )
626 {
627 field = fieldsMap[mapping.name_wx];
628 }
629 else
630 {
631 field = new SCH_FIELD( nullptr, FIELD_T::USER );
632 field->SetName( mapping.name_wx );
633 isNew = true;
634 fieldsMap[mapping.name_wx] = field;
635 }
636
637 if( !mapping.inherit_properties || isNew )
638 {
639 field->SetVisible( mapping.visible_on_add );
640 field->SetAutoAdded( true );
641 field->SetNameShown( mapping.show_name );
642 }
643
644 field->SetText( value );
645
646 if( isNew )
647 symbol->AddDrawItem( field, false );
648
649 m_customFields.insert( mapping.name_wx );
650
651 if( mapping.visible_in_chooser )
652 m_defaultShownFields.insert( mapping.name_wx );
653 }
654
655 symbol->GetDrawItems().sort();
656
657 return symbol;
658}
const char * name
std::map< std::string, std::any > ROW
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:387
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
void SetSubLibraryName(const UTF8 &aName)
Definition lib_id.h:131
Define a library symbol object.
Definition lib_symbol.h:85
bool IsPower() const override
virtual LIB_SYMBOL * Duplicate() const
Create a copy of a LIB_SYMBOL and assigns unique KIIDs to the copy and its children.
Definition lib_symbol.h:100
void SetAutoAdded(bool aAutoAdded)
Definition sch_field.h:221
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:203
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
std::unique_ptr< DATABASE_CONNECTION > m_conn
Generally will be null if no valid connection is established.
void GetDefaultSymbolFields(std::vector< wxString > &aNames) override
Retrieves a list of (custom) field names that should be shown by default for this library in the symb...
void ensureSettings(const wxString &aSettingsPath)
bool TestConnection(wxString *aErrorMsg=nullptr)
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_nameToSymbolcache
SYMBOL_LIB_TABLE * m_libTable
std::set< wxString > m_defaultShownFields
virtual ~SCH_IO_DATABASE()
void GetSubLibraryNames(std::vector< wxString > &aNames) override
Retrieves a list of sub-libraries in this library.
std::unique_ptr< DATABASE_LIB_SETTINGS > m_settings
static std::optional< bool > boolFromAny(const std::any &aVal)
long long m_cacheTimestamp
void GetAvailableSymbolFields(std::vector< wxString > &aNames) override
Retrieves a list of (custom) field names that are present on symbols in this library.
std::unique_ptr< LIB_SYMBOL > loadSymbolFromRow(const wxString &aSymbolName, const DATABASE_LIB_TABLE &aTable, const DATABASE_CONNECTION::ROW &aRow)
std::set< wxString > m_customFields
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
SCH_IO(const wxString &aName)
Definition sch_io.h:373
static const char * PropPowerSymsOnly
const char *const traceDatabase
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
static std::string strValue(double aValue)
bool visible_in_chooser
Whether the column is shown by default in the chooser.
std::string column
Database column name.
bool inherit_properties
Whether or not to inherit properties from symbol field.
bool visible_on_add
Whether to show the field when placing the symbol.
bool show_name
Whether or not to show the field name as well as its value.
wxString name_wx
KiCad field name (converted)
A database library table will be mapped to a sub-library provided by the database library entry in th...
std::string key_col
Unique key column name (will form part of the LIB_ID)
std::string name
KiCad library nickname (will form part of the LIB_ID)
std::string symbols_col
Column name containing KiCad symbol refs.
std::string footprints_col
Column name containing KiCad footprint refs.
std::vector< DATABASE_FIELD_MAPPING > fields
std::string table
Database table to pull content from.
MAPPABLE_SYMBOL_PROPERTIES properties
@ USER
The field ID hasn't been set yet; field is invalid.
wxString result
Test unit parsing edge cases and error handling.