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