diff options
Diffstat (limited to 'eeschema/dialogs/dialog_bom.cpp')
-rw-r--r-- | eeschema/dialogs/dialog_bom.cpp | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/eeschema/dialogs/dialog_bom.cpp b/eeschema/dialogs/dialog_bom.cpp new file mode 100644 index 0000000..23bc74d --- /dev/null +++ b/eeschema/dialogs/dialog_bom.cpp @@ -0,0 +1,621 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 Jean-Pierre Charras, jp.charras@wanadoo.fr + * Copyright (C) 1992-2015 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 + */ + +/** + * @file eeschema/dialogs/dialog_bom.cpp + * @brief Dialog box for creating bom and other documents from generic netlist. + */ + + +#include <fctsys.h> +#include <pgm_base.h> +#include <kiface_i.h> +#include <confirm.h> +#include <gestfich.h> +#include <schframe.h> + +#include <netlist.h> +#include <netlist_exporter_generic.h> +#include <sch_sheet.h> +#include <invoke_sch_dialog.h> +#include <dialog_helpers.h> +#include <dialog_bom_base.h> +#include <html_messagebox.h> +#include <reporter.h> + +#define BOM_PLUGINS_KEY wxT("bom_plugins") +#define BOM_PLUGIN_SELECTED_KEY wxT("bom_plugin_selected") + +const char * s_bomHelpInfo = +#include <dialog_bom_help_html.h> +; + +#include <dialog_bom_cfg_lexer.h> + +using namespace T_BOMCFG_T; + +/** + * Class BOM_CFG_READER_PARSER + * holds data and functions pertinent to parsing a S-expression file + * for a WORKSHEET_LAYOUT. + */ +class BOM_CFG_READER_PARSER : public DIALOG_BOM_CFG_LEXER +{ + wxArrayString* m_pluginsList; + +public: + BOM_CFG_READER_PARSER( wxArrayString* aPlugins, + const char* aData, const wxString& aSource ); + void Parse() throw( PARSE_ERROR, IO_ERROR ); + +private: + void parsePlugin() throw( IO_ERROR, PARSE_ERROR ); +}; + +// PCB_PLOT_PARAMS_PARSER + +BOM_CFG_READER_PARSER::BOM_CFG_READER_PARSER( wxArrayString* aPlugins, + const char* aLine, + const wxString& aSource ) : + DIALOG_BOM_CFG_LEXER( aLine, aSource ) +{ + m_pluginsList = aPlugins; +} + + +void BOM_CFG_READER_PARSER::Parse() throw( PARSE_ERROR, IO_ERROR ) +{ + 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 + parsePlugin(); + break; + + default: +// Unexpected( CurText() ); + break; + } + } +} + +void BOM_CFG_READER_PARSER::parsePlugin() throw( IO_ERROR, PARSE_ERROR ) +{ + wxString title, command; + + NeedSYMBOLorNUMBER(); + title = FromUTF8(); + + T token; + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_EOF) + break; + + switch( token ) + { + case T_LEFT: + break; + + case T_cmd: + NeedSYMBOLorNUMBER(); + command = FromUTF8(); + NeedRIGHT(); + break; + + case T_opts: + while( ( token = NextTok() ) != T_RIGHT && token != T_EOF ); + break; + + default: + Unexpected( CurText() ); + break; + } + } + + if( ! title.IsEmpty() ) + { + m_pluginsList->Add( title ); + m_pluginsList->Add( command ); + } +} + +// The main dialog frame tu run scripts to build bom +class DIALOG_BOM : public DIALOG_BOM_BASE +{ +private: + SCH_EDIT_FRAME* m_parent; + // The list of scripts (or plugins): + // a script descr uses 2 lines: + // the first is the title + // the second is the command line + wxArrayString m_plugins; + wxConfigBase* m_config; // to store the "plugins" + +public: + // Constructor and destructor + DIALOG_BOM( SCH_EDIT_FRAME* parent ); + ~DIALOG_BOM(); + +private: + void OnPluginSelected( wxCommandEvent& event ); + void OnRunPlugin( wxCommandEvent& event ); + void OnCancelClick( wxCommandEvent& event ); + void OnHelp( wxCommandEvent& event ); + void OnAddPlugin( wxCommandEvent& event ); + void OnRemovePlugin( wxCommandEvent& event ); + void OnEditPlugin( wxCommandEvent& event ); + void OnCommandLineEdited( wxCommandEvent& event ); + void OnNameEdited( wxCommandEvent& event ); + + void pluginInit(); + void installPluginsList(); + + /** + * @return the Plugin filename from a command line + * @param aCommand = the command line + */ + wxString getPluginFileName( const wxString& aCommand ); + + /** + * display (when exists) the text found between the keyword "@package" + * (compatible with doxygen comments) + * and the end of comment block (""" in python", --> in xml) + */ + void displayPluginInfo( FILE * aFile, const wxString& aFilename ); + + /** + * Browse plugin files, and set m_CommandStringCtrl field + * @return a command line ro run the plugin + */ + wxString choosePlugin(); +}; + +// Create and show DIALOG_BOM. +int InvokeDialogCreateBOM( SCH_EDIT_FRAME* aCaller ) +{ + DIALOG_BOM dlg( aCaller ); + return dlg.ShowModal(); +} + +DIALOG_BOM::DIALOG_BOM( SCH_EDIT_FRAME* parent ) : + DIALOG_BOM_BASE( parent ) +{ + m_parent = parent; + m_config = Kiface().KifaceSettings(); + installPluginsList(); + + FixOSXCancelButtonIssue(); + + // Now all widgets have the size fixed, call FinishDialogSettings + FinishDialogSettings(); +} + +DIALOG_BOM::~DIALOG_BOM() +{ + // Save the plugin descriptions in config. + // the config stores only one string. + // plugins are saved inside a S expr: + // ( plugins + // ( plugin "plugin name" (cmd "command line") ) + // .... + // ) + + STRING_FORMATTER writer; + writer.Print( 0, "(plugins" ); + + for( unsigned ii = 0; ii < m_plugins.GetCount(); ii += 2 ) + { + writer.Print( 1, "(plugin %s (cmd %s))", + writer.Quotew( m_plugins[ii] ).c_str(), + writer.Quotew( m_plugins[ii+1] ).c_str() ); + } + + writer.Print( 0, ")" ); + + wxString list( FROM_UTF8( writer.GetString().c_str() ) ); + + m_config->Write( BOM_PLUGINS_KEY, list ); + + wxString active_plugin_name = m_lbPlugins->GetStringSelection( ); + m_config->Write( BOM_PLUGIN_SELECTED_KEY, active_plugin_name ); + +} + +/* Read the initialized plugins in config and fill the list + * of names + */ +void DIALOG_BOM::installPluginsList() +{ + wxString list, active_plugin_name; + m_config->Read( BOM_PLUGINS_KEY, &list ); + m_config->Read( BOM_PLUGIN_SELECTED_KEY, &active_plugin_name ); + + if( !list.IsEmpty() ) + { + BOM_CFG_READER_PARSER cfg_parser( &m_plugins, TO_UTF8( list ), + wxT( "plugins" ) ); + try + { + cfg_parser.Parse(); + } + catch( const IO_ERROR& ioe ) + { +// wxLogMessage( ioe.errorText ); + } + } + + // Populate list box + for( unsigned ii = 0; ii < m_plugins.GetCount(); ii+=2 ) + { + m_lbPlugins->Append( m_plugins[ii] ); + + if( active_plugin_name == m_plugins[ii] ) + m_lbPlugins->SetSelection( ii/2 ); + } + + pluginInit(); +} + +void DIALOG_BOM::OnPluginSelected( wxCommandEvent& event ) +{ + pluginInit(); +} + +#include <wx/ffile.h> +void DIALOG_BOM::pluginInit() +{ + int ii = m_lbPlugins->GetSelection(); + + if( ii < 0 ) + { + m_textCtrlName->SetValue( wxEmptyString ); + m_textCtrlCommand->SetValue( wxEmptyString ); + return; + } + + m_textCtrlName->SetValue( m_plugins[2 * ii] ); + m_textCtrlCommand->SetValue( m_plugins[(2 * ii)+1] ); + + wxString pluginName = getPluginFileName( m_textCtrlCommand->GetValue() ); + + if( pluginName.IsEmpty() ) + return; + + FILE* pluginFile = wxFopen( pluginName, "rt" ); + + if( pluginFile == NULL ) + { + wxString msg; + msg.Printf( _( "Failed to open file '%s'" ), GetChars( pluginName ) ); + DisplayError( this, msg ); + return; + } + + displayPluginInfo( pluginFile, pluginName ); +} + + +void DIALOG_BOM::displayPluginInfo( FILE * aFile, const wxString& aFilename ) +{ + m_Messages->Clear(); + + // display (when exists) the text found between the keyword "@package" + // (compatible with doxygen comments) + // and the end of comment block (""" in python", --> in xml) + + wxString data; + wxFFile fdata( aFile ); // dtor will close the file + + if( !fdata.ReadAll( &data ) ) + return; + + wxString header( wxT( "@package" ) ); + wxString endsection( wxT( "-->" ) ); // For xml + + wxFileName fn( aFilename ); + + if( fn.GetExt().IsSameAs( wxT("py"), false ) ) + endsection = wxT( "\"\"\"" ); + else if( !fn.GetExt().IsSameAs( wxT("xsl"), false ) ) + // If this is not a python file, we know nothing about file + // and the info cannot be found + return; + + // Extract substring between @package and """ + int strstart = data.Find( header ); + + if( strstart == wxNOT_FOUND ) + return; + + strstart += header.Length(); + int strend = data.find( endsection, strstart ); + + if( strend == wxNOT_FOUND) + return; + + // Remove empty line if any + while( data[strstart] < ' ' ) + strstart++; + + m_Messages->SetValue( data.SubString( strstart, strend-1 ) ); +} + +/** + * Function RunPlugin + * run the plugin command line + */ +void DIALOG_BOM::OnRunPlugin( wxCommandEvent& event ) +{ + wxFileName fn; + + // Calculate the xml netlist filename + fn = g_RootSheet->GetScreen()->GetFileName(); + + fn.SetPath( wxPathOnly( Prj().GetProjectFullName() ) ); + + fn.ClearExt(); + wxString fullfilename = fn.GetFullPath(); + m_parent->ClearMsgPanel(); + + wxString reportmsg; + WX_STRING_REPORTER reporter( &reportmsg ); + m_parent->SetNetListerCommand( m_textCtrlCommand->GetValue() ); + m_parent->CreateNetlist( -1, fullfilename, 0, &reporter ); + + m_Messages->SetValue( reportmsg ); +} + + +void DIALOG_BOM::OnCancelClick( wxCommandEvent& event ) +{ + EndModal( wxID_CANCEL ); +} + + +/** + * Function OnRemovePlugin + * Remove a plugin from the list + */ +void DIALOG_BOM::OnRemovePlugin( wxCommandEvent& event ) +{ + int ii = m_lbPlugins->GetSelection(); + + if( ii < 0 ) + return; + + m_lbPlugins->Delete( ii ); + + m_plugins.RemoveAt( 2*ii, 2 ); // Remove title and command line + + // Select the next item, if exists + if( (int)m_lbPlugins->GetCount() >= ii ) + ii = m_lbPlugins->GetCount() - 1; + + if( ii >= 0 ) + m_lbPlugins->SetSelection( ii ); + + pluginInit(); +} + +/** + * Function OnAddPlugin + * Add a new panel for a new netlist plugin + */ +void DIALOG_BOM::OnAddPlugin( wxCommandEvent& event ) +{ + wxString cmdLine = choosePlugin(); + + if( cmdLine.IsEmpty() ) + return; + + // Creates a new plugin entry + wxFileName fn( getPluginFileName( cmdLine ) ); + + wxString defaultName = fn.GetName(); + wxString name = wxGetTextFromUser( _("Plugin name in plugin list") , + _("Plugin name"), defaultName ); + + if( name.IsEmpty() ) + return; + + // Verify if it does not exists + for( unsigned ii = 0; ii < m_plugins.GetCount(); ii += 2 ) + { + if( name == m_plugins[ii] ) + { + wxMessageBox( _("This name already exists. Abort") ); + return; + } + } + + // Eppend the new plugin + m_plugins.Add( name ); + m_plugins.Add( wxEmptyString ); + m_lbPlugins->SetSelection( m_lbPlugins->GetCount() - 1 ); + m_lbPlugins->Append( name ); + m_lbPlugins->SetSelection( m_lbPlugins->GetCount() - 1 ); + m_textCtrlCommand->SetValue( cmdLine ); + + pluginInit(); +} + +/* + * Browse plugin files, and set m_CommandStringCtrl field + */ +wxString DIALOG_BOM::choosePlugin() +{ + wxString mask = wxT( "*" ); +#ifndef __WXMAC__ + wxString path = Pgm().GetExecutablePath(); +#else + wxString path = GetOSXKicadDataDir() + wxT( "/plugins" ); +#endif + + wxString fullFileName = EDA_FILE_SELECTOR( _( "Plugin files:" ), + path, + wxEmptyString, + wxEmptyString, + mask, + this, + wxFD_OPEN, + true ); + if( fullFileName.IsEmpty() ) + return wxEmptyString; + + // Creates a default command line, + // suitable to run the external tool xslproc or python + // The default command line depending on plugin extension, currently + // "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\""), GetChars( fullFileName ) ); + else if( ext == wxT("exe" ) || ext.IsEmpty() ) + cmdLine.Printf(wxT("\"%s\" < \"%%I\" > \"%%O\""), GetChars( fullFileName ) ); + else if( ext == wxT("py" ) || ext.IsEmpty() ) + cmdLine.Printf(wxT("python \"%s\" \"%%I\" \"%%O\""), GetChars( fullFileName ) ); + else + cmdLine.Printf(wxT("\"%s\""), GetChars( fullFileName ) ); + + return cmdLine; +} + + +wxString DIALOG_BOM::getPluginFileName( const wxString& aCommand ) +{ + wxString pluginName; + + // Try to find the plugin name. + // This is possible if the name ends by .py or .xsl + int pos = -1; + + if( (pos = aCommand.Find( wxT(".py") )) != wxNOT_FOUND ) + pos += 2; + else if( (pos = aCommand.Find( wxT(".xsl") )) != wxNOT_FOUND ) + pos += 3; + + // the end of plugin name is at position pos. + if( pos > 0 ) + { + // Be sure this is the end of the name: the next char is " or space + int eos = aCommand[pos+1]; + + if( eos == ' '|| eos == '\"' ) + { + // search for the starting point of the name + int jj = pos-1; + while( jj >= 0 ) + if( aCommand[jj] != eos ) + jj--; + else + break; + + // extract the name + if( jj >= 0 ) + { + eos = aCommand[jj]; + + if( eos == ' '|| eos == '\"' ) // do not include delimiters + jj++; + + pluginName = aCommand.SubString( jj, pos ); + } + } + } + + // Using a format like %P is possible in plugin name, so expand it + wxString prj_dir = Prj().GetProjectPath(); + + if( prj_dir.EndsWith( '/' ) || prj_dir.EndsWith( '\\' ) ) + prj_dir.RemoveLast(); + + pluginName.Replace( wxT( "%P" ), prj_dir.GetData(), true ); + + return pluginName; +} + +void DIALOG_BOM::OnEditPlugin( wxCommandEvent& event ) +{ + wxString pluginName = getPluginFileName( m_textCtrlCommand->GetValue() ); + + if( pluginName.Length() <= 2 ) // if name != "" + { + wxMessageBox( _("Plugin file name not found. Cannot edit plugin file") ); + return; + } + + AddDelimiterString( pluginName ); + wxString editorname = Pgm().GetEditorName(); + + if( !editorname.IsEmpty() ) + ExecuteFile( this, editorname, pluginName ); + else + wxMessageBox( _("No text editor selected in KiCad. Please choose it") ); +} + +void DIALOG_BOM::OnHelp( wxCommandEvent& event ) +{ + HTML_MESSAGE_BOX help_Dlg( this, _("Bom Generation Help"), + wxDefaultPosition, wxSize( 750,550 ) ); + + wxString msg = FROM_UTF8(s_bomHelpInfo); + help_Dlg.m_htmlWindow->AppendToPage( msg ); + help_Dlg.ShowModal(); +} + +void DIALOG_BOM::OnCommandLineEdited( wxCommandEvent& event ) +{ + int ii = m_lbPlugins->GetSelection(); + + if( ii < 0 ) + return; + + m_plugins[(2 * ii)+1] = m_textCtrlCommand->GetValue(); +} + +void DIALOG_BOM::OnNameEdited( wxCommandEvent& event ) +{ + int ii = m_lbPlugins->GetSelection(); + + if( ii < 0 ) + return; + + m_plugins[2 * ii] = m_textCtrlName->GetValue(); + m_lbPlugins->SetString( ii, m_plugins[2 * ii] ); +} |