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