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