KiCad PCB EDA Suite
Loading...
Searching...
No Matches
lib_table_base.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) 2010-2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
5 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
6 * Copyright (C) 2012-2022 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26
27#include <wx/debug.h>
28#include <wx/filename.h>
29#include <set>
30#include <common.h>
31#include <kiface_base.h>
32#include <lib_table_base.h>
33#include <lib_table_lexer.h>
34#include <macros.h>
35#include <string_utils.h>
36
37#define OPT_SEP '|'
38
39
40using namespace LIB_TABLE_T;
41
42
44{
45 return aRow.clone();
46}
47
48
50{
51 properties.reset( aProperties );
52}
53
54
55void LIB_TABLE_ROW::SetFullURI( const wxString& aFullURI )
56{
57 uri_user = aFullURI;
58}
59
60
61const wxString LIB_TABLE_ROW::GetFullURI( bool aSubstituted ) const
62{
63 if( aSubstituted )
64 {
65 return ExpandEnvVarSubstitutions( uri_user, nullptr );
66 }
67
68 return uri_user;
69}
70
71
72void LIB_TABLE_ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const
73{
74 // In Kicad, we save path and file names using the Unix notation (separator = '/')
75 // So ensure separator is always '/' is saved URI string
76 wxString uri = GetFullURI();
77 uri.Replace( '\\', '/' );
78
79 wxString extraOptions;
80
81 if( !GetIsEnabled() )
82 extraOptions += "(disabled)";
83
84 if( !GetIsVisible() )
85 extraOptions += "(hidden)";
86
87 out->Print( nestLevel, "(lib (name %s)(type %s)(uri %s)(options %s)(descr %s)%s)\n",
88 out->Quotew( GetNickName() ).c_str(),
89 out->Quotew( GetType() ).c_str(),
90 out->Quotew( uri ).c_str(),
91 out->Quotew( GetOptions() ).c_str(),
92 out->Quotew( GetDescr() ).c_str(),
93 extraOptions.ToStdString().c_str() );
94}
95
96
98{
99 return nickName == r.nickName
100 && uri_user == r.uri_user
101 && options == r.options
103 && enabled == r.enabled
104 && visible == r.visible;
105}
106
107
108void LIB_TABLE_ROW::SetOptions( const wxString& aOptions )
109{
110 options = aOptions;
111
112 // set PROPERTIES* from options
114}
115
116
117LIB_TABLE::LIB_TABLE( LIB_TABLE* aFallBackTable ) :
118 m_fallBack( aFallBackTable ), m_version( 0 )
119{
120 // not copying fall back, simply search aFallBackTable separately
121 // if "nickName not found".
122}
123
124
126{
127 // *fallBack is not owned here.
128}
129
130
132{
133 m_rows.clear();
134 m_rowsMap.clear();
135}
136
137
138bool LIB_TABLE::IsEmpty( bool aIncludeFallback )
139{
140 if( !aIncludeFallback || !m_fallBack )
141 return m_rows.empty();
142
143 return m_rows.empty() && m_fallBack->IsEmpty( true );
144}
145
146
147const wxString LIB_TABLE::GetDescription( const wxString& aNickname )
148{
149 // Use "no exception" form of find row and ignore disabled flag.
150 const LIB_TABLE_ROW* row = findRow( aNickname );
151
152 if( row )
153 return row->GetDescr();
154 else
155 return wxEmptyString;
156}
157
158
159bool LIB_TABLE::HasLibrary( const wxString& aNickname, bool aCheckEnabled ) const
160{
161 const LIB_TABLE_ROW* row = findRow( aNickname, aCheckEnabled );
162
163 if( row == nullptr )
164 return false;
165
166 return true;
167}
168
169
170bool LIB_TABLE::HasLibraryWithPath( const wxString& aPath ) const
171{
172 for( const LIB_TABLE_ROW& row : m_rows )
173 {
174 if( row.GetFullURI() == aPath )
175 return true;
176 }
177
178 return false;
179}
180
181
182wxString LIB_TABLE::GetFullURI( const wxString& aNickname, bool aExpandEnvVars ) const
183{
184 const LIB_TABLE_ROW* row = findRow( aNickname, true );
185
186 wxString retv;
187
188 if( row )
189 retv = row->GetFullURI( aExpandEnvVars );
190
191 return retv;
192}
193
194
195LIB_TABLE_ROW* LIB_TABLE::findRow( const wxString& aNickName, bool aCheckIfEnabled ) const
196{
197 LIB_TABLE_ROW* row = nullptr;
198 LIB_TABLE* cur = (LIB_TABLE*) this;
199
200 do
201 {
202 std::shared_lock<std::shared_mutex> lock( cur->m_mutex );
203
204 if( cur->m_rowsMap.count( aNickName ) )
205 row = &*cur->m_rowsMap.at( aNickName );
206
207 if( row )
208 {
209 if( !aCheckIfEnabled || row->GetIsEnabled() )
210 return row;
211 else
212 return nullptr; // We found it, but it's disabled
213 }
214
215 // Repeat, this time looking for names that were "fixed" by legacy versions because
216 // the old eeschema file format didn't support spaces in tokens.
217 for( const std::pair<const wxString, LIB_TABLE_ROWS_ITER>& entry : cur->m_rowsMap )
218 {
219 wxString legacyLibName = entry.first;
220 legacyLibName.Replace( " ", "_" );
221
222 if( legacyLibName == aNickName )
223 {
224 row = &*entry.second;
225
226 if( !aCheckIfEnabled || row->GetIsEnabled() )
227 return row;
228 }
229 }
230
231 // not found, search fall back table(s), if any
232 } while( ( cur = cur->m_fallBack ) != nullptr );
233
234 return nullptr; // not found
235}
236
237
238const LIB_TABLE_ROW* LIB_TABLE::FindRowByURI( const wxString& aURI )
239{
240 LIB_TABLE* cur = this;
241
242 do
243 {
244 for( unsigned i = 0; i < cur->m_rows.size(); i++ )
245 {
246 wxString tmp = cur->m_rows[i].GetFullURI( true );
247
248 if( tmp.Find( "://" ) != wxNOT_FOUND )
249 {
250 if( tmp == aURI )
251 return &cur->m_rows[i]; // found as URI
252 }
253 else
254 {
255 wxFileName fn = aURI;
256
257 // This will also test if the file is a symlink so if we are comparing
258 // a symlink to the same real file, the comparison will be true. See
259 // wxFileName::SameAs() in the wxWidgets source.
260 if( fn == wxFileName( tmp ) )
261 return &cur->m_rows[i]; // found as full path and file name
262 }
263 }
264
265 // not found, search fall back table(s), if any
266 } while( ( cur = cur->m_fallBack ) != nullptr );
267
268 return nullptr; // not found
269}
270
271
272std::vector<wxString> LIB_TABLE::GetLogicalLibs()
273{
274 // Only return unique logical library names. Use std::set::insert() to quietly reject any
275 // duplicates (usually due to encountering a duplicate nickname in a fallback table).
276
277 std::set<wxString> unique;
278 std::vector<wxString> ret;
279 const LIB_TABLE* cur = this;
280
281 do
282 {
283 for( const LIB_TABLE_ROW& row : cur->m_rows )
284 {
285 if( row.GetIsEnabled() )
286 unique.insert( row.GetNickName() );
287 }
288
289 } while( ( cur = cur->m_fallBack ) != nullptr );
290
291 ret.reserve( unique.size() );
292
293 // return a sorted, unique set of nicknames in a std::vector<wxString> to caller
294 for( std::set< wxString >::const_iterator it = unique.begin(); it!=unique.end(); ++it )
295 ret.push_back( *it );
296
297 // We want to allow case-sensitive duplicates but sort by case-insensitive ordering
298 std::sort( ret.begin(), ret.end(),
299 []( const wxString& lhs, const wxString& rhs )
300 {
301 return StrNumCmp( lhs, rhs, true /* ignore case */ ) < 0;
302 } );
303
304 return ret;
305}
306
307
308bool LIB_TABLE::InsertRow( LIB_TABLE_ROW* aRow, bool doReplace )
309{
310 std::lock_guard<std::shared_mutex> lock( m_mutex );
311
312 doInsertRow( aRow, doReplace );
313 reindex();
314
315 return true;
316}
317
318
319bool LIB_TABLE::doInsertRow( LIB_TABLE_ROW* aRow, bool doReplace )
320{
321 auto it = m_rowsMap.find( aRow->GetNickName() );
322
323 if( it != m_rowsMap.end() )
324 {
325 if( !doReplace )
326 return false;
327
328 m_rows.replace( it->second, aRow );
329 }
330 else
331 {
332 m_rows.push_back( aRow );
333 }
334
335 aRow->SetParent( this );
336 reindex();
337 return true;
338}
339
340
342{
343 std::lock_guard<std::shared_mutex> lock( m_mutex );
344
345 bool found = false;
346 auto it = m_rowsMap.find( aRow->GetNickName() );
347
348 if( it != m_rowsMap.end() )
349 {
350 if( &*it->second == aRow )
351 {
352 found = true;
353 m_rows.erase( it->second );
354 }
355 }
356
357 if( !found )
358 {
359 // Bookkeeping got messed up...
360 for( int i = (int)m_rows.size() - 1; i >= 0; --i )
361 {
362 if( &m_rows[i] == aRow )
363 {
364 m_rows.erase( m_rows.begin() + i );
365 found = true;
366 break;
367 }
368 }
369 }
370
371 if( found )
372 reindex();
373
374 return found;
375}
376
377
378bool LIB_TABLE::ReplaceRow( size_t aIndex, LIB_TABLE_ROW* aRow )
379{
380 std::lock_guard<std::shared_mutex> lock( m_mutex );
381
382 if( aIndex >= m_rows.size() )
383 return false;
384
385 m_rowsMap.erase( m_rows[aIndex].GetNickName() );
386
387 m_rows.replace( aIndex, aRow );
388 reindex();
389 return true;
390}
391
392
393bool LIB_TABLE::ChangeRowOrder( size_t aIndex, int aOffset )
394{
395 std::lock_guard<std::shared_mutex> lock( m_mutex );
396
397 if( aIndex >= m_rows.size() )
398 return false;
399
400 int newPos = static_cast<int>( aIndex ) + aOffset;
401
402 if( newPos < 0 || newPos > static_cast<int>( m_rows.size() ) - 1 )
403 return false;
404
405 auto element = m_rows.release( m_rows.begin() + aIndex );
406
407 m_rows.insert( m_rows.begin() + newPos, element.release() );
408 reindex();
409
410 return true;
411}
412
413
415{
416 std::lock_guard<std::shared_mutex> lock( m_mutex );
417
418 clear();
419 m_rows.transfer( m_rows.end(), aRowsList.begin(), aRowsList.end(), aRowsList );
420
421 reindex();
422}
423
424
426{
427 m_rowsMap.clear();
428
429 for( LIB_TABLE_ROWS_ITER it = m_rows.begin(); it != m_rows.end(); ++it )
430 {
431 it->SetParent( this );
432 m_rowsMap[it->GetNickName()] = it;
433 }
434}
435
436
438{
439 bool table_updated = false;
440
441 for( LIB_TABLE_ROW& row : m_rows )
442 {
443 bool row_updated = false;
444 wxString uri = row.GetFullURI( true );
445
446 // If the uri still has a variable in it, that means that the user does not have
447 // these vars defined. We update the old vars to the KICAD7 versions on load
448 row_updated |= ( uri.Replace( wxS( "${KICAD5_" ), wxS( "${KICAD7_" ), false ) > 0 );
449 row_updated |= ( uri.Replace( wxS( "${KICAD6_" ), wxS( "${KICAD7_" ), false ) > 0 );
450
451 if( row_updated )
452 {
453 row.SetFullURI( uri );
454 table_updated = true;
455 }
456 }
457
458 return table_updated;
459}
460
461
462void LIB_TABLE::Load( const wxString& aFileName )
463{
464 std::lock_guard<std::shared_mutex> lock( m_mutex );
465 clear();
466
467 // It's OK if footprint library tables are missing.
468 if( wxFileName::IsFileReadable( aFileName ) )
469 {
470 FILE_LINE_READER reader( aFileName );
471 LIB_TABLE_LEXER lexer( &reader );
472
473 Parse( &lexer );
474
475 if( m_version != 7 && migrate() && wxFileName::IsFileWritable( aFileName ) )
476 Save( aFileName );
477
478 reindex();
479 }
480}
481
482
483void LIB_TABLE::Save( const wxString& aFileName ) const
484{
485 FILE_OUTPUTFORMATTER sf( aFileName );
486
487 // Force the lib table version to 7 before saving
488 m_version = 7;
489 Format( &sf, 0 );
490}
491
492
493STRING_UTF8_MAP* LIB_TABLE::ParseOptions( const std::string& aOptionsList )
494{
495 if( aOptionsList.size() )
496 {
497 const char* cp = &aOptionsList[0];
498 const char* end = cp + aOptionsList.size();
499
500 STRING_UTF8_MAP props;
501 std::string pair;
502
503 // Parse all name=value pairs
504 while( cp < end )
505 {
506 pair.clear();
507
508 // Skip leading white space.
509 while( cp < end && isspace( *cp ) )
510 ++cp;
511
512 // Find the end of pair/field
513 while( cp < end )
514 {
515 if( *cp == '\\' && cp + 1 < end && cp[1] == OPT_SEP )
516 {
517 ++cp; // skip the escape
518 pair += *cp++; // add the separator
519 }
520 else if( *cp == OPT_SEP )
521 {
522 ++cp; // skip the separator
523 break; // process the pair
524 }
525 else
526 {
527 pair += *cp++;
528 }
529 }
530
531 // stash the pair
532 if( pair.size() )
533 {
534 // first equals sign separates 'name' and 'value'.
535 size_t eqNdx = pair.find( '=' );
536
537 if( eqNdx != pair.npos )
538 {
539 std::string name = pair.substr( 0, eqNdx );
540 std::string value = pair.substr( eqNdx + 1 );
541 props[name] = value;
542 }
543 else
544 {
545 props[pair] = ""; // property is present, but with no value.
546 }
547 }
548 }
549
550 if( props.size() )
551 return new STRING_UTF8_MAP( props );
552 }
553
554 return nullptr;
555}
556
557
559{
560 UTF8 ret;
561
562 if( aProperties )
563 {
564 for( STRING_UTF8_MAP::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it )
565 {
566 const std::string& name = it->first;
567
568 const UTF8& value = it->second;
569
570 if( ret.size() )
571 ret += OPT_SEP;
572
573 ret += name;
574
575 // the separation between name and value is '='
576 if( value.size() )
577 {
578 ret += '=';
579
580 for( std::string::const_iterator si = value.begin(); si != value.end(); ++si )
581 {
582 // escape any separator in the value.
583 if( *si == OPT_SEP )
584 ret += '\\';
585
586 ret += *si;
587 }
588 }
589 }
590 }
591
592 return ret;
593}
const char * name
Definition: DXF_plotter.cpp:57
A LINE_READER that reads from an open file.
Definition: richio.h:185
Used for text file output.
Definition: richio.h:475
Hold a record identifying a library accessed by the appropriate plug in object in the LIB_TABLE.
void SetFullURI(const wxString &aFullURI)
Change the full URI for the library.
bool visible
Whether the LIB_TABLE_ROW is visible in choosers.
const wxString & GetOptions() const
Return the options string, which may hold a password or anything else needed to instantiate the under...
wxString nickName
const wxString & GetDescr() const
Return the description of the library referenced by this row.
std::unique_ptr< STRING_UTF8_MAP > properties
wxString options
wxString uri_user
what user entered from UI or loaded from disk
virtual const wxString GetType() const =0
Return the type of library represented by this row.
wxString description
void Format(OUTPUTFORMATTER *out, int nestLevel) const
Serialize this object as utf8 text to an OUTPUTFORMATTER, and tries to make it look good using multip...
bool enabled
Whether the LIB_TABLE_ROW is enabled.
void SetParent(LIB_TABLE *aParent)
void setProperties(STRING_UTF8_MAP *aProperties)
const wxString & GetNickName() const
const wxString GetFullURI(bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
LIB_TABLE_ROW * clone() const
bool operator==(const LIB_TABLE_ROW &r) const
bool GetIsEnabled() const
void SetOptions(const wxString &aOptions)
Change the library options strings.
bool GetIsVisible() const
Manage LIB_TABLE_ROW records (rows), and can be searched based on library nickname.
bool ReplaceRow(size_t aIndex, LIB_TABLE_ROW *aRow)
Replaces the Nth row with the given new row.
std::shared_mutex m_mutex
Mutex to protect access to the rows vector.
const wxString GetDescription(const wxString &aNickname)
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE.
std::map< wxString, LIB_TABLE_ROWS_ITER > m_rowsMap
this is a non-owning index into the LIB_TABLE_ROWS table
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
int m_version
Versioning to handle importing old tables.
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
void TransferRows(LIB_TABLE_ROWS &aRowsList)
Takes ownership of another list of rows; the original list will be freed.
LIB_TABLE_ROWS m_rows
Owning set of rows.
virtual ~LIB_TABLE()
bool doInsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Performs the mechanics of inserting a row, but without locking or reindexing.
bool migrate()
Updates the env vars from older version of KiCad, provided they do not currently resolve to anything.
void Load(const wxString &aFileName)
Load the library table using the path defined by aFileName aFallBackTable.
bool HasLibraryWithPath(const wxString &aPath) const
Test for the existence of aPath in the library table.
virtual void Format(OUTPUTFORMATTER *aOutput, int aIndentLevel) const =0
Generate the table in s-expression format to aOutput with an indentation level of aIndentLevel.
bool RemoveRow(const LIB_TABLE_ROW *aRow)
Removes a row from the table and frees the pointer.
LIB_TABLE * m_fallBack
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
LIB_TABLE(LIB_TABLE *aFallBackTable=nullptr)
Build a library table by pre-pending this table fragment in front of aFallBackTable.
bool IsEmpty(bool aIncludeFallback=true)
Return true if the table is empty.
virtual void Parse(LIB_TABLE_LEXER *aLexer)=0
Parse the #LIB_TABLE_LEXER s-expression library table format into the appropriate LIB_TABLE_ROW objec...
const LIB_TABLE_ROW * FindRowByURI(const wxString &aURI)
bool ChangeRowOrder(size_t aIndex, int aOffset)
Moves a row within the table.
void Save(const wxString &aFileName) const
Write this library table to aFileName in s-expression form.
static STRING_UTF8_MAP * ParseOptions(const std::string &aOptionsList)
Parses aOptionsList and places the result into a #PROPERTIES object which is returned.
LIB_TABLE_ROW * findRow(const wxString &aNickname, bool aCheckIfEnabled=false) const
Return a LIB_TABLE_ROW if aNickname is found in this table or in any chained fallBack table fragment,...
static UTF8 FormatOptions(const STRING_UTF8_MAP *aProperties)
Returns a list of options from the aProperties parameter.
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:322
std::string Quotew(const wxString &aWrapee) const
Definition: richio.cpp:526
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:458
A name/value tuple with unique names and optional values.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:72
std::string::const_iterator begin() const
Definition: utf8.h:197
std::string::size_type size() const
Definition: utf8.h:111
std::string::const_iterator end() const
Definition: utf8.h:198
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:343
The common library.
#define OPT_SEP
options separator character
LIB_TABLE_ROW * new_clone(const LIB_TABLE_ROW &aRow)
Allows boost pointer containers to make clones of the data stored in them.
boost::ptr_vector< LIB_TABLE_ROW > LIB_TABLE_ROWS
LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391