mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 02:03:12 +02:00
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.
520 lines
17 KiB
C++
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();
|
|
}
|