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-2017 Wayne Stambaugh <stambaughw@verizon.net>
5  * Copyright (C) 1992-2017 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 
37 #include <class_board.h>
38 #include <class_module.h>
39 #include <class_pcb_text.h>
40 #include <class_drawsegment.h>
41 #include <class_edge_mod.h>
42 #include <gpcb_plugin.h>
43 
44 #include <wx/dir.h>
45 #include <wx/filename.h>
46 #include <wx/wfstream.h>
47 #include <boost/ptr_container/ptr_map.hpp>
48 #include <memory.h>
49 
54 static const wxString traceFootprintLibrary( wxT( "GedaPcbFootprintLib" ) );
55 
56 #ifdef DEBUG
57 static void inline traceParams( wxArrayString& aParams )
58 {
59  wxString tmp;
60 
61  for( unsigned i = 0; i < aParams.GetCount(); i++ )
62  {
63  if( aParams[i].IsEmpty() )
64  tmp << wxT( "\"\" " );
65  else
66  tmp << aParams[i] << wxT( " " );
67  }
68 
69  wxLogTrace( traceFootprintLibrary, tmp );
70 }
71 #endif
72 
73 static inline long parseInt( const wxString& aValue, double aScalar )
74 {
75  double value = LONG_MAX;
76 
77  /*
78  * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
79  * Unit-less values are still centimils (100000 units per inch), like with
80  * the previous format.
81  *
82  * Distinction between the even older format (mils, 1000 units per inch)
83  * and the pre-2011 format is done in ::parseMODULE already; the
84  * distinction is by wether an object definition opens with '(' or '['.
85  * All values with explicite unit open with a '[' so there's no need to
86  * consider this distinction when parsing them.
87  *
88  * The solution here is to watch for a unit and, if present, convert the
89  * value to centimils. All unit-less values are read unaltered. This way
90  * the code below can contine to consider all read values to be in mils or
91  * centimils. It also matches the strategy gEDA/pcb uses for backwards
92  * compatibility with its own layouts.
93  *
94  * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
95  * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
96  * have to test for all 11 units gEDA/pcb allows in user dialogs.
97  */
98  if( aValue.EndsWith( wxT( "mm" ) ) )
99  {
100  aScalar *= 100000.0 / 25.4;
101  }
102  else if( aValue.EndsWith( wxT( "mil" ) ) )
103  {
104  aScalar *= 100.;
105  }
106 
107  // This conversion reports failure on strings as simple as "1000", still
108  // it returns the right result in &value. Thus, ignore the return value.
109  aValue.ToCDouble(&value);
110  if( value == LONG_MAX ) // conversion really failed
111  {
112  THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
113  aValue.GetData() ) );
114  return 0;
115  }
116 
117  return KiROUND( value * aScalar );
118 }
119 
120 
121 // Tracing for token parameter arrays.
122 #ifdef DEBUG
123 #define TRACE_PARAMS( arr ) traceParams( arr );
124 #else
125 #define TRACE_PARAMS( arr ) // Expands to nothing on non-debug builds.
126 #endif
127 
128 
139 {
140  wxFileName m_file_name;
141  bool m_writable;
142  wxDateTime m_mod_time;
143  std::unique_ptr<MODULE> m_module;
144 
145 public:
146  GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
147 
148  wxString GetName() const { return m_file_name.GetDirs().Last(); }
149  wxFileName GetFileName() const { return m_file_name; }
150  bool IsModified() const;
151  MODULE* GetModule() const { return m_module.get(); }
152  void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); }
153 };
154 
155 
156 GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
157  m_module( aModule )
158 {
159  m_file_name = aFileName;
160  m_writable = true; // temporary init
161 
162  if( m_file_name.FileExists() )
163  m_mod_time = m_file_name.GetModificationTime();
164  else
165  m_mod_time.Now();
166 }
167 
168 
170 {
171  if( !m_file_name.FileExists() )
172  return false;
173 
174  return m_file_name.GetModificationTime() != m_mod_time;
175 }
176 
177 
178 typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
179 typedef MODULE_MAP::iterator MODULE_ITER;
180 typedef MODULE_MAP::const_iterator MODULE_CITER;
181 
182 
184 {
186  wxFileName m_lib_path;
187  wxDateTime m_mod_time;
188  MODULE_MAP m_modules;
189 
190  MODULE* parseMODULE( LINE_READER* aLineReader );
191 
202  bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
203 
220  void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
221 
222 public:
223  GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
224 
225  wxString GetPath() const { return m_lib_path.GetPath(); }
226  wxDateTime GetLastModificationTime() const { return m_mod_time; }
227  bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
228  MODULE_MAP& GetModules() { return m_modules; }
229 
230  // Most all functions in this class throw IO_ERROR exceptions. There are no
231  // error codes nor user interface calls from here, nor in any PLUGIN.
232  // Catch these exceptions higher up please.
233 
235 
236  void Load();
237 
238  void Remove( const wxString& aFootprintName );
239 
240  wxDateTime GetLibModificationTime() const;
241 
253  bool IsModified( const wxString& aLibPath,
254  const wxString& aFootprintName = wxEmptyString ) const;
255 
268  bool IsPath( const wxString& aPath ) const;
269 };
270 
271 
272 GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
273 {
274  m_owner = aOwner;
275  m_lib_path.SetPath( aLibraryPath );
276 }
277 
278 
280 {
281  if( !m_lib_path.DirExists() )
282  return wxDateTime::Now();
283 
284  return m_lib_path.GetModificationTime();
285 }
286 
287 
289 {
290  // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
291  // and the footprints are the .fp files inside this folder.
292 
293  wxDir dir( m_lib_path.GetPath() );
294 
295  if( !dir.IsOpened() )
296  {
297  THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
298  m_lib_path.GetPath().GetData() ) );
299  }
300 
301  wxString fpFileName;
302  wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
303 
304  if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
305  return;
306 
307  wxString cacheErrorMsg;
308 
309  do
310  {
311  wxFileName fn( m_lib_path.GetPath(), fpFileName );
312 
313  // Queue I/O errors so only files that fail to parse don't get loaded.
314  try
315  {
316  // reader now owns fp, will close on exception or return
317  FILE_LINE_READER reader( fn.GetFullPath() );
318 
319  std::string name = TO_UTF8( fn.GetName() );
320  MODULE* footprint = parseMODULE( &reader );
321 
322  // The footprint name is the file name without the extension.
323  footprint->SetFPID( LIB_ID( fn.GetName() ) );
324  m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn.GetName() ) );
325  }
326  catch( const IO_ERROR& ioe )
327  {
328  if( !cacheErrorMsg.IsEmpty() )
329  cacheErrorMsg += "\n\n";
330 
331  cacheErrorMsg += ioe.What();
332  }
333  } while( dir.GetNext( &fpFileName ) );
334 
335  // Remember the file modification time of library file when the
336  // cache snapshot was made, so that in a networked environment we will
337  // reload the cache as needed.
339 
340  if( !cacheErrorMsg.IsEmpty() )
341  THROW_IO_ERROR( cacheErrorMsg );
342 }
343 
344 
345 void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
346 {
347  std::string footprintName = TO_UTF8( aFootprintName );
348 
349  MODULE_CITER it = m_modules.find( footprintName );
350 
351  if( it == m_modules.end() )
352  {
353  THROW_IO_ERROR( wxString::Format( _( "library <%s> has no footprint '%s' to delete" ),
354  m_lib_path.GetPath().GetData(),
355  aFootprintName.GetData() ) );
356  }
357 
358  // Remove the module from the cache and delete the module file from the library.
359  wxString fullPath = it->second->GetFileName().GetFullPath();
360  m_modules.erase( footprintName );
361  wxRemoveFile( fullPath );
362 }
363 
364 
365 bool GPCB_FPL_CACHE::IsPath( const wxString& aPath ) const
366 {
367  // Converts path separators to native path separators
368  wxFileName newPath;
369  newPath.AssignDir( aPath );
370 
371  return m_lib_path == newPath;
372 }
373 
374 
375 bool GPCB_FPL_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintName ) const
376 {
377  // The library is modified if the library path got deleted or changed.
378  if( !m_lib_path.DirExists() || !IsPath( aLibPath ) )
379  return true;
380 
381  // If no footprint was specified, check every file modification time against the time
382  // it was loaded.
383  if( aFootprintName.IsEmpty() )
384  {
385  for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
386  {
387  wxFileName fn = m_lib_path;
388 
389  fn.SetName( it->second->GetFileName().GetName() );
391 
392  if( !fn.FileExists() )
393  {
394  wxLogTrace( traceFootprintLibrary,
395  wxT( "Footprint cache file '%s' does not exist." ),
396  fn.GetFullPath().GetData() );
397  return true;
398  }
399 
400  if( it->second->IsModified() )
401  {
402  wxLogTrace( traceFootprintLibrary,
403  wxT( "Footprint cache file '%s' has been modified." ),
404  fn.GetFullPath().GetData() );
405  return true;
406  }
407  }
408  }
409  else
410  {
411  MODULE_CITER it = m_modules.find( TO_UTF8( aFootprintName ) );
412 
413  if( it == m_modules.end() || it->second->IsModified() )
414  return true;
415  }
416 
417  return false;
418 }
419 
420 
422 {
423  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
424  #define OLD_GPCB_UNIT_CONV IU_PER_MILS
425 
426  // Old version unit = 1 mil, so conv_unit is 10 or 0.1
427  #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
428 
429  int paramCnt;
430  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
431  wxPoint textPos;
432  wxString msg;
433  wxArrayString parameters;
434  std::unique_ptr<MODULE> module( new MODULE( NULL ) );
435 
436 
437  if( aLineReader->ReadLine() == NULL )
438  {
439  msg = aLineReader->GetSource() + ": empty file";
440  THROW_IO_ERROR( msg );
441  }
442 
443  parameters.Clear();
444  parseParameters( parameters, aLineReader );
445  paramCnt = parameters.GetCount();
446 
447  /* From the Geda PCB documentation, valid Element definitions:
448  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
449  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
450  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
451  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
452  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
453  */
454 
455  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
456  {
457  msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
458  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
459  aLineReader->LineNumber(), 0 );
460  }
461 
462  if( paramCnt < 10 || paramCnt > 14 )
463  {
464  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
465  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
466  aLineReader->LineNumber(), 0 );
467  }
468 
469  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
470  if( parameters[1] == wxT( "(" ) )
471  conv_unit = OLD_GPCB_UNIT_CONV;
472 
473  if( paramCnt > 10 )
474  {
475  module->SetDescription( parameters[3] );
476  module->SetReference( parameters[4] );
477  }
478  else
479  {
480  module->SetDescription( parameters[2] );
481  module->SetReference( parameters[3] );
482  }
483 
484  // Read value
485  if( paramCnt > 10 )
486  module->SetValue( parameters[5] );
487  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
488  // often empty in bare footprints.
489  if( module->Value().GetText().IsEmpty() )
490  module->Value().SetText( wxT( "Val**" ) );
491 
492 
493  if( paramCnt == 14 )
494  {
495  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
496  parseInt( parameters[9], conv_unit ) );
497  }
498  else
499  {
500  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
501  parseInt( parameters[7], conv_unit ) );
502  }
503 
504  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
505  module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
506 
507  // Calculate size: default height is 40 mils, width 30 mil.
508  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
509  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
510  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
511  int twsize = thsize * 30 / 40;
512  int thickness = thsize / 8;
513 
514  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
515  // Compensate for this by shifting the insertion point instead of the
516  // aligment, because alignment isn't changeable in the GUI.
517  textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
518  textPos.y += thsize / 2;
519 
520  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
521  // high/right. Compensate for similar appearance.
522  textPos.x -= thsize / 10;
523  textPos.y += thsize / 2;
524 
525  module->Reference().SetTextPos( textPos );
526  module->Reference().SetPos0( textPos );
527  module->Reference().SetTextSize( wxSize( twsize, thsize ) );
528  module->Reference().SetThickness( thickness );
529 
530  // gEDA/pcb shows only one of value/reference/description at a time. Which
531  // one is selectable by a global menu setting. pcbnew needs reference as
532  // well as value visible, so place the value right below the reference.
533  module->Value().SetTextAngle( module->Reference().GetTextAngle() );
534  module->Value().SetTextSize( module->Reference().GetTextSize() );
535  module->Value().SetThickness( module->Reference().GetThickness() );
536  textPos.y += thsize * 13 / 10; // 130% line height
537  module->Value().SetTextPos( textPos );
538  module->Value().SetPos0( textPos );
539 
540  while( aLineReader->ReadLine() )
541  {
542  parameters.Clear();
543  parseParameters( parameters, aLineReader );
544 
545  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
546  continue;
547 
548  if( parameters[0] == wxT( ")" ) )
549  break;
550 
551  paramCnt = parameters.GetCount();
552 
553  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
554  if( paramCnt > 3 )
555  {
556  if( parameters[1] == wxT( "(" ) )
557  conv_unit = OLD_GPCB_UNIT_CONV;
558  else
559  conv_unit = NEW_GPCB_UNIT_CONV;
560  }
561 
562  wxLogTrace( traceFootprintLibrary, wxT( "%s parameter count = %d." ),
563  GetChars( parameters[0] ), paramCnt );
564 
565  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
566  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
567  {
568  if( paramCnt != 8 )
569  {
570  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
571  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
572  aLineReader->LineNumber(), 0 );
573  }
574 
575  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
576  drawSeg->SetLayer( F_SilkS );
577  drawSeg->SetShape( S_SEGMENT );
578  drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
579  parseInt( parameters[3], conv_unit ) ) );
580  drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
581  parseInt( parameters[5], conv_unit ) ) );
582  drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
583  drawSeg->SetDrawCoord();
584  module->GraphicalItemsList().PushBack( drawSeg );
585  continue;
586  }
587 
588  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
589  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
590  {
591  if( paramCnt != 10 )
592  {
593  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
594  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
595  aLineReader->LineNumber(), 0 );
596  }
597 
598  // Pcbnew does know ellipse so we must have Width = Height
599  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
600  drawSeg->SetLayer( F_SilkS );
601  drawSeg->SetShape( S_ARC );
602  module->GraphicalItemsList().PushBack( drawSeg );
603 
604  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
605  int radius = ( parseInt( parameters[4], conv_unit ) +
606  parseInt( parameters[5], conv_unit ) ) / 2;
607 
608  wxPoint centre( parseInt( parameters[2], conv_unit ),
609  parseInt( parameters[3], conv_unit ) );
610 
611  drawSeg->SetStart0( centre );
612 
613  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
614  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
615 
616  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
617  double sweep_angle = parseInt( parameters[7], -10.0 );
618 
619  // Geda PCB does not support circles.
620  if( sweep_angle == -3600.0 )
621  drawSeg->SetShape( S_CIRCLE );
622 
623  // Angle value is clockwise in gpcb and Pcbnew.
624  drawSeg->SetAngle( sweep_angle );
625  drawSeg->SetEnd0( wxPoint( radius, 0 ) );
626 
627  // Calculate start point coordinate of arc
628  wxPoint arcStart( drawSeg->GetEnd0() );
629  RotatePoint( &arcStart, -start_angle );
630  drawSeg->SetEnd0( centre + arcStart );
631  drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
632  drawSeg->SetDrawCoord();
633  continue;
634  }
635 
636  // Parse a Pad with no hole with format:
637  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
638  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
639  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
640  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
641  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
642  {
643  if( paramCnt < 10 || paramCnt > 13 )
644  {
645  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
646  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
647  aLineReader->LineNumber(), 0 );
648  }
649 
650  D_PAD* pad = new D_PAD( module.get() );
651 
652  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
653  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
654 
655  pad->SetShape( PAD_SHAPE_RECT );
657  pad->SetLayerSet( pad_front );
658 
659  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
660  pad->SetLayerSet( pad_back );
661 
662  // Set the pad name:
663  // Pcbnew pad name is used for electrical connection calculations.
664  // Accordingly it should be mapped to gEDA's pin/pad number,
665  // which is used for the same purpose.
666  // gEDA also features a pin/pad "name", which is an arbitrary string
667  // and set to the pin name of the netlist on instantiation. Many gEDA
668  // bare footprints use identical strings for name and number, so this
669  // can be a bit confusing.
670  pad->SetPadName( parameters[paramCnt-3] );
671 
672  int x1 = parseInt( parameters[2], conv_unit );
673  int x2 = parseInt( parameters[4], conv_unit );
674  int y1 = parseInt( parameters[3], conv_unit );
675  int y2 = parseInt( parameters[5], conv_unit );
676  int width = parseInt( parameters[6], conv_unit );
677  wxPoint delta( x2 - x1, y2 - y1 );
678  double angle = atan2( (double)delta.y, (double)delta.x );
679 
680  // Get the pad clearance and the solder mask clearance.
681  if( paramCnt == 13 )
682  {
683  int clearance = parseInt( parameters[7], conv_unit );
684  // One of gEDA's oddities is that clearance between pad and polygon
685  // is given as the gap on both sides of the pad together, so for
686  // KiCad it has to halfed.
687  pad->SetLocalClearance( clearance / 2 );
688 
689  // In GEDA, the mask value is the size of the hole in this
690  // solder mask. In Pcbnew, it is a margin, therefore the distance
691  // between the copper and the mask
692  int maskMargin = parseInt( parameters[8], conv_unit );
693  maskMargin = ( maskMargin - width ) / 2;
694  pad->SetLocalSolderMaskMargin( maskMargin );
695  }
696 
697  // Negate angle (due to Y reversed axis) and convert it to internal units
698  angle = - RAD2DECIDEG( angle );
699  pad->SetOrientation( KiROUND( angle ) );
700 
701  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
702 
703  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
704  width ) );
705 
706  padPos += module->GetPosition();
707  pad->SetPos0( padPos );
708  pad->SetPosition( padPos );
709 
710  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
711  {
712  if( pad->GetSize().x == pad->GetSize().y )
713  pad->SetShape( PAD_SHAPE_CIRCLE );
714  else
715  pad->SetShape( PAD_SHAPE_OVAL );
716  }
717 
718  module->Add( pad );
719  continue;
720  }
721 
722  // Parse a Pin with through hole with format:
723  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
724  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
725  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
726  // Pin (aX aY Thickness Drill "Name" NFlags)
727  // Pin (aX aY Thickness "Name" NFlags)
728  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
729  {
730  if( paramCnt < 8 || paramCnt > 12 )
731  {
732  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
733  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
734  aLineReader->LineNumber(), 0 );
735  }
736 
737  D_PAD* pad = new D_PAD( module.get() );
738 
739  pad->SetShape( PAD_SHAPE_CIRCLE );
740 
741  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
742 
743  pad->SetLayerSet( pad_set );
744 
745  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
746  pad->SetShape( PAD_SHAPE_RECT );
747 
748  // Set the pad name:
749  // Pcbnew pad name is used for electrical connection calculations.
750  // Accordingly it should be mapped to gEDA's pin/pad number,
751  // which is used for the same purpose.
752  pad->SetPadName( parameters[paramCnt-3] );
753 
754  wxPoint padPos( parseInt( parameters[2], conv_unit ),
755  parseInt( parameters[3], conv_unit ) );
756 
757  int padSize = parseInt( parameters[4], conv_unit );
758 
759  pad->SetSize( wxSize( padSize, padSize ) );
760 
761  int drillSize = 0;
762 
763  // Get the pad clearance, solder mask clearance, and drill size.
764  if( paramCnt == 12 )
765  {
766  int clearance = parseInt( parameters[5], conv_unit );
767  // One of gEDA's oddities is that clearance between pad and polygon
768  // is given as the gap on both sides of the pad together, so for
769  // KiCad it has to halfed.
770  pad->SetLocalClearance( clearance / 2 );
771 
772  // In GEDA, the mask value is the size of the hole in this
773  // solder mask. In Pcbnew, it is a margin, therefore the distance
774  // between the copper and the mask
775  int maskMargin = parseInt( parameters[6], conv_unit );
776  maskMargin = ( maskMargin - padSize ) / 2;
777  pad->SetLocalSolderMaskMargin( maskMargin );
778 
779  drillSize = parseInt( parameters[7], conv_unit );
780  }
781  else
782  {
783  drillSize = parseInt( parameters[5], conv_unit );
784  }
785 
786  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
787  padPos += module->GetPosition();
788  pad->SetPos0( padPos );
789  pad->SetPosition( padPos );
790 
791  if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
792  pad->SetShape( PAD_SHAPE_OVAL );
793 
794  module->Add( pad );
795  continue;
796  }
797  }
798 
799  // Recalculate the bounding box
800  module->CalculateBoundingBox();
801  return module.release();
802 }
803 
804 
805 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
806 {
807  char key;
808  wxString tmp;
809  char* line = aLineReader->Line();
810 
811  // Last line already ready in main parser loop.
812  while( *line != 0 )
813  {
814  key = *line;
815  line++;
816 
817  switch( key )
818  {
819  case '[':
820  case '(':
821  if( !tmp.IsEmpty() )
822  {
823  aParameterList.Add( tmp );
824  tmp.Clear();
825  }
826 
827  tmp.Append( key );
828  aParameterList.Add( tmp );
829  tmp.Clear();
830 
831  // Opening delimiter "(" after Element statement. Any other occurrence is part
832  // of a keyword definition.
833  if( aParameterList.GetCount() == 1 )
834  {
835  TRACE_PARAMS( aParameterList );
836  return;
837  }
838 
839  break;
840 
841  case ']':
842  case ')':
843  if( !tmp.IsEmpty() )
844  {
845  aParameterList.Add( tmp );
846  tmp.Clear();
847  }
848 
849  tmp.Append( key );
850  aParameterList.Add( tmp );
851  TRACE_PARAMS( aParameterList );
852  return;
853 
854  case '\n':
855  case '\r':
856  // Element descriptions can span multiple lines.
857  line = aLineReader->ReadLine();
858 
859  // Fall through is intentional.
860 
861  case '\t':
862  case ' ':
863  if( !tmp.IsEmpty() )
864  {
865  aParameterList.Add( tmp );
866  tmp.Clear();
867  }
868 
869  break;
870 
871  case '"':
872  // Handle empty quotes.
873  if( *line == '"' )
874  {
875  line++;
876  tmp.Clear();
877  aParameterList.Add( wxEmptyString );
878  break;
879  }
880 
881  while( *line != 0 )
882  {
883  key = *line;
884  line++;
885 
886  if( key == '"' )
887  {
888  aParameterList.Add( tmp );
889  tmp.Clear();
890  break;
891  }
892  else
893  {
894  tmp.Append( key );
895  }
896  }
897 
898  break;
899 
900  case '#':
901  line = aLineReader->ReadLine();
902  break;
903 
904  default:
905  tmp.Append( key );
906  break;
907  }
908  }
909 }
910 
911 
912 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
913 {
914  wxString number;
915 
916  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
917  {
918  long lflags;
919 
920  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
921  return true;
922  }
923  else if( aFlag.Contains( aName ) )
924  {
925  return true;
926  }
927 
928  return false;
929 }
930 
931 
933  m_cache( 0 ),
934  m_ctl( 0 )
935 {
936  m_reader = NULL;
937  init( 0 );
938 }
939 
940 
941 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
942  m_cache( 0 ),
943  m_ctl( aControlFlags )
944 {
945  m_reader = NULL;
946  init( 0 );
947 }
948 
949 
951 {
952  delete m_cache;
953 }
954 
955 
956 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
957 {
958  m_props = aProperties;
959 }
960 
961 
962 void GPCB_PLUGIN::cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName )
963 {
964  if( !m_cache || m_cache->IsModified( aLibraryPath, aFootprintName ) )
965  {
966  // a spectacular episode in memory management:
967  delete m_cache;
968  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
969  m_cache->Load();
970  }
971 }
972 
973 
974 void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
975  const wxString& aLibraryPath,
976  const PROPERTIES* aProperties )
977 {
978  LOCALE_IO toggle; // toggles on, then off, the C locale.
979  wxDir dir( aLibraryPath );
980 
981  if( !dir.IsOpened() )
982  {
983  THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
984  GetChars( aLibraryPath ) ) );
985  }
986 
987  init( aProperties );
988 
989  wxString errorMsg;
990 
991  // Some of the files may have been parsed correctly so we want to add the valid files to
992  // the library.
993  try
994  {
995  cacheLib( aLibraryPath );
996  }
997  catch( const IO_ERROR& ioe )
998  {
999  errorMsg = ioe.What();
1000  }
1001 
1002  const MODULE_MAP& mods = m_cache->GetModules();
1003 
1004  for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
1005  {
1006  aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
1007  }
1008 
1009  if( !errorMsg.IsEmpty() )
1010  THROW_IO_ERROR( errorMsg );
1011 }
1012 
1013 
1014 MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
1015  const PROPERTIES* aProperties )
1016 {
1017  LOCALE_IO toggle; // toggles on, then off, the C locale.
1018 
1019  init( aProperties );
1020 
1021  cacheLib( aLibraryPath, aFootprintName );
1022 
1023  const MODULE_MAP& mods = m_cache->GetModules();
1024 
1025  MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
1026 
1027  if( it == mods.end() )
1028  {
1029  return NULL;
1030  }
1031 
1032  // copy constructor to clone the already loaded MODULE
1033  return new MODULE( *it->second->GetModule() );
1034 }
1035 
1036 
1037 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
1038  const PROPERTIES* aProperties )
1039 {
1040  LOCALE_IO toggle; // toggles on, then off, the C locale.
1041 
1042  init( aProperties );
1043 
1044  cacheLib( aLibraryPath );
1045 
1046  if( !m_cache->IsWritable() )
1047  {
1048  THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ),
1049  aLibraryPath.GetData() ) );
1050  }
1051 
1052  m_cache->Remove( aFootprintName );
1053 }
1054 
1055 
1056 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
1057 {
1058  wxFileName fn;
1059  fn.SetPath( aLibraryPath );
1060 
1061  // Return if there is no library path to delete.
1062  if( !fn.DirExists() )
1063  return false;
1064 
1065  if( !fn.IsDirWritable() )
1066  {
1067  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ),
1068  aLibraryPath.GetData() ) );
1069  }
1070 
1071  wxDir dir( aLibraryPath );
1072 
1073  if( dir.HasSubDirs() )
1074  {
1075  THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ),
1076  aLibraryPath.GetData() ) );
1077  }
1078 
1079  // All the footprint files must be deleted before the directory can be deleted.
1080  if( dir.HasFiles() )
1081  {
1082  unsigned i;
1083  wxFileName tmp;
1084  wxArrayString files;
1085 
1086  wxDir::GetAllFiles( aLibraryPath, &files );
1087 
1088  for( i = 0; i < files.GetCount(); i++ )
1089  {
1090  tmp = files[i];
1091 
1092  if( tmp.GetExt() != KiCadFootprintFileExtension )
1093  {
1094  THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' was found in library path '%s'" ),
1095  files[i].GetData(), aLibraryPath.GetData() ) );
1096  }
1097  }
1098 
1099  for( i = 0; i < files.GetCount(); i++ )
1100  {
1101  wxRemoveFile( files[i] );
1102  }
1103  }
1104 
1105  wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library '%s'" ),
1106  aLibraryPath.GetData() );
1107 
1108  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1109  // we don't want that. we want bare metal portability with no UI here.
1110  if( !wxRmdir( aLibraryPath ) )
1111  {
1112  THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ),
1113  aLibraryPath.GetData() ) );
1114  }
1115 
1116  // For some reason removing a directory in Windows is not immediately updated. This delay
1117  // prevents an error when attempting to immediately recreate the same directory when over
1118  // writing an existing library.
1119 #ifdef __WINDOWS__
1120  wxMilliSleep( 250L );
1121 #endif
1122 
1123  if( m_cache && m_cache->GetPath() == aLibraryPath )
1124  {
1125  delete m_cache;
1126  m_cache = NULL;
1127  }
1128 
1129  return true;
1130 }
1131 
1132 
1133 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1134 {
1135  LOCALE_IO toggle;
1136 
1137  init( NULL );
1138 
1139  cacheLib( aLibraryPath );
1140 
1141  return m_cache->IsWritable();
1142 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:104
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:639
void SetEnd0(const wxPoint &aPoint)
bool IsModified(const wxString &aLibPath, const wxString &aFootprintName=wxEmptyString) const
Function IsModified check if the footprint cache has been modified relative to aLibPath and aFootprin...
const wxString KiCadFootprintFileExtension
MODULE_MAP::iterator MODULE_ITER
wxDateTime m_mod_time
The last file modified time stamp.
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.
void SetShape(STROKE_T aShape)
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)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
Class LOCALE_IO is a class that can be instantiated within a scope in which you are expecting excepti...
Definition: common.h:166
#define OLD_GPCB_UNIT_CONV
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
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 wxPoint & GetEnd0() const
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:59
double RAD2DECIDEG(double rad)
Definition: trigo.h:196
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:169
Class GPCB_FPL_CACHE_ITEM is helper class for creating a footprint library cache. ...
void cacheLib(const wxString &aLibraryPath, const wxString &aFootprintName=wxEmptyString)
we only cache one footprint library for now, this determines which one.
wxString GetPath() const
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:187
GPCB_FPL_CACHE_ITEM(MODULE *aModule, const wxFileName &aFileName)
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
MODULE * GetModule() const
void Remove(const wxString &aFootprintName)
Class LIB_ID.
Definition: lib_id.h:56
Class PROPERTIES is a name/value tuple with unique names and optional values.
Definition: properties.h:34
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:166
static const wxString traceFootprintLibrary(wxT("GedaPcbFootprintLib"))
Definition for enabling and disabling footprint library trace output.
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:91
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:133
wxDateTime m_mod_time
The path of the library.
const wxString GedaPcbFootprintLibFileExtension
void SetPos0(const wxPoint &aPos)
Definition: class_pad.h:175
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.
const wxSize & GetSize() const
Definition: class_pad.h:182
void SetSize(const wxSize &aSize)
Definition: class_pad.h:181
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:90
wxFileName m_lib_path
Plugin object that owns the cache.
#define TRACE_PARAMS(arr)
#define THROW_IO_ERROR(x)
Definition: utf8.cpp:60
MODULE_MAP m_modules
Footprint library path modified time stamp.
void Load()
Save not implemented for the Geda PCB footprint library format.
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:348
wxString GetName() const
void SetLocalClearance(int aClearance)
Definition: class_pad.h:247
wxDateTime GetLibModificationTime() const
std::unique_ptr< MODULE > m_module
wxFileName GetFileName() const
MODULE * parseMODULE(LINE_READER *aLineReader)
Map of footprint file name per MODULE*.
bool IsWritable() const
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:234
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:33
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.
#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:244
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.
virtual const wxString & GetSource() const
Function GetSource returns the name of the source of the lines in an abstract sense.
Definition: richio.h:130
void SetDrawCoord()
Set draw coordinates (absolute values ) from relative coordinates.
void SetStart0(const wxPoint &aPoint)
The common library.
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:167
void SetPadName(const wxString &name)
Set the pad name (sometimes called pad number, although it can be an array ref like AA12 the pad name...
Definition: class_pad.cpp:455
const char * name
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:357
void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees...
friend class GPCB_FPL_CACHE
Definition: gpcb_plugin.h:49
LINE_READER * m_reader
no ownership here.
Definition: gpcb_plugin.h:93
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. ...
bool IsModified() const
EDGE_MODULE class definition.
wxDateTime GetLastModificationTime() const
Struct IO_ERROR is a class used to hold an error message and may be used when throwing exceptions con...
Definition: ki_exception.h:47
static long parseInt(const wxString &aValue, double aScalar)
Definition: gpcb_plugin.cpp:73
void SetWidth(int aWidth)