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