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