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