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>
31#include <settings/parameters.h>
34#include <wx/config.h>
35#include <wx/filename.h>
36#include <wx/log.h>
37
38
41
42
43PROJECT_FILE::PROJECT_FILE( const wxString& aFullPath ) :
45 m_ErcSettings( nullptr ),
46 m_SchematicSettings( nullptr ),
48 m_sheets(),
50 m_boards(),
51 m_project( nullptr ),
52 m_wasMigrated( false )
53{
54 // Keep old files around
56
57 m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "sheets", &m_sheets, {} ) );
58
59 m_params.emplace_back( new PARAM_LIST<TOP_LEVEL_SHEET_INFO>( "schematic.top_level_sheets",
60 &m_topLevelSheets, {} ) );
61
62 m_params.emplace_back( new PARAM_LIST<FILE_INFO_PAIR>( "boards", &m_boards, {} ) );
63
64 m_params.emplace_back( new PARAM_WXSTRING_MAP( "text_variables",
65 &m_TextVars, {}, false, true /* array behavior, even though stored as a map */ ) );
66
67 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
68 "schematic.drawing.field_names",
69 [&]() -> nlohmann::json
70 {
71 nlohmann::json ret = nlohmann::json::array();
72
73 for( const TEMPLATE_FIELDNAME& field :
74 m_TemplateFieldNames.GetTemplateFieldNames( TEMPLATES::SCOPE::PROJECT ) )
75 {
76 ret.push_back( nlohmann::json( {
77 { "name", field.m_Name },
78 { "visible", field.m_Visible },
79 { "url", field.m_URL }
80 } ) );
81 }
82
83 return ret;
84 },
85 [&]( const nlohmann::json& aJson )
86 {
87 if( aJson.is_string() )
88 {
89 m_TemplateFieldNames.DeleteFieldNameTemplates( TEMPLATES::SCOPE::PROJECT );
90 m_TemplateFieldNames.AddTemplateFieldNames( wxString::FromUTF8( aJson.get<std::string>() ),
91 TEMPLATES::SCOPE::PROJECT );
92 }
93 else if( aJson.is_array() )
94 {
95 m_TemplateFieldNames.DeleteFieldNameTemplates( TEMPLATES::SCOPE::PROJECT );
96
97 for( const nlohmann::json& entry : aJson )
98 {
99 if( entry.contains( "name" ) && entry.contains( "url" )
100 && entry.contains( "visible" ) )
101 {
102 TEMPLATE_FIELDNAME field( entry["name"].get<wxString>() );
103 field.m_URL = entry["url"].get<bool>();
104 field.m_Visible = entry["visible"].get<bool>();
105 m_TemplateFieldNames.AddTemplateFieldName( field, TEMPLATES::SCOPE::PROJECT );
106 }
107 }
108 }
109 },
110 {} ) );
111
112 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_symbol_libs",
113 &m_PinnedSymbolLibs, {} ) );
114
115 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_footprint_libs",
116 &m_PinnedFootprintLibs, {} ) );
117
118 m_params.emplace_back( new PARAM_LIST<wxString>( "libraries.pinned_design_block_libs",
120
121 m_params.emplace_back(
122 new PARAM_LIST<wxString>( "pcbnew.find_by_properties.recent_queries", &m_FindByPropertiesQueries, {} ) );
123
124 m_params.emplace_back( new PARAM_PATH_LIST( "cvpcb.equivalence_files",
125 &m_EquivalenceFiles, {} ) );
126
127 m_params.emplace_back( new PARAM_PATH( "pcbnew.page_layout_descr_file",
129
130 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.netlist",
132
133 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.idf",
135
136 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.vrml",
138
139 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.specctra_dsn",
141
142 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.plot",
144
145 m_params.emplace_back( new PARAM_PATH( "pcbnew.last_paths.step", &m_PcbLastPath[LAST_PATH_STEP], "" ) );
146
147 m_params.emplace_back( new PARAM<wxString>( "schematic.legacy_lib_dir",
148 &m_LegacyLibDir, "" ) );
149
150 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.legacy_lib_list",
151 [&]() -> nlohmann::json
152 {
153 nlohmann::json ret = nlohmann::json::array();
154
155 for( const wxString& libName : m_LegacyLibNames )
156 ret.push_back( libName );
157
158 return ret;
159 },
160 [&]( const nlohmann::json& aJson )
161 {
162 if( aJson.empty() || !aJson.is_array() )
163 return;
164
165 m_LegacyLibNames.clear();
166
167 for( const nlohmann::json& entry : aJson )
168 m_LegacyLibNames.push_back( entry.get<wxString>() );
169 }, {} ) );
170
171 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.bus_aliases",
172 [&]() -> nlohmann::json
173 {
174 // A project may be saved before its legacy schematic aliases are imported
175 if( !m_BusAliasesDefined && m_BusAliases.empty() )
176 return nlohmann::json();
177
178 nlohmann::json ret = nlohmann::json::object();
179
180 for( const auto& alias : m_BusAliases )
181 {
182 nlohmann::json members = nlohmann::json::array();
183
184 for( const wxString& member : alias.second )
185 members.push_back( member );
186
187 ret[ alias.first.ToStdString() ] = members;
188 }
189
190 return ret;
191 },
192 [&]( const nlohmann::json& aJson )
193 {
194 m_BusAliasesDefined = aJson.is_object();
195 m_BusAliases.clear();
196
197 if( !aJson.is_object() )
198 return;
199
200 for( auto it = aJson.begin(); it != aJson.end(); ++it )
201 {
202 const nlohmann::json& membersJson = it.value();
203
204 if( !membersJson.is_array() )
205 continue;
206
207 std::vector<wxString> members;
208
209 for( const nlohmann::json& entry : membersJson )
210 {
211 if( entry.is_string() )
212 {
213 wxString member = entry.get<wxString>().Strip( wxString::both );
214
215 if( !member.IsEmpty() )
216 members.push_back( member );
217 }
218 }
219
220 wxString name = wxString::FromUTF8( it.key().c_str() ).Strip( wxString::both );
221
222 if( !name.IsEmpty() )
223 m_BusAliases.emplace( name, std::move( members ) );
224 }
225 }, {} ) );
226
227 // Let the save drop deleted aliases instead of merging them back in
228 m_params.back()->SetClearUnknownKeys();
229
230 m_NetSettings = std::make_shared<NET_SETTINGS>( this, "net_settings" );
231
233 std::make_shared<COMPONENT_CLASS_SETTINGS>( this, "component_class_settings" );
234
235 m_tuningProfileParameters = std::make_shared<TUNING_PROFILES>( this, "tuning_profiles" );
236
237 m_params.emplace_back( new PARAM_LAYER_PRESET( "board.layer_presets", &m_LayerPresets ) );
238
239 m_params.emplace_back( new PARAM_VIEWPORT( "board.viewports", &m_Viewports ) );
240
241 m_params.emplace_back( new PARAM_VIEWPORT3D( "board.3dviewports", &m_Viewports3D ) );
242
243 m_params.emplace_back( new PARAM_LAYER_PAIRS( "board.layer_pairs", m_LayerPairInfos ) );
244
245 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.internal_id",
246 &m_IP2581Bom.id, wxEmptyString ) );
247
248 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mpn",
249 &m_IP2581Bom.MPN, wxEmptyString ) );
250
251 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mfg",
252 &m_IP2581Bom.mfg, wxEmptyString ) );
253
254 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.distpn",
255 &m_IP2581Bom.distPN, wxEmptyString ) );
256
257 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.dist",
258 &m_IP2581Bom.dist, wxEmptyString ) );
259
260 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.bom_rev",
261 &m_IP2581Bom.bomRev, wxEmptyString ) );
262
263 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.sch_revision",
264 &m_IP2581Bom.schRevision, wxEmptyString ) );
265
266 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.mode",
267 &m_IP2581Bom.mode, wxEmptyString ) );
268
269 // An empty key alone means the mode picks the sections, so the flag marks an empty choice
270 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "board.ipc2581.sections",
271 [this]() { return nlohmann::json( m_IP2581Bom.sections.value_or( wxString() ) ); },
272 [this]( const nlohmann::json& aVal )
273 {
274 wxString sections = aVal.is_string() ? aVal.get<wxString>() : wxString();
275 m_IP2581Bom.sections = sections.IsEmpty() ? std::nullopt : std::optional<wxString>( sections );
276 },
277 nlohmann::json( "" ) ) );
278
279 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "board.ipc2581.custom_sections",
280 [this]() { return nlohmann::json( m_IP2581Bom.sections && m_IP2581Bom.sections->IsEmpty() ); },
281 [this]( const nlohmann::json& aVal )
282 {
283 if( aVal.is_boolean() && aVal.get<bool>() && !m_IP2581Bom.sections )
284 m_IP2581Bom.sections = wxString();
285 },
286 nlohmann::json( false ) ) );
287
288 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.net_names",
289 &m_IP2581Bom.netNames, wxEmptyString ) );
290
291 m_params.emplace_back( new PARAM<wxString>( "board.ipc2581.ref_des",
292 &m_IP2581Bom.refDes, wxEmptyString ) );
293
294 m_params.emplace_back( new PARAM_ENUM<IDF_SETTINGS::UNITS>( "board.idf_export.units",
297
299 "board.idf_export.origin_mode", &m_IdfExportSettings.originMode,
302
303 m_params.emplace_back( new PARAM<double>( "board.idf_export.user_origin_x",
304 &m_IdfExportSettings.userOriginX, 0.0 ) );
305
306 m_params.emplace_back( new PARAM<double>( "board.idf_export.user_origin_y",
307 &m_IdfExportSettings.userOriginY, 0.0 ) );
308
309 m_params.emplace_back( new PARAM<bool>( "board.idf_export.include_unspecified",
310 &m_IdfExportSettings.includeUnspecified, true ) );
311
312 m_params.emplace_back( new PARAM<bool>( "board.idf_export.include_dnp",
313 &m_IdfExportSettings.includeDNP, true ) );
314
315 m_params.emplace_back( new PARAM<bool>( "board.idf_export.calculate_height_from_models",
316 &m_IdfExportSettings.calculateHeightFromModels, true ) );
317
318 m_params.emplace_back( new PARAM<wxString>( "board.idf_export.part_number_field",
319 &m_IdfExportSettings.partNumberField, wxS( "Value" ) ) );
320
321
322 registerMigration( 1, 2, std::bind( &PROJECT_FILE::migrateSchema1To2, this ) );
323 registerMigration( 2, 3, std::bind( &PROJECT_FILE::migrateSchema2To3, this ) );
324 registerMigration( 3, 4, std::bind( &PROJECT_FILE::migrateSchema3To4, this ) );
325}
326
327
329{
330 auto p( "/board/layer_presets"_json_pointer );
331
332 if( !m_internals->contains( p ) || !m_internals->at( p ).is_array() )
333 return true;
334
335 nlohmann::json& presets = m_internals->at( p );
336
337 for( nlohmann::json& entry : presets )
339
340 m_wasMigrated = true;
341
342 return true;
343}
344
345
347{
348 auto p( "/board/layer_presets"_json_pointer );
349
350 if( !m_internals->contains( p ) || !m_internals->at( p ).is_array() )
351 return true;
352
353 nlohmann::json& presets = m_internals->at( p );
354
355 for( nlohmann::json& entry : presets )
357
358 m_wasMigrated = true;
359
360 return true;
361}
362
363
365{
366 const auto aliases = GetJson( "schematic.bus_aliases" );
367
368 if( aliases && aliases->is_object() && aliases->empty() )
369 {
370 // Older project-only saves wrote empty tables before importing legacy sheet aliases
371 Set( "schematic.bus_aliases", nlohmann::json() );
372 m_wasMigrated = true;
373 }
374
375 return true;
376}
377
378
379bool PROJECT_FILE::MigrateFromLegacy( wxConfigBase* aCfg )
380{
381 bool ret = true;
382 wxString str;
383 long index = 0;
384
385 std::set<wxString> group_blacklist;
386
387 // Legacy files don't store board info; they assume board matches project name
388 // We will leave m_boards empty here so it can be populated with other code
389
390 // First handle migration of data that will be stored locally in this object
391
392 auto loadPinnedLibs =
393 [&]( const std::string& aDest )
394 {
395 int libIndex = 1;
396 wxString libKey = wxT( "PinnedItems" );
397 libKey << libIndex;
398
399 nlohmann::json libs = nlohmann::json::array();
400
401 while( aCfg->Read( libKey, &str ) )
402 {
403 libs.push_back( str );
404
405 aCfg->DeleteEntry( libKey, true );
406
407 libKey = wxT( "PinnedItems" );
408 libKey << ++libIndex;
409 }
410
411 Set( aDest, libs );
412 };
413
414 aCfg->SetPath( wxT( "/LibeditFrame" ) );
415 loadPinnedLibs( "libraries.pinned_symbol_libs" );
416
417 aCfg->SetPath( wxT( "/ModEditFrame" ) );
418 loadPinnedLibs( "libraries.pinned_footprint_libs" );
419
420 aCfg->SetPath( wxT( "/cvpcb/equfiles" ) );
421
422 {
423 int eqIdx = 1;
424 wxString eqKey = wxT( "EquName" );
425 eqKey << eqIdx;
426
427 nlohmann::json eqs = nlohmann::json::array();
428
429 while( aCfg->Read( eqKey, &str ) )
430 {
431 eqs.push_back( str );
432
433 eqKey = wxT( "EquName" );
434 eqKey << ++eqIdx;
435 }
436
437 Set( "cvpcb.equivalence_files", eqs );
438 }
439
440 // All CvPcb params that we want to keep have been migrated above
441 group_blacklist.insert( wxT( "/cvpcb" ) );
442
443 aCfg->SetPath( wxT( "/eeschema" ) );
444 fromLegacyString( aCfg, "LibDir", "schematic.legacy_lib_dir" );
445
446 aCfg->SetPath( wxT( "/eeschema/libraries" ) );
447
448 {
449 int libIdx = 1;
450 wxString libKey = wxT( "LibName" );
451 libKey << libIdx;
452
453 nlohmann::json libs = nlohmann::json::array();
454
455 while( aCfg->Read( libKey, &str ) )
456 {
457 libs.push_back( str );
458
459 libKey = wxT( "LibName" );
460 libKey << ++libIdx;
461 }
462
463 Set( "schematic.legacy_lib_list", libs );
464 }
465
466 group_blacklist.insert( wxT( "/eeschema" ) );
467
468 aCfg->SetPath( wxT( "/text_variables" ) );
469
470 {
471 int txtIdx = 1;
472 wxString txtKey;
473 txtKey << txtIdx;
474
475 nlohmann::json vars = nlohmann::json();
476
477 while( aCfg->Read( txtKey, &str ) )
478 {
479 wxArrayString tokens = wxSplit( str, ':' );
480
481 if( tokens.size() == 2 )
482 vars[ tokens[0].ToStdString() ] = tokens[1];
483
484 txtKey.clear();
485 txtKey << ++txtIdx;
486 }
487
488 Set( "text_variables", vars );
489 }
490
491 group_blacklist.insert( wxT( "/text_variables" ) );
492
493 aCfg->SetPath( wxT( "/schematic_editor" ) );
494
495 fromLegacyString( aCfg, "PageLayoutDescrFile", "schematic.page_layout_descr_file" );
496 fromLegacyString( aCfg, "PlotDirectoryName", "schematic.plot_directory" );
497 fromLegacyString( aCfg, "NetFmtName", "schematic.net_format_name" );
498 fromLegacy<bool>( aCfg, "SpiceAjustPassiveValues", "schematic.spice_adjust_passive_values" );
499 fromLegacy<int>( aCfg, "SubpartIdSeparator", "schematic.subpart_id_separator" );
500 fromLegacy<int>( aCfg, "SubpartFirstId", "schematic.subpart_first_id" );
501
502 fromLegacy<int>( aCfg, "LineThickness", "schematic.drawing.default_line_thickness" );
503 fromLegacy<int>( aCfg, "WireThickness", "schematic.drawing.default_wire_thickness" );
504 fromLegacy<int>( aCfg, "BusThickness", "schematic.drawing.default_bus_thickness" );
505 fromLegacy<int>( aCfg, "LabSize", "schematic.drawing.default_text_size" );
506
507 if( !fromLegacy<int>( aCfg, "PinSymbolSize", "schematic.drawing.pin_symbol_size" ) )
508 {
509 // Use the default symbol size algorithm of Eeschema V5 (based on pin name/number size)
510 Set( "schematic.drawing.pin_symbol_size", 0 );
511 }
512
513 fromLegacy<int>( aCfg, "JunctionSize", "schematic.drawing.default_junction_size" );
514
515 fromLegacyString( aCfg, "FieldNameTemplates", "schematic.drawing.field_names" );
516
517 if( !fromLegacy<double>( aCfg, "TextOffsetRatio", "schematic.drawing.text_offset_ratio" ) )
518 {
519 // Use the spacing of Eeschema V5
520 Set( "schematic.drawing.text_offset_ratio", 0.08 );
521 Set( "schematic.drawing.label_size_ratio", 0.25 );
522 }
523
524 // All schematic_editor keys we keep are migrated above
525 group_blacklist.insert( wxT( "/schematic_editor" ) );
526
527 aCfg->SetPath( wxT( "/pcbnew" ) );
528
529 fromLegacyString( aCfg, "PageLayoutDescrFile", "pcbnew.page_layout_descr_file" );
530 fromLegacyString( aCfg, "LastNetListRead", "pcbnew.last_paths.netlist" );
531 fromLegacyString( aCfg, "LastSTEPExportPath", "pcbnew.last_paths.step" );
532 fromLegacyString( aCfg, "LastIDFExportPath", "pcbnew.last_paths.idf" );
533 fromLegacyString( aCfg, "LastVRMLExportPath", "pcbnew.last_paths.vrml" );
534 fromLegacyString( aCfg, "LastSpecctraDSNExportPath", "pcbnew.last_paths.specctra_dsn" );
535 fromLegacyString( aCfg, "LastGenCADExportPath", "pcbnew.last_paths.gencad" );
536
537 std::string bp = "board.design_settings.";
538
539 {
540 int idx = 1;
541 wxString key = wxT( "DRCExclusion" );
542 key << idx;
543
544 nlohmann::json exclusions = nlohmann::json::array();
545
546 while( aCfg->Read( key, &str ) )
547 {
548 exclusions.push_back( str );
549
550 key = wxT( "DRCExclusion" );
551 key << ++idx;
552 }
553
554 Set( bp + "drc_exclusions", exclusions );
555 }
556
557 fromLegacy<bool>( aCfg, "AllowMicroVias", bp + "rules.allow_microvias" );
558 fromLegacy<bool>( aCfg, "AllowBlindVias", bp + "rules.allow_blind_buried_vias" );
559 fromLegacy<double>( aCfg, "MinClearance", bp + "rules.min_clearance" );
560 fromLegacy<double>( aCfg, "MinTrackWidth", bp + "rules.min_track_width" );
561 fromLegacy<double>( aCfg, "MinViaAnnulus", bp + "rules.min_via_annulus" );
562 fromLegacy<double>( aCfg, "MinViaDiameter", bp + "rules.min_via_diameter" );
563
564 if( !fromLegacy<double>( aCfg, "MinThroughDrill", bp + "rules.min_through_hole_diameter" ) )
565 fromLegacy<double>( aCfg, "MinViaDrill", bp + "rules.min_through_hole_diameter" );
566
567 fromLegacy<double>( aCfg, "MinMicroViaDiameter", bp + "rules.min_microvia_diameter" );
568 fromLegacy<double>( aCfg, "MinMicroViaDrill", bp + "rules.min_microvia_drill" );
569 fromLegacy<double>( aCfg, "MinHoleToHole", bp + "rules.min_hole_to_hole" );
570 fromLegacy<double>( aCfg, "CopperEdgeClearance", bp + "rules.min_copper_edge_clearance" );
571 fromLegacy<double>( aCfg, "SolderMaskClearance", bp + "rules.solder_mask_clearance" );
572 fromLegacy<double>( aCfg, "SolderMaskMinWidth", bp + "rules.solder_mask_min_width" );
573 fromLegacy<double>( aCfg, "SolderPasteClearance", bp + "rules.solder_paste_clearance" );
574 fromLegacy<double>( aCfg, "SolderPasteRatio", bp + "rules.solder_paste_margin_ratio" );
575
576 if( !fromLegacy<double>( aCfg, "SilkLineWidth", bp + "defaults.silk_line_width" ) )
577 fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.silk_line_width" );
578
579 if( !fromLegacy<double>( aCfg, "SilkTextSizeV", bp + "defaults.silk_text_size_v" ) )
580 fromLegacy<double>( aCfg, "ModuleTextSizeV", bp + "defaults.silk_text_size_v" );
581
582 if( !fromLegacy<double>( aCfg, "SilkTextSizeH", bp + "defaults.silk_text_size_h" ) )
583 fromLegacy<double>( aCfg, "ModuleTextSizeH", bp + "defaults.silk_text_size_h" );
584
585 if( !fromLegacy<double>( aCfg, "SilkTextSizeThickness", bp + "defaults.silk_text_thickness" ) )
586 fromLegacy<double>( aCfg, "ModuleTextSizeThickness", bp + "defaults.silk_text_thickness" );
587
588 fromLegacy<bool>( aCfg, "SilkTextItalic", bp + "defaults.silk_text_italic" );
589 fromLegacy<bool>( aCfg, "SilkTextUpright", bp + "defaults.silk_text_upright" );
590
591 if( !fromLegacy<double>( aCfg, "CopperLineWidth", bp + "defaults.copper_line_width" ) )
592 fromLegacy<double>( aCfg, "DrawSegmentWidth", bp + "defaults.copper_line_width" );
593
594 if( !fromLegacy<double>( aCfg, "CopperTextSizeV", bp + "defaults.copper_text_size_v" ) )
595 fromLegacy<double>( aCfg, "PcbTextSizeV", bp + "defaults.copper_text_size_v" );
596
597 if( !fromLegacy<double>( aCfg, "CopperTextSizeH", bp + "defaults.copper_text_size_h" ) )
598 fromLegacy<double>( aCfg, "PcbTextSizeH", bp + "defaults.copper_text_size_h" );
599
600 if( !fromLegacy<double>( aCfg, "CopperTextThickness", bp + "defaults.copper_text_thickness" ) )
601 fromLegacy<double>( aCfg, "PcbTextThickness", bp + "defaults.copper_text_thickness" );
602
603 fromLegacy<bool>( aCfg, "CopperTextItalic", bp + "defaults.copper_text_italic" );
604 fromLegacy<bool>( aCfg, "CopperTextUpright", bp + "defaults.copper_text_upright" );
605
606 if( !fromLegacy<double>( aCfg, "EdgeCutLineWidth", bp + "defaults.board_outline_line_width" ) )
607 fromLegacy<double>( aCfg, "BoardOutlineThickness",
608 bp + "defaults.board_outline_line_width" );
609
610 fromLegacy<double>( aCfg, "CourtyardLineWidth", bp + "defaults.courtyard_line_width" );
611
612 fromLegacy<double>( aCfg, "FabLineWidth", bp + "defaults.fab_line_width" );
613 fromLegacy<double>( aCfg, "FabTextSizeV", bp + "defaults.fab_text_size_v" );
614 fromLegacy<double>( aCfg, "FabTextSizeH", bp + "defaults.fab_text_size_h" );
615 fromLegacy<double>( aCfg, "FabTextSizeThickness", bp + "defaults.fab_text_thickness" );
616 fromLegacy<bool>( aCfg, "FabTextItalic", bp + "defaults.fab_text_italic" );
617 fromLegacy<bool>( aCfg, "FabTextUpright", bp + "defaults.fab_text_upright" );
618
619 if( !fromLegacy<double>( aCfg, "OthersLineWidth", bp + "defaults.other_line_width" ) )
620 fromLegacy<double>( aCfg, "ModuleOutlineThickness", bp + "defaults.other_line_width" );
621
622 fromLegacy<double>( aCfg, "OthersTextSizeV", bp + "defaults.other_text_size_v" );
623 fromLegacy<double>( aCfg, "OthersTextSizeH", bp + "defaults.other_text_size_h" );
624 fromLegacy<double>( aCfg, "OthersTextSizeThickness", bp + "defaults.other_text_thickness" );
625 fromLegacy<bool>( aCfg, "OthersTextItalic", bp + "defaults.other_text_italic" );
626 fromLegacy<bool>( aCfg, "OthersTextUpright", bp + "defaults.other_text_upright" );
627
628 fromLegacy<int>( aCfg, "DimensionUnits", bp + "defaults.dimension_units" );
629 fromLegacy<int>( aCfg, "DimensionPrecision", bp + "defaults.dimension_precision" );
630
631 std::string sev = bp + "rule_severities";
632
633 fromLegacy<bool>( aCfg, "RequireCourtyardDefinitions", sev + "legacy_no_courtyard_defined" );
634
635 fromLegacy<bool>( aCfg, "ProhibitOverlappingCourtyards", sev + "legacy_courtyards_overlap" );
636
637 {
638 int idx = 1;
639 wxString keyBase = "TrackWidth";
640 wxString key = keyBase;
641 double val;
642
643 nlohmann::json widths = nlohmann::json::array();
644
645 key << idx;
646
647 while( aCfg->Read( key, &val ) )
648 {
649 widths.push_back( val );
650 key = keyBase;
651 key << ++idx;
652 }
653
654 Set( bp + "track_widths", widths );
655 }
656
657 {
658 int idx = 1;
659 wxString keyBase = "ViaDiameter";
660 wxString key = keyBase;
661 double diameter;
662 double drill;
663
664 nlohmann::json vias = nlohmann::json::array();
665
666 key << idx;
667
668 while( aCfg->Read( key, &diameter ) )
669 {
670 key = "ViaDrill";
671 drill = 1.0;
672 aCfg->Read( key << idx, &drill );
673
674 nlohmann::json via = { { "diameter", diameter }, { "drill", drill } };
675 vias.push_back( via );
676
677 key = keyBase;
678 key << ++idx;
679 }
680
681 Set( bp + "via_dimensions", vias );
682 }
683
684 {
685 int idx = 1;
686 wxString keyBase = "dPairWidth";
687 wxString key = keyBase;
688 double width;
689 double gap;
690 double via_gap;
691
692 nlohmann::json pairs = nlohmann::json::array();
693
694 key << idx;
695
696 while( aCfg->Read( key, &width ) )
697 {
698 key = "dPairGap";
699 gap = 1.0;
700 aCfg->Read( key << idx, &gap );
701
702 key = "dPairViaGap";
703 via_gap = 1.0;
704 aCfg->Read( key << idx, &via_gap );
705
706 nlohmann::json pair = { { "width", width }, { "gap", gap }, { "via_gap", via_gap } };
707 pairs.push_back( pair );
708
709 key = keyBase;
710 key << ++idx;
711 }
712
713 Set( bp + "diff_pair_dimensions", pairs );
714 }
715
716 group_blacklist.insert( wxT( "/pcbnew" ) );
717
718 // General group is unused these days, we can throw it away
719 group_blacklist.insert( wxT( "/general" ) );
720
721 // Next load sheet names and put all other legacy data in the legacy dict
722 aCfg->SetPath( wxT( "/" ) );
723
724 auto loadSheetNames =
725 [&]() -> bool
726 {
727 int sheet = 1;
728 wxString entry;
729 nlohmann::json arr = nlohmann::json::array();
730
731 wxLogTrace( traceSettings, wxT( "Migrating sheet names" ) );
732
733 aCfg->SetPath( wxT( "/sheetnames" ) );
734
735 while( aCfg->Read( wxString::Format( "%d", sheet++ ), &entry ) )
736 {
737 wxArrayString tokens = wxSplit( entry, ':' );
738
739 if( tokens.size() == 2 )
740 {
741 wxLogTrace( traceSettings, wxT( "%d: %s = %s" ), sheet, tokens[0], tokens[1] );
742 arr.push_back( nlohmann::json::array( { tokens[0], tokens[1] } ) );
743 }
744 }
745
746 Set( "sheets", arr );
747
748 aCfg->SetPath( "/" );
749
750 return true;
751 };
752
753 std::vector<wxString> groups;
754
755 groups.emplace_back( wxEmptyString );
756
757 auto loadLegacyPairs =
758 [&]( const std::string& aGroup ) -> bool
759 {
760 wxLogTrace( traceSettings, wxT( "Migrating group %s" ), aGroup );
761 bool success = true;
762 wxString keyStr;
763 wxString val;
764
765 index = 0;
766
767 while( aCfg->GetNextEntry( keyStr, index ) )
768 {
769 if( !aCfg->Read( keyStr, &val ) )
770 continue;
771
772 std::string key( keyStr.ToUTF8() );
773
774 wxLogTrace( traceSettings, wxT( " %s = %s" ), key, val );
775
776 try
777 {
778 Set( "legacy." + aGroup + "." + key, val );
779 }
780 catch( ... )
781 {
782 success = false;
783 }
784 }
785
786 return success;
787 };
788
789 for( size_t i = 0; i < groups.size(); i++ )
790 {
791 aCfg->SetPath( groups[i] );
792
793 if( groups[i] == wxT( "/sheetnames" ) )
794 {
795 ret |= loadSheetNames();
796 continue;
797 }
798
799 aCfg->DeleteEntry( wxT( "last_client" ), true );
800 aCfg->DeleteEntry( wxT( "update" ), true );
801 aCfg->DeleteEntry( wxT( "version" ), true );
802
803 ret &= loadLegacyPairs( groups[i].ToStdString() );
804
805 index = 0;
806
807 while( aCfg->GetNextGroup( str, index ) )
808 {
809 wxString group = groups[i] + "/" + str;
810
811 if( !group_blacklist.count( group ) )
812 groups.emplace_back( group );
813 }
814
815 aCfg->SetPath( "/" );
816 }
817
818 return ret;
819}
820
821
822bool PROJECT_FILE::LoadFromFile( const wxString& aDirectory )
823{
824 bool success = JSON_SETTINGS::LoadFromFile( aDirectory );
825
826 if( success )
827 {
828 // Migrate from old single-root format to top_level_sheets format
829 if( m_topLevelSheets.empty() && m_project )
830 {
831 // Create a default top-level sheet entry based on the project name
832 wxString projectName = m_project->GetProjectName();
833
834 TOP_LEVEL_SHEET_INFO defaultSheet;
835 defaultSheet.uuid = niluuid; // Use niluuid for the first/default sheet
836 defaultSheet.name = projectName;
837 defaultSheet.filename = projectName + ".kicad_sch";
838
839 m_topLevelSheets.push_back( std::move( defaultSheet ) );
840
841 // Mark as migrated so it will be saved with the new format
842 m_wasMigrated = true;
843
844 wxLogTrace( traceSettings, wxT( "PROJECT_FILE: Migrated old single-root format to top_level_sheets" ) );
845 }
846
847 // When a project is created from a template, the top_level_sheets entries may
848 // still reference the template's schematic filenames rather than the new project's.
849 // The template copy renames files on disk but doesn't update the .kicad_pro content.
850 // Detect this and fix the references so the schematic can be found.
851 if( !m_topLevelSheets.empty() && m_project )
852 {
853 wxString projectPath = m_project->GetProjectPath();
854 wxString projectName = m_project->GetProjectName();
855
856 for( TOP_LEVEL_SHEET_INFO& sheetInfo : m_topLevelSheets )
857 {
858 wxFileName referencedFile( projectPath, sheetInfo.filename );
859
860 if( referencedFile.FileExists() )
861 continue;
862
863 // Try the project-name-based filename
864 wxString expectedFile = projectName + wxS( "." ) + FILEEXT::KiCadSchematicFileExtension;
865
866 wxFileName candidateFile( projectPath, expectedFile );
867
868 if( candidateFile.FileExists() )
869 {
870 wxLogTrace( traceSettings,
871 wxT( "PROJECT_FILE: Fixing stale top_level_sheets reference '%s' -> '%s'" ),
872 sheetInfo.filename, expectedFile );
873
874 sheetInfo.filename = expectedFile;
875 sheetInfo.name = projectName;
876 m_wasMigrated = true;
877 }
878 }
879 }
880 }
881
882 return success;
883}
884
885
887{
888 // An absent alias table permits legacy imports; an explicitly empty table suppresses them
889 if( m_BusAliasesDefined && !GetJson( "schematic.bus_aliases" ) )
890 m_modified = true;
891
892 return JSON_SETTINGS::Store();
893}
894
895
896bool PROJECT_FILE::SaveToFile( const wxString& aDirectory, bool aForce )
897{
898 wxASSERT( m_project );
899
900 Set( "meta.filename", m_project->GetProjectName() + "." + FILEEXT::ProjectFileExtension );
901
902 // Even if parameters were not modified, we should resave after migration
903 bool force = aForce || m_wasMigrated;
904
905 // If we're actually going ahead and doing the save, the flag that keeps code from doing the
906 // save should be cleared at this.
907 m_wasMigrated = false;
908
909 return JSON_SETTINGS::SaveToFile( aDirectory, force );
910}
911
912
914{
915 wxASSERT( m_project );
916
917 Set( "meta.filename", m_project->GetProjectName() + "." + FILEEXT::ProjectFileExtension );
918
920
921 try
922 {
923 return formatFileContents();
924 }
925 catch( ... )
926 {
927 wxLogTrace( traceSettings, wxT( "Error: could not serialize %s" ), GetFullFilename() );
928 return std::string();
929 }
930}
931
932
933bool PROJECT_FILE::SaveAs( const wxString& aDirectory, const wxString& aFile )
934{
935 wxFileName oldFilename( GetFilename() );
936 wxString oldProjectName = oldFilename.GetName();
937 wxString oldProjectPath = oldFilename.GetPath();
938
939 Set( "meta.filename", aFile + "." + FILEEXT::ProjectFileExtension );
940 SetFilename( aFile );
941
942 auto updatePath =
943 [&]( wxString& aPath )
944 {
945 if( aPath.StartsWith( oldProjectName + wxS( "." ) ) )
946 aPath.Replace( oldProjectName, aFile, false );
947 else if( aPath.StartsWith( oldProjectPath + wxS( "/" ) ) )
948 aPath.Replace( oldProjectPath, aDirectory, false );
949 };
950
951 updatePath( m_BoardDrawingSheetFile );
952
953 for( int ii = LAST_PATH_FIRST; ii < (int) LAST_PATH_SIZE; ++ii )
954 updatePath( m_PcbLastPath[ ii ] );
955
956 auto updatePathByPtr =
957 [&]( const std::string& aPtr )
958 {
959 if( std::optional<wxString> path = Get<wxString>( aPtr ) )
960 {
961 updatePath( path.value() );
962 Set( aPtr, path.value() );
963 }
964 };
965
966 updatePathByPtr( "schematic.page_layout_descr_file" );
967 updatePathByPtr( "schematic.plot_directory" );
968 updatePathByPtr( "schematic.ngspice.workbook_filename" );
969 updatePathByPtr( "pcbnew.page_layout_descr_file" );
970
971 for( auto& sheetInfo : m_topLevelSheets )
972 {
973 updatePath( sheetInfo.filename );
974
975 // Also update the display name if it matches the old project name
976 if( sheetInfo.name == oldProjectName )
977 sheetInfo.name = aFile;
978 }
979
980 // If we're actually going ahead and doing the save, the flag that keeps code from doing the save
981 // should be cleared at this point
982 m_wasMigrated = false;
983
984 // While performing Save As, we have already checked that we can write to the directory
985 // so don't carry the previous flag
986 SetReadOnly( false );
987 return JSON_SETTINGS::SaveToFile( aDirectory, true );
988}
989
990
992{
994}
995
996
1001
1002
1003void to_json( nlohmann::json& aJson, const FILE_INFO_PAIR& aPair )
1004{
1005 aJson = nlohmann::json::array( { aPair.first.AsString().ToUTF8(), aPair.second.ToUTF8() } );
1006}
1007
1008
1009void from_json( const nlohmann::json& aJson, FILE_INFO_PAIR& aPair )
1010{
1011 wxCHECK( aJson.is_array() && aJson.size() == 2, /* void */ );
1012 aPair.first = KIID( wxString( aJson[0].get<std::string>().c_str(), wxConvUTF8 ) );
1013 aPair.second = wxString( aJson[1].get<std::string>().c_str(), wxConvUTF8 );
1014}
1015
1016
1017void to_json( nlohmann::json& aJson, const TOP_LEVEL_SHEET_INFO& aInfo )
1018{
1019 aJson = nlohmann::json::object();
1020 aJson["uuid"] = aInfo.uuid.AsString().ToUTF8();
1021 aJson["name"] = aInfo.name.ToUTF8();
1022 aJson["filename"] = aInfo.filename.ToUTF8();
1023}
1024
1025
1026void from_json( const nlohmann::json& aJson, TOP_LEVEL_SHEET_INFO& aInfo )
1027{
1028 wxCHECK( aJson.is_object(), /* void */ );
1029
1030 if( aJson.contains( "uuid" ) )
1031 aInfo.uuid = KIID( wxString( aJson["uuid"].get<std::string>().c_str(), wxConvUTF8 ) );
1032
1033 if( aJson.contains( "name" ) )
1034 aInfo.name = wxString( aJson["name"].get<std::string>().c_str(), wxConvUTF8 );
1035
1036 if( aJson.contains( "filename" ) )
1037 aInfo.filename = wxString( aJson["filename"].get<std::string>().c_str(), wxConvUTF8 );
1038}
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.
bool flushToStore()
Flushes nested settings and parameters into the JSON store.
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...
std::optional< nlohmann::json > GetJson(const std::string &aPath) const
Fetches a JSON object that is a subset of this JSON_SETTINGS object, using a path of the form "key1....
bool m_modified
True if the JSON data store has been written to since the last file write.
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
void SetReadOnly(bool aReadOnly)
wxString GetFullFilename() const
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.
std::string formatFileContents()
Overlays the current store on the original file contents.
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
wxString GetFilename() const
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
Stores an enum as an integer.
Definition parameters.h:232
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
bool m_BusAliasesDefined
An explicit alias table was loaded or set, including a deliberately empty table.
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()
Schema version 2: Bump for KiCad 9 layer numbering changes.
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 Store() override
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
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()
bool migrateSchema3To4()
Schema version 4 distinguishes explicit empty alias tables from older default writes.
std::string SerializeToString()
Serialize to the bytes SaveToFile would write, without touching the file.
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.
IDF_EXPORT_SETTINGS m_IdfExportSettings
IPC-2581 BOM settings.
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.