KiCad PCB EDA Suite
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-2021 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>
41 #include <fp_shape.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 
51 
52 static 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  if( value == std::numeric_limits<double>::max() ) // conversion really failed
90  {
91  THROW_IO_ERROR( wxString::Format( _( "Cannot convert '%s' to an integer." ),
92  aValue.GetData() ) );
93  return 0;
94  }
95 
96  return KiROUND( value * aScalar );
97 }
98 
99 
109 {
110 public:
111  GPCB_FPL_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName );
112 
113  WX_FILENAME GetFileName() const { return m_filename; }
114  FOOTPRINT* GetFootprint() const { return m_footprint.get(); }
115 
116 private:
118  std::unique_ptr<FOOTPRINT> m_footprint;
119 };
120 
121 
123  m_filename( aFileName ),
124  m_footprint( aFootprint )
125 {
126 }
127 
128 
129 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > FOOTPRINT_MAP;
130 
131 
133 {
134 public:
135  GPCB_FPL_CACHE( GPCB_PLUGIN* 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 
164 private:
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 
201  long long m_cache_timestamp;
203 };
205 
206 
207 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
208 {
209  m_owner = aOwner;
210  m_lib_path.SetPath( aLibraryPath );
211  m_cache_timestamp = 0;
212  m_cache_dirty = true;
213 }
214 
215 
217 {
218  m_cache_dirty = false;
219  m_cache_timestamp = 0;
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( "*." ) + 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 += "\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 
274 void 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 
302 long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
303 {
304  wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
305 
306  return TimestampDir( aLibPath, fileSpec );
307 }
308 
309 
311 {
312  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
313  #define OLD_GPCB_UNIT_CONV 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*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  wxPoint 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() + ": 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->SetDescription( parameters[3] );
366  footprint->SetReference( parameters[4] );
367  }
368  else
369  {
370  footprint->SetDescription( 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 
384  if( paramCnt == 14 )
385  {
386  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
387  parseInt( parameters[9], conv_unit ) );
388  }
389  else
390  {
391  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
392  parseInt( parameters[7], conv_unit ) );
393  }
394 
395  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
396  footprint->Reference().SetTextAngle(( orientation % 2) ? 900 : 0 );
397 
398  // Calculate size: default height is 40 mils, width 30 mil.
399  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
400  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
401  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
402  int twsize = thsize * 30 / 40;
403  int thickness = thsize / 8;
404 
405  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
406  // Compensate for this by shifting the insertion point instead of the
407  // alignment, because alignment isn't changeable in the GUI.
408  textPos.x = textPos.x + twsize * footprint->GetReference().Len() / 2;
409  textPos.y += thsize / 2;
410 
411  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
412  // high/right. Compensate for similar appearance.
413  textPos.x -= thsize / 10;
414  textPos.y += thsize / 2;
415 
416  footprint->Reference().SetTextPos( textPos );
417  footprint->Reference().SetPos0( textPos );
418  footprint->Reference().SetTextSize( wxSize( twsize, thsize ) );
419  footprint->Reference().SetTextThickness( thickness );
420 
421  // gEDA/pcb shows only one of value/reference/description at a time. Which
422  // one is selectable by a global menu setting. pcbnew needs reference as
423  // well as value visible, so place the value right below the reference.
424  footprint->Value().SetTextAngle( footprint->Reference().GetTextAngle() );
425  footprint->Value().SetTextSize( footprint->Reference().GetTextSize() );
426  footprint->Value().SetTextThickness( footprint->Reference().GetTextThickness() );
427  textPos.y += thsize * 13 / 10; // 130% line height
428  footprint->Value().SetTextPos( textPos );
429  footprint->Value().SetPos0( textPos );
430 
431  while( aLineReader->ReadLine() )
432  {
433  parameters.Clear();
434  parseParameters( parameters, aLineReader );
435 
436  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
437  continue;
438 
439  if( parameters[0] == wxT( ")" ) )
440  break;
441 
442  paramCnt = parameters.GetCount();
443 
444  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
445  if( paramCnt > 3 )
446  {
447  if( parameters[1] == wxT( "(" ) )
448  conv_unit = OLD_GPCB_UNIT_CONV;
449  else
450  conv_unit = NEW_GPCB_UNIT_CONV;
451  }
452 
453  wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
454  parameters[0], paramCnt );
455 
456  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
457  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
458  {
459  if( paramCnt != 8 )
460  {
461  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
462  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
463  aLineReader->LineNumber(), 0 );
464  }
465 
466  FP_SHAPE* shape = new FP_SHAPE( footprint.get(), SHAPE_T::SEGMENT );
467  shape->SetLayer( F_SilkS );
468  shape->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
469  parseInt( parameters[3], conv_unit ) ) );
470  shape->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
471  parseInt( parameters[5], conv_unit ) ) );
472  shape->SetWidth( parseInt( parameters[6], conv_unit ) );
473  shape->SetDrawCoord();
474  footprint->Add( shape );
475  continue;
476  }
477 
478  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
479  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
480  {
481  if( paramCnt != 10 )
482  {
483  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
484  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
485  aLineReader->LineNumber(), 0 );
486  }
487 
488  // Pcbnew does know ellipse so we must have Width = Height
489  FP_SHAPE* shape = new FP_SHAPE( footprint.get(), SHAPE_T::ARC );
490  shape->SetLayer( F_SilkS );
491  footprint->Add( shape );
492 
493  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
494  int radius = ( parseInt( parameters[4], conv_unit ) +
495  parseInt( parameters[5], conv_unit ) ) / 2;
496 
497  wxPoint centre( parseInt( parameters[2], conv_unit ),
498  parseInt( parameters[3], conv_unit ) );
499 
500  shape->SetCenter0( centre );
501 
502  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
503  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
504 
505  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
506  double sweep_angle = parseInt( parameters[7], -10.0 );
507 
508  // Geda PCB does not support circles.
509  if( sweep_angle == -3600.0 )
510  shape->SetShape( SHAPE_T::CIRCLE );
511 
512  // Calculate start point coordinate of arc
513  wxPoint arcStart( radius, 0 );
514  RotatePoint( &arcStart, -start_angle );
515  shape->SetStart0( arcStart + centre );
516 
517  // Angle value is clockwise in gpcb and Pcbnew.
518  shape->SetArcAngleAndEnd0( sweep_angle );
519 
520  shape->SetWidth( parseInt( parameters[8], conv_unit ) );
521  shape->SetDrawCoord();
522  continue;
523  }
524 
525  // Parse a Pad with no hole with format:
526  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
527  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
528  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
529  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
530  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
531  {
532  if( paramCnt < 10 || paramCnt > 13 )
533  {
534  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
535  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
536  aLineReader->LineNumber(), 0 );
537  }
538 
539  PAD* pad = new PAD( footprint.get() );
540 
541  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
542  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
543 
544  pad->SetShape( PAD_SHAPE::RECT );
545  pad->SetAttribute( PAD_ATTRIB::SMD );
546  pad->SetLayerSet( pad_front );
547 
548  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
549  pad->SetLayerSet( pad_back );
550 
551  // Set the pad name:
552  // Pcbnew pad name is used for electrical connection calculations.
553  // Accordingly it should be mapped to gEDA's pin/pad number,
554  // which is used for the same purpose.
555  // gEDA also features a pin/pad "name", which is an arbitrary string
556  // and set to the pin name of the netlist on instantiation. Many gEDA
557  // bare footprints use identical strings for name and number, so this
558  // can be a bit confusing.
559  pad->SetNumber( parameters[paramCnt-3] );
560 
561  int x1 = parseInt( parameters[2], conv_unit );
562  int x2 = parseInt( parameters[4], conv_unit );
563  int y1 = parseInt( parameters[3], conv_unit );
564  int y2 = parseInt( parameters[5], conv_unit );
565  int width = parseInt( parameters[6], conv_unit );
566  wxPoint delta( x2 - x1, y2 - y1 );
567  double angle = atan2( (double)delta.y, (double)delta.x );
568 
569  // Get the pad clearance and the solder mask clearance.
570  if( paramCnt == 13 )
571  {
572  int clearance = parseInt( parameters[7], conv_unit );
573  // One of gEDA's oddities is that clearance between pad and polygon
574  // is given as the gap on both sides of the pad together, so for
575  // KiCad it has to halfed.
576  pad->SetLocalClearance( clearance / 2 );
577 
578  // In GEDA, the mask value is the size of the hole in this
579  // solder mask. In Pcbnew, it is a margin, therefore the distance
580  // between the copper and the mask
581  int maskMargin = parseInt( parameters[8], conv_unit );
582  maskMargin = ( maskMargin - width ) / 2;
583  pad->SetLocalSolderMaskMargin( maskMargin );
584  }
585 
586  // Negate angle (due to Y reversed axis) and convert it to internal units
587  angle = - RAD2DECIDEG( angle );
588  pad->SetOrientation( KiROUND( angle ) );
589 
590  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
591 
592  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width, width ) );
593 
594  // Set the relative position before adjusting the absolute position
595  pad->SetPos0( padPos );
596  padPos += footprint->GetPosition();
597  pad->SetPosition( padPos );
598 
599  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
600  {
601  if( pad->GetSize().x == pad->GetSize().y )
602  pad->SetShape( PAD_SHAPE::CIRCLE );
603  else
604  pad->SetShape( PAD_SHAPE::OVAL );
605  }
606 
607  footprint->Add( pad );
608  continue;
609  }
610 
611  // Parse a Pin with through hole with format:
612  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
613  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
614  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
615  // Pin (aX aY Thickness Drill "Name" NFlags)
616  // Pin (aX aY Thickness "Name" NFlags)
617  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
618  {
619  if( paramCnt < 8 || paramCnt > 12 )
620  {
621  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
622  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
623  aLineReader->LineNumber(), 0 );
624  }
625 
626  PAD* pad = new PAD( footprint.get() );
627 
628  pad->SetShape( PAD_SHAPE::CIRCLE );
629 
630  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
631 
632  pad->SetLayerSet( pad_set );
633 
634  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
635  pad->SetShape( PAD_SHAPE::RECT );
636 
637  // Set the pad name:
638  // Pcbnew pad name is used for electrical connection calculations.
639  // Accordingly it should be mapped to gEDA's pin/pad number,
640  // which is used for the same purpose.
641  pad->SetNumber( parameters[paramCnt-3] );
642 
643  wxPoint padPos( parseInt( parameters[2], conv_unit ),
644  parseInt( parameters[3], conv_unit ) );
645 
646  int padSize = parseInt( parameters[4], conv_unit );
647 
648  pad->SetSize( wxSize( padSize, padSize ) );
649 
650  int drillSize = 0;
651 
652  // Get the pad clearance, solder mask clearance, and drill size.
653  if( paramCnt == 12 )
654  {
655  int clearance = parseInt( parameters[5], conv_unit );
656  // One of gEDA's oddities is that clearance between pad and polygon
657  // is given as the gap on both sides of the pad together, so for
658  // KiCad it has to halfed.
659  pad->SetLocalClearance( clearance / 2 );
660 
661  // In GEDA, the mask value is the size of the hole in this
662  // solder mask. In Pcbnew, it is a margin, therefore the distance
663  // between the copper and the mask
664  int maskMargin = parseInt( parameters[6], conv_unit );
665  maskMargin = ( maskMargin - padSize ) / 2;
666  pad->SetLocalSolderMaskMargin( maskMargin );
667 
668  drillSize = parseInt( parameters[7], conv_unit );
669  }
670  else
671  {
672  drillSize = parseInt( parameters[5], conv_unit );
673  }
674 
675  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
676 
677  // Set the relative position before adjusting the absolute position
678  pad->SetPos0( padPos );
679  padPos += footprint->GetPosition();
680  pad->SetPosition( padPos );
681 
682  if( pad->GetShape() == PAD_SHAPE::CIRCLE && pad->GetSize().x != pad->GetSize().y )
683  pad->SetShape( PAD_SHAPE::OVAL );
684 
685  footprint->Add( pad );
686  continue;
687  }
688  }
689 
690  return footprint.release();
691 }
692 
693 
694 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
695 {
696  char key;
697  wxString tmp;
698  char* line = aLineReader->Line();
699 
700  // Last line already ready in main parser loop.
701  while( *line != 0 )
702  {
703  key = *line;
704  line++;
705 
706  switch( key )
707  {
708  case '[':
709  case '(':
710  if( !tmp.IsEmpty() )
711  {
712  aParameterList.Add( tmp );
713  tmp.Clear();
714  }
715 
716  tmp.Append( key );
717  aParameterList.Add( tmp );
718  tmp.Clear();
719 
720  // Opening delimiter "(" after Element statement. Any other occurrence is part
721  // of a keyword definition.
722  if( aParameterList.GetCount() == 1 )
723  {
724  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
725  return;
726  }
727 
728  break;
729 
730  case ']':
731  case ')':
732  if( !tmp.IsEmpty() )
733  {
734  aParameterList.Add( tmp );
735  tmp.Clear();
736  }
737 
738  tmp.Append( key );
739  aParameterList.Add( tmp );
740  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
741  return;
742 
743  case '\n':
744  case '\r':
745  // Element descriptions can span multiple lines.
746  line = aLineReader->ReadLine();
748 
749  case '\t':
750  case ' ':
751  if( !tmp.IsEmpty() )
752  {
753  aParameterList.Add( tmp );
754  tmp.Clear();
755  }
756 
757  break;
758 
759  case '"':
760  // Handle empty quotes.
761  if( *line == '"' )
762  {
763  line++;
764  tmp.Clear();
765  aParameterList.Add( wxEmptyString );
766  break;
767  }
768 
769  while( *line != 0 )
770  {
771  key = *line;
772  line++;
773 
774  if( key == '"' )
775  {
776  aParameterList.Add( tmp );
777  tmp.Clear();
778  break;
779  }
780  else
781  {
782  tmp.Append( key );
783  }
784  }
785 
786  break;
787 
788  case '#':
789  line = aLineReader->ReadLine();
790  break;
791 
792  default:
793  tmp.Append( key );
794  break;
795  }
796  }
797 }
798 
799 
800 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
801 {
802  wxString number;
803 
804  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
805  {
806  long lflags;
807 
808  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
809  return true;
810  }
811  else if( aFlag.Contains( aName ) )
812  {
813  return true;
814  }
815 
816  return false;
817 }
818 
819 
821  m_cache( nullptr ),
822  m_ctl( 0 )
823 {
824  m_reader = nullptr;
825  init( nullptr );
826 }
827 
828 
829 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
830  m_cache( nullptr ),
831  m_ctl( aControlFlags )
832 {
833  m_reader = nullptr;
834  init( nullptr );
835 }
836 
837 
839 {
840  delete m_cache;
841 }
842 
843 
844 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
845 {
846  m_props = aProperties;
847 }
848 
849 
850 void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
851 {
852  if( !m_cache || ( checkModified && m_cache->IsModified() ) )
853  {
854  // a spectacular episode in memory management:
855  delete m_cache;
856  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
857  m_cache->Load();
858  }
859 }
860 
861 
862 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
863  bool aBestEfforts, const PROPERTIES* aProperties )
864 {
865  LOCALE_IO toggle; // toggles on, then off, the C locale.
866  wxDir dir( aLibraryPath );
867  wxString errorMsg;
868 
869  if( !dir.IsOpened() )
870  {
871  if( aBestEfforts )
872  return;
873  else
874  {
875  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ),
876  aLibraryPath ) );
877  }
878  }
879 
880  init( aProperties );
881 
882  try
883  {
884  validateCache( aLibraryPath );
885  }
886  catch( const IO_ERROR& ioe )
887  {
888  errorMsg = ioe.What();
889  }
890 
891  // Some of the files may have been parsed correctly so we want to add the valid files to
892  // the library.
893 
894  for( const auto& footprint : m_cache->GetFootprints() )
895  aFootprintNames.Add( FROM_UTF8( footprint.first.c_str() ) );
896 
897  if( !errorMsg.IsEmpty() && !aBestEfforts )
898  THROW_IO_ERROR( errorMsg );
899 }
900 
901 
902 const FOOTPRINT* GPCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
903  const wxString& aFootprintName,
904  const PROPERTIES* aProperties,
905  bool checkModified )
906 {
907  LOCALE_IO toggle; // toggles on, then off, the C locale.
908 
909  init( aProperties );
910 
911  validateCache( aLibraryPath, checkModified );
912 
913  const FOOTPRINT_MAP& mods = m_cache->GetFootprints();
914 
915  FOOTPRINT_MAP::const_iterator it = mods.find( TO_UTF8( aFootprintName ) );
916 
917  if( it == mods.end() )
918  return nullptr;
919 
920  return it->second->GetFootprint();
921 }
922 
923 
924 const FOOTPRINT* GPCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
925  const wxString& aFootprintName,
926  const PROPERTIES* aProperties )
927 {
928  return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
929 }
930 
931 
932 FOOTPRINT* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
933  const wxString& aFootprintName,
934  bool aKeepUUID,
935  const PROPERTIES* aProperties )
936 {
937  const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
938 
939  if( footprint )
940  {
941  FOOTPRINT* copy = (FOOTPRINT*) footprint->Duplicate();
942  copy->SetParent( nullptr );
943  return copy;
944  }
945 
946  return nullptr;
947 }
948 
949 
950 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
951  const PROPERTIES* aProperties )
952 {
953  LOCALE_IO toggle; // toggles on, then off, the C locale.
954 
955  init( aProperties );
956 
957  validateCache( aLibraryPath );
958 
959  if( !m_cache->IsWritable() )
960  {
961  THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
962  aLibraryPath.GetData() ) );
963  }
964 
965  m_cache->Remove( aFootprintName );
966 }
967 
968 
969 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
970 {
971  wxFileName fn;
972  fn.SetPath( aLibraryPath );
973 
974  // Return if there is no library path to delete.
975  if( !fn.DirExists() )
976  return false;
977 
978  if( !fn.IsDirWritable() )
979  {
980  THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
981  aLibraryPath.GetData() ) );
982  }
983 
984  wxDir dir( aLibraryPath );
985 
986  if( dir.HasSubDirs() )
987  {
988  THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
989  aLibraryPath.GetData() ) );
990  }
991 
992  // All the footprint files must be deleted before the directory can be deleted.
993  if( dir.HasFiles() )
994  {
995  unsigned i;
996  wxFileName tmp;
997  wxArrayString files;
998 
999  wxDir::GetAllFiles( aLibraryPath, &files );
1000 
1001  for( i = 0; i < files.GetCount(); i++ )
1002  {
1003  tmp = files[i];
1004 
1005  if( tmp.GetExt() != KiCadFootprintFileExtension )
1006  {
1007  THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library '%s'." ),
1008  files[i].GetData(),
1009  aLibraryPath.GetData() ) );
1010  }
1011  }
1012 
1013  for( i = 0; i < files.GetCount(); i++ )
1014  {
1015  wxRemoveFile( files[i] );
1016  }
1017  }
1018 
1019  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1020  aLibraryPath.GetData() );
1021 
1022  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1023  // we don't want that. we want bare metal portability with no UI here.
1024  if( !wxRmdir( aLibraryPath ) )
1025  {
1026  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
1027  aLibraryPath.GetData() ) );
1028  }
1029 
1030  // For some reason removing a directory in Windows is not immediately updated. This delay
1031  // prevents an error when attempting to immediately recreate the same directory when over
1032  // writing an existing library.
1033 #ifdef __WINDOWS__
1034  wxMilliSleep( 250L );
1035 #endif
1036 
1037  if( m_cache && m_cache->GetPath() == aLibraryPath )
1038  {
1039  delete m_cache;
1040  m_cache = nullptr;
1041  }
1042 
1043  return true;
1044 }
1045 
1046 
1047 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1048 {
1049  return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1050 }
1051 
1052 
1053 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1054 {
1055  LOCALE_IO toggle;
1056 
1057  init( nullptr );
1058 
1059  validateCache( aLibraryPath );
1060 
1061  return m_cache->IsWritable();
1062 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:759
bool IsWritable() const
FOOTPRINT * GetFootprint() const
An abstract class from which implementation specific LINE_READERs may be derived to read single lines...
Definition: richio.h:80
char * Line() const
Return a pointer to the last line that was read in.
Definition: richio.h:117
void SetEnd0(const wxPoint &aPoint)
Definition: fp_shape.h:114
GPCB_PLUGIN * m_owner
Plugin object that owns the cache.
FOOTPRINT_MAP m_footprints
Map of footprint file name to FOOTPRINT*.
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
A PLUGIN derivation for saving and loading Geda PCB files.
Definition: gpcb_plugin.h:46
virtual const wxString & GetSource() const
Returns the name of the source of the lines in an abstract sense.
Definition: richio.h:109
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
#define OLD_GPCB_UNIT_CONV
static long long GetTimestamp(const wxString &aLibPath)
Generate a timestamp representing all source files in the cache (including the parent directory).
wxString GetPath() const
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:163
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
const std::string KiCadFootprintFileExtension
void SetArcAngleAndEnd0(double aAngle, bool aCheckNegativeAngle=false)
Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: fp_shape.cpp:188
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const PROPERTIES *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PL...
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
WX_FILENAME m_filename
The full file name and path of the footprint to cache.
Smd pad, appears on the solder paste layer (default)
WX_FILENAME GetFileName() const
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
FOOTPRINT_MAP & GetFootprints()
helper class for creating a footprint library cache.
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Extract parameters and tokens from aLineReader and adds them to aParameterList.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
void Remove(const wxString &aFootprintName)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void SetFullName(const wxString &aFileNameAndExtension)
Definition: wx_filename.cpp:34
A name/value tuple with unique names and optional values.
Definition: properties.h:33
void init(const PROPERTIES *aProperties)
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
GPCB_FPL_CACHE_ITEM(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
GPCB_FPL_CACHE * m_cache
Footprint library cache.
Definition: gpcb_plugin.h:102
A LINE_READER that reads from an open file.
Definition: richio.h:172
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:505
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
const FOOTPRINT * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties, bool checkModified)
virtual unsigned LineNumber() const
Return the line number of the last line read from this LINE_READER.
Definition: richio.h:135
#define NEW_GPCB_UNIT_CONV
Definition of file extensions used in Kicad.
FOOTPRINT * parseFOOTPRINT(LINE_READER *aLineReader)
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
Definition: gpcb_plugin.h:101
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:504
#define _(s)
wxFileName m_lib_path
The path of the library.
bool FootprintLibDelete(const wxString &aLibraryPath, const PROPERTIES *aProperties=nullptr) override
Delete an existing footprint library and returns true, or if library does not exist returns false,...
wxLogTrace helper definitions.
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:1724
void SetWidth(int aWidth)
Definition: eda_shape.h:88
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
Definition: fp_shape.cpp:81
void Load()
Save not implemented for the Geda PCB footprint library format.
void SetStart0(const wxPoint &aPoint)
Definition: fp_shape.h:111
long long m_cache_timestamp
A hash of the timestamps for all the footprint files.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
virtual char * ReadLine()=0
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:36
const char * name
Definition: DXF_plotter.cpp:56
#define TEXT_DEFAULT_SIZE
GPCB_FPL_CACHE(GPCB_PLUGIN *aOwner, const wxString &aLibraryPath)
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
std::map< wxString, FOOTPRINT * > FOOTPRINT_MAP
Definition: eagle_parser.h:50
std::unique_ptr< FOOTPRINT > m_footprint
Definition: layer_ids.h:71
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:91
#define IU_PER_MILS
Definition: plotter.cpp:136
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:73
const std::string GedaPcbFootprintLibFileExtension
constexpr int delta
bool IsModified()
Return true if the cache is not up-to-date.
friend class GPCB_FPL_CACHE
Definition: gpcb_plugin.h:97
LINE_READER * m_reader
no ownership here.
Definition: gpcb_plugin.h:104
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Test aFlag for aMask or aName.
Definition: pad.h:57
bool m_cache_dirty
Stored separately because it's expensive to check m_cache_timestamp against all the files.
void SetCenter0(const wxPoint &aPt)
Definition: fp_shape.cpp:162
boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > FOOTPRINT_MAP
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:75
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
static long parseInt(const wxString &aValue, double aScalar)
Definition: gpcb_plugin.cpp:52
const FOOTPRINT * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
A version of FootprintLoad() for use after FootprintEnumerate() for more efficient cache management.