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