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