From 26b7bdc35c5fd67b8a4f6f984f6dbb12c2628655 Mon Sep 17 00:00:00 2001
From: jean-pierre charras
Date: Fri, 14 Jun 2013 16:59:52 +0200
Subject: [PATCH] Eeschema: add dialog and utilities to create BOMs from
generic netlist
---
CMakeModules/Html2C.cmake | 3 +-
eeschema/CMakeLists.txt | 30 +
eeschema/component_references_lister.cpp | 248 ---
eeschema/dialogs/dialog_bom.cpp | 476 ++++++
eeschema/dialogs/dialog_bom_base.cpp | 117 ++
eeschema/dialogs/dialog_bom_base.fbp | 1520 +++++++++++++++++
eeschema/dialogs/dialog_bom_base.h | 103 ++
eeschema/dialogs/dialog_bom_cfg.keywords | 4 +
eeschema/dialogs/dialog_bom_help.html | 210 +++
eeschema/invoke_sch_dialog.h | 9 +-
eeschema/netlist.h | 43 -
eeschema/schframe.cpp | 5 +-
scripts/bom-in-python/README-bom.txt | 14 +
.../{ky/bom_example5.py => bom-generation.py} | 40 +-
.../{ky/bom_example1.py => bom_csv_by_ref.py} | 10 +-
.../bom_example2.py => bom_csv_by_ref_v2.py} | 12 +-
...xample3.py => bom_csv_grouped_by_value.py} | 13 +-
scripts/bom-in-python/bom_html_by_value.py | 119 ++
...ample4.py => bom_html_grouped_by_value.py} | 25 +-
.../ky.py => ky_generic_netlist_reader.py} | 0
scripts/bom-in-python/{ky => }/round_robin.py | 4 +-
.../{ky => }/round_value_robin.py | 22 +-
22 files changed, 2667 insertions(+), 360 deletions(-)
create mode 100644 eeschema/dialogs/dialog_bom.cpp
create mode 100644 eeschema/dialogs/dialog_bom_base.cpp
create mode 100644 eeschema/dialogs/dialog_bom_base.fbp
create mode 100644 eeschema/dialogs/dialog_bom_base.h
create mode 100644 eeschema/dialogs/dialog_bom_cfg.keywords
create mode 100644 eeschema/dialogs/dialog_bom_help.html
create mode 100644 scripts/bom-in-python/README-bom.txt
rename scripts/bom-in-python/{ky/bom_example5.py => bom-generation.py} (89%)
rename scripts/bom-in-python/{ky/bom_example1.py => bom_csv_by_ref.py} (82%)
rename scripts/bom-in-python/{ky/bom_example2.py => bom_csv_by_ref_v2.py} (81%)
rename scripts/bom-in-python/{ky/bom_example3.py => bom_csv_grouped_by_value.py} (83%)
create mode 100644 scripts/bom-in-python/bom_html_by_value.py
rename scripts/bom-in-python/{ky/bom_example4.py => bom_html_grouped_by_value.py} (89%)
rename scripts/bom-in-python/{ky/ky.py => ky_generic_netlist_reader.py} (100%)
rename scripts/bom-in-python/{ky => }/round_robin.py (87%)
rename scripts/bom-in-python/{ky => }/round_value_robin.py (88%)
diff --git a/CMakeModules/Html2C.cmake b/CMakeModules/Html2C.cmake
index 2c44322d64..99919b31ce 100644
--- a/CMakeModules/Html2C.cmake
+++ b/CMakeModules/Html2C.cmake
@@ -8,5 +8,6 @@ file( STRINGS ${inputFile} lines )
file( WRITE ${outputFile} "// Do not edit this file, it is autogenerated by CMake from an HTML file\n" )
foreach( line ${lines} )
- file( APPEND ${outputFile} "\"" ${line} "\"\n" )
+ STRING(REGEX REPLACE "\"" "\\\\\"" linem ${line})
+ file( APPEND ${outputFile} "\"" ${linem} "\\n\"\n" )
endforeach( line ${lines} )
diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt
index 416b853cf3..6c22e75182 100644
--- a/eeschema/CMakeLists.txt
+++ b/eeschema/CMakeLists.txt
@@ -14,6 +14,9 @@ set(EESCHEMA_DLGS
dialogs/dialog_color_config.cpp
dialogs/dialog_annotate.cpp
dialogs/dialog_annotate_base.cpp
+ dialogs/dialog_bom.cpp
+ dialogs/dialog_bom_base.cpp
+ dialogs/dialog_bom_cfg_keywords.cpp
dialogs/dialog_lib_edit_text.cpp
dialogs/dialog_lib_edit_text_base.cpp
dialogs/dialog_edit_component_in_lib.cpp
@@ -203,6 +206,33 @@ make_lexer(
template_fieldnames.h
)
+make_lexer(
+ ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_cfg.keywords
+ ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_cfg_lexer.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_cfg_keywords.cpp
+ T_BOMCFG_T
+
+ # Pass header file with dependency on *_lexer.h as extra_arg
+ /dialogs/dialog_bom_cfg.h
+ )
+
+# Create a C++ compilable string initializer containing html text into a *.h file:
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help_html.h
+ COMMAND ${CMAKE_COMMAND}
+ -DinputFile=${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help.html
+ -DoutputFile=${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help_html.h
+ -P ${CMAKE_MODULE_PATH}/Html2C.cmake
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help.html
+ COMMENT "creating ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help_html.h
+ from ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help.html"
+ )
+
+set_source_files_properties( dialogs/dialog_bom.cpp
+ PROPERTIES
+ OBJECT_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/dialogs/dialog_bom_help_html.h
+ )
+
add_executable(eeschema WIN32 MACOSX_BUNDLE
${EESCHEMA_SRCS}
${EESCHEMA_COMMON_SRCS}
diff --git a/eeschema/component_references_lister.cpp b/eeschema/component_references_lister.cpp
index df0f97effe..e352f785df 100644
--- a/eeschema/component_references_lister.cpp
+++ b/eeschema/component_references_lister.cpp
@@ -67,7 +67,6 @@ bool SCH_REFERENCE_LIST::sortByXPosition( const SCH_REFERENCE& item1,
return ii < 0;
}
-
bool SCH_REFERENCE_LIST::sortByYPosition( const SCH_REFERENCE& item1,
const SCH_REFERENCE& item2 )
{
@@ -106,252 +105,6 @@ bool SCH_REFERENCE_LIST::sortByRefAndValue( const SCH_REFERENCE& item1,
return ii < 0;
}
-bool SCH_REFERENCE_LIST::sortByValueAndRef( const SCH_REFERENCE& item1,
- const SCH_REFERENCE& item2 )
-{
- int ii = item1.CompareValue( item2 );
-
- if( ii == 0 )
- ii = RefDesStringCompare( item1.GetRef(), item2.GetRef() );
-
- if( ii == 0 )
- ii = item1.m_Unit - item2.m_Unit;
-
- if( ii == 0 )
- ii = item1.m_SheetNum - item2.m_SheetNum;
-
- if( ii == 0 )
- ii = item1.m_CmpPos.x - item2.m_CmpPos.x;
-
- if( ii == 0 )
- ii = item1.m_CmpPos.y - item2.m_CmpPos.y;
-
- if( ii == 0 )
- ii = item1.m_TimeStamp - item2.m_TimeStamp;
-
- return ii < 0;
-}
-
-/*
- * Helper function to calculate in a component value string
- * the value, depending on multiplier symbol:
- * pico
- * nano
- * micro (u)
- * milli (m)
- * kilo (k ou K)
- * Mega
- * Giga
- * Tera
- *
- * with notations like 1K; 1.5K; 1,5K; 1k5
- * returns true if the string is a value, false if not
- * (a value is a string starting by a number)
- */
-
-static bool engStrToDouble( wxString aStr, double* aDouble )
-{
- // A trick to take care of strings without a multiplier
- aStr.Append( wxT( "R" ) );
-
- // Regular expression for a value string, e.g., 47k2
-#if defined(KICAD_GOST)
- static wxRegEx valueRegEx( wxT( "^([0-9]+)(мк|[pnumRkKMGT.,кнМГ])([0-9]*)(мк*|[pnumRkKMGTкнМГ]*)" ) );
-#else
- static wxRegEx valueRegEx( wxT( "^([0-9]+)([pnumRkKMGT.,])([0-9]*)([pnumRkKMGT]*)" ) );
-#endif
-
- if( !valueRegEx.Matches( aStr ) )
- return false;
-
- wxString valueStr = wxString( valueRegEx.GetMatch( aStr, 1 )
- + wxT( "." )
- + valueRegEx.GetMatch( aStr, 3 ) );
- wxString multiplierString = valueRegEx.GetMatch( aStr, 2 );
-#if defined(KICAD_GOST)
- if ( multiplierString == wxT( "мк" ) )
- multiplierString = wxT( "u" );
- else if ( multiplierString == wxT( "к" ) )
- multiplierString = wxT( "k" );
- else if ( multiplierString == wxT( "н" ) )
- multiplierString = wxT( "n" );
- else if ( multiplierString == wxT( "М" ) )
- multiplierString = wxT( "M" );
- else if ( multiplierString == wxT( "Г" ) )
- multiplierString = wxT( "G" );
-#endif
- wxString post_multiplierString = valueRegEx.GetMatch( aStr, 4 );
-#if defined(KICAD_GOST)
- if ( post_multiplierString == wxT( "мк" ) )
- multiplierString = wxT( "u" );
- else if ( post_multiplierString == wxT( "к" ) )
- multiplierString = wxT( "k" );
- else if ( post_multiplierString == wxT( "н" ) )
- multiplierString = wxT( "n" );
- else if ( post_multiplierString == wxT( "М" ) )
- multiplierString = wxT( "M" );
- else if ( post_multiplierString == wxT( "Г" ) )
- multiplierString = wxT( "G" );
-#endif
- double multiplier;
-
- switch( (wxChar)multiplierString[0] )
- {
- case 'p':
- multiplier = 1e-12;
- break;
- case 'n':
- multiplier = 1e-9;
- break;
- case 'u':
- multiplier = 1e-6;
- break;
- case 'm':
- multiplier = 1e-3;
- break;
- case 'k':
- case 'K':
- multiplier = 1e3;
- break;
- case 'M':
- multiplier = 1e6;
- break;
- case 'G':
- multiplier = 1e9;
- break;
- case 'T':
- multiplier = 1e12;
- break;
- case 'R':
- case '.': // floating point separator
- case ',': // floating point separator (some languages)
- default:
- multiplier = 1;
- break;
- }
-
- switch( (wxChar)post_multiplierString[0] )
- {
- case 'p':
- multiplier = 1e-12;
- break;
- case 'n':
- multiplier = 1e-9;
- break;
- case 'u':
- multiplier = 1e-6;
- break;
- case 'm':
- multiplier = 1e-3;
- break;
- case 'k':
- case 'K':
- multiplier = 1e3;
- break;
- case 'M':
- multiplier = 1e6;
- break;
- case 'G':
- multiplier = 1e9;
- break;
- case 'T':
- multiplier = 1e12;
- break;
- case 'R':
- default:
- break;
- }
-
- LOCALE_IO dummy; // set to C floating point standard
- valueStr.ToDouble( aDouble );
- *aDouble *= multiplier;
-
- return true;
-}
-
-
-static bool splitRefStr( const wxString& aRef, wxString* aStr, int* aNumber )
-{
- static wxRegEx refRegEx( wxT( "^([a-zA-Z]+)([0-9]+)" ) );
-
- if( !refRegEx.Matches( aRef ) )
- return false;
-
- *aStr = refRegEx.GetMatch( aRef, 1 );
- *aNumber = wxAtoi( refRegEx.GetMatch( aRef, 2 ) );
- return true;
-}
-
-/* sort the list of references by value.
- * Components are grouped by type and are sorted by value:
- * The value of a component accept multiplier symbols (p, n, K ..)
- * groups are made by first letter of reference
- */
-bool SCH_REFERENCE_LIST::sortByValueOnly( const SCH_REFERENCE& item1,
- const SCH_REFERENCE& item2 )
-{
- // First, group by type according to reference text part (R, C, etc.)
- wxString text1 = item1.GetComponent()->GetField( REFERENCE )->GetText();
- wxString text2 = item2.GetComponent()->GetField( REFERENCE )->GetText();
- wxString refNameStr1, refNameStr2;
- int refNumber1, refNumber2;
-
- if( !splitRefStr( text1, &refNameStr1, &refNumber1 ) )
- return false;
-
- if( !splitRefStr( text2, &refNameStr2, &refNumber2 ) )
- return false;
-
- int ii = refNameStr1.CmpNoCase( refNameStr2 );
-
- if( ii != 0 )
- return ii < 0;
-
- // We can compare here 2 values relative to components of the same type
- // assuming references are correctly chosen
- text1 = item1.GetComponent()->GetField( VALUE )->GetText();
- text2 = item2.GetComponent()->GetField( VALUE )->GetText();
-
- double value1, value2;
-
- // Try to convert value to double (4k7 -> 4700 etc.)
- bool match1 = engStrToDouble( text1, &value1 );
- bool match2 = engStrToDouble( text2, &value2 );
-
- // Values come before other strings
- if( match1 && !match2 )
- return true;
-
- // Values come before other strings
- if( !match1 && match2 )
- return false;
-
- if( match1 && match2 && (value1 != value2) )
- return value1 < value2;
-
- // Inside a group of components of same value, it could be good to group per footprints
- text1 = item1.GetComponent()->GetField( FOOTPRINT )->GetText();
- text2 = item2.GetComponent()->GetField( FOOTPRINT )->GetText();
- ii = text1.CmpNoCase( text2 );
-
- if( ii != 0 )
- return ii < 0;
-
- if( refNumber1 != refNumber2 )
- return refNumber1 < refNumber2;
-
- // Fall back to normal string compare
- ii = text1.CmpNoCase( text2 );
-
- if( ii == 0 )
- ii = RefDesStringCompare( item1.GetRef(), item2.GetRef() );
-
- if( ii == 0 )
- ii = item1.m_Unit - item2.m_Unit;
-
- return ii < 0;
-}
-
bool SCH_REFERENCE_LIST::sortByReferenceOnly( const SCH_REFERENCE& item1,
const SCH_REFERENCE& item2 )
@@ -385,7 +138,6 @@ bool SCH_REFERENCE_LIST::sortByTimeStamp( const SCH_REFERENCE& item1,
return ii < 0;
}
-
int SCH_REFERENCE_LIST::FindUnit( size_t aIndex, int aUnit )
{
int NumRef;
diff --git a/eeschema/dialogs/dialog_bom.cpp b/eeschema/dialogs/dialog_bom.cpp
new file mode 100644
index 0000000000..e98d474fb3
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom.cpp
@@ -0,0 +1,476 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 Jean-Pierre Charras, jp.charras@wanadoo.fr
+ * Copyright (C) 1992-2013 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
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include <../common/dialogs/dialog_display_info_HTML_base.h>
+
+#define BOM_PLUGINS_KEY wxT("bom_plugins")
+#define BOM_PLUGIN_SELECTED_KEY wxT("bom_plugin_selected")
+
+const char * s_bomHelpInfo =
+#include
+;
+
+#include
+
+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;
+ wxConfig* 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 OnChoosePlugin( wxCommandEvent& event );
+ void OnRemovePlugin( wxCommandEvent& event );
+ void OnEditPlugin( wxCommandEvent& event );
+ void OnCommandLineEdited( wxCommandEvent& event );
+ void OnNameEdited( wxCommandEvent& event );
+
+ void pluginInit();
+ void installPluginsList();
+};
+
+// 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 = wxGetApp().GetSettings();
+ installPluginsList();
+
+ GetSizer()->SetSizeHints( this );
+ Centre();
+}
+
+DIALOG_BOM::~DIALOG_BOM()
+{
+ wxString list;
+
+ // 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;
+ list << wxT("(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() );
+ }
+
+ list << writer.GetString();
+ list << wxT(")");
+
+ 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, text, 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, list.c_str(), wxT( "plugins" ) );
+ try
+ {
+ cfg_parser.Parse();
+ }
+ catch( 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();
+}
+
+void DIALOG_BOM::pluginInit()
+{
+ int ii = m_lbPlugins->GetSelection();
+
+ if( ii < 0 )
+ return;
+
+ m_textCtrlName->SetValue( m_plugins[2 * ii] );
+ m_textCtrlCommand->SetValue( m_plugins[(2 * ii)+1] );
+}
+
+
+/**
+ * Function RunPlugin
+ * run the plugin command line
+ */
+void DIALOG_BOM::OnRunPlugin( wxCommandEvent& event )
+{
+ wxFileName fn;
+ wxString fileWildcard;
+ wxString title = _( "Save Netlist File" );
+
+ // Calculate the xml netlist filename
+ fn = g_RootSheet->GetScreen()->GetFileName();
+
+ if( fn.GetPath().IsEmpty() )
+ fn.SetPath( wxGetCwd() );
+
+ fn.ClearExt();
+ wxString fullfilename = fn.GetFullPath();
+ m_parent->ClearMsgPanel();
+
+ m_parent->SetNetListerCommand( m_textCtrlCommand->GetValue() );
+ m_parent->CreateNetlist( -1, fullfilename, 0 );
+}
+
+
+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
+}
+
+/**
+ * Function OnAddPlugin
+ * Add a new panel for a new netlist plugin
+ */
+void DIALOG_BOM::OnAddPlugin( wxCommandEvent& event )
+{
+ // Creates a new plugin entry
+ wxString name = wxGetTextFromUser( _("Plugin") );
+
+ 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 plugin already exists. Abort") );
+ return;
+ }
+ }
+
+ m_plugins.Add( name );
+ m_plugins.Add( wxEmptyString );
+ m_lbPlugins->Append( name );
+ m_lbPlugins->SetSelection( m_lbPlugins->GetCount() - 1 );
+}
+
+/*
+ * Browse plugin files, and set m_CommandStringCtrl field
+ */
+void DIALOG_BOM::OnChoosePlugin( wxCommandEvent& event )
+{
+ wxString FullFileName, Mask, Path;
+
+ Mask = wxT( "*" );
+ Path = wxGetApp().GetExecutablePath();
+ FullFileName = EDA_FileSelector( _( "Plugin files:" ),
+ Path,
+ FullFileName,
+ wxEmptyString,
+ Mask,
+ this,
+ wxFD_OPEN,
+ true
+ );
+ if( FullFileName.IsEmpty() )
+ return;
+
+ // 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\" > \"%%O\" < \"%%I\""), 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) );
+
+ m_textCtrlCommand->SetValue( cmdLine );
+}
+
+void DIALOG_BOM::OnEditPlugin( wxCommandEvent& event )
+{
+ wxString pluginName, cmdline;
+
+ // Try to find the plugin name.
+ // This is possible if the name ends by .py or .xsl
+ cmdline = m_textCtrlCommand->GetValue();
+ int pos = -1;
+
+ if( (pos = cmdline.Find( wxT(".py") )) != wxNOT_FOUND )
+ pos += 2;
+ else if( (pos = cmdline.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 = cmdline[pos+1];
+
+ if( eos == ' '|| eos == '\"' )
+ {
+ // search for the starting point of the name
+ int jj = pos-1;
+ while( jj >= 0 )
+ if( cmdline[jj] != eos )
+ jj--;
+ else
+ break;
+
+ // extract the name
+ if( jj >= 0 )
+ pluginName = cmdline.SubString( jj, pos );
+ }
+ }
+ AddDelimiterString( pluginName );
+ wxString editorname = wxGetApp().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 )
+{
+ DIALOG_DISPLAY_HTML_TEXT_BASE help_Dlg( this, wxID_ANY,
+ _("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] );
+}
diff --git a/eeschema/dialogs/dialog_bom_base.cpp b/eeschema/dialogs/dialog_bom_base.cpp
new file mode 100644
index 0000000000..f493248c94
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom_base.cpp
@@ -0,0 +1,117 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Oct 8 2012)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO "NOT" EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#include "dialog_bom_base.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+BEGIN_EVENT_TABLE( DIALOG_BOM_BASE, DIALOG_SHIM )
+ EVT_LISTBOX( wxID_ANY, DIALOG_BOM_BASE::_wxFB_OnPluginSelected )
+ EVT_TEXT( ID_CMDLINE, DIALOG_BOM_BASE::_wxFB_OnCommandLineEdited )
+ EVT_TEXT( IN_NAMELINE, DIALOG_BOM_BASE::_wxFB_OnNameEdited )
+ EVT_BUTTON( ID_CREATE_BOM, DIALOG_BOM_BASE::_wxFB_OnRunPlugin )
+ EVT_BUTTON( wxID_CANCEL, DIALOG_BOM_BASE::_wxFB_OnCancelClick )
+ EVT_BUTTON( ID_HELP, DIALOG_BOM_BASE::_wxFB_OnHelp )
+ EVT_BUTTON( ID_ADD_PLUGIN, DIALOG_BOM_BASE::_wxFB_OnAddPlugin )
+ EVT_BUTTON( wxID_BROWSE_PLUGINS, DIALOG_BOM_BASE::_wxFB_OnChoosePlugin )
+ EVT_BUTTON( ID_REMOVEL_PLUGIN, DIALOG_BOM_BASE::_wxFB_OnRemovePlugin )
+ EVT_BUTTON( wxID_ANY, DIALOG_BOM_BASE::_wxFB_OnEditPlugin )
+END_EVENT_TABLE()
+
+DIALOG_BOM_BASE::DIALOG_BOM_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
+{
+ this->SetSizeHints( wxDefaultSize, wxDefaultSize );
+
+ wxBoxSizer* bMainSizer;
+ bMainSizer = new wxBoxSizer( wxVERTICAL );
+
+ wxBoxSizer* bUpperSizer;
+ bUpperSizer = new wxBoxSizer( wxHORIZONTAL );
+
+ wxBoxSizer* bLeftSizer;
+ bLeftSizer = new wxBoxSizer( wxVERTICAL );
+
+ m_staticTextPluginTitle = new wxStaticText( this, wxID_ANY, _("Plugins"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextPluginTitle->Wrap( -1 );
+ bLeftSizer->Add( m_staticTextPluginTitle, 0, wxTOP|wxRIGHT|wxLEFT, 5 );
+
+ m_lbPlugins = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
+ bLeftSizer->Add( m_lbPlugins, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ m_staticTextCmd = new wxStaticText( this, wxID_ANY, _("Command line:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextCmd->Wrap( -1 );
+ bLeftSizer->Add( m_staticTextCmd, 0, wxRIGHT|wxLEFT, 5 );
+
+ m_textCtrlCommand = new wxTextCtrl( this, ID_CMDLINE, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+ m_textCtrlCommand->SetMaxLength( 0 );
+ m_textCtrlCommand->SetMinSize( wxSize( 300,-1 ) );
+
+ bLeftSizer->Add( m_textCtrlCommand, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+ m_staticTextName = new wxStaticText( this, wxID_ANY, _("Name:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextName->Wrap( -1 );
+ bLeftSizer->Add( m_staticTextName, 0, wxRIGHT|wxLEFT, 5 );
+
+ m_textCtrlName = new wxTextCtrl( this, IN_NAMELINE, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
+ m_textCtrlName->SetMaxLength( 0 );
+ bLeftSizer->Add( m_textCtrlName, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ bUpperSizer->Add( bLeftSizer, 1, wxEXPAND, 5 );
+
+ wxBoxSizer* bRightSizer;
+ bRightSizer = new wxBoxSizer( wxVERTICAL );
+
+ m_buttonNetlist = new wxButton( this, ID_CREATE_BOM, _("Generate"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_buttonNetlist->SetDefault();
+ bRightSizer->Add( m_buttonNetlist, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
+
+ m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Close"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonCancel, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
+
+ m_buttonHelp = new wxButton( this, ID_HELP, _("Help"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonHelp, 0, wxALL|wxEXPAND, 5 );
+
+ m_staticline2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
+ bRightSizer->Add( m_staticline2, 0, wxEXPAND | wxALL, 5 );
+
+ m_buttonAddPlugin = new wxButton( this, ID_ADD_PLUGIN, _("Add Plugin"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonAddPlugin, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
+
+ m_buttonBrowsePlugin = new wxButton( this, wxID_BROWSE_PLUGINS, _("Set Plugin"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonBrowsePlugin, 0, wxALL|wxEXPAND, 5 );
+
+ m_buttonDelPlugin = new wxButton( this, ID_REMOVEL_PLUGIN, _("Remove Plugin"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonDelPlugin, 0, wxALL|wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 );
+
+ m_buttonEdit = new wxButton( this, wxID_ANY, _("Edit Plugin"), wxDefaultPosition, wxDefaultSize, 0 );
+ bRightSizer->Add( m_buttonEdit, 0, wxALL|wxEXPAND, 5 );
+
+
+ bUpperSizer->Add( bRightSizer, 0, wxALIGN_CENTER_VERTICAL, 5 );
+
+
+ bMainSizer->Add( bUpperSizer, 1, wxEXPAND, 5 );
+
+ m_staticTextDefaultFN = new wxStaticText( this, wxID_ANY, _("Output filename:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticTextDefaultFN->Wrap( -1 );
+ bMainSizer->Add( m_staticTextDefaultFN, 0, wxRIGHT|wxLEFT, 5 );
+
+ m_textCtrlDefaultFileName = new wxTextCtrl( this, ID_FN, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
+ m_textCtrlDefaultFileName->SetMaxLength( 0 );
+ bMainSizer->Add( m_textCtrlDefaultFileName, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
+
+
+ this->SetSizer( bMainSizer );
+ this->Layout();
+
+ this->Centre( wxBOTH );
+}
+
+DIALOG_BOM_BASE::~DIALOG_BOM_BASE()
+{
+}
diff --git a/eeschema/dialogs/dialog_bom_base.fbp b/eeschema/dialogs/dialog_bom_base.fbp
new file mode 100644
index 0000000000..6455cc8023
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom_base.fbp
@@ -0,0 +1,1520 @@
+
+
+
+
+
diff --git a/eeschema/dialogs/dialog_bom_base.h b/eeschema/dialogs/dialog_bom_base.h
new file mode 100644
index 0000000000..ffd95a27c9
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom_base.h
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////
+// C++ code generated with wxFormBuilder (version Oct 8 2012)
+// http://www.wxformbuilder.org/
+//
+// PLEASE DO "NOT" EDIT THIS FILE!
+///////////////////////////////////////////////////////////////////////////
+
+#ifndef __DIALOG_BOM_BASE_H__
+#define __DIALOG_BOM_BASE_H__
+
+#include
+#include
+#include
+class DIALOG_SHIM;
+
+#include "dialog_shim.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+///////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+/// Class DIALOG_BOM_BASE
+///////////////////////////////////////////////////////////////////////////////
+class DIALOG_BOM_BASE : public DIALOG_SHIM
+{
+ DECLARE_EVENT_TABLE()
+ private:
+
+ // Private event handlers
+ void _wxFB_OnPluginSelected( wxCommandEvent& event ){ OnPluginSelected( event ); }
+ void _wxFB_OnCommandLineEdited( wxCommandEvent& event ){ OnCommandLineEdited( event ); }
+ void _wxFB_OnNameEdited( wxCommandEvent& event ){ OnNameEdited( event ); }
+ void _wxFB_OnRunPlugin( wxCommandEvent& event ){ OnRunPlugin( event ); }
+ void _wxFB_OnCancelClick( wxCommandEvent& event ){ OnCancelClick( event ); }
+ void _wxFB_OnHelp( wxCommandEvent& event ){ OnHelp( event ); }
+ void _wxFB_OnAddPlugin( wxCommandEvent& event ){ OnAddPlugin( event ); }
+ void _wxFB_OnChoosePlugin( wxCommandEvent& event ){ OnChoosePlugin( event ); }
+ void _wxFB_OnRemovePlugin( wxCommandEvent& event ){ OnRemovePlugin( event ); }
+ void _wxFB_OnEditPlugin( wxCommandEvent& event ){ OnEditPlugin( event ); }
+
+
+ protected:
+ enum
+ {
+ ID_CMDLINE = 1000,
+ IN_NAMELINE,
+ ID_CREATE_BOM,
+ ID_HELP,
+ ID_ADD_PLUGIN,
+ wxID_BROWSE_PLUGINS,
+ ID_REMOVEL_PLUGIN,
+ ID_FN
+ };
+
+ wxStaticText* m_staticTextPluginTitle;
+ wxListBox* m_lbPlugins;
+ wxStaticText* m_staticTextCmd;
+ wxTextCtrl* m_textCtrlCommand;
+ wxStaticText* m_staticTextName;
+ wxTextCtrl* m_textCtrlName;
+ wxButton* m_buttonNetlist;
+ wxButton* m_buttonCancel;
+ wxButton* m_buttonHelp;
+ wxStaticLine* m_staticline2;
+ wxButton* m_buttonAddPlugin;
+ wxButton* m_buttonBrowsePlugin;
+ wxButton* m_buttonDelPlugin;
+ wxButton* m_buttonEdit;
+ wxStaticText* m_staticTextDefaultFN;
+ wxTextCtrl* m_textCtrlDefaultFileName;
+
+ // Virtual event handlers, overide them in your derived class
+ virtual void OnPluginSelected( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCommandLineEdited( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnNameEdited( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnRunPlugin( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnHelp( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnAddPlugin( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnChoosePlugin( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnRemovePlugin( wxCommandEvent& event ) { event.Skip(); }
+ virtual void OnEditPlugin( wxCommandEvent& event ) { event.Skip(); }
+
+
+ public:
+
+ DIALOG_BOM_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Bill of Material"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 404,334 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
+ ~DIALOG_BOM_BASE();
+
+};
+
+#endif //__DIALOG_BOM_BASE_H__
diff --git a/eeschema/dialogs/dialog_bom_cfg.keywords b/eeschema/dialogs/dialog_bom_cfg.keywords
new file mode 100644
index 0000000000..8790280ce4
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom_cfg.keywords
@@ -0,0 +1,4 @@
+plugins
+plugin
+cmd
+opts
diff --git a/eeschema/dialogs/dialog_bom_help.html b/eeschema/dialogs/dialog_bom_help.html
new file mode 100644
index 0000000000..4ba5c5bac1
--- /dev/null
+++ b/eeschema/dialogs/dialog_bom_help.html
@@ -0,0 +1,210 @@
+
+
+
+
+ kicad help
+
+
+
+
+
+
+
+1 - Full
+documentation:
+
+The
+Eeschema
+documentation, chapter 14
+describes this intermediate netlist and gives examples See also
+https://answers.launchpad.net/kicad/+faq/2265
+
+2 - The
+intermediate Netlist File
+
+BOM files (and
+netlist files) can be created from an Intermediate netlist file
+created by Eeschema.
+
+This file uses XML
+syntax and is called the intermediate netlist. The intermediate
+netlist includes a large amount of data about your board and because
+of this, it can be used with post-processing to create a BOM or other
+reports.
+
+Depending on the
+output (BOM or netlist), different subsets of the complete
+Intermediate Netlist file will be used in the post-processing.
+
+3 - Conversion to
+a new format
+
+By applying a
+post-processing filter to the Intermediate netlist file you can
+generate foreign netlist files as well as BOM files. Because this
+conversion is a text to text transformation.
+
+this post-processing
+filter can be written using Python, XSLT, or any other tool capable
+of taking XML as input.
+
+XSLT
+itself is a XML language very suitable for XML transformations. There
+is a free program called xsltproc
+that
+you can download and install. The
+xsltproc
+program can be used to read the Intermediate XML netlist input file,
+applya
+style-sheet to transform the input, and save the results in an output
+file. Use of xsltproc requires a style-sheet file using XSLT
+conventions. The full conversion process is handled
+by
+Eeschema, after it is configured once to run xsltproc in a specific
+way.
+
+4 -
+Initialization of the dialog window
+
+You should add a new
+pluging (a script) in plugin list by clicking on the Add Plugin
+button.
+
+4.1 - Plugin
+Configuration Parameters
+
+The Eeschema plug-in
+configuration dialog requires the following information:
+
+
+ The title: for
+ instance, the name of the netlist format.
+
+ The command line to
+ launch the converter (usually a script).
+
+
+Once you click on
+the generate button the following will happen:
+
+
+ Eeschema creates an
+ intermediate netlist file *.xml, for instance test.xml.
+
+ Eeschema runs the
+ script from the command line to create the final output file.
+
+
+4.2 - Generate
+netlist files with the command line
+
+Assuming we are
+using the program xsltproc.exe
+to
+apply the sheet style to the intermediate file, xsltproc.exe
+is
+executed with the following command.
+
+xsltproc.exe -o <
+output filename > < style-sheet filename > < input XML
+file to convert >
+
+On
+Windows the command line is the
+following. f:/kicad/bin/xsltproc.exe
+-o “%O” f:/kicad/bin/plugins/myconverter.xsl “%I”
+
+On
+Linux the command becomes as
+following. xsltproc
+-o “%O” /usr/local/kicad/bin/plugins/myconverter .xsl
+“%I”
+
+Where
+myconverter.xsl
+is
+the style-sheet that you are applying. Do not forget the double
+quotes
+around
+the file names, this allows them to have spaces after the
+substitution by Eeschema.
+
+The command line
+format accepts parameters for filenames:
+
+The supported
+formatting parameters are.
+
+
+ %B => base
+ filename and path of selected output file, minus path and extension.
+
+ %I => complete
+ filename and path of the temporary input file (the intermediate net
+ file).
+
+ %O => complete
+ filename and path of the user chosen output file.
+
+
+%I will be replaced
+by the actual intermediate file name %O
+will be replaced by the actual output file name.
+
+4.3 - Command
+line format: example for xsltproc
+
+The
+command line format for xsltproc is the following: < path of
+xsltproc > xsltproc
+< xsltproc parameters >
+
+
\ No newline at end of file
diff --git a/eeschema/invoke_sch_dialog.h b/eeschema/invoke_sch_dialog.h
index a8ccbff50a..a1f6ffcc95 100644
--- a/eeschema/invoke_sch_dialog.h
+++ b/eeschema/invoke_sch_dialog.h
@@ -37,8 +37,8 @@
// DIALOG_.cpp file.
-#ifndef INVOKE_A_DIALOG_H_
-#define INVOKE_A_DIALOG_H_
+#ifndef INVOKE_SCH_DIALOG_H_
+#define INVOKE_SCH_DIALOG_H_
class wxFrame;
@@ -61,6 +61,9 @@ wxDialog* InvokeDialogERC( SCH_EDIT_FRAME* aCaller );
/// DIALOG_PRINT_USING_PRINTER::ShowModal() returns.
int InvokeDialogPrintUsingPrinter( SCH_EDIT_FRAME* aCaller );
+/// Create and show DIALOG_BOM and return whatever
+/// DIALOG_BOM::ShowModal() returns.
+int InvokeDialogCreateBOM( SCH_EDIT_FRAME* aCaller );
-#endif // INVOKE_A_DIALOG_H_
+#endif // INVOKE_SCH_DIALOG_H_
diff --git a/eeschema/netlist.h b/eeschema/netlist.h
index 55189e5df4..2a3c439366 100644
--- a/eeschema/netlist.h
+++ b/eeschema/netlist.h
@@ -374,26 +374,6 @@ public:
sort( componentFlatList.begin(), componentFlatList.end(), sortByRefAndValue );
}
- /**
- * Function SortByValueAndRef
- * sorts the list of references by value.
- *
- * Components are sorted in the following order:
- *
- *
Value of component.
- *
Numeric value of reference designator.
- *
Unit number when component has multiple parts.
- *
Sheet number.
- *
X coordinate position.
- *
Y coordinate position.
- *
- *
- */
- void SortByValueAndRef()
- {
- sort( componentFlatList.begin(), componentFlatList.end(), sortByValueAndRef );
- }
-
/**
* Function SortByReferenceOnly
* sorts the list of references by reference.
@@ -410,25 +390,6 @@ public:
sort( componentFlatList.begin(), componentFlatList.end(), sortByReferenceOnly );
}
- /**
- * Function SortByValueOnly
- * sort the list of references by value.
- *
- * Components are grouped by type and are sorted in the following order:
- *
- *
Value of component.
- *
Numeric value of reference designator.
- *
Unit number when component has multiple parts.
- *
- *
- * groups are made by the first letter of reference
- * or the 2 first letters when existing
- */
- void SortByValueOnly()
- {
- sort( componentFlatList.begin(), componentFlatList.end(), sortByValueOnly );
- }
-
/**
* Function GetUnit
* searches the sorted list of components for a another component with the same
@@ -474,16 +435,12 @@ private:
static bool sortByRefAndValue( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
- static bool sortByValueAndRef( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
-
static bool sortByXPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
static bool sortByYPosition( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
static bool sortByTimeStamp( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
- static bool sortByValueOnly( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
-
static bool sortByReferenceOnly( const SCH_REFERENCE& item1, const SCH_REFERENCE& item2 );
/**
diff --git a/eeschema/schframe.cpp b/eeschema/schframe.cpp
index 289f83e2ea..49fa8390e5 100644
--- a/eeschema/schframe.cpp
+++ b/eeschema/schframe.cpp
@@ -632,10 +632,7 @@ void SCH_EDIT_FRAME::OnCreateNetlist( wxCommandEvent& event )
void SCH_EDIT_FRAME::OnCreateBillOfMaterials( wxCommandEvent& )
{
- wxMessageDialog dlg( this,
- wxT( "https://answers.launchpad.net/kicad/+faq/2265" ),
- _( "BOM Howto" ) );
- dlg.ShowModal();
+ InvokeDialogCreateBOM( this );
}
diff --git a/scripts/bom-in-python/README-bom.txt b/scripts/bom-in-python/README-bom.txt
new file mode 100644
index 0000000000..e5be840892
--- /dev/null
+++ b/scripts/bom-in-python/README-bom.txt
@@ -0,0 +1,14 @@
+bom_?.py are some python scripts which read a generic xml netlist from eeschema,
+and create a bom.
+
+All examples use ky_generic_netlist_reader.py, which is a python utility to read
+and parse this generic xml netlist and create the corresponding data
+used to build the bom.
+
+You can modify them to build the bom you want.
+
+to use them, you should install python, and run:
+python bom_example?.py
+
+See Eeschema doc, chapter 14 for info about the generic xml netlist format,
+and how to run a script from Eeschema to create a customized netlist or BOM.
diff --git a/scripts/bom-in-python/ky/bom_example5.py b/scripts/bom-in-python/bom-generation.py
similarity index 89%
rename from scripts/bom-in-python/ky/bom_example5.py
rename to scripts/bom-in-python/bom-generation.py
index 7f6cd3be69..8746575348 100644
--- a/scripts/bom-in-python/ky/bom_example5.py
+++ b/scripts/bom-in-python/bom-generation.py
@@ -5,7 +5,7 @@
#
# Import the KiCad python helper module and the csv formatter
-import ky
+import ky_generic_netlist_reader
import sys
# Start with a basic html template
@@ -30,13 +30,13 @@ html = """
"""
def myEqu(self, other):
- """myEqu is a more advanced equivalence function for components which is
+ """myEqu is a more advanced equivalence function for components which is
used by component grouping. Normal operation is to group components based
on their Value, Library source, and Library part.
-
- In this example of a more advanced equivalency operator we also compare the
+
+ In this example of a more advanced equivalency operator we also compare the
custom fields Voltage, Tolerance and Manufacturer as well as the assigned
- footprint. If these fields are not used in some parts they will simply be
+ footprint. If these fields are not used in some parts they will simply be
ignored (they will match as both will be empty strings).
"""
@@ -54,18 +54,18 @@ def myEqu(self, other):
elif self.getField("Manufacturer") != other.getField("Manufacturer"):
result = False
elif self.getField("Voltage") != other.getField("Voltage"):
- result = False
-
+ result = False
+
return result
# Override the component equivalence operator - it is important to do this
# before loading the netlist, otherwise all components will have the original
# equivalency operator.
-ky.component.__equ__ = myEqu
-
+ky_generic_netlist_reader.component.__equ__ = myEqu
+
# Generate an instance of a generic netlist, and load the netlist tree from
# video.tmp. If the file doesn't exist, execution will stop
-net = ky.netlist(sys.argv[1])
+net = ky_generic_netlist_reader.netlist(sys.argv[1])
# Open a file to write too, if the file cannot be opened output to stdout
# instead
@@ -82,31 +82,33 @@ html = html.replace('', net.getTool())
html = html.replace('', "Component Count:" + \
str(len(net.components)))
-row = "
Ref
" + "
Qnty
"
+row = "
Ref
" + "
Qnty
"
row += "
Value
" + "
Part
" + "
Datasheet
"
row += "
Description
" + "
Vendor
"
-
+
html = html.replace('', row + "")
-# Get all of the components in groups of matching parts + values (see ky.py)
+# Get all of the components in groups of matching parts + values
+# (see ky_generic_netlist_reader.py)
grouped = net.groupComponents()
# Output all of the component information
for group in grouped:
refs = ""
- # Add the reference of every component in the group and keep a reference
+ # Add the reference of every component in the group and keep a reference
# to the component so that the other data can be filled in once per group
for component in group:
refs += component.getRef() + ", "
c = component
- row = "
" + refs +"
" + str(len(group))
- row += "
" + c.getValue() + "
" + c.getLib() + "/"
- row += c.getPart() + "
" + c.getDatasheet() + "
"
- row += c.getDescription() + "
" + c.getField("Vendor")
+ row = "\n "
+ row += "
" + refs +"
" + str(len(group))
+ row += "
" + c.getValue() + "
" + c.getLib() + "/"
+ row += c.getPart() + "
" + c.getDatasheet() + "
"
+ row += c.getDescription() + "
" + c.getField("Vendor")
row += "
"
-
+
html = html.replace('', row + "")
# Print the formatted html to output file
diff --git a/scripts/bom-in-python/ky/bom_example1.py b/scripts/bom-in-python/bom_csv_by_ref.py
similarity index 82%
rename from scripts/bom-in-python/ky/bom_example1.py
rename to scripts/bom-in-python/bom_csv_by_ref.py
index 354c6b5686..ccc874fd16 100644
--- a/scripts/bom-in-python/ky/bom_example1.py
+++ b/scripts/bom-in-python/bom_csv_by_ref.py
@@ -5,13 +5,13 @@
#
# Import the KiCad python helper module and the csv formatter
-import ky
+import ky_generic_netlist_reader
import csv
import sys
# Generate an instance of a generic netlist, and load the netlist tree from
# the command line option. If the file doesn't exist, execution will stop
-net = ky.netlist(sys.argv[1])
+net = ky_generic_netlist_reader.netlist(sys.argv[1])
# Open a file to write to, if the file cannot be opened output to stdout
# instead
@@ -23,16 +23,16 @@ except IOError:
# Create a new csv writer object to use as the output formatter, although we
# are created a tab delimited list instead!
-out = csv.writer(f, delimiter='\t', quoting=csv.QUOTE_NONE)
+out = csv.writer(f, lineterminator='\n', delimiter='\t', quoting=csv.QUOTE_NONE)
# Output a field delimited header line
out.writerow(['Source:', net.getSource()])
out.writerow(['Date:', net.getDate()])
out.writerow(['Tool:', net.getTool()])
-out.writerow(['Component Count:', len(net.components)])
+out.writerow(['Component Count:', len(net.components)])
out.writerow(['Ref', 'Value', 'Part', 'Documentation', 'Description', 'Vendor'])
# Output all of the component information
for c in net.components:
- out.writerow([c.getRef(), c.getValue(), c.getLib() + "/" + c.getPart(),
+ out.writerow([c.getRef(), c.getValue(), c.getLib() + "/" + c.getPart(),
c.getDatasheet(), c.getDescription(), c.getField("Vendor")])
diff --git a/scripts/bom-in-python/ky/bom_example2.py b/scripts/bom-in-python/bom_csv_by_ref_v2.py
similarity index 81%
rename from scripts/bom-in-python/ky/bom_example2.py
rename to scripts/bom-in-python/bom_csv_by_ref_v2.py
index 58b1c05453..38e6f0c75f 100644
--- a/scripts/bom-in-python/ky/bom_example2.py
+++ b/scripts/bom-in-python/bom_csv_by_ref_v2.py
@@ -5,16 +5,16 @@
#
# Import the KiCad python helper module
-import ky
+import ky_generic_netlist_reader
import csv
import sys
-
+
# Generate an instance of a generic netlist, and load the netlist tree from
# the command line option. If the file doesn't exist, execution will stop
-net = ky.netlist(sys.argv[1])
+net = ky_generic_netlist_reader.netlist(sys.argv[1])
# Open a file to write to, if the file cannot be opened output to stdout
-# instead
+# instead
try:
f = open(sys.argv[2], 'w')
except IOError:
@@ -22,13 +22,13 @@ except IOError:
f = stdout
# Create a new csv writer object to use as the output formatter
-out = csv.writer(f, delimiter=',', quotechar="\"", quoting=csv.QUOTE_ALL)
+out = csv.writer(f, lineterminator='\n', delimiter=',', quotechar="\"", quoting=csv.QUOTE_ALL)
# Output a field delimited header line
out.writerow(['Source:', net.getSource()])
out.writerow(['Date:', net.getDate()])
out.writerow(['Tool:', net.getTool()])
-out.writerow(['Component Count:', len(net.components)])
+out.writerow(['Component Count:', len(net.components)])
out.writerow(['Ref', 'Value', 'Footprint', 'Datasheet', 'Manufacturer', 'Vendor'])
# Output all of the component information (One component per row)
diff --git a/scripts/bom-in-python/ky/bom_example3.py b/scripts/bom-in-python/bom_csv_grouped_by_value.py
similarity index 83%
rename from scripts/bom-in-python/ky/bom_example3.py
rename to scripts/bom-in-python/bom_csv_grouped_by_value.py
index dcbbf15b32..81d3acecf6 100644
--- a/scripts/bom-in-python/ky/bom_example3.py
+++ b/scripts/bom-in-python/bom_csv_grouped_by_value.py
@@ -5,13 +5,13 @@
#
# Import the KiCad python helper module and the csv formatter
-import ky
+import ky_generic_netlist_reader
import csv
import sys
# Generate an instance of a generic netlist, and load the netlist tree from
# the command line option. If the file doesn't exist, execution will stop
-net = ky.netlist(sys.argv[1])
+net = ky_generic_netlist_reader.netlist(sys.argv[1])
# Open a file to write to, if the file cannot be opened output to stdout
# instead
@@ -22,23 +22,24 @@ except IOError:
f = stdout
# Create a new csv writer object to use as the output formatter
-out = csv.writer(f, delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL)
+out = csv.writer(f, lineterminator='\n', delimiter=',', quotechar='\"', quoting=csv.QUOTE_ALL)
# Output a set of rows for a header providing general information
out.writerow(['Source:', net.getSource()])
out.writerow(['Date:', net.getDate()])
out.writerow(['Tool:', net.getTool()])
-out.writerow(['Component Count:', len(net.components)])
+out.writerow(['Component Count:', len(net.components)])
out.writerow(['Ref', 'Qnty', 'Value', 'Part', 'Datasheet', 'Description', 'Vendor'])
-# Get all of the components in groups of matching parts + values (see ky.py)
+# Get all of the components in groups of matching parts + values
+# (see ky_generic_netlist_reader.py)
grouped = net.groupComponents()
# Output all of the component information
for group in grouped:
refs = ""
- # Add the reference of every component in the group and keep a reference
+ # Add the reference of every component in the group and keep a reference
# to the component so that the other data can be filled in once per group
for component in group:
refs += component.getRef() + ", "
diff --git a/scripts/bom-in-python/bom_html_by_value.py b/scripts/bom-in-python/bom_html_by_value.py
new file mode 100644
index 0000000000..4c7fa5f2a5
--- /dev/null
+++ b/scripts/bom-in-python/bom_html_by_value.py
@@ -0,0 +1,119 @@
+#
+# Example python script to generate a BOM from a KiCad generic netlist
+#
+# Example: Sorted and Grouped HTML BOM with more advanced grouping
+#
+
+# Import the KiCad python helper module and the csv formatter
+import ky_generic_netlist_reader
+import sys
+
+# Start with a basic html template
+html = """
+
+
+
+
+ KiCad BOM Example 5
+
+
+
+
+
+
+
+
+
+
+
+ """
+
+def myEqu(self, other):
+ """myEqu is a more advanced equivalence function for components which is
+ used by component grouping. Normal operation is to group components based
+ on their Value, Library source, and Library part.
+
+ In this example of a more advanced equivalency operator we also compare the
+ custom fields Voltage, Tolerance and Manufacturer as well as the assigned
+ footprint. If these fields are not used in some parts they will simply be
+ ignored (they will match as both will be empty strings).
+
+ """
+ result = True
+ if self.getValue() != other.getValue():
+ result = False
+ elif self.getLib() != other.getLib():
+ result = False
+ elif self.getPart() != other.getPart():
+ result = False
+ elif self.getFootprint() != other.getFootprint():
+ result = False
+ elif self.getField("Tolerance") != other.getField("Tolerance"):
+ result = False
+ elif self.getField("Manufacturer") != other.getField("Manufacturer"):
+ result = False
+ elif self.getField("Voltage") != other.getField("Voltage"):
+ result = False
+
+ return result
+
+# Override the component equivalence operator - it is important to do this
+# before loading the netlist, otherwise all components will have the original
+# equivalency operator.
+ky_generic_netlist_reader.component.__equ__ = myEqu
+
+# Generate an instance of a generic netlist, and load the netlist tree from
+# video.xml. If the file doesn't exist, execution will stop
+net = ky_generic_netlist_reader.netlist(sys.argv[1])
+
+# Open a file to write too, if the file cannot be opened output to stdout
+# instead
+try:
+ f = open(sys.argv[2], 'w')
+except IOError:
+ print >> sys.stderr, __file__, ":", e
+ f = stdout
+
+# Output a set of rows for a header providing general information
+html = html.replace('', net.getSource())
+html = html.replace('', net.getDate())
+html = html.replace('', net.getTool())
+html = html.replace('', "Component Count:" + \
+ str(len(net.components)))
+
+row = "
Ref
" + "
Qnty
"
+row += "
Value
" + "
Part
"
+row += "
Description
"
+#row += "
Datasheet
"
+row += "
PartNumber
" + "
Vendor
"
+
+html = html.replace('', row + "")
+
+# Get all of the components in groups of matching parts + values
+# (see ky_generic_netlist_reader.py)
+grouped = net.groupComponents()
+
+# Output all of the component information
+for group in grouped:
+ refs = ""
+
+ # Add the reference of every component in the group and keep a reference
+ # to the component so that the other data can be filled in once per group
+ for component in group:
+ refs += component.getRef() + ", "
+ c = component
+
+ row = "
" + refs +"
" + str(len(group))
+ row += "
" + c.getValue() + "
"
+ row += c.getLib() + "/" + c.getPart() + "
"
+ #row += c.getDatasheet() + "
"
+ row += c.getDescription() + "
"
+ row += c.getField("PartNumber") + "
"
+ row += c.getField("Vendor")
+ row += "
"
+
+ html = html.replace('', row + "")
+
+# Print the formatted html to output file
+print >> f, html
diff --git a/scripts/bom-in-python/ky/bom_example4.py b/scripts/bom-in-python/bom_html_grouped_by_value.py
similarity index 89%
rename from scripts/bom-in-python/ky/bom_example4.py
rename to scripts/bom-in-python/bom_html_grouped_by_value.py
index 201c041e39..d1cb8912f7 100644
--- a/scripts/bom-in-python/ky/bom_example4.py
+++ b/scripts/bom-in-python/bom_html_grouped_by_value.py
@@ -5,7 +5,7 @@
#
# Import the KiCad python helper module and the csv formatter
-import ky
+import ky_generic_netlist_reader
import sys
# Start with a basic html template
@@ -27,10 +27,10 @@ html = """