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 
30 #include <fctsys.h>
31 #include <common.h>
32 #include <macros.h>
33 #include <trigo.h>
35 #include <filter_reader.h>
36 #include <trace_helpers.h>
37 #include <math/util.h> // for KiROUND
38 
39 #include <class_board.h>
40 #include <class_module.h>
41 #include <class_pcb_text.h>
42 #include <class_drawsegment.h>
43 #include <class_edge_mod.h>
44 #include <gpcb_plugin.h>
45 
46 #include <wx/dir.h>
47 #include <wx/filename.h>
48 #include <wx/wfstream.h>
49 #include <boost/ptr_container/ptr_map.hpp>
50 #include <memory.h>
51 
52 
53 static inline long parseInt( const wxString& aValue, double aScalar )
54 {
55  double value = std::numeric_limits<double>::max();
56 
57  /*
58  * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
59  * Unit-less values are still centimils (100000 units per inch), like with
60  * the previous format.
61  *
62  * Distinction between the even older format (mils, 1000 units per inch)
63  * and the pre-2011 format is done in ::parseMODULE already; the
64  * distinction is by wether an object definition opens with '(' or '['.
65  * All values with explicite unit open with a '[' so there's no need to
66  * consider this distinction when parsing them.
67  *
68  * The solution here is to watch for a unit and, if present, convert the
69  * value to centimils. All unit-less values are read unaltered. This way
70  * the code below can contine to consider all read values to be in mils or
71  * centimils. It also matches the strategy gEDA/pcb uses for backwards
72  * compatibility with its own layouts.
73  *
74  * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
75  * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
76  * have to test for all 11 units gEDA/pcb allows in user dialogs.
77  */
78  if( aValue.EndsWith( wxT( "mm" ) ) )
79  {
80  aScalar *= 100000.0 / 25.4;
81  }
82  else if( aValue.EndsWith( wxT( "mil" ) ) )
83  {
84  aScalar *= 100.;
85  }
86 
87  // This conversion reports failure on strings as simple as "1000", still
88  // it returns the right result in &value. Thus, ignore the return value.
89  aValue.ToCDouble(&value);
90  if( value == std::numeric_limits<double>::max() ) // conversion really failed
91  {
92  THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
93  aValue.GetData() ) );
94  return 0;
95  }
96 
97  return KiROUND( value * aScalar );
98 }
99 
100 
111 {
113  std::unique_ptr<MODULE> m_module;
114 
115 public:
116  GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName );
117 
118  WX_FILENAME GetFileName() const { return m_filename; }
119  MODULE* GetModule() const { return m_module.get(); }
120 };
121 
122 
124  m_filename( aFileName ),
125  m_module( aModule )
126 {
127 }
128 
129 
130 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
131 typedef MODULE_MAP::iterator MODULE_ITER;
132 typedef MODULE_MAP::const_iterator MODULE_CITER;
133 
134 
136 {
138  wxFileName m_lib_path;
140 
141  bool m_cache_dirty; // Stored separately because it's expensive to check
142  // m_cache_timestamp against all the files.
143  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
144  // files.
145 
146  MODULE* parseMODULE( LINE_READER* aLineReader );
147 
158  bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
159 
176  void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
177 
178 public:
179  GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
180 
181  wxString GetPath() const { return m_lib_path.GetPath(); }
182  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
184 
185  // Most all functions in this class throw IO_ERROR exceptions. There are no
186  // error codes nor user interface calls from here, nor in any PLUGIN.
187  // Catch these exceptions higher up please.
188 
190 
191  void Load();
192 
193  void Remove( const wxString& aFootprintName );
194 
201  static long long GetTimestamp( const wxString& aLibPath );
202 
207  bool IsModified();
208 };
209 
210 
211 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
212 {
213  m_owner = aOwner;
214  m_lib_path.SetPath( aLibraryPath );
215  m_cache_timestamp = 0;
216  m_cache_dirty = true;
217 }
218 
219 
221 {
222  m_cache_dirty = false;
223  m_cache_timestamp = 0;
224 
225  // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
226  // and the footprints are the .fp files inside this folder.
227 
228  wxDir dir( m_lib_path.GetPath() );
229 
230  if( !dir.IsOpened() )
231  {
232  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
233  m_lib_path.GetPath().GetData() ) );
234  }
235 
236  wxString fullName;
237  wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
238 
239  // wxFileName construction is egregiously slow. Construct it once and just swap out
240  // the filename thereafter.
241  WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
242 
243  if( !dir.GetFirst( &fullName, fileSpec ) )
244  return;
245 
246  wxString cacheErrorMsg;
247 
248  do
249  {
250  fn.SetFullName( fullName );
251 
252  // Queue I/O errors so only files that fail to parse don't get loaded.
253  try
254  {
255  // reader now owns fp, will close on exception or return
256  FILE_LINE_READER reader( fn.GetFullPath() );
257 
258  std::string name = TO_UTF8( fn.GetName() );
259  MODULE* footprint = parseMODULE( &reader );
260 
261  // The footprint name is the file name without the extension.
262  footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
263  m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn ) );
264  }
265  catch( const IO_ERROR& ioe )
266  {
267  if( !cacheErrorMsg.IsEmpty() )
268  cacheErrorMsg += "\n\n";
269 
270  cacheErrorMsg += ioe.What();
271  }
272  } while( dir.GetNext( &fullName ) );
273 
274  if( !cacheErrorMsg.IsEmpty() )
275  THROW_IO_ERROR( cacheErrorMsg );
276 }
277 
278 
279 void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
280 {
281  std::string footprintName = TO_UTF8( aFootprintName );
282 
283  MODULE_CITER it = m_modules.find( footprintName );
284 
285  if( it == m_modules.end() )
286  {
287  THROW_IO_ERROR( wxString::Format( _( "library \"%s\" has no footprint \"%s\" to delete" ),
288  m_lib_path.GetPath().GetData(),
289  aFootprintName.GetData() ) );
290  }
291 
292  // Remove the module from the cache and delete the module file from the library.
293  wxString fullPath = it->second->GetFileName().GetFullPath();
294  m_modules.erase( footprintName );
295  wxRemoveFile( fullPath );
296 }
297 
298 
300 {
302 
303  return m_cache_dirty;
304 }
305 
306 
307 long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
308 {
309  wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
310 
311  return TimestampDir( aLibPath, fileSpec );
312 }
313 
314 
316 {
317  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
318  #define OLD_GPCB_UNIT_CONV IU_PER_MILS
319 
320  // Old version unit = 1 mil, so conv_unit is 10 or 0.1
321  #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
322 
323  int paramCnt;
324  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
325  wxPoint textPos;
326  wxString msg;
327  wxArrayString parameters;
328  std::unique_ptr<MODULE> module( new MODULE( NULL ) );
329 
330 
331  if( aLineReader->ReadLine() == NULL )
332  {
333  msg = aLineReader->GetSource() + ": empty file";
334  THROW_IO_ERROR( msg );
335  }
336 
337  parameters.Clear();
338  parseParameters( parameters, aLineReader );
339  paramCnt = parameters.GetCount();
340 
341  /* From the Geda PCB documentation, valid Element definitions:
342  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
343  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
344  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
345  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
346  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
347  */
348 
349  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
350  {
351  msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
352  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
353  aLineReader->LineNumber(), 0 );
354  }
355 
356  if( paramCnt < 10 || paramCnt > 14 )
357  {
358  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
359  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
360  aLineReader->LineNumber(), 0 );
361  }
362 
363  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
364  if( parameters[1] == wxT( "(" ) )
365  conv_unit = OLD_GPCB_UNIT_CONV;
366 
367  if( paramCnt > 10 )
368  {
369  module->SetDescription( parameters[3] );
370  module->SetReference( parameters[4] );
371  }
372  else
373  {
374  module->SetDescription( parameters[2] );
375  module->SetReference( parameters[3] );
376  }
377 
378  // Read value
379  if( paramCnt > 10 )
380  module->SetValue( parameters[5] );
381  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
382  // often empty in bare footprints.
383  if( module->Value().GetText().IsEmpty() )
384  module->Value().SetText( wxT( "Val**" ) );
385 
386 
387  if( paramCnt == 14 )
388  {
389  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
390  parseInt( parameters[9], conv_unit ) );
391  }
392  else
393  {
394  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
395  parseInt( parameters[7], conv_unit ) );
396  }
397 
398  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
399  module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
400 
401  // Calculate size: default height is 40 mils, width 30 mil.
402  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
403  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
404  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
405  int twsize = thsize * 30 / 40;
406  int thickness = thsize / 8;
407 
408  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
409  // Compensate for this by shifting the insertion point instead of the
410  // alignment, because alignment isn't changeable in the GUI.
411  textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
412  textPos.y += thsize / 2;
413 
414  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
415  // high/right. Compensate for similar appearance.
416  textPos.x -= thsize / 10;
417  textPos.y += thsize / 2;
418 
419  module->Reference().SetTextPos( textPos );
420  module->Reference().SetPos0( textPos );
421  module->Reference().SetTextSize( wxSize( twsize, thsize ) );
422  module->Reference().SetTextThickness( thickness );
423 
424  // gEDA/pcb shows only one of value/reference/description at a time. Which
425  // one is selectable by a global menu setting. pcbnew needs reference as
426  // well as value visible, so place the value right below the reference.
427  module->Value().SetTextAngle( module->Reference().GetTextAngle() );
428  module->Value().SetTextSize( module->Reference().GetTextSize() );
429  module->Value().SetTextThickness( module->Reference().GetTextThickness());
430  textPos.y += thsize * 13 / 10; // 130% line height
431  module->Value().SetTextPos( textPos );
432  module->Value().SetPos0( textPos );
433 
434  while( aLineReader->ReadLine() )
435  {
436  parameters.Clear();
437  parseParameters( parameters, aLineReader );
438 
439  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
440  continue;
441 
442  if( parameters[0] == wxT( ")" ) )
443  break;
444 
445  paramCnt = parameters.GetCount();
446 
447  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
448  if( paramCnt > 3 )
449  {
450  if( parameters[1] == wxT( "(" ) )
451  conv_unit = OLD_GPCB_UNIT_CONV;
452  else
453  conv_unit = NEW_GPCB_UNIT_CONV;
454  }
455 
456  wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
457  GetChars( parameters[0] ), paramCnt );
458 
459  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
460  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
461  {
462  if( paramCnt != 8 )
463  {
464  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
465  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
466  aLineReader->LineNumber(), 0 );
467  }
468 
469  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
470  drawSeg->SetLayer( F_SilkS );
471  drawSeg->SetShape( S_SEGMENT );
472  drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
473  parseInt( parameters[3], conv_unit ) ) );
474  drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
475  parseInt( parameters[5], conv_unit ) ) );
476  drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
477  drawSeg->SetDrawCoord();
478  module->Add( drawSeg );
479  continue;
480  }
481 
482  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
483  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
484  {
485  if( paramCnt != 10 )
486  {
487  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
488  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
489  aLineReader->LineNumber(), 0 );
490  }
491 
492  // Pcbnew does know ellipse so we must have Width = Height
493  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
494  drawSeg->SetLayer( F_SilkS );
495  drawSeg->SetShape( S_ARC );
496  module->Add( drawSeg );
497 
498  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
499  int radius = ( parseInt( parameters[4], conv_unit ) +
500  parseInt( parameters[5], conv_unit ) ) / 2;
501 
502  wxPoint centre( parseInt( parameters[2], conv_unit ),
503  parseInt( parameters[3], conv_unit ) );
504 
505  drawSeg->SetStart0( centre );
506 
507  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
508  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
509 
510  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
511  double sweep_angle = parseInt( parameters[7], -10.0 );
512 
513  // Geda PCB does not support circles.
514  if( sweep_angle == -3600.0 )
515  drawSeg->SetShape( S_CIRCLE );
516 
517  // Angle value is clockwise in gpcb and Pcbnew.
518  drawSeg->SetAngle( sweep_angle );
519  drawSeg->SetEnd0( wxPoint( radius, 0 ) );
520 
521  // Calculate start point coordinate of arc
522  wxPoint arcStart( drawSeg->GetEnd0() );
523  RotatePoint( &arcStart, -start_angle );
524  drawSeg->SetEnd0( centre + arcStart );
525  drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
526  drawSeg->SetDrawCoord();
527  continue;
528  }
529 
530  // Parse a Pad with no hole with format:
531  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
532  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
533  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
534  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
535  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
536  {
537  if( paramCnt < 10 || paramCnt > 13 )
538  {
539  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
540  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
541  aLineReader->LineNumber(), 0 );
542  }
543 
544  D_PAD* pad = new D_PAD( module.get() );
545 
546  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
547  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
548 
549  pad->SetShape( PAD_SHAPE_RECT );
551  pad->SetLayerSet( pad_front );
552 
553  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
554  pad->SetLayerSet( pad_back );
555 
556  // Set the pad name:
557  // Pcbnew pad name is used for electrical connection calculations.
558  // Accordingly it should be mapped to gEDA's pin/pad number,
559  // which is used for the same purpose.
560  // gEDA also features a pin/pad "name", which is an arbitrary string
561  // and set to the pin name of the netlist on instantiation. Many gEDA
562  // bare footprints use identical strings for name and number, so this
563  // can be a bit confusing.
564  pad->SetName( parameters[paramCnt-3] );
565 
566  int x1 = parseInt( parameters[2], conv_unit );
567  int x2 = parseInt( parameters[4], conv_unit );
568  int y1 = parseInt( parameters[3], conv_unit );
569  int y2 = parseInt( parameters[5], conv_unit );
570  int width = parseInt( parameters[6], conv_unit );
571  wxPoint delta( x2 - x1, y2 - y1 );
572  double angle = atan2( (double)delta.y, (double)delta.x );
573 
574  // Get the pad clearance and the solder mask clearance.
575  if( paramCnt == 13 )
576  {
577  int clearance = parseInt( parameters[7], conv_unit );
578  // One of gEDA's oddities is that clearance between pad and polygon
579  // is given as the gap on both sides of the pad together, so for
580  // KiCad it has to halfed.
581  pad->SetLocalClearance( clearance / 2 );
582 
583  // In GEDA, the mask value is the size of the hole in this
584  // solder mask. In Pcbnew, it is a margin, therefore the distance
585  // between the copper and the mask
586  int maskMargin = parseInt( parameters[8], conv_unit );
587  maskMargin = ( maskMargin - width ) / 2;
588  pad->SetLocalSolderMaskMargin( maskMargin );
589  }
590 
591  // Negate angle (due to Y reversed axis) and convert it to internal units
592  angle = - RAD2DECIDEG( angle );
593  pad->SetOrientation( KiROUND( angle ) );
594 
595  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
596 
597  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
598  width ) );
599 
600  // Set the relative position before adjusting the absolute position
601  pad->SetPos0( padPos );
602  padPos += module->GetPosition();
603  pad->SetPosition( padPos );
604 
605  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
606  {
607  if( pad->GetSize().x == pad->GetSize().y )
608  pad->SetShape( PAD_SHAPE_CIRCLE );
609  else
610  pad->SetShape( PAD_SHAPE_OVAL );
611  }
612 
613  module->Add( pad );
614  continue;
615  }
616 
617  // Parse a Pin with through hole with format:
618  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
619  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
620  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
621  // Pin (aX aY Thickness Drill "Name" NFlags)
622  // Pin (aX aY Thickness "Name" NFlags)
623  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
624  {
625  if( paramCnt < 8 || paramCnt > 12 )
626  {
627  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
628  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
629  aLineReader->LineNumber(), 0 );
630  }
631 
632  D_PAD* pad = new D_PAD( module.get() );
633 
634  pad->SetShape( PAD_SHAPE_CIRCLE );
635 
636  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
637 
638  pad->SetLayerSet( pad_set );
639 
640  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
641  pad->SetShape( PAD_SHAPE_RECT );
642 
643  // Set the pad name:
644  // Pcbnew pad name is used for electrical connection calculations.
645  // Accordingly it should be mapped to gEDA's pin/pad number,
646  // which is used for the same purpose.
647  pad->SetName( parameters[paramCnt-3] );
648 
649  wxPoint padPos( parseInt( parameters[2], conv_unit ),
650  parseInt( parameters[3], conv_unit ) );
651 
652  int padSize = parseInt( parameters[4], conv_unit );
653 
654  pad->SetSize( wxSize( padSize, padSize ) );
655 
656  int drillSize = 0;
657 
658  // Get the pad clearance, solder mask clearance, and drill size.
659  if( paramCnt == 12 )
660  {
661  int clearance = parseInt( parameters[5], conv_unit );
662  // One of gEDA's oddities is that clearance between pad and polygon
663  // is given as the gap on both sides of the pad together, so for
664  // KiCad it has to halfed.
665  pad->SetLocalClearance( clearance / 2 );
666 
667  // In GEDA, the mask value is the size of the hole in this
668  // solder mask. In Pcbnew, it is a margin, therefore the distance
669  // between the copper and the mask
670  int maskMargin = parseInt( parameters[6], conv_unit );
671  maskMargin = ( maskMargin - padSize ) / 2;
672  pad->SetLocalSolderMaskMargin( maskMargin );
673 
674  drillSize = parseInt( parameters[7], conv_unit );
675  }
676  else
677  {
678  drillSize = parseInt( parameters[5], conv_unit );
679  }
680 
681  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
682 
683  // Set the relative position before adjusting the absolute position
684  pad->SetPos0( padPos );
685  padPos += module->GetPosition();
686  pad->SetPosition( padPos );
687 
688  if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
689  pad->SetShape( PAD_SHAPE_OVAL );
690 
691  module->Add( pad );
692  continue;
693  }
694  }
695 
696  // Recalculate the bounding box
697  module->CalculateBoundingBox();
698  return module.release();
699 }
700 
701 
702 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
703 {
704  char key;
705  wxString tmp;
706  char* line = aLineReader->Line();
707 
708  // Last line already ready in main parser loop.
709  while( *line != 0 )
710  {
711  key = *line;
712  line++;
713 
714  switch( key )
715  {
716  case '[':
717  case '(':
718  if( !tmp.IsEmpty() )
719  {
720  aParameterList.Add( tmp );
721  tmp.Clear();
722  }
723 
724  tmp.Append( key );
725  aParameterList.Add( tmp );
726  tmp.Clear();
727 
728  // Opening delimiter "(" after Element statement. Any other occurrence is part
729  // of a keyword definition.
730  if( aParameterList.GetCount() == 1 )
731  {
732  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
733  return;
734  }
735 
736  break;
737 
738  case ']':
739  case ')':
740  if( !tmp.IsEmpty() )
741  {
742  aParameterList.Add( tmp );
743  tmp.Clear();
744  }
745 
746  tmp.Append( key );
747  aParameterList.Add( tmp );
748  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
749  return;
750 
751  case '\n':
752  case '\r':
753  // Element descriptions can span multiple lines.
754  line = aLineReader->ReadLine();
756 
757  case '\t':
758  case ' ':
759  if( !tmp.IsEmpty() )
760  {
761  aParameterList.Add( tmp );
762  tmp.Clear();
763  }
764 
765  break;
766 
767  case '"':
768  // Handle empty quotes.
769  if( *line == '"' )
770  {
771  line++;
772  tmp.Clear();
773  aParameterList.Add( wxEmptyString );
774  break;
775  }
776 
777  while( *line != 0 )
778  {
779  key = *line;
780  line++;
781 
782  if( key == '"' )
783  {
784  aParameterList.Add( tmp );
785  tmp.Clear();
786  break;
787  }
788  else
789  {
790  tmp.Append( key );
791  }
792  }
793 
794  break;
795 
796  case '#':
797  line = aLineReader->ReadLine();
798  break;
799 
800  default:
801  tmp.Append( key );
802  break;
803  }
804  }
805 }
806 
807 
808 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
809 {
810  wxString number;
811 
812  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
813  {
814  long lflags;
815 
816  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
817  return true;
818  }
819  else if( aFlag.Contains( aName ) )
820  {
821  return true;
822  }
823 
824  return false;
825 }
826 
827 
829  m_cache( 0 ),
830  m_ctl( 0 )
831 {
832  m_reader = NULL;
833  init( 0 );
834 }
835 
836 
837 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
838  m_cache( 0 ),
839  m_ctl( aControlFlags )
840 {
841  m_reader = NULL;
842  init( 0 );
843 }
844 
845 
847 {
848  delete m_cache;
849 }
850 
851 
852 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
853 {
854  m_props = aProperties;
855 }
856 
857 
858 void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
859 {
860  if( !m_cache || ( checkModified && m_cache->IsModified() ) )
861  {
862  // a spectacular episode in memory management:
863  delete m_cache;
864  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
865  m_cache->Load();
866  }
867 }
868 
869 
870 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
871  bool aBestEfforts, const PROPERTIES* aProperties )
872 {
873  LOCALE_IO toggle; // toggles on, then off, the C locale.
874  wxDir dir( aLibraryPath );
875  wxString errorMsg;
876 
877  if( !dir.IsOpened() )
878  {
879  if( aBestEfforts )
880  return;
881  else
882  {
883  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
884  aLibraryPath ) );
885  }
886  }
887 
888  init( aProperties );
889 
890  try
891  {
892  validateCache( aLibraryPath );
893  }
894  catch( const IO_ERROR& ioe )
895  {
896  errorMsg = ioe.What();
897  }
898 
899  // Some of the files may have been parsed correctly so we want to add the valid files to
900  // the library.
901 
902  for( MODULE_CITER it = m_cache->GetModules().begin(); it != m_cache->GetModules().end(); ++it )
903  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
904 
905  if( !errorMsg.IsEmpty() && !aBestEfforts )
906  THROW_IO_ERROR( errorMsg );
907 }
908 
909 
910 const MODULE* GPCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
911  const wxString& aFootprintName,
912  const PROPERTIES* aProperties,
913  bool checkModified )
914 {
915  LOCALE_IO toggle; // toggles on, then off, the C locale.
916 
917  init( aProperties );
918 
919  validateCache( aLibraryPath, checkModified );
920 
921  const MODULE_MAP& mods = m_cache->GetModules();
922 
923  MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
924 
925  if( it == mods.end() )
926  {
927  return NULL;
928  }
929 
930  return it->second->GetModule();
931 }
932 
933 
934 const MODULE* GPCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
935  const wxString& aFootprintName,
936  const PROPERTIES* aProperties )
937 {
938  return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
939 }
940 
941 
942 MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
943  const PROPERTIES* aProperties )
944 {
945  const MODULE* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
946  return footprint ? (MODULE*) footprint->Duplicate() : 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( _( "user does not have permission to delete directory \"%s\"" ),
981  aLibraryPath.GetData() ) );
982  }
983 
984  wxDir dir( aLibraryPath );
985 
986  if( dir.HasSubDirs() )
987  {
988  THROW_IO_ERROR( wxString::Format( _( "library directory \"%s\" has unexpected sub-directories" ),
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\" was found in library path \"%s\"" ),
1008  files[i].GetData(), aLibraryPath.GetData() ) );
1009  }
1010  }
1011 
1012  for( i = 0; i < files.GetCount(); i++ )
1013  {
1014  wxRemoveFile( files[i] );
1015  }
1016  }
1017 
1018  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1019  aLibraryPath.GetData() );
1020 
1021  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1022  // we don't want that. we want bare metal portability with no UI here.
1023  if( !wxRmdir( aLibraryPath ) )
1024  {
1025  THROW_IO_ERROR( wxString::Format( _( "footprint library \"%s\" cannot be deleted" ),
1026  aLibraryPath.GetData() ) );
1027  }
1028 
1029  // For some reason removing a directory in Windows is not immediately updated. This delay
1030  // prevents an error when attempting to immediately recreate the same directory when over
1031  // writing an existing library.
1032 #ifdef __WINDOWS__
1033  wxMilliSleep( 250L );
1034 #endif
1035 
1036  if( m_cache && m_cache->GetPath() == aLibraryPath )
1037  {
1038  delete m_cache;
1039  m_cache = NULL;
1040  }
1041 
1042  return true;
1043 }
1044 
1045 
1046 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1047 {
1048  return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1049 }
1050 
1051 
1052 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1053 {
1054  LOCALE_IO toggle;
1055 
1056  init( NULL );
1057 
1058  validateCache( aLibraryPath );
1059 
1060  return m_cache->IsWritable();
1061 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:133
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:719
bool IsWritable() const
void SetEnd0(const wxPoint &aPoint)
MODULE_MAP::iterator MODULE_ITER
LINE_READER is an abstract class from which implementation specific LINE_READERs may be derived to re...
Definition: richio.h:81
void SetShape(STROKE_T aShape)
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:139
GPCB_PLUGIN * m_owner
MODULE_MAP::const_iterator MODULE_CITER
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
GPCB_PLUGIN is a PLUGIN derivation for saving and loading Geda PCB files.
Definition: gpcb_plugin.h:47
TEXTE_PCB class definition.
virtual const wxString & GetSource() const
Function GetSource returns the name of the source of the lines in an abstract sense.
Definition: richio.h:130
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:216
#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
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
const wxChar *const traceGedaPcbPlugin
Flag to enable GEDA PCB plugin debug output.
const std::string KiCadFootprintFileExtension
MODULE_MAP & GetModules()
MODULE * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=NULL) override
Function FootprintLoad loads a footprint having aFootprintName from the aLibraryPath containing a lib...
bool IsFootprintLibWritable(const wxString &aLibraryPath) override
Function IsFootprintLibWritable returns true iff the library at aLibraryPath is writable.
const MODULE * GetEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=NULL) override
Function GetEnumeratedFootprint a version of FootprintLoad() for use after FootprintEnumerate() for m...
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
double RAD2DECIDEG(double rad)
Definition: trigo.h:224
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:159
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...
usual segment : line with rounded ends
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:88
void SetDrillSize(const wxSize &aSize)
Definition: class_pad.h:229
void Remove(const wxString &aFootprintName)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
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: common.cpp:774
PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
void init(const PROPERTIES *aProperties)
std::map< wxString, MODULE * > MODULE_MAP
Definition: eagle_parser.h:51
This file contains miscellaneous commonly used macros and functions.
#define TO_UTF8(wxstring)
Macro TO_UTF8 converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:100
GPCB_FPL_CACHE * m_cache
Footprint library cache.
Definition: gpcb_plugin.h:97
FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:180
LSET is a set of PCB_LAYER_IDs.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:162
#define NULL
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:29
virtual unsigned LineNumber() const
Function Line Number returns the line number of the last line read from this LINE_READER.
Definition: richio.h:159
void SetName(const wxString &aName)
Set the pad name (sometimes called pad number, although it can be an array reference like AA12).
Definition: class_pad.h:131
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:217
const MODULE * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties, bool checkModified)
#define NEW_GPCB_UNIT_CONV
Arcs (with rounded ends)
void SetAngle(double aAngle) override
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition of file extensions used in Kicad.
void SetSize(const wxSize &aSize)
Definition: class_pad.h:223
boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP
const PROPERTIES * m_props
passed via Save() or Load(), no ownership, may be NULL.
Definition: gpcb_plugin.h:96
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:973
wxFileName m_lib_path
Plugin object that owns the cache.
wxLogTrace helper definitions.
MODULE_MAP m_modules
The path of the library.
void Load()
Save not implemented for the Geda PCB footprint library format.
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:498
void SetLocalClearance(int aClearance)
Definition: class_pad.h:351
std::unique_ptr< MODULE > m_module
long long m_cache_timestamp
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const PROPERTIES *aProperties=NULL) override
Return a list of footprint names contained within the library at aLibraryPath.
MODULE * parseMODULE(LINE_READER *aLineReader)
BOARD_ITEM * Duplicate() const override
Function Duplicate creates a copy of a BOARD_ITEM.
MODULE_MAP::const_iterator MODULE_CITER
MODULE * GetModule() const
virtual char * ReadLine()=0
Function ReadLine reads a line of text into the buffer and increments the line number counter.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:153
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition: common.h:415
Class to handle a graphic segment.
const char * name
Definition: DXF_plotter.cpp:60
#define TEXT_DEFAULT_SIZE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
GPCB_FPL_CACHE(GPCB_PLUGIN *aOwner, const wxString &aLibraryPath)
void SetLocalSolderMaskMargin(int aMargin)
Definition: class_pad.h:347
#define _(s)
Definition: 3d_actions.cpp:33
void validateCache(const wxString &aLibraryPath, bool checkModified=true)
bool FootprintLibDelete(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Function FootprintLibDelete deletes an existing footprint library and returns true,...
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
void SetStart0(const wxPoint &aPoint)
#define IU_PER_MILS
Definition: plotter.cpp:138
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
The common library.
const std::string GedaPcbFootprintLibFileExtension
void SetShape(PAD_SHAPE_T aShape)
Set the new shape of this pad.
Definition: class_pad.h:148
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:517
const wxPoint & GetEnd0() const
bool IsModified()
Function IsModified Return true if the cache is not up-to-date.
friend class GPCB_FPL_CACHE
Definition: gpcb_plugin.h:49
LINE_READER * m_reader
no ownership here.
Definition: gpcb_plugin.h:99
PAD_SHAPE_T GetShape() const
Definition: class_pad.h:157
bool testFlags(const wxString &aFlag, long aMask, const wxChar *aName)
Function testFlags tests aFlag for aMask or aName.
void FootprintDelete(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=NULL) override
Function FootprintDelete deletes aFootprintName from the library at aLibraryPath.
const wxSize & GetSize() const
Definition: class_pad.h:224
EDGE_MODULE class definition.
bool m_cache_dirty
Map of footprint file name per MODULE*.
GPCB_FPL_CACHE_ITEM(MODULE *aModule, const WX_FILENAME &aFileName)
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:76
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void SetLayerSet(LSET aLayers) override
Definition: class_pad.h:330
static long parseInt(const wxString &aValue, double aScalar)
Definition: gpcb_plugin.cpp:53
void SetWidth(int aWidth)