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 <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 ? ( i2.GetNetCode() - i1.GetNetCode() ) : ( i1.GetNetCode() - i2.GetNetCode() );
680  }
681  else if( aCol == COLUMN_NAME )
682  {
683  const wxString& s1 = i1.GetNetName();
684  const wxString& s2 = i2.GetNetName();
685 
686  int res = aAsc ? s1.Cmp( s2 ) : s2.Cmp( s1 );
687 
688  if( res != 0 )
689  return res;
690  }
691 
692  else if( aCol == COLUMN_PAD_COUNT && i1.GetPadCount() != i2.GetPadCount() )
693  return compareUInt( i1.GetPadCount(), i2.GetPadCount(), aAsc );
694 
695  else if( aCol == COLUMN_VIA_COUNT && i1.GetViaCount() != i2.GetViaCount() )
696  return compareUInt( i1.GetViaCount(), i2.GetViaCount(), aAsc );
697 
698  else if( aCol == COLUMN_VIA_LENGTH && i1.GetViaLength() != i2.GetViaLength() )
699  return compareUInt( i1.GetViaLength(), i2.GetViaLength(), aAsc );
700 
701  else if( aCol == COLUMN_BOARD_LENGTH && i1.GetBoardWireLength() != i2.GetBoardWireLength() )
702  return compareUInt( i1.GetBoardWireLength(), i2.GetBoardWireLength(), aAsc );
703 
704  else if( aCol == COLUMN_CHIP_LENGTH && i1.GetChipWireLength() != i2.GetChipWireLength() )
705  return compareUInt( i1.GetChipWireLength(), i2.GetChipWireLength(), aAsc );
706 
707  else if( aCol == COLUMN_TOTAL_LENGTH && i1.GetTotalLength() != i2.GetTotalLength() )
708  return compareUInt( i1.GetTotalLength(), i2.GetTotalLength(), aAsc );
709 
710  // when the item values compare equal resort to pointer comparison.
711  wxUIntPtr id1 = wxPtrToUInt( aItem1.GetID() );
712  wxUIntPtr id2 = wxPtrToUInt( aItem2.GetID() );
713 
714  return aAsc ? id1 - id2 : id2 - id1;
715  }
716 
717  bool SetValue( const wxVariant& aInValue, const wxDataViewItem& aItem,
718  unsigned int aCol ) override
719  {
720  return false;
721  }
722 
723  wxDataViewItem GetParent( const wxDataViewItem& aItem ) const override
724  {
725  if( !aItem.IsOk() )
726  return wxDataViewItem();
727 
728  return wxDataViewItem( static_cast<const LIST_ITEM*>( aItem.GetID() )->Parent() );
729  }
730 
731  bool IsContainer( const wxDataViewItem& aItem ) const override
732  {
733  if( !aItem.IsOk() )
734  return true;
735 
736  return static_cast<const LIST_ITEM*>( aItem.GetID() )->GetIsGroup();
737  }
738 
739  bool HasContainerColumns( const wxDataViewItem& aItem ) const override
740  {
741  return IsContainer( aItem );
742  }
743 
744  unsigned int GetChildren( const wxDataViewItem& aParent,
745  wxDataViewItemArray& aChildren ) const override
746  {
747  const LIST_ITEM* p = static_cast<const LIST_ITEM*>( aParent.GetID() );
748 
749  if( !aParent.IsOk() )
750  {
751  aChildren.Alloc( m_items.size() );
752 
753  for( const std::unique_ptr<LIST_ITEM>& i : m_items )
754  {
755  if( i->Parent() == nullptr )
756  aChildren.Add( wxDataViewItem( &*i ) );
757  }
758 
759  return aChildren.GetCount();
760  }
761  else if( p->GetIsGroup() )
762  {
763  const int count = p->ChildrenCount();
764 
765  if( count == 0 )
766  return 0;
767 
768  aChildren.Alloc( count );
769 
770  for( auto i = p->ChildrenBegin(), end = p->ChildrenEnd(); i != end; ++i )
771  aChildren.Add( wxDataViewItem( *i ) );
772 
773  return aChildren.GetCount();
774  }
775 
776  return 0;
777  }
778 
779  wxString GetColumnType( unsigned int /* aCol */ ) const override
780  {
781  return wxS( "string" );
782  }
783 };
784 
785 
787  const SETTINGS& aSettings ) :
788  DIALOG_NET_INSPECTOR_BASE( aParent ),
789  m_zero_netitem( nullptr ),
790  m_frame( aParent )
791 {
792  m_brd = aParent->GetBoard();
793 
794  m_data_model = new DATA_MODEL( *this );
795  m_netsList->AssociateModel( &*m_data_model );
796 
797  std::array<std::function<void( void )>, 8> add_col = {
798  [&]()
799  {
800  m_netsList->AppendTextColumn( COLUMN_NET.display_name, COLUMN_NET,
801  wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
802  wxDATAVIEW_COL_SORTABLE );
803  },
804  [&]()
805  {
806  m_netsList->AppendTextColumn( COLUMN_NAME.display_name, COLUMN_NAME,
807  wxDATAVIEW_CELL_INERT, -1, wxALIGN_LEFT,
808  wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE |
809  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  {
844  wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER,
845  wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE );
846  }
847  };
848 
849  std::vector<int> col_order = aSettings.column_order;
850 
851  if( col_order.size() != add_col.size() )
852  {
853  col_order.resize( add_col.size() );
854 
855  for( unsigned int i = 0; i < add_col.size(); ++i )
856  col_order[i] = i;
857  }
858 
859  for( unsigned int i : col_order )
860  add_col.at( i )();
861 
862  m_netsList->SetExpanderColumn( m_netsList->GetColumn( 0 ) );
863 
864  // avoid onFilterChange for each of the settings as it will re-build the
865  // list over and over again.
867 
868  m_textCtrlFilter->SetValue( aSettings.filter_string );
869  m_cbShowZeroPad->SetValue( aSettings.show_zero_pad_nets );
870  m_groupBy->SetValue( aSettings.group_by );
871  m_groupByKind->SetSelection( aSettings.group_by_kind );
872  m_groupByText->SetValue( aSettings.group_by_text );
873 
875  buildNetsList();
876 
878 
879  m_addNet->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
880  m_renameNet->SetBitmap( KiBitmap( BITMAPS::small_edit ) );
881  m_deleteNet->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
882 
883  m_sdbSizerOK->SetDefault();
884  m_renameNet->Disable();
885  m_deleteNet->Disable();
886 
887  if( aSettings.sorting_column != -1 )
888  {
889  if( wxDataViewColumn* c = m_netsList->GetColumn( aSettings.sorting_column ) )
890  {
891  c->SetSortOrder( aSettings.sort_order_asc );
892  m_data_model->Resort();
893  }
894  }
895 
897 
898  m_frame->Connect( wxEVT_CLOSE_WINDOW,
899  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onParentWindowClosed ),
900  nullptr, this );
901  m_frame->Connect( UNITS_CHANGED,
902  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
903  nullptr, this );
904  m_frame->Connect( BOARD_CHANGED,
905  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
906  nullptr, this );
907 
908  if( m_brd != nullptr )
909  {
910  // if the dialog is opened while something is highlighted on the board ...
912 
913  m_brd->AddListener( this );
914  }
915 }
916 
917 
919 {
920  // the displayed list elements are going to be deleted before the list view itself.
921  // in some cases it might still do queries on the data model, which would crash
922  // from now on. so just disassociate it.
923  m_netsList->AssociateModel( nullptr );
924 
925  m_frame->Disconnect( wxEVT_CLOSE_WINDOW,
926  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onParentWindowClosed ),
927  nullptr, this );
928  m_frame->Disconnect( UNITS_CHANGED,
929  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onUnitsChanged ),
930  nullptr, this );
931  m_frame->Disconnect( BOARD_CHANGED,
932  wxCommandEventHandler( DIALOG_NET_INSPECTOR::onBoardChanged ),
933  nullptr, this );
934 
935  if( m_brd != nullptr )
936  m_brd->RemoveListener( this );
937 
938  m_frame->GetCanvas()->SetFocus();
939 }
940 
941 
943 {
944  std::vector<int> column_order( m_data_model->columnCount() );
945 
946  for( unsigned int i = 0; i < column_order.size(); ++i )
947  column_order[i] = m_netsList->GetColumn( i )->GetModelColumn();
948 
949  wxDataViewColumn* sorting_column = m_netsList->GetSortingColumn();
950 
951  SETTINGS r;
952  r.filter_string = m_textCtrlFilter->GetValue();
953  r.show_zero_pad_nets = m_cbShowZeroPad->IsChecked();
954  r.group_by = m_groupBy->IsChecked();
955  r.group_by_kind = m_groupByKind->GetSelection();
956  r.group_by_text = m_groupByText->GetValue();
957  r.sorting_column = sorting_column ? static_cast<int>( sorting_column->GetModelColumn() ) : -1;
958  r.sort_order_asc = sorting_column ? sorting_column->IsSortOrderAscending() : true;
959  r.column_order = column_order;
960 
961  return r;
962 }
963 
964 
965 void DIALOG_NET_INSPECTOR::onParentWindowClosed( wxCommandEvent& event )
966 {
967  Close();
968  event.Skip();
969 }
970 
971 
972 void DIALOG_NET_INSPECTOR::onUnitsChanged( wxCommandEvent& event )
973 {
974  this->m_units = m_frame->GetUserUnits();
975 
976  m_data_model->updateAllItems();
977 
978  event.Skip();
979 }
980 
981 
982 void DIALOG_NET_INSPECTOR::onBoardChanged( wxCommandEvent& event )
983 {
984  m_brd = m_frame->GetBoard();
985 
986  if( m_brd != nullptr )
987  m_brd->AddListener( this );
988 
989  buildNetsList();
990  m_netsList->Refresh();
991 
992  event.Skip();
993 }
994 
995 
997 {
998  // Note: the filtering is case insensitive.
999 
1000  // Never show the unconnected net
1001  if( aNet->GetNetCode() <= 0 )
1002  return false;
1003 
1004  // Show unconnected nets only if specifically asked for by filter
1005  if( m_netFilter.empty() )
1006  return !aNet->GetNetname().StartsWith( "unconnected-(" );
1007 
1008  wxString net_str = UnescapeString( aNet->GetNetname() ).Upper();
1009 
1010  for( const std::unique_ptr<EDA_PATTERN_MATCH>& f : m_netFilter )
1011  {
1012  if( f->Find( net_str ) )
1013  return true;
1014  }
1015 
1016  return false;
1017 }
1018 
1019 
1021 {
1022  bool operator()( const CN_ITEM* a, const CN_ITEM* b ) const
1023  {
1024  return a->Net() < b->Net();
1025  }
1026 
1027  bool operator()( const CN_ITEM* a, int b ) const
1028  {
1029  return a->Net() < b;
1030  }
1031 
1032  bool operator()( int a, const CN_ITEM* b ) const
1033  {
1034  return a < b->Net();
1035  }
1036 };
1037 
1038 
1040 {
1041  // pre-filter the connectivity items and sort them by netcode.
1042  // this avoids quadratic runtime when building the whole net list and
1043  // calculating the total length for each net.
1044 
1045  const auto type_bits = std::bitset<MAX_STRUCT_TYPE_ID>()
1046  .set( PCB_TRACE_T )
1047  .set( PCB_ARC_T )
1048  .set( PCB_VIA_T )
1049  .set( PCB_PAD_T );
1050 
1051  std::vector<CN_ITEM*> cn_items;
1052  cn_items.reserve( 1024 );
1053 
1054  for( CN_ITEM* cn_item : m_brd->GetConnectivity()->GetConnectivityAlgo()->ItemList() )
1055  {
1056  if( cn_item->Valid() && type_bits[cn_item->Parent()->Type()] )
1057  cn_items.push_back( cn_item );
1058  }
1059 
1060  std::sort( cn_items.begin(), cn_items.end(), NETCODE_CMP_LESS() );
1061 
1062  return cn_items;
1063 }
1064 
1065 
1067 {
1068  if( !aRow )
1069  return;
1070 
1071  wxDataViewItemArray sel;
1072  m_netsList->GetSelections( sel );
1073 
1074  m_data_model->updateItem( aRow );
1075 
1076  if( !sel.IsEmpty() )
1077  {
1078  m_netsList->SetSelections( sel );
1079  m_netsList->EnsureVisible( sel.Item( 0 ) );
1080  }
1081 }
1082 
1083 
1085 {
1086  return wxString::Format( "%.3d", aNet->GetNetCode() );
1087 }
1088 
1089 
1091 {
1092  return UnescapeString( aNet->GetNetname() );
1093 }
1094 
1095 
1096 wxString DIALOG_NET_INSPECTOR::formatCount( unsigned int aValue ) const
1097 {
1098  return wxString::Format( "%u", aValue );
1099 }
1100 
1101 
1102 wxString DIALOG_NET_INSPECTOR::formatLength( int64_t aValue ) const
1103 {
1104  return MessageTextFromValue( GetUserUnits(), static_cast<long long int>( aValue ) );
1105 }
1106 
1107 
1109 {
1110  if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1111  {
1112  // a new net has been added to the board. add it to our list if it
1113  // passes the netname filter test.
1114 
1115  if( netFilterMatches( net ) )
1116  {
1117  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( net );
1118 
1119  // the new net could have some pads already assigned, count them.
1120  new_item->SetPadCount( m_brd->GetNodesCount( net->GetNetCode() ) );
1121 
1122  m_data_model->addItem( std::move( new_item ) );
1123  }
1124  }
1125  else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1126  {
1127  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1128 
1129  if( r )
1130  {
1131  // try to handle frequent operations quickly.
1132  if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( i ) )
1133  {
1134  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1135  int len = track->GetLength();
1136 
1137  list_item->AddBoardWireLength( len );
1138 
1139  if( track->Type() == PCB_VIA_T )
1140  {
1141  list_item->AddViaCount( 1 );
1142  list_item->AddViaLength( calculateViaLength( track ) );
1143  }
1144 
1146  return;
1147  }
1148  }
1149 
1150  // resort to generic slower net update otherwise.
1151  updateNet( i->GetNet() );
1152  }
1153  else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1154  {
1155  for( const PAD* pad : footprint->Pads() )
1156  {
1157  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1158 
1159  if( !r )
1160  {
1161  // if show-zero-pads is off, we might not have this net
1162  // in our list yet, so add it first.
1163  // notice that at this point we are very certain that this net
1164  // will have at least one pad.
1165 
1166  if( netFilterMatches( pad->GetNet() ) )
1167  r = m_data_model->addItem( std::make_unique<LIST_ITEM>( pad->GetNet() ) );
1168  }
1169 
1170  if( r )
1171  {
1172  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1173  int len = pad->GetPadToDieLength();
1174 
1175  list_item->AddPadCount( 1 );
1176  list_item->AddChipWireLength( len );
1177 
1178  if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1179  m_data_model->deleteItem( r );
1180  else
1182  }
1183  }
1184  }
1185 }
1186 
1187 
1188 void DIALOG_NET_INSPECTOR::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aBoardItem )
1189 {
1190  for( BOARD_ITEM* item : aBoardItem )
1191  {
1192  OnBoardItemAdded( aBoard, item );
1193  }
1194 }
1195 
1196 
1198 {
1199  if( NETINFO_ITEM* net = dynamic_cast<NETINFO_ITEM*>( aBoardItem ) )
1200  {
1201  m_data_model->deleteItem( m_data_model->findItem( net ) );
1202  }
1203  else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( aBoardItem ) )
1204  {
1205  for( const PAD* pad : footprint->Pads() )
1206  {
1207  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( pad->GetNet() );
1208 
1209  if( r )
1210  {
1211  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1212  int len = pad->GetPadToDieLength();
1213 
1214  list_item->SubPadCount( 1 );
1215  list_item->SubChipWireLength( len );
1216 
1217  if( list_item->GetPadCount() == 0 && !m_cbShowZeroPad->IsChecked() )
1218  m_data_model->deleteItem( r );
1219  else
1221  }
1222  }
1223  }
1224  else if( BOARD_CONNECTED_ITEM* i = dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) )
1225  {
1226  OPT<LIST_ITEM_ITER> r = m_data_model->findItem( i->GetNet() );
1227 
1228  if( r )
1229  {
1230  // try to handle frequent operations quickly.
1231  if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( i ) )
1232  {
1233  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1234  int len = track->GetLength();
1235 
1236  list_item->SubBoardWireLength( len );
1237 
1238  if( track->Type() == PCB_VIA_T )
1239  {
1240  list_item->SubViaCount( 1 );
1241  list_item->SubViaLength( calculateViaLength( track ) );
1242  }
1243 
1245  return;
1246  }
1247 
1248  // resort to generic slower net update otherwise.
1249  updateNet( i->GetNet() );
1250  }
1251  }
1252 }
1253 
1254 
1256  std::vector<BOARD_ITEM*>& aBoardItems )
1257 {
1258  for( BOARD_ITEM* item : aBoardItems )
1259  {
1260  OnBoardItemRemoved( aBoard, item );
1261  }
1262 }
1263 
1264 
1266 {
1267  if( dynamic_cast<BOARD_CONNECTED_ITEM*>( aBoardItem ) != nullptr
1268  || dynamic_cast<FOOTPRINT*>( aBoardItem ) != nullptr )
1269  {
1270  buildNetsList();
1271  m_netsList->Refresh();
1272  }
1273 }
1274 
1275 
1277  std::vector<BOARD_ITEM*>& aBoardItems )
1278 {
1279  buildNetsList();
1280  m_netsList->Refresh();
1281 }
1282 
1283 
1285 {
1286  if( !m_brd->IsHighLightNetON() )
1287  {
1288  m_netsList->UnselectAll();
1289  }
1290  else
1291  {
1292  const std::set<int>& selected_codes = m_brd->GetHighLightNetCodes();
1293 
1294  wxDataViewItemArray new_selection;
1295  new_selection.Alloc( selected_codes.size() );
1296 
1297  for( int code : selected_codes )
1298  {
1299  if( OPT<LIST_ITEM_ITER> r = m_data_model->findItem( code ) )
1300  new_selection.Add( wxDataViewItem( &***r ) );
1301  }
1302 
1303  m_netsList->SetSelections( new_selection );
1304 
1305  if( !new_selection.IsEmpty() )
1306  m_netsList->EnsureVisible( new_selection.Item( 0 ) );
1307  }
1308 }
1309 
1310 
1312 {
1313  buildNetsList();
1314  m_netsList->Refresh();
1315 }
1316 
1317 
1319 {
1320  // something for the specified net has changed, update that row.
1321  // ignore nets that are not in our list because the filter doesn't match.
1322 
1323  if( !netFilterMatches( aNet ) )
1324  {
1325  m_data_model->deleteItem( m_data_model->findItem( aNet ) );
1326  return;
1327  }
1328 
1329  // if the net had no pads before, it might not be in the displayed list yet.
1330  // if it had pads and now doesn't anymore, we might need to remove it from the list.
1331 
1332  OPT<LIST_ITEM_ITER> cur_net_row = m_data_model->findItem( aNet );
1333 
1334  const unsigned int node_count = m_brd->GetNodesCount( aNet->GetNetCode() );
1335 
1336  if( node_count == 0 && !m_cbShowZeroPad->IsChecked() )
1337  {
1338  m_data_model->deleteItem( cur_net_row );
1339  return;
1340  }
1341 
1342  std::unique_ptr<LIST_ITEM> new_list_item = buildNewItem( aNet, node_count,
1344 
1345  if( !cur_net_row )
1346  {
1347  m_data_model->addItem( std::move( new_list_item ) );
1348  return;
1349  }
1350 
1351  const std::unique_ptr<LIST_ITEM>& cur_list_item = *cur_net_row.get();
1352 
1353  if( cur_list_item->GetNetName() != new_list_item->GetNetName() )
1354  {
1355  // if the name has changed, it might require re-grouping.
1356  // it's easier to remove and re-insert it
1357  m_data_model->deleteItem( cur_net_row );
1358  m_data_model->addItem( std::move( new_list_item ) );
1359  }
1360  else
1361  {
1362  // update fields only
1363  cur_list_item->SetPadCount( new_list_item->GetPadCount() );
1364  cur_list_item->SetViaCount( new_list_item->GetViaCount() );
1365  cur_list_item->SetBoardWireLength( new_list_item->GetBoardWireLength() );
1366  cur_list_item->SetChipWireLength( new_list_item->GetChipWireLength() );
1367 
1368  updateDisplayedRowValues( cur_net_row );
1369  }
1370 }
1371 
1372 
1373 unsigned int DIALOG_NET_INSPECTOR::calculateViaLength( const PCB_TRACK* aTrack ) const
1374 {
1375  const PCB_VIA& via = dynamic_cast<const PCB_VIA&>( *aTrack );
1377 
1378  // calculate the via length individually from the board stackup and via's start and end layer.
1379  if( bds.m_HasStackup )
1380  {
1381  const BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
1382  return stackup.GetLayerDistance( via.TopLayer(), via.BottomLayer() );
1383  }
1384  else
1385  {
1386  int dielectricLayers = bds.GetCopperLayerCount() - 1;
1387  int layerThickness = bds.GetBoardThickness() / dielectricLayers;
1388  int effectiveBottomLayer;
1389 
1390  if( via.BottomLayer() == B_Cu )
1391  effectiveBottomLayer = F_Cu + dielectricLayers;
1392  else
1393  effectiveBottomLayer = via.BottomLayer();
1394 
1395  int layerCount = effectiveBottomLayer - via.TopLayer();
1396 
1397  return layerCount * layerThickness;
1398  }
1399 }
1400 
1401 
1402 std::unique_ptr<DIALOG_NET_INSPECTOR::LIST_ITEM>
1403 DIALOG_NET_INSPECTOR::buildNewItem( NETINFO_ITEM* aNet, unsigned int aPadCount,
1404  const std::vector<CN_ITEM*>& aCNItems )
1405 {
1406  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( aNet );
1407 
1408  new_item->SetPadCount( aPadCount );
1409 
1410  const auto cn_items = std::equal_range( aCNItems.begin(), aCNItems.end(), aNet->GetNetCode(),
1411  NETCODE_CMP_LESS() );
1412 
1413  for( auto i = cn_items.first; i != cn_items.second; ++i )
1414  {
1415  BOARD_CONNECTED_ITEM* item = ( *i )->Parent();
1416 
1417  if( item->Type() == PCB_PAD_T )
1418  new_item->AddChipWireLength( static_cast<PAD*>( item )->GetPadToDieLength() );
1419 
1420  else if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
1421  {
1422  new_item->AddBoardWireLength( track->GetLength() );
1423 
1424  if( item->Type() == PCB_VIA_T )
1425  {
1426  new_item->AddViaCount( 1 );
1427  new_item->AddViaLength( calculateViaLength( track ) );
1428  }
1429  }
1430  }
1431 
1432  return new_item;
1433 }
1434 
1435 
1437 {
1438  // Only build the list of nets if there is a board present
1439  if( !m_brd )
1440  return;
1441 
1442  m_in_build_nets_list = true;
1443 
1444  // when rebuilding the netlist, try to keep the row selection
1445  // FIXME: handle group selections, preserve expanded/collapsed group states
1446  wxDataViewItemArray sel;
1447  m_netsList->GetSelections( sel );
1448 
1449  std::vector<int> prev_selected_netcodes;
1450  prev_selected_netcodes.reserve( sel.GetCount() );
1451 
1452  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1453  {
1454  const LIST_ITEM* item = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1455  prev_selected_netcodes.push_back( item->GetNetCode() );
1456  }
1457 
1458  m_data_model->deleteAllItems();
1459 
1460  std::vector<std::unique_ptr<LIST_ITEM>> new_items;
1461 
1462  // for group mode 0,1 each group filter string represents one displayed group,
1463  // so just add them first. for group mode 2,3 the groups are generated dynamically.
1464  if( m_groupBy->IsChecked()
1465  && ( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 1 ) )
1466  {
1467  for( unsigned int i = 0; i < m_groupFilter.size(); ++i )
1468  {
1469  const std::unique_ptr<EDA_PATTERN_MATCH>& filter = m_groupFilter[i];
1470  new_items.emplace_back( std::make_unique<LIST_ITEM>( i, filter->GetPattern() ) );
1471  }
1472  }
1473 
1474  std::vector<CN_ITEM*> prefiltered_cn_items = relevantConnectivityItems();
1475 
1476 
1477  // collect all nets which pass the filter string and also remember the
1478  // suffix after the filter match, if any.
1479  struct NET_INFO
1480  {
1481  int netcode;
1482  NETINFO_ITEM* net;
1483  unsigned int pad_count;
1484  };
1485 
1486  struct NET_INFO_CMP_LESS
1487  {
1488  bool operator()( const NET_INFO& a, const NET_INFO& b ) const
1489  {
1490  return a.netcode < b.netcode;
1491  }
1492  bool operator()( const NET_INFO& a, int b ) const
1493  {
1494  return a.netcode < b;
1495  }
1496  bool operator()( int a, const NET_INFO& b ) const
1497  {
1498  return a < b.netcode;
1499  }
1500  };
1501 
1502  std::vector<NET_INFO> nets;
1503  nets.reserve( m_brd->GetNetInfo().NetsByNetcode().size() );
1504 
1505  for( const std::pair<int, NETINFO_ITEM*> ni : m_brd->GetNetInfo().NetsByNetcode() )
1506  {
1507  if( ni.first == 0 )
1508  m_zero_netitem = ni.second;
1509 
1510  if( netFilterMatches( ni.second ) )
1511  nets.emplace_back( NET_INFO{ ni.first, ni.second, 0 } );
1512  }
1513 
1514  // count the pads for each net. since the nets are sorted by netcode
1515  // iterating over the footprints' pads is faster.
1516 
1517  for( FOOTPRINT* footprint : m_brd->Footprints() )
1518  {
1519  for( PAD* pad : footprint->Pads() )
1520  {
1521  auto i = std::lower_bound( nets.begin(), nets.end(), pad->GetNetCode(),
1522  NET_INFO_CMP_LESS() );
1523 
1524  if( i != nets.end() && i->netcode == pad->GetNetCode() )
1525  i->pad_count += 1;
1526  }
1527  }
1528 
1529  for( NET_INFO& ni : nets )
1530  {
1531  if( m_cbShowZeroPad->IsChecked() || ni.pad_count > 0 )
1532  new_items.emplace_back( buildNewItem( ni.net, ni.pad_count, prefiltered_cn_items ) );
1533  }
1534 
1535 
1536  m_data_model->addItems( std::move( new_items ) );
1537 
1538  // try to restore the selected rows. set the ones that we can't find anymore to -1.
1539  sel.Clear();
1540 
1541  for( int& nc : prev_selected_netcodes )
1542  {
1543  auto r = m_data_model->findItem( nc );
1544 
1545  if( r )
1546  {
1547  const std::unique_ptr<LIST_ITEM>& list_item = *r.get();
1548  sel.Add( wxDataViewItem( list_item.get() ) );
1549  }
1550  else
1551  {
1552  nc = -1;
1553  }
1554  }
1555 
1556  if( !sel.IsEmpty() )
1557  {
1558  m_netsList->SetSelections( sel );
1559  m_netsList->EnsureVisible( sel.Item( 0 ) );
1560  }
1561  else
1562  {
1563  m_netsList->UnselectAll();
1564  }
1565 
1566  prev_selected_netcodes.erase( std::remove( prev_selected_netcodes.begin(),
1567  prev_selected_netcodes.end(), -1 ),
1568  prev_selected_netcodes.end() );
1569 
1571 
1572  for( int& i : prev_selected_netcodes )
1573  m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings()->SetHighlight( true, i, true );
1574 
1576  m_frame->GetCanvas()->Refresh();
1577 
1578  m_in_build_nets_list = false;
1579 }
1580 
1581 
1582 void DIALOG_NET_INSPECTOR::onFilterChange( wxCommandEvent& aEvent )
1583 {
1584  wxStringTokenizer filters( m_textCtrlFilter->GetValue().Upper(), "," );
1585  m_netFilter.clear();
1586 
1587  while( filters.HasMoreTokens() )
1588  {
1589  wxString t = filters.GetNextToken();
1590  t.Trim( false );
1591  t.Trim( true );
1592 
1593  if( !t.IsEmpty() )
1594  {
1595  m_netFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1596  m_netFilter.back()->SetPattern( t );
1597  }
1598  }
1599 
1600  wxStringTokenizer group_filters( m_groupByText->GetValue(), "," );
1601  m_groupFilter.clear();
1602 
1603  while( group_filters.HasMoreTokens() )
1604  {
1605  wxString t = group_filters.GetNextToken();
1606  t.Trim( false );
1607  t.Trim( true );
1608 
1609  if( !t.IsEmpty() )
1610  {
1611  if( m_groupByKind->GetSelection() == 0 || m_groupByKind->GetSelection() == 2 )
1612  {
1613  // type 2: wildcard match, use the matching substring as a group key.
1614  // the number of groups is determined dynamically by the number of
1615  // resulting matches in the whole set.
1616  m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
1617  m_groupFilter.back()->SetPattern( t );
1618  }
1619  else if( m_groupByKind->GetSelection() == 1 || m_groupByKind->GetSelection() == 3 )
1620  {
1621  // type 3: regex match, use the matching substring as a group key.
1622  // the number of groups is determined dynamically by the number of
1623  // resulting matches in the whole set.
1624  m_groupFilter.emplace_back( std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
1625  m_groupFilter.back()->SetPattern( t );
1626  }
1627  }
1628  }
1629 
1631  buildNetsList();
1632 }
1633 
1634 
1635 void DIALOG_NET_INSPECTOR::onSelChanged( wxDataViewEvent& )
1636 {
1637  onSelChanged();
1638 }
1639 
1640 
1642 {
1643  // ignore selection changes while the whole list is being rebuilt.
1644  if( m_in_build_nets_list )
1645  return;
1646 
1648 
1649  bool enable_rename_button = false;
1650  bool enable_delete_button = false;
1651 
1652  if( m_netsList->HasSelection() )
1653  {
1654  wxDataViewItemArray sel;
1655  m_netsList->GetSelections( sel );
1656 
1657  ps->SetHighlight( false );
1658 
1659  enable_rename_button = sel.GetCount() == 1;
1660  enable_delete_button = true;
1661 
1662  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1663  {
1664  const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1665 
1666  if( ii->GetIsGroup() )
1667  {
1668  enable_rename_button = false;
1669 
1670  for( auto c = ii->ChildrenBegin(), end = ii->ChildrenEnd(); c != end; ++c )
1671  ps->SetHighlight( true, ( *c )->GetNetCode(), true );
1672  }
1673  else
1674  ps->SetHighlight( true, ii->GetNetCode(), true );
1675  }
1676  }
1677  else
1678  ps->SetHighlight( false );
1679 
1681  m_frame->GetCanvas()->Refresh();
1682 
1683  m_renameNet->Enable( enable_rename_button );
1684  m_deleteNet->Enable( enable_delete_button );
1685 }
1686 
1687 
1688 void DIALOG_NET_INSPECTOR::onSortingChanged( wxDataViewEvent& aEvent )
1689 {
1690  // FIXME: Whenever the sort criteria changes (sorting column)
1691  // the visible row-numers of the selection get preserved, not the actual
1692  // elements. Don't know at the moment how to preserve the selection,
1693  // so just clear it for now.
1694 
1695  m_netsList->UnselectAll();
1696 
1699  m_frame->GetCanvas()->Refresh();
1700 }
1701 
1702 
1704 {
1711  wxClientDC dc( GetParent() );
1712 
1713  int h, minw, minw_col0, minw_col1;
1714  int w0, w1, w2, w3, w4, w5, w6, w7;
1715 
1716  dc.GetTextExtent( COLUMN_NET.display_name, &w0, &h );
1717  dc.GetTextExtent( COLUMN_NAME.display_name, &minw_col1, &h );
1718  dc.GetTextExtent( COLUMN_PAD_COUNT.display_name, &w2, &h );
1719  dc.GetTextExtent( COLUMN_VIA_COUNT.display_name, &w3, &h );
1720  dc.GetTextExtent( COLUMN_VIA_LENGTH.display_name, &w4, &h );
1721  dc.GetTextExtent( COLUMN_BOARD_LENGTH.display_name, &w5, &h );
1722  dc.GetTextExtent( COLUMN_CHIP_LENGTH.display_name, &w6, &h );
1723  dc.GetTextExtent( COLUMN_TOTAL_LENGTH.display_name, &w7, &h );
1724  dc.GetTextExtent( "00000,000 mm", &minw, &h );
1725  dc.GetTextExtent( "00000", &minw_col0, &h );
1726 
1727  // Considering left and right margins.
1728  // For wxRenderGeneric it is 5px.
1729  // Also account for the sorting arrow in the column header.
1730  // Column 0 also needs space for any potential expander icons.
1731  const int extra_width = 30;
1732 
1733  w0 = std::max( w0, minw_col0 ) + extra_width;
1734  minw_col1 = minw_col1 + extra_width;
1735  w2 = w2 + extra_width;
1736  w3 = w3 + extra_width;
1737  w4 = std::max( w4 + extra_width, minw );
1738  w5 = std::max( w5 + extra_width, minw );
1739  w6 = std::max( w6 + extra_width, minw );
1740  w7 = std::max( w7 + extra_width, minw );
1741 
1742  // the columns might have been reordered. we work on the column model numbers though.
1743  std::vector<int> column_order( m_data_model->columnCount() );
1744 
1745  for( unsigned int i = 0; i < column_order.size(); ++i )
1746  column_order[m_netsList->GetColumn( i )->GetModelColumn()] = i;
1747 
1748  assert( column_order.size() == 8 );
1749 
1750  m_netsList->GetColumn( column_order[0] )->SetWidth( w0 );
1751  m_netsList->GetColumn( column_order[1] )->SetMinWidth( minw_col1 );
1752  m_netsList->GetColumn( column_order[2] )->SetWidth( w2 );
1753  m_netsList->GetColumn( column_order[3] )->SetWidth( w3 );
1754  m_netsList->GetColumn( column_order[4] )->SetWidth( w4 );
1755  m_netsList->GetColumn( column_order[5] )->SetWidth( w5 );
1756  m_netsList->GetColumn( column_order[6] )->SetWidth( w6 );
1757  m_netsList->GetColumn( column_order[7] )->SetWidth( w7 );
1758 
1759  // At resizing of the list the width of middle column (Net Names) changes only.
1760  int width = m_netsList->GetClientSize().x - 24;
1761  w1 = width - w0 - w2 - w3 - w4 - w5 - w6 - w7;
1762 
1763  // Column 1 (net names) need a minimal width to display net names
1764  dc.GetTextExtent( "MMMMMMMMMMMMMMMM", &minw, &h );
1765  w1 = std::max( w1, minw );
1766 
1767  m_netsList->GetColumn( column_order[1] )->SetWidth( w1 );
1768 
1769  m_netsList->Refresh();
1770 }
1771 
1772 
1773 void DIALOG_NET_INSPECTOR::onListSize( wxSizeEvent& aEvent )
1774 {
1775  aEvent.Skip();
1777 }
1778 
1779 
1780 void DIALOG_NET_INSPECTOR::onAddNet( wxCommandEvent& aEvent )
1781 {
1782  wxString newNetName;
1783  NETNAME_VALIDATOR validator( &newNetName );
1784 
1785  WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "New Net" ), newNetName );
1786  dlg.SetTextValidator( validator );
1787 
1788  while( true )
1789  {
1790  if( dlg.ShowModal() != wxID_OK || dlg.GetValue().IsEmpty() )
1791  return; //Aborted by user
1792 
1793  newNetName = dlg.GetValue();
1794 
1795  if( m_brd->FindNet( newNetName ) )
1796  {
1797  DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
1798  newNetName ) );
1799  newNetName = wxEmptyString;
1800  }
1801  else
1802  {
1803  break;
1804  }
1805  }
1806 
1807  NETINFO_ITEM *newnet = new NETINFO_ITEM( m_brd, dlg.GetValue(), 0 );
1808 
1809  m_brd->Add( newnet );
1810  m_frame->OnModify();
1811 
1812  // We'll get an OnBoardItemAdded callback from this to update our listbox
1813 }
1814 
1815 
1816 void DIALOG_NET_INSPECTOR::onRenameNet( wxCommandEvent& aEvent )
1817 {
1818  if( m_netsList->GetSelectedItemsCount() == 1 )
1819  {
1820  const LIST_ITEM* sel = static_cast<const LIST_ITEM*>( m_netsList->GetSelection().GetID() );
1821 
1822  if( sel->GetIsGroup() )
1823  return;
1824 
1825  NETINFO_ITEM* net = sel->GetNet();
1826  wxString fullNetName = net->GetNetname();
1827  wxString netPath;
1828  wxString shortNetName;
1829 
1830  if( fullNetName.Contains( "/" ) )
1831  {
1832  netPath = fullNetName.BeforeLast( '/' ) + '/';
1833  shortNetName = fullNetName.AfterLast( '/' );
1834  }
1835  else
1836  {
1837  shortNetName = fullNetName;
1838  }
1839 
1840  wxString unescapedShortName = UnescapeString( shortNetName );
1841 
1842  WX_TEXT_ENTRY_DIALOG dlg( this, _( "Net name:" ), _( "Rename Net" ), unescapedShortName );
1843  NETNAME_VALIDATOR validator( &unescapedShortName );
1844  dlg.SetTextValidator( validator );
1845 
1846  while( true )
1847  {
1848  if( dlg.ShowModal() != wxID_OK || dlg.GetValue() == unescapedShortName )
1849  return;
1850 
1851  unescapedShortName = dlg.GetValue();
1852 
1853  if( unescapedShortName.IsEmpty() )
1854  {
1855  DisplayError( this, wxString::Format( _( "Net name cannot be empty." ),
1856  unescapedShortName ) );
1857  continue;
1858  }
1859 
1860  shortNetName = EscapeString( unescapedShortName, CTX_NETNAME );
1861  fullNetName = netPath + shortNetName;
1862 
1863  if( m_brd->FindNet( shortNetName ) || m_brd->FindNet( fullNetName ) )
1864  {
1865  DisplayError( this, wxString::Format( _( "Net name '%s' is already in use." ),
1866  unescapedShortName ) );
1867  unescapedShortName = wxEmptyString;
1868  }
1869  else
1870  {
1871  break;
1872  }
1873  }
1874 
1875  // the changed name might require re-grouping. remove and re-insert
1876  // is easier.
1877  auto removed_item = m_data_model->deleteItem( m_data_model->findItem( net ) );
1878 
1879  m_brd->GetNetInfo().RemoveNet( net );
1880  net->SetNetname( fullNetName );
1881  m_brd->GetNetInfo().AppendNet( net );
1882  m_frame->OnModify();
1883 
1884  if( netFilterMatches( net ) )
1885  {
1886  std::unique_ptr<LIST_ITEM> new_item = std::make_unique<LIST_ITEM>( net );
1887  new_item->SetPadCount( removed_item->GetPadCount() );
1888  new_item->SetViaCount( removed_item->GetViaCount() );
1889  new_item->SetBoardWireLength( removed_item->GetBoardWireLength() );
1890  new_item->SetChipWireLength( removed_item->GetChipWireLength() );
1891 
1892  OPT<LIST_ITEM_ITER> added_row = m_data_model->addItem( std::move( new_item ) );
1893 
1894  wxDataViewItemArray new_sel;
1895  new_sel.Add( wxDataViewItem( &***added_row ) );
1896  m_netsList->SetSelections( new_sel );
1897  onSelChanged();
1898  }
1899 
1900  // Currently only tracks and pads have netname annotations and need to be redrawn,
1901  // but zones are likely to follow. Since we don't have a way to ask what is current,
1902  // just refresh all items.
1904  m_frame->GetCanvas()->Refresh();
1905  }
1906 }
1907 
1908 
1909 void DIALOG_NET_INSPECTOR::onDeleteNet( wxCommandEvent& aEvent )
1910 {
1911  if( !m_netsList->HasSelection() )
1912  return;
1913 
1914  wxDataViewItemArray sel;
1915  m_netsList->GetSelections( sel );
1916 
1917  auto delete_one =
1918  [this]( const LIST_ITEM* i )
1919  {
1920  if( i->GetPadCount() == 0
1921  || IsOK( this, wxString::Format( _( "Net '%s' is in use. Delete anyway?" ),
1922  i->GetNetName() ) ) )
1923  {
1924  // This is a bit hacky, but it will do for now, since this is the only path
1925  // outside the netlist updater where you can remove a net from a BOARD.
1926  int removedCode = i->GetNetCode();
1927 
1929  [removedCode]( KIGFX::VIEW_ITEM* aItem ) -> bool
1930  {
1931  if( auto bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( aItem ) )
1932  return bci->GetNetCode() == removedCode;
1933 
1934  return false;
1935  } );
1936 
1937  m_brd->Remove( i->GetNet() );
1938  m_frame->OnModify();
1939 
1940  // We'll get an OnBoardItemRemoved callback from this to update our listbox
1941  }
1942  };
1943 
1944  for( unsigned int i = 0; i < sel.GetCount(); ++i )
1945  {
1946  const LIST_ITEM* ii = static_cast<const LIST_ITEM*>( sel.Item( i ).GetID() );
1947 
1948  if( ii->GetIsGroup() )
1949  {
1950  if( ii->ChildrenCount() != 0
1951  && IsOK( this, wxString::Format( _( "Delete all nets in group '%s'?" ),
1952  ii->GetGroupName() ) ) )
1953  {
1954  // we can't be iterating the children container and deleting items from
1955  // it at the same time. thus take a copy of it first.
1956  std::vector<const LIST_ITEM*> children;
1957  children.reserve( ii->ChildrenCount() );
1958  std::copy( ii->ChildrenBegin(), ii->ChildrenEnd(), std::back_inserter( children ) );
1959 
1960  for( const LIST_ITEM* c : children )
1961  delete_one( c );
1962  }
1963  }
1964  else
1965  {
1966  delete_one( ii );
1967  }
1968  }
1969 }
1970 
1971 
1972 void DIALOG_NET_INSPECTOR::onReport( wxCommandEvent& aEvent )
1973 {
1974  wxFileDialog dlg( this, _( "Report file" ), "", "",
1975  _( "Report file" ) + AddFileExtListToFilter( { "csv" } ),
1976  wxFD_SAVE );
1977 
1978  if( dlg.ShowModal() == wxID_CANCEL )
1979  return;
1980 
1981  wxTextFile f( dlg.GetPath() );
1982 
1983  f.Create();
1984 
1985  wxString txt;
1986 
1987  // Print Header:
1988  for( auto&& col : m_data_model->columnDesc() )
1989  txt += '"' + col.csv_name + "\";";
1990 
1991  f.AddLine( txt );
1992 
1993  // Print list of nets:
1994  const unsigned int num_rows = m_data_model->itemCount();
1995 
1996  for( unsigned int row = 0; row < num_rows; row++ )
1997  {
1998  auto& i = m_data_model->itemAt( row );
1999 
2000  if( i.GetIsGroup() || i.GetNetCode() == 0 )
2001  continue;
2002 
2003  txt = "";
2004 
2005  for( auto&& col : m_data_model->columnDesc() )
2006  {
2007  if( col.csv_flags & COLUMN_DESC::CSV_QUOTE )
2008  txt += '"' + m_data_model->valueAt( col.num, row ).GetString() + "\";";
2009  else
2010  txt += m_data_model->valueAt( col.num, row ).GetString() + ';';
2011  }
2012 
2013  f.AddLine( txt );
2014  }
2015 
2016  f.Write();
2017  f.Close();
2018 }
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:2068
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1343
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:103
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:1426
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:80
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:402
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:1416
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:684
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:81
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:588
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 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:119
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:1081
#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:606
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: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:2075
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:233
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:344
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
#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:130
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
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.
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:386
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:198
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: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
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:208
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
Definition: string.cpp:135
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:708
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
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:57
void onFilterChange(wxCommandEvent &event) override
void UpdateAllLayersColor()
Apply the new coloring scheme to all layers.
Definition: view.cpp:774
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:296
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:113
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)