From e1900161a7320107cf8a6ae91c81eed83641eeea Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Thu, 13 Feb 2020 08:39:52 -0500 Subject: [PATCH] Eeschema: implement new symbol library file s-expression formatter. CHANGES: Symbol library file format has been converted to s-expressions. Add support code for picking apart symbols at some future junction that will allow full inheritance conversion of existing symbol libraries. For now, symbols arranged by unit and body style numbers are nested for round robin testing of symbol libraries once the parser is complete. --- common/base_units.cpp | 8 +- common/wildcards_and_files_ext.cpp | 11 +- eeschema/CMakeLists.txt | 9 + eeschema/class_libentry.cpp | 160 ++- eeschema/class_libentry.h | 40 +- eeschema/lib_arc.cpp | 11 +- eeschema/lib_arc.h | 5 +- eeschema/lib_bezier.cpp | 9 +- eeschema/lib_bezier.h | 10 +- eeschema/lib_circle.cpp | 9 +- eeschema/lib_circle.h | 5 +- eeschema/lib_field.cpp | 21 +- eeschema/lib_field.h | 9 +- eeschema/lib_item.cpp | 21 +- eeschema/lib_item.h | 21 +- eeschema/lib_pin.cpp | 41 +- eeschema/lib_pin.h | 8 +- eeschema/lib_polyline.cpp | 9 +- eeschema/lib_polyline.h | 5 +- eeschema/lib_rectangle.cpp | 9 +- eeschema/lib_rectangle.h | 5 +- eeschema/lib_text.cpp | 13 +- eeschema/lib_text.h | 5 +- eeschema/libedit/lib_manager.cpp | 10 +- eeschema/libedit/lib_manager.h | 4 +- eeschema/libedit/libedit.cpp | 22 +- eeschema/sch_base_frame.cpp | 4 +- eeschema/sch_io_mgr.cpp | 14 +- eeschema/sch_io_mgr.h | 4 +- eeschema/sch_sexpr_plugin.cpp | 2025 ++++++++++++++++++++++++++++ eeschema/sch_sexpr_plugin.h | 171 +++ eeschema/symbol_lib.keywords | 59 + include/wildcards_and_files_ext.h | 4 +- qa/eeschema/test_lib_part.cpp | 67 +- 34 files changed, 2747 insertions(+), 81 deletions(-) create mode 100644 eeschema/sch_sexpr_plugin.cpp create mode 100644 eeschema/sch_sexpr_plugin.h create mode 100644 eeschema/symbol_lib.keywords diff --git a/common/base_units.cpp b/common/base_units.cpp index 0c42fd4a82..318cb9fe48 100644 --- a/common/base_units.cpp +++ b/common/base_units.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 CERN - * Copyright (C) 1992-2018 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 1992-2020 KiCad Developers, see change_log.txt for contributors. * * * This program is free software; you can redistribute it and/or @@ -181,7 +181,7 @@ wxString MessageTextFromValue( EDA_UNITS aUnits, double aValue, bool aUseMils, E else { #if defined( EESCHEMA ) - format = wxT( "%.2f" ); + format = wxT( "%.4f" ); #else format = wxT( "%.3f" ); #endif @@ -526,9 +526,7 @@ std::string FormatInternalUnits( int aValue ) double engUnits = aValue; int len; -#ifndef EESCHEMA engUnits /= IU_PER_MM; -#endif if( engUnits != 0.0 && fabs( engUnits ) <= 0.0001 ) { @@ -537,11 +535,9 @@ std::string FormatInternalUnits( int aValue ) while( --len > 0 && buf[len] == '0' ) buf[len] = '\0'; -#ifndef EESCHEMA if( buf[len] == '.' ) buf[len] = '\0'; else -#endif ++len; } else diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 8ccd3c9c4a..71444ff26c 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008 Wayne Stambaugh - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 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 @@ -114,6 +114,7 @@ wxString AddFileExtListToFilter( const std::vector& aExts ) } +const std::string KiCadSymbolLibFileExtension( "kicad_sym" ); const std::string SchematicSymbolFileExtension( "sym" ); const std::string SchematicLibraryFileExtension( "lib" ); @@ -172,9 +173,15 @@ wxString SchematicSymbolFileWildcard() } +wxString KiCadSymbolLibFileWildcard() +{ + return _( "KiCad symbol library files" ) + AddFileExtListToFilter( { "kicad_sym" } ); +} + + wxString SchematicLibraryFileWildcard() { - return _( "KiCad symbol library files" ) + AddFileExtListToFilter( { "lib" } ); + return _( "KiCad legacy symbol library files" ) + AddFileExtListToFilter( { "lib" } ); } diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 00eb51870e..69ef67d594 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -199,6 +199,7 @@ set( EESCHEMA_SRCS sch_plugin.cpp sch_preview_panel.cpp sch_screen.cpp + sch_sexpr_plugin.cpp sch_sheet.cpp sch_sheet_path.cpp sch_sheet_pin.cpp @@ -476,4 +477,12 @@ make_lexer( T_BOMCFG_T ) +make_lexer( + eeschema_kiface_objects + symbol_lib.keywords + symbol_lib_lexer.h + symbol_lib_keywords.cpp + TSYMBOL_LIB_T + ) + add_subdirectory( plugins ) diff --git a/eeschema/class_libentry.cpp b/eeschema/class_libentry.cpp index 5b3189712c..df1841a177 100644 --- a/eeschema/class_libentry.cpp +++ b/eeschema/class_libentry.cpp @@ -90,7 +90,7 @@ LIB_PART::LIB_PART( const wxString& aName, LIB_PART* aParent, PART_LIB* aLibrary { m_dateLastEdition = 0; m_unitCount = 1; - m_pinNameOffset = Mils2iu( 40 ); + m_pinNameOffset = Mils2iu( DEFAULT_PIN_NAME_OFFSET ); m_options = ENTRY_NORMAL; m_unitsLocked = false; m_showPinNumbers = true; @@ -1123,3 +1123,161 @@ void LIB_PART::SetSubpartIdNotation( int aSep, int aFirstId ) if( aFirstId == '1' && aSep != 0 ) m_subpartFirstId = aFirstId; } + + +std::vector LIB_PART::GetUnitItems( int aUnit, int aConvert ) +{ + std::vector unitItems; + + for( LIB_ITEM& item : m_drawings ) + { + if( item.Type() == LIB_FIELD_T ) + continue; + + if( ( aConvert == -1 && item.GetUnit() == aUnit ) + || ( aUnit == -1 && item.GetConvert() == aConvert ) + || ( aUnit == item.GetUnit() && aConvert == item.GetConvert() ) ) + unitItems.push_back( &item ); + } + + return unitItems; +} + + +std::vector LIB_PART::GetUnitDrawItems() +{ + std::vector units; + + for( LIB_ITEM& item : m_drawings ) + { + if( item.Type() == LIB_FIELD_T ) + continue; + + int unit = item.GetUnit(); + int convert = item.GetConvert(); + + auto it = std::find_if( units.begin(), units.end(), + [unit, convert] ( const auto& a ) { + return a.m_unit == unit && a.m_convert == convert; + } ); + + if( it == units.end() ) + { + struct PART_UNITS newUnit; + newUnit.m_unit = item.GetUnit(); + newUnit.m_convert = item.GetConvert(); + newUnit.m_items.push_back( &item ); + units.emplace_back( newUnit ); + } + else + { + it->m_items.push_back( &item ); + } + } + + return units; +} + + +std::vector LIB_PART::GetUniqueUnits() +{ + int unitNum; + size_t i; + struct PART_UNITS unit; + std::vector compareDrawItems; + std::vector currentDrawItems; + std::vector uniqueUnits; + + // The first unit is guarenteed to be unique so always include it. + unit.m_unit = 1; + unit.m_convert = 1; + unit.m_items = GetUnitItems( 1, 1 ); + + // There are no unique units if there are no draw items other than fields. + if( unit.m_items.size() == 0 ) + return uniqueUnits; + + uniqueUnits.emplace_back( unit ); + + if( ( GetUnitCount() == 1 || UnitsLocked() ) && !HasConversion() ) + return uniqueUnits; + + currentDrawItems = unit.m_items; + + for( unitNum = 2; unitNum <= GetUnitCount(); unitNum++ ) + { + compareDrawItems = GetUnitItems( unitNum, 1 ); + + wxCHECK2_MSG( compareDrawItems.size() != 0, continue, + "Multiple unit symbol defined with empty units." ); + + if( currentDrawItems.size() != compareDrawItems.size() ) + { + unit.m_unit = unitNum; + unit.m_convert = 1; + unit.m_items = compareDrawItems; + uniqueUnits.emplace_back( unit ); + } + else + { + for( i = 0; i < currentDrawItems.size(); i++ ) + { + if( currentDrawItems[i]->compare( *compareDrawItems[i], + LIB_ITEM::COMPARE_FLAGS::UNIT ) != 0 ) + { + unit.m_unit = unitNum; + unit.m_convert = 1; + unit.m_items = compareDrawItems; + uniqueUnits.emplace_back( unit ); + } + } + } + } + + if( HasConversion() ) + { + currentDrawItems = GetUnitItems( 1, 2 ); + + if( ( GetUnitCount() == 1 || UnitsLocked() ) ) + { + unit.m_unit = 1; + unit.m_convert = 2; + unit.m_items = currentDrawItems; + uniqueUnits.emplace_back( unit ); + + return uniqueUnits; + } + + for( unitNum = 2; unitNum <= GetUnitCount(); unitNum++ ) + { + compareDrawItems = GetUnitItems( unitNum, 2 ); + + wxCHECK2_MSG( compareDrawItems.size() != 0, continue, + "Multiple unit symbol defined with empty units." ); + + if( currentDrawItems.size() != compareDrawItems.size() ) + { + unit.m_unit = unitNum; + unit.m_convert = 2; + unit.m_items = compareDrawItems; + uniqueUnits.emplace_back( unit ); + } + else + { + for( i = 0; i < currentDrawItems.size(); i++ ) + { + if( currentDrawItems[i]->compare( *compareDrawItems[i], + LIB_ITEM::COMPARE_FLAGS::UNIT ) != 0 ) + { + unit.m_unit = unitNum; + unit.m_convert = 2; + unit.m_items = compareDrawItems; + uniqueUnits.emplace_back( unit ); + } + } + } + } + } + + return uniqueUnits; +} diff --git a/eeschema/class_libentry.h b/eeschema/class_libentry.h index 238269f618..12af55b440 100644 --- a/eeschema/class_libentry.h +++ b/eeschema/class_libentry.h @@ -3,7 +3,7 @@ * * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008 Wayne Stambaugh - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -82,6 +82,14 @@ struct PART_DRAW_OPTIONS }; +struct PART_UNITS +{ + int m_unit; ///< The unit number. + int m_convert; ///< The alternate body style of the unit. + std::vector m_items; ///< The items unique to this unit and alternate body style. +}; + + /** * Define a library symbol object. * @@ -584,6 +592,36 @@ public: */ std::unique_ptr< LIB_PART > Flatten() const; + /** + * Return a list of LIB_ITEM objects separated by unit and convert number. + * + * @note This does not include LIB_FIELD objects since they are not associated with + * unit and/or convert numbers. + */ + std::vector GetUnitDrawItems(); + + /** + * Return a list of unit numbers that are unique to this symbol. + * + * If the symbol is inherited (alias), the unique units of the parent symbol are returned. + * When comparing pins, the pin number is ignored. + * + * @return a list of unique unit numbers and their associated draw items. + */ + std::vector GetUniqueUnits(); + + /** + * Return a list of item pointers for \a aUnit and \a aConvert for this symbol. + * + * @note #LIB_FIELD objects are not included. + * + * @param aUnit is the unit number of the item, -1 includes all units. + * @param aConvert is the alternate body styple of the item, -1 includes all body styles. + * + * @return a list of unit items. + */ + std::vector GetUnitItems( int aUnit, int aConvert ); + #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif diff --git a/eeschema/lib_arc.cpp b/eeschema/lib_arc.cpp index f4fa305621..62d14e80f8 100644 --- a/eeschema/lib_arc.cpp +++ b/eeschema/lib_arc.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2019 CERN * * This program is free software; you can redistribute it and/or @@ -74,7 +74,7 @@ bool LIB_ARC::HitTest( const wxPoint& aRefPoint, int aAccuracy ) const // We are on the circle, ensure we are only on the arc, i.e. between // m_ArcStart and m_ArcEnd - wxPoint startEndVector = twoPointVector( m_ArcStart, m_ArcEnd); + wxPoint startEndVector = twoPointVector( m_ArcStart, m_ArcEnd ); wxPoint startRelativePositionVector = twoPointVector( m_ArcStart, relativePosition ); wxPoint centerStartVector = twoPointVector( m_Pos, m_ArcStart ); @@ -132,10 +132,15 @@ EDA_ITEM* LIB_ARC::Clone() const } -int LIB_ARC::compare( const LIB_ITEM& aOther ) const +int LIB_ARC::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { wxASSERT( aOther.Type() == LIB_ARC_T ); + int retv = LIB_ITEM::compare( aOther ); + + if( retv ) + return retv; + const LIB_ARC* tmp = ( LIB_ARC* ) &aOther; if( m_Pos.x != tmp->m_Pos.x ) diff --git a/eeschema/lib_arc.h b/eeschema/lib_arc.h index 10ce460536..664c2cec77 100644 --- a/eeschema/lib_arc.h +++ b/eeschema/lib_arc.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 KiCad Developers, see change_log.txt for contributors. * Copyright (C) 2019 CERN * * This program is free software; you can redistribute it and/or @@ -139,7 +139,8 @@ private: * - Arc start angle. * - Arc end angle. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_bezier.cpp b/eeschema/lib_bezier.cpp index 0c2ee97946..8e9e81d22b 100644 --- a/eeschema/lib_bezier.cpp +++ b/eeschema/lib_bezier.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -51,10 +51,15 @@ EDA_ITEM* LIB_BEZIER::Clone() const } -int LIB_BEZIER::compare( const LIB_ITEM& aOther ) const +int LIB_BEZIER::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { wxASSERT( aOther.Type() == LIB_BEZIER_T ); + int retv = LIB_ITEM::compare( aOther ); + + if( retv ) + return retv; + const LIB_BEZIER* tmp = ( LIB_BEZIER* ) &aOther; if( m_BezierPoints.size() != tmp->m_BezierPoints.size() ) diff --git a/eeschema/lib_bezier.h b/eeschema/lib_bezier.h index 290f8e8c35..f328f60649 100644 --- a/eeschema/lib_bezier.h +++ b/eeschema/lib_bezier.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -29,8 +29,7 @@ /** - * LIB_BEZIER - * defines bezier curve graphic body item. + * Define a bezier curve graphic body item. */ class LIB_BEZIER : public LIB_ITEM { @@ -80,7 +79,7 @@ public: void MoveTo( const wxPoint& aPosition ) override; - wxPoint GetPosition() const override; + wxPoint GetPosition() const override; void MirrorHorizontal( const wxPoint& aCenter ) override; void MirrorVertical( const wxPoint& aCenter ) override; @@ -107,7 +106,8 @@ private: * - Bezier horizontal (X) point position. * - Bezier vertical (Y) point position. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_circle.cpp b/eeschema/lib_circle.cpp index 25a91023e5..cae301a324 100644 --- a/eeschema/lib_circle.cpp +++ b/eeschema/lib_circle.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -90,10 +90,15 @@ EDA_ITEM* LIB_CIRCLE::Clone() const } -int LIB_CIRCLE::compare( const LIB_ITEM& aOther ) const +int LIB_CIRCLE::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { wxASSERT( aOther.Type() == LIB_CIRCLE_T ); + int retv = LIB_ITEM::compare( aOther, aCompareFlags ); + + if( retv ) + return retv; + const LIB_CIRCLE* tmp = ( LIB_CIRCLE* ) &aOther; if( m_Pos.x != tmp->m_Pos.x ) diff --git a/eeschema/lib_circle.h b/eeschema/lib_circle.h index 653fa76ed8..8deb833a03 100644 --- a/eeschema/lib_circle.h +++ b/eeschema/lib_circle.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -107,7 +107,8 @@ private: * - Circle vertical (Y) position. * - Circle radius. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_field.cpp b/eeschema/lib_field.cpp index e14eb0add3..c2fac92953 100644 --- a/eeschema/lib_field.cpp +++ b/eeschema/lib_field.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -55,7 +55,7 @@ LIB_FIELD::LIB_FIELD( int idfield ) : } -LIB_FIELD::LIB_FIELD( int aID, wxString& aName ) : +LIB_FIELD::LIB_FIELD( int aID, const wxString& aName ) : LIB_ITEM( LIB_FIELD_T, NULL ) { Init( aID ); @@ -184,11 +184,16 @@ void LIB_FIELD::Copy( LIB_FIELD* aTarget ) const } -int LIB_FIELD::compare( const LIB_ITEM& other ) const +int LIB_FIELD::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { - wxASSERT( other.Type() == LIB_FIELD_T ); + wxASSERT( aOther.Type() == LIB_FIELD_T ); - const LIB_FIELD* tmp = ( LIB_FIELD* ) &other; + int retv = LIB_ITEM::compare( aOther, aCompareFlags ); + + if( retv ) + return retv; + + const LIB_FIELD* tmp = ( LIB_FIELD* ) &aOther; if( m_id != tmp->m_id ) return m_id - tmp->m_id; @@ -449,3 +454,9 @@ BITMAP_DEF LIB_FIELD::GetMenuImage() const { return move_xpm; } + + +bool LIB_FIELD::IsMandatory() const +{ + return m_id < MANDATORY_FIELDS; +} diff --git a/eeschema/lib_field.h b/eeschema/lib_field.h index 58a8656ea7..eb6acdcada 100644 --- a/eeschema/lib_field.h +++ b/eeschema/lib_field.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -84,7 +84,7 @@ public: LIB_FIELD( int idfield = 2 ); - LIB_FIELD( int aID, wxString& aName ); + LIB_FIELD( int aID, const wxString& aName ); LIB_FIELD( LIB_PART * aParent, int idfield = 2 ); @@ -198,6 +198,8 @@ public: EDA_ITEM* Clone() const override; + bool IsMandatory() const; + private: /** @@ -212,7 +214,8 @@ private: * - Field width. * - Field height. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; typedef std::vector< LIB_FIELD > LIB_FIELDS; diff --git a/eeschema/lib_item.cpp b/eeschema/lib_item.cpp index ee0297c4c3..9db4020cd6 100644 --- a/eeschema/lib_item.cpp +++ b/eeschema/lib_item.cpp @@ -73,12 +73,25 @@ void LIB_ITEM::GetMsgPanelInfo( EDA_UNITS aUnits, MSG_PANEL_ITEMS& aList ) } +int LIB_ITEM::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const +{ + if( Type() != aOther.Type() ) + return Type() - aOther.Type(); + + // When comparing unit LIB_ITEM objects, we ignore the unit number. + if( !( aCompareFlags & COMPARE_FLAGS::UNIT ) && m_Unit != aOther.m_Unit ) + return m_Unit - aOther.m_Unit; + + if( !( aCompareFlags & COMPARE_FLAGS::UNIT ) && m_Convert != aOther.m_Convert ) + return m_Convert - m_Convert; + + return 0; +} + + bool LIB_ITEM::operator==( const LIB_ITEM& aOther ) const { - return ( ( Type() == aOther.Type() ) - && ( m_Unit == aOther.m_Unit ) - && ( m_Convert == aOther.m_Convert ) - && compare( aOther ) == 0 ); + return compare( aOther ) == 0; } diff --git a/eeschema/lib_item.h b/eeschema/lib_item.h index 348316244d..4ae95633a6 100644 --- a/eeschema/lib_item.h +++ b/eeschema/lib_item.h @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jaen-pierre.charras at wanadoo.fr * Copyright (C) 2015 Wayne Stambaugh - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -106,6 +106,15 @@ public: // Define the enums for basic enum LIB_CONVERT : int { BASE = 1, DEMORGAN = 2 }; + /** + * The list of flags used by the #compare function. + * + * - NORMAL This compares everthing between two #LIB_ITEM objects. + * - UNIT This compare flag ignores unit and convert and pin number information when + * comparing #LIB_ITEM objects for unit comparison. + */ + enum COMPARE_FLAGS : int { NORMAL = 0x00, UNIT = 0x01 }; + /** * Provide a user-consumable name of the object type. Perform localization when * called so that run-time language selection works. @@ -309,7 +318,7 @@ public: void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif -private: +protected: /** * Provide the draw object specific comparison called by the == and < operators. @@ -321,12 +330,18 @@ private: * - KICAD_T enum value. * - Result of derived classes comparison. * + * @note Make sure you call down to #LIB_ITEM::compare before doing any derived object + * comparisons or you will break the sorting using the symbol library file format. + * * @param aOther A reference to the other #LIB_ITEM to compare the arc against. + * @param aCompareFlags The flags used to perform the comparison. + * * @return An integer value less than 0 if the object is less than \a aOther ojbect, * zero if the object is equal to \a aOther object, or greater than 0 if the * object is greater than \a aOther object. */ - virtual int compare( const LIB_ITEM& aOther ) const = 0; + virtual int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const; }; diff --git a/eeschema/lib_pin.cpp b/eeschema/lib_pin.cpp index 9acde7dff7..ed5476c846 100644 --- a/eeschema/lib_pin.cpp +++ b/eeschema/lib_pin.cpp @@ -1237,18 +1237,25 @@ EDA_ITEM* LIB_PIN::Clone() const } -int LIB_PIN::compare( const LIB_ITEM& other ) const +int LIB_PIN::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { - wxASSERT( other.Type() == LIB_PIN_T ); + wxASSERT( aOther.Type() == LIB_PIN_T ); - const LIB_PIN* tmp = (LIB_PIN*) &other; + int retv = LIB_ITEM::compare( aOther, aCompareFlags ); - if( m_number != tmp->m_number ) + if( retv ) + return retv; + + const LIB_PIN* tmp = (LIB_PIN*) &aOther; + + // When comparing units, we do not compare the part numbers. If everything else is + // identical, then we can just renumber the parts for the inherited symbol. + if( !( aCompareFlags & COMPARE_FLAGS::UNIT ) && m_number != tmp->m_number ) return m_number.Cmp( tmp->m_number ); int result = m_name.CmpNoCase( tmp->m_name ); - if( result != 0 ) + if( result ) return result; if( m_position.x != tmp->m_position.x ) @@ -1257,6 +1264,30 @@ int LIB_PIN::compare( const LIB_ITEM& other ) const if( m_position.y != tmp->m_position.y ) return m_position.y - tmp->m_position.y; + if( m_length != tmp->m_length ) + return m_length - tmp->m_length; + + if( m_orientation != tmp->m_orientation ) + return m_orientation - tmp->m_orientation; + + if( m_shape != tmp->m_shape ) + return static_cast( m_shape ) - static_cast( tmp->m_shape ); + + if( m_type != tmp->m_type ) + return static_cast( m_type ) - static_cast( tmp->m_type ); + + if( m_attributes != tmp->m_attributes ) + return m_attributes - tmp->m_attributes; + + if( m_width != tmp->m_width ) + return m_width - tmp->m_width; + + if( m_numTextSize != tmp->m_numTextSize ) + return m_numTextSize - tmp->m_numTextSize; + + if( m_nameTextSize != tmp->m_nameTextSize ) + return m_nameTextSize - tmp->m_nameTextSize; + return 0; } diff --git a/eeschema/lib_pin.h b/eeschema/lib_pin.h index 7ef1b7d139..7e70f8621d 100644 --- a/eeschema/lib_pin.h +++ b/eeschema/lib_pin.h @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jaen-pierre.charras at wanadoo.fr * Copyright (C) 2015 Wayne Stambaugh - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -39,6 +39,9 @@ class SCH_COMPONENT; #include "pin_type.h" #include "class_libentry.h" +/// The offset of the pin name string from the end of the pin in mils. +#define DEFAULT_PIN_NAME_OFFSET 40 + // Circle diameter drawn at the active end of pins: #define TARGET_PIN_RADIUS Mils2iu( 15 ) @@ -472,7 +475,8 @@ private: * - Pin horizontal (X) position. * - Pin vertical (Y) position. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_polyline.cpp b/eeschema/lib_polyline.cpp index 6bc089b01e..93f9c8d198 100644 --- a/eeschema/lib_polyline.cpp +++ b/eeschema/lib_polyline.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -52,10 +52,15 @@ EDA_ITEM* LIB_POLYLINE::Clone() const } -int LIB_POLYLINE::compare( const LIB_ITEM& aOther ) const +int LIB_POLYLINE::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { wxASSERT( aOther.Type() == LIB_POLYLINE_T ); + int retv = LIB_ITEM::compare( aOther ); + + if( retv ) + return retv; + const LIB_POLYLINE* tmp = (LIB_POLYLINE*) &aOther; if( m_PolyPoints.size() != tmp->m_PolyPoints.size() ) diff --git a/eeschema/lib_polyline.h b/eeschema/lib_polyline.h index ed6e45555b..dea4453011 100644 --- a/eeschema/lib_polyline.h +++ b/eeschema/lib_polyline.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -119,7 +119,8 @@ private: * - Line segment point horizontal (X) position. * - Line segment point vertical (Y) position. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_rectangle.cpp b/eeschema/lib_rectangle.cpp index 15c30e7ad4..b0fb774be5 100644 --- a/eeschema/lib_rectangle.cpp +++ b/eeschema/lib_rectangle.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -52,10 +52,15 @@ EDA_ITEM* LIB_RECTANGLE::Clone() const } -int LIB_RECTANGLE::compare( const LIB_ITEM& aOther ) const +int LIB_RECTANGLE::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { wxASSERT( aOther.Type() == LIB_RECTANGLE_T ); + int retv = LIB_ITEM::compare( aOther ); + + if( retv ) + return retv; + const LIB_RECTANGLE* tmp = ( LIB_RECTANGLE* ) &aOther; if( m_Pos.x != tmp->m_Pos.x ) diff --git a/eeschema/lib_rectangle.h b/eeschema/lib_rectangle.h index 93c8156b19..4ef345542b 100644 --- a/eeschema/lib_rectangle.h +++ b/eeschema/lib_rectangle.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -105,7 +105,8 @@ private: * - Rectangle horizontal (X) end position. * - Rectangle vertical (Y) end position. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/lib_text.cpp b/eeschema/lib_text.cpp index bdea2c6466..7b16df8df1 100644 --- a/eeschema/lib_text.cpp +++ b/eeschema/lib_text.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -89,11 +89,16 @@ EDA_ITEM* LIB_TEXT::Clone() const } -int LIB_TEXT::compare( const LIB_ITEM& other ) const +int LIB_TEXT::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const { - wxASSERT( other.Type() == LIB_TEXT_T ); + wxASSERT( aOther.Type() == LIB_TEXT_T ); - const LIB_TEXT* tmp = ( LIB_TEXT* ) &other; + int retv = LIB_ITEM::compare( aOther, aCompareFlags ); + + if( retv ) + return retv; + + const LIB_TEXT* tmp = ( LIB_TEXT* ) &aOther; int result = GetText().CmpNoCase( tmp->GetText() ); diff --git a/eeschema/lib_text.h b/eeschema/lib_text.h index 92acc0c401..828d1b3824 100644 --- a/eeschema/lib_text.h +++ b/eeschema/lib_text.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com - * Copyright (C) 2004-2017 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -115,7 +115,8 @@ private: * - Text width. * - Text height. */ - int compare( const LIB_ITEM& aOther ) const override; + int compare( const LIB_ITEM& aOther, + LIB_ITEM::COMPARE_FLAGS aCompareFlags = LIB_ITEM::COMPARE_FLAGS::NORMAL ) const override; }; diff --git a/eeschema/libedit/lib_manager.cpp b/eeschema/libedit/lib_manager.cpp index 15b697760a..41054ec210 100644 --- a/eeschema/libedit/lib_manager.cpp +++ b/eeschema/libedit/lib_manager.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 CERN - * Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors. * @author Maciej Suminski * * This program is free software; you can redistribute it and/or @@ -31,8 +31,9 @@ #include #include #include -#include +#include #include +#include #include @@ -129,12 +130,13 @@ SYMBOL_LIB_TABLE_ROW* LIB_MANAGER::GetLibrary( const wxString& aLibrary ) const } -bool LIB_MANAGER::SaveLibrary( const wxString& aLibrary, const wxString& aFileName ) +bool LIB_MANAGER::SaveLibrary( const wxString& aLibrary, const wxString& aFileName, + SCH_IO_MGR::SCH_FILE_T aFileType ) { wxCHECK( LibraryExists( aLibrary ), false ); wxFileName fn( aFileName ); wxCHECK( !fn.FileExists() || fn.IsFileWritable(), false ); - SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) ); + SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( aFileType ) ); bool res = true; // assume all libraries are successfully saved auto it = m_libs.find( aLibrary ); diff --git a/eeschema/libedit/lib_manager.h b/eeschema/libedit/lib_manager.h index 3cdc11834f..fc58f61b4d 100644 --- a/eeschema/libedit/lib_manager.h +++ b/eeschema/libedit/lib_manager.h @@ -33,6 +33,7 @@ #include #include #include +#include #include class LIB_PART; @@ -235,7 +236,8 @@ public: * @param aFileName is the target file name. * @return True on success, false otherwise. */ - bool SaveLibrary( const wxString& aLibrary, const wxString& aFileName ); + bool SaveLibrary( const wxString& aLibrary, const wxString& aFileName, + SCH_IO_MGR::SCH_FILE_T aFileType = SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY ); /** * Reverts unsaved changes for a particular part. diff --git a/eeschema/libedit/libedit.cpp b/eeschema/libedit/libedit.cpp index e7e5203a4e..29f7612432 100644 --- a/eeschema/libedit/libedit.cpp +++ b/eeschema/libedit/libedit.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008 Wayne Stambaugh - * Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-2020 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 @@ -789,6 +789,7 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) { wxFileName fn; wxString msg; + SCH_IO_MGR::SCH_FILE_T aFileType = SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY; PROJECT& prj = Prj(); m_toolManager->RunAction( ACTIONS::cancelInteractive, true ); @@ -812,8 +813,11 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) fn.SetName( aLibrary ); fn.SetExt( SchematicLibraryFileExtension ); + wxString wildcards = SchematicLibraryFileWildcard(); + wildcards += "|" + KiCadSymbolLibFileWildcard(); + wxFileDialog dlg( this, wxString::Format( _( "Save Library \"%s\" As..." ), aLibrary ), - default_path, fn.GetFullName(), SchematicLibraryFileWildcard(), + default_path, fn.GetFullName(), wildcards, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if( dlg.ShowModal() == wxID_CANCEL ) @@ -821,10 +825,16 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) fn = dlg.GetPath(); - // The GTK file chooser doesn't return the file extension added to - // file name so add it here. - if( fn.GetExt().IsEmpty() ) + // Update the file extension and plugin if a different library type was selected. + if( dlg.GetFilterIndex() == 0 ) + { fn.SetExt( SchematicLibraryFileExtension ); + } + else + { + fn.SetExt( KiCadSymbolLibFileExtension ); + aFileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD; + } } else { @@ -848,7 +858,7 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile ) if( !backupFile( docFileName, "bck" ) ) return false; - if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath() ) ) + if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath(), aFileType ) ) { msg.Printf( _( "Failed to save changes to symbol library file \"%s\"" ), fn.GetFullPath() ); diff --git a/eeschema/sch_base_frame.cpp b/eeschema/sch_base_frame.cpp index c5abfc731f..f9a2cc81a8 100644 --- a/eeschema/sch_base_frame.cpp +++ b/eeschema/sch_base_frame.cpp @@ -196,8 +196,8 @@ void SCH_BASE_FRAME::UpdateStatusBar() break; case EDA_UNITS::MILLIMETRES: - absformatter = "X %.2f Y %.2f"; - locformatter = "dx %.2f dy %.2f dist %.2f"; + absformatter = "X %.4f Y %.4f"; + locformatter = "dx %.4f dy %.4f dist %.4f"; break; case EDA_UNITS::UNSCALED: diff --git a/eeschema/sch_io_mgr.cpp b/eeschema/sch_io_mgr.cpp index 32327e2b8f..e568e37475 100644 --- a/eeschema/sch_io_mgr.cpp +++ b/eeschema/sch_io_mgr.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN - * Copyright (C) 2016-2017 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2016-2020 KiCad Developers, see change_log.txt for contributors. * * @author Wayne Stambaugh * @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -55,6 +56,8 @@ SCH_PLUGIN* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType ) { case SCH_LEGACY: return new SCH_LEGACY_PLUGIN(); + case SCH_KICAD: + return new SCH_SEXPR_PLUGIN(); case SCH_EAGLE: return new SCH_EAGLE_PLUGIN(); } @@ -87,6 +90,9 @@ const wxString SCH_IO_MGR::ShowType( SCH_FILE_T aType ) case SCH_LEGACY: return wxString( wxT( "Legacy" ) ); + case SCH_KICAD: + return wxString( "KiCad" ); + case SCH_EAGLE: return wxString( wxT( "EAGLE" ) ); } @@ -101,6 +107,8 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType ) if( aType == wxT( "Legacy" ) ) return SCH_LEGACY; + else if( aType == "KiCad" ) + return SCH_KICAD; else if( aType == wxT( "EAGLE" ) ) return SCH_EAGLE; @@ -134,6 +142,10 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& a { ret = SCH_LEGACY; } + else if( fn.GetExt() == KiCadSymbolLibFileExtension ) + { + ret = SCH_KICAD; + } return ret; } diff --git a/eeschema/sch_io_mgr.h b/eeschema/sch_io_mgr.h index 0be6fa21b1..87c448c6e1 100644 --- a/eeschema/sch_io_mgr.h +++ b/eeschema/sch_io_mgr.h @@ -5,7 +5,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN - * Copyright (C) 2016-2017 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2016-2020 KiCad Developers, see CHANGELOG.TXT for contributors. * * @author Wayne Stambaugh * @@ -53,7 +53,7 @@ public: DEFINE_ENUM_VECTOR( SCH_FILE_T, { SCH_LEGACY, ///< Legacy Eeschema file formats prior to s-expression. -// SCH_KICAD, ///< The s-expression version of the schematic file formats. + SCH_KICAD, ///< The s-expression version of the schematic file formats. SCH_EAGLE, ///< Autodesk Eagle file format // Add your schematic type here. diff --git a/eeschema/sch_sexpr_plugin.cpp b/eeschema/sch_sexpr_plugin.cpp new file mode 100644 index 0000000000..cea118aa78 --- /dev/null +++ b/eeschema/sch_sexpr_plugin.cpp @@ -0,0 +1,2025 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * + * @author Wayne Stambaugh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for MAX_UNIT_COUNT_PER_PACKAGE definition +#include // for PropPowerSymsOnly definintion. +#include +#include +#include +#include + +using namespace TSYMBOL_LIB_T; + + +#define SEXPR_SYMBOL_LIB_FILE_VERSION 20200126 // Initial version. + + +#define SCH_PARSE_ERROR( text, reader, pos ) \ + THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \ + reader.LineNumber(), pos - reader.Line() ) + + +static const char* emptyString = ""; + +/** + * Fill token formatting helper. + */ +static void FormatFill( const LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, int aNestLevel ) +{ + wxCHECK_RET( aItem && aItem->IsFillable() && aItem->GetFillMode() != NO_FILL, + "Invalid fill item." ); + + aFormatter.Print( aNestLevel, "(fill (type %s))", + aItem->GetFillMode() == FILLED_SHAPE ? "outline" : "background" ); +} + + +static const char* GetPinElectricalTypeToken( ELECTRICAL_PINTYPE aType ) +{ + switch( aType ) + { + case ELECTRICAL_PINTYPE::PT_INPUT: + return SYMBOL_LIB_LEXER::TokenName( T_input ); + + case ELECTRICAL_PINTYPE::PT_OUTPUT: + return SYMBOL_LIB_LEXER::TokenName( T_output ); + + case ELECTRICAL_PINTYPE::PT_BIDI: + return SYMBOL_LIB_LEXER::TokenName( T_bidirectional ); + + case ELECTRICAL_PINTYPE::PT_TRISTATE: + return SYMBOL_LIB_LEXER::TokenName( T_tri_state ); + + case ELECTRICAL_PINTYPE::PT_PASSIVE: + return SYMBOL_LIB_LEXER::TokenName( T_passive ); + + case ELECTRICAL_PINTYPE::PT_UNSPECIFIED: + return SYMBOL_LIB_LEXER::TokenName( T_unspecified ); + + case ELECTRICAL_PINTYPE::PT_POWER_IN: + return SYMBOL_LIB_LEXER::TokenName( T_power_in ); + + case ELECTRICAL_PINTYPE::PT_POWER_OUT: + return SYMBOL_LIB_LEXER::TokenName( T_power_out ); + + case ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR: + return SYMBOL_LIB_LEXER::TokenName( T_open_collector ); + + case ELECTRICAL_PINTYPE::PT_OPENEMITTER: + return SYMBOL_LIB_LEXER::TokenName( T_open_emitter ); + + case ELECTRICAL_PINTYPE::PT_NC: + return SYMBOL_LIB_LEXER::TokenName( T_unconnected ); + + default: + wxFAIL_MSG( "Missing symbol library pin connection type" ); + } + + return emptyString; +} + + +static const char* GetPinShapeToken( GRAPHIC_PINSHAPE aShape ) +{ + switch( aShape ) + { + case GRAPHIC_PINSHAPE::LINE: + return SYMBOL_LIB_LEXER::TokenName( T_line ); + + case GRAPHIC_PINSHAPE::INVERTED: + return SYMBOL_LIB_LEXER::TokenName( T_inverted ); + + case GRAPHIC_PINSHAPE::CLOCK: + return SYMBOL_LIB_LEXER::TokenName( T_clock ); + + case GRAPHIC_PINSHAPE::INVERTED_CLOCK: + return SYMBOL_LIB_LEXER::TokenName( T_inverted_clock ); + + case GRAPHIC_PINSHAPE::INPUT_LOW: + return SYMBOL_LIB_LEXER::TokenName( T_input_low ); + + case GRAPHIC_PINSHAPE::CLOCK_LOW: + return SYMBOL_LIB_LEXER::TokenName( T_clock_low ); + + case GRAPHIC_PINSHAPE::OUTPUT_LOW: + return SYMBOL_LIB_LEXER::TokenName( T_output_low ); + + case GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK: + return SYMBOL_LIB_LEXER::TokenName( T_edge_clock_high ); + + case GRAPHIC_PINSHAPE::NONLOGIC: + return SYMBOL_LIB_LEXER::TokenName( T_non_logic ); + + default: + wxFAIL_MSG( "Missing symbol library pin shape type" ); + } + + return emptyString; +} + + +static float GetPinAngle( int aOrientation ) +{ + switch( aOrientation ) + { + case PIN_RIGHT: + return 0.0; + + case PIN_LEFT: + return 180.0; + + case PIN_UP: + return 90.0; + + case PIN_DOWN: + return 270.0; + + default: + wxFAIL_MSG( "Missing symbol library pin orientation type" ); + + return 0.0; + } +} + + +/** + * A cache assistant for the part library portion of the #SCH_PLUGIN API, and only for the + * #SCH_SEXPR_PLUGIN, so therefore is private to this implementation file, i.e. not placed + * into a header. + */ +class SCH_SEXPR_PLUGIN_CACHE +{ + static int m_modHash; // Keep track of the modification status of the library. + + wxString m_fileName; // Absolute path and file name. + wxFileName m_libFileName; // Absolute path and file name is required here. + wxDateTime m_fileModTime; + LIB_PART_MAP m_symbols; // Map of names of #LIB_PART pointers. + bool m_isWritable; + bool m_isModified; + int m_versionMajor; + int m_versionMinor; + int m_libType; // Is this cache a component or symbol library. + + void loadHeader( FILE_LINE_READER& aReader ); + static void loadField( std::unique_ptr& aPart, LINE_READER& aReader ); + static void loadDrawEntries( std::unique_ptr& aPart, LINE_READER& aReader, + int aMajorVersion, int aMinorVersion ); + static void loadFootprintFilters( std::unique_ptr& aPart, + LINE_READER& aReader ); + static LIB_ARC* loadArc( std::unique_ptr& aPart, LINE_READER& aReader ); + static LIB_CIRCLE* loadCircle( std::unique_ptr& aPart, LINE_READER& aReader ); + static LIB_TEXT* loadText( std::unique_ptr& aPart, LINE_READER& aReader, + int aMajorVersion, int aMinorVersion ); + static LIB_RECTANGLE* loadRectangle( std::unique_ptr& aPart, + LINE_READER& aReader ); + static LIB_PIN* loadPin( std::unique_ptr& aPart, LINE_READER& aReader ); + static LIB_POLYLINE* loadPolyLine( std::unique_ptr& aPart, LINE_READER& aReader ); + static LIB_BEZIER* loadBezier( std::unique_ptr& aPart, LINE_READER& aReader ); + + static FILL_T parseFillMode( LINE_READER& aReader, const char* aLine, + const char** aOutput ); + LIB_PART* removeSymbol( LIB_PART* aAlias ); + + static void saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, + int aNestLevel ); + static void saveArc( LIB_ARC* aArc, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + static void saveBezier( LIB_BEZIER* aBezier, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0 ); + static void saveCircle( LIB_CIRCLE* aCircle, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0 ); + static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + static void savePolyLine( LIB_POLYLINE* aPolyLine, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0 ); + static void saveRectangle( LIB_RECTANGLE* aRectangle, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0 ); + static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter, int aNestLevel = 0 ); + + static void saveDcmInfoAsFields( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0, int aFirstId = MANDATORY_FIELDS ); + + friend SCH_SEXPR_PLUGIN; + +public: + SCH_SEXPR_PLUGIN_CACHE( const wxString& aLibraryPath ); + ~SCH_SEXPR_PLUGIN_CACHE(); + + int GetModifyHash() const { return m_modHash; } + + // Most all functions in this class throw IO_ERROR exceptions. There are no + // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects. + // Catch these exceptions higher up please. + + /// Save the entire library to file m_libFileName; + void Save(); + + void Load(); + + void AddSymbol( const LIB_PART* aPart ); + + void DeleteSymbol( const wxString& aName ); + + // If m_libFileName is a symlink follow it to the real source file + wxFileName GetRealFile() const; + + wxDateTime GetLibModificationTime(); + + bool IsFile( const wxString& aFullPathAndFileName ) const; + + bool IsFileChanged() const; + + void SetModified( bool aModified = true ) { m_isModified = aModified; } + + wxString GetLogicalName() const { return m_libFileName.GetName(); } + + void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; } + + wxString GetFileName() const { return m_libFileName.GetFullPath(); } + + static LIB_PART* LoadPart( LINE_READER& aReader, int aMajorVersion, int aMinorVersion, + LIB_PART_MAP* aMap = nullptr ); + static void SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel = 0, LIB_PART_MAP* aMap = nullptr ); +}; + + +SCH_SEXPR_PLUGIN::SCH_SEXPR_PLUGIN() +{ + init( NULL ); +} + + +SCH_SEXPR_PLUGIN::~SCH_SEXPR_PLUGIN() +{ + delete m_cache; +} + + +void SCH_SEXPR_PLUGIN::init( KIWAY* aKiway, const PROPERTIES* aProperties ) +{ + m_version = 0; + m_rootSheet = NULL; + m_props = aProperties; + m_kiway = aKiway; + m_cache = NULL; + m_out = NULL; +} + + +SCH_SHEET* SCH_SEXPR_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway, + SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties ) +{ + wxASSERT( !aFileName || aKiway != NULL ); + + LOCALE_IO toggle; // toggles on, then off, the C locale. + SCH_SHEET* sheet; + + wxFileName fn = aFileName; + + // Unfortunately child sheet file names the legacy schematic file format are not fully + // qualified and are always appended to the project path. The aFileName attribute must + // always be an absolute path so the project path can be used for load child sheet files. + wxASSERT( fn.IsAbsolute() ); + + if( aAppendToMe ) + { + wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".", + aFileName, aAppendToMe->GetFileName() ); + + wxFileName normedFn = aAppendToMe->GetFileName(); + + if( !normedFn.IsAbsolute() ) + { + if( aFileName.Right( normedFn.GetFullPath().Length() ) == normedFn.GetFullPath() ) + m_path = aFileName.Left( aFileName.Length() - normedFn.GetFullPath().Length() ); + } + + if( m_path.IsEmpty() ) + m_path = aKiway->Prj().GetProjectPath(); + + wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path ); + } + else + { + m_path = aKiway->Prj().GetProjectPath(); + } + + m_currentPath.push( m_path ); + init( aKiway, aProperties ); + + if( aAppendToMe == NULL ) + { + // Clean up any allocated memory if an exception occurs loading the schematic. + std::unique_ptr< SCH_SHEET > newSheet( new SCH_SHEET ); + newSheet->SetFileName( aFileName ); + m_rootSheet = newSheet.get(); + loadHierarchy( newSheet.get() ); + + // If we got here, the schematic loaded successfully. + sheet = newSheet.release(); + } + else + { + m_rootSheet = aAppendToMe->GetRootSheet(); + wxASSERT( m_rootSheet != NULL ); + sheet = aAppendToMe; + loadHierarchy( sheet ); + } + + wxASSERT( m_currentPath.size() == 1 ); // only the project path should remain + + return sheet; +} + + +// Everything below this comment is recursive. Modify with care. + +void SCH_SEXPR_PLUGIN::loadHierarchy( SCH_SHEET* aSheet ) +{ + SCH_SCREEN* screen = NULL; + + if( !aSheet->GetScreen() ) + { + // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only + // stores the file name and extension. Add the project path to the file name and + // extension to compare when calling SCH_SHEET::SearchHierarchy(). + wxFileName fileName = aSheet->GetFileName(); + + if( !fileName.IsAbsolute() ) + fileName.MakeAbsolute( m_currentPath.top() ); + + // Save the current path so that it gets restored when decending and ascending the + // sheet hierarchy which allows for sheet schematic files to be nested in folders + // relative to the last path a schematic was loaded from. + wxLogTrace( traceSchLegacyPlugin, "Saving path \"%s\"", m_currentPath.top() ); + m_currentPath.push( fileName.GetPath() ); + wxLogTrace( traceSchLegacyPlugin, "Current path \"%s\"", m_currentPath.top() ); + wxLogTrace( traceSchLegacyPlugin, "Loading \"%s\"", fileName.GetFullPath() ); + + m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen ); + + if( screen ) + { + aSheet->SetScreen( screen ); + + // Do not need to load the sub-sheets - this has already been done. + } + else + { + aSheet->SetScreen( new SCH_SCREEN( m_kiway ) ); + aSheet->GetScreen()->SetFileName( fileName.GetFullPath() ); + + try + { + loadFile( fileName.GetFullPath(), aSheet->GetScreen() ); + + for( auto aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) ) + { + assert( aItem->Type() == SCH_SHEET_T ); + auto sheet = static_cast( aItem ); + + // Set the parent to aSheet. This effectively creates a method to find + // the root sheet from any sheet so a pointer to the root sheet does not + // need to be stored globally. Note: this is not the same as a hierarchy. + // Complex hierarchies can have multiple copies of a sheet. This only + // provides a simple tree to find the root sheet. + sheet->SetParent( aSheet ); + + // Recursion starts here. + loadHierarchy( sheet ); + } + } + catch( const IO_ERROR& ioe ) + { + // If there is a problem loading the root sheet, there is no recovery. + if( aSheet == m_rootSheet ) + throw( ioe ); + + // For all subsheets, queue up the error message for the caller. + if( !m_error.IsEmpty() ) + m_error += "\n"; + + m_error += ioe.What(); + } + } + + m_currentPath.pop(); + wxLogTrace( traceSchLegacyPlugin, "Restoring path \"%s\"", m_currentPath.top() ); + } +} + + +void SCH_SEXPR_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen ) +{ + FILE_LINE_READER reader( aFileName ); + + loadHeader( reader, aScreen ); + + LoadContent( reader, aScreen, m_version ); +} + + +void SCH_SEXPR_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen, int version ) +{ + m_version = version; +} + + +void SCH_SEXPR_PLUGIN::loadHeader( LINE_READER& aReader, SCH_SCREEN* aScreen ) +{ + const char* line = aReader.ReadLine(); +} + + +void SCH_SEXPR_PLUGIN::loadPageSettings( LINE_READER& aReader, SCH_SCREEN* aScreen ) +{ + wxASSERT( aScreen != NULL ); + + wxString buf; + const char* line = aReader.Line(); + + PAGE_INFO pageInfo; + TITLE_BLOCK tb; + +} + + +SCH_SHEET* SCH_SEXPR_PLUGIN::loadSheet( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_SHEET > sheet( new SCH_SHEET() ); + + sheet->SetTimeStamp( GetNewTimeStamp() ); + + + return sheet.release(); +} + + +SCH_BITMAP* SCH_SEXPR_PLUGIN::loadBitmap( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_BITMAP > bitmap( new SCH_BITMAP ); + + const char* line = aReader.Line(); + + return bitmap.release(); +} + + +SCH_JUNCTION* SCH_SEXPR_PLUGIN::loadJunction( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_JUNCTION > junction( new SCH_JUNCTION ); + + const char* line = aReader.Line(); + + return junction.release(); +} + + +SCH_NO_CONNECT* SCH_SEXPR_PLUGIN::loadNoConnect( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_NO_CONNECT > no_connect( new SCH_NO_CONNECT ); + + const char* line = aReader.Line(); + + return no_connect.release(); +} + + +SCH_LINE* SCH_SEXPR_PLUGIN::loadWire( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_LINE > wire( new SCH_LINE ); + + const char* line = aReader.Line(); + + return wire.release(); +} + + +SCH_BUS_ENTRY_BASE* SCH_SEXPR_PLUGIN::loadBusEntry( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_BUS_ENTRY_BASE > busEntry; + + const char* line = aReader.Line(); + + return busEntry.release(); +} + + +SCH_TEXT* SCH_SEXPR_PLUGIN::loadText( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_TEXT> text; + + const char* line = aReader.Line(); + + return text.release(); +} + + +SCH_COMPONENT* SCH_SEXPR_PLUGIN::loadComponent( LINE_READER& aReader ) +{ + std::unique_ptr< SCH_COMPONENT > component( new SCH_COMPONENT() ); + + const char* line = aReader.Line(); + + line = aReader.ReadLine(); + + return component.release(); +} + + +std::shared_ptr SCH_SEXPR_PLUGIN::loadBusAlias( LINE_READER& aReader, + SCH_SCREEN* aScreen ) +{ + auto busAlias = std::make_shared< BUS_ALIAS >( aScreen ); + + return busAlias; +} + + +void SCH_SEXPR_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway, + const PROPERTIES* aProperties ) +{ + wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." ); + wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." ); + + LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values. + + init( aKiway, aProperties ); + + wxFileName fn = aFileName; + + // File names should be absolute. Don't assume everything relative to the project path + // works properly. + wxASSERT( fn.IsAbsolute() ); + + FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() ); + + m_out = &formatter; // no ownership + + Format( aScreen ); +} + + +void SCH_SEXPR_PLUGIN::Format( SCH_SCREEN* aScreen ) +{ + wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." ); + wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." ); + + // Write the header + + for( const auto& alias : aScreen->GetBusAliases() ) + { + saveBusAlias( alias ); + } + + for( auto item : aScreen->Items() ) + { + switch( item->Type() ) + { + case SCH_COMPONENT_T: + saveComponent( static_cast( item ) ); + break; + case SCH_BITMAP_T: + saveBitmap( static_cast( item ) ); + break; + case SCH_SHEET_T: + saveSheet( static_cast( item ) ); + break; + case SCH_JUNCTION_T: + saveJunction( static_cast( item ) ); + break; + case SCH_NO_CONNECT_T: + saveNoConnect( static_cast( item ) ); + break; + case SCH_BUS_WIRE_ENTRY_T: + case SCH_BUS_BUS_ENTRY_T: + saveBusEntry( static_cast( item ) ); + break; + case SCH_LINE_T: + saveLine( static_cast( item ) ); + break; + case SCH_TEXT_T: + case SCH_LABEL_T: + case SCH_GLOBAL_LABEL_T: + case SCH_HIER_LABEL_T: + saveText( static_cast( item ) ); + break; + default: + wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" ); + } + } + + m_out->Print( 0, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN::Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter ) +{ + m_out = aFormatter; + + for( unsigned i = 0; i < aSelection->GetSize(); ++i ) + { + SCH_ITEM* item = (SCH_ITEM*) aSelection->GetItem( i ); + + switch( item->Type() ) + { + case SCH_COMPONENT_T: + saveComponent( static_cast< SCH_COMPONENT* >( item ) ); + break; + case SCH_BITMAP_T: + saveBitmap( static_cast< SCH_BITMAP* >( item ) ); + break; + case SCH_SHEET_T: + saveSheet( static_cast< SCH_SHEET* >( item ) ); + break; + case SCH_JUNCTION_T: + saveJunction( static_cast< SCH_JUNCTION* >( item ) ); + break; + case SCH_NO_CONNECT_T: + saveNoConnect( static_cast< SCH_NO_CONNECT* >( item ) ); + break; + case SCH_BUS_WIRE_ENTRY_T: + case SCH_BUS_BUS_ENTRY_T: + saveBusEntry( static_cast< SCH_BUS_ENTRY_BASE* >( item ) ); + break; + case SCH_LINE_T: + saveLine( static_cast< SCH_LINE* >( item ) ); + break; + case SCH_TEXT_T: + case SCH_LABEL_T: + case SCH_GLOBAL_LABEL_T: + case SCH_HIER_LABEL_T: + saveText( static_cast< SCH_TEXT* >( item ) ); + break; + default: + wxASSERT( "Unexpected schematic object type in SCH_SEXPR_PLUGIN::Format()" ); + } + } +} + + +void SCH_SEXPR_PLUGIN::saveComponent( SCH_COMPONENT* aComponent ) +{ + std::string name1; + std::string name2; + wxArrayString reference_fields; + + static wxString delimiters( wxT( " " ) ); + + // This is redundant with the AR entries below, but it makes the files backwards-compatible. + if( aComponent->GetPathsAndReferences().GetCount() > 0 ) + { + reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[0], delimiters ); + name1 = toUTFTildaText( reference_fields[1] ); + } + else + { + if( aComponent->GetField( REFERENCE )->GetText().IsEmpty() ) + name1 = toUTFTildaText( aComponent->GetPrefix() ); + else + name1 = toUTFTildaText( aComponent->GetField( REFERENCE )->GetText() ); + } + + wxString part_name = aComponent->GetLibId().Format(); + + if( part_name.size() ) + { + name2 = toUTFTildaText( part_name ); + } + else + { + name2 = "_NONAME_"; + } +} + + +void SCH_SEXPR_PLUGIN::saveField( SCH_FIELD* aField ) +{ +} + + +void SCH_SEXPR_PLUGIN::saveBitmap( SCH_BITMAP* aBitmap ) +{ + wxCHECK_RET( aBitmap != NULL, "SCH_BITMAP* is NULL" ); + + const wxImage* image = aBitmap->GetImage()->GetImageData(); + + wxCHECK_RET( image != NULL, "wxImage* is NULL" ); + + m_out->Print( 0, "$Bitmap\n" ); + m_out->Print( 0, "Pos %-4d %-4d\n", + Iu2Mils( aBitmap->GetPosition().x ), + Iu2Mils( aBitmap->GetPosition().y ) ); + m_out->Print( 0, "Scale %f\n", aBitmap->GetImage()->GetScale() ); + m_out->Print( 0, "Data\n" ); + + wxMemoryOutputStream stream; + + image->SaveFile( stream, wxBITMAP_TYPE_PNG ); + + // Write binary data in hexadecimal form (ASCII) + wxStreamBuffer* buffer = stream.GetOutputStreamBuffer(); + char* begin = (char*) buffer->GetBufferStart(); + + for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ ) + { + if( ii >= 32 ) + { + ii = 0; + + m_out->Print( 0, "\n" ); + } + + m_out->Print( 0, "%2.2X ", *begin & 0xFF ); + } + + m_out->Print( 0, "\nEndData\n" ); + m_out->Print( 0, "$EndBitmap\n" ); +} + + +void SCH_SEXPR_PLUGIN::saveSheet( SCH_SHEET* aSheet ) +{ + wxCHECK_RET( aSheet != NULL, "SCH_SHEET* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveJunction( SCH_JUNCTION* aJunction ) +{ + wxCHECK_RET( aJunction != NULL, "SCH_JUNCTION* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveNoConnect( SCH_NO_CONNECT* aNoConnect ) +{ + wxCHECK_RET( aNoConnect != NULL, "SCH_NOCONNECT* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry ) +{ + wxCHECK_RET( aBusEntry != NULL, "SCH_BUS_ENTRY_BASE* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveLine( SCH_LINE* aLine ) +{ + wxCHECK_RET( aLine != NULL, "SCH_LINE* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveText( SCH_TEXT* aText ) +{ + wxCHECK_RET( aText != NULL, "SCH_TEXT* is NULL" ); +} + + +void SCH_SEXPR_PLUGIN::saveBusAlias( std::shared_ptr aAlias ) +{ + wxCHECK_RET( aAlias != NULL, "BUS_ALIAS* is NULL" ); + + wxString members = boost::algorithm::join( aAlias->Members(), " " ); +} + + +int SCH_SEXPR_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up + + +SCH_SEXPR_PLUGIN_CACHE::SCH_SEXPR_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) : + m_fileName( aFullPathAndFileName ), + m_libFileName( aFullPathAndFileName ), + m_isWritable( true ), + m_isModified( false ) +{ + m_versionMajor = -1; + m_versionMinor = -1; + m_libType = LIBRARY_TYPE_EESCHEMA; +} + + +SCH_SEXPR_PLUGIN_CACHE::~SCH_SEXPR_PLUGIN_CACHE() +{ + // When the cache is destroyed, all of the alias objects on the heap should be deleted. + for( LIB_PART_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it ) + delete it->second; + + m_symbols.clear(); +} + + +// If m_libFileName is a symlink follow it to the real source file +wxFileName SCH_SEXPR_PLUGIN_CACHE::GetRealFile() const +{ + wxFileName fn( m_libFileName ); + +#ifndef __WINDOWS__ + if( fn.Exists( wxFILE_EXISTS_SYMLINK ) ) + { + char buffer[ PATH_MAX + 1 ]; + ssize_t pathLen = readlink( TO_UTF8( fn.GetFullPath() ), buffer, PATH_MAX ); + + if( pathLen > 0 ) + { + buffer[ pathLen ] = '\0'; + fn.Assign( fn.GetPath() + wxT( "/" ) + wxString::FromUTF8( buffer ) ); + fn.Normalize(); + } + } +#endif + + return fn; +} + + +wxDateTime SCH_SEXPR_PLUGIN_CACHE::GetLibModificationTime() +{ + wxFileName fn = GetRealFile(); + + // update the writable flag while we have a wxFileName, in a network this + // is possibly quite dynamic anyway. + m_isWritable = fn.IsFileWritable(); + + return fn.GetModificationTime(); +} + + +bool SCH_SEXPR_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const +{ + return m_fileName == aFullPathAndFileName; +} + + +bool SCH_SEXPR_PLUGIN_CACHE::IsFileChanged() const +{ + wxFileName fn = GetRealFile(); + + if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() ) + return fn.GetModificationTime() != m_fileModTime; + + return false; +} + + +LIB_PART* SCH_SEXPR_PLUGIN_CACHE::removeSymbol( LIB_PART* aPart ) +{ + wxCHECK_MSG( aPart != NULL, NULL, "NULL pointer cannot be removed from library." ); + + LIB_PART* firstChild = NULL; + LIB_PART_MAP::iterator it = m_symbols.find( aPart->GetName() ); + + if( it == m_symbols.end() ) + return NULL; + + // If the entry pointer doesn't match the name it is mapped to in the library, we + // have done something terribly wrong. + wxCHECK_MSG( *it->second == aPart, NULL, + "Pointer mismatch while attempting to remove alias entry <" + aPart->GetName() + + "> from library cache <" + m_libFileName.GetName() + ">." ); + + // If the symbol is a root symbol used by other symbols find the first alias that uses + // the root part and make it the new root. + if( aPart->IsRoot() ) + { + for( auto entry : m_symbols ) + { + if( entry.second->IsAlias() + && entry.second->GetParent().lock() == aPart->SharedPtr() ) + { + firstChild = entry.second; + break; + } + } + + if( firstChild ) + { + for( LIB_ITEM& drawItem : aPart->GetDrawItems() ) + { + if( drawItem.Type() == LIB_FIELD_T ) + { + LIB_FIELD& field = static_cast( drawItem ); + + if( firstChild->FindField( field.GetName() ) ) + continue; + } + + LIB_ITEM* newItem = (LIB_ITEM*) drawItem.Clone(); + drawItem.SetParent( firstChild ); + firstChild->AddDrawItem( newItem ); + } + + // Reparent the remaining aliases. + for( auto entry : m_symbols ) + { + if( entry.second->IsAlias() + && entry.second->GetParent().lock() == aPart->SharedPtr() ) + entry.second->SetParent( firstChild ); + } + } + } + + m_symbols.erase( it ); + delete aPart; + m_isModified = true; + ++m_modHash; + return firstChild; +} + + +void SCH_SEXPR_PLUGIN_CACHE::AddSymbol( const LIB_PART* aPart ) +{ + // aPart is cloned in PART_LIB::AddPart(). The cache takes ownership of aPart. + wxString name = aPart->GetName(); + LIB_PART_MAP::iterator it = m_symbols.find( name ); + + if( it != m_symbols.end() ) + { + removeSymbol( it->second ); + } + + m_symbols[ name ] = const_cast< LIB_PART* >( aPart ); + m_isModified = true; + ++m_modHash; +} + + +void SCH_SEXPR_PLUGIN_CACHE::Load() +{ + if( !m_libFileName.FileExists() ) + { + THROW_IO_ERROR( wxString::Format( _( "Library file \"%s\" not found." ), + m_libFileName.GetFullPath() ) ); + } + + wxCHECK_RET( m_libFileName.IsAbsolute(), + wxString::Format( "Cannot use relative file paths in sexpr plugin to " + "open library \"%s\".", m_libFileName.GetFullPath() ) ); + + wxLogTrace( traceSchLegacyPlugin, "Loading sexpr symbol library file \"%s\"", + m_libFileName.GetFullPath() ); + + FILE_LINE_READER reader( m_libFileName.GetFullPath() ); + + if( !reader.ReadLine() ) + THROW_IO_ERROR( _( "unexpected end of file" ) ); + + const char* line = reader.Line(); + + + ++m_modHash; + + // Remember the file modification time of library file when the + // cache snapshot was made, so that in a networked environment we will + // reload the cache as needed. + m_fileModTime = GetLibModificationTime(); +} + + +void SCH_SEXPR_PLUGIN_CACHE::loadHeader( FILE_LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + +} + + +LIB_PART* SCH_SEXPR_PLUGIN_CACHE::LoadPart( LINE_READER& aReader, int aMajorVersion, + int aMinorVersion, LIB_PART_MAP* aMap ) +{ + const char* line = aReader.Line(); + + std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) ); + + return part.release(); +} + + +void SCH_SEXPR_PLUGIN_CACHE::loadField( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); +} + + +void SCH_SEXPR_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr& aPart, + LINE_READER& aReader, + int aMajorVersion, + int aMinorVersion ) +{ + const char* line = aReader.Line(); +} + + +FILL_T SCH_SEXPR_PLUGIN_CACHE::parseFillMode( LINE_READER& aReader, const char* aLine, + const char** aOutput ) +{ +} + + +LIB_ARC* SCH_SEXPR_PLUGIN_CACHE::loadArc( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_ARC* arc = new LIB_ARC( aPart.get() ); + + return arc; +} + + +LIB_CIRCLE* SCH_SEXPR_PLUGIN_CACHE::loadCircle( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_CIRCLE* circle = new LIB_CIRCLE( aPart.get() ); + + return circle; +} + + +LIB_TEXT* SCH_SEXPR_PLUGIN_CACHE::loadText( std::unique_ptr& aPart, + LINE_READER& aReader, + int aMajorVersion, + int aMinorVersion ) +{ + const char* line = aReader.Line(); + + LIB_TEXT* text = new LIB_TEXT( aPart.get() ); + + return text; +} + + +LIB_RECTANGLE* SCH_SEXPR_PLUGIN_CACHE::loadRectangle( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_RECTANGLE* rectangle = new LIB_RECTANGLE( aPart.get() ); + + return rectangle; +} + + +LIB_PIN* SCH_SEXPR_PLUGIN_CACHE::loadPin( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_PIN* pin = new LIB_PIN( aPart.get() ); + + return pin; +} + + +LIB_POLYLINE* SCH_SEXPR_PLUGIN_CACHE::loadPolyLine( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_POLYLINE* polyLine = new LIB_POLYLINE( aPart.get() ); + + return polyLine; +} + + +LIB_BEZIER* SCH_SEXPR_PLUGIN_CACHE::loadBezier( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); + + LIB_BEZIER* bezier = new LIB_BEZIER( aPart.get() ); + + return bezier; +} + + +void SCH_SEXPR_PLUGIN_CACHE::loadFootprintFilters( std::unique_ptr& aPart, + LINE_READER& aReader ) +{ + const char* line = aReader.Line(); +} + + +void SCH_SEXPR_PLUGIN_CACHE::Save() +{ + if( !m_isModified ) + return; + + // Write through symlinks, don't replace them. + wxFileName fn = GetRealFile(); + + std::unique_ptr< FILE_OUTPUTFORMATTER > formatter( new FILE_OUTPUTFORMATTER( fn.GetFullPath() ) ); + + formatter->Print( 0, "(kicad_symbol_lib (version %d) (host kicad_symbol_editor %s)\n", + SEXPR_SYMBOL_LIB_FILE_VERSION, + formatter->Quotew( GetBuildVersion() ).c_str() ); + + for( auto parent : m_symbols ) + { + // Save the root symbol first so alias can inherit from them. + if( parent.second->IsRoot() ) + { + SaveSymbol( parent.second, *formatter.get(), 1 ); + + // Save all of the aliases associated with the current root symbol. + for( auto alias : m_symbols ) + { + if( !alias.second->IsAlias() ) + continue; + + std::shared_ptr aliasParent = alias.second->GetParent().lock(); + + if( aliasParent.get() != parent.second ) + continue; + + SaveSymbol( alias.second, *formatter.get(), 1 ); + } + } + } + + formatter->Print( 0, ")\n" ); + + formatter.reset(); + + m_fileModTime = fn.GetModificationTime(); + m_isModified = false; +} + + +void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel, LIB_PART_MAP* aMap ) +{ + wxCHECK_RET( aSymbol, "Invalid LIB_PART pointer." ); + + if( aSymbol->IsRoot() ) + { + aFormatter.Print( aNestLevel, "(symbol %s", + aFormatter.Quotew( aSymbol->GetName() ).c_str() ); + + if( aSymbol->IsPower() ) + aFormatter.Print( 0, " power" ); + + // TODO: add uuid token here. + + // TODO: add anchor position token here. + + if( !aSymbol->ShowPinNumbers() ) + aFormatter.Print( 0, " (pin_numbers hide)" ); + + if( aSymbol->GetPinNameOffset() != Iu2Mils( DEFAULT_PIN_NAME_OFFSET ) + || !aSymbol->ShowPinNames() ) + { + aFormatter.Print( 0, " (pin_name" ); + + if( aSymbol->GetPinNameOffset() != Iu2Mils( DEFAULT_PIN_NAME_OFFSET ) ) + aFormatter.Print( 0, " (offset %s)", + FormatInternalUnits( aSymbol->GetPinNameOffset() ).c_str() ); + + if( !aSymbol->ShowPinNames() ) + aFormatter.Print( 0, " hide" ); + + aFormatter.Print( 0, ")" ); + } + + // TODO: add atomic token here. + + // TODO: add required token here." + + aFormatter.Print( 0, "\n" ); + + LIB_FIELDS fields; + + aSymbol->GetFields( fields ); + + for( auto field : fields ) + saveField( &field, aFormatter, aNestLevel + 1 ); + + saveDcmInfoAsFields( aSymbol, aFormatter, aNestLevel, fields.back().GetId() + 1 ); + + // Save the draw items grouped by units. + std::vector units = aSymbol->GetUnitDrawItems(); + + for( auto unit : units ) + { + aFormatter.Print( aNestLevel + 1, "(symbol \"%s_%d_%d\"\n", + TO_UTF8( aSymbol->GetName() ), + unit.m_unit, unit.m_convert ); + + for( auto item : unit.m_items ) + saveSymbolDrawItem( item, aFormatter, aNestLevel + 2 ); + + aFormatter.Print( aNestLevel + 1, ")\n" ); + } + } + else + { + std::shared_ptr parent = aSymbol->GetParent().lock(); + + wxASSERT( parent ); + + aFormatter.Print( aNestLevel, "(symbol %s (extends %s)\n", + aFormatter.Quotew( aSymbol->GetName() ).c_str(), + aFormatter.Quotew( parent->GetName() ).c_str() ); + + LIB_FIELD tmp = parent->GetValueField(); + tmp.SetText( aSymbol->GetName() ); + saveField( &tmp, aFormatter, aNestLevel + 1 ); + + if( !aSymbol->GetDocFileName().IsEmpty() ) + { + tmp = *aSymbol->GetField( DATASHEET ); + tmp.SetText( aSymbol->GetDocFileName() ); + saveField( &tmp, aFormatter, aNestLevel + 1 ); + } + + saveDcmInfoAsFields( aSymbol, aFormatter, aNestLevel, MANDATORY_FIELDS ); + } + + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveDcmInfoAsFields( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter, + int aNestLevel, int aFirstId ) +{ + wxCHECK_RET( aSymbol, "Invalid LIB_PART pointer." ); + + int id = aFirstId; + + if( !aSymbol->GetKeyWords().IsEmpty() ) + { + LIB_FIELD keywords( id, wxString( "ki_keywords" ) ); + keywords.SetVisible( false ); + keywords.SetText( aSymbol->GetKeyWords() ); + saveField( &keywords, aFormatter, aNestLevel + 1 ); + id += 1; + } + + if( !aSymbol->GetDescription().IsEmpty() ) + { + LIB_FIELD description( id, wxString( "ki_description" ) ); + description.SetVisible( false ); + description.SetText( aSymbol->GetDescription() ); + saveField( &description, aFormatter, aNestLevel + 1 ); + id += 1; + } + + wxArrayString fpFilters = aSymbol->GetFootprints(); + + if( !fpFilters.IsEmpty() ) + { + wxString tmp; + + for( auto filter : fpFilters ) + { + if( tmp.IsEmpty() ) + tmp = filter; + else + tmp += "\n" + filter; + } + + LIB_FIELD description( id, wxString( "ki_fp_filters" ) ); + description.SetVisible( false ); + description.SetText( tmp ); + saveField( &description, aFormatter, aNestLevel + 1 ); + id += 1; + } +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveSymbolDrawItem( LIB_ITEM* aItem, OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aItem, "Invalid LIB_ITEM pointer." ); + + switch( aItem->Type() ) + { + case LIB_ARC_T: + saveArc( (LIB_ARC*) aItem, aFormatter, aNestLevel ); + break; + + case LIB_BEZIER_T: + saveBezier( (LIB_BEZIER*) aItem, aFormatter, aNestLevel ); + break; + + case LIB_CIRCLE_T: + saveCircle( ( LIB_CIRCLE* ) aItem, aFormatter, aNestLevel ); + break; + + case LIB_PIN_T: + savePin( (LIB_PIN* ) aItem, aFormatter, aNestLevel ); + break; + + case LIB_POLYLINE_T: + savePolyLine( ( LIB_POLYLINE* ) aItem, aFormatter, aNestLevel ); + break; + + case LIB_RECTANGLE_T: + saveRectangle( ( LIB_RECTANGLE* ) aItem, aFormatter, aNestLevel ); + break; + + case LIB_TEXT_T: + saveText( ( LIB_TEXT* ) aItem, aFormatter, aNestLevel ); + break; + + default: + ; + } +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveArc( LIB_ARC* aArc, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aArc && aArc->Type() == LIB_ARC_T, "Invalid LIB_ARC object." ); + + aFormatter.Print( aNestLevel, + "(arc (start %s %s) (end %s %s) (radius (at %s %s) (length %s))", + FormatInternalUnits( aArc->GetStart().x ).c_str(), + FormatInternalUnits( aArc->GetStart().y ).c_str(), + FormatInternalUnits( aArc->GetEnd().x ).c_str(), + FormatInternalUnits( aArc->GetEnd().y ).c_str(), + FormatInternalUnits( aArc->GetPosition().x ).c_str(), + FormatInternalUnits( aArc->GetPosition().y ).c_str(), + FormatInternalUnits( aArc->GetRadius() ).c_str() ); + + bool needsSpace = false; + + if( Iu2Mils( aArc->GetWidth() ) != DEFAULTDRAWLINETHICKNESS + && aArc->GetWidth() != 0 ) + { + aFormatter.Print( 0, "(stroke (width %s))", + FormatInternalUnits( aArc->GetWidth() ).c_str() ); + needsSpace = true; + } + + if( aArc->GetFillMode() != NO_FILL ) + { + if( needsSpace ) + aFormatter.Print( 0, " " ); + + FormatFill( static_cast< LIB_ITEM* >( aArc ), aFormatter, 0 ); + } + + aFormatter.Print( 0, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveBezier( LIB_BEZIER* aBezier, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aBezier && aBezier->Type() == LIB_BEZIER_T, "Invalid LIB_BEZIER object." ); + + int newLine = 0; + int lineCount = 1; + aFormatter.Print( aNestLevel, "(bezier\n" ); + aFormatter.Print( aNestLevel + 1, "(pts " ); + + for( const auto& pt : aBezier->GetPoints() ) + { + if( newLine == 4 ) + { + aFormatter.Print( 0, "\n" ); + aFormatter.Print( aNestLevel + 2, "(xy %s %s)", + FormatInternalUnits( pt.x ).c_str(), + FormatInternalUnits( pt.y ).c_str() ); + } + else + { + aFormatter.Print( 0, " (xy %s %s)", + FormatInternalUnits( pt.x ).c_str(), + FormatInternalUnits( pt.y ).c_str() ); + } + + if( newLine < 4 ) + { + newLine += 1; + } + else + { + newLine = 0; + lineCount += 1; + } + } + + if( lineCount == 1 ) + { + aFormatter.Print( 0, ")\n" ); // Closes pts token on same line. + } + else + { + aFormatter.Print( 0, "\n" ); + aFormatter.Print( aNestLevel + 1, ")\n" ); // Closes pts token with multiple lines. + } + + bool needsSpace = false; + + if( Iu2Mils( aBezier->GetWidth() ) != DEFAULTDRAWLINETHICKNESS + && aBezier->GetWidth() != 0 ) + { + aFormatter.Print( aNestLevel + 1, "(stroke (width %s))", + FormatInternalUnits( aBezier->GetWidth() ).c_str() ); + needsSpace = true; + + if( aBezier->GetFillMode() == NO_FILL ) + aFormatter.Print( 0, "\n" ); + } + + if( aBezier->GetFillMode() != NO_FILL ) + { + if( needsSpace ) + { + aFormatter.Print( 0, " " ); + FormatFill( static_cast< LIB_ITEM* >( aBezier ), aFormatter, 0 ); + } + else + { + FormatFill( static_cast< LIB_ITEM* >( aBezier ), aFormatter, aNestLevel + 1 ); + } + + aFormatter.Print( 0, "\n" ); + } + + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveCircle( LIB_CIRCLE* aCircle, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aCircle && aCircle->Type() == LIB_CIRCLE_T, "Invalid LIB_CIRCLE object." ); + + aFormatter.Print( aNestLevel, "(circle (center %s %s) (radius %s)", + FormatInternalUnits( aCircle->GetPosition().x ).c_str(), + FormatInternalUnits( aCircle->GetPosition().y ).c_str(), + FormatInternalUnits( aCircle->GetRadius() ).c_str() ); + + if( Iu2Mils( aCircle->GetWidth() ) != DEFAULTDRAWLINETHICKNESS + && aCircle->GetWidth() != 0 ) + { + aFormatter.Print( 0, " (stroke (width %s))", + FormatInternalUnits( aCircle->GetWidth() ).c_str() ); + } + + if( aCircle->GetFillMode() != NO_FILL ) + { + aFormatter.Print( 0, " " ); + FormatFill( static_cast< LIB_ITEM* >( aCircle ), aFormatter, 0 ); + } + + aFormatter.Print( 0, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveField( LIB_FIELD* aField, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." ); + + wxString fieldName = aField->GetName(); + + // When saving legacy fields, prefix the field name with "ki_" to prevent name clashes + // with exisiting user defined fields. + if( aField->IsMandatory() && !fieldName.StartsWith( "ki_" ) ) + fieldName = "ki_" + fieldName.Lower(); + + aFormatter.Print( aNestLevel, "(property %s %s (at %s %s)", + aFormatter.Quotew( fieldName ).c_str(), + aFormatter.Quotew( aField->GetText() ).c_str(), + FormatInternalUnits( aField->GetPosition().x ).c_str(), + FormatInternalUnits( aField->GetPosition().y ).c_str() ); + + if( aField->IsVisible() && aField->IsDefaultFormatting() ) + { + aFormatter.Print( 0, ")\n" ); // Close property token if visible and no font effects. + } + else + { + if( !aField->IsVisible() ) + { + if( aField->IsDefaultFormatting() ) + aFormatter.Print( 0, " hide)\n" ); // Close property token if no font effects. + else + aFormatter.Print( 0, " hide" ); + } + + if( !aField->IsDefaultFormatting() ) + { + aFormatter.Print( 0, "\n" ); + aField->Format( &aFormatter, aNestLevel + 1, CTL_OMIT_HIDE ); + aFormatter.Print( aNestLevel, ")\n" ); // Close property token. + } + } +} + + +void SCH_SEXPR_PLUGIN_CACHE::savePin( LIB_PIN* aPin, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." ); + + aPin->ClearFlags( IS_CHANGED ); + + aFormatter.Print( aNestLevel, "(pin %s %s (at %s %s %s) (length %s)", + GetPinElectricalTypeToken( aPin->GetType() ), + GetPinShapeToken( aPin->GetShape() ), + FormatInternalUnits( aPin->GetPosition().x ).c_str(), + FormatInternalUnits( aPin->GetPosition().y ).c_str(), + FormatAngle( GetPinAngle( aPin->GetOrientation() ) * 10.0 ).c_str(), + FormatInternalUnits( aPin->GetLength() ).c_str() ); + + aFormatter.Print( 0, " (name %s", + aFormatter.Quotew( aPin->GetName() ).c_str() ); + + // This follows the EDA_TEXT effects formatting for future expansion. + if( aPin->GetNameTextSize() != Mils2iu( DEFAULTPINNAMESIZE ) ) + aFormatter.Print( 0, " (effects (font (size %s %s)))", + FormatInternalUnits( aPin->GetNameTextSize() ).c_str(), + FormatInternalUnits( aPin->GetNameTextSize() ).c_str() ); + + aFormatter.Print( 0, ")" ); + aFormatter.Print( 0, " (number %s", + aFormatter.Quotew( aPin->GetNumber() ).c_str() ); + + // This follows the EDA_TEXT effects formatting for future expansion. + if( aPin->GetNumberTextSize() != Mils2iu( DEFAULTPINNUMSIZE ) ) + aFormatter.Print( 0, " (effects (font (size %s %s)))", + FormatInternalUnits( aPin->GetNumberTextSize() ).c_str(), + FormatInternalUnits( aPin->GetNumberTextSize() ).c_str() ); + aFormatter.Print( 0, "))\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::savePolyLine( LIB_POLYLINE* aPolyLine, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aPolyLine && aPolyLine->Type() == LIB_POLYLINE_T, "Invalid LIB_POLYLINE object." ); + + int newLine = 0; + int lineCount = 1; + aFormatter.Print( aNestLevel, "(polyline\n" ); + aFormatter.Print( aNestLevel + 1, "(pts" ); + + for( const auto& pt : aPolyLine->GetPolyPoints() ) + { + if( newLine == 4 ) + { + aFormatter.Print( 0, "\n" ); + aFormatter.Print( aNestLevel + 2, "(xy %s %s)", + FormatInternalUnits( pt.x ).c_str(), + FormatInternalUnits( pt.y ).c_str() ); + } + else + { + aFormatter.Print( 0, " (xy %s %s)", + FormatInternalUnits( pt.x ).c_str(), + FormatInternalUnits( pt.y ).c_str() ); + } + + if( newLine < 4 ) + { + newLine += 1; + } + else + { + newLine = 0; + lineCount += 1; + } + } + + if( lineCount == 1 ) + { + aFormatter.Print( 0, ")\n" ); // Closes pts token on same line. + } + else + { + aFormatter.Print( 0, "\n" ); + aFormatter.Print( aNestLevel + 1, ")\n" ); // Closes pts token with multiple lines. + } + + bool needsSpace = false; + + if( Iu2Mils( aPolyLine->GetWidth() ) != DEFAULTDRAWLINETHICKNESS + && aPolyLine->GetWidth() != 0 ) + { + aFormatter.Print( aNestLevel + 1, "(stroke (width %s))", + FormatInternalUnits( aPolyLine->GetWidth() ).c_str() ); + needsSpace = true; + + if( aPolyLine->GetFillMode() == NO_FILL ) + aFormatter.Print( 0, "\n" ); + } + + if( aPolyLine->GetFillMode() != NO_FILL ) + { + if( needsSpace ) + { + aFormatter.Print( 0, " " ); + FormatFill( static_cast< LIB_ITEM* >( aPolyLine ), aFormatter, 0 ); + } + else + { + FormatFill( static_cast< LIB_ITEM* >( aPolyLine ), aFormatter, aNestLevel + 1 ); + } + + aFormatter.Print( 0, "\n" ); + } + + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveRectangle( LIB_RECTANGLE* aRectangle, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aRectangle && aRectangle->Type() == LIB_RECTANGLE_T, + "Invalid LIB_RECTANGLE object." ); + + aFormatter.Print( aNestLevel, "(rectangle (start %s %s) (end %s %s)", + FormatInternalUnits( aRectangle->GetPosition().x ).c_str(), + FormatInternalUnits( aRectangle->GetPosition().y ).c_str(), + FormatInternalUnits( aRectangle->GetEnd().x ).c_str(), + FormatInternalUnits( aRectangle->GetEnd().y ).c_str() ); + + bool needsSpace = false; + + if( Iu2Mils( aRectangle->GetWidth() ) != DEFAULTDRAWLINETHICKNESS + && aRectangle->GetWidth() != 0 ) + { + aFormatter.Print( 0, " (stroke (width %s))", + FormatInternalUnits( aRectangle->GetWidth() ).c_str() ); + needsSpace = true; + } + + if( aRectangle->GetFillMode() != NO_FILL ) + { + if( needsSpace ) + aFormatter.Print( 0, " " ); + + FormatFill( static_cast< LIB_ITEM* >( aRectangle ), aFormatter, 0 ); + } + + aFormatter.Print( 0, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::saveText( LIB_TEXT* aText, + OUTPUTFORMATTER& aFormatter, + int aNestLevel ) +{ + wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." ); + + aFormatter.Print( aNestLevel, "(text %s (at %s %s %g)\n", + aFormatter.Quotew( aText->GetText() ).c_str(), + FormatInternalUnits( aText->GetPosition().x ).c_str(), + FormatInternalUnits( aText->GetPosition().y ).c_str(), + aText->GetTextAngle() ); + aText->Format( &aFormatter, aNestLevel + 1, 0 ); + aFormatter.Print( aNestLevel, ")\n" ); +} + + +void SCH_SEXPR_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName ) +{ + LIB_PART_MAP::iterator it = m_symbols.find( aSymbolName ); + + if( it == m_symbols.end() ) + THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ), + m_libFileName.GetFullName(), aSymbolName ) ); + + LIB_PART* part = it->second; + + if( part->IsRoot() ) + { + LIB_PART* rootPart = part; + + // Remove the root symbol and all it's children. + m_symbols.erase( it ); + + LIB_PART_MAP::iterator it1 = m_symbols.begin(); + + while( it1 != m_symbols.end() ) + { + if( it1->second->IsAlias() && it1->second->GetParent().lock() == rootPart->SharedPtr() ) + { + delete it1->second; + it1 = m_symbols.erase( it1 ); + } + else + { + it1++; + } + } + + delete rootPart; + } + else + { + // Just remove the alias. + m_symbols.erase( it ); + delete part; + } + + ++m_modHash; + m_isModified = true; +} + + +void SCH_SEXPR_PLUGIN::cacheLib( const wxString& aLibraryFileName ) +{ + if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() ) + { + // a spectacular episode in memory management: + delete m_cache; + m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryFileName ); + + // Because m_cache is rebuilt, increment PART_LIBS::s_modify_generation + // to modify the hash value that indicate component to symbol links + // must be updated. + PART_LIBS::s_modify_generation++; + + if( !isBuffering( m_props ) ) + m_cache->Load(); + } +} + + +bool SCH_SEXPR_PLUGIN::isBuffering( const PROPERTIES* aProperties ) +{ + return ( aProperties && aProperties->Exists( SCH_SEXPR_PLUGIN::PropBuffering ) ); +} + + +int SCH_SEXPR_PLUGIN::GetModifyHash() const +{ + if( m_cache ) + return m_cache->GetModifyHash(); + + // If the cache hasn't been loaded, it hasn't been modified. + return 0; +} + + +void SCH_SEXPR_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList, + const wxString& aLibraryPath, + const PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + m_props = aProperties; + + bool powerSymbolsOnly = ( aProperties && + aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() ); + cacheLib( aLibraryPath ); + + const LIB_PART_MAP& symbols = m_cache->m_symbols; + + for( LIB_PART_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it ) + { + if( !powerSymbolsOnly || it->second->IsPower() ) + aSymbolNameList.Add( it->first ); + } +} + + +void SCH_SEXPR_PLUGIN::EnumerateSymbolLib( std::vector& aSymbolList, + const wxString& aLibraryPath, + const PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + m_props = aProperties; + + bool powerSymbolsOnly = ( aProperties && + aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() ); + cacheLib( aLibraryPath ); + + const LIB_PART_MAP& symbols = m_cache->m_symbols; + + for( LIB_PART_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it ) + { + if( !powerSymbolsOnly || it->second->IsPower() ) + aSymbolList.push_back( it->second ); + } +} + + +LIB_PART* SCH_SEXPR_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aSymbolName, + const PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + m_props = aProperties; + + cacheLib( aLibraryPath ); + + LIB_PART_MAP::const_iterator it = m_cache->m_symbols.find( aSymbolName ); + + if( it == m_cache->m_symbols.end() ) + return nullptr; + + return it->second; +} + + +void SCH_SEXPR_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol, + const PROPERTIES* aProperties ) +{ + m_props = aProperties; + + cacheLib( aLibraryPath ); + + m_cache->AddSymbol( aSymbol ); + + if( !isBuffering( aProperties ) ) + m_cache->Save(); +} + + +void SCH_SEXPR_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aSymbolName, + const PROPERTIES* aProperties ) +{ + m_props = aProperties; + + cacheLib( aLibraryPath ); + + m_cache->DeleteSymbol( aSymbolName ); + + if( !isBuffering( aProperties ) ) + m_cache->Save(); +} + + +void SCH_SEXPR_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties ) +{ + if( wxFileExists( aLibraryPath ) ) + { + THROW_IO_ERROR( wxString::Format( + _( "symbol library \"%s\" already exists, cannot create a new library" ), + aLibraryPath.GetData() ) ); + } + + LOCALE_IO toggle; + + m_props = aProperties; + + delete m_cache; + m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryPath ); + m_cache->SetModified(); + m_cache->Save(); + m_cache->Load(); // update m_writable and m_mod_time +} + + +bool SCH_SEXPR_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties ) +{ + wxFileName fn = aLibraryPath; + + if( !fn.FileExists() ) + return false; + + // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog + // we don't want that. we want bare metal portability with no UI here. + if( wxRemove( aLibraryPath ) ) + { + THROW_IO_ERROR( wxString::Format( _( "library \"%s\" cannot be deleted" ), + aLibraryPath.GetData() ) ); + } + + if( m_cache && m_cache->IsFile( aLibraryPath ) ) + { + delete m_cache; + m_cache = 0; + } + + return true; +} + + +void SCH_SEXPR_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties ) +{ + if( !m_cache ) + m_cache = new SCH_SEXPR_PLUGIN_CACHE( aLibraryPath ); + + wxString oldFileName = m_cache->GetFileName(); + + if( !m_cache->IsFile( aLibraryPath ) ) + { + m_cache->SetFileName( aLibraryPath ); + } + + // This is a forced save. + m_cache->SetModified(); + m_cache->Save(); + m_cache->SetFileName( oldFileName ); +} + + +bool SCH_SEXPR_PLUGIN::CheckHeader( const wxString& aFileName ) +{ + // Open file and check first line + wxTextFile tempFile; + + tempFile.Open( aFileName ); + wxString firstline; + // read the first line + firstline = tempFile.GetFirstLine(); + tempFile.Close(); + + return firstline.StartsWith( "EESchema" ); +} + + +bool SCH_SEXPR_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath ) +{ + return wxFileName::IsFileWritable( aLibraryPath ); +} + + +LIB_PART* SCH_SEXPR_PLUGIN::ParsePart( LINE_READER& reader, int aMajorVersion, + int aMinorVersion ) +{ + return SCH_SEXPR_PLUGIN_CACHE::LoadPart( reader, aMajorVersion, aMinorVersion ); +} + + +void SCH_SEXPR_PLUGIN::FormatPart( LIB_PART* part, OUTPUTFORMATTER & formatter ) +{ + SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( part, formatter ); +} + + +const char* SCH_SEXPR_PLUGIN::PropBuffering = "buffering"; diff --git a/eeschema/sch_sexpr_plugin.h b/eeschema/sch_sexpr_plugin.h new file mode 100644 index 0000000000..679401dfeb --- /dev/null +++ b/eeschema/sch_sexpr_plugin.h @@ -0,0 +1,171 @@ +#ifndef _SCH_SEXPR_PLUGIN_H_ +#define _SCH_SEXPR_PLUGIN_H_ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * + * @author Wayne Stambaugh + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include + + +class KIWAY; +class LINE_READER; +class SCH_SCREEN; +class SCH_SHEET; +class SCH_BITMAP; +class SCH_JUNCTION; +class SCH_NO_CONNECT; +class SCH_LINE; +class SCH_BUS_ENTRY_BASE; +class SCH_TEXT; +class SCH_COMPONENT; +class SCH_FIELD; +class PROPERTIES; +class SELECTION; +class SCH_SEXPR_PLUGIN_CACHE; +class LIB_PART; +class PART_LIB; +class BUS_ALIAS; + + +/** + * A #SCH_PLUGIN derivation for loading schematic files using the new s-expression + * file format. + * + * As with all SCH_PLUGINs there is no UI dependencies i.e. windowing calls allowed. + */ +class SCH_SEXPR_PLUGIN : public SCH_PLUGIN +{ +public: + + SCH_SEXPR_PLUGIN(); + virtual ~SCH_SEXPR_PLUGIN(); + + const wxString GetName() const override + { + return wxT( "Eeschema-Legacy" ); + } + + const wxString GetFileExtension() const override + { + return wxT( "sch" ); + } + + /** + * The property used internally by the plugin to enable cache buffering which prevents + * the library file from being written every time the cache is changed. This is useful + * when writing the schematic cache library file or saving a library to a new file name. + */ + static const char* PropBuffering; + + int GetModifyHash() const override; + + SCH_SHEET* Load( const wxString& aFileName, KIWAY* aKiway, + SCH_SHEET* aAppendToMe = nullptr, + const PROPERTIES* aProperties = nullptr ) override; + + void LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen, + int version = EESCHEMA_VERSION ); + + void Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway, + const PROPERTIES* aProperties = nullptr ) override; + + void Format( SCH_SCREEN* aScreen ); + + void Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter ); + + void EnumerateSymbolLib( wxArrayString& aSymbolNameList, + const wxString& aLibraryPath, + const PROPERTIES* aProperties = nullptr ) override; + void EnumerateSymbolLib( std::vector& aSymbolList, + const wxString& aLibraryPath, + const PROPERTIES* aProperties = nullptr ) override; + LIB_PART* LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName, + const PROPERTIES* aProperties = nullptr ) override; + void SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol, + const PROPERTIES* aProperties = nullptr ) override; + void DeleteSymbol( const wxString& aLibraryPath, const wxString& aSymbolName, + const PROPERTIES* aProperties = nullptr ) override; + void CreateSymbolLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties = nullptr ) override; + bool DeleteSymbolLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties = nullptr ) override; + void SaveLibrary( const wxString& aLibraryPath, + const PROPERTIES* aProperties = nullptr ) override; + + bool CheckHeader( const wxString& aFileName ) override; + bool IsSymbolLibWritable( const wxString& aLibraryPath ) override; + + const wxString& GetError() const override { return m_error; } + + static LIB_PART* ParsePart( LINE_READER& aReader, int majorVersion = 0, int minorVersion = 0 ); + static void FormatPart( LIB_PART* aPart, OUTPUTFORMATTER& aFormatter ); + +private: + void loadHierarchy( SCH_SHEET* aSheet ); + void loadHeader( LINE_READER& aReader, SCH_SCREEN* aScreen ); + void loadPageSettings( LINE_READER& aReader, SCH_SCREEN* aScreen ); + void loadFile( const wxString& aFileName, SCH_SCREEN* aScreen ); + SCH_SHEET* loadSheet( LINE_READER& aReader ); + SCH_BITMAP* loadBitmap( LINE_READER& aReader ); + SCH_JUNCTION* loadJunction( LINE_READER& aReader ); + SCH_NO_CONNECT* loadNoConnect( LINE_READER& aReader ); + SCH_LINE* loadWire( LINE_READER& aReader ); + SCH_BUS_ENTRY_BASE* loadBusEntry( LINE_READER& aReader ); + SCH_TEXT* loadText( LINE_READER& aReader ); + SCH_COMPONENT* loadComponent( LINE_READER& aReader ); + std::shared_ptr loadBusAlias( LINE_READER& aReader, SCH_SCREEN* aScreen ); + + void saveComponent( SCH_COMPONENT* aComponent ); + void saveField( SCH_FIELD* aField ); + void saveBitmap( SCH_BITMAP* aBitmap ); + void saveSheet( SCH_SHEET* aSheet ); + void saveJunction( SCH_JUNCTION* aJunction ); + void saveNoConnect( SCH_NO_CONNECT* aNoConnect ); + void saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry ); + void saveLine( SCH_LINE* aLine ); + void saveText( SCH_TEXT* aText ); + void saveBusAlias( std::shared_ptr aAlias ); + + void cacheLib( const wxString& aLibraryFileName ); + bool isBuffering( const PROPERTIES* aProperties ); + +protected: + int m_version; ///< Version of file being loaded. + + /** For throwing exceptions or errors on partial schematic loads. */ + wxString m_error; + + wxString m_path; ///< Root project path for loading child sheets. + std::stack m_currentPath;///< Stack to maintain nested sheet paths + const PROPERTIES* m_props; ///< Passed via Save() or Load(), no ownership, may be nullptr. + KIWAY* m_kiway; ///< Required for path to legacy component libraries. + SCH_SHEET* m_rootSheet; ///< The root sheet of the schematic being loaded.. + OUTPUTFORMATTER* m_out; ///< The output formatter for saving SCH_SCREEN objects. + SCH_SEXPR_PLUGIN_CACHE* m_cache; + + /// initialize PLUGIN like a constructor would. + void init( KIWAY* aKiway, const PROPERTIES* aProperties = nullptr ); +}; + +#endif // _SCH_SEXPR_PLUGIN_H_ diff --git a/eeschema/symbol_lib.keywords b/eeschema/symbol_lib.keywords new file mode 100644 index 0000000000..a69b3369bb --- /dev/null +++ b/eeschema/symbol_lib.keywords @@ -0,0 +1,59 @@ +alternate +anchor +arc +at +atomic +bezier +bidirectional +circle +clock +clock_low +color +edge_clock_high +end +extends +fill +hint_alt_swap +hint_pin_swap +input +input_low +inverted +inverted_clock +kicad_symbol_lib +length +line +mid +name +non_logic +number +open_collector +open_emitter +output_low +unconnected +output +passive +pin +pin_del +pin_merge +pin_rename +polyline +power +power_in +power_out +property +property_del +pts +radius +rectangle +required +shape +start +stroke +symbol +text +tri_state +type +unspecified +uuid +width +xy diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index ac94d8b18c..b5e5e90556 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -4,7 +4,7 @@ * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2007-2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2008 Wayne Stambaugh - * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2020 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 @@ -125,6 +125,7 @@ extern const std::string EquFileExtension; extern const std::string LegacyPcbFileExtension; extern const std::string KiCadPcbFileExtension; #define PcbFileExtension KiCadPcbFileExtension // symlink choice +extern const std::string KiCadSymbolLibFileExtension; extern const std::string PageLayoutDescrFileExtension; extern const std::string LegacyFootprintLibPathExtension; @@ -170,6 +171,7 @@ extern wxString AllFilesWildcard(); extern wxString ComponentFileWildcard(); extern wxString PageLayoutDescrFileWildcard(); extern wxString SchematicSymbolFileWildcard(); +extern wxString KiCadSymbolLibFileWildcard(); extern wxString SchematicLibraryFileWildcard(); extern wxString ProjectFileWildcard(); extern wxString SchematicFileWildcard(); diff --git a/qa/eeschema/test_lib_part.cpp b/qa/eeschema/test_lib_part.cpp index f2f2bd347c..045ced9b29 100644 --- a/qa/eeschema/test_lib_part.cpp +++ b/qa/eeschema/test_lib_part.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2019 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2019-2020 KiCad Developers, see CHANGELOG.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 @@ -39,7 +39,8 @@ class TEST_LIB_PART_FIXTURE { public: - TEST_LIB_PART_FIXTURE() : m_part_no_data( "part_name", nullptr ) + TEST_LIB_PART_FIXTURE() : + m_part_no_data( "part_name", nullptr ) { } @@ -368,6 +369,68 @@ BOOST_AUTO_TEST_CASE( Compare ) } +/** + * Check the fetch unit items code. + */ +BOOST_AUTO_TEST_CASE( GetUnitItems ) +{ + // There are no unit draw items in the empty LIB_PART object. + BOOST_CHECK( m_part_no_data.GetUnitItems( 1, 1 ).size() == 0 ); + + // A single unique unit with 1 pin common to all units and all body styles. + LIB_PIN* pin1 = new LIB_PIN( &m_part_no_data ); + m_part_no_data.AddDrawItem( pin1 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 0, 0 ).size() == 1 ); + + // A single unique unit with 1 pin in unit 1 and common to all body styles. + pin1->SetUnit( 1 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 1, 0 ).size() == 1 ); + + // A single unique unit with 1 pin in unit 1 and body style 1. + pin1->SetConvert( 1 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 1, 1 ).size() == 1 ); + + // Two unique units with pin 1 assigned to unit 1 and body style 1 and pin 2 assinged to + // unit 2 and body style 1. + LIB_PIN* pin2 = new LIB_PIN( &m_part_no_data ); + m_part_no_data.SetUnitCount( 2 ); + pin2->SetUnit( 2 ); + pin2->SetConvert( 2 ); + pin2->SetNumber( "4" ); + m_part_no_data.AddDrawItem( pin2 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 2, 2 ).size() == 1 ); + + // Make pin 1 body style common to all units. + pin1->SetConvert( 0 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 1, 1 ).size() == 0 ); + BOOST_CHECK( m_part_no_data.GetUnitItems( 2, 1 ).size() == 1 ); + + m_part_no_data.RemoveDrawItem( pin2 ); + m_part_no_data.RemoveDrawItem( pin1 ); + m_part_no_data.RemoveDrawItem( m_part_no_data.GetNextDrawItem() ); +} + + +/** + * Check the fetch unit draw items code. + */ +BOOST_AUTO_TEST_CASE( GetUnitDrawItems ) +{ + // There are no unit draw items in the empty LIB_PART object. + BOOST_CHECK( m_part_no_data.GetUnitDrawItems().size() == 0 ); + + // A single unique unit with 1 pin common to all units and all body styles. + LIB_PIN* pin1 = new LIB_PIN( &m_part_no_data ); + pin1->SetNumber( "1" ); + m_part_no_data.AddDrawItem( pin1 ); + std::vector units = m_part_no_data.GetUnitDrawItems(); + BOOST_CHECK( units.size() == 1 ); + BOOST_CHECK( units[0].m_unit == 0 ); + BOOST_CHECK( units[0].m_convert == 0 ); + BOOST_CHECK( units[0].m_items[0] == pin1 ); +} + + /** * Check inheritance support. */