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