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
1107 // Rebuild model and view
1108 static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->Update( m_markersProvider, getSeverities() );
1110 break;
1111 }
1112
1113 if( modified )
1114 {
1116 refreshEditor();
1117 m_frame->OnModify();
1118 }
1119}
1120
1121
1122void DIALOG_DRC::OnIgnoredItemRClick( wxListEvent& event )
1123{
1124 int errorCode = (int) event.m_item.GetData();
1125 wxMenu menu;
1126
1127 menu.Append( RPT_SEVERITY_ERROR, _( "Error" ), wxEmptyString, wxITEM_RADIO );
1128 menu.Append( RPT_SEVERITY_WARNING, _( "Warning" ), wxEmptyString, wxITEM_RADIO );
1129 menu.Append( RPT_SEVERITY_IGNORE, _( "Ignore" ), wxEmptyString, wxITEM_RADIO );
1130
1131 menu.Check( bds().GetSeverity( errorCode ), true );
1132
1133 int severity = GetPopupMenuSelectionFromUser( menu );
1134
1135 if( severity > 0 )
1136 {
1137 if( bds().m_DRCSeverities[ errorCode ] != severity )
1138 {
1139 bds().m_DRCSeverities[ errorCode ] = (SEVERITY) severity;
1140 m_ignoredList->DeleteItem( event.m_itemIndex );
1141
1143 refreshEditor();
1144 m_frame->OnModify();
1145 }
1146 }
1147}
1148
1149
1150void DIALOG_DRC::OnEditViolationSeverities( wxHyperlinkEvent& aEvent )
1151{
1152 m_frame->ShowBoardSetupDialog( _( "Violation Severity" ), this );
1153}
1154
1155
1156void DIALOG_DRC::OnSeverity( wxCommandEvent& aEvent )
1157{
1158 if( aEvent.GetEventObject() == m_showAll )
1159 {
1160 m_showErrors->SetValue( true );
1161 m_showWarnings->SetValue( aEvent.IsChecked() );
1162 m_showExclusions->SetValue( aEvent.IsChecked() );
1163 }
1164
1165 UpdateData();
1166}
1167
1168
1169void DIALOG_DRC::OnSaveReport( wxCommandEvent& aEvent )
1170{
1171 wxFileName fn( "DRC." + FILEEXT::ReportFileExtension );
1172
1173 wxFileDialog dlg( this, _( "Save Report File" ), Prj().GetProjectPath(), fn.GetFullName(),
1175 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1176
1178
1179 if( dlg.ShowModal() != wxID_OK )
1180 return;
1181
1182 fn = dlg.GetPath();
1183
1184 if( fn.GetExt().IsEmpty() )
1185 fn.SetExt( FILEEXT::ReportFileExtension );
1186
1187 if( !fn.IsAbsolute() )
1188 {
1189 wxString prj_path = Prj().GetProjectPath();
1190 fn.MakeAbsolute( prj_path );
1191 }
1192
1193 DRC_REPORT reportWriter( m_frame->GetBoard(), GetUserUnits(), m_markersProvider,
1195
1196 bool success = false;
1197
1198 if( fn.GetExt().Lower() == FILEEXT::JsonFileExtension )
1199 success = reportWriter.WriteJsonReport( fn.GetFullPath() );
1200 else
1201 success = reportWriter.WriteTextReport( fn.GetFullPath() );
1202
1203 if( success )
1204 m_messages->Report( wxString::Format( _( "Report file '%s' created<br>" ), fn.GetFullPath() ) );
1205 else
1206 DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ), fn.GetFullPath() ) );
1207}
1208
1209
1210void DIALOG_DRC::OnClose( wxCloseEvent& aEvent )
1211{
1212 wxCommandEvent dummy;
1214}
1215
1216
1217void DIALOG_DRC::OnCancelClick( wxCommandEvent& aEvent )
1218{
1219 if( m_running )
1220 {
1221 m_cancelled = true;
1222 return;
1223 }
1224
1225 m_frame->ClearFocus();
1226
1227 SetReturnCode( wxID_CANCEL );
1228
1229 // The dialog can be modal or not modal.
1230 // Leave the DRC caller destroy (or not) the dialog
1231 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1232 drcTool->DestroyDRCDialog();
1233}
1234
1235
1236void DIALOG_DRC::OnChangingNotebookPage( wxNotebookEvent& aEvent )
1237{
1238 m_markerDataView->UnselectAll();
1239 m_unconnectedDataView->UnselectAll();
1240 m_footprintsDataView->UnselectAll();
1241
1242 aEvent.Skip();
1243}
1244
1245
1247{
1248 WINDOW_THAWER thawer( m_frame );
1249
1250 m_frame->GetCanvas()->Refresh();
1251}
1252
1253
1255{
1256 if( m_Notebook->IsShown() )
1257 {
1258 switch( m_Notebook->GetSelection() )
1259 {
1260 case 0: m_markersTreeModel->PrevMarker(); break;
1261 case 1: m_unconnectedTreeModel->PrevMarker(); break;
1262 case 2: m_fpWarningsTreeModel->PrevMarker(); break;
1263 case 3: break;
1264 }
1265 }
1266}
1267
1268
1270{
1271 if( m_Notebook->IsShown() )
1272 {
1273 switch( m_Notebook->GetSelection() )
1274 {
1275 case 0: m_markersTreeModel->NextMarker(); break;
1276 case 1: m_unconnectedTreeModel->NextMarker(); break;
1277 case 2: m_fpWarningsTreeModel->NextMarker(); break;
1278 case 3: break;
1279 }
1280 }
1281}
1282
1283
1285{
1286 if( m_Notebook->IsShown() )
1287 {
1288 enum MARKER_BASE::MARKER_T markerType = aMarker->GetMarkerType();
1289
1290 if( markerType == MARKER_BASE::MARKER_DRC )
1291 m_Notebook->SetSelection( 0 );
1292 else if( markerType == MARKER_BASE::MARKER_PARITY )
1293 m_Notebook->SetSelection( 2 );
1294
1295 m_markersTreeModel->SelectMarker( aMarker );
1296
1297 CallAfter(
1298 [this, aMarker]
1299 {
1300 m_markersTreeModel->CenterMarker( aMarker );
1301 } );
1302 }
1303}
1304
1305
1307{
1308 if( !m_Notebook->IsShown() || m_Notebook->GetSelection() != 0 )
1309 return;
1310
1311 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
1312
1313 if( node && node->m_RcItem )
1314 {
1315 PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
1316
1317 if( marker && marker->GetSeverity() != RPT_SEVERITY_EXCLUSION )
1318 {
1319 marker->SetExcluded( true );
1320 bds().m_DrcExclusions.insert( DRC_EXCLUSION::FromMarker( *marker ) );
1321 m_frame->GetCanvas()->GetView()->Update( marker );
1322
1323 // Update view
1324 if( m_showExclusions->GetValue() )
1325 m_markersTreeModel->ValueChanged( node );
1326 else
1327 m_markersTreeModel->DeleteCurrentItem( false );
1328
1330 refreshEditor();
1331 m_frame->OnModify();
1332 }
1333 }
1334}
1335
1336
1337void DIALOG_DRC::deleteAllMarkers( bool aIncludeExclusions )
1338{
1339 // Clear current selection list to avoid selection of deleted items
1340 Freeze();
1341 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1342
1343 m_markersTreeModel->DeleteItems( false, aIncludeExclusions, false );
1344 m_unconnectedTreeModel->DeleteItems( false, aIncludeExclusions, false );
1345 m_fpWarningsTreeModel->DeleteItems( false, aIncludeExclusions, false );
1346
1347 m_frame->GetBoard()->DeleteMARKERs( true, aIncludeExclusions );
1348 Thaw();
1349}
1350
1351
1352void DIALOG_DRC::OnDeleteOneClick( wxCommandEvent& aEvent )
1353{
1354 if( m_Notebook->GetSelection() == 0 )
1355 {
1356 // Clear the selection. It may be the selected DRC marker.
1357 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
1358
1359 m_markersTreeModel->DeleteCurrentItem( true );
1360
1361 // redraw the pcb
1362 refreshEditor();
1363 }
1364 else if( m_Notebook->GetSelection() == 1 )
1365 {
1366 m_unconnectedTreeModel->DeleteCurrentItem( true );
1367 }
1368 else if( m_Notebook->GetSelection() == 2 )
1369 {
1370 m_fpWarningsTreeModel->DeleteCurrentItem( true );
1371 }
1372
1374}
1375
1376
1377void DIALOG_DRC::OnDeleteAllClick( wxCommandEvent& aEvent )
1378{
1379 static bool s_includeExclusions = false;
1380
1381 int numExcluded = 0;
1382
1383 if( m_markersProvider )
1384 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1385
1386 if( m_ratsnestProvider )
1387 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1388
1390 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1391
1392 if( numExcluded > 0 )
1393 {
1394 wxMessageDialog dlg( this, _( "Delete exclusions too?" ), _( "Delete All Markers" ),
1395 wxYES_NO | wxCANCEL | wxCENTER | wxICON_QUESTION );
1396 dlg.SetYesNoLabels( _( "Errors and Warnings Only" ),
1397 _( "Errors, Warnings and Exclusions" ) );
1398
1399 int ret = dlg.ShowModal();
1400
1401 if( ret == wxID_CANCEL )
1402 return;
1403 else if( ret == wxID_NO )
1404 s_includeExclusions = true;
1405 }
1406
1407 deleteAllMarkers( s_includeExclusions );
1408 m_drcRun = false;
1409
1410 refreshEditor();
1412}
1413
1414
1416{
1417 DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
1418 DRC_ENGINE* drcEngine = drcTool->GetDRCEngine().get();
1419
1420 // Collect counts:
1421
1422 int numMarkers = 0;
1423 int numUnconnected = 0;
1424 int numFootprints = 0;
1425
1426 int numErrors = 0;
1427 int numWarnings = 0;
1428 int numExcluded = 0;
1429
1430 if( m_markersProvider )
1431 {
1432 numMarkers += m_markersProvider->GetCount();
1433 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
1434 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
1435 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1436 }
1437
1438 if( m_ratsnestProvider )
1439 {
1440 numUnconnected += m_ratsnestProvider->GetCount();
1441 numErrors += m_ratsnestProvider->GetCount( RPT_SEVERITY_ERROR );
1442 numWarnings += m_ratsnestProvider->GetCount( RPT_SEVERITY_WARNING );
1443 numExcluded += m_ratsnestProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1444 }
1445
1447 {
1448 numFootprints += m_fpWarningsProvider->GetCount();
1449 numErrors += m_fpWarningsProvider->GetCount( RPT_SEVERITY_ERROR );
1450 numWarnings += m_fpWarningsProvider->GetCount( RPT_SEVERITY_WARNING );
1451 numExcluded += m_fpWarningsProvider->GetCount( RPT_SEVERITY_EXCLUSION );
1452 }
1453
1454 bool errorsOverflowed = false;
1455 bool warningsOverflowed = false;
1456 bool markersOverflowed = false;
1457 bool unconnectedOverflowed = false;
1458 bool footprintsOverflowed = false;
1459
1460 for( int ii = DRCE_FIRST; ii <= DRCE_LAST; ++ii )
1461 {
1462 const SEVERITY severity = bds().GetSeverity( ii );
1463
1464 if( drcEngine->IsErrorLimitExceeded( ii ) )
1465 {
1466 if( severity == RPT_SEVERITY_ERROR )
1467 errorsOverflowed = true;
1468 else if( severity == RPT_SEVERITY_WARNING )
1469 warningsOverflowed = true;
1470
1471 if( ii == DRCE_UNCONNECTED_ITEMS )
1472 {
1473 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1474 unconnectedOverflowed = true;
1475 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1476 unconnectedOverflowed = true;
1477 }
1478 else if( ii == DRCE_MISSING_FOOTPRINT
1480 || ii == DRCE_EXTRA_FOOTPRINT
1481 || ii == DRCE_NET_CONFLICT
1482 || ii == DRCE_SCHEMATIC_PARITY
1483 || ii == DRCE_FOOTPRINT_FILTERS
1485 {
1486 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1487 footprintsOverflowed = true;
1488 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1489 footprintsOverflowed = true;
1490 }
1491 else
1492 {
1493 if( m_showWarnings->GetValue() && severity == RPT_SEVERITY_WARNING )
1494 markersOverflowed = true;
1495 else if( m_showErrors->GetValue() && severity == RPT_SEVERITY_ERROR )
1496 markersOverflowed = true;
1497 }
1498 }
1499 }
1500
1501 wxString msg;
1502 wxString num;
1503
1504 // Update tab headers:
1505
1506 if( m_drcRun )
1507 {
1508 num.Printf( markersOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numMarkers );
1509 msg.Printf( m_markersTitleTemplate, num );
1510 }
1511 else
1512 {
1514 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1515 }
1516
1517 m_Notebook->SetPageText( 0, msg );
1518
1519 if( m_drcRun )
1520 {
1521 num.Printf( unconnectedOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numUnconnected );
1522 msg.sprintf( m_unconnectedTitleTemplate, num );
1523 }
1524 else
1525 {
1527 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1528 }
1529
1530 m_Notebook->SetPageText( 1, msg );
1531
1533 {
1534 num.Printf( footprintsOverflowed ? wxT( "%d+" ) : wxT( "%d" ), numFootprints );
1535 msg.sprintf( m_footprintsTitleTemplate, num );
1536 }
1537 else if( m_drcRun )
1538 {
1540 msg.Replace( wxT( "%s" ), _( "not run" ) );
1541 }
1542 else
1543 {
1545 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1546 }
1547
1548 m_Notebook->SetPageText( 2, msg );
1549
1550 if( m_drcRun )
1551 {
1552 num.Printf( wxT( "%d" ), m_ignoredList->GetItemCount() );
1553 msg.sprintf( m_ignoredTitleTemplate, num );
1554 }
1555 else
1556 {
1558 msg.Replace( wxT( "(%s)" ), wxEmptyString );
1559 }
1560
1561 m_Notebook->SetPageText( 3, msg );
1562
1563 // Update badges:
1564
1565 if( !m_drcRun && numErrors == 0 )
1566 numErrors = -1;
1567
1568 if( !m_drcRun && numWarnings == 0 )
1569 numWarnings = -1;
1570
1571 m_errorsBadge->SetMaximumNumber( numErrors );
1572 m_errorsBadge->UpdateNumber( errorsOverflowed ? numErrors + 1 : numErrors, RPT_SEVERITY_ERROR );
1573
1574 m_warningsBadge->SetMaximumNumber( numWarnings );
1575 m_warningsBadge->UpdateNumber( warningsOverflowed ? numWarnings + 1 : numWarnings, RPT_SEVERITY_WARNING );
1576
1577 m_exclusionsBadge->SetMaximumNumber( numExcluded );
1578 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
1579}
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:982
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)
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:410
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1199
const MARKERS & Markers() const
Definition board.h:496
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:1667
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition board.cpp:1695
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2143
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:80
@ MARKER_DRAWING_SHEET
Definition marker_base.h:52
void SetExcluded(bool aExcluded, const wxString &aComment=wxEmptyString)
Definition marker_base.h:91
enum MARKER_T GetMarkerType() const
Definition marker_base.h:88
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:136
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition rc_item.h:268
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:252
NODE_TYPE m_Type
Definition rc_item.h:251
Master controller class:
T * GetTool()
Return the tool of given type or nullptr if there is no such tool registered.
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:522
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:533
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:708
Definition of file extensions used in Kicad.