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 HYPERLINK_DV_RENDERER* hl = dynamic_cast<HYPERLINK_DV_RENDERER*>( col->GetRenderer() );
259
260 if( !hl )
261 return false;
262
263 wxVariant value;
264 wxDataViewItemAttr attr;
265
266 model->GetValue( value, item, col->GetModelColumn() );
267 model->GetAttr( item, col->GetModelColumn(), attr );
268
269 wxRect cellRect = aCtrl->GetItemRect( item, col );
270
271 return hl->HitTestRunsForCell( value.GetString(), attr, cellRect, aPoint, aHref );
272}
273
274
276{
277 UpdateData();
278 return true;
279}
280
281
282void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
283{
284 if( m_currentBoard != m_frame->GetBoard() )
285 {
286 // If m_currentBoard is not the current board, (for instance because a new board was loaded),
287 // close the dialog, because many pointers are now invalid in lists
288 SetReturnCode( wxID_CANCEL );
289 Close();
290
291 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
292 drcTool->DestroyDRCDialog();
293
294 return;
295 }
296
297 // Let DIALOG_SHIM re-establish keyboard focus so ESC keeps closing the dialog.
298 aEvent.Skip();
299}
300
301
302// PROGRESS_REPORTER calls
303
305{
306 if( m_maxProgress != 0 )
307 {
308 double cur = std::clamp( (double) m_progress.load() / m_maxProgress, 0.0, 1.0 );
309
310 int newValue = KiROUND( cur * 1000.0 );
311 m_gauge->SetValue( newValue );
312 }
313
315 {
316 int elapsed = static_cast<int>(
317 std::chrono::duration_cast<std::chrono::seconds>( std::chrono::steady_clock::now() - m_drcStartTime )
318 .count() );
319
320 if( elapsed != m_lastTickSeconds )
321 {
322 m_lastTickSeconds = elapsed;
323
324 wxString tick;
325
326 if( elapsed >= 60 )
327 tick = wxString::Format( wxT( "%1$d min %2$d s" ), elapsed / 60, elapsed % 60 );
328 else
329 tick = wxString::Format( wxT( "%d s" ), elapsed );
330
331 m_drcStatusBar->SetStatusText( wxString::Format( _( "Elapsed: %s" ), tick ), 1 );
332 }
333 }
334
335 // Repaint the dialog at ~10Hz using Update() which processes only pending expose/
336 // draw events without entering the full platform event loop.
337 if( m_updateThrottle.Ready() )
338 Update();
339
340 // Yield to the event loop infrequently so the cancel button remains functional.
341 // On some Linux systems with glycin-enabled gdk-pixbuf (2.44+), entering the GTK event
342 // loop triggers heavyweight sandbox process spawning that can add hundreds of milliseconds
343 // per call, so we keep this interval long.
344 if( m_yieldThrottle.Ready() )
345 Pgm().App().SafeYieldFor( this, wxEVT_CATEGORY_NATIVE_EVENTS );
346
347 return !m_cancelled;
348}
349
350
351void DIALOG_DRC::AdvancePhase( const wxString& aMessage )
352{
354 SetCurrentProgress( 0.0 );
355
356 m_messages->Report( aMessage );
357}
358
359
361{
362 int severities = 0;
363
364 if( m_showErrors->GetValue() )
365 severities |= RPT_SEVERITY_ERROR;
366
367 if( m_showWarnings->GetValue() )
368 severities |= RPT_SEVERITY_WARNING;
369
370 if( m_showExclusions->GetValue() )
371 severities |= RPT_SEVERITY_EXCLUSION;
372
373 return severities;
374}
375
376
377void DIALOG_DRC::OnMenu( wxCommandEvent& event )
378{
379 // Build a pop menu:
380 wxMenu menu;
381
382 menu.Append( 4205, _( "Report All Errors for Each Track" ),
383 _( "If unchecked, only the first error will be reported for each track" ),
384 wxITEM_CHECK );
385 menu.Check( 4205, m_report_all_track_errors );
386
387 menu.AppendSeparator();
388
389 menu.Append( 4206, _( "Cross-probe Selected Items" ),
390 _( "Highlight corresponding items on canvas when selected in the DRC list" ),
391 wxITEM_CHECK );
392 menu.Check( 4206, m_crossprobe );
393
394 menu.Append( 4207, _( "Center on Cross-probe" ),
395 _( "When cross-probing, scroll the canvas so that the item is visible" ),
396 wxITEM_CHECK );
397 menu.Check( 4207, m_scroll_on_crossprobe );
398
399 // menu_id is the selected submenu id from the popup menu or wxID_NONE
400 int menu_id = m_bMenu->GetPopupMenuSelectionFromUser( menu );
401
402 if( menu_id == 0 || menu_id == 4205 )
403 {
405 }
406 else if( menu_id == 2 || menu_id == 4206 )
407 {
409 }
410 else if( menu_id == 3 || menu_id == 4207 )
411 {
413 }
414}
415
416
417void DIALOG_DRC::OnErrorLinkClicked( wxHtmlLinkEvent& event )
418{
419 m_frame->ShowBoardSetupDialog( _( "Custom Rules" ), this );
420}
421
422
423void DIALOG_DRC::OnCharHook( wxKeyEvent& aEvt )
424{
425 if( int hotkey = aEvt.GetKeyCode() )
426 {
427 if( aEvt.ControlDown() )
428 hotkey |= MD_CTRL;
429 if( aEvt.ShiftDown() )
430 hotkey |= MD_SHIFT;
431 if( aEvt.AltDown() )
432 hotkey |= MD_ALT;
433
434 if( hotkey == ACTIONS::excludeMarker.GetHotKey() )
435 {
437 return;
438 }
439 }
440
442}
443
444
445void DIALOG_DRC::OnRunDRCClick( wxCommandEvent& aEvent )
446{
447 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
448 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
449 ZONE_FILLER_TOOL* zoneFillerTool = toolMgr->GetTool<ZONE_FILLER_TOOL>();
450 bool refillZones = m_cbRefillZones->GetValue();
451 bool testFootprints = m_cbTestFootprints->GetValue();
452
453 if( zoneFillerTool->IsBusy() )
454 {
455 wxBell();
456 return;
457 }
458
459 m_footprintTestsRun = false;
460 m_cancelled = false;
461
462 m_frame->GetBoard()->RecordDRCExclusions();
463 deleteAllMarkers( true );
464
465 // This is not the time to have stale or buggy rules. Ensure they're up-to-date
466 // and that they at least parse.
467 try
468 {
469 drcTool->GetDRCEngine()->InitEngine( m_frame->GetBoard()->GetDesignRulesPath() );
470 }
471 catch( PARSE_ERROR& )
472 {
473 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
474 m_DeleteCurrentMarkerButton->Enable( false );
475 m_DeleteAllMarkersButton->Enable( false );
476 m_saveReport->Enable( false );
477
478 m_messages->Clear();
479 m_messages->Report( _( "DRC incomplete: could not compile custom design rules." )
480 + wxS( "&nbsp;&nbsp;" )
481 + wxS( "<a href='$CUSTOM_RULES'>" ) + _( "Show design rules." ) + wxT( "</a>" ) );
482 m_messages->Flush();
483
484 Raise();
485
486 // set float level again, it can be lost due to window events during test run
488 return;
489 }
490
491 std::vector<std::reference_wrapper<RC_ITEM>> violations = DRC_ITEM::GetItemsWithSeverities();
492 m_ignoredList->DeleteAllItems();
493
494 for( std::reference_wrapper<RC_ITEM>& item : violations )
495 {
496 if( bds().GetSeverity( item.get().GetErrorCode() ) == RPT_SEVERITY_IGNORE )
497 {
498 wxListItem listItem;
499 listItem.SetId( m_ignoredList->GetItemCount() );
500 listItem.SetText( wxT( " • " ) + item.get().GetErrorText( true ) );
501 listItem.SetData( item.get().GetErrorCode() );
502
503 m_ignoredList->InsertItem( listItem );
504 }
505 }
506
507 m_ignoredList->SetColumnWidth( 0, m_ignoredList->GetParent()->GetClientSize().x - 20 );
508
509 Raise();
510
511 m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
512 m_messages->Clear();
513 Update(); // Repaint only, don't enter the full event loop
514
515 m_running = true;
516 m_sdbSizerCancel->SetLabel( _( "Cancel" ) );
517 m_sdbSizerOK->Enable( false );
518 m_DeleteCurrentMarkerButton->Enable( false );
519 m_DeleteAllMarkersButton->Enable( false );
520 m_saveReport->Enable( false );
521
522 m_drcStartTime = std::chrono::steady_clock::now();
524
525 if( m_drcStatusBar )
526 m_drcStatusBar->SetStatusText( _( "Elapsed: 0 s" ), 1 );
527
528 {
529 wxBusyCursor dummy;
530 drcTool->RunTests( this, refillZones, m_report_all_track_errors, testFootprints );
531 }
532
533 double elapsedMs =
534 std::chrono::duration<double, std::milli>( std::chrono::steady_clock::now() - m_drcStartTime ).count();
535
536 auto formatElapsed =
537 [&]() -> wxString
538 {
539 int totalSeconds = KiROUND( elapsedMs / 1000.0 );
540
541 if( totalSeconds >= 60 )
542 return wxString::Format( _( "%1$d min %2$d s" ), totalSeconds / 60, totalSeconds % 60 );
543
544 return wxString::Format( _( "%.2f s" ), elapsedMs / 1000.0 );
545 };
546
547 if( m_cancelled )
548 {
549 m_messages->Report( _( "-------- DRC canceled by user.<br><br>" ) );
550
551 if( m_drcStatusBar )
552 m_drcStatusBar->SetStatusText( wxString::Format( _( "Canceled after %s" ), formatElapsed() ), 1 );
553 }
554 else
555 {
556 m_messages->Report( _( "Done.<br><br>" ) );
557
558 if( m_drcStatusBar )
559 m_drcStatusBar->SetStatusText( wxString::Format( _( "Completed in %s" ), formatElapsed() ), 1 );
560 }
561
562 Raise();
563 Update(); // Repaint only, don't enter the full event loop
564
565 m_running = false;
566 m_sdbSizerCancel->SetLabel( _( "Close" ) );
567 m_sdbSizerOK->Enable( true );
568 m_DeleteCurrentMarkerButton->Enable( true );
569 m_DeleteAllMarkersButton->Enable( true );
570 m_saveReport->Enable( true );
571
572 if( !m_cancelled )
573 {
574 m_sdbSizerCancel->SetDefault();
575 // wxWidgets has a tendency to keep both buttons highlighted without the following:
576 m_sdbSizerOK->Enable( false );
577
578 wxMilliSleep( 500 );
579 m_runningResultsBook->ChangeSelection( 1 );
581
582 // now re-enable m_sdbSizerOK button
583 m_sdbSizerOK->Enable( true );
584 }
585
586 // set float level again, it can be lost due to window events during test run
589}
590
591
593{
594 int severities = getSeverities();
595
596 m_markersTreeModel->Update( m_markersProvider, severities );
597 m_unconnectedTreeModel->Update( m_ratsnestProvider, severities );
598 m_fpWarningsTreeModel->Update( m_fpWarningsProvider, severities );
599
601}
602
603
604void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
605{
606 if( !m_crossprobe )
607 {
608 aEvent.Skip();
609 return;
610 }
611
612 BOARD* board = m_frame->GetBoard();
613 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
614
615 if( !node )
616 {
617 // list is being freed; don't do anything with null ptrs
618
619 aEvent.Skip();
620 return;
621 }
622
623 std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
624
625 // The tree keeps its RC_ITEMs alive independently of the board, so rc_item->GetParent()
626 // can dangle once the owning marker is deleted (board edited, DRC re-run, undo) while this
627 // modeless dialog stays open. Recover the still-live marker by matching the shared RC_ITEM
628 // against the board's current markers instead of trusting the raw back-pointer.
629 PCB_MARKER* parentMarker = nullptr;
630
631 for( PCB_MARKER* marker : board->Markers() )
632 {
633 if( marker->GetRCItem() == rc_item )
634 {
635 parentMarker = marker;
636 break;
637 }
638 }
639
640 if( rc_item->GetErrorCode() == DRCE_UNRESOLVED_VARIABLE && parentMarker
642 {
643 m_frame->FocusOnLocation( parentMarker->GetPos(), m_scroll_on_crossprobe );
644
645 aEvent.Skip();
646 return;
647 }
648
649 const KIID& itemID = RC_TREE_MODEL::ToUUID( aEvent.GetItem() );
650 BOARD_ITEM* item = board->ResolveItem( itemID, true );
651
652 if( !item )
653 {
654 aEvent.Skip();
655 return;
656 }
657
658 PCB_LAYER_ID principalLayer;
659 LSET violationLayers;
660 BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
661 BOARD_ITEM* b = board->ResolveItem( rc_item->GetAuxItemID(), true );
662
663 auto focus = [&]( BOARD_ITEM* aItem )
664 {
665 std::vector<BOARD_ITEM*> items = { aItem };
666
667 if( parentMarker && parentMarker != aItem )
668 items.push_back( parentMarker );
669
670 m_frame->FocusOnItems( items, principalLayer, m_scroll_on_crossprobe );
671 };
672
673 DRC_ITEM::GetViolationLayers( board, rc_item, parentMarker, principalLayer, violationLayers );
674
675 WINDOW_THAWER thawer( m_frame );
676
677 if( principalLayer > UNDEFINED_LAYER && ( violationLayers & board->GetVisibleLayers() ).none() )
678 m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
679
680 if( principalLayer > UNDEFINED_LAYER && board->GetVisibleLayers().test( principalLayer ) )
681 m_frame->SetActiveLayer( principalLayer );
682
683 if( rc_item->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
684 {
685 if( !m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest )
686 m_frame->GetToolManager()->RunAction( PCB_ACTIONS::showRatsnest );
687
688 if( item->Type() == PCB_ZONE_T )
689 {
690 focus( item );
691
692 m_frame->GetBoard()->GetConnectivity()->RunOnUnconnectedEdges(
693 [&]( CN_EDGE& edge )
694 {
695 // Connectivity was valid when DRC was run, but this is a modeless dialog
696 // so it might not be now.
697 if( !edge.GetSourceNode() || edge.GetSourceNode()->Dirty() )
698 return true;
699
700 if( !edge.GetTargetNode() || edge.GetTargetNode()->Dirty() )
701 return true;
702
703 if( edge.GetSourceNode()->Parent() == a
704 && edge.GetTargetNode()->Parent() == b )
705 {
706 VECTOR2I focusPos;
707
708 if( item == a && item == b )
709 {
710 focusPos = ( node->m_Type == RC_TREE_NODE::MAIN_ITEM ) ? edge.GetSourcePos()
711 : edge.GetTargetPos();
712 }
713 else
714 {
715 focusPos = ( item == edge.GetSourceNode()->Parent() ) ? edge.GetSourcePos()
716 : edge.GetTargetPos();
717 }
718
719 m_frame->FocusOnLocation( focusPos, m_scroll_on_crossprobe );
720 m_frame->RefreshCanvas();
721
722 return false;
723 }
724
725 return true;
726 } );
727 }
728 else
729 {
730 focus( item );
731 }
732 }
733 else if( rc_item->GetErrorCode() == DRCE_DP_UNCOUPLED_LENGTH_TOO_LONG )
734 {
735 BOARD_CONNECTED_ITEM* track = dynamic_cast<PCB_TRACK*>( item );
736 std::vector<BOARD_ITEM*> items;
737
738 if( track )
739 {
740 int net = track->GetNetCode();
741
742 wxASSERT( net > 0 ); // Without a net how can it be a diff-pair?
743
744 for( const KIID& id : rc_item->GetIDs() )
745 {
746 auto* candidate = dynamic_cast<BOARD_CONNECTED_ITEM*>( board->ResolveItem( id, true ) );
747
748 if( candidate && candidate->GetNetCode() == net )
749 items.push_back( candidate );
750 }
751 }
752 else
753 {
754 items.push_back( item );
755 }
756
757 if( parentMarker && std::find( items.begin(), items.end(), parentMarker ) == items.end() )
758 {
759 items.push_back( parentMarker );
760 }
761
762 m_frame->FocusOnItems( items, principalLayer, m_scroll_on_crossprobe );
763 }
764 else
765 {
766 focus( item );
767 }
768
769 aEvent.Skip();
770}
771
772
773void DIALOG_DRC::OnDRCItemDClick( wxDataViewEvent& aEvent )
774{
775 if( aEvent.GetItem().IsOk() )
776 {
777 // turn control over to m_frame, hide this DIALOG_DRC window,
778 // no destruction so we can preserve listbox cursor
779 if( !IsModal() )
780 Show( false );
781 }
782
783 // Do not skip aEvent here: this is not useful, and Pcbnew crashes if skipped (at least on MSW)
784}
785
786
787void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
788{
789 TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
790 BOARD_INSPECTION_TOOL* inspectionTool = toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
791 DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
792 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
793
794 if( !node )
795 return;
796
797 std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
798 DRC_ITEM* drcItem = static_cast<DRC_ITEM*>( rcItem.get() );
799 std::shared_ptr<CONNECTIVITY_DATA> conn = m_currentBoard->GetConnectivity();
800 wxString listName;
801 wxMenu menu;
802
803 switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] )
804 {
805 case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
806 case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
807 default: listName = _( "appropriate" ); break;
808 }
809
810 enum MENU_IDS
811 {
812 ID_EDIT_EXCLUSION_COMMENT = 4467,
813 ID_REMOVE_EXCLUSION,
814 ID_REMOVE_EXCLUSION_ALL,
815 ID_ADD_EXCLUSION,
816 ID_ADD_EXCLUSION_WITH_COMMENT,
817 ID_ADD_EXCLUSION_ALL,
818 ID_INSPECT_VIOLATION,
819 ID_FIX_VIOLATION,
820 ID_SET_SEVERITY_TO_ERROR,
821 ID_SET_SEVERITY_TO_WARNING,
822 ID_SET_SEVERITY_TO_IGNORE,
823 ID_EDIT_SEVERITIES
824 };
825
826 if( rcItem->GetParent()->IsExcluded() )
827 {
828 menu.Append( ID_REMOVE_EXCLUSION,
829 _( "Remove exclusion for this violation" ),
830 wxString::Format( _( "It will be placed back in the %s list" ), listName ) );
831
832 menu.Append( ID_EDIT_EXCLUSION_COMMENT,
833 _( "Edit exclusion comment..." ) );
834
835 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
836 {
837 menu.Append( ID_REMOVE_EXCLUSION_ALL,
838 wxString::Format( _( "Remove all exclusions for violations of rule '%s'" ),
839 drcItem->GetViolatingRule()->m_Name ),
840 wxString::Format( _( "They will be placed back in the %s list" ), listName ) );
841 }
842 }
843 else
844 {
845 menu.Append( ID_ADD_EXCLUSION,
846 _( "Exclude this violation" ),
847 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
848
849 menu.Append( ID_ADD_EXCLUSION_WITH_COMMENT,
850 _( "Exclude with comment..." ),
851 wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
852
853 if( drcItem->GetViolatingRule() && !drcItem->GetViolatingRule()->IsImplicit() )
854 {
855 menu.Append( ID_ADD_EXCLUSION_ALL,
856 wxString::Format( _( "Exclude all violations of rule '%s'..." ),
857 drcItem->GetViolatingRule()->m_Name ),
858 wxString::Format( _( "They will be excluded from the %s list" ), listName ) );
859 }
860 }
861
862 menu.AppendSeparator();
863
864 wxString inspectDRCErrorMenuText = inspectionTool->InspectDRCErrorMenuText( rcItem );
865 wxString fixDRCErrorMenuText = drcTool->FixDRCErrorMenuText( rcItem );
866
867 if( !inspectDRCErrorMenuText.IsEmpty() || !fixDRCErrorMenuText.IsEmpty() )
868 {
869 if( !inspectDRCErrorMenuText.IsEmpty() )
870 menu.Append( ID_INSPECT_VIOLATION, inspectDRCErrorMenuText );
871
872 if( !fixDRCErrorMenuText.IsEmpty() )
873 menu.Append( ID_FIX_VIOLATION, fixDRCErrorMenuText );
874
875 menu.AppendSeparator();
876 }
877
878 if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
879 {
880 menu.Append( ID_SET_SEVERITY_TO_ERROR,
881 wxString::Format( _( "Change severity to Error for all '%s' violations" ),
882 rcItem->GetErrorText( true ) ),
883 _( "Violation severities can also be edited in Board Setup" ) );
884 }
885 else
886 {
887 menu.Append( ID_SET_SEVERITY_TO_WARNING,
888 wxString::Format( _( "Change severity to Warning for all '%s' violations" ),
889 rcItem->GetErrorText( true ) ),
890 _( "Violation severities can also be edited in Board Setup" ) );
891 }
892
893 menu.Append( ID_SET_SEVERITY_TO_IGNORE,
894 wxString::Format( _( "Ignore all '%s' violations" ), rcItem->GetErrorText( true ) ),
895 _( "Violations will not be checked or reported" ) );
896
897 menu.AppendSeparator();
898
899 menu.Append( ID_EDIT_SEVERITIES,
900 _( "Edit violation severities..." ),
901 _( "Open the Board Setup dialog" ) );
902
903 bool modified = false;
904 int command = GetPopupMenuSelectionFromUser( menu );
905
906 switch( command )
907 {
908 case ID_EDIT_EXCLUSION_COMMENT:
909 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() ) )
910 {
911 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), marker->GetComment(), true );
912
913 if( dlg.ShowModal() == wxID_CANCEL )
914 break;
915
916 marker->SetExcluded( true, dlg.GetValue() );
917
918 DRC_EXCLUSION exclusion = DRC_EXCLUSION::FromMarker( *marker );
919 exclusion.SetComment( dlg.GetValue() );
920 bds().m_DrcExclusions.erase( exclusion );
921 bds().m_DrcExclusions.insert( exclusion );
922
923 // Update view
924 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
925 modified = true;
926 }
927
928 break;
929
930 case ID_REMOVE_EXCLUSION:
931 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
932 {
933 marker->SetExcluded( false );
934 bds().m_DrcExclusions.erase( DRC_EXCLUSION::FromMarker( *marker ) );
935
936 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
937 {
938 m_frame->GetBoard()->UpdateRatsnestExclusions();
939 m_frame->GetCanvas()->RedrawRatsnest();
940 }
941 else
942 {
943 m_frame->GetCanvas()->GetView()->Update( marker );
944 }
945
946 // Update view
947 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
948 modified = true;
949 }
950
951 break;
952
953 case ID_ADD_EXCLUSION:
954 case ID_ADD_EXCLUSION_WITH_COMMENT:
955 if( PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( rcItem->GetParent() ) )
956 {
957 wxString comment;
958
959 if( command == ID_ADD_EXCLUSION_WITH_COMMENT )
960 {
961 WX_TEXT_ENTRY_DIALOG dlg( this, wxEmptyString, _( "Exclusion Comment" ), wxEmptyString, true );
962
963 if( dlg.ShowModal() == wxID_CANCEL )
964 break;
965
966 comment = dlg.GetValue();
967 }
968
969 marker->SetExcluded( true, comment );
970
971 DRC_EXCLUSION exclusion = DRC_EXCLUSION::FromMarker( *marker );
972 exclusion.SetComment( comment );
973 bds().m_DrcExclusions.insert( exclusion );
974
975 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
976 {
977 m_frame->GetBoard()->UpdateRatsnestExclusions();
978 m_frame->GetCanvas()->RedrawRatsnest();
979 }
980 else
981 {
982 m_frame->GetCanvas()->GetView()->Update( marker );
983 }
984
985 // Update view
986 if( m_showExclusions->GetValue() )
987 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
988 else
989 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
990
991 modified = true;
992 }
993
994 break;
995
996 case ID_REMOVE_EXCLUSION_ALL:
997 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
998 {
999 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
1000
1001 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
1002 {
1003 marker->SetExcluded( false );
1004 bds().m_DrcExclusions.erase( DRC_EXCLUSION::FromMarker( *marker ) );
1005 }
1006 }
1007
1008 // Rebuild model and view
1009 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1010 modified = true;
1011 break;
1012
1013 case ID_ADD_EXCLUSION_ALL:
1014 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1015 {
1016 DRC_ITEM* candidateDrcItem = static_cast<DRC_ITEM*>( marker->GetRCItem().get() );
1017
1018 if( candidateDrcItem->GetViolatingRule() == drcItem->GetViolatingRule() )
1019 {
1020 marker->SetExcluded( true );
1021 bds().m_DrcExclusions.insert( DRC_EXCLUSION::FromMarker( *marker ) );
1022 }
1023 }
1024
1025 // Rebuild model and view
1026 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1027 modified = true;
1028 break;
1029
1030 case ID_INSPECT_VIOLATION:
1031 inspectionTool->InspectDRCError( node->m_RcItem );
1032 break;
1033
1034 case ID_FIX_VIOLATION:
1035 drcTool->FixDRCError( node->m_RcItem );
1036 break;
1037
1038 case ID_SET_SEVERITY_TO_ERROR:
1039 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
1040
1041 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1042 {
1043 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1044 m_frame->GetCanvas()->GetView()->Update( marker );
1045 }
1046
1047 // Rebuild model and view
1048 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1049 modified = true;
1050 break;
1051
1052 case ID_SET_SEVERITY_TO_WARNING:
1053 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
1054
1055 for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
1056 {
1057 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1058 m_frame->GetCanvas()->GetView()->Update( marker );
1059 }
1060
1061 // Rebuild model and view
1062 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1063 modified = true;
1064 break;
1065
1066 case ID_SET_SEVERITY_TO_IGNORE:
1067 {
1068 bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
1069
1070 wxListItem listItem;
1071 listItem.SetId( m_ignoredList->GetItemCount() );
1072 listItem.SetText( wxT( " • " ) + rcItem->GetErrorText( true ) );
1073 listItem.SetData( rcItem->GetErrorCode() );
1074
1075 m_ignoredList->InsertItem( listItem );
1076
1077 BOARD* board = m_frame->GetBoard();
1078
1079 std::vector<BOARD_ITEM*> toRemove;
1080
1081 for( PCB_MARKER* marker : board->Markers() )
1082 {
1083 if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
1084 {
1085 m_frame->GetCanvas()->GetView()->Remove( marker );
1086 toRemove.emplace_back( marker );
1087 }
1088 }
1089
1090 for( BOARD_ITEM* marker : toRemove )
1091 board->Remove( marker, REMOVE_MODE::BULK );
1092
1093 board->FinalizeBulkRemove( toRemove );
1094
1095 if( rcItem->GetErrorCode() == DRCE_UNCONNECTED_ITEMS )
1096 m_frame->GetCanvas()->RedrawRatsnest();
1097
1098 // Rebuild model and view
1099 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1100 modified = true;
1101 break;
1102 }
1103
1104 case ID_EDIT_SEVERITIES:
1105 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1106 break;
1107 }
1108
1109 if( modified )
1110 {
1112 refreshEditor();
1113 m_frame->OnModify();
1114 }
1115}
1116
1117
1118void DIALOG_DRC::OnIgnoredItemRClick( wxListEvent& event )
1119{
1120 int errorCode = (int) event.m_item.GetData();
1121 wxMenu menu;
1122
1123 menu.Append( RPT_SEVERITY_ERROR, _( "Error" ), wxEmptyString, wxITEM_RADIO );
1124 menu.Append( RPT_SEVERITY_WARNING, _( "Warning" ), wxEmptyString, wxITEM_RADIO );
1125 menu.Append( RPT_SEVERITY_IGNORE, _( "Ignore" ), wxEmptyString, wxITEM_RADIO );
1126
1127 menu.Check( bds().GetSeverity( errorCode ), true );
1128
1129 int severity = GetPopupMenuSelectionFromUser( menu );
1130
1131 if( severity > 0 )
1132 {
1133 if( bds().m_DRCSeverities[ errorCode ] != severity )
1134 {
1135 bds().m_DRCSeverities[ errorCode ] = (SEVERITY) severity;
1136
1138 refreshEditor();
1139 m_frame->OnModify();
1140 }
1141 }
1142}
1143
1144
1145void DIALOG_DRC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
1146{
1147 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1148}
1149
1150
1151void DIALOG_DRC::OnSeverity( wxCommandEvent& aEvent )
1152{
1153 if( aEvent.GetEventObject() == m_showAll )
1154 {
1155 m_showErrors->SetValue( true );
1156 m_showWarnings->SetValue( aEvent.IsChecked() );
1157 m_showExclusions->SetValue( aEvent.IsChecked() );
1158 }
1159
1160 UpdateData();
1161}
1162
1163
1164void DIALOG_DRC::OnSaveReport( wxCommandEvent& aEvent )
1165{
1166 wxFileName fn( "DRC." + FILEEXT::ReportFileExtension );
1167
1168 wxFileDialog dlg( this, _( "Save Report File" ), Prj().GetProjectPath(), fn.GetFullName(),
1170 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1171
1173
1174 if( dlg.ShowModal() != wxID_OK )
1175 return;
1176
1177 fn = dlg.GetPath();
1178
1179 if( fn.GetExt().IsEmpty() )
1180 fn.SetExt( FILEEXT::ReportFileExtension );
1181
1182 if( !fn.IsAbsolute() )
1183 {
1184 wxString prj_path = Prj().GetProjectPath();
1185 fn.MakeAbsolute( prj_path );
1186 }
1187
1188 DRC_REPORT reportWriter( m_frame->GetBoard(), GetUserUnits(), m_markersProvider,
1190
1191 bool success = false;
1192 if( fn.GetExt() == FILEEXT::JsonFileExtension )
1193 success = reportWriter.WriteJsonReport( fn.GetFullPath() );
1194 else
1195 success = reportWriter.WriteTextReport( fn.GetFullPath() );
1196
1197 if( success )
1198 m_messages->Report( wxString::Format( _( "Report file '%s' created<br>" ), fn.GetFullPath() ) );
1199 else
1200 DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ), fn.GetFullPath() ) );
1201}
1202
1203
1204void DIALOG_DRC::OnClose( wxCloseEvent& aEvent )
1205{
1206 wxCommandEvent dummy;
1208}
1209
1210
1211void DIALOG_DRC::OnCancelClick( wxCommandEvent& aEvent )
1212{
1213 if( m_running )
1214 {
1215 m_cancelled = true;
1216 return;
1217 }
1218
1219 m_frame->ClearFocus();
1220
1221 SetReturnCode( wxID_CANCEL );
1222
1223 // The dialog can be modal or not modal.
1224 // Leave the DRC caller destroy (or not) the dialog
1225 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1226 drcTool->DestroyDRCDialog();
1227}
1228
1229
1230void DIALOG_DRC::OnChangingNotebookPage( wxNotebookEvent& aEvent )
1231{
1232 m_markerDataView->UnselectAll();
1233 m_unconnectedDataView->UnselectAll();
1234 m_footprintsDataView->UnselectAll();
1235
1236 aEvent.Skip();
1237}
1238
1239
1241{
1242 WINDOW_THAWER thawer( m_frame );
1243
1244 m_frame->GetCanvas()->Refresh();
1245}
1246
1247
1249{
1250 if( m_Notebook->IsShown() )
1251 {
1252 switch( m_Notebook->GetSelection() )
1253 {
1254 case 0: m_markersTreeModel->PrevMarker(); break;
1255 case 1: m_unconnectedTreeModel->PrevMarker(); break;
1256 case 2: m_fpWarningsTreeModel->PrevMarker(); break;
1257 case 3: break;
1258 }
1259 }
1260}
1261
1262
1264{
1265 if( m_Notebook->IsShown() )
1266 {
1267 switch( m_Notebook->GetSelection() )
1268 {
1269 case 0: m_markersTreeModel->NextMarker(); break;
1270 case 1: m_unconnectedTreeModel->NextMarker(); break;
1271 case 2: m_fpWarningsTreeModel->NextMarker(); break;
1272 case 3: break;
1273 }
1274 }
1275}
1276
1277
1279{
1280 if( m_Notebook->IsShown() )
1281 {
1282 enum MARKER_BASE::MARKER_T markerType = aMarker->GetMarkerType();
1283
1284 if( markerType == MARKER_BASE::MARKER_DRC )
1285 m_Notebook->SetSelection( 0 );
1286 else if( markerType == MARKER_BASE::MARKER_PARITY )
1287 m_Notebook->SetSelection( 2 );
1288
1289 m_markersTreeModel->SelectMarker( aMarker );
1290
1291 CallAfter(
1292 [this, aMarker]
1293 {
1294 m_markersTreeModel->CenterMarker( aMarker );
1295 } );
1296 }
1297}
1298
1299
1301{
1302 if( !m_Notebook->IsShown() || m_Notebook->GetSelection() != 0 )
1303 return;
1304
1305 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
1306
1307 if( node && node->m_RcItem )
1308 {
1309 PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
1310
1311 if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
1312 {
1313 marker->SetExcluded( true );
1314 bds().m_DrcExclusions.insert( DRC_EXCLUSION::FromMarker( *marker ) );
1315 m_frame->GetCanvas()->GetView()->Update( marker );
1316
1317 // Update view
1318 if( m_showExclusions->GetValue() )
1319 m_markersTreeModel->ValueChanged( node );
1320 else
1321 m_markersTreeModel->DeleteCurrentItem( false );
1322
1324 refreshEditor();
1325 m_frame->OnModify();
1326 }
1327 }
1328}
1329
1330
1331void DIALOG_DRC::deleteAllMarkers( bool aIncludeExclusions )
1332{
1333 // Clear current selection list to avoid selection of deleted items
1334 Freeze();
1335 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1336
1337 m_markersTreeModel->DeleteItems( false, aIncludeExclusions, false );
1338 m_unconnectedTreeModel->DeleteItems( false, aIncludeExclusions, false );
1339 m_fpWarningsTreeModel->DeleteItems( false, aIncludeExclusions, false );
1340
1341 m_frame->GetBoard()->DeleteMARKERs( true, aIncludeExclusions );
1342 Thaw();
1343}
1344
1345
1346void DIALOG_DRC::OnDeleteOneClick( wxCommandEvent& aEvent )
1347{
1348 if( m_Notebook->GetSelection() == 0 )
1349 {
1350 // Clear the selection. It may be the selected DRC marker.
1351 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1352
1353 m_markersTreeModel->DeleteCurrentItem( true );
1354
1355 // redraw the pcb
1356 refreshEditor();
1357 }
1358 else if( m_Notebook->GetSelection() == 1 )
1359 {
1360 m_unconnectedTreeModel->DeleteCurrentItem( true );
1361 }
1362 else if( m_Notebook->GetSelection() == 2 )
1363 {
1364 m_fpWarningsTreeModel->DeleteCurrentItem( true );
1365 }
1366
1368}
1369
1370
1371void DIALOG_DRC::OnDeleteAllClick( wxCommandEvent& aEvent )
1372{
1373 static bool s_includeExclusions = false;
1374
1375 int numExcluded = 0;
1376
1377 if( m_markersProvider )
1378 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1379
1380 if( m_ratsnestProvider )
1381 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1382
1384 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1385
1386 if( numExcluded > 0 )
1387 {
1388 wxMessageDialog dlg( this, _( "Delete exclusions too?" ), _( "Delete All Markers" ),
1389 wxYES_NO | wxCANCEL | wxCENTER | wxICON_QUESTION );
1390 dlg.SetYesNoLabels( _( "Errors and Warnings Only" ),
1391 _( "Errors, Warnings and Exclusions" ) );
1392
1393 int ret = dlg.ShowModal();
1394
1395 if( ret == wxID_CANCEL )
1396 return;
1397 else if( ret == wxID_NO )
1398 s_includeExclusions = true;
1399 }
1400
1401 deleteAllMarkers( s_includeExclusions );
1402 m_drcRun = false;
1403
1404 refreshEditor();
1406}
1407
1408
1410{
1411 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1412 DRC_ENGINE* drcEngine = drcTool->GetDRCEngine().get();
1413
1414 // Collect counts:
1415
1416 int numMarkers = 0;
1417 int numUnconnected = 0;
1418 int numFootprints = 0;
1419
1420 int numErrors = 0;
1421 int numWarnings = 0;
1422 int numExcluded = 0;
1423
1424 if( m_markersProvider )
1425 {
1426 numMarkers += m_markersProvider->GetCount();
1427 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
1428 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
1429 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1430 }
1431
1432 if( m_ratsnestProvider )
1433 {
1434 numUnconnected += m_ratsnestProvider->GetCount();
1435 numErrors += m_ratsnestProvider->GetCount( RPT_SEVERITY_ERROR );
1436 numWarnings += m_ratsnestProvider->GetCount( RPT_SEVERITY_WARNING );
1437 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1438 }
1439
1441 {
1442 numFootprints += m_fpWarningsProvider->GetCount();
1443 numErrors += m_fpWarningsProvider->GetCount( RPT_SEVERITY_ERROR );
1444 numWarnings += m_fpWarningsProvider->GetCount( RPT_SEVERITY_WARNING );
1445 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1446 }
1447
1448 bool errorsOverflowed = false;
1449 bool warningsOverflowed = false;
1450 bool markersOverflowed = false;
1451 bool unconnectedOverflowed = false;
1452 bool footprintsOverflowed = false;
1453
1454 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
1455 {
1456 const SEVERITY severity = bds().GetSeverity( ii );
1457
1458 if( drcEngine->IsErrorLimitExceeded( ii ) )
1459 {
1460 if( severity == RPT_SEVERITY_ERROR )
1461 errorsOverflowed = true;
1462 else if( severity == RPT_SEVERITY_WARNING )
1463 warningsOverflowed = true;
1464
1465 if( ii == DRCE_UNCONNECTED_ITEMS )
1466 {
1467 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1468 unconnectedOverflowed = true;
1469 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1470 unconnectedOverflowed = true;
1471 }
1472 else if( ii == DRCE_MISSING_FOOTPRINT
1474 || ii == DRCE_EXTRA_FOOTPRINT
1475 || ii == DRCE_NET_CONFLICT
1476 || ii == DRCE_SCHEMATIC_PARITY
1477 || ii == DRCE_FOOTPRINT_FILTERS
1479 {
1480 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1481 footprintsOverflowed = true;
1482 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1483 footprintsOverflowed = true;
1484 }
1485 else
1486 {
1487 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1488 markersOverflowed = true;
1489 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1490 markersOverflowed = true;
1491 }
1492 }
1493 }
1494
1495 wxString msg;
1496 wxString num;
1497
1498 // Update tab headers:
1499
1500 if( m_drcRun )
1501 {
1502 num.Printf( markersOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numMarkers );
1503 msg.Printf( m_markersTitleTemplate, num );
1504 }
1505 else
1506 {
1508 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1509 }
1510
1511 m_Notebook->SetPageText( 0, msg );
1512
1513 if( m_drcRun )
1514 {
1515 num.Printf( unconnectedOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numUnconnected );
1516 msg.sprintf( m_unconnectedTitleTemplate, num );
1517 }
1518 else
1519 {
1521 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1522 }
1523
1524 m_Notebook->SetPageText( 1, msg );
1525
1527 {
1528 num.Printf( footprintsOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numFootprints );
1529 msg.sprintf( m_footprintsTitleTemplate, num );
1530 }
1531 else if( m_drcRun )
1532 {
1534 msg.Replace( wxT( "%s" ), _( "not run" ) );
1535 }
1536 else
1537 {
1539 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1540 }
1541
1542 m_Notebook->SetPageText( 2, msg );
1543
1544 if( m_drcRun )
1545 {
1546 num.Printf( wxT( "%d" ), m_ignoredList->GetItemCount() );
1547 msg.sprintf( m_ignoredTitleTemplate, num );
1548 }
1549 else
1550 {
1552 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1553 }
1554
1555 m_Notebook->SetPageText( 3, msg );
1556
1557 // Update badges:
1558
1559 if( !m_drcRun && numErrors == 0 )
1560 numErrors = -1;
1561
1562 if( !m_drcRun && numWarnings == 0 )
1563 numWarnings = -1;
1564
1565 m_errorsBadge->SetMaximumNumber( numErrors );
1566 m_errorsBadge->UpdateNumber( errorsOverflowed ? numErrors + 1 : numErrors, RPT_SEVERITY_ERROR );
1567
1568 m_warningsBadge->SetMaximumNumber( numWarnings );
1569 m_warningsBadge->UpdateNumber( warningsOverflowed ? numWarnings + 1 : numWarnings, RPT_SEVERITY_WARNING );
1570
1571 m_exclusionsBadge->SetMaximumNumber( numExcluded );
1572 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
1573}
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:995
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< int, SEVERITY > m_DRCSeverities
SEVERITY GetSeverity(int aDRCErrorCode)
std::set< DRC_EXCLUSION, DRC_EXCLUSION_COMPARE > m_DrcExclusions
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:409
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1197
const MARKERS & Markers() const
Definition board.h:488
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:1640
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1668
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2116
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.
Container for an DRC exclusion, which is a PCB_MARKER plus an optional comment.
void SetComment(const wxString &aComment)
static DRC_EXCLUSION FromMarker(const PCB_MARKER &aMarker)
static std::vector< std::reference_wrapper< RC_ITEM > > GetItemsWithSeverities()
Definition drc_item.h:159
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:576
DRC_RULE * GetViolatingRule() const
Definition drc_item.h:178
bool WriteJsonReport(const wxString &aFullFileName)
bool WriteTextReport(const wxString &aFullFileName)
bool IsImplicit() const
Definition drc_rule.h:145
wxString m_Name
Definition drc_rule.h:155
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:110
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
virtual wxApp & App()
Return a bare naked wxApp which may come from wxPython, SINGLE_TOP, or kicad.exe.
Definition pgm_base.cpp:202
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:134
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition rc_item.h:267
void ValueChanged(RC_TREE_NODE *aNode)
Definition rc_item.cpp:623
void Update(std::shared_ptr< RC_ITEMS_PROVIDER > aProvider, int aSeverities)
Definition rc_item.cpp:425
void DeleteCurrentItem(bool aDeep)
Definition rc_item.cpp:674
void EnableHyperlinks(bool aEnable)
Render [label](url) markup as clickable links.
Definition rc_item.cpp:431
static KIID ToUUID(wxDataViewItem aItem)
Definition rc_item.cpp:223
std::shared_ptr< RC_ITEM > m_RcItem
Definition rc_item.h:251
NODE_TYPE m_Type
Definition rc_item.h:250
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:114
@ DRCE_FOOTPRINT_FILTERS
Definition drc_item.h:82
@ DRCE_UNCONNECTED_ITEMS
Definition drc_item.h:37
@ DRCE_SCHEMATIC_FIELDS_PARITY
Definition drc_item.h:126
@ DRCE_FIRST
Definition drc_item.h:36
@ DRCE_UNRESOLVED_VARIABLE
Definition drc_item.h:91
@ DRCE_DUPLICATE_FOOTPRINT
Definition drc_item.h:78
@ DRCE_EXTRA_FOOTPRINT
Definition drc_item.h:79
@ DRCE_LAST
Definition drc_item.h:131
@ DRCE_NET_CONFLICT
Definition drc_item.h:80
@ DRCE_MISSING_FOOTPRINT
Definition drc_item.h:77
@ DRCE_SCHEMATIC_PARITY
Definition drc_item.h:81
#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:100
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.