|
|
/*
* 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();}
|