KiCad PCB EDA Suite
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 (C) 2020-2022 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 ),
46 m_centerMarkerOnIdle( nullptr )
47{
48 m_markersProvider = std::make_shared<DRC_ITEMS_PROVIDER>( m_frame->GetBoard(),
50
52 m_markersDataView->AssociateModel( m_markersTreeModel );
54
56 {
59 }
60
61 SetupStandardButtons( { { wxID_OK, _( "Run Checks" ) },
62 { wxID_CANCEL, _( "Close" ) } } );
63
65
67}
68
69
71{
72 m_frame->FocusOnItem( nullptr );
73
76
77 m_markersTreeModel->DecRef();
78}
79
80
82{
83 return true;
84}
85
86
88{
89 return true;
90}
91
92
93// Don't globally define this; different facilities use different definitions of "ALL"
95
96
98{
103}
104
105
107{
108 BOARD* board = m_frame->GetBoard();
109 FOOTPRINT* footprint = board->GetFirstFootprint();
110 wxString msg;
111
113
114 if( !footprint )
115 {
116 msg = _( "No footprint loaded." );
117 return;
118 }
119
120 auto errorHandler =
121 [&]( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, const BOARD_ITEM* aItemC,
122 int aErrorCode, const wxString& aMsg, const VECTOR2I& aPt )
123 {
124 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( aErrorCode );
125
126 if( !aMsg.IsEmpty() )
127 drcItem->SetErrorMessage( drcItem->GetErrorText() + wxS( " " ) + aMsg );
128
129 drcItem->SetItems( aItemA, aItemB, aItemC );
130
131 PCB_MARKER* marker = new PCB_MARKER( drcItem, aPt );
132 board->Add( marker );
133 m_frame->GetCanvas()->GetView()->Add( marker );
134 };
135
136 OUTLINE_ERROR_HANDLER outlineErrorHandler =
137 [&]( const wxString& aMsg, BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, const VECTOR2I& aPt )
138 {
139 errorHandler( aItemA, aItemB, nullptr, DRCE_MALFORMED_COURTYARD, aMsg, aPt );
140 };
141
142 footprint->BuildCourtyardCaches( &outlineErrorHandler );
143
144 footprint->CheckFootprintAttributes(
145 [&]( const wxString& aMsg )
146 {
147 errorHandler( footprint, nullptr, nullptr, DRCE_FOOTPRINT_TYPE_MISMATCH, aMsg,
148 { 0, 0 } );
149 } );
150
151 footprint->CheckPads(
152 [&]( const PAD* aPad, int aErrorCode, const wxString& aMsg )
153 {
154 errorHandler( aPad, nullptr, nullptr, aErrorCode, aMsg, aPad->GetPosition() );
155 } );
156
157 footprint->CheckShortingPads(
158 [&]( const PAD* aPadA, const PAD* aPadB, const VECTOR2I& aPosition )
159 {
160 errorHandler( aPadA, aPadB, nullptr, DRCE_SHORTING_ITEMS, wxEmptyString,
161 aPosition );
162 } );
163
164 if( footprint->IsNetTie() )
165 {
166 footprint->CheckNetTiePadGroups(
167 [&]( const wxString& aMsg )
168 {
169 errorHandler( footprint, nullptr, nullptr, DRCE_FOOTPRINT, aMsg, { 0, 0 } );
170 } );
171
172 footprint->CheckNetTies(
173 [&]( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, const BOARD_ITEM* aItemC,
174 const VECTOR2I& aPosition )
175 {
176 errorHandler( aItemA, aItemB, aItemC, DRCE_SHORTING_ITEMS, wxEmptyString,
177 aPosition );
178 } );
179 }
180
181 m_checksRun = true;
182
185
187}
188
189
191{
193
194 // wxWidgets on some platforms fails to correctly ensure that a selected item is
195 // visible, so we have to do it in a separate idle event.
196 m_centerMarkerOnIdle = aMarker;
197 Bind( wxEVT_IDLE, &DIALOG_FOOTPRINT_CHECKER::centerMarkerIdleHandler, this );
198}
199
200
202{
204 m_centerMarkerOnIdle = nullptr;
205 Unbind( wxEVT_IDLE, &DIALOG_FOOTPRINT_CHECKER::centerMarkerIdleHandler, this );
206}
207
208
209void DIALOG_FOOTPRINT_CHECKER::OnRunChecksClick( wxCommandEvent& aEvent )
210{
211 m_checksRun = false;
212
213 runChecks();
214}
215
216
217void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
218{
219 BOARD* board = m_frame->GetBoard();
220 RC_TREE_NODE* node = RC_TREE_MODEL::ToNode( aEvent.GetItem() );
221 const KIID& itemID = node ? RC_TREE_MODEL::ToUUID( aEvent.GetItem() ) : niluuid;
222 BOARD_ITEM* item = board->GetItem( itemID );
223
225 {
226 // we already came from a cross-probe of the marker in the document; don't go
227 // around in circles
228
229 aEvent.Skip();
230 return;
231 }
232
233 if( node && item )
234 {
235 PCB_LAYER_ID principalLayer = UNDEFINED_LAYER;
236 LSET violationLayers;
237 std::shared_ptr<RC_ITEM> rc_item = node->m_RcItem;
238
239 if( item->GetLayerSet().count() > 0 )
240 principalLayer = item->GetLayerSet().Seq().front();
241
242 if( rc_item->GetErrorCode() == DRCE_MALFORMED_COURTYARD )
243 {
244 BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
245
246 if( a && ( a->GetFlags() & MALFORMED_B_COURTYARD ) > 0
247 && ( a->GetFlags() & MALFORMED_F_COURTYARD ) == 0 )
248 {
249 principalLayer = B_CrtYd;
250 }
251 else
252 {
253 principalLayer = F_CrtYd;
254 }
255 }
256 else if (rc_item->GetErrorCode() == DRCE_INVALID_OUTLINE )
257 {
258 principalLayer = Edge_Cuts;
259 }
260 else
261 {
262 BOARD_ITEM* a = board->GetItem( rc_item->GetMainItemID() );
263 BOARD_ITEM* b = board->GetItem( rc_item->GetAuxItemID() );
264 BOARD_ITEM* c = board->GetItem( rc_item->GetAuxItem2ID() );
265 BOARD_ITEM* d = board->GetItem( rc_item->GetAuxItem3ID() );
266
267 if( a || b || c || d )
268 violationLayers = LSET::AllLayersMask();
269
270 if( a )
271 violationLayers &= a->GetLayerSet();
272
273 if( b )
274 violationLayers &= b->GetLayerSet();
275
276 if( c )
277 violationLayers &= c->GetLayerSet();
278
279 if( d )
280 violationLayers &= d->GetLayerSet();
281 }
282
283 if( violationLayers.count() )
284 principalLayer = violationLayers.Seq().front();
285 else
286 violationLayers.set( principalLayer );
287
288 WINDOW_THAWER thawer( m_frame );
289
290 m_frame->FocusOnItem( item );
292
293 if( ( violationLayers & board->GetVisibleLayers() ) == 0 )
294 {
295 m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
297 }
298
299 if( board->GetVisibleLayers().test( principalLayer ) )
300 m_frame->SetActiveLayer( principalLayer );
301 }
302
303 aEvent.Skip();
304}
305
306
308{
309 if( m_markersDataView->GetCurrentItem().IsOk() )
310 {
311 // turn control over to m_frame, hide this DIALOG_FOOTPRINT_CHECKER window,
312 // no destruction so we can preserve listbox cursor
313 if( !IsModal() )
314 Show( false );
315 }
316
317 // Do not skip aVent here: this is not useful, and Pcbnew crashes
318 // if skipped (at least on Windows)
319}
320
321
322void DIALOG_FOOTPRINT_CHECKER::OnSeverity( wxCommandEvent& aEvent )
323{
324 int flag = 0;
325
326 if( aEvent.GetEventObject() == m_showAll )
328 else if( aEvent.GetEventObject() == m_showErrors )
330 else if( aEvent.GetEventObject() == m_showWarnings )
332 else if( aEvent.GetEventObject() == m_showExclusions )
334
335 if( aEvent.IsChecked() )
337 else if( aEvent.GetEventObject() == m_showAll )
339 else
340 m_severities &= ~flag;
341
343
346}
347
348
349void DIALOG_FOOTPRINT_CHECKER::OnCancelClick( wxCommandEvent& aEvent )
350{
351 m_frame->FocusOnItem( nullptr );
352
353 SetReturnCode( wxID_CANCEL );
354
355 // Leave the tool to destroy (or not) the dialog
357 tool->DestroyCheckerDialog();
358}
359
360
361void DIALOG_FOOTPRINT_CHECKER::OnClose( wxCloseEvent& aEvent )
362{
363 wxCommandEvent dummy;
365}
366
367
369{
370 WINDOW_THAWER thawer( m_frame );
371
373}
374
375
376void DIALOG_FOOTPRINT_CHECKER::OnDeleteOneClick( wxCommandEvent& aEvent )
377{
378 // Clear the selection. It may be the selected DRC marker.
380
382
383 // redraw the pcb
385
387}
388
389
391{
393
394 m_checksRun = false;
397}
398
399
401{
402 // Clear current selection list to avoid selection of deleted items
404
405 m_markersTreeModel->DeleteItems( false, true, false );
406 m_frame->GetBoard()->DeleteMARKERs( true, true );
407}
408
409
411{
412 // Collect counts:
413
414 int numErrors = 0;
415 int numWarnings = 0;
416 int numExcluded = 0;
417
419 {
420 numErrors += m_markersProvider->GetCount( RPT_SEVERITY_ERROR );
421 numWarnings += m_markersProvider->GetCount( RPT_SEVERITY_WARNING );
422 numExcluded += m_markersProvider->GetCount( RPT_SEVERITY_EXCLUSION );
423 }
424
425 // Update badges:
426
427 if( !m_checksRun && numErrors == 0 )
428 numErrors = -1;
429
430 if( !m_checksRun && numWarnings == 0 )
431 numWarnings = -1;
432
433 m_errorsBadge->SetMaximumNumber( numErrors );
435
436 m_warningsBadge->SetMaximumNumber( numWarnings );
438
439 m_exclusionsBadge->SetMaximumNumber( numExcluded );
441}
442
void SetLayerVisible(int aLayer, bool isVisible)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:172
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:265
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:515
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:686
BOARD_ITEM * GetItem(const KIID &aID) const
Definition: board.cpp:933
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:397
void DeleteMARKERs()
Delete all MARKERS from the board.
Definition: board.cpp:892
Class DIALOG_FOOTPRINT_CHECKER_BASE.
void centerMarkerIdleHandler(wxIdleEvent &aEvent)
void OnCancelClick(wxCommandEvent &aEvent) override
void OnDeleteAllClick(wxCommandEvent &event) override
void OnClose(wxCloseEvent &event) override
FOOTPRINT_EDIT_FRAME * m_frame
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:325
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...
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
Module editor specific tools.
void SetActiveLayer(PCB_LAYER_ID aLayer) override
void CheckShortingPads(const std::function< void(const PAD *, const PAD *, const VECTOR2I &)> &aErrorHandler)
Check for overlapping, different-numbered, non-net-tie pads.
Definition: footprint.cpp:2399
void CheckPads(const std::function< void(const PAD *, int, const wxString &)> &aErrorHandler)
Run non-board-specific DRC checks on footprint's pads.
Definition: footprint.cpp:2319
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.
Definition: footprint.cpp:2445
void BuildCourtyardCaches(OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Build complex polygons of the courtyard areas from graphic items on the courtyard layers.
Definition: footprint.cpp:2195
bool IsNetTie() const
Definition: footprint.h:252
void CheckNetTiePadGroups(const std::function< void(const wxString &)> &aErrorHandler)
Sanity check net-tie pad groups.
Definition: footprint.cpp:2551
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: footprint.cpp:2294
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition: pcb_view.cpp:58
Definition: kiid.h:47
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
static LSET AllLayersMask()
Definition: lset.cpp:808
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
void SetMaximumNumber(int aMax)
Set the maximum number to be shown on the badge.
void UpdateNumber(int aNumber, SEVERITY aSeverity)
Update the number displayed on the badge.
Definition: pad.h:58
VECTOR2I GetPosition() const override
Definition: pad.h:196
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
APPEARANCE_CONTROLS * GetAppearancePanel()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
void FocusOnItem(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
void SelectMarker(const MARKER_BASE *aMarker)
Definition: rc_item.cpp:607
static RC_TREE_NODE * ToNode(wxDataViewItem aItem)
Definition: rc_item.h:209
void Update(std::shared_ptr< RC_ITEMS_PROVIDER > aProvider, int aSeverities)
Definition: rc_item.cpp:289
void DeleteItems(bool aCurrentOnly, bool aIncludeExclusions, bool aDeep)
Deletes the current item or all items.
Definition: rc_item.cpp:469
void DeleteCurrentItem(bool aDeep)
Definition: rc_item.cpp:463
void CenterMarker(const MARKER_BASE *aMarker)
Definition: rc_item.cpp:620
static KIID ToUUID(wxDataViewItem aItem)
Definition: rc_item.cpp:149
std::shared_ptr< RC_ITEM > m_RcItem
Definition: rc_item.h:194
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:142
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
static int RPT_SEVERITY_ALL
static bool g_lastChecksRun
static FOOTPRINT * g_lastFootprint
@ DRCE_INVALID_OUTLINE
Definition: drc_item.h:68
@ DRCE_SHORTING_ITEMS
Definition: drc_item.h:40
@ DRCE_MALFORMED_COURTYARD
Definition: drc_item.h:63
@ DRCE_FOOTPRINT_TYPE_MISMATCH
Definition: drc_item.h:75
@ DRCE_FOOTPRINT
Definition: drc_item.h:79
#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:59
@ F_CrtYd
Definition: layer_ids.h:117
@ Edge_Cuts
Definition: layer_ids.h:113
@ B_CrtYd
Definition: layer_ids.h:116
@ UNDEFINED_LAYER
Definition: layer_ids.h:60
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_EXCLUSION
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:74