From 27b047ab3f5a67c2eea3786df080845518849e76 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Wed, 19 Aug 2020 19:21:24 +0100 Subject: [PATCH] Cache project text vars as properties in the PCB board file. Fixes https://gitlab.com/kicad/code/kicad/issues/5255 Fixes https://gitlab.com/kicad/code/kicad/issues/5005 --- common/common.cpp | 7 +++- include/common.h | 3 +- pcbnew/class_board.cpp | 25 +++++++++---- pcbnew/class_board.h | 75 ++++++++++----------------------------- pcbnew/class_pcb_text.cpp | 20 +++++------ pcbnew/class_text_mod.cpp | 13 ++++--- pcbnew/files.cpp | 19 +++++++--- pcbnew/kicad_plugin.cpp | 42 +++++++++------------- pcbnew/kicad_plugin.h | 6 +++- pcbnew/pcb_parser.cpp | 28 ++++++++++++--- pcbnew/pcb_parser.h | 2 ++ 11 files changed, 123 insertions(+), 117 deletions(-) diff --git a/common/common.cpp b/common/common.cpp index 745859dfec..2f9ce89b6b 100644 --- a/common/common.cpp +++ b/common/common.cpp @@ -381,7 +381,8 @@ enum Bracket wxString ExpandTextVars( const wxString& aSource, const std::function* aLocalResolver, - const PROJECT* aProject ) + const PROJECT* aProject, + const std::function* aFallbackResolver ) { wxString newbuf; size_t sourceLen = aSource.length(); @@ -413,6 +414,10 @@ wxString ExpandTextVars( const wxString& aSource, { newbuf.append( token ); } + else if( aFallbackResolver && (*aFallbackResolver)( &token ) ) + { + newbuf.append( token ); + } else { // Token not resolved: leave the reference unchanged diff --git a/include/common.h b/include/common.h index af8e34f0e3..9c2a55ed6f 100644 --- a/include/common.h +++ b/include/common.h @@ -330,7 +330,8 @@ const wxString ExpandEnvVarSubstitutions( const wxString& aString, PROJECT* aPro */ wxString ExpandTextVars( const wxString& aSource, const std::function* aLocalResolver, - const PROJECT* aProject ); + const PROJECT* aProject, + const std::function* aFallbackResolver = nullptr ); /** * Replace any environment and/or text variables in file-path uris (leaving network-path URIs diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index fd7f3a5a49..0cfebf6d06 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -68,9 +68,6 @@ BOARD::BOARD() : // we have not loaded a board yet, assume latest until then. m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION; - m_CurrentZoneContour = NULL; // This ZONE_CONTAINER handle the - // zone contour currently in progress - for( LAYER_NUM layer = 0; layer < PCB_LAYER_ID_COUNT; ++layer ) { m_Layer[layer].m_name = GetStandardLayerName( ToLAYER_ID( layer ) ); @@ -133,9 +130,6 @@ BOARD::~BOARD() m_drawings.clear(); - delete m_CurrentZoneContour; - m_CurrentZoneContour = NULL; - m_groups.clear(); } @@ -191,6 +185,18 @@ void BOARD::ClearProject() } +bool BOARD::ResolveTextVar( wxString* token, int aDepth ) const +{ + if( m_properties.count( *token ) ) + { + *token = m_properties.at( *token ); + return true; + } + + return false; +} + + wxPoint BOARD::GetPosition() const { return ZeroOffset; @@ -1300,6 +1306,13 @@ std::vector BOARD::GetNetClassAssignmentCandidates() } +void BOARD::SynchronizeProperties() +{ + if( m_project ) + SetProperties( m_project->GetTextVars() ); +} + + void BOARD::SynchronizeNetsAndNetClasses() { if( m_project ) diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 3460ac295b..59f1a57133 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -25,23 +25,17 @@ #ifndef CLASS_BOARD_H_ #define CLASS_BOARD_H_ -#include #include #include #include #include -#include #include // PAGE_INFO -#include #include #include #include #include -#include #include -#include - class BOARD_COMMIT; class PCB_BASE_FRAME; class PCB_EDIT_FRAME; @@ -186,25 +180,12 @@ class BOARD : public BOARD_ITEM_CONTAINER friend class PCB_EDIT_FRAME; private: - /// the board filename wxString m_fileName; - - /// MARKER_PCBs for clearance problems, owned by pointer. MARKERS m_markers; - - /// BOARD_ITEMs for drawings on the board, owned by pointer. DRAWINGS m_drawings; - - /// MODULES for components on the board, owned by pointer. MODULES m_modules; - - /// TRACKS for traces on the board, owned by pointer. TRACKS m_tracks; - - /// GROUPS for groups on the board, owned by pointer. GROUPS m_groups; - - /// edge zone descriptors, owned by pointer. ZONE_CONTAINERS m_ZoneDescriptorList; LAYER m_Layer[PCB_LAYER_ID_COUNT]; @@ -215,7 +196,8 @@ private: int m_fileFormatVersionAtLoad; // the version loaded from the file - std::shared_ptr m_connectivity; + std::map m_properties; + std::shared_ptr m_connectivity; PAGE_INFO m_paper; TITLE_BLOCK m_titles; // text in lower right of screen and plots @@ -261,38 +243,17 @@ public: const wxString &GetFileName() const { return m_fileName; } - TRACKS& Tracks() - { - return m_tracks; - } - const TRACKS& Tracks() const - { - return m_tracks; - } + TRACKS& Tracks() { return m_tracks; } + const TRACKS& Tracks() const { return m_tracks; } - MODULES& Modules() - { - return m_modules; - } - const MODULES& Modules() const - { - return m_modules; - } + MODULES& Modules() { return m_modules; } + const MODULES& Modules() const { return m_modules; } - DRAWINGS& Drawings() - { - return m_drawings; - } + DRAWINGS& Drawings() { return m_drawings; } - ZONE_CONTAINERS& Zones() - { - return m_ZoneDescriptorList; - } + ZONE_CONTAINERS& Zones() { return m_ZoneDescriptorList; } - MARKERS& Markers() - { - return m_markers; - } + MARKERS& Markers() { return m_markers; } /** * The groups must maintain the folowing invariants. These are checked by @@ -302,15 +263,14 @@ public: * - If a group specifies a name, it must be unique * - The graph of groups contianing subgroups must be acyclic. */ - GROUPS& Groups() - { - return m_groups; - } + GROUPS& Groups() { return m_groups; } const std::vector AllConnectedItems(); - /// zone contour currently in progress - ZONE_CONTAINER* m_CurrentZoneContour; + const std::map& GetProperties() const { return m_properties; } + void SetProperties( const std::map& aProps ) { m_properties = aProps; } + + bool ResolveTextVar( wxString* token, int aDepth ) const; /// Visibility settings stored in board prior to 6.0, only used for loading legacy files LSET m_LegacyVisibleLayers; @@ -885,8 +845,11 @@ public: */ void SynchronizeNetsAndNetClasses(); - - /***************************************************************************/ + /** + * Function SynchronizeProperties + * copies the current project's text variables into the boards property cache. + */ + void SynchronizeProperties(); wxString GetClass() const override { diff --git a/pcbnew/class_pcb_text.cpp b/pcbnew/class_pcb_text.cpp index 55541380e3..16d966fb0d 100644 --- a/pcbnew/class_pcb_text.cpp +++ b/pcbnew/class_pcb_text.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2015 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 @@ -23,21 +23,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/** - * @file class_pcb_text.cpp - * @brief Class TEXTE_PCB texts on copper or technical layers implementation - */ - #include -#include #include -#include #include #include #include -#include -#include - #include #include #include @@ -92,11 +82,17 @@ wxString TEXTE_PCB::GetShownText( int aDepth ) const return false; }; + std::function boardTextResolver = + [&]( wxString* token ) -> bool + { + return board->ResolveTextVar( token, aDepth + 1 ); + }; + bool processTextVars = false; wxString text = EDA_TEXT::GetShownText( &processTextVars ); if( processTextVars && aDepth < 10 ) - text = ExpandTextVars( text, &pcbTextResolver, board->GetProject() ); + text = ExpandTextVars( text, &pcbTextResolver, board->GetProject(), &boardTextResolver ); return text; } diff --git a/pcbnew/class_text_mod.cpp b/pcbnew/class_text_mod.cpp index 4b6c547cce..a1b16fd23e 100644 --- a/pcbnew/class_text_mod.cpp +++ b/pcbnew/class_text_mod.cpp @@ -24,15 +24,11 @@ */ #include -#include -#include #include #include #include #include #include -#include -#include #include TEXTE_MODULE::TEXTE_MODULE( MODULE* parent, TEXT_TYPE text_type ) : @@ -428,6 +424,7 @@ wxString TEXTE_MODULE::GetShownText( int aDepth ) const { const MODULE* module = static_cast( GetParent() ); wxASSERT( module ); + const BOARD* board = module->GetBoard(); std::function moduleResolver = [&]( wxString* token ) -> bool @@ -435,6 +432,12 @@ wxString TEXTE_MODULE::GetShownText( int aDepth ) const return module && module->ResolveTextVar( token, aDepth ); }; + std::function boardTextResolver = + [&]( wxString* token ) -> bool + { + return board->ResolveTextVar( token, aDepth + 1 ); + }; + bool processTextVars = false; wxString text = EDA_TEXT::GetShownText( &processTextVars ); @@ -446,7 +449,7 @@ wxString TEXTE_MODULE::GetShownText( int aDepth ) const project = static_cast( module->GetParent() )->GetProject(); if( aDepth < 10 ) - text = ExpandTextVars( text, &moduleResolver, project ); + text = ExpandTextVars( text, &moduleResolver, project, &boardTextResolver ); } return text; diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index cce22b19fc..90ab67d59b 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -335,8 +335,8 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id ) { bool addToHistory = false; wxString orig_name; - wxFileName::SplitPath( GetBoard()->GetFileName(), - nullptr, nullptr, &orig_name, nullptr ); + wxFileName::SplitPath( GetBoard()->GetFileName(), nullptr, nullptr, &orig_name, + nullptr ); if( orig_name.IsEmpty() ) { @@ -706,9 +706,12 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory, // TODO: this will break if we ever go multi-board wxFileName projectFile( pcbFileName ); - projectFile.SetExt( ProjectFileExtension ); + bool projectFileExists = false; - if( aChangeProject && !projectFile.FileExists() ) + projectFile.SetExt( ProjectFileExtension ); + projectFileExists = projectFile.FileExists(); + + if( aChangeProject && !projectFileExists ) { // If this is a new board, project filename won't be set yet if( projectFile.GetFullPath() != Prj().GetProjectFullName() ) @@ -720,11 +723,17 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory, mgr->SaveProject( Prj().GetProjectFullName() ); mgr->UnloadProject( &Prj() ); - mgr->LoadProject( projectFile.GetFullPath() ); + // If no project to load then initialize project text vars with board properties + if( !mgr->LoadProject( projectFile.GetFullPath() ) ) + Prj().GetTextVars() = GetBoard()->GetProperties(); + GetBoard()->SetProject( &Prj() ); } } + if( projectFileExists ) + GetBoard()->SynchronizeProperties(); + wxFileName tempFile( aFileName ); tempFile.SetName( wxT( "." ) + tempFile.GetName() ); tempFile.SetExt( tempFile.GetExt() + wxT( "$" ) ); diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 6d817fcfc7..fb50380176 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -23,11 +23,8 @@ */ #include -#include #include // LEGACY_BOARD_FILE_VERSION -#include #include - #include #include #include @@ -45,34 +42,13 @@ #include #include #include -#include -#include -#include #include -#include -#include #include // for enum RECT_CHAMFER_POSITIONS definition #include using namespace PCB_KEYS_T; -///> Removes empty nets (i.e. with node count equal zero) from net classes -void filterNetClass( const BOARD& aBoard, NETCLASS& aNetClass ) -{ - auto connectivity = aBoard.GetConnectivity(); - - for( NETCLASS::iterator it = aNetClass.begin(); it != aNetClass.end(); ) - { - NETINFO_ITEM* netinfo = aBoard.FindNet( *it ); - - if( netinfo && connectivity->GetNodeCount( netinfo->GetNet() ) <= 0 ) // hopefully there are no nets with negative - aNetClass.Remove( it++ ); // node count, but you never know.. - else - ++it; - } -} - /** * FP_CACHE_ITEM * is helper class for creating a footprint library cache. @@ -606,6 +582,19 @@ void PCB_IO::formatNetInformation( BOARD* aBoard, int aNestLevel ) const } +void PCB_IO::formatProperties( BOARD* aBoard, int aNestLevel ) const +{ + for( const std::pair& prop : aBoard->GetProperties() ) + { + m_out->Print( aNestLevel+1, "(property %s %s)\n", + m_out->Quotew( prop.first ).c_str(), + m_out->Quotew( prop.second ).c_str() ); + } + + m_out->Print( 0, "\n" ); +} + + void PCB_IO::formatHeader( BOARD* aBoard, int aNestLevel ) const { formatGeneral( aBoard, aNestLevel ); @@ -615,13 +604,16 @@ void PCB_IO::formatHeader( BOARD* aBoard, int aNestLevel ) const // Setup formatSetup( aBoard, aNestLevel ); + // Properties + formatProperties( aBoard, aNestLevel ); + // Save net codes and names formatNetInformation( aBoard, aNestLevel ); } + void PCB_IO::format( BOARD* aBoard, int aNestLevel ) const { - std::set sorted_modules( aBoard->Modules().begin(), aBoard->Modules().end() ); std::set sorted_drawings( aBoard->Drawings().begin(), diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index 748d0556ea..6858c88a0e 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -78,7 +78,8 @@ class TEXTE_PCB; //#define SEXPR_BOARD_FILE_VERSION 20200808 // Add properties to modules //#define SEXPR_BOARD_FILE_VERSION 20200809 // Add REMOVE_UNUSED_LAYERS option to vias and THT pads //#define SEXPR_BOARD_FILE_VERSION 20200811 // Add groups -#define SEXPR_BOARD_FILE_VERSION 20200818 // Remove Status flag bitmap and setup counts +//#define SEXPR_BOARD_FILE_VERSION 20200818 // Remove Status flag bitmap and setup counts +#define SEXPR_BOARD_FILE_VERSION 20200819 // Add board-level properties #define CTL_STD_LAYER_NAMES (1 << 0) ///< Use English Standard layer names #define CTL_OMIT_NETS (1 << 1) ///< Omit pads net names (useless in library) @@ -235,6 +236,9 @@ protected: /// formats the Nets and Netclasses void formatNetInformation( BOARD* aBoard, int aNestLevel = 0 ) const; + /// formats the Nets and Netclasses + void formatProperties( BOARD* aBoard, int aNestLevel = 0 ) const; + /// writes everything that comes before the board_items, like settings and layers etc void formatHeader( BOARD* aBoard, int aNestLevel = 0 ) const; diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index c4f0d6a214..60a22cb719 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -249,6 +249,21 @@ void PCB_PARSER::parseXY( int* aX, int* aY ) } +std::pair PCB_PARSER::parseProperty() +{ + wxString pName; + wxString pValue; + + NeedSYMBOL(); + pName = FromUTF8(); + NeedSYMBOL(); + pValue = FromUTF8(); + NeedRIGHT(); + + return { pName, pValue }; +} + + void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText ) { wxCHECK_RET( CurTok() == T_effects, @@ -524,6 +539,7 @@ BOARD* PCB_PARSER::parseBOARD() BOARD* PCB_PARSER::parseBOARD_unchecked() { T token; + std::map properties; parseHeader(); @@ -559,6 +575,10 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() parseSetup(); break; + case T_property: + properties.insert( parseProperty() ); + break; + case T_net: parseNETINFO_ITEM(); break; @@ -620,6 +640,8 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() } } + m_board->SetProperties( properties ); + if( m_undefinedLayers.size() > 0 ) { bool deleteItems; @@ -2603,11 +2625,7 @@ MODULE* PCB_PARSER::parseMODULE_unchecked( wxArrayString* aInitialComments ) break; case T_property: - NeedSYMBOL(); - name = FromUTF8(); - NeedSYMBOL(); - properties[ name ] = FromUTF8(); - NeedRIGHT(); + properties.insert( parseProperty() ); break; case T_path: diff --git a/pcbnew/pcb_parser.h b/pcbnew/pcb_parser.h index c8d0b7226d..59e48775f7 100644 --- a/pcbnew/pcb_parser.h +++ b/pcbnew/pcb_parser.h @@ -238,6 +238,8 @@ class PCB_PARSER : public PCB_LEXER void parseXY( int* aX, int* aY ); + std::pair parseProperty(); + /** * Function parseEDA_TEXT * parses the common settings for any object derived from #EDA_TEXT.