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