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-2019 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 
38 #include <class_board.h>
39 #include <class_module.h>
40 #include <class_pcb_text.h>
41 #include <class_drawsegment.h>
42 #include <class_edge_mod.h>
43 #include <gpcb_plugin.h>
44 
45 #include <wx/dir.h>
46 #include <wx/filename.h>
47 #include <wx/wfstream.h>
48 #include <boost/ptr_container/ptr_map.hpp>
49 #include <memory.h>
50 
51 
52 static inline long parseInt( const wxString& aValue, double aScalar )
53 {
54  double value = LONG_MAX;
55 
56  /*
57  * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
58  * Unit-less values are still centimils (100000 units per inch), like with
59  * the previous format.
60  *
61  * Distinction between the even older format (mils, 1000 units per inch)
62  * and the pre-2011 format is done in ::parseMODULE already; the
63  * distinction is by wether an object definition opens with '(' or '['.
64  * All values with explicite unit open with a '[' so there's no need to
65  * consider this distinction when parsing them.
66  *
67  * The solution here is to watch for a unit and, if present, convert the
68  * value to centimils. All unit-less values are read unaltered. This way
69  * the code below can contine to consider all read values to be in mils or
70  * centimils. It also matches the strategy gEDA/pcb uses for backwards
71  * compatibility with its own layouts.
72  *
73  * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
74  * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
75  * have to test for all 11 units gEDA/pcb allows in user dialogs.
76  */
77  if( aValue.EndsWith( wxT( "mm" ) ) )
78  {
79  aScalar *= 100000.0 / 25.4;
80  }
81  else if( aValue.EndsWith( wxT( "mil" ) ) )
82  {
83  aScalar *= 100.;
84  }
85 
86  // This conversion reports failure on strings as simple as "1000", still
87  // it returns the right result in &value. Thus, ignore the return value.
88  aValue.ToCDouble(&value);
89  if( value == LONG_MAX ) // conversion really failed
90  {
91  THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
92  aValue.GetData() ) );
93  return 0;
94  }
95 
96  return KiROUND( value * aScalar );
97 }
98 
99 
110 {
112  std::unique_ptr<MODULE> m_module;
113 
114 public:
115  GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName );
116 
117  WX_FILENAME GetFileName() const { return m_filename; }
118  MODULE* GetModule() const { return m_module.get(); }
119 };
120 
121 
123  m_filename( aFileName ),
124  m_module( aModule )
125 {
126 }
127 
128 
129 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
130 typedef MODULE_MAP::iterator MODULE_ITER;
131 typedef MODULE_MAP::const_iterator MODULE_CITER;
132 
133 
135 {
137  wxFileName m_lib_path;
139 
140  bool m_cache_dirty; // Stored separately because it's expensive to check
141  // m_cache_timestamp against all the files.
142  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
143  // files.
144 
145  MODULE* parseMODULE( LINE_READER* aLineReader );
146 
157  bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
158 
175  void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
176 
177 public:
178  GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
179 
180  wxString GetPath() const { return m_lib_path.GetPath(); }
181  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
183 
184  // Most all functions in this class throw IO_ERROR exceptions. There are no
185  // error codes nor user interface calls from here, nor in any PLUGIN.
186  // Catch these exceptions higher up please.
187 
189 
190  void Load();
191 
192  void Remove( const wxString& aFootprintName );
193 
200  static long long GetTimestamp( const wxString& aLibPath );
201 
206  bool IsModified();
207 };
208 
209 
210 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
211 {
212  m_owner = aOwner;
213  m_lib_path.SetPath( aLibraryPath );
214  m_cache_timestamp = 0;
215  m_cache_dirty = true;
216 }
217 
218 
220 {
221  m_cache_dirty = false;
222  m_cache_timestamp = 0;
223 
224  // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
225  // and the footprints are the .fp files inside this folder.
226 
227  wxDir dir( m_lib_path.GetPath() );
228 
229  if( !dir.IsOpened() )
230  {
231  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
232  m_lib_path.GetPath().GetData() ) );
233  }
234 
235  wxString fullName;
236  wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
237 
238  // wxFileName construction is egregiously slow. Construct it once and just swap out
239  // the filename thereafter.
240  WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
241 
242  if( !dir.GetFirst( &fullName, fileSpec ) )
243  return;
244 
245  wxString cacheErrorMsg;
246 
247  do
248  {
249  fn.SetFullName( fullName );
250 
251  // Queue I/O errors so only files that fail to parse don't get loaded.
252  try
253  {
254  // reader now owns fp, will close on exception or return
255  FILE_LINE_READER reader( fn.GetFullPath() );
256 
257  std::string name = TO_UTF8( fn.GetName() );
258  MODULE* footprint = parseMODULE( &reader );
259 
260  // The footprint name is the file name without the extension.
261  footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
262  m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn ) );
263  }
264  catch( const IO_ERROR& ioe )
265  {
266  if( !cacheErrorMsg.IsEmpty() )
267  cacheErrorMsg += "\n\n";
268 
269  cacheErrorMsg += ioe.What();
270  }
271  } while( dir.GetNext( &fullName ) );
272 
273  if( !cacheErrorMsg.IsEmpty() )
274  THROW_IO_ERROR( cacheErrorMsg );
275 }
276 
277 
278 void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
279 {
280  std::string footprintName = TO_UTF8( aFootprintName );
281 
282  MODULE_CITER it = m_modules.find( footprintName );
283 
284  if( it == m_modules.end() )
285  {
286  THROW_IO_ERROR( wxString::Format( _( "library \"%s\" has no footprint \"%s\" to delete" ),
287  m_lib_path.GetPath().GetData(),
288  aFootprintName.GetData() ) );
289  }
290 
291  // Remove the module from the cache and delete the module file from the library.
292  wxString fullPath = it->second->GetFileName().GetFullPath();
293  m_modules.erase( footprintName );
294  wxRemoveFile( fullPath );
295 }
296 
297 
299 {
301 
302  return m_cache_dirty;
303 }
304 
305 
306 long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
307 {
308  wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
309 
310  return TimestampDir( aLibPath, fileSpec );
311 }
312 
313 
315 {
316  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
317  #define OLD_GPCB_UNIT_CONV IU_PER_MILS
318 
319  // Old version unit = 1 mil, so conv_unit is 10 or 0.1
320  #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
321 
322  int paramCnt;
323  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
324  wxPoint textPos;
325  wxString msg;
326  wxArrayString parameters;
327  std::unique_ptr<MODULE> module( new MODULE( NULL ) );
328 
329 
330  if( aLineReader->ReadLine() == NULL )
331  {
332  msg = aLineReader->GetSource() + ": empty file";
333  THROW_IO_ERROR( msg );
334  }
335 
336  parameters.Clear();
337  parseParameters( parameters, aLineReader );
338  paramCnt = parameters.GetCount();
339 
340  /* From the Geda PCB documentation, valid Element definitions:
341  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
342  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
343  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
344  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
345  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
346  */
347 
348  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
349  {
350  msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
351  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
352  aLineReader->LineNumber(), 0 );
353  }
354 
355  if( paramCnt < 10 || paramCnt > 14 )
356  {
357  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
358  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
359  aLineReader->LineNumber(), 0 );
360  }
361 
362  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
363  if( parameters[1] == wxT( "(" ) )
364  conv_unit = OLD_GPCB_UNIT_CONV;
365 
366  if( paramCnt > 10 )
367  {
368  module->SetDescription( parameters[3] );
369  module->SetReference( parameters[4] );
370  }
371  else
372  {
373  module->SetDescription( parameters[2] );
374  module->SetReference( parameters[3] );
375  }
376 
377  // Read value
378  if( paramCnt > 10 )
379  module->SetValue( parameters[5] );
380  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
381  // often empty in bare footprints.
382  if( module->Value().GetText().IsEmpty() )
383  module->Value().SetText( wxT( "Val**" ) );
384 
385 
386  if( paramCnt == 14 )
387  {
388  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
389  parseInt( parameters[9], conv_unit ) );
390  }
391  else
392  {
393  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
394  parseInt( parameters[7], conv_unit ) );
395  }
396 
397  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
398  module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
399 
400  // Calculate size: default height is 40 mils, width 30 mil.
401  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
402  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
403  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
404  int twsize = thsize * 30 / 40;
405  int thickness = thsize / 8;
406 
407  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
408  // Compensate for this by shifting the insertion point instead of the
409  // aligment, because alignment isn't changeable in the GUI.
410  textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
411  textPos.y += thsize / 2;
412 
413  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
414  // high/right. Compensate for similar appearance.
415  textPos.x -= thsize / 10;
416  textPos.y += thsize / 2;
417 
418  module->Reference().SetTextPos( textPos );
419  module->Reference().SetPos0( textPos );
420  module->Reference().SetTextSize( wxSize( twsize, thsize ) );
421  module->Reference().SetThickness( thickness );
422 
423  // gEDA/pcb shows only one of value/reference/description at a time. Which
424  // one is selectable by a global menu setting. pcbnew needs reference as
425  // well as value visible, so place the value right below the reference.
426  module->Value().SetTextAngle( module->Reference().GetTextAngle() );
427  module->Value().SetTextSize( module->Reference().GetTextSize() );
428  module->Value().SetThickness( module->Reference().GetThickness() );
429  textPos.y += thsize * 13 / 10; // 130% line height
430  module->Value().SetTextPos( textPos );
431  module->Value().SetPos0( textPos );
432 
433  while( aLineReader->ReadLine() )
434  {
435  parameters.Clear();
436  parseParameters( parameters, aLineReader );
437 
438  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
439  continue;
440 
441  if( parameters[0] == wxT( ")" ) )
442  break;
443 
444  paramCnt = parameters.GetCount();
445 
446  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
447  if( paramCnt > 3 )
448  {
449  if( parameters[1] == wxT( "(" ) )
450  conv_unit = OLD_GPCB_UNIT_CONV;
451  else
452  conv_unit = NEW_GPCB_UNIT_CONV;
453  }
454 
455  wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
456  GetChars( parameters[0] ), paramCnt );
457 
458  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
459  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
460  {
461  if( paramCnt != 8 )
462  {
463  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
464  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
465  aLineReader->LineNumber(), 0 );
466  }
467 
468  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
469  drawSeg->SetLayer( F_SilkS );
470  drawSeg->SetShape( S_SEGMENT );
471  drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
472  parseInt( parameters[3], conv_unit ) ) );
473  drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
474  parseInt( parameters[5], conv_unit ) ) );
475  drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
476  drawSeg->SetDrawCoord();
477  module->GraphicalItemsList().PushBack( drawSeg );
478  continue;
479  }
480 
481  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
482  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
483  {
484  if( paramCnt != 10 )
485  {
486  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
487  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
488  aLineReader->LineNumber(), 0 );
489  }
490 
491  // Pcbnew does know ellipse so we must have Width = Height
492  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
493  drawSeg->SetLayer( F_SilkS );
494  drawSeg->SetShape( S_ARC );
495  module->GraphicalItemsList().PushBack( drawSeg );
496 
497  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
498  int radius = ( parseInt( parameters[4], conv_unit ) +
499  parseInt( parameters[5], conv_unit ) ) / 2;
500 
501  wxPoint centre( parseInt( parameters[2], conv_unit ),
502  parseInt( parameters[3], conv_unit ) );
503 
504  drawSeg->SetStart0( centre );
505 
506  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
507  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
508 
509  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
510  double sweep_angle = parseInt( parameters[7], -10.0 );
511 
512  // Geda PCB does not support circles.
513  if( sweep_angle == -3600.0 )
514  drawSeg->SetShape( S_CIRCLE );
515 
516  // Angle value is clockwise in gpcb and Pcbnew.
517  drawSeg->SetAngle( sweep_angle );
518  drawSeg->SetEnd0( wxPoint( radius, 0 ) );
519 
520  // Calculate start point coordinate of arc
521  wxPoint arcStart( drawSeg->GetEnd0() );
522  RotatePoint( &arcStart, -start_angle );
523  drawSeg->SetEnd0( centre + arcStart );
524  drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
525  drawSeg->SetDrawCoord();
526  continue;
527  }
528 
529  // Parse a Pad with no hole with format:
530  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
531  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
532  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
533  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
534  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
535  {
536  if( paramCnt < 10 || paramCnt > 13 )
537  {
538  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
539  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
540  aLineReader->LineNumber(), 0 );
541  }
542 
543  D_PAD* pad = new D_PAD( module.get() );
544 
545  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
546  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
547 
548  pad->SetShape( PAD_SHAPE_RECT );
550  pad->SetLayerSet( pad_front );
551 
552  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
553  pad->SetLayerSet( pad_back );
554 
555  // Set the pad name:
556  // Pcbnew pad name is used for electrical connection calculations.
557  // Accordingly it should be mapped to gEDA's pin/pad number,
558  // which is used for the same purpose.
559  // gEDA also features a pin/pad "name", which is an arbitrary string
560  // and set to the pin name of the netlist on instantiation. Many gEDA
561  // bare footprints use identical strings for name and number, so this
562  // can be a bit confusing.
563  pad->SetName( parameters[paramCnt-3] );
564 
565  int x1 = parseInt( parameters[2], conv_unit );
566  int x2 = parseInt( parameters[4], conv_unit );
567  int y1 = parseInt( parameters[3], conv_unit );
568  int y2 = parseInt( parameters[5], conv_unit );
569  int width = parseInt( parameters[6], conv_unit );
570  wxPoint delta( x2 - x1, y2 - y1 );
571  double angle = atan2( (double)delta.y, (double)delta.x );
572 
573  // Get the pad clearance and the solder mask clearance.
574  if( paramCnt == 13 )
575  {
576  int clearance = parseInt( parameters[7], conv_unit );
577  // One of gEDA's oddities is that clearance between pad and polygon
578  // is given as the gap on both sides of the pad together, so for
579  // KiCad it has to halfed.
580  pad->SetLocalClearance( clearance / 2 );
581 
582  // In GEDA, the mask value is the size of the hole in this
583  // solder mask. In Pcbnew, it is a margin, therefore the distance
584  // between the copper and the mask
585  int maskMargin = parseInt( parameters[8], conv_unit );
586  maskMargin = ( maskMargin - width ) / 2;
587  pad->SetLocalSolderMaskMargin( maskMargin );
588  }
589 
590  // Negate angle (due to Y reversed axis) and convert it to internal units
591  angle = - RAD2DECIDEG( angle );
592  pad->SetOrientation( KiROUND( angle ) );
593 
594  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
595 
596  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
597  width ) );
598 
599  // Set the relative position before adjusting the absolute position
600  pad->SetPos0( padPos );
601  padPos += module->GetPosition();
602  pad->SetPosition( padPos );
603 
604  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
605  {
606  if( pad->GetSize().x == pad->GetSize().y )
607  pad->SetShape( PAD_SHAPE_CIRCLE );
608  else
609  pad->SetShape( PAD_SHAPE_OVAL );
610  }
611 
612  module->Add( pad );
613  continue;
614  }
615 
616  // Parse a Pin with through hole with format:
617  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
618  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
619  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
620  // Pin (aX aY Thickness Drill "Name" NFlags)
621  // Pin (aX aY Thickness "Name" NFlags)
622  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
623  {
624  if( paramCnt < 8 || paramCnt > 12 )
625  {
626  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
627  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
628  aLineReader->LineNumber(), 0 );
629  }
630 
631  D_PAD* pad = new D_PAD( module.get() );
632 
633  pad->SetShape( PAD_SHAPE_CIRCLE );
634 
635  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
636 
637  pad->SetLayerSet( pad_set );
638 
639  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
640  pad->SetShape( PAD_SHAPE_RECT );
641 
642  // Set the pad name:
643  // Pcbnew pad name is used for electrical connection calculations.
644  // Accordingly it should be mapped to gEDA's pin/pad number,
645  // which is used for the same purpose.
646  pad->SetName( parameters[paramCnt-3] );
647 
648  wxPoint padPos( parseInt( parameters[2], conv_unit ),
649  parseInt( parameters[3], conv_unit ) );
650 
651  int padSize = parseInt( parameters[4], conv_unit );
652 
653  pad->SetSize( wxSize( padSize, padSize ) );
654 
655  int drillSize = 0;
656 
657  // Get the pad clearance, solder mask clearance, and drill size.
658  if( paramCnt == 12 )
659  {
660  int clearance = parseInt( parameters[5], conv_unit );
661  // One of gEDA's oddities is that clearance between pad and polygon
662  // is given as the gap on both sides of the pad together, so for
663  // KiCad it has to halfed.
664  pad->SetLocalClearance( clearance / 2 );
665 
666  // In GEDA, the mask value is the size of the hole in this
667  // solder mask. In Pcbnew, it is a margin, therefore the distance
668  // between the copper and the mask
669  int maskMargin = parseInt( parameters[6], conv_unit );
670  maskMargin = ( maskMargin - padSize ) / 2;
671  pad->SetLocalSolderMaskMargin( maskMargin );
672 
673  drillSize = parseInt( parameters[7], conv_unit );
674  }
675  else
676  {
677  drillSize = parseInt( parameters[5], conv_unit );
678  }
679 
680  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
681 
682  // Set the relative position before adjusting the absolute position
683  pad->SetPos0( padPos );
684  padPos += module->GetPosition();
685  pad->SetPosition( padPos );
686 
687  if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
688  pad->SetShape( PAD_SHAPE_OVAL );
689 
690  module->Add( pad );
691  continue;
692  }
693  }
694 
695  // Recalculate the bounding box
696  module->CalculateBoundingBox();
697  return module.release();
698 }
699 
700 
701 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
702 {
703  char key;
704  wxString tmp;
705  char* line = aLineReader->Line();
706 
707  // Last line already ready in main parser loop.
708  while( *line != 0 )
709  {
710  key = *line;
711  line++;
712 
713  switch( key )
714  {
715  case '[':
716  case '(':
717  if( !tmp.IsEmpty() )
718  {
719  aParameterList.Add( tmp );
720  tmp.Clear();
721  }
722 
723  tmp.Append( key );
724  aParameterList.Add( tmp );
725  tmp.Clear();
726 
727  // Opening delimiter "(" after Element statement. Any other occurrence is part
728  // of a keyword definition.
729  if( aParameterList.GetCount() == 1 )
730  {
731  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
732  return;
733  }
734 
735  break;
736 
737  case ']':
738  case ')':
739  if( !tmp.IsEmpty() )
740  {
741  aParameterList.Add( tmp );
742  tmp.Clear();
743  }
744 
745  tmp.Append( key );
746  aParameterList.Add( tmp );
747  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
748  return;
749 
750  case '\n':
751  case '\r':
752  // Element descriptions can span multiple lines.
753  line = aLineReader->ReadLine();
754 
755  // Fall through is intentional.
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,
871  const wxString& aLibraryPath,
872  const PROPERTIES* aProperties )
873 {
874  LOCALE_IO toggle; // toggles on, then off, the C locale.
875  wxDir dir( aLibraryPath );
876 
877  if( !dir.IsOpened() )
878  {
879  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
880  GetChars( aLibraryPath ) ) );
881  }
882 
883  init( aProperties );
884 
885  wxString errorMsg;
886 
887  // Some of the files may have been parsed correctly so we want to add the valid files to
888  // the library.
889  try
890  {
891  validateCache( aLibraryPath );
892  }
893  catch( const IO_ERROR& ioe )
894  {
895  errorMsg = ioe.What();
896  }
897 
898  const MODULE_MAP& mods = m_cache->GetModules();
899 
900  for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
901  {
902  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
903  }
904 
905  if( !errorMsg.IsEmpty() )
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 ? new MODULE( *footprint ) : 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:112
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:676
bool IsWritable() const
void SetEnd0(const wxPoint &aPoint)
MODULE_MAP::iterator MODULE_ITER
Class LINE_READER is an abstract class from which implementation specific LINE_READERs may be derived...
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:53
Class 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
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:121
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: common.h:180
#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...
Class BOARD to handle a board.
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:204
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:219
Class 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:274
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
void Remove(const wxString &aFootprintName)
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:633
Class PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
void init(const PROPERTIES *aProperties)
static const int delta[8][2]
Definition: solve.cpp:112
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:47
GPCB_FPL_CACHE * m_cache
Footprint library cache.
Definition: gpcb_plugin.h:97
Class FILE_LINE_READER is a LINE_READER that reads from an open file.
Definition: richio.h:180
Class LSET is a set of PCB_LAYER_IDs.
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:182
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:262
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:268
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:832
wxFileName m_lib_path
Plugin object that owns the cache.
wxLogTrace helper definitions.
#define THROW_IO_ERROR(msg)
std::map< wxString, MODULE * > MODULE_MAP
Definition: eagle_plugin.h:36
#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:420
void SetLocalClearance(int aClearance)
Definition: class_pad.h:425
std::unique_ptr< MODULE > m_module
long long m_cache_timestamp
MODULE * parseMODULE(LINE_READER *aLineReader)
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:408
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:92
A wrapper around a wxFileName which is much more performant with a subset of the API.
Definition: common.h:400
Class to handle a graphic segment.
const char * name
Definition: DXF_plotter.cpp:61
#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
#define max(a, b)
Definition: auxiliary.h:86
GPCB_FPL_CACHE(GPCB_PLUGIN *aOwner, const wxString &aLibraryPath)
void SetLocalSolderMaskMargin(int aMargin)
Definition: class_pad.h:422
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)
size_t i
Definition: json11.cpp:597
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:134
The common library.
const std::string GedaPcbFootprintLibFileExtension
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:217
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:429
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:216
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Return a list of footprint names contained within the library at aLibraryPath.
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:269
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:52
void SetWidth(int aWidth)