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.

4222 lines
135 KiB

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