diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 0615293b0e..561b217c72 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -42,18 +42,23 @@ #include #include +#define OPT_SEP '|' ///< options separator character + using namespace FP_LIB_TABLE_T; static const wxChar global_tbl_name[] = wxT( "fp-lib-table" ); -void FP_LIB_TABLE_ROW::SetType( const wxString& aType ) +LIB_TABLE_ROW* new_clone( const LIB_TABLE_ROW& aRow ) { - type = IO_MGR::EnumFromStr( aType ); + return aRow.clone(); +} - if( IO_MGR::PCB_FILE_T( -1 ) == type ) - type = IO_MGR::KICAD; + +void LIB_TABLE_ROW::setProperties( PROPERTIES* aProperties ) +{ + properties.reset( aProperties ); } @@ -75,49 +80,11 @@ const wxString LIB_TABLE_ROW::GetFullURI( bool aSubstituted ) const return uri_expanded; #else // late expansion - return FP_LIB_TABLE::ExpandSubstitutions( uri_user ); + return LIB_TABLE::ExpandSubstitutions( uri_user ); #endif } - else - return uri_user; -} - -LIB_TABLE_ROW::LIB_TABLE_ROW( const LIB_TABLE_ROW& a ) : - nickName( a.nickName ), - options( a.options ), - description( a.description ), - properties( 0 ) -{ - // may call ExpandSubstitutions() - SetFullURI( a.uri_user ); - - if( a.properties ) - properties = new PROPERTIES( *a.properties ); -} - - -LIB_TABLE_ROW& LIB_TABLE_ROW::operator=( const LIB_TABLE_ROW& r ) -{ - nickName = r.nickName; - options = r.options; - description = r.description; - properties = r.properties ? new PROPERTIES( *r.properties ) : NULL; - - // may call ExpandSubstitutions() - SetFullURI( r.uri_user ); - - return *this; -} - - -bool LIB_TABLE_ROW::operator==( const LIB_TABLE_ROW& r ) const -{ - return nickName == r.nickName - && uri_user == r.uri_user - && options == r.options - && description == r.description - ; + return uri_user; } @@ -134,31 +101,113 @@ void LIB_TABLE_ROW::Format( OUTPUTFORMATTER* out, int nestLevel ) const } +void LIB_TABLE_ROW::Parse( std::unique_ptr< LIB_TABLE_ROW >& aRow, FP_LIB_TABLE_LEXER* in ) + throw( IO_ERROR, PARSE_ERROR ) +{ + /* + * (lib (name NICKNAME)(descr DESCRIPTION)(type TYPE)(full_uri FULL_URI)(options OPTIONS)) + * + * Elements after (name) are order independent. + */ + + T tok = in->NextTok(); + + if( tok != T_lib ) + in->Expecting( T_lib ); + + // (name NICKNAME) + in->NeedLEFT(); + + if( ( tok = in->NextTok() ) != T_name ) + in->Expecting( T_name ); + + in->NeedSYMBOLorNUMBER(); + + aRow->SetNickName( in->FromUTF8() ); + + in->NeedRIGHT(); + + // After (name), remaining (lib) elements are order independent, and in + // some cases optional. + bool sawType = false; + bool sawOpts = false; + bool sawDesc = false; + bool sawUri = false; + + while( ( tok = in->NextTok() ) != T_RIGHT ) + { + if( tok == T_EOF ) + in->Unexpected( T_EOF ); + + if( tok != T_LEFT ) + in->Expecting( T_LEFT ); + + tok = in->NeedSYMBOLorNUMBER(); + + switch( tok ) + { + case T_uri: + if( sawUri ) + in->Duplicate( tok ); + sawUri = true; + in->NeedSYMBOLorNUMBER(); + aRow->SetFullURI( in->FromUTF8() ); + break; + + case T_type: + if( sawType ) + in->Duplicate( tok ); + sawType = true; + in->NeedSYMBOLorNUMBER(); + aRow->SetType( in->FromUTF8() ); + break; + + case T_options: + if( sawOpts ) + in->Duplicate( tok ); + sawOpts = true; + in->NeedSYMBOLorNUMBER(); + aRow->SetOptions( in->FromUTF8() ); + break; + + case T_descr: + if( sawDesc ) + in->Duplicate( tok ); + sawDesc = true; + in->NeedSYMBOLorNUMBER(); + aRow->SetDescr( in->FromUTF8() ); + break; + + default: + in->Unexpected( tok ); + } + + in->NeedRIGHT(); + } + + if( !sawType ) + in->Expecting( T_type ); + + if( !sawUri ) + in->Expecting( T_uri ); +} + + +bool LIB_TABLE_ROW::operator==( const LIB_TABLE_ROW& r ) const +{ + return nickName == r.nickName + && uri_user == r.uri_user + && options == r.options + && description == r.description; +} + + void LIB_TABLE_ROW::SetOptions( const wxString& aOptions ) { options = aOptions; // set PROPERTIES* from options - setProperties( FP_LIB_TABLE::ParseOptions( TO_UTF8( aOptions ) ) ); -} - - -FP_LIB_TABLE_ROW::FP_LIB_TABLE_ROW( const FP_LIB_TABLE_ROW& aRow ) : - LIB_TABLE_ROW( aRow ), - type( aRow.type ) -{ -} - - -FP_LIB_TABLE_ROW& FP_LIB_TABLE_ROW::operator=( const FP_LIB_TABLE_ROW& aRow ) -{ - LIB_TABLE_ROW::operator = ( aRow ); - type = aRow.type; - - // Do not copy the PLUGIN, it is lazily created. Delete any existing destination plugin. - setPlugin( NULL ); - - return *this; + setProperties( LIB_TABLE::ParseOptions( TO_UTF8( aOptions ) ) ); } @@ -168,7 +217,16 @@ bool FP_LIB_TABLE_ROW::operator==( const FP_LIB_TABLE_ROW& aRow ) const } -FP_LIB_TABLE::FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable ) : +void FP_LIB_TABLE_ROW::SetType( const wxString& aType ) +{ + type = IO_MGR::EnumFromStr( aType ); + + if( IO_MGR::PCB_FILE_T( -1 ) == type ) + type = IO_MGR::KICAD; +} + + +LIB_TABLE::LIB_TABLE( LIB_TABLE* aFallBackTable ) : fallBack( aFallBackTable ) { // not copying fall back, simply search aFallBackTable separately @@ -176,12 +234,406 @@ FP_LIB_TABLE::FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable ) : } -FP_LIB_TABLE::~FP_LIB_TABLE() +LIB_TABLE::~LIB_TABLE() { // *fallBack is not owned here. } +bool LIB_TABLE::IsEmpty( bool aIncludeFallback ) +{ + if( !aIncludeFallback || !fallBack ) + return rows.empty(); + + return rows.empty() && fallBack->IsEmpty( true ); +} + + +const wxString LIB_TABLE::GetDescription( const wxString& aNickname ) +{ + // use "no exception" form of find row: + const LIB_TABLE_ROW* row = findRow( aNickname ); + + if( row ) + return row->GetDescr(); + else + return wxEmptyString; +} + + +LIB_TABLE_ROW* LIB_TABLE::findRow( const wxString& aNickName ) const +{ + LIB_TABLE* cur = (LIB_TABLE*) this; + + do + { + cur->ensureIndex(); + + INDEX_CITER it = cur->nickIndex.find( aNickName ); + + if( it != cur->nickIndex.end() ) + { + return &cur->rows[it->second]; // found + } + + // not found, search fall back table(s), if any + } while( ( cur = cur->fallBack ) != 0 ); + + return NULL; // not found +} + + +const LIB_TABLE_ROW* LIB_TABLE::FindRowByURI( const wxString& aURI ) +{ + LIB_TABLE* cur = this; + + do + { + cur->ensureIndex(); + + for( unsigned i = 0; i < cur->rows.size(); i++ ) + { + wxString uri = cur->rows[i].GetFullURI( true ); + + if( wxFileName::GetPathSeparator() == wxChar( '\\' ) && uri.Find( wxChar( '/' ) ) >= 0 ) + uri.Replace( "/", "\\" ); + + if( (wxFileName::IsCaseSensitive() && uri == aURI) + || (!wxFileName::IsCaseSensitive() && uri.Upper() == aURI.Upper() ) ) + { + return &cur->rows[i]; // found + } + } + + // not found, search fall back table(s), if any + } while( ( cur = cur->fallBack ) != 0 ); + + return NULL; // not found +} + + +std::vector LIB_TABLE::GetLogicalLibs() +{ + // Only return unique logical library names. Use std::set::insert() to + // quietly reject any duplicates, which can happen when encountering a duplicate + // nickname from one of the fall back table(s). + + std::set< wxString > unique; + std::vector< wxString > ret; + const LIB_TABLE* cur = this; + + do + { + for( LIB_TABLE_ROWS_CITER it = cur->rows.begin(); it!=cur->rows.end(); ++it ) + { + unique.insert( it->GetNickName() ); + } + + } while( ( cur = cur->fallBack ) != 0 ); + + ret.reserve( unique.size() ); + + // return a sorted, unique set of nicknames in a std::vector to caller + for( std::set< wxString >::const_iterator it = unique.begin(); it!=unique.end(); ++it ) + { + ret.push_back( *it ); + } + + return ret; +} + + +bool LIB_TABLE::InsertRow( LIB_TABLE_ROW* aRow, bool doReplace ) +{ + ensureIndex(); + + INDEX_CITER it = nickIndex.find( aRow->GetNickName() ); + + if( it == nickIndex.end() ) + { + rows.push_back( aRow ); + nickIndex.insert( INDEX_VALUE( aRow->GetNickName(), rows.size() - 1 ) ); + return true; + } + + if( doReplace ) + { + rows.replace( it->second, aRow ); + return true; + } + + return false; +} + + +void LIB_TABLE::Load( const wxString& aFileName ) + throw( IO_ERROR ) +{ + // It's OK if footprint library tables are missing. + if( wxFileName::IsFileReadable( aFileName ) ) + { + FILE_LINE_READER reader( aFileName ); + FP_LIB_TABLE_LEXER lexer( &reader ); + + Parse( &lexer ); + } +} + + +void LIB_TABLE::Save( const wxString& aFileName ) const + throw( IO_ERROR, boost::interprocess::lock_exception ) +{ + FILE_OUTPUTFORMATTER sf( aFileName ); + Format( &sf, 0 ); +} + + +PROPERTIES* LIB_TABLE::ParseOptions( const std::string& aOptionsList ) +{ + if( aOptionsList.size() ) + { + const char* cp = &aOptionsList[0]; + const char* end = cp + aOptionsList.size(); + + PROPERTIES props; + std::string pair; + + // Parse all name=value pairs + while( cp < end ) + { + pair.clear(); + + // Skip leading white space. + while( cp < end && isspace( *cp ) ) + ++cp; + + // Find the end of pair/field + while( cp < end ) + { + if( *cp == '\\' && cp + 1 < end && cp[1] == OPT_SEP ) + { + ++cp; // skip the escape + pair += *cp++; // add the separator + } + else if( *cp == OPT_SEP ) + { + ++cp; // skip the separator + break; // process the pair + } + else + pair += *cp++; + } + + // stash the pair + if( pair.size() ) + { + // first equals sign separates 'name' and 'value'. + size_t eqNdx = pair.find( '=' ); + + if( eqNdx != pair.npos ) + { + std::string name = pair.substr( 0, eqNdx ); + std::string value = pair.substr( eqNdx + 1 ); + props[name] = value; + } + else + props[pair] = ""; // property is present, but with no value. + } + } + + if( props.size() ) + return new PROPERTIES( props ); + } + + return NULL; +} + + +UTF8 LIB_TABLE::FormatOptions( const PROPERTIES* aProperties ) +{ + UTF8 ret; + + if( aProperties ) + { + for( PROPERTIES::const_iterator it = aProperties->begin(); it != aProperties->end(); ++it ) + { + const std::string& name = it->first; + + const UTF8& value = it->second; + + if( ret.size() ) + ret += OPT_SEP; + + ret += name; + + // the separation between name and value is '=' + if( value.size() ) + { + ret += '='; + + for( std::string::const_iterator si = value.begin(); si != value.end(); ++si ) + { + // escape any separator in the value. + if( *si == OPT_SEP ) + ret += '\\'; + + ret += *si; + } + } + } + } + + return ret; +} + + +const wxString LIB_TABLE::ExpandSubstitutions( const wxString& aString ) +{ + return ExpandEnvVarSubstitutions( aString ); +} + + +FP_LIB_TABLE::FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable ) : + LIB_TABLE( aFallBackTable ) +{ + // not copying fall back, simply search aFallBackTable separately + // if "nickName not found". +} + + +void FP_LIB_TABLE::Parse( FP_LIB_TABLE_LEXER* in ) throw() +{ + T tok; + + // This table may be nested within a larger s-expression, or not. + // Allow for parser of that optional containing s-epression to have looked ahead. + if( in->CurTok() != T_fp_lib_table ) + { + in->NeedLEFT(); + if( ( tok = in->NextTok() ) != T_fp_lib_table ) + in->Expecting( T_fp_lib_table ); + } + + while( ( tok = in->NextTok() ) != T_RIGHT ) + { + std::unique_ptr< FP_LIB_TABLE_ROW > row( new FP_LIB_TABLE_ROW ); + + if( tok == T_EOF ) + in->Expecting( T_RIGHT ); + + if( tok != T_LEFT ) + in->Expecting( T_LEFT ); + + // in case there is a "row integrity" error, tell where later. + int lineNum = in->CurLineNumber(); + int offset = in->CurOffset(); + + if( ( tok = in->NextTok() ) != T_lib ) + in->Expecting( T_lib ); + + // (name NICKNAME) + in->NeedLEFT(); + + if( ( tok = in->NextTok() ) != T_name ) + in->Expecting( T_name ); + + in->NeedSYMBOLorNUMBER(); + + row->SetNickName( in->FromUTF8() ); + + in->NeedRIGHT(); + + // After (name), remaining (lib) elements are order independent, and in + // some cases optional. + bool sawType = false; + bool sawOpts = false; + bool sawDesc = false; + bool sawUri = false; + + while( ( tok = in->NextTok() ) != T_RIGHT ) + { + if( tok == T_EOF ) + in->Unexpected( T_EOF ); + + if( tok != T_LEFT ) + in->Expecting( T_LEFT ); + + tok = in->NeedSYMBOLorNUMBER(); + + switch( tok ) + { + case T_uri: + if( sawUri ) + in->Duplicate( tok ); + sawUri = true; + in->NeedSYMBOLorNUMBER(); + row->SetFullURI( in->FromUTF8() ); + break; + + case T_type: + if( sawType ) + in->Duplicate( tok ); + sawType = true; + in->NeedSYMBOLorNUMBER(); + row->SetType( in->FromUTF8() ); + break; + + case T_options: + if( sawOpts ) + in->Duplicate( tok ); + sawOpts = true; + in->NeedSYMBOLorNUMBER(); + row->SetOptions( in->FromUTF8() ); + break; + + case T_descr: + if( sawDesc ) + in->Duplicate( tok ); + sawDesc = true; + in->NeedSYMBOLorNUMBER(); + row->SetDescr( in->FromUTF8() ); + break; + + default: + in->Unexpected( tok ); + } + + in->NeedRIGHT(); + } + + if( !sawType ) + in->Expecting( T_type ); + + if( !sawUri ) + in->Expecting( T_uri ); + + // all nickNames within this table fragment must be unique, so we do not + // use doReplace in InsertRow(). (However a fallBack table can have a + // conflicting nickName and ours will supercede that one since in + // FindLib() we search this table before any fall back.) + if( !InsertRow( row.release() ) ) + { + wxString msg = wxString::Format( + _( "'%s' is a duplicate footprint library nickName" ), + GetChars( row->GetNickName() ) ); + THROW_PARSE_ERROR( msg, in->CurSource(), in->CurLine(), lineNum, offset ); + } + } +} + + +void FP_LIB_TABLE::Format( OUTPUTFORMATTER* out, int nestLevel ) const + throw() +{ + out->Print( nestLevel, "(fp_lib_table\n" ); + + for( LIB_TABLE_ROWS_CITER it = rows.begin(); it != rows.end(); ++it ) + it->Format( out, nestLevel+1 ); + + out->Print( nestLevel, ")\n" ); +} + + wxArrayString FP_LIB_TABLE::FootprintEnumerate( const wxString& aNickname ) { const FP_LIB_TABLE_ROW* row = FindRow( aNickname ); @@ -190,6 +642,30 @@ wxArrayString FP_LIB_TABLE::FootprintEnumerate( const wxString& aNickname ) } +const FP_LIB_TABLE_ROW* FP_LIB_TABLE::FindRow( const wxString& aNickname ) + throw( IO_ERROR ) +{ + FP_LIB_TABLE_ROW* row = dynamic_cast< FP_LIB_TABLE_ROW* >( findRow( aNickname ) ); + + if( !row ) + { + wxString msg = wxString::Format( + _( "fp-lib-table files contain no library with nickname '%s'" ), + GetChars( aNickname ) ); + + THROW_IO_ERROR( msg ); + } + + // We've been 'lazy' up until now, but it cannot be deferred any longer, + // instantiate a PLUGIN of the proper kind if it is not already in this + // FP_LIB_TABLE_ROW. + if( !row->plugin ) + row->setPlugin( IO_MGR::PluginFind( row->type ) ); + + return row; +} + + MODULE* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname, const wxString& aFootprintName ) { const FP_LIB_TABLE_ROW* row = FindRow( aNickname ); @@ -280,423 +756,6 @@ void FP_LIB_TABLE::FootprintLibCreate( const wxString& aNickname ) } -const wxString FP_LIB_TABLE::GetDescription( const wxString& aNickname ) -{ - // use "no exception" form of find row: - const FP_LIB_TABLE_ROW* row = dynamic_cast< FP_LIB_TABLE_ROW* >( findRow( aNickname ) ); - - if( row ) - return row->GetDescr(); - else - return wxEmptyString; -} - - -void FP_LIB_TABLE::Parse( FP_LIB_TABLE_LEXER* in ) throw( IO_ERROR, PARSE_ERROR ) -{ - /* - (fp_lib_table - (lib (name NICKNAME)(descr DESCRIPTION)(type TYPE)(full_uri FULL_URI)(options OPTIONS)) - : - ) - - Elements after (name) are order independent. - */ - - T tok; - - // This table may be nested within a larger s-expression, or not. - // Allow for parser of that optional containing s-epression to have looked ahead. - if( in->CurTok() != T_fp_lib_table ) - { - in->NeedLEFT(); - if( ( tok = in->NextTok() ) != T_fp_lib_table ) - in->Expecting( T_fp_lib_table ); - } - - while( ( tok = in->NextTok() ) != T_RIGHT ) - { - FP_LIB_TABLE_ROW row; // reconstructed for each row in input stream. - - if( tok == T_EOF ) - in->Expecting( T_RIGHT ); - - if( tok != T_LEFT ) - in->Expecting( T_LEFT ); - - // in case there is a "row integrity" error, tell where later. - int lineNum = in->CurLineNumber(); - int offset = in->CurOffset(); - - if( ( tok = in->NextTok() ) != T_lib ) - in->Expecting( T_lib ); - - // (name NICKNAME) - in->NeedLEFT(); - - if( ( tok = in->NextTok() ) != T_name ) - in->Expecting( T_name ); - - in->NeedSYMBOLorNUMBER(); - - row.SetNickName( in->FromUTF8() ); - - in->NeedRIGHT(); - - // After (name), remaining (lib) elements are order independent, and in - // some cases optional. - bool sawType = false; - bool sawOpts = false; - bool sawDesc = false; - bool sawUri = false; - - while( ( tok = in->NextTok() ) != T_RIGHT ) - { - if( tok == T_EOF ) - in->Unexpected( T_EOF ); - - if( tok != T_LEFT ) - in->Expecting( T_LEFT ); - - tok = in->NeedSYMBOLorNUMBER(); - - switch( tok ) - { - case T_uri: - if( sawUri ) - in->Duplicate( tok ); - sawUri = true; - in->NeedSYMBOLorNUMBER(); - row.SetFullURI( in->FromUTF8() ); - break; - - case T_type: - if( sawType ) - in->Duplicate( tok ); - sawType = true; - in->NeedSYMBOLorNUMBER(); - row.SetType( in->FromUTF8() ); - break; - - case T_options: - if( sawOpts ) - in->Duplicate( tok ); - sawOpts = true; - in->NeedSYMBOLorNUMBER(); - row.SetOptions( in->FromUTF8() ); - break; - - case T_descr: - if( sawDesc ) - in->Duplicate( tok ); - sawDesc = true; - in->NeedSYMBOLorNUMBER(); - row.SetDescr( in->FromUTF8() ); - break; - - default: - in->Unexpected( tok ); - } - - in->NeedRIGHT(); - } - - if( !sawType ) - in->Expecting( T_type ); - - if( !sawUri ) - in->Expecting( T_uri ); - - // all nickNames within this table fragment must be unique, so we do not - // use doReplace in InsertRow(). (However a fallBack table can have a - // conflicting nickName and ours will supercede that one since in - // FindLib() we search this table before any fall back.) - if( !InsertRow( row ) ) - { - wxString msg = wxString::Format( - _( "'%s' is a duplicate footprint library nickName" ), - GetChars( row.GetNickName() ) ); - THROW_PARSE_ERROR( msg, in->CurSource(), in->CurLine(), lineNum, offset ); - } - } -} - - -void FP_LIB_TABLE::Format( OUTPUTFORMATTER* out, int nestLevel ) const - throw( IO_ERROR, boost::interprocess::lock_exception ) -{ - out->Print( nestLevel, "(fp_lib_table\n" ); - - for( FP_LIB_TABLE_ROWS_CITER it = rows.begin(); it != rows.end(); ++it ) - it->Format( out, nestLevel+1 ); - - out->Print( nestLevel, ")\n" ); -} - - -#define OPT_SEP '|' ///< options separator character - -PROPERTIES* FP_LIB_TABLE::ParseOptions( const std::string& aOptionsList ) -{ - if( aOptionsList.size() ) - { - const char* cp = &aOptionsList[0]; - const char* end = cp + aOptionsList.size(); - - PROPERTIES props; - std::string pair; - - // Parse all name=value pairs - while( cp < end ) - { - pair.clear(); - - // Skip leading white space. - while( cp < end && isspace( *cp ) ) - ++cp; - - // Find the end of pair/field - while( cp < end ) - { - if( *cp=='\\' && cp+1 FP_LIB_TABLE::GetLogicalLibs() -{ - // Only return unique logical library names. Use std::set::insert() to - // quietly reject any duplicates, which can happen when encountering a duplicate - // nickname from one of the fall back table(s). - - std::set unique; - std::vector ret; - const FP_LIB_TABLE* cur = this; - - do - { - for( FP_LIB_TABLE_ROWS_CITER it = cur->rows.begin(); it!=cur->rows.end(); ++it ) - { - unique.insert( it->GetNickName() ); - } - - } while( ( cur = cur->fallBack ) != 0 ); - - ret.reserve( unique.size() ); - - // DBG(printf( "%s: count:%zd\n", __func__, unique.size() );) - - // return a sorted, unique set of nicknames in a std::vector to caller - for( std::set::const_iterator it = unique.begin(); it!=unique.end(); ++it ) - { - //DBG(printf( " %s\n", TO_UTF8( *it ) );) - ret.push_back( *it ); - } - - return ret; -} - - -LIB_TABLE_ROW* FP_LIB_TABLE::findRow( const wxString& aNickName ) const -{ - FP_LIB_TABLE* cur = (FP_LIB_TABLE*) this; - - do - { - cur->ensureIndex(); - - INDEX_CITER it = cur->nickIndex.find( aNickName ); - - if( it != cur->nickIndex.end() ) - { - return &cur->rows[it->second]; // found - } - - // not found, search fall back table(s), if any - } while( ( cur = cur->fallBack ) != 0 ); - - return 0; // not found -} - - -const FP_LIB_TABLE_ROW* FP_LIB_TABLE::FindRowByURI( const wxString& aURI ) -{ - FP_LIB_TABLE* cur = this; - - do - { - cur->ensureIndex(); - - for( unsigned i = 0; i < cur->rows.size(); i++ ) - { - wxString uri = cur->rows[i].GetFullURI( true ); - - if( wxFileName::GetPathSeparator() == wxChar( '\\' ) && uri.Find( wxChar( '/' ) ) >= 0 ) - uri.Replace( "/", "\\" ); - - if( (wxFileName::IsCaseSensitive() && uri == aURI) - || (!wxFileName::IsCaseSensitive() && uri.Upper() == aURI.Upper() ) ) - { - return &cur->rows[i]; // found - } - } - - // not found, search fall back table(s), if any - } while( ( cur = cur->fallBack ) != 0 ); - - return 0; // not found -} - - -bool FP_LIB_TABLE::InsertRow( const FP_LIB_TABLE_ROW& aRow, bool doReplace ) -{ - ensureIndex(); - - INDEX_CITER it = nickIndex.find( aRow.GetNickName() ); - - if( it == nickIndex.end() ) - { - rows.push_back( aRow ); - nickIndex.insert( INDEX_VALUE( aRow.GetNickName(), rows.size() - 1 ) ); - return true; - } - - if( doReplace ) - { - rows[it->second] = aRow; - return true; - } - - return false; -} - - -const FP_LIB_TABLE_ROW* FP_LIB_TABLE::FindRow( const wxString& aNickname ) - throw( IO_ERROR ) -{ - FP_LIB_TABLE_ROW* row = dynamic_cast< FP_LIB_TABLE_ROW* >( findRow( aNickname ) ); - - if( !row ) - { - wxString msg = wxString::Format( - _( "fp-lib-table files contain no lib with nickname '%s'" ), - GetChars( aNickname ) ); - - THROW_IO_ERROR( msg ); - } - - // We've been 'lazy' up until now, but it cannot be deferred any longer, - // instantiate a PLUGIN of the proper kind if it is not already in this LIB_TABLE_ROW. - if( !row->plugin ) - row->setPlugin( IO_MGR::PluginFind( row->type ) ); - - return row; -} - - -// wxGetenv( wchar_t* ) is not re-entrant on linux. -// Put a lock on multithreaded use of wxGetenv( wchar_t* ), called from wxEpandEnvVars(), -// needed by bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ); -#include - -const wxString FP_LIB_TABLE::ExpandSubstitutions( const wxString& aString ) -{ -// Duplicate code: the same is now in common.cpp, due to the fact it is used -// in many other places than FP_LIB_TABLE -#if 0 - static MUTEX getenv_mutex; - - MUTLOCK lock( getenv_mutex ); - - // We reserve the right to do this another way, by providing our own member - // function. - return wxExpandEnvVars( aString ); -#else - return ExpandEnvVarSubstitutions( aString ); -#endif -} - - -bool FP_LIB_TABLE::IsEmpty( bool aIncludeFallback ) -{ - if( !aIncludeFallback || !fallBack ) - return rows.empty(); - - return rows.empty() && fallBack->IsEmpty( true ); -} - - MODULE* FP_LIB_TABLE::FootprintLoadWithOptionalNickname( const FPID& aFootprintId ) throw( IO_ERROR, PARSE_ERROR, boost::interprocess::lock_exception ) { @@ -714,11 +773,12 @@ MODULE* FP_LIB_TABLE::FootprintLoadWithOptionalNickname( const FPID& aFootprintI std::vector nicks = GetLogicalLibs(); // Search each library going through libraries alphabetically. - for( unsigned i = 0; i #include #include +#include + #define FP_LATE_ENVVAR 1 ///< late=1/early=0 environment variable expansion @@ -41,62 +43,68 @@ class OUTPUTFORMATTER; class MODULE; class FP_LIB_TABLE_LEXER; class FPID; +class FP_TBL_MODEL; +class LIB_TABLE_ROW; + + +/** + * Function new_clone + * + * Allows boost pointer containers to make clones of the data stored in them. Since they + * store pointers the data is cloned. Copying and assigning pointers would cause ownership + * issues if the standard C++ containers were used. + */ +LIB_TABLE_ROW* new_clone( const LIB_TABLE_ROW& aRow ); /** * Class LIB_TABLE_ROW * * holds a record identifying a library accessed by the appropriate #PLUGIN object in the - * #FP_LIB_TABLE. + * #LIB_TABLE. This is an abstract base class from which to derive specific library rows. */ -class LIB_TABLE_ROW +class LIB_TABLE_ROW : boost::noncopyable { public: - - LIB_TABLE_ROW() : - properties( 0 ) + LIB_TABLE_ROW() { } virtual ~LIB_TABLE_ROW() { - delete properties; } LIB_TABLE_ROW( const wxString& aNick, const wxString& aURI, const wxString& aOptions, const wxString& aDescr = wxEmptyString ) : nickName( aNick ), - description( aDescr ), - properties( 0 ) + description( aDescr ) { - SetOptions( aOptions ), + properties.reset(); + SetOptions( aOptions ); SetFullURI( aURI ); } - LIB_TABLE_ROW( const LIB_TABLE_ROW& aRow ); - - LIB_TABLE_ROW& operator=( const LIB_TABLE_ROW& r ); - bool operator==( const LIB_TABLE_ROW& r ) const; bool operator!=( const LIB_TABLE_ROW& r ) const { return !( *this == r ); } - //----------------------------------------------------------- - /** * Function GetNickName - * returns the short name of this library table row. + * + * @return the logical name of this library table row. */ const wxString& GetNickName() const { return nickName; } /** * Function SetNickName + * * changes the logical name of this library, useful for an editor. */ void SetNickName( const wxString& aNickName ) { nickName = aNickName; } /** * Function GetType + * * is a pure virtual function that returns the type of LIB represented by this row. */ virtual const wxString GetType() const = 0; @@ -111,6 +119,7 @@ public: /** * Function GetFullURI + * * returns the full location specifying URI for the LIB, either in original * UI form or in environment variable expanded form. * @@ -120,12 +129,14 @@ public: /** * Function SetFullURI + * * changes the full URI for the library. */ void SetFullURI( const wxString& aFullURI ); /** * Function GetOptions + * * returns the options string, which may hold a password or anything else needed to * instantiate the underlying LIB_SOURCE. */ @@ -138,24 +149,25 @@ public: /** * Function GetDescr + * * returns the description of the library referenced by this row. */ const wxString& GetDescr() const { return description; } /** * Function SetDescr + * * changes the description of the library referenced by this row. */ void SetDescr( const wxString& aDescr ) { description = aDescr; } /** * Function GetProperties + * * returns the constant PROPERTIES for this library (LIB_TABLE_ROW). These are * the "options" in a table. */ - const PROPERTIES* GetProperties() const { return properties; } - - //---------------------------------------------------------- + const PROPERTIES* GetProperties() const { return properties.get(); } /** * Function Format @@ -165,26 +177,42 @@ public: * * @param out is an #OUTPUTFORMATTER * @param nestLevel is the indentation level to base all lines of the output. - * Actual indentation will be 2 spaces for each nestLevel. + * Actual indentation will be 2 spaces for each nestLevel. */ void Format( OUTPUTFORMATTER* out, int nestLevel ) const throw( IO_ERROR, boost::interprocess::lock_exception ); -private: + static void Parse( std::unique_ptr< LIB_TABLE_ROW >& aRow, FP_LIB_TABLE_LEXER* in ) + throw( IO_ERROR, PARSE_ERROR ); - /** - * Function setProperties - * - * sets this LIB_TABLE_ROW's PROPERTIES by taking ownership of @a aProperties. - * - * @param aProperties ownership is given over to this LIB_TABLE_ROW. - */ - void setProperties( const PROPERTIES* aProperties ) + LIB_TABLE_ROW* clone() const { - delete properties; - properties = aProperties; + return do_clone(); } +protected: + LIB_TABLE_ROW( const LIB_TABLE_ROW& aRow ) : + nickName( aRow.nickName ), + uri_user( aRow.uri_user ), +#if !FP_LATE_ENVVAR + uri_expanded( aRow.uri_expanded ), +#endif + options( aRow.options ), + description( aRow.description ) + { + if( aRow.properties ) + properties.reset( new PROPERTIES( *aRow.properties.get() ) ); + else + properties.reset(); + } + + void operator=( const LIB_TABLE_ROW& aRow ); + +private: + virtual LIB_TABLE_ROW* do_clone() const = 0; + + void setProperties( PROPERTIES* aProperties ); + wxString nickName; wxString uri_user; ///< what user entered from UI or loaded from disk @@ -195,14 +223,19 @@ private: wxString options; wxString description; - const PROPERTIES* properties; + std::unique_ptr< PROPERTIES > properties; }; +/** + * Class FP_LIB_TABLE_ROW + * + * holds a record identifying a library accessed by the appropriate footprint library #PLUGIN + *& object in the #FP_LIB_TABLE. + */ class FP_LIB_TABLE_ROW : public LIB_TABLE_ROW { friend class FP_LIB_TABLE; - friend class DIALOG_FP_LIB_TABLE; public: typedef IO_MGR::PCB_FILE_T LIB_T; @@ -219,28 +252,38 @@ public: { } - FP_LIB_TABLE_ROW( const FP_LIB_TABLE_ROW& aRow ); - - FP_LIB_TABLE_ROW& operator=( const FP_LIB_TABLE_ROW& aRow ); - - /// Used in DIALOG_FP_LIB_TABLE for detecting an edit. bool operator==( const FP_LIB_TABLE_ROW& aRow ) const; bool operator!=( const FP_LIB_TABLE_ROW& aRow ) const { return !( *this == aRow ); } /** * Function GetType - * returns the type of LIB represented by this row. + * + * returns the type of footprint library table represented by this row. */ const wxString GetType() const override { return IO_MGR::ShowType( type ); } /** * Function SetType + * * changes the type represented by this row. */ void SetType( const wxString& aType ) override; +protected: + FP_LIB_TABLE_ROW( const FP_LIB_TABLE_ROW& aRow ) : + LIB_TABLE_ROW( aRow ), + type( aRow.type ) + { + } + private: + + virtual LIB_TABLE_ROW* do_clone() const override + { + return new FP_LIB_TABLE_ROW( *this ); + } + void setPlugin( PLUGIN* aPlugin ) { plugin.set( aPlugin ); @@ -252,19 +295,20 @@ private: /** - * Class FP_LIB_TABLE - * holds FP_LIB_TABLE::ROW records (rows), and can be searched based on library nickName. + * Class LIB_TABLE + * holds LIB_TABLE_ROW records (rows), and can be searched based on library nickname. *

