You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1061 lines
35 KiB

11 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
  5. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /**
  25. * @file gpcb_plugin.cpp
  26. * @brief Geda PCB file plugin implementation file.
  27. */
  28. #include <fctsys.h>
  29. #include <common.h>
  30. #include <macros.h>
  31. #include <trigo.h>
  32. #include <wildcards_and_files_ext.h>
  33. #include <filter_reader.h>
  34. #include <trace_helpers.h>
  35. #include <class_board.h>
  36. #include <class_module.h>
  37. #include <class_pcb_text.h>
  38. #include <class_drawsegment.h>
  39. #include <class_edge_mod.h>
  40. #include <gpcb_plugin.h>
  41. #include <wx/dir.h>
  42. #include <wx/filename.h>
  43. #include <wx/wfstream.h>
  44. #include <boost/ptr_container/ptr_map.hpp>
  45. #include <memory.h>
  46. static inline long parseInt( const wxString& aValue, double aScalar )
  47. {
  48. double value = LONG_MAX;
  49. /*
  50. * In 2011 gEDA/pcb introduced values with units, like "10mm" or "200mil".
  51. * Unit-less values are still centimils (100000 units per inch), like with
  52. * the previous format.
  53. *
  54. * Distinction between the even older format (mils, 1000 units per inch)
  55. * and the pre-2011 format is done in ::parseMODULE already; the
  56. * distinction is by wether an object definition opens with '(' or '['.
  57. * All values with explicite unit open with a '[' so there's no need to
  58. * consider this distinction when parsing them.
  59. *
  60. * The solution here is to watch for a unit and, if present, convert the
  61. * value to centimils. All unit-less values are read unaltered. This way
  62. * the code below can contine to consider all read values to be in mils or
  63. * centimils. It also matches the strategy gEDA/pcb uses for backwards
  64. * compatibility with its own layouts.
  65. *
  66. * Fortunately gEDA/pcb allows only units 'mil' and 'mm' in files, see
  67. * definition of ALLOW_READABLE in gEDA/pcb's pcb_printf.h. So we don't
  68. * have to test for all 11 units gEDA/pcb allows in user dialogs.
  69. */
  70. if( aValue.EndsWith( wxT( "mm" ) ) )
  71. {
  72. aScalar *= 100000.0 / 25.4;
  73. }
  74. else if( aValue.EndsWith( wxT( "mil" ) ) )
  75. {
  76. aScalar *= 100.;
  77. }
  78. // This conversion reports failure on strings as simple as "1000", still
  79. // it returns the right result in &value. Thus, ignore the return value.
  80. aValue.ToCDouble(&value);
  81. if( value == LONG_MAX ) // conversion really failed
  82. {
  83. THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
  84. aValue.GetData() ) );
  85. return 0;
  86. }
  87. return KiROUND( value * aScalar );
  88. }
  89. /**
  90. * Class GPCB_FPL_CACHE_ITEM
  91. * is helper class for creating a footprint library cache.
  92. *
  93. * The new footprint library design is a file path of individual module files
  94. * that contain a single module per file. This class is a helper only for the
  95. * footprint portion of the PLUGIN API, and only for the #PCB_IO plugin. It is
  96. * private to this implementation file so it is not placed into a header.
  97. */
  98. class GPCB_FPL_CACHE_ITEM
  99. {
  100. WX_FILENAME m_filename; ///< The the full file name and path of the footprint to cache.
  101. std::unique_ptr<MODULE> m_module;
  102. public:
  103. GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName );
  104. WX_FILENAME GetFileName() const { return m_filename; }
  105. MODULE* GetModule() const { return m_module.get(); }
  106. };
  107. GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const WX_FILENAME& aFileName ) :
  108. m_filename( aFileName ),
  109. m_module( aModule )
  110. {
  111. }
  112. typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
  113. typedef MODULE_MAP::iterator MODULE_ITER;
  114. typedef MODULE_MAP::const_iterator MODULE_CITER;
  115. class GPCB_FPL_CACHE
  116. {
  117. GPCB_PLUGIN* m_owner; /// Plugin object that owns the cache.
  118. wxFileName m_lib_path; /// The path of the library.
  119. MODULE_MAP m_modules; /// Map of footprint file name per MODULE*.
  120. bool m_cache_dirty; // Stored separately because it's expensive to check
  121. // m_cache_timestamp against all the files.
  122. long long m_cache_timestamp; // A hash of the timestamps for all the footprint
  123. // files.
  124. MODULE* parseMODULE( LINE_READER* aLineReader );
  125. /**
  126. * Function testFlags
  127. * tests \a aFlag for \a aMask or \a aName.
  128. * @param aFlag = List of flags to test against: can be a bit field flag or a list name flag
  129. * a bit field flag is an hexadecimal value: Ox00020000
  130. * a list name flag is a string list of flags, comma separated like square,option1
  131. * @param aMask = flag list to test
  132. * @param aName = flag name to find in list
  133. * @return true if found
  134. */
  135. bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
  136. /**
  137. * Function parseParameters
  138. * extracts parameters and tokens from \a aLineReader and adds them to \a aParameterList.
  139. *
  140. * Delimiter characters are:
  141. * [ ] ( ) Begin and end of parameter list and units indicator
  142. * " is a string delimiter
  143. * space is the param separator
  144. * The first word is the keyword
  145. * the second item is one of ( or [
  146. * other are parameters (number or delimited string)
  147. * last parameter is ) or ]
  148. *
  149. * @param aParameterList This list of parameters parsed.
  150. * @param aLineReader The line reader object to parse.
  151. */
  152. void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
  153. public:
  154. GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
  155. wxString GetPath() const { return m_lib_path.GetPath(); }
  156. bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
  157. MODULE_MAP& GetModules() { return m_modules; }
  158. // Most all functions in this class throw IO_ERROR exceptions. There are no
  159. // error codes nor user interface calls from here, nor in any PLUGIN.
  160. // Catch these exceptions higher up please.
  161. /// Save not implemented for the Geda PCB footprint library format.
  162. void Load();
  163. void Remove( const wxString& aFootprintName );
  164. /**
  165. * Function GetTimestamp
  166. * Generate a timestamp representing all source files in the cache (including the
  167. * parent directory).
  168. * Timestamps should not be considered ordered. They either match or they don't.
  169. */
  170. static long long GetTimestamp( const wxString& aLibPath );
  171. /**
  172. * Function IsModified
  173. * Return true if the cache is not up-to-date.
  174. */
  175. bool IsModified();
  176. };
  177. GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
  178. {
  179. m_owner = aOwner;
  180. m_lib_path.SetPath( aLibraryPath );
  181. m_cache_timestamp = 0;
  182. m_cache_dirty = true;
  183. }
  184. void GPCB_FPL_CACHE::Load()
  185. {
  186. m_cache_dirty = false;
  187. m_cache_timestamp = 0;
  188. // Note: like our .pretty footprint libraries, the gpcb footprint libraries are folders,
  189. // and the footprints are the .fp files inside this folder.
  190. wxDir dir( m_lib_path.GetPath() );
  191. if( !dir.IsOpened() )
  192. {
  193. THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
  194. m_lib_path.GetPath().GetData() ) );
  195. }
  196. wxString fullName;
  197. wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
  198. // wxFileName construction is egregiously slow. Construct it once and just swap out
  199. // the filename thereafter.
  200. WX_FILENAME fn( m_lib_path.GetPath(), wxT( "dummyName" ) );
  201. if( !dir.GetFirst( &fullName, fileSpec ) )
  202. return;
  203. wxString cacheErrorMsg;
  204. do
  205. {
  206. fn.SetFullName( fullName );
  207. // Queue I/O errors so only files that fail to parse don't get loaded.
  208. try
  209. {
  210. // reader now owns fp, will close on exception or return
  211. FILE_LINE_READER reader( fn.GetFullPath() );
  212. std::string name = TO_UTF8( fn.GetName() );
  213. MODULE* footprint = parseMODULE( &reader );
  214. // The footprint name is the file name without the extension.
  215. footprint->SetFPID( LIB_ID( wxEmptyString, fn.GetName() ) );
  216. m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn ) );
  217. }
  218. catch( const IO_ERROR& ioe )
  219. {
  220. if( !cacheErrorMsg.IsEmpty() )
  221. cacheErrorMsg += "\n\n";
  222. cacheErrorMsg += ioe.What();
  223. }
  224. } while( dir.GetNext( &fullName ) );
  225. if( !cacheErrorMsg.IsEmpty() )
  226. THROW_IO_ERROR( cacheErrorMsg );
  227. }
  228. void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
  229. {
  230. std::string footprintName = TO_UTF8( aFootprintName );
  231. MODULE_CITER it = m_modules.find( footprintName );
  232. if( it == m_modules.end() )
  233. {
  234. THROW_IO_ERROR( wxString::Format( _( "library \"%s\" has no footprint \"%s\" to delete" ),
  235. m_lib_path.GetPath().GetData(),
  236. aFootprintName.GetData() ) );
  237. }
  238. // Remove the module from the cache and delete the module file from the library.
  239. wxString fullPath = it->second->GetFileName().GetFullPath();
  240. m_modules.erase( footprintName );
  241. wxRemoveFile( fullPath );
  242. }
  243. bool GPCB_FPL_CACHE::IsModified()
  244. {
  245. m_cache_dirty = m_cache_dirty || GetTimestamp( m_lib_path.GetFullPath() ) != m_cache_timestamp;
  246. return m_cache_dirty;
  247. }
  248. long long GPCB_FPL_CACHE::GetTimestamp( const wxString& aLibPath )
  249. {
  250. wxString fileSpec = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
  251. return TimestampDir( aLibPath, fileSpec );
  252. }
  253. MODULE* GPCB_FPL_CACHE::parseMODULE( LINE_READER* aLineReader )
  254. {
  255. #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
  256. #define OLD_GPCB_UNIT_CONV IU_PER_MILS
  257. // Old version unit = 1 mil, so conv_unit is 10 or 0.1
  258. #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
  259. int paramCnt;
  260. double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
  261. wxPoint textPos;
  262. wxString msg;
  263. wxArrayString parameters;
  264. std::unique_ptr<MODULE> module( new MODULE( NULL ) );
  265. if( aLineReader->ReadLine() == NULL )
  266. {
  267. msg = aLineReader->GetSource() + ": empty file";
  268. THROW_IO_ERROR( msg );
  269. }
  270. parameters.Clear();
  271. parseParameters( parameters, aLineReader );
  272. paramCnt = parameters.GetCount();
  273. /* From the Geda PCB documentation, valid Element definitions:
  274. * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
  275. * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
  276. * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
  277. * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
  278. * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
  279. */
  280. if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
  281. {
  282. msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
  283. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  284. aLineReader->LineNumber(), 0 );
  285. }
  286. if( paramCnt < 10 || paramCnt > 14 )
  287. {
  288. msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
  289. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  290. aLineReader->LineNumber(), 0 );
  291. }
  292. // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
  293. if( parameters[1] == wxT( "(" ) )
  294. conv_unit = OLD_GPCB_UNIT_CONV;
  295. if( paramCnt > 10 )
  296. {
  297. module->SetDescription( parameters[3] );
  298. module->SetReference( parameters[4] );
  299. }
  300. else
  301. {
  302. module->SetDescription( parameters[2] );
  303. module->SetReference( parameters[3] );
  304. }
  305. // Read value
  306. if( paramCnt > 10 )
  307. module->SetValue( parameters[5] );
  308. // With gEDA/pcb, value is meaningful after instantiation, only, so it's
  309. // often empty in bare footprints.
  310. if( module->Value().GetText().IsEmpty() )
  311. module->Value().SetText( wxT( "Val**" ) );
  312. if( paramCnt == 14 )
  313. {
  314. textPos = wxPoint( parseInt( parameters[8], conv_unit ),
  315. parseInt( parameters[9], conv_unit ) );
  316. }
  317. else
  318. {
  319. textPos = wxPoint( parseInt( parameters[6], conv_unit ),
  320. parseInt( parameters[7], conv_unit ) );
  321. }
  322. int orientation = parseInt( parameters[paramCnt-4], 1.0 );
  323. module->Reference().SetTextAngle( (orientation % 2) ? 900 : 0 );
  324. // Calculate size: default height is 40 mils, width 30 mil.
  325. // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
  326. int thsize = parseInt( parameters[paramCnt-3], TEXT_DEFAULT_SIZE ) / 100;
  327. thsize = std::max( (int)( 5 * IU_PER_MILS ), thsize ); // Ensure a minimal size = 5 mils
  328. int twsize = thsize * 30 / 40;
  329. int thickness = thsize / 8;
  330. // gEDA/pcb aligns top/left, not pcbnew's default, center/center.
  331. // Compensate for this by shifting the insertion point instead of the
  332. // aligment, because alignment isn't changeable in the GUI.
  333. textPos.x = textPos.x + twsize * module->GetReference().Len() / 2;
  334. textPos.y += thsize / 2;
  335. // gEDA/pcb draws a bit too low/left, while pcbnew draws a bit too
  336. // high/right. Compensate for similar appearance.
  337. textPos.x -= thsize / 10;
  338. textPos.y += thsize / 2;
  339. module->Reference().SetTextPos( textPos );
  340. module->Reference().SetPos0( textPos );
  341. module->Reference().SetTextSize( wxSize( twsize, thsize ) );
  342. module->Reference().SetThickness( thickness );
  343. // gEDA/pcb shows only one of value/reference/description at a time. Which
  344. // one is selectable by a global menu setting. pcbnew needs reference as
  345. // well as value visible, so place the value right below the reference.
  346. module->Value().SetTextAngle( module->Reference().GetTextAngle() );
  347. module->Value().SetTextSize( module->Reference().GetTextSize() );
  348. module->Value().SetThickness( module->Reference().GetThickness() );
  349. textPos.y += thsize * 13 / 10; // 130% line height
  350. module->Value().SetTextPos( textPos );
  351. module->Value().SetPos0( textPos );
  352. while( aLineReader->ReadLine() )
  353. {
  354. parameters.Clear();
  355. parseParameters( parameters, aLineReader );
  356. if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
  357. continue;
  358. if( parameters[0] == wxT( ")" ) )
  359. break;
  360. paramCnt = parameters.GetCount();
  361. // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
  362. if( paramCnt > 3 )
  363. {
  364. if( parameters[1] == wxT( "(" ) )
  365. conv_unit = OLD_GPCB_UNIT_CONV;
  366. else
  367. conv_unit = NEW_GPCB_UNIT_CONV;
  368. }
  369. wxLogTrace( traceGedaPcbPlugin, wxT( "%s parameter count = %d." ),
  370. GetChars( parameters[0] ), paramCnt );
  371. // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
  372. if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
  373. {
  374. if( paramCnt != 8 )
  375. {
  376. msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
  377. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  378. aLineReader->LineNumber(), 0 );
  379. }
  380. EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
  381. drawSeg->SetLayer( F_SilkS );
  382. drawSeg->SetShape( S_SEGMENT );
  383. drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
  384. parseInt( parameters[3], conv_unit ) ) );
  385. drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
  386. parseInt( parameters[5], conv_unit ) ) );
  387. drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
  388. drawSeg->SetDrawCoord();
  389. module->GraphicalItemsList().PushBack( drawSeg );
  390. continue;
  391. }
  392. // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
  393. if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
  394. {
  395. if( paramCnt != 10 )
  396. {
  397. msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
  398. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  399. aLineReader->LineNumber(), 0 );
  400. }
  401. // Pcbnew does know ellipse so we must have Width = Height
  402. EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
  403. drawSeg->SetLayer( F_SilkS );
  404. drawSeg->SetShape( S_ARC );
  405. module->GraphicalItemsList().PushBack( drawSeg );
  406. // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
  407. int radius = ( parseInt( parameters[4], conv_unit ) +
  408. parseInt( parameters[5], conv_unit ) ) / 2;
  409. wxPoint centre( parseInt( parameters[2], conv_unit ),
  410. parseInt( parameters[3], conv_unit ) );
  411. drawSeg->SetStart0( centre );
  412. // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
  413. double start_angle = parseInt( parameters[6], -10.0 ) + 1800.0;
  414. // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
  415. double sweep_angle = parseInt( parameters[7], -10.0 );
  416. // Geda PCB does not support circles.
  417. if( sweep_angle == -3600.0 )
  418. drawSeg->SetShape( S_CIRCLE );
  419. // Angle value is clockwise in gpcb and Pcbnew.
  420. drawSeg->SetAngle( sweep_angle );
  421. drawSeg->SetEnd0( wxPoint( radius, 0 ) );
  422. // Calculate start point coordinate of arc
  423. wxPoint arcStart( drawSeg->GetEnd0() );
  424. RotatePoint( &arcStart, -start_angle );
  425. drawSeg->SetEnd0( centre + arcStart );
  426. drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
  427. drawSeg->SetDrawCoord();
  428. continue;
  429. }
  430. // Parse a Pad with no hole with format:
  431. // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
  432. // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
  433. // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
  434. // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
  435. if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
  436. {
  437. if( paramCnt < 10 || paramCnt > 13 )
  438. {
  439. msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
  440. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  441. aLineReader->LineNumber(), 0 );
  442. }
  443. D_PAD* pad = new D_PAD( module.get() );
  444. static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
  445. static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
  446. pad->SetShape( PAD_SHAPE_RECT );
  447. pad->SetAttribute( PAD_ATTRIB_SMD );
  448. pad->SetLayerSet( pad_front );
  449. if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
  450. pad->SetLayerSet( pad_back );
  451. // Set the pad name:
  452. // Pcbnew pad name is used for electrical connection calculations.
  453. // Accordingly it should be mapped to gEDA's pin/pad number,
  454. // which is used for the same purpose.
  455. // gEDA also features a pin/pad "name", which is an arbitrary string
  456. // and set to the pin name of the netlist on instantiation. Many gEDA
  457. // bare footprints use identical strings for name and number, so this
  458. // can be a bit confusing.
  459. pad->SetName( parameters[paramCnt-3] );
  460. int x1 = parseInt( parameters[2], conv_unit );
  461. int x2 = parseInt( parameters[4], conv_unit );
  462. int y1 = parseInt( parameters[3], conv_unit );
  463. int y2 = parseInt( parameters[5], conv_unit );
  464. int width = parseInt( parameters[6], conv_unit );
  465. wxPoint delta( x2 - x1, y2 - y1 );
  466. double angle = atan2( (double)delta.y, (double)delta.x );
  467. // Get the pad clearance and the solder mask clearance.
  468. if( paramCnt == 13 )
  469. {
  470. int clearance = parseInt( parameters[7], conv_unit );
  471. // One of gEDA's oddities is that clearance between pad and polygon
  472. // is given as the gap on both sides of the pad together, so for
  473. // KiCad it has to halfed.
  474. pad->SetLocalClearance( clearance / 2 );
  475. // In GEDA, the mask value is the size of the hole in this
  476. // solder mask. In Pcbnew, it is a margin, therefore the distance
  477. // between the copper and the mask
  478. int maskMargin = parseInt( parameters[8], conv_unit );
  479. maskMargin = ( maskMargin - width ) / 2;
  480. pad->SetLocalSolderMaskMargin( maskMargin );
  481. }
  482. // Negate angle (due to Y reversed axis) and convert it to internal units
  483. angle = - RAD2DECIDEG( angle );
  484. pad->SetOrientation( KiROUND( angle ) );
  485. wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
  486. pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
  487. width ) );
  488. // Set the relative position before adjusting the absolute position
  489. pad->SetPos0( padPos );
  490. padPos += module->GetPosition();
  491. pad->SetPosition( padPos );
  492. if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
  493. {
  494. if( pad->GetSize().x == pad->GetSize().y )
  495. pad->SetShape( PAD_SHAPE_CIRCLE );
  496. else
  497. pad->SetShape( PAD_SHAPE_OVAL );
  498. }
  499. module->Add( pad );
  500. continue;
  501. }
  502. // Parse a Pin with through hole with format:
  503. // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
  504. // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
  505. // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
  506. // Pin (aX aY Thickness Drill "Name" NFlags)
  507. // Pin (aX aY Thickness "Name" NFlags)
  508. if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
  509. {
  510. if( paramCnt < 8 || paramCnt > 12 )
  511. {
  512. msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
  513. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  514. aLineReader->LineNumber(), 0 );
  515. }
  516. D_PAD* pad = new D_PAD( module.get() );
  517. pad->SetShape( PAD_SHAPE_CIRCLE );
  518. static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
  519. pad->SetLayerSet( pad_set );
  520. if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
  521. pad->SetShape( PAD_SHAPE_RECT );
  522. // Set the pad name:
  523. // Pcbnew pad name is used for electrical connection calculations.
  524. // Accordingly it should be mapped to gEDA's pin/pad number,
  525. // which is used for the same purpose.
  526. pad->SetName( parameters[paramCnt-3] );
  527. wxPoint padPos( parseInt( parameters[2], conv_unit ),
  528. parseInt( parameters[3], conv_unit ) );
  529. int padSize = parseInt( parameters[4], conv_unit );
  530. pad->SetSize( wxSize( padSize, padSize ) );
  531. int drillSize = 0;
  532. // Get the pad clearance, solder mask clearance, and drill size.
  533. if( paramCnt == 12 )
  534. {
  535. int clearance = parseInt( parameters[5], conv_unit );
  536. // One of gEDA's oddities is that clearance between pad and polygon
  537. // is given as the gap on both sides of the pad together, so for
  538. // KiCad it has to halfed.
  539. pad->SetLocalClearance( clearance / 2 );
  540. // In GEDA, the mask value is the size of the hole in this
  541. // solder mask. In Pcbnew, it is a margin, therefore the distance
  542. // between the copper and the mask
  543. int maskMargin = parseInt( parameters[6], conv_unit );
  544. maskMargin = ( maskMargin - padSize ) / 2;
  545. pad->SetLocalSolderMaskMargin( maskMargin );
  546. drillSize = parseInt( parameters[7], conv_unit );
  547. }
  548. else
  549. {
  550. drillSize = parseInt( parameters[5], conv_unit );
  551. }
  552. pad->SetDrillSize( wxSize( drillSize, drillSize ) );
  553. // Set the relative position before adjusting the absolute position
  554. pad->SetPos0( padPos );
  555. padPos += module->GetPosition();
  556. pad->SetPosition( padPos );
  557. if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
  558. pad->SetShape( PAD_SHAPE_OVAL );
  559. module->Add( pad );
  560. continue;
  561. }
  562. }
  563. // Recalculate the bounding box
  564. module->CalculateBoundingBox();
  565. return module.release();
  566. }
  567. void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
  568. {
  569. char key;
  570. wxString tmp;
  571. char* line = aLineReader->Line();
  572. // Last line already ready in main parser loop.
  573. while( *line != 0 )
  574. {
  575. key = *line;
  576. line++;
  577. switch( key )
  578. {
  579. case '[':
  580. case '(':
  581. if( !tmp.IsEmpty() )
  582. {
  583. aParameterList.Add( tmp );
  584. tmp.Clear();
  585. }
  586. tmp.Append( key );
  587. aParameterList.Add( tmp );
  588. tmp.Clear();
  589. // Opening delimiter "(" after Element statement. Any other occurrence is part
  590. // of a keyword definition.
  591. if( aParameterList.GetCount() == 1 )
  592. {
  593. wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
  594. return;
  595. }
  596. break;
  597. case ']':
  598. case ')':
  599. if( !tmp.IsEmpty() )
  600. {
  601. aParameterList.Add( tmp );
  602. tmp.Clear();
  603. }
  604. tmp.Append( key );
  605. aParameterList.Add( tmp );
  606. wxLogTrace( traceGedaPcbPlugin, dump( aParameterList ) );
  607. return;
  608. case '\n':
  609. case '\r':
  610. // Element descriptions can span multiple lines.
  611. line = aLineReader->ReadLine();
  612. // Fall through is intentional.
  613. case '\t':
  614. case ' ':
  615. if( !tmp.IsEmpty() )
  616. {
  617. aParameterList.Add( tmp );
  618. tmp.Clear();
  619. }
  620. break;
  621. case '"':
  622. // Handle empty quotes.
  623. if( *line == '"' )
  624. {
  625. line++;
  626. tmp.Clear();
  627. aParameterList.Add( wxEmptyString );
  628. break;
  629. }
  630. while( *line != 0 )
  631. {
  632. key = *line;
  633. line++;
  634. if( key == '"' )
  635. {
  636. aParameterList.Add( tmp );
  637. tmp.Clear();
  638. break;
  639. }
  640. else
  641. {
  642. tmp.Append( key );
  643. }
  644. }
  645. break;
  646. case '#':
  647. line = aLineReader->ReadLine();
  648. break;
  649. default:
  650. tmp.Append( key );
  651. break;
  652. }
  653. }
  654. }
  655. bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
  656. {
  657. wxString number;
  658. if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
  659. {
  660. long lflags;
  661. if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
  662. return true;
  663. }
  664. else if( aFlag.Contains( aName ) )
  665. {
  666. return true;
  667. }
  668. return false;
  669. }
  670. GPCB_PLUGIN::GPCB_PLUGIN() :
  671. m_cache( 0 ),
  672. m_ctl( 0 )
  673. {
  674. m_reader = NULL;
  675. init( 0 );
  676. }
  677. GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
  678. m_cache( 0 ),
  679. m_ctl( aControlFlags )
  680. {
  681. m_reader = NULL;
  682. init( 0 );
  683. }
  684. GPCB_PLUGIN::~GPCB_PLUGIN()
  685. {
  686. delete m_cache;
  687. }
  688. void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
  689. {
  690. m_props = aProperties;
  691. }
  692. void GPCB_PLUGIN::validateCache( const wxString& aLibraryPath, bool checkModified )
  693. {
  694. if( !m_cache || ( checkModified && m_cache->IsModified() ) )
  695. {
  696. // a spectacular episode in memory management:
  697. delete m_cache;
  698. m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
  699. m_cache->Load();
  700. }
  701. }
  702. void GPCB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
  703. const wxString& aLibraryPath,
  704. const PROPERTIES* aProperties )
  705. {
  706. LOCALE_IO toggle; // toggles on, then off, the C locale.
  707. wxDir dir( aLibraryPath );
  708. if( !dir.IsOpened() )
  709. {
  710. THROW_IO_ERROR( wxString::Format( _( "footprint library path \"%s\" does not exist" ),
  711. GetChars( aLibraryPath ) ) );
  712. }
  713. init( aProperties );
  714. wxString errorMsg;
  715. // Some of the files may have been parsed correctly so we want to add the valid files to
  716. // the library.
  717. try
  718. {
  719. validateCache( aLibraryPath );
  720. }
  721. catch( const IO_ERROR& ioe )
  722. {
  723. errorMsg = ioe.What();
  724. }
  725. const MODULE_MAP& mods = m_cache->GetModules();
  726. for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
  727. {
  728. aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
  729. }
  730. if( !errorMsg.IsEmpty() )
  731. THROW_IO_ERROR( errorMsg );
  732. }
  733. const MODULE* GPCB_PLUGIN::getFootprint( const wxString& aLibraryPath,
  734. const wxString& aFootprintName,
  735. const PROPERTIES* aProperties,
  736. bool checkModified )
  737. {
  738. LOCALE_IO toggle; // toggles on, then off, the C locale.
  739. init( aProperties );
  740. validateCache( aLibraryPath, checkModified );
  741. const MODULE_MAP& mods = m_cache->GetModules();
  742. MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
  743. if( it == mods.end() )
  744. {
  745. return NULL;
  746. }
  747. return it->second->GetModule();
  748. }
  749. const MODULE* GPCB_PLUGIN::GetEnumeratedFootprint( const wxString& aLibraryPath,
  750. const wxString& aFootprintName,
  751. const PROPERTIES* aProperties )
  752. {
  753. return getFootprint( aLibraryPath, aFootprintName, aProperties, false );
  754. }
  755. MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
  756. const PROPERTIES* aProperties )
  757. {
  758. const MODULE* footprint = getFootprint( aLibraryPath, aFootprintName, aProperties, true );
  759. return footprint ? new MODULE( *footprint ) : nullptr;
  760. }
  761. void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
  762. const PROPERTIES* aProperties )
  763. {
  764. LOCALE_IO toggle; // toggles on, then off, the C locale.
  765. init( aProperties );
  766. validateCache( aLibraryPath );
  767. if( !m_cache->IsWritable() )
  768. {
  769. THROW_IO_ERROR( wxString::Format( _( "Library \"%s\" is read only" ),
  770. aLibraryPath.GetData() ) );
  771. }
  772. m_cache->Remove( aFootprintName );
  773. }
  774. bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
  775. {
  776. wxFileName fn;
  777. fn.SetPath( aLibraryPath );
  778. // Return if there is no library path to delete.
  779. if( !fn.DirExists() )
  780. return false;
  781. if( !fn.IsDirWritable() )
  782. {
  783. THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory \"%s\"" ),
  784. aLibraryPath.GetData() ) );
  785. }
  786. wxDir dir( aLibraryPath );
  787. if( dir.HasSubDirs() )
  788. {
  789. THROW_IO_ERROR( wxString::Format( _( "library directory \"%s\" has unexpected sub-directories" ),
  790. aLibraryPath.GetData() ) );
  791. }
  792. // All the footprint files must be deleted before the directory can be deleted.
  793. if( dir.HasFiles() )
  794. {
  795. unsigned i;
  796. wxFileName tmp;
  797. wxArrayString files;
  798. wxDir::GetAllFiles( aLibraryPath, &files );
  799. for( i = 0; i < files.GetCount(); i++ )
  800. {
  801. tmp = files[i];
  802. if( tmp.GetExt() != KiCadFootprintFileExtension )
  803. {
  804. THROW_IO_ERROR( wxString::Format( _( "unexpected file \"%s\" was found in library path \"%s\"" ),
  805. files[i].GetData(), aLibraryPath.GetData() ) );
  806. }
  807. }
  808. for( i = 0; i < files.GetCount(); i++ )
  809. {
  810. wxRemoveFile( files[i] );
  811. }
  812. }
  813. wxLogTrace( traceGedaPcbPlugin, wxT( "Removing footprint library '%s'" ),
  814. aLibraryPath.GetData() );
  815. // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
  816. // we don't want that. we want bare metal portability with no UI here.
  817. if( !wxRmdir( aLibraryPath ) )
  818. {
  819. THROW_IO_ERROR( wxString::Format( _( "footprint library \"%s\" cannot be deleted" ),
  820. aLibraryPath.GetData() ) );
  821. }
  822. // For some reason removing a directory in Windows is not immediately updated. This delay
  823. // prevents an error when attempting to immediately recreate the same directory when over
  824. // writing an existing library.
  825. #ifdef __WINDOWS__
  826. wxMilliSleep( 250L );
  827. #endif
  828. if( m_cache && m_cache->GetPath() == aLibraryPath )
  829. {
  830. delete m_cache;
  831. m_cache = NULL;
  832. }
  833. return true;
  834. }
  835. long long GPCB_PLUGIN::GetLibraryTimestamp( const wxString& aLibraryPath ) const
  836. {
  837. return GPCB_FPL_CACHE::GetTimestamp( aLibraryPath );
  838. }
  839. bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
  840. {
  841. LOCALE_IO toggle;
  842. init( NULL );
  843. validateCache( aLibraryPath );
  844. return m_cache->IsWritable();
  845. }