KiCad PCB EDA Suite
net_selector.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) 2018 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <kicad_string.h>
25 #include <kiplatform/ui.h>
26 
27 #include <widgets/net_selector.h>
28 
29 #include <board.h>
30 #include <netinfo.h>
31 #include <wx/arrstr.h>
32 #include <wx/display.h>
33 
34 wxDEFINE_EVENT( NET_SELECTED, wxCommandEvent );
35 
36 #if defined( __WXOSX_MAC__ )
37  #define POPUP_PADDING 2
38  #define LIST_ITEM_PADDING 5
39  #define LIST_PADDING 5
40 #elif defined( __WXMSW__ )
41  #define POPUP_PADDING 0
42  #define LIST_ITEM_PADDING 2
43  #define LIST_PADDING 5
44 #else
45  #define POPUP_PADDING 0
46  #define LIST_ITEM_PADDING 6
47  #define LIST_PADDING 5
48 #endif
49 
50 #define NO_NET _( "<no net>" )
51 #define CREATE_NET _( "<create net>" )
52 
53 
54 class NET_SELECTOR_COMBOPOPUP : public wxPanel, public wxComboPopup
55 {
56 public:
58  m_filterValidator( nullptr ),
59  m_filterCtrl( nullptr ),
60  m_listBox( nullptr ),
61  m_minPopupWidth( -1 ),
62  m_maxPopupHeight( 1000 ),
63  m_netinfoList( nullptr ),
64  m_board( nullptr ),
65  m_selectedNetcode( 0 ),
66  m_focusHandler( nullptr )
67  { }
68 
69  bool Create(wxWindow* aParent) override
70  {
71  wxPanel::Create( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER );
72 
73  wxBoxSizer* mainSizer;
74  mainSizer = new wxBoxSizer( wxVERTICAL );
75 
76  wxStaticText* filterLabel = new wxStaticText( this, wxID_ANY, _( "Filter:" ) );
77  mainSizer->Add( filterLabel, 0, wxEXPAND, 0 );
78 
79  m_filterCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition,
80  wxDefaultSize, wxTE_PROCESS_ENTER );
81  m_filterValidator = new wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
82  m_filterValidator->SetCharExcludes( " " );
83  m_filterCtrl->SetValidator( *m_filterValidator );
84  mainSizer->Add( m_filterCtrl, 0, wxEXPAND, 0 );
85 
86  m_listBox = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, 0,
87  wxLB_SINGLE|wxLB_NEEDED_SB );
88  mainSizer->Add( m_listBox, 0, wxEXPAND|wxTOP, 2 );
89 
90  SetSizer( mainSizer );
91  Layout();
92 
93  Connect( wxEVT_IDLE, wxIdleEventHandler( NET_SELECTOR_COMBOPOPUP::onIdle ), NULL, this );
94  Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR_COMBOPOPUP::onKeyDown ), NULL, this );
95  Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( NET_SELECTOR_COMBOPOPUP::onMouseClick ), NULL, this );
96  m_listBox->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( NET_SELECTOR_COMBOPOPUP::onMouseClick ), NULL, this );
97  m_filterCtrl->Connect( wxEVT_TEXT, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onFilterEdit ), NULL, this );
98  m_filterCtrl->Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onEnter ), NULL, this );
99 
100  // <enter> in a ListBox comes in as a double-click on GTK
101  m_listBox->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onEnter ), NULL, this );
102 
103  return true;
104  }
105 
106  wxWindow *GetControl() override { return this; }
107 
108  void SetStringValue( const wxString& aNetName ) override
109  {
110  // shouldn't be here (combo is read-only)
111  }
112 
113  wxString GetStringValue() const override
114  {
115  if( m_selectedNetcode == -1 )
116  return m_indeterminateLabel;
117 
119 
120  if( netInfo && netInfo->GetNetCode() > 0 )
121  return netInfo->GetNetname();
122 
123  return NO_NET;
124  }
125 
126  void SetNetInfo( NETINFO_LIST* aNetInfoList )
127  {
128  m_netinfoList = aNetInfoList;
129  rebuildList();
130  }
131 
132  void SetIndeterminateLabel( const wxString& aIndeterminateLabel )
133  {
134  m_indeterminateLabel = aIndeterminateLabel;
135  rebuildList();
136  }
137 
138  void SetBoard( BOARD* aBoard )
139  {
140  m_board = aBoard;
141  }
142 
144  bool IsIndeterminate() { return m_selectedNetcode == -1; }
145 
146  void SetSelectedNetcode( int aNetcode ) { m_selectedNetcode = aNetcode; }
148 
149  void SetSelectedNet( const wxString& aNetname )
150  {
151  if( m_netinfoList && m_netinfoList->GetNetItem( aNetname ) )
153  }
154 
156  {
159  else
160  return wxEmptyString;
161  }
162 
163  wxSize GetAdjustedSize( int aMinWidth, int aPrefHeight, int aMaxHeight ) override
164  {
165  // Called when the popup is first shown. Stash the minWidth and maxHeight so we
166  // can use them later when refreshing the sizes after filter changes.
167  m_minPopupWidth = aMinWidth;
168  m_maxPopupHeight = aMaxHeight;
169 
170  return updateSize();
171  }
172 
173  void OnPopup() override
174  {
175  // While it can sometimes be useful to keep the filter, it's always unexpected.
176  // Better to clear it.
177  m_filterCtrl->Clear();
178 
179  // The updateSize() call in GetAdjustedSize() leaves the height off-by-one for
180  // some reason, so do it again.
181  updateSize();
182  }
183 
184  void OnStartingKey( wxKeyEvent& aEvent )
185  {
187  doStartingKey( aEvent );
188  }
189 
190  void Accept()
191  {
192  wxString selectedNetName;
193  wxString escapedNetName;
194  wxString remainingName;
195  int selection = m_listBox->GetSelection();
196  wxString prefix = CREATE_NET;
197 
198  if( selection >= 0 )
199  selectedNetName = m_listBox->GetString( (unsigned) selection );
200 
201  auto it = m_unescapedNetNameMap.find( selectedNetName );
202 
203  if( it != m_unescapedNetNameMap.end() )
204  escapedNetName = it->second;
205  else // shouldn't happen....
206  escapedNetName = selectedNetName;
207 
208  Dismiss();
209 
210  if( escapedNetName.IsEmpty() || escapedNetName == m_indeterminateLabel )
211  {
212  m_selectedNetcode = -1;
213  GetComboCtrl()->SetValue( m_indeterminateLabel );
214  }
215  else if( escapedNetName == NO_NET )
216  {
217  m_selectedNetcode = 0;
218  GetComboCtrl()->SetValue( NO_NET );
219  }
220  else if( escapedNetName.StartsWith( CREATE_NET, &remainingName ) &&
221  !remainingName.IsEmpty() )
222  {
223  // Remove the first character ':' and all whitespace
224  remainingName = remainingName.Mid( 1 ).Trim().Trim( false );
225 
226  BOARD* board = m_netinfoList->GetParent();
227  NETINFO_ITEM *newnet = new NETINFO_ITEM( m_board, remainingName, 0 );
228 
229  // add the new netinfo through the board's function so that
230  // board listeners get notified and things stay in sync.
231  if( board != nullptr )
232  board->Add( newnet );
233  else
234  m_netinfoList->AppendNet( newnet );
235 
236  rebuildList();
237 
238  if( newnet->GetNetCode() > 0 )
239  {
240  m_selectedNetcode = newnet->GetNetCode();
241  GetComboCtrl()->SetValue( UnescapeString( remainingName ) );
242  }
243  else
244  {
245  // This indicates that the NETINFO_ITEM was not successfully appended
246  // to the list for unknown reasons
247  if( board != nullptr )
248  board->Remove( newnet );
249  else
250  m_netinfoList->RemoveNet( newnet );
251 
252  delete newnet;
253  }
254  }
255  else
256  {
257  NETINFO_ITEM* netInfo = m_netinfoList->GetNetItem( escapedNetName );
258 
259  if( netInfo == nullptr || netInfo->GetNetCode() == 0 )
260  {
261  m_selectedNetcode = 0;
262  GetComboCtrl()->SetValue( NO_NET );
263  }
264  else
265  {
266  m_selectedNetcode = netInfo->GetNetCode();
267  GetComboCtrl()->SetValue( UnescapeString( escapedNetName ) );
268  }
269  }
270 
271  wxCommandEvent changeEvent( NET_SELECTED );
272  wxPostEvent( GetComboCtrl(), changeEvent );
273  }
274 
275 protected:
276  wxSize updateSize()
277  {
278  int listTop = m_listBox->GetRect().y;
279  int itemHeight = KIUI::GetTextSize( wxT( "Xy" ), this ).y + LIST_ITEM_PADDING;
280  int listHeight = m_listBox->GetCount() * itemHeight + LIST_PADDING;
281 
282  if( listTop + listHeight >= m_maxPopupHeight )
283  listHeight = m_maxPopupHeight - listTop - 1;
284 
285  int listWidth = m_minPopupWidth;
286 
287  for( size_t i = 0; i < m_listBox->GetCount(); ++i )
288  {
289  int itemWidth = KIUI::GetTextSize( m_listBox->GetString( i ), m_listBox ).x;
290  listWidth = std::max( listWidth, itemWidth + LIST_PADDING * 3 );
291  }
292 
293  wxSize listSize( listWidth, listHeight );
294  wxSize popupSize( listWidth, listTop + listHeight );
295 
296  SetSize( popupSize ); // us
297  GetParent()->SetSize( popupSize ); // the window that wxComboCtrl put us in
298 
299  m_listBox->SetMinSize( listSize );
300  m_listBox->SetSize( listSize );
301 
302  return popupSize;
303  }
304 
305  void rebuildList()
306  {
307  wxArrayString netNames;
308  wxString netstring = m_filterCtrl->GetValue().MakeLower();
309  wxString filter = netstring;
310 
311  m_unescapedNetNameMap.clear();
312 
313  if( !filter.IsEmpty() )
314  filter = wxT( "*" ) + filter + wxT( "*" );
315 
316  for( NETINFO_ITEM* netinfo : *m_netinfoList )
317  {
318  if( netinfo->GetNetCode() > 0 && netinfo->IsCurrent() )
319  {
320  wxString netname = UnescapeString( netinfo->GetNetname() );
321 
322  if( filter.IsEmpty() || wxString( netname ).MakeLower().Matches( filter ) )
323  {
324  netNames.push_back( netname );
325  m_unescapedNetNameMap[ netname ] = netinfo->GetNetname();
326  }
327  }
328  }
329 
330  std::sort( netNames.begin(), netNames.end() );
331 
332  // Special handling for <no net>
333  if( filter.IsEmpty() || wxString( NO_NET ).MakeLower().Matches( filter ) )
334  netNames.insert( netNames.begin(), NO_NET );
335 
336  if( !filter.IsEmpty() && netNames.IsEmpty() )
337  {
338  wxString newnet = wxString::Format( "%s: %s", CREATE_NET, netstring );
339  netNames.insert( netNames.begin(), newnet );
340  }
341 
342  if( !m_indeterminateLabel.IsEmpty() )
343  netNames.push_back( m_indeterminateLabel );
344 
345  m_listBox->Set( netNames );
346  }
347 
348  void onIdle( wxIdleEvent& aEvent )
349  {
350  // Generate synthetic (but reliable) MouseMoved events
351  static wxPoint lastPos;
352  wxPoint screenPos = wxGetMousePosition();
353 
354  if( screenPos != lastPos )
355  {
356  lastPos = screenPos;
357  onMouseMoved( screenPos );
358  }
359 
360  if( m_focusHandler )
361  {
362  m_filterCtrl->PushEventHandler( m_focusHandler );
363  m_focusHandler = nullptr;
364  }
365  }
366 
367  // Hot-track the mouse (for focus and listbox selection)
368  void onMouseMoved( const wxPoint aScreenPos )
369  {
370  if( m_listBox->GetScreenRect().Contains( aScreenPos ) )
371  {
373 
374  wxPoint relativePos = m_listBox->ScreenToClient( aScreenPos );
375  int item = m_listBox->HitTest( relativePos );
376 
377  if( item >= 0 )
378  m_listBox->SetSelection( item );
379  }
380  else if( m_filterCtrl->GetScreenRect().Contains( aScreenPos ) )
381  {
383  }
384  }
385 
386  void onMouseClick( wxMouseEvent& aEvent )
387  {
388  // Accept a click event from anywhere. Different platform implementations have
389  // different foibles with regard to transient popups and their children.
390 
391  if( aEvent.GetEventObject() == m_listBox )
392  {
393  m_listBox->SetSelection( m_listBox->HitTest( aEvent.GetPosition() ) );
394  Accept();
395  return;
396  }
397 
398  wxWindow* window = dynamic_cast<wxWindow*>( aEvent.GetEventObject() );
399 
400  if( window )
401  {
402  wxPoint screenPos = window->ClientToScreen( aEvent.GetPosition() );
403 
404  if( m_listBox->GetScreenRect().Contains( screenPos ) )
405  {
406  wxPoint localPos = m_listBox->ScreenToClient( screenPos );
407 
408  m_listBox->SetSelection( m_listBox->HitTest( localPos ) );
409  Accept();
410  }
411  }
412  }
413 
414  void onKeyDown( wxKeyEvent& aEvent )
415  {
416  switch( aEvent.GetKeyCode() )
417  {
418  // Control keys go to the parent combobox
419  case WXK_TAB:
420  Dismiss();
421 
422  m_parent->NavigateIn( ( aEvent.ShiftDown() ? 0 : wxNavigationKeyEvent::IsForward ) |
423  ( aEvent.ControlDown() ? wxNavigationKeyEvent::WinChange : 0 ) );
424  break;
425 
426  case WXK_ESCAPE:
427  Dismiss();
428  break;
429 
430  case WXK_RETURN:
431  Accept();
432  break;
433 
434  // Arrows go to the list box
435  case WXK_DOWN:
436  case WXK_NUMPAD_DOWN:
438  m_listBox->SetSelection( std::min( m_listBox->GetSelection() + 1, (int) m_listBox->GetCount() - 1 ) );
439  break;
440 
441  case WXK_UP:
442  case WXK_NUMPAD_UP:
444  m_listBox->SetSelection( std::max( m_listBox->GetSelection() - 1, 0 ) );
445  break;
446 
447  // Everything else goes to the filter textbox
448  default:
449  if( !m_filterCtrl->HasFocus() )
450  {
452 
453  // Because we didn't have focus we missed our chance to have the native widget
454  // handle the keystroke. We'll have to do the first character ourselves.
455  doStartingKey( aEvent );
456  }
457  else
458  {
459  // On some platforms a wxComboFocusHandler will have been pushed which
460  // unhelpfully gives the event right back to the popup. Make sure the filter
461  // control is going to get the event.
462  if( m_filterCtrl->GetEventHandler() != m_filterCtrl )
463  m_focusHandler = m_filterCtrl->PopEventHandler();
464 
465  aEvent.Skip();
466  }
467  break;
468  }
469  }
470 
471  void onEnter( wxCommandEvent& aEvent )
472  {
473  Accept();
474  }
475 
476  void onFilterEdit( wxCommandEvent& aEvent )
477  {
478  rebuildList();
479  updateSize();
480 
481  if( m_listBox->GetCount() > 0 )
482  m_listBox->SetSelection( 0 );
483  }
484 
485  void doStartingKey( wxKeyEvent& aEvent )
486  {
487  if( aEvent.GetKeyCode() == WXK_BACK )
488  {
489  const long pos = m_filterCtrl->GetLastPosition();
490  m_filterCtrl->Remove( pos - 1, pos );
491  }
492  else
493  {
494  bool isPrintable;
495  int ch = aEvent.GetUnicodeKey();
496 
497  if( ch != WXK_NONE )
498  isPrintable = true;
499  else
500  {
501  ch = aEvent.GetKeyCode();
502  isPrintable = ch > WXK_SPACE && ch < WXK_START;
503  }
504 
505  if( isPrintable )
506  {
507  wxString text( static_cast<wxChar>( ch ) );
508 
509  // wxCHAR_HOOK chars have been converted to uppercase.
510  if( !aEvent.ShiftDown() )
511  text.MakeLower();
512 
513  m_filterCtrl->AppendText( text );
514  }
515  }
516  }
517 
518  void doSetFocus( wxWindow* aWindow )
519  {
520  KIPLATFORM::UI::ForceFocus( aWindow );
521  }
522 
523 protected:
524  wxTextValidator* m_filterValidator;
525  wxTextCtrl* m_filterCtrl;
526  wxListBox* m_listBox;
529 
533 
535 
536  std::map<wxString, wxString> m_unescapedNetNameMap;
537 
538  wxEvtHandler* m_focusHandler;
539 };
540 
541 
542 NET_SELECTOR::NET_SELECTOR( wxWindow *parent, wxWindowID id, const wxPoint &pos,
543  const wxSize &size, long style ) :
544  wxComboCtrl( parent, id, wxEmptyString, pos, size, style|wxCB_READONLY|wxTE_PROCESS_ENTER )
545 {
546  UseAltPopupWindow();
547 
549  SetPopupControl( m_netSelectorPopup );
550 
551  Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR::onKeyDown ), NULL, this );
552 }
553 
554 
556 {
557  Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR::onKeyDown ), NULL, this );
558 }
559 
560 
561 void NET_SELECTOR::onKeyDown( wxKeyEvent& aEvt )
562 {
563  int key = aEvt.GetKeyCode();
564 
565  if( IsPopupShown() )
566  {
567  // If the popup is shown then it's CHAR_HOOK should be eating these before they
568  // even get to us. But just to be safe, we go ahead and skip.
569  aEvt.Skip();
570  }
571 
572  // Shift-return accepts dialog
573  else if( key == WXK_RETURN && aEvt.ShiftDown() )
574  {
575  wxPostEvent( m_parent, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
576  }
577 
578  // Return, arrow-down and space-bar all open popup
579  else if( key == WXK_RETURN || key == WXK_DOWN || key == WXK_NUMPAD_DOWN || key == WXK_SPACE )
580  {
581  Popup();
582  }
583 
584  // Non-control characters go to filterbox in popup
585  else if( key > WXK_SPACE && key < WXK_START )
586  {
587  Popup();
589  }
590 
591  else
592  {
593  aEvt.Skip();
594  }
595 }
596 
597 
599 {
600  m_netSelectorPopup->SetNetInfo( aNetInfoList );
601 }
602 
603 
604 void NET_SELECTOR::SetIndeterminateString( const wxString& aString )
605 {
606  m_indeterminateString = aString;
608 }
609 
610 
612 {
613  m_netSelectorPopup->SetBoard( aBoard );
614 }
615 
616 
618 {
621 }
622 
623 
624 void NET_SELECTOR::SetSelectedNet( const wxString& aNetname )
625 {
626  m_netSelectorPopup->SetSelectedNet( aNetname );
628 }
629 
630 
632 {
634 }
635 
636 
638 {
640  SetValue( m_indeterminateString );
641 }
642 
643 
645 {
647 }
648 
649 
651 {
653 }
654 
void SetIndeterminateString(const wxString &aString)
#define CREATE_NET
void SetSelectedNet(const wxString &aNetname)
BOARD * GetParent() const
Definition: netinfo.h:447
void onIdle(wxIdleEvent &aEvent)
void onFilterEdit(wxCommandEvent &aEvent)
wxTextValidator * m_filterValidator
void SetBoard(BOARD *aBoard)
wxString GetStringValue() const override
void SetNetInfo(NETINFO_LIST *aNetInfoList)
void SetSelectedNetcode(int aNetcode)
int GetSelectedNetcode()
wxEvtHandler * m_focusHandler
NET_SELECTOR(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0)
wxSize GetAdjustedSize(int aMinWidth, int aPrefHeight, int aMaxHeight) override
NETINFO_LIST * m_netinfoList
std::map< wxString, wxString > m_unescapedNetNameMap
wxString GetSelectedNetname()
bool Create(wxWindow *aParent) override
void SetBoard(BOARD *aBoard)
void AppendNet(NETINFO_ITEM *aNewElement)
Add aNewElement to the end of the net list.
wxWindow * GetControl() override
void onKeyDown(wxKeyEvent &aEvent)
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:563
void RemoveNet(NETINFO_ITEM *aNet)
Remove a net from the net list.
#define NULL
wxDEFINE_EVENT(NET_SELECTED, wxCommandEvent)
wxSize GetTextSize(const wxString &aSingleLine, wxWindow *aWindow)
Return the size of aSingleLine of text when it is rendered in aWindow using whatever font is currentl...
Definition: ui_common.cpp:58
void onKeyDown(wxKeyEvent &aEvt)
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:315
void SetSelectedNetcode(int aNetcode)
bool IsIndeterminate()
const wxString & GetNetname() const
Definition: netinfo.h:119
#define LIST_ITEM_PADDING
void OnPopup() override
void SetNetInfo(NETINFO_LIST *aNetInfoList)
void SetStringValue(const wxString &aNetName) override
void SetIndeterminateLabel(const wxString &aIndeterminateLabel)
#define NO_NET
wxString m_indeterminateString
Definition: net_selector.h:69
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 doStartingKey(wxKeyEvent &aEvent)
Handle the data for a net.
Definition: netinfo.h:64
void SetIndeterminate()
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
void onEnter(wxCommandEvent &aEvent)
void ForceFocus(wxWindow *aWindow)
Pass the current focus to the window.
Definition: gtk/ui.cpp:44
#define _(s)
Definition: 3d_actions.cpp:33
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:150
void SetSelectedNet(const wxString &aNetname)
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:665
~NET_SELECTOR() override
#define LIST_PADDING
void OnStartingKey(wxKeyEvent &aEvent)
void doSetFocus(wxWindow *aWindow)
NETINFO_ITEM * GetNetItem(int aNetCode) const
void onMouseClick(wxMouseEvent &aEvent)
NET_SELECTOR_COMBOPOPUP * m_netSelectorPopup
Definition: net_selector.h:68
void onMouseMoved(const wxPoint aScreenPos)
int GetNetCode() const
Definition: netinfo.h:113