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" ), symbol->GetField( FIELD_T::REFERENCE )->GetText() );
356 LIB_ID libId = symbol->GetLibId();
357 wxString libName = libId.GetLibNickname();
358 wxString symbolName = libId.GetLibItemName();
359
360 WX_HTML_REPORT_BOX* r = dialog->AddHTMLPage( _( "Summary" ) );
361
362 r->Report( wxS( "<h7>" ) + _( "Schematic vs library diff for:" ) + wxS( "</h7>" ) );
363 r->Report( wxS( "<ul><li>" ) + EscapeHTML( symbolDesc ) + wxS( "</li>" )
364 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
365 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( symbolName )
366 + wxS( "</li></ul>" ) );
367
368 r->Report( "" );
369
371
372 if( !libs->HasLibrary( libName, false ) )
373 {
374 r->Report( _( "The library is not included in the current configuration." )
375 + wxS( "&nbsp;&nbsp;&nbsp" )
376 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
377 }
378 else if( !libs->HasLibrary( libName, true ) )
379 {
380 r->Report( _( "The library is not enabled in the current configuration." )
381 + wxS( "&nbsp;&nbsp;&nbsp" )
382 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
383 }
384 else
385 {
386 std::unique_ptr<LIB_SYMBOL> flattenedLibSymbol;
387 std::unique_ptr<LIB_SYMBOL> flattenedSchSymbol = symbol->GetLibSymbolRef()->Flatten();
388
389 try
390 {
391 if( LIB_SYMBOL* libAlias = libs->LoadSymbol( libName, symbolName ) )
392 flattenedLibSymbol = libAlias->Flatten();
393 }
394 catch( const IO_ERROR& )
395 {
396 }
397
398 if( !flattenedLibSymbol )
399 {
400 r->Report( wxString::Format( _( "The library no longer contains the item %s." ), symbolName ) );
401 }
402 else
403 {
404 std::vector<SCH_FIELD> fields;
405
406 for( SCH_FIELD& field : symbol->GetFields() )
407 {
408 fields.emplace_back( SCH_FIELD( flattenedLibSymbol.get(), field.GetId(), field.GetName( false ) ) );
409 fields.back().CopyText( field );
410 fields.back().SetAttributes( field );
411 fields.back().Move( -symbol->GetPosition() );
412 }
413
414 flattenedSchSymbol->SetFields( fields );
415
416 int flags = schEditorFrame->Schematic().Settings().SymbolCompareFlags();
417
418 if( flattenedSchSymbol->Compare( *flattenedLibSymbol, flags, r ) == 0 )
419 r->Report( _( "No relevant differences detected." ) );
420
421 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
422 SYMBOL_DIFF_WIDGET* diff = constructDiffPanel( panel );
423
424 diff->DisplayDiff( flattenedSchSymbol.release(), flattenedLibSymbol.release(),
425 symbol->GetUnit(), symbol->GetBodyStyle() );
426 }
427 }
428
429 r->Flush();
430
431 dialog->Raise();
432 dialog->Show( true );
433}
434
435
437{
438 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
439
440 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
441 SYMBOL_DIFF_WIDGET* diffWidget = new SYMBOL_DIFF_WIDGET( aParentPanel, backend );
442
443 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
444 aParentPanel->SetSizer( sizer );
445 aParentPanel->Layout();
446
447 return diffWidget;
448}
449
450
451namespace
452{
453void buildSchOverrides( const KICAD_DIFF::DOCUMENT_DIFF& aDiff, const KICAD_DIFF::DIFF_COLOR_THEME& aTheme,
454 std::map<KIID, KIGFX::COLOR4D>& aRefO, std::map<KIID, KIGFX::COLOR4D>& aCompO,
455 std::map<KIID, KICAD_DIFF::CATEGORY>& aCats )
456{
457 aRefO.clear();
458 aCompO.clear();
459 aCats.clear();
460
461 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> visit =
462 [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
463 {
464 if( !aChange.id.empty() )
465 {
466 const KIID& kiid = aChange.id.back();
467 aCats[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
468
469 switch( aChange.kind )
470 {
471 case KICAD_DIFF::CHANGE_KIND::ADDED: aCompO[kiid] = aTheme.added; break;
473 aRefO[kiid] = aTheme.removed;
474 aCompO[kiid] = aTheme.removed;
475 break;
477 aRefO[kiid] = aTheme.modified;
478 aCompO[kiid] = aTheme.modified;
479 break;
480 default:
481 aRefO[kiid] = aTheme.conflict;
482 aCompO[kiid] = aTheme.conflict;
483 break;
484 }
485 }
486
487 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
488 visit( child );
489 };
490
491 for( const KICAD_DIFF::ITEM_CHANGE& change : aDiff.changes )
492 visit( change );
493}
494
495
496DIALOG_KICAD_DIFF::SHEET_SWITCHER makeSchSwitcher( SCHEMATIC* aRef, SCHEMATIC* aComp,
497 std::map<KIID, KIGFX::COLOR4D> aRefOverrides,
498 std::map<KIID, KIGFX::COLOR4D> aCompOverrides,
499 std::map<KIID, KICAD_DIFF::CATEGORY> aCategories,
500 const KICAD_DIFF::DIFF_COLOR_THEME& aTheme )
501{
502 auto holder = std::make_shared<std::vector<std::unique_ptr<SCH_ITEM>>>();
503
504 return [aRef, aComp, modifiedColor = aTheme.modified, removedColor = aTheme.removed,
505 refO = std::move( aRefOverrides ), compO = std::move( aCompOverrides ), cats = std::move( aCategories ),
506 holder]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
507 {
508 SCH_SCREEN* compScreen = aComp->RootScreen();
509 SCH_SCREEN* refScreen = aRef->RootScreen();
510
511 if( !aSheetPath.empty() )
512 {
513 if( auto sp = aComp->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
514 compScreen = sp->LastScreen();
515
516 if( auto sp = aRef->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
517 refScreen = sp->LastScreen();
518 }
519
520 holder->clear();
521
522 if( refScreen )
523 {
524 for( const auto& [kiid, c] : refO )
525 {
526 if( c != removedColor )
527 continue;
528
529 for( SCH_ITEM* item : refScreen->Items() )
530 {
531 if( item && item->m_Uuid == kiid )
532 {
533 if( auto* clone = dynamic_cast<SCH_ITEM*>( item->Clone() ) )
534 holder->emplace_back( clone );
535
536 break;
537 }
538 }
539 }
540 }
541
542 std::vector<KIGFX::VIEW_ITEM*> extras;
543
544 for( const auto& clone : *holder )
545 extras.push_back( clone.get() );
546
547 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, nullptr, aComp, modifiedColor, compO, extras, cats, nullptr,
548 compScreen );
549 };
550}
551
552
553struct DRILL_FRAME
554{
555 SCH_SHEET_PATH editorPath;
556 SCHEMATIC* compSch = nullptr;
557 wxString compFile;
558 KIID_PATH compScope;
559};
560
561
562void reloadDrillFrame( DIALOG_KICAD_DIFF& aDlg, SCHEMATIC* aRefSch, const KICAD_DIFF::DIFF_COLOR_THEME& aTheme,
563 const DRILL_FRAME& aFrame )
564{
565 KICAD_DIFF::SCH_DIFFER differ( aRefSch, aFrame.compSch, aFrame.compFile );
566 const KIID_PATH scope = aFrame.editorPath.Path();
567
568 if( !scope.empty() && !aFrame.compScope.empty() )
569 differ.SetScope( scope, aFrame.compScope );
570
571 KICAD_DIFF::DOCUMENT_DIFF diff = differ.Diff();
572
573 std::map<KIID, KIGFX::COLOR4D> refO;
574 std::map<KIID, KIGFX::COLOR4D> compO;
575 std::map<KIID, KICAD_DIFF::CATEGORY> cats;
576 buildSchOverrides( diff, aTheme, refO, compO, cats );
577
578 auto switcher = makeSchSwitcher( aRefSch, aFrame.compSch, refO, compO, cats, aTheme );
579
580 SCH_SCREEN* refScreen = aFrame.editorPath.LastScreen();
581 wxString refLabel = refScreen ? refScreen->GetFileName() : wxString();
582
583 aDlg.Reload( refLabel, aFrame.compFile, std::move( diff ), /*aReferenceGeometry=*/{},
584 /*aComparisonGeometry=*/{}, std::move( switcher ), scope );
585}
586} // namespace
587
588
590{
591 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
592
593 wxCHECK( schEditorFrame, 0 );
594
595 wxFileDialog dlg( schEditorFrame, _( "Choose Schematic to Compare With" ), wxEmptyString,
596 wxEmptyString, FILEEXT::KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
597
598 if( dlg.ShowModal() != wxID_OK )
599 return 0;
600
601 wxFileName otherFn( dlg.GetPath() );
602 otherFn.MakeAbsolute();
603
604 if( !otherFn.GetExt().IsSameAs( FILEEXT::KiCadSchematicFileExtension, false ) )
605 {
606 schEditorFrame->ShowInfoBarError( _( "Select a KiCad s-expression schematic file (.kicad_sch)." ) );
607 return 0;
608 }
609
610 const wxString otherPath = otherFn.GetFullPath();
611
612 wxFileName projectFn = otherFn;
613 projectFn.SetExt( FILEEXT::ProjectFileExtension );
614 const wxString projectPath = projectFn.GetFullPath();
615
616 wxFileName activeProjectFn( schEditorFrame->Prj().GetProjectFullName() );
617 activeProjectFn.MakeAbsolute();
618
619 // Refuse the self-compare; attaching the active PROJECT to a second
620 // SCHEMATIC would clobber its ERC/schematic settings.
621 if( projectFn.SameAs( activeProjectFn ) )
622 {
623 schEditorFrame->ShowInfoBarError( _( "Select a schematic file from another project to compare." ) );
624 return 0;
625 }
626
627 return showSchematicComparison( otherPath, projectPath, otherPath );
628}
629
630
631int SCH_INSPECTION_TOOL::showSchematicComparison( const wxString& aOtherPath, const wxString& aProjectPath,
632 const wxString& aComparisonLabel )
633{
634 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
635
636 wxCHECK( schEditorFrame, 0 );
637
638 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
639
640 wxCHECK( mgr, 0 );
641
642 // A missing .kicad_pro is fine (LoadProject returns false but still
643 // inserts a defaults-only slot); a present-but-malformed .kicad_pro is
644 // treated as failure.
645 bool projectLoadOk = mgr->LoadProject( aProjectPath, false );
646 PROJECT* otherPrj = mgr->GetProject( aProjectPath );
647
648 if( !otherPrj )
649 {
650 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
651 return 0;
652 }
653
654 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
655 {
656 mgr->UnloadProject( otherPrj, false );
657 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
658 return 0;
659 }
660
661 // SCH_DIFFER walks the sheet hierarchy and properties; connectivity build
662 // is unnecessary and slow for a read-only compare.
663 SCHEMATIC* loadedSchematic = nullptr;
664
665 try
666 {
667 loadedSchematic = EESCHEMA_HELPERS::LoadSchematic( aOtherPath,
668 /*aSetActive=*/false,
669 /*aForceDefaultProject=*/false, otherPrj,
670 /*aCalculateConnectivity=*/false );
671 }
672 catch( ... )
673 {
674 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
675 mgr->UnloadProject( otherPrj, false );
676 return 0;
677 }
678
679 SCHEMATIC* mySch = &schEditorFrame->Schematic();
680
681 // Belt-and-suspenders: EESCHEMA_HELPERS::LoadSchematic has a fallback
682 // branch that returns the active editor's schematic when the project
683 // happens to be the active one. The path guard above should have caught
684 // this, but a path-alias miss would leave the unique_ptr owning the live
685 // schematic and SetProject(nullptr) would destroy editor state.
686 if( loadedSchematic == mySch )
687 {
688 schEditorFrame->ShowInfoBarError( _( "Select a schematic file from another project to compare." ) );
689 mgr->UnloadProject( otherPrj, false );
690 return 0;
691 }
692
693 std::unique_ptr<SCHEMATIC> otherSchematic{ loadedSchematic };
694
695 if( !otherSchematic )
696 {
697 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
698 mgr->UnloadProject( otherPrj, false );
699 return 0;
700 }
701
702 KICAD_DIFF::SCH_DIFFER differ( mySch, otherSchematic.get(), aOtherPath );
703
704 // Scope to the editor's current sheet. Comparison resolves the matching
705 // sheet by KIID when present, else falls back to its root.
706 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
707 const SCH_SHEET_LIST otherSheets = otherSchematic->BuildSheetListSortedByPageNumbers();
708 KIID_PATH scopeAfter;
709
710 if( auto sp = otherSchematic->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
711 scopeAfter = sp->Path();
712 else if( !otherSheets.empty() )
713 scopeAfter = otherSheets.front().Path();
714
715 if( !scopeBefore.empty() && !scopeAfter.empty() )
716 differ.SetScope( scopeBefore, scopeAfter );
717
719
721
722 std::map<KIID, KIGFX::COLOR4D> refOverrides;
723 std::map<KIID, KIGFX::COLOR4D> compOverrides;
724 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
725
726 buildSchOverrides( result, theme, refOverrides, compOverrides, kiidCategories );
727
729 refOverrides );
731 theme.comparison,
732 compOverrides );
733
734 DIALOG_KICAD_DIFF::SHEET_SWITCHER initialSwitcher = makeSchSwitcher( mySch, otherSchematic.get(), refOverrides,
735 compOverrides, kiidCategories, theme );
736
737 KIID_PATH initialSheet = schEditorFrame->GetCurrentSheet().Path();
738 SCH_SCREEN* currentSheetScreen = schEditorFrame->GetCurrentSheet().LastScreen();
739 wxString referenceLabel = currentSheetScreen ? currentSheetScreen->GetFileName() : mySch->GetFileName();
740
741 DIALOG_KICAD_DIFF dlgDiff( schEditorFrame, referenceLabel, aComparisonLabel, result, std::move( refGeometry ),
742 std::move( compGeometry ), std::move( initialSwitcher ), std::move( initialSheet ) );
743
744 // Drill state owns the comparison schematics loaded across double-click
745 // drills. The editor schematic stays the reference on all drills.
746 DRILL_FRAME drillCurrent{ schEditorFrame->GetCurrentSheet(), otherSchematic.get(), aOtherPath, scopeAfter };
747
748 std::vector<DRILL_FRAME> drillBack;
749 std::vector<std::unique_ptr<SCHEMATIC>> drilledSchs;
750
751 drilledSchs.push_back( std::move( otherSchematic ) );
752
753 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff.DiffCanvas() )
754 {
755 canvas->SetDoubleClickHandler(
756 [&dlgDiff, &drillCurrent, &drillBack, &drilledSchs, mySch, otherPrj, theme,
757 schEditorFrame]( KIGFX::VIEW_ITEM* aItem )
758 {
759 auto* sheet = dynamic_cast<SCH_SHEET*>( aItem );
760
761 if( !sheet )
762 return;
763
764 const wxString sheetFile = sheet->GetFileName();
765
766 if( sheetFile.IsEmpty() )
767 return;
768
769 KIID_PATH newEditorKiid = drillCurrent.editorPath.Path();
770 newEditorKiid.push_back( sheet->m_Uuid );
771
772 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
773
774 if( !newEditorSheet )
775 return;
776
777 // Prefer resolving inside the current comparison schematic so
778 // shared-sheet instance context is preserved on both sides.
779 DRILL_FRAME next{ *newEditorSheet, drillCurrent.compSch, drillCurrent.compFile, KIID_PATH() };
780
781 if( auto compMatch = next.compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
782 {
783 next.compScope = compMatch->Path();
784
785 if( SCH_SCREEN* compMatchScreen = compMatch->LastScreen() )
786 next.compFile = compMatchScreen->GetFileName();
787 }
788 else
789 {
790 wxFileName newCompFn( wxFileName( drillCurrent.compFile ).GetPath(), sheetFile );
791 newCompFn.MakeAbsolute();
792
793 SCHEMATIC* loaded = EESCHEMA_HELPERS::LoadSchematic( newCompFn.GetFullPath(),
794 /*aSetActive*/ false,
795 /*aForceDefaultProject*/ false, otherPrj,
796 /*aCalculateConnectivity*/ false );
797
798 if( !loaded )
799 {
800 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ),
801 newCompFn.GetFullPath() ) );
802 return;
803 }
804
805 const SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
806
807 if( !loadedSheets.empty() )
808 next.compScope = loadedSheets.front().Path();
809
810 drilledSchs.emplace_back( loaded );
811 next.compSch = loaded;
812 next.compFile = newCompFn.GetFullPath();
813 }
814
815 reloadDrillFrame( dlgDiff, mySch, theme, next );
816
817 drillBack.push_back( drillCurrent );
818 drillCurrent = next;
819 dlgDiff.EnableUp( true );
820 } );
821
822 dlgDiff.SetUpHandler(
823 [&dlgDiff, &drillCurrent, &drillBack, mySch, theme]()
824 {
825 if( drillBack.empty() )
826 return;
827
828 reloadDrillFrame( dlgDiff, mySch, theme, drillBack.back() );
829
830 drillCurrent = drillBack.back();
831 drillBack.pop_back();
832 dlgDiff.EnableUp( !drillBack.empty() );
833 } );
834 }
835
836 dlgDiff.ShowModal();
837
838 // Detach schematics from the project before unloading so the project's
839 // ERC and schematic settings release cleanly.
840 for( auto& sch : drilledSchs )
841 {
842 if( sch )
843 sch->SetProject( nullptr );
844 }
845
846 drilledSchs.clear();
847
848 mgr->UnloadProject( otherPrj, false );
849
850 return 0;
851}
852
853
855{
856 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
857
858 wxCHECK( schEditorFrame, 0 );
859
860 SCHEMATIC* mySch = &schEditorFrame->Schematic();
861
862 if( mySch->GetFileName().IsEmpty() )
863 {
864 schEditorFrame->ShowInfoBarError( _( "Save the schematic before comparing against local history." ) );
865 return 0;
866 }
867
868 const wxString projectPath = schEditorFrame->Prj().GetProjectPath();
869 LOCAL_HISTORY& history = schEditorFrame->Kiway().LocalHistory();
870
871 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
872
873 if( snapshots.empty() )
874 {
875 schEditorFrame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
876 return 0;
877 }
878
879 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
880
881 wxCHECK( mgr, 0 );
882
883 auto relTo =
884 [&]( const wxString& aFull )
885 {
886 wxFileName fn( aFull );
887 fn.MakeRelativeTo( projectPath );
888 return fn.GetFullPath( wxPATH_UNIX );
889 };
890
891 const wxString rootRel = relTo( mySch->GetFileName() );
892 const wxString projRel = relTo( schEditorFrame->Prj().GetProjectFullName() );
893
894 // One entry per distinct schematic version: newest-first, skipping commits
895 // with no schematic or whose schematic content (any .kicad_sch sheet) matches
896 // the previously kept one. This drops board-only saves like "PCB Save".
897 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> filtered;
898 wxString prevFingerprint;
899
900 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
901 {
902 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_sch" ) );
903
904 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
905 continue;
906
907 prevFingerprint = fingerprint;
908 filtered.push_back( s );
909 }
910
911 if( filtered.empty() )
912 {
913 schEditorFrame->ShowInfoBarError( _( "No local history snapshots change this schematic." ) );
914 return 0;
915 }
916
917 snapshots = std::move( filtered );
918
919 std::vector<wxString> labels;
920
921 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
922 {
923 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
924 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
925 }
926
928 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
929
930 struct SCH_DIFF_VIEW
931 {
936 KIID_PATH compScope;
937 };
938
939 auto buildView =
940 [&]( SCHEMATIC* aComp, const wxString& aPath ) -> SCH_DIFF_VIEW
941 {
942 SCH_DIFF_VIEW view;
943 KICAD_DIFF::SCH_DIFFER differ( mySch, aComp, aPath );
944
945 KIID_PATH scopeAfter;
946
947 if( std::optional<SCH_SHEET_PATH> sp = aComp->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
948 {
949 scopeAfter = sp->Path();
950 }
951 else
952 {
954
955 if( !sheets.empty() )
956 scopeAfter = sheets.front().Path();
957 }
958
959 if( !scopeBefore.empty() && !scopeAfter.empty() )
960 differ.SetScope( scopeBefore, scopeAfter );
961
962 view.compScope = scopeAfter;
963 view.result = differ.Diff();
964
965 std::map<KIID, KIGFX::COLOR4D> refO;
966 std::map<KIID, KIGFX::COLOR4D> compO;
967 std::map<KIID, KICAD_DIFF::CATEGORY> cats;
968 buildSchOverrides( view.result, theme, refO, compO, cats );
969
970 view.refGeom = KICAD_DIFF::ExtractSchematicGeometry( *mySch, theme.reference, refO );
971 view.compGeom = KICAD_DIFF::ExtractSchematicGeometry( *aComp, theme.comparison, compO );
972 view.switcher = makeSchSwitcher( mySch, aComp, refO, compO, cats, theme );
973
974 return view;
975 };
976
977 // State for the revision currently shown. Swapped on each dropdown change.
978 std::unique_ptr<SCHEMATIC> curSch;
979 PROJECT* curPrj = nullptr;
980 wxString curTempDir;
981
982 // Drill state: which comparison sheet is shown and sub-schematics loaded on
983 // double-click. Reset when the revision changes.
984 DRILL_FRAME drillCurrent;
985 std::vector<DRILL_FRAME> drillBack;
986 std::vector<std::unique_ptr<SCHEMATIC>> drilledSchs;
987
988 auto cleanupCurrent =
989 [&]()
990 {
991 for( std::unique_ptr<SCHEMATIC>& sch : drilledSchs )
992 {
993 if( sch )
994 sch->SetProject( nullptr );
995 }
996
997 drilledSchs.clear();
998
999 if( curSch )
1000 {
1001 curSch->SetProject( nullptr );
1002 curSch.reset();
1003 }
1004
1005 // Skip if the project was already evicted from the manager.
1006 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
1007 mgr->UnloadProject( curPrj, false );
1008
1009 curPrj = nullptr;
1010
1011 if( !curTempDir.IsEmpty() )
1012 {
1013 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
1014 curTempDir.Clear();
1015 }
1016 };
1017
1018 // Extract the hierarchy at snapshot aIndex and load its root sheet, cleaning
1019 // up the temp dir on any failure.
1020 auto loadRevision =
1021 [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir ) -> bool
1022 {
1023 const wxString hash = snapshots[aIndex].hash;
1024 wxFileName dirFn;
1025 dirFn.AssignDir( wxFileName::GetTempDir() );
1026 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
1027 const wxString tempDir = dirFn.GetPath();
1028
1029 // Extract just the schematic sheets and project file, skipping the
1030 // board, 3D models, gerbers, etc.
1031 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
1032 { wxS( ".kicad_sch" ), wxS( ".kicad_pro" ) } ) )
1033 {
1034 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1035 return false;
1036 }
1037
1038 const wxString root = tempDir + wxS( "/" ) + rootRel;
1039 const wxString proj = tempDir + wxS( "/" ) + projRel;
1040
1041 mgr->LoadProject( proj, false );
1042 PROJECT* prj = mgr->GetProject( proj );
1043
1044 if( !prj )
1045 {
1046 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1047 return false;
1048 }
1049
1050 SCHEMATIC* loaded = nullptr;
1051
1052 try
1053 {
1054 loaded = EESCHEMA_HELPERS::LoadSchematic( root, /*aSetActive*/ false,
1055 /*aForceDefaultProject*/ false, prj,
1056 /*aCalculateConnectivity*/ false );
1057 }
1058 catch( ... )
1059 {
1060 mgr->UnloadProject( prj, false );
1061 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1062 return false;
1063 }
1064
1065 if( !loaded || loaded == mySch )
1066 {
1067 mgr->UnloadProject( prj, false );
1068 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1069 return false;
1070 }
1071
1072 aSch.reset( loaded );
1073 aPrj = prj;
1074 aTempDir = tempDir;
1075 return true;
1076 };
1077
1078 auto loadView =
1079 [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir,
1080 SCH_DIFF_VIEW& aView ) -> bool
1081 {
1082 if( !loadRevision( aIndex, aSch, aPrj, aTempDir ) )
1083 return false;
1084
1085 try
1086 {
1087 aView = buildView( aSch.get(), aTempDir + wxS( "/" ) + rootRel );
1088 }
1089 catch( ... )
1090 {
1091 aSch->SetProject( nullptr );
1092 aSch.reset();
1093 mgr->UnloadProject( aPrj, false );
1094 aPrj = nullptr;
1095 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
1096 aTempDir.Clear();
1097 return false;
1098 }
1099
1100 return true;
1101 };
1102
1103 SCH_DIFF_VIEW view;
1104 int startIndex = 0;
1105
1106 if( !loadView( 0, curSch, curPrj, curTempDir, view ) )
1107 {
1108 schEditorFrame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
1109 return 0;
1110 }
1111
1112 // Default to the first commit back from HEAD that actually differs from the
1113 // current schematic, so opening lands on real changes rather than an in-sync HEAD.
1114 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
1115 {
1116 std::unique_ptr<SCHEMATIC> nextSch;
1117 PROJECT* nextPrj = nullptr;
1118 wxString nextTempDir;
1119 SCH_DIFF_VIEW nextView;
1120
1121 if( !loadView( startIndex + 1, nextSch, nextPrj, nextTempDir, nextView ) )
1122 break;
1123
1124 cleanupCurrent();
1125 view = std::move( nextView );
1126 curSch = std::move( nextSch );
1127 curPrj = nextPrj;
1128 curTempDir = nextTempDir;
1129 startIndex++;
1130 }
1131
1132 drillCurrent = { schEditorFrame->GetCurrentSheet(), curSch.get(), curTempDir + wxS( "/" ) + rootRel,
1133 view.compScope };
1134
1135 int shownIndex = startIndex;
1136
1137 SCH_SCREEN* curScreen = schEditorFrame->GetCurrentSheet().LastScreen();
1138 wxString referenceLabel = curScreen ? curScreen->GetFileName() : mySch->GetFileName();
1139
1140 std::unique_ptr<DIALOG_KICAD_DIFF> dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( schEditorFrame, referenceLabel,
1141 labels[startIndex], view.result,
1142 view.refGeom, view.compGeom,
1143 view.switcher, scopeBefore );
1144
1145 // Double-click a sheet to drill into its sub-schematic, within the current
1146 // revision's extracted hierarchy.
1147 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff->DiffCanvas() )
1148 {
1149 canvas->SetDoubleClickHandler(
1150 [&]( KIGFX::VIEW_ITEM* aItem )
1151 {
1152 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( aItem );
1153
1154 if( !sheet || sheet->GetFileName().IsEmpty() )
1155 return;
1156
1157 KIID_PATH newEditorKiid = drillCurrent.editorPath.Path();
1158 newEditorKiid.push_back( sheet->m_Uuid );
1159
1160 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
1161
1162 if( !newEditorSheet )
1163 return;
1164
1165 DRILL_FRAME next{ *newEditorSheet, drillCurrent.compSch, drillCurrent.compFile, KIID_PATH() };
1166
1167 if( auto compMatch = next.compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
1168 {
1169 next.compScope = compMatch->Path();
1170
1171 if( SCH_SCREEN* matchScreen = compMatch->LastScreen() )
1172 next.compFile = matchScreen->GetFileName();
1173 }
1174 else
1175 {
1176 wxFileName subFn( wxFileName( drillCurrent.compFile ).GetPath(), sheet->GetFileName() );
1177 subFn.MakeAbsolute();
1178
1179 SCHEMATIC* loaded = nullptr;
1180
1181 try
1182 {
1183 loaded = EESCHEMA_HELPERS::LoadSchematic( subFn.GetFullPath(), /*aSetActive=*/false,
1184 /*aForceDefaultProject=*/false, curPrj,
1185 /*aCalculateConnectivity=*/false );
1186 }
1187 catch( ... )
1188 {
1189 loaded = nullptr;
1190 }
1191
1192 if( !loaded || loaded == mySch )
1193 {
1194 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ),
1195 subFn.GetFullPath() ) );
1196 return;
1197 }
1198
1199 SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
1200
1201 if( !loadedSheets.empty() )
1202 next.compScope = loadedSheets.front().Path();
1203
1204 drilledSchs.emplace_back( loaded );
1205 next.compSch = loaded;
1206 next.compFile = subFn.GetFullPath();
1207 }
1208
1209 try
1210 {
1211 reloadDrillFrame( *dlgDiff, mySch, theme, next );
1212 }
1213 catch( ... )
1214 {
1215 schEditorFrame->ShowInfoBarError( _( "Could not open this sheet for comparison." ) );
1216 return;
1217 }
1218
1219 drillBack.push_back( drillCurrent );
1220 drillCurrent = next;
1221 dlgDiff->EnableUp( true );
1222 } );
1223
1224 dlgDiff->SetUpHandler(
1225 [&]()
1226 {
1227 if( drillBack.empty() )
1228 return;
1229
1230 try
1231 {
1232 reloadDrillFrame( *dlgDiff, mySch, theme, drillBack.back() );
1233 }
1234 catch( ... )
1235 {
1236 schEditorFrame->ShowInfoBarError( _( "Could not open this sheet for comparison." ) );
1237 return;
1238 }
1239
1240 drillCurrent = drillBack.back();
1241 drillBack.pop_back();
1242 dlgDiff->EnableUp( !drillBack.empty() );
1243 } );
1244 }
1245
1246 dlgDiff->SetRevisionChooser(
1247 labels, startIndex,
1248 [&]( int aIndex )
1249 {
1250 if( aIndex == shownIndex )
1251 return;
1252
1253 std::unique_ptr<SCHEMATIC> newSch;
1254 PROJECT* newPrj = nullptr;
1255 wxString newTempDir;
1256 SCH_DIFF_VIEW newView;
1257
1258 if( !loadView( aIndex, newSch, newPrj, newTempDir, newView ) )
1259 {
1260 schEditorFrame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
1261 return;
1262 }
1263
1264 dlgDiff->Reload( referenceLabel, labels[aIndex], newView.result, newView.refGeom,
1265 newView.compGeom, newView.switcher, scopeBefore );
1266
1267 cleanupCurrent();
1268 view = std::move( newView );
1269 curSch = std::move( newSch );
1270 curPrj = newPrj;
1271 curTempDir = newTempDir;
1272 shownIndex = aIndex;
1273
1274 // Restart drilling from the new revision's root.
1275 drillBack.clear();
1276 drillCurrent = { schEditorFrame->GetCurrentSheet(), curSch.get(), curTempDir + wxS( "/" ) + rootRel,
1277 view.compScope };
1278 dlgDiff->EnableUp( false );
1279 } );
1280
1281 dlgDiff->ShowModal();
1282
1283 // Destroy the dialog before the schematics it references are freed.
1284 dlgDiff.reset();
1285
1286 cleanupCurrent();
1287 return 0;
1288}
1289
1290
1292{
1293 SIMULATOR_FRAME* simFrame = (SIMULATOR_FRAME*) m_frame->Kiway().Player( FRAME_SIMULATOR, true );
1294
1295 if( !simFrame )
1296 return -1;
1297
1298 if( wxWindow* blocking_win = simFrame->Kiway().GetBlockingDialog() )
1299 blocking_win->Close( true );
1300
1301 simFrame->Show( true );
1302
1303 // On Windows, Raise() does not bring the window on screen, when iconized
1304 if( simFrame->IsIconized() )
1305 simFrame->Iconize( false );
1306
1307 simFrame->Raise();
1308
1309 return 0;
1310}
1311
1312
1314{
1315 wxString datasheet;
1316 std::vector<EMBEDDED_FILES*> filesStack;
1317
1318 if( m_frame->IsType( FRAME_SCH_SYMBOL_EDITOR ) )
1319 {
1320 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1321
1322 if( !symbol )
1323 return 0;
1324
1325 datasheet = symbol->GetDatasheetField().GetText();
1326 filesStack.push_back( symbol );
1327 }
1328 else if( m_frame->IsType( FRAME_SCH_VIEWER ) )
1329 {
1330 LIB_SYMBOL* entry = static_cast<SYMBOL_VIEWER_FRAME*>( m_frame )->GetSelectedSymbol();
1331
1332 if( !entry )
1333 return 0;
1334
1335 datasheet = entry->GetDatasheetField().GetText();
1336 filesStack.push_back( entry );
1337 }
1338 else if( m_frame->IsType( FRAME_SCH ) )
1339 {
1340 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
1341
1342 if( selection.Empty() )
1343 return 0;
1344
1345 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
1346 SCH_FIELD* field = symbol->GetField( FIELD_T::DATASHEET );
1347
1348 // Use GetShownText() to resolve any text variables
1349 datasheet = field->GetShownText( &symbol->Schematic()->CurrentSheet(), FOR_GUI );
1350 filesStack.push_back( symbol->Schematic() );
1351
1352 if( symbol->GetLibSymbolRef() )
1353 filesStack.push_back( symbol->GetLibSymbolRef().get() );
1354 }
1355
1356 if( datasheet.IsEmpty() || datasheet == wxS( "~" ) )
1357 {
1358 m_frame->ShowInfoBarError( _( "No datasheet defined." ) );
1359 }
1360 else
1361 {
1363 filesStack );
1364 }
1365
1366 return 0;
1367}
1368
1369
1371{
1372 SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1373 SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
1374 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1375 SCH_SELECTION& selection = selTool->GetSelection();
1376
1377 // Note: the symbol viewer manages its own message panel
1378
1379 if( symbolEditFrame || schEditFrame )
1380 {
1381 if( selection.GetSize() == 1 )
1382 {
1383 EDA_ITEM* item = (EDA_ITEM*) selection.Front();
1384 std::vector<MSG_PANEL_ITEM> msgItems;
1385
1386 if( std::optional<wxString> uuid = GetMsgPanelDisplayUuid( item->m_Uuid ) )
1387 msgItems.emplace_back( _( "UUID" ), *uuid );
1388
1389 item->GetMsgPanelInfo( m_frame, msgItems );
1390 m_frame->SetMsgPanel( msgItems );
1391 }
1392 else
1393 {
1394 m_frame->ClearMsgPanel();
1395 }
1396 }
1397
1398 if( schEditFrame )
1399 {
1400 schEditFrame->UpdateNetHighlightStatus();
1401 schEditFrame->UpdateHierarchySelection();
1402 }
1403
1404 return 0;
1405}
1406
1407
1409{
1413 // See note 1:
1417
1424
1426
1427 // Note 1: tUpdateMessagePanel is called by CrossProbe. So uncomment this line if
1428 // call to CrossProbe is modifiied
1429 // Go( &SCH_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::SelectedEvent );
1433}
1434
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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).
void SetUpHandler(UP_HANDLER aHandler)
std::function< void(WIDGET_DIFF_CANVAS &, const KIID_PATH &)> SHEET_SWITCHER
WIDGET_DIFF_CANVAS * DiffCanvas() const
void EnableUp(bool aEnable)
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:98
const KIID m_Uuid
Definition eda_item.h:597
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
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:296
static SCHEMATIC * LoadSchematic(const wxString &aFileName, bool aSetActive, bool aForceDefaultProject, PROJECT *aProject=nullptr, bool aCalculateConnectivity=true, REPORTER *aRootReporter=nullptr)
static const TOOL_EVENT ClearedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedEvent
Definition actions.h:343
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:350
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:342
static const TOOL_EVENT UnselectedEvent
Definition actions.h:344
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:46
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:680
LOCAL_HISTORY & LocalHistory()
Return the LOCAL_HISTORY associated with this KIWAY.
Definition kiway.h:451
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:119
SCH_FIELD & GetDatasheetField()
Return reference to the datasheet field.
Definition lib_symbol.h:449
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:63
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:148
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
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.
SCH_SCREEN * RootScreen() const
Helper to retrieve the screen of the root sheet.
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:303
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()
void UpdateNetHighlightStatus()
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, int aDepth=0) 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:165
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:281
int GetBodyStyle() const
Definition sch_item.h:247
int GetUnit() const
Definition sch_item.h:237
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
const wxString & GetFileName() const
Definition sch_screen.h:153
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.
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:48
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:384
Schematic symbol object.
Definition sch_symbol.h:75
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:934
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:164
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
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:104
EDA_ITEM * Front() const
Definition selection.h:176
bool Empty() const
Checks if there is anything selected.
Definition selection.h:114
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.
@ FOR_GUI
Definition common.h:89
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:127
CITER next(CITER it)
Definition ptree.cpp:120
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:168
@ SCH_MARKER_T
Definition typeinfo.h:154
Definition of file extensions used in Kicad.