KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_footprint_checker.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 The 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
26#include <tool/tool_manager.h>
27#include <tools/pcb_actions.h>
28#include <footprint.h>
29#include <pad.h>
30#include <pcb_marker.h>
31#include <drc/drc_item.h>
35
36
37static FOOTPRINT* g_lastFootprint = nullptr;
38static bool g_lastChecksRun = false;
39
40
43 m_frame( aParent ),
44 m_checksRun( false ),
45 m_centerMarkerOnIdle( nullptr )
46{
47 m_markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_frame->GetBoard(), MARKER_BASE::MARKER_DRC );
48
50 m_markersDataView->AssociateModel( m_markersTreeModel );
51 m_markersDataView->SetLayoutDirection( wxLayout_LeftToRight );
52
53 if( m_frame->GetBoard()->GetFirstFootprint() == g_lastFootprint )
55
56 SetupStandardButtons( { { wxID_OK, _( "Run Checks" ) },
57 { wxID_CANCEL, _( "Close" ) } } );
58
60}
61
62
64{
65 m_frame->ClearFocus();
66
67 g_lastFootprint = m_frame->GetBoard()->GetFirstFootprint();
69
70 m_markersTreeModel->DecRef();
71}
72
73
79
80
86
87
89{
90 BOARD* board = m_frame->GetBoard();
91 FOOTPRINT* footprint = board->GetFirstFootprint();
92 wxString msg;
93
95
96 if( !footprint )
97 {
98 msg = _( "No footprint loaded." );
99 return;
100 }
101
102 auto errorHandler =
103 [&]( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, const BOARD_ITEM* aItemC,
104 int aErrorCode, const wxString& aMsg, const VECTOR2I& aPt )
105 {
106 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( aErrorCode );
107
108 if( !aMsg.IsEmpty() )
109 drcItem->SetErrorDetail( aMsg );
110
111 drcItem->SetItems( aItemA, aItemB, aItemC );
112
113 PCB_MARKER* marker = new PCB_MARKER( drcItem, aPt );
114 board->Add( marker );
115 m_frame->GetCanvas()->GetView()->Add( marker );
116 };
117
118 OUTLINE_ERROR_HANDLER outlineErrorHandler =
119 [&]( const wxString& aMsg, BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, const VECTOR2I& aPt )
120 {
121 if( !aItemA ) // If we only have a single item, make sure it's A
122 std::swap( aItemA, aItemB );
123
124 errorHandler( aItemA, aItemB, nullptr, DRCE_MALFORMED_COURTYARD, aMsg, aPt );
125 };
126
127 footprint->BuildCourtyardCaches( &outlineErrorHandler );
128
129 if( !footprint->AllowMissingCourtyard()
130 && footprint->GetCourtyard( F_CrtYd ).OutlineCount() == 0
131 && footprint->GetCourtyard( B_CrtYd ).OutlineCount() == 0 )
132 {
133 errorHandler( footprint, nullptr, nullptr, DRCE_MISSING_COURTYARD, wxEmptyString, { 0, 0 } );
134 }
135
136 footprint->CheckFootprintAttributes(
137 [&]( const wxString& aMsg )
138 {
139 errorHandler( footprint, nullptr, nullptr, DRCE_FOOTPRINT_TYPE_MISMATCH, aMsg, { 0, 0 } );
140 } );
141
142 footprint->CheckPads( m_frame,
143 [&]( const PAD* aPad, int aErrorCode, const wxString& aMsg )
144 {
145 errorHandler( aPad, nullptr, nullptr, aErrorCode, aMsg, aPad->GetPosition() );
146 } );
147
148 footprint->CheckShortingPads(
149 [&]( const PAD* aPadA, const PAD* aPadB, int aErrorCode, const VECTOR2I& aPosition )
150 {
151 errorHandler( aPadA, aPadB, nullptr, aErrorCode, wxEmptyString, aPosition );
152 } );
153
154 if( footprint->IsNetTie() )
155 {
156 footprint->CheckNetTiePadGroups(
157 [&]( const wxString& aMsg )
158 {
159 errorHandler( footprint, nullptr, nullptr, DRCE_FOOTPRINT, aMsg, { 0, 0 } );
160 } );
161
162 footprint->CheckNetTies(
163 [&]( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, const BOARD_ITEM* aItemC,
164 const VECTOR2I& aPt )
165 {
166 errorHandler( aItemA, aItemB, aItemC, DRCE_SHORTING_ITEMS, wxEmptyString, aPt );
167 } );
168 }
169
170 footprint->CheckClippedSilk(
171 [&]( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, const VECTOR2I& aPt )
172 {
173 errorHandler( aItemA, aItemB, nullptr, DRCE_SILK_MASK_CLEARANCE, wxEmptyString, aPt );
174 } );
175
176 m_checksRun = true;
177 updateData();
179}
180
181
183{
184 m_markersTreeModel->SelectMarker( aMarker );
185
186 // wxWidgets on some platforms fails to correctly ensure that a selected item is
187 // visible, so we have to do it in a separate idle event.
188 m_centerMarkerOnIdle = aMarker;
189 Bind( wxEVT_IDLE, &DIALOG_FOOTPRINT_CHECKER::centerMarkerIdleHandler, this );
190}
191
192
194{
195 if( m_markersTreeModel->GetView()->IsFrozen() )
196 return;
197
199 m_centerMarkerOnIdle = nullptr;
200 Unbind( wxEVT_IDLE, &DIALOG_FOOTPRINT_CHECKER::centerMarkerIdleHandler, this );
201}
202
203
204void DIALOG_FOOTPRINT_CHECKER::OnRunChecksClick( wxCommandEvent& aEvent )
205{
206 m_checksRun = false;
207 runChecks();
208}
209
210
211void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
212{
213 BOARD* board = m_frame->GetBoard();
214 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
215 const KIID& itemID = node ? RC_TREE_MODEL::ToUUID( aEvent.GetItem() ) : niluuid;
216 BOARD_ITEM* item = board->ResolveItem( itemID, true );
217
219 {
220 // we already came from a cross-probe of the marker in the document; don't go around in circles
221 aEvent.Skip();
222 return;
223 }
224
225 if( node && item )
226 {
227 PCB_LAYER_ID principalLayer = UNDEFINED_LAYER;
228 LSET violationLayers;
229 std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
230
231 if( item->GetLayerSet().count() > 0 )
232 principalLayer = item->GetLayerSet().Seq().front();
233
234 if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
235 {
236 BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
237
238 if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
239 && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
240 {
241 principalLayer = B_CrtYd;
242 }
243 else
244 {
245 principalLayer = F_CrtYd;
246 }
247 }
248 else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
249 {
250 principalLayer = Edge_Cuts;
251 }
252 else
253 {
254 BOARD_ITEM* a = board->ResolveItem( rc_item->GetMainItemID(), true );
255 BOARD_ITEM* b = board->ResolveItem( rc_item->GetAuxItemID(), true );
256 BOARD_ITEM* c = board->ResolveItem( rc_item->GetAuxItem2ID(), true );
257 BOARD_ITEM* d = board->ResolveItem( rc_item->GetAuxItem3ID(), true );
258
259 if( a || b || c || d )
260 violationLayers = LSET::AllLayersMask();
261
262 if( a )
263 violationLayers &= a->GetLayerSet();
264
265 if( b )
266 violationLayers &= b->GetLayerSet();
267
268 if( c )
269 violationLayers &= c->GetLayerSet();
270
271 if( d )
272 violationLayers &= d->GetLayerSet();
273 }
274
275 if( violationLayers.count() )
276 principalLayer = violationLayers.Seq().front();
277 else if( principalLayer >= 0 )
278 violationLayers.set( principalLayer );
279
280 WINDOW_THAWER thawer( m_frame );
281
282 m_frame->FocusOnItem( item );
283 m_frame->GetCanvas()->Refresh();
284
285 if( ( violationLayers & board->GetVisibleLayers() ).none() )
286 {
287 m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
288 m_frame->GetCanvas()->Refresh();
289 }
290
291 if( board->GetVisibleLayers().test( principalLayer ) )
292 m_frame->SetActiveLayer( principalLayer );
293 }
294
295 aEvent.Skip();
296}
297
298
300{
301 if( m_markersDataView->GetCurrentItem().IsOk() )
302 {
303 // turn control over to m_frame, hide this DIALOG_FOOTPRINT_CHECKER window,
304 // no destruction so we can preserve listbox cursor
305 if( !IsModal() )
306 Show( false );
307 }
308
309 // Do not skip event here: this is not useful, and Pcbnew crashes if skipped (at least on MSW)
310}
311
312
314{
315 int severities = 0;
316
317 if( m_showErrors->GetValue() )
318 severities |= RPT_SEVERITY_ERROR;
319
320 if( m_showWarnings->GetValue() )
321 severities |= RPT_SEVERITY_WARNING;
322
323 if( m_showExclusions->GetValue() )
324 severities |= RPT_SEVERITY_EXCLUSION;
325
326 return severities;
327}
328
329
330void DIALOG_FOOTPRINT_CHECKER::OnSeverity( wxCommandEvent& aEvent )
331{
332 if( aEvent.GetEventObject() == m_showAll )
333 {
334 m_showErrors->SetValue( true );
335 m_showWarnings->SetValue( aEvent.IsChecked() );
336 m_showExclusions->SetValue( aEvent.IsChecked() );
337 }
338
339 updateData();
340}
341
342
343void DIALOG_FOOTPRINT_CHECKER::OnCancelClick( wxCommandEvent& aEvent )
344{
345 m_frame->ClearFocus();
346
347 SetReturnCode( wxID_CANCEL );
348
349 // Leave the tool to destroy (or not) the dialog
350 FOOTPRINT_EDITOR_CONTROL* tool = m_frame->GetToolManager()->GetTool<FOOTPRINT_EDITOR_CONTROL>();
351 tool->DestroyCheckerDialog();
352}
353
354
355void DIALOG_FOOTPRINT_CHECKER::OnClose( wxCloseEvent& aEvent )
356{
357 wxCommandEvent dummy;
359}
360
361
363{
364 WINDOW_THAWER thawer( m_frame );
365
366 m_frame->GetCanvas()->Refresh();
367}
368
369
370void DIALOG_FOOTPRINT_CHECKER::OnDeleteOneClick( wxCommandEvent& aEvent )
371{
372 // Clear the selection. It may be the selected DRC marker.
373 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
374
375 m_markersTreeModel->DeleteCurrentItem( true );
376
377 // redraw the pcb
379
381}
382
383
385{
387
388 m_checksRun = false;
391}
392
393
395{
396 // Clear current selection list to avoid selection of deleted items
397 m_frame->GetToolManager()->RunAction( ACTIONS::selectionClear );
398
399 m_markersTreeModel->DeleteItems( false, true, false );
400 m_frame->GetBoard()->DeleteMARKERs( true, true );
401}
402
403
405{
406 // Collect counts:
407
408 int numErrors = 0;
409 int numWarnings = 0;
410 int numExcluded = 0;
411
413 {
414 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
415 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
416 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
417 }
418
419 // Update badges:
420
421 if( !m_checksRun && numErrors == 0 )
422 numErrors = -1;
423
424 if( !m_checksRun && numWarnings == 0 )
425 numWarnings = -1;
426
427 m_errorsBadge->SetMaximumNumber( numErrors );
428 m_errorsBadge->UpdateNumber( numErrors, RPT_SEVERITY_ERROR );
429
430 m_warningsBadge->SetMaximumNumber( numWarnings );
431 m_warningsBadge->UpdateNumber( numWarnings, RPT_SEVERITY_WARNING );
432
433 m_exclusionsBadge->SetMaximumNumber( numExcluded );
434 m_exclusionsBadge->UpdateNumber( numExcluded, RPT_SEVERITY_EXCLUSION );
435}
436
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
BASE_SET & set(size_t pos)
Definition base_set.h:116
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:256
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1224
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:983
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition board.h:530
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:1777
DIALOG_FOOTPRINT_CHECKER_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Footprint Checker"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void centerMarkerIdleHandler(wxIdleEvent &aEvent)
void OnCancelClick(wxCommandEvent &aEvent) override
void OnDeleteAllClick(wxCommandEvent &event) override
void OnClose(wxCloseEvent &event) override
std::shared_ptr< RC_ITEMS_PROVIDER > m_markersProvider
void OnSeverity(wxCommandEvent &aEvent) override
DIALOG_FOOTPRINT_CHECKER(FOOTPRINT_EDIT_FRAME *aParent)
void OnRunChecksClick(wxCommandEvent &aEvent) override
void OnSelectItem(wxDataViewEvent &event) override
void SelectMarker(const PCB_MARKER *aMarker)
void OnDeleteOneClick(wxCommandEvent &event) override
void OnLeftDClickItem(wxMouseEvent &event) override
const PCB_MARKER * m_centerMarkerOnIdle
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...
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:400
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:150
Module editor specific tools.
void CheckClippedSilk(const std::function< void(BOARD_ITEM *aItemA, BOARD_ITEM *aItemB, const VECTOR2I &aPt)> &aErrorHandler)
void CheckNetTies(const std::function< void(const BOARD_ITEM *aItem, const BOARD_ITEM *bItem, const BOARD_ITEM *cItem, const VECTOR2I &)> &aErrorHandler)
Check for un-allowed shorting of pads in net-tie footprints.
void CheckPads(UNITS_PROVIDER *aUnitsProvider, const std::function< void(const PAD *, int, const wxString &)> &aErrorHandler)
Run non-board-specific DRC checks on footprint's pads.
bool AllowMissingCourtyard() const
Definition footprint.h:420
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
bool IsNetTie() const
Definition footprint.h:430
void CheckShortingPads(const std::function< void(const PAD *, const PAD *, int aErrorCode, const VECTOR2I &)> &aErrorHandler)
Check for overlapping, different-numbered, non-net-tie pads.
void CheckNetTiePadGroups(const std::function< void(const wxString &)> &aErrorHandler)
Sanity check net-tie pad groups.
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
void CheckFootprintAttributes(const std::function< void(const wxString &)> &aErrorHandler)
Test if footprint attributes for type (SMD/Through hole/Other) match the expected type based on the p...
Definition kiid.h:49
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:313
static const LSET & AllLayersMask()
Definition lset.cpp:641
Definition pad.h:55
VECTOR2I GetPosition() const override
Definition pad.h:209
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition rc_item.h:250
static KIID ToUUID(wxDataViewItem aItem)
Definition rc_item.cpp:226
std::shared_ptr< RC_ITEM > m_RcItem
Definition rc_item.h:235
int OutlineCount() const
Return the number of outlines in the set.
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
static bool g_lastChecksRun
static FOOTPRINT * g_lastFootprint
@ DRCE_SILK_MASK_CLEARANCE
Definition drc_item.h:97
@ DRCE_INVALID_OUTLINE
Definition drc_item.h:73
@ DRCE_MISSING_COURTYARD
Definition drc_item.h:67
@ DRCE_SHORTING_ITEMS
Definition drc_item.h:41
@ DRCE_MALFORMED_COURTYARD
Definition drc_item.h:68
@ DRCE_FOOTPRINT_TYPE_MISMATCH
Definition drc_item.h:82
@ DRCE_FOOTPRINT
Definition drc_item.h:86
#define _(s)
#define MALFORMED_F_COURTYARD
#define MALFORMED_B_COURTYARD
KIID niluuid(0)
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ Edge_Cuts
Definition layer_ids.h:112
@ B_CrtYd
Definition layer_ids.h:115
@ UNDEFINED_LAYER
Definition layer_ids.h:61
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_EXCLUSION
std::vector< FAB_LAYER_COLOR > dummy
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695