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 <stambaughw@gmail.com>
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() );
467  shape->SetLayer( F_SilkS );
468  shape->SetShape( SHAPE_T::SEGMENT );
469  shape->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
470  parseInt( parameters[3], conv_unit ) ) );
471  shape->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
472  parseInt( parameters[5], conv_unit ) ) );
473  shape->SetWidth( parseInt( parameters[6], conv_unit ) );
474  shape->SetDrawCoord();
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  FP_SHAPE* shape = new FP_SHAPE( footprint.get() );
491  shape->SetLayer( F_SilkS );
492  shape->SetShape( SHAPE_T::ARC );
493  footprint->Add( shape );
494 
495  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
496  int radius = ( parseInt( parameters[4], conv_unit ) +
497  parseInt( parameters[5], conv_unit ) ) / 2;
498 
499  wxPoint centre( parseInt( parameters[2], conv_unit ),
500  parseInt( parameters[3], conv_unit ) );
501 
502  shape->SetStart0( centre );
503 
504  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
505  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
506 
507  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
508  double sweep_angle = parseInt( parameters[7], -10.0 );
509 
510  // Geda PCB does not support circles.
511  if( sweep_angle == -3600.0 )
512  shape->SetShape( SHAPE_T::CIRCLE );
513 
514  // Angle value is clockwise in gpcb and Pcbnew.
515  shape->SetAngle( sweep_angle );
516  shape->SetEnd0( wxPoint( radius, 0 ) );
517 
518  // Calculate start point coordinate of arc
519  wxPoint arcStart( shape->GetEnd0() );
520  RotatePoint( &arcStart, -start_angle );
521  shape->SetEnd0( centre + arcStart );
522  shape->SetWidth( parseInt( parameters[8], conv_unit ) );
523  shape->SetDrawCoord();
524  continue;
525  }
526 
527  // Parse a Pad with no hole with format:
528  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
529  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
530  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
531  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
532  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
533  {
534  if( paramCnt < 10 || paramCnt > 13 )
535  {
536  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
537  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
538  aLineReader->LineNumber(), 0 );
539  }
540 
541  PAD* pad = new PAD( footprint.get() );
542 
543  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
544  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
545 
546  pad->SetShape( PAD_SHAPE::RECT );
547  pad->SetAttribute( PAD_ATTRIB::SMD );
548  pad->SetLayerSet( pad_front );
549 
550  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
551  pad->SetLayerSet( pad_back );
552 
553  // Set the pad name:
554  // Pcbnew pad name is used for electrical connection calculations.
555  // Accordingly it should be mapped to gEDA's pin/pad number,
556  // which is used for the same purpose.
557  // gEDA also features a pin/pad "name", which is an arbitrary string
558  // and set to the pin name of the netlist on instantiation. Many gEDA
559  // bare footprints use identical strings for name and number, so this
560  // can be a bit confusing.
561  pad->SetNumber( parameters[paramCnt-3] );
562 
563  int x1 = parseInt( parameters[2], conv_unit );
564  int x2 = parseInt( parameters[4], conv_unit );
565  int y1 = parseInt( parameters[3], conv_unit );
566  int y2 = parseInt( parameters[5], conv_unit );
567  int width = parseInt( parameters[6], conv_unit );
568  wxPoint delta( x2 - x1, y2 - y1 );
569  double angle = atan2( (double)delta.y, (double)delta.x );
570 
571  // Get the pad clearance and the solder mask clearance.
572  if( paramCnt == 13 )
573  {
574  int clearance = parseInt( parameters[7], conv_unit );
575  // One of gEDA's oddities is that clearance between pad and polygon
576  // is given as the gap on both sides of the pad together, so for
577  // KiCad it has to halfed.
578  pad->SetLocalClearance( clearance / 2 );
579 
580  // In GEDA, the mask value is the size of the hole in this
581  // solder mask. In Pcbnew, it is a margin, therefore the distance
582  // between the copper and the mask
583  int maskMargin = parseInt( parameters[8], conv_unit );
584  maskMargin = ( maskMargin - width ) / 2;
585  pad->SetLocalSolderMaskMargin( maskMargin );
586  }
587 
588  // Negate angle (due to Y reversed axis) and convert it to internal units
589  angle = - RAD2DECIDEG( angle );
590  pad->SetOrientation( KiROUND( angle ) );
591 
592  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
593 
594  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width, width ) );
595 
596  // Set the relative position before adjusting the absolute position
597  pad->SetPos0( padPos );
598  padPos += footprint->GetPosition();
599  pad->SetPosition( padPos );
600 
601  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
602  {
603  if( pad->GetSize().x == pad->GetSize().y )
604  pad->SetShape( PAD_SHAPE::CIRCLE );
605  else
606  pad->SetShape( PAD_SHAPE::OVAL );
607  }
608 
609  footprint->Add( pad );
610  continue;
611  }
612 
613  // Parse a Pin with through hole with format:
614  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
615  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
616  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
617  // Pin (aX aY Thickness Drill "Name" NFlags)
618  // Pin (aX aY Thickness "Name" NFlags)
619  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
620  {
621  if( paramCnt < 8 || paramCnt > 12 )
622  {
623  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
624  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
625  aLineReader->LineNumber(), 0 );
626  }
627 
628  PAD* pad = new PAD( footprint.get() );
629 
630  pad->SetShape( PAD_SHAPE::CIRCLE );
631 
632  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
633 
634  pad->SetLayerSet( pad_set );
635 
636  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
637  pad->SetShape( PAD_SHAPE::RECT );
638 
639  // Set the pad name:
640  // Pcbnew pad name is used for electrical connection calculations.
641  // Accordingly it should be mapped to gEDA's pin/pad number,
642  // which is used for the same purpose.
643  pad->SetNumber( parameters[paramCnt-3] );
644 
645  wxPoint padPos( parseInt( parameters[2], conv_unit ),
646  parseInt( parameters[3], conv_unit ) );
647 
648  int padSize = parseInt( parameters[4], conv_unit );
649 
650  pad->SetSize( wxSize( padSize, padSize ) );
651 
652  int drillSize = 0;
653 
654  // Get the pad clearance, solder mask clearance, and drill size.
655  if( paramCnt == 12 )
656  {
657  int clearance = parseInt( parameters[5], conv_unit );
658  // One of gEDA's oddities is that clearance between pad and polygon
659  // is given as the gap on both sides of the pad together, so for
660  // KiCad it has to halfed.
661  pad->SetLocalClearance( clearance / 2 );
662 
663  // In GEDA, the mask value is the size of the hole in this
664  // solder mask. In Pcbnew, it is a margin, therefore the distance
665  // between the copper and the mask
666  int maskMargin = parseInt( parameters[6], conv_unit );
667  maskMargin = ( maskMargin - padSize ) / 2;
668  pad->SetLocalSolderMaskMargin( maskMargin );
669 
670  drillSize = parseInt( parameters[7], conv_unit );
671  }
672  else
673  {
674  drillSize = parseInt( parameters[5], conv_unit );
675  }
676 
677  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
678 
679  // Set the relative position before adjusting the absolute position
680  pad->SetPos0( padPos );
681  padPos += footprint->GetPosition();
682  pad->SetPosition( padPos );
683 
684  if( pad->GetShape() == PAD_SHAPE::CIRCLE && pad->GetSize().x != pad->GetSize().y )
685  pad->SetShape( PAD_SHAPE::OVAL );
686 
687  footprint->Add( pad );
688  continue;
689  }
690  }
691 
692  return footprint.release();
693 }
694 
695 
696 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
697 {
698  char key;
699  wxString tmp;
700  char* line = aLineReader->Line();
701 
702  // Last line already ready in main parser loop.
703  while( *line != 0 )
704  {
705  key = *line;
706  line++;
707 
708  switch( key )
709  {
710  case '[':
711  case '(':
712  if( !tmp.IsEmpty() )
713  {
714  aParameterList.Add( tmp );
715  tmp.Clear();
716  }
717 
718  tmp.Append( key );
719  aParameterList.Add( tmp );
720  tmp.Clear();
721 
722  // Opening delimiter "(" after Element statement. Any other occurrence is part
723  // of a keyword definition.
724  if( aParameterList.GetCount() == 1 )
725  {
726  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
727  return;
728  }
729 
730  break;
731 
732  case ']':
733  case ')':
734  if( !tmp.IsEmpty() )
735  {
736  aParameterList.Add( tmp );
737  tmp.Clear();
738  }
739 
740  tmp.Append( key );
741  aParameterList.Add( tmp );
742  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
743  return;
744 
745  case '\n':
746  case '\r':
747  // Element descriptions can span multiple lines.
748  line = aLineReader->ReadLine();
750 
751  case '\t':
752  case ' ':
753  if( !tmp.IsEmpty() )
754  {
755  aParameterList.Add( tmp );
756  tmp.Clear();
757  }
758 
759  break;
760 
761  case '"':
762  // Handle empty quotes.
763  if( *line == '"' )
764  {
765  line++;
766  tmp.Clear();
767  aParameterList.Add( wxEmptyString );
768  break;
769  }
770 
771  while( *line != 0 )
772  {
773  key = *line;
774  line++;
775 
776  if( key == '"' )
777  {
778  aParameterList.Add( tmp );
779  tmp.Clear();
780  break;
781  }
782  else
783  {
784  tmp.Append( key );
785  }
786  }
787 
788  break;
789 
790  case '#':
791  line = aLineReader->ReadLine();
792  break;
793 
794  default:
795  tmp.Append( key );
796  break;
797  }
798  }
799 }
800 
801 
802 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
803 {
804  wxString number;
805 
806  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
807  {
808  long lflags;
809 
810  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
811  return true;
812  }
813  else if( aFlag.Contains( aName ) )
814  {
815  return true;
816  }
817 
818  return false;
819 }
820 
821 
823  m_cache( nullptr ),
824  m_ctl( 0 )
825 {
826  m_reader = nullptr;
827  init( nullptr );
828 }
829 
830 
831 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
832  m_cache( nullptr ),
833  m_ctl( aControlFlags )
834 {
835  m_reader = nullptr;
836  init( nullptr );
837 }
838 
839 
841 {
842  delete m_cache;
843 }
844 
845 
846 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
847 {
848  m_props = aProperties;
849 }
850 
851 
852 void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
853 {
854  if( !m_cache || ( checkModified && m_cache->IsModified() ) )
855  {
856  // a spectacular episode in memory management:
857  delete m_cache;
858  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
859  m_cache->Load();
860  }
861 }
862 
863 
864 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
865  bool aBestEfforts, const PROPERTIES* aProperties )
866 {
867  LOCALE_IO toggle; // toggles on, then off, the C locale.
868  wxDir dir( aLibraryPath );
869  wxString errorMsg;
870 
871  if( !dir.IsOpened() )
872  {
873  if( aBestEfforts )
874  return;
875  else
876  {
877  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' not found." ),
878  aLibraryPath ) );
879  }
880  }
881 
882  init( aProperties );
883 
884  try
885  {
886  validateCache( aLibraryPath );
887  }
888  catch( const IO_ERROR& ioe )
889  {
890  errorMsg = ioe.What();
891  }
892 
893  // Some of the files may have been parsed correctly so we want to add the valid files to
894  // the library.
895 
896  for( const auto& footprint : m_cache->GetFootprints() )
897  aFootprintNames.Add( FROM_UTF8( footprint.first.c_str() ) );
898 
899  if( !errorMsg.IsEmpty() && !aBestEfforts )
900  THROW_IO_ERROR( errorMsg );
901 }
902 
903 
904 const FOOTPRINT* GPCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
905  const wxString& aFootprintName,
906  const PROPERTIES* aProperties,
907  bool checkModified )
908 {
909  LOCALE_IO toggle; // toggles on, then off, the C locale.
910 
911  init( aProperties );
912 
913  validateCache( aLibraryPath, checkModified );
914 
915  const FOOTPRINT_MAP& mods = m_cache->GetFootprints();
916 
917  FOOTPRINT_MAP::const_iterator it = mods.find( TO_UTF8( aFootprintName ) );
918 
919  if( it == mods.end() )
920  return nullptr;
921 
922  return it->second->GetFootprint();
923 }
924 
925 
926 const FOOTPRINT* GPCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
927  const wxString& aFootprintName,
928  const PROPERTIES* aProperties )
929 {
930  return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
931 }
932 
933 
934 FOOTPRINT* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
935  const wxString& aFootprintName,
936  bool aKeepUUID,
937  const PROPERTIES* aProperties )
938 {
939  const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
940 
941  if( footprint )
942  {
943  FOOTPRINT* copy = (FOOTPRINT*) footprint->Duplicate();
944  copy->SetParent( nullptr );
945  return copy;
946  }
947 
948  return nullptr;
949 }
950 
951 
952 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
953  const PROPERTIES* aProperties )
954 {
955  LOCALE_IO toggle; // toggles on, then off, the C locale.
956 
957  init( aProperties );
958 
959  validateCache( aLibraryPath );
960 
961  if( !m_cache->IsWritable() )
962  {
963  THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only." ),
964  aLibraryPath.GetData() ) );
965  }
966 
967  m_cache->Remove( aFootprintName );
968 }
969 
970 
971 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
972 {
973  wxFileName fn;
974  fn.SetPath( aLibraryPath );
975 
976  // Return if there is no library path to delete.
977  if( !fn.DirExists() )
978  return false;
979 
980  if( !fn.IsDirWritable() )
981  {
982  THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
983  aLibraryPath.GetData() ) );
984  }
985 
986  wxDir dir( aLibraryPath );
987 
988  if( dir.HasSubDirs() )
989  {
990  THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected sub-folders." ),
991  aLibraryPath.GetData() ) );
992  }
993 
994  // All the footprint files must be deleted before the directory can be deleted.
995  if( dir.HasFiles() )
996  {
997  unsigned i;
998  wxFileName tmp;
999  wxArrayString files;
1000 
1001  wxDir::GetAllFiles( aLibraryPath, &files );
1002 
1003  for( i = 0; i < files.GetCount(); i++ )
1004  {
1005  tmp = files[i];
1006 
1007  if( tmp.GetExt() != KiCadFootprintFileExtension )
1008  {
1009  THROW_IO_ERROR( wxString::Format( _( "Unexpected file '%s' found in library '%s'." ),
1010  files[i].GetData(),
1011  aLibraryPath.GetData() ) );
1012  }
1013  }
1014 
1015  for( i = 0; i < files.GetCount(); i++ )
1016  {
1017  wxRemoveFile( files[i] );
1018  }
1019  }
1020 
1021  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1022  aLibraryPath.GetData() );
1023 
1024  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1025  // we don't want that. we want bare metal portability with no UI here.
1026  if( !wxRmdir( aLibraryPath ) )
1027  {
1028  THROW_IO_ERROR( wxString::Format( _( "Footprint library '%s' cannot be deleted." ),
1029  aLibraryPath.GetData() ) );
1030  }
1031 
1032  // For some reason removing a directory in Windows is not immediately updated. This delay
1033  // prevents an error when attempting to immediately recreate the same directory when over
1034  // writing an existing library.
1035 #ifdef __WINDOWS__
1036  wxMilliSleep( 250L );
1037 #endif
1038 
1039  if( m_cache && m_cache->GetPath() == aLibraryPath )
1040  {
1041  delete m_cache;
1042  m_cache = nullptr;
1043  }
1044 
1045  return true;
1046 }
1047 
1048 
1049 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1050 {
1051  return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1052 }
1053 
1054 
1055 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1056 {
1057  LOCALE_IO toggle;
1058 
1059  init( nullptr );
1060 
1061  validateCache( aLibraryPath );
1062 
1063  return m_cache->IsWritable();
1064 }
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:750
bool IsWritable() const
FOOTPRINT * GetFootprint() const
Arcs (with rounded ends)
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
void SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
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:192
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
const std::string KiCadFootprintFileExtension
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
usual segment : line with rounded ends
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:502
#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
void SetAngle(double aAngle, bool aUpdateEnd=true) override
Sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: fp_shape.cpp:142
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:516
#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:1662
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:82
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:70
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).
const wxPoint & GetEnd0() const
Definition: fp_shape.h:115
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
#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.
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.