/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013-2017 Jean-Pierre Charras, jp.charras@wanadoo.fr * Copyright (C) 2013 Wayne Stambaugh * Copyright (C) 1992-2022 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /* Functions relative to the dialog creating the netlist for Pcbnew. The dialog is a notebook * with 4 fixed netlist formats: * Pcbnew * ORCADPCB2 * CADSTAR * SPICE * and up to CUSTOMPANEL_COUNTMAX user programmable formats. These external converters are * referred to as plugins, but they are really just external binaries. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CUSTOMPANEL_COUNTMAX 8 // Max number of netlist plugins /* panel (notebook page) identifiers * if modified, fix also the DEFINED_NETLISTS_COUNT value * PANEL_NETLIST_INDEX values are used as index in m_PanelNetType[] */ enum PANEL_NETLIST_INDEX { PANELPCBNEW = 0, /* Handle Netlist format Pcbnew */ PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */ PANELALLEGRO, /* Handle Netlist format Allegro */ PANELCADSTAR, /* Handle Netlist format CadStar */ PANELPADS, /* Handle Netlist format PADS */ PANELSPICE, /* Handle Netlist format Spice */ PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */ PANELCUSTOMBASE /* First auxiliary panel (custom netlists). * others use PANELCUSTOMBASE+1, PANELCUSTOMBASE+2.. */ }; // The count of panels for internally defined netlist formats // (the max count of panel is DEFINED_NETLISTS_COUNT+CUSTOMPANEL_COUNTMAX) #define DEFINED_NETLISTS_COUNT 6 /* wxPanels for creating the NoteBook pages for each netlist format: */ class EXPORT_NETLIST_PAGE : public wxPanel { public: /** * Create a setup page for one netlist format. * * Used in Netlist format dialog box creation. * * @param parent is the wxNotebook parent. * @param title is the title of the notebook page. * @param id_NetType is the netlist ID type. */ EXPORT_NETLIST_PAGE( wxNotebook* aParent, const wxString& aTitle, NETLIST_TYPE_ID aIdNetType, bool aCustom ); ~EXPORT_NETLIST_PAGE() { }; /** * @return the name of the netlist format for this page. */ const wxString GetPageNetFmtName() { return m_pageNetFmtName; } NETLIST_TYPE_ID m_IdNetType; // opt to reformat passive component values (e.g. 1M -> 1Meg): wxCheckBox* m_CurSheetAsRoot; wxCheckBox* m_SaveAllVoltages; wxCheckBox* m_SaveAllCurrents; wxCheckBox* m_SaveAllDissipations; wxCheckBox* m_SaveAllEvents; wxCheckBox* m_RunExternalSpiceCommand; wxTextCtrl* m_CommandStringCtrl; wxTextCtrl* m_TitleStringCtrl; wxBoxSizer* m_LeftBoxSizer; wxBoxSizer* m_RightBoxSizer; wxBoxSizer* m_RightOptionsBoxSizer; wxBoxSizer* m_LowBoxSizer; bool IsCustom() const { return m_custom; } private: wxString m_pageNetFmtName; bool m_custom; }; /* Dialog frame for creating netlists */ class DIALOG_EXPORT_NETLIST : public DIALOG_EXPORT_NETLIST_BASE { public: DIALOG_EXPORT_NETLIST( SCH_EDIT_FRAME* parent ); ~DIALOG_EXPORT_NETLIST() { }; private: void InstallCustomPages(); EXPORT_NETLIST_PAGE* AddOneCustomPage( const wxString& aTitle, const wxString& aCommandString, NETLIST_TYPE_ID aNetTypeId ); void InstallPageSpice(); void InstallPageSpiceModel(); bool TransferDataFromWindow() override; bool NetlistUpdateOpt(); void updateGeneratorButtons(); // Called when changing the notebook page (and therefore the current netlist format) void OnNetlistTypeSelection( wxNotebookEvent& event ) override; /** * Add a new panel for a new netlist plugin. */ void OnAddGenerator( wxCommandEvent& event ) override; /** * Remove a panel relative to a netlist plugin. */ void OnDelGenerator( wxCommandEvent& event ) override; /** * Write the current netlist options setup in the configuration. */ void WriteCurrentNetlistSetup(); /** * Return the filename extension and the wildcard string for this page or a void name * if there is no default name. * * @param aType is the netlist type ( NET_TYPE_PCBNEW ... ). * @param aExt [in] is a holder for the netlist file extension. * @param aWildCard [in] is a holder for netlist file dialog wildcard. * @return true for known netlist type, false for custom formats. */ bool FilenamePrms( NETLIST_TYPE_ID aType, wxString* aExt, wxString* aWildCard ); public: SCH_EDIT_FRAME* m_Parent; EXPORT_NETLIST_PAGE* m_PanelNetType[DEFINED_NETLISTS_COUNT + CUSTOMPANEL_COUNTMAX]; }; class NETLIST_DIALOG_ADD_GENERATOR : public NETLIST_DIALOG_ADD_GENERATOR_BASE { public: NETLIST_DIALOG_ADD_GENERATOR( DIALOG_EXPORT_NETLIST* parent ); const wxString GetGeneratorTitle() { return m_textCtrlName->GetValue(); } const wxString GetGeneratorTCommandLine() { return m_textCtrlCommand->GetValue(); } bool TransferDataFromWindow() override; private: /* * Browse plugin files, and set m_CommandStringCtrl field */ void OnBrowseGenerators( wxCommandEvent& event ) override; DIALOG_EXPORT_NETLIST* m_Parent; }; /* Event id for notebook page buttons: */ enum id_netlist { ID_CREATE_NETLIST = ID_END_EESCHEMA_ID_LIST + 1, ID_CUR_SHEET_AS_ROOT, ID_SAVE_ALL_VOLTAGES, ID_SAVE_ALL_CURRENTS, ID_SAVE_ALL_DISSIPATIONS, ID_SAVE_ALL_EVENTS, ID_RUN_SIMULATOR }; EXPORT_NETLIST_PAGE::EXPORT_NETLIST_PAGE( wxNotebook* aParent, const wxString& aTitle, NETLIST_TYPE_ID aIdNetType, bool aCustom ) : wxPanel( aParent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ) { m_IdNetType = aIdNetType; m_pageNetFmtName = aTitle; m_CommandStringCtrl = nullptr; m_CurSheetAsRoot = nullptr; m_TitleStringCtrl = nullptr; m_SaveAllVoltages = nullptr; m_SaveAllCurrents = nullptr; m_SaveAllDissipations = nullptr; m_SaveAllEvents = nullptr; m_RunExternalSpiceCommand = nullptr; m_custom = aCustom; aParent->AddPage( this, aTitle, false ); wxBoxSizer* MainBoxSizer = new wxBoxSizer( wxVERTICAL ); SetSizer( MainBoxSizer ); wxBoxSizer* UpperBoxSizer = new wxBoxSizer( wxHORIZONTAL ); m_LowBoxSizer = new wxBoxSizer( wxVERTICAL ); MainBoxSizer->Add( UpperBoxSizer, 0, wxGROW | wxALL, 5 ); MainBoxSizer->Add( m_LowBoxSizer, 0, wxGROW | wxALL, 5 ); m_LeftBoxSizer = new wxBoxSizer( wxVERTICAL ); m_RightBoxSizer = new wxBoxSizer( wxVERTICAL ); m_RightOptionsBoxSizer = new wxBoxSizer( wxVERTICAL ); UpperBoxSizer->Add( m_LeftBoxSizer, 0, wxGROW | wxALL, 5 ); UpperBoxSizer->Add( m_RightBoxSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 ); UpperBoxSizer->Add( m_RightOptionsBoxSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 ); } DIALOG_EXPORT_NETLIST::DIALOG_EXPORT_NETLIST( SCH_EDIT_FRAME* parent ) : DIALOG_EXPORT_NETLIST_BASE( parent ) { m_Parent = parent; SCHEMATIC_SETTINGS& settings = m_Parent->Schematic().Settings(); for( EXPORT_NETLIST_PAGE*& page : m_PanelNetType ) page = nullptr; // Add notebook pages: EXPORT_NETLIST_PAGE* page = nullptr; wxStaticText* label = nullptr; page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "KiCad" ), NET_TYPE_PCBNEW, false ); label = new wxStaticText( page, wxID_ANY, _( "Export netlist in legacy KiCad format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); m_PanelNetType[PANELPCBNEW] = page; page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "OrcadPCB2" ), NET_TYPE_ORCADPCB2, false ); label = new wxStaticText( page, wxID_ANY, _( "Export netlist in OrcadPCB2 format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); m_PanelNetType[PANELORCADPCB2] = page; page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "Allegro" ), NET_TYPE_ALLEGRO, false ); label = new wxStaticText( page, wxID_ANY, _( "Export netlist in Allegro format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); m_PanelNetType[PANELALLEGRO] = page; page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "PADS" ), NET_TYPE_PADS, false ); label = new wxStaticText( page, wxID_ANY, _( "Export netlist in PADS format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); m_PanelNetType[PANELPADS] = page; page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ), NET_TYPE_CADSTAR, false ); label = new wxStaticText( page, wxID_ANY, _( "Export netlist in CadStar format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); m_PanelNetType[PANELCADSTAR] = page; InstallPageSpice(); InstallPageSpiceModel(); InstallCustomPages(); SetupStandardButtons( { { wxID_OK, _( "Export Netlist" ) }, { wxID_CANCEL, _( "Close" ) } } ); for( int ii = 0; (ii < 4 + CUSTOMPANEL_COUNTMAX) && m_PanelNetType[ii]; ++ii ) { if( m_PanelNetType[ii]->GetPageNetFmtName() == settings.m_NetFormatName ) { m_NoteBook->ChangeSelection( ii ); break; } } // Now all widgets have the size fixed, call FinishDialogSettings finishDialogSettings(); updateGeneratorButtons(); } void DIALOG_EXPORT_NETLIST::InstallPageSpice() { EXPORT_NETLIST_PAGE* page = m_PanelNetType[PANELSPICE] = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "SPICE" ), NET_TYPE_SPICE, false ); SCHEMATIC_SETTINGS& settings = m_Parent->Schematic().Settings(); wxStaticText* label = new wxStaticText( page, wxID_ANY, _( "Export netlist in SPICE format" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); page->m_CurSheetAsRoot = new wxCheckBox( page, ID_CUR_SHEET_AS_ROOT, _( "Use current sheet as root" ) ); page->m_CurSheetAsRoot->SetToolTip( _( "Export netlist only for the current sheet" ) ); page->m_CurSheetAsRoot->SetValue( settings.m_SpiceCurSheetAsRoot ); page->m_LeftBoxSizer->Add( page->m_CurSheetAsRoot, 0, wxGROW | wxBOTTOM | wxRIGHT, 5 ); page->m_SaveAllVoltages = new wxCheckBox( page, ID_SAVE_ALL_VOLTAGES, _( "Save all voltages" ) ); page->m_SaveAllVoltages->SetToolTip( _( "Write a directive to save all voltages (.save all)" ) ); page->m_SaveAllVoltages->SetValue( settings.m_SpiceSaveAllVoltages ); page->m_LeftBoxSizer->Add( page->m_SaveAllVoltages, 0, wxBOTTOM | wxRIGHT, 5 ); page->m_SaveAllCurrents = new wxCheckBox( page, ID_SAVE_ALL_CURRENTS, _( "Save all currents" ) ); page->m_SaveAllCurrents->SetToolTip( _( "Write a directive to save all currents (.probe alli)" ) ); page->m_SaveAllCurrents->SetValue( settings.m_SpiceSaveAllCurrents ); page->m_LeftBoxSizer->Add( page->m_SaveAllCurrents, 0, wxBOTTOM | wxRIGHT, 5 ); page->m_SaveAllDissipations = new wxCheckBox( page, ID_SAVE_ALL_DISSIPATIONS, _( "Save all power dissipations" ) ); page->m_SaveAllDissipations->SetToolTip( _( "Write directives to save power dissipation of all items (.probe p())" ) ); page->m_SaveAllDissipations->SetValue( settings.m_SpiceSaveAllDissipations ); page->m_LeftBoxSizer->Add( page->m_SaveAllDissipations, 0, wxBOTTOM | wxRIGHT, 5 ); page->m_SaveAllEvents = new wxCheckBox( page, ID_SAVE_ALL_EVENTS, _( "Save all digital event data" ) ); page->m_SaveAllEvents->SetToolTip( _( "If not set, write a directive to prevent the saving of digital event data (esave none)" ) ); page->m_SaveAllEvents->SetValue( settings.m_SpiceSaveAllEvents ); page->m_LeftBoxSizer->Add( page->m_SaveAllEvents, 0, wxBOTTOM | wxRIGHT, 5 ); page->m_RunExternalSpiceCommand = new wxCheckBox( page, ID_RUN_SIMULATOR, _( "Run external simulator command:" ) ); wxString simulatorCommand = settings.m_SpiceCommandString; page->m_RunExternalSpiceCommand->SetToolTip( _( "Enter the command line to run SPICE\n" "Usually ' \"%I\"'\n" "%I will be replaced by the netlist filepath" ) ); page->m_LowBoxSizer->Add( page->m_RunExternalSpiceCommand, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); page->m_CommandStringCtrl = new wxTextCtrl( page, -1, simulatorCommand, wxDefaultPosition, wxDefaultSize ); page->m_CommandStringCtrl->SetInsertionPoint( 1 ); page->m_LowBoxSizer->Add( page->m_CommandStringCtrl, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); } void DIALOG_EXPORT_NETLIST::InstallPageSpiceModel() { EXPORT_NETLIST_PAGE* page = m_PanelNetType[PANELSPICEMODEL] = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "SPICE Model" ), NET_TYPE_SPICE_MODEL, false ); SCHEMATIC_SETTINGS& settings = m_Parent->Schematic().Settings(); wxStaticText* label = new wxStaticText( page, wxID_ANY, _( "Export netlist as a SPICE .subckt model" ) ); page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 ); page->m_CurSheetAsRoot = new wxCheckBox( page, ID_CUR_SHEET_AS_ROOT, _( "Use current sheet as root" ) ); page->m_CurSheetAsRoot->SetToolTip( _( "Export netlist only for the current sheet" ) ); page->m_CurSheetAsRoot->SetValue( settings.m_SpiceModelCurSheetAsRoot ); page->m_LeftBoxSizer->Add( page->m_CurSheetAsRoot, 0, wxGROW | wxBOTTOM | wxRIGHT, 5 ); } void DIALOG_EXPORT_NETLIST::InstallCustomPages() { EXPORT_NETLIST_PAGE* currPage; EESCHEMA_SETTINGS* cfg = dynamic_cast( Kiface().KifaceSettings() ); wxASSERT( cfg ); if( cfg ) { for( size_t i = 0; i < CUSTOMPANEL_COUNTMAX && i < cfg->m_NetlistPanel.plugins.size(); i++ ) { // pairs of (title, command) are stored wxString title = cfg->m_NetlistPanel.plugins[i].name; if( i >= cfg->m_NetlistPanel.plugins.size() ) break; // No more panel to install wxString command = cfg->m_NetlistPanel.plugins[i].command; currPage = AddOneCustomPage( title, command, static_cast( NET_TYPE_CUSTOM1 + i ) ); m_PanelNetType[PANELCUSTOMBASE + i] = currPage; } } } EXPORT_NETLIST_PAGE* DIALOG_EXPORT_NETLIST::AddOneCustomPage( const wxString& aTitle, const wxString& aCommandString, NETLIST_TYPE_ID aNetTypeId ) { EXPORT_NETLIST_PAGE* currPage = new EXPORT_NETLIST_PAGE( m_NoteBook, aTitle, aNetTypeId, true ); currPage->m_LowBoxSizer->Add( new wxStaticText( currPage, -1, _( "Title:" ) ), 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); currPage->m_TitleStringCtrl = new wxTextCtrl( currPage, -1, aTitle, wxDefaultPosition, wxDefaultSize ); currPage->m_TitleStringCtrl->SetInsertionPoint( 1 ); currPage->m_LowBoxSizer->Add( currPage->m_TitleStringCtrl, 0, wxGROW | wxTOP | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); currPage->m_LowBoxSizer->Add( new wxStaticText( currPage, -1, _( "Netlist command:" ) ), 0, wxGROW | wxLEFT | wxRIGHT | wxTOP, 5 ); currPage->m_CommandStringCtrl = new wxTextCtrl( currPage, -1, aCommandString, wxDefaultPosition, wxDefaultSize ); currPage->m_CommandStringCtrl->SetInsertionPoint( 1 ); currPage->m_LowBoxSizer->Add( currPage->m_CommandStringCtrl, 0, wxGROW | wxTOP | wxLEFT | wxRIGHT | wxBOTTOM, 5 ); return currPage; } void DIALOG_EXPORT_NETLIST::OnNetlistTypeSelection( wxNotebookEvent& event ) { updateGeneratorButtons(); } bool DIALOG_EXPORT_NETLIST::NetlistUpdateOpt() { bool changed = false; bool saveAllVoltages = m_PanelNetType[ PANELSPICE ]->m_SaveAllVoltages->IsChecked(); bool saveAllCurrents = m_PanelNetType[ PANELSPICE ]->m_SaveAllCurrents->IsChecked(); bool saveAllDissipations = m_PanelNetType[ PANELSPICE ]->m_SaveAllDissipations->IsChecked(); bool saveAllEvents = m_PanelNetType[ PANELSPICE ]->m_SaveAllEvents->IsChecked(); wxString spiceCmdString = m_PanelNetType[ PANELSPICE ]->m_CommandStringCtrl->GetValue(); bool curSheetAsRoot = m_PanelNetType[ PANELSPICE ]->m_CurSheetAsRoot->GetValue(); bool spiceModelCurSheetAsRoot = m_PanelNetType[ PANELSPICEMODEL ]->m_CurSheetAsRoot->GetValue(); SCHEMATIC_SETTINGS& settings = m_Parent->Schematic().Settings(); wxString netFormatName = m_PanelNetType[m_NoteBook->GetSelection()]->GetPageNetFmtName(); changed |= ( settings.m_SpiceSaveAllVoltages != saveAllVoltages ); changed |= ( settings.m_SpiceSaveAllCurrents != saveAllCurrents ); changed |= ( settings.m_SpiceSaveAllDissipations != saveAllDissipations ); changed |= ( settings.m_SpiceSaveAllEvents != saveAllEvents ); changed |= ( settings.m_SpiceCommandString != spiceCmdString ); changed |= ( settings.m_SpiceCurSheetAsRoot != curSheetAsRoot ); changed |= ( settings.m_SpiceModelCurSheetAsRoot != spiceModelCurSheetAsRoot ); changed |= ( settings.m_NetFormatName != netFormatName ); settings.m_SpiceSaveAllVoltages = saveAllVoltages; settings.m_SpiceSaveAllCurrents = saveAllCurrents; settings.m_SpiceSaveAllDissipations = saveAllDissipations; settings.m_SpiceSaveAllEvents = saveAllEvents; settings.m_SpiceCommandString = spiceCmdString; settings.m_SpiceCurSheetAsRoot = curSheetAsRoot; settings.m_SpiceModelCurSheetAsRoot = spiceModelCurSheetAsRoot; settings.m_NetFormatName = netFormatName; return changed; } bool DIALOG_EXPORT_NETLIST::TransferDataFromWindow() { wxFileName fn; wxString fileWildcard; wxString fileExt; wxString title = _( "Save Netlist File" ); if( NetlistUpdateOpt() ) m_Parent->OnModify(); EXPORT_NETLIST_PAGE* currPage; currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage(); bool runExternalSpiceCommand = false; unsigned netlist_opt = 0; // Calculate the netlist filename fn = m_Parent->Schematic().GetFileName(); FilenamePrms( currPage->m_IdNetType, &fileExt, &fileWildcard ); // Set some parameters switch( currPage->m_IdNetType ) { case NET_TYPE_SPICE: // Set spice netlist options: netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SIM_COMMAND; if( currPage->m_SaveAllVoltages->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES; if( currPage->m_SaveAllCurrents->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS; if( currPage->m_SaveAllDissipations->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS; if( currPage->m_SaveAllEvents->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_EVENTS; if( currPage->m_CurSheetAsRoot->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_CUR_SHEET_AS_ROOT; runExternalSpiceCommand = currPage->m_RunExternalSpiceCommand->GetValue(); break; case NET_TYPE_SPICE_MODEL: if( currPage->m_CurSheetAsRoot->GetValue() ) netlist_opt |= NETLIST_EXPORTER_SPICE::OPTION_CUR_SHEET_AS_ROOT; break; case NET_TYPE_CADSTAR: break; case NET_TYPE_PCBNEW: break; case NET_TYPE_ORCADPCB2: break; case NET_TYPE_ALLEGRO: break; case NET_TYPE_PADS: break; default: // custom, NET_TYPE_CUSTOM1 and greater { title.Printf( _( "%s Export" ), currPage->m_TitleStringCtrl->GetValue() ); break; } } wxString fullpath; if( runExternalSpiceCommand ) { fn.SetExt( FILEEXT::SpiceFileExtension ); fullpath = fn.GetFullPath(); } else { fn.SetExt( fileExt ); if( fn.GetPath().IsEmpty() ) fn.SetPath( wxPathOnly( Prj().GetProjectFullName() ) ); wxString fullname = fn.GetFullName(); wxString path = fn.GetPath(); // full name does not and should not include the path, per wx docs. wxFileDialog dlg( this, title, path, fullname, fileWildcard, wxFD_SAVE ); if( dlg.ShowModal() == wxID_CANCEL ) return false; fullpath = dlg.GetPath(); // directory + filename } m_Parent->ClearMsgPanel(); REPORTER& reporter = m_MessagesBox->Reporter(); if( currPage->m_CommandStringCtrl ) m_Parent->SetNetListerCommand( currPage->m_CommandStringCtrl->GetValue() ); else m_Parent->SetNetListerCommand( wxEmptyString ); if( !m_Parent->ReadyToNetlist( _( "Exporting netlist requires a fully annotated schematic." ) ) ) return false; m_Parent->WriteNetListFile( currPage->m_IdNetType, fullpath, netlist_opt, &reporter ); if( runExternalSpiceCommand ) { // Build the command line wxString commandLine = m_Parent->Schematic().Settings().m_SpiceCommandString; commandLine.Replace( wxS( "%I" ), fullpath, true ); commandLine.Trim( true ).Trim( false ); if( !commandLine.IsEmpty() ) { wxProcess* process = new wxProcess( GetEventHandler(), wxID_ANY ); process->Redirect(); wxExecute( commandLine, wxEXEC_ASYNC, process ); reporter.ReportHead( commandLine, RPT_SEVERITY_ACTION ); process->Activate(); std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); // give the process time to start and output any data or errors if( process->IsInputAvailable() ) { wxInputStream* in = process->GetInputStream(); wxTextInputStream textstream( *in ); while( in->CanRead() ) { wxString line = textstream.ReadLine(); if( !line.IsEmpty() ) reporter.Report( line, RPT_SEVERITY_INFO ); } } if( process->IsErrorAvailable() ) { wxInputStream* err = process->GetErrorStream(); wxTextInputStream textstream( *err ); while( err->CanRead() ) { wxString line = textstream.ReadLine(); if( !line.IsEmpty() ) { if( line.EndsWith( wxS( "failed with error 2!" ) ) ) // ENOENT { reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR ); reporter.Report( _( "Note: command line is usually: " "<path to SPICE binary> \"%I\"" ), RPT_SEVERITY_INFO ); } else if( line.EndsWith( wxS( "failed with error 8!" ) ) ) // ENOEXEC { reporter.Report( _( "external simulator has the wrong format or " "architecture" ), RPT_SEVERITY_ERROR ); } else if( line.EndsWith( "failed with error 13!" ) ) // EACCES { reporter.Report( _( "permission denied" ), RPT_SEVERITY_ERROR ); } else { reporter.Report( line, RPT_SEVERITY_ERROR ); } } } } process->CloseOutput(); process->Detach(); // Do not delete process, it will delete itself when it terminates } } WriteCurrentNetlistSetup(); return !runExternalSpiceCommand; } bool DIALOG_EXPORT_NETLIST::FilenamePrms( NETLIST_TYPE_ID aType, wxString * aExt, wxString * aWildCard ) { wxString fileExt; wxString fileWildcard; bool ret = true; switch( aType ) { case NET_TYPE_SPICE: fileExt = FILEEXT::SpiceFileExtension; fileWildcard = FILEEXT::SpiceNetlistFileWildcard(); break; case NET_TYPE_CADSTAR: fileExt = FILEEXT::CadstarNetlistFileExtension; fileWildcard = FILEEXT::CadstarNetlistFileWildcard(); break; case NET_TYPE_ORCADPCB2: fileExt = FILEEXT::OrCadPcb2NetlistFileExtension; fileWildcard = FILEEXT::OrCadPcb2NetlistFileWildcard(); break; case NET_TYPE_PCBNEW: fileExt = FILEEXT::NetlistFileExtension; fileWildcard = FILEEXT::NetlistFileWildcard(); break; case NET_TYPE_ALLEGRO: fileExt = FILEEXT::AllegroNetlistFileExtension; fileWildcard = FILEEXT::AllegroNetlistFileWildcard(); break; case NET_TYPE_PADS: fileExt = FILEEXT::PADSNetlistFileExtension; fileWildcard = FILEEXT::PADSNetlistFileWildcard(); break; default: // custom, NET_TYPE_CUSTOM1 and greater fileWildcard = FILEEXT::AllFilesWildcard(); ret = false; } if( aExt ) *aExt = fileExt; if( aWildCard ) *aWildCard = fileWildcard; return ret; } void DIALOG_EXPORT_NETLIST::WriteCurrentNetlistSetup() { if( NetlistUpdateOpt() ) m_Parent->OnModify(); EESCHEMA_SETTINGS* cfg = dynamic_cast( Kiface().KifaceSettings() ); wxASSERT( cfg ); if( !cfg ) return; cfg->m_NetlistPanel.plugins.clear(); // Update existing custom pages for( int ii = 0; ii < CUSTOMPANEL_COUNTMAX; ii++ ) { EXPORT_NETLIST_PAGE* currPage = m_PanelNetType[ii + PANELCUSTOMBASE]; if( currPage == nullptr ) break; wxString title = currPage->m_TitleStringCtrl->GetValue(); wxString command = currPage->m_CommandStringCtrl->GetValue(); if( title.IsEmpty() || command.IsEmpty() ) continue; cfg->m_NetlistPanel.plugins.emplace_back( title, wxEmptyString ); cfg->m_NetlistPanel.plugins.back().command = command; } } void DIALOG_EXPORT_NETLIST::OnDelGenerator( wxCommandEvent& event ) { EXPORT_NETLIST_PAGE* currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage(); if( !currPage->IsCustom() ) return; currPage->m_CommandStringCtrl->SetValue( wxEmptyString ); currPage->m_TitleStringCtrl->SetValue( wxEmptyString ); WriteCurrentNetlistSetup(); if( IsQuasiModal() ) EndQuasiModal( NET_PLUGIN_CHANGE ); else EndDialog( NET_PLUGIN_CHANGE ); } void DIALOG_EXPORT_NETLIST::OnAddGenerator( wxCommandEvent& event ) { NETLIST_DIALOG_ADD_GENERATOR dlg( this ); if( dlg.ShowModal() != wxID_OK ) return; // Creates a new custom plugin page wxString title = dlg.GetGeneratorTitle(); // Verify it does not exists int netTypeId = PANELCUSTOMBASE; // the first not used type id EXPORT_NETLIST_PAGE* currPage; for( int ii = 0; ii < CUSTOMPANEL_COUNTMAX; ii++ ) { netTypeId = PANELCUSTOMBASE + ii; currPage = m_PanelNetType[ii + PANELCUSTOMBASE]; if( currPage == nullptr ) break; if( currPage->GetPageNetFmtName() == title ) { wxMessageBox( _("This plugin already exists.") ); return; } } wxString cmd = dlg.GetGeneratorTCommandLine(); currPage = AddOneCustomPage( title,cmd, (NETLIST_TYPE_ID)netTypeId ); m_PanelNetType[netTypeId] = currPage; WriteCurrentNetlistSetup(); if( IsQuasiModal() ) EndQuasiModal( NET_PLUGIN_CHANGE ); else EndDialog( NET_PLUGIN_CHANGE ); } NETLIST_DIALOG_ADD_GENERATOR::NETLIST_DIALOG_ADD_GENERATOR( DIALOG_EXPORT_NETLIST* parent ) : NETLIST_DIALOG_ADD_GENERATOR_BASE( parent ) { m_Parent = parent; SetupStandardButtons(); GetSizer()->SetSizeHints( this ); } bool NETLIST_DIALOG_ADD_GENERATOR::TransferDataFromWindow() { if( !wxDialog::TransferDataFromWindow() ) return false; if( m_textCtrlCommand->GetValue() == wxEmptyString ) { wxMessageBox( _( "You must provide a netlist generator command string" ) ); return false; } if( m_textCtrlName->GetValue() == wxEmptyString ) { wxMessageBox( _( "You must provide a netlist generator title" ) ); return false; } return true; } void NETLIST_DIALOG_ADD_GENERATOR::OnBrowseGenerators( wxCommandEvent& event ) { wxString FullFileName, Path; #ifndef __WXMAC__ Path = Pgm().GetExecutablePath(); #else Path = PATHS::GetOSXKicadDataDir() + wxT( "/plugins" ); #endif FullFileName = wxFileSelector( _( "Generator File" ), Path, FullFileName, wxEmptyString, wxFileSelectorDefaultWildcardStr, wxFD_OPEN, this ); if( FullFileName.IsEmpty() ) return; // Creates a default command line, suitable for external tool xslproc or python, based on // the plugin extension ("xsl" or "exe" or "py") wxString cmdLine; wxFileName fn( FullFileName ); wxString ext = fn.GetExt(); if( ext == wxT( "xsl" ) ) cmdLine.Printf( wxT( "xsltproc -o \"%%O\" \"%s\" \"%%I\"" ), FullFileName ); else if( ext == wxT( "exe" ) || ext.IsEmpty() ) cmdLine.Printf( wxT( "\"%s\" > \"%%O\" < \"%%I\"" ), FullFileName ); else if( ext == wxT( "py" ) || ext.IsEmpty() ) cmdLine.Printf( wxT( "python \"%s\" \"%%I\" \"%%O\"" ), FullFileName ); else cmdLine.Printf( wxT( "\"%s\"" ), FullFileName ); m_textCtrlCommand->SetValue( cmdLine ); // We need a title for this panel // Propose a default value if empty ( i.e. the short filename of the script) if( m_textCtrlName->GetValue().IsEmpty() ) m_textCtrlName->SetValue( fn.GetName() ); } void DIALOG_EXPORT_NETLIST::updateGeneratorButtons() { EXPORT_NETLIST_PAGE* currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage(); if( currPage == nullptr ) return; m_buttonDelGenerator->Enable( currPage->IsCustom() ); } int InvokeDialogNetList( SCH_EDIT_FRAME* aCaller ) { DIALOG_EXPORT_NETLIST dlg( aCaller ); int ret = dlg.ShowModal(); aCaller->SaveProjectLocalSettings(); return ret; }