KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_geda.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
30
31#include <kiplatform/io.h>
33#include <string_utils.h>
34#include <trace_helpers.h>
35#include <math/util.h> // for KiROUND
36
37#include <board.h>
38#include <font/fontconfig.h>
39#include <footprint.h>
40#include <pad.h>
41#include <macros.h>
42#include <pcb_text.h>
43#include <pcb_shape.h>
44#include <reporter.h>
45#include <wx_filename.h>
46
47#include <wx/dir.h>
48#include <wx/log.h>
49#include <wx/filename.h>
50#include <wx/wfstream.h>
51#include <boost/ptr_container/ptr_map.hpp>
52#include <filter_reader.h>
53
54
55static inline long parseInt( const wxString& aValue, double aScalar )
56{
57 double value = std::numeric_limits<double>::max();
58
59 /*
60 * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
61 * Unit-less values are still centimils (100000 units per inch), like with
62 * the previous format.
63 *
64 * Distinction between the even older format (mils, 1000 units per inch)
65 * and the pre-2011 format is done in ::parseFOOTPRINT already; the
66 * distinction is by whether an object definition opens with '(' or '['.
67 * All values with explicit unit open with a '[' so there's no need to
68 * consider this distinction when parsing them.
69 *
70 * The solution here is to watch for a unit and, if present, convert the
71 * value to centimils. All unit-less values are read unaltered. This way
72 * the code below can continue to consider all read values to be in mils or
73 * centimils. It also matches the strategy gEDA/pcb uses for backwards
74 * compatibility with its own layouts.
75 *
76 * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
77 * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
78 * have to test for all 11 units gEDA/pcb allows in user dialogs.
79 */
80 if( aValue.EndsWith( wxT( "mm" ) ) )
81 {
82 aScalar *= 100000.0 / 25.4;
83 }
84 else if( aValue.EndsWith( wxT( "mil" ) ) )
85 {
86 aScalar *= 100.;
87 }
88
89 // This conversion reports failure on strings as simple as "1000", still
90 // it returns the right result in &value. Thus, ignore the return value.
91 aValue.ToCDouble(&value);
92
93 if( value == std::numeric_limits<double>::max() ) // conversion really failed
94 {
95 THROW_IO_ERROR( wxString::Format( _( "Cannot convert '%s' to an integer." ),
96 aValue.GetData() ) );
97 }
98
99 return KiROUND( value * aScalar );
100}
101
102
112{
113public:
114 GPCB_FPL_CACHE_ENTRY( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName ) :
115 m_filename( aFileName ),
116 m_footprint( aFootprint )
117 { }
118
120 std::unique_ptr<FOOTPRINT>& GetFootprint() { return m_footprint; }
121
122private:
124 std::unique_ptr<FOOTPRINT> m_footprint;
125};
126
127
129{
130public:
131 GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath );
132
133 wxString GetPath() const { return m_lib_path.GetPath(); }
134 bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
135 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY>& GetFootprints() { return m_footprints; }
136
137 // Most all functions in this class throw IO_ERROR exceptions. There are no
138 // error codes nor user interface calls from here, nor in any PLUGIN.
139 // Catch these exceptions higher up please.
140
142
143 void Load();
144
145 void Remove( const wxString& aFootprintName );
146
153 static long long GetTimestamp( const wxString& aLibPath );
154
158 bool IsModified();
159
160private:
161 FOOTPRINT* parseFOOTPRINT( LINE_READER* aLineReader );
162
173 bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
174
190 void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
191
193 wxFileName m_lib_path;
194
195 boost::ptr_map<std::string, GPCB_FPL_CACHE_ENTRY> m_footprints;
197
202};
203
204
205GPCB_FPL_CACHE::GPCB_FPL_CACHE( PCB_IO_GEDA* aOwner, const wxString& aLibraryPath )
206{
207 m_owner = aOwner;
208 m_lib_path.SetPath( aLibraryPath );
210 m_cache_dirty = true;
211}
212
213
215{
216 m_cache_dirty = false;
218
219 // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
220 // and the footprints are the .fp files inside this folder.
221
222 wxDir dir( m_lib_path.GetPath() );
223
224 if( !dir.IsOpened() )
225 {
226 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ),
227 m_lib_path.GetPath().GetData() ) );
228 }
229
230 wxString fullName;
231 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
232
233 // wxFileName construction is egregiously slow. Construct it once and just swap out
234 // the filename thereafter.
235 WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
236
237 if( !dir.GetFirst( &fullName, fileSpec ) )
238 return;
239
240 wxString cacheErrorMsg;
241
242 do
243 {
244 fn.SetFullName( fullName );
245
246 // Queue I/O errors so only files that fail to parse don't get loaded.
247 try
248 {
249 // reader now owns fp, will close on exception or return
250 FILE_LINE_READER reader( fn.GetFullPath() );
251 std::string name = TO_UTF8( fn.GetName() );
252 FOOTPRINT* footprint = parseFOOTPRINT( &reader );
253
254 // The footprint name is the file name without the extension.
255 footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
256 m_footprints.insert( name, new GPCB_FPL_CACHE_ENTRY( footprint, fn ) );
257 }
258 catch( const IO_ERROR& ioe )
259 {
260 if( !cacheErrorMsg.IsEmpty() )
261 cacheErrorMsg += wxT( "\n\n" );
262
263 cacheErrorMsg += ioe.What();
264 }
265 } while( dir.GetNext( &fullName ) );
266
267 if( !cacheErrorMsg.IsEmpty() )
268 THROW_IO_ERROR( cacheErrorMsg );
269}
270
271
272void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
273{
274 std::string footprintName = TO_UTF8( aFootprintName );
275
276 auto it = m_footprints.find( footprintName );
277
278 if( it == m_footprints.end() )
279 {
280 THROW_IO_ERROR( wxString::Format( _( "Library '%s' has no footprint '%s'." ),
281 m_lib_path.GetPath().GetData(),
282 aFootprintName.GetData() ) );
283 }
284
285 // Remove the footprint from the cache and delete the footprint file from the library.
286 wxString fullPath = it->second->GetFileName().GetFullPath();
287 m_footprints.erase( footprintName );
288 wxRemoveFile( fullPath );
289}
290
291
298
299
300long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
301{
302 wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::GedaPcbFootprintLibFileExtension );
303
304 return KIPLATFORM::IO::TimestampDir( aLibPath, fileSpec );
305}
306
307
309{
310 #define TEXT_DEFAULT_SIZE ( 40*pcbIUScale.IU_PER_MILS )
311 #define OLD_GPCB_UNIT_CONV pcbIUScale.IU_PER_MILS
312
313 // Old version unit = 1 mil, so conv_unit is 10 or 0.1
314 #define NEW_GPCB_UNIT_CONV ( 0.01*pcbIUScale.IU_PER_MILS )
315
316 int paramCnt;
317
318 // GPCB unit = 0.01 mils and Pcbnew 0.1.
319 double conv_unit = NEW_GPCB_UNIT_CONV;
320 wxString msg;
321 wxArrayString parameters;
322 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( nullptr );
323
324 if( aLineReader->ReadLine() == nullptr )
325 {
326 msg = aLineReader->GetSource() + wxT( ": empty file" );
327 THROW_IO_ERROR( msg );
328 }
329
330 parameters.Clear();
331 parseParameters( parameters, aLineReader );
332 paramCnt = parameters.GetCount();
333
334 /* From the Geda PCB documentation, valid Element definitions:
335 * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
336 * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
337 * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
338 * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
339 * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
340 */
341
342 if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
343 {
344 msg.Printf( _( "Unknown token '%s'" ), parameters[0] );
345 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
346 aLineReader->LineNumber(), 0 );
347 }
348
349 if( paramCnt < 10 || paramCnt > 14 )
350 {
351 msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
352 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
353 aLineReader->LineNumber(), 0 );
354 }
355
356 // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
357 if( parameters[1] == wxT( "(" ) )
358 conv_unit = OLD_GPCB_UNIT_CONV;
359
360 if( paramCnt > 10 )
361 {
362 footprint->SetLibDescription( parameters[3] );
363 footprint->SetReference( parameters[4] );
364 }
365 else
366 {
367 footprint->SetLibDescription( parameters[2] );
368 footprint->SetReference( parameters[3] );
369 }
370
371 // Read value
372 if( paramCnt > 10 )
373 footprint->SetValue( parameters[5] );
374
375 // With gEDA/pcb, value is meaningful after instantiation, only, so it's
376 // often empty in bare footprints.
377 if( footprint->Value().GetText().IsEmpty() )
378 footprint->Value().SetText( wxT( "VAL**" ) );
379
380 if( footprint->Reference().GetText().IsEmpty() )
381 footprint->Reference().SetText( wxT( "REF**" ) );
382
383 while( aLineReader->ReadLine() )
384 {
385 parameters.Clear();
386 parseParameters( parameters, aLineReader );
387
388 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
389 continue;
390
391 if( parameters[0] == wxT( ")" ) )
392 break;
393
394 paramCnt = parameters.GetCount();
395
396 // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
397 if( paramCnt > 3 )
398 {
399 if( parameters[1] == wxT( "(" ) )
400 conv_unit = OLD_GPCB_UNIT_CONV;
401 else
402 conv_unit = NEW_GPCB_UNIT_CONV;
403 }
404
405 wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
406 parameters[0], paramCnt );
407
408 // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
409 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
410 {
411 if( paramCnt != 8 )
412 {
413 msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
414 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
415 aLineReader->LineNumber(), 0 );
416 }
417
418 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
419 shape->SetLayer( F_SilkS );
420 shape->SetStart( VECTOR2I( parseInt( parameters[2], conv_unit ),
421 parseInt( parameters[3], conv_unit ) ) );
422 shape->SetEnd( VECTOR2I( parseInt( parameters[4], conv_unit ),
423 parseInt( parameters[5], conv_unit ) ) );
424 shape->SetStroke( STROKE_PARAMS( parseInt( parameters[6], conv_unit ),
426
427 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
428 shape->Move( footprint->GetPosition() );
429
430 footprint->Add( shape );
431 continue;
432 }
433
434 // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
435 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
436 {
437 if( paramCnt != 10 )
438 {
439 msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
440 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
441 aLineReader->LineNumber(), 0 );
442 }
443
444 // Pcbnew does know ellipse so we must have Width = Height
445 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
446 shape->SetLayer( F_SilkS );
447 footprint->Add( shape );
448
449 // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
450 int radius = ( parseInt( parameters[4], conv_unit ) +
451 parseInt( parameters[5], conv_unit ) ) / 2;
452
453 VECTOR2I centre( parseInt( parameters[2], conv_unit ),
454 parseInt( parameters[3], conv_unit ) );
455
456 // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
457 EDA_ANGLE start_angle( (int) parseInt( parameters[6], -10.0 ), TENTHS_OF_A_DEGREE_T );
458 start_angle += ANGLE_180;
459
460 // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
461 EDA_ANGLE sweep_angle( (int) parseInt( parameters[7], -10.0 ), TENTHS_OF_A_DEGREE_T );
462
463 // Geda PCB does not support circles.
464 if( sweep_angle == -ANGLE_360 )
465 {
466 shape->SetShape( SHAPE_T::CIRCLE );
467 shape->SetCenter( centre );
468 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
469 }
470 else
471 {
472 // Calculate start point coordinate of arc
473 VECTOR2I arcStart( radius, 0 );
474 RotatePoint( arcStart, -start_angle );
475 shape->SetCenter( centre );
476 shape->SetStart( arcStart + centre );
477
478 // Angle value is clockwise in gpcb and Pcbnew.
479 shape->SetArcAngleAndEnd( sweep_angle, true );
480 }
481
482 shape->SetStroke( STROKE_PARAMS( parseInt( parameters[8], conv_unit ),
484
485 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
486 shape->Move( footprint->GetPosition() );
487 continue;
488 }
489
490 // Parse a Pad with no hole with format:
491 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
492 // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
493 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
494 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
495 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
496 {
497 if( paramCnt < 10 || paramCnt > 13 )
498 {
499 msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
500 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
501 aLineReader->LineNumber(), 0 );
502 }
503
504 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
505
506 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
507 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
508
510 pad->SetAttribute( PAD_ATTRIB::SMD );
511 pad->SetLayerSet( pad_front );
512
513 if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
514 pad->SetLayerSet( pad_back );
515
516 // Set the pad name:
517 // Pcbnew pad name is used for electrical connection calculations.
518 // Accordingly it should be mapped to gEDA's pin/pad number,
519 // which is used for the same purpose.
520 // gEDA also features a pin/pad "name", which is an arbitrary string
521 // and set to the pin name of the netlist on instantiation. Many gEDA
522 // bare footprints use identical strings for name and number, so this
523 // can be a bit confusing.
524 pad->SetNumber( parameters[paramCnt-3] );
525
526 int x1 = parseInt( parameters[2], conv_unit );
527 int x2 = parseInt( parameters[4], conv_unit );
528 int y1 = parseInt( parameters[3], conv_unit );
529 int y2 = parseInt( parameters[5], conv_unit );
530 int width = parseInt( parameters[6], conv_unit );
531 VECTOR2I delta( x2 - x1, y2 - y1 );
532 double angle = atan2( (double)delta.y, (double)delta.x );
533
534 // Get the pad clearance and the solder mask clearance.
535 if( paramCnt == 13 )
536 {
537 int clearance = parseInt( parameters[7], conv_unit );
538 // One of gEDA's oddities is that clearance between pad and polygon
539 // is given as the gap on both sides of the pad together, so for
540 // KiCad it has to halfed.
541 pad->SetLocalClearance( clearance / 2 );
542
543 // In GEDA, the mask value is the size of the hole in this
544 // solder mask. In Pcbnew, it is a margin, therefore the distance
545 // between the copper and the mask
546 int maskMargin = parseInt( parameters[8], conv_unit );
547 maskMargin = ( maskMargin - width ) / 2;
548 pad->SetLocalSolderMaskMargin( maskMargin );
549 }
550
551 // Negate angle (due to Y reversed axis)
552 EDA_ANGLE orient( -angle, RADIANS_T );
553 pad->SetOrientation( orient );
554
555 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
556
557 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( delta.EuclideanNorm() + width, width ) );
558
559 padPos += footprint->GetPosition();
560 pad->SetPosition( padPos );
561
562 if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
563 {
564 if( pad->GetSize( PADSTACK::ALL_LAYERS ).x == pad->GetSize( PADSTACK::ALL_LAYERS ).y )
566 else
568 }
569
570 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
571 {
572 footprint->Add( pad.release() );
573 }
574 else
575 {
576 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
577 aLineReader->GetSource() );
578 }
579
580 continue;
581 }
582
583 // Parse a Pin with through hole with format:
584 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
585 // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
586 // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
587 // Pin (aX aY Thickness Drill "Name" NFlags)
588 // Pin (aX aY Thickness "Name" NFlags)
589 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
590 {
591 if( paramCnt < 8 || paramCnt > 12 )
592 {
593 msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
594 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
595 aLineReader->LineNumber(), 0 );
596 }
597
598 PAD* pad = new PAD( footprint.get() );
599
601
602 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
603
604 pad->SetLayerSet( pad_set );
605
606 if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
608
609 // Set the pad name:
610 // Pcbnew pad name is used for electrical connection calculations.
611 // Accordingly it should be mapped to gEDA's pin/pad number,
612 // which is used for the same purpose.
613 pad->SetNumber( parameters[paramCnt-3] );
614
615 VECTOR2I padPos( parseInt( parameters[2], conv_unit ),
616 parseInt( parameters[3], conv_unit ) );
617
618 int padSize = parseInt( parameters[4], conv_unit );
619
620 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( padSize, padSize ) );
621
622 int drillSize = 0;
623
624 // Get the pad clearance, solder mask clearance, and drill size.
625 if( paramCnt == 12 )
626 {
627 int clearance = parseInt( parameters[5], conv_unit );
628 // One of gEDA's oddities is that clearance between pad and polygon
629 // is given as the gap on both sides of the pad together, so for
630 // KiCad it has to halfed.
631 pad->SetLocalClearance( clearance / 2 );
632
633 // In GEDA, the mask value is the size of the hole in this
634 // solder mask. In Pcbnew, it is a margin, therefore the distance
635 // between the copper and the mask
636 int maskMargin = parseInt( parameters[6], conv_unit );
637 maskMargin = ( maskMargin - padSize ) / 2;
638 pad->SetLocalSolderMaskMargin( maskMargin );
639
640 drillSize = parseInt( parameters[7], conv_unit );
641 }
642 else
643 {
644 drillSize = parseInt( parameters[5], conv_unit );
645 }
646
647 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
648
649 padPos += footprint->GetPosition();
650 pad->SetPosition( padPos );
651
652 if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
653 && pad->GetSize( PADSTACK::ALL_LAYERS ).x != pad->GetSize( PADSTACK::ALL_LAYERS ).y )
654 {
656 }
657
658 footprint->Add( pad );
659 continue;
660 }
661 }
662
663 footprint->AutoPositionFields();
664
665 return footprint.release();
666}
667
668
669void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
670{
671 char key;
672 wxString tmp;
673 char* line = aLineReader->Line();
674
675 // Last line already ready in main parser loop.
676 while( *line != 0 )
677 {
678 key = *line;
679 line++;
680
681 switch( key )
682 {
683 case '[':
684 case '(':
685 if( !tmp.IsEmpty() )
686 {
687 aParameterList.Add( tmp );
688 tmp.Clear();
689 }
690
691 tmp.Append( key );
692 aParameterList.Add( tmp );
693 tmp.Clear();
694
695 // Opening delimiter "(" after Element statement. Any other occurrence is part
696 // of a keyword definition.
697 if( aParameterList.GetCount() == 1 )
698 {
699 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
700 return;
701 }
702
703 break;
704
705 case ']':
706 case ')':
707 if( !tmp.IsEmpty() )
708 {
709 aParameterList.Add( tmp );
710 tmp.Clear();
711 }
712
713 tmp.Append( key );
714 aParameterList.Add( tmp );
715 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
716 return;
717
718 case '\n':
719 case '\r':
720 // Element descriptions can span multiple lines.
721 line = aLineReader->ReadLine();
723
724 case '\t':
725 case ' ':
726 if( !tmp.IsEmpty() )
727 {
728 aParameterList.Add( tmp );
729 tmp.Clear();
730 }
731
732 break;
733
734 case '"':
735 // Handle empty quotes.
736 if( *line == '"' )
737 {
738 line++;
739 tmp.Clear();
740 aParameterList.Add( wxEmptyString );
741 break;
742 }
743
744 while( *line != 0 )
745 {
746 key = *line;
747 line++;
748
749 if( key == '"' )
750 {
751 aParameterList.Add( tmp );
752 tmp.Clear();
753 break;
754 }
755 else
756 {
757 tmp.Append( key );
758 }
759 }
760
761 break;
762
763 case '#':
764 line = aLineReader->ReadLine();
765 break;
766
767 default:
768 tmp.Append( key );
769 break;
770 }
771 }
772}
773
774
775bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
776{
777 wxString number;
778
779 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
780 {
781 long lflags;
782
783 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
784 return true;
785 }
786 else if( aFlag.Contains( aName ) )
787 {
788 return true;
789 }
790
791 return false;
792}
793
794
795PCB_IO_GEDA::PCB_IO_GEDA() : PCB_IO( wxS( "gEDA PCB" ) ),
796 m_cache( nullptr ),
797 m_ctl( 0 )
798{
799 m_reader = nullptr;
800 init( nullptr );
801}
802
803
804PCB_IO_GEDA::PCB_IO_GEDA( int aControlFlags ) : PCB_IO( wxS( "gEDA PCB" ) ),
805 m_cache( nullptr ),
806 m_ctl( aControlFlags )
807{
808 m_reader = nullptr;
809 init( nullptr );
810}
811
812
814{
815 delete m_cache;
816}
817
818
819void PCB_IO_GEDA::init( const std::map<std::string, UTF8>* aProperties )
820{
821 m_props = aProperties;
822}
823
824
825void PCB_IO_GEDA::validateCache( const wxString& aLibraryPath, bool checkModified )
826{
827 if( !m_cache || ( checkModified && m_cache->IsModified() ) )
828 {
829 // a spectacular episode in memory management:
830 delete m_cache;
831 m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
832 m_cache->Load();
833 }
834}
835
836
837FOOTPRINT* PCB_IO_GEDA::ImportFootprint( const wxString& aFootprintPath,
838 wxString& aFootprintNameOut,
839 const std::map<std::string, UTF8>* aProperties )
840{
841 wxFileName fn( aFootprintPath );
842
843 FILE_LINE_READER freader( aFootprintPath );
844 WHITESPACE_FILTER_READER reader( freader );
845
846 reader.ReadLine();
847 char* line = reader.Line();
848
849 if( !line )
850 return nullptr;
851
852 if( strncasecmp( line, "Element", strlen( "Element" ) ) != 0 )
853 return nullptr;
854
855 aFootprintNameOut = fn.GetName();
856
857 return FootprintLoad( fn.GetPath(), aFootprintNameOut );
858}
859
860
861void PCB_IO_GEDA::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
862 bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
863{
864 wxDir dir( aLibraryPath );
865 wxString errorMsg;
866
867 if( !dir.IsOpened() )
868 {
869 if( aBestEfforts )
870 return;
871 else
872 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ), aLibraryPath ) );
873 }
874
875 init( aProperties );
876
877 try
878 {
879 validateCache( aLibraryPath );
880 }
881 catch( const IO_ERROR& ioe )
882 {
883 errorMsg = ioe.What();
884 }
885
886 // Some of the files may have been parsed correctly so we want to add the valid files to
887 // the library.
888
889 for( const auto& footprint : m_cache->GetFootprints() )
890 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
891
892 if( !errorMsg.IsEmpty() && !aBestEfforts )
893 THROW_IO_ERROR( errorMsg );
894}
895
896
897const FOOTPRINT* PCB_IO_GEDA::getFootprint( const wxString& aLibraryPath,
898 const wxString& aFootprintName,
899 const std::map<std::string, UTF8>* aProperties,
900 bool checkModified )
901{
902 init( aProperties );
903
904 validateCache( aLibraryPath, checkModified );
905
906 auto it = m_cache->GetFootprints().find( TO_UTF8( aFootprintName ) );
907
908 if( it == m_cache->GetFootprints().end() )
909 return nullptr;
910
911 return it->second->GetFootprint().get();
912}
913
914
915FOOTPRINT* PCB_IO_GEDA::FootprintLoad( const wxString& aLibraryPath,
916 const wxString& aFootprintName,
917 bool aKeepUUID,
918 const std::map<std::string, UTF8>* aProperties )
919{
920 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
921 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
922
923 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
924
925 if( footprint )
926 {
928 copy->SetParent( nullptr );
929 return copy;
930 }
931
932 return nullptr;
933}
934
935
936void PCB_IO_GEDA::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
937 const std::map<std::string, UTF8>* aProperties )
938{
939 init( aProperties );
940
941 validateCache( aLibraryPath );
942
943 if( !m_cache->IsWritable() )
944 {
945 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
946 aLibraryPath.GetData() ) );
947 }
948
949 m_cache->Remove( aFootprintName );
950}
951
952
953bool PCB_IO_GEDA::DeleteLibrary( const wxString& aLibraryPath, const std::map<std::string, UTF8>* aProperties )
954{
955 wxFileName fn;
956 fn.SetPath( aLibraryPath );
957
958 // Return if there is no library path to delete.
959 if( !fn.DirExists() )
960 return false;
961
962 if( !fn.IsDirWritable() )
963 {
964 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
965 aLibraryPath.GetData() ) );
966 }
967
968 wxDir dir( aLibraryPath );
969
970 if( dir.HasSubDirs() )
971 {
972 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
973 aLibraryPath.GetData() ) );
974 }
975
976 // All the footprint files must be deleted before the directory can be deleted.
977 if( dir.HasFiles() )
978 {
979 unsigned i;
980 wxFileName tmp;
981 wxArrayString files;
982
983 wxDir::GetAllFiles( aLibraryPath, &files );
984
985 for( i = 0; i < files.GetCount(); i++ )
986 {
987 tmp = files[i];
988
989 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
990 {
991 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library '%s'." ),
992 files[i].GetData(),
993 aLibraryPath.GetData() ) );
994 }
995 }
996
997 for( i = 0; i < files.GetCount(); i++ )
998 {
999 wxRemoveFile( files[i] );
1000 }
1001 }
1002
1003 wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1004 aLibraryPath.GetData() );
1005
1006 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1007 // we don't want that. we want bare metal portability with no UI here.
1008 if( !wxRmdir( aLibraryPath ) )
1009 {
1010 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
1011 aLibraryPath.GetData() ) );
1012 }
1013
1014 // For some reason removing a directory in Windows is not immediately updated. This delay
1015 // prevents an error when attempting to immediately recreate the same directory when over
1016 // writing an existing library.
1017#ifdef __WINDOWS__
1018 wxMilliSleep( 250L );
1019#endif
1020
1021 if( m_cache && m_cache->GetPath() == aLibraryPath )
1022 {
1023 delete m_cache;
1024 m_cache = nullptr;
1025 }
1026
1027 return true;
1028}
1029
1030
1031long long PCB_IO_GEDA::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1032{
1033 return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1034}
1035
1036
1037bool PCB_IO_GEDA::IsLibraryWritable( const wxString& aLibraryPath )
1038{
1039 init( nullptr );
1040
1041 validateCache( aLibraryPath );
1042
1043 return m_cache->IsWritable();
1044}
const char * name
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void SetCenter(const VECTOR2I &aCenter)
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:168
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
A LINE_READER that reads from an open file.
Definition richio.h:158
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:334
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:352
BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const override
Create a copy of this BOARD_ITEM.
helper class for creating a footprint library cache.
std::unique_ptr< FOOTPRINT > m_footprint
WX_FILENAME GetFileName() const
std::unique_ptr< FOOTPRINT > & GetFootprint()
WX_FILENAME m_filename
The full file name and path of the footprint to cache.
GPCB_FPL_CACHE_ENTRY(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
FOOTPRINT * parseFOOTPRINT(LINE_READER *aLineReader)
void Remove(const wxString &aFootprintName)
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > & GetFootprints()
GPCB_FPL_CACHE(PCB_IO_GEDA *aOwner, const wxString &aLibraryPath)
bool IsModified()
Return true if the cache is not up-to-date.
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
PCB_IO_GEDA * m_owner
Plugin object that owns the cache.
boost::ptr_map< std::string, GPCB_FPL_CACHE_ENTRY > m_footprints
Map of footprint filename to cache entries.
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Extract parameters and tokens from aLineReader and adds them to aParameterList.
long long m_cache_timestamp
A hash of the timestamps for all the footprint files.
bool IsWritable() const
wxFileName m_lib_path
The path of the library.
bool m_cache_dirty
Stored separately because it's expensive to check m_cache_timestamp against all the files.
void Load()
Save not implemented for the Geda PCB footprint library format.
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Test aFlag for aMask or aName.
wxString GetPath() const
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition richio.h:66
virtual char * ReadLine()=0
Read a line of text into the buffer and increments the line number counter.
virtual const wxString & GetSource() const
Returns the name of the source of the lines in an abstract sense.
Definition richio.h:94
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition richio.h:120
char * Line() const
Return a pointer to the last line that was read in.
Definition richio.h:102
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
A #PLUGIN derivation for saving and loading Geda PCB files.
Definition pcb_io_geda.h:48
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
bool DeleteLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Delete an existing library and returns true, or if library does not exist returns false,...
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
void init(const std::map< std::string, UTF8 > *aProperties)
GPCB_FPL_CACHE * m_cache
Footprint library cache.
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
LINE_READER * m_reader
no ownership here.
friend class GPCB_FPL_CACHE
Definition pcb_io_geda.h:98
FOOTPRINT * ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const std::map< std::string, UTF8 > *aProperties) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const std::map< std::string, UTF8 > *aProperties, bool checkModified)
PCB_IO(const wxString &aName)
Definition pcb_io.h:337
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition pcb_io.h:347
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition pcb_shape.h:92
Simple container to manage line stroke parameters.
Read lines of text from another LINE_READER but only returns non-comment lines and non-blank lines wi...
char * ReadLine() override
Read a line of text into the buffer and increments the line number counter.
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition wx_filename.h:50
void SetFullName(const wxString &aFileNameAndExtension)
wxString GetName() const
wxString GetFullPath() const
#define _(s)
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
@ RADIANS_T
Definition eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
@ SEGMENT
Definition eda_shape.h:45
static const std::string GedaPcbFootprintLibFileExtension
static const std::string KiCadFootprintFileExtension
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
@ F_Paste
Definition layer_ids.h:104
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_SilkS
Definition layer_ids.h:100
@ F_Cu
Definition layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
long long TimestampDir(const wxString &aDirPath, const wxString &aFilespec)
Computes a hash of modification times and sizes for files matching a pattern.
Definition unix/io.cpp:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ RECTANGLE
Definition padstack.h:54
static long parseInt(const wxString &aValue, double aScalar)
#define NEW_GPCB_UNIT_CONV
#define OLD_GPCB_UNIT_CONV
wxString From_UTF8(const char *cstring)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
int radius
int clearance
int delta
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
wxLogTrace helper definitions.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.