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.

4535 lines
145 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 CERN
  5. * Copyright (C) 2016-2019 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Wayne Stambaugh <stambaughw@gmail.com>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License along
  20. * with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. #include <algorithm>
  23. #include <boost/algorithm/string/join.hpp>
  24. #include <cctype>
  25. #include <set>
  26. #include <wx/mstream.h>
  27. #include <wx/filename.h>
  28. #include <wx/tokenzr.h>
  29. #include <pgm_base.h>
  30. #include <gr_text.h>
  31. #include <kiway.h>
  32. #include <kicad_string.h>
  33. #include <richio.h>
  34. #include <core/typeinfo.h>
  35. #include <properties.h>
  36. #include <trace_helpers.h>
  37. #include <general.h>
  38. #include <sch_bitmap.h>
  39. #include <sch_bus_entry.h>
  40. #include <sch_component.h>
  41. #include <sch_junction.h>
  42. #include <sch_line.h>
  43. #include <sch_marker.h>
  44. #include <sch_no_connect.h>
  45. #include <sch_text.h>
  46. #include <sch_sheet.h>
  47. #include <sch_bitmap.h>
  48. #include <bus_alias.h>
  49. #include <sch_legacy_plugin.h>
  50. #include <template_fieldnames.h>
  51. #include <sch_screen.h>
  52. #include <class_libentry.h>
  53. #include <class_library.h>
  54. #include <lib_arc.h>
  55. #include <lib_bezier.h>
  56. #include <lib_circle.h>
  57. #include <lib_field.h>
  58. #include <lib_pin.h>
  59. #include <lib_polyline.h>
  60. #include <lib_rectangle.h>
  61. #include <lib_text.h>
  62. #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
  63. #include <symbol_lib_table.h> // for PropPowerSymsOnly definintion.
  64. #include <confirm.h>
  65. #include <tool/selection.h>
  66. #define Mils2Iu( x ) Mils2iu( x )
  67. // Must be the first line of part library document (.dcm) files.
  68. #define DOCFILE_IDENT "EESchema-DOCLIB Version 2.0"
  69. #define SCH_PARSE_ERROR( text, reader, pos ) \
  70. THROW_PARSE_ERROR( text, reader.GetSource(), reader.Line(), \
  71. reader.LineNumber(), pos - reader.Line() )
  72. // Token delimiters.
  73. const char* delims = " \t\r\n";
  74. // Tokens to read/save graphic lines style
  75. #define T_STYLE "style"
  76. #define T_COLOR "rgb" // cannot be modifed (used by wxWidgets)
  77. #define T_COLORA "rgba" // cannot be modifed (used by wxWidgets)
  78. #define T_WIDTH "width"
  79. static bool is_eol( char c )
  80. {
  81. // The default file eol character used internally by KiCad.
  82. // |
  83. // | Possible eol if someone edited the file by hand on certain platforms.
  84. // | |
  85. // | | May have gone past eol with strtok().
  86. // | | |
  87. if( c == '\n' || c == '\r' || c == 0 )
  88. return true;
  89. return false;
  90. }
  91. /**
  92. * Compare \a aString to the string starting at \a aLine and advances the character point to
  93. * the end of \a String and returns the new pointer position in \a aOutput if it is not NULL.
  94. *
  95. * @param aString - A pointer to the string to compare.
  96. * @param aLine - A pointer to string to begin the comparison.
  97. * @param aOutput - A pointer to a string pointer to the end of the comparison if not NULL.
  98. * @return true if \a aString was found starting at \a aLine. Otherwise false.
  99. */
  100. static bool strCompare( const char* aString, const char* aLine, const char** aOutput = NULL )
  101. {
  102. size_t len = strlen( aString );
  103. bool retv = ( strncasecmp( aLine, aString, len ) == 0 ) &&
  104. ( isspace( aLine[ len ] ) || aLine[ len ] == 0 );
  105. if( retv && aOutput )
  106. {
  107. const char* tmp = aLine;
  108. // Move past the end of the token.
  109. tmp += len;
  110. // Move to the beginning of the next token.
  111. while( *tmp && isspace( *tmp ) )
  112. tmp++;
  113. *aOutput = tmp;
  114. }
  115. return retv;
  116. }
  117. /**
  118. * Parse an ASCII integer string with possible leading whitespace into
  119. * an integer and updates the pointer at \a aOutput if it is not NULL, just
  120. * like "man strtol()".
  121. *
  122. * @param aReader - The line reader used to generate exception throw information.
  123. * @param aLine - A pointer the current position in a string.
  124. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  125. * the parsing is complete.
  126. * @return A valid integer value.
  127. * @throw An #IO_ERROR on an unexpected end of line.
  128. * @throw A #PARSE_ERROR if the parsed token is not a valid integer.
  129. */
  130. static int parseInt( LINE_READER& aReader, const char* aLine, const char** aOutput = NULL )
  131. {
  132. if( !*aLine )
  133. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  134. // Clear errno before calling strtol() in case some other crt call set it.
  135. errno = 0;
  136. long retv = strtol( aLine, (char**) aOutput, 10 );
  137. // Make sure no error occurred when calling strtol().
  138. if( errno == ERANGE )
  139. SCH_PARSE_ERROR( "invalid integer value", aReader, aLine );
  140. // strtol does not strip off whitespace before the next token.
  141. if( aOutput )
  142. {
  143. const char* next = *aOutput;
  144. while( *next && isspace( *next ) )
  145. next++;
  146. *aOutput = next;
  147. }
  148. return (int) retv;
  149. }
  150. /**
  151. * Parse an ASCII hex integer string with possible leading whitespace into
  152. * a long integer and updates the pointer at \a aOutput if it is not NULL, just
  153. * like "man strtoll".
  154. *
  155. * @param aReader - The line reader used to generate exception throw information.
  156. * @param aLine - A pointer the current position in a string.
  157. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  158. * the parsing is complete.
  159. * @return A valid uint32_t value.
  160. * @throw IO_ERROR on an unexpected end of line.
  161. * @throw PARSE_ERROR if the parsed token is not a valid integer.
  162. */
  163. static uint32_t parseHex( LINE_READER& aReader, const char* aLine,
  164. const char** aOutput = NULL )
  165. {
  166. if( !*aLine )
  167. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  168. // Due to some issues between some files created by a 64 bits version and those
  169. // created by a 32 bits version, we use here a temporary at least 64 bits storage:
  170. unsigned long long retv;
  171. // Clear errno before calling strtoull() in case some other crt call set it.
  172. errno = 0;
  173. retv = strtoull( aLine, (char**) aOutput, 16 );
  174. // Make sure no error occurred when calling strtoull().
  175. if( errno == ERANGE )
  176. SCH_PARSE_ERROR( "invalid hexadecimal number", aReader, aLine );
  177. // Strip off whitespace before the next token.
  178. if( aOutput )
  179. {
  180. const char* next = *aOutput;
  181. while( *next && isspace( *next ) )
  182. next++;
  183. *aOutput = next;
  184. }
  185. return (uint32_t)retv;
  186. }
  187. /**
  188. * Parses an ASCII point string with possible leading whitespace into a double precision
  189. * floating point number and updates the pointer at \a aOutput if it is not NULL, just
  190. * like "man strtod".
  191. *
  192. * @param aReader - The line reader used to generate exception throw information.
  193. * @param aLine - A pointer the current position in a string.
  194. * @param aOutput - The pointer to a string pointer to copy the string pointer position when
  195. * the parsing is complete.
  196. * @return A valid double value.
  197. * @throw IO_ERROR on an unexpected end of line.
  198. * @throw PARSE_ERROR if the parsed token is not a valid integer.
  199. */
  200. static double parseDouble( LINE_READER& aReader, const char* aLine,
  201. const char** aOutput = NULL )
  202. {
  203. if( !*aLine )
  204. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aLine );
  205. // Clear errno before calling strtod() in case some other crt call set it.
  206. errno = 0;
  207. double retv = strtod( aLine, (char**) aOutput );
  208. // Make sure no error occurred when calling strtod().
  209. if( errno == ERANGE )
  210. SCH_PARSE_ERROR( "invalid floating point number", aReader, aLine );
  211. // strtod does not strip off whitespace before the next token.
  212. if( aOutput )
  213. {
  214. const char* next = *aOutput;
  215. while( *next && isspace( *next ) )
  216. next++;
  217. *aOutput = next;
  218. }
  219. return retv;
  220. }
  221. /**
  222. * Parse a single ASCII character and updates the pointer at \a aOutput if it is not NULL.
  223. *
  224. * @param aReader - The line reader used to generate exception throw information.
  225. * @param aCurrentToken - A pointer the current position in a string.
  226. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  227. * the parsing is complete.
  228. * @return A valid ASCII character.
  229. * @throw IO_ERROR on an unexpected end of line.
  230. * @throw PARSE_ERROR if the parsed token is not a a single character token.
  231. */
  232. static char parseChar( LINE_READER& aReader, const char* aCurrentToken,
  233. const char** aNextToken = NULL )
  234. {
  235. while( *aCurrentToken && isspace( *aCurrentToken ) )
  236. aCurrentToken++;
  237. if( !*aCurrentToken )
  238. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  239. if( !isspace( *( aCurrentToken + 1 ) ) )
  240. SCH_PARSE_ERROR( "expected single character token", aReader, aCurrentToken );
  241. if( aNextToken )
  242. {
  243. const char* next = aCurrentToken + 2;
  244. while( *next && isspace( *next ) )
  245. next++;
  246. *aNextToken = next;
  247. }
  248. return *aCurrentToken;
  249. }
  250. /**
  251. * Parse an unquoted utf8 string and updates the pointer at \a aOutput if it is not NULL.
  252. *
  253. * The parsed string must be a continuous string with no white space.
  254. *
  255. * @param aString - A reference to the parsed string.
  256. * @param aReader - The line reader used to generate exception throw information.
  257. * @param aCurrentToken - A pointer the current position in a string.
  258. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  259. * the parsing is complete.
  260. * @param aCanBeEmpty - True if the parsed string is optional. False if it is mandatory.
  261. * @throw IO_ERROR on an unexpected end of line.
  262. * @throw PARSE_ERROR if the \a aCanBeEmpty is false and no string was parsed.
  263. */
  264. static void parseUnquotedString( wxString& aString, LINE_READER& aReader,
  265. const char* aCurrentToken, const char** aNextToken = NULL,
  266. bool aCanBeEmpty = false )
  267. {
  268. if( !*aCurrentToken )
  269. {
  270. if( aCanBeEmpty )
  271. return;
  272. else
  273. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  274. }
  275. const char* tmp = aCurrentToken;
  276. while( *tmp && isspace( *tmp ) )
  277. tmp++;
  278. if( !*tmp )
  279. {
  280. if( aCanBeEmpty )
  281. return;
  282. else
  283. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  284. }
  285. std::string utf8;
  286. while( *tmp && !isspace( *tmp ) )
  287. utf8 += *tmp++;
  288. aString = FROM_UTF8( utf8.c_str() );
  289. if( aString.IsEmpty() && !aCanBeEmpty )
  290. SCH_PARSE_ERROR( _( "expected unquoted string" ), aReader, aCurrentToken );
  291. if( aNextToken )
  292. {
  293. const char* next = tmp;
  294. while( *next && isspace( *next ) )
  295. next++;
  296. *aNextToken = next;
  297. }
  298. }
  299. /**
  300. * Parse an quoted ASCII utf8 and updates the pointer at \a aOutput if it is not NULL.
  301. *
  302. * The parsed string must be contained within a single line. There are no multi-line
  303. * quoted strings in the legacy schematic file format.
  304. *
  305. * @param aString - A reference to the parsed string.
  306. * @param aReader - The line reader used to generate exception throw information.
  307. * @param aCurrentToken - A pointer the current position in a string.
  308. * @param aNextToken - The pointer to a string pointer to copy the string pointer position when
  309. * the parsing is complete.
  310. * @param aCanBeEmpty - True if the parsed string is optional. False if it is mandatory.
  311. * @throw IO_ERROR on an unexpected end of line.
  312. * @throw PARSE_ERROR if the \a aCanBeEmpty is false and no string was parsed.
  313. */
  314. static void parseQuotedString( wxString& aString, LINE_READER& aReader,
  315. const char* aCurrentToken, const char** aNextToken = NULL,
  316. bool aCanBeEmpty = false )
  317. {
  318. if( !*aCurrentToken )
  319. {
  320. if( aCanBeEmpty )
  321. return;
  322. else
  323. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  324. }
  325. const char* tmp = aCurrentToken;
  326. while( *tmp && isspace( *tmp ) )
  327. tmp++;
  328. if( !*tmp )
  329. {
  330. if( aCanBeEmpty )
  331. return;
  332. else
  333. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  334. }
  335. // Verify opening quote.
  336. if( *tmp != '"' )
  337. SCH_PARSE_ERROR( "expecting opening quote", aReader, aCurrentToken );
  338. tmp++;
  339. std::string utf8; // utf8 without escapes and quotes.
  340. // Fetch everything up to closing quote.
  341. while( *tmp )
  342. {
  343. if( *tmp == '\\' )
  344. {
  345. tmp++;
  346. if( !*tmp )
  347. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, aCurrentToken );
  348. // Do not copy the escape byte if it is followed by \ or "
  349. if( *tmp != '"' && *tmp != '\\' )
  350. utf8 += '\\';
  351. utf8 += *tmp;
  352. }
  353. else if( *tmp == '"' ) // Closing double quote.
  354. {
  355. break;
  356. }
  357. else
  358. {
  359. utf8 += *tmp;
  360. }
  361. tmp++;
  362. }
  363. aString = FROM_UTF8( utf8.c_str() );
  364. if( aString.IsEmpty() && !aCanBeEmpty )
  365. SCH_PARSE_ERROR( "expected quoted string", aReader, aCurrentToken );
  366. if( *tmp && *tmp != '"' )
  367. SCH_PARSE_ERROR( "no closing quote for string found", aReader, tmp );
  368. // Move past the closing quote.
  369. tmp++;
  370. if( aNextToken )
  371. {
  372. const char* next = tmp;
  373. while( *next && *next == ' ' )
  374. next++;
  375. *aNextToken = next;
  376. }
  377. }
  378. /**
  379. * A cache assistant for the part library portion of the #SCH_PLUGIN API, and only for the
  380. * #SCH_LEGACY_PLUGIN, so therefore is private to this implementation file, i.e. not placed
  381. * into a header.
  382. */
  383. class SCH_LEGACY_PLUGIN_CACHE
  384. {
  385. static int m_modHash; // Keep track of the modification status of the library.
  386. wxString m_fileName; // Absolute path and file name.
  387. wxFileName m_libFileName; // Absolute path and file name is required here.
  388. wxDateTime m_fileModTime;
  389. LIB_PART_MAP m_symbols; // Map of names of #LIB_PART pointers.
  390. bool m_isWritable;
  391. bool m_isModified;
  392. int m_versionMajor;
  393. int m_versionMinor;
  394. int m_libType; // Is this cache a component or symbol library.
  395. void loadHeader( FILE_LINE_READER& aReader );
  396. static void loadAliases( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader,
  397. LIB_PART_MAP* aMap = nullptr );
  398. static void loadField( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  399. static void loadDrawEntries( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader,
  400. int aMajorVersion, int aMinorVersion );
  401. static void loadFootprintFilters( std::unique_ptr<LIB_PART>& aPart,
  402. LINE_READER& aReader );
  403. void loadDocs();
  404. static LIB_ARC* loadArc( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  405. static LIB_CIRCLE* loadCircle( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  406. static LIB_TEXT* loadText( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader,
  407. int aMajorVersion, int aMinorVersion );
  408. static LIB_RECTANGLE* loadRectangle( std::unique_ptr<LIB_PART>& aPart,
  409. LINE_READER& aReader );
  410. static LIB_PIN* loadPin( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  411. static LIB_POLYLINE* loadPolyLine( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  412. static LIB_BEZIER* loadBezier( std::unique_ptr<LIB_PART>& aPart, LINE_READER& aReader );
  413. static FILL_T parseFillMode( LINE_READER& aReader, const char* aLine,
  414. const char** aOutput );
  415. LIB_PART* removeSymbol( LIB_PART* aAlias );
  416. void saveDocFile();
  417. static void saveArc( LIB_ARC* aArc, OUTPUTFORMATTER& aFormatter );
  418. static void saveBezier( LIB_BEZIER* aBezier, OUTPUTFORMATTER& aFormatter );
  419. static void saveCircle( LIB_CIRCLE* aCircle, OUTPUTFORMATTER& aFormatter );
  420. static void saveField( LIB_FIELD* aField, OUTPUTFORMATTER& aFormatter );
  421. static void savePin( LIB_PIN* aPin, OUTPUTFORMATTER& aFormatter );
  422. static void savePolyLine( LIB_POLYLINE* aPolyLine, OUTPUTFORMATTER& aFormatter );
  423. static void saveRectangle( LIB_RECTANGLE* aRectangle, OUTPUTFORMATTER& aFormatter );
  424. static void saveText( LIB_TEXT* aText, OUTPUTFORMATTER& aFormatter );
  425. friend SCH_LEGACY_PLUGIN;
  426. public:
  427. SCH_LEGACY_PLUGIN_CACHE( const wxString& aLibraryPath );
  428. ~SCH_LEGACY_PLUGIN_CACHE();
  429. int GetModifyHash() const { return m_modHash; }
  430. // Most all functions in this class throw IO_ERROR exceptions. There are no
  431. // error codes nor user interface calls from here, nor in any SCH_PLUGIN objects.
  432. // Catch these exceptions higher up please.
  433. /// Save the entire library to file m_libFileName;
  434. void Save( bool aSaveDocFile = true );
  435. void Load();
  436. void AddSymbol( const LIB_PART* aPart );
  437. void DeleteSymbol( const wxString& aName );
  438. // If m_libFileName is a symlink follow it to the real source file
  439. wxFileName GetRealFile() const;
  440. wxDateTime GetLibModificationTime();
  441. bool IsFile( const wxString& aFullPathAndFileName ) const;
  442. bool IsFileChanged() const;
  443. void SetModified( bool aModified = true ) { m_isModified = aModified; }
  444. wxString GetLogicalName() const { return m_libFileName.GetName(); }
  445. void SetFileName( const wxString& aFileName ) { m_libFileName = aFileName; }
  446. wxString GetFileName() const { return m_libFileName.GetFullPath(); }
  447. static LIB_PART* LoadPart( LINE_READER& aReader, int aMajorVersion, int aMinorVersion,
  448. LIB_PART_MAP* aMap = nullptr );
  449. static void SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter,
  450. LIB_PART_MAP* aMap = nullptr );
  451. };
  452. SCH_LEGACY_PLUGIN::SCH_LEGACY_PLUGIN()
  453. {
  454. init( NULL );
  455. }
  456. SCH_LEGACY_PLUGIN::~SCH_LEGACY_PLUGIN()
  457. {
  458. delete m_cache;
  459. }
  460. void SCH_LEGACY_PLUGIN::init( KIWAY* aKiway, const PROPERTIES* aProperties )
  461. {
  462. m_version = 0;
  463. m_rootSheet = NULL;
  464. m_props = aProperties;
  465. m_kiway = aKiway;
  466. m_cache = NULL;
  467. m_out = NULL;
  468. }
  469. SCH_SHEET* SCH_LEGACY_PLUGIN::Load( const wxString& aFileName, KIWAY* aKiway,
  470. SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
  471. {
  472. wxASSERT( !aFileName || aKiway != NULL );
  473. LOCALE_IO toggle; // toggles on, then off, the C locale.
  474. SCH_SHEET* sheet;
  475. wxFileName fn = aFileName;
  476. // Unfortunately child sheet file names the legacy schematic file format are not fully
  477. // qualified and are always appended to the project path. The aFileName attribute must
  478. // always be an absolute path so the project path can be used for load child sheet files.
  479. wxASSERT( fn.IsAbsolute() );
  480. if( aAppendToMe )
  481. {
  482. wxLogTrace( traceSchLegacyPlugin, "Append \"%s\" to sheet \"%s\".",
  483. aFileName, aAppendToMe->GetFileName() );
  484. wxFileName normedFn = aAppendToMe->GetFileName();
  485. if( !normedFn.IsAbsolute() )
  486. {
  487. if( aFileName.Right( normedFn.GetFullPath().Length() ) == normedFn.GetFullPath() )
  488. m_path = aFileName.Left( aFileName.Length() - normedFn.GetFullPath().Length() );
  489. }
  490. if( m_path.IsEmpty() )
  491. m_path = aKiway->Prj().GetProjectPath();
  492. wxLogTrace( traceSchLegacyPlugin, "Normalized append path \"%s\".", m_path );
  493. }
  494. else
  495. {
  496. m_path = aKiway->Prj().GetProjectPath();
  497. }
  498. m_currentPath.push( m_path );
  499. init( aKiway, aProperties );
  500. if( aAppendToMe == NULL )
  501. {
  502. // Clean up any allocated memory if an exception occurs loading the schematic.
  503. std::unique_ptr< SCH_SHEET > newSheet( new SCH_SHEET );
  504. newSheet->SetFileName( aFileName );
  505. m_rootSheet = newSheet.get();
  506. loadHierarchy( newSheet.get() );
  507. // If we got here, the schematic loaded successfully.
  508. sheet = newSheet.release();
  509. }
  510. else
  511. {
  512. m_rootSheet = aAppendToMe->GetRootSheet();
  513. wxASSERT( m_rootSheet != NULL );
  514. sheet = aAppendToMe;
  515. loadHierarchy( sheet );
  516. }
  517. wxASSERT( m_currentPath.size() == 1 ); // only the project path should remain
  518. return sheet;
  519. }
  520. // Everything below this comment is recursive. Modify with care.
  521. void SCH_LEGACY_PLUGIN::loadHierarchy( SCH_SHEET* aSheet )
  522. {
  523. SCH_SCREEN* screen = NULL;
  524. if( !aSheet->GetScreen() )
  525. {
  526. // SCH_SCREEN objects store the full path and file name where the SCH_SHEET object only
  527. // stores the file name and extension. Add the project path to the file name and
  528. // extension to compare when calling SCH_SHEET::SearchHierarchy().
  529. wxFileName fileName = aSheet->GetFileName();
  530. if( !fileName.IsAbsolute() )
  531. fileName.MakeAbsolute( m_currentPath.top() );
  532. // Save the current path so that it gets restored when decending and ascending the
  533. // sheet hierarchy which allows for sheet schematic files to be nested in folders
  534. // relative to the last path a schematic was loaded from.
  535. wxLogTrace( traceSchLegacyPlugin, "Saving path \"%s\"", m_currentPath.top() );
  536. m_currentPath.push( fileName.GetPath() );
  537. wxLogTrace( traceSchLegacyPlugin, "Current path \"%s\"", m_currentPath.top() );
  538. wxLogTrace( traceSchLegacyPlugin, "Loading \"%s\"", fileName.GetFullPath() );
  539. m_rootSheet->SearchHierarchy( fileName.GetFullPath(), &screen );
  540. if( screen )
  541. {
  542. aSheet->SetScreen( screen );
  543. // Do not need to load the sub-sheets - this has already been done.
  544. }
  545. else
  546. {
  547. aSheet->SetScreen( new SCH_SCREEN( m_kiway ) );
  548. aSheet->GetScreen()->SetFileName( fileName.GetFullPath() );
  549. try
  550. {
  551. loadFile( fileName.GetFullPath(), aSheet->GetScreen() );
  552. for( auto aItem : aSheet->GetScreen()->Items().OfType( SCH_SHEET_T ) )
  553. {
  554. assert( aItem->Type() == SCH_SHEET_T );
  555. auto sheet = static_cast<SCH_SHEET*>( aItem );
  556. // Set the parent to aSheet. This effectively creates a method to find
  557. // the root sheet from any sheet so a pointer to the root sheet does not
  558. // need to be stored globally. Note: this is not the same as a hierarchy.
  559. // Complex hierarchies can have multiple copies of a sheet. This only
  560. // provides a simple tree to find the root sheet.
  561. sheet->SetParent( aSheet );
  562. // Recursion starts here.
  563. loadHierarchy( sheet );
  564. }
  565. }
  566. catch( const IO_ERROR& ioe )
  567. {
  568. // If there is a problem loading the root sheet, there is no recovery.
  569. if( aSheet == m_rootSheet )
  570. throw( ioe );
  571. // For all subsheets, queue up the error message for the caller.
  572. if( !m_error.IsEmpty() )
  573. m_error += "\n";
  574. m_error += ioe.What();
  575. }
  576. }
  577. m_currentPath.pop();
  578. wxLogTrace( traceSchLegacyPlugin, "Restoring path \"%s\"", m_currentPath.top() );
  579. }
  580. }
  581. void SCH_LEGACY_PLUGIN::loadFile( const wxString& aFileName, SCH_SCREEN* aScreen )
  582. {
  583. FILE_LINE_READER reader( aFileName );
  584. loadHeader( reader, aScreen );
  585. LoadContent( reader, aScreen, m_version );
  586. // Unfortunately schematic files prior to version 2 are not terminated with $EndSCHEMATC
  587. // so checking for it's existance will fail so just exit here and take our chances. :(
  588. if( m_version > 1 )
  589. {
  590. char* line = reader.Line();
  591. while( *line == ' ' )
  592. line++;
  593. if( !strCompare( "$EndSCHEMATC", line ) )
  594. THROW_IO_ERROR( "'$EndSCHEMATC' not found" );
  595. }
  596. }
  597. void SCH_LEGACY_PLUGIN::LoadContent( LINE_READER& aReader, SCH_SCREEN* aScreen, int version )
  598. {
  599. m_version = version;
  600. while( aReader.ReadLine() )
  601. {
  602. char* line = aReader.Line();
  603. while( *line == ' ' )
  604. line++;
  605. // Either an object will be loaded properly or the file load will fail and raise
  606. // an exception.
  607. if( strCompare( "$Descr", line ) )
  608. loadPageSettings( aReader, aScreen );
  609. else if( strCompare( "$Comp", line ) )
  610. aScreen->Append( loadComponent( aReader ) );
  611. else if( strCompare( "$Sheet", line ) )
  612. aScreen->Append( loadSheet( aReader ) );
  613. else if( strCompare( "$Bitmap", line ) )
  614. aScreen->Append( loadBitmap( aReader ) );
  615. else if( strCompare( "Connection", line ) )
  616. aScreen->Append( loadJunction( aReader ) );
  617. else if( strCompare( "NoConn", line ) )
  618. aScreen->Append( loadNoConnect( aReader ) );
  619. else if( strCompare( "Wire", line ) )
  620. aScreen->Append( loadWire( aReader ) );
  621. else if( strCompare( "Entry", line ) )
  622. aScreen->Append( loadBusEntry( aReader ) );
  623. else if( strCompare( "Text", line ) )
  624. aScreen->Append( loadText( aReader ) );
  625. else if( strCompare( "BusAlias", line ) )
  626. aScreen->AddBusAlias( loadBusAlias( aReader, aScreen ) );
  627. else if( strCompare( "$EndSCHEMATC", line ) )
  628. return;
  629. else
  630. SCH_PARSE_ERROR( "unrecognized token", aReader, line );
  631. }
  632. }
  633. void SCH_LEGACY_PLUGIN::loadHeader( LINE_READER& aReader, SCH_SCREEN* aScreen )
  634. {
  635. const char* line = aReader.ReadLine();
  636. if( !line || !strCompare( "Eeschema Schematic File Version", line, &line ) )
  637. {
  638. m_error.Printf( _( "\"%s\" does not appear to be an Eeschema file" ),
  639. GetChars( aScreen->GetFileName() ) );
  640. THROW_IO_ERROR( m_error );
  641. }
  642. // get the file version here.
  643. m_version = parseInt( aReader, line, &line );
  644. // The next lines are the lib list section, and are mainly comments, like:
  645. // LIBS:power
  646. // the lib list is not used, but is in schematic file just in case.
  647. // It is usually not empty, but we accept empty list.
  648. // If empty, there is a legacy section, not used
  649. // EELAYER i j
  650. // and the last line is
  651. // EELAYER END
  652. // Skip all lines until the end of header "EELAYER END" is found
  653. while( aReader.ReadLine() )
  654. {
  655. line = aReader.Line();
  656. while( *line == ' ' )
  657. line++;
  658. if( strCompare( "EELAYER END", line ) )
  659. return;
  660. }
  661. THROW_IO_ERROR( _( "Missing 'EELAYER END'" ) );
  662. }
  663. void SCH_LEGACY_PLUGIN::loadPageSettings( LINE_READER& aReader, SCH_SCREEN* aScreen )
  664. {
  665. wxASSERT( aScreen != NULL );
  666. wxString buf;
  667. const char* line = aReader.Line();
  668. PAGE_INFO pageInfo;
  669. TITLE_BLOCK tb;
  670. wxCHECK_RET( strCompare( "$Descr", line, &line ), "Invalid sheet description" );
  671. parseUnquotedString( buf, aReader, line, &line );
  672. if( !pageInfo.SetType( buf ) )
  673. SCH_PARSE_ERROR( "invalid page size", aReader, line );
  674. int pagew = parseInt( aReader, line, &line );
  675. int pageh = parseInt( aReader, line, &line );
  676. if( buf == PAGE_INFO::Custom )
  677. {
  678. pageInfo.SetWidthMils( pagew );
  679. pageInfo.SetHeightMils( pageh );
  680. }
  681. else
  682. {
  683. wxString orientation;
  684. // Non custom size, set portrait if its present. Can be empty string which defaults
  685. // to landscape.
  686. parseUnquotedString( orientation, aReader, line, &line, true );
  687. if( orientation == "portrait" )
  688. pageInfo.SetPortrait( true );
  689. }
  690. aScreen->SetPageSettings( pageInfo );
  691. while( line != NULL )
  692. {
  693. buf.clear();
  694. if( !aReader.ReadLine() )
  695. SCH_PARSE_ERROR( _( "unexpected end of file" ), aReader, line );
  696. line = aReader.Line();
  697. if( strCompare( "Sheet", line, &line ) )
  698. {
  699. aScreen->m_ScreenNumber = parseInt( aReader, line, &line );
  700. aScreen->m_NumberOfScreens = parseInt( aReader, line, &line );
  701. }
  702. else if( strCompare( "Title", line, &line ) )
  703. {
  704. parseQuotedString( buf, aReader, line, &line, true );
  705. tb.SetTitle( buf );
  706. }
  707. else if( strCompare( "Date", line, &line ) )
  708. {
  709. parseQuotedString( buf, aReader, line, &line, true );
  710. tb.SetDate( buf );
  711. }
  712. else if( strCompare( "Rev", line, &line ) )
  713. {
  714. parseQuotedString( buf, aReader, line, &line, true );
  715. tb.SetRevision( buf );
  716. }
  717. else if( strCompare( "Comp", line, &line ) )
  718. {
  719. parseQuotedString( buf, aReader, line, &line, true );
  720. tb.SetCompany( buf );
  721. }
  722. else if( strCompare( "Comment1", line, &line ) )
  723. {
  724. parseQuotedString( buf, aReader, line, &line, true );
  725. tb.SetComment( 0, buf );
  726. }
  727. else if( strCompare( "Comment2", line, &line ) )
  728. {
  729. parseQuotedString( buf, aReader, line, &line, true );
  730. tb.SetComment( 1, buf );
  731. }
  732. else if( strCompare( "Comment3", line, &line ) )
  733. {
  734. parseQuotedString( buf, aReader, line, &line, true );
  735. tb.SetComment( 2, buf );
  736. }
  737. else if( strCompare( "Comment4", line, &line ) )
  738. {
  739. parseQuotedString( buf, aReader, line, &line, true );
  740. tb.SetComment( 3, buf );
  741. }
  742. else if( strCompare( "Comment5", line, &line ) )
  743. {
  744. parseQuotedString( buf, aReader, line, &line, true );
  745. tb.SetComment( 4, buf );
  746. }
  747. else if( strCompare( "Comment6", line, &line ) )
  748. {
  749. parseQuotedString( buf, aReader, line, &line, true );
  750. tb.SetComment( 5, buf );
  751. }
  752. else if( strCompare( "Comment7", line, &line ) )
  753. {
  754. parseQuotedString( buf, aReader, line, &line, true );
  755. tb.SetComment( 6, buf );
  756. }
  757. else if( strCompare( "Comment8", line, &line ) )
  758. {
  759. parseQuotedString( buf, aReader, line, &line, true );
  760. tb.SetComment( 7, buf );
  761. }
  762. else if( strCompare( "Comment9", line, &line ) )
  763. {
  764. parseQuotedString( buf, aReader, line, &line, true );
  765. tb.SetComment( 8, buf );
  766. }
  767. else if( strCompare( "$EndDescr", line ) )
  768. {
  769. aScreen->SetTitleBlock( tb );
  770. return;
  771. }
  772. }
  773. SCH_PARSE_ERROR( "missing 'EndDescr'", aReader, line );
  774. }
  775. SCH_SHEET* SCH_LEGACY_PLUGIN::loadSheet( LINE_READER& aReader )
  776. {
  777. std::unique_ptr< SCH_SHEET > sheet( new SCH_SHEET() );
  778. sheet->SetTimeStamp( GetNewTimeStamp() );
  779. const char* line = aReader.ReadLine();
  780. while( line != NULL )
  781. {
  782. if( strCompare( "S", line, &line ) ) // Sheet dimensions.
  783. {
  784. wxPoint position;
  785. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  786. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  787. sheet->SetPosition( position );
  788. wxSize size;
  789. size.SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  790. size.SetHeight( Mils2Iu( parseInt( aReader, line, &line ) ) );
  791. sheet->SetSize( size );
  792. }
  793. else if( strCompare( "U", line, &line ) ) // Sheet time stamp.
  794. {
  795. sheet->SetTimeStamp( parseHex( aReader, line ) );
  796. }
  797. else if( *line == 'F' ) // Sheet field.
  798. {
  799. line++;
  800. wxString text;
  801. int size;
  802. int fieldId = parseInt( aReader, line, &line );
  803. if( fieldId == 0 || fieldId == 1 ) // Sheet name and file name.
  804. {
  805. parseQuotedString( text, aReader, line, &line );
  806. size = Mils2Iu( parseInt( aReader, line, &line ) );
  807. if( fieldId == 0 )
  808. {
  809. sheet->SetName( text );
  810. sheet->SetSheetNameSize( size );
  811. }
  812. else
  813. {
  814. sheet->SetFileName( text );
  815. sheet->SetFileNameSize( size );
  816. }
  817. }
  818. else // Sheet pin.
  819. {
  820. // Use a unique_ptr so that we clean up in the case of a throw
  821. std::unique_ptr< SCH_SHEET_PIN > sheetPin( new SCH_SHEET_PIN( sheet.get() ) );
  822. sheetPin->SetNumber( fieldId );
  823. // Can be empty fields.
  824. parseQuotedString( text, aReader, line, &line, true );
  825. sheetPin->SetText( text );
  826. if( line == NULL )
  827. THROW_IO_ERROR( _( "unexpected end of line" ) );
  828. switch( parseChar( aReader, line, &line ) )
  829. {
  830. case 'I':
  831. sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );
  832. break;
  833. case 'O':
  834. sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );
  835. break;
  836. case 'B':
  837. sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );
  838. break;
  839. case 'T':
  840. sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_TRISTATE );
  841. break;
  842. case 'U':
  843. sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
  844. break;
  845. default:
  846. SCH_PARSE_ERROR( "invalid sheet pin type", aReader, line );
  847. }
  848. switch( parseChar( aReader, line, &line ) )
  849. {
  850. case 'R': sheetPin->SetEdge( SHEET_RIGHT_SIDE ); break;
  851. case 'T': sheetPin->SetEdge( SHEET_TOP_SIDE ); break;
  852. case 'B': sheetPin->SetEdge( SHEET_BOTTOM_SIDE ); break;
  853. case 'L': sheetPin->SetEdge( SHEET_LEFT_SIDE ); break;
  854. default:
  855. SCH_PARSE_ERROR( "invalid sheet pin side", aReader, line );
  856. }
  857. wxPoint position;
  858. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  859. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  860. sheetPin->SetPosition( position );
  861. size = Mils2Iu( parseInt( aReader, line, &line ) );
  862. sheetPin->SetTextSize( wxSize( size, size ) );
  863. sheet->AddPin( sheetPin.release() );
  864. }
  865. }
  866. else if( strCompare( "$EndSheet", line ) )
  867. return sheet.release();
  868. line = aReader.ReadLine();
  869. }
  870. SCH_PARSE_ERROR( "missing '$EndSheet`", aReader, line );
  871. return NULL; // Prevents compiler warning. Should never get here.
  872. }
  873. SCH_BITMAP* SCH_LEGACY_PLUGIN::loadBitmap( LINE_READER& aReader )
  874. {
  875. std::unique_ptr< SCH_BITMAP > bitmap( new SCH_BITMAP );
  876. const char* line = aReader.Line();
  877. wxCHECK( strCompare( "$Bitmap", line, &line ), NULL );
  878. line = aReader.ReadLine();
  879. while( line != NULL )
  880. {
  881. if( strCompare( "Pos", line, &line ) )
  882. {
  883. wxPoint position;
  884. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  885. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  886. bitmap->SetPosition( position );
  887. }
  888. else if( strCompare( "Scale", line, &line ) )
  889. {
  890. auto scalefactor = parseDouble( aReader, line, &line );
  891. // Prevent scalefactor values that cannot be displayed.
  892. // In the case of a bad value, we accept that the image might be mis-scaled
  893. // rather than removing the full image. Users can then edit the scale factor in
  894. // Eeschema to the appropriate value
  895. if( !std::isnormal( scalefactor ) )
  896. scalefactor = 1.0;
  897. bitmap->GetImage()->SetScale( scalefactor );
  898. }
  899. else if( strCompare( "Data", line, &line ) )
  900. {
  901. wxMemoryOutputStream stream;
  902. while( line )
  903. {
  904. if( !aReader.ReadLine() )
  905. SCH_PARSE_ERROR( _( "Unexpected end of file" ), aReader, line );
  906. line = aReader.Line();
  907. if( strCompare( "EndData", line ) )
  908. {
  909. // all the PNG date is read.
  910. // We expect here m_image and m_bitmap are void
  911. wxImage* image = new wxImage();
  912. wxMemoryInputStream istream( stream );
  913. image->LoadFile( istream, wxBITMAP_TYPE_PNG );
  914. bitmap->GetImage()->SetImage( image );
  915. bitmap->GetImage()->SetBitmap( new wxBitmap( *image ) );
  916. break;
  917. }
  918. // Read PNG data, stored in hexadecimal,
  919. // each byte = 2 hexadecimal digits and a space between 2 bytes
  920. // and put it in memory stream buffer
  921. int len = strlen( line );
  922. for( ; len > 0 && !isspace( *line ); len -= 3, line += 3 )
  923. {
  924. int value = 0;
  925. if( sscanf( line, "%X", &value ) == 1 )
  926. stream.PutC( (char) value );
  927. else
  928. THROW_IO_ERROR( "invalid PNG data" );
  929. }
  930. }
  931. if( line == NULL )
  932. THROW_IO_ERROR( _( "unexpected end of file" ) );
  933. }
  934. else if( strCompare( "$EndBitmap", line ) )
  935. return bitmap.release();
  936. line = aReader.ReadLine();
  937. }
  938. THROW_IO_ERROR( _( "unexpected end of file" ) );
  939. }
  940. SCH_JUNCTION* SCH_LEGACY_PLUGIN::loadJunction( LINE_READER& aReader )
  941. {
  942. std::unique_ptr< SCH_JUNCTION > junction( new SCH_JUNCTION );
  943. const char* line = aReader.Line();
  944. wxCHECK( strCompare( "Connection", line, &line ), NULL );
  945. wxString name;
  946. parseUnquotedString( name, aReader, line, &line );
  947. wxPoint position;
  948. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  949. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  950. junction->SetPosition( position );
  951. return junction.release();
  952. }
  953. SCH_NO_CONNECT* SCH_LEGACY_PLUGIN::loadNoConnect( LINE_READER& aReader )
  954. {
  955. std::unique_ptr< SCH_NO_CONNECT > no_connect( new SCH_NO_CONNECT );
  956. const char* line = aReader.Line();
  957. wxCHECK( strCompare( "NoConn", line, &line ), NULL );
  958. wxString name;
  959. parseUnquotedString( name, aReader, line, &line );
  960. wxPoint position;
  961. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  962. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  963. no_connect->SetPosition( position );
  964. return no_connect.release();
  965. }
  966. SCH_LINE* SCH_LEGACY_PLUGIN::loadWire( LINE_READER& aReader )
  967. {
  968. std::unique_ptr< SCH_LINE > wire( new SCH_LINE );
  969. const char* line = aReader.Line();
  970. wxCHECK( strCompare( "Wire", line, &line ), NULL );
  971. if( strCompare( "Wire", line, &line ) )
  972. wire->SetLayer( LAYER_WIRE );
  973. else if( strCompare( "Bus", line, &line ) )
  974. wire->SetLayer( LAYER_BUS );
  975. else if( strCompare( "Notes", line, &line ) )
  976. wire->SetLayer( LAYER_NOTES );
  977. else
  978. SCH_PARSE_ERROR( "invalid line type", aReader, line );
  979. if( !strCompare( "Line", line, &line ) )
  980. SCH_PARSE_ERROR( "invalid wire definition", aReader, line );
  981. // Since Sept 15, 2017, a line style is alloved (width, style, color)
  982. // Only non default values are stored
  983. while( !is_eol( *line ) )
  984. {
  985. wxString buf;
  986. parseUnquotedString( buf, aReader, line, &line );
  987. if( buf == ")" )
  988. continue;
  989. else if( buf == T_WIDTH )
  990. {
  991. int size = Mils2Iu( parseInt( aReader, line, &line ) );
  992. wire->SetLineWidth( size );
  993. }
  994. else if( buf == T_STYLE )
  995. {
  996. parseUnquotedString( buf, aReader, line, &line );
  997. PLOT_DASH_TYPE style = SCH_LINE::GetLineStyleByName( buf );
  998. wire->SetLineStyle( style );
  999. }
  1000. else // should be the color parameter.
  1001. {
  1002. // The color param is something like rgb(150, 40, 191)
  1003. // and because there is no space between ( and 150
  1004. // the first param is inside buf.
  1005. // So break keyword and the first param into 2 separate strings.
  1006. wxString prm, keyword;
  1007. keyword = buf.BeforeLast( '(', &prm );
  1008. if( ( keyword == T_COLOR ) || ( keyword == T_COLORA ) )
  1009. {
  1010. long color[4] = { 0 };
  1011. int ii = 0;
  1012. if( !prm.IsEmpty() )
  1013. {
  1014. prm.ToLong( &color[ii] );
  1015. ii++;
  1016. }
  1017. int prm_count = ( keyword == T_COLORA ) ? 4 : 3;
  1018. // fix opacity to 1.0 or 255, when not exists in file
  1019. color[3] = 255;
  1020. for(; ii < prm_count && !is_eol( *line ); ii++ )
  1021. {
  1022. color[ii] = parseInt( aReader, line, &line );
  1023. // Skip the separator between values
  1024. if( *line == ',' || *line == ' ')
  1025. line++;
  1026. }
  1027. wire->SetLineColor( color[0]/255.0, color[1]/255.0, color[2]/255.0,color[3]/255.0 );
  1028. }
  1029. }
  1030. }
  1031. // Read the segment en points coordinates:
  1032. line = aReader.ReadLine();
  1033. wxPoint begin, end;
  1034. begin.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1035. begin.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1036. end.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1037. end.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1038. wire->SetStartPoint( begin );
  1039. wire->SetEndPoint( end );
  1040. return wire.release();
  1041. }
  1042. SCH_BUS_ENTRY_BASE* SCH_LEGACY_PLUGIN::loadBusEntry( LINE_READER& aReader )
  1043. {
  1044. const char* line = aReader.Line();
  1045. wxCHECK( strCompare( "Entry", line, &line ), NULL );
  1046. std::unique_ptr< SCH_BUS_ENTRY_BASE > busEntry;
  1047. if( strCompare( "Wire", line, &line ) )
  1048. {
  1049. busEntry.reset( new SCH_BUS_WIRE_ENTRY );
  1050. if( !strCompare( "Line", line, &line ) )
  1051. SCH_PARSE_ERROR( "invalid bus entry definition expected 'Line'", aReader, line );
  1052. }
  1053. else if( strCompare( "Bus", line, &line ) )
  1054. {
  1055. busEntry.reset( new SCH_BUS_BUS_ENTRY );
  1056. if( !strCompare( "Bus", line, &line ) )
  1057. SCH_PARSE_ERROR( "invalid bus entry definition expected 'Bus'", aReader, line );
  1058. }
  1059. else
  1060. SCH_PARSE_ERROR( "invalid bus entry type", aReader, line );
  1061. line = aReader.ReadLine();
  1062. wxPoint pos;
  1063. wxSize size;
  1064. pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1065. pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1066. size.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1067. size.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1068. size.x -= pos.x;
  1069. size.y -= pos.y;
  1070. busEntry->SetPosition( pos );
  1071. busEntry->SetSize( size );
  1072. return busEntry.release();
  1073. }
  1074. // clang-format off
  1075. const std::map<PINSHEETLABEL_SHAPE, const char*> sheetLabelNames
  1076. {
  1077. { PINSHEETLABEL_SHAPE::PS_INPUT, "Input" },
  1078. { PINSHEETLABEL_SHAPE::PS_OUTPUT, "Output" },
  1079. { PINSHEETLABEL_SHAPE::PS_BIDI, "BiDi" },
  1080. { PINSHEETLABEL_SHAPE::PS_TRISTATE, "3State" },
  1081. { PINSHEETLABEL_SHAPE::PS_UNSPECIFIED, "UnSpc" },
  1082. };
  1083. // clang-format on
  1084. SCH_TEXT* SCH_LEGACY_PLUGIN::loadText( LINE_READER& aReader )
  1085. {
  1086. const char* line = aReader.Line();
  1087. wxCHECK( strCompare( "Text", line, &line ), NULL );
  1088. std::unique_ptr< SCH_TEXT> text;
  1089. if( strCompare( "Notes", line, &line ) )
  1090. text.reset( new SCH_TEXT );
  1091. else if( strCompare( "Label", line, &line ) )
  1092. text.reset( new SCH_LABEL );
  1093. else if( strCompare( "HLabel", line, &line ) )
  1094. text.reset( new SCH_HIERLABEL );
  1095. else if( strCompare( "GLabel", line, &line ) )
  1096. {
  1097. // Prior to version 2, the SCH_GLOBALLABEL object did not exist.
  1098. if( m_version == 1 )
  1099. text.reset( new SCH_HIERLABEL );
  1100. else
  1101. text.reset( new SCH_GLOBALLABEL );
  1102. }
  1103. else
  1104. SCH_PARSE_ERROR( "unknown Text type", aReader, line );
  1105. // Parse the parameters common to all text objects.
  1106. wxPoint position;
  1107. position.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1108. position.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1109. text->SetPosition( position );
  1110. int spinStyle = parseInt( aReader, line, &line );
  1111. // Sadly we store the orientation of hierarchical and global labels using a different
  1112. // int encoding than that for local labels:
  1113. // Global Local
  1114. // Left justified 0 2
  1115. // Up 1 3
  1116. // Right justified 2 0
  1117. // Down 3 1
  1118. // So we must flip it as the enum is setup with the "global" numbering
  1119. if( text->Type() != SCH_GLOBAL_LABEL_T && text->Type() != SCH_HIER_LABEL_T )
  1120. {
  1121. if( spinStyle == 0 )
  1122. spinStyle = 2;
  1123. else if( spinStyle == 2 )
  1124. spinStyle = 0;
  1125. }
  1126. text->SetLabelSpinStyle( spinStyle );
  1127. int size = Mils2Iu( parseInt( aReader, line, &line ) );
  1128. text->SetTextSize( wxSize( size, size ) );
  1129. // Parse the global and hierarchical label type.
  1130. if( text->Type() == SCH_HIER_LABEL_T || text->Type() == SCH_GLOBAL_LABEL_T )
  1131. {
  1132. auto resultIt = std::find_if( sheetLabelNames.begin(), sheetLabelNames.end(),
  1133. [ &line ]( const auto& it )
  1134. {
  1135. return strCompare( it.second, line, &line );
  1136. } );
  1137. if( resultIt != sheetLabelNames.end() )
  1138. text->SetShape( resultIt->first );
  1139. else
  1140. SCH_PARSE_ERROR( "invalid label type", aReader, line );
  1141. }
  1142. int thickness = 0;
  1143. // The following tokens do not exist in version 1 schematic files,
  1144. // and not always in version 2 for HLabels and GLabels
  1145. if( m_version > 1 )
  1146. {
  1147. if( m_version > 2 || *line >= ' ' )
  1148. {
  1149. if( strCompare( "Italic", line, &line ) )
  1150. text->SetItalic( true );
  1151. else if( !strCompare( "~", line, &line ) )
  1152. SCH_PARSE_ERROR( _( "expected 'Italics' or '~'" ), aReader, line );
  1153. }
  1154. // The thickness token does not exist in older versions of the schematic file format
  1155. // so calling parseInt will be made only if the EOL is not reached.
  1156. if( *line >= ' ' )
  1157. thickness = parseInt( aReader, line, &line );
  1158. }
  1159. text->SetBold( thickness != 0 );
  1160. text->SetThickness( thickness != 0 ? GetPenSizeForBold( size ) : 0 );
  1161. // Read the text string for the text.
  1162. char* tmp = aReader.ReadLine();
  1163. tmp = strtok( tmp, "\r\n" );
  1164. wxString val = FROM_UTF8( tmp );
  1165. for( ; ; )
  1166. {
  1167. int i = val.find( wxT( "\\n" ) );
  1168. if( i == wxNOT_FOUND )
  1169. break;
  1170. val.erase( i, 2 );
  1171. val.insert( i, wxT( "\n" ) );
  1172. }
  1173. text->SetText( val );
  1174. return text.release();
  1175. }
  1176. SCH_COMPONENT* SCH_LEGACY_PLUGIN::loadComponent( LINE_READER& aReader )
  1177. {
  1178. const char* line = aReader.Line();
  1179. wxCHECK( strCompare( "$Comp", line, &line ), NULL );
  1180. std::unique_ptr< SCH_COMPONENT > component( new SCH_COMPONENT() );
  1181. line = aReader.ReadLine();
  1182. while( line != NULL )
  1183. {
  1184. if( strCompare( "L", line, &line ) )
  1185. {
  1186. wxString libName;
  1187. size_t pos = 2; // "X" plus ' ' space character.
  1188. wxString utf8Line = wxString::FromUTF8( line );
  1189. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  1190. if( tokens.CountTokens() < 2 )
  1191. THROW_PARSE_ERROR( "invalid symbol library definition", aReader.GetSource(),
  1192. aReader.Line(), aReader.LineNumber(), pos );
  1193. libName = tokens.GetNextToken();
  1194. libName.Replace( "~", " " );
  1195. LIB_ID libId;
  1196. // Prior to schematic version 4, library IDs did not have a library nickname so
  1197. // parsing the symbol name with LIB_ID::Parse() would break symbol library links
  1198. // that contained '/' and ':' characters.
  1199. if( m_version > 3 )
  1200. libId.Parse( libName, LIB_ID::ID_SCH, true );
  1201. else
  1202. libId.SetLibItemName( libName, false );
  1203. component->SetLibId( libId );
  1204. wxString refDesignator = tokens.GetNextToken();
  1205. refDesignator.Replace( "~", " " );
  1206. wxString prefix = refDesignator;
  1207. while( prefix.Length() )
  1208. {
  1209. if( ( prefix.Last() < '0' || prefix.Last() > '9') && prefix.Last() != '?' )
  1210. break;
  1211. prefix.RemoveLast();
  1212. }
  1213. // Avoid a prefix containing trailing/leading spaces
  1214. prefix.Trim( true );
  1215. prefix.Trim( false );
  1216. if( prefix.IsEmpty() )
  1217. component->SetPrefix( wxString( "U" ) );
  1218. else
  1219. component->SetPrefix( prefix );
  1220. }
  1221. else if( strCompare( "U", line, &line ) )
  1222. {
  1223. // This fixes a potentially buggy files caused by unit being set to zero which
  1224. // causes netlist issues. See https://bugs.launchpad.net/kicad/+bug/1677282.
  1225. int unit = parseInt( aReader, line, &line );
  1226. if( unit == 0 )
  1227. {
  1228. unit = 1;
  1229. // Set the file as modified so the user can be warned.
  1230. if( m_rootSheet && m_rootSheet->GetScreen() )
  1231. m_rootSheet->GetScreen()->SetModify();
  1232. }
  1233. component->SetUnit( unit );
  1234. // Same can also happen with the convert parameter
  1235. int convert = parseInt( aReader, line, &line );
  1236. if( convert == 0 )
  1237. {
  1238. convert = 1;
  1239. // Set the file as modified so the user can be warned.
  1240. if( m_rootSheet && m_rootSheet->GetScreen() )
  1241. m_rootSheet->GetScreen()->SetModify();
  1242. }
  1243. component->SetConvert( convert );
  1244. component->SetTimeStamp( parseHex( aReader, line, &line ) );
  1245. }
  1246. else if( strCompare( "P", line, &line ) )
  1247. {
  1248. wxPoint pos;
  1249. pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1250. pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1251. component->SetPosition( pos );
  1252. }
  1253. else if( strCompare( "AR", line, &line ) )
  1254. {
  1255. const char* strCompare = "Path=";
  1256. int len = strlen( strCompare );
  1257. if( strncasecmp( strCompare, line, len ) != 0 )
  1258. SCH_PARSE_ERROR( "missing 'Path=' token", aReader, line );
  1259. line += len;
  1260. wxString path, reference, unit;
  1261. parseQuotedString( path, aReader, line, &line );
  1262. strCompare = "Ref=";
  1263. len = strlen( strCompare );
  1264. if( strncasecmp( strCompare, line, len ) != 0 )
  1265. SCH_PARSE_ERROR( "missing 'Ref=' token", aReader, line );
  1266. line+= len;
  1267. parseQuotedString( reference, aReader, line, &line );
  1268. strCompare = "Part=";
  1269. len = strlen( strCompare );
  1270. if( strncasecmp( strCompare, line, len ) != 0 )
  1271. SCH_PARSE_ERROR( "missing 'Part=' token", aReader, line );
  1272. line+= len;
  1273. parseQuotedString( unit, aReader, line, &line );
  1274. long tmp;
  1275. if( !unit.ToLong( &tmp, 10 ) )
  1276. SCH_PARSE_ERROR( "expected integer value", aReader, line );
  1277. if( tmp < 0 || tmp > MAX_UNIT_COUNT_PER_PACKAGE )
  1278. SCH_PARSE_ERROR( "unit value out of range", aReader, line );
  1279. component->AddHierarchicalReference( path, reference, (int)tmp );
  1280. component->GetField( REFERENCE )->SetText( reference );
  1281. }
  1282. else if( strCompare( "F", line, &line ) )
  1283. {
  1284. int index = parseInt( aReader, line, &line );
  1285. wxString text, name;
  1286. parseQuotedString( text, aReader, line, &line, true );
  1287. char orientation = parseChar( aReader, line, &line );
  1288. wxPoint pos;
  1289. pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
  1290. pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
  1291. int size = Mils2Iu( parseInt( aReader, line, &line ) );
  1292. int attributes = parseHex( aReader, line, &line );
  1293. if( index >= component->GetFieldCount() )
  1294. {
  1295. // The first MANDATOR_FIELDS _must_ be constructed within
  1296. // the SCH_COMPONENT constructor. This assert is simply here
  1297. // to guard against a change in that constructor.
  1298. wxASSERT( component->GetFieldCount() >= MANDATORY_FIELDS );
  1299. // Ignore the _supplied_ fieldNdx. It is not important anymore
  1300. // if within the user defined fields region (i.e. >= MANDATORY_FIELDS).
  1301. // We freely renumber the index to fit the next available field slot.
  1302. index = component->GetFieldCount(); // new has this index after insertion
  1303. SCH_FIELD field( wxPoint( 0, 0 ), -1, component.get(), name );
  1304. component->AddField( field );
  1305. }
  1306. // Prior to version 2 of the schematic file format, none of the following existed.
  1307. if( m_version > 1 )
  1308. {
  1309. wxString textAttrs;
  1310. char hjustify = parseChar( aReader, line, &line );
  1311. parseUnquotedString( textAttrs, aReader, line, &line );
  1312. // The name of the field is optional.
  1313. parseQuotedString( name, aReader, line, &line, true );
  1314. if( hjustify == 'L' )
  1315. component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  1316. else if( hjustify == 'R' )
  1317. component->GetField( index )->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  1318. else if( hjustify != 'C' )
  1319. SCH_PARSE_ERROR( "component field text horizontal justification must be "
  1320. "L, R, or C", aReader, line );
  1321. // We are guaranteed to have a least one character here for older file formats
  1322. // otherwise an exception would have been raised..
  1323. if( textAttrs[0] == 'T' )
  1324. component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  1325. else if( textAttrs[0] == 'B' )
  1326. component->GetField( index )->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  1327. else if( textAttrs[0] != 'C' )
  1328. SCH_PARSE_ERROR( "component field text vertical justification must be "
  1329. "B, T, or C", aReader, line );
  1330. // Newer file formats include the bold and italics text attribute.
  1331. if( textAttrs.Length() > 1 )
  1332. {
  1333. if( textAttrs.Length() != 3 )
  1334. SCH_PARSE_ERROR( _( "component field text attributes must be 3 characters wide" ),
  1335. aReader, line );
  1336. if( textAttrs[1] == 'I' )
  1337. component->GetField( index )->SetItalic( true );
  1338. else if( textAttrs[1] != 'N' )
  1339. SCH_PARSE_ERROR( "component field text italics indicator must be I or N",
  1340. aReader, line );
  1341. if( textAttrs[2] == 'B' )
  1342. component->GetField( index )->SetBold( true );
  1343. else if( textAttrs[2] != 'N' )
  1344. SCH_PARSE_ERROR( "component field text bold indicator must be B or N",
  1345. aReader, line );
  1346. }
  1347. }
  1348. component->GetField( index )->SetText( text );
  1349. component->GetField( index )->SetTextPos( pos );
  1350. component->GetField( index )->SetVisible( !attributes );
  1351. component->GetField( index )->SetTextSize( wxSize( size, size ) );
  1352. if( orientation == 'H' )
  1353. component->GetField( index )->SetTextAngle( TEXT_ANGLE_HORIZ );
  1354. else if( orientation == 'V' )
  1355. component->GetField( index )->SetTextAngle( TEXT_ANGLE_VERT );
  1356. else
  1357. SCH_PARSE_ERROR( "component field orientation must be H or V",
  1358. aReader, line );
  1359. if( name.IsEmpty() )
  1360. name = TEMPLATE_FIELDNAME::GetDefaultFieldName( index );
  1361. component->GetField( index )->SetName( name );
  1362. }
  1363. else if( strCompare( "$EndComp", line ) )
  1364. {
  1365. // Ensure all flags (some are set by previous initializations) are reset:
  1366. component->ClearFlags();
  1367. return component.release();
  1368. }
  1369. else
  1370. {
  1371. // There are two lines that begin with a tab or spaces that includes a line with the
  1372. // redundant position information and the transform matrix settings.
  1373. // Parse the redundant position information just the same to check for formatting
  1374. // errors.
  1375. parseInt( aReader, line, &line ); // Always 1.
  1376. parseInt( aReader, line, &line ); // The X coordinate.
  1377. parseInt( aReader, line, &line ); // The Y coordinate.
  1378. line = aReader.ReadLine();
  1379. TRANSFORM transform;
  1380. transform.x1 = parseInt( aReader, line, &line );
  1381. if( transform.x1 < -1 || transform.x1 > 1 )
  1382. SCH_PARSE_ERROR( "invalid component X1 transform value", aReader, line );
  1383. transform.y1 = parseInt( aReader, line, &line );
  1384. if( transform.y1 < -1 || transform.y1 > 1 )
  1385. SCH_PARSE_ERROR( "invalid component Y1 transform value", aReader, line );
  1386. transform.x2 = parseInt( aReader, line, &line );
  1387. if( transform.x2 < -1 || transform.x2 > 1 )
  1388. SCH_PARSE_ERROR( "invalid component X2 transform value", aReader, line );
  1389. transform.y2 = parseInt( aReader, line, &line );
  1390. if( transform.y2 < -1 || transform.y2 > 1 )
  1391. SCH_PARSE_ERROR( "invalid component Y2 transform value", aReader, line );
  1392. component->SetTransform( transform );
  1393. }
  1394. line = aReader.ReadLine();
  1395. }
  1396. SCH_PARSE_ERROR( "invalid component line", aReader, line );
  1397. return NULL; // Prevents compiler warning. Should never get here.
  1398. }
  1399. std::shared_ptr<BUS_ALIAS> SCH_LEGACY_PLUGIN::loadBusAlias( LINE_READER& aReader,
  1400. SCH_SCREEN* aScreen )
  1401. {
  1402. auto busAlias = std::make_shared< BUS_ALIAS >( aScreen );
  1403. const char* line = aReader.Line();
  1404. wxCHECK( strCompare( "BusAlias", line, &line ), NULL );
  1405. wxString buf;
  1406. parseUnquotedString( buf, aReader, line, &line );
  1407. busAlias->SetName( buf );
  1408. while( *line != '\0' )
  1409. {
  1410. buf.clear();
  1411. parseUnquotedString( buf, aReader, line, &line, true );
  1412. if( buf.Len() > 0 )
  1413. {
  1414. busAlias->AddMember( buf );
  1415. }
  1416. }
  1417. return busAlias;
  1418. }
  1419. void SCH_LEGACY_PLUGIN::Save( const wxString& aFileName, SCH_SCREEN* aScreen, KIWAY* aKiway,
  1420. const PROPERTIES* aProperties )
  1421. {
  1422. wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN object." );
  1423. wxCHECK_RET( !aFileName.IsEmpty(), "No schematic file name defined." );
  1424. LOCALE_IO toggle; // toggles on, then off, the C locale, to write floating point values.
  1425. init( aKiway, aProperties );
  1426. wxFileName fn = aFileName;
  1427. // File names should be absolute. Don't assume everything relative to the project path
  1428. // works properly.
  1429. wxASSERT( fn.IsAbsolute() );
  1430. FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
  1431. m_out = &formatter; // no ownership
  1432. Format( aScreen );
  1433. }
  1434. void SCH_LEGACY_PLUGIN::Format( SCH_SCREEN* aScreen )
  1435. {
  1436. wxCHECK_RET( aScreen != NULL, "NULL SCH_SCREEN* object." );
  1437. wxCHECK_RET( m_kiway != NULL, "NULL KIWAY* object." );
  1438. // Write the header
  1439. m_out->Print( 0, "%s %s %d\n", "EESchema", SCHEMATIC_HEAD_STRING, EESCHEMA_VERSION );
  1440. // This section is not used, but written for file compatibility
  1441. m_out->Print( 0, "EELAYER %d %d\n", SCH_LAYER_ID_COUNT, 0 );
  1442. m_out->Print( 0, "EELAYER END\n" );
  1443. /* Write page info, ScreenNumber and NumberOfScreen; not very meaningful for
  1444. * SheetNumber and Sheet Count in a complex hierarchy, but useful in
  1445. * simple hierarchy and flat hierarchy. Used also to search the root
  1446. * sheet ( ScreenNumber = 1 ) within the files
  1447. */
  1448. const TITLE_BLOCK& tb = aScreen->GetTitleBlock();
  1449. const PAGE_INFO& page = aScreen->GetPageSettings();
  1450. m_out->Print( 0, "$Descr %s %d %d%s\n", TO_UTF8( page.GetType() ),
  1451. page.GetWidthMils(),
  1452. page.GetHeightMils(),
  1453. !page.IsCustom() && page.IsPortrait() ? " portrait" : "" );
  1454. m_out->Print( 0, "encoding utf-8\n" );
  1455. m_out->Print( 0, "Sheet %d %d\n", aScreen->m_ScreenNumber, aScreen->m_NumberOfScreens );
  1456. m_out->Print( 0, "Title %s\n", EscapedUTF8( tb.GetTitle() ).c_str() );
  1457. m_out->Print( 0, "Date %s\n", EscapedUTF8( tb.GetDate() ).c_str() );
  1458. m_out->Print( 0, "Rev %s\n", EscapedUTF8( tb.GetRevision() ).c_str() );
  1459. m_out->Print( 0, "Comp %s\n", EscapedUTF8( tb.GetCompany() ).c_str() );
  1460. m_out->Print( 0, "Comment1 %s\n", EscapedUTF8( tb.GetComment( 0 ) ).c_str() );
  1461. m_out->Print( 0, "Comment2 %s\n", EscapedUTF8( tb.GetComment( 1 ) ).c_str() );
  1462. m_out->Print( 0, "Comment3 %s\n", EscapedUTF8( tb.GetComment( 2 ) ).c_str() );
  1463. m_out->Print( 0, "Comment4 %s\n", EscapedUTF8( tb.GetComment( 3 ) ).c_str() );
  1464. m_out->Print( 0, "Comment5 %s\n", EscapedUTF8( tb.GetComment( 4 ) ).c_str() );
  1465. m_out->Print( 0, "Comment6 %s\n", EscapedUTF8( tb.GetComment( 5 ) ).c_str() );
  1466. m_out->Print( 0, "Comment7 %s\n", EscapedUTF8( tb.GetComment( 6 ) ).c_str() );
  1467. m_out->Print( 0, "Comment8 %s\n", EscapedUTF8( tb.GetComment( 7 ) ).c_str() );
  1468. m_out->Print( 0, "Comment9 %s\n", EscapedUTF8( tb.GetComment( 8 ) ).c_str() );
  1469. m_out->Print( 0, "$EndDescr\n" );
  1470. for( const auto& alias : aScreen->GetBusAliases() )
  1471. {
  1472. saveBusAlias( alias );
  1473. }
  1474. // Enforce item ordering
  1475. auto cmp = []( const SCH_ITEM* a, const SCH_ITEM* b ) { return *a < *b; };
  1476. std::multiset<SCH_ITEM*, decltype( cmp )> save_map( cmp );
  1477. for( auto item : aScreen->Items() )
  1478. save_map.insert( item );
  1479. for( auto& item : save_map )
  1480. {
  1481. switch( item->Type() )
  1482. {
  1483. case SCH_COMPONENT_T:
  1484. saveComponent( static_cast<SCH_COMPONENT*>( item ) );
  1485. break;
  1486. case SCH_BITMAP_T:
  1487. saveBitmap( static_cast<SCH_BITMAP*>( item ) );
  1488. break;
  1489. case SCH_SHEET_T:
  1490. saveSheet( static_cast<SCH_SHEET*>( item ) );
  1491. break;
  1492. case SCH_JUNCTION_T:
  1493. saveJunction( static_cast<SCH_JUNCTION*>( item ) );
  1494. break;
  1495. case SCH_NO_CONNECT_T:
  1496. saveNoConnect( static_cast<SCH_NO_CONNECT*>( item ) );
  1497. break;
  1498. case SCH_BUS_WIRE_ENTRY_T:
  1499. case SCH_BUS_BUS_ENTRY_T:
  1500. saveBusEntry( static_cast<SCH_BUS_ENTRY_BASE*>( item ) );
  1501. break;
  1502. case SCH_LINE_T:
  1503. saveLine( static_cast<SCH_LINE*>( item ) );
  1504. break;
  1505. case SCH_TEXT_T:
  1506. case SCH_LABEL_T:
  1507. case SCH_GLOBAL_LABEL_T:
  1508. case SCH_HIER_LABEL_T:
  1509. saveText( static_cast<SCH_TEXT*>( item ) );
  1510. break;
  1511. default:
  1512. wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
  1513. }
  1514. }
  1515. m_out->Print( 0, "$EndSCHEMATC\n" );
  1516. }
  1517. void SCH_LEGACY_PLUGIN::Format( SELECTION* aSelection, OUTPUTFORMATTER* aFormatter )
  1518. {
  1519. m_out = aFormatter;
  1520. for( unsigned i = 0; i < aSelection->GetSize(); ++i )
  1521. {
  1522. SCH_ITEM* item = (SCH_ITEM*) aSelection->GetItem( i );
  1523. switch( item->Type() )
  1524. {
  1525. case SCH_COMPONENT_T:
  1526. saveComponent( static_cast< SCH_COMPONENT* >( item ) );
  1527. break;
  1528. case SCH_BITMAP_T:
  1529. saveBitmap( static_cast< SCH_BITMAP* >( item ) );
  1530. break;
  1531. case SCH_SHEET_T:
  1532. saveSheet( static_cast< SCH_SHEET* >( item ) );
  1533. break;
  1534. case SCH_JUNCTION_T:
  1535. saveJunction( static_cast< SCH_JUNCTION* >( item ) );
  1536. break;
  1537. case SCH_NO_CONNECT_T:
  1538. saveNoConnect( static_cast< SCH_NO_CONNECT* >( item ) );
  1539. break;
  1540. case SCH_BUS_WIRE_ENTRY_T:
  1541. case SCH_BUS_BUS_ENTRY_T:
  1542. saveBusEntry( static_cast< SCH_BUS_ENTRY_BASE* >( item ) );
  1543. break;
  1544. case SCH_LINE_T:
  1545. saveLine( static_cast< SCH_LINE* >( item ) );
  1546. break;
  1547. case SCH_TEXT_T:
  1548. case SCH_LABEL_T:
  1549. case SCH_GLOBAL_LABEL_T:
  1550. case SCH_HIER_LABEL_T:
  1551. saveText( static_cast< SCH_TEXT* >( item ) );
  1552. break;
  1553. default:
  1554. wxASSERT( "Unexpected schematic object type in SCH_LEGACY_PLUGIN::Format()" );
  1555. }
  1556. }
  1557. }
  1558. void SCH_LEGACY_PLUGIN::saveComponent( SCH_COMPONENT* aComponent )
  1559. {
  1560. std::string name1;
  1561. std::string name2;
  1562. wxArrayString reference_fields;
  1563. static wxString delimiters( wxT( " " ) );
  1564. // This is redundant with the AR entries below, but it makes the files backwards-compatible.
  1565. if( aComponent->GetPathsAndReferences().GetCount() > 0 )
  1566. {
  1567. reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[0], delimiters );
  1568. name1 = toUTFTildaText( reference_fields[1] );
  1569. }
  1570. else
  1571. {
  1572. if( aComponent->GetField( REFERENCE )->GetText().IsEmpty() )
  1573. name1 = toUTFTildaText( aComponent->GetPrefix() );
  1574. else
  1575. name1 = toUTFTildaText( aComponent->GetField( REFERENCE )->GetText() );
  1576. }
  1577. wxString part_name = aComponent->GetLibId().Format();
  1578. if( part_name.size() )
  1579. {
  1580. name2 = toUTFTildaText( part_name );
  1581. }
  1582. else
  1583. {
  1584. name2 = "_NONAME_";
  1585. }
  1586. m_out->Print( 0, "$Comp\n" );
  1587. m_out->Print( 0, "L %s %s\n", name2.c_str(), name1.c_str() );
  1588. // Generate unit number, convert and time stamp
  1589. m_out->Print( 0, "U %d %d %8.8X\n", aComponent->GetUnit(), aComponent->GetConvert(),
  1590. aComponent->GetTimeStamp() );
  1591. // Save the position
  1592. m_out->Print( 0, "P %d %d\n",
  1593. Iu2Mils( aComponent->GetPosition().x ),
  1594. Iu2Mils( aComponent->GetPosition().y ) );
  1595. /* If this is a complex hierarchy; save hierarchical references.
  1596. * but for simple hierarchies it is not necessary.
  1597. * the reference inf is already saved
  1598. * this is useful for old Eeschema version compatibility
  1599. */
  1600. if( aComponent->GetPathsAndReferences().GetCount() > 1 )
  1601. {
  1602. for( unsigned int ii = 0; ii < aComponent->GetPathsAndReferences().GetCount(); ii++ )
  1603. {
  1604. /*format:
  1605. * AR Path="/140/2" Ref="C99" Part="1"
  1606. * where 140 is the uid of the containing sheet
  1607. * and 2 is the timestamp of this component.
  1608. * (timestamps are actually 8 hex chars)
  1609. * Ref is the conventional component reference for this 'path'
  1610. * Part is the conventional component part selection for this 'path'
  1611. */
  1612. reference_fields = wxStringTokenize( aComponent->GetPathsAndReferences()[ii],
  1613. delimiters );
  1614. m_out->Print( 0, "AR Path=\"%s\" Ref=\"%s\" Part=\"%s\" \n",
  1615. TO_UTF8( reference_fields[0] ),
  1616. TO_UTF8( reference_fields[1] ),
  1617. TO_UTF8( reference_fields[2] ) );
  1618. }
  1619. }
  1620. // update the ugly field index, which I would like to see go away someday soon.
  1621. for( int i = 0; i < aComponent->GetFieldCount(); ++i )
  1622. aComponent->GetField( i )->SetId( i );
  1623. // Fixed fields:
  1624. // Save mandatory fields even if they are blank,
  1625. // because the visibility, size and orientation are set from library editor.
  1626. for( unsigned i = 0; i < MANDATORY_FIELDS; ++i )
  1627. saveField( aComponent->GetField( i ) );
  1628. // User defined fields:
  1629. // The *policy* about which user defined fields are part of a symbol is now
  1630. // only in the dialog editors. No policy should be enforced here, simply
  1631. // save all the user defined fields, they are present because a dialog editor
  1632. // thought they should be. If you disagree, go fix the dialog editors.
  1633. for( int i = MANDATORY_FIELDS; i < aComponent->GetFieldCount(); ++i )
  1634. saveField( aComponent->GetField( i ) );
  1635. // Unit number, position, box ( old standard )
  1636. m_out->Print( 0, "\t%-4d %-4d %-4d\n", aComponent->GetUnit(),
  1637. Iu2Mils( aComponent->GetPosition().x ),
  1638. Iu2Mils( aComponent->GetPosition().y ) );
  1639. TRANSFORM transform = aComponent->GetTransform();
  1640. m_out->Print( 0, "\t%-4d %-4d %-4d %-4d\n",
  1641. transform.x1, transform.y1, transform.x2, transform.y2 );
  1642. m_out->Print( 0, "$EndComp\n" );
  1643. }
  1644. void SCH_LEGACY_PLUGIN::saveField( SCH_FIELD* aField )
  1645. {
  1646. char hjustify = 'C';
  1647. if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  1648. hjustify = 'L';
  1649. else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  1650. hjustify = 'R';
  1651. char vjustify = 'C';
  1652. if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  1653. vjustify = 'B';
  1654. else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  1655. vjustify = 'T';
  1656. m_out->Print( 0, "F %d %s %c %-3d %-3d %-3d %4.4X %c %c%c%c",
  1657. aField->GetId(),
  1658. EscapedUTF8( aField->GetText() ).c_str(), // wraps in quotes too
  1659. aField->GetTextAngle() == TEXT_ANGLE_HORIZ ? 'H' : 'V',
  1660. Iu2Mils( aField->GetLibPosition().x ),
  1661. Iu2Mils( aField->GetLibPosition().y ),
  1662. Iu2Mils( aField->GetTextWidth() ),
  1663. !aField->IsVisible(),
  1664. hjustify, vjustify,
  1665. aField->IsItalic() ? 'I' : 'N',
  1666. aField->IsBold() ? 'B' : 'N' );
  1667. // Save field name, if the name is user definable
  1668. if( aField->GetId() >= FIELD1 )
  1669. {
  1670. m_out->Print( 0, " %s", EscapedUTF8( aField->GetName() ).c_str() );
  1671. }
  1672. m_out->Print( 0, "\n" );
  1673. }
  1674. void SCH_LEGACY_PLUGIN::saveBitmap( SCH_BITMAP* aBitmap )
  1675. {
  1676. wxCHECK_RET( aBitmap != NULL, "SCH_BITMAP* is NULL" );
  1677. const wxImage* image = aBitmap->GetImage()->GetImageData();
  1678. wxCHECK_RET( image != NULL, "wxImage* is NULL" );
  1679. m_out->Print( 0, "$Bitmap\n" );
  1680. m_out->Print( 0, "Pos %-4d %-4d\n",
  1681. Iu2Mils( aBitmap->GetPosition().x ),
  1682. Iu2Mils( aBitmap->GetPosition().y ) );
  1683. m_out->Print( 0, "Scale %f\n", aBitmap->GetImage()->GetScale() );
  1684. m_out->Print( 0, "Data\n" );
  1685. wxMemoryOutputStream stream;
  1686. image->SaveFile( stream, wxBITMAP_TYPE_PNG );
  1687. // Write binary data in hexadecimal form (ASCII)
  1688. wxStreamBuffer* buffer = stream.GetOutputStreamBuffer();
  1689. char* begin = (char*) buffer->GetBufferStart();
  1690. for( int ii = 0; begin < buffer->GetBufferEnd(); begin++, ii++ )
  1691. {
  1692. if( ii >= 32 )
  1693. {
  1694. ii = 0;
  1695. m_out->Print( 0, "\n" );
  1696. }
  1697. m_out->Print( 0, "%2.2X ", *begin & 0xFF );
  1698. }
  1699. m_out->Print( 0, "\nEndData\n" );
  1700. m_out->Print( 0, "$EndBitmap\n" );
  1701. }
  1702. void SCH_LEGACY_PLUGIN::saveSheet( SCH_SHEET* aSheet )
  1703. {
  1704. wxCHECK_RET( aSheet != NULL, "SCH_SHEET* is NULL" );
  1705. m_out->Print( 0, "$Sheet\n" );
  1706. m_out->Print( 0, "S %-4d %-4d %-4d %-4d\n",
  1707. Iu2Mils( aSheet->GetPosition().x ), Iu2Mils( aSheet->GetPosition().y ),
  1708. Iu2Mils( aSheet->GetSize().x ), Iu2Mils( aSheet->GetSize().y ) );
  1709. m_out->Print( 0, "U %8.8X\n", aSheet->GetTimeStamp() );
  1710. if( !aSheet->GetName().IsEmpty() )
  1711. m_out->Print( 0, "F0 %s %d\n", EscapedUTF8( aSheet->GetName() ).c_str(),
  1712. Iu2Mils( aSheet->GetSheetNameSize() ) );
  1713. if( !aSheet->GetFileName().IsEmpty() )
  1714. m_out->Print( 0, "F1 %s %d\n", EscapedUTF8( aSheet->GetFileName() ).c_str(),
  1715. Iu2Mils( aSheet->GetFileNameSize() ) );
  1716. for( const SCH_SHEET_PIN* pin : aSheet->GetPins() )
  1717. {
  1718. int type, side;
  1719. if( pin->GetText().IsEmpty() )
  1720. break;
  1721. switch( pin->GetEdge() )
  1722. {
  1723. default:
  1724. case SHEET_LEFT_SIDE: side = 'L'; break;
  1725. case SHEET_RIGHT_SIDE: side = 'R'; break;
  1726. case SHEET_TOP_SIDE: side = 'T'; break;
  1727. case SHEET_BOTTOM_SIDE: side = 'B'; break;
  1728. }
  1729. switch( pin->GetShape() )
  1730. {
  1731. case PINSHEETLABEL_SHAPE::PS_INPUT:
  1732. type = 'I';
  1733. break;
  1734. case PINSHEETLABEL_SHAPE::PS_OUTPUT:
  1735. type = 'O';
  1736. break;
  1737. case PINSHEETLABEL_SHAPE::PS_BIDI:
  1738. type = 'B';
  1739. break;
  1740. case PINSHEETLABEL_SHAPE::PS_TRISTATE:
  1741. type = 'T';
  1742. break;
  1743. default:
  1744. case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED:
  1745. type = 'U';
  1746. break;
  1747. }
  1748. m_out->Print( 0, "F%d %s %c %c %-3d %-3d %-3d\n", pin->GetNumber(),
  1749. EscapedUTF8( pin->GetText() ).c_str(), // supplies wrapping quotes
  1750. type, side, Iu2Mils( pin->GetPosition().x ),
  1751. Iu2Mils( pin->GetPosition().y ),
  1752. Iu2Mils( pin->GetTextWidth() ) );
  1753. }
  1754. m_out->Print( 0, "$EndSheet\n" );
  1755. }
  1756. void SCH_LEGACY_PLUGIN::saveJunction( SCH_JUNCTION* aJunction )
  1757. {
  1758. wxCHECK_RET( aJunction != NULL, "SCH_JUNCTION* is NULL" );
  1759. m_out->Print( 0, "Connection ~ %-4d %-4d\n",
  1760. Iu2Mils( aJunction->GetPosition().x ), Iu2Mils( aJunction->GetPosition().y ) );
  1761. }
  1762. void SCH_LEGACY_PLUGIN::saveNoConnect( SCH_NO_CONNECT* aNoConnect )
  1763. {
  1764. wxCHECK_RET( aNoConnect != NULL, "SCH_NOCONNECT* is NULL" );
  1765. m_out->Print( 0, "NoConn ~ %-4d %-4d\n", Iu2Mils( aNoConnect->GetPosition().x ),
  1766. Iu2Mils( aNoConnect->GetPosition().y ) );
  1767. }
  1768. void SCH_LEGACY_PLUGIN::saveBusEntry( SCH_BUS_ENTRY_BASE* aBusEntry )
  1769. {
  1770. wxCHECK_RET( aBusEntry != NULL, "SCH_BUS_ENTRY_BASE* is NULL" );
  1771. if( aBusEntry->GetLayer() == LAYER_WIRE )
  1772. m_out->Print( 0, "Entry Wire Line\n\t%-4d %-4d %-4d %-4d\n",
  1773. Iu2Mils( aBusEntry->GetPosition().x ),
  1774. Iu2Mils( aBusEntry->GetPosition().y ),
  1775. Iu2Mils( aBusEntry->m_End().x ), Iu2Mils( aBusEntry->m_End().y ) );
  1776. else
  1777. m_out->Print( 0, "Entry Bus Bus\n\t%-4d %-4d %-4d %-4d\n",
  1778. Iu2Mils( aBusEntry->GetPosition().x ),
  1779. Iu2Mils( aBusEntry->GetPosition().y ),
  1780. Iu2Mils( aBusEntry->m_End().x ), Iu2Mils( aBusEntry->m_End().y ) );
  1781. }
  1782. void SCH_LEGACY_PLUGIN::saveLine( SCH_LINE* aLine )
  1783. {
  1784. wxCHECK_RET( aLine != NULL, "SCH_LINE* is NULL" );
  1785. const char* layer = "Notes";
  1786. const char* width = "Line";
  1787. if( aLine->GetLayer() == LAYER_WIRE )
  1788. layer = "Wire";
  1789. else if( aLine->GetLayer() == LAYER_BUS )
  1790. layer = "Bus";
  1791. m_out->Print( 0, "Wire %s %s", layer, width );
  1792. // Write line style (width, type, color) only for non default values
  1793. if( aLine->IsGraphicLine() )
  1794. {
  1795. if( aLine->GetPenSize() != aLine->GetDefaultWidth() )
  1796. m_out->Print( 0, " %s %d", T_WIDTH, Iu2Mils( aLine->GetLineSize() ) );
  1797. if( aLine->GetLineStyle() != aLine->GetDefaultStyle() )
  1798. m_out->Print( 0, " %s %s", T_STYLE,
  1799. SCH_LINE::GetLineStyleName( aLine->GetLineStyle() ) );
  1800. if( aLine->GetLineColor() != aLine->GetDefaultColor() )
  1801. m_out->Print( 0, " %s",
  1802. TO_UTF8( aLine->GetLineColor().ToColour().GetAsString( wxC2S_CSS_SYNTAX ) ) );
  1803. }
  1804. m_out->Print( 0, "\n" );
  1805. m_out->Print( 0, "\t%-4d %-4d %-4d %-4d",
  1806. Iu2Mils( aLine->GetStartPoint().x ), Iu2Mils( aLine->GetStartPoint().y ),
  1807. Iu2Mils( aLine->GetEndPoint().x ), Iu2Mils( aLine->GetEndPoint().y ) );
  1808. m_out->Print( 0, "\n");
  1809. }
  1810. void SCH_LEGACY_PLUGIN::saveText( SCH_TEXT* aText )
  1811. {
  1812. wxCHECK_RET( aText != NULL, "SCH_TEXT* is NULL" );
  1813. const char* italics = "~";
  1814. const char* textType = "Notes";
  1815. if( aText->IsItalic() )
  1816. italics = "Italic";
  1817. wxString text = aText->GetText();
  1818. SCH_LAYER_ID layer = aText->GetLayer();
  1819. if( layer == LAYER_NOTES || layer == LAYER_LOCLABEL )
  1820. {
  1821. if( layer == LAYER_NOTES )
  1822. {
  1823. // For compatibility reasons, the text must be saved in only one text line
  1824. // so replace all EOLs with \\n
  1825. text.Replace( wxT( "\n" ), wxT( "\\n" ) );
  1826. // Here we should have no CR or LF character in line
  1827. // This is not always the case if a multiline text was copied (using a copy/paste
  1828. // function) from a text that uses E.O.L characters that differs from the current
  1829. // EOL format. This is mainly the case under Linux using LF symbol when copying
  1830. // a text from Windows (using CRLF symbol) so we must just remove the extra CR left
  1831. // (or LF left under MacOSX)
  1832. for( unsigned ii = 0; ii < text.Len(); )
  1833. {
  1834. if( text[ii] == 0x0A || text[ii] == 0x0D )
  1835. text.erase( ii, 1 );
  1836. else
  1837. ii++;
  1838. }
  1839. }
  1840. else
  1841. {
  1842. textType = "Label";
  1843. }
  1844. // Local labels must have their spin style inverted for left and right
  1845. int spinStyle = static_cast<int>( aText->GetLabelSpinStyle() );
  1846. if( spinStyle == 0 )
  1847. spinStyle = 2;
  1848. else if( spinStyle == 2 )
  1849. spinStyle = 0;
  1850. m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %d\n%s\n", textType,
  1851. Iu2Mils( aText->GetPosition().x ), Iu2Mils( aText->GetPosition().y ),
  1852. spinStyle,
  1853. Iu2Mils( aText->GetTextWidth() ),
  1854. italics, Iu2Mils( aText->GetThickness() ), TO_UTF8( text ) );
  1855. }
  1856. else if( layer == LAYER_GLOBLABEL || layer == LAYER_HIERLABEL )
  1857. {
  1858. textType = ( layer == LAYER_GLOBLABEL ) ? "GLabel" : "HLabel";
  1859. auto shapeLabelIt = sheetLabelNames.find( aText->GetShape() );
  1860. wxCHECK_RET( shapeLabelIt != sheetLabelNames.end(), "Shape not found in names list" );
  1861. m_out->Print( 0, "Text %s %-4d %-4d %-4d %-4d %s %s %d\n%s\n", textType,
  1862. Iu2Mils( aText->GetPosition().x ), Iu2Mils( aText->GetPosition().y ),
  1863. static_cast<int>( aText->GetLabelSpinStyle() ),
  1864. Iu2Mils( aText->GetTextWidth() ),
  1865. shapeLabelIt->second,
  1866. italics,
  1867. Iu2Mils( aText->GetThickness() ), TO_UTF8( text ) );
  1868. }
  1869. }
  1870. void SCH_LEGACY_PLUGIN::saveBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
  1871. {
  1872. wxCHECK_RET( aAlias != NULL, "BUS_ALIAS* is NULL" );
  1873. wxString members = boost::algorithm::join( aAlias->Members(), " " );
  1874. m_out->Print( 0, "BusAlias %s %s\n",
  1875. TO_UTF8( aAlias->GetName() ), TO_UTF8( members ) );
  1876. }
  1877. int SCH_LEGACY_PLUGIN_CACHE::m_modHash = 1; // starts at 1 and goes up
  1878. SCH_LEGACY_PLUGIN_CACHE::SCH_LEGACY_PLUGIN_CACHE( const wxString& aFullPathAndFileName ) :
  1879. m_fileName( aFullPathAndFileName ),
  1880. m_libFileName( aFullPathAndFileName ),
  1881. m_isWritable( true ),
  1882. m_isModified( false )
  1883. {
  1884. m_versionMajor = -1;
  1885. m_versionMinor = -1;
  1886. m_libType = LIBRARY_TYPE_EESCHEMA;
  1887. }
  1888. SCH_LEGACY_PLUGIN_CACHE::~SCH_LEGACY_PLUGIN_CACHE()
  1889. {
  1890. std::vector< LIB_PART* > rootParts;
  1891. // When the cache is destroyed, all of the alias objects on the heap should be deleted.
  1892. for( LIB_PART_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it )
  1893. delete it->second;
  1894. m_symbols.clear();
  1895. }
  1896. // If m_libFileName is a symlink follow it to the real source file
  1897. wxFileName SCH_LEGACY_PLUGIN_CACHE::GetRealFile() const
  1898. {
  1899. wxFileName fn( m_libFileName );
  1900. #ifndef __WINDOWS__
  1901. if( fn.Exists( wxFILE_EXISTS_SYMLINK ) )
  1902. {
  1903. char buffer[ PATH_MAX + 1 ];
  1904. ssize_t pathLen = readlink( TO_UTF8( fn.GetFullPath() ), buffer, PATH_MAX );
  1905. if( pathLen > 0 )
  1906. {
  1907. buffer[ pathLen ] = '\0';
  1908. fn.Assign( fn.GetPath() + wxT( "/" ) + wxString::FromUTF8( buffer ) );
  1909. fn.Normalize();
  1910. }
  1911. }
  1912. #endif
  1913. return fn;
  1914. }
  1915. wxDateTime SCH_LEGACY_PLUGIN_CACHE::GetLibModificationTime()
  1916. {
  1917. wxFileName fn = GetRealFile();
  1918. // update the writable flag while we have a wxFileName, in a network this
  1919. // is possibly quite dynamic anyway.
  1920. m_isWritable = fn.IsFileWritable();
  1921. return fn.GetModificationTime();
  1922. }
  1923. bool SCH_LEGACY_PLUGIN_CACHE::IsFile( const wxString& aFullPathAndFileName ) const
  1924. {
  1925. return m_fileName == aFullPathAndFileName;
  1926. }
  1927. bool SCH_LEGACY_PLUGIN_CACHE::IsFileChanged() const
  1928. {
  1929. wxFileName fn = GetRealFile();
  1930. if( m_fileModTime.IsValid() && fn.IsOk() && fn.FileExists() )
  1931. return fn.GetModificationTime() != m_fileModTime;
  1932. return false;
  1933. }
  1934. LIB_PART* SCH_LEGACY_PLUGIN_CACHE::removeSymbol( LIB_PART* aPart )
  1935. {
  1936. wxCHECK_MSG( aPart != NULL, NULL, "NULL pointer cannot be removed from library." );
  1937. LIB_PART* firstChild = NULL;
  1938. LIB_PART_MAP::iterator it = m_symbols.find( aPart->GetName() );
  1939. if( it == m_symbols.end() )
  1940. return NULL;
  1941. // If the entry pointer doesn't match the name it is mapped to in the library, we
  1942. // have done something terribly wrong.
  1943. wxCHECK_MSG( *it->second == aPart, NULL,
  1944. "Pointer mismatch while attempting to remove alias entry <" + aPart->GetName() +
  1945. "> from library cache <" + m_libFileName.GetName() + ">." );
  1946. // If the symbol is a root symbol used by other symbols find the first alias that uses
  1947. // the root part and make it the new root.
  1948. if( aPart->IsRoot() )
  1949. {
  1950. for( auto entry : m_symbols )
  1951. {
  1952. if( entry.second->IsAlias()
  1953. && entry.second->GetParent().lock() == aPart->SharedPtr() )
  1954. {
  1955. firstChild = entry.second;
  1956. break;
  1957. }
  1958. }
  1959. if( firstChild )
  1960. {
  1961. for( LIB_ITEM& drawItem : aPart->GetDrawItems() )
  1962. {
  1963. if( drawItem.Type() == LIB_FIELD_T )
  1964. {
  1965. LIB_FIELD& field = static_cast<LIB_FIELD&>( drawItem );
  1966. if( firstChild->FindField( field.GetName() ) )
  1967. continue;
  1968. }
  1969. LIB_ITEM* newItem = (LIB_ITEM*) drawItem.Clone();
  1970. drawItem.SetParent( firstChild );
  1971. firstChild->AddDrawItem( newItem );
  1972. }
  1973. // Reparent the remaining aliases.
  1974. for( auto entry : m_symbols )
  1975. {
  1976. if( entry.second->IsAlias()
  1977. && entry.second->GetParent().lock() == aPart->SharedPtr() )
  1978. entry.second->SetParent( firstChild );
  1979. }
  1980. }
  1981. }
  1982. m_symbols.erase( it );
  1983. delete aPart;
  1984. m_isModified = true;
  1985. ++m_modHash;
  1986. return firstChild;
  1987. }
  1988. void SCH_LEGACY_PLUGIN_CACHE::AddSymbol( const LIB_PART* aPart )
  1989. {
  1990. // aPart is cloned in PART_LIB::AddPart(). The cache takes ownership of aPart.
  1991. wxString name = aPart->GetName();
  1992. LIB_PART_MAP::iterator it = m_symbols.find( name );
  1993. if( it != m_symbols.end() )
  1994. {
  1995. removeSymbol( it->second );
  1996. }
  1997. m_symbols[ name ] = const_cast< LIB_PART* >( aPart );
  1998. m_isModified = true;
  1999. ++m_modHash;
  2000. }
  2001. void SCH_LEGACY_PLUGIN_CACHE::Load()
  2002. {
  2003. if( !m_libFileName.FileExists() )
  2004. {
  2005. THROW_IO_ERROR( wxString::Format( _( "Library file \"%s\" not found." ),
  2006. m_libFileName.GetFullPath() ) );
  2007. }
  2008. wxCHECK_RET( m_libFileName.IsAbsolute(),
  2009. wxString::Format( "Cannot use relative file paths in legacy plugin to "
  2010. "open library \"%s\".", m_libFileName.GetFullPath() ) );
  2011. wxLogTrace( traceSchLegacyPlugin, "Loading legacy symbol file \"%s\"",
  2012. m_libFileName.GetFullPath() );
  2013. FILE_LINE_READER reader( m_libFileName.GetFullPath() );
  2014. if( !reader.ReadLine() )
  2015. THROW_IO_ERROR( _( "unexpected end of file" ) );
  2016. const char* line = reader.Line();
  2017. if( !strCompare( "EESchema-LIBRARY Version", line, &line ) )
  2018. {
  2019. // Old .sym files (which are libraries with only one symbol, used to store and reuse shapes)
  2020. // EESchema-LIB Version x.x SYMBOL. They are valid files.
  2021. if( !strCompare( "EESchema-LIB Version", line, &line ) )
  2022. SCH_PARSE_ERROR( "file is not a valid component or symbol library file", reader, line );
  2023. }
  2024. m_versionMajor = parseInt( reader, line, &line );
  2025. if( *line != '.' )
  2026. SCH_PARSE_ERROR( "invalid file version formatting in header", reader, line );
  2027. line++;
  2028. m_versionMinor = parseInt( reader, line, &line );
  2029. if( m_versionMajor < 1 || m_versionMinor < 0 || m_versionMinor > 99 )
  2030. SCH_PARSE_ERROR( "invalid file version in header", reader, line );
  2031. // Check if this is a symbol library which is the same as a component library but without
  2032. // any alias, documentation, footprint filters, etc.
  2033. if( strCompare( "SYMBOL", line, &line ) )
  2034. {
  2035. // Symbol files add date and time stamp info to the header.
  2036. m_libType = LIBRARY_TYPE_SYMBOL;
  2037. /// @todo Probably should check for a valid date and time stamp even though it's not used.
  2038. }
  2039. else
  2040. {
  2041. m_libType = LIBRARY_TYPE_EESCHEMA;
  2042. }
  2043. while( reader.ReadLine() )
  2044. {
  2045. line = reader.Line();
  2046. if( *line == '#' || isspace( *line ) ) // Skip comments and blank lines.
  2047. continue;
  2048. // Headers where only supported in older library file formats.
  2049. if( m_libType == LIBRARY_TYPE_EESCHEMA && strCompare( "$HEADER", line ) )
  2050. loadHeader( reader );
  2051. if( strCompare( "DEF", line ) )
  2052. {
  2053. // Read one DEF/ENDDEF part entry from library:
  2054. LIB_PART* part = LoadPart( reader, m_versionMajor, m_versionMinor, &m_symbols );
  2055. m_symbols[ part->GetName() ] = part;
  2056. }
  2057. }
  2058. ++m_modHash;
  2059. // Remember the file modification time of library file when the
  2060. // cache snapshot was made, so that in a networked environment we will
  2061. // reload the cache as needed.
  2062. m_fileModTime = GetLibModificationTime();
  2063. if( USE_OLD_DOC_FILE_FORMAT( m_versionMajor, m_versionMinor ) )
  2064. loadDocs();
  2065. }
  2066. void SCH_LEGACY_PLUGIN_CACHE::loadDocs()
  2067. {
  2068. const char* line;
  2069. wxString text;
  2070. wxString aliasName;
  2071. wxFileName fn = m_libFileName;
  2072. LIB_PART* symbol = NULL;;
  2073. fn.SetExt( DOC_EXT );
  2074. // Not all libraries will have a document file.
  2075. if( !fn.FileExists() )
  2076. return;
  2077. if( !fn.IsFileReadable() )
  2078. THROW_IO_ERROR( wxString::Format( _( "user does not have permission to read library "
  2079. "document file \"%s\"" ), fn.GetFullPath() ) );
  2080. FILE_LINE_READER reader( fn.GetFullPath() );
  2081. line = reader.ReadLine();
  2082. if( !line )
  2083. THROW_IO_ERROR( _( "symbol document library file is empty" ) );
  2084. if( !strCompare( DOCFILE_IDENT, line, &line ) )
  2085. SCH_PARSE_ERROR( "invalid document library file version formatting in header",
  2086. reader, line );
  2087. while( reader.ReadLine() )
  2088. {
  2089. line = reader.Line();
  2090. if( *line == '#' ) // Comment line.
  2091. continue;
  2092. if( !strCompare( "$CMP", line, &line ) != 0 )
  2093. SCH_PARSE_ERROR( "$CMP command expected", reader, line );
  2094. aliasName = wxString::FromUTF8( line );
  2095. aliasName.Trim();
  2096. aliasName = LIB_ID::FixIllegalChars( aliasName, LIB_ID::ID_SCH );
  2097. LIB_PART_MAP::iterator it = m_symbols.find( aliasName );
  2098. if( it == m_symbols.end() )
  2099. wxLogWarning( "Symbol '%s' not found in library:\n\n"
  2100. "'%s'\n\nat line %d offset %d", aliasName, fn.GetFullPath(),
  2101. reader.LineNumber(), (int) (line - reader.Line() ) );
  2102. else
  2103. symbol = it->second;
  2104. // Read the curent alias associated doc.
  2105. // if the alias does not exist, just skip the description
  2106. // (Can happen if a .dcm is not synchronized with the corresponding .lib file)
  2107. while( reader.ReadLine() )
  2108. {
  2109. line = reader.Line();
  2110. if( !line )
  2111. SCH_PARSE_ERROR( "unexpected end of file", reader, line );
  2112. if( strCompare( "$ENDCMP", line, &line ) )
  2113. break;
  2114. text = FROM_UTF8( line + 2 );
  2115. // Remove spaces at eol, and eol chars:
  2116. text = text.Trim();
  2117. switch( line[0] )
  2118. {
  2119. case 'D':
  2120. if( symbol )
  2121. symbol->SetDescription( text );
  2122. break;
  2123. case 'K':
  2124. if( symbol )
  2125. symbol->SetKeyWords( text );
  2126. break;
  2127. case 'F':
  2128. if( symbol )
  2129. {
  2130. symbol->SetDocFileName( text );
  2131. symbol->GetField( DATASHEET )->SetText( text );
  2132. }
  2133. break;
  2134. case 0:
  2135. case '\n':
  2136. case '\r':
  2137. case '#':
  2138. // Empty line or commment
  2139. break;
  2140. default:
  2141. SCH_PARSE_ERROR( "expected token in symbol definition", reader, line );
  2142. }
  2143. }
  2144. }
  2145. }
  2146. void SCH_LEGACY_PLUGIN_CACHE::loadHeader( FILE_LINE_READER& aReader )
  2147. {
  2148. const char* line = aReader.Line();
  2149. wxASSERT( strCompare( "$HEADER", line, &line ) );
  2150. while( aReader.ReadLine() )
  2151. {
  2152. line = (char*) aReader;
  2153. // The time stamp saved in old library files is not used or saved in the latest
  2154. // library file version.
  2155. if( strCompare( "TimeStamp", line, &line ) )
  2156. continue;
  2157. else if( strCompare( "$ENDHEADER", line, &line ) )
  2158. return;
  2159. }
  2160. SCH_PARSE_ERROR( "$ENDHEADER not found", aReader, line );
  2161. }
  2162. LIB_PART* SCH_LEGACY_PLUGIN_CACHE::LoadPart( LINE_READER& aReader, int aMajorVersion,
  2163. int aMinorVersion, LIB_PART_MAP* aMap )
  2164. {
  2165. const char* line = aReader.Line();
  2166. while( *line == '#' )
  2167. aReader.ReadLine();
  2168. if( !strCompare( "DEF", line, &line ) )
  2169. SCH_PARSE_ERROR( "invalid symbol definition", aReader, line );
  2170. long num;
  2171. size_t pos = 4; // "DEF" plus the first space.
  2172. wxString utf8Line = wxString::FromUTF8( line );
  2173. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2174. if( tokens.CountTokens() < 8 )
  2175. SCH_PARSE_ERROR( "invalid symbol definition", aReader, line );
  2176. // Read DEF line:
  2177. std::unique_ptr< LIB_PART > part( new LIB_PART( wxEmptyString ) );
  2178. wxString name, prefix, tmp;
  2179. name = tokens.GetNextToken();
  2180. pos += name.size() + 1;
  2181. prefix = tokens.GetNextToken();
  2182. pos += prefix.size() + 1;
  2183. tmp = tokens.GetNextToken();
  2184. pos += tmp.size() + 1; // NumOfPins, unused.
  2185. tmp = tokens.GetNextToken(); // Pin name offset.
  2186. if( !tmp.ToLong( &num ) )
  2187. THROW_PARSE_ERROR( "invalid pin offset", aReader.GetSource(), aReader.Line(),
  2188. aReader.LineNumber(), pos );
  2189. pos += tmp.size() + 1;
  2190. part->SetPinNameOffset( Mils2Iu( (int)num ) );
  2191. tmp = tokens.GetNextToken(); // Show pin numbers.
  2192. if( !( tmp == "Y" || tmp == "N") )
  2193. THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
  2194. aReader.LineNumber(), pos );
  2195. pos += tmp.size() + 1;
  2196. part->SetShowPinNumbers( ( tmp == "N" ) ? false : true );
  2197. tmp = tokens.GetNextToken(); // Show pin names.
  2198. if( !( tmp == "Y" || tmp == "N") )
  2199. THROW_PARSE_ERROR( "expected Y or N", aReader.GetSource(), aReader.Line(),
  2200. aReader.LineNumber(), pos );
  2201. pos += tmp.size() + 1;
  2202. part->SetShowPinNames( ( tmp == "N" ) ? false : true );
  2203. tmp = tokens.GetNextToken(); // Number of units.
  2204. if( !tmp.ToLong( &num ) )
  2205. THROW_PARSE_ERROR( "invalid unit count", aReader.GetSource(), aReader.Line(),
  2206. aReader.LineNumber(), pos );
  2207. pos += tmp.size() + 1;
  2208. part->SetUnitCount( (int)num );
  2209. // Ensure m_unitCount is >= 1. Could be read as 0 in old libraries.
  2210. if( part->GetUnitCount() < 1 )
  2211. part->SetUnitCount( 1 );
  2212. // Copy part name and prefix.
  2213. // The root alias is added to the alias list by SetName() which is called by SetText().
  2214. if( name.IsEmpty() )
  2215. {
  2216. part->SetName( "~" );
  2217. }
  2218. else if( name[0] != '~' )
  2219. {
  2220. part->SetName( name );
  2221. }
  2222. else
  2223. {
  2224. part->SetName( name.Right( name.Length() - 1 ) );
  2225. part->GetValueField().SetVisible( false );
  2226. }
  2227. // Don't set the library alias, this is determined by the symbol library table.
  2228. part->SetLibId( LIB_ID( wxEmptyString, part->GetName() ) );
  2229. LIB_FIELD& reference = part->GetReferenceField();
  2230. if( prefix == "~" )
  2231. {
  2232. reference.Empty();
  2233. reference.SetVisible( false );
  2234. }
  2235. else
  2236. {
  2237. reference.SetText( prefix );
  2238. }
  2239. // In version 2.2 and earlier, this parameter was a '0' which was just a place holder.
  2240. // The was no concept of interchangeable multiple unit symbols.
  2241. if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
  2242. && LIB_VERSION( aMajorVersion, aMinorVersion ) <= LIB_VERSION( 2, 2 ) )
  2243. {
  2244. // Nothing needs to be set since the default setting for symbols with multiple
  2245. // units were never interchangeable. Just parse the 0 an move on.
  2246. tmp = tokens.GetNextToken();
  2247. pos += tmp.size() + 1;
  2248. }
  2249. else
  2250. {
  2251. tmp = tokens.GetNextToken();
  2252. if( tmp == "L" )
  2253. part->LockUnits( true );
  2254. else if( tmp == "F" || tmp == "0" )
  2255. part->LockUnits( false );
  2256. else
  2257. THROW_PARSE_ERROR( "expected L, F, or 0", aReader.GetSource(), aReader.Line(),
  2258. aReader.LineNumber(), pos );
  2259. pos += tmp.size() + 1;
  2260. }
  2261. // There is the optional power component flag.
  2262. if( tokens.HasMoreTokens() )
  2263. {
  2264. tmp = tokens.GetNextToken();
  2265. if( tmp == "P" )
  2266. part->SetPower();
  2267. else if( tmp == "N" )
  2268. part->SetNormal();
  2269. else
  2270. THROW_PARSE_ERROR( "expected P or N", aReader.GetSource(), aReader.Line(),
  2271. aReader.LineNumber(), pos );
  2272. }
  2273. line = aReader.ReadLine();
  2274. // Read lines until "ENDDEF" is found.
  2275. while( line )
  2276. {
  2277. if( *line == '#' ) // Comment
  2278. ;
  2279. else if( strCompare( "Ti", line, &line ) ) // Modification date is ignored.
  2280. continue;
  2281. else if( strCompare( "ALIAS", line, &line ) ) // Aliases
  2282. loadAliases( part, aReader, aMap );
  2283. else if( *line == 'F' ) // Fields
  2284. loadField( part, aReader );
  2285. else if( strCompare( "DRAW", line, &line ) ) // Drawing objects.
  2286. loadDrawEntries( part, aReader, aMajorVersion, aMinorVersion );
  2287. else if( strCompare( "$FPLIST", line, &line ) ) // Footprint filter list
  2288. loadFootprintFilters( part, aReader );
  2289. else if( strCompare( "ENDDEF", line, &line ) ) // End of part description
  2290. {
  2291. return part.release();
  2292. }
  2293. line = aReader.ReadLine();
  2294. }
  2295. SCH_PARSE_ERROR( "missing ENDDEF", aReader, line );
  2296. }
  2297. void SCH_LEGACY_PLUGIN_CACHE::loadAliases( std::unique_ptr<LIB_PART>& aPart,
  2298. LINE_READER& aReader,
  2299. LIB_PART_MAP* aMap )
  2300. {
  2301. wxString newAliasName;
  2302. const char* line = aReader.Line();
  2303. wxCHECK_RET( strCompare( "ALIAS", line, &line ), "Invalid ALIAS section" );
  2304. wxString utf8Line = wxString::FromUTF8( line );
  2305. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2306. // Parse the ALIAS list.
  2307. while( tokens.HasMoreTokens() )
  2308. {
  2309. newAliasName = tokens.GetNextToken();
  2310. if( aMap )
  2311. {
  2312. LIB_PART* newPart = new LIB_PART( newAliasName );
  2313. newPart->SetParent( aPart.get() );
  2314. // This will prevent duplicate aliases.
  2315. (*aMap)[ newPart->GetName() ] = newPart;
  2316. }
  2317. }
  2318. }
  2319. void SCH_LEGACY_PLUGIN_CACHE::loadField( std::unique_ptr<LIB_PART>& aPart,
  2320. LINE_READER& aReader )
  2321. {
  2322. const char* line = aReader.Line();
  2323. wxCHECK_RET( *line == 'F', "Invalid field line" );
  2324. wxString text;
  2325. int id;
  2326. if( sscanf( line + 1, "%d", &id ) != 1 || id < 0 )
  2327. SCH_PARSE_ERROR( "invalid field ID", aReader, line + 1 );
  2328. LIB_FIELD* field;
  2329. if( (unsigned) id < MANDATORY_FIELDS )
  2330. {
  2331. field = aPart->GetField( id );
  2332. // this will fire only if somebody broke a constructor or editor.
  2333. // MANDATORY_FIELDS are always present in ram resident components, no
  2334. // exceptions, and they always have their names set, even fixed fields.
  2335. wxASSERT( field );
  2336. }
  2337. else
  2338. {
  2339. field = new LIB_FIELD( aPart.get(), id );
  2340. aPart->AddDrawItem( field );
  2341. }
  2342. // Skip to the first double quote.
  2343. while( *line != '"' && *line != 0 )
  2344. line++;
  2345. if( *line == 0 )
  2346. SCH_PARSE_ERROR( _( "unexpected end of line" ), aReader, line );
  2347. parseQuotedString( text, aReader, line, &line, true );
  2348. field->SetText( text );
  2349. // Doctor the *.lib file field which has a "~" in blank fields. New saves will
  2350. // not save like this.
  2351. if( text.size() == 1 && text[0] == '~' )
  2352. field->SetText( wxEmptyString );
  2353. else
  2354. field->SetText( text );
  2355. wxPoint pos;
  2356. pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2357. pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2358. field->SetPosition( pos );
  2359. wxSize textSize;
  2360. textSize.x = textSize.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2361. field->SetTextSize( textSize );
  2362. char textOrient = parseChar( aReader, line, &line );
  2363. if( textOrient == 'H' )
  2364. field->SetTextAngle( TEXT_ANGLE_HORIZ );
  2365. else if( textOrient == 'V' )
  2366. field->SetTextAngle( TEXT_ANGLE_VERT );
  2367. else
  2368. SCH_PARSE_ERROR( "invalid field text orientation parameter", aReader, line );
  2369. char textVisible = parseChar( aReader, line, &line );
  2370. if( textVisible == 'V' )
  2371. field->SetVisible( true );
  2372. else if ( textVisible == 'I' )
  2373. field->SetVisible( false );
  2374. else
  2375. SCH_PARSE_ERROR( "invalid field text visibility parameter", aReader, line );
  2376. // It may be technically correct to use the library version to determine if the field text
  2377. // attributes are present. If anyone knows if that is valid and what version that would be,
  2378. // please change this to test the library version rather than an EOL or the quoted string
  2379. // of the field name.
  2380. if( *line != 0 && *line != '"' )
  2381. {
  2382. char textHJustify = parseChar( aReader, line, &line );
  2383. if( textHJustify == 'C' )
  2384. field->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
  2385. else if( textHJustify == 'L' )
  2386. field->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  2387. else if( textHJustify == 'R' )
  2388. field->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  2389. else
  2390. SCH_PARSE_ERROR( "invalid field text horizontal justification", aReader, line );
  2391. wxString attributes;
  2392. parseUnquotedString( attributes, aReader, line, &line );
  2393. size_t attrSize = attributes.size();
  2394. if( !(attrSize == 3 || attrSize == 1 ) )
  2395. SCH_PARSE_ERROR( "invalid field text attributes size", aReader, line );
  2396. switch( (wxChar) attributes[0] )
  2397. {
  2398. case 'C': field->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
  2399. case 'B': field->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
  2400. case 'T': field->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
  2401. default: SCH_PARSE_ERROR( "invalid field text vertical justification", aReader, line );
  2402. }
  2403. if( attrSize == 3 )
  2404. {
  2405. wxChar attr_1 = attributes[1];
  2406. wxChar attr_2 = attributes[2];
  2407. if( attr_1 == 'I' ) // Italic
  2408. field->SetItalic( true );
  2409. else if( attr_1 != 'N' ) // No italics is default, check for error.
  2410. SCH_PARSE_ERROR( "invalid field text italic parameter", aReader, line );
  2411. if ( attr_2 == 'B' ) // Bold
  2412. field->SetBold( true );
  2413. else if( attr_2 != 'N' ) // No bold is default, check for error.
  2414. SCH_PARSE_ERROR( "invalid field text bold parameter", aReader, line );
  2415. }
  2416. }
  2417. // Fields in RAM must always have names.
  2418. if( (unsigned) id < MANDATORY_FIELDS )
  2419. {
  2420. // Fields in RAM must always have names, because we are trying to get
  2421. // less dependent on field ids and more dependent on names.
  2422. // Plus assumptions are made in the field editors.
  2423. field->m_name = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
  2424. // Ensure the VALUE field = the part name (can be not the case
  2425. // with malformed libraries: edited by hand, or converted from other tools)
  2426. if( id == VALUE )
  2427. field->SetText( aPart->GetName() );
  2428. }
  2429. else
  2430. {
  2431. parseQuotedString( field->m_name, aReader, line, &line, true ); // Optional.
  2432. }
  2433. }
  2434. void SCH_LEGACY_PLUGIN_CACHE::loadDrawEntries( std::unique_ptr<LIB_PART>& aPart,
  2435. LINE_READER& aReader,
  2436. int aMajorVersion,
  2437. int aMinorVersion )
  2438. {
  2439. const char* line = aReader.Line();
  2440. wxCHECK_RET( strCompare( "DRAW", line, &line ), "Invalid DRAW section" );
  2441. line = aReader.ReadLine();
  2442. while( line )
  2443. {
  2444. if( strCompare( "ENDDRAW", line, &line ) )
  2445. return;
  2446. switch( line[0] )
  2447. {
  2448. case 'A': // Arc
  2449. aPart->AddDrawItem( loadArc( aPart, aReader ) );
  2450. break;
  2451. case 'C': // Circle
  2452. aPart->AddDrawItem( loadCircle( aPart, aReader ) );
  2453. break;
  2454. case 'T': // Text
  2455. aPart->AddDrawItem( loadText( aPart, aReader, aMajorVersion, aMinorVersion ) );
  2456. break;
  2457. case 'S': // Square
  2458. aPart->AddDrawItem( loadRectangle( aPart, aReader ) );
  2459. break;
  2460. case 'X': // Pin Description
  2461. aPart->AddDrawItem( loadPin( aPart, aReader ) );
  2462. break;
  2463. case 'P': // Polyline
  2464. aPart->AddDrawItem( loadPolyLine( aPart, aReader ) );
  2465. break;
  2466. case 'B': // Bezier Curves
  2467. aPart->AddDrawItem( loadBezier( aPart, aReader ) );
  2468. break;
  2469. case '#': // Comment
  2470. case '\n': // Empty line
  2471. case '\r':
  2472. case 0:
  2473. break;
  2474. default:
  2475. SCH_PARSE_ERROR( "undefined DRAW entry", aReader, line );
  2476. }
  2477. line = aReader.ReadLine();
  2478. }
  2479. SCH_PARSE_ERROR( "file ended prematurely loading component draw element", aReader, line );
  2480. }
  2481. FILL_T SCH_LEGACY_PLUGIN_CACHE::parseFillMode( LINE_READER& aReader, const char* aLine,
  2482. const char** aOutput )
  2483. {
  2484. switch( parseChar( aReader, aLine, aOutput ) )
  2485. {
  2486. case 'F': return FILLED_SHAPE;
  2487. case 'f': return FILLED_WITH_BG_BODYCOLOR;
  2488. case 'N': return NO_FILL;
  2489. default: SCH_PARSE_ERROR( "invalid fill type, expected f, F, or N", aReader, aLine );
  2490. }
  2491. }
  2492. LIB_ARC* SCH_LEGACY_PLUGIN_CACHE::loadArc( std::unique_ptr<LIB_PART>& aPart,
  2493. LINE_READER& aReader )
  2494. {
  2495. const char* line = aReader.Line();
  2496. wxCHECK_MSG( strCompare( "A", line, &line ), NULL, "Invalid LIB_ARC definition" );
  2497. LIB_ARC* arc = new LIB_ARC( aPart.get() );
  2498. wxPoint center;
  2499. center.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2500. center.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2501. arc->SetPosition( center );
  2502. arc->SetRadius( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2503. int angle1 = parseInt( aReader, line, &line );
  2504. int angle2 = parseInt( aReader, line, &line );
  2505. NORMALIZE_ANGLE_POS( angle1 );
  2506. NORMALIZE_ANGLE_POS( angle2 );
  2507. arc->SetFirstRadiusAngle( angle1 );
  2508. arc->SetSecondRadiusAngle( angle2 );
  2509. arc->SetUnit( parseInt( aReader, line, &line ) );
  2510. arc->SetConvert( parseInt( aReader, line, &line ) );
  2511. arc->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2512. // Old libraries (version <= 2.2) do not have always this FILL MODE param
  2513. // when fill mode is no fill (default mode).
  2514. if( *line != 0 )
  2515. arc->SetFillMode( parseFillMode( aReader, line, &line ) );
  2516. // Actual Coordinates of arc ends are read from file
  2517. if( *line != 0 )
  2518. {
  2519. wxPoint arcStart, arcEnd;
  2520. arcStart.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2521. arcStart.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2522. arcEnd.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2523. arcEnd.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2524. arc->SetStart( arcStart );
  2525. arc->SetEnd( arcEnd );
  2526. }
  2527. else
  2528. {
  2529. // Actual Coordinates of arc ends are not read from file
  2530. // (old library), calculate them
  2531. wxPoint arcStart( arc->GetRadius(), 0 );
  2532. wxPoint arcEnd( arc->GetRadius(), 0 );
  2533. RotatePoint( &arcStart.x, &arcStart.y, -angle1 );
  2534. arcStart += arc->GetPosition();
  2535. arc->SetStart( arcStart );
  2536. RotatePoint( &arcEnd.x, &arcEnd.y, -angle2 );
  2537. arcEnd += arc->GetPosition();
  2538. arc->SetEnd( arcEnd );
  2539. }
  2540. return arc;
  2541. }
  2542. LIB_CIRCLE* SCH_LEGACY_PLUGIN_CACHE::loadCircle( std::unique_ptr<LIB_PART>& aPart,
  2543. LINE_READER& aReader )
  2544. {
  2545. const char* line = aReader.Line();
  2546. wxCHECK_MSG( strCompare( "C", line, &line ), NULL, "Invalid LIB_CIRCLE definition" );
  2547. LIB_CIRCLE* circle = new LIB_CIRCLE( aPart.get() );
  2548. wxPoint center;
  2549. center.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2550. center.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2551. circle->SetPosition( center );
  2552. circle->SetRadius( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2553. circle->SetUnit( parseInt( aReader, line, &line ) );
  2554. circle->SetConvert( parseInt( aReader, line, &line ) );
  2555. circle->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2556. if( *line != 0 )
  2557. circle->SetFillMode( parseFillMode( aReader, line, &line ) );
  2558. return circle;
  2559. }
  2560. LIB_TEXT* SCH_LEGACY_PLUGIN_CACHE::loadText( std::unique_ptr<LIB_PART>& aPart,
  2561. LINE_READER& aReader,
  2562. int aMajorVersion,
  2563. int aMinorVersion )
  2564. {
  2565. const char* line = aReader.Line();
  2566. wxCHECK_MSG( strCompare( "T", line, &line ), NULL, "Invalid LIB_TEXT definition" );
  2567. LIB_TEXT* text = new LIB_TEXT( aPart.get() );
  2568. text->SetTextAngle( (double) parseInt( aReader, line, &line ) );
  2569. wxPoint center;
  2570. center.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2571. center.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2572. text->SetPosition( center );
  2573. wxSize size;
  2574. size.x = size.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2575. text->SetTextSize( size );
  2576. text->SetVisible( !parseInt( aReader, line, &line ) );
  2577. text->SetUnit( parseInt( aReader, line, &line ) );
  2578. text->SetConvert( parseInt( aReader, line, &line ) );
  2579. wxString str;
  2580. // If quoted string loading fails, load as not quoted string.
  2581. if( *line == '"' )
  2582. parseQuotedString( str, aReader, line, &line );
  2583. else
  2584. {
  2585. parseUnquotedString( str, aReader, line, &line );
  2586. // In old libs, "spaces" are replaced by '~' in unquoted strings:
  2587. str.Replace( "~", " " );
  2588. }
  2589. if( !str.IsEmpty() )
  2590. {
  2591. // convert two apostrophes back to double quote
  2592. str.Replace( "''", "\"" );
  2593. }
  2594. text->SetText( str );
  2595. // Here things are murky and not well defined. At some point it appears the format
  2596. // was changed to add text properties. However rather than add the token to the end of
  2597. // the text definition, it was added after the string and no mention if the file
  2598. // verion was bumped or not so this code make break on very old component libraries.
  2599. //
  2600. // Update: apparently even in the latest version this can be different so added a test
  2601. // for end of line before checking for the text properties.
  2602. if( LIB_VERSION( aMajorVersion, aMinorVersion ) > 0
  2603. && LIB_VERSION( aMajorVersion, aMinorVersion ) > LIB_VERSION( 2, 0 ) && !is_eol( *line ) )
  2604. {
  2605. if( strCompare( "Italic", line, &line ) )
  2606. text->SetItalic( true );
  2607. else if( !strCompare( "Normal", line, &line ) )
  2608. SCH_PARSE_ERROR( "invalid text stype, expected 'Normal' or 'Italic'", aReader, line );
  2609. if( parseInt( aReader, line, &line ) > 0 )
  2610. text->SetBold( true );
  2611. // Some old libaries version > 2.0 do not have these options for text justification:
  2612. if( !is_eol( *line ) )
  2613. {
  2614. switch( parseChar( aReader, line, &line ) )
  2615. {
  2616. case 'L': text->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
  2617. case 'C': text->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break;
  2618. case 'R': text->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
  2619. default: SCH_PARSE_ERROR( "invalid horizontal text justication; expected L, C, or R",
  2620. aReader, line );
  2621. }
  2622. switch( parseChar( aReader, line, &line ) )
  2623. {
  2624. case 'T': text->SetVertJustify( GR_TEXT_VJUSTIFY_TOP ); break;
  2625. case 'C': text->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER ); break;
  2626. case 'B': text->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM ); break;
  2627. default: SCH_PARSE_ERROR( "invalid vertical text justication; expected T, C, or B",
  2628. aReader, line );
  2629. }
  2630. }
  2631. }
  2632. return text;
  2633. }
  2634. LIB_RECTANGLE* SCH_LEGACY_PLUGIN_CACHE::loadRectangle( std::unique_ptr<LIB_PART>& aPart,
  2635. LINE_READER& aReader )
  2636. {
  2637. const char* line = aReader.Line();
  2638. wxCHECK_MSG( strCompare( "S", line, &line ), NULL, "Invalid LIB_RECTANGLE definition" );
  2639. LIB_RECTANGLE* rectangle = new LIB_RECTANGLE( aPart.get() );
  2640. wxPoint pos;
  2641. pos.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2642. pos.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2643. rectangle->SetPosition( pos );
  2644. wxPoint end;
  2645. end.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2646. end.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2647. rectangle->SetEnd( end );
  2648. rectangle->SetUnit( parseInt( aReader, line, &line ) );
  2649. rectangle->SetConvert( parseInt( aReader, line, &line ) );
  2650. rectangle->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2651. if( *line != 0 )
  2652. rectangle->SetFillMode( parseFillMode( aReader, line, &line ) );
  2653. return rectangle;
  2654. }
  2655. LIB_PIN* SCH_LEGACY_PLUGIN_CACHE::loadPin( std::unique_ptr<LIB_PART>& aPart,
  2656. LINE_READER& aReader )
  2657. {
  2658. const char* line = aReader.Line();
  2659. wxCHECK_MSG( strCompare( "X", line, &line ), NULL, "Invalid LIB_PIN definition" );
  2660. LIB_PIN* pin = new LIB_PIN( aPart.get() );
  2661. size_t pos = 2; // "X" plus ' ' space character.
  2662. wxString tmp;
  2663. wxString utf8Line = wxString::FromUTF8( line );
  2664. wxStringTokenizer tokens( utf8Line, " \r\n\t" );
  2665. if( tokens.CountTokens() < 11 )
  2666. SCH_PARSE_ERROR( "invalid pin definition", aReader, line );
  2667. pin->m_name = tokens.GetNextToken();
  2668. pos += pin->m_name.size() + 1;
  2669. pin->m_number = tokens.GetNextToken();
  2670. pos += pin->m_number.size() + 1;
  2671. long num;
  2672. wxPoint position;
  2673. tmp = tokens.GetNextToken();
  2674. if( !tmp.ToLong( &num ) )
  2675. THROW_PARSE_ERROR( "invalid pin X coordinate", aReader.GetSource(), aReader.Line(),
  2676. aReader.LineNumber(), pos );
  2677. pos += tmp.size() + 1;
  2678. position.x = Mils2Iu( (int) num );
  2679. tmp = tokens.GetNextToken();
  2680. if( !tmp.ToLong( &num ) )
  2681. THROW_PARSE_ERROR( "invalid pin Y coordinate", aReader.GetSource(), aReader.Line(),
  2682. aReader.LineNumber(), pos );
  2683. pos += tmp.size() + 1;
  2684. position.y = Mils2Iu( (int) num );
  2685. pin->m_position = position;
  2686. tmp = tokens.GetNextToken();
  2687. if( !tmp.ToLong( &num ) )
  2688. THROW_PARSE_ERROR( "invalid pin length", aReader.GetSource(), aReader.Line(),
  2689. aReader.LineNumber(), pos );
  2690. pos += tmp.size() + 1;
  2691. pin->m_length = Mils2Iu( (int) num );
  2692. tmp = tokens.GetNextToken();
  2693. if( tmp.size() > 1 )
  2694. THROW_PARSE_ERROR( "invalid pin orientation", aReader.GetSource(), aReader.Line(),
  2695. aReader.LineNumber(), pos );
  2696. pos += tmp.size() + 1;
  2697. pin->m_orientation = tmp[0];
  2698. tmp = tokens.GetNextToken();
  2699. if( !tmp.ToLong( &num ) )
  2700. THROW_PARSE_ERROR( "invalid pin number text size", aReader.GetSource(), aReader.Line(),
  2701. aReader.LineNumber(), pos );
  2702. pos += tmp.size() + 1;
  2703. pin->m_numTextSize = Mils2Iu( (int) num );
  2704. tmp = tokens.GetNextToken();
  2705. if( !tmp.ToLong( &num ) )
  2706. THROW_PARSE_ERROR( "invalid pin name text size", aReader.GetSource(), aReader.Line(),
  2707. aReader.LineNumber(), pos );
  2708. pos += tmp.size() + 1;
  2709. pin->m_nameTextSize = Mils2Iu( (int) num );
  2710. tmp = tokens.GetNextToken();
  2711. if( !tmp.ToLong( &num ) )
  2712. THROW_PARSE_ERROR( "invalid pin unit", aReader.GetSource(), aReader.Line(),
  2713. aReader.LineNumber(), pos );
  2714. pos += tmp.size() + 1;
  2715. pin->m_Unit = (int) num;
  2716. tmp = tokens.GetNextToken();
  2717. if( !tmp.ToLong( &num ) )
  2718. THROW_PARSE_ERROR( "invalid pin alternate body type", aReader.GetSource(), aReader.Line(),
  2719. aReader.LineNumber(), pos );
  2720. pos += tmp.size() + 1;
  2721. pin->m_Convert = (int) num;
  2722. tmp = tokens.GetNextToken();
  2723. if( tmp.size() != 1 )
  2724. THROW_PARSE_ERROR( "invalid pin type", aReader.GetSource(), aReader.Line(),
  2725. aReader.LineNumber(), pos );
  2726. pos += tmp.size() + 1;
  2727. char type = tmp[0];
  2728. wxString attributes;
  2729. switch( type )
  2730. {
  2731. case 'I':
  2732. pin->m_type = ELECTRICAL_PINTYPE::PT_INPUT;
  2733. break;
  2734. case 'O':
  2735. pin->m_type = ELECTRICAL_PINTYPE::PT_OUTPUT;
  2736. break;
  2737. case 'B':
  2738. pin->m_type = ELECTRICAL_PINTYPE::PT_BIDI;
  2739. break;
  2740. case 'T':
  2741. pin->m_type = ELECTRICAL_PINTYPE::PT_TRISTATE;
  2742. break;
  2743. case 'P':
  2744. pin->m_type = ELECTRICAL_PINTYPE::PT_PASSIVE;
  2745. break;
  2746. case 'U':
  2747. pin->m_type = ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
  2748. break;
  2749. case 'W':
  2750. pin->m_type = ELECTRICAL_PINTYPE::PT_POWER_IN;
  2751. break;
  2752. case 'w':
  2753. pin->m_type = ELECTRICAL_PINTYPE::PT_POWER_OUT;
  2754. break;
  2755. case 'C':
  2756. pin->m_type = ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
  2757. break;
  2758. case 'E':
  2759. pin->m_type = ELECTRICAL_PINTYPE::PT_OPENEMITTER;
  2760. break;
  2761. case 'N':
  2762. pin->m_type = ELECTRICAL_PINTYPE::PT_NC;
  2763. break;
  2764. default:
  2765. THROW_PARSE_ERROR( "unknown pin type", aReader.GetSource(), aReader.Line(),
  2766. aReader.LineNumber(), pos );
  2767. }
  2768. // Optional
  2769. if( tokens.HasMoreTokens() ) /* Special Symbol defined */
  2770. {
  2771. tmp = tokens.GetNextToken();
  2772. enum
  2773. {
  2774. INVERTED = 1 << 0,
  2775. CLOCK = 1 << 1,
  2776. LOWLEVEL_IN = 1 << 2,
  2777. LOWLEVEL_OUT = 1 << 3,
  2778. FALLING_EDGE = 1 << 4,
  2779. NONLOGIC = 1 << 5
  2780. };
  2781. int flags = 0;
  2782. for( int j = tmp.size(); j > 0; )
  2783. {
  2784. switch( tmp[--j].GetValue() )
  2785. {
  2786. case '~': break;
  2787. case 'N': pin->m_attributes |= PIN_INVISIBLE; break;
  2788. case 'I': flags |= INVERTED; break;
  2789. case 'C': flags |= CLOCK; break;
  2790. case 'L': flags |= LOWLEVEL_IN; break;
  2791. case 'V': flags |= LOWLEVEL_OUT; break;
  2792. case 'F': flags |= FALLING_EDGE; break;
  2793. case 'X': flags |= NONLOGIC; break;
  2794. default: THROW_PARSE_ERROR( "invalid pin attribut", aReader.GetSource(),
  2795. aReader.Line(), aReader.LineNumber(), pos );
  2796. }
  2797. pos += 1;
  2798. }
  2799. switch( flags )
  2800. {
  2801. case 0:
  2802. pin->m_shape = GRAPHIC_PINSHAPE::LINE;
  2803. break;
  2804. case INVERTED:
  2805. pin->m_shape = GRAPHIC_PINSHAPE::INVERTED;
  2806. break;
  2807. case CLOCK:
  2808. pin->m_shape = GRAPHIC_PINSHAPE::CLOCK;
  2809. break;
  2810. case INVERTED | CLOCK:
  2811. pin->m_shape = GRAPHIC_PINSHAPE::INVERTED_CLOCK;
  2812. break;
  2813. case LOWLEVEL_IN:
  2814. pin->m_shape = GRAPHIC_PINSHAPE::INPUT_LOW;
  2815. break;
  2816. case LOWLEVEL_IN | CLOCK:
  2817. pin->m_shape = GRAPHIC_PINSHAPE::CLOCK_LOW;
  2818. break;
  2819. case LOWLEVEL_OUT:
  2820. pin->m_shape = GRAPHIC_PINSHAPE::OUTPUT_LOW;
  2821. break;
  2822. case FALLING_EDGE:
  2823. pin->m_shape = GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK;
  2824. break;
  2825. case NONLOGIC:
  2826. pin->m_shape = GRAPHIC_PINSHAPE::NONLOGIC;
  2827. break;
  2828. default:
  2829. SCH_PARSE_ERROR( "pin attributes do not define a valid pin shape", aReader, line );
  2830. }
  2831. }
  2832. return pin;
  2833. }
  2834. LIB_POLYLINE* SCH_LEGACY_PLUGIN_CACHE::loadPolyLine( std::unique_ptr<LIB_PART>& aPart,
  2835. LINE_READER& aReader )
  2836. {
  2837. const char* line = aReader.Line();
  2838. wxCHECK_MSG( strCompare( "P", line, &line ), NULL, "Invalid LIB_POLYLINE definition" );
  2839. LIB_POLYLINE* polyLine = new LIB_POLYLINE( aPart.get() );
  2840. int points = parseInt( aReader, line, &line );
  2841. polyLine->SetUnit( parseInt( aReader, line, &line ) );
  2842. polyLine->SetConvert( parseInt( aReader, line, &line ) );
  2843. polyLine->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2844. polyLine->Reserve( points );
  2845. wxPoint pt;
  2846. for( int i = 0; i < points; i++ )
  2847. {
  2848. pt.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2849. pt.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2850. polyLine->AddPoint( pt );
  2851. }
  2852. if( *line != 0 )
  2853. polyLine->SetFillMode( parseFillMode( aReader, line, &line ) );
  2854. return polyLine;
  2855. }
  2856. LIB_BEZIER* SCH_LEGACY_PLUGIN_CACHE::loadBezier( std::unique_ptr<LIB_PART>& aPart,
  2857. LINE_READER& aReader )
  2858. {
  2859. const char* line = aReader.Line();
  2860. wxCHECK_MSG( strCompare( "B", line, &line ), NULL, "Invalid LIB_BEZIER definition" );
  2861. LIB_BEZIER* bezier = new LIB_BEZIER( aPart.get() );
  2862. int points = parseInt( aReader, line, &line );
  2863. bezier->SetUnit( parseInt( aReader, line, &line ) );
  2864. bezier->SetConvert( parseInt( aReader, line, &line ) );
  2865. bezier->SetWidth( Mils2Iu( parseInt( aReader, line, &line ) ) );
  2866. wxPoint pt;
  2867. bezier->Reserve( points );
  2868. for( int i = 0; i < points; i++ )
  2869. {
  2870. pt.x = Mils2Iu( parseInt( aReader, line, &line ) );
  2871. pt.y = Mils2Iu( parseInt( aReader, line, &line ) );
  2872. bezier->AddPoint( pt );
  2873. }
  2874. if( *line != 0 )
  2875. bezier->SetFillMode( parseFillMode( aReader, line, &line ) );
  2876. return bezier;
  2877. }
  2878. void SCH_LEGACY_PLUGIN_CACHE::loadFootprintFilters( std::unique_ptr<LIB_PART>& aPart,
  2879. LINE_READER& aReader )
  2880. {
  2881. const char* line = aReader.Line();
  2882. wxCHECK_RET( strCompare( "$FPLIST", line, &line ), "Invalid footprint filter list" );
  2883. line = aReader.ReadLine();
  2884. wxArrayString footprintFilters;
  2885. while( line )
  2886. {
  2887. if( strCompare( "$ENDFPLIST", line, &line ) )
  2888. {
  2889. aPart->SetFootprintFilters( footprintFilters );
  2890. return;
  2891. }
  2892. wxString footprint;
  2893. parseUnquotedString( footprint, aReader, line, &line );
  2894. footprintFilters.Add( footprint );
  2895. line = aReader.ReadLine();
  2896. }
  2897. SCH_PARSE_ERROR( "file ended prematurely while loading footprint filters", aReader, line );
  2898. }
  2899. void SCH_LEGACY_PLUGIN_CACHE::Save( bool aSaveDocFile )
  2900. {
  2901. if( !m_isModified )
  2902. return;
  2903. // Write through symlinks, don't replace them
  2904. wxFileName fn = GetRealFile();
  2905. std::unique_ptr< FILE_OUTPUTFORMATTER > formatter( new FILE_OUTPUTFORMATTER( fn.GetFullPath() ) );
  2906. formatter->Print( 0, "%s %d.%d\n", LIBFILE_IDENT, LIB_VERSION_MAJOR, LIB_VERSION_MINOR );
  2907. formatter->Print( 0, "#encoding utf-8\n");
  2908. for( LIB_PART_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); it++ )
  2909. {
  2910. if( !it->second->IsRoot() )
  2911. continue;
  2912. SaveSymbol( it->second, *formatter.get(), &m_symbols );
  2913. }
  2914. formatter->Print( 0, "#\n#End Library\n" );
  2915. formatter.reset();
  2916. m_fileModTime = fn.GetModificationTime();
  2917. m_isModified = false;
  2918. if( aSaveDocFile )
  2919. saveDocFile();
  2920. }
  2921. void SCH_LEGACY_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter,
  2922. LIB_PART_MAP* aMap )
  2923. {
  2924. wxCHECK_RET( aSymbol && aSymbol->IsRoot(), "Invalid LIB_PART pointer." );
  2925. // LIB_ALIAS objects are deprecated but we still need to gather up the derived symbols
  2926. // and save their names for the old file format.
  2927. wxArrayString aliasNames;
  2928. if( aMap )
  2929. {
  2930. for( auto entry : *aMap )
  2931. {
  2932. LIB_PART* part = entry.second;
  2933. if( part->IsAlias() && part->GetParent().lock() == aSymbol->SharedPtr() )
  2934. aliasNames.Add( part->GetName() );
  2935. }
  2936. }
  2937. LIB_FIELD& value = aSymbol->GetValueField();
  2938. // First line: it s a comment (component name for readers)
  2939. aFormatter.Print( 0, "#\n# %s\n#\n", TO_UTF8( value.GetText() ) );
  2940. // Save data
  2941. aFormatter.Print( 0, "DEF" );
  2942. aFormatter.Print( 0, " %s", TO_UTF8( value.GetText() ) );
  2943. LIB_FIELD& reference = aSymbol->GetReferenceField();
  2944. if( !reference.GetText().IsEmpty() )
  2945. {
  2946. aFormatter.Print( 0, " %s", TO_UTF8( reference.GetText() ) );
  2947. }
  2948. else
  2949. {
  2950. aFormatter.Print( 0, " ~" );
  2951. }
  2952. aFormatter.Print( 0, " %d %d %c %c %d %c %c\n",
  2953. 0, Iu2Mils( aSymbol->GetPinNameOffset() ),
  2954. aSymbol->ShowPinNumbers() ? 'Y' : 'N',
  2955. aSymbol->ShowPinNames() ? 'Y' : 'N',
  2956. aSymbol->GetUnitCount(), aSymbol->UnitsLocked() ? 'L' : 'F',
  2957. aSymbol->IsPower() ? 'P' : 'N' );
  2958. timestamp_t dateModified = aSymbol->GetDateLastEdition();
  2959. if( dateModified != 0 )
  2960. {
  2961. int sec = dateModified & 63;
  2962. int min = ( dateModified >> 6 ) & 63;
  2963. int hour = ( dateModified >> 12 ) & 31;
  2964. int day = ( dateModified >> 17 ) & 31;
  2965. int mon = ( dateModified >> 22 ) & 15;
  2966. int year = ( dateModified >> 26 ) + 1990;
  2967. aFormatter.Print( 0, "Ti %d/%d/%d %d:%d:%d\n", year, mon, day, hour, min, sec );
  2968. }
  2969. LIB_FIELDS fields;
  2970. aSymbol->GetFields( fields );
  2971. // Mandatory fields:
  2972. // may have their own save policy so there is a separate loop for them.
  2973. // Empty fields are saved, because the user may have set visibility,
  2974. // size and orientation
  2975. for( int i = 0; i < MANDATORY_FIELDS; ++i )
  2976. {
  2977. saveField( &fields[i], aFormatter );
  2978. }
  2979. // User defined fields:
  2980. // may have their own save policy so there is a separate loop for them.
  2981. int fieldId = MANDATORY_FIELDS; // really wish this would go away.
  2982. for( unsigned i = MANDATORY_FIELDS; i < fields.size(); ++i )
  2983. {
  2984. // There is no need to save empty fields, i.e. no reason to preserve field
  2985. // names now that fields names come in dynamically through the template
  2986. // fieldnames.
  2987. if( !fields[i].GetText().IsEmpty() )
  2988. {
  2989. fields[i].SetId( fieldId++ );
  2990. saveField( &fields[i], aFormatter );
  2991. }
  2992. }
  2993. // Save the alias list: a line starting by "ALIAS".
  2994. if( !aliasNames.IsEmpty() )
  2995. {
  2996. aFormatter.Print( 0, "ALIAS" );
  2997. for( unsigned i = 0; i < aliasNames.GetCount(); i++ )
  2998. {
  2999. aFormatter.Print( 0, " %s", TO_UTF8( aliasNames[i] ) );
  3000. }
  3001. aFormatter.Print( 0, "\n" );
  3002. }
  3003. wxArrayString footprints = aSymbol->GetFootprints();
  3004. // Write the footprint filter list
  3005. if( footprints.GetCount() != 0 )
  3006. {
  3007. aFormatter.Print( 0, "$FPLIST\n" );
  3008. for( unsigned i = 0; i < footprints.GetCount(); i++ )
  3009. {
  3010. aFormatter.Print( 0, " %s\n", TO_UTF8( footprints[i] ) );
  3011. }
  3012. aFormatter.Print( 0, "$ENDFPLIST\n" );
  3013. }
  3014. // Save graphics items (including pins)
  3015. if( !aSymbol->GetDrawItems().empty() )
  3016. {
  3017. // Sort the draw items in order to editing a file editing by hand.
  3018. aSymbol->GetDrawItems().sort();
  3019. aFormatter.Print( 0, "DRAW\n" );
  3020. for( LIB_ITEM& item : aSymbol->GetDrawItems() )
  3021. {
  3022. switch( item.Type() )
  3023. {
  3024. case LIB_FIELD_T: // Fields have already been saved above.
  3025. continue;
  3026. case LIB_ARC_T:
  3027. saveArc( (LIB_ARC*) &item, aFormatter );
  3028. break;
  3029. case LIB_BEZIER_T:
  3030. saveBezier( (LIB_BEZIER*) &item, aFormatter );
  3031. break;
  3032. case LIB_CIRCLE_T:
  3033. saveCircle( ( LIB_CIRCLE* ) &item, aFormatter );
  3034. break;
  3035. case LIB_PIN_T:
  3036. savePin( (LIB_PIN* ) &item, aFormatter );
  3037. break;
  3038. case LIB_POLYLINE_T:
  3039. savePolyLine( ( LIB_POLYLINE* ) &item, aFormatter );
  3040. break;
  3041. case LIB_RECTANGLE_T:
  3042. saveRectangle( ( LIB_RECTANGLE* ) &item, aFormatter );
  3043. break;
  3044. case LIB_TEXT_T:
  3045. saveText( ( LIB_TEXT* ) &item, aFormatter );
  3046. break;
  3047. default:
  3048. ;
  3049. }
  3050. }
  3051. aFormatter.Print( 0, "ENDDRAW\n" );
  3052. }
  3053. aFormatter.Print( 0, "ENDDEF\n" );
  3054. }
  3055. void SCH_LEGACY_PLUGIN_CACHE::saveArc( LIB_ARC* aArc,
  3056. OUTPUTFORMATTER& aFormatter )
  3057. {
  3058. wxCHECK_RET( aArc && aArc->Type() == LIB_ARC_T, "Invalid LIB_ARC object." );
  3059. int x1 = aArc->GetFirstRadiusAngle();
  3060. if( x1 > 1800 )
  3061. x1 -= 3600;
  3062. int x2 = aArc->GetSecondRadiusAngle();
  3063. if( x2 > 1800 )
  3064. x2 -= 3600;
  3065. aFormatter.Print( 0, "A %d %d %d %d %d %d %d %d %c %d %d %d %d\n",
  3066. Iu2Mils( aArc->GetPosition().x ), Iu2Mils( aArc->GetPosition().y ),
  3067. Iu2Mils( aArc->GetRadius() ), x1, x2, aArc->GetUnit(), aArc->GetConvert(),
  3068. Iu2Mils( aArc->GetWidth() ), fill_tab[aArc->GetFillMode()],
  3069. Iu2Mils( aArc->GetStart().x ), Iu2Mils( aArc->GetStart().y ),
  3070. Iu2Mils( aArc->GetEnd().x ), Iu2Mils( aArc->GetEnd().y ) );
  3071. }
  3072. void SCH_LEGACY_PLUGIN_CACHE::saveBezier( LIB_BEZIER* aBezier,
  3073. OUTPUTFORMATTER& aFormatter )
  3074. {
  3075. wxCHECK_RET( aBezier && aBezier->Type() == LIB_BEZIER_T, "Invalid LIB_BEZIER object." );
  3076. aFormatter.Print( 0, "B %u %d %d %d", (unsigned)aBezier->GetPoints().size(),
  3077. aBezier->GetUnit(), aBezier->GetConvert(), Iu2Mils( aBezier->GetWidth() ) );
  3078. for( const auto& pt : aBezier->GetPoints() )
  3079. aFormatter.Print( 0, " %d %d", Iu2Mils( pt.x ), Iu2Mils( pt.y ) );
  3080. aFormatter.Print( 0, " %c\n", fill_tab[aBezier->GetFillMode()] );
  3081. }
  3082. void SCH_LEGACY_PLUGIN_CACHE::saveCircle( LIB_CIRCLE* aCircle,
  3083. OUTPUTFORMATTER& aFormatter )
  3084. {
  3085. wxCHECK_RET( aCircle && aCircle->Type() == LIB_CIRCLE_T, "Invalid LIB_CIRCLE object." );
  3086. aFormatter.Print( 0, "C %d %d %d %d %d %d %c\n",
  3087. Iu2Mils( aCircle->GetPosition().x ), Iu2Mils( aCircle->GetPosition().y ),
  3088. Iu2Mils( aCircle->GetRadius() ), aCircle->GetUnit(), aCircle->GetConvert(),
  3089. Iu2Mils( aCircle->GetWidth() ), fill_tab[aCircle->GetFillMode()] );
  3090. }
  3091. void SCH_LEGACY_PLUGIN_CACHE::saveField( LIB_FIELD* aField,
  3092. OUTPUTFORMATTER& aFormatter )
  3093. {
  3094. wxCHECK_RET( aField && aField->Type() == LIB_FIELD_T, "Invalid LIB_FIELD object." );
  3095. int hjustify, vjustify;
  3096. int id = aField->GetId();
  3097. wxString text = aField->GetText();
  3098. hjustify = 'C';
  3099. if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  3100. hjustify = 'L';
  3101. else if( aField->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  3102. hjustify = 'R';
  3103. vjustify = 'C';
  3104. if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  3105. vjustify = 'B';
  3106. else if( aField->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  3107. vjustify = 'T';
  3108. aFormatter.Print( 0, "F%d %s %d %d %d %c %c %c %c%c%c",
  3109. id,
  3110. EscapedUTF8( text ).c_str(), // wraps in quotes
  3111. Iu2Mils( aField->GetTextPos().x ), Iu2Mils( aField->GetTextPos().y ),
  3112. Iu2Mils( aField->GetTextWidth() ),
  3113. aField->GetTextAngle() == 0 ? 'H' : 'V',
  3114. aField->IsVisible() ? 'V' : 'I',
  3115. hjustify, vjustify,
  3116. aField->IsItalic() ? 'I' : 'N',
  3117. aField->IsBold() ? 'B' : 'N' );
  3118. /* Save field name, if necessary
  3119. * Field name is saved only if it is not the default name.
  3120. * Just because default name depends on the language and can change from
  3121. * a country to another
  3122. */
  3123. wxString defName = TEMPLATE_FIELDNAME::GetDefaultFieldName( id );
  3124. if( id >= FIELD1 && !aField->m_name.IsEmpty() && aField->m_name != defName )
  3125. aFormatter.Print( 0, " %s", EscapedUTF8( aField->m_name ).c_str() );
  3126. aFormatter.Print( 0, "\n" );
  3127. }
  3128. void SCH_LEGACY_PLUGIN_CACHE::savePin( LIB_PIN* aPin,
  3129. OUTPUTFORMATTER& aFormatter )
  3130. {
  3131. wxCHECK_RET( aPin && aPin->Type() == LIB_PIN_T, "Invalid LIB_PIN object." );
  3132. int Etype;
  3133. switch( aPin->GetType() )
  3134. {
  3135. default:
  3136. case ELECTRICAL_PINTYPE::PT_INPUT:
  3137. Etype = 'I';
  3138. break;
  3139. case ELECTRICAL_PINTYPE::PT_OUTPUT:
  3140. Etype = 'O';
  3141. break;
  3142. case ELECTRICAL_PINTYPE::PT_BIDI:
  3143. Etype = 'B';
  3144. break;
  3145. case ELECTRICAL_PINTYPE::PT_TRISTATE:
  3146. Etype = 'T';
  3147. break;
  3148. case ELECTRICAL_PINTYPE::PT_PASSIVE:
  3149. Etype = 'P';
  3150. break;
  3151. case ELECTRICAL_PINTYPE::PT_UNSPECIFIED:
  3152. Etype = 'U';
  3153. break;
  3154. case ELECTRICAL_PINTYPE::PT_POWER_IN:
  3155. Etype = 'W';
  3156. break;
  3157. case ELECTRICAL_PINTYPE::PT_POWER_OUT:
  3158. Etype = 'w';
  3159. break;
  3160. case ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR:
  3161. Etype = 'C';
  3162. break;
  3163. case ELECTRICAL_PINTYPE::PT_OPENEMITTER:
  3164. Etype = 'E';
  3165. break;
  3166. case ELECTRICAL_PINTYPE::PT_NC:
  3167. Etype = 'N';
  3168. break;
  3169. }
  3170. if( !aPin->GetName().IsEmpty() )
  3171. aFormatter.Print( 0, "X %s", TO_UTF8( aPin->GetName() ) );
  3172. else
  3173. aFormatter.Print( 0, "X ~" );
  3174. aFormatter.Print( 0, " %s %d %d %d %c %d %d %d %d %c",
  3175. aPin->GetNumber().IsEmpty() ? "~" : TO_UTF8( aPin->GetNumber() ),
  3176. Iu2Mils( aPin->GetPosition().x ), Iu2Mils( aPin->GetPosition().y ),
  3177. Iu2Mils( (int) aPin->GetLength() ), (int) aPin->GetOrientation(),
  3178. Iu2Mils( aPin->GetNumberTextSize() ), Iu2Mils( aPin->GetNameTextSize() ),
  3179. aPin->GetUnit(), aPin->GetConvert(), Etype );
  3180. if( aPin->GetShape() != GRAPHIC_PINSHAPE::LINE || !aPin->IsVisible() )
  3181. aFormatter.Print( 0, " " );
  3182. if( !aPin->IsVisible() )
  3183. aFormatter.Print( 0, "N" );
  3184. switch( aPin->GetShape() )
  3185. {
  3186. case GRAPHIC_PINSHAPE::LINE:
  3187. break;
  3188. case GRAPHIC_PINSHAPE::INVERTED:
  3189. aFormatter.Print( 0, "I" );
  3190. break;
  3191. case GRAPHIC_PINSHAPE::CLOCK:
  3192. aFormatter.Print( 0, "C" );
  3193. break;
  3194. case GRAPHIC_PINSHAPE::INVERTED_CLOCK:
  3195. aFormatter.Print( 0, "IC" );
  3196. break;
  3197. case GRAPHIC_PINSHAPE::INPUT_LOW:
  3198. aFormatter.Print( 0, "L" );
  3199. break;
  3200. case GRAPHIC_PINSHAPE::CLOCK_LOW:
  3201. aFormatter.Print( 0, "CL" );
  3202. break;
  3203. case GRAPHIC_PINSHAPE::OUTPUT_LOW:
  3204. aFormatter.Print( 0, "V" );
  3205. break;
  3206. case GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK:
  3207. aFormatter.Print( 0, "F" );
  3208. break;
  3209. case GRAPHIC_PINSHAPE::NONLOGIC:
  3210. aFormatter.Print( 0, "X" );
  3211. break;
  3212. default:
  3213. assert( !"Invalid pin shape" );
  3214. }
  3215. aFormatter.Print( 0, "\n" );
  3216. aPin->ClearFlags( IS_CHANGED );
  3217. }
  3218. void SCH_LEGACY_PLUGIN_CACHE::savePolyLine( LIB_POLYLINE* aPolyLine,
  3219. OUTPUTFORMATTER& aFormatter )
  3220. {
  3221. wxCHECK_RET( aPolyLine && aPolyLine->Type() == LIB_POLYLINE_T, "Invalid LIB_POLYLINE object." );
  3222. int ccount = aPolyLine->GetCornerCount();
  3223. aFormatter.Print( 0, "P %d %d %d %d", ccount, aPolyLine->GetUnit(), aPolyLine->GetConvert(),
  3224. Iu2Mils( aPolyLine->GetWidth() ) );
  3225. for( const auto& pt : aPolyLine->GetPolyPoints() )
  3226. {
  3227. aFormatter.Print( 0, " %d %d", Iu2Mils( pt.x ), Iu2Mils( pt.y ) );
  3228. }
  3229. aFormatter.Print( 0, " %c\n", fill_tab[aPolyLine->GetFillMode()] );
  3230. }
  3231. void SCH_LEGACY_PLUGIN_CACHE::saveRectangle( LIB_RECTANGLE* aRectangle,
  3232. OUTPUTFORMATTER& aFormatter )
  3233. {
  3234. wxCHECK_RET( aRectangle && aRectangle->Type() == LIB_RECTANGLE_T,
  3235. "Invalid LIB_RECTANGLE object." );
  3236. aFormatter.Print( 0, "S %d %d %d %d %d %d %d %c\n",
  3237. Iu2Mils( aRectangle->GetPosition().x ),
  3238. Iu2Mils( aRectangle->GetPosition().y ),
  3239. Iu2Mils( aRectangle->GetEnd().x ), Iu2Mils( aRectangle->GetEnd().y ),
  3240. aRectangle->GetUnit(), aRectangle->GetConvert(),
  3241. Iu2Mils( aRectangle->GetWidth() ), fill_tab[aRectangle->GetFillMode()] );
  3242. }
  3243. void SCH_LEGACY_PLUGIN_CACHE::saveText( LIB_TEXT* aText,
  3244. OUTPUTFORMATTER& aFormatter )
  3245. {
  3246. wxCHECK_RET( aText && aText->Type() == LIB_TEXT_T, "Invalid LIB_TEXT object." );
  3247. wxString text = aText->GetText();
  3248. if( text.Contains( wxT( " " ) ) || text.Contains( wxT( "~" ) ) || text.Contains( wxT( "\"" ) ) )
  3249. {
  3250. // convert double quote to similar-looking two apostrophes
  3251. text.Replace( wxT( "\"" ), wxT( "''" ) );
  3252. text = wxT( "\"" ) + text + wxT( "\"" );
  3253. }
  3254. aFormatter.Print( 0, "T %g %d %d %d %d %d %d %s", aText->GetTextAngle(),
  3255. Iu2Mils( aText->GetTextPos().x ), Iu2Mils( aText->GetTextPos().y ),
  3256. Iu2Mils( aText->GetTextWidth() ), !aText->IsVisible(),
  3257. aText->GetUnit(), aText->GetConvert(), TO_UTF8( text ) );
  3258. aFormatter.Print( 0, " %s %d", aText->IsItalic() ? "Italic" : "Normal", aText->IsBold() );
  3259. char hjustify = 'C';
  3260. if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  3261. hjustify = 'L';
  3262. else if( aText->GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  3263. hjustify = 'R';
  3264. char vjustify = 'C';
  3265. if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  3266. vjustify = 'B';
  3267. else if( aText->GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  3268. vjustify = 'T';
  3269. aFormatter.Print( 0, " %c %c\n", hjustify, vjustify );
  3270. }
  3271. void SCH_LEGACY_PLUGIN_CACHE::saveDocFile()
  3272. {
  3273. wxFileName fileName = m_libFileName;
  3274. fileName.SetExt( DOC_EXT );
  3275. FILE_OUTPUTFORMATTER formatter( fileName.GetFullPath() );
  3276. formatter.Print( 0, "%s\n", DOCFILE_IDENT );
  3277. for( LIB_PART_MAP::iterator it = m_symbols.begin(); it != m_symbols.end(); ++it )
  3278. {
  3279. wxString description = it->second->GetDescription();
  3280. wxString keyWords = it->second->GetKeyWords();
  3281. wxString docFileName = it->second->GetDocFileName();
  3282. if( description.IsEmpty() && keyWords.IsEmpty() && docFileName.IsEmpty() )
  3283. continue;
  3284. formatter.Print( 0, "#\n$CMP %s\n", TO_UTF8( it->second->GetName() ) );
  3285. if( !description.IsEmpty() )
  3286. formatter.Print( 0, "D %s\n", TO_UTF8( description ) );
  3287. if( !keyWords.IsEmpty() )
  3288. formatter.Print( 0, "K %s\n", TO_UTF8( keyWords ) );
  3289. if( !docFileName.IsEmpty() )
  3290. formatter.Print( 0, "F %s\n", TO_UTF8( docFileName ) );
  3291. formatter.Print( 0, "$ENDCMP\n" );
  3292. }
  3293. formatter.Print( 0, "#\n#End Doc Library\n" );
  3294. }
  3295. void SCH_LEGACY_PLUGIN_CACHE::DeleteSymbol( const wxString& aSymbolName )
  3296. {
  3297. LIB_PART_MAP::iterator it = m_symbols.find( aSymbolName );
  3298. if( it == m_symbols.end() )
  3299. THROW_IO_ERROR( wxString::Format( _( "library %s does not contain a symbol named %s" ),
  3300. m_libFileName.GetFullName(), aSymbolName ) );
  3301. LIB_PART* part = it->second;
  3302. if( part->IsRoot() )
  3303. {
  3304. LIB_PART* rootPart = part;
  3305. // Remove the root symbol and all it's children.
  3306. m_symbols.erase( it );
  3307. LIB_PART_MAP::iterator it1 = m_symbols.begin();
  3308. while( it1 != m_symbols.end() )
  3309. {
  3310. if( it1->second->IsAlias() && it1->second->GetParent().lock() == rootPart->SharedPtr() )
  3311. {
  3312. delete it1->second;
  3313. it1 = m_symbols.erase( it1 );
  3314. }
  3315. else
  3316. {
  3317. it1++;
  3318. }
  3319. }
  3320. delete rootPart;
  3321. }
  3322. else
  3323. {
  3324. // Just remove the alias.
  3325. m_symbols.erase( it );
  3326. delete part;
  3327. }
  3328. ++m_modHash;
  3329. m_isModified = true;
  3330. }
  3331. void SCH_LEGACY_PLUGIN::cacheLib( const wxString& aLibraryFileName )
  3332. {
  3333. if( !m_cache || !m_cache->IsFile( aLibraryFileName ) || m_cache->IsFileChanged() )
  3334. {
  3335. // a spectacular episode in memory management:
  3336. delete m_cache;
  3337. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryFileName );
  3338. // Because m_cache is rebuilt, increment PART_LIBS::s_modify_generation
  3339. // to modify the hash value that indicate component to symbol links
  3340. // must be updated.
  3341. PART_LIBS::s_modify_generation++;
  3342. if( !isBuffering( m_props ) )
  3343. m_cache->Load();
  3344. }
  3345. }
  3346. bool SCH_LEGACY_PLUGIN::writeDocFile( const PROPERTIES* aProperties )
  3347. {
  3348. std::string propName( SCH_LEGACY_PLUGIN::PropNoDocFile );
  3349. if( aProperties && aProperties->find( propName ) != aProperties->end() )
  3350. return false;
  3351. return true;
  3352. }
  3353. bool SCH_LEGACY_PLUGIN::isBuffering( const PROPERTIES* aProperties )
  3354. {
  3355. return ( aProperties && aProperties->Exists( SCH_LEGACY_PLUGIN::PropBuffering ) );
  3356. }
  3357. int SCH_LEGACY_PLUGIN::GetModifyHash() const
  3358. {
  3359. if( m_cache )
  3360. return m_cache->GetModifyHash();
  3361. // If the cache hasn't been loaded, it hasn't been modified.
  3362. return 0;
  3363. }
  3364. void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
  3365. const wxString& aLibraryPath,
  3366. const PROPERTIES* aProperties )
  3367. {
  3368. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3369. m_props = aProperties;
  3370. bool powerSymbolsOnly = ( aProperties &&
  3371. aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
  3372. cacheLib( aLibraryPath );
  3373. const LIB_PART_MAP& symbols = m_cache->m_symbols;
  3374. for( LIB_PART_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
  3375. {
  3376. if( !powerSymbolsOnly || it->second->IsPower() )
  3377. aSymbolNameList.Add( it->first );
  3378. }
  3379. }
  3380. void SCH_LEGACY_PLUGIN::EnumerateSymbolLib( std::vector<LIB_PART*>& aSymbolList,
  3381. const wxString& aLibraryPath,
  3382. const PROPERTIES* aProperties )
  3383. {
  3384. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3385. m_props = aProperties;
  3386. bool powerSymbolsOnly = ( aProperties &&
  3387. aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) != aProperties->end() );
  3388. cacheLib( aLibraryPath );
  3389. const LIB_PART_MAP& symbols = m_cache->m_symbols;
  3390. for( LIB_PART_MAP::const_iterator it = symbols.begin(); it != symbols.end(); ++it )
  3391. {
  3392. if( !powerSymbolsOnly || it->second->IsPower() )
  3393. aSymbolList.push_back( it->second );
  3394. }
  3395. }
  3396. LIB_PART* SCH_LEGACY_PLUGIN::LoadSymbol( const wxString& aLibraryPath, const wxString& aSymbolName,
  3397. const PROPERTIES* aProperties )
  3398. {
  3399. LOCALE_IO toggle; // toggles on, then off, the C locale.
  3400. m_props = aProperties;
  3401. cacheLib( aLibraryPath );
  3402. LIB_PART_MAP::const_iterator it = m_cache->m_symbols.find( aSymbolName );
  3403. if( it == m_cache->m_symbols.end() )
  3404. return nullptr;
  3405. return it->second;
  3406. }
  3407. void SCH_LEGACY_PLUGIN::SaveSymbol( const wxString& aLibraryPath, const LIB_PART* aSymbol,
  3408. const PROPERTIES* aProperties )
  3409. {
  3410. m_props = aProperties;
  3411. cacheLib( aLibraryPath );
  3412. m_cache->AddSymbol( aSymbol );
  3413. if( !isBuffering( aProperties ) )
  3414. m_cache->Save( writeDocFile( aProperties ) );
  3415. }
  3416. void SCH_LEGACY_PLUGIN::DeleteSymbol( const wxString& aLibraryPath, const wxString& aSymbolName,
  3417. const PROPERTIES* aProperties )
  3418. {
  3419. m_props = aProperties;
  3420. cacheLib( aLibraryPath );
  3421. m_cache->DeleteSymbol( aSymbolName );
  3422. if( !isBuffering( aProperties ) )
  3423. m_cache->Save( writeDocFile( aProperties ) );
  3424. }
  3425. void SCH_LEGACY_PLUGIN::CreateSymbolLib( const wxString& aLibraryPath,
  3426. const PROPERTIES* aProperties )
  3427. {
  3428. if( wxFileExists( aLibraryPath ) )
  3429. {
  3430. THROW_IO_ERROR( wxString::Format(
  3431. _( "symbol library \"%s\" already exists, cannot create a new library" ),
  3432. aLibraryPath.GetData() ) );
  3433. }
  3434. LOCALE_IO toggle;
  3435. m_props = aProperties;
  3436. delete m_cache;
  3437. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
  3438. m_cache->SetModified();
  3439. m_cache->Save( writeDocFile( aProperties ) );
  3440. m_cache->Load(); // update m_writable and m_mod_time
  3441. }
  3442. bool SCH_LEGACY_PLUGIN::DeleteSymbolLib( const wxString& aLibraryPath,
  3443. const PROPERTIES* aProperties )
  3444. {
  3445. wxFileName fn = aLibraryPath;
  3446. if( !fn.FileExists() )
  3447. return false;
  3448. // Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
  3449. // we don't want that. we want bare metal portability with no UI here.
  3450. if( wxRemove( aLibraryPath ) )
  3451. {
  3452. THROW_IO_ERROR( wxString::Format( _( "library \"%s\" cannot be deleted" ),
  3453. aLibraryPath.GetData() ) );
  3454. }
  3455. if( m_cache && m_cache->IsFile( aLibraryPath ) )
  3456. {
  3457. delete m_cache;
  3458. m_cache = 0;
  3459. }
  3460. return true;
  3461. }
  3462. void SCH_LEGACY_PLUGIN::SaveLibrary( const wxString& aLibraryPath, const PROPERTIES* aProperties )
  3463. {
  3464. if( !m_cache )
  3465. m_cache = new SCH_LEGACY_PLUGIN_CACHE( aLibraryPath );
  3466. wxString oldFileName = m_cache->GetFileName();
  3467. if( !m_cache->IsFile( aLibraryPath ) )
  3468. {
  3469. m_cache->SetFileName( aLibraryPath );
  3470. }
  3471. // This is a forced save.
  3472. m_cache->SetModified();
  3473. m_cache->Save( writeDocFile( aProperties ) );
  3474. m_cache->SetFileName( oldFileName );
  3475. }
  3476. bool SCH_LEGACY_PLUGIN::CheckHeader( const wxString& aFileName )
  3477. {
  3478. // Open file and check first line
  3479. wxTextFile tempFile;
  3480. tempFile.Open( aFileName );
  3481. wxString firstline;
  3482. // read the first line
  3483. firstline = tempFile.GetFirstLine();
  3484. tempFile.Close();
  3485. return firstline.StartsWith( "EESchema" );
  3486. }
  3487. bool SCH_LEGACY_PLUGIN::IsSymbolLibWritable( const wxString& aLibraryPath )
  3488. {
  3489. return wxFileName::IsFileWritable( aLibraryPath );
  3490. }
  3491. LIB_PART* SCH_LEGACY_PLUGIN::ParsePart( LINE_READER& reader, int aMajorVersion,
  3492. int aMinorVersion )
  3493. {
  3494. return SCH_LEGACY_PLUGIN_CACHE::LoadPart( reader, aMajorVersion, aMinorVersion );
  3495. }
  3496. void SCH_LEGACY_PLUGIN::FormatPart( LIB_PART* part, OUTPUTFORMATTER & formatter )
  3497. {
  3498. SCH_LEGACY_PLUGIN_CACHE::SaveSymbol( part, formatter );
  3499. }
  3500. const char* SCH_LEGACY_PLUGIN::PropBuffering = "buffering";
  3501. const char* SCH_LEGACY_PLUGIN::PropNoDocFile = "no_doc_file";