2018-08-19 16:11:58 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2021-07-29 09:56:22 +00:00
|
|
|
#include <string_utils.h>
|
2020-06-03 14:58:54 +00:00
|
|
|
#include <kiplatform/ui.h>
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2021-06-06 19:03:10 +00:00
|
|
|
#include <widgets/ui_common.h>
|
2018-08-19 16:11:58 +00:00
|
|
|
#include <widgets/net_selector.h>
|
|
|
|
|
2020-11-12 20:19:22 +00:00
|
|
|
#include <board.h>
|
2018-08-19 16:11:58 +00:00
|
|
|
#include <netinfo.h>
|
|
|
|
#include <wx/arrstr.h>
|
2018-09-21 19:39:28 +00:00
|
|
|
#include <wx/display.h>
|
2021-05-01 07:50:29 +00:00
|
|
|
#include <wx/valtext.h>
|
|
|
|
#include <wx/listbox.h>
|
|
|
|
#include <wx/stattext.h>
|
|
|
|
#include <wx/sizer.h>
|
|
|
|
#include <wx/textctrl.h>
|
2021-05-01 18:04:11 +00:00
|
|
|
#include <wx/panel.h>
|
2021-05-01 07:50:29 +00:00
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
|
|
|
|
wxDEFINE_EVENT( NET_SELECTED, wxCommandEvent );
|
|
|
|
|
2018-09-27 12:50:06 +00:00
|
|
|
#if defined( __WXOSX_MAC__ )
|
|
|
|
#define POPUP_PADDING 2
|
|
|
|
#define LIST_ITEM_PADDING 5
|
|
|
|
#define LIST_PADDING 5
|
|
|
|
#elif defined( __WXMSW__ )
|
|
|
|
#define POPUP_PADDING 0
|
|
|
|
#define LIST_ITEM_PADDING 2
|
|
|
|
#define LIST_PADDING 5
|
2018-09-26 21:50:27 +00:00
|
|
|
#else
|
2018-09-27 12:50:06 +00:00
|
|
|
#define POPUP_PADDING 0
|
2018-10-11 17:51:48 +00:00
|
|
|
#define LIST_ITEM_PADDING 6
|
2018-09-27 12:50:06 +00:00
|
|
|
#define LIST_PADDING 5
|
2018-09-26 21:50:27 +00:00
|
|
|
#endif
|
2018-08-19 16:11:58 +00:00
|
|
|
|
|
|
|
#define NO_NET _( "<no net>" )
|
2020-03-04 22:07:18 +00:00
|
|
|
#define CREATE_NET _( "<create net>" )
|
2018-08-19 16:11:58 +00:00
|
|
|
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
class NET_SELECTOR_COMBOPOPUP : public wxPanel, public wxComboPopup
|
2018-09-27 19:42:59 +00:00
|
|
|
{
|
|
|
|
public:
|
2018-09-29 21:04:32 +00:00
|
|
|
NET_SELECTOR_COMBOPOPUP() :
|
2019-02-17 09:36:05 +00:00
|
|
|
m_filterValidator( nullptr ),
|
|
|
|
m_filterCtrl( nullptr ),
|
|
|
|
m_listBox( nullptr ),
|
2018-09-29 21:04:32 +00:00
|
|
|
m_minPopupWidth( -1 ),
|
|
|
|
m_maxPopupHeight( 1000 ),
|
|
|
|
m_netinfoList( nullptr ),
|
2020-03-04 22:07:18 +00:00
|
|
|
m_board( nullptr ),
|
2018-09-29 22:05:43 +00:00
|
|
|
m_selectedNetcode( 0 ),
|
|
|
|
m_focusHandler( nullptr )
|
2018-09-29 21:04:32 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
bool Create(wxWindow* aParent) override
|
2018-09-27 19:42:59 +00:00
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
wxPanel::Create( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSIMPLE_BORDER );
|
2018-09-27 19:42:59 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxBoxSizer* mainSizer;
|
|
|
|
mainSizer = new wxBoxSizer( wxVERTICAL );
|
2018-09-27 19:42:59 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxStaticText* filterLabel = new wxStaticText( this, wxID_ANY, _( "Filter:" ) );
|
|
|
|
mainSizer->Add( filterLabel, 0, wxEXPAND, 0 );
|
2018-09-27 19:42:59 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
m_filterCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition,
|
2018-09-29 00:21:52 +00:00
|
|
|
wxDefaultSize, wxTE_PROCESS_ENTER );
|
2018-10-10 17:58:19 +00:00
|
|
|
m_filterValidator = new wxTextValidator( wxFILTER_EXCLUDE_CHAR_LIST );
|
2018-10-10 18:48:34 +00:00
|
|
|
m_filterValidator->SetCharExcludes( " " );
|
2018-10-10 17:58:19 +00:00
|
|
|
m_filterCtrl->SetValidator( *m_filterValidator );
|
2018-09-29 21:04:32 +00:00
|
|
|
mainSizer->Add( m_filterCtrl, 0, wxEXPAND, 0 );
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
m_listBox = new wxListBox( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr,
|
2018-09-29 21:04:32 +00:00
|
|
|
wxLB_SINGLE|wxLB_NEEDED_SB );
|
|
|
|
mainSizer->Add( m_listBox, 0, wxEXPAND|wxTOP, 2 );
|
2018-09-28 21:39:08 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
SetSizer( mainSizer );
|
2018-08-19 16:11:58 +00:00
|
|
|
Layout();
|
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
Connect( wxEVT_IDLE, wxIdleEventHandler( NET_SELECTOR_COMBOPOPUP::onIdle ), nullptr, this );
|
|
|
|
Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR_COMBOPOPUP::onKeyDown ), nullptr, this );
|
|
|
|
Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( NET_SELECTOR_COMBOPOPUP::onMouseClick ), nullptr, this );
|
|
|
|
m_listBox->Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( NET_SELECTOR_COMBOPOPUP::onMouseClick ), nullptr, this );
|
|
|
|
m_filterCtrl->Connect( wxEVT_TEXT, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onFilterEdit ), nullptr, this );
|
|
|
|
m_filterCtrl->Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onEnter ), nullptr, this );
|
2018-09-29 00:21:52 +00:00
|
|
|
|
|
|
|
// <enter> in a ListBox comes in as a double-click on GTK
|
2021-04-22 21:20:34 +00:00
|
|
|
m_listBox->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( NET_SELECTOR_COMBOPOPUP::onEnter ), nullptr, this );
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
return true;
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxWindow *GetControl() override { return this; }
|
2018-09-29 00:21:52 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void SetStringValue( const wxString& aNetName ) override
|
|
|
|
{
|
|
|
|
// shouldn't be here (combo is read-only)
|
2018-09-22 10:51:18 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxString GetStringValue() const override
|
2018-08-19 16:11:58 +00:00
|
|
|
{
|
2020-05-05 15:40:18 +00:00
|
|
|
if( m_selectedNetcode == -1 )
|
|
|
|
return m_indeterminateLabel;
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
NETINFO_ITEM* netInfo = m_netinfoList->GetNetItem( m_selectedNetcode );
|
|
|
|
|
2020-12-08 13:02:08 +00:00
|
|
|
if( netInfo && netInfo->GetNetCode() > 0 )
|
2018-09-29 21:04:32 +00:00
|
|
|
return netInfo->GetNetname();
|
|
|
|
|
|
|
|
return NO_NET;
|
2018-09-26 20:25:27 +00:00
|
|
|
}
|
2018-09-25 16:53:04 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void SetNetInfo( NETINFO_LIST* aNetInfoList )
|
2018-09-26 20:25:27 +00:00
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
m_netinfoList = aNetInfoList;
|
|
|
|
rebuildList();
|
2018-09-26 20:25:27 +00:00
|
|
|
}
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2020-05-05 15:40:18 +00:00
|
|
|
void SetIndeterminateLabel( const wxString& aIndeterminateLabel )
|
|
|
|
{
|
|
|
|
m_indeterminateLabel = aIndeterminateLabel;
|
|
|
|
rebuildList();
|
|
|
|
}
|
|
|
|
|
2020-03-04 22:07:18 +00:00
|
|
|
void SetBoard( BOARD* aBoard )
|
|
|
|
{
|
|
|
|
m_board = aBoard;
|
|
|
|
}
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void SetIndeterminate() { m_selectedNetcode = -1; }
|
|
|
|
bool IsIndeterminate() { return m_selectedNetcode == -1; }
|
2018-09-27 19:42:59 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void SetSelectedNetcode( int aNetcode ) { m_selectedNetcode = aNetcode; }
|
|
|
|
int GetSelectedNetcode() { return m_selectedNetcode; }
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-10-10 17:42:27 +00:00
|
|
|
void SetSelectedNet( const wxString& aNetname )
|
|
|
|
{
|
|
|
|
if( m_netinfoList && m_netinfoList->GetNetItem( aNetname ) )
|
2020-12-08 13:02:08 +00:00
|
|
|
m_selectedNetcode = m_netinfoList->GetNetItem( aNetname )->GetNetCode();
|
2018-10-10 17:42:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
wxString GetSelectedNetname()
|
|
|
|
{
|
|
|
|
if( m_netinfoList && m_netinfoList->GetNetItem( m_selectedNetcode ) )
|
|
|
|
return m_netinfoList->GetNetItem( m_selectedNetcode )->GetNetname();
|
|
|
|
else
|
|
|
|
return wxEmptyString;
|
|
|
|
}
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxSize GetAdjustedSize( int aMinWidth, int aPrefHeight, int aMaxHeight ) override
|
|
|
|
{
|
|
|
|
// Called when the popup is first shown. Stash the minWidth and maxHeight so we
|
|
|
|
// can use them later when refreshing the sizes after filter changes.
|
|
|
|
m_minPopupWidth = aMinWidth;
|
|
|
|
m_maxPopupHeight = aMaxHeight;
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
return updateSize();
|
2018-09-26 20:25:27 +00:00
|
|
|
}
|
2018-09-21 19:39:28 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void OnPopup() override
|
2018-09-26 20:25:27 +00:00
|
|
|
{
|
2020-11-22 18:19:50 +00:00
|
|
|
// While it can sometimes be useful to keep the filter, it's always unexpected.
|
2018-10-11 10:21:28 +00:00
|
|
|
// Better to clear it.
|
|
|
|
m_filterCtrl->Clear();
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
// The updateSize() call in GetAdjustedSize() leaves the height off-by-one for
|
|
|
|
// some reason, so do it again.
|
|
|
|
updateSize();
|
|
|
|
}
|
2018-09-21 19:39:28 +00:00
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
void OnStartingKey( wxKeyEvent& aEvent )
|
|
|
|
{
|
|
|
|
doSetFocus( m_filterCtrl );
|
|
|
|
doStartingKey( aEvent );
|
|
|
|
}
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
void Accept()
|
|
|
|
{
|
2020-03-04 22:07:18 +00:00
|
|
|
wxString selectedNetName;
|
2020-11-22 18:19:50 +00:00
|
|
|
wxString escapedNetName;
|
2020-03-04 22:07:18 +00:00
|
|
|
wxString remainingName;
|
|
|
|
int selection = m_listBox->GetSelection();
|
2018-09-29 21:04:32 +00:00
|
|
|
|
|
|
|
if( selection >= 0 )
|
|
|
|
selectedNetName = m_listBox->GetString( (unsigned) selection );
|
2018-09-28 21:39:08 +00:00
|
|
|
|
2020-11-22 18:19:50 +00:00
|
|
|
auto it = m_unescapedNetNameMap.find( selectedNetName );
|
|
|
|
|
|
|
|
if( it != m_unescapedNetNameMap.end() )
|
|
|
|
escapedNetName = it->second;
|
|
|
|
else // shouldn't happen....
|
|
|
|
escapedNetName = selectedNetName;
|
|
|
|
|
|
|
|
Dismiss();
|
|
|
|
|
|
|
|
if( escapedNetName.IsEmpty() || escapedNetName == m_indeterminateLabel )
|
2018-09-29 21:04:32 +00:00
|
|
|
{
|
|
|
|
m_selectedNetcode = -1;
|
2020-05-05 15:40:18 +00:00
|
|
|
GetComboCtrl()->SetValue( m_indeterminateLabel );
|
2018-09-29 21:04:32 +00:00
|
|
|
}
|
2020-11-22 18:19:50 +00:00
|
|
|
else if( escapedNetName == NO_NET )
|
2018-09-29 21:04:32 +00:00
|
|
|
{
|
|
|
|
m_selectedNetcode = 0;
|
|
|
|
GetComboCtrl()->SetValue( NO_NET );
|
|
|
|
}
|
2020-11-22 18:19:50 +00:00
|
|
|
else if( escapedNetName.StartsWith( CREATE_NET, &remainingName ) &&
|
2020-03-04 22:07:18 +00:00
|
|
|
!remainingName.IsEmpty() )
|
|
|
|
{
|
|
|
|
// Remove the first character ':' and all whitespace
|
|
|
|
remainingName = remainingName.Mid( 1 ).Trim().Trim( false );
|
2020-04-12 19:29:16 +00:00
|
|
|
|
|
|
|
BOARD* board = m_netinfoList->GetParent();
|
2020-03-04 22:07:18 +00:00
|
|
|
NETINFO_ITEM *newnet = new NETINFO_ITEM( m_board, remainingName, 0 );
|
|
|
|
|
2020-04-12 19:29:16 +00:00
|
|
|
// add the new netinfo through the board's function so that
|
|
|
|
// board listeners get notified and things stay in sync.
|
|
|
|
if( board != nullptr )
|
|
|
|
board->Add( newnet );
|
|
|
|
else
|
|
|
|
m_netinfoList->AppendNet( newnet );
|
|
|
|
|
2020-03-04 22:07:18 +00:00
|
|
|
rebuildList();
|
|
|
|
|
2020-12-08 13:02:08 +00:00
|
|
|
if( newnet->GetNetCode() > 0 )
|
2020-03-04 22:07:18 +00:00
|
|
|
{
|
2020-12-08 13:02:08 +00:00
|
|
|
m_selectedNetcode = newnet->GetNetCode();
|
2020-11-22 18:19:50 +00:00
|
|
|
GetComboCtrl()->SetValue( UnescapeString( remainingName ) );
|
2020-03-04 22:07:18 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This indicates that the NETINFO_ITEM was not successfully appended
|
|
|
|
// to the list for unknown reasons
|
2020-04-12 19:29:16 +00:00
|
|
|
if( board != nullptr )
|
|
|
|
board->Remove( newnet );
|
|
|
|
else
|
|
|
|
m_netinfoList->RemoveNet( newnet );
|
|
|
|
|
2020-03-04 22:07:18 +00:00
|
|
|
delete newnet;
|
|
|
|
}
|
|
|
|
}
|
2018-09-29 21:04:32 +00:00
|
|
|
else
|
|
|
|
{
|
2020-11-22 18:19:50 +00:00
|
|
|
NETINFO_ITEM* netInfo = m_netinfoList->GetNetItem( escapedNetName );
|
2018-10-19 11:28:34 +00:00
|
|
|
|
2020-12-08 13:02:08 +00:00
|
|
|
if( netInfo == nullptr || netInfo->GetNetCode() == 0 )
|
2018-10-19 11:28:34 +00:00
|
|
|
{
|
|
|
|
m_selectedNetcode = 0;
|
|
|
|
GetComboCtrl()->SetValue( NO_NET );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-12-08 13:02:08 +00:00
|
|
|
m_selectedNetcode = netInfo->GetNetCode();
|
2020-11-22 18:19:50 +00:00
|
|
|
GetComboCtrl()->SetValue( UnescapeString( escapedNetName ) );
|
2018-10-19 11:28:34 +00:00
|
|
|
}
|
2018-09-28 18:36:31 +00:00
|
|
|
}
|
2018-09-29 21:04:32 +00:00
|
|
|
|
|
|
|
wxCommandEvent changeEvent( NET_SELECTED );
|
|
|
|
wxPostEvent( GetComboCtrl(), changeEvent );
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-09-29 21:04:32 +00:00
|
|
|
wxSize updateSize()
|
2018-08-19 16:11:58 +00:00
|
|
|
{
|
2020-10-26 02:29:53 +00:00
|
|
|
int listTop = m_listBox->GetRect().y;
|
2020-10-25 16:41:38 +00:00
|
|
|
int itemHeight = KIUI::GetTextSize( wxT( "Xy" ), this ).y + LIST_ITEM_PADDING;
|
2020-10-26 02:29:53 +00:00
|
|
|
int listHeight = m_listBox->GetCount() * itemHeight + LIST_PADDING;
|
2018-08-19 16:11:58 +00:00
|
|
|
|
|
|
|
if( listTop + listHeight >= m_maxPopupHeight )
|
|
|
|
listHeight = m_maxPopupHeight - listTop - 1;
|
|
|
|
|
2018-09-28 21:39:08 +00:00
|
|
|
int listWidth = m_minPopupWidth;
|
|
|
|
|
|
|
|
for( size_t i = 0; i < m_listBox->GetCount(); ++i )
|
|
|
|
{
|
2020-10-25 16:41:38 +00:00
|
|
|
int itemWidth = KIUI::GetTextSize( m_listBox->GetString( i ), m_listBox ).x;
|
2018-09-28 21:39:08 +00:00
|
|
|
listWidth = std::max( listWidth, itemWidth + LIST_PADDING * 3 );
|
|
|
|
}
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
wxSize listSize( listWidth, listHeight );
|
|
|
|
wxSize popupSize( listWidth, listTop + listHeight );
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
SetSize( popupSize ); // us
|
|
|
|
GetParent()->SetSize( popupSize ); // the window that wxComboCtrl put us in
|
|
|
|
|
|
|
|
m_listBox->SetMinSize( listSize );
|
|
|
|
m_listBox->SetSize( listSize );
|
|
|
|
|
|
|
|
return popupSize;
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void rebuildList()
|
|
|
|
{
|
|
|
|
wxArrayString netNames;
|
2021-04-09 14:57:38 +00:00
|
|
|
wxString netstring = m_filterCtrl->GetValue().Trim().Trim( false );
|
|
|
|
wxString filter = netstring.Lower();
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2020-11-22 18:19:50 +00:00
|
|
|
m_unescapedNetNameMap.clear();
|
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
if( !filter.IsEmpty() )
|
|
|
|
filter = wxT( "*" ) + filter + wxT( "*" );
|
|
|
|
|
|
|
|
for( NETINFO_ITEM* netinfo : *m_netinfoList )
|
|
|
|
{
|
2020-12-08 13:02:08 +00:00
|
|
|
if( netinfo->GetNetCode() > 0 && netinfo->IsCurrent() )
|
2018-10-18 09:35:59 +00:00
|
|
|
{
|
2018-10-19 11:28:34 +00:00
|
|
|
wxString netname = UnescapeString( netinfo->GetNetname() );
|
|
|
|
|
|
|
|
if( filter.IsEmpty() || wxString( netname ).MakeLower().Matches( filter ) )
|
2020-11-22 18:19:50 +00:00
|
|
|
{
|
2018-10-19 11:28:34 +00:00
|
|
|
netNames.push_back( netname );
|
2020-11-22 18:19:50 +00:00
|
|
|
m_unescapedNetNameMap[ netname ] = netinfo->GetNetname();
|
|
|
|
}
|
2018-10-18 09:35:59 +00:00
|
|
|
}
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
2020-11-22 18:19:50 +00:00
|
|
|
|
2022-03-07 21:03:25 +00:00
|
|
|
std::sort( netNames.begin(), netNames.end(),
|
|
|
|
[]( const wxString& lhs, const wxString& rhs )
|
|
|
|
{
|
|
|
|
return StrNumCmp( lhs, rhs, true /* ignore case */ ) < 0;
|
|
|
|
} );
|
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-10-18 09:35:59 +00:00
|
|
|
// Special handling for <no net>
|
2018-08-19 16:11:58 +00:00
|
|
|
if( filter.IsEmpty() || wxString( NO_NET ).MakeLower().Matches( filter ) )
|
|
|
|
netNames.insert( netNames.begin(), NO_NET );
|
|
|
|
|
2021-04-09 14:57:38 +00:00
|
|
|
if( !filter.IsEmpty() && !m_netinfoList->GetNetItem( netstring ) )
|
2020-03-04 22:07:18 +00:00
|
|
|
{
|
|
|
|
wxString newnet = wxString::Format( "%s: %s", CREATE_NET, netstring );
|
2021-04-09 14:57:38 +00:00
|
|
|
netNames.insert( netNames.end(), newnet );
|
2020-03-04 22:07:18 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 15:40:18 +00:00
|
|
|
if( !m_indeterminateLabel.IsEmpty() )
|
|
|
|
netNames.push_back( m_indeterminateLabel );
|
|
|
|
|
2018-09-28 21:39:08 +00:00
|
|
|
m_listBox->Set( netNames );
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 20:25:27 +00:00
|
|
|
void onIdle( wxIdleEvent& aEvent )
|
|
|
|
{
|
2018-09-27 10:52:58 +00:00
|
|
|
// Generate synthetic (but reliable) MouseMoved events
|
2018-09-26 20:25:27 +00:00
|
|
|
static wxPoint lastPos;
|
2023-09-18 03:13:09 +00:00
|
|
|
wxPoint screenPos = KIPLATFORM::UI::GetMousePosition();
|
2018-09-26 20:25:27 +00:00
|
|
|
|
2018-09-27 10:52:58 +00:00
|
|
|
if( screenPos != lastPos )
|
|
|
|
{
|
2018-09-26 20:25:27 +00:00
|
|
|
lastPos = screenPos;
|
2018-09-27 10:52:58 +00:00
|
|
|
onMouseMoved( screenPos );
|
|
|
|
}
|
2018-09-29 22:05:43 +00:00
|
|
|
|
|
|
|
if( m_focusHandler )
|
|
|
|
{
|
|
|
|
m_filterCtrl->PushEventHandler( m_focusHandler );
|
|
|
|
m_focusHandler = nullptr;
|
|
|
|
}
|
2018-09-27 10:52:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Hot-track the mouse (for focus and listbox selection)
|
|
|
|
void onMouseMoved( const wxPoint aScreenPos )
|
2018-09-27 19:42:59 +00:00
|
|
|
{
|
2018-09-28 21:39:08 +00:00
|
|
|
if( m_listBox->GetScreenRect().Contains( aScreenPos ) )
|
2018-09-26 20:25:27 +00:00
|
|
|
{
|
2018-09-28 21:39:08 +00:00
|
|
|
doSetFocus( m_listBox );
|
2018-09-26 20:25:27 +00:00
|
|
|
|
2018-09-28 21:39:08 +00:00
|
|
|
wxPoint relativePos = m_listBox->ScreenToClient( aScreenPos );
|
|
|
|
int item = m_listBox->HitTest( relativePos );
|
2018-09-26 20:25:27 +00:00
|
|
|
|
|
|
|
if( item >= 0 )
|
2018-09-28 21:39:08 +00:00
|
|
|
m_listBox->SetSelection( item );
|
2018-09-26 20:25:27 +00:00
|
|
|
}
|
2018-09-27 10:52:58 +00:00
|
|
|
else if( m_filterCtrl->GetScreenRect().Contains( aScreenPos ) )
|
2018-09-26 20:25:27 +00:00
|
|
|
{
|
|
|
|
doSetFocus( m_filterCtrl );
|
|
|
|
}
|
2018-09-29 21:04:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void onMouseClick( wxMouseEvent& aEvent )
|
|
|
|
{
|
|
|
|
// Accept a click event from anywhere. Different platform implementations have
|
|
|
|
// different foibles with regard to transient popups and their children.
|
|
|
|
|
|
|
|
if( aEvent.GetEventObject() == m_listBox )
|
2018-09-27 19:42:59 +00:00
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
m_listBox->SetSelection( m_listBox->HitTest( aEvent.GetPosition() ) );
|
|
|
|
Accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxWindow* window = dynamic_cast<wxWindow*>( aEvent.GetEventObject() );
|
|
|
|
|
|
|
|
if( window )
|
|
|
|
{
|
|
|
|
wxPoint screenPos = window->ClientToScreen( aEvent.GetPosition() );
|
|
|
|
|
|
|
|
if( m_listBox->GetScreenRect().Contains( screenPos ) )
|
|
|
|
{
|
|
|
|
wxPoint localPos = m_listBox->ScreenToClient( screenPos );
|
|
|
|
|
|
|
|
m_listBox->SetSelection( m_listBox->HitTest( localPos ) );
|
|
|
|
Accept();
|
|
|
|
}
|
2018-09-27 19:42:59 +00:00
|
|
|
}
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-09-22 10:51:18 +00:00
|
|
|
void onKeyDown( wxKeyEvent& aEvent )
|
|
|
|
{
|
2018-09-26 20:25:27 +00:00
|
|
|
switch( aEvent.GetKeyCode() )
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
// Control keys go to the parent combobox
|
2018-09-27 23:23:22 +00:00
|
|
|
case WXK_TAB:
|
2018-09-29 21:04:32 +00:00
|
|
|
Dismiss();
|
2018-09-27 23:23:22 +00:00
|
|
|
|
|
|
|
m_parent->NavigateIn( ( aEvent.ShiftDown() ? 0 : wxNavigationKeyEvent::IsForward ) |
|
|
|
|
( aEvent.ControlDown() ? wxNavigationKeyEvent::WinChange : 0 ) );
|
|
|
|
break;
|
|
|
|
|
2018-09-26 20:25:27 +00:00
|
|
|
case WXK_ESCAPE:
|
2018-09-29 21:04:32 +00:00
|
|
|
Dismiss();
|
2018-09-26 20:25:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case WXK_RETURN:
|
2023-03-10 21:24:21 +00:00
|
|
|
case WXK_NUMPAD_ENTER:
|
2018-09-29 21:04:32 +00:00
|
|
|
Accept();
|
2018-09-26 20:25:27 +00:00
|
|
|
break;
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
// Arrows go to the list box
|
2018-09-26 20:25:27 +00:00
|
|
|
case WXK_DOWN:
|
|
|
|
case WXK_NUMPAD_DOWN:
|
2018-09-28 21:39:08 +00:00
|
|
|
doSetFocus( m_listBox );
|
|
|
|
m_listBox->SetSelection( std::min( m_listBox->GetSelection() + 1, (int) m_listBox->GetCount() - 1 ) );
|
2018-09-26 20:25:27 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case WXK_UP:
|
|
|
|
case WXK_NUMPAD_UP:
|
2018-09-28 21:39:08 +00:00
|
|
|
doSetFocus( m_listBox );
|
|
|
|
m_listBox->SetSelection( std::max( m_listBox->GetSelection() - 1, 0 ) );
|
2018-09-26 20:25:27 +00:00
|
|
|
break;
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
// Everything else goes to the filter textbox
|
2018-09-26 20:25:27 +00:00
|
|
|
default:
|
2018-09-29 21:04:32 +00:00
|
|
|
if( !m_filterCtrl->HasFocus() )
|
|
|
|
{
|
|
|
|
doSetFocus( m_filterCtrl );
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
// Because we didn't have focus we missed our chance to have the native widget
|
|
|
|
// handle the keystroke. We'll have to do the first character ourselves.
|
|
|
|
doStartingKey( aEvent );
|
2018-09-29 21:04:32 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// On some platforms a wxComboFocusHandler will have been pushed which
|
|
|
|
// unhelpfully gives the event right back to the popup. Make sure the filter
|
|
|
|
// control is going to get the event.
|
|
|
|
if( m_filterCtrl->GetEventHandler() != m_filterCtrl )
|
2018-09-29 22:05:43 +00:00
|
|
|
m_focusHandler = m_filterCtrl->PopEventHandler();
|
2018-09-29 21:04:32 +00:00
|
|
|
|
|
|
|
aEvent.Skip();
|
|
|
|
}
|
2018-09-27 10:02:23 +00:00
|
|
|
break;
|
2018-09-27 19:42:59 +00:00
|
|
|
}
|
2018-09-26 20:25:27 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 00:21:52 +00:00
|
|
|
void onEnter( wxCommandEvent& aEvent )
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
Accept();
|
2018-09-29 00:21:52 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
void onFilterEdit( wxCommandEvent& aEvent )
|
|
|
|
{
|
|
|
|
rebuildList();
|
|
|
|
updateSize();
|
|
|
|
|
|
|
|
if( m_listBox->GetCount() > 0 )
|
|
|
|
m_listBox->SetSelection( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
void doStartingKey( wxKeyEvent& aEvent )
|
2018-09-27 10:52:58 +00:00
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
if( aEvent.GetKeyCode() == WXK_BACK )
|
|
|
|
{
|
|
|
|
const long pos = m_filterCtrl->GetLastPosition();
|
|
|
|
m_filterCtrl->Remove( pos - 1, pos );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool isPrintable;
|
|
|
|
int ch = aEvent.GetUnicodeKey();
|
|
|
|
|
|
|
|
if( ch != WXK_NONE )
|
|
|
|
isPrintable = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ch = aEvent.GetKeyCode();
|
2018-10-10 17:58:19 +00:00
|
|
|
isPrintable = ch > WXK_SPACE && ch < WXK_START;
|
2018-09-29 21:04:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( isPrintable )
|
|
|
|
{
|
|
|
|
wxString text( static_cast<wxChar>( ch ) );
|
|
|
|
|
|
|
|
// wxCHAR_HOOK chars have been converted to uppercase.
|
|
|
|
if( !aEvent.ShiftDown() )
|
|
|
|
text.MakeLower();
|
|
|
|
|
2018-10-09 12:17:17 +00:00
|
|
|
m_filterCtrl->AppendText( text );
|
2018-09-29 21:04:32 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-27 10:52:58 +00:00
|
|
|
}
|
|
|
|
|
2018-09-26 20:25:27 +00:00
|
|
|
void doSetFocus( wxWindow* aWindow )
|
|
|
|
{
|
2020-06-03 14:58:54 +00:00
|
|
|
KIPLATFORM::UI::ForceFocus( aWindow );
|
2018-09-22 10:51:18 +00:00
|
|
|
}
|
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
protected:
|
2018-10-10 17:58:19 +00:00
|
|
|
wxTextValidator* m_filterValidator;
|
|
|
|
wxTextCtrl* m_filterCtrl;
|
|
|
|
wxListBox* m_listBox;
|
|
|
|
int m_minPopupWidth;
|
|
|
|
int m_maxPopupHeight;
|
2018-08-19 16:11:58 +00:00
|
|
|
|
2018-10-10 17:58:19 +00:00
|
|
|
NETINFO_LIST* m_netinfoList;
|
2020-05-05 15:40:18 +00:00
|
|
|
wxString m_indeterminateLabel;
|
2020-03-04 22:07:18 +00:00
|
|
|
BOARD* m_board;
|
2018-09-21 19:39:28 +00:00
|
|
|
|
2018-10-10 17:58:19 +00:00
|
|
|
int m_selectedNetcode;
|
2018-09-29 22:05:43 +00:00
|
|
|
|
2020-11-22 18:19:50 +00:00
|
|
|
std::map<wxString, wxString> m_unescapedNetNameMap;
|
|
|
|
|
2018-10-10 17:58:19 +00:00
|
|
|
wxEvtHandler* m_focusHandler;
|
2018-08-19 16:11:58 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
NET_SELECTOR::NET_SELECTOR( wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
|
|
|
const wxSize &size, long style ) :
|
2018-10-10 11:51:08 +00:00
|
|
|
wxComboCtrl( parent, id, wxEmptyString, pos, size, style|wxCB_READONLY|wxTE_PROCESS_ENTER )
|
2018-09-27 10:52:58 +00:00
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
UseAltPopupWindow();
|
2018-09-27 10:52:58 +00:00
|
|
|
|
2018-09-29 21:04:32 +00:00
|
|
|
m_netSelectorPopup = new NET_SELECTOR_COMBOPOPUP();
|
|
|
|
SetPopupControl( m_netSelectorPopup );
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2021-04-22 21:20:34 +00:00
|
|
|
Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR::onKeyDown ), nullptr, this );
|
2018-10-10 11:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NET_SELECTOR::~NET_SELECTOR()
|
|
|
|
{
|
2021-04-22 21:20:34 +00:00
|
|
|
Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( NET_SELECTOR::onKeyDown ), nullptr, this );
|
2018-10-10 11:51:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NET_SELECTOR::onKeyDown( wxKeyEvent& aEvt )
|
|
|
|
{
|
|
|
|
int key = aEvt.GetKeyCode();
|
|
|
|
|
|
|
|
if( IsPopupShown() )
|
|
|
|
{
|
|
|
|
// If the popup is shown then it's CHAR_HOOK should be eating these before they
|
|
|
|
// even get to us. But just to be safe, we go ahead and skip.
|
|
|
|
aEvt.Skip();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shift-return accepts dialog
|
2023-03-10 21:24:21 +00:00
|
|
|
else if( ( key == WXK_RETURN || key == WXK_NUMPAD_ENTER ) && aEvt.ShiftDown() )
|
2018-10-10 11:51:08 +00:00
|
|
|
{
|
|
|
|
wxPostEvent( m_parent, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return, arrow-down and space-bar all open popup
|
2023-03-10 21:24:21 +00:00
|
|
|
else if( key == WXK_RETURN || key == WXK_NUMPAD_ENTER || key == WXK_DOWN
|
|
|
|
|| key == WXK_NUMPAD_DOWN || key == WXK_SPACE )
|
2018-10-10 11:51:08 +00:00
|
|
|
{
|
|
|
|
Popup();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Non-control characters go to filterbox in popup
|
|
|
|
else if( key > WXK_SPACE && key < WXK_START )
|
|
|
|
{
|
|
|
|
Popup();
|
|
|
|
m_netSelectorPopup->OnStartingKey( aEvt );
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aEvt.Skip();
|
|
|
|
}
|
2018-09-21 19:39:28 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
void NET_SELECTOR::SetNetInfo( NETINFO_LIST* aNetInfoList )
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
m_netSelectorPopup->SetNetInfo( aNetInfoList );
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2020-05-05 15:40:18 +00:00
|
|
|
void NET_SELECTOR::SetIndeterminateString( const wxString& aString )
|
|
|
|
{
|
|
|
|
m_indeterminateString = aString;
|
|
|
|
m_netSelectorPopup->SetIndeterminateLabel( aString );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-04 22:07:18 +00:00
|
|
|
void NET_SELECTOR::SetBoard( BOARD* aBoard )
|
|
|
|
{
|
|
|
|
m_netSelectorPopup->SetBoard( aBoard );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
void NET_SELECTOR::SetSelectedNetcode( int aNetcode )
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
m_netSelectorPopup->SetSelectedNetcode( aNetcode );
|
2020-11-22 18:19:50 +00:00
|
|
|
SetValue( UnescapeString( m_netSelectorPopup->GetStringValue() ) );
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2018-10-10 17:42:27 +00:00
|
|
|
void NET_SELECTOR::SetSelectedNet( const wxString& aNetname )
|
|
|
|
{
|
|
|
|
m_netSelectorPopup->SetSelectedNet( aNetname );
|
2020-11-22 18:19:50 +00:00
|
|
|
SetValue( UnescapeString( m_netSelectorPopup->GetStringValue() ) );
|
2018-10-10 17:42:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wxString NET_SELECTOR::GetSelectedNetname()
|
|
|
|
{
|
|
|
|
return m_netSelectorPopup->GetSelectedNetname();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
void NET_SELECTOR::SetIndeterminate()
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
m_netSelectorPopup->SetIndeterminate();
|
2020-05-05 15:40:18 +00:00
|
|
|
SetValue( m_indeterminateString );
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
bool NET_SELECTOR::IsIndeterminate()
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
return m_netSelectorPopup->IsIndeterminate();
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 11:51:08 +00:00
|
|
|
|
2018-08-19 16:11:58 +00:00
|
|
|
int NET_SELECTOR::GetSelectedNetcode()
|
|
|
|
{
|
2018-09-29 21:04:32 +00:00
|
|
|
return m_netSelectorPopup->GetSelectedNetcode();
|
2018-08-19 16:11:58 +00:00
|
|
|
}
|
|
|
|
|