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.

3546 lines
109 KiB

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