diff --git a/3d-viewer/3d_canvas/board_adapter.cpp b/3d-viewer/3d_canvas/board_adapter.cpp index 335b6ab90f..8fc70360c8 100644 --- a/3d-viewer/3d_canvas/board_adapter.cpp +++ b/3d-viewer/3d_canvas/board_adapter.cpp @@ -188,8 +188,7 @@ bool BOARD_ADAPTER::Is3DLayerEnabled( PCB_LAYER_ID aLayer ) const case B_Cu: case F_Cu: - return m_board->GetDesignSettings().IsLayerVisible( aLayer ) || - GetFlag( FL_USE_REALISTIC_MODE ); + return m_board->IsLayerVisible( aLayer ) || GetFlag( FL_USE_REALISTIC_MODE ); break; default: @@ -201,7 +200,7 @@ bool BOARD_ADAPTER::Is3DLayerEnabled( PCB_LAYER_ID aLayer ) const return false; } - return m_board->GetDesignSettings().IsLayerVisible( aLayer ); + return m_board->IsLayerVisible( aLayer ); } // The layer has a flag, return the flag diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 0883e764f2..aac8a743c8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -317,8 +317,10 @@ set( COMMON_SRCS lib_tree_model.cpp lib_tree_model_adapter.cpp lockfile.cpp + lset.cpp marker_base.cpp msgpanel.cpp + netclass.cpp observable.cpp prependpath.cpp printout.cpp @@ -398,9 +400,12 @@ set( COMMON_SRCS settings/common_settings.cpp settings/json_settings.cpp settings/nested_settings.cpp - settings/project_file.cpp settings/settings_manager.cpp + project/net_settings.cpp + project/project_file.cpp + project/project_local_settings.cpp + libeval/numeric_evaluator.cpp ) @@ -438,7 +443,6 @@ set( PCB_COMMON_SRCS eda_text.cpp fp_lib_table.cpp hash_eda.cpp - lset.cpp origin_viewitem.cpp page_info.cpp ${CMAKE_SOURCE_DIR}/pcbnew/pcb_base_frame.cpp @@ -453,7 +457,6 @@ set( PCB_COMMON_SRCS ${CMAKE_SOURCE_DIR}/pcbnew/class_edge_mod.cpp ${CMAKE_SOURCE_DIR}/pcbnew/class_marker_pcb.cpp ${CMAKE_SOURCE_DIR}/pcbnew/class_module.cpp - ${CMAKE_SOURCE_DIR}/pcbnew/netclass.cpp ${CMAKE_SOURCE_DIR}/pcbnew/netinfo_item.cpp ${CMAKE_SOURCE_DIR}/pcbnew/netinfo_list.cpp ${CMAKE_SOURCE_DIR}/pcbnew/class_pad.cpp diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp index ac031f33cd..56677762c4 100644 --- a/common/lib_tree_model_adapter.cpp +++ b/common/lib_tree_model_adapter.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,7 @@ unsigned int LIB_TREE_MODEL_ADAPTER::IntoArray( LIB_TREE_NODE const& aNode, } -LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent ) : +LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, wxString aPinnedKey ) : m_parent( aParent ), m_filter( CMP_FILTER_NONE ), m_show_units( true ), @@ -81,7 +82,9 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent ) : m_freeze( 0 ), m_col_part( nullptr ), m_col_desc( nullptr ), - m_widget( nullptr ) + m_widget( nullptr ), + m_pinnedLibs(), + m_pinnedKey( aPinnedKey ) { // Default column widths m_colWidths[PART_COL] = 360; @@ -90,12 +93,15 @@ LIB_TREE_MODEL_ADAPTER::LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent ) : auto cfg = Kiface().KifaceSettings(); m_colWidths[PART_COL] = cfg->m_LibTree.column_width; - // TODO(JE) PROJECT -#if 0 // Read the pinned entries from the project config - m_parent->Kiway().Prj().ConfigLoad( Kiface().KifaceSearch(), m_parent->GetName(), - GetProjectFileParameters() ); -#endif + PROJECT_FILE& project = m_parent->Kiway().Prj().GetProjectFile(); + + std::vector& entries = ( m_pinnedKey == "pinned_symbol_libs" ) ? + project.m_PinnedSymbolLibs : + project.m_PinnedFootprintLibs; + + for( const wxString& entry : entries ) + m_pinnedLibs.push_back( entry ); } @@ -117,32 +123,27 @@ void LIB_TREE_MODEL_ADAPTER::SaveColWidths() } -std::vector& LIB_TREE_MODEL_ADAPTER::GetProjectFileParameters() -{ - if( !m_projectFileParams.empty() ) - return m_projectFileParams; - - m_projectFileParams.push_back( new PARAM_CFG_LIBNAME_LIST( PINNED_ITEMS_KEY, &m_pinnedLibs ) ); - - return m_projectFileParams; -} - - void LIB_TREE_MODEL_ADAPTER::SavePinnedItems() { + PROJECT_FILE& project = m_parent->Kiway().Prj().GetProjectFile(); + + std::vector& entries = ( m_pinnedKey == "pinned_symbol_libs" ) ? + project.m_PinnedSymbolLibs : + project.m_PinnedFootprintLibs; + + entries.clear(); m_pinnedLibs.clear(); for( auto& child: m_tree.m_Children ) { if( child->m_Pinned ) + { m_pinnedLibs.push_back( child->m_LibId.GetLibNickname() ); + entries.push_back( child->m_LibId.GetLibNickname() ); + } } - // TODO(JE) PROJECT -#if 0 - m_parent->Kiway().Prj().ConfigSave( Kiface().KifaceSearch(), m_parent->GetName(), - GetProjectFileParameters() ); -#endif + } diff --git a/common/lib_tree_model_adapter.h b/common/lib_tree_model_adapter.h index eedda7ca81..016c22e57b 100644 --- a/common/lib_tree_model_adapter.h +++ b/common/lib_tree_model_adapter.h @@ -136,8 +136,6 @@ public: void SaveColWidths(); void SavePinnedItems(); - std::vector& GetProjectFileParameters(); - /** * Set the component filter type. Must be set before adding libraries * @@ -282,7 +280,12 @@ protected: LIB_TREE_NODE_ROOT m_tree; - LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent ); + /** + * Creates the adapter + * @param aParent is the parent frame + * @param aPinnedKey is the key to load the pinned libraries list from the project file + */ + LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, wxString aPinnedKey ); LIB_TREE_NODE_LIB& DoAddLibraryNode( wxString const& aNodeName, wxString const& aDesc ); @@ -368,6 +371,7 @@ private: int m_colWidths[NUM_COLS]; wxArrayString m_pinnedLibs; + wxString m_pinnedKey; /** * Find any results worth highlighting and expand them, according to given criteria diff --git a/pcbnew/netclass.cpp b/common/netclass.cpp similarity index 50% rename from pcbnew/netclass.cpp rename to common/netclass.cpp index a2edb1d172..fa2f419ae4 100644 --- a/pcbnew/netclass.cpp +++ b/common/netclass.cpp @@ -24,15 +24,16 @@ */ #include -#include #include -#include -#include #include -#include #include +#ifndef PCBNEW +#define PCBNEW // needed to define the right value of Millimeter2iu(x) +#endif +#include + // This will get mapped to "kicad_default" in the specctra_export. const char NETCLASS::Default[] = "Default"; @@ -152,84 +153,6 @@ NETCLASSPTR NETCLASSES::Find( const wxString& aName ) const } -void BOARD::SynchronizeNetsAndNetClasses() -{ - NETCLASSES& netClasses = m_designSettings.m_NetClasses; - NETCLASSPTR defaultNetClass = netClasses.GetDefault(); - - // set all NETs to the default NETCLASS, then later override some - // as we go through the NETCLASSes. - - for( NETINFO_LIST::iterator net( m_NetInfo.begin() ), netEnd( m_NetInfo.end() ); - net != netEnd; ++net ) - { - net->SetClass( defaultNetClass ); - } - - // Add netclass name and pointer to nets. If a net is in more than one netclass, - // set the net's name and pointer to only the first netclass. Subsequent - // and therefore bogus netclass memberships will be deleted in logic below this loop. - for( NETCLASSES::iterator clazz = netClasses.begin(); clazz != netClasses.end(); ++clazz ) - { - NETCLASSPTR netclass = clazz->second; - - for( NETCLASS::const_iterator member = netclass->begin(); member != netclass->end(); ++member ) - { - const wxString& netname = *member; - - // although this overall function seems to be adequately fast, - // FindNet( wxString ) uses now a fast binary search and is fast - // event for large net lists - NETINFO_ITEM* net = FindNet( netname ); - - if( net && net->GetClassName() == NETCLASS::Default ) - { - net->SetClass( netclass ); - } - } - } - - // Finally, make sure that every NET is in a NETCLASS, even if that - // means the Default NETCLASS. And make sure that all NETCLASSes do not - // contain netnames that do not exist, by deleting all netnames from - // every netclass and re-adding them. - - for( NETCLASSES::iterator clazz = netClasses.begin(); clazz != netClasses.end(); ++clazz ) - { - NETCLASSPTR netclass = clazz->second; - - netclass->Clear(); - } - - defaultNetClass->Clear(); - - for( NETINFO_LIST::iterator net( m_NetInfo.begin() ), netEnd( m_NetInfo.end() ); - net != netEnd; ++net ) - { - const wxString& classname = net->GetClassName(); - - // because of the std:map<> this should be fast, and because of - // prior logic, netclass should not be NULL. - NETCLASSPTR netclass = netClasses.Find( classname ); - - wxASSERT( netclass ); - - netclass->Add( net->GetNetname() ); - } - - // Set initial values for custom track width & via size to match the default netclass settings - m_designSettings.UseCustomTrackViaSize( false ); - m_designSettings.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() ); - m_designSettings.SetCustomViaSize( defaultNetClass->GetViaDiameter() ); - m_designSettings.SetCustomViaDrill( defaultNetClass->GetViaDrill() ); - m_designSettings.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() ); - m_designSettings.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() ); - m_designSettings.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() ); - - InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this ); -} - - #if defined(DEBUG) void NETCLASS::Show( int nestLevel, std::ostream& os ) const @@ -250,38 +173,3 @@ void NETCLASS::Show( int nestLevel, std::ostream& os ) const } #endif - - -void NETCLASS::Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const -{ - aFormatter->Print( aNestLevel, "(net_class %s %s\n", - aFormatter->Quotew( GetName() ).c_str(), - aFormatter->Quotew( GetDescription() ).c_str() ); - - aFormatter->Print( aNestLevel+1, "(clearance %s)\n", FormatInternalUnits( GetClearance() ).c_str() ); - aFormatter->Print( aNestLevel+1, "(trace_width %s)\n", FormatInternalUnits( GetTrackWidth() ).c_str() ); - - aFormatter->Print( aNestLevel+1, "(via_dia %s)\n", FormatInternalUnits( GetViaDiameter() ).c_str() ); - aFormatter->Print( aNestLevel+1, "(via_drill %s)\n", FormatInternalUnits( GetViaDrill() ).c_str() ); - - aFormatter->Print( aNestLevel+1, "(uvia_dia %s)\n", FormatInternalUnits( GetuViaDiameter() ).c_str() ); - aFormatter->Print( aNestLevel+1, "(uvia_drill %s)\n", FormatInternalUnits( GetuViaDrill() ).c_str() ); - - // Save the diff_pair_gap and diff_pair_width values only if not the default, to avoid unnecessary - // incompatibility with previous Pcbnew versions. - if( ( DEFAULT_DIFF_PAIR_WIDTH != GetDiffPairWidth() ) || - ( DEFAULT_DIFF_PAIR_GAP != GetDiffPairGap() ) ) - { - aFormatter->Print( aNestLevel+1, "(diff_pair_width %s)\n", - FormatInternalUnits( GetDiffPairWidth() ).c_str() ); - aFormatter->Print( aNestLevel+1, "(diff_pair_gap %s)\n", - FormatInternalUnits( GetDiffPairGap() ).c_str() ); - - // 6.0 TODO: figure out what to do with DiffPairViaGap... - } - - for( NETCLASS::const_iterator it = begin(); it != end(); ++it ) - aFormatter->Print( aNestLevel+1, "(add_net %s)\n", aFormatter->Quotew( *it ).c_str() ); - - aFormatter->Print( aNestLevel, ")\n\n" ); -} diff --git a/common/project.cpp b/common/project.cpp index ab0f6e50d4..ee98406979 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -23,20 +23,20 @@ #include +#include // NAMELESS_PROJECT +#include +#include #include +#include +#include +#include +#include #include #include #include -#include // NAMELESS_PROJECT -#include -#include -#include -#include -#include -#include -#include +#include #include -#include +#include PROJECT::PROJECT() : diff --git a/common/project/net_settings.cpp b/common/project/net_settings.cpp new file mode 100644 index 0000000000..441c8689c6 --- /dev/null +++ b/common/project/net_settings.cpp @@ -0,0 +1,142 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * @author Jon Evans + * + * 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 . + */ + +#include +#include + +// Netclasses were originally only stored in board files. The IU context is PCBNEW. +#ifndef PCBNEW +#define PCBNEW +#endif +#include + + +const int netSettingsSchemaVersion = 0; + + +NET_SETTINGS::NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) : + NESTED_SETTINGS( "net_settings", netSettingsSchemaVersion, aParent, aPath ), + m_NetClasses() +{ + m_params.emplace_back( new PARAM_LAMBDA( "classes", + [&]() -> nlohmann::json + { + nlohmann::json ret = nlohmann::json::array(); + + NETCLASSPTR netclass = m_NetClasses.GetDefault(); + NETCLASSES::const_iterator nc = m_NetClasses.begin(); + + for( unsigned int idx = 0; idx <= m_NetClasses.GetCount(); idx++ ) + { + if( idx > 0 ) + { + netclass = nc->second; + ++nc; + } + + nlohmann::json netJson = { + { "name", netclass->GetName().ToUTF8() }, + { "clearance", Iu2Millimeter( netclass->GetClearance() ) }, + { "track_width", Iu2Millimeter( netclass->GetTrackWidth() ) }, + { "via_diameter", Iu2Millimeter( netclass->GetViaDiameter() ) }, + { "via_drill", Iu2Millimeter( netclass->GetViaDrill() ) }, + { "microvia_diameter", Iu2Millimeter( netclass->GetuViaDiameter() ) }, + { "microvia_drill", Iu2Millimeter( netclass->GetuViaDrill() ) }, + { "diff_pair_width", Iu2Millimeter( netclass->GetDiffPairWidth() ) }, + { "diff_pair_gap", Iu2Millimeter( netclass->GetDiffPairGap() ) }, + { "diff_pair_via_gap", Iu2Millimeter( netclass->GetDiffPairViaGap() ) } + }; + + nlohmann::json nets = nlohmann::json::array(); + + for( NETCLASS::const_iterator i = netclass->begin(); i != netclass->end(); ++i ) + if( !i->empty() ) + nets.push_back( std::string( i->ToUTF8() ) ); + + netJson["nets"] = nets; + + ret.push_back( netJson ); + } + + return ret; + }, + [&]( const nlohmann::json& aJson ) + { + if( !aJson.is_array() ) + return; + + m_NetClasses.Clear(); + NETCLASSPTR netclass; + NETCLASSPTR defaultClass = m_NetClasses.GetDefault(); + + auto get = + []( const nlohmann::json& aObj, const std::string& aKey, int aDefault ) + { + if( aObj.contains( aKey ) ) + return Millimeter2iu( aObj[aKey].get() ); + else + return aDefault; + }; + + for( const nlohmann::json& entry : aJson ) + { + if( !entry.is_object() || !entry.contains( "name" ) ) + continue; + + wxString name = entry["name"]; + + if( name == defaultClass->GetName() ) + netclass = defaultClass; + else + netclass = std::make_shared( name ); + + netclass->SetClearance( get( aJson, "clearance", netclass->GetClearance() ) ); + netclass->SetTrackWidth( + get( aJson, "track_width", netclass->GetTrackWidth() ) ); + netclass->SetViaDiameter( + get( aJson, "via_diameter", netclass->GetViaDiameter() ) ); + netclass->SetViaDrill( get( aJson, "via_drill", netclass->GetViaDrill() ) ); + netclass->SetuViaDiameter( + get( aJson, "microvia_diameter", netclass->GetuViaDiameter() ) ); + netclass->SetuViaDrill( + get( aJson, "microvia_drill", netclass->GetuViaDrill() ) ); + netclass->SetDiffPairWidth( + get( aJson, "diff_pair_width", netclass->GetDiffPairWidth() ) ); + netclass->SetDiffPairGap( + get( aJson, "diff_pair_gap", netclass->GetDiffPairGap() ) ); + netclass->SetDiffPairViaGap( + get( aJson, "diff_pair_via_gap", netclass->GetDiffPairViaGap() ) ); + + if( netclass != defaultClass ) + m_NetClasses.Add( netclass ); + } + }, {} ) ); +} + + +NET_SETTINGS::~NET_SETTINGS() +{ + // Release early before destroying members + if( m_parent ) + { + m_parent->ReleaseNestedSettings( this ); + m_parent = nullptr; + } +} diff --git a/common/project/project_file.cpp b/common/project/project_file.cpp new file mode 100644 index 0000000000..ad64a3817b --- /dev/null +++ b/common/project/project_file.cpp @@ -0,0 +1,478 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * @author Jon Evans + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern const char* traceSettings; + +///! Update the schema version whenever a migration is required +const int projectFileSchemaVersion = 1; + + +PROJECT_FILE::PROJECT_FILE( const std::string& aFullPath ) : + JSON_SETTINGS( aFullPath, SETTINGS_LOC::PROJECT, projectFileSchemaVersion ), + m_sheets(), m_boards(), m_BoardSettings() +{ + // Keep old files around + m_deleteLegacyAfterMigration = false; + + m_params.emplace_back( new PARAM_LIST( "sheets", &m_sheets, {} ) ); + + m_params.emplace_back( new PARAM_LIST( "boards", &m_boards, {} ) ); + + m_params.emplace_back( + new PARAM_LIST( "libraries.pinned_symbol_libs", &m_PinnedSymbolLibs, {} ) ); + + m_params.emplace_back( new PARAM_LIST( + "libraries.pinned_footprint_libs", &m_PinnedFootprintLibs, {} ) ); + + m_params.emplace_back( + new PARAM_PATH_LIST( "cvpcb.equivalence_files", &m_EquivalenceFiles, {} ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.page_layout_descr_file", &m_PageLayoutDescrFile, "" ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.last_paths.netlist", &m_PcbLastPath[LAST_PATH_NETLIST], "" ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.last_paths.step", &m_PcbLastPath[LAST_PATH_STEP], "" ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.last_paths.idf", &m_PcbLastPath[LAST_PATH_IDF], "" ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.last_paths.vmrl", &m_PcbLastPath[LAST_PATH_VRML], "" ) ); + + m_params.emplace_back( new PARAM_PATH( + "pcbnew.last_paths.specctra_dsn", &m_PcbLastPath[LAST_PATH_SPECCTRADSN], "" ) ); + + m_params.emplace_back( + new PARAM_PATH( "pcbnew.last_paths.gencad", &m_PcbLastPath[LAST_PATH_GENCAD], "" ) ); + + m_NetSettings = std::make_shared( this, "net_settings" ); +} + + +bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) +{ + bool ret = true; + wxString str; + long index = 0; + + std::set group_blacklist; + + // Legacy files don't store board info; they assume board matches project name + // We will leave m_boards empty here so it can be populated with other code + + // First handle migration of data that will be stored locally in this object + + auto loadPinnedLibs = + [&]( const std::string& aDest ) + { + int libIndex = 1; + wxString libKey = wxT( "PinnedItems" ); + libKey << libIndex; + + nlohmann::json libs = nlohmann::json::array(); + + while( aCfg->Read( libKey, &str ) ) + { + libs.push_back( str ); + + aCfg->DeleteEntry( libKey, true ); + + libKey = wxT( "PinnedItems" ); + libKey << ++libIndex; + } + + ( *this )[PointerFromString( aDest )] = libs; + }; + + aCfg->SetPath( wxT( "/LibeditFrame" ) ); + loadPinnedLibs( "libraries.pinned_symbol_libs" ); + + aCfg->SetPath( wxT( "/ModEditFrame" ) ); + loadPinnedLibs( "libraries.pinned_footprint_libs" ); + + aCfg->SetPath( wxT( "/cvpcb/equfiles" ) ); + + { + int eqIdx = 1; + wxString eqKey = wxT( "EquName" ); + eqKey << eqIdx; + + nlohmann::json eqs = nlohmann::json::array(); + + while( aCfg->Read( eqKey, &str ) ) + { + eqs.push_back( str ); + + eqKey = wxT( "EquName" ); + eqKey << ++eqIdx; + } + + ( *this )[PointerFromString( "cvpcb.equivalence_files" )] = eqs; + } + + // All CvPcb params that we want to keep have been migrated above + group_blacklist.insert( wxT( "/cvpcb" ) ); + + aCfg->SetPath( wxT( "/pcbnew" ) ); + + fromLegacyString( aCfg, "PageLayoutDescrFile", "pcbnew.page_layout_descr_file" ); + fromLegacyString( aCfg, "LastNetListRead", "pcbnew.last_paths.netlist" ); + fromLegacyString( aCfg, "LastSTEPExportPath", "pcbnew.last_paths.step" ); + fromLegacyString( aCfg, "LastIDFExportPath", "pcbnew.last_paths.idf" ); + fromLegacyString( aCfg, "LastVRMLExportPath", "pcbnew.last_paths.vmrl" ); + fromLegacyString( aCfg, "LastSpecctraDSNExportPath", "pcbnew.last_paths.specctra_dsn" ); + fromLegacyString( aCfg, "LastGenCADExportPath", "pcbnew.last_paths.gencad" ); + + std::string bp = "board.design_settings."; + + { + int idx = 1; + wxString key = wxT( "DRCExclusion" ); + key << idx; + + nlohmann::json exclusions = nlohmann::json::array(); + + while( aCfg->Read( key, &str ) ) + { + exclusions.push_back( str ); + + key = wxT( "DRCExclusion" ); + key << ++idx; + } + + ( *this )[PointerFromString( bp + "drc_exclusions" )] = exclusions; + } + + fromLegacy( aCfg, "AllowMicroVias", bp + "rules.allow_microvias" ); + fromLegacy( aCfg, "AllowBlindVias", bp + "rules.allow_blind_buried_vias" ); + fromLegacy( aCfg, "MinClearance", bp + "rules.min_clearance" ); + fromLegacy( aCfg, "MinTrackWidth", bp + "rules.min_track_width" ); + fromLegacy( aCfg, "MinViaAnnulus", bp + "rules.min_via_annulus" ); + fromLegacy( aCfg, "MinViaDiameter", bp + "rules.min_via_diameter" ); + + if( !fromLegacy( aCfg, "MinThroughDrill", bp + "rules.min_through_hole_diameter" ) ) + fromLegacy( aCfg, "MinViaDrill", bp + "rules.min_through_hole_diameter" ); + + fromLegacy( aCfg, "MinMicroViaDiameter", bp + "rules.min_microvia_diameter" ); + fromLegacy( aCfg, "MinMicroViaDrill", bp + "rules.min_microvia_drill" ); + fromLegacy( aCfg, "MinHoleToHole", bp + "rules.min_hole_to_hole" ); + fromLegacy( aCfg, "CopperEdgeClearance", bp + "rules.min_copper_edge_clearance" ); + fromLegacy( aCfg, "SolderMaskClearance", bp + "rules.solder_mask_clearance" ); + fromLegacy( aCfg, "SolderMaskMinWidth", bp + "rules.solder_mask_min_width" ); + fromLegacy( aCfg, "SolderPasteClearance", bp + "rules.solder_paste_clearance" ); + fromLegacy( aCfg, "SolderPasteRatio", bp + "rules.solder_paste_margin_ratio" ); + + if( !fromLegacy( aCfg, "SilkLineWidth", bp + "defaults.silk_line_width" ) ) + fromLegacy( aCfg, "ModuleOutlineThickness", bp + "defaults.silk_line_width" ); + + if( !fromLegacy( aCfg, "SilkTextSizeV", bp + "defaults.silk_text_size_v" ) ) + fromLegacy( aCfg, "ModuleTextSizeV", bp + "defaults.silk_text_size_v" ); + + if( !fromLegacy( aCfg, "SilkTextSizeH", bp + "defaults.silk_text_size_h" ) ) + fromLegacy( aCfg, "ModuleTextSizeH", bp + "defaults.silk_text_size_h" ); + + if( !fromLegacy( aCfg, "SilkTextSizeThickness", bp + "defaults.silk_text_thickness" ) ) + fromLegacy( aCfg, "ModuleTextSizeThickness", bp + "defaults.silk_text_thickness" ); + + fromLegacy( aCfg, "SilkTextItalic", bp + "defaults.silk_text_italic" ); + fromLegacy( aCfg, "SilkTextUpright", bp + "defaults.silk_text_upright" ); + + if( !fromLegacy( aCfg, "CopperLineWidth", bp + "defaults.copper_line_width" ) ) + fromLegacy( aCfg, "DrawSegmentWidth", bp + "defaults.copper_line_width" ); + + if( !fromLegacy( aCfg, "CopperTextSizeV", bp + "defaults.copper_text_size_v" ) ) + fromLegacy( aCfg, "PcbTextSizeV", bp + "defaults.copper_text_size_v" ); + + if( !fromLegacy( aCfg, "CopperTextSizeH", bp + "defaults.copper_text_size_h" ) ) + fromLegacy( aCfg, "PcbTextSizeH", bp + "defaults.copper_text_size_h" ); + + if( !fromLegacy( aCfg, "CopperTextThickness", bp + "defaults.copper_text_thickness" ) ) + fromLegacy( aCfg, "PcbTextThickness", bp + "defaults.copper_text_thickness" ); + + fromLegacy( aCfg, "CopperTextItalic", bp + "defaults.copper_text_italic" ); + fromLegacy( aCfg, "CopperTextUpright", bp + "defaults.copper_text_upright" ); + + if( !fromLegacy( aCfg, "EdgeCutLineWidth", bp + "defaults.board_outline_line_width" ) ) + fromLegacy( + aCfg, "BoardOutlineThickness", bp + "defaults.board_outline_line_width" ); + + fromLegacy( aCfg, "CourtyardLineWidth", bp + "defaults.courtyard_line_width" ); + + fromLegacy( aCfg, "FabLineWidth", bp + "defaults.fab_line_width" ); + fromLegacy( aCfg, "FabTextSizeV", bp + "defaults.fab_text_size_v" ); + fromLegacy( aCfg, "FabTextSizeH", bp + "defaults.fab_text_size_h" ); + fromLegacy( aCfg, "FabTextSizeThickness", bp + "defaults.fab_text_thickness" ); + fromLegacy( aCfg, "FabTextItalic", bp + "defaults.fab_text_italic" ); + fromLegacy( aCfg, "FabTextUpright", bp + "defaults.fab_text_upright" ); + + if( !fromLegacy( aCfg, "OthersLineWidth", bp + "defaults.other_line_width" ) ) + fromLegacy( aCfg, "ModuleOutlineThickness", bp + "defaults.other_line_width" ); + + fromLegacy( aCfg, "OthersTextSizeV", bp + "defaults.other_text_size_v" ); + fromLegacy( aCfg, "OthersTextSizeH", bp + "defaults.other_text_size_h" ); + fromLegacy( aCfg, "OthersTextSizeThickness", bp + "defaults.other_text_thickness" ); + fromLegacy( aCfg, "OthersTextItalic", bp + "defaults.other_text_italic" ); + fromLegacy( aCfg, "OthersTextUpright", bp + "defaults.other_text_upright" ); + + fromLegacy( aCfg, "DimensionUnits", bp + "defaults.dimension_units" ); + fromLegacy( aCfg, "DimensionPrecision", bp + "defaults.dimension_precision" ); + + std::string sev = bp + "rule_severities"; + + fromLegacy( + aCfg, "RequireCourtyardDefinitions", sev + "legacy_no_courtyard_defined" ); + + fromLegacy( aCfg, "ProhibitOverlappingCourtyards", sev + "legacy_ourtyards_overlap" ); + + { + int idx = 1; + wxString keyBase = "TrackWidth"; + wxString key = keyBase; + double val; + + nlohmann::json widths = nlohmann::json::array(); + + key << idx; + + while( aCfg->Read( key, &val ) ) + { + widths.push_back( val ); + key = keyBase; + key << ++idx; + } + + ( *this )[PointerFromString( bp + "track_widths" )] = widths; + } + + { + int idx = 1; + wxString keyBase = "ViaDiameter"; + wxString key = keyBase; + double diameter; + double drill = 1.0; + + nlohmann::json vias = nlohmann::json::array(); + + key << idx; + + while( aCfg->Read( key, &diameter ) ) + { + key = "ViaDrill"; + aCfg->Read( key << idx, &drill ); + + nlohmann::json via = { { "diameter", diameter }, { "drill", drill } }; + vias.push_back( via ); + + key = keyBase; + key << ++idx; + } + + ( *this )[PointerFromString( bp + "via_dimensions" )] = vias; + } + + { + int idx = 1; + wxString keyBase = "dPairWidth"; + wxString key = keyBase; + double width; + double gap = 1.0; + double via_gap = 1.0; + + nlohmann::json pairs = nlohmann::json::array(); + + key << idx; + + while( aCfg->Read( key, &width ) ) + { + key = "dPairGap"; + aCfg->Read( key << idx, &gap ); + + key = "dPairViaGap"; + aCfg->Read( key << idx, &via_gap ); + + nlohmann::json pair = { { "width", width }, { "gap", gap }, { "via_gap", via_gap } }; + pairs.push_back( pair ); + + key = keyBase; + key << ++idx; + } + + ( *this )[PointerFromString( bp + "diff_pair_dimensions" )] = pairs; + } + + // NOTE: severities are just left alone to be migrated by BOARD_DESIGN_SETTINGS when it + // initializes, so that common doesn't need knowledge of the DRC error list (this is the + // downside of storing them as string keys... Do not blacklist the /pcbnew group so that + // this works! + + // General group is unused these days, we can throw it away + group_blacklist.insert( wxT( "/general" ) ); + + // Next load sheet names and put all other legacy data in the legacy dict + aCfg->SetPath( wxT( "/" ) ); + + auto loadSheetNames = + [&]() -> bool + { + int sheet = 1; + wxString entry; + nlohmann::json arr = nlohmann::json::array(); + + wxLogTrace( traceSettings, "Migrating sheet names" ); + + aCfg->SetPath( wxT( "/sheetnames" ) ); + + while( aCfg->Read( wxString::Format( "%d", sheet++ ), &entry ) ) + { + wxArrayString tokens = wxSplit( entry, ':' ); + + if( tokens.size() == 2 ) + { + wxLogTrace( traceSettings, "%d: %s = %s", sheet, tokens[0], tokens[1] ); + arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) ); + } + } + + ( *this )[PointerFromString( "sheets" )] = arr; + + aCfg->SetPath( "/" ); + + // TODO: any reason we want to fail on this? + return true; + }; + + std::vector groups; + + groups.emplace_back( "" ); + + auto loadLegacyPairs = + [&]( const std::string& aGroup ) -> bool + { + wxLogTrace( traceSettings, "Migrating group %s", aGroup ); + bool success = true; + wxString keyStr; + wxString val; + + index = 0; + + while( aCfg->GetNextEntry( keyStr, index ) ) + { + if( !aCfg->Read( keyStr, &val ) ) + continue; + + std::string key( keyStr.ToUTF8() ); + + wxLogTrace( traceSettings, " %s = %s", key, val ); + + try + { + nlohmann::json::json_pointer ptr( "/legacy" + aGroup + "/" + key ); + ( *this )[ptr] = val; + } + catch( ... ) + { + success = false; + } + } + + return success; + }; + + for( size_t i = 0; i < groups.size(); i++ ) + { + aCfg->SetPath( groups[i] ); + + if( groups[i] == wxT( "/sheetnames" ) ) + { + ret |= loadSheetNames(); + continue; + } + + aCfg->DeleteEntry( wxT( "last_client" ), true ); + aCfg->DeleteEntry( wxT( "update" ), true ); + aCfg->DeleteEntry( wxT( "version" ), true ); + + ret &= loadLegacyPairs( groups[i].ToStdString() ); + + index = 0; + + while( aCfg->GetNextGroup( str, index ) ) + { + wxString group = groups[i] + "/" + str; + + if( !group_blacklist.count( group ) ) + groups.emplace_back( group ); + } + + aCfg->SetPath( "/" ); + } + + return ret; +} + + +bool PROJECT_FILE::SaveToFile( const std::string& aDirectory, bool aForce ) +{ + wxASSERT( m_project ); + + ( *this )[PointerFromString( "meta.filename" )] = + m_project->GetProjectName() + "." + ProjectFileExtension; + + return JSON_SETTINGS::SaveToFile( aDirectory, aForce ); +} + + +wxString PROJECT_FILE::getFileExt() const +{ + return ProjectFileExtension; +} + + +wxString PROJECT_FILE::getLegacyFileExt() const +{ + return LegacyProjectFileExtension; +} + + +void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair ) +{ + aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } ); +} + + +void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair ) +{ + wxASSERT( aJson.is_array() && aJson.size() == 2 ); + aPair.first = KIID( wxString( aJson[0].get().c_str(), wxConvUTF8 ) ); + aPair.second = wxString( aJson[1].get().c_str(), wxConvUTF8 ); +} diff --git a/common/project/project_local_settings.cpp b/common/project/project_local_settings.cpp new file mode 100644 index 0000000000..0766f0004f --- /dev/null +++ b/common/project/project_local_settings.cpp @@ -0,0 +1,105 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * @author Jon Evans + * + * 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 . + */ + +#include +#include +#include + +const int projectLocalSettingsVersion = 1; + + +PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( const std::string& aFilename ) : + JSON_SETTINGS( aFilename, SETTINGS_LOC::PROJECT, projectLocalSettingsVersion, + /* aCreateIfMissing = */ true, /* aCreateIfDefault = */ false, + /* aWriteFile = */ true ), + m_project( nullptr ) +{ + m_params.emplace_back( new PARAM_LAMBDA( "board.visible_layers", + [&]() -> std::string + { + return m_VisibleLayers.FmtHex(); + }, + [&]( const std::string& aString ) + { + m_VisibleLayers.ParseHex( aString.c_str(), aString.size() ); + }, + LSET::AllLayersMask().FmtHex() ) ); + + static GAL_SET defaultVisible; + defaultVisible.set().reset( GAL_LAYER_INDEX( LAYER_MOD_TEXT_INVISIBLE ) ); + + m_params.emplace_back( new PARAM_LAMBDA( "board.visible_items", + [&]() -> nlohmann::json + { + nlohmann::json ret = nlohmann::json::array(); + + for( size_t i = 0; i < m_VisibleItems.size(); i++ ) + if( m_VisibleItems.test( i ) ) + ret.push_back( i ); + + return ret; + }, + [&]( const nlohmann::json& aVal ) + { + if( !aVal.is_array() || aVal.empty() ) + { + m_VisibleItems = defaultVisible; + return; + } + + m_VisibleItems.reset(); + + for( const nlohmann::json& entry : aVal ) + { + try + { + int i = entry.get(); + m_VisibleItems.set( i ); + } + catch( ... ) + { + // Non-integer or out of range entry in the array; ignore + } + } + }, + {} ) ); +} + + +bool PROJECT_LOCAL_SETTINGS::MigrateFromLegacy( wxConfigBase* aLegacyConfig ) +{ + /** + * The normal legacy migration code won't be used for this because the only legacy + * information stored here was stored in board files, so we do that migration when loading + * the board. + */ + return true; +} + + +bool PROJECT_LOCAL_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) +{ + wxASSERT( m_project ); + + ( *this )[PointerFromString( "meta.filename" )] = + m_project->GetProjectName() + "." + ProjectLocalSettingsFileExtension; + + return JSON_SETTINGS::SaveToFile( aDirectory, aForce ); +} diff --git a/common/settings/json_settings.cpp b/common/settings/json_settings.cpp index 5b088e6c9a..d519fadb55 100644 --- a/common/settings/json_settings.cpp +++ b/common/settings/json_settings.cpp @@ -46,11 +46,11 @@ JSON_SETTINGS::JSON_SETTINGS( const std::string& aFilename, SETTINGS_LOC aLocati m_createIfDefault( aCreateIfDefault ), m_writeFile( aWriteFile ), m_deleteLegacyAfterMigration( true ), + m_resetParamsIfMissing( true ), m_schemaVersion( aSchemaVersion ), m_manager( nullptr ) { - m_params.emplace_back( - new PARAM( "meta.filename", &m_filename, m_filename, true ) ); + ( *this )[PointerFromString( "meta.filename" )] = GetFullFilename(); m_params.emplace_back( new PARAM( "meta.version", &m_schemaVersion, m_schemaVersion, true ) ); @@ -66,13 +66,19 @@ JSON_SETTINGS::~JSON_SETTINGS() } +wxString JSON_SETTINGS::GetFullFilename() const +{ + return wxString( m_filename.c_str(), wxConvUTF8 ) + "." + getFileExt(); +} + + void JSON_SETTINGS::Load() { for( auto param : m_params ) { try { - param->Load( this ); + param->Load( this, m_resetParamsIfMissing ); } catch( ... ) { @@ -106,7 +112,8 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory ) if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) ) { - wxLogTrace( traceSettings, "%s: could not create temp file for migration", m_filename ); + wxLogTrace( traceSettings, "%s: could not create temp file for migration", + GetFullFilename() ); backed_up = false; } @@ -118,11 +125,12 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory ) if( !MigrateFromLegacy( cfg.get() ) ) { wxLogTrace( traceSettings, - "%s: migrated; not all settings were found in legacy file", m_filename ); + "%s: migrated; not all settings were found in legacy file", + GetFullFilename() ); } else { - wxLogTrace( traceSettings, "%s: migrated from legacy format", m_filename ); + wxLogTrace( traceSettings, "%s: migrated from legacy format", GetFullFilename() ); } if( backed_up ) @@ -186,14 +194,15 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory ) } catch( ... ) { - wxLogTrace( traceSettings, "%s: file version could not be read!", m_filename ); + wxLogTrace( + traceSettings, "%s: file version could not be read!", GetFullFilename() ); success = false; } if( filever >= 0 && filever < m_schemaVersion ) { wxLogTrace( traceSettings, "%s: attempting migration from version %d to %d", - m_filename, filever, m_schemaVersion ); + GetFullFilename(), filever, m_schemaVersion ); if( Migrate() ) { @@ -201,13 +210,13 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory ) } else { - wxLogTrace( traceSettings, "%s: migration failed!", m_filename ); + wxLogTrace( traceSettings, "%s: migration failed!", GetFullFilename() ); } } else if( filever > m_schemaVersion ) { wxLogTrace( traceSettings, - "%s: warning: file version %d is newer than latest (%d)", m_filename, + "%s: warning: file version %d is newer than latest (%d)", GetFullFilename(), filever, m_schemaVersion ); } } @@ -227,7 +236,7 @@ bool JSON_SETTINGS::LoadFromFile( const std::string& aDirectory ) for( auto settings : m_nested_settings ) settings->LoadFromFile(); - wxLogTrace( traceSettings, "Loaded %s with schema %d", GetFilename(), m_schemaVersion ); + wxLogTrace( traceSettings, "Loaded %s with schema %d", GetFullFilename(), m_schemaVersion ); // If we migrated, clean up the legacy file (with no extension) if( legacy_migrated || migrated ) @@ -292,7 +301,7 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) { wxLogTrace( traceSettings, "File for %s doesn't exist and m_createIfMissing == false; not saving", - m_filename ); + GetFullFilename() ); return false; } @@ -305,27 +314,25 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) if( !modified && !aForce && path.FileExists() ) { - wxLogTrace( traceSettings, "%s contents not modified, skipping save", m_filename ); + wxLogTrace( traceSettings, "%s contents not modified, skipping save", GetFullFilename() ); return false; } else if( !modified && !aForce && !m_createIfDefault ) { wxLogTrace( traceSettings, "%s contents still default and m_createIfDefault == false; not saving", - m_filename ); + GetFullFilename() ); return false; } - wxLogTrace( traceSettings, "Saving %s", m_filename ); - if( !path.DirExists() && !path.Mkdir() ) { wxLogTrace( traceSettings, "Warning: could not create path %s, can't save %s", - path.GetPath(), m_filename ); + path.GetPath(), GetFullFilename() ); return false; } - wxLogTrace( traceSettings, "Saving %s", m_filename ); + wxLogTrace( traceSettings, "Saving %s", GetFullFilename() ); LOCALE_IO dummy; @@ -336,7 +343,7 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) } catch( const std::exception& e ) { - wxLogTrace( traceSettings, "Warning: could not save %s: %s", m_filename, e.what() ); + wxLogTrace( traceSettings, "Warning: could not save %s: %s", GetFullFilename(), e.what() ); } catch( ... ) { @@ -346,9 +353,9 @@ bool JSON_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) } -OPT JSON_SETTINGS::GetJson( std::string aPath ) const +OPT JSON_SETTINGS::GetJson( const std::string& aPath ) const { - nlohmann::json::json_pointer ptr = PointerFromString( std::move( aPath ) ); + nlohmann::json::json_pointer ptr = PointerFromString( aPath ); if( this->contains( ptr ) ) { @@ -498,6 +505,9 @@ void JSON_SETTINGS::AddNestedSettings( NESTED_SETTINGS* aSettings ) void JSON_SETTINGS::ReleaseNestedSettings( NESTED_SETTINGS* aSettings ) { + if( !aSettings ) + return; + auto it = std::find_if( m_nested_settings.begin(), m_nested_settings.end(), [&aSettings]( const JSON_SETTINGS* aPtr ) { return aPtr == aSettings; @@ -509,21 +519,23 @@ void JSON_SETTINGS::ReleaseNestedSettings( NESTED_SETTINGS* aSettings ) ( *it )->SaveToFile(); m_nested_settings.erase( it ); } + + aSettings->SetParent( nullptr ); } // Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API -template<> OPT JSON_SETTINGS::Get( std::string aPath ) const +template<> OPT JSON_SETTINGS::Get( const std::string& aPath ) const { - if( OPT opt_json = GetJson( std::move( aPath ) ) ) + if( OPT opt_json = GetJson( aPath ) ) return wxString( opt_json->get().c_str(), wxConvUTF8 ); return NULLOPT; } -template<> void JSON_SETTINGS::Set( std::string aPath, wxString aVal ) +template<> void JSON_SETTINGS::Set( const std::string& aPath, wxString aVal ) { ( *this )[PointerFromString( std::move( aPath ) ) ] = aVal.ToUTF8(); } diff --git a/common/settings/nested_settings.cpp b/common/settings/nested_settings.cpp index c685a7e895..7ece005482 100644 --- a/common/settings/nested_settings.cpp +++ b/common/settings/nested_settings.cpp @@ -30,19 +30,14 @@ NESTED_SETTINGS::NESTED_SETTINGS( const std::string& aName, int aVersion, JSON_S JSON_SETTINGS( aName, SETTINGS_LOC::NESTED, aVersion ), m_parent( aParent ), m_path( aPath ) { - if( m_parent ) - { - m_parent->AddNestedSettings( this ); - } - - // In case we were created after the parent's ctor - LoadFromFile(); + SetParent( aParent ); } NESTED_SETTINGS::~NESTED_SETTINGS() { - m_parent->ReleaseNestedSettings( this ); + if( m_parent ) + m_parent->ReleaseNestedSettings( this ); } @@ -77,6 +72,9 @@ bool NESTED_SETTINGS::LoadFromFile( const std::string& aDirectory ) bool NESTED_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) { + if( !m_parent ) + return false; + bool modified = Store(); try @@ -108,3 +106,15 @@ bool NESTED_SETTINGS::SaveToFile( const std::string& aDirectory, bool aForce ) return modified; } + + +void NESTED_SETTINGS::SetParent( JSON_SETTINGS* aParent ) +{ + m_parent = aParent; + + if( m_parent ) + m_parent->AddNestedSettings( this ); + + // In case we were created after the parent's ctor + LoadFromFile(); +} diff --git a/common/settings/project_file.cpp b/common/settings/project_file.cpp deleted file mode 100644 index d30b9161b1..0000000000 --- a/common/settings/project_file.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2020 CERN - * @author Jon Evans - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -extern const char* traceSettings; - -///! Update the schema version whenever a migration is required -const int projectFileSchemaVersion = 1; - - -PROJECT_FILE::PROJECT_FILE( const std::string& aFullPath ) : - JSON_SETTINGS( aFullPath, SETTINGS_LOC::NONE, projectFileSchemaVersion ), - m_sheets(), m_boards() -{ - // Keep old files around - m_deleteLegacyAfterMigration = false; - - m_params.emplace_back( new PARAM_LIST( "sheets", &m_sheets, {} ) ); - - m_params.emplace_back( new PARAM_LIST( "boards", &m_boards, {} ) ); - - m_params.emplace_back( - new PARAM_LIST( "libraries.pinned_symbol_libs", &m_PinnedSymbolLibs, {} ) ); - - m_params.emplace_back( new PARAM_LIST( - "libraries.pinned_footprint_libs", &m_PinnedFootprintLibs, {} ) ); - - m_params.emplace_back( - new PARAM_PATH_LIST( "cvpcb.equivalence_files", &m_EquivalenceFiles, {} ) ); -} - - -bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aLegacyFile ) -{ - bool ret = true; - wxString str; - long index = 0; - - std::set group_blacklist; - - // Legacy files don't store board info; they assume board matches project name - // We will leave m_boards empty here so it can be populated with other code - - // First handle migration of data that will be stored locally in this object - - auto loadPinnedLibs = - [&]( const std::string& aDest ) - { - int libIndex = 1; - wxString libKey = wxT( "PinnedItems" ); - libKey << libIndex; - - nlohmann::json libs = nlohmann::json::array(); - - while( aLegacyFile->Read( libKey, &str ) ) - { - libs.push_back( str ); - - aLegacyFile->DeleteEntry( libKey, true ); - - libKey = wxT( "PinnedItems" ); - libKey << ++libIndex; - } - - ( *this )[PointerFromString( aDest )] = libs; - }; - - aLegacyFile->SetPath( wxT( "/LibeditFrame" ) ); - loadPinnedLibs( "libraries.pinned_symbol_libs" ); - - aLegacyFile->SetPath( wxT( "/ModEditFrame" ) ); - loadPinnedLibs( "libraries.pinned_footprint_libs" ); - - aLegacyFile->SetPath( wxT( "/cvpcb/equfiles" ) ); - - { - int eqIdx = 1; - wxString eqKey = wxT( "EquName" ); - eqKey << eqIdx; - - nlohmann::json eqs = nlohmann::json::array(); - - while( aLegacyFile->Read( eqKey, &str ) ) - { - eqs.push_back( str ); - - eqKey = wxT( "EquName" ); - eqKey << ++eqIdx; - } - - ( *this )[PointerFromString( "cvpcb.equivalence_files" )] = eqs; - } - - // No other cvpcb params are currently used - group_blacklist.insert( "/cvpcb" ); - - // Next load sheet names and put all other legacy data in the legacy dict - aLegacyFile->SetPath( "/" ); - - auto loadSheetNames = - [&]() -> bool - { - int sheet = 1; - wxString entry; - nlohmann::json arr = nlohmann::json::array(); - - wxLogTrace( traceSettings, "Migrating sheet names" ); - - aLegacyFile->SetPath( wxT( "/sheetnames" ) ); - - while( aLegacyFile->Read( wxString::Format( "%d", sheet++ ), &entry ) ) - { - wxArrayString tokens = wxSplit( entry, ':' ); - - if( tokens.size() == 2 ) - { - wxLogTrace( traceSettings, "%d: %s = %s", sheet, tokens[0], tokens[1] ); - arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) ); - } - } - - ( *this )[PointerFromString( "sheets" )] = arr; - - aLegacyFile->SetPath( "/" ); - - // TODO: any reason we want to fail on this? - return true; - }; - - std::vector groups; - - groups.emplace_back( "" ); - - auto loadLegacyPairs = - [&]( const std::string& aGroup ) -> bool - { - wxLogTrace( traceSettings, "Migrating group %s", aGroup ); - bool success = true; - wxString keyStr; - wxString val; - - index = 0; - - while( aLegacyFile->GetNextEntry( keyStr, index ) ) - { - if( !aLegacyFile->Read( keyStr, &val ) ) - continue; - - std::string key( keyStr.ToUTF8() ); - - wxLogTrace( traceSettings, " %s = %s", key, val ); - - try - { - nlohmann::json::json_pointer ptr( "/legacy" + aGroup + "/" + key ); - ( *this )[ptr] = val; - } - catch( ... ) - { - success = false; - } - } - - return success; - }; - - for( size_t i = 0; i < groups.size(); i++ ) - { - aLegacyFile->SetPath( groups[i] ); - - if( groups[i] == wxT( "/sheetnames" ) ) - { - ret |= loadSheetNames(); - continue; - } - - aLegacyFile->DeleteEntry( wxT( "last_client" ), true ); - aLegacyFile->DeleteEntry( wxT( "update" ), true ); - aLegacyFile->DeleteEntry( wxT( "version" ), true ); - - ret &= loadLegacyPairs( groups[i].ToStdString() ); - - index = 0; - - while( aLegacyFile->GetNextGroup( str, index ) ) - { - wxString group = groups[i] + "/" + str; - - if( !group_blacklist.count( group ) ) - groups.emplace_back( group ); - } - - aLegacyFile->SetPath( "/" ); - } - - return ret; -} - - -wxString PROJECT_FILE::getFileExt() const -{ - return ProjectFileExtension; -} - - -wxString PROJECT_FILE::getLegacyFileExt() const -{ - return LegacyProjectFileExtension; -} - - -void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair ) -{ - aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } ); -} - - -void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair ) -{ - wxASSERT( aJson.is_array() && aJson.size() == 2 ); - aPair.first = KIID( wxString( aJson[0].get().c_str(), wxConvUTF8 ) ); - aPair.second = wxString( aJson[1].get().c_str(), wxConvUTF8 ); -} \ No newline at end of file diff --git a/common/settings/settings_manager.cpp b/common/settings/settings_manager.cpp index e130e73dca..559614b81a 100644 --- a/common/settings/settings_manager.cpp +++ b/common/settings/settings_manager.cpp @@ -30,10 +30,11 @@ #include #include #include +#include +#include #include #include #include -#include #include #include @@ -46,7 +47,7 @@ const char* traceSettings = "SETTINGS"; /// Project settings path will be + this -#define PROJECT_SETTINGS_DIR_SUFFIX wxT( "-settings" ) +#define PROJECT_BACKUPS_DIR_SUFFIX wxT( "-backups" ) SETTINGS_MANAGER::SETTINGS_MANAGER() : @@ -83,7 +84,7 @@ JSON_SETTINGS* SETTINGS_MANAGER::RegisterSettings( JSON_SETTINGS* aSettings, boo ptr->SetManager( this ); - wxLogTrace( traceSettings, "Registered new settings object %s", ptr->GetFilename() ); + wxLogTrace( traceSettings, "Registered new settings object %s", ptr->GetFullFilename() ); if( aLoadNow ) ptr->LoadFromFile( GetPathForSettingsFile( ptr.get() ) ); @@ -139,13 +140,13 @@ void SETTINGS_MANAGER::Save( JSON_SETTINGS* aSettings ) if( it != m_settings.end() ) { - wxLogTrace( traceSettings, "Saving %s", ( *it )->GetFilename() ); + wxLogTrace( traceSettings, "Saving %s", ( *it )->GetFullFilename() ); ( *it )->SaveToFile( GetPathForSettingsFile( it->get() ) ); } } -void SETTINGS_MANAGER::FlushAndRelease( JSON_SETTINGS* aSettings ) +void SETTINGS_MANAGER::FlushAndRelease( JSON_SETTINGS* aSettings, bool aSave ) { auto it = std::find_if( m_settings.begin(), m_settings.end(), [&aSettings]( const std::unique_ptr& aPtr ) @@ -155,8 +156,11 @@ void SETTINGS_MANAGER::FlushAndRelease( JSON_SETTINGS* aSettings ) if( it != m_settings.end() ) { - wxLogTrace( traceSettings, "Flush and release %s", ( *it )->GetFilename() ); - ( *it )->SaveToFile( GetPathForSettingsFile( it->get() ) ); + wxLogTrace( traceSettings, "Flush and release %s", ( *it )->GetFullFilename() ); + + if( aSave ) + ( *it )->SaveToFile( GetPathForSettingsFile( it->get() ) ); + m_settings.erase( it ); } } @@ -332,8 +336,7 @@ std::string SETTINGS_MANAGER::GetPathForSettingsFile( JSON_SETTINGS* aSettings ) return GetUserSettingsPath(); case SETTINGS_LOC::PROJECT: - // TODO(JE) - return ""; + return std::string( Prj().GetProjectPath().ToUTF8() ); case SETTINGS_LOC::COLORS: return GetColorSettingsPath(); @@ -667,7 +670,7 @@ bool SETTINGS_MANAGER::extractVersion( const std::string& aVersionString, int* a } -bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath ) +bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive ) { // Normalize path to new format even if migrating from a legacy file wxFileName path( aFullPath ); @@ -678,33 +681,45 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath ) wxString fullPath = path.GetFullPath(); // Unload if already loaded - if( m_projects.count( fullPath ) && !UnloadProject( *m_projects.at( fullPath ).get() ) ) + if( m_projects.count( fullPath ) && !UnloadProject( m_projects.at( fullPath ).get() ) ) return false; // No MDI yet - if( !m_projects.empty() && !UnloadProject( *m_projects.begin()->second ) ) + if( aSetActive && !m_projects.empty() && !UnloadProject( m_projects.begin()->second.get() ) ) return false; wxLogTrace( traceSettings, "Load project %s", fullPath ); - m_projects[fullPath] = std::make_unique(); - m_projects[fullPath]->setProjectFullName( fullPath ); + std::unique_ptr project = std::make_unique(); + project->setProjectFullName( fullPath ); - return loadProjectFile( *m_projects[fullPath] ); + bool success = loadProjectFile( *project ); + + m_projects[fullPath].reset( project.release() ); + + std::string fn( path.GetName() ); + + PROJECT_LOCAL_SETTINGS* settings = static_cast( + RegisterSettings( new PROJECT_LOCAL_SETTINGS( fn ) ) ); + + m_projects[fullPath]->setLocalSettings( settings ); + settings->SetProject( m_projects[fullPath].get() ); + + return success; } -bool SETTINGS_MANAGER::UnloadProject( PROJECT& aProject ) +bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave ) { - if( !m_projects.count( aProject.GetProjectFullName() ) ) + if( !aProject || !m_projects.count( aProject->GetProjectFullName() ) ) return false; - if( !unloadProjectFile( aProject ) ) + if( !unloadProjectFile( aProject, aSave ) ) return false; - wxLogTrace( traceSettings, "Unload project %s", aProject.GetProjectFullName() ); + wxLogTrace( traceSettings, "Unload project %s", aProject->GetProjectFullName() ); - m_projects.erase( aProject.GetProjectFullName() ); + m_projects.erase( aProject->GetProjectFullName() ); return true; } @@ -712,34 +727,50 @@ bool SETTINGS_MANAGER::UnloadProject( PROJECT& aProject ) PROJECT& SETTINGS_MANAGER::Prj() const { - // No MDI yet - wxASSERT( m_projects.size() == 1 ); + // No MDI yet: First project in the list is the active project return *m_projects.begin()->second; } -bool SETTINGS_MANAGER::SaveProject() +PROJECT* SETTINGS_MANAGER::GetProject( const wxString& aFullPath ) const { - wxString name = Prj().GetProjectFullName(); + if( m_projects.count( aFullPath ) ) + return m_projects.at( aFullPath ).get(); - if( !m_project_files.count( name ) ) + return nullptr; +} + + +bool SETTINGS_MANAGER::SaveProject( const wxString& aFullPath ) +{ + wxString path = aFullPath; + + if( path.empty() ) + path = Prj().GetProjectFullName(); + + if( !m_project_files.count( path ) ) return false; - m_project_files[name]->SaveToFile(); + PROJECT_FILE* project = m_project_files.at( path ); + std::string projectPath = GetPathForSettingsFile( project ); + + project->SaveToFile( projectPath ); + Prj().GetLocalSettings().SaveToFile( projectPath ); return true; } -wxString SETTINGS_MANAGER::GetProjectSettingsPath() const +wxString SETTINGS_MANAGER::GetProjectBackupsPath() const { - return Prj().GetProjectPath() + Prj().GetProjectName() + PROJECT_SETTINGS_DIR_SUFFIX; + return Prj().GetProjectPath() + Prj().GetProjectName() + PROJECT_BACKUPS_DIR_SUFFIX; } bool SETTINGS_MANAGER::loadProjectFile( PROJECT& aProject ) { - std::string fn( aProject.GetProjectFullName().ToUTF8() ); + wxFileName fullFn( aProject.GetProjectFullName() ); + std::string fn( fullFn.GetName().ToUTF8() ); PROJECT_FILE* file = static_cast( RegisterSettings( new PROJECT_FILE( fn ), false ) ); @@ -747,14 +778,18 @@ bool SETTINGS_MANAGER::loadProjectFile( PROJECT& aProject ) m_project_files[aProject.GetProjectFullName()] = file; aProject.setProjectFile( file ); + file->SetProject( &aProject ); - return file->LoadFromFile(); + return file->LoadFromFile( std::string( fullFn.GetPath().ToUTF8() ) ); } -bool SETTINGS_MANAGER::unloadProjectFile( PROJECT& aProject ) +bool SETTINGS_MANAGER::unloadProjectFile( PROJECT* aProject, bool aSave ) { - wxString name = aProject.GetProjectFullName(); + if( !aProject ) + return false; + + wxString name = aProject->GetProjectFullName(); if( !m_project_files.count( name ) ) return false; @@ -769,7 +804,13 @@ bool SETTINGS_MANAGER::unloadProjectFile( PROJECT& aProject ) if( it != m_settings.end() ) { - ( *it )->SaveToFile(); + std::string projectPath = GetPathForSettingsFile( it->get() ); + + FlushAndRelease( &aProject->GetLocalSettings(), aSave ); + + if( aSave ) + ( *it )->SaveToFile( projectPath ); + m_settings.erase( it ); } diff --git a/pcbnew/swig/netclass.i b/common/swig/netclass.i similarity index 100% rename from pcbnew/swig/netclass.i rename to common/swig/netclass.i diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 3731dcab68..7d91bd47c9 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -122,6 +122,7 @@ const std::string VrmlFileExtension( "wrl" ); const std::string ProjectFileExtension( "kicad_pro" ); const std::string LegacyProjectFileExtension( "pro" ); +const std::string ProjectLocalSettingsFileExtension( "kicad_prl" ); const std::string LegacySchematicFileExtension( "sch" ); const std::string KiCadSchematicFileExtension( "kicad_sch" ); const std::string NetlistFileExtension( "net" ); diff --git a/cvpcb/auto_associate.cpp b/cvpcb/auto_associate.cpp index 7f6bf9fba4..8872a37c76 100644 --- a/cvpcb/auto_associate.cpp +++ b/cvpcb/auto_associate.cpp @@ -39,7 +39,7 @@ #include #include #include -#include +#include #define QUOTE '\'' diff --git a/cvpcb/dialogs/dialog_config_equfiles.cpp b/cvpcb/dialogs/dialog_config_equfiles.cpp index a8d42a0733..052314effe 100644 --- a/cvpcb/dialogs/dialog_config_equfiles.cpp +++ b/cvpcb/dialogs/dialog_config_equfiles.cpp @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 0ce6ecea22..55a0e1bf5b 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -54,7 +54,7 @@ #include #include #include -#include +#include #include #include #include @@ -262,7 +262,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in return false; wxFileName pro = fullFileName; - pro.SetExt( LegacyProjectFileExtension ); + pro.SetExt( ProjectFileExtension ); bool is_new = !wxFileName::IsFileReadable( fullFileName ); @@ -332,6 +332,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in Prj().SetElem( PROJECT::ELEM_SYMBOL_LIB_TABLE, NULL ); Prj().SchSymbolLibTable(); + Schematic().SetProject( Prj() ); + SetShutdownBlockReason( _( "Schematic file changes are unsaved" ) ); if( is_new ) diff --git a/eeschema/symbol_tree_model_adapter.cpp b/eeschema/symbol_tree_model_adapter.cpp index c288c50480..3ec560e3b6 100644 --- a/eeschema/symbol_tree_model_adapter.cpp +++ b/eeschema/symbol_tree_model_adapter.cpp @@ -43,7 +43,7 @@ SYMBOL_TREE_MODEL_ADAPTER::PTR SYMBOL_TREE_MODEL_ADAPTER::Create( EDA_BASE_FRAME SYMBOL_TREE_MODEL_ADAPTER::SYMBOL_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs ) : - LIB_TREE_MODEL_ADAPTER( aParent ), + LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs" ), m_libs( (SYMBOL_LIB_TABLE*) aLibs ) {} diff --git a/eeschema/symbol_tree_synchronizing_adapter.cpp b/eeschema/symbol_tree_synchronizing_adapter.cpp index 10fa479746..589353cb09 100644 --- a/eeschema/symbol_tree_synchronizing_adapter.cpp +++ b/eeschema/symbol_tree_synchronizing_adapter.cpp @@ -39,7 +39,7 @@ LIB_TREE_MODEL_ADAPTER::PTR SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Create( LIB_EDIT_ SYMBOL_TREE_SYNCHRONIZING_ADAPTER::SYMBOL_TREE_SYNCHRONIZING_ADAPTER( LIB_EDIT_FRAME* aParent, LIB_MANAGER* aLibMgr ) : - LIB_TREE_MODEL_ADAPTER( aParent ), + LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs" ), m_frame( aParent ), m_libMgr( aLibMgr ), m_lastSyncHash( -1 ) diff --git a/include/board_design_settings.h b/include/board_design_settings.h index 0ad8fa330f..16edf9021e 100644 --- a/include/board_design_settings.h +++ b/include/board_design_settings.h @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #define DEFAULT_SILK_LINE_WIDTH 0.12 @@ -208,7 +211,7 @@ enum class VIATYPE : int; * BOARD_DESIGN_SETTINGS * contains design settings for a BOARD object. */ -class BOARD_DESIGN_SETTINGS +class BOARD_DESIGN_SETTINGS : public NESTED_SETTINGS { public: // Note: the first value in each dimensions list is the current netclass value @@ -217,7 +220,7 @@ public: std::vector m_DiffPairDimensionsList; // List of netclasses. There is always the default netclass. - NETCLASSES m_NetClasses; + //NETCLASSES m_NetClasses; std::vector m_DRCRuleSelectors; std::vector m_DRCRules; @@ -242,6 +245,9 @@ public: std::map< int, int > m_DRCSeverities; // Map from DRCErrorCode to SEVERITY + /// Excluded DRC items + std::set m_DrcExclusions; + /** Option to handle filled polygons in zones: * the "legacy" option is using thick outlines around filled polygons: give the best shape * the "new" option is using only filled polygons (no outline: give the faster redraw time @@ -310,9 +316,7 @@ private: int m_copperLayerCount; ///< Number of copper layers for this design LSET m_enabledLayers; ///< Bit-mask for layer enabling - LSET m_visibleLayers; ///< Bit-mask for layer visibility - int m_visibleElements; ///< Bit-mask for element category visibility int m_boardThickness; ///< Board thickness for 3D viewer /// Current net class name used to display netclass info. @@ -325,8 +329,25 @@ private: */ BOARD_STACKUP m_stackup; + /// Net classes that are loaded from the board file before these were stored in the project + NETCLASSES m_internalNetClasses; + + /// This will point to m_internalNetClasses until it is repointed to the project after load + NETCLASSES* m_netClasses; + + /// The defualt settings that will be used for new zones + ZONE_SETTINGS m_defaultZoneSettings; + + SEVERITY severityFromString( const wxString& aSeverity ); + + wxString severityToString( const SEVERITY& aSeverity ); + public: - BOARD_DESIGN_SETTINGS(); + BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ); + + virtual ~BOARD_DESIGN_SETTINGS(); + + bool LoadFromFile( const std::string& aDirectory = "" ) override; BOARD_STACKUP& GetStackupDescriptor() { return m_stackup; } @@ -337,13 +358,36 @@ public: */ bool Ignore( int aDRCErrorCode ); + NETCLASSES& GetNetClasses() const + { + return *m_netClasses; + } + + void SetNetClasses( NETCLASSES* aNetClasses ) + { + if( aNetClasses ) + m_netClasses = aNetClasses; + else + m_netClasses = &m_internalNetClasses; + } + + ZONE_SETTINGS& GetDefaultZoneSettings() + { + return m_defaultZoneSettings; + } + + void SetDefaultZoneSettings( const ZONE_SETTINGS& aSettings ) + { + m_defaultZoneSettings = aSettings; + } + /** * Function GetDefault * @return the default netclass. */ inline NETCLASS* GetDefault() const { - return m_NetClasses.GetDefaultPtr(); + return GetNetClasses().GetDefaultPtr(); } /** @@ -712,95 +756,6 @@ public: */ void SetCopperEdgeClearance( int aDistance ); - /** - * Function GetVisibleLayers - * returns a bit-mask of all the layers that are visible - * @return int - the visible layers in bit-mapped form. - */ - inline LSET GetVisibleLayers() const - { - return m_visibleLayers; - } - - /** - * Function SetVisibleAlls - * Set the bit-mask of all visible elements categories, - * including enabled layers - */ - void SetVisibleAlls(); - - /** - * Function SetVisibleLayers - * changes the bit-mask of visible layers - * @param aMask = The new bit-mask of visible layers - */ - inline void SetVisibleLayers( LSET aMask ) - { - m_visibleLayers = aMask & m_enabledLayers; - } - - /** - * Function IsLayerVisible - * tests whether a given layer is visible - * @param aLayerId = The layer to be tested - * @return bool - true if the layer is visible. - */ - inline bool IsLayerVisible( PCB_LAYER_ID aLayerId ) const - { - // If a layer is disabled, it is automatically invisible - return (m_visibleLayers & m_enabledLayers)[aLayerId]; - } - - /** - * Function SetLayerVisibility - * changes the visibility of a given layer - * @param aLayerId = The layer to be changed - * @param aNewState = The new visibility state of the layer - */ - void SetLayerVisibility( PCB_LAYER_ID aLayerId, bool aNewState ); - - /** - * Function GetVisibleElements - * returns a bit-mask of all the element categories that are visible - * @return int - the visible element categories in bit-mapped form. - */ - inline int GetVisibleElements() const - { - return m_visibleElements; - } - - /** - * Function SetVisibleElements - * changes the bit-mask of visible element categories - * @param aMask = The new bit-mask of visible element categories - */ - inline void SetVisibleElements( int aMask ) - { - m_visibleElements = aMask; - } - - /** - * Function IsElementVisible - * tests whether a given element category is visible. Keep this as an - * inline function. - * @param aElementCategory is from the enum by the same name - * @return bool - true if the element is visible. - * @see enum GAL_LAYER_ID - */ - inline bool IsElementVisible( GAL_LAYER_ID aElementCategory ) const - { - return ( m_visibleElements & ( 1 << GAL_LAYER_INDEX( aElementCategory ) ) ); - } - - /** - * Function SetElementVisibility - * changes the visibility of an element category - * @param aElementCategory is from the enum by the same name - * @param aNewState = The new visibility state of the element category - * @see enum GAL_LAYER_ID - */ - void SetElementVisibility( GAL_LAYER_ID aElementCategory, bool aNewState ); - /** * Function GetEnabledLayers * returns a bit-mask of all the layers that are enabled @@ -845,14 +800,6 @@ public: */ void SetCopperLayerCount( int aNewLayerCount ); - /** - * Function AppendConfigs - * appends to @a aResult the configuration setting accessors which will later - * allow reading or writing of configuration file information directly into - * this object. - */ - void AppendConfigs( BOARD* aBoard, std::vector* aResult ); - inline int GetBoardThickness() const { return m_boardThickness; } inline void SetBoardThickness( int aThickness ) { m_boardThickness = aThickness; } diff --git a/include/footprint_editor_settings.h b/include/footprint_editor_settings.h index 57648bfb6d..740f6065a6 100644 --- a/include/footprint_editor_settings.h +++ b/include/footprint_editor_settings.h @@ -44,6 +44,8 @@ public: bool Migrate() override; + /// Only some of these settings are actually used for footprint editing + // TODO: factor out the relevant stuff so the whole BDS doesn't have to be here BOARD_DESIGN_SETTINGS m_DesignSettings; // Only the magneticPads element is used diff --git a/include/layers_id_colors_and_visibility.h b/include/layers_id_colors_and_visibility.h index 39847161bc..fbc7422064 100644 --- a/include/layers_id_colors_and_visibility.h +++ b/include/layers_id_colors_and_visibility.h @@ -218,6 +218,8 @@ enum GAL_LAYER_ID: int /// Use this macro to convert a GAL layer to a 0-indexed offset from LAYER_VIAS #define GAL_LAYER_INDEX( x ) ( x - GAL_LAYER_ID_START ) +constexpr int GAL_LAYER_ID_COUNT = GAL_LAYER_ID_END - GAL_LAYER_ID_START; + inline GAL_LAYER_ID operator++( GAL_LAYER_ID& a ) { a = GAL_LAYER_ID( int( a ) + 1 ); @@ -402,6 +404,7 @@ public: } }; +typedef std::bitset GAL_SET; typedef std::bitset BASE_SET; diff --git a/pcbnew/netclass.h b/include/netclass.h similarity index 95% rename from pcbnew/netclass.h rename to include/netclass.h index 6c4885fccf..8874e34ddf 100644 --- a/pcbnew/netclass.h +++ b/include/netclass.h @@ -201,16 +201,6 @@ public: */ void SetParams( const NETCLASS& aDefaults ); - /** - * Function Format - * outputs the net class to \a aFormatter in s-expression form. - * - * @param aFormatter The #OUTPUTFORMATTER object to write to. - * @param aNestLevel The indentation next level. - * @param aControlBits The control bit definition for object specific formatting. - * @throw IO_ERROR on write error. - */ - void Format( OUTPUTFORMATTER* aFormatter, int aNestLevel, int aControlBits ) const; #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const; diff --git a/include/pcb_base_frame.h b/include/pcb_base_frame.h index 78f8b04285..63e9a72898 100644 --- a/include/pcb_base_frame.h +++ b/include/pcb_base_frame.h @@ -148,12 +148,10 @@ public: void SetTitleBlock( const TITLE_BLOCK& aTitleBlock ) override; /** - * Function GetDesignSettings - * returns the BOARD_DESIGN_SETTINGS for the BOARD owned by this frame. + * Returns the BOARD_DESIGN_SETTINGS for the open project * Overloaded in FOOTPRINT_EDIT_FRAME. */ virtual BOARD_DESIGN_SETTINGS& GetDesignSettings() const; - virtual void SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ); /** * Helper to retrieve the current color settings diff --git a/include/project.h b/include/project.h index 96750d3171..ef2e69a901 100644 --- a/include/project.h +++ b/include/project.h @@ -48,6 +48,7 @@ class KIWAY; class SYMBOL_LIB_TABLE; class FILENAME_RESOLVER; class PROJECT_FILE; +class PROJECT_LOCAL_SETTINGS; #define VTBL_ENTRY virtual @@ -132,6 +133,12 @@ public: return *m_projectFile; } + VTBL_ENTRY PROJECT_LOCAL_SETTINGS& GetLocalSettings() const + { + wxASSERT( m_localSettings ); + return *m_localSettings; + } + /** * Function ConfigSave * saves the current "project" parameters into the wxConfigBase* derivative. @@ -340,6 +347,15 @@ private: m_projectFile = aFile; } + /** + * Sets the local settings backing store. Should only be called by SETTINGS_MANAGER on load. + * @param aSettings is the local settings object (may or may not exist on disk at this point) + */ + VTBL_ENTRY void setLocalSettings( PROJECT_LOCAL_SETTINGS* aSettings ) + { + m_localSettings = aSettings; + } + /** * Function configCreate * loads a *.pro file and returns a wxConfigBase. @@ -362,6 +378,9 @@ private: /// Backing store for project data -- owned by SETTINGS_MANAGER PROJECT_FILE* m_projectFile; + /// Backing store for project local settings -- owned by SETTINGS_MANAGER + PROJECT_LOCAL_SETTINGS* m_localSettings; + std::map m_sheetNames; std::map m_textVars; diff --git a/include/project/net_settings.h b/include/project/net_settings.h new file mode 100644 index 0000000000..b755ff726d --- /dev/null +++ b/include/project/net_settings.h @@ -0,0 +1,46 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * @author Jon Evans + * + * 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 . + */ + +#ifndef KICAD_NET_SETTINGS_H +#define KICAD_NET_SETTINGS_H + +#include +#include + +/** + * NET_SETTINGS stores various net-related settings in a project context. These settings are + * accessible and editable from both the schematic and PCB editors. + */ +class NET_SETTINGS : public NESTED_SETTINGS +{ +public: + NET_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ); + + virtual ~NET_SETTINGS(); + + NETCLASSES m_NetClasses; + +private: + NETCLASSPTR m_defaultClass; + + // TODO: Add diff pairs, bus information, etc here. +}; + +#endif // KICAD_NET_SETTINGS_H diff --git a/include/settings/project_file.h b/include/project/project_file.h similarity index 63% rename from include/settings/project_file.h rename to include/project/project_file.h index 7587b18fa3..a4127c47c0 100644 --- a/include/settings/project_file.h +++ b/include/project/project_file.h @@ -23,7 +23,9 @@ #include #include +#include +class NET_SETTINGS; /** * For files like sheets and boards, a pair of that object KIID and display name @@ -31,6 +33,20 @@ */ typedef std::pair FILE_INFO_PAIR; +/** + * For storing PcbNew MRU paths of various types + */ +enum LAST_PATH_TYPE : unsigned int +{ + LAST_PATH_NETLIST = 0, + LAST_PATH_STEP, + LAST_PATH_IDF, + LAST_PATH_VRML, + LAST_PATH_SPECCTRADSN, + LAST_PATH_GENCAD, + + LAST_PATH_SIZE +}; /** * PROJECT_FILE is the backing store for a PROJECT, in JSON format. @@ -47,9 +63,16 @@ public: */ PROJECT_FILE( const std::string& aFullPath ); - virtual ~PROJECT_FILE() {} + virtual ~PROJECT_FILE() = default; - virtual bool MigrateFromLegacy( wxConfigBase* aLegacyFile ) override; + virtual bool MigrateFromLegacy( wxConfigBase* aCfg ) override; + + bool SaveToFile( const std::string& aDirectory = "", bool aForce = false ) override; + + void SetProject( PROJECT* aProject ) + { + m_project = aProject; + } std::vector& GetSheets() { @@ -61,6 +84,11 @@ public: return m_boards; } + NET_SETTINGS& NetSettings() + { + return *m_NetSettings; + } + protected: wxString getFileExt() const override; @@ -74,6 +102,9 @@ private: /// A list of board files in this project std::vector m_boards; + /// A link to the owning PROJECT + PROJECT* m_project; + /** * Below are project-level settings that have not been moved to a dedicated file */ @@ -95,6 +126,31 @@ public: /// List of equivalence (equ) files used in the project std::vector m_EquivalenceFiles; + + /** + * PcbNew params + */ + + /// Page layout description file + wxString m_PageLayoutDescrFile; + + /// MRU path storage + wxString m_PcbLastPath[LAST_PATH_SIZE]; + + /** + * Board design settings for this project's board. This will be initialized by PcbNew after + * loading a board so that BOARD_DESIGN_SETTINGS doesn't need to live in common for now. + * Owned by the BOARD; may be null if a board isn't loaded: be careful + */ + NESTED_SETTINGS* m_BoardSettings; + + /** + * Net settings for this project (owned here) + * NOTE: If we go multi-board in the future, we have to decide whether to use a global + * NET_SETTINGS or have one per board. Right now I think global makes more sense (one set of + * schematics, one netlist partitioned into multiple boards) + */ + std::shared_ptr m_NetSettings; }; // Specializations to allow directly reading/writing FILE_INFO_PAIRs from JSON diff --git a/include/project/project_local_settings.h b/include/project/project_local_settings.h new file mode 100644 index 0000000000..a2ce498777 --- /dev/null +++ b/include/project/project_local_settings.h @@ -0,0 +1,83 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2020 CERN + * @author Jon Evans + * + * 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 . + */ + +#ifndef KICAD_PROJECT_LOCAL_SETTINGS_H +#define KICAD_PROJECT_LOCAL_SETTINGS_H + +#include +#include +#include + +class PROJECT; + +/** + * The project local settings are things that are attached to a particular project, but also might + * be particular to a certain user editing that project, or change quickly, and therefore may not + * want to be checked in to version control or otherwise distributed with the main project. + * + * Examples include layer visibility, recently-used design entry settings, and so on. + * + * The backing store is a JSON file named .kicad_prl + * + * This file doesn't need to exist for a project to be loaded. It will be created on-demand if + * any of the things stored here are modified by the user. + */ +class PROJECT_LOCAL_SETTINGS : public JSON_SETTINGS +{ +public: + PROJECT_LOCAL_SETTINGS( const std::string& aFilename ); + + virtual ~PROJECT_LOCAL_SETTINGS() {} + + bool MigrateFromLegacy( wxConfigBase* aLegacyConfig ) override; + + bool SaveToFile( const std::string& aDirectory = "", bool aForce = false ) override; + + void SetProject( PROJECT* aProject ) + { + m_project = aProject; + } + +protected: + + wxString getFileExt() const override + { + return ProjectLocalSettingsFileExtension; + } + +private: + + /// A link to the owning project + PROJECT* m_project; + +public: + + /** + * Board settings + */ + + /// The board layers that are turned on for viewing (@see PCB_LAYER_ID) + LSET m_VisibleLayers; + + /// The GAL layers (aka items) that are turned on for viewing (@see GAL_LAYER_ID) + GAL_SET m_VisibleItems; +}; + +#endif diff --git a/include/settings/color_settings.h b/include/settings/color_settings.h index 98afc21551..8f1fbac266 100644 --- a/include/settings/color_settings.h +++ b/include/settings/color_settings.h @@ -105,17 +105,15 @@ public: m_map( aMap ) {} - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; - COLOR4D val = m_default; - if( OPT optval = aSettings->Get( m_path ) ) - val = *optval; - - ( *m_map )[ m_key ] = val; + ( *m_map )[ m_key ] = *optval; + else if( aResetIfMissing ) + ( *m_map )[ m_key ] = m_default; } void Store( JSON_SETTINGS* aSettings) const override diff --git a/include/settings/json_settings.h b/include/settings/json_settings.h index b74dae95b9..5731e1c376 100644 --- a/include/settings/json_settings.h +++ b/include/settings/json_settings.h @@ -55,6 +55,8 @@ public: std::string GetFilename() const { return m_filename; } + wxString GetFullFilename() const; + SETTINGS_LOC GetLocation() const { return m_location; } void SetLegacyFilename( const std::string& aFilename ) { m_legacy_filename = aFilename; } @@ -81,8 +83,7 @@ public: /** * Calls Store() and then writes the contents of the JSON document to a file * @param aDirectory is the directory to save to, including trailing separator - * @param aForce if true will always save, even if contents are not modified - * @return true if the file was saved +c * @return true if the file was saved */ virtual bool SaveToFile( const std::string& aDirectory = "", bool aForce = false ); @@ -97,7 +98,7 @@ public: * @param aPath is a string containing one or more keys separated by '.' * @return a JSON object from within this one */ - OPT GetJson( std::string aPath ) const; + OPT GetJson( const std::string& aPath ) const; /** * Fetches a value from within the JSON document. @@ -107,9 +108,9 @@ public: * @return a value from within this document */ template - OPT Get( std::string aPath ) const + OPT Get( const std::string& aPath ) const { - if( OPT ret = GetJson( std::move( aPath ) ) ) + if( OPT ret = GetJson( aPath ) ) { try { @@ -131,9 +132,9 @@ public: * @param aVal is the value to store */ template - void Set( std::string aPath, ValueType aVal ) + void Set( const std::string& aPath, ValueType aVal ) { - ( *this )[PointerFromString( std::move( aPath ) ) ] = aVal; + ( *this )[PointerFromString( aPath ) ] = aVal; } /** @@ -253,6 +254,9 @@ protected: /// Whether or not to delete legacy file after migration bool m_deleteLegacyAfterMigration; + /// Whether or not to set parameters to their default value if missing from JSON on Load() + bool m_resetParamsIfMissing; + /// Version of this settings schema. int m_schemaVersion; @@ -265,9 +269,9 @@ protected: // Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API -template<> OPT JSON_SETTINGS::Get( std::string aPath ) const; +template<> OPT JSON_SETTINGS::Get( const std::string& aPath ) const; -template<> void JSON_SETTINGS::Set( std::string aPath, wxString aVal ); +template<> void JSON_SETTINGS::Set( const std::string& aPath, wxString aVal ); // Specializations to allow directly reading/writing wxStrings from JSON diff --git a/include/settings/nested_settings.h b/include/settings/nested_settings.h index 203099c57d..6fd4c5f181 100644 --- a/include/settings/nested_settings.h +++ b/include/settings/nested_settings.h @@ -49,7 +49,14 @@ public: */ bool SaveToFile( const std::string& aDirectory = "", bool aForce = false ) override; -private: + void SetParent( JSON_SETTINGS* aParent ); + + JSON_SETTINGS* GetParent() + { + return m_parent; + } + +protected: /// A pointer to the parent object to load and store from JSON_SETTINGS* m_parent; diff --git a/include/settings/parameters.h b/include/settings/parameters.h index 91090ca07a..424ca7e1ff 100644 --- a/include/settings/parameters.h +++ b/include/settings/parameters.h @@ -42,8 +42,9 @@ public: /** * Loads the value of this parameter from JSON to the underlying storage * @param aSettings is the JSON_SETTINGS object to load from. + * @param aResetIfMissing if true will set the parameter to its default value if load fails */ - virtual void Load( JSON_SETTINGS* aSettings ) const = 0; + virtual void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const = 0; /** * Stores the value of this parameter to the given JSON_SETTINGS object @@ -90,42 +91,42 @@ public: PARAM( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), - m_ptr( aPtr ), - m_default( aDefault ), m_min(), m_max(), - m_use_minmax( false ) + m_use_minmax( false ), + m_ptr( aPtr ), + m_default( aDefault ) { } PARAM( const std::string& aJsonPath, ValueType* aPtr, ValueType aDefault, ValueType aMin, ValueType aMax, bool aReadOnly = false ) : PARAM_BASE( aJsonPath, aReadOnly ), - m_ptr( aPtr ), - m_default( aDefault ), m_min( aMin ), m_max( aMax ), - m_use_minmax( true ) + m_use_minmax( true ), + m_ptr( aPtr ), + m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; - ValueType val = m_default; - if( OPT optval = aSettings->Get( m_path ) ) { - val = *optval; + ValueType val = *optval; if( m_use_minmax ) { if( m_max < val || val < m_min ) val = m_default; } - } - *m_ptr = val; + *m_ptr = val; + } + else if( aResetIfMissing ) + *m_ptr = m_default; } void Store( JSON_SETTINGS* aSettings ) const override @@ -157,13 +158,66 @@ public: } private: - ValueType* m_ptr; - ValueType m_default; ValueType m_min; ValueType m_max; bool m_use_minmax; + +protected: + ValueType* m_ptr; + ValueType m_default; }; +/** + * Stores a path as a string with directory separators normalized to unix-style + */ +class PARAM_PATH : public PARAM +{ +public: + PARAM_PATH( const std::string& aJsonPath, wxString* aPtr, wxString aDefault, + bool aReadOnly = false ) : + PARAM( aJsonPath, aPtr, aDefault, aReadOnly ) + { } + + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override + { + if( m_readOnly ) + return; + + PARAM::Load( aSettings, aResetIfMissing ); + + *m_ptr = fromFileFormat( *m_ptr ); + } + + void Store( JSON_SETTINGS* aSettings ) const override + { + aSettings->Set( m_path, toFileFormat( *m_ptr ) ); + } + + bool MatchesFile( JSON_SETTINGS* aSettings ) const override + { + if( OPT optval = aSettings->Get( m_path ) ) + return fromFileFormat( *optval ) == *m_ptr; + + return false; + } + +private: + wxString toFileFormat( const wxString& aString ) const + { + wxString ret = aString; + ret.Replace( wxT( "\\" ), wxT( "/" ) ); + return ret; + } + + wxString fromFileFormat( const wxString& aString ) const + { + wxString ret = aString; +#ifdef __WINDOWS__ + ret.Replace( wxT( "/" ), wxT( "\\" ) ); +#endif + return ret; + } +}; /** * Like a normal param, but with custom getter and setter functions @@ -182,28 +236,28 @@ public: m_setter( aSetter ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; - ValueType val = m_default; - if( std::is_same::value ) { if( OPT optval = aSettings->GetJson( m_path ) ) - val = *optval; + m_setter( *optval ); + else + m_setter( m_default ); } else { if( OPT optval = aSettings->Get( m_path ) ) - val = *optval; + m_setter( *optval ); + else + m_setter( m_default ); } - - m_setter( val ); } - void Store( JSON_SETTINGS* aSettings) const override + void Store( JSON_SETTINGS* aSettings ) const override { try { @@ -287,7 +341,7 @@ public: m_scale( aScale ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; @@ -296,6 +350,8 @@ public: if( OPT optval = aSettings->Get( m_path ) ) dval = *optval; + else if( !aResetIfMissing ) + return; ValueType val = KiROUND( dval / m_scale ); @@ -363,25 +419,25 @@ public: m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; - std::vector val = m_default; - if( OPT js = aSettings->GetJson( m_path ) ) { + std::vector val; + if( js->is_array() ) { - val.clear(); - for( const auto& el : js->items() ) val.push_back( el.value().get() ); } - } - *m_ptr = val; + *m_ptr = val; + } + else if( aResetIfMissing ) + *m_ptr = m_default; } void Store( JSON_SETTINGS* aSettings) const override @@ -445,12 +501,12 @@ public: PARAM_LIST( aJsonPath, aPtr, aDefault, aReadOnly ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; - PARAM_LIST::Load( aSettings ); + PARAM_LIST::Load( aSettings, aResetIfMissing ); for( size_t i = 0; i < m_ptr->size(); i++ ) ( *m_ptr )[i] = fromFileFormat( ( *m_ptr )[i] ); @@ -526,7 +582,7 @@ public: m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings ) const override + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override { if( m_readOnly ) return; diff --git a/include/settings/settings_manager.h b/include/settings/settings_manager.h index 73213fe85c..37f773ed1b 100644 --- a/include/settings/settings_manager.h +++ b/include/settings/settings_manager.h @@ -61,7 +61,7 @@ public: * If the given settings object is registered, save it to disk and unregister it * @param aSettings is the object to release */ - void FlushAndRelease( JSON_SETTINGS* aSettings ); + void FlushAndRelease( JSON_SETTINGS* aSettings, bool aSave = true ); /** * Returns a handle to the a given settings by type @@ -184,16 +184,18 @@ public: /** * Loads a project or sets up a new project with a specified path * @param aFullPath is the full path to the project + * @param aSetActive if true will set the loaded project as the active project * @return true if the PROJECT_FILE was successfully loaded from disk */ - bool LoadProject( const wxString& aFullPath ); + bool LoadProject( const wxString& aFullPath, bool aSetActive = true ); /** * Saves, unloads and unregisters the given PROJECT * @param aProject is the project object to unload + * @param aSave if true will save the project before unloading * @return true if the PROJECT file was successfully saved */ - bool UnloadProject( PROJECT& aProject ); + bool UnloadProject( PROJECT* aProject, bool aSave = true ); /** * A helper while we are not MDI-capable -- return the one and only project @@ -202,16 +204,23 @@ public: PROJECT& Prj() const; /** - * Saves the one and only project - * TODO: Update for MDI - * @return true if save was successful + * Retrieves a loaded project by name + * @param aFullPath is the full path including name and extension to the project file + * @return a pointer to the project if loaded, or nullptr */ - bool SaveProject(); + PROJECT* GetProject( const wxString& aFullPath ) const; /** - * @return the path to the settings folder for the loaded project + * Saves a loaded project. + * @param aFullPath is the project name to save. If empty, will save the first loaded project. + * @return true if save was successful */ - wxString GetProjectSettingsPath() const; + bool SaveProject( const wxString& aFullPath = wxEmptyString ); + + /** + * @return the path to the backups folder for the loaded project + */ + wxString GetProjectBackupsPath() const; /** * Checks if a given path is probably a valid KiCad configuration directory. @@ -300,11 +309,12 @@ private: bool loadProjectFile( PROJECT& aProject ); /** - * Saves, unloads and unregisters the given PROJECT_FILE + * Optionally saves, and then unloads and unregisters the given PROJECT_FILE * @param aProject is the project object to unload the file for + * @param aSave if true will save the project file before unloading * @return true if the PROJECT file was successfully saved */ - bool unloadProjectFile( PROJECT& aProject ); + bool unloadProjectFile( PROJECT* aProject, bool aSave ); private: diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index e7cb974511..541ff77ebf 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -116,6 +116,7 @@ extern const std::string SchematicBackupFileExtension; extern const std::string VrmlFileExtension; extern const std::string ProjectFileExtension; extern const std::string LegacyProjectFileExtension; +extern const std::string ProjectLocalSettingsFileExtension; extern const std::string LegacySchematicFileExtension; extern const std::string KiCadSchematicFileExtension; extern const std::string NetlistFileExtension; diff --git a/kicad/kicad_manager_frame.cpp b/kicad/kicad_manager_frame.cpp index 2b433f430e..6451f5983f 100644 --- a/kicad/kicad_manager_frame.cpp +++ b/kicad/kicad_manager_frame.cpp @@ -56,14 +56,6 @@ #define SEP() wxFileName::GetPathSeparator() -// Not really useful, provided to save/restore params in project config file, -// (Add them in s_KicadManagerParams if any) -// Used also to create new .pro files from the kicad.pro template file -// for new projects -#define GeneralGroupName wxT( "/general" ) - -std::vector s_KicadManagerParams; - // Menubar and toolbar event table BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME ) diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 052431f496..1e14156409 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -448,7 +448,6 @@ if( KICAD_SCRIPTING ) # Generate pcbnew.py and pcbnew_wrap.cxx using swig DEPENDS swig/pcb_target.i DEPENDS swig/pcb_plot_params.i DEPENDS swig/footprint.i - DEPENDS swig/netclass.i DEPENDS swig/netinfo.i DEPENDS swig/pad.i DEPENDS swig/pcb_text.i @@ -463,6 +462,7 @@ if( KICAD_SCRIPTING ) # Generate pcbnew.py and pcbnew_wrap.cxx using swig DEPENDS ${CMAKE_SOURCE_DIR}/common/swig/kicad.i DEPENDS ${CMAKE_SOURCE_DIR}/common/swig/wx.i DEPENDS ${CMAKE_SOURCE_DIR}/common/swig/ki_exception.i + DEPENDS ${CMAKE_SOURCE_DIR}/common/swig/netclass.i DEPENDS ${CMAKE_SOURCE_DIR}/scripting/kicadplugins.i COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/docstrings diff --git a/pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp b/pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp index 72cde5b289..3769360256 100644 --- a/pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp +++ b/pcbnew/altium2kicadpcb_plugin/altium_pcb.cpp @@ -757,7 +757,7 @@ void ALTIUM_PCB::ParseClasses6Data( if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS ) { const NETCLASSPTR& netclass = std::make_shared( elem.name ); - designSettings.m_NetClasses.Add( netclass ); + designSettings.GetNetClasses().Add( netclass ); for( const auto& name : elem.names ) { diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 40382e30bb..c0740335ab 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -32,502 +32,33 @@ #include #include #include - -#define CopperLayerCountKey wxT( "CopperLayerCount" ) -#define BoardThicknessKey wxT( "BoardThickness" ) - -#define LayerKeyPrefix wxT( "Layer" ) -#define LayerNameKey wxT( "Name" ) -#define LayerTypeKey wxT( "Type" ) -#define LayerEnabledKey wxT( "Enabled" ) - -#define NetclassNameKey wxT( "Name" ) -#define ClearanceKey wxT( "Clearance" ) -#define TrackWidthKey wxT( "TrackWidth" ) -#define ViaDiameterKey wxT( "ViaDiameter" ) -#define ViaDrillKey wxT( "ViaDrill" ) -#define uViaDiameterKey wxT( "uViaDiameter" ) -#define uViaDrillKey wxT( "uViaDrill" ) -#define dPairWidthKey wxT( "dPairWidth" ) -#define dPairGapKey wxT( "dPairGap" ) -#define dPairViaGapKey wxT( "dPairViaGap" ) +#include +#include -class PARAM_CFG_SEVERITIES : public PARAM_CFG +const int bdsSchemaVersion = 0; + + +BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) : + NESTED_SETTINGS( "board_design_settings", bdsSchemaVersion, aParent, aPath ), + m_Pad_Master( NULL ) + { -protected: - BOARD* m_Pt_param; ///< Pointer to the parameter value + // We want to leave alone parameters that aren't found in the project JSON as they may be + // initialized by the board file parser before NESTED_SETTINGS::LoadFromFile is called. + m_resetParamsIfMissing = false; -public: - PARAM_CFG_SEVERITIES( BOARD* ptparam, const wxChar* group = nullptr ) : - PARAM_CFG( wxEmptyString, PARAM_SEVERITIES, group ) - { - m_Pt_param = ptparam; - } + // Create a default NETCLASS list so that things don't break horribly if there's no project + // loaded. This also is used during file load for legacy boards that have netclasses stored + // in the file. After load, this information will be moved to the project and the pointer + // updated. + m_netClasses = &m_internalNetClasses; - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - BOARD* board = m_Pt_param; - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - wxString oldPath = aConfig->GetPath(); - - // Read legacy settings first so that modern settings will overwrite them - bool flag; - - if( aConfig->Read( wxT( "RequireCourtyardDefinitions" ), &flag, false ) ) - { - if( flag ) - bds.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_ERROR; - else - bds.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; - } - - if( aConfig->Read( wxT( "ProhibitOverlappingCourtyards" ), &flag, false ) ) - { - if( flag ) - bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; - else - bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_IGNORE; - } - - DRC_ITEM drc( 0 ); - wxString severity; - - auto mapSeverity = []( const wxString& aSeverity ) - { - if( aSeverity == wxT( "warning" ) ) - return RPT_SEVERITY_WARNING; - else if( aSeverity == wxT( "ignore" ) ) - return RPT_SEVERITY_IGNORE; - else - return RPT_SEVERITY_ERROR; - }; - - for( int i = DRCE_FIRST; i <= DRCE_LAST; ++i ) - { - wxString name = drc.GetErrorText( i, false ); - name.Replace( wxT( " " ), wxT( "_" ) ); - - if( aConfig->Read( name, &severity, wxEmptyString ) ) - bds.m_DRCSeverities[i] = mapSeverity( severity ); - } - - aConfig->SetPath( oldPath ); - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - BOARD* board = m_Pt_param; - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - wxString oldPath = aConfig->GetPath(); - DRC_ITEM drc( 0 ); - - auto mapSeverity = []( int aSeverity ) - { - if( aSeverity == RPT_SEVERITY_IGNORE ) - return wxT( "ignore" ); - else if( aSeverity == RPT_SEVERITY_WARNING ) - return wxT( "warning" ); - else - return wxT( "error" ); - }; - - for( int i = DRCE_FIRST; i <= DRCE_LAST; ++i ) - { - wxString name = drc.GetErrorText( i, false ); - name.Replace( wxT( " " ), wxT( "_" ) ); - - aConfig->Write( name, mapSeverity( bds.m_DRCSeverities[i] ) ); - } - - aConfig->SetPath( oldPath ); - } -}; - - -// -// NOTE: layer configuration info is stored in both the BOARD and BOARD_DESIGN_SETTINGS so one -// of the two needs to read/write the config so we don't end up with order dependency issues. -// -class PARAM_CFG_LAYERS : public PARAM_CFG -{ -protected: - BOARD* m_Pt_param; ///< Pointer to the parameter value - -public: - PARAM_CFG_LAYERS( BOARD* ptparam, const wxChar* group = nullptr ) : - PARAM_CFG( wxEmptyString, PARAM_LAYERS, group ) - { - m_Pt_param = ptparam; - } - - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - BOARD* board = m_Pt_param; - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - LSET enabledLayers = bds.GetEnabledLayers(); - wxString oldPath = aConfig->GetPath(); - wxString layerKeyPrefix = LayerKeyPrefix; - - bds.SetCopperLayerCount( aConfig->Read( CopperLayerCountKey, 2 ) ); - - double thickness = aConfig->ReadDouble( BoardThicknessKey, DEFAULT_BOARD_THICKNESS_MM ); - bds.SetBoardThickness( Millimeter2iu( thickness ) ); - - for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq ) - { - PCB_LAYER_ID layer = *seq; - wxString path = layerKeyPrefix + wxT( "." ) + board->GetStandardLayerName( layer ); - wxString layerName; - int layerType; - bool layerEnabled; - - aConfig->SetPath( oldPath ); - aConfig->SetPath( path ); - - if( aConfig->Read( LayerNameKey, &layerName ) ) - board->SetLayerName( layer, layerName ); - - if( aConfig->Read( LayerTypeKey, &layerType ) ) - board->SetLayerType( layer, (LAYER_T) layerType ); - - if( aConfig->Read( LayerEnabledKey, &layerEnabled ) ) - enabledLayers.set( layer, layerEnabled ); - } - - board->SetEnabledLayers( enabledLayers ); - - aConfig->SetPath( oldPath ); - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - BOARD* board = m_Pt_param; - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - wxString oldPath = aConfig->GetPath(); - wxString layerKeyPrefix = LayerKeyPrefix; - - aConfig->Write( CopperLayerCountKey, board->GetCopperLayerCount() ); - aConfig->Write( BoardThicknessKey, Iu2Millimeter( bds.GetBoardThickness() ) ); - - for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq ) - { - PCB_LAYER_ID layer = *seq; - wxString path = layerKeyPrefix + wxT( "." ) + board->GetStandardLayerName( layer ); - wxString layerName = board->GetLayerName( layer ); - LAYER_T layerType = board->GetLayerType( layer ); - - aConfig->SetPath( oldPath ); - aConfig->SetPath( path ); - - if( IsCopperLayer( layer ) ) - { - aConfig->Write( LayerNameKey, layerName ); - aConfig->Write( LayerTypeKey, (int) layerType ); - } - - aConfig->Write( LayerEnabledKey, board->IsLayerEnabled( layer ) ); - } - - aConfig->SetPath( oldPath ); - } -}; - - -class PARAM_CFG_TRACKWIDTHS : public PARAM_CFG -{ -protected: - std::vector* m_Pt_param; ///< Pointer to the parameter value - -public: - PARAM_CFG_TRACKWIDTHS( std::vector* ptparam, const wxChar* group = nullptr ) : - PARAM_CFG( wxEmptyString, PARAM_TRACKWIDTHS, group ) - { - m_Pt_param = ptparam; - } - - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - m_Pt_param->clear(); - - for( int index = 1; ; ++index ) - { - wxString key = TrackWidthKey; - double width; - - if( !aConfig->Read( key << index, &width ) ) - break; - - m_Pt_param->push_back( Millimeter2iu( width ) ); - } - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - for( size_t index = 1; index <= m_Pt_param->size(); ++index ) - { - wxString key = TrackWidthKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ) ) ); - } - } -}; - - -class PARAM_CFG_VIADIMENSIONS : public PARAM_CFG -{ -protected: - std::vector* m_Pt_param; ///< Pointer to the parameter value - -public: - PARAM_CFG_VIADIMENSIONS( std::vector* ptparam, const wxChar* group = nullptr ) : - PARAM_CFG( wxEmptyString, PARAM_VIADIMENSIONS, group ) - { - m_Pt_param = ptparam; - } - - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - m_Pt_param->clear(); - - for( int index = 1; ; ++index ) - { - double diameter = 0.0, drill = 0.0; - - wxString key = ViaDiameterKey; - - if( !aConfig->Read( key << index, &diameter ) ) - break; - - key = ViaDrillKey; - drill = aConfig->ReadDouble( key << index, 0.0 ); - - m_Pt_param->emplace_back( VIA_DIMENSION( Millimeter2iu( diameter ), - Millimeter2iu( drill ) ) ); - } - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - for( size_t index = 1; index <= m_Pt_param->size(); ++index ) - { - wxString key = ViaDiameterKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ).m_Diameter ) ); - key = ViaDrillKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ).m_Drill ) ); - } - } -}; - - -class PARAM_CFG_DIFFPAIRDIMENSIONS : public PARAM_CFG -{ -protected: - std::vector* m_Pt_param; ///< Pointer to the parameter value - -public: - PARAM_CFG_DIFFPAIRDIMENSIONS( std::vector* ptparam, - const wxChar* group = nullptr ) : - PARAM_CFG( wxEmptyString, PARAM_DIFFPAIRDIMENSIONS, group ) - { - m_Pt_param = ptparam; - } - - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - m_Pt_param->clear(); - - for( int index = 1; ; ++index ) - { - double width, gap, viagap; - - wxString key = dPairWidthKey; - - if( !aConfig->Read( key << index, &width ) ) - break; - - key = dPairGapKey; - gap = aConfig->ReadDouble( key << index, 0.0 ); - - key = dPairViaGapKey; - viagap = aConfig->ReadDouble( key << index, 0.0 ); - - m_Pt_param->emplace_back( DIFF_PAIR_DIMENSION( Millimeter2iu( width ), - Millimeter2iu( gap ), - Millimeter2iu( viagap ) ) ); - } - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - for( size_t index = 1; index <= m_Pt_param->size(); ++index ) - { - wxString key = dPairWidthKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ).m_Width ) ); - key = dPairGapKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ).m_Gap ) ); - key = dPairViaGapKey; - aConfig->Write( key << index, Iu2Millimeter( m_Pt_param->at( index - 1 ).m_ViaGap ) ); - } - } -}; - - -class PARAM_CFG_NETCLASSES : public PARAM_CFG -{ -protected: - NETCLASSES* m_Pt_param; ///< Pointer to the parameter value - -public: - PARAM_CFG_NETCLASSES( const wxChar* ident, NETCLASSES* ptparam, - const wxChar* group = nullptr ) : - PARAM_CFG( ident, PARAM_NETCLASSES, group ) - { - m_Pt_param = ptparam; - } - - void ReadParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - wxString oldPath = aConfig->GetPath(); - - m_Pt_param->Clear(); - - for( int index = 0; ; ++index ) - { - wxString path = ""; - NETCLASSPTR netclass; - wxString netclassName; - - if( index == 0 ) - path = "Default"; - else - path << index; - - aConfig->SetPath( oldPath ); - aConfig->SetPath( m_Ident ); - aConfig->SetPath( path ); - - if( !aConfig->Read( NetclassNameKey, &netclassName ) ) - break; - - if( index == 0 ) - netclass = m_Pt_param->GetDefault(); - else - netclass = std::make_shared( netclassName ); - -#define READ_MM( aKey, aDefault ) Millimeter2iu( aConfig->ReadDouble( aKey, aDefault ) ) - netclass->SetClearance( READ_MM( ClearanceKey, netclass->GetClearance() ) ); - netclass->SetTrackWidth( READ_MM( TrackWidthKey, netclass->GetTrackWidth() ) ); - netclass->SetViaDiameter( READ_MM( ViaDiameterKey, netclass->GetViaDiameter() ) ); - netclass->SetViaDrill( READ_MM( ViaDrillKey, netclass->GetViaDrill() ) ); - netclass->SetuViaDiameter( READ_MM( uViaDiameterKey, netclass->GetuViaDiameter() ) ); - netclass->SetuViaDrill( READ_MM( uViaDrillKey, netclass->GetuViaDrill() ) ); - netclass->SetDiffPairWidth( READ_MM( dPairWidthKey, netclass->GetDiffPairWidth() ) ); - netclass->SetDiffPairGap( READ_MM( dPairGapKey, netclass->GetDiffPairGap() ) ); - netclass->SetDiffPairViaGap( READ_MM( dPairViaGapKey, netclass->GetDiffPairViaGap() ) ); - - if( index > 0 ) - m_Pt_param->Add( netclass ); - } - - aConfig->SetPath( oldPath ); - } - - void SaveParam( wxConfigBase* aConfig ) const override - { - if( !m_Pt_param || !aConfig ) - return; - - wxString oldPath = aConfig->GetPath(); - NETCLASSES::const_iterator nc = m_Pt_param->begin(); - - for( unsigned index = 0; index <= m_Pt_param->GetCount(); ++index ) - { - wxString path = ""; - NETCLASSPTR netclass; - - if( index == 0 ) - path = "Default"; - else - path << index; - - aConfig->SetPath( oldPath ); - aConfig->SetPath( m_Ident ); - aConfig->SetPath( path ); - - if( index == 0 ) - { - netclass = m_Pt_param->GetDefault(); - } - else - { - netclass = nc->second; - ++nc; - } - - aConfig->Write( NetclassNameKey, netclass->GetName() ); - -#define WRITE_MM( aKey, aValue ) aConfig->Write( aKey, Iu2Millimeter( aValue ) ) - WRITE_MM( ClearanceKey, netclass->GetClearance() ); - WRITE_MM( TrackWidthKey, netclass->GetTrackWidth() ); - WRITE_MM( ViaDiameterKey, netclass->GetViaDiameter() ); - WRITE_MM( ViaDrillKey, netclass->GetViaDrill() ); - WRITE_MM( uViaDiameterKey, netclass->GetuViaDiameter() ); - WRITE_MM( uViaDrillKey, netclass->GetuViaDrill() ); - WRITE_MM( dPairWidthKey, netclass->GetDiffPairWidth() ); - WRITE_MM( dPairGapKey, netclass->GetDiffPairGap() ); - WRITE_MM( dPairViaGapKey, netclass->GetDiffPairViaGap() ); - } - - aConfig->SetPath( oldPath ); - } -}; - - -BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : - m_Pad_Master( NULL ) -{ m_HasStackup = false; // no stackup defined by default LSET all_set = LSET().set(); m_enabledLayers = all_set; // All layers enabled at first. // SetCopperLayerCount() will adjust this. - SetVisibleLayers( all_set ); - - // set all but hidden text as visible. - m_visibleElements = ~( 1 << GAL_LAYER_INDEX( LAYER_MOD_TEXT_INVISIBLE ) ); SetCopperLayerCount( 2 ); // Default design is a double sided board m_CurrentViaType = VIATYPE::THROUGH; @@ -642,212 +173,499 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS() : m_viaSizeIndex = 0; m_trackWidthIndex = 0; m_diffPairIndex = 0; -} -// Add parameters to save in project config. -// values are saved in mm -void BOARD_DESIGN_SETTINGS::AppendConfigs( BOARD* aBoard, std::vector* aResult ) -{ - aResult->push_back( new PARAM_CFG_LAYERS( aBoard ) ); + // Parameters stored in JSON in the project file - aResult->push_back( new PARAM_CFG_BOOL( wxT( "AllowMicroVias" ), - &m_MicroViasAllowed, false ) ); + // NOTE: Previously, BOARD_DESIGN_SETTINGS stored the basic board layer information (layer + // names and enable/disable state) in the project file even though this information is also + // stored in the board file. This was implemented for importing these settings from another + // project. Going forward, the import feature will just import from other board files (since + // we could have multi-board projects in the future anyway) so this functionality is dropped. - aResult->push_back( new PARAM_CFG_BOOL( wxT( "AllowBlindVias" ), - &m_BlindBuriedViaAllowed, false ) ); + m_params.emplace_back( new PARAM( "rules.allow_microvias", &m_MicroViasAllowed, false ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinClearance" ), - &m_MinClearance, - Millimeter2iu( DEFAULT_MINCLEARANCE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( + new PARAM( "rules.allow_blind_buried_vias", &m_BlindBuriedViaAllowed, false ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinTrackWidth" ), - &m_TrackMinWidth, - Millimeter2iu( DEFAULT_TRACKMINWIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_clearance", &m_MinClearance, + Millimeter2iu( DEFAULT_MINCLEARANCE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), + MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinViaAnnulus" ), - &m_ViasMinAnnulus, - Millimeter2iu( DEFAULT_VIASMINSIZE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_track_width", &m_TrackMinWidth, + Millimeter2iu( DEFAULT_TRACKMINWIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), + MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinViaDiameter" ), - &m_ViasMinSize, - Millimeter2iu( DEFAULT_VIASMINSIZE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_via_annulus", &m_ViasMinAnnulus, + Millimeter2iu( DEFAULT_VIASMINSIZE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), + MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinThroughDrill" ), - &m_MinThroughDrill, - Millimeter2iu( DEFAULT_MINTHROUGHDRILL ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU, wxT( "MinViaDrill" ) ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_via_diameter", &m_ViasMinSize, + Millimeter2iu( DEFAULT_VIASMINSIZE ), Millimeter2iu( 0.01 ), Millimeter2iu( 25.0 ), + MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinMicroViaDiameter" ), - &m_MicroViasMinSize, - Millimeter2iu( DEFAULT_MICROVIASMINSIZE ), Millimeter2iu( 0.01 ), Millimeter2iu( 10.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_through_hole_diameter", + &m_MinThroughDrill, Millimeter2iu( DEFAULT_MINTHROUGHDRILL ), Millimeter2iu( 0.01 ), + Millimeter2iu( 25.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinMicroViaDrill" ), - &m_MicroViasMinDrill, - Millimeter2iu( DEFAULT_MICROVIASMINDRILL ), Millimeter2iu( 0.01 ), Millimeter2iu( 10.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_microvia_diameter", + &m_MicroViasMinSize, Millimeter2iu( DEFAULT_MICROVIASMINSIZE ), Millimeter2iu( 0.01 ), + Millimeter2iu( 10.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "MinHoleToHole" ), - &m_HoleToHoleMin, - Millimeter2iu( DEFAULT_HOLETOHOLEMIN ), Millimeter2iu( 0.0 ), Millimeter2iu( 10.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_microvia_drill", &m_MicroViasMinDrill, + Millimeter2iu( DEFAULT_MICROVIASMINDRILL ), Millimeter2iu( 0.01 ), + Millimeter2iu( 10.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_SEVERITIES( aBoard ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_hole_to_hole", &m_HoleToHoleMin, + Millimeter2iu( DEFAULT_HOLETOHOLEMIN ), Millimeter2iu( 0.00 ), Millimeter2iu( 10.0 ), + MM_PER_IU ) ); // Note: a clearance of -0.01 is a flag indicating we should use the legacy (pre-6.0) method // based on the edge cut thicknesses. - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CopperEdgeClearance" ), - &m_CopperEdgeClearance, - Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ), Millimeter2iu( -0.01 ), Millimeter2iu( 25.0 ), - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.min_copper_edge_clearance", + &m_CopperEdgeClearance, Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ), + Millimeter2iu( -0.01 ), Millimeter2iu( 25.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_TRACKWIDTHS( &m_TrackWidthList ) ); - aResult->push_back( new PARAM_CFG_VIADIMENSIONS( &m_ViasDimensionsList ) ); - aResult->push_back( new PARAM_CFG_DIFFPAIRDIMENSIONS( &m_DiffPairDimensionsList ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.solder_mask_clearance", + &m_SolderMaskMargin, Millimeter2iu( DEFAULT_SOLDERMASK_CLEARANCE ), + Millimeter2iu( -1.0 ), Millimeter2iu( 1.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_NETCLASSES( wxT( "Netclasses" ), &m_NetClasses ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.solder_mask_min_width", + &m_SolderMaskMinWidth, Millimeter2iu( DEFAULT_SOLDERMASK_MIN_WIDTH ), 0, + Millimeter2iu( 1.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SilkLineWidth" ), - &m_LineThickness[ LAYER_CLASS_SILK ], - Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU, wxT( "ModuleOutlineThickness" ) ) ); + m_params.emplace_back( new PARAM_SCALED( "rules.solder_paste_clearance", + &m_SolderPasteMargin, Millimeter2iu( DEFAULT_SOLDERPASTE_CLEARANCE ), + Millimeter2iu( -1.0 ), Millimeter2iu( 1.0 ), MM_PER_IU ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SilkTextSizeV" ), - &m_TextSize[ LAYER_CLASS_SILK ].y, - Millimeter2iu( DEFAULT_SILK_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU, wxT( "ModuleTextSizeV" ) ) ); + m_params.emplace_back( new PARAM( "rules.solder_paste_margin_ratio", + &m_SolderPasteMarginRatio, DEFAULT_SOLDERPASTE_RATIO, -0.5, 1.0 ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SilkTextSizeH" ), - &m_TextSize[ LAYER_CLASS_SILK ].x, - Millimeter2iu( DEFAULT_SILK_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU, wxT( "ModuleTextSizeH" ) ) ); + m_params.emplace_back( new PARAM_LAMBDA( "rule_severities", + [&]() -> nlohmann::json + { + nlohmann::json ret = {}; + DRC_ITEM drc( 0 ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SilkTextSizeThickness" ), - &m_TextThickness[ LAYER_CLASS_SILK ], - Millimeter2iu( DEFAULT_SILK_TEXT_WIDTH ), 1, TEXTS_MAX_WIDTH, - nullptr, MM_PER_IU, wxT( "ModuleTextSizeThickness" ) ) ); + for( int i = DRCE_FIRST; i <= DRCE_LAST; ++i ) + { + if( !m_DRCSeverities.count( i ) ) + continue; - aResult->push_back( new PARAM_CFG_BOOL( wxT( "SilkTextItalic" ), - &m_TextItalic[ LAYER_CLASS_SILK ], false ) ); + wxString name = drc.GetErrorText( i, false ); + name.Replace( wxT( " " ), wxT( "_" ) ); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "SilkTextUpright" ), - &m_TextUpright[ LAYER_CLASS_SILK ], true ) ); + ret[std::string( name.ToUTF8() )] = + severityToString( static_cast( m_DRCSeverities[i] ) ); + } - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CopperLineWidth" ), - &m_LineThickness[ LAYER_CLASS_COPPER ], - Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU, wxT( "DrawSegmentWidth" ) ) ); + return ret; + }, + [&]( const nlohmann::json& aJson ) + { + if( !aJson.is_object() ) + return; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CopperTextSizeV" ), - &m_TextSize[ LAYER_CLASS_COPPER ].y, - Millimeter2iu( DEFAULT_COPPER_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU, wxT( "PcbTextSizeV" ) ) ); + DRC_ITEM drc( 0 ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CopperTextSizeH" ), - &m_TextSize[ LAYER_CLASS_COPPER ].x, - Millimeter2iu( DEFAULT_COPPER_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU, wxT( "PcbTextSizeH" ) ) ); + for( int i = DRCE_FIRST; i <= DRCE_LAST; ++i ) + { + wxString name = drc.GetErrorText( i, false ); + name.Replace( wxT( " " ), wxT( "_" ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CopperTextThickness" ), - &m_TextThickness[ LAYER_CLASS_COPPER ], - Millimeter2iu( DEFAULT_COPPER_TEXT_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU, wxT( "PcbTextThickness" ) ) ); + std::string key( name.ToUTF8() ); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "CopperTextItalic" ), - &m_TextItalic[ LAYER_CLASS_COPPER ], false ) ); + if( aJson.contains( key ) ) + m_DRCSeverities[i] = severityFromString( aJson[key] ); + } + }, {} ) ); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "CopperTextUpright" ), - &m_TextUpright[ LAYER_CLASS_COPPER ], true ) ); + m_params.emplace_back( new PARAM_LAMBDA( "drc_exclusions", + [&]() -> nlohmann::json + { + nlohmann::json js = nlohmann::json::array(); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "EdgeCutLineWidth" ), - &m_LineThickness[ LAYER_CLASS_EDGES ], - Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU, wxT( "BoardOutlineThickness" ) ) ); + for( const auto& entry : m_DrcExclusions ) + js.push_back( entry ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "CourtyardLineWidth" ), - &m_LineThickness[ LAYER_CLASS_COURTYARD ], - Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU ) ); + return js; + }, + [&]( const nlohmann::json& aObj ) + { + m_DrcExclusions.clear(); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "FabLineWidth" ), - &m_LineThickness[ LAYER_CLASS_FAB ], - Millimeter2iu( DEFAULT_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU ) ); + if( !aObj.is_array() ) + return; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "FabTextSizeV" ), - &m_TextSize[ LAYER_CLASS_FAB ].x, - Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU ) ); + for( const nlohmann::json& entry : aObj ) + { + if( entry.empty() ) + continue; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "FabTextSizeH" ), - &m_TextSize[ LAYER_CLASS_FAB ].y, - Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU ) ); + m_DrcExclusions.insert( entry.get() ); + } + }, + {} ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "FabTextSizeThickness" ), - &m_TextThickness[ LAYER_CLASS_FAB ], - Millimeter2iu( DEFAULT_TEXT_WIDTH ), 1, TEXTS_MAX_WIDTH, - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_LAMBDA( "track_widths", + [&]() -> nlohmann::json + { + nlohmann::json js = nlohmann::json::array(); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "FabTextItalic" ), - &m_TextItalic[ LAYER_CLASS_FAB ], false ) ); + for( const int& width : m_TrackWidthList ) + js.push_back( Iu2Millimeter( width ) ); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "FabTextUpright" ), - &m_TextUpright[ LAYER_CLASS_FAB ], true ) ); + return js; + }, + [&]( const nlohmann::json& aJson ) + { + if( !aJson.is_array() ) + return; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "OthersLineWidth" ), - &m_LineThickness[ LAYER_CLASS_OTHERS ], - Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), - nullptr, MM_PER_IU, wxT( "ModuleOutlineThickness" ) ) ); + m_TrackWidthList.clear(); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "OthersTextSizeV" ), - &m_TextSize[ LAYER_CLASS_OTHERS ].x, - Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU ) ); + for( const nlohmann::json& entry : aJson ) + { + if( entry.empty() ) + continue; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "OthersTextSizeH" ), - &m_TextSize[ LAYER_CLASS_OTHERS ].y, - Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, - nullptr, MM_PER_IU ) ); + m_TrackWidthList.emplace_back( Millimeter2iu( entry.get() ) ); + } + }, + {} ) ); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "OthersTextSizeThickness" ), - &m_TextThickness[ LAYER_CLASS_OTHERS ], - Millimeter2iu( DEFAULT_TEXT_WIDTH ), 1, TEXTS_MAX_WIDTH, - nullptr, MM_PER_IU ) ); + m_params.emplace_back( new PARAM_LAMBDA( "via_dimensions", + [&]() -> nlohmann::json + { + nlohmann::json js = nlohmann::json::array(); - aResult->push_back( new PARAM_CFG_BOOL( wxT( "OthersTextItalic" ), - &m_TextItalic[ LAYER_CLASS_OTHERS ], false ) ); + for( const auto& via : m_ViasDimensionsList ) + { + nlohmann::json entry = {}; - aResult->push_back( new PARAM_CFG_BOOL( wxT( "OthersTextUpright" ), - &m_TextUpright[ LAYER_CLASS_OTHERS ], true ) ); + entry["diameter"] = Iu2Millimeter( via.m_Diameter ); + entry["drill"] = Iu2Millimeter( via.m_Drill ); - aResult->push_back( new PARAM_CFG_INT( wxT( "DimensionUnits" ), - &m_DimensionUnits, 0, 0, 2 ) ); - aResult->push_back( new PARAM_CFG_INT( wxT( "DimensionPrecision" ), - &m_DimensionPrecision, 1, 0, 2 ) ); + js.push_back( entry ); + } - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SolderMaskClearance" ), - &m_SolderMaskMargin, - Millimeter2iu( DEFAULT_SOLDERMASK_CLEARANCE ), Millimeter2iu( -1.0 ), Millimeter2iu( 1.0 ), - nullptr, MM_PER_IU ) ); + return js; + }, + [&]( const nlohmann::json& aObj ) + { + if( !aObj.is_array() ) + return; - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SolderMaskMinWidth" ), - &m_SolderMaskMinWidth, - Millimeter2iu( DEFAULT_SOLDERMASK_MIN_WIDTH ), 0, Millimeter2iu( 1.0 ), - nullptr, MM_PER_IU ) ); + m_ViasDimensionsList.clear(); - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "SolderPasteClearance" ), - &m_SolderPasteMargin, - Millimeter2iu( DEFAULT_SOLDERPASTE_CLEARANCE ), Millimeter2iu( -1.0 ), Millimeter2iu( 1.0 ), - nullptr, MM_PER_IU ) ); + for( const nlohmann::json& entry : aObj ) + { + if( entry.empty() || !entry.is_object() ) + continue; - aResult->push_back( new PARAM_CFG_DOUBLE( wxT( "SolderPasteRatio" ), - &m_SolderPasteMarginRatio, - DEFAULT_SOLDERPASTE_RATIO, -0.5, 1.0 ) ); + if( !entry.contains( "diameter" ) || !entry.contains( "drill" ) ) + continue; + + int diameter = Millimeter2iu( entry["diameter"].get() ); + int drill = Millimeter2iu( entry["drill"].get() ); + + m_ViasDimensionsList.emplace_back( VIA_DIMENSION( diameter, drill ) ); + } + }, + {} ) ); + + m_params.emplace_back( new PARAM_LAMBDA( "diff_pair_dimensions", + [&]() -> nlohmann::json + { + nlohmann::json js = nlohmann::json::array(); + + for( const auto& pair : m_DiffPairDimensionsList ) + { + nlohmann::json entry = {}; + + entry["width"] = Iu2Millimeter( pair.m_Width ); + entry["gap"] = Iu2Millimeter( pair.m_Gap ); + entry["via_gap"] = Iu2Millimeter( pair.m_ViaGap ); + + js.push_back( entry ); + } + + return js; + }, + [&]( const nlohmann::json& aObj ) + { + if( !aObj.is_array() ) + return; + + m_DiffPairDimensionsList.clear(); + + for( const nlohmann::json& entry : aObj ) + { + if( entry.empty() || !entry.is_object() ) + continue; + + if( !entry.contains( "width" ) || !entry.contains( "gap" ) + || !entry.contains( "via_gap" ) ) + continue; + + int width = Millimeter2iu( entry["width"].get() ); + int gap = Millimeter2iu( entry["gap"].get() ); + int via_gap = Millimeter2iu( entry["via_gap"].get() ); + + m_DiffPairDimensionsList.emplace_back( + DIFF_PAIR_DIMENSION( width, gap, via_gap ) ); + } + }, + {} ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.silk_line_width", + &m_LineThickness[LAYER_CLASS_SILK], Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.silk_text_size_v", + &m_TextSize[LAYER_CLASS_SILK].y, Millimeter2iu( DEFAULT_SILK_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.silk_text_size_h", + &m_TextSize[LAYER_CLASS_SILK].x, Millimeter2iu( DEFAULT_SILK_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.silk_text_thickness", + &m_TextThickness[LAYER_CLASS_SILK], Millimeter2iu( DEFAULT_SILK_TEXT_WIDTH ), 1, + TEXTS_MAX_WIDTH, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM( + "defaults.silk_text_italic", &m_TextItalic[LAYER_CLASS_SILK], false ) ); + + m_params.emplace_back( new PARAM( + "defaults.silk_text_upright", &m_TextUpright[ LAYER_CLASS_SILK ], true ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.copper_line_width", + &m_LineThickness[LAYER_CLASS_COPPER], Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.copper_text_size_v", + &m_TextSize[LAYER_CLASS_COPPER].y, Millimeter2iu( DEFAULT_COPPER_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.copper_text_size_h", + &m_TextSize[LAYER_CLASS_COPPER].x, Millimeter2iu( DEFAULT_COPPER_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.copper_text_thickness", + &m_TextThickness[LAYER_CLASS_COPPER], Millimeter2iu( DEFAULT_COPPER_TEXT_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM( + "defaults.copper_text_italic", &m_TextItalic[LAYER_CLASS_COPPER], false ) ); + + m_params.emplace_back( new PARAM( + "defaults.copper_text_upright", &m_TextUpright[LAYER_CLASS_COPPER], true ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.board_outline_line_width", + &m_LineThickness[LAYER_CLASS_EDGES], Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.courtyard_line_width", + &m_LineThickness[LAYER_CLASS_COURTYARD], Millimeter2iu( DEFAULT_SILK_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.fab_line_width", + &m_LineThickness[LAYER_CLASS_FAB], Millimeter2iu( DEFAULT_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.fab_text_size_v", + &m_TextSize[LAYER_CLASS_FAB].y, Millimeter2iu( DEFAULT_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.fab_text_size_h", + &m_TextSize[LAYER_CLASS_FAB].x, Millimeter2iu( DEFAULT_TEXT_SIZE ), + TEXTS_MIN_SIZE, TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.fab_text_thickness", + &m_TextThickness[LAYER_CLASS_FAB], Millimeter2iu( DEFAULT_TEXT_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( + new PARAM( "defaults.fab_text_italic", &m_TextItalic[LAYER_CLASS_FAB], false ) ); + + m_params.emplace_back( + new PARAM( "defaults.fab_text_upright", &m_TextUpright[LAYER_CLASS_FAB], true ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.other_line_width", + &m_LineThickness[LAYER_CLASS_OTHERS], Millimeter2iu( DEFAULT_LINE_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.other_text_size_v", + &m_TextSize[LAYER_CLASS_OTHERS].y, Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, + TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.other_text_size_h", + &m_TextSize[LAYER_CLASS_OTHERS].x, Millimeter2iu( DEFAULT_TEXT_SIZE ), TEXTS_MIN_SIZE, + TEXTS_MAX_SIZE, MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.other_text_thickness", + &m_TextThickness[LAYER_CLASS_OTHERS], Millimeter2iu( DEFAULT_TEXT_WIDTH ), + Millimeter2iu( 0.01 ), Millimeter2iu( 5.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM( + "defaults.other_text_italic", &m_TextItalic[LAYER_CLASS_OTHERS], false ) ); + + m_params.emplace_back( new PARAM( + "defaults.other_text_upright", &m_TextUpright[LAYER_CLASS_OTHERS], true ) ); + + m_params.emplace_back( + new PARAM( "defaults.dimension_units", &m_DimensionUnits, 0, 0, 2 ) ); + + m_params.emplace_back( + new PARAM( "defaults.dimension_precision", &m_DimensionPrecision, 1, 0, 2 ) ); + + m_params.emplace_back( new PARAM( + "defaults.zones.45_degree_only", &m_defaultZoneSettings.m_Zone_45_Only, false ) ); + + m_params.emplace_back( new PARAM_SCALED( "defaults.zones.min_clearance", + &m_defaultZoneSettings.m_ZoneClearance, Mils2iu( ZONE_CLEARANCE_MIL ), + Millimeter2iu( 0.0 ), Millimeter2iu( 25.0 ), MM_PER_IU ) ); + + m_params.emplace_back( new PARAM_LAMBDA( "defaults.pads", + [&]() -> nlohmann::json + { + nlohmann::json ret = + { + { "width", Iu2Millimeter( m_Pad_Master.GetSize().x ) }, + { "height", Iu2Millimeter( m_Pad_Master.GetSize().y ) }, + { "drill", Iu2Millimeter( m_Pad_Master.GetDrillSize().x ) } + }; + + return ret; + }, + [&]( const nlohmann::json& aJson ) + { + if( aJson.contains( "width" ) && aJson.contains( "height" ) + && aJson.contains( "drill" ) ) + { + wxSize sz; + sz.SetWidth( Millimeter2iu( aJson["width"].get() ) ); + sz.SetHeight( Millimeter2iu( aJson["height"].get() ) ); + + m_Pad_Master.SetSize( sz ); + + int drill = Millimeter2iu( aJson["drill"].get() ); + + m_Pad_Master.SetDrillSize( wxSize( drill, drill ) ); + } + }, {} ) ); + + m_params.emplace_back( new PARAM_SCALED( "rules.max_error", &m_MaxError, ARC_HIGH_DEF, + Millimeter2iu( 0.0001 ), Millimeter2iu( 1.0 ), MM_PER_IU ) ); + + m_params.emplace_back( + new PARAM( "zones_use_no_outline", &m_ZoneUseNoOutlineInFill, false ) ); +} + + +BOARD_DESIGN_SETTINGS::~BOARD_DESIGN_SETTINGS() +{ + if( m_parent ) + { + m_parent->ReleaseNestedSettings( this ); + m_parent = nullptr; + } +} + + +SEVERITY BOARD_DESIGN_SETTINGS::severityFromString( const wxString& aSeverity ) +{ + if( aSeverity == wxT( "warning" ) ) + return RPT_SEVERITY_WARNING; + else if( aSeverity == wxT( "ignore" ) ) + return RPT_SEVERITY_IGNORE; + else + return RPT_SEVERITY_ERROR; +} + +wxString BOARD_DESIGN_SETTINGS::severityToString( const SEVERITY& aSeverity ) +{ + if( aSeverity == RPT_SEVERITY_IGNORE ) + return wxT( "ignore" ); + else if( aSeverity == RPT_SEVERITY_WARNING ) + return wxT( "warning" ); + else + return wxT( "error" ); +} + + +bool BOARD_DESIGN_SETTINGS::LoadFromFile( const std::string& aDirectory ) +{ + bool ret = NESTED_SETTINGS::LoadFromFile( aDirectory ); + + // A number of things won't have been translated by the PROJECT_FILE migration because of + // descoped objects required to decode this data. So, it will be in the legacy.pcbnew + // section and needs to be pulled out here + + PROJECT_FILE* project = dynamic_cast( GetParent() ); + + if( !project ) + return ret; + + bool migrated = false; + + DRC_ITEM drc( 0 ); + + auto drcName = + [drc]( int aCode ) -> std::string + { + wxString name = drc.GetErrorText( aCode, false ); + name.Replace( wxT( " " ), wxT( "_" ) ); + return std::string( name.ToUTF8() ); + }; + + std::string bp = "board.design_settings.rule_severities."; + std::string rs = "rule_severities."; + + if( OPT v = + project->Get( PointerFromString( bp + "legacy_no_courtyard_defined" ) ) ) + { + if( *v ) + ( *this )[PointerFromString( rs + drcName( DRCE_MISSING_COURTYARD ) )] = "error"; + else + ( *this )[PointerFromString( rs + drcName( DRCE_MISSING_COURTYARD ) )] = "ignore"; + + project->erase( PointerFromString( bp + "legacy_no_courtyard_defined" ) ); + migrated = true; + } + + if( OPT v = project->Get( PointerFromString( bp + "legacy_ourtyards_overlap" ) ) ) + { + if( *v ) + ( *this )[PointerFromString( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ) )] = "error"; + else + ( *this )[PointerFromString( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ) )] = "ignore"; + + project->erase( PointerFromString( bp + "legacy_ourtyards_overlap" ) ); + migrated = true; + } + + if( project->contains( PointerFromString( "legacy.pcbnew" ) ) ) + { + // DRC Severities + for( int i = DRCE_FIRST; i <= DRCE_LAST; ++i ) + { + std::string key( severityToString( static_cast( i ) ).ToUTF8() ); + + if( OPT v = project->Get( "legacy.pcbnew." + key ) ) + ( *this )[PointerFromString( rs + key )] = *v; + } + + // We are the only place that needs this info, so now it can be deleted + project->at( "legacy" ).erase( "pcbnew" ); + migrated = true; + } + + // Now that we have everything, we need to load again + if( migrated ) + Load(); + + return ret; } @@ -865,12 +683,12 @@ bool BOARD_DESIGN_SETTINGS::Ignore( int aDRCErrorCode ) bool BOARD_DESIGN_SETTINGS::SetCurrentNetClass( const wxString& aNetClassName ) { - NETCLASSPTR netClass = m_NetClasses.Find( aNetClassName ); + NETCLASSPTR netClass = GetNetClasses().Find( aNetClassName ); bool lists_sizes_modified = false; // if not found (should not happen) use the default if( !netClass ) - netClass = m_NetClasses.GetDefault(); + netClass = GetNetClasses().GetDefault(); m_currentNetClassName = netClass->GetName(); @@ -949,7 +767,7 @@ int BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue() { int clearance = GetDefault()->GetClearance(); - for( const std::pair& netclass : m_NetClasses.NetClasses() ) + for( const std::pair& netclass : GetNetClasses().NetClasses() ) clearance = std::max( clearance, netclass.second->GetClearance() ); for( const DRC_RULE* rule : m_DRCRules ) @@ -963,7 +781,7 @@ int BOARD_DESIGN_SETTINGS::GetSmallestClearanceValue() { int clearance = GetDefault()->GetClearance(); - for( const std::pair& netclass : m_NetClasses.NetClasses() ) + for( const std::pair& netclass : GetNetClasses().NetClasses() ) clearance = std::min( clearance, netclass.second->GetClearance() ); return clearance; @@ -972,7 +790,7 @@ int BOARD_DESIGN_SETTINGS::GetSmallestClearanceValue() int BOARD_DESIGN_SETTINGS::GetCurrentMicroViaSize() { - NETCLASSPTR netclass = m_NetClasses.Find( m_currentNetClassName ); + NETCLASSPTR netclass = GetNetClasses().Find( m_currentNetClassName ); return netclass->GetuViaDiameter(); } @@ -980,7 +798,7 @@ int BOARD_DESIGN_SETTINGS::GetCurrentMicroViaSize() int BOARD_DESIGN_SETTINGS::GetCurrentMicroViaDrill() { - NETCLASSPTR netclass = m_NetClasses.Find( m_currentNetClassName ); + NETCLASSPTR netclass = GetNetClasses().Find( m_currentNetClassName ); return netclass->GetuViaDrill(); } @@ -1032,28 +850,6 @@ void BOARD_DESIGN_SETTINGS::SetCopperEdgeClearance( int aDistance ) } -void BOARD_DESIGN_SETTINGS::SetVisibleAlls() -{ - SetVisibleLayers( LSET().set() ); - m_visibleElements = -1; -} - - -void BOARD_DESIGN_SETTINGS::SetLayerVisibility( PCB_LAYER_ID aLayer, bool aNewState ) -{ - m_visibleLayers.set( aLayer, aNewState && IsLayerEnabled( aLayer )); -} - - -void BOARD_DESIGN_SETTINGS::SetElementVisibility( GAL_LAYER_ID aElementCategory, bool aNewState ) -{ - if( aNewState ) - m_visibleElements |= 1 << GAL_LAYER_INDEX( aElementCategory ); - else - m_visibleElements &= ~( 1 << GAL_LAYER_INDEX( aElementCategory ) ); -} - - void BOARD_DESIGN_SETTINGS::SetCopperLayerCount( int aNewLayerCount ) { m_copperLayerCount = aNewLayerCount; @@ -1073,9 +869,6 @@ void BOARD_DESIGN_SETTINGS::SetEnabledLayers( LSET aMask ) m_enabledLayers = aMask; - // A disabled layer cannot be visible - m_visibleLayers &= aMask; - // update m_CopperLayerCount to ensure its consistency with m_EnabledLayers m_copperLayerCount = ( aMask & LSET::AllCuMask() ).count(); } diff --git a/pcbnew/board_item_container.h b/pcbnew/board_item_container.h index 35ac895eca..9f4b7f4ea5 100644 --- a/pcbnew/board_item_container.h +++ b/pcbnew/board_item_container.h @@ -68,26 +68,26 @@ public: Remove( aItem ); delete aItem; } - - /** - * @brief Fetch the zone settings for this container - */ - const ZONE_SETTINGS& GetZoneSettings() const - { - return m_zoneSettings; - } - - /** - * @brief Set the zone settings for this container - * @param aSettings new Zone settings for this container - */ - void SetZoneSettings( const ZONE_SETTINGS& aSettings ) - { - m_zoneSettings = aSettings; - } - -private: - ZONE_SETTINGS m_zoneSettings; + + /** + * @brief Fetch the zone settings for this container + */ + virtual const ZONE_SETTINGS& GetZoneSettings() const + { + return m_zoneSettings; + } + + /** + * @brief Set the zone settings for this container + * @param aSettings new Zone settings for this container + */ + virtual void SetZoneSettings( const ZONE_SETTINGS& aSettings ) + { + m_zoneSettings = aSettings; + } + +private: + ZONE_SETTINGS m_zoneSettings; }; #endif /* BOARD_ITEM_CONTAINER_H */ diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 1e9022688b..001401a5a5 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -41,6 +41,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -81,23 +85,19 @@ DELETED_BOARD_ITEM* g_DeletedItem = nullptr; */ wxPoint BOARD_ITEM::ZeroOffset( 0, 0 ); -// Dummy settings used to initialize the board. -// This is needed because some APIs that make use of BOARD without the context of a frame or -// application, and so the BOARD needs to store a valid pointer to a PCBNEW_SETTINGS even if -// one hasn't been provided by the application. -static PCBNEW_SETTINGS dummyGeneralSettings; BOARD::BOARD() : BOARD_ITEM_CONTAINER( (BOARD_ITEM*) NULL, PCB_T ), m_paper( PAGE_INFO::A4 ), + m_project( nullptr ), + m_designSettings( new BOARD_DESIGN_SETTINGS( nullptr, "board.design_settings" ) ), m_NetInfo( this ), - m_project( nullptr ) + m_LegacyDesignSettingsLoaded( false ), + m_LegacyNetclassesLoaded( false ) { // we have not loaded a board yet, assume latest until then. m_fileFormatVersionAtLoad = LEGACY_BOARD_FILE_VERSION; - m_generalSettings = &dummyGeneralSettings; - m_CurrentZoneContour = NULL; // This ZONE_CONTAINER handle the // zone contour currently in progress @@ -113,19 +113,25 @@ BOARD::BOARD() : m_Layer[layer].m_type = LT_UNDEFINED; } + BOARD_DESIGN_SETTINGS& bds = GetDesignSettings(); + // Initialize default netclass. - NETCLASS* defaultClass = m_designSettings.GetDefault(); + NETCLASS* defaultClass = bds.GetDefault(); defaultClass->SetDescription( _( "This is the default net class." ) ); - m_designSettings.SetCurrentNetClass( defaultClass->GetName() ); + bds.SetCurrentNetClass( defaultClass->GetName() ); // Set sensible initial values for custom track width & via size - m_designSettings.UseCustomTrackViaSize( false ); - m_designSettings.SetCustomTrackWidth( m_designSettings.GetCurrentTrackWidth() ); - m_designSettings.SetCustomViaSize( m_designSettings.GetCurrentViaSize() ); - m_designSettings.SetCustomViaDrill( m_designSettings.GetCurrentViaDrill() ); + bds.UseCustomTrackViaSize( false ); + bds.SetCustomTrackWidth( bds.GetCurrentTrackWidth() ); + bds.SetCustomViaSize( bds.GetCurrentViaSize() ); + bds.SetCustomViaDrill( bds.GetCurrentViaDrill() ); // Initialize ratsnest m_connectivity.reset( new CONNECTIVITY_DATA() ); + + // Set flag bits on these that will only be cleared if these are loaded from a legacy file + m_LegacyVisibleLayers.reset().set( Rescue ); + m_LegacyVisibleItems.reset().set( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) ); } @@ -170,6 +176,54 @@ void BOARD::BuildConnectivity() } +void BOARD::SetProject( PROJECT* aProject ) +{ + m_project = aProject; + + if( aProject ) + { + PROJECT_FILE& project = aProject->GetProjectFile(); + + // Link the design settings object to the project file + project.m_BoardSettings = &GetDesignSettings(); + + // Set parent, which also will load the values from JSON stored in the project + project.m_BoardSettings->SetParent( &project ); + + // The netclasses pointer will be pointing to the internal netclasses list at this point. If + // it has anything other than the default net, this means we loaded some netclasses from a + // board saved in legacy format where the netclass info is included. Move this info to the + // netclasses stored in the project. + + NETCLASSES& local = GetDesignSettings().GetNetClasses(); + + if( m_LegacyNetclassesLoaded ) + project.NetSettings().m_NetClasses = local; + + GetDesignSettings().SetNetClasses( &project.NetSettings().m_NetClasses ); + } +} + + +void BOARD::ClearProject() +{ + if( !m_project ) + return; + + PROJECT_FILE& project = m_project->GetProjectFile(); + + // Owned by the BOARD + if( project.m_BoardSettings ) + { + project.ReleaseNestedSettings( project.m_BoardSettings ); + project.m_BoardSettings = nullptr; + } + + GetDesignSettings().SetParent( nullptr ); + m_project = nullptr; +} + + const wxPoint BOARD::GetPosition() const { return ZeroOffset; @@ -392,50 +446,56 @@ LAYER_T LAYER::ParseType( const char* aType ) int BOARD::GetCopperLayerCount() const { - return m_designSettings.GetCopperLayerCount(); + return GetDesignSettings().GetCopperLayerCount(); } void BOARD::SetCopperLayerCount( int aCount ) { - m_designSettings.SetCopperLayerCount( aCount ); + GetDesignSettings().SetCopperLayerCount( aCount ); } LSET BOARD::GetEnabledLayers() const { - return m_designSettings.GetEnabledLayers(); + return GetDesignSettings().GetEnabledLayers(); +} + + +bool BOARD::IsLayerVisible( PCB_LAYER_ID aLayer ) const +{ + // If there is no project, assume layer is visible always + return GetDesignSettings().IsLayerEnabled( aLayer ) + && ( !m_project || m_project->GetLocalSettings().m_VisibleLayers[aLayer] ); } LSET BOARD::GetVisibleLayers() const { - return m_designSettings.GetVisibleLayers(); + return m_project ? m_project->GetLocalSettings().m_VisibleLayers : LSET::AllLayersMask(); } void BOARD::SetEnabledLayers( LSET aLayerSet ) { - m_designSettings.SetEnabledLayers( aLayerSet ); + GetDesignSettings().SetEnabledLayers( aLayerSet ); } void BOARD::SetVisibleLayers( LSET aLayerSet ) { - m_designSettings.SetVisibleLayers( aLayerSet ); + if( m_project ) + m_project->GetLocalSettings().m_VisibleLayers = aLayerSet; } -void BOARD::SetVisibleElements( int aMask ) +void BOARD::SetVisibleElements( const GAL_SET& aSet ) { // Call SetElementVisibility for each item // to ensure specific calculations that can be needed by some items, // just changing the visibility flags could be not sufficient. - for( GAL_LAYER_ID ii = GAL_LAYER_ID_START; ii < GAL_LAYER_ID_BITMASK_END; ++ii ) - { - int item_mask = 1 << GAL_LAYER_INDEX( ii ); - SetElementVisibility( ii, aMask & item_mask ); - } + for( size_t i = 0; i < aSet.size(); i++ ) + SetElementVisibility( GAL_LAYER_ID_START + static_cast( i ), aSet[i] ); } @@ -450,21 +510,22 @@ void BOARD::SetVisibleAlls() } -int BOARD::GetVisibleElements() const +GAL_SET BOARD::GetVisibleElements() const { - return m_designSettings.GetVisibleElements(); + return m_project ? m_project->GetLocalSettings().m_VisibleItems : GAL_SET().set(); } bool BOARD::IsElementVisible( GAL_LAYER_ID aLayer ) const { - return m_designSettings.IsElementVisible( aLayer ); + return !m_project || m_project->GetLocalSettings().m_VisibleItems[aLayer - GAL_LAYER_ID_START]; } void BOARD::SetElementVisibility( GAL_LAYER_ID aLayer, bool isEnabled ) { - m_designSettings.SetElementVisibility( aLayer, isEnabled ); + if( m_project ) + m_project->GetLocalSettings().m_VisibleItems.set( aLayer - GAL_LAYER_ID_START, isEnabled ); switch( aLayer ) { @@ -1216,6 +1277,86 @@ int BOARD::SortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount ) } +void BOARD::SynchronizeNetsAndNetClasses() +{ + NETCLASSES& netClasses = GetDesignSettings().GetNetClasses(); + NETCLASSPTR defaultNetClass = netClasses.GetDefault(); + + // set all NETs to the default NETCLASS, then later override some + // as we go through the NETCLASSes. + + for( NETINFO_LIST::iterator net( m_NetInfo.begin() ), netEnd( m_NetInfo.end() ); + net != netEnd; ++net ) + { + net->SetClass( defaultNetClass ); + } + + // Add netclass name and pointer to nets. If a net is in more than one netclass, + // set the net's name and pointer to only the first netclass. Subsequent + // and therefore bogus netclass memberships will be deleted in logic below this loop. + for( NETCLASSES::iterator clazz = netClasses.begin(); clazz != netClasses.end(); ++clazz ) + { + NETCLASSPTR netclass = clazz->second; + + for( NETCLASS::const_iterator member = netclass->begin(); member != netclass->end(); ++member ) + { + const wxString& netname = *member; + + // although this overall function seems to be adequately fast, + // FindNet( wxString ) uses now a fast binary search and is fast + // event for large net lists + NETINFO_ITEM* net = FindNet( netname ); + + if( net && net->GetClassName() == NETCLASS::Default ) + { + net->SetClass( netclass ); + } + } + } + + // Finally, make sure that every NET is in a NETCLASS, even if that + // means the Default NETCLASS. And make sure that all NETCLASSes do not + // contain netnames that do not exist, by deleting all netnames from + // every netclass and re-adding them. + + for( NETCLASSES::iterator clazz = netClasses.begin(); clazz != netClasses.end(); ++clazz ) + { + NETCLASSPTR netclass = clazz->second; + + netclass->Clear(); + } + + defaultNetClass->Clear(); + + for( NETINFO_LIST::iterator net( m_NetInfo.begin() ), netEnd( m_NetInfo.end() ); + net != netEnd; ++net ) + { + const wxString& classname = net->GetClassName(); + + // because of the std:map<> this should be fast, and because of + // prior logic, netclass should not be NULL. + NETCLASSPTR netclass = netClasses.Find( classname ); + + wxASSERT( netclass ); + + netclass->Add( net->GetNetname() ); + } + + BOARD_DESIGN_SETTINGS& bds = GetDesignSettings(); + + // Set initial values for custom track width & via size to match the default netclass settings + bds.UseCustomTrackViaSize( false ); + bds.SetCustomTrackWidth( defaultNetClass->GetTrackWidth() ); + bds.SetCustomViaSize( defaultNetClass->GetViaDiameter() ); + bds.SetCustomViaDrill( defaultNetClass->GetViaDrill() ); + bds.SetCustomDiffPairWidth( defaultNetClass->GetDiffPairWidth() ); + bds.SetCustomDiffPairGap( defaultNetClass->GetDiffPairGap() ); + bds.SetCustomDiffPairViaGap( defaultNetClass->GetDiffPairViaGap() ); + + InvokeListeners( &BOARD_LISTENER::OnBoardNetSettingsChanged, *this ); +} + + int BOARD::SetAreasNetCodesFromNetNames() { int error_count = 0; diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 917f5f2996..9b07ef4138 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -42,7 +42,6 @@ class PCB_BASE_FRAME; class PCB_EDIT_FRAME; -class PCBNEW_SETTINGS; class PICKED_ITEMS_LIST; class BOARD; class ZONE_CONTAINER; @@ -211,13 +210,24 @@ private: std::shared_ptr m_connectivity; - BOARD_DESIGN_SETTINGS m_designSettings; - PCBNEW_SETTINGS* m_generalSettings; // reference only; I have no ownership PAGE_INFO m_paper; TITLE_BLOCK m_titles; // text in lower right of screen and plots PCB_PLOT_PARAMS m_plotOptions; + PROJECT* m_project; // project this board is a part of + + /** + * All of the board design settings are stored as a JSON object inside the project file. The + * object itself is located here because the alternative is to require a valid project be + * passed in when constructing a BOARD, since things in the BOARD constructor rely on access + * to the BOARD_DESIGN_SETTINGS object. + * + * A reference to this object is set up in the PROJECT_FILE for the PROJECT this board is + * part of, so that the JSON load/store operations work. This link is established when + * boards are loaded from disk. + */ + std::unique_ptr m_designSettings; + NETINFO_LIST m_NetInfo; // net info list (name, design constraints .. - PROJECT* m_project; // project this board is a part of (if any) std::vector m_listeners; @@ -282,6 +292,16 @@ public: /// zone contour currently in progress ZONE_CONTAINER* m_CurrentZoneContour; + /// Visibility settings stored in board prior to 6.0, only used for loading legacy files + LSET m_LegacyVisibleLayers; + GAL_SET m_LegacyVisibleItems; + + /// True if the legacy board design settings were loaded from a file + bool m_LegacyDesignSettingsLoaded; + + /// True if netclasses were loaded from the file + bool m_LegacyNetclassesLoaded; + BOARD(); ~BOARD(); @@ -354,7 +374,15 @@ public: void DeleteZONEOutlines(); PROJECT* GetProject() const { return m_project; } - void SetProject( PROJECT* aProject ) { m_project = aProject; } + + /** + * Links a board to a given project. Should be called immediately after loading board in + * order for everything to work + * @param aProject is a loaded project to link to + */ + void SetProject( PROJECT* aProject ); + + void ClearProject(); /** * Function ResetNetHighLight @@ -434,7 +462,7 @@ public: */ bool IsLayerEnabled( PCB_LAYER_ID aLayer ) const { - return m_designSettings.IsLayerEnabled( aLayer ); + return GetDesignSettings().IsLayerEnabled( aLayer ); } /** @@ -444,10 +472,7 @@ public: * @param aLayer = The layer to be tested * @return bool - true if the layer is visible. */ - bool IsLayerVisible( PCB_LAYER_ID aLayer ) const - { - return m_designSettings.IsLayerVisible( aLayer ); - } + bool IsLayerVisible( PCB_LAYER_ID aLayer ) const; /** * Function GetVisibleLayers @@ -469,13 +494,11 @@ public: // are not stored in the bitmap. /** - * Function GetVisibleElements - * is a proxy function that calls the correspondent function in m_BoardSettings - * returns a bit-mask of all the element categories that are visible - * @return int - the visible element bitmap or-ed from enum GAL_LAYER_ID + * Returns a set of all the element categories that are visible + * @return the set of visible GAL layers * @see enum GAL_LAYER_ID */ - int GetVisibleElements() const; + GAL_SET GetVisibleElements() const; /** * Function SetVisibleElements @@ -484,7 +507,7 @@ public: * @param aMask = The new bit-mask of visible element bitmap or-ed from enum GAL_LAYER_ID * @see enum GAL_LAYER_ID */ - void SetVisibleElements( int aMask ); + void SetVisibleElements( const GAL_SET& aMask ); /** * Function SetVisibleAlls @@ -528,16 +551,22 @@ public: BOARD_DESIGN_SETTINGS& GetDesignSettings() const { // remove const-ness with cast. TODO(snh): Make GetDesignSettings const - return const_cast( m_designSettings ); + // NOTE(JE) If we want this to be const, it's going to have to involve making BOARD and + // everything else that relies on BOARD_DESIGN_SETTINGS aware of the PROJECT so that it + // can be retrieved from there. This will also currently require constructing BOARD with + // a valid PROJECT passed in to the ctor. + + return const_cast( *m_designSettings.get() ); } - /** - * Function SetDesignSettings - * @param aDesignSettings the new BOARD_DESIGN_SETTINGS to use - */ - void SetDesignSettings( const BOARD_DESIGN_SETTINGS& aDesignSettings ) + const ZONE_SETTINGS& GetZoneSettings() const override { - m_designSettings = aDesignSettings; + return GetDesignSettings().GetDefaultZoneSettings(); + } + + void SetZoneSettings( const ZONE_SETTINGS& aSettings ) override + { + GetDesignSettings().SetDefaultZoneSettings( aSettings ); } const PAGE_INFO& GetPageSettings() const { return m_paper; } @@ -551,13 +580,6 @@ public: wxString GetSelectMenuText( EDA_UNITS aUnits ) const override; - const PCBNEW_SETTINGS& GeneralSettings() const { return *m_generalSettings; } - - void SetGeneralSettings( PCBNEW_SETTINGS* aSettings ) - { - m_generalSettings = aSettings; - } - /** * Function GetBoardPolygonOutlines * Extracts the board outlines and build a closed polygon diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 4d0186a7b7..e131bf5acf 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -487,37 +487,6 @@ void D_PAD::MirrorXPrimitives( int aX ) } -void D_PAD::AppendConfigs( std::vector* aResult ) -{ - // Parameters stored in config are only significant parameters - // for a template. - // So not all parameters are stored, just few. - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "PadDrill" ), - &m_Drill.x, - Millimeter2iu( 0.6 ), - Millimeter2iu( 0.1 ), Millimeter2iu( 10.0 ), - NULL, MM_PER_IU ) ); - - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "PadDrillOvalY" ), - &m_Drill.y, - Millimeter2iu( 0.6 ), - Millimeter2iu( 0.1 ), Millimeter2iu( 10.0 ), - NULL, MM_PER_IU ) ); - - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "PadSizeH" ), - &m_Size.x, - Millimeter2iu( 1.4 ), - Millimeter2iu( 0.1 ), Millimeter2iu( 20.0 ), - NULL, MM_PER_IU ) ); - - aResult->push_back( new PARAM_CFG_INT_WITH_SCALE( wxT( "PadSizeV" ), - &m_Size.y, - Millimeter2iu( 1.4 ), - Millimeter2iu( 0.1 ), Millimeter2iu( 20.0 ), - NULL, MM_PER_IU ) ); -} - - // Returns the position of the pad. wxPoint D_PAD::ShapePos() const { diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index f04dbb9a09..c34f8e2941 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -551,14 +551,6 @@ public: */ wxString ShowPadAttr() const; - /** - * Function AppendConfigs - * appends to @a aResult the configuration setting accessors which will later - * allow reading or writing of configuration file information directly into - * this object. - */ - void AppendConfigs( std::vector* aResult ); - EDA_ITEM* Clone() const override; /** diff --git a/pcbnew/dialogs/dialog_board_setup.cpp b/pcbnew/dialogs/dialog_board_setup.cpp index abb5f4b560..56cd7cf1ab 100644 --- a/pcbnew/dialogs/dialog_board_setup.cpp +++ b/pcbnew/dialogs/dialog_board_setup.cpp @@ -24,18 +24,24 @@ #include #include #include <../board_stackup_manager/panel_board_stackup.h> +#include #include #include #include #include +#include #include #include +#include +#include +#include +#include #include "dialog_board_setup.h" #include "panel_setup_rules.h" DIALOG_BOARD_SETUP::DIALOG_BOARD_SETUP( PCB_EDIT_FRAME* aFrame ) : - PAGED_DIALOG( aFrame, _( "Board Setup" ), _( "Import Settings from Another Project..." ) ), + PAGED_DIALOG( aFrame, _( "Board Setup" ), _( "Import Settings from Another Board..." ) ), m_frame( aFrame ) { m_layers = new PANEL_SETUP_LAYERS( this, aFrame ); @@ -127,30 +133,59 @@ void DIALOG_BOARD_SETUP::OnAuxiliaryAction( wxCommandEvent& event ) if( importDlg.ShowModal() == wxID_CANCEL ) return; - wxConfigBase* cfg = new wxFileConfig( wxEmptyString, wxEmptyString, importDlg.GetFilePath() ); + wxFileName boardFn( importDlg.GetFilePath() ); + wxFileName projectFn( boardFn ); - // We do not want expansion of env var values when reading our project config file - cfg->SetExpandEnvVars( false ); - cfg->SetPath( wxCONFIG_PATH_SEPARATOR ); + projectFn.SetExt( ProjectFileExtension ); - BOARD* dummyBoard = new BOARD(); - std::vector designSettingsConfig; + if( !m_frame->GetSettingsManager()->LoadProject( projectFn.GetFullPath(), false ) ) + { + wxString msg = wxString::Format( _( "Error importing settings from borad:\n" + "Associated project file %s could not be loaded" ), + projectFn.GetFullPath() ); + DisplayErrorMessage( this, msg ); - dummyBoard->GetDesignSettings().AppendConfigs( dummyBoard, &designSettingsConfig ); - wxConfigLoadParams( cfg, designSettingsConfig, GROUP_PCB ); + return; + } + + PROJECT* otherPrj = m_frame->GetSettingsManager()->GetProject( projectFn.GetFullPath() ); + + PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) ); + + BOARD* otherBoard = new BOARD(); + + try + { + otherBoard = pi->Load( boardFn.GetFullPath(), nullptr, nullptr ); + } + catch( const IO_ERROR& ioe ) + { + if( ioe.Problem() != wxT( "CANCEL" ) ) + { + wxString msg = + wxString::Format( _( "Error loading board file:\n%s" ), boardFn.GetFullPath() ); + DisplayErrorMessage( this, msg, ioe.What() ); + } + + m_frame->GetSettingsManager()->UnloadProject( otherPrj, false ); + + return; + } + + otherBoard->SetProject( otherPrj ); if( importDlg.m_LayersOpt->GetValue() ) - m_layers->ImportSettingsFrom( dummyBoard ); + m_layers->ImportSettingsFrom( otherBoard ); if( importDlg.m_TextAndGraphicsOpt->GetValue() ) - m_textAndGraphics->ImportSettingsFrom( dummyBoard ); + m_textAndGraphics->ImportSettingsFrom( otherBoard ); if( importDlg.m_ConstraintsOpt->GetValue() ) - m_constraints->ImportSettingsFrom( dummyBoard ); + m_constraints->ImportSettingsFrom( otherBoard ); if( importDlg.m_NetclassesOpt->GetValue() ) - m_netclasses->ImportSettingsFrom( dummyBoard ); + m_netclasses->ImportSettingsFrom( otherBoard ); if( importDlg.m_TracksAndViasOpt->GetValue() ) - m_tracksAndVias->ImportSettingsFrom( dummyBoard ); + m_tracksAndVias->ImportSettingsFrom( otherBoard ); if( importDlg.m_MaskAndPasteOpt->GetValue() ) - m_maskAndPaste->ImportSettingsFrom( dummyBoard ); + m_maskAndPaste->ImportSettingsFrom( otherBoard ); // If layers options are imported, import also the stackup // layers options and stackup are linked, so they cannot be imported @@ -159,12 +194,16 @@ void DIALOG_BOARD_SETUP::OnAuxiliaryAction( wxCommandEvent& event ) // Note also currently only the list of enabled layers can be imported, because // we import settings from a .pro project file, not the settings inside // a board, and info only living in the board is not imported. + // TODO: Add import of physical settings now that we are actually loading the board here if( importDlg.m_LayersOpt->GetValue() ) - m_physicalStackup->ImportSettingsFrom( dummyBoard ); + m_physicalStackup->ImportSettingsFrom( otherBoard ); if( importDlg.m_SeveritiesOpt->GetValue() ) - m_severities->ImportSettingsFrom( dummyBoard->GetDesignSettings().m_DRCSeverities ); + m_severities->ImportSettingsFrom( otherBoard->GetDesignSettings().m_DRCSeverities ); - delete dummyBoard; - delete cfg; + otherBoard->ClearProject(); + + m_frame->GetSettingsManager()->UnloadProject( otherPrj, false ); + + delete otherBoard; } diff --git a/pcbnew/dialogs/dialog_export_idf.cpp b/pcbnew/dialogs/dialog_export_idf.cpp index 30fc89dc61..9b1f4d9ada 100644 --- a/pcbnew/dialogs/dialog_export_idf.cpp +++ b/pcbnew/dialogs/dialog_export_idf.cpp @@ -30,6 +30,7 @@ #include #include #include +#include // LAST_PATH_TYPE #include diff --git a/pcbnew/dialogs/dialog_export_step.cpp b/pcbnew/dialogs/dialog_export_step.cpp index 65995e271b..9a5ede7273 100644 --- a/pcbnew/dialogs/dialog_export_step.cpp +++ b/pcbnew/dialogs/dialog_export_step.cpp @@ -33,6 +33,7 @@ #include "class_board.h" #include "dialog_export_step_base.h" #include +#include // LAST_PATH_TYPE #include #include diff --git a/pcbnew/dialogs/dialog_export_vrml.cpp b/pcbnew/dialogs/dialog_export_vrml.cpp index d3b9147d75..9a2c431fe5 100644 --- a/pcbnew/dialogs/dialog_export_vrml.cpp +++ b/pcbnew/dialogs/dialog_export_vrml.cpp @@ -35,6 +35,7 @@ #include #include #include +#include // LAST_PATH_TYPE /* the dialog to create VRML files, derived from DIALOG_EXPORT_3DFILE_BASE, diff --git a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp index 599c5284c1..bfb0e5e2e8 100644 --- a/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp +++ b/pcbnew/dialogs/dialog_global_edit_tracks_and_vias.cpp @@ -170,7 +170,7 @@ void DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::buildFilterLists() // Populate the netclass filter list with netclass names wxArrayString netclassNames; - NETCLASSES& netclasses = m_brd->GetDesignSettings().m_NetClasses; + NETCLASSES& netclasses = m_brd->GetDesignSettings().GetNetClasses(); netclassNames.push_back( netclasses.GetDefaultPtr()->GetName() ); @@ -200,7 +200,7 @@ void DIALOG_GLOBAL_EDIT_TRACKS_AND_VIAS::buildNetclassesGrid() m_netclassGrid->SetCellValue( 0, GRID_uVIASIZE, _( "uVia Size" ) ); m_netclassGrid->SetCellValue( 0, GRID_uVIADRILL, _( "uVia Drill" ) ); - NETCLASSES& netclasses = m_brd->GetDesignSettings().m_NetClasses; + NETCLASSES& netclasses = m_brd->GetDesignSettings().GetNetClasses(); NETCLASS* defaultNetclass = m_brd->GetDesignSettings().GetDefault(); m_netclassGrid->AppendRows( netclasses.GetCount() + 1 ); diff --git a/pcbnew/dialogs/dialog_import_settings.cpp b/pcbnew/dialogs/dialog_import_settings.cpp index 6d7ec1a274..3cc2bbe9fd 100644 --- a/pcbnew/dialogs/dialog_import_settings.cpp +++ b/pcbnew/dialogs/dialog_import_settings.cpp @@ -59,7 +59,7 @@ void DIALOG_IMPORT_SETTINGS::OnBrowseClicked( wxCommandEvent& event ) fn.SetExt( LegacyProjectFileExtension ); wxFileDialog dlg( this, _( "Import Settings From" ), fn.GetPath(), fn.GetFullName(), - ProjectFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR ); + PcbFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR ); if( dlg.ShowModal() == wxID_OK ) m_filePathCtrl->SetValue( dlg.GetPath() ); diff --git a/pcbnew/dialogs/dialog_netlist.cpp b/pcbnew/dialogs/dialog_netlist.cpp index e618106177..39a534e1ce 100644 --- a/pcbnew/dialogs/dialog_netlist.cpp +++ b/pcbnew/dialogs/dialog_netlist.cpp @@ -41,6 +41,7 @@ #include #include #include +#include // LAST_PATH_TYPE #include diff --git a/pcbnew/dialogs/panel_modedit_defaults.cpp b/pcbnew/dialogs/panel_modedit_defaults.cpp index 1c826ccb3c..9274f3de90 100644 --- a/pcbnew/dialogs/panel_modedit_defaults.cpp +++ b/pcbnew/dialogs/panel_modedit_defaults.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -361,7 +362,8 @@ bool PANEL_MODEDIT_DEFAULTS::TransferDataFromWindow() m_brdSettings.m_DefaultFPTextItems.emplace_back( text, visible, layer ); } - m_frame->SetDesignSettings( m_brdSettings ); + if( FOOTPRINT_EDITOR_SETTINGS* cfg = m_frame->GetSettings() ) + cfg->m_DesignSettings = m_brdSettings; return true; } diff --git a/pcbnew/dialogs/panel_setup_netclasses.cpp b/pcbnew/dialogs/panel_setup_netclasses.cpp index 10fa75a87f..79d3c82b22 100644 --- a/pcbnew/dialogs/panel_setup_netclasses.cpp +++ b/pcbnew/dialogs/panel_setup_netclasses.cpp @@ -152,8 +152,8 @@ static void netclassToGridRow( EDA_UNITS aUnits, wxGrid* aGrid, int aRow, const bool PANEL_SETUP_NETCLASSES::TransferDataToWindow() { - NETCLASSES& netclasses = m_BrdSettings->m_NetClasses; - NETCLASSPTR netclass = netclasses.GetDefault(); + NETCLASSES& netclasses = m_BrdSettings->GetNetClasses(); + NETCLASSPTR netclass = netclasses.GetDefault(); if( m_netclassGrid->GetNumberRows() ) m_netclassGrid->DeleteRows( 0, m_netclassGrid->GetNumberRows() ); @@ -250,7 +250,7 @@ bool PANEL_SETUP_NETCLASSES::TransferDataFromWindow() if( !validateData() ) return false; - NETCLASSES& netclasses = m_BrdSettings->m_NetClasses; + NETCLASSES& netclasses = m_BrdSettings->GetNetClasses(); // Remove all netclasses from board. We'll copy new list after netclasses.Clear(); @@ -263,7 +263,7 @@ bool PANEL_SETUP_NETCLASSES::TransferDataFromWindow() { NETCLASSPTR nc = std::make_shared( m_netclassGrid->GetCellValue( row, GRID_NAME ) ); - if( m_BrdSettings->m_NetClasses.Add( nc ) ) + if( netclasses.Add( nc ) ) gridRowToNetclass( m_Frame->GetUserUnits(), m_netclassGrid, row, nc ); } diff --git a/pcbnew/drc/drc_netclass_tester.cpp b/pcbnew/drc/drc_netclass_tester.cpp index 6d2ad68078..e3f5eacfb3 100644 --- a/pcbnew/drc/drc_netclass_tester.cpp +++ b/pcbnew/drc/drc_netclass_tester.cpp @@ -39,7 +39,7 @@ bool DRC_NETCLASS_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) m_board = &aBoard; bool success = true; - NETCLASSES& netclasses = m_board->GetDesignSettings().m_NetClasses; + NETCLASSES& netclasses = m_board->GetDesignSettings().GetNetClasses(); success &= checkNetClass( netclasses.GetDefault() ); diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp index 7ac7de2697..f4b92b90df 100644 --- a/pcbnew/drc/drc_rule_parser.cpp +++ b/pcbnew/drc/drc_rule_parser.cpp @@ -132,7 +132,7 @@ void DRC_RULES_PARSER::Parse( std::vector& aSelectors, DRC_SELECTOR* DRC_RULES_PARSER::parseDRC_SELECTOR( wxString* aRuleName ) { - NETCLASSES& netclasses = m_board->GetDesignSettings().m_NetClasses; + NETCLASSES& netclasses = m_board->GetDesignSettings().GetNetClasses(); DRC_SELECTOR* selector = new DRC_SELECTOR(); T token; diff --git a/pcbnew/exporters/export_gencad.cpp b/pcbnew/exporters/export_gencad.cpp index 63e9a0cfc7..66a6d3eee1 100644 --- a/pcbnew/exporters/export_gencad.cpp +++ b/pcbnew/exporters/export_gencad.cpp @@ -45,6 +45,7 @@ #include #include #include +#include // LAST_PATH_TYPE #include static bool CreateHeaderInfoData( FILE* aFile, PCB_EDIT_FRAME* frame ); diff --git a/pcbnew/exporters/gerber_jobfile_writer.cpp b/pcbnew/exporters/gerber_jobfile_writer.cpp index fb925f066b..d299f80e3a 100644 --- a/pcbnew/exporters/gerber_jobfile_writer.cpp +++ b/pcbnew/exporters/gerber_jobfile_writer.cpp @@ -452,7 +452,7 @@ void GERBER_JOBFILE_WRITER::addJSONDesignRules() bool hasInnerLayers = m_pcb->GetCopperLayerCount() > 2; // Search a smaller clearance in other net classes, if any. - for( const std::pair& entry : dsnSettings.m_NetClasses ) + for( const std::pair& entry : dsnSettings.GetNetClasses() ) minclearanceOuter = std::min( minclearanceOuter, entry.second->GetClearance() ); // job file knows different clearance types. diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index b33b49c05a..f220d55dff 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -52,6 +52,8 @@ #include #include +#include +#include //#define USE_INSTRUMENTATION 1 @@ -305,14 +307,19 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id ) return false; } - if( !Clear_Pcb( false ) ) - return false; + GetSettingsManager()->SaveProject( GetSettingsManager()->Prj().GetProjectFullName() ); + GetBoard()->ClearProject(); wxFileName fn( wxStandardPaths::Get().GetDocumentsDir(), wxT( "noname" ), - LegacyProjectFileExtension ); + ProjectFileExtension ); GetSettingsManager()->LoadProject( fn.GetFullPath() ); + LoadProjectSettings(); + + if( !Clear_Pcb( false ) ) + return false; + onBoardLoaded(); OnModify(); @@ -478,7 +485,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in ReleaseFile(); wxFileName pro = fullFileName; - pro.SetExt( LegacyProjectFileExtension ); + pro.SetExt( ProjectFileExtension ); bool is_new = !wxFileName::IsFileReadable( fullFileName ); @@ -494,7 +501,11 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in wxWindowUpdateLocker no_update( m_Layers ); // Avoid flicker when rebuilding m_Layers - Clear_Pcb( false ); // pass false since we prompted above for a modified board + // Unlink the old project if needed + GetBoard()->ClearProject(); + + // No save prompt (we already prompted above), and only reset to a new blank board if new + Clear_Pcb( false, !is_new ); IO_MGR::PCB_FILE_T pluginType = plugin_type( fullFileName, aCtl ); @@ -502,8 +513,7 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in if( !converted ) { - // PROJECT::SetProjectFullName() is an impactful function. It should only be - // called under carefully considered circumstances. + // Loading a project should only be done under carefully considered circumstances. // The calling code should know not to ask me here to change projects unless // it knows what consequences that will have on other KIFACEs running and using @@ -560,40 +570,30 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in DisplayErrorMessage( this, msg, ioe.What() ); } + // We didn't create a new blank board above, so do that now + Clear_Pcb( false ); + return false; } - BOARD_DESIGN_SETTINGS& bds = loadedBoard->m_designSettings; + SetBoard( loadedBoard ); - if( bds.m_CopperEdgeClearance == Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ) ) + // On save; design settings will be removed from the board + if( loadedBoard->m_LegacyDesignSettingsLoaded ) + loadedBoard->SetModified(); + + // Move legacy view settings to local project settings + if( !loadedBoard->m_LegacyVisibleLayers.test( Rescue ) ) { - // 5.1 boards stored some settings in the config so as not to bump the file version. - // These will have been loaded into the config-initialized board, so we copy them - // from there. - BOARD_DESIGN_SETTINGS& configBds = GetBoard()->GetDesignSettings(); - - bds.m_DRCSeverities = configBds.m_DRCSeverities; - bds.m_HoleToHoleMin = configBds.m_HoleToHoleMin; - bds.m_LineThickness[LAYER_CLASS_OTHERS] = configBds.m_LineThickness[LAYER_CLASS_OTHERS]; - bds.m_TextSize[LAYER_CLASS_OTHERS] = configBds.m_TextSize[LAYER_CLASS_OTHERS]; - bds.m_TextThickness[LAYER_CLASS_OTHERS] = configBds.m_TextThickness[LAYER_CLASS_OTHERS]; - std::copy( configBds.m_TextItalic, configBds.m_TextItalic + 4, bds.m_TextItalic ); - std::copy( configBds.m_TextUpright, configBds.m_TextUpright + 4, bds.m_TextUpright ); - bds.m_DiffPairDimensionsList = configBds.m_DiffPairDimensionsList; - bds.m_CopperEdgeClearance = configBds.m_CopperEdgeClearance; - - // Before we had a copper edge clearance setting, the edge line widths could be used - // as a kludge to control them. So if there's no setting then infer it from the - // edge widths. - if( bds.m_CopperEdgeClearance == Millimeter2iu( LEGACY_COPPEREDGECLEARANCE ) ) - bds.SetCopperEdgeClearance( inferLegacyEdgeClearance( loadedBoard ) ); + Prj().GetLocalSettings().m_VisibleLayers = loadedBoard->m_LegacyVisibleLayers; + loadedBoard->SetModified(); } - // We store the severities in the config to keep board-file changes to a minimum - BOARD_DESIGN_SETTINGS& configBds = GetBoard()->GetDesignSettings(); - bds.m_DRCSeverities = configBds.m_DRCSeverities; - - SetBoard( loadedBoard ); + if( !loadedBoard->m_LegacyVisibleItems.test( GAL_LAYER_INDEX( GAL_LAYER_ID_BITMASK_END ) ) ) + { + Prj().GetLocalSettings().m_VisibleItems = loadedBoard->m_LegacyVisibleItems; + loadedBoard->SetModified(); + } // we should not ask PLUGINs to do these items: loadedBoard->BuildListOfNets(); @@ -723,6 +723,21 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF return false; } + // TODO: this will break if we ever go multi-board + wxFileName projectFile( pcbFileName ); + projectFile.SetExt( ProjectFileExtension ); + + if( !projectFile.FileExists() ) + { + // If this is a new board, project filename won't be set yet + if( projectFile.GetFullPath() != Prj().GetProjectFullName() ) + { + GetBoard()->ClearProject(); + GetSettingsManager()->LoadProject( projectFile.GetFullPath() ); + GetBoard()->SetProject( &Prj() ); + } + } + wxString backupFileName; if( aCreateBackupFile ) @@ -743,6 +758,8 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool aCreateBackupF // edited via the DRC dialog as well as the Board Setup dialog), DRC exclusions, etc. SaveProjectSettings(); + GetSettingsManager()->SaveProject(); + ClearMsgPanel(); wxString upperTxt; diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index 3c0376c0ae..b5fabf3f1b 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -401,12 +401,6 @@ BOARD_DESIGN_SETTINGS& FOOTPRINT_EDIT_FRAME::GetDesignSettings() const } -void FOOTPRINT_EDIT_FRAME::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ) -{ - GetBoard()->SetDesignSettings( aSettings ); -} - - const PCB_PLOT_PARAMS& FOOTPRINT_EDIT_FRAME::GetPlotSettings() const { wxFAIL_MSG( "Plotting not supported in Footprint Editor" ); diff --git a/pcbnew/footprint_edit_frame.h b/pcbnew/footprint_edit_frame.h index 348fb54b45..17ecc8c746 100644 --- a/pcbnew/footprint_edit_frame.h +++ b/pcbnew/footprint_edit_frame.h @@ -74,7 +74,6 @@ public: FOOTPRINT_EDITOR_SETTINGS* GetSettings(); BOARD_DESIGN_SETTINGS& GetDesignSettings() const override; - void SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ) override; const PCB_PLOT_PARAMS& GetPlotSettings() const override; void SetPlotSettings( const PCB_PLOT_PARAMS& aSettings ) override; diff --git a/pcbnew/footprint_editor_settings.cpp b/pcbnew/footprint_editor_settings.cpp index cf892ccc84..42b8998faf 100644 --- a/pcbnew/footprint_editor_settings.cpp +++ b/pcbnew/footprint_editor_settings.cpp @@ -39,7 +39,7 @@ const int fpEditSchemaVersion = 1; FOOTPRINT_EDITOR_SETTINGS::FOOTPRINT_EDITOR_SETTINGS() : APP_SETTINGS_BASE( "fpedit", fpEditSchemaVersion ), - m_DesignSettings(), + m_DesignSettings( nullptr, "fpedit.settings" ), m_MagneticItems(), m_Display(), m_UserGrid(), diff --git a/pcbnew/fp_tree_model_adapter.cpp b/pcbnew/fp_tree_model_adapter.cpp index 19ece66f70..bbc629cdb7 100644 --- a/pcbnew/fp_tree_model_adapter.cpp +++ b/pcbnew/fp_tree_model_adapter.cpp @@ -34,7 +34,7 @@ FP_TREE_MODEL_ADAPTER::PTR FP_TREE_MODEL_ADAPTER::Create( EDA_BASE_FRAME* aParen FP_TREE_MODEL_ADAPTER::FP_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs ) : - LIB_TREE_MODEL_ADAPTER( aParent ), + LIB_TREE_MODEL_ADAPTER( aParent, "pinned_footprint_libs" ), m_libs( (FP_LIB_TABLE*) aLibs ) {} diff --git a/pcbnew/initpcb.cpp b/pcbnew/initpcb.cpp index 1fc7a9b2cf..91043b83bb 100644 --- a/pcbnew/initpcb.cpp +++ b/pcbnew/initpcb.cpp @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include @@ -55,18 +58,11 @@ bool PCB_EDIT_FRAME::Clear_Pcb( bool aQuery, bool aFinal ) GetScreen()->ClearUndoRedoList(); GetScreen()->ClrModify(); - // Items visibility flags will be set because a new board will be created. - // Grid and ratsnest can be left to their previous state - bool showGrid = IsElementVisible( LAYER_GRID ); - bool showRats = GetDisplayOptions().m_ShowGlobalRatsnest; - if( !aFinal ) { // delete the old BOARD and create a new BOARD so that the default // layer names are put into the BOARD. SetBoard( new BOARD() ); - SetElementVisibility( LAYER_GRID, showGrid ); - SetElementVisibility( LAYER_RATSNEST, showRats ); // clear filename, to avoid overwriting an old file GetBoard()->SetFileName( wxEmptyString ); @@ -118,10 +114,6 @@ bool FOOTPRINT_EDIT_FRAME::Clear_Pcb( bool aQuery ) BOARD* board = new BOARD; - // Transfer current design settings - if( GetBoard() ) - board->SetDesignSettings( GetBoard()->GetDesignSettings() ); - board->SynchronizeNetsAndNetClasses(); SetBoard( board ); diff --git a/pcbnew/kicad_plugin.cpp b/pcbnew/kicad_plugin.cpp index 814c4a6432..a1f65e071d 100644 --- a/pcbnew/kicad_plugin.cpp +++ b/pcbnew/kicad_plugin.cpp @@ -472,8 +472,6 @@ void PCB_IO::formatLayer( const BOARD_ITEM* aItem ) const void PCB_IO::formatSetup( BOARD* aBoard, int aNestLevel ) const { - const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings(); - // Setup m_out->Print( aNestLevel, "(setup\n" ); @@ -483,104 +481,7 @@ void PCB_IO::formatSetup( BOARD* aBoard, int aNestLevel ) const if( aBoard->GetDesignSettings().m_HasStackup ) stackup.FormatBoardStackup( m_out,aBoard, aNestLevel+1 ); - // Save current default track width, for compatibility with older Pcbnew version; - m_out->Print( aNestLevel+1, "(last_trace_width %s)\n", - FormatInternalUnits( dsnSettings.GetCurrentTrackWidth() ).c_str() ); - - // Save custom track widths list (the first is not saved here: it's the netclass value) - for( unsigned ii = 1; ii < dsnSettings.m_TrackWidthList.size(); ii++ ) - { - m_out->Print( aNestLevel+1, "(user_trace_width %s)\n", - FormatInternalUnits( dsnSettings.m_TrackWidthList[ii] ).c_str() ); - } - - m_out->Print( aNestLevel+1, "(trace_clearance %s)\n", - FormatInternalUnits( dsnSettings.GetDefault()->GetClearance() ).c_str() ); - - // ZONE_SETTINGS - m_out->Print( aNestLevel+1, "(zone_clearance %s)\n", - FormatInternalUnits( aBoard->GetZoneSettings().m_ZoneClearance ).c_str() ); - m_out->Print( aNestLevel+1, "(zone_45_only %s)\n", - aBoard->GetZoneSettings().m_Zone_45_Only ? "yes" : "no" ); - - m_out->Print( aNestLevel+1, "(trace_min %s)\n", - FormatInternalUnits( dsnSettings.m_TrackMinWidth ).c_str() ); - m_out->Print( aNestLevel+1, "(clearance_min %s)\n", - FormatInternalUnits( dsnSettings.m_MinClearance ).c_str() ); - m_out->Print( aNestLevel+1, "(via_min_annulus %s)\n", - FormatInternalUnits( dsnSettings.m_ViasMinAnnulus ).c_str() ); - m_out->Print( aNestLevel+1, "(via_min_size %s)\n", - FormatInternalUnits( dsnSettings.m_ViasMinSize ).c_str() ); - m_out->Print( aNestLevel+1, "(through_hole_min %s)\n", - FormatInternalUnits( dsnSettings.m_MinThroughDrill ).c_str() ); - m_out->Print( aNestLevel+1, "(hole_to_hole_min %s)\n", - FormatInternalUnits( dsnSettings.m_HoleToHoleMin ).c_str() ); - - // Save current default via size, for compatibility with older Pcbnew version; - m_out->Print( aNestLevel+1, "(via_size %s)\n", - FormatInternalUnits( dsnSettings.GetDefault()->GetViaDiameter() ).c_str() ); - m_out->Print( aNestLevel+1, "(via_drill %s)\n", - FormatInternalUnits( dsnSettings.GetDefault()->GetViaDrill() ).c_str() ); - - // Save custom via dimensions list (the first is not saved here: it's the netclass value) - for( unsigned ii = 1; ii < dsnSettings.m_ViasDimensionsList.size(); ii++ ) - m_out->Print( aNestLevel+1, "(user_via %s %s)\n", - FormatInternalUnits( dsnSettings.m_ViasDimensionsList[ii].m_Diameter ).c_str(), - FormatInternalUnits( dsnSettings.m_ViasDimensionsList[ii].m_Drill ).c_str() ); - - // Save custom diff-pair dimensions (the first is not saved here: it's the netclass value) - for( unsigned ii = 1; ii < dsnSettings.m_DiffPairDimensionsList.size(); ii++ ) - { - m_out->Print( aNestLevel+1, "(user_diff_pair %s %s %s)\n", - FormatInternalUnits( dsnSettings.m_DiffPairDimensionsList[ii].m_Width ).c_str(), - FormatInternalUnits( dsnSettings.m_DiffPairDimensionsList[ii].m_Gap ).c_str(), - FormatInternalUnits( dsnSettings.m_DiffPairDimensionsList[ii].m_ViaGap ).c_str() ); - } - - // for old versions compatibility: - if( dsnSettings.m_BlindBuriedViaAllowed ) - m_out->Print( aNestLevel+1, "(blind_buried_vias_allowed yes)\n" ); - - m_out->Print( aNestLevel+1, "(uvia_size %s)\n", - FormatInternalUnits( dsnSettings.GetDefault()->GetuViaDiameter() ).c_str() ); - m_out->Print( aNestLevel+1, "(uvia_drill %s)\n", - FormatInternalUnits( dsnSettings.GetDefault()->GetuViaDrill() ).c_str() ); - m_out->Print( aNestLevel+1, "(uvias_allowed %s)\n", - ( dsnSettings.m_MicroViasAllowed ) ? "yes" : "no" ); - m_out->Print( aNestLevel+1, "(uvia_min_size %s)\n", - FormatInternalUnits( dsnSettings.m_MicroViasMinSize ).c_str() ); - m_out->Print( aNestLevel+1, "(uvia_min_drill %s)\n", - FormatInternalUnits( dsnSettings.m_MicroViasMinDrill ).c_str() ); - - m_out->Print( aNestLevel+1, "(max_error %s)\n", - FormatInternalUnits( dsnSettings.m_MaxError ).c_str() ); - - // Store this option only if it is not the legacy option: - if( dsnSettings.m_ZoneUseNoOutlineInFill ) - m_out->Print( aNestLevel+1, "(filled_areas_thickness no)\n" ); - - formatDefaults( dsnSettings, aNestLevel+1 ); - - m_out->Print( aNestLevel+1, "(pad_size %s %s)\n", - FormatInternalUnits( dsnSettings.m_Pad_Master.GetSize().x ).c_str(), - FormatInternalUnits( dsnSettings.m_Pad_Master.GetSize().y ).c_str() ); - m_out->Print( aNestLevel+1, "(pad_drill %s)\n", - FormatInternalUnits( dsnSettings.m_Pad_Master.GetDrillSize().x ).c_str() ); - - m_out->Print( aNestLevel+1, "(pad_to_mask_clearance %s)\n", - FormatInternalUnits( dsnSettings.m_SolderMaskMargin ).c_str() ); - - if( dsnSettings.m_SolderMaskMinWidth ) - m_out->Print( aNestLevel+1, "(solder_mask_min_width %s)\n", - FormatInternalUnits( dsnSettings.m_SolderMaskMinWidth ).c_str() ); - - if( dsnSettings.m_SolderPasteMargin != 0 ) - m_out->Print( aNestLevel+1, "(pad_to_paste_clearance %s)\n", - FormatInternalUnits( dsnSettings.m_SolderPasteMargin ).c_str() ); - - if( dsnSettings.m_SolderPasteMarginRatio != 0 ) - m_out->Print( aNestLevel+1, "(pad_to_paste_clearance_ratio %s)\n", - Double2Str( dsnSettings.m_SolderPasteMarginRatio ).c_str() ); + BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings(); if( dsnSettings.m_AuxOrigin != wxPoint( 0, 0 ) ) m_out->Print( aNestLevel+1, "(aux_axis_origin %s %s)\n", @@ -592,71 +493,12 @@ void PCB_IO::formatSetup( BOARD* aBoard, int aNestLevel ) const FormatInternalUnits( dsnSettings.m_GridOrigin.x ).c_str(), FormatInternalUnits( dsnSettings.m_GridOrigin.y ).c_str() ); - m_out->Print( aNestLevel+1, "(visible_elements %X)\n", - dsnSettings.GetVisibleElements() ); - aBoard->GetPlotOptions().Format( m_out, aNestLevel+1 ); m_out->Print( aNestLevel, ")\n\n" ); } -void PCB_IO::formatDefaults( const BOARD_DESIGN_SETTINGS& aSettings, int aNestLevel ) const -{ - m_out->Print( aNestLevel, "(defaults\n" ); - - m_out->Print( aNestLevel+1, "(edge_clearance %s)\n", - FormatInternalUnits( aSettings.m_CopperEdgeClearance ).c_str() ); - - m_out->Print( aNestLevel+1, "(edge_cuts_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_EDGES ] ).c_str() ); - - m_out->Print( aNestLevel+1, "(courtyard_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_COURTYARD ] ).c_str() ); - - m_out->Print( aNestLevel+1, "(copper_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_COPPER ] ).c_str() ); - m_out->Print( aNestLevel+1, "(copper_text_dims (size %s %s) (thickness %s)%s%s)\n", - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_COPPER ].x ).c_str(), - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_COPPER ].y ).c_str(), - FormatInternalUnits( aSettings.m_TextThickness[ LAYER_CLASS_COPPER ] ).c_str(), - aSettings.m_TextItalic[ LAYER_CLASS_COPPER ] ? " italic" : "", - aSettings.m_TextUpright[ LAYER_CLASS_COPPER ] ? " keep_upright" : "" ); - - m_out->Print( aNestLevel+1, "(silk_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_SILK ] ).c_str() ); - m_out->Print( aNestLevel+1, "(silk_text_dims (size %s %s) (thickness %s)%s%s)\n", - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_SILK ].x ).c_str(), - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_SILK ].y ).c_str(), - FormatInternalUnits( aSettings.m_TextThickness[ LAYER_CLASS_SILK ] ).c_str(), - aSettings.m_TextItalic[ LAYER_CLASS_SILK ] ? " italic" : "", - aSettings.m_TextUpright[ LAYER_CLASS_SILK ] ? " keep_upright" : "" ); - - m_out->Print( aNestLevel+1, "(fab_layers_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_FAB ] ).c_str() ); - m_out->Print( aNestLevel+1, "(fab_layers_text_dims (size %s %s) (thickness %s)%s%s)\n", - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_FAB ].x ).c_str(), - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_FAB ].y ).c_str(), - FormatInternalUnits( aSettings.m_TextThickness[ LAYER_CLASS_FAB ] ).c_str(), - aSettings.m_TextItalic[ LAYER_CLASS_OTHERS ] ? " italic" : "", - aSettings.m_TextUpright[ LAYER_CLASS_OTHERS ] ? " keep_upright" : "" ); - - m_out->Print( aNestLevel+1, "(other_layers_line_width %s)\n", - FormatInternalUnits( aSettings.m_LineThickness[ LAYER_CLASS_OTHERS ] ).c_str() ); - m_out->Print( aNestLevel+1, "(other_layers_text_dims (size %s %s) (thickness %s)%s%s)\n", - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_OTHERS ].x ).c_str(), - FormatInternalUnits( aSettings.m_TextSize[ LAYER_CLASS_OTHERS ].y ).c_str(), - FormatInternalUnits( aSettings.m_TextThickness[ LAYER_CLASS_OTHERS ] ).c_str(), - aSettings.m_TextItalic[ LAYER_CLASS_OTHERS ] ? " italic" : "", - aSettings.m_TextUpright[ LAYER_CLASS_OTHERS ] ? " keep_upright" : "" ); - - m_out->Print( aNestLevel+1, "(dimension_units %d)\n", aSettings.m_DimensionUnits ); - m_out->Print( aNestLevel+1, "(dimension_precision %d)\n", aSettings.m_DimensionPrecision ); - - m_out->Print( aNestLevel, ")\n" ); -} - - void PCB_IO::formatGeneral( BOARD* aBoard, int aNestLevel ) const { const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings(); @@ -683,7 +525,6 @@ void PCB_IO::formatBoardLayers( BOARD* aBoard, int aNestLevel ) const m_out->Print( aNestLevel, "(layers\n" ); // Save only the used copper layers from front to back. - LSET visible_layers = aBoard->GetVisibleLayers(); for( LSEQ cu = aBoard->GetEnabledLayers().CuStack(); cu; ++cu ) { @@ -693,9 +534,6 @@ void PCB_IO::formatBoardLayers( BOARD* aBoard, int aNestLevel ) const m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str(), LAYER::ShowType( aBoard->GetLayerType( layer ) ) ); - if( !visible_layers[layer] ) - m_out->Print( 0, " hide" ); - m_out->Print( 0, ")\n" ); } @@ -730,9 +568,6 @@ void PCB_IO::formatBoardLayers( BOARD* aBoard, int aNestLevel ) const m_out->Print( aNestLevel+1, "(%d %s user", layer, m_out->Quotew( aBoard->GetLayerName( layer ) ).c_str() ); - if( !visible_layers[layer] ) - m_out->Print( 0, " hide" ); - m_out->Print( 0, ")\n" ); } @@ -742,7 +577,6 @@ void PCB_IO::formatBoardLayers( BOARD* aBoard, int aNestLevel ) const void PCB_IO::formatNetInformation( BOARD* aBoard, int aNestLevel ) const { - const BOARD_DESIGN_SETTINGS& dsnSettings = aBoard->GetDesignSettings(); for( NETINFO_ITEM* net : *m_mapping ) { m_out->Print( aNestLevel, "(net %d %s)\n", @@ -751,19 +585,6 @@ void PCB_IO::formatNetInformation( BOARD* aBoard, int aNestLevel ) const } m_out->Print( 0, "\n" ); - - // Save the default net class first. - NETCLASS defaultNC = *dsnSettings.GetDefault(); - filterNetClass( *aBoard, defaultNC ); // Remove empty nets (from a copy of a netclass) - defaultNC.Format( m_out, aNestLevel, m_ctl ); - - // Save the rest of the net classes alphabetically. - for( const auto& it : dsnSettings.m_NetClasses ) - { - NETCLASS netclass = *it.second; - filterNetClass( *aBoard, netclass ); // Remove empty nets (from a copy of a netclass) - netclass.Format( m_out, aNestLevel, m_ctl ); - } } diff --git a/pcbnew/kicad_plugin.h b/pcbnew/kicad_plugin.h index f780449ccb..2366e7c45a 100644 --- a/pcbnew/kicad_plugin.h +++ b/pcbnew/kicad_plugin.h @@ -70,7 +70,8 @@ class TEXTE_PCB; //#define SEXPR_BOARD_FILE_VERSION 20200512 // page -> paper //#define SEXPR_BOARD_FILE_VERSION 20200518 // save hole_to_hole_min //#define SEXPR_BOARD_FILE_VERSION 20200614 // Add support for fp_rects and gr_rects -#define SEXPR_BOARD_FILE_VERSION 20200625 // Multilayer zones, zone names, island controls +//#define SEXPR_BOARD_FILE_VERSION 20200625 // Multilayer zones, zone names, island controls +#define SEXPR_BOARD_FILE_VERSION 20200628 // remove visibility settings #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) @@ -216,9 +217,6 @@ protected: /// formats the board setup information void formatSetup( BOARD* aBoard, int aNestLevel = 0 ) const; - /// formats the defaults subsection of the board setup - void formatDefaults( const BOARD_DESIGN_SETTINGS& aSettings, int aNestLevel ) const; - /// formats the General section of the file void formatGeneral( BOARD* aBoard, int aNestLevel = 0 ) const; diff --git a/pcbnew/legacy_plugin.cpp b/pcbnew/legacy_plugin.cpp index 8789d140bc..83be7acf7c 100644 --- a/pcbnew/legacy_plugin.cpp +++ b/pcbnew/legacy_plugin.cpp @@ -832,13 +832,13 @@ void LEGACY_PLUGIN::loadSHEET() void LEGACY_PLUGIN::loadSETUP() { - NETCLASS* netclass_default = m_board->GetDesignSettings().GetDefault(); - // TODO Orson: is it really necessary to first operate on a copy and then apply it? - // would not it be better to use reference here and apply all the changes instantly? - BOARD_DESIGN_SETTINGS bds = m_board->GetDesignSettings(); - ZONE_SETTINGS zs = m_board->GetZoneSettings(); - char* line; - char* saveptr; + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + ZONE_SETTINGS zs = m_board->GetZoneSettings(); + NETCLASS* netclass_default = bds.GetDefault(); + char* line; + char* saveptr; + + m_board->m_LegacyDesignSettingsLoaded = true; while( ( line = READLINE( m_reader ) ) != NULL ) { @@ -1118,12 +1118,17 @@ void LEGACY_PLUGIN::loadSETUP() else if( TESTLINE( "VisibleElements" ) ) { int visibleElements = hexParse( line + SZ( "VisibleElements" ) ); - bds.SetVisibleElements( visibleElements ); + + GAL_SET visibles; + + for( size_t i = 0; i < visibles.size(); i++ ) + visibles.set( i, visibleElements & ( 1u << i ) ); + + m_board->SetVisibleElements( visibles ); } else if( TESTLINE( "$EndSETUP" ) ) { - m_board->SetDesignSettings( bds ); m_board->SetZoneSettings( zs ); // Very old *.brd file does not have NETCLASSes @@ -2399,7 +2404,7 @@ void LEGACY_PLUGIN::loadNETCLASS() else if( TESTLINE( "$EndNCLASS" ) ) { - if( !m_board->GetDesignSettings().m_NetClasses.Add( nc ) ) + if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) ) { // Must have been a name conflict, this is a bad board file. // User may have done a hand edit to the file. diff --git a/pcbnew/netinfo_item.cpp b/pcbnew/netinfo_item.cpp index 96b79e12ec..377006aa64 100644 --- a/pcbnew/netinfo_item.cpp +++ b/pcbnew/netinfo_item.cpp @@ -56,7 +56,7 @@ NETINFO_ITEM::NETINFO_ITEM( BOARD* aParent, const wxString& aNetName, int aNetCo m_parent = aParent; if( aParent ) - m_NetClass = aParent->GetDesignSettings().m_NetClasses.GetDefault(); + m_NetClass = aParent->GetDesignSettings().GetNetClasses().GetDefault(); else m_NetClass = std::make_shared( "" ); } @@ -71,7 +71,7 @@ NETINFO_ITEM::~NETINFO_ITEM() void NETINFO_ITEM::SetClass( const NETCLASSPTR& aNetClass ) { wxCHECK( m_parent, /* void */ ); - m_NetClass = aNetClass ? aNetClass : m_parent->GetDesignSettings().m_NetClasses.GetDefault(); + m_NetClass = aNetClass ? aNetClass : m_parent->GetDesignSettings().GetNetClasses().GetDefault(); } diff --git a/pcbnew/netlist_reader/netlist.cpp b/pcbnew/netlist_reader/netlist.cpp index b1b471e915..b0e9bff033 100644 --- a/pcbnew/netlist_reader/netlist.cpp +++ b/pcbnew/netlist_reader/netlist.cpp @@ -48,6 +48,7 @@ using namespace std::placeholders; #include #include #include +#include // LAST_PATH_TYPE #include extern void SpreadFootprints( std::vector* aFootprints, diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp index 1968987edf..b7c25418e5 100644 --- a/pcbnew/pcb_base_frame.cpp +++ b/pcbnew/pcb_base_frame.cpp @@ -136,7 +136,6 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard ) { delete m_Pcb; m_Pcb = aBoard; - m_Pcb->SetGeneralSettings( m_Settings ); wxCommandEvent e( BOARD_CHANGED ); ProcessEventLocally( e ); @@ -277,13 +276,6 @@ BOARD_DESIGN_SETTINGS& PCB_BASE_FRAME::GetDesignSettings() const } -void PCB_BASE_FRAME::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings ) -{ - wxASSERT( m_Pcb ); - m_Pcb->SetDesignSettings( aSettings ); -} - - void PCB_BASE_FRAME::SetDrawBgColor( COLOR4D aColor ) { m_drawBgColor= aColor; @@ -294,14 +286,14 @@ void PCB_BASE_FRAME::SetDrawBgColor( COLOR4D aColor ) const ZONE_SETTINGS& PCB_BASE_FRAME::GetZoneSettings() const { wxASSERT( m_Pcb ); - return m_Pcb->GetZoneSettings(); + return m_Pcb->GetDesignSettings().GetDefaultZoneSettings(); } void PCB_BASE_FRAME::SetZoneSettings( const ZONE_SETTINGS& aSettings ) { wxASSERT( m_Pcb ); - m_Pcb->SetZoneSettings( aSettings ); + m_Pcb->GetDesignSettings().SetDefaultZoneSettings( aSettings ); } @@ -735,8 +727,6 @@ void PCB_BASE_FRAME::ActivateGalCanvas() GetCanvas()->GetViewControls(), config(), this ); } - SetBoard( m_Pcb ); - if( m_toolManager ) m_toolManager->ResetTools( TOOL_BASE::GAL_SWITCH ); diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index cb2ada5148..529d555ba6 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -369,6 +370,9 @@ PCB_EDIT_FRAME::~PCB_EDIT_FRAME() void PCB_EDIT_FRAME::SetBoard( BOARD* aBoard ) { + if( m_Pcb ) + m_Pcb->ClearProject(); + PCB_BASE_EDIT_FRAME::SetBoard( aBoard ); aBoard->SetProject( &Prj() ); @@ -497,39 +501,42 @@ void PCB_EDIT_FRAME::OnQuit( wxCommandEvent& event ) void PCB_EDIT_FRAME::RecordDRCExclusions() { - m_drcExclusions.clear(); + BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); + bds.m_DrcExclusions.clear(); for( MARKER_PCB* marker : GetBoard()->Markers() ) { if( marker->IsExcluded() ) - m_drcExclusions.insert( marker->Serialize() ); + bds.m_DrcExclusions.insert( marker->Serialize() ); } } void PCB_EDIT_FRAME::ResolveDRCExclusions() { + BOARD_DESIGN_SETTINGS& bds = GetBoard()->GetDesignSettings(); + for( MARKER_PCB* marker : GetBoard()->Markers() ) { - auto i = m_drcExclusions.find( marker->Serialize() ); + auto i = bds.m_DrcExclusions.find( marker->Serialize() ); - if( i != m_drcExclusions.end() ) + if( i != bds.m_DrcExclusions.end() ) { marker->SetExcluded( true ); - m_drcExclusions.erase( i ); + bds.m_DrcExclusions.erase( i ); } } BOARD_COMMIT commit( this ); - for( const wxString& exclusionData : m_drcExclusions ) + for( const wxString& exclusionData : bds.m_DrcExclusions ) { MARKER_PCB* marker = MARKER_PCB::Deserialize( exclusionData ); marker->SetExcluded( true ); commit.Add( marker ); } - m_drcExclusions.clear(); + bds.m_DrcExclusions.clear(); commit.Push( wxEmptyString, false, false ); } @@ -847,10 +854,12 @@ void PCB_EDIT_FRAME::ShowChangedLanguage() wxString PCB_EDIT_FRAME::GetLastPath( LAST_PATH_TYPE aType ) { - if( m_lastPath[ aType ].IsEmpty() ) + PROJECT_FILE& project = Prj().GetProjectFile(); + + if( project.m_PcbLastPath[ aType ].IsEmpty() ) return wxEmptyString; - wxFileName absoluteFileName = m_lastPath[ aType ]; + wxFileName absoluteFileName = project.m_PcbLastPath[ aType ]; wxFileName pcbFileName = GetBoard()->GetFileName(); absoluteFileName.MakeAbsolute( pcbFileName.GetPath() ); @@ -860,14 +869,16 @@ wxString PCB_EDIT_FRAME::GetLastPath( LAST_PATH_TYPE aType ) void PCB_EDIT_FRAME::SetLastPath( LAST_PATH_TYPE aType, const wxString& aLastPath ) { + PROJECT_FILE& project = Prj().GetProjectFile(); + wxFileName relativeFileName = aLastPath; wxFileName pcbFileName = GetBoard()->GetFileName(); relativeFileName.MakeRelativeTo( pcbFileName.GetPath() ); - if( relativeFileName.GetFullPath() != m_lastPath[ aType ] ) + if( relativeFileName.GetFullPath() != project.m_PcbLastPath[ aType ] ) { - m_lastPath[ aType ] = relativeFileName.GetFullPath(); + project.m_PcbLastPath[ aType ] = relativeFileName.GetFullPath(); SaveProjectSettings(); } } diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 3c8b5b0f84..6bc0fc099b 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -24,7 +24,6 @@ #include #include #include "pcb_base_edit_frame.h" -#include "config_params.h" #include "undo_redo_container.h" #include "zones.h" @@ -60,6 +59,7 @@ class IO_ERROR; class FP_LIB_TABLE; class BOARD_NETLIST_UPDATER; class ACTION_MENU; +enum LAST_PATH_TYPE : unsigned int; namespace PCB { struct IFACE; } // KIFACE_I is in pcbnew.cpp @@ -73,18 +73,6 @@ enum TRACK_ACTION_RESULT TRACK_ACTION_NONE //!< TRACK_ACTION_NONE - Nothing to change }; -enum LAST_PATH_TYPE -{ - LAST_PATH_NETLIST = 0, - LAST_PATH_STEP, - LAST_PATH_IDF, - LAST_PATH_VRML, - LAST_PATH_SPECCTRADSN, - LAST_PATH_GENCAD, - - LAST_PATH_SIZE -}; - /** * PCB_EDIT_FRAME * is the main frame for Pcbnew. @@ -102,11 +90,6 @@ class PCB_EDIT_FRAME : public PCB_BASE_EDIT_FRAME ACTION_TOOLBAR* m_microWaveToolBar; protected: - std::vector m_projectFileParams; - - wxString m_lastPath[ LAST_PATH_SIZE ]; - - std::set m_drcExclusions; /** * Store the previous layer toolbar icon state information @@ -377,20 +360,6 @@ public: #endif - /** - * Function GetProjectFileParameters - * returns a project file parameter list for Pcbnew. - *

