KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_drc.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2009-2016 Dick Hollenbeck, [email protected]
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <confirm.h>
27#include <core/throttle.h>
28#include <dialog_drc.h>
30#include <kiface_base.h>
31#include <macros.h>
32#include <pad.h>
33#include <drc/drc_item.h>
34#include <drc/drc_report.h>
38#include <pcb_edit_frame.h>
39#include <tool/tool_manager.h>
40#include <tools/pcb_actions.h>
42#include <pcb_marker.h>
43#include <pcb_track.h>
44#include <pgm_base.h>
45#include <wx/app.h>
46#include <wx/filedlg.h>
47#include <wx/msgdlg.h>
48#include <wx/wupdlock.h>
50#include <widgets/ui_common.h>
54#include <view/view_controls.h>
57#include <tools/drc_tool.h>
60#include <kiplatform/ui.h>
61
62// wxWidgets spends *far* too long calcuating column widths (most of it, believe it or
63// not, in repeatedly creating/destroying a wxDC to do the measurement in).
64// Use default column widths instead.
65static int DEFAULT_SINGLE_COL_WIDTH = 660;
66
67static BOARD* g_lastDRCBoard = nullptr;
68static bool g_lastDRCRun = false;
69static bool g_lastFootprintTestsRun = false;
70
71static std::vector<std::pair<wxString, int>> g_lastIgnored;
72
73
74DIALOG_DRC::DIALOG_DRC( PCB_EDIT_FRAME* aEditorFrame, wxWindow* aParent ) :
75 DIALOG_DRC_BASE( aParent ),
77 m_running( false ),
78 m_drcRun( false ),
79 m_footprintTestsRun( false ),
81 m_crossprobe( true ),
83 m_markersTreeModel( nullptr ),
84 m_unconnectedTreeModel( nullptr ),
85 m_fpWarningsTreeModel( nullptr ),
86 m_updateThrottle( std::chrono::milliseconds( 100 ) ),
87 m_yieldThrottle( std::chrono::milliseconds( 2000 ) )
88{
89 SetName( DIALOG_DRC_WINDOW_NAME ); // Set a window name to be able to find it
91
92 m_frame = aEditorFrame;
93 m_currentBoard = m_frame->GetBoard();
94
95 m_bMenu->SetBitmap( KiBitmapBundle( BITMAPS::config ) );
96
97 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
98 {
99 m_report_all_track_errors = cfg->m_DRCDialog.report_all_track_errors;
100 m_crossprobe = cfg->m_DRCDialog.crossprobe;
101 m_scroll_on_crossprobe = cfg->m_DRCDialog.scroll_on_crossprobe;
102 }
103
104 m_messages->SetImmediateMode();
105
106 m_markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
109
110 m_ratsnestProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
112
113 m_fpWarningsProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_currentBoard,
115
117 m_markerDataView->AssociateModel( m_markersTreeModel );
118
121
124
125 // Prevent RTL locales from mirroring the text in the data views
126 m_markerDataView->SetLayoutDirection( wxLayout_LeftToRight );
127 m_unconnectedDataView->SetLayoutDirection( wxLayout_LeftToRight );
128 m_footprintsDataView->SetLayoutDirection( wxLayout_LeftToRight );
129 m_ignoredList->SetLayoutDirection( wxLayout_LeftToRight );
130
131 m_ignoredList->InsertColumn( 0, wxEmptyString, wxLIST_FORMAT_LEFT, DEFAULT_SINGLE_COL_WIDTH );
132
134 {
137
138 for( const auto& [ str, code ] : g_lastIgnored )
139 {
140 wxListItem listItem;
141 listItem.SetId( m_ignoredList->GetItemCount() );
142 listItem.SetText( str );
143 listItem.SetData( code );
144
145 m_ignoredList->InsertItem( listItem );
146 }
147 }
148
149 m_Notebook->SetSelection( 0 );
150
151 if( Kiface().IsSingle() )
152 m_cbTestFootprints->Hide();
153
154 SetupStandardButtons( { { wxID_OK, _( "Run DRC" ) },
155 { wxID_CANCEL, _( "Close" ) } } );
156
157 m_markersTitleTemplate = m_Notebook->GetPageText( 0 );
158 m_unconnectedTitleTemplate = m_Notebook->GetPageText( 1 );
159 m_footprintsTitleTemplate = m_Notebook->GetPageText( 2 );
160 m_ignoredTitleTemplate = m_Notebook->GetPageText( 3 );
161
162 // DPI fix
163 bSizerViolationsBox->SetMinSize( FromDIP( bSizerViolationsBox->GetMinSize() ) );
164
165 Layout(); // adding the units above expanded Clearance text, now resize.
166
167 SetFocus();
168
170}
171
172
174{
175 if( PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings() )
176 {
177 cfg->m_DRCDialog.report_all_track_errors = m_report_all_track_errors;
178 cfg->m_DRCDialog.crossprobe = m_crossprobe;
179 cfg->m_DRCDialog.scroll_on_crossprobe = m_scroll_on_crossprobe;
180 }
181
182 m_frame->ClearFocus();
183
187
188 g_lastIgnored.clear();
189
190 for( int ii = 0; ii < m_ignoredList->GetItemCount(); ++ii )
191 g_lastIgnored.push_back( { m_ignoredList->GetItemText( ii ), m_ignoredList->GetItemData( ii ) } );
192
193 m_markersTreeModel->DecRef();
194 m_unconnectedTreeModel->DecRef();
195 m_fpWarningsTreeModel->DecRef();
196}
197
198
200{
201 UpdateData();
202 return true;
203}
204
205
206void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
207{
208 if( m_currentBoard != m_frame->GetBoard() )
209 {
210 // If m_currentBoard is not the current board, (for instance because a new board was loaded),
211 // close the dialog, because many pointers are now invalid in lists
212 SetReturnCode( wxID_CANCEL );
213 Close();
214
215 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
216 drcTool->DestroyDRCDialog();
217 }
218}
219
220
221// PROGRESS_REPORTER calls
222
224{
225 if( m_maxProgress != 0 )
226 {
227 double cur = std::clamp( (double) m_progress.load() / m_maxProgress, 0.0, 1.0 );
228
229 int newValue = KiROUND( cur * 1000.0 );
230 m_gauge->SetValue( newValue );
231 }
232
233 // Repaint the dialog at ~10Hz using Update() which processes only pending expose/
234 // draw events without entering the full platform event loop.
235 if( m_updateThrottle.Ready() )
236 Update();
237
238 // Yield to the event loop infrequently so the cancel button remains functional.
239 // On some Linux systems with glycin-enabled gdk-pixbuf (2.44+), entering the GTK event
240 // loop triggers heavyweight sandbox process spawning that can add hundreds of milliseconds
241 // per call, so we keep this interval long.
242 if( m_yieldThrottle.Ready() )
243 Pgm().App().SafeYieldFor( this, wxEVT_CATEGORY_NATIVE_EVENTS );
244
245 return !m_cancelled;
246}
247
248
249void DIALOG_DRC::AdvancePhase( const wxString& aMessage )
250{
252 SetCurrentProgress( 0.0 );
253
254 m_messages->Report( aMessage );
255}
256
257
259{
260 int severities = 0;
261
262 if( m_showErrors->GetValue() )
263 severities |= RPT_SEVERITY_ERROR;
264
265 if( m_showWarnings->GetValue() )
266 severities |= RPT_SEVERITY_WARNING;
267
268 if( m_showExclusions->GetValue() )
269 severities |= RPT_SEVERITY_EXCLUSION;
270
271 return severities;
272}
273
274
275void DIALOG_DRC::OnMenu( wxCommandEvent& event )
276{
277 // Build a pop menu:
278 wxMenu menu;
279
280 menu.Append( 4205, _( "Report All Errors for Each Track" ),
281 _( "If unchecked, only the first error will be reported for each track" ),
282 wxITEM_CHECK );
283 menu.Check( 4205, m_report_all_track_errors );
284
285 menu.AppendSeparator();
286
287 menu.Append( 4206, _( "Cross-probe Selected Items" ),
288 _( "Highlight corresponding items on canvas when selected in the DRC list" ),
289 wxITEM_CHECK );
290 menu.Check( 4206, m_crossprobe );
291
292 menu.Append( 4207, _( "Center on Cross-probe" ),
293 _( "When cross-probing, scroll the canvas so that the item is visible" ),
294 wxITEM_CHECK );
295 menu.Check( 4207, m_scroll_on_crossprobe );
296
297 // menu_id is the selected submenu id from the popup menu or wxID_NONE
298 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
299
300 if( menu_id == 0 || menu_id == 4205 )
301 {
303 }
304 else if( menu_id == 2 || menu_id == 4206 )
305 {
307 }
308 else if( menu_id == 3 || menu_id == 4207 )
309 {
311 }
312}
313
314
315void DIALOG_DRC::OnErrorLinkClicked( wxHtmlLinkEvent& event )
316{
317 m_frame->ShowBoardSetupDialog( _( "Custom Rules" ), this );
318}
319
320
321void DIALOG_DRC::OnCharHook( wxKeyEvent& aEvt )
322{
323 if( int hotkey = aEvt.GetKeyCode() )
324 {
325 if( aEvt.ControlDown() )
326 hotkey |= MD_CTRL;
327 if( aEvt.ShiftDown() )
328 hotkey |= MD_SHIFT;
329 if( aEvt.AltDown() )
330 hotkey |= MD_ALT;
331
332 if( hotkey == ACTIONS::excludeMarker.GetHotKey() )
333 {
335 return;
336 }
337 }
338
340}
341
342
343void DIALOG_DRC::OnRunDRCClick( wxCommandEvent& aEvent )
344{
345 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
346 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
347 ZONE_FILLER_TOOL* zoneFillerTool = toolMgr->GetTool<ZONE_FILLER_TOOL>();
348 bool refillZones = m_cbRefillZones->GetValue();
349 bool testFootprints = m_cbTestFootprints->GetValue();
350
351 if( zoneFillerTool->IsBusy() )
352 {
353 wxBell();
354 return;
355 }
356
357 // This is not the time to have stale or buggy rules. Ensure they're up-to-date
358 // and that they at least parse.
359 try
360 {
361 drcTool->GetDRCEngine()->InitEngine( m_frame->GetDesignRulesPath() );
362 }
363 catch( PARSE_ERROR& )
364 {
365 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
366 m_DeleteCurrentMarkerButton->Enable( false );
367 m_DeleteAllMarkersButton->Enable( false );
368 m_saveReport->Enable( false );
369
370 m_messages->Clear();
371 m_messages->Report( _( "DRC incomplete: could not compile custom design rules." )
372 + wxS( "&nbsp;&nbsp;" )
373 + wxS( "<a href='$CUSTOM_RULES'>" ) + _( "Show design rules." ) + wxT( "</a>" ) );
374 m_messages->Flush();
375
376 Raise();
377
378 // set float level again, it can be lost due to window events during test run
380 return;
381 }
382
383 m_footprintTestsRun = false;
384 m_cancelled = false;
385
386 m_frame->GetBoard()->RecordDRCExclusions();
387 deleteAllMarkers( true );
388
389 std::vector<std::reference_wrapper<RC_ITEM>> violations = DRC_ITEM::GetItemsWithSeverities();
390 m_ignoredList->DeleteAllItems();
391
392 for( std::reference_wrapper<RC_ITEM>& item : violations )
393 {
394 if( bds().GetSeverity( item.get().GetErrorCode() ) == RPT_SEVERITY_IGNORE )
395 {
396 wxListItem listItem;
397 listItem.SetId( m_ignoredList->GetItemCount() );
398 listItem.SetText( wxT( " • " ) + item.get().GetErrorText( true ) );
399 listItem.SetData( item.get().GetErrorCode() );
400
401 m_ignoredList->InsertItem( listItem );
402 }
403 }
404
405 m_ignoredList->SetColumnWidth( 0, m_ignoredList->GetParent()->GetClientSize().x - 20 );
406
407 Raise();
408
409 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
410 m_messages->Clear();
411 wxSafeYield(); // Allow time slice to refresh Messages
412
413 m_running = true;
414 m_sdbSizerCancel->SetLabel( _( "Cancel" ) );
415 m_sdbSizerOK->Enable( false );
416 m_DeleteCurrentMarkerButton->Enable( false );
417 m_DeleteAllMarkersButton->Enable( false );
418 m_saveReport->Enable( false );
419
420 {
421 wxBusyCursor dummy;
422 drcTool->RunTests( this, refillZones, m_report_all_track_errors, testFootprints );
423 }
424
425 if( m_cancelled )
426 m_messages->Report( _( "-------- DRC canceled by user.<br><br>" ) );
427 else
428 m_messages->Report( _( "Done.<br><br>" ) );
429
430 Raise();
431 wxSafeYield(); // Allow time slice to refresh Messages
432
433 m_running = false;
434 m_sdbSizerCancel->SetLabel( _( "Close" ) );
435 m_sdbSizerOK->Enable( true );
436 m_DeleteCurrentMarkerButton->Enable( true );
437 m_DeleteAllMarkersButton->Enable( true );
438 m_saveReport->Enable( true );
439
440 if( !m_cancelled )
441 {
442 m_sdbSizerCancel->SetDefault();
443 // wxWidgets has a tendency to keep both buttons highlighted without the following:
444 m_sdbSizerOK->Enable( false );
445
446 wxMilliSleep( 500 );
447 m_runningResultsBook->ChangeSelection( 1 );
449
450 // now re-enable m_sdbSizerOK button
451 m_sdbSizerOK->Enable( true );
452 }
453
454 // set float level again, it can be lost due to window events during test run
457}
458
459
461{
462 int severities = getSeverities();
463
464 m_markersTreeModel->Update( m_markersProvider, severities );
465 m_unconnectedTreeModel->Update( m_ratsnestProvider, severities );
466 m_fpWarningsTreeModel->Update( m_fpWarningsProvider, severities );
467
469}
470
471
472void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
473{
474 if( !m_crossprobe )
475 {
476 aEvent.Skip();
477 return;
478 }
479
480 BOARD* board = m_frame->GetBoard();
481 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
482
483 auto getActiveLayers =
484 []( BOARD_ITEM* aItem ) -> LSET
485 {
486 if( aItem->Type() == PCB_PAD_T )
487 {
488 PAD* pad = static_cast<PAD*>( aItem );
489 LSET layers;
490
491 for( int layer : aItem->GetLayerSet() )
492 {
493 if( pad->FlashLayer( layer ) )
494 layers.set( layer );
495 }
496
497 return layers;
498 }
499 else
500 {
501 return aItem->GetLayerSet();
502 }
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 m_frame->FocusOnItem( item, principalLayer, m_scroll_on_crossprobe );
672 }
673
674 aEvent.Skip();
675}
676
677
678void DIALOG_DRC::OnDRCItemDClick( wxDataViewEvent& aEvent )
679{
680 if( aEvent.GetItem().IsOk() )
681 {
682 // turn control over to m_frame, hide this DIALOG_DRC window,
683 // no destruction so we can preserve listbox cursor
684 if( !IsModal() )
685 Show( false );
686 }
687
688 // Do not skip aEvent here: this is not useful, and Pcbnew crashes if skipped (at least on MSW)
689}
690
691
692void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
693{
694 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
695 BOARD_INSPECTION_TOOL* inspectionTool = toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
696 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
697 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
698
699 if( !node )
700 return;
701
702 std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
703 DRC_ITEM* drcItem = static_cast<DRC_ITEM*>( rcItem.get() );
704 std::shared_ptr<CONNECTIVITY_DATA> conn = m_currentBoard->GetConnectivity();
705 wxString listName;
706 wxMenu menu;
707
708 switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] )
709 {
710 case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
711 case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
712 default: listName = _( "appropriate" ); break;
713 }
714
715 enum MENU_IDS
716 {
717 ID_EDIT_EXCLUSION_COMMENT = 4467,
718 ID_REMOVE_EXCLUSION,
719 ID_REMOVE_EXCLUSION_ALL,
720 ID_ADD_EXCLUSION,
721 ID_ADD_EXCLUSION_WITH_COMMENT,
722 ID_ADD_EXCLUSION_ALL,
723 ID_INSPECT_VIOLATION,
724 ID_FIX_VIOLATION,
725 ID_SET_SEVERITY_TO_ERROR,
726 ID_SET_SEVERITY_TO_WARNING,
727 ID_SET_SEVERITY_TO_IGNORE,
728 ID_EDIT_SEVERITIES
729 };
730
731 if( rcItem->GetParent()->IsExcluded() )
732 {
733 menu.Append( ID_REMOVE_EXCLUSION,
734 _( "Remove exclusion for this violation" ),
735 wxString::Format( _( "It will be placed back in the %s list" ), listName ) );
736
737 menu.Append( ID_EDIT_EXCLUSION_COMMENT,
738 _( "Edit exclusion comment..." ) );
739
740 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
741 {
742 menu.Append( ID_REMOVE_EXCLUSION_ALL,
743 wxString::Format( _( "Remove all exclusions for violations of rule '%s'" ),
744 drcItem->GetViolatingRule()->m_Name ),
745 wxString::Format( _( "They will be placed back in the %s list" ), listName ) );
746 }
747 }
748 else
749 {
750 menu.Append( ID_ADD_EXCLUSION,
751 _( "Exclude this violation" ),
752 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
753
754 menu.Append( ID_ADD_EXCLUSION_WITH_COMMENT,
755 _( "Exclude with comment..." ),
756 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
757
758 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
759 {
760 menu.Append( ID_ADD_EXCLUSION_ALL,
761 wxString::Format( _( "Exclude all violations of rule '%s'..." ),
762 drcItem->GetViolatingRule()->m_Name ),
763 wxString::Format( _( "They will be excluded from the %s list" ), listName ) );
764 }
765 }
766
767 menu.AppendSeparator();
768
769 wxString inspectDRCErrorMenuText = inspectionTool->InspectDRCErrorMenuText( rcItem );
770 wxString fixDRCErrorMenuText = drcTool->FixDRCErrorMenuText( rcItem );
771
772 if( !inspectDRCErrorMenuText.IsEmpty() || !fixDRCErrorMenuText.IsEmpty() )
773 {
774 if( !inspectDRCErrorMenuText.IsEmpty() )
775 menu.Append( ID_INSPECT_VIOLATION, inspectDRCErrorMenuText );
776
777 if( !fixDRCErrorMenuText.IsEmpty() )
778 menu.Append( ID_FIX_VIOLATION, fixDRCErrorMenuText );
779
780 menu.AppendSeparator();
781 }
782
783 if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
784 {
785 menu.Append( ID_SET_SEVERITY_TO_ERROR,
786 wxString::Format( _( "Change severity to Error for all '%s' violations" ),
787 rcItem->GetErrorText( true ) ),
788 _( "Violation severities can also be edited in the Board Setup... dialog" ) );
789 }
790 else
791 {
792 menu.Append( ID_SET_SEVERITY_TO_WARNING,
793 wxString::Format( _( "Change severity to Warning for all '%s' violations" ),
794 rcItem->GetErrorText( true ) ),
795 _( "Violation severities can also be edited in the Board Setup... dialog" ) );
796 }
797
798 menu.Append( ID_SET_SEVERITY_TO_IGNORE,
799 wxString::Format( _( "Ignore all '%s' violations" ), rcItem->GetErrorText( true ) ),
800 _( "Violations will not be checked or reported" ) );
801
802 menu.AppendSeparator();
803
804 menu.Append( ID_EDIT_SEVERITIES,
805 _( "Edit violation severities..." ),
806 _( "Open the Board Setup... dialog" ) );
807
808 bool modified = false;
809 int command = GetPopupMenuSelectionFromUser( menu );
810
811 switch( command )
812 {
813 case ID_EDIT_EXCLUSION_COMMENT:
814 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() ) )
815 {
816 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), marker->GetComment(), true );
817
818 if( dlg.ShowModal() == wxID_CANCEL )
819 break;
820
821 marker->SetExcluded( true, dlg.GetValue() );
822
823 wxString serialized = marker->SerializeToString();
824 bds().m_DrcExclusions.insert( serialized );
825 bds().m_DrcExclusionComments[serialized] = dlg.GetValue();
826
827 // Update view
828 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
829 modified = true;
830 }
831
832 break;
833
834 case ID_REMOVE_EXCLUSION:
835 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
836 {
837 marker->SetExcluded( false );
838
839 wxString serialized = marker->SerializeToString();
840 bds().m_DrcExclusions.erase( serialized );
841 bds().m_DrcExclusionComments.erase( serialized );
842
843 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
844 {
845 m_frame->GetBoard()->UpdateRatsnestExclusions();
846 m_frame->GetCanvas()->RedrawRatsnest();
847 }
848 else
849 {
850 m_frame->GetCanvas()->GetView()->Update( marker );
851 }
852
853 // Update view
854 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
855 modified = true;
856 }
857
858 break;
859
860 case ID_ADD_EXCLUSION:
861 case ID_ADD_EXCLUSION_WITH_COMMENT:
862 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
863 {
864 wxString comment;
865
866 if( command == ID_ADD_EXCLUSION_WITH_COMMENT )
867 {
868 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), wxEmptyString, true );
869
870 if( dlg.ShowModal() == wxID_CANCEL )
871 break;
872
873 comment = dlg.GetValue();
874 }
875
876 marker->SetExcluded( true, comment );
877
878 wxString serialized = marker->SerializeToString();
879 bds().m_DrcExclusions.insert( serialized );
880 bds().m_DrcExclusionComments[serialized] = comment;
881
882 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
883 {
884 m_frame->GetBoard()->UpdateRatsnestExclusions();
885 m_frame->GetCanvas()->RedrawRatsnest();
886 }
887 else
888 {
889 m_frame->GetCanvas()->GetView()->Update( marker );
890 }
891
892 // Update view
893 if( m_showExclusions->GetValue() )
894 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
895 else
896 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
897
898 modified = true;
899 }
900
901 break;
902
903 case ID_REMOVE_EXCLUSION_ALL:
904 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
905 {
906 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
907
908 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
909 {
910 marker->SetExcluded( false );
911
912 wxString serialized = marker->SerializeToString();
913 bds().m_DrcExclusions.erase( serialized );
914 bds().m_DrcExclusionComments.erase( serialized );
915 }
916 }
917
918 // Rebuild model and view
919 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
920 modified = true;
921 break;
922
923 case ID_ADD_EXCLUSION_ALL:
924 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
925 {
926 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
927
928 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
929 {
930 marker->SetExcluded( true );
931
932 wxString serialized = marker->SerializeToString();
933 bds().m_DrcExclusions.insert( serialized );
934 }
935 }
936
937 // Rebuild model and view
938 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
939 modified = true;
940 break;
941
942 case ID_INSPECT_VIOLATION:
943 inspectionTool->InspectDRCError( node->m_RcItem );
944 break;
945
946 case ID_FIX_VIOLATION:
947 drcTool->FixDRCError( node->m_RcItem );
948 break;
949
950 case ID_SET_SEVERITY_TO_ERROR:
951 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
952
953 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
954 {
955 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
956 m_frame->GetCanvas()->GetView()->Update( marker );
957 }
958
959 // Rebuild model and view
960 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
961 modified = true;
962 break;
963
964 case ID_SET_SEVERITY_TO_WARNING:
965 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
966
967 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
968 {
969 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
970 m_frame->GetCanvas()->GetView()->Update( marker );
971 }
972
973 // Rebuild model and view
974 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
975 modified = true;
976 break;
977
978 case ID_SET_SEVERITY_TO_IGNORE:
979 {
980 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
981
982 wxListItem listItem;
983 listItem.SetId( m_ignoredList->GetItemCount() );
984 listItem.SetText( wxT( " • " ) + rcItem->GetErrorText( true ) );
985 listItem.SetData( rcItem->GetErrorCode() );
986
987 m_ignoredList->InsertItem( listItem );
988
989 BOARD* board = m_frame->GetBoard();
990
991 std::vector<BOARD_ITEM*> toRemove;
992
993 for( PCB_MARKER* marker : board->Markers() )
994 {
995 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
996 {
997 m_frame->GetCanvas()->GetView()->Remove( marker );
998 toRemove.emplace_back( marker );
999 }
1000 }
1001
1002 for( BOARD_ITEM* marker : toRemove )
1003 board->Remove( marker, REMOVE_MODE::BULK );
1004
1005 board->FinalizeBulkRemove( toRemove );
1006
1007 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
1008 m_frame->GetCanvas()->RedrawRatsnest();
1009
1010 // Rebuild model and view
1011 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1012 modified = true;
1013 break;
1014 }
1015
1016 case ID_EDIT_SEVERITIES:
1017 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1018 break;
1019 }
1020
1021 if( modified )
1022 {
1024 refreshEditor();
1025 m_frame->OnModify();
1026 }
1027}
1028
1029
1030void DIALOG_DRC::OnIgnoredItemRClick( wxListEvent& event )
1031{
1032 int errorCode = (int) event.m_item.GetData();
1033 wxMenu menu;
1034
1035 menu.Append( RPT_SEVERITY_ERROR, _( "Error" ), wxEmptyString, wxITEM_CHECK );
1036 menu.Append( RPT_SEVERITY_WARNING, _( "Warning" ), wxEmptyString, wxITEM_CHECK );
1037 menu.Append( RPT_SEVERITY_IGNORE, _( "Ignore" ), wxEmptyString, wxITEM_CHECK );
1038
1039 menu.Check( bds().GetSeverity( errorCode ), true );
1040
1041 int severity = GetPopupMenuSelectionFromUser( menu );
1042
1043 if( severity > 0 )
1044 {
1045 if( bds().m_DRCSeverities[ errorCode ] != severity )
1046 {
1047 bds().m_DRCSeverities[ errorCode ] = (SEVERITY) severity;
1048
1050 refreshEditor();
1051 m_frame->OnModify();
1052 }
1053 }
1054}
1055
1056
1057void DIALOG_DRC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
1058{
1059 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1060}
1061
1062
1063void DIALOG_DRC::OnSeverity( wxCommandEvent& aEvent )
1064{
1065 if( aEvent.GetEventObject() == m_showAll )
1066 {
1067 m_showErrors->SetValue( true );
1068 m_showWarnings->SetValue( aEvent.IsChecked() );
1069 m_showExclusions->SetValue( aEvent.IsChecked() );
1070 }
1071
1072 UpdateData();
1073}
1074
1075
1076void DIALOG_DRC::OnSaveReport( wxCommandEvent& aEvent )
1077{
1078 wxFileName fn( "DRC." + FILEEXT::ReportFileExtension );
1079
1080 wxFileDialog dlg( this, _( "Save Report File" ), Prj().GetProjectPath(), fn.GetFullName(),
1082 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1083
1085
1086 if( dlg.ShowModal() != wxID_OK )
1087 return;
1088
1089 fn = dlg.GetPath();
1090
1091 if( fn.GetExt().IsEmpty() )
1092 fn.SetExt( FILEEXT::ReportFileExtension );
1093
1094 if( !fn.IsAbsolute() )
1095 {
1096 wxString prj_path = Prj().GetProjectPath();
1097 fn.MakeAbsolute( prj_path );
1098 }
1099
1100 DRC_REPORT reportWriter( m_frame->GetBoard(), GetUserUnits(), m_markersProvider,
1102
1103 bool success = false;
1104 if( fn.GetExt() == FILEEXT::JsonFileExtension )
1105 success = reportWriter.WriteJsonReport( fn.GetFullPath() );
1106 else
1107 success = reportWriter.WriteTextReport( fn.GetFullPath() );
1108
1109 if( success )
1110 m_messages->Report( wxString::Format( _( "Report file '%s' created<br>" ), fn.GetFullPath() ) );
1111 else
1112 DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ), fn.GetFullPath() ) );
1113}
1114
1115
1116void DIALOG_DRC::OnClose( wxCloseEvent& aEvent )
1117{
1118 wxCommandEvent dummy;
1120}
1121
1122
1123void DIALOG_DRC::OnCancelClick( wxCommandEvent& aEvent )
1124{
1125 if( m_running )
1126 {
1127 m_cancelled = true;
1128 return;
1129 }
1130
1131 m_frame->ClearFocus();
1132
1133 SetReturnCode( wxID_CANCEL );
1134
1135 // The dialog can be modal or not modal.
1136 // Leave the DRC caller destroy (or not) the dialog
1137 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1138 drcTool->DestroyDRCDialog();
1139}
1140
1141
1142void DIALOG_DRC::OnChangingNotebookPage( wxNotebookEvent& aEvent )
1143{
1144 m_markerDataView->UnselectAll();
1145 m_unconnectedDataView->UnselectAll();
1146 m_footprintsDataView->UnselectAll();
1147
1148 aEvent.Skip();
1149}
1150
1151
1153{
1154 WINDOW_THAWER thawer( m_frame );
1155
1156 m_frame->GetCanvas()->Refresh();
1157}
1158
1159
1161{
1162 if( m_Notebook->IsShown() )
1163 {
1164 switch( m_Notebook->GetSelection() )
1165 {
1166 case 0: m_markersTreeModel->PrevMarker(); break;
1167 case 1: m_unconnectedTreeModel->PrevMarker(); break;
1168 case 2: m_fpWarningsTreeModel->PrevMarker(); break;
1169 case 3: break;
1170 }
1171 }
1172}
1173
1174
1176{
1177 if( m_Notebook->IsShown() )
1178 {
1179 switch( m_Notebook->GetSelection() )
1180 {
1181 case 0: m_markersTreeModel->NextMarker(); break;
1182 case 1: m_unconnectedTreeModel->NextMarker(); break;
1183 case 2: m_fpWarningsTreeModel->NextMarker(); break;
1184 case 3: break;
1185 }
1186 }
1187}
1188
1189
1191{
1192 if( m_Notebook->IsShown() )
1193 {
1194 enum MARKER_BASE::MARKER_T markerType = aMarker->GetMarkerType();
1195
1196 if( markerType == MARKER_BASE::MARKER_DRC )
1197 m_Notebook->SetSelection( 0 );
1198 else if( markerType == MARKER_BASE::MARKER_PARITY )
1199 m_Notebook->SetSelection( 2 );
1200
1201 m_markersTreeModel->SelectMarker( aMarker );
1202
1203 CallAfter(
1204 [this, aMarker]
1205 {
1206 m_markersTreeModel->CenterMarker( aMarker );
1207 } );
1208 }
1209}
1210
1211
1213{
1214 if( !m_Notebook->IsShown() || m_Notebook->GetSelection() != 0 )
1215 return;
1216
1217 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
1218
1219 if( node && node->m_RcItem )
1220 {
1221 PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
1222
1223 if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
1224 {
1225 marker->SetExcluded( true );
1226 bds().m_DrcExclusions.insert( marker->SerializeToString() );
1227 m_frame->GetCanvas()->GetView()->Update( marker );
1228
1229 // Update view
1230 if( m_showExclusions->GetValue() )
1231 m_markersTreeModel->ValueChanged( node );
1232 else
1233 m_markersTreeModel->DeleteCurrentItem( false );
1234
1236 refreshEditor();
1237 m_frame->OnModify();
1238 }
1239 }
1240}
1241
1242
1243void DIALOG_DRC::deleteAllMarkers( bool aIncludeExclusions )
1244{
1245 // Clear current selection list to avoid selection of deleted items
1246 Freeze();
1247 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1248
1249 m_markersTreeModel->DeleteItems( false, aIncludeExclusions, false );
1250 m_unconnectedTreeModel->DeleteItems( false, aIncludeExclusions, false );
1251 m_fpWarningsTreeModel->DeleteItems( false, aIncludeExclusions, false );
1252
1253 m_frame->GetBoard()->DeleteMARKERs( true, aIncludeExclusions );
1254 Thaw();
1255}
1256
1257
1258void DIALOG_DRC::OnDeleteOneClick( wxCommandEvent& aEvent )
1259{
1260 if( m_Notebook->GetSelection() == 0 )
1261 {
1262 // Clear the selection. It may be the selected DRC marker.
1263 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1264
1265 m_markersTreeModel->DeleteCurrentItem( true );
1266
1267 // redraw the pcb
1268 refreshEditor();
1269 }
1270 else if( m_Notebook->GetSelection() == 1 )
1271 {
1272 m_unconnectedTreeModel->DeleteCurrentItem( true );
1273 }
1274 else if( m_Notebook->GetSelection() == 2 )
1275 {
1276 m_fpWarningsTreeModel->DeleteCurrentItem( true );
1277 }
1278
1280}
1281
1282
1283void DIALOG_DRC::OnDeleteAllClick( wxCommandEvent& aEvent )
1284{
1285 static bool s_includeExclusions = false;
1286
1287 int numExcluded = 0;
1288
1289 if( m_markersProvider )
1290 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1291
1292 if( m_ratsnestProvider )
1293 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1294
1296 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1297
1298 if( numExcluded > 0 )
1299 {
1300 wxMessageDialog dlg( this, _( "Delete exclusions too?" ), _( "Delete All Markers" ),
1301 wxYES_NO | wxCANCEL | wxCENTER | wxICON_QUESTION );
1302 dlg.SetYesNoLabels( _( "Errors and Warnings Only" ),
1303 _( "Errors, Warnings and Exclusions" ) );
1304
1305 int ret = dlg.ShowModal();
1306
1307 if( ret == wxID_CANCEL )
1308 return;
1309 else if( ret == wxID_NO )
1310 s_includeExclusions = true;
1311 }
1312
1313 deleteAllMarkers( s_includeExclusions );
1314 m_drcRun = false;
1315
1316 refreshEditor();
1318}
1319
1320
1322{
1323 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1324 DRC_ENGINE* drcEngine = drcTool->GetDRCEngine().get();
1325
1326 // Collect counts:
1327
1328 int numMarkers = 0;
1329 int numUnconnected = 0;
1330 int numFootprints = 0;
1331
1332 int numErrors = 0;
1333 int numWarnings = 0;
1334 int numExcluded = 0;
1335
1336 if( m_markersProvider )
1337 {
1338 numMarkers += m_markersProvider->GetCount();
1339 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
1340 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
1341 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1342 }
1343
1344 if( m_ratsnestProvider )
1345 {
1346 numUnconnected += m_ratsnestProvider->GetCount();
1347 numErrors += m_ratsnestProvider->GetCount( RPT_SEVERITY_ERROR );
1348 numWarnings += m_ratsnestProvider->GetCount( RPT_SEVERITY_WARNING );
1349 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1350 }
1351
1353 {
1354 numFootprints += m_fpWarningsProvider->GetCount();
1355 numErrors += m_fpWarningsProvider->GetCount( RPT_SEVERITY_ERROR );
1356 numWarnings += m_fpWarningsProvider->GetCount( RPT_SEVERITY_WARNING );
1357 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1358 }
1359
1360 bool errorsOverflowed = false;
1361 bool warningsOverflowed = false;
1362 bool markersOverflowed = false;
1363 bool unconnectedOverflowed = false;
1364 bool footprintsOverflowed = false;
1365
1366 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
1367 {
1368 const SEVERITY severity = bds().GetSeverity( ii );
1369
1370 if( drcEngine->IsErrorLimitExceeded( ii ) )
1371 {
1372 if( severity == RPT_SEVERITY_ERROR )
1373 errorsOverflowed = true;
1374 else if( severity == RPT_SEVERITY_WARNING )
1375 warningsOverflowed = true;
1376
1377 if( ii == DRCE_UNCONNECTED_ITEMS )
1378 {
1379 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1380 unconnectedOverflowed = true;
1381 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1382 unconnectedOverflowed = true;
1383 }
1384 else if( ii == DRCE_MISSING_FOOTPRINT
1386 || ii == DRCE_EXTRA_FOOTPRINT
1387 || ii == DRCE_NET_CONFLICT
1388 || ii == DRCE_SCHEMATIC_PARITY
1389 || ii == DRCE_FOOTPRINT_FILTERS
1391 {
1392 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1393 footprintsOverflowed = true;
1394 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1395 footprintsOverflowed = true;
1396 }
1397 else
1398 {
1399 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1400 markersOverflowed = true;
1401 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1402 markersOverflowed = true;
1403 }
1404 }
1405 }
1406
1407 wxString msg;
1408 wxString num;
1409
1410 // Update tab headers:
1411
1412 if( m_drcRun )
1413 {
1414 num.Printf( markersOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numMarkers );
1415 msg.Printf( m_markersTitleTemplate, num );
1416 }
1417 else
1418 {
1420 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1421 }
1422
1423 m_Notebook->SetPageText( 0, msg );
1424
1425 if( m_drcRun )
1426 {
1427 num.Printf( unconnectedOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numUnconnected );
1428 msg.sprintf( m_unconnectedTitleTemplate, num );
1429 }
1430 else
1431 {
1433 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1434 }
1435
1436 m_Notebook->SetPageText( 1, msg );
1437
1439 {
1440 num.Printf( footprintsOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numFootprints );
1441 msg.sprintf( m_footprintsTitleTemplate, num );
1442 }
1443 else if( m_drcRun )
1444 {
1446 msg.Replace( wxT( "%s" ), _( "not run" ) );
1447 }
1448 else
1449 {
1451 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1452 }
1453
1454 m_Notebook->SetPageText( 2, msg );
1455
1456 if( m_drcRun )
1457 {
1458 num.Printf( wxT( "%d" ), m_ignoredList->GetItemCount() );
1459 msg.sprintf( m_ignoredTitleTemplate, num );
1460 }
1461 else
1462 {
1464 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1465 }
1466
1467 m_Notebook->SetPageText( 3, msg );
1468
1469 // Update badges:
1470
1471 if( !m_drcRun && numErrors == 0 )
1472 numErrors = -1;
1473
1474 if( !m_drcRun && numWarnings == 0 )
1475 numWarnings = -1;
1476
1477 m_errorsBadge->SetMaximumNumber( numErrors );
1478 m_errorsBadge->UpdateNumber( errorsOverflowed ? numErrors + 1 : numErrors, RPT_SEVERITY_ERROR );
1479
1480 m_warningsBadge->SetMaximumNumber( numWarnings );
1481 m_warningsBadge->UpdateNumber( warningsOverflowed ? numWarnings + 1 : numWarnings, RPT_SEVERITY_WARNING );
1482
1483 m_exclusionsBadge->SetMaximumNumber( numExcluded );
1484 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
1485}
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:982
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:1359
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1382
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1780
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)
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
THROTTLE m_yieldThrottle
Definition dialog_drc.h:140
void OnDRCItemDClick(wxDataViewEvent &aEvent) override
THROTTLE m_updateThrottle
Definition dialog_drc.h:139
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:143
DRC_RULE * GetViolatingRule() const
Definition drc_item.h:162
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:127
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:137
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:212
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:120
@ 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:122
@ 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.