From 17d9ff4fe771c05ac780212b20199ea92dcb2f95 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Thu, 14 Aug 2025 15:36:42 -0700 Subject: [PATCH] Add the ability to edit advanced config If you set the env var KICAD_EDIT_ADVANCED_CFG=1, you get a new menu option in the KiCad project window's Edit menu. Lets you modify the settings for advanced config graphically without having to remember/lookup the magic incantation --- common/advanced_config.cpp | 168 ++++++++------- common/config_params.cpp | 16 +- include/advanced_config.h | 20 +- include/config_params.h | 8 +- kicad/CMakeLists.txt | 1 + kicad/dialogs/dialog_edit_cfg.cpp | 344 ++++++++++++++++++++++++++++++ kicad/dialogs/dialog_edit_cfg.h | 44 ++++ kicad/kicad_id.h | 1 + kicad/kicad_manager_frame.cpp | 9 + kicad/kicad_manager_frame.h | 1 + kicad/menubar.cpp | 10 + 11 files changed, 530 insertions(+), 92 deletions(-) create mode 100644 kicad/dialogs/dialog_edit_cfg.cpp create mode 100644 kicad/dialogs/dialog_edit_cfg.h diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index 50e304d307..7c62ad2f3a 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -186,13 +186,13 @@ wxString dumpParamCfg( const PARAM_CFG& aParam ) /** * Dump the configs in the given array to trace. */ -static void dumpCfg( const std::vector& aArray ) +static void dumpCfg( const std::vector>& aArray ) { // only dump if we need to if( !wxLog::IsAllowedTraceMask( AdvancedConfigMask ) ) return; - for( const PARAM_CFG* param : aArray ) + for( const auto& param : aArray ) { wxLogTrace( AdvancedConfigMask, dumpParamCfg( *param ) ); } @@ -326,6 +326,21 @@ const ADVANCED_CFG& ADVANCED_CFG::GetCfg() } +void ADVANCED_CFG::Reload() +{ + loadFromConfigFile(); +} + + +void ADVANCED_CFG::Save() +{ + wxFileName k_advanced = getAdvancedCfgFilename(); + wxFileConfig file_cfg( wxS( "" ), wxS( "" ), k_advanced.GetFullPath() ); + + wxConfigSaveSetups( &file_cfg, m_entries ); +} + + void ADVANCED_CFG::loadFromConfigFile() { const wxFileName k_advanced = getAdvancedCfgFilename(); @@ -355,270 +370,268 @@ void ADVANCED_CFG::loadFromConfigFile() void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) { - std::vector configParams; + m_entries.clear(); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::ExtraFillMargin, + m_entries.push_back( std::make_unique( true, AC_KEYS::ExtraFillMargin, &m_ExtraClearance, m_ExtraClearance, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableCreepageSlot, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableCreepageSlot, &m_EnableCreepageSlot, m_EnableCreepageSlot ) ); - - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCEpsilon, + m_entries.push_back( std::make_unique( true, AC_KEYS::DRCEpsilon, &m_DRCEpsilon, m_DRCEpsilon, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCSliverWidthTolerance, + m_entries.push_back( std::make_unique( true, AC_KEYS::DRCSliverWidthTolerance, &m_SliverWidthTolerance, m_SliverWidthTolerance, 0.01, 0.25 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCSliverMinimumLength, + m_entries.push_back( std::make_unique( true, AC_KEYS::DRCSliverMinimumLength, &m_SliverMinimumLength, m_SliverMinimumLength, 1e-9, 10 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DRCSliverAngleTolerance, + m_entries.push_back( std::make_unique( true, AC_KEYS::DRCSliverAngleTolerance, &m_SliverAngleTolerance, m_SliverAngleTolerance, 1.0, 90.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::HoleWallThickness, + m_entries.push_back( std::make_unique( true, AC_KEYS::HoleWallThickness, &m_HoleWallThickness, m_HoleWallThickness, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::CoroutineStackSize, + m_entries.push_back( std::make_unique( true, AC_KEYS::CoroutineStackSize, &m_CoroutineStackSize, AC_STACK::default_stack, AC_STACK::min_stack, AC_STACK::max_stack ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::UpdateUIEventInterval, + m_entries.push_back( std::make_unique( true, AC_KEYS::UpdateUIEventInterval, &m_UpdateUIEventInterval, m_UpdateUIEventInterval, -1, 100000 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ShowRouterDebugGraphics, + m_entries.push_back( std::make_unique( true, AC_KEYS::ShowRouterDebugGraphics, &m_ShowRouterDebugGraphics, m_ShowRouterDebugGraphics ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableRouterDump, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableRouterDump, &m_EnableRouterDump, m_EnableRouterDump ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::HyperZoom, + m_entries.push_back( std::make_unique( true, AC_KEYS::HyperZoom, &m_HyperZoom, m_HyperZoom ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::CompactFileSave, + m_entries.push_back( std::make_unique( true, AC_KEYS::CompactFileSave, &m_CompactSave, m_CompactSave ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DrawArcAccuracy, + m_entries.push_back( std::make_unique( true, AC_KEYS::DrawArcAccuracy, &m_DrawArcAccuracy, m_DrawArcAccuracy, 0.0, 100000.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::DrawArcCenterStartEndMaxAngle, + m_entries.push_back( std::make_unique( true, AC_KEYS::DrawArcCenterStartEndMaxAngle, &m_DrawArcCenterMaxAngle, m_DrawArcCenterMaxAngle, 0.0, 100000.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::MaxTangentTrackAngleDeviation, + m_entries.push_back( std::make_unique( true, AC_KEYS::MaxTangentTrackAngleDeviation, &m_MaxTangentAngleDeviation, m_MaxTangentAngleDeviation, 0.0, 90.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::MaxTrackLengthToKeep, + m_entries.push_back( std::make_unique( true, AC_KEYS::MaxTrackLengthToKeep, &m_MaxTrackLengthToKeep, m_MaxTrackLengthToKeep, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ExtraZoneDisplayModes, + m_entries.push_back( std::make_unique( true, AC_KEYS::ExtraZoneDisplayModes, &m_ExtraZoneDisplayModes, m_ExtraZoneDisplayModes ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::StrokeTriangulation, + m_entries.push_back( std::make_unique( true, AC_KEYS::StrokeTriangulation, &m_DrawTriangulationOutlines, m_DrawTriangulationOutlines ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::MinPlotPenWidth, + m_entries.push_back( std::make_unique( true, AC_KEYS::MinPlotPenWidth, &m_MinPlotPenWidth, m_MinPlotPenWidth, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::DebugZoneFiller, + m_entries.push_back( std::make_unique( true, AC_KEYS::DebugZoneFiller, &m_DebugZoneFiller, m_DebugZoneFiller ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::DebugPDFWriter, + m_entries.push_back( std::make_unique( true, AC_KEYS::DebugPDFWriter, &m_DebugPDFWriter, m_DebugPDFWriter ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::SmallDrillMarkSize, + m_entries.push_back( std::make_unique( true, AC_KEYS::SmallDrillMarkSize, &m_SmallDrillMarkSize, m_SmallDrillMarkSize, 0.0, 3.0 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::HotkeysDumper, + m_entries.push_back( std::make_unique( true, AC_KEYS::HotkeysDumper, &m_HotkeysDumper, m_HotkeysDumper ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::DrawBoundingBoxes, + m_entries.push_back( std::make_unique( true, AC_KEYS::DrawBoundingBoxes, &m_DrawBoundingBoxes, m_DrawBoundingBoxes ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ShowPcbnewExportNetlist, + m_entries.push_back( std::make_unique( true, AC_KEYS::ShowPcbnewExportNetlist, &m_ShowPcbnewExportNetlist, m_ShowPcbnewExportNetlist ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::Skip3DModelFileCache, + m_entries.push_back( std::make_unique( true, AC_KEYS::Skip3DModelFileCache, &m_Skip3DModelFileCache, m_Skip3DModelFileCache ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::Skip3DModelMemoryCache, + m_entries.push_back( std::make_unique( true, AC_KEYS::Skip3DModelMemoryCache, &m_Skip3DModelMemoryCache, m_Skip3DModelMemoryCache ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::HideVersionFromTitle, + m_entries.push_back( std::make_unique( true, AC_KEYS::HideVersionFromTitle, &m_HideVersionFromTitle, m_HideVersionFromTitle ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ShowRepairSchematic, + m_entries.push_back( std::make_unique( true, AC_KEYS::ShowRepairSchematic, &m_ShowRepairSchematic, m_ShowRepairSchematic ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ShowEventCounters, + m_entries.push_back( std::make_unique( true, AC_KEYS::ShowEventCounters, &m_ShowEventCounters, m_ShowEventCounters ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::AllowManualCanvasScale, + m_entries.push_back( std::make_unique( true, AC_KEYS::AllowManualCanvasScale, &m_AllowManualCanvasScale, m_AllowManualCanvasScale ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::V3DRT_BevelHeight_um, + m_entries.push_back( std::make_unique( true, AC_KEYS::V3DRT_BevelHeight_um, &m_3DRT_BevelHeight_um, m_3DRT_BevelHeight_um, 0, std::numeric_limits::max(), AC_GROUPS::V3D_RayTracing ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::V3DRT_BevelExtentFactor, + m_entries.push_back( std::make_unique( true, AC_KEYS::V3DRT_BevelExtentFactor, &m_3DRT_BevelExtentFactor, m_3DRT_BevelExtentFactor, 0.0, 100.0, AC_GROUPS::V3D_RayTracing ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::Use3DConnexionDriver, + m_entries.push_back( std::make_unique( true, AC_KEYS::Use3DConnexionDriver, &m_Use3DConnexionDriver, m_Use3DConnexionDriver ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::IncrementalConnectivity, + m_entries.push_back( std::make_unique( true, AC_KEYS::IncrementalConnectivity, &m_IncrementalConnectivity, m_IncrementalConnectivity ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::DisambiguationTime, + m_entries.push_back( std::make_unique( true, AC_KEYS::DisambiguationTime, &m_DisambiguationMenuDelay, m_DisambiguationMenuDelay, 50, 10000 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnablePcbDesignBlocks, &m_EnablePcbDesignBlocks, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnablePcbDesignBlocks, &m_EnablePcbDesignBlocks, m_EnablePcbDesignBlocks ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableGenerators, &m_EnableGenerators, m_EnableGenerators ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableAPILogging, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableAPILogging, &m_EnableAPILogging, m_EnableAPILogging ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableLibWithText, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableLibWithText, &m_EnableLibWithText, m_EnableLibWithText ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableLibDir, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableLibDir, &m_EnableLibDir, m_EnableLibDir ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::PcbSelectionVisibilityRatio, + m_entries.push_back( std::make_unique( true, AC_KEYS::PcbSelectionVisibilityRatio, &m_PcbSelectionVisibilityRatio, m_PcbSelectionVisibilityRatio, 0.0, 1.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::FontErrorSize, + m_entries.push_back( std::make_unique( true, AC_KEYS::FontErrorSize, &m_FontErrorSize, m_FontErrorSize, 0.01, 100 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::OcePluginLinearDeflection, + m_entries.push_back( std::make_unique( true, AC_KEYS::OcePluginLinearDeflection, &m_OcePluginLinearDeflection, m_OcePluginLinearDeflection, 0.01, 1.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::OcePluginAngularDeflection, + m_entries.push_back( std::make_unique( true, AC_KEYS::OcePluginAngularDeflection, &m_OcePluginAngularDeflection, m_OcePluginAngularDeflection, 0.01, 360.0 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::TriangulateSimplificationLevel, + m_entries.push_back( std::make_unique( true, AC_KEYS::TriangulateSimplificationLevel, &m_TriangulateSimplificationLevel, m_TriangulateSimplificationLevel, 5, 1000 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::TriangulateMinimumArea, + m_entries.push_back( std::make_unique( true, AC_KEYS::TriangulateMinimumArea, &m_TriangulateMinimumArea, m_TriangulateMinimumArea, 25, 100000 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableCacheFriendlyFracture, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableCacheFriendlyFracture, &m_EnableCacheFriendlyFracture, m_EnableCacheFriendlyFracture ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MaxFileSystemWatchers, + m_entries.push_back( std::make_unique( true, AC_KEYS::MaxFileSystemWatchers, &m_MaxFilesystemWatchers, m_MaxFilesystemWatchers, 0, 2147483647 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MinorSchematicGraphSize, + m_entries.push_back( std::make_unique( true, AC_KEYS::MinorSchematicGraphSize, &m_MinorSchematicGraphSize, m_MinorSchematicGraphSize, 0, 2147483647 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::ResolveTextRecursionDepth, + m_entries.push_back( std::make_unique( true, AC_KEYS::ResolveTextRecursionDepth, &m_ResolveTextRecursionDepth, m_ResolveTextRecursionDepth, 0, 10 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableExtensionSnaps, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableExtensionSnaps, &m_EnableExtensionSnaps, m_EnableExtensionSnaps ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::ExtensionSnapTimeoutMs, + m_entries.push_back( std::make_unique( true, AC_KEYS::ExtensionSnapTimeoutMs, &m_ExtensionSnapTimeoutMs, m_ExtensionSnapTimeoutMs, 0 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ExtensionSnapActivateOnHover, + m_entries.push_back( std::make_unique( true, AC_KEYS::ExtensionSnapActivateOnHover, &m_ExtensionSnapActivateOnHover, m_ExtensionSnapActivateOnHover ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableSnapAnchorsDebug, + m_entries.push_back( std::make_unique( true, AC_KEYS::EnableSnapAnchorsDebug, &m_EnableSnapAnchorsDebug, m_EnableSnapAnchorsDebug ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::MinParallelAngle, + m_entries.push_back( std::make_unique( true, AC_KEYS::MinParallelAngle, &m_MinParallelAngle, m_MinParallelAngle, 0.0, 45.0 ) ); - configParams.push_back( new PARAM_CFG_DOUBLE( true, AC_KEYS::HoleWallPaintingMultiplier, + m_entries.push_back( std::make_unique( true, AC_KEYS::HoleWallPaintingMultiplier, &m_HoleWallPaintingMultiplier, m_HoleWallPaintingMultiplier, 0.1, 100.0 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MsgPanelShowUuids, + m_entries.push_back( std::make_unique( true, AC_KEYS::MsgPanelShowUuids, &m_MsgPanelShowUuids, m_MsgPanelShowUuids ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MaximumThreads, + m_entries.push_back( std::make_unique( true, AC_KEYS::MaximumThreads, &m_MaximumThreads, m_MaximumThreads, 0, 500 ) ); - configParams.push_back( - new PARAM_CFG_INT( true, AC_KEYS::NetInspectorBulkUpdateOptimisationThreshold, + m_entries.push_back( + std::make_unique( true, AC_KEYS::NetInspectorBulkUpdateOptimisationThreshold, &m_NetInspectorBulkUpdateOptimisationThreshold, m_NetInspectorBulkUpdateOptimisationThreshold, 0, 1000 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::ExcludeFromSimulationLineWidth, + m_entries.push_back( std::make_unique( true, AC_KEYS::ExcludeFromSimulationLineWidth, &m_ExcludeFromSimulationLineWidth, m_ExcludeFromSimulationLineWidth, 1, 100 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::GitIconRefreshInterval, + m_entries.push_back( std::make_unique( true, AC_KEYS::GitIconRefreshInterval, &m_GitIconRefreshInterval, m_GitIconRefreshInterval, 0, 100000 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::ConfigurableToolbars, + m_entries.push_back( std::make_unique( true, AC_KEYS::ConfigurableToolbars, &m_ConfigurableToolbars, m_ConfigurableToolbars ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::MaxPastedTextLength, + m_entries.push_back( std::make_unique( true, AC_KEYS::MaxPastedTextLength, &m_MaxPastedTextLength, m_MaxPastedTextLength, 0, 100000 ) ); - configParams.push_back( new PARAM_CFG_INT( true, AC_KEYS::PNSProcessClusterTimeout, + m_entries.push_back( std::make_unique( true, AC_KEYS::PNSProcessClusterTimeout, &m_PNSProcessClusterTimeout, 100, 10, 10000 ) ); // Special case for trace mask setting...we just grab them and set them immediately // Because we even use wxLogTrace inside of advanced config - wxString traceMasks; - configParams.push_back( new PARAM_CFG_WXSTRING( true, AC_KEYS::TraceMasks, &traceMasks, + m_entries.push_back( std::make_unique( true, AC_KEYS::TraceMasks, &m_traceMasks, wxS( "" ) ) ); // Load the config from file - wxConfigLoadSetups( &aCfg, configParams ); + wxConfigLoadSetups( &aCfg, m_entries ); // Now actually set the trace masks - wxStringTokenizer traceMaskTokenizer( traceMasks, wxS( "," ) ); + wxStringTokenizer traceMaskTokenizer( m_traceMasks, wxS( "," ) ); while( traceMaskTokenizer.HasMoreTokens() ) { @@ -626,10 +639,7 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) wxLog::AddTraceMask( mask ); } - dumpCfg( configParams ); - - for( PARAM_CFG* param : configParams ) - delete param; + dumpCfg( m_entries ); wxLogTrace( kicadTraceCoroutineStack, wxT( "Using coroutine stack size %d" ), m_CoroutineStackSize ); diff --git a/common/config_params.cpp b/common/config_params.cpp index 5a721a3f57..4272ac5aac 100644 --- a/common/config_params.cpp +++ b/common/config_params.cpp @@ -30,12 +30,12 @@ #include // for wxConfigBase #include // for wxASSERT -void wxConfigLoadParams( wxConfigBase* aCfg, const std::vector& aList, +void wxConfigLoadParams( wxConfigBase* aCfg, const std::vector>& aList, const wxString& aGroup ) { wxASSERT( aCfg ); - for( PARAM_CFG* param : aList ) + for( const auto& param : aList ) { if( !!param->m_Group ) aCfg->SetPath( param->m_Group ); @@ -50,11 +50,11 @@ void wxConfigLoadParams( wxConfigBase* aCfg, const std::vector& aLis } -void wxConfigLoadSetups( wxConfigBase* aCfg, const std::vector& aList ) +void wxConfigLoadSetups( wxConfigBase* aCfg, const std::vector>& aList ) { wxASSERT( aCfg ); - for( PARAM_CFG* param : aList ) + for( const auto& param : aList ) { if( !param->m_Setup ) continue; @@ -64,12 +64,12 @@ void wxConfigLoadSetups( wxConfigBase* aCfg, const std::vector& aLis } -void wxConfigSaveParams( wxConfigBase* aCfg, const std::vector& aList, +void wxConfigSaveParams( wxConfigBase* aCfg, const std::vector>& aList, const wxString& aGroup ) { wxASSERT( aCfg ); - for( PARAM_CFG* param : aList ) + for( const auto& param : aList ) { if( !!param->m_Group ) aCfg->SetPath( param->m_Group ); @@ -92,11 +92,11 @@ void wxConfigSaveParams( wxConfigBase* aCfg, const std::vector& aLis } -void wxConfigSaveSetups( wxConfigBase* aCfg, const std::vector& aList ) +void wxConfigSaveSetups( wxConfigBase* aCfg, const std::vector>& aList ) { wxASSERT( aCfg ); - for( PARAM_CFG* param : aList ) + for( const auto& param : aList ) { if( !param->m_Setup ) continue; diff --git a/include/advanced_config.h b/include/advanced_config.h index fa80c046bd..99b597a38f 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -24,8 +24,11 @@ #pragma once #include +#include +#include class wxConfigBase; +class PARAM_CFG; /** * @defgroup advanced_config Advanced Configuration Variables @@ -58,7 +61,7 @@ class wxConfigBase; * config files, and why you might want to set them, see #AC_KEYS * */ - +#include class KICOMMON_API ADVANCED_CFG { public: @@ -70,6 +73,18 @@ public: */ static const ADVANCED_CFG& GetCfg(); + /** + * Reload the configuration from the configuration file. + */ + void Reload(); + + /** + * Save the configuration to the configuration file. + */ + void Save(); + + const std::vector>& GetEntries() const { return m_entries; } + ///@{ /// \ingroup advanced_config @@ -766,6 +781,7 @@ public: */ int m_PNSProcessClusterTimeout; + wxString m_traceMasks; ///< Trace masks for wxLogTrace, loaded from the config file. ///@} private: @@ -780,4 +796,6 @@ private: * Load config from the given configuration base. */ void loadSettings( wxConfigBase& aCfg ); + + std::vector> m_entries; }; diff --git a/include/config_params.h b/include/config_params.h index 2ef894fdf2..f159cc8b3b 100644 --- a/include/config_params.h +++ b/include/config_params.h @@ -288,7 +288,7 @@ public: * @param aCfg where to save. * @param aList holds some configuration parameters, not all of which will necessarily be saved. */ -KICOMMON_API void wxConfigSaveSetups( wxConfigBase* aCfg, const std::vector& aList ); +KICOMMON_API void wxConfigSaveSetups( wxConfigBase* aCfg, const std::vector>& aList ); /** * Write @a aList of #PARAM_CFG objects @a aCfg. @@ -300,7 +300,7 @@ KICOMMON_API void wxConfigSaveSetups( wxConfigBase* aCfg, const std::vector& aList, +KICOMMON_API void wxConfigSaveParams( wxConfigBase* aCfg, const std::vector>& aList, const wxString& aGroup ); /** @@ -311,7 +311,7 @@ KICOMMON_API void wxConfigSaveParams( wxConfigBase* aCfg, const std::vector& aList ); +KICOMMON_API void wxConfigLoadSetups( wxConfigBase* aCfg, const std::vector>& aList ); /** * Use @a aList of #PARAM_CFG objects to load configuration values from @a aCfg. @@ -322,7 +322,7 @@ KICOMMON_API void wxConfigLoadSetups( wxConfigBase* aCfg, const std::vector& aList, +KICOMMON_API void wxConfigLoadParams( wxConfigBase* aCfg, const std::vector>& aList, const wxString& aGroup ); diff --git a/kicad/CMakeLists.txt b/kicad/CMakeLists.txt index 628cd4fe97..c09ca778d9 100644 --- a/kicad/CMakeLists.txt +++ b/kicad/CMakeLists.txt @@ -29,6 +29,7 @@ set( KICAD_SRCS dialogs/dialog_update_notice.cpp dialogs/dialog_template_selector_base.cpp dialogs/dialog_template_selector.cpp + dialogs/dialog_edit_cfg.cpp dialogs/panel_kicad_launcher_base.cpp dialogs/panel_kicad_launcher.cpp dialogs/panel_jobset_base.cpp diff --git a/kicad/dialogs/dialog_edit_cfg.cpp b/kicad/dialogs/dialog_edit_cfg.cpp new file mode 100644 index 0000000000..bdf55b6c67 --- /dev/null +++ b/kicad/dialogs/dialog_edit_cfg.cpp @@ -0,0 +1,344 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The 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 "dialog_edit_cfg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static wxString paramValueString( const PARAM_CFG& aParam ) +{ + wxString s; + + try + { + switch( aParam.m_Type ) + { + case paramcfg_id::PARAM_INT: s << *static_cast( aParam ).m_Pt_param; break; + case paramcfg_id::PARAM_DOUBLE: + s = wxString::FromCDouble( *static_cast( aParam ).m_Pt_param ); + break; + case paramcfg_id::PARAM_WXSTRING: s = *static_cast( aParam ).m_Pt_param; break; + case paramcfg_id::PARAM_BOOL: + s << ( *static_cast( aParam ).m_Pt_param ? wxS( "true" ) : wxS( "false" ) ); + break; + default: + wxLogError( wxS( "Unsupported PARAM_CFG variant: " ) + wxString::Format( wxS( "%d" ), aParam.m_Type ) ); + } + } + catch( ... ) + { + wxLogError( wxS( "Error converting parameter value to string." ) ); + } + return s; +} + +static wxString paramDefaultString( const PARAM_CFG& aParam ) +{ + wxString s; + + try + { + switch( aParam.m_Type ) + { + case paramcfg_id::PARAM_INT: s << static_cast( aParam ).m_Default; break; + case paramcfg_id::PARAM_DOUBLE: + s = wxString::FromCDouble( static_cast( aParam ).m_Default ); + break; + case paramcfg_id::PARAM_WXSTRING: s << static_cast( aParam ).m_default; break; + case paramcfg_id::PARAM_BOOL: + s << ( static_cast( aParam ).m_Default ? wxS( "true" ) : wxS( "false" ) ); + break; + default: break; + } + } + catch( ... ) + { + wxLogError( wxS( "Error converting parameter default value to string." ) ); + } + + return s; +} + +static void writeParam( PARAM_CFG& aParam, const wxString& aValue ) +{ + switch( aParam.m_Type ) + { + case paramcfg_id::PARAM_INT: + { + PARAM_CFG_INT& param = static_cast( aParam ); + *param.m_Pt_param = wxAtoi( aValue ); + break; + } + case paramcfg_id::PARAM_BOOL: + { + PARAM_CFG_BOOL& param = static_cast( aParam ); + + if( aValue.CmpNoCase( wxS( "true" ) ) == 0 || aValue.CmpNoCase( wxS( "yes" ) ) == 0 + || aValue.CmpNoCase( wxS( "1" ) ) == 0 ) + { + *param.m_Pt_param = true; + } + else + { + *param.m_Pt_param = false; + } + break; + } + case paramcfg_id::PARAM_DOUBLE: + { + PARAM_CFG_DOUBLE& param = static_cast( aParam ); + aValue.ToCDouble( param.m_Pt_param ); + break; + } + case paramcfg_id::PARAM_WXSTRING: + { + PARAM_CFG_WXSTRING& param = static_cast( aParam ); + *param.m_Pt_param = aValue; + break; + } + default: + wxASSERT_MSG( false, + wxS( "Unsupported PARAM_CFG variant: " ) + wxString::Format( wxS( "%d" ), aParam.m_Type ) ); + } +} + +DIALOG_EDIT_CFG::DIALOG_EDIT_CFG( wxWindow* aParent ) : + wxDialog( aParent, wxID_ANY, _( "Edit Advanced Configuration" ), wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ) +{ + m_cfgFile = wxFileName( PATHS::GetUserSettingsPath(), wxS( "kicad_advanced" ) ); + + m_grid = new wxGrid( this, wxID_ANY ); + m_grid->CreateGrid( 0, 3 ); + m_grid->SetColLabelValue( 0, _( "Key" ) ); + m_grid->SetColLabelValue( 1, _( "Value" ) ); + m_grid->SetColLabelValue( 2, _( "Extant" ) ); + m_grid->HideCol( 2 ); + m_grid->SetRowLabelSize( 0 ); + + loadSettings(); + + wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer(); + wxButton* okButton = new wxButton( this, wxID_OK, _( "OK" ) ); + okButton->SetDefault(); + buttonSizer->AddButton( okButton ); + buttonSizer->Realize(); + + wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); + sizer->Add( m_grid, 1, wxEXPAND | wxALL, 5 ); + sizer->Add( buttonSizer, 0, wxEXPAND | wxALL, 5 ); + SetSizer( sizer ); + + wxDisplay display( wxDisplay::GetFromWindow( this ) ); + wxRect displayRect = display.GetClientArea(); + + SetMinSize( wxSize( 500, 300 ) ); + SetMaxSize( wxSize( displayRect.GetWidth() - 100, displayRect.GetHeight() - 100 ) ); + SetSizeHints( wxSize( 700, 500 ) ); + + m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_EDIT_CFG::OnCellChange, this ); + m_grid->Bind( wxEVT_GRID_CELL_RIGHT_CLICK, &DIALOG_EDIT_CFG::OnCellRightClick, this ); + Bind( wxEVT_SIZE, &DIALOG_EDIT_CFG::OnSize, this ); + + m_contextRow = -1; + + Layout(); + Centre(); + + // CallAfter ensures layout is complete before column adjustment + CallAfter( + [this]() + { + adjustColumnWidths(); + } ); +} + +void DIALOG_EDIT_CFG::adjustColumnWidths() +{ + if( !m_grid || m_grid->GetNumberRows() == 0 ) + return; + + m_grid->Layout(); + + wxSize clientSize = m_grid->GetClientSize(); + int availableWidth = clientSize.GetWidth(); + + // Reserve space for vertical scrollbar when we have many rows + availableWidth -= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X ); + + if( availableWidth < 200 ) + availableWidth = 200; + + int keyWidth = ( availableWidth * 6 ) / 10; + int valueWidth = availableWidth - keyWidth; + + keyWidth = std::max( keyWidth, 100 ); + valueWidth = std::max( valueWidth, 80 ); + + m_grid->SetColSize( 0, keyWidth ); + m_grid->SetColSize( 1, valueWidth ); + + // Prevent wxWidgets auto-sizing from interfering + m_grid->SetColMinimalAcceptableWidth( 50 ); +} + +void DIALOG_EDIT_CFG::OnSize( wxSizeEvent& aEvent ) +{ + aEvent.Skip(); + + if( m_grid && m_grid->GetNumberRows() > 0 ) + { + // Defer column adjustment until resize is complete + CallAfter( + [this]() + { + adjustColumnWidths(); + } ); + } +} + +void DIALOG_EDIT_CFG::loadSettings() +{ + const ADVANCED_CFG& adv = ADVANCED_CFG::GetCfg(); + const std::vector>& entries = adv.GetEntries(); + + for( const auto& entry : entries ) + { + wxString value = paramValueString( *entry ); + wxString def = paramDefaultString( *entry ); + int row = m_grid->GetNumberRows(); + m_grid->AppendRows( 1 ); + m_grid->SetCellValue( row, 0, entry->m_Ident ); + m_grid->SetCellValue( row, 1, value ); + m_grid->SetCellValue( row, 2, def == value ? wxS( "0" ) : wxS( "1" ) ); + m_grid->SetReadOnly( row, 2 ); + updateRowAppearance( row ); + } +} + +void DIALOG_EDIT_CFG::saveSettings() +{ + int rows = m_grid->GetNumberRows(); + + for( int row = 0; row < rows; ++row ) + { + wxString key = m_grid->GetCellValue( row, 0 ); + wxString val = m_grid->GetCellValue( row, 1 ); + wxString ext = m_grid->GetCellValue( row, 2 ); + + if( key.IsEmpty() || ext != wxS( "1" ) ) + continue; + + const std::vector>& entries = ADVANCED_CFG::GetCfg().GetEntries(); + for( auto& entry : entries ) + { + if( entry->m_Ident == key ) + { + writeParam( *entry, val ); + break; + } + } + } + + ADVANCED_CFG& adv = const_cast( ADVANCED_CFG::GetCfg() ); + adv.Save(); +} + +void DIALOG_EDIT_CFG::OnCellChange( wxGridEvent& aEvent ) +{ + int row = aEvent.GetRow(); + int col = aEvent.GetCol(); + + if( col == 0 || col == 1 ) + { + m_grid->SetCellValue( row, 2, wxS( "1" ) ); + updateRowAppearance( row ); + } + + saveSettings(); + + int lastRow = m_grid->GetNumberRows() - 1; + if( !m_grid->GetCellValue( lastRow, 0 ).IsEmpty() || !m_grid->GetCellValue( lastRow, 1 ).IsEmpty() ) + { + m_grid->AppendRows( 1 ); + m_grid->SetCellValue( m_grid->GetNumberRows() - 1, 2, wxS( "0" ) ); + m_grid->SetReadOnly( m_grid->GetNumberRows() - 1, 2 ); + updateRowAppearance( m_grid->GetNumberRows() - 1 ); + } + + aEvent.Skip(); +} + +void DIALOG_EDIT_CFG::OnCellRightClick( wxGridEvent& aEvent ) +{ + m_contextRow = aEvent.GetRow(); + wxMenu menu; + menu.Append( wxID_ANY, _( "Reset to default" ) ); + menu.Bind( wxEVT_MENU, &DIALOG_EDIT_CFG::OnResetDefault, this ); + PopupMenu( &menu ); + menu.Unbind( wxEVT_MENU, &DIALOG_EDIT_CFG::OnResetDefault, this ); +} + +void DIALOG_EDIT_CFG::OnResetDefault( wxCommandEvent& aEvent ) +{ + if( m_contextRow < 0 ) + return; + + wxString key = m_grid->GetCellValue( m_contextRow, 0 ); + wxString def; + + const ADVANCED_CFG& adv = ADVANCED_CFG::GetCfg(); + const std::vector>& entries = adv.GetEntries(); + + for( const auto& entry : entries ) + { + if( entry->m_Ident == key ) + { + def = paramDefaultString( *entry ); + break; + } + } + + m_grid->SetCellValue( m_contextRow, 1, def ); + m_grid->SetCellValue( m_contextRow, 2, wxS( "0" ) ); + updateRowAppearance( m_contextRow ); + saveSettings(); +} + +void DIALOG_EDIT_CFG::updateRowAppearance( int aRow ) +{ + bool ext = m_grid->GetCellValue( aRow, 2 ) == wxS( "1" ); + wxFont font = m_grid->GetCellFont( aRow, 0 ); + font.SetWeight( ext ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL ); + m_grid->SetCellFont( aRow, 0, font ); + m_grid->SetCellFont( aRow, 1, font ); +} \ No newline at end of file diff --git a/kicad/dialogs/dialog_edit_cfg.h b/kicad/dialogs/dialog_edit_cfg.h new file mode 100644 index 0000000000..670989a52a --- /dev/null +++ b/kicad/dialogs/dialog_edit_cfg.h @@ -0,0 +1,44 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright The 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 . + */ + +#pragma once + +#include +#include +#include + +class DIALOG_EDIT_CFG : public wxDialog +{ +public: + DIALOG_EDIT_CFG( wxWindow* aParent ); + +private: + void adjustColumnWidths(); + void OnSize( wxSizeEvent& aEvent ); + void loadSettings(); + void saveSettings(); + void OnCellChange( wxGridEvent& aEvent ); + void OnCellRightClick( wxGridEvent& aEvent ); + void OnResetDefault( wxCommandEvent& aEvent ); + void updateRowAppearance( int aRow ); + + wxGrid* m_grid; + wxFileName m_cfgFile; + int m_contextRow; +}; diff --git a/kicad/kicad_id.h b/kicad/kicad_id.h index eec46362e1..2802624ce1 100644 --- a/kicad/kicad_id.h +++ b/kicad/kicad_id.h @@ -35,6 +35,7 @@ enum id_kicad_frm { ID_LEFT_FRAME = ID_KICAD_MANAGER_START, ID_PROJECT_TREE, ID_EDIT_LOCAL_FILE_IN_TEXT_EDITOR, + ID_EDIT_ADVANCED_CFG, ID_IMPORT_CADSTAR_ARCHIVE_PROJECT, ID_IMPORT_EAGLE_PROJECT, ID_IMPORT_EASYEDA_PROJECT, diff --git a/kicad/kicad_manager_frame.cpp b/kicad/kicad_manager_frame.cpp index 24bbf83161..39ef55609f 100644 --- a/kicad/kicad_manager_frame.cpp +++ b/kicad/kicad_manager_frame.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,7 @@ BEGIN_EVENT_TABLE( KICAD_MANAGER_FRAME, EDA_BASE_FRAME ) // Menu events EVT_MENU( wxID_EXIT, KICAD_MANAGER_FRAME::OnExit ) EVT_MENU( ID_EDIT_LOCAL_FILE_IN_TEXT_EDITOR, KICAD_MANAGER_FRAME::OnOpenFileInTextEditor ) + EVT_MENU( ID_EDIT_ADVANCED_CFG, KICAD_MANAGER_FRAME::OnEditAdvancedCfg ) EVT_MENU( ID_IMPORT_CADSTAR_ARCHIVE_PROJECT, KICAD_MANAGER_FRAME::OnImportCadstarArchiveFiles ) EVT_MENU( ID_IMPORT_EAGLE_PROJECT, KICAD_MANAGER_FRAME::OnImportEagleFiles ) EVT_MENU( ID_IMPORT_EASYEDA_PROJECT, KICAD_MANAGER_FRAME::OnImportEasyEdaFiles ) @@ -971,6 +973,13 @@ void KICAD_MANAGER_FRAME::OnOpenFileInTextEditor( wxCommandEvent& event ) } +void KICAD_MANAGER_FRAME::OnEditAdvancedCfg( wxCommandEvent& WXUNUSED( event ) ) +{ + DIALOG_EDIT_CFG dlg( this ); + dlg.ShowModal(); +} + + void KICAD_MANAGER_FRAME::RefreshProjectTree() { m_leftWin->ReCreateTreePrj(); diff --git a/kicad/kicad_manager_frame.h b/kicad/kicad_manager_frame.h index 990aaa29f2..c4840f386a 100644 --- a/kicad/kicad_manager_frame.h +++ b/kicad/kicad_manager_frame.h @@ -57,6 +57,7 @@ public: void UnarchiveFiles(); void OnOpenFileInTextEditor( wxCommandEvent& event ); + void OnEditAdvancedCfg( wxCommandEvent& event ); void OnFileHistory( wxCommandEvent& event ); void OnClearFileHistory( wxCommandEvent& aEvent ); diff --git a/kicad/menubar.cpp b/kicad/menubar.cpp index 1851b4c137..253fc19df6 100644 --- a/kicad/menubar.cpp +++ b/kicad/menubar.cpp @@ -40,6 +40,7 @@ #include "kicad_id.h" #include #include +#include void KICAD_MANAGER_FRAME::doReCreateMenuBar() @@ -153,6 +154,15 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar() editMenu->Add( ACTIONS::copy ); editMenu->Add( ACTIONS::paste ); + wxString editCfgEnv; + if( wxGetEnv( wxS( "KICAD_EDIT_ADVANCED_CFG" ), &editCfgEnv ); editCfgEnv == wxS( "1" ) ) + { + editMenu->Add( _( "Edit Advanced Config..." ), + _( "Edit advanced settings" ), + ID_EDIT_ADVANCED_CFG, + BITMAPS::editor ); + } + //-- View menu ----------------------------------------------------------- // ACTION_MENU* viewMenu = new ACTION_MENU( false, controlTool );