Normalize formatting of booleans in the PCB file format

This commit is contained in:
Jon Evans 2023-09-24 12:53:34 -04:00
parent 2545999b3d
commit 55bca5e7ac
10 changed files with 364 additions and 153 deletions

View File

@ -492,6 +492,7 @@ set( COMMON_SRCS
page_info.cpp
plugin_file_desc.cpp
plugins/plugin_utils.cpp
plugins/kicad/kicad_plugin_utils.cpp
printout.cpp
project.cpp
ptree.cpp

View File

@ -111,6 +111,7 @@ extension_offset
fab_layers_line_width
fab_layers_text_dims
face
false
feature1
feature2
fill
@ -319,6 +320,7 @@ trapezoid
thru
thru_hole
thru_hole_only
true
tstamp
type
units

View File

@ -18,6 +18,7 @@ plot_on_all_layers_selection
linewidth
mirror
mode
no
outputdirectory
outputformat
pcbplotparams
@ -42,3 +43,4 @@ usegerberextensions
viasonmask
usegerberattributes
usegerberadvancedattributes
yes

View File

@ -0,0 +1,31 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 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 as published by the
* Free Software Foundation, either version 3 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 <http://www.gnu.org/licenses/>.
*/
#include <richio.h>
#include <plugins/kicad/kicad_plugin_utils.h>
namespace KICAD_FORMAT {
void FormatBool( OUTPUTFORMATTER* aOut, int aNestLevel, const wxString& aKey, bool aValue,
char aSuffix )
{
aOut->Print( aNestLevel, "(%ls %s)%c", aKey.wc_str(), aValue ? "yes" : "no", aSuffix );
}
} // namespace KICAD_FORMAT

View File

@ -0,0 +1,43 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 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 as published by the
* Free Software Foundation, either version 3 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 <http://www.gnu.org/licenses/>.
*/
#ifndef KICAD_PLUGIN_UTILS_H
#define KICAD_PLUGIN_UTILS_H
#include <wx/string.h>
class OUTPUTFORMATTER;
namespace KICAD_FORMAT {
/**
* Writes a boolean to the formatter, in the style (aKey [yes|no])
*
* @param aOut is the output formatter to write to
* @param aNestLevel is passed to the output formatter to control indentation
* @param aKey is the name of the boolean flag
* @param aValue is the value to write
* @param aSuffix is the character to format after the end of the boolean (after the close paren)
*/
void FormatBool( OUTPUTFORMATTER* aOut, int aNestLevel, const wxString& aKey, bool aValue,
char aSuffix = '\n' );
} // namespace KICAD_FORMAT
#endif //KICAD_PLUGIN_UTILS_H

View File

@ -29,6 +29,7 @@
#include <pcb_plot_params.h>
#include <pcb_plot_params_parser.h>
#include <plotters/plotter.h>
#include <plugins/kicad/kicad_plugin_utils.h>
#include <settings/color_settings.h>
@ -171,12 +172,6 @@ void PCB_PLOT_PARAMS::SetSvgPrecision( unsigned aPrecision )
void PCB_PLOT_PARAMS::Format( OUTPUTFORMATTER* aFormatter,
int aNestLevel, int aControl ) const
{
auto printBool =
[]( bool aBool ) -> const char*
{
return aBool ? "true" : "false";
};
aFormatter->Print( aNestLevel, "(pcbplotparams\n" );
aFormatter->Print( aNestLevel+1, "(layerselection 0x%s)\n",
@ -185,20 +180,20 @@ void PCB_PLOT_PARAMS::Format( OUTPUTFORMATTER* aFormatter,
aFormatter->Print( aNestLevel+1, "(plot_on_all_layers_selection 0x%s)\n",
m_plotOnAllLayersSelection.FmtHex().c_str() );
aFormatter->Print( aNestLevel+1, "(disableapertmacros %s)\n",
printBool( m_gerberDisableApertMacros ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "disableapertmacros",
m_gerberDisableApertMacros );
aFormatter->Print( aNestLevel+1, "(usegerberextensions %s)\n",
printBool( m_useGerberProtelExtensions) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "usegerberextensions",
m_useGerberProtelExtensions );
aFormatter->Print( aNestLevel+1, "(usegerberattributes %s)\n",
printBool( GetUseGerberX2format()) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "usegerberattributes",
GetUseGerberX2format() );
aFormatter->Print( aNestLevel+1, "(usegerberadvancedattributes %s)\n",
printBool( GetIncludeGerberNetlistInfo()) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "usegerberadvancedattributes",
GetIncludeGerberNetlistInfo() );
aFormatter->Print( aNestLevel+1, "(creategerberjobfile %s)\n",
printBool( GetCreateGerberJobFile()) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "creategerberjobfile",
GetCreateGerberJobFile() );
// save this option only if it is not the default value,
// to avoid incompatibility with older Pcbnew version
@ -211,10 +206,10 @@ void PCB_PLOT_PARAMS::Format( OUTPUTFORMATTER* aFormatter,
// SVG options
aFormatter->Print( aNestLevel+1, "(svgprecision %d)\n", m_svgPrecision );
aFormatter->Print( aNestLevel+1, "(plotframeref %s)\n", printBool( m_plotDrawingSheet ) );
aFormatter->Print( aNestLevel+1, "(viasonmask %s)\n", printBool( m_plotViaOnMaskLayer ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "plotframeref", m_plotDrawingSheet );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "viasonmask", m_plotViaOnMaskLayer );
aFormatter->Print( aNestLevel+1, "(mode %d)\n", GetPlotMode() == SKETCH ? 2 : 1 );
aFormatter->Print( aNestLevel+1, "(useauxorigin %s)\n", printBool( m_useAuxOrigin ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "useauxorigin", m_useAuxOrigin );
// HPGL options
aFormatter->Print( aNestLevel+1, "(hpglpennumber %d)\n", m_HPGLPenNum );
@ -222,33 +217,37 @@ void PCB_PLOT_PARAMS::Format( OUTPUTFORMATTER* aFormatter,
aFormatter->Print( aNestLevel+1, "(hpglpendiameter %f)\n", m_HPGLPenDiam );
// PDF options
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_pdf_front_fp_property_popups ),
printBool( m_PDFFrontFPPropertyPopups ) );
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_pdf_back_fp_property_popups ),
printBool( m_PDFBackFPPropertyPopups ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1,
getTokenName( T_pdf_front_fp_property_popups ),
m_PDFFrontFPPropertyPopups );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1,
getTokenName( T_pdf_back_fp_property_popups ),
m_PDFBackFPPropertyPopups );
// DXF options
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_dxfpolygonmode ),
printBool( m_DXFPolygonMode ) );
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_dxfimperialunits ),
printBool( m_DXFUnits == DXF_UNITS::INCHES ) );
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_dxfusepcbnewfont ),
printBool( m_textMode != PLOT_TEXT_MODE::NATIVE ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, getTokenName( T_dxfpolygonmode ),
m_DXFPolygonMode );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, getTokenName( T_dxfimperialunits ),
m_DXFUnits == DXF_UNITS::INCHES );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, getTokenName( T_dxfusepcbnewfont ),
m_textMode != PLOT_TEXT_MODE::NATIVE );
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_psnegative ),
printBool( m_negative ) );
aFormatter->Print( aNestLevel+1, "(%s %s)\n", getTokenName( T_psa4output ),
printBool( m_A4Output ) );
aFormatter->Print( aNestLevel+1, "(plotreference %s)\n", printBool( m_plotReference ) );
aFormatter->Print( aNestLevel+1, "(plotvalue %s)\n", printBool( m_plotValue ) );
aFormatter->Print( aNestLevel+1, "(plotfptext %s)\n", printBool( m_plotFPText ) );
aFormatter->Print( aNestLevel+1, "(plotinvisibletext %s)\n", printBool( m_plotInvisibleText ) );
aFormatter->Print( aNestLevel+1, "(sketchpadsonfab %s)\n",
printBool( m_sketchPadsOnFabLayers ) );
aFormatter->Print( aNestLevel+1, "(subtractmaskfromsilk %s)\n",
printBool( m_subtractMaskFromSilk ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, getTokenName( T_psnegative ),
m_negative );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, getTokenName( T_psa4output ),
m_A4Output );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "plotreference", m_plotReference );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "plotvalue", m_plotValue );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "plotfptext", m_plotFPText );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "plotinvisibletext",
m_plotInvisibleText );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "sketchpadsonfab",
m_sketchPadsOnFabLayers );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "subtractmaskfromsilk",
m_subtractMaskFromSilk );
aFormatter->Print( aNestLevel+1, "(outputformat %d)\n", static_cast<int>( m_format ) );
aFormatter->Print( aNestLevel+1, "(mirror %s)\n", printBool( m_mirror ) );
KICAD_FORMAT::FormatBool( aFormatter, aNestLevel + 1, "mirror", m_mirror );
aFormatter->Print( aNestLevel+1, "(drillshape %d)\n", (int)m_drillMarks );
aFormatter->Print( aNestLevel+1, "(scaleselection %d)\n", m_scaleSelection );
aFormatter->Print( aNestLevel+1, "(outputdirectory \"%s\")",
@ -663,10 +662,20 @@ bool PCB_PLOT_PARAMS_PARSER::parseBool()
{
T token = NeedSYMBOL();
if( token != T_false && token != T_true )
Expecting( "true|false" );
switch( token )
{
case T_false:
case T_no:
return false;
return token == T_true;
case T_true:
case T_yes:
return true;
default:
Expecting( "true, false, yes, or no" );
return false;
}
}

View File

@ -228,6 +228,40 @@ bool PCB_PARSER::parseBool()
}
/*
* e.g. "hide", "hide)", "(hide yes)"
*/
bool PCB_PARSER::parseMaybeAbsentBool( bool aDefaultValue )
{
bool ret = aDefaultValue;
if( PrevTok() == T_LEFT )
{
T token = NextTok();
// "hide)"
if( static_cast<int>( token ) == DSN_RIGHT )
return aDefaultValue;
if( token == T_yes || token == T_true )
ret = true;
else if( token == T_no || token == T_false )
ret = false;
else
Expecting( "yes or no" );
NeedRIGHT();
}
else
{
// "hide"
return aDefaultValue;
}
return ret;
}
wxString PCB_PARSER::GetRequiredVersion()
{
int year, month, day;
@ -569,7 +603,10 @@ void PCB_PARSER::parseEDA_TEXT( EDA_TEXT* aText )
break;
case T_hide:
aText->SetVisible( false );
if( NextTok() == T_yes || NextTok() == T_no )
aText->SetVisible( !parseBool() );
else
aText->SetVisible( false );
break;
default:
@ -678,8 +715,11 @@ FP_3DMODEL* PCB_PARSER::parse3DModel()
break;
case T_hide:
n3D->m_Show = false;
{
bool hide = parseMaybeAbsentBool( true );
n3D->m_Show = !hide;
break;
}
case T_opacity:
n3D->m_Opacity = parseDouble( "opacity value" );
@ -1245,8 +1285,7 @@ void PCB_PARSER::parseGeneralSection()
break;
case T_legacy_teardrops:
m_board->SetLegacyTeardrops( true );
NeedRIGHT();
m_board->SetLegacyTeardrops( parseMaybeAbsentBool( true ) );
break;
default: // Skip everything else.
@ -2900,11 +2939,13 @@ PCB_SHAPE* PCB_PARSER::parsePCB_SHAPE( BOARD_ITEM* aParent )
NeedRIGHT();
break;
// Continue to process "(locked)" format which was output during 5.99 development
// Handle (locked) from 5.99 development, and (locked yes) from modern times
case T_locked:
shape->SetLocked( true );
NeedRIGHT();
{
bool locked = parseMaybeAbsentBool( true );
shape->SetLocked( locked );
break;
}
case T_net:
if( !shape->SetNetCode( getNetCode( parseInt( "net number" ) ), /* aNoAssert */ true ) )
@ -3064,6 +3105,7 @@ PCB_TEXT* PCB_PARSER::parsePCB_TEXT( BOARD_ITEM* aParent )
text = std::make_unique<PCB_TEXT>( aParent );
}
// Legacy bare locked token
if( token == T_locked )
{
text->SetLocked( true );
@ -3119,6 +3161,7 @@ void PCB_PARSER::parsePCB_TEXT_effects( PCB_TEXT* aText )
token = NextTok();
}
// Legacy location of this token; presence implies true
if( parentFP && CurTok() == T_unlocked )
{
aText->SetKeepUpright( false );
@ -3154,12 +3197,34 @@ void PCB_PARSER::parsePCB_TEXT_effects( PCB_TEXT* aText )
break;
case T_hide:
{
// In older files, the hide token appears bare, and indicates hide==true.
// In newer files, it will be an explicit bool in a list like (hide yes)
bool hide = parseMaybeAbsentBool( true );
if( parentFP )
aText->SetVisible( false );
aText->SetVisible( !hide );
else
Expecting( "layer, effects, locked, render_cache or tstamp" );
break;
}
case T_locked:
// Newer list-enclosed locked
aText->SetLocked( parseBool() );
NeedRIGHT();
break;
// Confusingly, "unlocked" is not the opposite of "locked", but refers to "keep upright"
case T_unlocked:
if( parentFP )
aText->SetKeepUpright( !parseBool() );
else
Expecting( "layer, effects, locked, render_cache or tstamp" );
NeedRIGHT();
break;
case T_effects:
parseEDA_TEXT( static_cast<EDA_TEXT*>( aText ) );
@ -3332,6 +3397,7 @@ PCB_DIMENSION_BASE* PCB_PARSER::parseDIMENSION( BOARD_ITEM* aParent )
token = NextTok();
// Free 'locked' token from 6.0/7.0 formats
if( token == T_locked )
{
locked = true;
@ -3727,6 +3793,15 @@ PCB_DIMENSION_BASE* PCB_PARSER::parseDIMENSION( BOARD_ITEM* aParent )
NeedRIGHT();
break;
// Handle (locked yes) from modern times
case T_locked:
{
// Unsure if we ever wrote out (locked) for dimensions, so use maybeAbsent just in case
bool isLocked = parseMaybeAbsentBool( true );
dim->SetLocked( isLocked );
break;
}
default:
Expecting( "layer, tstamp, gr_text, feature1, feature2, crossbar, arrow1a, "
"arrow1b, arrow2a, or arrow2b" );
@ -4654,14 +4729,18 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
break;
case T_remove_unused_layers:
pad->SetRemoveUnconnected( true );
NeedRIGHT();
{
bool remove = parseMaybeAbsentBool( true );
pad->SetRemoveUnconnected( remove );
break;
}
case T_keep_end_layers:
pad->SetKeepTopBottom( true );
NeedRIGHT();
{
bool keep = parseMaybeAbsentBool( true );
pad->SetKeepTopBottom( keep );
break;
}
case T_zone_layer_connections:
{
@ -4686,7 +4765,7 @@ PAD* PCB_PARSER::parsePAD( FOOTPRINT* aParent )
// Continue to process "(locked)" format which was output during 5.99 development
case T_locked:
// Pad locking is now a session preference
NeedRIGHT();
parseMaybeAbsentBool( true );
break;
case T_tstamp:
@ -4864,24 +4943,36 @@ void PCB_PARSER::parseGROUP( BOARD_ITEM* aParent )
Expecting( "group name or locked" );
}
token = NextTok();
for( ; token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
if( token != T_id )
Expecting( T_id );
token = NextTok();
NextTok();
groupInfo.uuid = CurStrToKIID();
NeedRIGHT();
switch( token )
{
case T_id:
NextTok();
groupInfo.uuid = CurStrToKIID();
NeedRIGHT();
break;
NeedLEFT();
token = NextTok();
case T_locked:
groupInfo.locked = parseBool();
NeedRIGHT();
break;
if( token != T_members )
Expecting( T_members );
case T_members:
{
parseGROUP_members( groupInfo );
break;
}
parseGROUP_members( groupInfo );
NeedRIGHT();
default:
Expecting( "id, locked, or members" );
}
}
}
@ -5035,6 +5126,7 @@ PCB_ARC* PCB_PARSER::parseARC()
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
// Legacy locked
if( token == T_locked )
{
arc->SetLocked( true );
@ -5052,26 +5144,31 @@ PCB_ARC* PCB_PARSER::parseARC()
pt.x = parseBoardUnits( "start x" );
pt.y = parseBoardUnits( "start y" );
arc->SetStart( pt );
NeedRIGHT();
break;
case T_mid:
pt.x = parseBoardUnits( "mid x" );
pt.y = parseBoardUnits( "mid y" );
arc->SetMid( pt );
NeedRIGHT();
break;
case T_end:
pt.x = parseBoardUnits( "end x" );
pt.y = parseBoardUnits( "end y" );
arc->SetEnd( pt );
NeedRIGHT();
break;
case T_width:
arc->SetWidth( parseBoardUnits( "width" ) );
NeedRIGHT();
break;
case T_layer:
arc->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_net:
@ -5080,28 +5177,28 @@ PCB_ARC* PCB_PARSER::parseARC()
wxLogError( _( "Invalid net ID in\nfile: %s\nline: %d\noffset: %d." ),
CurSource(), CurLineNumber(), CurOffset() );
}
NeedRIGHT();
break;
case T_tstamp:
NextTok();
const_cast<KIID&>( arc->m_Uuid ) = CurStrToKIID();
NeedRIGHT();
break;
// We continue to parse the status field but it is no longer written
case T_status:
parseHex();
NeedRIGHT();
break;
// Continue to process "(locked)" format which was output during 5.99 development
case T_locked:
arc->SetLocked( true );
arc->SetLocked( parseMaybeAbsentBool( true ) );
break;
default:
Expecting( "start, mid, end, width, layer, net, tstamp, or status" );
}
NeedRIGHT();
}
return arc.release();
@ -5120,6 +5217,7 @@ PCB_TRACK* PCB_PARSER::parsePCB_TRACK()
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
// Legacy locked flag
if( token == T_locked )
{
track->SetLocked( true );
@ -5137,20 +5235,24 @@ PCB_TRACK* PCB_PARSER::parsePCB_TRACK()
pt.x = parseBoardUnits( "start x" );
pt.y = parseBoardUnits( "start y" );
track->SetStart( pt );
NeedRIGHT();
break;
case T_end:
pt.x = parseBoardUnits( "end x" );
pt.y = parseBoardUnits( "end y" );
track->SetEnd( pt );
NeedRIGHT();
break;
case T_width:
track->SetWidth( parseBoardUnits( "width" ) );
NeedRIGHT();
break;
case T_layer:
track->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_net:
@ -5159,28 +5261,28 @@ PCB_TRACK* PCB_PARSER::parsePCB_TRACK()
wxLogError( _( "Invalid net ID in\nfile: '%s'\nline: %d\noffset: %d." ),
CurSource(), CurLineNumber(), CurOffset() );
}
NeedRIGHT();
break;
case T_tstamp:
NextTok();
const_cast<KIID&>( track->m_Uuid ) = CurStrToKIID();
NeedRIGHT();
break;
// We continue to parse the status field but it is no longer written
case T_status:
parseHex();
NeedRIGHT();
break;
// Continue to process "(locked)" format which was output during 5.99 development
case T_locked:
track->SetLocked( true );
track->SetLocked( parseMaybeAbsentBool( true ) );
break;
default:
Expecting( "start, end, width, layer, net, tstamp, or locked" );
}
NeedRIGHT();
}
return track.release();
@ -5203,6 +5305,7 @@ PCB_VIA* PCB_PARSER::parsePCB_VIA()
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
// Legacy locked
if( token == T_locked )
{
via->SetLocked( true );
@ -5263,14 +5366,18 @@ PCB_VIA* PCB_PARSER::parsePCB_VIA()
break;
case T_remove_unused_layers:
via->SetRemoveUnconnected( true );
NeedRIGHT();
{
bool remove = parseMaybeAbsentBool( true );
via->SetRemoveUnconnected( remove );
break;
}
case T_keep_end_layers:
via->SetKeepStartEnd( true );
NeedRIGHT();
{
bool keep = parseMaybeAbsentBool( true );
via->SetKeepStartEnd( keep );
break;
}
case T_zone_layer_connections:
{
@ -5310,15 +5417,12 @@ PCB_VIA* PCB_PARSER::parsePCB_VIA()
NeedRIGHT();
break;
// Continue to process "(locked)" format which was output during 5.99 development
case T_locked:
via->SetLocked( true );
NeedRIGHT();
via->SetLocked( parseMaybeAbsentBool( true ) );
break;
case T_free:
via->SetIsFree();
NeedRIGHT();
via->SetIsFree( parseMaybeAbsentBool( true ) );
break;
default:
@ -5360,6 +5464,7 @@ ZONE* PCB_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent )
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
// legacy locked
if( token == T_locked )
{
zone->SetLocked( true );
@ -5855,9 +5960,14 @@ ZONE* PCB_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent )
}
break;
case T_locked:
zone->SetLocked( parseBool() );
NeedRIGHT();
break;
default:
Expecting( "net, layer/layers, tstamp, hatch, priority, connect_pads, min_thickness, "
"fill, polygon, filled_polygon, fill_segments, attr, or name" );
"fill, polygon, filled_polygon, fill_segments, attr, locked, or name" );
}
}

