2017-04-02 12:09:01 +00:00
|
|
|
/*
|
|
|
|
* 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 <wx/colour.h>
|
|
|
|
#include <wx/msgdlg.h>
|
|
|
|
#include <wx/progdlg.h>
|
|
|
|
#include <wx/file.h>
|
|
|
|
#include <wx/filename.h>
|
|
|
|
|
|
|
|
#include <confirm.h>
|
|
|
|
|
|
|
|
#include <build_version.h>
|
|
|
|
#include <general.h>
|
|
|
|
#include <class_library.h>
|
|
|
|
|
|
|
|
#include <bom_exporter.h>
|
|
|
|
|
|
|
|
#include "dialog_bom_editor.h"
|
|
|
|
#include <bom_table_model.h>
|
|
|
|
|
|
|
|
/* 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();
|
|
|
|
|
2017-04-18 09:25:02 +00:00
|
|
|
Update();
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DIALOG_BOM_EDITOR::~DIALOG_BOM_EDITOR()
|
|
|
|
{
|
|
|
|
//TODO
|
|
|
|
}
|
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
/* Struct for keeping track of schematic sheet changes
|
|
|
|
* Stores:
|
|
|
|
* SHEET_PATH - Schematic to apply changes to
|
|
|
|
* PICKED_ITEMS_LIST - List of changes to apply
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
SCH_SHEET_PATH path;
|
|
|
|
PICKED_ITEMS_LIST items;
|
|
|
|
} SheetUndoList;
|
|
|
|
|
2017-04-02 12:09:01 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2017-04-18 09:25:02 +00:00
|
|
|
bool DIALOG_BOM_EDITOR::TransferDataFromWindow()
|
2017-04-02 12:09:01 +00:00
|
|
|
{
|
|
|
|
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:
|
2017-04-18 09:25:02 +00:00
|
|
|
return false;
|
2017-04-02 12:09:01 +00:00
|
|
|
// Do not save, exit
|
|
|
|
default:
|
2017-04-18 09:25:02 +00:00
|
|
|
return true;
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( saveChanges )
|
|
|
|
{
|
2017-04-18 14:10:17 +00:00
|
|
|
/**
|
|
|
|
* As we may be saving changes across multiple sheets,
|
|
|
|
* we need to first determine which changes need to be made to which sheet.
|
|
|
|
* To this end, we perform the following:
|
|
|
|
* 1. Save the "path" of the currently displayed sheet
|
|
|
|
* 2. Create a MAP of <SheetPath:ChangeList> changes that need to be made
|
|
|
|
* 3. Push UNDO actions to appropriate sheets
|
|
|
|
* 4. Perform all the update actions
|
|
|
|
* 5. Reset the sheet view to the current sheet
|
2017-04-02 12:09:01 +00:00
|
|
|
*/
|
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
auto currentSheet = m_parent->GetCurrentSheet();
|
|
|
|
|
|
|
|
//! Create a map of changes required for each sheet
|
|
|
|
std::map<wxString, SheetUndoList> undoSheetMap;
|
2017-04-02 12:09:01 +00:00
|
|
|
|
|
|
|
// List of components that have changed
|
|
|
|
auto changed = m_bom->GetChangedComponents();
|
|
|
|
|
|
|
|
ITEM_PICKER picker;
|
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
// Iterate through each of the components that were changed
|
|
|
|
for( auto ref : changed )
|
2017-04-02 12:09:01 +00:00
|
|
|
{
|
2017-04-18 14:10:17 +00:00
|
|
|
// Extract the SCH_COMPONENT* object
|
|
|
|
auto cmp = ref.GetComp();
|
|
|
|
|
|
|
|
wxString path = ref.GetSheetPath().Path();
|
|
|
|
|
2017-04-02 12:09:01 +00:00
|
|
|
// Push the component into the picker list
|
|
|
|
picker = ITEM_PICKER( cmp, UR_CHANGED );
|
|
|
|
picker.SetFlags( cmp->GetFlags() );
|
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is not currently an undo list for the given sheet,
|
|
|
|
* create an empty one
|
|
|
|
*/
|
|
|
|
|
|
|
|
if( undoSheetMap.count( path ) == 0 )
|
|
|
|
{
|
|
|
|
SheetUndoList newList;
|
|
|
|
|
|
|
|
newList.path = ref.GetSheetPath();
|
|
|
|
|
|
|
|
undoSheetMap[path] = newList;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& pickerList = undoSheetMap[path];
|
|
|
|
|
|
|
|
pickerList.items.PushItem( picker );
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
// Iterate through each sheet that needs updating
|
|
|
|
for( auto it = undoSheetMap.begin(); it != undoSheetMap.end(); ++it )
|
2017-04-02 12:09:01 +00:00
|
|
|
{
|
2017-04-18 14:10:17 +00:00
|
|
|
auto undo = it->second;
|
|
|
|
|
|
|
|
m_parent->SetCurrentSheet( undo.path );
|
|
|
|
m_parent->SaveCopyInUndoList( undo.items, UR_CHANGED );
|
|
|
|
m_parent->OnModify();
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
2017-04-16 06:43:59 +00:00
|
|
|
|
2017-04-18 14:10:17 +00:00
|
|
|
// Apply all the field changes
|
|
|
|
m_bom->ApplyFieldChanges();
|
|
|
|
|
|
|
|
// Redraw the current sheet and mark as dirty
|
|
|
|
m_parent->Refresh();
|
2017-04-16 06:43:59 +00:00
|
|
|
m_parent->OnModify();
|
2017-04-18 14:10:17 +00:00
|
|
|
|
|
|
|
// Reset the view to where we left the user
|
|
|
|
m_parent->SetCurrentSheet(currentSheet);
|
|
|
|
|
|
|
|
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 09:25:02 +00:00
|
|
|
return true;
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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" ),
|
2017-04-04 12:27:13 +00:00
|
|
|
(unsigned int) m_bom->Groups.size(),
|
2017-04-02 12:09:01 +00:00
|
|
|
_( "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();
|
|
|
|
|
2017-04-18 09:25:02 +00:00
|
|
|
Update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DIALOG_BOM_EDITOR::OnUpdateUI( wxUpdateUIEvent& event )
|
|
|
|
{
|
|
|
|
m_regroupComponentsButton->Enable( m_bom->GetColumnGrouping() );
|
|
|
|
|
|
|
|
m_reloadTableButton->Enable( m_bom->HaveFieldsChanged() );
|
2017-04-02 12:09:01 +00:00
|
|
|
|
|
|
|
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<BOM_COLUMN*> 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; row<m_bom->GroupCount(); 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 )
|
|
|
|
{
|
2017-04-18 09:25:02 +00:00
|
|
|
Update();
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DIALOG_BOM_EDITOR::OnRegroupComponents( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
m_bom->ReloadTable();
|
2017-04-18 09:25:02 +00:00
|
|
|
Update();
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DIALOG_BOM_EDITOR::OnRevertFieldChanges( wxCommandEvent& event )
|
|
|
|
{
|
|
|
|
if( m_bom->HaveFieldsChanged() )
|
|
|
|
{
|
|
|
|
if( IsOK( this, _( "Revert all component table changes?" ) ) )
|
|
|
|
{
|
|
|
|
m_bom->RevertFieldChanges();
|
2017-04-18 09:25:02 +00:00
|
|
|
Update();
|
2017-04-02 12:09:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|