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