You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
10 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <dialog_footprint_checker.h>
  24. #include <widgets/appearance_controls.h>
  25. #include <tool/tool_manager.h>
  26. #include <tools/pcb_actions.h>
  27. #include <pcb_marker.h>
  28. #include <drc/drc_results_provider.h>
  29. #include <footprint_edit_frame.h>
  30. #include <convert_drawsegment_list_to_polygon.h>
  31. #include <tools/footprint_editor_control.h>
  32. DIALOG_FOOTPRINT_CHECKER::DIALOG_FOOTPRINT_CHECKER( FOOTPRINT_EDIT_FRAME* aParent ) :
  33. DIALOG_FOOTPRINT_CHECKER_BASE( aParent ),
  34. m_frame( aParent ),
  35. m_checksRun( false ),
  36. m_markersProvider( nullptr ),
  37. m_severities( RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING )
  38. {
  39. m_markersTreeModel = new RC_TREE_MODEL( m_frame, m_markersDataView );
  40. m_markersDataView->AssociateModel( m_markersTreeModel );
  41. m_markersTreeModel->SetSeverities( -1 );
  42. // We use a sdbSizer to get platform-dependent ordering of the action buttons, but
  43. // that requires us to correct the button labels here.
  44. m_sdbSizerOK->SetLabel( _( "Run Checks" ) );
  45. m_sdbSizerCancel->SetLabel( _( "Close" ) );
  46. m_sdbSizerOK->SetDefault();
  47. syncCheckboxes();
  48. finishDialogSettings();
  49. }
  50. DIALOG_FOOTPRINT_CHECKER::~DIALOG_FOOTPRINT_CHECKER()
  51. {
  52. m_markersTreeModel->DecRef();
  53. }
  54. bool DIALOG_FOOTPRINT_CHECKER::TransferDataToWindow()
  55. {
  56. runChecks();
  57. return true;
  58. }
  59. bool DIALOG_FOOTPRINT_CHECKER::TransferDataFromWindow()
  60. {
  61. return true;
  62. }
  63. // Don't globally define this; different facilities use different definitions of "ALL"
  64. static int RPT_SEVERITY_ALL = RPT_SEVERITY_WARNING | RPT_SEVERITY_ERROR | RPT_SEVERITY_EXCLUSION;
  65. void DIALOG_FOOTPRINT_CHECKER::syncCheckboxes()
  66. {
  67. m_showAll->SetValue( m_severities == RPT_SEVERITY_ALL );
  68. m_showErrors->SetValue( m_severities & RPT_SEVERITY_ERROR );
  69. m_showWarnings->SetValue( m_severities & RPT_SEVERITY_WARNING );
  70. m_showExclusions->SetValue( m_severities & RPT_SEVERITY_EXCLUSION );
  71. }
  72. void DIALOG_FOOTPRINT_CHECKER::runChecks()
  73. {
  74. BOARD* board = m_frame->GetBoard();
  75. FOOTPRINT* footprint = board->GetFirstFootprint();
  76. wxString msg;
  77. deleteAllMarkers();
  78. if( !footprint )
  79. {
  80. msg = _( "No footprint loaded." );
  81. return;
  82. }
  83. OUTLINE_ERROR_HANDLER errorHandler =
  84. [&]( const wxString& aMsg, BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, const wxPoint& aPt )
  85. {
  86. std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_MALFORMED_COURTYARD );
  87. drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + aMsg );
  88. drcItem->SetItems( aItemA, aItemB );
  89. PCB_MARKER* marker = new PCB_MARKER( drcItem, aPt );
  90. board->Add( marker );
  91. m_frame->GetCanvas()->GetView()->Add( marker );
  92. };
  93. footprint->BuildPolyCourtyards( &errorHandler );
  94. m_checksRun = true;
  95. SetMarkersProvider( new BOARD_DRC_ITEMS_PROVIDER( m_frame->GetBoard() ) );
  96. refreshEditor();
  97. }
  98. void DIALOG_FOOTPRINT_CHECKER::SetMarkersProvider( RC_ITEMS_PROVIDER* aProvider )
  99. {
  100. m_markersTreeModel->SetProvider( aProvider );
  101. updateDisplayedCounts();
  102. }
  103. void DIALOG_FOOTPRINT_CHECKER::OnRunChecksClick( wxCommandEvent& aEvent )
  104. {
  105. m_checksRun = false;
  106. runChecks();
  107. }
  108. void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
  109. {
  110. BOARD* board = m_frame->GetBoard();
  111. RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
  112. const KIID& itemID = node ? RC_TREE_MODEL::ToUUID( aEvent.GetItem() ) : niluuid;
  113. BOARD_ITEM* item = board->GetItem( itemID );
  114. if( node && item )
  115. {
  116. PCB_LAYER_ID principalLayer = item->GetLayer();
  117. LSET violationLayers;
  118. std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
  119. if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
  120. {
  121. BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
  122. if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
  123. && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
  124. {
  125. principalLayer = B_CrtYd;
  126. }
  127. else
  128. {
  129. principalLayer = F_CrtYd;
  130. }
  131. }
  132. else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
  133. {
  134. principalLayer = Edge_Cuts;
  135. }
  136. else
  137. {
  138. BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
  139. BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() );
  140. BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() );
  141. BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
  142. if( a || b || c || d )
  143. violationLayers = LSET::AllLayersMask();
  144. if( a )
  145. violationLayers &= a->GetLayerSet();
  146. if( b )
  147. violationLayers &= b->GetLayerSet();
  148. if( c )
  149. violationLayers &= c->GetLayerSet();
  150. if( d )
  151. violationLayers &= d->GetLayerSet();
  152. }
  153. if( violationLayers.count() )
  154. principalLayer = violationLayers.Seq().front();
  155. else
  156. violationLayers.set( principalLayer );
  157. WINDOW_THAWER thawer( m_frame );
  158. m_frame->FocusOnItem( item );
  159. m_frame->GetCanvas()->Refresh();
  160. if( ( violationLayers & board->GetVisibleLayers() ) == 0 )
  161. {
  162. m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
  163. m_frame->GetCanvas()->Refresh();
  164. }
  165. if( board->GetVisibleLayers().test( principalLayer ) )
  166. m_frame->SetActiveLayer( principalLayer );
  167. }
  168. aEvent.Skip();
  169. }
  170. void DIALOG_FOOTPRINT_CHECKER::OnLeftDClickItem( wxMouseEvent& event )
  171. {
  172. if( m_markersDataView->GetCurrentItem().IsOk() )
  173. {
  174. // turn control over to m_frame, hide this DIALOG_FOOTPRINT_CHECKER window,
  175. // no destruction so we can preserve listbox cursor
  176. if( !IsModal() )
  177. Show( false );
  178. }
  179. // Do not skip aVent here: tihs is not useful, and Pcbnew crashes
  180. // if skipped (at least on Windows)
  181. }
  182. void DIALOG_FOOTPRINT_CHECKER::OnSeverity( wxCommandEvent& aEvent )
  183. {
  184. int flag = 0;
  185. if( aEvent.GetEventObject() == m_showAll )
  186. flag = RPT_SEVERITY_ALL;
  187. else if( aEvent.GetEventObject() == m_showErrors )
  188. flag = RPT_SEVERITY_ERROR;
  189. else if( aEvent.GetEventObject() == m_showWarnings )
  190. flag = RPT_SEVERITY_WARNING;
  191. else if( aEvent.GetEventObject() == m_showExclusions )
  192. flag = RPT_SEVERITY_EXCLUSION;
  193. if( aEvent.IsChecked() )
  194. m_severities |= flag;
  195. else if( aEvent.GetEventObject() == m_showAll )
  196. m_severities = RPT_SEVERITY_ERROR;
  197. else
  198. m_severities &= ~flag;
  199. syncCheckboxes();
  200. // Set the provider's severity levels through the TreeModel so that the old tree
  201. // can be torn down before the severity changes.
  202. //
  203. // It's not clear this is required, but we've had a lot of issues with wxDataView
  204. // being cranky on various platforms.
  205. m_markersTreeModel->SetSeverities( m_severities );
  206. updateDisplayedCounts();
  207. }
  208. void DIALOG_FOOTPRINT_CHECKER::OnCancelClick( wxCommandEvent& aEvent )
  209. {
  210. m_frame->FocusOnItem( nullptr );
  211. SetReturnCode( wxID_CANCEL );
  212. // Leave the tool to destroy (or not) the dialog
  213. FOOTPRINT_EDITOR_CONTROL* tool = m_frame->GetToolManager()->GetTool<FOOTPRINT_EDITOR_CONTROL>();
  214. tool->DestroyCheckerDialog();
  215. }
  216. void DIALOG_FOOTPRINT_CHECKER::OnClose( wxCloseEvent& aEvent )
  217. {
  218. wxCommandEvent dummy;
  219. OnCancelClick( dummy );
  220. }
  221. void DIALOG_FOOTPRINT_CHECKER::refreshEditor()
  222. {
  223. WINDOW_THAWER thawer( m_frame );
  224. m_frame->GetCanvas()->Refresh();
  225. }
  226. void DIALOG_FOOTPRINT_CHECKER::OnDeleteOneClick( wxCommandEvent& aEvent )
  227. {
  228. // Clear the selection. It may be the selected DRC marker.
  229. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true );
  230. m_markersTreeModel->DeleteCurrentItem( true );
  231. // redraw the pcb
  232. refreshEditor();
  233. updateDisplayedCounts();
  234. }
  235. void DIALOG_FOOTPRINT_CHECKER::OnDeleteAllClick( wxCommandEvent& event )
  236. {
  237. deleteAllMarkers();
  238. m_checksRun = false;
  239. refreshEditor();
  240. updateDisplayedCounts();
  241. }
  242. void DIALOG_FOOTPRINT_CHECKER::deleteAllMarkers()
  243. {
  244. // Clear current selection list to avoid selection of deleted items
  245. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear, true );
  246. m_markersTreeModel->DeleteItems( false, true, true );
  247. }
  248. void DIALOG_FOOTPRINT_CHECKER::updateDisplayedCounts()
  249. {
  250. // Collect counts:
  251. int numErrors = 0;
  252. int numWarnings = 0;
  253. int numExcluded = 0;
  254. if( m_markersProvider )
  255. {
  256. numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
  257. numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
  258. numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
  259. }
  260. // Update badges:
  261. if( !m_checksRun && numErrors == 0 )
  262. numErrors = -1;
  263. if( !m_checksRun && numWarnings == 0 )
  264. numWarnings = -1;
  265. m_errorsBadge->SetMaximumNumber( numErrors );
  266. m_errorsBadge->UpdateNumber( numErrors, RPT_SEVERITY_ERROR );
  267. m_warningsBadge->SetMaximumNumber( numWarnings );
  268. m_warningsBadge->UpdateNumber( numWarnings, RPT_SEVERITY_WARNING );
  269. m_exclusionsBadge->SetMaximumNumber( numExcluded );
  270. m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
  271. }