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-2018 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 {
111  wxFileName m_file_name;
112  bool m_writable;
113  std::unique_ptr<MODULE> m_module;
114 
115 public:
116  GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
117 
118  wxString GetName() const { return m_file_name.GetDirs().Last(); }
119  wxFileName GetFileName() const { return m_file_name; }
120  MODULE* GetModule() const { return m_module.get(); }
121 };
122 
123 
124 GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
125  m_module( aModule )
126 {
127  m_file_name = aFileName;
128  m_writable = true; // temporary init
129 }
130 
131 
132 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
133 typedef MODULE_MAP::iterator MODULE_ITER;
134 typedef MODULE_MAP::const_iterator MODULE_CITER;
135 
136 
138 {
140  wxFileName m_lib_path;
142 
143  bool m_cache_dirty; // Stored separately because it's expensive to check
144  // m_cache_timestamp against all the files.
145  long long m_cache_timestamp; // A hash of the timestamps for all the footprint
146  // files.
147 
148  MODULE* parseMODULE( LINE_READER* aLineReader );
149 
160  bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
161 
178  void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
179 
180 public:
181  GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
182 
183  wxString GetPath() const { return m_lib_path.GetPath(); }
184  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
185  MODULE_MAP& GetModules() { return m_modules; }
186 
187  // Most all functions in this class throw IO_ERROR exceptions. There are no
188  // error codes nor user interface calls from here, nor in any PLUGIN.
189  // Catch these exceptions higher up please.
190 
192 
193  void Load();
194 
195  void Remove( const wxString& aFootprintName );
196 
203  long long GetTimestamp();
204 
209  bool IsModified();
210 
223  bool IsPath( const wxString& aPath ) const;
224 };
225 
226 
227 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
228 {
229  m_owner = aOwner;
230  m_lib_path.SetPath( aLibraryPath );
231  m_cache_timestamp = 0;
232  m_cache_dirty = true;
233 }
234 
235 
237 {
238  // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
239  // and the footprints are the .fp files inside this folder.
240 
241  wxDir dir( m_lib_path.GetPath() );
242 
243  if( !dir.IsOpened() )
244  {
245  m_cache_timestamp = 0;
246  m_cache_dirty = false;
247 
248  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
249  m_lib_path.GetPath().GetData() ) );
250  }
251  else
252  {
253  m_cache_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
254  m_cache_dirty = false;
255  }
256 
257  wxString fpFileName;
258  wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
259 
260  if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
261  return;
262 
263  wxString cacheErrorMsg;
264 
265  do
266  {
267  wxFileName fn( m_lib_path.GetPath(), fpFileName );
268 
269  // Queue I/O errors so only files that fail to parse don't get loaded.
270  try
271  {
272  // reader now owns fp, will close on exception or return
273  FILE_LINE_READER reader( fn.GetFullPath() );
274 
275  std::string name = TO_UTF8( fn.GetName() );
276  MODULE* footprint = parseMODULE( &reader );
277 
278  // The footprint name is the file name without the extension.
279  footprint->SetFPID( LIB_ID( fn.GetName() ) );
280  m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn.GetName() ) );
281  }
282  catch( const IO_ERROR& ioe )
283  {
284  if( !cacheErrorMsg.IsEmpty() )
285  cacheErrorMsg += "\n\n";
286 
287  cacheErrorMsg += ioe.What();
288  }
289  } while( dir.GetNext( &fpFileName ) );
290 
291  if( !cacheErrorMsg.IsEmpty() )
292  THROW_IO_ERROR( cacheErrorMsg );
293 }
294 
295 
296 void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
297 {
298  std::string footprintName = TO_UTF8( aFootprintName );
299 
300  MODULE_CITER it = m_modules.find( footprintName );
301 
302  if( it == m_modules.end() )
303  {
304  THROW_IO_ERROR( wxString::Format( _( "library \"%s\" has no footprint \"%s\" to delete" ),
305  m_lib_path.GetPath().GetData(),
306  aFootprintName.GetData() ) );
307  }
308 
309  // Remove the module from the cache and delete the module file from the library.
310  wxString fullPath = it->second->GetFileName().GetFullPath();
311  m_modules.erase( footprintName );
312  wxRemoveFile( fullPath );
313 }
314 
315 
316 bool GPCB_FPL_CACHE::IsPath( const wxString& aPath ) const
317 {
318  // Converts path separators to native path separators
319  wxFileName newPath;
320  newPath.AssignDir( aPath );
321 
322  return m_lib_path == newPath;
323 }
324 
325 
327 {
328  if( m_cache_dirty )
329  return true;
330  else
331  return GetTimestamp() != m_cache_timestamp;
332 }
333 
334 
336 {
337  // Avoid expensive GetModificationTime checks if we already know we're dirty
338  if( m_cache_dirty )
339  return wxDateTime::Now().GetValue().GetValue();
340 
341  long long files_timestamp = 0;
342 
343  if( m_lib_path.DirExists() )
344  {
345  files_timestamp = m_lib_path.GetModificationTime().GetValue().GetValue();
346 
347  for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
348  {
349  wxFileName moduleFile = it->second->GetFileName();
350  if( moduleFile.FileExists() )
351  files_timestamp += moduleFile.GetModificationTime().GetValue().GetValue();
352  }
353  }
354 
355  // If the new timestamp doesn't match the cache timestamp, then save ourselves the
356  // expensive calls next time
357  if( m_cache_timestamp != files_timestamp )
358  m_cache_dirty = true;
359 
360  return files_timestamp;
361 }
362 
363 
365 {
366  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
367  #define OLD_GPCB_UNIT_CONV IU_PER_MILS
368 
369  // Old version unit = 1 mil, so conv_unit is 10 or 0.1
370  #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
371 
372  int paramCnt;
373  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
374  wxPoint textPos;
375  wxString msg;
376  wxArrayString parameters;
377  std::unique_ptr<MODULE> module( new MODULE( NULL ) );
378 
379 
380  if( aLineReader->ReadLine() == NULL )
381  {
382  msg = aLineReader->GetSource() + ": empty file";
383  THROW_IO_ERROR( msg );
384  }
385 
386  parameters.Clear();
387  parseParameters( parameters, aLineReader );
388  paramCnt = parameters.GetCount();
389 
390  /* From the Geda PCB documentation, valid Element definitions:
391  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
392  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
393  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
394  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
395  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
396  */
397 
398  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
399  {
400  msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
401  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
402  aLineReader->LineNumber(), 0 );
403  }
404 
405  if( paramCnt < 10 || paramCnt > 14 )
406  {
407  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
408  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
409  aLineReader->LineNumber(), 0 );
410  }
411 
412  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
413  if( parameters[1] == wxT( "(" ) )
414  conv_unit = OLD_GPCB_UNIT_CONV;
415 
416  if( paramCnt > 10 )
417  {
418  module->SetDescription( parameters[3] );
419  module->SetReference( parameters[4] );
420  }
421  else
422  {
423  module->SetDescription( parameters[2] );
424  module->SetReference( parameters[3] );
425  }
426 
427  // Read value
428  if( paramCnt > 10 )
429  module->SetValue( parameters[5] );
430  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
431  // often empty in bare footprints.
432  if( module->Value().GetText().IsEmpty() )
433  module->Value().SetText( wxT( "Val**" ) );
434 
435 
436  if( paramCnt == 14 )
437  {
438  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
439  parseInt( parameters[9], conv_unit ) );
440  }
441  else
442  {
443  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
444  parseInt( parameters[7], conv_unit ) );
445  }
446 
447  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
448  module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
449 
450  // Calculate size: default height is 40 mils, width 30 mil.
451  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
452  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
453  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
454  int twsize = thsize * 30 / 40;
455  int thickness = thsize / 8;
456 
457  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
458  // Compensate for this by shifting the insertion point instead of the
459  // aligment, because alignment isn't changeable in the GUI.
460  textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
461  textPos.y += thsize / 2;
462 
463  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
464  // high/right. Compensate for similar appearance.
465  textPos.x -= thsize / 10;
466  textPos.y += thsize / 2;
467 
468  module->Reference().SetTextPos( textPos );
469  module->Reference().SetPos0( textPos );
470  module->Reference().SetTextSize( wxSize( twsize, thsize ) );
471  module->Reference().SetThickness( thickness );
472 
473  // gEDA/pcb shows only one of value/reference/description at a time. Which
474  // one is selectable by a global menu setting. pcbnew needs reference as
475  // well as value visible, so place the value right below the reference.
476  module->Value().SetTextAngle( module->Reference().GetTextAngle() );
477  module->Value().SetTextSize( module->Reference().GetTextSize() );
478  module->Value().SetThickness( module->Reference().GetThickness() );
479  textPos.y += thsize * 13 / 10; // 130% line height
480  module->Value().SetTextPos( textPos );
481  module->Value().SetPos0( textPos );
482 
483  while( aLineReader->ReadLine() )
484  {
485  parameters.Clear();
486  parseParameters( parameters, aLineReader );
487 
488  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
489  continue;
490 
491  if( parameters[0] == wxT( ")" ) )
492  break;
493 
494  paramCnt = parameters.GetCount();
495 
496  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
497  if( paramCnt > 3 )
498  {
499  if( parameters[1] == wxT( "(" ) )
500  conv_unit = OLD_GPCB_UNIT_CONV;
501  else
502  conv_unit = NEW_GPCB_UNIT_CONV;
503  }
504 
505  wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
506  GetChars( parameters[0] ), paramCnt );
507 
508  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
509  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
510  {
511  if( paramCnt != 8 )
512  {
513  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
514  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
515  aLineReader->LineNumber(), 0 );
516  }
517 
518  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
519  drawSeg->SetLayer( F_SilkS );
520  drawSeg->SetShape( S_SEGMENT );
521  drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
522  parseInt( parameters[3], conv_unit ) ) );
523  drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
524  parseInt( parameters[5], conv_unit ) ) );
525  drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
526  drawSeg->SetDrawCoord();
527  module->GraphicalItemsList().PushBack( drawSeg );
528  continue;
529  }
530 
531  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
532  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
533  {
534  if( paramCnt != 10 )
535  {
536  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
537  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
538  aLineReader->LineNumber(), 0 );
539  }
540 
541  // Pcbnew does know ellipse so we must have Width = Height
542  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
543  drawSeg->SetLayer( F_SilkS );
544  drawSeg->SetShape( S_ARC );
545  module->GraphicalItemsList().PushBack( drawSeg );
546 
547  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
548  int radius = ( parseInt( parameters[4], conv_unit ) +
549  parseInt( parameters[5], conv_unit ) ) / 2;
550 
551  wxPoint centre( parseInt( parameters[2], conv_unit ),
552  parseInt( parameters[3], conv_unit ) );
553 
554  drawSeg->SetStart0( centre );
555 
556  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
557  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
558 
559  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
560  double sweep_angle = parseInt( parameters[7], -10.0 );
561 
562  // Geda PCB does not support circles.
563  if( sweep_angle == -3600.0 )
564  drawSeg->SetShape( S_CIRCLE );
565 
566  // Angle value is clockwise in gpcb and Pcbnew.
567  drawSeg->SetAngle( sweep_angle );
568  drawSeg->SetEnd0( wxPoint( radius, 0 ) );
569 
570  // Calculate start point coordinate of arc
571  wxPoint arcStart( drawSeg->GetEnd0() );
572  RotatePoint( &arcStart, -start_angle );
573  drawSeg->SetEnd0( centre + arcStart );
574  drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
575  drawSeg->SetDrawCoord();
576  continue;
577  }
578 
579  // Parse a Pad with no hole with format:
580  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
581  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
582  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
583  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
584  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
585  {
586  if( paramCnt < 10 || paramCnt > 13 )
587  {
588  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
589  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
590  aLineReader->LineNumber(), 0 );
591  }
592 
593  D_PAD* pad = new D_PAD( module.get() );
594 
595  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
596  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
597 
598  pad->SetShape( PAD_SHAPE_RECT );
599  pad->SetAttribute( PAD_ATTRIB_SMD );
600  pad->SetLayerSet( pad_front );
601 
602  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
603  pad->SetLayerSet( pad_back );
604 
605  // Set the pad name:
606  // Pcbnew pad name is used for electrical connection calculations.
607  // Accordingly it should be mapped to gEDA's pin/pad number,
608  // which is used for the same purpose.
609  // gEDA also features a pin/pad "name", which is an arbitrary string
610  // and set to the pin name of the netlist on instantiation. Many gEDA
611  // bare footprints use identical strings for name and number, so this
612  // can be a bit confusing.
613  pad->SetName( parameters[paramCnt-3] );
614 
615  int x1 = parseInt( parameters[2], conv_unit );
616  int x2 = parseInt( parameters[4], conv_unit );
617  int y1 = parseInt( parameters[3], conv_unit );
618  int y2 = parseInt( parameters[5], conv_unit );
619  int width = parseInt( parameters[6], conv_unit );
620  wxPoint delta( x2 - x1, y2 - y1 );
621  double angle = atan2( (double)delta.y, (double)delta.x );
622 
623  // Get the pad clearance and the solder mask clearance.
624  if( paramCnt == 13 )
625  {
626  int clearance = parseInt( parameters[7], conv_unit );
627  // One of gEDA's oddities is that clearance between pad and polygon
628  // is given as the gap on both sides of the pad together, so for
629  // KiCad it has to halfed.
630  pad->SetLocalClearance( clearance / 2 );
631 
632  // In GEDA, the mask value is the size of the hole in this
633  // solder mask. In Pcbnew, it is a margin, therefore the distance
634  // between the copper and the mask
635  int maskMargin = parseInt( parameters[8], conv_unit );
636  maskMargin = ( maskMargin - width ) / 2;
637  pad->SetLocalSolderMaskMargin( maskMargin );
638  }
639 
640  // Negate angle (due to Y reversed axis) and convert it to internal units
641  angle = - RAD2DECIDEG( angle );
642  pad->SetOrientation( KiROUND( angle ) );
643 
644  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
645 
646  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
647  width ) );
648 
649  padPos += module->GetPosition();
650  pad->SetPos0( padPos );
651  pad->SetPosition( padPos );
652 
653  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
654  {
655  if( pad->GetSize().x == pad->GetSize().y )
656  pad->SetShape( PAD_SHAPE_CIRCLE );
657  else
658  pad->SetShape( PAD_SHAPE_OVAL );
659  }
660 
661  module->Add( pad );
662  continue;
663  }
664 
665  // Parse a Pin with through hole with format:
666  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
667  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
668  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
669  // Pin (aX aY Thickness Drill "Name" NFlags)
670  // Pin (aX aY Thickness "Name" NFlags)
671  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
672  {
673  if( paramCnt < 8 || paramCnt > 12 )
674  {
675  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
676  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
677  aLineReader->LineNumber(), 0 );
678  }
679 
680  D_PAD* pad = new D_PAD( module.get() );
681 
682  pad->SetShape( PAD_SHAPE_CIRCLE );
683 
684  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
685 
686  pad->SetLayerSet( pad_set );
687 
688  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
689  pad->SetShape( PAD_SHAPE_RECT );
690 
691  // Set the pad name:
692  // Pcbnew pad name is used for electrical connection calculations.
693  // Accordingly it should be mapped to gEDA's pin/pad number,
694  // which is used for the same purpose.
695  pad->SetName( parameters[paramCnt-3] );
696 
697  wxPoint padPos( parseInt( parameters[2], conv_unit ),
698  parseInt( parameters[3], conv_unit ) );
699 
700  int padSize = parseInt( parameters[4], conv_unit );
701 
702  pad->SetSize( wxSize( padSize, padSize ) );
703 
704  int drillSize = 0;
705 
706  // Get the pad clearance, solder mask clearance, and drill size.
707  if( paramCnt == 12 )
708  {
709  int clearance = parseInt( parameters[5], conv_unit );
710  // One of gEDA's oddities is that clearance between pad and polygon
711  // is given as the gap on both sides of the pad together, so for
712  // KiCad it has to halfed.
713  pad->SetLocalClearance( clearance / 2 );
714 
715  // In GEDA, the mask value is the size of the hole in this
716  // solder mask. In Pcbnew, it is a margin, therefore the distance
717  // between the copper and the mask
718  int maskMargin = parseInt( parameters[6], conv_unit );
719  maskMargin = ( maskMargin - padSize ) / 2;
720  pad->SetLocalSolderMaskMargin( maskMargin );
721 
722  drillSize = parseInt( parameters[7], conv_unit );
723  }
724  else
725  {
726  drillSize = parseInt( parameters[5], conv_unit );
727  }
728 
729  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
730  padPos += module->GetPosition();
731  pad->SetPos0( padPos );
732  pad->SetPosition( padPos );
733 
734  if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
735  pad->SetShape( PAD_SHAPE_OVAL );
736 
737  module->Add( pad );
738  continue;
739  }
740  }
741 
742  // Recalculate the bounding box
743  module->CalculateBoundingBox();
744  return module.release();
745 }
746 
747 
748 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
749 {
750  char key;
751  wxString tmp;
752  char* line = aLineReader->Line();
753 
754  // Last line already ready in main parser loop.
755  while( *line != 0 )
756  {
757  key = *line;
758  line++;
759 
760  switch( key )
761  {
762  case '[':
763  case '(':
764  if( !tmp.IsEmpty() )
765  {
766  aParameterList.Add( tmp );
767  tmp.Clear();
768  }
769 
770  tmp.Append( key );
771  aParameterList.Add( tmp );
772  tmp.Clear();
773 
774  // Opening delimiter "(" after Element statement. Any other occurrence is part
775  // of a keyword definition.
776  if( aParameterList.GetCount() == 1 )
777  {
778  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
779  return;
780  }
781 
782  break;
783 
784  case ']':
785  case ')':
786  if( !tmp.IsEmpty() )
787  {
788  aParameterList.Add( tmp );
789  tmp.Clear();
790  }
791 
792  tmp.Append( key );
793  aParameterList.Add( tmp );
794  wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
795  return;
796 
797  case '\n':
798  case '\r':
799  // Element descriptions can span multiple lines.
800  line = aLineReader->ReadLine();
801 
802  // Fall through is intentional.
803 
804  case '\t':
805  case ' ':
806  if( !tmp.IsEmpty() )
807  {
808  aParameterList.Add( tmp );
809  tmp.Clear();
810  }
811 
812  break;
813 
814  case '"':
815  // Handle empty quotes.
816  if( *line == '"' )
817  {
818  line++;
819  tmp.Clear();
820  aParameterList.Add( wxEmptyString );
821  break;
822  }
823 
824  while( *line != 0 )
825  {
826  key = *line;
827  line++;
828 
829  if( key == '"' )
830  {
831  aParameterList.Add( tmp );
832  tmp.Clear();
833  break;
834  }
835  else
836  {
837  tmp.Append( key );
838  }
839  }
840 
841  break;
842 
843  case '#':
844  line = aLineReader->ReadLine();
845  break;
846 
847  default:
848  tmp.Append( key );
849  break;
850  }
851  }
852 }
853 
854 
855 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
856 {
857  wxString number;
858 
859  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
860  {
861  long lflags;
862 
863  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
864  return true;
865  }
866  else if( aFlag.Contains( aName ) )
867  {
868  return true;
869  }
870 
871  return false;
872 }
873 
874 
876  m_cache( 0 ),
877  m_ctl( 0 )
878 {
879  m_reader = NULL;
880  init( 0 );
881 }
882 
883 
884 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
885  m_cache( 0 ),
886  m_ctl( aControlFlags )
887 {
888  m_reader = NULL;
889  init( 0 );
890 }
891 
892 
894 {
895  delete m_cache;
896 }
897 
898 
899 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
900 {
901  m_props = aProperties;
902 }
903 
904 
905 void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
906 {
907  if( !m_cache || ( checkModified && m_cache->IsModified() ) )
908  {
909  // a spectacular episode in memory management:
910  delete m_cache;
911  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
912  m_cache->Load();
913  }
914 }
915 
916 
917 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
918  const wxString& aLibraryPath,
919  const PROPERTIES* aProperties )
920 {
921  LOCALE_IO toggle; // toggles on, then off, the C locale.
922  wxDir dir( aLibraryPath );
923 
924  if( !dir.IsOpened() )
925  {
926  THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
927  GetChars( aLibraryPath ) ) );
928  }
929 
930  init( aProperties );
931 
932  wxString errorMsg;
933 
934  // Some of the files may have been parsed correctly so we want to add the valid files to
935  // the library.
936  try
937  {
938  validateCache( aLibraryPath );
939  }
940  catch( const IO_ERROR& ioe )
941  {
942  errorMsg = ioe.What();
943  }
944 
945  const MODULE_MAP& mods = m_cache->GetModules();
946 
947  for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
948  {
949  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
950  }
951 
952  if( !errorMsg.IsEmpty() )
953  THROW_IO_ERROR( errorMsg );
954 }
955 
956 
957 MODULE* GPCB_PLUGIN::doLoadFootprint( const wxString& aLibraryPath,
958  const wxString& aFootprintName,
959  const PROPERTIES* aProperties,
960  bool checkModified )
961 {
962  LOCALE_IO toggle; // toggles on, then off, the C locale.
963 
964  init( aProperties );
965 
966  validateCache( aLibraryPath, checkModified );
967 
968  const MODULE_MAP& mods = m_cache->GetModules();
969 
970  MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
971 
972  if( it == mods.end() )
973  {
974  return NULL;
975  }
976 
977  // copy constructor to clone the already loaded MODULE
978  return new MODULE( *it->second->GetModule() );
979 }
980 
981 
982 MODULE* GPCB_PLUGIN::LoadEnumeratedFootprint( const wxString& aLibraryPath,
983  const wxString& aFootprintName,
984  const PROPERTIES* aProperties )
985 {
986  return doLoadFootprint( aLibraryPath, aFootprintName, aProperties, false );
987 }
988 
989 
990 MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
991  const PROPERTIES* aProperties )
992 {
993  return doLoadFootprint( aLibraryPath, aFootprintName, aProperties, true );
994 }
995 
996 
997 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
998  const PROPERTIES* aProperties )
999 {
1000  LOCALE_IO toggle; // toggles on, then off, the C locale.
1001 
1002  init( aProperties );
1003 
1004  validateCache( aLibraryPath );
1005 
1006  if( !m_cache->IsWritable() )
1007  {
1008  THROW_IO_ERROR( wxString::Format( _( "Library \"%s\" is read only" ),
1009  aLibraryPath.GetData() ) );
1010  }
1011 
1012  m_cache->Remove( aFootprintName );
1013 }
1014 
1015 
1016 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
1017 {
1018  wxFileName fn;
1019  fn.SetPath( aLibraryPath );
1020 
1021  // Return if there is no library path to delete.
1022  if( !fn.DirExists() )
1023  return false;
1024 
1025  if( !fn.IsDirWritable() )
1026  {
1027  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory \"%s\"" ),
1028  aLibraryPath.GetData() ) );
1029  }
1030 
1031  wxDir dir( aLibraryPath );
1032 
1033  if( dir.HasSubDirs() )
1034  {
1035  THROW_IO_ERROR( wxString::Format( _( "library directory \"%s\" has unexpected sub-directories" ),
1036  aLibraryPath.GetData() ) );
1037  }
1038 
1039  // All the footprint files must be deleted before the directory can be deleted.
1040  if( dir.HasFiles() )
1041  {
1042  unsigned i;
1043  wxFileName tmp;
1044  wxArrayString files;
1045 
1046  wxDir::GetAllFiles( aLibraryPath, &files );
1047 
1048  for( i = 0; i < files.GetCount(); i++ )
1049  {
1050  tmp = files[i];
1051 
1052  if( tmp.GetExt() != KiCadFootprintFileExtension )
1053  {
1054  THROW_IO_ERROR( wxString::Format( _( "unexpected file \"%s\" was found in library path \"%s\"" ),
1055  files[i].GetData(), aLibraryPath.GetData() ) );
1056  }
1057  }
1058 
1059  for( i = 0; i < files.GetCount(); i++ )
1060  {
1061  wxRemoveFile( files[i] );
1062  }
1063  }
1064 
1065  wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
1066  aLibraryPath.GetData() );
1067 
1068  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1069  // we don't want that. we want bare metal portability with no UI here.
1070  if( !wxRmdir( aLibraryPath ) )
1071  {
1072  THROW_IO_ERROR( wxString::Format( _( "footprint library \"%s\" cannot be deleted" ),
1073  aLibraryPath.GetData() ) );
1074  }
1075 
1076  // For some reason removing a directory in Windows is not immediately updated. This delay
1077  // prevents an error when attempting to immediately recreate the same directory when over
1078  // writing an existing library.
1079 #ifdef __WINDOWS__
1080  wxMilliSleep( 250L );
1081 #endif
1082 
1083  if( m_cache && m_cache->GetPath() == aLibraryPath )
1084  {
1085  delete m_cache;
1086  m_cache = NULL;
1087  }
1088 
1089  return true;
1090 }
1091 
1092 
1093 long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
1094 {
1095  // If we have no cache, return a number which won't match any stored timestamps
1096  if( !m_cache || !m_cache->IsPath( aLibraryPath ) )
1097  return wxDateTime::Now().GetValue().GetValue();
1098 
1099  return m_cache->GetTimestamp();
1100 }
1101 
1102 
1103 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1104 {
1105  LOCALE_IO toggle;
1106 
1107  init( NULL );
1108 
1109  validateCache( aLibraryPath );
1110 
1111  return m_cache->IsWritable();
1112 }
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:673
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
bool m_writable
Writability status of the footprint file.
GPCB_PLUGIN * m_owner
MODULE_MAP::const_iterator MODULE_CITER
virtual unsigned LineNumber() const
Function Line Number returns the line number of the last line read from this LINE_READER.
Definition: richio.h:159
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.
wxFileName m_file_name
The the full file name and path of the footprint to cache.
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:106
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown...
Definition: common.h:167
#define OLD_GPCB_UNIT_CONV
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.
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 wxString KiCadFootprintFileExtension
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:61
double RAD2DECIDEG(double rad)
Definition: trigo.h:204
Class GPCB_FPL_CACHE_ITEM is helper class for creating a footprint library cache. ...
wxString GetPath() const
void parseParameters(wxArrayString &aParameterList, LINE_READER *aLineReader)
Function parseParameters extracts parameters and tokens from aLineReader and adds them to aParameterL...
long long GetTimestamp()
Function GetTimestamp Generate a timestamp representing all source files in the cache (including the ...
usual segment : line with rounded ends
GPCB_FPL_CACHE_ITEM(MODULE *aModule, const wxFileName &aFileName)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:216
MODULE * GetModule() const
void Remove(const wxString &aFootprintName)
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
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:96
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.
#define THROW_PARSE_ERROR(aProblem, aSource, aInputLine, aLineNumber, aByteIndex)
Definition: ki_exception.h:162
MODULE * doLoadFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties, bool checkModified)
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.
char * Line() const
Function Line returns a pointer to the last line that was read in.
Definition: richio.h:139
#define NEW_GPCB_UNIT_CONV
Arcs (with rounded ends)
The common library.
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:95
wxFileName m_lib_path
Plugin object that owns the cache.
wxLogTrace helper definitions.
std::map< wxString, MODULE * > MODULE_MAP
Definition: eagle_plugin.h:36
MODULE_MAP m_modules
The path of the library.
void Load()
Save not implemented for the Geda PCB footprint library format.
wxString GetName() const
std::unique_ptr< MODULE > m_module
long long m_cache_timestamp
wxFileName GetFileName() const
const wxString GedaPcbFootprintLibFileExtension
MODULE * parseMODULE(LINE_READER *aLineReader)
bool IsWritable() const
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
MODULE_MAP::const_iterator MODULE_CITER
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
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 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, or if library does not exist returns false, or throws an exception if library exists but is read only or cannot be deleted for some other reason.
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
bool IsPath(const wxString &aPath) const
Function IsPath checks if aPath is the same as the current cache path.
MODULE * LoadEnumeratedFootprint(const wxString &aLibraryPath, const wxString &aFootprintName, const PROPERTIES *aProperties=NULL) override
Function LoadEnumeratedFootprint a version of FootprintLoad() for use after FootprintEnumerate() for ...
size_t i
Definition: json11.cpp:597
virtual const wxString & GetSource() const
Function GetSource returns the name of the source of the lines in an abstract sense.
Definition: richio.h:130
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory)...
The common library.
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:217
KICAD_PLUGIN_EXPORT SCENEGRAPH * Load(char const *aFileName)
reads a model file and creates a generic display structure
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:98
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. ...
EDGE_MODULE class definition.
bool m_cache_dirty
Map of footprint file name per MODULE*.
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
static long parseInt(const wxString &aValue, double aScalar)
Definition: gpcb_plugin.cpp:52