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 <olegendo@gcc.gnu.org>
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 <kicad_string.h>
28 #include <board.h>
29 #include <track.h>
30 #include <dialog_net_inspector.h>
31 #include <eda_pattern_match.h>
33 #include <view/view.h>
34 #include <view/view_controls.h>
35 #include <pcb_painter.h>
39 #include <validators.h>
40 #include <bitmaps.h>
41 #include <wx/tokenzr.h>
42 #include <bitset>
43 
45 {
46  enum
47  {
48  CSV_NONE = 0,
49  CSV_QUOTE = 1 << 0
50  };
51 
52  unsigned int num;
53  wxString display_name;
54  wxString csv_name;
55  unsigned int csv_flags;
56 
57  operator unsigned int() const
58  {
59  return num;
60  }
61 };
62 
63 
64 #define def_col( c, num, name, csv_name, csv_flags ) \
65  const DIALOG_NET_INSPECTOR::COLUMN_DESC DIALOG_NET_INSPECTOR::c = { num, \
66  name, \
67  csv_name, \
68  COLUMN_DESC::csv_flags }
69 
70 def_col( COLUMN_NET, 0, _( "Net" ), _( "Net Code" ), CSV_NONE );
71 def_col( COLUMN_NAME, 1, _( "Name" ), _( "Net Name" ), CSV_QUOTE );
72 def_col( COLUMN_PAD_COUNT, 2, _( "Pad Count" ), _( "Pad Count" ), CSV_NONE );
73 def_col( COLUMN_VIA_COUNT, 3, _( "Via Count" ), _( "Via Count" ), CSV_NONE );
74 def_col( COLUMN_VIA_LENGTH, 4, _( "Via Length" ), _( "Via Length" ), CSV_NONE );
75 def_col( COLUMN_BOARD_LENGTH, 5, _( "Track Length" ), _( "Track Length" ), CSV_NONE );
76 def_col( COLUMN_CHIP_LENGTH, 6, _( "Die Length" ), _( "Die Length" ), CSV_NONE );
77 def_col( COLUMN_TOTAL_LENGTH, 7, _( "Total Length" ), _( "Net Length" ), CSV_NONE );
78 
79 #undef def_col
80 
81 
83 {
84 private:
85  // an item can be the child of only one parent at a time.
86  // FIXME: could use a more lightweight container like intrusive forward list.
87  LIST_ITEM* m_parent = nullptr;
88  std::vector<LIST_ITEM*> m_children;
89 
90  bool m_is_group = false;
91  unsigned int m_group_number = 0;
92  NETINFO_ITEM* m_net = nullptr;
93  unsigned int m_pad_count = 0;
94  unsigned int m_via_count = 0;
95  uint64_t m_via_length = 0;
96  uint64_t m_board_wire_length = 0;
97  uint64_t m_chip_wire_length = 0;
98 
99  // dirty bits to record when some attribute has changed. this is to
100  // avoid unnecessary resort operations.
101  std::bitset<5> m_column_changed;
102 
103  // cached formatted net name for faster display sorting.
104  wxString m_net_name;
105 
106 public:
107  LIST_ITEM( unsigned int aGroupNumber, const wxString& aGroupName ) :
108  m_is_group( true ),
109  m_group_number( aGroupNumber ),
110  m_net_name( aGroupName )
111  {
112  }
113 
115  m_net( aNet )
116  {
117  m_net_name = UnescapeString( aNet->GetNetname() );
118  }
119 
120  LIST_ITEM& operator=( const LIST_ITEM& ) = delete;
121 
122  bool GetIsGroup() const { return m_is_group; }
123 
124  auto ChildrenBegin() const { return m_children.begin(); }
125  auto ChildrenEnd() const { return m_children.end(); }
126  unsigned int ChildrenCount() const { return m_children.size(); }
127 
128  NETINFO_ITEM* GetNet() const { return m_net; }
129 
130  int GetNetCode() const
131  {
132  return GetIsGroup() ? ( 0 - int( m_group_number ) - 1 ) : m_net->GetNetCode();
133  }
134 
135  const wxString& GetNetName() const { return m_net_name; }
136  const wxString& GetGroupName() const { return m_net_name; }
137 
139  {
140  m_column_changed.reset();
141  }
142 
143 #define gen( mvar, chg_bit, get, set, add, sub, changed ) \
144  decltype( mvar ) get() const \
145  { \
146  return mvar; \
147  } \
148  \
149  bool changed() const \
150  { \
151  return m_column_changed[chg_bit]; \
152  } \
153  \
154  void set( const decltype( mvar )& aValue ) \
155  { \
156  if( m_parent ) \
157  m_parent->set( m_parent->get() - mvar + aValue ); \
158  \
159  static_assert( chg_bit < decltype( m_column_changed )().size(), "" ); \
160  m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( mvar != aValue ); \
161  mvar = aValue; \
162  } \
163  \
164  void add( const decltype( mvar )& aValue ) \
165  { \
166  if( m_parent ) \
167  m_parent->add( aValue ); \
168  \
169  static_assert( chg_bit < decltype( m_column_changed )().size(), "" ); \
170  m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( aValue != 0 ); \
171  mvar += aValue; \
172  } \
173  \
174  void sub( const decltype( mvar )& aValue ) \
175  { \
176  if( m_parent ) \
177  m_parent->sub( aValue ); \
178  \
179  static_assert( chg_bit < decltype( m_column_changed )().size(), "" ); \
180  m_column_changed[chg_bit] = m_column_changed[chg_bit] | ( aValue != 0 ); \
181  mvar -= aValue; \
182  }
183 
184  gen( m_pad_count, 0, GetPadCount, SetPadCount, AddPadCount, SubPadCount, PadCountChanged );
185  gen( m_via_count, 1, GetViaCount, SetViaCount, AddViaCount, SubViaCount, ViaCountChanged );
186  gen( m_via_length, 2, GetViaLength, SetViaLength, AddViaLength, SubViaLength, ViaLengthChanged );
187  gen( m_board_wire_length, 3, GetBoardWireLength, SetBoardWireLength, AddBoardWireLength,
188  SubBoardWireLength, BoardWireLengthChanged );
189  gen( m_chip_wire_length, 4, GetChipWireLength, SetChipWireLength, AddChipWireLength,
190  SubChipWireLength, ChipWireLengthChanged );
191 
192 #undef gen
193 
194  // the total length column is always computed, never stored.
195  unsigned long long int GetTotalLength() const
196  {
197  return GetBoardWireLength() + GetViaLength() + GetChipWireLength();
198  }
199 
200  bool TotalLengthChanged() const
201  {
202  return BoardWireLengthChanged() | ViaLengthChanged() | ChipWireLengthChanged();
203  }
204 
205  LIST_ITEM* Parent() const
206  {
207  return m_parent;
208  }
209 
210  void SetParent( LIST_ITEM* aParent )
211  {
212  if( m_parent == aParent )
213  return;
214 
215  if( m_parent != nullptr )
216  {
217  m_parent->SubPadCount( GetPadCount() );
218  m_parent->SubViaCount( GetViaCount() );
219  m_parent->SubViaLength( GetViaLength() );
220  m_parent->SubBoardWireLength( GetBoardWireLength() );
221  m_parent->SubChipWireLength( GetChipWireLength() );
222 
223  m_parent->m_children.erase( std::find( m_parent->m_children.begin(),
224  m_parent->m_children.end(), this ) );
225  }
226 
227  m_parent = aParent;
228 
229  if( m_parent != nullptr )
230  {
231  m_parent->AddPadCount( GetPadCount() );
232  m_parent->AddViaCount( GetViaCount() );
233  m_parent->AddViaLength( GetViaLength() );
234  m_parent->AddBoardWireLength( GetBoardWireLength() );
235  m_parent->AddChipWireLength( GetChipWireLength() );
236 
237  m_parent->m_children.push_back( this );
238  }
239  }
240 };
241 
242 
244 {
245  template <typename T>
246  bool operator()( const T& a, const T& b ) const
247  {
248  return a->GetNetCode() < b->GetNetCode();
249  }
250 
251  template <typename T>
252  bool operator()( const T& a, int b ) const
253  {
254  return a->GetNetCode() < b;
255  }
256 
257  template <typename T>
258  bool operator()( int a, const T& b ) const
259  {
260  return a < b->GetNetCode();
261  }
262 };
263 
264 
265 class DIALOG_NET_INSPECTOR::DATA_MODEL : public wxDataViewModel
266 {
267 private:
269 
270  // primary container, sorted by netcode number.
271  // groups have netcode < 0, so they always come first, in the order
272  // of the filter strings as input by the user (group mode 0, 1) or
273  // in order of occurance (group mode 2, 3).
274  std::vector<std::unique_ptr<LIST_ITEM>> m_items;
275 
276 public:
277  static const auto& columnDesc()
278  {
279  static const std::array<COLUMN_DESC, 8> r =
280  { { COLUMN_NET,
281  COLUMN_NAME,
288 
289  return r;
290  }
291 
292  DATA_MODEL( DIALOG_NET_INSPECTOR& parent ) : m_parent( parent )
293  {
294  }
295 
296  unsigned int columnCount() const
297  {
298  return columnDesc().size();
299  }
300 
301  unsigned int itemCount() const
302  {
303  return m_items.size();
304  }
305 
306  wxVariant valueAt( unsigned int aCol, unsigned int aRow ) const
307  {
308  wxVariant r;
309  GetValue( r, wxDataViewItem( const_cast<LIST_ITEM*>( &*( m_items[aRow] ) ) ), aCol );
310  return r;
311  }
312 
313  const LIST_ITEM& itemAt( unsigned int aRow ) const
314  {
315  return *m_items.at( aRow );
316  }
317 
319  {
320  auto i = std::lower_bound(
321  m_items.begin(), m_items.end(), aNetCode, LIST_ITEM_NETCODE_CMP_LESS() );
322 
323  if( i == m_items.end() || ( *i )->GetNetCode() != aNetCode )
324  return {};
325 
326  return { i };
327  }
328 
330  {
331  if( aNet != nullptr )
332  return findItem( aNet->GetNetCode() );
333  else
334  return {};
335  }
336 
337  OPT<LIST_ITEM_ITER> addItem( std::unique_ptr<LIST_ITEM> aItem )
338  {
339  if( aItem == nullptr )
340  return {};
341 
342  // make sure that the vector is always sorted. usually when new nets are added,
343  // they always get a higher netcode number than the already existing ones.
344  // however, if we've got filtering enabled, we might not have all the nets in
345  // our list, so do a sorted insertion.
346 
347  auto new_iter = std::lower_bound( m_items.begin(), m_items.end(), aItem->GetNetCode(),
349 
350  new_iter = m_items.insert( new_iter, std::move( aItem ) );
351  const std::unique_ptr<LIST_ITEM>& new_item = *new_iter;
352 
353  if( m_parent.m_groupBy->IsChecked()
354  && ( m_parent.m_groupByKind->GetSelection() == 0
355  || m_parent.m_groupByKind->GetSelection() == 1 ) )
356  {
357  for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j )
358  {
359  if( m_parent.m_groupFilter[j]->Find( new_item->GetNetName() ) )
360  {
361  new_item->SetParent( &*m_items[j] );
362  break;
363  }
364  }
365  }
366  else if( m_parent.m_groupBy->IsChecked()
367  && ( m_parent.m_groupByKind->GetSelection() == 2
368  || m_parent.m_groupByKind->GetSelection() == 3 ) )
369  {
370  auto groups_begin = m_items.begin();
371  auto groups_end = std::find_if_not( m_items.begin(), m_items.end(),
372  []( const std::unique_ptr<LIST_ITEM>& x )
373  {
374  return x->GetIsGroup();
375  } );
376 
377  for( std::unique_ptr<EDA_PATTERN_MATCH>& f : m_parent.m_groupFilter )
378  {
379  EDA_PATTERN_MATCH::FIND_RESULT match = f->Find( new_item->GetNetName() );
380 
381  if( match )
382  {
383  wxString match_str = new_item->GetNetName().substr( match.start, match.length );
384 
385  auto group = std::find_if( groups_begin, groups_end,
386  [&]( const std::unique_ptr<LIST_ITEM>& x )
387  {
388  return x->GetNetName() == match_str;
389  } );
390 
391  if( group == groups_end )
392  {
393  int dist = std::distance( groups_end, groups_begin );
394  group = m_items.insert( groups_end,
395  std::make_unique<LIST_ITEM>( dist, match_str ) );
396 
397  groups_end = group + 1;
398 
399  ItemAdded( wxDataViewItem(( *group )->Parent() ),
400  wxDataViewItem( &**group ) );
401  }
402 
403  new_item->SetParent( &**group );
404  break;
405  }
406  }
407  }
408 
409  ItemAdded( wxDataViewItem( new_item->Parent() ), wxDataViewItem( new_item.get() ) );
410 
411  return { new_iter };
412  }
413 
414  void addItems( std::vector<std::unique_ptr<LIST_ITEM>>&& aItems )
415  {
416  if( m_items.empty() )
417  {
418  m_items = std::move( aItems );
419 
420  if( m_parent.m_groupBy->IsChecked()
421  && ( m_parent.m_groupByKind->GetSelection() == 0
422  || m_parent.m_groupByKind->GetSelection() == 1 ) )
423  {
424  // assume that there are fewer group filters than nets.
425  // walk over the list items and assign them to groups. note that the
426  // first items are group items themselves, so start after those.
427  for( unsigned int i = m_parent.m_groupFilter.size(); i < m_items.size(); ++i )
428  {
429  for( unsigned int j = 0; j < m_parent.m_groupFilter.size(); ++j )
430  {
431  if( m_parent.m_groupFilter[j]->Find( m_items[ i ]->GetNetName() ) )
432  {
433  m_items[i]->SetParent( &*m_items[j] );
434  break;
435  }
436  }
437  }
438  }
439  else if( m_parent.m_groupBy->IsChecked()
440  && ( m_parent.m_groupByKind->GetSelection() == 2
441  || m_parent.m_groupByKind->GetSelection() == 3 ) )
442  {
443  // assume that there will be fewer resulting groups than nets.
444  // dynamically generate groups for the actual strings of the match result.
445  // try out each filter on each item and group by the resulting substrings.
446  std::vector<std::unique_ptr<LIST_ITEM>> groups;
447 
448  for( std::unique_ptr<LIST_ITEM>& i : m_items )
449  {
450  for( std::unique_ptr<EDA_PATTERN_MATCH>& f : m_parent.m_groupFilter )
451  {
452  EDA_PATTERN_MATCH::FIND_RESULT match = f->Find( i->GetNetName() );
453 
454  if( match )
455  {
456  wxString match_str = i->GetNetName().substr( match.start, match.length );
457 
458  auto group = std::find_if( groups.begin(), groups.end(),
459  [&]( const std::unique_ptr<LIST_ITEM>& x )
460  {
461  return x->GetNetName() == match_str;
462  } );
463 
464  if( group == groups.end() )
465  {
466  groups.emplace_back( std::make_unique<LIST_ITEM>( groups.size(),
467  match_str ) );
468  group = groups.end() - 1;
469  }
470 
471  i->SetParent( &**group );
472  break;
473  }
474  }
475  }
476 
477  // insert the group items at the front of the items list.
478  for( std::unique_ptr<LIST_ITEM>& g : groups )
479  m_items.emplace_back( std::move( g ) );
480 
481  std::rotate( m_items.begin(), m_items.end() - groups.size(), m_items.end() );
482  }
483 
484  for( std::unique_ptr<LIST_ITEM>& i : m_items )
485  ItemAdded( wxDataViewItem( i->Parent() ), wxDataViewItem( &*i ) );
486  }
487  else
488  {
489  m_items.reserve( m_items.size() + aItems.size() );
490 
491  for( std::unique_ptr<LIST_ITEM>& i : aItems )
492  addItem( std::move( i ) );
493  }
494  }
495 
496  std::unique_ptr<LIST_ITEM> deleteItem( const OPT<LIST_ITEM_ITER>& aRow )
497  {
498  if( !aRow )
499  return {};
500 
501  std::unique_ptr<LIST_ITEM> i = std::move( **aRow );
502 
503  // if the row has a parent, detach it first
504  LIST_ITEM* parent = i->Parent();
505  i->SetParent( nullptr );
506 
507  m_items.erase( *aRow );
508  ItemDeleted( wxDataViewItem( parent ), wxDataViewItem( &*i ) );
509 
510  if( parent )
511  {
512  ItemChanged( wxDataViewItem( parent ) );
513 
514  // for grouping type 2,3 a group item might disappear if it becomes empty.
515  if( ( m_parent.m_groupByKind->GetSelection() == 2
516  || m_parent.m_groupByKind->GetSelection() == 3 )
517  && parent != nullptr && parent->ChildrenCount() == 0 )
518  {
519  auto p = std::find_if( m_items.begin(), m_items.end(),
520  [&]( std::unique_ptr<LIST_ITEM>& x )
521  {
522  return x.get() == parent;
523  } );
524 
525  wxASSERT( p != m_items.end() );
526  m_items.erase( p );
527 
528  ItemDeleted( wxDataViewItem( parent->Parent() ), wxDataViewItem( parent ) );
529  }
530  }
531 
532  Resort();
533  return i;
534  }
535 
537  {
538  BeforeReset();
539  m_items.clear();
540  AfterReset();
541  }
542 
543  void updateItem( const OPT<LIST_ITEM_ITER>& aRow )
544  {
545  if( aRow )
546  {
547  const std::unique_ptr<LIST_ITEM>& listItem = *aRow.get();
548 
549  if( listItem->Parent() )
550  ItemChanged( wxDataViewItem( listItem->Parent() ) );
551 
552  ItemChanged( wxDataViewItem( listItem.get() ) );
553  resortIfChanged( listItem.get() );
554  }
555  }
556 
558  {
559  for( std::unique_ptr<LIST_ITEM>& i : m_items )
560  ItemChanged( wxDataViewItem( i.get() ) );
561  }
562 
563  void resortIfChanged( LIST_ITEM* aItem )
564  {
565  if( wxDataViewColumn* column = m_parent.m_netsList->GetSortingColumn() )
566  {
567  bool changed = false;
568 
569  for( const LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
570  changed |= itemColumnChanged( i, column->GetModelColumn() );
571 
572  for( LIST_ITEM* i = aItem; i != nullptr; i = i->Parent() )
573  i->ResetColumnChangedBits();
574 
575  if( changed )
576  Resort();
577  }
578  }
579 
580  bool itemColumnChanged( const LIST_ITEM* aItem, unsigned int aCol ) const
581  {
582  if( aItem == nullptr || aCol >= columnDesc().size() )
583  return false;
584 
585  if( aCol == COLUMN_PAD_COUNT )
586  return aItem->PadCountChanged();
587 
588  else if( aCol == COLUMN_VIA_COUNT )
589  return aItem->ViaCountChanged();
590 
591  else if( aCol == COLUMN_VIA_LENGTH )
592  return aItem->ViaLengthChanged();
593 
594  else if( aCol == COLUMN_BOARD_LENGTH )
595  return aItem->BoardWireLengthChanged();
596 
597  else if( aCol == COLUMN_CHIP_LENGTH )
598  return aItem->ChipWireLengthChanged();
599 
600  else if( aCol == COLUMN_TOTAL_LENGTH )
601  return aItem->TotalLengthChanged();
602 
603  return false;
604  }
605 
606  // implementation of wxDataViewModel interface
607  // these are used to query the data model by the GUI view implementation.
608  // these are not supposed to be used to modify the data model. for that
609  // use the public functions above.
610 
611 protected:
612  unsigned int GetColumnCount() const override
613  {
614  return columnCount();
615  }
616 
617  void GetValue( wxVariant& aOutValue, const wxDataViewItem& aItem,
618  unsigned int aCol ) const override
619  {
620  if( LIST_ITEM* i = static_cast<LIST_ITEM*>( aItem.GetID() ) )
621  {
622  if( aCol == COLUMN_NET && !i->GetIsGroup() )
623  aOutValue = m_parent.formatNetCode( i->GetNet() );
624 
625  else if( aCol == COLUMN_NET && i->GetIsGroup() )
626  aOutValue = "";
627 
628  else if( aCol == COLUMN_NAME )
629  aOutValue = i->GetNetName();
630 
631  else if( aCol == COLUMN_PAD_COUNT )
632  aOutValue = m_parent.formatCount( i->GetPadCount() );
633 
634  else if( aCol == COLUMN_VIA_COUNT )
635  aOutValue = m_parent.formatCount( i->GetViaCount() );
636 
637  else if( aCol == COLUMN_VIA_LENGTH )
638  aOutValue = m_parent.formatLength( i->GetViaLength() );
639 
640  else if( aCol == COLUMN_BOARD_LENGTH )
641  aOutValue = m_parent.formatLength( i->GetBoardWireLength() );
642 
643  else if( aCol == COLUMN_CHIP_LENGTH )
644  aOutValue = m_parent.formatLength( i->GetChipWireLength() );
645 
646  else if( aCol == COLUMN_TOTAL_LENGTH )
647  aOutValue = m_parent.formatLength( i->GetTotalLength() );
648  }
649  }
650 
651  static int compareUInt( uint64_t aValue1, uint64_t aValue2, bool aAsc )
652  {
653  if( aAsc )
654  return aValue1 < aValue2 ? -1 : 1;
655  else
656  return aValue2 < aValue1 ? -1 : 1;
657  }
658 
659  int Compare( const wxDataViewItem& aItem1, const wxDataViewItem& aItem2,
660  unsigned int aCol, bool aAsc ) const override
661  {
662  const LIST_ITEM& i1 = *static_cast<const LIST_ITEM*>( aItem1.GetID() );
663  const LIST_ITEM& i2 = *static_cast<const LIST_ITEM*>( aItem2.GetID() );
664 
665  if( i1.GetIsGroup() && !i2.GetIsGroup() )
666  return -1;
667 
668  if( i2.GetIsGroup() && !i1.GetIsGroup() )
669  return 1;
670 
671  if( aCol == COLUMN_NET && i1.GetNetCode() != i2.GetNetCode() )
672  {
673  return aAsc ? ( i2.GetNetCode() - i1.GetNetCode() ) : ( i1.GetNetCode() - i2.GetNetCode() );
674  }
675  else if( aCol == COLUMN_NAME )
676  {
677  const wxString& s1 = i1.GetNetName();
678  const wxString& s2 = i2.GetNetName();
679 
680  int res = aAsc ? s1.Cmp( s2 ) : s2.Cmp( s1 );
681 
682  if( res != 0 )
683  return res;
684  }
685 
686  else if( aCol == COLUMN_PAD_COUNT && i1.GetPadCount() != i2.GetPadCount() )
687  return compareUInt( i1.GetPadCount(), i2.GetPadCount(), aAsc );
688 
689  else if( aCol == COLUMN_VIA_COUNT && i1.GetViaCount() != i2.GetViaCount() )
690  return compareUInt( i1.GetViaCount(), i2.GetViaCount(), aAsc );
691 
692  else if( aCol == COLUMN_VIA_LENGTH && i1.GetViaLength() != i2.GetViaLength() )
693  return compareUInt( i1.GetViaLength(), i2.GetViaLength(), aAsc );
694 
695  else if( aCol == COLUMN_BOARD_LENGTH && i1.GetBoardWireLength() != i2.GetBoardWireLength() )
696  return compareUInt( i1.GetBoardWireLength(), i2.GetBoardWireLength(), aAsc );
697 
698  else if( aCol == COLUMN_CHIP_LENGTH && i1.GetChipWireLength() != i2.GetChipWireLength() )
699  return compareUInt( i1.GetChipWireLength(), i2.GetChipWireLength(), aAsc );
700 
701  else if( aCol == COLUMN_TOTAL_LENGTH && i1.GetTotalLength() != i2.GetTotalLength() )
702  return compareUInt( i1.GetTotalLength(), i2.GetTotalLength(), aAsc );
703 
704  // when the item values compare equal resort to pointer comparison.
705  wxUIntPtr id1 = wxPtrToUInt( aItem1.GetID() );
706  wxUIntPtr id2 = wxPtrToUInt( aItem2.GetID() );
707 
708  return aAsc ? id1 - id2 : id2 - id1;
709  }
710 
711  bool SetValue( const wxVariant& aInValue, const wxDataViewItem& aItem,
712  unsigned int aCol ) override
713  {
714  return false;
715  }
716 
717  wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override
718  {
719  if( !aItem.IsOk() )
720  return wxDataViewItem();
721 
722  return wxDataViewItem( static_cast<const LIST_ITEM*>( aItem.GetID() )->Parent() );
723  }
724 
725  bool IsContainer( const wxDataViewItem& aItem ) const override
726  {
727  if( !aItem.IsOk() )
728  return true;
729 
730  return static_cast<const LIST_ITEM*>( aItem.GetID() )->GetIsGroup();
731  }
732 
733  bool HasContainerColumns( const wxDataViewItem& aItem ) const override
734  {
735  return IsContainer( aItem );
736  }
737 
738  unsigned int GetChildren( const wxDataViewItem& aParent,
739  wxDataViewItemArray& aChildren ) const override
740  {
741  const LIST_ITEM* p = static_cast<const LIST_ITEM*>( aParent.GetID() );
742 
743  if( !aParent.IsOk() )
744  {
745  aChildren.Alloc( m_items.size() );
746 
747  for( const std::unique_ptr<LIST_ITEM>& i : m_items )
748  {
749  if( i->Parent() == nullptr )
750  aChildren.Add( wxDataViewItem( &*i ) );
751  }
752 
753  return aChildren.GetCount();
754  }
755  else if( p->GetIsGroup() )
756  {
757  const int count = p->ChildrenCount();
758 
759  if( count == 0 )
760  return 0;
761 
762  aChildren.Alloc( count );
763 
764  for( auto i = p->ChildrenBegin(), end = p->ChildrenEnd(); i != end; ++i )
765  aChildren.Add( wxDataViewItem( *i ) );
766 
767  return aChildren.GetCount();
768  }
769 
770  return 0;
771  }
772 
773  wxString GetColumnType( unsigned int /* aCol */ ) const override
774  {
775  return wxS( "string" );
776  }
777 };
778 
779 
781  const SETTINGS& aSettings ) :
782  DIALOG_NET_INSPECTOR_BASE( aParent ),
783  m_zero_netitem( nullptr ),
784  m_frame( aParent )
785 {
786  m_brd = aParent->GetBoard();
787 
788  m_data_model = new DATA_MODEL( *this );
789  m_netsList->AssociateModel( &*m_data_model );
790 
791  std::array<std::function<void( void )>, 8> add_col = {
792  [&]()
793  {
794  m_netsList->AppendTextColumn( COLUMN_NET.display_name, COLUMN_NET,
795  wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
796  wxDATAVIEW_COL_SORTABLE );
797  },
798  [&]()
799  {
800  m_netsList->AppendTextColumn( COLUMN_NAME.display_name, COLUMN_NAME,
801  wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
802  wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
803  wxDATAVIEW_COL_SORTABLE );
804  },
805  [&]()
806  {
808  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
809  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
810  },
811  [&]()
812  {
814  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
815  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
816  },
817  [&]()
818  {
820  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
821  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
822  },
823  [&]()
824  {
826  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
827  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
828  },
829  [&]()
830  {
832  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
833  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
834  },
835  [&]()
836  {
838  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
839  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
840  }
841  };
842 
843  std::vector<int> col_order = aSettings.column_order;
844 
845  if( col_order.size() != add_col.size() )
846  {
847  col_order.resize( add_col.size() );
848 
849  for( unsigned int i = 0; i < add_col.size(); ++i )
850  col_order[i] = i;
851  }
852 
853  for( unsigned int i : col_order )
854  add_col.at( i )();
855 
856  m_netsList->SetExpanderColumn( m_netsList->GetColumn( 0 ) );
857 
858  // avoid onFilterChange for each of the settings as it will re-build the
859  // list over and over again.
861 
862  m_textCtrlFilter->SetValue( aSettings.filter_string );
863  m_cbShowZeroPad->SetValue( aSettings.show_zero_pad_nets );
864  m_groupBy->SetValue( aSettings.group_by );
865  m_groupByKind->SetSelection( aSettings.group_by_kind );
866  m_groupByText->SetValue( aSettings.group_by_text );
867 
869  buildNetsList();
870 
872 
873  m_addNet->SetBitmap( KiBitmap( small_plus_xpm ) );
874  m_renameNet->SetBitmap( KiBitmap( small_edit_xpm ) );
875  m_deleteNet->SetBitmap( KiBitmap( small_trash_xpm ) );
876 
877  m_sdbSizerOK->SetDefault();
878  m_renameNet->Disable();
879  m_deleteNet->Disable();
880 
881  if( aSettings.sorting_column != -1 )
882  {
883  if( wxDataViewColumn* c = m_netsList->GetColumn( aSettings.sorting_column ) )
884  {
885  c->SetSortOrder( aSettings.sort_order_asc );
886  m_data_model->Resort();
887  }
888  }
889 
891 
892  m_frame->Connect( wxEVT_CLOSE_WINDOW,
893  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onParentWindowClosed ),
894  nullptr, this );
895  m_frame->Connect( UNITS_CHANGED,
896  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
897  nullptr, this );
898  m_frame->Connect( BOARD_CHANGED,
899  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
900  nullptr, this );
901 
902  if( m_brd != nullptr )
903  {
904  // if the dialog is opened while something is highlighted on the board ...
906 
907  m_brd->AddListener( this );
908  }
909 }
910 
911 
913 {
914  // the displayed list elements are going to be deleted before the list view itself.
915  // in some cases it might still do queries on the data model, which would crash
916  // from now on. so just disassociate it.
917  m_netsList->AssociateModel( nullptr );
918 
919  m_frame->Disconnect( wxEVT_CLOSE_WINDOW,
920  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onParentWindowClosed ),
921  nullptr, this );
922  m_frame->Disconnect( UNITS_CHANGED,
923  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
924  nullptr, this );
925  m_frame->Disconnect( BOARD_CHANGED,
926  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
927  nullptr, this );
928 
929  if( m_brd != nullptr )
930  m_brd->RemoveListener( this );
931 
932  m_frame->GetCanvas()->SetFocus();
933 }
934 
935 
937 {
938  std::vector<int> column_order( m_data_model->columnCount() );
939 
940  for( unsigned int i = 0; i < column_order.size(); ++i )
941  column_order[i] = m_netsList->GetColumn( i )->GetModelColumn();
942 
943  wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn();
944 
945  SETTINGS r;
946  r.filter_string = m_textCtrlFilter->GetValue();
947  r.show_zero_pad_nets = m_cbShowZeroPad->IsChecked();
948  r.group_by = m_groupBy->IsChecked();
949  r.group_by_kind = m_groupByKind->GetSelection();
950  r.group_by_text = m_groupByText->GetValue();
951  r.sorting_column = sorting_column ? static_cast<int>( sorting_column->GetModelColumn() ) : -1;
952  r.sort_order_asc = sorting_column ? sorting_column->IsSortOrderAscending() : true;
953  r.column_order = column_order;
954 
955  return r;
956 }
957 
958 
959 void DIALOG_NET_INSPECTOR::onParentWindowClosed( wxCommandEvent& event )
960 {
961  Close();
962  event.Skip();
963 }
964 
965 
966 void DIALOG_NET_INSPECTOR::onUnitsChanged( wxCommandEvent& event )
967 {
968  this->m_units = m_frame->GetUserUnits();
969 
970  m_data_model->updateAllItems();
971 
972  event.Skip();
973 }
974 
975 
976 void DIALOG_NET_INSPECTOR::onBoardChanged( wxCommandEvent& event )
977 {
978  m_brd = m_frame->GetBoard();
979 
980  if( m_brd != nullptr )
981  m_brd->AddListener( this );
982 
983  buildNetsList();
984  m_netsList->Refresh();
985 
986  event.Skip();
987 }
988 
989 
991 {
992  // Note: the filtering is case insensitive.
993 
994  // Never show the unconnected net
995  if( aNet->GetNetCode() <= 0 )
996  return false;
997 
998  // Show unconnected nets only if specifically asked for by filter
999  if( m_netFilter.empty() )
1000  return !aNet->GetNetname().StartsWith( "unconnected-(" );
1001 
1002  wxString net_str = UnescapeString( aNet->GetNetname() ).Upper();
1003 
1004  for( const std::unique_ptr<EDA_PATTERN_MATCH>& f : m_netFilter )
1005  {
1006  if( f->Find( net_str ) )
1007  return true;
1008  }
1009 
1010  return false;
1011 }
1012 
1013 
1015 {
1016  bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const
1017  {
1018  return a->Net() < b->Net();
1019  }
1020 
1021  bool operator()( const CN_ITEM* a, int b ) const
1022  {
1023  return a->Net() < b;
1024  }
1025 
1026  bool operator()( int a, const CN_ITEM* b ) const
1027  {
1028  return a < b->Net();
1029  }
1030 };
1031 
1032 
1034 {
1035  // pre-filter the connectivity items and sort them by netcode.
1036  // this avoids quadratic runtime when building the whole net list and
1037  // calculating the total length for each net.
1038 
1039  const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>()
1040  .set( PCB_TRACE_T )
1041  .set( PCB_VIA_T )
1042  .set( PCB_PAD_T );
1043 
1044  std::vector<CN_ITEM*> cn_items;
1045  cn_items.reserve( 1024 );
1046 
1047  for( CN_ITEM* cn_item : m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
1048  {
1049  if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
1050  cn_items.push_back( cn_item );
1051  }
1052 
1053  std::sort( cn_items.begin(), cn_items.end(), NETCODE_CMP_LESS() );
1054 
1055  return cn_items;
1056 }
1057 
1058 
1060 {
1061  if( !aRow )
1062  return;
1063 
1064  wxDataViewItemArray sel;
1065  m_netsList->GetSelections( sel );
1066 
1067  m_data_model->updateItem( aRow );
1068 
1069  if( !sel.IsEmpty() )
1070  {
1071  m_netsList->SetSelections( sel );
1072  m_netsList->EnsureVisible( sel.Item( 0 ) );
1073  }
1074 }
1075 
1076 
1078 {
1079  return wxString::Format( "%.3d", aNet->GetNetCode() );
1080 }
1081 
1082 
1084 {
1085  return UnescapeString( aNet->GetNetname() );
1086 }
1087 
1088 
1089 wxString DIALOG_NET_INSPECTOR::formatCount( unsigned int aValue ) const
1090 {
1091  return wxString::Format( "%u", aValue );
1092 }
1093 
1094 
1095 wxString DIALOG_NET_INSPECTOR::formatLength( int64_t aValue ) const
1096 {
1097  return MessageTextFromValue( GetUserUnits(), static_cast<long long int>( aValue ) );
1098 }
1099 
1100 
1102 {
1103  if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1104  {
1105  // a new net has been added to the board. add it to our list if it
1106  // passes the netname filter test.
1107 
1108  if( netFilterMatches( net ) )
1109  {
1110  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( net );
1111 
1112  // the new net could have some pads already assigned, count them.
1113  new_item->SetPadCount( m_brd->GetNodesCount( net->GetNetCode() ) );
1114 
1115  m_data_model->addItem( std::move( new_item ) );
1116  }
1117  }
1118  else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1119  {
1120  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1121 
1122  if( r )
1123  {
1124  // try to handle frequent operations quickly.
1125  if( TRACK* track = dynamic_cast<TRACK*>( i ) )
1126  {
1127  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1128  int len = track->GetLength();
1129 
1130  list_item->AddBoardWireLength( len );
1131 
1132  if( track->Type() == PCB_VIA_T )
1133  {
1134  list_item->AddViaCount( 1 );
1135  list_item->AddViaLength( calculateViaLength( track ) );
1136  }
1137 
1139  return;
1140  }
1141  }
1142 
1143  // resort to generic slower net update otherwise.
1144  updateNet( i->GetNet() );
1145  }
1146  else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1147  {
1148  for( const PAD* pad : footprint->Pads() )
1149  {
1150  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1151 
1152  if( !r )
1153  {
1154  // if show-zero-pads is off, we might not have this net
1155  // in our list yet, so add it first.
1156  // notice that at this point we are very certain that this net
1157  // will have at least one pad.
1158 
1159  if( netFilterMatches( pad->GetNet() ) )
1160  r = m_data_model->addItem( std::make_unique<LIST_ITEM>( pad->GetNet() ) );
1161  }
1162 
1163  if( r )
1164  {
1165  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1166  int len = pad->GetPadToDieLength();
1167 
1168  list_item->AddPadCount( 1 );
1169  list_item->AddChipWireLength( len );
1170 
1171  if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1172  m_data_model->deleteItem( r );
1173  else
1175  }
1176  }
1177  }
1178 }
1179 
1180 
1181 void DIALOG_NET_INSPECTOR::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItem )
1182 {
1183  for( BOARD_ITEM* item : aBoardItem )
1184  {
1185  OnBoardItemAdded( aBoard, item );
1186  }
1187 }
1188 
1189 
1191 {
1192  if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1193  {
1194  m_data_model->deleteItem( m_data_model->findItem( net ) );
1195  }
1196  else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1197  {
1198  for( const PAD* pad : footprint->Pads() )
1199  {
1200  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1201 
1202  if( r )
1203  {
1204  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1205  int len = pad->GetPadToDieLength();
1206 
1207  list_item->SubPadCount( 1 );
1208  list_item->SubChipWireLength( len );
1209 
1210  if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1211  m_data_model->deleteItem( r );
1212  else
1214  }
1215  }
1216  }
1217  else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1218  {
1219  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1220 
1221  if( r )
1222  {
1223  // try to handle frequent operations quickly.
1224  if( TRACK* track = dynamic_cast<TRACK*>( i ) )
1225  {
1226  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1227  int len = track->GetLength();
1228 
1229  list_item->SubBoardWireLength( len );
1230 
1231  if( track->Type() == PCB_VIA_T )
1232  {
1233  list_item->SubViaCount( 1 );
1234  list_item->SubViaLength( calculateViaLength( track ) );
1235  }
1236 
1238  return;
1239  }
1240 
1241  // resort to generic slower net update otherwise.
1242  updateNet( i->GetNet() );
1243  }
1244  }
1245 }
1246 
1247 
1249  std::vector<BOARD_ITEM*>& aBoardItems )
1250 {
1251  for( BOARD_ITEM* item : aBoardItems )
1252  {
1253  OnBoardItemRemoved( aBoard, item );
1254  }
1255 }
1256 
1257 
1259 {
1260  if( dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) != nullptr
1261  || dynamic_cast<FOOTPRINT*>( aBoardItem ) != nullptr )
1262  {
1263  buildNetsList();
1264  m_netsList->Refresh();
1265  }
1266 }
1267 
1268 
1270  std::vector<BOARD_ITEM*>& aBoardItems )
1271 {
1272  buildNetsList();
1273  m_netsList->Refresh();
1274 }
1275 
1276 
1278 {
1279  if( !m_brd->IsHighLightNetON() )
1280  {
1281  m_netsList->UnselectAll();
1282  }
1283  else
1284  {
1285  const std::set<int>& selected_codes = m_brd->GetHighLightNetCodes();
1286 
1287  wxDataViewItemArray new_selection;
1288  new_selection.Alloc( selected_codes.size() );
1289 
1290  for( int code : selected_codes )
1291  {
1292  if( OPT<LIST_ITEM_ITER> r = m_data_model->findItem( code ) )
1293  new_selection.Add( wxDataViewItem( &***r ) );
1294  }
1295 
1296  m_netsList->SetSelections( new_selection );
1297 
1298  if( !new_selection.IsEmpty() )
1299  m_netsList->EnsureVisible( new_selection.Item( 0 ) );
1300  }
1301 }
1302 
1303 
1305 {
1306  buildNetsList();
1307  m_netsList->Refresh();
1308 }
1309 
1310 
1312 {
1313  // something for the specified net has changed, update that row.
1314  // ignore nets that are not in our list because the filter doesn't match.
1315 
1316  if( !netFilterMatches( aNet ) )
1317  {
1318  m_data_model->deleteItem( m_data_model->findItem( aNet ) );
1319  return;
1320  }
1321 
1322  // if the net had no pads before, it might not be in the displayed list yet.
1323  // if it had pads and now doesn't anymore, we might need to remove it from the list.
1324 
1325  OPT<LIST_ITEM_ITER> cur_net_row = m_data_model->findItem( aNet );
1326 
1327  const unsigned int node_count = m_brd->GetNodesCount( aNet->GetNetCode() );
1328 
1329  if( node_count == 0 && !m_cbShowZeroPad->IsChecked() )
1330  {
1331  m_data_model->deleteItem( cur_net_row );
1332  return;
1333  }
1334 
1335  std::unique_ptr<LIST_ITEM> new_list_item = buildNewItem( aNet, node_count,
1337 
1338  if( !cur_net_row )
1339  {
1340  m_data_model->addItem( std::move( new_list_item ) );
1341  return;
1342  }
1343 
1344  const std::unique_ptr<LIST_ITEM>& cur_list_item = *cur_net_row.get();
1345 
1346  if( cur_list_item->GetNetName() != new_list_item->GetNetName() )
1347  {
1348  // if the name has changed, it might require re-grouping.
1349  // it's easier to remove and re-insert it
1350  m_data_model->deleteItem( cur_net_row );
1351  m_data_model->addItem( std::move( new_list_item ) );
1352  }
1353  else
1354  {
1355  // update fields only
1356  cur_list_item->SetPadCount( new_list_item->GetPadCount() );
1357  cur_list_item->SetViaCount( new_list_item->GetViaCount() );
1358  cur_list_item->SetBoardWireLength( new_list_item->GetBoardWireLength() );
1359  cur_list_item->SetChipWireLength( new_list_item->GetChipWireLength() );
1360 
1361  updateDisplayedRowValues( cur_net_row );
1362  }
1363 }
1364 
1365 
1366 unsigned int DIALOG_NET_INSPECTOR::calculateViaLength( const TRACK* aTrack ) const
1367 {
1368  const VIA& via = dynamic_cast<const VIA&>( *aTrack );
1370 
1371  // calculate the via length individually from the board stackup and via's start and end layer.
1372  if( bds.m_HasStackup )
1373  {
1374  const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
1375 
1376  std::pair<PCB_LAYER_ID, int> layer_dist[2] = { std::make_pair( via.TopLayer(), 0 ),
1377  std::make_pair( via.BottomLayer(), 0 ) };
1378 
1379  for( const BOARD_STACKUP_ITEM* i : stackup.GetList() )
1380  {
1381  for( std::pair<PCB_LAYER_ID, int>& j : layer_dist )
1382  {
1383  if( j.first != UNDEFINED_LAYER )
1384  j.second += i->GetThickness();
1385 
1386  if( j.first == i->GetBrdLayerId() )
1387  j.first = UNDEFINED_LAYER;
1388  }
1389  }
1390 
1391  return std::abs( layer_dist[0].second - layer_dist[1].second );
1392  }
1393  else
1394  {
1395  int dielectricLayers = bds.GetCopperLayerCount() - 1;
1396  int layerThickness = bds.GetBoardThickness() / dielectricLayers;
1397  int effectiveBottomLayer;
1398 
1399  if( via.BottomLayer() == B_Cu )
1400  effectiveBottomLayer = F_Cu + dielectricLayers;
1401  else
1402  effectiveBottomLayer = via.BottomLayer();
1403 
1404  int layerCount = effectiveBottomLayer - via.TopLayer();
1405 
1406  return layerCount * layerThickness;
1407  }
1408 }
1409 
1410 
1411 std::unique_ptr<DIALOG_NET_INSPECTOR::LIST_ITEM>
1412 DIALOG_NET_INSPECTOR::buildNewItem( NETINFO_ITEM* aNet, unsigned int aPadCount,
1413  const std::vector<CN_ITEM*>& aCNItems )
1414 {
1415  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( aNet );
1416 
1417  new_item->SetPadCount( aPadCount );
1418 
1419  const auto cn_items = std::equal_range( aCNItems.begin(), aCNItems.end(), aNet->GetNetCode(),
1420  NETCODE_CMP_LESS() );
1421 
1422  for( auto i = cn_items.first; i != cn_items.second; ++i )
1423  {
1424  BOARD_CONNECTED_ITEM* item = ( *i )->Parent();
1425 
1426  if( item->Type() == PCB_PAD_T )
1427  new_item->AddChipWireLength( static_cast<PAD*>( item )->GetPadToDieLength() );
1428 
1429  else if( TRACK* track = dynamic_cast<TRACK*>( item ) )
1430  {
1431  new_item->AddBoardWireLength( track->GetLength() );
1432 
1433  if( item->Type() == PCB_VIA_T )
1434  {
1435  new_item->AddViaCount( 1 );
1436  new_item->AddViaLength( calculateViaLength( track ) );
1437  }
1438  }
1439  }
1440 
1441  return new_item;
1442 }
1443 
1444 
1446 {
1447  // Only build the list of nets if there is a board present
1448  if( !m_brd )
1449  return;
1450 
1451  m_in_build_nets_list = true;
1452 
1453  // when rebuilding the netlist, try to keep the row selection
1454  // FIXME: handle group selections, preserve expanded/collapsed group states
1455  wxDataViewItemArray sel;
1456  m_netsList->GetSelections( sel );
1457 
1458  std::vector<int> prev_selected_netcodes;
1459  prev_selected_netcodes.reserve( sel.GetCount() );
1460 
1461  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1462  {
1463  const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1464  prev_selected_netcodes.push_back( item->GetNetCode() );
1465  }
1466 
1467  m_data_model->deleteAllItems();
1468 
1469  std::vector<std::unique_ptr<LIST_ITEM>> new_items;
1470 
1471  // for group mode 0,1 each group filter string represents one displayed group,
1472  // so just add them first. for group mode 2,3 the groups are generated dynamically.
1473  if( m_groupBy->IsChecked()
1474  && ( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 1 ) )
1475  {
1476  for( unsigned int i = 0; i < m_groupFilter.size(); ++i )
1477  {
1478  const std::unique_ptr<EDA_PATTERN_MATCH>& filter = m_groupFilter[i];
1479  new_items.emplace_back( std::make_unique<LIST_ITEM>( i, filter->GetPattern() ) );
1480  }
1481  }
1482 
1483  std::vector<CN_ITEM*> prefiltered_cn_items = relevantConnectivityItems();
1484 
1485 
1486  // collect all nets which pass the filter string and also remember the
1487  // suffix after the filter match, if any.
1488  struct NET_INFO
1489  {
1490  int netcode;
1491  NETINFO_ITEM* net;
1492  unsigned int pad_count;
1493  };
1494 
1495  struct NET_INFO_CMP_LESS
1496  {
1497  bool operator()( const NET_INFO& a, const NET_INFO& b ) const
1498  {
1499  return a.netcode < b.netcode;
1500  }
1501  bool operator()( const NET_INFO& a, int b ) const
1502  {
1503  return a.netcode < b;
1504  }
1505  bool operator()( int a, const NET_INFO& b ) const
1506  {
1507  return a < b.netcode;
1508  }
1509  };
1510 
1511  std::vector<NET_INFO> nets;
1512  nets.reserve( m_brd->GetNetInfo().NetsByNetcode().size() );
1513 
1514  for( const std::pair<int, NETINFO_ITEM*> ni : m_brd->GetNetInfo().NetsByNetcode() )
1515  {
1516  if( ni.first == 0 )
1517  m_zero_netitem = ni.second;
1518 
1519  if( netFilterMatches( ni.second ) )
1520  nets.emplace_back( NET_INFO{ ni.first, ni.second, 0 } );
1521  }
1522 
1523  // count the pads for each net. since the nets are sorted by netcode
1524  // iterating over the footprints' pads is faster.
1525 
1526  for( FOOTPRINT* footprint : m_brd->Footprints() )
1527  {
1528  for( PAD* pad : footprint->Pads() )
1529  {
1530  auto i = std::lower_bound( nets.begin(), nets.end(), pad->GetNetCode(),
1531  NET_INFO_CMP_LESS() );
1532 
1533  if( i != nets.end() && i->netcode == pad->GetNetCode() )
1534  i->pad_count += 1;
1535  }
1536  }
1537 
1538  for( NET_INFO& ni : nets )
1539  {
1540  if( m_cbShowZeroPad->IsChecked() || ni.pad_count > 0 )
1541  new_items.emplace_back( buildNewItem( ni.net, ni.pad_count, prefiltered_cn_items ) );
1542  }
1543 
1544 
1545  m_data_model->addItems( std::move( new_items ) );
1546 
1547  // try to restore the selected rows. set the ones that we can't find anymore to -1.
1548  sel.Clear();
1549 
1550  for( int& nc : prev_selected_netcodes )
1551  {
1552  auto r = m_data_model->findItem( nc );
1553 
1554  if( r )
1555  {
1556  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1557  sel.Add( wxDataViewItem( list_item.get() ) );
1558  }
1559  else
1560  {
1561  nc = -1;
1562  }
1563  }
1564 
1565  if( !sel.IsEmpty() )
1566  {
1567  m_netsList->SetSelections( sel );
1568  m_netsList->EnsureVisible( sel.Item( 0 ) );
1569  }
1570  else
1571  {
1572  m_netsList->UnselectAll();
1573  }
1574 
1575  prev_selected_netcodes.erase( std::remove( prev_selected_netcodes.begin(),
1576  prev_selected_netcodes.end(), -1 ),
1577  prev_selected_netcodes.end() );
1578 
1580 
1581  for( int& i : prev_selected_netcodes )
1582  m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( true, i, true );
1583 
1585  m_frame->GetCanvas()->Refresh();
1586 
1587  m_in_build_nets_list = false;
1588 }
1589 
1590 
1591 void DIALOG_NET_INSPECTOR::onFilterChange( wxCommandEvent& aEvent )
1592 {
1593  wxStringTokenizer filters( m_textCtrlFilter->GetValue().Upper(), "," );
1594  m_netFilter.clear();
1595 
1596  while( filters.HasMoreTokens() )
1597  {
1598  wxString t = filters.GetNextToken();
1599  t.Trim( false );
1600  t.Trim( true );
1601 
1602  if( !t.IsEmpty() )
1603  {
1604  m_netFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1605  m_netFilter.back()->SetPattern( t );
1606  }
1607  }
1608 
1609  wxStringTokenizer group_filters( m_groupByText->GetValue(), "," );
1610  m_groupFilter.clear();
1611 
1612  while( group_filters.HasMoreTokens() )
1613  {
1614  wxString t = group_filters.GetNextToken();
1615  t.Trim( false );
1616  t.Trim( true );
1617 
1618  if( !t.IsEmpty() )
1619  {
1620  if( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 2 )
1621  {
1622  // type 2: wildcard match, use the matching substring as a group key.
1623  // the number of groups is determined dynamically by the number of
1624  // resulting matches in the whole set.
1625  m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1626  m_groupFilter.back()->SetPattern( t );
1627  }
1628  else if( m_groupByKind->GetSelection() == 1 || m_groupByKind->GetSelection() == 3 )
1629  {
1630  // type 3: regex match, use the matching substring as a group key.
1631  // the number of groups is determined dynamically by the number of
1632  // resulting matches in the whole set.
1633  m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
1634  m_groupFilter.back()->SetPattern( t );
1635  }
1636  }
1637  }
1638 
1640  buildNetsList();
1641 }
1642 
1643 
1644 void DIALOG_NET_INSPECTOR::onSelChanged( wxDataViewEvent& )
1645 {
1646  onSelChanged();
1647 }
1648 
1649 
1651 {
1652  // ignore selection changes while the whole list is being rebuilt.
1653  if( m_in_build_nets_list )
1654  return;
1655 
1657 
1658  bool enable_rename_button = false;
1659  bool enable_delete_button = false;
1660 
1661  if( m_netsList->HasSelection() )
1662  {
1663  wxDataViewItemArray sel;
1664  m_netsList->GetSelections( sel );
1665 
1666  ps->SetHighlight( false );
1667 
1668  enable_rename_button = sel.GetCount() == 1;
1669  enable_delete_button = true;
1670 
1671  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1672  {
1673  const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1674 
1675  if( ii->GetIsGroup() )
1676  {
1677  enable_rename_button = false;
1678 
1679  for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
1680  ps->SetHighlight( true, ( *c )->GetNetCode(), true );
1681  }
1682  else
1683  ps->SetHighlight( true, ii->GetNetCode(), true );
1684  }
1685  }
1686  else
1687  ps->SetHighlight( false );
1688 
1690  m_frame->GetCanvas()->Refresh();
1691 
1692  m_renameNet->Enable( enable_rename_button );
1693  m_deleteNet->Enable( enable_delete_button );
1694 }
1695 
1696 
1697 void DIALOG_NET_INSPECTOR::onSortingChanged( wxDataViewEvent& aEvent )
1698 {
1699  // FIXME: Whenever the sort criteria changes (sorting column)
1700  // the visible row-numers of the selection get preserved, not the actual
1701  // elements. Don't know at the moment how to preserve the selection,
1702  // so just clear it for now.
1703 
1704  m_netsList->UnselectAll();
1705 
1708  m_frame->GetCanvas()->Refresh();
1709 }
1710 
1711 
1713 {
1720  wxClientDC dc( GetParent() );
1721 
1722  int h, minw, minw_col0, minw_col1;
1723  int w0, w1, w2, w3, w4, w5, w6, w7;
1724 
1725  dc.GetTextExtent( COLUMN_NET.display_name, &w0, &h );
1726  dc.GetTextExtent( COLUMN_NAME.display_name, &minw_col1, &h );
1727  dc.GetTextExtent( COLUMN_PAD_COUNT.display_name, &w2, &h );
1728  dc.GetTextExtent( COLUMN_VIA_COUNT.display_name, &w3, &h );
1729  dc.GetTextExtent( COLUMN_VIA_LENGTH.display_name, &w4, &h );
1730  dc.GetTextExtent( COLUMN_BOARD_LENGTH.display_name, &w5, &h );
1731  dc.GetTextExtent( COLUMN_CHIP_LENGTH.display_name, &w6, &h );
1732  dc.GetTextExtent( COLUMN_TOTAL_LENGTH.display_name, &w7, &h );
1733  dc.GetTextExtent( "00000,000 mm", &minw, &h );
1734  dc.GetTextExtent( "00000", &minw_col0, &h );
1735 
1736  // Considering left and right margins.
1737  // For wxRenderGeneric it is 5px.
1738  // Also account for the sorting arrow in the column header.
1739  // Column 0 also needs space for any potential expander icons.
1740  const int extra_width = 30;
1741 
1742  w0 = std::max( w0, minw_col0 ) + extra_width;
1743  minw_col1 = minw_col1 + extra_width;
1744  w2 = w2 + extra_width;
1745  w3 = w3 + extra_width;
1746  w4 = std::max( w4 + extra_width, minw );
1747  w5 = std::max( w5 + extra_width, minw );
1748  w6 = std::max( w6 + extra_width, minw );
1749  w7 = std::max( w7 + extra_width, minw );
1750 
1751  // the columns might have been reordered. we work on the column model numbers though.
1752  std::vector<int> column_order( m_data_model->columnCount() );
1753 
1754  for( unsigned int i = 0; i < column_order.size(); ++i )
1755  column_order[m_netsList->GetColumn( i )->GetModelColumn()] = i;
1756 
1757  assert( column_order.size() == 8 );
1758 
1759  m_netsList->GetColumn( column_order[0] )->SetWidth( w0 );
1760  m_netsList->GetColumn( column_order[1] )->SetMinWidth( minw_col1 );
1761  m_netsList->GetColumn( column_order[2] )->SetWidth( w2 );
1762  m_netsList->GetColumn( column_order[3] )->SetWidth( w3 );
1763  m_netsList->GetColumn( column_order[4] )->SetWidth( w4 );
1764  m_netsList->GetColumn( column_order[5] )->SetWidth( w5 );
1765  m_netsList->GetColumn( column_order[6] )->SetWidth( w6 );
1766  m_netsList->GetColumn( column_order[7] )->SetWidth( w7 );
1767 
1768  // At resizing of the list the width of middle column (Net Names) changes only.
1769  int width = m_netsList->GetClientSize().x - 24;
1770  w1 = width - w0 - w2 - w3 - w4 - w5 - w6 - w7;
1771 
1772  // Column 1 (net names) need a minimal width to display net names
1773  dc.GetTextExtent( "MMMMMMMMMMMMMMMM", &minw, &h );
1774  w1 = std::max( w1, minw );
1775 
1776  m_netsList->GetColumn( column_order[1] )->SetWidth( w1 );
1777 
1778  m_netsList->Refresh();
1779 }
1780 
1781 
1782 void DIALOG_NET_INSPECTOR::onListSize( wxSizeEvent& aEvent )
1783 {
1784  aEvent.Skip();
1786 }
1787 
1788 
1789 void DIALOG_NET_INSPECTOR::onAddNet( wxCommandEvent& aEvent )
1790 {
1791  wxString newNetName;
1792  NETNAME_VALIDATOR validator( &newNetName );
1793 
1794  WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
1795  dlg.SetTextValidator( validator );
1796 
1797  while( true )
1798  {
1799  if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1800  return; //Aborted by user
1801 
1802  newNetName = dlg.GetValue();
1803 
1804  if( m_brd->FindNet( newNetName ) )
1805  {
1806  DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
1807  newNetName ) );
1808  newNetName = wxEmptyString;
1809  }
1810  else
1811  {
1812  break;
1813  }
1814  }
1815 
1816  NETINFO_ITEM *newnet = new NETINFO_ITEM( m_brd, dlg.GetValue(), 0 );
1817 
1818  m_brd->Add( newnet );
1819  m_frame->OnModify();
1820 
1821  // We'll get an OnBoardItemAdded callback from this to update our listbox
1822 }
1823 
1824 
1825 void DIALOG_NET_INSPECTOR::onRenameNet( wxCommandEvent& aEvent )
1826 {
1827  if( m_netsList->GetSelectedItemsCount() == 1 )
1828  {
1829  const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1830 
1831  if( sel->GetIsGroup() )
1832  return;
1833 
1834  NETINFO_ITEM* net = sel->GetNet();
1835  wxString fullNetName = net->GetNetname();
1836  wxString netPath;
1837  wxString shortNetName;
1838 
1839  if( fullNetName.Contains( "/" ) )
1840  {
1841  netPath = fullNetName.BeforeLast( '/' ) + '/';
1842  shortNetName = fullNetName.AfterLast( '/' );
1843  }
1844  else
1845  {
1846  shortNetName = fullNetName;
1847  }
1848 
1849  wxString unescapedShortName = UnescapeString( shortNetName );
1850 
1851  WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
1852  NETNAME_VALIDATOR validator( &unescapedShortName );
1853  dlg.SetTextValidator( validator );
1854 
1855  while( true )
1856  {
1857  if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
1858  return;
1859 
1860  unescapedShortName = dlg.GetValue();
1861 
1862  if( unescapedShortName.IsEmpty() )
1863  {
1864  DisplayError( this, wxString::Format( _( "Net name cannot be empty." ),
1865  unescapedShortName ) );
1866  continue;
1867  }
1868 
1869  shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
1870  fullNetName = netPath + shortNetName;
1871 
1872  if( m_brd->FindNet( shortNetName ) || m_brd->FindNet( fullNetName ) )
1873  {
1874  DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
1875  unescapedShortName ) );
1876  unescapedShortName = wxEmptyString;
1877  }
1878  else
1879  {
1880  break;
1881  }
1882  }
1883 
1884  // the changed name might require re-grouping. remove and re-insert
1885  // is easier.
1886  auto removed_item = m_data_model->deleteItem( m_data_model->findItem( net ) );
1887 
1888  net->SetNetname( fullNetName );
1889  m_frame->OnModify();
1890 
1891  if( netFilterMatches( net ) )
1892  {
1893  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( net );
1894  new_item->SetPadCount( removed_item->GetPadCount() );
1895  new_item->SetViaCount( removed_item->GetViaCount() );
1896  new_item->SetBoardWireLength( removed_item->GetBoardWireLength() );
1897  new_item->SetChipWireLength( removed_item->GetChipWireLength() );
1898 
1899  OPT<LIST_ITEM_ITER> added_row = m_data_model->addItem( std::move( new_item ) );
1900 
1901  wxDataViewItemArray new_sel;
1902  new_sel.Add( wxDataViewItem( &***added_row ) );
1903  m_netsList->SetSelections( new_sel );
1904  onSelChanged();
1905  }
1906 
1907  // Currently only tracks and pads have netname annotations and need to be redrawn,
1908  // but zones are likely to follow. Since we don't have a way to ask what is current,
1909  // just refresh all items.
1911  m_frame->GetCanvas()->Refresh();
1912  }
1913 }
1914 
1915 
1916 void DIALOG_NET_INSPECTOR::onDeleteNet( wxCommandEvent& aEvent )
1917 {
1918  if( !m_netsList->HasSelection() )
1919  return;
1920 
1921  wxDataViewItemArray sel;
1922  m_netsList->GetSelections( sel );
1923 
1924  auto delete_one =
1925  [this]( const LIST_ITEM* i )
1926  {
1927  if( i->GetPadCount() == 0
1928  || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ),
1929  i->GetNetName() ) ) )
1930  {
1931  m_brd->Remove( i->GetNet() );
1932  m_frame->OnModify();
1933 
1934  // We'll get an OnBoardItemRemoved callback from this to update our listbox
1935  }
1936  };
1937 
1938  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1939  {
1940  const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1941 
1942  if( ii->GetIsGroup() )
1943  {
1944  if( ii->ChildrenCount() != 0
1945  && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ),
1946  ii->GetGroupName() ) ) )
1947  {
1948  // we can't be iterating the children container and deleting items from
1949  // it at the same time. thus take a copy of it first.
1950  std::vector<const LIST_ITEM*> children;
1951  children.reserve( ii->ChildrenCount() );
1952  std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
1953 
1954  for( const LIST_ITEM* c : children )
1955  delete_one( c );
1956  }
1957  }
1958  else
1959  {
1960  delete_one( ii );
1961  }
1962  }
1963 }
1964 
1965 
1966 void DIALOG_NET_INSPECTOR::onReport( wxCommandEvent& aEvent )
1967 {
1968  wxFileDialog dlg( this, _( "Report file" ), "", "",
1969  _( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
1970  wxFD_SAVE );
1971 
1972  if( dlg.ShowModal() == wxID_CANCEL )
1973  return;
1974 
1975  wxTextFile f( dlg.GetPath() );
1976 
1977  f.Create();
1978 
1979  wxString txt;
1980 
1981  // Print Header:
1982  for( auto&& col : m_data_model->columnDesc() )
1983  txt += '"' + col.csv_name + "\";";
1984 
1985  f.AddLine( txt );
1986 
1987  // Print list of nets:
1988  const unsigned int num_rows = m_data_model->itemCount();
1989 
1990  for( unsigned int row = 0; row < num_rows; row++ )
1991  {
1992  auto& i = m_data_model->itemAt( row );
1993 
1994  if( i.GetIsGroup() || i.GetNetCode() == 0 )
1995  continue;
1996 
1997  txt = "";
1998 
1999  for( auto&& col : m_data_model->columnDesc() )
2000  {
2001  if( col.csv_flags & COLUMN_DESC::CSV_QUOTE )
2002  txt += '"' + m_data_model->valueAt( col.num, row ).GetString() + "\";";
2003  else
2004  txt += m_data_model->valueAt( col.num, row ).GetString() + ';';
2005  }
2006 
2007  f.AddLine( txt );
2008  }
2009 
2010  f.Write();
2011  f.Close();
2012 }
void DisplayError(wxWindow *aParent, const wxString &aText, int aDisplayTime)
Display an error or warning message box with aMessage.
Definition: confirm.cpp:252
static const COLUMN_DESC COLUMN_CHIP_LENGTH
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:1953
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1268
wxString GetValue() const
std::vector< CN_ITEM * > relevantConnectivityItems() const
void OnModify() override
Must be called after a board change to set the modified flag.
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:125
Definition: track.h:343
OPT< LIST_ITEM_ITER > addItem(std::unique_ptr< LIST_ITEM > aItem)
virtual void OnBoardItemRemoved(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void onRenameNet(wxCommandEvent &event) override
wxVariant valueAt(unsigned int aCol, unsigned int aRow) const
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
this class manage the layers needed to make a physical board they are solder mask,...
virtual void OnBoardNetSettingsChanged(BOARD &aBoard) override
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
virtual void OnBoardHighlightNetChanged(BOARD &aBoard) override
const NETCODES_MAP & NetsByNetcode() const
Return the netcode map, at least for python.
Definition: netinfo.h:361
void onBoardChanged(wxCommandEvent &event)
bool IsHighLightNetON() const
Definition: board.h:469
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
bool operator()(int a, const CN_ITEM *b) const
A KICAD version of wxTextEntryDialog which supports the various improvments/work-arounds from DIALOG_...
void SetTextValidator(wxTextValidatorStyle style)
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition: view.cpp:1412
static const COLUMN_DESC COLUMN_NET
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:591
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:752
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_netFilter
class PAD, a pad in a footprint
Definition: typeinfo.h:89
wxString formatCount(unsigned int aValue) const
static const COLUMN_DESC COLUMN_TOTAL_LENGTH
virtual void OnBoardItemsChanged(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
void onDeleteNet(wxCommandEvent &event) override
bool SetValue(const wxVariant &aInValue, const wxDataViewItem &aItem, unsigned int aCol) override
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
static const COLUMN_DESC COLUMN_PAD_COUNT
int Net() const
allow parallel connection threads
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:207
class TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
Abstract pattern-matching tool and implementations.
void onParentWindowClosed(wxCommandEvent &event)
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:120
virtual void OnBoardItemChanged(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
void onSortingChanged(wxDataViewEvent &event) override
void onReport(wxCommandEvent &event) override
wxString GetColumnType(unsigned int) const override
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
unsigned GetNodesCount(int aNet=-1) const
Definition: board.cpp:1006
#define def_col(c, num, name, csv_name, csv_flags)
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:563
OPT< LIST_ITEM_ITER > findItem(int aNetCode)
BOARD_STACKUP & GetStackupDescriptor()
unsigned int calculateViaLength(const TRACK *) const
Item needs to be redrawn.
Definition: view_item.h:57
bool netFilterMatches(NETINFO_ITEM *aNet) const
bool HasContainerColumns(const wxDataViewItem &aItem) const override
gen(m_pad_count, 0, GetPadCount, SetPadCount, AddPadCount, SubPadCount, PadCountChanged)
bool IsContainer(const wxDataViewItem &aItem) const override
void RemoveListener(BOARD_LISTENER *aListener)
Remove the specified listener.
Definition: board.cpp:1960
void updateNet(NETINFO_ITEM *aNet)
bool itemColumnChanged(const LIST_ITEM *aItem, unsigned int aCol) const
void updateDisplayedRowValues(const OPT< LIST_ITEM_ITER > &aRow)
FOOTPRINTS & Footprints()
Definition: board.h:303
static const COLUMN_DESC COLUMN_NAME
void updateItem(const OPT< LIST_ITEM_ITER > &aRow)
static const COLUMN_DESC COLUMN_BOARD_LENGTH
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:414
Definition of file extensions used in Kicad.
const wxString & GetNetname() const
Definition: netinfo.h:119
unsigned int GetChildren(const wxDataViewItem &aParent, wxDataViewItemArray &aChildren) const override
virtual void OnBoardItemsAdded(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
virtual void SetFocus() override
wxString formatLength(int64_t aValue) const
void onUnitsChanged(wxCommandEvent &event)
unsigned long long int GetTotalLength() const
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
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:130
this class manage one layer needed to make a physical board it can be a solder mask,...
void onListSize(wxSizeEvent &event) override
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
const BITMAP_OPAQUE small_trash_xpm[1]
Definition: small_trash.cpp:23
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=NULL) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
const std::set< int > & GetHighLightNetCodes() const
Definition: board.h:454
bool operator()(const CN_ITEM *a, const CN_ITEM *b) const
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
void GetValue(wxVariant &aOutValue, const wxDataViewItem &aItem, unsigned int aCol) const override
void addItems(std::vector< std::unique_ptr< LIST_ITEM >> &&aItems)
EDA_UNITS m_units
Definition: dialog_shim.h:199
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_groupFilter
const BITMAP_OPAQUE small_edit_xpm[1]
Definition: small_edit.cpp:22
Handle the data for a net.
Definition: netinfo.h:64
std::unique_ptr< LIST_ITEM > buildNewItem(NETINFO_ITEM *aNet, unsigned int aPadCount, const std::vector< CN_ITEM * > &aCNItems)
void SetHighlight(bool aEnabled, int aNetcode=-1, bool aMulti=false)
Turns on/off highlighting.
std::unique_ptr< LIST_ITEM > deleteItem(const OPT< LIST_ITEM_ITER > &aRow)
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:150
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:77
int Compare(const wxDataViewItem &aItem1, const wxDataViewItem &aItem2, unsigned int aCol, bool aAsc) const override
unsigned int GetColumnCount() const override
LIST_ITEM & operator=(const LIST_ITEM &)=delete
DIALOG_NET_INSPECTOR(PCB_EDIT_FRAME *aParent, const SETTINGS &aSettings)
DATA_MODEL(DIALOG_NET_INSPECTOR &parent)
The main frame for Pcbnew.
const LIST_ITEM & itemAt(unsigned int aRow) const
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:665
PCB_LAYER_ID TopLayer() const
Definition: track.cpp:457
OPT< LIST_ITEM_ITER > findItem(NETINFO_ITEM *aNet)
boost::optional< T > OPT
Definition: optional.h:7
void onAddNet(wxCommandEvent &event) override
LIST_ITEM(unsigned int aGroupNumber, const wxString &aGroupName)
static const COLUMN_DESC COLUMN_VIA_LENGTH
Class DIALOG_NET_INSPECTOR_BASE.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
virtual void OnBoardItemsRemoved(BOARD &aBoard, std::vector< BOARD_ITEM * > &aBoardItems) override
BOARD * GetBoard() const
std::vector< std::unique_ptr< LIST_ITEM > > m_items
virtual void OnBoardItemAdded(BOARD &aBoard, BOARD_ITEM *aBoardItem) override
bool operator()(const CN_ITEM *a, int b) const
Definition: pad.h:60
void onFilterChange(wxCommandEvent &event) override
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:770
const wxString & GetGroupName() const
PCB_LAYER_ID BottomLayer() const
Definition: track.cpp:463
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:296
const BITMAP_OPAQUE small_plus_xpm[1]
Definition: small_plus.cpp:18
static const COLUMN_DESC COLUMN_VIA_COUNT
Custom text control validator definitions.
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
Definition: track.h:83
std::vector< LIST_ITEM * > m_children
wxDataViewItem GetParent(const wxDataViewItem &aItem) const override
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:162
wxString AddFileExtListToFilter(const std::vector< std::string > &aExts)
Build the wildcard extension file dialog wildcard filter to add to the base message dialog.
Container for design settings for a BOARD object.
wxString formatNetName(const NETINFO_ITEM *aNet) const
wxString formatNetCode(const NETINFO_ITEM *aNet) const
wxObjectDataPtr< DATA_MODEL > m_data_model
int GetNetCode() const
Definition: netinfo.h:113
static int compareUInt(uint64_t aValue1, uint64_t aValue2, bool aAsc)