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>
34#include <jobs/job_sch_erc.h>
35#include <jobs/job_sch_import.h>
40#include <schematic.h>
41#include <schematic_settings.h>
42#include <sch_screen.h>
43#include <sch_sheet.h>
44#include <sch_sheet_path.h>
45#include <sch_commit.h>
46#include <save_project_utils.h>
47#include <tool/tool_manager.h>
48#include <project.h>
50#include <wx/dir.h>
51#include <wx/file.h>
52#include <memory>
53#include <connection_graph.h>
54#include "eeschema_helpers.h"
55#include <filename_resolver.h>
56#include <kiway.h>
57#include <sch_painter.h>
58#include <locale_io.h>
59#include <erc/erc.h>
60#include <erc/erc_report.h>
64#include <paths.h>
65#include <reporter.h>
66#include <scoped_set_reset.h>
67#include <string_utils.h>
68
70
71#include <sch_file_versions.h>
72#include <sch_io/sch_io.h>
74
75#include <netlist.h>
85
86#include <fields_data_model.h>
87
92#include <confirm.h>
93#include <project_sch.h>
94
96
97
99 JOB_DISPATCHER( aKiway ),
100 m_cliSchematic( nullptr )
101{
102 Register( "bom", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportBom, this, std::placeholders::_1 ),
103 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
104 {
105 JOB_EXPORT_SCH_BOM* bomJob = dynamic_cast<JOB_EXPORT_SCH_BOM*>( job );
106
107 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
108
109 wxCHECK( bomJob && editFrame, false );
110
111 DIALOG_SYMBOL_FIELDS_TABLE dlg( editFrame, bomJob );
112
113 if( dlg.WasAborted() )
114 return false;
115
116 return dlg.ShowModal() == wxID_OK;
117 } );
118 Register( "pythonbom", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportPythonBom, this, std::placeholders::_1 ),
119 []( JOB* job, wxWindow* aParent ) -> bool
120 {
121 return true;
122 } );
123 Register( "netlist", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportNetlist, this, std::placeholders::_1 ),
124 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
125 {
126 JOB_EXPORT_SCH_NETLIST* netJob = dynamic_cast<JOB_EXPORT_SCH_NETLIST*>( job );
127
128 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
129
130 wxCHECK( netJob && editFrame, false );
131
132 DIALOG_EXPORT_NETLIST dlg( editFrame, aParent, netJob );
133 return dlg.ShowModal() == wxID_OK;
134 } );
135 Register( "plot", std::bind( &EESCHEMA_JOBS_HANDLER::JobExportPlot, this, std::placeholders::_1 ),
136 [aKiway]( JOB* job, wxWindow* aParent ) -> bool
137 {
138 JOB_EXPORT_SCH_PLOT* plotJob = dynamic_cast<JOB_EXPORT_SCH_PLOT*>( job );
139
140 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( aKiway->Player( FRAME_SCH, false ) );
141
142 wxCHECK( plotJob && editFrame, false );
143
144 if( plotJob->m_plotFormat == SCH_PLOT_FORMAT::HPGL )
145 {
146 DisplayErrorMessage( editFrame,
147 _( "Plotting to HPGL is no longer supported as of KiCad 10.0." ) );
148 return false;
149 }
150
151 DIALOG_PLOT_SCHEMATIC dlg( editFrame, aParent, plotJob );
152 return dlg.ShowModal() == wxID_OK;
153 } );
154 Register( "symupgrade", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymUpgrade, this, std::placeholders::_1 ),
155 []( JOB* job, wxWindow* aParent ) -> bool
156 {
157 return true;
158 } );
159 Register( "symsvg", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymExportSvg, this, std::placeholders::_1 ),
160 []( JOB* job, wxWindow* aParent ) -> bool
161 {
162 return true;
163 } );
164 Register( "sch_diff", std::bind( &EESCHEMA_JOBS_HANDLER::JobSchDiff, this, std::placeholders::_1 ),
165 []( JOB* job, wxWindow* aParent ) -> bool
166 {
167 return true;
168 } );
169 Register( "sym_diff", std::bind( &EESCHEMA_JOBS_HANDLER::JobSymDiff, this, std::placeholders::_1 ),
170 []( JOB* job, wxWindow* aParent ) -> bool
171 {
172 return true;
173 } );
174 Register( "erc", std::bind( &EESCHEMA_JOBS_HANDLER::JobSchErc, this, std::placeholders::_1 ),
175 []( JOB* job, wxWindow* aParent ) -> bool
176 {
177 JOB_SCH_ERC* ercJob = dynamic_cast<JOB_SCH_ERC*>( job );
178
179 wxCHECK( ercJob, false );
180
181 DIALOG_ERC_JOB_CONFIG dlg( aParent, ercJob );
182 return dlg.ShowModal() == wxID_OK;
183 } );
184 Register( "upgrade", std::bind( &EESCHEMA_JOBS_HANDLER::JobUpgrade, this, std::placeholders::_1 ),
185 []( JOB* job, wxWindow* aParent ) -> bool
186 {
187 return true;
188 } );
189 Register( "sch_import", std::bind( &EESCHEMA_JOBS_HANDLER::JobImport, this, std::placeholders::_1 ),
190 []( JOB* job, wxWindow* aParent ) -> bool
191 {
192 return true;
193 } );
194}
195
196
202
203
205{
206 SCHEMATIC* sch = nullptr;
207
208 if( !Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpenNotDummy() )
209 {
211 wxString schPath = aPath;
212
213 if( schPath.IsEmpty() )
214 {
215 wxFileName path = project.GetProjectFullName();
217 path.MakeAbsolute();
218 schPath = path.GetFullPath();
219 }
220
221 if( !m_cliSchematic )
222 m_cliSchematic = EESCHEMA_HELPERS::LoadSchematic( schPath, true, false, &project );
223
224 sch = m_cliSchematic;
225 }
226 else if( Pgm().IsGUI() && Pgm().GetSettingsManager().IsProjectOpen() )
227 {
228 SCH_EDIT_FRAME* editFrame = static_cast<SCH_EDIT_FRAME*>( m_kiway->Player( FRAME_SCH, false ) );
229
230 if( editFrame )
231 sch = &editFrame->Schematic();
232 }
233 else if( !aPath.IsEmpty() )
234 {
235 sch = EESCHEMA_HELPERS::LoadSchematic( aPath, true, false );
236 }
237
238 if( !sch )
239 m_reporter->Report( _( "Failed to load schematic\n" ), RPT_SEVERITY_ERROR );
240
241 return sch;
242}
243
244void EESCHEMA_JOBS_HANDLER::InitRenderSettings( SCH_RENDER_SETTINGS* aRenderSettings, const wxString& aTheme,
245 SCHEMATIC* aSch, const wxString& aDrawingSheetOverride )
246{
247 COLOR_SETTINGS* cs = ::GetColorSettings( aTheme );
248 aRenderSettings->LoadColors( cs );
249 aRenderSettings->m_ShowHiddenPins = false;
250 aRenderSettings->m_ShowHiddenFields = false;
251 aRenderSettings->m_ShowPinAltIcons = false;
252
253 aRenderSettings->SetDefaultPenWidth( aSch->Settings().m_DefaultLineWidth );
254 aRenderSettings->m_LabelSizeRatio = aSch->Settings().m_LabelSizeRatio;
255 aRenderSettings->m_TextOffsetRatio = aSch->Settings().m_TextOffsetRatio;
256 aRenderSettings->m_PinSymbolSize = aSch->Settings().m_PinSymbolSize;
257
258 aRenderSettings->SetDashLengthRatio( aSch->Settings().m_DashedLineDashRatio );
259 aRenderSettings->SetGapLengthRatio( aSch->Settings().m_DashedLineGapRatio );
260
261 // Load the drawing sheet from the filename stored in BASE_SCREEN::m_DrawingSheetFileName.
262 // If empty, or not existing, the default drawing sheet is loaded.
263
264 auto loadSheet = [&]( const wxString& path ) -> bool
265 {
266 wxString msg;
267 FILENAME_RESOLVER resolve;
268 resolve.SetProject( &aSch->Project() );
269 resolve.SetProgramBase( &Pgm() );
270
271 wxString absolutePath = resolve.ResolvePath( path, wxGetCwd(), { aSch->GetEmbeddedFiles() } );
272
273 if( !DS_DATA_MODEL::GetTheInstance().LoadDrawingSheet( absolutePath, &msg ) )
274 {
275 m_reporter->Report( wxString::Format( _( "Error loading drawing sheet '%s'." ), path ) + wxS( "\n" ) + msg
276 + wxS( "\n" ),
278 return false;
279 }
280
281 return true;
282 };
283
284 // try to load the override first
285 if( !aDrawingSheetOverride.IsEmpty() && loadSheet( aDrawingSheetOverride ) )
286 return;
287
288 // no override or failed override continues here
289 loadSheet( aSch->Settings().m_SchDrawingSheetFileName );
290}
291
292
294{
295 JOB_EXPORT_SCH_PLOT* aPlotJob = dynamic_cast<JOB_EXPORT_SCH_PLOT*>( aJob );
296
297 wxCHECK( aPlotJob, CLI::EXIT_CODES::ERR_UNKNOWN );
298
299 if( aPlotJob->m_plotFormat == SCH_PLOT_FORMAT::HPGL )
300 {
301 m_reporter->Report( _( "Plotting to HPGL is no longer supported as of KiCad 10.0.\n" ), RPT_SEVERITY_ERROR );
303 }
304
305 SCHEMATIC* sch = getSchematic( aPlotJob->m_filename );
306
307 if( !sch )
309
310 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
311 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
312
313 // Determine the variant to use. The dialog edit path writes m_variant (the scalar),
314 // while the CLI path populates m_variantNames directly. Prefer the scalar so a
315 // dialog-edited selection always wins over a stale list left over from CLI input.
316 wxString variantName;
317
318 if( !aPlotJob->m_variant.IsEmpty() )
319 variantName = aPlotJob->m_variant;
320 else if( !aPlotJob->m_variantNames.empty() )
321 variantName = aPlotJob->m_variantNames.front();
322
323 if( !variantName.IsEmpty() && variantName != wxS( "all" ) )
324 sch->SetCurrentVariant( variantName );
325
326 std::unique_ptr<SCH_RENDER_SETTINGS> renderSettings = std::make_unique<SCH_RENDER_SETTINGS>();
327 InitRenderSettings( renderSettings.get(), aPlotJob->m_theme, sch, aPlotJob->m_drawingSheet );
328
329 wxString font = aPlotJob->m_defaultFont;
330
331 if( font.IsEmpty() )
332 {
334 font = cfg ? cfg->m_Appearance.default_font : wxString( KICAD_FONT_NAME );
335 }
336
337 renderSettings->SetDefaultFont( font );
338 renderSettings->SetMinPenWidth( aPlotJob->m_minPenWidth );
339
340 // Clear cached bounding boxes for all text items so they're recomputed with the correct
341 // default font. This is necessary because text bounding boxes may have been cached during
342 // schematic loading before the render settings (and thus default font) were configured.
343 SCH_SCREENS screens( sch->Root() );
344
345 for( SCH_SCREEN* screen = screens.GetFirst(); screen; screen = screens.GetNext() )
346 {
347 for( SCH_ITEM* item : screen->Items() )
348 item->ClearCaches();
349
350 for( const auto& [libItemName, libSymbol] : screen->GetLibSymbols() )
351 libSymbol->ClearCaches();
352 }
353
354 std::unique_ptr<SCH_PLOTTER> schPlotter = std::make_unique<SCH_PLOTTER>( sch );
355
357
358 switch( aPlotJob->m_plotFormat )
359 {
360 case SCH_PLOT_FORMAT::DXF: format = PLOT_FORMAT::DXF; break;
361 case SCH_PLOT_FORMAT::PDF: format = PLOT_FORMAT::PDF; break;
362 case SCH_PLOT_FORMAT::SVG: format = PLOT_FORMAT::SVG; break;
363 case SCH_PLOT_FORMAT::POST: format = PLOT_FORMAT::POST; break;
364 case SCH_PLOT_FORMAT::PNG: format = PLOT_FORMAT::PNG; break;
365 case SCH_PLOT_FORMAT::HPGL: /* no longer supported */ break;
366 }
367
368 int pageSizeSelect = PageFormatReq::PAGE_SIZE_AUTO;
369
370 switch( aPlotJob->m_pageSizeSelect )
371 {
372 case JOB_PAGE_SIZE::PAGE_SIZE_A: pageSizeSelect = PageFormatReq::PAGE_SIZE_A; break;
373 case JOB_PAGE_SIZE::PAGE_SIZE_A4: pageSizeSelect = PageFormatReq::PAGE_SIZE_A4; break;
375 }
376
377 if( !aPlotJob->GetOutputPathIsDirectory() && aPlotJob->GetConfiguredOutputPath().IsEmpty() )
378 {
379 wxFileName fn = sch->GetFileName();
380 fn.SetName( fn.GetName() );
381 fn.SetExt( GetDefaultPlotExtension( format ) );
382
383 aPlotJob->SetConfiguredOutputPath( fn.GetFullName() );
384 }
385
386 wxString outPath = aPlotJob->GetFullOutputPath( &sch->Project() );
387
388 if( !PATHS::EnsurePathExists( outPath, !aPlotJob->GetOutputPathIsDirectory() ) )
389 {
390 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
392 }
393
394 SCH_PLOT_OPTS plotOpts;
395 plotOpts.m_blackAndWhite = aPlotJob->m_blackAndWhite;
396 plotOpts.m_PDFPropertyPopups = aPlotJob->m_PDFPropertyPopups;
398 plotOpts.m_PDFMetadata = aPlotJob->m_PDFMetadata;
399
400 if( aPlotJob->GetOutputPathIsDirectory() )
401 {
402 plotOpts.m_outputDirectory = outPath;
403 plotOpts.m_outputFile = wxEmptyString;
404 }
405 else
406 {
407 plotOpts.m_outputDirectory = wxEmptyString;
408 plotOpts.m_outputFile = outPath;
409 }
410
411 plotOpts.m_pageSizeSelect = pageSizeSelect;
412 plotOpts.m_plotAll = aPlotJob->m_plotAll;
413 plotOpts.m_plotDrawingSheet = aPlotJob->m_plotDrawingSheet;
414 plotOpts.m_plotPages = aPlotJob->m_plotPages;
415 plotOpts.m_theme = aPlotJob->m_theme;
416 plotOpts.m_useBackgroundColor = aPlotJob->m_useBackgroundColor;
417 plotOpts.m_plotHopOver = aPlotJob->m_show_hop_over;
418
419 if( !variantName.IsEmpty() )
420 plotOpts.m_variant = variantName;
421
422 // Always export dxf in mm by kicad-cli (similar to Pcbnew)
424
425 if( aPlotJob->m_plotFormat == SCH_PLOT_FORMAT::PNG )
426 {
427 JOB_EXPORT_SCH_PLOT_PNG* pngJob = static_cast<JOB_EXPORT_SCH_PLOT_PNG*>( aPlotJob );
428 plotOpts.m_pngDPI = pngJob->m_dpi;
429 plotOpts.m_pngAntialias = pngJob->m_antialias;
430 }
431
432 schPlotter->Plot( format, plotOpts, renderSettings.get(), m_reporter );
433
434 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
436
437 for( const wxString& outputPath : schPlotter->GetOutputFilePaths() )
438 aJob->AddOutput( outputPath );
439
440 return CLI::EXIT_CODES::OK;
441}
442
443
445{
446 JOB_EXPORT_SCH_NETLIST* aNetJob = dynamic_cast<JOB_EXPORT_SCH_NETLIST*>( aJob );
447
448 wxCHECK( aNetJob, CLI::EXIT_CODES::ERR_UNKNOWN );
449
450 SCHEMATIC* sch = getSchematic( aNetJob->m_filename );
451
452 if( !sch )
454
455 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
456 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
457
458 // Apply variant if specified
459 if( !aNetJob->m_variantNames.empty() )
460 {
461 // For netlist export, we use the first variant name from the set
462 wxString variantName = *aNetJob->m_variantNames.begin();
463
464 if( variantName != wxS( "all" ) )
465 sch->SetCurrentVariant( variantName );
466 }
467
468 // Annotation warning check
469 SCH_REFERENCE_LIST referenceList;
470 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_ALL );
471
472 if( referenceList.GetCount() > 0 )
473 {
474 if( referenceList.CheckAnnotation(
475 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
476 {
477 // We're only interested in the end result -- either errors or not
478 } )
479 > 0 )
480 {
481 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the "
482 "schematic editor to fix them\n" ),
484 }
485 }
486
487 // Test duplicate sheet names:
488 ERC_TESTER erc( sch );
489
490 if( erc.TestDuplicateSheetNames( false ) > 0 )
491 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
492
493 std::unique_ptr<NETLIST_EXPORTER_BASE> helper;
494 unsigned netlistOption = 0;
495
496 wxString fileExt;
497
498 switch( aNetJob->format )
499 {
502 helper = std::make_unique<NETLIST_EXPORTER_KICAD>( sch );
503 break;
504
507 helper = std::make_unique<NETLIST_EXPORTER_ORCADPCB2>( sch );
508 break;
509
512 helper = std::make_unique<NETLIST_EXPORTER_CADSTAR>( sch );
513 break;
514
518 helper = std::make_unique<NETLIST_EXPORTER_SPICE>( sch );
519 break;
520
523 helper = std::make_unique<NETLIST_EXPORTER_SPICE_MODEL>( sch );
524 break;
525
527 fileExt = wxS( "xml" );
528 helper = std::make_unique<NETLIST_EXPORTER_XML>( sch );
529 break;
530
532 fileExt = wxS( "asc" );
533 helper = std::make_unique<NETLIST_EXPORTER_PADS>( sch );
534 break;
535
537 fileExt = wxS( "txt" );
538 helper = std::make_unique<NETLIST_EXPORTER_ALLEGRO>( sch );
539 break;
540
541 default:
542 m_reporter->Report( _( "Unknown netlist format.\n" ), RPT_SEVERITY_ERROR );
544 }
545
546 if( aNetJob->GetConfiguredOutputPath().IsEmpty() )
547 {
548 wxFileName fn = sch->GetFileName();
549 fn.SetName( fn.GetName() );
550 fn.SetExt( fileExt );
551
552 aNetJob->SetConfiguredOutputPath( fn.GetFullName() );
553 }
554
555 wxString outPath = aNetJob->GetFullOutputPath( &sch->Project() );
556
557 if( !PATHS::EnsurePathExists( outPath, true ) )
558 {
559 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
561 }
562
563 bool res = helper->WriteNetlist( outPath, netlistOption, *m_reporter );
564
565 if( !res )
567
568 aJob->AddOutput( outPath );
569
570 return CLI::EXIT_CODES::OK;
571}
572
573
575{
576 JOB_EXPORT_SCH_BOM* aBomJob = dynamic_cast<JOB_EXPORT_SCH_BOM*>( aJob );
577
578 wxCHECK( aBomJob, CLI::EXIT_CODES::ERR_UNKNOWN );
579
580 SCHEMATIC* sch = getSchematic( aBomJob->m_filename );
581
582 if( !sch )
584
585 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
586 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
587
588 wxString currentVariant;
589
590 if( !aBomJob->m_variantNames.empty() )
591 {
592 currentVariant = aBomJob->m_variantNames.front();
593
594 if( currentVariant != wxS( "all" ) )
595 sch->SetCurrentVariant( currentVariant );
596 }
597
598 // Annotation warning check
599 SCH_REFERENCE_LIST referenceList;
600 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_NON_POWER, false );
601
602 if( referenceList.GetCount() > 0 )
603 {
604 SCH_REFERENCE_LIST copy = referenceList;
605
606 // Check annotation splits references...
607 if( copy.CheckAnnotation(
608 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
609 {
610 // We're only interested in the end result -- either errors or not
611 } )
612 > 0 )
613 {
614 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the schematic "
615 "editor to fix them\n" ),
617 }
618 }
619
620 // Test duplicate sheet names:
621 ERC_TESTER erc( sch );
622
623 if( erc.TestDuplicateSheetNames( false ) > 0 )
624 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
625
626 // Build our data model
627 FIELDS_EDITOR_GRID_DATA_MODEL dataModel( referenceList, nullptr );
628
629 // Mandatory fields first
630 for( FIELD_T fieldId : MANDATORY_FIELDS )
631 {
632 dataModel.AddColumn( GetCanonicalFieldName( fieldId ), GetDefaultFieldName( fieldId, DO_TRANSLATE ), false,
633 currentVariant );
634 }
635
636 // Generated/virtual fields (e.g. ${QUANTITY}, ${ITEM_NUMBER}) present only in the fields table
639 currentVariant );
642 currentVariant );
643
644 // Attribute fields (boolean flags on symbols)
645 dataModel.AddColumn( wxS( "${DNP}" ), GetGeneratedFieldDisplayName( wxS( "${DNP}" ) ), false, currentVariant );
646 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOM}" ) ),
647 false, currentVariant );
648 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOARD}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOARD}" ) ),
649 false, currentVariant );
650 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_SIM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_SIM}" ) ),
651 false, currentVariant );
652
653 // User field names in symbols second
654 std::set<wxString> userFieldNames;
655
656 for( size_t i = 0; i < referenceList.GetCount(); ++i )
657 {
658 SCH_SYMBOL* symbol = referenceList[i].GetSymbol();
659
660 for( SCH_FIELD& field : symbol->GetFields() )
661 {
662 if( !field.IsMandatory() && !field.IsPrivate() )
663 userFieldNames.insert( field.GetName() );
664 }
665 }
666
667 for( const wxString& fieldName : userFieldNames )
668 dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, currentVariant );
669
670 // Add any templateFieldNames which aren't already present in the userFieldNames
671 for( const TEMPLATE_FIELDNAME& templateFieldname : sch->Settings().m_TemplateFieldNames.GetTemplateFieldNames() )
672 {
673 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
674 {
675 dataModel.AddColumn( templateFieldname.m_Name, GetGeneratedFieldDisplayName( templateFieldname.m_Name ),
676 false, currentVariant );
677 }
678 }
679
680 BOM_PRESET preset;
681
682 // Load a preset if one is specified
683 if( !aBomJob->m_bomPresetName.IsEmpty() )
684 {
685 // Find the preset
686 const BOM_PRESET* schPreset = nullptr;
687
688 for( const BOM_PRESET& p : BOM_PRESET::BuiltInPresets() )
689 {
690 if( p.name == aBomJob->m_bomPresetName )
691 {
692 schPreset = &p;
693 break;
694 }
695 }
696
697 for( const BOM_PRESET& p : sch->Settings().m_BomPresets )
698 {
699 if( p.name == aBomJob->m_bomPresetName )
700 {
701 schPreset = &p;
702 break;
703 }
704 }
705
706 if( !schPreset )
707 {
708 m_reporter->Report(
709 wxString::Format( _( "BOM preset '%s' not found" ) + wxS( "\n" ), aBomJob->m_bomPresetName ),
711
713 }
714
715 preset = *schPreset;
716 }
717 else
718 {
719 // Normalize field names so that bare generated-field tokens (e.g. "QUANTITY") are
720 // accepted alongside the canonical "${QUANTITY}" form. Shell expansion of ${VAR}
721 // inside double quotes silently produces an empty string, so this also guards against
722 // that common CLI pitfall.
723 auto normalizeFieldName = [&dataModel]( const wxString& aName ) -> wxString
724 {
725 if( aName.IsEmpty() )
726 return wxEmptyString;
727
728 if( IsGeneratedField( aName ) )
729 return aName;
730
731 wxString wrapped = wxS( "${" ) + aName + wxS( "}" );
732
733 if( IsGeneratedField( wrapped ) && dataModel.GetFieldNameCol( wrapped ) != -1 )
734 return wrapped;
735
736 return aName;
737 };
738
739 size_t i = 0;
740
741 for( const wxString& rawFieldName : aBomJob->m_fieldsOrdered )
742 {
743 wxString fieldName = normalizeFieldName( rawFieldName );
744
745 if( fieldName.IsEmpty() )
746 {
747 i++;
748 continue;
749 }
750
751 // Handle wildcard. We allow the wildcard anywhere in the list, but it needs to respect
752 // fields that come before and after the wildcard.
753 if( fieldName == wxS( "*" ) )
754 {
755 for( const BOM_FIELD& modelField : dataModel.GetFieldsOrdered() )
756 {
757 struct BOM_FIELD field;
758
759 field.name = modelField.name;
760 field.show = true;
761 field.groupBy = false;
762 field.label = field.name;
763
764 bool fieldAlreadyPresent = false;
765
766 for( BOM_FIELD& presetField : preset.fieldsOrdered )
767 {
768 if( presetField.name == field.name )
769 {
770 fieldAlreadyPresent = true;
771 break;
772 }
773 }
774
775 bool fieldLaterInList = false;
776
777 for( const wxString& fieldInList : aBomJob->m_fieldsOrdered )
778 {
779 if( normalizeFieldName( fieldInList ) == field.name )
780 {
781 fieldLaterInList = true;
782 break;
783 }
784 }
785
786 if( !fieldAlreadyPresent && !fieldLaterInList )
787 preset.fieldsOrdered.emplace_back( field );
788 }
789
790 continue;
791 }
792
793 struct BOM_FIELD field;
794
795 field.name = fieldName;
796 field.show = !fieldName.StartsWith( wxT( "__" ), &field.name );
797
798 field.groupBy = alg::contains( aBomJob->m_fieldsGroupBy, field.name )
799 || alg::contains( aBomJob->m_fieldsGroupBy, rawFieldName );
800
801 if( ( aBomJob->m_fieldsLabels.size() > i ) && !aBomJob->m_fieldsLabels[i].IsEmpty() )
802 field.label = aBomJob->m_fieldsLabels[i];
803 else if( IsGeneratedField( field.name ) )
804 field.label = GetGeneratedFieldDisplayName( field.name );
805 else
806 field.label = field.name;
807
808 preset.fieldsOrdered.emplace_back( field );
809 i++;
810 }
811
812 preset.sortAsc = aBomJob->m_sortAsc;
813 preset.sortField = normalizeFieldName( aBomJob->m_sortField );
814 preset.filterString = aBomJob->m_filterString;
815 preset.groupSymbols = aBomJob->m_groupSymbols;
816 preset.excludeDNP = aBomJob->m_excludeDNP;
817 }
818
819 BOM_FMT_PRESET fmt;
820
821 // Load a format preset if one is specified
822 if( !aBomJob->m_bomFmtPresetName.IsEmpty() )
823 {
824 std::optional<BOM_FMT_PRESET> schFmtPreset;
825
827 {
828 if( p.name == aBomJob->m_bomFmtPresetName )
829 {
830 schFmtPreset = p;
831 break;
832 }
833 }
834
835 for( const BOM_FMT_PRESET& p : sch->Settings().m_BomFmtPresets )
836 {
837 if( p.name == aBomJob->m_bomFmtPresetName )
838 {
839 schFmtPreset = p;
840 break;
841 }
842 }
843
844 if( !schFmtPreset )
845 {
846 m_reporter->Report( wxString::Format( _( "BOM format preset '%s' not found" ) + wxS( "\n" ),
847 aBomJob->m_bomFmtPresetName ),
849
851 }
852
853 fmt = *schFmtPreset;
854 }
855 else
856 {
857 fmt.fieldDelimiter = aBomJob->m_fieldDelimiter;
858 fmt.stringDelimiter = aBomJob->m_stringDelimiter;
859 fmt.refDelimiter = aBomJob->m_refDelimiter;
861 fmt.keepTabs = aBomJob->m_keepTabs;
862 fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
863 }
864
865 if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
866 {
867 wxFileName fn = sch->GetFileName();
868 fn.SetName( fn.GetName() );
869 fn.SetExt( FILEEXT::CsvFileExtension );
870
871 aBomJob->SetConfiguredOutputPath( fn.GetFullName() );
872 }
873
874 wxString configuredPath = aBomJob->GetConfiguredOutputPath();
875 bool hasVariantPlaceholder = configuredPath.Contains( wxS( "${VARIANT}" ) );
876
877 // Determine which variants to process
878 std::vector<wxString> variantsToProcess;
879
880 if( aBomJob->m_variantNames.size() > 1 && hasVariantPlaceholder )
881 {
882 variantsToProcess = aBomJob->m_variantNames;
883 }
884 else
885 {
886 variantsToProcess.push_back( currentVariant );
887 }
888
889 for( const wxString& variantName : variantsToProcess )
890 {
891 std::vector<wxString> singleVariant = { variantName };
892 dataModel.SetVariantNames( singleVariant );
893 dataModel.SetCurrentVariant( variantName );
894 dataModel.ApplyBomPreset( preset, variantName );
895
896 wxString outPath;
897
898 if( hasVariantPlaceholder )
899 {
900 wxString variantPath = configuredPath;
901 variantPath.Replace( wxS( "${VARIANT}" ), variantName );
902 aBomJob->SetConfiguredOutputPath( variantPath );
903 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
904 aBomJob->SetConfiguredOutputPath( configuredPath );
905 }
906 else
907 {
908 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
909 }
910
911 if( !PATHS::EnsurePathExists( outPath, true ) )
912 {
913 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
915 }
916
917 wxFile f;
918
919 if( !f.Open( outPath, wxFile::write ) )
920 {
921 m_reporter->Report( wxString::Format( _( "Unable to open destination '%s'" ), outPath ),
923
925 }
926
927 bool res = f.Write( dataModel.Export( fmt ) );
928
929 if( !res )
931
932 aJob->AddOutput( outPath );
933
934 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
935 }
936
937 return CLI::EXIT_CODES::OK;
938}
939
940
942{
943 JOB_EXPORT_SCH_PYTHONBOM* aNetJob = dynamic_cast<JOB_EXPORT_SCH_PYTHONBOM*>( aJob );
944
945 wxCHECK( aNetJob, CLI::EXIT_CODES::ERR_UNKNOWN );
946
947 SCHEMATIC* sch = getSchematic( aNetJob->m_filename );
948
949 if( !sch )
951
952 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
953 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
954
955 // Annotation warning check
956 SCH_REFERENCE_LIST referenceList;
957 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_ALL );
958
959 if( referenceList.GetCount() > 0 )
960 {
961 if( referenceList.CheckAnnotation(
962 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
963 {
964 // We're only interested in the end result -- either errors or not
965 } )
966 > 0 )
967 {
968 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the "
969 "schematic editor to fix them\n" ),
971 }
972 }
973
974 // Test duplicate sheet names:
975 ERC_TESTER erc( sch );
976
977 if( erc.TestDuplicateSheetNames( false ) > 0 )
978 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
979
980 std::unique_ptr<NETLIST_EXPORTER_XML> xmlNetlist = std::make_unique<NETLIST_EXPORTER_XML>( sch );
981
982 if( aNetJob->GetConfiguredOutputPath().IsEmpty() )
983 {
984 wxFileName fn = sch->GetFileName();
985 fn.SetName( fn.GetName() + "-bom" );
986 fn.SetExt( FILEEXT::XmlFileExtension );
987
988 aNetJob->SetConfiguredOutputPath( fn.GetFullName() );
989 }
990
991 wxString outPath = aNetJob->GetFullOutputPath( &sch->Project() );
992
993 if( !PATHS::EnsurePathExists( outPath, true ) )
994 {
995 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
997 }
998
999 bool res = xmlNetlist->WriteNetlist( outPath, GNL_OPT_BOM, *m_reporter );
1000
1001 if( !res )
1003
1004 aJob->AddOutput( outPath );
1005
1006 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
1007
1008 return CLI::EXIT_CODES::OK;
1009}
1010
1011
1013 LIB_SYMBOL* symbol )
1014{
1015 wxCHECK( symbol, CLI::EXIT_CODES::ERR_UNKNOWN );
1016
1017 std::shared_ptr<LIB_SYMBOL> parent;
1018 LIB_SYMBOL* symbolToPlot = symbol;
1019
1020 // if the symbol is an alias, then the draw items are stored in the root symbol
1021 if( symbol->IsDerived() )
1022 {
1023 parent = symbol->GetRootSymbol();
1024
1025 wxCHECK( parent, CLI::EXIT_CODES::ERR_UNKNOWN );
1026
1027 symbolToPlot = parent.get();
1028 }
1029
1030 // iterate from unit 1, unit 0 would be "all units" which we don't want
1031 for( int unit = 1; unit < symbol->GetUnitCount() + 1; unit++ )
1032 {
1033 for( int bodyStyle = 1; bodyStyle <= symbol->GetBodyStyleCount(); ++bodyStyle )
1034 {
1035 wxString filename;
1036 wxFileName fn;
1037
1038 fn.SetPath( aSvgJob->m_outputDirectory );
1039 fn.SetExt( FILEEXT::SVGFileExtension );
1040
1041 filename = symbol->GetName();
1042
1043 for( wxChar c : wxFileName::GetForbiddenChars( wxPATH_DOS ) )
1044 filename.Replace( c, ' ' );
1045
1046 // Even single units get a unit number in the filename. This simplifies the
1047 // handling of the files as they have a uniform pattern.
1048 // Also avoids aliasing 'sym', unit 2 and 'sym_unit2', unit 1 to the same file.
1049 filename += wxString::Format( "_unit%d", unit );
1050
1051 if( symbol->HasDeMorganBodyStyles() )
1052 {
1053 if( bodyStyle == 2 )
1054 filename += wxS( "_demorgan" );
1055 }
1056 else if( bodyStyle <= (int) symbol->GetBodyStyleNames().size() )
1057 {
1058 filename += wxS( "_" ) + symbol->GetBodyStyleNames()[bodyStyle - 1].Lower();
1059 }
1060
1061 fn.SetName( filename );
1062 m_reporter->Report( wxString::Format( _( "Plotting symbol '%s' unit %d to '%s'\n" ), symbol->GetName(),
1063 unit, fn.GetFullPath() ),
1065
1066 // Get the symbol bounding box to fit the plot page to it
1067 BOX2I symbolBB = symbol->Flatten()->GetUnitBoundingBox( unit, bodyStyle, !aSvgJob->m_includeHiddenFields );
1068 PAGE_INFO pageInfo( PAGE_SIZE_TYPE::User );
1069 pageInfo.SetHeightMils( schIUScale.IUToMils( symbolBB.GetHeight() * 1.2 ) );
1070 pageInfo.SetWidthMils( schIUScale.IUToMils( symbolBB.GetWidth() * 1.2 ) );
1071
1072 SVG_PLOTTER* plotter = new SVG_PLOTTER();
1073 plotter->SetRenderSettings( aRenderSettings );
1074 plotter->SetPageSettings( pageInfo );
1075 plotter->SetColorMode( !aSvgJob->m_blackAndWhite );
1076
1077 VECTOR2I plot_offset = symbolBB.GetCenter();
1078 const double scale = 1.0;
1079
1080 // Currently, plot units are in decimal
1081 plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, scale, false );
1082
1083 plotter->SetCreator( wxT( "Eeschema-SVG" ) );
1084
1085 if( !plotter->OpenFile( fn.GetFullPath() ) )
1086 {
1087 m_reporter->Report(
1088 wxString::Format( _( "Unable to open destination '%s'" ) + wxS( "\n" ), fn.GetFullPath() ),
1090
1091 delete plotter;
1093 }
1094
1095 LOCALE_IO toggle;
1096 SCH_PLOT_OPTS plotOpts;
1097
1098 plotter->StartPlot( wxT( "1" ) );
1099
1100 bool background = true;
1101 VECTOR2I offset( pageInfo.GetWidthIU( schIUScale.IU_PER_MILS ) / 2,
1102 pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) / 2 );
1103
1104 // note, we want the fields from the original symbol pointer (in case of non-alias)
1105 symbolToPlot->Plot( plotter, background, plotOpts, unit, bodyStyle, offset, false );
1106 symbol->PlotFields( plotter, background, plotOpts, unit, bodyStyle, offset, false );
1107
1108 symbolToPlot->Plot( plotter, !background, plotOpts, unit, bodyStyle, offset, false );
1109 symbol->PlotFields( plotter, !background, plotOpts, unit, bodyStyle, offset, false );
1110
1111 plotter->EndPlot();
1112 delete plotter;
1113 }
1114 }
1115
1116 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
1118
1119 return CLI::EXIT_CODES::OK;
1120}
1121
1122
1124{
1125 JOB_SYM_EXPORT_SVG* svgJob = dynamic_cast<JOB_SYM_EXPORT_SVG*>( aJob );
1126
1127 wxCHECK( svgJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1128
1129 wxFileName fn( svgJob->m_libraryPath );
1130 fn.MakeAbsolute();
1131
1132 // When the input is a single symbol file we restrict plotting to the symbols defined in
1133 // that file. Stays empty (no restriction) when the input is a whole library.
1134 wxString singleFileFilter;
1135
1136 auto schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetFullPath() );
1137
1138 try
1139 {
1140 schLibrary->Load();
1141 }
1142 catch( ... )
1143 {
1144 // A single file holding a derived symbol whose parent is in a sibling file cannot load
1145 // alone. Retry against the enclosing directory, then plot only this file's symbols.
1146 bool recovered = false;
1147
1148 if( !fn.IsDir() && wxDir::Exists( fn.GetPath() ) )
1149 {
1150 try
1151 {
1152 schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetPath() );
1153 schLibrary->Load();
1154 singleFileFilter = fn.GetFullPath();
1155 recovered = true;
1156 }
1157 catch( ... )
1158 {
1159 // Fall through to the generic load error below.
1160 }
1161 }
1162
1163 if( !recovered )
1164 {
1165 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1167 }
1168 }
1169
1170 if( m_progressReporter )
1171 m_progressReporter->KeepRefreshing();
1172
1173 LIB_SYMBOL* symbol = nullptr;
1174
1175 if( !svgJob->m_symbol.IsEmpty() )
1176 {
1177 // See if the selected symbol exists
1178 symbol = schLibrary->GetSymbol( svgJob->m_symbol );
1179
1180 if( !symbol )
1181 {
1182 m_reporter->Report( _( "There is no symbol selected to save." ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
1184 }
1185 }
1186
1187 if( !svgJob->m_outputDirectory.IsEmpty() && !wxDir::Exists( svgJob->m_outputDirectory ) )
1188 {
1189 if( !wxFileName::Mkdir( svgJob->m_outputDirectory ) )
1190 {
1191 m_reporter->Report( wxString::Format( _( "Unable to create output directory '%s'." ) + wxS( "\n" ),
1192 svgJob->m_outputDirectory ),
1195 }
1196 }
1197
1198 SCH_RENDER_SETTINGS renderSettings;
1200 renderSettings.LoadColors( cs );
1201 renderSettings.SetDefaultPenWidth( DEFAULT_LINE_WIDTH_MILS * schIUScale.IU_PER_MILS );
1202 renderSettings.m_ShowHiddenPins = svgJob->m_includeHiddenPins;
1203 renderSettings.m_ShowHiddenFields = svgJob->m_includeHiddenFields;
1204
1205 int exitCode = CLI::EXIT_CODES::OK;
1206
1207 if( symbol )
1208 {
1209 exitCode = doSymExportSvg( svgJob, &renderSettings, symbol );
1210 }
1211 else
1212 {
1213 // Just plot all the symbols we can
1214 const LIB_SYMBOL_MAP& libSymMap = schLibrary->GetSymbolMap();
1215 const std::map<wxString, wxString>& sourceFiles = schLibrary->GetSymbolSourceFiles();
1216 const wxFileName filterFile( singleFileFilter );
1217
1218 for( const auto& [name, libSymbol] : libSymMap )
1219 {
1220 // When a single file was requested, skip symbols that came from sibling files.
1221 if( !singleFileFilter.IsEmpty() )
1222 {
1223 auto srcIt = sourceFiles.find( name );
1224
1225 if( srcIt == sourceFiles.end() || !wxFileName( srcIt->second ).SameAs( filterFile ) )
1226 continue;
1227 }
1228
1229 if( m_progressReporter )
1230 {
1231 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), name ) );
1232 m_progressReporter->KeepRefreshing();
1233 }
1234
1235 exitCode = doSymExportSvg( svgJob, &renderSettings, libSymbol );
1236
1237 if( exitCode != CLI::EXIT_CODES::OK )
1238 break;
1239 }
1240 }
1241
1242 return exitCode;
1243}
1244
1245
1247{
1248 JOB_SYM_UPGRADE* upgradeJob = dynamic_cast<JOB_SYM_UPGRADE*>( aJob );
1249
1250 wxCHECK( upgradeJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1251
1252 wxFileName fn( upgradeJob->m_libraryPath );
1253 fn.MakeAbsolute();
1254
1255 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
1256
1257 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1258 {
1259 if( wxFile::Exists( upgradeJob->m_outputLibraryPath ) )
1260 {
1261 m_reporter->Report( _( "Output path must not conflict with existing path\n" ), RPT_SEVERITY_ERROR );
1262
1264 }
1265 }
1266 else if( fileType != SCH_IO_MGR::SCH_KICAD )
1267 {
1268 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
1270
1272 }
1273
1274 if( fileType == SCH_IO_MGR::SCH_KICAD )
1275 {
1276 SCH_IO_KICAD_SEXPR_LIB_CACHE schLibrary( fn.GetFullPath() );
1277
1278 try
1279 {
1280 schLibrary.Load();
1281 }
1282 catch( ... )
1283 {
1284 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1286 }
1287
1288 if( m_progressReporter )
1289 m_progressReporter->KeepRefreshing();
1290
1291 bool shouldSave =
1293
1294 if( shouldSave )
1295 {
1296 m_reporter->Report( _( "Saving symbol library in updated format\n" ), RPT_SEVERITY_ACTION );
1297
1298 try
1299 {
1300 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1301 schLibrary.SetFileName( upgradeJob->m_outputLibraryPath );
1302
1303 schLibrary.SetModified();
1304 schLibrary.Save();
1305 }
1306 catch( ... )
1307 {
1308 m_reporter->Report( ( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
1310 }
1311 }
1312 else
1313 {
1314 m_reporter->Report( _( "Symbol library was not updated\n" ), RPT_SEVERITY_ERROR );
1315 }
1316 }
1317 else
1318 {
1319 if( !SCH_IO_MGR::ConvertLibrary( nullptr, fn.GetAbsolutePath(), upgradeJob->m_outputLibraryPath ) )
1320 {
1321 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
1323 }
1324 }
1325
1326 return CLI::EXIT_CODES::OK;
1327}
1328
1329
1331{
1332 JOB_SCH_ERC* ercJob = dynamic_cast<JOB_SCH_ERC*>( aJob );
1333
1334 wxCHECK( ercJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1335
1336 SCHEMATIC* sch = getSchematic( ercJob->m_filename );
1337
1338 if( !sch )
1340
1341 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
1342 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
1343
1344 if( ercJob->GetConfiguredOutputPath().IsEmpty() )
1345 {
1346 wxFileName fn = sch->GetFileName();
1347 fn.SetName( fn.GetName() + wxS( "-erc" ) );
1348
1350 fn.SetExt( FILEEXT::JsonFileExtension );
1351 else
1352 fn.SetExt( FILEEXT::ReportFileExtension );
1353
1354 // Use a transient working path so an empty configured output filename isn't persisted
1355 // back into the jobset file. Mirrors the PCB DRC handler.
1356 ercJob->SetWorkingOutputPath( fn.GetFullName() );
1357 }
1358
1359 wxString outPath = ercJob->GetFullOutputPath( &sch->Project() );
1360
1361 if( !PATHS::EnsurePathExists( outPath, true ) )
1362 {
1363 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1365 }
1366
1367 EDA_UNITS units;
1368
1369 switch( ercJob->m_units )
1370 {
1371 case JOB_SCH_ERC::UNITS::INCH: units = EDA_UNITS::INCH; break;
1372 case JOB_SCH_ERC::UNITS::MILS: units = EDA_UNITS::MILS; break;
1373 case JOB_SCH_ERC::UNITS::MM: units = EDA_UNITS::MM; break;
1374 default: units = EDA_UNITS::MM; break;
1375 }
1376
1377 std::shared_ptr<SHEETLIST_ERC_ITEMS_PROVIDER> markersProvider =
1378 std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( sch );
1379
1380 // Running ERC requires libraries be loaded, so make sure they have been
1382 adapter->AsyncLoad();
1383 adapter->BlockUntilLoaded();
1384
1385 ERC_TESTER ercTester( sch );
1386
1387 std::unique_ptr<DS_PROXY_VIEW_ITEM> drawingSheet( getDrawingSheetProxyView( sch ) );
1388 ercTester.RunTests( drawingSheet.get(), nullptr, m_kiway->KiFACE( KIWAY::FACE_CVPCB ), &sch->Project(),
1390
1391 markersProvider->SetSeverities( ercJob->m_severity );
1392
1393 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ), markersProvider->GetCount() ),
1395
1396 ERC_REPORT reportWriter( sch, units, markersProvider );
1397
1398 bool wroteReport = false;
1399
1401 wroteReport = reportWriter.WriteJsonReport( outPath );
1402 else
1403 wroteReport = reportWriter.WriteTextReport( outPath );
1404
1405 if( !wroteReport )
1406 {
1407 m_reporter->Report( wxString::Format( _( "Unable to save ERC report to %s\n" ), outPath ), RPT_SEVERITY_ERROR );
1409 }
1410
1411 m_reporter->Report( wxString::Format( _( "Saved ERC Report to %s\n" ), outPath ), RPT_SEVERITY_ACTION );
1412
1413 if( ercJob->m_exitCodeViolations )
1414 {
1415 if( markersProvider->GetCount() > 0 )
1417 }
1418
1420}
1421
1422
1424{
1425 JOB_SCH_UPGRADE* aUpgradeJob = dynamic_cast<JOB_SCH_UPGRADE*>( aJob );
1426
1427 if( aUpgradeJob == nullptr )
1429
1430 SCHEMATIC* sch = getSchematic( aUpgradeJob->m_filename );
1431
1432 if( !sch )
1434
1435 bool shouldSave = aUpgradeJob->m_force;
1436
1438 shouldSave = true;
1439
1440 if( !shouldSave )
1441 {
1442 m_reporter->Report( _( "Schematic file was not updated\n" ), RPT_SEVERITY_ERROR );
1444 }
1445
1446 // needs an absolute path
1447 wxFileName schPath( aUpgradeJob->m_filename );
1448 schPath.MakeAbsolute();
1449 const wxString schFullPath = schPath.GetFullPath();
1450
1451 try
1452 {
1453 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1454 SCH_SHEET* loadedSheet = pi->LoadSchematicFile( schFullPath, sch );
1455 pi->SaveSchematicFile( schFullPath, loadedSheet, sch );
1456 }
1457 catch( const IO_ERROR& ioe )
1458 {
1459 wxString msg =
1460 wxString::Format( _( "Error saving schematic file '%s'.\n%s" ), schFullPath, ioe.What().GetData() );
1461 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1463 }
1464
1465 m_reporter->Report( _( "Successfully saved schematic file using the latest format\n" ), RPT_SEVERITY_INFO );
1466
1468}
1469
1470
1472{
1473 JOB_SCH_IMPORT* job = dynamic_cast<JOB_SCH_IMPORT*>( aJob );
1474
1475 if( !job )
1477
1478 if( !wxFile::Exists( job->m_inputFile ) )
1479 {
1480 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ), job->m_inputFile ),
1483 }
1484
1485 // AUTO restricts autodetect to non-KiCad plugins so a native file is not re-imported.
1486 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_UNKNOWN;
1487
1488 switch( job->m_format )
1489 {
1492 break;
1493 case JOB_SCH_IMPORT::FORMAT::ALTIUM: fileType = SCH_IO_MGR::SCH_ALTIUM; break;
1494 case JOB_SCH_IMPORT::FORMAT::EAGLE: fileType = SCH_IO_MGR::SCH_EAGLE; break;
1495 case JOB_SCH_IMPORT::FORMAT::CADSTAR: fileType = SCH_IO_MGR::SCH_CADSTAR_ARCHIVE; break;
1496 case JOB_SCH_IMPORT::FORMAT::EASYEDA: fileType = SCH_IO_MGR::SCH_EASYEDA; break;
1497 case JOB_SCH_IMPORT::FORMAT::EASYEDAPRO: fileType = SCH_IO_MGR::SCH_EASYEDAPRO; break;
1498 case JOB_SCH_IMPORT::FORMAT::LTSPICE: fileType = SCH_IO_MGR::SCH_LTSPICE; break;
1499 case JOB_SCH_IMPORT::FORMAT::PADS: fileType = SCH_IO_MGR::SCH_PADS; break;
1500 case JOB_SCH_IMPORT::FORMAT::DIPTRACE: fileType = SCH_IO_MGR::SCH_DIPTRACE; break;
1501 }
1502
1503 if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1504 {
1505 // Quiet sentinel: lets the top-level `import` command treat the file as not-a-schematic.
1506 m_reporter->Report( wxString::Format( _( "No schematic importer recognizes the file format "
1507 "of '%s'\n" ),
1508 job->m_inputFile ),
1511 }
1512
1513 wxString outputPath = job->GetConfiguredOutputPath();
1514
1515 if( outputPath.IsEmpty() )
1517
1518 wxFileName inputFn( job->m_inputFile );
1519 inputFn.MakeAbsolute();
1520
1521 wxFileName outputFn( outputPath );
1522 outputFn.MakeAbsolute();
1523
1524 // Foreign importers resolve their symbol library against the *active* project, so an import
1525 // with no project loaded needs a transient active one at the output location (never written to
1526 // disk; LoadProject returns false yet still registers it, hence the GetProject() check).
1528 PROJECT* projectPtr = nullptr;
1529 bool createdTransientProject = false;
1530
1531 if( mgr.IsProjectOpenNotDummy() )
1532 {
1533 projectPtr = &mgr.Prj();
1534 }
1535 else
1536 {
1537 wxFileName projectFn( outputFn );
1538 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1539
1540 mgr.LoadProject( projectFn.GetFullPath(), true );
1541 projectPtr = mgr.GetProject( projectFn.GetFullPath() );
1542 createdTransientProject = ( projectPtr != nullptr );
1543 }
1544
1545 if( !projectPtr )
1546 {
1547 m_reporter->Report( _( "Could not establish a project for the import\n" ),
1550 }
1551
1552 PROJECT& project = *projectPtr;
1553
1554 // Declared before the SCHEMATIC so reverse-destruction tears the schematic (which references
1555 // the project) down first; unloads the transient project on every exit path.
1556 struct TRANSIENT_PROJECT_GUARD
1557 {
1558 SETTINGS_MANAGER& m_mgr;
1559 PROJECT* m_project;
1560 bool m_active;
1561
1562 ~TRANSIENT_PROJECT_GUARD()
1563 {
1564 if( m_active )
1565 m_mgr.UnloadProject( m_project, false );
1566 }
1567 } transientProjectGuard{ mgr, projectPtr, createdTransientProject };
1568
1570
1571 std::unique_ptr<SCHEMATIC> schematic = std::make_unique<SCHEMATIC>( &project );
1572
1573 wxString formatName = SCH_IO_MGR::ShowType( fileType );
1574 SCH_SHEET* loadedSheet = nullptr;
1575
1576 try
1577 {
1578 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
1579
1580 if( !pi )
1581 {
1582 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
1583 formatName ),
1586 }
1587
1588 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
1589 inputFn.GetFullPath(), formatName ),
1591
1592 loadedSheet = pi->LoadSchematicFile( inputFn.GetFullPath(), schematic.get() );
1593
1594 if( !loadedSheet )
1595 {
1596 m_reporter->Report( _( "Failed to load schematic\n" ), RPT_SEVERITY_ERROR );
1598 }
1599 }
1600 catch( const IO_ERROR& ioe )
1601 {
1602 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
1605 }
1606
1607 size_t symbolCount = 0;
1608 size_t sheetCount = 0;
1609
1610 try
1611 {
1612 // Some importers build the top-level sheet set themselves; only collapse to the returned
1613 // sheet otherwise (mirrors SCH_EDIT_FRAME::importFile()).
1614 std::vector<SCH_SHEET*> topLevelSheets = schematic->GetTopLevelSheets();
1615 bool loadedIsTopLevel = std::find( topLevelSheets.begin(), topLevelSheets.end(),
1616 loadedSheet ) != topLevelSheets.end();
1617 bool loadedIsVirtualRoot = loadedSheet == &schematic->Root()
1618 || loadedSheet->IsVirtualRootSheet();
1619
1620 if( !loadedIsTopLevel && !loadedIsVirtualRoot )
1621 schematic->SetTopLevelSheets( { loadedSheet } );
1622
1623 // Recompute connectivity so instance data is valid before saving, as importFile() does.
1624 std::unique_ptr<TOOL_MANAGER> toolManager = std::make_unique<TOOL_MANAGER>();
1625 toolManager->SetEnvironment( schematic.get(), nullptr, nullptr, Kiface().KifaceSettings(),
1626 nullptr );
1627
1628 {
1629 SCH_COMMIT dummyCommit( toolManager.get() );
1630 schematic->RecalculateConnections( &dummyCommit, GLOBAL_CLEANUP, toolManager.get() );
1631 }
1632
1633 schematic->SetSheetNumberAndCount();
1634
1635 if( SCH_SHEET* topSheet = schematic->GetTopLevelSheet() )
1636 topSheet->SetFileName( outputFn.GetFullName() );
1637
1638 schematic->RootScreen()->SetFileName( outputFn.GetFullPath() );
1639
1640 SCH_SCREENS screens( schematic->Root() );
1641
1642 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
1643 filenameMap[schematic->RootScreen()] = outputFn.GetFullPath();
1644
1645 wxString errorMsg;
1646
1647 if( !PrepareSaveAsFiles( *schematic, screens, inputFn, outputFn, /*aSaveCopy*/ true,
1648 /*aCopySubsheets*/ true, /*aIncludeExternSheets*/ true,
1649 filenameMap, errorMsg ) )
1650 {
1651 m_reporter->Report( errorMsg + wxS( "\n" ), RPT_SEVERITY_ERROR );
1653 }
1654
1655 // PrepareSaveAsFiles seeds an entry (empty for sheets it does not relocate) for every
1656 // screen; empty paths are skipped.
1657 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1658
1659 for( size_t i = 0; i < screens.GetCount(); i++ )
1660 {
1661 SCH_SCREEN* screen = screens.GetScreen( i );
1662 wxString path = filenameMap[screen];
1663
1664 if( path.IsEmpty() )
1665 continue;
1666
1667 wxFileName fn( path );
1669
1670 pi->SaveSchematicFile( fn.GetFullPath(), screens.GetSheet( i ), schematic.get() );
1671 sheetCount++;
1672
1673 auto symbols = screen->Items().OfType( SCH_SYMBOL_T );
1674 symbolCount += std::distance( symbols.begin(), symbols.end() );
1675 }
1676 }
1677 catch( const IO_ERROR& ioe )
1678 {
1679 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1680 ioe.What() ),
1683 }
1684 catch( const std::exception& exc )
1685 {
1686 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1687 exc.what() ),
1690 }
1691
1692 m_reporter->Report( wxString::Format( _( "Successfully saved imported schematic to '%s'\n" ),
1693 outputFn.GetFullPath() ),
1695
1696 // Linked by the top-level `import` command's subsequent SaveProject().
1697 if( Pgm().GetSettingsManager().IsProjectOpenNotDummy() )
1698 {
1699 std::vector<FILE_INFO_PAIR>& projectSheets = project.GetProjectFile().GetSheets();
1700 projectSheets.clear();
1701
1702 for( const SCH_SHEET_PATH& sheetPath : schematic->Hierarchy() )
1703 {
1704 SCH_SHEET* sheet = sheetPath.Last();
1705
1706 if( sheet && !sheet->IsVirtualRootSheet() )
1707 projectSheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1708 }
1709 }
1710
1712 {
1713 IMPORT_REPORT_DATA reportData;
1714
1715 reportData.m_sourceFile = inputFn.GetFullName();
1716 reportData.m_sourceFormat = formatName;
1717 reportData.m_outputFile = outputFn.GetFullName();
1718 reportData.m_statistics = {
1719 { wxS( "symbols" ), symbolCount },
1720 { wxS( "sheets" ), sheetCount }
1721 };
1722
1723 WriteImportReport( m_reporter, job->m_reportFormat, job->m_reportFile, reportData );
1724 }
1725
1727}
1728
1729
1731{
1732 DS_PROXY_VIEW_ITEM* drawingSheet =
1734 &aSch->RootScreen()->GetTitleBlock(), aSch->GetProperties() );
1735
1736 drawingSheet->SetPageNumber( TO_UTF8( aSch->RootScreen()->GetPageNumber() ) );
1737 drawingSheet->SetSheetCount( aSch->RootScreen()->GetPageCount() );
1738 drawingSheet->SetFileName( TO_UTF8( aSch->RootScreen()->GetFileName() ) );
1741 drawingSheet->SetIsFirstPage( aSch->RootScreen()->GetVirtualPageNumber() == 1 );
1742
1743 wxString currentVariant = aSch->GetCurrentVariant();
1744 wxString variantDesc = aSch->GetVariantDescription( currentVariant );
1745 drawingSheet->SetVariantName( TO_UTF8( currentVariant ) );
1746 drawingSheet->SetVariantDesc( TO_UTF8( variantDesc ) );
1747
1748 drawingSheet->SetSheetName( "" );
1749 drawingSheet->SetSheetPath( "" );
1750
1751 return drawingSheet;
1752}
1753
1754
1755// ============================================================================
1756// JobSchDiff: sch_diff implementation
1757// ============================================================================
1760#include <diff_merge/diff_scene.h>
1761#include <diff_merge/sch_differ.h>
1765#include <project/project_file.h>
1767#include <jobs/job_sch_diff.h>
1768#include <jobs/scratch_doc.h>
1769#include <wx/file.h>
1770
1771
1772// Load a schematic into a SCRATCH_DOC<SCHEMATIC> that keeps a dedicated scratch
1773// PROJECT attached for the document's lifetime. Without a per-document project,
1774// a second LoadProject(path, true) destroys the first project and the first
1775// schematic's m_project dangles. The destructor severs the link via
1776// SetProject( nullptr ). Shared by every SCH diff/merge job.
1778{
1780 aMgr, aPath,
1781 [aPath]( PROJECT* aProject )
1782 {
1783 return std::unique_ptr<SCHEMATIC>(
1785 /*aSetActive=*/false,
1786 /*aForceDefaultProject=*/false, aProject,
1787 /*aCalculateConnectivity=*/false ) );
1788 },
1789 []( SCHEMATIC* aSch )
1790 {
1791 aSch->SetProject( nullptr );
1792 } );
1793}
1794
1795
1797{
1798 JOB_SCH_DIFF* diffJob = dynamic_cast<JOB_SCH_DIFF*>( aJob );
1799
1800 if( !diffJob )
1802
1803 // Two schematics in the same SettingsManager need scratch PROJECTs;
1804 // otherwise the second LoadProject(path, true) destroys the first
1805 // project and the first schematic's m_project dangles, crashing on
1806 // any per-instance bbox / field / reference resolution (e.g. inside
1807 // SCH_DIFFER's makeDescriptor calling SCH_SYMBOL::GetRef).
1809
1812
1813 if( !a.doc )
1814 {
1815 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputA ), RPT_SEVERITY_ERROR );
1817 }
1818
1819 if( !b.doc )
1820 {
1821 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputB ), RPT_SEVERITY_ERROR );
1823 }
1824
1825 SCHEMATIC* schA = a.doc.get();
1826 SCHEMATIC* schB = b.doc.get();
1827
1828 KICAD_DIFF::SCH_DIFFER differ( schA, schB, diffJob->m_inputB );
1830
1831 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1832
1833 if( diffJob->m_exitCodeOnly )
1834 return diffExitCode;
1835
1836 // The schematic geometry (wires, junctions, symbol/sheet/label bbox
1837 // outlines) renders beneath the change rectangles for PNG/SVG, matching
1838 // the interactive dialog.
1840 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1842 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1843 { return KICAD_DIFF::ExtractSchematicGeometry( *schA, aColor ); };
1844 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1845 { return KICAD_DIFF::ExtractSchematicGeometry( *schB, aColor ); };
1846
1847 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
1848}
1849
1850
1851// ============================================================================
1852// JobSymDiff: sym_diff implementation
1853// ============================================================================
1855#include <jobs/job_sym_diff.h>
1856
1857
1858// Load one side of a symbol-library diff into its owner vector and name map.
1859// When aAllowEmpty is set an empty path resolves to a clean (empty) side; the
1860// non-interactive job path leaves it unset so a missing path is an input error.
1861static int loadSymbolLibrarySide( const wxString& aPath,
1862 std::vector<std::unique_ptr<LIB_SYMBOL>>& aOwners,
1863 KICAD_DIFF::SYM_LIB_DIFFER::SYMBOL_MAP& aMap, bool aAllowEmpty,
1864 REPORTER& aReporter )
1865{
1866 if( aAllowEmpty && aPath.IsEmpty() )
1868
1869 try
1870 {
1871 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
1872 aOwners = std::move( loaded.first );
1873 aMap = std::move( loaded.second );
1875 }
1876 catch( const IO_ERROR& ioe )
1877 {
1878 aReporter.Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
1880 }
1881 catch( const std::exception& e )
1882 {
1883 aReporter.Report(
1884 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
1886 }
1887
1889}
1890
1891
1892// Flatten a symbol-library name map into a single DOCUMENT_GEOMETRY tinted with
1893// the supplied per-side theme colour.
1896{
1898
1899 for( const auto& [name, symbol] : aMap )
1900 {
1901 if( symbol )
1902 KICAD_DIFF::AppendGeometry( geometry, KICAD_DIFF::ExtractSymbolGeometry( *symbol, aColor ) );
1903 }
1904
1905 return geometry;
1906}
1907
1908
1910{
1911 JOB_SYM_DIFF* diffJob = dynamic_cast<JOB_SYM_DIFF*>( aJob );
1912
1913 if( !diffJob )
1915
1916 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
1917 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
1920
1921 if( int rc = loadSymbolLibrarySide( diffJob->m_inputA, ownersA, mapA, false, *m_reporter );
1923 {
1924 return rc;
1925 }
1926
1927 if( int rc = loadSymbolLibrarySide( diffJob->m_inputB, ownersB, mapB, false, *m_reporter );
1929 {
1930 return rc;
1931 }
1932
1933 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, diffJob->m_inputB );
1935
1936 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1937
1938 if( diffJob->m_exitCodeOnly )
1939 return diffExitCode;
1940
1942 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1944 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1945 { return symbolLibraryGeometry( mapA, aColor ); };
1946 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1947 { return symbolLibraryGeometry( mapB, aColor ); };
1948
1949 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
1950}
1951
1952
1953// ============================================================================
1954// JobOpenDiffDialog: load two on-disk files and open DIALOG_KICAD_DIFF.
1955// Dispatched from the project manager / PR-review dialog via KIWAY.
1956// ============================================================================
1960#include <jobs/scratch_doc.h>
1961
1962
1964 const wxString& aFileB, const wxString& aLabelA,
1965 const wxString& aLabelB, wxWindow* aParent,
1966 REPORTER* aReporter )
1967{
1968 // Restore m_reporter on scope exit so a caller's transient (often
1969 // stack-local) reporter doesn't outlive this call as a dangling member.
1971 aReporter ? aReporter : m_reporter );
1972
1973 wxWindow* parent = aParent ? aParent : ( wxTheApp ? wxTheApp->GetTopWindow() : nullptr );
1974
1977 KICAD_DIFF::DOCUMENT_GEOMETRY compGeometry;
1978
1979 switch( aKind )
1980 {
1982 {
1984
1987
1988 if( !a.doc && !aFileA.IsEmpty() )
1989 {
1990 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileA ), RPT_SEVERITY_ERROR );
1992 }
1993
1994 if( !b.doc && !aFileB.IsEmpty() )
1995 {
1996 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileB ), RPT_SEVERITY_ERROR );
1998 }
1999
2000 // Synthesize empty SCHEMATICs against scratch PROJECTs for any
2001 // missing side so SCH_DIFFER sees a valid empty document.
2002 PROJECT scratchPrjA;
2003 PROJECT scratchPrjB;
2004 std::unique_ptr<SCHEMATIC> emptyA;
2005 std::unique_ptr<SCHEMATIC> emptyB;
2006
2007 if( !a.doc )
2008 {
2009 emptyA = std::make_unique<SCHEMATIC>( &scratchPrjA );
2010 emptyA->CreateDefaultScreens();
2011 }
2012
2013 if( !b.doc )
2014 {
2015 emptyB = std::make_unique<SCHEMATIC>( &scratchPrjB );
2016 emptyB->CreateDefaultScreens();
2017 }
2018
2019 SCHEMATIC* schA = a.doc ? a.doc.get() : emptyA.get();
2020 SCHEMATIC* schB = b.doc ? b.doc.get() : emptyB.get();
2021
2022 KICAD_DIFF::SCH_DIFFER differ( schA, schB, aFileB );
2023 result = differ.Diff();
2024
2025 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2026 refGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schA, theme.reference );
2027 compGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schB, theme.comparison );
2028
2029 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2030 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2031
2033 parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ),
2034 [schA, schB, color = theme.reference]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
2035 {
2036 SCH_SCREEN* refScreen = schA ? schA->RootScreen() : nullptr;
2037 SCH_SCREEN* compScreen = schB ? schB->RootScreen() : nullptr;
2038
2039 if( !aSheetPath.empty() )
2040 {
2041 if( schA )
2042 {
2043 if( auto sp = schA->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2044 refScreen = sp->LastScreen();
2045 }
2046
2047 if( schB )
2048 {
2049 if( auto sp = schB->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2050 compScreen = sp->LastScreen();
2051 }
2052 }
2053
2054 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, schA, schB, color, {}, {}, {}, refScreen,
2055 compScreen );
2056 } );
2057 dlg.ShowModal();
2058
2059 if( emptyA )
2060 emptyA->SetProject( nullptr );
2061
2062 if( emptyB )
2063 emptyB->SetProject( nullptr );
2064
2066 }
2068 {
2069 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
2070 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
2073
2074 if( int rc = loadSymbolLibrarySide( aFileA, ownersA, mapA, true, *m_reporter );
2076 {
2077 return rc;
2078 }
2079
2080 if( int rc = loadSymbolLibrarySide( aFileB, ownersB, mapB, true, *m_reporter );
2082 {
2083 return rc;
2084 }
2085
2086 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, aFileB );
2087 result = differ.Diff();
2088
2089 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2090 refGeometry = symbolLibraryGeometry( mapA, theme.reference );
2091 compGeometry = symbolLibraryGeometry( mapB, theme.comparison );
2092 break;
2093 }
2094 default:
2095 m_reporter->Report( _( "Unsupported document kind for this dispatcher.\n" ), RPT_SEVERITY_ERROR );
2097 }
2098
2099 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2100 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2101
2102 DIALOG_KICAD_DIFF dlg( parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ) );
2103 dlg.ShowModal();
2104
2106}
2107
2108
2109// ============================================================================
2110// JobSchMerge: sch_merge implementation
2111// ============================================================================
2114#include <jobs/scratch_doc.h>
2115
2116
2117int EESCHEMA_JOBS_HANDLER::RunMerge( KICAD_DIFF::DOC_KIND aKind, const wxString& aAncestor,
2118 const wxString& aOurs, const wxString& aTheirs,
2119 const wxString& aOutput, bool aInteractive, bool aSingleFile,
2120 REPORTER* aReporter )
2121{
2122 // Restore m_reporter on scope exit so a caller's transient (often
2123 // stack-local) reporter doesn't outlive this call as a dangling member.
2125 aReporter ? aReporter : m_reporter );
2126
2127 if( aKind == KICAD_DIFF::DOC_KIND::SYM_LIB )
2128 return runSymLibMerge( aAncestor, aOurs, aTheirs, aOutput );
2129
2130 return runSchMerge( aAncestor, aOurs, aTheirs, aOutput, aInteractive );
2131}
2132
2133
2134int EESCHEMA_JOBS_HANDLER::runSchMerge( const wxString& aAncestor, const wxString& aOurs,
2135 const wxString& aTheirs, const wxString& aOutput,
2136 bool aInteractive )
2137{
2139
2140 SCRATCH_DOC<SCHEMATIC> ancestor = loadScratchSchematic( mgr, aAncestor );
2141 SCRATCH_DOC<SCHEMATIC> ours = loadScratchSchematic( mgr, aOurs );
2142 SCRATCH_DOC<SCHEMATIC> theirs = loadScratchSchematic( mgr, aTheirs );
2143
2144 if( !ancestor.doc || !ours.doc || !theirs.doc )
2145 {
2146 m_reporter->Report( _( "Failed to load one or more input schematics\n" ), RPT_SEVERITY_ERROR );
2148 }
2149
2150 // Multi-sheet hierarchies are supported: each non-root sub-sheet is
2151 // written alongside the output root using its original basename. Top-level
2152 // sheets stay singular — multiple roots is an editor invariant the diff
2153 // engine never models, so refuse those.
2154 auto hasSingleRoot = []( const SCHEMATIC* aSch )
2155 {
2156 return aSch->GetTopLevelSheets().size() == 1;
2157 };
2158
2159 if( !hasSingleRoot( ancestor.doc.get() ) || !hasSingleRoot( ours.doc.get() ) || !hasSingleRoot( theirs.doc.get() ) )
2160 {
2161 m_reporter->Report( _( "sch merge requires each input to have a single top-level sheet\n" ),
2164 }
2165
2166 KICAD_DIFF::SCH_DIFFER ourDiff( ancestor.doc.get(), ours.doc.get() );
2167 KICAD_DIFF::SCH_DIFFER theirDiff( ancestor.doc.get(), theirs.doc.get() );
2168
2169 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2170 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2171
2173 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2174
2175 // A cancelled dialog leaves plan unresolved and falls through to the
2176 // marker flow below.
2177 if( aInteractive && !plan.Resolved() )
2178 {
2179 if( !Pgm().IsGUI() )
2180 {
2181 m_reporter->Report( _( "--interactive requires a GUI KiCad process; the console "
2182 "kicad-cli cannot open dialogs.\n" ),
2185 }
2186
2187 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2189
2190 if( ancestor.doc )
2192
2193 if( ours.doc )
2195
2196 if( theirs.doc )
2198
2200 KICAD_DIFF::CollectChangeBBoxes( theirDocDiff, ctx.theirsBBoxes );
2201
2202 DIALOG_KICAD_MERGE_3WAY dlg( wxTheApp->GetTopWindow(), plan, std::move( ctx ) );
2203
2204 if( dlg.ShowModal() == wxID_APPLY )
2205 plan = dlg.GetResolvedPlan();
2206 }
2207
2208 // Snapshot of the plan before the applier moves it; drives the
2209 // unresolved-conflict report below.
2210 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2211
2212 KICAD_DIFF::SCH_MERGE_APPLIER applier( ancestor.doc.get(), ours.doc.get(), theirs.doc.get(), std::move( plan ) );
2213
2214 if( !applier.Apply() )
2215 {
2216 m_reporter->Report( _( "Merge applier failed to produce a schematic\n" ), RPT_SEVERITY_ERROR );
2218 }
2219
2220 // Sheet add/remove/replace resolutions are explicitly skipped by
2221 // SCH_MERGE_APPLIER (see isSheetItem); succeeding here would silently
2222 // drop hierarchy edits.
2223 if( applier.GetReport().sheetActionsSkipped > 0 )
2224 {
2225 m_reporter->Report( _( "Merge contains hierarchical sheet structure changes that sch merge "
2226 "cannot apply\n" ),
2229 }
2230
2231 // Refusal above guarantees a single top-level sheet.
2232 SCH_SHEET* rootSheet = ancestor.doc->GetTopLevelSheet( 0 );
2233
2234 wxFileName outFn( aOutput );
2235 outFn.MakeAbsolute();
2236
2237 // Sub-sheets land alongside the root by basename. Preserving the original
2238 // relative-path subdirectory structure would force kicad-cli sch merge to
2239 // mkdir into user space; the basename-flat scheme is what makes the common
2240 // git-mergetool case work without surprises.
2241 const wxString outDir = outFn.GetPath();
2242
2243 SCH_SCREENS screens( rootSheet );
2244 SCH_SCREEN* rootScreen = rootSheet->GetScreen();
2245
2246 // Detect two sub-sheets sharing a basename (e.g., a/foo.kicad_sch and
2247 // b/foo.kicad_sch) before any I/O — the flat output layout can't honor
2248 // both, and silently overwriting one is the worst outcome.
2249 std::map<wxString, SCH_SCREEN*> basenameOwner;
2250
2251 for( size_t i = 0; i < screens.GetCount(); ++i )
2252 {
2253 SCH_SCREEN* screen = screens.GetScreen( i );
2254
2255 if( !screen || screen == rootScreen )
2256 continue;
2257
2258 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2259
2260 if( basename.IsEmpty() )
2261 continue;
2262
2263 auto [it, inserted] = basenameOwner.emplace( basename, screen );
2264
2265 if( !inserted && it->second != screen )
2266 {
2267 m_reporter->Report( wxString::Format( _( "Cannot flatten sub-sheets with duplicate "
2268 "basename '%s'\n" ),
2269 basename ),
2272 }
2273 }
2274
2275 // Rewrite every SCH_SHEET symbol's filename field to its child screen's
2276 // basename, so the root file (and any intermediate sheet) references the
2277 // flattened layout we're about to write.
2278 for( size_t i = 0; i < screens.GetCount(); ++i )
2279 {
2280 SCH_SCREEN* parent = screens.GetScreen( i );
2281
2282 if( !parent )
2283 continue;
2284
2285 for( SCH_ITEM* item : parent->Items().OfType( SCH_SHEET_T ) )
2286 {
2287 SCH_SHEET* childRef = static_cast<SCH_SHEET*>( item );
2288 SCH_SCREEN* childScreen = childRef->GetScreen();
2289
2290 if( !childScreen || childScreen == rootScreen )
2291 continue;
2292
2293 const wxString basename = wxFileName( childScreen->GetFileName() ).GetFullName();
2294
2295 if( !basename.IsEmpty() )
2296 childRef->SetFileName( basename );
2297 }
2298 }
2299
2300 try
2301 {
2302 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2303 pi->SaveSchematicFile( outFn.GetFullPath(), rootSheet, ancestor.doc.get() );
2304
2305 for( size_t i = 0; i < screens.GetCount(); ++i )
2306 {
2307 SCH_SCREEN* screen = screens.GetScreen( i );
2308 SCH_SHEET* sheet = screens.GetSheet( i );
2309
2310 if( !screen || !sheet || screen == rootScreen )
2311 continue;
2312
2313 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2314
2315 if( basename.IsEmpty() )
2316 continue;
2317
2318 wxFileName outSubFn( outDir, basename );
2319 pi->SaveSchematicFile( outSubFn.GetFullPath(), sheet, ancestor.doc.get() );
2320 }
2321 }
2322 catch( const IO_ERROR& ioe )
2323 {
2324 m_reporter->Report( wxString::Format( _( "Failed to save merged schematic: %s\n" ), ioe.What() ),
2327 }
2328
2329 // If the applier mutated project-file-scoped state (ERC severities, etc),
2330 // persist it as a sibling .kicad_pro alongside the .kicad_sch output —
2331 // otherwise the resolution dies with the process. Only write when actually
2332 // needed; clobbering an existing .kicad_pro with ancestor's project
2333 // would lose unrelated user settings (library tables, mru paths).
2334 if( applier.GetReport().projectFileTouched && ancestor.project )
2335 {
2336 wxFileName proFn = outFn;
2337 proFn.SetExt( FILEEXT::ProjectFileExtension );
2338
2339 // JSON-patch path: only the diffed DOC_PROP fields are written into
2340 // the output .kicad_pro, so any non-diffed user customisations
2341 // (library tables, last paths, layer presets, text variables) are
2342 // preserved. Fall back to SaveProjectCopy on parse failure.
2343 PROJECT_FILE& ancProj = ancestor.project->GetProjectFile();
2344 ancProj.Store();
2345
2346 const KICAD_DIFF::SCH_MERGE_APPLIER::REPORT& mergeReport = applier.GetReport();
2347
2348 // PROJECT_FILE::Store() flushes the project file's own params but not
2349 // its registered NESTED_SETTINGS. Flush only the resolved nested
2350 // settings so Internals() reflects the merge result without touching
2351 // unrelated project subtrees.
2352 if( mergeReport.ercSeveritiesTouched && ancestor.doc )
2353 ancestor.doc->ErcSettings().SaveToFile( wxEmptyString, true );
2354
2355 if( mergeReport.drawingSheetFileTouched && ancestor.doc )
2356 ancestor.doc->Settings().SaveToFile( wxEmptyString, true );
2357
2358 std::set<wxString> touched;
2359 if( mergeReport.ercSeveritiesTouched )
2360 touched.insert( KICAD_DIFF::DOC_PROP_ERC_SEVERITIES );
2361
2362 if( mergeReport.drawingSheetFileTouched )
2363 touched.insert( KICAD_DIFF::DOC_PROP_DRAWING_SHEET );
2364
2365 if( !KICAD_DIFF::ApplyProjectFilePatches( proFn.GetFullPath(), *ancProj.Internals(), touched,
2367 {
2368 if( !Pgm().GetSettingsManager().SaveProjectCopy( proFn.GetFullPath(), ancestor.project ) )
2369 {
2370 m_reporter->Report(
2371 wxString::Format( _( "Failed to save merged project file: %s\n" ), proFn.GetFullPath() ),
2374 }
2375 }
2376 }
2377
2378 // Surface post-apply validator findings (refdes collisions, schema
2379 // mismatch, missed connectivity rebuild). Advisory — they do not change the
2380 // exit code, only the merge's resolved/unresolved status does.
2382 m_reporter->Report( wxString::Format( wxS( "%s: %s\n" ), f.validator, f.message ), f.severity );
2383
2384 // The merged schematic was written to m_outputPath above, so the output is
2385 // always valid. Unresolved conflicts are reported and signalled via the
2386 // exit code; the user resolves them with the interactive mergetool.
2387 if( !planSnapshot.Resolved() )
2388 {
2389 m_reporter->Report( wxString::Format( _( "Merge completed with %zu unresolved conflict(s) in %s\n" ),
2390 planSnapshot.ConflictCount(), aOutput ),
2393 }
2394
2396}
2397
2398
2399// ============================================================================
2400// JobSymLibMerge: 3-way merge of .kicad_sym libraries.
2401// ============================================================================
2404#include <wx/ffile.h>
2405
2406
2407int EESCHEMA_JOBS_HANDLER::runSymLibMerge( const wxString& aAncestor, const wxString& aOurs,
2408 const wxString& aTheirs, const wxString& aOutput )
2409{
2410 if( aOutput.IsEmpty() )
2411 {
2412 m_reporter->Report( _( "--output is required\n" ), RPT_SEVERITY_ERROR );
2414 }
2415
2416 // Three sides into name -> LIB_SYMBOL maps.
2417 struct LIB_SIDE
2418 {
2419 std::vector<std::unique_ptr<LIB_SYMBOL>> owners;
2421 };
2422
2423 LIB_SIDE ancestor, ours, theirs;
2424
2425 auto loadSide = [&]( const wxString& aPath, LIB_SIDE& aSide ) -> int
2426 {
2427 try
2428 {
2429 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
2430 aSide.owners = std::move( loaded.first );
2431 aSide.map = std::move( loaded.second );
2433 }
2434 catch( const IO_ERROR& ioe )
2435 {
2436 m_reporter->Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
2438 }
2439 catch( const std::exception& e )
2440 {
2441 m_reporter->Report(
2442 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
2444 }
2445
2447 };
2448
2449 if( int rc = loadSide( aAncestor, ancestor ); rc != CLI::EXIT_CODES::SUCCESS )
2450 return rc;
2451
2452 if( int rc = loadSide( aOurs, ours ); rc != CLI::EXIT_CODES::SUCCESS )
2453 return rc;
2454
2455 if( int rc = loadSide( aTheirs, theirs ); rc != CLI::EXIT_CODES::SUCCESS )
2456 return rc;
2457
2458 KICAD_DIFF::SYM_LIB_DIFFER ourDiff( ancestor.map, ours.map, aOurs );
2459 KICAD_DIFF::SYM_LIB_DIFFER theirDiff( ancestor.map, theirs.map, aTheirs );
2460
2461 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2462 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2463
2465 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2466
2467 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2468
2469 KICAD_DIFF::SYM_LIB_MERGE_APPLIER applier( ancestor.map, ours.map, theirs.map, std::move( plan ) );
2470 std::vector<std::unique_ptr<LIB_SYMBOL>> merged = applier.Apply();
2471
2472 // Per-property symbol merge isn't implemented; MERGE_PROPS resolutions are
2473 // downgraded to TAKE_OURS. Surface that as unresolved so the user sees a
2474 // marker instead of silent partial-merge.
2475 const bool hadSilentFallback = applier.GetReport().mergePropsFallback > 0;
2476
2477 // Serialize via the sexpr lib cache: create at output path, add each
2478 // merged symbol, save. The cache owns its symbols once added; clone
2479 // before handing off so the applier's unique_ptrs stay intact for the
2480 // post-save report.
2481 wxFileName outFn( aOutput );
2482 outFn.MakeAbsolute();
2483
2484 try
2485 {
2486 SCH_IO_KICAD_SEXPR_LIB_CACHE cache( outFn.GetFullPath() );
2487
2488 // SCH_IO_LIB_CACHE::AddSymbol takes ownership of the raw pointer; the
2489 // cache destructor deletes from m_symbols. Release the unique_ptrs so
2490 // we don't double-free.
2491 for( auto& sym : merged )
2492 {
2493 if( sym )
2494 cache.AddSymbol( sym.release() );
2495 }
2496
2497 cache.SetModified( true );
2498 cache.Save();
2499 }
2500 catch( const IO_ERROR& ioe )
2501 {
2502 m_reporter->Report( wxString::Format( _( "Failed to save merged symbol library: %s\n" ), ioe.What() ),
2505 }
2506
2507 // The merged library was saved above, so the output is always valid.
2508 if( !planSnapshot.Resolved() || hadSilentFallback )
2509 {
2510 // Conflict count = engine-unresolved ∪ applier-downgraded (deduped, so
2511 // an item that was both unresolved and silently downgraded counts once).
2512 std::set<KIID_PATH> conflicts( planSnapshot.unresolved.begin(), planSnapshot.unresolved.end() );
2513
2514 for( const KIID_PATH& id : applier.GetReport().mergePropsFallbackIds )
2515 conflicts.insert( id );
2516
2517 m_reporter->Report( wxString::Format( _( "Symbol library merge completed with %zu unresolved "
2518 "conflict(s) in %s\n" ),
2519 conflicts.size(), aOutput ),
2522 }
2523
2525}
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:918
int GetPageCount() const
Definition base_screen.h:68
int GetVirtualPageNumber() const
Definition base_screen.h:71
const wxString & GetPageNumber() const
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr const Vec GetCenter() const
Definition box2.h:226
constexpr size_type GetHeight() const
Definition box2.h:211
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:531
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true)
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...
SCHEMATIC * getSchematic(const wxString &aPath)
DS_PROXY_VIEW_ITEM * getDrawingSheetProxyView(SCHEMATIC *aSch)
int runSymLibMerge(const wxString &aAncestor, const wxString &aOurs, const wxString &aTheirs, const wxString &aOutput)
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:221
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:2276
int GetFieldNameCol(const wxString &aFieldName) const
void AddColumn(const wxString &aFieldName, const wxString &aLabel, bool aAddedByUser, const wxString &aVariantName)
wxString Export(const BOM_FMT_PRESET &settings)
void ApplyBomPreset(const BOM_PRESET &preset, const wxString &aVariantName)
static const wxString ITEM_NUMBER_VARIABLE
void SetVariantNames(const std::vector< wxString > &aVariantNames)
static const wxString QUANTITY_VARIABLE
std::vector< BOM_FIELD > GetFieldsOrdered()
void SetCurrentVariant(const wxString &aVariantName)
Set the current variant name for highlighting purposes.
Provide an extensible class to resolve 3D model paths.
wxString ResolvePath(const wxString &aFileName, const wxString &aWorkingPath, std::vector< const EMBEDDED_FILES * > aEmbeddedFilesStack)
Determine the full path of the given file name.
void SetProgramBase(PGM_BASE *aBase)
Set a pointer to the application's PGM_BASE instance used to extract the local env vars.
bool SetProject(const PROJECT *aProject, bool *flgChanged=nullptr)
Set the current KiCad project directory as the first entry in the model path list.
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
std::vector< wxString > m_fieldsLabels
std::vector< wxString > m_fieldsOrdered
std::vector< wxString > m_fieldsGroupBy
std::vector< wxString > m_variantNames
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
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:163
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:156
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:311
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:398
@ FACE_CVPCB
Definition kiway.h:320
void AsyncLoad()
Loads all available libraries for this adapter type in the background.
Define a library symbol object.
Definition lib_symbol.h:79
const BOX2I GetUnitBoundingBox(int aUnit, int aBodyStyle, bool aIgnoreHiddenFields=true, bool aIgnoreLabelsOnInvisiblePins=true) const
Get the bounding box for the symbol.
bool IsDerived() const
Definition lib_symbol.h:196
void Plot(PLOTTER *aPlotter, bool aBackground, const SCH_PLOT_OPTS &aPlotOpts, int aUnit, int aBodyStyle, const VECTOR2I &aOffset, bool aDimmed) override
Plot the item to aPlotter.
void PlotFields(PLOTTER *aPlotter, bool aBackground, const SCH_PLOT_OPTS &aPlotOpts, int aUnit, int aBodyStyle, const VECTOR2I &aOffset, bool aDimmed)
Plot symbol fields.
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:141
const std::vector< wxString > & GetBodyStyleNames() const
Definition lib_symbol.h:784
bool HasDeMorganBodyStyles() const override
Definition lib_symbol.h:781
int GetBodyStyleCount() const override
Definition lib_symbol.h:773
int GetUnitCount() const override
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition locale_io.h:37
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition page_info.h:164
void SetHeightMils(double aHeightInMils)
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition page_info.h:155
void SetWidthMils(double aWidthInMils)
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
Definition paths.cpp:518
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:73
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition plotter.h:166
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:163
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:185
virtual void SetColorMode(bool aColorMode)
Plot in B/W or color.
Definition plotter.h:160
The backing store for a PROJECT, in JSON format.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Container for project specific data.
Definition project.h:62
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:200
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:100
std::vector< BOM_PRESET > m_BomPresets
std::vector< BOM_FMT_PRESET > m_BomFmtPresets
Holds all the data relating to one schematic.
Definition schematic.h:90
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:105
wxString GetCurrentVariant() const
Return the current variant being edited.
EMBEDDED_FILES * GetEmbeddedFiles() override
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
const std::map< wxString, wxString > * GetProperties()
Definition schematic.h:108
SCH_SHEET & Root() const
Definition schematic.h:134
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)
virtual void AddSymbol(const LIB_SYMBOL *aSymbol)
void SetModified(bool aModified=true)
static bool ConvertLibrary(std::map< std::string, UTF8 > *aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilepath)
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:162
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:746
SCH_SCREEN * GetNext()
SCH_SCREEN * GetScreen(unsigned int aIndex) const
SCH_SCREEN * GetFirst()
size_t GetCount() const
Definition sch_screen.h:751
SCH_SHEET * GetSheet(unsigned int aIndex) const
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:137
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const wxString & GetFileName() const
Definition sch_screen.h:150
int GetFileFormatVersionAtLoad() const
Definition sch_screen.h:135
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:161
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:44
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:376
wxString GetName() const
Definition sch_sheet.h:136
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
bool IsVirtualRootSheet() const
Schematic symbol object.
Definition sch_symbol.h:69
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.
virtual bool StartPlot(const wxString &aPageNumber) override
Create SVG file header.
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror) override
Set the plot offset and scaling for the current plot.
virtual bool EndPlot() override
An interface to the global shared library manager that is schematic-specific and linked to one projec...
const std::vector< TEMPLATE_FIELDNAME > & GetTemplateFieldNames()
Return a 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:444
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:456
The common library.
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::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:494
@ LAYER_SCHEMATIC_PAGE_LIMITS
Definition layer_ids.h:495
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:60
Plotting engines similar to ps (PostScript, Gerber, svg)
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
#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.
@ PAGE_SIZE_AUTO
Definition sch_plotter.h:45
@ PAGE_SIZE_A
Definition sch_plotter.h:47
@ PAGE_SIZE_A4
Definition sch_plotter.h:46
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:79
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)
const int scale
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
static std::vector< BOM_FMT_PRESET > BuiltInPresets()
wxString stringDelimiter
wxString refRangeDelimiter
wxString refDelimiter
wxString sortField
bool groupSymbols
std::vector< BOM_FIELD > fieldsOrdered
static std::vector< BOM_PRESET > BuiltInPresets()
bool excludeDNP
wxString filterString
Phase 8 context for the conflict canvas.
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:55
wxString m_theme
Definition sch_plotter.h:64
DXF_UNITS m_DXF_File_Unit
Definition sch_plotter.h:71
bool m_PDFPropertyPopups
Definition sch_plotter.h:61
wxString m_outputDirectory
Definition sch_plotter.h:66
bool m_pngAntialias
Definition sch_plotter.h:74
wxString m_outputFile
Definition sch_plotter.h:67
bool m_blackAndWhite
Definition sch_plotter.h:58
wxString m_variant
Definition sch_plotter.h:68
bool m_PDFHierarchicalLinks
Definition sch_plotter.h:62
bool m_useBackgroundColor
Definition sch_plotter.h:60
bool m_plotDrawingSheet
Definition sch_plotter.h:54
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.
std::map< wxString, LIB_SYMBOL *, LibSymbolMapSort > LIB_SYMBOL_MAP
wxString GetDefaultFieldName(FIELD_T aFieldId, bool aTranslateForHI)
Return a default symbol field name for a mandatory field type.
#define DO_TRANSLATE
#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...
wxString GetCanonicalFieldName(FIELD_T aFieldType)
std::string path
VECTOR3I res
wxString result
Test unit parsing edge cases and error handling.
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_SHEET_T
Definition typeinfo.h:172
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.