KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_inspection_tool.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) 2019-2023 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU 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
23
24#include <sch_symbol.h>
25#include <id.h>
26#include <kiway.h>
27#include <kiplatform/ui.h>
28#include <confirm.h>
29#include <string_utils.h>
33#include <tools/sch_actions.h>
35#include <tools/sch_selection.h>
36#include <sim/simulator_frame.h>
37#include <sch_edit_frame.h>
38#include <symbol_edit_frame.h>
39#include <symbol_viewer_frame.h>
40#include <eda_doc.h>
41#include <sch_marker.h>
42#include <project.h>
43#include <project_sch.h>
45#include <dialogs/dialog_erc.h>
50#include <math/util.h> // for KiROUND
51
57#include <eeschema_helpers.h>
58#include <schematic.h>
60#include <local_history.h>
62#include <wx/filedlg.h>
63#include <wx/filename.h>
64
65
67 SCH_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.InspectionTool" ), m_busSyntaxHelp( nullptr )
68{
69}
70
71
73{
75
76 // Add inspection actions to the selection tool menu
77 //
78 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
79
81
84
85 return true;
86}
87
88
90{
91 SCH_TOOL_BASE::Reset( aReason );
92
93 if( aReason == SUPERMODEL_RELOAD || aReason == RESET_REASON::SHUTDOWN )
94 {
95 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY );
96
97 wxQueueEvent( m_frame, evt );
98 }
99}
100
101
103{
105 return 0;
106}
107
108
110{
111 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
112
113 wxCHECK( frame, /* void */ );
114
115 DIALOG_ERC* dlg = frame->GetErcDialog();
116
117 wxCHECK( dlg, /* void */ );
118
119 // Needed at least on Windows. Raise() is not enough
120 dlg->Show( true );
121
122 // Bring it to the top if already open. Dual monitor users need this.
123 dlg->Raise();
124
125 if( wxButton* okButton = dynamic_cast<wxButton*>( dlg->FindWindow( wxID_OK ) ) )
126 {
127 KIPLATFORM::UI::ForceFocus( okButton );
128 okButton->SetDefault();
129 }
130}
131
132
134{
135 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
136
137 wxCHECK( frame, 0 );
138
139 DIALOG_ERC* dlg = frame->GetErcDialog();
140
141 if( dlg )
142 {
143 dlg->Show( true );
144 dlg->Raise();
145 dlg->PrevMarker();
146 }
147
148 return 0;
149}
150
151
153{
154 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
155
156 wxCHECK( frame, 0 );
157
158 DIALOG_ERC* dlg = frame->GetErcDialog();
159
160 wxCHECK( dlg, 0 );
161
162 dlg->Show( true );
163 dlg->Raise();
164 dlg->NextMarker();
165
166 return 0;
167}
168
169
171{
172 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
173
174 wxCHECK( selectionTool, 0 );
175
176 SCH_SELECTION& selection = selectionTool->GetSelection();
177
178 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
179 {
180 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
181 DIALOG_ERC* dlg = frame ? frame->GetErcDialog() : nullptr;
182
183 if( dlg && dlg->IsShownOnScreen() )
184 dlg->SelectMarker( static_cast<SCH_MARKER*>( selection.Front() ) );
185 }
186
187 // Show the item info on a left click on this item
188 UpdateMessagePanel( aEvent );
189
190 return 0;
191}
192
193
195{
196 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
197
198 wxCHECK( frame, /* void */ );
199
200 DIALOG_ERC* dlg = frame->GetErcDialog();
201
202 if( dlg )
203 {
204 if( !dlg->IsShownOnScreen() )
205 {
206 dlg->Show( true );
207 dlg->Raise();
208 }
209
210 dlg->SelectMarker( aMarker );
211 }
212}
213
214
215wxString SCH_INSPECTION_TOOL::InspectERCErrorMenuText( const std::shared_ptr<RC_ITEM>& aERCItem )
216{
217 if( aERCItem->GetErrorCode() == ERCE_BUS_TO_NET_CONFLICT )
218 {
219 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::showBusSyntaxHelp );
220 }
221 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
222 {
223 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::diffSymbol );
224 }
225
226 return wxEmptyString;
227}
228
229
230void SCH_INSPECTION_TOOL::InspectERCError( const std::shared_ptr<RC_ITEM>& aERCItem )
231{
232 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
233
234 wxCHECK( frame, /* void */ );
235
236 EDA_ITEM* a = frame->ResolveItem( aERCItem->GetMainItemID() );
237
238 if( aERCItem->GetErrorCode() == ERCE_BUS_TO_NET_CONFLICT )
239 {
241 }
242 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
243 {
244 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( a ) )
245 DiffSymbol( symbol );
246 }
247}
248
249
251{
252 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
253 SCH_SELECTION& selection = selTool->GetSelection();
254 SCH_MARKER* marker = nullptr;
255
256 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
257 marker = static_cast<SCH_MARKER*>( selection.Front() );
258
259 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
260
261 wxCHECK( frame, 0 );
262
263 DIALOG_ERC* dlg = frame->GetErcDialog();
264
265 wxCHECK( dlg, 0 );
266
267 // Let the ERC dialog handle it since it owns the marker provider's cached counts and view
268 // updates. If marker is nullptr the dialog excludes whichever marker is selected in the
269 // dialog itself.
270 dlg->ExcludeMarker( marker );
271
272 return 0;
273}
274
275
276extern void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
277 int aGridForPins, UNITS_PROVIDER* aUnitsProvider );
278
280{
281 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
282
283 if( !symbol )
284 return 0;
285
286 std::vector<wxString> messages;
287 const int grid_size = KiROUND( getView()->GetGAL()->GetGridSize().x );
288
289 CheckLibSymbol( symbol, messages, grid_size, m_frame );
290
291 if( messages.empty() )
292 {
293 DisplayInfoMessage( m_frame, _( "No symbol issues found." ) );
294 }
295 else
296 {
297 HTML_MESSAGE_BOX dlg( m_frame, _( "Symbol Warnings" ) );
298
299 for( const wxString& single_msg : messages )
300 dlg.AddHTML_Text( single_msg );
301
302 dlg.ShowModal();
303 }
304
305 return 0;
306}
307
308
310{
311 if( m_busSyntaxHelp )
312 {
313 m_busSyntaxHelp->Raise();
314 m_busSyntaxHelp->Show( true );
315 return 0;
316 }
317
319 return 0;
320}
321
322
324{
325 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
326
327 wxCHECK( schEditorFrame, 0 );
328
329 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
330
331 if( selection.Empty() )
332 {
333 m_frame->ShowInfoBarError( _( "Select a symbol to diff against its library equivalent." ) );
334 return 0;
335 }
336
337 DiffSymbol( static_cast<SCH_SYMBOL*>( selection.Front() ) );
338 return 0;
339}
340
341
343{
344 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
345
346 wxCHECK( schEditorFrame, /* void */ );
347
348 DIALOG_BOOK_REPORTER* dialog = schEditorFrame->GetSymbolDiffDialog();
349
350 wxCHECK( dialog, /* void */ );
351
352 dialog->DeleteAllPages();
353 dialog->SetUserItemID( symbol->m_Uuid );
354
355 wxString symbolDesc = wxString::Format( _( "Symbol %s" ),
356 symbol->GetField( FIELD_T::REFERENCE )->GetText() );
357 LIB_ID libId = symbol->GetLibId();
358 wxString libName = libId.GetLibNickname();
359 wxString symbolName = libId.GetLibItemName();
360
361 WX_HTML_REPORT_BOX* r = dialog->AddHTMLPage( _( "Summary" ) );
362
363 r->Report( wxS( "<h7>" ) + _( "Schematic vs library diff for:" ) + wxS( "</h7>" ) );
364 r->Report( wxS( "<ul><li>" ) + EscapeHTML( symbolDesc ) + wxS( "</li>" )
365 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
366 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( symbolName )
367 + wxS( "</li></ul>" ) );
368
369 r->Report( "" );
370
372
373 if( !libs->HasLibrary( libName, false ) )
374 {
375 r->Report( _( "The library is not included in the current configuration." )
376 + wxS( "&nbsp;&nbsp;&nbsp" )
377 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
378 }
379 else if( !libs->HasLibrary( libName, true ) )
380 {
381 r->Report( _( "The library is not enabled in the current configuration." )
382 + wxS( "&nbsp;&nbsp;&nbsp" )
383 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
384 }
385 else
386 {
387 std::unique_ptr<LIB_SYMBOL> flattenedLibSymbol;
388 std::unique_ptr<LIB_SYMBOL> flattenedSchSymbol = symbol->GetLibSymbolRef()->Flatten();
389
390 try
391 {
392 if( LIB_SYMBOL* libAlias = libs->LoadSymbol( libName, symbolName ) )
393 flattenedLibSymbol = libAlias->Flatten();
394 }
395 catch( const IO_ERROR& )
396 {
397 }
398
399 if( !flattenedLibSymbol )
400 {
401 r->Report( wxString::Format( _( "The library no longer contains the item %s." ),
402 symbolName ) );
403 }
404 else
405 {
406 std::vector<SCH_FIELD> fields;
407
408 for( SCH_FIELD& field : symbol->GetFields() )
409 {
410 fields.emplace_back( SCH_FIELD( flattenedLibSymbol.get(), field.GetId(),
411 field.GetName( false ) ) );
412 fields.back().CopyText( field );
413 fields.back().SetAttributes( field );
414 fields.back().Move( -symbol->GetPosition() );
415 }
416
417 flattenedSchSymbol->SetFields( fields );
418
419 if( flattenedSchSymbol->Compare( *flattenedLibSymbol, SCH_ITEM::COMPARE_FLAGS::ERC,
420 r ) == 0 )
421 {
422 r->Report( _( "No relevant differences detected." ) );
423 }
424
425 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
426 SYMBOL_DIFF_WIDGET* diff = constructDiffPanel( panel );
427
428 diff->DisplayDiff( flattenedSchSymbol.release(), flattenedLibSymbol.release(),
429 symbol->GetUnit(), symbol->GetBodyStyle() );
430 }
431 }
432
433 r->Flush();
434
435 dialog->Raise();
436 dialog->Show( true );
437}
438
439
441{
442 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
443
444 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
445 SYMBOL_DIFF_WIDGET* diffWidget = new SYMBOL_DIFF_WIDGET( aParentPanel, backend );
446
447 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
448 aParentPanel->SetSizer( sizer );
449 aParentPanel->Layout();
450
451 return diffWidget;
452}
453
454
455namespace
456{
457void buildSchOverrides( const KICAD_DIFF::DOCUMENT_DIFF& aDiff, const KICAD_DIFF::DIFF_COLOR_THEME& aTheme,
458 std::map<KIID, KIGFX::COLOR4D>& aRefO, std::map<KIID, KIGFX::COLOR4D>& aCompO,
459 std::map<KIID, KICAD_DIFF::CATEGORY>& aCats )
460{
461 aRefO.clear();
462 aCompO.clear();
463 aCats.clear();
464
465 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> visit = [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
466 {
467 if( !aChange.id.empty() )
468 {
469 const KIID& kiid = aChange.id.back();
470 aCats[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
471
472 switch( aChange.kind )
473 {
474 case KICAD_DIFF::CHANGE_KIND::ADDED: aCompO[kiid] = aTheme.added; break;
476 aRefO[kiid] = aTheme.removed;
477 aCompO[kiid] = aTheme.removed;
478 break;
480 aRefO[kiid] = aTheme.modified;
481 aCompO[kiid] = aTheme.modified;
482 break;
483 default:
484 aRefO[kiid] = aTheme.conflict;
485 aCompO[kiid] = aTheme.conflict;
486 break;
487 }
488 }
489
490 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
491 visit( child );
492 };
493
494 for( const KICAD_DIFF::ITEM_CHANGE& change : aDiff.changes )
495 visit( change );
496}
497
498
499DIALOG_KICAD_DIFF::SHEET_SWITCHER makeSchSwitcher( SCHEMATIC* aRef, SCHEMATIC* aComp,
500 std::map<KIID, KIGFX::COLOR4D> aRefOverrides,
501 std::map<KIID, KIGFX::COLOR4D> aCompOverrides,
502 std::map<KIID, KICAD_DIFF::CATEGORY> aCategories,
503 const KICAD_DIFF::DIFF_COLOR_THEME& aTheme )
504{
505 auto holder = std::make_shared<std::vector<std::unique_ptr<SCH_ITEM>>>();
506
507 return [aRef, aComp, modifiedColor = aTheme.modified, removedColor = aTheme.removed,
508 refO = std::move( aRefOverrides ), compO = std::move( aCompOverrides ), cats = std::move( aCategories ),
509 holder]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
510 {
511 SCH_SCREEN* compScreen = aComp->RootScreen();
512 SCH_SCREEN* refScreen = aRef->RootScreen();
513
514 if( !aSheetPath.empty() )
515 {
516 if( auto sp = aComp->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
517 compScreen = sp->LastScreen();
518
519 if( auto sp = aRef->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
520 refScreen = sp->LastScreen();
521 }
522
523 holder->clear();
524
525 if( refScreen )
526 {
527 for( const auto& [kiid, c] : refO )
528 {
529 if( c != removedColor )
530 continue;
531
532 for( SCH_ITEM* item : refScreen->Items() )
533 {
534 if( item && item->m_Uuid == kiid )
535 {
536 if( auto* clone = dynamic_cast<SCH_ITEM*>( item->Clone() ) )
537 holder->emplace_back( clone );
538
539 break;
540 }
541 }
542 }
543 }
544
545 std::vector<KIGFX::VIEW_ITEM*> extras;
546
547 for( const auto& clone : *holder )
548 extras.push_back( clone.get() );
549
550 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, nullptr, aComp, modifiedColor, compO, extras, cats, nullptr,
551 compScreen );
552 };
553}
554} // namespace
555
556
558{
559 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
560
561 wxCHECK( schEditorFrame, 0 );
562
563 wxFileDialog dlg( schEditorFrame, _( "Choose Schematic to Compare With" ), wxEmptyString,
565 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
566
567 if( dlg.ShowModal() != wxID_OK )
568 return 0;
569
570 wxFileName otherFn( dlg.GetPath() );
571 otherFn.MakeAbsolute();
572
573 if( !otherFn.GetExt().IsSameAs( FILEEXT::KiCadSchematicFileExtension, false ) )
574 {
575 schEditorFrame->ShowInfoBarError(
576 _( "Select a KiCad s-expression schematic file (.kicad_sch)." ) );
577 return 0;
578 }
579
580 const wxString otherPath = otherFn.GetFullPath();
581
582 wxFileName projectFn = otherFn;
583 projectFn.SetExt( FILEEXT::ProjectFileExtension );
584 const wxString projectPath = projectFn.GetFullPath();
585
586 wxFileName activeProjectFn( schEditorFrame->Prj().GetProjectFullName() );
587 activeProjectFn.MakeAbsolute();
588
589 // Refuse the self-compare; attaching the active PROJECT to a second
590 // SCHEMATIC would clobber its ERC/schematic settings.
591 if( projectFn.SameAs( activeProjectFn ) )
592 {
593 schEditorFrame->ShowInfoBarError(
594 _( "Select a schematic file from another project to compare." ) );
595 return 0;
596 }
597
598 return showSchematicComparison( otherPath, projectPath, otherPath );
599}
600
601
602int SCH_INSPECTION_TOOL::showSchematicComparison( const wxString& aOtherPath, const wxString& aProjectPath,
603 const wxString& aComparisonLabel )
604{
605 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
606
607 wxCHECK( schEditorFrame, 0 );
608
609 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
610
611 wxCHECK( mgr, 0 );
612
613 // A missing .kicad_pro is fine (LoadProject returns false but still
614 // inserts a defaults-only slot); a present-but-malformed .kicad_pro is
615 // treated as failure.
616 bool projectLoadOk = mgr->LoadProject( aProjectPath, false );
617 PROJECT* otherPrj = mgr->GetProject( aProjectPath );
618
619 if( !otherPrj )
620 {
621 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
622 return 0;
623 }
624
625 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
626 {
627 mgr->UnloadProject( otherPrj, false );
628 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
629 return 0;
630 }
631
632 // SCH_DIFFER walks the sheet hierarchy and properties; connectivity build
633 // is unnecessary and slow for a read-only compare.
634 SCHEMATIC* loadedSchematic = nullptr;
635
636 try
637 {
638 loadedSchematic = EESCHEMA_HELPERS::LoadSchematic( aOtherPath,
639 /*aSetActive=*/false,
640 /*aForceDefaultProject=*/false, otherPrj,
641 /*aCalculateConnectivity=*/false );
642 }
643 catch( ... )
644 {
645 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
646 mgr->UnloadProject( otherPrj, false );
647 return 0;
648 }
649
650 SCHEMATIC* mySch = &schEditorFrame->Schematic();
651
652 // Belt-and-suspenders: EESCHEMA_HELPERS::LoadSchematic has a fallback
653 // branch that returns the active editor's schematic when the project
654 // happens to be the active one. The path guard above should have caught
655 // this, but a path-alias miss would leave the unique_ptr owning the live
656 // schematic and SetProject(nullptr) would destroy editor state.
657 if( loadedSchematic == mySch )
658 {
659 schEditorFrame->ShowInfoBarError(
660 _( "Select a schematic file from another project to compare." ) );
661 mgr->UnloadProject( otherPrj, false );
662 return 0;
663 }
664
665 std::unique_ptr<SCHEMATIC> otherSchematic{ loadedSchematic };
666
667 if( !otherSchematic )
668 {
669 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
670 mgr->UnloadProject( otherPrj, false );
671 return 0;
672 }
673
674 KICAD_DIFF::SCH_DIFFER differ( mySch, otherSchematic.get(), aOtherPath );
675
676 // Scope to the editor's current sheet. Comparison resolves the matching
677 // sheet by KIID when present, else falls back to its root.
678 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
679 const SCH_SHEET_LIST otherSheets = otherSchematic->BuildSheetListSortedByPageNumbers();
680 KIID_PATH scopeAfter;
681
682 if( auto sp = otherSchematic->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
683 scopeAfter = sp->Path();
684 else if( !otherSheets.empty() )
685 scopeAfter = otherSheets.front().Path();
686
687 if( !scopeBefore.empty() && !scopeAfter.empty() )
688 differ.SetScope( scopeBefore, scopeAfter );
689
691
693
694 std::map<KIID, KIGFX::COLOR4D> refOverrides;
695 std::map<KIID, KIGFX::COLOR4D> compOverrides;
696 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
697
698 buildSchOverrides( result, theme, refOverrides, compOverrides, kiidCategories );
699
701 KICAD_DIFF::ExtractSchematicGeometry( *mySch, theme.reference, refOverrides );
702 KICAD_DIFF::DOCUMENT_GEOMETRY compGeometry =
703 KICAD_DIFF::ExtractSchematicGeometry( *otherSchematic, theme.comparison, compOverrides );
704
705 auto initialSwitcher =
706 makeSchSwitcher( mySch, otherSchematic.get(), refOverrides, compOverrides, kiidCategories, theme );
707
708 KIID_PATH initialSheet = schEditorFrame->GetCurrentSheet().Path();
709 SCH_SCREEN* currentSheetScreen = schEditorFrame->GetCurrentSheet().LastScreen();
710 wxString referenceLabel = currentSheetScreen ? currentSheetScreen->GetFileName() : mySch->GetFileName();
711
712 DIALOG_KICAD_DIFF dlgDiff( schEditorFrame, referenceLabel, aComparisonLabel, result, std::move( refGeometry ),
713 std::move( compGeometry ), std::move( initialSwitcher ), std::move( initialSheet ) );
714
715 // Drill state owns the comparison schematics loaded across double-click
716 // drills. The editor schematic stays the reference on all drills.
717 struct DRILL_STATE
718 {
719 SCH_SHEET_PATH editorPath;
720 SCHEMATIC* compSch;
721 wxString compFile;
722 std::vector<std::unique_ptr<SCHEMATIC>> ownedSchs;
723 };
724
725 DRILL_STATE drillState;
726 drillState.editorPath = schEditorFrame->GetCurrentSheet();
727 drillState.compSch = otherSchematic.get();
728 drillState.compFile = aOtherPath;
729 drillState.ownedSchs.push_back( std::move( otherSchematic ) );
730
731 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff.DiffCanvas() )
732 {
733 canvas->SetDoubleClickHandler(
734 [&dlgDiff, &drillState, mySch, otherPrj, theme, schEditorFrame]( KIGFX::VIEW_ITEM* aItem )
735 {
736 auto* sheet = dynamic_cast<SCH_SHEET*>( aItem );
737
738 if( !sheet )
739 return;
740
741 const wxString sheetFile = sheet->GetFileName();
742
743 if( sheetFile.IsEmpty() )
744 return;
745
746 KIID_PATH newEditorKiid = drillState.editorPath.Path();
747 newEditorKiid.push_back( sheet->m_Uuid );
748
749 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
750
751 if( !newEditorSheet )
752 return;
753
754 // Prefer resolving inside the current comparison schematic so
755 // shared-sheet instance context is preserved on both sides.
756 SCHEMATIC* compSch = drillState.compSch;
757 wxString compFile = drillState.compFile;
758 KIID_PATH scopeBefore = newEditorKiid;
759 KIID_PATH scopeAfter;
760
761 if( auto compMatch = compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
762 {
763 scopeAfter = compMatch->Path();
764
765 if( SCH_SCREEN* compMatchScreen = compMatch->LastScreen() )
766 compFile = compMatchScreen->GetFileName();
767 }
768 else
769 {
770 wxFileName newCompFn( wxFileName( drillState.compFile ).GetPath(), sheetFile );
771 newCompFn.MakeAbsolute();
772
773 SCHEMATIC* loaded =
774 EESCHEMA_HELPERS::LoadSchematic( newCompFn.GetFullPath(), /*aSetActive=*/false,
775 /*aForceDefaultProject=*/false, otherPrj,
776 /*aCalculateConnectivity=*/false );
777
778 if( !loaded )
779 {
780 schEditorFrame->ShowInfoBarError(
781 wxString::Format( _( "Failed to load %s" ), newCompFn.GetFullPath() ) );
782 return;
783 }
784
785 const SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
786
787 if( !loadedSheets.empty() )
788 scopeAfter = loadedSheets.front().Path();
789
790 drillState.ownedSchs.emplace_back( loaded );
791 compSch = loaded;
792 compFile = newCompFn.GetFullPath();
793 }
794
795 KICAD_DIFF::SCH_DIFFER newDiffer( mySch, compSch, compFile );
796
797 if( !scopeBefore.empty() && !scopeAfter.empty() )
798 newDiffer.SetScope( scopeBefore, scopeAfter );
799
800 KICAD_DIFF::DOCUMENT_DIFF newDiff = newDiffer.Diff();
801
802 std::map<KIID, KIGFX::COLOR4D> newRefO;
803 std::map<KIID, KIGFX::COLOR4D> newCompO;
804 std::map<KIID, KICAD_DIFF::CATEGORY> newCats;
805 buildSchOverrides( newDiff, theme, newRefO, newCompO, newCats );
806
807 auto newSwitcher = makeSchSwitcher( mySch, compSch, newRefO, newCompO, newCats, theme );
808
809 drillState.editorPath = *newEditorSheet;
810 drillState.compSch = compSch;
811 drillState.compFile = compFile;
812
813 SCH_SCREEN* newRefScreen = newEditorSheet->LastScreen();
814 wxString newRefLabel = newRefScreen ? newRefScreen->GetFileName() : wxString();
815
816 dlgDiff.Reload( newRefLabel, compFile, std::move( newDiff ),
817 /*aReferenceGeometry=*/{}, /*aComparisonGeometry=*/{}, std::move( newSwitcher ),
818 scopeBefore );
819 } );
820 }
821
822 dlgDiff.ShowModal();
823
824 // Detach schematics from the project before unloading so the project's
825 // ERC and schematic settings release cleanly.
826 for( auto& sch : drillState.ownedSchs )
827 {
828 if( sch )
829 sch->SetProject( nullptr );
830 }
831
832 drillState.ownedSchs.clear();
833
834 mgr->UnloadProject( otherPrj, false );
835
836 return 0;
837}
838
839
841{
842 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
843
844 wxCHECK( schEditorFrame, 0 );
845
846 SCHEMATIC* mySch = &schEditorFrame->Schematic();
847
848 if( mySch->GetFileName().IsEmpty() )
849 {
850 schEditorFrame->ShowInfoBarError( _( "Save the schematic before comparing against local history." ) );
851 return 0;
852 }
853
854 const wxString projectPath = schEditorFrame->Prj().GetProjectPath();
855 LOCAL_HISTORY& history = schEditorFrame->Kiway().LocalHistory();
856
857 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
858
859 if( snapshots.empty() )
860 {
861 schEditorFrame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
862 return 0;
863 }
864
865 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
866
867 wxCHECK( mgr, 0 );
868
869 auto relTo = [&]( const wxString& aFull )
870 {
871 wxFileName fn( aFull );
872 fn.MakeRelativeTo( projectPath );
873 return fn.GetFullPath( wxPATH_UNIX );
874 };
875
876 const wxString rootRel = relTo( mySch->GetFileName() );
877 const wxString projRel = relTo( schEditorFrame->Prj().GetProjectFullName() );
878
879 // One entry per distinct schematic version: newest-first, skipping commits
880 // with no schematic or whose schematic content (any .kicad_sch sheet) matches
881 // the previously kept one. This drops board-only saves like "PCB Save".
882 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> filtered;
883 wxString prevFingerprint;
884
885 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
886 {
887 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_sch" ) );
888
889 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
890 continue;
891
892 prevFingerprint = fingerprint;
893 filtered.push_back( s );
894 }
895
896 if( filtered.empty() )
897 {
898 schEditorFrame->ShowInfoBarError( _( "No local history snapshots change this schematic." ) );
899 return 0;
900 }
901
902 snapshots = std::move( filtered );
903
904 std::vector<wxString> labels;
905
906 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
907 {
908 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
909 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
910 }
911
913 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
914
915 struct SCH_DIFF_VIEW
916 {
921 };
922
923 auto buildView = [&]( SCHEMATIC* aComp, const wxString& aPath ) -> SCH_DIFF_VIEW
924 {
925 SCH_DIFF_VIEW view;
926 KICAD_DIFF::SCH_DIFFER differ( mySch, aComp, aPath );
927
928 KIID_PATH scopeAfter;
929
930 if( auto sp = aComp->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
931 {
932 scopeAfter = sp->Path();
933 }
934 else
935 {
937
938 if( !sheets.empty() )
939 scopeAfter = sheets.front().Path();
940 }
941
942 if( !scopeBefore.empty() && !scopeAfter.empty() )
943 differ.SetScope( scopeBefore, scopeAfter );
944
945 view.result = differ.Diff();
946
947 std::map<KIID, KIGFX::COLOR4D> refO;
948 std::map<KIID, KIGFX::COLOR4D> compO;
949 std::map<KIID, KICAD_DIFF::CATEGORY> cats;
950 buildSchOverrides( view.result, theme, refO, compO, cats );
951
952 view.refGeom = KICAD_DIFF::ExtractSchematicGeometry( *mySch, theme.reference, refO );
953 view.compGeom = KICAD_DIFF::ExtractSchematicGeometry( *aComp, theme.comparison, compO );
954 view.switcher = makeSchSwitcher( mySch, aComp, refO, compO, cats, theme );
955
956 return view;
957 };
958
959 // State for the revision currently shown. Swapped on each dropdown change.
960 std::unique_ptr<SCHEMATIC> curSch;
961 PROJECT* curPrj = nullptr;
962 wxString curTempDir;
963
964 // Drill state: which comparison sheet is shown and sub-schematics loaded on
965 // double-click. Reset when the revision changes.
966 SCH_SHEET_PATH drillEditorPath = schEditorFrame->GetCurrentSheet();
967 SCHEMATIC* drillCompSch = nullptr;
968 wxString drillCompFile;
969 std::vector<std::unique_ptr<SCHEMATIC>> drilledSchs;
970
971 auto cleanupCurrent = [&]()
972 {
973 for( auto& sch : drilledSchs )
974 {
975 if( sch )
976 sch->SetProject( nullptr );
977 }
978
979 drilledSchs.clear();
980
981 if( curSch )
982 {
983 curSch->SetProject( nullptr );
984 curSch.reset();
985 }
986
987 // Skip if the project was already evicted from the manager.
988 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
989 mgr->UnloadProject( curPrj, false );
990
991 curPrj = nullptr;
992
993 if( !curTempDir.IsEmpty() )
994 {
995 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
996 curTempDir.Clear();
997 }
998 };
999
1000 // Extract the hierarchy at snapshot aIndex and load its root sheet, cleaning
1001 // up the temp dir on any failure.
1002 auto loadRevision = [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir ) -> bool
1003 {
1004 const wxString hash = snapshots[aIndex].hash;
1005 wxFileName dirFn;
1006 dirFn.AssignDir( wxFileName::GetTempDir() );
1007 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
1008 const wxString tempDir = dirFn.GetPath();
1009
1010 // Extract just the schematic sheets and project file, skipping the
1011 // board, 3D models, gerbers, etc.
1012 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
1013 { wxS( ".kicad_sch" ), wxS( ".kicad_pro" ) } ) )
1014 {
1015 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1016 return false;
1017 }
1018
1019 const wxString root = tempDir + wxS( "/" ) + rootRel;
1020 const wxString proj = tempDir + wxS( "/" ) + projRel;
1021
1022 mgr->LoadProject( proj, false );
1023 PROJECT* prj = mgr->GetProject( proj );
1024
1025 if( !prj )
1026 {
1027 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1028 return false;
1029 }
1030
1031 SCHEMATIC* loaded = nullptr;
1032
1033 try
1034 {
1035 loaded = EESCHEMA_HELPERS::LoadSchematic( root, /*aSetActive=*/false, /*aForceDefaultProject=*/false, prj,
1036 /*aCalculateConnectivity=*/false );
1037 }
1038 catch( ... )
1039 {
1040 mgr->UnloadProject( prj, false );
1041 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1042 return false;
1043 }
1044
1045 if( !loaded || loaded == mySch )
1046 {
1047 mgr->UnloadProject( prj, false );
1048 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1049 return false;
1050 }
1051
1052 aSch.reset( loaded );
1053 aPrj = prj;
1054 aTempDir = tempDir;
1055 return true;
1056 };
1057
1058 auto loadView = [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir,
1059 SCH_DIFF_VIEW& aView ) -> bool
1060 {
1061 if( !loadRevision( aIndex, aSch, aPrj, aTempDir ) )
1062 return false;
1063
1064 try
1065 {
1066 aView = buildView( aSch.get(), aTempDir + wxS( "/" ) + rootRel );
1067 }
1068 catch( ... )
1069 {
1070 aSch->SetProject( nullptr );
1071 aSch.reset();
1072 mgr->UnloadProject( aPrj, false );
1073 aPrj = nullptr;
1074 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
1075 aTempDir.Clear();
1076 return false;
1077 }
1078
1079 return true;
1080 };
1081
1082 SCH_DIFF_VIEW view;
1083 int startIndex = 0;
1084
1085 if( !loadView( 0, curSch, curPrj, curTempDir, view ) )
1086 {
1087 schEditorFrame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
1088 return 0;
1089 }
1090
1091 // Default to the first commit back from HEAD that actually differs from the
1092 // current schematic, so opening lands on real changes rather than an in-sync HEAD.
1093 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
1094 {
1095 std::unique_ptr<SCHEMATIC> nextSch;
1096 PROJECT* nextPrj = nullptr;
1097 wxString nextTempDir;
1098 SCH_DIFF_VIEW nextView;
1099
1100 if( !loadView( startIndex + 1, nextSch, nextPrj, nextTempDir, nextView ) )
1101 break;
1102
1103 cleanupCurrent();
1104 view = std::move( nextView );
1105 curSch = std::move( nextSch );
1106 curPrj = nextPrj;
1107 curTempDir = nextTempDir;
1108 startIndex++;
1109 }
1110
1111 drillCompSch = curSch.get();
1112 drillCompFile = curTempDir + wxS( "/" ) + rootRel;
1113
1114 SCH_SCREEN* curScreen = schEditorFrame->GetCurrentSheet().LastScreen();
1115 wxString referenceLabel = curScreen ? curScreen->GetFileName() : mySch->GetFileName();
1116
1117 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( schEditorFrame, referenceLabel, labels[startIndex], view.result,
1118 view.refGeom, view.compGeom, view.switcher, scopeBefore );
1119
1120 // Double-click a sheet to drill into its sub-schematic, within the current
1121 // revision's extracted hierarchy.
1122 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff->DiffCanvas() )
1123 {
1124 canvas->SetDoubleClickHandler(
1125 [&]( KIGFX::VIEW_ITEM* aItem )
1126 {
1127 auto* sheet = dynamic_cast<SCH_SHEET*>( aItem );
1128
1129 if( !sheet || sheet->GetFileName().IsEmpty() )
1130 return;
1131
1132 KIID_PATH newEditorKiid = drillEditorPath.Path();
1133 newEditorKiid.push_back( sheet->m_Uuid );
1134
1135 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
1136
1137 if( !newEditorSheet )
1138 return;
1139
1140 SCHEMATIC* compSch = drillCompSch;
1141 wxString compFile = drillCompFile;
1142 KIID_PATH drillScopeBefore = newEditorKiid;
1143 KIID_PATH drillScopeAfter;
1144
1145 if( auto compMatch = compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
1146 {
1147 drillScopeAfter = compMatch->Path();
1148
1149 if( SCH_SCREEN* matchScreen = compMatch->LastScreen() )
1150 compFile = matchScreen->GetFileName();
1151 }
1152 else
1153 {
1154 wxFileName subFn( wxFileName( drillCompFile ).GetPath(), sheet->GetFileName() );
1155 subFn.MakeAbsolute();
1156
1157 SCHEMATIC* loaded = nullptr;
1158
1159 try
1160 {
1161 loaded = EESCHEMA_HELPERS::LoadSchematic( subFn.GetFullPath(), /*aSetActive=*/false,
1162 /*aForceDefaultProject=*/false, curPrj,
1163 /*aCalculateConnectivity=*/false );
1164 }
1165 catch( ... )
1166 {
1167 loaded = nullptr;
1168 }
1169
1170 if( !loaded || loaded == mySch )
1171 {
1172 schEditorFrame->ShowInfoBarError(
1173 wxString::Format( _( "Failed to load %s" ), subFn.GetFullPath() ) );
1174 return;
1175 }
1176
1177 SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
1178
1179 if( !loadedSheets.empty() )
1180 drillScopeAfter = loadedSheets.front().Path();
1181
1182 drilledSchs.emplace_back( loaded );
1183 compSch = loaded;
1184 compFile = subFn.GetFullPath();
1185 }
1186
1187 try
1188 {
1189 KICAD_DIFF::SCH_DIFFER newDiffer( mySch, compSch, compFile );
1190
1191 if( !drillScopeBefore.empty() && !drillScopeAfter.empty() )
1192 newDiffer.SetScope( drillScopeBefore, drillScopeAfter );
1193
1194 KICAD_DIFF::DOCUMENT_DIFF newDiff = newDiffer.Diff();
1195
1196 std::map<KIID, KIGFX::COLOR4D> newRefO;
1197 std::map<KIID, KIGFX::COLOR4D> newCompO;
1198 std::map<KIID, KICAD_DIFF::CATEGORY> newCats;
1199 buildSchOverrides( newDiff, theme, newRefO, newCompO, newCats );
1200
1201 auto newSwitcher = makeSchSwitcher( mySch, compSch, newRefO, newCompO, newCats, theme );
1202
1203 SCH_SCREEN* newRefScreen = newEditorSheet->LastScreen();
1204 wxString newRefLabel = newRefScreen ? newRefScreen->GetFileName() : wxString();
1205
1206 dlgDiff->Reload( newRefLabel, compFile, std::move( newDiff ), /*aReferenceGeometry=*/{},
1207 /*aComparisonGeometry=*/{}, std::move( newSwitcher ), drillScopeBefore );
1208 }
1209 catch( ... )
1210 {
1211 schEditorFrame->ShowInfoBarError( _( "Could not open this sheet for comparison." ) );
1212 return;
1213 }
1214
1215 drillEditorPath = *newEditorSheet;
1216 drillCompSch = compSch;
1217 drillCompFile = compFile;
1218 } );
1219 }
1220
1221 dlgDiff->SetRevisionChooser( labels, startIndex,
1222 [&]( int aIndex )
1223 {
1224 std::unique_ptr<SCHEMATIC> newSch;
1225 PROJECT* newPrj = nullptr;
1226 wxString newTempDir;
1227 SCH_DIFF_VIEW newView;
1228
1229 if( !loadView( aIndex, newSch, newPrj, newTempDir, newView ) )
1230 {
1231 schEditorFrame->ShowInfoBarError(
1232 _( "Could not compare against the selected snapshot." ) );
1233 return;
1234 }
1235
1236 dlgDiff->Reload( referenceLabel, labels[aIndex], newView.result, newView.refGeom,
1237 newView.compGeom, newView.switcher, scopeBefore );
1238
1239 cleanupCurrent();
1240 view = std::move( newView );
1241 curSch = std::move( newSch );
1242 curPrj = newPrj;
1243 curTempDir = newTempDir;
1244
1245 // Restart drilling from the new revision's root.
1246 drillEditorPath = schEditorFrame->GetCurrentSheet();
1247 drillCompSch = curSch.get();
1248 drillCompFile = curTempDir + wxS( "/" ) + rootRel;
1249 } );
1250
1251 dlgDiff->ShowModal();
1252
1253 // Destroy the dialog before the schematics it references are freed.
1254 dlgDiff.reset();
1255
1256 cleanupCurrent();
1257 return 0;
1258}
1259
1260
1262{
1263 SIMULATOR_FRAME* simFrame = (SIMULATOR_FRAME*) m_frame->Kiway().Player( FRAME_SIMULATOR, true );
1264
1265 if( !simFrame )
1266 return -1;
1267
1268 if( wxWindow* blocking_win = simFrame->Kiway().GetBlockingDialog() )
1269 blocking_win->Close( true );
1270
1271 simFrame->Show( true );
1272
1273 // On Windows, Raise() does not bring the window on screen, when iconized
1274 if( simFrame->IsIconized() )
1275 simFrame->Iconize( false );
1276
1277 simFrame->Raise();
1278
1279 return 0;
1280}
1281
1282
1284{
1285 wxString datasheet;
1286 std::vector<EMBEDDED_FILES*> filesStack;
1287
1288 if( m_frame->IsType( FRAME_SCH_SYMBOL_EDITOR ) )
1289 {
1290 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1291
1292 if( !symbol )
1293 return 0;
1294
1295 datasheet = symbol->GetDatasheetField().GetText();
1296 filesStack.push_back( symbol );
1297 }
1298 else if( m_frame->IsType( FRAME_SCH_VIEWER ) )
1299 {
1300 LIB_SYMBOL* entry = static_cast<SYMBOL_VIEWER_FRAME*>( m_frame )->GetSelectedSymbol();
1301
1302 if( !entry )
1303 return 0;
1304
1305 datasheet = entry->GetDatasheetField().GetText();
1306 filesStack.push_back( entry );
1307 }
1308 else if( m_frame->IsType( FRAME_SCH ) )
1309 {
1310 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
1311
1312 if( selection.Empty() )
1313 return 0;
1314
1315 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
1316 SCH_FIELD* field = symbol->GetField( FIELD_T::DATASHEET );
1317
1318 // Use GetShownText() to resolve any text variables, but don't allow adding extra text
1319 // (ie: the field name)
1320 datasheet = field->GetShownText( &symbol->Schematic()->CurrentSheet(), false );
1321 filesStack.push_back( symbol->Schematic() );
1322
1323 if( symbol->GetLibSymbolRef() )
1324 filesStack.push_back( symbol->GetLibSymbolRef().get() );
1325 }
1326
1327 if( datasheet.IsEmpty() || datasheet == wxS( "~" ) )
1328 {
1329 m_frame->ShowInfoBarError( _( "No datasheet defined." ) );
1330 }
1331 else
1332 {
1334 PROJECT_SCH::SchSearchS( &m_frame->Prj() ), filesStack );
1335 }
1336
1337 return 0;
1338}
1339
1340
1342{
1343 SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1344 SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
1345 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1346 SCH_SELECTION& selection = selTool->GetSelection();
1347
1348 // Note: the symbol viewer manages its own message panel
1349
1350 if( symbolEditFrame || schEditFrame )
1351 {
1352 if( selection.GetSize() == 1 )
1353 {
1354 EDA_ITEM* item = (EDA_ITEM*) selection.Front();
1355 std::vector<MSG_PANEL_ITEM> msgItems;
1356
1357 if( std::optional<wxString> uuid = GetMsgPanelDisplayUuid( item->m_Uuid ) )
1358 msgItems.emplace_back( _( "UUID" ), *uuid );
1359
1360 item->GetMsgPanelInfo( m_frame, msgItems );
1361 m_frame->SetMsgPanel( msgItems );
1362 }
1363 else
1364 {
1365 m_frame->ClearMsgPanel();
1366 }
1367 }
1368
1369 if( schEditFrame )
1370 {
1371 schEditFrame->UpdateNetHighlightStatus();
1372 schEditFrame->UpdateHierarchySelection();
1373 }
1374
1375 return 0;
1376}
1377
1378
1380{
1384 // See note 1:
1388
1396
1398
1399 // Note 1: tUpdateMessagePanel is called by CrossProbe. So uncomment this line if
1400 // call to CrossProbe is modifiied
1401 // Go( &SCH_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::SelectedEvent );
1405}
1406
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION excludeMarker
Definition actions.h:125
static TOOL_ACTION nextMarker
Definition actions.h:124
static TOOL_ACTION showDatasheet
Definition actions.h:263
static TOOL_ACTION prevMarker
Definition actions.h:123
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
wxPanel * AddBlankPage(const wxString &aTitle)
WX_HTML_REPORT_BOX * AddHTMLPage(const wxString &aTitle)
void SetUserItemID(const KIID &aID)
void ExcludeMarker(SCH_MARKER *aMarker=nullptr)
Exclude aMarker from the ERC list.
void SelectMarker(const SCH_MARKER *aMarker)
void PrevMarker()
void NextMarker()
File-compare dialog (Phase 7).
std::function< void(WIDGET_DIFF_CANVAS &, const KIID_PATH &)> SHEET_SWITCHER
WIDGET_DIFF_CANVAS * DiffCanvas() const
void Reload(const wxString &aReferencePath, const wxString &aComparisonPath, KICAD_DIFF::DOCUMENT_DIFF aDiff, KICAD_DIFF::DOCUMENT_GEOMETRY aReferenceGeometry, KICAD_DIFF::DOCUMENT_GEOMETRY aComparisonGeometry, SHEET_SWITCHER aSheetSwitcher, KIID_PATH aInitialSheet)
Swap in a fresh diff with new schematics.
bool Show(bool show) override
int ShowModal() override
SETTINGS_MANAGER * GetSettingsManager() const
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, INFOBAR_MESSAGE_TYPE aType=INFOBAR_MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
virtual void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Populate aList of MSG_PANEL_ITEM objects with it's internal state for display purposes.
Definition eda_item.h:230
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true)
static const TOOL_EVENT ClearedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedEvent
Definition actions.h:341
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:348
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:340
static const TOOL_EVENT UnselectedEvent
Definition actions.h:342
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Diff two already-parsed SCHEMATICs and produce a DOCUMENT_DIFF.
Definition sch_differ.h:55
void SetScope(const KIID_PATH &aBeforeScope, const KIID_PATH &aAfterScope)
Restrict the diff to one sheet on each side.
DOCUMENT_DIFF Diff() override
Produce a DOCUMENT_DIFF of the inputs the concrete differ was constructed with.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
Definition kiid.h:44
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
wxWindow * GetBlockingDialog()
Gets the window pointer to the blocking dialog (to send it signals)
Definition kiway.cpp:686
LOCAL_HISTORY & LocalHistory()
Return the LOCAL_HISTORY associated with this KIWAY.
Definition kiway.h:422
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Define a library symbol object.
Definition lib_symbol.h:80
SCH_FIELD & GetDatasheetField()
Return reference to the datasheet field.
Definition lib_symbol.h:390
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
Simple local history manager built on libgit2.
std::vector< LOCAL_HISTORY_SNAPSHOT_INFO > GetSnapshots(const wxString &aProjectPath)
Snapshots (commits) for the project, newest first.
wxString TreeFingerprint(const wxString &aProjectPath, const wxString &aHash, const wxString &aExtension)
Fingerprint of all files ending in aExtension recorded by commit aHash (sorted path:blob pairs).
bool ExtractAllFilesAtCommit(const wxString &aProjectPath, const wxString &aHash, const wxString &aDestDir, const std::vector< wxString > &aExtensions={})
Write files recorded at aHash into aDestDir, recreating the project's relative folder structure.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
static SEARCH_STACK * SchSearchS(PROJECT *aProject)
Accessor for Eeschema search stack.
Container for project specific data.
Definition project.h:62
virtual const wxString GetProjectFullName() const
Return the full path and name of the project.
Definition project.cpp:177
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:183
Holds all the data relating to one schematic.
Definition schematic.h:90
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
wxString GetFileName() const
Helper to retrieve the filename from the root sheet screen.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:189
static TOOL_ACTION compareSchematicWithHistory
static TOOL_ACTION showBusSyntaxHelp
static TOOL_ACTION checkSymbol
static TOOL_ACTION compareSchematicWithFile
static TOOL_ACTION showSimulator
static TOOL_ACTION runERC
Inspection and Editing.
static TOOL_ACTION diffSymbol
A shim class between EDA_DRAW_FRAME and several derived classes: SYMBOL_EDIT_FRAME,...
static SELECTION_CONDITION SingleSymbol
static SELECTION_CONDITION SingleNonExcludedMarker
Schematic editor (Eeschema) main window.
void UpdateHierarchySelection()
Update the hierarchy navigation tree selection (cross-probe from schematic to hierarchy pane).
SCH_SHEET_PATH & GetCurrentSheet() const
SCHEMATIC & Schematic() const
DIALOG_BOOK_REPORTER * GetSymbolDiffDialog()
DIALOG_ERC * GetErcDialog()
EDA_ITEM * ResolveItem(const KIID &aId, bool aAllowNullptrReturn=false) const override
Fetch an item by KIID.
void UpdateNetHighlightStatus()
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
int CompareSchematicWithHistory(const TOOL_EVENT &aEvent)
Diff the current schematic against the most recent local-history commit.
void InspectERCError(const std::shared_ptr< RC_ITEM > &aERCItem)
bool Init() override
Init() is called once upon a registration of the tool.
SYMBOL_DIFF_WIDGET * constructDiffPanel(wxPanel *aParentPanel)
This method is meant to be overridden in order to specify handlers for events.
int NextMarker(const TOOL_EVENT &aEvent)
int DiffSymbol(const TOOL_EVENT &aEvent)
int RunERC(const TOOL_EVENT &aEvent)
HTML_MESSAGE_BOX * m_busSyntaxHelp
int RunSimulation(const TOOL_EVENT &aEvent)
int ShowDatasheet(const TOOL_EVENT &aEvent)
wxString InspectERCErrorMenuText(const std::shared_ptr< RC_ITEM > &aERCItem)
int ExcludeMarker(const TOOL_EVENT &aEvent)
int CheckSymbol(const TOOL_EVENT &aEvent)
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int UpdateMessagePanel(const TOOL_EVENT &aEvent)
Display the selected item info (when clicking on a item)
int PrevMarker(const TOOL_EVENT &aEvent)
int showSchematicComparison(const wxString &aOtherPath, const wxString &aProjectPath, const wxString &aComparisonLabel)
Diff the schematic at aOtherPath against the live one and show the dialog.
int CompareSchematicWithFile(const TOOL_EVENT &aEvent)
Diff the current schematic against a user-selected .kicad_sch file.
int CrossProbe(const TOOL_EVENT &aEvent)
Called when clicking on a item:
int ShowBusSyntaxHelp(const TOOL_EVENT &aEvent)
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:162
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:268
int GetBodyStyle() const
Definition sch_item.h:242
int GetUnit() const
Definition sch_item.h:233
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const wxString & GetFileName() const
Definition sch_screen.h:150
SCH_SELECTION & GetSelection()
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
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.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:370
Schematic symbol object.
Definition sch_symbol.h:69
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:913
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:158
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:177
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
static HTML_MESSAGE_BOX * ShowSyntaxHelp(wxWindow *aParentWindow)
bool Init() override
Init() is called once upon a registration of the tool.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
SCH_TOOL_BASE(const std::string &aName)
SCH_SELECTION_TOOL * m_selectionTool
static bool Idle(const SELECTION &aSelection)
Test if there no items selected or being edited.
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition selection.h:101
EDA_ITEM * Front() const
Definition selection.h:173
bool Empty() const
Checks if there is anything selected.
Definition selection.h:111
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool IsProjectLoaded(PROJECT *aProject) const
True if aProject is still owned by the manager.
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.
The SIMULATOR_FRAME holds the main user-interface for running simulations.
void DisplayDiff(LIB_SYMBOL *aSchSymbol, LIB_SYMBOL *aLibSymbol, int aUnit, int aBodyStyle)
Set the currently displayed symbol.
The symbol library editor main window.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
Symbol library viewer main window.
KIGFX::VIEW * getView() const
Definition tool_base.cpp:34
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:167
void Go(int(SCH_BASE_FRAME::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
GAL-backed canvas for visualizing a KICAD_DIFF::DIFF_SCENE.
A slimmed down version of WX_HTML_REPORT_PANEL.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void Flush()
Build the HTML messages page.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Display an informational message box with aMessage.
Definition confirm.cpp:245
This file is part of the common library.
#define _(s)
bool GetAssociatedDocument(wxWindow *aParent, const wxString &aDocName, PROJECT *aProject, SEARCH_STACK *aPaths, std::vector< EMBEDDED_FILES * > aFilesStack)
Open a document (file) with the suitable browser.
Definition eda_doc.cpp:59
This file is part of the common library.
@ ERCE_BUS_TO_NET_CONFLICT
A bus wire is graphically connected to a net port/pin (or vice versa).
@ ERCE_LIB_SYMBOL_MISMATCH
Symbol doesn't match copy in library.
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
@ FRAME_SCH_VIEWER
Definition frame_type.h:32
@ FRAME_SCH
Definition frame_type.h:30
@ FRAME_SIMULATOR
Definition frame_type.h:34
static const std::string ProjectFileExtension
static const std::string KiCadSchematicFileExtension
static wxString KiCadSchematicFileWildcard()
std::optional< wxString > GetMsgPanelDisplayUuid(const KIID &aKiid)
Get a formatted UUID string for display in the message panel, according to the current advanced confi...
Definition msgpanel.cpp:216
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)
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...
CATEGORY CategoryFor(CHANGE_KIND aKind)
Map a CHANGE_KIND to the visual category it belongs to.
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition wxgtk/ui.cpp:126
void CheckLibSymbol(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, int aGridForPins, UNITS_PROVIDER *aUnitsProvider)
Check a library symbol to find incorrect settings.
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
KIGFX::COLOR4D reference
Default color for source-document context geometry.
Definition diff_scene.h:287
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
One change record on a single item.
std::vector< ITEM_CHANGE > children
void CheckLibSymbol(LIB_SYMBOL *aSymbol, std::vector< wxString > &aMessages, int aGridForPins, UNITS_PROVIDER *aUnitsProvider)
Check a library symbol to find incorrect settings.
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
wxString result
Test unit parsing edge cases and error handling.
@ SCH_SYMBOL_T
Definition typeinfo.h:169
@ SCH_MARKER_T
Definition typeinfo.h:155
Definition of file extensions used in Kicad.