- * This class owns the footprint library table, which is like fstab in concept and maps - * logical library name to the library URI, type, and options. It is heavily based on the SWEET - * parser work done by Dick Hollenbeck and can be seen in new/sch_lib_table.h. A footprint - * library table had the following columns: + * This class owns the library table, which is like fstab in concept and maps + * logical library name to the library URI, type, and options. It is heavily based on + * the SWEET parser work done by Dick Hollenbeck and can be seen in new/sch_lib_table.h. + * A library table has the following columns: *

    *
  • Logical Library Name (Nickname) *
  • Library Type, used to determine which plugin to load to access the library. *
  • Library URI. The full URI to the library source, form dependent on Type. *
  • Options, used for as yet to be defined information such as user names or passwords *
+ *

*

* The Library Type can be one of: *

    @@ -272,6 +316,7 @@ private: *
  • "ftp" *
  • "http" *
+ *

*

* For now, the Library URI types needed to support the various types can be one of those * shown below, which are typical of each type: @@ -280,37 +325,43 @@ private: *

  • "ftp://kicad.org/partlib/trunk" *
  • "http://kicad.org/partlib" * + *

    *

    - * The footprint library table is built up from several additive entries (table fragments), - * and the final table is a (conceptual) merging of the table fragments. Two - * anticipated sources of the entries are a personal table saved in the KiCad configuration - * and a project resident table that resides in project file. The project footprint table - * entries are considered a higher priority in the final dynamically assembled library table. - * An row in the project file contribution to the library table takes precedence over the - * personal table if there is a collision of logical library names. Otherwise, the entries - * simply combine without issue to make up the applicable library table. + * The library table is built up from several additive entries (table fragments), and the + * final table is a (conceptual) merging of the table fragments. Two anticipated sources + * of the entries are a personal table saved in the KiCad configuration and a project + * resident table that resides in project file. The project footprint table entries are + * considered a higher priority in the final dynamically assembled library table. An row + * in the project file contribution to the library table takes precedence over the personal + * table if there is a collision of logical library names. Otherwise, the entries simply + * combine without issue to make up the applicable library table. + *

    * * @author Wayne Stambaugh */ -class FP_LIB_TABLE : public PROJECT::_ELEM +class LIB_TABLE : public PROJECT::_ELEM { - friend class FP_LIB_TABLE_ROW; friend class DIALOG_FP_LIB_TABLE; + friend class FP_TBL_MODEL; public: + virtual void Parse( FP_LIB_TABLE_LEXER* aLexer ) throw() = 0; + + virtual void Format( OUTPUTFORMATTER* out, int nestLevel ) const throw() = 0; + /** - * Constructor FP_LIB_TABLE + * Constructor LIB_TABLE * builds a library table by pre-pending this table fragment in front of * @a aFallBackTable. Loading of this table fragment is done by using Parse(). * - * @param aFallBackTable is another FP_LIB_TABLE which is searched only when + * @param aFallBackTable is another LIB_TABLE which is searched only when * a row is not found in this table. No ownership is * taken of aFallBackTable. */ - FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable = NULL ); + LIB_TABLE( LIB_TABLE* aFallBackTable = NULL ); - ~FP_LIB_TABLE(); + virtual ~LIB_TABLE(); /// Delete all rows. void Clear() @@ -319,11 +370,12 @@ public: nickIndex.clear(); } - bool operator==( const FP_LIB_TABLE& r ) const + bool operator==( const LIB_TABLE& r ) const { if( rows.size() == r.rows.size() ) { unsigned i; + for( i = 0; i < rows.size() && rows[i] == r.rows[i]; ++i ) ; @@ -334,79 +386,197 @@ public: return false; } - bool operator!=( const FP_LIB_TABLE& r ) const { return !( *this == r ); } + bool operator!=( const LIB_TABLE& r ) const { return !( *this == r ); } - int GetCount() { return rows.size(); } + int GetCount() { return rows.size(); } - LIB_TABLE_ROW& At( int aIndex ) { return rows[aIndex]; } + LIB_TABLE_ROW* At( int aIndex ) { return &rows[aIndex]; } /** - * Function Parse - * fills this table fragment from information in the input stream \a aParser, which - * is a DSNLEXER customized for the grammar needed to describe instances of this object. - * The entire textual element spec is
    + * Function IsEmpty * - *
    -     * (fp_lib_table
    -     *   (lib (name LOGICAL)(descr DESCRIPTION)(uri FULL_URI)(type TYPE)(options OPTIONS))
    -     *   (lib (name LOGICAL)(descr DESCRIPTION)(uri FULL_URI)(type TYPE)(options OPTIONS))
    -     *   (lib (name LOGICAL)(descr DESCRIPTION)(uri FULL_URI)(type TYPE)(options OPTIONS))
    -     *  )
    -     * 
    + * @param aIncludeFallback is used to determine if the fallback table should be + * included in the test. * - * When this function is called, the input token stream given by \a aParser - * is assumed to be positioned at the '^' in the following example, i.e. just - * after the identifying keyword and before the content specifying stuff. - *
    - * (lib_table ^ (....) ) - * - * @param aParser is the input token stream of keywords and symbols. + * @return true if the footprint library table is empty. */ - void Parse( FP_LIB_TABLE_LEXER* aParser ) throw( IO_ERROR, PARSE_ERROR ); + bool IsEmpty( bool aIncludeFallback = true ); + + /** + * Function GetDescription + * + * @return the library description from @a aNickname, or an empty string + * if @a aNickname does not exist. + */ + const wxString GetDescription( const wxString& aNickname ); + + /** + * Function GetLogicalLibs + * + * returns the logical library names, all of them that are pertinent to + * a look up done on this LIB_TABLE. + */ + std::vector GetLogicalLibs(); + + /** + * Function InsertRow + * + * adds aRow if it does not already exist or if doReplace is true. If doReplace + * is not true and the key for aRow already exists, the function fails and returns false. + * + * The key for the table is the nickName, and all in this table must be unique. + * + * @param aRow is the new row to insert, or to forcibly add if doReplace is true. + * @param doReplace if true, means insert regardless of whether aRow's key already + * exists. If false, then fail if the key already exists. + * + * @return bool - true if the operation succeeded. + */ + bool InsertRow( LIB_TABLE_ROW* aRow, bool doReplace = false ); + + /** + * Function FindRowByURI + * + * @return a #LIB_TABLE_ROW pointer if \a aURI is found in this table or in any chained + * fallBack table fragments, else NULL. + */ + const LIB_TABLE_ROW* FindRowByURI( const wxString& aURI ); + + /** + * Function Load + * + * loads the library table using the path defined by \a aFileName aFallBackTable. + * + * @param aFileName contains the full path to the s-expression file. + * + * @throw IO_ERROR if an error occurs attempting to load the footprint library + * table. + */ + void Load( const wxString& aFileName ) throw( IO_ERROR ); + + /** + * Function Save + * + * writes this library table to \a aFileName in s-expression form. + * + * @param aFileName is the name of the file to write to. + */ + void Save( const wxString& aFileName ) const + throw( IO_ERROR, boost::interprocess::lock_exception ); /** * Function ParseOptions - * parses @a aOptionsList and places the result into a PROPERTIES object - * which is returned. If the options field is empty, then the returned PROPERTIES - * will be a NULL pointer. + * + * parses @a aOptionsList and places the result into a PROPERTIES object which is + * returned. If the options field is empty, then the returned PROPERTIES will be + * a NULL pointer. *

    * Typically aOptionsList comes from the "options" field within a LIB_TABLE_ROW and * the format is simply a comma separated list of name value pairs. e.g.: * [name1[=value1][|name2[=value2]]] etc. When using the UI to create or edit - * a fp lib table, this formatting is handled for you. + * a library table, this formatting is handled for you. + *

    */ static PROPERTIES* ParseOptions( const std::string& aOptionsList ); /** * Function FormatOptions + * * returns a list of options from the aProperties parameter. The name=value - * pairs will be separted with the '|' character. The =value portion may not + * pairs will be separated with the '|' character. The =value portion may not * be present. You might expect something like "name1=value1|name2=value2|flag_me". * Notice that flag_me does not have a value. This is ok. * * @param aProperties is the PROPERTIES to format or NULL. If NULL the returned - * string will be empty. + * string will be empty. */ static UTF8 FormatOptions( const PROPERTIES* aProperties ); /** - * Function Format - * serializes this object as utf8 text to an #OUTPUTFORMATTER, and tries to - * make it look good using multiple lines and indentation. + * Function ExpandSubstitutions * - * @param out is an #OUTPUTFORMATTER - * @param nestLevel is the indentation level to base all lines of the output. - * Actual indentation will be 2 spaces for each nestLevel. + * replaces any environment variable references with their values and is here to fully + * embellish the TABLE_ROW::uri in a platform independent way. This enables library + * tables to have platform dependent environment variables in them, allowing for a + * uniform table across platforms. */ - void Format( OUTPUTFORMATTER* out, int nestLevel ) const - throw( IO_ERROR, boost::interprocess::lock_exception ); + static const wxString ExpandSubstitutions( const wxString& aString ); + +protected: /** - * Function GetLogicalLibs - * returns the logical library names, all of them that are pertinent to - * a lookup done on this FP_LIB_TABLE. + * Function findRow + * returns a LIB_TABLE_ROW if aNickname is found in this table or in any chained + * fallBack table fragment, else NULL. */ - std::vector GetLogicalLibs(); + LIB_TABLE_ROW* findRow( const wxString& aNickname ) const; + + void reindex() + { + nickIndex.clear(); + + for( LIB_TABLE_ROWS_ITER it = rows.begin(); it != rows.end(); ++it ) + nickIndex.insert( INDEX_VALUE( it->GetNickName(), it - rows.begin() ) ); + } + + void ensureIndex() + { + // The dialog lib table editor may not maintain the nickIndex. + // Lazy indexing may be required. To handle lazy indexing, we must enforce + // that "nickIndex" is either empty or accurate, but never inaccurate. + if( !nickIndex.size() ) + reindex(); + } + + typedef boost::ptr_vector< LIB_TABLE_ROW > LIB_TABLE_ROWS; + typedef LIB_TABLE_ROWS::iterator LIB_TABLE_ROWS_ITER; + typedef LIB_TABLE_ROWS::const_iterator LIB_TABLE_ROWS_CITER; + + LIB_TABLE_ROWS rows; + + /// this is a non-owning index into the LIB_TABLE_ROWS table + typedef std::map INDEX; // "int" is std::vector array index + typedef INDEX::iterator INDEX_ITER; + typedef INDEX::const_iterator INDEX_CITER; + typedef INDEX::value_type INDEX_VALUE; + + /// this particular key is the nickName within each row. + INDEX nickIndex; + + LIB_TABLE* fallBack; +}; + + +class FP_LIB_TABLE : public LIB_TABLE +{ +public: + + virtual void Parse( FP_LIB_TABLE_LEXER* aLexer ) throw() override; + + virtual void Format( OUTPUTFORMATTER* out, int nestLevel ) const throw() override; + + /** + * Constructor FP_LIB_TABLE + * + * builds a footprint library table by pre-pending this table fragment in front of + * @a aFallBackTable. Loading of this table fragment is done by using Parse(). + * + * @param aFallBackTable is another FP_LIB_TABLE which is searched only when + * a row is not found in this table. No ownership is + * taken of aFallBackTable. + */ + FP_LIB_TABLE( FP_LIB_TABLE* aFallBackTable = NULL ); + + /** + * Function FindRow + * + * returns an FP_LIB_TABLE_ROW if \a aNickName is found in this table or in any chained + * fallBack table fragment. The #PLUGIN is loaded and attached to the "plugin" field + * of the #FP_LIB_TABLE_ROW if not already loaded. + * + * @throw IO_ERROR if \a aNickName cannot be found. + */ + const FP_LIB_TABLE_ROW* FindRow( const wxString& aNickName ) throw( IO_ERROR ); //-------------------------------- @@ -415,11 +585,9 @@ public: * returns a list of footprint names contained within the library given by * @a aNickname. * - * @param aNickname is a locator for the "library", it is a "name" - * in FP_LIB_TABLE::LIB_TABLE_ROW + * @param aNickname is a locator for the "library", it is a "name" in LIB_TABLE_ROW. * - * @return wxArrayString - is the array of available footprint names inside - * a library + * @return wxArrayString - is the array of available footprint names inside a library * * @throw IO_ERROR if the library cannot be found, or footprint cannot be loaded. */ @@ -427,10 +595,10 @@ public: /** * Function FootprintLoad + * * loads a footprint having @a aFootprintName from the library given by @a aNickname. * - * @param aNickname is a locator for the "library", it is a "name" - * in FP_LIB_TABLE::LIB_TABLE_ROW + * @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW * * @param aFootprintName is the name of the footprint to load. * @@ -453,30 +621,31 @@ public: /** * Function FootprintSave + * * will write @a aFootprint to an existing library given by @a aNickname. * If a footprint by the same name already exists, it is replaced. * - * @param aNickname is a locator for the "library", it is a "name" - * in FP_LIB_TABLE::LIB_TABLE_ROW + * @param aNickname is a locator for the "library", it is a "name" in LIB_TABLE_ROW * - * @param aFootprint is what to store in the library. The caller continues - * to own the footprint after this call. + * @param aFootprint is what to store in the library. The caller continues to own the + * footprint after this call. * - * @param aOverwrite when true means overwrite any existing footprint by the - * same name, else if false means skip the write and return SAVE_SKIPPED. + * @param aOverwrite when true means overwrite any existing footprint by the same name, + * else if false means skip the write and return SAVE_SKIPPED. * * @return SAVE_T - SAVE_OK or SAVE_SKIPPED. If error saving, then IO_ERROR is thrown. * * @throw IO_ERROR if there is a problem saving. */ - SAVE_T FootprintSave( const wxString& aNickname, const MODULE* aFootprint, bool aOverwrite = true ); + SAVE_T FootprintSave( const wxString& aNickname, const MODULE* aFootprint, + bool aOverwrite = true ); /** * Function FootprintDelete + * * deletes the @a aFootprintName from the library given by @a aNickname. * - * @param aNickname is a locator for the "library", it is a "name" - * in FP_LIB_TABLE::LIB_TABLE_ROW + * @param aNickname is a locator for the "library", it is a "name" in LIB_TABLE_ROW. * * @param aFootprintName is the name of a footprint to delete from the specified library. * @@ -486,7 +655,8 @@ public: /** * Function IsFootprintLibWritable - * returns true iff the library given by @a aNickname is writable. (Often + * + * returns true if the library given by @a aNickname is writable. (Often * system libraries are read only because of where they are installed.) * * @throw IO_ERROR if no library at aLibraryPath exists. @@ -503,7 +673,7 @@ public: * Function FootprintLoadWithOptionalNickname * loads a footprint having @a aFootprintId with possibly an empty nickname. * - * @param aFootprintId the [nickname] & fooprint name of the footprint to load. + * @param aFootprintId the [nickname] & footprint name of the footprint to load. * * @return MODULE* - if found caller owns it, else NULL if not found. * @@ -514,59 +684,6 @@ public: MODULE* FootprintLoadWithOptionalNickname( const FPID& aFootprintId ) throw( IO_ERROR, PARSE_ERROR, boost::interprocess::lock_exception ); - /** - * Function GetDescription - * returns the library desicription from @a aNickname, or an empty string - * if aNickname does not exist. - */ - const wxString GetDescription( const wxString& aNickname ); - - /** - * Function InsertRow - * adds aRow if it does not already exist or if doReplace is true. If doReplace - * is not true and the key for aRow already exists, the function fails and returns false. - * The key for the table is the nickName, and all in this table must be unique. - * @param aRow is the new row to insert, or to forcibly add if doReplace is true. - * @param doReplace if true, means insert regardless of whether aRow's key already - * exists. If false, then fail if the key already exists. - * @return bool - true if the operation succeeded. - */ - bool InsertRow( const FP_LIB_TABLE_ROW& aRow, bool doReplace = false ); - - /** - * Function FindRow - * returns a LIB_TABLE_ROW if aNickName is found in this table or in any chained - * fallBack table fragment. The PLUGIN is loaded and attached - * to the "plugin" field of the LIB_TABLE_ROW if not already loaded. - * - * @throw IO_ERROR if aNickName cannot be found. - */ - const FP_LIB_TABLE_ROW* FindRow( const wxString& aNickName ) throw( IO_ERROR ); - - /** - * Function FindRowByURI - * returns a #FP_LIB_TABLE::LIB_TABLE_ROW if aURE is found in this table or in any chained - * fallBack table fragments, else NULL. - */ - const FP_LIB_TABLE_ROW* FindRowByURI( const wxString& aURI ); - - /** - * Function IsEmpty - * @param aIncludeFallback is used to determine if the fallback table should be - * included in the test. - * @return true if the footprint library table is empty. - */ - bool IsEmpty( bool aIncludeFallback = true ); - - /** - * Function ExpandSubstitutions - * replaces any environment variable references with their values and is - * here to fully embellish the LIB_TABLE_ROW::uri in a platform independent way. - * This enables (fp_lib_table)s to have platform dependent environment - * variables in them, allowing for a uniform table across platforms. - */ - static const wxString ExpandSubstitutions( const wxString& aString ); - /** * Function LoadGlobalTable * loads the global footprint library table into \a aTable. @@ -585,90 +702,21 @@ public: /** * Function GetGlobalTableFileName + * * @return the platform specific global footprint library path and file name. */ static wxString GetGlobalTableFileName(); -#if 0 - /** - * Function GetFileName - * @return the footprint library file name. - */ - static const wxString GetFileName(); -#endif - /** * Function GlobalPathEnvVarVariableName + * * returns the name of the environment variable used to hold the directory of * locally installed "KiCad sponsored" system footprint libraries. These can * be either legacy or pretty format. The only thing special about this * particular environment variable is that it is set automatically by - * KiCad on program startup, iff it is not set already in the environment. + * KiCad on program start up, if it is not set already in the environment. */ static const wxString GlobalPathEnvVariableName(); - - /** - * Function Load - * loads the footprint library table using the path defined in \a aFileName with - * \a aFallBackTable. - * - * @param aFileName contains the full path to the s-expression file. - * - * @throw IO_ERROR if an error occurs attempting to load the footprint library - * table. - */ - void Load( const wxString& aFileName ) throw( IO_ERROR ); - - /** - * Function Save - * writes this table to aFileName in s-expression form. - * @param aFileName is the name of the file to write to. - */ - void Save( const wxString& aFileName ) const - throw( IO_ERROR, boost::interprocess::lock_exception ); - -protected: - - /** - * Function findRow - * returns a LIB_TABLE_ROW if aNickname is found in this table or in any chained - * fallBack table fragment, else NULL. - */ - LIB_TABLE_ROW* findRow( const wxString& aNickname ) const; - - void reindex() - { - nickIndex.clear(); - - for( FP_LIB_TABLE_ROWS_CITER it = rows.begin(); it != rows.end(); ++it ) - nickIndex.insert( INDEX_VALUE( it->GetNickName(), it - rows.begin() ) ); - } - - void ensureIndex() - { - // The dialog lib table editor may not maintain the nickIndex. - // Lazy indexing may be required. To handle lazy indexing, we must enforce - // that "nickIndex" is either empty or accurate, but never inaccurate. - if( !nickIndex.size() ) - reindex(); - } - - typedef std::vector FP_LIB_TABLE_ROWS; - typedef FP_LIB_TABLE_ROWS::iterator FP_LIB_TABLE_ROWS_ITER; - typedef FP_LIB_TABLE_ROWS::const_iterator FP_LIB_TABLE_ROWS_CITER; - - FP_LIB_TABLE_ROWS rows; - - /// this is a non-owning index into the LIB_TABLE_ROWS table - typedef std::map INDEX; // "int" is std::vector array index - typedef INDEX::iterator INDEX_ITER; - typedef INDEX::const_iterator INDEX_CITER; - typedef INDEX::value_type INDEX_VALUE; - - /// this particular key is the nickName within each row. - INDEX nickIndex; - - FP_LIB_TABLE* fallBack; }; diff --git a/pcbnew/dialogs/dialog_fp_lib_table.cpp b/pcbnew/dialogs/dialog_fp_lib_table.cpp index afa65e30b7..516b8bb591 100644 --- a/pcbnew/dialogs/dialog_fp_lib_table.cpp +++ b/pcbnew/dialogs/dialog_fp_lib_table.cpp @@ -74,9 +74,9 @@ public: * is a copy constructor that builds a wxGridTableBase (table model) by wrapping * an FP_LIB_TABLE. */ - FP_TBL_MODEL( const FP_LIB_TABLE& aTableToEdit ) : - FP_LIB_TABLE( aTableToEdit ) // copy constructor + FP_TBL_MODEL( const FP_LIB_TABLE& aTableToEdit ) { + rows = aTableToEdit.rows; } //------------------------------------------------ @@ -88,15 +88,15 @@ public: { if( unsigned( aRow ) < rows.size() ) { - const FP_LIB_TABLE_ROW& r = rows[aRow]; + const LIB_TABLE_ROW* r = &rows[aRow]; switch( aCol ) { - case COL_NICKNAME: return r.GetNickName(); - case COL_URI: return r.GetFullURI(); - case COL_TYPE: return r.GetType(); - case COL_OPTIONS: return r.GetOptions(); - case COL_DESCR: return r.GetDescr(); + case COL_NICKNAME: return r->GetNickName(); + case COL_URI: return r->GetFullURI(); + case COL_TYPE: return r->GetType(); + case COL_OPTIONS: return r->GetOptions(); + case COL_DESCR: return r->GetDescr(); default: ; // fall thru to wxEmptyString } @@ -109,15 +109,15 @@ public: { if( unsigned( aRow ) < rows.size() ) { - FP_LIB_TABLE_ROW& r = rows[aRow]; + LIB_TABLE_ROW* r = &rows[aRow]; switch( aCol ) { - case COL_NICKNAME: r.SetNickName( aValue ); break; - case COL_URI: r.SetFullURI( aValue ); break; - case COL_TYPE: r.SetType( aValue ); break; - case COL_OPTIONS: r.SetOptions( aValue ); break; - case COL_DESCR: r.SetDescr( aValue ); break; + case COL_NICKNAME: r->SetNickName( aValue ); break; + case COL_URI: r->SetFullURI( aValue ); break; + case COL_TYPE: r->SetType( aValue ); break; + case COL_OPTIONS: r->SetOptions( aValue ); break; + case COL_DESCR: r->SetDescr( aValue ); break; } } } @@ -131,7 +131,11 @@ public: { if( aPos < rows.size() ) { - rows.insert( rows.begin() + aPos, aNumRows, FP_LIB_TABLE_ROW() ); + for( size_t i = 0; i < aNumRows; i++ ) + { + rows.insert( rows.begin() + i, + dynamic_cast< LIB_TABLE_ROW* >( new FP_LIB_TABLE_ROW ) ); + } // use the (wxGridStringTable) source Luke. if( GetView() ) @@ -146,6 +150,7 @@ public: return true; } + return false; } @@ -153,7 +158,7 @@ public: { // do not modify aNumRows, original value needed for wxGridTableMessage below for( int i = aNumRows; i; --i ) - rows.push_back( FP_LIB_TABLE_ROW() ); + rows.push_back( new FP_LIB_TABLE_ROW ); if( GetView() ) { @@ -173,7 +178,7 @@ public: // aPos+aNumRows may wrap here, so both ends of the range are tested. if( aPos < rows.size() && aPos + aNumRows <= rows.size() ) { - FP_LIB_TABLE_ROWS_ITER start = rows.begin() + aPos; + LIB_TABLE_ROWS_ITER start = rows.begin() + aPos; rows.erase( start, start + aNumRows ); if( GetView() ) @@ -188,13 +193,8 @@ public: return true; } - return false; - } - void Clear() override - { - rows.clear(); - nickIndex.clear(); + return false; } wxString GetColLabelValue( int aCol ) override @@ -267,9 +267,10 @@ protected: for( int i = 0; i < tmp_tbl.GetCount(); ++i ) { - tbl->At( cur_row+i ) = tmp_tbl.At( i ); + tbl->rows.replace( cur_row+i, tmp_tbl.At( i ) ); } } + m_grid->AutoSizeColumns( false ); } else @@ -364,8 +365,8 @@ public: // Gives a selection for each grid, mainly for delete lib button. // Without that, we do not see what lib will be deleted - m_global_grid->SelectRow(0); - m_project_grid->SelectRow(0); + m_global_grid->SelectRow( 0 ); + m_project_grid->SelectRow( 0 ); // for ALT+A handling, we want the initial focus to be on the first selected grid. m_cur_grid->SetFocus(); @@ -534,7 +535,6 @@ private: void deleteRowHandler( wxCommandEvent& event ) override { -#if 1 int currRow = getCursorRow(); wxArrayInt selectedRows = m_cur_grid->GetSelectedRows(); @@ -553,36 +553,29 @@ private: m_cur_grid->SetGridCursor(m_cur_grid->GetNumberRows()-1, getCursorCol() ); m_cur_grid->SelectRow( m_cur_grid->GetGridCursorRow() ); -#else - int rowCount = m_cur_grid->GetNumberRows(); - int curRow = getCursorRow(); - - if( curRow >= 0 ) - { - m_cur_grid->DeleteRows( curRow ); - - if( curRow && curRow == rowCount - 1 ) - { - m_cur_grid->SetGridCursor( curRow-1, getCursorCol() ); - } - } -#endif } void moveUpHandler( wxCommandEvent& event ) override { - int curRow = getCursorRow(); + wxArrayInt rowsSelected = m_cur_grid->GetSelectedRows(); + + if( rowsSelected.GetCount() == 0 ) + return; + + // @todo: add multiple selection moves. + int curRow = rowsSelected[0]; + if( curRow >= 1 ) { int curCol = getCursorCol(); FP_TBL_MODEL* tbl = cur_model(); - ROW move_me = tbl->rows[curRow]; + boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me = + tbl->rows.release( tbl->rows.begin() + curRow ); - tbl->rows.erase( tbl->rows.begin() + curRow ); --curRow; - tbl->rows.insert( tbl->rows.begin() + curRow, move_me ); + tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() ); if( tbl->GetView() ) { @@ -603,18 +596,25 @@ private: void moveDownHandler( wxCommandEvent& event ) override { + wxArrayInt rowsSelected = m_cur_grid->GetSelectedRows(); + + if( rowsSelected.GetCount() == 0 ) + return; + FP_TBL_MODEL* tbl = cur_model(); - int curRow = getCursorRow(); + // @todo: add multiple selection moves. + int curRow = rowsSelected[0]; + if( unsigned( curRow + 1 ) < tbl->rows.size() ) { - int curCol = getCursorCol(); + int curCol = getCursorCol(); - ROW move_me = tbl->rows[curRow]; + boost::ptr_vector< LIB_TABLE_ROW >::auto_type move_me = + tbl->rows.release( tbl->rows.begin() + curRow ); - tbl->rows.erase( tbl->rows.begin() + curRow ); - ++curRow; - tbl->rows.insert( tbl->rows.begin() + curRow, move_me ); + ++curRow; + tbl->rows.insert( tbl->rows.begin() + curRow, move_me.release() ); if( tbl->GetView() ) { @@ -639,17 +639,17 @@ private: if( tbl->GetNumberRows() ) { - int curRow = getCursorRow(); - ROW& row = tbl->rows[curRow]; + int curRow = getCursorRow(); + LIB_TABLE_ROW* row = &tbl->rows[curRow]; wxString result; - const wxString& options = row.GetOptions(); + const wxString& options = row->GetOptions(); - InvokePluginOptionsEditor( this, row.GetNickName(), row.GetType(), options, &result ); + InvokePluginOptionsEditor( this, row->GetNickName(), row->GetType(), options, &result ); if( options != result ) { - row.SetOptions( result ); + row->SetOptions( result ); // all but options: m_cur_grid->AutoSizeColumn( COL_NICKNAME, false ); @@ -690,7 +690,9 @@ private: { dialogRet |= 1; - *m_global = *global_model(); + m_global->Clear(); + m_global->rows.transfer( m_global->rows.end(), global_model()->rows.begin(), + global_model()->rows.end(), global_model()->rows ); m_global->reindex(); } @@ -698,7 +700,9 @@ private: { dialogRet |= 2; - *m_project = *project_model(); + m_project->Clear(); + m_project->rows.transfer( m_project->rows.end(), project_model()->rows.begin(), + project_model()->rows.end(), project_model()->rows ); m_project->reindex(); } @@ -769,6 +773,7 @@ private: m_path_subs_grid->AppendRows( unique.size() ); row = 0; + for( SET_CITER it = unique.begin(); it != unique.end(); ++it, ++row ) { wxString evName = *it; @@ -786,17 +791,18 @@ private: //-------------------------------------- // caller's tables are modified only on OK button and successful verification. - FP_LIB_TABLE* m_global; - FP_LIB_TABLE* m_project; + FP_LIB_TABLE* m_global; + FP_LIB_TABLE* m_project; - FP_TBL_MODEL* global_model() const { return (FP_TBL_MODEL*) m_global_grid->GetTable(); } - FP_TBL_MODEL* project_model() const { return (FP_TBL_MODEL*) m_project_grid->GetTable(); } - FP_TBL_MODEL* cur_model() const { return (FP_TBL_MODEL*) m_cur_grid->GetTable(); } + FP_TBL_MODEL* global_model() const { return (FP_TBL_MODEL*) m_global_grid->GetTable(); } + FP_TBL_MODEL* project_model() const { return (FP_TBL_MODEL*) m_project_grid->GetTable(); } + FP_TBL_MODEL* cur_model() const { return (FP_TBL_MODEL*) m_cur_grid->GetTable(); } - wxGrid* m_cur_grid; ///< changed based on tab choice - static int m_pageNdx; ///< Remember the last notebook page selected during a session + wxGrid* m_cur_grid; ///< changed based on tab choice + static int m_pageNdx; ///< Remember the last notebook page selected during a session }; + int DIALOG_FP_LIB_TABLE::m_pageNdx = 0; @@ -846,7 +852,8 @@ void DIALOG_FP_LIB_TABLE::OnClickLibraryWizard( wxCommandEvent& event ) } -int InvokePcbLibTableEditor( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ) +int InvokePcbLibTableEditor( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, + FP_LIB_TABLE* aProject ) { DIALOG_FP_LIB_TABLE dlg( aParent, aGlobal, aProject ); @@ -856,7 +863,8 @@ int InvokePcbLibTableEditor( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, F } -int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ) +int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, + FP_LIB_TABLE* aProject ) { WIZARD_FPLIB_TABLE dlg( aParent ); @@ -875,10 +883,10 @@ int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_ if( it->GetStatus() == WIZARD_FPLIB_TABLE::LIBRARY::INVALID ) continue; - FP_LIB_TABLE_ROW row( it->GetDescription(), - it->GetAutoPath( scope ), - it->GetPluginName(), - wxEmptyString ); // options + FP_LIB_TABLE_ROW* row = new FP_LIB_TABLE_ROW( it->GetDescription(), + it->GetAutoPath( scope ), + it->GetPluginName(), + wxEmptyString ); // options fp_tbl->InsertRow( row ); } } diff --git a/pcbnew/dialogs/dialog_fp_plugin_options.cpp b/pcbnew/dialogs/dialog_fp_plugin_options.cpp index f54f33b702..c823c8be79 100644 --- a/pcbnew/dialogs/dialog_fp_plugin_options.cpp +++ b/pcbnew/dialogs/dialog_fp_plugin_options.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2013 CERN - * Copyright (C) 2013 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2013-2016 KiCad Developers, see change_log.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 @@ -73,7 +73,7 @@ public: // Fill the grid with existing aOptions string options = TO_UTF8( aOptions ); - PROPERTIES* props = FP_LIB_TABLE::ParseOptions( options ); + PROPERTIES* props = LIB_TABLE::ParseOptions( options ); if( props ) { @@ -192,7 +192,7 @@ private: } } - return FP_LIB_TABLE::FormatOptions( &props ); + return LIB_TABLE::FormatOptions( &props ); } void saveColSizes() diff --git a/pcbnew/dialogs/wizard_add_fplib.cpp b/pcbnew/dialogs/wizard_add_fplib.cpp index 1d0ccbfc77..d909759f76 100644 --- a/pcbnew/dialogs/wizard_add_fplib.cpp +++ b/pcbnew/dialogs/wizard_add_fplib.cpp @@ -4,7 +4,7 @@ * Copyright (C) 2015 CERN * @author Maciej Suminski * Copyright (C) 2014-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2016 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 diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index 31055f6ed8..bc6e0f9ac6 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -381,7 +381,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP { wxString wx_pretty_dir = pretty_dir; - wx_pretty_dir = FP_LIB_TABLE::ExpandSubstitutions( wx_pretty_dir ); + wx_pretty_dir = LIB_TABLE::ExpandSubstitutions( wx_pretty_dir ); wxFileName wx_pretty_fn = wx_pretty_dir; diff --git a/pcbnew/invoke_pcb_dialog.h b/pcbnew/invoke_pcb_dialog.h index aa5ad14e6b..e74e8a2325 100644 --- a/pcbnew/invoke_pcb_dialog.h +++ b/pcbnew/invoke_pcb_dialog.h @@ -5,7 +5,7 @@ /* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2013 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2013-2016 KiCad Developers, see change_log.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 @@ -71,7 +71,8 @@ class PCB_PLOT_PARAMS; * @return int - bits 0 and 1 tell whether a change was made to the @a aGlobal * and/or the @a aProject table, respectively. If set, table was modified. */ -int InvokePcbLibTableEditor( wxTopLevelWindow* aCaller, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ); +int InvokePcbLibTableEditor( wxTopLevelWindow* aCaller, FP_LIB_TABLE* aGlobal, + FP_LIB_TABLE* aProject ); /** * Function InvokeFootprintWizard @@ -88,7 +89,8 @@ int InvokePcbLibTableEditor( wxTopLevelWindow* aCaller, FP_LIB_TABLE* aGlobal, F * 2 - changes in the project table * 3 - changes in both tables */ -int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, FP_LIB_TABLE* aProject ); +int InvokeFootprintWizard( wxTopLevelWindow* aParent, FP_LIB_TABLE* aGlobal, + FP_LIB_TABLE* aProject ); /** * Function Invoke3DShapeLibsDownloaderWizard diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp index 4a62c43ee0..cf87d8a9db 100644 --- a/pcbnew/modview_frame.cpp +++ b/pcbnew/modview_frame.cpp @@ -727,7 +727,7 @@ void FOOTPRINT_VIEWER_FRAME::UpdateTitle() if( getCurNickname().size() ) { FP_LIB_TABLE* libtable = Prj().PcbFootprintLibs(); - const FP_LIB_TABLE_ROW* row = libtable->FindRow( getCurNickname() ); + const LIB_TABLE_ROW* row = libtable->FindRow( getCurNickname() ); if( row ) title << L" \u2014 " << row->GetFullURI( true );