diff --git a/eeschema/bom_plugins.cpp b/eeschema/bom_plugins.cpp index fff25c7e25..86f444dafd 100644 --- a/eeschema/bom_plugins.cpp +++ b/eeschema/bom_plugins.cpp @@ -23,14 +23,23 @@ */ #include "bom_plugins.h" +#include <paths.h> #include <wx/ffile.h> +#include <wx/log.h> + + +constexpr wxChar BOM_TRACE[] = wxT( "BOM_GENERATORS" ); + BOM_GENERATOR_HANDLER::BOM_GENERATOR_HANDLER( const wxString& aFile ) : m_file( aFile ) { m_isOk = false; - if( !wxFile::Exists( aFile ) ) + if( !wxFile::Exists( m_file.GetFullPath() ) ) + m_file = FindFilePath(); + + if( !wxFile::Exists( m_file.GetFullPath() ) ) { m_info.Printf( _("Script file:\n%s\nnot found. Script not available."), aFile ); return; @@ -77,6 +86,8 @@ BOM_GENERATOR_HANDLER::BOM_GENERATOR_HANDLER( const wxString& aFile ) { m_cmd = m_file.GetFullPath(); } + + wxLogTrace( BOM_TRACE, "%s: extracted command line %s", m_name, m_cmd ); } @@ -147,3 +158,36 @@ wxString BOM_GENERATOR_HANDLER::getOutputExtension( const wxString& aHeader ) return aHeader.SubString( strstart, strend - 1 ); } + + +wxFileName BOM_GENERATOR_HANDLER::FindFilePath() const +{ + if( m_file.IsAbsolute() && m_file.Exists( wxFILE_EXISTS_REGULAR ) ) + { + wxLogTrace( BOM_TRACE, "%s found directly", m_file.GetFullPath() ); + return m_file; + } + + wxFileName test( PATHS::GetUserPluginsPath(), m_file.GetName(), m_file.GetExt() ); + + if( test.Exists( wxFILE_EXISTS_REGULAR ) ) + { + wxLogTrace( BOM_TRACE, "%s found in user plugins path %s", m_file.GetFullName(), + PATHS::GetUserPluginsPath() ); + return test; + } + + test = wxFileName( PATHS::GetStockPluginsPath(), m_file.GetName(), m_file.GetExt() ); + + if( test.Exists( wxFILE_EXISTS_REGULAR ) ) + { + wxLogTrace( BOM_TRACE, "%s found in stock plugins path %s", m_file.GetFullName(), + PATHS::GetStockPluginsPath() ); + return test; + } + + wxLogTrace( BOM_TRACE, "Could not find %s (checked %s, %s)", m_file.GetFullName(), + PATHS::GetUserPluginsPath(), PATHS::GetStockPluginsPath() ); + + return m_file; +} diff --git a/eeschema/bom_plugins.h b/eeschema/bom_plugins.h index a14b035189..32f46f4759 100644 --- a/eeschema/bom_plugins.h +++ b/eeschema/bom_plugins.h @@ -33,6 +33,8 @@ #include <memory> +extern const wxChar BOM_TRACE[]; + /** * Bill of material output generator. * @@ -78,6 +80,16 @@ public: return m_file; } + /** + * Returns the calculated path to the plugin: if the path is already absolute and exists, + * just return it. Otherwise if the path is just a filename, look for that file in the user + * and system plugin directories and return the first one found. If neither is found, just + * return m_file. + * + * @return the full path to the plugin + */ + wxFileName FindFilePath() const; + /** * Return the customisable plugin name. */ @@ -140,7 +152,7 @@ protected: bool m_isOk; ///< Path to the plugin - const wxFileName m_file; + wxFileName m_file; ///< User customisable name wxString m_name; diff --git a/eeschema/dialogs/dialog_bom.cpp b/eeschema/dialogs/dialog_bom.cpp index f5604b21d0..b175ae6655 100644 --- a/eeschema/dialogs/dialog_bom.cpp +++ b/eeschema/dialogs/dialog_bom.cpp @@ -46,131 +46,18 @@ #include <schematic.h> #include <paths.h> -#include <dialogs/dialog_bom_cfg_lexer.h> - #include <wx/filedlg.h> #include <wx/textdlg.h> -static constexpr wxChar BOM_TRACE[] = wxT( "BOM_GENERATORS" ); - wxString s_bomHelpInfo = #include <dialog_bom_help_md.h> ; -using namespace T_BOMCFG_T; // for the BOM_CFG_PARSER parser and its keywords - // BOM "plugins" are not actually plugins. They are external tools // (scripts or executables) called by this dialog. typedef std::vector<BOM_GENERATOR_HANDLER::PTR> BOM_GENERATOR_ARRAY; -/** - * Holds data and functions pertinent to parsing a S-expression file - */ -class BOM_CFG_PARSER : public DIALOG_BOM_CFG_LEXER -{ - BOM_GENERATOR_ARRAY* m_generatorsList; - -public: - BOM_CFG_PARSER( BOM_GENERATOR_ARRAY* aGenerators, const char* aData, const wxString& aSource ); - void Parse(); - -private: - void parseGenerator(); -}; - - -BOM_CFG_PARSER::BOM_CFG_PARSER( BOM_GENERATOR_ARRAY* aGenerators, const char* aLine, - const wxString& aSource ) : - DIALOG_BOM_CFG_LEXER( aLine, aSource ) -{ - m_generatorsList = aGenerators; -} - - -void BOM_CFG_PARSER::Parse() -{ - T token; - - while( ( token = NextTok() ) != T_RIGHT ) - { - if( token == T_EOF) - break; - - if( token == T_LEFT ) - token = NextTok(); - - if( token == T_plugins ) - continue; - - switch( token ) - { - case T_plugin: // Defines a new plugin - parseGenerator(); - break; - - default: -// Unexpected( CurText() ); - break; - } - } -} - - -void BOM_CFG_PARSER::parseGenerator() -{ - NeedSYMBOLorNUMBER(); - wxString name = FromUTF8(); - auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( name ); - - T token; - - while( ( token = NextTok() ) != T_RIGHT ) - { - if( token == T_EOF) - break; - - switch( token ) - { - case T_LEFT: - break; - - case T_cmd: - NeedSYMBOLorNUMBER(); - - if( plugin ) - plugin->SetCommand( FromUTF8() ); - - NeedRIGHT(); - break; - - case T_opts: - NeedSYMBOLorNUMBER(); - - if( plugin ) - { - wxString option = FromUTF8(); - - if( option.StartsWith( "nickname=", &name ) ) - plugin->SetName( name ); - else - plugin->Options().Add( option ); - } - - NeedRIGHT(); - break; - - default: - Unexpected( CurText() ); - break; - } - } - - if( plugin ) - m_generatorsList->push_back( std::move( plugin ) ); -} - - // The main dialog frame to run scripts to build bom class DIALOG_BOM : public DIALOG_BOM_BASE { @@ -251,56 +138,44 @@ DIALOG_BOM::DIALOG_BOM( SCH_EDIT_FRAME* parent ) : // Now all widgets have the size fixed, call FinishDialogSettings finishDialogSettings(); + + m_buttonReset->Bind( wxEVT_BUTTON, + [&]( wxCommandEvent& ) + { + EESCHEMA_SETTINGS* cfg = m_parent->eeconfig(); + + cfg->m_BomPanel.selected_plugin = wxEmptyString; + cfg->m_BomPanel.plugins = cfg->DefaultBomPlugins(); + + installGeneratorsList(); + } ); } + DIALOG_BOM::~DIALOG_BOM() { if( m_helpWindow ) m_helpWindow->Destroy(); - // TODO(JE) maybe unpack this into JSON instead of sexpr + EESCHEMA_SETTINGS* cfg = m_parent->eeconfig(); - // Save the plugin descriptions in config. - // The config stores only one string, so we save the plugins inside a S-expr: - // ( plugins - // ( plugin "plugin name 1" (cmd "command line 1") ) - // ( plugin "plugin name 2" (cmd "command line 2") (opts "option1") (opts "option2") ) - // .... - // ) + cfg->m_BomPanel.plugins.clear(); - STRING_FORMATTER writer; - writer.Print( 0, "(plugins" ); - - for( auto& plugin : m_generators ) + for( const std::unique_ptr<BOM_GENERATOR_HANDLER>& plugin : m_generators ) { - writer.Print( 1, "(plugin %s (cmd %s)", - writer.Quotew( plugin->GetFile().GetFullPath() ).c_str(), - writer.Quotew( plugin->GetCommand() ).c_str() ); + wxString name = plugin->GetName(); + wxFileName path = plugin->GetFile(); - for( unsigned jj = 0; jj < plugin->Options().GetCount(); jj++ ) - { - writer.Print( 1, "(opts %s)", - writer.Quotew( plugin->Options().Item( jj ) ).c_str() ); - } + // handle empty nickname by stripping path + if( name.IsEmpty() ) + name = path.GetName(); - if( !plugin->GetName().IsEmpty() ) - { - wxString option = wxString::Format( "nickname=%s", plugin->GetName() ); + EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS setting( name, path.GetFullPath() ); + setting.command = plugin->GetCommand(); - writer.Print( 1, "(opts %s)", - writer.Quotew( option ).c_str() ); - } - - writer.Print( 0, ")" ); + cfg->m_BomPanel.plugins.emplace_back( setting ); } - writer.Print( 0, ")" ); - - wxString list( FROM_UTF8( writer.GetString().c_str() ) ); - - auto cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ); - - cfg->m_BomPanel.plugins = list.ToStdString(); cfg->m_BomPanel.selected_plugin = m_lbGenerators->GetStringSelection().ToStdString(); } @@ -308,33 +183,36 @@ DIALOG_BOM::~DIALOG_BOM() // Read the initialized plugins in config and fill the list of names void DIALOG_BOM::installGeneratorsList() { - auto cfg = static_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ); + EESCHEMA_SETTINGS* cfg = m_parent->eeconfig(); - wxString list = cfg->m_BomPanel.plugins; wxString active_plugin_name = cfg->m_BomPanel.selected_plugin; - if( !list.IsEmpty() ) + m_generators.clear(); + + for( EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS& setting : cfg->m_BomPanel.plugins ) { - BOM_CFG_PARSER cfg_parser( &m_generators, TO_UTF8( list ), wxT( "plugins" ) ); + auto plugin = std::make_unique<BOM_GENERATOR_HANDLER>( setting.path ); - try - { - cfg_parser.Parse(); - } - catch( const IO_ERROR& ) - { -// wxLogMessage( ioe.What() ); - } - catch( std::runtime_error& e ) - { - DisplayError( nullptr, e.what() ); - } + plugin->SetName( setting.name ); - // Populate list box + if( !setting.command.IsEmpty() ) + plugin->SetCommand( setting.command ); + + m_generators.emplace_back( std::move( plugin ) ); + } + + m_lbGenerators->Clear(); + + if( !m_generators.empty() ) + { for( unsigned ii = 0; ii < m_generators.size(); ii++ ) { - if( !m_generators[ii]->GetFile().Exists( wxFILE_EXISTS_REGULAR ) ) + if( !m_generators[ii]->FindFilePath().Exists( wxFILE_EXISTS_REGULAR ) ) + { + wxLogTrace( BOM_TRACE, "BOM plugin %s not found", + m_generators[ii]->FindFilePath().GetFullName() ); continue; + } m_lbGenerators->Append( m_generators[ii]->GetName() ); @@ -343,53 +221,6 @@ void DIALOG_BOM::installGeneratorsList() } } - if( m_generators.empty() ) // No plugins found? - { - // Load plugins from the default locations - std::vector<wxString> pluginPaths = { -#if defined(__WXGTK__) - "/usr/share/kicad/plugins", - "/usr/local/share/kicad/plugins", -#elif defined(__WXMSW__) - wxString::Format( "%s\\scripting\\plugins", Pgm().GetExecutablePath() ), -#elif defined(__WXMAC__) - wxString::Format( "%s/plugins", PATHS::GetOSXKicadDataDir() ), -#endif - }; - - wxFileName pluginPath; - - for( const auto& path : pluginPaths ) - { - wxDir dir( path ); - - if( !dir.IsOpened() ) - continue; - - pluginPath.AssignDir( dir.GetName() ); - wxString fileName; - bool cont = dir.GetFirst( &fileName, wxFileSelectorDefaultWildcardStr, wxDIR_FILES ); - - while( cont ) - { - try - { - wxLogTrace( BOM_TRACE,"Checking if %s is a BOM generator", fileName ); - - if( BOM_GENERATOR_HANDLER::IsValidGenerator( fileName ) ) - { - pluginPath.SetFullName( fileName ); - addGenerator( pluginPath.GetFullPath() ); - } - } - catch( ... ) { /* well, no big deal */ } - - cont = dir.GetNext( &fileName ); - } - } - } - - pluginInit(); } diff --git a/eeschema/dialogs/dialog_bom_base.cpp b/eeschema/dialogs/dialog_bom_base.cpp index 0a15347995..4e9f017b57 100644 --- a/eeschema/dialogs/dialog_bom_base.cpp +++ b/eeschema/dialogs/dialog_bom_base.cpp @@ -109,6 +109,17 @@ DIALOG_BOM_BASE::DIALOG_BOM_BASE( wxWindow* parent, wxWindowID id, const wxStrin m_staticline = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bMainSizer->Add( m_staticline, 0, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); + wxBoxSizer* bSizer8; + bSizer8 = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonReset = new wxButton( this, wxID_ANY, _("Reset to Defaults"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonReset->SetToolTip( _("Reset the list of BOM generator scripts to the default settings") ); + + bSizer8->Add( m_buttonReset, 0, wxALL, 5 ); + + + bSizer8->Add( 0, 0, 1, wxEXPAND, 5 ); + m_sdbSizer = new wxStdDialogButtonSizer(); m_sdbSizerOK = new wxButton( this, wxID_OK ); m_sdbSizer->AddButton( m_sdbSizerOK ); @@ -118,7 +129,10 @@ DIALOG_BOM_BASE::DIALOG_BOM_BASE( wxWindow* parent, wxWindowID id, const wxStrin m_sdbSizer->AddButton( m_sdbSizerHelp ); m_sdbSizer->Realize(); - bMainSizer->Add( m_sdbSizer, 0, wxEXPAND|wxALL, 5 ); + bSizer8->Add( m_sdbSizer, 0, wxEXPAND|wxALL, 5 ); + + + bMainSizer->Add( bSizer8, 0, wxEXPAND, 5 ); this->SetSizer( bMainSizer ); diff --git a/eeschema/dialogs/dialog_bom_base.fbp b/eeschema/dialogs/dialog_bom_base.fbp index 7d5900ae96..d032e248e1 100644 --- a/eeschema/dialogs/dialog_bom_base.fbp +++ b/eeschema/dialogs/dialog_bom_base.fbp @@ -920,22 +920,115 @@ </object> <object class="sizeritem" expanded="1"> <property name="border">5</property> - <property name="flag">wxEXPAND|wxALL</property> + <property name="flag">wxEXPAND</property> <property name="proportion">0</property> - <object class="wxStdDialogButtonSizer" expanded="1"> - <property name="Apply">0</property> - <property name="Cancel">1</property> - <property name="ContextHelp">0</property> - <property name="Help">1</property> - <property name="No">0</property> - <property name="OK">1</property> - <property name="Save">0</property> - <property name="Yes">0</property> + <object class="wxBoxSizer" expanded="1"> <property name="minimum_size"></property> - <property name="name">m_sdbSizer</property> - <property name="permission">protected</property> - <event name="OnHelpButtonClick">OnHelp</event> - <event name="OnOKButtonClick">OnRunGenerator</event> + <property name="name">bSizer8</property> + <property name="orient">wxHORIZONTAL</property> + <property name="permission">none</property> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxALL</property> + <property name="proportion">0</property> + <object class="wxButton" expanded="1"> + <property name="BottomDockable">1</property> + <property name="LeftDockable">1</property> + <property name="RightDockable">1</property> + <property name="TopDockable">1</property> + <property name="aui_layer"></property> + <property name="aui_name"></property> + <property name="aui_position"></property> + <property name="aui_row"></property> + <property name="best_size"></property> + <property name="bg"></property> + <property name="bitmap"></property> + <property name="caption"></property> + <property name="caption_visible">1</property> + <property name="center_pane">0</property> + <property name="close_button">1</property> + <property name="context_help"></property> + <property name="context_menu">1</property> + <property name="current"></property> + <property name="default">0</property> + <property name="default_pane">0</property> + <property name="disabled"></property> + <property name="dock">Dock</property> + <property name="dock_fixed">0</property> + <property name="docking">Left</property> + <property name="enabled">1</property> + <property name="fg"></property> + <property name="floatable">1</property> + <property name="focus"></property> + <property name="font"></property> + <property name="gripper">0</property> + <property name="hidden">0</property> + <property name="id">wxID_ANY</property> + <property name="label">Reset to Defaults</property> + <property name="margins"></property> + <property name="markup">0</property> + <property name="max_size"></property> + <property name="maximize_button">0</property> + <property name="maximum_size"></property> + <property name="min_size"></property> + <property name="minimize_button">0</property> + <property name="minimum_size"></property> + <property name="moveable">1</property> + <property name="name">m_buttonReset</property> + <property name="pane_border">1</property> + <property name="pane_position"></property> + <property name="pane_size"></property> + <property name="permission">protected</property> + <property name="pin_button">1</property> + <property name="pos"></property> + <property name="position"></property> + <property name="pressed"></property> + <property name="resize">Resizable</property> + <property name="show">1</property> + <property name="size"></property> + <property name="style"></property> + <property name="subclass">; ; forward_declare</property> + <property name="toolbar_pane">0</property> + <property name="tooltip">Reset the list of BOM generator scripts to the default settings</property> + <property name="validator_data_type"></property> + <property name="validator_style">wxFILTER_NONE</property> + <property name="validator_type">wxDefaultValidator</property> + <property name="validator_variable"></property> + <property name="window_extra_style"></property> + <property name="window_name"></property> + <property name="window_style"></property> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND</property> + <property name="proportion">1</property> + <object class="spacer" expanded="1"> + <property name="height">0</property> + <property name="permission">protected</property> + <property name="width">0</property> + </object> + </object> + <object class="sizeritem" expanded="1"> + <property name="border">5</property> + <property name="flag">wxEXPAND|wxALL</property> + <property name="proportion">0</property> + <object class="wxStdDialogButtonSizer" expanded="1"> + <property name="Apply">0</property> + <property name="Cancel">1</property> + <property name="ContextHelp">0</property> + <property name="Help">1</property> + <property name="No">0</property> + <property name="OK">1</property> + <property name="Save">0</property> + <property name="Yes">0</property> + <property name="minimum_size"></property> + <property name="name">m_sdbSizer</property> + <property name="permission">protected</property> + <event name="OnHelpButtonClick">OnHelp</event> + <event name="OnOKButtonClick">OnRunGenerator</event> + </object> + </object> </object> </object> </object> diff --git a/eeschema/dialogs/dialog_bom_base.h b/eeschema/dialogs/dialog_bom_base.h index 581ffa587e..00c3a3ba73 100644 --- a/eeschema/dialogs/dialog_bom_base.h +++ b/eeschema/dialogs/dialog_bom_base.h @@ -57,6 +57,7 @@ class DIALOG_BOM_BASE : public DIALOG_SHIM wxTextCtrl* m_textCtrlCommand; wxCheckBox* m_checkBoxShowConsole; wxStaticLine* m_staticline; + wxButton* m_buttonReset; wxStdDialogButtonSizer* m_sdbSizer; wxButton* m_sdbSizerOK; wxButton* m_sdbSizerCancel; diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp index 2e34a0ab9a..41c4ee4029 100644 --- a/eeschema/eeschema_settings.cpp +++ b/eeschema/eeschema_settings.cpp @@ -21,6 +21,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ +#include <functional> + +#include <dialogs/dialog_bom_cfg_lexer.h> #include <eeschema_settings.h> #include <layers_id_colors_and_visibility.h> #include <symbol_editor_settings.h> @@ -32,8 +35,22 @@ #include <widgets/ui_common.h> #include <default_values.h> // For some default values +using namespace T_BOMCFG_T; // for the BOM_CFG_PARSER parser and its keywords + ///! Update the schema version whenever a migration is required -const int eeschemaSchemaVersion = 0; +const int eeschemaSchemaVersion = 1; + +/// Default value for bom.plugins +const nlohmann::json defaultBomPlugins = { + { + { "name", "bom_csv_grouped_by_value" }, + { "path", "bom_csv_grouped_by_value.py" } + }, + { + { "name", "bom_csv_grouped_by_value_with_fp" }, + { "path", "bom_csv_grouped_by_value_with_fp.py" } + }, + }; EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : @@ -184,8 +201,18 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : m_params.emplace_back( new PARAM<wxString>( "bom.selected_plugin", &m_BomPanel.selected_plugin, "" ) ); - m_params.emplace_back( new PARAM<wxString>( "bom.plugins", - &m_BomPanel.plugins, "" ) ); + m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "bom.plugins", + std::bind( &EESCHEMA_SETTINGS::bomSettingsToJson, this ), + [&]( const nlohmann::json& aObj ) + { + if( !aObj.is_array() ) + return; + + const nlohmann::json& list = aObj.empty() ? defaultBomPlugins : aObj; + + m_BomPanel.plugins = bomSettingsFromJson( list ); + }, + defaultBomPlugins ) ); m_params.emplace_back( new PARAM<bool>( "page_settings.export_paper", &m_PageSettings.export_paper, false ) ); @@ -352,6 +379,15 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : m_params.emplace_back( new PARAM<wxString>( "system.last_symbol_lib_dir", &m_lastSymbolLibDir, "" ) ); + + // Migrations + + registerMigration( 0, 1, + [&]() -> bool + { + // Version 0 to 1: BOM plugin settings moved from sexpr to JSON + return migrateBomSettings(); + } ); } @@ -415,6 +451,8 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) ret &= fromLegacyString( aCfg, "bom_plugin_selected", "bom.selected_plugin" ); ret &= fromLegacyString( aCfg, "bom_plugins", "bom.plugins" ); + migrateBomSettings(); + ret &= fromLegacyString( aCfg, "SymbolFieldsShownColumns", "edit_sch_component.visible_columns" ); @@ -595,3 +633,191 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) return ret; } + + +/** + * Used for parsing legacy-format bom plugin configurations. Only used for migrating into + * EESCHEMA_SETTINGS JSON format. + */ +class BOM_CFG_PARSER : public DIALOG_BOM_CFG_LEXER +{ + std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS>* m_pluginList; + +public: + BOM_CFG_PARSER( std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS>* aPluginList, + const char* aData, const wxString& aSource ); + + void Parse(); + +private: + void parseGenerator(); +}; + + +std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS> EESCHEMA_SETTINGS::DefaultBomPlugins() +{ + return bomSettingsFromJson( defaultBomPlugins ); +} + + +bool EESCHEMA_SETTINGS::migrateBomSettings() +{ + nlohmann::json::json_pointer ptr = PointerFromString( "bom.plugins" ); + + if( !contains( ptr ) ) + return false; + + wxString list = at( ptr ).get<wxString>(); + + BOM_CFG_PARSER cfg_parser( &m_BomPanel.plugins, TO_UTF8( list ), wxT( "plugins" ) ); + + try + { + cfg_parser.Parse(); + } + catch( const IO_ERROR& ) + { + return false; + } + + // Parser will have loaded up our array, let's dump it out to JSON + at( ptr ) = bomSettingsToJson(); + + return true; +} + + +nlohmann::json EESCHEMA_SETTINGS::bomSettingsToJson() const +{ + nlohmann::json js = nlohmann::json::array(); + + for( const BOM_PLUGIN_SETTINGS& plugin : m_BomPanel.plugins ) + { + nlohmann::json pluginJson; + + pluginJson["name"] = plugin.name.ToUTF8(); + pluginJson["path"] = plugin.path.ToUTF8(); + pluginJson["command"] = plugin.command.ToUTF8(); + + js.push_back( pluginJson ); + } + + return js; +} + + +std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS> EESCHEMA_SETTINGS::bomSettingsFromJson( + const nlohmann::json& aObj ) +{ + std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS> ret; + + wxASSERT( aObj.is_array() ); + + for( const nlohmann::json& entry : aObj ) + { + if( entry.empty() || !entry.is_object() ) + continue; + + if( !entry.contains( "name" ) || !entry.contains( "path" ) ) + continue; + + BOM_PLUGIN_SETTINGS plugin( entry.at( "name" ).get<wxString>(), + entry.at( "path" ).get<wxString>() ); + + if( entry.contains( "command" ) ) + plugin.command = entry.at( "command" ).get<wxString>(); + + ret.emplace_back( plugin ); + } + + return ret; +} + + +BOM_CFG_PARSER::BOM_CFG_PARSER( std::vector<EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS>* aPluginList, + const char* aLine, const wxString& aSource ) : + DIALOG_BOM_CFG_LEXER( aLine, aSource ) +{ + wxASSERT( aPluginList ); + m_pluginList = aPluginList; +} + + +void BOM_CFG_PARSER::Parse() +{ + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_plugins ) + continue; + + switch( token ) + { + case T_plugin: // Defines a new plugin + parseGenerator(); + break; + + default: +// Unexpected( CurText() ); + break; + } + } +} + + +void BOM_CFG_PARSER::parseGenerator() +{ + wxString str; + EESCHEMA_SETTINGS::BOM_PLUGIN_SETTINGS settings; + + NeedSYMBOLorNUMBER(); + settings.path = FromUTF8(); + + T token; + + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_LEFT: + break; + + case T_cmd: + NeedSYMBOLorNUMBER(); + + settings.command = FromUTF8(); + + NeedRIGHT(); + break; + + case T_opts: + { + NeedSYMBOLorNUMBER(); + + wxString option = FromUTF8(); + + if( option.StartsWith( "nickname=", &str ) ) + settings.name = str; + + NeedRIGHT(); + break; + } + + default: + Unexpected( CurText() ); + break; + } + } + + m_pluginList->emplace_back( settings ); +} diff --git a/eeschema/eeschema_settings.h b/eeschema/eeschema_settings.h index e837617d51..a30db095ef 100644 --- a/eeschema/eeschema_settings.h +++ b/eeschema/eeschema_settings.h @@ -57,6 +57,20 @@ public: bool align_to_grid; }; + struct BOM_PLUGIN_SETTINGS + { + BOM_PLUGIN_SETTINGS() = default; + + BOM_PLUGIN_SETTINGS( const wxString& aName, const wxString& aPath ) : + name( aName ), + path( aPath ) + {} + + wxString name; + wxString path; + wxString command; + }; + struct DRAWING { int default_bus_thickness; @@ -126,7 +140,7 @@ public: struct PANEL_BOM { wxString selected_plugin; - wxString plugins; + std::vector<BOM_PLUGIN_SETTINGS> plugins; }; struct PANEL_FIELD_EDITOR @@ -188,6 +202,8 @@ public: virtual bool MigrateFromLegacy( wxConfigBase* aLegacyConfig ) override; + static std::vector<BOM_PLUGIN_SETTINGS> DefaultBomPlugins(); + APPEARANCE m_Appearance; AUTOPLACE_FIELDS m_AutoplaceFields; @@ -223,6 +239,14 @@ public: protected: virtual std::string getLegacyFrameName() const override { return "SchematicFrame"; } + +private: + + bool migrateBomSettings(); + + nlohmann::json bomSettingsToJson() const; + + static std::vector<BOM_PLUGIN_SETTINGS> bomSettingsFromJson( const nlohmann::json& aObj ); };