KiCad PCB EDA Suite
Loading...
Searching...
No Matches
project_file.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Jon Evans <[email protected]>
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
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <project.h>
30#include <settings/parameters.h>
33#include <wx/config.h>
34#include <wx/filename.h>
35#include <wx/log.h>
36
37
40
41
42PROJECT_FILE::PROJECT_FILE( const wxString& aFullPath ) :
44 m_ErcSettings( nullptr ),
45 m_SchematicSettings( nullptr ),
47 m_sheets(),
49 m_boards(),
50 m_project( nullptr ),
51 m_wasMigrated( false )
52{
53 // Keep old files around
55
56 m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "sheets", &m_sheets, {} ) );
57
58 m_params.emplace_back( new PARAM_LIST<TOP_LEVEL_SHEET_INFO>( "schematic.top_level_sheets",
59 &m_topLevelSheets, {} ) );
60
61 m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "boards", &m_boards, {} ) );
62
63 m_params.emplace_back( new PARAM_WXSTRING_MAP( "text_variables",
64 &m_TextVars, {}, false, true /* array behavior, even though stored as a map */ ) );
65
66 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.drawing.field_names",
67 [&]() -> nlohmann::json
68 {
69 nlohmann::json ret = nlohmann::json::array();
70
71 for( const TEMPLATE_FIELDNAME& field :
72 m_TemplateFieldNames.GetTemplateFieldNames( TEMPLATES::SCOPE::PROJECT ) )
73 {
74 ret.push_back( nlohmann::json( {
75 { "name", field.m_Name },
76 { "visible", field.m_Visible },
77 { "url", field.m_URL }
78 } ) );
79 }
80
81 return ret;
82 },
83 [&]( const nlohmann::json& aJson )
84 {
85 if( !aJson.empty() && aJson.is_array() )
86 {
87 m_TemplateFieldNames.DeleteFieldNameTemplates( TEMPLATES::SCOPE::PROJECT );
88
89 for( const nlohmann::json& entry : aJson )
90 {
91 if( entry.contains( "name" ) && entry.contains( "url" )
92 && entry.contains( "visible" ) )
93 {
94 TEMPLATE_FIELDNAME field( entry["name"].get<wxString>() );
95 field.m_URL = entry["url"].get<bool>();
96 field.m_Visible = entry["visible"].get<bool>();
97 m_TemplateFieldNames.AddTemplateFieldName( field, TEMPLATES::SCOPE::PROJECT );
98 }
99 }
100 }
101 }, {} ) );
102
103 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_symbol_libs",
104 &m_PinnedSymbolLibs, {} ) );
105
106 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_footprint_libs",
107 &m_PinnedFootprintLibs, {} ) );
108
109 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_design_block_libs",
111
112 m_params.emplace_back(
113 new PARAM_LIST<wxString>( "pcbnew.find_by_properties.recent_queries", &m_FindByPropertiesQueries, {} ) );
114
115 m_params.emplace_back( new PARAM_PATH_LIST( "cvpcb.equivalence_files",
116 &m_EquivalenceFiles, {} ) );
117
118 m_params.emplace_back( new PARAM_PATH( "pcbnew.page_layout_descr_file",
120
121 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.netlist",
123
124 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.idf",
126
127 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.vrml",
129
130 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.specctra_dsn",
132
133 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.plot",
135
136 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.step", &m_PcbLastPath[LAST_PATH_STEP], "" ) );
137
138 m_params.emplace_back( new PARAM<wxString>( "schematic.legacy_lib_dir",
139 &m_LegacyLibDir, "" ) );
140
141 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.legacy_lib_list",
142 [&]() -> nlohmann::json
143 {
144 nlohmann::json ret = nlohmann::json::array();
145
146 for( const wxString& libName : m_LegacyLibNames )
147 ret.push_back( libName );
148
149 return ret;
150 },
151 [&]( const nlohmann::json& aJson )
152 {
153 if( aJson.empty() || !aJson.is_array() )
154 return;
155
156 m_LegacyLibNames.clear();
157
158 for( const nlohmann::json& entry : aJson )
159 m_LegacyLibNames.push_back( entry.get<wxString>() );
160 }, {} ) );
161
162 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.bus_aliases",
163 [&]() -> nlohmann::json
164 {
165 nlohmann::json ret = nlohmann::json::object();
166
167 for( const auto& alias : m_BusAliases )
168 {
169 nlohmann::json members = nlohmann::json::array();
170
171 for( const wxString& member : alias.second )
172 members.push_back( member );
173
174 ret[ alias.first.ToStdString() ] = members;
175 }
176
177 return ret;
178 },
179 [&]( const nlohmann::json& aJson )
180 {
181 if( !aJson.is_object() )
182 return;
183
184 m_BusAliases.clear();
185
186 for( auto it = aJson.begin(); it != aJson.end(); ++it )
187 {
188 const nlohmann::json& membersJson = it.value();
189
190 if( !membersJson.is_array() )
191 continue;
192
193 std::vector<wxString> members;
194
195 for( const nlohmann::json& entry : membersJson )
196 {
197 if( entry.is_string() )
198 {
199 wxString member = entry.get<wxString>().Strip( wxString::both );
200
201 if( !member.IsEmpty() )
202 members.push_back( member );
203 }
204 }
205
206 wxString name = wxString::FromUTF8( it.key().c_str() ).Strip( wxString::both );
207
208 if( !name.IsEmpty() )
209 m_BusAliases.emplace( name, std::move( members ) );
210 }
211 }, {} ) );
212
213 // Let the save drop deleted aliases instead of merging them back in
214 m_params.back()->SetClearUnknownKeys();
215
216 m_NetSettings = std::make_shared<NET_SETTINGS>( this, "net_settings" );
217
219 std::make_shared<COMPONENT_CLASS_SETTINGS>( this, "component_class_settings" );
220
221 m_tuningProfileParameters = std::make_shared<TUNING_PROFILES>( this, "tuning_profiles" );
222
223 m_params.emplace_back( new PARAM_LAYER_PRESET( "board.layer_presets", &m_LayerPresets ) );
224
225 m_params.emplace_back( new PARAM_VIEWPORT( "board.viewports", &m_Viewports ) );
226
227 m_params.emplace_back( new PARAM_VIEWPORT3D( "board.3dviewports", &m_Viewports3D ) );
228
229 m_params.emplace_back( new PARAM_LAYER_PAIRS( "board.layer_pairs", m_LayerPairInfos ) );
230
231 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.internal_id",
232 &m_IP2581Bom.id, wxEmptyString ) );
233
234 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mpn",
235 &m_IP2581Bom.MPN, wxEmptyString ) );
236
237 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mfg",
238 &m_IP2581Bom.mfg, wxEmptyString ) );
239
240 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.distpn",
241 &m_IP2581Bom.distPN, wxEmptyString ) );
242
243 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.dist",
244 &m_IP2581Bom.dist, wxEmptyString ) );
245
246 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.bom_rev",
247 &m_IP2581Bom.bomRev, wxEmptyString ) );
248
249 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.sch_revision",
250 &m_IP2581Bom.schRevision, wxEmptyString ) );
251
252 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mode",
253 &m_IP2581Bom.mode, wxEmptyString ) );
254
255 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.sections",
256 &m_IP2581Bom.sections, wxEmptyString ) );
257
258 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.net_names",
259 &m_IP2581Bom.netNames, wxEmptyString ) );
260
261 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.ref_des",
262 &m_IP2581Bom.refDes, wxEmptyString ) );
263
264
265 registerMigration( 1, 2, std::bind( &PROJECT_FILE::migrateSchema1To2, this ) );
266 registerMigration( 2, 3, std::bind( &PROJECT_FILE::migrateSchema2To3, this ) );
267}
268
269
271{
272 auto p( "/board/layer_presets"_json_pointer );
273
274 if( !m_internals->contains( p ) || !m_internals->at( p ).is_array() )
275 return true;
276
277 nlohmann::json& presets = m_internals->at( p );
278
279 for( nlohmann::json& entry : presets )
281
282 m_wasMigrated = true;
283
284 return true;
285}
286
287
289{
290 auto p( "/board/layer_presets"_json_pointer );
291
292 if( !m_internals->contains( p ) || !m_internals->at( p ).is_array() )
293 return true;
294
295 nlohmann::json& presets = m_internals->at( p );
296
297 for( nlohmann::json& entry : presets )
299
300 m_wasMigrated = true;
301
302 return true;
303}
304
305
306bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg )
307{
308 bool ret = true;
309 wxString str;
310 long index = 0;
311
312 std::set<wxString> group_blacklist;
313
314 // Legacy files don't store board info; they assume board matches project name
315 // We will leave m_boards empty here so it can be populated with other code
316
317 // First handle migration of data that will be stored locally in this object
318
319 auto loadPinnedLibs =
320 [&]( const std::string& aDest )
321 {
322 int libIndex = 1;
323 wxString libKey = wxT( "PinnedItems" );
324 libKey << libIndex;
325
326 nlohmann::json libs = nlohmann::json::array();
327
328 while( aCfg->Read( libKey, &str ) )
329 {
330 libs.push_back( str );
331
332 aCfg->DeleteEntry( libKey, true );
333
334 libKey = wxT( "PinnedItems" );
335 libKey << ++libIndex;
336 }
337
338 Set( aDest, libs );
339 };
340
341 aCfg->SetPath( wxT( "/LibeditFrame" ) );
342 loadPinnedLibs( "libraries.pinned_symbol_libs" );
343
344 aCfg->SetPath( wxT( "/ModEditFrame" ) );
345 loadPinnedLibs( "libraries.pinned_footprint_libs" );
346
347 aCfg->SetPath( wxT( "/cvpcb/equfiles" ) );
348
349 {
350 int eqIdx = 1;
351 wxString eqKey = wxT( "EquName" );
352 eqKey << eqIdx;
353
354 nlohmann::json eqs = nlohmann::json::array();
355
356 while( aCfg->Read( eqKey, &str ) )
357 {
358 eqs.push_back( str );
359
360 eqKey = wxT( "EquName" );
361 eqKey << ++eqIdx;
362 }
363
364 Set( "cvpcb.equivalence_files", eqs );
365 }
366
367 // All CvPcb params that we want to keep have been migrated above
368 group_blacklist.insert( wxT( "/cvpcb" ) );
369
370 aCfg->SetPath( wxT( "/eeschema" ) );
371 fromLegacyString( aCfg, "LibDir", "schematic.legacy_lib_dir" );
372
373 aCfg->SetPath( wxT( "/eeschema/libraries" ) );
374
375 {
376 int libIdx = 1;
377 wxString libKey = wxT( "LibName" );
378 libKey << libIdx;
379
380 nlohmann::json libs = nlohmann::json::array();
381
382 while( aCfg->Read( libKey, &str ) )
383 {
384 libs.push_back( str );
385
386 libKey = wxT( "LibName" );
387 libKey << ++libIdx;
388 }
389
390 Set( "schematic.legacy_lib_list", libs );
391 }
392
393 group_blacklist.insert( wxT( "/eeschema" ) );
394
395 aCfg->SetPath( wxT( "/text_variables" ) );
396
397 {
398 int txtIdx = 1;
399 wxString txtKey;
400 txtKey << txtIdx;
401
402 nlohmann::json vars = nlohmann::json();
403
404 while( aCfg->Read( txtKey, &str ) )
405 {
406 wxArrayString tokens = wxSplit( str, ':' );
407
408 if( tokens.size() == 2 )
409 vars[ tokens[0].ToStdString() ] = tokens[1];
410
411 txtKey.clear();
412 txtKey << ++txtIdx;
413 }
414
415 Set( "text_variables", vars );
416 }
417
418 group_blacklist.insert( wxT( "/text_variables" ) );
419
420 aCfg->SetPath( wxT( "/schematic_editor" ) );
421
422 fromLegacyString( aCfg, "PageLayoutDescrFile", "schematic.page_layout_descr_file" );
423 fromLegacyString( aCfg, "PlotDirectoryName", "schematic.plot_directory" );
424 fromLegacyString( aCfg, "NetFmtName", "schematic.net_format_name" );
425 fromLegacy<bool>( aCfg, "SpiceAjustPassiveValues", "schematic.spice_adjust_passive_values" );
426 fromLegacy<int>( aCfg, "SubpartIdSeparator", "schematic.subpart_id_separator" );
427 fromLegacy<int>( aCfg, "SubpartFirstId", "schematic.subpart_first_id" );
428
429 fromLegacy<int>( aCfg, "LineThickness", "schematic.drawing.default_line_thickness" );
430 fromLegacy<int>( aCfg, "WireThickness", "schematic.drawing.default_wire_thickness" );
431 fromLegacy<int>( aCfg, "BusThickness", "schematic.drawing.default_bus_thickness" );
432 fromLegacy<int>( aCfg, "LabSize", "schematic.drawing.default_text_size" );
433
434 if( !fromLegacy<int>( aCfg, "PinSymbolSize", "schematic.drawing.pin_symbol_size" ) )
435 {
436 // Use the default symbol size algorithm of Eeschema V5 (based on pin name/number size)
437 Set( "schematic.drawing.pin_symbol_size", 0 );
438 }
439
440 fromLegacy<int>( aCfg, "JunctionSize", "schematic.drawing.default_junction_size" );
441
442 fromLegacyString( aCfg, "FieldNameTemplates", "schematic.drawing.field_names" );
443
444 if( !fromLegacy<double>( aCfg, "TextOffsetRatio", "schematic.drawing.text_offset_ratio" ) )
445 {
446 // Use the spacing of Eeschema V5
447 Set( "schematic.drawing.text_offset_ratio", 0.08 );
448 Set( "schematic.drawing.label_size_ratio", 0.25 );
449 }
450
451 // All schematic_editor keys we keep are migrated above
452 group_blacklist.insert( wxT( "/schematic_editor" ) );
453
454 aCfg->SetPath( wxT( "/pcbnew" ) );
455
456 fromLegacyString( aCfg, "PageLayoutDescrFile", "pcbnew.page_layout_descr_file" );
457 fromLegacyString( aCfg, "LastNetListRead", "pcbnew.last_paths.netlist" );
458 fromLegacyString( aCfg, "LastSTEPExportPath", "pcbnew.last_paths.step" );
459 fromLegacyString( aCfg, "LastIDFExportPath", "pcbnew.last_paths.idf" );
460 fromLegacyString( aCfg, "LastVRMLExportPath", "pcbnew.last_paths.vmrl" );
461 fromLegacyString( aCfg, "LastSpecctraDSNExportPath", "pcbnew.last_paths.specctra_dsn" );
462 fromLegacyString( aCfg, "LastGenCADExportPath", "pcbnew.last_paths.gencad" );
463
464 std::string bp = "board.design_settings.";
465
466 {
467 int idx = 1;
468 wxString key = wxT( "DRCExclusion" );
469 key << idx;
470
471 nlohmann::json exclusions = nlohmann::json::array();
472
473 while( aCfg->Read( key, &str ) )
474 {
475 exclusions.push_back( str );
476
477 key = wxT( "DRCExclusion" );
478 key << ++idx;
479 }
480
481 Set( bp + "drc_exclusions", exclusions );
482 }
483
484 fromLegacy<bool>( aCfg, "AllowMicroVias", bp + "rules.allow_microvias" );
485 fromLegacy<bool>( aCfg, "AllowBlindVias", bp + "rules.allow_blind_buried_vias" );
486 fromLegacy<double>( aCfg, "MinClearance", bp + "rules.min_clearance" );
487 fromLegacy<double>( aCfg, "MinTrackWidth", bp + "rules.min_track_width" );
488 fromLegacy<double>( aCfg, "MinViaAnnulus", bp + "rules.min_via_annulus" );
489 fromLegacy<double>( aCfg, "MinViaDiameter", bp + "rules.min_via_diameter" );
490
491 if( !fromLegacy<double>( aCfg, "MinThroughDrill", bp + "rules.min_through_hole_diameter" ) )
492 fromLegacy<double>( aCfg, "MinViaDrill", bp + "rules.min_through_hole_diameter" );
493
494 fromLegacy<double>( aCfg, "MinMicroViaDiameter", bp + "rules.min_microvia_diameter" );
495 fromLegacy<double>( aCfg, "MinMicroViaDrill", bp + "rules.min_microvia_drill" );
496 fromLegacy<double>( aCfg, "MinHoleToHole", bp + "rules.min_hole_to_hole" );
497 fromLegacy<double>( aCfg, "CopperEdgeClearance", bp + "rules.min_copper_edge_clearance" );
498 fromLegacy<double>( aCfg, "SolderMaskClearance", bp + "rules.solder_mask_clearance" );
499 fromLegacy<double>( aCfg, "SolderMaskMinWidth", bp + "rules.solder_mask_min_width" );
500 fromLegacy<double>( aCfg, "SolderPasteClearance", bp + "rules.solder_paste_clearance" );
501 fromLegacy<double>( aCfg, "SolderPasteRatio", bp + "rules.solder_paste_margin_ratio" );
502
503 if( !fromLegacy<double>( aCfg, "SilkLineWidth", bp + "defaults.silk_line_width" ) )
504 fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.silk_line_width" );
505
506 if( !fromLegacy<double>( aCfg, "SilkTextSizeV", bp + "defaults.silk_text_size_v" ) )
507 fromLegacy<double>( aCfg, "ModuleTextSizeV", bp + "defaults.silk_text_size_v" );
508
509 if( !fromLegacy<double>( aCfg, "SilkTextSizeH", bp + "defaults.silk_text_size_h" ) )
510 fromLegacy<double>( aCfg, "ModuleTextSizeH", bp + "defaults.silk_text_size_h" );
511
512 if( !fromLegacy<double>( aCfg, "SilkTextSizeThickness", bp + "defaults.silk_text_thickness" ) )
513 fromLegacy<double>( aCfg, "ModuleTextSizeThickness", bp + "defaults.silk_text_thickness" );
514
515 fromLegacy<bool>( aCfg, "SilkTextItalic", bp + "defaults.silk_text_italic" );
516 fromLegacy<bool>( aCfg, "SilkTextUpright", bp + "defaults.silk_text_upright" );
517
518 if( !fromLegacy<double>( aCfg, "CopperLineWidth", bp + "defaults.copper_line_width" ) )
519 fromLegacy<double>( aCfg, "DrawSegmentWidth", bp + "defaults.copper_line_width" );
520
521 if( !fromLegacy<double>( aCfg, "CopperTextSizeV", bp + "defaults.copper_text_size_v" ) )
522 fromLegacy<double>( aCfg, "PcbTextSizeV", bp + "defaults.copper_text_size_v" );
523
524 if( !fromLegacy<double>( aCfg, "CopperTextSizeH", bp + "defaults.copper_text_size_h" ) )
525 fromLegacy<double>( aCfg, "PcbTextSizeH", bp + "defaults.copper_text_size_h" );
526
527 if( !fromLegacy<double>( aCfg, "CopperTextThickness", bp + "defaults.copper_text_thickness" ) )
528 fromLegacy<double>( aCfg, "PcbTextThickness", bp + "defaults.copper_text_thickness" );
529
530 fromLegacy<bool>( aCfg, "CopperTextItalic", bp + "defaults.copper_text_italic" );
531 fromLegacy<bool>( aCfg, "CopperTextUpright", bp + "defaults.copper_text_upright" );
532
533 if( !fromLegacy<double>( aCfg, "EdgeCutLineWidth", bp + "defaults.board_outline_line_width" ) )
534 fromLegacy<double>( aCfg, "BoardOutlineThickness",
535 bp + "defaults.board_outline_line_width" );
536
537 fromLegacy<double>( aCfg, "CourtyardLineWidth", bp + "defaults.courtyard_line_width" );
538
539 fromLegacy<double>( aCfg, "FabLineWidth", bp + "defaults.fab_line_width" );
540 fromLegacy<double>( aCfg, "FabTextSizeV", bp + "defaults.fab_text_size_v" );
541 fromLegacy<double>( aCfg, "FabTextSizeH", bp + "defaults.fab_text_size_h" );
542 fromLegacy<double>( aCfg, "FabTextSizeThickness", bp + "defaults.fab_text_thickness" );
543 fromLegacy<bool>( aCfg, "FabTextItalic", bp + "defaults.fab_text_italic" );
544 fromLegacy<bool>( aCfg, "FabTextUpright", bp + "defaults.fab_text_upright" );
545
546 if( !fromLegacy<double>( aCfg, "OthersLineWidth", bp + "defaults.other_line_width" ) )
547 fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.other_line_width" );
548
549 fromLegacy<double>( aCfg, "OthersTextSizeV", bp + "defaults.other_text_size_v" );
550 fromLegacy<double>( aCfg, "OthersTextSizeH", bp + "defaults.other_text_size_h" );
551 fromLegacy<double>( aCfg, "OthersTextSizeThickness", bp + "defaults.other_text_thickness" );
552 fromLegacy<bool>( aCfg, "OthersTextItalic", bp + "defaults.other_text_italic" );
553 fromLegacy<bool>( aCfg, "OthersTextUpright", bp + "defaults.other_text_upright" );
554
555 fromLegacy<int>( aCfg, "DimensionUnits", bp + "defaults.dimension_units" );
556 fromLegacy<int>( aCfg, "DimensionPrecision", bp + "defaults.dimension_precision" );
557
558 std::string sev = bp + "rule_severities";
559
560 fromLegacy<bool>( aCfg, "RequireCourtyardDefinitions", sev + "legacy_no_courtyard_defined" );
561
562 fromLegacy<bool>( aCfg, "ProhibitOverlappingCourtyards", sev + "legacy_courtyards_overlap" );
563
564 {
565 int idx = 1;
566 wxString keyBase = "TrackWidth";
567 wxString key = keyBase;
568 double val;
569
570 nlohmann::json widths = nlohmann::json::array();
571
572 key << idx;
573
574 while( aCfg->Read( key, &val ) )
575 {
576 widths.push_back( val );
577 key = keyBase;
578 key << ++idx;
579 }
580
581 Set( bp + "track_widths", widths );
582 }
583
584 {
585 int idx = 1;
586 wxString keyBase = "ViaDiameter";
587 wxString key = keyBase;
588 double diameter;
589 double drill = 1.0;
590
591 nlohmann::json vias = nlohmann::json::array();
592
593 key << idx;
594
595 while( aCfg->Read( key, &diameter ) )
596 {
597 key = "ViaDrill";
598 aCfg->Read( key << idx, &drill );
599
600 nlohmann::json via = { { "diameter", diameter }, { "drill", drill } };
601 vias.push_back( via );
602
603 key = keyBase;
604 key << ++idx;
605 }
606
607 Set( bp + "via_dimensions", vias );
608 }
609
610 {
611 int idx = 1;
612 wxString keyBase = "dPairWidth";
613 wxString key = keyBase;
614 double width;
615 double gap = 1.0;
616 double via_gap = 1.0;
617
618 nlohmann::json pairs = nlohmann::json::array();
619
620 key << idx;
621
622 while( aCfg->Read( key, &width ) )
623 {
624 key = "dPairGap";
625 aCfg->Read( key << idx, &gap );
626
627 key = "dPairViaGap";
628 aCfg->Read( key << idx, &via_gap );
629
630 nlohmann::json pair = { { "width", width }, { "gap", gap }, { "via_gap", via_gap } };
631 pairs.push_back( pair );
632
633 key = keyBase;
634 key << ++idx;
635 }
636
637 Set( bp + "diff_pair_dimensions", pairs );
638 }
639
640 group_blacklist.insert( wxT( "/pcbnew" ) );
641
642 // General group is unused these days, we can throw it away
643 group_blacklist.insert( wxT( "/general" ) );
644
645 // Next load sheet names and put all other legacy data in the legacy dict
646 aCfg->SetPath( wxT( "/" ) );
647
648 auto loadSheetNames =
649 [&]() -> bool
650 {
651 int sheet = 1;
652 wxString entry;
653 nlohmann::json arr = nlohmann::json::array();
654
655 wxLogTrace( traceSettings, wxT( "Migrating sheet names" ) );
656
657 aCfg->SetPath( wxT( "/sheetnames" ) );
658
659 while( aCfg->Read( wxString::Format( "%d", sheet++ ), &entry ) )
660 {
661 wxArrayString tokens = wxSplit( entry, ':' );
662
663 if( tokens.size() == 2 )
664 {
665 wxLogTrace( traceSettings, wxT( "%d: %s = %s" ), sheet, tokens[0],
666 tokens[1] );
667 arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) );
668 }
669 }
670
671 Set( "sheets", arr );
672
673 aCfg->SetPath( "/" );
674
675 return true;
676 };
677
678 std::vector<wxString> groups;
679
680 groups.emplace_back( wxEmptyString );
681
682 auto loadLegacyPairs =
683 [&]( const std::string& aGroup ) -> bool
684 {
685 wxLogTrace( traceSettings, wxT( "Migrating group %s" ), aGroup );
686 bool success = true;
687 wxString keyStr;
688 wxString val;
689
690 index = 0;
691
692 while( aCfg->GetNextEntry( keyStr, index ) )
693 {
694 if( !aCfg->Read( keyStr, &val ) )
695 continue;
696
697 std::string key( keyStr.ToUTF8() );
698
699 wxLogTrace( traceSettings, wxT( " %s = %s" ), key, val );
700
701 try
702 {
703 Set( "legacy." + aGroup + "." + key, val );
704 }
705 catch( ... )
706 {
707 success = false;
708 }
709 }
710
711 return success;
712 };
713
714 for( size_t i = 0; i < groups.size(); i++ )
715 {
716 aCfg->SetPath( groups[i] );
717
718 if( groups[i] == wxT( "/sheetnames" ) )
719 {
720 ret |= loadSheetNames();
721 continue;
722 }
723
724 aCfg->DeleteEntry( wxT( "last_client" ), true );
725 aCfg->DeleteEntry( wxT( "update" ), true );
726 aCfg->DeleteEntry( wxT( "version" ), true );
727
728 ret &= loadLegacyPairs( groups[i].ToStdString() );
729
730 index = 0;
731
732 while( aCfg->GetNextGroup( str, index ) )
733 {
734 wxString group = groups[i] + "/" + str;
735
736 if( !group_blacklist.count( group ) )
737 groups.emplace_back( group );
738 }
739
740 aCfg->SetPath( "/" );
741 }
742
743 return ret;
744}
745
746
747bool PROJECT_FILE::LoadFromFile( const wxString& aDirectory )
748{
749 bool success = JSON_SETTINGS::LoadFromFile( aDirectory );
750
751 if( success )
752 {
753 // Migrate from old single-root format to top_level_sheets format
754 if( m_topLevelSheets.empty() && m_project )
755 {
756 // Create a default top-level sheet entry based on the project name
757 wxString projectName = m_project->GetProjectName();
758
759 TOP_LEVEL_SHEET_INFO defaultSheet;
760 defaultSheet.uuid = niluuid; // Use niluuid for the first/default sheet
761 defaultSheet.name = projectName;
762 defaultSheet.filename = projectName + ".kicad_sch";
763
764 m_topLevelSheets.push_back( std::move( defaultSheet ) );
765
766 // Mark as migrated so it will be saved with the new format
767 m_wasMigrated = true;
768
769 wxLogTrace( traceSettings, wxT( "PROJECT_FILE: Migrated old single-root format to top_level_sheets" ) );
770 }
771
772 // When a project is created from a template, the top_level_sheets entries may
773 // still reference the template's schematic filenames rather than the new project's.
774 // The template copy renames files on disk but doesn't update the .kicad_pro content.
775 // Detect this and fix the references so the schematic can be found.
776 if( !m_topLevelSheets.empty() && m_project )
777 {
778 wxString projectPath = m_project->GetProjectPath();
779 wxString projectName = m_project->GetProjectName();
780
781 for( TOP_LEVEL_SHEET_INFO& sheetInfo : m_topLevelSheets )
782 {
783 wxFileName referencedFile( projectPath, sheetInfo.filename );
784
785 if( referencedFile.FileExists() )
786 continue;
787
788 // Try the project-name-based filename
789 wxString expectedFile =
790 projectName + wxS( "." ) + FILEEXT::KiCadSchematicFileExtension;
791
792 wxFileName candidateFile( projectPath, expectedFile );
793
794 if( candidateFile.FileExists() )
795 {
796 wxLogTrace( traceSettings,
797 wxT( "PROJECT_FILE: Fixing stale top_level_sheets reference "
798 "'%s' -> '%s'" ),
799 sheetInfo.filename, expectedFile );
800
801 sheetInfo.filename = expectedFile;
802 sheetInfo.name = projectName;
803 m_wasMigrated = true;
804 }
805 }
806 }
807 }
808
809 return success;
810}
811
812
813bool PROJECT_FILE::SaveToFile( const wxString& aDirectory, bool aForce )
814{
815 wxASSERT( m_project );
816
817 Set( "meta.filename", m_project->GetProjectName() + "." + FILEEXT::ProjectFileExtension );
818
819 // Even if parameters were not modified, we should resave after migration
820 bool force = aForce || m_wasMigrated;
821
822 // If we're actually going ahead and doing the save, the flag that keeps code from doing the
823 // save should be cleared at this.
824 m_wasMigrated = false;
825
826 return JSON_SETTINGS::SaveToFile( aDirectory, force );
827}
828
829
830bool PROJECT_FILE::SaveAs( const wxString& aDirectory, const wxString& aFile )
831{
832 wxFileName oldFilename( GetFilename() );
833 wxString oldProjectName = oldFilename.GetName();
834 wxString oldProjectPath = oldFilename.GetPath();
835
836 Set( "meta.filename", aFile + "." + FILEEXT::ProjectFileExtension );
837 SetFilename( aFile );
838
839 auto updatePath =
840 [&]( wxString& aPath )
841 {
842 if( aPath.StartsWith( oldProjectName + wxS( "." ) ) )
843 aPath.Replace( oldProjectName, aFile, false );
844 else if( aPath.StartsWith( oldProjectPath + wxS( "/" ) ) )
845 aPath.Replace( oldProjectPath, aDirectory, false );
846 };
847
848 updatePath( m_BoardDrawingSheetFile );
849
850 for( int ii = LAST_PATH_FIRST; ii < (int) LAST_PATH_SIZE; ++ii )
851 updatePath( m_PcbLastPath[ ii ] );
852
853 auto updatePathByPtr =
854 [&]( const std::string& aPtr )
855 {
856 if( std::optional<wxString> path = Get<wxString>( aPtr ) )
857 {
858 updatePath( path.value() );
859 Set( aPtr, path.value() );
860 }
861 };
862
863 updatePathByPtr( "schematic.page_layout_descr_file" );
864 updatePathByPtr( "schematic.plot_directory" );
865 updatePathByPtr( "schematic.ngspice.workbook_filename" );
866 updatePathByPtr( "pcbnew.page_layout_descr_file" );
867
868 for( auto& sheetInfo : m_topLevelSheets )
869 {
870 updatePath( sheetInfo.filename );
871
872 // Also update the display name if it matches the old project name
873 if( sheetInfo.name == oldProjectName )
874 sheetInfo.name = aFile;
875 }
876
877 // If we're actually going ahead and doing the save, the flag that keeps code from doing the save
878 // should be cleared at this point
879 m_wasMigrated = false;
880
881 // While performing Save As, we have already checked that we can write to the directory
882 // so don't carry the previous flag
883 SetReadOnly( false );
884 return JSON_SETTINGS::SaveToFile( aDirectory, true );
885}
886
887
889{
891}
892
893
898
899
900void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair )
901{
902 aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } );
903}
904
905
906void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair )
907{
908 wxCHECK( aJson.is_array() && aJson.size() == 2, /* void */ );
909 aPair.first = KIID( wxString( aJson[0].get<std::string>().c_str(), wxConvUTF8 ) );
910 aPair.second = wxString( aJson[1].get<std::string>().c_str(), wxConvUTF8 );
911}
912
913
914void to_json( nlohmann::json& aJson, const TOP_LEVEL_SHEET_INFO& aInfo )
915{
916 aJson = nlohmann::json::object();
917 aJson["uuid"] = aInfo.uuid.AsString().ToUTF8();
918 aJson["name"] = aInfo.name.ToUTF8();
919 aJson["filename"] = aInfo.filename.ToUTF8();
920}
921
922
923void from_json( const nlohmann::json& aJson, TOP_LEVEL_SHEET_INFO& aInfo )
924{
925 wxCHECK( aJson.is_object(), /* void */ );
926
927 if( aJson.contains( "uuid" ) )
928 aInfo.uuid = KIID( wxString( aJson["uuid"].get<std::string>().c_str(), wxConvUTF8 ) );
929
930 if( aJson.contains( "name" ) )
931 aInfo.name = wxString( aJson["name"].get<std::string>().c_str(), wxConvUTF8 );
932
933 if( aJson.contains( "filename" ) )
934 aInfo.filename = wxString( aJson["filename"].get<std::string>().c_str(), wxConvUTF8 );
935}
int index
const char * name
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
void SetReadOnly(bool aReadOnly)
std::optional< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
bool m_deleteLegacyAfterMigration
Whether or not to delete legacy file after migration.
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
void SetFilename(const wxString &aFilename)
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
wxString GetFilename() const
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
Like a normal param, but with custom getter and setter functions.
Definition parameters.h:299
static void MigrateToV9Layers(nlohmann::json &aJson)
static void MigrateToNamedRenderLayers(nlohmann::json &aJson)
Represents a list of strings holding directory paths.
Definition parameters.h:693
Stores a path as a string with directory separators normalized to unix-style.
Definition parameters.h:178
A helper for <wxString, wxString> maps.
Definition parameters.h:840
std::map< wxString, wxString > m_TextVars
wxString getFileExt() const override
std::vector< LAYER_PAIR_INFO > m_LayerPairInfos
List of stored 3D viewports (view matrixes)
ERC_SETTINGS * m_ErcSettings
Eeschema params.
wxString m_LegacyLibDir
SCHEMATIC_SETTINGS * m_SchematicSettings
bool migrateSchema1To2()
IPC-2581 BOM settings.
wxString m_BoardDrawingSheetFile
PcbNew params.
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
struct IP2581_BOM m_IP2581Bom
Layer pair list for the board.
wxString m_PcbLastPath[LAST_PATH_SIZE]
MRU path storage.
TEMPLATES m_TemplateFieldNames
Project and global field name templates shared by project editors.
PROJECT * m_project
A link to the owning PROJECT.
std::vector< TOP_LEVEL_SHEET_INFO > m_topLevelSheets
A list of top-level schematic sheets in this project.
std::vector< VIEWPORT > m_Viewports
List of stored layer presets.
BOARD_DESIGN_SETTINGS * m_BoardSettings
Board design settings for this project's board.
std::vector< wxString > m_PinnedDesignBlockLibs
The list of pinned design block libraries.
bool SaveAs(const wxString &aDirectory, const wxString &aFile)
std::map< wxString, std::vector< wxString > > m_BusAliases
Bus alias definitions for the schematic project.
std::vector< wxString > m_EquivalenceFiles
CvPcb params.
bool migrateSchema2To3()
Schema version 3: move layer presets to use named render layers.
wxString getLegacyFileExt() const override
std::vector< wxString > m_PinnedFootprintLibs
The list of pinned footprint libraries.
bool LoadFromFile(const wxString &aDirectory="") override
Loads the backing file from disk and then calls Load()
std::vector< FILE_INFO_PAIR > m_sheets
An list of schematic sheets in this project.
std::vector< wxString > m_FindByPropertiesQueries
Recent queries for Find by Properties dialog.
virtual bool MigrateFromLegacy(wxConfigBase *aCfg) override
Migrates from wxConfig to JSON-based configuration.
std::vector< LAYER_PRESET > m_LayerPresets
std::vector< FILE_INFO_PAIR > m_boards
A list of board files in this project.
std::shared_ptr< TUNING_PROFILES > m_tuningProfileParameters
Tuning profile parameters for this project.
wxArrayString m_LegacyLibNames
std::vector< wxString > m_PinnedSymbolLibs
Below are project-level settings that have not been moved to a dedicated file.
std::vector< VIEWPORT3D > m_Viewports3D
List of stored viewports (pos + zoom)
bool SaveToFile(const wxString &aDirectory="", bool aForce=false) override
Calls Store() and then writes the contents of the JSON document to a file.
PROJECT_FILE(const wxString &aFullPath)
Construct the project file for a project.
std::shared_ptr< COMPONENT_CLASS_SETTINGS > m_ComponentClassSettings
Component class settings for the project (owned here)
Container for project specific data.
Definition project.h:63
static std::string ToStdString(const wxString &aStr)
static const std::string ProjectFileExtension
static const std::string LegacyProjectFileExtension
static const std::string KiCadSchematicFileExtension
SETTINGS_LOC
#define traceSettings
KIID niluuid(0)
void to_json(nlohmann::json &aJson, const FILE_INFO_PAIR &aPair)
void from_json(const nlohmann::json &aJson, FILE_INFO_PAIR &aPair)
const int projectFileSchemaVersion
! Update the schema version whenever a migration is required
@ LAST_PATH_PLOT
@ LAST_PATH_SPECCTRADSN
@ LAST_PATH_SIZE
@ LAST_PATH_FIRST
@ LAST_PATH_IDF
@ LAST_PATH_VRML
@ LAST_PATH_NETLIST
@ LAST_PATH_STEP
std::pair< KIID, wxString > FILE_INFO_PAIR
For files like sheets and boards, a pair of that object KIID and display name Display name is typical...
Hold a name of a symbol's field, field value, and default visibility.
Information about a top-level schematic sheet.
KIID uuid
Unique identifier for the sheet.
wxString name
Display name for the sheet.
wxString filename
Relative path to the sheet file.
std::string path
Definition of file extensions used in Kicad.