KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_net_inspector.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Oleg Endo <[email protected]>
5 * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
6 * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <string_utils.h>
29#include <confirm.h>
30#include <pcb_track.h>
32#include <eda_pattern_match.h>
34#include <view/view.h>
35#include <view/view_controls.h>
36#include <pcb_painter.h>
37#include <kiplatform/ui.h>
40#include <validators.h>
41#include <bitmaps.h>
43#include "pcbnew_settings.h"
45#include <pgm_base.h>
46
47#include <wx/tokenzr.h>
48#include <wx/filedlg.h>
49#include <wx/dcclient.h>
50#include <wx/wupdlock.h>
51
52#include <bitset>
53#include <vector>
54
55
56wxDEFINE_EVENT( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxCommandEvent );
57
58
59static wxString g_filter_string;
60
61
62enum class CSV_COLUMN_DESC : int
63{
64 CSV_NONE = 0,
65 CSV_QUOTE = 1 << 0
66};
67
68
70{
71 COLUMN_DESC( unsigned aNum, PCB_LAYER_ID aLayer, const wxString& aDisp, const wxString& aCsv,
72 CSV_COLUMN_DESC aFlags, bool aHasUnits ) :
73 num( aNum ),
74 layer( aLayer ),
75 display_name( aDisp ),
76 csv_name( aCsv ),
77 csv_flags( aFlags ),
78 has_units( aHasUnits )
79 {}
80
81 unsigned int num;
83 wxString display_name;
84 wxString csv_name;
87
88 operator unsigned int() const
89 {
90 return num;
91 }
92};
93
94enum
95{
106
107
109{
110public:
111 LIST_ITEM( unsigned int aGroupNumber, const wxString& aGroupName ) :
112 m_is_group( true ),
113 m_group_number( aGroupNumber ),
114 m_net_name( aGroupName )
115 {
117 }
118
120 m_net( aNet )
121 {
122 wxASSERT( aNet );
125 }
126
128 {
130 }
131
132 LIST_ITEM& operator=( const LIST_ITEM& ) = delete;
133
134 bool GetIsGroup() const { return m_is_group; }
135
136 auto ChildrenBegin() const { return m_children.begin(); }
137 auto ChildrenEnd() const { return m_children.end(); }
138 unsigned int ChildrenCount() const { return m_children.size(); }
139
140 NETINFO_ITEM* GetNet() const { return m_net; }
141
142 int GetNetCode() const
143 {
144 return GetIsGroup() ? ( 0 - int( m_group_number ) - 1 ) : m_net->GetNetCode();
145 }
146
147 const wxString& GetNetName() const { return m_net_name; }
148 const wxString& GetGroupName() const { return m_net_name; }
149
151 {
152 std::fill( m_column_changed.begin(), m_column_changed.end(), 0 );
153 }
154
155 unsigned int GetPadCount() const
156 {
157 return m_pad_count;
158 }
159
160 bool PadCountChanged() const
161 {
163 }
164
165 void SetPadCount( unsigned int aValue )
166 {
167 if( m_parent )
169
171 m_pad_count = aValue;
172 }
173
174 void AddPadCount( unsigned int aValue )
175 {
176 if( m_parent )
177 m_parent->AddPadCount( aValue );
178
179 m_column_changed[COLUMN_PAD_COUNT] |= ( aValue != 0 );
180 m_pad_count += aValue;
181 }
182
183 void SubPadCount( unsigned int aValue )
184 {
185 if( m_parent )
186 m_parent->SubPadCount( aValue );
187
188 m_column_changed[COLUMN_PAD_COUNT] |= ( aValue != 0 );
189 m_pad_count -= aValue;
190 }
191
192 unsigned GetViaCount() const
193 {
194 return m_via_count;
195 }
196
197 bool ViaCountChanged() const
198 {
200 }
201
202 void SetViaCount( unsigned int aValue )
203 {
204 if( m_parent )
206
208 m_via_count = aValue;
209 }
210
211 void AddViaCount( unsigned int aValue )
212 {
213 if( m_parent )
214 m_parent->AddViaCount( aValue );
215
216 m_column_changed[COLUMN_VIA_COUNT] |= ( aValue != 0 );
217 m_via_count += aValue;
218 }
219
220 void SubViaCount( unsigned int aValue )
221 {
222 if( m_parent )
223 m_parent->SubViaCount( aValue );
224
225 m_column_changed[COLUMN_VIA_COUNT] |= ( aValue != 0 );
226 m_via_count -= aValue;
227 }
228
229 uint64_t GetViaLength() const
230 {
231 return m_via_length;
232 }
233
234 bool ViaLengthChanged() const
235 {
237 }
238
239 void SetViaLength( unsigned int aValue )
240 {
241 if( m_parent )
243
245 m_via_length = aValue;
246 }
247
248 void AddViaLength( unsigned int aValue )
249 {
250 if( m_parent )
251 m_parent->AddViaLength( aValue );
252
253 m_column_changed[COLUMN_VIA_LENGTH] |= ( aValue != 0 );
254 m_via_length += aValue;
255 }
256
257 void SubViaLength( uint64_t aValue )
258 {
259 if( m_parent )
260 m_parent->SubViaLength( aValue );
261
262 m_column_changed[COLUMN_VIA_LENGTH] |= ( aValue != 0 );
263 m_via_length -= aValue;
264 }
265
266 uint64_t GetBoardWireLength() const
267 {
268 uint64_t retval = 0;
269
270 for( uint64_t val : m_layer_wire_length )
271 retval += val;
272
273 return retval;
274 }
275
276 uint64_t GetLayerWireLength( size_t aLayer ) const
277 {
278 wxCHECK_MSG( aLayer < m_layer_wire_length.size(), 0, wxT( "Invalid layer specified" ) );
279
280 return m_layer_wire_length[aLayer];
281 }
282
284 {
286 }
287
288 void SetLayerWireLength( const uint64_t aValue, size_t aLayer )
289 {
290 wxCHECK_RET( aLayer < m_layer_wire_length.size(), wxT( "Invalid layer specified" ) );
291
292 if( m_parent )
294 m_parent->GetBoardWireLength() - m_layer_wire_length[aLayer] + aValue, aLayer );
295
297 m_layer_wire_length[aLayer] = aValue;
298 }
299
300 void AddLayerWireLength( const uint64_t aValue, size_t aLayer )
301 {
302 if( m_parent )
303 m_parent->AddLayerWireLength( aValue, aLayer );
304
306 m_layer_wire_length[aLayer] += aValue;
307 }
308
309 void SubLayerWireLength( const uint64_t aValue, size_t aLayer )
310 {
311 if( m_parent )
312 m_parent->SubLayerWireLength( aValue, aLayer );
313
315 m_layer_wire_length[aLayer] -= aValue;
316 }
317
318 uint64_t GetChipWireLength() const
319 {
320 return m_chip_wire_length;
321 }
322
324 {
326 }
327
328 void SetChipWireLength( uint64_t aValue )
329 {
330 if( m_parent )
332
334 m_chip_wire_length = aValue;
335 }
336
337 void AddChipWireLength( uint64_t aValue )
338 {
339 if( m_parent )
340 m_parent->AddChipWireLength( aValue );
341
342 m_column_changed[COLUMN_CHIP_LENGTH] |= ( aValue != 0 );
343 m_chip_wire_length += aValue;
344 }
345
346 void SubChipWireLength( uint64_t aValue )
347 {
348 if( m_parent )
349 m_parent->SubChipWireLength( aValue );
350
351 m_column_changed[COLUMN_CHIP_LENGTH] |= ( aValue != 0 );
352 m_chip_wire_length -= aValue;
353 }
354
355 // the total length column is always computed, never stored.
356 unsigned long long int GetTotalLength() const
357 {
359 }
360
362 {
364 }
365
367 {
368 return m_parent;
369 }
370
371 void SetParent( LIST_ITEM* aParent )
372 {
373 if( m_parent == aParent )
374 return;
375
376 if( m_parent != nullptr )
377 {
381
382 for( size_t ii = 0; ii < m_layer_wire_length.size(); ++ii )
384
386
387 m_parent->m_children.erase( std::find( m_parent->m_children.begin(),
388 m_parent->m_children.end(), this ) );
389 }
390
391 m_parent = aParent;
392
393 if( m_parent != nullptr )
394 {
398
399 for( size_t ii = 0; ii < m_layer_wire_length.size(); ++ii )
401
403
404 m_parent->m_children.push_back( this );
405 }
406 }
407
408private:
409 // an item can be the child of only one parent at a time.
410 // FIXME: could use a more lightweight container like intrusive forward list.
411 LIST_ITEM* m_parent = nullptr;
412 std::vector<LIST_ITEM*> m_children;
413
414 bool m_is_group = false;
415 unsigned int m_group_number = 0;
416 NETINFO_ITEM* m_net = nullptr;
417 unsigned int m_pad_count = 0;
418 unsigned int m_via_count = 0;
419 uint64_t m_via_length = 0;
420 uint64_t m_chip_wire_length = 0;
421
422 std::array<uint64_t, MAX_CU_LAYERS> m_layer_wire_length{};
423
424 // Dirty bits to record when some attribute has changed, in order to avoid unnecessary sort
425 // operations.
426 // The values are semantically bools, but STL auto-promotes a std::vector<bool> to a bitset,
427 // and then operator|= doesn't work.
428 std::vector<int> m_column_changed;
429
430 // cached formatted net name for faster display sorting.
431 wxString m_net_name;
432};
433
434
436{
437 template <typename T>
438 bool operator()( const T& a, const T& b ) const
439 {
440 return a->GetNetCode() < b->GetNetCode();
441 }
442
443 template <typename T>
444 bool operator()( const T& a, int b ) const
445 {
446 return a->GetNetCode() < b;
447 }
448
449 template <typename T>
450 bool operator()( int a, const T& b ) const
451 {
452 return a < b->GetNetCode();
453 }
454};
455
456
457class DIALOG_NET_INSPECTOR::DATA_MODEL : public wxDataViewModel
458{
459public:
460
462 {
463 }
464
465 unsigned int columnCount() const
466 {
467 return m_parent.m_columns.size();
468 }
469
470 unsigned int itemCount() const
471 {
472 return m_items.size();
473 }
474
475 wxVariant valueAt( unsigned int aCol, unsigned int aRow ) const
476 {
477 wxVariant r;
478 GetValue( r, wxDataViewItem( const_cast<LIST_ITEM*>( &*( m_items[aRow] ) ) ), aCol );
479 return r;
480 }
481
482 const LIST_ITEM& itemAt( unsigned int aRow ) const
483 {
484 return *m_items.at( aRow );
485 }
486
487 std::optional<LIST_ITEM_ITER> findItem( int aNetCode )
488 {
489 auto i = std::lower_bound(
490 m_items.begin(), m_items.end(), aNetCode, LIST_ITEM_NETCODE_CMP_LESS() );
491
492 if( i == m_items.end() || ( *i )->GetNetCode() != aNetCode )
493 return std::nullopt;
494
495 return { i };
496 }
497
498 std::optional<LIST_ITEM_ITER> findItem( NETINFO_ITEM* aNet )
499 {
500 if( aNet != nullptr )
501 return findItem( aNet->GetNetCode() );
502 else
503 return std::nullopt;
504 }
505
506 std::optional<LIST_ITEM_ITER> addItem( std::unique_ptr<LIST_ITEM> aItem )
507 {
508 if( aItem == nullptr )
509 return {};
510
511 // make sure that the vector is always sorted. usually when new nets are added,
512 // they always get a higher netcode number than the already existing ones.
513 // however, if we've got filtering enabled, we might not have all the nets in
514 // our list, so do a sorted insertion.
515
516 auto new_iter = std::lower_bound( m_items.begin(), m_items.end(), aItem->GetNetCode(),
518
519 new_iter = m_items.insert( new_iter, std::move( aItem ) );
520 const std::unique_ptr<LIST_ITEM>& new_item = *new_iter;
521
522 if( m_parent.m_groupBy->IsChecked()
523 && ( m_parent.m_groupByKind->GetSelection() == 0
524 || m_parent.m_groupByKind->GetSelection() == 1 ) )
525 {
526 for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j )
527 {
528 if( m_parent.m_groupFilter[j]->Find( new_item->GetNetName() ) )
529 {
530 new_item->SetParent( &*m_items[j] );
531 break;
532 }
533 }
534 }
535 else if( m_parent.m_groupBy->IsChecked()
536 && ( m_parent.m_groupByKind->GetSelection() == 2
537 || m_parent.m_groupByKind->GetSelection() == 3 ) )
538 {
539 auto groups_begin = m_items.begin();
540 auto groups_end = std::find_if_not( m_items.begin(), m_items.end(),
541 []( const std::unique_ptr<LIST_ITEM>& x )
542 {
543 return x->GetIsGroup();
544 } );
545
546 for( std::unique_ptr<EDA_PATTERN_MATCH>& f : m_parent.m_groupFilter )
547 {
548 EDA_PATTERN_MATCH::FIND_RESULT match = f->Find( new_item->GetNetName() );
549
550 if( match )
551 {
552 wxString match_str = new_item->GetNetName().substr( match.start, match.length );
553
554 auto group = std::find_if( groups_begin, groups_end,
555 [&]( const std::unique_ptr<LIST_ITEM>& x )
556 {
557 return x->GetNetName() == match_str;
558 } );
559
560 if( group == groups_end )
561 {
562 int dist = std::distance( groups_end, groups_begin );
563 group = m_items.insert( groups_end,
564 std::make_unique<LIST_ITEM>( dist, match_str ) );
565
566 groups_end = group + 1;
567
568 ItemAdded( wxDataViewItem(( *group )->Parent() ),
569 wxDataViewItem( &**group ) );
570 }
571
572 new_item->SetParent( &**group );
573 break;
574 }
575 }
576 }
577
578 ItemAdded( wxDataViewItem( new_item->Parent() ), wxDataViewItem( new_item.get() ) );
579
580 return { new_iter };
581 }
582
583 void addItems( std::vector<std::unique_ptr<LIST_ITEM>> aItems )
584 {
585 if( m_items.empty() )
586 {
587 m_items = std::move( aItems );
588
589 if( m_parent.m_groupBy->IsChecked()
590 && ( m_parent.m_groupByKind->GetSelection() == 0
591 || m_parent.m_groupByKind->GetSelection() == 1 ) )
592 {
593 // assume that there are fewer group filters than nets.
594 // walk over the list items and assign them to groups. note that the
595 // first items are group items themselves, so start after those.
596 for( unsigned int i = m_parent.m_groupFilter.size(); i < m_items.size(); ++i )
597 {
598 for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j )
599 {
600 if( m_parent.m_groupFilter[j]->Find( m_items[ i ]->GetNetName() ) )
601 {
602 m_items[i]->SetParent( &*m_items[j] );
603 break;
604 }
605 }
606 }
607 }
608 else if( m_parent.m_groupBy->IsChecked()
609 && ( m_parent.m_groupByKind->GetSelection() == 2
610 || m_parent.m_groupByKind->GetSelection() == 3 ) )
611 {
612 // assume that there will be fewer resulting groups than nets.
613 // dynamically generate groups for the actual strings of the match result.
614 // try out each filter on each item and group by the resulting substrings.
615 std::vector<std::unique_ptr<LIST_ITEM>> groups;
616
617 for( std::unique_ptr<LIST_ITEM>& i : m_items )
618 {
619 for( std::unique_ptr<EDA_PATTERN_MATCH>& f : m_parent.m_groupFilter )
620 {
621 EDA_PATTERN_MATCH::FIND_RESULT match = f->Find( i->GetNetName() );
622
623 if( match )
624 {
625 wxString match_str = i->GetNetName().substr( match.start,
626 match.length );
627
628 auto group = std::find_if( groups.begin(), groups.end(),
629 [&]( const std::unique_ptr<LIST_ITEM>& x )
630 {
631 return x->GetNetName() == match_str;
632 } );
633
634 if( group == groups.end() )
635 {
636 groups.emplace_back( std::make_unique<LIST_ITEM>( groups.size(),
637 match_str ) );
638 group = groups.end() - 1;
639 }
640
641 i->SetParent( &**group );
642 break;
643 }
644 }
645 }
646
647 // insert the group items at the front of the items list.
648 for( std::unique_ptr<LIST_ITEM>& g : groups )
649 m_items.emplace_back( std::move( g ) );
650
651 std::rotate( m_items.begin(), m_items.end() - groups.size(), m_items.end() );
652 }
653
654 for( std::unique_ptr<LIST_ITEM>& i : m_items )
655 ItemAdded( wxDataViewItem( i->Parent() ), wxDataViewItem( &*i ) );
656 }
657 else
658 {
659 m_items.reserve( m_items.size() + aItems.size() );
660
661 for( std::unique_ptr<LIST_ITEM>& i : aItems )
662 addItem( std::move( i ) );
663 }
664 }
665
666 std::unique_ptr<LIST_ITEM> deleteItem( const std::optional<LIST_ITEM_ITER>& aRow )
667 {
668 if( !aRow )
669 return {};
670
671 std::unique_ptr<LIST_ITEM> i = std::move( **aRow );
672
673 // if the row has a parent, detach it first
674 LIST_ITEM* parent = i->Parent();
675 i->SetParent( nullptr );
676
677 m_items.erase( *aRow );
678 ItemDeleted( wxDataViewItem( parent ), wxDataViewItem( &*i ) );
679
680 if( parent )
681 {
682 ItemChanged( wxDataViewItem( parent ) );
683
684 // for grouping type 2,3 a group item might disappear if it becomes empty.
685 if( ( m_parent.m_groupByKind->GetSelection() == 2
686 || m_parent.m_groupByKind->GetSelection() == 3 )
687 && parent != nullptr && parent->ChildrenCount() == 0 )
688 {
689 auto p = std::find_if( m_items.begin(), m_items.end(),
690 [&]( std::unique_ptr<LIST_ITEM>& x )
691 {
692 return x.get() == parent;
693 } );
694
695 wxASSERT( p != m_items.end() );
696 m_items.erase( p );
697
698 ItemDeleted( wxDataViewItem( parent->Parent() ), wxDataViewItem( parent ) );
699 }
700 }
701
702 Resort();
703 return i;
704 }
705
707 {
708 BeforeReset();
709 m_items.clear();
710 AfterReset();
711 }
712
713 void updateItem( const std::optional<LIST_ITEM_ITER>& aRow )
714 {
715 if( aRow )
716 {
717 const std::unique_ptr<LIST_ITEM>& listItem = *aRow.value();
718
719 if( listItem->Parent() )
720 ItemChanged( wxDataViewItem( listItem->Parent() ) );
721
722 ItemChanged( wxDataViewItem( listItem.get() ) );
723 resortIfChanged( listItem.get() );
724 }
725 }
726
728 {
729 for( std::unique_ptr<LIST_ITEM>& i : m_items )
730 ItemChanged( wxDataViewItem( i.get() ) );
731 }
732
734 {
735 if( wxDataViewColumn* column = m_parent.m_netsList->GetSortingColumn() )
736 {
737 bool changed = false;
738
739 for( const LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
740 changed |= itemColumnChanged( i, column->GetModelColumn() );
741
742 for( LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
743 i->ResetColumnChangedBits();
744
745 if( changed )
746 Resort();
747 }
748 }
749
750 bool itemColumnChanged( const LIST_ITEM* aItem, unsigned int aCol ) const
751 {
752 if( aItem == nullptr || aCol >= m_parent.m_columns.size() )
753 return false;
754
755 if( aCol == COLUMN_PAD_COUNT )
756 return aItem->PadCountChanged();
757
758 else if( aCol == COLUMN_VIA_COUNT )
759 return aItem->ViaCountChanged();
760
761 else if( aCol == COLUMN_VIA_LENGTH )
762 return aItem->ViaLengthChanged();
763
764 else if( aCol == COLUMN_BOARD_LENGTH )
765 return aItem->BoardWireLengthChanged();
766
767 else if( aCol == COLUMN_CHIP_LENGTH )
768 return aItem->ChipWireLengthChanged();
769
770 else if( aCol == COLUMN_TOTAL_LENGTH )
771 return aItem->TotalLengthChanged();
772
773 else if( aCol > COLUMN_NUM_STATIC_COL )
774 return aItem->BoardWireLengthChanged();
775
776
777 return false;
778 }
779
780 // implementation of wxDataViewModel interface
781 // these are used to query the data model by the GUI view implementation.
782 // these are not supposed to be used to modify the data model. for that
783 // use the public functions above.
784
785protected:
786 unsigned int GetColumnCount() const override
787 {
788 return columnCount();
789 }
790
791 void GetValue( wxVariant& aOutValue, const wxDataViewItem& aItem,
792 unsigned int aCol ) const override
793 {
794 if( LIST_ITEM* i = static_cast<LIST_ITEM*>( aItem.GetID() ) )
795 {
796 if( aCol == COLUMN_NET && !i->GetIsGroup() )
797 aOutValue = m_parent.formatNetCode( i->GetNet() );
798
799 else if( aCol == COLUMN_NET && i->GetIsGroup() )
800 aOutValue = "";
801
802 else if( aCol == COLUMN_NAME )
803 aOutValue = i->GetNetName();
804
805 else if( aCol == COLUMN_PAD_COUNT )
806 aOutValue = m_parent.formatCount( i->GetPadCount() );
807
808 else if( aCol == COLUMN_VIA_COUNT )
809 aOutValue = m_parent.formatCount( i->GetViaCount() );
810
811 else if( aCol == COLUMN_VIA_LENGTH )
812 aOutValue = m_parent.formatLength( i->GetViaLength() );
813
814 else if( aCol == COLUMN_BOARD_LENGTH )
815 aOutValue = m_parent.formatLength( i->GetBoardWireLength() );
816
817 else if( aCol == COLUMN_CHIP_LENGTH )
818 aOutValue = m_parent.formatLength( i->GetChipWireLength() );
819
820 else if( aCol == COLUMN_TOTAL_LENGTH )
821 aOutValue = m_parent.formatLength( i->GetTotalLength() );
822
823 else if( aCol > COLUMN_NUM_STATIC_COL && aCol <= m_parent.m_columns.size() )
824 aOutValue = m_parent.formatLength( i->GetLayerWireLength( m_parent.m_columns[aCol].layer ) );
825
826 else
827 aOutValue = "";
828 }
829 }
830
831 static int compareUInt( uint64_t aValue1, uint64_t aValue2, bool aAsc )
832 {
833 if( aAsc )
834 return aValue1 < aValue2 ? -1 : 1;
835 else
836 return aValue2 < aValue1 ? -1 : 1;
837 }
838
839 int Compare( const wxDataViewItem& aItem1, const wxDataViewItem& aItem2,
840 unsigned int aCol, bool aAsc ) const override
841 {
842 const LIST_ITEM& i1 = *static_cast<const LIST_ITEM*>( aItem1.GetID() );
843 const LIST_ITEM& i2 = *static_cast<const LIST_ITEM*>( aItem2.GetID() );
844
845 if( i1.GetIsGroup() && !i2.GetIsGroup() )
846 return -1;
847
848 if( i2.GetIsGroup() && !i1.GetIsGroup() )
849 return 1;
850
851 if( aCol == COLUMN_NET && i1.GetNetCode() != i2.GetNetCode() )
852 {
853 return aAsc ? ( i1.GetNetCode() - i2.GetNetCode() )
854 : ( i2.GetNetCode() - i1.GetNetCode() );
855 }
856 else if( aCol == COLUMN_NAME )
857 {
858 const wxString& s1 = i1.GetNetName();
859 const wxString& s2 = i2.GetNetName();
860
861 int res = aAsc ? ValueStringCompare( s1, s2 ) : ValueStringCompare( s2, s1 );
862
863 if( res != 0 )
864 return res;
865 }
866
867 else if( aCol == COLUMN_PAD_COUNT && i1.GetPadCount() != i2.GetPadCount() )
868 return compareUInt( i1.GetPadCount(), i2.GetPadCount(), aAsc );
869
870 else if( aCol == COLUMN_VIA_COUNT && i1.GetViaCount() != i2.GetViaCount() )
871 return compareUInt( i1.GetViaCount(), i2.GetViaCount(), aAsc );
872
873 else if( aCol == COLUMN_VIA_LENGTH && i1.GetViaLength() != i2.GetViaLength() )
874 return compareUInt( i1.GetViaLength(), i2.GetViaLength(), aAsc );
875
876 else if( aCol == COLUMN_BOARD_LENGTH && i1.GetBoardWireLength() != i2.GetBoardWireLength() )
877 return compareUInt( i1.GetBoardWireLength(), i2.GetBoardWireLength(), aAsc );
878
879 else if( aCol == COLUMN_CHIP_LENGTH && i1.GetChipWireLength() != i2.GetChipWireLength() )
880 return compareUInt( i1.GetChipWireLength(), i2.GetChipWireLength(), aAsc );
881
882 else if( aCol == COLUMN_TOTAL_LENGTH && i1.GetTotalLength() != i2.GetTotalLength() )
883 return compareUInt( i1.GetTotalLength(), i2.GetTotalLength(), aAsc );
884
885 else if( aCol > COLUMN_NUM_STATIC_COL
886 && aCol < m_parent.m_columns.size()
887 && i1.GetLayerWireLength( m_parent.m_columns[aCol].layer )
888 != i2.GetLayerWireLength( m_parent.m_columns[aCol].layer ) )
889 {
890 return compareUInt( i1.GetLayerWireLength( m_parent.m_columns[aCol].layer ),
891 i2.GetLayerWireLength( m_parent.m_columns[aCol].layer ), aAsc );
892 }
893
894 // when the item values compare equal resort to pointer comparison.
895 wxUIntPtr id1 = wxPtrToUInt( aItem1.GetID() );
896 wxUIntPtr id2 = wxPtrToUInt( aItem2.GetID() );
897
898 return aAsc ? id1 - id2 : id2 - id1;
899 }
900
901 bool SetValue( const wxVariant& aInValue, const wxDataViewItem& aItem,
902 unsigned int aCol ) override
903 {
904 return false;
905 }
906
907 wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override
908 {
909 if( !aItem.IsOk() )
910 return wxDataViewItem();
911
912 return wxDataViewItem( static_cast<const LIST_ITEM*>( aItem.GetID() )->Parent() );
913 }
914
915 bool IsContainer( const wxDataViewItem& aItem ) const override
916 {
917 if( !aItem.IsOk() )
918 return true;
919
920 return static_cast<const LIST_ITEM*>( aItem.GetID() )->GetIsGroup();
921 }
922
923 bool HasContainerColumns( const wxDataViewItem& aItem ) const override
924 {
925 return IsContainer( aItem );
926 }
927
928 unsigned int GetChildren( const wxDataViewItem& aParent,
929 wxDataViewItemArray& aChildren ) const override
930 {
931 const LIST_ITEM* p = static_cast<const LIST_ITEM*>( aParent.GetID() );
932
933 if( !aParent.IsOk() )
934 {
935 aChildren.Alloc( m_items.size() );
936
937 for( const std::unique_ptr<LIST_ITEM>& i : m_items )
938 {
939 if( i->Parent() == nullptr )
940 aChildren.Add( wxDataViewItem( &*i ) );
941 }
942
943 return aChildren.GetCount();
944 }
945 else if( p->GetIsGroup() )
946 {
947 const int count = p->ChildrenCount();
948
949 if( count == 0 )
950 return 0;
951
952 aChildren.Alloc( count );
953
954 for( auto i = p->ChildrenBegin(), end = p->ChildrenEnd(); i != end; ++i )
955 aChildren.Add( wxDataViewItem( *i ) );
956
957 return aChildren.GetCount();
958 }
959
960 return 0;
961 }
962
963 wxString GetColumnType( unsigned int /* aCol */ ) const override
964 {
965 return wxS( "string" );
966 }
967
968private:
970
971 // primary container, sorted by netcode number.
972 // groups have netcode < 0, so they always come first, in the order
973 // of the filter strings as input by the user (group mode 0, 1) or
974 // in order of occurrence (group mode 2, 3).
975 std::vector<std::unique_ptr<LIST_ITEM>> m_items;
976};
977
978
980 DIALOG_NET_INSPECTOR_BASE( aParent ),
981 m_zero_netitem( nullptr ),
982 m_frame( aParent )
983{
984 m_columns.emplace_back( 0u, UNDEFINED_LAYER, _( "Net" ), _( "Net Code" ),
985 CSV_COLUMN_DESC::CSV_NONE, false );
986 m_columns.emplace_back( 1u, UNDEFINED_LAYER, _( "Name" ), _( "Net Name" ),
987 CSV_COLUMN_DESC::CSV_QUOTE, false );
988 m_columns.emplace_back( 2u, UNDEFINED_LAYER, _( "Pad Count" ), _( "Pad Count" ),
989 CSV_COLUMN_DESC::CSV_NONE, false );
990 m_columns.emplace_back( 3u, UNDEFINED_LAYER, _( "Via Count" ), _( "Via Count" ),
991 CSV_COLUMN_DESC::CSV_NONE, false );
992 m_columns.emplace_back( 4u, UNDEFINED_LAYER, _( "Via Length" ), _( "Via Length" ),
993 CSV_COLUMN_DESC::CSV_NONE, true );
994 m_columns.emplace_back( 5u, UNDEFINED_LAYER, _( "Track Length" ), _( "Track Length" ),
995 CSV_COLUMN_DESC::CSV_NONE, true );
996 m_columns.emplace_back( 6u, UNDEFINED_LAYER, _( "Die Length" ), _( "Die Length" ),
997 CSV_COLUMN_DESC::CSV_NONE, true );
998 m_columns.emplace_back( 7u, UNDEFINED_LAYER, _( "Total Length" ), _( "Net Length" ),
999 CSV_COLUMN_DESC::CSV_NONE, true );
1000
1001 m_brd = aParent->GetBoard();
1002
1003 m_data_model = new DATA_MODEL( *this );
1004 m_netsList->AssociateModel( &*m_data_model );
1005
1006 std::vector<std::function<void( void )>> add_col{
1007 [&]()
1008 {
1009 m_netsList->AppendTextColumn( m_columns[COLUMN_NET].display_name,
1011 wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
1012 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE );
1013 },
1014 [&]()
1015 {
1016 m_netsList->AppendTextColumn( m_columns[COLUMN_NAME].display_name,
1018 wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
1019 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1020 wxDATAVIEW_COL_SORTABLE );
1021 },
1022 [&]()
1023 {
1024 m_netsList->AppendTextColumn( m_columns[COLUMN_PAD_COUNT].display_name,
1026 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1027 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1028 wxDATAVIEW_COL_SORTABLE );
1029 },
1030 [&]()
1031 {
1032 m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_COUNT].display_name,
1034 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1035 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1036 wxDATAVIEW_COL_SORTABLE );
1037 },
1038 [&]()
1039 {
1040 m_netsList->AppendTextColumn( m_columns[COLUMN_VIA_LENGTH].display_name,
1042 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1043 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1044 wxDATAVIEW_COL_SORTABLE );
1045 },
1046 [&]()
1047 {
1048 m_netsList->AppendTextColumn( m_columns[COLUMN_BOARD_LENGTH].display_name,
1050 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1051 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1052 wxDATAVIEW_COL_SORTABLE );
1053 },
1054 [&]()
1055 {
1056 m_netsList->AppendTextColumn( m_columns[COLUMN_CHIP_LENGTH].display_name,
1058 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1059 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1060 wxDATAVIEW_COL_SORTABLE );
1061 },
1062 [&]()
1063 {
1064 m_netsList->AppendTextColumn( m_columns[COLUMN_TOTAL_LENGTH].display_name,
1066 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1067 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1068 wxDATAVIEW_COL_SORTABLE );
1069 }
1070 };
1071
1072 auto& cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>()->m_NetInspector;
1073
1074 if( cfg.col_order.size() != add_col.size() )
1075 {
1076 cfg.col_order.resize( add_col.size() );
1077
1078 for( int i = 0; i < (int) add_col.size(); ++i )
1079 cfg.col_order[i] = i;
1080 }
1081
1082 for( int i : cfg.col_order )
1083 add_col.at( i )();
1084
1085 for( PCB_LAYER_ID layer : m_brd->GetEnabledLayers().Seq() )
1086 {
1087 if( !IsCopperLayer( layer ) )
1088 continue;
1089
1090 m_columns.emplace_back( m_columns.size(), layer, m_brd->GetLayerName( layer ),
1091 m_brd->GetLayerName( layer ), CSV_COLUMN_DESC::CSV_NONE, true );
1092
1093 m_netsList->AppendTextColumn( m_brd->GetLayerName( layer ), m_columns.back(),
1094 wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
1095 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
1096 wxDATAVIEW_COL_SORTABLE );
1097 }
1098
1099 m_netsList->SetMinSize( FromDIP( m_netsList->GetMinSize() ) );
1100 m_netsList->SetExpanderColumn( m_netsList->GetColumn( 0 ) );
1101
1102 // avoid onFilterChange for each of the settings as it will re-build the
1103 // list over and over again.
1105
1106 m_textCtrlFilter->SetValue( g_filter_string );
1107 m_cbShowZeroPad->SetValue( cfg.show_zero_pad_nets );
1108
1109
1110 m_groupBy->SetValue( cfg.group_by );
1111 m_groupByKind->SetSelection( cfg.group_by_kind );
1112
1113 m_groupByText->SetValue( cfg.group_by_text );
1114
1116 buildNetsList();
1117
1119
1120 m_addNet->SetBitmap( KiBitmapBundle( BITMAPS::small_plus ) );
1121 m_renameNet->SetBitmap( KiBitmapBundle( BITMAPS::small_edit ) );
1122 m_deleteNet->SetBitmap( KiBitmapBundle( BITMAPS::small_trash ) );
1123
1125 // Now SetupStandardButtons is called, change the label of Cancel button
1126 // to a better name
1127 m_sdbSizerCancel->SetLabel( _( "Close" ) );
1128
1129 m_renameNet->Disable();
1130 m_deleteNet->Disable();
1131
1132 if( cfg.sorting_column != -1 )
1133 {
1134 if( wxDataViewColumn* c = m_netsList->GetColumn( cfg.sorting_column ) )
1135 {
1136 c->SetSortOrder( cfg.sort_order_asc );
1137 m_data_model->Resort();
1138 }
1139 }
1140
1142
1143 if( cfg.dlg_width > 0 && cfg.dlg_height > 0 )
1144 {
1145 SetSize( cfg.dlg_width, cfg.dlg_height );
1146 Center();
1147 }
1148
1149 Bind( EDA_EVT_UNITS_CHANGED, &DIALOG_NET_INSPECTOR::onUnitsChanged, this );
1150 Bind( EDA_EVT_BOARD_CHANGED, &DIALOG_NET_INSPECTOR::onBoardChanged, this );
1151
1152 if( m_brd != nullptr )
1153 {
1154 // if the dialog is opened while something is highlighted on the board ...
1156
1157 m_brd->AddListener( this );
1158 }
1159}
1160
1161
1163{
1164 g_filter_string = m_textCtrlFilter->GetValue();
1165
1166 PCBNEW_SETTINGS* app_settings = nullptr;
1167
1168 try
1169 {
1170 app_settings = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
1171 }
1172 catch( const std::runtime_error& err )
1173 {
1174 wxLogWarning( wxS( "%s" ), err.what() );
1175 }
1176
1177 if( app_settings )
1178 {
1180 wxDataViewColumn* sortingCol = m_netsList->GetSortingColumn();
1181
1182 cfg.show_zero_pad_nets = m_cbShowZeroPad->IsChecked();
1183
1184 cfg.group_by = m_groupBy->IsChecked();
1185 cfg.group_by_kind = m_groupByKind->GetSelection();
1186 cfg.group_by_text = m_groupByText->GetValue();
1187
1188 cfg.sorting_column = sortingCol ? static_cast<int>( sortingCol->GetModelColumn() )
1189 : -1;
1190 cfg.sort_order_asc = sortingCol ? sortingCol->IsSortOrderAscending()
1191 : true;
1192
1193 cfg.col_order.resize( m_data_model->columnCount() );
1194 cfg.col_widths.resize( m_data_model->columnCount() );
1195
1196 for( unsigned int ii = 0; ii < m_data_model->columnCount(); ++ii )
1197 {
1198 cfg.col_order[ii] = (int) m_netsList->GetColumn( ii )->GetModelColumn();
1199 cfg.col_widths[ii] = m_netsList->GetColumn( ii )->GetWidth();
1200 }
1201
1202 cfg.dlg_width = GetSize().x;
1203 cfg.dlg_height = GetSize().y;
1204 }
1205
1206 // the displayed list elements are going to be deleted before the list view itself.
1207 // in some cases it might still do queries on the data model, which would crash
1208 // from now on. so just disassociate it.
1209 m_netsList->AssociateModel( nullptr );
1210
1211 Unbind( EDA_EVT_UNITS_CHANGED, &DIALOG_NET_INSPECTOR::onUnitsChanged, this );
1212 Unbind( EDA_EVT_BOARD_CHANGED, &DIALOG_NET_INSPECTOR::onBoardChanged, this );
1213
1214 if( m_brd != nullptr )
1215 m_brd->RemoveListener( this );
1216}
1217
1218
1219void DIALOG_NET_INSPECTOR::onUnitsChanged( wxCommandEvent& event )
1220{
1222
1223 m_data_model->updateAllItems();
1224
1225 event.Skip();
1226}
1227
1228
1229void DIALOG_NET_INSPECTOR::onBoardChanged( wxCommandEvent& event )
1230{
1231 m_brd = m_frame->GetBoard();
1232
1233 if( m_brd != nullptr )
1234 m_brd->AddListener( this );
1235
1236 buildNetsList();
1237 m_netsList->Refresh();
1238
1239 event.Skip();
1240}
1241
1242
1244{
1245 // Note: the filtering is case insensitive.
1246
1247 // Never show the unconnected net
1248 if( aNet->GetNetCode() <= 0 )
1249 return false;
1250
1251 // Show unconnected nets only if specifically asked for by filter
1252 if( m_netFilter.empty() )
1253 return !aNet->GetNetname().StartsWith( wxT( "unconnected-(" ) );
1254
1255 wxString net_str = UnescapeString( aNet->GetNetname() ).Upper();
1256
1257 for( const std::unique_ptr<EDA_PATTERN_MATCH>& f : m_netFilter )
1258 {
1259 if( f->Find( net_str ) )
1260 return true;
1261 }
1262
1263 return false;
1264}
1265
1266
1268{
1269 bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const
1270 {
1271 return a->Net() < b->Net();
1272 }
1273
1274 bool operator()( const CN_ITEM* a, int b ) const
1275 {
1276 return a->Net() < b;
1277 }
1278
1279 bool operator()( int a, const CN_ITEM* b ) const
1280 {
1281 return a < b->Net();
1282 }
1283};
1284
1285
1287{
1288 // pre-filter the connectivity items and sort them by netcode.
1289 // this avoids quadratic runtime when building the whole net list and
1290 // calculating the total length for each net.
1291
1292 const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>()
1293 .set( PCB_TRACE_T )
1294 .set( PCB_ARC_T )
1295 .set( PCB_VIA_T )
1296 .set( PCB_PAD_T );
1297
1298 std::vector<CN_ITEM*> cn_items;
1299 cn_items.reserve( 1024 );
1300
1301 for( CN_ITEM* cn_item : m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
1302 {
1303 if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
1304 cn_items.push_back( cn_item );
1305 }
1306
1307 std::sort( cn_items.begin(), cn_items.end(), NETCODE_CMP_LESS() );
1308
1309 return cn_items;
1310}
1311
1312
1313void DIALOG_NET_INSPECTOR::updateDisplayedRowValues( const std::optional<LIST_ITEM_ITER>& aRow )
1314{
1315 if( !aRow )
1316 return;
1317
1318 wxDataViewItemArray sel;
1319 m_netsList->GetSelections( sel );
1320
1321 m_data_model->updateItem( aRow );
1322
1323 if( !sel.IsEmpty() )
1324 {
1325 m_netsList->SetSelections( sel );
1326 m_netsList->EnsureVisible( sel.Item( 0 ) );
1327 }
1328}
1329
1330
1332{
1333 return wxString::Format( wxT( "%.3d" ), aNet->GetNetCode() );
1334}
1335
1336
1338{
1339 return UnescapeString( aNet->GetNetname() );
1340}
1341
1342
1343wxString DIALOG_NET_INSPECTOR::formatCount( unsigned int aValue ) const
1344{
1345 return wxString::Format( wxT( "%u" ), aValue );
1346}
1347
1348
1349wxString DIALOG_NET_INSPECTOR::formatLength( int64_t aValue ) const
1350{
1351 return m_frame->MessageTextFromValue( static_cast<long long int>( aValue ),
1352 !m_in_reporting /* Don't include unit label in the string when reporting */ );
1353}
1354
1355
1357{
1358 if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1359 {
1360 // a new net has been added to the board. add it to our list if it
1361 // passes the netname filter test.
1362
1363 if( netFilterMatches( net ) )
1364 {
1365 std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( net );
1366
1367 // the new net could have some pads already assigned, count them.
1368 new_item->SetPadCount( m_brd->GetNodesCount( net->GetNetCode() ) );
1369
1370 m_data_model->addItem( std::move( new_item ) );
1371 }
1372 }
1373 else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1374 {
1375 std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1376
1377 if( r )
1378 {
1379 // try to handle frequent operations quickly.
1380 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( i ) )
1381 {
1382 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
1383 int len = track->GetLength();
1384
1385 list_item->AddLayerWireLength( len, static_cast<int>( track->GetLayer() ) );
1386
1387 if( track->Type() == PCB_VIA_T )
1388 {
1389 list_item->AddViaCount( 1 );
1390 list_item->AddViaLength( calculateViaLength( track ) );
1391 }
1392
1394 return;
1395 }
1396 }
1397
1398 // resort to generic slower net update otherwise.
1399 updateNet( i->GetNet() );
1400 }
1401 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1402 {
1403 for( const PAD* pad : footprint->Pads() )
1404 {
1405 std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1406
1407 if( !r )
1408 {
1409 // if show-zero-pads is off, we might not have this net
1410 // in our list yet, so add it first.
1411 // notice that at this point we are very certain that this net
1412 // will have at least one pad.
1413
1414 if( netFilterMatches( pad->GetNet() ) )
1415 r = m_data_model->addItem( std::make_unique<LIST_ITEM>( pad->GetNet() ) );
1416 }
1417
1418 if( r )
1419 {
1420 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
1421 int len = pad->GetPadToDieLength();
1422
1423 list_item->AddPadCount( 1 );
1424 list_item->AddChipWireLength( len );
1425
1426 if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1427 m_data_model->deleteItem( r );
1428 else
1430 }
1431 }
1432 }
1433}
1434
1435
1436void DIALOG_NET_INSPECTOR::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItem )
1437{
1438 for( BOARD_ITEM* item : aBoardItem )
1439 {
1440 OnBoardItemAdded( aBoard, item );
1441 }
1442}
1443
1444
1446{
1447 if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1448 {
1449 m_data_model->deleteItem( m_data_model->findItem( net ) );
1450 }
1451 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1452 {
1453 for( const PAD* pad : footprint->Pads() )
1454 {
1455 std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1456
1457 if( r )
1458 {
1459 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
1460 int len = pad->GetPadToDieLength();
1461
1462 list_item->SubPadCount( 1 );
1463 list_item->SubChipWireLength( len );
1464
1465 if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1466 m_data_model->deleteItem( r );
1467 else
1469 }
1470 }
1471 }
1472 else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1473 {
1474 std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1475
1476 if( r )
1477 {
1478 // try to handle frequent operations quickly.
1479 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( i ) )
1480 {
1481 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
1482 int len = track->GetLength();
1483
1484 list_item->SubLayerWireLength( len, static_cast<int>( track->GetLayer() ) );
1485
1486 if( track->Type() == PCB_VIA_T )
1487 {
1488 list_item->SubViaCount( 1 );
1489 list_item->SubViaLength( calculateViaLength( track ) );
1490 }
1491
1493 return;
1494 }
1495
1496 // resort to generic slower net update otherwise.
1497 updateNet( i->GetNet() );
1498 }
1499 }
1500}
1501
1502
1504 std::vector<BOARD_ITEM*>& aBoardItems )
1505{
1506 for( BOARD_ITEM* item : aBoardItems )
1507 {
1508 OnBoardItemRemoved( aBoard, item );
1509 }
1510}
1511
1512
1514{
1515 if( dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) != nullptr
1516 || dynamic_cast<FOOTPRINT*>( aBoardItem ) != nullptr )
1517 {
1518 buildNetsList();
1519 m_netsList->Refresh();
1520 }
1521}
1522
1523
1525 std::vector<BOARD_ITEM*>& aBoardItems )
1526{
1527 buildNetsList();
1528 m_netsList->Refresh();
1529}
1530
1531
1533{
1534 if( !m_brd->IsHighLightNetON() )
1535 {
1536 m_netsList->UnselectAll();
1537 }
1538 else
1539 {
1540 const std::set<int>& selected_codes = m_brd->GetHighLightNetCodes();
1541
1542 wxDataViewItemArray new_selection;
1543 new_selection.Alloc( selected_codes.size() );
1544
1545 for( int code : selected_codes )
1546 {
1547 if( std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( code ) )
1548 new_selection.Add( wxDataViewItem( &***r ) );
1549 }
1550
1551 m_netsList->SetSelections( new_selection );
1552
1553 if( !new_selection.IsEmpty() )
1554 m_netsList->EnsureVisible( new_selection.Item( 0 ) );
1555 }
1556}
1557
1558
1560{
1561 buildNetsList();
1562 m_netsList->Refresh();
1563}
1564
1565
1567{
1568 // something for the specified net has changed, update that row.
1569 // ignore nets that are not in our list because the filter doesn't match.
1570
1571 if( !netFilterMatches( aNet ) )
1572 {
1573 m_data_model->deleteItem( m_data_model->findItem( aNet ) );
1574 return;
1575 }
1576
1577 // if the net had no pads before, it might not be in the displayed list yet.
1578 // if it had pads and now doesn't anymore, we might need to remove it from the list.
1579
1580 std::optional<LIST_ITEM_ITER> cur_net_row = m_data_model->findItem( aNet );
1581
1582 const unsigned int node_count = m_brd->GetNodesCount( aNet->GetNetCode() );
1583
1584 if( node_count == 0 && !m_cbShowZeroPad->IsChecked() )
1585 {
1586 m_data_model->deleteItem( cur_net_row );
1587 return;
1588 }
1589
1590 std::unique_ptr<LIST_ITEM> new_list_item = buildNewItem( aNet, node_count,
1592
1593 if( !cur_net_row )
1594 {
1595 m_data_model->addItem( std::move( new_list_item ) );
1596 return;
1597 }
1598
1599 const std::unique_ptr<LIST_ITEM>& cur_list_item = *cur_net_row.value();
1600
1601 if( cur_list_item->GetNetName() != new_list_item->GetNetName() )
1602 {
1603 // if the name has changed, it might require re-grouping.
1604 // it's easier to remove and re-insert it
1605 m_data_model->deleteItem( cur_net_row );
1606 m_data_model->addItem( std::move( new_list_item ) );
1607 }
1608 else
1609 {
1610 // update fields only
1611 cur_list_item->SetPadCount( new_list_item->GetPadCount() );
1612 cur_list_item->SetViaCount( new_list_item->GetViaCount() );
1613
1614 for( size_t ii = 0; ii < MAX_CU_LAYERS; ++ii )
1615 cur_list_item->SetLayerWireLength( new_list_item->GetLayerWireLength( ii ), ii );
1616
1617 cur_list_item->SetChipWireLength( new_list_item->GetChipWireLength() );
1618
1619 updateDisplayedRowValues( cur_net_row );
1620 }
1621}
1622
1623
1624unsigned int DIALOG_NET_INSPECTOR::calculateViaLength( const PCB_TRACK* aTrack ) const
1625{
1626 const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aTrack );
1627
1628 if( !via )
1629 return 0;
1630
1632
1633 // Must be static to keep from raising its ugly head in performance profiles
1634 static std::initializer_list<KICAD_T> traceAndPadTypes = { PCB_TRACE_T, PCB_ARC_T, PCB_PAD_T };
1635
1636 // calculate the via length individually from the board stackup and via's start and end layer.
1637 if( bds.m_HasStackup )
1638 {
1639 PCB_LAYER_ID top_layer = UNDEFINED_LAYER;
1640 PCB_LAYER_ID bottom_layer = UNDEFINED_LAYER;
1641
1642 for( int layer = via->TopLayer(); layer <= via->BottomLayer(); ++layer )
1643 {
1644 if( m_brd->GetConnectivity()->IsConnectedOnLayer( via, layer, traceAndPadTypes ) )
1645 {
1646 if( top_layer == UNDEFINED_LAYER )
1647 top_layer = PCB_LAYER_ID( layer );
1648 else
1649 bottom_layer = PCB_LAYER_ID( layer );
1650 }
1651 }
1652
1653 if( top_layer == UNDEFINED_LAYER )
1654 top_layer = via->TopLayer();
1655 if( bottom_layer == UNDEFINED_LAYER )
1656 bottom_layer = via->BottomLayer();
1657
1658 const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
1659 return stackup.GetLayerDistance( top_layer, bottom_layer );
1660 }
1661 else
1662 {
1663 int dielectricLayers = bds.GetCopperLayerCount() - 1;
1664 // FIXME: not all dielectric layers are the same thickness!
1665 int layerThickness = bds.GetBoardThickness() / dielectricLayers;
1666 int effectiveBottomLayer;
1667
1668 if( via->BottomLayer() == B_Cu )
1669 effectiveBottomLayer = F_Cu + dielectricLayers;
1670 else
1671 effectiveBottomLayer = via->BottomLayer();
1672
1673 int layerCount = effectiveBottomLayer - via->TopLayer();
1674
1675 return layerCount * layerThickness;
1676 }
1677}
1678
1679
1680std::unique_ptr<DIALOG_NET_INSPECTOR::LIST_ITEM>
1682 const std::vector<CN_ITEM*>& aCNItems )
1683{
1684 std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( aNet );
1685
1686 new_item->SetPadCount( aPadCount );
1687
1688 const auto cn_items = std::equal_range( aCNItems.begin(), aCNItems.end(), aNet->GetNetCode(),
1689 NETCODE_CMP_LESS() );
1690
1691 for( auto i = cn_items.first; i != cn_items.second; ++i )
1692 {
1693 BOARD_CONNECTED_ITEM* item = ( *i )->Parent();
1694
1695 if( item->Type() == PCB_PAD_T )
1696 new_item->AddChipWireLength( static_cast<PAD*>( item )->GetPadToDieLength() );
1697
1698 else if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1699 {
1700 new_item->AddLayerWireLength( track->GetLength(),
1701 static_cast<int>( track->GetLayer() ) );
1702
1703 if( item->Type() == PCB_VIA_T )
1704 {
1705 new_item->AddViaCount( 1 );
1706 new_item->AddViaLength( calculateViaLength( track ) );
1707 }
1708 }
1709 }
1710
1711 return new_item;
1712}
1713
1714
1716{
1717 PCBNEW_SETTINGS* app_settings = nullptr;
1718
1719 try
1720 {
1721 app_settings = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
1722 }
1723 catch( const std::runtime_error& err )
1724 {
1725 wxLogWarning( wxS( "%s" ), err.what() );
1726 }
1727
1728 // Only build the list of nets if there is a board && app present
1729 if( !m_brd || !app_settings )
1730 return;
1731
1733
1734 m_in_build_nets_list = true;
1735
1736 // when rebuilding the netlist, try to keep the row selection
1737 // FIXME: handle group selections, preserve expanded/collapsed group states
1738 wxDataViewItemArray sel;
1739 m_netsList->GetSelections( sel );
1740
1741 std::vector<int> prev_selected_netcodes;
1742 prev_selected_netcodes.reserve( sel.GetCount() );
1743
1744 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1745 {
1746 const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1747 prev_selected_netcodes.push_back( item->GetNetCode() );
1748 }
1749
1750 // At least on GTK, wxDVC will crash if you rebuild with a sorting column set.
1751 if( wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn() )
1752 {
1753 cfg.sorting_column = static_cast<int>( sorting_column->GetModelColumn() ) ;
1754 cfg.sort_order_asc = sorting_column->IsSortOrderAscending();
1755 sorting_column->UnsetAsSortKey();
1756 }
1757
1758 m_data_model->deleteAllItems();
1759
1760 std::vector<std::unique_ptr<LIST_ITEM>> new_items;
1761
1762 // for group mode 0,1 each group filter string represents one displayed group,
1763 // so just add them first. for group mode 2,3 the groups are generated dynamically.
1764 if( m_groupBy->IsChecked()
1765 && ( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 1 ) )
1766 {
1767 for( unsigned int i = 0; i < m_groupFilter.size(); ++i )
1768 {
1769 const std::unique_ptr<EDA_PATTERN_MATCH>& filter = m_groupFilter[i];
1770 new_items.emplace_back( std::make_unique<LIST_ITEM>( i, filter->GetPattern() ) );
1771 }
1772 }
1773
1774 std::vector<CN_ITEM*> prefiltered_cn_items = relevantConnectivityItems();
1775
1776
1777 // collect all nets which pass the filter string and also remember the
1778 // suffix after the filter match, if any.
1779 struct NET_INFO
1780 {
1781 int netcode;
1782 NETINFO_ITEM* net;
1783 unsigned int pad_count;
1784 };
1785
1786 struct NET_INFO_CMP_LESS
1787 {
1788 bool operator()( const NET_INFO& a, const NET_INFO& b ) const
1789 {
1790 return a.netcode < b.netcode;
1791 }
1792 bool operator()( const NET_INFO& a, int b ) const
1793 {
1794 return a.netcode < b;
1795 }
1796 bool operator()( int a, const NET_INFO& b ) const
1797 {
1798 return a < b.netcode;
1799 }
1800 };
1801
1802 std::vector<NET_INFO> nets;
1803 nets.reserve( m_brd->GetNetInfo().NetsByNetcode().size() );
1804
1805 for( const std::pair<int, NETINFO_ITEM*> ni : m_brd->GetNetInfo().NetsByNetcode() )
1806 {
1807 if( ni.first == 0 )
1808 m_zero_netitem = ni.second;
1809
1810 if( netFilterMatches( ni.second ) )
1811 nets.emplace_back( NET_INFO{ ni.first, ni.second, 0 } );
1812 }
1813
1814 // count the pads for each net. since the nets are sorted by netcode
1815 // iterating over the footprints' pads is faster.
1816
1817 for( FOOTPRINT* footprint : m_brd->Footprints() )
1818 {
1819 for( PAD* pad : footprint->Pads() )
1820 {
1821 auto i = std::lower_bound( nets.begin(), nets.end(), pad->GetNetCode(),
1822 NET_INFO_CMP_LESS() );
1823
1824 if( i != nets.end() && i->netcode == pad->GetNetCode() )
1825 i->pad_count += 1;
1826 }
1827 }
1828
1829 for( NET_INFO& ni : nets )
1830 {
1831 if( m_cbShowZeroPad->IsChecked() || ni.pad_count > 0 )
1832 new_items.emplace_back( buildNewItem( ni.net, ni.pad_count, prefiltered_cn_items ) );
1833 }
1834
1835
1836 m_data_model->addItems( std::move( new_items ) );
1837
1838 if( cfg.sorting_column != -1 )
1839 {
1840 if( wxDataViewColumn* c = m_netsList->GetColumn( cfg.sorting_column ) )
1841 {
1842 c->SetSortOrder( cfg.sort_order_asc );
1843 m_data_model->Resort();
1844 }
1845 }
1846
1847 // try to restore the selected rows. set the ones that we can't find anymore to -1.
1848 sel.Clear();
1849
1850 for( int& nc : prev_selected_netcodes )
1851 {
1852 std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( nc );
1853
1854 if( r )
1855 {
1856 const std::unique_ptr<LIST_ITEM>& list_item = *r.value();
1857 sel.Add( wxDataViewItem( list_item.get() ) );
1858 }
1859 else
1860 {
1861 nc = -1;
1862 }
1863 }
1864
1865 if( !sel.IsEmpty() )
1866 {
1867 m_netsList->SetSelections( sel );
1868 m_netsList->EnsureVisible( sel.Item( 0 ) );
1869 }
1870 else
1871 {
1872 m_netsList->UnselectAll();
1873 }
1874
1875 alg::delete_matching( prev_selected_netcodes, -1 );
1876
1878
1879 for( int& i : prev_selected_netcodes )
1880 m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( true, i, true );
1881
1884
1885 m_in_build_nets_list = false;
1886}
1887
1888
1889void DIALOG_NET_INSPECTOR::onFilterChange( wxCommandEvent& aEvent )
1890{
1891 wxStringTokenizer filters( m_textCtrlFilter->GetValue().Upper(), wxT( "," ) );
1892 m_netFilter.clear();
1893
1894 while( filters.HasMoreTokens() )
1895 {
1896 wxString t = filters.GetNextToken();
1897 t.Trim( false );
1898 t.Trim( true );
1899
1900 if( !t.IsEmpty() )
1901 {
1902 m_netFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1903 m_netFilter.back()->SetPattern( t );
1904 }
1905 }
1906
1907 wxStringTokenizer group_filters( m_groupByText->GetValue(), wxT( "," ) );
1908 m_groupFilter.clear();
1909
1910 while( group_filters.HasMoreTokens() )
1911 {
1912 wxString t = group_filters.GetNextToken();
1913 t.Trim( false );
1914 t.Trim( true );
1915
1916 if( !t.IsEmpty() )
1917 {
1918 if( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 2 )
1919 {
1920 // type 2: wildcard match, use the matching substring as a group key.
1921 // the number of groups is determined dynamically by the number of
1922 // resulting matches in the whole set.
1923 m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1924 m_groupFilter.back()->SetPattern( t );
1925 }
1926 else if( m_groupByKind->GetSelection() == 1 || m_groupByKind->GetSelection() == 3 )
1927 {
1928 // type 3: regex match, use the matching substring as a group key.
1929 // the number of groups is determined dynamically by the number of
1930 // resulting matches in the whole set.
1931 m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
1932 m_groupFilter.back()->SetPattern( t );
1933 }
1934 }
1935 }
1936
1938 buildNetsList();
1939}
1940
1941
1943{
1944 onSelChanged();
1945}
1946
1947
1949{
1950 // ignore selection changes while the whole list is being rebuilt.
1952 return;
1953
1954 RENDER_SETTINGS* renderSettings = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
1955
1956 bool enable_rename_button = false;
1957 bool enable_delete_button = false;
1958
1959 if( m_netsList->HasSelection() )
1960 {
1961 wxDataViewItemArray sel;
1962 m_netsList->GetSelections( sel );
1963
1964 renderSettings->SetHighlight( false );
1965
1966 enable_rename_button = sel.GetCount() == 1;
1967 enable_delete_button = true;
1968
1969 for( unsigned int i = 0; i < sel.GetCount(); ++i )
1970 {
1971 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1972
1973 if( ii->GetIsGroup() )
1974 {
1975 enable_rename_button = false;
1976
1977 for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
1978 renderSettings->SetHighlight( true, ( *c )->GetNetCode(), true );
1979 }
1980 else
1981 {
1982 renderSettings->SetHighlight( true, ii->GetNetCode(), true );
1983 }
1984 }
1985 }
1986 else
1987 {
1988 renderSettings->SetHighlight( false );
1989 }
1990
1993
1994 m_renameNet->Enable( enable_rename_button );
1995 m_deleteNet->Enable( enable_delete_button );
1996}
1997
1998
1999void DIALOG_NET_INSPECTOR::onSortingChanged( wxDataViewEvent& aEvent )
2000{
2001 // FIXME: Whenever the sort criteria changes (sorting column)
2002 // the visible row-numers of the selection get preserved, not the actual
2003 // elements. Don't know at the moment how to preserve the selection,
2004 // so just clear it for now.
2005
2006 m_netsList->UnselectAll();
2007
2011}
2012
2013
2015{
2016 PCBNEW_SETTINGS* app_settings = nullptr;
2017
2018 try
2019 {
2020 app_settings = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
2021 }
2022 catch( const std::runtime_error& err )
2023 {
2024 wxLogWarning( wxS( "%s" ), err.what() );
2025 }
2026
2027 if( app_settings )
2028 {
2030 wxWindowUpdateLocker locker( m_netsList );
2031
2032 if( cfg.col_widths.size() < m_columns.size() )
2033 {
2034 cfg.col_widths.reserve( m_columns.size() );
2035
2037 cfg.col_widths.push_back( GetTextExtent( m_column.display_name ).x );
2038
2039 int minValueWidth = GetTextExtent( wxT( "00000,000 mm" ) ).x;
2040 int minNumberWidth = GetTextExtent( wxT( "000" ) ).x;
2041 int minNameWidth = GetTextExtent( wxT( "MMMMMM" ) ).x;
2042
2043 // Considering left and right margins.
2044 // For wxRenderGeneric it is 5px.
2045 // Also account for the sorting arrow in the column header.
2046 // Column 0 also needs space for any potential expander icons.
2047 const int margins = 15;
2048 const int extra_width = 30;
2049
2050 cfg.col_widths[0] = std::max( cfg.col_widths[0], minNumberWidth ) + extra_width;
2051 cfg.col_widths[1] = std::max( cfg.col_widths[1], minNameWidth ) + margins;
2052 cfg.col_widths[2] = std::max( cfg.col_widths[2], minNumberWidth ) + margins;
2053 cfg.col_widths[3] = std::max( cfg.col_widths[3], minNumberWidth ) + margins;
2054
2055 for( size_t ii = 4; ii < cfg.col_widths.size(); ++ii )
2056 cfg.col_widths[ii] = std::max( cfg.col_widths[ii], minValueWidth ) + margins;
2057 }
2058
2059 for( size_t ii = 0; ii < m_data_model->columnCount(); ++ii )
2060 m_netsList->GetColumn( ii )->SetWidth( cfg.col_widths[ii] );
2061
2062 m_netsList->Refresh();
2063
2064 // Force refresh on GTK so that horizontal scroll bar won't appear
2065#ifdef __WXGTK__
2066 wxPoint pos = m_netsList->GetPosition();
2067 m_netsList->Move( pos.x, pos.y + 1 );
2068 m_netsList->Move( pos.x, pos.y );
2069#endif
2070 }
2071}
2072
2073
2074void DIALOG_NET_INSPECTOR::onAddNet( wxCommandEvent& aEvent )
2075{
2076 wxString newNetName;
2077 NETNAME_VALIDATOR validator( &newNetName );
2078
2079 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
2080 dlg.SetTextValidator( validator );
2081
2082 while( true )
2083 {
2084 if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
2085 return; //Aborted by user
2086
2087 newNetName = dlg.GetValue();
2088
2089 if( m_brd->FindNet( newNetName ) )
2090 {
2091 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
2092 newNetName ) );
2093 newNetName = wxEmptyString;
2094 }
2095 else
2096 {
2097 break;
2098 }
2099 }
2100
2101 NETINFO_ITEM *newnet = new NETINFO_ITEM( m_brd, dlg.GetValue(), 0 );
2102
2103 m_brd->Add( newnet );
2104 m_frame->OnModify();
2105
2106 // We'll get an OnBoardItemAdded callback from this to update our listbox
2107}
2108
2109
2110void DIALOG_NET_INSPECTOR::onRenameNet( wxCommandEvent& aEvent )
2111{
2112 if( m_netsList->GetSelectedItemsCount() == 1 )
2113 {
2114 const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
2115
2116 if( sel->GetIsGroup() )
2117 return;
2118
2119 NETINFO_ITEM* net = sel->GetNet();
2120 wxString fullNetName = net->GetNetname();
2121 wxString netPath;
2122 wxString shortNetName;
2123
2124 if( fullNetName.Contains( wxT( "/" ) ) )
2125 {
2126 netPath = fullNetName.BeforeLast( '/' ) + '/';
2127 shortNetName = fullNetName.AfterLast( '/' );
2128 }
2129 else
2130 {
2131 shortNetName = fullNetName;
2132 }
2133
2134 wxString unescapedShortName = UnescapeString( shortNetName );
2135
2136 WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
2137 NETNAME_VALIDATOR validator( &unescapedShortName );
2138 dlg.SetTextValidator( validator );
2139
2140 while( true )
2141 {
2142 if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
2143 return;
2144
2145 unescapedShortName = dlg.GetValue();
2146
2147 if( unescapedShortName.IsEmpty() )
2148 {
2149 DisplayError( this, wxString::Format( _( "Net name cannot be empty." ),
2150 unescapedShortName ) );
2151 continue;
2152 }
2153
2154 shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
2155 fullNetName = netPath + shortNetName;
2156
2157 if( m_brd->FindNet( shortNetName ) || m_brd->FindNet( fullNetName ) )
2158 {
2159 DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
2160 unescapedShortName ) );
2161 unescapedShortName = wxEmptyString;
2162 }
2163 else
2164 {
2165 break;
2166 }
2167 }
2168
2169 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
2170 {
2171 if( boardItem->GetNet() == net )
2172 boardItem->SetFlags( CANDIDATE );
2173 else
2174 boardItem->ClearFlags( CANDIDATE );
2175 }
2176
2177 // the changed name might require re-grouping. remove/re-insert is easier.
2178 auto removed_item = m_data_model->deleteItem( m_data_model->findItem( net ) );
2179
2180 m_brd->Remove( net );
2181 net->SetNetname( fullNetName );
2182 m_brd->Add( net );
2183
2184 for( BOARD_CONNECTED_ITEM* boardItem : m_frame->GetBoard()->AllConnectedItems() )
2185 {
2186 if( boardItem->GetFlags() & CANDIDATE )
2187 boardItem->SetNet( net );
2188 }
2189
2190 buildNetsList();
2191
2192 if( std::optional<LIST_ITEM_ITER> r = m_data_model->findItem( net ) )
2193 m_netsList->Select( wxDataViewItem( r.value()->get() ) );
2194
2195 m_frame->OnModify();
2196
2197 // Currently only tracks and pads have netname annotations and need to be redrawn,
2198 // but zones are likely to follow. Since we don't have a way to ask what is current,
2199 // just refresh all items.
2202 }
2203}
2204
2205
2206void DIALOG_NET_INSPECTOR::onDeleteNet( wxCommandEvent& aEvent )
2207{
2208 if( !m_netsList->HasSelection() )
2209 return;
2210
2211 wxDataViewItemArray sel;
2212 m_netsList->GetSelections( sel );
2213
2214 auto delete_one =
2215 [this]( const LIST_ITEM* i )
2216 {
2217 if( i->GetPadCount() == 0
2218 || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ),
2219 i->GetNetName() ) ) )
2220 {
2221 // This is a bit hacky, but it will do for now, since this is the only path
2222 // outside the netlist updater where you can remove a net from a BOARD.
2223 int removedCode = i->GetNetCode();
2224
2226 [removedCode]( KIGFX::VIEW_ITEM* aItem ) -> int
2227 {
2228 auto boardItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem );
2229
2230 if( boardItem && boardItem->GetNetCode() == removedCode )
2231 return KIGFX::REPAINT;
2232
2233 EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aItem );
2234
2235 if( text && text->HasTextVars() )
2236 {
2237 text->ClearRenderCache();
2238 text->ClearBoundingBoxCache();
2240 }
2241
2242 return 0;
2243 } );
2244
2245 m_brd->Remove( i->GetNet() );
2246 m_frame->OnModify();
2247
2248 // We'll get an OnBoardItemRemoved callback from this to update our listbox
2249 }
2250 };
2251
2252 for( unsigned int i = 0; i < sel.GetCount(); ++i )
2253 {
2254 const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
2255
2256 if( ii->GetIsGroup() )
2257 {
2258 if( ii->ChildrenCount() != 0
2259 && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ),
2260 ii->GetGroupName() ) ) )
2261 {
2262 // we can't be iterating the children container and deleting items from
2263 // it at the same time. thus take a copy of it first.
2264 std::vector<const LIST_ITEM*> children;
2265 children.reserve( ii->ChildrenCount() );
2266 std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
2267
2268 for( const LIST_ITEM* c : children )
2269 delete_one( c );
2270 }
2271 }
2272 else
2273 {
2274 delete_one( ii );
2275 }
2276 }
2277}
2278
2279
2280void DIALOG_NET_INSPECTOR::onReport( wxCommandEvent& aEvent )
2281{
2282 wxFileDialog dlg( this, _( "Save Report File" ), "", "",
2283 _( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
2284 wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
2285
2286 if( dlg.ShowModal() == wxID_CANCEL )
2287 return;
2288
2289 wxTextFile f( dlg.GetPath() );
2290
2291 f.Create();
2292
2293 wxString txt;
2294
2295 m_in_reporting = true;
2296
2297 // Print Header:
2298 for( auto&& col : m_columns )
2299 {
2300 txt += '"';
2301
2302 if( col.has_units )
2303 {
2304 txt += wxString::Format( _( "%s (%s)" ), col.csv_name,
2306 }
2307 else
2308 {
2309 txt += col.csv_name;
2310 }
2311 txt += wxT( "\";" );
2312 }
2313
2314 f.AddLine( txt );
2315
2316 // Print list of nets:
2317 const unsigned int num_rows = m_data_model->itemCount();
2318
2319 for( unsigned int row = 0; row < num_rows; row++ )
2320 {
2321 auto& i = m_data_model->itemAt( row );
2322
2323 if( i.GetIsGroup() || i.GetNetCode() == 0 )
2324 continue;
2325
2326 txt = "";
2327
2328 for( auto&& col : m_columns )
2329 {
2330 if( static_cast<int>( col.csv_flags ) & static_cast<int>( CSV_COLUMN_DESC::CSV_QUOTE ) )
2331 txt += '"' + m_data_model->valueAt( col.num, row ).GetString() + wxT( "\";" );
2332 else
2333 txt += m_data_model->valueAt( col.num, row ).GetString() + ';';
2334 }
2335
2336 f.AddLine( txt );
2337 }
2338
2339 m_in_reporting = false;
2340
2341 f.Write();
2342 f.Close();
2343}
2344
2345
2346void DIALOG_NET_INSPECTOR::onClose( wxCloseEvent& aEvent )
2347{
2348 // Dialog is mode-less so let the parent know that it needs to be destroyed.
2349 if( !IsModal() && !IsQuasiModal() )
2350 {
2351 wxCommandEvent* evt = new wxCommandEvent( EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxID_ANY );
2352
2353 evt->SetEventObject( this );
2354 wxWindow* parent = GetParent();
2355
2356 if( parent )
2357 wxQueueEvent( parent, evt );
2358 }
2359
2360 aEvent.Skip();
2361}
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
int GetBoardThickness() const
The full thickness of the board including copper and masks.
BOARD_STACKUP & GetStackupDescriptor()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:77
Manage layers needed to make a physical board.
int GetLayerDistance(PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer) const
Calculate the distance (height) between the two given copper layers.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:276
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:832
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:689
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:891
const std::set< int > & GetHighLightNetCodes() const
Definition: board.h:498
const std::vector< BOARD_CONNECTED_ITEM * > AllConnectedItems()
Definition: board.cpp:2303
void AddListener(BOARD_LISTENER *aListener)
Add a listener to the board to receive calls whenever something on the board has been modified.
Definition: board.cpp:2353
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1680
FOOTPRINTS & Footprints()
Definition: board.h:318
unsigned GetNodesCount(int aNet=-1) const
Definition: board.cpp:1408
bool IsHighLightNetON() const
Definition: board.h:514
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:576
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:806
void RemoveListener(BOARD_LISTENER *aListener)
Remove the specified listener.
Definition: board.cpp:2360
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1007
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:441
CN_ITEM represents a BOARD_CONNETED_ITEM in the connectivity system (ie: a pad, track/arc/via,...
int Net() const
std::optional< LIST_ITEM_ITER > findItem(int aNetCode)
unsigned int GetColumnCount() const override
wxDataViewItem GetParent(const wxDataViewItem &aItem) const override
bool SetValue(const wxVariant &aInValue, const wxDataViewItem &aItem, unsigned int aCol) override
static int compareUInt(uint64_t aValue1, uint64_t aValue2, bool aAsc)
void GetValue(wxVariant &aOutValue, const wxDataViewItem &aItem, unsigned int aCol) const override
int Compare(const wxDataViewItem &aItem1, const wxDataViewItem &aItem2, unsigned int aCol, bool aAsc) const override
void addItems(std::vector< std::unique_ptr< LIST_ITEM > > aItems)
bool HasContainerColumns(const wxDataViewItem &aItem) const override
std::vector< std::unique_ptr< LIST_ITEM > > m_items
bool IsContainer(const wxDataViewItem &aItem) const override
const LIST_ITEM & itemAt(unsigned int aRow) const
wxString GetColumnType(unsigned int) const override
DATA_MODEL(DIALOG_NET_INSPECTOR &parent)
void updateItem(const std::optional< LIST_ITEM_ITER > &aRow)
std::optional< LIST_ITEM_ITER > addItem(std::unique_ptr< LIST_ITEM > aItem)
std::optional< LIST_ITEM_ITER > findItem(NETINFO_ITEM *aNet)
unsigned int GetChildren(const wxDataViewItem &aParent, wxDataViewItemArray &aChildren) const override
std::unique_ptr< LIST_ITEM > deleteItem(const std::optional< LIST_ITEM_ITER > &aRow)
wxVariant valueAt(unsigned int aCol, unsigned int aRow) const
bool itemColumnChanged(const LIST_ITEM *aItem, unsigned int aCol) const
unsigned long long int GetTotalLength() const
void SetPadCount(unsigned int aValue)
void SubLayerWireLength(const uint64_t aValue, size_t aLayer)
LIST_ITEM & operator=(const LIST_ITEM &)=delete
std::array< uint64_t, MAX_CU_LAYERS > m_layer_wire_length
void AddPadCount(unsigned int aValue)
uint64_t GetLayerWireLength(size_t aLayer) const
void SetViaLength(unsigned int aValue)
void AddViaLength(unsigned int aValue)
void SetLayerWireLength(const uint64_t aValue, size_t aLayer)
void AddViaCount(unsigned int aValue)
std::vector< LIST_ITEM * > m_children
LIST_ITEM(unsigned int aGroupNumber, const wxString &aGroupName)
void SubPadCount(unsigned int aValue)
const wxString & GetGroupName() const
void AddLayerWireLength(const uint64_t aValue, size_t aLayer)
void SubViaCount(unsigned int aValue)
void SetViaCount(unsigned int aValue)
Class DIALOG_NET_INSPECTOR_BASE.
void onUnitsChanged(wxCommandEvent &event)
wxString formatNetName(const NETINFO_ITEM *aNet) const
void onDeleteNet(wxCommandEvent &event) override
virtual void OnBoardHighlightNetChanged(BOARD &aBoard) override
virtual void OnBoardItemAdded(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
virtual void OnBoardItemChanged(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void updateNet(NETINFO_ITEM *aNet)
virtual void OnBoardNetSettingsChanged(BOARD &aBoard) override
DIALOG_NET_INSPECTOR(PCB_EDIT_FRAME *aParent)
virtual void OnBoardItemsChanged(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_groupFilter
void onAddNet(wxCommandEvent &event) override
virtual void OnBoardItemRemoved(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void onReport(wxCommandEvent &event) override
virtual void OnBoardItemsAdded(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
void updateDisplayedRowValues(const std::optional< LIST_ITEM_ITER > &aRow)
virtual void onClose(wxCloseEvent &aEvent) override
virtual void OnBoardItemsRemoved(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
std::vector< CN_ITEM * > relevantConnectivityItems() const
wxObjectDataPtr< DATA_MODEL > m_data_model
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_netFilter
void onRenameNet(wxCommandEvent &event) override
wxString formatLength(int64_t aValue) const
std::unique_ptr< LIST_ITEM > buildNewItem(NETINFO_ITEM *aNet, unsigned int aPadCount, const std::vector< CN_ITEM * > &aCNItems)
void onFilterChange(wxCommandEvent &event) override
bool netFilterMatches(NETINFO_ITEM *aNet) const
void onSortingChanged(wxDataViewEvent &event) override
unsigned int calculateViaLength(const PCB_TRACK *) const
wxString formatNetCode(const NETINFO_ITEM *aNet) const
std::vector< COLUMN_DESC > m_columns
wxString formatCount(unsigned int aValue) const
void onBoardChanged(wxCommandEvent &event)
EDA_UNITS m_units
Definition: dialog_shim.h:204
void SetupStandardButtons(std::map< int, wxString > aLabels={})
bool IsQuasiModal() const
Definition: dialog_shim.h:106
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:80
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition: view_item.h:84
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:763
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition: view.cpp:1518
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:215
void UpdateAllItemsConditionally(int aUpdateFlags, std::function< bool(VIEW_ITEM *)> aCondition)
Update items in the view according to the given flags and condition.
Definition: view.cpp:1528
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:418
Handle the data for a net.
Definition: netinfo.h:56
void SetNetname(const wxString &aNewName)
Set the long netname to aNetName, the short netname to the last token in the long netname's path,...
Definition: netinfo.h:139
const wxString & GetNetname() const
Definition: netinfo.h:114
int GetNetCode() const
Definition: netinfo.h:108
const NETCODES_MAP & NetsByNetcode() const
Return the netcode map, at least for python.
Definition: netinfo.h:371
Definition: pad.h:59
DIALOG_NET_INSPECTOR m_NetInspector
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
bool Enable(bool aEnable=true) override
void SetBitmap(const wxBitmapBundle &aBmp)
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
EDA_UNITS GetUserUnits() const
A KICAD version of wxTextEntryDialog which supports the various improvements/work-arounds from DIALOG...
wxString GetValue() const
void SetTextValidator(wxTextValidatorStyle style)
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:360
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:280
This file is part of the common library.
static wxString g_filter_string
wxDEFINE_EVENT(EDA_EVT_CLOSE_NET_INSPECTOR_DIALOG, wxCommandEvent)
@ COLUMN_VIA_COUNT
@ COLUMN_NUM_STATIC_COL
@ COLUMN_PAD_COUNT
@ COLUMN_TOTAL_LENGTH
@ COLUMN_VIA_LENGTH
@ COLUMN_BOARD_LENGTH
@ COLUMN_CHIP_LENGTH
#define _(s)
#define CANDIDATE
flag indicating that the structure is connected
Abstract pattern-matching tool and implementations.
#define MAX_CU_LAYERS
Definition: layer_ids.h:141
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:879
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:96
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:65
KICOMMON_API wxString GetLabel(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:139
@ REPAINT
Item needs to be redrawn.
Definition: view_item.h:57
@ GEOMETRY
Position or shape has changed.
Definition: view_item.h:54
void delete_matching(_Container &__c, _Value __value)
Covers for the horrifically named std::remove and std::remove_if (neither of which remove anything).
Definition: kicad_algo.h:165
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
int ValueStringCompare(const wxString &strFWord, const wxString &strSWord)
Compare strings like the strcmp function but handle numbers and modifiers within the string text corr...
@ CTX_NETNAME
Definition: string_utils.h:53
COLUMN_DESC(unsigned aNum, PCB_LAYER_ID aLayer, const wxString &aDisp, const wxString &aCsv, CSV_COLUMN_DESC aFlags, bool aHasUnits)
bool operator()(const CN_ITEM *a, const CN_ITEM *b) const
bool operator()(int a, const CN_ITEM *b) const
bool operator()(const CN_ITEM *a, int b) const
VECTOR3I res
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
Custom text control validator definitions.
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Definition of file extensions used in Kicad.