KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_erc.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) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <advanced_config.h>
28#include <gestfich.h>
29#include <sch_screen.h>
30#include <sch_edit_frame.h>
31#include <project.h>
32#include <kiface_base.h>
33#include <reporter.h>
35#include <sch_marker.h>
36#include <connection_graph.h>
37#include <tools/sch_actions.h>
39#include <dialog_erc.h>
40#include <erc/erc.h>
41#include <erc/erc_report.h>
42#include <id.h>
43#include <confirm.h>
47#include <string_utils.h>
48#include <kiplatform/ui.h>
49
50#include <wx/ffile.h>
51#include <wx/filedlg.h>
52#include <wx/hyperlink.h>
53#include <wx/msgdlg.h>
54#include <sch_edit_tool.h>
55
56
57wxDEFINE_EVENT( EDA_EVT_CLOSE_ERC_DIALOG, wxCommandEvent );
58
59
60// wxWidgets spends *far* too long calculating column widths (most of it, believe it or
61// not, in repeatedly creating/destroying a wxDC to do the measurement in).
62// Use default column widths instead.
63static int DEFAULT_SINGLE_COL_WIDTH = 660;
64
65
66static SCHEMATIC* g_lastERCSchematic = nullptr;
67static bool g_lastERCRun = false;
68
69static std::vector<std::pair<wxString, int>> g_lastERCIgnored;
70
71
73 DIALOG_ERC_BASE( parent ),
75 m_parent( parent ),
76 m_markerTreeModel( nullptr ),
77 m_running( false ),
78 m_ercRun( false ),
79 m_centerMarkerOnIdle( nullptr ),
80 m_crossprobe( true ),
82 m_showAllErrors( false )
83{
84 m_currentSchematic = &parent->Schematic();
85
86 SetName( DIALOG_ERC_WINDOW_NAME ); // Set a window name to be able to find it
88
89 m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
90
91 m_messages->SetImmediateMode();
92
93 m_markerProvider = std::make_shared<SHEETLIST_ERC_ITEMS_PROVIDER>( &m_parent->Schematic() );
94
96 m_markerDataView->AssociateModel( m_markerTreeModel );
98
99 m_ignoredList->InsertColumn( 0, wxEmptyString, wxLIST_FORMAT_LEFT, DEFAULT_SINGLE_COL_WIDTH );
100
102 {
104
105 for( const auto& [ str, code ] : g_lastERCIgnored )
106 {
107 wxListItem listItem;
108 listItem.SetId( m_ignoredList->GetItemCount() );
109 listItem.SetText( str );
110 listItem.SetData( code );
111
112 m_ignoredList->InsertItem( listItem );
113 }
114 }
115
116 m_notebook->SetSelection( 0 );
117
118 SetupStandardButtons( { { wxID_OK, _( "Run ERC" ) },
119 { wxID_CANCEL, _( "Close" ) } } );
120
121 m_violationsTitleTemplate = m_notebook->GetPageText( 0 );
122 m_ignoredTitleTemplate = m_notebook->GetPageText( 1 );
123
124 m_errorsBadge->SetMaximumNumber( 999 );
125 m_warningsBadge->SetMaximumNumber( 999 );
126 m_exclusionsBadge->SetMaximumNumber( 999 );
127
129
130 Layout();
131
132 SetFocus();
133
135 {
136 m_crossprobe = cfg->m_ERCDialog.crossprobe;
137 m_scroll_on_crossprobe = cfg->m_ERCDialog.scroll_on_crossprobe;
138 m_showAllErrors = cfg->m_ERCDialog.show_all_errors;
139 }
140
141 // Now all widgets have the size fixed, call FinishDialogSettings
143}
144
145
147{
150
151 g_lastERCIgnored.clear();
152
153 for( int ii = 0; ii < m_ignoredList->GetItemCount(); ++ii )
154 g_lastERCIgnored.push_back( { m_ignoredList->GetItemText( ii ), m_ignoredList->GetItemData( ii ) } );
155
157 {
158 cfg->m_ERCDialog.crossprobe = m_crossprobe;
159 cfg->m_ERCDialog.scroll_on_crossprobe = m_scroll_on_crossprobe;
160 cfg->m_ERCDialog.show_all_errors = m_showAllErrors;
161 }
162
163 m_markerTreeModel->DecRef();
164}
165
166
168{
169 if( m_parent->CheckAnnotate(
170 []( ERCE_T, const wxString&, SCH_REFERENCE*, SCH_REFERENCE* )
171 {
172 } ) )
173 {
174 if( !m_infoBar->IsShownOnScreen() )
175 {
176 wxHyperlinkCtrl* button = new wxHyperlinkCtrl( m_infoBar, wxID_ANY, _( "Show Annotation dialog" ),
177 wxEmptyString );
178
179 button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
180 [&]( wxHyperlinkEvent& aEvent )
181 {
182 wxHtmlLinkEvent htmlEvent( aEvent.GetId(), wxHtmlLinkInfo( aEvent.GetURL() ) );
183 OnLinkClicked( htmlEvent );
184 } ) );
185
186 m_infoBar->RemoveAllButtons();
187 m_infoBar->AddButton( button );
188 m_infoBar->ShowMessage( _( "Schematic is not fully annotated. ERC results will be incomplete." ) );
189 }
190 }
191 else
192 {
193 if( m_infoBar->IsShownOnScreen() )
194 {
195 m_infoBar->RemoveAllButtons();
196 m_infoBar->Hide();
197 }
198 }
199}
200
201
203{
204 int severities = 0;
205
206 if( m_showErrors->GetValue() )
207 severities |= RPT_SEVERITY_ERROR;
208
209 if( m_showWarnings->GetValue() )
210 severities |= RPT_SEVERITY_WARNING;
211
212 if( m_showExclusions->GetValue() )
213 severities |= RPT_SEVERITY_EXCLUSION;
214
215 return severities;
216}
217
218
219void DIALOG_ERC::OnMenu( wxCommandEvent& event )
220{
221 // Build a pop menu:
222 wxMenu menu;
223
224 menu.Append( 4206, _( "Cross-probe Selected Items" ),
225 _( "Highlight corresponding items on canvas when selected in the ERC list" ),
226 wxITEM_CHECK );
227 menu.Check( 4206, m_crossprobe );
228
229 menu.Append( 4207, _( "Center on Cross-probe" ),
230 _( "When cross-probing, scroll the canvas so that the item is visible" ),
231 wxITEM_CHECK );
232 menu.Check( 4207, m_scroll_on_crossprobe );
233
234 menu.Append( 4208, _( "Show all errors" ),
235 _( "Show duplicate ERC markers on all applicable pins" ),
236 wxITEM_CHECK );
237 menu.Check( 4208, m_showAllErrors );
238
239 // menu_id is the selected submenu id from the popup menu or wxID_NONE
240 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
241
242 if( menu_id == 0 || menu_id == 4206 )
243 {
245 }
246 else if( menu_id == 1 || menu_id == 4207 )
247 {
249 }
250 else if( menu_id == 2 || menu_id == 4208 )
251 {
253 }
254}
255
256
257void DIALOG_ERC::OnCharHook( wxKeyEvent& aEvt )
258{
259 if( int hotkey = aEvt.GetKeyCode() )
260 {
261 if( aEvt.ControlDown() )
262 hotkey |= MD_CTRL;
263 if( aEvt.ShiftDown() )
264 hotkey |= MD_SHIFT;
265 if( aEvt.AltDown() )
266 hotkey |= MD_ALT;
267
268 if( hotkey == ACTIONS::excludeMarker.GetHotKey() )
269 {
271 return;
272 }
273 }
274
276}
277
278
280{
281 UpdateData();
282 return true;
283}
284
285
287{
288 // If ERC checks ever get slow enough we'll want a progress indicator...
289 //
290 // double cur = (double) m_progress.load() / m_maxProgress;
291 // cur = std::max( 0.0, std::min( cur, 1.0 ) );
292 //
293 // m_gauge->SetValue( KiROUND( cur * 1000.0 ) );
294 // wxSafeYield( this );
295
296 return !m_cancelled;
297}
298
299
300void DIALOG_ERC::AdvancePhase( const wxString& aMessage )
301{
302 // Will also call Report( aMessage ):
304 SetCurrentProgress( 0.0 );
305}
306
307
308void DIALOG_ERC::Report( const wxString& aMessage )
309{
310 m_messages->Report( aMessage );
311}
312
313
319
320
322{
323 int numErrors = 0;
324 int numWarnings = 0;
325 int numExcluded = 0;
326
327 int numMarkers = 0;
328
329 if( m_markerProvider )
330 {
331 numMarkers += m_markerProvider->GetCount();
332 numErrors += m_markerProvider->GetCount( RPT_SEVERITY_ERROR );
333 numWarnings += m_markerProvider->GetCount( RPT_SEVERITY_WARNING );
334 numExcluded += m_markerProvider->GetCount( RPT_SEVERITY_EXCLUSION );
335 }
336
337 bool markersOverflowed = false;
338
339 // We don't currently have a limit on ERC violations, so the above is always false.
340
341 wxString num;
342 wxString msg;
343
344 if( m_ercRun )
345 {
346 num.Printf( markersOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numMarkers );
347 msg.Printf( m_violationsTitleTemplate, num );
348 }
349 else
350 {
352 msg.Replace( wxT( "(%s)" ), wxEmptyString );
353 }
354
355 m_notebook->SetPageText( 0, msg );
356
357 if( m_ercRun )
358 {
359 num.Printf( wxT( "%d" ), m_ignoredList->GetItemCount() );
360 msg.sprintf( m_ignoredTitleTemplate, num );
361 }
362 else
363 {
365 msg.Replace( wxT( "(%s)" ), wxEmptyString );
366 }
367
368 m_notebook->SetPageText( 1, msg );
369
370 if( !m_ercRun && numErrors == 0 )
371 numErrors = -1;
372
373 if( !m_ercRun && numWarnings == 0 )
374 numWarnings = -1;
375
376 m_errorsBadge->UpdateNumber( numErrors, RPT_SEVERITY_ERROR );
377 m_warningsBadge->UpdateNumber( numWarnings, RPT_SEVERITY_WARNING );
378 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
379}
380
381
382void DIALOG_ERC::OnDeleteOneClick( wxCommandEvent& aEvent )
383{
384 if( m_notebook->GetSelection() == 0 )
385 {
386 // Clear the selection. It may be the selected ERC marker.
387 m_parent->GetToolManager()->RunAction( ACTIONS::selectionClear );
388
389 m_markerTreeModel->DeleteCurrentItem( true );
390
391 // redraw the schematic
393 }
394
396}
397
398
399void DIALOG_ERC::OnDeleteAllClick( wxCommandEvent& event )
400{
401 bool includeExclusions = false;
402 int numExcluded = 0;
403
404 if( m_markerProvider )
405 numExcluded += m_markerProvider->GetCount( RPT_SEVERITY_EXCLUSION );
406
407 if( numExcluded > 0 )
408 {
409 wxMessageDialog dlg( this, _( "Delete exclusions too?" ), _( "Delete All Markers" ),
410 wxYES_NO | wxCANCEL | wxCENTER | wxICON_QUESTION );
411 dlg.SetYesNoLabels( _( "Errors and Warnings Only" ),
412 _( "Errors, Warnings and Exclusions" ) );
413
414 int ret = dlg.ShowModal();
415
416 if( ret == wxID_CANCEL )
417 return;
418 else if( ret == wxID_NO )
419 includeExclusions = true;
420 }
421
422 deleteAllMarkers( includeExclusions );
423 m_ercRun = false;
424
425 // redraw the schematic
428}
429
430
431void DIALOG_ERC::OnCancelClick( wxCommandEvent& aEvent )
432{
433 if( m_running )
434 {
435 m_cancelled = true;
436 return;
437 }
438
439 m_parent->ClearFocus();
440
441 aEvent.Skip();
442}
443
444
445void DIALOG_ERC::OnCloseErcDialog( wxCloseEvent& aEvent )
446{
447 m_parent->ClearFocus();
448
449 // Dialog is mode-less so let the parent know that it needs to be destroyed.
450 if( !IsModal() && !IsQuasiModal() )
451 {
452 if( wxWindow* parent = GetParent() )
453 wxQueueEvent( parent, new wxCommandEvent( EDA_EVT_CLOSE_ERC_DIALOG, wxID_ANY ) );
454 }
455
456 aEvent.Skip();
457}
458
459
460void DIALOG_ERC::OnLinkClicked( wxHtmlLinkEvent& event )
461{
462 m_parent->OnAnnotate();
463}
464
465
466void DIALOG_ERC::OnRunERCClick( wxCommandEvent& event )
467{
468 wxBusyCursor busy;
469
470 SCHEMATIC* sch = &m_parent->Schematic();
471
473
474 sch->RecordERCExclusions();
475 deleteAllMarkers( true );
476
477 std::vector<std::reference_wrapper<RC_ITEM>> violations = ERC_ITEM::GetItemsWithSeverities();
478 m_ignoredList->DeleteAllItems();
479
480 for( std::reference_wrapper<RC_ITEM>& item : violations )
481 {
482 if( sch->ErcSettings().GetSeverity( item.get().GetErrorCode() ) == RPT_SEVERITY_IGNORE )
483 {
484 wxListItem listItem;
485 listItem.SetId( m_ignoredList->GetItemCount() );
486 listItem.SetText( wxT( " • " ) + item.get().GetErrorText( true ) );
487 listItem.SetData( item.get().GetErrorCode() );
488
489 m_ignoredList->InsertItem( listItem );
490 }
491 }
492
493 m_ignoredList->SetColumnWidth( 0, m_ignoredList->GetParent()->GetClientSize().x - 20 );
494
495 m_cancelled = false;
496 Raise();
497
498 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
499 m_messages->Clear();
500 wxSafeYield(); // Allow time slice to refresh Messages
501
502 m_running = true;
503 m_sdbSizer1Cancel->SetLabel( _( "Cancel" ) );
504 m_sdbSizer1OK->Enable( false );
505 m_deleteOneMarker->Enable( false );
506 m_deleteAllMarkers->Enable( false );
507 m_saveReport->Enable( false );
508
509 int itemsNotAnnotated = m_parent->CheckAnnotate(
510 []( ERCE_T aType, const wxString& aMsg, SCH_REFERENCE* aItemA, SCH_REFERENCE* aItemB )
511 {
512 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( aType );
513 ercItem->SetErrorMessage( aMsg );
514
515 if( aItemB )
516 ercItem->SetItems( aItemA->GetSymbol(), aItemB->GetSymbol() );
517 else
518 ercItem->SetItems( aItemA->GetSymbol() );
519
520 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aItemA->GetSymbol()->GetPosition() );
521 aItemA->GetSheetPath().LastScreen()->Append( marker );
522 } );
523
524 testErc();
525
526 if( itemsNotAnnotated )
527 {
528 m_messages->ReportHead( wxString::Format( _( "%d symbol(s) require annotation.<br><br>" ),
529 itemsNotAnnotated ),
531 }
532
533 if( m_cancelled )
534 m_messages->Report( _( "-------- ERC cancelled by user.<br><br>" ), RPT_SEVERITY_INFO );
535 else
536 m_messages->Report( _( "Done.<br><br>" ), RPT_SEVERITY_INFO );
537
538 Raise();
539 wxSafeYield(); // Allow time slice to refresh Messages
540
541 m_running = false;
542 m_sdbSizer1Cancel->SetLabel( _( "Close" ) );
543 m_sdbSizer1OK->Enable( true );
544 m_deleteOneMarker->Enable( true );
545 m_deleteAllMarkers->Enable( true );
546 m_saveReport->Enable( true );
547
548 if( !m_cancelled )
549 {
550 m_sdbSizer1Cancel->SetDefault();
551
552 // wxWidgets has a tendency to keep both buttons highlighted without the following:
553 m_sdbSizer1OK->Enable( false );
554
555 wxMilliSleep( 500 );
556 m_runningResultsBook->ChangeSelection( 1 );
558
559 // now re-enable m_sdbSizerOK button
560 m_sdbSizer1OK->Enable( true );
561 }
562
563 m_ercRun = true;
566 // set float level again, it can be lost due to window events during test run
568}
569
570
572{
573 WINDOW_THAWER thawer( m_parent );
574
575 m_parent->GetCanvas()->Refresh();
576}
577
578
580{
581 wxFileName fn;
582
583 SCHEMATIC* sch = &m_parent->Schematic();
584
585 SCH_SCREENS screens( sch->Root() );
586 ERC_TESTER tester( sch, m_showAllErrors );
587
588 {
589 wxBusyCursor dummy;
590 tester.RunTests( m_parent->GetCanvas()->GetView()->GetDrawingSheet(), m_parent,
591 m_parent->Kiway().KiFACE( KIWAY::FACE_CVPCB ), &m_parent->Prj(), this );
592 }
593
594 // Update marker list:
596
597 // Display new markers from the current screen:
598 for( SCH_ITEM* marker : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) )
599 {
600 m_parent->GetCanvas()->GetView()->Remove( marker );
601 m_parent->GetCanvas()->GetView()->Add( marker );
602 }
603
604 m_parent->GetCanvas()->Refresh();
605}
606
607
608void DIALOG_ERC::OnERCItemSelected( wxDataViewEvent& aEvent )
609{
610 if( !m_crossprobe )
611 {
612 aEvent.Skip();
613 return;
614 }
615
616 const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
617 SCH_SHEET_PATH sheet;
618 SCH_ITEM* item = m_parent->Schematic().ResolveItem( itemID, &sheet, true );
619
621 {
622 // we already came from a cross-probe of the marker in the document; don't go
623 // around in circles
624 }
625 else if( item && item->GetClass() != wxT( "DELETED_SHEET_ITEM" ) )
626 {
627 const RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
628
629 if( node )
630 {
631 // Determine the owning sheet for sheet-specific items
632 std::shared_ptr<ERC_ITEM> ercItem = std::static_pointer_cast<ERC_ITEM>( node->m_RcItem );
633
634 switch( node->m_Type )
635 {
637 if( ercItem->IsSheetSpecific() )
638 sheet = ercItem->GetSpecificSheetPath();
639 break;
641 if( ercItem->MainItemHasSheetPath() )
642 sheet = ercItem->GetMainItemSheetPath();
643 break;
645 if( ercItem->AuxItemHasSheetPath() )
646 sheet = ercItem->GetAuxItemSheetPath();
647 break;
648 default:
649 break;
650 }
651 }
652
653 WINDOW_THAWER thawer( m_parent );
654
655 if( !sheet.empty() && sheet != m_parent->GetCurrentSheet() )
656 {
657 m_parent->GetToolManager()->RunAction<SCH_SHEET_PATH*>( SCH_ACTIONS::changeSheet, &sheet );
658 m_parent->RedrawScreen( m_parent->GetScreen()->m_ScrollCenter, false );
659 }
660
661 m_parent->FocusOnItem( item, m_scroll_on_crossprobe );
663 }
664
665 aEvent.Skip();
666}
667
668
669void DIALOG_ERC::OnERCItemDClick( wxDataViewEvent& aEvent )
670{
671 if( aEvent.GetItem().IsOk() )
672 {
673 // turn control over to m_parent, hide this DIALOG_ERC window,
674 // no destruction so we can preserve listbox cursor
675 if( !IsModal() )
676 Show( false );
677 }
678
679 aEvent.Skip();
680}
681
682
683void DIALOG_ERC::OnERCItemRClick( wxDataViewEvent& aEvent )
684{
685 TOOL_MANAGER* toolMgr = m_parent->GetToolManager();
686 SCH_INSPECTION_TOOL* inspectionTool = toolMgr->GetTool<SCH_INSPECTION_TOOL>();
687 SCH_EDIT_TOOL* editTool = toolMgr->GetTool<SCH_EDIT_TOOL>();
688 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
689
690 if( !node )
691 return;
692
693 ERC_SETTINGS& settings = m_parent->Schematic().ErcSettings();
694
695 std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
696 wxString listName;
697 wxMenu menu;
698
699 switch( settings.GetSeverity( rcItem->GetErrorCode() ) )
700 {
701 case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
702 case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
703 default: listName = _( "appropriate" ); break;
704 }
705
706 enum MENU_IDS
707 {
708 ID_EDIT_EXCLUSION_COMMENT = 4467,
709 ID_REMOVE_EXCLUSION,
710 ID_REMOVE_EXCLUSION_ALL,
711 ID_ADD_EXCLUSION,
712 ID_ADD_EXCLUSION_WITH_COMMENT,
713 ID_ADD_EXCLUSION_ALL,
714 ID_INSPECT_VIOLATION,
715 ID_FIX_VIOLATION,
716 ID_EDIT_PIN_CONFLICT_MAP,
717 ID_EDIT_CONNECTION_GRID,
718 ID_SET_SEVERITY_TO_ERROR,
719 ID_SET_SEVERITY_TO_WARNING,
720 ID_SET_SEVERITY_TO_IGNORE,
721 ID_EDIT_SEVERITIES,
722 };
723
724 if( rcItem->GetParent()->IsExcluded() )
725 {
726 menu.Append( ID_REMOVE_EXCLUSION,
727 _( "Remove exclusion for this violation" ),
728 wxString::Format( _( "It will be placed back in the %s list" ), listName ) );
729
730 menu.Append( ID_EDIT_EXCLUSION_COMMENT,
731 _( "Edit exclusion comment..." ) );
732 }
733 else
734 {
735 menu.Append( ID_ADD_EXCLUSION,
736 _( "Exclude this violation" ),
737 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
738
739 menu.Append( ID_ADD_EXCLUSION_WITH_COMMENT,
740 _( "Exclude with comment..." ),
741 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
742 }
743
744 menu.AppendSeparator();
745
746 wxString inspectERCErrorMenuText = inspectionTool->InspectERCErrorMenuText( rcItem );
747 wxString fixERCErrorMenuText = editTool->FixERCErrorMenuText( rcItem );
748
749 if( !inspectERCErrorMenuText.IsEmpty() || !fixERCErrorMenuText.IsEmpty() )
750 {
751 if( !inspectERCErrorMenuText.IsEmpty() )
752 menu.Append( ID_INSPECT_VIOLATION, inspectERCErrorMenuText );
753
754 if( !fixERCErrorMenuText.IsEmpty() )
755 menu.Append( ID_FIX_VIOLATION, fixERCErrorMenuText );
756
757 menu.AppendSeparator();
758 }
759
760 if( rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_WARNING
761 || rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_ERROR )
762 {
763 // Pin to pin severities edited through pin conflict map
764 }
765 else if( settings.GetSeverity( rcItem->GetErrorCode() ) == RPT_SEVERITY_WARNING )
766 {
767 menu.Append( ID_SET_SEVERITY_TO_ERROR,
768 wxString::Format( _( "Change severity to Error for all '%s' violations" ),
769 rcItem->GetErrorText( true ) ),
770 _( "Violation severities can also be edited in the Schematic Setup... dialog" ) );
771 }
772 else
773 {
774 menu.Append( ID_SET_SEVERITY_TO_WARNING,
775 wxString::Format( _( "Change severity to Warning for all '%s' violations" ),
776 rcItem->GetErrorText( true ) ),
777 _( "Violation severities can also be edited in the Schematic Setup... "
778 "dialog" ) );
779 }
780
781 menu.Append( ID_SET_SEVERITY_TO_IGNORE,
782 wxString::Format( _( "Ignore all '%s' violations" ), rcItem->GetErrorText( true ) ),
783 _( "Violations will not be checked or reported" ) );
784
785 menu.AppendSeparator();
786
787 if( rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_WARNING
788 || rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_ERROR )
789 {
790 menu.Append( ID_EDIT_PIN_CONFLICT_MAP,
791 _( "Edit pin-to-pin conflict map..." ),
792 _( "Open the Schematic Setup... dialog" ) );
793 }
794 else
795 {
796 menu.Append( ID_EDIT_SEVERITIES,
797 _( "Edit violation severities..." ),
798 _( "Open the Schematic Setup... dialog" ) );
799 }
800
801 if( rcItem->GetErrorCode() == ERCE_ENDPOINT_OFF_GRID )
802 {
803 menu.Append( ID_EDIT_CONNECTION_GRID,
804 _( "Edit connection grid spacing..." ),
805 _( "Open the Schematic Setup... dialog" ) );
806 }
807
808 bool modified = false;
809 int command = GetPopupMenuSelectionFromUser( menu );
810
811 switch( command )
812 {
813 case ID_EDIT_EXCLUSION_COMMENT:
814 if( SCH_MARKER* marker = dynamic_cast<SCH_MARKER*>( node->m_RcItem->GetParent() ) )
815 {
816 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), marker->GetComment(), true );
817
818 if( dlg.ShowModal() == wxID_CANCEL )
819 break;
820
821 marker->SetExcluded( true, dlg.GetValue() );
822
823 // Update view
824 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
825 modified = true;
826 }
827
828 break;
829
830 case ID_REMOVE_EXCLUSION:
831 if( SCH_MARKER* marker = dynamic_cast<SCH_MARKER*>( node->m_RcItem->GetParent() ) )
832 {
833 marker->SetExcluded( false );
834 m_parent->GetCanvas()->GetView()->Update( marker );
835
836 // Update view
837 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
838 modified = true;
839 }
840
841 break;
842
843 case ID_ADD_EXCLUSION:
844 case ID_ADD_EXCLUSION_WITH_COMMENT:
845 if( SCH_MARKER* marker = dynamic_cast<SCH_MARKER*>( node->m_RcItem->GetParent() ) )
846 {
847 wxString comment;
848
849 if( command == ID_ADD_EXCLUSION_WITH_COMMENT )
850 {
851 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), wxEmptyString, true );
852
853 if( dlg.ShowModal() == wxID_CANCEL )
854 break;
855
856 comment = dlg.GetValue();
857 }
858
859 marker->SetExcluded( true, comment );
860
861 m_parent->GetCanvas()->GetView()->Update( marker );
862
863 // Update view
865 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
866 else
867 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
868
869 modified = true;
870 }
871
872 break;
873
874 case ID_INSPECT_VIOLATION:
875 inspectionTool->InspectERCError( node->m_RcItem );
876 break;
877
878 case ID_FIX_VIOLATION:
879 editTool->FixERCError( node->m_RcItem );
880 break;
881
882 case ID_SET_SEVERITY_TO_ERROR:
883 settings.SetSeverity( rcItem->GetErrorCode(), RPT_SEVERITY_ERROR );
884
885 for( SCH_ITEM* item : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) )
886 {
887 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
888
889 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
890 m_parent->GetCanvas()->GetView()->Update( marker );
891 }
892
893 // Rebuild model and view
894 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
895 modified = true;
896 break;
897
898 case ID_SET_SEVERITY_TO_WARNING:
899 settings.SetSeverity( rcItem->GetErrorCode(), RPT_SEVERITY_WARNING );
900
901 for( SCH_ITEM* item : m_parent->GetScreen()->Items().OfType( SCH_MARKER_T ) )
902 {
903 SCH_MARKER* marker = static_cast<SCH_MARKER*>( item );
904
905 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
906 m_parent->GetCanvas()->GetView()->Update( marker );
907 }
908
909 // Rebuild model and view
910 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
911 modified = true;
912 break;
913
914 case ID_SET_SEVERITY_TO_IGNORE:
915 {
916 settings.SetSeverity( rcItem->GetErrorCode(), RPT_SEVERITY_IGNORE );
917
918 if( rcItem->GetErrorCode() == ERCE_PIN_TO_PIN_ERROR )
920
921 wxListItem listItem;
922 listItem.SetId( m_ignoredList->GetItemCount() );
923 listItem.SetText( wxT( " • " ) + rcItem->GetErrorText( true ) );
924 listItem.SetData( rcItem->GetErrorCode() );
925
926 m_ignoredList->InsertItem( listItem );
927
928 // Clear the selection before deleting markers. It may be some selected ERC markers.
929 // Deleting a selected marker without deselecting it first generates a crash
930 m_parent->GetToolManager()->RunAction( ACTIONS::selectionClear );
931
932 SCH_SCREENS ScreenList( m_parent->Schematic().Root() );
933 ScreenList.DeleteMarkers( MARKER_BASE::MARKER_ERC, rcItem->GetErrorCode() );
934
935 // Rebuild model and view
936 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markerProvider, getSeverities() );
937 modified = true;
938 break;
939 }
940
941 case ID_EDIT_PIN_CONFLICT_MAP:
942 m_parent->ShowSchematicSetupDialog( _( "Pin Conflicts Map" ) );
943 break;
944
945 case ID_EDIT_SEVERITIES:
946 m_parent->ShowSchematicSetupDialog( _( "Violation Severity" ) );
947 break;
948
949 case ID_EDIT_CONNECTION_GRID:
950 m_parent->ShowSchematicSetupDialog( _( "Formatting" ) );
951 break;
952 }
953
954 if( modified )
955 {
958 m_parent->OnModify();
959 }
960}
961
962
963void DIALOG_ERC::OnIgnoredItemRClick( wxListEvent& event )
964{
965 ERC_SETTINGS& settings = m_parent->Schematic().ErcSettings();
966 int errorCode = (int) event.m_item.GetData();
967 wxMenu menu;
968
969 menu.Append( RPT_SEVERITY_ERROR, _( "Error" ), wxEmptyString, wxITEM_CHECK );
970 menu.Append( RPT_SEVERITY_WARNING, _( "Warning" ), wxEmptyString, wxITEM_CHECK );
971 menu.Append( RPT_SEVERITY_IGNORE, _( "Ignore" ), wxEmptyString, wxITEM_CHECK );
972
973 menu.Check( settings.GetSeverity( errorCode ), true );
974
975 int severity = GetPopupMenuSelectionFromUser( menu );
976
977 if( severity > 0 )
978 {
979 if( settings.GetSeverity( errorCode ) != severity )
980 {
981 settings.SetSeverity( errorCode, (SEVERITY) severity );
982
985 m_parent->OnModify();
986 }
987 }
988}
989
990
992{
993 if( m_notebook->IsShown() )
994 {
995 if( m_notebook->GetSelection() != 0 )
996 m_notebook->SetSelection( 0 );
997
998 m_markerTreeModel->PrevMarker();
999 }
1000}
1001
1002
1004{
1005 if( m_notebook->IsShown() )
1006 {
1007 if( m_notebook->GetSelection() != 0 )
1008 m_notebook->SetSelection( 0 );
1009
1010 m_markerTreeModel->NextMarker();
1011 }
1012}
1013
1014
1016{
1017 if( m_notebook->IsShown() )
1018 {
1019 m_notebook->SetSelection( 0 );
1020 m_markerTreeModel->SelectMarker( aMarker );
1021
1022 // wxWidgets on some platforms fails to correctly ensure that a selected item is
1023 // visible, so we have to do it in a separate idle event.
1024 m_centerMarkerOnIdle = aMarker;
1025 Bind( wxEVT_IDLE, &DIALOG_ERC::centerMarkerIdleHandler, this );
1026 }
1027}
1028
1029
1030void DIALOG_ERC::centerMarkerIdleHandler( wxIdleEvent& aEvent )
1031{
1032 if( m_markerTreeModel->GetView()->IsFrozen() )
1033 return;
1034
1035 m_markerTreeModel->CenterMarker( m_centerMarkerOnIdle );
1036 m_centerMarkerOnIdle = nullptr;
1037 Unbind( wxEVT_IDLE, &DIALOG_ERC::centerMarkerIdleHandler, this );
1038}
1039
1040
1042{
1043 SCH_MARKER* marker = aMarker;
1044
1045 if( marker != nullptr )
1046 m_markerTreeModel->SelectMarker( marker );
1047
1048 if( m_notebook->GetSelection() != 0 )
1049 return;
1050
1051 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
1052
1053 if( node && node->m_RcItem )
1054 marker = dynamic_cast<SCH_MARKER*>( node->m_RcItem->GetParent() );
1055
1056 if( node && marker && !marker->IsExcluded() )
1057 {
1058 marker->SetExcluded( true );
1059 m_parent->GetCanvas()->GetView()->Update( marker );
1060
1061 // Update view
1063 m_markerTreeModel->ValueChanged( node );
1064 else
1065 m_markerTreeModel->DeleteCurrentItem( false );
1066
1069 m_parent->OnModify();
1070 }
1071}
1072
1073
1074void DIALOG_ERC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
1075{
1076 m_parent->ShowSchematicSetupDialog( _( "Violation Severity" ) );
1077}
1078
1079
1080void DIALOG_ERC::OnSeverity( wxCommandEvent& aEvent )
1081{
1082 if( aEvent.GetEventObject() == m_showAll )
1083 {
1084 m_showErrors->SetValue( true );
1085 m_showWarnings->SetValue( aEvent.IsChecked() );
1086 m_showExclusions->SetValue( aEvent.IsChecked() );
1087 }
1088
1089 UpdateData();
1090}
1091
1092
1093void DIALOG_ERC::deleteAllMarkers( bool aIncludeExclusions )
1094{
1095 // Clear current selection list to avoid selection of deleted items
1096 // Freeze to avoid repainting the dialog, which can cause a RePaint()
1097 // of the screen as well
1098 Freeze();
1099
1100 m_parent->GetToolManager()->RunAction( ACTIONS::selectionClear );
1101
1102 m_markerTreeModel->DeleteItems( false, aIncludeExclusions, false );
1103
1104 SCH_SCREENS screens( m_parent->Schematic().Root() );
1105 screens.DeleteAllMarkers( MARKER_BASE::MARKER_ERC, aIncludeExclusions );
1106
1107 Thaw();
1108}
1109
1110
1111void DIALOG_ERC::OnSaveReport( wxCommandEvent& aEvent )
1112{
1113 wxFileName fn( wxS( "ERC." ) + wxString( FILEEXT::ReportFileExtension ) );
1114
1115 wxFileDialog dlg( this, _( "Save Report File" ), Prj().GetProjectPath(), fn.GetFullName(),
1117 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1118
1119 if( dlg.ShowModal() != wxID_OK )
1120 return;
1121
1122 fn = dlg.GetPath();
1123
1124 if( fn.GetExt().IsEmpty() )
1125 fn.SetExt( FILEEXT::ReportFileExtension );
1126
1127 if( !fn.IsAbsolute() )
1128 {
1129 wxString prj_path = Prj().GetProjectPath();
1130 fn.MakeAbsolute( prj_path );
1131 }
1132
1133 ERC_REPORT reportWriter( &m_parent->Schematic(), m_parent->GetUserUnits(), m_markerProvider );
1134
1135 bool success = false;
1136 if( fn.GetExt() == FILEEXT::JsonFileExtension )
1137 success = reportWriter.WriteJsonReport( fn.GetFullPath() );
1138 else
1139 success = reportWriter.WriteTextReport( fn.GetFullPath() );
1140
1141 if( success )
1142 m_messages->Report( wxString::Format( _( "Report file '%s' created." ), fn.GetFullPath() ) );
1143 else
1144 DisplayErrorMessage( this, wxString::Format( _( "Failed to create file '%s'." ), fn.GetFullPath() ) );
1145}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
static TOOL_ACTION excludeMarker
Definition actions.h:129
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
wxButton * m_saveReport
wxButton * m_sdbSizer1Cancel
wxSimplebook * m_runningResultsBook
wxCheckBox * m_showExclusions
NUMBER_BADGE * m_errorsBadge
WX_INFOBAR * m_infoBar
STD_BITMAP_BUTTON * m_bMenu
wxCheckBox * m_showErrors
wxNotebook * m_notebook
wxCheckBox * m_showAll
NUMBER_BADGE * m_exclusionsBadge
wxListCtrl * m_ignoredList
WX_HTML_REPORT_BOX * m_messages
wxDataViewCtrl * m_markerDataView
wxButton * m_deleteAllMarkers
NUMBER_BADGE * m_warningsBadge
wxButton * m_deleteOneMarker
wxCheckBox * m_showWarnings
wxButton * m_sdbSizer1OK
DIALOG_ERC_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Electrical Rules Checker"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnMenu(wxCommandEvent &aEvent) override
void ExcludeMarker(SCH_MARKER *aMarker=nullptr)
Exclude aMarker from the ERC list.
void OnIgnoredItemRClick(wxListEvent &aEvent) override
const SCH_MARKER * m_centerMarkerOnIdle
Definition dialog_erc.h:119
void OnERCItemDClick(wxDataViewEvent &aEvent) override
std::shared_ptr< RC_ITEMS_PROVIDER > m_markerProvider
Definition dialog_erc.h:113
bool TransferDataToWindow() override
bool m_ercRun
Definition dialog_erc.h:117
bool m_crossprobe
Definition dialog_erc.h:121
DIALOG_ERC(SCH_EDIT_FRAME *parent)
bool m_scroll_on_crossprobe
Definition dialog_erc.h:122
void testErc()
void SelectMarker(const SCH_MARKER *aMarker)
bool updateUI() override
SCH_EDIT_FRAME * m_parent
Definition dialog_erc.h:107
wxString m_violationsTitleTemplate
Definition dialog_erc.h:110
void OnERCItemRClick(wxDataViewEvent &aEvent) override
void centerMarkerIdleHandler(wxIdleEvent &aEvent)
void OnDeleteAllClick(wxCommandEvent &event) override
void OnLinkClicked(wxHtmlLinkEvent &event) override
void OnCharHook(wxKeyEvent &aEvt) override
void OnRunERCClick(wxCommandEvent &event) override
void deleteAllMarkers(bool aIncludeExclusions)
wxString m_ignoredTitleTemplate
Definition dialog_erc.h:111
void OnDeleteOneClick(wxCommandEvent &event) override
void UpdateData()
void OnSaveReport(wxCommandEvent &aEvent) override
int getSeverities()
void PrevMarker()
void NextMarker()
SCHEMATIC * m_currentSchematic
Definition dialog_erc.h:108
void OnEditViolationSeverities(wxHyperlinkEvent &aEvent) override
void UpdateAnnotationWarning()
void redrawDrawPanel()
void Report(const wxString &aMessage) override
Display aMessage in the progress bar dialog.
void OnERCItemSelected(wxDataViewEvent &aEvent) override
RC_TREE_MODEL * m_markerTreeModel
Definition dialog_erc.h:114
void updateDisplayedCounts()
void OnSeverity(wxCommandEvent &aEvent) override
bool m_running
Definition dialog_erc.h:116
bool m_showAllErrors
Definition dialog_erc.h:123
void OnCloseErcDialog(wxCloseEvent &event) override
void OnCancelClick(wxCommandEvent &event) override
bool Show(bool show) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
bool IsQuasiModal() const
Definition dialog_shim.h:93
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual void OnCharHook(wxKeyEvent &aEvt)
int ShowModal() override
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition erc_item.cpp:307
static std::vector< std::reference_wrapper< RC_ITEM > > GetItemsWithSeverities()
Definition erc_item.h:76
bool WriteJsonReport(const wxString &aFullFileName)
Writes a JSON formatted ERC Report to the given file path in the c-locale.
bool WriteTextReport(const wxString &aFullFileName)
Writes the text report also available via GetTextReport directly to a given file path.
Container for ERC settings.
SEVERITY GetSeverity(int aErrorCode) const
void SetSeverity(int aErrorCode, SEVERITY aSeverity)
void RunTests(DS_PROXY_VIEW_ITEM *aDrawingSheet, SCH_EDIT_FRAME *aEditFrame, KIFACE *aCvPcb, PROJECT *aProject, PROGRESS_REPORTER *aProgressReporter)
Definition erc.cpp:1907
A specialisation of the RC_TREE_MODEL class to enable ERC errors / warnings to be resolved in a speci...
Definition erc_item.h:38
Definition kiid.h:49
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
@ FACE_CVPCB
Definition kiway.h:303
bool IsExcluded() const
Definition marker_base.h:93
std::shared_ptr< RC_ITEM > GetRCItem() const
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:94
virtual void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
virtual void SetCurrentProgress(double aProgress) override
Set the progress value to aProgress (0..1).
virtual void AdvancePhase()=0
Use the next available virtual zone of the dialog progress bar.
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:167
int GetErrorCode() const
Definition rc_item.h:158
MARKER_BASE * GetParent() const
Definition rc_item.h:133
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition rc_item.h:250
void ValueChanged(RC_TREE_NODE *aNode)
Definition rc_item.cpp:527
void Update(std::shared_ptr< RC_ITEMS_PROVIDER > aProvider, int aSeverities)
Definition rc_item.cpp:370
void DeleteCurrentItem(bool aDeep)
Definition rc_item.cpp:576
static KIID ToUUID(wxDataViewItem aItem)
Definition rc_item.cpp:226
std::shared_ptr< RC_ITEM > m_RcItem
Definition rc_item.h:235
NODE_TYPE m_Type
Definition rc_item.h:234
Holds all the data relating to one schematic.
Definition schematic.h:88
void RecordERCExclusions()
Scan existing markers and record data from any that are Excluded.
SCH_SHEET & Root() const
Definition schematic.h:132
ERC_SETTINGS & ErcSettings() const
static TOOL_ACTION changeSheet
Schematic editor (Eeschema) main window.
SCHEMATIC & Schematic() const
void InspectERCError(const std::shared_ptr< RC_ITEM > &aERCItem)
wxString InspectERCErrorMenuText(const std::shared_ptr< RC_ITEM > &aERCItem)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
wxString GetClass() const override
Return the class name.
Definition sch_item.h:177
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
SCH_SYMBOL * GetSymbol() const
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:747
void DeleteMarkers(enum MARKER_BASE::MARKER_T aMarkerTyp, int aErrorCode, bool aIncludeExclusions=true)
Delete all markers of a particular type and error code.
void DeleteAllMarkers(enum MARKER_BASE::MARKER_T aMarkerType, bool aIncludeExclusions)
Delete all electronic rules check markers of aMarkerType from all the screens in the list.
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool empty() const
Forwarded method from std::vector.
SCH_SCREEN * LastScreen()
VECTOR2I GetPosition() const override
Definition sch_symbol.h:854
Master controller class:
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
This file is part of the common library.
static bool g_lastERCRun
wxDEFINE_EVENT(EDA_EVT_CLOSE_ERC_DIALOG, wxCommandEvent)
static std::vector< std::pair< wxString, int > > g_lastERCIgnored
static SCHEMATIC * g_lastERCSchematic
#define DIALOG_ERC_WINDOW_NAME
Definition dialog_erc.h:39
#define _(s)
static int DEFAULT_SINGLE_COL_WIDTH
ERCE_T
ERC error codes.
@ ERCE_ENDPOINT_OFF_GRID
Pin or wire-end off grid.
@ ERCE_PIN_TO_PIN_WARNING
@ ERCE_PIN_TO_PIN_ERROR
static const std::string ReportFileExtension
static const std::string JsonFileExtension
static wxString JsonFileWildcard()
static wxString ReportFileWildcard()
void SetFloatLevel(wxWindow *aWindow)
Intended to set the floating window level in macOS on a window.
Definition wxgtk/ui.cpp:407
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition wxgtk/ui.cpp:124
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_EXCLUSION
@ RPT_SEVERITY_IGNORE
@ RPT_SEVERITY_INFO
T * GetAppSettings(const char *aFilename)
std::vector< FAB_LAYER_COLOR > dummy
@ MD_ALT
Definition tool_event.h:145
@ MD_CTRL
Definition tool_event.h:144
@ MD_SHIFT
Definition tool_event.h:143
@ SCH_MARKER_T
Definition typeinfo.h:162
Definition of file extensions used in Kicad.