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.

687 lines
24 KiB

5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 CERN
  5. * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Jon Evans <jon@craftyjon.com>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <project.h>
  22. #include <project/net_settings.h>
  23. #include <settings/json_settings_internals.h>
  24. #include <project/project_file.h>
  25. #include <settings/common_settings.h>
  26. #include <settings/parameters.h>
  27. #include <wildcards_and_files_ext.h>
  28. #include <wx/config.h>
  29. #include <wx/log.h>
  30. ///! Update the schema version whenever a migration is required
  31. const int projectFileSchemaVersion = 2;
  32. PROJECT_FILE::PROJECT_FILE( const wxString& aFullPath ) :
  33. JSON_SETTINGS( aFullPath, SETTINGS_LOC::PROJECT, projectFileSchemaVersion ),
  34. m_ErcSettings( nullptr ),
  35. m_SchematicSettings( nullptr ),
  36. m_BoardSettings(),
  37. m_sheets(),
  38. m_boards(),
  39. m_project( nullptr )
  40. {
  41. // Keep old files around
  42. m_deleteLegacyAfterMigration = false;
  43. m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "sheets", &m_sheets, {} ) );
  44. m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "boards", &m_boards, {} ) );
  45. m_params.emplace_back( new PARAM_WXSTRING_MAP( "text_variables",
  46. &m_TextVars, {}, false, true /* array behavior, even though stored as a map */ ) );
  47. m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_symbol_libs",
  48. &m_PinnedSymbolLibs, {} ) );
  49. m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_footprint_libs",
  50. &m_PinnedFootprintLibs, {} ) );
  51. m_params.emplace_back( new PARAM_PATH_LIST( "cvpcb.equivalence_files",
  52. &m_EquivalenceFiles, {} ) );
  53. m_params.emplace_back( new PARAM_PATH( "pcbnew.page_layout_descr_file",
  54. &m_BoardDrawingSheetFile, "" ) );
  55. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.netlist",
  56. &m_PcbLastPath[LAST_PATH_NETLIST], "" ) );
  57. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.step",
  58. &m_PcbLastPath[LAST_PATH_STEP], "" ) );
  59. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.idf",
  60. &m_PcbLastPath[LAST_PATH_IDF], "" ) );
  61. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.vrml",
  62. &m_PcbLastPath[LAST_PATH_VRML], "" ) );
  63. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.specctra_dsn",
  64. &m_PcbLastPath[LAST_PATH_SPECCTRADSN], "" ) );
  65. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.gencad",
  66. &m_PcbLastPath[LAST_PATH_GENCAD], "" ) );
  67. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.pos_files",
  68. &m_PcbLastPath[LAST_PATH_POS_FILES], "" ) );
  69. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.svg",
  70. &m_PcbLastPath[LAST_PATH_SVG], "" ) );
  71. m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.plot",
  72. &m_PcbLastPath[LAST_PATH_PLOT], "" ) );
  73. m_params.emplace_back( new PARAM<wxString>( "schematic.legacy_lib_dir",
  74. &m_LegacyLibDir, "" ) );
  75. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.legacy_lib_list",
  76. [&]() -> nlohmann::json
  77. {
  78. nlohmann::json ret = nlohmann::json::array();
  79. for( const wxString& libName : m_LegacyLibNames )
  80. ret.push_back( libName );
  81. return ret;
  82. },
  83. [&]( const nlohmann::json& aJson )
  84. {
  85. if( aJson.empty() || !aJson.is_array() )
  86. return;
  87. m_LegacyLibNames.clear();
  88. for( const nlohmann::json& entry : aJson )
  89. m_LegacyLibNames.push_back( entry.get<wxString>() );
  90. }, {} ) );
  91. m_NetSettings = std::make_shared<NET_SETTINGS>( this, "net_settings" );
  92. m_params.emplace_back( new PARAM_LAYER_PRESET( "board.layer_presets", &m_LayerPresets ) );
  93. m_params.emplace_back( new PARAM_VIEWPORT( "board.viewports", &m_Viewports ) );
  94. m_params.emplace_back( new PARAM_VIEWPORT3D( "board.3dviewports", &m_Viewports3D ) );
  95. m_params.emplace_back( new PARAM_LAYER_PAIRS( "board.layer_pairs", m_LayerPairInfos ) );
  96. m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.internal_id",
  97. &m_IP2581Bom.id, wxEmptyString ) );
  98. m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mpn",
  99. &m_IP2581Bom.MPN, wxEmptyString ) );
  100. m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mfg",
  101. &m_IP2581Bom.mfg, wxEmptyString ) );
  102. m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.distpn",
  103. &m_IP2581Bom.distPN, wxEmptyString ) );
  104. m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.dist",
  105. &m_IP2581Bom.dist, wxEmptyString ) );
  106. registerMigration( 1, 2, std::bind( &PROJECT_FILE::migrateSchema1To2, this ) );
  107. }
  108. /**
  109. * Schema version 2: Bump for KiCad 9 layer numbering changes
  110. * Migrate layer presets to use new enum values for copper layers
  111. */
  112. bool PROJECT_FILE::migrateSchema1To2()
  113. {
  114. auto p( "/board/layer_presets"_json_pointer );
  115. if( !m_internals->contains( p ) || !m_internals->at( p ).is_array() )
  116. return true;
  117. nlohmann::json& presets = m_internals->at( p );
  118. for( nlohmann::json& entry : presets )
  119. PARAM_LAYER_PRESET::MigrateToV9Layers( entry );
  120. return true;
  121. }
  122. bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg )
  123. {
  124. bool ret = true;
  125. wxString str;
  126. long index = 0;
  127. std::set<wxString> group_blacklist;
  128. // Legacy files don't store board info; they assume board matches project name
  129. // We will leave m_boards empty here so it can be populated with other code
  130. // First handle migration of data that will be stored locally in this object
  131. auto loadPinnedLibs =
  132. [&]( const std::string& aDest )
  133. {
  134. int libIndex = 1;
  135. wxString libKey = wxT( "PinnedItems" );
  136. libKey << libIndex;
  137. nlohmann::json libs = nlohmann::json::array();
  138. while( aCfg->Read( libKey, &str ) )
  139. {
  140. libs.push_back( str );
  141. aCfg->DeleteEntry( libKey, true );
  142. libKey = wxT( "PinnedItems" );
  143. libKey << ++libIndex;
  144. }
  145. Set( aDest, libs );
  146. };
  147. aCfg->SetPath( wxT( "/LibeditFrame" ) );
  148. loadPinnedLibs( "libraries.pinned_symbol_libs" );
  149. aCfg->SetPath( wxT( "/ModEditFrame" ) );
  150. loadPinnedLibs( "libraries.pinned_footprint_libs" );
  151. aCfg->SetPath( wxT( "/cvpcb/equfiles" ) );
  152. {
  153. int eqIdx = 1;
  154. wxString eqKey = wxT( "EquName" );
  155. eqKey << eqIdx;
  156. nlohmann::json eqs = nlohmann::json::array();
  157. while( aCfg->Read( eqKey, &str ) )
  158. {
  159. eqs.push_back( str );
  160. eqKey = wxT( "EquName" );
  161. eqKey << ++eqIdx;
  162. }
  163. Set( "cvpcb.equivalence_files", eqs );
  164. }
  165. // All CvPcb params that we want to keep have been migrated above
  166. group_blacklist.insert( wxT( "/cvpcb" ) );
  167. aCfg->SetPath( wxT( "/eeschema" ) );
  168. fromLegacyString( aCfg, "LibDir", "schematic.legacy_lib_dir" );
  169. aCfg->SetPath( wxT( "/eeschema/libraries" ) );
  170. {
  171. int libIdx = 1;
  172. wxString libKey = wxT( "LibName" );
  173. libKey << libIdx;
  174. nlohmann::json libs = nlohmann::json::array();
  175. while( aCfg->Read( libKey, &str ) )
  176. {
  177. libs.push_back( str );
  178. libKey = wxT( "LibName" );
  179. libKey << ++libIdx;
  180. }
  181. Set( "schematic.legacy_lib_list", libs );
  182. }
  183. group_blacklist.insert( wxT( "/eeschema" ) );
  184. aCfg->SetPath( wxT( "/text_variables" ) );
  185. {
  186. int txtIdx = 1;
  187. wxString txtKey;
  188. txtKey << txtIdx;
  189. nlohmann::json vars = nlohmann::json();
  190. while( aCfg->Read( txtKey, &str ) )
  191. {
  192. wxArrayString tokens = wxSplit( str, ':' );
  193. if( tokens.size() == 2 )
  194. vars[ tokens[0].ToStdString() ] = tokens[1];
  195. txtKey.clear();
  196. txtKey << ++txtIdx;
  197. }
  198. Set( "text_variables", vars );
  199. }
  200. group_blacklist.insert( wxT( "/text_variables" ) );
  201. aCfg->SetPath( wxT( "/schematic_editor" ) );
  202. fromLegacyString( aCfg, "PageLayoutDescrFile", "schematic.page_layout_descr_file" );
  203. fromLegacyString( aCfg, "PlotDirectoryName", "schematic.plot_directory" );
  204. fromLegacyString( aCfg, "NetFmtName", "schematic.net_format_name" );
  205. fromLegacy<bool>( aCfg, "SpiceAjustPassiveValues", "schematic.spice_adjust_passive_values" );
  206. fromLegacy<int>( aCfg, "SubpartIdSeparator", "schematic.subpart_id_separator" );
  207. fromLegacy<int>( aCfg, "SubpartFirstId", "schematic.subpart_first_id" );
  208. fromLegacy<int>( aCfg, "LineThickness", "schematic.drawing.default_line_thickness" );
  209. fromLegacy<int>( aCfg, "WireThickness", "schematic.drawing.default_wire_thickness" );
  210. fromLegacy<int>( aCfg, "BusThickness", "schematic.drawing.default_bus_thickness" );
  211. fromLegacy<int>( aCfg, "LabSize", "schematic.drawing.default_text_size" );
  212. if( !fromLegacy<int>( aCfg, "PinSymbolSize", "schematic.drawing.pin_symbol_size" ) )
  213. {
  214. // Use the default symbol size algorithm of Eeschema V5 (based on pin name/number size)
  215. Set( "schematic.drawing.pin_symbol_size", 0 );
  216. }
  217. fromLegacy<int>( aCfg, "JunctionSize", "schematic.drawing.default_junction_size" );
  218. fromLegacyString( aCfg, "FieldNameTemplates", "schematic.drawing.field_names" );
  219. if( !fromLegacy<double>( aCfg, "TextOffsetRatio", "schematic.drawing.text_offset_ratio" ) )
  220. {
  221. // Use the spacing of Eeschema V5
  222. Set( "schematic.drawing.text_offset_ratio", 0.08 );
  223. Set( "schematic.drawing.label_size_ratio", 0.25 );
  224. }
  225. // All schematic_editor keys we keep are migrated above
  226. group_blacklist.insert( wxT( "/schematic_editor" ) );
  227. aCfg->SetPath( wxT( "/pcbnew" ) );
  228. fromLegacyString( aCfg, "PageLayoutDescrFile", "pcbnew.page_layout_descr_file" );
  229. fromLegacyString( aCfg, "LastNetListRead", "pcbnew.last_paths.netlist" );
  230. fromLegacyString( aCfg, "LastSTEPExportPath", "pcbnew.last_paths.step" );
  231. fromLegacyString( aCfg, "LastIDFExportPath", "pcbnew.last_paths.idf" );
  232. fromLegacyString( aCfg, "LastVRMLExportPath", "pcbnew.last_paths.vmrl" );
  233. fromLegacyString( aCfg, "LastSpecctraDSNExportPath", "pcbnew.last_paths.specctra_dsn" );
  234. fromLegacyString( aCfg, "LastGenCADExportPath", "pcbnew.last_paths.gencad" );
  235. std::string bp = "board.design_settings.";
  236. {
  237. int idx = 1;
  238. wxString key = wxT( "DRCExclusion" );
  239. key << idx;
  240. nlohmann::json exclusions = nlohmann::json::array();
  241. while( aCfg->Read( key, &str ) )
  242. {
  243. exclusions.push_back( str );
  244. key = wxT( "DRCExclusion" );
  245. key << ++idx;
  246. }
  247. Set( bp + "drc_exclusions", exclusions );
  248. }
  249. fromLegacy<bool>( aCfg, "AllowMicroVias", bp + "rules.allow_microvias" );
  250. fromLegacy<bool>( aCfg, "AllowBlindVias", bp + "rules.allow_blind_buried_vias" );
  251. fromLegacy<double>( aCfg, "MinClearance", bp + "rules.min_clearance" );
  252. fromLegacy<double>( aCfg, "MinTrackWidth", bp + "rules.min_track_width" );
  253. fromLegacy<double>( aCfg, "MinViaAnnulus", bp + "rules.min_via_annulus" );
  254. fromLegacy<double>( aCfg, "MinViaDiameter", bp + "rules.min_via_diameter" );
  255. if( !fromLegacy<double>( aCfg, "MinThroughDrill", bp + "rules.min_through_hole_diameter" ) )
  256. fromLegacy<double>( aCfg, "MinViaDrill", bp + "rules.min_through_hole_diameter" );
  257. fromLegacy<double>( aCfg, "MinMicroViaDiameter", bp + "rules.min_microvia_diameter" );
  258. fromLegacy<double>( aCfg, "MinMicroViaDrill", bp + "rules.min_microvia_drill" );
  259. fromLegacy<double>( aCfg, "MinHoleToHole", bp + "rules.min_hole_to_hole" );
  260. fromLegacy<double>( aCfg, "CopperEdgeClearance", bp + "rules.min_copper_edge_clearance" );
  261. fromLegacy<double>( aCfg, "SolderMaskClearance", bp + "rules.solder_mask_clearance" );
  262. fromLegacy<double>( aCfg, "SolderMaskMinWidth", bp + "rules.solder_mask_min_width" );
  263. fromLegacy<double>( aCfg, "SolderPasteClearance", bp + "rules.solder_paste_clearance" );
  264. fromLegacy<double>( aCfg, "SolderPasteRatio", bp + "rules.solder_paste_margin_ratio" );
  265. if( !fromLegacy<double>( aCfg, "SilkLineWidth", bp + "defaults.silk_line_width" ) )
  266. fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.silk_line_width" );
  267. if( !fromLegacy<double>( aCfg, "SilkTextSizeV", bp + "defaults.silk_text_size_v" ) )
  268. fromLegacy<double>( aCfg, "ModuleTextSizeV", bp + "defaults.silk_text_size_v" );
  269. if( !fromLegacy<double>( aCfg, "SilkTextSizeH", bp + "defaults.silk_text_size_h" ) )
  270. fromLegacy<double>( aCfg, "ModuleTextSizeH", bp + "defaults.silk_text_size_h" );
  271. if( !fromLegacy<double>( aCfg, "SilkTextSizeThickness", bp + "defaults.silk_text_thickness" ) )
  272. fromLegacy<double>( aCfg, "ModuleTextSizeThickness", bp + "defaults.silk_text_thickness" );
  273. fromLegacy<bool>( aCfg, "SilkTextItalic", bp + "defaults.silk_text_italic" );
  274. fromLegacy<bool>( aCfg, "SilkTextUpright", bp + "defaults.silk_text_upright" );
  275. if( !fromLegacy<double>( aCfg, "CopperLineWidth", bp + "defaults.copper_line_width" ) )
  276. fromLegacy<double>( aCfg, "DrawSegmentWidth", bp + "defaults.copper_line_width" );
  277. if( !fromLegacy<double>( aCfg, "CopperTextSizeV", bp + "defaults.copper_text_size_v" ) )
  278. fromLegacy<double>( aCfg, "PcbTextSizeV", bp + "defaults.copper_text_size_v" );
  279. if( !fromLegacy<double>( aCfg, "CopperTextSizeH", bp + "defaults.copper_text_size_h" ) )
  280. fromLegacy<double>( aCfg, "PcbTextSizeH", bp + "defaults.copper_text_size_h" );
  281. if( !fromLegacy<double>( aCfg, "CopperTextThickness", bp + "defaults.copper_text_thickness" ) )
  282. fromLegacy<double>( aCfg, "PcbTextThickness", bp + "defaults.copper_text_thickness" );
  283. fromLegacy<bool>( aCfg, "CopperTextItalic", bp + "defaults.copper_text_italic" );
  284. fromLegacy<bool>( aCfg, "CopperTextUpright", bp + "defaults.copper_text_upright" );
  285. if( !fromLegacy<double>( aCfg, "EdgeCutLineWidth", bp + "defaults.board_outline_line_width" ) )
  286. fromLegacy<double>( aCfg, "BoardOutlineThickness", bp + "defaults.board_outline_line_width" );
  287. fromLegacy<double>( aCfg, "CourtyardLineWidth", bp + "defaults.courtyard_line_width" );
  288. fromLegacy<double>( aCfg, "FabLineWidth", bp + "defaults.fab_line_width" );
  289. fromLegacy<double>( aCfg, "FabTextSizeV", bp + "defaults.fab_text_size_v" );
  290. fromLegacy<double>( aCfg, "FabTextSizeH", bp + "defaults.fab_text_size_h" );
  291. fromLegacy<double>( aCfg, "FabTextSizeThickness", bp + "defaults.fab_text_thickness" );
  292. fromLegacy<bool>( aCfg, "FabTextItalic", bp + "defaults.fab_text_italic" );
  293. fromLegacy<bool>( aCfg, "FabTextUpright", bp + "defaults.fab_text_upright" );
  294. if( !fromLegacy<double>( aCfg, "OthersLineWidth", bp + "defaults.other_line_width" ) )
  295. fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.other_line_width" );
  296. fromLegacy<double>( aCfg, "OthersTextSizeV", bp + "defaults.other_text_size_v" );
  297. fromLegacy<double>( aCfg, "OthersTextSizeH", bp + "defaults.other_text_size_h" );
  298. fromLegacy<double>( aCfg, "OthersTextSizeThickness", bp + "defaults.other_text_thickness" );
  299. fromLegacy<bool>( aCfg, "OthersTextItalic", bp + "defaults.other_text_italic" );
  300. fromLegacy<bool>( aCfg, "OthersTextUpright", bp + "defaults.other_text_upright" );
  301. fromLegacy<int>( aCfg, "DimensionUnits", bp + "defaults.dimension_units" );
  302. fromLegacy<int>( aCfg, "DimensionPrecision", bp + "defaults.dimension_precision" );
  303. std::string sev = bp + "rule_severities";
  304. fromLegacy<bool>( aCfg, "RequireCourtyardDefinitions", sev + "legacy_no_courtyard_defined" );
  305. fromLegacy<bool>( aCfg, "ProhibitOverlappingCourtyards", sev + "legacy_courtyards_overlap" );
  306. {
  307. int idx = 1;
  308. wxString keyBase = "TrackWidth";
  309. wxString key = keyBase;
  310. double val;
  311. nlohmann::json widths = nlohmann::json::array();
  312. key << idx;
  313. while( aCfg->Read( key, &val ) )
  314. {
  315. widths.push_back( val );
  316. key = keyBase;
  317. key << ++idx;
  318. }
  319. Set( bp + "track_widths", widths );
  320. }
  321. {
  322. int idx = 1;
  323. wxString keyBase = "ViaDiameter";
  324. wxString key = keyBase;
  325. double diameter;
  326. double drill = 1.0;
  327. nlohmann::json vias = nlohmann::json::array();
  328. key << idx;
  329. while( aCfg->Read( key, &diameter ) )
  330. {
  331. key = "ViaDrill";
  332. aCfg->Read( key << idx, &drill );
  333. nlohmann::json via = { { "diameter", diameter }, { "drill", drill } };
  334. vias.push_back( via );
  335. key = keyBase;
  336. key << ++idx;
  337. }
  338. Set( bp + "via_dimensions", vias );
  339. }
  340. {
  341. int idx = 1;
  342. wxString keyBase = "dPairWidth";
  343. wxString key = keyBase;
  344. double width;
  345. double gap = 1.0;
  346. double via_gap = 1.0;
  347. nlohmann::json pairs = nlohmann::json::array();
  348. key << idx;
  349. while( aCfg->Read( key, &width ) )
  350. {
  351. key = "dPairGap";
  352. aCfg->Read( key << idx, &gap );
  353. key = "dPairViaGap";
  354. aCfg->Read( key << idx, &via_gap );
  355. nlohmann::json pair = { { "width", width }, { "gap", gap }, { "via_gap", via_gap } };
  356. pairs.push_back( pair );
  357. key = keyBase;
  358. key << ++idx;
  359. }
  360. Set( bp + "diff_pair_dimensions", pairs );
  361. }
  362. group_blacklist.insert( wxT( "/pcbnew" ) );
  363. // General group is unused these days, we can throw it away
  364. group_blacklist.insert( wxT( "/general" ) );
  365. // Next load sheet names and put all other legacy data in the legacy dict
  366. aCfg->SetPath( wxT( "/" ) );
  367. auto loadSheetNames =
  368. [&]() -> bool
  369. {
  370. int sheet = 1;
  371. wxString entry;
  372. nlohmann::json arr = nlohmann::json::array();
  373. wxLogTrace( traceSettings, wxT( "Migrating sheet names" ) );
  374. aCfg->SetPath( wxT( "/sheetnames" ) );
  375. while( aCfg->Read( wxString::Format( "%d", sheet++ ), &entry ) )
  376. {
  377. wxArrayString tokens = wxSplit( entry, ':' );
  378. if( tokens.size() == 2 )
  379. {
  380. wxLogTrace( traceSettings, wxT( "%d: %s = %s" ), sheet, tokens[0],
  381. tokens[1] );
  382. arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) );
  383. }
  384. }
  385. Set( "sheets", arr );
  386. aCfg->SetPath( "/" );
  387. // TODO: any reason we want to fail on this?
  388. return true;
  389. };
  390. std::vector<wxString> groups;
  391. groups.emplace_back( wxEmptyString );
  392. auto loadLegacyPairs =
  393. [&]( const std::string& aGroup ) -> bool
  394. {
  395. wxLogTrace( traceSettings, wxT( "Migrating group %s" ), aGroup );
  396. bool success = true;
  397. wxString keyStr;
  398. wxString val;
  399. index = 0;
  400. while( aCfg->GetNextEntry( keyStr, index ) )
  401. {
  402. if( !aCfg->Read( keyStr, &val ) )
  403. continue;
  404. std::string key( keyStr.ToUTF8() );
  405. wxLogTrace( traceSettings, wxT( " %s = %s" ), key, val );
  406. try
  407. {
  408. Set( "legacy." + aGroup + "." + key, val );
  409. }
  410. catch( ... )
  411. {
  412. success = false;
  413. }
  414. }
  415. return success;
  416. };
  417. for( size_t i = 0; i < groups.size(); i++ )
  418. {
  419. aCfg->SetPath( groups[i] );
  420. if( groups[i] == wxT( "/sheetnames" ) )
  421. {
  422. ret |= loadSheetNames();
  423. continue;
  424. }
  425. aCfg->DeleteEntry( wxT( "last_client" ), true );
  426. aCfg->DeleteEntry( wxT( "update" ), true );
  427. aCfg->DeleteEntry( wxT( "version" ), true );
  428. ret &= loadLegacyPairs( groups[i].ToStdString() );
  429. index = 0;
  430. while( aCfg->GetNextGroup( str, index ) )
  431. {
  432. wxString group = groups[i] + "/" + str;
  433. if( !group_blacklist.count( group ) )
  434. groups.emplace_back( group );
  435. }
  436. aCfg->SetPath( "/" );
  437. }
  438. return ret;
  439. }
  440. bool PROJECT_FILE::SaveToFile( const wxString& aDirectory, bool aForce )
  441. {
  442. wxASSERT( m_project );
  443. Set( "meta.filename", m_project->GetProjectName() + "." + FILEEXT::ProjectFileExtension );
  444. return JSON_SETTINGS::SaveToFile( aDirectory, aForce );
  445. }
  446. bool PROJECT_FILE::SaveAs( const wxString& aDirectory, const wxString& aFile )
  447. {
  448. wxFileName oldFilename( GetFilename() );
  449. wxString oldProjectName = oldFilename.GetName();
  450. wxString oldProjectPath = oldFilename.GetPath();
  451. Set( "meta.filename", aFile + "." + FILEEXT::ProjectFileExtension );
  452. SetFilename( aFile );
  453. auto updatePath =
  454. [&]( wxString& aPath )
  455. {
  456. if( aPath.StartsWith( oldProjectName + wxS( "." ) ) )
  457. aPath.Replace( oldProjectName, aFile, false );
  458. else if( aPath.StartsWith( oldProjectPath + wxS( "/" ) ) )
  459. aPath.Replace( oldProjectPath, aDirectory, false );
  460. };
  461. updatePath( m_BoardDrawingSheetFile );
  462. for( int ii = LAST_PATH_FIRST; ii < (int) LAST_PATH_SIZE; ++ii )
  463. updatePath( m_PcbLastPath[ ii ] );
  464. auto updatePathByPtr =
  465. [&]( const std::string& aPtr )
  466. {
  467. if( std::optional<wxString> path = Get<wxString>( aPtr ) )
  468. {
  469. updatePath( path.value() );
  470. Set( aPtr, path.value() );
  471. }
  472. };
  473. updatePathByPtr( "schematic.page_layout_descr_file" );
  474. updatePathByPtr( "schematic.plot_directory" );
  475. updatePathByPtr( "schematic.ngspice.workbook_filename" );
  476. updatePathByPtr( "pcbnew.page_layout_descr_file" );
  477. // While performing Save As, we have already checked that we can write to the directory
  478. // so don't carry the previous flag
  479. SetReadOnly( false );
  480. return JSON_SETTINGS::SaveToFile( aDirectory, true );
  481. }
  482. wxString PROJECT_FILE::getFileExt() const
  483. {
  484. return FILEEXT::ProjectFileExtension;
  485. }
  486. wxString PROJECT_FILE::getLegacyFileExt() const
  487. {
  488. return FILEEXT::LegacyProjectFileExtension;
  489. }
  490. void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair )
  491. {
  492. aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } );
  493. }
  494. void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair )
  495. {
  496. wxCHECK( aJson.is_array() && aJson.size() == 2, /* void */ );
  497. aPair.first = KIID( wxString( aJson[0].get<std::string>().c_str(), wxConvUTF8 ) );
  498. aPair.second = wxString( aJson[1].get<std::string>().c_str(), wxConvUTF8 );
  499. }