From 7c86749ca6075a8904e240f28138c08aef69e97f Mon Sep 17 00:00:00 2001 From: Wayne Stambaugh Date: Fri, 28 Dec 2012 15:52:12 -0500 Subject: [PATCH] Add Pcbnew GEDA PCB module plugin support. * Create new GEDA PCB plug in. * Add support for opening GEDA PCB footprints with module editor. * Make import footprint file dialog remember last selected footprint type during current session. * Update module editor file import to use new GEDA PCB plug in. * Let IO_MGR know about GEDA PCB plug in. * Create a WHITESPACE_FILTER_READER to simplify parsing GEDA PCB footprint files. --- common/CMakeLists.txt | 1 + common/filter_reader.cpp | 40 ++ common/wildcards_and_files_ext.cpp | 2 + include/filter_reader.h | 34 + include/wildcards_and_files_ext.h | 2 + pcbnew/class_drawsegment.cpp | 43 +- pcbnew/gpcb_plugin.cpp | 975 +++++++++++++++++++++++++++++ pcbnew/gpcb_plugin.h | 103 +++ pcbnew/io_mgr.cpp | 16 + pcbnew/io_mgr.h | 5 +- pcbnew/librairi.cpp | 72 ++- pcbnew/menubar_modedit.cpp | 3 +- 12 files changed, 1261 insertions(+), 35 deletions(-) create mode 100644 pcbnew/gpcb_plugin.cpp create mode 100644 pcbnew/gpcb_plugin.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index dee8dfa568..e00ab28bfe 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -121,6 +121,7 @@ set(PCB_COMMON_SRCS ../pcbnew/eagle_plugin.cpp ../pcbnew/legacy_plugin.cpp ../pcbnew/kicad_plugin.cpp + ../pcbnew/gpcb_plugin.cpp pcb_plot_params_keywords.cpp pcb_keywords.cpp ../pcbnew/pcb_parser.cpp diff --git a/common/filter_reader.cpp b/common/filter_reader.cpp index 20f58ceb62..7ab901866f 100644 --- a/common/filter_reader.cpp +++ b/common/filter_reader.cpp @@ -65,3 +65,43 @@ char* FILTER_READER::ReadLine() throw( IO_ERROR ) return length ? line : NULL; } + +WHITESPACE_FILTER_READER::WHITESPACE_FILTER_READER( LINE_READER& aReader ) : + LINE_READER( 1 ), + reader( aReader ) +{ + // Not using our own line buffer, will be using aReader's. This changes + // the meaning of this->line to be merely a pointer to aReader's line, which of course + // is not owned here. + delete [] line; + + line = 0; +} + + +WHITESPACE_FILTER_READER::~WHITESPACE_FILTER_READER() +{ + // Our 'line' points to aReader's, and he will delete that buffer. + // Prevent subsequent call to ~LINE_READER() from deleting a buffer we do not own. + line = 0; +} + + +char* WHITESPACE_FILTER_READER::ReadLine() throw( IO_ERROR ) +{ + char* s; + + while( ( s = reader.ReadLine() ) != NULL ) + { + while( s != NULL && strchr( " \t", *s ) ) + s++; + + if( s != NULL && !strchr( "#\n\r", *s ) ) + break; + } + + line = s; + length = reader.Length(); + + return length ? line : NULL; +} diff --git a/common/wildcards_and_files_ext.cpp b/common/wildcards_and_files_ext.cpp index 488ad91d59..8fc7c3f381 100644 --- a/common/wildcards_and_files_ext.cpp +++ b/common/wildcards_and_files_ext.cpp @@ -57,6 +57,7 @@ const wxString FootprintPlaceFileExtension( wxT( "pos" ) ); const wxString KiCadFootprintLibPathExtension( wxT( "pretty" ) ); ///< KICAD PLUGIN libpath const wxString KiCadFootprintFileExtension( wxT( "kicad_mod" ) ); +const wxString GedaPcbFootprintLibFileExtension( wxT( "fp" ) ); // These strings are wildcards for file selection dialogs. // Because these are static, one should explicitly call wxGetTranslation @@ -73,6 +74,7 @@ const wxString PcbFileWildcard( _( "KiCad s-expr printed circuit board files (*. const wxString KiCadFootprintLibFileWildcard( _( "KiCad footprint s-expre library file (*.kicad_mod)|*.kicad_mod" ) ); const wxString KiCadFootprintLibPathWildcard( _( "KiCad footprint s-expre library path (*.pretty)|*.pretty" ) ); const wxString LegacyFootprintLibPathWildcard( _( "Legacy footprint library file (*.mod)|*.mod" ) ); +const wxString GedaPcbFootprintLibFileWildcard( _( "Geda PCB footprint library file (*.fp)|*.fp" ) ); const wxString MacrosFileWildcard( _( "KiCad recorded macros (*.mcr)|*.mcr" ) ); // generic: diff --git a/include/filter_reader.h b/include/filter_reader.h index 84aa14fc59..154430230d 100644 --- a/include/filter_reader.h +++ b/include/filter_reader.h @@ -62,4 +62,38 @@ public: } }; + +/** + * Class WHITESPACE_FILTER_READER + * reads lines of text from another LINE_READER, but only returns non-comment + * lines and non-blank lines with leading whitespace characters (space and tab) + * removed from its ReadLine() function. + */ +class WHITESPACE_FILTER_READER : public LINE_READER +{ + LINE_READER& reader; + +public: + + /** + * Constructor ( LINE_READER& ) + * does not take ownership over @a aReader, so will not destroy it. + */ + WHITESPACE_FILTER_READER( LINE_READER& aReader ); + + ~WHITESPACE_FILTER_READER(); + + char* ReadLine() throw( IO_ERROR ); + + const wxString& GetSource() const + { + return reader.GetSource(); + } + + unsigned LineNumber() const + { + return reader.LineNumber(); + } +}; + #endif // FILTER_READER_H_ diff --git a/include/wildcards_and_files_ext.h b/include/wildcards_and_files_ext.h index e241155692..6ddd10437a 100644 --- a/include/wildcards_and_files_ext.h +++ b/include/wildcards_and_files_ext.h @@ -66,6 +66,7 @@ extern const wxString ReportFileExtension; extern const wxString FootprintPlaceFileExtension; extern const wxString KiCadFootprintFileExtension; extern const wxString KiCadFootprintLibPathExtension; +extern const wxString GedaPcbFootprintLibFileExtension; /// Proper wxFileDialog wild card definitions. extern const wxString SchematicSymbolFileWildcard; @@ -92,5 +93,6 @@ extern const wxString DocModulesFileName; extern const wxString LegacyFootprintLibPathWildcard; extern const wxString KiCadFootprintLibFileWildcard; extern const wxString KiCadFootprintLibPathWildcard; +extern const wxString GedaPcbFootprintLibFileWildcard; #endif // INCLUDE_WILDCARDS_AND_FILES_EXT_H_ diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index e591022d71..da06ceee47 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -265,31 +265,34 @@ void DRAWSEGMENT::Draw( EDA_DRAW_PANEL* panel, wxDC* DC, GR_DRAWMODE draw_mode, radius, m_Width, color ); } break; - case S_CURVE: - m_BezierPoints = Bezier2Poly(m_Start, m_BezierC1, m_BezierC2, m_End); - for (unsigned int i=1; i < m_BezierPoints.size(); i++) { - if( mode == LINE ) - GRLine( panel->GetClipBox(), DC, - m_BezierPoints[i].x, m_BezierPoints[i].y, - m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, 0, - color ); - else if( mode == SKETCH ) - { - GRCSegm( panel->GetClipBox(), DC, + case S_CURVE: + m_BezierPoints = Bezier2Poly(m_Start, m_BezierC1, m_BezierC2, m_End); + + for (unsigned int i=1; i < m_BezierPoints.size(); i++) { + if( mode == LINE ) + GRLine( panel->GetClipBox(), DC, + m_BezierPoints[i].x, m_BezierPoints[i].y, + m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, 0, + color ); + else if( mode == SKETCH ) + { + GRCSegm( panel->GetClipBox(), DC, + m_BezierPoints[i].x, m_BezierPoints[i].y, + m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, + m_Width, color ); + } + else + { + GRFillCSegm( panel->GetClipBox(), DC, m_BezierPoints[i].x, m_BezierPoints[i].y, m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, m_Width, color ); - } - else - { - GRFillCSegm( panel->GetClipBox(), DC, - m_BezierPoints[i].x, m_BezierPoints[i].y, - m_BezierPoints[i-1].x, m_BezierPoints[i-1].y, - m_Width, color ); - } } - break; + } + + break; + default: if( mode == LINE ) { diff --git a/pcbnew/gpcb_plugin.cpp b/pcbnew/gpcb_plugin.cpp new file mode 100644 index 0000000000..0edd498b1c --- /dev/null +++ b/pcbnew/gpcb_plugin.cpp @@ -0,0 +1,975 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Wayne Stambaugh + * Copyright (C) 1992-2012 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 + */ + +/** + * @file gpcb_plugin.cpp + * @brief Geda PCB file plugin implementation file. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace std; + + +/** + * Definition for enabling and disabling footprint library trace output. See the + * wxWidgets documentation on using the WXTRACE environment variable. + */ +static const wxString traceFootprintLibrary( wxT( "GedaPcbFootprintLib" ) ); + + +static const char delims[] = " \t\r\n"; + +static bool inline isSpace( int c ) { return strchr( delims, c ) != 0; } + +static void inline traceParams( wxArrayString& aParams ) +{ + wxString tmp; + + for( unsigned i = 0; i < aParams.GetCount(); i++ ) + { + if( aParams[i].IsEmpty() ) + tmp << wxT( "\"\" " ); + else + tmp << aParams[i] << wxT( " " ); + } + + wxLogTrace( traceFootprintLibrary, tmp ); +} + + +static inline long parseInt( const wxString& aValue ) +{ + long value; + + if( aValue.ToLong( &value ) ) + return value; + + THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ), + aValue.GetData() ) ); +} + + +static inline long parseInt( const wxString& aValue, double aScalar ) +{ + return KiROUND( parseInt( aValue ) * aScalar ); +} + + +// Tracing for token parameter arrays. +#ifdef DEBUG +#define TRACE_PARAMS( arr ) traceParams( arr ); +#else +#define TRACE_PARAMS( arr ) // Expands to nothing on non-debug builds. +#endif + + +/** + * Class GPCB_FPL_CACHE_ITEM + * is helper class for creating a footprint library cache. + * + * The new footprint library design is a file path of individual module files + * that contain a single module per file. This class is a helper only for the + * footprint portion of the PLUGIN API, and only for the #PCB_IO plugin. It is + * private to this implementation file so it is not placed into a header. + */ +class GPCB_FPL_CACHE_ITEM +{ + wxFileName m_file_name; ///< The the full file name and path of the footprint to cache. + bool m_writable; ///< Writability status of the footprint file. + wxDateTime m_mod_time; ///< The last file modified time stamp. + auto_ptr< MODULE > m_module; + +public: + GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ); + + wxString GetName() const { return m_file_name.GetDirs().Last(); } + wxFileName GetFileName() const { return m_file_name; } + bool IsModified() const; + MODULE* GetModule() const { return m_module.get(); } + void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); } +}; + + +GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) : + m_module( aModule ) +{ + m_file_name = aFileName; + m_mod_time.Now(); +} + + +bool GPCB_FPL_CACHE_ITEM::IsModified() const +{ + return m_file_name.GetModificationTime() != m_mod_time; +} + + +typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP; +typedef MODULE_MAP::iterator MODULE_ITER; +typedef MODULE_MAP::const_iterator MODULE_CITER; + + +class GPCB_FPL_CACHE +{ + GPCB_PLUGIN* m_owner; /// Plugin object that owns the cache. + wxFileName m_lib_path; /// The path of the library. + wxDateTime m_mod_time; /// Footprint library path modified time stamp. + MODULE_MAP m_modules; /// Map of footprint file name per MODULE*. + + MODULE* parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR ); + + /** + * Function testFlags + * tests \a aFlag for \a aMask or \a aName. + * @param aFlag = List of flags to test against: can be a bit field flag or a list name flag + * a bit field flag is an hexadecimal value: Ox00020000 + * a list name flag is a string list of flags, comma separated like square,option1 + * @param aMask = flag list to test + * @param aName = flag name to find in list + * @return true if found + */ + bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName ); + + /** + * Function parseParameters + * extracts parameters and tokens from \a aLineReader and adds them to \a aParameterList. + * + * Delimiter characters are: + * [ ] ( ) Begin and end of parameter list and units indicator + * " is a string delimiter + * space is the param separator + * The first word is the keyword + * the second item is one of ( or [ + * other are parameters (number or delimited string) + * last parameter is ) or ] + * + * @param aParameterList. This list of parameters parsed. + * @param aLineReader The line reader object to parse. + */ + void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader ); + +public: + GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath ); + + wxString GetPath() const { return m_lib_path.GetPath(); } + wxDateTime GetLastModificationTime() const { return m_mod_time; } + bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); } + MODULE_MAP& GetModules() { return m_modules; } + + // Most all functions in this class throw IO_ERROR exceptions. There are no + // error codes nor user interface calls from here, nor in any PLUGIN. + // Catch these exceptions higher up please. + + /// Save not implemented for the Geda PCB footprint library format. + + void Load(); + + void Remove( const wxString& aFootprintName ); + + wxDateTime GetLibModificationTime(); + + bool IsModified(); +}; + + +GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath ) +{ + m_owner = aOwner; + m_lib_path.SetPath( aLibraryPath ); +} + + +wxDateTime GPCB_FPL_CACHE::GetLibModificationTime() +{ + return m_lib_path.GetModificationTime(); +} + + +void GPCB_FPL_CACHE::Load() +{ + wxDir dir( m_lib_path.GetPath() ); + + if( !dir.IsOpened() ) + { + THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ), + m_lib_path.GetPath().GetData() ) ); + } + + wxString fpFileName; + wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension; + + if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) ) + return; + + do + { + wxFileName fn = fpFileName; + fn.SetPath( m_lib_path.GetPath() ); + + // reader now owns fp, will close on exception or return + FILE_LINE_READER reader( fn.GetFullPath() ); + std::string name = TO_UTF8( fn.GetName() ); + MODULE* module = parseMODULE( &reader ); + + // Set the module name to the file name sans path and extension. + if( module->Reference().GetText().IsEmpty() ) + { + module->Reference().SetText( fn.GetName() ); + } + + m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( module, fn.GetName() ) ); + + } while( dir.GetNext( &fpFileName ) ); + + // Remember the file modification time of library file when the + // cache snapshot was made, so that in a networked environment we will + // reload the cache as needed. + m_mod_time = GetLibModificationTime(); +} + + +void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName ) +{ + + std::string footprintName = TO_UTF8( aFootprintName ); + + MODULE_CITER it = m_modules.find( footprintName ); + + if( it == m_modules.end() ) + { + THROW_IO_ERROR( wxString::Format( _( "library '%s' has no footprint '%s' to delete" ), + m_lib_path.GetPath().GetData(), + aFootprintName.GetData() ) ); + } + + // Remove the module from the cache and delete the module file from the library. + wxString fullPath = it->second->GetFileName().GetFullPath(); + m_modules.erase( footprintName ); + wxRemoveFile( fullPath ); +} + + +bool GPCB_FPL_CACHE::IsModified() +{ + if( !m_lib_path.DirExists() ) + return true; + + for( MODULE_ITER it = m_modules.begin(); it != m_modules.end(); ++it ) + { + wxFileName fn = it->second->GetFileName(); + + if( !fn.FileExists() ) + { + wxLogTrace( traceFootprintLibrary, wxT( "Footprint cache file '%s' does not exist." ), + fn.GetFullPath().GetData() ); + return true; + } + + if( it->second->IsModified() ) + { + wxLogTrace( traceFootprintLibrary, + wxT( "Footprint cache file '%s' has been modified." ), + fn.GetFullPath().GetData() ); + return true; + } + } + + return false; +} + + +MODULE* GPCB_FPL_CACHE::parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR ) +{ + #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS ) + #define OLD_GPCB_UNIT_CONV IU_PER_MILS + + // Old version unit = 1 mil, so conv_unit is 10 or 0.1 + #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS ) + + int paramCnt; + double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1 + wxPoint refPos( 0, 0 ); + wxPoint textPos; + wxString msg; + wxArrayString parameters; + auto_ptr< MODULE > module( new MODULE( NULL ) ); + + + if( aLineReader->ReadLine() == NULL ) + THROW_IO_ERROR( "unexpected end of file" ); + + parameters.Clear(); + parseParameters( parameters, aLineReader ); + paramCnt = parameters.GetCount(); + + /* From the Geda PCB documentation, valid Element definitions: + * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags] + * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags) + * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags) + * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags) + * Element ("Desc" "Name" TX TY TDir TScale TNFlags) + */ + + if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 ) + { + msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + if( paramCnt < 10 || paramCnt > 14 ) + { + msg.Printf( _( "Element token contains %d parameters." ), paramCnt ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil + if( parameters[1] == wxT( "(" ) ) + conv_unit = OLD_GPCB_UNIT_CONV; + + if( paramCnt > 10 ) + { + module->SetDescription( parameters[3] ); + module->SetReference( parameters[4] ); + } + else + { + module->SetDescription( parameters[2] ); + module->SetReference( parameters[3] ); + } + + // Read value + if( paramCnt > 10 ) + module->SetValue( parameters[5] ); + + if( paramCnt == 14 ) + { + refPos = wxPoint( parseInt( parameters[6], conv_unit ), + parseInt( parameters[7], conv_unit ) ); + textPos = wxPoint( parseInt( parameters[8], conv_unit ), + parseInt( parameters[9], conv_unit ) ); + } + else + { + textPos = wxPoint( parseInt( parameters[6], conv_unit ), + parseInt( parameters[7], conv_unit ) ); + } + + module->Reference().SetPos( textPos ); + module->Reference().SetPos0( textPos ); + + int orientation = parseInt( parameters[paramCnt-4] ); + module->Reference().SetOrientation( (orientation % 2) ? 900 : 0 ); + + // Calculate size: default is 40 mils + // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size + int tsize = ( parseInt( parameters[paramCnt-3] ) * TEXT_DEFAULT_SIZE ) / 100; + int thickness = module->Reference().GetSize().x / 6; + + tsize = std::max( (int)(5 * IU_PER_MILS), tsize ); // Ensure a minimal size = 5 mils + module->Reference().SetSize( wxSize( tsize, tsize ) ); + module->Reference().SetThickness( thickness ); + module->Value().SetOrientation( module->Reference().GetOrientation() ); + module->Value().SetSize( module->Reference().GetSize() ); + module->Value().SetThickness( module->Reference().GetThickness() ); + textPos.y += tsize + thickness; + module->Value().SetPos( textPos ); + module->Value().SetPos0( textPos ); + + while( aLineReader->ReadLine() ) + { + parameters.Clear(); + parseParameters( parameters, aLineReader ); + + if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) ) + continue; + + if( parameters[0] == wxT( ")" ) ) + break; + + paramCnt = parameters.GetCount(); + + // Test units value for a string line param (more than 3 parameters : ident [ xx ] ) + if( paramCnt > 3 ) + { + if( parameters[1] == wxT( "(" ) ) + conv_unit = OLD_GPCB_UNIT_CONV; + else + conv_unit = NEW_GPCB_UNIT_CONV; + } + + // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness] + if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 ) + { + if( paramCnt != 8 ) + { + msg.Printf( _( "ElementLine token contains %d parameters." ), paramCnt ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() ); + drawSeg->SetLayer( SILKSCREEN_N_FRONT ); + drawSeg->SetShape( S_SEGMENT ); + drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ), + parseInt( parameters[3], conv_unit ) ) ); + drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ), + parseInt( parameters[5], conv_unit ) ) ); + drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) ); + drawSeg->SetDrawCoord(); + module->m_Drawings.PushBack( drawSeg ); + continue; + } + + // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness] + if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 ) + { + if( paramCnt != 10 ) + { + msg.Printf( _( "ElementArc token contains %d parameters." ), paramCnt ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + // Pcbnew does know ellipse so we must have Width = Height + EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() ); + drawSeg->SetLayer( SILKSCREEN_N_FRONT ); + drawSeg->SetShape( S_ARC ); + module->m_Drawings.PushBack( drawSeg ); + + // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses + int radius = ( parseInt( parameters[4], conv_unit ) + + parseInt( parameters[5], conv_unit ) ) / 2; + + wxPoint centre( parseInt( parameters[2], conv_unit ), + parseInt( parameters[3], conv_unit ) ); + + drawSeg->SetStart0( centre ); + + // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles. + double start_angle = ( parseInt( parameters[6] ) * -10.0 ) + 1800.0; + + // Pcbnew delta angle direction is the opposite of Geda PCB delta angles. + double sweep_angle = parseInt( parameters[7] ) * -10.0; + + // Geda PCB does not support circles. + if( sweep_angle == -3600.0 ) + drawSeg->SetShape( S_CIRCLE ); + + // Angle value is clockwise in gpcb and Pcbnew. + drawSeg->SetAngle( sweep_angle ); + drawSeg->SetEnd0( wxPoint( radius, 0 ) ); + + // Calculate start point coordinate of arc + wxPoint arcStart( drawSeg->GetEnd0() ); + RotatePoint( &arcStart, -start_angle ); + drawSeg->SetEnd0( centre + arcStart ); + drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) ); + drawSeg->SetDrawCoord(); + continue; + } + + // Parse a Pad with no hole with format: + // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags] + // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags) + // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags) + // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags) + if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 ) + { + if( paramCnt < 10 || paramCnt > 13 ) + { + msg.Printf( _( "Pad token contains %d parameters." ), paramCnt ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + D_PAD* pad = new D_PAD( module.get() ); + pad->SetShape( PAD_RECT ); + pad->SetAttribute( PAD_SMD ); + pad->SetLayerMask( LAYER_FRONT | SOLDERMASK_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT ); + + if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) ) + pad->SetLayerMask( LAYER_BACK | SOLDERMASK_LAYER_BACK | SOLDERPASTE_LAYER_BACK ); + + // Read pad number: + if( paramCnt > 10 ) + { + pad->SetPadName( parameters[paramCnt-4] ); + } + else + { + pad->SetPadName( parameters[paramCnt-3] ); + } + + int x1 = parseInt( parameters[2], conv_unit ); + int x2 = parseInt( parameters[4], conv_unit ); + int y1 = parseInt( parameters[3], conv_unit ); + int y2 = parseInt( parameters[5], conv_unit ); + int width = parseInt( parameters[6], conv_unit ); + wxPoint delta( x2 - x1, y2 - y1 ); + double angle = atan2( (double)delta.y, (double)delta.x ); + + // Get the pad clearance and the solder mask clearance. + if( paramCnt == 13 ) + { + pad->SetLocalClearance( parseInt( parameters[7], conv_unit ) ); + pad->SetLocalSolderMaskMargin( parseInt( parameters[8], conv_unit ) ); + } + + // Negate angle (due to Y reversed axis) and convert it to internal units + angle = - angle * 1800.0 / M_PI; + pad->SetOrientation( KiROUND( angle ) ); + + wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 ); + + pad->SetSize( wxSize( KiROUND( hypot( (double)delta.x, (double)delta.y ) ) + width, + width ) ); + + padPos += module->GetPosition(); + pad->SetPos0( padPos ); + pad->SetPosition( padPos ); + + if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) ) + { + if( pad->GetSize().x == pad->GetSize().y ) + pad->SetShape( PAD_ROUND ); + else + pad->SetShape( PAD_OVAL ); + } + + module->AddPad( pad ); + continue; + } + + // Parse a Pin with through hole with format: + // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags] + // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags) + // Pin (aX aY Thickness Drill "Name" "Number" NFlags) + // Pin (aX aY Thickness Drill "Name" NFlags) + // Pin (aX aY Thickness "Name" NFlags) + if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 ) + { + if( paramCnt < 8 || paramCnt > 12 ) + { + msg.Printf( _( "Pin token contains %d parameters." ), paramCnt ); + THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader, + aLineReader->LineNumber(), 0 ); + } + + D_PAD* pad = new D_PAD( module.get() ); + pad->SetShape( PAD_ROUND ); + pad->SetLayerMask( ALL_CU_LAYERS | + SILKSCREEN_LAYER_FRONT | + SOLDERMASK_LAYER_FRONT | + SOLDERMASK_LAYER_BACK ); + + if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) ) + pad->SetShape( PAD_RECT ); + + // Read pad number: + if( paramCnt > 9 ) + { + pad->SetPadName( parameters[paramCnt-4] ); + } + else + { + pad->SetPadName( parameters[paramCnt-3] ); + } + + wxPoint padPos( parseInt( parameters[2], conv_unit ), + parseInt( parameters[3], conv_unit ) ); + + int drillSize = parseInt( parameters[5], conv_unit ); + + pad->SetDrillSize( wxSize( drillSize, drillSize ) ); + + int padSize = parseInt( parameters[4], conv_unit ); + + // Get the pad clearance and the solder mask clearance. + if( paramCnt == 13 ) + { + pad->SetLocalClearance( parseInt( parameters[5], conv_unit ) ); + pad->SetLocalSolderMaskMargin( parseInt( parameters[6], conv_unit ) ); + } + + pad->SetSize( wxSize( padSize, padSize ) ); + padPos += module->GetPosition(); + pad->SetPos0( padPos ); + pad->SetPosition( padPos ); + + if( pad->GetShape() == PAD_ROUND && pad->GetSize().x != pad->GetSize().y ) + pad->SetShape( PAD_OVAL ); + + module->AddPad( pad ); + continue; + } + } + + if( module->Value().GetText().IsEmpty() ) + module->Value().SetText( wxT( "Val**" ) ); + + // Recalculate the bounding box + module->CalculateBoundingBox(); + return module.release(); +} + + +void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader ) +{ + char key; + wxString tmp; + char* line = aLineReader->Line(); + + // Last line already ready in main parser loop. + while( *line != 0 ) + { + key = *line; + line++; + + switch( key ) + { + case '[': + case '(': + if( !tmp.IsEmpty() ) + { + aParameterList.Add( tmp ); + tmp.Clear(); + } + + tmp.Append( key ); + aParameterList.Add( tmp ); + tmp.Clear(); + + // Opening delimiter "(" after Element statement. Any other occurrence is part + // of a keyword definition. + if( aParameterList.GetCount() == 1 ) + { + TRACE_PARAMS( aParameterList ); + return; + } + + break; + + case ']': + case ')': + if( !tmp.IsEmpty() ) + { + aParameterList.Add( tmp ); + tmp.Clear(); + } + + tmp.Append( key ); + aParameterList.Add( tmp ); + TRACE_PARAMS( aParameterList ); + return; + + case '\n': + case '\r': + // Element descriptions can span multiple lines. + line = aLineReader->ReadLine(); + + // Fall through is intentional. + + case '\t': + case ' ': + if( !tmp.IsEmpty() ) + { + aParameterList.Add( tmp ); + tmp.Clear(); + } + + break; + + case '"': + // Handle empty quotes. + if( *line == '"' ) + { + line++; + tmp.Clear(); + aParameterList.Add( wxEmptyString ); + break; + } + + while( *line != 0 ) + { + key = *line; + line++; + + if( key == '"' ) + { + aParameterList.Add( tmp ); + tmp.Clear(); + break; + } + else + { + tmp.Append( key ); + } + } + + break; + + case '#': + line = aLineReader->ReadLine(); + break; + + default: + tmp.Append( key ); + break; + } + } +} + + +bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName ) +{ + wxString number; + + if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) ) + { + long lflags; + + if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) ) + return true; + } + else if( aFlag.Contains( aName ) ) + { + return true; + } + + return false; +} + + +GPCB_PLUGIN::GPCB_PLUGIN() : + m_cache( 0 ), + m_ctl( 0 ) +{ + init( 0 ); +} + + +GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) : + m_cache( 0 ), + m_ctl( aControlFlags ) +{ + init( 0 ); +} + + +GPCB_PLUGIN::~GPCB_PLUGIN() +{ + delete m_cache; +} + + +void GPCB_PLUGIN::init( PROPERTIES* aProperties ) +{ + m_props = aProperties; +} + + +void GPCB_PLUGIN::cacheLib( const wxString& aLibraryPath ) +{ + if( !m_cache || m_cache->GetPath() != aLibraryPath || m_cache->IsModified() ) + { + // a spectacular episode in memory management: + delete m_cache; + m_cache = new GPCB_FPL_CACHE( this, aLibraryPath ); + m_cache->Load(); + } +} + + +wxArrayString GPCB_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath, + PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + cacheLib( aLibraryPath ); + + const MODULE_MAP& mods = m_cache->GetModules(); + + wxArrayString ret; + + for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it ) + { + ret.Add( FROM_UTF8( it->first.c_str() ) ); + } + + return ret; +} + + +MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + PROPERTIES* aProperties ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( aProperties ); + + cacheLib( aLibraryPath ); + + const MODULE_MAP& mods = m_cache->GetModules(); + + MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) ); + + if( it == mods.end() ) + { + return NULL; + } + + // copy constructor to clone the already loaded MODULE + return new MODULE( *it->second->GetModule() ); +} + + +void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName ) +{ + LOCALE_IO toggle; // toggles on, then off, the C locale. + + init( NULL ); + + cacheLib( aLibraryPath ); + + if( !m_cache->IsWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ), + aLibraryPath.GetData() ) ); + } + + m_cache->Remove( aFootprintName ); +} + + +bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES* aProperties ) +{ + wxFileName fn; + fn.SetPath( aLibraryPath ); + + // Return if there is no library path to delete. + if( !fn.DirExists() ) + return false; + + if( !fn.IsDirWritable() ) + { + THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ), + aLibraryPath.GetData() ) ); + } + + wxDir dir( aLibraryPath ); + + if( dir.HasSubDirs() ) + { + THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ), + aLibraryPath.GetData() ) ); + } + + // All the footprint files must be deleted before the directory can be deleted. + if( dir.HasFiles() ) + { + unsigned i; + wxFileName tmp; + wxArrayString files; + + wxDir::GetAllFiles( aLibraryPath, &files ); + + for( i = 0; i < files.GetCount(); i++ ) + { + tmp = files[i]; + + if( tmp.GetExt() != KiCadFootprintFileExtension ) + { + THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' has found in library path '%'" ), + files[i].GetData(), aLibraryPath.GetData() ) ); + } + } + + for( i = 0; i < files.GetCount(); i++ ) + { + wxRemoveFile( files[i] ); + } + } + + wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library '%s'" ), + aLibraryPath.GetData() ); + + // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog + // we don't want that. we want bare metal portability with no UI here. + if( !wxRmdir( aLibraryPath ) ) + { + THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ), + aLibraryPath.GetData() ) ); + } + + // For some reason removing a directory in Windows is not immediately updated. This delay + // prevents an error when attempting to immediately recreate the same directory when over + // writing an existing library. +#ifdef __WINDOWS__ + wxMilliSleep( 250L ); +#endif + + if( m_cache && m_cache->GetPath() == aLibraryPath ) + { + delete m_cache; + m_cache = NULL; + } + + return true; +} + + +bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath ) +{ + LOCALE_IO toggle; + + init( NULL ); + + cacheLib( aLibraryPath ); + + return m_cache->IsWritable(); +} diff --git a/pcbnew/gpcb_plugin.h b/pcbnew/gpcb_plugin.h new file mode 100644 index 0000000000..7d33bf28cd --- /dev/null +++ b/pcbnew/gpcb_plugin.h @@ -0,0 +1,103 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 Wayne Stambaugh + * Copyright (C) 1992-2012 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 + */ + +/** + * @file gpcb_plugin.cpp + * @brief Geda PCB file plugin definition file. + */ + +#ifndef _GPCB_PLUGIN_H_ +#define _GPCB_PLUGIN_H_ + +#include +#include + + +class GPCB_FPL_CACHE; + + +/** + * Class GPCB_PLUGIN + * is a PLUGIN derivation for saving and loading Geda PCB files. + * + * @note This class is not thread safe, but it is re-entrant multiple times in sequence. + * @note Currently only reading GPCB footprint files is implemented. + */ +class GPCB_PLUGIN : public PLUGIN +{ + friend class GPCB_FPL_CACHE; + +public: + + //-------------------------------------------------------------- + + const wxString& PluginName() const + { + static const wxString name = wxT( "Geda PCB" ); + return name; + } + + const wxString& GetFileExtension() const + { + static const wxString extension = wxT( "fp" ); + return extension; + } + + wxArrayString FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL); + + MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, + PROPERTIES* aProperties = NULL ); + + void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName ); + + bool FootprintLibDelete( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL ); + + bool IsFootprintLibWritable( const wxString& aLibraryPath ); + + //------------------------------------------------------------- + + GPCB_PLUGIN(); + + GPCB_PLUGIN( int aControlFlags ); + + ~GPCB_PLUGIN(); + +protected: + + wxString m_error; ///< for throwing exceptions + PROPERTIES* m_props; ///< passed via Save() or Load(), no ownership, may be NULL. + GPCB_FPL_CACHE* m_cache; ///< Footprint library cache. + int m_ctl; + + LINE_READER* m_reader; ///< no ownership here. + wxString m_filename; ///< for saves only, name is in m_reader for loads + +private: + /// we only cache one footprint library for now, this determines which one. + void cacheLib( const wxString& aLibraryPath ); + + void init( PROPERTIES* aProperties ); +}; + +#endif // _GPCB_PLUGIN_H_ diff --git a/pcbnew/io_mgr.cpp b/pcbnew/io_mgr.cpp index 505fe72d19..057b294766 100644 --- a/pcbnew/io_mgr.cpp +++ b/pcbnew/io_mgr.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #define FMT_UNIMPLEMENTED _( "Plugin '%s' does not implement the '%s' function." ) @@ -64,6 +65,9 @@ PLUGIN* IO_MGR::PluginFind( PCB_FILE_T aFileType ) case EAGLE: return new EAGLE_PLUGIN(); + + case GEDA_PCB: + return new GPCB_PLUGIN(); } return NULL; @@ -99,6 +103,9 @@ const wxString IO_MGR::ShowType( PCB_FILE_T aType ) case EAGLE: return wxString( wxT( "Eagle" ) ); + + case GEDA_PCB: + return wxString( wxT( "Geda-PCB" ) ); } } @@ -118,6 +125,9 @@ IO_MGR::PCB_FILE_T IO_MGR::EnumFromStr( const wxString& aType ) if( aType == wxT( "Eagle" ) ) return EAGLE; + if( aType == wxT( "Geda-PCB" ) ) + return GEDA_PCB; + // wxASSERT( blow up here ) return PCB_FILE_T( -1 ); @@ -145,7 +155,13 @@ IO_MGR::PCB_FILE_T IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath PCB_FILE_T ret; if( fn.GetExt() == LegacyFootprintLibPathExtension ) + { ret = LEGACY; + } + else if( fn.GetExt() == GedaPcbFootprintLibFileExtension ) + { + ret = GEDA_PCB; + } else { // Although KICAD PLUGIN uses libpaths with fixed extension of diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h index 53778ca310..f747c8b211 100644 --- a/pcbnew/io_mgr.h +++ b/pcbnew/io_mgr.h @@ -51,7 +51,7 @@ public: LEGACY, //< Legacy Pcbnew file formats prior to s-expression. KICAD, //< S-expression Pcbnew file format. EAGLE, - + GEDA_PCB, //< Geda PCB file formats. // add your type here. // ALTIUM, @@ -272,7 +272,8 @@ public: * * @throw IO_ERROR if the library cannot be found, or footprint cannot be loaded. */ - virtual wxArrayString FootprintEnumerate( const wxString& aLibraryPath, PROPERTIES* aProperties = NULL); + virtual wxArrayString FootprintEnumerate( const wxString& aLibraryPath, + PROPERTIES* aProperties = NULL); /** * Function FootprintLoad diff --git a/pcbnew/librairi.cpp b/pcbnew/librairi.cpp index 371dae7562..65d060fd3b 100644 --- a/pcbnew/librairi.cpp +++ b/pcbnew/librairi.cpp @@ -1,3 +1,27 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2012 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 + */ + /** * @file librairi.cpp * @brief Manage module (footprint) libraries. @@ -69,8 +93,10 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() { // use the clipboard for this in the future? - wxString lastOpenedPathForLoading; - wxConfig* config = wxGetApp().GetSettings(); + // Some day it might be useful save the last library type selected aong with the path. + static int lastFilterIndex = 0; + wxString lastOpenedPathForLoading; + wxConfig* config = wxGetApp().GetSettings(); if( config ) config->Read( EXPORT_IMPORT_LASTPATH_KEY, &lastOpenedPathForLoading ); @@ -78,17 +104,22 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() wxString wildCard; wildCard << wxGetTranslation( KiCadFootprintLibFileWildcard ) << wxChar( '|' ) - << wxGetTranslation( ModExportFileWildcard ) << wxChar( '|' ) - << wxGetTranslation( ModImportFileWildcard ); + << wxGetTranslation( ModExportFileWildcard ) << wxChar( '|' ) + << wxGetTranslation( ModImportFileWildcard ) << wxChar( '|' ) + << wxGetTranslation( GedaPcbFootprintLibFileWildcard ); wxFileDialog dlg( this, FMT_IMPORT_MODULE, lastOpenedPathForLoading, wxEmptyString, wildCard, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); + dlg.SetFilterIndex( lastFilterIndex ); if( dlg.ShowModal() == wxID_CANCEL ) return NULL; + lastFilterIndex = dlg.GetFilterIndex(); + FILE* fp = wxFopen( dlg.GetPath(), wxT( "rt" ) ); + if( !fp ) { wxString msg = wxString::Format( FMT_FILE_NOT_FOUND, GetChars( dlg.GetPath() ) ); @@ -104,12 +135,12 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() wxString moduleName; - bool isGeda = false; - bool isLegacy = false; + bool isGeda = false; + bool isLegacy = false; { - FILE_LINE_READER freader( fp, dlg.GetPath() ); // I own fp, and will close it. - FILTER_READER reader( freader ); // skip blank lines + FILE_LINE_READER freader( fp, dlg.GetPath() ); // I own fp, and will close it. + WHITESPACE_FILTER_READER reader( freader ); // skip blank lines reader.ReadLine(); char* line = reader.Line(); @@ -148,11 +179,28 @@ MODULE* FOOTPRINT_EDIT_FRAME::Import_Module() if( isGeda ) { - LOCALE_IO toggle; + try + { + wxFileName fn = dlg.GetPath(); + PLUGIN::RELEASER pi( IO_MGR::PluginFind( IO_MGR::GEDA_PCB ) ); - // @todo GEDA plugin - module = new MODULE( GetBoard() ); - module->Read_GPCB_Descr( dlg.GetPath() ); + moduleName = fn.GetName(); + module = pi->FootprintLoad( fn.GetPath(), moduleName ); + + if( !module ) + { + wxString msg = wxString::Format( + FMT_MOD_NOT_FOUND, GetChars( moduleName ), GetChars( fn.GetPath() ) ); + + DisplayError( this, msg ); + return NULL; + } + } + catch( IO_ERROR ioe ) + { + DisplayError( this, ioe.errorText ); + return NULL; + } } else if( isLegacy ) { diff --git a/pcbnew/menubar_modedit.cpp b/pcbnew/menubar_modedit.cpp index 890684e00c..49060946a0 100644 --- a/pcbnew/menubar_modedit.cpp +++ b/pcbnew/menubar_modedit.cpp @@ -1,3 +1,4 @@ + /* * This program source code file is part of KiCad, a free EDA CAD application. * @@ -76,7 +77,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateMenuBar() // from File AddMenuItem( openSubmenu, ID_MODEDIT_IMPORT_PART, - _( "Import Module from File (&Import)" ), + _( "&Import Module from File" ), _( "Import a footprint from an existing file" ), KiBitmap( import_module_xpm ) );