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