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-2020 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 <locale_io.h>
37 #include <pcb_text.h>
38 #include <pcb_shape.h>
39 #include <fp_shape.h>
41 #include <wx_filename.h>
42 
43 #include <wx/dir.h>
44 #include <wx/filename.h>
45 #include <wx/wfstream.h>
46 #include <boost/ptr_container/ptr_map.hpp>
47 #include <memory.h>
48 
49 
50 static inline long parseInt( const wxString& aValue, double aScalar )
51 {
52  double value = std::numeric_limits<double>::max();
53 
54  /*
55  * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
56  * Unit-less values are still centimils (100000 units per inch), like with
57  * the previous format.
58  *
59  * Distinction between the even older format (mils, 1000 units per inch)
60  * and the pre-2011 format is done in ::parseFOOTPRINT already; the
61  * distinction is by wether an object definition opens with '(' or '['.
62  * All values with explicite unit open with a '[' so there's no need to
63  * consider this distinction when parsing them.
64  *
65  * The solution here is to watch for a unit and, if present, convert the
66  * value to centimils. All unit-less values are read unaltered. This way
67  * the code below can contine to consider all read values to be in mils or
68  * centimils. It also matches the strategy gEDA/pcb uses for backwards
69  * compatibility with its own layouts.
70  *
71  * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
72  * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
73  * have to test for all 11 units gEDA/pcb allows in user dialogs.
74  */
75  if( aValue.EndsWith( wxT( "mm" ) ) )
76  {
77  aScalar *= 100000.0 / 25.4;
78  }
79  else if( aValue.EndsWith( wxT( "mil" ) ) )
80  {
81  aScalar *= 100.;
82  }
83 
84  // This conversion reports failure on strings as simple as "1000", still
85  // it returns the right result in &value. Thus, ignore the return value.
86  aValue.ToCDouble(&value);
87  if( value == std::numeric_limits<double>::max() ) // conversion really failed
88  {
89  THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
90  aValue.GetData() ) );
91  return 0;
92  }
93 
94  return KiROUND( value * aScalar );
95 }
96 
97 
108 {
110  std::unique_ptr<FOOTPRINT> m_footprint;
111 
112 public:
113  GPCB_FPL_CACHE_ITEM( FOOTPRINT* aFootprint, const WX_FILENAME& aFileName );
114 
115  WX_FILENAME GetFileName() const { return m_filename; }
116  FOOTPRINT* GetFootprint() const { return m_footprint.get(); }
117 };
118 
119 
121  m_filename( aFileName ),
122  m_footprint( aFootprint )
123 {
124 }
125 
126 
127 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > FOOTPRINT_MAP;
128 
129 
131 {
133  wxFileName m_lib_path;
135 
137  long long m_cache_timestamp;
139 
141  FOOTPRINT* parseFOOTPRINT( LINE_READER* aLineReader );
142 
153  bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
154 
171  void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
172 
173 public:
174  GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
175 
176  wxString GetPath() const { return m_lib_path.GetPath(); }
177  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
179 
180  // Most all functions in this class throw IO_ERROR exceptions. There are no
181  // error codes nor user interface calls from here, nor in any PLUGIN.
182  // Catch these exceptions higher up please.
183 
185 
186  void Load();
187 
188  void Remove( const wxString& aFootprintName );
189 
196  static long long GetTimestamp( const wxString& aLibPath );
197 
202  bool IsModified();
203 };
204 
205 
206 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
207 {
208  m_owner = aOwner;
209  m_lib_path.SetPath( aLibraryPath );
210  m_cache_timestamp = 0;
211  m_cache_dirty = true;
212 }
213 
214 
216 {
217  m_cache_dirty = false;
218  m_cache_timestamp = 0;
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 path \"%s\" does not exist" ),
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 += "\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 
273 void 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\" to delete" ),
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 
301 long 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*IU_PER_MILS )
312  #define OLD_GPCB_UNIT_CONV 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*IU_PER_MILS )
316 
317  int paramCnt;
318  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
319  wxPoint textPos;
320  wxString msg;
321  wxArrayString parameters;
322  std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( nullptr );
323 
324 
325  if( aLineReader->ReadLine() == NULL )
326  {
327  msg = aLineReader->GetSource() + ": empty file";
328  THROW_IO_ERROR( msg );
329  }
330 
331  parameters.Clear();
332  parseParameters( parameters, aLineReader );
333  paramCnt = parameters.GetCount();
334 
335  /* From the Geda PCB documentation, valid Element definitions:
336  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
337  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
338  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
339  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
340  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
341  */
342 
343  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
344  {
345  msg.Printf( _( "unknown token \"%s\"" ), parameters[0] );
346  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
347  aLineReader->LineNumber(), 0 );
348  }
349 
350  if( paramCnt < 10 || paramCnt > 14 )
351  {
352  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
353  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
354  aLineReader->LineNumber(), 0 );
355  }
356 
357  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
358  if( parameters[1] == wxT( "(" ) )
359  conv_unit = OLD_GPCB_UNIT_CONV;
360 
361  if( paramCnt > 10 )
362  {
363  footprint->SetDescription( parameters[3] );
364  footprint->SetReference( parameters[4] );
365  }
366  else
367  {
368  footprint->SetDescription( parameters[2] );
369  footprint->SetReference( parameters[3] );
370  }
371 
372  // Read value
373  if( paramCnt > 10 )
374  footprint->SetValue( parameters[5] );
375  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
376  // often empty in bare footprints.
377  if( footprint->Value().GetText().IsEmpty() )
378  footprint->Value().SetText( wxT( "Val**" ) );
379 
380 
381  if( paramCnt == 14 )
382  {
383  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
384  parseInt( parameters[9], conv_unit ) );
385  }
386  else
387  {
388  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
389  parseInt( parameters[7], conv_unit ) );
390  }
391 
392  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
393  footprint->Reference().SetTextAngle(( orientation % 2) ? 900 : 0 );
394 
395  // Calculate size: default height is 40 mils, width 30 mil.
396  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
397  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
398  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
399  int twsize = thsize * 30 / 40;
400  int thickness = thsize / 8;
401 
402  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
403  // Compensate for this by shifting the insertion point instead of the
404  // alignment, because alignment isn't changeable in the GUI.
405  textPos.x = textPos.x + twsize * footprint->GetReference().Len() / 2;
406  textPos.y += thsize / 2;
407 
408  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
409  // high/right. Compensate for similar appearance.
410  textPos.x -= thsize / 10;
411  textPos.y += thsize / 2;
412 
413  footprint->Reference().SetTextPos( textPos );
414  footprint->Reference().SetPos0( textPos );
415  footprint->Reference().SetTextSize( wxSize( twsize, thsize ) );
416  footprint->Reference().SetTextThickness( thickness );
417 
418  // gEDA/pcb shows only one of value/reference/description at a time. Which
419  // one is selectable by a global menu setting. pcbnew needs reference as
420  // well as value visible, so place the value right below the reference.
421  footprint->Value().SetTextAngle( footprint->Reference().GetTextAngle() );
422  footprint->Value().SetTextSize( footprint->Reference().GetTextSize() );
423  footprint->Value().SetTextThickness( footprint->Reference().GetTextThickness() );
424  textPos.y += thsize * 13 / 10; // 130% line height
425  footprint->Value().SetTextPos( textPos );
426  footprint->Value().SetPos0( 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(
451  traceGedaPcbPlugin, wxT( "%s parameter count = %d." ), 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  FP_SHAPE* shape = new FP_SHAPE( footprint.get() );
464  shape->SetLayer( F_SilkS );
465  shape->SetShape( S_SEGMENT );
466  shape->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
467  parseInt( parameters[3], conv_unit ) ) );
468  shape->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
469  parseInt( parameters[5], conv_unit ) ) );
470  shape->SetWidth( parseInt( parameters[6], conv_unit ) );
471  shape->SetDrawCoord();
472  footprint->Add( shape );
473  continue;
474  }
475 
476  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
477  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
478  {
479  if( paramCnt != 10 )
480  {
481  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
482  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
483  aLineReader->LineNumber(), 0 );
484  }
485 
486  // Pcbnew does know ellipse so we must have Width = Height
487  FP_SHAPE* shape = new FP_SHAPE( footprint.get() );
488  shape->SetLayer( F_SilkS );
489  shape->SetShape( S_ARC );
490  footprint->Add( shape );
491 
492  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
493  int radius = ( parseInt( parameters[4], conv_unit ) +
494  parseInt( parameters[5], conv_unit ) ) / 2;
495 
496  wxPoint centre( parseInt( parameters[2], conv_unit ),
497  parseInt( parameters[3], conv_unit ) );
498 
499  shape->SetStart0( centre );
500 
501  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
502  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
503 
504  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
505  double sweep_angle = parseInt( parameters[7], -10.0 );
506 
507  // Geda PCB does not support circles.
508  if( sweep_angle == -3600.0 )
509  shape->SetShape( S_CIRCLE );
510 
511  // Angle value is clockwise in gpcb and Pcbnew.
512  shape->SetAngle( sweep_angle );
513  shape->SetEnd0( wxPoint( radius, 0 ) );
514 
515  // Calculate start point coordinate of arc
516  wxPoint arcStart( shape->GetEnd0() );
517  RotatePoint( &arcStart, -start_angle );
518  shape->SetEnd0( centre + arcStart );
519  shape->SetWidth( parseInt( parameters[8], conv_unit ) );
520  shape->SetDrawCoord();
521  continue;
522  }
523 
524  // Parse a Pad with no hole with format:
525  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
526  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
527  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
528  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
529  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
530  {
531  if( paramCnt < 10 || paramCnt > 13 )
532  {
533  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
534  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
535  aLineReader->LineNumber(), 0 );
536  }
537 
538  PAD* pad = new PAD( footprint.get() );
539 
540  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
541  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
542 
543  pad->SetShape( PAD_SHAPE_RECT );
545  pad->SetLayerSet( pad_front );
546 
547  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
548  pad->SetLayerSet( pad_back );
549 
550  // Set the pad name:
551  // Pcbnew pad name is used for electrical connection calculations.
552  // Accordingly it should be mapped to gEDA's pin/pad number,
553  // which is used for the same purpose.
554  // gEDA also features a pin/pad "name", which is an arbitrary string
555  // and set to the pin name of the netlist on instantiation. Many gEDA
556  // bare footprints use identical strings for name and number, so this
557  // can be a bit confusing.
558  pad->SetName( parameters[paramCnt-3] );
559 
560  int x1 = parseInt( parameters[2], conv_unit );
561  int x2 = parseInt( parameters[4], conv_unit );
562  int y1 = parseInt( parameters[3], conv_unit );
563  int y2 = parseInt( parameters[5], conv_unit );
564  int width = parseInt( parameters[6], conv_unit );
565  wxPoint delta( x2 - x1, y2 - y1 );
566  double angle = atan2( (double)delta.y, (double)delta.x );
567 
568  // Get the pad clearance and the solder mask clearance.
569  if( paramCnt == 13 )
570  {
571  int clearance = parseInt( parameters[7], conv_unit );
572  // One of gEDA's oddities is that clearance between pad and polygon
573  // is given as the gap on both sides of the pad together, so for
574  // KiCad it has to halfed.
575  pad->SetLocalClearance( clearance / 2 );
576 
577  // In GEDA, the mask value is the size of the hole in this
578  // solder mask. In Pcbnew, it is a margin, therefore the distance
579  // between the copper and the mask
580  int maskMargin = parseInt( parameters[8], conv_unit );
581  maskMargin = ( maskMargin - width ) / 2;
582  pad->SetLocalSolderMaskMargin( maskMargin );
583  }
584 
585  // Negate angle (due to Y reversed axis) and convert it to internal units
586  angle = - RAD2DECIDEG( angle );
587  pad->SetOrientation( KiROUND( angle ) );
588 
589  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
590 
591  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
592  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->SetName( 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( 0 ),
822  m_ctl( 0 )
823 {
824  m_reader = NULL;
825  init( 0 );
826 }
827 
828 
829 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
830  m_cache( 0 ),
831  m_ctl( aControlFlags )
832 {
833  m_reader = NULL;
834  init( 0 );
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 path \"%s\" does not exist" ),
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 NULL;
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  const PROPERTIES* aProperties )
935 {
936  const FOOTPRINT* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
937 
938  if( footprint )
939  {
940  FOOTPRINT* copy = (FOOTPRINT*) footprint->Duplicate();
941  copy->SetParent( nullptr );
942  return copy;
943  }
944 
945  return nullptr;
946 }
947 
948 
949 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
950  const PROPERTIES* aProperties )
951 {
952  LOCALE_IO toggle; // toggles on, then off, the C locale.
953 
954  init( aProperties );
955 
956  validateCache( aLibraryPath );
957 
958  if( !m_cache->IsWritable() )
959  {
960  THROW_IO_ERROR( wxString::Format( _( "Library \"%s\" is read only" ),
961  aLibraryPath.GetData() ) );
962  }
963 
964  m_cache->Remove( aFootprintName );
965 }
966 
967 
968 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
969 {
970  wxFileName fn;
971  fn.SetPath( aLibraryPath );
972 
973  // Return if there is no library path to delete.
974  if( !fn.DirExists() )
975  return false;
976 
977  if( !fn.IsDirWritable() )
978  {
979  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory \"%s\"" ),
980  aLibraryPath.GetData() ) );
981  }
982 
983  wxDir dir( aLibraryPath );
984 
985  if( dir.HasSubDirs() )
986  {
987  THROW_IO_ERROR( wxString::Format( _( "library directory \"%s\" has unexpected sub-directories" ),
988  aLibraryPath.GetData() ) );
989  }
990 
991  // All the footprint files must be deleted before the directory can be deleted.
992  if( dir.HasFiles() )
993  {
994  unsigned i;
995  wxFileName tmp;
996  wxArrayString files;
997 
998  wxDir::GetAllFiles( aLibraryPath, &files );
999 
1000  for( i = 0; i < files.GetCount(); i++ )
1001  {
1002  tmp = files[i];
1003 
1004  if( tmp.GetExt() != KiCadFootprintFileExtension )
1005  {
1006  THROW_IO_ERROR( wxString::Format( _( "unexpected file \"%s\" was found in library path \"%s\"" ),
1007  files[i].GetData(), aLibraryPath.GetData() ) );
1008  }
1009  }
1010 
1011  for( i = 0; i < files.GetCount(); i++ )
1012  {
1013  wxRemoveFile( files[i] );
1014  }
1015  }
1016 
1017  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1018  aLibraryPath.GetData() );
1019 
1020  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1021  // we don't want that. we want bare metal portability with no UI here.
1022  if( !wxRmdir( aLibraryPath ) )
1023  {
1024  THROW_IO_ERROR( wxString::Format( _( "footprint library \"%s\" cannot be deleted" ),
1025  aLibraryPath.GetData() ) );
1026  }
1027 
1028  // For some reason removing a directory in Windows is not immediately updated. This delay
1029  // prevents an error when attempting to immediately recreate the same directory when over
1030  // writing an existing library.
1031 #ifdef __WINDOWS__
1032  wxMilliSleep( 250L );
1033 #endif
1034 
1035  if( m_cache && m_cache->GetPath() == aLibraryPath )
1036  {
1037  delete m_cache;
1038  m_cache = NULL;
1039  }
1040 
1041  return true;
1042 }
1043 
1044 
1045 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1046 {
1047  return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1048 }
1049 
1050 
1051 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1052 {
1053  LOCALE_IO toggle;
1054 
1055  init( NULL );
1056 
1057  validateCache( aLibraryPath );
1058 
1059  return m_cache->IsWritable();
1060 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:148
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
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)
Function GetTimestamp Generate a timestamp representing all source files in the cache (including the ...
wxString GetPath() const
void SetLocalClearance(int aClearance)
Definition: pad.h:383
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:194
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
const std::string KiCadFootprintFileExtension
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Return true if the library at aLibraryPath is writable.
WX_FILENAME m_filename
The the full file name and path of the footprint to cache.
WX_FILENAME GetFileName() const
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
void SetName(const wxString &aName)
Set the pad name (sometimes called pad number, although it can be an array reference like AA12).
Definition: pad.h:132
double RAD2DECIDEG(double rad)
Definition: trigo.h:236
usual segment : line with rounded ends
Definition: board_item.h:50
FOOTPRINT_MAP & GetFootprints()
GPCB_FPL_CACHE_ITEM is helper class for creating a footprint library cache.
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Function parseParameters extracts parameters and tokens from aLineReader and adds them to aParameterL...
Arcs (with rounded ends)
Definition: board_item.h:52
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
PAD_SHAPE_T GetShape() const
Definition: pad.h:169
void SetSize(const wxSize &aSize)
Definition: pad.h:231
void Remove(const wxString &aFootprintName)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
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:33
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.
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:165
GPCB_FPL_CACHE_ITEM(FOOTPRINT *aFootprint, const WX_FILENAME &aFileName)
#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:100
void SetLocalSolderMaskMargin(int aMargin)
Definition: pad.h:379
A LINE_READER that reads from an open file.
Definition: richio.h:172
LSET is a set of PCB_LAYER_IDs.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:164
#define NULL
void SetPos0(const wxPoint &aPos)
Definition: pad.h:225
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
void SetShape(PCB_SHAPE_TYPE_T aShape)
Definition: pcb_shape.h:129
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:29
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 SetDrillSize(const wxSize &aSize)
Definition: pad.h:241
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:141
const wxSize & GetSize() const
Definition: pad.h:232
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:99
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:549
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 a of this BOARD_ITEM.
Definition: footprint.cpp:1587
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
Delete aFootprintName from the library at aLibraryPath.
void SetShape(PAD_SHAPE_T aShape)
Set the new shape of this pad.
Definition: pad.h:160
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
void SetLayerSet(LSET aLayers) override
Definition: pad.h:359
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:59
#define TEXT_DEFAULT_SIZE
GPCB_FPL_CACHE(GPCB_PLUGIN *aOwner, const wxString &aLibraryPath)
#define _(s)
Definition: 3d_actions.cpp:33
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
std::map< wxString, FOOTPRINT * > FOOTPRINT_MAP
Definition: eagle_parser.h:52
std::unique_ptr< FOOTPRINT > m_footprint
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:171
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:117
#define IU_PER_MILS
Definition: plotter.cpp:137
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:68
const std::string GedaPcbFootprintLibFileExtension
ring
Definition: board_item.h:53
bool IsModified()
Function IsModified Return true if the cache is not up-to-date.
friend class GPCB_FPL_CACHE
Definition: gpcb_plugin.h:95
LINE_READER * m_reader
no ownership here.
Definition: gpcb_plugin.h:102
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Function testFlags tests aFlag for aMask or aName.
Definition: pad.h:60
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PL...
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:50
void SetOrientation(double aAngle)
Set the rotation angle of the pad.
Definition: pcbnew/pad.cpp:579
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.
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: pcbnew/pad.cpp:560