diff --git a/3d-viewer/3d_viewer/3d_viewer_settings.cpp b/3d-viewer/3d_viewer/3d_viewer_settings.cpp index 7278fa661b..5a2052042f 100644 --- a/3d-viewer/3d_viewer/3d_viewer_settings.cpp +++ b/3d-viewer/3d_viewer/3d_viewer_settings.cpp @@ -240,7 +240,7 @@ bool EDA_3D_VIEWER_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) if( aCfg->Read( k_r, &color.r ) && aCfg->Read( k_g, &color.g ) && aCfg->Read( k_b, &color.b ) ) { - ( *this )[PointerFromString( destKey )] = color; + Set( destKey, color ); } }; diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c1e8c8a494..f2d9a2c2b3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -457,6 +457,7 @@ set( COMMON_SRCS settings/common_settings.cpp settings/json_settings.cpp settings/nested_settings.cpp + settings/parameters.cpp settings/settings_manager.cpp project/board_project_settings.cpp diff --git a/common/project/net_settings.cpp b/common/project/net_settings.cpp index 8272781636..9d91f08bad 100644 --- a/common/project/net_settings.cpp +++ b/common/project/net_settings.cpp @@ -18,6 +18,8 @@ * with this program. If not, see . */ +#include + #include #include #include diff --git a/common/project/project_file.cpp b/common/project/project_file.cpp index 6a3437ec02..207de1760d 100644 --- a/common/project/project_file.cpp +++ b/common/project/project_file.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -142,7 +143,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) libKey << ++libIndex; } - ( *this )[PointerFromString( aDest )] = libs; + Set( aDest, libs ); }; aCfg->SetPath( wxT( "/LibeditFrame" ) ); @@ -168,7 +169,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) eqKey << ++eqIdx; } - ( *this )[PointerFromString( "cvpcb.equivalence_files" )] = eqs; + Set( "cvpcb.equivalence_files", eqs ); } // All CvPcb params that we want to keep have been migrated above @@ -194,7 +195,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) libKey << ++libIdx; } - ( *this )[PointerFromString( "schematic.legacy_lib_list" )] = libs; + Set( "schematic.legacy_lib_list", libs ); } group_blacklist.insert( wxT( "/eeschema" ) ); @@ -219,7 +220,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) txtKey << ++txtIdx; } - ( *this )[PointerFromString( "text_variables" )] = vars; + Set( "text_variables", vars ); } group_blacklist.insert( wxT( "/text_variables" ) ); @@ -273,7 +274,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) key << ++idx; } - ( *this )[PointerFromString( bp + "drc_exclusions" )] = exclusions; + Set( bp + "drc_exclusions", exclusions ); } fromLegacy( aCfg, "AllowMicroVias", bp + "rules.allow_microvias" ); @@ -372,7 +373,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) key << ++idx; } - ( *this )[PointerFromString( bp + "track_widths" )] = widths; + Set( bp + "track_widths", widths ); } { @@ -398,7 +399,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) key << ++idx; } - ( *this )[PointerFromString( bp + "via_dimensions" )] = vias; + Set( bp + "via_dimensions", vias ); } { @@ -428,7 +429,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) key << ++idx; } - ( *this )[PointerFromString( bp + "diff_pair_dimensions" )] = pairs; + Set( bp + "diff_pair_dimensions", pairs ); } group_blacklist.insert( wxT( "/pcbnew" ) ); @@ -461,7 +462,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) } } - ( *this )[PointerFromString( "sheets" )] = arr; + Set( "sheets", arr ); aCfg->SetPath( "/" ); @@ -494,8 +495,7 @@ bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg ) try { - nlohmann::json::json_pointer ptr( "/legacy" + aGroup + "/" + key ); - ( *this )[ptr] = val; + Set( "legacy." + aGroup + "." + key, val ); } catch( ... ) { @@ -543,8 +543,7 @@ bool PROJECT_FILE::SaveToFile( const wxString& aDirectory, bool aForce ) { wxASSERT( m_project ); - ( *this )[PointerFromString( "meta.filename" )] = - m_project->GetProjectName() + "." + ProjectFileExtension; + Set( "meta.filename", m_project->GetProjectName() + "." + ProjectFileExtension ); return JSON_SETTINGS::SaveToFile( aDirectory, aForce ); } diff --git a/common/project/project_local_settings.cpp b/common/project/project_local_settings.cpp index db9d2b2597..095c535f00 100644 --- a/common/project/project_local_settings.cpp +++ b/common/project/project_local_settings.cpp @@ -20,6 +20,7 @@ #include #include +#include #include const int projectLocalSettingsVersion = 3; @@ -230,18 +231,18 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin * LAYER_PADS and LAYER_ZONES added to visibility controls */ - nlohmann::json::json_pointer ptr( "/board/visible_items"_json_pointer ); + std::string ptr( "board.visible_items" ); - if( contains( ptr ) ) + if( Contains( ptr ) ) { - if( ( *this )[ptr].is_array() ) + if( At( ptr ).is_array() ) { - ( *this )[ptr].push_back( LAYER_PADS ); - ( *this )[ptr].push_back( LAYER_ZONES ); + At( ptr ).push_back( LAYER_PADS ); + At( ptr ).push_back( LAYER_ZONES ); } else { - at( "board" ).erase( "visible_items" ); + At( "board" ).erase( "visible_items" ); } } @@ -277,13 +278,13 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin { 40, 33 }, // LAYER_ZONES }; - nlohmann::json::json_pointer ptr( "/board/visible_items"_json_pointer ); + std::string ptr( "board.visible_items" ); - if( contains( ptr ) && at( ptr ).is_array() ) + if( Contains( ptr ) && At( ptr ).is_array() ) { nlohmann::json visible = nlohmann::json::array(); - for( const nlohmann::json& val : at( ptr ) ) + for( const nlohmann::json& val : At( ptr ) ) { try { @@ -300,7 +301,7 @@ PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxStrin } } - at( "board" )["visible_items"] = visible; + At( "board" )["visible_items"] = visible; } return true; @@ -323,8 +324,7 @@ bool PROJECT_LOCAL_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce { wxASSERT( m_project ); - ( *this )[PointerFromString( "meta.filename" )] = - m_project->GetProjectName() + "." + ProjectLocalSettingsFileExtension; + Set( "meta.filename", m_project->GetProjectName() + "." + ProjectLocalSettingsFileExtension ); return JSON_SETTINGS::SaveToFile( aDirectory, aForce ); } diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp index c909a6cb98..e019065201 100644 --- a/common/settings/app_settings.cpp +++ b/common/settings/app_settings.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -175,7 +176,7 @@ bool APP_SETTINGS_BASE::MigrateFromLegacy( wxConfigBase* aCfg ) js.push_back( i ); } - ( *this )[PointerFromString( "printing.layers" ) ] = js; + Set( "printing.layers", js ); } ret &= fromLegacy( aCfg, f + "FirstRunShown", "system.first_run_shown" ); @@ -196,7 +197,7 @@ bool APP_SETTINGS_BASE::MigrateFromLegacy( wxConfigBase* aCfg ) js.push_back( file.ToStdString() ); } - ( *this )[PointerFromString( "system.file_history" )] = js; + Set( "system.file_history", js ); } ret &= migrateWindowConfig( aCfg, f, "window" ); @@ -224,8 +225,8 @@ void APP_SETTINGS_BASE::migrateFindReplace( wxConfigBase* aCfg ) replace_history.push_back( tmp.ToStdString() ); } - ( *this )[PointerFromString( "find_replace.find_history" )] = find_history; - ( *this )[PointerFromString( "find_replace.replace_history" )] = replace_history; + Set( "find_replace.find_history", find_history ); + Set( "find_replace.replace_history", replace_history ); } diff --git a/common/settings/color_settings.cpp b/common/settings/color_settings.cpp index 597c48163f..62a152837a 100644 --- a/common/settings/color_settings.cpp +++ b/common/settings/color_settings.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -223,7 +224,7 @@ COLOR_SETTINGS::COLOR_SETTINGS( wxString aFilename ) : // Fix LAYER_VIA_HOLES color - before version 2, this setting had no effect nlohmann::json::json_pointer ptr( "/board/via_hole"); - ( *this )[ptr] = COLOR4D( 0.5, 0.4, 0, 0.8 ).ToWxString( wxC2S_CSS_SYNTAX ); + ( *m_internals )[ptr] = COLOR4D( 0.5, 0.4, 0, 0.8 ).ToWxString( wxC2S_CSS_SYNTAX ); return true; } ); @@ -285,10 +286,7 @@ bool COLOR_SETTINGS::migrateSchema0to1() return false; } - nlohmann::json::json_pointer board( "/board" ); - nlohmann::json::json_pointer fpedit( "/fpedit" ); - - if( !contains( fpedit ) ) + if( !Contains( "fpedit" ) ) { wxLogTrace( traceSettings, "migrateSchema0to1: %s doesn't have fpedit settings; skipping.", m_filename ); @@ -300,19 +298,18 @@ bool COLOR_SETTINGS::migrateSchema0to1() COLOR_SETTINGS* fpsettings = m_manager->AddNewColorSettings( filename ); // Start out with a clone - nlohmann::json::json_pointer root( "" ); - ( *fpsettings )[root] = at( root ); + fpsettings->Set( "", At( "" ) ); // Footprint editor now just looks at the "board" namespace - ( *fpsettings )[board] = fpsettings->at( fpedit ); + fpsettings->Set( "board", fpsettings->At( "fpedit" ) ); - fpsettings->erase( "fpedit" ); + fpsettings->Internals()->erase( "fpedit" ); fpsettings->Load(); fpsettings->SetName( fpsettings->GetName() + wxS( " " ) + _( "(Footprints)" ) ); m_manager->Save( fpsettings ); // Now we can get rid of our own copy - erase( "fpedit" ); + m_internals->erase( "fpedit" ); return true; } diff --git a/common/settings/common_settings.cpp b/common/settings/common_settings.cpp index dc5d48a6c7..e4d2a9e4db 100644 --- a/common/settings/common_settings.cpp +++ b/common/settings/common_settings.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -300,8 +301,8 @@ bool COMMON_SETTINGS::migrateSchema0to1() try { - mwp = at( mwp_pointer ); - at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "mousewheel_pan" ); + mwp = m_internals->at( mwp_pointer ); + m_internals->At( "input" ).erase( "mousewheel_pan" ); } catch( ... ) { @@ -310,19 +311,19 @@ bool COMMON_SETTINGS::migrateSchema0to1() if( mwp ) { - ( *this )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true; + ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = true; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_SHIFT; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = 0; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = WXK_CONTROL; } else { - ( *this )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false; + ( *m_internals )[nlohmann::json::json_pointer( "/input/horizontal_pan" )] = false; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT; - ( *this )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_h" )] = WXK_CONTROL; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_pan_v" )] = WXK_SHIFT; + ( *m_internals )[nlohmann::json::json_pointer( "/input/scroll_modifier_zoom" )] = 0; } return true; @@ -337,8 +338,8 @@ bool COMMON_SETTINGS::migrateSchema1to2() try { - prefer_selection = at( v1_pointer ); - at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "prefer_select_to_drag" ); + prefer_selection = m_internals->at( v1_pointer ); + m_internals->at( nlohmann::json::json_pointer( "/input"_json_pointer ) ).erase( "prefer_select_to_drag" ); } catch( ... ) { @@ -346,9 +347,9 @@ bool COMMON_SETTINGS::migrateSchema1to2() } if( prefer_selection ) - ( *this )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT; + ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::SELECT; else - ( *this )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY; + ( *m_internals )[nlohmann::json::json_pointer( "/input/mouse_left" )] = MOUSE_DRAG_ACTION::DRAG_ANY; return true; } @@ -374,10 +375,10 @@ bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) auto load_env_vars = [&] () { wxString key, value; long index = 0; - nlohmann::json::json_pointer ptr = PointerFromString( "environment.vars" ); + nlohmann::json::json_pointer ptr = m_internals->PointerFromString( "environment.vars" ); aCfg->SetPath( "EnvironmentVariables" ); - ( *this )[ptr] = nlohmann::json( {} ); + ( *m_internals )[ptr] = nlohmann::json( {} ); while( aCfg->GetNextEntry( key, index ) ) { @@ -394,7 +395,7 @@ bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ptr.push_back( key.ToStdString() ); wxLogTrace( traceSettings, "Migrate Env: %s=%s", ptr.to_string(), value ); - ( *this )[ptr] = value.ToUTF8(); + ( *m_internals )[ptr] = value.ToUTF8(); ptr.pop_back(); } @@ -409,11 +410,10 @@ bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) if( aCfg->Read( "MousewheelPAN", &mousewheel_pan ) && mousewheel_pan ) { - ( *this )[PointerFromString( "input.horizontal_pan" )] = true; - - ( *this )[PointerFromString( "input.scroll_modifier_pan_h" )] = WXK_SHIFT; - ( *this )[PointerFromString( "input.scroll_modifier_pan_v" )] = 0; - ( *this )[PointerFromString( "input.scroll_modifier_zoom" )] = WXK_CONTROL; + Set( "input.horizontal_pan", true ); + Set( "input.scroll_modifier_pan_h", static_cast( WXK_SHIFT ) ); + Set( "input.scroll_modifier_pan_v", 0 ); + Set( "input.scroll_modifier_zoom", static_cast( WXK_CONTROL ) ); } ret &= fromLegacy( aCfg, "AutoPAN", "input.auto_pan" ); @@ -423,11 +423,8 @@ bool COMMON_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ret &= fromLegacy( aCfg, "ZoomNoCenter", "input.center_on_zoom" ); // This was stored inverted in legacy config - if( ret ) - { - auto p = PointerFromString( "input.center_on_zoom" ); - ( *this )[p] = !( *this )[p]; - } + if( OPT value = Get( "input.center_on_zoom" ) ) + Set( "input.center_on_zoom", !( *value ) ); ret &= fromLegacy( aCfg, "OpenGLAntialiasingMode", "graphics.opengl_antialiasing_mode" ); ret &= fromLegacy( aCfg, "CairoAntialiasingMode", "graphics.cairo_antialiasing_mode" ); diff --git a/common/settings/json_settings.cpp b/common/settings/json_settings.cpp index 3ecc3385f9..eba4acfae5 100644 --- a/common/settings/json_settings.cpp +++ b/common/settings/json_settings.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -36,13 +37,32 @@ #include #include - const wxChar* const traceSettings = wxT( "KICAD_SETTINGS" ); + +nlohmann::json::json_pointer JSON_SETTINGS_INTERNALS::PointerFromString( std::string aPath ) +{ + std::replace( aPath.begin(), aPath.end(), '.', '/' ); + aPath.insert( 0, "/" ); + + nlohmann::json::json_pointer p; + + try + { + p = nlohmann::json::json_pointer( aPath ); + } + catch( ... ) + { + wxASSERT_MSG( false, wxT( "Invalid pointer path in PointerFromString!" ) ); + } + + return p; +} + + JSON_SETTINGS::JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation, int aSchemaVersion, bool aCreateIfMissing, bool aCreateIfDefault, bool aWriteFile ) : - nlohmann::json(), m_filename( aFilename ), m_legacy_filename( "" ), m_location( aLocation ), @@ -54,9 +74,11 @@ JSON_SETTINGS::JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation, m_schemaVersion( aSchemaVersion ), m_manager( nullptr ) { + m_internals = std::make_unique(); + try { - ( *this )[PointerFromString( "meta.filename" )] = GetFullFilename(); + m_internals->SetFromString( "meta.filename", GetFullFilename() ); } catch( ... ) { @@ -85,6 +107,30 @@ wxString JSON_SETTINGS::GetFullFilename() const } +nlohmann::json& JSON_SETTINGS::At( const std::string& aPath ) +{ + return m_internals->At( aPath ); +} + + +bool JSON_SETTINGS::Contains( const std::string& aPath ) const +{ + return m_internals->contains( JSON_SETTINGS_INTERNALS::PointerFromString( aPath ) ); +} + + +size_t JSON_SETTINGS::Count( const std::string& aPath ) const +{ + return m_internals->count( JSON_SETTINGS_INTERNALS::PointerFromString( aPath ) ); +} + + +JSON_SETTINGS_INTERNALS* JSON_SETTINGS::Internals() +{ + return m_internals.get(); +} + + void JSON_SETTINGS::Load() { for( auto param : m_params ) @@ -105,7 +151,7 @@ void JSON_SETTINGS::Load() bool JSON_SETTINGS::LoadFromFile( const wxString& aDirectory ) { // First, load all params to default values - clear(); + m_internals->clear(); Load(); bool success = true; @@ -220,16 +266,17 @@ bool JSON_SETTINGS::LoadFromFile( const wxString& aDirectory ) if( fp ) { - *static_cast( this ) = nlohmann::json::parse( fp, nullptr, - /* allow_exceptions = */ true, - /* ignore_comments = */ true ); + *static_cast( m_internals.get() ) = + nlohmann::json::parse( fp, nullptr, + /* allow_exceptions = */ true, + /* ignore_comments = */ true ); // If parse succeeds, check if schema migration is required int filever = -1; try { - filever = at( PointerFromString( "meta.version" ) ).get(); + filever = m_internals->Get( "meta.version" ); } catch( ... ) { @@ -407,7 +454,7 @@ bool JSON_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce ) try { std::stringstream buffer; - buffer << std::setw( 2 ) << *this << std::endl; + buffer << std::setw( 2 ) << *m_internals << std::endl; wxFFileOutputStream fileStream( path.GetFullPath(), "wb" ); @@ -436,13 +483,13 @@ bool JSON_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce ) OPT JSON_SETTINGS::GetJson( const std::string& aPath ) const { - nlohmann::json::json_pointer ptr = PointerFromString( aPath ); + nlohmann::json::json_pointer ptr = m_internals->PointerFromString( aPath ); - if( contains( ptr ) ) + if( m_internals->contains( ptr ) ) { try { - return OPT{ at( ptr ) }; + return OPT{ m_internals->at( ptr ) }; } catch( ... ) { @@ -453,6 +500,55 @@ OPT JSON_SETTINGS::GetJson( const std::string& aPath ) const } +template +OPT JSON_SETTINGS::Get( const std::string& aPath ) const +{ + if( OPT ret = GetJson( aPath ) ) + { + try + { + return ret->get(); + } + catch( ... ) + { + } + } + + return NULLOPT; +} + + +// Instantiate all required templates here to allow reducing scope of json.hpp +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; +template OPT JSON_SETTINGS::Get( const std::string& aPath ) const; + + +template +void JSON_SETTINGS::Set( const std::string& aPath, ValueType aVal ) +{ + m_internals->SetFromString( aPath, aVal ); +} + + +// Instantiate all required templates here to allow reducing scope of json.hpp +template void JSON_SETTINGS::Set( const std::string& aPath, bool aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, double aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, float aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, int aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, unsigned int aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, unsigned long long aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, const char* aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, std::string aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, nlohmann::json aValue ); +template void JSON_SETTINGS::Set( const std::string& aPath, KIGFX::COLOR4D aValue ); + + void JSON_SETTINGS::registerMigration( int aOldSchemaVersion, int aNewSchemaVersion, std::function aMigrator ) { @@ -464,7 +560,7 @@ void JSON_SETTINGS::registerMigration( int aOldSchemaVersion, int aNewSchemaVers bool JSON_SETTINGS::Migrate() { - int filever = at( PointerFromString( "meta.version" ) ).get(); + int filever = m_internals->Get( "meta.version" ); while( filever < m_schemaVersion ) { @@ -482,7 +578,7 @@ bool JSON_SETTINGS::Migrate() wxLogTrace( traceSettings, "Migrated %s from %d to %d", typeid( *this ).name(), filever, pair.first ); filever = pair.first; - ( *this )[PointerFromString( "meta.version" )] = filever; + m_internals->At( "meta.version" ) = filever; } else { @@ -504,30 +600,10 @@ bool JSON_SETTINGS::MigrateFromLegacy( wxConfigBase* aLegacyConfig ) } -nlohmann::json::json_pointer JSON_SETTINGS::PointerFromString( std::string aPath ) -{ - std::replace( aPath.begin(), aPath.end(), '.', '/' ); - aPath.insert( 0, "/" ); - - nlohmann::json::json_pointer p; - - try - { - p = nlohmann::json::json_pointer( aPath ); - } - catch( ... ) - { - wxASSERT_MSG( false, wxT( "Invalid pointer path in PointerFromString!" ) ); - } - - return p; -} - - bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, wxString& aTarget ) { - nlohmann::json::json_pointer ptr = PointerFromString( aPath ); + nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath ); if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() ) { @@ -542,7 +618,7 @@ bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, bool& aTarget ) { - nlohmann::json::json_pointer ptr = PointerFromString( aPath ); + nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath ); if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() ) { @@ -557,7 +633,7 @@ bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, int& aTarget ) { - nlohmann::json::json_pointer ptr = PointerFromString( aPath ); + nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath ); if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() ) { @@ -572,7 +648,7 @@ bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath, unsigned int& aTarget ) { - nlohmann::json::json_pointer ptr = PointerFromString( aPath ); + nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath ); if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() ) { @@ -594,7 +670,7 @@ bool JSON_SETTINGS::fromLegacy( wxConfigBase* aConfig, const std::string& aKey, { try { - ( *this )[PointerFromString( aDest )] = val; + ( *m_internals )[aDest] = val; } catch( ... ) { @@ -630,7 +706,7 @@ bool JSON_SETTINGS::fromLegacyString( wxConfigBase* aConfig, const std::string& { try { - ( *this )[PointerFromString( aDest )] = str.ToUTF8(); + ( *m_internals )[aDest] = str.ToUTF8(); } catch( ... ) { @@ -658,7 +734,7 @@ bool JSON_SETTINGS::fromLegacyColor( wxConfigBase* aConfig, const std::string& a try { nlohmann::json js = nlohmann::json::array( { color.r, color.g, color.b, color.a } ); - ( *this )[PointerFromString( aDest )] = js; + ( *m_internals )[aDest] = js; } catch( ... ) { @@ -714,7 +790,7 @@ template<> OPT JSON_SETTINGS::Get( const std::string& aPath ) const template<> void JSON_SETTINGS::Set( const std::string& aPath, wxString aVal ) { - ( *this )[PointerFromString( std::move( aPath ) ) ] = aVal.ToUTF8(); + ( *m_internals )[aPath] = aVal.ToUTF8(); } // Specializations to allow directly reading/writing wxStrings from JSON diff --git a/common/settings/nested_settings.cpp b/common/settings/nested_settings.cpp index 392be8ddde..1cc049d425 100644 --- a/common/settings/nested_settings.cpp +++ b/common/settings/nested_settings.cpp @@ -20,6 +20,7 @@ #include +#include #include NESTED_SETTINGS::NESTED_SETTINGS( const std::string& aName, int aVersion, JSON_SETTINGS* aParent, @@ -40,18 +41,18 @@ NESTED_SETTINGS::~NESTED_SETTINGS() bool NESTED_SETTINGS::LoadFromFile( const wxString& aDirectory ) { - clear(); + m_internals->clear(); bool success = false; if( m_parent ) { - nlohmann::json::json_pointer ptr = PointerFromString( m_path ); + nlohmann::json::json_pointer ptr = m_internals->PointerFromString( m_path ); - if( m_parent->contains( ptr ) ) + if( m_parent->m_internals->contains( ptr ) ) { try { - update( ( *m_parent )[ptr] ); + m_internals->update( ( *m_parent->m_internals )[ptr] ); wxLogTrace( traceSettings, "Loaded NESTED_SETTINGS %s", GetFilename() ); @@ -71,7 +72,7 @@ bool NESTED_SETTINGS::LoadFromFile( const wxString& aDirectory ) try { - filever = at( PointerFromString( "meta.version" ) ).get(); + filever = m_internals->Get( "meta.version" ); } catch( ... ) { @@ -118,8 +119,9 @@ bool NESTED_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce ) try { - nlohmann::json patch = - nlohmann::json::diff( *this, ( *m_parent )[PointerFromString( m_path )] ); + nlohmann::json patch = nlohmann::json::diff( *m_internals, + m_parent->m_internals->Get( m_path ) ); + modified |= !patch.empty(); } catch( ... ) @@ -132,7 +134,7 @@ bool NESTED_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce ) try { - ( *m_parent )[PointerFromString( m_path ) ].update( *this ); + m_parent->m_internals->At( m_path ).update( *m_internals ); wxLogTrace( traceSettings, "Stored NESTED_SETTINGS %s with schema %d", GetFilename(), m_schemaVersion ); diff --git a/common/settings/parameters.cpp b/common/settings/parameters.cpp new file mode 100644 index 0000000000..c00c02967f --- /dev/null +++ b/common/settings/parameters.cpp @@ -0,0 +1,290 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Jon Evans + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include + +#include +#include +#include + + +template +void PARAM_LAMBDA::Load( JSON_SETTINGS* aSettings, bool aResetIfMissing ) const +{ + if( m_readOnly ) + return; + + if( std::is_same::value ) + { + if( OPT optval = aSettings->GetJson( m_path ) ) + m_setter( *optval ); + else + m_setter( m_default ); + } + else + { + if( OPT optval = aSettings->Get( m_path ) ) + m_setter( *optval ); + else + m_setter( m_default ); + } +} + + +template +bool PARAM_LAMBDA::MatchesFile( JSON_SETTINGS* aSettings ) const +{ + if( std::is_same::value ) + { + if( OPT optval = aSettings->GetJson( m_path ) ) + return *optval == m_getter(); + } + else + { + if( OPT optval = aSettings->Get( m_path ) ) + return *optval == m_getter(); + } + + // Not in file + return false; +} + + +// Instantiate all required templates here to allow reducing scope of json.hpp +template class PARAM_LAMBDA; +template class PARAM_LAMBDA; +template class PARAM_LAMBDA; +template class PARAM_LAMBDA; + + +template +void PARAM_LIST::Load( JSON_SETTINGS* aSettings, bool aResetIfMissing ) const +{ + if( m_readOnly ) + return; + + if( OPT js = aSettings->GetJson( m_path ) ) + { + std::vector val; + + if( js->is_array() ) + { + for( const auto& el : js->items() ) + val.push_back( el.value().get() ); + } + + *m_ptr = val; + } + else if( aResetIfMissing ) + *m_ptr = m_default; +} + + +template +void PARAM_LIST::Store( JSON_SETTINGS* aSettings ) const +{ + nlohmann::json js = nlohmann::json::array(); + + for( const auto& el : *m_ptr ) + js.push_back( el ); + + aSettings->Set( m_path, js ); +} + + +template +bool PARAM_LIST::MatchesFile( JSON_SETTINGS* aSettings ) const +{ + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_array() ) + { + std::vector val; + + for( const auto& el : js->items() ) + val.emplace_back( el.value().get() ); + + return val == *m_ptr; + } + } + + return false; +} + + +template class PARAM_LIST; +template class PARAM_LIST; +template class PARAM_LIST; +template class PARAM_LIST; +template class PARAM_LIST; + + +void PARAM_PATH_LIST::Store( JSON_SETTINGS* aSettings ) const +{ + nlohmann::json js = nlohmann::json::array(); + + for( const auto& el : *m_ptr ) + js.push_back( toFileFormat( el ) ); + + aSettings->Set( m_path, js ); +} + + +bool PARAM_PATH_LIST::MatchesFile( JSON_SETTINGS* aSettings ) const +{ + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_array() ) + { + std::vector val; + + for( const auto& el : js->items() ) + val.emplace_back( fromFileFormat( el.value().get() ) ); + + return val == *m_ptr; + } + } + + return false; +} + + +template +void PARAM_MAP::Load( JSON_SETTINGS* aSettings, bool aResetIfMissing ) const +{ + if( m_readOnly ) + return; + + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_object() ) + { + m_ptr->clear(); + + for( const auto& el : js->items() ) + ( *m_ptr )[el.key()] = el.value().get(); + } + } + else if( aResetIfMissing ) + *m_ptr = m_default; +} + + +template +void PARAM_MAP::Store( JSON_SETTINGS* aSettings ) const +{ + nlohmann::json js( {} ); + + for( const auto& el : *m_ptr ) + js[el.first] = el.second; + + aSettings->Set( m_path, js ); +} + + +template +bool PARAM_MAP::MatchesFile( JSON_SETTINGS* aSettings ) const +{ + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_object() ) + { + if( m_ptr->size() != js->size() ) + return false; + + std::map val; + + for( const auto& el : js->items() ) + val[el.key()] = el.value().get(); + + return val == *m_ptr; + } + } + + return false; +} + + +template class PARAM_MAP; +template class PARAM_MAP; +template class PARAM_MAP; + + +void PARAM_WXSTRING_MAP::Load( JSON_SETTINGS* aSettings, bool aResetIfMissing ) const +{ + if( m_readOnly ) + return; + + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_object() ) + { + m_ptr->clear(); + + for( const auto& el : js->items() ) + { + ( *m_ptr )[wxString( el.key().c_str(), wxConvUTF8 )] = el.value().get(); + } + } + } + else if( aResetIfMissing ) + *m_ptr = m_default; +} + + +void PARAM_WXSTRING_MAP::Store( JSON_SETTINGS* aSettings ) const +{ + nlohmann::json js( {} ); + + for( const auto& el : *m_ptr ) + { + std::string key( el.first.ToUTF8() ); + js[key] = el.second; + } + + aSettings->Set( m_path, js ); +} + + +bool PARAM_WXSTRING_MAP::MatchesFile( JSON_SETTINGS* aSettings ) const +{ + if( OPT js = aSettings->GetJson( m_path ) ) + { + if( js->is_object() ) + { + if( m_ptr->size() != js->size() ) + return false; + + std::map val; + + for( const auto& el : js->items() ) + { + wxString key( el.key().c_str(), wxConvUTF8 ); + val[key] = el.value().get(); + } + + return val == *m_ptr; + } + } + + return false; +} diff --git a/common/settings/settings_manager.cpp b/common/settings/settings_manager.cpp index 2b584b3ad2..a2a2dd9d75 100644 --- a/common/settings/settings_manager.cpp +++ b/common/settings/settings_manager.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -325,7 +326,8 @@ void SETTINGS_MANAGER::SaveColorSettings( COLOR_SETTINGS* aSettings, const std:: } ) != m_color_settings.end() ); - nlohmann::json::json_pointer ptr = JSON_SETTINGS::PointerFromString( aNamespace ); + if( aSettings->IsReadOnly() ) + return; if( !aSettings->Store() ) { @@ -334,17 +336,17 @@ void SETTINGS_MANAGER::SaveColorSettings( COLOR_SETTINGS* aSettings, const std:: return; } - wxASSERT( aSettings->contains( ptr ) ); + wxASSERT( aSettings->Contains( aNamespace ) ); wxLogTrace( traceSettings, "Saving color scheme %s, preserving %s", aSettings->GetFilename(), - aNamespace ); + aNamespace ); - nlohmann::json backup = aSettings->at( ptr ); + nlohmann::json backup = aSettings->At( aNamespace ); wxString path = GetColorSettingsPath(); aSettings->LoadFromFile( path ); - ( *aSettings )[ptr].update( backup ); + ( *aSettings->Internals() )[aNamespace].update( backup ); aSettings->Load(); aSettings->SaveToFile( path, true ); diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp index d6cdf20d5e..0c63ab694d 100644 --- a/eeschema/eeschema_settings.cpp +++ b/eeschema/eeschema_settings.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -405,16 +406,16 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) // Now modify the loaded grid selection, because in earlier versions the grids index was shared // between all applications and started at 1000 mils. There is a 4-position offset between // this index and the possible eeschema grids list that we have to subtract. - nlohmann::json::json_pointer gridSizePtr = PointerFromString( "window.grid.last_size" ); + std::string gridSizePtr = "window.grid.last_size"; - try + if( OPT currentSize = Get( gridSizePtr ) ) { - ( *this )[gridSizePtr] = ( *this )[gridSizePtr].get() - 4; + Set( gridSizePtr, *currentSize - 4 ); } - catch( ... ) + else { // Otherwise, default grid size should be 50 mils; index 1 - ( *this )[gridSizePtr] = 1; + Set( gridSizePtr, 1 ); } ret &= fromLegacy( aCfg, "FootprintPreview", "appearance.footprint_preview" ); @@ -497,8 +498,8 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) } } - ( *this )[PointerFromString( "netlist.custom_command_titles" )] = js_title; - ( *this )[PointerFromString( "netlist.custom_command_paths" )] = js_cmd; + Set( "netlist.custom_command_titles", js_title ); + Set( "netlist.custom_command_paths", js_cmd ); } { @@ -518,11 +519,11 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) if( aCfg->Read( key, &value ) ) { std::string key_utf( key.ToUTF8() ); - js[PointerFromString( key_utf )] = value; + js[JSON_SETTINGS_INTERNALS::PointerFromString( key_utf )] = value; } } - ( *this )[PointerFromString( "field_editor.fields_show" ) ] = js; + Set( "field_editor.fields_show", js ); aCfg->SetPath( "../GroupBy" ); @@ -531,11 +532,11 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) if( aCfg->Read( key, &value ) ) { std::string key_utf( key.ToUTF8() ); - js[PointerFromString( key_utf )] = value; + js[JSON_SETTINGS_INTERNALS::PointerFromString( key_utf )] = value; } } - ( *this )[PointerFromString( "field_editor.fields_group_by" ) ] = js; + Set( "field_editor.fields_group_by", js ); aCfg->SetPath( "../.." ); } @@ -628,7 +629,7 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) Pgm().GetSettingsManager().SaveColorSettings( cs, "schematic" ); - ( *this )[PointerFromString( "appearance.color_theme" )] = cs->GetFilename(); + Set( "appearance.color_theme", cs->GetFilename() ); // LibEdit settings were stored with eeschema. If eeschema is the first app to run, // we need to migrate the LibEdit settings here @@ -668,12 +669,10 @@ std::vector EESCHEMA_SETTINGS::DefaultBo bool EESCHEMA_SETTINGS::migrateBomSettings() { - nlohmann::json::json_pointer ptr = PointerFromString( "bom.plugins" ); - - if( !contains( ptr ) ) + if( !Contains( "bom.plugins" ) ) return false; - wxString list = at( ptr ).get(); + wxString list = Get( "bom.plugins" ).value(); BOM_CFG_PARSER cfg_parser( &m_BomPanel.plugins, TO_UTF8( list ), wxT( "plugins" ) ); @@ -687,7 +686,7 @@ bool EESCHEMA_SETTINGS::migrateBomSettings() } // Parser will have loaded up our array, let's dump it out to JSON - at( ptr ) = bomSettingsToJson(); + At( "bom.plugins" ) = bomSettingsToJson(); return true; } diff --git a/eeschema/erc_settings.cpp b/eeschema/erc_settings.cpp index b3a9275a56..bc14d5d6d9 100644 --- a/eeschema/erc_settings.cpp +++ b/eeschema/erc_settings.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include diff --git a/eeschema/schematic_settings.cpp b/eeschema/schematic_settings.cpp index 6ae5b1a0cb..07c6dd9d55 100644 --- a/eeschema/schematic_settings.cpp +++ b/eeschema/schematic_settings.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/eeschema/symbol_editor/symbol_editor_settings.cpp b/eeschema/symbol_editor/symbol_editor_settings.cpp index dec5c5b9c1..008ad61481 100644 --- a/eeschema/symbol_editor/symbol_editor_settings.cpp +++ b/eeschema/symbol_editor/symbol_editor_settings.cpp @@ -92,16 +92,16 @@ bool SYMBOL_EDITOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) // Now modify the loaded grid selection, because in earlier versions the grids index was shared // between all applications and started at 1000 mils. There is a 4-position offset between // this index and the possible eeschema grids list that we have to subtract. - nlohmann::json::json_pointer gridSizePtr = PointerFromString( "window.grid.last_size" ); + std::string gridSizePtr = "window.grid.last_size"; - try + if( OPT currentSize = Get( gridSizePtr ) ) { - ( *this )[gridSizePtr] = ( *this )[gridSizePtr].get() - 4; + Set( gridSizePtr, *currentSize - 4 ); } - catch( ... ) + else { // Otherwise, default grid size should be 50 mils; index 1 - ( *this )[gridSizePtr] = 1; + Set( gridSizePtr, 1 ); } ret &= fromLegacy( aCfg, "DefaultWireWidth", "defaults.line_width" ); diff --git a/gerbview/gerbview_settings.cpp b/gerbview/gerbview_settings.cpp index 4e781b8ed5..d1cfa51d87 100644 --- a/gerbview/gerbview_settings.cpp +++ b/gerbview/gerbview_settings.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -99,7 +100,7 @@ bool GERBVIEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) aCfg->SetPath( ".." ); - ( *this )[PointerFromString( aDest )] = js; + Set( aDest, js ); }; migrate_files( "drl_files", "system.drill_file_history" ); @@ -109,15 +110,14 @@ bool GERBVIEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) { wxString key; int value = 0; - nlohmann::json::json_pointer ptr = PointerFromString( "gerber_to_pcb_layers" ); - ( *this )[ptr] = nlohmann::json::array(); + At( "gerber_to_pcb_layers" ) = nlohmann::json::array(); for( int i = 0; i < GERBER_DRAWLAYERS_COUNT; i++ ) { key.Printf( "GbrLyr%dToPcb", i ); aCfg->Read( key, &value, UNSELECTED_LAYER ); - ( *this )[ptr].emplace_back( value ); + At( "gerber_to_pcb_layers" ).emplace_back( value ); } } @@ -147,7 +147,7 @@ bool GERBVIEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) Pgm().GetSettingsManager().SaveColorSettings( cs, "gerbview" ); - ( *this )[PointerFromString( "appearance.color_theme" )] = cs->GetFilename(); + Set( "appearance.color_theme", cs->GetFilename() ); return ret; } diff --git a/include/project/board_project_settings.h b/include/project/board_project_settings.h index 20df87c080..666ac667ae 100644 --- a/include/project/board_project_settings.h +++ b/include/project/board_project_settings.h @@ -24,6 +24,9 @@ #include #include +// Can be removed by refactoring PARAM_LAYER_PRESET +#include + /** * This file contains data structures that are saved in the project file or project local settings * file that are specific to PcbNew. This is done so that these structures are available in common. diff --git a/include/settings/color_settings.h b/include/settings/color_settings.h index 1774a744b0..e87f72fdd7 100644 --- a/include/settings/color_settings.h +++ b/include/settings/color_settings.h @@ -21,6 +21,8 @@ #ifndef _COLOR_SETTINGS_H #define _COLOR_SETTINGS_H +#include + #include #include #include diff --git a/include/settings/json_settings.h b/include/settings/json_settings.h index 97c4006a6b..d06a71d14a 100644 --- a/include/settings/json_settings.h +++ b/include/settings/json_settings.h @@ -23,11 +23,11 @@ #include -#include #include #include #include +#include class wxConfigBase; class NESTED_SETTINGS; @@ -52,9 +52,14 @@ enum class SETTINGS_LOC { }; -class JSON_SETTINGS : public nlohmann::json +/// pimpl to allow hiding json.hpp +class JSON_SETTINGS_INTERNALS; + +class JSON_SETTINGS { public: + friend class NESTED_SETTINGS; + JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation, int aSchemaVersion ) : JSON_SETTINGS( aFilename, aLocation, aSchemaVersion, true, true, true ) {} @@ -76,6 +81,17 @@ public: bool IsReadOnly() const { return !m_writeFile; } void SetReadOnly( bool aReadOnly ) { m_writeFile = !aReadOnly; } + /** + * Wrappers for the underlying JSON API so that most consumers don't need json.hpp + * All of these functions take a string that is passed to PointerFromString internally. + */ + + nlohmann::json& At( const std::string& aPath ); + bool Contains( const std::string& aPath ) const; + size_t Count( const std::string& aPath ) const; + + JSON_SETTINGS_INTERNALS* Internals(); + /** * Updates the parameters of this object based on the current JSON document contents */ @@ -130,21 +146,7 @@ c * @return true if the file was saved * @return a value from within this document */ template - OPT Get( const std::string& aPath ) const - { - if( OPT ret = GetJson( aPath ) ) - { - try - { - return ret->get(); - } - catch( ... ) - { - } - } - - return NULLOPT; - } + OPT Get( const std::string& aPath ) const; /** * Stores a value into the JSON document @@ -154,10 +156,7 @@ c * @return true if the file was saved * @param aVal is the value to store */ template - void Set( const std::string& aPath, ValueType aVal ) - { - ( *this )[PointerFromString( aPath ) ] = aVal; - } + void Set( const std::string& aPath, ValueType aVal ); /** * Migrates the schema of this settings from the version in the file to the latest version @@ -199,13 +198,6 @@ c * @return true if the file was saved m_manager = aManager; } - /** - * Builds a JSON pointer based on a given string - * @param aPath is the path in the form "key1.key2.key3" - * @return a JSON pointer that can be used to index into a JSON object - */ - static nlohmann::json::json_pointer PointerFromString( std::string aPath ); - /** * Sets the given string if the given key/path is present * @param aObj is the source object @@ -329,11 +321,10 @@ protected: /// A pointer to the settings manager managing this file (may be null) SETTINGS_MANAGER* m_manager; - /// A list of JSON pointers that are preserved during a read-update-write to disk - std::vector m_preserved_paths; - /// A map of starting schema version to a pair of std::map>> m_migrators; + + std::unique_ptr m_internals; }; // Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API diff --git a/include/settings/json_settings_internals.h b/include/settings/json_settings_internals.h new file mode 100644 index 0000000000..1d8ef6faef --- /dev/null +++ b/include/settings/json_settings_internals.h @@ -0,0 +1,67 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Jon Evans + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef KICAD_JSON_SETTINGS_INTERNALS_H +#define KICAD_JSON_SETTINGS_INTERNALS_H + +// This is a pretty heavy file. Try to use json_fwd.hpp most places. +#include + +class JSON_SETTINGS_INTERNALS : public nlohmann::json +{ + friend class JSON_SETTINGS; + +public: + JSON_SETTINGS_INTERNALS() : + nlohmann::json() + {} + + /** + * Builds a JSON pointer based on a given string + * @param aPath is the path in the form "key1.key2.key3" + * @return a JSON pointer that can be used to index into a JSON object + */ + static nlohmann::json::json_pointer PointerFromString( std::string aPath ); + + template + void SetFromString( const std::string& aPath, ValueType aVal ) + { + // Calls the overload below, which will convert from dotted string to JSON pointer + ( *this )[aPath] = aVal; + } + + template + ValueType Get( const std::string& aPath ) const + { + return at( PointerFromString( aPath ) ).get(); + } + + nlohmann::json& At( const std::string& aPath ) + { + return at( PointerFromString( aPath ) ); + } + + nlohmann::json& operator[]( const std::string& aPath ) + { + return nlohmann::json::operator[]( PointerFromString( aPath ) ); + } +}; + +#endif // KICAD_JSON_SETTINGS_INTERNALS_H diff --git a/include/settings/parameters.h b/include/settings/parameters.h index 19c7a26812..644c0ce7c9 100644 --- a/include/settings/parameters.h +++ b/include/settings/parameters.h @@ -305,26 +305,7 @@ public: m_setter( aSetter ) { } - void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override - { - if( m_readOnly ) - return; - - if( std::is_same::value ) - { - if( OPT optval = aSettings->GetJson( m_path ) ) - m_setter( *optval ); - else - m_setter( m_default ); - } - else - { - if( OPT optval = aSettings->Get( m_path ) ) - m_setter( *optval ); - else - m_setter( m_default ); - } - } + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override; void Store( JSON_SETTINGS* aSettings ) const override { @@ -352,22 +333,7 @@ public: return m_getter() == m_default; } - bool MatchesFile( JSON_SETTINGS* aSettings ) const override - { - if( std::is_same::value ) - { - if( OPT optval = aSettings->GetJson( m_path ) ) - return *optval == m_getter(); - } - else - { - if( OPT optval = aSettings->Get( m_path ) ) - return *optval == m_getter(); - } - - // Not in file - return false; - } + bool MatchesFile( JSON_SETTINGS* aSettings ) const override; private: ValueType m_default; @@ -488,36 +454,9 @@ public: m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override - { - if( m_readOnly ) - return; + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override; - if( OPT js = aSettings->GetJson( m_path ) ) - { - std::vector val; - - if( js->is_array() ) - { - for( const auto& el : js->items() ) - val.push_back( el.value().get() ); - } - - *m_ptr = val; - } - else if( aResetIfMissing ) - *m_ptr = m_default; - } - - void Store( JSON_SETTINGS* aSettings) const override - { - nlohmann::json js = nlohmann::json::array(); - - for( const auto& el : *m_ptr ) - js.push_back( el ); - - aSettings->Set( m_path, js ); - } + void Store( JSON_SETTINGS* aSettings) const override; void SetDefault() override { @@ -529,23 +468,7 @@ public: return *m_ptr == m_default; } - bool MatchesFile( JSON_SETTINGS* aSettings ) const override - { - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_array() ) - { - std::vector val; - - for( const auto& el : js->items() ) - val.emplace_back( el.value().get() ); - - return val == *m_ptr; - } - } - - return false; - } + bool MatchesFile( JSON_SETTINGS* aSettings ) const override; protected: std::vector* m_ptr; @@ -581,33 +504,9 @@ public: ( *m_ptr )[i] = fromFileFormat( ( *m_ptr )[i] ); } - void Store( JSON_SETTINGS* aSettings) const override - { - nlohmann::json js = nlohmann::json::array(); + void Store( JSON_SETTINGS* aSettings) const override; - for( const auto& el : *m_ptr ) - js.push_back( toFileFormat( el ) ); - - aSettings->Set( m_path, js ); - } - - bool MatchesFile( JSON_SETTINGS* aSettings ) const override - { - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_array() ) - { - std::vector val; - - for( const auto& el : js->items() ) - val.emplace_back( fromFileFormat( el.value().get() ) ); - - return val == *m_ptr; - } - } - - return false; - } + bool MatchesFile( JSON_SETTINGS* aSettings ) const override; private: wxString toFileFormat( const wxString& aString ) const @@ -651,34 +550,9 @@ public: m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override - { - if( m_readOnly ) - return; + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override; - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_object() ) - { - m_ptr->clear(); - - for( const auto& el : js->items() ) - ( *m_ptr )[ el.key() ] = el.value().get(); - } - } - else if( aResetIfMissing ) - *m_ptr = m_default; - } - - void Store( JSON_SETTINGS* aSettings) const override - { - nlohmann::json js( {} ); - - for( const auto& el : *m_ptr ) - js[ el.first ] = el.second; - - aSettings->Set( m_path, js ); - } + void Store( JSON_SETTINGS* aSettings) const override; virtual void SetDefault() override { @@ -690,26 +564,7 @@ public: return *m_ptr == m_default; } - bool MatchesFile( JSON_SETTINGS* aSettings ) const override - { - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_object() ) - { - if( m_ptr->size() != js->size() ) - return false; - - std::map val; - - for( const auto& el : js->items() ) - val[ el.key() ] = el.value().get(); - - return val == *m_ptr; - } - } - - return false; - } + bool MatchesFile( JSON_SETTINGS* aSettings ) const override; private: std::map* m_ptr; @@ -732,40 +587,9 @@ public: m_default( aDefault ) { } - void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override - { - if( m_readOnly ) - return; + void Load( JSON_SETTINGS* aSettings, bool aResetIfMissing = true ) const override; - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_object() ) - { - m_ptr->clear(); - - for( const auto& el : js->items() ) - { - ( *m_ptr )[wxString( el.key().c_str(), wxConvUTF8 )] = - el.value().get(); - } - } - } - else if( aResetIfMissing ) - *m_ptr = m_default; - } - - void Store( JSON_SETTINGS* aSettings) const override - { - nlohmann::json js( {} ); - - for( const auto& el : *m_ptr ) - { - std::string key( el.first.ToUTF8() ); - js[ key ] = el.second; - } - - aSettings->Set( m_path, js ); - } + void Store( JSON_SETTINGS* aSettings) const override; virtual void SetDefault() override { @@ -777,29 +601,7 @@ public: return *m_ptr == m_default; } - bool MatchesFile( JSON_SETTINGS* aSettings ) const override - { - if( OPT js = aSettings->GetJson( m_path ) ) - { - if( js->is_object() ) - { - if( m_ptr->size() != js->size() ) - return false; - - std::map val; - - for( const auto& el : js->items() ) - { - wxString key( el.key().c_str(), wxConvUTF8 ); - val[key] = el.value().get(); - } - - return val == *m_ptr; - } - } - - return false; - } + bool MatchesFile( JSON_SETTINGS* aSettings ) const override; private: std::map* m_ptr; diff --git a/include/settings/settings_manager.h b/include/settings/settings_manager.h index 7b47899f53..93658083c0 100644 --- a/include/settings/settings_manager.h +++ b/include/settings/settings_manager.h @@ -21,6 +21,7 @@ #ifndef _SETTINGS_MANAGER_H #define _SETTINGS_MANAGER_H +#include #include #include // for wxString hash #include diff --git a/pcb_calculator/pcb_calculator_settings.cpp b/pcb_calculator/pcb_calculator_settings.cpp index 50c7773a1f..115139ac11 100644 --- a/pcb_calculator/pcb_calculator_settings.cpp +++ b/pcb_calculator/pcb_calculator_settings.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -214,7 +215,9 @@ bool PCB_CALCULATOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ret &= fromLegacy( aCfg, "Att_selection", "attenuators.type" ); { - nlohmann::json::json_pointer ptr = PointerFromString( "attenuators" ); + nlohmann::json::json_pointer ptr = + JSON_SETTINGS_INTERNALS::PointerFromString( "attenuators" ); + const std::array att_names = { "att_pi", "att_tee", "att_bridge", "att_splitter" }; double val = 0; @@ -225,13 +228,13 @@ bool PCB_CALCULATOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ptr.push_back( att ); if( aCfg->Read( "Attenuation", &val ) ) - ( *this )[ptr]["attenuation"] = val; + ( *m_internals )[ptr]["attenuation"] = val; if( aCfg->Read( "Zin", &val ) ) - ( *this )[ptr]["zin"] = val; + ( *m_internals )[ptr]["zin"] = val; if( aCfg->Read( "Zout", &val ) ) - ( *this )[ptr]["zout"] = val; + ( *m_internals )[ptr]["zout"] = val; ptr.pop_back(); aCfg->SetPath( "../.." ); @@ -269,7 +272,9 @@ bool PCB_CALCULATOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ret &= fromLegacy( aCfg, "Transline_selection", "trans_line.selection" ); { - nlohmann::json::json_pointer ptr = PointerFromString( "trans_line" ); + nlohmann::json::json_pointer ptr = + JSON_SETTINGS_INTERNALS::PointerFromString( "trans_line" ); + wxString key; double value = 0; int units = 0; @@ -295,7 +300,7 @@ bool PCB_CALCULATOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) aCfg->Read( key, &units ); ptr.push_back( "units" ); - ( *this )[ptr].push_back( { { dest.ToStdString(), units } } ); + ( *m_internals )[ptr].push_back( { { dest.ToStdString(), units } } ); ptr.pop_back(); } @@ -304,7 +309,7 @@ bool PCB_CALCULATOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) aCfg->Read( key, &value ); ptr.push_back( "values" ); - ( *this )[ptr].push_back( { { dest.ToStdString(), value } } ); + ( *m_internals )[ptr].push_back( { { dest.ToStdString(), value } } ); ptr.pop_back(); } diff --git a/pcb_calculator/pcb_calculator_settings.h b/pcb_calculator/pcb_calculator_settings.h index 1124b497f1..5d2789bbb9 100644 --- a/pcb_calculator/pcb_calculator_settings.h +++ b/pcb_calculator/pcb_calculator_settings.h @@ -25,6 +25,7 @@ #define _PCB_CALCULATOR_SETTINGS_H #include +#include #include class PCB_CALCULATOR_SETTINGS : public APP_SETTINGS_BASE diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index b85e8a4362..6317055121 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -622,10 +623,10 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std: try { - at( "rules" ).erase( "solder_mask_clearance" ); - at( "rules" ).erase( "solder_mask_min_width" ); - at( "rules" ).erase( "solder_paste_clearance" ); - at( "rules" ).erase( "solder_paste_margin_ratio" ); + At( "rules" ).erase( "solder_mask_clearance" ); + At( "rules" ).erase( "solder_mask_min_width" ); + At( "rules" ).erase( "solder_paste_clearance" ); + At( "rules" ).erase( "solder_paste_margin_ratio" ); } catch( ... ) {} @@ -765,19 +766,19 @@ bool BOARD_DESIGN_SETTINGS::migrateSchema0to1() * 1: Mils * 2: Millimetres */ - nlohmann::json::json_pointer units_ptr( "/defaults/dimension_units" ); - nlohmann::json::json_pointer precision_ptr( "/defaults/dimension_precision" ); + std::string units_ptr( "defaults.dimension_units" ); + std::string precision_ptr( "defaults.dimension_precision" ); - if( !( contains( units_ptr ) && contains( precision_ptr ) && - at( units_ptr ).is_number_integer() && - at( precision_ptr ).is_number_integer() ) ) + if( !( Contains( units_ptr ) && Contains( precision_ptr ) && + At( units_ptr ).is_number_integer() && + At( precision_ptr ).is_number_integer() ) ) { // if either is missing or invalid, migration doesn't make sense return true; } - int units = at( units_ptr ).get(); - int precision = at( precision_ptr ).get(); + int units = Get( units_ptr ).value(); + int precision = Get( precision_ptr ).value(); // The enum maps directly to precision if the units is mils int extraDigits = 0; @@ -791,7 +792,7 @@ bool BOARD_DESIGN_SETTINGS::migrateSchema0to1() precision += extraDigits; - ( *this )[precision_ptr] = precision; + Set( precision_ptr, precision ); return true; } @@ -823,36 +824,35 @@ bool BOARD_DESIGN_SETTINGS::LoadFromFile( const wxString& aDirectory ) 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( OPT v = project->Get( bp + "legacy_no_courtyard_defined" ) ) { if( *v ) - ( *this )[PointerFromString( rs + drcName( DRCE_MISSING_COURTYARD ) )] = "error"; + Set( rs + drcName( DRCE_MISSING_COURTYARD ), "error" ); else - ( *this )[PointerFromString( rs + drcName( DRCE_MISSING_COURTYARD ) )] = "ignore"; + Set( rs + drcName( DRCE_MISSING_COURTYARD ), "ignore" ); - project->erase( PointerFromString( bp + "legacy_no_courtyard_defined" ) ); + project->Internals()->erase( m_internals->PointerFromString( bp + "legacy_no_courtyard_defined" ) ); migrated = true; } - if( OPT v = project->Get( PointerFromString( bp + "legacy_courtyards_overlap" ) ) ) + if( OPT v = project->Get( bp + "legacy_courtyards_overlap" ) ) { if( *v ) - ( *this )[PointerFromString( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ) )] = "error"; + Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "error" ); else - ( *this )[PointerFromString( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ) )] = "ignore"; + Set( rs + drcName( DRCE_OVERLAPPING_FOOTPRINTS ), "ignore" ); - project->erase( PointerFromString( bp + "legacy_courtyards_overlap" ) ); + project->Internals()->erase( JSON_SETTINGS_INTERNALS::PointerFromString( bp + "legacy_courtyards_overlap" ) ); migrated = true; } - if( project->contains( "legacy" ) ) + if( Contains( "legacy" ) ) { // This defaults to false for new boards, but version 5.1.x and prior kept the fillets // so we do the same for legacy boards. m_ZoneKeepExternalFillets = true; - project->at( "legacy" ).erase( "pcbnew" ); + project->At( "legacy" ).erase( "pcbnew" ); } // Now that we have everything, we need to load again diff --git a/pcbnew/footprint_editor_settings.cpp b/pcbnew/footprint_editor_settings.cpp index ef096299ba..0a94d4914c 100644 --- a/pcbnew/footprint_editor_settings.cpp +++ b/pcbnew/footprint_editor_settings.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -317,7 +318,7 @@ bool FOOTPRINT_EDITOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) { "", true, F_Fab } } ); - ( *this )[PointerFromString( "design_settings.default_footprint_text_items" )] = textItems; + Set( "design_settings.default_footprint_text_items", textItems ); ret &= fromLegacyString( aCfg, "FpEditorRefDefaultText", "design_settings.default_footprint_text_items.0.0" ); ret &= fromLegacy( aCfg, "FpEditorRefDefaultVisibility", "design_settings.default_footprint_text_items.0.1" ); @@ -371,7 +372,7 @@ bool FOOTPRINT_EDITOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) manager.SaveColorSettings( cs, "board" ); - ( *this )[PointerFromString( "appearance.color_theme" )] = "user_footprints"; + ( *m_internals )[m_internals->PointerFromString( "appearance.color_theme" )] = "user_footprints"; double x, y; f = "ModEditFrame"; @@ -385,8 +386,8 @@ bool FOOTPRINT_EDITOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) x = From_User_Unit( u, x ); y = From_User_Unit( u, y ); - ( *this )[PointerFromString( "window.grid.user_grid_x" )] = StringFromValue( u, x ); - ( *this )[PointerFromString( "window.grid.user_grid_y" )] = StringFromValue( u, y ); + Set( "window.grid.user_grid_x", StringFromValue( u, x ) ); + Set( "window.grid.user_grid_y", StringFromValue( u, y ) ); } return ret; @@ -409,12 +410,12 @@ bool FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1() return false; } - nlohmann::json::json_pointer theme_ptr( "/appearance/color_theme" ); + std::string theme_ptr( "appearance.color_theme" ); - if( !count( theme_ptr ) ) + if( !Count( theme_ptr ) ) return true; - wxString selected = at( theme_ptr ).get(); + wxString selected = At( theme_ptr ).get(); wxString search = selected + wxT( "_footprints" ); for( COLOR_SETTINGS* settings : Pgm().GetSettingsManager().GetColorSettingsList() ) @@ -423,7 +424,7 @@ bool FOOTPRINT_EDITOR_SETTINGS::migrateSchema0to1() { wxLogTrace( traceSettings, "Updating footprint editor theme from %s to %s", selected, search ); - ( *this )[theme_ptr] = search; + Set( theme_ptr, search ); return true; } } diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp index 540307107a..cf9a8caba4 100644 --- a/pcbnew/pcbnew_settings.cpp +++ b/pcbnew/pcbnew_settings.cpp @@ -608,7 +608,7 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) js.push_back( i ); } - ( *this )[PointerFromString( "export_svg.layers" ) ] = js; + Set( "export_svg.layers", js ); } { @@ -639,7 +639,7 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) } } - ( *this )[PointerFromString( "action_plugins" ) ] = js; + Set( "action_plugins", js ); } // @@ -706,10 +706,10 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) const std::string p = "pcbnew.InteractiveRouter."; - ( *this )[PointerFromString( "tools.pns.meta" )] = nlohmann::json( { - { "filename", "pns" }, - { "version", 0 } - } ); + Set( "tools.pns.meta", nlohmann::json( { + { "filename", "pns" }, + { "version", 0 } + } ) ); ret &= fromLegacy( aCfg, p + "Mode", "tools.pns.mode" ); ret &= fromLegacy( aCfg, p + "OptimizerEffort", "tools.pns.effort" ); @@ -728,7 +728,7 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ret &= fromLegacy( aCfg, p + "InlineDragEnabled", "tools.pns.inline_drag" ); // Initialize some new PNS settings to legacy behaviors if coming from legacy - ( *this )[PointerFromString( "tools.pns.fix_all_segments" )] = false; + Set( "tools.pns.fix_all_segments", false ); // Migrate color settings that were stored in the pcbnew config file @@ -766,7 +766,7 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) Pgm().GetSettingsManager().SaveColorSettings( cs, "board" ); - ( *this )[PointerFromString( "appearance.color_theme" )] = cs->GetFilename(); + Set( "appearance.color_theme", cs->GetFilename() ); double x, y; @@ -779,8 +779,8 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) x = From_User_Unit( u, x ); y = From_User_Unit( u, y ); - ( *this )[PointerFromString( "window.grid.user_grid_x" )] = StringFromValue( u, x ); - ( *this )[PointerFromString( "window.grid.user_grid_y" )] = StringFromValue( u, y ); + Set( "window.grid.user_grid_x", StringFromValue( u, x ) ); + Set( "window.grid.user_grid_y", StringFromValue( u, y ) ); } // Footprint editor settings were stored in pcbnew config file. Migrate them here.