kicad-source/eeschema/dialogs/dialog_bus_manager.cpp
Jon Evans d7bd4c9b04 Move Eeschema globals to new SCHEMATIC object
Set up a new lineage for SCH_ITEMS to get back to the SCHEMATIC
they live on: Items will all be parented to the SCH_SCREEN that
they are added to, and each SCH_SCREEN will point back to the
SCHEMATIC that it is part of.  Note that this hierarchy is not
the same as the actual schematic hierarchy, which continues to
be managed through SCH_SHEETs and SCH_SHEET_PATHS.
2020-05-18 13:04:56 -04:00

520 lines
17 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2018 CERN
* @author Jon Evans <jon@craftyjon.com>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <wx/tokenzr.h>
#include <invoke_sch_dialog.h>
#include <sch_sheet_path.h>
#include <schematic.h>
#include "dialog_bus_manager.h"
BEGIN_EVENT_TABLE( DIALOG_BUS_MANAGER, DIALOG_SHIM )
EVT_BUTTON( wxID_OK, DIALOG_BUS_MANAGER::OnOkClick )
EVT_BUTTON( wxID_CANCEL, DIALOG_BUS_MANAGER::OnCancelClick )
END_EVENT_TABLE()
DIALOG_BUS_MANAGER::DIALOG_BUS_MANAGER( SCH_EDIT_FRAME* aParent )
: DIALOG_SHIM( aParent, wxID_ANY, _( "Bus Definitions" ),
wxDefaultPosition, wxSize( 640, 480 ),
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_parent( aParent )
{
auto sizer = new wxBoxSizer( wxVERTICAL );
auto buttons = new wxStdDialogButtonSizer();
buttons->AddButton( new wxButton( this, wxID_OK ) );
buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
buttons->Realize();
auto top_container = new wxBoxSizer( wxHORIZONTAL );
auto left_pane = new wxBoxSizer( wxVERTICAL );
auto right_pane = new wxBoxSizer( wxVERTICAL );
// Left pane: alias list
auto lbl_aliases = new wxStaticText( this, wxID_ANY, _( "Bus Aliases" ),
wxDefaultPosition, wxDefaultSize,
wxALIGN_LEFT );
m_bus_list_view = new wxListView( this, wxID_ANY, wxDefaultPosition,
wxSize( 300, 300 ), wxLC_ALIGN_LEFT |
wxLC_NO_HEADER | wxLC_REPORT |
wxLC_SINGLE_SEL );
m_bus_list_view->InsertColumn( 0, "" );
auto lbl_alias_edit = new wxStaticText( this, wxID_ANY, _( "Alias Name" ),
wxDefaultPosition, wxDefaultSize,
wxALIGN_LEFT );
m_bus_edit = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
auto left_button_sizer = new wxBoxSizer( wxHORIZONTAL );
m_btn_add_bus = new wxButton( this, wxID_ANY, _( "Add" ) );
m_btn_rename_bus = new wxButton( this, wxID_ANY, _( "Rename" ) );
m_btn_remove_bus = new wxButton( this, wxID_ANY, _( "Remove" ) );
left_button_sizer->Add( m_btn_add_bus );
left_button_sizer->Add( m_btn_rename_bus );
left_button_sizer->Add( m_btn_remove_bus );
left_pane->Add( lbl_aliases, 0, wxEXPAND | wxALL, 5 );
left_pane->Add( m_bus_list_view, 1, wxEXPAND | wxALL, 5 );
left_pane->Add( lbl_alias_edit, 0, wxEXPAND | wxALL, 5 );
left_pane->Add( m_bus_edit, 0, wxEXPAND | wxALL, 5 );
left_pane->Add( left_button_sizer, 0, wxEXPAND | wxALL, 5 );
// Right pane: signal list
auto lbl_signals = new wxStaticText( this, wxID_ANY, _( "Alias Members" ),
wxDefaultPosition, wxDefaultSize,
wxALIGN_LEFT );
m_signal_list_view = new wxListView( this, wxID_ANY, wxDefaultPosition,
wxSize( 300, 300 ), wxLC_ALIGN_LEFT |
wxLC_NO_HEADER | wxLC_REPORT |
wxLC_SINGLE_SEL );
m_signal_list_view->InsertColumn( 0, "" );
auto lbl_signal_edit = new wxStaticText( this, wxID_ANY, _( "Member Name" ),
wxDefaultPosition, wxDefaultSize,
wxALIGN_LEFT );
m_signal_edit = new wxTextCtrl( this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
auto right_button_sizer = new wxBoxSizer( wxHORIZONTAL );
m_btn_add_signal = new wxButton( this, wxID_ANY, _( "Add" ) );
m_btn_rename_signal = new wxButton( this, wxID_ANY, _( "Rename" ) );
m_btn_remove_signal = new wxButton( this, wxID_ANY, _( "Remove" ) );
right_button_sizer->Add( m_btn_add_signal );
right_button_sizer->Add( m_btn_rename_signal );
right_button_sizer->Add( m_btn_remove_signal );
right_pane->Add( lbl_signals, 0, wxEXPAND | wxALL, 5 );
right_pane->Add( m_signal_list_view, 1, wxEXPAND | wxALL, 5 );
right_pane->Add( lbl_signal_edit, 0, wxEXPAND | wxALL, 5 );
right_pane->Add( m_signal_edit, 0, wxEXPAND | wxALL, 5 );
right_pane->Add( right_button_sizer, 0, wxEXPAND | wxALL, 5 );
top_container->Add( left_pane, 1, wxEXPAND );
top_container->Add( right_pane, 1, wxEXPAND );
sizer->Add( top_container, 1, wxEXPAND | wxALL, 5 );
sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 );
SetSizer( sizer );
// Setup validators
wxTextValidator validator;
validator.SetStyle( wxFILTER_EXCLUDE_CHAR_LIST );
validator.SetCharExcludes( "\r\n\t " );
m_bus_edit->SetValidator( validator );
// Allow spaces in the signal edit, so that you can type in a list of
// signals in the box and it can automatically split them when you add.
validator.SetCharExcludes( "\r\n\t" );
m_signal_edit->SetValidator( validator );
// Setup events
Bind( wxEVT_INIT_DIALOG, &DIALOG_BUS_MANAGER::OnInitDialog, this );
m_bus_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectBus ), NULL, this );
m_bus_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectBus ), NULL, this );
m_signal_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectSignal ), NULL, this );
m_signal_list_view->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
wxListEventHandler( DIALOG_BUS_MANAGER::OnSelectSignal ), NULL, this );
m_btn_add_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddBus ), NULL, this );
m_btn_rename_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRenameBus ), NULL, this );
m_btn_remove_bus->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRemoveBus ), NULL, this );
m_signal_edit->Connect( wxEVT_TEXT_ENTER,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddSignal ), NULL, this );
m_btn_add_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddSignal ), NULL, this );
m_btn_rename_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRenameSignal ), NULL, this );
m_btn_remove_signal->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnRemoveSignal ), NULL, this );
m_bus_edit->Connect( wxEVT_TEXT_ENTER,
wxCommandEventHandler( DIALOG_BUS_MANAGER::OnAddBus ), NULL, this );
// Set initial UI state
m_btn_rename_bus->Disable();
m_btn_remove_bus->Disable();
m_btn_add_signal->Disable();
m_btn_rename_signal->Disable();
m_btn_remove_signal->Disable();
m_bus_edit->SetHint( _( "Bus Alias Name" ) );
m_signal_edit->SetHint( _( "Net or Bus Name" ) );
FinishDialogSettings();
}
void DIALOG_BUS_MANAGER::OnInitDialog( wxInitDialogEvent& aEvent )
{
TransferDataToWindow();
}
bool DIALOG_BUS_MANAGER::TransferDataToWindow()
{
m_aliases.clear();
const SCH_SHEET_LIST& sheets = m_parent->Schematic().GetSheets();
std::vector< std::shared_ptr< BUS_ALIAS > > original_aliases;
// collect aliases from each open sheet
for( unsigned i = 0; i < sheets.size(); i++ )
{
auto sheet_aliases = sheets[i].LastScreen()->GetBusAliases();
original_aliases.insert( original_aliases.end(), sheet_aliases.begin(),
sheet_aliases.end() );
}
original_aliases.erase( std::unique( original_aliases.begin(),
original_aliases.end() ),
original_aliases.end() );
// clone into a temporary working set
int idx = 0;
for( const auto& alias : original_aliases )
{
m_aliases.push_back( alias->Clone() );
auto text = getAliasDisplayText( alias );
m_bus_list_view->InsertItem( idx, text );
m_bus_list_view->SetItemPtrData( idx, wxUIntPtr( m_aliases[idx].get() ) );
idx++;
}
m_bus_list_view->SetColumnWidth( 0, -1 );
return true;
}
void DIALOG_BUS_MANAGER::OnOkClick( wxCommandEvent& aEvent )
{
if( TransferDataFromWindow() )
{
( ( SCH_EDIT_FRAME* )GetParent() )->OnModify();
EndModal( wxID_OK );
}
}
void DIALOG_BUS_MANAGER::OnCancelClick( wxCommandEvent& aEvent )
{
EndModal( wxID_CANCEL );
}
bool DIALOG_BUS_MANAGER::TransferDataFromWindow()
{
// Since we have a clone of all the data, and it is from potentially
// multiple screens, the way this works is to rebuild each screen's aliases.
// A list of screens is stored here so that the screen's alias list is only
// cleared once.
std::unordered_set< SCH_SCREEN* > cleared_list;
for( const auto& alias : m_aliases )
{
auto screen = alias->GetParent();
if( cleared_list.count( screen ) == 0 )
{
screen->ClearBusAliases();
cleared_list.insert( screen );
}
screen->AddBusAlias( alias );
}
return true;
}
void DIALOG_BUS_MANAGER::OnSelectBus( wxListEvent& event )
{
if( event.GetEventType() == wxEVT_COMMAND_LIST_ITEM_SELECTED )
{
auto alias = m_aliases[ event.GetIndex() ];
if( m_active_alias != alias )
{
m_active_alias = alias;
m_bus_edit->ChangeValue( alias->GetName() );
m_btn_add_bus->Disable();
m_btn_rename_bus->Enable();
m_btn_remove_bus->Enable();
auto members = alias->Members();
// TODO(JE) Clear() seems to be clearing the hint, contrary to
// the wx documentation.
m_signal_edit->Clear();
m_signal_list_view->DeleteAllItems();
for( unsigned i = 0; i < members.size(); i++ )
{
m_signal_list_view->InsertItem( i, members[i] );
}
m_signal_list_view->SetColumnWidth( 0, -1 );
m_btn_add_signal->Enable();
m_btn_rename_signal->Disable();
m_btn_remove_signal->Disable();
}
}
else
{
m_active_alias = NULL;
m_bus_edit->Clear();
m_signal_edit->Clear();
m_signal_list_view->DeleteAllItems();
m_btn_add_bus->Enable();
m_btn_rename_bus->Disable();
m_btn_remove_bus->Disable();
m_btn_add_signal->Disable();
m_btn_rename_signal->Disable();
m_btn_remove_signal->Disable();
}
}
void DIALOG_BUS_MANAGER::OnSelectSignal( wxListEvent& event )
{
if( event.GetEventType() == wxEVT_COMMAND_LIST_ITEM_SELECTED )
{
m_signal_edit->ChangeValue( event.GetText() );
m_btn_rename_signal->Enable();
m_btn_remove_signal->Enable();
}
else
{
m_signal_edit->Clear();
m_btn_rename_signal->Disable();
m_btn_remove_signal->Disable();
}
}
void DIALOG_BUS_MANAGER::OnAddBus( wxCommandEvent& aEvent )
{
// If there is an active alias, then check that the user actually
// changed the text in the edit box (we can't have duplicate aliases)
auto new_name = m_bus_edit->GetValue();
if( new_name.Length() == 0 )
{
return;
}
for( const auto& alias : m_aliases )
{
if( alias->GetName() == new_name )
{
// TODO(JE) display error?
return;
}
}
if( !m_active_alias ||
( m_active_alias && m_active_alias->GetName().Cmp( new_name ) ) )
{
// The values are different; create a new alias
auto alias = std::make_shared<BUS_ALIAS>();
alias->SetName( new_name );
// New aliases get stored on the currently visible sheet
alias->SetParent( static_cast<SCH_EDIT_FRAME*>( GetParent() )->GetScreen() );
auto text = getAliasDisplayText( alias );
m_aliases.push_back( alias );
long idx = m_bus_list_view->InsertItem( m_aliases.size() - 1, text );
m_bus_list_view->SetColumnWidth( 0, -1 );
m_bus_list_view->Select( idx );
m_bus_edit->Clear();
}
else
{
// TODO(JE) Check about desired result here.
// Maybe warn the user? Or just do nothing
}
}
void DIALOG_BUS_MANAGER::OnRenameBus( wxCommandEvent& aEvent )
{
// We should only get here if there is an active alias
wxASSERT( m_active_alias );
m_active_alias->SetName( m_bus_edit->GetValue() );
long idx = m_bus_list_view->FindItem( -1, wxUIntPtr( m_active_alias.get() ) );
wxASSERT( idx >= 0 );
m_bus_list_view->SetItemText( idx, getAliasDisplayText( m_active_alias ) );
}
void DIALOG_BUS_MANAGER::OnRemoveBus( wxCommandEvent& aEvent )
{
// We should only get here if there is an active alias
wxASSERT( m_active_alias );
long i = m_bus_list_view->GetFirstSelected();
wxASSERT( m_active_alias == m_aliases[ i ] );
m_bus_list_view->DeleteItem( i );
m_aliases.erase( m_aliases.begin() + i );
m_bus_edit->Clear();
m_active_alias = NULL;
auto evt = wxListEvent( wxEVT_COMMAND_LIST_ITEM_DESELECTED );
OnSelectBus( evt );
}
void DIALOG_BUS_MANAGER::OnAddSignal( wxCommandEvent& aEvent )
{
auto name_list = m_signal_edit->GetValue();
if( !m_active_alias || name_list.Length() == 0 )
{
return;
}
// String collecting net names that were not added to the bus
wxString notAdded;
// Parse a space-separated list and add each one
wxStringTokenizer tok( name_list, " " );
while( tok.HasMoreTokens() )
{
auto name = tok.GetNextToken();
if( !m_active_alias->Contains( name ) )
{
m_active_alias->AddMember( name );
long idx = m_signal_list_view->InsertItem(
m_active_alias->GetMemberCount() - 1, name );
m_signal_list_view->SetColumnWidth( 0, -1 );
m_signal_list_view->Select( idx );
}
else
{
// Some of the requested net names were not added to the list, so keep them for editing
notAdded = notAdded.IsEmpty() ? name : notAdded + " " + name;
}
}
m_signal_edit->SetValue( notAdded );
m_signal_edit->SetInsertionPointEnd();
}
void DIALOG_BUS_MANAGER::OnRenameSignal( wxCommandEvent& aEvent )
{
// We should only get here if there is an active alias
wxASSERT( m_active_alias );
auto new_name = m_signal_edit->GetValue();
long idx = m_signal_list_view->GetFirstSelected();
wxASSERT( idx >= 0 );
auto old_name = m_active_alias->Members()[ idx ];
// User could have typed a space here, so check first
if( new_name.Find( " " ) != wxNOT_FOUND )
{
// TODO(JE) error feedback
m_signal_edit->ChangeValue( old_name );
return;
}
m_active_alias->Members()[ idx ] = new_name;
m_signal_list_view->SetItemText( idx, new_name );
m_signal_list_view->SetColumnWidth( 0, -1 );
}
void DIALOG_BUS_MANAGER::OnRemoveSignal( wxCommandEvent& aEvent )
{
// We should only get here if there is an active alias
wxASSERT( m_active_alias );
long idx = m_signal_list_view->GetFirstSelected();
wxASSERT( idx >= 0 );
m_active_alias->Members().erase( m_active_alias->Members().begin() + idx );
m_signal_list_view->DeleteItem( idx );
m_signal_edit->Clear();
m_btn_rename_signal->Disable();
m_btn_remove_signal->Disable();
}
wxString DIALOG_BUS_MANAGER::getAliasDisplayText( std::shared_ptr< BUS_ALIAS > aAlias )
{
wxString name = aAlias->GetName();
wxFileName sheet_name( aAlias->GetParent()->GetFileName() );
name += _T( " (" ) + sheet_name.GetFullName() + _T( ")" );
return name;
}
// see invoke_sch_dialog.h
void InvokeDialogBusManager( SCH_EDIT_FRAME* aCaller )
{
DIALOG_BUS_MANAGER dlg( aCaller );
dlg.ShowModal();
}