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