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
21#include <tool/tool_manager.h>
24
25#include <sch_symbol.h>
26#include <id.h>
27#include <kiway.h>
28#include <kiplatform/ui.h>
29#include <confirm.h>
30#include <string_utils.h>
34#include <tools/sch_actions.h>
36#include <tools/sch_selection.h>
37#include <sim/simulator_frame.h>
38#include <sch_edit_frame.h>
39#include <symbol_edit_frame.h>
40#include <symbol_viewer_frame.h>
41#include <eda_doc.h>
42#include <sch_marker.h>
43#include <project.h>
44#include <project_sch.h>
46#include <dialogs/dialog_erc.h>
51#include <math/util.h> // for KiROUND
52
58#include <eeschema_helpers.h>
59#include <schematic.h>
61#include <local_history.h>
63#include <wx/filedlg.h>
64#include <wx/filename.h>
65
66
68 SCH_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.InspectionTool" ), m_busSyntaxHelp( nullptr )
69{
70}
71
72
74{
76
77 // Add inspection actions to the selection tool menu
78 //
79 CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
80
82
85
86 return true;
87}
88
89
91{
92 SCH_TOOL_BASE::Reset( aReason );
93
94 if( aReason == SUPERMODEL_RELOAD || aReason == RESET_REASON::SHUTDOWN )
95 {
96 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY );
97
98 wxQueueEvent( m_frame, evt );
99 }
100}
101
102
104{
106 return 0;
107}
108
109
111{
112 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
113
114 wxCHECK( frame, /* void */ );
115
116 DIALOG_ERC* dlg = frame->GetErcDialog();
117
118 wxCHECK( dlg, /* void */ );
119
120 // Needed at least on Windows. Raise() is not enough
121 dlg->Show( true );
122
123 // Bring it to the top if already open. Dual monitor users need this.
124 dlg->Raise();
125
126 if( wxButton* okButton = dynamic_cast<wxButton*>( dlg->FindWindow( wxID_OK ) ) )
127 {
128 KIPLATFORM::UI::ForceFocus( okButton );
129 okButton->SetDefault();
130 }
131}
132
133
135{
136 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
137
138 wxCHECK( frame, 0 );
139
140 DIALOG_ERC* dlg = frame->GetErcDialog();
141
142 if( dlg )
143 {
144 dlg->Show( true );
145 dlg->Raise();
146 dlg->PrevMarker();
147 }
148
149 return 0;
150}
151
152
154{
155 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
156
157 wxCHECK( frame, 0 );
158
159 DIALOG_ERC* dlg = frame->GetErcDialog();
160
161 wxCHECK( dlg, 0 );
162
163 dlg->Show( true );
164 dlg->Raise();
165 dlg->NextMarker();
166
167 return 0;
168}
169
170
172{
173 SCH_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
174
175 wxCHECK( selectionTool, 0 );
176
177 SCH_SELECTION& selection = selectionTool->GetSelection();
178
179 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
180 {
181 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
182 DIALOG_ERC* dlg = frame ? frame->GetErcDialog() : nullptr;
183
184 if( dlg && dlg->IsShownOnScreen() )
185 dlg->SelectMarker( static_cast<SCH_MARKER*>( selection.Front() ) );
186 }
187
188 // Show the item info on a left click on this item
189 UpdateMessagePanel( aEvent );
190
191 return 0;
192}
193
194
196{
197 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
198
199 wxCHECK( frame, /* void */ );
200
201 DIALOG_ERC* dlg = frame->GetErcDialog();
202
203 if( dlg )
204 {
205 if( !dlg->IsShownOnScreen() )
206 {
207 dlg->Show( true );
208 dlg->Raise();
209 }
210
211 dlg->SelectMarker( aMarker );
212 }
213}
214
215
216wxString SCH_INSPECTION_TOOL::InspectERCErrorMenuText( const std::shared_ptr<RC_ITEM>& aERCItem )
217{
218 if( aERCItem->GetErrorCode() == ERCE_BUS_TO_NET_CONFLICT )
219 {
220 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::showBusSyntaxHelp );
221 }
222 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
223 {
224 return m_frame->GetRunMenuCommandDescription( SCH_ACTIONS::diffSymbol );
225 }
226
227 return wxEmptyString;
228}
229
230
231void SCH_INSPECTION_TOOL::InspectERCError( const std::shared_ptr<RC_ITEM>& aERCItem )
232{
233 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
234
235 wxCHECK( frame, /* void */ );
236
237 EDA_ITEM* a = frame->ResolveItem( aERCItem->GetMainItemID() );
238
239 if( aERCItem->GetErrorCode() == ERCE_BUS_TO_NET_CONFLICT )
240 {
242 }
243 else if( aERCItem->GetErrorCode() == ERCE_LIB_SYMBOL_MISMATCH )
244 {
245 if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( a ) )
246 DiffSymbol( symbol );
247 }
248}
249
250
252{
253 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
254 SCH_SELECTION& selection = selTool->GetSelection();
255 SCH_MARKER* marker = nullptr;
256
257 if( selection.GetSize() == 1 && selection.Front()->Type() == SCH_MARKER_T )
258 marker = static_cast<SCH_MARKER*>( selection.Front() );
259
260 SCH_EDIT_FRAME* frame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
261
262 wxCHECK( frame, 0 );
263
264 DIALOG_ERC* dlg = frame->GetErcDialog();
265
266 wxCHECK( dlg, 0 );
267
268 // Let the ERC dialog handle it since it owns the marker provider's cached counts and view
269 // updates. If marker is nullptr the dialog excludes whichever marker is selected in the
270 // dialog itself.
271 dlg->ExcludeMarker( marker );
272
273 return 0;
274}
275
276
277extern void CheckLibSymbol( LIB_SYMBOL* aSymbol, std::vector<wxString>& aMessages,
278 int aGridForPins, UNITS_PROVIDER* aUnitsProvider );
279
281{
282 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
283
284 if( !symbol )
285 return 0;
286
287 std::vector<wxString> messages;
288 const int grid_size = KiROUND( getView()->GetGAL()->GetGridSize().x );
289
290 CheckLibSymbol( symbol, messages, grid_size, m_frame );
291
292 if( messages.empty() )
293 {
294 DisplayInfoMessage( m_frame, _( "No symbol issues found." ) );
295 }
296 else
297 {
298 HTML_MESSAGE_BOX dlg( m_frame, _( "Symbol Warnings" ) );
299
300 for( const wxString& single_msg : messages )
301 dlg.AddHTML_Text( single_msg );
302
303 dlg.ShowModal();
304 }
305
306 return 0;
307}
308
309
311{
312 if( m_busSyntaxHelp )
313 {
314 m_busSyntaxHelp->Raise();
315 m_busSyntaxHelp->Show( true );
316 return 0;
317 }
318
320 return 0;
321}
322
323
325{
326 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
327
328 wxCHECK( schEditorFrame, 0 );
329
330 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
331
332 if( selection.Empty() )
333 {
334 m_frame->ShowInfoBarError( _( "Select a symbol to diff against its library equivalent." ) );
335 return 0;
336 }
337
338 DiffSymbol( static_cast<SCH_SYMBOL*>( selection.Front() ) );
339 return 0;
340}
341
342
344{
345 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
346
347 wxCHECK( schEditorFrame, /* void */ );
348
349 DIALOG_BOOK_REPORTER* dialog = schEditorFrame->GetSymbolDiffDialog();
350
351 wxCHECK( dialog, /* void */ );
352
353 dialog->DeleteAllPages();
354 dialog->SetUserItemID( symbol->m_Uuid );
355
356 wxString symbolDesc = wxString::Format( _( "Symbol %s" ), symbol->GetField( FIELD_T::REFERENCE )->GetText() );
357 LIB_ID libId = symbol->GetLibId();
358 wxString libName = libId.GetLibNickname();
359 wxString symbolName = libId.GetLibItemName();
360
361 WX_HTML_REPORT_BOX* r = dialog->AddHTMLPage( _( "Summary" ) );
362
363 r->Report( wxS( "<h7>" ) + _( "Schematic vs library diff for:" ) + wxS( "</h7>" ) );
364 r->Report( wxS( "<ul><li>" ) + EscapeHTML( symbolDesc ) + wxS( "</li>" )
365 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
366 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( symbolName )
367 + wxS( "</li></ul>" ) );
368
369 r->Report( "" );
370
372
373 if( !libs->HasLibrary( libName, false ) )
374 {
375 r->Report( _( "The library is not included in the current configuration." )
376 + wxS( "&nbsp;&nbsp;&nbsp" )
377 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
378 }
379 else if( !libs->HasLibrary( libName, true ) )
380 {
381 r->Report( _( "The library is not enabled in the current configuration." )
382 + wxS( "&nbsp;&nbsp;&nbsp" )
383 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Symbol Libraries" ) + wxS( "</a>" ) );
384 }
385 else
386 {
387 std::unique_ptr<LIB_SYMBOL> flattenedLibSymbol;
388 std::unique_ptr<LIB_SYMBOL> flattenedSchSymbol = symbol->GetLibSymbolRef()->Flatten();
389
390 try
391 {
392 if( LIB_SYMBOL* libAlias = libs->LoadSymbol( libName, symbolName ) )
393 flattenedLibSymbol = libAlias->Flatten();
394 }
395 catch( const IO_ERROR& )
396 {
397 }
398
399 if( !flattenedLibSymbol )
400 {
401 r->Report( wxString::Format( _( "The library no longer contains the item %s." ), symbolName ) );
402 }
403 else
404 {
405 std::vector<SCH_FIELD> fields;
406
407 for( SCH_FIELD& field : symbol->GetFields() )
408 {
409 fields.emplace_back( SCH_FIELD( flattenedLibSymbol.get(), field.GetId(), field.GetName( false ) ) );
410 fields.back().CopyText( field );
411 fields.back().SetAttributes( field );
412 fields.back().Move( -symbol->GetPosition() );
413 }
414
415 flattenedSchSymbol->SetFields( fields );
416
417 int flags = schEditorFrame->Schematic().Settings().SymbolCompareFlags();
418
419 if( flattenedSchSymbol->Compare( *flattenedLibSymbol, flags, r ) == 0 )
420 r->Report( _( "No relevant differences detected." ) );
421
422 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
423 SYMBOL_DIFF_WIDGET* diff = constructDiffPanel( panel );
424
425 diff->DisplayDiff( flattenedSchSymbol.release(), flattenedLibSymbol.release(),
426 symbol->GetUnit(), symbol->GetBodyStyle() );
427 }
428 }
429
430 r->Flush();
431
432 dialog->Raise();
433 dialog->Show( true );
434}
435
436
438{
439 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
440
441 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
442 SYMBOL_DIFF_WIDGET* diffWidget = new SYMBOL_DIFF_WIDGET( aParentPanel, backend );
443
444 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
445 aParentPanel->SetSizer( sizer );
446 aParentPanel->Layout();
447
448 return diffWidget;
449}
450
451
452namespace
453{
454void buildSchOverrides( const KICAD_DIFF::DOCUMENT_DIFF& aDiff, const KICAD_DIFF::DIFF_COLOR_THEME& aTheme,
455 std::map<KIID, KIGFX::COLOR4D>& aRefO, std::map<KIID, KIGFX::COLOR4D>& aCompO,
456 std::map<KIID, KICAD_DIFF::CATEGORY>& aCats )
457{
458 aRefO.clear();
459 aCompO.clear();
460 aCats.clear();
461
462 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> visit =
463 [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
464 {
465 if( !aChange.id.empty() )
466 {
467 const KIID& kiid = aChange.id.back();
468 aCats[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
469
470 switch( aChange.kind )
471 {
472 case KICAD_DIFF::CHANGE_KIND::ADDED: aCompO[kiid] = aTheme.added; break;
474 aRefO[kiid] = aTheme.removed;
475 aCompO[kiid] = aTheme.removed;
476 break;
478 aRefO[kiid] = aTheme.modified;
479 aCompO[kiid] = aTheme.modified;
480 break;
481 default:
482 aRefO[kiid] = aTheme.conflict;
483 aCompO[kiid] = aTheme.conflict;
484 break;
485 }
486 }
487
488 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
489 visit( child );
490 };
491
492 for( const KICAD_DIFF::ITEM_CHANGE& change : aDiff.changes )
493 visit( change );
494}
495
496
497DIALOG_KICAD_DIFF::SHEET_SWITCHER makeSchSwitcher( SCHEMATIC* aRef, SCHEMATIC* aComp,
498 std::map<KIID, KIGFX::COLOR4D> aRefOverrides,
499 std::map<KIID, KIGFX::COLOR4D> aCompOverrides,
500 std::map<KIID, KICAD_DIFF::CATEGORY> aCategories,
501 const KICAD_DIFF::DIFF_COLOR_THEME& aTheme )
502{
503 auto holder = std::make_shared<std::vector<std::unique_ptr<SCH_ITEM>>>();
504
505 return [aRef, aComp, modifiedColor = aTheme.modified, removedColor = aTheme.removed,
506 refO = std::move( aRefOverrides ), compO = std::move( aCompOverrides ), cats = std::move( aCategories ),
507 holder]( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& aSheetPath )
508 {
509 SCH_SCREEN* compScreen = aComp->RootScreen();
510 SCH_SCREEN* refScreen = aRef->RootScreen();
511
512 if( !aSheetPath.empty() )
513 {
514 if( auto sp = aComp->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
515 compScreen = sp->LastScreen();
516
517 if( auto sp = aRef->Hierarchy().GetSheetPathByKIIDPath( aSheetPath, true ) )
518 refScreen = sp->LastScreen();
519 }
520
521 aCanvas.SetContextItems( {} );
522 holder->clear();
523
524 if( refScreen )
525 {
526 for( const auto& [kiid, c] : refO )
527 {
528 if( c != removedColor )
529 continue;
530
531 for( SCH_ITEM* item : refScreen->Items() )
532 {
533 if( item && item->m_Uuid == kiid )
534 {
535 if( auto* clone = dynamic_cast<SCH_ITEM*>( item->Clone() ) )
536 holder->emplace_back( clone );
537
538 break;
539 }
540 }
541 }
542 }
543
544 std::vector<KIGFX::VIEW_ITEM*> extras;
545
546 for( const auto& clone : *holder )
547 extras.push_back( clone.get() );
548
549 KICAD_DIFF::ConfigureSchDiffCanvasContext( aCanvas, nullptr, aComp, modifiedColor, compO, extras, cats, nullptr,
550 compScreen );
551 };
552}
553
554
555struct DRILL_FRAME
556{
557 SCH_SHEET_PATH editorPath;
558 SCHEMATIC* compSch = nullptr;
559 wxString compFile;
560 KIID_PATH compScope;
561};
562
563
564void reloadDrillFrame( DIALOG_KICAD_DIFF& aDlg, SCHEMATIC* aRefSch, const KICAD_DIFF::DIFF_COLOR_THEME& aTheme,
565 const DRILL_FRAME& aFrame )
566{
567 KICAD_DIFF::SCH_DIFFER differ( aRefSch, aFrame.compSch, aFrame.compFile );
568 const KIID_PATH scope = aFrame.editorPath.Path();
569
570 if( !scope.empty() && !aFrame.compScope.empty() )
571 differ.SetScope( scope, aFrame.compScope );
572
573 KICAD_DIFF::DOCUMENT_DIFF diff = differ.Diff();
574
575 std::map<KIID, KIGFX::COLOR4D> refO;
576 std::map<KIID, KIGFX::COLOR4D> compO;
577 std::map<KIID, KICAD_DIFF::CATEGORY> cats;
578 buildSchOverrides( diff, aTheme, refO, compO, cats );
579
580 auto switcher = makeSchSwitcher( aRefSch, aFrame.compSch, refO, compO, cats, aTheme );
581
582 SCH_SCREEN* refScreen = aFrame.editorPath.LastScreen();
583 wxString refLabel = refScreen ? refScreen->GetFileName() : wxString();
584
585 aDlg.Reload( refLabel, aFrame.compFile, std::move( diff ), /*aReferenceGeometry=*/{},
586 /*aComparisonGeometry=*/{}, std::move( switcher ), scope );
587}
588} // namespace
589
590
592{
593 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
594
595 wxCHECK( schEditorFrame, 0 );
596
597 wxFileDialog dlg( schEditorFrame, _( "Choose Schematic to Compare With" ), wxEmptyString,
598 wxEmptyString, FILEEXT::KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
599
600 if( dlg.ShowModal() != wxID_OK )
601 return 0;
602
603 wxFileName otherFn( dlg.GetPath() );
604 otherFn.MakeAbsolute();
605
606 if( !otherFn.GetExt().IsSameAs( FILEEXT::KiCadSchematicFileExtension, false ) )
607 {
608 schEditorFrame->ShowInfoBarError( _( "Select a KiCad s-expression schematic file (.kicad_sch)." ) );
609 return 0;
610 }
611
612 const wxString otherPath = otherFn.GetFullPath();
613
614 wxFileName projectFn = otherFn;
615 projectFn.SetExt( FILEEXT::ProjectFileExtension );
616 const wxString projectPath = projectFn.GetFullPath();
617
618 wxFileName activeProjectFn( schEditorFrame->Prj().GetProjectFullName() );
619 activeProjectFn.MakeAbsolute();
620
621 // Refuse the self-compare; attaching the active PROJECT to a second
622 // SCHEMATIC would clobber its ERC/schematic settings.
623 if( projectFn.SameAs( activeProjectFn ) )
624 {
625 schEditorFrame->ShowInfoBarError( _( "Select a schematic file from another project to compare." ) );
626 return 0;
627 }
628
629 return showSchematicComparison( otherPath, projectPath, otherPath );
630}
631
632
633int SCH_INSPECTION_TOOL::showSchematicComparison( const wxString& aOtherPath, const wxString& aProjectPath,
634 const wxString& aComparisonLabel )
635{
636 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
637
638 wxCHECK( schEditorFrame, 0 );
639
640 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
641
642 wxCHECK( mgr, 0 );
643
644 // A missing .kicad_pro is fine (LoadProject returns false but still
645 // inserts a defaults-only slot); a present-but-malformed .kicad_pro is
646 // treated as failure.
647 bool projectLoadOk = mgr->LoadProject( aProjectPath, false );
648 PROJECT* otherPrj = mgr->GetProject( aProjectPath );
649
650 if( !otherPrj )
651 {
652 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
653 return 0;
654 }
655
656 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
657 {
658 mgr->UnloadProject( otherPrj, false );
659 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
660 return 0;
661 }
662
663 // SCH_DIFFER walks the sheet hierarchy and properties; connectivity build
664 // is unnecessary and slow for a read-only compare.
665 SCHEMATIC* loadedSchematic = nullptr;
666
667 try
668 {
669 loadedSchematic = EESCHEMA_HELPERS::LoadSchematic( aOtherPath,
670 /*aSetActive=*/false,
671 /*aForceDefaultProject=*/false, otherPrj,
672 /*aCalculateConnectivity=*/false );
673 }
674 catch( ... )
675 {
676 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
677 mgr->UnloadProject( otherPrj, false );
678 return 0;
679 }
680
681 SCHEMATIC* mySch = &schEditorFrame->Schematic();
682
683 // Belt-and-suspenders: EESCHEMA_HELPERS::LoadSchematic has a fallback
684 // branch that returns the active editor's schematic when the project
685 // happens to be the active one. The path guard above should have caught
686 // this, but a path-alias miss would leave the unique_ptr owning the live
687 // schematic and SetProject(nullptr) would destroy editor state.
688 if( loadedSchematic == mySch )
689 {
690 schEditorFrame->ShowInfoBarError( _( "Select a schematic file from another project to compare." ) );
691 mgr->UnloadProject( otherPrj, false );
692 return 0;
693 }
694
695 std::unique_ptr<SCHEMATIC> otherSchematic{ loadedSchematic };
696
697 if( !otherSchematic )
698 {
699 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
700 mgr->UnloadProject( otherPrj, false );
701 return 0;
702 }
703
704 KICAD_DIFF::SCH_DIFFER differ( mySch, otherSchematic.get(), aOtherPath );
705
706 // Scope to the editor's current sheet. Comparison resolves the matching
707 // sheet by KIID when present, else falls back to its root.
708 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
709 const SCH_SHEET_LIST otherSheets = otherSchematic->BuildSheetListSortedByPageNumbers();
710 KIID_PATH scopeAfter;
711
712 if( auto sp = otherSchematic->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
713 scopeAfter = sp->Path();
714 else if( !otherSheets.empty() )
715 scopeAfter = otherSheets.front().Path();
716
717 if( !scopeBefore.empty() && !scopeAfter.empty() )
718 differ.SetScope( scopeBefore, scopeAfter );
719
721
723
724 std::map<KIID, KIGFX::COLOR4D> refOverrides;
725 std::map<KIID, KIGFX::COLOR4D> compOverrides;
726 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
727
728 buildSchOverrides( result, theme, refOverrides, compOverrides, kiidCategories );
729
731 refOverrides );
733 theme.comparison,
734 compOverrides );
735
736 DIALOG_KICAD_DIFF::SHEET_SWITCHER initialSwitcher = makeSchSwitcher( mySch, otherSchematic.get(), refOverrides,
737 compOverrides, kiidCategories, theme );
738
739 KIID_PATH initialSheet = schEditorFrame->GetCurrentSheet().Path();
740 SCH_SCREEN* currentSheetScreen = schEditorFrame->GetCurrentSheet().LastScreen();
741 wxString referenceLabel = currentSheetScreen ? currentSheetScreen->GetFileName() : mySch->GetFileName();
742
743 DIALOG_KICAD_DIFF dlgDiff( schEditorFrame, referenceLabel, aComparisonLabel, result, std::move( refGeometry ),
744 std::move( compGeometry ), std::move( initialSwitcher ), std::move( initialSheet ) );
745
746 // Drill state owns the comparison schematics loaded across double-click
747 // drills. The editor schematic stays the reference on all drills.
748 DRILL_FRAME drillCurrent{ schEditorFrame->GetCurrentSheet(), otherSchematic.get(), aOtherPath, scopeAfter };
749
750 std::vector<DRILL_FRAME> drillBack;
751 std::vector<std::unique_ptr<SCHEMATIC>> drilledSchs;
752
753 drilledSchs.push_back( std::move( otherSchematic ) );
754
755 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff.DiffCanvas() )
756 {
757 canvas->SetDoubleClickHandler(
758 [&dlgDiff, &drillCurrent, &drillBack, &drilledSchs, mySch, otherPrj, theme,
759 schEditorFrame]( KIGFX::VIEW_ITEM* aItem )
760 {
761 auto* sheet = dynamic_cast<SCH_SHEET*>( aItem );
762
763 if( !sheet )
764 return;
765
766 const wxString sheetFile = sheet->GetFileName();
767
768 if( sheetFile.IsEmpty() )
769 return;
770
771 KIID_PATH newEditorKiid = drillCurrent.editorPath.Path();
772 newEditorKiid.push_back( sheet->m_Uuid );
773
774 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
775
776 if( !newEditorSheet )
777 return;
778
779 // Prefer resolving inside the current comparison schematic so
780 // shared-sheet instance context is preserved on both sides.
781 DRILL_FRAME next{ *newEditorSheet, drillCurrent.compSch, drillCurrent.compFile, KIID_PATH() };
782
783 if( auto compMatch = next.compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
784 {
785 next.compScope = compMatch->Path();
786
787 if( SCH_SCREEN* compMatchScreen = compMatch->LastScreen() )
788 next.compFile = compMatchScreen->GetFileName();
789 }
790 else
791 {
792 wxFileName newCompFn( sheetFile );
793 newCompFn.MakeAbsolute( wxFileName( drillCurrent.compFile ).GetPath() );
794
795 SCHEMATIC* loaded = EESCHEMA_HELPERS::LoadSchematic( newCompFn.GetFullPath(),
796 /*aSetActive*/ false,
797 /*aForceDefaultProject*/ false, otherPrj,
798 /*aCalculateConnectivity*/ false );
799
800 if( !loaded )
801 {
802 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ),
803 newCompFn.GetFullPath() ) );
804 return;
805 }
806
807 const SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
808
809 if( !loadedSheets.empty() )
810 next.compScope = loadedSheets.front().Path();
811
812 drilledSchs.emplace_back( loaded );
813 next.compSch = loaded;
814 next.compFile = newCompFn.GetFullPath();
815 }
816
817 reloadDrillFrame( dlgDiff, mySch, theme, next );
818
819 drillBack.push_back( drillCurrent );
820 drillCurrent = next;
821 dlgDiff.EnableUp( true );
822 } );
823
824 dlgDiff.SetUpHandler(
825 [&dlgDiff, &drillCurrent, &drillBack, mySch, theme]()
826 {
827 if( drillBack.empty() )
828 return;
829
830 reloadDrillFrame( dlgDiff, mySch, theme, drillBack.back() );
831
832 drillCurrent = drillBack.back();
833 drillBack.pop_back();
834 dlgDiff.EnableUp( !drillBack.empty() );
835 } );
836 }
837
838 dlgDiff.ShowModal();
839
840 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff.DiffCanvas() )
841 canvas->SetContextItems( {} );
842
843 // Detach schematics from the project before unloading so the project's
844 // ERC and schematic settings release cleanly.
845 for( auto& sch : drilledSchs )
846 {
847 if( sch )
848 sch->SetProject( nullptr );
849 }
850
851 drilledSchs.clear();
852
853 mgr->UnloadProject( otherPrj, false );
854
855 return 0;
856}
857
858
860{
861 SCH_EDIT_FRAME* schEditorFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
862
863 wxCHECK( schEditorFrame, 0 );
864
865 SCHEMATIC* mySch = &schEditorFrame->Schematic();
866
867 if( mySch->GetFileName().IsEmpty() )
868 {
869 schEditorFrame->ShowInfoBarError( _( "Save the schematic before comparing against local history." ) );
870 return 0;
871 }
872
873 const wxString projectPath = schEditorFrame->Prj().GetProjectPath();
874 LOCAL_HISTORY& history = schEditorFrame->Kiway().LocalHistory();
875
876 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
877
878 if( snapshots.empty() )
879 {
880 schEditorFrame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
881 return 0;
882 }
883
884 SETTINGS_MANAGER* mgr = schEditorFrame->GetSettingsManager();
885
886 wxCHECK( mgr, 0 );
887
888 auto relTo =
889 [&]( const wxString& aFull )
890 {
891 wxFileName fn( aFull );
892 fn.MakeRelativeTo( projectPath );
893 return fn.GetFullPath( wxPATH_UNIX );
894 };
895
896 const wxString rootRel = relTo( mySch->GetFileName() );
897 const wxString projRel = relTo( schEditorFrame->Prj().GetProjectFullName() );
898
899 // One entry per distinct schematic version: newest-first, skipping commits
900 // with no schematic or whose schematic content (any .kicad_sch sheet) matches
901 // the previously kept one. This drops board-only saves like "PCB Save".
902 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> filtered;
903 wxString prevFingerprint;
904
905 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
906 {
907 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_sch" ) );
908
909 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
910 continue;
911
912 prevFingerprint = fingerprint;
913 filtered.push_back( s );
914 }
915
916 if( filtered.empty() )
917 {
918 schEditorFrame->ShowInfoBarError( _( "No local history snapshots change this schematic." ) );
919 return 0;
920 }
921
922 snapshots = std::move( filtered );
923
924 std::vector<wxString> labels;
925
926 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
927 {
928 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
929 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
930 }
931
933 const KIID_PATH scopeBefore = schEditorFrame->GetCurrentSheet().Path();
934
935 struct SCH_DIFF_VIEW
936 {
941 KIID_PATH compScope;
942 };
943
944 auto buildView =
945 [&]( SCHEMATIC* aComp, const wxString& aPath ) -> SCH_DIFF_VIEW
946 {
947 SCH_DIFF_VIEW view;
948 KICAD_DIFF::SCH_DIFFER differ( mySch, aComp, aPath );
949
950 KIID_PATH scopeAfter;
951
952 if( std::optional<SCH_SHEET_PATH> sp = aComp->Hierarchy().GetSheetPathByKIIDPath( scopeBefore, true ) )
953 {
954 scopeAfter = sp->Path();
955 }
956 else
957 {
959
960 if( !sheets.empty() )
961 scopeAfter = sheets.front().Path();
962 }
963
964 if( !scopeBefore.empty() && !scopeAfter.empty() )
965 differ.SetScope( scopeBefore, scopeAfter );
966
967 view.compScope = scopeAfter;
968 view.result = differ.Diff();
969
970 std::map<KIID, KIGFX::COLOR4D> refO;
971 std::map<KIID, KIGFX::COLOR4D> compO;
972 std::map<KIID, KICAD_DIFF::CATEGORY> cats;
973 buildSchOverrides( view.result, theme, refO, compO, cats );
974
975 view.refGeom = KICAD_DIFF::ExtractSchematicGeometry( *mySch, theme.reference, refO );
976 view.compGeom = KICAD_DIFF::ExtractSchematicGeometry( *aComp, theme.comparison, compO );
977 view.switcher = makeSchSwitcher( mySch, aComp, refO, compO, cats, theme );
978
979 return view;
980 };
981
982 // State for the revision currently shown. Swapped on each dropdown change.
983 std::unique_ptr<SCHEMATIC> curSch;
984 PROJECT* curPrj = nullptr;
985 wxString curTempDir;
986
987 // Drill state: which comparison sheet is shown and sub-schematics loaded on
988 // double-click. Reset when the revision changes.
989 DRILL_FRAME drillCurrent;
990 std::vector<DRILL_FRAME> drillBack;
991 std::vector<std::unique_ptr<SCHEMATIC>> drilledSchs;
992
993 auto cleanupCurrent =
994 [&]()
995 {
996 for( std::unique_ptr<SCHEMATIC>& sch : drilledSchs )
997 {
998 if( sch )
999 sch->SetProject( nullptr );
1000 }
1001
1002 drilledSchs.clear();
1003
1004 if( curSch )
1005 {
1006 curSch->SetProject( nullptr );
1007 curSch.reset();
1008 }
1009
1010 // Skip if the project was already evicted from the manager.
1011 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
1012 mgr->UnloadProject( curPrj, false );
1013
1014 curPrj = nullptr;
1015
1016 if( !curTempDir.IsEmpty() )
1017 {
1018 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
1019 curTempDir.Clear();
1020 }
1021 };
1022
1023 // Extract the hierarchy at snapshot aIndex and load its root sheet, cleaning
1024 // up the temp dir on any failure.
1025 auto loadRevision =
1026 [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir ) -> bool
1027 {
1028 const wxString hash = snapshots[aIndex].hash;
1029 wxFileName dirFn;
1030 dirFn.AssignDir( wxFileName::GetTempDir() );
1031 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
1032 const wxString tempDir = dirFn.GetPath();
1033
1034 // Extract just the schematic sheets and project file, skipping the
1035 // board, 3D models, gerbers, etc.
1036 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
1037 { wxS( ".kicad_sch" ), wxS( ".kicad_pro" ) } ) )
1038 {
1039 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1040 return false;
1041 }
1042
1043 const wxString root = tempDir + wxS( "/" ) + rootRel;
1044 const wxString proj = tempDir + wxS( "/" ) + projRel;
1045
1046 mgr->LoadProject( proj, false );
1047 PROJECT* prj = mgr->GetProject( proj );
1048
1049 if( !prj )
1050 {
1051 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1052 return false;
1053 }
1054
1055 SCHEMATIC* loaded = nullptr;
1056
1057 try
1058 {
1059 loaded = EESCHEMA_HELPERS::LoadSchematic( root, /*aSetActive*/ false,
1060 /*aForceDefaultProject*/ false, prj,
1061 /*aCalculateConnectivity*/ false );
1062 }
1063 catch( ... )
1064 {
1065 mgr->UnloadProject( prj, false );
1066 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1067 return false;
1068 }
1069
1070 if( !loaded || loaded == mySch )
1071 {
1072 mgr->UnloadProject( prj, false );
1073 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
1074 return false;
1075 }
1076
1077 aSch.reset( loaded );
1078 aPrj = prj;
1079 aTempDir = tempDir;
1080 return true;
1081 };
1082
1083 auto loadView =
1084 [&]( int aIndex, std::unique_ptr<SCHEMATIC>& aSch, PROJECT*& aPrj, wxString& aTempDir,
1085 SCH_DIFF_VIEW& aView ) -> bool
1086 {
1087 if( !loadRevision( aIndex, aSch, aPrj, aTempDir ) )
1088 return false;
1089
1090 try
1091 {
1092 aView = buildView( aSch.get(), aTempDir + wxS( "/" ) + rootRel );
1093 }
1094 catch( ... )
1095 {
1096 aSch->SetProject( nullptr );
1097 aSch.reset();
1098 mgr->UnloadProject( aPrj, false );
1099 aPrj = nullptr;
1100 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
1101 aTempDir.Clear();
1102 return false;
1103 }
1104
1105 return true;
1106 };
1107
1108 SCH_DIFF_VIEW view;
1109 int startIndex = 0;
1110
1111 if( !loadView( 0, curSch, curPrj, curTempDir, view ) )
1112 {
1113 schEditorFrame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
1114 return 0;
1115 }
1116
1117 // Default to the first commit back from HEAD that actually differs from the
1118 // current schematic, so opening lands on real changes rather than an in-sync HEAD.
1119 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
1120 {
1121 std::unique_ptr<SCHEMATIC> nextSch;
1122 PROJECT* nextPrj = nullptr;
1123 wxString nextTempDir;
1124 SCH_DIFF_VIEW nextView;
1125
1126 if( !loadView( startIndex + 1, nextSch, nextPrj, nextTempDir, nextView ) )
1127 break;
1128
1129 cleanupCurrent();
1130 view = std::move( nextView );
1131 curSch = std::move( nextSch );
1132 curPrj = nextPrj;
1133 curTempDir = nextTempDir;
1134 startIndex++;
1135 }
1136
1137 drillCurrent = { schEditorFrame->GetCurrentSheet(), curSch.get(), curTempDir + wxS( "/" ) + rootRel,
1138 view.compScope };
1139
1140 int shownIndex = startIndex;
1141
1142 SCH_SCREEN* curScreen = schEditorFrame->GetCurrentSheet().LastScreen();
1143 wxString referenceLabel = curScreen ? curScreen->GetFileName() : mySch->GetFileName();
1144
1145 std::unique_ptr<DIALOG_KICAD_DIFF> dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( schEditorFrame, referenceLabel,
1146 labels[startIndex], view.result,
1147 view.refGeom, view.compGeom,
1148 view.switcher, scopeBefore );
1149
1150 // Double-click a sheet to drill into its sub-schematic, within the current
1151 // revision's extracted hierarchy.
1152 if( WIDGET_DIFF_CANVAS* canvas = dlgDiff->DiffCanvas() )
1153 {
1154 canvas->SetDoubleClickHandler(
1155 [&]( KIGFX::VIEW_ITEM* aItem )
1156 {
1157 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( aItem );
1158
1159 if( !sheet || sheet->GetFileName().IsEmpty() )
1160 return;
1161
1162 KIID_PATH newEditorKiid = drillCurrent.editorPath.Path();
1163 newEditorKiid.push_back( sheet->m_Uuid );
1164
1165 auto newEditorSheet = mySch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true );
1166
1167 if( !newEditorSheet )
1168 return;
1169
1170 DRILL_FRAME next{ *newEditorSheet, drillCurrent.compSch, drillCurrent.compFile, KIID_PATH() };
1171
1172 if( auto compMatch = next.compSch->Hierarchy().GetSheetPathByKIIDPath( newEditorKiid, true ) )
1173 {
1174 next.compScope = compMatch->Path();
1175
1176 if( SCH_SCREEN* matchScreen = compMatch->LastScreen() )
1177 next.compFile = matchScreen->GetFileName();
1178 }
1179 else
1180 {
1181 wxFileName subFn( sheet->GetFileName() );
1182 subFn.MakeAbsolute( wxFileName( drillCurrent.compFile ).GetPath() );
1183
1184 SCHEMATIC* loaded = nullptr;
1185
1186 try
1187 {
1188 loaded = EESCHEMA_HELPERS::LoadSchematic( subFn.GetFullPath(), /*aSetActive=*/false,
1189 /*aForceDefaultProject=*/false, curPrj,
1190 /*aCalculateConnectivity=*/false );
1191 }
1192 catch( ... )
1193 {
1194 loaded = nullptr;
1195 }
1196
1197 if( !loaded || loaded == mySch )
1198 {
1199 schEditorFrame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ),
1200 subFn.GetFullPath() ) );
1201 return;
1202 }
1203
1204 SCH_SHEET_LIST loadedSheets = loaded->BuildSheetListSortedByPageNumbers();
1205
1206 if( !loadedSheets.empty() )
1207 next.compScope = loadedSheets.front().Path();
1208
1209 drilledSchs.emplace_back( loaded );
1210 next.compSch = loaded;
1211 next.compFile = subFn.GetFullPath();
1212 }
1213
1214 try
1215 {
1216 reloadDrillFrame( *dlgDiff, mySch, theme, next );
1217 }
1218 catch( ... )
1219 {
1220 schEditorFrame->ShowInfoBarError( _( "Could not open this sheet for comparison." ) );
1221 return;
1222 }
1223
1224 drillBack.push_back( drillCurrent );
1225 drillCurrent = next;
1226 dlgDiff->EnableUp( true );
1227 } );
1228
1229 dlgDiff->SetUpHandler(
1230 [&]()
1231 {
1232 if( drillBack.empty() )
1233 return;
1234
1235 try
1236 {
1237 reloadDrillFrame( *dlgDiff, mySch, theme, drillBack.back() );
1238 }
1239 catch( ... )
1240 {
1241 schEditorFrame->ShowInfoBarError( _( "Could not open this sheet for comparison." ) );
1242 return;
1243 }
1244
1245 drillCurrent = drillBack.back();
1246 drillBack.pop_back();
1247 dlgDiff->EnableUp( !drillBack.empty() );
1248 } );
1249 }
1250
1251 dlgDiff->SetRevisionChooser(
1252 labels, startIndex,
1253 [&]( int aIndex )
1254 {
1255 if( aIndex == shownIndex )
1256 return;
1257
1258 std::unique_ptr<SCHEMATIC> newSch;
1259 PROJECT* newPrj = nullptr;
1260 wxString newTempDir;
1261 SCH_DIFF_VIEW newView;
1262
1263 if( !loadView( aIndex, newSch, newPrj, newTempDir, newView ) )
1264 {
1265 schEditorFrame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
1266 return;
1267 }
1268
1269 dlgDiff->Reload( referenceLabel, labels[aIndex], newView.result, newView.refGeom,
1270 newView.compGeom, newView.switcher, scopeBefore );
1271
1272 cleanupCurrent();
1273 view = std::move( newView );
1274 curSch = std::move( newSch );
1275 curPrj = newPrj;
1276 curTempDir = newTempDir;
1277 shownIndex = aIndex;
1278
1279 // Restart drilling from the new revision's root.
1280 drillBack.clear();
1281 drillCurrent = { schEditorFrame->GetCurrentSheet(), curSch.get(), curTempDir + wxS( "/" ) + rootRel,
1282 view.compScope };
1283 dlgDiff->EnableUp( false );
1284 } );
1285
1286 dlgDiff->ShowModal();
1287
1288 // Destroy the dialog before the schematics it references are freed.
1289 dlgDiff.reset();
1290
1291 cleanupCurrent();
1292 return 0;
1293}
1294
1295
1297{
1298 SIMULATOR_FRAME* simFrame = (SIMULATOR_FRAME*) m_frame->Kiway().Player( FRAME_SIMULATOR, true );
1299
1300 if( !simFrame )
1301 return -1;
1302
1303 if( wxWindow* blocking_win = simFrame->Kiway().GetBlockingDialog() )
1304 blocking_win->Close( true );
1305
1306 simFrame->Show( true );
1307
1308 // On Windows, Raise() does not bring the window on screen, when iconized
1309 if( simFrame->IsIconized() )
1310 simFrame->Iconize( false );
1311
1312 simFrame->Raise();
1313
1314 return 0;
1315}
1316
1317
1319{
1320 wxString datasheet;
1321 std::vector<EMBEDDED_FILES*> filesStack;
1322
1323 if( m_frame->IsType( FRAME_SCH_SYMBOL_EDITOR ) )
1324 {
1325 LIB_SYMBOL* symbol = static_cast<SYMBOL_EDIT_FRAME*>( m_frame )->GetCurSymbol();
1326
1327 if( !symbol )
1328 return 0;
1329
1330 datasheet = symbol->GetDatasheetField().GetText();
1331 filesStack.push_back( symbol );
1332 }
1333 else if( m_frame->IsType( FRAME_SCH_VIEWER ) )
1334 {
1335 LIB_SYMBOL* entry = static_cast<SYMBOL_VIEWER_FRAME*>( m_frame )->GetSelectedSymbol();
1336
1337 if( !entry )
1338 return 0;
1339
1340 datasheet = entry->GetDatasheetField().GetText();
1341 filesStack.push_back( entry );
1342 }
1343 else if( m_frame->IsType( FRAME_SCH ) )
1344 {
1345 SCH_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_SYMBOL_T } );
1346
1347 if( selection.Empty() )
1348 return 0;
1349
1350 SCH_SYMBOL* symbol = (SCH_SYMBOL*) selection.Front();
1351 SCH_FIELD* field = symbol->GetField( FIELD_T::DATASHEET );
1352
1353 // Use GetShownText() to resolve any text variables
1354 datasheet = field->GetShownText( &symbol->Schematic()->CurrentSheet(), FOR_GUI );
1355 filesStack.push_back( symbol->Schematic() );
1356
1357 if( symbol->GetLibSymbolRef() )
1358 filesStack.push_back( symbol->GetLibSymbolRef().get() );
1359 }
1360
1361 if( datasheet.IsEmpty() || datasheet == wxS( "~" ) )
1362 {
1363 m_frame->ShowInfoBarError( _( "No datasheet defined." ) );
1364 }
1365 else
1366 {
1368 filesStack );
1369 }
1370
1371 return 0;
1372}
1373
1374
1376{
1377 SYMBOL_EDIT_FRAME* symbolEditFrame = dynamic_cast<SYMBOL_EDIT_FRAME*>( m_frame );
1378 SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame );
1379 SCH_SELECTION_TOOL* selTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
1380 SCH_SELECTION& selection = selTool->GetSelection();
1381
1382 // Note: the symbol viewer manages its own message panel
1383
1384 if( symbolEditFrame || schEditFrame )
1385 {
1386 if( selection.GetSize() == 1 )
1387 {
1388 EDA_ITEM* item = (EDA_ITEM*) selection.Front();
1389 std::vector<MSG_PANEL_ITEM> msgItems;
1390
1391 if( std::optional<wxString> uuid = GetMsgPanelDisplayUuid( item->m_Uuid ) )
1392 msgItems.emplace_back( _( "UUID" ), *uuid );
1393
1394 item->GetMsgPanelInfo( m_frame, msgItems );
1395 m_frame->SetMsgPanel( msgItems );
1396 }
1397 else
1398 {
1399 m_frame->ClearMsgPanel();
1400 }
1401 }
1402
1403 if( schEditFrame )
1404 {
1405 schEditFrame->UpdateNetHighlightStatus();
1406 schEditFrame->UpdateHierarchySelection();
1407 }
1408
1409 return 0;
1410}
1411
1412
1414{
1418 // See note 1:
1422
1429
1431
1432 // Note 1: tUpdateMessagePanel is called by CrossProbe. So uncomment this line if
1433 // call to CrossProbe is modifiied
1434 // Go( &SCH_INSPECTION_TOOL::UpdateMessagePanel, EVENTS::SelectedEvent );
1438}
1439
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
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:599
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:347
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsModified
Selected item had a property changed (except movement)
Definition actions.h:352
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:344
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
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:459
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:114
SCH_FIELD & GetDatasheetField()
Definition lib_symbol.h:459
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:149
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:304
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:139
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)
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:170
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
int GetBodyStyle() const
Definition sch_item.h:253
int GetUnit() const
Definition sch_item.h:243
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
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:392
Schematic symbol object.
Definition sch_symbol.h:73
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:906
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:163
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
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.
void SetContextItems(const std::vector< KIGFX::VIEW_ITEM * > &aItems)
Replace source document context items.
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:91
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:206
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.