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-2016 Wayne Stambaugh <stambaughw@verizon.net>
5  * Copyright (C) 1992-2016 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 ) throw( IO_ERROR, PARSE_ERROR );
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";
330 
331  cacheErrorMsg += ioe.What();
332  }
333 
334  } while( dir.GetNext( &fpFileName ) );
335 
336  // Remember the file modification time of library file when the
337  // cache snapshot was made, so that in a networked environment we will
338  // reload the cache as needed.
339  m_mod_time = GetLibModificationTime();
340 
341 #if 0
342  if( !cacheErrorMsg.IsEmpty() )
343  THROW_IO_ERROR( cacheErrorMsg );
344 #endif
345 }
346 
347 
348 void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
349 {
350  std::string footprintName = TO_UTF8( aFootprintName );
351 
352  MODULE_CITER it = m_modules.find( footprintName );
353 
354  if( it == m_modules.end() )
355  {
356  THROW_IO_ERROR( wxString::Format( _( "library <%s> has no footprint '%s' to delete" ),
357  m_lib_path.GetPath().GetData(),
358  aFootprintName.GetData() ) );
359  }
360 
361  // Remove the module from the cache and delete the module file from the library.
362  wxString fullPath = it->second->GetFileName().GetFullPath();
363  m_modules.erase( footprintName );
364  wxRemoveFile( fullPath );
365 }
366 
367 
368 bool GPCB_FPL_CACHE::IsPath( const wxString& aPath ) const
369 {
370  // Converts path separators to native path separators
371  wxFileName newPath;
372  newPath.AssignDir( aPath );
373 
374  return m_lib_path == newPath;
375 }
376 
377 
378 bool GPCB_FPL_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintName ) const
379 {
380  // The library is modified if the library path got deleted or changed.
381  if( !m_lib_path.DirExists() || !IsPath( aLibPath ) )
382  return true;
383 
384  // If no footprint was specified, check every file modification time against the time
385  // it was loaded.
386  if( aFootprintName.IsEmpty() )
387  {
388  for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
389  {
390  wxFileName fn = m_lib_path;
391 
392  fn.SetName( it->second->GetFileName().GetName() );
393  fn.SetExt( KiCadFootprintFileExtension );
394 
395  if( !fn.FileExists() )
396  {
397  wxLogTrace( traceFootprintLibrary,
398  wxT( "Footprint cache file '%s' does not exist." ),
399  fn.GetFullPath().GetData() );
400  return true;
401  }
402 
403  if( it->second->IsModified() )
404  {
405  wxLogTrace( traceFootprintLibrary,
406  wxT( "Footprint cache file '%s' has been modified." ),
407  fn.GetFullPath().GetData() );
408  return true;
409  }
410  }
411  }
412  else
413  {
414  MODULE_CITER it = m_modules.find( TO_UTF8( aFootprintName ) );
415 
416  if( it == m_modules.end() || it->second->IsModified() )
417  return true;
418  }
419 
420  return false;
421 }
422 
423 
425 {
426  #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
427  #define OLD_GPCB_UNIT_CONV IU_PER_MILS
428 
429  // Old version unit = 1 mil, so conv_unit is 10 or 0.1
430  #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
431 
432  int paramCnt;
433  double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
434  wxPoint textPos;
435  wxString msg;
436  wxArrayString parameters;
437  std::unique_ptr<MODULE> module( new MODULE( NULL ) );
438 
439 
440  if( aLineReader->ReadLine() == NULL )
441  {
442  msg = aLineReader->GetSource() + ": empty file";
443  THROW_IO_ERROR( msg );
444  }
445 
446  parameters.Clear();
447  parseParameters( parameters, aLineReader );
448  paramCnt = parameters.GetCount();
449 
450  /* From the Geda PCB documentation, valid Element definitions:
451  * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
452  * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
453  * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
454  * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
455  * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
456  */
457 
458  if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
459  {
460  msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
461  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
462  aLineReader->LineNumber(), 0 );
463  }
464 
465  if( paramCnt < 10 || paramCnt > 14 )
466  {
467  msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
468  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
469  aLineReader->LineNumber(), 0 );
470  }
471 
472  // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
473  if( parameters[1] == wxT( "(" ) )
474  conv_unit = OLD_GPCB_UNIT_CONV;
475 
476  if( paramCnt > 10 )
477  {
478  module->SetDescription( parameters[3] );
479  module->SetReference( parameters[4] );
480  }
481  else
482  {
483  module->SetDescription( parameters[2] );
484  module->SetReference( parameters[3] );
485  }
486 
487  // Read value
488  if( paramCnt > 10 )
489  module->SetValue( parameters[5] );
490  // With gEDA/pcb, value is meaningful after instantiation, only, so it's
491  // often empty in bare footprints.
492  if( module->Value().GetText().IsEmpty() )
493  module->Value().SetText( wxT( "Val**" ) );
494 
495 
496  if( paramCnt == 14 )
497  {
498  textPos = wxPoint( parseInt( parameters[8], conv_unit ),
499  parseInt( parameters[9], conv_unit ) );
500  }
501  else
502  {
503  textPos = wxPoint( parseInt( parameters[6], conv_unit ),
504  parseInt( parameters[7], conv_unit ) );
505  }
506 
507  int orientation = parseInt( parameters[paramCnt-4], 1.0 );
508  module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
509 
510  // Calculate size: default height is 40 mils, width 30 mil.
511  // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
512  int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
513  thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
514  int twsize = thsize * 30 / 40;
515  int thickness = thsize / 8;
516 
517  // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
518  // Compensate for this by shifting the insertion point instead of the
519  // aligment, because alignment isn't changeable in the GUI.
520  textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
521  textPos.y += thsize / 2;
522 
523  // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
524  // high/right. Compensate for similar appearance.
525  textPos.x -= thsize / 10;
526  textPos.y += thsize / 2;
527 
528  module->Reference().SetTextPos( textPos );
529  module->Reference().SetPos0( textPos );
530  module->Reference().SetTextSize( wxSize( twsize, thsize ) );
531  module->Reference().SetThickness( thickness );
532 
533  // gEDA/pcb shows only one of value/reference/description at a time. Which
534  // one is selectable by a global menu setting. pcbnew needs reference as
535  // well as value visible, so place the value right below the reference.
536  module->Value().SetTextAngle( module->Reference().GetTextAngle() );
537  module->Value().SetTextSize( module->Reference().GetTextSize() );
538  module->Value().SetThickness( module->Reference().GetThickness() );
539  textPos.y += thsize * 13 / 10; // 130% line height
540  module->Value().SetTextPos( textPos );
541  module->Value().SetPos0( textPos );
542 
543  while( aLineReader->ReadLine() )
544  {
545  parameters.Clear();
546  parseParameters( parameters, aLineReader );
547 
548  if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
549  continue;
550 
551  if( parameters[0] == wxT( ")" ) )
552  break;
553 
554  paramCnt = parameters.GetCount();
555 
556  // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
557  if( paramCnt > 3 )
558  {
559  if( parameters[1] == wxT( "(" ) )
560  conv_unit = OLD_GPCB_UNIT_CONV;
561  else
562  conv_unit = NEW_GPCB_UNIT_CONV;
563  }
564 
565  wxLogTrace( traceFootprintLibrary, wxT( "%s parameter count = %d." ),
566  GetChars( parameters[0] ), paramCnt );
567 
568  // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
569  if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
570  {
571  if( paramCnt != 8 )
572  {
573  msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
574  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
575  aLineReader->LineNumber(), 0 );
576  }
577 
578  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
579  drawSeg->SetLayer( F_SilkS );
580  drawSeg->SetShape( S_SEGMENT );
581  drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
582  parseInt( parameters[3], conv_unit ) ) );
583  drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
584  parseInt( parameters[5], conv_unit ) ) );
585  drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
586  drawSeg->SetDrawCoord();
587  module->GraphicalItems().PushBack( drawSeg );
588  continue;
589  }
590 
591  // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
592  if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
593  {
594  if( paramCnt != 10 )
595  {
596  msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
597  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
598  aLineReader->LineNumber(), 0 );
599  }
600 
601  // Pcbnew does know ellipse so we must have Width = Height
602  EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
603  drawSeg->SetLayer( F_SilkS );
604  drawSeg->SetShape( S_ARC );
605  module->GraphicalItems().PushBack( drawSeg );
606 
607  // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
608  int radius = ( parseInt( parameters[4], conv_unit ) +
609  parseInt( parameters[5], conv_unit ) ) / 2;
610 
611  wxPoint centre( parseInt( parameters[2], conv_unit ),
612  parseInt( parameters[3], conv_unit ) );
613 
614  drawSeg->SetStart0( centre );
615 
616  // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
617  double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
618 
619  // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
620  double sweep_angle = parseInt( parameters[7], -10.0 );
621 
622  // Geda PCB does not support circles.
623  if( sweep_angle == -3600.0 )
624  drawSeg->SetShape( S_CIRCLE );
625 
626  // Angle value is clockwise in gpcb and Pcbnew.
627  drawSeg->SetAngle( sweep_angle );
628  drawSeg->SetEnd0( wxPoint( radius, 0 ) );
629 
630  // Calculate start point coordinate of arc
631  wxPoint arcStart( drawSeg->GetEnd0() );
632  RotatePoint( &arcStart, -start_angle );
633  drawSeg->SetEnd0( centre + arcStart );
634  drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
635  drawSeg->SetDrawCoord();
636  continue;
637  }
638 
639  // Parse a Pad with no hole with format:
640  // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
641  // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
642  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
643  // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
644  if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
645  {
646  if( paramCnt < 10 || paramCnt > 13 )
647  {
648  msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
649  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
650  aLineReader->LineNumber(), 0 );
651  }
652 
653  D_PAD* pad = new D_PAD( module.get() );
654 
655  static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
656  static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
657 
658  pad->SetShape( PAD_SHAPE_RECT );
660  pad->SetLayerSet( pad_front );
661 
662  if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
663  pad->SetLayerSet( pad_back );
664 
665  // Set the pad name:
666  // Pcbnew pad name is used for electrical connection calculations.
667  // Accordingly it should be mapped to gEDA's pin/pad number,
668  // which is used for the same purpose.
669  // gEDA also features a pin/pad "name", which is an arbitrary string
670  // and set to the pin name of the netlist on instantiation. Many gEDA
671  // bare footprints use identical strings for name and number, so this
672  // can be a bit confusing.
673  pad->SetPadName( parameters[paramCnt-3] );
674 
675  int x1 = parseInt( parameters[2], conv_unit );
676  int x2 = parseInt( parameters[4], conv_unit );
677  int y1 = parseInt( parameters[3], conv_unit );
678  int y2 = parseInt( parameters[5], conv_unit );
679  int width = parseInt( parameters[6], conv_unit );
680  wxPoint delta( x2 - x1, y2 - y1 );
681  double angle = atan2( (double)delta.y, (double)delta.x );
682 
683  // Get the pad clearance and the solder mask clearance.
684  if( paramCnt == 13 )
685  {
686  int clearance = parseInt( parameters[7], conv_unit );
687  // One of gEDA's oddities is that clearance between pad and polygon
688  // is given as the gap on both sides of the pad together, so for
689  // KiCad it has to halfed.
690  pad->SetLocalClearance( clearance / 2 );
691 
692  // In GEDA, the mask value is the size of the hole in this
693  // solder mask. In Pcbnew, it is a margin, therefore the distance
694  // between the copper and the mask
695  int maskMargin = parseInt( parameters[8], conv_unit );
696  maskMargin = ( maskMargin - width ) / 2;
697  pad->SetLocalSolderMaskMargin( maskMargin );
698  }
699 
700  // Negate angle (due to Y reversed axis) and convert it to internal units
701  angle = - RAD2DECIDEG( angle );
702  pad->SetOrientation( KiROUND( angle ) );
703 
704  wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
705 
706  pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
707  width ) );
708 
709  padPos += module->GetPosition();
710  pad->SetPos0( padPos );
711  pad->SetPosition( padPos );
712 
713  if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
714  {
715  if( pad->GetSize().x == pad->GetSize().y )
716  pad->SetShape( PAD_SHAPE_CIRCLE );
717  else
718  pad->SetShape( PAD_SHAPE_OVAL );
719  }
720 
721  module->Add( pad );
722  continue;
723  }
724 
725  // Parse a Pin with through hole with format:
726  // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
727  // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
728  // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
729  // Pin (aX aY Thickness Drill "Name" NFlags)
730  // Pin (aX aY Thickness "Name" NFlags)
731  if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
732  {
733  if( paramCnt < 8 || paramCnt > 12 )
734  {
735  msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
736  THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
737  aLineReader->LineNumber(), 0 );
738  }
739 
740  D_PAD* pad = new D_PAD( module.get() );
741 
742  pad->SetShape( PAD_SHAPE_CIRCLE );
743 
744  static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
745 
746  pad->SetLayerSet( pad_set );
747 
748  if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
749  pad->SetShape( PAD_SHAPE_RECT );
750 
751  // Set the pad name:
752  // Pcbnew pad name is used for electrical connection calculations.
753  // Accordingly it should be mapped to gEDA's pin/pad number,
754  // which is used for the same purpose.
755  pad->SetPadName( parameters[paramCnt-3] );
756 
757  wxPoint padPos( parseInt( parameters[2], conv_unit ),
758  parseInt( parameters[3], conv_unit ) );
759 
760  int padSize = parseInt( parameters[4], conv_unit );
761 
762  pad->SetSize( wxSize( padSize, padSize ) );
763 
764  int drillSize = 0;
765 
766  // Get the pad clearance, solder mask clearance, and drill size.
767  if( paramCnt == 12 )
768  {
769  int clearance = parseInt( parameters[5], conv_unit );
770  // One of gEDA's oddities is that clearance between pad and polygon
771  // is given as the gap on both sides of the pad together, so for
772  // KiCad it has to halfed.
773  pad->SetLocalClearance( clearance / 2 );
774 
775  // In GEDA, the mask value is the size of the hole in this
776  // solder mask. In Pcbnew, it is a margin, therefore the distance
777  // between the copper and the mask
778  int maskMargin = parseInt( parameters[6], conv_unit );
779  maskMargin = ( maskMargin - padSize ) / 2;
780  pad->SetLocalSolderMaskMargin( maskMargin );
781 
782  drillSize = parseInt( parameters[7], conv_unit );
783  }
784  else
785  {
786  drillSize = parseInt( parameters[5], conv_unit );
787  }
788 
789  pad->SetDrillSize( wxSize( drillSize, drillSize ) );
790  padPos += module->GetPosition();
791  pad->SetPos0( padPos );
792  pad->SetPosition( padPos );
793 
794  if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
795  pad->SetShape( PAD_SHAPE_OVAL );
796 
797  module->Add( pad );
798  continue;
799  }
800  }
801 
802  // Recalculate the bounding box
803  module->CalculateBoundingBox();
804  return module.release();
805 }
806 
807 
808 void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
809 {
810  char key;
811  wxString tmp;
812  char* line = aLineReader->Line();
813 
814  // Last line already ready in main parser loop.
815  while( *line != 0 )
816  {
817  key = *line;
818  line++;
819 
820  switch( key )
821  {
822  case '[':
823  case '(':
824  if( !tmp.IsEmpty() )
825  {
826  aParameterList.Add( tmp );
827  tmp.Clear();
828  }
829 
830  tmp.Append( key );
831  aParameterList.Add( tmp );
832  tmp.Clear();
833 
834  // Opening delimiter "(" after Element statement. Any other occurrence is part
835  // of a keyword definition.
836  if( aParameterList.GetCount() == 1 )
837  {
838  TRACE_PARAMS( aParameterList );
839  return;
840  }
841 
842  break;
843 
844  case ']':
845  case ')':
846  if( !tmp.IsEmpty() )
847  {
848  aParameterList.Add( tmp );
849  tmp.Clear();
850  }
851 
852  tmp.Append( key );
853  aParameterList.Add( tmp );
854  TRACE_PARAMS( aParameterList );
855  return;
856 
857  case '\n':
858  case '\r':
859  // Element descriptions can span multiple lines.
860  line = aLineReader->ReadLine();
861 
862  // Fall through is intentional.
863 
864  case '\t':
865  case ' ':
866  if( !tmp.IsEmpty() )
867  {
868  aParameterList.Add( tmp );
869  tmp.Clear();
870  }
871 
872  break;
873 
874  case '"':
875  // Handle empty quotes.
876  if( *line == '"' )
877  {
878  line++;
879  tmp.Clear();
880  aParameterList.Add( wxEmptyString );
881  break;
882  }
883 
884  while( *line != 0 )
885  {
886  key = *line;
887  line++;
888 
889  if( key == '"' )
890  {
891  aParameterList.Add( tmp );
892  tmp.Clear();
893  break;
894  }
895  else
896  {
897  tmp.Append( key );
898  }
899  }
900 
901  break;
902 
903  case '#':
904  line = aLineReader->ReadLine();
905  break;
906 
907  default:
908  tmp.Append( key );
909  break;
910  }
911  }
912 }
913 
914 
915 bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
916 {
917  wxString number;
918 
919  if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
920  {
921  long lflags;
922 
923  if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
924  return true;
925  }
926  else if( aFlag.Contains( aName ) )
927  {
928  return true;
929  }
930 
931  return false;
932 }
933 
934 
936  m_cache( 0 ),
937  m_ctl( 0 )
938 {
939  m_reader = NULL;
940  init( 0 );
941 }
942 
943 
944 GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
945  m_cache( 0 ),
946  m_ctl( aControlFlags )
947 {
948  m_reader = NULL;
949  init( 0 );
950 }
951 
952 
954 {
955  delete m_cache;
956 }
957 
958 
959 void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
960 {
961  m_props = aProperties;
962 }
963 
964 
965 void GPCB_PLUGIN::cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName )
966 {
967  if( !m_cache || m_cache->IsModified( aLibraryPath, aFootprintName ) )
968  {
969  // a spectacular episode in memory management:
970  delete m_cache;
971  m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
972  m_cache->Load();
973  }
974 }
975 
976 
977 wxArrayString GPCB_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath,
978  const PROPERTIES* aProperties )
979 {
980  LOCALE_IO toggle; // toggles on, then off, the C locale.
981  wxArrayString ret;
982  wxDir dir( aLibraryPath );
983 
984  if( !dir.IsOpened() )
985  {
986  THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
987  GetChars( aLibraryPath ) ) );
988  }
989 
990  init( aProperties );
991 
992 #if 1 // Set to 0 to only read directory contents, not load cache.
993 
994  cacheLib( aLibraryPath );
995 
996  const MODULE_MAP& mods = m_cache->GetModules();
997 
998 
999  for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
1000  {
1001  ret.Add( FROM_UTF8( it->first.c_str() ) );
1002  }
1003 #else
1004  wxString fpFileName;
1005  wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
1006 
1007  if( dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
1008  {
1009  do
1010  {
1011  wxFileName fn( aLibraryPath, fpFileName );
1012  ret.Add( fn.GetName() );
1013  } while( dir.GetNext( &fpFileName ) );
1014  }
1015 #endif
1016 
1017  return ret;
1018 }
1019 
1020 
1021 MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
1022  const PROPERTIES* aProperties )
1023 {
1024  LOCALE_IO toggle; // toggles on, then off, the C locale.
1025 
1026  init( aProperties );
1027 
1028  cacheLib( aLibraryPath, aFootprintName );
1029 
1030  const MODULE_MAP& mods = m_cache->GetModules();
1031 
1032  MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
1033 
1034  if( it == mods.end() )
1035  {
1036  return NULL;
1037  }
1038 
1039  // copy constructor to clone the already loaded MODULE
1040  return new MODULE( *it->second->GetModule() );
1041 }
1042 
1043 
1044 void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
1045  const PROPERTIES* aProperties )
1046 {
1047  LOCALE_IO toggle; // toggles on, then off, the C locale.
1048 
1049  init( aProperties );
1050 
1051  cacheLib( aLibraryPath );
1052 
1053  if( !m_cache->IsWritable() )
1054  {
1055  THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ),
1056  aLibraryPath.GetData() ) );
1057  }
1058 
1059  m_cache->Remove( aFootprintName );
1060 }
1061 
1062 
1063 bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
1064 {
1065  wxFileName fn;
1066  fn.SetPath( aLibraryPath );
1067 
1068  // Return if there is no library path to delete.
1069  if( !fn.DirExists() )
1070  return false;
1071 
1072  if( !fn.IsDirWritable() )
1073  {
1074  THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ),
1075  aLibraryPath.GetData() ) );
1076  }
1077 
1078  wxDir dir( aLibraryPath );
1079 
1080  if( dir.HasSubDirs() )
1081  {
1082  THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ),
1083  aLibraryPath.GetData() ) );
1084  }
1085 
1086  // All the footprint files must be deleted before the directory can be deleted.
1087  if( dir.HasFiles() )
1088  {
1089  unsigned i;
1090  wxFileName tmp;
1091  wxArrayString files;
1092 
1093  wxDir::GetAllFiles( aLibraryPath, &files );
1094 
1095  for( i = 0; i < files.GetCount(); i++ )
1096  {
1097  tmp = files[i];
1098 
1099  if( tmp.GetExt() != KiCadFootprintFileExtension )
1100  {
1101  THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' was found in library path '%s'" ),
1102  files[i].GetData(), aLibraryPath.GetData() ) );
1103  }
1104  }
1105 
1106  for( i = 0; i < files.GetCount(); i++ )
1107  {
1108  wxRemoveFile( files[i] );
1109  }
1110  }
1111 
1112  wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library '%s'" ),
1113  aLibraryPath.GetData() );
1114 
1115  // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
1116  // we don't want that. we want bare metal portability with no UI here.
1117  if( !wxRmdir( aLibraryPath ) )
1118  {
1119  THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ),
1120  aLibraryPath.GetData() ) );
1121  }
1122 
1123  // For some reason removing a directory in Windows is not immediately updated. This delay
1124  // prevents an error when attempting to immediately recreate the same directory when over
1125  // writing an existing library.
1126 #ifdef __WINDOWS__
1127  wxMilliSleep( 250L );
1128 #endif
1129 
1130  if( m_cache && m_cache->GetPath() == aLibraryPath )
1131  {
1132  delete m_cache;
1133  m_cache = NULL;
1134  }
1135 
1136  return true;
1137 }
1138 
1139 
1140 bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
1141 {
1142  LOCALE_IO toggle;
1143 
1144  init( NULL );
1145 
1146  cacheLib( aLibraryPath );
1147 
1148  return m_cache->IsWritable();
1149 }
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
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
wxArrayString FootprintEnumerate(const wxString &aLibraryPath, const PROPERTIES *aProperties=NULL) override
Function FootprintEnumerate returns a list of footprint names contained within the library at aLibrar...
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:90
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:89
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
Struct PARSE_ERROR contains a filename or source description, a problem input line, a line number, a byte offset, and an error message which contains the the caller's report and his call site information: CPP source file, function, and line number.
Definition: ki_exception.h:94
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.
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:92
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)