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