KiCad PCB EDA Suite
Loading...
Searching...
No Matches
board_inspection_tool.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, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <bitmaps.h>
23#include <board_loader.h>
24#include <collectors.h>
29#include <footprint.h>
30#include <pcb_io/pcb_io_mgr.h>
32#include <kiway.h>
33#include <local_history.h>
35#include <wx/filedlg.h>
36#include <wx/filename.h>
37#include <pcb_group.h>
38#include <tool/tool_manager.h>
41#include <tools/edit_tool.h>
42#include <tools/drc_tool.h>
43#include <pcb_painter.h>
45#include <drc/drc_engine.h>
50#include <dialogs/dialog_drc.h>
51#include <kiplatform/ui.h>
52#include <status_popup.h>
53#include <string_utils.h>
56#include <pcb_marker.h>
57#include <pcb_painter.h>
58#include <pcb_shape.h>
62#include <wx/choice.h>
63#include <wx/sizer.h>
64#include <wx/stattext.h>
65#include <drc/drc_item.h>
66#include <pad.h>
67#include <pcb_track.h>
68#include <project_pcb.h>
69#include <view/view_controls.h>
70
71
73 PCB_TOOL_BASE( "pcbnew.InspectionTool" ),
74 m_frame( nullptr )
75{
76 m_dynamicData = nullptr;
77}
78
79
81{
82public:
95
96private:
97 ACTION_MENU* create() const override
98 {
99 return new NET_CONTEXT_MENU();
100 }
101};
102
103
105{
106 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
107
108 std::shared_ptr<NET_CONTEXT_MENU> netSubMenu = std::make_shared<NET_CONTEXT_MENU>();
109 netSubMenu->SetTool( this );
110
111 // Only show the net menu if all items in the selection are connectable
112 auto showNetMenuFunc =
113 []( const SELECTION& aSelection )
114 {
115 if( aSelection.Empty() )
116 return false;
117
118 for( const EDA_ITEM* item : aSelection )
119 {
120 switch( item->Type() )
121 {
122 case PCB_TRACE_T:
123 case PCB_ARC_T:
124 case PCB_VIA_T:
125 case PCB_PAD_T:
126 case PCB_ZONE_T:
127 continue;
128
129 case PCB_SHAPE_T:
130 {
131 if( !static_cast<const PCB_SHAPE*>( item )->IsOnCopperLayer() )
132 return false;
133 else
134 continue;
135 }
136
137 default:
138 return false;
139 }
140 }
141
142 return true;
143 };
144
145 CONDITIONAL_MENU& menu = selectionTool->GetToolMenu().GetMenu();
146
147 selectionTool->GetToolMenu().RegisterSubMenu( netSubMenu );
148
149 menu.AddMenu( netSubMenu.get(), showNetMenuFunc, 100 );
150
151 return true;
152}
153
154
159
160
162{
164 dialog.ShowModal();
165 return 0;
166}
167
168
169std::unique_ptr<DRC_ENGINE> BOARD_INSPECTION_TOOL::makeDRCEngine( bool* aCompileError,
170 bool* aCourtyardError )
171{
172 auto engine = std::make_unique<DRC_ENGINE>( m_frame->GetBoard(),
173 &m_frame->GetBoard()->GetDesignSettings() );
174
175 try
176 {
177 engine->InitEngine( m_frame->GetBoard()->GetDesignRulesPath() );
178 }
179 catch( PARSE_ERROR& )
180 {
181 if( aCompileError )
182 *aCompileError = true;
183 }
184
185 for( ZONE* zone : m_frame->GetBoard()->Zones() )
186 zone->CacheBoundingBox();
187
188 for( FOOTPRINT* footprint : m_frame->GetBoard()->Footprints() )
189 {
190 for( ZONE* zone : footprint->Zones() )
191 zone->CacheBoundingBox();
192
193 footprint->BuildCourtyardCaches();
194
195 if( aCourtyardError && ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
196 *aCourtyardError = true;
197 }
198
199 return engine;
200}
201
202
203bool isNPTHPad( const BOARD_ITEM& aItem )
204{
205 if( aItem.Type() != PCB_PAD_T )
206 return false;
207
208 const PAD& pad = static_cast<const PAD&>( aItem );
209 return pad.GetAttribute() == PAD_ATTRIB::NPTH;
210}
211
212
214{
215 // Null items have no description
216 if( !aItem )
217 return wxString();
218
219 wxString msg = aItem->GetItemDescription( m_frame, true );
220
221 if( aItem->IsConnected() && !isNPTHPad( *aItem ) )
222 {
223 const BOARD_CONNECTED_ITEM* cItem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
224
225 msg += wxS( " " )
226 + wxString::Format( _( "[netclass %s]" ),
228 }
229
230 return msg;
231};
232
233
235 const VECTOR2I& aPos )
236{
237 std::vector<BOARD_ITEM*> toAdd;
238
239 for( int i = 0; i < aCollector.GetCount(); ++i )
240 {
241 if( aCollector[i]->Type() == PCB_GROUP_T )
242 {
243 PCB_GROUP* group = static_cast<PCB_GROUP*>( aCollector[i] );
244
245 group->RunOnChildren(
246 [&]( BOARD_ITEM* child )
247 {
248 if( child->Type() == PCB_GROUP_T )
249 return;
250
251 if( !child->HitTest( aPos ) )
252 return;
253
254 toAdd.push_back( child );
255
256 if( child->Type() == PCB_FOOTPRINT_T )
257 {
258 for( PAD* pad : static_cast<FOOTPRINT*>( child )->Pads() )
259 {
260 if( pad->HitTest( aPos ) )
261 toAdd.push_back( pad );
262 }
263 }
264 },
266 }
267 }
268
269 for( BOARD_ITEM* item : toAdd )
270 aCollector.Append( item );
271
272 bool hasPadOrTrack = false;
273
274 for( int i = 0; i < aCollector.GetCount(); ++i )
275 {
276 KICAD_T type = aCollector[i]->Type();
277
278 if( type == PCB_PAD_T || type == PCB_VIA_T || type == PCB_TRACE_T
279 || type == PCB_ARC_T || type == PCB_ZONE_T )
280 {
281 hasPadOrTrack = true;
282 break;
283 }
284 }
285
286 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
287 {
288 BOARD_ITEM* item = aCollector[i];
289
290 if( hasPadOrTrack && item->Type() == PCB_FOOTPRINT_T )
291 {
292 aCollector.Remove( i );
293 continue;
294 }
295
296 if( item->Type() == PCB_GROUP_T )
297 aCollector.Remove( i );
298 }
299}
300
301
303 const wxString& aPrompt,
304 const std::vector<KICAD_T>& aTypes,
305 BOARD_ITEM* aLockedHighlight )
306{
307 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
308 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
309 STATUS_TEXT_POPUP statusPopup( m_frame );
310 BOARD_ITEM* pickedItem = nullptr;
311 BOARD_ITEM* highlightedItem = nullptr;
312 bool done = false;
313
314 statusPopup.SetText( aPrompt );
315
316 picker->SetCursor( KICURSOR::BULLSEYE );
317 picker->SetSnapping( false );
318 picker->ClearHandlers();
319
320 picker->SetClickHandler(
321 [&]( const VECTOR2D& aPoint ) -> bool
322 {
324
325 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
326 GENERAL_COLLECTOR collector;
327
328 collector.Collect( m_frame->GetBoard(), aTypes, aPoint, guide );
329
330 for( int i = collector.GetCount() - 1; i >= 0; --i )
331 {
332 if( !selTool->Selectable( collector[i] ) )
333 collector.Remove( i );
334 }
335
336 filterCollectorForInspection( collector, aPoint );
337
338 if( collector.GetCount() > 1 )
339 selTool->GuessSelectionCandidates( collector, aPoint );
340
341 if( collector.GetCount() == 0 )
342 return true;
343
344 pickedItem = collector[0];
345 statusPopup.Hide();
346
347 return false;
348 } );
349
350 picker->SetMotionHandler(
351 [&]( const VECTOR2D& aPos )
352 {
353 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
354
355 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
356 GENERAL_COLLECTOR collector;
357
358 collector.Collect( m_frame->GetBoard(), aTypes, aPos, guide );
359
360 for( int i = collector.GetCount() - 1; i >= 0; --i )
361 {
362 if( !selTool->Selectable( collector[i] ) )
363 collector.Remove( i );
364 }
365
366 filterCollectorForInspection( collector, aPos );
367
368 if( collector.GetCount() > 1 )
369 selTool->GuessSelectionCandidates( collector, aPos );
370
371 BOARD_ITEM* item = collector.GetCount() >= 1 ? collector[0] : nullptr;
372
373 if( highlightedItem != item )
374 {
375 if( highlightedItem && highlightedItem != aLockedHighlight )
376 selTool->UnbrightenItem( highlightedItem );
377
378 highlightedItem = item;
379
380 if( highlightedItem && highlightedItem != aLockedHighlight )
381 selTool->BrightenItem( highlightedItem );
382 }
383 } );
384
385 picker->SetCancelHandler(
386 [&]()
387 {
388 if( highlightedItem && highlightedItem != aLockedHighlight )
389 selTool->UnbrightenItem( highlightedItem );
390
391 highlightedItem = nullptr;
392 statusPopup.Hide();
393 done = true;
394 } );
395
396 picker->SetFinalizeHandler(
397 [&]( const int& aFinalState )
398 {
399 if( highlightedItem && highlightedItem != aLockedHighlight )
400 selTool->UnbrightenItem( highlightedItem );
401
402 highlightedItem = nullptr;
403
404 if( !pickedItem )
405 done = true;
406 } );
407
408 statusPopup.Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
409 statusPopup.Popup();
410 m_frame->GetCanvas()->SetStatusPopup( statusPopup.GetPanel() );
411
412 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
413
414 while( !done && !pickedItem )
415 {
416 if( TOOL_EVENT* evt = Wait() )
417 evt->SetPassEvent();
418 else
419 break;
420 }
421
422 picker->ClearHandlers();
423 m_frame->GetCanvas()->SetStatusPopup( nullptr );
424
425 return pickedItem;
426}
427
428
430{
431 r->Report( "" );
432 r->Report( _( "Report incomplete: could not compile custom design rules." )
433 + wxS( "&nbsp;&nbsp;" )
434 + wxS( "<a href='$CUSTOM_RULES'>" ) + _( "Show design rules." ) + wxS( "</a>" ) );
435}
436
437
438void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, const BOARD_ITEM* a, REPORTER* r )
439{
440 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
441 r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li></ul>" ) );
442}
443
444
445void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, const BOARD_ITEM* a, const BOARD_ITEM* b,
446 REPORTER* r )
447{
448 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
449 r->Report( wxT( "<ul><li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
450 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
451}
452
453
454void BOARD_INSPECTION_TOOL::reportHeader( const wxString& aTitle, const BOARD_ITEM* a, const BOARD_ITEM* b,
455 PCB_LAYER_ID aLayer, REPORTER* r )
456{
457 wxString layerStr = _( "Layer" ) + wxS( " " ) + m_frame->GetBoard()->GetLayerName( aLayer );
458
459 r->Report( wxT( "<h7>" ) + EscapeHTML( aTitle ) + wxT( "</h7>" ) );
460 r->Report( wxT( "<ul><li>" ) + EscapeHTML( layerStr ) + wxT( "</li>" )
461 + wxT( "<li>" ) + EscapeHTML( getItemDescription( a ) ) + wxT( "</li>" )
462 + wxT( "<li>" ) + EscapeHTML( getItemDescription( b ) ) + wxT( "</li></ul>" ) );
463}
464
465
466namespace
467{
468class VECTOR_REPORTER : public REPORTER
469{
470public:
471 REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
472 {
473 m_messages.push_back( aText );
474 return *this;
475 }
476
477 bool HasMessage() const override { return !m_messages.empty(); }
478 EDA_UNITS GetUnits() const override { return EDA_UNITS::UNSCALED; }
479
480 std::vector<wxString> m_messages;
481};
482} // namespace
483
484
485wxString reportMin( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
486{
487 if( aConstraint.m_Value.HasMin() )
488 return aFrame->StringFromValue( aConstraint.m_Value.Min(), true );
489 else
490 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
491}
492
493
494wxString reportOpt( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
495{
496 if( aConstraint.m_Value.HasOpt() )
497 return aFrame->StringFromValue( aConstraint.m_Value.Opt(), true );
498 else
499 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
500}
501
502
503wxString reportMax( PCB_BASE_FRAME* aFrame, DRC_CONSTRAINT& aConstraint )
504{
505 if( aConstraint.m_Value.HasMax() )
506 return aFrame->StringFromValue( aConstraint.m_Value.Max(), true );
507 else
508 return wxT( "<i>" ) + _( "undefined" ) + wxT( "</i>" );
509}
510
511
512wxString BOARD_INSPECTION_TOOL::InspectDRCErrorMenuText( const std::shared_ptr<RC_ITEM>& aDRCItem )
513{
514 if( aDRCItem->GetErrorCode() == DRCE_CLEARANCE
515 || aDRCItem->GetErrorCode() == DRCE_EDGE_CLEARANCE
516 || aDRCItem->GetErrorCode() == DRCE_HOLE_CLEARANCE
517 || aDRCItem->GetErrorCode() == DRCE_DRILLED_HOLES_TOO_CLOSE
518 || aDRCItem->GetErrorCode() == DRCE_STARVED_THERMAL )
519 {
520 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::inspectClearance );
521 }
522 else if( aDRCItem->GetErrorCode() == DRCE_TEXT_HEIGHT
523 || aDRCItem->GetErrorCode() == DRCE_TEXT_THICKNESS
524 || aDRCItem->GetErrorCode() == DRCE_DP_UNCOUPLED_LENGTH_TOO_LONG
525 || aDRCItem->GetErrorCode() == DRCE_TRACK_WIDTH
526 || aDRCItem->GetErrorCode() == DRCE_TRACK_ANGLE
527 || aDRCItem->GetErrorCode() == DRCE_TRACK_SEGMENT_LENGTH
528 || aDRCItem->GetErrorCode() == DRCE_VIA_DIAMETER
529 || aDRCItem->GetErrorCode() == DRCE_ANNULAR_WIDTH
530 || aDRCItem->GetErrorCode() == DRCE_DRILL_OUT_OF_RANGE
531 || aDRCItem->GetErrorCode() == DRCE_MICROVIA_DRILL_OUT_OF_RANGE
532 || aDRCItem->GetErrorCode() == DRCE_CONNECTION_WIDTH
533 || aDRCItem->GetErrorCode() == DRCE_ASSERTION_FAILURE )
534 {
535 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::inspectConstraints );
536 }
537 else if( aDRCItem->GetErrorCode() == DRCE_LIB_FOOTPRINT_MISMATCH )
538 {
539 return m_frame->GetRunMenuCommandDescription( PCB_ACTIONS::diffFootprint );
540 }
541
542 return wxEmptyString;
543}
544
545
546void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDRCItem )
547{
548 DRC_TOOL* drcTool = m_toolMgr->GetTool<DRC_TOOL>();
549
550 wxCHECK( drcTool && m_frame, /* void */ );
551
552 BOARD_ITEM* a = m_frame->GetBoard()->ResolveItem( aDRCItem->GetMainItemID() );
553 BOARD_ITEM* b = m_frame->GetBoard()->ResolveItem( aDRCItem->GetAuxItemID() );
554 const BOARD_CONNECTED_ITEM* ac = dynamic_cast<const BOARD_CONNECTED_ITEM*>( a );
555 const BOARD_CONNECTED_ITEM* bc = dynamic_cast<const BOARD_CONNECTED_ITEM*>( b );
556 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
557
558 if( aDRCItem->GetErrorCode() == DRCE_LIB_FOOTPRINT_MISMATCH )
559 {
560 if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( a ) )
561 DiffFootprint( footprint, drcTool->GetDRCDialog() );
562
563 return;
564 }
565
566 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectDrcErrorDialog();
567 wxCHECK( dialog, /* void */ );
568
569 dialog->DeleteAllPages();
570
571 bool compileError = false;
572 bool courtyardError = false;
573 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
574
575 // Use the same principal-layer policy as the DRC dialog's cross-probe.
576 PCB_MARKER* marker = nullptr;
577
578 for( PCB_MARKER* candidate : m_frame->GetBoard()->Markers() )
579 {
580 if( candidate->GetRCItem() == aDRCItem )
581 {
582 marker = candidate;
583 break;
584 }
585 }
586
587 // Pull out the principal layer for this DRC item if there is one.
588 //
589 // Note: some tests evaluate the constraint with UNDEFINED_LAYER, so a
590 // rule's (layer ...) restriction is not checked against the item's layer
591 // (DRC_TEST_PROVIDER_EDGE_CLEARANCE, for example). When we evaluate
592 // the constraint later, match the provider's layer choice in those cases so
593 // the resolution agrees with the DRC run.
594
595 {
596 PCB_LAYER_ID principalLayer = UNDEFINED_LAYER;
597 LSET violationLayers;
598
599 DRC_ITEM::GetViolationLayers( m_frame->GetBoard(), aDRCItem, marker, principalLayer, violationLayers );
600
601 if( principalLayer > UNDEFINED_LAYER )
602 layer = principalLayer;
603 }
604
605 WX_HTML_REPORT_BOX* r = nullptr;
606 DRC_CONSTRAINT constraint;
607 int clearance = 0;
608 wxString clearanceStr;
609
610 switch( aDRCItem->GetErrorCode() )
611 {
613 {
614 for( KIID id : aDRCItem->GetIDs() )
615 {
616 bc = dynamic_cast<BOARD_CONNECTED_ITEM*>( m_frame->GetBoard()->ResolveItem( id, true ) );
617
618 if( ac && bc && ac->GetNetCode() != bc->GetNetCode() )
619 break;
620 }
621
622 r = dialog->AddHTMLPage( _( "Uncoupled Length" ) );
623 reportHeader( _( "Diff pair uncoupled length resolution for:" ), ac, bc, r );
624
625 if( compileError )
627
628 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, a, b, layer, r );
629
630 r->Report( "" );
631 r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
632 reportMax( m_frame, constraint ) ) );
633 break;
634 }
635
636 case DRCE_TEXT_HEIGHT:
637 r = dialog->AddHTMLPage( _( "Text Height" ) );
638 reportHeader( _( "Text height resolution for:" ), a, r );
639
640 if( compileError )
642
643 constraint = drcEngine->EvalRules( TEXT_HEIGHT_CONSTRAINT, a, b, layer, r );
644
645 r->Report( "" );
646 r->Report( wxString::Format( _( "Resolved height constraints: min %s; max %s." ),
647 reportMin( m_frame, constraint ),
648 reportMax( m_frame, constraint ) ) );
649 break;
650
652 r = dialog->AddHTMLPage( _( "Text Thickness" ) );
653 reportHeader( _( "Text thickness resolution for:" ), a, r );
654
655 if( compileError )
657
658 constraint = drcEngine->EvalRules( TEXT_THICKNESS_CONSTRAINT, a, b, layer, r );
659
660 r->Report( "" );
661 r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; max %s." ),
662 reportMin( m_frame, constraint ),
663 reportMax( m_frame, constraint ) ) );
664 break;
665
666 case DRCE_TRACK_WIDTH:
667 r = dialog->AddHTMLPage( _( "Track Width" ) );
668 reportHeader( _( "Track width resolution for:" ), a, r );
669
670 if( compileError )
672
673 constraint = drcEngine->EvalRules( TRACK_WIDTH_CONSTRAINT, a, b, layer, r );
674
675 r->Report( "" );
676 r->Report( wxString::Format( _( "Resolved width constraints: min %s; max %s." ),
677 reportMin( m_frame, constraint ),
678 reportMax( m_frame, constraint ) ) );
679 break;
680
681 case DRCE_TRACK_ANGLE:
682 r = dialog->AddHTMLPage( _( "Track Angle" ) );
683 reportHeader( _( "Track Angle resolution for:" ), a, r );
684
685 if( compileError )
687
688 constraint = drcEngine->EvalRules( TRACK_ANGLE_CONSTRAINT, a, b, layer, r );
689
690 r->Report( "" );
691 r->Report( wxString::Format( _( "Resolved angle constraints: min %s; max %s." ),
692 reportMin( m_frame, constraint ),
693 reportMax( m_frame, constraint ) ) );
694 break;
695
697 r = dialog->AddHTMLPage( _( "Track Segment Length" ) );
698 reportHeader( _( "Track segment length resolution for:" ), a, r );
699
700 if( compileError )
702
703 constraint = drcEngine->EvalRules( TRACK_SEGMENT_LENGTH_CONSTRAINT, a, b, layer, r );
704
705 r->Report( "" );
706 r->Report( wxString::Format( _( "Resolved segment length constraints: min %s; max %s." ),
707 reportMin( m_frame, constraint ),
708 reportMax( m_frame, constraint ) ) );
709 break;
710
712 r = dialog->AddHTMLPage( _( "Connection Width" ) );
713 reportHeader( _( "Connection width resolution for:" ), a, b, r );
714
715 if( compileError )
717
718 constraint = drcEngine->EvalRules( CONNECTION_WIDTH_CONSTRAINT, a, b, layer, r );
719
720 r->Report( "" );
721 r->Report( wxString::Format( _( "Resolved min connection width: %s." ),
722 reportMin( m_frame, constraint ) ) );
723 break;
724
726 r = dialog->AddHTMLPage( _( "Via Diameter" ) );
727 reportHeader( _( "Via diameter resolution for:" ), a, r );
728
729 if( compileError )
731
732 constraint = drcEngine->EvalRules( VIA_DIAMETER_CONSTRAINT, a, b, layer, r );
733
734 r->Report( "" );
735 r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; max %s." ),
736 reportMin( m_frame, constraint ),
737 reportMax( m_frame, constraint ) ) );
738 break;
739
741 r = dialog->AddHTMLPage( _( "Via Annulus" ) );
742 reportHeader( _( "Via annular width resolution for:" ), a, r );
743
744 if( compileError )
746
747 constraint = drcEngine->EvalRules( ANNULAR_WIDTH_CONSTRAINT, a, b, layer, r );
748
749 r->Report( "" );
750 r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; max %s." ),
751 reportMin( m_frame, constraint ),
752 reportMax( m_frame, constraint ) ) );
753 break;
754
757 r = dialog->AddHTMLPage( _( "Hole Size" ) );
758 reportHeader( _( "Hole size resolution for:" ), a, r );
759
760 if( compileError )
762
763 // Hole tests were done with UNDEFINED_LAYER
764 constraint = drcEngine->EvalRules( HOLE_SIZE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
765
766 r->Report( "" );
767 r->Report( wxString::Format( _( "Resolved hole size constraints: min %s; max %s." ),
768 reportMin( m_frame, constraint ),
769 reportMax( m_frame, constraint ) ) );
770 break;
771
773 r = dialog->AddHTMLPage( _( "Hole Clearance" ) );
774 reportHeader( _( "Hole clearance resolution for:" ), a, b, r );
775
776 if( compileError )
778
779 if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
780 {
781 r->Report( "" );
782 r->Report( _( "Items belong to the same net. Clearance is 0." ) );
783 }
784 else
785 {
786 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
787 clearance = constraint.m_Value.Min();
788 clearanceStr = m_frame->StringFromValue( clearance, true );
789
790 r->Report( "" );
791 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
792 }
793
794 r->Report( "" );
795 r->Report( "" );
796 r->Report( "" );
797 reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
798
799 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
800 clearance = constraint.m_Value.Min();
801 clearanceStr = m_frame->StringFromValue( clearance, true );
802
803 if( !drcEngine->HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
804 {
805 r->Report( "" );
806 r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
807 }
808 else
809 {
810 r->Report( "" );
811 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
812 }
813
814 break;
815
817 r = dialog->AddHTMLPage( _( "Hole to Hole" ) );
818 reportHeader( _( "Hole-to-hole clearance resolution for:" ), a, b, r );
819
820 if( compileError )
822
823 constraint = drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
824 clearance = constraint.m_Value.Min();
825 clearanceStr = m_frame->StringFromValue( clearance, true );
826
827 r->Report( "" );
828 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
829 break;
830
832 r = dialog->AddHTMLPage( _( "Edge Clearance" ) );
833 reportHeader( _( "Edge clearance resolution for:" ), a, b, r );
834
835 if( compileError )
837
838 // Edge clearance test is done with UNDEFINED_LAYER
839 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
840 clearance = constraint.m_Value.Min();
841 clearanceStr = m_frame->StringFromValue( clearance, true );
842
843 r->Report( "" );
844 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
845 break;
846
847 case DRCE_CLEARANCE:
848 {
849 r = dialog->AddHTMLPage( _( "Clearance" ) );
850 reportHeader( _( "Clearance resolution for:" ), a, b, layer, r );
851
852 if( compileError )
854
855 if( ac && bc && ac->GetNetCode() == bc->GetNetCode() )
856 {
857 r->Report( "" );
858 r->Report( _( "Items belong to the same net. Clearance is 0." ) );
859 }
860 else
861 {
862 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, r );
863 clearance = constraint.m_Value.Min();
864 clearanceStr = m_frame->StringFromValue( clearance, true );
865
866 r->Report( "" );
867 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
868 }
869
870 r->Report( "" );
871 r->Report( "" );
872 r->Report( "" );
873 reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
874
875 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
876 clearance = constraint.m_Value.Min();
877 clearanceStr = m_frame->StringFromValue( clearance, true );
878
879 if( !drcEngine->HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
880 {
881 r->Report( "" );
882 r->Report( _( "No 'physical_clearance' constraints defined." ) );
883 }
884 else
885 {
886 r->Report( "" );
887 r->Report( wxString::Format( _( "Resolved min clearance: %s." ), clearanceStr ) );
888 }
889
890 break;
891 }
893 r = dialog->AddHTMLPage( _( "Assertions" ) );
894 reportHeader( _( "Assertions for:" ), a, r );
895
896 if( compileError )
898
899 drcEngine->ProcessAssertions( a, []( const DRC_CONSTRAINT* c ){}, r );
900 break;
901
902 default:
903 return;
904 }
905
906 r->Flush();
907
908 KIPLATFORM::UI::ReparentWindow( dialog, drcTool->GetDRCDialog() );
909 dialog->Show( true );
910}
911
912
914{
915 wxCHECK( m_frame, 0 );
916
917 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
918
919 wxCHECK( selTool, 0 );
920
921 const PCB_SELECTION& selection = selTool->GetSelection();
922 BOARD_ITEM* firstItem = nullptr;
923 BOARD_ITEM* secondItem = nullptr;
924
925 if( selection.Size() == 2 )
926 {
927 if( !selection.GetItem( 0 )->IsBOARD_ITEM() || !selection.GetItem( 1 )->IsBOARD_ITEM() )
928 return 0;
929
930 firstItem = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
931 secondItem = static_cast<BOARD_ITEM*>( selection.GetItem( 1 ) );
932
933 reportClearance( firstItem, secondItem );
934 return 0;
935 }
936
937 // Selection size is not 2, so we need to use picker mode.
938 // If there is one item selected, use it as the first item.
939 if( selection.Size() == 1 && selection.GetItem( 0 )->IsBOARD_ITEM() )
940 firstItem = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
941
942 static const std::vector<KICAD_T> clearanceTypes = {
943 PCB_PAD_T,
944 PCB_VIA_T,
946 PCB_ARC_T,
951 };
952
953 Activate();
954
955 if( !firstItem )
956 {
957 firstItem = pickItemForInspection( aEvent,
958 _( "Select first item for clearance resolution..." ),
959 clearanceTypes, nullptr );
960
961 if( !firstItem )
962 return 0;
963 }
964
965 // Keep the first item highlighted while selecting the second
966 selTool->BrightenItem( firstItem );
967
968 secondItem = pickItemForInspection( aEvent,
969 _( "Select second item for clearance resolution..." ),
970 clearanceTypes, firstItem );
971
972 selTool->UnbrightenItem( firstItem );
973
974 if( !secondItem )
975 return 0;
976
977 if( firstItem == secondItem )
978 {
979 m_frame->ShowInfoBarError( _( "Select two different items for clearance resolution." ) );
980 return 0;
981 }
982
983 reportClearance( firstItem, secondItem );
984
985 return 0;
986}
987
988
990{
991 wxCHECK( m_frame && aItemA && aItemB, /* void */ );
992
993 const BOARD_ITEM* a = aItemA;
994 const BOARD_ITEM* b = aItemB;
995
996 if( a->Type() == PCB_GROUP_T )
997 {
998 const PCB_GROUP* ag = static_cast<const PCB_GROUP*>( a );
999
1000 if( ag->GetItems().empty() )
1001 {
1002 m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
1003 return;
1004 }
1005
1006 a = static_cast<const BOARD_ITEM*>( *ag->GetItems().begin() );
1007 }
1008
1009 if( b->Type() == PCB_GROUP_T )
1010 {
1011 const PCB_GROUP* bg = static_cast<const PCB_GROUP*>( b );
1012
1013 if( bg->GetItems().empty() )
1014 {
1015 m_frame->ShowInfoBarError( _( "Cannot generate clearance report on empty group." ) );
1016 return;
1017 }
1018
1019 b = static_cast<const BOARD_ITEM*>( *bg->GetItems().begin() );
1020 }
1021
1022 if( !a || !b )
1023 return;
1024
1025 const auto checkFootprint =
1026 [&]( const FOOTPRINT* footprint ) -> const BOARD_ITEM*
1027 {
1028 const PAD* foundPad = nullptr;
1029
1030 for( const PAD* pad : footprint->Pads() )
1031 {
1032 if( !foundPad || pad->SameLogicalPadAs( foundPad ) )
1033 foundPad = pad;
1034 else
1035 return footprint;
1036 }
1037
1038 if( !foundPad )
1039 return footprint;
1040
1041 return foundPad;
1042 };
1043
1044 if( a->Type() == PCB_FOOTPRINT_T )
1045 a = checkFootprint( static_cast<const FOOTPRINT*>( a ) );
1046
1047 if( b->Type() == PCB_FOOTPRINT_T )
1048 b = checkFootprint( static_cast<const FOOTPRINT*>( b ) );
1049
1050 if( !a || !b )
1051 return;
1052
1053 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectClearanceDialog();
1054
1055 wxCHECK( dialog, /* void */ );
1056
1057 dialog->DeleteAllPages();
1058
1059 if( a->Type() != PCB_ZONE_T && b->Type() == PCB_ZONE_T )
1060 std::swap( a, b );
1061 else if( !a->IsConnected() && b->IsConnected() )
1062 std::swap( a, b );
1063
1064 WX_HTML_REPORT_BOX* r = nullptr;
1065 PCB_LAYER_ID active = m_frame->GetActiveLayer();
1066 LSET layerIntersection = a->GetLayerSet() & b->GetLayerSet();
1067 LSET copperIntersection = layerIntersection & LSET::AllCuMask();
1068 DRC_CONSTRAINT constraint;
1069 int clearance = 0;
1070
1071 const BOARD_CONNECTED_ITEM* ac = dynamic_cast<const BOARD_CONNECTED_ITEM*>( a );
1072 const BOARD_CONNECTED_ITEM* bc = dynamic_cast<const BOARD_CONNECTED_ITEM*>( b );
1073 const ZONE* zone = dynamic_cast<const ZONE*>( a );
1074 const PAD* pad = dynamic_cast<const PAD*>( b );
1075 const FOOTPRINT* aFP = dynamic_cast<const FOOTPRINT*>( a );
1076 const FOOTPRINT* bFP = dynamic_cast<const FOOTPRINT*>( b );
1077
1078 bool compileError = false;
1079 bool courtyardError = false;
1080 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
1081
1082 if( copperIntersection.any() && zone && pad && zone->GetNetCode() == pad->GetNetCode() )
1083 {
1085
1086 if( zone->IsOnLayer( active ) )
1087 layer = active;
1088 else if( zone->GetLayerSet().count() > 0 )
1089 layer = zone->GetLayerSet().Seq().front();
1090
1091 r = dialog->AddHTMLPage( _( "Zone" ) );
1092 reportHeader( _( "Zone connection resolution for:" ), a, b, layer, r );
1093
1094 constraint = drcEngine->EvalZoneConnection( pad, zone, layer, r );
1095
1096 if( constraint.m_ZoneConnection == ZONE_CONNECTION::THERMAL )
1097 {
1098 r->Report( "" );
1099 r->Report( "" );
1100 reportHeader( _( "Thermal-relief gap resolution for:" ), a, b, layer, r );
1101
1102 constraint = drcEngine->EvalRules( THERMAL_RELIEF_GAP_CONSTRAINT, pad, zone, layer, r );
1103 int gap = constraint.m_Value.Min();
1104
1105 if( compileError )
1106 reportCompileError( r );
1107
1108 r->Report( "" );
1109 r->Report( wxString::Format( _( "Resolved thermal relief gap: %s." ),
1110 m_frame->StringFromValue( gap, true ) ) );
1111
1112 r->Report( "" );
1113 r->Report( "" );
1114 reportHeader( _( "Thermal-relief spoke width resolution for:" ), a, b, layer, r );
1115
1116 constraint = drcEngine->EvalRules( THERMAL_SPOKE_WIDTH_CONSTRAINT, pad, zone, layer, r );
1117 int width = constraint.m_Value.Opt();
1118
1119 if( compileError )
1120 reportCompileError( r );
1121
1122 r->Report( "" );
1123 r->Report( wxString::Format( _( "Resolved spoke width: %s." ),
1124 m_frame->StringFromValue( width, true ) ) );
1125
1126 r->Report( "" );
1127 r->Report( "" );
1128 reportHeader( _( "Thermal-relief min spoke count resolution for:" ), a, b, layer, r );
1129
1130 constraint = drcEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, zone, layer, r );
1131 int minSpokes = constraint.m_Value.Min();
1132
1133 if( compileError )
1134 reportCompileError( r );
1135
1136 r->Report( "" );
1137 r->Report( wxString::Format( _( "Resolved min spoke count: %d." ),
1138 minSpokes ) );
1139
1140 std::shared_ptr<CONNECTIVITY_DATA> connectivity = pad->GetBoard()->GetConnectivity();
1141 }
1142 else if( constraint.m_ZoneConnection == ZONE_CONNECTION::NONE )
1143 {
1144 r->Report( "" );
1145 r->Report( "" );
1146 reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
1147
1148 clearance = zone->GetLocalClearance().value();
1149 r->Report( "" );
1150 r->Report( wxString::Format( _( "Zone clearance: %s." ),
1151 m_frame->StringFromValue( clearance, true ) ) );
1152
1153 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, pad, zone, layer, r );
1154
1155 if( constraint.m_Value.Min() > clearance )
1156 {
1157 clearance = constraint.m_Value.Min();
1158
1159 r->Report( "" );
1160 r->Report( wxString::Format( _( "Overridden by larger physical clearance from %s;"
1161 "clearance: %s." ),
1162 EscapeHTML( constraint.GetName() ),
1163 m_frame->StringFromValue( clearance, true ) ) );
1164 }
1165
1166 if( !pad->FlashLayer( layer ) )
1167 {
1168 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, pad, zone,
1169 layer, r );
1170
1171 if( constraint.m_Value.Min() > clearance )
1172 {
1173 clearance = constraint.m_Value.Min();
1174
1175 r->Report( "" );
1176 r->Report( wxString::Format( _( "Overridden by larger physical hole clearance "
1177 "from %s; clearance: %s." ),
1178 EscapeHTML( constraint.GetName() ),
1179 m_frame->StringFromValue( clearance, true ) ) );
1180 }
1181 }
1182
1183 if( compileError )
1184 reportCompileError( r );
1185
1186 r->Report( "" );
1187 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1188 m_frame->StringFromValue( clearance, true ) ) );
1189 }
1190 else
1191 {
1192 r->Report( "" );
1193 r->Report( "" );
1194 reportHeader( _( "Zone clearance resolution for:" ), a, b, layer, r );
1195
1196 if( compileError )
1197 reportCompileError( r );
1198
1199 r->Report( "" );
1200 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1201 m_frame->StringFromValue( 0, true ) ) );
1202 }
1203
1204 r->Flush();
1205 }
1206 else if( copperIntersection.any() && !aFP && !bFP )
1207 {
1208 bool sameNet = ac && bc && ac->GetNetCode() > 0 && ac->GetNetCode() == bc->GetNetCode();
1209
1210 std::vector<PCB_LAYER_ID> layers;
1211
1212 if( copperIntersection.test( active ) )
1213 layers.push_back( active );
1214
1215 for( PCB_LAYER_ID layer : copperIntersection.Seq() )
1216 {
1217 if( layer != active )
1218 layers.push_back( layer );
1219 }
1220
1221 auto fillReport =
1222 [&]( PCB_LAYER_ID layer, REPORTER* rep )
1223 {
1224 reportHeader( _( "Clearance resolution for:" ), a, b, layer, rep );
1225
1226 if( sameNet )
1227 {
1228 rep->Report( _( "Items belong to the same net. Min clearance is 0." ) );
1229 return;
1230 }
1231
1232 constraint = drcEngine->EvalRules( CLEARANCE_CONSTRAINT, a, b, layer, rep );
1233 clearance = constraint.m_Value.Min();
1234
1235 if( compileError )
1236 reportCompileError( rep );
1237
1238 rep->Report( "" );
1239
1240 if( constraint.IsNull() )
1241 {
1242 rep->Report( _( "Min clearance is 0." ) );
1243 }
1244 else if( clearance < 0 )
1245 {
1246 rep->Report( wxString::Format( _( "Resolved clearance: %s; clearance will "
1247 "not be tested." ),
1248 m_frame->StringFromValue( clearance, true ) ) );
1249 }
1250 else
1251 {
1252 rep->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1253 m_frame->StringFromValue( clearance, true ) ) );
1254 }
1255 };
1256
1257 if( layers.size() == 1 )
1258 {
1259 PCB_LAYER_ID layer = layers.front();
1260
1261 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1262 fillReport( layer, r );
1263 r->Flush();
1264 }
1265 else
1266 {
1267 auto perLayerMessages = std::make_shared<std::vector<std::vector<wxString>>>();
1268 perLayerMessages->reserve( layers.size() );
1269
1270 for( PCB_LAYER_ID layer : layers )
1271 {
1272 VECTOR_REPORTER tmp;
1273 fillReport( layer, &tmp );
1274 perLayerMessages->push_back( std::move( tmp.m_messages ) );
1275 }
1276
1277 wxPanel* panel = dialog->AddBlankPage( _( "Clearance" ) );
1278 wxBoxSizer* vbox = new wxBoxSizer( wxVERTICAL );
1279
1280 wxChoice* choice = new wxChoice( panel, wxID_ANY );
1281
1282 for( PCB_LAYER_ID layer : layers )
1283 choice->Append( m_frame->GetBoard()->GetLayerName( layer ) );
1284
1285 choice->SetSelection( 0 );
1286
1287 WX_HTML_REPORT_BOX* reportBox = new WX_HTML_REPORT_BOX( panel, wxID_ANY, wxDefaultPosition,
1288 wxDefaultSize,
1289 wxHW_SCROLLBAR_AUTO | wxBORDER_SIMPLE );
1290 reportBox->SetUnits( m_frame->GetUserUnits() );
1291
1292 wxStaticText* layerLabel = new wxStaticText( panel, wxID_ANY, _( "Layer:" ) );
1293
1294 vbox->Add( layerLabel, 0, wxLEFT | wxRIGHT | wxTOP, 5 );
1295 vbox->Add( choice, 0, wxEXPAND | wxALL, 5 );
1296 vbox->Add( reportBox, 1, wxEXPAND | wxALL, 5 );
1297 panel->SetSizer( vbox );
1298 panel->Layout();
1299
1300 auto refresh =
1301 [reportBox, perLayerMessages]( int sel )
1302 {
1303 reportBox->Clear();
1304
1305 if( sel >= 0 && sel < (int) perLayerMessages->size() )
1306 {
1307 for( const wxString& line : ( *perLayerMessages )[sel] )
1308 reportBox->Report( line );
1309 }
1310
1311 reportBox->Flush();
1312 };
1313
1314 choice->Bind( wxEVT_CHOICE,
1315 [refresh]( wxCommandEvent& evt )
1316 {
1317 refresh( evt.GetSelection() );
1318 } );
1319
1320 refresh( 0 );
1321 }
1322 }
1323
1324 if( ac && bc )
1325 {
1326 NETINFO_ITEM* refNet = ac->GetNet();
1327 wxString coupledNet;
1328 wxString dummy;
1329
1330 if( DRC_ENGINE::MatchDpSuffix( refNet->GetNetname(), coupledNet, dummy )
1331 && bc->GetNetname() == coupledNet )
1332 {
1333 LSET dpIntersection = ac->GetLayerSet() & bc->GetLayerSet() & LSET::AllCuMask();
1334 PCB_LAYER_ID dpLayer = active;
1335
1336 if( !dpIntersection.test( dpLayer ) && dpIntersection.any() )
1337 dpLayer = dpIntersection.Seq().front();
1338
1339 r = dialog->AddHTMLPage( _( "Diff Pair" ) );
1340 reportHeader( _( "Diff-pair gap resolution for:" ), ac, bc, dpLayer, r );
1341
1342 constraint = drcEngine->EvalRules( DIFF_PAIR_GAP_CONSTRAINT, ac, bc, dpLayer, r );
1343
1344 r->Report( "" );
1345 r->Report( wxString::Format( _( "Resolved gap constraints: min %s; opt %s; max %s." ),
1346 reportMin( m_frame, constraint ),
1347 reportOpt( m_frame, constraint ),
1348 reportMax( m_frame, constraint ) ) );
1349
1350 r->Report( "" );
1351 r->Report( "" );
1352 r->Report( "" );
1353 reportHeader( _( "Diff-pair max uncoupled length resolution for:" ), ac, bc, dpLayer, r );
1354
1355 if( !drcEngine->HasRulesForConstraintType( MAX_UNCOUPLED_CONSTRAINT ) )
1356 {
1357 r->Report( "" );
1358 r->Report( _( "No 'diff_pair_uncoupled' constraints defined." ) );
1359 }
1360 else
1361 {
1362 constraint = drcEngine->EvalRules( MAX_UNCOUPLED_CONSTRAINT, ac, bc, dpLayer, r );
1363
1364 r->Report( "" );
1365 r->Report( wxString::Format( _( "Resolved max uncoupled length: %s." ),
1366 reportMax( m_frame, constraint ) ) );
1367 }
1368
1369 r->Flush();
1370 }
1371 }
1372
1373 const auto isOnCorrespondingLayer=
1374 [&]( const BOARD_ITEM& aItem, PCB_LAYER_ID aLayer, wxString* aWarning )
1375 {
1376 if( aItem.IsOnLayer( aLayer ) )
1377 return true;
1378
1379 PCB_LAYER_ID correspondingMask = IsFrontLayer( aLayer ) ? F_Mask : B_Mask;
1380 PCB_LAYER_ID correspondingCopper = IsFrontLayer( aLayer ) ? F_Cu : B_Cu;
1381
1382 if( aItem.IsOnLayer( aLayer ) )
1383 return true;
1384
1385 if( aItem.IsOnLayer( correspondingMask ) )
1386 return true;
1387
1388 if( aItem.IsTented( correspondingMask ) && aItem.IsOnLayer( correspondingCopper ) )
1389 {
1390 *aWarning = wxString::Format( _( "Note: %s is tented; clearance will only be "
1391 "applied to holes." ),
1392 getItemDescription( &aItem ) );
1393 return true;
1394 }
1395
1396 return false;
1397 };
1398
1399 for( PCB_LAYER_ID layer : { F_SilkS, B_SilkS } )
1400 {
1401 wxString warning;
1402
1403 if( ( a->IsOnLayer( layer ) && isOnCorrespondingLayer( *b, layer, &warning ) )
1404 || ( b->IsOnLayer( layer ) && isOnCorrespondingLayer( *a, layer, &warning ) ) )
1405 {
1406 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1407 reportHeader( _( "Silkscreen clearance resolution for:" ), a, b, layer, r );
1408
1409 constraint = drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT, a, b, layer, r );
1410 clearance = constraint.m_Value.Min();
1411
1412 if( compileError )
1413 reportCompileError( r );
1414
1415 r->Report( "" );
1416
1417 if( !warning.IsEmpty() )
1418 r->Report( warning );
1419
1420 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1421 m_frame->StringFromValue( clearance, true ) ) );
1422
1423 r->Flush();
1424 }
1425 }
1426
1427 for( PCB_LAYER_ID layer : { F_CrtYd, B_CrtYd } )
1428 {
1429 bool aCourtyard = aFP && !aFP->GetCourtyard( layer ).IsEmpty();
1430 bool bCourtyard = bFP && !bFP->GetCourtyard( layer ).IsEmpty();
1431
1432 if( aCourtyard && bCourtyard )
1433 {
1434 r = dialog->AddHTMLPage( m_frame->GetBoard()->GetLayerName( layer ) );
1435 reportHeader( _( "Courtyard clearance resolution for:" ), a, b, layer, r );
1436
1437 constraint = drcEngine->EvalRules( COURTYARD_CLEARANCE_CONSTRAINT, a, b, layer, r );
1438 clearance = constraint.m_Value.Min();
1439
1440 if( compileError )
1441 reportCompileError( r );
1442
1443 r->Report( "" );
1444 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1445 m_frame->StringFromValue( clearance, true ) ) );
1446
1447 r->Flush();
1448 }
1449 }
1450
1451 if( a->HasHole() || b->HasHole() )
1452 {
1454 bool pageAdded = false;
1455
1456 if( a->HasHole() && b->IsOnLayer( active ) && IsCopperLayer( active ) )
1457 layer = active;
1458 else if( b->HasHole() && a->IsOnLayer( active ) && IsCopperLayer( active ) )
1459 layer = active;
1460 else if( a->HasHole() && b->IsOnCopperLayer() )
1461 layer = b->GetLayer();
1462 else if( b->HasHole() && a->IsOnCopperLayer() )
1463 layer = a->GetLayer();
1464
1465 if( layer >= 0 )
1466 {
1467 r = dialog->AddHTMLPage( _( "Hole" ) );
1468 pageAdded = true;
1469
1470 reportHeader( _( "Hole clearance resolution for:" ), a, b, layer, r );
1471
1472 constraint = drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1473 clearance = constraint.m_Value.Min();
1474
1475 if( compileError )
1476 reportCompileError( r );
1477
1478 r->Report( "" );
1479 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1480 m_frame->StringFromValue( clearance, true ) ) );
1481
1482 r->Flush();
1483 }
1484
1485 if( a->HasDrilledHole() || b->HasDrilledHole() )
1486 {
1487 if( !pageAdded )
1488 {
1489 r = dialog->AddHTMLPage( _( "Hole" ) );
1490 pageAdded = true;
1491 }
1492 else
1493 {
1494 r->Report( "" );
1495 r->Report( "" );
1496 r->Report( "" );
1497 }
1498
1499 reportHeader( _( "Hole-to-hole clearance resolution for:" ), a, b, r );
1500
1501 constraint = drcEngine->EvalRules( HOLE_TO_HOLE_CONSTRAINT, a, b, UNDEFINED_LAYER, r );
1502 clearance = constraint.m_Value.Min();
1503
1504 if( compileError )
1505 reportCompileError( r );
1506
1507 r->Report( "" );
1508 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1509 m_frame->StringFromValue( clearance, true ) ) );
1510
1511 r->Flush();
1512 }
1513 }
1514
1515 for( PCB_LAYER_ID edgeLayer : { Edge_Cuts, Margin } )
1516 {
1518
1519 if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
1520 {
1521 if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
1522 layer = active;
1523 else if( IsCopperLayer( b->GetLayer() ) )
1524 layer = b->GetLayer();
1525 }
1526 else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
1527 {
1528 if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
1529 layer = active;
1530 else if( IsCopperLayer( a->GetLayer() ) )
1531 layer = a->GetLayer();
1532 }
1533
1534 if( layer >= 0 )
1535 {
1536 wxString layerName = m_frame->GetBoard()->GetLayerName( edgeLayer );
1537 r = dialog->AddHTMLPage( layerName + wxS( " " ) + _( "Clearance" ) );
1538 reportHeader( _( "Edge clearance resolution for:" ), a, b, layer, r );
1539
1540 constraint = drcEngine->EvalRules( EDGE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1541 clearance = constraint.m_Value.Min();
1542
1543 if( compileError )
1544 reportCompileError( r );
1545
1546 r->Report( "" );
1547 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1548 m_frame->StringFromValue( clearance, true ) ) );
1549
1550 r->Flush();
1551 }
1552 }
1553
1554 r = dialog->AddHTMLPage( _( "Physical Clearances" ) );
1555
1556 if( compileError )
1557 {
1558 reportCompileError( r );
1559 }
1560 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
1561 {
1562 r->Report( "" );
1563 r->Report( _( "No 'physical_clearance' constraints defined." ) );
1564 }
1565 else
1566 {
1567 LSET reportLayers = layerIntersection;
1568 bool reported = false;
1569
1570 if( a->IsOnLayer( Edge_Cuts ) )
1571 {
1572 LSET edgeInteractingLayers = bFP ? LSET( { F_CrtYd, B_CrtYd } )
1574 reportLayers |= edgeInteractingLayers;
1575 }
1576
1577 if( b->IsOnLayer( Edge_Cuts ) )
1578 {
1579 LSET edgeInteractingLayers = aFP ? LSET( { F_CrtYd, B_CrtYd } )
1581 reportLayers |= edgeInteractingLayers;
1582 }
1583
1584 for( PCB_LAYER_ID layer : reportLayers )
1585 {
1586 reported = true;
1587 reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
1588
1589 constraint = drcEngine->EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
1590 clearance = constraint.m_Value.Min();
1591
1592 if( constraint.IsNull() )
1593 {
1594 r->Report( "" );
1595 r->Report( wxString::Format( _( "No 'physical_clearance' constraints in effect on %s." ),
1596 m_frame->GetBoard()->GetLayerName( layer ) ) );
1597 }
1598 else
1599 {
1600 r->Report( "" );
1601 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1602 m_frame->StringFromValue( clearance, true ) ) );
1603 }
1604
1605 r->Report( "" );
1606 r->Report( "" );
1607 r->Report( "" );
1608 }
1609
1610 if( !reported )
1611 {
1612 reportHeader( _( "Physical clearance resolution for:" ), a, b, r );
1613 r->Report( "" );
1614 r->Report( _( "Items share no relevant layers. No 'physical_clearance' constraints will "
1615 "be applied." ) );
1616 }
1617 }
1618
1619 if( a->HasHole() || b->HasHole() )
1620 {
1621 PCB_LAYER_ID layer;
1622
1623 if( a->HasHole() && b->IsOnLayer( active ) )
1624 layer = active;
1625 else if( b->HasHole() && a->IsOnLayer( active ) )
1626 layer = active;
1627 else if( a->HasHole() )
1628 layer = b->GetLayer();
1629 else
1630 layer = a->GetLayer();
1631
1632 reportHeader( _( "Physical hole clearance resolution for:" ), a, b, layer, r );
1633
1634 constraint = drcEngine->EvalRules( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, a, b, layer, r );
1635 clearance = constraint.m_Value.Min();
1636
1637 if( compileError )
1638 {
1639 reportCompileError( r );
1640 }
1641 else if( !drcEngine->HasRulesForConstraintType( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT ) )
1642 {
1643 r->Report( "" );
1644 r->Report( _( "No 'physical_hole_clearance' constraints defined." ) );
1645 }
1646 else
1647 {
1648 r->Report( "" );
1649 r->Report( wxString::Format( _( "Resolved min clearance: %s." ),
1650 m_frame->StringFromValue( clearance, true ) ) );
1651 }
1652 }
1653
1654 r->Flush();
1655
1656 dialog->Raise();
1657 dialog->Show( true );
1658}
1659
1660
1662{
1663#define EVAL_RULES( constraint, a, b, layer, r ) drcEngine->EvalRules( constraint, a, b, layer, r )
1664
1665 wxCHECK( m_frame, 0 );
1666
1667 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1668
1669 wxCHECK( selTool, 0 );
1670
1671 const PCB_SELECTION& selection = selTool->GetSelection();
1672 BOARD_ITEM* item = nullptr;
1673
1674 if( selection.Size() == 1 && selection.GetItem( 0 )->IsBOARD_ITEM() )
1675 {
1676 item = static_cast<BOARD_ITEM*>( selection.GetItem( 0 ) );
1677 }
1678 else if( selection.Size() == 0 )
1679 {
1680 static const std::vector<KICAD_T> constraintTypes = {
1681 PCB_PAD_T,
1682 PCB_VIA_T,
1684 PCB_ARC_T,
1685 PCB_ZONE_T,
1689 PCB_TEXT_T,
1692 };
1693
1694 Activate();
1695
1696 item = pickItemForInspection( aEvent, _( "Select item for constraints resolution..." ),
1697 constraintTypes, nullptr );
1698
1699 if( !item )
1700 return 0;
1701 }
1702 else
1703 {
1704 m_frame->ShowInfoBarError( _( "Select a single item for a constraints resolution report." ) );
1705 return 0;
1706 }
1707
1708 DIALOG_BOOK_REPORTER* dialog = m_frame->GetInspectConstraintsDialog();
1709
1710 wxCHECK( dialog, 0 );
1711
1712 dialog->DeleteAllPages();
1713 DRC_CONSTRAINT constraint;
1714
1715 bool compileError = false;
1716 bool courtyardError = false;
1717 std::unique_ptr<DRC_ENGINE> drcEngine = makeDRCEngine( &compileError, &courtyardError );
1718
1719 WX_HTML_REPORT_BOX* r = nullptr;
1720
1721 if( item->Type() == PCB_TRACE_T )
1722 {
1723 r = dialog->AddHTMLPage( _( "Track Width" ) );
1724 reportHeader( _( "Track width resolution for:" ), item, r );
1725
1726 constraint = EVAL_RULES( TRACK_WIDTH_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1727
1728 if( compileError )
1729 reportCompileError( r );
1730
1731 r->Report( "" );
1732 r->Report( wxString::Format( _( "Resolved width constraints: min %s; opt %s; max %s." ),
1733 reportMin( m_frame, constraint ),
1734 reportOpt( m_frame, constraint ),
1735 reportMax( m_frame, constraint ) ) );
1736
1737 r->Flush();
1738 }
1739
1740 if( item->Type() == PCB_VIA_T )
1741 {
1742 r = dialog->AddHTMLPage( _( "Via Diameter" ) );
1743 reportHeader( _( "Via diameter resolution for:" ), item, r );
1744
1745 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1746 constraint = EVAL_RULES( VIA_DIAMETER_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1747
1748 if( compileError )
1749 reportCompileError( r );
1750
1751 r->Report( "" );
1752 r->Report( wxString::Format( _( "Resolved diameter constraints: min %s; opt %s; max %s." ),
1753 reportMin( m_frame, constraint ),
1754 reportOpt( m_frame, constraint ),
1755 reportMax( m_frame, constraint ) ) );
1756
1757 r->Flush();
1758
1759 r = dialog->AddHTMLPage( _( "Via Annular Width" ) );
1760 reportHeader( _( "Via annular width resolution for:" ), item, r );
1761
1762 // PADSTACKS TODO: once we have padstacks we'll need to run this per-layer....
1763 constraint = EVAL_RULES( ANNULAR_WIDTH_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1764
1765 if( compileError )
1766 reportCompileError( r );
1767
1768 r->Report( "" );
1769 r->Report( wxString::Format( _( "Resolved annular width constraints: min %s; opt %s; max %s." ),
1770 reportMin( m_frame, constraint ),
1771 reportOpt( m_frame, constraint ),
1772 reportMax( m_frame, constraint ) ) );
1773
1774 r->Flush();
1775 }
1776
1777 if( ( item->Type() == PCB_PAD_T && static_cast<PAD*>( item )->GetDrillSize().x > 0 )
1778 || item->Type() == PCB_VIA_T )
1779 {
1780 r = dialog->AddHTMLPage( _( "Hole Size" ) );
1781 reportHeader( _( "Hole size resolution for:" ), item, r );
1782
1783 constraint = EVAL_RULES( HOLE_SIZE_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1784
1785 if( compileError )
1786 reportCompileError( r );
1787
1788 r->Report( "" );
1789 r->Report( wxString::Format( _( "Resolved hole size constraints: min %s; opt %s; max %s." ),
1790 reportMin( m_frame, constraint ),
1791 reportOpt( m_frame, constraint ),
1792 reportMax( m_frame, constraint ) ) );
1793
1794 r->Flush();
1795 }
1796
1797 if( item->Type() == PCB_PAD_T || item->Type() == PCB_SHAPE_T || dynamic_cast<PCB_TRACK*>( item ) )
1798 {
1799 r = dialog->AddHTMLPage( _( "Solder Mask" ) );
1800 reportHeader( _( "Solder mask expansion resolution for:" ), item, r );
1801
1802 constraint = EVAL_RULES( SOLDER_MASK_EXPANSION_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1803
1804 if( compileError )
1805 reportCompileError( r );
1806
1807 r->Report( "" );
1808 r->Report( wxString::Format( _( "Resolved solder mask expansion: %s." ),
1809 reportOpt( m_frame, constraint ) ) );
1810
1811 r->Flush();
1812 }
1813
1814 if( item->Type() == PCB_PAD_T )
1815 {
1816 r = dialog->AddHTMLPage( _( "Solder Paste" ) );
1817 reportHeader( _( "Solder paste absolute clearance resolution for:" ), item, r );
1818
1819 constraint = EVAL_RULES( SOLDER_PASTE_ABS_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1820
1821 if( compileError )
1822 reportCompileError( r );
1823
1824 r->Report( "" );
1825 r->Report( wxString::Format( _( "Resolved solder paste absolute clearance: %s." ),
1826 reportOpt( m_frame, constraint ) ) );
1827
1828 reportHeader( _( "Solder paste relative clearance resolution for:" ), item, r );
1829
1830 constraint = EVAL_RULES( SOLDER_PASTE_REL_MARGIN_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1831
1832 if( compileError )
1833 reportCompileError( r );
1834
1835 r->Report( "" );
1836 r->Report( "" );
1837 r->Report( "" );
1838 r->Report( wxString::Format( _( "Resolved solder paste relative clearance: %s." ),
1839 reportOpt( m_frame, constraint ) ) );
1840
1841 r->Flush();
1842 }
1843
1844 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T || item->Type() == PCB_TEXTBOX_T )
1845 {
1846 r = dialog->AddHTMLPage( _( "Text Size" ) );
1847 reportHeader( _( "Text height resolution for:" ), item, r );
1848
1849 constraint = EVAL_RULES( TEXT_HEIGHT_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1850
1851 if( compileError )
1852 reportCompileError( r );
1853
1854 r->Report( "" );
1855 r->Report( wxString::Format( _( "Resolved height constraints: min %s; opt %s; max %s." ),
1856 reportMin( m_frame, constraint ),
1857 reportOpt( m_frame, constraint ),
1858 reportMax( m_frame, constraint ) ) );
1859
1860 r->Report( "" );
1861 r->Report( "" );
1862 r->Report( "" );
1863 reportHeader( _( "Text thickness resolution for:" ), item, r );
1864
1865 constraint = EVAL_RULES( TEXT_THICKNESS_CONSTRAINT, item, nullptr, UNDEFINED_LAYER, r );
1866
1867 if( compileError )
1868 reportCompileError( r );
1869
1870 r->Report( "" );
1871 r->Report( wxString::Format( _( "Resolved thickness constraints: min %s; opt %s; max %s." ),
1872 reportMin( m_frame, constraint ),
1873 reportOpt( m_frame, constraint ),
1874 reportMax( m_frame, constraint ) ) );
1875
1876 r->Flush();
1877 }
1878
1879 r = dialog->AddHTMLPage( _( "Keepouts" ) );
1880 reportHeader( _( "Keepout resolution for:" ), item, r );
1881
1882 constraint = EVAL_RULES( DISALLOW_CONSTRAINT, item, nullptr, item->GetLayer(), r );
1883
1884 if( compileError )
1885 reportCompileError( r );
1886
1887 if( courtyardError )
1888 {
1889 r->Report( "" );
1890 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1891 + wxS( "&nbsp;&nbsp;" )
1892 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1893 + wxS( "</a>" ) );
1894 }
1895
1896 r->Report( "" );
1897
1898 if( constraint.m_DisallowFlags )
1899 r->Report( _( "Item <b>disallowed</b> at current location." ) );
1900 else
1901 r->Report( _( "Item allowed at current location." ) );
1902
1903 r->Flush();
1904
1905 r = dialog->AddHTMLPage( _( "Assertions" ) );
1906 reportHeader( _( "Assertions for:" ), item, r );
1907
1908 if( compileError )
1909 reportCompileError( r );
1910
1911 if( courtyardError )
1912 {
1913 r->Report( "" );
1914 r->Report( _( "Report may be incomplete: some footprint courtyards are malformed." )
1915 + wxS( "&nbsp;&nbsp;" )
1916 + wxS( "<a href='$DRC'>" ) + _( "Run DRC for a full analysis." )
1917 + wxS( "</a>" ) );
1918 }
1919
1920 drcEngine->ProcessAssertions( item, []( const DRC_CONSTRAINT* c ){}, r );
1921 r->Flush();
1922
1923 dialog->Raise();
1924 dialog->Show( true );
1925 return 0;
1926}
1927
1928
1930{
1931 wxCHECK( m_frame, 0 );
1932
1933 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
1934
1935 wxCHECK( selTool, 0 );
1936
1937 const PCB_SELECTION& selection = selTool->RequestSelection(
1938 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1939 {
1940 // Iterate from the back so we don't have to worry about removals.
1941 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1942 {
1943 BOARD_ITEM* item = aCollector[ i ];
1944
1945 if( !dynamic_cast<FOOTPRINT*>( item ) )
1946 aCollector.Remove( item );
1947 }
1948 } );
1949
1950 if( selection.Size() == 1 )
1951 DiffFootprint( static_cast<FOOTPRINT*>( selection.GetItem( 0 ) ) );
1952 else
1953 m_frame->ShowInfoBarError( _( "Select a footprint to diff with its library equivalent." ) );
1954
1955 return 0;
1956}
1957
1958
1960{
1961 wxCHECK( m_frame, 0 );
1962
1963 wxFileDialog dlg( m_frame, _( "Choose Board to Compare With" ), wxEmptyString,
1964 wxEmptyString, FILEEXT::PcbFileWildcard(),
1965 wxFD_OPEN | wxFD_FILE_MUST_EXIST );
1966
1967 if( dlg.ShowModal() != wxID_OK )
1968 return 0;
1969
1970 wxFileName otherFn( dlg.GetPath() );
1971 otherFn.MakeAbsolute();
1972
1973 const wxString otherPath = otherFn.GetFullPath();
1974
1975 wxFileName projectFn = otherFn;
1976 projectFn.SetExt( FILEEXT::ProjectFileExtension );
1977 const wxString projectPath = projectFn.GetFullPath();
1978
1979 wxFileName activeProjectFn( m_frame->Prj().GetProjectFullName() );
1980 activeProjectFn.MakeAbsolute();
1981
1982 // Refuse the self-compare before touching SETTINGS_MANAGER — attaching the
1983 // active PROJECT to a second BOARD would clobber its m_BoardSettings. Use
1984 // wxFileName::SameAs (path normalization + case folding) rather than a raw
1985 // string compare, matching SCH_INSPECTION_TOOL::CompareSchematic.
1986 if( projectFn.SameAs( activeProjectFn ) )
1987 {
1988 m_frame->ShowInfoBarError(
1989 _( "Select a board file from another project to compare." ) );
1990 return 0;
1991 }
1992
1993 return showBoardComparison( otherPath, projectPath, otherPath );
1994}
1995
1996
1997namespace
1998{
1999// Everything needed to show one board-vs-board comparison: the diff, the clones
2000// of removed live-board items (kept alive because the canvas references them),
2001// and the canvas-context switcher. Moveable so it can be swapped on reload.
2002struct PCB_DIFF_VIEW
2003{
2005 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2007};
2008
2009
2010PCB_DIFF_VIEW buildPcbDiffView( BOARD* aLive, BOARD* aOther, const wxString& aOtherPath )
2011{
2012 PCB_DIFF_VIEW view;
2013
2014 KICAD_DIFF::PCB_DIFFER differ( aLive, aOther, aOtherPath );
2015 view.result = differ.Diff();
2016
2017 const KICAD_DIFF::DIFF_COLOR_THEME theme;
2018
2019 std::map<KIID, KIGFX::COLOR4D> refOverrides;
2020 std::map<KIID, KIGFX::COLOR4D> compOverrides;
2021 std::map<KIID, KICAD_DIFF::CATEGORY> kiidCategories;
2022
2023 std::function<void( const KICAD_DIFF::ITEM_CHANGE& )> collect =
2024 [&]( const KICAD_DIFF::ITEM_CHANGE& aChange )
2025 {
2026 if( !aChange.id.empty() )
2027 {
2028 const KIID& kiid = aChange.id.back();
2029 kiidCategories[kiid] = KICAD_DIFF::CategoryFor( aChange.kind );
2030
2031 switch( aChange.kind )
2032 {
2033 case KICAD_DIFF::CHANGE_KIND::ADDED: compOverrides[kiid] = theme.added; break;
2035 refOverrides[kiid] = theme.removed;
2036 compOverrides[kiid] = theme.removed;
2037 break;
2039 refOverrides[kiid] = theme.modified;
2040 compOverrides[kiid] = theme.modified;
2041 break;
2042 default:
2043 refOverrides[kiid] = theme.conflict;
2044 compOverrides[kiid] = theme.conflict;
2045 break;
2046 }
2047 }
2048
2049 for( const KICAD_DIFF::ITEM_CHANGE& child : aChange.children )
2050 collect( child );
2051 };
2052
2053 for( const KICAD_DIFF::ITEM_CHANGE& change : view.result.changes )
2054 collect( change );
2055
2056 // Clone removed items from the live board because their originals are
2057 // pinned to the editor's VIEW.
2058 for( const auto& [kiid, color] : refOverrides )
2059 {
2060 if( color != theme.removed )
2061 continue;
2062
2063 if( BOARD_ITEM* found = KICAD_DIFF::FindPcbDiffItem( aLive, kiid ) )
2064 {
2065 if( BOARD_ITEM* clone = dynamic_cast<BOARD_ITEM*>( found->Clone() ) )
2066 view.clones.emplace_back( clone );
2067 }
2068 }
2069
2070 std::vector<KIGFX::VIEW_ITEM*> extraItems;
2071
2072 for( const std::unique_ptr<BOARD_ITEM>& clone : view.clones )
2073 {
2074 // A footprint draws nothing itself. Its pads, graphics and fields are
2075 // independent view items, so add them too or the clone is invisible.
2076 if( FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( clone.get() ) )
2077 {
2078 std::vector<KIGFX::VIEW_ITEM*> fpItems = KICAD_DIFF::CollectFootprintDiffContextItems( *fp );
2079 extraItems.insert( extraItems.end(), fpItems.begin(), fpItems.end() );
2080 }
2081 else
2082 {
2083 extraItems.push_back( clone.get() );
2084 }
2085 }
2086
2087 view.switcher =
2088 [other = aOther, color = theme.modified, overrides = compOverrides, extras = std::move( extraItems ),
2089 categories = kiidCategories] ( WIDGET_DIFF_CANVAS& aCanvas, const KIID_PATH& )
2090 {
2091 KICAD_DIFF::ConfigurePcbDiffCanvasContext( aCanvas, nullptr, other, color, overrides, extras,
2092 categories );
2093 };
2094
2095 return view;
2096}
2097} // namespace
2098
2099
2100int BOARD_INSPECTION_TOOL::showBoardComparison( const wxString& aOtherPath, const wxString& aProjectPath,
2101 const wxString& aComparisonLabel )
2102{
2103 // Load the comparison board into its own non-active PROJECT so the live
2104 // project settings are never overwritten. A missing .kicad_pro is fine
2105 // (LoadProject returns false but still inserts a defaults-only slot); a
2106 // present-but-malformed .kicad_pro is treated as failure.
2107 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2108 bool projectLoadOk = mgr && mgr->LoadProject( aProjectPath, false );
2109 PROJECT* otherPrj = mgr ? mgr->GetProject( aProjectPath ) : nullptr;
2110
2111 if( !otherPrj )
2112 {
2113 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2114 return 0;
2115 }
2116
2117 if( !projectLoadOk && wxFileName( aProjectPath ).FileExists() )
2118 {
2119 mgr->UnloadProject( otherPrj, false );
2120 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load project for %s" ), aOtherPath ) );
2121 return 0;
2122 }
2123
2125
2126 // Skip editor-frame initialization (drawing sheet singleton, BASE_SCREEN
2127 // globals, DRC engine, connectivity) so the read-only compare cannot leak
2128 // state into the live frame.
2129 BOARD_LOADER::OPTIONS loadOptions;
2130 loadOptions.initialize_after_load = false;
2131
2132 std::unique_ptr<BOARD> otherBoard;
2133
2134 try
2135 {
2136 otherBoard = BOARD_LOADER::Load( aOtherPath, pluginType, otherPrj, loadOptions );
2137 }
2138 catch( const IO_ERROR& ioe )
2139 {
2140 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s: %s" ), aOtherPath, ioe.What() ) );
2141 mgr->UnloadProject( otherPrj, false );
2142 return 0;
2143 }
2144 catch( ... )
2145 {
2146 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2147 mgr->UnloadProject( otherPrj, false );
2148 return 0;
2149 }
2150
2151 if( !otherBoard )
2152 {
2153 m_frame->ShowInfoBarError( wxString::Format( _( "Failed to load %s" ), aOtherPath ) );
2154 mgr->UnloadProject( otherPrj, false );
2155 return 0;
2156 }
2157
2158 BOARD* board = m_frame->GetBoard();
2159 PCB_DIFF_VIEW view = buildPcbDiffView( board, otherBoard.get(), aOtherPath );
2160
2161 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, board->GetFileName(), aComparisonLabel, view.result,
2163 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2164 dlgDiff->ShowModal();
2165
2166 // Destroy the dialog (its canvas drops its references to the board's items)
2167 // before the board is freed, or teardown dereferences freed items.
2168 dlgDiff.reset();
2169
2170 otherBoard->ClearProject();
2171 otherBoard.reset();
2172
2173 mgr->UnloadProject( otherPrj, false );
2174
2175 return 0;
2176}
2177
2178
2180{
2181 wxCHECK( m_frame, 0 );
2182
2183 BOARD* liveBoard = m_frame->GetBoard();
2184
2185 if( !liveBoard || liveBoard->GetFileName().IsEmpty() )
2186 {
2187 m_frame->ShowInfoBarError( _( "Save the board before comparing against local history." ) );
2188 return 0;
2189 }
2190
2191 const wxString projectPath = m_frame->Prj().GetProjectPath();
2192 LOCAL_HISTORY& history = m_frame->Kiway().LocalHistory();
2193
2194 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> snapshots = history.GetSnapshots( projectPath );
2195
2196 if( snapshots.empty() )
2197 {
2198 m_frame->ShowInfoBarError( _( "No local history snapshots for this project." ) );
2199 return 0;
2200 }
2201
2202 // The history repo stores files by project-relative path with forward slashes.
2203 wxFileName boardFn( liveBoard->GetFileName() );
2204 boardFn.MakeRelativeTo( projectPath );
2205 const wxString relPath = boardFn.GetFullPath( wxPATH_UNIX );
2206
2207 wxFileName projFn( m_frame->Prj().GetProjectFullName() );
2208 projFn.MakeRelativeTo( projectPath );
2209 const wxString projRel = projFn.GetFullPath( wxPATH_UNIX );
2210
2211 // One entry per distinct board version: newest-first, skipping commits with
2212 // no board or whose board content matches the previously kept one. This
2213 // drops schematic-only saves and collapses runs that left the board untouched.
2214 std::vector<LOCAL_HISTORY_SNAPSHOT_INFO> boardSnapshots;
2215 wxString prevFingerprint;
2216
2217 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2218 {
2219 wxString fingerprint = history.TreeFingerprint( projectPath, s.hash, wxS( ".kicad_pcb" ) );
2220
2221 if( fingerprint.IsEmpty() || fingerprint == prevFingerprint )
2222 continue;
2223
2224 prevFingerprint = fingerprint;
2225 boardSnapshots.push_back( s );
2226 }
2227
2228 if( boardSnapshots.empty() )
2229 {
2230 m_frame->ShowInfoBarError( _( "No local history snapshots change this board." ) );
2231 return 0;
2232 }
2233
2234 snapshots = std::move( boardSnapshots );
2235
2236 SETTINGS_MANAGER* mgr = m_frame->GetSettingsManager();
2237
2238 std::vector<wxString> labels;
2239
2240 for( const LOCAL_HISTORY_SNAPSHOT_INFO& s : snapshots )
2241 {
2242 wxString summary = s.summary.IsEmpty() ? s.message.BeforeFirst( '\n' ) : s.summary;
2243 labels.push_back( wxString::Format( wxS( "%s (%s)" ), summary, s.hash.Left( 8 ) ) );
2244 }
2245
2246 // State for the revision currently shown. Swapped on each dropdown change.
2247 std::unique_ptr<BOARD> curBoard;
2248 PROJECT* curPrj = nullptr;
2249 wxString curTempDir;
2250
2251 auto cleanupCurrent =
2252 [&]()
2253 {
2254 if( curBoard )
2255 {
2256 curBoard->ClearProject();
2257 curBoard.reset();
2258 }
2259
2260 // Skip if the project was already evicted from the manager.
2261 if( curPrj && mgr->IsProjectLoaded( curPrj ) )
2262 mgr->UnloadProject( curPrj, false );
2263
2264 curPrj = nullptr;
2265
2266 if( !curTempDir.IsEmpty() )
2267 {
2268 wxFileName::Rmdir( curTempDir, wxPATH_RMDIR_RECURSIVE );
2269 curTempDir.Clear();
2270 }
2271 };
2272
2273 // Extract + load snapshot aIndex into the out-params. Cleans up its own temp
2274 // on any failure.
2275 auto loadRevision =
2276 [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir ) -> bool
2277 {
2278 const wxString hash = snapshots[aIndex].hash;
2279 wxFileName dirFn;
2280 dirFn.AssignDir( wxFileName::GetTempDir() );
2281 dirFn.AppendDir( wxS( "kicad-history-" ) + hash.Left( 8 ) );
2282 const wxString tempDir = dirFn.GetPath();
2283
2284 // Extract just the board and project file (its real net classes and
2285 // design settings), skipping the schematic, 3D models, gerbers, etc.
2286 if( !history.ExtractAllFilesAtCommit( projectPath, hash, tempDir,
2287 { wxS( ".kicad_pcb" ), wxS( ".kicad_pro" ) } ) )
2288 {
2289 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2290 return false;
2291 }
2292
2293 const wxString boardPath = tempDir + wxS( "/" ) + relPath;
2294 const wxString proPath = tempDir + wxS( "/" ) + projRel;
2295
2296 mgr->LoadProject( proPath, false );
2297 PROJECT* prj = mgr->GetProject( proPath );
2298
2299 if( !prj )
2300 {
2301 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2302 return false;
2303 }
2304
2305 BOARD_LOADER::OPTIONS loadOptions;
2306 loadOptions.initialize_after_load = false;
2307
2308 std::unique_ptr<BOARD> loaded;
2309
2310 try
2311 {
2312 loaded = BOARD_LOADER::Load( boardPath,
2314 prj, loadOptions );
2315 }
2316 catch( ... )
2317 {
2318 // A historical board may be malformed or in a format this build cannot parse. Skip it
2319 // rather than letting the throw escape.
2320 mgr->UnloadProject( prj, false );
2321 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2322 return false;
2323 }
2324
2325 if( !loaded )
2326 {
2327 mgr->UnloadProject( prj, false );
2328 wxFileName::Rmdir( tempDir, wxPATH_RMDIR_RECURSIVE );
2329 return false;
2330 }
2331
2332 aBoard = std::move( loaded );
2333 aPrj = prj;
2334 aTempDir = tempDir;
2335 return true;
2336 };
2337
2338 // Load a revision and build its diff view, guarding both the parse and the
2339 // diff so a bad snapshot is skipped instead of crashing.
2340 auto loadView =
2341 [&]( int aIndex, std::unique_ptr<BOARD>& aBoard, PROJECT*& aPrj, wxString& aTempDir,
2342 PCB_DIFF_VIEW& aView ) -> bool
2343 {
2344 if( !loadRevision( aIndex, aBoard, aPrj, aTempDir ) )
2345 return false;
2346
2347 try
2348 {
2349 aView = buildPcbDiffView( liveBoard, aBoard.get(), aTempDir + wxS( "/" ) + relPath );
2350 }
2351 catch( ... )
2352 {
2353 aBoard->ClearProject();
2354 aBoard.reset();
2355 mgr->UnloadProject( aPrj, false );
2356 aPrj = nullptr;
2357 wxFileName::Rmdir( aTempDir, wxPATH_RMDIR_RECURSIVE );
2358 aTempDir.Clear();
2359 return false;
2360 }
2361
2362 return true;
2363 };
2364
2365 PCB_DIFF_VIEW view;
2366 int startIndex = 0;
2367
2368 if( !loadView( 0, curBoard, curPrj, curTempDir, view ) )
2369 {
2370 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2371 return 0;
2372 }
2373
2374 // Default to the first commit back from HEAD that actually differs from the
2375 // current board, so opening lands on real changes rather than an in-sync HEAD.
2376 while( view.result.Empty() && startIndex + 1 < static_cast<int>( snapshots.size() ) )
2377 {
2378 std::unique_ptr<BOARD> nextBoard;
2379 PROJECT* nextPrj = nullptr;
2380 wxString nextTempDir;
2381 PCB_DIFF_VIEW nextView;
2382
2383 if( !loadView( startIndex + 1, nextBoard, nextPrj, nextTempDir, nextView ) )
2384 break;
2385
2386 cleanupCurrent();
2387 view = std::move( nextView );
2388 curBoard = std::move( nextBoard );
2389 curPrj = nextPrj;
2390 curTempDir = nextTempDir;
2391 startIndex++;
2392 }
2393
2394 int shownIndex = startIndex;
2395
2396 auto dlgDiff = std::make_unique<DIALOG_KICAD_DIFF>( m_frame, liveBoard->GetFileName(), labels[startIndex],
2398 KICAD_DIFF::DOCUMENT_GEOMETRY{}, view.switcher );
2399
2400 dlgDiff->SetRevisionChooser(
2401 labels, startIndex,
2402 [&]( int aIndex )
2403 {
2404 if( aIndex == shownIndex )
2405 return;
2406
2407 std::unique_ptr<BOARD> newBoard;
2408 PROJECT* newPrj = nullptr;
2409 wxString newTempDir;
2410 PCB_DIFF_VIEW newView;
2411
2412 if( !loadView( aIndex, newBoard, newPrj, newTempDir, newView ) )
2413 {
2414 m_frame->ShowInfoBarError( _( "Could not compare against the selected snapshot." ) );
2415 return;
2416 }
2417
2418 dlgDiff->Reload( liveBoard->GetFileName(), labels[aIndex], newView.result,
2419 /*aReferenceGeometry=*/{}, /*aComparisonGeometry=*/{}, newView.switcher, {} );
2420
2421 // The canvas now references newBoard, so the previous one can go.
2422 cleanupCurrent();
2423 view = std::move( newView );
2424 curBoard = std::move( newBoard );
2425 curPrj = newPrj;
2426 curTempDir = newTempDir;
2427 shownIndex = aIndex;
2428 } );
2429
2430 dlgDiff->ShowModal();
2431
2432 // Destroy the dialog before the board it references is freed.
2433 dlgDiff.reset();
2434
2435 cleanupCurrent();
2436 return 0;
2437}
2438
2439
2441{
2442 wxCHECK( m_frame, 0 );
2443
2444 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2445
2446 wxCHECK( selTool, 0 );
2447
2448 const PCB_SELECTION& selection = selTool->GetSelection();
2449
2450 if( selection.Size() != 1 || selection.Front()->Type() != PCB_FOOTPRINT_T )
2451 {
2452 m_frame->ShowInfoBarError( _( "Select a footprint for a footprint associations report." ) );
2453 return 0;
2454 }
2455
2456 DIALOG_FOOTPRINT_ASSOCIATIONS dlg( m_frame, static_cast<FOOTPRINT*>( selection.Front() ) );
2457
2458 dlg.ShowModal();
2459
2460 return 0;
2461}
2462
2463
2464void BOARD_INSPECTION_TOOL::DiffFootprint( FOOTPRINT* aFootprint, wxTopLevelWindow* aReparentTo )
2465{
2466 DIALOG_BOOK_REPORTER* dialog = m_frame->GetFootprintDiffDialog();
2467
2468 wxCHECK( dialog, /* void */ );
2469
2470 dialog->DeleteAllPages();
2471 dialog->SetUserItemID( aFootprint->m_Uuid );
2472
2473 LIB_ID fpID = aFootprint->GetFPID();
2474 wxString libName = fpID.GetLibNickname();
2475 wxString fpName = fpID.GetLibItemName();
2476 WX_HTML_REPORT_BOX* r = nullptr;
2477
2478 r = dialog->AddHTMLPage( _( "Summary" ) );
2479
2480 r->Report( wxS( "<h7>" ) + _( "Board vs library diff for:" ) + wxS( "</h7>" ) );
2481 r->Report( wxS( "<ul><li>" ) + EscapeHTML( getItemDescription( aFootprint ) ) + wxS( "</li>" )
2482 + wxS( "<li>" ) + _( "Library: " ) + EscapeHTML( libName ) + wxS( "</li>" )
2483 + wxS( "<li>" ) + _( "Library item: " ) + EscapeHTML( fpName ) + wxS( "</li></ul>" ) );
2484
2485 r->Report( "" );
2486
2487 PROJECT* project = aFootprint->GetBoard()->GetProject();
2489
2490 if( !adapter->HasLibrary( libName, false ) )
2491 {
2492 r->Report( _( "The library is not included in the current configuration." )
2493 + wxS( "&nbsp;&nbsp;&nbsp" )
2494 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2495 + wxS( "</a>" ) );
2496
2497 }
2498 else if( !adapter->HasLibrary( libName, true ) )
2499 {
2500 r->Report( _( "The library is not enabled in the current configuration." )
2501 + wxS( "&nbsp;&nbsp;&nbsp" )
2502 + wxS( "<a href='$CONFIG'>" ) + _( "Manage Footprint Libraries" )
2503 + wxS( "</a>" ) );
2504
2505 }
2506 else
2507 {
2508 std::shared_ptr<FOOTPRINT> libFootprint;
2509
2510 try
2511 {
2512 libFootprint.reset( adapter->LoadFootprint( libName, fpName, true ) );
2513 }
2514 catch( const IO_ERROR& )
2515 {
2516 }
2517
2518 if( !libFootprint )
2519 {
2520 r->Report( wxString::Format( _( "The library no longer contains the item %s." ),
2521 fpName) );
2522 }
2523 else
2524 {
2525 if( !aFootprint->FootprintNeedsUpdate( libFootprint.get(), 0, r ) )
2526 r->Report( _( "No relevant differences detected." ) );
2527
2528 wxPanel* panel = dialog->AddBlankPage( _( "Visual" ) );
2530
2531 diff->DisplayDiff( aFootprint, libFootprint );
2532 }
2533 }
2534
2535 r->Flush();
2536
2537 if( aReparentTo )
2538 KIPLATFORM::UI::ReparentWindow( dialog, aReparentTo );
2539 else
2540 dialog->Raise();
2541
2542 dialog->Show( true );
2543}
2544
2545
2547{
2548 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
2549
2550 FOOTPRINT_DIFF_WIDGET* diffWidget = new FOOTPRINT_DIFF_WIDGET( aParentPanel, m_frame->Kiway() );
2551
2552 sizer->Add( diffWidget, 1, wxEXPAND | wxALL, 5 );
2553 aParentPanel->SetSizer( sizer );
2554 aParentPanel->Layout();
2555
2556 return diffWidget;
2557}
2558
2559
2561{
2562 BOARD_ITEM* item = aEvent.Parameter<BOARD_ITEM*>();
2563
2564 m_frame->m_ProbingSchToPcb = true; // recursion guard
2565 {
2566 m_toolMgr->RunAction( ACTIONS::selectionClear );
2567
2568 if( item )
2569 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, item );
2570 }
2571 m_frame->m_ProbingSchToPcb = false;
2572
2573 bool request3DviewRedraw = frame()->GetPcbNewSettings()->m_Display.m_Live3DRefresh;
2574
2575 if( item && item->Type() != PCB_FOOTPRINT_T )
2576 request3DviewRedraw = false;
2577
2578 // Update 3D viewer highlighting
2579 if( request3DviewRedraw )
2580 m_frame->Update3DView( false, true );
2581
2582 return 0;
2583}
2584
2585
2586 bool BOARD_INSPECTION_TOOL::highlightNet( const VECTOR2D& aPosition, bool aUseSelection )
2587{
2588 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
2590 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2591
2592 int net = -1;
2593 bool enableHighlight = false;
2594
2595 if( aUseSelection )
2596 {
2597 const PCB_SELECTION& selection = selectionTool->GetSelection();
2598 std::set<int> netcodes;
2599
2600 for( EDA_ITEM* item : selection )
2601 {
2602 if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2603 netcodes.insert( ci->GetNetCode() );
2604 }
2605
2606 enableHighlight = !netcodes.empty();
2607
2608 if( enableHighlight && netcodes.size() > 1 )
2609 {
2610 // If we are doing a multi-highlight, cross-probing back and other stuff is not
2611 // yet supported
2612 settings->SetHighlight( netcodes );
2613 board->ResetNetHighLight();
2614
2615 for( int multiNet : netcodes )
2616 board->SetHighLightNet( multiNet, true );
2617
2618 board->HighLightON();
2619 m_toolMgr->GetView()->UpdateAllLayersColor();
2620 m_currentlyHighlighted = netcodes;
2621 return true;
2622 }
2623 else if( enableHighlight )
2624 {
2625 net = *netcodes.begin();
2626 }
2627 }
2628
2629 // If we didn't get a net to highlight from the selection, use the cursor
2630 if( net < 0 )
2631 {
2632 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2633 guide.SetIgnoreZoneFills( false );
2634 guide.SetIgnoreNoNets( true );
2635
2636 PCB_LAYER_ID activeLayer = static_cast<PCB_LAYER_ID>( view()->GetTopLayer() );
2637 guide.SetPreferredLayer( activeLayer );
2638
2639 GENERAL_COLLECTOR collector;
2640 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, aPosition,
2641 guide );
2642
2643 if( collector.GetCount() == 0 )
2644 collector.Collect( board, { PCB_ZONE_T }, aPosition, guide );
2645
2646 // Apply the active selection filter, except we want to allow picking locked items for
2647 // highlighting even if the user has disabled them for selection
2648 PCB_SELECTION_FILTER_OPTIONS& filter = selectionTool->GetFilter();
2649
2650 bool saved = filter.lockedItems;
2651 filter.lockedItems = true;
2652
2653 selectionTool->FilterCollectedItems( collector, true, nullptr );
2654
2655 filter.lockedItems = saved;
2656
2657 // Clear the previous highlight
2658 //m_frame->SendMessageToEESCHEMA( nullptr );
2659
2660 bool highContrast = settings->GetHighContrast();
2661 PCB_LAYER_ID contrastLayer = settings->GetPrimaryHighContrastLayer();
2662
2663 for( int i = collector.GetCount() - 1; i >= 0; i-- )
2664 {
2665 LSET itemLayers = collector[i]->GetLayerSet();
2666
2667 if( ( itemLayers & LSET::AllCuMask() ).none() ||
2668 ( highContrast && !itemLayers.Contains( contrastLayer ) ) )
2669 {
2670 collector.Remove( i );
2671 continue;
2672 }
2673 }
2674
2675 enableHighlight = ( collector.GetCount() > 0 );
2676
2677 // Obtain net code for the clicked item
2678 if( enableHighlight )
2679 {
2680 BOARD_CONNECTED_ITEM* targetItem = static_cast<BOARD_CONNECTED_ITEM*>( collector[0] );
2681
2682 if( targetItem->Type() == PCB_PAD_T )
2683 m_frame->SendCrossProbeItem( targetItem );
2684
2685 net = targetItem->GetNetCode();
2686 }
2687 }
2688
2689 const std::set<int>& netcodes = settings->GetHighlightNetCodes();
2690
2691 if( !aUseSelection && net >= 0 && netcodes.size() == 1 && netcodes.contains( net ) && settings->IsHighlightEnabled() )
2692 {
2693 if( BOARD* board2 = m_frame->GetBoard() )
2694 {
2695 if( NETINFO_ITEM* netinfo = board2->FindNet( net ) )
2696 {
2697 wxString sig = netinfo->GetNetChain();
2698 if( !sig.IsEmpty() )
2699 {
2700 int count = 0;
2701
2702 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2703 {
2704 if( n->GetNetChain() == sig )
2705 count++;
2706 }
2707
2708 if( count > 1 )
2709 {
2710 std::set<int> sigCodes;
2711
2712
2713 for( NETINFO_ITEM* n : board2->GetNetInfo() )
2714 {
2715 if( n->GetNetChain() == sig )
2716 sigCodes.insert( n->GetNetCode() );
2717 }
2718
2719 settings->SetHighlight( sigCodes, true );
2720 m_toolMgr->GetView()->UpdateAllLayersColor();
2721 m_currentlyHighlighted = sigCodes;
2723
2724 board2->ResetNetHighLight();
2725
2726 for( int c : sigCodes )
2727 board2->SetHighLightNet( c, true );
2728
2729 board2->HighLightON();
2730
2731 if( auto pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2732 pcbSettings->SetHighlightedNetChain( sig );
2733
2734 return true;
2735 }
2736 }
2737 }
2738 }
2739
2740 enableHighlight = !settings->IsHighlightEnabled();
2741 }
2742 else if( !aUseSelection && netcodes.size() == 1 && netcodes.contains( net ) )
2743 {
2744 enableHighlight = !settings->IsHighlightEnabled();
2745 }
2746
2747 if( enableHighlight != settings->IsHighlightEnabled() || !netcodes.count( net ) )
2748 {
2749 if( !netcodes.empty() )
2750 m_lastHighlighted = netcodes;
2751
2752 KIGFX::VIEW* view = m_toolMgr->GetView();
2753 const bool wasEnabled = settings->IsHighlightEnabled();
2754 const std::set<int> before = settings->GetHighlightNetCodes();
2755
2756 settings->SetHighlight( enableHighlight, net );
2757
2758 auto* pcbSettings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings );
2759 const bool retained = view->GetGAL()->HasRetainedScene();
2760
2761 auto onChangedNet =
2762 [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
2763 {
2764 BOARD_CONNECTED_ITEM* connected = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
2765
2766 return connected
2767 && ( connected->GetNetCode() == net || before.contains( connected->GetNetCode() ) );
2768 };
2769
2770 // Switching between highlighted nets recolours only those two nets. Toggling highlight
2771 // dims or restores everything else, which a canvas that fades layers does itself.
2772 const bool switchedNet = wasEnabled && enableHighlight;
2773
2774 if( retained && ( switchedNet || pcbSettings->HasCanvasFadedLayers() ) )
2775 {
2776 if( !switchedNet )
2777 {
2778 view->UpdateLayersColor( false,
2779 [pcbSettings]( int aLayer )
2780 {
2781 return !pcbSettings->IsCanvasFadedLayer( aLayer );
2782 } );
2783 }
2784
2785 view->UpdateAllItemsConditionally( KIGFX::COLOR, onChangedNet );
2786 }
2787 else
2788 {
2789 view->UpdateAllLayersColor();
2790 }
2791 }
2792
2793 // Store the highlighted netcode in the current board (for dialogs for instance)
2794 if( enableHighlight && net >= 0 )
2795 {
2796 m_HighlightedNetChain.clear();
2797 m_currentlyHighlighted = netcodes;
2798 board->SetHighLightNet( net );
2799 board->HighLightON();
2800
2801 NETINFO_ITEM* netinfo = board->FindNet( net );
2802
2803 if( netinfo )
2804 {
2805 std::vector<MSG_PANEL_ITEM> items;
2806 netinfo->GetMsgPanelInfo( m_frame, items );
2807 m_frame->SetMsgPanel( items );
2808 m_frame->SendCrossProbeNetName( netinfo->GetNetname() );
2809 }
2810 }
2811 else
2812 {
2813 m_HighlightedNetChain.clear();
2814 m_currentlyHighlighted.clear();
2815 board->ResetNetHighLight();
2816 m_frame->SetMsgPanel( board );
2817 m_frame->SendCrossProbeNetName( "" );
2818 }
2819
2820 return true;
2821}
2822
2823
2825{
2826 int netcode = aEvent.Parameter<int>();
2827 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2828 const std::set<int>& highlighted = settings->GetHighlightNetCodes();
2829
2830 if( netcode > 0 )
2831 {
2832 m_lastHighlighted = highlighted;
2833 settings->SetHighlight( true, netcode );
2834 m_toolMgr->GetView()->UpdateAllLayersColor();
2835 m_currentlyHighlighted.clear();
2836 m_currentlyHighlighted.insert( netcode );
2837
2838 // If this net belongs to a multi-net chain and was already highlighted, escalate to chain highlight
2839 if( BOARD* board = m_frame->GetBoard() )
2840 {
2841 if( NETINFO_ITEM* net = board->FindNet( netcode ) )
2842 {
2843 wxString sig = net->GetNetChain();
2844 if( !sig.IsEmpty() )
2845 {
2846 int count = 0;
2847 for( NETINFO_ITEM* n : board->GetNetInfo() )
2848 if( n->GetNetChain() == sig )
2849 count++;
2850 bool alreadyHighlighted = highlighted.count( netcode );
2851 if( count > 1 && alreadyHighlighted )
2852 {
2853 if( m_HighlightedNetChain != sig )
2854 {
2855 TOOL_EVENT sigEvt = PCB_ACTIONS::highlightNetChain.MakeEvent();
2856 HighlightNetChain( sigEvt );
2857 }
2858 }
2859 }
2860 }
2861 }
2862 }
2863 else if( aEvent.IsAction( &PCB_ACTIONS::highlightNetSelection ) )
2864 {
2865 // Highlight selection (cursor position will be ignored)
2866 highlightNet( getViewControls()->GetMousePosition(), true );
2867 }
2868 else if( aEvent.IsAction( &PCB_ACTIONS::toggleLastNetHighlight ) )
2869 {
2870 std::set<int> temp = highlighted;
2871 settings->SetHighlight( m_lastHighlighted );
2872 m_toolMgr->GetView()->UpdateAllLayersColor();
2874 m_lastHighlighted = std::move( temp );
2875 }
2876 else if( aEvent.IsAction( &PCB_ACTIONS::toggleNetHighlight ) )
2877 {
2878 bool turnOn = highlighted.empty() && !m_currentlyHighlighted.empty();
2879 settings->SetHighlight( m_currentlyHighlighted, turnOn );
2880 m_toolMgr->GetView()->UpdateAllLayersColor();
2881 }
2882 else // Highlight the net belonging to the item under the cursor
2883 {
2884 highlightNet( getViewControls()->GetMousePosition(), false );
2885 }
2886
2887 return 0;
2888}
2889
2890
2892{
2894 VECTOR2D cursorPos = controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2895 BOARD_ITEM* item = nullptr;
2896
2897 {
2898 // Collect nearest connectable item at cursor position
2899 BOARD* board = m_frame->GetBoard();
2900 GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
2901 GENERAL_COLLECTOR collector;
2902 collector.Collect( board, { PCB_PAD_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_SHAPE_T }, cursorPos, guide );
2903
2904 if( collector.GetCount() > 0 )
2905 item = collector[0];
2906 }
2907
2908 wxString sig;
2909
2910 if( item )
2911 {
2912 NETINFO_ITEM* net = nullptr;
2913
2914 if( PAD* pad = dynamic_cast<PAD*>( item ) )
2915 net = pad->GetNet();
2916 else if( BOARD_CONNECTED_ITEM* ci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
2917 net = ci->GetNet();
2918
2919 if( net )
2920 sig = net->GetNetChain();
2921 }
2922
2923 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
2924
2925 // If no chain under cursor and a chain is currently highlighted, toggle off
2926 if( sig.IsEmpty() && !m_HighlightedNetChain.IsEmpty() )
2927 {
2928 m_HighlightedNetChain.clear();
2929 settings->SetHighlight( false );
2930 m_currentlyHighlighted.clear();
2931 m_toolMgr->GetView()->UpdateAllLayersColor();
2932
2933 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2934 pcbSettings->SetHighlightedNetChain( wxString() );
2935
2936 return 0;
2937 }
2938
2939
2940 // If same chain already highlighted, clear highlight
2941 if( !sig.IsEmpty() && sig == m_HighlightedNetChain )
2942 {
2943 m_HighlightedNetChain.clear();
2944 settings->SetHighlight( false );
2945 m_currentlyHighlighted.clear();
2946 m_toolMgr->GetView()->UpdateAllLayersColor();
2947
2948 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2949 pcbSettings->SetHighlightedNetChain( wxString() );
2950
2951 return 0;
2952 }
2953
2954 // If we have a net highlight active but no chain highlight, convert to chain highlight
2955 if( !sig.IsEmpty() && m_HighlightedNetChain.IsEmpty() && !m_currentlyHighlighted.empty() )
2956 {
2957 // Determine chain from first highlighted net if cursor item had no chain
2958 if( sig.IsEmpty() )
2959 {
2960 int firstCode = *m_currentlyHighlighted.begin();
2961
2962 if( NETINFO_ITEM* net = m_frame->GetBoard()->FindNet( firstCode ) )
2963 sig = net->GetNetChain();
2964 }
2965 }
2966
2968
2969 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
2970 pcbSettings->SetHighlightedNetChain( sig );
2971
2972 std::set<int> codes;
2973
2974 if( !sig.IsEmpty() )
2975 {
2976 for( NETINFO_ITEM* net : m_frame->GetBoard()->GetNetInfo() )
2977 {
2978 if( net->GetNetChain() == sig )
2979 codes.insert( net->GetNetCode() );
2980 }
2981 }
2982
2983 settings->SetHighlight( codes, true );
2984 m_toolMgr->GetView()->UpdateAllLayersColor();
2985 m_currentlyHighlighted = codes;
2986
2987 return 0;
2988}
2989
2990
2992{
2993 if( m_HighlightedNetChain.IsEmpty() )
2994 return 0;
2995
2996 // Parameters are passed as a single pair<old,new>
2997 std::pair<wxString, wxString> ids = aEvent.Parameter<std::pair<wxString, wxString>>();
2998 KIID oldId( ids.first );
2999 KIID newId( ids.second );
3000 m_frame->GetBoard()->ReplaceNetChainTerminalPad( m_HighlightedNetChain, oldId, newId );
3001 return 0;
3002}
3003
3004
3006{
3007 BOARD* board = static_cast<BOARD*>( m_toolMgr->GetModel() );
3008 KIGFX::RENDER_SETTINGS* settings = m_toolMgr->GetView()->GetPainter()->GetSettings();
3009
3010 m_currentlyHighlighted.clear();
3011 m_lastHighlighted.clear();
3012
3013 board->ResetNetHighLight();
3014 settings->SetHighlight( false );
3015
3016 // Also clear any chain-specific state
3017 if( KIGFX::PCB_RENDER_SETTINGS* pcbSettings = dynamic_cast<KIGFX::PCB_RENDER_SETTINGS*>( settings ) )
3018 pcbSettings->SetHighlightedNetChain( wxString() );
3019
3020 m_toolMgr->GetView()->UpdateAllLayersColor();
3021 m_frame->SetMsgPanel( board );
3022 m_frame->SendCrossProbeNetName( "" );
3023 return 0;
3024}
3025
3026
3028{
3029 PCB_PICKER_TOOL* picker = m_toolMgr->GetTool<PCB_PICKER_TOOL>();
3030
3031 // Deactivate other tools; particularly important if another PICKER is currently running
3032 Activate();
3033
3034 picker->SetCursor( KICURSOR::BULLSEYE );
3035 picker->SetSnapping( false );
3036 picker->ClearHandlers();
3037
3038 picker->SetClickHandler(
3039 [this]( const VECTOR2D& pt ) -> bool
3040 {
3041 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3042
3043 m_toolMgr->RunAction( ACTIONS::selectionClear );
3045 EDIT_TOOL::PadFilter );
3046
3047 PCB_SELECTION& selection = selectionTool->GetSelection();
3048
3049 if( selection.Empty() )
3050 {
3052 EDIT_TOOL::FootprintFilter );
3053 selection = selectionTool->GetSelection();
3054 }
3055
3056 if( selection.Empty() )
3057 {
3058 // Clear the previous local ratsnest if we click off all items
3059 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
3060 {
3061 for( PAD* pad : fp->Pads() )
3062 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
3063 }
3064 }
3065 else
3066 {
3067 for( EDA_ITEM* item : selection )
3068 {
3069 if( PAD* pad = dyn_cast<PAD*>( item) )
3070 {
3071 pad->SetLocalRatsnestVisible( !pad->GetLocalRatsnestVisible() );
3072 }
3073 else if( FOOTPRINT* fp = dyn_cast<FOOTPRINT*>( item) )
3074 {
3075 if( !fp->Pads().empty() )
3076 {
3077 bool enable = !fp->Pads()[0]->GetLocalRatsnestVisible();
3078
3079 for( PAD* childPad : fp->Pads() )
3080 childPad->SetLocalRatsnestVisible( enable );
3081 }
3082 }
3083 }
3084 }
3085
3086 m_toolMgr->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
3087
3088 return true;
3089 } );
3090
3091 picker->SetFinalizeHandler(
3092 [this]( int aCondition )
3093 {
3094 if( aCondition != PCB_PICKER_TOOL::END_ACTIVATE )
3095 {
3096 for( FOOTPRINT* fp : getModel<BOARD>()->Footprints() )
3097 {
3098 for( PAD* pad : fp->Pads() )
3099 pad->SetLocalRatsnestVisible( displayOptions().m_ShowGlobalRatsnest );
3100 }
3101 }
3102 } );
3103
3104 m_toolMgr->RunAction( ACTIONS::pickerTool, &aEvent );
3105
3106 return 0;
3107}
3108
3109
3111{
3112 VECTOR2I delta = aEvent.Parameter<VECTOR2I>();
3113
3114 if( delta == VECTOR2I() )
3115 {
3116 // We can delete the existing map to force a recalculation
3117 delete m_dynamicData;
3118 m_dynamicData = nullptr;
3119 }
3120
3121 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3122 PCB_SELECTION& selection = selectionTool->GetSelection();
3123 std::shared_ptr<CONNECTIVITY_DATA> connectivity = getModel<BOARD>()->GetConnectivity();
3124
3125 if( selection.Empty() )
3126 {
3127 connectivity->ClearLocalRatsnest();
3128 delete m_dynamicData;
3129 m_dynamicData = nullptr;
3130 }
3131 else
3132 {
3134 }
3135
3136 return 0;
3137}
3138
3139
3141{
3142 getModel<BOARD>()->GetConnectivity()->ClearLocalRatsnest();
3143 delete m_dynamicData;
3144 m_dynamicData = nullptr;
3145
3146 return 0;
3147}
3148
3149
3151{
3152 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3153 SELECTION& selection = selectionTool->GetSelection();
3154 std::shared_ptr<CONNECTIVITY_DATA> connectivity = board()->GetConnectivity();
3155 std::vector<BOARD_ITEM*> items;
3156 std::deque<EDA_ITEM*> queued_items( selection.begin(), selection.end() );
3157
3158 for( std::size_t i = 0; i < queued_items.size(); ++i )
3159 {
3160 if( !queued_items[i]->IsBOARD_ITEM() )
3161 continue;
3162
3163 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( queued_items[i] );
3164
3165 wxCHECK2( item, continue );
3166
3167 if( item->Type() == PCB_FOOTPRINT_T )
3168 {
3169 for( PAD* pad : static_cast<FOOTPRINT*>( item )->Pads() )
3170 {
3171 if( pad->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3172 items.push_back( pad );
3173 }
3174 }
3175 else if( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T )
3176 {
3177 item->RunOnChildren( [ &queued_items ]( BOARD_ITEM *aItem )
3178 {
3179 queued_items.push_back( aItem );
3180 },
3182 }
3183 else if( BOARD_CONNECTED_ITEM* boardItem = dyn_cast<BOARD_CONNECTED_ITEM*>( item ) )
3184 {
3185 if( boardItem->GetLocalRatsnestVisible() || displayOptions().m_ShowModuleRatsnest )
3186 items.push_back( boardItem );
3187 }
3188 }
3189
3190 if( items.empty() || std::none_of( items.begin(), items.end(),
3191 []( const BOARD_ITEM* aItem )
3192 {
3193 return( aItem->Type() == PCB_TRACE_T
3194 || aItem->Type() == PCB_PAD_T
3195 || aItem->Type() == PCB_ARC_T
3196 || aItem->Type() == PCB_ZONE_T
3197 || aItem->Type() == PCB_FOOTPRINT_T
3198 || aItem->Type() == PCB_VIA_T
3199 || aItem->Type() == PCB_SHAPE_T );
3200 } ) )
3201 {
3202 return;
3203 }
3204
3205 if( !m_dynamicData )
3206 {
3207 m_dynamicData = new CONNECTIVITY_DATA( board()->GetConnectivity(), items, true );
3208 connectivity->BlockRatsnestItems( items );
3209 }
3210 else
3211 {
3212 m_dynamicData->Move( aDelta );
3213 }
3214
3215 connectivity->ComputeLocalRatsnest( items, m_dynamicData );
3216}
3217
3218
3220{
3221 doHideRatsnestNet( aEvent.Parameter<int>(), true );
3222 return 0;
3223}
3224
3225
3227{
3228 doHideRatsnestNet( aEvent.Parameter<int>(), false );
3229 return 0;
3230}
3231
3232
3233void BOARD_INSPECTION_TOOL::doHideRatsnestNet( int aNetCode, bool aHide )
3234{
3236 m_toolMgr->GetView()->GetPainter()->GetSettings() );
3237
3238 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3239 SELECTION& selection = selectionTool->GetSelection();
3240
3241 if( aNetCode <= 0 && !selection.Empty() )
3242 {
3243 for( EDA_ITEM* item : selection )
3244 {
3245 if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
3246 {
3247 if( bci->GetNetCode() > 0 )
3248 doHideRatsnestNet( bci->GetNetCode(), aHide );
3249 }
3250 }
3251
3252 return;
3253 }
3254
3255 if( aHide )
3256 rs->GetHiddenNets().insert( aNetCode );
3257 else
3258 rs->GetHiddenNets().erase( aNetCode );
3259
3260 if( !m_frame->GetAppearancePanel()->IsTogglingNetclassRatsnestVisibility() )
3261 {
3262 m_frame->GetCanvas()->RedrawRatsnest();
3263 m_frame->GetCanvas()->Refresh();
3264
3265 m_frame->GetAppearancePanel()->OnNetVisibilityChanged( aNetCode, !aHide );
3266 }
3267}
3268
3269
3271{
3275
3284
3293
3296}
std::function< void(const VECTOR2I &, GENERAL_COLLECTOR &, PCB_SELECTION_TOOL *)> CLIENT_SELECTION_FILTER
Definition actions.h:33
#define EVAL_RULES(constraint, a, b, layer, r)
wxString reportMax(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
wxString reportMin(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
bool isNPTHPad(const BOARD_ITEM &aItem)
wxString reportOpt(PCB_BASE_FRAME *aFrame, DRC_CONSTRAINT &aConstraint)
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:213
static TOOL_ACTION pickerTool
Definition actions.h:249
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
void SetTitle(const wxString &aTitle) override
Set title for the menu.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
NETINFO_ITEM * GetNet() const
Return NETINFO_ITEM object for a given item.
void InspectDRCError(const std::shared_ptr< RC_ITEM > &aDRCItem)
int HighlightNet(const TOOL_EVENT &aEvent)
Highlight net belonging to the item under the cursor.
void calculateSelectionRatsnest(const VECTOR2I &aDelta)
Recalculate dynamic ratsnest for the current selection.
void setTransitions() override
Bind handlers to corresponding TOOL_ACTIONs.
int ShowBoardStatistics(const TOOL_EVENT &aEvent)
Show dialog with board statistics.
wxString InspectDRCErrorMenuText(const std::shared_ptr< RC_ITEM > &aDRCItem)
bool highlightNet(const VECTOR2D &aPosition, bool aUseSelection)
Look for a BOARD_CONNECTED_ITEM in a given spot and if one is found - it enables highlight for its ne...
int HighlightNetChain(const TOOL_EVENT &aEvent)
int DiffFootprint(const TOOL_EVENT &aEvent)
wxString getItemDescription(const BOARD_ITEM *aItem)
int LocalRatsnestTool(const TOOL_EVENT &aEvent)
Show local ratsnest of a component.
bool Init() override
Init() is called once upon a registration of the tool.
int ShowNetInRatsnest(const TOOL_EVENT &aEvent)
Show the ratsnest for a given net.
void reportCompileError(REPORTER *r)
int ClearHighlight(const TOOL_EVENT &aEvent)
Clear all board highlights.
void reportClearance(const BOARD_ITEM *aItemA, const BOARD_ITEM *aItemB)
std::unique_ptr< DRC_ENGINE > makeDRCEngine(bool *aCompileError, bool *aCourtyardError)
int HideNetInRatsnest(const TOOL_EVENT &aEvent)
Hide the ratsnest for a given net.
int InspectConstraints(const TOOL_EVENT &aEvent)
void doHideRatsnestNet(int aNetCode, bool aHide)
BOARD_ITEM * pickItemForInspection(const TOOL_EVENT &aEvent, const wxString &aPrompt, const std::vector< KICAD_T > &aTypes, BOARD_ITEM *aLockedHighlight)
CONNECTIVITY_DATA * m_dynamicData
int showBoardComparison(const wxString &aOtherPath, const wxString &aProjectPath, const wxString &aComparisonLabel)
Diff the board at aOtherPath against the live one and show the dialog.
int ReplaceTerminalPad(const TOOL_EVENT &aEvent)
int ShowFootprintLinks(const TOOL_EVENT &aEvent)
void filterCollectorForInspection(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aPos)
int CompareBoardWithFile(const TOOL_EVENT &aEvent)
Diff the current board against a user-selected .kicad_pcb file.
FOOTPRINT_DIFF_WIDGET * constructDiffPanel(wxPanel *aParentPanel)
int InspectClearance(const TOOL_EVENT &aEvent)
Show the clearance resolution for two selected items.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
std::set< int > m_currentlyHighlighted
int UpdateLocalRatsnest(const TOOL_EVENT &aEvent)
Update ratsnest for selected items.
void reportHeader(const wxString &aTitle, const BOARD_ITEM *a, REPORTER *r)
int CompareBoardWithHistory(const TOOL_EVENT &aEvent)
Diff the current board against the most recent local-history commit.
int HideLocalRatsnest(const TOOL_EVENT &aEvent)
Hide ratsnest for selected items. Called when there are no items selected.
int HighlightItem(const TOOL_EVENT &aEvent)
Perform the appropriate action in response to an Eeschema cross-probe.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:85
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:173
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition board_item.h:445
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:418
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:355
virtual bool IsTented(PCB_LAYER_ID aLayer) const
Checks if the given object is tented (its copper shape is covered by solder mask) on a given side of ...
Definition board_item.h:225
virtual void RunOnChildren(const std::function< void(BOARD_ITEM *)> &aFunction, RECURSE_MODE aMode) const
Invoke a function on all children.
Definition board_item.h:265
virtual bool HasDrilledHole() const
Definition board_item.h:213
virtual bool IsOnCopperLayer() const
Definition board_item.h:190
virtual bool HasHole() const
Definition board_item.h:208
static std::unique_ptr< BOARD > Load(const wxString &aFileName, PCB_IO_MGR::PCB_FILE_T aFormat, PROJECT *aProject, const OPTIONS &aOptions)
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
const wxString & GetFileName() const
Definition board.h:454
PROJECT * GetProject() const
Definition board.h:774
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition board.h:758
int GetCount() const
Return the number of objects in the list.
Definition collector.h:79
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition collector.h:107
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition collector.h:97
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
Dialog to show common board info.
wxPanel * AddBlankPage(const wxString &aTitle)
WX_HTML_REPORT_BOX * AddHTMLPage(const wxString &aTitle)
void SetUserItemID(const KIID &aID)
Dialog to show footprint library and symbol links.
std::function< void(WIDGET_DIFF_CANVAS &, const KIID_PATH &)> SHEET_SWITCHER
bool Show(bool show) override
int ShowModal() override
wxString GetName() const
Definition drc_rule.h:162
int m_DisallowFlags
Definition drc_rule.h:205
ZONE_CONNECTION m_ZoneConnection
Definition drc_rule.h:206
MINOPTMAX< int > m_Value
Definition drc_rule.h:204
bool IsNull() const
Definition drc_rule.h:122
static int MatchDpSuffix(const wxString &aNetName, wxString &aComplementNet, wxString &aBaseDpName)
Check if the given net is a diff pair, returning its polarity and complement if so.
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
DIALOG_DRC * GetDRCDialog()
Definition drc_tool.h:60
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:78
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:304
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition eda_item.h:309
void DisplayDiff(FOOTPRINT *aBoardFootprint, std::shared_ptr< FOOTPRINT > &aLibFootprint)
Set the currently displayed symbol.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
FOOTPRINT * LoadFootprint(const wxString &aNickname, const wxString &aName, bool aKeepUUID)
Load a FOOTPRINT having aName from the library given by aNickname.
bool FootprintNeedsUpdate(const FOOTPRINT *aLibFP, int aCompareFlags=0, REPORTER *aReporter=nullptr)
Return true if a board footprint differs from the library version.
const LIB_ID & GetFPID() const
Definition footprint.h:417
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
A general implementation of a COLLECTORS_GUIDE.
Definition collectors.h:320
void SetIgnoreZoneFills(bool ignore)
Definition collectors.h:470
void SetPreferredLayer(PCB_LAYER_ID aLayer)
Definition collectors.h:387
void SetIgnoreNoNets(bool ignore)
Definition collectors.h:473
Used when the right click button is pressed, or when the select tool is in effect.
Definition collectors.h:203
void Collect(BOARD_ITEM *aItem, const std::vector< KICAD_T > &aScanList, const VECTOR2I &aRefPos, const COLLECTORS_GUIDE &aGuide)
Scan a BOARD_ITEM using this class's Inspector method, which does the collection.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
Diff two already-parsed BOARDs and produce a DOCUMENT_DIFF.
Definition pcb_differ.h:62
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
PCB specific render settings.
Definition pcb_painter.h:94
std::set< int > & GetHiddenNets()
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
bool IsHighlightEnabled() const
Return current highlight setting.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An interface for classes handling user events controlling the view behavior such as zooming,...
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
virtual int GetTopLayer() const
Definition view.cpp:1239
PAINTER * GetPainter() const
Return the painter object used by the view for drawing VIEW_ITEM objects.
Definition view.h:242
Definition kiid.h:46
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library tables.
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Simple local history manager built on libgit2.
std::vector< LOCAL_HISTORY_SNAPSHOT_INFO > GetSnapshots(const wxString &aProjectPath)
Snapshots (commits) for the project, newest first.
wxString TreeFingerprint(const wxString &aProjectPath, const wxString &aHash, const wxString &aExtension)
Fingerprint of all files ending in aExtension recorded by commit aHash (sorted path:blob pairs).
bool ExtractAllFilesAtCommit(const wxString &aProjectPath, const wxString &aHash, const wxString &aDestDir, const std::vector< wxString > &aExtensions={})
Write files recorded at aHash into aDestDir, recreating the project's relative folder structure.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition lset.h:63
T Min() const
Definition minoptmax.h:29
bool HasMax() const
Definition minoptmax.h:38
bool HasMin() const
Definition minoptmax.h:37
T Max() const
Definition minoptmax.h:30
T Opt() const
Definition minoptmax.h:31
bool HasOpt() const
Definition minoptmax.h:39
const wxString GetHumanReadableName() const
Gets the consolidated name of this netclass (which may be an aggregate).
Definition netclass.cpp:346
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:122
const wxString & GetNetname() const
Definition netinfo.h:110
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Return the information about the NETINFO_ITEM in aList to display in the message panel.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
Definition pad.h:61
VECTOR2I GetDrillSize() const
Definition pad.h:320
static TOOL_ACTION highlightItem
static TOOL_ACTION toggleNetHighlight
static TOOL_ACTION setTerminalPad
static TOOL_ACTION highlightNet
static TOOL_ACTION hideNetInRatsnest
static TOOL_ACTION hideLocalRatsnest
static TOOL_ACTION showNetInRatsnest
static TOOL_ACTION highlightNetChain
static TOOL_ACTION toggleLastNetHighlight
static TOOL_ACTION compareBoardWithHistory
static TOOL_ACTION inspectConstraints
static TOOL_ACTION clearHighlight
static TOOL_ACTION inspectClearance
static TOOL_ACTION updateLocalRatsnest
static TOOL_ACTION compareBoardWithFile
static TOOL_ACTION diffFootprint
static TOOL_ACTION showFootprintAssociations
static TOOL_ACTION highlightNetSelection
static TOOL_ACTION boardStatistics
static TOOL_ACTION localRatsnestTool
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
PCB_FILE_T
The set of file types that the PCB_IO_MGR knows about, and for which there has been a plugin written,...
Definition pcb_io_mgr.h:52
static PCB_FILE_T FindPluginTypeFromBoardPath(const wxString &aFileName, int aCtl=0)
Return a plugin type given a path for a board file.
Generic tool for picking an item.
void ClearHandlers()
Handlers only.
The selection tool: currently supports:
void GuessSelectionCandidates(GENERAL_COLLECTOR &aCollector, const VECTOR2I &aWhere) const
Try to guess best selection candidates in case multiple items are clicked, by doing some brain-dead h...
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter)
Return the current selection, filtered according to aClientFilter.
bool Selectable(const BOARD_ITEM *aItem, bool checkVisibilityOnly=false) const
PCB_SELECTION_FILTER_OPTIONS & GetFilter()
PCB_SELECTION & GetSelection()
void FilterCollectedItems(GENERAL_COLLECTOR &aCollector, bool aMultiSelect, PCB_SELECTION_FILTER_OPTIONS *aRejected=nullptr)
Apply the SELECTION_FITLER_OPTIONS to the collector.
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
BOARD * board() const
PCBNEW_SETTINGS::DISPLAY_OPTIONS & displayOptions() const
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition picker_tool.h:81
void SetSnapping(bool aSnap)
Definition picker_tool.h:65
void SetCursor(KICURSOR aCursor)
Definition picker_tool.h:63
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
void BrightenItem(EDA_ITEM *aItem)
void UnbrightenItem(EDA_ITEM *aItem)
bool LoadProject(const wxString &aFullPath, bool aSetActive=true)
Load a project or sets up a new project with a specified path.
bool IsProjectLoaded(PROJECT *aProject) const
True if aProject is still owned by the manager.
PROJECT * GetProject(const wxString &aFullPath) const
Retrieve a loaded project by name.
bool UnloadProject(PROJECT *aProject, bool aSave=true)
Save, unload and unregister the given PROJECT.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
Extension of STATUS_POPUP for displaying a single line text.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
TOOL_MANAGER * m_toolMgr
Definition tool_base.h:220
KIGFX::VIEW * getView() const
Return the instance of KIGFX::VIEW object used in the application.
Definition tool_base.cpp:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
Generic, UI-independent tool event.
Definition tool_event.h:167
bool DisableGridSnapping() const
Definition tool_event.h:367
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:484
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
CONDITIONAL_MENU & GetMenu()
Definition tool_menu.cpp:40
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition tool_menu.cpp:46
wxString StringFromValue(double aValue, bool aAddUnitLabel=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
Converts aValue in internal units into a united string.
GAL-backed canvas for visualizing a KICAD_DIFF::DIFF_SCENE.
A slimmed down version of WX_HTML_REPORT_PANEL.
void Clear() override
Delete the stored messages.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
void SetUnits(EDA_UNITS aUnits)
void Flush()
Build the HTML messages page.
Handle a list of polygons defining a copper zone.
Definition zone.h:70
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1097
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:806
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
@ BULLSEYE
Definition cursors.h:55
@ ANNULAR_WIDTH_CONSTRAINT
@ COURTYARD_CLEARANCE_CONSTRAINT
@ VIA_DIAMETER_CONSTRAINT
@ DIFF_PAIR_GAP_CONSTRAINT
@ DISALLOW_CONSTRAINT
@ TRACK_WIDTH_CONSTRAINT
@ SILK_CLEARANCE_CONSTRAINT
@ EDGE_CLEARANCE_CONSTRAINT
@ MIN_RESOLVED_SPOKES_CONSTRAINT
@ TRACK_SEGMENT_LENGTH_CONSTRAINT
@ TEXT_THICKNESS_CONSTRAINT
@ PHYSICAL_HOLE_CLEARANCE_CONSTRAINT
@ CLEARANCE_CONSTRAINT
@ THERMAL_SPOKE_WIDTH_CONSTRAINT
@ CONNECTION_WIDTH_CONSTRAINT
@ THERMAL_RELIEF_GAP_CONSTRAINT
@ MAX_UNCOUPLED_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
@ SOLDER_PASTE_ABS_MARGIN_CONSTRAINT
@ SOLDER_MASK_EXPANSION_CONSTRAINT
@ TRACK_ANGLE_CONSTRAINT
@ HOLE_SIZE_CONSTRAINT
@ TEXT_HEIGHT_CONSTRAINT
@ PHYSICAL_CLEARANCE_CONSTRAINT
@ SOLDER_PASTE_REL_MARGIN_CONSTRAINT
@ HOLE_TO_HOLE_CONSTRAINT
@ DRCE_DP_UNCOUPLED_LENGTH_TOO_LONG
Definition drc_item.h:125
@ DRCE_HOLE_CLEARANCE
Definition drc_item.h:63
@ DRCE_VIA_DIAMETER
Definition drc_item.h:70
@ DRCE_TRACK_WIDTH
Definition drc_item.h:64
@ DRCE_DRILL_OUT_OF_RANGE
Definition drc_item.h:69
@ DRCE_EDGE_CLEARANCE
Definition drc_item.h:55
@ DRCE_STARVED_THERMAL
Definition drc_item.h:58
@ DRCE_TRACK_SEGMENT_LENGTH
Definition drc_item.h:66
@ DRCE_TRACK_ANGLE
Definition drc_item.h:65
@ DRCE_CLEARANCE
Definition drc_item.h:52
@ DRCE_DRILLED_HOLES_TOO_CLOSE
Definition drc_item.h:61
@ DRCE_MICROVIA_DRILL_OUT_OF_RANGE
Definition drc_item.h:73
@ DRCE_TEXT_HEIGHT
Definition drc_item.h:115
@ DRCE_ASSERTION_FAILURE
Definition drc_item.h:103
@ DRCE_LIB_FOOTPRINT_MISMATCH
Definition drc_item.h:97
@ DRCE_TEXT_THICKNESS
Definition drc_item.h:116
@ DRCE_CONNECTION_WIDTH
Definition drc_item.h:68
@ DRCE_ANNULAR_WIDTH
Definition drc_item.h:67
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define MALFORMED_COURTYARDS
EDA_UNITS
Definition eda_units.h:44
static const std::string ProjectFileExtension
static wxString PcbFileWildcard()
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:844
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:741
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ Margin
Definition layer_ids.h:109
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
void ConfigurePcbDiffCanvasContext(WIDGET_DIFF_CANVAS &aCanvas, BOARD *aReference, BOARD *aComparison, const KIGFX::COLOR4D &aColor, const std::map< KIID, KIGFX::COLOR4D > &aOverrides, const std::vector< KIGFX::VIEW_ITEM * > &aExtraItems, const std::map< KIID, KICAD_DIFF::CATEGORY > &aCategories)
BOARD_ITEM * FindPcbDiffItem(const BOARD *aBoard, const KIID &aId)
Resolve an actual UUID or a footprint field's diff ID on a board.
std::vector< KIGFX::VIEW_ITEM * > CollectFootprintDiffContextItems(FOOTPRINT &aFootprint)
CATEGORY CategoryFor(CHANGE_KIND aKind)
Map a CHANGE_KIND to the visual category it belongs to.
@ COLOR
Color has changed.
Definition view_item.h:50
@ TARGET_OVERLAY
Items that may change while the view stays the same (noncached)
Definition definitions.h:35
void ReparentWindow(wxNonOwnedWindow *aWindow, wxTopLevelWindow *aParent)
Definition wxgtk/ui.cpp:341
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:958
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
Class to handle a set of BOARD_ITEMs.
SEVERITY
@ RPT_SEVERITY_UNDEFINED
std::vector< FAB_LAYER_COLOR > dummy
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
Diff colors with defaults for light backgrounds.
Definition diff_scene.h:278
The full set of changes between two parsed documents of one type.
std::vector< ITEM_CHANGE > changes
Aggregate of background geometry extracted from one source document.
Definition diff_scene.h:163
One change record on a single item.
std::vector< ITEM_CHANGE > children
A filename or source description, a problem input line, a line number, a byte offset,...
This file contains data structures that are saved in the project file or project local settings file ...
int clearance
wxString result
Test unit parsing edge cases and error handling.
int delta
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition typeinfo.h:83
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
Definition of file extensions used in Kicad.
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ NONE
Pads are not covered.
Definition zones.h:45