KiCad PCB EDA Suite
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 (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <confirm.h>
27 #include <dialog_drc.h>
28 #include <board_design_settings.h>
29 #include <kiface_base.h>
30 #include <macros.h>
31 #include <pad.h>
32 #include <pcb_edit_frame.h>
33 #include <pcbnew_settings.h>
34 #include <tool/tool_manager.h>
35 #include <tools/pcb_actions.h>
37 #include <pcb_marker.h>
38 #include <wx/filedlg.h>
39 #include <wx/wupdlock.h>
41 #include <widgets/ui_common.h>
45 #include <tools/drc_tool.h>
46 #include <tools/zone_filler_tool.h>
48 #include <kiplatform/ui.h>
49 
50 DIALOG_DRC::DIALOG_DRC( PCB_EDIT_FRAME* aEditorFrame, wxWindow* aParent ) :
51  DIALOG_DRC_BASE( aParent ),
53  m_running( false ),
54  m_cancelled( false ),
55  m_drcRun( false ),
56  m_footprintTestsRun( false ),
57  m_markersProvider( nullptr ),
58  m_markersTreeModel( nullptr ),
59  m_unconnectedItemsProvider( nullptr ),
60  m_unconnectedTreeModel( nullptr ),
61  m_footprintWarningsProvider( nullptr ),
62  m_footprintWarningsTreeModel( nullptr ),
63  m_centerMarkerOnIdle( nullptr ),
65 {
66  SetName( DIALOG_DRC_WINDOW_NAME ); // Set a window name to be able to find it
67 
68  m_frame = aEditorFrame;
70 
72 
74  m_markerDataView->AssociateModel( m_markersTreeModel );
75 
78 
81 
82  if( Kiface().IsSingle() )
83  m_cbTestFootprints->Hide();
84 
85  // We use a sdbSizer here to get the order right, which is platform-dependent
86  m_sdbSizerOK->SetLabel( _( "Run DRC" ) );
87  m_sdbSizerCancel->SetLabel( _( "Close" ) );
88  m_sizerButtons->Layout();
89 
90  m_sdbSizerOK->SetDefault();
91 
92  initValues();
94 
96 }
97 
98 
100 {
101  m_frame->FocusOnItem( nullptr );
102 
104  settings->m_DrcDialog.refill_zones = m_cbRefillZones->GetValue();
106 
107  if( !Kiface().IsSingle() )
108  settings->m_DrcDialog.test_footprints = m_cbTestFootprints->GetValue();
109 
110  settings->m_DrcDialog.severities = m_severities;
111 
112  m_markersTreeModel->DecRef();
113 }
114 
115 
116 void DIALOG_DRC::OnActivateDlg( wxActivateEvent& aEvent )
117 {
118  if( m_currentBoard != m_frame->GetBoard() )
119  {
120  // If m_currentBoard is not the current board, (for instance because a new board
121  // was loaded), close the dialog, because many pointers are now invalid in lists
122  SetReturnCode( wxID_CANCEL );
123  Close();
124 
125  DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
126  drcTool->DestroyDRCDialog();
127  }
128 }
129 
130 
132 {
133  m_markersTitleTemplate = m_Notebook->GetPageText( 0 );
134  m_unconnectedTitleTemplate = m_Notebook->GetPageText( 1 );
135  m_footprintsTitleTemplate = m_Notebook->GetPageText( 2 );
136 
137  auto cfg = m_frame->GetPcbNewSettings();
138 
139  m_cbRefillZones->SetValue( cfg->m_DrcDialog.refill_zones );
140  m_cbReportAllTrackErrors->SetValue( cfg->m_DrcDialog.test_all_track_errors );
141 
142 
143  if( !Kiface().IsSingle() )
144  m_cbTestFootprints->SetValue( cfg->m_DrcDialog.test_footprints );
145 
146  m_severities = cfg->m_DrcDialog.severities;
150 
151  Layout(); // adding the units above expanded Clearance text, now resize.
152 
153  SetFocus();
154 }
155 
156 
158 {
159  double cur = (double) m_progress.load() / m_maxProgress;
160  cur = std::max( 0.0, std::min( cur, 1.0 ) );
161 
162  m_gauge->SetValue( KiROUND( cur * 1000.0 ) );
163  wxSafeYield( this );
164 
165  return !m_cancelled;
166 }
167 
168 
169 void DIALOG_DRC::AdvancePhase( const wxString& aMessage )
170 {
172  SetCurrentProgress( 0.0 );
173 
174  m_messages->Report( aMessage );
175 }
176 
177 
178 // Don't globally define this; different facilities use different definitions of "ALL"
180 
181 
183 {
184  m_showAll->SetValue( m_severities == RPT_SEVERITY_ALL );
188 }
189 
190 
191 void DIALOG_DRC::OnErrorLinkClicked( wxHtmlLinkEvent& event )
192 {
193  m_frame->ShowBoardSetupDialog( _( "Custom Rules" ) );
194 }
195 
196 
197 void DIALOG_DRC::OnRunDRCClick( wxCommandEvent& aEvent )
198 {
199  TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
200  DRC_TOOL* drcTool = toolMgr->GetTool<DRC_TOOL>();
201  ZONE_FILLER_TOOL* zoneFillerTool = toolMgr->GetTool<ZONE_FILLER_TOOL>();
202  bool refillZones = m_cbRefillZones->GetValue();
203  bool reportAllTrackErrors = m_cbReportAllTrackErrors->GetValue();
204  bool testFootprints = m_cbTestFootprints->GetValue();
205 
206  if( zoneFillerTool->IsBusy() )
207  {
208  wxBell();
209  return;
210  }
211 
212  // This is not the time to have stale or buggy rules. Ensure they're up-to-date
213  // and that they at least parse.
214  try
215  {
216  drcTool->GetDRCEngine()->InitEngine( m_frame->GetDesignRulesPath() );
217  }
218  catch( PARSE_ERROR& )
219  {
220  m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
221  m_DeleteCurrentMarkerButton->Enable( false );
222  m_DeleteAllMarkersButton->Enable( false );
223  m_saveReport->Enable( false );
224 
225  m_messages->Clear();
226  m_messages->Report( _( "DRC incomplete: could not compile custom design rules. " )
227  + wxT( "<a href='boardsetup'>" )
228  + _( "Show design rules." )
229  + wxT( "</a>" ) );
230  m_messages->Flush();
231 
232  Raise();
233  return;
234  }
235 
236  m_drcRun = false;
237  m_footprintTestsRun = false;
238  m_cancelled = false;
239 
241  deleteAllMarkers( true );
242  m_unconnectedTreeModel->DeleteItems( false, true, true );
243  m_footprintWarningsTreeModel->DeleteItems( false, true, true );
244 
245  Raise();
246 
247  m_runningResultsBook->ChangeSelection( 0 ); // Display the "Tests Running..." tab
248  m_messages->Clear();
249  wxYield(); // Allow time slice to refresh Messages
250 
251  m_running = true;
252  m_sdbSizerCancel->SetLabel( _( "Cancel" ) );
253  m_sdbSizerOK->Enable( false );
254  m_DeleteCurrentMarkerButton->Enable( false );
255  m_DeleteAllMarkersButton->Enable( false );
256  m_saveReport->Enable( false );
257 
258  drcTool->RunTests( this, refillZones, reportAllTrackErrors, testFootprints );
259 
260  if( m_cancelled )
261  m_messages->Report( _( "-------- DRC cancelled by user.<br><br>" ) );
262  else
263  m_messages->Report( _( "Done.<br><br>" ) );
264 
265  Raise();
266  wxYield(); // Allow time slice to refresh Messages
267 
268  m_running = false;
269  m_sdbSizerCancel->SetLabel( _( "Close" ) );
270  m_sdbSizerOK->Enable( true );
271  m_DeleteCurrentMarkerButton->Enable( true );
272  m_DeleteAllMarkersButton->Enable( true );
273  m_saveReport->Enable( true );
274 
275  if( !m_cancelled )
276  {
277  wxMilliSleep( 500 );
278  m_runningResultsBook->ChangeSelection( 1 );
280  }
281 
282  refreshEditor();
283 }
284 
285 
287 {
288  m_markersProvider = aProvider;
291 }
292 
293 
295 {
296  m_unconnectedItemsProvider = aProvider;
299 }
300 
301 
303 {
304  m_footprintWarningsProvider = aProvider;
307 }
308 
309 
310 void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
311 {
312  BOARD* board = m_frame->GetBoard();
313  RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
314  const KIID& itemID = node ? RC_TREE_MODEL::ToUUID( aEvent.GetItem() ) : niluuid;
315  BOARD_ITEM* item = board->GetItem( itemID );
316 
317  auto getActiveLayers =
318  []( BOARD_ITEM* aItem ) -> LSET
319  {
320  if( aItem->Type() == PCB_PAD_T )
321  {
322  PAD* pad = static_cast<PAD*>( aItem );
323  LSET layers;
324 
325  for( int layer : aItem->GetLayerSet().Seq() )
326  {
327  if( pad->FlashLayer( layer ) )
328  layers.set( layer );
329  }
330 
331  return layers;
332  }
333  else
334  {
335  return aItem->GetLayerSet();
336  }
337  };
338 
339  if( node && item )
340  {
341  PCB_LAYER_ID principalLayer = item->GetLayer();
342  LSET violationLayers;
343  std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
344 
345  if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
346  {
347  BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
348 
349  if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
350  && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
351  {
352  principalLayer = B_CrtYd;
353  }
354  else
355  {
356  principalLayer = F_CrtYd;
357  }
358  }
359  else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
360  {
361  principalLayer = Edge_Cuts;
362  }
363  else
364  {
365  BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
366  BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() );
367  BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() );
368  BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
369 
370  if( a || b || c || d )
371  violationLayers = LSET::AllLayersMask();
372 
373  if( a )
374  violationLayers &= getActiveLayers( a );
375 
376  if( b )
377  violationLayers &= getActiveLayers( b );
378 
379  if( c )
380  violationLayers &= getActiveLayers( c );
381 
382  if( d )
383  violationLayers &= getActiveLayers( d );
384  }
385 
386  if( violationLayers.count() )
387  principalLayer = violationLayers.Seq().front();
388  else
389  violationLayers.set( principalLayer );
390 
391  WINDOW_THAWER thawer( m_frame );
392 
393  if( ( violationLayers & board->GetVisibleLayers() ) == 0 )
394  m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
395 
396  if( board->GetVisibleLayers().test( principalLayer ) )
397  m_frame->SetActiveLayer( principalLayer );
398 
399  if( rc_item->GetErrorCode() == DRCE_DIFF_PAIR_UNCOUPLED_LENGTH_TOO_LONG )
400  {
401  PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
402 
403  if( track )
404  {
405  int net = track->GetNetCode();
406  std::vector<BOARD_ITEM*> segs;
407 
408  for( const KIID& id : rc_item->GetIDs() )
409  {
410  PCB_TRACK* candidate = dynamic_cast<PCB_TRACK*>( board->GetItem( id ) );
411 
412  if( candidate && candidate->GetNetCode() == net )
413  segs.push_back( candidate );
414  }
415 
416  m_frame->FocusOnItems( segs, principalLayer );
417  }
418  else
419  {
420  m_frame->FocusOnItem( item, principalLayer );
421  }
422  }
423  else if( m_centerMarkerOnIdle )
424  {
425  // we already came from a cross-probe of the marker in the document; don't go
426  // around in circles
427  }
428  else
429  {
430  m_frame->FocusOnItem( item, principalLayer );
431  }
432  }
433 
434  aEvent.Skip();
435 }
436 
437 
438 void DIALOG_DRC::OnDRCItemDClick( wxDataViewEvent& aEvent )
439 {
440  if( aEvent.GetItem().IsOk() )
441  {
442  // turn control over to m_frame, hide this DIALOG_DRC window,
443  // no destruction so we can preserve listbox cursor
444  if( !IsModal() )
445  Show( false );
446  }
447 
448  // Do not skip aVent here: this is not useful, and Pcbnew crashes
449  // if skipped (at least on Windows)
450 }
451 
452 
453 void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
454 {
455  RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
456 
457  if( !node )
458  return;
459 
460  std::shared_ptr<RC_ITEM> rcItem = node->m_RcItem;
461  wxString listName;
462  wxMenu menu;
463  wxString msg;
464 
465  switch( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] )
466  {
467  case RPT_SEVERITY_ERROR: listName = _( "errors" ); break;
468  case RPT_SEVERITY_WARNING: listName = _( "warnings" ); break;
469  default: listName = _( "appropriate" ); break;
470  }
471 
472  if( rcItem->GetParent()->IsExcluded() )
473  {
474  menu.Append( 1, _( "Remove exclusion for this violation" ),
475  wxString::Format( _( "It will be placed back in the %s list" ), listName ) );
476  }
477  else
478  {
479  menu.Append( 2, _( "Exclude this violation" ),
480  wxString::Format( _( "It will be excluded from the %s list" ), listName ) );
481  }
482 
483  if( rcItem->GetErrorCode() == DRCE_CLEARANCE
484  || rcItem->GetErrorCode() == DRCE_EDGE_CLEARANCE )
485  {
486  menu.Append( 3, _( "Run clearance resolution tool..." ) );
487  }
488 
489  menu.AppendSeparator();
490 
491  if( bds().m_DRCSeverities[ rcItem->GetErrorCode() ] == RPT_SEVERITY_WARNING )
492  {
493  msg.Printf( _( "Change severity to Error for all '%s' violations" ),
494  rcItem->GetErrorText(),
495  _( "Violation severities can also be edited in the Board Setup... dialog" ) );
496  menu.Append( 4, msg );
497  }
498  else
499  {
500  msg.Printf( _( "Change severity to Warning for all '%s' violations" ),
501  rcItem->GetErrorText(),
502  _( "Violation severities can also be edited in the Board Setup... dialog" ) );
503  menu.Append( 5, msg );
504  }
505 
506  msg.Printf( _( "Ignore all '%s' violations" ),
507  rcItem->GetErrorText(),
508  _( "Violations will not be checked or reported" ) );
509  menu.Append( 6, msg );
510 
511  menu.AppendSeparator();
512 
513  menu.Append( 7, _( "Edit violation severities..." ), _( "Open the Board Setup... dialog" ) );
514 
515  bool modified = false;
516 
517  switch( GetPopupMenuSelectionFromUser( menu ) )
518  {
519  case 1:
520  {
521  PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
522 
523  if( marker )
524  {
525  marker->SetExcluded( false );
526  m_frame->GetCanvas()->GetView()->Update( marker );
527 
528  // Update view
529  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
530  modified = true;
531  }
532 
533  break;
534  }
535 
536  case 2:
537  {
538  PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
539 
540  if( marker )
541  {
542  marker->SetExcluded( true );
543  m_frame->GetCanvas()->GetView()->Update( marker );
544 
545  // Update view
547  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->ValueChanged( node );
548  else
549  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->DeleteCurrentItem( false );
550 
551  modified = true;
552  }
553 
554  break;
555  }
556 
557  case 3:
558  {
559  TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
560  BOARD_INSPECTION_TOOL* inspectionTool = toolMgr->GetTool<BOARD_INSPECTION_TOOL>();
561 
562  inspectionTool->InspectDRCError( node->m_RcItem );
563  break;
564  }
565 
566  case 4:
567  bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_ERROR;
568 
569  for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
570  {
571  if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
572  m_frame->GetCanvas()->GetView()->Update( marker );
573  }
574 
575  // Rebuild model and view
576  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->SetProvider( m_markersProvider );
577  modified = true;
578  break;
579 
580  case 5:
581  bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_WARNING;
582 
583  for( PCB_MARKER* marker : m_frame->GetBoard()->Markers() )
584  {
585  if( marker->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
586  m_frame->GetCanvas()->GetView()->Update( marker );
587  }
588 
589  // Rebuild model and view
590  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->SetProvider( m_markersProvider );
591  modified = true;
592  break;
593 
594  case 6:
595  {
596  bds().m_DRCSeverities[ rcItem->GetErrorCode() ] = RPT_SEVERITY_IGNORE;
597 
598  std::vector<PCB_MARKER*>& markers = m_frame->GetBoard()->Markers();
599 
600  for( unsigned i = 0; i < markers.size(); )
601  {
602  if( markers[i]->GetRCItem()->GetErrorCode() == rcItem->GetErrorCode() )
603  {
604  m_frame->GetCanvas()->GetView()->Remove( markers.at( i ) );
605  markers.erase( markers.begin() + i );
606  }
607  else
608  ++i;
609  }
610 
611  // Rebuild model and view
612  static_cast<RC_TREE_MODEL*>( aEvent.GetModel() )->SetProvider( m_markersProvider );
613  modified = true;
614  break;
615  }
616 
617  case 7:
618  m_frame->ShowBoardSetupDialog( _( "Violation Severity" ) );
619  break;
620  }
621 
622  if( modified )
623  {
625  refreshEditor();
626  m_frame->OnModify();
627  }
628 }
629 
630 
631 void DIALOG_DRC::OnSeverity( wxCommandEvent& aEvent )
632 {
633  int flag = 0;
634 
635  if( aEvent.GetEventObject() == m_showAll )
637  else if( aEvent.GetEventObject() == m_showErrors )
639  else if( aEvent.GetEventObject() == m_showWarnings )
641  else if( aEvent.GetEventObject() == m_showExclusions )
643 
644  if( aEvent.IsChecked() )
645  m_severities |= flag;
646  else if( aEvent.GetEventObject() == m_showAll )
648  else
649  m_severities &= ~flag;
650 
651  syncCheckboxes();
652 
653  // Set the provider's severity levels through the TreeModel so that the old tree
654  // can be torn down before the severity changes.
655  //
656  // It's not clear this is required, but we've had a lot of issues with wxDataView
657  // being cranky on various platforms.
658 
662 
664 }
665 
666 
667 void DIALOG_DRC::OnSaveReport( wxCommandEvent& aEvent )
668 {
669  wxFileName fn( "DRC." + ReportFileExtension );
670 
671  wxFileDialog dlg( this, _( "Save Report to File" ), Prj().GetProjectPath(), fn.GetFullName(),
672  ReportFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
673 
674  if( dlg.ShowModal() != wxID_OK )
675  return;
676 
677  fn = dlg.GetPath();
678 
679  if( fn.GetExt().IsEmpty() )
680  fn.SetExt( ReportFileExtension );
681 
682  if( !fn.IsAbsolute() )
683  {
684  wxString prj_path = Prj().GetProjectPath();
685  fn.MakeAbsolute( prj_path );
686  }
687 
688  if( writeReport( fn.GetFullPath() ) )
689  {
690  m_messages->Report( wxString::Format( _( "Report file '%s' created<br>" ),
691  fn.GetFullPath() ) );
692  }
693  else
694  {
695  DisplayError( this, wxString::Format( _( "Failed to create file '%s'." ),
696  fn.GetFullPath() ) );
697  }
698 }
699 
700 
701 void DIALOG_DRC::OnClose( wxCloseEvent& aEvent )
702 {
703  if( m_running )
704  aEvent.Veto();
705 
706  wxCommandEvent dummy;
707 
708  OnCancelClick( dummy );
709 }
710 
711 
712 void DIALOG_DRC::OnCancelClick( wxCommandEvent& aEvent )
713 {
714  if( m_running )
715  {
716  m_cancelled = true;
717  return;
718  }
719 
720  m_frame->FocusOnItem( nullptr );
721 
722  SetReturnCode( wxID_CANCEL );
723 
724  // The dialog can be modal or not modal.
725  // Leave the DRC caller destroy (or not) the dialog
726  DRC_TOOL* drcTool = m_frame->GetToolManager()->GetTool<DRC_TOOL>();
727  drcTool->DestroyDRCDialog();
728 }
729 
730 
731 void DIALOG_DRC::OnChangingNotebookPage( wxNotebookEvent& aEvent )
732 {
733  // Shouldn't be necessary, but is on at least OSX
734  if( aEvent.GetSelection() >= 0 )
735  m_Notebook->ChangeSelection( (unsigned) aEvent.GetSelection() );
736 
737  m_markerDataView->UnselectAll();
738  m_unconnectedDataView->UnselectAll();
739  m_footprintsDataView->UnselectAll();
740 }
741 
742 
744 {
745  WINDOW_THAWER thawer( m_frame );
746 
747  m_frame->GetCanvas()->Refresh();
748 }
749 
750 
752 {
753  if( m_Notebook->IsShown() )
754  {
755  switch( m_Notebook->GetSelection() )
756  {
757  case 0: m_markersTreeModel->PrevMarker(); break;
758  case 1: m_unconnectedTreeModel->PrevMarker(); break;
759  case 2: m_footprintWarningsTreeModel->PrevMarker(); break;
760  }
761  }
762 }
763 
764 
766 {
767  if( m_Notebook->IsShown() )
768  {
769  switch( m_Notebook->GetSelection() )
770  {
771  case 0: m_markersTreeModel->NextMarker(); break;
772  case 1: m_unconnectedTreeModel->NextMarker(); break;
773  case 2: m_footprintWarningsTreeModel->NextMarker(); break;
774  }
775  }
776 }
777 
778 
779 void DIALOG_DRC::SelectMarker( const PCB_MARKER* aMarker )
780 {
781  if( m_Notebook->IsShown() )
782  {
783  m_Notebook->SetSelection( 0 );
784  m_markersTreeModel->SelectMarker( aMarker );
785 
786  // wxWidgets on some platforms fails to correctly ensure that a selected item is
787  // visible, so we have to do it in a separate idle event.
788  m_centerMarkerOnIdle = aMarker;
789  Bind( wxEVT_IDLE, &DIALOG_DRC::centerMarkerIdleHandler, this );
790  }
791 }
792 
793 
794 void DIALOG_DRC::centerMarkerIdleHandler( wxIdleEvent& aEvent )
795 {
797  m_centerMarkerOnIdle = nullptr;
798  Unbind( wxEVT_IDLE, &DIALOG_DRC::centerMarkerIdleHandler, this );
799 }
800 
801 
803 {
804  if( !m_Notebook->IsShown() || m_Notebook->GetSelection() != 0 )
805  return;
806 
807  RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( m_markerDataView->GetCurrentItem() );
808  PCB_MARKER* marker = dynamic_cast<PCB_MARKER*>( node->m_RcItem->GetParent() );
809 
810  if( marker && !marker->IsExcluded() )
811  {
812  marker->SetExcluded( true );
813  m_frame->GetCanvas()->GetView()->Update( marker );
814 
815  // Update view
818  else
820 
822  refreshEditor();
823  m_frame->OnModify();
824  }
825 }
826 
827 
828 void DIALOG_DRC::deleteAllMarkers( bool aIncludeExclusions )
829 {
830  // Clear current selection list to avoid selection of deleted items
832 
833  m_markersTreeModel->DeleteItems( false, aIncludeExclusions, true );
834 }
835 
836 
837 bool DIALOG_DRC::writeReport( const wxString& aFullFileName )
838 {
839  FILE* fp = wxFopen( aFullFileName, wxT( "w" ) );
840 
841  if( fp == nullptr )
842  return false;
843 
844  std::map<KIID, EDA_ITEM*> itemMap;
845  m_frame->GetBoard()->FillItemMap( itemMap );
846 
847  EDA_UNITS units = GetUserUnits();
849  int count;
850 
851  fprintf( fp, "** Drc report for %s **\n", TO_UTF8( m_frame->GetBoard()->GetFileName() ) );
852 
853  wxDateTime now = wxDateTime::Now();
854 
855  fprintf( fp, "** Created on %s **\n", TO_UTF8( now.Format( wxT( "%F %T" ) ) ) );
856 
857  count = m_markersProvider->GetCount();
858 
859  fprintf( fp, "\n** Found %d DRC violations **\n", count );
860 
861  for( int i = 0; i < count; ++i )
862  {
863  const std::shared_ptr<RC_ITEM>& item = m_markersProvider->GetItem( i );
864  SEVERITY severity = bds.GetSeverity( item->GetErrorCode() );
865 
866  fprintf( fp, "%s", TO_UTF8( item->ShowReport( units, severity, itemMap ) ) );
867  }
868 
870 
871  fprintf( fp, "\n** Found %d unconnected pads **\n", count );
872 
873  for( int i = 0; i < count; ++i )
874  {
875  const std::shared_ptr<RC_ITEM>& item = m_unconnectedItemsProvider->GetItem( i );
876  SEVERITY severity = bds.GetSeverity( item->GetErrorCode() );
877 
878  fprintf( fp, "%s", TO_UTF8( item->ShowReport( units, severity, itemMap ) ) );
879  }
880 
882 
883  fprintf( fp, "\n** Found %d Footprint errors **\n", count );
884 
885  for( int i = 0; i < count; ++i )
886  {
887  const std::shared_ptr<RC_ITEM>& item = m_footprintWarningsProvider->GetItem( i );
888  SEVERITY severity = bds.GetSeverity( item->GetErrorCode() );
889 
890  fprintf( fp, "%s", TO_UTF8( item->ShowReport( units, severity, itemMap ) ) );
891  }
892 
893 
894  fprintf( fp, "\n** End of Report **\n" );
895 
896  fclose( fp );
897 
898  return true;
899 }
900 
901 
902 void DIALOG_DRC::OnDeleteOneClick( wxCommandEvent& aEvent )
903 {
904  if( m_Notebook->GetSelection() == 0 )
905  {
906  // Clear the selection. It may be the selected DRC marker.
908 
910 
911  // redraw the pcb
912  refreshEditor();
913  }
914  else if( m_Notebook->GetSelection() == 1 )
915  {
917  }
918  else if( m_Notebook->GetSelection() == 2 )
919  {
921  }
922 
924 }
925 
926 
927 void DIALOG_DRC::OnDeleteAllClick( wxCommandEvent& aEvent )
928 {
929  static bool s_includeExclusions = false;
930 
931  int numExcluded = 0;
932 
933  if( m_markersProvider )
935 
938 
941 
942  if( numExcluded > 0 )
943  {
944  wxRichMessageDialog dlg( this, _( "Do you wish to delete excluded markers as well?" ),
945  _( "Delete All Markers" ),
946  wxOK | wxCANCEL | wxCENTER | wxICON_QUESTION );
947  dlg.ShowCheckBox( _( "Delete exclusions" ), s_includeExclusions );
948 
949  int ret = dlg.ShowModal();
950 
951  if( ret == wxID_CANCEL )
952  return;
953  else
954  s_includeExclusions = dlg.IsCheckBoxChecked();
955  }
956 
957  deleteAllMarkers( s_includeExclusions );
958 
959  m_drcRun = false;
960  refreshEditor();
962 }
963 
964 
966 {
967  // Collect counts:
968 
969  int numMarkers = 0;
970  int numUnconnected = 0;
971  int numFootprints = 0;
972 
973  int numErrors = 0;
974  int numWarnings = 0;
975  int numExcluded = 0;
976 
977  if( m_markersProvider )
978  {
979  numMarkers += m_markersProvider->GetCount();
983  }
984 
986  {
987  numUnconnected += m_unconnectedItemsProvider->GetCount();
991  }
992 
994  {
995  numFootprints += m_footprintWarningsProvider->GetCount();
999  }
1000 
1001  wxString msg;
1002 
1003  // Update tab headers:
1004 
1005  if( m_drcRun )
1006  {
1007  msg.sprintf( m_markersTitleTemplate, numMarkers );
1008  m_Notebook->SetPageText( 0, msg );
1009 
1010  msg.sprintf( m_unconnectedTitleTemplate, numUnconnected );
1011  m_Notebook->SetPageText( 1, msg );
1012 
1013  if( m_footprintTestsRun )
1014  {
1015  msg.sprintf( m_footprintsTitleTemplate, numFootprints );
1016  }
1017  else
1018  {
1020  msg.Replace( wxT( "%d" ), _( "not run" ) );
1021  }
1022  m_Notebook->SetPageText( 2, msg );
1023  }
1024  else
1025  {
1026  msg = m_markersTitleTemplate;
1027  msg.Replace( wxT( "(%d)" ), wxEmptyString );
1028  m_Notebook->SetPageText( 0, msg );
1029 
1031  msg.Replace( wxT( "(%d)" ), wxEmptyString );
1032  m_Notebook->SetPageText( 1, msg );
1033 
1035  msg.Replace( wxT( "(%d)" ), wxEmptyString );
1036  m_Notebook->SetPageText( 2, msg );
1037  }
1038 
1039  // Update badges:
1040 
1041  if( !m_drcRun && numErrors == 0 )
1042  numErrors = -1;
1043 
1044  if( !m_drcRun && numWarnings == 0 )
1045  numWarnings = -1;
1046 
1047  m_errorsBadge->SetMaximumNumber( numErrors );
1049 
1050  m_warningsBadge->SetMaximumNumber( numWarnings );
1052 
1053  m_exclusionsBadge->SetMaximumNumber( numExcluded );
1055 }
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
wxCheckBox * m_showWarnings
BOARD_ITEM * GetItem(const KIID &aID) const
Definition: board.cpp:845
void initValues()
Definition: dialog_drc.cpp:131
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:279
void DeleteItems(bool aCurrentOnly, bool aIncludeExclusions, bool aDeep)
Deletes the current item or all items.
Definition: rc_item.cpp:467
void syncCheckboxes()
Definition: dialog_drc.cpp:182
wxCheckBox * m_cbTestFootprints
void OnModify() override
Must be called after a board change to set the modified flag.
void NextMarker()
Definition: rc_item.cpp:577
wxNotebook * m_Notebook
void NextMarker()
Definition: dialog_drc.cpp:765
void updateDisplayedCounts()
Definition: dialog_drc.cpp:965
BOARD_DESIGN_SETTINGS & bds()
Definition: dialog_drc.h:109
static int RPT_SEVERITY_ALL
Definition: dialog_drc.cpp:179
KIID niluuid(0)
void OnDRCItemRClick(wxDataViewEvent &aEvent) override
Definition: dialog_drc.cpp:453
#define MALFORMED_B_COURTYARD
void SetProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: rc_item.cpp:294
void OnErrorLinkClicked(wxHtmlLinkEvent &event) override
Definition: dialog_drc.cpp:191
wxCheckBox * m_cbReportAllTrackErrors
void deleteAllMarkers(bool aIncludeExclusions)
Definition: dialog_drc.cpp:828
wxButton * m_DeleteCurrentMarkerButton
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
void CenterMarker(const MARKER_BASE *aMarker)
Definition: rc_item.cpp:618
This file is part of the common library.
void Clear()
Delete the stored messages.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
Class DIALOG_DRC_BASE.
virtual void AdvancePhase() override
Use the next available virtual zone of the dialog progress bar.
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
wxSimplebook * m_runningResultsBook
MARKERS & Markers()
Definition: board.h:243
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
#define MALFORMED_F_COURTYARD
bool writeReport(const wxString &aFullFileName)
Function writeReport outputs the MARKER items and unconnecte DRC_ITEMs with commentary to an open tex...
Definition: dialog_drc.cpp:837
void Flush()
Build the HTML messages page.
void UpdateNumber(int aNumber, SEVERITY aSeverity)
Update the number displayed on the badge.
void PrevMarker()
Definition: dialog_drc.cpp:751
void InspectDRCError(const std::shared_ptr< RC_ITEM > &aDRCItem)
Show the clearance resolution for two selected items.
void OnDeleteOneClick(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:902
bool m_footprintTestsRun
Definition: dialog_drc.h:116
wxString m_footprintsTitleTemplate
Definition: dialog_drc.h:120
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:481
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
class PAD, a pad in a footprint
Definition: typeinfo.h:89
wxButton * m_sdbSizerCancel
static KIID ToUUID(wxDataViewItem aItem)
Definition: rc_item.cpp:147
void ExcludeMarker()
Definition: dialog_drc.cpp:802
RC_ITEMS_PROVIDER * m_markersProvider
Definition: dialog_drc.h:122
void OnDRCItemDClick(wxDataViewEvent &aEvent) override
Definition: dialog_drc.cpp:438
void RecordDRCExclusions()
Scan existing markers and record data from any that are Excluded.
virtual int GetCount(int aSeverity=-1) const =0
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
APPEARANCE_CONTROLS * GetAppearancePanel()
const wxString & GetFileName() const
Definition: board.h:229
BOARD * m_currentBoard
Definition: dialog_drc.h:111
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:122
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:75
void DeleteCurrentItem(bool aDeep)
Definition: rc_item.cpp:461
void SetSeverities(int aSeverities)
Definition: rc_item.cpp:300
This file contains miscellaneous commonly used macros and functions.
Provide an abstract interface of a RC_ITEM* list manager.
Definition: rc_item.h:45
void OnChangingNotebookPage(wxNotebookEvent &aEvent) override
Definition: dialog_drc.cpp:731
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:121
RC_ITEMS_PROVIDER * m_unconnectedItemsProvider
Definition: dialog_drc.h:125
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:72
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
Master controller class:
Definition: tool_manager.h:54
wxCheckBox * m_showExclusions
Definition: kiid.h:44
wxString m_markersTitleTemplate
Definition: dialog_drc.h:118
void SetImmediateMode()
In immediate mode, messages are flushed as they are added.
wxCheckBox * m_cbRefillZones
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:92
Handle actions specific to filling copper zones.
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:516
void SetExcluded(bool aExcluded)
Definition: marker_base.h:96
void ShowBoardSetupDialog(const wxString &aInitialPage=wxEmptyString)
std::shared_ptr< RC_ITEM > m_RcItem
Definition: rc_item.h:197
wxDataViewCtrl * m_footprintsDataView
RC_ITEMS_PROVIDER * m_footprintWarningsProvider
Definition: dialog_drc.h:128
wxBoxSizer * m_sizerButtons
wxButton * m_DeleteAllMarkersButton
wxButton * m_sdbSizerOK
void OnDeleteAllClick(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:927
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition: rc_item.h:212
wxString ReportFileWildcard()
void OnDRCItemSelected(wxDataViewEvent &aEvent) override
Definition: dialog_drc.cpp:310
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
DIALOG_DRC(PCB_EDIT_FRAME *aEditorFrame, wxWindow *aParent)
Constructors.
Definition: dialog_drc.cpp:50
RC_TREE_MODEL * m_footprintWarningsTreeModel
Definition: dialog_drc.h:129
void FocusOnItems(std::vector< BOARD_ITEM * > aItems, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
void SetFootprintsProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: dialog_drc.cpp:302
std::atomic< bool > m_cancelled
Definition: dialog_drc.h:114
Definition of file extensions used in Kicad.
NUMBER_BADGE * m_exclusionsBadge
void SelectMarker(const MARKER_BASE *aMarker)
Definition: rc_item.cpp:605
#define DIALOG_DRC_WINDOW_NAME
Definition: dialog_drc.h:41
Functions to provide common constants and other functions to assist in making a consistent UI.
#define _(s)
NUMBER_BADGE * m_errorsBadge
static LSET AllLayersMask()
Definition: lset.cpp:796
SEVERITY GetSeverity(int aDRCErrorCode)
void OnClose(wxCloseEvent &event) override
Definition: dialog_drc.cpp:701
SEVERITY
void PrevMarker()
Definition: rc_item.cpp:556
std::shared_ptr< DRC_ENGINE > GetDRCEngine()
Definition: drc_tool.h:78
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void OnRunDRCClick(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:197
bool Show(bool show) override
void FocusOnItem(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
void RunTests(PROGRESS_REPORTER *aProgressReporter, bool aRefillZones, bool aReportAllTrackErrors, bool aTestFootprints)
Run the DRC tests.
Definition: drc_tool.cpp:132
RC_TREE_MODEL * m_unconnectedTreeModel
Definition: dialog_drc.h:126
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:154
This implements all the tricky bits for thread safety, but the GUI is left to derived classes.
wxDataViewCtrl * m_unconnectedDataView
void OnCancelClick(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:712
wxButton * m_saveReport
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
EDA_UNITS
Definition: eda_units.h:38
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
void SetMarkersProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: dialog_drc.cpp:286
wxDataViewCtrl * m_markerDataView
virtual std::shared_ptr< RC_ITEM > GetItem(int aIndex) const =0
Retrieve a RC_ITEM by index.
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
WX_HTML_REPORT_BOX * m_messages
const std::string ReportFileExtension
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition: gtk/ui.cpp:44
void refreshEditor()
Definition: dialog_drc.cpp:743
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
wxCheckBox * m_showErrors
std::map< int, SEVERITY > m_DRCSeverities
NUMBER_BADGE * m_warningsBadge
wxCheckBox * m_showAll
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
The main frame for Pcbnew.
PCBNEW_SETTINGS * GetPcbNewSettings() const
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:73
void OnActivateDlg(wxActivateEvent &aEvent) override
Definition: dialog_drc.cpp:116
bool m_drcRun
Definition: dialog_drc.h:115
void SelectMarker(const PCB_MARKER *aMarker)
Definition: dialog_drc.cpp:779
int m_severities
Definition: dialog_drc.h:133
void SetLayerVisible(LAYER_NUM aLayer, bool isVisible)
void ValueChanged(const RC_TREE_NODE *aNode)
Definition: rc_item.cpp:444
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
virtual void SetCurrentProgress(double aProgress) override
Set the progress value to aProgress (0..1).
RC_TREE_MODEL * m_markersTreeModel
Definition: dialog_drc.h:123
DIALOG_DRC m_DrcDialog
BOARD * GetBoard() const
void DestroyDRCDialog()
Close and free the DRC dialog.
Definition: drc_tool.cpp:122
Definition: pad.h:57
PCB_EDIT_FRAME * m_frame
Definition: dialog_drc.h:112
void OnSaveReport(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:667
void centerMarkerIdleHandler(wxIdleEvent &aEvent)
Definition: dialog_drc.cpp:794
const PCB_MARKER * m_centerMarkerOnIdle
Definition: dialog_drc.h:131
void SetMaximumNumber(int aMax)
Set the maximum number to be shown on the badge.
bool m_running
Definition: dialog_drc.h:113
bool updateUI() override
Definition: dialog_drc.cpp:157
void SetActiveLayer(PCB_LAYER_ID aLayer) override
Change the currently active layer to aLayer and also update the APPEARANCE_CONTROLS.
void FillItemMap(std::map< KIID, EDA_ITEM * > &aMap)
Definition: board.cpp:924
void SetUnconnectedProvider(RC_ITEMS_PROVIDER *aProvider)
Definition: dialog_drc.cpp:294
wxString m_unconnectedTitleTemplate
Definition: dialog_drc.h:119
void OnSeverity(wxCommandEvent &aEvent) override
Definition: dialog_drc.cpp:631
Container for design settings for a BOARD object.
Tool for pcb inspection.