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