KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eeschema_jobs_handler.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) 2022 Mark Roszko <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
22#include <common.h>
23#include <pgm_base.h>
24#include <kiface_base.h>
25#include <kiway_player.h>
26#include <cli/exit_codes.h>
27#include <sch_plotter.h>
30#include <jobs/job_export_bom.h>
34#include <jobs/job_sch_erc.h>
35#include <jobs/job_sch_import.h>
40#include <schematic.h>
41#include <import_net_map.h>
42#include <schematic_settings.h>
43#include <sch_screen.h>
44#include <sch_sheet.h>
45#include <sch_sheet_path.h>
46#include <sch_commit.h>
48#include <save_project_utils.h>
49#include <tool/tool_manager.h>
50#include <project.h>
52#include <wx/dir.h>
53#include <wx/file.h>
54#include <memory>
55#include <connection_graph.h>
56#include "eeschema_helpers.h"
57#include <filename_resolver.h>
58#include <kiway.h>
59#include <sch_painter.h>
60#include <locale_io.h>
61#include <erc/erc.h>
62#include <erc/erc_report.h>
66#include <paths.h>
67#include <reporter.h>
68#include <scoped_set_reset.h>
69#include <string_utils.h>
70
72
73#include <sch_file_versions.h>
74#include <sch_io/sch_io.h>
76
77#include <netlist.h>
87
89
94#include <confirm.h>
95#include <project_sch.h>
96
98
99
101 JOB_DISPATCHER( aKiway ),
102 m_cliSchematic( nullptr )
103{
104 Register( "bom", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportBom, this, std::placeholders::_1 ),
105 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
106 {
107 JOB_EXPORT_BOM* bomJob = dynamic_cast<JOB_EXPORT_BOM*>( job );
108
109 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
110
111 wxCHECK( bomJob && editFrame, false );
112
113 DIALOG_SYMBOL_FIELDS_TABLE dlg( editFrame, bomJob );
114
115 if( dlg.WasAborted() )
116 return false;
117
118 return dlg.ShowModal() == wxID_OK;
119 } );
120 Register( "pythonbom", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportPythonBom, this, std::placeholders::_1 ),
121 []( JOB* job, wxWindow* aParent ) -> bool
122 {
123 return true;
124 } );
125 Register( "netlist", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportNetlist, this, std::placeholders::_1 ),
126 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
127 {
128 JOB_EXPORT_SCH_NETLIST* netJob = dynamic_cast<JOB_EXPORT_SCH_NETLIST*>( job );
129
130 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
131
132 wxCHECK( netJob && editFrame, false );
133
134 DIALOG_EXPORT_NETLIST dlg( editFrame, aParent, netJob );
135 return dlg.ShowModal() == wxID_OK;
136 } );
137 Register( "plot", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportPlot, this, std::placeholders::_1 ),
138 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
139 {
140 JOB_EXPORT_SCH_PLOT* plotJob = dynamic_cast<JOB_EXPORT_SCH_PLOT*>( job );
141
142 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
143
144 wxCHECK( plotJob && editFrame, false );
145
146 if( plotJob->m_plotFormat == SCH_PLOT_FORMAT::HPGL )
147 {
148 DisplayErrorMessage( editFrame,
149 _( "Plotting to HPGL is no longer supported as of KiCad 10.0." ) );
150 return false;
151 }
152
153 DIALOG_PLOT_SCHEMATIC dlg( editFrame, aParent, plotJob );
154 return dlg.ShowModal() == wxID_OK;
155 } );
156 Register( "symupgrade", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymUpgrade, this, std::placeholders::_1 ),
157 []( JOB* job, wxWindow* aParent ) -> bool
158 {
159 return true;
160 } );
161 Register( "symsvg", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymExportSvg, this, std::placeholders::_1 ),
162 []( JOB* job, wxWindow* aParent ) -> bool
163 {
164 return true;
165 } );
166 Register( "sch_diff", std::bind( &EESCHEMA_JOBS_HANDLER::JobSchDiff, this, std::placeholders::_1 ),
167 []( JOB* job, wxWindow* aParent ) -> bool
168 {
169 return true;
170 } );
171 Register( "sym_diff", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymDiff, this, std::placeholders::_1 ),
172 []( JOB* job, wxWindow* aParent ) -> bool
173 {
174 return true;
175 } );
176 Register( "erc", std::bind( &EESCHEMA_JOBS_HANDLER::JobSchErc, this, std::placeholders::_1 ),
177 []( JOB* job, wxWindow* aParent ) -> bool
178 {
179 JOB_SCH_ERC* ercJob = dynamic_cast<JOB_SCH_ERC*>( job );
180
181 wxCHECK( ercJob, false );
182
183 DIALOG_ERC_JOB_CONFIG dlg( aParent, ercJob );
184 return dlg.ShowModal() == wxID_OK;
185 } );
186 Register( "upgrade", std::bind( &EESCHEMA_JOBS_HANDLER::JobUpgrade, this, std::placeholders::_1 ),
187 []( JOB* job, wxWindow* aParent ) -> bool
188 {
189 return true;
190 } );
191 Register( "sch_import", std::bind( &EESCHEMA_JOBS_HANDLER::JobImport, this, std::placeholders::_1 ),
192 []( JOB* job, wxWindow* aParent ) -> bool
193 {
194 return true;
195 } );
196}
197
198
200{
201 if( m_cliSchematic )
202 m_cliSchematic->SetProject( nullptr );
203
204 delete m_cliSchematic;
205 m_cliSchematic = nullptr;
207}
208
209
210SCHEMATIC* EESCHEMA_JOBS_HANDLER::getSchematic( const wxString& aPath, bool aRequireRoot )
211{
212 SCHEMATIC* sch = nullptr;
213
214 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpenNotDummy() )
215 {
217 wxString schPath = aPath;
218
219 if( schPath.IsEmpty() )
220 {
221 wxFileName path = project.GetProjectFullName();
223 path.MakeAbsolute();
224 schPath = path.GetFullPath();
225 }
226
227 if( m_cliSchematic && aRequireRoot && !m_cliSchematicRootValidated )
229
230 if( !m_cliSchematic )
231 {
233 schPath, true, false, &project, true, aRequireRoot ? m_reporter : nullptr );
234 m_cliSchematicRootValidated = aRequireRoot;
235 }
236
237 sch = m_cliSchematic;
238 }
239 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
240 {
241 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( m_kiway->Player( FRAME_SCH, false ) );
242
243 if( editFrame )
244 sch = &editFrame->Schematic();
245 }
246 else if( !aPath.IsEmpty() )
247 {
249 aPath, true, false, nullptr, true, aRequireRoot ? m_reporter : nullptr );
250 }
251
252 if( !sch )
253 m_reporter->Report( _( "Failed to load schematic\n" ), RPT_SEVERITY_ERROR );
254
255 return sch;
256}
257
258void EESCHEMA_JOBS_HANDLER::InitRenderSettings( SCH_RENDER_SETTINGS* aRenderSettings, const wxString& aTheme,
259 SCHEMATIC* aSch, const wxString& aDrawingSheetOverride )
260{
261 COLOR_SETTINGS* cs = ::GetColorSettings( aTheme );
262 aRenderSettings->LoadColors( cs );
263 aRenderSettings->m_ShowHiddenPins = false;
264 aRenderSettings->m_ShowHiddenFields = false;
265 aRenderSettings->m_ShowPinAltIcons = false;
266
267 aRenderSettings->SetDefaultPenWidth( aSch->Settings().m_DefaultLineWidth );
268 aRenderSettings->m_LabelSizeRatio = aSch->Settings().m_LabelSizeRatio;
269 aRenderSettings->m_TextOffsetRatio = aSch->Settings().m_TextOffsetRatio;
270 aRenderSettings->m_PinSymbolSize = aSch->Settings().m_PinSymbolSize;
271 aRenderSettings->m_ShowDNPMarkers = aSch->Settings().m_ShowDNPMarkers;
272
273 aRenderSettings->SetDashLengthRatio( aSch->Settings().m_DashedLineDashRatio );
274 aRenderSettings->SetGapLengthRatio( aSch->Settings().m_DashedLineGapRatio );
275
276 // Load the drawing sheet from the filename stored in BASE_SCREEN::m_DrawingSheetFileName.
277 // If empty, or not existing, the default drawing sheet is loaded.
278
279 auto loadSheet = [&]( const wxString& path ) -> bool
280 {
281 wxString msg;
282
283 if( !DS_DATA_MODEL::GetTheInstance().LoadFromName( path, wxGetCwd(), &aSch->Project(),
284 { aSch->GetEmbeddedFiles() }, &msg ) )
285 {
286 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ), path ) + wxS( "\n" ) + msg
287 + wxS( "\n" ),
289 return false;
290 }
291
292 return true;
293 };
294
295 // try to load the override first
296 if( !aDrawingSheetOverride.IsEmpty() && loadSheet( aDrawingSheetOverride ) )
297 return;
298
299 if( aSch->Settings().m_SchDrawingSheetFileName == wxS( "empty.kicad_wks" ) )
300 {
302 return;
303 }
304
305 // no override or failed override continues here
306 loadSheet( aSch->Settings().m_SchDrawingSheetFileName );
307}
308
309
311{
312 JOB_EXPORT_SCH_PLOT* aPlotJob = dynamic_cast<JOB_EXPORT_SCH_PLOT*>( aJob );
313
314 wxCHECK( aPlotJob, CLI::EXIT_CODES::ERR_UNKNOWN );
315
316 if( aPlotJob->m_plotFormat == SCH_PLOT_FORMAT::HPGL )
317 {
318 m_reporter->Report( _( "Plotting to HPGL is no longer supported as of KiCad 10.0.\n" ), RPT_SEVERITY_ERROR );
320 }
321
322 SCHEMATIC* sch = getSchematic( aPlotJob->m_filename );
323
324 if( !sch )
326
327 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
328 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
329
330 // Determine the variant to use. The dialog edit path writes m_variant (the scalar),
331 // while the CLI path populates m_variantNames directly. Prefer the scalar so a
332 // dialog-edited selection always wins over a stale list left over from CLI input.
333 wxString variantName;
334
335 if( !aPlotJob->m_variant.IsEmpty() )
336 variantName = aPlotJob->m_variant;
337 else if( !aPlotJob->m_variantNames.empty() )
338 variantName = aPlotJob->m_variantNames.front();
339
340 if( !variantName.IsEmpty() && variantName != wxS( "all" ) )
341 sch->SetCurrentVariant( variantName );
342
343 std::unique_ptr<SCH_RENDER_SETTINGS> renderSettings = std::make_unique<SCH_RENDER_SETTINGS>();
344 InitRenderSettings( renderSettings.get(), aPlotJob->m_theme, sch, aPlotJob->m_drawingSheet );
345
346 wxString font = aPlotJob->m_defaultFont;
347
348 if( font.IsEmpty() )
349 {
351 font = cfg ? cfg->m_Appearance.default_font : wxString( KICAD_FONT_NAME );
352 }
353
354 renderSettings->SetDefaultFont( font );
355 renderSettings->SetMinPenWidth( aPlotJob->m_minPenWidth );
356
357 // Clear cached bounding boxes for all text items so they're recomputed with the correct
358 // default font. This is necessary because text bounding boxes may have been cached during
359 // schematic loading before the render settings (and thus default font) were configured.
360 SCH_SCREENS screens( sch->Root() );
361
362 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
363 {
364 for( SCH_ITEM* item : screen->Items() )
365 item->ClearCaches();
366
367 for( const auto& [libItemName, libSymbol] : screen->GetLibSymbols() )
368 libSymbol->ClearCaches();
369 }
370
371 std::unique_ptr<SCH_PLOTTER> schPlotter = std::make_unique<SCH_PLOTTER>( sch );
372
374
375 switch( aPlotJob->m_plotFormat )
376 {
377 case SCH_PLOT_FORMAT::DXF: format = PLOT_FORMAT::DXF; break;
378 case SCH_PLOT_FORMAT::PDF: format = PLOT_FORMAT::PDF; break;
379 case SCH_PLOT_FORMAT::SVG: format = PLOT_FORMAT::SVG; break;
380 case SCH_PLOT_FORMAT::POST: format = PLOT_FORMAT::POST; break;
381 case SCH_PLOT_FORMAT::PNG: format = PLOT_FORMAT::PNG; break;
382 case SCH_PLOT_FORMAT::HPGL: /* no longer supported */ break;
383 }
384
385 int pageSizeSelect = PageFormatReq::PAGE_SIZE_AUTO;
386
387 switch( aPlotJob->m_pageSizeSelect )
388 {
389 case JOB_PAGE_SIZE::PAGE_SIZE_A: pageSizeSelect = PageFormatReq::PAGE_SIZE_A; break;
390 case JOB_PAGE_SIZE::PAGE_SIZE_A4: pageSizeSelect = PageFormatReq::PAGE_SIZE_A4; break;
392 }
393
394 if( !aPlotJob->GetOutputPathIsDirectory() && aPlotJob->GetConfiguredOutputPath().IsEmpty() )
395 {
396 wxFileName fn = sch->GetFileName();
397 fn.SetName( fn.GetName() );
398 fn.SetExt( GetDefaultPlotExtension( format ) );
399
400 aPlotJob->SetConfiguredOutputPath( fn.GetFullName() );
401 }
402
403 wxString outPath = aPlotJob->GetFullOutputPath( &sch->Project() );
404
405 if( !PATHS::EnsurePathExists( outPath, !aPlotJob->GetOutputPathIsDirectory() ) )
406 {
407 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
409 }
410
411 SCH_PLOT_OPTS plotOpts;
412 plotOpts.m_blackAndWhite = aPlotJob->m_blackAndWhite;
413 plotOpts.m_PDFPropertyPopups = aPlotJob->m_PDFPropertyPopups;
415 plotOpts.m_PDFMetadata = aPlotJob->m_PDFMetadata;
416
417 if( aPlotJob->GetOutputPathIsDirectory() )
418 {
419 plotOpts.m_outputDirectory = outPath;
420 plotOpts.m_outputFile = wxEmptyString;
421 }
422 else
423 {
424 plotOpts.m_outputDirectory = wxEmptyString;
425 plotOpts.m_outputFile = outPath;
426 }
427
428 plotOpts.m_pageSizeSelect = pageSizeSelect;
429 plotOpts.m_plotAll = aPlotJob->m_plotAll;
430 plotOpts.m_plotDrawingSheet = aPlotJob->m_plotDrawingSheet;
431 plotOpts.m_plotPages = aPlotJob->m_plotPages;
432 plotOpts.m_theme = aPlotJob->m_theme;
433 plotOpts.m_useBackgroundColor = aPlotJob->m_useBackgroundColor;
434 plotOpts.m_plotHopOver = aPlotJob->m_show_hop_over;
435
436 if( !variantName.IsEmpty() )
437 plotOpts.m_variant = variantName;
438
439 // Always export dxf in mm by kicad-cli (similar to Pcbnew)
441
442 if( aPlotJob->m_plotFormat == SCH_PLOT_FORMAT::PNG )
443 {
444 JOB_EXPORT_SCH_PLOT_PNG* pngJob = static_cast<JOB_EXPORT_SCH_PLOT_PNG*>( aPlotJob );
445 plotOpts.m_pngDPI = pngJob->m_dpi;
446 plotOpts.m_pngAntialias = pngJob->m_antialias;
447 }
448
449 schPlotter->Plot( format, plotOpts, renderSettings.get(), m_reporter );
450
451 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
453
454 for( const wxString& outputPath : schPlotter->GetOutputFilePaths() )
455 aJob->AddOutput( outputPath );
456
457 return CLI::EXIT_CODES::OK;
458}
459
460
462{
463 JOB_EXPORT_SCH_NETLIST* aNetJob = dynamic_cast<JOB_EXPORT_SCH_NETLIST*>( aJob );
464
465 wxCHECK( aNetJob, CLI::EXIT_CODES::ERR_UNKNOWN );
466
467 SCHEMATIC* sch = getSchematic( aNetJob->m_filename );
468
469 if( !sch )
471
472 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
473 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
474
475 // Apply variant if specified
476 if( !aNetJob->m_variantNames.empty() )
477 {
478 // For netlist export, we use the first variant name from the set
479 wxString variantName = *aNetJob->m_variantNames.begin();
480
481 if( variantName != wxS( "all" ) )
482 sch->SetCurrentVariant( variantName );
483 }
484
485 // Annotation warning check
486 SCH_REFERENCE_LIST referenceList;
487 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_ALL );
488
489 if( referenceList.GetCount() > 0 )
490 {
491 if( referenceList.CheckAnnotation(
492 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
493 {
494 // We're only interested in the end result -- either errors or not
495 } )
496 > 0 )
497 {
498 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the "
499 "schematic editor to fix them\n" ),
501 }
502 }
503
504 // Test duplicate sheet names:
505 ERC_TESTER erc( sch );
506
507 if( erc.TestDuplicateSheetNames( false ) > 0 )
508 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
509
510 std::unique_ptr<NETLIST_EXPORTER_BASE> helper;
511 unsigned netlistOption = 0;
512
513 wxString fileExt;
514
515 switch( aNetJob->format )
516 {
519 helper = std::make_unique<NETLIST_EXPORTER_KICAD>( sch );
520 break;
521
524 helper = std::make_unique<NETLIST_EXPORTER_ORCADPCB2>( sch );
525 break;
526
529 helper = std::make_unique<NETLIST_EXPORTER_CADSTAR>( sch );
530 break;
531
535 helper = std::make_unique<NETLIST_EXPORTER_SPICE>( sch );
536 break;
537
540 helper = std::make_unique<NETLIST_EXPORTER_SPICE_MODEL>( sch );
541 break;
542
544 fileExt = wxS( "xml" );
545 helper = std::make_unique<NETLIST_EXPORTER_XML>( sch );
546 break;
547
549 fileExt = wxS( "asc" );
550 helper = std::make_unique<NETLIST_EXPORTER_PADS>( sch );
551 break;
552
554 fileExt = wxS( "txt" );
555 helper = std::make_unique<NETLIST_EXPORTER_ALLEGRO>( sch );
556 break;
557
558 default:
559 m_reporter->Report( _( "Unknown netlist format.\n" ), RPT_SEVERITY_ERROR );
561 }
562
563 if( aNetJob->GetConfiguredOutputPath().IsEmpty() )
564 {
565 wxFileName fn = sch->GetFileName();
566 fn.SetName( fn.GetName() );
567 fn.SetExt( fileExt );
568
569 aNetJob->SetConfiguredOutputPath( fn.GetFullName() );
570 }
571
572 wxString outPath = aNetJob->GetFullOutputPath( &sch->Project() );
573
574 if( !PATHS::EnsurePathExists( outPath, true ) )
575 {
576 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
578 }
579
580 helper->SetKiway( m_kiway );
581
582 bool res = helper->WriteNetlist( outPath, netlistOption, *m_reporter );
583
584 if( !res )
586
587 aJob->AddOutput( outPath );
588
589 return CLI::EXIT_CODES::OK;
590}
591
592
594{
595 JOB_EXPORT_BOM* aBomJob = dynamic_cast<JOB_EXPORT_BOM*>( aJob );
596
597 wxCHECK( aBomJob, CLI::EXIT_CODES::ERR_UNKNOWN );
598
599 SCHEMATIC* sch = getSchematic( aBomJob->m_filename );
600
601 if( !sch )
603
604 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
605 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
606
607 wxString currentVariant = aBomJob->GetSelectedVariant();
608
609 if( !currentVariant.IsEmpty() && currentVariant != wxS( "all" ) )
610 sch->SetCurrentVariant( currentVariant );
611
612 // Annotation warning check
613 SCH_REFERENCE_LIST referenceList;
614 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_NON_POWER, false );
615
616 if( referenceList.GetCount() > 0 )
617 {
618 SCH_REFERENCE_LIST copy = referenceList;
619
620 // Check annotation splits references...
621 if( copy.CheckAnnotation(
622 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
623 {
624 // We're only interested in the end result -- either errors or not
625 } )
626 > 0 )
627 {
628 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the schematic "
629 "editor to fix them\n" ),
631 }
632 }
633
634 // Test duplicate sheet names:
635 ERC_TESTER erc( sch );
636
637 if( erc.TestDuplicateSheetNames( false ) > 0 )
638 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
639
640 // Build our data model
641 SYMBOL_FIELDS_EDITOR_GRID_DATA_MODEL dataModel( referenceList );
642 dataModel.SetCurrentVariant( currentVariant );
643
644 // Mandatory fields first
645 for( FIELD_T fieldId : MANDATORY_FIELDS )
646 dataModel.AddColumn( GetDefaultFieldName( fieldId, UNTRANSLATED ),
647 GetDefaultFieldName( fieldId, TRANSLATED ), false );
648
649 // Generated/virtual fields (e.g. ${QUANTITY}, ${ITEM_NUMBER}) present only in the fields table
652 false );
655 false );
656
657 // Attribute fields (boolean flags on symbols)
658 dataModel.AddColumn( wxS( "${DNP}" ), GetGeneratedFieldDisplayName( wxS( "${DNP}" ) ), false );
659 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOM}" ) ),
660 false );
661 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOARD}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOARD}" ) ),
662 false );
663 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_SIM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_SIM}" ) ),
664 false );
665
666 // User field names in symbols second
667 std::set<wxString> userFieldNames;
668
669 for( size_t i = 0; i < referenceList.GetCount(); ++i )
670 {
671 SCH_SYMBOL* symbol = referenceList[i].GetSymbol();
672
673 for( SCH_FIELD& field : symbol->GetFields() )
674 {
675 if( !field.IsMandatory() && !field.IsPrivate() )
676 userFieldNames.insert( field.GetName() );
677 }
678 }
679
680 for( const wxString& fieldName : userFieldNames )
681 dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true );
682
683 // Add any templateFieldNames which aren't already present in the userFieldNames
684 for( const TEMPLATE_FIELDNAME& templateFieldname :
686 {
687 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
688 {
689 dataModel.AddColumn( templateFieldname.m_Name, GetGeneratedFieldDisplayName( templateFieldname.m_Name ),
690 false );
691 }
692 }
693
694 BOM_PRESET preset;
695
696 // Load a preset if one is specified
697 if( !aBomJob->m_bomPresetName.IsEmpty() )
698 {
699 // Find the preset
700 const BOM_PRESET* schPreset = nullptr;
701
702 for( const BOM_PRESET& p : BOM_PRESET::BuiltInPresets() )
703 {
704 if( p.name == aBomJob->m_bomPresetName )
705 {
706 schPreset = &p;
707 break;
708 }
709 }
710
711 for( const BOM_PRESET& p : sch->Settings().m_BomPresets )
712 {
713 if( p.name == aBomJob->m_bomPresetName )
714 {
715 schPreset = &p;
716 break;
717 }
718 }
719
720 if( !schPreset )
721 {
722 m_reporter->Report(
723 wxString::Format( _( "BOM preset '%s' not found" ) + wxS( "\n" ), aBomJob->m_bomPresetName ),
725
727 }
728
729 preset = *schPreset;
730 }
731 else
732 {
733 // Normalize field names so that bare generated-field tokens (e.g. "QUANTITY") are
734 // accepted alongside the canonical "${QUANTITY}" form. Shell expansion of ${VAR}
735 // inside double quotes silently produces an empty string, so this also guards against
736 // that common CLI pitfall.
737 auto normalizeFieldName = [&dataModel]( const wxString& aName ) -> wxString
738 {
739 if( aName.IsEmpty() )
740 return wxEmptyString;
741
742 if( IsGeneratedField( aName ) )
743 return aName;
744
745 wxString wrapped = wxS( "${" ) + aName + wxS( "}" );
746
747 if( IsGeneratedField( wrapped ) && dataModel.GetFieldNameCol( wrapped ) != -1 )
748 return wrapped;
749
750 return aName;
751 };
752
753 size_t i = 0;
754
755 for( const wxString& rawFieldName : aBomJob->m_fieldsOrdered )
756 {
757 wxString fieldName = normalizeFieldName( rawFieldName );
758
759 if( fieldName.IsEmpty() )
760 {
761 i++;
762 continue;
763 }
764
765 // Handle wildcard. We allow the wildcard anywhere in the list, but it needs to respect
766 // fields that come before and after the wildcard.
767 if( fieldName == wxS( "*" ) )
768 {
769 for( const BOM_FIELD& modelField : dataModel.GetFieldsOrdered() )
770 {
771 struct BOM_FIELD field;
772
773 field.name = modelField.name;
774 field.show = true;
775 field.groupBy = false;
776 field.label = field.name;
777
778 bool fieldAlreadyPresent = false;
779
780 for( BOM_FIELD& presetField : preset.fieldsOrdered )
781 {
782 if( presetField.name == field.name )
783 {
784 fieldAlreadyPresent = true;
785 break;
786 }
787 }
788
789 bool fieldLaterInList = false;
790
791 for( const wxString& fieldInList : aBomJob->m_fieldsOrdered )
792 {
793 if( normalizeFieldName( fieldInList ) == field.name )
794 {
795 fieldLaterInList = true;
796 break;
797 }
798 }
799
800 if( !fieldAlreadyPresent && !fieldLaterInList )
801 preset.fieldsOrdered.emplace_back( field );
802 }
803
804 continue;
805 }
806
807 struct BOM_FIELD field;
808
809 field.name = fieldName;
810 field.show = !fieldName.StartsWith( wxT( "__" ), &field.name );
811
812 field.groupBy = alg::contains( aBomJob->m_fieldsGroupBy, field.name )
813 || alg::contains( aBomJob->m_fieldsGroupBy, rawFieldName );
814
815 if( ( aBomJob->m_fieldsLabels.size() > i ) && !aBomJob->m_fieldsLabels[i].IsEmpty() )
816 field.label = aBomJob->m_fieldsLabels[i];
817 else if( IsGeneratedField( field.name ) )
818 field.label = GetGeneratedFieldDisplayName( field.name );
819 else
820 field.label = field.name;
821
822 preset.fieldsOrdered.emplace_back( field );
823 i++;
824 }
825
826 preset.sortAsc = aBomJob->m_sortAsc;
827 preset.sortField = normalizeFieldName( aBomJob->m_sortField );
828 preset.filterString = aBomJob->m_filterString;
829 preset.filterScope = aBomJob->m_filterScope;
830 preset.groupSymbols = aBomJob->m_groupSymbols;
831 preset.excludeDNP = aBomJob->m_excludeDNP;
832 }
833
834 BOM_FMT_PRESET fmt;
835
836 // Load a format preset if one is specified
837 if( !aBomJob->m_bomFmtPresetName.IsEmpty() )
838 {
839 std::optional<BOM_FMT_PRESET> schFmtPreset;
840
842 {
843 if( p.name == aBomJob->m_bomFmtPresetName )
844 {
845 schFmtPreset = p;
846 break;
847 }
848 }
849
850 for( const BOM_FMT_PRESET& p : sch->Settings().m_BomFmtPresets )
851 {
852 if( p.name == aBomJob->m_bomFmtPresetName )
853 {
854 schFmtPreset = p;
855 break;
856 }
857 }
858
859 if( !schFmtPreset )
860 {
861 m_reporter->Report( wxString::Format( _( "BOM format preset '%s' not found" ) + wxS( "\n" ),
862 aBomJob->m_bomFmtPresetName ),
864
866 }
867
868 fmt = *schFmtPreset;
869 }
870 else
871 {
872 fmt.fieldDelimiter = aBomJob->m_fieldDelimiter;
873 fmt.stringDelimiter = aBomJob->m_stringDelimiter;
874 fmt.refDelimiter = aBomJob->m_refDelimiter;
876 fmt.keepTabs = aBomJob->m_keepTabs;
877 fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
879 }
880
881 if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
882 {
883 wxFileName fn = sch->GetFileName();
884 fn.SetName( fn.GetName() );
885 fn.SetExt( FILEEXT::CsvFileExtension );
886
887 aBomJob->SetConfiguredOutputPath( fn.GetFullName() );
888 }
889
890 wxString configuredPath = aBomJob->GetConfiguredOutputPath();
891 bool hasVariantPlaceholder = configuredPath.Contains( wxS( "${VARIANT}" ) );
892
893 // Determine which variants to process
894 std::vector<wxString> variantsToProcess;
895
896 if( aBomJob->m_variantNames.size() > 1 && hasVariantPlaceholder )
897 {
898 variantsToProcess = aBomJob->m_variantNames;
899 }
900 else
901 {
902 variantsToProcess.push_back( currentVariant );
903 }
904
905 for( const wxString& variantName : variantsToProcess )
906 {
907 std::vector<wxString> singleVariant = { variantName };
908 dataModel.SetVariantNames( singleVariant );
909 dataModel.SetCurrentVariant( variantName );
910 dataModel.UpdateReferences( dataModel.GetReferenceList() );
911 dataModel.ApplyBomPreset( preset );
912
913 wxString outPath;
914
915 if( hasVariantPlaceholder )
916 {
917 wxString variantPath = configuredPath;
918 variantPath.Replace( wxS( "${VARIANT}" ), variantName );
919 aBomJob->SetConfiguredOutputPath( variantPath );
920 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
921 aBomJob->SetConfiguredOutputPath( configuredPath );
922 }
923 else
924 {
925 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
926 }
927
928 if( !PATHS::EnsurePathExists( outPath, true ) )
929 {
930 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
932 }
933
934 wxFile f;
935
936 if( !f.Open( outPath, wxFile::write ) )
937 {
938 m_reporter->Report( wxString::Format( _( "Unable to open destination '%s'" ), outPath ),
940
942 }
943
944 bool res = f.Write( dataModel.Export( fmt ) );
945
946 if( !res )
948
949 aJob->AddOutput( outPath );
950
951 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
952 }
953
954 return CLI::EXIT_CODES::OK;
955}
956
957
959{
960 JOB_EXPORT_SCH_PYTHONBOM* aNetJob = dynamic_cast<JOB_EXPORT_SCH_PYTHONBOM*>( aJob );
961
962 wxCHECK( aNetJob, CLI::EXIT_CODES::ERR_UNKNOWN );
963
964 SCHEMATIC* sch = getSchematic( aNetJob->m_filename );
965
966 if( !sch )
968
969 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
970 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
971
972 // Annotation warning check
973 SCH_REFERENCE_LIST referenceList;
974 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_ALL );
975
976 if( referenceList.GetCount() > 0 )
977 {
978 if( referenceList.CheckAnnotation(
979 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
980 {
981 // We're only interested in the end result -- either errors or not
982 } )
983 > 0 )
984 {
985 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the "
986 "schematic editor to fix them\n" ),
988 }
989 }
990
991 // Test duplicate sheet names:
992 ERC_TESTER erc( sch );
993
994 if( erc.TestDuplicateSheetNames( false ) > 0 )
995 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
996
997 std::unique_ptr<NETLIST_EXPORTER_XML> xmlNetlist = std::make_unique<NETLIST_EXPORTER_XML>( sch );
998
999 if( aNetJob->GetConfiguredOutputPath().IsEmpty() )
1000 {
1001 wxFileName fn = sch->GetFileName();
1002 fn.SetName( fn.GetName() + "-bom" );
1003 fn.SetExt( FILEEXT::XmlFileExtension );
1004
1005 aNetJob->SetConfiguredOutputPath( fn.GetFullName() );
1006 }
1007
1008 wxString outPath = aNetJob->GetFullOutputPath( &sch->Project() );
1009
1010 if( !PATHS::EnsurePathExists( outPath, true ) )
1011 {
1012 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1014 }
1015
1016 bool res = xmlNetlist->WriteNetlist( outPath, GNL_OPT_BOM, *m_reporter );
1017
1018 if( !res )
1020
1021 aJob->AddOutput( outPath );
1022
1023 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
1024
1025 return CLI::EXIT_CODES::OK;
1026}
1027
1028
1030 LIB_SYMBOL* symbol )
1031{
1032 wxCHECK( symbol, CLI::EXIT_CODES::ERR_UNKNOWN );
1033
1034 std::shared_ptr<LIB_SYMBOL> parent;
1035 LIB_SYMBOL* symbolToPlot = symbol;
1036
1037 // if the symbol is an alias, then the draw items are stored in the root symbol
1038 if( symbol->IsDerived() )
1039 {
1040 parent = symbol->GetRootSymbol();
1041
1042 wxCHECK( parent, CLI::EXIT_CODES::ERR_UNKNOWN );
1043
1044 symbolToPlot = parent.get();
1045 }
1046
1047 // iterate from unit 1, unit 0 would be "all units" which we don't want
1048 for( int unit = 1; unit < symbol->GetUnitCount() + 1; unit++ )
1049 {
1050 for( int bodyStyle = 1; bodyStyle <= symbol->GetBodyStyleCount(); ++bodyStyle )
1051 {
1052 wxString filename;
1053 wxFileName fn;
1054
1055 fn.SetPath( aSvgJob->m_outputDirectory );
1056 fn.SetExt( FILEEXT::SVGFileExtension );
1057
1058 filename = symbol->GetName();
1059
1060 for( wxChar c : wxFileName::GetForbiddenChars( wxPATH_DOS ) )
1061 filename.Replace( c, ' ' );
1062
1063 // Even single units get a unit number in the filename. This simplifies the
1064 // handling of the files as they have a uniform pattern.
1065 // Also avoids aliasing 'sym', unit 2 and 'sym_unit2', unit 1 to the same file.
1066 filename += wxString::Format( "_unit%d", unit );
1067
1068 if( symbol->HasDeMorganBodyStyles() )
1069 {
1070 if( bodyStyle == 2 )
1071 filename += wxS( "_demorgan" );
1072 }
1073 else if( bodyStyle <= (int) symbol->GetBodyStyleNames().size() )
1074 {
1075 filename += wxS( "_" ) + symbol->GetBodyStyleNames()[bodyStyle - 1].Lower();
1076 }
1077
1078 fn.SetName( filename );
1079 m_reporter->Report( wxString::Format( _( "Plotting symbol '%s' unit %d to '%s'\n" ), symbol->GetName(),
1080 unit, fn.GetFullPath() ),
1082
1083 BOX2I symbolBB = GetSymbolPlotBBox( *symbol, unit, bodyStyle, aSvgJob->m_includeHiddenFields );
1084
1085 if( !PlotSymbolToSVG( *symbolToPlot, *symbol, unit, bodyStyle, symbolBB, *aRenderSettings,
1086 aSvgJob->m_blackAndWhite, fn.GetFullPath(), m_reporter ) )
1087 {
1089 }
1090 }
1091 }
1092
1093 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
1095
1096 return CLI::EXIT_CODES::OK;
1097}
1098
1099
1101{
1102 JOB_SYM_EXPORT_SVG* svgJob = dynamic_cast<JOB_SYM_EXPORT_SVG*>( aJob );
1103
1104 wxCHECK( svgJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1105
1106 wxFileName fn( svgJob->m_libraryPath );
1107 fn.MakeAbsolute();
1108
1109 // When the input is a single symbol file we restrict plotting to the symbols defined in
1110 // that file. Stays empty (no restriction) when the input is a whole library.
1111 wxString singleFileFilter;
1112
1113 auto schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetFullPath() );
1114
1115 try
1116 {
1117 schLibrary->Load();
1118 }
1119 catch( ... )
1120 {
1121 // A single file holding a derived symbol whose parent is in a sibling file cannot load
1122 // alone. Retry against the enclosing directory, then plot only this file's symbols.
1123 bool recovered = false;
1124
1125 if( !fn.IsDir() && wxDir::Exists( fn.GetPath() ) )
1126 {
1127 try
1128 {
1129 schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetPath() );
1130 schLibrary->Load();
1131 singleFileFilter = fn.GetFullPath();
1132 recovered = true;
1133 }
1134 catch( ... )
1135 {
1136 // Fall through to the generic load error below.
1137 }
1138 }
1139
1140 if( !recovered )
1141 {
1142 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1144 }
1145 }
1146
1147 if( m_progressReporter )
1148 m_progressReporter->KeepRefreshing();
1149
1150 LIB_SYMBOL* symbol = nullptr;
1151
1152 if( !svgJob->m_symbol.IsEmpty() )
1153 {
1154 // See if the selected symbol exists
1155 symbol = schLibrary->GetSymbol( svgJob->m_symbol );
1156
1157 if( !symbol )
1158 {
1159 m_reporter->Report( _( "There is no symbol selected to save." ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
1161 }
1162 }
1163
1164 if( !svgJob->m_outputDirectory.IsEmpty() && !wxDir::Exists( svgJob->m_outputDirectory ) )
1165 {
1166 if( !wxFileName::Mkdir( svgJob->m_outputDirectory ) )
1167 {
1168 m_reporter->Report( wxString::Format( _( "Unable to create output directory '%s'." ) + wxS( "\n" ),
1169 svgJob->m_outputDirectory ),
1172 }
1173 }
1174
1175 SCH_RENDER_SETTINGS renderSettings;
1177 renderSettings.LoadColors( cs );
1178 renderSettings.SetDefaultPenWidth( DEFAULT_LINE_WIDTH_MILS * schIUScale.IU_PER_MILS );
1179 renderSettings.m_ShowHiddenPins = svgJob->m_includeHiddenPins;
1180 renderSettings.m_ShowHiddenFields = svgJob->m_includeHiddenFields;
1181
1182 int exitCode = CLI::EXIT_CODES::OK;
1183
1184 if( symbol )
1185 {
1186 exitCode = doSymExportSvg( svgJob, &renderSettings, symbol );
1187 }
1188 else
1189 {
1190 // Just plot all the symbols we can
1191 const LIB_SYMBOL_MAP& libSymMap = schLibrary->GetSymbolMap();
1192 const std::map<wxString, wxString>& sourceFiles = schLibrary->GetSymbolSourceFiles();
1193 const wxFileName filterFile( singleFileFilter );
1194
1195 for( const auto& [name, libSymbol] : libSymMap )
1196 {
1197 // When a single file was requested, skip symbols that came from sibling files.
1198 if( !singleFileFilter.IsEmpty() )
1199 {
1200 auto srcIt = sourceFiles.find( name );
1201
1202 if( srcIt == sourceFiles.end() || !wxFileName( srcIt->second ).SameAs( filterFile ) )
1203 continue;
1204 }
1205
1206 if( m_progressReporter )
1207 {
1208 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), name ) );
1209 m_progressReporter->KeepRefreshing();
1210 }
1211
1212 exitCode = doSymExportSvg( svgJob, &renderSettings, libSymbol );
1213
1214 if( exitCode != CLI::EXIT_CODES::OK )
1215 break;
1216 }
1217 }
1218
1219 return exitCode;
1220}
1221
1222
1224{
1225 JOB_SYM_UPGRADE* upgradeJob = dynamic_cast<JOB_SYM_UPGRADE*>( aJob );
1226
1227 wxCHECK( upgradeJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1228
1229 wxFileName fn( upgradeJob->m_libraryPath );
1230 fn.MakeAbsolute();
1231
1232 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
1233
1234 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1235 {
1236 if( wxFile::Exists( upgradeJob->m_outputLibraryPath ) )
1237 {
1238 m_reporter->Report( _( "Output path must not conflict with existing path\n" ), RPT_SEVERITY_ERROR );
1239
1241 }
1242 }
1243 else if( fileType != SCH_IO_MGR::SCH_KICAD )
1244 {
1245 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
1247
1249 }
1250
1251 if( fileType == SCH_IO_MGR::SCH_KICAD )
1252 {
1253 SCH_IO_KICAD_SEXPR_LIB_CACHE schLibrary( fn.GetFullPath() );
1254
1255 try
1256 {
1257 schLibrary.Load();
1258 }
1259 catch( ... )
1260 {
1261 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1263 }
1264
1265 if( m_progressReporter )
1266 m_progressReporter->KeepRefreshing();
1267
1268 bool shouldSave =
1270
1271 if( shouldSave )
1272 {
1273 m_reporter->Report( _( "Saving symbol library in updated format\n" ), RPT_SEVERITY_ACTION );
1274
1275 try
1276 {
1277 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1278 schLibrary.SetFileName( upgradeJob->m_outputLibraryPath );
1279
1280 schLibrary.SetModified();
1281 schLibrary.Save();
1282 }
1283 catch( ... )
1284 {
1285 m_reporter->Report( ( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
1287 }
1288 }
1289 else
1290 {
1291 m_reporter->Report( _( "Symbol library was not updated\n" ), RPT_SEVERITY_ERROR );
1292 }
1293 }
1294 else
1295 {
1296 if( !SCH_IO_MGR::ConvertLibrary( nullptr, fn.GetAbsolutePath(), upgradeJob->m_outputLibraryPath, m_reporter ) )
1297 {
1298 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
1300 }
1301 }
1302
1303 return CLI::EXIT_CODES::OK;
1304}
1305
1306
1308{
1309 JOB_SCH_ERC* ercJob = dynamic_cast<JOB_SCH_ERC*>( aJob );
1310
1311 wxCHECK( ercJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1312
1313 SCHEMATIC* sch = getSchematic( ercJob->m_filename );
1314
1315 if( !sch )
1317
1318 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
1319 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
1320
1321 if( ercJob->GetConfiguredOutputPath().IsEmpty() )
1322 {
1323 wxFileName fn = sch->GetFileName();
1324 fn.SetName( fn.GetName() + wxS( "-erc" ) );
1325
1327 fn.SetExt( FILEEXT::JsonFileExtension );
1328 else
1329 fn.SetExt( FILEEXT::ReportFileExtension );
1330
1331 // Use a transient working path so an empty configured output filename isn't persisted
1332 // back into the jobset file. Mirrors the PCB DRC handler.
1333 ercJob->SetWorkingOutputPath( fn.GetFullName() );
1334 }
1335
1336 wxString outPath = ercJob->GetFullOutputPath( &sch->Project() );
1337
1338 if( !PATHS::EnsurePathExists( outPath, true ) )
1339 {
1340 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1342 }
1343
1344 EDA_UNITS units;
1345
1346 switch( ercJob->m_units )
1347 {
1348 case JOB_SCH_ERC::UNITS::INCH: units = EDA_UNITS::INCH; break;
1349 case JOB_SCH_ERC::UNITS::MILS: units = EDA_UNITS::MILS; break;
1350 case JOB_SCH_ERC::UNITS::MM: units = EDA_UNITS::MM; break;
1351 default: units = EDA_UNITS::MM; break;
1352 }
1353
1354 std::shared_ptr<SHEETLIST_ERC_ITEMS_PROVIDER> markersProvider =
1355 std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( sch );
1356
1357 // Running ERC requires libraries be loaded, so make sure they have been
1359 adapter->AsyncLoad();
1360 adapter->BlockUntilLoaded();
1361
1362 ERC_TESTER ercTester( sch );
1363
1364 std::unique_ptr<DS_PROXY_VIEW_ITEM> drawingSheet( getDrawingSheetProxyView( sch ) );
1365 SCH_EDIT_FRAME* editFrame = nullptr;
1366
1367 if( Pgm().IsGUI() )
1368 {
1369 editFrame = static_cast<SCH_EDIT_FRAME*>( m_kiway->Player( FRAME_SCH, false ) );
1370
1371 if( editFrame && &editFrame->Schematic() != sch )
1372 editFrame = nullptr;
1373 }
1374
1375 if( editFrame )
1376 editFrame->ClearErcMarkers();
1377
1378 ercTester.RunTests( drawingSheet.get(), editFrame, m_kiway->KiFACE( KIWAY::FACE_CVPCB ), &sch->Project(),
1380
1381 if( editFrame )
1382 editFrame->RefreshErcMarkers();
1383
1384 markersProvider->SetSeverities( ercJob->m_severity );
1385
1386 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ), markersProvider->GetCount() ),
1388
1389 ERC_REPORT reportWriter( sch, units, markersProvider );
1390
1391 bool wroteReport = false;
1392
1394 wroteReport = reportWriter.WriteJsonReport( outPath );
1395 else
1396 wroteReport = reportWriter.WriteTextReport( outPath );
1397
1398 if( !wroteReport )
1399 {
1400 m_reporter->Report( wxString::Format( _( "Unable to save ERC report to %s\n" ), outPath ), RPT_SEVERITY_ERROR );
1402 }
1403
1404 m_reporter->Report( wxString::Format( _( "Saved ERC Report to %s\n" ), outPath ), RPT_SEVERITY_ACTION );
1405
1406 if( ercJob->m_exitCodeViolations )
1407 {
1408 if( markersProvider->GetCount() > 0 )
1410 }
1411
1413}
1414
1415
1417{
1418 JOB_SCH_UPGRADE* aUpgradeJob = dynamic_cast<JOB_SCH_UPGRADE*>( aJob );
1419
1420 if( aUpgradeJob == nullptr )
1422
1423 SCHEMATIC* sch = getSchematic( aUpgradeJob->m_filename, false );
1424
1425 if( !sch )
1427
1428 bool shouldSave = aUpgradeJob->m_force;
1429
1431 shouldSave = true;
1432
1433 if( !shouldSave )
1434 {
1435 m_reporter->Report( _( "Schematic file was not updated\n" ), RPT_SEVERITY_ERROR );
1437 }
1438
1439 // needs an absolute path
1440 wxFileName schPath( aUpgradeJob->m_filename );
1441 schPath.MakeAbsolute();
1442 const wxString schFullPath = schPath.GetFullPath();
1443
1444 try
1445 {
1446 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1447 SCH_SHEET* loadedSheet = pi->LoadSchematicFile( schFullPath, sch );
1448 pi->SaveSchematicFile( schFullPath, loadedSheet, sch );
1449 }
1450 catch( const IO_ERROR& ioe )
1451 {
1452 wxString msg =
1453 wxString::Format( _( "Error saving schematic file '%s'.\n%s" ), schFullPath, ioe.What().GetData() );
1454 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1456 }
1457
1458 m_reporter->Report( _( "Successfully saved schematic file using the latest format\n" ), RPT_SEVERITY_INFO );
1459
1461}
1462
1463
1465{
1466 JOB_SCH_IMPORT* job = dynamic_cast<JOB_SCH_IMPORT*>( aJob );
1467
1468 if( !job )
1470
1471 job->m_netNameMap.clear();
1472
1473 if( !wxFile::Exists( job->m_inputFile ) )
1474 {
1475 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ), job->m_inputFile ),
1478 }
1479
1480 // AUTO restricts autodetect to non-KiCad plugins so a native file is not re-imported.
1481 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_UNKNOWN;
1482
1483 switch( job->m_format )
1484 {
1487 break;
1488 case JOB_SCH_IMPORT::FORMAT::ALTIUM: fileType = SCH_IO_MGR::SCH_ALTIUM; break;
1489 case JOB_SCH_IMPORT::FORMAT::EAGLE: fileType = SCH_IO_MGR::SCH_EAGLE; break;
1490 case JOB_SCH_IMPORT::FORMAT::CADSTAR: fileType = SCH_IO_MGR::SCH_CADSTAR_ARCHIVE; break;
1491 case JOB_SCH_IMPORT::FORMAT::EASYEDA: fileType = SCH_IO_MGR::SCH_EASYEDA; break;
1492 case JOB_SCH_IMPORT::FORMAT::EASYEDAPRO: fileType = SCH_IO_MGR::SCH_EASYEDAPRO; break;
1493 case JOB_SCH_IMPORT::FORMAT::LTSPICE: fileType = SCH_IO_MGR::SCH_LTSPICE; break;
1494 case JOB_SCH_IMPORT::FORMAT::PADS: fileType = SCH_IO_MGR::SCH_PADS; break;
1495 case JOB_SCH_IMPORT::FORMAT::DIPTRACE: fileType = SCH_IO_MGR::SCH_DIPTRACE; break;
1496 case JOB_SCH_IMPORT::FORMAT::PCAD: fileType = SCH_IO_MGR::SCH_PCAD; break;
1497 case JOB_SCH_IMPORT::FORMAT::ORCAD: fileType = SCH_IO_MGR::SCH_ORCAD; break;
1498 }
1499
1500 if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1501 {
1502 // Quiet sentinel: lets the top-level `import` command treat the file as not-a-schematic.
1503 m_reporter->Report( wxString::Format( _( "No schematic importer recognizes the file format "
1504 "of '%s'\n" ),
1505 job->m_inputFile ),
1508 }
1509
1510 wxString outputPath = job->GetConfiguredOutputPath();
1511
1512 if( outputPath.IsEmpty() )
1514
1515 wxFileName inputFn( job->m_inputFile );
1516 inputFn.MakeAbsolute();
1517
1518 wxFileName outputFn( outputPath );
1519 outputFn.MakeAbsolute();
1520
1521 // Foreign importers resolve their symbol library against the *active* project, so an import
1522 // with no project loaded needs a transient active one at the output location (never written to
1523 // disk; LoadProject returns false yet still registers it, hence the GetProject() check).
1525 PROJECT* projectPtr = nullptr;
1526 bool createdTransientProject = false;
1527
1528 if( mgr.IsProjectOpenNotDummy() )
1529 {
1530 projectPtr = &mgr.Prj();
1531 }
1532 else
1533 {
1534 wxFileName projectFn( outputFn );
1535 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1536
1537 mgr.LoadProject( projectFn.GetFullPath(), true );
1538 projectPtr = mgr.GetProject( projectFn.GetFullPath() );
1539 createdTransientProject = ( projectPtr != nullptr );
1540 }
1541
1542 if( !projectPtr )
1543 {
1544 m_reporter->Report( _( "Could not establish a project for the import\n" ),
1547 }
1548
1549 PROJECT& project = *projectPtr;
1550
1551 // Declared before the SCHEMATIC so reverse-destruction tears the schematic (which references
1552 // the project) down first; unloads the transient project on every exit path.
1553 struct TRANSIENT_PROJECT_GUARD
1554 {
1555 SETTINGS_MANAGER& m_mgr;
1556 PROJECT* m_project;
1557 bool m_active;
1558
1559 ~TRANSIENT_PROJECT_GUARD()
1560 {
1561 if( m_active )
1562 m_mgr.UnloadProject( m_project, false );
1563 }
1564 } transientProjectGuard{ mgr, projectPtr, createdTransientProject };
1565
1567
1568 std::unique_ptr<SCHEMATIC> schematic = std::make_unique<SCHEMATIC>( &project );
1569
1570 wxString formatName = SCH_IO_MGR::ShowType( fileType );
1571 SCH_SHEET* loadedSheet = nullptr;
1572
1573 // outlives the load so the retained symbol definitions can be reconciled below
1574 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
1575
1576 if( !pi )
1577 {
1578 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
1579 formatName ),
1582 }
1583
1584 try
1585 {
1586 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
1587 inputFn.GetFullPath(), formatName ),
1589
1590 loadedSheet = pi->LoadSchematicFile( inputFn.GetFullPath(), schematic.get() );
1591
1592 if( !loadedSheet )
1593 {
1594 m_reporter->Report( _( "Failed to load schematic\n" ), RPT_SEVERITY_ERROR );
1596 }
1597 }
1598 catch( const IO_ERROR& ioe )
1599 {
1600 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
1603 }
1604
1605 size_t symbolCount = 0;
1606 size_t sheetCount = 0;
1607
1608 try
1609 {
1610 // Some importers build the top-level sheet set themselves; only collapse to the returned
1611 // sheet otherwise (mirrors SCH_EDIT_FRAME::importFile()).
1612 std::vector<SCH_SHEET*> topLevelSheets = schematic->GetTopLevelSheets();
1613 bool loadedIsTopLevel = std::find( topLevelSheets.begin(), topLevelSheets.end(),
1614 loadedSheet ) != topLevelSheets.end();
1615 bool loadedIsVirtualRoot = loadedSheet == &schematic->Root()
1616 || loadedSheet->IsVirtualRootSheet();
1617
1618 if( !loadedIsTopLevel && !loadedIsVirtualRoot )
1619 schematic->SetTopLevelSheets( { loadedSheet } );
1620
1621 // Extract a project symbol library and re-link LIB_IDs, as importFile() does; without it
1622 // the saved schematic references nicknames no library table row resolves.
1623 ReconcileImportedSymbols( *pi, *schematic, project, inputFn.GetFullPath(), nullptr,
1624 *m_reporter );
1625
1626 // Recompute connectivity so instance data is valid before saving, as importFile() does.
1627 std::unique_ptr<TOOL_MANAGER> toolManager = std::make_unique<TOOL_MANAGER>();
1628 toolManager->SetEnvironment( schematic.get(), nullptr, nullptr, Kiface().KifaceSettings(),
1629 nullptr );
1630
1631 {
1632 SCH_COMMIT dummyCommit( toolManager.get() );
1633 schematic->RecalculateConnections( &dummyCommit, GLOBAL_CLEANUP, toolManager.get() );
1634 dummyCommit.Push( _( "Schematic Cleanup" ), SKIP_UNDO | SKIP_CONNECTIVITY | DELETE_REMOVED_ITEMS );
1635 }
1636
1637 schematic->SetSheetNumberAndCount();
1638
1639 // Imported schematics preserve their source page frame as schematic graphics.
1640 // Match the editor import path and suppress KiCad's default frame/title block.
1641 schematic->Settings().m_SchDrawingSheetFileName = wxS( "empty.kicad_wks" );
1643
1644 if( SCH_SHEET* topSheet = schematic->GetTopLevelSheet() )
1645 topSheet->SetFileName( outputFn.GetFullName() );
1646
1647 schematic->RootScreen()->SetFileName( outputFn.GetFullPath() );
1648
1649 SCH_SCREENS screens( schematic->Root() );
1650
1651 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
1652 filenameMap[schematic->RootScreen()] = outputFn.GetFullPath();
1653
1654 wxString errorMsg;
1655
1656 if( !PrepareSaveAsFiles( *schematic, screens, inputFn, outputFn, /*aSaveCopy*/ true,
1657 /*aCopySubsheets*/ true, /*aIncludeExternSheets*/ true,
1658 filenameMap, errorMsg ) )
1659 {
1660 m_reporter->Report( errorMsg + wxS( "\n" ), RPT_SEVERITY_ERROR );
1662 }
1663
1664 // PrepareSaveAsFiles seeds an entry (empty for sheets it does not relocate) for every
1665 // screen; empty paths are skipped.
1666 IO_RELEASER<SCH_IO> kicadPi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1667
1668 for( size_t i = 0; i < screens.GetCount(); i++ )
1669 {
1670 SCH_SCREEN* screen = screens.GetScreen( i );
1671 wxString path = filenameMap[screen];
1672
1673 if( path.IsEmpty() )
1674 continue;
1675
1676 wxFileName fn( path );
1678
1679 kicadPi->SaveSchematicFile( fn.GetFullPath(), screens.GetSheet( i ), schematic.get() );
1680 sheetCount++;
1681
1682 auto symbols = screen->Items().OfType( SCH_SYMBOL_T );
1683 symbolCount += std::distance( symbols.begin(), symbols.end() );
1684 }
1685 }
1686 catch( const IO_ERROR& ioe )
1687 {
1688 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1689 ioe.What() ),
1692 }
1693 catch( const std::exception& exc )
1694 {
1695 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1696 exc.what() ),
1699 }
1700
1701 // The board job renames its nets from this; nothing is written beside the schematic.
1702 if( const IMPORT_NET_MAP* map = schematic->GetImportNetMap() )
1704
1705 m_reporter->Report( wxString::Format( _( "Successfully saved imported schematic to '%s'\n" ),
1706 outputFn.GetFullPath() ),
1708
1709 // Linked by the top-level `import` command's subsequent SaveProject().
1710 if( Pgm().GetSettingsManager().IsProjectOpenNotDummy() )
1711 {
1712 std::vector<FILE_INFO_PAIR>& projectSheets = project.GetProjectFile().GetSheets();
1713 projectSheets.clear();
1714
1715 for( const SCH_SHEET_PATH& sheetPath : schematic->Hierarchy() )
1716 {
1717 SCH_SHEET* sheet = sheetPath.Last();
1718
1719 if( sheet && !sheet->IsVirtualRootSheet() )
1720 projectSheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1721 }
1722
1723 // Reload uses the top-level sheet list, not the UUID-to-name map.
1724 const std::vector<SCH_SHEET*>& topLevelSheets = schematic->GetTopLevelSheets();
1725
1726 if( !topLevelSheets.empty() )
1727 {
1728 std::vector<TOP_LEVEL_SHEET_INFO>& infos = project.GetProjectFile().GetTopLevelSheets();
1729 infos.clear();
1730
1731 wxString projectPath = project.GetProjectPath();
1732
1733 for( SCH_SHEET* sheet : topLevelSheets )
1734 {
1735 if( !sheet || !sheet->GetScreen() )
1736 continue;
1737
1738 wxFileName sheetFn( sheet->GetScreen()->GetFileName() );
1739
1740 if( sheetFn.IsAbsolute() )
1741 sheetFn.MakeRelativeTo( projectPath );
1742
1743 infos.emplace_back( sheet->m_Uuid, sheet->GetName(), sheetFn.GetFullPath() );
1744 }
1745 }
1746 }
1747
1748 // Extra top-level sheets need a project file to remain reachable.
1749 if( createdTransientProject && schematic->GetTopLevelSheets().size() > 1 )
1750 {
1751 m_reporter->Report( wxString::Format( _( "%zu sheets were written, but only '%s' is "
1752 "reachable: top-level sheets are recorded in a "
1753 "project file. Use 'kicad-cli import' to create "
1754 "one.\n" ),
1755 sheetCount, outputFn.GetFullName() ),
1757 }
1758
1760 {
1761 IMPORT_REPORT_DATA reportData;
1762
1763 reportData.m_sourceFile = inputFn.GetFullName();
1764 reportData.m_sourceFormat = formatName;
1765 reportData.m_outputFile = outputFn.GetFullName();
1766 reportData.m_statistics = {
1767 { wxS( "symbols" ), symbolCount },
1768 { wxS( "sheets" ), sheetCount }
1769 };
1770
1771 reportData.m_statistics.emplace_back( wxS( "renamed_board_nets" ), job->m_netNameMap.size() );
1772
1773 WriteImportReport( m_reporter, job->m_reportFormat, job->m_reportFile, reportData );
1774 }
1775
1777}
1778
1779
1781{
1782 DS_PROXY_VIEW_ITEM* drawingSheet =
1784 &aSch->RootScreen()->GetTitleBlock(), aSch->GetProperties() );
1785
1786 drawingSheet->SetPageNumber( TO_UTF8( aSch->RootScreen()->GetPageNumber() ) );
1787 drawingSheet->SetSheetCount( aSch->RootScreen()->GetPageCount() );
1788 drawingSheet->SetFileName( TO_UTF8( aSch->RootScreen()->GetFileName() ) );
1791 drawingSheet->SetIsFirstPage( aSch->RootScreen()->GetVirtualPageNumber() == 1 );
1792
1793 wxString currentVariant = aSch->GetCurrentVariant();
1794 wxString variantDesc = aSch->GetVariantDescription( currentVariant );
1795 drawingSheet->SetVariantName( TO_UTF8( currentVariant ) );
1796 drawingSheet->SetVariantDesc( TO_UTF8( variantDesc ) );
1797
1798 drawingSheet->SetSheetName( "" );
1799 drawingSheet->SetSheetPath( "" );
1800
1801 return drawingSheet;
1802}
1803
1804
1805// ============================================================================
1806// JobSchDiff: sch_diff implementation
1807// ============================================================================
1810#include <diff_merge/diff_scene.h>
1811#include <diff_merge/sch_differ.h>
1815#include <project/project_file.h>
1817#include <jobs/job_sch_diff.h>
1818#include <jobs/scratch_doc.h>
1819#include <wx/file.h>
1820
1821
1822// Load a schematic into a SCRATCH_DOC<SCHEMATIC> that keeps a dedicated scratch
1823// PROJECT attached for the document's lifetime. Without a per-document project,
1824// a second LoadProject(path, true) destroys the first project and the first
1825// schematic's m_project dangles. The destructor severs the link via
1826// SetProject( nullptr ). Shared by every SCH diff/merge job.
1828{
1830 aMgr, aPath,
1831 [aPath]( PROJECT* aProject )
1832 {
1833 return std::unique_ptr<SCHEMATIC>(
1835 /*aSetActive=*/false,
1836 /*aForceDefaultProject=*/false, aProject,
1837 /*aCalculateConnectivity=*/false ) );
1838 },
1839 []( SCHEMATIC* aSch )
1840 {
1841 aSch->SetProject( nullptr );
1842 } );
1843}
1844
1845
1847{
1848 JOB_SCH_DIFF* diffJob = dynamic_cast<JOB_SCH_DIFF*>( aJob );
1849
1850 if( !diffJob )
1852
1853 // Two schematics in the same SettingsManager need scratch PROJECTs;
1854 // otherwise the second LoadProject(path, true) destroys the first
1855 // project and the first schematic's m_project dangles, crashing on
1856 // any per-instance bbox / field / reference resolution (e.g. inside
1857 // SCH_DIFFER's makeDescriptor calling SCH_SYMBOL::GetRef).
1859
1862
1863 if( !a.doc )
1864 {
1865 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputA ), RPT_SEVERITY_ERROR );
1867 }
1868
1869 if( !b.doc )
1870 {
1871 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputB ), RPT_SEVERITY_ERROR );
1873 }
1874
1875 SCHEMATIC* schA = a.doc.get();
1876 SCHEMATIC* schB = b.doc.get();
1877
1878 KICAD_DIFF::SCH_DIFFER differ( schA, schB, diffJob->m_inputB );
1880
1881 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1882
1883 if( diffJob->m_exitCodeOnly )
1884 return diffExitCode;
1885
1886 // The schematic geometry (wires, junctions, symbol/sheet/label bbox
1887 // outlines) renders beneath the change rectangles for PNG/SVG, matching
1888 // the interactive dialog.
1890 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1892 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1893 { return KICAD_DIFF::ExtractSchematicGeometry( *schA, aColor ); };
1894 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1895 { return KICAD_DIFF::ExtractSchematicGeometry( *schB, aColor ); };
1896
1897 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
1898}
1899
1900
1901// ============================================================================
1902// JobSymDiff: sym_diff implementation
1903// ============================================================================
1905#include <jobs/job_sym_diff.h>
1906
1907
1908// Load one side of a symbol-library diff into its owner vector and name map.
1909// When aAllowEmpty is set an empty path resolves to a clean (empty) side; the
1910// non-interactive job path leaves it unset so a missing path is an input error.
1911static int loadSymbolLibrarySide( const wxString& aPath,
1912 std::vector<std::unique_ptr<LIB_SYMBOL>>& aOwners,
1913 KICAD_DIFF::SYM_LIB_DIFFER::SYMBOL_MAP& aMap, bool aAllowEmpty,
1914 REPORTER& aReporter )
1915{
1916 if( aAllowEmpty && aPath.IsEmpty() )
1918
1919 try
1920 {
1921 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
1922 aOwners = std::move( loaded.first );
1923 aMap = std::move( loaded.second );
1925 }
1926 catch( const IO_ERROR& ioe )
1927 {
1928 aReporter.Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
1930 }
1931 catch( const std::exception& e )
1932 {
1933 aReporter.Report(
1934 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
1936 }
1937
1939}
1940
1941
1942// Flatten a symbol-library name map into a single DOCUMENT_GEOMETRY tinted with
1943// the supplied per-side theme colour.
1946{
1948
1949 for( const auto& [name, symbol] : aMap )
1950 {
1951 if( symbol )
1952 KICAD_DIFF::AppendGeometry( geometry, KICAD_DIFF::ExtractSymbolGeometry( *symbol, aColor ) );
1953 }
1954
1955 return geometry;
1956}
1957
1958
1960{
1961 JOB_SYM_DIFF* diffJob = dynamic_cast<JOB_SYM_DIFF*>( aJob );
1962
1963 if( !diffJob )
1965
1966 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
1967 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
1970
1971 if( int rc = loadSymbolLibrarySide( diffJob->m_inputA, ownersA, mapA, false, *m_reporter );
1973 {
1974 return rc;
1975 }
1976
1977 if( int rc = loadSymbolLibrarySide( diffJob->m_inputB, ownersB, mapB, false, *m_reporter );
1979 {
1980 return rc;
1981 }
1982
1983 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, diffJob->m_inputB );
1985
1986 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1987
1988 if( diffJob->m_exitCodeOnly )
1989 return diffExitCode;
1990
1992 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1994 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1995 { return symbolLibraryGeometry( mapA, aColor ); };
1996 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1997 { return symbolLibraryGeometry( mapB, aColor ); };
1998
1999 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
2000}
2001
2002
2003// ============================================================================
2004// JobOpenDiffDialog: load two on-disk files and open DIALOG_KICAD_DIFF.
2005// Dispatched from the project manager / PR-review dialog via KIWAY.
2006// ============================================================================
2010#include <jobs/scratch_doc.h>
2011
2012
2014 const wxString& aFileB, const wxString& aLabelA,
2015 const wxString& aLabelB, wxWindow* aParent,
2016 REPORTER* aReporter )
2017{
2018 // Restore m_reporter on scope exit so a caller's transient (often
2019 // stack-local) reporter doesn't outlive this call as a dangling member.
2021 aReporter ? aReporter : m_reporter );
2022
2023 wxWindow* parent = aParent ? aParent : ( wxTheApp ? wxTheApp->GetTopWindow() : nullptr );
2024
2027 KICAD_DIFF::DOCUMENT_GEOMETRY compGeometry;
2028
2029 switch( aKind )
2030 {
2032 {
2034
2037
2038 if( !a.doc && !aFileA.IsEmpty() )
2039 {
2040 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileA ), RPT_SEVERITY_ERROR );
2042 }
2043
2044 if( !b.doc && !aFileB.IsEmpty() )
2045 {
2046 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileB ), RPT_SEVERITY_ERROR );
2048 }
2049
2050 // Synthesize empty SCHEMATICs against scratch PROJECTs for any
2051 // missing side so SCH_DIFFER sees a valid empty document.
2052 PROJECT scratchPrjA;
2053 PROJECT scratchPrjB;
2054 std::unique_ptr<SCHEMATIC> emptyA;
2055 std::unique_ptr<SCHEMATIC> emptyB;
2056
2057 if( !a.doc )
2058 {
2059 emptyA = std::make_unique<SCHEMATIC>( &scratchPrjA );
2060 emptyA->CreateDefaultScreens();
2061 }
2062
2063 if( !b.doc )
2064 {
2065 emptyB = std::make_unique<SCHEMATIC>( &scratchPrjB );
2066 emptyB->CreateDefaultScreens();
2067 }
2068
2069 SCHEMATIC* schA = a.doc ? a.doc.get() : emptyA.get();
2070 SCHEMATIC* schB = b.doc ? b.doc.get() : emptyB.get();
2071
2072 KICAD_DIFF::SCH_DIFFER differ( schA, schB, aFileB );
2073 result = differ.Diff();
2074
2075 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2076 refGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schA, theme.reference );
2077 compGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schB, theme.comparison );
2078
2079 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2080 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2081
2083 parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ),
2084 [schA, schB, color = theme.reference]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
2085 {
2086 SCH_SCREEN* refScreen = schA ? schA->RootScreen() : nullptr;
2087 SCH_SCREEN* compScreen = schB ? schB->RootScreen() : nullptr;
2088
2089 if( !aSheetPath.empty() )
2090 {
2091 if( schA )
2092 {
2093 if( auto sp = schA->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2094 refScreen = sp->LastScreen();
2095 }
2096
2097 if( schB )
2098 {
2099 if( auto sp = schB->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2100 compScreen = sp->LastScreen();
2101 }
2102 }
2103
2104 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, schA, schB, color, {}, {}, {}, refScreen,
2105 compScreen );
2106 } );
2107 dlg.ShowModal();
2108
2109 if( emptyA )
2110 emptyA->SetProject( nullptr );
2111
2112 if( emptyB )
2113 emptyB->SetProject( nullptr );
2114
2116 }
2118 {
2119 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
2120 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
2123
2124 if( int rc = loadSymbolLibrarySide( aFileA, ownersA, mapA, true, *m_reporter );
2126 {
2127 return rc;
2128 }
2129
2130 if( int rc = loadSymbolLibrarySide( aFileB, ownersB, mapB, true, *m_reporter );
2132 {
2133 return rc;
2134 }
2135
2136 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, aFileB );
2137 result = differ.Diff();
2138
2139 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2140 refGeometry = symbolLibraryGeometry( mapA, theme.reference );
2141 compGeometry = symbolLibraryGeometry( mapB, theme.comparison );
2142 break;
2143 }
2144 default:
2145 m_reporter->Report( _( "Unsupported document kind for this dispatcher.\n" ), RPT_SEVERITY_ERROR );
2147 }
2148
2149 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2150 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2151
2152 DIALOG_KICAD_DIFF dlg( parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ) );
2153 dlg.ShowModal();
2154
2156}
2157
2158
2159// ============================================================================
2160// JobSchMerge: sch_merge implementation
2161// ============================================================================
2164#include <jobs/scratch_doc.h>
2165
2166
2167int EESCHEMA_JOBS_HANDLER::RunMerge( KICAD_DIFF::DOC_KIND aKind, const wxString& aAncestor,
2168 const wxString& aOurs, const wxString& aTheirs,
2169 const wxString& aOutput, bool aInteractive, bool aSingleFile,
2170 REPORTER* aReporter )
2171{
2172 // Restore m_reporter on scope exit so a caller's transient (often
2173 // stack-local) reporter doesn't outlive this call as a dangling member.
2175 aReporter ? aReporter : m_reporter );
2176
2177 if( aKind == KICAD_DIFF::DOC_KIND::SYM_LIB )
2178 return runSymLibMerge( aAncestor, aOurs, aTheirs, aOutput );
2179
2180 return runSchMerge( aAncestor, aOurs, aTheirs, aOutput, aInteractive );
2181}
2182
2183
2184int EESCHEMA_JOBS_HANDLER::runSchMerge( const wxString& aAncestor, const wxString& aOurs,
2185 const wxString& aTheirs, const wxString& aOutput,
2186 bool aInteractive )
2187{
2189
2190 SCRATCH_DOC<SCHEMATIC> ancestor = loadScratchSchematic( mgr, aAncestor );
2191 SCRATCH_DOC<SCHEMATIC> ours = loadScratchSchematic( mgr, aOurs );
2192 SCRATCH_DOC<SCHEMATIC> theirs = loadScratchSchematic( mgr, aTheirs );
2193
2194 if( !ancestor.doc || !ours.doc || !theirs.doc )
2195 {
2196 m_reporter->Report( _( "Failed to load one or more input schematics\n" ), RPT_SEVERITY_ERROR );
2198 }
2199
2200 // Multi-sheet hierarchies are supported: each non-root sub-sheet is
2201 // written alongside the output root using its original basename. Top-level
2202 // sheets stay singular — multiple roots is an editor invariant the diff
2203 // engine never models, so refuse those.
2204 auto hasSingleRoot = []( const SCHEMATIC* aSch )
2205 {
2206 return aSch->GetTopLevelSheets().size() == 1;
2207 };
2208
2209 if( !hasSingleRoot( ancestor.doc.get() ) || !hasSingleRoot( ours.doc.get() ) || !hasSingleRoot( theirs.doc.get() ) )
2210 {
2211 m_reporter->Report( _( "sch merge requires each input to have a single top-level sheet\n" ),
2214 }
2215
2216 KICAD_DIFF::SCH_DIFFER ourDiff( ancestor.doc.get(), ours.doc.get() );
2217 KICAD_DIFF::SCH_DIFFER theirDiff( ancestor.doc.get(), theirs.doc.get() );
2218
2219 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2220 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2221
2223 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2224
2225 // A cancelled dialog leaves plan unresolved and falls through to the
2226 // marker flow below.
2227 if( aInteractive && !plan.Resolved() )
2228 {
2229 if( !Pgm().IsGUI() )
2230 {
2231 m_reporter->Report( _( "--interactive requires a GUI KiCad process; the console "
2232 "kicad-cli cannot open dialogs.\n" ),
2235 }
2236
2237 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2239
2240 if( ancestor.doc )
2242
2243 if( ours.doc )
2245
2246 if( theirs.doc )
2248
2250 KICAD_DIFF::CollectChangeBBoxes( theirDocDiff, ctx.theirsBBoxes );
2251
2252 DIALOG_KICAD_MERGE_3WAY dlg( wxTheApp->GetTopWindow(), plan, std::move( ctx ) );
2253
2254 if( dlg.ShowModal() == wxID_APPLY )
2255 plan = dlg.GetResolvedPlan();
2256 }
2257
2258 // Snapshot of the plan before the applier moves it; drives the
2259 // unresolved-conflict report below.
2260 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2261
2262 KICAD_DIFF::SCH_MERGE_APPLIER applier( ancestor.doc.get(), ours.doc.get(), theirs.doc.get(), std::move( plan ) );
2263
2264 if( !applier.Apply() )
2265 {
2266 m_reporter->Report( _( "Merge applier failed to produce a schematic\n" ), RPT_SEVERITY_ERROR );
2268 }
2269
2270 // Sheet add/remove/replace resolutions are explicitly skipped by
2271 // SCH_MERGE_APPLIER (see isSheetItem); succeeding here would silently
2272 // drop hierarchy edits.
2273 if( applier.GetReport().sheetActionsSkipped > 0 )
2274 {
2275 m_reporter->Report( _( "Merge contains hierarchical sheet structure changes that sch merge "
2276 "cannot apply\n" ),
2279 }
2280
2281 // Refusal above guarantees a single top-level sheet.
2282 SCH_SHEET* rootSheet = ancestor.doc->GetTopLevelSheet( 0 );
2283
2284 wxFileName outFn( aOutput );
2285 outFn.MakeAbsolute();
2286
2287 // Sub-sheets land alongside the root by basename. Preserving the original
2288 // relative-path subdirectory structure would force kicad-cli sch merge to
2289 // mkdir into user space; the basename-flat scheme is what makes the common
2290 // git-mergetool case work without surprises.
2291 const wxString outDir = outFn.GetPath();
2292
2293 SCH_SCREENS screens( rootSheet );
2294 SCH_SCREEN* rootScreen = rootSheet->GetScreen();
2295
2296 // Detect two sub-sheets sharing a basename (e.g., a/foo.kicad_sch and
2297 // b/foo.kicad_sch) before any I/O — the flat output layout can't honor
2298 // both, and silently overwriting one is the worst outcome.
2299 std::map<wxString, SCH_SCREEN*> basenameOwner;
2300
2301 for( size_t i = 0; i < screens.GetCount(); ++i )
2302 {
2303 SCH_SCREEN* screen = screens.GetScreen( i );
2304
2305 if( !screen || screen == rootScreen )
2306 continue;
2307
2308 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2309
2310 if( basename.IsEmpty() )
2311 continue;
2312
2313 auto [it, inserted] = basenameOwner.emplace( basename, screen );
2314
2315 if( !inserted && it->second != screen )
2316 {
2317 m_reporter->Report( wxString::Format( _( "Cannot flatten sub-sheets with duplicate "
2318 "basename '%s'\n" ),
2319 basename ),
2322 }
2323 }
2324
2325 // Rewrite every SCH_SHEET symbol's filename field to its child screen's
2326 // basename, so the root file (and any intermediate sheet) references the
2327 // flattened layout we're about to write.
2328 for( size_t i = 0; i < screens.GetCount(); ++i )
2329 {
2330 SCH_SCREEN* parent = screens.GetScreen( i );
2331
2332 if( !parent )
2333 continue;
2334
2335 for( SCH_ITEM* item : parent->Items().OfType( SCH_SHEET_T ) )
2336 {
2337 SCH_SHEET* childRef = static_cast<SCH_SHEET*>( item );
2338 SCH_SCREEN* childScreen = childRef->GetScreen();
2339
2340 if( !childScreen || childScreen == rootScreen )
2341 continue;
2342
2343 const wxString basename = wxFileName( childScreen->GetFileName() ).GetFullName();
2344
2345 if( !basename.IsEmpty() )
2346 childRef->SetFileName( basename );
2347 }
2348 }
2349
2350 try
2351 {
2352 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2353 pi->SaveSchematicFile( outFn.GetFullPath(), rootSheet, ancestor.doc.get() );
2354
2355 for( size_t i = 0; i < screens.GetCount(); ++i )
2356 {
2357 SCH_SCREEN* screen = screens.GetScreen( i );
2358 SCH_SHEET* sheet = screens.GetSheet( i );
2359
2360 if( !screen || !sheet || screen == rootScreen )
2361 continue;
2362
2363 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2364
2365 if( basename.IsEmpty() )
2366 continue;
2367
2368 wxFileName outSubFn( outDir, basename );
2369 pi->SaveSchematicFile( outSubFn.GetFullPath(), sheet, ancestor.doc.get() );
2370 }
2371 }
2372 catch( const IO_ERROR& ioe )
2373 {
2374 m_reporter->Report( wxString::Format( _( "Failed to save merged schematic: %s\n" ), ioe.What() ),
2377 }
2378
2379 // If the applier mutated project-file-scoped state (ERC severities, etc),
2380 // persist it as a sibling .kicad_pro alongside the .kicad_sch output —
2381 // otherwise the resolution dies with the process. Only write when actually
2382 // needed; clobbering an existing .kicad_pro with ancestor's project
2383 // would lose unrelated user settings (library tables, mru paths).
2384 if( applier.GetReport().projectFileTouched && ancestor.project )
2385 {
2386 wxFileName proFn = outFn;
2387 proFn.SetExt( FILEEXT::ProjectFileExtension );
2388
2389 // JSON-patch path: only the diffed DOC_PROP fields are written into
2390 // the output .kicad_pro, so any non-diffed user customisations
2391 // (library tables, last paths, layer presets, text variables) are
2392 // preserved. Fall back to SaveProjectCopy on parse failure.
2393 PROJECT_FILE& ancProj = ancestor.project->GetProjectFile();
2394 ancProj.Store();
2395
2396 const KICAD_DIFF::SCH_MERGE_APPLIER::REPORT& mergeReport = applier.GetReport();
2397
2398 // PROJECT_FILE::Store() flushes the project file's own params but not
2399 // its registered NESTED_SETTINGS. Flush only the resolved nested
2400 // settings so Internals() reflects the merge result without touching
2401 // unrelated project subtrees.
2402 if( mergeReport.ercSeveritiesTouched && ancestor.doc )
2403 ancestor.doc->ErcSettings().SaveToFile( wxEmptyString, true );
2404
2405 if( mergeReport.drawingSheetFileTouched && ancestor.doc )
2406 ancestor.doc->Settings().SaveToFile( wxEmptyString, true );
2407
2408 std::set<wxString> touched;
2409 if( mergeReport.ercSeveritiesTouched )
2410 touched.insert( KICAD_DIFF::DOC_PROP_ERC_SEVERITIES );
2411
2412 if( mergeReport.drawingSheetFileTouched )
2413 touched.insert( KICAD_DIFF::DOC_PROP_DRAWING_SHEET );
2414
2415 if( !KICAD_DIFF::ApplyProjectFilePatches( proFn.GetFullPath(), *ancProj.Internals(), touched,
2417 {
2418 if( !Pgm().GetSettingsManager().SaveProjectCopy( proFn.GetFullPath(), ancestor.project ) )
2419 {
2420 m_reporter->Report(
2421 wxString::Format( _( "Failed to save merged project file: %s\n" ), proFn.GetFullPath() ),
2424 }
2425 }
2426 }
2427
2428 // Surface post-apply validator findings (refdes collisions, schema
2429 // mismatch, missed connectivity rebuild). Advisory — they do not change the
2430 // exit code, only the merge's resolved/unresolved status does.
2432 m_reporter->Report( wxString::Format( wxS( "%s: %s\n" ), f.validator, f.message ), f.severity );
2433
2434 // The merged schematic was written to m_outputPath above, so the output is
2435 // always valid. Unresolved conflicts are reported and signalled via the
2436 // exit code; the user resolves them with the interactive mergetool.
2437 if( !planSnapshot.Resolved() )
2438 {
2439 m_reporter->Report( wxString::Format( _( "Merge completed with %zu unresolved conflict(s) in %s\n" ),
2440 planSnapshot.ConflictCount(), aOutput ),
2443 }
2444
2446}
2447
2448
2449// ============================================================================
2450// JobSymLibMerge: 3-way merge of .kicad_sym libraries.
2451// ============================================================================
2454#include <wx/ffile.h>
2455
2456
2457int EESCHEMA_JOBS_HANDLER::runSymLibMerge( const wxString& aAncestor, const wxString& aOurs,
2458 const wxString& aTheirs, const wxString& aOutput )
2459{
2460 if( aOutput.IsEmpty() )
2461 {
2462 m_reporter->Report( _( "--output is required\n" ), RPT_SEVERITY_ERROR );
2464 }
2465
2466 // Three sides into name -> LIB_SYMBOL maps.
2467 struct LIB_SIDE
2468 {
2469 std::vector<std::unique_ptr<LIB_SYMBOL>> owners;
2471 };
2472
2473 LIB_SIDE ancestor, ours, theirs;
2474
2475 auto loadSide = [&]( const wxString& aPath, LIB_SIDE& aSide ) -> int
2476 {
2477 try
2478 {
2479 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
2480 aSide.owners = std::move( loaded.first );
2481 aSide.map = std::move( loaded.second );
2483 }
2484 catch( const IO_ERROR& ioe )
2485 {
2486 m_reporter->Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
2488 }
2489 catch( const std::exception& e )
2490 {
2491 m_reporter->Report(
2492 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
2494 }
2495
2497 };
2498
2499 if( int rc = loadSide( aAncestor, ancestor ); rc != CLI::EXIT_CODES::SUCCESS )
2500 return rc;
2501
2502 if( int rc = loadSide( aOurs, ours ); rc != CLI::EXIT_CODES::SUCCESS )
2503 return rc;
2504
2505 if( int rc = loadSide( aTheirs, theirs ); rc != CLI::EXIT_CODES::SUCCESS )
2506 return rc;
2507
2508 KICAD_DIFF::SYM_LIB_DIFFER ourDiff( ancestor.map, ours.map, aOurs );
2509 KICAD_DIFF::SYM_LIB_DIFFER theirDiff( ancestor.map, theirs.map, aTheirs );
2510
2511 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2512 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2513
2515 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2516
2517 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2518
2519 KICAD_DIFF::SYM_LIB_MERGE_APPLIER applier( ancestor.map, ours.map, theirs.map, std::move( plan ) );
2520 std::vector<std::unique_ptr<LIB_SYMBOL>> merged = applier.Apply();
2521
2522 // Per-property symbol merge isn't implemented; MERGE_PROPS resolutions are
2523 // downgraded to TAKE_OURS. Surface that as unresolved so the user sees a
2524 // marker instead of silent partial-merge.
2525 const bool hadSilentFallback = applier.GetReport().mergePropsFallback > 0;
2526
2527 // Serialize via the sexpr lib cache: create at output path, add each
2528 // merged symbol, save. The cache owns its symbols once added.
2529 wxFileName outFn( aOutput );
2530 outFn.MakeAbsolute();
2531
2532 try
2533 {
2534 SCH_IO_KICAD_SEXPR_LIB_CACHE cache( outFn.GetFullPath() );
2535
2536 // SCH_IO_LIB_CACHE::AddSymbol takes ownership; the cache destructor
2537 // deletes the symbols it holds.
2538 for( auto& sym : merged )
2539 {
2540 if( sym )
2541 cache.AddSymbol( std::move( sym ) );
2542 }
2543
2544 cache.SetModified( true );
2545 cache.Save();
2546 }
2547 catch( const IO_ERROR& ioe )
2548 {
2549 m_reporter->Report( wxString::Format( _( "Failed to save merged symbol library: %s\n" ), ioe.What() ),
2552 }
2553
2554 // The merged library was saved above, so the output is always valid.
2555 if( !planSnapshot.Resolved() || hadSilentFallback )
2556 {
2557 // Conflict count = engine-unresolved ∪ applier-downgraded (deduped, so
2558 // an item that was both unresolved and silently downgraded counts once).
2559 std::set<KIID_PATH> conflicts( planSnapshot.unresolved.begin(), planSnapshot.unresolved.end() );
2560
2561 for( const KIID_PATH& id : applier.GetReport().mergePropsFallbackIds )
2562 conflicts.insert( id );
2563
2564 m_reporter->Report( wxString::Format( _( "Symbol library merge completed with %zu unresolved "
2565 "conflict(s) in %s\n" ),
2566 conflicts.size(), aOutput ),
2569 }
2570
2572}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
int GetPageCount() const
Definition base_screen.h:68
int GetVirtualPageNumber() const
Definition base_screen.h:71
const wxString & GetPageNumber() const
Color settings are a bit different than most of the settings objects in that there can be more than o...
File-compare dialog (Phase 7).
3-way merge resolution dialog (Phase 8).
const KICAD_DIFF::MERGE_PLAN & GetResolvedPlan() const
Returns the plan with the user's resolutions applied.
int ShowModal() override
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
void SetSheetPath(const std::string &aSheetPath)
Set the sheet path displayed in the title block.
void SetSheetCount(int aSheetCount)
Change the sheet-count number displayed in the title block.
void SetVariantName(const std::string &aVariant)
Set the current variant name and description to be shown on the drawing sheet.
void SetVariantDesc(const std::string &aVariantDesc)
void SetPageNumber(const std::string &aPageNumber)
Change the page number displayed in the title block.
void SetSheetName(const std::string &aSheetName)
Set the sheet name displayed in the title block.
void SetPageBorderColorLayer(int aLayerId)
Override the layer used to pick the color of the page border (normally LAYER_GRID)
void SetIsFirstPage(bool aIsFirstPage)
Change if this is first page.
void SetFileName(const std::string &aFileName)
Set the file name displayed in the title block.
void SetColorLayer(int aLayerId)
Can be used to override which layer ID is used for drawing sheet item colors.
const KIID m_Uuid
Definition eda_item.h:597
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true, REPORTER *aRootReporter=nullptr)
int runSchMerge(const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput, bool aInteractive)
void InitRenderSettings(SCH_RENDER_SETTINGS *aRenderSettings, const wxString &aTheme, SCHEMATIC *aSch, const wxString &aDrawingSheetOverride=wxEmptyString)
Configure the SCH_RENDER_SETTINGS object with the correct data to be used with plotting.
int RunMerge(KICAD_DIFF::DOC_KIND aKind, const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput, bool aInteractive, bool aSingleFile, REPORTER *aReporter)
Non-job entry points (reached via the kiface KIFACE_MERGE_DOCUMENT / KIFACE_OPEN_DIFF_DIALOG function...
DS_PROXY_VIEW_ITEM * getDrawingSheetProxyView(SCHEMATIC *aSch)
int runSymLibMerge(const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput)
SCHEMATIC * getSchematic(const wxString &aPath, bool aRequireRoot=true)
int OpenDiffDialog(KICAD_DIFF::DOC_KIND aKind, const wxString &aFileA, const wxString &aFileB, const wxString &aLabelA, const wxString &aLabelB, wxWindow *aParent, REPORTER *aReporter)
void ClearCachedSchematic()
Clear the cached CLI schematic so the next job reloads from the current project.
int doSymExportSvg(JOB_SYM_EXPORT_SVG *aSvgJob, SCH_RENDER_SETTINGS *aRenderSettings, LIB_SYMBOL *symbol)
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
bool WriteJsonReport(const wxString &aFullFileName)
Writes a JSON formatted ERC Report to the given file path in the c-locale.
bool WriteTextReport(const wxString &aFullFileName)
Writes the text report also available via GetTextReport directly to a given file path.
void RunTests(DS_PROXY_VIEW_ITEM *aDrawingSheet, SCH_EDIT_FRAME *aEditFrame, KIFACE *aCvPcb, PROJECT *aProject, PROGRESS_REPORTER *aProgressReporter)
Definition erc.cpp:2751
void SetVariantNames(const std::vector< wxString > &aVariantNames)
std::vector< BOM_FIELD > GetFieldsOrdered()
static const wxString ITEM_NUMBER_VARIABLE
wxString Export(const BOM_FMT_PRESET &aSettings)
int GetFieldNameCol(const wxString &aFieldName) const
void SetCurrentVariant(const wxString &aVariantName)
Set the current variant name for highlighting purposes.
void ApplyBomPreset(const BOM_PRESET &aPreset)
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser) override
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
wxString m_inputB
Comparison document (file or directory)
wxString m_inputA
Reference document (file or directory)
void Register(const std::string &aJobTypeName, std::function< int(JOB *job)> aHandler, std::function< bool(JOB *job, wxWindow *aParent)> aConfigHandler)
JOB_DISPATCHER(KIWAY *aKiway)
PROGRESS_REPORTER * m_progressReporter
REPORTER * m_reporter
wxString GetSelectedVariant() const
std::vector< wxString > m_variantNames
wxString m_stringDelimiter
wxString m_fieldDelimiter
bool m_includeByteOrderMark
wxString m_filename
wxString m_filterString
std::vector< wxString > m_fieldsOrdered
wxString m_refRangeDelimiter
std::vector< wxString > m_fieldsLabels
wxString m_refDelimiter
std::vector< wxString > m_fieldsGroupBy
wxString m_bomFmtPresetName
wxString m_sortField
wxString m_bomPresetName
BOM_FILTER_SCOPE m_filterScope
std::vector< wxString > m_variantNames
JOB_PAGE_SIZE m_pageSizeSelect
SCH_PLOT_FORMAT m_plotFormat
std::vector< wxString > m_variantNames
std::vector< wxString > m_plotPages
bool m_exitCodeViolations
Definition job_rc.h:52
int m_severity
Definition job_rc.h:49
UNITS m_units
Definition job_rc.h:48
OUTPUT_FORMAT m_format
Definition job_rc.h:50
wxString m_filename
Definition job_rc.h:47
Job to import a non-KiCad schematic file to KiCad format.
wxString m_reportFile
wxString m_inputFile
IMPORT_REPORT_FORMAT m_reportFormat
std::map< wxString, wxString > m_netNameMap
wxString m_outputLibraryPath
An simple container class that lets us dispatch output jobs to kifaces.
Definition job.h:184
void SetConfiguredOutputPath(const wxString &aPath)
Sets the configured output path for the job, this path is always saved to file.
Definition job.cpp:157
void AddOutput(wxString aOutputPath)
Definition job.h:216
wxString GetFullOutputPath(PROJECT *aProject) const
Returns the full output path for the job, taking into account the configured output path,...
Definition job.cpp:150
bool GetOutputPathIsDirectory() const
Definition job.h:258
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition job.h:235
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition job.h:204
void SetWorkingOutputPath(const wxString &aPath)
Sets a transient output path for the job, it takes priority over the configured output path when GetF...
Definition job.h:241
const std::map< wxString, wxString > & GetVarOverrides() const
Definition job.h:197
JSON_SETTINGS_INTERNALS * Internals()
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
Three-way merge plan generator.
MERGE_PLAN Plan(const DOCUMENT_DIFF &aAncestorOurs, const DOCUMENT_DIFF &aAncestorTheirs) const
Plan the merge given the canonical pair of diffs.
const REPORT & GetReport() const
std::vector< std::unique_ptr< ITEM > > Apply()
Diff two already-parsed SCHEMATICs and produce a DOCUMENT_DIFF.
Definition sch_differ.h:55
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
Materialize a MERGE_PLAN into a merged SCHEMATIC by mutating the ancestor in place.
bool Apply()
Apply the plan to the ancestor.
const REPORT & GetReport() const
Diff two .kicad_sym symbol libraries.
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
static std::pair< std::vector< std::unique_ptr< LIB_SYMBOL > >, SYMBOL_MAP > LoadLibrary(const wxString &aPath)
Convenience: load a .kicad_sym path into a SYMBOL_MAP using SCH_IO_KICAD_SEXPR::EnumerateSymbolLib.
std::map< wxString, const LIB_SYMBOL * > SYMBOL_MAP
Library content is a map of (canonical_name -> LIB_SYMBOL*).
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
void SetDefaultPenWidth(int aWidth)
void SetGapLengthRatio(double aRatio)
void SetDashLengthRatio(double aRatio)
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:340
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:388
@ FACE_CVPCB
Definition kiway.h:349
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
Define a library symbol object.
Definition lib_symbol.h:119
bool IsDerived() const
Definition lib_symbol.h:236
std::shared_ptr< LIB_SYMBOL > GetRootSymbol() const
Get the parent symbol that does not have another parent.
wxString GetName() const override
Definition lib_symbol.h:181
const std::vector< wxString > & GetBodyStyleNames() const
bool HasDeMorganBodyStyles() const override
int GetBodyStyleCount() const override
The body styles are a property of the drawings, which a derived symbol inherits from its root symbol ...
int GetUnitCount() const override
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:508
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:123
The backing store for a PROJECT, in JSON format.
TEMPLATES m_TemplateFieldNames
Project and global field name templates shared by project editors.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Container for project specific data.
Definition project.h:63
virtual void ApplyTextVars(const std::map< wxString, wxString > &aVarsMap)
Applies the given var map, it will create or update existing vars.
Definition project.cpp:132
virtual PROJECT_FILE & GetProjectFile() const
Definition project.h:201
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Holds all the data relating to one schematic.
Definition schematic.h:148
void SetCurrentVariant(const wxString &aVariantName)
wxString GetVariantDescription(const wxString &aVariantName) const
Return the description for a variant.
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
SCHEMATIC_SETTINGS & Settings() const
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:170
wxString GetCurrentVariant() const
Return the current variant being edited.
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
const std::map< wxString, wxString > * GetProperties()
Definition schematic.h:173
SCH_SHEET & Root() const
Definition schematic.h:199
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Schematic editor (Eeschema) main window.
SCHEMATIC & Schematic() const
A cache assistant for the KiCad s-expression symbol libraries.
void Save(const std::optional< bool > &aOpt=std::nullopt) override
Save the entire library to file m_libFileName;.
void SetFileName(const wxString &aFileName)
void SetModified(bool aModified=true)
virtual void AddSymbol(std::unique_ptr< LIB_SYMBOL > aSymbol)
static bool ConvertLibrary(std::map< std::string, UTF8 > *aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilepath, REPORTER *aReporter=nullptr)
Convert a schematic symbol library to the latest KiCad format.
static const wxString ShowType(SCH_FILE_T aFileType)
Return a brief name for a plugin, given aFileType enum.
static SCH_FILE_T GuessPluginTypeFromSchPath(const wxString &aSchematicPath, int aCtl=0)
Return a plugin type given a schematic using the file extension of aSchematicPath.
static SCH_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a symbol library using the file extension of aLibPath.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
int CheckAnnotation(ANNOTATION_ERROR_HANDLER aErrorHandler)
Check for annotations errors.
A helper to define a symbol's reference designator in a schematic.
void LoadColors(const COLOR_SETTINGS *aSettings) override
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:758
SCH_SCREEN * GetNext()
SCH_SCREEN * GetScreen(unsigned int aIndex) const
SCH_SCREEN * GetFirst()
size_t GetCount() const
Definition sch_screen.h:763
SCH_SHEET * GetSheet(unsigned int aIndex) const
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:140
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
const wxString & GetFileName() const
Definition sch_screen.h:153
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:138
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:164
void GetSymbols(SCH_REFERENCE_LIST &aReferences, SYMBOL_FILTER aSymbolFilter, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:390
wxString GetName() const
Definition sch_sheet.h:142
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
bool IsVirtualRootSheet() const
Schematic symbol object.
Definition sch_symbol.h:75
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
RAII class that sets an value at construction and resets it to the original value at destruction.
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
bool IsProjectOpenNotDummy() const
Helper for checking if we have a project open that is not a dummy project.
void UpdateReferences(const SCH_REFERENCE_LIST &aRefs)
const SCH_REFERENCE_LIST & GetReferenceList() const
An interface to the global shared library manager that is schematic-specific and linked to one projec...
const std::vector< TEMPLATE_FIELDNAME > & GetResolvedTemplateFieldNames()
Return the resolved project and global template field name list for read only access.
GAL-backed canvas for visualizing a KICAD_DIFF::DIFF_SCENE.
wxString GetGeneratedFieldDisplayName(const wxString &aSource)
Returns any variables unexpanded, e.g.
Definition common.cpp:481
bool IsGeneratedField(const wxString &aFieldName)
Returns true if the entire string is generated, e.g is a single text var reference.
Definition common.cpp:494
wxString GetDefaultPlotExtension(PLOT_FORMAT aFormat)
Return the default plot extension for a format.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
#define _(s)
EDA_UNITS
Definition eda_units.h:44
bool PrepareSaveAsFiles(SCHEMATIC &aSchematic, SCH_SCREENS &aScreens, const wxFileName &aOldRoot, const wxFileName &aNewRoot, bool aSaveCopy, bool aCopySubsheets, bool aIncludeExternSheets, std::unordered_map< SCH_SCREEN *, wxString > &aFilenameMap, wxString &aErrorMsg)
static int loadSymbolLibrarySide(const wxString &aPath, std::vector< std::unique_ptr< LIB_SYMBOL > > &aOwners, KICAD_DIFF::SYM_LIB_DIFFER::SYMBOL_MAP &aMap, bool aAllowEmpty, REPORTER &aReporter)
static SCRATCH_DOC< SCHEMATIC > loadScratchSchematic(SETTINGS_MANAGER &aMgr, const wxString &aPath)
static KICAD_DIFF::DOCUMENT_GEOMETRY symbolLibraryGeometry(const KICAD_DIFF::SYM_LIB_DIFFER::SYMBOL_MAP &aMap, const KIGFX::COLOR4D &aColor)
ERCE_T
ERC error codes.
@ FRAME_SCH
Definition frame_type.h:30
static const std::string CadstarNetlistFileExtension
static const std::string NetlistFileExtension
static const std::string ReportFileExtension
static const std::string ProjectFileExtension
static const std::string JsonFileExtension
static const std::string XmlFileExtension
static const std::string KiCadSchematicFileExtension
static const std::string OrCadPcb2NetlistFileExtension
static const std::string CsvFileExtension
static const std::string SpiceFileExtension
static const std::string SVGFileExtension
std::map< wxString, wxString > GetBoardNetNameMap(const IMPORT_NET_MAP &aMap, REPORTER &aReporter)
Reduce the map to the board net renames it justifies.
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
void WriteImportReport(REPORTER *aReporter, IMPORT_REPORT_FORMAT aFormat, const wxString &aReportFile, const IMPORT_REPORT_DATA &aData)
Emit an import report in the requested format to aReportFile, or to aReporter (at INFO severity) when...
wxString DefaultImportOutputPath(const wxString &aInputFile, const wxString &aKiCadExt)
Build the default output path for an import by swapping the input file's extension for the given KiCa...
#define KICAD_FONT_NAME
#define KICTL_NONKICAD_ONLY
chosen file is non-KiCad according to user
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition layer_ids.h:518
@ LAYER_SCHEMATIC_PAGE_LIMITS
Definition layer_ids.h:519
static std::vector< PENDING_PROPERTY > plan(const EDA_ITEM &aSource, const EDA_ITEM &aTarget, const std::set< wxString > &aEnabledKeys)
Target properties to write, paired with values read off the source.
static const int ERR_ARGS
Definition exit_codes.h:31
static const int OK
Definition exit_codes.h:30
static const int ERR_RC_VIOLATIONS
Rules check violation count was greater than 0.
Definition exit_codes.h:37
static const int ERR_INVALID_INPUT_FILE
Definition exit_codes.h:33
static const int SUCCESS
Definition exit_codes.h:29
static const int ERR_INVALID_OUTPUT_CONFLICT
Definition exit_codes.h:34
static const int ERR_UNKNOWN_FILE_FORMAT
No plugin for the requested face recognized the input file format.
Definition exit_codes.h:42
static const int ERR_UNKNOWN
Definition exit_codes.h:32
const wxString DOC_PROP_ERC_SEVERITIES
void ConfigureSchDiffCanvasContext(WIDGET_DIFF_CANVAS &aCanvas, SCHEMATIC *aReference, SCHEMATIC *aComparison, const KIGFX::COLOR4D &aColor, const std::map< KIID, KIGFX::COLOR4D > &aOverrides, const std::vector< KIGFX::VIEW_ITEM * > &aExtraItems, const std::map< KIID, KICAD_DIFF::CATEGORY > &aCategories, SCH_SCREEN *aReferenceScreen, SCH_SCREEN *aComparisonScreen)
void CollectChangeBBoxes(const DOCUMENT_DIFF &aDiff, std::map< KIID_PATH, BOX2I > &aOut)
Walk a DOCUMENT_DIFF and populate a (KIID_PATH → BOX2I) map with each changed item's bbox,...
DOCUMENT_GEOMETRY ExtractSymbolGeometry(const LIB_SYMBOL &aSymbol, const KIGFX::COLOR4D &aColor, int aUnit, int aBodyStyle)
Extract coarse drawable context from a library symbol for visual symbol diffs.
DOCUMENT_GEOMETRY ExtractSchematicGeometry(const SCHEMATIC &aSchematic, const KIGFX::COLOR4D &aColor, const std::map< KIID, KIGFX::COLOR4D > &aOverrides, bool aOnlyOverrides)
Extract a coarse outline of a SCHEMATIC into a DOCUMENT_GEOMETRY for use as background context in DIF...
void AppendGeometry(DOCUMENT_GEOMETRY &aDst, DOCUMENT_GEOMETRY &&aSrc)
Move all primitives from aSrc into aDst.
DIFF_EMIT_OPTIONS MakeEmitOptions(const JOB_DIFF_BASE &aJob, const wxString &aLabelA, const wxString &aLabelB)
Build a DIFF_EMIT_OPTIONS pre-filled from the job's format, resolved output path and the supplied per...
const wxString DOC_PROP_DRAWING_SHEET
DOC_KIND
Document type a diff/merge entry point should route to, derived from a file path's extension.
int EmitDiffResult(const DOCUMENT_DIFF &aResult, const DIFF_EMIT_OPTIONS &aOptions, int aDiffExitCode, REPORTER &aReporter)
Emit a computed DOCUMENT_DIFF in the requested format.
LIB_MERGE_APPLIER< LIB_SYMBOL > SYM_LIB_MERGE_APPLIER
Symbol-library 3-way merge applier. See LIB_MERGE_APPLIER for behavior.
bool ApplyProjectFilePatches(const wxString &aOutputProPath, const nlohmann::json &aSource, const std::set< wxString > &aDocProps, DOC_KIND aKind)
Higher-level orchestrator: load the existing aOutputProPath as JSON (or start from aSource if the fil...
int DiffExitCode(const DOCUMENT_DIFF &aResult)
Map a computed diff onto its CLI exit code – SUCCESS when empty, otherwise ERR_RC_VIOLATIONS.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
@ GNL_OPT_BOM
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
PLOT_FORMAT
The set of supported output plot formats.
Definition plotter.h:63
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
#define SKIP_CONNECTIVITY
Definition sch_commit.h:41
#define DELETE_REMOVED_ITEMS
Definition sch_commit.h:43
#define SKIP_UNDO
Definition sch_commit.h:38
#define SEXPR_SYMBOL_LIB_FILE_VERSION
This file contains the file format version information for the s-expression schematic and symbol libr...
#define SEXPR_SCHEMATIC_FILE_VERSION
Schematic file version.
bool PlotSymbolToSVG(LIB_SYMBOL &aDrawSymbol, LIB_SYMBOL &aFieldsSymbol, int aUnit, int aBodyStyle, const BOX2I &aBBox, SCH_RENDER_SETTINGS &aRenderSettings, bool aBlackAndWhite, const wxString &aFileName, REPORTER *aReporter)
Plot a single symbol variant (unit and body style) to an SVG file.
BOX2I GetSymbolPlotBBox(const LIB_SYMBOL &aSymbol, int aUnit, int aBodyStyle, bool aIncludeHiddenFields)
Compute the bounding box used to size a symbol SVG plot.
@ PAGE_SIZE_AUTO
Definition sch_plotter.h:46
@ PAGE_SIZE_A
Definition sch_plotter.h:48
@ PAGE_SIZE_A4
Definition sch_plotter.h:47
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
@ SYMBOL_FILTER_NON_POWER
@ SYMBOL_FILTER_ALL
@ GLOBAL_CLEANUP
Definition schematic.h:94
SCRATCH_DOC< DOC > LoadScratchDoc(SETTINGS_MANAGER &aMgr, const wxString &aDocPath, Loader aLoader, ClearFn aClearFn)
Construct a SCRATCH_DOC by loading a project non-active and then handing it to the caller's document ...
COLOR_SETTINGS * GetColorSettings(const wxString &aName)
T * GetAppSettings(const char *aFilename)
std::vector< FAB_LAYER_COLOR > dummy
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
wxString label
wxString name
wxString fieldDelimiter
bool includeByteOrderMark
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
Phase 8 context for the conflict canvas.
std::vector< BOM_PRESET > m_BomPresets
std::vector< BOM_FMT_PRESET > m_BomFmtPresets
Import provenance, held only for the lifetime of the importing SCHEMATIC.
KIGFX::COLOR4D reference
Default color for source-document context geometry.
Definition diff_scene.h:287
Describes how a computed DOCUMENT_DIFF should be emitted by a diff job.
std::function< DOCUMENT_GEOMETRY(const KIGFX::COLOR4D &)> comparisonGeometry
DOC_KIND docKind
Source document type, propagated onto the scene so the PNG/SVG renderer sizes its viewport with the m...
std::function< DOCUMENT_GEOMETRY(const KIGFX::COLOR4D &)> referenceGeometry
The full set of changes between two parsed documents of one type.
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
Result of planning a 3-way merge.
std::size_t ConflictCount() const
std::vector< KIID_PATH > unresolved
bool projectFileTouched
True iff the applier resolved state that lives in the .kicad_pro.
std::size_t sheetActionsSkipped
Number of actions skipped because they targeted a SCH_SHEET.
VALIDATION_REPORT validation
Post-apply validator pipeline result.
Outcome of a single validator run.
std::vector< VALIDATION_FAILURE > failures
std::vector< wxString > m_plotPages
Definition sch_plotter.h:56
wxString m_theme
Definition sch_plotter.h:65
DXF_UNITS m_DXF_File_Unit
Definition sch_plotter.h:72
bool m_PDFPropertyPopups
Definition sch_plotter.h:62
wxString m_outputDirectory
Definition sch_plotter.h:67
bool m_pngAntialias
Definition sch_plotter.h:75
wxString m_outputFile
Definition sch_plotter.h:68
bool m_blackAndWhite
Definition sch_plotter.h:59
wxString m_variant
Definition sch_plotter.h:69
bool m_PDFHierarchicalLinks
Definition sch_plotter.h:63
bool m_useBackgroundColor
Definition sch_plotter.h:61
bool m_plotDrawingSheet
Definition sch_plotter.h:55
Move-only RAII wrapper for "load a KiCad document into a non-active scratch PROJECT and clean up afte...
PROJECT * project
std::unique_ptr< DOC > doc
Hold a name of a symbol's field, field value, and default visibility.
SYMBOL_IMPORT_RECONCILE_RESULT ReconcileImportedSymbols(SCH_IO &aPlugin, SCHEMATIC &aSchematic, PROJECT &aProject, const wxString &aSchematicPath, const std::map< std::string, UTF8 > *aProperties, REPORTER &aReporter)
Reconcile aSchematic against the definitions aPlugin retained while loading it.
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
#define MANDATORY_FIELDS
FIELD_T
The set of all field indices assuming an array like sequence that a SCH_COMPONENT or LIB_PART can hol...
@ UNTRANSLATED
@ TRANSLATED
std::string path
VECTOR3I res
wxString result
Test unit parsing edge cases and error handling.
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171
Definition of file extensions used in Kicad.