KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_drc.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) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2009-2016 Dick Hollenbeck, [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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <confirm.h>
23#include <core/throttle.h>
24#include <dialog_drc.h>
26#include <kiface_base.h>
27#include <macros.h>
28#include <pad.h>
29#include <drc/drc_item.h>
30#include <drc/drc_report.h>
34#include <pcb_edit_frame.h>
35#include <tool/tool_manager.h>
36#include <tools/pcb_actions.h>
38#include <pcb_marker.h>
39#include <pcb_track.h>
40#include <pgm_base.h>
41#include <wx/app.h>
42#include <wx/filedlg.h>
43#include <wx/msgdlg.h>
44#include <wx/statusbr.h>
45#include <wx/wupdlock.h>
47#include <widgets/ui_common.h>
52#include <view/view_controls.h>
55#include <tools/drc_tool.h>
58#include <kiplatform/ui.h>
59
60// wxWidgets spends *far* too long calcuating 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
65static BOARD* g_lastDRCBoard = nullptr;
66static bool g_lastDRCRun = false;
67static bool g_lastFootprintTestsRun = false;
68
69static std::vector<std::pair<wxString, int>> g_lastIgnored;
70
71
72DIALOG_DRC::DIALOG_DRC( PCB_EDIT_FRAME* aEditorFrame, wxWindow* aParent ) :
73 DIALOG_DRC_BASE( aParent ),
75 m_running( false ),
76 m_drcRun( false ),
77 m_footprintTestsRun( false ),
79 m_crossprobe( true ),
81 m_markersTreeModel( nullptr ),
82 m_unconnectedTreeModel( nullptr ),
83 m_fpWarningsTreeModel( nullptr ),
84 m_updateThrottle( std::chrono::milliseconds( 100 ) ),
85 m_yieldThrottle( std::chrono::milliseconds( 2000 ) ),
86 m_drcStatusBar( nullptr ),
88{
89 SetName( DIALOG_DRC_WINDOW_NAME ); // Set a window name to be able to find it
91
92 m_frame = aEditorFrame;
93 m_currentBoard = m_frame->GetBoard();
94
95 m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
96
97 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
98 {
99 m_report_all_track_errors = cfg->m_DRCDialog.report_all_track_errors;
100 m_crossprobe = cfg->m_DRCDialog.crossprobe;
101 m_scroll_on_crossprobe = cfg->m_DRCDialog.scroll_on_crossprobe;
102 }
103
104 m_messages->SetImmediateMode();
105
106 m_markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
109
110 m_ratsnestProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
112
113 m_fpWarningsProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
115
116 auto installModel = [this]( RC_TREE_MODEL*& aModel, wxDataViewCtrl* aCtrl )
117 {
118 aModel = new RC_TREE_MODEL( m_frame, aCtrl );
119 aModel->EnableHyperlinks( true );
120 aCtrl->AssociateModel( aModel );
121 installLinkHandlers( aCtrl );
122 };
123
124 installModel( m_markersTreeModel, m_markerDataView );
127
128 // Prevent RTL locales from mirroring the text in the data views
129 m_markerDataView->SetLayoutDirection( wxLayout_LeftToRight );
130 m_unconnectedDataView->SetLayoutDirection( wxLayout_LeftToRight );
131 m_footprintsDataView->SetLayoutDirection( wxLayout_LeftToRight );
132 m_ignoredList->SetLayoutDirection( wxLayout_LeftToRight );
133
134 m_ignoredList->InsertColumn( 0, wxEmptyString, wxLIST_FORMAT_LEFT, DEFAULT_SINGLE_COL_WIDTH );
135
137 {
140
141 for( const auto& [ str, code ] : g_lastIgnored )
142 {
143 wxListItem listItem;
144 listItem.SetId( m_ignoredList->GetItemCount() );
145 listItem.SetText( str );
146 listItem.SetData( code );
147
148 m_ignoredList->InsertItem( listItem );
149 }
150 }
151
152 m_Notebook->SetSelection( 0 );
153
154 if( Kiface().IsSingle() )
155 m_cbTestFootprints->Hide();
156
157 SetupStandardButtons( { { wxID_OK, _( "Run DRC" ) },
158 { wxID_CANCEL, _( "Close" ) } } );
159
160 m_markersTitleTemplate = m_Notebook->GetPageText( 0 );
161 m_unconnectedTitleTemplate = m_Notebook->GetPageText( 1 );
162 m_footprintsTitleTemplate = m_Notebook->GetPageText( 2 );
163 m_ignoredTitleTemplate = m_Notebook->GetPageText( 3 );
164
165 // DPI fix
166 bSizerViolationsBox->SetMinSize( FromDIP( bSizerViolationsBox->GetMinSize() ) );
167
168 m_drcStatusBar = new wxStatusBar( this, wxID_ANY, wxSTB_DEFAULT_STYLE );
169 m_drcStatusBar->SetFieldsCount( 2 );
170
171 int statusBarWidths[2] = { FromDIP( 12 ), -1 };
172 m_drcStatusBar->SetStatusWidths( 2, statusBarWidths );
173
174 if( wxSizer* mainSizer = GetSizer() )
175 mainSizer->Add( m_drcStatusBar, 0, wxEXPAND );
176
177 Layout(); // adding the units above expanded Clearance text, now resize.
178
179 SetFocus();
180
182}
183
184
186{
187 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
188 {
189 cfg->m_DRCDialog.report_all_track_errors = m_report_all_track_errors;
190 cfg->m_DRCDialog.crossprobe = m_crossprobe;
191 cfg->m_DRCDialog.scroll_on_crossprobe = m_scroll_on_crossprobe;
192 }
193
194 m_frame->ClearFocus();
195
199
200 g_lastIgnored.clear();
201
202 for( int ii = 0; ii < m_ignoredList->GetItemCount(); ++ii )
203 g_lastIgnored.push_back( { m_ignoredList->GetItemText( ii ), m_ignoredList->GetItemData( ii ) } );
204
205 m_markersTreeModel->DecRef();
206 m_unconnectedTreeModel->DecRef();
207 m_fpWarningsTreeModel->DecRef();
208}
209
210
211void DIALOG_DRC::installLinkHandlers( wxDataViewCtrl* aCtrl )
212{
213 aCtrl->Bind( wxEVT_MOTION, &DIALOG_DRC::onDataViewMotion, this );
214 aCtrl->Bind( wxEVT_LEFT_UP, &DIALOG_DRC::onDataViewLeftUp, this );
215}
216
217
218void DIALOG_DRC::onDataViewMotion( wxMouseEvent& aEvent )
219{
220 aEvent.Skip();
221
222 wxDataViewCtrl* ctrl = dynamic_cast<wxDataViewCtrl*>( aEvent.GetEventObject() );
223
224 if( !ctrl )
225 return;
226
227 bool overLink = hitTestLink( ctrl, aEvent.GetPosition(), nullptr );
228 ctrl->SetCursor( overLink ? wxCursor( wxCURSOR_HAND ) : wxNullCursor );
229}
230
231
232void DIALOG_DRC::onDataViewLeftUp( wxMouseEvent& aEvent )
233{
234 aEvent.Skip();
235
236 wxDataViewCtrl* ctrl = dynamic_cast<wxDataViewCtrl*>( aEvent.GetEventObject() );
237 wxString href;
238
239 if( ctrl && hitTestLink( ctrl, aEvent.GetPosition(), &href ) )
240 wxLaunchDefaultBrowser( href );
241}
242
243
244bool DIALOG_DRC::hitTestLink( wxDataViewCtrl* aCtrl, const wxPoint& aPoint, wxString* aHref )
245{
246 wxDataViewModel* model = aCtrl->GetModel();
247
248 if( !model )
249 return false;
250
251 wxDataViewItem item;
252 wxDataViewColumn* col = nullptr;
253 aCtrl->HitTest( aPoint, item, col );
254
255 if( !item.IsOk() || !col )
256 return false;
257
258 auto* hl = dynamic_cast<HYPERLINK_DV_RENDERER*>( col->GetRenderer() );
259
260 if( !hl )
261 return false;
262
263 wxVariant value;
264 model->GetValue( value, item, col->GetModelColumn() );
265
266 wxRect cell = aCtrl->GetItemRect( item, col );
267
268 return hl->HitTestRunsForCell( value.GetString(), cell, aPoint, aHref );
269}
270
271
273{
274 UpdateData();
275 return true;
276}
277
278
279void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
280{
281 if( m_currentBoard != m_frame->GetBoard() )
282 {
283 // If m_currentBoard is not the current board, (for instance because a new board was loaded),
284 // close the dialog, because many pointers are now invalid in lists
285 SetReturnCode( wxID_CANCEL );
286 Close();
287
288 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
289 drcTool->DestroyDRCDialog();
290 }
291}
292
293
294// PROGRESS_REPORTER calls
295
297{
298 if( m_maxProgress != 0 )
299 {
300 double cur = std::clamp( (double) m_progress.load() / m_maxProgress, 0.0, 1.0 );
301
302 int newValue = KiROUND( cur * 1000.0 );
303 m_gauge->SetValue( newValue );
304 }
305
307 {
308 int elapsed = static_cast<int>(
309 std::chrono::duration_cast<std::chrono::seconds>( std::chrono::steady_clock::now() - m_drcStartTime )
310 .count() );
311
312 if( elapsed != m_lastTickSeconds )
313 {
314 m_lastTickSeconds = elapsed;
315
316 wxString tick;
317
318 if( elapsed >= 60 )
319 tick = wxString::Format( wxT( "%1$d min %2$d s" ), elapsed / 60, elapsed % 60 );
320 else
321 tick = wxString::Format( wxT( "%d s" ), elapsed );
322
323 m_drcStatusBar->SetStatusText( wxString::Format( _( "Elapsed: %s" ), tick ), 1 );
324 }
325 }
326
327 // Repaint the dialog at ~10Hz using Update() which processes only pending expose/
328 // draw events without entering the full platform event loop.
329 if( m_updateThrottle.Ready() )
330 Update();
331
332 // Yield to the event loop infrequently so the cancel button remains functional.
333 // On some Linux systems with glycin-enabled gdk-pixbuf (2.44+), entering the GTK event
334 // loop triggers heavyweight sandbox process spawning that can add hundreds of milliseconds
335 // per call, so we keep this interval long.
336 if( m_yieldThrottle.Ready() )
337 Pgm().App().SafeYieldFor( this, wxEVT_CATEGORY_NATIVE_EVENTS );
338
339 return !m_cancelled;
340}
341
342
343void DIALOG_DRC::AdvancePhase( const wxString& aMessage )
344{
346 SetCurrentProgress( 0.0 );
347
348 m_messages->Report( aMessage );
349}
350
351
353{
354 int severities = 0;
355
356 if( m_showErrors->GetValue() )
357 severities |= RPT_SEVERITY_ERROR;
358
359 if( m_showWarnings->GetValue() )
360 severities |= RPT_SEVERITY_WARNING;
361
362 if( m_showExclusions->GetValue() )
363 severities |= RPT_SEVERITY_EXCLUSION;
364
365 return severities;
366}
367
368
369void DIALOG_DRC::OnMenu( wxCommandEvent& event )
370{
371 // Build a pop menu:
372 wxMenu menu;
373
374 menu.Append( 4205, _( "Report All Errors for Each Track" ),
375 _( "If unchecked, only the first error will be reported for each track" ),
376 wxITEM_CHECK );
377 menu.Check( 4205, m_report_all_track_errors );
378
379 menu.AppendSeparator();
380
381 menu.Append( 4206, _( "Cross-probe Selected Items" ),
382 _( "Highlight corresponding items on canvas when selected in the DRC list" ),
383 wxITEM_CHECK );
384 menu.Check( 4206, m_crossprobe );
385
386 menu.Append( 4207, _( "Center on Cross-probe" ),
387 _( "When cross-probing, scroll the canvas so that the item is visible" ),
388 wxITEM_CHECK );
389 menu.Check( 4207, m_scroll_on_crossprobe );
390
391 // menu_id is the selected submenu id from the popup menu or wxID_NONE
392 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
393
394 if( menu_id == 0 || menu_id == 4205 )
395 {
397 }
398 else if( menu_id == 2 || menu_id == 4206 )
399 {
401 }
402 else if( menu_id == 3 || menu_id == 4207 )
403 {
405 }
406}
407
408
409void DIALOG_DRC::OnErrorLinkClicked( wxHtmlLinkEvent& event )
410{
411 m_frame->ShowBoardSetupDialog( _( "Custom Rules" ), this );
412}
413
414
415void DIALOG_DRC::OnCharHook( wxKeyEvent& aEvt )
416{
417 if( int hotkey = aEvt.GetKeyCode() )
418 {
419 if( aEvt.ControlDown() )
420 hotkey |= MD_CTRL;
421 if( aEvt.ShiftDown() )
422 hotkey |= MD_SHIFT;
423 if( aEvt.AltDown() )
424 hotkey |= MD_ALT;
425
426 if( hotkey == ACTIONS::excludeMarker.GetHotKey() )
427 {
429 return;
430 }
431 }
432
434}
435
436
437void DIALOG_DRC::OnRunDRCClick( wxCommandEvent& aEvent )
438{
439 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
440 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
441 ZONE_FILLER_TOOL* zoneFillerTool = toolMgr->GetTool<ZONE_FILLER_TOOL>();
442 bool refillZones = m_cbRefillZones->GetValue();
443 bool testFootprints = m_cbTestFootprints->GetValue();
444
445 if( zoneFillerTool->IsBusy() )
446 {
447 wxBell();
448 return;
449 }
450
451 m_footprintTestsRun = false;
452 m_cancelled = false;
453
454 m_frame->GetBoard()->RecordDRCExclusions();
455 deleteAllMarkers( true );
456
457 // This is not the time to have stale or buggy rules. Ensure they're up-to-date
458 // and that they at least parse.
459 try
460 {
461 drcTool->GetDRCEngine()->InitEngine( m_frame->GetBoard()->GetDesignRulesPath() );
462 }
463 catch( PARSE_ERROR& )
464 {
465 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
466 m_DeleteCurrentMarkerButton->Enable( false );
467 m_DeleteAllMarkersButton->Enable( false );
468 m_saveReport->Enable( false );
469
470 m_messages->Clear();
471 m_messages->Report( _( "DRC incomplete: could not compile custom design rules." )
472 + wxS( "&nbsp;&nbsp;" )
473 + wxS( "<a href='$CUSTOM_RULES'>" ) + _( "Show design rules." ) + wxT( "</a>" ) );
474 m_messages->Flush();
475
476 Raise();
477
478 // set float level again, it can be lost due to window events during test run
480 return;
481 }
482
483 std::vector<std::reference_wrapper<RC_ITEM>> violations = DRC_ITEM::GetItemsWithSeverities();
484 m_ignoredList->DeleteAllItems();
485
486 for( std::reference_wrapper<RC_ITEM>& item : violations )
487 {
488 if( bds().GetSeverity( item.get().GetErrorCode() ) == RPT_SEVERITY_IGNORE )
489 {
490 wxListItem listItem;
491 listItem.SetId( m_ignoredList->GetItemCount() );
492 listItem.SetText( wxT( " • " ) + item.get().GetErrorText( true ) );
493 listItem.SetData( item.get().GetErrorCode() );
494
495 m_ignoredList->InsertItem( listItem );
496 }
497 }
498
499 m_ignoredList->SetColumnWidth( 0, m_ignoredList->GetParent()->GetClientSize().x - 20 );
500
501 Raise();
502
503 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
504 m_messages->Clear();
505 Update(); // Repaint only, don't enter the full event loop
506
507 m_running = true;
508 m_sdbSizerCancel->SetLabel( _( "Cancel" ) );
509 m_sdbSizerOK->Enable( false );
510 m_DeleteCurrentMarkerButton->Enable( false );
511 m_DeleteAllMarkersButton->Enable( false );
512 m_saveReport->Enable( false );
513
514 m_drcStartTime = std::chrono::steady_clock::now();
516
517 if( m_drcStatusBar )
518 m_drcStatusBar->SetStatusText( _( "Elapsed: 0 s" ), 1 );
519
520 {
521 wxBusyCursor dummy;
522 drcTool->RunTests( this, refillZones, m_report_all_track_errors, testFootprints );
523 }
524
525 double elapsedMs =
526 std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - m_drcStartTime ).count();
527
528 auto formatElapsed =
529 [&]() -> wxString
530 {
531 int totalSeconds = KiROUND( elapsedMs / 1000.0 );
532
533 if( totalSeconds >= 60 )
534 return wxString::Format( _( "%1$d min %2$d s" ), totalSeconds / 60, totalSeconds % 60 );
535
536 return wxString::Format( _( "%.2f s" ), elapsedMs / 1000.0 );
537 };
538
539 if( m_cancelled )
540 {
541 m_messages->Report( _( "-------- DRC canceled by user.<br><br>" ) );
542
543 if( m_drcStatusBar )
544 m_drcStatusBar->SetStatusText( wxString::Format( _( "Canceled after %s" ), formatElapsed() ), 1 );
545 }
546 else
547 {
548 m_messages->Report( _( "Done.<br><br>" ) );
549
550 if( m_drcStatusBar )
551 m_drcStatusBar->SetStatusText( wxString::Format( _( "Completed in %s" ), formatElapsed() ), 1 );
552 }
553
554 Raise();
555 Update(); // Repaint only, don't enter the full event loop
556
557 m_running = false;
558 m_sdbSizerCancel->SetLabel( _( "Close" ) );
559 m_sdbSizerOK->Enable( true );
560 m_DeleteCurrentMarkerButton->Enable( true );
561 m_DeleteAllMarkersButton->Enable( true );
562 m_saveReport->Enable( true );
563
564 if( !m_cancelled )
565 {
566 m_sdbSizerCancel->SetDefault();
567 // wxWidgets has a tendency to keep both buttons highlighted without the following:
568 m_sdbSizerOK->Enable( false );
569
570 wxMilliSleep( 500 );
571 m_runningResultsBook->ChangeSelection( 1 );
573
574 // now re-enable m_sdbSizerOK button
575 m_sdbSizerOK->Enable( true );
576 }
577
578 // set float level again, it can be lost due to window events during test run
581}
582
583
585{
586 int severities = getSeverities();
587
588 m_markersTreeModel->Update( m_markersProvider, severities );
589 m_unconnectedTreeModel->Update( m_ratsnestProvider, severities );
590 m_fpWarningsTreeModel->Update( m_fpWarningsProvider, severities );
591
593}
594
595
596void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
597{
598 if( !m_crossprobe )
599 {
600 aEvent.Skip();
601 return;
602 }
603
604 BOARD* board = m_frame->GetBoard();
605 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
606
607 auto getActiveLayers =
608 []( BOARD_ITEM* aItem ) -> LSET
609 {
610 if( aItem->Type() == PCB_PAD_T )
611 {
612 PAD* pad = static_cast<PAD*>( aItem );
613 LSET layers;
614
615 for( int layer : aItem->GetLayerSet() )
616 {
617 if( pad->FlashLayer( layer ) )
618 layers.set( layer );
619 }
620
621 return layers;
622 }
623 else
624 {
625 return aItem->GetLayerSet();
626 }
627 };
628
629 if( !node )
630 {
631 // list is being freed; don't do anything with null ptrs
632
633 aEvent.Skip();
634 return;
635 }
636
637 std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
638
639 if( rc_item->GetErrorCode() == DRCE_UNRESOLVED_VARIABLE
640 && rc_item->GetParent()->GetMarkerType() == MARKER_BASE::MARKER_DRAWING_SHEET )
641 {
642 m_frame->FocusOnLocation( node->m_RcItem->GetParent()->GetPos(), m_scroll_on_crossprobe );
643
644 aEvent.Skip();
645 return;
646 }
647
648 const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
649 BOARD_ITEM* item = board->ResolveItem( itemID, true );
650
651 if( !item )
652 {
653 aEvent.Skip();
654 return;
655 }
656
657 PCB_MARKER* parentMarker = dynamic_cast<PCB_MARKER*>( rc_item->GetParent() );
658 PCB_LAYER_ID principalLayer;
659 LSET violationLayers;
660 BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
661 BOARD_ITEM* b = board->ResolveItem( rc_item->GetAuxItemID(), true );
662 BOARD_ITEM* c = board->ResolveItem( rc_item->GetAuxItem2ID(), true );
663 BOARD_ITEM* d = board->ResolveItem( rc_item->GetAuxItem3ID(), true );
664
665 auto focus = [&]( BOARD_ITEM* aItem )
666 {
667 std::vector<BOARD_ITEM*> items = { aItem };
668
669 if( parentMarker && parentMarker != aItem )
670 items.push_back( parentMarker );
671
672 m_frame->FocusOnItems( items, principalLayer, m_scroll_on_crossprobe );
673 };
674
675 if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
676 {
677 if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
678 && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
679 {
680 principalLayer = B_CrtYd;
681 }
682 else
683 {
684 principalLayer = F_CrtYd;
685 }
686 }
687 else if( rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
688 {
689 principalLayer = Edge_Cuts;
690 }
691 else
692 {
693 principalLayer = UNDEFINED_LAYER;
694
695 // The marker's layer is set by the test provider
696 if( parentMarker )
697 {
698 PCB_LAYER_ID markerLayer = parentMarker->GetLayer();
699
700 if( markerLayer > UNDEFINED_LAYER )
701 principalLayer = markerLayer;
702 }
703
704 // Fall back to intersecting the contributing items layer sets.
705 if( principalLayer <= UNDEFINED_LAYER )
706 {
707 if( a || b || c || d )
708 violationLayers = LSET::AllLayersMask();
709
710 for( BOARD_ITEM* it: { a, b, c, d } )
711 {
712 if( !it )
713 continue;
714
715 LSET layersList = getActiveLayers( it );
716 violationLayers &= layersList;
717
718 if( principalLayer <= UNDEFINED_LAYER && layersList.count() )
719 principalLayer = layersList.Seq().front();
720
721 }
722 }
723 }
724
725 if( violationLayers.count() )
726 principalLayer = violationLayers.Seq().front();
727 else if( principalLayer >= 0 )
728 violationLayers.set( principalLayer );
729
730 WINDOW_THAWER thawer( m_frame );
731
732 if( principalLayer > UNDEFINED_LAYER && ( violationLayers & board->GetVisibleLayers() ).none() )
733 m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
734
735 if( principalLayer > UNDEFINED_LAYER && board->GetVisibleLayers().test( principalLayer ) )
736 m_frame->SetActiveLayer( principalLayer );
737
738 if( rc_item->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
739 {
740 if( !m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest )
741 m_frame->GetToolManager()->RunAction( PCB_ACTIONS::showRatsnest );
742
743 if( item->Type() == PCB_ZONE_T )
744 {
745 focus( item );
746
747 m_frame->GetBoard()->GetConnectivity()->RunOnUnconnectedEdges(
748 [&]( CN_EDGE& edge )
749 {
750 // Connectivity was valid when DRC was run, but this is a modeless dialog
751 // so it might not be now.
752 if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
753 return true;
754
755 if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
756 return true;
757
758 if( edge.GetSourceNode()->Parent() == a
759 && edge.GetTargetNode()->Parent() == b )
760 {
761 VECTOR2I focusPos;
762
763 if( item == a && item == b )
764 {
765 focusPos = ( node->m_Type == RC_TREE_NODE::MAIN_ITEM ) ? edge.GetSourcePos()
766 : edge.GetTargetPos();
767 }
768 else
769 {
770 focusPos = ( item == edge.GetSourceNode()->Parent() ) ? edge.GetSourcePos()
771 : edge.GetTargetPos();
772 }
773
774 m_frame->FocusOnLocation( focusPos, m_scroll_on_crossprobe );
775 m_frame->RefreshCanvas();
776
777 return false;
778 }
779
780 return true;
781 } );
782 }
783 else
784 {
785 focus( item );
786 }
787 }
788 else if( rc_item->GetErrorCode() == DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG )
789 {
790 BOARD_CONNECTED_ITEM* track = dynamic_cast<PCB_TRACK*>( item );
791 std::vector<BOARD_ITEM*> items;
792
793 if( track )
794 {
795 int net = track->GetNetCode();
796
797 wxASSERT( net > 0 ); // Without a net how can it be a diff-pair?
798
799 for( const KIID& id : rc_item->GetIDs() )
800 {
801 auto* candidate = dynamic_cast<BOARD_CONNECTED_ITEM*>( board->ResolveItem( id, true ) );
802
803 if( candidate && candidate->GetNetCode() == net )
804 items.push_back( candidate );
805 }
806 }
807 else
808 {
809 items.push_back( item );
810 }
811
812 if( parentMarker && std::find( items.begin(), items.end(), parentMarker ) == items.end() )
813 {
814 items.push_back( parentMarker );
815 }
816
817 m_frame->FocusOnItems( items, principalLayer, m_scroll_on_crossprobe );
818 }
819 else
820 {
821 focus( item );
822 }
823
824 aEvent.Skip();
825}
826
827
828void DIALOG_DRC::OnDRCItemDClick( wxDataViewEvent& aEvent )
829{
830 if( aEvent.GetItem().IsOk() )
831 {
832 // turn control over to m_frame, hide this DIALOG_DRC window,
833 // no destruction so we can preserve listbox cursor
834 if( !IsModal() )
835 Show( false );
836 }
837
838 // Do not skip aEvent here: this is not useful, and Pcbnew crashes if skipped (at least on MSW)
839}
840
841
842void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
843{
844 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
845 BOARD_INSPECTION_TOOL* inspectionTool = toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
846 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
847 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
848
849 if( !node )
850 return;
851
852 std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
853 DRC_ITEM* drcItem = static_cast<DRC_ITEM*>( rcItem.get() );
854 std::shared_ptr<CONNECTIVITY_DATA> conn = m_currentBoard->GetConnectivity();
855 wxString listName;
856 wxMenu menu;
857
858 switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] )
859 {
860 case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
861 case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
862 default: listName = _( "appropriate" ); break;
863 }
864
865 enum MENU_IDS
866 {
867 ID_EDIT_EXCLUSION_COMMENT = 4467,
868 ID_REMOVE_EXCLUSION,
869 ID_REMOVE_EXCLUSION_ALL,
870 ID_ADD_EXCLUSION,
871 ID_ADD_EXCLUSION_WITH_COMMENT,
872 ID_ADD_EXCLUSION_ALL,
873 ID_INSPECT_VIOLATION,
874 ID_FIX_VIOLATION,
875 ID_SET_SEVERITY_TO_ERROR,
876 ID_SET_SEVERITY_TO_WARNING,
877 ID_SET_SEVERITY_TO_IGNORE,
878 ID_EDIT_SEVERITIES
879 };
880
881 if( rcItem->GetParent()->IsExcluded() )
882 {
883 menu.Append( ID_REMOVE_EXCLUSION,
884 _( "Remove exclusion for this violation" ),
885 wxString::Format( _( "It will be placed back in the %s list" ), listName ) );
886
887 menu.Append( ID_EDIT_EXCLUSION_COMMENT,
888 _( "Edit exclusion comment..." ) );
889
890 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
891 {
892 menu.Append( ID_REMOVE_EXCLUSION_ALL,
893 wxString::Format( _( "Remove all exclusions for violations of rule '%s'" ),
894 drcItem->GetViolatingRule()->m_Name ),
895 wxString::Format( _( "They will be placed back in the %s list" ), listName ) );
896 }
897 }
898 else
899 {
900 menu.Append( ID_ADD_EXCLUSION,
901 _( "Exclude this violation" ),
902 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
903
904 menu.Append( ID_ADD_EXCLUSION_WITH_COMMENT,
905 _( "Exclude with comment..." ),
906 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
907
908 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
909 {
910 menu.Append( ID_ADD_EXCLUSION_ALL,
911 wxString::Format( _( "Exclude all violations of rule '%s'..." ),
912 drcItem->GetViolatingRule()->m_Name ),
913 wxString::Format( _( "They will be excluded from the %s list" ), listName ) );
914 }
915 }
916
917 menu.AppendSeparator();
918
919 wxString inspectDRCErrorMenuText = inspectionTool->InspectDRCErrorMenuText( rcItem );
920 wxString fixDRCErrorMenuText = drcTool->FixDRCErrorMenuText( rcItem );
921
922 if( !inspectDRCErrorMenuText.IsEmpty() || !fixDRCErrorMenuText.IsEmpty() )
923 {
924 if( !inspectDRCErrorMenuText.IsEmpty() )
925 menu.Append( ID_INSPECT_VIOLATION, inspectDRCErrorMenuText );
926
927 if( !fixDRCErrorMenuText.IsEmpty() )
928 menu.Append( ID_FIX_VIOLATION, fixDRCErrorMenuText );
929
930 menu.AppendSeparator();
931 }
932
933 if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
934 {
935 menu.Append( ID_SET_SEVERITY_TO_ERROR,
936 wxString::Format( _( "Change severity to Error for all '%s' violations" ),
937 rcItem->GetErrorText( true ) ),
938 _( "Violation severities can also be edited in Board Setup" ) );
939 }
940 else
941 {
942 menu.Append( ID_SET_SEVERITY_TO_WARNING,
943 wxString::Format( _( "Change severity to Warning for all '%s' violations" ),
944 rcItem->GetErrorText( true ) ),
945 _( "Violation severities can also be edited in Board Setup" ) );
946 }
947
948 menu.Append( ID_SET_SEVERITY_TO_IGNORE,
949 wxString::Format( _( "Ignore all '%s' violations" ), rcItem->GetErrorText( true ) ),
950 _( "Violations will not be checked or reported" ) );
951
952 menu.AppendSeparator();
953
954 menu.Append( ID_EDIT_SEVERITIES,
955 _( "Edit violation severities..." ),
956 _( "Open the Board Setup dialog" ) );
957
958 bool modified = false;
959 int command = GetPopupMenuSelectionFromUser( menu );
960
961 switch( command )
962 {
963 case ID_EDIT_EXCLUSION_COMMENT:
964 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() ) )
965 {
966 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), marker->GetComment(), true );
967
968 if( dlg.ShowModal() == wxID_CANCEL )
969 break;
970
971 marker->SetExcluded( true, dlg.GetValue() );
972
973 wxString serialized = marker->SerializeToString();
974 bds().m_DrcExclusions.insert( serialized );
975 bds().m_DrcExclusionComments[serialized] = dlg.GetValue();
976
977 // Update view
978 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
979 modified = true;
980 }
981
982 break;
983
984 case ID_REMOVE_EXCLUSION:
985 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
986 {
987 marker->SetExcluded( false );
988
989 wxString serialized = marker->SerializeToString();
990 bds().m_DrcExclusions.erase( serialized );
991 bds().m_DrcExclusionComments.erase( serialized );
992
993 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
994 {
995 m_frame->GetBoard()->UpdateRatsnestExclusions();
996 m_frame->GetCanvas()->RedrawRatsnest();
997 }
998 else
999 {
1000 m_frame->GetCanvas()->GetView()->Update( marker );
1001 }
1002
1003 // Update view
1004 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
1005 modified = true;
1006 }
1007
1008 break;
1009
1010 case ID_ADD_EXCLUSION:
1011 case ID_ADD_EXCLUSION_WITH_COMMENT:
1012 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
1013 {
1014 wxString comment;
1015
1016 if( command == ID_ADD_EXCLUSION_WITH_COMMENT )
1017 {
1018 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), wxEmptyString, true );
1019
1020 if( dlg.ShowModal() == wxID_CANCEL )
1021 break;
1022
1023 comment = dlg.GetValue();
1024 }
1025
1026 marker->SetExcluded( true, comment );
1027
1028 wxString serialized = marker->SerializeToString();
1029 bds().m_DrcExclusions.insert( serialized );
1030 bds().m_DrcExclusionComments[serialized] = comment;
1031
1032 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
1033 {
1034 m_frame->GetBoard()->UpdateRatsnestExclusions();
1035 m_frame->GetCanvas()->RedrawRatsnest();
1036 }
1037 else
1038 {
1039 m_frame->GetCanvas()->GetView()->Update( marker );
1040 }
1041
1042 // Update view
1043 if( m_showExclusions->GetValue() )
1044 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
1045 else
1046 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
1047
1048 modified = true;
1049 }
1050
1051 break;
1052
1053 case ID_REMOVE_EXCLUSION_ALL:
1054 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1055 {
1056 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
1057
1058 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
1059 {
1060 marker->SetExcluded( false );
1061
1062 wxString serialized = marker->SerializeToString();
1063 bds().m_DrcExclusions.erase( serialized );
1064 bds().m_DrcExclusionComments.erase( serialized );
1065 }
1066 }
1067
1068 // Rebuild model and view
1069 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1070 modified = true;
1071 break;
1072
1073 case ID_ADD_EXCLUSION_ALL:
1074 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1075 {
1076 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
1077
1078 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
1079 {
1080 marker->SetExcluded( true );
1081
1082 wxString serialized = marker->SerializeToString();
1083 bds().m_DrcExclusions.insert( serialized );
1084 }
1085 }
1086
1087 // Rebuild model and view
1088 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1089 modified = true;
1090 break;
1091
1092 case ID_INSPECT_VIOLATION:
1093 inspectionTool->InspectDRCError( node->m_RcItem );
1094 break;
1095
1096 case ID_FIX_VIOLATION:
1097 drcTool->FixDRCError( node->m_RcItem );
1098 break;
1099
1100 case ID_SET_SEVERITY_TO_ERROR:
1101 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
1102
1103 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1104 {
1105 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1106 m_frame->GetCanvas()->GetView()->Update( marker );
1107 }
1108
1109 // Rebuild model and view
1110 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1111 modified = true;
1112 break;
1113
1114 case ID_SET_SEVERITY_TO_WARNING:
1115 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
1116
1117 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1118 {
1119 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1120 m_frame->GetCanvas()->GetView()->Update( marker );
1121 }
1122
1123 // Rebuild model and view
1124 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1125 modified = true;
1126 break;
1127
1128 case ID_SET_SEVERITY_TO_IGNORE:
1129 {
1130 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
1131
1132 wxListItem listItem;
1133 listItem.SetId( m_ignoredList->GetItemCount() );
1134 listItem.SetText( wxT( " • " ) + rcItem->GetErrorText( true ) );
1135 listItem.SetData( rcItem->GetErrorCode() );
1136
1137 m_ignoredList->InsertItem( listItem );
1138
1139 BOARD* board = m_frame->GetBoard();
1140
1141 std::vector<BOARD_ITEM*> toRemove;
1142
1143 for( PCB_MARKER* marker : board->Markers() )
1144 {
1145 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1146 {
1147 m_frame->GetCanvas()->GetView()->Remove( marker );
1148 toRemove.emplace_back( marker );
1149 }
1150 }
1151
1152 for( BOARD_ITEM* marker : toRemove )
1153 board->Remove( marker, REMOVE_MODE::BULK );
1154
1155 board->FinalizeBulkRemove( toRemove );
1156
1157 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
1158 m_frame->GetCanvas()->RedrawRatsnest();
1159
1160 // Rebuild model and view
1161 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1162 modified = true;
1163 break;
1164 }
1165
1166 case ID_EDIT_SEVERITIES:
1167 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1168 break;
1169 }
1170
1171 if( modified )
1172 {
1174 refreshEditor();
1175 m_frame->OnModify();
1176 }
1177}
1178
1179
1180void DIALOG_DRC::OnIgnoredItemRClick( wxListEvent& event )
1181{
1182 int errorCode = (int) event.m_item.GetData();
1183 wxMenu menu;
1184
1185 menu.Append( RPT_SEVERITY_ERROR, _( "Error" ), wxEmptyString, wxITEM_RADIO );
1186 menu.Append( RPT_SEVERITY_WARNING, _( "Warning" ), wxEmptyString, wxITEM_RADIO );
1187 menu.Append( RPT_SEVERITY_IGNORE, _( "Ignore" ), wxEmptyString, wxITEM_RADIO );
1188
1189 menu.Check( bds().GetSeverity( errorCode ), true );
1190
1191 int severity = GetPopupMenuSelectionFromUser( menu );
1192
1193 if( severity > 0 )
1194 {
1195 if( bds().m_DRCSeverities[ errorCode ] != severity )
1196 {
1197 bds().m_DRCSeverities[ errorCode ] = (SEVERITY) severity;
1198
1200 refreshEditor();
1201 m_frame->OnModify();
1202 }
1203 }
1204}
1205
1206
1207void DIALOG_DRC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
1208{
1209 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1210}
1211
1212
1213void DIALOG_DRC::OnSeverity( wxCommandEvent& aEvent )
1214{
1215 if( aEvent.GetEventObject() == m_showAll )
1216 {
1217 m_showErrors->SetValue( true );
1218 m_showWarnings->SetValue( aEvent.IsChecked() );
1219 m_showExclusions->SetValue( aEvent.IsChecked() );
1220 }
1221
1222 UpdateData();
1223}
1224
1225
1226void DIALOG_DRC::OnSaveReport( wxCommandEvent& aEvent )
1227{
1228 wxFileName fn( "DRC." + FILEEXT::ReportFileExtension );
1229
1230 wxFileDialog dlg( this, _( "Save Report File" ), Prj().GetProjectPath(), fn.GetFullName(),
1232 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1233
1235
1236 if( dlg.ShowModal() != wxID_OK )
1237 return;
1238
1239 fn = dlg.GetPath();
1240
1241 if( fn.GetExt().IsEmpty() )
1242 fn.SetExt( FILEEXT::ReportFileExtension );
1243
1244 if( !fn.IsAbsolute() )
1245 {
1246 wxString prj_path = Prj().GetProjectPath();
1247 fn.MakeAbsolute( prj_path );
1248 }
1249
1250 DRC_REPORT reportWriter( m_frame->GetBoard(), GetUserUnits(), m_markersProvider,
1252
1253 bool success = false;
1254 if( fn.GetExt() == FILEEXT::JsonFileExtension )
1255 success = reportWriter.WriteJsonReport( fn.GetFullPath() );
1256 else
1257 success = reportWriter.WriteTextReport( fn.GetFullPath() );
1258
1259 if( success )
1260 m_messages->Report( wxString::Format( _( "Report file '%s' created<br>" ), fn.GetFullPath() ) );
1261 else
1262 DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ), fn.GetFullPath() ) );
1263}
1264
1265
1266void DIALOG_DRC::OnClose( wxCloseEvent& aEvent )
1267{
1268 wxCommandEvent dummy;
1270}
1271
1272
1273void DIALOG_DRC::OnCancelClick( wxCommandEvent& aEvent )
1274{
1275 if( m_running )
1276 {
1277 m_cancelled = true;
1278 return;
1279 }
1280
1281 m_frame->ClearFocus();
1282
1283 SetReturnCode( wxID_CANCEL );
1284
1285 // The dialog can be modal or not modal.
1286 // Leave the DRC caller destroy (or not) the dialog
1287 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1288 drcTool->DestroyDRCDialog();
1289}
1290
1291
1292void DIALOG_DRC::OnChangingNotebookPage( wxNotebookEvent& aEvent )
1293{
1294 m_markerDataView->UnselectAll();
1295 m_unconnectedDataView->UnselectAll();
1296 m_footprintsDataView->UnselectAll();
1297
1298 aEvent.Skip();
1299}
1300
1301
1303{
1304 WINDOW_THAWER thawer( m_frame );
1305
1306 m_frame->GetCanvas()->Refresh();
1307}
1308
1309
1311{
1312 if( m_Notebook->IsShown() )
1313 {
1314 switch( m_Notebook->GetSelection() )
1315 {
1316 case 0: m_markersTreeModel->PrevMarker(); break;
1317 case 1: m_unconnectedTreeModel->PrevMarker(); break;
1318 case 2: m_fpWarningsTreeModel->PrevMarker(); break;
1319 case 3: break;
1320 }
1321 }
1322}
1323
1324
1326{
1327 if( m_Notebook->IsShown() )
1328 {
1329 switch( m_Notebook->GetSelection() )
1330 {
1331 case 0: m_markersTreeModel->NextMarker(); break;
1332 case 1: m_unconnectedTreeModel->NextMarker(); break;
1333 case 2: m_fpWarningsTreeModel->NextMarker(); break;
1334 case 3: break;
1335 }
1336 }
1337}
1338
1339
1341{
1342 if( m_Notebook->IsShown() )
1343 {
1344 enum MARKER_BASE::MARKER_T markerType = aMarker->GetMarkerType();
1345
1346 if( markerType == MARKER_BASE::MARKER_DRC )
1347 m_Notebook->SetSelection( 0 );
1348 else if( markerType == MARKER_BASE::MARKER_PARITY )
1349 m_Notebook->SetSelection( 2 );
1350
1351 m_markersTreeModel->SelectMarker( aMarker );
1352
1353 CallAfter(
1354 [this, aMarker]
1355 {
1356 m_markersTreeModel->CenterMarker( aMarker );
1357 } );
1358 }
1359}
1360
1361
1363{
1364 if( !m_Notebook->IsShown() || m_Notebook->GetSelection() != 0 )
1365 return;
1366
1367 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
1368
1369 if( node && node->m_RcItem )
1370 {
1371 PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
1372
1373 if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
1374 {
1375 marker->SetExcluded( true );
1376 bds().m_DrcExclusions.insert( marker->SerializeToString() );
1377 m_frame->GetCanvas()->GetView()->Update( marker );
1378
1379 // Update view
1380 if( m_showExclusions->GetValue() )
1381 m_markersTreeModel->ValueChanged( node );
1382 else
1383 m_markersTreeModel->DeleteCurrentItem( false );
1384
1386 refreshEditor();
1387 m_frame->OnModify();
1388 }
1389 }
1390}
1391
1392
1393void DIALOG_DRC::deleteAllMarkers( bool aIncludeExclusions )
1394{
1395 // Clear current selection list to avoid selection of deleted items
1396 Freeze();
1397 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1398
1399 m_markersTreeModel->DeleteItems( false, aIncludeExclusions, false );
1400 m_unconnectedTreeModel->DeleteItems( false, aIncludeExclusions, false );
1401 m_fpWarningsTreeModel->DeleteItems( false, aIncludeExclusions, false );
1402
1403 m_frame->GetBoard()->DeleteMARKERs( true, aIncludeExclusions );
1404 Thaw();
1405}
1406
1407
1408void DIALOG_DRC::OnDeleteOneClick( wxCommandEvent& aEvent )
1409{
1410 if( m_Notebook->GetSelection() == 0 )
1411 {
1412 // Clear the selection. It may be the selected DRC marker.
1413 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1414
1415 m_markersTreeModel->DeleteCurrentItem( true );
1416
1417 // redraw the pcb
1418 refreshEditor();
1419 }
1420 else if( m_Notebook->GetSelection() == 1 )
1421 {
1422 m_unconnectedTreeModel->DeleteCurrentItem( true );
1423 }
1424 else if( m_Notebook->GetSelection() == 2 )
1425 {
1426 m_fpWarningsTreeModel->DeleteCurrentItem( true );
1427 }
1428
1430}
1431
1432
1433void DIALOG_DRC::OnDeleteAllClick( wxCommandEvent& aEvent )
1434{
1435 static bool s_includeExclusions = false;
1436
1437 int numExcluded = 0;
1438
1439 if( m_markersProvider )
1440 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1441
1442 if( m_ratsnestProvider )
1443 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1444
1446 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1447
1448 if( numExcluded > 0 )
1449 {
1450 wxMessageDialog dlg( this, _( "Delete exclusions too?" ), _( "Delete All Markers" ),
1451 wxYES_NO | wxCANCEL | wxCENTER | wxICON_QUESTION );
1452 dlg.SetYesNoLabels( _( "Errors and Warnings Only" ),
1453 _( "Errors, Warnings and Exclusions" ) );
1454
1455 int ret = dlg.ShowModal();
1456
1457 if( ret == wxID_CANCEL )
1458 return;
1459 else if( ret == wxID_NO )
1460 s_includeExclusions = true;
1461 }
1462
1463 deleteAllMarkers( s_includeExclusions );
1464 m_drcRun = false;
1465
1466 refreshEditor();
1468}
1469
1470
1472{
1473 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1474 DRC_ENGINE* drcEngine = drcTool->GetDRCEngine().get();
1475
1476 // Collect counts:
1477
1478 int numMarkers = 0;
1479 int numUnconnected = 0;
1480 int numFootprints = 0;
1481
1482 int numErrors = 0;
1483 int numWarnings = 0;
1484 int numExcluded = 0;
1485
1486 if( m_markersProvider )
1487 {
1488 numMarkers += m_markersProvider->GetCount();
1489 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
1490 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
1491 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1492 }
1493
1494 if( m_ratsnestProvider )
1495 {
1496 numUnconnected += m_ratsnestProvider->GetCount();
1497 numErrors += m_ratsnestProvider->GetCount( RPT_SEVERITY_ERROR );
1498 numWarnings += m_ratsnestProvider->GetCount( RPT_SEVERITY_WARNING );
1499 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1500 }
1501
1503 {
1504 numFootprints += m_fpWarningsProvider->GetCount();
1505 numErrors += m_fpWarningsProvider->GetCount( RPT_SEVERITY_ERROR );
1506 numWarnings += m_fpWarningsProvider->GetCount( RPT_SEVERITY_WARNING );
1507 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1508 }
1509
1510 bool errorsOverflowed = false;
1511 bool warningsOverflowed = false;
1512 bool markersOverflowed = false;
1513 bool unconnectedOverflowed = false;
1514 bool footprintsOverflowed = false;
1515
1516 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
1517 {
1518 const SEVERITY severity = bds().GetSeverity( ii );
1519
1520 if( drcEngine->IsErrorLimitExceeded( ii ) )
1521 {
1522 if( severity == RPT_SEVERITY_ERROR )
1523 errorsOverflowed = true;
1524 else if( severity == RPT_SEVERITY_WARNING )
1525 warningsOverflowed = true;
1526
1527 if( ii == DRCE_UNCONNECTED_ITEMS )
1528 {
1529 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1530 unconnectedOverflowed = true;
1531 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1532 unconnectedOverflowed = true;
1533 }
1534 else if( ii == DRCE_MISSING_FOOTPRINT
1536 || ii == DRCE_EXTRA_FOOTPRINT
1537 || ii == DRCE_NET_CONFLICT
1538 || ii == DRCE_SCHEMATIC_PARITY
1539 || ii == DRCE_FOOTPRINT_FILTERS
1541 {
1542 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1543 footprintsOverflowed = true;
1544 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1545 footprintsOverflowed = true;
1546 }
1547 else
1548 {
1549 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1550 markersOverflowed = true;
1551 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1552 markersOverflowed = true;
1553 }
1554 }
1555 }
1556
1557 wxString msg;
1558 wxString num;
1559
1560 // Update tab headers:
1561
1562 if( m_drcRun )
1563 {
1564 num.Printf( markersOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numMarkers );
1565 msg.Printf( m_markersTitleTemplate, num );
1566 }
1567 else
1568 {
1570 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1571 }
1572
1573 m_Notebook->SetPageText( 0, msg );
1574
1575 if( m_drcRun )
1576 {
1577 num.Printf( unconnectedOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numUnconnected );
1578 msg.sprintf( m_unconnectedTitleTemplate, num );
1579 }
1580 else
1581 {
1583 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1584 }
1585
1586 m_Notebook->SetPageText( 1, msg );
1587
1589 {
1590 num.Printf( footprintsOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numFootprints );
1591 msg.sprintf( m_footprintsTitleTemplate, num );
1592 }
1593 else if( m_drcRun )
1594 {
1596 msg.Replace( wxT( "%s" ), _( "not run" ) );
1597 }
1598 else
1599 {
1601 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1602 }
1603
1604 m_Notebook->SetPageText( 2, msg );
1605
1606 if( m_drcRun )
1607 {
1608 num.Printf( wxT( "%d" ), m_ignoredList->GetItemCount() );
1609 msg.sprintf( m_ignoredTitleTemplate, num );
1610 }
1611 else
1612 {
1614 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1615 }
1616
1617 m_Notebook->SetPageText( 3, msg );
1618
1619 // Update badges:
1620
1621 if( !m_drcRun && numErrors == 0 )
1622 numErrors = -1;
1623
1624 if( !m_drcRun && numWarnings == 0 )
1625 numWarnings = -1;
1626
1627 m_errorsBadge->SetMaximumNumber( numErrors );
1628 m_errorsBadge->UpdateNumber( errorsOverflowed ? numErrors + 1 : numErrors, RPT_SEVERITY_ERROR );
1629
1630 m_warningsBadge->SetMaximumNumber( numWarnings );
1631 m_warningsBadge->UpdateNumber( warningsOverflowed ? numWarnings + 1 : numWarnings, RPT_SEVERITY_WARNING );
1632
1633 m_exclusionsBadge->SetMaximumNumber( numExcluded );
1634 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
1635}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION excludeMarker
Definition actions.h:125
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
std::map< wxString, wxString > m_DrcExclusionComments
std::map< int, SEVERITY > m_DRCSeverities
std::set< wxString > m_DrcExclusions
SEVERITY GetSeverity(int aDRCErrorCode)
Tool for pcb inspection.
void InspectDRCError(const std::shared_ptr< RC_ITEM > &aDRCItem)
Show the clearance resolution for two selected items.
wxString InspectDRCErrorMenuText(const std::shared_ptr< RC_ITEM > &aDRCItem)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:372
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1048
const MARKERS & Markers() const
Definition board.h:432
void FinalizeBulkRemove(std::vector< BOARD_ITEM * > &aRemovedItems)
Must be used if Remove() is used using a BULK_x REMOVE_MODE to generate a change event for listeners.
Definition board.cpp:1419
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1442
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1846
bool Dirty() const
BOARD_CONNECTED_ITEM * Parent() const
CN_EDGE represents a point-to-point connection, whether realized or unrealized (ie: tracks etc.
std::shared_ptr< const CN_ANCHOR > GetSourceNode() const
std::shared_ptr< const CN_ANCHOR > GetTargetNode() const
const VECTOR2I GetTargetPos() const
const VECTOR2I GetSourcePos() const
wxCheckBox * m_cbRefillZones
wxBoxSizer * bSizerViolationsBox
wxCheckBox * m_showAll
wxButton * m_DeleteAllMarkersButton
wxCheckBox * m_showExclusions
wxNotebook * m_Notebook
wxCheckBox * m_showErrors
wxDataViewCtrl * m_unconnectedDataView
wxDataViewCtrl * m_footprintsDataView
NUMBER_BADGE * m_warningsBadge
NUMBER_BADGE * m_exclusionsBadge
wxSimplebook * m_runningResultsBook
wxButton * m_DeleteCurrentMarkerButton
STD_BITMAP_BUTTON * m_bMenu
wxButton * m_sdbSizerCancel
wxButton * m_saveReport
wxCheckBox * m_cbTestFootprints
DIALOG_DRC_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Design Rules Checker"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
wxCheckBox * m_showWarnings
wxButton * m_sdbSizerOK
wxListCtrl * m_ignoredList
NUMBER_BADGE * m_errorsBadge
WX_HTML_REPORT_BOX * m_messages
wxDataViewCtrl * m_markerDataView
bool updateUI() override
void OnDRCItemSelected(wxDataViewEvent &aEvent) override
wxString m_footprintsTitleTemplate
Definition dialog_drc.h:133
void UpdateData()
Rebuild the contents of the violation tabs based on the current markers and severties.
std::shared_ptr< RC_ITEMS_PROVIDER > m_ratsnestProvider
Definition dialog_drc.h:137
wxString m_markersTitleTemplate
Definition dialog_drc.h:131
bool m_footprintTestsRun
Definition dialog_drc.h:125
DIALOG_DRC(PCB_EDIT_FRAME *aEditorFrame, wxWindow *aParent)
Constructors.
void OnEditViolationSeverities(wxHyperlinkEvent &aEvent) override
wxStatusBar * m_drcStatusBar
Definition dialog_drc.h:147
void OnDeleteOneClick(wxCommandEvent &aEvent) override
RC_TREE_MODEL * m_fpWarningsTreeModel
Definition dialog_drc.h:142
static bool hitTestLink(wxDataViewCtrl *aCtrl, const wxPoint &aPoint, wxString *aHref)
bool m_running
Definition dialog_drc.h:123
void onDataViewMotion(wxMouseEvent &aEvent)
void OnDeleteAllClick(wxCommandEvent &aEvent) override
bool m_crossprobe
Definition dialog_drc.h:128
void OnMenu(wxCommandEvent &aEvent) override
bool TransferDataToWindow() override
void OnErrorLinkClicked(wxHtmlLinkEvent &event) override
int m_lastTickSeconds
Definition dialog_drc.h:149
BOARD_DESIGN_SETTINGS & bds()
Definition dialog_drc.h:118
bool m_report_all_track_errors
Definition dialog_drc.h:127
void SelectMarker(const PCB_MARKER *aMarker)
void OnClose(wxCloseEvent &event) override
void installLinkHandlers(wxDataViewCtrl *aCtrl)
void OnCharHook(wxKeyEvent &aEvt) override
std::chrono::steady_clock::time_point m_drcStartTime
Definition dialog_drc.h:148
int getSeverities()
BOARD * m_currentBoard
Definition dialog_drc.h:121
void OnDRCItemRClick(wxDataViewEvent &aEvent) override
void PrevMarker()
void OnRunDRCClick(wxCommandEvent &aEvent) override
wxString m_ignoredTitleTemplate
Definition dialog_drc.h:134
THROTTLE m_yieldThrottle
Definition dialog_drc.h:145
void OnDRCItemDClick(wxDataViewEvent &aEvent) override
THROTTLE m_updateThrottle
Definition dialog_drc.h:144
void deleteAllMarkers(bool aIncludeExclusions)
void updateDisplayedCounts()
RC_TREE_MODEL * m_unconnectedTreeModel
Definition dialog_drc.h:141
bool m_scroll_on_crossprobe
Definition dialog_drc.h:129
void onDataViewLeftUp(wxMouseEvent &aEvent)
void refreshEditor()
wxString m_unconnectedTitleTemplate
Definition dialog_drc.h:132
std::shared_ptr< RC_ITEMS_PROVIDER > m_fpWarningsProvider
Definition dialog_drc.h:138
std::shared_ptr< RC_ITEMS_PROVIDER > m_markersProvider
Definition dialog_drc.h:136
void OnSeverity(wxCommandEvent &aEvent) override
PCB_EDIT_FRAME * m_frame
Definition dialog_drc.h:122
bool m_drcRun
Definition dialog_drc.h:124
void OnIgnoredItemRClick(wxListEvent &event) override
void OnCancelClick(wxCommandEvent &aEvent) override
void NextMarker()
void OnActivateDlg(wxActivateEvent &aEvent) override
void OnChangingNotebookPage(wxNotebookEvent &aEvent) override
void OnSaveReport(wxCommandEvent &aEvent) override
void ExcludeMarker()
RC_TREE_MODEL * m_markersTreeModel
Definition dialog_drc.h:140
bool Show(bool show) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
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)
EDA_UNITS GetUserUnits() const
int ShowModal() override
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
static std::vector< std::reference_wrapper< RC_ITEM > > GetItemsWithSeverities()
Definition drc_item.h:142
DRC_RULE * GetViolatingRule() const
Definition drc_item.h:161
bool WriteJsonReport(const wxString &aFullFileName)
bool WriteTextReport(const wxString &aFullFileName)
bool IsImplicit() const
Definition drc_rule.h:143
wxString m_Name
Definition drc_rule.h:153
void DestroyDRCDialog()
Close and free the DRC dialog.
Definition drc_tool.cpp:125
std::shared_ptr< DRC_ENGINE > GetDRCEngine()
Definition drc_tool.h:83
void RunTests(PROGRESS_REPORTER *aProgressReporter, bool aRefillZones, bool aReportAllTrackErrors, bool aTestFootprints)
Run the DRC tests.
Definition drc_tool.cpp:135
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:155
Definition kiid.h:44
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static const LSET & AllLayersMask()
Definition lset.cpp:637
const VECTOR2I & GetPos() const
Definition marker_base.h:79
@ MARKER_DRAWING_SHEET
Definition marker_base.h:52
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:90
enum MARKER_T GetMarkerType() const
Definition marker_base.h:87
Definition pad.h:61
static TOOL_ACTION showRatsnest
The main frame for Pcbnew.
SEVERITY GetSeverity() const override
wxString SerializeToString() const
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition pgm_base.cpp:204
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:183
MARKER_BASE * GetParent() const
Definition rc_item.h:132
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition rc_item.h:267
void ValueChanged(RC_TREE_NODE *aNode)
Definition rc_item.cpp:611
void Update(std::shared_ptr< RC_ITEMS_PROVIDER > aProvider, int aSeverities)
Definition rc_item.cpp:413
void DeleteCurrentItem(bool aDeep)
Definition rc_item.cpp:662
void EnableHyperlinks(bool aEnable)
Render [label](url) markup as clickable links.
Definition rc_item.cpp:419
static KIID ToUUID(wxDataViewItem aItem)
Definition rc_item.cpp:223
std::shared_ptr< RC_ITEM > m_RcItem
Definition rc_item.h:246
NODE_TYPE m_Type
Definition rc_item.h:245
Master controller class:
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
Handle actions specific to filling copper zones.
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
static bool g_lastDRCRun
static BOARD * g_lastDRCBoard
static bool g_lastFootprintTestsRun
static std::vector< std::pair< wxString, int > > g_lastIgnored
#define DIALOG_DRC_WINDOW_NAME
Definition dialog_drc.h:40
@ DRCE_FOOTPRINT_FILTERS
Definition drc_item.h:76
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:36
@ DRCE_INVALID_OUTLINE
Definition drc_item.h:69
@ DRCE_SCHEMATIC_FIELDS_PARITY
Definition drc_item.h:119
@ DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG
Definition drc_item.h:107
@ DRCE_MALFORMED_COURTYARD
Definition drc_item.h:64
@ DRCE_FIRST
Definition drc_item.h:35
@ DRCE_UNRESOLVED_VARIABLE
Definition drc_item.h:85
@ DRCE_DUPLICATE_FOOTPRINT
Definition drc_item.h:72
@ DRCE_EXTRA_FOOTPRINT
Definition drc_item.h:73
@ DRCE_LAST
Definition drc_item.h:121
@ DRCE_NET_CONFLICT
Definition drc_item.h:74
@ DRCE_MISSING_FOOTPRINT
Definition drc_item.h:71
@ DRCE_SCHEMATIC_PARITY
Definition drc_item.h:75
#define _(s)
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
static int DEFAULT_SINGLE_COL_WIDTH
static const std::string ReportFileExtension
static const std::string JsonFileExtension
static wxString JsonFileWildcard()
static wxString ReportFileWildcard()
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
This file contains miscellaneous commonly used macros and functions.
void SetFloatLevel(wxWindow *aWindow)
Intended to set the floating window level in macOS on a window.
Definition wxgtk/ui.cpp:437
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition wxgtk/ui.cpp:126
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_EXCLUSION
@ RPT_SEVERITY_IGNORE
std::vector< FAB_LAYER_COLOR > dummy
A filename or source description, a problem input line, a line number, a byte offset,...
KIBIS_MODEL * model
@ MD_ALT
Definition tool_event.h:141
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.