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.

1067 lines
34 KiB

Modular-Kicad milestone B), major portions: *) Rework the set language support, simplify it by using KIWAY. Now any major frame with a "change language" menu can change the language for all KIWAY_PLAYERs in the whole KIWAY. Multiple KIWAYs are not supported yet. *) Simplify "modal wxFrame" support, and add that support exclusively to KIWAY_PLAYER where it is inherited by all derivatives. The function KIWAY_PLAYER::ShowModal() is in the vtable and so is cross module capable. *) Remove the requirements and assumptions that the wxFrame hierarchy always had PCB_EDIT_FRAME and SCH_EDIT_FRAME as immediate parents of their viewers and editors. This is no longer the case, nor required. *) Use KIWAY::Player() everywhere to make KIWAY_PLAYERs, this registers the KIWAY_PLAYER within the KIWAY and makes it very easy to find an open frame quickly. It also gives control to the KIWAY as to frame hierarchical relationships. *) Change single_top to use the KIWAY for loading a KIFACE and instantiating the single KIWAY_PLAYER, see bullet immediately above. *) Add KIWAY::OnKiwayEnd() and call it from PGM_BASE at program termination, this gives the KIFACEs a chance to save their final configuration dope to disk. *) Add dedicated FRAME_T's for the modal frames, so m_Ident can be tested and these modal frames are distinctly different than their non-modal equivalents. KIWAY_PLAYER::IsModal() is !not! a valid test during the wxFrame's constructor, so this is another important reason for having a dedicated FRAME_T for each modal wxFrame. On balance, more lines were deleted than were added to achieve all this.
12 years ago
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@verizon.net>
  5. * Copyright (C) 1992-2015 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file gpcb_plugin.cpp
  27. * @brief Geda PCB file plugin implementation file.
  28. */
  29. #include <fctsys.h>
  30. #include <common.h>
  31. #include <macros.h>
  32. #include <trigo.h>
  33. #include <wildcards_and_files_ext.h>
  34. #include <filter_reader.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. /**
  47. * Definition for enabling and disabling footprint library trace output. See the
  48. * wxWidgets documentation on using the WXTRACE environment variable.
  49. */
  50. static const wxString traceFootprintLibrary( wxT( "GedaPcbFootprintLib" ) );
  51. static const char delims[] = " \t\r\n";
  52. static bool inline isSpace( int c ) { return strchr( delims, c ) != 0; }
  53. static void inline traceParams( wxArrayString& aParams )
  54. {
  55. wxString tmp;
  56. for( unsigned i = 0; i < aParams.GetCount(); i++ )
  57. {
  58. if( aParams[i].IsEmpty() )
  59. tmp << wxT( "\"\" " );
  60. else
  61. tmp << aParams[i] << wxT( " " );
  62. }
  63. wxLogTrace( traceFootprintLibrary, tmp );
  64. }
  65. static inline long parseInt( const wxString& aValue )
  66. {
  67. long value;
  68. if( aValue.ToLong( &value ) )
  69. return value;
  70. THROW_IO_ERROR( wxString::Format( _( "Cannot convert \"%s\" to an integer" ),
  71. aValue.GetData() ) );
  72. }
  73. static inline long parseInt( const wxString& aValue, double aScalar )
  74. {
  75. return KiROUND( parseInt( aValue ) * aScalar );
  76. }
  77. // Tracing for token parameter arrays.
  78. #ifdef DEBUG
  79. #define TRACE_PARAMS( arr ) traceParams( arr );
  80. #else
  81. #define TRACE_PARAMS( arr ) // Expands to nothing on non-debug builds.
  82. #endif
  83. /**
  84. * Class GPCB_FPL_CACHE_ITEM
  85. * is helper class for creating a footprint library cache.
  86. *
  87. * The new footprint library design is a file path of individual module files
  88. * that contain a single module per file. This class is a helper only for the
  89. * footprint portion of the PLUGIN API, and only for the #PCB_IO plugin. It is
  90. * private to this implementation file so it is not placed into a header.
  91. */
  92. class GPCB_FPL_CACHE_ITEM
  93. {
  94. wxFileName m_file_name; ///< The the full file name and path of the footprint to cache.
  95. bool m_writable; ///< Writability status of the footprint file.
  96. wxDateTime m_mod_time; ///< The last file modified time stamp.
  97. std::auto_ptr<MODULE> m_module;
  98. public:
  99. GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName );
  100. wxString GetName() const { return m_file_name.GetDirs().Last(); }
  101. wxFileName GetFileName() const { return m_file_name; }
  102. bool IsModified() const;
  103. MODULE* GetModule() const { return m_module.get(); }
  104. void UpdateModificationTime() { m_mod_time = m_file_name.GetModificationTime(); }
  105. };
  106. GPCB_FPL_CACHE_ITEM::GPCB_FPL_CACHE_ITEM( MODULE* aModule, const wxFileName& aFileName ) :
  107. m_module( aModule )
  108. {
  109. m_file_name = aFileName;
  110. m_writable = true; // temporary init
  111. if( m_file_name.FileExists() )
  112. m_mod_time = m_file_name.GetModificationTime();
  113. else
  114. m_mod_time.Now();
  115. }
  116. bool GPCB_FPL_CACHE_ITEM::IsModified() const
  117. {
  118. if( !m_file_name.FileExists() )
  119. return false;
  120. return m_file_name.GetModificationTime() != m_mod_time;
  121. }
  122. typedef boost::ptr_map< std::string, GPCB_FPL_CACHE_ITEM > MODULE_MAP;
  123. typedef MODULE_MAP::iterator MODULE_ITER;
  124. typedef MODULE_MAP::const_iterator MODULE_CITER;
  125. class GPCB_FPL_CACHE
  126. {
  127. GPCB_PLUGIN* m_owner; /// Plugin object that owns the cache.
  128. wxFileName m_lib_path; /// The path of the library.
  129. wxDateTime m_mod_time; /// Footprint library path modified time stamp.
  130. MODULE_MAP m_modules; /// Map of footprint file name per MODULE*.
  131. MODULE* parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR );
  132. /**
  133. * Function testFlags
  134. * tests \a aFlag for \a aMask or \a aName.
  135. * @param aFlag = List of flags to test against: can be a bit field flag or a list name flag
  136. * a bit field flag is an hexadecimal value: Ox00020000
  137. * a list name flag is a string list of flags, comma separated like square,option1
  138. * @param aMask = flag list to test
  139. * @param aName = flag name to find in list
  140. * @return true if found
  141. */
  142. bool testFlags( const wxString& aFlag, long aMask, const wxChar* aName );
  143. /**
  144. * Function parseParameters
  145. * extracts parameters and tokens from \a aLineReader and adds them to \a aParameterList.
  146. *
  147. * Delimiter characters are:
  148. * [ ] ( ) Begin and end of parameter list and units indicator
  149. * " is a string delimiter
  150. * space is the param separator
  151. * The first word is the keyword
  152. * the second item is one of ( or [
  153. * other are parameters (number or delimited string)
  154. * last parameter is ) or ]
  155. *
  156. * @param aParameterList This list of parameters parsed.
  157. * @param aLineReader The line reader object to parse.
  158. */
  159. void parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader );
  160. public:
  161. GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath );
  162. wxString GetPath() const { return m_lib_path.GetPath(); }
  163. wxDateTime GetLastModificationTime() const { return m_mod_time; }
  164. bool IsWritable() const { return m_lib_path.IsOk() && m_lib_path.IsDirWritable(); }
  165. MODULE_MAP& GetModules() { return m_modules; }
  166. // Most all functions in this class throw IO_ERROR exceptions. There are no
  167. // error codes nor user interface calls from here, nor in any PLUGIN.
  168. // Catch these exceptions higher up please.
  169. /// Save not implemented for the Geda PCB footprint library format.
  170. void Load();
  171. void Remove( const wxString& aFootprintName );
  172. wxDateTime GetLibModificationTime() const;
  173. /**
  174. * Function IsModified
  175. * check if the footprint cache has been modified relative to \a aLibPath
  176. * and \a aFootprintName.
  177. *
  178. * @param aLibPath is a path to test the current cache library path against.
  179. * @param aFootprintName is the footprint name in the cache to test. If the footprint
  180. * name is empty, the all the footprint files in the library are
  181. * checked to see if they have been modified.
  182. * @return true if the cache has been modified.
  183. */
  184. bool IsModified( const wxString& aLibPath,
  185. const wxString& aFootprintName = wxEmptyString ) const;
  186. /**
  187. * Function IsPath
  188. * checks if \a aPath is the same as the current cache path.
  189. *
  190. * This tests paths by converting \a aPath using the native separators. Internally
  191. * #FP_CACHE stores the current path using native separators. This prevents path
  192. * miscompares on Windows due to the fact that paths can be stored with / instead of \\
  193. * in the footprint library table.
  194. *
  195. * @param aPath is the library path to test against.
  196. * @return true if \a aPath is the same as the cache path.
  197. */
  198. bool IsPath( const wxString& aPath ) const;
  199. };
  200. GPCB_FPL_CACHE::GPCB_FPL_CACHE( GPCB_PLUGIN* aOwner, const wxString& aLibraryPath )
  201. {
  202. m_owner = aOwner;
  203. m_lib_path.SetPath( aLibraryPath );
  204. }
  205. wxDateTime GPCB_FPL_CACHE::GetLibModificationTime() const
  206. {
  207. if( !m_lib_path.DirExists() )
  208. return wxDateTime::Now();
  209. return m_lib_path.GetModificationTime();
  210. }
  211. void GPCB_FPL_CACHE::Load()
  212. {
  213. wxDir dir( m_lib_path.GetPath() );
  214. if( !dir.IsOpened() )
  215. {
  216. THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
  217. m_lib_path.GetPath().GetData() ) );
  218. }
  219. wxString fpFileName;
  220. wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
  221. if( !dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
  222. return;
  223. do
  224. {
  225. wxFileName fn( m_lib_path.GetPath(), fpFileName );
  226. // reader now owns fp, will close on exception or return
  227. FILE_LINE_READER reader( fn.GetFullPath() );
  228. std::string name = TO_UTF8( fn.GetName() );
  229. MODULE* footprint = parseMODULE( &reader );
  230. // The footprint name is the file name without the extension.
  231. footprint->SetFPID( FPID( fn.GetName() ) );
  232. m_modules.insert( name, new GPCB_FPL_CACHE_ITEM( footprint, fn.GetName() ) );
  233. } while( dir.GetNext( &fpFileName ) );
  234. // Remember the file modification time of library file when the
  235. // cache snapshot was made, so that in a networked environment we will
  236. // reload the cache as needed.
  237. m_mod_time = GetLibModificationTime();
  238. }
  239. void GPCB_FPL_CACHE::Remove( const wxString& aFootprintName )
  240. {
  241. std::string footprintName = TO_UTF8( aFootprintName );
  242. MODULE_CITER it = m_modules.find( footprintName );
  243. if( it == m_modules.end() )
  244. {
  245. THROW_IO_ERROR( wxString::Format( _( "library <%s> has no footprint '%s' to delete" ),
  246. m_lib_path.GetPath().GetData(),
  247. aFootprintName.GetData() ) );
  248. }
  249. // Remove the module from the cache and delete the module file from the library.
  250. wxString fullPath = it->second->GetFileName().GetFullPath();
  251. m_modules.erase( footprintName );
  252. wxRemoveFile( fullPath );
  253. }
  254. bool GPCB_FPL_CACHE::IsPath( const wxString& aPath ) const
  255. {
  256. // Converts path separators to native path separators
  257. wxFileName newPath;
  258. newPath.AssignDir( aPath );
  259. return m_lib_path == newPath;
  260. }
  261. bool GPCB_FPL_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintName ) const
  262. {
  263. // The library is modified if the library path got deleted or changed.
  264. if( !m_lib_path.DirExists() || !IsPath( aLibPath ) )
  265. return true;
  266. // If no footprint was specified, check every file modification time against the time
  267. // it was loaded.
  268. if( aFootprintName.IsEmpty() )
  269. {
  270. for( MODULE_CITER it = m_modules.begin(); it != m_modules.end(); ++it )
  271. {
  272. wxFileName fn = m_lib_path;
  273. fn.SetName( it->second->GetFileName().GetName() );
  274. fn.SetExt( KiCadFootprintFileExtension );
  275. if( !fn.FileExists() )
  276. {
  277. wxLogTrace( traceFootprintLibrary,
  278. wxT( "Footprint cache file '%s' does not exist." ),
  279. fn.GetFullPath().GetData() );
  280. return true;
  281. }
  282. if( it->second->IsModified() )
  283. {
  284. wxLogTrace( traceFootprintLibrary,
  285. wxT( "Footprint cache file '%s' has been modified." ),
  286. fn.GetFullPath().GetData() );
  287. return true;
  288. }
  289. }
  290. }
  291. else
  292. {
  293. MODULE_CITER it = m_modules.find( TO_UTF8( aFootprintName ) );
  294. if( it == m_modules.end() || it->second->IsModified() )
  295. return true;
  296. }
  297. return false;
  298. }
  299. MODULE* GPCB_FPL_CACHE::parseMODULE( LINE_READER* aLineReader ) throw( IO_ERROR, PARSE_ERROR )
  300. {
  301. #define TEXT_DEFAULT_SIZE ( 40*IU_PER_MILS )
  302. #define OLD_GPCB_UNIT_CONV IU_PER_MILS
  303. // Old version unit = 1 mil, so conv_unit is 10 or 0.1
  304. #define NEW_GPCB_UNIT_CONV ( 0.01*IU_PER_MILS )
  305. int paramCnt;
  306. double conv_unit = NEW_GPCB_UNIT_CONV; // GPCB unit = 0.01 mils and Pcbnew 0.1
  307. wxPoint refPos( 0, 0 );
  308. wxPoint textPos;
  309. wxString msg;
  310. wxArrayString parameters;
  311. std::auto_ptr<MODULE> module( new MODULE( NULL ) );
  312. if( aLineReader->ReadLine() == NULL )
  313. THROW_IO_ERROR( "unexpected end of file" );
  314. parameters.Clear();
  315. parseParameters( parameters, aLineReader );
  316. paramCnt = parameters.GetCount();
  317. /* From the Geda PCB documentation, valid Element definitions:
  318. * Element [SFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TSFlags]
  319. * Element (NFlags "Desc" "Name" "Value" MX MY TX TY TDir TScale TNFlags)
  320. * Element (NFlags "Desc" "Name" "Value" TX TY TDir TScale TNFlags)
  321. * Element (NFlags "Desc" "Name" TX TY TDir TScale TNFlags)
  322. * Element ("Desc" "Name" TX TY TDir TScale TNFlags)
  323. */
  324. if( parameters[0].CmpNoCase( wxT( "Element" ) ) != 0 )
  325. {
  326. msg.Printf( _( "unknown token \"%s\"" ), GetChars( parameters[0] ) );
  327. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  328. aLineReader->LineNumber(), 0 );
  329. }
  330. if( paramCnt < 10 || paramCnt > 14 )
  331. {
  332. msg.Printf( _( "Element token contains %d parameters." ), paramCnt );
  333. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  334. aLineReader->LineNumber(), 0 );
  335. }
  336. // Test symbol after "Element": if [ units = 0.01 mils, and if ( units = 1 mil
  337. if( parameters[1] == wxT( "(" ) )
  338. conv_unit = OLD_GPCB_UNIT_CONV;
  339. if( paramCnt > 10 )
  340. {
  341. module->SetDescription( parameters[3] );
  342. module->SetReference( parameters[4] );
  343. }
  344. else
  345. {
  346. module->SetDescription( parameters[2] );
  347. module->SetReference( parameters[3] );
  348. }
  349. // Read value
  350. if( paramCnt > 10 )
  351. module->SetValue( parameters[5] );
  352. if( paramCnt == 14 )
  353. {
  354. refPos = wxPoint( parseInt( parameters[6], conv_unit ),
  355. parseInt( parameters[7], conv_unit ) );
  356. textPos = wxPoint( parseInt( parameters[8], conv_unit ),
  357. parseInt( parameters[9], conv_unit ) );
  358. }
  359. else
  360. {
  361. textPos = wxPoint( parseInt( parameters[6], conv_unit ),
  362. parseInt( parameters[7], conv_unit ) );
  363. }
  364. module->Reference().SetTextPosition( textPos );
  365. module->Reference().SetPos0( textPos );
  366. int orientation = parseInt( parameters[paramCnt-4] );
  367. module->Reference().SetOrientation( (orientation % 2) ? 900 : 0 );
  368. // Calculate size: default is 40 mils
  369. // real size is: default * ibuf[idx+3] / 100 (size in gpcb is given in percent of default size
  370. int tsize = ( parseInt( parameters[paramCnt-3] ) * TEXT_DEFAULT_SIZE ) / 100;
  371. int thickness = module->Reference().GetSize().x / 6;
  372. tsize = std::max( KiROUND(5 * IU_PER_MILS), tsize ); // Ensure a minimal size = 5 mils
  373. module->Reference().SetSize( wxSize( tsize, tsize ) );
  374. module->Reference().SetThickness( thickness );
  375. module->Value().SetOrientation( module->Reference().GetOrientation() );
  376. module->Value().SetSize( module->Reference().GetSize() );
  377. module->Value().SetThickness( module->Reference().GetThickness() );
  378. textPos.y += tsize + thickness;
  379. module->Value().SetTextPosition( textPos );
  380. module->Value().SetPos0( textPos );
  381. while( aLineReader->ReadLine() )
  382. {
  383. parameters.Clear();
  384. parseParameters( parameters, aLineReader );
  385. if( parameters.IsEmpty() || parameters[0] == wxT( "(" ) )
  386. continue;
  387. if( parameters[0] == wxT( ")" ) )
  388. break;
  389. paramCnt = parameters.GetCount();
  390. // Test units value for a string line param (more than 3 parameters : ident [ xx ] )
  391. if( paramCnt > 3 )
  392. {
  393. if( parameters[1] == wxT( "(" ) )
  394. conv_unit = OLD_GPCB_UNIT_CONV;
  395. else
  396. conv_unit = NEW_GPCB_UNIT_CONV;
  397. }
  398. wxLogTrace( traceFootprintLibrary, wxT( "%s parameter count = %d." ),
  399. GetChars( parameters[0] ), paramCnt );
  400. // Parse a line with format: ElementLine [X1 Y1 X2 Y2 Thickness]
  401. if( parameters[0].CmpNoCase( wxT( "ElementLine" ) ) == 0 )
  402. {
  403. if( paramCnt != 8 )
  404. {
  405. msg.Printf( wxT( "ElementLine token contains %d parameters." ), paramCnt );
  406. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  407. aLineReader->LineNumber(), 0 );
  408. }
  409. EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
  410. drawSeg->SetLayer( F_SilkS );
  411. drawSeg->SetShape( S_SEGMENT );
  412. drawSeg->SetStart0( wxPoint( parseInt( parameters[2], conv_unit ),
  413. parseInt( parameters[3], conv_unit ) ) );
  414. drawSeg->SetEnd0( wxPoint( parseInt( parameters[4], conv_unit ),
  415. parseInt( parameters[5], conv_unit ) ) );
  416. drawSeg->SetWidth( parseInt( parameters[6], conv_unit ) );
  417. drawSeg->SetDrawCoord();
  418. module->GraphicalItems().PushBack( drawSeg );
  419. continue;
  420. }
  421. // Parse an arc with format: ElementArc [X Y Width Height StartAngle DeltaAngle Thickness]
  422. if( parameters[0].CmpNoCase( wxT( "ElementArc" ) ) == 0 )
  423. {
  424. if( paramCnt != 10 )
  425. {
  426. msg.Printf( wxT( "ElementArc token contains %d parameters." ), paramCnt );
  427. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  428. aLineReader->LineNumber(), 0 );
  429. }
  430. // Pcbnew does know ellipse so we must have Width = Height
  431. EDGE_MODULE* drawSeg = new EDGE_MODULE( module.get() );
  432. drawSeg->SetLayer( F_SilkS );
  433. drawSeg->SetShape( S_ARC );
  434. module->GraphicalItems().PushBack( drawSeg );
  435. // for and arc: ibuf[3] = ibuf[4]. Pcbnew does not know ellipses
  436. int radius = ( parseInt( parameters[4], conv_unit ) +
  437. parseInt( parameters[5], conv_unit ) ) / 2;
  438. wxPoint centre( parseInt( parameters[2], conv_unit ),
  439. parseInt( parameters[3], conv_unit ) );
  440. drawSeg->SetStart0( centre );
  441. // Pcbnew start angles are inverted and 180 degrees from Geda PCB angles.
  442. double start_angle = ( parseInt( parameters[6] ) * -10.0 ) + 1800.0;
  443. // Pcbnew delta angle direction is the opposite of Geda PCB delta angles.
  444. double sweep_angle = parseInt( parameters[7] ) * -10.0;
  445. // Geda PCB does not support circles.
  446. if( sweep_angle == -3600.0 )
  447. drawSeg->SetShape( S_CIRCLE );
  448. // Angle value is clockwise in gpcb and Pcbnew.
  449. drawSeg->SetAngle( sweep_angle );
  450. drawSeg->SetEnd0( wxPoint( radius, 0 ) );
  451. // Calculate start point coordinate of arc
  452. wxPoint arcStart( drawSeg->GetEnd0() );
  453. RotatePoint( &arcStart, -start_angle );
  454. drawSeg->SetEnd0( centre + arcStart );
  455. drawSeg->SetWidth( parseInt( parameters[8], conv_unit ) );
  456. drawSeg->SetDrawCoord();
  457. continue;
  458. }
  459. // Parse a Pad with no hole with format:
  460. // Pad [rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" SFlags]
  461. // Pad (rX1 rY1 rX2 rY2 Thickness Clearance Mask "Name" "Number" NFlags)
  462. // Pad (aX1 aY1 aX2 aY2 Thickness "Name" "Number" NFlags)
  463. // Pad (aX1 aY1 aX2 aY2 Thickness "Name" NFlags)
  464. if( parameters[0].CmpNoCase( wxT( "Pad" ) ) == 0 )
  465. {
  466. if( paramCnt < 10 || paramCnt > 13 )
  467. {
  468. msg.Printf( wxT( "Pad token contains %d parameters." ), paramCnt );
  469. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  470. aLineReader->LineNumber(), 0 );
  471. }
  472. D_PAD* pad = new D_PAD( module.get() );
  473. static const LSET pad_front( 3, F_Cu, F_Mask, F_Paste );
  474. static const LSET pad_back( 3, B_Cu, B_Mask, B_Paste );
  475. pad->SetShape( PAD_SHAPE_RECT );
  476. pad->SetAttribute( PAD_ATTRIB_SMD );
  477. pad->SetLayerSet( pad_front );
  478. if( testFlags( parameters[paramCnt-2], 0x0080, wxT( "onsolder" ) ) )
  479. pad->SetLayerSet( pad_back );
  480. // Read pad number:
  481. if( paramCnt > 10 )
  482. {
  483. pad->SetPadName( parameters[paramCnt-4] );
  484. }
  485. else
  486. {
  487. pad->SetPadName( parameters[paramCnt-3] );
  488. }
  489. int x1 = parseInt( parameters[2], conv_unit );
  490. int x2 = parseInt( parameters[4], conv_unit );
  491. int y1 = parseInt( parameters[3], conv_unit );
  492. int y2 = parseInt( parameters[5], conv_unit );
  493. int width = parseInt( parameters[6], conv_unit );
  494. wxPoint delta( x2 - x1, y2 - y1 );
  495. double angle = atan2( (double)delta.y, (double)delta.x );
  496. // Get the pad clearance and the solder mask clearance.
  497. if( paramCnt == 13 )
  498. {
  499. pad->SetLocalClearance( parseInt( parameters[7], conv_unit ) );
  500. pad->SetLocalSolderMaskMargin( parseInt( parameters[8], conv_unit ) );
  501. }
  502. // Negate angle (due to Y reversed axis) and convert it to internal units
  503. angle = - RAD2DECIDEG( angle );
  504. pad->SetOrientation( KiROUND( angle ) );
  505. wxPoint padPos( (x1 + x2) / 2, (y1 + y2) / 2 );
  506. pad->SetSize( wxSize( KiROUND( EuclideanNorm( delta ) ) + width,
  507. width ) );
  508. padPos += module->GetPosition();
  509. pad->SetPos0( padPos );
  510. pad->SetPosition( padPos );
  511. if( !testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
  512. {
  513. if( pad->GetSize().x == pad->GetSize().y )
  514. pad->SetShape( PAD_SHAPE_CIRCLE );
  515. else
  516. pad->SetShape( PAD_SHAPE_OVAL );
  517. }
  518. module->Add( pad );
  519. continue;
  520. }
  521. // Parse a Pin with through hole with format:
  522. // Pin [rX rY Thickness Clearance Mask Drill "Name" "Number" SFlags]
  523. // Pin (rX rY Thickness Clearance Mask Drill "Name" "Number" NFlags)
  524. // Pin (aX aY Thickness Drill "Name" "Number" NFlags)
  525. // Pin (aX aY Thickness Drill "Name" NFlags)
  526. // Pin (aX aY Thickness "Name" NFlags)
  527. if( parameters[0].CmpNoCase( wxT( "Pin" ) ) == 0 )
  528. {
  529. if( paramCnt < 8 || paramCnt > 12 )
  530. {
  531. msg.Printf( wxT( "Pin token contains %d parameters." ), paramCnt );
  532. THROW_PARSE_ERROR( msg, aLineReader->GetSource(), (const char *)aLineReader,
  533. aLineReader->LineNumber(), 0 );
  534. }
  535. D_PAD* pad = new D_PAD( module.get() );
  536. pad->SetShape( PAD_SHAPE_CIRCLE );
  537. static const LSET pad_set = LSET::AllCuMask() | LSET( 3, F_SilkS, F_Mask, B_Mask );
  538. pad->SetLayerSet( pad_set );
  539. if( testFlags( parameters[paramCnt-2], 0x0100, wxT( "square" ) ) )
  540. pad->SetShape( PAD_SHAPE_RECT );
  541. // Read pad number:
  542. if( paramCnt > 9 )
  543. {
  544. pad->SetPadName( parameters[paramCnt-4] );
  545. }
  546. else
  547. {
  548. pad->SetPadName( parameters[paramCnt-3] );
  549. }
  550. wxPoint padPos( parseInt( parameters[2], conv_unit ),
  551. parseInt( parameters[3], conv_unit ) );
  552. int padSize = parseInt( parameters[4], conv_unit );
  553. pad->SetSize( wxSize( padSize, padSize ) );
  554. int drillSize = 0;
  555. // Get the pad clearance, solder mask clearance, and drill size.
  556. if( paramCnt == 12 )
  557. {
  558. pad->SetLocalClearance( parseInt( parameters[5], conv_unit ) );
  559. pad->SetLocalSolderMaskMargin( parseInt( parameters[6], conv_unit ) );
  560. drillSize = parseInt( parameters[7], conv_unit );
  561. }
  562. else
  563. {
  564. drillSize = parseInt( parameters[5], conv_unit );
  565. }
  566. pad->SetDrillSize( wxSize( drillSize, drillSize ) );
  567. padPos += module->GetPosition();
  568. pad->SetPos0( padPos );
  569. pad->SetPosition( padPos );
  570. if( pad->GetShape() == PAD_SHAPE_CIRCLE && pad->GetSize().x != pad->GetSize().y )
  571. pad->SetShape( PAD_SHAPE_OVAL );
  572. module->Add( pad );
  573. continue;
  574. }
  575. }
  576. if( module->Value().GetText().IsEmpty() )
  577. module->Value().SetText( wxT( "Val**" ) );
  578. // Recalculate the bounding box
  579. module->CalculateBoundingBox();
  580. return module.release();
  581. }
  582. void GPCB_FPL_CACHE::parseParameters( wxArrayString& aParameterList, LINE_READER* aLineReader )
  583. {
  584. char key;
  585. wxString tmp;
  586. char* line = aLineReader->Line();
  587. // Last line already ready in main parser loop.
  588. while( *line != 0 )
  589. {
  590. key = *line;
  591. line++;
  592. switch( key )
  593. {
  594. case '[':
  595. case '(':
  596. if( !tmp.IsEmpty() )
  597. {
  598. aParameterList.Add( tmp );
  599. tmp.Clear();
  600. }
  601. tmp.Append( key );
  602. aParameterList.Add( tmp );
  603. tmp.Clear();
  604. // Opening delimiter "(" after Element statement. Any other occurrence is part
  605. // of a keyword definition.
  606. if( aParameterList.GetCount() == 1 )
  607. {
  608. TRACE_PARAMS( aParameterList );
  609. return;
  610. }
  611. break;
  612. case ']':
  613. case ')':
  614. if( !tmp.IsEmpty() )
  615. {
  616. aParameterList.Add( tmp );
  617. tmp.Clear();
  618. }
  619. tmp.Append( key );
  620. aParameterList.Add( tmp );
  621. TRACE_PARAMS( aParameterList );
  622. return;
  623. case '\n':
  624. case '\r':
  625. // Element descriptions can span multiple lines.
  626. line = aLineReader->ReadLine();
  627. // Fall through is intentional.
  628. case '\t':
  629. case ' ':
  630. if( !tmp.IsEmpty() )
  631. {
  632. aParameterList.Add( tmp );
  633. tmp.Clear();
  634. }
  635. break;
  636. case '"':
  637. // Handle empty quotes.
  638. if( *line == '"' )
  639. {
  640. line++;
  641. tmp.Clear();
  642. aParameterList.Add( wxEmptyString );
  643. break;
  644. }
  645. while( *line != 0 )
  646. {
  647. key = *line;
  648. line++;
  649. if( key == '"' )
  650. {
  651. aParameterList.Add( tmp );
  652. tmp.Clear();
  653. break;
  654. }
  655. else
  656. {
  657. tmp.Append( key );
  658. }
  659. }
  660. break;
  661. case '#':
  662. line = aLineReader->ReadLine();
  663. break;
  664. default:
  665. tmp.Append( key );
  666. break;
  667. }
  668. }
  669. }
  670. bool GPCB_FPL_CACHE::testFlags( const wxString& aFlag, long aMask, const wxChar* aName )
  671. {
  672. wxString number;
  673. if( aFlag.StartsWith( wxT( "0x" ), &number ) || aFlag.StartsWith( wxT( "0X" ), &number ) )
  674. {
  675. long lflags;
  676. if( number.ToLong( &lflags, 16 ) && ( lflags & aMask ) )
  677. return true;
  678. }
  679. else if( aFlag.Contains( aName ) )
  680. {
  681. return true;
  682. }
  683. return false;
  684. }
  685. GPCB_PLUGIN::GPCB_PLUGIN() :
  686. m_cache( 0 ),
  687. m_ctl( 0 )
  688. {
  689. m_reader = NULL;
  690. init( 0 );
  691. }
  692. GPCB_PLUGIN::GPCB_PLUGIN( int aControlFlags ) :
  693. m_cache( 0 ),
  694. m_ctl( aControlFlags )
  695. {
  696. m_reader = NULL;
  697. init( 0 );
  698. }
  699. GPCB_PLUGIN::~GPCB_PLUGIN()
  700. {
  701. delete m_cache;
  702. }
  703. void GPCB_PLUGIN::init( const PROPERTIES* aProperties )
  704. {
  705. m_props = aProperties;
  706. }
  707. void GPCB_PLUGIN::cacheLib( const wxString& aLibraryPath, const wxString& aFootprintName )
  708. {
  709. if( !m_cache || m_cache->IsModified( aLibraryPath, aFootprintName ) )
  710. {
  711. // a spectacular episode in memory management:
  712. delete m_cache;
  713. m_cache = new GPCB_FPL_CACHE( this, aLibraryPath );
  714. m_cache->Load();
  715. }
  716. }
  717. wxArrayString GPCB_PLUGIN::FootprintEnumerate( const wxString& aLibraryPath,
  718. const PROPERTIES* aProperties )
  719. {
  720. LOCALE_IO toggle; // toggles on, then off, the C locale.
  721. wxArrayString ret;
  722. wxDir dir( aLibraryPath );
  723. if( !dir.IsOpened() )
  724. {
  725. THROW_IO_ERROR( wxString::Format( _( "footprint library path '%s' does not exist" ),
  726. GetChars( aLibraryPath ) ) );
  727. }
  728. init( aProperties );
  729. #if 1 // Set to 0 to only read directory contents, not load cache.
  730. cacheLib( aLibraryPath );
  731. const MODULE_MAP& mods = m_cache->GetModules();
  732. for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
  733. {
  734. ret.Add( FROM_UTF8( it->first.c_str() ) );
  735. }
  736. #else
  737. wxString fpFileName;
  738. wxString wildcard = wxT( "*." ) + GedaPcbFootprintLibFileExtension;
  739. if( dir.GetFirst( &fpFileName, wildcard, wxDIR_FILES ) )
  740. {
  741. do
  742. {
  743. wxFileName fn( aLibraryPath, fpFileName );
  744. ret.Add( fn.GetName() );
  745. } while( dir.GetNext( &fpFileName ) );
  746. }
  747. #endif
  748. return ret;
  749. }
  750. MODULE* GPCB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName,
  751. const PROPERTIES* aProperties )
  752. {
  753. LOCALE_IO toggle; // toggles on, then off, the C locale.
  754. init( aProperties );
  755. cacheLib( aLibraryPath, aFootprintName );
  756. const MODULE_MAP& mods = m_cache->GetModules();
  757. MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
  758. if( it == mods.end() )
  759. {
  760. return NULL;
  761. }
  762. // copy constructor to clone the already loaded MODULE
  763. return new MODULE( *it->second->GetModule() );
  764. }
  765. void GPCB_PLUGIN::FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
  766. const PROPERTIES* aProperties )
  767. {
  768. LOCALE_IO toggle; // toggles on, then off, the C locale.
  769. init( aProperties );
  770. cacheLib( aLibraryPath );
  771. if( !m_cache->IsWritable() )
  772. {
  773. THROW_IO_ERROR( wxString::Format( _( "Library '%s' is read only" ),
  774. aLibraryPath.GetData() ) );
  775. }
  776. m_cache->Remove( aFootprintName );
  777. }
  778. bool GPCB_PLUGIN::FootprintLibDelete( const wxString& aLibraryPath, const PROPERTIES* aProperties )
  779. {
  780. wxFileName fn;
  781. fn.SetPath( aLibraryPath );
  782. // Return if there is no library path to delete.
  783. if( !fn.DirExists() )
  784. return false;
  785. if( !fn.IsDirWritable() )
  786. {
  787. THROW_IO_ERROR( wxString::Format( _( "user does not have permission to delete directory '%s'" ),
  788. aLibraryPath.GetData() ) );
  789. }
  790. wxDir dir( aLibraryPath );
  791. if( dir.HasSubDirs() )
  792. {
  793. THROW_IO_ERROR( wxString::Format( _( "library directory '%s' has unexpected sub-directories" ),
  794. aLibraryPath.GetData() ) );
  795. }
  796. // All the footprint files must be deleted before the directory can be deleted.
  797. if( dir.HasFiles() )
  798. {
  799. unsigned i;
  800. wxFileName tmp;
  801. wxArrayString files;
  802. wxDir::GetAllFiles( aLibraryPath, &files );
  803. for( i = 0; i < files.GetCount(); i++ )
  804. {
  805. tmp = files[i];
  806. if( tmp.GetExt() != KiCadFootprintFileExtension )
  807. {
  808. THROW_IO_ERROR( wxString::Format( _( "unexpected file '%s' was found in library path '%s'" ),
  809. files[i].GetData(), aLibraryPath.GetData() ) );
  810. }
  811. }
  812. for( i = 0; i < files.GetCount(); i++ )
  813. {
  814. wxRemoveFile( files[i] );
  815. }
  816. }
  817. wxLogTrace( traceFootprintLibrary, wxT( "Removing footprint library '%s'" ),
  818. aLibraryPath.GetData() );
  819. // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
  820. // we don't want that. we want bare metal portability with no UI here.
  821. if( !wxRmdir( aLibraryPath ) )
  822. {
  823. THROW_IO_ERROR( wxString::Format( _( "footprint library '%s' cannot be deleted" ),
  824. aLibraryPath.GetData() ) );
  825. }
  826. // For some reason removing a directory in Windows is not immediately updated. This delay
  827. // prevents an error when attempting to immediately recreate the same directory when over
  828. // writing an existing library.
  829. #ifdef __WINDOWS__
  830. wxMilliSleep( 250L );
  831. #endif
  832. if( m_cache && m_cache->GetPath() == aLibraryPath )
  833. {
  834. delete m_cache;
  835. m_cache = NULL;
  836. }
  837. return true;
  838. }
  839. bool GPCB_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
  840. {
  841. LOCALE_IO toggle;
  842. init( NULL );
  843. cacheLib( aLibraryPath );
  844. return m_cache->IsWritable();
  845. }