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