diff --git a/bitmaps_png/CMakeLists.txt b/bitmaps_png/CMakeLists.txt index 80bdcea45d..02c6c061f7 100644 --- a/bitmaps_png/CMakeLists.txt +++ b/bitmaps_png/CMakeLists.txt @@ -531,6 +531,7 @@ set( BMAPS_MID show_no_layers show_no_copper_layers show_all_copper_layers + spreadsheet svg_file swap_layer text_sketch diff --git a/bitmaps_png/cpp_26/spreadsheet.cpp b/bitmaps_png/cpp_26/spreadsheet.cpp new file mode 100644 index 0000000000..fb71a5fc5d --- /dev/null +++ b/bitmaps_png/cpp_26/spreadsheet.cpp @@ -0,0 +1,45 @@ + +/* Do not modify this file, it was automatically generated by the + * PNG2cpp CMake script, using a *.png file as input. + */ + +#include + +static const unsigned char png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x1a, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0x4a, 0x4c, + 0xce, 0x00, 0x00, 0x01, 0xbd, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0x63, 0x60, 0xa0, 0x17, 0x10, + 0x92, 0x90, 0x08, 0xe3, 0xd5, 0x32, 0x7b, 0xcd, 0xa7, 0x6b, 0xfd, 0x9e, 0x47, 0xdd, 0xe4, 0x23, + 0x8f, 0x86, 0xc9, 0x47, 0x10, 0x1b, 0x19, 0x73, 0xab, 0xe8, 0x7d, 0x22, 0x46, 0x8c, 0x57, 0xc3, + 0xf4, 0x23, 0xc8, 0x0c, 0x14, 0x31, 0x4d, 0xb3, 0xd7, 0x82, 0x32, 0x4a, 0x51, 0x0c, 0xc2, 0xb2, + 0x4a, 0x77, 0x18, 0x5a, 0xf6, 0xff, 0x67, 0x68, 0x3d, 0xf0, 0x9f, 0x21, 0xa9, 0xef, 0x3f, 0x43, + 0x72, 0x3f, 0x84, 0x8d, 0x8c, 0x1d, 0x62, 0x89, 0x13, 0x4b, 0x9d, 0xf4, 0x9f, 0x21, 0xa1, 0x1b, + 0x55, 0x0c, 0x68, 0x36, 0xaf, 0x9a, 0xc1, 0x13, 0x06, 0x61, 0x79, 0xd5, 0xfb, 0x70, 0x41, 0x5a, + 0x58, 0x04, 0xc4, 0x7c, 0x1a, 0x26, 0xcf, 0xe9, 0x67, 0x11, 0x9f, 0xb8, 0xf4, 0x33, 0xb0, 0x26, + 0x10, 0xd6, 0x77, 0xfd, 0xaf, 0xe7, 0xec, 0xfb, 0xdf, 0x39, 0x26, 0x03, 0x05, 0x2b, 0xe8, 0x18, + 0x11, 0x25, 0x66, 0xec, 0xe6, 0xff, 0xdf, 0xc0, 0xc9, 0x1b, 0x43, 0x9c, 0x4f, 0x4c, 0xfa, 0x29, + 0x86, 0x8f, 0x16, 0x2e, 0x5f, 0xf5, 0xff, 0xd9, 0xb3, 0x67, 0x28, 0x38, 0x3f, 0x3f, 0x9f, 0x28, + 0xb1, 0xf5, 0xeb, 0xd7, 0xff, 0x5f, 0xb6, 0x6c, 0x19, 0x86, 0xb8, 0x84, 0x84, 0xc4, 0xcd, 0x61, + 0x68, 0x91, 0x90, 0x9c, 0xd2, 0x03, 0x86, 0x8a, 0x75, 0xff, 0xc1, 0x38, 0xa2, 0xe1, 0xff, 0xa4, + 0xe9, 0xb3, 0xfe, 0x5f, 0xb8, 0x70, 0x01, 0x05, 0xa7, 0xa4, 0xa4, 0x10, 0x25, 0x36, 0x6f, 0xde, + 0xbc, 0xff, 0xd3, 0xa6, 0x4d, 0xc3, 0x10, 0x17, 0x13, 0x13, 0xbb, 0xc5, 0x20, 0x20, 0x21, 0xfb, + 0x94, 0xc1, 0x37, 0xff, 0x3f, 0x18, 0x5b, 0x06, 0xfd, 0x8f, 0x4d, 0x48, 0xfa, 0xdf, 0xda, 0xda, + 0x8a, 0x82, 0x1d, 0x1c, 0x1c, 0x88, 0x12, 0x4b, 0x4d, 0x4d, 0xfd, 0x9f, 0x90, 0x90, 0x80, 0x21, + 0x2e, 0x24, 0x24, 0xf4, 0x60, 0x34, 0x31, 0x90, 0x6f, 0x91, 0x80, 0xa4, 0xdc, 0x13, 0x86, 0x90, + 0xaa, 0xff, 0x60, 0x6c, 0x17, 0xf5, 0x3f, 0x2d, 0x2b, 0xe7, 0xff, 0xa4, 0x49, 0x93, 0x50, 0xb0, + 0x9b, 0x9b, 0x1b, 0x51, 0x62, 0x20, 0xcb, 0x33, 0x33, 0x33, 0x31, 0xc4, 0x45, 0x44, 0x44, 0xee, + 0x31, 0x08, 0xc9, 0x28, 0x3d, 0x64, 0x28, 0x5c, 0xfc, 0x1f, 0x8c, 0x83, 0x2b, 0xfe, 0x77, 0xf4, + 0x4d, 0xf8, 0x7f, 0xf8, 0xf0, 0x61, 0x14, 0x1c, 0x17, 0x17, 0x47, 0x94, 0xd8, 0x94, 0x29, 0x53, + 0xfe, 0xf7, 0xf6, 0xf6, 0x62, 0x88, 0x8b, 0x8a, 0x8a, 0xde, 0x1e, 0x4d, 0x0c, 0xe4, 0x5b, 0x84, + 0x5e, 0x7a, 0xfb, 0x06, 0x04, 0x81, 0x0d, 0x41, 0xc6, 0x66, 0x66, 0x66, 0x44, 0x89, 0x85, 0x84, + 0x84, 0xfc, 0x0f, 0x08, 0x08, 0xc0, 0x10, 0x07, 0x66, 0xd8, 0x87, 0xa3, 0x71, 0x44, 0x81, 0x45, + 0x68, 0x8d, 0x13, 0x6a, 0x5b, 0xf4, 0xe4, 0xc9, 0x13, 0x50, 0x3e, 0xba, 0x86, 0xd1, 0xdc, 0x12, + 0x97, 0x90, 0x7a, 0x2a, 0x25, 0x25, 0xf5, 0x08, 0x19, 0x03, 0x15, 0x3e, 0x27, 0x46, 0x4c, 0x5c, + 0x5c, 0xfc, 0x09, 0x10, 0xa3, 0xe8, 0x07, 0xfa, 0xe6, 0xae, 0xb0, 0xb0, 0x70, 0x10, 0xdd, 0xda, + 0x8f, 0x00, 0x62, 0x96, 0x75, 0xd5, 0x49, 0xff, 0x1d, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const BITMAP_OPAQUE spreadsheet_xpm[1] = {{ png, sizeof( png ), "spreadsheet_xpm" }}; + +//EOF diff --git a/bitmaps_png/sources/spreadsheet.svg b/bitmaps_png/sources/spreadsheet.svg new file mode 100644 index 0000000000..9dcb7d7f32 --- /dev/null +++ b/bitmaps_png/sources/spreadsheet.svg @@ -0,0 +1,440 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/dialog_shim.cpp b/common/dialog_shim.cpp index 2b1a682666..22d5515e52 100644 --- a/common/dialog_shim.cpp +++ b/common/dialog_shim.cpp @@ -97,7 +97,8 @@ DIALOG_SHIM::~DIALOG_SHIM() if( IsQuasiModal() ) EndQuasiModal( wxID_CANCEL ); - delete m_qmodal_parent_disabler; // usually NULL by now + if( m_qmodal_parent_disabler ) + delete m_qmodal_parent_disabler; // usually NULL by now } void DIALOG_SHIM::FinishDialogSettings() diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 0e295f0d15..ddeaef3109 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -94,6 +94,7 @@ const wxString ComponentFileWildcard( _( "KiCad cmp/footprint link files (*.cmp) const wxString DrillFileWildcard( _( "Drill files (*.drl)|*.drl;*.DRL" ) ); const wxString SVGFileWildcard( _( "SVG files (*.svg)|*.svg;*.SVG" ) ); const wxString HtmlFileWildcard( _( "HTML files (*.html)|*.htm;*.html" ) ); +const wxString CsvFileWildcard( _( "CSV Files (*.csv)|*.csv" ) ); const wxString PdfFileWildcard( _( "Portable document format files (*.pdf)|*.pdf" ) ); const wxString PSFileWildcard( _( "PostScript files (.ps)|*.ps" ) ); const wxString ReportFileWildcard = _( "Report files (*.rpt)|*.rpt" ); diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index c5395632b4..fd1135eaa2 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -25,6 +25,8 @@ set( EESCHEMA_DLGS dialogs/dialog_annotate_base.cpp dialogs/dialog_bom.cpp dialogs/dialog_bom_base.cpp + dialogs/dialog_bom_editor.cpp + dialogs/dialog_bom_editor_base.cpp dialogs/dialog_bom_cfg_keywords.cpp dialogs/dialog_choose_component.cpp dialogs/dialog_lib_edit_text.cpp @@ -85,6 +87,9 @@ set( EESCHEMA_SRCS backanno.cpp block.cpp block_libedit.cpp + bom_exporter.cpp + bom_table_model.cpp + bom_table_column.cpp busentry.cpp bus-wire-junction.cpp class_drc_erc_item.cpp diff --git a/eeschema/bom_exporter.cpp b/eeschema/bom_exporter.cpp new file mode 100644 index 0000000000..0c2a43f437 --- /dev/null +++ b/eeschema/bom_exporter.cpp @@ -0,0 +1,420 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +#include "bom_exporter.h" + +/** + * BOM_FILE_WRITER class (pure virtual) + */ +BOM_FILE_WRITER::BOM_FILE_WRITER() : + m_includeExtraData( true ), + m_showRowNumbers( true ) +{ +} + +void BOM_FILE_WRITER::SetHeader( const wxArrayString aHeader ) +{ + m_bomHeader = aHeader; +} + +void BOM_FILE_WRITER::AddLine( wxArrayString aLine ) +{ + m_bomLines.push_back( aLine ); +} + +/** + * Function ExtraDataPairs + * Format extra data for writing to file + */ +wxArrayString BOM_FILE_WRITER::ExtraDataPairs() +{ + wxArrayString pairs; + + if( m_groupCount != m_componentCount ) + { + pairs.Add( DataPair( _( "Group Count" ), m_groupCount ) ); + } + + pairs.Add( DataPair( _( "Component Count" ), m_componentCount ) ); + pairs.Add( DataPair( _( "Schematic Title" ), m_schematicTitle ) ); + pairs.Add( DataPair( _( "Schematic Date" ), m_schematicDate ) ); + pairs.Add( DataPair( _( "Schematic Version" ), m_schematicVersion ) ); + pairs.Add( DataPair( _( "KiCad Version" ), m_kicadVersion ) ); + + return pairs; +} + +BOM_CSV_WRITER::BOM_CSV_WRITER( wxChar aDelim ) : + BOM_FILE_WRITER() +{ + m_delim = aDelim; +} + +/** + * Function WriteToFile + * Write delimited data to file + */ +bool BOM_CSV_WRITER::WriteToFile( wxFile& aFile ) +{ + + // Generate table header + wxString line = wxJoin( EscapeLine( m_bomHeader ), m_delim )+ "\n"; + + if( m_showRowNumbers ) + { + line = m_delim + line; + } + + if( !aFile.Write( line )) + { + return false; + } + + // Write each line in the file + for( unsigned int ii=0; ii\n" ) ) + return false; + + if( !aFile.Write( TableHeader( m_bomHeader ) ) ) + return false; + + // Write each line of the BOM + for( unsigned int ii=0; ii\n" ) ) + return false; + + if( !aFile.Write( HtmlFooter() ) ) + return false; + + return true; +} + +wxString BOM_HTML_WRITER::HtmlHeader() +{ + wxString header = wxEmptyString; + + header += "\n\n"; + //TODO - Project title + header += ""; + header += m_schematicTitle; + header += "\n"; + + header += HtmlMetaTag( "charset", "UTF-8" ) + "\n"; + + //TODO - KiCad reference data here + + header += "\n\n"; + + header += "\n"; + + return header; +} + +wxString BOM_HTML_WRITER::HtmlFooter() +{ + wxString footer = wxEmptyString; + + footer += "\n\n"; + footer += "\n"; + + return footer; +} + +wxString BOM_HTML_WRITER::HtmlMetaTag( const wxString aTitle, const wxString aData ) +{ + wxString tag = "\n"; + + for( wxString pair : ExtraDataPairs() ) + { + extra += pair + "\n"; + } + + extra += "\n"; + + return extra; +} + +/** + * Function LinkText + * Automatically detect linkable text, and wrap it in tag + * @aText - Text to (potentially) link + */ +wxString BOM_HTML_WRITER::LinkText( const wxString& aText ) +{ + // Should we provide a link to the text? + wxString lower = aText.Lower(); + + bool found = false; + + for( wxString check : m_linkChecks ) + { + if( lower.Matches( check ) ) + { + found = true; + break; + } + } + + if( found ) + { + wxString link = ""; + link += aText; + link += ""; + + return link; + } + else + { + return aText; + } +} + +wxString BOM_HTML_WRITER::TableHeader( const wxArrayString& aHeaderData ) +{ + wxString header = "\n"; + + if( m_showRowNumbers ) + { + header += "\n"; + } + + for( wxString item : aHeaderData ) + { + header += ""; + header += item; + header += ""; + header += "\n"; + } + + header += "\n"; + + return header; +} + +wxString BOM_HTML_WRITER::DataPair( const wxString& aFirst, const wxString& aSecond ) +{ + wxString html = "\n"; + + html += TableEntry( aFirst ) + "\n"; + html += TableEntry( aSecond ) + "\n"; + + html += "\n"; + + return html; +} + +/** + * Function TableRow + * Generate a single row of BOM data + * @aRowNum is the number of the row + * @aRowData is the array of data for the given row + */ +wxString BOM_HTML_WRITER::TableRow( const int& aRowNum, const wxArrayString& aRowData ) +{ + wxString row = wxEmptyString; + + row += "\n"; + + if( m_showRowNumbers ) + { + row += ""; + row += wxString::Format( "%i", aRowNum ); + row += "\n"; + } + + for( wxString data : aRowData ) + { + row += TableEntry( data ); + row += "\n"; + } + + row += "\n"; + + return row; +} + +/** + * Function TableEntry + * Wrap a string in tags + * @aData is the text to be wrapped + * @aColor is an (optional) HTML background color + */ +wxString BOM_HTML_WRITER::TableEntry( wxString aData, wxString aColor ) +{ + wxString cell = " +#include + +#include "bom_table_column.h" +#include "bom_table_model.h" + +enum BOM_EXPORT_TYPE +{ + BOM_EXPORT_CSV = 0, + BOM_EXPORT_HTML +}; + +class BOM_FILE_WRITER +{ +public: + BOM_FILE_WRITER(); + virtual ~BOM_FILE_WRITER() {}; + + virtual bool WriteToFile( wxFile& aFile ) = 0; + + void SetHeader( const wxArrayString aHeader ); + void AddLine( const wxArrayString aLine ); + + void IncludeExtraData( bool aInclude = true ) { m_includeExtraData = aInclude; } + void ShowRowNumbers( bool aShow = true ) { m_showRowNumbers = aShow; } + + // Project information + void SetKicadVersion( const wxString aVersion ) { m_kicadVersion = aVersion; } + void SetSchematicTitle( const wxString aProject ) { m_schematicTitle = aProject; } + void SetSchematicVersion( const wxString aVersion ) { m_schematicVersion = aVersion; } + void SetSchematicDate( const wxString aDate ) { m_schematicDate = aDate; } + + void SetGroupCount( unsigned int aCount ) { m_groupCount = wxString::Format( "%u", aCount ); } + void SetComponentCount( unsigned int aCount ) { m_componentCount = wxString::Format( "%u", aCount ); } + +protected: + wxArrayString ExtraDataPairs(); + virtual wxString DataPair( const wxString& aFirst, const wxString& aSecond ) = 0; + + wxArrayString m_bomHeader; + std::vector< wxArrayString > m_bomLines; + + bool m_includeExtraData; + bool m_showRowNumbers; + + // Extra details for BOM file + wxString m_kicadVersion; + wxString m_schematicTitle; + wxString m_schematicVersion; + wxString m_schematicDate; + + wxString m_componentCount; + wxString m_groupCount; + +}; + +class BOM_CSV_WRITER : public BOM_FILE_WRITER +{ +public: + BOM_CSV_WRITER( wxChar aDelim=',' ); + + virtual bool WriteToFile( wxFile& aFile ) override; + +protected: + wxArrayString EscapeLine( wxArrayString line ); + virtual wxString DataPair( const wxString& aFirst, const wxString& aSecond ) override; + + wxChar m_delim; +}; + +class BOM_HTML_WRITER : public BOM_FILE_WRITER +{ +public: + BOM_HTML_WRITER(); + + virtual bool WriteToFile( wxFile& aFile ) override; + +protected: + virtual wxString DataPair( const wxString& aFirst, const wxString& aSecond ) override; + + wxString HtmlHeader(); + wxString HtmlFooter(); + wxString HtmlMetaTag( const wxString aTitle, const wxString aData ); + + wxString ExtraData(); + + wxString LinkText( const wxString& aText ); + + wxString TableHeader( const wxArrayString& aHeaderData ); + wxString TableRow( const int& aRowNum, const wxArrayString& aRowData ); + wxString TableEntry( wxString aData, wxString aColor = wxEmptyString ); + + wxArrayString m_linkChecks; +}; + +#endif /* EESCHEMA_BOM_EXPORTER_H_ */ diff --git a/eeschema/bom_table_column.cpp b/eeschema/bom_table_column.cpp new file mode 100644 index 0000000000..01b71205c7 --- /dev/null +++ b/eeschema/bom_table_column.cpp @@ -0,0 +1,136 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +#include "bom_table_column.h" + +BOM_COLUMN_LIST::BOM_COLUMN_LIST() : m_nextFieldId( BOM_COL_ID_USER ) +{ + +} + +void BOM_COLUMN_LIST::Clear() +{ + Columns.clear(); + m_nextFieldId = BOM_COL_ID_USER; +} + +/** + * Return the number of columns + * @param aIncludeHidden - If this is false, only visible columns will be included + */ +unsigned int BOM_COLUMN_LIST::ColumnCount( bool aIncludeHidden ) const +{ + unsigned int count = 0; + + for( BOM_COLUMN* col : Columns ) + { + if( col && ( col->IsVisible() || aIncludeHidden ) ) + { + count++; + } + } + + return count; +} + +/** + * Return a column based on its stored position + */ +BOM_COLUMN* BOM_COLUMN_LIST::GetColumnByIndex( unsigned int aColId ) +{ + if( aColId < Columns.size() ) + return Columns[aColId]; + + return nullptr; +} + +/** + * Return a column based on its unique ID + */ +BOM_COLUMN* BOM_COLUMN_LIST::GetColumnById( unsigned int aColId ) +{ + for( unsigned int ii=0; iiId() == aColId ) + return Columns[ii]; + } + + return nullptr; +} + +/** + * Return a column based on its string title + */ +BOM_COLUMN* BOM_COLUMN_LIST::GetColumnByTitle( wxString aColTitle ) +{ + for( unsigned int ii=0; iiTitle().CmpNoCase( aColTitle ) == 0 ) + return Columns[ii]; + } + + return nullptr; +} + +/** + * Test if the list includes a column with the given unique ID + */ +bool BOM_COLUMN_LIST::ContainsColumn( unsigned int aColId ) +{ + for( BOM_COLUMN* col : Columns ) + { + if( col && col->Id() == aColId ) + return true; + } + + return false; +} + +/** + * Test if the list includes a column with the given title + */ +bool BOM_COLUMN_LIST::ContainsColumn( wxString aColTitle ) +{ + return nullptr != GetColumnByTitle( aColTitle ); +} + +/** + * Add a new column to the list + */ +bool BOM_COLUMN_LIST::AddColumn( BOM_COLUMN* aCol ) +{ + if( nullptr == aCol ) + return false; + + if( ContainsColumn( aCol->Id() ) ) + return false; + + Columns.push_back( aCol ); + + // If this is a user field, increment the counter + if( aCol->Id() >= BOM_COL_ID_USER ) + m_nextFieldId++; + + return true; +} diff --git a/eeschema/bom_table_column.h b/eeschema/bom_table_column.h new file mode 100644 index 0000000000..3e297566e3 --- /dev/null +++ b/eeschema/bom_table_column.h @@ -0,0 +1,147 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +#ifndef EESCHEMA_BOM_TABLE_COLUMN_H_ +#define EESCHEMA_BOM_TABLE_COLUMN_H_ + +#include +#include +#include + +// Default column names (translated) +#define BOM_COL_TITLE_REFERENCE _( "Reference" ) +#define BOM_COL_TITLE_DESCRIPTION _( "Description" ) +#define BOM_COL_TITLE_FOOTPRINT _( "Footprint" ) +#define BOM_COL_TITLE_VALUE _( "Value" ) +#define BOM_COL_TITLE_DATASHEET _( "Datasheet" ) +#define BOM_COL_TITLE_QUANTITY _( "Quantity" ) + +/** + * Column type enumeration + * Not currently implemented, + * in the future the different column 'types' might + * be used for something... + */ +enum BOM_COLUMN_TYPE +{ + BOM_COL_TYPE_KICAD = 0, ///< Default column (editable) + BOM_COL_TYPE_LIBRARY, ///< Default column (non-editable) + BOM_COL_TYPE_GENERATED, ///< Generated column (e.g. Quantity) + BOM_COL_TYPE_USER, ///< User data +}; + + +/** + * Predefined column ID values for default columns. + * User columns are assigned IDs of 1000 and above + */ +enum BOM_COLUMN_ID +{ + // Default component fields + BOM_COL_ID_REFERENCE = 0, + BOM_COL_ID_DESCRIPTION, + BOM_COL_ID_FOOTPRINT, + BOM_COL_ID_VALUE, + BOM_COL_ID_DATASHEET, + + // Meta-data fields + BOM_COL_ID_QUANTITY = 100, + + // Custom data fields + BOM_COL_ID_USER = 1000, +}; + +/** + * BOM_COLUMN class + * Models a single column in the BOM view + * Each column can be used to group components, + * and can be hidden from the output BOM + */ +class BOM_COLUMN +{ +protected: + unsigned int m_id; ///< Unique column ID + BOM_COLUMN_TYPE m_Type; ///< Column type + + wxString m_Title; ///< The column (field) title + bool m_Show; ///< Is this column visible? + bool m_ReadOnly; ///< Is this column read only? + +public: + BOM_COLUMN( unsigned int aId, BOM_COLUMN_TYPE aType, const wxString aTitle, bool aShow, bool aReadOnly = false ) : + m_id( aId ), + m_Type( aType ), + m_Title( aTitle.Strip( wxString::both ) ), + m_Show( aShow ), + m_ReadOnly( aReadOnly ) + { + } + + unsigned int Id() const { return m_id; } + BOM_COLUMN_TYPE Type() const { return m_Type; } + wxString Title() const { return m_Title; } + bool IsVisible() const { return m_Show; } + bool IsReadOnly() const { return m_ReadOnly; } + + //TODO - Should renaming of columns be allowed? + //bool SetTitle( const wxString aTitle ); + void SetVisible( bool aShow = true ) { m_Show = aShow; } + void SetReadOnly( bool aReadOnly = true ) { m_ReadOnly = aReadOnly; } + +}; + +/* + * The BOM_COLUMN_LIST class contains information + * on all columns existing in the BOM + */ +class BOM_COLUMN_LIST +{ +protected: + + unsigned int m_nextFieldId; + +public: + std::vector< BOM_COLUMN* > Columns; + + BOM_COLUMN_LIST(); + + void Clear(); + + unsigned int NextFieldId() const { return m_nextFieldId; } + unsigned int ColumnCount( bool aIncludeHidden = true ) const; + + BOM_COLUMN* GetColumnByIndex( unsigned int aColIndex ); + BOM_COLUMN* GetColumnById( unsigned int aColId ); + BOM_COLUMN* GetColumnByTitle( const wxString aColTitle ) ; + + bool ContainsColumn( unsigned int aColId ); + bool ContainsColumn( const wxString aColTitle ); + + bool AddColumn( BOM_COLUMN* aCol ); + +}; + + + +#endif /* EESCHEMA_BOM_TABLE_COLUMN_H_ */ diff --git a/eeschema/bom_table_model.cpp b/eeschema/bom_table_model.cpp new file mode 100644 index 0000000000..a43d749042 --- /dev/null +++ b/eeschema/bom_table_model.cpp @@ -0,0 +1,1404 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +#include "bom_table_model.h" + +// Indicator that multiple values exist in child rows +#define ROW_MULT_ITEMS wxString( "<...>" ) + +static const wxColor ROW_COLOUR_ITEM_CHANGED( 200, 0, 0 ); +static const wxColor ROW_COLOUR_MULTIPLE_ITEMS( 60, 90, 200 ); + +/** + * Convert BOM_TABLE_ROW -> wxDataViewItem + */ +static wxDataViewItem RowToItem( BOM_TABLE_ROW const* aRow ) +{ + return wxDataViewItem( const_cast( static_cast( aRow ) ) ); +} + +/** + * Convert wxDataViewItem -> BOM_TABEL_ROW + */ +static BOM_TABLE_ROW const* ItemToRow( wxDataViewItem aItem ) +{ + if( !aItem.IsOk() ) + { + return nullptr; + } + else + { + return static_cast( aItem.GetID() ); + } +} + +BOM_TABLE_ROW::BOM_TABLE_ROW() : m_columnList( nullptr ) +{ +} + +/** + * Update cell attributes based on parameters of the cell + * Default implementation highlights cells that have been altered + */ +bool BOM_TABLE_ROW::GetAttr( unsigned int aFieldId, wxDataViewItemAttr& aAttr ) const +{ + auto field = m_columnList->GetColumnById( aFieldId ); + + if( HasValueChanged( field ) ) + { + aAttr.SetBold( true ); + aAttr.SetItalic( true ); + aAttr.SetColour( ROW_COLOUR_ITEM_CHANGED ); + return true; + } + + return false; +} + +bool BOM_TABLE_ROW::HasChanged() const +{ + if( !m_columnList ) + return false; + + for( auto& column : m_columnList->Columns ) + { + if( column && HasValueChanged( column ) ) + { + return true; + } + } + + return false; +} + +/** + * Create a new group (which contains one or more components) + */ +BOM_TABLE_GROUP::BOM_TABLE_GROUP( BOM_COLUMN_LIST* aColumnList ) +{ + m_columnList = aColumnList; +} + +bool BOM_TABLE_GROUP::GetAttr( unsigned int aFieldId, wxDataViewItemAttr& aAttr ) const +{ + if( GetFieldValue( aFieldId ).Cmp( ROW_MULT_ITEMS ) == 0 ) + { + aAttr.SetItalic( true ); + aAttr.SetColour( ROW_COLOUR_MULTIPLE_ITEMS ); + return true; + } + + return BOM_TABLE_ROW::GetAttr( aFieldId, aAttr ); +} + +/** + * Return the value associated with a given field in the group. + * Some fields require special attention. + */ +wxString BOM_TABLE_GROUP::GetFieldValue( unsigned int aFieldId ) const +{ + wxString value; + + // Account for special cases + switch( aFieldId ) + { + // QUANTITY returns the size of the group + case BOM_COL_ID_QUANTITY: + value = wxString::Format( "%u", (unsigned int) GroupSize() ); + break; + // REFERENCE field returns consolidated list of references + case BOM_COL_ID_REFERENCE: + value = wxJoin( GetReferences(), ' ' ); + break; + // Otherwise, return component data + default: + if( Components.size() == 0 ) + { + value = wxEmptyString; + } + else + { + // If the components in this group contain multiple items, + // display a special string indicating this + for( unsigned int i=0; iGetFieldValue( aFieldId ); + } + // Mismatch found + else if( value.Cmp( cmp->GetFieldValue( aFieldId ) ) != 0 ) + { + value = ROW_MULT_ITEMS; + break; + } + } + } + break; + } + + return value; +} + +/** + * Set the value of a field in a group + * The new value is pushed to all components that are children of this group + */ +bool BOM_TABLE_GROUP::SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite ) +{ + bool result = false; + + for( auto& cmp : Components ) + { + result |= cmp->SetFieldValue( aFieldId, aValue, aOverwrite ); + } + + return result; +} + +/** + * Determines if a given component matches against a particular field. + * + * - Tests each field in turn; all fields must match + * - Some fields require special checking + * + * @aFieldId - The ID of the field + * @aCmp - The component being tested + */ +bool BOM_TABLE_GROUP::TestField( BOM_COLUMN* aField, BOM_TABLE_COMPONENT* aComponent ) const +{ + if( !aField || !aComponent ) + return false; + + if( Components.size() == 0 ) + return true; + + wxString componentValue; + wxString comparisonValue; + + // Some fields are handled in a special manner + // (handle these first) + switch( aField->Id() ) + { + // These fields should NOT be compared (return True) + case BOM_COL_ID_QUANTITY: + return true; + // Reference matching is done only on prefix + case BOM_COL_ID_REFERENCE: + componentValue = aComponent->GetPrefix(); + comparisonValue = Components[0]->GetPrefix(); + break; + default: + componentValue = aComponent->GetFieldValue( aField->Id() ); + comparisonValue = Components[0]->GetFieldValue( aField->Id() ); + break; + } + + bool result = componentValue.Cmp( comparisonValue ) == 0; + + return result; +} + +/** + * Add a new component to the group. + * It is assumed at this stage that the component is a good match for the group. + * @param aCmp is the new component to add + */ +bool BOM_TABLE_GROUP::AddComponent( BOM_TABLE_COMPONENT* aComponent ) +{ + if( !aComponent ) + return false; + + // To be a match, all fields must match! + bool match = true; + + for( auto* column : m_columnList->Columns ) + { + match = TestField( column, aComponent ); + + // Escape on first mismatch + if( !match ) + { + break; + } + } + + if( match ) + { + aComponent->SetParent( this ); + Components.push_back( aComponent ); + return true; + } + else + { + return false; + } + + return false; +} + + +/** + * Adds each child row to the supplied list, and returns the total child count + */ +unsigned int BOM_TABLE_GROUP::GetChildren( wxDataViewItemArray& aChildren ) const +{ + // Show drop-down for child components + for( auto& row : Components ) + { + if( row ) + { + aChildren.push_back( RowToItem( &*row ) ); + } + } + + return aChildren.size(); +} + +/** + * Test if any components in this group have a new value in the provided field + * @param aField is the field to test + * @return true if any children have changed else false + */ +bool BOM_TABLE_GROUP::HasValueChanged( BOM_COLUMN* aField ) const +{ + + bool changed = false; + + if( !aField ) + return false; + + for( auto const& row : Components ) + { + if( row->HasValueChanged( aField ) ) + { + changed = true; + break; + } + } + + // If the value has changed, the group field data must be updated + if( changed ) + { + //TODO + } + + return changed; +} + +/** + * Return a list of (ordered) references + * for all the components in this group + * + * @aSort - Sort the references + */ +wxArrayString BOM_TABLE_GROUP::GetReferences( bool aSort) const +{ + wxArrayString refs; + + for( auto const& cmp : Components ) + { + if( cmp ) + { + refs.Add( cmp->GetFieldValue( BOM_COL_ID_REFERENCE ) ); + } + } + + if( aSort ) + { + refs.Sort( SortReferences ); + } + + return refs; +} + +/** + * Compare two references (e.g. "R100", "R19") and perform a 'natural' sort + * This sorting must preserve numerical order rather than alphabetical + * e.g. "R100" is lower (alphabetically) than "R19" + * BUT should be placed after R19 + */ +int BOM_TABLE_GROUP::SortReferences( const wxString& aFirst, const wxString& aSecond ) +{ + // Default sorting + int defaultSort = aFirst.Cmp( aSecond ); + + static const wxString REGEX_STRING = "^([a-zA-Z]+)(\\d+)$"; + + // Compile regex statically + static wxRegEx regexFirst( REGEX_STRING, wxRE_ICASE | wxRE_ADVANCED ); + static wxRegEx regexSecond( REGEX_STRING, wxRE_ICASE | wxRE_ADVANCED ); + + if( !regexFirst.Matches( aFirst ) || !regexSecond.Matches( aSecond ) ) + { + return defaultSort; + } + + // First priority is to order by prefix + wxString prefixFirst = regexFirst.GetMatch( aFirst, 1 ); + wxString prefixSecond = regexSecond.GetMatch( aSecond, 1 ); + + if( prefixFirst.CmpNoCase( prefixSecond ) != 0 ) // Different prefixes! + { + return defaultSort; + } + + wxString numStrFirst = regexFirst.GetMatch( aFirst, 2 ); + wxString numStrSecond = regexSecond.GetMatch( aSecond, 2 ); + + // If either match failed, just return normal string comparison + if( numStrFirst.IsEmpty() || numStrSecond.IsEmpty() ) + { + return defaultSort; + } + + // Convert each number string to an integer + long numFirst = 0; + long numSecond = 0; + + // If either conversion fails, return normal string comparison + if( !numStrFirst.ToLong( &numFirst ) || !numStrSecond.ToLong( &numSecond ) ) + { + return defaultSort; + } + + return (int) (numFirst - numSecond); +} + +/** + * Compare two VALUE fields. + * A value field can reasonably be expected to be one of: + * a) Purely numerical e.g. '22' + * b) Numerical with included units e.g. '15uF' + * c) Numerical with included prefix but no units e.g. '20n' + * d) Numerical with prefix inside number e.g. '4K7' + * e) Other, e.g. 'MAX232' + * + * Cases a) to d) should be detected and converted to a common representation + * Values that do not match this pattern should revert to standard string comparison + */ +int BOM_TABLE_GROUP::SortValues( const wxString& aFirst, const wxString& aSecond ) +{ + //TODO - Intelligent comparison of component values + // e.g. 4K > 499 + // e.g. 1nF < 0.1u + + // For now, just return default comparison + + return aFirst.CmpNoCase( aSecond ); +} + +/** + * Create a new COMPONENT row + * Each COMPONENT row is associated with a single component item. + */ +BOM_TABLE_COMPONENT::BOM_TABLE_COMPONENT( BOM_TABLE_GROUP* aParent, + BOM_COLUMN_LIST* aColumnList) +{ + m_parent = aParent; + m_columnList = aColumnList; +} + +/** + * Try to add a unit to this component + * If the references match, it will be added + */ +bool BOM_TABLE_COMPONENT::AddUnit( SCH_REFERENCE aUnit ) +{ + // Addition is successful if the references match or there are currently no units in the group + if( Units.size() == 0 || Units[0].GetRef().Cmp( aUnit.GetRef() ) == 0 ) + { + Units.push_back( aUnit ); + + wxString value; + + // Extract the component data + for( auto column : m_columnList->Columns ) + { + auto cmp = aUnit.GetComp(); + + switch( column->Id() ) + { + case BOM_COL_ID_QUANTITY: + value = wxEmptyString; + break; + case BOM_COL_ID_DESCRIPTION: + value = cmp->GetAliasDescription(); + break; + case BOM_COL_ID_DATASHEET: + value = cmp->GetField( DATASHEET )->GetText(); + if( value.IsEmpty() ) + { + value = cmp->GetAliasDocumentation(); + } + break; + case BOM_COL_ID_REFERENCE: + value = cmp->GetField( REFERENCE )->GetText(); + break; + case BOM_COL_ID_VALUE: + value = cmp->GetField( VALUE )->GetText(); + break; + case BOM_COL_ID_FOOTPRINT: + value = cmp->GetField( FOOTPRINT )->GetText(); + break; + + // User fields + default: + value = cmp->GetFieldText( column->Title() ); + break; + } + + SetFieldValue( column->Id(), value ); + + // Add the value to the fallback map + m_fallbackData[column->Id()] = value; + } + + return true; + } + + return false; +} + +/** + * Return the value associated with a particular field + * If no field is found, return an empty string + */ +wxString BOM_TABLE_COMPONENT::GetFieldValue( unsigned int aFieldId ) const +{ + auto search = m_fieldData.find( aFieldId ); + + if( search != m_fieldData.end() ) + { + return search->second; + } + else + { + return wxEmptyString; + } +} + +/** + * Set the value of a field in the component + * @param aFieldId is the unique ID of the field to update + * @param aValue is the new value + * @param aOverwrite enforces writing even if a value exists + */ +bool BOM_TABLE_COMPONENT::SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite ) +{ + if( aOverwrite || m_fieldData.count( aFieldId ) == 0 || m_fieldData[aFieldId].IsEmpty() ) + { + m_fieldData[aFieldId] = aValue; + return true; + } + + return false; +} + +/** + * Return the prefix of a component e.g. "R23" -> "R" + */ +wxString BOM_TABLE_COMPONENT::GetPrefix() const +{ + if( Units.size() == 0 ) + return wxEmptyString; + + return Units[0].GetComp()->GetPrefix(); +} + +/** + * Return the reference of a component e.g. "R23" + */ +wxString BOM_TABLE_COMPONENT::GetReference() const +{ + if( Units.size() == 0 ) + return wxEmptyString; + + return Units[0].GetRef(); +} + +/** + * Determines if the given field has been changed for this component + */ +bool BOM_TABLE_COMPONENT::HasValueChanged( BOM_COLUMN* aField ) const +{ + if( !aField ) + { + return false; + } + + auto value = m_fieldData.find( aField->Id() ); + auto backup = m_fallbackData.find( aField->Id() ); + + wxString currentValue = value == m_fieldData.end() ? wxString() : value->second; + wxString backupValue = backup == m_fallbackData.end() ? wxString() : backup->second; + + return currentValue.Cmp( backupValue ) != 0; +} + +/** + * If any changes have been made to this component, + * they are now applied to the schematic component + */ +void BOM_TABLE_COMPONENT::ApplyFieldChanges() +{ + for( auto& unit : Units ) + { + auto cmp = unit.GetComp(); + + if( !cmp ) + continue; + + // Iterate over each column + SCH_FIELD* field; + + for( auto& column : m_columnList->Columns ) + { + if( column && HasValueChanged( column ) ) + { + wxString value = GetFieldValue( column->Id() ); + + switch( column->Id() ) + { + // Ignore read-only fields + case BOM_COL_ID_REFERENCE: + case BOM_COL_ID_QUANTITY: + continue; + // Special field considerations + case BOM_COL_ID_FOOTPRINT: + field = cmp->GetField( FOOTPRINT ); + break; + case BOM_COL_ID_VALUE: + field = cmp->GetField( VALUE ); + break; + case BOM_COL_ID_DATASHEET: + field = cmp->GetField( DATASHEET ); + break; + default: + field = cmp->FindField( column->Title() ); + break; + } + + if( !field && !value.IsEmpty() ) + { + SCH_FIELD newField( wxPoint( 0, 0 ), -1, cmp, column->Title() ); + field = cmp->AddField( newField ); + } + + if( field ) + { + field->SetText( value ); + } + } + } + } +} + +/** + * Revert the displayed fields for this component + * to their original values (matching the schematic data) + */ +void BOM_TABLE_COMPONENT::RevertFieldChanges() +{ + for( auto& column : m_columnList->Columns ) + { + switch( column->Id() ) + { + case BOM_COL_ID_REFERENCE: + case BOM_COL_ID_QUANTITY: + continue; + default: + break; + } + + if( column && HasValueChanged( column ) ) + { + if( m_fallbackData.count( column->Id() ) > 0 ) + { + m_fieldData[column->Id()] = m_fallbackData[column->Id()]; + } + else + { + m_fieldData[column->Id()] = wxEmptyString; + } + } + } +} + +BOM_TABLE_MODEL::BOM_TABLE_MODEL() : + m_widget( nullptr ), + m_sortingColumn( BOM_COL_ID_REFERENCE ), + m_sortingOrder( true ) +{ + //TODO +} + +/** + * Create a container for the BOM_TABLE_MODEL + * This is required for reference counting by wxDataViewCtrl + */ +BOM_TABLE_MODEL::MODEL_PTR BOM_TABLE_MODEL::Create() +{ + auto model = new BOM_TABLE_MODEL(); + + auto container = BOM_TABLE_MODEL::MODEL_PTR( model ); + + return container; +} + +BOM_TABLE_MODEL::~BOM_TABLE_MODEL() +{ + //TODO +} + +wxDataViewColumn* BOM_TABLE_MODEL::AddColumn( BOM_COLUMN* aColumn, int aPosition ) +{ + static const unsigned int flags = wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE; + + if( !m_widget || !aColumn || !aColumn->IsVisible() ) + return nullptr; + + wxDataViewCellMode editFlag = aColumn->IsReadOnly() ? wxDATAVIEW_CELL_INERT : wxDATAVIEW_CELL_EDITABLE; + + auto renderer = new wxDataViewTextRenderer( "string" , editFlag ); + + auto column = new wxDataViewColumn( aColumn->Title(), + renderer, + aColumn->Id(), + 150, //TODO - variable default width? + wxAlignment( wxALIGN_CENTER ), + flags ); + + // Work out where to insert the column + std::set columnsBefore; + + for( auto testCol : ColumnList.Columns ) + { + if( testCol->Id() == aColumn->Id() ) + { + break; + } + else + { + columnsBefore.insert( testCol->Id() ); + } + } + + bool found = false; + + for( unsigned int ii=0; iiGetColumnCount(); ii++ ) + { + auto col = m_widget->GetColumn( ii ); + + if( !col ) + continue; + + // If the new column is already in the view, escape + if( col->GetModelColumn() == aColumn->Id() ) + { + return col; + } + + // If we should insert the new column BEFORE this one + if( columnsBefore.count( col->GetModelColumn() ) == 0 ) + { + found = true; + m_widget->InsertColumn( ii, column ); + break; + } + } + + if( !found ) + m_widget->AppendColumn( column ); + + //TODO - wxCOL_WIDTH_AUTOSIZE prevents columns from thereafter being resized + // This requires some further attention + + /** + column->SetWidth( wxCOL_WIDTH_AUTOSIZE ); + column->SetWidth( column->GetWidth() ); + **/ + column->SetResizeable( true ); + + return column; +} + +/** + * Gracefully remove the given column from the wxDataViewCtrl + * Removing columns individually prevents bad redraw of entire table + */ +bool BOM_TABLE_MODEL::RemoveColumn( BOM_COLUMN* aColumn ) +{ + if( !m_widget || !aColumn ) + return false; + + for( unsigned int ii=0; iiGetColumnCount(); ii++ ) + { + auto col = m_widget->GetColumn( ii ); + + if( col && col->GetModelColumn() == aColumn->Id() ) + { + m_widget->DeleteColumn( col ); + return true; + } + } + + return false; +} + +/** + * Attach the MODEL to a particular VIEW + * This function causes the view to be updated appropriately + */ +void BOM_TABLE_MODEL::AttachTo( wxDataViewCtrl* aView ) +{ + if( !aView ) + { + return; + } + + m_widget = aView; + aView->Freeze(); + Cleared(); + + aView->AssociateModel( this ); + aView->ClearColumns(); + + // Add all columns + for( auto col : ColumnList.Columns ) + { + AddColumn( col ); + } + + aView->Thaw(); + + // Notify the view that the data needs to be redrawn + aView->Update(); +} + +/** + * Return the total number of components displayed by the model + */ +unsigned int BOM_TABLE_MODEL::ComponentCount() const +{ + unsigned int count = 0; + + for( auto& group : Groups ) + { + if( group ) + count += group->GroupSize(); + } + + return count; +} + +void BOM_TABLE_MODEL::ClearColumns() +{ + ColumnList.Clear(); +} + +/** + * Add default columns to the table + * These columns are ALWAYS available in the table + * They are immutable - can be hidden by user but not removed + */ +void BOM_TABLE_MODEL::AddDefaultColumns() +{ + // Reference column is read-only + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_REFERENCE, + BOM_COL_TYPE_GENERATED, + BOM_COL_TITLE_REFERENCE, + true, true ) ); + + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_VALUE, + BOM_COL_TYPE_KICAD, + BOM_COL_TITLE_VALUE, + true, false ) ); + + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_FOOTPRINT, + BOM_COL_TYPE_KICAD, + BOM_COL_TITLE_FOOTPRINT, + true, false ) ); + + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_DATASHEET, + BOM_COL_TYPE_KICAD, + BOM_COL_TITLE_DATASHEET, + true, false ) ); + + // Description comes from .dcm file and is read-only + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_DESCRIPTION, + BOM_COL_TYPE_LIBRARY, + BOM_COL_TITLE_DESCRIPTION, + true, true ) ); + + // Quantity column is read-only + ColumnList.AddColumn( new BOM_COLUMN( + BOM_COL_ID_QUANTITY, + BOM_COL_TYPE_GENERATED, + BOM_COL_TITLE_QUANTITY, + true, true ) ); +} + +/** + * Extract field data from all components + * Compiles an inclusive list of all field names from all components + */ +void BOM_TABLE_MODEL::AddComponentFields( SCH_COMPONENT* aCmp ) +{ + std::vector< SCH_FIELD* > fields; + + SCH_FIELD* field; + wxString fieldName; + + if( nullptr == aCmp ) + return; + + // Extract custom columns from component + fields.clear(); + aCmp->GetFields( fields, false ); + + // Iterate over custom field datas + for( unsigned int i=MANDATORY_FIELDS; iGetName(); + + auto existing = ColumnList.GetColumnByTitle( fieldName ); + + // As columns are sorted by ID, we can allow user to + // create a column with a "special" name + if( existing && existing->Id() >= MANDATORY_FIELDS) + continue; + + ColumnList.AddColumn( new BOM_COLUMN( ColumnList.NextFieldId(), + BOM_COL_TYPE_USER, + field->GetName(), + true, false ) ); + } +} + +/** + * Add a list of component items to the BOM manager + * Creates consolidated groups of components as required + */ +void BOM_TABLE_MODEL::SetComponents( SCH_REFERENCE_LIST aRefs ) +{ + // Add default columns + AddDefaultColumns(); + + // Extract all component fields + for( unsigned int ii=0; iiAddUnit( ref ) ) + { + found = true; + break; + } + } + + if( !found ) + { + auto* newComponent = new BOM_TABLE_COMPONENT( nullptr, &ColumnList ); + newComponent->AddUnit( ref ); + + m_components.push_back( std::unique_ptr( newComponent ) ); + } + } +} + +/** + * Recalculate grouping of components and reload table + **/ +void BOM_TABLE_MODEL::ReloadTable() +{ + if( m_widget ) + { + m_widget->Freeze(); + } + + // Alert the view that the model data has changed + Cleared(); + + Groups.clear(); + + for( auto& cmp : m_components ) + { + bool grouped = false; + + if( m_groupColumns ) + { + for( auto& group : Groups ) + { + if( group->AddComponent( &*cmp ) ) + { + grouped = true; + break; + } + } + } + + // No suitable group was found for this component + if( !grouped ) + { + auto* newGroup = new BOM_TABLE_GROUP( &ColumnList ); + + newGroup->AddComponent( &*cmp ); + + Groups.push_back( std::unique_ptr( newGroup ) ); + } + } + + // Update the display + if( m_widget ) + { + //Cleared(); + m_widget->AssociateModel( this ); + m_widget->Thaw(); + } +} + +/** + * Return a string array of data from a given row + */ +wxArrayString BOM_TABLE_MODEL::GetRowData( unsigned int aRow, std::vector aColumns ) const +{ + wxArrayString row; + + wxString data; + + if( Groups.size() <= aRow ) + return row; + + auto const& group = Groups[aRow]; + + if ( !group ) + return row; + + for( auto const col : aColumns ) + { + if( !col ) + { + row.Add( wxEmptyString ); + continue; + } + + row.Add( group->GetFieldValue( col->Id() ) ); + } + + return row; +} + +/** + * Get the value of a particular item in the model + */ +void BOM_TABLE_MODEL::GetValue( + wxVariant& aVariant, + const wxDataViewItem& aItem, + unsigned int aFieldId ) const +{ + auto row = ItemToRow( aItem ); + + if( row ) + { + aVariant = row->GetFieldValue( aFieldId ); + } +} + +/** + * Set the value of a particular item in the model + */ +bool BOM_TABLE_MODEL::SetValue( + const wxVariant& aVariant, + const wxDataViewItem& aItem, + unsigned int aFieldId ) +{ + if( !aItem.IsOk() || !m_widget ) + { + return false; + } + + // Extract the value to be set + if( aVariant.GetType().Cmp( "string" ) == 0 ) + { + wxString value = aVariant.GetString(); + + bool result = false; + + wxDataViewItemArray selectedItems; + m_widget->GetSelections( selectedItems ); + + // Set the row value for all selected rows + + for( auto item : selectedItems ) + { + auto selectedRow = static_cast( item.GetID() ); + + if( selectedRow ) + { + result |= selectedRow->SetFieldValue( aFieldId, value, true ); + } + } + + return result; + } + + // Default + return false; +} + +/** + * Return the parent item for a given item in the model. + * If no parent is found (or the item is invalid) return an invalid item. + */ +wxDataViewItem BOM_TABLE_MODEL::GetParent( const wxDataViewItem& aItem ) const +{ + auto row = ItemToRow( aItem ); + auto parent = row ? row->GetParent() : nullptr; + + if( parent ) + { + return RowToItem( parent ); + } + + // Return an invalid item + return wxDataViewItem(); +} + +/** + * Returns true if the supplied item has children + */ +bool BOM_TABLE_MODEL::IsContainer( const wxDataViewItem& aItem ) const +{ + auto row = ItemToRow( aItem ); + + if( row ) + { + return row->HasChildren(); + } + + return true; +} + +/** + * Push all children of the supplied item into the list + * If the supplied item is invalid, push all the top-level items + */ +unsigned int BOM_TABLE_MODEL::GetChildren( + const wxDataViewItem& aItem, + wxDataViewItemArray& aChildren ) const +{ + auto row = aItem.IsOk() ? ItemToRow( aItem ) : nullptr; + + // Valid row, return its children + if( row ) + { + return row->GetChildren( aChildren ); + } + else + { + for( auto& group : Groups ) + { + aChildren.Add( RowToItem( &*group ) ); + } + + return aChildren.size(); + } +} + +bool BOM_TABLE_MODEL::GetAttr( const wxDataViewItem& aItem, + unsigned int aFieldId, + wxDataViewItemAttr& aAttr ) const +{ + auto row = aItem.IsOk() ? ItemToRow( aItem ) : nullptr; + + if( row ) + { + return row->GetAttr( aFieldId, aAttr ); + } + else + { + return false; + } +} + +/** + * Custom comparison function for improved column sorting + * Alphanumeric sorting is not sufficient for correct ordering of some fields + * Some columns are sorted numerically, others with more complex rules. + */ +int BOM_TABLE_MODEL::Compare( const wxDataViewItem& aItem1, + const wxDataViewItem& aItem2, + unsigned int aColumnId, + bool aAscending ) const +{ + if( !aItem1.IsOk() || !aItem2.IsOk() ) + return 0; + + int result = 0; + + auto row1 = ItemToRow( aItem1 ); + auto row2 = ItemToRow( aItem2 ); + + if( !row1 || !row2 ) + return 0; + + if( row1->GetParent() != row2->GetParent() ) + return 0; + + wxString strVal1 = row1->GetFieldValue( aColumnId ); + wxString strVal2 = row2->GetFieldValue( aColumnId ); + + long numVal1; + long numVal2; + + switch( aColumnId ) + { + // Reference column sorted by reference val + case BOM_COL_ID_REFERENCE: + result = BOM_TABLE_GROUP::SortReferences( strVal1, strVal2 ); + break; + case BOM_COL_ID_VALUE: + result = BOM_TABLE_GROUP::SortValues( strVal1, strVal2 ); + break; + // These columns are sorted numerically + case BOM_COL_ID_QUANTITY: + if( strVal1.ToLong( &numVal1 ) && strVal2.ToLong( &numVal2 ) ) + { + result = numVal1 - numVal2; + } + else + { + result = strVal1.Cmp( strVal2 ); + } + break; + default: + // Default comparison (no special case) + result = strVal1.Cmp( strVal2 ); + break; + } + + // If initial sorting failed, sort secondly by reference + if( result == 0 && aColumnId != BOM_COL_ID_REFERENCE ) + { + result = BOM_TABLE_GROUP::SortReferences( + row1->GetFieldValue( BOM_COL_ID_REFERENCE ), + row2->GetFieldValue( BOM_COL_ID_REFERENCE ) ); + } + + // If sorting still failed, sort thirdly by value + if( result == 0 && aColumnId != BOM_COL_ID_VALUE ) + { + result = BOM_TABLE_GROUP::SortValues( + row1->GetFieldValue( BOM_COL_ID_VALUE ), + row2->GetFieldValue( BOM_COL_ID_VALUE ) ); + } + + if( !aAscending ) + { + result *= -1; + } + + return result; +} + +/** + * Revert all component data back to the original values. + * The table view is updated accordingly + */ +void BOM_TABLE_MODEL::RevertFieldChanges() +{ + for( auto& group : Groups ) + { + if( !group ) + continue; + + bool changed = false; + + for( auto& component : group->Components ) + { + if( !component ) + continue; + + if( component->HasChanged() ) + { + component->RevertFieldChanges(); + ItemChanged( RowToItem( &*component ) ); + changed = true; + } + } + + // Update the group if any components changed + if( changed ) + { + ItemChanged( RowToItem( &*group ) ); + } + } +} + +/** + * Apply all outstanding field changes. + * This is performed only when the window is closed + */ +void BOM_TABLE_MODEL::ApplyFieldChanges() +{ + for( auto& group : Groups ) + { + if( !group ) + continue; + + bool changed = false; + + for( auto& component : group->Components ) + { + if( !component ) + continue; + + if( component->HasChanged() ) + { + component->ApplyFieldChanges(); + ItemChanged( RowToItem( &*component ) ); + changed = true; + } + } + + if( changed ) + { + ItemChanged( RowToItem( &*group ) ); + } + } +} + +/** + * Tests if any component values in the table have been altered + */ +bool BOM_TABLE_MODEL::HaveFieldsChanged() const +{ + for( auto const& group : Groups ) + { + if( !group ) + continue; + + for( auto const& cmp : group->Components ) + { + if( !cmp ) + continue; + + if( cmp->HasChanged() ) + { + return true; + } + } + } + + return false; +} + +/** + * Returns a list of only those components that have been changed + */ +std::vector BOM_TABLE_MODEL::GetChangedComponents() +{ + std::vector components; + + for( auto& group : Groups ) + { + if( !group ) + continue; + + for( auto& component : group->Components ) + { + if( !component ) + continue; + + if( component->HasChanged() ) + { + for( auto& unit : component->Units ) + { + auto cmp = unit.GetComp(); + + if( cmp ) + { + components.push_back( cmp ); + } + } + } + } + } + + return components; +} + +/** + * Returns a count of the components that have been changed + */ +unsigned int BOM_TABLE_MODEL::CountChangedComponents() +{ + unsigned int count = 0; + + for( auto& group : Groups ) + { + if( !group ) + continue; + + for( auto& component : group->Components ) + { + if( component && component->HasChanged() ) + { + count++; + } + } + } + + return count; +} diff --git a/eeschema/bom_table_model.h b/eeschema/bom_table_model.h new file mode 100644 index 0000000000..8cf3898331 --- /dev/null +++ b/eeschema/bom_table_model.h @@ -0,0 +1,282 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 KiCad Developers, see CHANGELOG.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 + */ + +#ifndef _EESCHEMA_BOM_TABLE_MODEL_H_ +#define _EESCHEMA_BOM_TABLE_MODEL_H_ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "bom_table_column.h" + +// Forward-declare classes +class BOM_TABLE_ROW; // Base-class for table row data model +class BOM_TABLE_GROUP; // Class for displaying a group of components +class BOM_TABLE_COMPONENT; // Class for displaying a single component + +/** + * Virtual base class determining how a row is displayed + * There are three types of rows: + * GROUP - Displays a group of (one or more) components + * COMPONENT - Displays a single component + * UNIT - Child of COMPONENT for multi-unit components + */ +class BOM_TABLE_ROW +{ +public: + BOM_TABLE_ROW(); + virtual ~BOM_TABLE_ROW() {} + + /// Set display properties for a cell + virtual bool GetAttr( unsigned int aFieldId, wxDataViewItemAttr& aAttr ) const; + + /// Get the row value associated with provided field ID + virtual wxString GetFieldValue( unsigned int aFieldId ) const = 0; + + /// Set the field value associated with the provided field ID + virtual bool SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite = false ) = 0; + + /// Return parent item + virtual BOM_TABLE_ROW* GetParent() const { return nullptr; } + + /// Test if row has any child rows + virtual bool HasChildren() const { return false; } + + /// Return any child rows + virtual unsigned int GetChildren( wxDataViewItemArray& aChildren ) const { return 0; } + + /// Determine if a value has changed + virtual bool HasValueChanged( BOM_COLUMN* aField ) const { return false; } + + /// Determine if any values have changed + bool HasChanged() const; + +protected: + + /// Pointer to list of columns + BOM_COLUMN_LIST* m_columnList; +}; + +/** + * BOM_TABLE_GROUP class displays a group of similar components + * If the group contains more than one component, + * they are each displayed as child items of the group + */ +class BOM_TABLE_GROUP : public BOM_TABLE_ROW +{ +public: + // List of components stored in this group + std::vector Components; + + BOM_TABLE_GROUP( BOM_COLUMN_LIST* aColumnList ); + virtual ~BOM_TABLE_GROUP() {} + + // Set display properties for + virtual bool GetAttr( unsigned int aFieldId, wxDataViewItemAttr& aAttr ) const override; + + // Get group row value + virtual wxString GetFieldValue( unsigned int aFieldId ) const override; + + // Set group row value + virtual bool SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite = false ) override; + + // Attempt to add a new component to the group + bool AddComponent( BOM_TABLE_COMPONENT* aComponent ); + + // Test if this group should display children + virtual bool HasChildren() const override { return Components.size() > 1; } + + // Return a list of children items of this group + virtual unsigned int GetChildren( wxDataViewItemArray& aChildren ) const override; + + // Test if any children have changed + virtual bool HasValueChanged( BOM_COLUMN* aField ) const override; + + // Return the number of child items in this group + unsigned int GroupSize( void ) const { return Components.size(); } + + // Return a sorted, concatenated list of references + wxArrayString GetReferences( bool aSort = true ) const; + + // Function for sorting two reference strings + static int SortReferences( const wxString& aFirst, const wxString& aSecond ); + + // Function for sorting two value strings + static int SortValues( const wxString& aFirst, const wxString& aSecond ); + +protected: + // Test if a particular field matches against another component + bool TestField( BOM_COLUMN* aField, BOM_TABLE_COMPONENT* aComponent ) const; +}; + +class BOM_TABLE_COMPONENT : public BOM_TABLE_ROW +{ +public: + // List of units associated with this component + std::vector Units; + + BOM_TABLE_COMPONENT( BOM_TABLE_GROUP* aParent, BOM_COLUMN_LIST* aColumnList ); + + bool AddUnit( SCH_REFERENCE aUnit ); + + virtual wxString GetFieldValue( unsigned int aFieldId ) const override; + + virtual bool SetFieldValue( unsigned int aFieldId, const wxString aValue, bool aOverwrite = false ) override; + + virtual bool HasValueChanged( BOM_COLUMN* aField ) const override; + + // Return the reference of the first unit (all units must be the same + wxString GetReference() const; + + wxString GetPrefix() const; + + void ApplyFieldChanges(); + + void RevertFieldChanges(); + + void SetParent( BOM_TABLE_GROUP* aParent ) { m_parent = aParent; } + + virtual BOM_TABLE_ROW* GetParent() const override { return m_parent; } + +protected: + // Initial data for reverting component values + std::map m_fallbackData; + + // Data as it is updated + std::map m_fieldData; + + BOM_TABLE_GROUP* m_parent; +}; + +/** + * BOM_TABLE_MODEL class + * + * Contains complete BOM information: + * a) List of columns (fields) to display + * b) List of groups of consolidated components + */ +class BOM_TABLE_MODEL : public wxDataViewModel +{ +protected: + BOM_TABLE_MODEL(); + + std::vector> m_components; + + // BOM Preferences + //! Group components based on values + bool m_groupColumns = true; + //! Filter components + bool m_filterColumns = false; + //! Allow blank fields to be merged with otherwise matching groups + bool m_mergeBlankFields = false; + + wxDataViewCtrl* m_widget; + + //! ID of column to sort by + unsigned int m_sortingColumn; + bool m_sortingOrder; + + void AddDefaultColumns(); + void ClearColumns(); + + virtual bool HasContainerColumns( const wxDataViewItem& aItem ) const override { return true; } + + virtual bool IsContainer( const wxDataViewItem& aItem ) const override; + + virtual wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override; + + virtual unsigned int GetChildren( const wxDataViewItem& aItem, wxDataViewItemArray& aChildren ) const override; + + virtual unsigned int GetColumnCount() const override { return ColumnList.ColumnCount( false ); } + + virtual wxString GetColumnType( unsigned int aFieldId ) const override { return wxString( "string" ); } + + virtual void GetValue( wxVariant& aVariant, const wxDataViewItem& aItem, unsigned int aFieldId ) const override; + + virtual bool SetValue( const wxVariant& aVariant, const wxDataViewItem& item, unsigned int aFieldId ) override; + +public: + + virtual ~BOM_TABLE_MODEL(); + + BOM_COLUMN_LIST ColumnList; + + /// List of component groups + std::vector> Groups; + + typedef wxObjectDataPtr MODEL_PTR; + + static MODEL_PTR Create(); + + void AttachTo( wxDataViewCtrl* aView ); + + wxDataViewColumn* AddColumn( BOM_COLUMN* aColumn, int aPosition = -1 ); + bool RemoveColumn( BOM_COLUMN* aColumn ); + + // wxDataViewModel functions + virtual bool GetAttr( const wxDataViewItem& aItem, unsigned int aFieldId, wxDataViewItemAttr& aAttr ) const override; + virtual bool HasDefaultCompare() const override { return false; } + virtual int Compare( const wxDataViewItem& aItem1, + const wxDataViewItem& aItem2, + unsigned int aColumnId, + bool aAscending ) const override; + + void ReloadTable(); + + unsigned int ColumnCount() const { return ColumnList.ColumnCount(); } + unsigned int GroupCount() const { return (unsigned int) Groups.size(); } + unsigned int ComponentCount() const; + + void SetColumnGrouping( const bool aGroup = true ) { m_groupColumns = aGroup; } + bool GetColumnGrouping() const { return m_groupColumns; } + + void SetColumnFiltering( const bool aFilter = true ) { m_filterColumns = aFilter; } + bool GetColumnFiltering() const { return m_filterColumns; } + + void SetBlankMerging( const bool aMerge = true ) { m_mergeBlankFields = aMerge; } + bool GetBlankMerging() const { return m_mergeBlankFields; } + + wxArrayString GetRowData( unsigned int aRow, std::vector aColumns ) const; + + void SetComponents( SCH_REFERENCE_LIST aRefs ); + void AddComponentFields( SCH_COMPONENT* aCmp ); + + void RevertFieldChanges(); + void ApplyFieldChanges(); + + bool HaveFieldsChanged() const; + + std::vector GetChangedComponents(); + unsigned int CountChangedComponents(); +}; + +#endif // _EESCHEMA_BOM_TABLE_MODEL_H_ diff --git a/eeschema/dialogs/dialog_bom_editor.cpp b/eeschema/dialogs/dialog_bom_editor.cpp new file mode 100644 index 0000000000..a835b03146 --- /dev/null +++ b/eeschema/dialogs/dialog_bom_editor.cpp @@ -0,0 +1,439 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 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 + */ + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include "dialog_bom_editor.h" +#include + +/* BOM Table Colours */ + +// Create and show BOM editor +int InvokeDialogCreateBOMEditor( SCH_EDIT_FRAME* aCaller ) +{ + DIALOG_BOM_EDITOR dlg( aCaller ); + return dlg.ShowModal(); +} + +DIALOG_BOM_EDITOR::DIALOG_BOM_EDITOR( SCH_EDIT_FRAME* parent ) : + DIALOG_BOM_EDITOR_BASE( parent ), + m_parent( parent ) +{ + m_bom = BOM_TABLE_MODEL::Create(); + + m_columnListCtrl->DeleteAllItems(); + m_columnListCtrl->ClearColumns(); + + auto nameColumn = m_columnListCtrl->AppendTextColumn( _( "Field" ) ); + + auto showColumn = m_columnListCtrl->AppendToggleColumn( + _( "Show" ), + wxDATAVIEW_CELL_ACTIVATABLE, + 100 ); + + // Resize the columns appropriately + m_columnListCtrl->Update(); + + showColumn->SetWidth( wxCOL_WIDTH_AUTOSIZE ); + showColumn->SetMinWidth( showColumn->GetWidth() ); + showColumn->SetResizeable( false ); + + m_columnListCtrl->Update(); + + nameColumn->SetWidth( wxCOL_WIDTH_AUTOSIZE ); + nameColumn->SetResizeable( true ); + + // Read all components + LoadComponents(); + + LoadColumnNames(); + ReloadColumns(); + + m_bom->ReloadTable(); + + UpdateTitle(); +} + +DIALOG_BOM_EDITOR::~DIALOG_BOM_EDITOR() +{ + //TODO +} + +/** + * When the component table dialog is closed, + * work out if we need to save any changed. + * If so, capture those changes and push them to the undo stack. + */ +void DIALOG_BOM_EDITOR::OnBomEditorClosed( wxCloseEvent& event ) +{ + bool saveChanges = false; + + // If there are changed values, warn the user first + if( m_bom->HaveFieldsChanged() ) + { + int result = DisplayExitDialog( this, _( "Changes exist in component table" ) ); + + switch( result ) + { + // Save and exit + case wxID_YES: + saveChanges = true; + break; + // Cancel (do not exit) + case wxID_CANCEL: + event.Veto(); + return; + // Do not save, exit + default: + break; + } + } + + if( saveChanges ) + { + /** Create a list of picked items for undo + * PICKED_ITEMS_LIST contains multiple ITEM_PICKER instances + * Each ITEM_PICKER contains a component and a command + */ + + auto pickerList = PICKED_ITEMS_LIST(); + + // List of components that have changed + auto changed = m_bom->GetChangedComponents(); + + ITEM_PICKER picker; + + for( auto cmp : changed ) + { + // Push the component into the picker list + picker = ITEM_PICKER( cmp, UR_CHANGED ); + picker.SetFlags( cmp->GetFlags() ); + //picker.SetLink( DuplicateStruct( cmp, true ) ); + + pickerList.PushItem( picker ); + } + + if( pickerList.GetCount() > 0 ) + { + m_parent->SaveCopyInUndoList( pickerList, UR_CHANGED ); + m_bom->ApplyFieldChanges(); + m_parent->Refresh(); + } + } + + Destroy(); +} + +/** + * Update the window title to reflect the contents of the table + */ +void DIALOG_BOM_EDITOR::UpdateTitle() +{ + wxString title = _( "Component table" ) + wxString( " - " ); + + title += wxString::Format( "%u %s", + m_bom->ComponentCount(), + _( "components" ) ); + + if( m_bom->GetColumnGrouping() ) + { + title += wxString::Format( " %s %u %s", + _( "in" ), + m_bom->Groups.size(), + _( "groups" ) ); + } + + unsigned int count = m_bom->CountChangedComponents(); + + if( count > 0 ) + { + title += wxString::Format( " - %u %s", + count, + _( "changed" ) ); + } + + SetTitle( title ); +} + +/** + * Load component data from the entire schematic set + */ +void DIALOG_BOM_EDITOR::LoadComponents() +{ + if( !m_parent ) return; + + // List of component objects + SCH_REFERENCE_LIST refs; + + // Generate a list of schematic sheets + SCH_SHEET_LIST sheets( g_RootSheet ); + sheets.GetComponents( m_parent->Prj().SchLibs(), refs, false ); + + // Pass the references through to the model + m_bom->SetComponents( refs ); +} + +/** + * Display list of columns (fields) + */ +void DIALOG_BOM_EDITOR::LoadColumnNames() +{ + m_columnListCtrl->DeleteAllItems(); + + wxVector< wxVariant > data; + + for( auto* col : m_bom->ColumnList.Columns ) + { + if( nullptr == col ) + continue; + + data.clear(); + + data.push_back( wxVariant( col->Title() ) ); // Column title (string) + data.push_back( wxVariant( col->IsVisible() ) ); // Column visibility (bool) + + m_columnListCtrl->AppendItem( data ); + } +} + +void DIALOG_BOM_EDITOR::ReloadColumns() +{ + m_bom->AttachTo( m_bomView ); + + UpdateTitle(); +} + +void DIALOG_BOM_EDITOR::OnColumnItemToggled( wxDataViewEvent& event ) +{ + wxDataViewItem item = event.GetItem(); + + int row = m_columnListCtrl->ItemToRow( item ); + + int col = event.GetColumn(); + + if( row == wxNOT_FOUND || row < 0 || row >= (int) m_bom->ColumnCount() ) return; + + BOM_COLUMN* bomColumn = m_bom->ColumnList.GetColumnByIndex( row ); + + if( nullptr == bomColumn ) return; + + bool bValue = m_columnListCtrl->GetToggleValue( row, col ); + + switch ( col ) + { + default: + break; + case 1: // Column visibility + bomColumn->SetVisible( bValue ); + + // Insert a new column + if( bValue ) + { + m_bom->AddColumn( bomColumn ); + } + else + { + m_bom->RemoveColumn( bomColumn ); + } + break; + } +} + +/** + * Called when the "Group Components" toggle is pressed + */ +void DIALOG_BOM_EDITOR::OnGroupComponentsToggled( wxCommandEvent& event ) +{ + bool group = m_groupComponentsBox->GetValue(); + + m_bom->SetColumnGrouping( group ); + m_bom->ReloadTable(); + + m_regroupComponentsButton->Enable( group ); + + UpdateTitle(); +} + +/** + * Called when the "Export BOM" button is pressed + * Extract row data from the component table, + * and export it to a BOM file + */ +void DIALOG_BOM_EDITOR::OnExportBOM( wxCommandEvent& event ) +{ + // Allowable BOM file formats + static const wxString wildcard = _( "BOM Files" ) + wxString( " *.csv, *.tsv, *.html)|*.csv;*.tsv;*.htm;*.html" ); + + wxFileDialog bomFileDialog(this, _("Select BOM file"), + Prj().GetProjectPath(), + wxEmptyString, + wildcard, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); + + + if( bomFileDialog.ShowModal() == wxID_CANCEL ) + { + return; + } + + // Ensure the component groups are correct + m_bom->ReloadTable(); + + wxString msg; + + wxFileName filename = bomFileDialog.GetPath(); + + // Ensure correct file format + BOM_FILE_WRITER* writer; + + wxString fn = filename.GetFullPath().Lower(); + + // CSV File + if( fn.EndsWith( ".csv" ) ) + { + writer = new BOM_CSV_WRITER(); + } + // TSV file + else if( fn.EndsWith( ".tsv" ) ) + { + writer = new BOM_CSV_WRITER( '\t' ); + } + // HTML file + else if( fn.EndsWith( ".html" ) || fn.EndsWith( ".htm" ) ) + { + writer = new BOM_HTML_WRITER(); + } + // Unknown file! + else + { + msg.Printf("%s:\n%s", + _( "Unsupported file type" ), + filename.GetExt() ); + + wxMessageBox( msg ); + return; + } + + // Set export preferences + writer->IncludeExtraData( m_includeProjectData->GetValue() ); + writer->ShowRowNumbers( m_showRowNumbers->GetValue() ); + + // Project information + writer->SetKicadVersion( GetBuildVersion() ); + + // Extract sheet info from top-level sheet + if( g_RootSheet ) + { + const TITLE_BLOCK& tb = g_RootSheet->GetScreen()->GetTitleBlock(); + + writer->SetSchematicDate( tb.GetDate() ); + writer->SetSchematicVersion( tb.GetRevision() ); + writer->SetSchematicTitle( tb.GetTitle() ); + } + + std::vector columns; + wxArrayString headings; + + // Extract the visible column data + for( auto column : m_bom->ColumnList.Columns ) + { + if( column && column->IsVisible() ) + { + columns.push_back( column ); + headings.push_back( column->Title() ); + } + } + + writer->SetHeader( headings ); + + // Extract the row data + for( unsigned int row=0; rowGroupCount(); row++ ) + { + writer->AddLine( m_bom->GetRowData( row, columns ) ); + } + + writer->SetGroupCount( m_bom->GroupCount() ); + writer->SetComponentCount( m_bom->ComponentCount() ); + + // Open the BOM file for writing + wxFile bomFile( filename.GetFullPath(), wxFile::write ); + + if( bomFile.IsOpened() ) + { + if( !writer->WriteToFile( bomFile ) ) + { + msg.Printf( "%s:\n%s", + _( "Error writing BOM file" ), + filename.GetFullPath() ); + } + + bomFile.Close(); + } + else + { + msg.Printf( "%s:\n%s", + _( "Error opening BOM file" ), + filename.GetFullPath() ); + + wxMessageBox( msg ); + } +} + +void DIALOG_BOM_EDITOR::OnTableValueChanged( wxDataViewEvent& event ) +{ + m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() ); + + UpdateTitle(); +} + +void DIALOG_BOM_EDITOR::OnRegroupComponents( wxCommandEvent& event ) +{ + m_bom->ReloadTable(); +} + +void DIALOG_BOM_EDITOR::OnRevertFieldChanges( wxCommandEvent& event ) +{ + if( m_bom->HaveFieldsChanged() ) + { + if( IsOK( this, _( "Revert all component table changes?" ) ) ) + { + m_bom->RevertFieldChanges(); + m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() ); + UpdateTitle(); + } + } +} diff --git a/eeschema/dialogs/dialog_bom_editor.h b/eeschema/dialogs/dialog_bom_editor.h new file mode 100644 index 0000000000..9269134c54 --- /dev/null +++ b/eeschema/dialogs/dialog_bom_editor.h @@ -0,0 +1,83 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Oliver Walters + * Copyright (C) 2017 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. + */ + +#ifndef EESCHEMA_DIALOGS_DIALOG_BOM_EDITOR_H_ +#define EESCHEMA_DIALOGS_DIALOG_BOM_EDITOR_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class DIALOG_BOM_EDITOR : public DIALOG_BOM_EDITOR_BASE +{ + +public: + DIALOG_BOM_EDITOR( SCH_EDIT_FRAME* parent ); + virtual ~DIALOG_BOM_EDITOR(); + +private: + //! Parent object (Schematic) + SCH_EDIT_FRAME* m_parent; + + BOM_TABLE_MODEL::MODEL_PTR m_bom; + + void LoadComponents( void ); + + void LoadColumnNames( void ); + void ReloadColumns( void ); + + // Checkbox event callbacks + virtual void OnColumnItemToggled( wxDataViewEvent& event ) override; + virtual void OnGroupComponentsToggled( wxCommandEvent& event ) override; + + virtual void OnExportBOM( wxCommandEvent& event ) override; + + virtual void OnRevertFieldChanges( wxCommandEvent& event ) override; + + virtual void OnRegroupComponents( wxCommandEvent& event ) override; + + // Called after a value in the table has changed + virtual void OnTableValueChanged( wxDataViewEvent& event ) override; + + virtual void OnBomEditorClosed( wxCloseEvent& event ) override; + + void UpdateTitle( void ); +}; + +#endif /* EESCHEMA_DIALOGS_DIALOG_BOM_EDITOR_H_ */ diff --git a/eeschema/dialogs/dialog_bom_editor_base.cpp b/eeschema/dialogs/dialog_bom_editor_base.cpp new file mode 100644 index 0000000000..d5e07a425e --- /dev/null +++ b/eeschema/dialogs/dialog_bom_editor_base.cpp @@ -0,0 +1,150 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 1 2017) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_bom_editor_base.h" + +/////////////////////////////////////////////////////////////////////////// + +BEGIN_EVENT_TABLE( DIALOG_BOM_EDITOR_BASE, DIALOG_SHIM ) + EVT_CLOSE( DIALOG_BOM_EDITOR_BASE::_wxFB_OnBomEditorClosed ) + EVT_CHECKBOX( OPT_GROUP_COMPONENTS, DIALOG_BOM_EDITOR_BASE::_wxFB_OnGroupComponentsToggled ) + EVT_BUTTON( ID_BUTTON_REGROUP, DIALOG_BOM_EDITOR_BASE::_wxFB_OnRegroupComponents ) + EVT_BUTTON( ID_BUTTON_REVERT_CHANGES, DIALOG_BOM_EDITOR_BASE::_wxFB_OnRevertFieldChanges ) + EVT_DATAVIEW_ITEM_VALUE_CHANGED( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnColumnItemToggled ) + EVT_BUTTON( wxID_BOM_BUTTON_EXPORT, DIALOG_BOM_EDITOR_BASE::_wxFB_OnExportBOM ) + EVT_DATAVIEW_COLUMN_REORDERED( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnBomColumReordered ) + EVT_DATAVIEW_COLUMN_SORTED( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnBomColumnSorted ) + EVT_DATAVIEW_ITEM_EDITING_DONE( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnTableValueChanged ) + EVT_DATAVIEW_SELECTION_CHANGED( wxID_ANY, DIALOG_BOM_EDITOR_BASE::_wxFB_OnSelectionChanged ) +END_EVENT_TABLE() + +DIALOG_BOM_EDITOR_BASE::DIALOG_BOM_EDITOR_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* bHorizontalSizer; + bHorizontalSizer = new wxBoxSizer( wxVERTICAL ); + + m_panel = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + m_splitter1 = new wxSplitterWindow( m_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_3D ); + m_splitter1->Connect( wxEVT_IDLE, wxIdleEventHandler( DIALOG_BOM_EDITOR_BASE::m_splitter1OnIdle ), NULL, this ); + + m_leftPanel = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer6; + bSizer6 = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer1; + sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( m_leftPanel, wxID_ANY, _("Options") ), wxVERTICAL ); + + m_groupComponentsBox = new wxCheckBox( sbSizer1->GetStaticBox(), OPT_GROUP_COMPONENTS, _("Group components"), wxDefaultPosition, wxDefaultSize, 0 ); + m_groupComponentsBox->SetValue(true); + m_groupComponentsBox->SetToolTip( _("Group components together based on common properties") ); + + sbSizer1->Add( m_groupComponentsBox, 0, wxALL|wxEXPAND, 5 ); + + m_regroupComponentsButton = new wxButton( sbSizer1->GetStaticBox(), ID_BUTTON_REGROUP, _("Regroup components"), wxDefaultPosition, wxDefaultSize, 0 ); + sbSizer1->Add( m_regroupComponentsButton, 0, wxALL|wxEXPAND, 5 ); + + m_reloadTableButton = new wxButton( sbSizer1->GetStaticBox(), ID_BUTTON_REVERT_CHANGES, _("Revert all changes"), wxDefaultPosition, wxDefaultSize, 0 ); + m_reloadTableButton->Enable( false ); + m_reloadTableButton->SetToolTip( _("Reload table (reverts component field changes)") ); + + sbSizer1->Add( m_reloadTableButton, 0, wxALL|wxEXPAND, 5 ); + + + bSizer6->Add( sbSizer1, 0, wxEXPAND, 5 ); + + wxBoxSizer* bSizer9; + bSizer9 = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* m_fieldListSizer; + m_fieldListSizer = new wxStaticBoxSizer( new wxStaticBox( m_leftPanel, wxID_ANY, _("Fields") ), wxVERTICAL ); + + m_columnListCtrl = new wxDataViewListCtrl( m_fieldListSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ); + m_fieldListSizer->Add( m_columnListCtrl, 1, wxALL|wxEXPAND, 5 ); + + + bSizer9->Add( m_fieldListSizer, 1, wxEXPAND, 5 ); + + + bSizer6->Add( bSizer9, 5, wxEXPAND, 5 ); + + + bSizer6->Add( 0, 0, 0, wxEXPAND, 5 ); + + wxStaticBoxSizer* sbSizer2; + sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( m_leftPanel, wxID_ANY, _("Export options") ), wxVERTICAL ); + + m_includeProjectData = new wxCheckBox( sbSizer2->GetStaticBox(), wxID_BOM_OPT_INC_PRJ_DATA, _("Include project data"), wxDefaultPosition, wxDefaultSize, 0 ); + m_includeProjectData->SetValue(true); + m_includeProjectData->SetToolTip( _("Include project information in BOM file") ); + + sbSizer2->Add( m_includeProjectData, 0, wxALL|wxEXPAND, 5 ); + + m_showRowNumbers = new wxCheckBox( sbSizer2->GetStaticBox(), wxID_BOM_OPT_SHOW_ROW_NUMS, _("Show row numbers"), wxDefaultPosition, wxDefaultSize, 0 ); + m_showRowNumbers->SetValue(true); + m_showRowNumbers->SetToolTip( _("Show BOM row numbers ") ); + + sbSizer2->Add( m_showRowNumbers, 0, wxALL|wxEXPAND, 5 ); + + + sbSizer2->Add( 0, 0, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizer13; + bSizer13 = new wxBoxSizer( wxHORIZONTAL ); + + + bSizer13->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_exportButton = new wxButton( sbSizer2->GetStaticBox(), wxID_BOM_BUTTON_EXPORT, _("Export BOM"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer13->Add( m_exportButton, 0, wxALL, 5 ); + + + sbSizer2->Add( bSizer13, 0, wxEXPAND, 5 ); + + + bSizer6->Add( sbSizer2, 0, wxEXPAND, 5 ); + + + m_leftPanel->SetSizer( bSizer6 ); + m_leftPanel->Layout(); + bSizer6->Fit( m_leftPanel ); + m_panel4 = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer5; + bSizer5 = new wxBoxSizer( wxVERTICAL ); + + m_bomView = new wxDataViewCtrl( m_panel4, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE|wxDV_ROW_LINES|wxDV_VERT_RULES ); + m_bomView->SetMinSize( wxSize( 250,250 ) ); + + bSizer5->Add( m_bomView, 1, wxALL|wxEXPAND, 5 ); + + + m_panel4->SetSizer( bSizer5 ); + m_panel4->Layout(); + bSizer5->Fit( m_panel4 ); + m_splitter1->SplitVertically( m_leftPanel, m_panel4, 231 ); + bSizer7->Add( m_splitter1, 1, wxEXPAND, 5 ); + + + m_panel->SetSizer( bSizer7 ); + m_panel->Layout(); + bSizer7->Fit( m_panel ); + bHorizontalSizer->Add( m_panel, 1, wxEXPAND | wxALL, 5 ); + + + this->SetSizer( bHorizontalSizer ); + this->Layout(); + + this->Centre( wxBOTH ); +} + +DIALOG_BOM_EDITOR_BASE::~DIALOG_BOM_EDITOR_BASE() +{ +} diff --git a/eeschema/dialogs/dialog_bom_editor_base.fbp b/eeschema/dialogs/dialog_bom_editor_base.fbp new file mode 100644 index 0000000000..9d1cf28e1f --- /dev/null +++ b/eeschema/dialogs/dialog_bom_editor_base.fbp @@ -0,0 +1,1202 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + table + dialog_bom_editor_base + 1000 + none + 1 + dialog_bom_editor_base + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_BOM_EDITOR_BASE + + 1047,649 + wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + BOM editor + + + + + + + + + + + + + + OnBomEditorClosed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bHorizontalSizer + wxVERTICAL + none + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer7 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_splitter1 + 1 + + + protected + 1 + + Resizable + 0.0 + 231 + -1 + 1 + + wxSPLIT_VERTICAL + wxSP_3D + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_leftPanel + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer6 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + wxID_ANY + Options + + sbSizer1 + wxVERTICAL + 1 + none + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + OPT_GROUP_COMPONENTS + Group components + + 0 + + + 0 + + 1 + m_groupComponentsBox + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + Group components together based on common properties + + wxFILTER_NONE + wxDefaultValidator + + + + + + OnGroupComponentsToggled + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + ID_BUTTON_REGROUP + Regroup components + + 0 + + + 0 + + 1 + m_regroupComponentsButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnRegroupComponents + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + ID_BUTTON_REVERT_CHANGES + Revert all changes + + 0 + + + 0 + + 1 + m_reloadTableButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + Reload table (reverts component field changes) + + wxFILTER_NONE + wxDefaultValidator + + + + + OnRevertFieldChanges + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 5 + + + bSizer9 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + wxID_ANY + Fields + + m_fieldListSizer + wxVERTICAL + 1 + none + + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + 1 + + + 0 + wxID_ANY + + + m_columnListCtrl + protected + + + + + + + + + + + + + + + + + + + + + + + + + + OnColumnItemToggled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + protected + 0 + + + + 5 + wxEXPAND + 0 + + wxID_ANY + Export options + + sbSizer2 + wxVERTICAL + 1 + none + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_BOM_OPT_INC_PRJ_DATA + Include project data + + 0 + + + 0 + + 1 + m_includeProjectData + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + Include project information in BOM file + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_BOM_OPT_SHOW_ROW_NUMS + Show row numbers + + 0 + + + 0 + + 1 + m_showRowNumbers + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + Show BOM row numbers + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxEXPAND + 0 + + + bSizer13 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_BOM_BUTTON_EXPORT + Export BOM + + 0 + + + 0 + + 1 + m_exportButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnExportBOM + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel4 + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer5 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + + + 1 + 1 + + + 0 + wxID_ANY + + 250,250 + m_bomView + protected + + + wxDV_MULTIPLE|wxDV_ROW_LINES|wxDV_VERT_RULES + + + + + + + + + OnBomColumReordered + OnBomColumnSorted + + + + + + + + OnTableValueChanged + + + + + + OnSelectionChanged + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eeschema/dialogs/dialog_bom_editor_base.h b/eeschema/dialogs/dialog_bom_editor_base.h new file mode 100644 index 0000000000..277bcff18a --- /dev/null +++ b/eeschema/dialogs/dialog_bom_editor_base.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Apr 1 2017) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_BOM_EDITOR_BASE_H__ +#define __DIALOG_BOM_EDITOR_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +#define OPT_GROUP_COMPONENTS 1000 +#define ID_BUTTON_REGROUP 1001 +#define ID_BUTTON_REVERT_CHANGES 1002 +#define wxID_BOM_OPT_INC_PRJ_DATA 1003 +#define wxID_BOM_OPT_SHOW_ROW_NUMS 1004 +#define wxID_BOM_BUTTON_EXPORT 1005 + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_BOM_EDITOR_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_BOM_EDITOR_BASE : public DIALOG_SHIM +{ + DECLARE_EVENT_TABLE() + private: + + // Private event handlers + void _wxFB_OnBomEditorClosed( wxCloseEvent& event ){ OnBomEditorClosed( event ); } + void _wxFB_OnGroupComponentsToggled( wxCommandEvent& event ){ OnGroupComponentsToggled( event ); } + void _wxFB_OnRegroupComponents( wxCommandEvent& event ){ OnRegroupComponents( event ); } + void _wxFB_OnRevertFieldChanges( wxCommandEvent& event ){ OnRevertFieldChanges( event ); } + void _wxFB_OnColumnItemToggled( wxDataViewEvent& event ){ OnColumnItemToggled( event ); } + void _wxFB_OnExportBOM( wxCommandEvent& event ){ OnExportBOM( event ); } + void _wxFB_OnBomColumReordered( wxDataViewEvent& event ){ OnBomColumReordered( event ); } + void _wxFB_OnBomColumnSorted( wxDataViewEvent& event ){ OnBomColumnSorted( event ); } + void _wxFB_OnTableValueChanged( wxDataViewEvent& event ){ OnTableValueChanged( event ); } + void _wxFB_OnSelectionChanged( wxDataViewEvent& event ){ OnSelectionChanged( event ); } + + + protected: + wxPanel* m_panel; + wxSplitterWindow* m_splitter1; + wxPanel* m_leftPanel; + wxCheckBox* m_groupComponentsBox; + wxButton* m_regroupComponentsButton; + wxButton* m_reloadTableButton; + wxDataViewListCtrl* m_columnListCtrl; + wxCheckBox* m_includeProjectData; + wxCheckBox* m_showRowNumbers; + wxButton* m_exportButton; + wxPanel* m_panel4; + wxDataViewCtrl* m_bomView; + + // Virtual event handlers, overide them in your derived class + virtual void OnBomEditorClosed( wxCloseEvent& event ) { event.Skip(); } + virtual void OnGroupComponentsToggled( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRegroupComponents( wxCommandEvent& event ) { event.Skip(); } + virtual void OnRevertFieldChanges( wxCommandEvent& event ) { event.Skip(); } + virtual void OnColumnItemToggled( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnExportBOM( wxCommandEvent& event ) { event.Skip(); } + virtual void OnBomColumReordered( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnBomColumnSorted( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnTableValueChanged( wxDataViewEvent& event ) { event.Skip(); } + virtual void OnSelectionChanged( wxDataViewEvent& event ) { event.Skip(); } + + + public: + + DIALOG_BOM_EDITOR_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("BOM editor"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 1047,649 ), long style = wxDEFAULT_DIALOG_STYLE|wxMAXIMIZE_BOX|wxRESIZE_BORDER ); + ~DIALOG_BOM_EDITOR_BASE(); + + void m_splitter1OnIdle( wxIdleEvent& ) + { + m_splitter1->SetSashPosition( 231 ); + m_splitter1->Disconnect( wxEVT_IDLE, wxIdleEventHandler( DIALOG_BOM_EDITOR_BASE::m_splitter1OnIdle ), NULL, this ); + } + +}; + +#endif //__DIALOG_BOM_EDITOR_BASE_H__ diff --git a/eeschema/invoke_sch_dialog.h b/eeschema/invoke_sch_dialog.h index fe36a41f79..9e8c55b26d 100644 --- a/eeschema/invoke_sch_dialog.h +++ b/eeschema/invoke_sch_dialog.h @@ -80,6 +80,9 @@ int InvokeDialogPrintUsingPrinter( SCH_EDIT_FRAME* aCaller ); /// DIALOG_BOM::ShowModal() returns. int InvokeDialogCreateBOM( SCH_EDIT_FRAME* aCaller ); +/// Create and show DIALOG_BOM_EDITOR +int InvokeDialogCreateBOMEditor( SCH_EDIT_FRAME* aCaller ); + /** * Function InvokeDialogNetList * creates and shows NETLIST_DIALOG and returns whatever diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp index a6da75ea6e..0d2a2d9ca8 100644 --- a/eeschema/menubar.cpp +++ b/eeschema/menubar.cpp @@ -502,12 +502,19 @@ void prepareToolsMenu( wxMenu* aParentMenu ) _( "Generate the component netlist file" ), KiBitmap( netlist_xpm ) ); + AddMenuItem( aParentMenu, + ID_OPEN_CMP_TABLE, + _( "Component Table &View" ), + KiBitmap( spreadsheet_xpm ) ); + AddMenuItem( aParentMenu, ID_GET_TOOLS, _( "Generate Bill of &Materials..." ), HELP_GENERATE_BOM, KiBitmap( bom_xpm ) ); + + aParentMenu->AppendSeparator(); // Run CvPcb diff --git a/eeschema/sch_component.cpp b/eeschema/sch_component.cpp index fa2ce5e565..8c9f64b064 100644 --- a/eeschema/sch_component.cpp +++ b/eeschema/sch_component.cpp @@ -293,6 +293,43 @@ void SCH_COMPONENT::SetLibId( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aSymLibTab } } +/** + * Function GetAliasDescription + * Return the description text for the given part alias + */ +wxString SCH_COMPONENT::GetAliasDescription() const +{ + if( PART_SPTR part = m_part.lock() ) + { + LIB_ALIAS* alias = part->GetAlias( GetLibId().GetLibItemName() ); + + if( !alias ) + return wxEmptyString; + + return alias->GetDescription(); + } + + return wxEmptyString; +} + +/** + * Function GetAliasDocumentation + * Return the documentation text for the given part alias + */ +wxString SCH_COMPONENT::GetAliasDocumentation() const +{ + if( PART_SPTR part = m_part.lock() ) + { + LIB_ALIAS* alias = part->GetAlias( GetLibId().GetLibItemName() ); + + if( !alias ) + return wxEmptyString; + + return alias->GetDocFileName(); + } + + return wxEmptyString; +} bool SCH_COMPONENT::Resolve( PART_LIBS* aLibs ) { @@ -732,6 +769,37 @@ SCH_FIELD* SCH_COMPONENT::GetField( int aFieldNdx ) const return (SCH_FIELD*) field; } +wxString SCH_COMPONENT::GetFieldText( wxString aFieldName ) const +{ + + // Field name for comparison + wxString cmpFieldName; + + // Default field names + for ( unsigned int i=0; i& aVector, bool aVisibleOnly ) { diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index 9469e7ef9f..958b845f35 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -169,6 +169,12 @@ public: PART_REF& GetPartRef() { return m_part; } + /** + * Return information about the aliased parts + */ + wxString GetAliasDescription() const; + wxString GetAliasDocumentation() const; + /** * Function Resolve * [re-]assigns the current LIB_PART from aLibs which this component @@ -292,6 +298,12 @@ public: */ SCH_FIELD* GetField( int aFieldNdx ) const; + /** + * Returns text associated with a given field (if such a field exists) + * @aFieldName is the name of the field + */ + wxString GetFieldText( wxString aFieldName ) const; + /** * Function GetFields * populates a std::vector with SCH_FIELDs. diff --git a/eeschema/schframe.cpp b/eeschema/schframe.cpp index 42687c20f5..3e7c2ddb69 100644 --- a/eeschema/schframe.cpp +++ b/eeschema/schframe.cpp @@ -267,6 +267,7 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, EDA_DRAW_FRAME ) EVT_TOOL( ID_GET_NETLIST, SCH_EDIT_FRAME::OnCreateNetlist ) EVT_TOOL( ID_UPDATE_PCB_FROM_SCH, SCH_EDIT_FRAME::OnUpdatePCB ) EVT_TOOL( ID_GET_TOOLS, SCH_EDIT_FRAME::OnCreateBillOfMaterials ) + EVT_TOOL( ID_OPEN_CMP_TABLE, SCH_EDIT_FRAME::OnLaunchBomManager ) EVT_TOOL( ID_FIND_ITEMS, SCH_EDIT_FRAME::OnFindItems ) EVT_TOOL( wxID_REPLACE, SCH_EDIT_FRAME::OnFindItems ) EVT_TOOL( ID_BACKANNO_ITEMS, SCH_EDIT_FRAME::OnLoadCmpToFootprintLinkFile ) @@ -764,6 +765,8 @@ void SCH_EDIT_FRAME::OnModify() GetScreen()->SetSave(); m_foundItems.SetForceSearch(); + + m_canvas->Refresh(); } @@ -912,6 +915,14 @@ void SCH_EDIT_FRAME::OnCreateBillOfMaterials( wxCommandEvent& ) InvokeDialogCreateBOM( this ); } +void SCH_EDIT_FRAME::OnLaunchBomManager( wxCommandEvent& event ) +{ + // First ensure that entire schematic is annotated + if( !prepareForNetlist() ) + return; + + InvokeDialogCreateBOMEditor( this ); +} void SCH_EDIT_FRAME::OnFindItems( wxCommandEvent& aEvent ) { diff --git a/eeschema/schframe.h b/eeschema/schframe.h index 4fe4a6546b..b2d8783c56 100644 --- a/eeschema/schframe.h +++ b/eeschema/schframe.h @@ -833,6 +833,7 @@ private: void OnUpdatePCB( wxCommandEvent& event ); void OnSimulate( wxCommandEvent& event ); void OnCreateBillOfMaterials( wxCommandEvent& event ); + void OnLaunchBomManager( wxCommandEvent& event ); void OnFindItems( wxCommandEvent& event ); void OnFindDialogClose( wxFindDialogEvent& event ); void OnFindDrcMarker( wxFindDialogEvent& event ); diff --git a/eeschema/tool_sch.cpp b/eeschema/tool_sch.cpp index 6060e36386..1b087472e6 100644 --- a/eeschema/tool_sch.cpp +++ b/eeschema/tool_sch.cpp @@ -160,10 +160,13 @@ void SCH_EDIT_FRAME::ReCreateHToolbar() m_mainToolBar->AddTool( ID_GET_NETLIST, wxEmptyString, KiBitmap( netlist_xpm ), _( "Generate netlist" ) ); + m_mainToolBar->AddTool( ID_OPEN_CMP_TABLE, wxEmptyString, KiBitmap( spreadsheet_xpm ), + _( "Component table view" ) ); + + m_mainToolBar->AddTool( ID_GET_TOOLS, wxEmptyString, KiBitmap( bom_xpm ), HELP_GENERATE_BOM ); - m_mainToolBar->AddSeparator(); m_mainToolBar->AddTool( ID_RUN_PCB, wxEmptyString, KiBitmap( pcbnew_xpm ), diff --git a/include/bitmaps.h b/include/bitmaps.h index e58ade38d4..cd0d990717 100644 --- a/include/bitmaps.h +++ b/include/bitmaps.h @@ -493,6 +493,7 @@ EXTERN_BITMAP( showtrack_xpm ) EXTERN_BITMAP( show_zone_xpm ) EXTERN_BITMAP( show_zone_disable_xpm ) EXTERN_BITMAP( show_zone_outline_only_xpm ) +EXTERN_BITMAP( spreadsheet_xpm ) EXTERN_BITMAP( svg_file_xpm ) EXTERN_BITMAP( swap_layer_xpm ) EXTERN_BITMAP( text_sketch_xpm ) diff --git a/include/id.h b/include/id.h index c01676de6f..780958de99 100644 --- a/include/id.h +++ b/include/id.h @@ -249,6 +249,7 @@ enum main_id ID_PAN_RIGHT, ID_GET_NETLIST, + ID_OPEN_CMP_TABLE, ID_GET_TOOLS, ID_FIND_ITEMS, diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index 63b43fcd73..5bd7fa1ca3 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -83,6 +83,7 @@ extern const wxString BoardFileWildcard; extern const wxString NetlistFileWildcard; extern const wxString GerberFileWildcard; extern const wxString HtmlFileWildcard; +extern const wxString CsvFileWildcard; extern const wxString LegacyPcbFileWildcard; extern const wxString PcbFileWildcard; extern const wxString EaglePcbFileWildcard;