View File

@ -343,6 +343,18 @@ private:
bool parseBool();
/**
* Parses a boolean flag inside a list that existed before boolean normalization.
*
* For example, this will handle both (legacy_teardrops) and (legacy_teardrops yes).
* Call this after parsing the T_legacy_teardrops, and aDefaultValue will be returned for the
* first case, or true will be returned for the second case.
*
* @param aDefaultValue will be returned if the end of the list is encountered as the next token
* @return the parsed boolean
*/
bool parseMaybeAbsentBool( bool aDefaultValue );
/*
* @return if m_appendToExisting, returns new KIID(), otherwise returns CurStr() as KIID.
*/

View File

@ -47,6 +47,7 @@
#include <zone.h>
#include <pcbnew_settings.h>
#include <pgm_base.h>
#include <plugins/kicad/kicad_plugin_utils.h>
#include <plugins/kicad/pcb_plugin.h>
#include <plugins/kicad/pcb_parser.h>
#include <trace_helpers.h>
@ -575,10 +576,8 @@ void PCB_PLUGIN::formatSetup( const BOARD* aBoard, int aNestLevel ) const
FormatDouble2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() );
}
if( dsnSettings.m_AllowSoldermaskBridgesInFPs )
{
m_out->Print( aNestLevel+1, "(allow_soldermask_bridges_in_footprints yes)\n" );
}
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "allow_soldermask_bridges_in_footprints",
dsnSettings.m_AllowSoldermaskBridgesInFPs );
VECTOR2I origin = dsnSettings.GetAuxOrigin();
@ -614,8 +613,8 @@ void PCB_PLUGIN::formatGeneral( const BOARD* aBoard, int aNestLevel ) const
m_out->Print( aNestLevel+1, "(thickness %s)\n",
formatInternalUnits( dsnSettings.GetBoardThickness() ).c_str() );
if( aBoard->LegacyTeardrops() )
m_out->Print( aNestLevel+1, "(legacy_teardrops)\n" );
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "legacy_teardrops",
aBoard->LegacyTeardrops() );
m_out->Print( aNestLevel, ")\n\n" );
@ -839,9 +838,6 @@ void PCB_PLUGIN::format( const PCB_DIMENSION_BASE* aDimension, int aNestLevel )
m_out->Print( aNestLevel, "(dimension" );
if( aDimension->IsLocked() )
m_out->Print( 0, " locked" );
if( ortho ) // must be tested before aligned, because ortho is derived from aligned
// and aligned is not null
m_out->Print( 0, " (type orthogonal)" );
@ -856,6 +852,9 @@ void PCB_PLUGIN::format( const PCB_DIMENSION_BASE* aDimension, int aNestLevel )
else
wxFAIL_MSG( wxT( "Cannot format unknown dimension type!" ) );
if( aDimension->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "locked", aDimension->IsLocked() );
formatLayer( aDimension->GetLayer() );
m_out->Print( 0, " (tstamp %s)", TO_UTF8( aDimension->m_Uuid.AsString() ) );
@ -939,38 +938,33 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
{
FOOTPRINT* parentFP = aShape->GetParentFootprint();
std::string prefix = parentFP ? "fp" : "gr";
std::string locked = aShape->IsLocked() ? " locked" : "";
switch( aShape->GetShape() )
{
case SHAPE_T::SEGMENT:
m_out->Print( aNestLevel, "(%s_line%s (start %s) (end %s)\n",
m_out->Print( aNestLevel, "(%s_line (start %s) (end %s)\n",
prefix.c_str(),
locked.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
break;
case SHAPE_T::RECTANGLE:
m_out->Print( aNestLevel, "(%s_rect%s (start %s) (end %s)\n",
m_out->Print( aNestLevel, "(%s_rect (start %s) (end %s)\n",
prefix.c_str(),
locked.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
break;
case SHAPE_T::CIRCLE:
m_out->Print( aNestLevel, "(%s_circle%s (center %s) (end %s)\n",
m_out->Print( aNestLevel, "(%s_circle (center %s) (end %s)\n",
prefix.c_str(),
locked.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
break;
case SHAPE_T::ARC:
m_out->Print( aNestLevel, "(%s_arc%s (start %s) (mid %s) (end %s)\n",
m_out->Print( aNestLevel, "(%s_arc (start %s) (mid %s) (end %s)\n",
prefix.c_str(),
locked.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetArcMid(), parentFP ).c_str(),
formatInternalUnits( aShape->GetEnd(), parentFP ).c_str() );
@ -982,9 +976,7 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
const SHAPE_POLY_SET& poly = aShape->GetPolyShape();
const SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
m_out->Print( aNestLevel, "(%s_poly%s\n",
prefix.c_str(),
locked.c_str() );
m_out->Print( aNestLevel, "(%s_poly\n", prefix.c_str() );
formatPolyPts( outline, aNestLevel, ADVANCED_CFG::GetCfg().m_CompactSave, parentFP );
}
else
@ -996,9 +988,8 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
break;
case SHAPE_T::BEZIER:
m_out->Print( aNestLevel, "(%s_curve%s (pts (xy %s) (xy %s) (xy %s) (xy %s))\n",
m_out->Print( aNestLevel, "(%s_curve (pts (xy %s) (xy %s) (xy %s) (xy %s))\n",
prefix.c_str(),
locked.c_str(),
formatInternalUnits( aShape->GetStart(), parentFP ).c_str(),
formatInternalUnits( aShape->GetBezierC1(), parentFP ).c_str(),
formatInternalUnits( aShape->GetBezierC2(), parentFP ).c_str(),
@ -1010,6 +1001,9 @@ void PCB_PLUGIN::format( const PCB_SHAPE* aShape, int aNestLevel ) const
return;
};
if( aShape->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "locked", aShape->IsLocked() );
aShape->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
// The filled flag represents if a solid fill is present on circles, rectangles and polygons
@ -1313,9 +1307,11 @@ void PCB_PLUGIN::format( const FOOTPRINT* aFootprint, int aNestLevel ) const
{
if( !bs3D->m_Filename.IsEmpty() )
{
m_out->Print( aNestLevel+1, "(model %s%s\n",
m_out->Quotew( bs3D->m_Filename ).c_str(),
bs3D->m_Show ? "" : " hide" );
m_out->Print( aNestLevel+1, "(model %s\n",
m_out->Quotew( bs3D->m_Filename ).c_str() );
if( !bs3D->m_Show )
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "hide", !bs3D->m_Show );
if( bs3D->m_Opacity != 1.0 )
m_out->Print( aNestLevel+2, "(opacity %0.4f)", bs3D->m_Opacity );
@ -1486,9 +1482,6 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
type,
shape );
if( aPad->IsLocked() )
m_out->Print( 0, " locked" );
m_out->Print( 0, " (at %s", formatInternalUnits( aPad->GetFPRelativePosition() ).c_str() );
if( !aPad->GetOrientation().IsZero() )
@ -1532,12 +1525,11 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
if( aPad->GetAttribute() == PAD_ATTRIB::PTH )
{
KICAD_FORMAT::FormatBool( m_out, 0, "remove_unused_layers", aPad->GetRemoveUnconnected() );
if( aPad->GetRemoveUnconnected() )
{
m_out->Print( 0, " (remove_unused_layers)" );
if( aPad->GetKeepTopBottom() )
m_out->Print( 0, " (keep_end_layers)" );
KICAD_FORMAT::FormatBool( m_out, 0, "keep_end_layers", aPad->GetKeepTopBottom() );
if( board ) // Will be nullptr in footprint library
{
@ -1793,7 +1785,7 @@ void PCB_PLUGIN::format( const PAD* aPad, int aNestLevel ) const
|| ( primitive->GetShape() == SHAPE_T::RECTANGLE )
|| ( primitive->GetShape() == SHAPE_T::CIRCLE ) )
{
m_out->Print( 0, primitive->IsFilled() ? " (fill yes)" : " (fill none)" );
KICAD_FORMAT::FormatBool( m_out, 0, "fill", primitive->IsFilled() );
}
m_out->Print( 0, ")" );
@ -1843,9 +1835,11 @@ void PCB_PLUGIN::format( const PCB_TEXT* aText, int aNestLevel ) const
if( !isField )
{
m_out->Print( aNestLevel, "(%s_text%s%s %s", prefix.c_str(), type.c_str(),
aText->IsLocked() ? " locked" : "",
m_out->Print( aNestLevel, "(%s_text%s %s", prefix.c_str(), type.c_str(),
m_out->Quotew( aText->GetText() ).c_str() );
if( aText->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, 0, "locked", aText->IsLocked() );
}
m_out->Print( 0, " (at %s", formatInternalUnits( pos ).c_str() );
@ -1854,15 +1848,15 @@ void PCB_PLUGIN::format( const PCB_TEXT* aText, int aNestLevel ) const
// To avoid issues in the future, always save the angle, even if it is 0
m_out->Print( 0, " %s", EDA_UNIT_UTILS::FormatAngle( aText->GetTextAngle() ).c_str() );
if( parentFP && !aText->IsKeepUpright() )
m_out->Print( 0, " unlocked" );
m_out->Print( 0, ")" );
if( parentFP && !aText->IsKeepUpright() )
KICAD_FORMAT::FormatBool( m_out, 0, "unlocked", !aText->IsKeepUpright() );
formatLayer( aText->GetLayer(), aText->IsKnockout() );
if( parentFP && !aText->IsVisible() )
m_out->Print( 0, " hide" );
KICAD_FORMAT::FormatBool( m_out, 0, "hide", !aText->IsVisible() );
m_out->Print( 0, " (tstamp %s)", TO_UTF8( aText->m_Uuid.AsString() ) );
@ -1882,11 +1876,13 @@ void PCB_PLUGIN::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) const
{
FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
m_out->Print( aNestLevel, "(%s%s %s\n",
m_out->Print( aNestLevel, "(%s %s\n",
parentFP ? "fp_text_box" : "gr_text_box",
aTextBox->IsLocked() ? " locked" : "",
m_out->Quotew( aTextBox->GetText() ).c_str() );
if( aTextBox->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, aNestLevel, "locked", aTextBox->IsLocked() );
if( aTextBox->GetShape() == SHAPE_T::RECTANGLE )
{
m_out->Print( aNestLevel + 1, "(start %s) (end %s)",
@ -1926,12 +1922,7 @@ void PCB_PLUGIN::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) const
// PCB_TEXTBOXes are never hidden, so always omit "hide" attribute
aTextBox->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
m_out->Print( aNestLevel + 1, "(border" );
if( aTextBox->IsBorderEnabled() )
m_out->Print( 0, " yes)" );
else
m_out->Print( 0, " no)" );
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "border", aTextBox->IsBorderEnabled() );
aTextBox->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
@ -1948,11 +1939,13 @@ void PCB_PLUGIN::format( const PCB_GROUP* aGroup, int aNestLevel ) const
if( aGroup->GetItems().empty() )
return;
m_out->Print( aNestLevel, "(group %s%s (id %s)\n",
m_out->Print( aNestLevel, "(group %s (id %s)\n",
m_out->Quotew( aGroup->GetName() ).c_str(),
aGroup->IsLocked() ? " locked" : "",
TO_UTF8( aGroup->m_Uuid.AsString() ) );
if( aGroup->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "locked", aGroup->IsLocked() );
m_out->Print( aNestLevel + 1, "(members\n" );
wxArrayString memberIds;
@ -2098,9 +2091,6 @@ void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
THROW_IO_ERROR( wxString::Format( _( "unknown via type %d" ), via->GetViaType() ) );
}
if( via->IsLocked() )
m_out->Print( 0, " locked" );
m_out->Print( 0, " (at %s) (size %s)",
formatInternalUnits( aTrack->GetStart() ).c_str(),
formatInternalUnits( aTrack->GetWidth() ).c_str() );
@ -2121,14 +2111,17 @@ void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
if( via->GetRemoveUnconnected() )
{
m_out->Print( 0, " (remove_unused_layers)" );
KICAD_FORMAT::FormatBool( m_out, 0, "remove_unused_layers",
via->GetRemoveUnconnected() );
if( via->GetKeepStartEnd() )
m_out->Print( 0, " (keep_end_layers)" );
KICAD_FORMAT::FormatBool( m_out, 0, "keep_end_layers", via->GetKeepStartEnd() );
}
if( via->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, 0, "locked", via->IsLocked() );
if( via->GetIsFree() )
m_out->Print( 0, " (free)" );
KICAD_FORMAT::FormatBool( m_out, 0, "free", via->GetIsFree() );
if( via->GetRemoveUnconnected() )
{
@ -2153,23 +2146,27 @@ void PCB_PLUGIN::format( const PCB_TRACK* aTrack, int aNestLevel ) const
{
const PCB_ARC* arc = static_cast<const PCB_ARC*>( aTrack );
m_out->Print( aNestLevel, "(arc%s (start %s) (mid %s) (end %s) (width %s)",
arc->IsLocked() ? " locked" : "",
m_out->Print( aNestLevel, "(arc (start %s) (mid %s) (end %s) (width %s)",
formatInternalUnits( arc->GetStart() ).c_str(),
formatInternalUnits( arc->GetMid() ).c_str(),
formatInternalUnits( arc->GetEnd() ).c_str(),
formatInternalUnits( arc->GetWidth() ).c_str() );
if( arc->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, 0, "locked", arc->IsLocked() );
m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( arc->GetLayer() ) ).c_str() );
}
else
{
m_out->Print( aNestLevel, "(segment%s (start %s) (end %s) (width %s)",
aTrack->IsLocked() ? " locked" : "",
m_out->Print( aNestLevel, "(segment (start %s) (end %s) (width %s)",
formatInternalUnits( aTrack->GetStart() ).c_str(),
formatInternalUnits( aTrack->GetEnd() ).c_str(),
formatInternalUnits( aTrack->GetWidth() ).c_str() );
if( aTrack->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, 0, "locked", aTrack->IsLocked() );
m_out->Print( 0, " (layer %s)", m_out->Quotew( LSET::Name( aTrack->GetLayer() ) ).c_str() );
}
@ -2189,11 +2186,13 @@ void PCB_PLUGIN::format( const ZONE* aZone, int aNestLevel ) const
// (perhaps netcode and netname should be not stored)
bool has_no_net = aZone->GetIsRuleArea() || !aZone->IsOnCopperLayer();
m_out->Print( aNestLevel, "(zone%s (net %d) (net_name %s)",
aZone->IsLocked() ? " locked" : "",
m_out->Print( aNestLevel, "(zone (net %d) (net_name %s)",
has_no_net ? 0 : m_mapping->Translate( aZone->GetNetCode() ),
m_out->Quotew( has_no_net ? wxString( wxT("") ) : aZone->GetNetname() ).c_str() );
if( aZone->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, 0, "locked", aZone->IsLocked() );
// If a zone exists on multiple layers, format accordingly
if( aZone->GetLayerSet().count() > 1 )
{

View File

@ -142,12 +142,14 @@ class PCB_PLUGIN; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20230825 // Textbox explicit border flag
//#define SEXPR_BOARD_FILE_VERSION 20230906 // Multiple image type support in files
//#define SEXPR_BOARD_FILE_VERSION 20230913 // Custom-shaped-pad spoke templates
#define SEXPR_BOARD_FILE_VERSION 20231007 // Generative objects
//#define SEXPR_BOARD_FILE_VERSION 20231007 // Generative objects
#define SEXPR_BOARD_FILE_VERSION 20231014 // V8 file format normalization
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
#define LEGACY_NET_TIES 20220815 ///< These were the last to use the keywords field
///< to indicate a net-tie.
#define FIRST_NORMALIZED_VERISON 20230924 ///< Earlier files did not have normalized bools
#define CTL_OMIT_PAD_NETS (1 << 1) ///< Omit pads net names (useless in library)
#define CTL_OMIT_TSTAMPS (1 << 2) ///< Omit component time stamp (useless in library)