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