From 61b4f8a9ebc4aaf5f4f5a59ea3cd791f06b6f02c Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Thu, 25 Apr 2013 12:29:35 -0400 Subject: [PATCH] Pcbnew NETLIST_READER improvements. * Create separate NETLIST object to hold contents of netlist files. * Read entire netlist and footprint link files before making applying changes to board. * Add BOARD::ReplaceNetlist() function to eliminate the calls between the NETLIST_READER, PCB_EDIT_FRAME, and BOARD objects. * Change placement of new components below the center of the current board or in the center of the page if the BOARD is empty. * Add dry run option to netlist dialog to print changes to message control without making changes. * Add button to netlist dialog to allow saving contents of message control to a file. * Eliminate the need to compile netlist_reader_*.cpp in both CvPcb and Pcbnew. * Add netlist_reader_*.cpp to the pcbcommon library. * Remove redundant load component link file code from CvPcb. * Modify CvPcb new to work with the new NETLIST_READER object. * Add compare() function and < and == operators to FPID object. * Add REPORTER class to hide an underlying string writing implementation for use in low level objects. Thank you Dick for the idea. * Lots of minor coding policy, Doxygen comment, and missing license fixes. --- 3d-viewer/modelparsers.h | 3 +- TODO.txt | 9 +- common/CMakeLists.txt | 4 + common/fpid.cpp | 20 + common/reporter.cpp | 46 + common/wildcards_and_files_ext.cpp | 3 +- cvpcb/CMakeLists.txt | 3 - cvpcb/autosel.cpp | 76 +- cvpcb/class_footprints_listbox.cpp | 85 +- cvpcb/cvframe.cpp | 39 +- cvpcb/cvpcb.h | 4 - cvpcb/cvpcb_mainframe.h | 65 +- cvpcb/cvstruct.h | 63 +- cvpcb/listboxes.cpp | 41 +- cvpcb/loadcmp.cpp | 10 +- cvpcb/read_write_cmpfile.cpp | 108 +-- cvpcb/readschematicnetlist.cpp | 92 +- cvpcb/readwrite_dlgs.cpp | 95 +-- eeschema/class_library.cpp | 2 +- include/appl_wxstruct.h | 4 +- include/dialog_helpers.h | 25 + include/fpid.h | 23 + include/reporter.h | 89 ++ include/wildcards_and_files_ext.h | 1 + include/wxBasePcbFrame.h | 13 + include/wxPcbStruct.h | 77 +- pcbnew/CMakeLists.txt | 3 - pcbnew/class_board.cpp | 267 ++++++ pcbnew/class_board.h | 77 +- pcbnew/class_module.cpp | 59 +- pcbnew/class_module.h | 31 +- pcbnew/class_netinfo.h | 31 + pcbnew/class_netinfo_item.cpp | 7 - pcbnew/class_pad.cpp | 50 +- pcbnew/class_pad.h | 12 + pcbnew/dialogs/dialog_edit_module_text.cpp | 4 +- pcbnew/dialogs/dialog_netlist.cpp | 235 ++++- pcbnew/dialogs/dialog_netlist.h | 51 +- pcbnew/dialogs/dialog_netlist_fbp.cpp | 36 +- pcbnew/dialogs/dialog_netlist_fbp.fbp | 207 ++++- pcbnew/dialogs/dialog_netlist_fbp.h | 6 + pcbnew/dialogs/dialog_orient_footprints.cpp | 43 +- pcbnew/dialogs/dialog_pad_properties.cpp | 4 +- pcbnew/loadcmp.cpp | 53 +- pcbnew/muonde.cpp | 56 +- pcbnew/netlist.cpp | 311 +++---- pcbnew/netlist_reader.h | 900 +++++++++++++------- pcbnew/netlist_reader_common.cpp | 779 +++++++---------- pcbnew/netlist_reader_firstformat.cpp | 370 +++----- pcbnew/netlist_reader_kicad.cpp | 448 ++++------ 50 files changed, 2888 insertions(+), 2152 deletions(-) create mode 100644 common/reporter.cpp create mode 100644 include/reporter.h diff --git a/3d-viewer/modelparsers.h b/3d-viewer/modelparsers.h index 7153c21ba2..147a0fda16 100644 --- a/3d-viewer/modelparsers.h +++ b/3d-viewer/modelparsers.h @@ -112,7 +112,8 @@ public: * Function GetNodeProperties * Collects all node properties to map. * - * @param aProps contains map of found properties + * @param aNode is an XML node. + * @param aProps contains map of found properties. */ static void GetNodeProperties( wxXmlNode* aNode, PROPERTY_MAP& aProps ); diff --git a/TODO.txt b/TODO.txt index 2dce8bed09..7a7a30ec34 100644 --- a/TODO.txt +++ b/TODO.txt @@ -10,7 +10,7 @@ WXMAC Platform Common ------ * Grep for @TODO or TODO for sourcecode tasks -* Use doxygen compatible comments on member functions (.h files) +* Use Doxygen compatible comments on member functions (.h files) * Add tooltip text to all non-obvious controls in every dialog window. Use wxFormBuilder. * Component and module search displays in which library the @@ -113,9 +113,9 @@ const wxString FP_LIB_TABLE::ExpandSubtitutions( const wxString aString ) -EESchema +Eeschema -------- -* Drag and drop between two EESchema windows. +* Drag and drop between two Eeschema windows. Wayne: E3) Hook up perform last library search hot key to replace search libraries for @@ -159,5 +159,6 @@ PCBNew of PLUGIN::Footprint*() functions. At least LEGACY and KICAD are both needed concurrently. - +*) Add a hot key to toggle the 45 degree constraint on and off so that it can be + changed when drawing a trace. diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index b0d61ebd8c..ffc0279f08 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -72,6 +72,7 @@ set(COMMON_SRCS newstroke_font.cpp projet_config.cpp ptree.cpp + reporter.cpp richio.cpp selcolor.cpp string.cpp @@ -116,6 +117,9 @@ set(PCB_COMMON_SRCS ../pcbnew/class_zone_settings.cpp ../pcbnew/classpcb.cpp ../pcbnew/collectors.cpp + ../pcbnew/netlist_reader_common.cpp + ../pcbnew/netlist_reader_firstformat.cpp + ../pcbnew/netlist_reader_kicad.cpp ../pcbnew/sel_layer.cpp ../pcbnew/pcb_plot_params.cpp ../pcbnew/io_mgr.cpp diff --git a/common/fpid.cpp b/common/fpid.cpp index 48433985cd..984604d3cc 100644 --- a/common/fpid.cpp +++ b/common/fpid.cpp @@ -310,6 +310,26 @@ std::string FPID::Format( const std::string& aLogicalLib, const std::string& aFo } +int FPID::compare( const FPID& aFPID ) const +{ + // Don't bother comparing the same object. + if( this == &aFPID ) + return 0; + + int retv = nickname.compare( aFPID.nickname ); + + if( retv != 0 ) + return retv; + + retv = footprint.compare( aFPID.footprint ); + + if( retv != 0 ) + return retv; + + return revision.compare( aFPID.revision ); +} + + #if 0 && defined(DEBUG) // build this with Debug CMAKE_BUILD_TYPE diff --git a/common/reporter.cpp b/common/reporter.cpp new file mode 100644 index 0000000000..8402d0c2b8 --- /dev/null +++ b/common/reporter.cpp @@ -0,0 +1,46 @@ +/** + * @file reporter.h + */ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Wayne Stambaugh + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + + +REPORTER& REPORTER::Report( const char *aText ) +{ + Report( FROM_UTF8( aText ) ); + return *this; +} + + +REPORTER& WX_TEXT_CTRL_REPORTER::Report( const wxString& aText ) +{ + wxCHECK_MSG( m_textCtrl != NULL, *this, + wxT( "No wxTextCtrl object defined in WX_TEXT_CTRL_REPORTER." ) ); + + m_textCtrl->AppendText( aText ); + return *this; +} diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 2a09e046fb..9149ae229c 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 20012 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 2012 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com * Copyright (C) 2008-2012 Wayne Stambaugh * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. * @@ -96,3 +96,4 @@ const wxString PSFileWildcard( _( "PostScript files (.ps)|*.ps" ) ); const wxString ReportFileWildcard = _( "Report files (*.rpt)|*.rpt" ); const wxString FootprintPlaceFileWildcard = _( "Footprint place files (*.pos)|*.pos" ); const wxString Shapes3DFileWildcard( _( "Vrml and x3d files (*.wrl *.x3d)|*.wrl;*.x3d" ) ); +const wxString TextWildcard( _( "Text files (*.txt)|*.txt" ) ); diff --git a/cvpcb/CMakeLists.txt b/cvpcb/CMakeLists.txt index f154c169d4..ad48351d46 100644 --- a/cvpcb/CMakeLists.txt +++ b/cvpcb/CMakeLists.txt @@ -27,9 +27,6 @@ set(CVPCB_DIALOGS set(CVPCB_SRCS ../common/base_units.cpp - ../pcbnew/netlist_reader_common.cpp - ../pcbnew/netlist_reader_kicad.cpp - ../pcbnew/netlist_reader_firstformat.cpp ../pcbnew/class_drc_item.cpp autosel.cpp cfg.cpp diff --git a/cvpcb/autosel.cpp b/cvpcb/autosel.cpp index fd9fc906c4..0e96048b06 100644 --- a/cvpcb/autosel.cpp +++ b/cvpcb/autosel.cpp @@ -1,3 +1,26 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 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 autosel.cpp */ @@ -39,10 +62,16 @@ typedef boost::ptr_vector< FOOTPRINT_ALIAS > FOOTPRINT_ALIAS_LIST; wxString GetQuotedText( wxString & text ) { int i = text.Find( QUOTE ); - if( wxNOT_FOUND == i ) return wxT( "" ); + + if( wxNOT_FOUND == i ) + return wxT( "" ); + wxString shrt = text.Mid( i + 1 ); i = shrt.Find( QUOTE ); - if( wxNOT_FOUND == i ) return wxT( "" ); + + if( wxNOT_FOUND == i ) + return wxT( "" ); + text = shrt.Mid( i + 1 ); return shrt.Mid( 0, i ); } @@ -52,13 +81,14 @@ void CVPCB_MAINFRAME::AssocieModule( wxCommandEvent& event ) { FOOTPRINT_ALIAS_LIST aliases; FOOTPRINT_ALIAS* alias; + COMPONENT* component; wxFileName fn; wxString msg, tmp; char Line[1024]; FILE* file; size_t ii; - if( m_components.empty() ) + if( m_netlist.IsEmpty() ) return; /* Find equivalents in all available files. */ @@ -79,8 +109,8 @@ void CVPCB_MAINFRAME::AssocieModule( wxCommandEvent& event ) if( !tmp ) { - msg.Printf( _( "Footprint alias library file <%s> could not be \ -found in the default search paths." ), + msg.Printf( _( "Footprint alias library file <%s> could not be found in the " + "default search paths." ), GetChars( fn.GetFullName() ) ); wxMessageBox( msg, titleLibLoadError, wxOK | wxICON_ERROR ); continue; @@ -127,18 +157,21 @@ found in the default search paths." ), m_skipComponentSelect = true; ii = 0; - BOOST_FOREACH( COMPONENT_INFO& component, m_components ) + + for( unsigned kk = 0; kk < m_netlist.GetCount(); kk++ ) { + component = m_netlist.GetComponent( kk ); + bool found = false; m_ListCmp->SetSelection( ii++, true ); - if( !component.m_Footprint.IsEmpty() ) + if( !component->GetFootprintLibName().IsEmpty() ) continue; BOOST_FOREACH( FOOTPRINT_ALIAS& alias, aliases ) { - if( alias.m_Name.CmpNoCase( component.m_Value ) != 0 ) + if( alias.m_Name.CmpNoCase( component->GetValue() ) != 0 ) continue; /* filter alias so one can use multiple aliases (for polar and nonpolar caps for @@ -147,23 +180,23 @@ found in the default search paths." ), if( module ) { - size_t filtercount = component.m_FootprintFilter.GetCount(); + size_t filtercount = component->GetFootprintFilters().GetCount(); found = ( 0 == filtercount ); // if no entries, do not filter for( size_t jj = 0; jj < filtercount && !found; jj++ ) { - found = module->m_Module.Matches( component.m_FootprintFilter[jj] ); + found = module->m_Module.Matches( component->GetFootprintFilters()[jj] ); } } else { - msg.Printf( _( "Component %s: footprint %s not found in \ -any of the project footprint libraries." ), - GetChars( component.m_Reference ), + msg.Printf( _( "Component %s: footprint %s not found in any of the project " + "footprint libraries." ), + GetChars( component->GetReference() ), GetChars( alias.m_FootprintName ) ); - wxMessageBox( msg, _( "CvPcb Error" ), wxOK | wxICON_ERROR, - this ); + wxMessageBox( msg, _( "CvPcb Error" ), wxOK | wxICON_ERROR, this ); } + if( found ) { SetNewPkg( alias.m_FootprintName ); @@ -171,15 +204,20 @@ any of the project footprint libraries." ), } } + /* obviously the last chance: there's only one filter matching one footprint */ - if( !found && 1 == component.m_FootprintFilter.GetCount() ) { + if( !found && 1 == component->GetFootprintFilters().GetCount() ) + { /* we do not need to analyse wildcards: single footprint do not contain them */ /* and if there are wildcards it just will not match any */ - FOOTPRINT_INFO *module = m_footprints.GetModuleInfo( component.m_FootprintFilter[0] ); - if( module ) { - SetNewPkg( component.m_FootprintFilter[0] ); + FOOTPRINT_INFO *module = m_footprints.GetModuleInfo( component->GetFootprintFilters()[0] ); + + if( module ) + { + SetNewPkg( component->GetFootprintFilters()[0] ); } } } + m_skipComponentSelect = false; } diff --git a/cvpcb/class_footprints_listbox.cpp b/cvpcb/class_footprints_listbox.cpp index a80c69c115..38421a34f7 100644 --- a/cvpcb/class_footprints_listbox.cpp +++ b/cvpcb/class_footprints_listbox.cpp @@ -1,6 +1,30 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2009 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2012 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 class_footprints_listbox.cpp - * class to display the list fo available footprints + * class to display the list of available footprints */ #include @@ -132,7 +156,7 @@ void FOOTPRINTS_LISTBOX::SetFootprintFullList( FOOTPRINT_LIST& list ) } -void FOOTPRINTS_LISTBOX::SetFootprintFilteredList( COMPONENT_INFO* Component, +void FOOTPRINTS_LISTBOX::SetFootprintFilteredList( COMPONENT* aComponent, FOOTPRINT_LIST& list ) { wxString msg; @@ -149,13 +173,16 @@ void FOOTPRINTS_LISTBOX::SetFootprintFilteredList( COMPONENT_INFO* Component, // The search is case insensitive wxString module = footprint.m_Module.Upper(); wxString candidate; - for( jj = 0; jj < Component->m_FootprintFilter.GetCount(); jj++ ) + + for( jj = 0; jj < aComponent->GetFootprintFilters().GetCount(); jj++ ) { - candidate = Component->m_FootprintFilter[jj].Upper(); + candidate = aComponent->GetFootprintFilters()[jj].Upper(); + if( !module.Matches( candidate ) ) continue; + msg.Printf( wxT( "%3zu %s" ), m_FilteredFootprintList.GetCount() + 1, - footprint.m_Module.GetData() ); + footprint.m_Module.GetData() ); m_FilteredFootprintList.Add( msg ); hasItem = true; } @@ -172,8 +199,10 @@ void FOOTPRINTS_LISTBOX::SetFootprintFilteredList( COMPONENT_INFO* Component, Refresh(); } -void FOOTPRINTS_LISTBOX::SetFootprintFilteredByPinCount( COMPONENT_INFO* Component, - FOOTPRINT_LIST& list ) { + +void FOOTPRINTS_LISTBOX::SetFootprintFilteredByPinCount( COMPONENT* aComponent, + FOOTPRINT_LIST& list ) +{ wxString msg; int oldSelection = GetSelection(); bool hasItem = false; @@ -184,10 +213,10 @@ void FOOTPRINTS_LISTBOX::SetFootprintFilteredByPinCount( COMPONENT_INFO* Compone { FOOTPRINT_INFO& footprint = list.GetItem(ii); - if( Component->m_pinCount == footprint.m_padCount ) + if( aComponent->GetNetCount() == footprint.m_padCount ) { msg.Printf( wxT( "%3zu %s" ), m_FilteredFootprintList.GetCount() + 1, - footprint.m_Module.GetData() ); + footprint.m_Module.GetData() ); m_FilteredFootprintList.Add( msg ); hasItem = true; } @@ -204,13 +233,7 @@ void FOOTPRINTS_LISTBOX::SetFootprintFilteredByPinCount( COMPONENT_INFO* Compone Refresh(); } -/** Set the footprint list. We can have 2 footprint list: - * The full footprint list - * The filtered footprint list (if the current selected component has a - * filter for footprints) - * @param FullList true = full footprint list, false = filtered footprint list - * @param Redraw = true to redraw the window - */ + void FOOTPRINTS_LISTBOX::SetActiveFootprintList( bool FullList, bool Redraw ) { bool old_selection = m_UseFootprintFullList; @@ -226,10 +249,12 @@ void FOOTPRINTS_LISTBOX::SetActiveFootprintList( bool FullList, bool Redraw ) if( m_ActiveFootprintList ) { bool new_selection; + if( FullList ) new_selection = true; else new_selection = false; + if( new_selection != old_selection ) SetSelection( 0, true ); } @@ -264,14 +289,12 @@ void FOOTPRINTS_LISTBOX::SetActiveFootprintList( bool FullList, bool Redraw ) /**************************************/ BEGIN_EVENT_TABLE( FOOTPRINTS_LISTBOX, ITEMS_LISTBOX_BASE ) -EVT_SIZE( ITEMS_LISTBOX_BASE::OnSize ) -EVT_CHAR( FOOTPRINTS_LISTBOX::OnChar ) + EVT_SIZE( ITEMS_LISTBOX_BASE::OnSize ) + EVT_CHAR( FOOTPRINTS_LISTBOX::OnChar ) END_EVENT_TABLE() -/********************************************************/ void FOOTPRINTS_LISTBOX::OnLeftClick( wxListEvent& event ) -/********************************************************/ { FOOTPRINT_INFO* Module; wxString footprintName = GetSelectedFootprint(); @@ -297,9 +320,7 @@ void FOOTPRINTS_LISTBOX::OnLeftClick( wxListEvent& event ) } -/******************************************************/ void FOOTPRINTS_LISTBOX::OnLeftDClick( wxListEvent& event ) -/******************************************************/ { wxString footprintName = GetSelectedFootprint(); @@ -307,21 +328,10 @@ void FOOTPRINTS_LISTBOX::OnLeftDClick( wxListEvent& event ) } -/** - * Function OnChar - * called on a key pressed - * Call default handler for some special keys, - * and for "ascii" keys, select the first footprint - * that the name starts by the letter. - * This is the defaut behaviour of a listbox, but because we use - * virtual lists, the listbox does not know anything to what is displayed, - * we must handle this behaviour here. - * Furthermore the footprint name is not at the beginning of - * displayed lines (the first word is the line number) - */ void FOOTPRINTS_LISTBOX::OnChar( wxKeyEvent& event ) { int key = event.GetKeyCode(); + switch( key ) { case WXK_LEFT: @@ -343,16 +353,20 @@ void FOOTPRINTS_LISTBOX::OnChar( wxKeyEvent& event ) default: break; } + // Search for an item name starting by the key code: key = toupper(key); + for( unsigned ii = 0; ii < m_ActiveFootprintList->GetCount(); ii++ ) { wxString text = m_ActiveFootprintList->Item(ii); + /* search for the start char of the footprint name. * we must skip the line number - */ + */ text.Trim(false); // Remove leading spaces in line unsigned jj = 0; + for( ; jj < text.Len(); jj++ ) { // skip line number @@ -367,6 +381,7 @@ void FOOTPRINTS_LISTBOX::OnChar( wxKeyEvent& event ) } int start_char = toupper( text[jj] ); + if( key == start_char ) { Focus( ii ); diff --git a/cvpcb/cvframe.cpp b/cvpcb/cvframe.cpp index c3c271c318..4098dc44f7 100644 --- a/cvpcb/cvframe.cpp +++ b/cvpcb/cvframe.cpp @@ -334,7 +334,7 @@ void CVPCB_MAINFRAME::ToFirstNA( wxCommandEvent& event ) int ii = 0; int selection; - if( m_components.empty() ) + if( m_netlist.IsEmpty() ) return; selection = m_ListCmp->GetSelection(); @@ -342,9 +342,9 @@ void CVPCB_MAINFRAME::ToFirstNA( wxCommandEvent& event ) if( selection < 0 ) selection = 0; - BOOST_FOREACH( COMPONENT_INFO & component, m_components ) + for( unsigned jj = 0; jj < m_netlist.GetCount(); jj++ ) { - if( component.m_Footprint.IsEmpty() && ii > selection ) + if( m_netlist.GetComponent( jj )->GetFootprintLibName().IsEmpty() && ii > selection ) { m_ListCmp->SetSelection( ii ); SendMessageToEESCHEMA(); @@ -363,7 +363,7 @@ void CVPCB_MAINFRAME::ToPreviousNA( wxCommandEvent& event ) int ii; int selection; - if( m_components.empty() ) + if( m_netlist.IsEmpty() ) return; ii = m_ListCmp->GetCount() - 1; @@ -372,9 +372,9 @@ void CVPCB_MAINFRAME::ToPreviousNA( wxCommandEvent& event ) if( selection < 0 ) selection = m_ListCmp->GetCount() - 1; - BOOST_REVERSE_FOREACH( COMPONENT_INFO & component, m_components ) + for( unsigned kk = m_netlist.GetCount() - 1; kk >= 0; kk-- ) { - if( component.m_Footprint.IsEmpty() && ii < selection ) + if( m_netlist.GetComponent( kk )->GetFootprintLibName().IsEmpty() && ii < selection ) { m_ListCmp->SetSelection( ii ); SendMessageToEESCHEMA(); @@ -412,15 +412,15 @@ void CVPCB_MAINFRAME::DelAssociations( wxCommandEvent& event ) m_skipComponentSelect = true; m_ListCmp->SetSelection( 0 ); - BOOST_FOREACH( COMPONENT_INFO & component, m_components ) + for( unsigned i = 0; i < m_netlist.GetCount(); i++ ) { - component.m_Footprint.Empty(); + m_netlist.GetComponent( i )->SetFootprintLibName( wxEmptyString ); SetNewPkg( wxEmptyString ); } m_skipComponentSelect = false; m_ListCmp->SetSelection( 0 ); - m_undefinedComponentCnt = m_components.size(); + m_undefinedComponentCnt = m_netlist.GetCount(); } DisplayStatus(); @@ -538,18 +538,18 @@ void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event ) else { - if( &m_components[ selection ] == NULL ) + if( m_netlist.GetComponent( selection ) == NULL ) m_FootprintList->SetActiveFootprintList( SELECT_FULL_LIST, REDRAW_LIST ); else { if( m_mainToolBar->GetToolToggled( ID_CVPCB_FOOTPRINT_DISPLAY_PIN_FILTERED_LIST ) ) { - m_FootprintList->SetFootprintFilteredByPinCount( &m_components[ selection ], + m_FootprintList->SetFootprintFilteredByPinCount( m_netlist.GetComponent( selection ), m_footprints ); } else { - m_FootprintList->SetFootprintFilteredList( &m_components[ selection ], + m_FootprintList->SetFootprintFilteredList( m_netlist.GetComponent( selection ), m_footprints ); } } @@ -568,7 +568,7 @@ void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event ) if( FindFocus() == m_ListCmp ) { - wxString module = *(&m_components[ selection ].m_Footprint); + wxString module = m_netlist.GetComponent( selection )->GetFootprintLibName(); bool found = false; for( int ii = 0; ii < m_FootprintList->GetCount(); ii++ ) @@ -642,7 +642,8 @@ void CVPCB_MAINFRAME::DisplayStatus() { wxString msg; - msg.Printf( _( "Components: %d (free: %d)" ), (int) m_components.size(), m_undefinedComponentCnt ); + msg.Printf( _( "Components: %d (free: %d)" ), (int) m_netlist.GetCount(), + m_undefinedComponentCnt ); SetStatusText( msg, 0 ); SetStatusText( wxEmptyString, 1 ); @@ -733,9 +734,9 @@ void CVPCB_MAINFRAME::SendMessageToEESCHEMA() { char cmd[1024]; int selection; - COMPONENT_INFO* Component; + COMPONENT* Component; - if( m_components.empty() ) + if( m_netlist.IsEmpty() ) return; selection = m_ListCmp->GetSelection(); @@ -743,12 +744,12 @@ void CVPCB_MAINFRAME::SendMessageToEESCHEMA() if ( selection < 0 ) selection = 0; - if( &m_components[ selection ] == NULL ) + if( m_netlist.GetComponent( selection ) == NULL ) return; - Component = &m_components[ selection ]; + Component = m_netlist.GetComponent( selection ); - sprintf( cmd, "$PART: \"%s\"", TO_UTF8( Component->m_Reference ) ); + sprintf( cmd, "$PART: \"%s\"", TO_UTF8( Component->GetReference() ) ); SendCommand( MSG_TO_SCH, cmd ); diff --git a/cvpcb/cvpcb.h b/cvpcb/cvpcb.h index 08b8b6e209..c506ea41df 100644 --- a/cvpcb/cvpcb.h +++ b/cvpcb/cvpcb.h @@ -21,10 +21,6 @@ #define LISTB_STYLE (wxSUNKEN_BORDER | wxLC_NO_HEADER | wxLC_REPORT | wxLC_VIRTUAL) -#include - -typedef boost::ptr_vector< COMPONENT_INFO > COMPONENT_LIST; - extern const wxString FootprintAliasFileExtension; extern const wxString RetroFileExtension; diff --git a/cvpcb/cvpcb_mainframe.h b/cvpcb/cvpcb_mainframe.h index 7e3a665fd8..49d8fe0ac3 100644 --- a/cvpcb/cvpcb_mainframe.h +++ b/cvpcb/cvpcb_mainframe.h @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2012 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 cvpcb_mainframe.h */ @@ -7,6 +31,7 @@ #include #include +#include #include #include @@ -28,19 +53,19 @@ class CVPCB_MAINFRAME : public EDA_BASE_FRAME { public: - bool m_KeepCvpcbOpen; + bool m_KeepCvpcbOpen; FOOTPRINTS_LISTBOX* m_FootprintList; COMPONENTS_LISTBOX* m_ListCmp; DISPLAY_FOOTPRINTS_FRAME* m_DisplayFootprintFrame; - wxAuiToolBar* m_mainToolBar; - wxFileName m_NetlistFileName; + wxAuiToolBar* m_mainToolBar; + wxFileName m_NetlistFileName; wxArrayString m_ModuleLibNames; wxArrayString m_AliasLibNames; - wxString m_UserLibraryPath; - wxString m_NetlistFileExtension; - wxString m_DocModulesFileName; - FOOTPRINT_LIST m_footprints; - COMPONENT_LIST m_components; + wxString m_UserLibraryPath; + wxString m_NetlistFileExtension; + wxString m_DocModulesFileName; + FOOTPRINT_LIST m_footprints; + NETLIST m_netlist; protected: int m_undefinedComponentCnt; @@ -60,7 +85,7 @@ public: /** * Function OnSelectComponent * Called when clicking on a component in component list window - * * Updates the filtered foorprint list, if the filtered list option is selected + * * Updates the filtered footprint list, if the filtered list option is selected * * Updates the current selected footprint in footprint list * * Updates the footprint shown in footprint display window (if opened) */ @@ -141,22 +166,12 @@ public: * file name of the netlist or cmp file. * If aFullFileName is empty, a file name will be asked to the user * @return 0 if an error occurred saving the link file to \a aFullFileName. - * -1 if cancelled + * -1 if canceled * 1 if OK */ int SaveCmpLinkFile( const wxString& aFullFileName ); - /** - * Function LoadComponentFile - * loads the .cmp link file \a aCmpFileName which stores - * the component/footprint association. - * - * @param aFileName The full filename of .cmp file to load - * If empty, a filename will be asked to the user - */ - bool LoadComponentLinkFile( const wxString& aFileName ); - /** * Function WriteComponentLinkFile * Writes the component footprint link file \a aFullFileName on disk. @@ -166,16 +181,6 @@ public: */ bool WriteComponentLinkFile( const wxString& aFullFileName ); - /** - * Function ReadComponentLinkFile - * Reads the component footprint link file \a aFullFileName. - * - * @param aFile = the opened the opened file to read. - * ReadComponentLinkFile will close the file - * @return true if OK, false if error. - */ - bool ReadComponentLinkFile( FILE * aFile ); - /** * Function ReadNetList * reads the netlist (.net) file defined by #m_NetlistFileName. diff --git a/cvpcb/cvstruct.h b/cvpcb/cvstruct.h index 5d7ed48187..6bfbf3c445 100644 --- a/cvpcb/cvstruct.h +++ b/cvpcb/cvstruct.h @@ -1,6 +1,29 @@ -/*********************************************************/ -/* cvstruct.h */ -/*********************************************************/ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 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 cvstruct.h + */ #ifndef CVSTRUCT_H #define CVSTRUCT_H @@ -10,6 +33,8 @@ /* Forward declarations of all top-level window classes. */ class CVPCB_MAINFRAME; +class COMPONENT; + /*********************************************************************/ /* ListBox (base class) to display lists of components or footprints */ @@ -53,10 +78,19 @@ public: void SetString( unsigned linecount, const wxString& text ); void AppendLine( const wxString& text ); void SetFootprintFullList( FOOTPRINT_LIST& list ); - void SetFootprintFilteredList( COMPONENT_INFO* Component, - FOOTPRINT_LIST& list ); - void SetFootprintFilteredByPinCount( COMPONENT_INFO* Component, - FOOTPRINT_LIST& list ); + void SetFootprintFilteredList( COMPONENT* aComponent, + FOOTPRINT_LIST& aList ); + void SetFootprintFilteredByPinCount( COMPONENT* aComponent, + FOOTPRINT_LIST& aList ); + + /** + * Set the footprint list. We can have 2 footprint list: + * The full footprint list + * The filtered footprint list (if the current selected component has a + * filter for footprints) + * @param FullList true = full footprint list, false = filtered footprint list + * @param Redraw = true to redraw the window + */ void SetActiveFootprintList( bool FullList, bool Redraw = false ); wxString GetSelectedFootprint(); @@ -65,6 +99,19 @@ public: // Events functions: void OnLeftClick( wxListEvent& event ); void OnLeftDClick( wxListEvent& event ); + + /** + * Function OnChar + * called on a key pressed + * Call default handler for some special keys, + * and for "ascii" keys, select the first footprint + * that the name starts by the letter. + * This is the default behavior of a listbox, but because we use + * virtual lists, the listbox does not know anything to what is displayed, + * we must handle this behavior here. + * Furthermore the footprint name is not at the beginning of + * displayed lines (the first word is the line number) + */ void OnChar( wxKeyEvent& event ); DECLARE_EVENT_TABLE() @@ -78,7 +125,7 @@ class COMPONENTS_LISTBOX : public ITEMS_LISTBOX_BASE { public: wxArrayString m_ComponentList; - CVPCB_MAINFRAME* m_Parent; + CVPCB_MAINFRAME* m_Parent; public: diff --git a/cvpcb/listboxes.cpp b/cvpcb/listboxes.cpp index c1e68a5910..b1fd0627b1 100644 --- a/cvpcb/listboxes.cpp +++ b/cvpcb/listboxes.cpp @@ -1,3 +1,26 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 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 listboxes.cpp * @brief Implementation of class for displaying footprint list and component lists. @@ -67,9 +90,10 @@ CVPCB_MAINFRAME* ITEMS_LISTBOX_BASE::GetParent() */ void CVPCB_MAINFRAME::BuildCmpListBox() { - wxString msg; - wxSize size( 10, 10 ); - wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); + COMPONENT* component; + wxString msg; + wxSize size( 10, 10 ); + wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); if( m_ListCmp == NULL ) { @@ -86,11 +110,14 @@ void CVPCB_MAINFRAME::BuildCmpListBox() m_ListCmp->m_ComponentList.Clear(); - BOOST_FOREACH( COMPONENT_INFO & component, m_components ) { + for( unsigned i = 0; i < m_netlist.GetCount(); i++ ) + { + component = m_netlist.GetComponent( i ); + msg.Printf( CMP_FORMAT, m_ListCmp->GetCount() + 1, - GetChars(component.m_Reference), - GetChars(component.m_Value), - GetChars(component.m_Footprint) ); + GetChars( component->GetReference() ), + GetChars( component->GetValue() ), + GetChars( component->GetFootprintLibName() ) ); m_ListCmp->m_ComponentList.Add( msg ); } diff --git a/cvpcb/loadcmp.cpp b/cvpcb/loadcmp.cpp index d01699a8be..21cc0cc961 100644 --- a/cvpcb/loadcmp.cpp +++ b/cvpcb/loadcmp.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -45,9 +46,9 @@ MODULE* DISPLAY_FOOTPRINTS_FRAME::Get_Module( const wxString& aFootprintName ) if( !libPath ) { - wxString msg = wxString::Format( - _("PCB foot print library file <%s> could not be found in the default search paths." ), - fn.GetFullName().GetData() ); + wxString msg = wxString::Format( _( "PCB footprint library file <%s> could not " + "be found in the default search paths." ), + fn.GetFullName().GetData() ); // @todo we should not be using wxMessageBox directly. wxMessageBox( msg, titleLibLoadError, wxOK | wxICON_ERROR, this ); @@ -58,7 +59,7 @@ MODULE* DISPLAY_FOOTPRINTS_FRAME::Get_Module( const wxString& aFootprintName ) if( footprint ) { - footprint->SetParent( GetBoard() ); + footprint->SetParent( (EDA_ITEM*) GetBoard() ); footprint->SetPosition( wxPoint( 0, 0 ) ); return footprint; } @@ -74,4 +75,3 @@ MODULE* DISPLAY_FOOTPRINTS_FRAME::Get_Module( const wxString& aFootprintName ) DisplayError( this, msg ); return NULL; } - diff --git a/cvpcb/read_write_cmpfile.cpp b/cvpcb/read_write_cmpfile.cpp index c527b09f51..fc0a0bc23f 100644 --- a/cvpcb/read_write_cmpfile.cpp +++ b/cvpcb/read_write_cmpfile.cpp @@ -54,6 +54,7 @@ static char HeaderLinkFile[] = { "Cmp-Mod V01" }; bool CVPCB_MAINFRAME::WriteComponentLinkFile( const wxString& aFullFileName ) { + COMPONENT* component; FILE* outputFile; wxFileName fn( aFullFileName ); wxString Title = wxGetApp().GetTitle() + wxT( " " ) + GetBuildVersion(); @@ -69,13 +70,15 @@ bool CVPCB_MAINFRAME::WriteComponentLinkFile( const wxString& aFullFileName ) retval |= fprintf( outputFile, " Created by %s", TO_UTF8( Title ) ); retval |= fprintf( outputFile, " date = %s\n", TO_UTF8( DateAndTime() ) ); - BOOST_FOREACH( COMPONENT_INFO& component, m_components ) + for( unsigned i = 0; i < m_netlist.GetCount(); i++ ) { + component = m_netlist.GetComponent( i ); retval |= fprintf( outputFile, "\nBeginCmp\n" ); - retval |= fprintf( outputFile, "TimeStamp = %s;\n", TO_UTF8( component.m_TimeStamp ) ); - retval |= fprintf( outputFile, "Reference = %s;\n", TO_UTF8( component.m_Reference ) ); - retval |= fprintf( outputFile, "ValeurCmp = %s;\n", TO_UTF8( component.m_Value ) ); - retval |= fprintf( outputFile, "IdModule = %s;\n", TO_UTF8( component.m_Footprint ) ); + retval |= fprintf( outputFile, "TimeStamp = %s;\n", TO_UTF8( component->GetTimeStamp() ) ); + retval |= fprintf( outputFile, "Reference = %s;\n", TO_UTF8( component->GetReference() ) ); + retval |= fprintf( outputFile, "ValeurCmp = %s;\n", TO_UTF8( component->GetValue() ) ); + retval |= fprintf( outputFile, "IdModule = %s;\n", + TO_UTF8( component->GetFootprintLibName() ) ); retval |= fprintf( outputFile, "EndCmp\n" ); } @@ -83,98 +86,3 @@ bool CVPCB_MAINFRAME::WriteComponentLinkFile( const wxString& aFullFileName ) fclose( outputFile ); return retval >= 0; } - -bool CVPCB_MAINFRAME::ReadComponentLinkFile( FILE * aFile ) -{ - wxString timestamp, valeur, ilib, namecmp, msg; - bool read_cmp_data = false, eof = false; - char Line[1024], * ident, * data; - - // Identification of the type of link file - if( fgets( Line, sizeof(Line), aFile ) == 0 || - strnicmp( Line, HeaderLinkFile, 11 ) != 0 ) - { - fclose( aFile ); - return false; - } - - while( !eof && fgets( Line, sizeof(Line), aFile ) != 0 ) - { - if( strnicmp( Line, "EndListe", 8 ) == 0 ) - break; - - /* Search the beginning of the component description. */ - if( strnicmp( Line, "BeginCmp", 8 ) != 0 ) - continue; - - timestamp.Empty(); - valeur.Empty(); - ilib.Empty(); - namecmp.Empty(); - read_cmp_data = true; - - while( !eof && read_cmp_data ) - { - if( fgets( Line, 1024, aFile ) == 0 ) - { - eof = true; - break; - } - - if( strnicmp( Line, "EndCmp", 6 ) == 0 ) - { - read_cmp_data = true; - break; - } - - ident = strtok( Line, "=;\n\r" ); - data = strtok( NULL, ";\n\r" ); - - if( strnicmp( ident, "TimeStamp", 9 ) == 0 ) - { - timestamp = FROM_UTF8( data ); - timestamp.Trim( true ); - timestamp.Trim( false ); - continue; - } - - if( strnicmp( ident, "Reference", 9 ) == 0 ) - { - namecmp = FROM_UTF8( data ); - namecmp.Trim( true ); - namecmp.Trim( false ); - continue; - } - - if( strnicmp( ident, "ValeurCmp", 9 ) == 0 ) - { - valeur = FROM_UTF8( data ); - valeur.Trim( true ); - valeur.Trim( false ); - continue; - } - - if( strnicmp( ident, "IdModule", 8 ) == 0 ) - { - ilib = FROM_UTF8( data ); - ilib.Trim( true ); - ilib.Trim( false ); - continue; - } - } // End reading one component link block. - - // Search corresponding component info in list and update its parameters. - BOOST_FOREACH( COMPONENT_INFO& component, m_components ) - { - if( namecmp != component.m_Reference ) - continue; - - /* Copy the name of the corresponding module. */ - component.m_Footprint = ilib; - } - } - - fclose( aFile ); - return true; -} - diff --git a/cvpcb/readschematicnetlist.cpp b/cvpcb/readschematicnetlist.cpp index 06e0a36bbf..402443bb98 100644 --- a/cvpcb/readschematicnetlist.cpp +++ b/cvpcb/readschematicnetlist.cpp @@ -26,7 +26,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/* Read a nelist type Eeschema (New and Old format) +/* Read a netlist type Eeschema (New and Old format) * or OrcadPCB2 and build the component list */ @@ -35,82 +35,56 @@ #include #include #include +#include #include #include - #include -// COMPONENT_INFO object list sort function: -bool operator < ( const COMPONENT_INFO& item1, const COMPONENT_INFO& item2 ) -{ - return StrNumCmp( item1.m_Reference, item2.m_Reference, INT_MAX, true ) < 0; -} - int CVPCB_MAINFRAME::ReadSchematicNetlist() { - FILE* netfile = wxFopen( m_NetlistFileName.GetFullPath(), wxT( "rt" ) ); + wxBusyCursor dummy; // Shows an hourglass while loading. + NETLIST_READER* netlistReader; + wxString msg; + wxString compFootprintLinkFileName; + wxFileName fn = m_NetlistFileName; - if( netfile == NULL ) + // Load the footprint association file if it has already been created. + fn.SetExt( ComponentFileExtension ); + + if( fn.FileExists() && fn.IsFileReadable() ) + compFootprintLinkFileName = fn.GetFullPath(); + + m_netlist.Clear(); + + try { - wxString msg; - msg.Printf( _( "Could not open file <%>" ), - GetChars( m_NetlistFileName.GetFullPath() ) ); - wxMessageBox( msg ); - return -1; + netlistReader = NETLIST_READER::GetNetlistReader( &m_netlist, + m_NetlistFileName.GetFullPath(), + compFootprintLinkFileName ); + std::auto_ptr< NETLIST_READER > nlr( netlistReader ); + netlistReader->LoadNetlist(); + } + catch( IO_ERROR& ioe ) + { + msg = wxString::Format( _( "Error loading netlist.\n%s" ), ioe.errorText.GetData() ); + wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR ); + return 1; } - NETLIST_READER netList_Reader( NULL, NULL ); - netList_Reader.m_UseTimeStamp = false; - netList_Reader.m_ChangeFootprints = false; - netList_Reader.m_UseCmpFile = false; - netList_Reader.SetFilesnames( m_NetlistFileName.GetFullPath(), wxEmptyString ); - // True to read footprint filters section: true for CvPcb, false for Pcbnew - netList_Reader.ReadLibpartSectionSetOpt( true ); - - // on OSX otherwise reloading a file you will see duplicates - m_components.clear(); - - bool success = netList_Reader.ReadNetList( netfile ); - if( !success ) + // We also remove footprint name if it is "$noname" because this is a dummy name, + // not the actual name of the footprint. + for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ ) { - wxMessageBox( _("Netlist read error") ); - return false; + if( m_netlist.GetComponent( ii )->GetFootprintLibName() == wxT( "$noname" ) ) + m_netlist.GetComponent( ii )->SetFootprintLibName( wxEmptyString ); } - // Now copy footprints info into Cvpcb list: - // We also remove footprint name if it is "$noname" - // because this is a dummy name,, not an actual name - COMPONENT_INFO_LIST& cmpInfo = netList_Reader.GetComponentInfoList(); - for( unsigned ii = 0; ii < cmpInfo.size(); ii++ ) - { - m_components.push_back( cmpInfo[ii] ); - if( cmpInfo[ii]->m_Footprint == wxT( "$noname" ) ) - cmpInfo[ii]->m_Footprint.Empty(); - } - cmpInfo.clear(); // cmpInfo is no more owner of the list. - // Sort components by reference: - sort( m_components.begin(), m_components.end() ); - - // Now copy filters in m_components, if netlist type is KICAD - // ( when the format is the "old" PCBNEW format, filters are already in - // m_component list - if( NETLIST_TYPE_KICAD == netList_Reader.GetNetlistType() ) - { - for( unsigned ii = 0; ii < m_components.size(); ii++ ) - { - LIPBART_INFO* libpart = netList_Reader.GetLibpart(m_components[ii].m_Libpart); - if( libpart == NULL ) - continue; - - // now copy filter list - m_components[ii].m_FootprintFilter = libpart->m_FootprintFilter; - } - } + m_netlist.SortByReference(); return 0; } diff --git a/cvpcb/readwrite_dlgs.cpp b/cvpcb/readwrite_dlgs.cpp index 881b0fbef3..4027980809 100644 --- a/cvpcb/readwrite_dlgs.cpp +++ b/cvpcb/readwrite_dlgs.cpp @@ -41,17 +41,17 @@ void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName ) { - COMPONENT_INFO* component; + COMPONENT* component; bool hasFootprint = false; int componentIndex; wxString description; - if( m_components.empty() ) + if( m_netlist.IsEmpty() ) return; - // if no component is selected, select the first one + // If no component is selected, select the first one - if(m_ListCmp->GetFirstSelected() < 0) + if( m_ListCmp->GetFirstSelected() < 0 ) { componentIndex = 0; m_ListCmp->SetSelection( componentIndex, true ); @@ -61,28 +61,28 @@ void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName ) while( m_ListCmp->GetFirstSelected() != -1) { - // get the component for the current iteration + // Get the component for the current iteration componentIndex = m_ListCmp->GetFirstSelected(); - component = &m_components[componentIndex]; + component = m_netlist.GetComponent( componentIndex ); if( component == NULL ) return; - // check to see if the component has allready a footprint set. + // Check to see if the component has already a footprint set. - hasFootprint = !(component->m_Footprint.IsEmpty()); + hasFootprint = !(component->GetFootprintLibName().IsEmpty()); - component->m_Footprint = aFootprintName; + component->SetFootprintLibName( aFootprintName ); // create the new component description description.Printf( CMP_FORMAT, componentIndex + 1, - GetChars( component->m_Reference ), - GetChars( component->m_Value ), - GetChars( component->m_Footprint ) ); + GetChars( component->GetReference() ), + GetChars( component->GetValue() ), + GetChars( component->GetFootprintLibName() ) ); - // if the component hasn't had a footprint associated with it + // If the component hasn't had a footprint associated with it // it now has, so we decrement the count of components without // a footprint assigned. @@ -92,12 +92,12 @@ void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName ) m_undefinedComponentCnt -= 1; } - // set the new description and deselect the processed component + // Set the new description and deselect the processed component m_ListCmp->SetString( componentIndex, description ); m_ListCmp->SetSelection( componentIndex, false ); } - // mark this "session" as modified + // Mark this "session" as modified m_modified = true; // select the next component, if there is one @@ -113,22 +113,10 @@ void CVPCB_MAINFRAME::SetNewPkg( const wxString& aFootprintName ) bool CVPCB_MAINFRAME::ReadNetListAndLinkFiles() { + COMPONENT* component; wxString msg; - int error_level; - error_level = ReadSchematicNetlist(); - - if( error_level < 0 ) - { - msg.Printf( _( "File <%s> does not appear to be a valid KiCad net list file." ), - GetChars( m_NetlistFileName.GetFullPath() ) ); - wxMessageBox( msg, _( "File Error" ), wxOK | wxICON_ERROR, this ); - m_NetlistFileName.Clear(); - UpdateTitle(); - return false; - } - - LoadComponentLinkFile( m_NetlistFileName.GetFullPath() ); + ReadSchematicNetlist(); if( m_ListCmp == NULL ) return false; @@ -140,19 +128,21 @@ bool CVPCB_MAINFRAME::ReadNetListAndLinkFiles() m_ListCmp->Clear(); m_undefinedComponentCnt = 0; - BOOST_FOREACH( COMPONENT_INFO& component, m_components ) + for( unsigned i = 0; i < m_netlist.GetCount(); i++ ) { + component = m_netlist.GetComponent( i ); + msg.Printf( CMP_FORMAT, m_ListCmp->GetCount() + 1, - GetChars( component.m_Reference ), - GetChars( component.m_Value ), - GetChars( component.m_Footprint ) ); + GetChars( component->GetReference() ), + GetChars( component->GetValue() ), + GetChars( component->GetFootprintLibName() ) ); m_ListCmp->AppendLine( msg ); - if( component.m_Footprint.IsEmpty() ) + if( component->GetFootprintLibName().IsEmpty() ) m_undefinedComponentCnt += 1; } - if( !m_components.empty() ) + if( !m_netlist.IsEmpty() ) m_ListCmp->SetSelection( 0, true ); DisplayStatus(); @@ -165,37 +155,6 @@ bool CVPCB_MAINFRAME::ReadNetListAndLinkFiles() } -bool CVPCB_MAINFRAME::LoadComponentLinkFile( const wxString& aFileName ) -{ - FILE* linkfile; - wxFileName fn = aFileName; - - fn.SetExt( ComponentFileExtension ); - - linkfile = wxFopen( fn.GetFullPath(), wxT( "rt" ) ); - if( linkfile == NULL ) - { - wxString msg; - msg.Printf( _( "Cannot open CvPcb component file <%s>." ), - GetChars( fn.GetFullPath() ) ); - msg << wxT( "\n" ) << _( "This is normal if you are opening a new netlist file" ); - wxMessageBox( msg, titleComponentLibErr, wxOK | wxICON_ERROR ); - return false; - } - - // read and close the file - if( ! ReadComponentLinkFile( linkfile ) ) - { - wxString msg; - msg.Printf( _( " <%s> does not appear to be a valid KiCad component link file." ), - GetChars( fn.GetFullPath() ) ); - wxMessageBox( msg, titleComponentLibErr, wxOK | wxICON_ERROR ); - return false; - } - - return true; -} - int CVPCB_MAINFRAME::SaveCmpLinkFile( const wxString& aFullFileName ) { wxFileName fn; @@ -207,7 +166,7 @@ int CVPCB_MAINFRAME::SaveCmpLinkFile( const wxString& aFullFileName ) } else { - wxFileDialog dlg( this, _( "Save Component/Footprint Link File" ), wxGetCwd(), + wxFileDialog dlg( this, _( "Save Component Footprint Link File" ), wxGetCwd(), wxEmptyString, ComponentFileWildcard, wxFD_SAVE ); if( dlg.ShowModal() == wxID_CANCEL ) @@ -224,7 +183,7 @@ int CVPCB_MAINFRAME::SaveCmpLinkFile( const wxString& aFullFileName ) if( WriteComponentLinkFile( fn.GetFullPath() ) == 0 ) { - DisplayError( this, _( "Unable to create component file (.cmp)" ) ); + DisplayError( this, _( "Unable to create component footprint link file (.cmp)" ) ); return 0; } diff --git a/eeschema/class_library.cpp b/eeschema/class_library.cpp index d5ef45001a..53e69728d7 100644 --- a/eeschema/class_library.cpp +++ b/eeschema/class_library.cpp @@ -148,7 +148,7 @@ void CMP_LIBRARY::GetEntryNames( wxArrayString& aNames, bool aSort, bool aMakeUp * simple function used as comparator to sort a std::vector&. * * @param aItem1 is the first comparison parameter. - * @param aItem1 is the second. + * @param aItem2 is the second. * @return bool - which item should be put first in the sorted list. */ bool sortFunction( wxArrayString aItem1, wxArrayString aItem2 ) diff --git a/include/appl_wxstruct.h b/include/appl_wxstruct.h index e793795dd3..014de9a7e1 100644 --- a/include/appl_wxstruct.h +++ b/include/appl_wxstruct.h @@ -259,7 +259,7 @@ public: * true. * @param aList = array of PARAM_CFG_BASE pointers */ - void SaveCurrentSetupValues( const PARAM_CFG_ARRAY& List ); + void SaveCurrentSetupValues( const PARAM_CFG_ARRAY& aList ); /** * Function ReadCurrentSetupValues @@ -268,7 +268,7 @@ public: * true. * @param aList = array of PARAM_CFG_BASE pointers */ - void ReadCurrentSetupValues( const PARAM_CFG_ARRAY& List ); + void ReadCurrentSetupValues( const PARAM_CFG_ARRAY& aList ); /** * Function ReadProjectConfig diff --git a/include/dialog_helpers.h b/include/dialog_helpers.h index 9f5bb90cd1..45e3ecfb13 100644 --- a/include/dialog_helpers.h +++ b/include/dialog_helpers.h @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2010 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2012 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 dialog_helpers.h * @brief Helper dialog and control classes. @@ -38,6 +62,7 @@ public: * Constructor: * @param aParent Pointer to the parent window. * @param aTitle = The title shown on top. + * @param aItemHeaders is an array containing the column header names for the dialog. * @param aItemList = A wxArrayString of the list of elements. * @param aRefText = An item name if an item must be preselected. * @param aCallBackFunction = callback function to display comments diff --git a/include/fpid.h b/include/fpid.h index 221e9c6a1a..657a6a7101 100644 --- a/include/fpid.h +++ b/include/fpid.h @@ -133,8 +133,31 @@ public: const std::string& aRevision ) throw( PARSE_ERROR ); + /** + * Function clear + * clears the contents of the library nickname, footprint name, and revision strings. + */ void clear(); + /** + * Function empty + * @return a boolean true value if the FPID is empty. Otherwise return false. + */ + bool empty() const { return nickname.empty() && footprint.empty() && revision.empty(); } + + /** + * Function Compare + * compares the contents of FPID objects by performing a std::string comparison of the + * library nickname, footprint name, and revision strings respectively. + * + * @param aFPID is the FPID to compare against. + * @return -1 if less than \a aFPID, 1 if greater than \a aFPID, and 0 if equal to \a aFPID. + */ + int compare( const FPID& aFPID ) const; + + bool operator <( const FPID& aFPID ) const { return this->compare( aFPID ) < 0; } + bool operator ==( const FPID& aFPID ) const { return this->compare( aFPID ) == 0; } + #if defined(DEBUG) static void Test(); #endif diff --git a/include/reporter.h b/include/reporter.h new file mode 100644 index 0000000000..9f2081a8a6 --- /dev/null +++ b/include/reporter.h @@ -0,0 +1,89 @@ +#ifndef _REPORTER_H_ +#define _REPORTER_H_ + +/** + * @file reporter.h + * @author Wayne Stambaugh + * @note A special thanks to Dick Hollenbeck who came up with the idea that inspired + * me to write this. + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 Wayne Stambaugh + * Copyright (C) 1992-2013 KiCad Developers, see change_log.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 + */ + + +class wxString; +class wxTextCtrl; + + +/** + * Class REPORTER + * is a pure virtual class used to derive REPORTOR objects from. + * + * The purpose of the REPORTER object is to hide an object that take a string as an input + * from other objects. This prevents objects such as wxWidgets UI control internals from + * being exposed to low level KiCad objects dervice from #BOARD_ITEM and #SCH_ITEM. + */ +class REPORTER +{ +public: + /** + * Function Report + * is a pure virtual function to override in the derived object. + * + * @param aText is the string to report. + */ + virtual REPORTER& Report( const wxString& aText ) = 0; + + REPORTER& Report( const char *aText ); + + REPORTER& operator <<( const wxString& aText ) { return Report( aText ); } + + REPORTER& operator <<( const wxChar* aText ) { return Report( wxString( aText ) ); } + + REPORTER& operator <<( wxChar aChar ) { return Report( wxString( aChar ) ); } + + REPORTER& operator <<( const char* aText ) { return Report( aText ); } +}; + + +/** + * Class WX_TEXT_CTRL_REPORTER + * is wrapper for reporting to a wxTextCtrl object. + */ +class WX_TEXT_CTRL_REPORTER : public REPORTER +{ + wxTextCtrl* m_textCtrl; + +public: + WX_TEXT_CTRL_REPORTER( wxTextCtrl* aTextCtrl ) : + REPORTER(), + m_textCtrl( aTextCtrl ) + { + } + + REPORTER& Report( const wxString& aText ); +}; + +#endif // _REPORTER_H_ diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index 9f1368eabb..39dc4f5165 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -98,6 +98,7 @@ extern const wxString KiCadFootprintLibFileWildcard; extern const wxString KiCadFootprintLibPathWildcard; extern const wxString GedaPcbFootprintLibFileWildcard; extern const wxString EagleFootprintLibPathWildcard; +extern const wxString TextWildcard; #endif // INCLUDE_WILDCARDS_AND_FILES_EXT_H_ diff --git a/include/wxBasePcbFrame.h b/include/wxBasePcbFrame.h index cdc7b19cf7..964b4163d4 100644 --- a/include/wxBasePcbFrame.h +++ b/include/wxBasePcbFrame.h @@ -91,6 +91,19 @@ protected: void updateZoomSelectBox(); virtual void unitsChangeRefresh(); + /** + * Function loadFootprint + * attempts to load \a aFootprintName from the list of libraries. + * + * @param aFootprintName is the name of component footprint to load. + * @return the #MODULE if found or NULL if \a aFootprintName not found in any of the + * libraries. + * @throw IO_ERROR if an I/O error occurs or a #PARSE_ERROR if a file parsing error + * occurs while reading footprint library files. + */ + MODULE* loadFootprint( const wxString& aFootprintName ) + throw( IO_ERROR, PARSE_ERROR ); + public: PCB_BASE_FRAME( wxWindow* aParent, ID_DRAWFRAME_TYPE aFrameType, const wxString& aTitle, diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index 5ba16dc3e6..8a87869d7b 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -59,6 +59,10 @@ class PCB_LAYER_WIDGET; class MARKER_PCB; class BOARD_ITEM; class PCB_LAYER_BOX_SELECTOR; +class NETLIST; +class REPORTER; +class PARSE_ERROR; +class IO_ERROR; /** @@ -80,6 +84,18 @@ class PCB_EDIT_FRAME : public PCB_BASE_FRAME /// The auxiliary right vertical tool bar used to access the microwave tools. wxAuiToolBar* m_microWaveToolBar; + /** + * Function loadFootprints + * loads the footprints for each #COMPONENT in \a aNetlist from the list of libraries. + * + * @param aNetlist is the netlist of components to load the footprints into. + * @param aReporter is the #REPORTER object to report to. + * @throw IO_ERROR if an I/O error occurs or a #PARSE_ERROR if a file parsing error + * occurs while reading footprint library files. + */ + void loadFootprints( NETLIST& aNetlist, REPORTER* aReporter ) + throw( IO_ERROR, PARSE_ERROR ); + protected: #ifdef KICAD_SCRIPTING_WXPYTHON @@ -414,27 +430,6 @@ public: m_useCmpFileForFpNames = aUseCmpfile; } - /** - * Function Test_Duplicate_Missing_And_Extra_Footprints - * Build a list of duplicate, missing and extra footprints - * from the current board and a netlist netlist : - * Shows 3 lists: - * 1 - duplicate footprints on board - * 2 - missing footprints (found in netlist but not on board) - * 3 - footprints not in netlist but on board - * @param aFilename = the full filename netlist - * @param aDuplicate = the list of duplicate modules to populate - * @param aMissing = the list of missing module references and values - * to populate. For each missing item, the first string is the ref, - * the second is the value. - * @param aNotInNetlist = the list of not-in-netlist modules to populate - * @return true if the netlist was read, or false - */ - bool Test_Duplicate_Missing_And_Extra_Footprints( const wxString& aFilename, - std::vector & aDuplicate, - wxArrayString& aMissing, - std::vector & aNotInNetlist ); - /** * Function OnHotKey. * ** Commands are case insensitive ** @@ -960,6 +955,11 @@ public: */ void Access_to_External_Tool( wxCommandEvent& event ); + /** + * Function ListAndSelectModuleName + * builds and shows a list of existing modules on board that the user can select. + * @return a pointer to the selected module or NULL. + */ MODULE* ListAndSelectModuleName(); /** @@ -1421,29 +1421,31 @@ public: /** * Function ReadPcbNetlist - * Update footprints (load missing footprints and delete on demand extra - * footprints) + * reads \a aNetlistFileName and ppdates the footprints (load missing footprints and + * delete on demand extra footprints) on the board. * Update connectivity info, references, values and "TIME STAMP" - * @param aNetlistFullFilename = netlist file name (*.net) - * @param aCmpFullFileName = cmp/footprint link file name (*.cmp). - * if not found or empty, only the netlist will be used - * @param aMessageWindow = a reference to a wxTextCtrl where to display messages. - * can be NULL + * + * @param aNetlistFileName = netlist file name (*.net) + * @param aCmpFileName = cmp/footprint link file name (*.cmp). + * if not found or empty, only the netlist will be used + * @param aReporter is a pointer to a #REPORTER object to write display messages. + * can be NULL. * @param aChangeFootprint if true, footprints that have changed in netlist will be changed * @param aDeleteBadTracks if true, erroneous tracks will be deleted * @param aDeleteExtraFootprints if true, remove unlocked footprints that are not in netlist - * @param aSelect_By_Timestamp if true, use timestamp instead of reference to identify - * footprints from components (use after reannotation of the - * schematic) - * @return true if Ok + * @param aSelectByTimestamp if true, use timestamp instead of reference to identify + * footprints from components (use after reannotation of the + * schematic) + * @param aIsDryRun performs a dry run without making any changes if true. */ - bool ReadPcbNetlist( const wxString& aNetlistFullFilename, - const wxString& aCmpFullFileName, - wxTextCtrl* aMessageWindow, + void ReadPcbNetlist( const wxString& aNetlistFileName, + const wxString& aCmpFileName, + REPORTER* aReporter, bool aChangeFootprint, bool aDeleteBadTracks, bool aDeleteExtraFootprints, - bool aSelect_By_Timestamp ); + bool aSelectByTimestamp, + bool aIsDryRun ); /** * Function RemoveMisConnectedTracks @@ -1480,8 +1482,7 @@ public: * @param include_fixe = true to orient locked footprints * @return true if some footprints modified, false if no change */ - bool ReOrientModules( const wxString& ModuleMask, int Orient, - bool include_fixe ); + bool ReOrientModules( const wxString& ModuleMask, int Orient, bool include_fixe ); void LockModule( MODULE* aModule, bool aLocked ); void AutoMoveModulesOnPcb( bool PlaceModulesHorsPcb ); diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index ba68add0c8..1c6bf153e4 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -181,9 +181,6 @@ set(PCBNEW_CLASS_SRCS muonde.cpp muwave_command.cpp netlist.cpp - netlist_reader_common.cpp - netlist_reader_firstformat.cpp - netlist_reader_kicad.cpp onleftclick.cpp onrightclick.cpp pad_edition_functions.cpp diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index eb7c6d540b..6d9fbd1d25 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include @@ -1411,6 +1413,26 @@ MODULE* BOARD::FindModuleByReference( const wxString& aReference ) const } +MODULE* BOARD::FindModule( const wxString& aRefOrTimeStamp, bool aSearchByTimeStamp ) +{ + for( MODULE* module = m_Modules; module != NULL; module = module->Next() ) + { + if( aSearchByTimeStamp ) + { + if( aRefOrTimeStamp.CmpNoCase( module->GetPath() ) == 0 ) + return module; + } + else + { + if( aRefOrTimeStamp.CmpNoCase( module->GetReference() ) == 0 ) + return module; + } + } + + return NULL; +} + + // Sort nets by decreasing pad count static bool s_SortByNodes( const NETINFO_ITEM* a, const NETINFO_ITEM* b ) { @@ -2310,6 +2332,251 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI } +void BOARD::ReplaceNetlist( NETLIST& aNetlist, REPORTER* aReporter ) +{ + unsigned i; + wxPoint bestPosition; + wxString msg; + D_PAD* pad; + MODULE* footprint; + COMPONENT* component; + COMPONENT_NET net; + + if( !IsEmpty() ) + { + // Position new components below any existing board features. + EDA_RECT bbbox = ComputeBoundingBox( true ); + + if( bbbox.GetWidth() || bbbox.GetHeight() ) + { + bestPosition.x = bbbox.Centre().x; + bestPosition.y = bbbox.GetBottom() + DMils2iu( 5000 ); + } + } + else + { + // Position new components in the center of the page when the board is empty. + wxSize pageSize = m_paper.GetSizeIU(); + + bestPosition.x = pageSize.GetWidth() / 2; + bestPosition.y = pageSize.GetHeight() / 2; + } + + m_Status_Pcb = 0; + + for( i = 0; i < aNetlist.GetCount(); i++ ) + { + component = aNetlist.GetComponent( i ); + + if( aReporter ) + { + msg.Printf( _( "Checking netlist component footprint \"%s:%s:%s\".\n" ), + GetChars( component->GetReference() ), + GetChars( component->GetTimeStamp() ), + GetChars( component->GetFootprintLibName() ) ); + aReporter->Report( msg ); + } + + if( aNetlist.IsFindByTimeStamp() ) + footprint = FindModule( aNetlist.GetComponent( i )->GetTimeStamp(), true ); + else + footprint = FindModule( aNetlist.GetComponent( i )->GetReference() ); + + if( footprint == NULL ) // A new footprint. + { + if( aReporter ) + { + msg.Printf( _( "Adding new component \"%s:%s\" footprint \"%s\".\n" ), + GetChars( component->GetReference() ), + GetChars( component->GetTimeStamp() ), + GetChars( component->GetFootprintLibName() ) ); + aReporter->Report( msg ); + } + + // Owned by NETLIST, can only copy and read it. + footprint = component->GetModule(); + + wxCHECK2_MSG( footprint != NULL, continue, + wxString::Format( wxT( "No footprint loaded for component \"%s\"." ), + GetChars( component->GetReference() ) ) ); + + if( !aNetlist.IsDryRun() ) + { + footprint = new MODULE( *footprint ); + footprint->SetParent( this ); + footprint->SetPosition( bestPosition ); + footprint->SetTimeStamp( GetNewTimeStamp() ); + Add( footprint, ADD_APPEND ); + } + } + else // An existing footprint. + { + // Test for footprint change. + if( !component->GetFootprintLibName().IsEmpty() && + footprint->GetLibRef() != component->GetFootprintLibName() ) + { + if( aNetlist.GetReplaceFootprints() ) + { + if( aReporter ) + { + msg.Printf( _( "Replacing component \"%s:%s\" footprint \"%s\" with \"%s\".\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( footprint->GetLibRef() ), + GetChars( component->GetFootprintLibName() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + { + wxASSERT( footprint != NULL ); + MODULE* newFootprint = new MODULE( *component->GetModule() ); + + if( aNetlist.IsFindByTimeStamp() ) + newFootprint->SetReference( footprint->GetReference() ); + else + newFootprint->SetPath( footprint->GetPath() ); + + footprint->CopyNetlistSettings( newFootprint ); + Remove( footprint ); + Add( newFootprint, ADD_APPEND ); + footprint = newFootprint; + } + } + } + + // Test for reference designator field change. + if( footprint->GetReference() != component->GetReference() ) + { + if( aReporter ) + { + msg.Printf( _( "Changing footprint \"%s:%s\" reference to \"%s\".\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( component->GetReference() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + footprint->SetReference( component->GetReference() ); + } + + // Test for value field change. + if( footprint->GetValue() != component->GetValue() ) + { + if( aReporter ) + { + msg.Printf( _( "Changing footprint \"%s:%s\" value from \"%s\" to \"%s\".\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( footprint->GetValue() ), + GetChars( component->GetValue() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + footprint->SetValue( component->GetValue() ); + } + + // Test for time stamp change. + if( footprint->GetPath() != component->GetTimeStamp() ) + { + if( aReporter ) + { + msg.Printf( _( "Changing footprint path \"%s:%s\" to \"%s\".\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( component->GetTimeStamp() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + footprint->SetPath( component->GetTimeStamp() ); + } + } + + wxASSERT( component != NULL ); + + // At this point, the component footprint is updated. Now update the nets. + for( pad = footprint->Pads(); pad; pad = pad->Next() ) + { + net = component->GetNet( pad->GetPadName() ); + + if( !net.IsValid() ) // Footprint pad had no net. + { + if( !pad->GetNetname().IsEmpty() ) + { + if( aReporter ) + { + msg.Printf( _( "Clearing component \"%s:%s\" pin \"%s\" net name.\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( pad->GetPadName() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + pad->SetNetname( wxEmptyString ); + } + } + else // Footprint pad has a net. + { + if( net.GetNetName() != pad->GetNetname() ) + { + if( aReporter ) + { + msg.Printf( _( "Changing component \"%s:%s\" pin \"%s\" net name from " + "\"%s\" to \"%s\".\n" ), + GetChars( footprint->GetReference() ), + GetChars( footprint->GetPath() ), + GetChars( pad->GetPadName() ), + GetChars( pad->GetNetname() ), + GetChars( net.GetNetName() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + pad->SetNetname( net.GetNetName() ); + } + } + } + } + + // Remove all components not in the netlist. + if( aNetlist.GetDeleteExtraFootprints() ) + { + MODULE* nextModule; + + for( MODULE* module = m_Modules; module != NULL; module = nextModule ) + { + nextModule = module->Next(); + + if( module->IsLocked() ) + continue; + + if( aNetlist.IsFindByTimeStamp() ) + component = aNetlist.GetComponentByTimeStamp( module->GetPath() ); + else + component = aNetlist.GetComponentByReference( module->GetReference() ); + + if( component == NULL ) + { + if( aReporter ) + { + msg.Printf( _( "Removing footprint \"%s:%s\".\n" ), + GetChars( module->GetReference() ), + GetChars( module->GetPath() ) ); + aReporter->Report( msg ); + } + + if( !aNetlist.IsDryRun() ) + module->DeleteStructure(); + } + } + } +} + + #if defined(DEBUG) void BOARD::Show( int nestLevel, std::ostream& os ) const diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index bd0b33ea4e..6e28cb9028 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2007 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2012 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 class_board.h * @brief Class BOARD to handle a board. @@ -30,6 +54,8 @@ class TRACK; class D_PAD; class MARKER_PCB; class MSG_PANEL_ITEM; +class NETLIST; +class REPORTER; // non-owning container of item candidates when searching for items on the same track. @@ -282,6 +308,12 @@ public: BOARD(); ~BOARD(); + bool IsEmpty() const + { + return m_Drawings.GetCount() == 0 && m_Modules.GetCount() == 0 && + m_Track.GetCount() == 0 && m_Zone.GetCount() == 0; + } + void Move( const wxPoint& aMoveVector ); // overload void SetFileFormatVersionAtLoad( int aVersion ) { m_fileFormatVersionAtLoad = aVersion; } @@ -849,6 +881,47 @@ public: */ MODULE* FindModuleByReference( const wxString& aReference ) const; + /** + * Function FindModule + * searches for a module matching \a aRefOrTimeStamp depending on the state of + * \a aSearchByTimeStamp. + * @param aRefOrTimeStamp is the search string. + * @param aSearchByTimeStamp searches by the module time stamp value if true. Otherwise + * search by reference designator. + * @return the module found or NULL if not module is found that meets the search criteria. + */ + MODULE* FindModule( const wxString& aRefOrTimeStamp, bool aSearchByTimeStamp = false ); + + /** + * Function ReplaceNetlist + * updates the #BOARD according to \a aNetlist. + * + * The changes are made to the board are as follows they are not disabled in the status + * settings in the #NETLIST: + * - If a new component is found in the #NETLIST and not in the #BOARD, it is added + * to the #BOARD. + * - If a the component in the #NETLIST is already on the #BOARD, then one or more of the + * following actions can occur: + * + If the footprint name in the #NETLIST does not match the footprint name on the + * #BOARD, the footprint on the #BOARD is replaced with the footprint specified in + * the #NETLIST and the proper parameters are copied from the existing footprint. + * + If the reference designator in the #NETLIST does not match the reference designator + * on the #BOARD, the reference designator is updated from the #NETLIST. + * + If the value field in the #NETLIST does not match the value field on the #BOARD, + * the value field is updated from the #NETLIST. + * + If the time stamp in the #NETLIST does not match the time stamp on the #BOARD, + * the time stamp is updated from the #NETLIST. + * - After each footprint is added or update as described above, each footprint pad net + * name is compared and updated to the value defined in the #NETLIST. + * - After all of the footprints have been added, updated, and net names properly set, + * any extra unlock footprints are removed from the #BOARD. + * + * @param aNetlist is the new netlist to revise the contents of the #BOARD with. + * @param aReporter is a #REPORTER object to report the changes \a aNetlist makes to + * the #BOARD. If NULL, no change reporting occurs. + */ + void ReplaceNetlist( NETLIST& aNetlist, REPORTER* aReporter = NULL ); + /** * Function ReturnSortedNetnamesList * @param aNames An array string to fill with net names. @@ -1229,12 +1302,12 @@ public: /** * Function GetPadFast - * return pad found at \a aPosition on \a aLayer using the fast search method. + * return pad found at \a aPosition on \a aLayerMask using the fast search method. *

* The fast search method only works if the pad list has already been built. *

* @param aPosition A wxPoint object containing the position to hit test. - * @param aLayer A layer or layers to mask the hit test. + * @param aLayerMask A layer or layers to mask the hit test. * @return A pointer to a D_PAD object if found or NULL if not found. */ D_PAD* GetPadFast( const wxPoint& aPosition, LAYER_MSK aLayerMask ); diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index b000a609ca..3324585d52 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -290,15 +290,45 @@ void MODULE::Copy( MODULE* aModule ) } -/** - * Function Draw - * Draws the footprint to the current Device Context - * @param aPanel = draw panel, Used to know the clip box - * @param aDC = Current Device Context - * @param aDrawMode = GR_OR, GR_XOR.. - * @param aOffset = draw offset (usually wxPoint(0,0) - */ -void MODULE::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, const wxPoint& aOffset ) +void MODULE::CopyNetlistSettings( MODULE* aModule ) +{ + // Don't do anything foolish like trying to copy to yourself. + wxCHECK_RET( aModule != NULL && aModule != this, wxT( "Cannot copy to NULL or yourself." ) ); + + // Not sure what to do with the value field. Use netlist for now. + aModule->SetPosition( GetPosition() ); + + if( aModule->GetLayer() != GetLayer() ) + aModule->Flip( aModule->GetPosition() ); + + if( aModule->GetOrientation() != GetOrientation() ) + aModule->Rotate( aModule->GetPosition(), GetOrientation() ); + + aModule->SetLocalSolderMaskMargin( GetLocalSolderMaskMargin() ); + aModule->SetLocalClearance( GetLocalClearance() ); + aModule->SetLocalSolderPasteMargin( GetLocalSolderPasteMargin() ); + aModule->SetLocalSolderPasteMarginRatio( GetLocalSolderPasteMarginRatio() ); + aModule->SetZoneConnection( GetZoneConnection() ); + aModule->SetThermalWidth( GetThermalWidth() ); + aModule->SetThermalGap( GetThermalGap() ); + + for( D_PAD* pad = Pads(); pad; pad = pad->Next() ) + { + D_PAD* newPad = aModule->FindPadByName( pad->GetPadName() ); + + if( newPad ) + pad->CopyNetlistSettings( newPad ); + } + + // Not sure about copying description, keywords, 3D models or any other + // local user changes to footprint. Stick with the new footprint settings + // called out in the footprint loaded in the netlist. + aModule->CalculateBoundingBox(); +} + + +void MODULE::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, + const wxPoint& aOffset ) { if( (m_Flags & DO_NOT_DRAW) || (IsMoving()) ) return; @@ -354,14 +384,6 @@ void MODULE::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, con } -/** - * Function DrawEdgesOnly - * Draws the footprint edges only to the current Device Context - * @param panel = The active Draw Panel (used to know the clip box) - * @param DC = current Device Context - * @param offset = draw offset (usually wxPoint(0,0) - * @param draw_mode = GR_OR, GR_XOR, GR_AND - */ void MODULE::DrawEdgesOnly( EDA_DRAW_PANEL* panel, wxDC* DC, const wxPoint& offset, GR_DRAWMODE draw_mode ) { @@ -687,6 +709,7 @@ EDA_ITEM* MODULE::Clone() const return new MODULE( *this ); } + /* Test for validity of the name in a library of the footprint * ( no spaces, dir separators ... ) * return true if the given name is valid @@ -874,6 +897,7 @@ void MODULE::SetPosition( const wxPoint& newpos ) CalculateBoundingBox(); } + void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector ) { /* Move the reference point of the footprint @@ -932,6 +956,7 @@ void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector ) CalculateBoundingBox(); } + void MODULE::SetOrientation( double newangle ) { double angleChange = newangle - m_Orient; // change in rotation diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 507d38e0b1..a8239fcb05 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -229,6 +229,14 @@ public: /* drawing functions */ + /** + * Function Draw + * draws the footprint to the \a aDC. + * @param aPanel = draw panel, Used to know the clip box + * @param aDC = Current Device Context + * @param aDrawMode = GR_OR, GR_XOR.. + * @param aOffset = draw offset (usually wxPoint(0,0) + */ void Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDC, GR_DRAWMODE aDrawMode, @@ -236,6 +244,14 @@ public: void Draw3D( EDA_3D_CANVAS* glcanvas ); + /** + * Function DrawEdgesOnly + * Draws the footprint edges only to the current Device Context + * @param panel = The active Draw Panel (used to know the clip box) + * @param DC = current Device Context + * @param offset = draw offset (usually wxPoint(0,0) + * @param draw_mode = GR_OR, GR_XOR, GR_AND + */ void DrawEdgesOnly( EDA_DRAW_PANEL* panel, wxDC* DC, const wxPoint& offset, GR_DRAWMODE draw_mode ); @@ -306,7 +322,7 @@ public: /** * Function GetPad - * get a pad at \a aPosition on \a aLayer in the footprint. + * get a pad at \a aPosition on \a aLayerMask in the footprint. * * @param aPosition A wxPoint object containing the position to hit test. * @param aLayerMask A layer or layers to mask the hit test. @@ -361,6 +377,19 @@ public: EDA_ITEM* Clone() const; + /** + * Function CopyNetlistSettings + * copies the netlist settings to \a aModule. + * + * The netlist settings are all of the #MODULE settings not define by a #MODULE in + * a netlist. These setting include position, orientation, local clearances, ets. + * The reference designator, value, path, and physical geometry settings are not + * copied. + * + * @param aModule is the #MODULE to copy the settings to. + */ + void CopyNetlistSettings( MODULE* aModule ); + /** * static function IsLibNameValid * Test for validity of a name of a footprint to be used in a footprint library diff --git a/pcbnew/class_netinfo.h b/pcbnew/class_netinfo.h index 1080ddcbe1..926d5c1c56 100644 --- a/pcbnew/class_netinfo.h +++ b/pcbnew/class_netinfo.h @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2009 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com + * Copyright (C) 1992-2012 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 class_netinfo.h */ @@ -384,6 +408,13 @@ public: */ void SetNetname( const wxString& aNetname ); + /** + * Function GetMsgPanelInfo + * returns the information about the #NETINFO_ITEM in \a aList to display in the + * message panel. + * + * @param aList is the list in which to place the status information. + */ void GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ); }; diff --git a/pcbnew/class_netinfo_item.cpp b/pcbnew/class_netinfo_item.cpp index cb3622ef62..f96132dfc1 100644 --- a/pcbnew/class_netinfo_item.cpp +++ b/pcbnew/class_netinfo_item.cpp @@ -99,13 +99,6 @@ void NETINFO_ITEM::Draw( EDA_DRAW_PANEL* panel, } -/** - * Function DisplayInfo - * has knowledge about the frame and how and where to put status information - * about this object into the frame's message panel. - * Is virtual from EDA_ITEM. - * @param frame A EDA_DRAW_FRAME in which to print status information. - */ void NETINFO_ITEM::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) { int count; diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index ed5e4cf1b3..700ceb841b 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -301,10 +301,6 @@ void D_PAD::SetPadName( const wxString& name ) } -/** - * Function SetNetname - * @param aNetname: the new netname - */ void D_PAD::SetNetname( const wxString& aNetname ) { m_Netname = aNetname; @@ -348,14 +344,23 @@ void D_PAD::Copy( D_PAD* source ) } -/** - * Function GetClearance (virtual) - * returns the clearance in internal units. If \a aItem is not NULL then the - * returned clearance is the greater of this object's clearance and - * aItem's clearance. If \a aItem is NULL, then this object clearance is returned. - * @param aItem is another BOARD_CONNECTED_ITEM or NULL - * @return int - the clearance in internal units. - */ +void D_PAD::CopyNetlistSettings( D_PAD* aPad ) +{ + // Don't do anything foolish like trying to copy to yourself. + wxCHECK_RET( aPad != NULL && aPad != this, wxT( "Cannot copy to NULL or yourself." ) ); + + aPad->SetNetname( GetNetname() ); + + aPad->SetLocalClearance( m_LocalClearance ); + aPad->SetLocalSolderMaskMargin( m_LocalSolderMaskMargin ); + aPad->SetLocalSolderPasteMargin( m_LocalSolderPasteMargin ); + aPad->SetLocalSolderPasteMarginRatio( m_LocalSolderPasteMarginRatio ); + aPad->SetZoneConnection( m_ZoneConnection ); + aPad->SetThermalWidth( m_ThermalWidth ); + aPad->SetThermalGap( m_ThermalGap ); +} + + int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem ) const { // A pad can have specific clearance parameters that @@ -387,15 +392,6 @@ int D_PAD::GetClearance( BOARD_CONNECTED_ITEM* aItem ) const // Mask margins handling: -/** - * Function GetSolderMaskMargin - * @return the margin for the solder mask layer - * usually > 0 (mask shape bigger than pad - * value is - * 1 - the local value - * 2 - if null, the parent footprint value - * 1 - if null, the global value - */ int D_PAD::GetSolderMaskMargin() { int margin = m_LocalSolderMaskMargin; @@ -429,15 +425,6 @@ int D_PAD::GetSolderMaskMargin() } -/** - * Function GetSolderPasteMargin - * @return the margin for the solder mask layer - * usually < 0 (mask shape smaller than pad - * value is - * 1 - the local value - * 2 - if null, the parent footprint value - * 3 - if null, the global value - */ wxSize D_PAD::GetSolderPasteMargin() { int margin = m_LocalSolderPasteMargin; @@ -538,7 +525,7 @@ void D_PAD::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM>& aList ) board = GetBoard(); - aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), + aList.push_back( MSG_PANEL_ITEM( _( "Layer" ), LayerMaskDescribe( board, m_layerMask ), DARKGREEN ) ); aList.push_back( MSG_PANEL_ITEM( ShowPadShape(), ShowPadAttr(), DARKGREEN ) ); @@ -754,6 +741,7 @@ wxString D_PAD::GetSelectMenuText() const return text; } + EDA_ITEM* D_PAD::Clone() const { return new D_PAD( *this ); diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 55387f4378..1c5de49642 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -395,6 +395,18 @@ public: EDA_ITEM* Clone() const; + /** + * Function CopyNetlistSettings + * copies the netlist settings to \a aPad. + * + * The netlist settings are all of the #D_PAD settings not define by a #D_PAD in + * a netlist. These setting include local clearances, net names, etc. The pad + * physical geometry settings are not copied. + * + * @param aPad is the #D_PAD to copy the settings to. + */ + void CopyNetlistSettings( D_PAD* aPad ); + #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const; // overload #endif diff --git a/pcbnew/dialogs/dialog_edit_module_text.cpp b/pcbnew/dialogs/dialog_edit_module_text.cpp index c4630694f6..317f823b7d 100644 --- a/pcbnew/dialogs/dialog_edit_module_text.cpp +++ b/pcbnew/dialogs/dialog_edit_module_text.cpp @@ -1,6 +1,6 @@ /** - * @file dialog_edit_module_text.cpp. - * @brief dialog editor for texts (fields) in footprints + * @file dialog_edit_module_text.cpp + * @brief dialog editor for texts (fields) in footprints. */ /* diff --git a/pcbnew/dialogs/dialog_netlist.cpp b/pcbnew/dialogs/dialog_netlist.cpp index a9ad301876..55b4966004 100644 --- a/pcbnew/dialogs/dialog_netlist.cpp +++ b/pcbnew/dialogs/dialog_netlist.cpp @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include @@ -65,20 +67,20 @@ void PCB_EDIT_FRAME::InstallNetlistFrame( wxDC* DC ) // Save project settings if needed. // Project settings are saved in the corresponding .pro file bool configChanged = lastNetlistName != GetLastNetListRead(); + if( dlg.UseCmpFileForFpNames() != GetUseCmpFileForFpNames() ) { SetUseCmpFileForFpNames( dlg.UseCmpFileForFpNames() ); configChanged = true; } - if( configChanged && - !GetBoard()->GetFileName().IsEmpty() && - IsOK(NULL, _("Project config has changed. Save it ?") ) ) + if( configChanged + && !GetBoard()->GetFileName().IsEmpty() + && IsOK( NULL, _( "The project configuration has changed. Do you want to save it?" ) ) ) { wxFileName fn = GetBoard()->GetFileName(); fn.SetExt( ProjectFileExtension ); - wxGetApp().WriteProjectConfig( fn.GetFullPath(), GROUP, - GetProjectFileParameters() ); + wxGetApp().WriteProjectConfig( fn.GetFullPath(), GROUP, GetProjectFileParameters() ); } } @@ -126,20 +128,50 @@ void DIALOG_NETLIST::OnOpenNetlistClick( wxCommandEvent& event ) void DIALOG_NETLIST::OnReadNetlistFileClick( wxCommandEvent& event ) { - wxString fullNetfileName = m_NetlistFilenameCtrl->GetValue(); - wxString cmpFilename; + wxString msg; + wxString netlistFileName = m_NetlistFilenameCtrl->GetValue(); + wxString cmpFileName; + if( UseCmpFileForFpNames() ) { wxFileName fn = m_NetlistFilenameCtrl->GetValue(); fn.SetExt( ComponentFileExtension ); - cmpFilename = fn.GetFullPath(); + cmpFileName = fn.GetFullPath(); } - m_parent->ReadPcbNetlist( fullNetfileName, cmpFilename, m_MessageWindow, - m_ChangeExistingFootprintCtrl->GetSelection() == 1 ? true : false, - m_DeleteBadTracks->GetSelection() == 1 ? true : false, - m_RemoveExtraFootprintsCtrl->GetSelection() == 1 ? true : false, - m_Select_By_Timestamp->GetSelection() == 1 ? true : false ); + // Give the user a chance to bail out when making changes from a netlist. + if( !m_parent->GetBoard()->IsEmpty() + && !IsOK( NULL, _( "The changes made by reading the netlist cannot be undone. Are you " + "sure you want to read the netlist?" ) ) ) + return; + + wxBusyCursor busy(); + m_MessageWindow->Clear(); + + msg.Printf( _( "Reading netlist file \"%s\".\n" ), GetChars( netlistFileName ) ); + m_MessageWindow->AppendText( msg ); + + if( !cmpFileName.IsEmpty() ) + { + msg.Printf( _( "Using component footprint link file \"%s\".\n" ), GetChars( cmpFileName ) ); + m_MessageWindow->AppendText( msg ); + } + + if( m_Select_By_Timestamp->GetSelection() == 1 ) + { + msg.Printf( _( "Using time stamps to select footprints in file \"%s\".\n" ), + GetChars( cmpFileName ) ); + m_MessageWindow->AppendText( msg ); + } + + WX_TEXT_CTRL_REPORTER reporter( m_MessageWindow ); + + m_parent->ReadPcbNetlist( netlistFileName, cmpFileName, &reporter, + m_ChangeExistingFootprintCtrl->GetSelection() == 1, + m_DeleteBadTracks->GetSelection() == 1, + m_RemoveExtraFootprintsCtrl->GetSelection() == 1, + m_Select_By_Timestamp->GetSelection() == 1, + m_checkDryRun->GetValue() ); } @@ -156,14 +188,18 @@ void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event ) wxArrayString missing; std::vector notInNetlist; wxString netlistFilename = m_NetlistFilenameCtrl->GetValue(); + wxString cmpFilename; - if( ! m_parent->Test_Duplicate_Missing_And_Extra_Footprints( - netlistFilename, duplicate, missing, notInNetlist ) ) + if( UseCmpFileForFpNames() ) { - wxMessageBox( _("Netlist file not found!") ); - return; + wxFileName fn = m_NetlistFilenameCtrl->GetValue(); + fn.SetExt( ComponentFileExtension ); + cmpFilename = fn.GetFullPath(); } + if( !verifyFootprints( netlistFilename, cmpFilename, duplicate, missing, notInNetlist ) ) + return; + #define ERR_CNT_MAX 100 // Max number of errors to output in dialog // to avoid a too long message list @@ -221,24 +257,24 @@ void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event ) // Search for modules found on board but not in net list. if( notInNetlist.size() == 0 ) - list << wxT("

") << _( "No extra modules." ) << wxT("

"); + list << wxT( "

" ) << _( "No extra modules." ) << wxT( "

" ); else { - list << wxT("

") << _( "Not in Netlist:" ) << wxT("

"); + list << wxT( "

" ) << _( "Not in Netlist:" ) << wxT( "

" ); for( unsigned ii = 0; ii < notInNetlist.size(); ii++ ) { MODULE* module = notInNetlist[ii]; if( module->GetReference().IsEmpty() ) - list << wxT("
") << wxT("[noref)"); + list << wxT( "
" ) << wxT( "[noref)" ); else - list << wxT("
") << module->GetReference() ; + list << wxT( "
" ) << module->GetReference() ; - list << wxT(" (") << module->GetValue() << wxT(")"); - list << wxT(" @ "); + list << wxT( " (" ) << module->GetValue() << wxT( ")" ); + list << wxT( " @ " ); list << CoordinateToString( module->GetPosition().x ), - list << wxT(", ") << CoordinateToString( module->GetPosition().y ), + list << wxT( ", " ) << CoordinateToString( module->GetPosition().y ), err_cnt++; if( ERR_CNT_MAX < err_cnt ) @@ -248,13 +284,13 @@ void DIALOG_NETLIST::OnTestFootprintsClick( wxCommandEvent& event ) if( ERR_CNT_MAX < err_cnt ) { - list << wxT("

") + list << wxT( "

" ) << _( "Too many errors: some are skipped" ) - << wxT("

"); + << wxT( "

" ); } HTML_MESSAGE_BOX dlg( this, _( "Check Modules" ) ); - dlg.AddHTML_Text(list); + dlg.AddHTML_Text( list ); dlg.ShowModal(); } @@ -277,3 +313,148 @@ void DIALOG_NETLIST::OnCancelClick( wxCommandEvent& event ) { EndModal( wxID_CANCEL ); } + + +void DIALOG_NETLIST::OnSaveMessagesToFile( wxCommandEvent& aEvent ) +{ + wxFileName fn; + + if( !m_parent->GetLastNetListRead().IsEmpty() ) + { + fn = m_parent->GetLastNetListRead(); + fn.SetExt( wxT( "txt" ) ); + } + else + { + fn.SetPath( wxFileName::GetCwd() ); + } + + wxFileDialog dlg( this, _( "Save contents of message window" ), fn.GetPath(), fn.GetName(), + TextWildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); + + if( dlg.ShowModal() != wxID_OK ) + return; + + fn = dlg.GetPath(); + + if( fn.GetExt().IsEmpty() ) + fn.SetExt( wxT( "txt" ) ); + + wxFile f( fn.GetFullPath(), wxFile::write ); + + if( !f.IsOpened() ) + { + wxString msg; + + msg.Printf( _( "Cannot write message contents to file \"%s\"." ), + GetChars( fn.GetFullPath() ) ); + wxMessageBox( msg, _( "File Write Error" ), wxOK | wxICON_ERROR, this ); + return; + } + + f.Write( m_MessageWindow->GetValue() ); +} + + +void DIALOG_NETLIST::OnUpdateUISaveMessagesToFile( wxUpdateUIEvent& aEvent ) +{ + aEvent.Enable( !m_MessageWindow->IsEmpty() ); +} + + +void DIALOG_NETLIST::OnUpdateUIValidNetlistFile( wxUpdateUIEvent& aEvent ) +{ + aEvent.Enable( !m_NetlistFilenameCtrl->GetValue().IsEmpty() ); +} + + +bool DIALOG_NETLIST::verifyFootprints( const wxString& aNetlistFilename, + const wxString & aCmpFilename, + std::vector< MODULE* >& aDuplicates, + wxArrayString& aMissing, + std::vector< MODULE* >& aNotInNetlist ) +{ + wxString msg; + MODULE* module; + MODULE* nextModule; + NETLIST netlist; + wxBusyCursor dummy; // Shows an hourglass while calculating. + NETLIST_READER* netlistReader; + COMPONENT* component; + + try + { + netlistReader = NETLIST_READER::GetNetlistReader( &netlist, aNetlistFilename, + aCmpFilename ); + + if( netlistReader == NULL ) + { + msg.Printf( _( "Cannot open netlist file \"%s\"." ), GetChars( aNetlistFilename ) ); + wxMessageBox( msg, _( "Netlist Load Error." ), wxOK | wxICON_ERROR ); + return false; + } + + std::auto_ptr< NETLIST_READER > nlr( netlistReader ); + netlistReader->LoadNetlist(); + } + catch( IO_ERROR& ioe ) + { + msg.Printf( _( "Error loading netlist file:\n%s" ), ioe.errorText.GetData() ); + wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR ); + return false; + } + + +#if defined( DEBUG ) + m_MessageWindow->Clear(); + WX_TEXT_CTRL_REPORTER rpt( m_MessageWindow ); + netlist.Show( 0, rpt ); +#endif + + BOARD* pcb = m_parent->GetBoard(); + + // Search for duplicate footprints. + module = pcb->m_Modules; + + for( ; module != NULL; module = module->Next() ) + { + nextModule = module->Next(); + + for( ; nextModule != NULL; nextModule = nextModule->Next() ) + { + if( module->GetReference().CmpNoCase( nextModule->GetReference() ) == 0 ) + { + aDuplicates.push_back( module ); + break; + } + } + } + + // Search for component footprints in the netlist but not on the board. + for( unsigned ii = 0; ii < netlist.GetCount(); ii++ ) + { + component = netlist.GetComponent( ii ); + + module = pcb->FindModuleByReference( component->GetReference() ); + + if( module == NULL ) + { + aMissing.Add( component->GetReference() ); + aMissing.Add( component->GetValue() ); + } + } + + // Search for component footprints found on board but not in netlist. + module = pcb->m_Modules; + + for( ; module != NULL; module = module->Next() ) + { + + component = netlist.GetComponentByReference( module->GetReference() ); + + if( component == NULL ) + aNotInNetlist.push_back( module ); + } + + return true; +} diff --git a/pcbnew/dialogs/dialog_netlist.h b/pcbnew/dialogs/dialog_netlist.h index dce8c83ef5..332403c57f 100644 --- a/pcbnew/dialogs/dialog_netlist.h +++ b/pcbnew/dialogs/dialog_netlist.h @@ -31,31 +31,68 @@ #include +class MODULE; +class NETLIST; + + class DIALOG_NETLIST : public DIALOG_NETLIST_FBP { private: - PCB_EDIT_FRAME * m_parent; - wxDC * m_dc; + PCB_EDIT_FRAME* m_parent; + wxDC* m_dc; public: - DIALOG_NETLIST( PCB_EDIT_FRAME* aParent, wxDC * aDC, - const wxString & aNetlistFullFilename ); + DIALOG_NETLIST( PCB_EDIT_FRAME* aParent, wxDC* aDC, const wxString & aNetlistFullFilename ); ~DIALOG_NETLIST() {}; - // return true if the user choice is tu use the .cmp file - // created by CvPcb to know footprin names associated to components - // and false tu use the netlist only + + // return true if the user choice is to use the .cmp file + // created by CvPcb to know footprint names associated to components + // and false to use the netlist only bool UseCmpFileForFpNames() { return m_cmpNameSourceOpt->GetSelection() == 1; } private: + /** + * Function verifyFootprints + * compares the netlist to the board and builds a list of duplicate, missing, and + * extra footprints. + * + * @param aNetlistFilename the netlist filename. + * @param aCmpFilename the component link filename. + * @param aDuplicate the list of duplicate modules to populate + * @param aMissing the list of missing module references and values to populate. For + * each missing item, the first string is the reference designator and + * the second is the value. + * @param aNotInNetlist is the list of component footprint found in the netlist but not on + * the board. + * @return true if no errors occurred while reading the netlist. Otherwise false. + */ + bool verifyFootprints( const wxString& aNetlistFilename, + const wxString& aCmpFilename, + std::vector< MODULE* >& aDuplicate, + wxArrayString& aMissing, + std::vector< MODULE* >& aNotInNetlist ); + + /** + * Function loadFootprints + * loads the footprints for each #COMPONENT in \a aNetlist from the list of libraries. + * + * @param aNetlist is the netlist of components to load the footprints into. + */ + void loadFootprints( NETLIST& aNetlist ); + // Virtual event handlers: void OnOpenNetlistClick( wxCommandEvent& event ); void OnReadNetlistFileClick( wxCommandEvent& event ); void OnTestFootprintsClick( wxCommandEvent& event ); void OnCompileRatsnestClick( wxCommandEvent& event ); void OnCancelClick( wxCommandEvent& event ); + void OnSaveMessagesToFile( wxCommandEvent& aEvent ); + + void OnUpdateUISaveMessagesToFile( wxUpdateUIEvent& aEvent ); + void OnUpdateUIValidNetlistFile( wxUpdateUIEvent& aEvent ); }; diff --git a/pcbnew/dialogs/dialog_netlist_fbp.cpp b/pcbnew/dialogs/dialog_netlist_fbp.cpp index 8b84a06fb8..c6f1d115a5 100644 --- a/pcbnew/dialogs/dialog_netlist_fbp.cpp +++ b/pcbnew/dialogs/dialog_netlist_fbp.cpp @@ -24,7 +24,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w wxString m_Select_By_TimestampChoices[] = { _("Reference"), _("Timestamp") }; int m_Select_By_TimestampNChoices = sizeof( m_Select_By_TimestampChoices ) / sizeof( wxString ); - m_Select_By_Timestamp = new wxRadioBox( this, wxID_ANY, _("Module Selection:"), wxDefaultPosition, wxDefaultSize, m_Select_By_TimestampNChoices, m_Select_By_TimestampChoices, 1, wxRA_SPECIFY_COLS ); + m_Select_By_Timestamp = new wxRadioBox( this, wxID_ANY, _("Module Selection"), wxDefaultPosition, wxDefaultSize, m_Select_By_TimestampNChoices, m_Select_By_TimestampChoices, 1, wxRA_SPECIFY_COLS ); m_Select_By_Timestamp->SetSelection( 0 ); m_Select_By_Timestamp->SetToolTip( _("Select how footprints are recognized:\nby their reference (U1, R3...) (normal setting)\nor their time stamp (special setting after a full schematic reannotation)") ); @@ -32,7 +32,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w wxString m_cmpNameSourceOptChoices[] = { _("From netlist"), _("From separate .cmp file") }; int m_cmpNameSourceOptNChoices = sizeof( m_cmpNameSourceOptChoices ) / sizeof( wxString ); - m_cmpNameSourceOpt = new wxRadioBox( this, wxID_ANY, _("Module Name Source:"), wxDefaultPosition, wxDefaultSize, m_cmpNameSourceOptNChoices, m_cmpNameSourceOptChoices, 1, wxRA_SPECIFY_COLS ); + m_cmpNameSourceOpt = new wxRadioBox( this, wxID_ANY, _("Module Name Source"), wxDefaultPosition, wxDefaultSize, m_cmpNameSourceOptNChoices, m_cmpNameSourceOptChoices, 1, wxRA_SPECIFY_COLS ); m_cmpNameSourceOpt->SetSelection( 0 ); m_cmpNameSourceOpt->SetToolTip( _("Source of footprints names for component:\n- the netlist (if you have filled the footprint field of each component in schematic)\n- the .cmp file created by CvPcb") ); @@ -40,7 +40,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w wxString m_ChangeExistingFootprintCtrlChoices[] = { _("Keep"), _("Change") }; int m_ChangeExistingFootprintCtrlNChoices = sizeof( m_ChangeExistingFootprintCtrlChoices ) / sizeof( wxString ); - m_ChangeExistingFootprintCtrl = new wxRadioBox( this, wxID_ANY, _("Exchange Module:"), wxDefaultPosition, wxDefaultSize, m_ChangeExistingFootprintCtrlNChoices, m_ChangeExistingFootprintCtrlChoices, 1, wxRA_SPECIFY_COLS ); + m_ChangeExistingFootprintCtrl = new wxRadioBox( this, wxID_ANY, _("Exchange Module"), wxDefaultPosition, wxDefaultSize, m_ChangeExistingFootprintCtrlNChoices, m_ChangeExistingFootprintCtrlChoices, 1, wxRA_SPECIFY_COLS ); m_ChangeExistingFootprintCtrl->SetSelection( 0 ); m_ChangeExistingFootprintCtrl->SetToolTip( _("Keep or change an existing footprint when the netlist gives a different footprint") ); @@ -54,7 +54,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w wxString m_DeleteBadTracksChoices[] = { _("Keep"), _("Delete") }; int m_DeleteBadTracksNChoices = sizeof( m_DeleteBadTracksChoices ) / sizeof( wxString ); - m_DeleteBadTracks = new wxRadioBox( this, wxID_ANY, _("Bad Tracks Deletion:"), wxDefaultPosition, wxDefaultSize, m_DeleteBadTracksNChoices, m_DeleteBadTracksChoices, 1, wxRA_SPECIFY_COLS ); + m_DeleteBadTracks = new wxRadioBox( this, wxID_ANY, _("Unconnected Tracks"), wxDefaultPosition, wxDefaultSize, m_DeleteBadTracksNChoices, m_DeleteBadTracksChoices, 1, wxRA_SPECIFY_COLS ); m_DeleteBadTracks->SetSelection( 0 ); m_DeleteBadTracks->SetToolTip( _("Keep or delete bad tracks after a netlist change") ); @@ -74,7 +74,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bRightSizerButtons; bRightSizerButtons = new wxBoxSizer( wxVERTICAL ); - m_buttonBrowse = new wxButton( this, ID_OPEN_NELIST, _("Browse Netlist Files"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonBrowse = new wxButton( this, ID_OPEN_NELIST, _("Open Netlist File"), wxDefaultPosition, wxDefaultSize, 0 ); bRightSizerButtons->Add( m_buttonBrowse, 0, wxEXPAND|wxALL, 5 ); m_buttonRead = new wxButton( this, ID_READ_NETLIST_FILE, _("Read Current Netlist"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -83,7 +83,7 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w bRightSizerButtons->Add( m_buttonRead, 0, wxEXPAND|wxALL, 5 ); - m_buttonFPTest = new wxButton( this, ID_TEST_NETLIST, _("Footprints Test"), wxDefaultPosition, wxDefaultSize, 0 ); + m_buttonFPTest = new wxButton( this, ID_TEST_NETLIST, _("Test Footprints"), wxDefaultPosition, wxDefaultSize, 0 ); m_buttonFPTest->SetToolTip( _("Read the current neltist file and list missing and extra footprints") ); bRightSizerButtons->Add( m_buttonFPTest, 0, wxEXPAND|wxALL, 5 ); @@ -93,15 +93,27 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w bRightSizerButtons->Add( m_buttonRebild, 0, wxEXPAND|wxALL, 5 ); + m_buttonSaveMessages = new wxButton( this, wxID_ANY, _("Save Messages to File"), wxDefaultPosition, wxDefaultSize, 0 ); + bRightSizerButtons->Add( m_buttonSaveMessages, 0, wxALL|wxEXPAND, 5 ); + m_buttonClose = new wxButton( this, wxID_CANCEL, _("Close"), wxDefaultPosition, wxDefaultSize, 0 ); bRightSizerButtons->Add( m_buttonClose, 0, wxALL|wxEXPAND, 5 ); - bUpperSizer->Add( bRightSizerButtons, 0, wxALIGN_CENTER_VERTICAL, 5 ); + bUpperSizer->Add( bRightSizerButtons, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxTOP, 5 ); bMainSizer->Add( bUpperSizer, 0, wxEXPAND|wxLEFT|wxRIGHT|wxTOP, 5 ); + wxBoxSizer* bCenterSizer; + bCenterSizer = new wxBoxSizer( wxVERTICAL ); + + m_checkDryRun = new wxCheckBox( this, wxID_ANY, _("Only report changes in message panel"), wxDefaultPosition, wxDefaultSize, 0 ); + bCenterSizer->Add( m_checkDryRun, 0, wxALL, 5 ); + + + bMainSizer->Add( bCenterSizer, 0, wxALL|wxEXPAND, 5 ); + m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); bMainSizer->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 ); @@ -137,8 +149,13 @@ DIALOG_NETLIST_FBP::DIALOG_NETLIST_FBP( wxWindow* parent, wxWindowID id, const w // Connect Events m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnOpenNetlistClick ), NULL, this ); m_buttonRead->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnReadNetlistFileClick ), NULL, this ); + m_buttonRead->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); m_buttonFPTest->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnTestFootprintsClick ), NULL, this ); + m_buttonFPTest->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); m_buttonRebild->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnCompileRatsnestClick ), NULL, this ); + m_buttonRebild->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); + m_buttonSaveMessages->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnSaveMessagesToFile ), NULL, this ); + m_buttonSaveMessages->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUISaveMessagesToFile ), NULL, this ); m_buttonClose->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnCancelClick ), NULL, this ); } @@ -147,8 +164,13 @@ DIALOG_NETLIST_FBP::~DIALOG_NETLIST_FBP() // Disconnect Events m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnOpenNetlistClick ), NULL, this ); m_buttonRead->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnReadNetlistFileClick ), NULL, this ); + m_buttonRead->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); m_buttonFPTest->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnTestFootprintsClick ), NULL, this ); + m_buttonFPTest->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); m_buttonRebild->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnCompileRatsnestClick ), NULL, this ); + m_buttonRebild->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUIValidNetlistFile ), NULL, this ); + m_buttonSaveMessages->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnSaveMessagesToFile ), NULL, this ); + m_buttonSaveMessages->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_NETLIST_FBP::OnUpdateUISaveMessagesToFile ), NULL, this ); m_buttonClose->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_NETLIST_FBP::OnCancelClick ), NULL, this ); } diff --git a/pcbnew/dialogs/dialog_netlist_fbp.fbp b/pcbnew/dialogs/dialog_netlist_fbp.fbp index d4dee8a418..2336b347e7 100644 --- a/pcbnew/dialogs/dialog_netlist_fbp.fbp +++ b/pcbnew/dialogs/dialog_netlist_fbp.fbp @@ -142,7 +142,7 @@ 0 0 wxID_ANY - Module Selection: + Module Selection 1 0 @@ -232,7 +232,7 @@ 0 0 wxID_ANY - Module Name Source: + Module Name Source 1 0 @@ -322,7 +322,7 @@ 0 0 wxID_ANY - Exchange Module: + Exchange Module 1 0 @@ -423,7 +423,7 @@ 0 0 wxID_ANY - Bad Tracks Deletion: + Unconnected Tracks 1 0 @@ -574,7 +574,7 @@ 5 - wxALIGN_CENTER_VERTICAL + wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxTOP 0 @@ -614,7 +614,7 @@ 0 0 ID_OPEN_NELIST - Browse Netlist Files + Open Netlist File 0 @@ -754,7 +754,7 @@ - + OnUpdateUIValidNetlistFile @@ -790,7 +790,7 @@ 0 0 ID_TEST_NETLIST - Footprints Test + Test Footprints 0 @@ -842,7 +842,7 @@ - + OnUpdateUIValidNetlistFile @@ -930,7 +930,95 @@ - + OnUpdateUIValidNetlistFile + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Save Messages to File + + 0 + + + 0 + + 1 + m_buttonSaveMessages + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnSaveMessagesToFile + + + + + + + + + + + + + + + + + + + + + + + OnUpdateUISaveMessagesToFile @@ -1025,6 +1113,105 @@ + + 5 + wxALL|wxEXPAND + 0 + + + bCenterSizer + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Only report changes in message panel + + 0 + + + 0 + + 1 + m_checkDryRun + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 wxEXPAND | wxALL diff --git a/pcbnew/dialogs/dialog_netlist_fbp.h b/pcbnew/dialogs/dialog_netlist_fbp.h index acf63e437f..271709b822 100644 --- a/pcbnew/dialogs/dialog_netlist_fbp.h +++ b/pcbnew/dialogs/dialog_netlist_fbp.h @@ -22,6 +22,7 @@ class DIALOG_SHIM; #include #include #include +#include #include #include #include @@ -54,7 +55,9 @@ class DIALOG_NETLIST_FBP : public DIALOG_SHIM wxButton* m_buttonRead; wxButton* m_buttonFPTest; wxButton* m_buttonRebild; + wxButton* m_buttonSaveMessages; wxButton* m_buttonClose; + wxCheckBox* m_checkDryRun; wxStaticLine* m_staticline1; wxStaticText* m_staticTextNetfilename; wxTextCtrl* m_NetlistFilenameCtrl; @@ -64,8 +67,11 @@ class DIALOG_NETLIST_FBP : public DIALOG_SHIM // Virtual event handlers, overide them in your derived class virtual void OnOpenNetlistClick( wxCommandEvent& event ) { event.Skip(); } virtual void OnReadNetlistFileClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnUpdateUIValidNetlistFile( wxUpdateUIEvent& event ) { event.Skip(); } virtual void OnTestFootprintsClick( wxCommandEvent& event ) { event.Skip(); } virtual void OnCompileRatsnestClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnSaveMessagesToFile( wxCommandEvent& event ) { event.Skip(); } + virtual void OnUpdateUISaveMessagesToFile( wxUpdateUIEvent& event ) { event.Skip(); } virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } diff --git a/pcbnew/dialogs/dialog_orient_footprints.cpp b/pcbnew/dialogs/dialog_orient_footprints.cpp index 01cae40e27..bffc487ee8 100644 --- a/pcbnew/dialogs/dialog_orient_footprints.cpp +++ b/pcbnew/dialogs/dialog_orient_footprints.cpp @@ -1,5 +1,5 @@ /** - * @file DIALOG_ORIENT_FOOTPRINTS.cpp + * @file dialog_orient_footprints.cpp */ /* * This program source code file is part of KiCad, a free EDA CAD application. @@ -58,10 +58,12 @@ public: { return m_ApplyToLocked->IsChecked(); } + int GetOrientation() { return newOrientation; } + wxString GetFilter() { return m_FilterPattern->GetValue(); @@ -73,6 +75,8 @@ private: void OnOkClick( wxCommandEvent& event ); void OnCancelClick( wxCommandEvent& event ); }; + + int DIALOG_ORIENT_FOOTPRINTS::newOrientation = 0; @@ -81,23 +85,18 @@ DIALOG_ORIENT_FOOTPRINTS::DIALOG_ORIENT_FOOTPRINTS( PCB_EDIT_FRAME* parent ) { m_Parent = parent; wxString txt; - txt.Printf(wxT("%g"), (double) newOrientation/10); - m_OrientationCtrl->SetValue(txt); - SetFocus( ); - GetSizer()->SetSizeHints(this); + txt.Printf( wxT( "%g" ), (double) newOrientation/10 ); + m_OrientationCtrl->SetValue( txt ); + SetFocus(); + GetSizer()->SetSizeHints( this ); Centre(); } -/****************************************************************/ void PCB_EDIT_FRAME::OnOrientFootprints( wxCommandEvent& event ) -/****************************************************************/ -/** - * Function OnOrientFootprints - * install the dialog box for the comman Orient Footprints - */ { - DIALOG_ORIENT_FOOTPRINTS dlg(this); + DIALOG_ORIENT_FOOTPRINTS dlg( this ); + if( dlg.ShowModal() != wxID_OK ) return; @@ -111,23 +110,13 @@ void PCB_EDIT_FRAME::OnOrientFootprints( wxCommandEvent& event ) } -/*******************************************************************/ -bool PCB_EDIT_FRAME::ReOrientModules( const wxString& ModuleMask, - int Orient, bool include_fixe ) -/*******************************************************************/ -/** - * Function ReOrientModules - * Set the orientation of footprints - * @param ModuleMask = mask (wildcard allowed) selection - * @param Orient = new orientation - * @param include_fixe = true to orient locked footprints - * @return true if some footprints modified, false if no change - */ +bool PCB_EDIT_FRAME::ReOrientModules( const wxString& ModuleMask, int Orient, bool include_fixe ) { wxString line; bool modified = false; line.Printf( _( "OK to set footprints orientation to %.1f degrees ?" ), (double)Orient / 10 ); + if( !IsOK( this, line ) ) return false; @@ -155,13 +144,13 @@ void DIALOG_ORIENT_FOOTPRINTS::OnOkClick( wxCommandEvent& event ) double d_orient; wxString text = m_OrientationCtrl->GetValue(); - if ( ! text.ToDouble(&d_orient) ) + if ( ! text.ToDouble( &d_orient ) ) { - DisplayError(this, _("Bad value for footprints orientation")); + DisplayError( this, _( "Bad value for footprints orientation" ) ); return; } - newOrientation = KiROUND(d_orient * 10); + newOrientation = KiROUND( d_orient * 10 ); NORMALIZE_ANGLE_180( newOrientation ); EndModal( wxID_OK ); } diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp index 6cecfca528..4046ee200f 100644 --- a/pcbnew/dialogs/dialog_pad_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_properties.cpp @@ -1,6 +1,6 @@ /** - * @file dialog_pad_properties.cpp. - * @brief Pad editing functions and dialog pad editor + * @file dialog_pad_properties.cpp + * @brief Pad editing functions and dialog pad editor. */ /* diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 20ac9c0b74..f4d4070605 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -132,7 +132,7 @@ wxString PCB_BASE_FRAME::SelectFootprintFromLibBrowser( void ) wxMilliSleep( 50 ); } - // Returnd the full fp name, i.e. the lib name and th fp name, + // Returns the full fp name, i.e. the lib name and th fp name, // separated by a '/' // (/ is now an illegal char in fp names) wxString fpname = viewer->GetSelectedLibraryFullName(); @@ -269,15 +269,6 @@ MODULE* PCB_BASE_FRAME::Load_Module_From_Library( const wxString& aLibrary, } -/* scans active libraries to find and load aFootprintName. - * If found the module is added to the BOARD, just for good measure. - * aLibraryPath is the full/short name of the library. - * if empty, search in all libraries - * aFootprintName is the footprint to load - * aDisplayError = true to display an error message if any. - * - * return a pointer to the new module, or NULL - */ MODULE* PCB_BASE_FRAME::GetModuleLibrary( const wxString& aLibraryPath, const wxString& aFootprintName, bool aDisplayError ) @@ -289,12 +280,6 @@ MODULE* PCB_BASE_FRAME::GetModuleLibrary( const wxString& aLibraryPath, } -/* loads aFootprintName from aLibraryPath. - * If found the module is added to the BOARD, just for good measure. - * - * aLibraryPath - the full filename or the short name of the library to read. - * if it is a short name, the file is searched in all library valid paths - */ MODULE* PCB_BASE_FRAME::loadFootprintFromLibrary( const wxString& aLibraryPath, const wxString& aFootprintName, bool aDisplayError, @@ -337,10 +322,6 @@ MODULE* PCB_BASE_FRAME::loadFootprintFromLibrary( const wxString& aLibraryPath, } -/* Explore the libraries list and - * loads aFootprintName from the first library it is found - * If found add the module is also added to the BOARD, just for good measure. - */ MODULE* PCB_BASE_FRAME::loadFootprintFromLibraries( const wxString& aFootprintName, bool aDisplayError ) { @@ -399,6 +380,35 @@ MODULE* PCB_BASE_FRAME::loadFootprintFromLibraries( { DisplayError( this, ioe.errorText ); } + + return NULL; +} + + +MODULE* PCB_BASE_FRAME::loadFootprint( const wxString& aFootprintName ) + throw( IO_ERROR, PARSE_ERROR ) +{ + wxString libPath; + wxFileName fn; + MODULE* footprint; + + PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::LEGACY ) ); + + for( unsigned ii = 0; ii < g_LibraryNames.GetCount(); ii++ ) + { + fn = wxFileName( wxEmptyString, g_LibraryNames[ii], LegacyFootprintLibPathExtension ); + + libPath = wxGetApp().FindLibraryPath( fn ); + + if( !libPath ) + continue; + + footprint = pi->FootprintLoad( libPath, aFootprintName ); + + if( footprint ) + return footprint; + } + return NULL; } @@ -486,9 +496,6 @@ wxString PCB_BASE_FRAME::Select_1_Module_From_List( EDA_DRAW_FRAME* aWindow, } -/* Find and display the doc Component Name - * The list of doc is pointed to by mlist. - */ static void DisplayCmpDoc( wxString& Name ) { FOOTPRINT_INFO* module_info = MList.GetModuleInfo( Name ); diff --git a/pcbnew/muonde.cpp b/pcbnew/muonde.cpp index 9092563ab2..42a6bd3f3b 100644 --- a/pcbnew/muonde.cpp +++ b/pcbnew/muonde.cpp @@ -688,40 +688,40 @@ MODULE* PCB_EDIT_FRAME::Create_MuWaveComponent( int shape_type ) break; case 2: // Arc Stub created by a polygonal approach: + { + EDGE_MODULE* edge = new EDGE_MODULE( module ); + module->GraphicalItems().PushFront( edge ); + + edge->SetShape( S_POLYGON ); + edge->SetLayer( LAYER_N_FRONT ); + + int numPoints = angle / 50 + 3; // Note: angles are in 0.1 degrees + std::vector polyPoints = edge->GetPolyPoints(); + polyPoints.reserve( numPoints ); + + edge->m_Start0.y = -pad->GetSize().y / 2; + + polyPoints.push_back( wxPoint( 0, 0 ) ); + + int theta = -angle / 2; + + for( int ii = 1; iiGraphicalItems().PushFront( edge ); + wxPoint pt( 0, -gap_size ); - edge->SetShape( S_POLYGON ); - edge->SetLayer( LAYER_N_FRONT ); + RotatePoint( &pt.x, &pt.y, theta ); - int numPoints = angle / 50 + 3; // Note: angles are in 0.1 degrees - std::vector polyPoints = edge->GetPolyPoints(); - polyPoints.reserve( numPoints ); + polyPoints.push_back( pt ); - edge->m_Start0.y = -pad->GetSize().y / 2; + theta += 50; - polyPoints.push_back( wxPoint( 0, 0 ) ); - - int theta = -angle / 2; - - for( int ii = 1; ii angle / 2 ) - theta = angle / 2; - } - - // Close the polygon: - polyPoints.push_back( polyPoints[0] ); + if( theta > angle / 2 ) + theta = angle / 2; } + + // Close the polygon: + polyPoints.push_back( polyPoints[0] ); + } break; default: diff --git a/pcbnew/netlist.cpp b/pcbnew/netlist.cpp index 590905e98e..978cabd23c 100644 --- a/pcbnew/netlist.cpp +++ b/pcbnew/netlist.cpp @@ -25,184 +25,96 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -/* - * Functions to read a netlist: - * - Load new footprints and initialize net info - * - Test for missing or extra footprints - * - Recalculate full connectivity info - * - * Important remark: - * When reading a netlist, Pcbnew must identify existing footprints (link - * between existing footprints an components in netlist) - * This identification can be made from 2 fields: - * - The reference (U2, R5 ..): this is the normal mode - * - The Time Stamp : useful after a full schematic - * reannotation because references can be changed for the component linked to its footprint. - * So when reading a netlist, ReadPcbNetlist() can use references or time stamps - * to identify footprints on board and the corresponding component in schematic. - * If we want to fully reannotate a schematic this sequence must be used - * 1 - SAVE your board !!! - * 2 - Create and read the netlist (to ensure all info is correct, mainly - * references and time stamp) - * 3 - Reannotate the schematic (references will be changed, but not time stamps ) - * 4 - Recreate and read the new netlist using the Time Stamp identification - * (that reinit the new references) - */ - - - #include +#include #include #include #include #include +#include +#include +#include +#include + #include #include #include -#include -#include +#include #include -/** - * Function OpenNetlistFile - * used to open a netlist file - */ -static FILE* OpenNetlistFile( const wxString& aFullFileName ) -{ - if( aFullFileName.IsEmpty() ) - return NULL; // No filename: exit - FILE* file = wxFopen( aFullFileName, wxT( "rt" ) ); - - if( file == NULL ) - { - wxString msg; - msg.Printf( _( "Netlist file <%s> not found" ), GetChars( aFullFileName ) ); - wxMessageBox( msg ); - } - - return file; -} - - - -/* Update footprints (load missing footprints and delete on request extra footprints) - * Update connectivity info ( Net Name list ) - * Update Reference, value and "TIME STAMP" - * param aNetlistFullFilename = netlist file name (*.net) - * param aCmpFullFileName = cmp/footprint list file name (*.cmp) if not found, - * param aMessageWindow = a wxTextCtrl to print messages (can be NULL). - * param aChangeFootprint = true to change existing footprints - * when the netlist gives a different footprint. - * false to keep existing footprints - * param aDeleteBadTracks - true to erase erroneous tracks after updating connectivity info. - * param aDeleteExtraFootprints - true to remove unlocked footprints found on board but not - * in netlist. - * param aSelect_By_Timestamp - true to use schematic timestamps instead of schematic references - * to identify footprints on board - * (Must be used after a full reannotation in schematic). - * param aUseCmpFileForFootprintsNames = false to use only the netlist to know the - * fontprint names of each component. - * = true to use the .cmp file created by CvPcb - * return true if Ok - */ -bool PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFullFilename, - const wxString& aCmpFullFileName, - wxTextCtrl* aMessageWindow, - bool aChangeFootprint, - bool aDeleteBadTracks, +void PCB_EDIT_FRAME::ReadPcbNetlist( const wxString& aNetlistFileName, + const wxString& aCmpFileName, + REPORTER* aReporter, + bool aChangeFootprints, + bool aDeleteUnconnectedTracks, bool aDeleteExtraFootprints, - bool aSelect_By_Timestamp ) + bool aSelectByTimeStamp, + bool aIsDryRun ) { - FILE* netfile = OpenNetlistFile( aNetlistFullFilename ); + wxString msg; + NETLIST netlist; + NETLIST_READER* netlistReader; - if( !netfile ) - return false; - - SetLastNetListRead( aNetlistFullFilename ); - bool useCmpfile = !aCmpFullFileName.IsEmpty() && wxFileExists( aCmpFullFileName ); - - if( aMessageWindow ) + try { - wxString msg; - msg.Printf( _( "Reading Netlist <%s>" ), GetChars( aNetlistFullFilename ) ); - aMessageWindow->AppendText( msg + wxT( "\n" ) ); + netlistReader = NETLIST_READER::GetNetlistReader( &netlist, aNetlistFileName, + aCmpFileName ); - if( useCmpfile ) + if( netlistReader == NULL ) { - msg.Printf( _( "Using component/footprint link file <%s>" ), - GetChars( aCmpFullFileName ) ); - aMessageWindow->AppendText( msg + wxT( "\n" ) ); + msg.Printf( _( "Cannot open netlist file \"%s\"." ), GetChars( aNetlistFileName ) ); + wxMessageBox( msg, _( "Netlist Load Error." ), wxOK | wxICON_ERROR, this ); + return; } - if( aSelect_By_Timestamp ) - { - msg.Printf( _( "Using time stamp selection" ), - GetChars( aCmpFullFileName ) ); - aMessageWindow->AppendText( msg + wxT( "\n" ) ); - } + std::auto_ptr< NETLIST_READER > nlr( netlistReader ); + SetLastNetListRead( aNetlistFileName ); + netlistReader->LoadNetlist(); + loadFootprints( netlist, aReporter ); } + catch( IO_ERROR& ioe ) + { + msg = wxString::Format( _( "Error loading netlist.\n%s" ), ioe.errorText.GetData() ); + wxMessageBox( msg, _( "Netlist Load Error" ), wxOK | wxICON_ERROR ); + return; + } + + netlist.SetIsDryRun( aIsDryRun ); + netlist.SetFindByTimeStamp( aSelectByTimeStamp ); + netlist.SetDeleteExtraFootprints( aDeleteExtraFootprints ); + netlist.SetReplaceFootprints( aChangeFootprints ); // Clear undo and redo lists to avoid inconsistencies between lists - GetScreen()->ClearUndoRedoList(); + if( !netlist.IsDryRun() ) + GetScreen()->ClearUndoRedoList(); + + netlist.SortByReference(); + GetBoard()->ReplaceNetlist( netlist, aReporter ); + + // If it was a dry run, nothing has changed so we're done. + if( netlist.IsDryRun() ) + return; OnModify(); - // Clear flags and pointers to avoid inconsistencies - GetBoard()->m_Status_Pcb = 0; SetCurItem( NULL ); - wxBusyCursor dummy; // Shows an hourglass while calculating - - NETLIST_READER netList_Reader( this, aMessageWindow ); - netList_Reader.m_UseTimeStamp = aSelect_By_Timestamp; - netList_Reader.m_ChangeFootprints = aChangeFootprint; - netList_Reader.m_UseCmpFile = useCmpfile; - netList_Reader.SetFilesnames( aNetlistFullFilename, aCmpFullFileName ); - - // True to read footprint filters section: true for CvPcb, false for Pcbnew - netList_Reader.ReadLibpartSectionSetOpt( false ); - - bool success = netList_Reader.ReadNetList( netfile ); - - if( !success ) + if( aDeleteUnconnectedTracks && GetBoard()->m_Track ) { - wxMessageBox( _("Netlist read error") ); - return false; - } - - // Delete footprints not found in netlist: - if( aDeleteExtraFootprints ) - { - if( IsOK( NULL, - _( "OK to delete not locked footprints not found in netlist?" ) ) ) - netList_Reader.RemoveExtraFootprints(); + // Remove erroneous tracks. This should probably pushed down to the #BOARD object. + RemoveMisConnectedTracks(); } // Rebuild the board connectivity: Compile_Ratsnest( NULL, true ); - - if( aDeleteBadTracks && GetBoard()->m_Track ) - { - // Remove erroneous tracks - if( RemoveMisConnectedTracks() ) - Compile_Ratsnest( NULL, true ); - } - SetMsgPanel( GetBoard() ); m_canvas->Refresh(); - - return true; } -/** - * build and shows a list of existing modules on board - * The user can select a module from this list - * @return a pointer to the selected module or NULL - */ -MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void ) +MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName() { MODULE* Module; @@ -219,9 +131,9 @@ MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void ) listnames.Add( Module->GetReference() ); wxArrayString headers; - headers.Add( wxT("Module") ); + headers.Add( wxT( "Module" ) ); std::vector itemsToDisplay; - + // Conversion from wxArrayString to vector of ArrayString for( unsigned i = 0; i < listnames.GetCount(); i++ ) { @@ -229,6 +141,7 @@ MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void ) item.Add( listnames[i] ); itemsToDisplay.push_back( item ); } + EDA_LIST_DIALOG dlg( this, _( "Components" ), headers, itemsToDisplay, wxEmptyString ); if( dlg.ShowModal() != wxID_OK ) @@ -247,84 +160,70 @@ MODULE* PCB_EDIT_FRAME::ListAndSelectModuleName( void ) } -/* - * Function Test_Duplicate_Missing_And_Extra_Footprints - * Build a list of duplicate, missing and extra footprints - * from the current board and a netlist netlist : - * Shows 3 lists: - * 1 - duplicate footprints on board - * 2 - missing footprints (found in netlist but not on board) - * 3 - footprints not in netlist but on board - * param aFilename = the full filename netlist - * param aDuplicate = the list of duplicate modules to populate - * param aMissing = the list of missing module references and values - * to populate. For each missing item, the first string is the ref, - * the second is the value. - * param aNotInNetlist = the list of not-in-netlist modules to populate - */ -bool PCB_EDIT_FRAME::Test_Duplicate_Missing_And_Extra_Footprints( - const wxString& aFilename, - std::vector & aDuplicate, - wxArrayString& aMissing, - std::vector & aNotInNetlist ) +void PCB_EDIT_FRAME::loadFootprints( NETLIST& aNetlist, REPORTER* aReporter ) + throw( IO_ERROR, PARSE_ERROR ) { - FILE* netfile = OpenNetlistFile( aFilename ); - if( !netfile ) - return false; + wxString msg; + wxString lastFootprintLibName; + COMPONENT* component; + MODULE* module; - // Build the list of references of the net list modules. - NETLIST_READER netList_Reader( this ); - netList_Reader.SetFilesnames( aFilename, wxEmptyString ); - netList_Reader.BuildModuleListOnlySetOpt( true ); - if( ! netList_Reader.ReadNetList( netfile ) ) - return false; // error + if( aNetlist.IsEmpty() ) + return; - COMPONENT_INFO_LIST& moduleInfoList = netList_Reader.GetComponentInfoList(); + aNetlist.SortByFootprintLibName(); - // Search for duplicate footprints. - MODULE* module = GetBoard()->m_Modules; + wxString libPath; + wxFileName fn; - for( ; module != NULL; module = module->Next() ) + PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::LEGACY ) ); + + for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ ) { - MODULE* altmodule = module->Next(); + component = aNetlist.GetComponent( ii ); - for( ; altmodule != NULL; altmodule = altmodule->Next() ) + if( ii == 0 || component->GetFootprintLibName() != lastFootprintLibName ) { - if( module->GetReference().CmpNoCase( altmodule->GetReference() ) == 0 ) + module = NULL; + + for( unsigned ii = 0; ii < g_LibraryNames.GetCount(); ii++ ) { - aDuplicate.push_back( module ); - break; + fn = wxFileName( wxEmptyString, g_LibraryNames[ii], + LegacyFootprintLibPathExtension ); + + libPath = wxGetApp().FindLibraryPath( fn ); + + if( !libPath ) + continue; + + module = pi->FootprintLoad( libPath, component->GetFootprintLibName() ); + + if( module ) + { + lastFootprintLibName = component->GetFootprintLibName(); + break; + } + } + + if( module == NULL ) + { + wxString msg; + msg.Printf( _( "Component `%s` footprint <%s> was not found in any libraries." ), + GetChars( component->GetReference() ), + GetChars( component->GetFootprintLibName() ) ); + THROW_IO_ERROR( msg ); } } - } - - // Search for missing modules on board. - for( unsigned ii = 0; ii < moduleInfoList.size(); ii++ ) - { - COMPONENT_INFO* cmp_info = moduleInfoList[ii]; - module = GetBoard()->FindModuleByReference( cmp_info->m_Reference ); - if( module == NULL ) // Module missing, not found in board + else { - aMissing.Add( cmp_info->m_Reference ); - aMissing.Add( cmp_info->m_Value ); - } - } + // Footprint already loaded from a library, duplicate it (faster) + if( module == NULL ) + continue; // Module does not exist in any library. - // Search for modules found on board but not in net list. - module = GetBoard()->m_Modules; - for( ; module != NULL; module = module->Next() ) - { - unsigned ii; - for( ii = 0; ii < moduleInfoList.size(); ii++ ) - { - COMPONENT_INFO* cmp_info = moduleInfoList[ii]; - if( module->GetReference().CmpNoCase( cmp_info->m_Reference ) == 0 ) - break; // Module is in net list. + module = new MODULE( *module ); } - if( ii == moduleInfoList.size() ) // Module not found in netlist - aNotInNetlist.push_back( module ); + wxASSERT( module != NULL ); + component->SetModule( module ); } - - return true; } diff --git a/pcbnew/netlist_reader.h b/pcbnew/netlist_reader.h index dabf7b8a95..c307648ade 100644 --- a/pcbnew/netlist_reader.h +++ b/pcbnew/netlist_reader.h @@ -1,5 +1,5 @@ #ifndef NETLIST_READER_H -#define NETLIST_READER_H +#define NETLIST_READER_H /** * @file netlist_reader.h @@ -9,6 +9,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Jean-Pierre Charras. + * Copyright (C) 2013 Wayne Stambaugh . * Copyright (C) 2012 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or @@ -33,304 +34,472 @@ #include #include -#include -#include -#include #include -#include -#include -#include +#include // netlist_lexer is common to Eeschema and Pcbnew -/* - * Helper class, to store for a footprint the footprint filter info, - * found in new format KiCad netlist. - * For CvPcb only - * Note: features for CvPcb are for a temporary use. - * They could be removed when CvPcb is modified - * (perhaps when it does not use anumore a netlist to build the component to footprint link) + +using namespace NL_T; + + +class MODULE; +class LINE_READER; +class REPORTER; + + +/** + * Class COMPONENT_NET + * is used to store the component pin name to net name associations stored in a netlist. */ -class LIPBART_INFO +class COMPONENT_NET { -public: - wxString m_Libpart; // the libpart name. - wxArrayString m_FootprintFilter; // an array of footprint filters found in netlist, - // for this footprint + wxString m_pinName; + wxString m_netNumber; + wxString m_netName; public: + COMPONENT_NET() {} - LIPBART_INFO( const wxString& aLibpart ) + COMPONENT_NET( const wxString& aPinName, const wxString& aNetName ) { - m_Libpart = aLibpart; + m_pinName = aPinName; + m_netName = aNetName; } + + const wxString& GetPinName() const { return m_pinName; } + + const wxString& GetNetName() const { return m_netName; } + + bool IsValid() const { return !m_pinName.IsEmpty(); } + + bool operator <( const COMPONENT_NET& aNet ) const + { + return m_pinName < aNet.m_pinName; + } + +#if defined(DEBUG) + /** + * Function Show + * is used to output the object tree, currently for debugging only. + * @param aNestLevel An aid to prettier tree indenting, and is the level + * of nesting of this object within the overall tree. + * @param aReporter A reference to a #REPORTER object to output to. + */ + virtual void Show( int aNestLevel, REPORTER& aReporter ); +#endif }; -typedef std::vector LIPBART_INFO_LIST; + +typedef std::vector< COMPONENT_NET > COMPONENT_NETS; -/* - * Helper class, to store components and footprints info found in netlist. - * (component reference and time stamp, footprint name ... +/** + * Class COMPONENT + * is used to store components and all of their related information found in a netlist. */ -class COMPONENT_INFO +class COMPONENT { -public: - wxString m_Footprint; // the footprint name found in netlist, the in .cmp file - wxString m_Reference; // the schematic reference found in netlist - wxString m_Value; // the schematic value found in netlist + COMPONENT_NETS m_nets; + wxArrayString m_footprintFilters; ///< Footprint filters found in netlist. + wxString m_reference; ///< The component reference designator found in netlist. + wxString m_value; ///< The component value found in netlist. + // ZZZ This timestamp is string, not time_t - wxString m_TimeStamp; // the schematic full time stamp found in netlist - wxString m_Libpart; // the schematic libpart found in netlist - wxArrayString m_FootprintFilter; // a footprint filters list found in old format netlist - int m_pinCount; // the number of pins found in the netlist + wxString m_timeStamp; ///< The component full time stamp found in netlist. + wxString m_name; ///< The name of the component found in the netlist. -public: COMPONENT_INFO( const wxString& libname, - const wxString& cmpname, - const wxString& value, - const wxString& timestamp ) + /// The name of the footprint in the library assigned to the component. + wxString m_footprintLibName; + + /// The lib part name used to look up the component library part information. This only has + /// meaning in the new s-expression netlist file format. + wxString m_libraryName; + wxString m_libraryPartName; + + /// The footprint loaded from the library for this component. + std::auto_ptr< MODULE > m_footprint; + + static COMPONENT_NET m_emptyNet; + +public: + COMPONENT( const wxString& aName, + const wxString& aReference, + const wxString& aValue, + const wxString& aTimeStamp ) { - m_Footprint = libname; - m_Reference = cmpname; - m_Value = value; - m_TimeStamp = timestamp; - m_pinCount = 0; + m_name = aName; + m_reference = aReference; + m_value = aValue; + m_timeStamp = aTimeStamp; } - ~COMPONENT_INFO() { }; + virtual ~COMPONENT() { }; + + void AddNet( const wxString& aPinName, const wxString& aNetName ) + { + m_nets.push_back( COMPONENT_NET( aPinName, aNetName ) ); + } + + unsigned GetNetCount() const { return m_nets.size(); } + + const COMPONENT_NET& GetNet( unsigned aIndex ) const { return m_nets[aIndex]; } + + const COMPONENT_NET& GetNet( const wxString& aPinName ); + + void SortPins() { sort( m_nets.begin(), m_nets.end() ); } + + const wxString& GetReference() const { return m_reference; } + + const wxString& GetValue() const { return m_value; } + + void SetFootprintLibName( const wxString& aFootprintLibName ) + { + m_footprintLibName = aFootprintLibName; + } + + const wxString& GetFootprintLibName() const { return m_footprintLibName; } + + const wxString& GetTimeStamp() const { return m_timeStamp; } + + const wxString& GetLibName() const { return m_name; } + + void SetLibrarySource( const wxString& aLibName, const wxString& aCompName ) + { + m_libraryName = aLibName; + m_libraryPartName = aCompName; + } + + void SetFootprintFilters( const wxArrayString& aFilterList ) + { + m_footprintFilters = aFilterList; + } + + const wxArrayString& GetFootprintFilters() const { return m_footprintFilters; } + + MODULE* GetModule( bool aRelease = false ) + { + return ( aRelease ) ? m_footprint.release() : m_footprint.get(); + } + + void SetModule( MODULE* aModule ); + + bool IsLibSource( const wxString& aLibName, const wxString& aCompName ) const + { + return aLibName == m_libraryName && aCompName == m_libraryPartName; + } + +#if defined(DEBUG) + /** + * Function Show + * is used to output the object tree, currently for debugging only. + * @param aNestLevel An aid to prettier tree indenting, and is the level + * of nesting of this object within the overall tree. + * @param aReporter A reference to a #REPORTER object to output to. + */ + virtual void Show( int aNestLevel, REPORTER& aReporter ); +#endif }; -enum typenetlist + +typedef boost::ptr_vector< COMPONENT > COMPONENTS; +typedef COMPONENTS::iterator COMPONENTS_ITER; +typedef COMPONENTS::const_iterator COMPONENTS_CITER; + + +/** + * Class NETLIST + * stores all of information read from a netlist along with the flags used to update + * the NETLIST in the #BOARD. + */ +class NETLIST { - NETLIST_TYPE_UNSPECIFIED = 0, - NETLIST_TYPE_ORCADPCB2, // the basic format used by pcbnew - NETLIST_TYPE_PCBNEW, // the format used by pcbnew, basic format + more info - NETLIST_TYPE_KICAD // new format using common S expression + COMPONENTS m_components; ///< Components found in the netlist. + + /// Remove footprints from #BOARD not found in netlist when true. + bool m_deleteExtraFootprints; + + /// Do not actually make any changes. Only report changes to #BOARD from netlist + /// when true. + bool m_isDryRun; + + /// Find component by time stamp if true or reference designator if false. + bool m_findByTimeStamp; + + /// Replace component footprints when they differ from the netlist if true. + bool m_replaceFootprints; + +public: + NETLIST() : + m_deleteExtraFootprints( false ), + m_isDryRun( false ), + m_findByTimeStamp( false ), + m_replaceFootprints( false ) + { + } + + /** + * Function IsEmpty() + * @return true if there are no components in the netlist. + */ + bool IsEmpty() const { return m_components.empty(); } + + /** + * Function Clear + * removes all components from the netlist. + */ + void Clear() { m_components.clear(); } + + /** + * Function GetCount + * @return the number of components in the netlist. + */ + unsigned GetCount() const { return m_components.size(); } + + /** + * Function GetComponent + * returns the #COMPONENT at \a aIndex. + * + * @param aIndex the index in #m_components to fetch. + * @return a pointer to the #COMPONENT at \a Index. + */ + COMPONENT* GetComponent( unsigned aIndex ) { return &m_components[ aIndex ]; } + + /** + * Function AddComponent + * adds \a aComponent to the NETLIST. + * + * @note If \a aComponent already exists in the NETLIST, \a aComponent is deleted + * to prevent memory leaks. An assertion is raised in debug builds. + * + * @param aComponent is the COMPONENT to save to the NETLIST. + */ + void AddComponent( COMPONENT* aComponent ); + + /* + * Function GetComponentByReference + * returns a #COMPONENT by \a aReference. + * + * @param aReference is the reference designator the #COMPONENT. + * @return a pointer to the #COMPONENT that matches \a aReference if found. Otherwise NULL. + */ + COMPONENT* GetComponentByReference( const wxString& aReference ); + + /* + * Function GetComponentByTimeStamp + * returns a #COMPONENT by \a aTimeStamp. + * + * @param aTimeStamp is the time stamp the #COMPONENT. + * @return a pointer to the #COMPONENT that matches \a aTimeStamp if found. Otherwise NULL. + */ + COMPONENT* GetComponentByTimeStamp( const wxString& aTimeStamp ); + + /* + * Function GetComponentByLibName + * returns a #COMPONENT by \a aLibName. + * + * @param aLibName is the component library name of the #COMPONENT. + * @return a pointer to the #COMPONENT that matches \a aLibName if found. Otherwise NULL. + */ + COMPONENT* GetComponentByLibName( const wxString& aLibName ); + + void SortByFootprintLibName(); + + void SortByReference(); + + void SetDeleteExtraFootprints( bool aDeleteExtraFootprints ) + { + m_deleteExtraFootprints = aDeleteExtraFootprints; + } + + bool GetDeleteExtraFootprints() const { return m_deleteExtraFootprints; } + + void SetIsDryRun( bool aIsDryRun ) { m_isDryRun = aIsDryRun; } + + bool IsDryRun() const { return m_isDryRun; } + + void SetFindByTimeStamp( bool aFindByTimeStamp ) { m_findByTimeStamp = aFindByTimeStamp; } + + bool IsFindByTimeStamp() const { return m_findByTimeStamp; } + + void SetReplaceFootprints( bool aReplaceFootprints ) + { + m_replaceFootprints = aReplaceFootprints; + } + + bool GetReplaceFootprints() const { return m_replaceFootprints; } + +#if defined(DEBUG) + /** + * Function Show + * is used to output the object tree, currently for debugging only. + * @param aNestLevel An aid to prettier tree indenting, and is the level + * of nesting of this object within the overall tree. + * @param aReporter A reference to a #REPORTER object to output to. + */ + virtual void Show( int aNestLevel, REPORTER& aReporter ); +#endif }; -typedef std::vector COMPONENT_INFO_LIST; -/* - * Helper class, to read a netlist. +/** + * Class CMP_READER + * reads a component footprint link file (*.cmp) format. + */ +class CMP_READER +{ + LINE_READER* m_lineReader; ///< The line reader to read. + +public: + CMP_READER( LINE_READER* aLineReader ) + { + m_lineReader = aLineReader; + } + + /** + * Function Load + * read the *.cmp file format contains the component footprint assignments created by CvPcb + * into \a aNetlist. + * + * @param aNetlist is the #NETLIST to read into. + * + * @todo At some point in the future, use the footprint field in the new s-expression + * netlist file to assign a footprint to a component instead of using a secondary + * (*.cmp) file. + * + * Sample file footprint assignment entry: + * + * Cmp-Mod V01 Genere by CvPcb 29/10/2003-13: 11:6 * + * BeginCmp + * TimeStamp = /32307DE2/AA450F67; + * Reference = C1; + * ValeurCmp = 47uF; + * IdModule = CP6; + * EndCmp + * + * @throw IO_ERROR if a the #LINE_READER IO error occurs. + * @throw PARSE_ERROR if an error occurs while parsing the file. + */ + void Load( NETLIST* aNetlist ) throw( IO_ERROR, PARSE_ERROR ); +}; + + +/** + * Class NETLIST_READER + * is a pure virtual class to derive a specific type of netlist reader from. */ class NETLIST_READER { -private: - PCB_EDIT_FRAME* m_pcbframe; // the main Pcbnew frame (or NULL for CvPcb) - wxTextCtrl* m_messageWindow; // a textctrl to show messages (can be NULL) - wxString m_netlistFullName; // The full netlist filename - wxString m_cmplistFullName; // The full component/footprint association filename - MODULE* m_currModule; // The footprint currently being read in netlist - COMPONENT_INFO_LIST m_componentsInNetlist; // The list of footprints, found in netlist - // (must be loaded from libraries) - COMPONENT_INFO_LIST m_newModulesList; // The list of new footprints, - // found in netlist, but not on board - // (must be loaded from libraries) - LIPBART_INFO_LIST m_libpartList; // For Kicad new netlist format: - // list of libpart found in netlist - // A libpart contains the footprint filters for CvPcb - bool m_buildModuleListOnly; // if true read netlist, populates m_componentsInNetlist - // but do not read and change nets and modules on board - bool m_readLibpartSection; // if true read Libparts section, - // and therefore the footprints filters - enum typenetlist m_typeNetlist; // type opt the netlist currently read +protected: + NETLIST* m_netlist; ///< The net list to read the file(s) into. + bool m_loadFootprintFilters; ///< Load the component footprint filters section if true. + bool m_loadNets; ///< Load the nets section of the netlist file if true. + LINE_READER* m_lineReader; ///< The line reader of the netlist. -public: - bool m_UseCmpFile; // true to use .cmp files as component/footprint file link - // false to use netlist only to know component/footprint link - bool m_UseTimeStamp; // Set to true to identify footprints by time stamp - // false to use schematic reference - bool m_ChangeFootprints; // Set to true to change existing footprints to new ones - // when netlist gives a different footprint name + /// The reader used to load the footprint links. If NULL, footprint links are not read. + CMP_READER* m_footprintReader; public: - NETLIST_READER( PCB_EDIT_FRAME* aFrame, wxTextCtrl* aMessageWindow = NULL ) + enum NETLIST_FILE_T { - m_pcbframe = aFrame; - m_messageWindow = aMessageWindow; - m_UseTimeStamp = false; - m_ChangeFootprints = false; - m_UseCmpFile = true; - m_buildModuleListOnly = false; - m_readLibpartSection = false; - m_typeNetlist = NETLIST_TYPE_UNSPECIFIED; + UNKNOWN = -1, + ORCAD, + LEGACY, + KICAD, + + // Add new types here. Don't forget to create the appropriate class derived from + // NETCLASS_READER and add the entry to the NETLIST_READER::GetNetlistReader() + // function. + }; + + NETLIST_READER( LINE_READER* aLineReader, + NETLIST* aNetlist, + CMP_READER* aFootprintLinkReader = NULL ) + { + wxASSERT( aLineReader != NULL ); + + m_lineReader = aLineReader; + m_footprintReader = aFootprintLinkReader; + m_netlist = aNetlist; + m_loadFootprintFilters = true; + m_loadNets = true; } - ~NETLIST_READER() - { - // Free modules info list: - for( unsigned ii = 0; ii < m_newModulesList.size(); ii++ ) - delete m_componentsInNetlist[ii]; - - m_componentsInNetlist.clear(); - m_newModulesList.clear(); - - // Free libpart info list: - for( unsigned ii = 0; ii < m_libpartList.size(); ii++ ) - delete m_libpartList[ii]; - m_libpartList.clear(); - } + virtual ~NETLIST_READER(); /** - * Function GetNetlistType - * @return the type of netlist read: - * NETLIST_TYPE_UNSPECIFIED: Unknown format - * NETLIST_TYPE_ORCADPCB2: the basic format used by pcbnew - * NETLIST_TYPE_PCBNEW: the format used by pcbnew, basic format + more info - * NETLIST_TYPE_KICAD: the new format + * Function GuessNetlistFileType + * looks at \a aFileHeaderLine to see if it matches any of the netlist file types it + * knows about. + * + * @param aLineReader is the #LINE_READER object containing lines from the netlist to test. + * @return the #NETLIST_FILE_T of \a aLineReader. */ - int GetNetlistType() - { - return m_typeNetlist; - } + static NETLIST_FILE_T GuessNetlistFileType( LINE_READER* aLineReader ); /** - * Function GetComponentInfoList - * @return the component info list built from the netlist + * Function GetNetlistReader + * attempts to determine the net list file type of \a aNetlistFileName and return the + * appropriate NETLIST_READER type. + * + * @param aNetlist is the netlist to load \a aNetlistFileName into. + * @param aNetlistFileName is the full path and file name of the net list to read. + * @param aCompFootprintFileName is the full path and file name of the component footprint + * associations to read. Set to wxEmptyString if loading the + * footprint association file is not required. + * @return the appropriate NETLIST_READER if \a aNetlistFileName is a valid netlist or + * NULL if \a aNetlistFileName is not a valid netlist files. */ - COMPONENT_INFO_LIST& GetComponentInfoList() - { - return m_componentsInNetlist; - } + static NETLIST_READER* GetNetlistReader( NETLIST* aNetlist, + const wxString& aNetlistFileName, + const wxString& aCompFootprintFileName = wxEmptyString ) + throw( IO_ERROR ); /** - * Function GetComponentInfoList - * @return a reference to the libpart info corresponding to a given part - * @param aPartname = the name of the libpart + * Function LoadNetlist + * loads the contents of the netlist file into \a aNetlist. + * + * @throw IO_ERROR if a file IO error occurs. + * @throw PARSE_ERROR if an error occurs while parsing the file. */ - LIPBART_INFO* GetLibpart(const wxString & aPartname); + virtual void LoadNetlist() throw ( IO_ERROR, PARSE_ERROR ) = 0; /** - * Function IsCvPcbMode - * @return true if the netlist is read by CvPcb - * In cvpcb mode, nets are stored in module info, - * and the footprint filters list is read. - * There is also no board in CvPcb + * Function GetLineReader() + * @return the #LINE_READER associated with the #NETLIST_READER. */ - bool IsCvPcbMode() { return m_pcbframe == 0; } + LINE_READER* GetLineReader(); +}; + +/** + * Class LEGACY_NETLIST_READER + * reads the KiCad legacy and the old Orcad netlist formats. + * + * The KiCad legacy netlist format was derived directly from an old Orcad netlist format. The + * primary difference is the header was changed so this reader can read both formats. + */ +class LEGACY_NETLIST_READER : public NETLIST_READER +{ /** - * Function AddModuleInfo - * Add a new module info to the main list of modules ifo - * @param aModInfo = a reference to the item to add + * Function loadComponent + * read the \a aLine containing the description of a component from a legacy format + * netlist and add it to the netlist. + * + * Analyze the first line of a component description in netlist: + * ( /40C08647 $noname R20 4.7K {Lib=R} + * + * @param aText contains the first line of description + * @return the new component created by parsing \a aLine + * @throw PARSE_ERROR when \a aLine is not a valid component description. */ - void AddModuleInfo( COMPONENT_INFO* aModInfo ) - { - m_componentsInNetlist.push_back( aModInfo ); - } + COMPONENT* loadComponent( char* aText ) throw( PARSE_ERROR ); /** - * Function AddLibpartInfo - * LIPBART_INFO items (and therefore footprint filter strings) are stored in - * m_libpartList - * @param aPartInfo = a refernce to the LIPBART_INFO to add in list - */ - void AddLibpartInfo( LIPBART_INFO * aPartInfo ) - { - m_libpartList.push_back( aPartInfo ); - } - - /** - * Function ReadLibpartSectionSetOpt - * Set to true or false the read Partlists section. - * footprint filters are found in this section - * When this option is false, the Partlists section is ignored - * When this option is true, the Partlists section is read, - * Libpart items (and therefore footprint filter strings) are stored in - * m_libpartList - * @param aOpt = the value of option - */ - void ReadLibpartSectionSetOpt( bool aOpt ) - { - m_readLibpartSection = aOpt; - } - - /** - * Function ReadLibpartSectionOpt - * @return the readPartlist option - */ - bool ReadLibpartSectionOpt() { return m_readLibpartSection; } - - /** - * Function BuildModuleListOnlySetOpt - * Set to true or false the Build Module List Only option - * When this option is false, a full netlist read is made, - * and modules are added/modified - * When this option is true, a partial netlist read is made - * and only the list of modules found in netlist is built - * @param aOpt = the value of option - */ - void BuildModuleListOnlySetOpt( bool aOpt ) - { - m_buildModuleListOnly = aOpt; - } - - /** - * Function BuildModuleListOnlyOpt - * Get the Build Module List Only option state - * @return the state of option (true/false) - */ - bool BuildModuleListOnlyOpt() - { - return m_buildModuleListOnly; - } - - /** - * Function InitializeModules - * Called when reading a netlist and after the module info list is populated - * Load new module and clear pads netnames - * return true if all modules are loaded, false if some are missing - */ - bool InitializeModules(); - - /** - * Function TestFootprintsMatchingAndExchange - * Called when reading a netlist, after the module info list is populated - * module reference updated (after a call to InitializeModules) - * Test, for each module, if the current footprint matches the footprint - * given by the netlist (or the cmp file, if used) - * print a list of mismatches od exchange footprints i - * m_ChangeFootprints == true - */ - void TestFootprintsMatchingAndExchange(); - - - /** - * Function SetFilesnames - * initialize filenames - * @param aNetlistFileName = full filename of netlist - * @param aCmplistFileName = full filename of components file (can be empty) - * and the components file will be non used - */ - void SetFilesnames( const wxString& aNetlistFileName, - const wxString& aCmplistFileName ) - { - m_netlistFullName = aNetlistFileName; - m_cmplistFullName = aCmplistFileName; - } - - /** - * Function ReadNetList - * The main function to detect a netlist format, read the netlist, - * and update the board - * depending on the detected format, calls ReadOldFmtdNetList or ReadKicadNetList - * @param aFile = the already opened file (will be closed by the netlist reader) - * @return true if success - */ - bool ReadNetList( FILE* aFile ); - - /** - * Function ReadOldFmtdNetList - * The main function to read a netlist (old netlist format), - * and update the board - * @param aFile = the already opened file (will be closed by ReadOldFmtdNetList) - * @return true if success - */ - bool ReadOldFmtdNetList( FILE* aFile ); - - /** - * Function ReadOldFmtFootprintFilterList - * Read the section "Allowed footprints" like: + * Function loadFootprintFilters + * loads the footprint filter section of netlist file. + * + * Sample legacy footprint filter section: * { Allowed footprints by component: * $component R11 * R? @@ -342,99 +511,168 @@ public: * $endfootprintlist * } * - * And add the strings giving the footprint filter to m_FootprintFilter + * @throw IO_ERROR if a file IO error occurs. + * @throw PARSE_ERROR if an error occurs while parsing the file. + */ + void loadFootprintFilters() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function loadNet + * read a component net description from \a aText. + * + * @param aText is current line read from the netlist. + * @param aComponent is the component to add the net to. + * @throw PARSE_ERROR if a error occurs reading \a aText. + */ + void loadNet( char* aText, COMPONENT* aComponent ) throw( PARSE_ERROR ); + +public: + + LEGACY_NETLIST_READER( LINE_READER* aLineReader, + NETLIST* aNetlist, + CMP_READER* aFootprintLinkReader = NULL ) : + NETLIST_READER( aLineReader, aNetlist, aFootprintLinkReader ) + { + } + + /** + * Function LoadNetlist + * read the netlist file in the legacy format into \a aNetlist. + * + * The legacy netlist format is: + * \# EESchema Netlist Version 1.0 generee le 18/5/2005-12:30:22 + * ( + * ( 40C08647 $noname R20 4,7K {Lib=R} + * ( 1 VCC ) + * ( 2 MODB_1 ) + * ) + * ( 40C0863F $noname R18 4,7_k {Lib=R} + * ( 1 VCC ) + * ( 2 MODA_1 ) + * ) + * } + * \#End + * + * @throw IO_ERROR if a file IO error occurs. + * @throw PARSE_ERROR if an error occurs while parsing the file. + */ + virtual void LoadNetlist() throw ( IO_ERROR, PARSE_ERROR ); +}; + + +/** + * Class KICAD_NETLIST_PARSER + * is the parser for reading the KiCad s-expression netlist format. + */ +class KICAD_NETLIST_PARSER : public NETLIST_LEXER +{ +private: + T token; + LINE_READER* m_lineReader; ///< The line reader used to parse the netlist. Not owned. + NETLIST* m_netlist; ///< The netlist to parse into. Not owned. + + /** + * Function skipCurrent + * Skip the current token level, i.e + * search for the RIGHT parenthesis which closes the current description + */ + void skipCurrent() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function parseComponent + * parse a component description: + * (comp (ref P1) + * (value DB25FEMELLE) + * (footprint DB25FC) + * (libsource (lib conn) (part DB25)) + * (sheetpath (names /) (tstamps /)) + * (tstamp 3256759C)) + */ + void parseComponent() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function parseNet + * Parses a section like + * (net (code 20) (name /PC-A0) + * (node (ref BUS1) (pin 62)) + * (node (ref U3) (pin 3)) + * (node (ref U9) (pin M6))) + * + * and set the corresponding pads netnames + */ + void parseNet() throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function parseLibPartList + * reads the section "libparts" in the netlist: + * (libparts + * (libpart (lib device) (part C) + * (description "Condensateur non polarise") + * (footprints + * (fp SM*) + * (fp C?) + * (fp C1-1)) + * (fields + * (field (name Reference) C) + * (field (name Value) C)) + * (pins + * (pin (num 1) (name ~) (type passive)) + * (pin (num 2) (name ~) (type passive)))) + * + * And add the strings giving the footprint filter (subsection footprints) * of the corresponding module info *

This section is used by CvPcb, and is not useful in Pcbnew, * therefore it it not always read

*/ - bool ReadOldFmtFootprintFilterList( FILE_LINE_READER& aNetlistReader ); + void parseLibPartList() throw( IO_ERROR, PARSE_ERROR ); + + +public: + KICAD_NETLIST_PARSER( LINE_READER* aReader, NETLIST* aNetlist ); + + void SetLineReader( LINE_READER* aLineReader ); + + void SetNetlist( NETLIST* aNetlist ) { m_netlist = aNetlist; } /** - * Function ReadKicadNetList - * The main function to read a netlist (new netlist format, using S expressions), - * and update the board - * @param aFile = the already opened file (will be closed by ReadKicadNetList) - * @return true if success + * Function Parse + * parse the full netlist */ - bool ReadKicadNetList( FILE* aFile ); + void Parse() throw( IO_ERROR, PARSE_ERROR ); - /** - * function RemoveExtraFootprints - * Remove (delete) not locked footprints found on board, but not in netlist - * The netlist is expected to be read, and the main module list info up to date - */ - void RemoveExtraFootprints( ); - - /** - * Function SetPadsNetName - * Update pads netnames for a given module. - * Because a pad name can be found more than once in this module, - * all pads matching the pad name are updated - * @param aModule = module reference - * @param aPadname = pad name (pad num) - * @param aNetname = new net name of the pad - * @param aPadList = a std::vector& buffer where the updated pads can be stored - * @return the pad count - */ - int SetPadsNetName( const wxString & aModule, const wxString & aPadname, - const wxString & aNetname, std::vector & aPadList ); - -private: - - /** - * Function FindModule - * search for a module id the modules existing in the current BOARD. - * @param aId = the key to identify the module to find: - * The reference or the full time stamp, according to m_UseTimeStamp - * @return the module found, or NULL. - */ - MODULE* FindModule( const wxString& aId ); - - /** - * Function SetPadNetName - * Update a pad netname using the current footprint - * from the netlist (line format: ( \ \ ) ) - * @param aText = current line read from netlist - */ - bool SetPadNetName( char* aText ); - - /** - * Function ReadOldFmtNetlistModuleDescr - * Read the full description of a footprint, from the netlist - * and update the corresponding module. - * @param aBuildList bool to switch between 2 modes: - * aBuildList = true: - * add module info added to m_newModulesList - * aBuildList = false: - * The module is searched in the board modules list - * @param aText contains the first line of description - * This function uses m_useFichCmp as a flag to know the footprint name: - * If true: component file *.cmp is used - * If false: the netlist only is used - * This flag is reset to false if the .cmp file is not found - * @return if aBuildList = true, a reference to the COMPONENT_INFO - * if aBuildList = false, a reference to the corresponding MODULE on board (NULL if not found) - */ - void* ReadOldFmtNetlistModuleDescr( char* aText, bool aBuildList ); - - /** - * Function loadNewModules - * Load from libraries new modules found in netlist and add them to the current Board. - * modules to load come from m_newModulesList - * @return false if a footprint is not found, true if all footprints are loaded - */ - bool loadNewModules(); - - /** - * function readModuleComponentLinkfile - * read the *.cmp file ( filename in m_cmplistFullName ) - * and initialize the m_Footprint member of each item in m_componentsInNetlist, - * when it is found in file, and with a non empty footprint value - * giving the equivalence between footprint names and components - * to find the footprint name corresponding to aCmpIdent - * @return true and the file can be read - */ - bool readModuleComponentLinkfile(); + // Useful for debug only: + const char* getTokenName( T aTok ) + { + return NETLIST_LEXER::TokenName( aTok ); + } }; -#endif // NETLIST_READER_H + +/** + * Class KICAD_NETLIST_READER + * read the new s-expression based KiCad netlist format. + */ +class KICAD_NETLIST_READER : public NETLIST_READER +{ + KICAD_NETLIST_PARSER* m_parser; ///< The s-expression format parser. + +public: + KICAD_NETLIST_READER( LINE_READER* aLineReader, + NETLIST* aNetlist, + CMP_READER* aFootprintLinkReader = NULL ) : + NETLIST_READER( aLineReader, aNetlist, aFootprintLinkReader ), + m_parser( new KICAD_NETLIST_PARSER( aLineReader, aNetlist ) ) + { + } + + virtual ~KICAD_NETLIST_READER() + { + if( m_parser ) + delete m_parser; + } + + virtual void LoadNetlist() throw ( IO_ERROR, PARSE_ERROR ); +}; + + +#endif // NETLIST_READER_H diff --git a/pcbnew/netlist_reader_common.cpp b/pcbnew/netlist_reader_common.cpp index 87acf9bf8c..7c563d4562 100644 --- a/pcbnew/netlist_reader_common.cpp +++ b/pcbnew/netlist_reader_common.cpp @@ -5,6 +5,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2011 Jean-Pierre Charras. + * Copyright (C) 2013 Wayne Stambaugh . * Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -27,584 +28,392 @@ -#include -#include #include -#include - -#include -#include -#include +#include +#include #include +#include -#include +#include -/* - * Function ReadNetList - * The main function to detect the netlist format,and run the right netlist reader - * aFile = the already opened file (will be closed by the netlist reader) - */ -bool NETLIST_READER::ReadNetList( FILE* aFile ) -{ - // Try to determine the netlist type: - // Beginning of the first line of known formats, without spaces - #define HEADERS_COUNT 3 - #define HEADER_ORCADPCB "({EESchemaNetlist" - #define HEADER_PCBNEW "#EESchemaNetlist" - #define HEADER_KICAD_NETFMT "(export" - const std::string headers[HEADERS_COUNT] = - { - HEADER_ORCADPCB, HEADER_PCBNEW, HEADER_KICAD_NETFMT - }; - - int format = -1; - for ( int jj = 0; jj < HEADERS_COUNT; jj++ ) - { - int imax = headers[jj].size(); - int ii = 0; - for( ; ii < imax; ii++ ) - { - int data; - // Read header, and skip blanks to avoid errors if an header changes - do - { - data = fgetc( aFile ); - } while ( ( data == ' ' ) &&( EOF != data ) ) ; - - if( (int)headers[jj][ii] == data ) - continue; - break; - } - if( ii == imax ) // header found - { - format = jj; - break; - } - rewind( aFile ); - } - - rewind( aFile ); - bool success = false; - switch( format ) - { - case 0: - m_typeNetlist = NETLIST_TYPE_ORCADPCB2; - success = ReadOldFmtdNetList( aFile ); - break; - - case 1: - m_typeNetlist = NETLIST_TYPE_PCBNEW; - success = ReadOldFmtdNetList( aFile ); - break; - - case 2: - m_typeNetlist = NETLIST_TYPE_KICAD; - success = ReadKicadNetList( aFile ); - break; - - default: // Unrecognized format: - break; - - } - - return success; -} /** - * Function GetComponentInfoList - * @return a reference to the libpart info corresponding to a given part - * @param aPartname = the name of the libpart - */ -LIPBART_INFO* NETLIST_READER::GetLibpart(const wxString & aPartname) + * Function NestedSpace + * outputs nested space for pretty indenting. + * @param aNestLevel The nest count + * @param aReporter A reference to a #REPORTER object where to output. + * @return REPORTER& for continuation. + **/ +static REPORTER& NestedSpace( int aNestLevel, REPORTER& aReporter ) { - for( unsigned ii = 0; ii < m_libpartList.size(); ii++ ) - { - if( m_libpartList[ii]->m_Libpart == aPartname ) - return m_libpartList[ii]; - } + for( int i = 0; i < aNestLevel; ++i ) + aReporter.Report( wxT( " " ) ); - return NULL; + return aReporter; } -bool NETLIST_READER::InitializeModules() +#if defined(DEBUG) +void COMPONENT_NET::Show( int aNestLevel, REPORTER& aReporter ) { - if( m_UseCmpFile ) // Try to get footprint name from .cmp file - { - readModuleComponentLinkfile(); - } - - if( m_pcbframe == NULL ) - return true; - - for( unsigned ii = 0; ii < m_componentsInNetlist.size(); ii++ ) - { - COMPONENT_INFO* currcmp_info = m_componentsInNetlist[ii]; - // Test if module is already loaded. - wxString * idMod = m_UseTimeStamp? - &currcmp_info->m_TimeStamp : &currcmp_info->m_Reference; - - MODULE* module = FindModule( *idMod ); - if( module == NULL ) // not existing, load it - { - m_newModulesList.push_back( currcmp_info ); - } - } - - bool success = loadNewModules(); - - // Update modules fields - for( unsigned ii = 0; ii < m_componentsInNetlist.size(); ii++ ) - { - COMPONENT_INFO* currcmp_info = m_componentsInNetlist[ii]; - // Test if module is already loaded. - wxString * idMod = m_UseTimeStamp? - &currcmp_info->m_TimeStamp : &currcmp_info->m_Reference; - - MODULE* module = FindModule( *idMod ); - if( module ) - { - // Update current module ( reference, value and "Time Stamp") - module->SetReference( currcmp_info->m_Reference ); - module->SetValue(currcmp_info->m_Value ); - module->SetPath( currcmp_info->m_TimeStamp ); - } - else // not existing - { - } - } - - // clear pads netnames -#if 1 - // Clear only footprints found in netlist: - // This allow to have some footprints added by hand to the board - // left initialized - for( unsigned ii = 0; ii < m_componentsInNetlist.size(); ii++ ) - { - COMPONENT_INFO* currcmp_info = m_componentsInNetlist[ii]; - // We can used the reference to find the footprint, because - // it is now updated - wxString * idMod = &currcmp_info->m_Reference; - - MODULE* module = FindModule( *idMod ); - if( module ) - { - for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) - pad->SetNetname( wxEmptyString ); - } - } - -#else - // Clear all footprints - for( MODULE* module = m_pcbframe->GetBoard()->m_Modules; module; module = module->Next() ) - { - for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() ) - pad->SetNetname( wxEmptyString ); - } + NestedSpace( aNestLevel, aReporter ); + aReporter.Report( wxString::Format( wxT( "\n" ), + GetChars( m_pinName ), GetChars( m_netName ) ) ); +} #endif - return success; + +void COMPONENT::SetModule( MODULE* aModule ) +{ + m_footprint.reset( aModule ); + + if( aModule == NULL ) + return; + + aModule->SetReference( m_reference ); + aModule->SetValue( m_value ); + aModule->SetLibRef( m_footprintLibName ); + aModule->SetPath( m_timeStamp ); } -void NETLIST_READER::TestFootprintsMatchingAndExchange() + +COMPONENT_NET COMPONENT::m_emptyNet; + + +const COMPONENT_NET& COMPONENT::GetNet( const wxString& aPinName ) { -#ifdef PCBNEW - - // If a module is "exchanged", the new module is added to the end of - // module list. - - // Calculates the module count - int moduleCount = m_pcbframe->GetBoard()->m_Modules.GetCount(); - - MODULE* nextmodule; - MODULE *module = m_pcbframe->GetBoard()->m_Modules; - for( ; module && moduleCount; module = nextmodule, moduleCount-- ) + for( unsigned i = 0; i < m_nets.size(); i++ ) { - // Module can be deleted if exchanged, so store the next module. - nextmodule = module->Next(); + if( m_nets[i].GetPinName() == aPinName ) + return m_nets[i]; + } - // Search for the corresponding module info - COMPONENT_INFO * cmp_info = NULL; - for( unsigned ii = 0; ii < m_componentsInNetlist.size(); ii++ ) + return m_emptyNet; +} + + +#if defined(DEBUG) +void COMPONENT::Show( int aNestLevel, REPORTER& aReporter ) +{ + NestedSpace( aNestLevel, aReporter ); + aReporter.Report( wxT( "\n" ) ); + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( wxString::Format( wxT( "\n" ), + GetChars( m_reference ), GetChars( m_value ), + GetChars( m_name ), GetChars( m_footprintLibName ), + GetChars( m_timeStamp ) ) ); + + if( !m_footprintFilters.IsEmpty() ) + { + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( wxT( "\n" ) ); + + for( unsigned i = 0; i < m_footprintFilters.GetCount(); i++ ) { - COMPONENT_INFO * candidate = m_componentsInNetlist[ii]; - // Test if cmp_info matches the current module: - if( candidate->m_Reference.CmpNoCase( module->GetReference() ) == 0 ) - { - cmp_info = candidate; - break; - } + NestedSpace( aNestLevel+2, aReporter ); + aReporter.Report( wxString::Format( wxT( "<%s>\n" ), + GetChars( m_footprintFilters[i] ) ) ); } - if( cmp_info == NULL ) // not found in netlist - continue; + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( wxT( "\n" ) ); + } - if( module->GetLibRef().CmpNoCase( cmp_info->m_Footprint ) != 0 ) + if( !m_nets.empty() ) + { + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( wxT( "\n" ) ); + + for( unsigned i = 0; i < m_nets.size(); i++ ) + m_nets[i].Show( aNestLevel+3, aReporter ); + + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( "\n" ); + } + + NestedSpace( aNestLevel, aReporter ); + aReporter.Report( "\n" ); +} +#endif + + +void NETLIST::AddComponent( COMPONENT* aComponent ) +{ + m_components.push_back( aComponent ); +} + + +COMPONENT* NETLIST::GetComponentByReference( const wxString& aReference ) +{ + COMPONENT* component = NULL; + + for( unsigned i = 0; i < m_components.size(); i++ ) + { + if( m_components[i].GetReference() == aReference ) { - if( m_ChangeFootprints ) // footprint exchange allowed. - { - MODULE* newModule = m_pcbframe->GetModuleLibrary( wxEmptyString, - cmp_info->m_Footprint, - false ); - - if( newModule ) - { - wxString msg; - msg.Printf( _( "Module ref %s, change footprint %s to %s\n" ), - GetChars( module->GetReference() ), - GetChars( module->GetLibRef() ), - GetChars( cmp_info->m_Footprint ) ); - m_messageWindow->AppendText( msg ); - // Change old module to the new module (and delete the old one) - m_pcbframe->Exchange_Module( module, newModule, NULL ); - } - else if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Component %s: module %s not found\n" ), - GetChars( cmp_info->m_Reference ), - GetChars( cmp_info->m_Footprint ) ); - - m_messageWindow->AppendText( msg ); - } - } - else if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Component %s: Mismatch! module is %s and netlist said %s\n" ), - GetChars( cmp_info->m_Reference ), - GetChars( module->GetLibRef() ), - GetChars( cmp_info->m_Footprint ) ); - - m_messageWindow->AppendText( msg ); - } + component = &m_components[i]; + break; } } -#endif + + return component; } + +COMPONENT* NETLIST::GetComponentByTimeStamp( const wxString& aTimeStamp ) +{ + COMPONENT* component = NULL; + + for( unsigned i = 0; i < m_components.size(); i++ ) + { + if( m_components[i].GetTimeStamp() == aTimeStamp ) + { + component = &m_components[i]; + break; + } + } + + return component; +} + + +COMPONENT* NETLIST::GetComponentByLibName( const wxString& aLibName ) +{ + COMPONENT* component = NULL; + + for( unsigned i = 0; i < m_components.size(); i++ ) + { + if( m_components[i].GetLibName() == aLibName ) + { + component = &m_components[i]; + break; + } + } + + return component; +} + + /** - * Function SetPadsNetName - * Update pads netnames for a given module. - * Because a pad name can be found more than once in this module, - * all pads matching the pad name are updated - * @param aModule = module reference - * @param aPadname = pad name (pad num) - * @param aNetname = new net name of the pad - * @param aPadList = a std::vector& buffer where the updated pads can be stored - * @return the pad count + * Function SortByLibName + * is a helper function used to sort the component list used by loadNewModules. */ -int NETLIST_READER::SetPadsNetName( const wxString & aModule, const wxString & aPadname, - const wxString & aNetname, std::vector & aPadList ) +static bool SortByLibName( const COMPONENT& ref, const COMPONENT& cmp ) { - if( m_pcbframe == NULL ) - return 0; - - int padcount = 0; - MODULE* module = m_pcbframe->GetBoard()->FindModuleByReference( aModule ); - - if( module ) - { - D_PAD * pad = module->FindPadByName( aPadname ); - - if( pad ) - { - padcount++; - aPadList.push_back( pad ); - pad->SetNetname( aNetname ); - // Search for other pads having the same pad name/num - for( D_PAD* curr_pad = pad->Next(); curr_pad; curr_pad = curr_pad->Next() ) - { - if( pad->PadNameEqual( curr_pad ) ) - { - padcount++; - aPadList.push_back( curr_pad ); - curr_pad->SetNetname( aNetname ); - } - } - return padcount; - } - - if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Module %s: Pad %s not found" ), - GetChars( aModule ), GetChars( aPadname ) ); - m_messageWindow->AppendText( msg + wxT( "\n" ) ); - } - } - - return 0; + return ref.GetFootprintLibName().CmpNoCase( cmp.GetFootprintLibName() ) > 0; } -/* function RemoveExtraFootprints - * Remove (delete) not locked footprints found on board, but not in netlist - */ -void NETLIST_READER::RemoveExtraFootprints() +void NETLIST::SortByFootprintLibName() { - MODULE* nextModule; - MODULE* module = m_pcbframe->GetBoard()->m_Modules; + sort( m_components.begin(), m_components.end(), SortByLibName ); +} - for( ; module != NULL; module = nextModule ) + +/** + * Operator < + * compares two #COMPONENT objects by reference designator. + */ +bool operator < ( const COMPONENT& item1, const COMPONENT& item2 ) +{ + return StrNumCmp( item1.GetReference(), item2.GetReference(), INT_MAX, true ) < 0; +} + + +void NETLIST::SortByReference() +{ + sort( m_components.begin(), m_components.end() ); +} + + +#if defined( DEBUG ) +void NETLIST::Show( int aNestLevel, REPORTER& aReporter ) +{ + NestedSpace( aNestLevel, aReporter ); + aReporter.Report( "\n" ); + + if( !m_components.empty() ) { - unsigned ii; - nextModule = module->Next(); + NestedSpace( aNestLevel+1, aReporter ); + aReporter.Report( "\n" ); - if( module->IsLocked() ) - continue; - - for( ii = 0; ii < m_componentsInNetlist.size(); ii++ ) + for( unsigned i = 0; i < m_components.size(); i++ ) { - COMPONENT_INFO* cmp_info = m_componentsInNetlist[ii]; - - if( module->GetReference().CmpNoCase( cmp_info->m_Reference ) == 0 ) - break; // Module is found in net list. + m_components[i].Show( aNestLevel+2, aReporter ); } - if( ii == m_componentsInNetlist.size() ) // Module not found in netlist. - module->DeleteStructure(); + NestedSpace( aNestLevel+1, aReporter ); + + aReporter.Report( "\n" ); + } + + NestedSpace( aNestLevel, aReporter ); + aReporter.Report( "\n" ); +} +#endif + + +NETLIST_READER::~NETLIST_READER() +{ + if( m_lineReader ) + { + delete m_lineReader; + m_lineReader = NULL; + } + + if( m_footprintReader ) + { + delete m_footprintReader; + m_footprintReader = NULL; } } -/* Search for a module id the modules existing in the current BOARD. - * aId is a key to identify the module to find: - * The reference or the full time stamp, according to m_UseTimeStamp - * Returns the module is found, NULL otherwise. - */ -MODULE* NETLIST_READER::FindModule( const wxString& aId ) + +NETLIST_READER::NETLIST_FILE_T NETLIST_READER::GuessNetlistFileType( LINE_READER* aLineReader ) { - MODULE* module = m_pcbframe->GetBoard()->m_Modules; - for( ; module != NULL; module = module->Next() ) + wxRegEx reOrcad( wxT( "(?i)[ ]*\\({EESchema[ \t]+Netlist[ \t]+" ), wxRE_ADVANCED ); + wxASSERT( reOrcad.IsValid() ); + wxRegEx reLegacy( wxT( "(?i)#[ \t]+EESchema[ \t]+Netlist[ \t]+" ), wxRE_ADVANCED ); + wxASSERT( reLegacy.IsValid() ); + wxRegEx reKicad( wxT( "[ ]*\\(export[ ]+" ), wxRE_ADVANCED ); + wxASSERT( reKicad.IsValid() ); + + wxString line; + + while( aLineReader->ReadLine() ) { - if( m_UseTimeStamp ) // identification by time stamp - { - if( aId.CmpNoCase( module->GetPath() ) == 0 ) - return module; - } - else // identification by Reference - { - if( aId.CmpNoCase( module->GetReference() ) == 0 ) - return module; - } + line = FROM_UTF8( aLineReader->Line() ); + + if( reOrcad.Matches( line ) ) + return ORCAD; + else if( reLegacy.Matches( line ) ) + return LEGACY; + else if( reKicad.Matches( line ) ) + return KICAD; } - return NULL; + return UNKNOWN; } -/* - * function readModuleComponentLinkfile - * read the *.cmp file ( filename in m_cmplistFullName ) - * giving the equivalence Footprint_names / components - * to find the footprint name corresponding to aCmpIdent - * return true if the file can be read - * - * Sample file: - * - * Cmp-Mod V01 Genere by CvPcb 29/10/2003-13: 11:6 * - * BeginCmp - * TimeStamp = /32307DE2/AA450F67; - * Reference = C1; - * ValeurCmp = 47uF; - * IdModule = CP6; - * EndCmp - * - */ - -bool NETLIST_READER::readModuleComponentLinkfile() +NETLIST_READER* NETLIST_READER::GetNetlistReader( NETLIST* aNetlist, + const wxString& aNetlistFileName, + const wxString& aCompFootprintFileName ) + throw( IO_ERROR ) { - wxString refcurrcmp; // Stores value read from line like Reference = BUS1; - wxString timestamp; // Stores value read from line like TimeStamp = /32307DE2/AA450F67; - wxString footprint; // Stores value read from line like IdModule = CP6; + wxASSERT( aNetlist != NULL ); - FILE* cmpFile = wxFopen( m_cmplistFullName, wxT( "rt" ) ); + FILE* file = wxFopen( aNetlistFileName, wxT( "rt" ) ); - if( cmpFile == NULL ) + if( file == NULL ) { wxString msg; - msg.Printf( _( "File <%s> not found, use Netlist for footprints selection" ), - GetChars( m_cmplistFullName ) ); - - if( m_messageWindow ) - m_messageWindow->AppendText( msg ); - return false; + msg.Printf( _( "Cannot open file %s for reading." ), GetChars( aNetlistFileName ) ); + THROW_IO_ERROR( msg ); } - // netlineReader dtor will close cmpFile - FILE_LINE_READER netlineReader( cmpFile, m_cmplistFullName ); + FILE_LINE_READER* reader = new FILE_LINE_READER( file, aNetlistFileName ); + std::auto_ptr< FILE_LINE_READER > r( reader ); + + NETLIST_FILE_T type = GuessNetlistFileType( reader ); + reader->Rewind(); + + // The component footprint link reader is NULL if no file name was specified. + CMP_READER* cmpFileReader = NULL; + + if( !aCompFootprintFileName.IsEmpty() ) + { + cmpFileReader = new CMP_READER( new FILE_LINE_READER( aCompFootprintFileName ) ); + } + + switch( type ) + { + case LEGACY: + case ORCAD: + return new LEGACY_NETLIST_READER( r.release(), aNetlist, cmpFileReader ); + + case KICAD: + return new KICAD_NETLIST_READER( r.release(), aNetlist, cmpFileReader ); + + default: // Unrecognized format: + break; + + } + + return NULL; +} + + +void CMP_READER::Load( NETLIST* aNetlist ) throw( IO_ERROR, PARSE_ERROR ) +{ + wxCHECK_RET( aNetlist != NULL, wxT( "No netlist passed to CMP_READER::Load()" ) ); + + wxString reference; // Stores value read from line like Reference = BUS1; + wxString timestamp; // Stores value read from line like TimeStamp = /32307DE2/AA450F67; + wxString footprint; // Stores value read from line like IdModule = CP6; wxString buffer; wxString value; - while( netlineReader.ReadLine() ) - { - buffer = FROM_UTF8( netlineReader.Line() ); - if( ! buffer.StartsWith( wxT("BeginCmp") ) ) + while( m_lineReader->ReadLine() ) + { + buffer = FROM_UTF8( m_lineReader->Line() ); + + if( !buffer.StartsWith( wxT( "BeginCmp" ) ) ) continue; // Begin component description. - refcurrcmp.Empty(); + reference.Empty(); footprint.Empty(); timestamp.Empty(); - while( netlineReader.ReadLine() ) + while( m_lineReader->ReadLine() ) { - buffer = FROM_UTF8( netlineReader.Line() ); + buffer = FROM_UTF8( m_lineReader->Line() ); - if( buffer.StartsWith( wxT("EndCmp") ) ) + if( buffer.StartsWith( wxT( "EndCmp" ) ) ) break; // store string value, stored between '=' and ';' delimiters. value = buffer.AfterFirst( '=' ); - value = value.BeforeLast( ';'); - value.Trim(true); - value.Trim(false); + value = value.BeforeLast( ';' ); + value.Trim( true ); + value.Trim( false ); - if( buffer.StartsWith( wxT("Reference") ) ) + if( buffer.StartsWith( wxT( "Reference" ) ) ) { - refcurrcmp = value; + reference = value; continue; } - if( buffer.StartsWith( wxT("IdModule =" ) ) ) + if( buffer.StartsWith( wxT( "IdModule =" ) ) ) { footprint = value; continue; } - if( buffer.StartsWith( wxT("TimeStamp =" ) ) ) + if( buffer.StartsWith( wxT( "TimeStamp =" ) ) ) { timestamp = value; continue; } } - // Find the corresponding item in module info list: - for( unsigned ii = 0; ii < m_componentsInNetlist.size(); ii++ ) + // Find the corresponding item in component list: + COMPONENT* component = aNetlist->GetComponentByReference( reference ); + + // This cannot happen with a valid file. + if( component == NULL ) { - COMPONENT_INFO * cmp_info = m_componentsInNetlist[ii]; - if( m_UseTimeStamp ) // Use schematic timestamp to locate the footprint - { - if( cmp_info->m_TimeStamp.CmpNoCase( timestamp ) == 0 && - !timestamp.IsEmpty() ) - { // Found - if( !footprint.IsEmpty() ) - cmp_info->m_Footprint = footprint; - break; - } - } - else // Use schematic reference to locate the footprint - { - if( cmp_info->m_Reference.CmpNoCase( refcurrcmp ) == 0 ) // Found! - { - if( !footprint.IsEmpty() ) - cmp_info->m_Footprint = footprint; - break; - } - } + wxString msg; + msg.Printf( _( "Cannot find component \'%s\' in footprint assignment file." ), + GetChars( reference ) ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), m_lineReader->Line(), + m_lineReader->LineNumber(), m_lineReader->Length() ); } + + component->SetFootprintLibName( footprint ); } - - return true; -} - - -/* Function to sort the footprint list, used by loadNewModules. - * the given list is sorted by name - */ -#ifdef PCBNEW -static bool SortByLibName( COMPONENT_INFO* ref, COMPONENT_INFO* cmp ) -{ - int ii = ref->m_Footprint.CmpNoCase( cmp->m_Footprint ); - return ii > 0; -} -#endif - -/* Load new modules from library. - * If a new module is already loaded it is duplicated, which avoid multiple - * unnecessary disk or net access to read libraries. - * return false if a footprint is not found, true if OK - */ -bool NETLIST_READER::loadNewModules() -{ - bool success = true; -#ifdef PCBNEW - COMPONENT_INFO* ref_info, * cmp_info; - MODULE* Module = NULL; - wxPoint ModuleBestPosition; - BOARD* pcb = m_pcbframe->GetBoard(); - - if( m_newModulesList.size() == 0 ) - return true; - - sort( m_newModulesList.begin(), m_newModulesList.end(), SortByLibName ); - - // Calculate the footprint "best" position: - EDA_RECT bbbox = pcb->ComputeBoundingBox( true ); - - if( bbbox.GetWidth() || bbbox.GetHeight() ) - { - ModuleBestPosition = bbbox.GetEnd(); - ModuleBestPosition.y += 5000; - } - - ref_info = cmp_info = m_newModulesList[0]; - - for( unsigned ii = 0; ii < m_newModulesList.size(); ii++ ) - { - cmp_info = m_newModulesList[ii]; - - if( (ii == 0) || ( ref_info->m_Footprint != cmp_info->m_Footprint) ) - { - // New footprint : must be loaded from a library - Module = m_pcbframe->GetModuleLibrary( wxEmptyString, - cmp_info->m_Footprint, false ); - ref_info = cmp_info; - - if( Module == NULL ) - { - success = false; - if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Component %s: footprint %s not found" ), - GetChars( cmp_info->m_Reference ), - GetChars( cmp_info->m_Footprint ) ); - - msg += wxT("\n"); - m_messageWindow->AppendText( msg ); - } - continue; - } - - Module->SetPosition( ModuleBestPosition ); - - /* Update schematic links : reference "Time Stamp" and schematic - * hierarchical path */ - Module->SetReference( cmp_info->m_Reference ); - Module->SetTimeStamp( GetNewTimeStamp() ); - Module->SetPath( cmp_info->m_TimeStamp ); - } - else - { - // Footprint already loaded from a library, duplicate it (faster) - if( Module == NULL ) - continue; // Module does not exist in library. - - MODULE* newmodule = new MODULE( *Module ); - newmodule->SetParent( pcb ); - - pcb->Add( newmodule, ADD_APPEND ); - - Module = newmodule; - Module->SetReference( cmp_info->m_Reference ); - Module->SetTimeStamp( GetNewTimeStamp() ); - Module->SetPath( cmp_info->m_TimeStamp ); - } - } -#endif - return success; } diff --git a/pcbnew/netlist_reader_firstformat.cpp b/pcbnew/netlist_reader_firstformat.cpp index c98b1eb122..57a7ec7e75 100644 --- a/pcbnew/netlist_reader_firstformat.cpp +++ b/pcbnew/netlist_reader_firstformat.cpp @@ -6,6 +6,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 1992-2011 Jean-Pierre Charras. + * Copyright (C) 2013 Wayne Stambaugh . * Copyright (C) 1992-2011 KiCad Developers, see change_log.txt for contributors. * * This program is free software; you can redistribute it and/or @@ -26,67 +27,23 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - -/* - * Netlist reader using the first format of pcbnew netlist. - * This netlist reader build the list of modules found in netlist - * (list in m_componentsInNetlist) - * and update pads netnames - */ - #include -#include -#include #include - -#include -#include -#include +#include #include -#include - -// constants used by ReadOldFmtNetlistModuleDescr(): -#define BUILDLIST true -#define READMODULE false -/* - * Function ReadOldFmtdNetList - * Update footprints (load missing footprints and delete on request extra - * footprints) - * Update References, values, "TIME STAMP" and connectivity data - * return true if Ok - * - * the format of the netlist is something like: - * # EESchema Netlist Version 1.0 generee le 18/5/2005-12:30:22 - * ( - * ( 40C08647 $noname R20 4,7K {Lib=R} - * ( 1 VCC ) - * ( 2 MODB_1 ) - * ) - * ( 40C0863F $noname R18 4,7_k {Lib=R} - * ( 1 VCC ) - * ( 2 MODA_1 ) - * ) - * } - * #End - */ -bool NETLIST_READER::ReadOldFmtdNetList( FILE* aFile ) + +void LEGACY_NETLIST_READER::LoadNetlist() throw ( IO_ERROR, PARSE_ERROR ) { - int state = 0; - bool is_comment = false; + int state = 0; + bool is_comment = false; + COMPONENT* component = NULL; - /* First, read the netlist: Build the list of footprints found in netlist - */ - - // netlineReader dtor will close aFile - FILE_LINE_READER netlineReader( aFile, m_netlistFullName ); - COMPONENT_INFO* curComponent = NULL; - - while( netlineReader.ReadLine() ) + while( m_lineReader->ReadLine() ) { - char* line = StrPurge( netlineReader.Line() ); + char* line = StrPurge( m_lineReader->Line() ); if( is_comment ) // Comments in progress { @@ -96,92 +53,17 @@ bool NETLIST_READER::ReadOldFmtdNetList( FILE* aFile ) is_comment = false; } + if( *line == '{' ) // Start Comment or Pcbnew info section { is_comment = true; - if( ReadLibpartSectionOpt() && state == 0 && - (strnicmp( line, "{ Allowed footprints", 20 ) == 0) ) + + if( m_loadFootprintFilters && state == 0 + && (strnicmp( line, "{ Allowed footprints", 20 ) == 0) ) { - ReadOldFmtFootprintFilterList( netlineReader ); + loadFootprintFilters(); continue; } - if( ( line = strchr( line, '}' ) ) == NULL ) - continue; - } - - if( *line == '(' ) - state++; - - if( *line == ')' ) - state--; - - if( state == 2 ) - { - curComponent = (COMPONENT_INFO*) ReadOldFmtNetlistModuleDescr( line, BUILDLIST ); - continue; - } - - if( state >= 3 ) // First pass: pad descriptions are not read here. - { - if( curComponent ) - curComponent->m_pinCount++; - - state--; - } - } - - if( IsCvPcbMode() ) - { - for( ; ; ) - { - /* Search the beginning of Allowed footprints section */ - - if( netlineReader.ReadLine( ) == 0 ) - break; - char* line = StrPurge( netlineReader.Line() ); - if( strnicmp( line, "{ Allowed footprints", 20 ) == 0 ) - { - ReadOldFmtFootprintFilterList( netlineReader ); - return true; - } - } - return true; - } - - if( BuildModuleListOnlyOpt() ) - return true; // at this point, the module list is read and built. - - // Load new footprints - bool success = InitializeModules(); - - if( !success ) - wxMessageBox( _( "Some footprints are not found in libraries" ) ); - - TestFootprintsMatchingAndExchange(); - - /* Second read , All footprints are on board. - * Update the schematic info (pad netnames) - */ - netlineReader.Rewind(); - m_currModule = NULL; - state = 0; - is_comment = false; - - while( netlineReader.ReadLine() ) - { - char* line = StrPurge( netlineReader.Line() ); - - if( is_comment ) // we are reading a comment - { - // Test for end of the current comment - if( ( line = strchr( line, '}' ) ) == NULL ) - continue; - is_comment = false; - } - - if( *line == '{' ) // this is the beginning of a comment - { - is_comment = true; if( ( line = strchr( line, '}' ) ) == NULL ) continue; @@ -195,190 +77,142 @@ bool NETLIST_READER::ReadOldFmtdNetList( FILE* aFile ) if( state == 2 ) { - m_currModule = (MODULE*) ReadOldFmtNetlistModuleDescr( line, READMODULE ); + component = loadComponent( line ); continue; } - if( state >= 3 ) + if( state >= 3 ) // Pad descriptions are read here. { - if( m_currModule ) - SetPadNetName( line ); + wxASSERT( component != NULL ); + + loadNet( line, component ); state--; } } - return true; + if( m_footprintReader ) + { + m_footprintReader->Load( m_netlist ); + } } -/* Function ReadOldFmtNetlistModuleDescr - * Read the beginning of a footprint description, from the netlist - * and add a module info to m_componentsInNetlist - * Analyze the first line of a component description in netlist like: - * ( /40C08647 $noname R20 4.7K {Lib=R} - * (1 VCC) - * (2 MODB_1) - * ) - */ -void* NETLIST_READER::ReadOldFmtNetlistModuleDescr( char* aText, bool aBuildList ) +COMPONENT* LEGACY_NETLIST_READER::loadComponent( char* aText ) throw( PARSE_ERROR ) { char* text; - wxString timeStampPath; // the full time stamp read from netlist - wxString footprintName; // the footprint name read from netlist - wxString cmpValue; // the component value read from netlist - wxString cmpReference; // the component schematic reference read from netlist - bool error = false; + wxString msg; + wxString timeStamp; // the full time stamp read from netlist + wxString name; // the component name read from netlist + wxString value; // the component value read from netlist + wxString reference; // the component schematic reference designator read from netlist char line[1024]; strcpy( line, aText ); - cmpValue = wxT( "~" ); + value = wxT( "~" ); - // Read descr line like /40C08647 $noname R20 4.7K {Lib=R} + // Sample component line: /40C08647 $noname R20 4.7K {Lib=R} // Read time stamp (first word) if( ( text = strtok( line, " ()\t\n" ) ) == NULL ) - error = true; - else - timeStampPath = FROM_UTF8( text ); + { + msg = _( "Cannot parse time stamp in component section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), line, m_lineReader->LineNumber(), + m_lineReader->Length() ); + } + + timeStamp = FROM_UTF8( text ); // Read footprint name (second word) if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL ) - error = true; - else - footprintName = FROM_UTF8( text ); + { + msg = _( "Cannot parse name in component section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), aText, m_lineReader->LineNumber(), + m_lineReader->Length() ); + } - // Read schematic reference (third word) + name = FROM_UTF8( text ); + + // Read schematic reference designator (third word) if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL ) - error = true; - else - cmpReference = FROM_UTF8( text ); + { + msg = _( "Cannot parse reference designator in component section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), aText, m_lineReader->LineNumber(), + m_lineReader->Length() ); + } + + reference = FROM_UTF8( text ); // Read schematic value (forth word) if( ( text = strtok( NULL, " ()\t\n" ) ) == NULL ) - error = true; - else - cmpValue = FROM_UTF8( text ); - - if( error ) - return NULL; - - if( aBuildList ) { - COMPONENT_INFO* cmp_info = new COMPONENT_INFO( footprintName, cmpReference, - cmpValue, timeStampPath ); - AddModuleInfo( cmp_info ); - return cmp_info; + msg = _( "Cannot parse value in component section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), aText, m_lineReader->LineNumber(), + m_lineReader->Length() ); } - // search the module loaded on board - // reference and time stamps are already updated so we can use search by reference only - MODULE* module = m_pcbframe->GetBoard()->FindModuleByReference( cmpReference ); - if( module == NULL ) - { - if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Component %s not found" ), GetChars( cmpReference ) ); - m_messageWindow->AppendText( msg + wxT( "\n" ) ); - } - } + value = FROM_UTF8( text ); - return module; + COMPONENT* component = new COMPONENT( name, reference, value, timeStamp ); + m_netlist->AddComponent( component ); + return component; } -/* - * Function SetPadNetName - * Update a pad netname using the current footprint - * Line format: ( = ) - * Param aText = current line read from netlist - */ -bool NETLIST_READER::SetPadNetName( char* aText ) +void LEGACY_NETLIST_READER::loadNet( char* aText, COMPONENT* aComponent ) throw( PARSE_ERROR ) { - char* p; - char line[256]; + wxString msg; + char* p; + char line[256]; - if( m_currModule == NULL ) - return false; - - strncpy( line, aText, sizeof(line) ); + strncpy( line, aText, sizeof( line ) ); if( ( p = strtok( line, " ()\t\n" ) ) == NULL ) - return false; + { + msg = _( "Cannot parse pin name in component net section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), line, m_lineReader->LineNumber(), + m_lineReader->Length() ); + } wxString pinName = FROM_UTF8( p ); if( ( p = strtok( NULL, " ()\t\n" ) ) == NULL ) - return false; + { + msg = _( "Cannot parse net name in component net section of netlist." ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), line, m_lineReader->LineNumber(), + m_lineReader->Length() ); + } wxString netName = FROM_UTF8( p ); - bool found = false; - for( D_PAD* pad = m_currModule->Pads(); pad; pad = pad->Next() ) - { - wxString padName = pad->GetPadName(); + if( (char) netName[0] == '?' ) // ? indicates no net connected to pin. + netName = wxEmptyString; - if( padName == pinName ) - { - found = true; - if( (char) netName[0] != '?' ) - pad->SetNetname( netName ); - else - pad->SetNetname( wxEmptyString ); - } - } - - if( !found ) - { - if( m_messageWindow ) - { - wxString msg; - msg.Printf( _( "Module %s: Pad %s not found" ), - GetChars( m_currModule->GetReference() ), - GetChars( pinName ) ); - m_messageWindow->AppendText( msg + wxT( "\n" ) ); - } - } - - return found; + aComponent->AddNet( pinName, netName ); } -/* - * Read the section "Allowed footprints" like: - * { Allowed footprints by component: - * $component R11 - * R? - * SM0603 - * SM0805 - * R?-* - * SM1206 - * $endlist - * $endfootprintlist - * } - * - * And add the strings giving the footprint filter to m_FootprintFilter - * of the corresponding module info - * This section is used by CvPcb, and is not useful in Pcbnew, - * therefore it it not always read - */ -bool NETLIST_READER::ReadOldFmtFootprintFilterList( FILE_LINE_READER& aNetlistReader ) +void LEGACY_NETLIST_READER::loadFootprintFilters() throw( IO_ERROR, PARSE_ERROR ) { - wxString cmpRef; - COMPONENT_INFO* cmp_info = NULL; - char* line; + wxArrayString filters; + wxString cmpRef; + char* line; + COMPONENT* component; - while( ( line = aNetlistReader.ReadLine() ) != NULL ) + while( ( line = m_lineReader->ReadLine() ) != NULL ) { - if( strnicmp( line, "$endlist", 8 ) == 0 ) // end of list for the current component + if( strnicmp( line, "$endlist", 8 ) == 0 ) // end of list for the current component { - cmp_info = NULL; + wxASSERT( component != NULL ); + component->SetFootprintFilters( filters ); + component = NULL; + filters.Clear(); continue; } + if( strnicmp( line, "$endfootprintlist", 4 ) == 0 ) // End of this section - return 0; + return; if( strnicmp( line, "$component", 10 ) == 0 ) // New component reference found { @@ -386,25 +220,25 @@ bool NETLIST_READER::ReadOldFmtFootprintFilterList( FILE_LINE_READER& aNetlistR cmpRef.Trim( true ); cmpRef.Trim( false ); - // Search the current component in module info list: - BOOST_FOREACH( COMPONENT_INFO * &component, m_componentsInNetlist ) + component = m_netlist->GetComponentByReference( cmpRef ); + + // Cannot happen if the netlist is valid. + if( component == NULL ) { - if( component->m_Reference == cmpRef ) - { - cmp_info = component; - break; - } + wxString msg; + msg.Printf( _( "Cannot find component \'%s\' in footprint filter section " + "of netlist." ), GetChars( cmpRef ) ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), line, m_lineReader->LineNumber(), + m_lineReader->Length() ); } } - else if( cmp_info ) + else { // Add new filter to list wxString fp = FROM_UTF8( line + 1 ); fp.Trim( false ); fp.Trim( true ); - cmp_info->m_FootprintFilter.Add( fp ); + filters.Add( fp ); } } - - return true; } diff --git a/pcbnew/netlist_reader_kicad.cpp b/pcbnew/netlist_reader_kicad.cpp index 6b7de75230..424d7cb963 100644 --- a/pcbnew/netlist_reader_kicad.cpp +++ b/pcbnew/netlist_reader_kicad.cpp @@ -27,142 +27,50 @@ #include #include // netlist_lexer is common to Eeschema and Pcbnew +#include #include using namespace NL_T; -/** - * Class PCB_PLOT_PARAMS_PARSER - * is the parser class for PCB_PLOT_PARAMS. - */ -class NETLIST_READER_KICAD_PARSER : public NETLIST_LEXER + +void KICAD_NETLIST_READER::LoadNetlist() throw ( IO_ERROR, PARSE_ERROR ) { -private: - T token; - NETLIST_READER * netlist_reader; + m_parser->Parse(); -public: - NETLIST_READER_KICAD_PARSER( FILE_LINE_READER* aReader, NETLIST_READER *aNetlistReader ); - - /** - * Function Parse - * parse the full netlist - */ - void Parse( BOARD * aBrd ) throw( IO_ERROR, PARSE_ERROR ); - - /** - * Function ParseComp - * parse the comp description like - * (comp (ref P1) - * (value DB25FEMELLE) - * (footprint DB25FC) - * (libsource (lib conn) (part DB25)) - * (sheetpath (names /) (tstamps /)) - * (tstamp 3256759C)) - */ - COMPONENT_INFO* ParseComp() throw( IO_ERROR, PARSE_ERROR ); - - - /** - * Function ParseKicadLibpartList - * Read the section "libparts" like: - * (libparts - * (libpart (lib device) (part C) - * (description "Condensateur non polarise") - * (footprints - * (fp SM*) - * (fp C?) - * (fp C1-1)) - * (fields - * (field (name Reference) C) - * (field (name Value) C)) - * (pins - * (pin (num 1) (name ~) (type passive)) - * (pin (num 2) (name ~) (type passive)))) - * - * And add the strings giving the footprint filter (subsection footprints) - * of the corresponding module info - *

This section is used by CvPcb, and is not useful in Pcbnew, - * therefore it it not always read

- */ - void ParseKicadLibpartList() throw( IO_ERROR, PARSE_ERROR ); - - /** - * Function ParseNet - * Parses a section like - * (net (code 20) (name /PC-A0) - * (node (ref BUS1) (pin 62)) - * (node (ref U3) (pin 3)) - * (node (ref U9) (pin M6))) - * - * and set the corresponfings pads netnames - */ - void ParseNet( BOARD * aBrd ) throw( IO_ERROR, PARSE_ERROR ); - - /** - * Function SkipCurrent - * Skip the current token level, i.e - * search for the RIGHT parenthesis which closes the current description - */ - void SkipCurrent() throw( IO_ERROR, PARSE_ERROR ); - - // Useful for debug only: - const char* getTokenName( T aTok ) + if( m_footprintReader ) { - return NETLIST_LEXER::TokenName( aTok ); + m_footprintReader->Load( m_netlist ); + + // Sort the component pins so they are in the same order as the legacy format. This + // is useful for comparing legacy and s-expression netlist dumps. + for( unsigned i = 0; i < m_netlist->GetCount(); i++ ) + m_netlist->GetComponent( i )->SortPins(); } -}; - - -bool NETLIST_READER::ReadKicadNetList( FILE* aFile ) -{ - BOARD * brd = m_pcbframe ? m_pcbframe->GetBoard() : NULL; - - // netlineReader dtor will close aFile - FILE_LINE_READER netlineReader( aFile, m_netlistFullName ); - NETLIST_READER_KICAD_PARSER netlist_parser( &netlineReader, this ); - - try - { - netlist_parser.Parse( brd ); - } - catch( IO_ERROR& ioe ) - { - ioe.errorText += '\n'; - ioe.errorText += _("Netlist error."); - - wxMessageBox( ioe.errorText ); - return false; - } - - return true; } - -// NETLIST_READER_KICAD_PARSER -NETLIST_READER_KICAD_PARSER::NETLIST_READER_KICAD_PARSER( FILE_LINE_READER* aReader, - NETLIST_READER *aNetlistReader ) : +// KICAD_NETLIST_PARSER +KICAD_NETLIST_PARSER::KICAD_NETLIST_PARSER( LINE_READER* aReader, NETLIST* aNetlist ) : NETLIST_LEXER( aReader ) { - netlist_reader = aNetlistReader; + m_lineReader = aReader; + m_netlist = aNetlist; } -/** - * Function SkipCurrent - * Skip the current token level, i.e - * search for the RIGHT parenthesis which closes the current description - */ -void NETLIST_READER_KICAD_PARSER::SkipCurrent() throw( IO_ERROR, PARSE_ERROR ) + +void KICAD_NETLIST_PARSER::skipCurrent() throw( IO_ERROR, PARSE_ERROR ) { int curr_level = 0; + while( ( token = NextTok() ) != T_EOF ) { if( token == T_LEFT ) curr_level--; + if( token == T_RIGHT ) { curr_level++; + if( curr_level > 0 ) return; } @@ -170,9 +78,10 @@ void NETLIST_READER_KICAD_PARSER::SkipCurrent() throw( IO_ERROR, PARSE_ERROR ) } -void NETLIST_READER_KICAD_PARSER::Parse( BOARD * aBrd ) - throw( IO_ERROR, PARSE_ERROR ) +void KICAD_NETLIST_PARSER::Parse() throw( IO_ERROR, PARSE_ERROR ) { + wxString text; + int plevel = 0; // the count of ')' to read and end of file, // after parsing all sections @@ -183,98 +92,91 @@ void NETLIST_READER_KICAD_PARSER::Parse( BOARD * aBrd ) switch( token ) { - case T_export: // The netlist starts here. - // nothing to do here, - // just increment the count of ')' to read and end of file - plevel++; - break; + case T_export: // The netlist starts here. + // nothing to do here, + // just increment the count of ')' to read and end of file + plevel++; + break; - case T_version: // The netlist starts here. - // version id not yet used: read it but does not use it - NextTok(); - NeedRIGHT(); - break; + case T_version: // The netlist starts here. + // version id not yet used: read it but does not use it + NextTok(); + NeedRIGHT(); + break; - case T_components: // The section comp starts here. - while( ( token = NextTok() ) != T_RIGHT ) + case T_components: // The section comp starts here. + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_comp ) // A component section found. Read it + parseComponent(); + } + + break; + + case T_nets: // The section nets starts here. + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_net ) { - if( token == T_LEFT ) - token = NextTok(); - if( token == T_comp ) // A comp section if found. Read it - { - COMPONENT_INFO* cmp_info = ParseComp(); - netlist_reader->AddModuleInfo( cmp_info ); - } + // A net section if found. Read it + parseNet(); } - if( netlist_reader->BuildModuleListOnlyOpt() ) - return; // at this point, the module list is read and built. - // Load new footprints - netlist_reader->InitializeModules(); - netlist_reader->TestFootprintsMatchingAndExchange(); - break; + } - case T_nets: // The section nets starts here. - while( ( token = NextTok() ) != T_RIGHT ) + break; + + case T_libparts: // The section libparts starts here. + while( ( token = NextTok() ) != T_RIGHT ) + { + if( token == T_LEFT ) + token = NextTok(); + + if( token == T_libpart ) { - if( token == T_LEFT ) - token = NextTok(); - if( token == T_net ) - { - // A net section if found. Read it - ParseNet( aBrd ); - } + // A libpart section if found. Read it + parseLibPartList(); } - break; + } - case T_libparts: // The section libparts starts here. - if( netlist_reader->ReadLibpartSectionOpt() ) - { - while( ( token = NextTok() ) != T_RIGHT ) - { - if( token == T_LEFT ) - token = NextTok(); - if( token == T_libpart ) - { - // A libpart section if found. Read it - ParseKicadLibpartList(); - } - } - } - else - SkipCurrent(); - break; + break; - case T_libraries: // The section libraries starts here. - // List of libraries in use. - // Not used here, just skip it - SkipCurrent(); - break; + case T_libraries: // The section libraries starts here. + // List of libraries in use. + // Not used here, just skip it + skipCurrent(); + break; - case T_design: // The section design starts here. - // Not used (mainly thet are comments), just skip it - SkipCurrent(); - break; + case T_design: // The section design starts here. + // Not used (mainly they are comments), just skip it + skipCurrent(); + break; - case T_RIGHT: // The closing parenthesis of the file. - // Not used (mainly thet are comments), just skip it - plevel--; - break; + case T_RIGHT: // The closing parenthesis of the file. + // Not used (mainly they are comments), just skip it + plevel--; + break; - default: - SkipCurrent(); - break; + default: + skipCurrent(); + break; } } if( plevel != 0 ) { - wxLogDebug(wxT("NETLIST_READER_KICAD_PARSER::Parse(): bad parenthesis count (count = %d"), + wxLogDebug( wxT( "KICAD_NETLIST_PARSER::Parse(): bad parenthesis count (count = %d"), plevel ); } } -void NETLIST_READER_KICAD_PARSER::ParseNet( BOARD * aBrd ) - throw( IO_ERROR, PARSE_ERROR ) + +void KICAD_NETLIST_PARSER::parseNet() throw( IO_ERROR, PARSE_ERROR ) { /* Parses a section like * (net (code 20) (name /PC-A0) @@ -283,32 +185,35 @@ void NETLIST_READER_KICAD_PARSER::ParseNet( BOARD * aBrd ) * (node (ref U9) (pin M6))) */ - wxString code; - wxString name; - wxString cmpref; - wxString pin; - int nodecount = 0; - std::vector padList; + COMPONENT* component = NULL; + wxString code; + wxString name; + wxString reference; + wxString pin; + int nodecount = 0; // The token net was read, so the next data is (code ) while( (token = NextTok()) != T_RIGHT ) { if( token == T_LEFT ) token = NextTok(); + switch( token ) { case T_code: NeedSYMBOLorNUMBER(); code = FROM_UTF8( CurText() ); NeedRIGHT(); - break; + break; case T_name: NeedSYMBOLorNUMBER(); name = FROM_UTF8( CurText() ); NeedRIGHT(); + if( name.IsEmpty() ) // Give a dummy net name like N-000109 name = wxT("N-00000") + code; + break; case T_node: @@ -316,11 +221,12 @@ void NETLIST_READER_KICAD_PARSER::ParseNet( BOARD * aBrd ) { if( token == T_LEFT ) token = NextTok(); + switch( token ) { case T_ref: NeedSYMBOLorNUMBER(); - cmpref = FROM_UTF8( CurText() ); + reference = FROM_UTF8( CurText() ); NeedRIGHT(); break; @@ -331,31 +237,37 @@ void NETLIST_READER_KICAD_PARSER::ParseNet( BOARD * aBrd ) break; default: - SkipCurrent(); + skipCurrent(); break; } } - netlist_reader->SetPadsNetName( cmpref, pin, name, padList ); + + + component = m_netlist->GetComponentByReference( reference ); + + // Cannot happen if the netlist is valid. + if( component == NULL ) + { + wxString msg; + msg.Printf( _( "Cannot find component with reference \"%s\" in netlist." ), + GetChars( reference ) ); + THROW_PARSE_ERROR( msg, m_lineReader->GetSource(), m_lineReader->Line(), + m_lineReader->LineNumber(), m_lineReader->Length() ); + } + + component->AddNet( pin, name ); nodecount++; break; default: - SkipCurrent(); + skipCurrent(); break; } } - - // When there is only one item in net, clear pad netname - // Remember one can have more than one pad in list, because a footprint - // can have many pads with the same pad name - if( nodecount < 2 ) - for( unsigned ii = 0; ii < padList.size(); ii++ ) - padList[ii]->SetNetname( wxEmptyString ); } -COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() - throw( IO_ERROR, PARSE_ERROR ) +void KICAD_NETLIST_PARSER::parseComponent() throw( IO_ERROR, PARSE_ERROR ) { /* Parses a section like * (comp (ref P1) @@ -366,13 +278,14 @@ COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() * (tstamp 3256759C)) * * other fields (unused) are skipped - * A component need a reference, value, foorprint name and a full time stamp + * A component need a reference, value, footprint name and a full time stamp * The full time stamp is the sheetpath time stamp + the component time stamp */ wxString ref; wxString value; - wxString footprint; - wxString libpart; + wxString componentName; + wxString libPartName; + wxString libName; wxString pathtimestamp, timestamp; // The token comp was read, so the next data is (ref P1) @@ -380,6 +293,7 @@ COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() { if( token == T_LEFT ) token = NextTok(); + switch( token ) { case T_ref: @@ -396,7 +310,7 @@ COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() case T_footprint: NeedSYMBOLorNUMBER(); - footprint = FROM_UTF8( CurText() ); + componentName = FROM_UTF8( CurText() ); NeedRIGHT(); break; @@ -406,14 +320,23 @@ COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() { if( token == T_LEFT ) token = NextTok(); - if( token == T_part ) + + if( token == T_lib ) { NeedSYMBOLorNUMBER(); - libpart = FROM_UTF8( CurText() ); + libName = FROM_UTF8( CurText() ); + NeedRIGHT(); + } + else if( token == T_part ) + { + NeedSYMBOLorNUMBER(); + libPartName = FROM_UTF8( CurText() ); NeedRIGHT(); } else - SkipCurrent(); + { + Expecting( "part or lib" ); + } } break; @@ -433,95 +356,92 @@ COMPONENT_INFO* NETLIST_READER_KICAD_PARSER::ParseComp() default: // Skip not used data (i.e all other tokens) - SkipCurrent(); + skipCurrent(); break; } } - pathtimestamp += timestamp; - COMPONENT_INFO* cmp_info = new COMPONENT_INFO( footprint, ref, value, pathtimestamp ); - cmp_info->m_Libpart = libpart; - return cmp_info; + pathtimestamp += timestamp; + COMPONENT* component = new COMPONENT( componentName, ref, value, pathtimestamp ); + component->SetLibrarySource( libName, libPartName ); + m_netlist->AddComponent( component ); } -/* Read the section "libparts" like: - * (libparts - * (libpart (lib device) (part C) - * (description "Condensateur non polarise") - * (footprints - * (fp SM*) - * (fp C?) - * (fp C1-1)) - * (fields - * (field (name Reference) C) - * (field (name Value) C)) - * (pins - * (pin (num 1) (name ~) (type passive)) - * (pin (num 2) (name ~) (type passive)))) - * - * And add the strings giving the footprint filter (subsection footprints) - * of the corresponding module info - */ -void NETLIST_READER_KICAD_PARSER::ParseKicadLibpartList() throw( IO_ERROR, PARSE_ERROR ) + +void KICAD_NETLIST_PARSER::parseLibPartList() throw( IO_ERROR, PARSE_ERROR ) { /* Parses a section like - * (libpart (lib device) (part C) - * (description "Condensateur non polarise") - * (footprints - * (fp SM*) - * (fp C?) - * (fp C1-1)) - * (fields - * (field (name Reference) C) - * (field (name Value) C)) - * (pins - * (pin (num 1) (name ~) (type passive)) - * (pin (num 2) (name ~) (type passive)))) - * - * Currently footprints section/fp are read and data stored - * other fields (unused) are skipped - */ - wxString device; - wxString filter; - LIPBART_INFO* libpart_info = NULL; + * (libpart (lib device) (part C) + * (description "Condensateur non polarise") + * (footprints + * (fp SM*) + * (fp C?) + * (fp C1-1)) + * (fields + * (field (name Reference) C) + * (field (name Value) C)) + * (pins + * (pin (num 1) (name ~) (type passive)) + * (pin (num 2) (name ~) (type passive)))) + * + * Currently footprints section/fp are read and data stored + * other fields (unused) are skipped + */ + COMPONENT* component = NULL; + wxString libName; + wxString libPartName; + wxArrayString footprintFilters; // The last token read was libpart, so read the next token while( (token = NextTok()) != T_RIGHT ) { if( token == T_LEFT ) token = NextTok(); + switch( token ) { + case T_lib: + NeedSYMBOLorNUMBER(); + libName = FROM_UTF8( CurText() ); + NeedRIGHT(); + break; + case T_part: NeedSYMBOLorNUMBER(); - device = FROM_UTF8( CurText() ); + libPartName = FROM_UTF8( CurText() ); NeedRIGHT(); - libpart_info = new LIPBART_INFO( device ); - netlist_reader->AddLibpartInfo( libpart_info ); break; case T_footprints: - // Ensure "(part C)" was already read - if( libpart_info == NULL ) - Expecting( T_part ); // Read all fp elements (footprint filter item) while( (token = NextTok()) != T_RIGHT ) { if( token == T_LEFT ) token = NextTok(); + if( token != T_fp ) Expecting( T_fp ); + NeedSYMBOLorNUMBER(); - filter = FROM_UTF8( CurText() ); + footprintFilters.Add( FROM_UTF8( CurText() ) ); NeedRIGHT(); - libpart_info->m_FootprintFilter.Add( filter ); } + break; default: // Skip not used data (i.e all other tokens) - SkipCurrent(); + skipCurrent(); break; } } + + // Find all of the components that reference this component library part definition. + for( unsigned i = 0; i < m_netlist->GetCount(); i++ ) + { + component = m_netlist->GetComponent( i ); + + if( component->IsLibSource( libName, libPartName ) ) + component->SetFootprintFilters( footprintFilters ); + } }