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