- * Populate a project file parameter array specific to Pcbnew. - * Creating the parameter list at run time has the advantage of being able - * to define local variables. The old method of statically building the array - * at compile time requiring global variable definitions by design. - *

- * @return std::vector - it is only good until SetBoard() is called, so - * don't keep it around past that event. - */ - std::vector& GetProjectFileParameters(); - /** * Function SaveProjectSettings * saves changes to the project settings to the project (.pro) file. diff --git a/pcbnew/pcb_layer_widget.cpp b/pcbnew/pcb_layer_widget.cpp index 727e020d05..c0a38c17af 100644 --- a/pcbnew/pcb_layer_widget.cpp +++ b/pcbnew/pcb_layer_widget.cpp @@ -331,7 +331,6 @@ void PCB_LAYER_WIDGET::SetLayersManagerTabsText() void PCB_LAYER_WIDGET::ReFillRender() { BOARD* board = myframe->GetBoard(); - auto settings = board->GetDesignSettings(); ClearRenderRows(); @@ -346,17 +345,6 @@ void PCB_LAYER_WIDGET::ReFillRender() if( m_fp_editor_mode && !isAllowedInFpMode( renderRow.id ) ) continue; - // Don't remove microvia and bblind vias if they're not allowed: that's only a DRC - // setting (which might be set to ignore) and the user can add them irrespective of - // the setting. - /* - if( renderRow.id == LAYER_VIA_MICROVIA && !settings.m_MicroViasAllowed ) - continue; - - if( renderRow.id == LAYER_VIA_BBLIND && !settings.m_BlindBuriedViaAllowed ) - continue; - */ - if( !renderRow.spacer ) { renderRow.tooltip = wxGetTranslation( s_render_rows[row].tooltip ); @@ -640,10 +628,6 @@ void PCB_LAYER_WIDGET::OnLayerVisible( int aLayer, bool isVisible, bool isFinal brd->SetVisibleLayers( visibleLayers ); - // Layer visibility is not stored in .kicad_mod files - if( !m_fp_editor_mode ) - myframe->OnModify(); - if( myframe->GetCanvas() ) myframe->GetCanvas()->GetView()->SetLayerVisible( aLayer, isVisible ); } @@ -685,14 +669,6 @@ void PCB_LAYER_WIDGET::OnRenderEnable( int aId, bool isEnabled ) BOARD* brd = myframe->GetBoard(); wxASSERT( aId > GAL_LAYER_ID_START && aId < GAL_LAYER_ID_END ); - if( myframe->IsType( FRAME_PCB_EDITOR ) ) - { - // The layer visibility status is saved in the board file so set the board - // modified state so the user has the option to save the changes. - if( brd->IsElementVisible( static_cast( aId ) ) != isEnabled ) - myframe->OnModify(); - } - // Grid is not set through the board visibility if( aId == LAYER_GRID ) myframe->SetGridVisibility( isEnabled ); diff --git a/pcbnew/pcb_parser.cpp b/pcbnew/pcb_parser.cpp index e1049d5142..f940e2f92c 100644 --- a/pcbnew/pcb_parser.cpp +++ b/pcbnew/pcb_parser.cpp @@ -577,6 +577,7 @@ BOARD* PCB_PARSER::parseBOARD_unchecked() case T_net_class: parseNETCLASS(); + m_board->m_LegacyNetclassesLoaded = true; break; case T_gr_arc: @@ -1253,6 +1254,7 @@ void PCB_PARSER::parseLayers() LSET enabledLayers; int copperLayerCount = 0; LAYER layer; + bool anyHidden = false; std::unordered_map< std::string, std::string > v3_layer_names; std::vector cu; @@ -1292,6 +1294,8 @@ void PCB_PARSER::parseLayers() if( it->m_visible ) visibleLayers.set( it->m_number ); + else + anyHidden = true; m_board->SetLayerDescr( PCB_LAYER_ID( it->m_number ), *it ); @@ -1341,6 +1345,8 @@ void PCB_PARSER::parseLayers() if( layer.m_visible ) visibleLayers.set( layer.m_number ); + else + anyHidden = true; m_board->SetLayerDescr( it->second, layer ); @@ -1364,8 +1370,10 @@ void PCB_PARSER::parseLayers() m_board->SetCopperLayerCount( copperLayerCount ); m_board->SetEnabledLayers( enabledLayers ); - // call SetEnabledLayers before SetVisibleLayers() - m_board->SetVisibleLayers( visibleLayers ); + // Only set this if any layers were explicitly marked as hidden. Otherwise, we want to leave + // this alone; default visibility will show everything + if( anyHidden ) + m_board->m_LegacyVisibleLayers = visibleLayers; } @@ -1433,9 +1441,9 @@ void PCB_PARSER::parseSetup() wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as setup." ) ); T token; - NETCLASS* defaultNetClass = m_board->GetDesignSettings().GetDefault(); - BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings(); - ZONE_SETTINGS zoneSettings = m_board->GetZoneSettings(); + NETCLASS* defaultNetClass = m_board->GetDesignSettings().GetDefault(); + BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings(); + ZONE_SETTINGS& zoneSettings = designSettings.GetDefaultZoneSettings(); // Missing soldermask min width value means that the user has set the value to 0 and // not the default value (0.25mm) @@ -1461,67 +1469,80 @@ void PCB_PARSER::parseSetup() case T_user_trace_width: designSettings.m_TrackWidthList.push_back( parseBoardUnits( T_user_trace_width ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_trace_clearance: defaultNetClass->SetClearance( parseBoardUnits( T_trace_clearance ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_zone_clearance: zoneSettings.m_ZoneClearance = parseBoardUnits( T_zone_clearance ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_zone_45_only: zoneSettings.m_Zone_45_Only = parseBool(); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_clearance_min: designSettings.m_MinClearance = parseBoardUnits( T_clearance_min ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_trace_min: designSettings.m_TrackMinWidth = parseBoardUnits( T_trace_min ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_via_size: defaultNetClass->SetViaDiameter( parseBoardUnits( T_via_size ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_via_drill: defaultNetClass->SetViaDrill( parseBoardUnits( T_via_drill ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_via_min_annulus: designSettings.m_ViasMinAnnulus = parseBoardUnits( T_via_min_annulus ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_via_min_size: designSettings.m_ViasMinSize = parseBoardUnits( T_via_min_size ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_through_hole_min: designSettings.m_MinThroughDrill = parseBoardUnits( T_through_hole_min ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; // Legacy token for T_through_hole_min case T_via_min_drill: designSettings.m_MinThroughDrill = parseBoardUnits( T_via_min_drill ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_hole_to_hole_min: designSettings.m_HoleToHoleMin = parseBoardUnits( T_hole_to_hole_min ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; @@ -1530,37 +1551,44 @@ void PCB_PARSER::parseSetup() int viaSize = parseBoardUnits( "user via size" ); int viaDrill = parseBoardUnits( "user via drill" ); designSettings.m_ViasDimensionsList.emplace_back( VIA_DIMENSION( viaSize, viaDrill ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; case T_uvia_size: defaultNetClass->SetuViaDiameter( parseBoardUnits( T_uvia_size ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_uvia_drill: defaultNetClass->SetuViaDrill( parseBoardUnits( T_uvia_drill ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_uvias_allowed: designSettings.m_MicroViasAllowed = parseBool(); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_blind_buried_vias_allowed: designSettings.m_BlindBuriedViaAllowed = parseBool(); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_uvia_min_size: designSettings.m_MicroViasMinSize = parseBoardUnits( T_uvia_min_size ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_uvia_min_drill: designSettings.m_MicroViasMinDrill = parseBoardUnits( T_uvia_min_drill ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; @@ -1570,49 +1598,58 @@ void PCB_PARSER::parseSetup() int gap = parseBoardUnits( "user diff-pair gap" ); int viaGap = parseBoardUnits( "user diff-pair via gap" ); designSettings.m_DiffPairDimensionsList.emplace_back( DIFF_PAIR_DIMENSION( width, gap, viaGap ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; case T_segment_width: // note: legacy (pre-6.0) token designSettings.m_LineThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_segment_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_edge_width: // note: legacy (pre-6.0) token designSettings.m_LineThickness[ LAYER_CLASS_EDGES ] = parseBoardUnits( T_edge_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_mod_edge_width: // note: legacy (pre-6.0) token designSettings.m_LineThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_edge_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_pcb_text_width: // note: legacy (pre-6.0) token designSettings.m_TextThickness[ LAYER_CLASS_COPPER ] = parseBoardUnits( T_pcb_text_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_mod_text_width: // note: legacy (pre-6.0) token designSettings.m_TextThickness[ LAYER_CLASS_SILK ] = parseBoardUnits( T_mod_text_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_pcb_text_size: // note: legacy (pre-6.0) token designSettings.m_TextSize[ LAYER_CLASS_COPPER ].x = parseBoardUnits( "pcb text width" ); designSettings.m_TextSize[ LAYER_CLASS_COPPER ].y = parseBoardUnits( "pcb text height" ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_mod_text_size: // note: legacy (pre-6.0) token designSettings.m_TextSize[ LAYER_CLASS_SILK ].x = parseBoardUnits( "module text width" ); designSettings.m_TextSize[ LAYER_CLASS_SILK ].y = parseBoardUnits( "module text height" ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_defaults: parseDefaults( designSettings ); + m_board->m_LegacyDesignSettingsLoaded = true; break; case T_pad_size: @@ -1621,6 +1658,7 @@ void PCB_PARSER::parseSetup() sz.SetWidth( parseBoardUnits( "master pad width" ) ); sz.SetHeight( parseBoardUnits( "master pad height" ) ); designSettings.m_Pad_Master.SetSize( sz ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; @@ -1629,27 +1667,32 @@ void PCB_PARSER::parseSetup() { int drillSize = parseBoardUnits( T_pad_drill ); designSettings.m_Pad_Master.SetDrillSize( wxSize( drillSize, drillSize ) ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; case T_pad_to_mask_clearance: designSettings.m_SolderMaskMargin = parseBoardUnits( T_pad_to_mask_clearance ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_solder_mask_min_width: designSettings.m_SolderMaskMinWidth = parseBoardUnits( T_solder_mask_min_width ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_pad_to_paste_clearance: designSettings.m_SolderPasteMargin = parseBoardUnits( T_pad_to_paste_clearance ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_pad_to_paste_clearance_ratio: designSettings.m_SolderPasteMarginRatio = parseDouble( T_pad_to_paste_clearance_ratio ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; @@ -1657,8 +1700,9 @@ void PCB_PARSER::parseSetup() { int x = parseBoardUnits( "auxiliary origin X" ); int y = parseBoardUnits( "auxiliary origin Y" ); - // m_board->SetAuxOrigin( wxPoint( x, y ) ); gets overwritten via SetDesignSettings below designSettings.m_AuxOrigin = wxPoint( x, y ); + // Aux origin still stored in board for the moment + //m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; @@ -1667,24 +1711,36 @@ void PCB_PARSER::parseSetup() { int x = parseBoardUnits( "grid origin X" ); int y = parseBoardUnits( "grid origin Y" ); - // m_board->SetGridOrigin( wxPoint( x, y ) ); gets overwritten SetDesignSettings below designSettings.m_GridOrigin = wxPoint( x, y ); + // Grid origin still stored in board for the moment + //m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); } break; + // Stored in board prior to 6.0 case T_visible_elements: - designSettings.SetVisibleElements( parseHex() | MIN_VISIBILITY_MASK ); - NeedRIGHT(); + { + m_board->m_LegacyVisibleItems.reset(); + + int visible = parseHex() | MIN_VISIBILITY_MASK; + + for( size_t i = 0; i < sizeof( int ) * CHAR_BIT; i++ ) + m_board->m_LegacyVisibleItems.set( i, visible & ( 1u << i ) ); + + NeedRIGHT(); + } break; case T_max_error: designSettings.m_MaxError = parseBoardUnits( T_max_error ); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; case T_filled_areas_thickness: designSettings.m_ZoneUseNoOutlineInFill = not parseBool(); + m_board->m_LegacyDesignSettingsLoaded = true; NeedRIGHT(); break; @@ -1707,9 +1763,6 @@ void PCB_PARSER::parseSetup() Unexpected( CurText() ); } } - - //m_board->SetDesignSettings( designSettings ); - m_board->SetZoneSettings( zoneSettings ); } @@ -1925,7 +1978,7 @@ void PCB_PARSER::parseNETCLASS() NeedRIGHT(); } - if( !m_board->GetDesignSettings().m_NetClasses.Add( nc ) ) + if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) ) { // Must have been a name conflict, this is a bad board file. // User may have done a hand edit to the file. diff --git a/pcbnew/pcbnew_config.cpp b/pcbnew/pcbnew_config.cpp index 9d3d115353..30160ca175 100644 --- a/pcbnew/pcbnew_config.cpp +++ b/pcbnew/pcbnew_config.cpp @@ -46,6 +46,7 @@ #include #include #include +#include void PCB_EDIT_FRAME::On3DShapeLibWizard( wxCommandEvent& event ) @@ -77,7 +78,9 @@ bool PCB_EDIT_FRAME::LoadProjectSettings() { wxLogDebug( wxT( "Loading project '%s' settings." ), GetChars( Prj().GetProjectFullName() ) ); - bool rc = Prj().ConfigLoad( Kiface().KifaceSearch(), GROUP_PCB, GetProjectFileParameters() ); + PROJECT_FILE& project = Prj().GetProjectFile(); + + BASE_SCREEN::m_PageLayoutDescrFileName = project.m_PageLayoutDescrFile; // Load the page layout decr file, from the filename stored in // BASE_SCREEN::m_PageLayoutDescrFileName, read in config project file @@ -88,7 +91,7 @@ bool PCB_EDIT_FRAME::LoadProjectSettings() pglayout.SetPageLayout( filename ); - return rc; + return true; } @@ -105,45 +108,12 @@ void PCB_EDIT_FRAME::SaveProjectSettings() if( !IsWritable( fn ) ) return; - wxString pro_name = fn.GetFullPath(); + PROJECT_FILE& project = Prj().GetProjectFile(); + + // TODO: Can this be pulled out of BASE_SCREEN? + project.m_PageLayoutDescrFile = BASE_SCREEN::m_PageLayoutDescrFileName; RecordDRCExclusions(); - Prj().ConfigSave( Kiface().KifaceSearch(), GROUP_PCB, GetProjectFileParameters(), pro_name ); -} - - -std::vector& PCB_EDIT_FRAME::GetProjectFileParameters() -{ - m_projectFileParams.clear(); - - // This one cannot be cached because some settings are going to/from the BOARD, - // so pointers into that cannot be saved for long. - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "PageLayoutDescrFile" ), - &BASE_SCREEN::m_PageLayoutDescrFileName ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastNetListRead" ), - &m_lastPath[ LAST_PATH_NETLIST ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastSTEPExportPath" ), - &m_lastPath[ LAST_PATH_STEP ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastIDFExportPath" ), - &m_lastPath[ LAST_PATH_IDF ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastVRMLExportPath" ), - &m_lastPath[ LAST_PATH_VRML ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastSpecctraDSNExportPath" ), - &m_lastPath[ LAST_PATH_SPECCTRADSN ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_FILENAME( wxT( "LastGenCADExportPath" ), - &m_lastPath[ LAST_PATH_GENCAD ] ) ); - - m_projectFileParams.push_back( new PARAM_CFG_WXSTRING_SET( wxT( "DRCExclusion" ), - &m_drcExclusions ) ); - - GetBoard()->GetDesignSettings().AppendConfigs( GetBoard(), &m_projectFileParams); - - return m_projectFileParams; + + GetSettingsManager()->SaveProject(); } diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 1ebeac416c..c7188ed945 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -126,7 +126,7 @@ PNS_PCBNEW_RULE_RESOLVER::PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER* ent.coupledNet = DpCoupledNet( i ); wxString netClassName = ni->GetClassName(); - NETCLASSPTR nc = m_board->GetDesignSettings().m_NetClasses.Find( netClassName ); + NETCLASSPTR nc = m_board->GetDesignSettings().GetNetClasses().Find( netClassName ); int clearance = nc->GetClearance(); ent.clearance = clearance; @@ -154,7 +154,7 @@ PNS_PCBNEW_RULE_RESOLVER::PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER* } } - auto defaultRule = m_board->GetDesignSettings().m_NetClasses.Find ("Default"); + auto defaultRule = m_board->GetDesignSettings().GetNetClasses().Find ("Default"); if( defaultRule ) { diff --git a/pcbnew/router/pns_sizes_settings.cpp b/pcbnew/router/pns_sizes_settings.cpp index b7286374ad..b86f0ac401 100644 --- a/pcbnew/router/pns_sizes_settings.cpp +++ b/pcbnew/router/pns_sizes_settings.cpp @@ -89,12 +89,12 @@ void SIZES_SETTINGS::Init( BOARD* aBoard, ITEM* aStartItem, int aNet ) if( ni ) { wxString netClassName = ni->GetClassName(); - netClass = bds.m_NetClasses.Find( netClassName ); + netClass = bds.GetNetClasses().Find( netClassName ); } } if( !netClass ) - netClass = bds.m_NetClasses.GetDefault(); + netClass = bds.GetNetClasses().GetDefault(); m_trackWidth = 0; diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 9ff39dfd42..cac1f74f3e 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -1430,7 +1430,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) //-----< output vias used in netclasses >----------------------------------- { - NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses; + NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses(); // Assume the netclass vias are all the same kind of thru, blind, or buried vias. // This is in lieu of either having each netclass via have its own layer pair in @@ -1641,7 +1641,7 @@ void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) //--------------------------------------------------------- - NETCLASSES& nclasses = aBoard->GetDesignSettings().m_NetClasses; + NETCLASSES& nclasses = aBoard->GetDesignSettings().GetNetClasses(); exportNETCLASS( nclasses.GetDefault(), aBoard ); diff --git a/pcbnew/specctra_import_export/specctra_import.cpp b/pcbnew/specctra_import_export/specctra_import.cpp index 39916834aa..bbb4cb4b9f 100644 --- a/pcbnew/specctra_import_export/specctra_import.cpp +++ b/pcbnew/specctra_import_export/specctra_import.cpp @@ -499,7 +499,7 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard ) GetChars( psid ) ) ); } - NETCLASSPTR netclass = aBoard->GetDesignSettings().m_NetClasses.GetDefault(); + NETCLASSPTR netclass = aBoard->GetDesignSettings().GetNetClasses().GetDefault(); int via_drill_default = netclass->GetViaDrill(); diff --git a/pcbnew/swig/board.i b/pcbnew/swig/board.i index 3bdc1e610c..c0742c2c5c 100644 --- a/pcbnew/swig/board.i +++ b/pcbnew/swig/board.i @@ -96,11 +96,16 @@ HANDLE_EXCEPTIONS(BOARD::TracksInNetBetweenPoints) } %{ #include +#include %} // std::vector templates %template(VIA_DIMENSION_Vector) std::vector; + +// Do not permit default BOARD ctor since it won't initialize the project +%ignore BOARD::BOARD(); + %include class_board.h %{ #include @@ -123,11 +128,25 @@ HANDLE_EXCEPTIONS(BOARD::TracksInNetBetweenPoints) %extend BOARD { + // NOTE: this does not generate a ctor, despite swig docs saying it should. Not sure why. + // Because of this, we use the __init__ override hack below. + // BOARD() + // { + // return CreateEmptyBoard(); + // } + // BOARD_ITEM_CONTAINER's interface functions will be implemented by SWIG // automatically and inherited by the python wrapper class. %pythoncode %{ + def __init__(self, *args): + this = CreateEmptyBoard() + + try: + self.this.append(this) + except: + self.this = this def GetModules(self): return self.Modules() def GetDrawings(self): return self.Drawings() diff --git a/pcbnew/swig/pcbnew_scripting_helpers.cpp b/pcbnew/swig/pcbnew_scripting_helpers.cpp index 9e846eccff..e5c3daf3cb 100644 --- a/pcbnew/swig/pcbnew_scripting_helpers.cpp +++ b/pcbnew/swig/pcbnew_scripting_helpers.cpp @@ -42,9 +42,19 @@ #include #include #include +#include +#include static PCB_EDIT_FRAME* s_PcbEditFrame = NULL; +/** + * We need to track the loaded PROJECTs for each loaded BOARD here, since in Python you can + * easily load more than one board if desired. + */ +static std::map s_Projects; + +static SETTINGS_MANAGER* s_SettingsManager = nullptr; + BOARD* GetBoard() { if( s_PcbEditFrame ) @@ -73,17 +83,72 @@ BOARD* LoadBoard( wxString& aFileName ) } +SETTINGS_MANAGER* GetSettingsManager() +{ + if( !s_SettingsManager ) + s_SettingsManager = new SETTINGS_MANAGER; + + return s_SettingsManager; +} + + +PROJECT* GetDefaultProject() +{ + PROJECT* project = nullptr; + + if( s_Projects.count( "" ) ) + project = s_Projects.at( "" ); + else + { + GetSettingsManager()->LoadProject( "" ); + project = GetSettingsManager()->GetProject( "" ); + s_Projects[""] = project; + } + + return project; +} + + BOARD* LoadBoard( wxString& aFileName, IO_MGR::PCB_FILE_T aFormat ) { + wxFileName pro = aFileName; + pro.SetExt( ProjectFileExtension ); + pro.MakeAbsolute(); + wxString projectPath = pro.GetFullPath(); + + PROJECT* project = nullptr; + + if( s_Projects.count( projectPath ) ) + project = s_Projects.at( projectPath ); + else if( GetSettingsManager()->LoadProject( projectPath ) ) + { + project = GetSettingsManager()->GetProject( projectPath ); + s_Projects[projectPath] = project; + } + + // Board cannot be loaded without a project, so create the default project + if( !project ) + project = GetDefaultProject(); + BOARD* brd = IO_MGR::Load( aFormat, aFileName ); if( brd ) { + brd->SetProject( project ); brd->BuildConnectivity(); brd->BuildListOfNets(); brd->SynchronizeNetsAndNetClasses(); } + return brd; +} + + +BOARD* CreateEmptyBoard() +{ + BOARD* brd = new BOARD(); + + brd->SetProject( GetDefaultProject() ); return brd; } @@ -97,6 +162,13 @@ bool SaveBoard( wxString& aFileName, BOARD* aBoard, IO_MGR::PCB_FILE_T aFormat ) IO_MGR::Save( aFormat, aFileName, aBoard, NULL ); + wxFileName pro = aFileName; + pro.SetExt( ProjectFileExtension ); + pro.MakeAbsolute(); + wxString projectPath = pro.GetFullPath(); + + GetSettingsManager()->SaveProject( pro.GetFullPath() ); + return true; } diff --git a/pcbnew/swig/pcbnew_scripting_helpers.h b/pcbnew/swig/pcbnew_scripting_helpers.h index 2c341943a6..2379312add 100644 --- a/pcbnew/swig/pcbnew_scripting_helpers.h +++ b/pcbnew/swig/pcbnew_scripting_helpers.h @@ -45,6 +45,12 @@ BOARD* LoadBoard( wxString& aFileName, IO_MGR::PCB_FILE_T aFormat ); // Default LoadBoard() to load .kicad_pcb files:. BOARD* LoadBoard( wxString& aFileName ); +/** + * Constructs a default BOARD with a tempoary (no filename) project + * @return the created board + */ +BOARD* CreateEmptyBoard(); + // Boards can be saved only as .kicad_pcb file format, // so no option to choose the file format. bool SaveBoard( wxString& aFileName, BOARD* aBoard ); diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index cc08b8e953..d4c16c0017 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -32,8 +32,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -52,6 +52,7 @@ #include #include #include +#include // LAST_PATH_TYPE #include #include #include diff --git a/pcbnew/tools/pcbnew_control.cpp b/pcbnew/tools/pcbnew_control.cpp index b4701b1682..40df69ea47 100644 --- a/pcbnew/tools/pcbnew_control.cpp +++ b/pcbnew/tools/pcbnew_control.cpp @@ -883,7 +883,7 @@ int PCBNEW_CONTROL::AppendBoard( PLUGIN& pi, wxString& fileName ) props["page_width"] = xbuf; props["page_height"] = ybuf; - editFrame->GetDesignSettings().m_NetClasses.Clear(); + editFrame->GetDesignSettings().GetNetClasses().Clear(); pi.Load( fileName, brd, &props ); } catch( const IO_ERROR& ioe ) diff --git a/pcbnew/zone_settings.h b/pcbnew/zone_settings.h index ba492884b4..d39023f150 100644 --- a/pcbnew/zone_settings.h +++ b/pcbnew/zone_settings.h @@ -32,7 +32,8 @@ #include #include -#include + +class wxDataViewListCtrl; enum class ZONE_FILL_MODE { diff --git a/qa/pcbnew/drc/test_drc_courtyard_invalid.cpp b/qa/pcbnew/drc/test_drc_courtyard_invalid.cpp index df2827dc3a..64038d6d55 100644 --- a/qa/pcbnew/drc/test_drc_courtyard_invalid.cpp +++ b/qa/pcbnew/drc/test_drc_courtyard_invalid.cpp @@ -228,25 +228,6 @@ std::unique_ptr MakeBoard( const std::vectorSetDesignSettings( GetOverlapCheckDesignSettings() ); + BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + + // do the overlap tests - that's a different test, but if not set, + // the invalid courtyard checks don't run either + bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; + + // we will also check for missing courtyards here + bds.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_ERROR; // list of markers to collect std::vector> markers; diff --git a/qa/pcbnew/drc/test_drc_courtyard_overlap.cpp b/qa/pcbnew/drc/test_drc_courtyard_overlap.cpp index f8b52f836a..8f3e829e60 100644 --- a/qa/pcbnew/drc/test_drc_courtyard_overlap.cpp +++ b/qa/pcbnew/drc/test_drc_courtyard_overlap.cpp @@ -441,21 +441,6 @@ static void CheckCollisionsMatchExpected( BOARD& aBoard, } -/** - * Get a #BOARD_DESIGN_SETTINGS object that will cause DRC to check for courtyard overlaps - */ -static BOARD_DESIGN_SETTINGS GetOverlapCheckDesignSettings() -{ - BOARD_DESIGN_SETTINGS des_settings; - des_settings.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; - - // we might not always have courtyards - that's a separate test - des_settings.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; - - return des_settings; -} - - /** * Run a single courtyard overlap testcase * @param aCase The testcase to run. @@ -468,7 +453,12 @@ static void DoCourtyardOverlapTest( const COURTYARD_OVERLAP_TEST_CASE& aCase, // Dump if env var set aDumper.DumpBoardToFile( *board, aCase.m_case_name ); - board->SetDesignSettings( GetOverlapCheckDesignSettings() ); + BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + + bds.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; + + // we might not always have courtyards - that's a separate test + bds.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; // list of markers to collect std::vector> markers; diff --git a/qa/pcbnew_tools/tools/drc_tool/drc_tool.cpp b/qa/pcbnew_tools/tools/drc_tool/drc_tool.cpp index 8450f868eb..1ec499f537 100644 --- a/qa/pcbnew_tools/tools/drc_tool/drc_tool.cpp +++ b/qa/pcbnew_tools/tools/drc_tool/drc_tool.cpp @@ -69,7 +69,7 @@ public: if( m_exec_context.m_verbose ) std::cout << "Running DRC check: " << getRunnerIntro() << std::endl; - aBoard.SetDesignSettings( getDesignSettings() ); + setDesignSettings( aBoard.GetDesignSettings() ); std::vector> markers; @@ -101,9 +101,10 @@ private: virtual std::string getRunnerIntro() const = 0; /** - * Get suitable design settings for this DRC runner + * Set suitable design settings for this DRC runner + * @param aSettings is a reference to the design settings object of the board under test */ - virtual BOARD_DESIGN_SETTINGS getDesignSettings() const = 0; + virtual void setDesignSettings( BOARD_DESIGN_SETTINGS& aSettings ) const = 0; virtual std::unique_ptr createDrcProvider( BOARD& aBoard, DRC_TEST_PROVIDER::MARKER_HANDLER aHandler ) = 0; @@ -157,13 +158,10 @@ private: return "Courtyard overlap"; } - BOARD_DESIGN_SETTINGS getDesignSettings() const override + void setDesignSettings( BOARD_DESIGN_SETTINGS& aSettings ) const override { - BOARD_DESIGN_SETTINGS des_settings; - des_settings.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; - des_settings.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; - - return des_settings; + aSettings.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_IGNORE; + aSettings.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_ERROR; } std::unique_ptr createDrcProvider( @@ -194,13 +192,10 @@ private: return "Courtyard missing"; } - BOARD_DESIGN_SETTINGS getDesignSettings() const override + void setDesignSettings( BOARD_DESIGN_SETTINGS& aSettings ) const override { - BOARD_DESIGN_SETTINGS des_settings; - des_settings.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_ERROR; - des_settings.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_IGNORE; - - return des_settings; + aSettings.m_DRCSeverities[ DRCE_MISSING_COURTYARD ] = RPT_SEVERITY_ERROR; + aSettings.m_DRCSeverities[ DRCE_OVERLAPPING_FOOTPRINTS ] = RPT_SEVERITY_IGNORE; } std::unique_ptr createDrcProvider(