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 = LONG_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 == LONG_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().SetThickness( 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().SetThickness( module->Reference().GetThickness() );
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();
755 
756  // Intentionally fall through
757 
758  case '\t':
759  case ' ':
760  if( !tmp.IsEmpty() )
761  {
762  aParameterList.Add( tmp );
763  tmp.Clear();
764  }
765 
766  break;
767 
768  case '"':
769  // Handle empty quotes.
770  if( *line == '"' )
771  {
772  line++;
773  tmp.Clear();
774  aParameterList.Add( wxEmptyString );
775  break;
776  }
777 
778  while( *line != 0 )
779  {
780  key = *line;
781  line++;
782 
783  if( key == '"' )
784  {
785  aParameterList.Add( tmp );
786  tmp.Clear();
787  break;
788  }
789  else
790  {
791  tmp.Append( key );
792  }
793  }
794 
795  break;
796 
797  case '#':
798  line = aLineReader->ReadLine();
799  break;
800 
801  default:
802  tmp.Append( key );
803  break;
804  }
805  }
806 }
807 
808 
809 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
810 {
811  wxString number;
812 
813  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
814  {
815  long lflags;
816 
817  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
818  return true;
819  }
820  else if( aFlag.Contains( aName ) )
821  {
822  return true;
823  }
824 
825  return false;
826 }
827 
828 
830  m_cache( 0 ),
831  m_ctl( 0 )
832 {
833  m_reader = NULL;
834  init( 0 );
835 }
836 
837 
838 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
839  m_cache( 0 ),
840  m_ctl( aControlFlags )
841 {
842  m_reader = NULL;
843  init( 0 );
844 }
845 
846 
848 {
849  delete m_cache;
850 }
851 
852 
853 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
854 {
855  m_props = aProperties;
856 }
857 
858 
859 void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
860 {
861  if( !m_cache || ( checkModified && m_cache->IsModified() ) )
862  {
863  // a spectacular episode in memory management:
864  delete m_cache;
865  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
866  m_cache->Load();
867  }
868 }
869 
870 
871 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
872  bool aBestEfforts, const PROPERTIES* aProperties )
873 {
874  LOCALE_IO toggle; // toggles on, then off, the C locale.
875  wxDir dir( aLibraryPath );
876  wxString errorMsg;
877 
878  if( !dir.IsOpened() )
879  {
880  if( aBestEfforts )
881  return;
882  else
883  {
884  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
885  aLibraryPath ) );
886  }
887  }
888 
889  init( aProperties );
890 
891  try
892  {
893  validateCache( aLibraryPath );
894  }
895  catch( const IO_ERROR& ioe )
896  {
897  errorMsg = ioe.What();
898  }
899 
900  // Some of the files may have been parsed correctly so we want to add the valid files to
901  // the library.
902 
903  for( MODULE_CITER it = m_cache->GetModules().begin(); it != m_cache->GetModules().end(); ++it )
904  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
905 
906  if( !errorMsg.IsEmpty() && !aBestEfforts )
907  THROW_IO_ERROR( errorMsg );
908 }
909 
910 
911 const MODULE* GPCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
912  const wxString& aFootprintName,
913  const PROPERTIES* aProperties,
914  bool checkModified )
915 {
916  LOCALE_IO toggle; // toggles on, then off, the C locale.
917 
918  init( aProperties );
919 
920  validateCache( aLibraryPath, checkModified );
921 
922  const MODULE_MAP& mods = m_cache->GetModules();
923 
924  MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
925 
926  if( it == mods.end() )
927  {
928  return NULL;
929  }
930 
931  return it->second->GetModule();
932 }
933 
934 
935 const MODULE* GPCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
936  const wxString& aFootprintName,
937  const PROPERTIES* aProperties )
938 {
939  return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
940 }
941 
942 
943 MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
944  const PROPERTIES* aProperties )
945 {
946  const MODULE* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
947  return footprint ? (MODULE*) footprint->Duplicate() : nullptr;
948 }
949 
950 
951 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
952  const PROPERTIES* aProperties )
953 {
954  LOCALE_IO toggle; // toggles on, then off, the C locale.
955 
956  init( aProperties );
957 
958  validateCache( aLibraryPath );
959 
960  if( !m_cache->IsWritable() )
961  {
962  THROW_IO_ERROR( wxString::Format( _( "Library \"%s\" is read only" ),
963  aLibraryPath.GetData() ) );
964  }
965 
966  m_cache->Remove( aFootprintName );
967 }
968 
969 
970 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
971 {
972  wxFileName fn;
973  fn.SetPath( aLibraryPath );
974 
975  // Return if there is no library path to delete.
976  if( !fn.DirExists() )
977  return false;
978 
979  if( !fn.IsDirWritable() )
980  {
981  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory \"%s\"" ),
982  aLibraryPath.GetData() ) );
983  }
984 
985  wxDir dir( aLibraryPath );
986 
987  if( dir.HasSubDirs() )
988  {
989  THROW_IO_ERROR( wxString::Format( _( "library directory \"%s\" has unexpected sub-directories" ),
990  aLibraryPath.GetData() ) );
991  }
992 
993  // All the footprint files must be deleted before the directory can be deleted.
994  if( dir.HasFiles() )
995  {
996  unsigned i;
997  wxFileName tmp;
998  wxArrayString files;
999 
1000  wxDir::GetAllFiles( aLibraryPath, &files );
1001 
1002  for( i = 0; i < files.GetCount(); i++ )
1003  {
1004  tmp = files[i];
1005 
1006  if( tmp.GetExt() != KiCadFootprintFileExtension )
1007  {
1008  THROW_IO_ERROR( wxString::Format( _( "unexpected file \"%s\" was found in library path \"%s\"" ),
1009  files[i].GetData(), aLibraryPath.GetData() ) );
1010  }
1011  }
1012 
1013  for( i = 0; i < files.GetCount(); i++ )
1014  {
1015  wxRemoveFile( files[i] );
1016  }
1017  }
1018 
1019  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1020  aLibraryPath.GetData() );
1021 
1022  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1023  // we don't want that. we want bare metal portability with no UI here.
1024  if( !wxRmdir( aLibraryPath ) )
1025  {
1026  THROW_IO_ERROR( wxString::Format( _( "footprint library \"%s\" cannot be deleted" ),
1027  aLibraryPath.GetData() ) );
1028  }
1029 
1030  // For some reason removing a directory in Windows is not immediately updated. This delay
1031  // prevents an error when attempting to immediately recreate the same directory when over
1032  // writing an existing library.
1033 #ifdef __WINDOWS__
1034  wxMilliSleep( 250L );
1035 #endif
1036 
1037  if( m_cache && m_cache->GetPath() == aLibraryPath )
1038  {
1039  delete m_cache;
1040  m_cache = NULL;
1041  }
1042 
1043  return true;
1044 }
1045 
1046 
1047 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1048 {
1049  return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
1050 }
1051 
1052 
1053 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1054 {
1055  LOCALE_IO toggle;
1056 
1057  init( NULL );
1058 
1059  validateCache( aLibraryPath );
1060 
1061  return m_cache->IsWritable();
1062 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
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:686
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:62
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:202
#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:62
double RAD2DECIDEG(double rad)
Definition: trigo.h:219
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:240
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
void SetDrillSize(const wxSize &aSize)
Definition: class_pad.h:305
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:717
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:48
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 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:33
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:187
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:293
const MODULE * getFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties, bool checkModified)
#define NEW_GPCB_UNIT_CONV
Arcs (with rounded ends)
Definition of file extensions used in Kicad.
void SetSize(const wxSize &aSize)
Definition: class_pad.h:299
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:916
wxFileName m_lib_path
Plugin object that owns the cache.
wxLogTrace helper definitions.
#define THROW_IO_ERROR(msg)
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
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:423
void SetLocalClearance(int aClearance)
Definition: class_pad.h:465
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)
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:445
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:101
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition: common.h:391
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:205
GPCB_FPL_CACHE(GPCB_PLUGIN *aOwner, const wxString &aLibraryPath)
void SetLocalSolderMaskMargin(int aMargin)
Definition: class_pad.h:462
#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: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:61
The common library.
const std::string GedaPcbFootprintLibFileExtension
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:238
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:438
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
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
Function GetShape.
Definition: class_pad.h:237
Module description (excepted pads)
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:300
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
static long parseInt(const wxString &aValue, double aScalar)
Definition: gpcb_plugin.cpp:53
BOARD_ITEM * Duplicate() const
Function Duplicate creates a copy of a BOARD_ITEM.
void SetWidth(int aWidth)