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