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