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 helper->SetKiway( m_kiway );
564
565 bool res = helper->WriteNetlist( outPath, netlistOption, *m_reporter );
566
567 if( !res )
569
570 aJob->AddOutput( outPath );
571
572 return CLI::EXIT_CODES::OK;
573}
574
575
577{
578 JOB_EXPORT_SCH_BOM* aBomJob = dynamic_cast<JOB_EXPORT_SCH_BOM*>( aJob );
579
580 wxCHECK( aBomJob, CLI::EXIT_CODES::ERR_UNKNOWN );
581
582 SCHEMATIC* sch = getSchematic( aBomJob->m_filename );
583
584 if( !sch )
586
587 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
588 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
589
590 wxString currentVariant = aBomJob->GetSelectedVariant();
591
592 if( !currentVariant.IsEmpty() && currentVariant != wxS( "all" ) )
593 sch->SetCurrentVariant( currentVariant );
594
595 // Annotation warning check
596 SCH_REFERENCE_LIST referenceList;
597 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_NON_POWER, false );
598
599 if( referenceList.GetCount() > 0 )
600 {
601 SCH_REFERENCE_LIST copy = referenceList;
602
603 // Check annotation splits references...
604 if( copy.CheckAnnotation(
605 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
606 {
607 // We're only interested in the end result -- either errors or not
608 } )
609 > 0 )
610 {
611 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the schematic "
612 "editor to fix them\n" ),
614 }
615 }
616
617 // Test duplicate sheet names:
618 ERC_TESTER erc( sch );
619
620 if( erc.TestDuplicateSheetNames( false ) > 0 )
621 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
622
623 // Build our data model
624 FIELDS_EDITOR_GRID_DATA_MODEL dataModel( referenceList, nullptr );
625
626 // Mandatory fields first
627 for( FIELD_T fieldId : MANDATORY_FIELDS )
628 {
629 dataModel.AddColumn( GetCanonicalFieldName( fieldId ), GetDefaultFieldName( fieldId, DO_TRANSLATE ), false,
630 currentVariant );
631 }
632
633 // Generated/virtual fields (e.g. ${QUANTITY}, ${ITEM_NUMBER}) present only in the fields table
636 currentVariant );
639 currentVariant );
640
641 // Attribute fields (boolean flags on symbols)
642 dataModel.AddColumn( wxS( "${DNP}" ), GetGeneratedFieldDisplayName( wxS( "${DNP}" ) ), false, currentVariant );
643 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOM}" ) ),
644 false, currentVariant );
645 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_BOARD}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_BOARD}" ) ),
646 false, currentVariant );
647 dataModel.AddColumn( wxS( "${EXCLUDE_FROM_SIM}" ), GetGeneratedFieldDisplayName( wxS( "${EXCLUDE_FROM_SIM}" ) ),
648 false, currentVariant );
649
650 // User field names in symbols second
651 std::set<wxString> userFieldNames;
652
653 for( size_t i = 0; i < referenceList.GetCount(); ++i )
654 {
655 SCH_SYMBOL* symbol = referenceList[i].GetSymbol();
656
657 for( SCH_FIELD& field : symbol->GetFields() )
658 {
659 if( !field.IsMandatory() && !field.IsPrivate() )
660 userFieldNames.insert( field.GetName() );
661 }
662 }
663
664 for( const wxString& fieldName : userFieldNames )
665 dataModel.AddColumn( fieldName, GetGeneratedFieldDisplayName( fieldName ), true, currentVariant );
666
667 // Add any templateFieldNames which aren't already present in the userFieldNames
668 for( const TEMPLATE_FIELDNAME& templateFieldname : sch->Settings().m_TemplateFieldNames.GetTemplateFieldNames() )
669 {
670 if( userFieldNames.count( templateFieldname.m_Name ) == 0 )
671 {
672 dataModel.AddColumn( templateFieldname.m_Name, GetGeneratedFieldDisplayName( templateFieldname.m_Name ),
673 false, currentVariant );
674 }
675 }
676
677 BOM_PRESET preset;
678
679 // Load a preset if one is specified
680 if( !aBomJob->m_bomPresetName.IsEmpty() )
681 {
682 // Find the preset
683 const BOM_PRESET* schPreset = nullptr;
684
685 for( const BOM_PRESET& p : BOM_PRESET::BuiltInPresets() )
686 {
687 if( p.name == aBomJob->m_bomPresetName )
688 {
689 schPreset = &p;
690 break;
691 }
692 }
693
694 for( const BOM_PRESET& p : sch->Settings().m_BomPresets )
695 {
696 if( p.name == aBomJob->m_bomPresetName )
697 {
698 schPreset = &p;
699 break;
700 }
701 }
702
703 if( !schPreset )
704 {
705 m_reporter->Report(
706 wxString::Format( _( "BOM preset '%s' not found" ) + wxS( "\n" ), aBomJob->m_bomPresetName ),
708
710 }
711
712 preset = *schPreset;
713 }
714 else
715 {
716 // Normalize field names so that bare generated-field tokens (e.g. "QUANTITY") are
717 // accepted alongside the canonical "${QUANTITY}" form. Shell expansion of ${VAR}
718 // inside double quotes silently produces an empty string, so this also guards against
719 // that common CLI pitfall.
720 auto normalizeFieldName = [&dataModel]( const wxString& aName ) -> wxString
721 {
722 if( aName.IsEmpty() )
723 return wxEmptyString;
724
725 if( IsGeneratedField( aName ) )
726 return aName;
727
728 wxString wrapped = wxS( "${" ) + aName + wxS( "}" );
729
730 if( IsGeneratedField( wrapped ) && dataModel.GetFieldNameCol( wrapped ) != -1 )
731 return wrapped;
732
733 return aName;
734 };
735
736 size_t i = 0;
737
738 for( const wxString& rawFieldName : aBomJob->m_fieldsOrdered )
739 {
740 wxString fieldName = normalizeFieldName( rawFieldName );
741
742 if( fieldName.IsEmpty() )
743 {
744 i++;
745 continue;
746 }
747
748 // Handle wildcard. We allow the wildcard anywhere in the list, but it needs to respect
749 // fields that come before and after the wildcard.
750 if( fieldName == wxS( "*" ) )
751 {
752 for( const BOM_FIELD& modelField : dataModel.GetFieldsOrdered() )
753 {
754 struct BOM_FIELD field;
755
756 field.name = modelField.name;
757 field.show = true;
758 field.groupBy = false;
759 field.label = field.name;
760
761 bool fieldAlreadyPresent = false;
762
763 for( BOM_FIELD& presetField : preset.fieldsOrdered )
764 {
765 if( presetField.name == field.name )
766 {
767 fieldAlreadyPresent = true;
768 break;
769 }
770 }
771
772 bool fieldLaterInList = false;
773
774 for( const wxString& fieldInList : aBomJob->m_fieldsOrdered )
775 {
776 if( normalizeFieldName( fieldInList ) == field.name )
777 {
778 fieldLaterInList = true;
779 break;
780 }
781 }
782
783 if( !fieldAlreadyPresent && !fieldLaterInList )
784 preset.fieldsOrdered.emplace_back( field );
785 }
786
787 continue;
788 }
789
790 struct BOM_FIELD field;
791
792 field.name = fieldName;
793 field.show = !fieldName.StartsWith( wxT( "__" ), &field.name );
794
795 field.groupBy = alg::contains( aBomJob->m_fieldsGroupBy, field.name )
796 || alg::contains( aBomJob->m_fieldsGroupBy, rawFieldName );
797
798 if( ( aBomJob->m_fieldsLabels.size() > i ) && !aBomJob->m_fieldsLabels[i].IsEmpty() )
799 field.label = aBomJob->m_fieldsLabels[i];
800 else if( IsGeneratedField( field.name ) )
801 field.label = GetGeneratedFieldDisplayName( field.name );
802 else
803 field.label = field.name;
804
805 preset.fieldsOrdered.emplace_back( field );
806 i++;
807 }
808
809 preset.sortAsc = aBomJob->m_sortAsc;
810 preset.sortField = normalizeFieldName( aBomJob->m_sortField );
811 preset.filterString = aBomJob->m_filterString;
812 preset.groupSymbols = aBomJob->m_groupSymbols;
813 preset.excludeDNP = aBomJob->m_excludeDNP;
814 }
815
816 BOM_FMT_PRESET fmt;
817
818 // Load a format preset if one is specified
819 if( !aBomJob->m_bomFmtPresetName.IsEmpty() )
820 {
821 std::optional<BOM_FMT_PRESET> schFmtPreset;
822
824 {
825 if( p.name == aBomJob->m_bomFmtPresetName )
826 {
827 schFmtPreset = p;
828 break;
829 }
830 }
831
832 for( const BOM_FMT_PRESET& p : sch->Settings().m_BomFmtPresets )
833 {
834 if( p.name == aBomJob->m_bomFmtPresetName )
835 {
836 schFmtPreset = p;
837 break;
838 }
839 }
840
841 if( !schFmtPreset )
842 {
843 m_reporter->Report( wxString::Format( _( "BOM format preset '%s' not found" ) + wxS( "\n" ),
844 aBomJob->m_bomFmtPresetName ),
846
848 }
849
850 fmt = *schFmtPreset;
851 }
852 else
853 {
854 fmt.fieldDelimiter = aBomJob->m_fieldDelimiter;
855 fmt.stringDelimiter = aBomJob->m_stringDelimiter;
856 fmt.refDelimiter = aBomJob->m_refDelimiter;
858 fmt.keepTabs = aBomJob->m_keepTabs;
859 fmt.keepLineBreaks = aBomJob->m_keepLineBreaks;
860 }
861
862 if( aBomJob->GetConfiguredOutputPath().IsEmpty() )
863 {
864 wxFileName fn = sch->GetFileName();
865 fn.SetName( fn.GetName() );
866 fn.SetExt( FILEEXT::CsvFileExtension );
867
868 aBomJob->SetConfiguredOutputPath( fn.GetFullName() );
869 }
870
871 wxString configuredPath = aBomJob->GetConfiguredOutputPath();
872 bool hasVariantPlaceholder = configuredPath.Contains( wxS( "${VARIANT}" ) );
873
874 // Determine which variants to process
875 std::vector<wxString> variantsToProcess;
876
877 if( aBomJob->m_variantNames.size() > 1 && hasVariantPlaceholder )
878 {
879 variantsToProcess = aBomJob->m_variantNames;
880 }
881 else
882 {
883 variantsToProcess.push_back( currentVariant );
884 }
885
886 for( const wxString& variantName : variantsToProcess )
887 {
888 std::vector<wxString> singleVariant = { variantName };
889 dataModel.SetVariantNames( singleVariant );
890 dataModel.SetCurrentVariant( variantName );
891 dataModel.ApplyBomPreset( preset, variantName );
892
893 wxString outPath;
894
895 if( hasVariantPlaceholder )
896 {
897 wxString variantPath = configuredPath;
898 variantPath.Replace( wxS( "${VARIANT}" ), variantName );
899 aBomJob->SetConfiguredOutputPath( variantPath );
900 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
901 aBomJob->SetConfiguredOutputPath( configuredPath );
902 }
903 else
904 {
905 outPath = aBomJob->GetFullOutputPath( &sch->Project() );
906 }
907
908 if( !PATHS::EnsurePathExists( outPath, true ) )
909 {
910 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
912 }
913
914 wxFile f;
915
916 if( !f.Open( outPath, wxFile::write ) )
917 {
918 m_reporter->Report( wxString::Format( _( "Unable to open destination '%s'" ), outPath ),
920
922 }
923
924 bool res = f.Write( dataModel.Export( fmt ) );
925
926 if( !res )
928
929 aJob->AddOutput( outPath );
930
931 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
932 }
933
934 return CLI::EXIT_CODES::OK;
935}
936
937
939{
940 JOB_EXPORT_SCH_PYTHONBOM* aNetJob = dynamic_cast<JOB_EXPORT_SCH_PYTHONBOM*>( aJob );
941
942 wxCHECK( aNetJob, CLI::EXIT_CODES::ERR_UNKNOWN );
943
944 SCHEMATIC* sch = getSchematic( aNetJob->m_filename );
945
946 if( !sch )
948
949 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
950 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
951
952 // Annotation warning check
953 SCH_REFERENCE_LIST referenceList;
954 sch->Hierarchy().GetSymbols( referenceList, SYMBOL_FILTER_ALL );
955
956 if( referenceList.GetCount() > 0 )
957 {
958 if( referenceList.CheckAnnotation(
959 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
960 {
961 // We're only interested in the end result -- either errors or not
962 } )
963 > 0 )
964 {
965 m_reporter->Report( _( "Warning: schematic has annotation errors, please use the "
966 "schematic editor to fix them\n" ),
968 }
969 }
970
971 // Test duplicate sheet names:
972 ERC_TESTER erc( sch );
973
974 if( erc.TestDuplicateSheetNames( false ) > 0 )
975 m_reporter->Report( _( "Warning: duplicate sheet names.\n" ), RPT_SEVERITY_WARNING );
976
977 std::unique_ptr<NETLIST_EXPORTER_XML> xmlNetlist = std::make_unique<NETLIST_EXPORTER_XML>( sch );
978
979 if( aNetJob->GetConfiguredOutputPath().IsEmpty() )
980 {
981 wxFileName fn = sch->GetFileName();
982 fn.SetName( fn.GetName() + "-bom" );
983 fn.SetExt( FILEEXT::XmlFileExtension );
984
985 aNetJob->SetConfiguredOutputPath( fn.GetFullName() );
986 }
987
988 wxString outPath = aNetJob->GetFullOutputPath( &sch->Project() );
989
990 if( !PATHS::EnsurePathExists( outPath, true ) )
991 {
992 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
994 }
995
996 bool res = xmlNetlist->WriteNetlist( outPath, GNL_OPT_BOM, *m_reporter );
997
998 if( !res )
1000
1001 aJob->AddOutput( outPath );
1002
1003 m_reporter->Report( wxString::Format( _( "Wrote bill of materials to '%s'." ), outPath ), RPT_SEVERITY_ACTION );
1004
1005 return CLI::EXIT_CODES::OK;
1006}
1007
1008
1010 LIB_SYMBOL* symbol )
1011{
1012 wxCHECK( symbol, CLI::EXIT_CODES::ERR_UNKNOWN );
1013
1014 std::shared_ptr<LIB_SYMBOL> parent;
1015 LIB_SYMBOL* symbolToPlot = symbol;
1016
1017 // if the symbol is an alias, then the draw items are stored in the root symbol
1018 if( symbol->IsDerived() )
1019 {
1020 parent = symbol->GetRootSymbol();
1021
1022 wxCHECK( parent, CLI::EXIT_CODES::ERR_UNKNOWN );
1023
1024 symbolToPlot = parent.get();
1025 }
1026
1027 // iterate from unit 1, unit 0 would be "all units" which we don't want
1028 for( int unit = 1; unit < symbol->GetUnitCount() + 1; unit++ )
1029 {
1030 for( int bodyStyle = 1; bodyStyle <= symbol->GetBodyStyleCount(); ++bodyStyle )
1031 {
1032 wxString filename;
1033 wxFileName fn;
1034
1035 fn.SetPath( aSvgJob->m_outputDirectory );
1036 fn.SetExt( FILEEXT::SVGFileExtension );
1037
1038 filename = symbol->GetName();
1039
1040 for( wxChar c : wxFileName::GetForbiddenChars( wxPATH_DOS ) )
1041 filename.Replace( c, ' ' );
1042
1043 // Even single units get a unit number in the filename. This simplifies the
1044 // handling of the files as they have a uniform pattern.
1045 // Also avoids aliasing 'sym', unit 2 and 'sym_unit2', unit 1 to the same file.
1046 filename += wxString::Format( "_unit%d", unit );
1047
1048 if( symbol->HasDeMorganBodyStyles() )
1049 {
1050 if( bodyStyle == 2 )
1051 filename += wxS( "_demorgan" );
1052 }
1053 else if( bodyStyle <= (int) symbol->GetBodyStyleNames().size() )
1054 {
1055 filename += wxS( "_" ) + symbol->GetBodyStyleNames()[bodyStyle - 1].Lower();
1056 }
1057
1058 fn.SetName( filename );
1059 m_reporter->Report( wxString::Format( _( "Plotting symbol '%s' unit %d to '%s'\n" ), symbol->GetName(),
1060 unit, fn.GetFullPath() ),
1062
1063 // Get the symbol bounding box to fit the plot page to it
1064 BOX2I symbolBB = symbol->Flatten()->GetUnitBoundingBox( unit, bodyStyle, !aSvgJob->m_includeHiddenFields );
1065 PAGE_INFO pageInfo( PAGE_SIZE_TYPE::User );
1066 pageInfo.SetHeightMils( schIUScale.IUToMils( symbolBB.GetHeight() * 1.2 ) );
1067 pageInfo.SetWidthMils( schIUScale.IUToMils( symbolBB.GetWidth() * 1.2 ) );
1068
1069 SVG_PLOTTER* plotter = new SVG_PLOTTER();
1070 plotter->SetRenderSettings( aRenderSettings );
1071 plotter->SetPageSettings( pageInfo );
1072 plotter->SetColorMode( !aSvgJob->m_blackAndWhite );
1073
1074 VECTOR2I plot_offset = symbolBB.GetCenter();
1075 const double scale = 1.0;
1076
1077 // Currently, plot units are in decimal
1078 plotter->SetViewport( plot_offset, schIUScale.IU_PER_MILS / 10, scale, false );
1079
1080 plotter->SetCreator( wxT( "Eeschema-SVG" ) );
1081
1082 if( !plotter->OpenFile( fn.GetFullPath() ) )
1083 {
1084 m_reporter->Report(
1085 wxString::Format( _( "Unable to open destination '%s'" ) + wxS( "\n" ), fn.GetFullPath() ),
1087
1088 delete plotter;
1090 }
1091
1092 LOCALE_IO toggle;
1093 SCH_PLOT_OPTS plotOpts;
1094
1095 plotter->StartPlot( wxT( "1" ) );
1096
1097 bool background = true;
1098 VECTOR2I offset( pageInfo.GetWidthIU( schIUScale.IU_PER_MILS ) / 2,
1099 pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) / 2 );
1100
1101 // note, we want the fields from the original symbol pointer (in case of non-alias)
1102 symbolToPlot->Plot( plotter, background, plotOpts, unit, bodyStyle, offset, false );
1103 symbol->PlotFields( plotter, background, plotOpts, unit, bodyStyle, offset, false );
1104
1105 symbolToPlot->Plot( plotter, !background, plotOpts, unit, bodyStyle, offset, false );
1106 symbol->PlotFields( plotter, !background, plotOpts, unit, bodyStyle, offset, false );
1107
1108 plotter->EndPlot();
1109 delete plotter;
1110 }
1111 }
1112
1113 if( m_reporter->HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
1115
1116 return CLI::EXIT_CODES::OK;
1117}
1118
1119
1121{
1122 JOB_SYM_EXPORT_SVG* svgJob = dynamic_cast<JOB_SYM_EXPORT_SVG*>( aJob );
1123
1124 wxCHECK( svgJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1125
1126 wxFileName fn( svgJob->m_libraryPath );
1127 fn.MakeAbsolute();
1128
1129 // When the input is a single symbol file we restrict plotting to the symbols defined in
1130 // that file. Stays empty (no restriction) when the input is a whole library.
1131 wxString singleFileFilter;
1132
1133 auto schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetFullPath() );
1134
1135 try
1136 {
1137 schLibrary->Load();
1138 }
1139 catch( ... )
1140 {
1141 // A single file holding a derived symbol whose parent is in a sibling file cannot load
1142 // alone. Retry against the enclosing directory, then plot only this file's symbols.
1143 bool recovered = false;
1144
1145 if( !fn.IsDir() && wxDir::Exists( fn.GetPath() ) )
1146 {
1147 try
1148 {
1149 schLibrary = std::make_unique<SCH_IO_KICAD_SEXPR_LIB_CACHE>( fn.GetPath() );
1150 schLibrary->Load();
1151 singleFileFilter = fn.GetFullPath();
1152 recovered = true;
1153 }
1154 catch( ... )
1155 {
1156 // Fall through to the generic load error below.
1157 }
1158 }
1159
1160 if( !recovered )
1161 {
1162 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1164 }
1165 }
1166
1167 if( m_progressReporter )
1168 m_progressReporter->KeepRefreshing();
1169
1170 LIB_SYMBOL* symbol = nullptr;
1171
1172 if( !svgJob->m_symbol.IsEmpty() )
1173 {
1174 // See if the selected symbol exists
1175 symbol = schLibrary->GetSymbol( svgJob->m_symbol );
1176
1177 if( !symbol )
1178 {
1179 m_reporter->Report( _( "There is no symbol selected to save." ) + wxS( "\n" ), RPT_SEVERITY_ERROR );
1181 }
1182 }
1183
1184 if( !svgJob->m_outputDirectory.IsEmpty() && !wxDir::Exists( svgJob->m_outputDirectory ) )
1185 {
1186 if( !wxFileName::Mkdir( svgJob->m_outputDirectory ) )
1187 {
1188 m_reporter->Report( wxString::Format( _( "Unable to create output directory '%s'." ) + wxS( "\n" ),
1189 svgJob->m_outputDirectory ),
1192 }
1193 }
1194
1195 SCH_RENDER_SETTINGS renderSettings;
1197 renderSettings.LoadColors( cs );
1198 renderSettings.SetDefaultPenWidth( DEFAULT_LINE_WIDTH_MILS * schIUScale.IU_PER_MILS );
1199 renderSettings.m_ShowHiddenPins = svgJob->m_includeHiddenPins;
1200 renderSettings.m_ShowHiddenFields = svgJob->m_includeHiddenFields;
1201
1202 int exitCode = CLI::EXIT_CODES::OK;
1203
1204 if( symbol )
1205 {
1206 exitCode = doSymExportSvg( svgJob, &renderSettings, symbol );
1207 }
1208 else
1209 {
1210 // Just plot all the symbols we can
1211 const LIB_SYMBOL_MAP& libSymMap = schLibrary->GetSymbolMap();
1212 const std::map<wxString, wxString>& sourceFiles = schLibrary->GetSymbolSourceFiles();
1213 const wxFileName filterFile( singleFileFilter );
1214
1215 for( const auto& [name, libSymbol] : libSymMap )
1216 {
1217 // When a single file was requested, skip symbols that came from sibling files.
1218 if( !singleFileFilter.IsEmpty() )
1219 {
1220 auto srcIt = sourceFiles.find( name );
1221
1222 if( srcIt == sourceFiles.end() || !wxFileName( srcIt->second ).SameAs( filterFile ) )
1223 continue;
1224 }
1225
1226 if( m_progressReporter )
1227 {
1228 m_progressReporter->AdvancePhase( wxString::Format( _( "Exporting %s" ), name ) );
1229 m_progressReporter->KeepRefreshing();
1230 }
1231
1232 exitCode = doSymExportSvg( svgJob, &renderSettings, libSymbol );
1233
1234 if( exitCode != CLI::EXIT_CODES::OK )
1235 break;
1236 }
1237 }
1238
1239 return exitCode;
1240}
1241
1242
1244{
1245 JOB_SYM_UPGRADE* upgradeJob = dynamic_cast<JOB_SYM_UPGRADE*>( aJob );
1246
1247 wxCHECK( upgradeJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1248
1249 wxFileName fn( upgradeJob->m_libraryPath );
1250 fn.MakeAbsolute();
1251
1252 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
1253
1254 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1255 {
1256 if( wxFile::Exists( upgradeJob->m_outputLibraryPath ) )
1257 {
1258 m_reporter->Report( _( "Output path must not conflict with existing path\n" ), RPT_SEVERITY_ERROR );
1259
1261 }
1262 }
1263 else if( fileType != SCH_IO_MGR::SCH_KICAD )
1264 {
1265 m_reporter->Report( _( "Output path must be specified to convert legacy and non-KiCad libraries\n" ),
1267
1269 }
1270
1271 if( fileType == SCH_IO_MGR::SCH_KICAD )
1272 {
1273 SCH_IO_KICAD_SEXPR_LIB_CACHE schLibrary( fn.GetFullPath() );
1274
1275 try
1276 {
1277 schLibrary.Load();
1278 }
1279 catch( ... )
1280 {
1281 m_reporter->Report( _( "Unable to load library\n" ), RPT_SEVERITY_ERROR );
1283 }
1284
1285 if( m_progressReporter )
1286 m_progressReporter->KeepRefreshing();
1287
1288 bool shouldSave =
1290
1291 if( shouldSave )
1292 {
1293 m_reporter->Report( _( "Saving symbol library in updated format\n" ), RPT_SEVERITY_ACTION );
1294
1295 try
1296 {
1297 if( !upgradeJob->m_outputLibraryPath.IsEmpty() )
1298 schLibrary.SetFileName( upgradeJob->m_outputLibraryPath );
1299
1300 schLibrary.SetModified();
1301 schLibrary.Save();
1302 }
1303 catch( ... )
1304 {
1305 m_reporter->Report( ( "Unable to save library\n" ), RPT_SEVERITY_ERROR );
1307 }
1308 }
1309 else
1310 {
1311 m_reporter->Report( _( "Symbol library was not updated\n" ), RPT_SEVERITY_ERROR );
1312 }
1313 }
1314 else
1315 {
1316 if( !SCH_IO_MGR::ConvertLibrary( nullptr, fn.GetAbsolutePath(), upgradeJob->m_outputLibraryPath ) )
1317 {
1318 m_reporter->Report( ( "Unable to convert library\n" ), RPT_SEVERITY_ERROR );
1320 }
1321 }
1322
1323 return CLI::EXIT_CODES::OK;
1324}
1325
1326
1328{
1329 JOB_SCH_ERC* ercJob = dynamic_cast<JOB_SCH_ERC*>( aJob );
1330
1331 wxCHECK( ercJob, CLI::EXIT_CODES::ERR_UNKNOWN );
1332
1333 SCHEMATIC* sch = getSchematic( ercJob->m_filename );
1334
1335 if( !sch )
1337
1338 aJob->SetTitleBlock( sch->RootScreen()->GetTitleBlock() );
1339 sch->Project().ApplyTextVars( aJob->GetVarOverrides() );
1340
1341 if( ercJob->GetConfiguredOutputPath().IsEmpty() )
1342 {
1343 wxFileName fn = sch->GetFileName();
1344 fn.SetName( fn.GetName() + wxS( "-erc" ) );
1345
1347 fn.SetExt( FILEEXT::JsonFileExtension );
1348 else
1349 fn.SetExt( FILEEXT::ReportFileExtension );
1350
1351 // Use a transient working path so an empty configured output filename isn't persisted
1352 // back into the jobset file. Mirrors the PCB DRC handler.
1353 ercJob->SetWorkingOutputPath( fn.GetFullName() );
1354 }
1355
1356 wxString outPath = ercJob->GetFullOutputPath( &sch->Project() );
1357
1358 if( !PATHS::EnsurePathExists( outPath, true ) )
1359 {
1360 m_reporter->Report( _( "Failed to create output directory\n" ), RPT_SEVERITY_ERROR );
1362 }
1363
1364 EDA_UNITS units;
1365
1366 switch( ercJob->m_units )
1367 {
1368 case JOB_SCH_ERC::UNITS::INCH: units = EDA_UNITS::INCH; break;
1369 case JOB_SCH_ERC::UNITS::MILS: units = EDA_UNITS::MILS; break;
1370 case JOB_SCH_ERC::UNITS::MM: units = EDA_UNITS::MM; break;
1371 default: units = EDA_UNITS::MM; break;
1372 }
1373
1374 std::shared_ptr<SHEETLIST_ERC_ITEMS_PROVIDER> markersProvider =
1375 std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( sch );
1376
1377 // Running ERC requires libraries be loaded, so make sure they have been
1379 adapter->AsyncLoad();
1380 adapter->BlockUntilLoaded();
1381
1382 ERC_TESTER ercTester( sch );
1383
1384 std::unique_ptr<DS_PROXY_VIEW_ITEM> drawingSheet( getDrawingSheetProxyView( sch ) );
1385 ercTester.RunTests( drawingSheet.get(), nullptr, m_kiway->KiFACE( KIWAY::FACE_CVPCB ), &sch->Project(),
1387
1388 markersProvider->SetSeverities( ercJob->m_severity );
1389
1390 m_reporter->Report( wxString::Format( _( "Found %d violations\n" ), markersProvider->GetCount() ),
1392
1393 ERC_REPORT reportWriter( sch, units, markersProvider );
1394
1395 bool wroteReport = false;
1396
1398 wroteReport = reportWriter.WriteJsonReport( outPath );
1399 else
1400 wroteReport = reportWriter.WriteTextReport( outPath );
1401
1402 if( !wroteReport )
1403 {
1404 m_reporter->Report( wxString::Format( _( "Unable to save ERC report to %s\n" ), outPath ), RPT_SEVERITY_ERROR );
1406 }
1407
1408 m_reporter->Report( wxString::Format( _( "Saved ERC Report to %s\n" ), outPath ), RPT_SEVERITY_ACTION );
1409
1410 if( ercJob->m_exitCodeViolations )
1411 {
1412 if( markersProvider->GetCount() > 0 )
1414 }
1415
1417}
1418
1419
1421{
1422 JOB_SCH_UPGRADE* aUpgradeJob = dynamic_cast<JOB_SCH_UPGRADE*>( aJob );
1423
1424 if( aUpgradeJob == nullptr )
1426
1427 SCHEMATIC* sch = getSchematic( aUpgradeJob->m_filename );
1428
1429 if( !sch )
1431
1432 bool shouldSave = aUpgradeJob->m_force;
1433
1435 shouldSave = true;
1436
1437 if( !shouldSave )
1438 {
1439 m_reporter->Report( _( "Schematic file was not updated\n" ), RPT_SEVERITY_ERROR );
1441 }
1442
1443 // needs an absolute path
1444 wxFileName schPath( aUpgradeJob->m_filename );
1445 schPath.MakeAbsolute();
1446 const wxString schFullPath = schPath.GetFullPath();
1447
1448 try
1449 {
1450 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1451 SCH_SHEET* loadedSheet = pi->LoadSchematicFile( schFullPath, sch );
1452 pi->SaveSchematicFile( schFullPath, loadedSheet, sch );
1453 }
1454 catch( const IO_ERROR& ioe )
1455 {
1456 wxString msg =
1457 wxString::Format( _( "Error saving schematic file '%s'.\n%s" ), schFullPath, ioe.What().GetData() );
1458 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1460 }
1461
1462 m_reporter->Report( _( "Successfully saved schematic file using the latest format\n" ), RPT_SEVERITY_INFO );
1463
1465}
1466
1467
1469{
1470 JOB_SCH_IMPORT* job = dynamic_cast<JOB_SCH_IMPORT*>( aJob );
1471
1472 if( !job )
1474
1475 if( !wxFile::Exists( job->m_inputFile ) )
1476 {
1477 m_reporter->Report( wxString::Format( _( "Input file not found: '%s'\n" ), job->m_inputFile ),
1480 }
1481
1482 // AUTO restricts autodetect to non-KiCad plugins so a native file is not re-imported.
1483 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_UNKNOWN;
1484
1485 switch( job->m_format )
1486 {
1489 break;
1490 case JOB_SCH_IMPORT::FORMAT::ALTIUM: fileType = SCH_IO_MGR::SCH_ALTIUM; break;
1491 case JOB_SCH_IMPORT::FORMAT::EAGLE: fileType = SCH_IO_MGR::SCH_EAGLE; break;
1492 case JOB_SCH_IMPORT::FORMAT::CADSTAR: fileType = SCH_IO_MGR::SCH_CADSTAR_ARCHIVE; break;
1493 case JOB_SCH_IMPORT::FORMAT::EASYEDA: fileType = SCH_IO_MGR::SCH_EASYEDA; break;
1494 case JOB_SCH_IMPORT::FORMAT::EASYEDAPRO: fileType = SCH_IO_MGR::SCH_EASYEDAPRO; break;
1495 case JOB_SCH_IMPORT::FORMAT::LTSPICE: fileType = SCH_IO_MGR::SCH_LTSPICE; break;
1496 case JOB_SCH_IMPORT::FORMAT::PADS: fileType = SCH_IO_MGR::SCH_PADS; break;
1497 case JOB_SCH_IMPORT::FORMAT::DIPTRACE: fileType = SCH_IO_MGR::SCH_DIPTRACE; break;
1498 case JOB_SCH_IMPORT::FORMAT::PCAD: fileType = SCH_IO_MGR::SCH_PCAD; break;
1499 }
1500
1501 if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1502 {
1503 // Quiet sentinel: lets the top-level `import` command treat the file as not-a-schematic.
1504 m_reporter->Report( wxString::Format( _( "No schematic importer recognizes the file format "
1505 "of '%s'\n" ),
1506 job->m_inputFile ),
1509 }
1510
1511 wxString outputPath = job->GetConfiguredOutputPath();
1512
1513 if( outputPath.IsEmpty() )
1515
1516 wxFileName inputFn( job->m_inputFile );
1517 inputFn.MakeAbsolute();
1518
1519 wxFileName outputFn( outputPath );
1520 outputFn.MakeAbsolute();
1521
1522 // Foreign importers resolve their symbol library against the *active* project, so an import
1523 // with no project loaded needs a transient active one at the output location (never written to
1524 // disk; LoadProject returns false yet still registers it, hence the GetProject() check).
1526 PROJECT* projectPtr = nullptr;
1527 bool createdTransientProject = false;
1528
1529 if( mgr.IsProjectOpenNotDummy() )
1530 {
1531 projectPtr = &mgr.Prj();
1532 }
1533 else
1534 {
1535 wxFileName projectFn( outputFn );
1536 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1537
1538 mgr.LoadProject( projectFn.GetFullPath(), true );
1539 projectPtr = mgr.GetProject( projectFn.GetFullPath() );
1540 createdTransientProject = ( projectPtr != nullptr );
1541 }
1542
1543 if( !projectPtr )
1544 {
1545 m_reporter->Report( _( "Could not establish a project for the import\n" ),
1548 }
1549
1550 PROJECT& project = *projectPtr;
1551
1552 // Declared before the SCHEMATIC so reverse-destruction tears the schematic (which references
1553 // the project) down first; unloads the transient project on every exit path.
1554 struct TRANSIENT_PROJECT_GUARD
1555 {
1556 SETTINGS_MANAGER& m_mgr;
1557 PROJECT* m_project;
1558 bool m_active;
1559
1560 ~TRANSIENT_PROJECT_GUARD()
1561 {
1562 if( m_active )
1563 m_mgr.UnloadProject( m_project, false );
1564 }
1565 } transientProjectGuard{ mgr, projectPtr, createdTransientProject };
1566
1568
1569 std::unique_ptr<SCHEMATIC> schematic = std::make_unique<SCHEMATIC>( &project );
1570
1571 wxString formatName = SCH_IO_MGR::ShowType( fileType );
1572 SCH_SHEET* loadedSheet = nullptr;
1573
1574 try
1575 {
1576 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
1577
1578 if( !pi )
1579 {
1580 m_reporter->Report( wxString::Format( _( "No plugin found for file type '%s'\n" ),
1581 formatName ),
1584 }
1585
1586 m_reporter->Report( wxString::Format( _( "Importing '%s' using %s format...\n" ),
1587 inputFn.GetFullPath(), formatName ),
1589
1590 loadedSheet = pi->LoadSchematicFile( inputFn.GetFullPath(), schematic.get() );
1591
1592 if( !loadedSheet )
1593 {
1594 m_reporter->Report( _( "Failed to load schematic\n" ), RPT_SEVERITY_ERROR );
1596 }
1597 }
1598 catch( const IO_ERROR& ioe )
1599 {
1600 m_reporter->Report( wxString::Format( _( "Error during import: %s\n" ), ioe.What() ),
1603 }
1604
1605 size_t symbolCount = 0;
1606 size_t sheetCount = 0;
1607
1608 try
1609 {
1610 // Some importers build the top-level sheet set themselves; only collapse to the returned
1611 // sheet otherwise (mirrors SCH_EDIT_FRAME::importFile()).
1612 std::vector<SCH_SHEET*> topLevelSheets = schematic->GetTopLevelSheets();
1613 bool loadedIsTopLevel = std::find( topLevelSheets.begin(), topLevelSheets.end(),
1614 loadedSheet ) != topLevelSheets.end();
1615 bool loadedIsVirtualRoot = loadedSheet == &schematic->Root()
1616 || loadedSheet->IsVirtualRootSheet();
1617
1618 if( !loadedIsTopLevel && !loadedIsVirtualRoot )
1619 schematic->SetTopLevelSheets( { loadedSheet } );
1620
1621 // Recompute connectivity so instance data is valid before saving, as importFile() does.
1622 std::unique_ptr<TOOL_MANAGER> toolManager = std::make_unique<TOOL_MANAGER>();
1623 toolManager->SetEnvironment( schematic.get(), nullptr, nullptr, Kiface().KifaceSettings(),
1624 nullptr );
1625
1626 {
1627 SCH_COMMIT dummyCommit( toolManager.get() );
1628 schematic->RecalculateConnections( &dummyCommit, GLOBAL_CLEANUP, toolManager.get() );
1629 }
1630
1631 schematic->SetSheetNumberAndCount();
1632
1633 if( SCH_SHEET* topSheet = schematic->GetTopLevelSheet() )
1634 topSheet->SetFileName( outputFn.GetFullName() );
1635
1636 schematic->RootScreen()->SetFileName( outputFn.GetFullPath() );
1637
1638 SCH_SCREENS screens( schematic->Root() );
1639
1640 std::unordered_map<SCH_SCREEN*, wxString> filenameMap;
1641 filenameMap[schematic->RootScreen()] = outputFn.GetFullPath();
1642
1643 wxString errorMsg;
1644
1645 if( !PrepareSaveAsFiles( *schematic, screens, inputFn, outputFn, /*aSaveCopy*/ true,
1646 /*aCopySubsheets*/ true, /*aIncludeExternSheets*/ true,
1647 filenameMap, errorMsg ) )
1648 {
1649 m_reporter->Report( errorMsg + wxS( "\n" ), RPT_SEVERITY_ERROR );
1651 }
1652
1653 // PrepareSaveAsFiles seeds an entry (empty for sheets it does not relocate) for every
1654 // screen; empty paths are skipped.
1655 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
1656
1657 for( size_t i = 0; i < screens.GetCount(); i++ )
1658 {
1659 SCH_SCREEN* screen = screens.GetScreen( i );
1660 wxString path = filenameMap[screen];
1661
1662 if( path.IsEmpty() )
1663 continue;
1664
1665 wxFileName fn( path );
1667
1668 pi->SaveSchematicFile( fn.GetFullPath(), screens.GetSheet( i ), schematic.get() );
1669 sheetCount++;
1670
1671 auto symbols = screen->Items().OfType( SCH_SYMBOL_T );
1672 symbolCount += std::distance( symbols.begin(), symbols.end() );
1673 }
1674 }
1675 catch( const IO_ERROR& ioe )
1676 {
1677 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1678 ioe.What() ),
1681 }
1682 catch( const std::exception& exc )
1683 {
1684 m_reporter->Report( wxString::Format( _( "Error saving imported schematic: %s\n" ),
1685 exc.what() ),
1688 }
1689
1690 m_reporter->Report( wxString::Format( _( "Successfully saved imported schematic to '%s'\n" ),
1691 outputFn.GetFullPath() ),
1693
1694 // Linked by the top-level `import` command's subsequent SaveProject().
1695 if( Pgm().GetSettingsManager().IsProjectOpenNotDummy() )
1696 {
1697 std::vector<FILE_INFO_PAIR>& projectSheets = project.GetProjectFile().GetSheets();
1698 projectSheets.clear();
1699
1700 for( const SCH_SHEET_PATH& sheetPath : schematic->Hierarchy() )
1701 {
1702 SCH_SHEET* sheet = sheetPath.Last();
1703
1704 if( sheet && !sheet->IsVirtualRootSheet() )
1705 projectSheets.emplace_back( std::make_pair( sheet->m_Uuid, sheet->GetName() ) );
1706 }
1707 }
1708
1710 {
1711 IMPORT_REPORT_DATA reportData;
1712
1713 reportData.m_sourceFile = inputFn.GetFullName();
1714 reportData.m_sourceFormat = formatName;
1715 reportData.m_outputFile = outputFn.GetFullName();
1716 reportData.m_statistics = {
1717 { wxS( "symbols" ), symbolCount },
1718 { wxS( "sheets" ), sheetCount }
1719 };
1720
1721 WriteImportReport( m_reporter, job->m_reportFormat, job->m_reportFile, reportData );
1722 }
1723
1725}
1726
1727
1729{
1730 DS_PROXY_VIEW_ITEM* drawingSheet =
1732 &aSch->RootScreen()->GetTitleBlock(), aSch->GetProperties() );
1733
1734 drawingSheet->SetPageNumber( TO_UTF8( aSch->RootScreen()->GetPageNumber() ) );
1735 drawingSheet->SetSheetCount( aSch->RootScreen()->GetPageCount() );
1736 drawingSheet->SetFileName( TO_UTF8( aSch->RootScreen()->GetFileName() ) );
1739 drawingSheet->SetIsFirstPage( aSch->RootScreen()->GetVirtualPageNumber() == 1 );
1740
1741 wxString currentVariant = aSch->GetCurrentVariant();
1742 wxString variantDesc = aSch->GetVariantDescription( currentVariant );
1743 drawingSheet->SetVariantName( TO_UTF8( currentVariant ) );
1744 drawingSheet->SetVariantDesc( TO_UTF8( variantDesc ) );
1745
1746 drawingSheet->SetSheetName( "" );
1747 drawingSheet->SetSheetPath( "" );
1748
1749 return drawingSheet;
1750}
1751
1752
1753// ============================================================================
1754// JobSchDiff: sch_diff implementation
1755// ============================================================================
1758#include <diff_merge/diff_scene.h>
1759#include <diff_merge/sch_differ.h>
1763#include <project/project_file.h>
1765#include <jobs/job_sch_diff.h>
1766#include <jobs/scratch_doc.h>
1767#include <wx/file.h>
1768
1769
1770// Load a schematic into a SCRATCH_DOC<SCHEMATIC> that keeps a dedicated scratch
1771// PROJECT attached for the document's lifetime. Without a per-document project,
1772// a second LoadProject(path, true) destroys the first project and the first
1773// schematic's m_project dangles. The destructor severs the link via
1774// SetProject( nullptr ). Shared by every SCH diff/merge job.
1776{
1778 aMgr, aPath,
1779 [aPath]( PROJECT* aProject )
1780 {
1781 return std::unique_ptr<SCHEMATIC>(
1783 /*aSetActive=*/false,
1784 /*aForceDefaultProject=*/false, aProject,
1785 /*aCalculateConnectivity=*/false ) );
1786 },
1787 []( SCHEMATIC* aSch )
1788 {
1789 aSch->SetProject( nullptr );
1790 } );
1791}
1792
1793
1795{
1796 JOB_SCH_DIFF* diffJob = dynamic_cast<JOB_SCH_DIFF*>( aJob );
1797
1798 if( !diffJob )
1800
1801 // Two schematics in the same SettingsManager need scratch PROJECTs;
1802 // otherwise the second LoadProject(path, true) destroys the first
1803 // project and the first schematic's m_project dangles, crashing on
1804 // any per-instance bbox / field / reference resolution (e.g. inside
1805 // SCH_DIFFER's makeDescriptor calling SCH_SYMBOL::GetRef).
1807
1810
1811 if( !a.doc )
1812 {
1813 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputA ), RPT_SEVERITY_ERROR );
1815 }
1816
1817 if( !b.doc )
1818 {
1819 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), diffJob->m_inputB ), RPT_SEVERITY_ERROR );
1821 }
1822
1823 SCHEMATIC* schA = a.doc.get();
1824 SCHEMATIC* schB = b.doc.get();
1825
1826 KICAD_DIFF::SCH_DIFFER differ( schA, schB, diffJob->m_inputB );
1828
1829 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1830
1831 if( diffJob->m_exitCodeOnly )
1832 return diffExitCode;
1833
1834 // The schematic geometry (wires, junctions, symbol/sheet/label bbox
1835 // outlines) renders beneath the change rectangles for PNG/SVG, matching
1836 // the interactive dialog.
1838 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1840 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1841 { return KICAD_DIFF::ExtractSchematicGeometry( *schA, aColor ); };
1842 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1843 { return KICAD_DIFF::ExtractSchematicGeometry( *schB, aColor ); };
1844
1845 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
1846}
1847
1848
1849// ============================================================================
1850// JobSymDiff: sym_diff implementation
1851// ============================================================================
1853#include <jobs/job_sym_diff.h>
1854
1855
1856// Load one side of a symbol-library diff into its owner vector and name map.
1857// When aAllowEmpty is set an empty path resolves to a clean (empty) side; the
1858// non-interactive job path leaves it unset so a missing path is an input error.
1859static int loadSymbolLibrarySide( const wxString& aPath,
1860 std::vector<std::unique_ptr<LIB_SYMBOL>>& aOwners,
1861 KICAD_DIFF::SYM_LIB_DIFFER::SYMBOL_MAP& aMap, bool aAllowEmpty,
1862 REPORTER& aReporter )
1863{
1864 if( aAllowEmpty && aPath.IsEmpty() )
1866
1867 try
1868 {
1869 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
1870 aOwners = std::move( loaded.first );
1871 aMap = std::move( loaded.second );
1873 }
1874 catch( const IO_ERROR& ioe )
1875 {
1876 aReporter.Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
1878 }
1879 catch( const std::exception& e )
1880 {
1881 aReporter.Report(
1882 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
1884 }
1885
1887}
1888
1889
1890// Flatten a symbol-library name map into a single DOCUMENT_GEOMETRY tinted with
1891// the supplied per-side theme colour.
1894{
1896
1897 for( const auto& [name, symbol] : aMap )
1898 {
1899 if( symbol )
1900 KICAD_DIFF::AppendGeometry( geometry, KICAD_DIFF::ExtractSymbolGeometry( *symbol, aColor ) );
1901 }
1902
1903 return geometry;
1904}
1905
1906
1908{
1909 JOB_SYM_DIFF* diffJob = dynamic_cast<JOB_SYM_DIFF*>( aJob );
1910
1911 if( !diffJob )
1913
1914 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
1915 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
1918
1919 if( int rc = loadSymbolLibrarySide( diffJob->m_inputA, ownersA, mapA, false, *m_reporter );
1921 {
1922 return rc;
1923 }
1924
1925 if( int rc = loadSymbolLibrarySide( diffJob->m_inputB, ownersB, mapB, false, *m_reporter );
1927 {
1928 return rc;
1929 }
1930
1931 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, diffJob->m_inputB );
1933
1934 int diffExitCode = KICAD_DIFF::DiffExitCode( result );
1935
1936 if( diffJob->m_exitCodeOnly )
1937 return diffExitCode;
1938
1940 KICAD_DIFF::MakeEmitOptions( *diffJob, diffJob->m_inputA, diffJob->m_inputB );
1942 emitOpts.referenceGeometry = [&]( const KIGFX::COLOR4D& aColor )
1943 { return symbolLibraryGeometry( mapA, aColor ); };
1944 emitOpts.comparisonGeometry = [&]( const KIGFX::COLOR4D& aColor )
1945 { return symbolLibraryGeometry( mapB, aColor ); };
1946
1947 return KICAD_DIFF::EmitDiffResult( result, emitOpts, diffExitCode, *m_reporter );
1948}
1949
1950
1951// ============================================================================
1952// JobOpenDiffDialog: load two on-disk files and open DIALOG_KICAD_DIFF.
1953// Dispatched from the project manager / PR-review dialog via KIWAY.
1954// ============================================================================
1958#include <jobs/scratch_doc.h>
1959
1960
1962 const wxString& aFileB, const wxString& aLabelA,
1963 const wxString& aLabelB, wxWindow* aParent,
1964 REPORTER* aReporter )
1965{
1966 // Restore m_reporter on scope exit so a caller's transient (often
1967 // stack-local) reporter doesn't outlive this call as a dangling member.
1969 aReporter ? aReporter : m_reporter );
1970
1971 wxWindow* parent = aParent ? aParent : ( wxTheApp ? wxTheApp->GetTopWindow() : nullptr );
1972
1975 KICAD_DIFF::DOCUMENT_GEOMETRY compGeometry;
1976
1977 switch( aKind )
1978 {
1980 {
1982
1985
1986 if( !a.doc && !aFileA.IsEmpty() )
1987 {
1988 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileA ), RPT_SEVERITY_ERROR );
1990 }
1991
1992 if( !b.doc && !aFileB.IsEmpty() )
1993 {
1994 m_reporter->Report( wxString::Format( _( "Failed to load %s\n" ), aFileB ), RPT_SEVERITY_ERROR );
1996 }
1997
1998 // Synthesize empty SCHEMATICs against scratch PROJECTs for any
1999 // missing side so SCH_DIFFER sees a valid empty document.
2000 PROJECT scratchPrjA;
2001 PROJECT scratchPrjB;
2002 std::unique_ptr<SCHEMATIC> emptyA;
2003 std::unique_ptr<SCHEMATIC> emptyB;
2004
2005 if( !a.doc )
2006 {
2007 emptyA = std::make_unique<SCHEMATIC>( &scratchPrjA );
2008 emptyA->CreateDefaultScreens();
2009 }
2010
2011 if( !b.doc )
2012 {
2013 emptyB = std::make_unique<SCHEMATIC>( &scratchPrjB );
2014 emptyB->CreateDefaultScreens();
2015 }
2016
2017 SCHEMATIC* schA = a.doc ? a.doc.get() : emptyA.get();
2018 SCHEMATIC* schB = b.doc ? b.doc.get() : emptyB.get();
2019
2020 KICAD_DIFF::SCH_DIFFER differ( schA, schB, aFileB );
2021 result = differ.Diff();
2022
2023 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2024 refGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schA, theme.reference );
2025 compGeometry = KICAD_DIFF::ExtractSchematicGeometry( *schB, theme.comparison );
2026
2027 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2028 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2029
2031 parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ),
2032 [schA, schB, color = theme.reference]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
2033 {
2034 SCH_SCREEN* refScreen = schA ? schA->RootScreen() : nullptr;
2035 SCH_SCREEN* compScreen = schB ? schB->RootScreen() : nullptr;
2036
2037 if( !aSheetPath.empty() )
2038 {
2039 if( schA )
2040 {
2041 if( auto sp = schA->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2042 refScreen = sp->LastScreen();
2043 }
2044
2045 if( schB )
2046 {
2047 if( auto sp = schB->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
2048 compScreen = sp->LastScreen();
2049 }
2050 }
2051
2052 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, schA, schB, color, {}, {}, {}, refScreen,
2053 compScreen );
2054 } );
2055 dlg.ShowModal();
2056
2057 if( emptyA )
2058 emptyA->SetProject( nullptr );
2059
2060 if( emptyB )
2061 emptyB->SetProject( nullptr );
2062
2064 }
2066 {
2067 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersA;
2068 std::vector<std::unique_ptr<LIB_SYMBOL>> ownersB;
2071
2072 if( int rc = loadSymbolLibrarySide( aFileA, ownersA, mapA, true, *m_reporter );
2074 {
2075 return rc;
2076 }
2077
2078 if( int rc = loadSymbolLibrarySide( aFileB, ownersB, mapB, true, *m_reporter );
2080 {
2081 return rc;
2082 }
2083
2084 KICAD_DIFF::SYM_LIB_DIFFER differ( mapA, mapB, aFileB );
2085 result = differ.Diff();
2086
2087 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2088 refGeometry = symbolLibraryGeometry( mapA, theme.reference );
2089 compGeometry = symbolLibraryGeometry( mapB, theme.comparison );
2090 break;
2091 }
2092 default:
2093 m_reporter->Report( _( "Unsupported document kind for this dispatcher.\n" ), RPT_SEVERITY_ERROR );
2095 }
2096
2097 const wxString labelA = aLabelA.IsEmpty() ? aFileA : aLabelA;
2098 const wxString labelB = aLabelB.IsEmpty() ? aFileB : aLabelB;
2099
2100 DIALOG_KICAD_DIFF dlg( parent, labelA, labelB, result, std::move( refGeometry ), std::move( compGeometry ) );
2101 dlg.ShowModal();
2102
2104}
2105
2106
2107// ============================================================================
2108// JobSchMerge: sch_merge implementation
2109// ============================================================================
2112#include <jobs/scratch_doc.h>
2113
2114
2115int EESCHEMA_JOBS_HANDLER::RunMerge( KICAD_DIFF::DOC_KIND aKind, const wxString& aAncestor,
2116 const wxString& aOurs, const wxString& aTheirs,
2117 const wxString& aOutput, bool aInteractive, bool aSingleFile,
2118 REPORTER* aReporter )
2119{
2120 // Restore m_reporter on scope exit so a caller's transient (often
2121 // stack-local) reporter doesn't outlive this call as a dangling member.
2123 aReporter ? aReporter : m_reporter );
2124
2125 if( aKind == KICAD_DIFF::DOC_KIND::SYM_LIB )
2126 return runSymLibMerge( aAncestor, aOurs, aTheirs, aOutput );
2127
2128 return runSchMerge( aAncestor, aOurs, aTheirs, aOutput, aInteractive );
2129}
2130
2131
2132int EESCHEMA_JOBS_HANDLER::runSchMerge( const wxString& aAncestor, const wxString& aOurs,
2133 const wxString& aTheirs, const wxString& aOutput,
2134 bool aInteractive )
2135{
2137
2138 SCRATCH_DOC<SCHEMATIC> ancestor = loadScratchSchematic( mgr, aAncestor );
2139 SCRATCH_DOC<SCHEMATIC> ours = loadScratchSchematic( mgr, aOurs );
2140 SCRATCH_DOC<SCHEMATIC> theirs = loadScratchSchematic( mgr, aTheirs );
2141
2142 if( !ancestor.doc || !ours.doc || !theirs.doc )
2143 {
2144 m_reporter->Report( _( "Failed to load one or more input schematics\n" ), RPT_SEVERITY_ERROR );
2146 }
2147
2148 // Multi-sheet hierarchies are supported: each non-root sub-sheet is
2149 // written alongside the output root using its original basename. Top-level
2150 // sheets stay singular — multiple roots is an editor invariant the diff
2151 // engine never models, so refuse those.
2152 auto hasSingleRoot = []( const SCHEMATIC* aSch )
2153 {
2154 return aSch->GetTopLevelSheets().size() == 1;
2155 };
2156
2157 if( !hasSingleRoot( ancestor.doc.get() ) || !hasSingleRoot( ours.doc.get() ) || !hasSingleRoot( theirs.doc.get() ) )
2158 {
2159 m_reporter->Report( _( "sch merge requires each input to have a single top-level sheet\n" ),
2162 }
2163
2164 KICAD_DIFF::SCH_DIFFER ourDiff( ancestor.doc.get(), ours.doc.get() );
2165 KICAD_DIFF::SCH_DIFFER theirDiff( ancestor.doc.get(), theirs.doc.get() );
2166
2167 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2168 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2169
2171 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2172
2173 // A cancelled dialog leaves plan unresolved and falls through to the
2174 // marker flow below.
2175 if( aInteractive && !plan.Resolved() )
2176 {
2177 if( !Pgm().IsGUI() )
2178 {
2179 m_reporter->Report( _( "--interactive requires a GUI KiCad process; the console "
2180 "kicad-cli cannot open dialogs.\n" ),
2183 }
2184
2185 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2187
2188 if( ancestor.doc )
2190
2191 if( ours.doc )
2193
2194 if( theirs.doc )
2196
2198 KICAD_DIFF::CollectChangeBBoxes( theirDocDiff, ctx.theirsBBoxes );
2199
2200 DIALOG_KICAD_MERGE_3WAY dlg( wxTheApp->GetTopWindow(), plan, std::move( ctx ) );
2201
2202 if( dlg.ShowModal() == wxID_APPLY )
2203 plan = dlg.GetResolvedPlan();
2204 }
2205
2206 // Snapshot of the plan before the applier moves it; drives the
2207 // unresolved-conflict report below.
2208 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2209
2210 KICAD_DIFF::SCH_MERGE_APPLIER applier( ancestor.doc.get(), ours.doc.get(), theirs.doc.get(), std::move( plan ) );
2211
2212 if( !applier.Apply() )
2213 {
2214 m_reporter->Report( _( "Merge applier failed to produce a schematic\n" ), RPT_SEVERITY_ERROR );
2216 }
2217
2218 // Sheet add/remove/replace resolutions are explicitly skipped by
2219 // SCH_MERGE_APPLIER (see isSheetItem); succeeding here would silently
2220 // drop hierarchy edits.
2221 if( applier.GetReport().sheetActionsSkipped > 0 )
2222 {
2223 m_reporter->Report( _( "Merge contains hierarchical sheet structure changes that sch merge "
2224 "cannot apply\n" ),
2227 }
2228
2229 // Refusal above guarantees a single top-level sheet.
2230 SCH_SHEET* rootSheet = ancestor.doc->GetTopLevelSheet( 0 );
2231
2232 wxFileName outFn( aOutput );
2233 outFn.MakeAbsolute();
2234
2235 // Sub-sheets land alongside the root by basename. Preserving the original
2236 // relative-path subdirectory structure would force kicad-cli sch merge to
2237 // mkdir into user space; the basename-flat scheme is what makes the common
2238 // git-mergetool case work without surprises.
2239 const wxString outDir = outFn.GetPath();
2240
2241 SCH_SCREENS screens( rootSheet );
2242 SCH_SCREEN* rootScreen = rootSheet->GetScreen();
2243
2244 // Detect two sub-sheets sharing a basename (e.g., a/foo.kicad_sch and
2245 // b/foo.kicad_sch) before any I/O — the flat output layout can't honor
2246 // both, and silently overwriting one is the worst outcome.
2247 std::map<wxString, SCH_SCREEN*> basenameOwner;
2248
2249 for( size_t i = 0; i < screens.GetCount(); ++i )
2250 {
2251 SCH_SCREEN* screen = screens.GetScreen( i );
2252
2253 if( !screen || screen == rootScreen )
2254 continue;
2255
2256 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2257
2258 if( basename.IsEmpty() )
2259 continue;
2260
2261 auto [it, inserted] = basenameOwner.emplace( basename, screen );
2262
2263 if( !inserted && it->second != screen )
2264 {
2265 m_reporter->Report( wxString::Format( _( "Cannot flatten sub-sheets with duplicate "
2266 "basename '%s'\n" ),
2267 basename ),
2270 }
2271 }
2272
2273 // Rewrite every SCH_SHEET symbol's filename field to its child screen's
2274 // basename, so the root file (and any intermediate sheet) references the
2275 // flattened layout we're about to write.
2276 for( size_t i = 0; i < screens.GetCount(); ++i )
2277 {
2278 SCH_SCREEN* parent = screens.GetScreen( i );
2279
2280 if( !parent )
2281 continue;
2282
2283 for( SCH_ITEM* item : parent->Items().OfType( SCH_SHEET_T ) )
2284 {
2285 SCH_SHEET* childRef = static_cast<SCH_SHEET*>( item );
2286 SCH_SCREEN* childScreen = childRef->GetScreen();
2287
2288 if( !childScreen || childScreen == rootScreen )
2289 continue;
2290
2291 const wxString basename = wxFileName( childScreen->GetFileName() ).GetFullName();
2292
2293 if( !basename.IsEmpty() )
2294 childRef->SetFileName( basename );
2295 }
2296 }
2297
2298 try
2299 {
2300 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
2301 pi->SaveSchematicFile( outFn.GetFullPath(), rootSheet, ancestor.doc.get() );
2302
2303 for( size_t i = 0; i < screens.GetCount(); ++i )
2304 {
2305 SCH_SCREEN* screen = screens.GetScreen( i );
2306 SCH_SHEET* sheet = screens.GetSheet( i );
2307
2308 if( !screen || !sheet || screen == rootScreen )
2309 continue;
2310
2311 const wxString basename = wxFileName( screen->GetFileName() ).GetFullName();
2312
2313 if( basename.IsEmpty() )
2314 continue;
2315
2316 wxFileName outSubFn( outDir, basename );
2317 pi->SaveSchematicFile( outSubFn.GetFullPath(), sheet, ancestor.doc.get() );
2318 }
2319 }
2320 catch( const IO_ERROR& ioe )
2321 {
2322 m_reporter->Report( wxString::Format( _( "Failed to save merged schematic: %s\n" ), ioe.What() ),
2325 }
2326
2327 // If the applier mutated project-file-scoped state (ERC severities, etc),
2328 // persist it as a sibling .kicad_pro alongside the .kicad_sch output —
2329 // otherwise the resolution dies with the process. Only write when actually
2330 // needed; clobbering an existing .kicad_pro with ancestor's project
2331 // would lose unrelated user settings (library tables, mru paths).
2332 if( applier.GetReport().projectFileTouched && ancestor.project )
2333 {
2334 wxFileName proFn = outFn;
2335 proFn.SetExt( FILEEXT::ProjectFileExtension );
2336
2337 // JSON-patch path: only the diffed DOC_PROP fields are written into
2338 // the output .kicad_pro, so any non-diffed user customisations
2339 // (library tables, last paths, layer presets, text variables) are
2340 // preserved. Fall back to SaveProjectCopy on parse failure.
2341 PROJECT_FILE& ancProj = ancestor.project->GetProjectFile();
2342 ancProj.Store();
2343
2344 const KICAD_DIFF::SCH_MERGE_APPLIER::REPORT& mergeReport = applier.GetReport();
2345
2346 // PROJECT_FILE::Store() flushes the project file's own params but not
2347 // its registered NESTED_SETTINGS. Flush only the resolved nested
2348 // settings so Internals() reflects the merge result without touching
2349 // unrelated project subtrees.
2350 if( mergeReport.ercSeveritiesTouched && ancestor.doc )
2351 ancestor.doc->ErcSettings().SaveToFile( wxEmptyString, true );
2352
2353 if( mergeReport.drawingSheetFileTouched && ancestor.doc )
2354 ancestor.doc->Settings().SaveToFile( wxEmptyString, true );
2355
2356 std::set<wxString> touched;
2357 if( mergeReport.ercSeveritiesTouched )
2358 touched.insert( KICAD_DIFF::DOC_PROP_ERC_SEVERITIES );
2359
2360 if( mergeReport.drawingSheetFileTouched )
2361 touched.insert( KICAD_DIFF::DOC_PROP_DRAWING_SHEET );
2362
2363 if( !KICAD_DIFF::ApplyProjectFilePatches( proFn.GetFullPath(), *ancProj.Internals(), touched,
2365 {
2366 if( !Pgm().GetSettingsManager().SaveProjectCopy( proFn.GetFullPath(), ancestor.project ) )
2367 {
2368 m_reporter->Report(
2369 wxString::Format( _( "Failed to save merged project file: %s\n" ), proFn.GetFullPath() ),
2372 }
2373 }
2374 }
2375
2376 // Surface post-apply validator findings (refdes collisions, schema
2377 // mismatch, missed connectivity rebuild). Advisory — they do not change the
2378 // exit code, only the merge's resolved/unresolved status does.
2380 m_reporter->Report( wxString::Format( wxS( "%s: %s\n" ), f.validator, f.message ), f.severity );
2381
2382 // The merged schematic was written to m_outputPath above, so the output is
2383 // always valid. Unresolved conflicts are reported and signalled via the
2384 // exit code; the user resolves them with the interactive mergetool.
2385 if( !planSnapshot.Resolved() )
2386 {
2387 m_reporter->Report( wxString::Format( _( "Merge completed with %zu unresolved conflict(s) in %s\n" ),
2388 planSnapshot.ConflictCount(), aOutput ),
2391 }
2392
2394}
2395
2396
2397// ============================================================================
2398// JobSymLibMerge: 3-way merge of .kicad_sym libraries.
2399// ============================================================================
2402#include <wx/ffile.h>
2403
2404
2405int EESCHEMA_JOBS_HANDLER::runSymLibMerge( const wxString& aAncestor, const wxString& aOurs,
2406 const wxString& aTheirs, const wxString& aOutput )
2407{
2408 if( aOutput.IsEmpty() )
2409 {
2410 m_reporter->Report( _( "--output is required\n" ), RPT_SEVERITY_ERROR );
2412 }
2413
2414 // Three sides into name -> LIB_SYMBOL maps.
2415 struct LIB_SIDE
2416 {
2417 std::vector<std::unique_ptr<LIB_SYMBOL>> owners;
2419 };
2420
2421 LIB_SIDE ancestor, ours, theirs;
2422
2423 auto loadSide = [&]( const wxString& aPath, LIB_SIDE& aSide ) -> int
2424 {
2425 try
2426 {
2427 auto loaded = KICAD_DIFF::SYM_LIB_DIFFER::LoadLibrary( aPath );
2428 aSide.owners = std::move( loaded.first );
2429 aSide.map = std::move( loaded.second );
2431 }
2432 catch( const IO_ERROR& ioe )
2433 {
2434 m_reporter->Report( wxString::Format( _( "Failed to load %s: %s\n" ), aPath, ioe.What() ),
2436 }
2437 catch( const std::exception& e )
2438 {
2439 m_reporter->Report(
2440 wxString::Format( _( "Failed to load %s: %s\n" ), aPath, wxString::FromUTF8( e.what() ) ),
2442 }
2443
2445 };
2446
2447 if( int rc = loadSide( aAncestor, ancestor ); rc != CLI::EXIT_CODES::SUCCESS )
2448 return rc;
2449
2450 if( int rc = loadSide( aOurs, ours ); rc != CLI::EXIT_CODES::SUCCESS )
2451 return rc;
2452
2453 if( int rc = loadSide( aTheirs, theirs ); rc != CLI::EXIT_CODES::SUCCESS )
2454 return rc;
2455
2456 KICAD_DIFF::SYM_LIB_DIFFER ourDiff( ancestor.map, ours.map, aOurs );
2457 KICAD_DIFF::SYM_LIB_DIFFER theirDiff( ancestor.map, theirs.map, aTheirs );
2458
2459 KICAD_DIFF::DOCUMENT_DIFF ourDocDiff = ourDiff.Diff();
2460 KICAD_DIFF::DOCUMENT_DIFF theirDocDiff = theirDiff.Diff();
2461
2463 KICAD_DIFF::MERGE_PLAN plan = engine.Plan( ourDocDiff, theirDocDiff );
2464
2465 const KICAD_DIFF::MERGE_PLAN planSnapshot = plan;
2466
2467 KICAD_DIFF::SYM_LIB_MERGE_APPLIER applier( ancestor.map, ours.map, theirs.map, std::move( plan ) );
2468 std::vector<std::unique_ptr<LIB_SYMBOL>> merged = applier.Apply();
2469
2470 // Per-property symbol merge isn't implemented; MERGE_PROPS resolutions are
2471 // downgraded to TAKE_OURS. Surface that as unresolved so the user sees a
2472 // marker instead of silent partial-merge.
2473 const bool hadSilentFallback = applier.GetReport().mergePropsFallback > 0;
2474
2475 // Serialize via the sexpr lib cache: create at output path, add each
2476 // merged symbol, save. The cache owns its symbols once added; clone
2477 // before handing off so the applier's unique_ptrs stay intact for the
2478 // post-save report.
2479 wxFileName outFn( aOutput );
2480 outFn.MakeAbsolute();
2481
2482 try
2483 {
2484 SCH_IO_KICAD_SEXPR_LIB_CACHE cache( outFn.GetFullPath() );
2485
2486 // SCH_IO_LIB_CACHE::AddSymbol takes ownership of the raw pointer; the
2487 // cache destructor deletes from m_symbols. Release the unique_ptrs so
2488 // we don't double-free.
2489 for( auto& sym : merged )
2490 {
2491 if( sym )
2492 cache.AddSymbol( sym.release() );
2493 }
2494
2495 cache.SetModified( true );
2496 cache.Save();
2497 }
2498 catch( const IO_ERROR& ioe )
2499 {
2500 m_reporter->Report( wxString::Format( _( "Failed to save merged symbol library: %s\n" ), ioe.What() ),
2503 }
2504
2505 // The merged library was saved above, so the output is always valid.
2506 if( !planSnapshot.Resolved() || hadSilentFallback )
2507 {
2508 // Conflict count = engine-unresolved ∪ applier-downgraded (deduped, so
2509 // an item that was both unresolved and silently downgraded counts once).
2510 std::set<KIID_PATH> conflicts( planSnapshot.unresolved.begin(), planSnapshot.unresolved.end() );
2511
2512 for( const KIID_PATH& id : applier.GetReport().mergePropsFallbackIds )
2513 conflicts.insert( id );
2514
2515 m_reporter->Report( wxString::Format( _( "Symbol library merge completed with %zu unresolved "
2516 "conflict(s) in %s\n" ),
2517 conflicts.size(), aOutput ),
2520 }
2521
2523}
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:2480
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
wxString GetSelectedVariant() const
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:157
void AddOutput(wxString aOutputPath)
Definition job.h:216
wxString GetFullOutputPath(PROJECT *aProject) const
Returns the full output path for the job, taking into account the configured output path,...
Definition job.cpp:150
bool GetOutputPathIsDirectory() const
Definition job.h:258
wxString GetConfiguredOutputPath() const
Returns the configured output path for the job.
Definition job.h:235
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition job.h:204
void SetWorkingOutputPath(const wxString &aPath)
Sets a transient output path for the job, it takes priority over the configured output path when GetF...
Definition job.h:241
const std::map< wxString, wxString > & GetVarOverrides() const
Definition job.h:197
JSON_SETTINGS_INTERNALS * Internals()
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
Three-way merge plan generator.
MERGE_PLAN Plan(const DOCUMENT_DIFF &aAncestorOurs, const DOCUMENT_DIFF &aAncestorTheirs) const
Plan the merge given the canonical pair of diffs.
const REPORT & GetReport() const
std::vector< std::unique_ptr< ITEM > > Apply()
Diff two already-parsed SCHEMATICs and produce a DOCUMENT_DIFF.
Definition sch_differ.h:55
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
Materialize a MERGE_PLAN into a merged SCHEMATIC by mutating the ancestor in place.
bool Apply()
Apply the plan to the ancestor.
const REPORT & GetReport() const
Diff two .kicad_sym symbol libraries.
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
static std::pair< std::vector< std::unique_ptr< LIB_SYMBOL > >, SYMBOL_MAP > LoadLibrary(const wxString &aPath)
Convenience: load a .kicad_sym path into a SYMBOL_MAP using SCH_IO_KICAD_SEXPR::EnumerateSymbolLib.
std::map< wxString, const LIB_SYMBOL * > SYMBOL_MAP
Library content is a map of (canonical_name -> LIB_SYMBOL*).
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
void SetDefaultPenWidth(int aWidth)
void SetGapLengthRatio(double aRatio)
void SetDashLengthRatio(double aRatio)
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h: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:80
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:197
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:142
const std::vector< wxString > & GetBodyStyleNames() const
Definition lib_symbol.h:834
bool HasDeMorganBodyStyles() const override
Definition lib_symbol.h:831
int GetBodyStyleCount() const override
Definition lib_symbol.h:823
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:458
bool IsGeneratedField(const wxString &aSource)
Returns true if the string is generated, e.g contains a single text var reference.
Definition common.cpp:470
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.