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-2023 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 VECTOR2I textPos;
325 wxString msg;
326 wxArrayString parameters;
327 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( nullptr );
328
329 if( aLineReader->ReadLine() == nullptr )
330 {
331 msg = aLineReader->GetSource() + wxT( ": empty file" );
332 THROW_IO_ERROR( msg );
333 }
334
335 parameters.Clear();
336 parseParameters( parameters, aLineReader );
337 paramCnt = parameters.GetCount();
338
339 /* From the Geda PCB documentation, valid Element definitions:
340 * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
341 * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
342 * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
343 * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
344 * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
345 */
346
347 if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
348 {
349 msg.Printf( _( "Unknown token '%s'" ), parameters[0] );
350 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
351 aLineReader->LineNumber(), 0 );
352 }
353
354 if( paramCnt < 10 || paramCnt > 14 )
355 {
356 msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
357 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
358 aLineReader->LineNumber(), 0 );
359 }
360
361 // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
362 if( parameters[1] == wxT( "(" ) )
363 conv_unit = OLD_GPCB_UNIT_CONV;
364
365 if( paramCnt > 10 )
366 {
367 footprint->SetLibDescription( parameters[3] );
368 footprint->SetReference( parameters[4] );
369 }
370 else
371 {
372 footprint->SetLibDescription( parameters[2] );
373 footprint->SetReference( parameters[3] );
374 }
375
376 // Read value
377 if( paramCnt > 10 )
378 footprint->SetValue( parameters[5] );
379
380 // With gEDA/pcb, value is meaningful after instantiation, only, so it's
381 // often empty in bare footprints.
382 if( footprint->Value().GetText().IsEmpty() )
383 footprint->Value().SetText( wxT( "VAL**" ) );
384
385 if( footprint->Reference().GetText().IsEmpty() )
386 footprint->Reference().SetText( wxT( "REF**" ) );
387
388 while( aLineReader->ReadLine() )
389 {
390 parameters.Clear();
391 parseParameters( parameters, aLineReader );
392
393 if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
394 continue;
395
396 if( parameters[0] == wxT( ")" ) )
397 break;
398
399 paramCnt = parameters.GetCount();
400
401 // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
402 if( paramCnt > 3 )
403 {
404 if( parameters[1] == wxT( "(" ) )
405 conv_unit = OLD_GPCB_UNIT_CONV;
406 else
407 conv_unit = NEW_GPCB_UNIT_CONV;
408 }
409
410 wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
411 parameters[0], paramCnt );
412
413 // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
414 if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
415 {
416 if( paramCnt != 8 )
417 {
418 msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
419 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
420 aLineReader->LineNumber(), 0 );
421 }
422
423 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
424 shape->SetLayer( F_SilkS );
425 shape->SetStart( VECTOR2I( parseInt( parameters[2], conv_unit ),
426 parseInt( parameters[3], conv_unit ) ) );
427 shape->SetEnd( VECTOR2I( parseInt( parameters[4], conv_unit ),
428 parseInt( parameters[5], conv_unit ) ) );
429 shape->SetStroke( STROKE_PARAMS( parseInt( parameters[6], conv_unit ),
430 LINE_STYLE::SOLID ) );
431
432 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
433 shape->Move( footprint->GetPosition() );
434
435 footprint->Add( shape );
436 continue;
437 }
438
439 // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
440 if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
441 {
442 if( paramCnt != 10 )
443 {
444 msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
445 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
446 aLineReader->LineNumber(), 0 );
447 }
448
449 // Pcbnew does know ellipse so we must have Width = Height
450 PCB_SHAPE* shape = new PCB_SHAPE( footprint.get(), SHAPE_T::ARC );
451 shape->SetLayer( F_SilkS );
452 footprint->Add( shape );
453
454 // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
455 int radius = ( parseInt( parameters[4], conv_unit ) +
456 parseInt( parameters[5], conv_unit ) ) / 2;
457
458 VECTOR2I centre( parseInt( parameters[2], conv_unit ),
459 parseInt( parameters[3], conv_unit ) );
460
461 // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
462 EDA_ANGLE start_angle( (int) parseInt( parameters[6], -10.0 ), TENTHS_OF_A_DEGREE_T );
463 start_angle += ANGLE_180;
464
465 // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
466 EDA_ANGLE sweep_angle( (int) parseInt( parameters[7], -10.0 ), TENTHS_OF_A_DEGREE_T );
467
468 // Geda PCB does not support circles.
469 if( sweep_angle == -ANGLE_360 )
470 {
471 shape->SetShape( SHAPE_T::CIRCLE );
472 shape->SetCenter( centre );
473 shape->SetEnd( centre + VECTOR2I( radius, 0 ) );
474 }
475 else
476 {
477 // Calculate start point coordinate of arc
478 VECTOR2I arcStart( radius, 0 );
479 RotatePoint( arcStart, -start_angle );
480 shape->SetCenter( centre );
481 shape->SetStart( arcStart + centre );
482
483 // Angle value is clockwise in gpcb and Pcbnew.
484 shape->SetArcAngleAndEnd( sweep_angle, true );
485 }
486
487 shape->SetStroke( STROKE_PARAMS( parseInt( parameters[8], conv_unit ),
488 LINE_STYLE::SOLID ) );
489
490 shape->Rotate( { 0, 0 }, footprint->GetOrientation() );
491 shape->Move( footprint->GetPosition() );
492 continue;
493 }
494
495 // Parse a Pad with no hole with format:
496 // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
497 // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
498 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
499 // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
500 if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
501 {
502 if( paramCnt < 10 || paramCnt > 13 )
503 {
504 msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
505 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
506 aLineReader->LineNumber(), 0 );
507 }
508
509 std::unique_ptr<PAD> pad = std::make_unique<PAD>( footprint.get() );
510
511 static const LSET pad_front( { F_Cu, F_Mask, F_Paste } );
512 static const LSET pad_back( { B_Cu, B_Mask, B_Paste } );
513
514 pad->SetShape( PAD_SHAPE::RECTANGLE );
515 pad->SetAttribute( PAD_ATTRIB::SMD );
516 pad->SetLayerSet( pad_front );
517
518 if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
519 pad->SetLayerSet( pad_back );
520
521 // Set the pad name:
522 // Pcbnew pad name is used for electrical connection calculations.
523 // Accordingly it should be mapped to gEDA's pin/pad number,
524 // which is used for the same purpose.
525 // gEDA also features a pin/pad "name", which is an arbitrary string
526 // and set to the pin name of the netlist on instantiation. Many gEDA
527 // bare footprints use identical strings for name and number, so this
528 // can be a bit confusing.
529 pad->SetNumber( parameters[paramCnt-3] );
530
531 int x1 = parseInt( parameters[2], conv_unit );
532 int x2 = parseInt( parameters[4], conv_unit );
533 int y1 = parseInt( parameters[3], conv_unit );
534 int y2 = parseInt( parameters[5], conv_unit );
535 int width = parseInt( parameters[6], conv_unit );
536 VECTOR2I delta( x2 - x1, y2 - y1 );
537 double angle = atan2( (double)delta.y, (double)delta.x );
538
539 // Get the pad clearance and the solder mask clearance.
540 if( paramCnt == 13 )
541 {
542 int clearance = parseInt( parameters[7], conv_unit );
543 // One of gEDA's oddities is that clearance between pad and polygon
544 // is given as the gap on both sides of the pad together, so for
545 // KiCad it has to halfed.
546 pad->SetLocalClearance( clearance / 2 );
547
548 // In GEDA, the mask value is the size of the hole in this
549 // solder mask. In Pcbnew, it is a margin, therefore the distance
550 // between the copper and the mask
551 int maskMargin = parseInt( parameters[8], conv_unit );
552 maskMargin = ( maskMargin - width ) / 2;
553 pad->SetLocalSolderMaskMargin( maskMargin );
554 }
555
556 // Negate angle (due to Y reversed axis)
557 EDA_ANGLE orient( -angle, RADIANS_T );
558 pad->SetOrientation( orient );
559
560 VECTOR2I padPos( ( x1 + x2 ) / 2, ( y1 + y2 ) / 2 );
561
562 pad->SetSize( VECTOR2I( delta.EuclideanNorm() + width, width ) );
563
564 padPos += footprint->GetPosition();
565 pad->SetPosition( padPos );
566
567 if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
568 {
569 if( pad->GetSize().x == pad->GetSize().y )
570 pad->SetShape( PAD_SHAPE::CIRCLE );
571 else
572 pad->SetShape( PAD_SHAPE::OVAL );
573 }
574
575 if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 )
576 {
577 footprint->Add( pad.release() );
578 }
579 else
580 {
581 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
582 aLineReader->GetSource() );
583 }
584
585 continue;
586 }
587
588 // Parse a Pin with through hole with format:
589 // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
590 // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
591 // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
592 // Pin (aX aY Thickness Drill "Name" NFlags)
593 // Pin (aX aY Thickness "Name" NFlags)
594 if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
595 {
596 if( paramCnt < 8 || paramCnt > 12 )
597 {
598 msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
599 THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
600 aLineReader->LineNumber(), 0 );
601 }
602
603 PAD* pad = new PAD( footprint.get() );
604
605 pad->SetShape( PAD_SHAPE::CIRCLE );
606
607 static const LSET pad_set = LSET::AllCuMask() | LSET( { F_SilkS, F_Mask, B_Mask } );
608
609 pad->SetLayerSet( pad_set );
610
611 if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
612 pad->SetShape( PAD_SHAPE::RECTANGLE );
613
614 // Set the pad name:
615 // Pcbnew pad name is used for electrical connection calculations.
616 // Accordingly it should be mapped to gEDA's pin/pad number,
617 // which is used for the same purpose.
618 pad->SetNumber( parameters[paramCnt-3] );
619
620 VECTOR2I padPos( parseInt( parameters[2], conv_unit ),
621 parseInt( parameters[3], conv_unit ) );
622
623 int padSize = parseInt( parameters[4], conv_unit );
624
625 pad->SetSize( VECTOR2I( padSize, padSize ) );
626
627 int drillSize = 0;
628
629 // Get the pad clearance, solder mask clearance, and drill size.
630 if( paramCnt == 12 )
631 {
632 int clearance = parseInt( parameters[5], conv_unit );
633 // One of gEDA's oddities is that clearance between pad and polygon
634 // is given as the gap on both sides of the pad together, so for
635 // KiCad it has to halfed.
636 pad->SetLocalClearance( clearance / 2 );
637
638 // In GEDA, the mask value is the size of the hole in this
639 // solder mask. In Pcbnew, it is a margin, therefore the distance
640 // between the copper and the mask
641 int maskMargin = parseInt( parameters[6], conv_unit );
642 maskMargin = ( maskMargin - padSize ) / 2;
643 pad->SetLocalSolderMaskMargin( maskMargin );
644
645 drillSize = parseInt( parameters[7], conv_unit );
646 }
647 else
648 {
649 drillSize = parseInt( parameters[5], conv_unit );
650 }
651
652 pad->SetDrillSize( VECTOR2I( drillSize, drillSize ) );
653
654 padPos += footprint->GetPosition();
655 pad->SetPosition( padPos );
656
657 if( pad->GetShape() == PAD_SHAPE::CIRCLE && pad->GetSize().x != pad->GetSize().y )
658 pad->SetShape( PAD_SHAPE::OVAL );
659
660 footprint->Add( pad );
661 continue;
662 }
663 }
664
665 footprint->AutoPositionFields();
666
667 return footprint.release();
668}
669
670
671void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
672{
673 char key;
674 wxString tmp;
675 char* line = aLineReader->Line();
676
677 // Last line already ready in main parser loop.
678 while( *line != 0 )
679 {
680 key = *line;
681 line++;
682
683 switch( key )
684 {
685 case '[':
686 case '(':
687 if( !tmp.IsEmpty() )
688 {
689 aParameterList.Add( tmp );
690 tmp.Clear();
691 }
692
693 tmp.Append( key );
694 aParameterList.Add( tmp );
695 tmp.Clear();
696
697 // Opening delimiter "(" after Element statement. Any other occurrence is part
698 // of a keyword definition.
699 if( aParameterList.GetCount() == 1 )
700 {
701 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
702 return;
703 }
704
705 break;
706
707 case ']':
708 case ')':
709 if( !tmp.IsEmpty() )
710 {
711 aParameterList.Add( tmp );
712 tmp.Clear();
713 }
714
715 tmp.Append( key );
716 aParameterList.Add( tmp );
717 wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
718 return;
719
720 case '\n':
721 case '\r':
722 // Element descriptions can span multiple lines.
723 line = aLineReader->ReadLine();
725
726 case '\t':
727 case ' ':
728 if( !tmp.IsEmpty() )
729 {
730 aParameterList.Add( tmp );
731 tmp.Clear();
732 }
733
734 break;
735
736 case '"':
737 // Handle empty quotes.
738 if( *line == '"' )
739 {
740 line++;
741 tmp.Clear();
742 aParameterList.Add( wxEmptyString );
743 break;
744 }
745
746 while( *line != 0 )
747 {
748 key = *line;
749 line++;
750
751 if( key == '"' )
752 {
753 aParameterList.Add( tmp );
754 tmp.Clear();
755 break;
756 }
757 else
758 {
759 tmp.Append( key );
760 }
761 }
762
763 break;
764
765 case '#':
766 line = aLineReader->ReadLine();
767 break;
768
769 default:
770 tmp.Append( key );
771 break;
772 }
773 }
774}
775
776
777bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
778{
779 wxString number;
780
781 if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
782 {
783 long lflags;
784
785 if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
786 return true;
787 }
788 else if( aFlag.Contains( aName ) )
789 {
790 return true;
791 }
792
793 return false;
794}
795
796
797PCB_IO_GEDA::PCB_IO_GEDA() : PCB_IO( wxS( "gEDA PCB" ) ),
798 m_cache( nullptr ),
799 m_ctl( 0 )
800{
801 m_reader = nullptr;
802 init( nullptr );
803}
804
805
806PCB_IO_GEDA::PCB_IO_GEDA( int aControlFlags ) : PCB_IO( wxS( "gEDA PCB" ) ),
807 m_cache( nullptr ),
808 m_ctl( aControlFlags )
809{
810 m_reader = nullptr;
811 init( nullptr );
812}
813
814
816{
817 delete m_cache;
818}
819
820
821void PCB_IO_GEDA::init( const STRING_UTF8_MAP* aProperties )
822{
823 m_props = aProperties;
824}
825
826
827void PCB_IO_GEDA::validateCache( const wxString& aLibraryPath, bool checkModified )
828{
829 if( !m_cache || ( checkModified && m_cache->IsModified() ) )
830 {
831 // a spectacular episode in memory management:
832 delete m_cache;
833 m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
834 m_cache->Load();
835 }
836}
837
838
839FOOTPRINT* PCB_IO_GEDA::ImportFootprint( const wxString& aFootprintPath,
840 wxString& aFootprintNameOut,
841 const STRING_UTF8_MAP* aProperties )
842{
843 wxFileName fn( aFootprintPath );
844
845 FILE_LINE_READER freader( aFootprintPath );
846 WHITESPACE_FILTER_READER reader( freader );
847
848 reader.ReadLine();
849 char* line = reader.Line();
850
851 if( !line )
852 return nullptr;
853
854 if( strncasecmp( line, "Element", strlen( "Element" ) ) != 0 )
855 return nullptr;
856
857 aFootprintNameOut = fn.GetName();
858
859 return FootprintLoad( fn.GetPath(), aFootprintNameOut );
860}
861
862
863void PCB_IO_GEDA::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
864 bool aBestEfforts, const STRING_UTF8_MAP* aProperties )
865{
866 LOCALE_IO toggle; // toggles on, then off, the C locale.
867 wxDir dir( aLibraryPath );
868 wxString errorMsg;
869
870 if( !dir.IsOpened() )
871 {
872 if( aBestEfforts )
873 return;
874 else
875 {
876 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ),
877 aLibraryPath ) );
878 }
879 }
880
881 init( aProperties );
882
883 try
884 {
885 validateCache( aLibraryPath );
886 }
887 catch( const IO_ERROR& ioe )
888 {
889 errorMsg = ioe.What();
890 }
891
892 // Some of the files may have been parsed correctly so we want to add the valid files to
893 // the library.
894
895 for( const auto& footprint : m_cache->GetFootprints() )
896 aFootprintNames.Add( From_UTF8( footprint.first.c_str() ) );
897
898 if( !errorMsg.IsEmpty() && !aBestEfforts )
899 THROW_IO_ERROR( errorMsg );
900}
901
902
903const FOOTPRINT* PCB_IO_GEDA::getFootprint( const wxString& aLibraryPath,
904 const wxString& aFootprintName,
905 const STRING_UTF8_MAP* aProperties,
906 bool checkModified )
907{
908 LOCALE_IO toggle; // toggles on, then off, the C locale.
909
910 init( aProperties );
911
912 validateCache( aLibraryPath, checkModified );
913
914 const FOOTPRINT_MAP& mods = m_cache->GetFootprints();
915
916 FOOTPRINT_MAP::const_iterator it = mods.find( TO_UTF8( aFootprintName ) );
917
918 if( it == mods.end() )
919 return nullptr;
920
921 return it->second->GetFootprint();
922}
923
924
925const FOOTPRINT* PCB_IO_GEDA::GetEnumeratedFootprint( const wxString& aLibraryPath,
926 const wxString& aFootprintName,
927 const STRING_UTF8_MAP* aProperties )
928{
929 return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
930}
931
932
933FOOTPRINT* PCB_IO_GEDA::FootprintLoad( const wxString& aLibraryPath,
934 const wxString& aFootprintName,
935 bool aKeepUUID,
936 const STRING_UTF8_MAP* aProperties )
937{
939
940 const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
941
942 if( footprint )
943 {
944 FOOTPRINT* copy = (FOOTPRINT*) footprint->Duplicate();
945 copy->SetParent( nullptr );
946 return copy;
947 }
948
949 return nullptr;
950}
951
952
953void PCB_IO_GEDA::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
954 const STRING_UTF8_MAP* aProperties )
955{
956 LOCALE_IO toggle; // toggles on, then off, the C locale.
957
958 init( aProperties );
959
960 validateCache( aLibraryPath );
961
962 if( !m_cache->IsWritable() )
963 {
964 THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
965 aLibraryPath.GetData() ) );
966 }
967
968 m_cache->Remove( aFootprintName );
969}
970
971
972bool PCB_IO_GEDA::DeleteLibrary( const wxString& aLibraryPath, const STRING_UTF8_MAP* aProperties )
973{
974 wxFileName fn;
975 fn.SetPath( aLibraryPath );
976
977 // Return if there is no library path to delete.
978 if( !fn.DirExists() )
979 return false;
980
981 if( !fn.IsDirWritable() )
982 {
983 THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
984 aLibraryPath.GetData() ) );
985 }
986
987 wxDir dir( aLibraryPath );
988
989 if( dir.HasSubDirs() )
990 {
991 THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
992 aLibraryPath.GetData() ) );
993 }
994
995 // All the footprint files must be deleted before the directory can be deleted.
996 if( dir.HasFiles() )
997 {
998 unsigned i;
999 wxFileName tmp;
1000 wxArrayString files;
1001
1002 wxDir::GetAllFiles( aLibraryPath, &files );
1003
1004 for( i = 0; i < files.GetCount(); i++ )
1005 {
1006 tmp = files[i];
1007
1008 if( tmp.GetExt() != FILEEXT::KiCadFootprintFileExtension )
1009 {
1010 THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library '%s'." ),
1011 files[i].GetData(),
1012 aLibraryPath.GetData() ) );
1013 }
1014 }
1015
1016 for( i = 0; i < files.GetCount(); i++ )
1017 {
1018 wxRemoveFile( files[i] );
1019 }
1020 }
1021
1022 wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1023 aLibraryPath.GetData() );
1024
1025 // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1026 // we don't want that. we want bare metal portability with no UI here.
1027 if( !wxRmdir( aLibraryPath ) )
1028 {
1029 THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
1030 aLibraryPath.GetData() ) );
1031 }
1032
1033 // For some reason removing a directory in Windows is not immediately updated. This delay
1034 // prevents an error when attempting to immediately recreate the same directory when over
1035 // writing an existing library.
1036#ifdef __WINDOWS__
1037 wxMilliSleep( 250L );
1038#endif
1039
1040 if( m_cache && m_cache->GetPath() == aLibraryPath )
1041 {
1042 delete m_cache;
1043 m_cache = nullptr;
1044 }
1045
1046 return true;
1047}
1048
1049
1050long long PCB_IO_GEDA::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1051{
1052 return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1053}
1054
1055
1056bool PCB_IO_GEDA::IsLibraryWritable( const wxString& aLibraryPath )
1057{
1058 LOCALE_IO toggle;
1059
1060 init( nullptr );
1061
1062 validateCache( aLibraryPath );
1063
1064 return m_cache->IsWritable();
1065}
const char * name
Definition: DXF_plotter.cpp:57
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:564
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:710
A LINE_READER that reads from an open file.
Definition: richio.h:185
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:237
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:2433
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:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:737
Definition: pad.h:54
A #PLUGIN derivation for saving and loading Geda PCB files.
Definition: pcb_io_geda.h:48
void init(const STRING_UTF8_MAP *aProperties)
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties, bool checkModified)
bool IsLibraryWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
GPCB_FPL_CACHE * m_cache
Footprint library cache.
Definition: pcb_io_geda.h:106
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const STRING_UTF8_MAP *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
bool DeleteLibrary(const wxString &aLibraryPath, const STRING_UTF8_MAP *aProperties=nullptr) override
Delete an existing library and returns true, or if library does not exist returns false,...
FOOTPRINT * ImportFootprint(const wxString &aFootprintPath, wxString &aFootprintNameOut, const STRING_UTF8_MAP *aProperties) override
Load a single footprint from aFootprintPath and put its name in aFootprintNameOut.
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const STRING_UTF8_MAP *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.
LINE_READER * m_reader
no ownership here.
Definition: pcb_io_geda.h:108
friend class GPCB_FPL_CACHE
Definition: pcb_io_geda.h:102
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const STRING_UTF8_MAP *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
A base class that BOARD loading and saving plugins should derive from.
Definition: pcb_io.h:72
const STRING_UTF8_MAP * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
Definition: pcb_io.h:346
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:526
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:317
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:455
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:86
A name/value tuple with unique names and optional values.
Simple container to manage line stroke parameters.
Definition: stroke_params.h:81
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:63
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:599
#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:101
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ F_Mask
Definition: layer_ids.h:107
@ B_Paste
Definition: layer_ids.h:100
@ F_SilkS
Definition: layer_ids.h:104
@ 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:228
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673
Definition of file extensions used in Kicad.