1144 lines
35 KiB
C++
1144 lines
35 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
|
* Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
|
|
* Copyright (C) 1992-2021 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
|
|
*/
|
|
|
|
#include <bitmaps.h>
|
|
#include <confirm.h>
|
|
#include <eda_dde.h>
|
|
#include <fp_lib_table.h>
|
|
#include <kiface_i.h>
|
|
#include <kiplatform/app.h>
|
|
#include <kiway_express.h>
|
|
#include <macros.h>
|
|
#include <netlist_reader/netlist_reader.h>
|
|
#include <numeric>
|
|
#include <tool/action_manager.h>
|
|
#include <tool/action_toolbar.h>
|
|
#include <tool/common_control.h>
|
|
#include <tool/editor_conditions.h>
|
|
#include <tool/tool_dispatcher.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <widgets/progress_reporter.h>
|
|
#include <wx/statline.h>
|
|
|
|
#include <cvpcb_association.h>
|
|
#include <cvpcb_id.h>
|
|
#include <cvpcb_mainframe.h>
|
|
#include <cvpcb_settings.h>
|
|
#include <display_footprints_frame.h>
|
|
#include <kiplatform/ui.h>
|
|
#include <listboxes.h>
|
|
#include <tools/cvpcb_actions.h>
|
|
#include <tools/cvpcb_association_tool.h>
|
|
#include <tools/cvpcb_control.h>
|
|
|
|
#define CVPCB_MAINFRAME_NAME wxT( "CvpcbFrame" )
|
|
|
|
|
|
CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
|
KIWAY_PLAYER( aKiway, aParent, FRAME_CVPCB, _( "Assign Footprints" ), wxDefaultPosition,
|
|
wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, CVPCB_MAINFRAME_NAME )
|
|
{
|
|
m_compListBox = NULL;
|
|
m_footprintListBox = NULL;
|
|
m_libListBox = NULL;
|
|
m_mainToolBar = NULL;
|
|
m_modified = false;
|
|
m_skipComponentSelect = false;
|
|
m_filteringOptions = FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST;
|
|
m_tcFilterString = NULL;
|
|
m_FootprintsList = FOOTPRINT_LIST::GetInstance( Kiway() );
|
|
m_initialized = false;
|
|
m_aboutTitle = "CvPcb";
|
|
|
|
// Give an icon
|
|
wxIcon icon;
|
|
icon.CopyFromBitmap( KiBitmap( icon_cvpcb_xpm ) );
|
|
SetIcon( icon );
|
|
|
|
SetAutoLayout( true );
|
|
|
|
LoadSettings( config() );
|
|
|
|
setupTools();
|
|
setupUIConditions();
|
|
ReCreateMenuBar();
|
|
ReCreateHToolbar();
|
|
|
|
// Create list of available footprints and components of the schematic
|
|
BuildCmpListBox();
|
|
BuildFOOTPRINTS_LISTBOX();
|
|
BuildLIBRARY_LISTBOX();
|
|
|
|
m_auimgr.SetManagedWindow( this );
|
|
|
|
m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) );
|
|
|
|
m_auimgr.AddPane( m_libListBox, EDA_PANE().Palette().Name( "Libraries" ).Left().Layer(1)
|
|
.Caption( _( "Footprint Libraries" ) )
|
|
.BestSize((int) ( m_frameSize.x * 0.20 ), m_frameSize.y ) );
|
|
|
|
m_auimgr.AddPane( m_compListBox, EDA_PANE().Palette().Name( "Components" ).Center().Layer(0)
|
|
.Caption( _( "Symbol : Footprint Assignments" ) ) );
|
|
|
|
m_auimgr.AddPane( m_footprintListBox, EDA_PANE().Palette().Name( "Footprints" ).Right().Layer(1)
|
|
.Caption( _( "Filtered Footprints" ) )
|
|
.BestSize((int) ( m_frameSize.x * 0.30 ), m_frameSize.y ) );
|
|
|
|
// Build the bottom panel, to display 2 status texts and the buttons:
|
|
auto bottomPanel = new wxPanel( this );
|
|
auto panelSizer = new wxBoxSizer( wxVERTICAL );
|
|
|
|
wxFlexGridSizer* fgSizerStatus = new wxFlexGridSizer( 3, 1, 0, 0 );
|
|
fgSizerStatus->SetFlexibleDirection( wxBOTH );
|
|
fgSizerStatus->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
|
|
|
m_statusLine1 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
|
|
fgSizerStatus->Add( m_statusLine1, 0, 0, 5 );
|
|
|
|
m_statusLine2 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
|
|
fgSizerStatus->Add( m_statusLine2, 0, 0, 5 );
|
|
|
|
m_statusLine3 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString );
|
|
fgSizerStatus->Add( m_statusLine3, 0, wxBOTTOM, 3 );
|
|
|
|
panelSizer->Add( fgSizerStatus, 1, wxEXPAND|wxLEFT, 2 );
|
|
|
|
wxStaticLine* staticline1 = new wxStaticLine( bottomPanel );
|
|
panelSizer->Add( staticline1, 0, wxEXPAND, 5 );
|
|
|
|
wxFont statusFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
|
statusFont.SetSymbolicSize( wxFONTSIZE_SMALL );
|
|
m_statusLine1->SetFont( statusFont );
|
|
m_statusLine2->SetFont( statusFont );
|
|
m_statusLine3->SetFont( statusFont );
|
|
|
|
// Add buttons:
|
|
auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL );
|
|
auto sdbSizer = new wxStdDialogButtonSizer();
|
|
|
|
m_saveAndContinue = new wxButton( bottomPanel, ID_SAVE_PROJECT,
|
|
_( "Apply, Save Schematic && Continue" ) );
|
|
buttonsSizer->Add( m_saveAndContinue, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 20 );
|
|
|
|
auto sdbSizerOK = new wxButton( bottomPanel, wxID_OK );
|
|
sdbSizer->AddButton( sdbSizerOK );
|
|
auto sdbSizerCancel = new wxButton( bottomPanel, wxID_CANCEL );
|
|
sdbSizer->AddButton( sdbSizerCancel );
|
|
sdbSizer->Realize();
|
|
|
|
buttonsSizer->Add( sdbSizer, 0, 0, 5 );
|
|
panelSizer->Add( buttonsSizer, 0, wxALIGN_RIGHT|wxALL, 5 );
|
|
|
|
bottomPanel->SetSizer( panelSizer );
|
|
bottomPanel->Fit();
|
|
|
|
sdbSizerOK->SetDefault();
|
|
KIPLATFORM::UI::FixupCancelButtonCmdKeyCollision( this );
|
|
|
|
m_auimgr.AddPane( bottomPanel, EDA_PANE().HToolbar().Name( "Buttons" ).Bottom().Layer(6) );
|
|
|
|
m_auimgr.Update();
|
|
m_initialized = true;
|
|
|
|
if( CVPCB_SETTINGS* cfg = dynamic_cast<CVPCB_SETTINGS*>( config() ) )
|
|
{
|
|
if( cfg->m_LibrariesWidth > 0 )
|
|
{
|
|
wxAuiPaneInfo& librariesPane = m_auimgr.GetPane( "Libraries" );
|
|
|
|
// wxAUI hack: force width by setting MinSize() and then Fixed()
|
|
// thanks to ZenJu http://trac.wxwidgets.org/ticket/13180
|
|
librariesPane.MinSize( cfg->m_LibrariesWidth, -1 );
|
|
librariesPane.BestSize( cfg->m_LibrariesWidth, -1 );
|
|
librariesPane.MaxSize( cfg->m_LibrariesWidth, -1 );
|
|
librariesPane.Fixed();
|
|
m_auimgr.Update();
|
|
|
|
// now make it resizable again
|
|
librariesPane.MinSize( 20, -1 );
|
|
librariesPane.Resizable();
|
|
m_auimgr.Update();
|
|
}
|
|
|
|
if( cfg->m_FootprintsWidth > 0 )
|
|
{
|
|
wxAuiPaneInfo& footprintsPane = m_auimgr.GetPane( "Footprints" );
|
|
|
|
// wxAUI hack: force width by setting MinSize() and then Fixed()
|
|
// thanks to ZenJu http://trac.wxwidgets.org/ticket/13180
|
|
footprintsPane.MinSize( cfg->m_FootprintsWidth, -1 );
|
|
footprintsPane.BestSize( cfg->m_FootprintsWidth, -1 );
|
|
footprintsPane.MaxSize( cfg->m_FootprintsWidth, -1 );
|
|
footprintsPane.Fixed();
|
|
m_auimgr.Update();
|
|
|
|
// now make it resizable again
|
|
footprintsPane.MinSize( 20, -1 );
|
|
footprintsPane.Resizable();
|
|
m_auimgr.Update();
|
|
}
|
|
}
|
|
|
|
// Connect Events
|
|
setupEventHandlers();
|
|
|
|
// Start the main processing loop
|
|
m_toolManager->InvokeTool( "cvpcb.Control" );
|
|
|
|
KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Symbol to footprint changes are unsaved" ) );
|
|
}
|
|
|
|
|
|
CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
|
|
{
|
|
// Shutdown all running tools
|
|
if( m_toolManager )
|
|
m_toolManager->ShutdownAllTools();
|
|
|
|
// Clean up the tool infrastructure
|
|
delete m_actions;
|
|
delete m_toolManager;
|
|
delete m_toolDispatcher;
|
|
|
|
m_auimgr.UnInit();
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::setupTools()
|
|
{
|
|
// Create the manager
|
|
m_actions = new CVPCB_ACTIONS();
|
|
m_toolManager = new TOOL_MANAGER;
|
|
m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
|
|
m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager, m_actions );
|
|
|
|
// Register tools
|
|
m_toolManager->RegisterTool( new COMMON_CONTROL );
|
|
m_toolManager->RegisterTool( new CVPCB_CONTROL );
|
|
m_toolManager->RegisterTool( new CVPCB_ASSOCIATION_TOOL );
|
|
m_toolManager->InitTools();
|
|
|
|
CVPCB_CONTROL* tool = m_toolManager->GetTool<CVPCB_CONTROL>();
|
|
|
|
// Even though these menus will open with the right-click, we treat them as a normal
|
|
// menu instead of a context menu because we don't care about their position and want
|
|
// to be able to tell the difference between a menu click and a hotkey activation.
|
|
|
|
// Create the context menu for the component list box
|
|
m_componentContextMenu = new ACTION_MENU( false );
|
|
m_componentContextMenu->SetTool( tool );
|
|
m_componentContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer );
|
|
m_componentContextMenu->AppendSeparator();
|
|
m_componentContextMenu->Add( ACTIONS::cut );
|
|
m_componentContextMenu->Add( ACTIONS::copy );
|
|
m_componentContextMenu->Add( ACTIONS::paste );
|
|
m_componentContextMenu->AppendSeparator();
|
|
m_componentContextMenu->Add( CVPCB_ACTIONS::deleteAssoc );
|
|
|
|
// Create the context menu for the footprint list box
|
|
m_footprintContextMenu = new ACTION_MENU( false );
|
|
m_footprintContextMenu->SetTool( tool );
|
|
m_footprintContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::setupUIConditions()
|
|
{
|
|
EDA_BASE_FRAME::setupUIConditions();
|
|
|
|
ACTION_MANAGER* mgr = m_toolManager->GetActionManager();
|
|
EDITOR_CONDITIONS cond( this );
|
|
|
|
wxASSERT( mgr );
|
|
|
|
#define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
|
|
#define CHECK( x ) ACTION_CONDITIONS().Check( x )
|
|
|
|
mgr->SetConditions( CVPCB_ACTIONS::saveAssociations, ENABLE( cond.ContentModified() ) );
|
|
mgr->SetConditions( ACTIONS::undo, ENABLE( cond.UndoAvailable() ) );
|
|
mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) );
|
|
|
|
auto compFilter =
|
|
[this] ( const SELECTION& )
|
|
{
|
|
return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS;
|
|
};
|
|
|
|
auto libFilter =
|
|
[this] ( const SELECTION& )
|
|
{
|
|
return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY;
|
|
};
|
|
|
|
auto pinFilter =
|
|
[this] ( const SELECTION& )
|
|
{
|
|
return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT;
|
|
};
|
|
|
|
mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyFPFilters, CHECK( compFilter ) );
|
|
mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyLibrary, CHECK( libFilter ) );
|
|
mgr->SetConditions( CVPCB_ACTIONS::filterFPbyPin, CHECK( pinFilter ) );
|
|
|
|
#undef CHECK
|
|
#undef ENABLE
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::setupEventHandlers()
|
|
{
|
|
// Connect the handlers to launch the context menus in the listboxes
|
|
m_footprintListBox->Bind( wxEVT_RIGHT_DOWN,
|
|
[this]( wxMouseEvent& )
|
|
{
|
|
PopupMenu( m_footprintContextMenu );
|
|
} );
|
|
|
|
m_compListBox->Bind( wxEVT_RIGHT_DOWN,
|
|
[this]( wxMouseEvent& )
|
|
{
|
|
PopupMenu( m_componentContextMenu );
|
|
} );
|
|
|
|
// Connect the handler for the save button
|
|
m_saveAndContinue->Bind( wxEVT_COMMAND_BUTTON_CLICKED,
|
|
[this]( wxCommandEvent& )
|
|
{
|
|
// saveAssociations must be run immediately
|
|
GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociations, true );
|
|
} );
|
|
|
|
// Connect the handlers for the ok/cancel buttons
|
|
Bind( wxEVT_BUTTON,
|
|
[this]( wxCommandEvent& )
|
|
{
|
|
// saveAssociations must be run immediately, before running Close( true )
|
|
GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociations, true );
|
|
Close( true );
|
|
}, wxID_OK );
|
|
Bind( wxEVT_BUTTON,
|
|
[this]( wxCommandEvent& )
|
|
{
|
|
Close( false );
|
|
}, wxID_CANCEL );
|
|
|
|
// Connect the handlers for the close events
|
|
Bind( wxEVT_MENU,
|
|
[this]( wxCommandEvent& )
|
|
{
|
|
Close( false );
|
|
}, wxID_CLOSE );
|
|
Bind( wxEVT_MENU,
|
|
[this]( wxCommandEvent& )
|
|
{
|
|
Close( false );
|
|
}, wxID_EXIT );
|
|
|
|
// Toolbar events
|
|
Bind( wxEVT_TEXT, &CVPCB_MAINFRAME::OnEnterFilteringText, this, ID_CVPCB_FILTER_TEXT_EDIT );
|
|
|
|
// Just skip the resize events
|
|
Bind( wxEVT_SIZE,
|
|
[]( wxSizeEvent& aEvent )
|
|
{
|
|
aEvent.Skip();
|
|
} );
|
|
|
|
// Attach the events to the tool dispatcher
|
|
Bind( wxEVT_TOOL, &TOOL_DISPATCHER::DispatchWxCommand, m_toolDispatcher );
|
|
Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
|
|
Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
|
|
}
|
|
|
|
|
|
bool CVPCB_MAINFRAME::canCloseWindow( wxCloseEvent& aEvent )
|
|
{
|
|
if( m_modified )
|
|
{
|
|
// Shutdown blocks must be determined and vetoed as early as possible
|
|
if( KIPLATFORM::APP::SupportsShutdownBlockReason()
|
|
&& aEvent.GetId() == wxEVT_QUERY_END_SESSION )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. "
|
|
"Save changes?" ),
|
|
[&]() -> bool
|
|
{
|
|
return SaveFootprintAssociation( false );
|
|
} ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::doCloseWindow()
|
|
{
|
|
if( GetFootprintViewerFrame() )
|
|
GetFootprintViewerFrame()->Close( true );
|
|
|
|
m_modified = false;
|
|
|
|
// clear highlight symbol in schematic:
|
|
SendMessageToEESCHEMA( true );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::OnEnterFilteringText( wxCommandEvent& aEvent )
|
|
{
|
|
// Called when changing the filter string in main toolbar.
|
|
// If the option FOOTPRINTS_LISTBOX::FILTERING_BY_TEXT_PATTERN is set, update the list
|
|
// of available footprints which match the filter
|
|
|
|
wxListEvent l_event;
|
|
OnSelectComponent( l_event );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event )
|
|
{
|
|
if( m_skipComponentSelect )
|
|
return;
|
|
|
|
wxString libraryName;
|
|
COMPONENT* component = GetSelectedComponent();
|
|
libraryName = m_libListBox->GetSelectedLibrary();
|
|
|
|
m_footprintListBox->SetFootprints( *m_FootprintsList, libraryName, component,
|
|
m_tcFilterString->GetValue(), m_filteringOptions );
|
|
|
|
if( component && component->GetFPID().IsValid() )
|
|
m_footprintListBox->SetSelectedFootprint( component->GetFPID() );
|
|
else
|
|
m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false );
|
|
|
|
refreshAfterComponentSearch( component );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EDA_BASE_FRAME::LoadSettings( aCfg );
|
|
|
|
CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg );
|
|
|
|
m_filteringOptions = cfg->m_FilterFootprint;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EDA_BASE_FRAME::SaveSettings( aCfg );
|
|
|
|
CVPCB_SETTINGS* cfg = static_cast<CVPCB_SETTINGS*>( aCfg );
|
|
cfg->m_FilterFootprint = m_filteringOptions;
|
|
|
|
cfg->m_LibrariesWidth = m_libListBox->GetSize().x;
|
|
cfg->m_FootprintsWidth = m_footprintListBox->GetSize().x;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::UndoAssociation()
|
|
{
|
|
if( m_undoList.size() == 0 )
|
|
return;
|
|
|
|
CVPCB_UNDO_REDO_ENTRIES redoEntries;
|
|
CVPCB_UNDO_REDO_ENTRIES curEntry = m_undoList.back();
|
|
m_undoList.pop_back();
|
|
|
|
// Iterate over the entries to undo
|
|
for( const auto& assoc : curEntry )
|
|
{
|
|
AssociateFootprint( assoc, true, false );
|
|
redoEntries.emplace_back( assoc.Reverse() );
|
|
}
|
|
|
|
// Add the redo entries to the redo stack
|
|
m_redoList.emplace_back( redoEntries );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::RedoAssociation()
|
|
{
|
|
if( m_redoList.size() == 0 )
|
|
return;
|
|
|
|
CVPCB_UNDO_REDO_ENTRIES curEntry = m_redoList.back();
|
|
m_redoList.pop_back();
|
|
|
|
// Iterate over the entries to undo
|
|
bool firstAssoc = true;
|
|
|
|
for( const auto& assoc : curEntry )
|
|
{
|
|
AssociateFootprint( assoc, firstAssoc );
|
|
firstAssoc = false;
|
|
}
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::AssociateFootprint( const CVPCB_ASSOCIATION& aAssociation,
|
|
bool aNewEntry, bool aAddUndoItem )
|
|
{
|
|
if( m_netlist.IsEmpty() )
|
|
return;
|
|
|
|
COMPONENT* component = m_netlist.GetComponent( aAssociation.GetComponentIndex() );
|
|
|
|
if( component == NULL )
|
|
return;
|
|
|
|
LIB_ID fpid = aAssociation.GetNewFootprint();
|
|
LIB_ID oldFpid = component->GetFPID();
|
|
|
|
// Test for validity of the requested footprint
|
|
if( !fpid.empty() && !fpid.IsValid() )
|
|
{
|
|
wxString msg = wxString::Format( _( "\"%s\" is not a valid footprint." ),
|
|
fpid.Format().wx_str() );
|
|
DisplayErrorMessage( this, msg );
|
|
return;
|
|
}
|
|
|
|
const KIID& id = component->GetKIIDs().front();
|
|
|
|
// Set new footprint to all instances of the selected component
|
|
for( unsigned int idx : GetComponentIndices() )
|
|
{
|
|
COMPONENT* candidate = m_netlist.GetComponent( idx );
|
|
const std::vector<KIID>& kiids = candidate->GetKIIDs();
|
|
|
|
if( std::find( kiids.begin(), kiids.end(), id ) != kiids.end() )
|
|
{
|
|
// Set the new footprint
|
|
candidate->SetFPID( fpid );
|
|
|
|
// create the new component description and set it
|
|
wxString description = wxString::Format( CMP_FORMAT,
|
|
idx + 1,
|
|
candidate->GetReference(),
|
|
candidate->GetValue(),
|
|
candidate->GetFPID().Format().wx_str() );
|
|
m_compListBox->SetString( idx, description );
|
|
}
|
|
}
|
|
|
|
// Mark the data as being modified
|
|
m_modified = true;
|
|
|
|
// Update the statusbar and refresh the list
|
|
DisplayStatus();
|
|
m_compListBox->Refresh();
|
|
|
|
if( !aAddUndoItem )
|
|
return;
|
|
|
|
// Update the undo list
|
|
if ( aNewEntry )
|
|
{
|
|
// Create a new entry for this association
|
|
CVPCB_UNDO_REDO_ENTRIES newEntry;
|
|
newEntry.emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(), oldFpid,
|
|
aAssociation.GetNewFootprint() ) );
|
|
m_undoList.emplace_back( newEntry );
|
|
|
|
// Clear the redo list
|
|
m_redoList.clear();
|
|
}
|
|
else
|
|
{
|
|
m_undoList.back().emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(),
|
|
oldFpid,
|
|
aAssociation.GetNewFootprint() ) );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
bool CVPCB_MAINFRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::refreshAfterComponentSearch( COMPONENT* component )
|
|
{
|
|
// Tell AuiMgr that objects are changed !
|
|
if( m_auimgr.GetManagedWindow() ) // Be sure Aui Manager is initialized
|
|
m_auimgr.Update(); // (could be not the case when starting CvPcb)
|
|
|
|
if( component == NULL )
|
|
{
|
|
DisplayStatus();
|
|
return;
|
|
}
|
|
|
|
// Preview of the already assigned footprint.
|
|
// Find the footprint that was already chosen for this component and select it,
|
|
// but only if the selection is made from the component list or the library list.
|
|
// If the selection is made from the footprint list, do not change the current
|
|
// selected footprint.
|
|
if( FindFocus() == m_compListBox || FindFocus() == m_libListBox )
|
|
{
|
|
wxString footprintName = FROM_UTF8( component->GetFPID().Format().c_str() );
|
|
|
|
m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false );
|
|
|
|
for( int ii = 0; ii < m_footprintListBox->GetCount(); ii++ )
|
|
{
|
|
wxString candidateName;
|
|
wxString msg = m_footprintListBox->OnGetItemText( ii, 0 );
|
|
msg.Trim( true );
|
|
msg.Trim( false );
|
|
candidateName = msg.AfterFirst( wxChar( ' ' ) );
|
|
|
|
if( footprintName.Cmp( candidateName ) == 0 )
|
|
{
|
|
m_footprintListBox->SetSelection( ii, true );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( GetFootprintViewerFrame() )
|
|
m_toolManager->RunAction( CVPCB_ACTIONS::showFootprintViewer, true );
|
|
}
|
|
|
|
SendMessageToEESCHEMA();
|
|
DisplayStatus();
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SetFootprintFilter( FOOTPRINTS_LISTBOX::FP_FILTER_T aFilter,
|
|
CVPCB_MAINFRAME::CVPCB_FILTER_ACTION aAction )
|
|
{
|
|
int option = aFilter;
|
|
|
|
// Apply the filter accordingly
|
|
switch( aAction )
|
|
{
|
|
case CVPCB_MAINFRAME::FILTER_DISABLE: m_filteringOptions &= ~option; break;
|
|
case CVPCB_MAINFRAME::FILTER_ENABLE: m_filteringOptions |= option; break;
|
|
case CVPCB_MAINFRAME::FILTER_TOGGLE: m_filteringOptions ^= option; break;
|
|
}
|
|
|
|
wxListEvent l_event;
|
|
OnSelectComponent( l_event );
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::DisplayStatus()
|
|
{
|
|
if( !m_initialized )
|
|
return;
|
|
|
|
wxString filters, msg;
|
|
COMPONENT* component = GetSelectedComponent();
|
|
|
|
if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS ) )
|
|
{
|
|
msg.Empty();
|
|
|
|
if( component )
|
|
{
|
|
for( unsigned ii = 0; ii < component->GetFootprintFilters().GetCount(); ii++ )
|
|
{
|
|
if( msg.IsEmpty() )
|
|
msg += component->GetFootprintFilters()[ii];
|
|
else
|
|
msg += wxT( ", " ) + component->GetFootprintFilters()[ii];
|
|
}
|
|
}
|
|
|
|
filters += _( "Keywords" );
|
|
|
|
if( !msg.IsEmpty() )
|
|
filters += wxString::Format( wxT( " (%s)" ), msg );
|
|
}
|
|
|
|
if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT ) )
|
|
{
|
|
msg.Empty();
|
|
|
|
if( component )
|
|
msg = wxString::Format( wxT( "%i" ), component->GetPinCount() );
|
|
|
|
if( !filters.IsEmpty() )
|
|
filters += wxT( ", " );
|
|
|
|
filters += _( "Pin Count" );
|
|
|
|
if( !msg.IsEmpty() )
|
|
filters += wxString::Format( wxT( " (%s)" ), msg );
|
|
}
|
|
|
|
if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY ) )
|
|
{
|
|
msg = m_libListBox->GetSelectedLibrary();
|
|
|
|
if( !filters.IsEmpty() )
|
|
filters += wxT( ", " );
|
|
|
|
filters += _( "Library" );
|
|
|
|
if( !msg.IsEmpty() )
|
|
filters += wxString::Format( wxT( " (%s)" ), msg );
|
|
}
|
|
|
|
wxString textFilter = m_tcFilterString->GetValue();
|
|
|
|
if( !textFilter.IsEmpty() )
|
|
{
|
|
if( !filters.IsEmpty() )
|
|
filters += wxT( ", " );
|
|
|
|
filters += _( "Search Text" ) + wxString::Format( wxT( " (%s)" ), textFilter );
|
|
}
|
|
|
|
if( filters.IsEmpty() )
|
|
msg = _( "No Filtering" );
|
|
else
|
|
msg.Printf( _( "Filtered by %s" ), filters );
|
|
|
|
msg << wxT( ": " ) << m_footprintListBox->GetCount();
|
|
|
|
SetStatusText( msg );
|
|
|
|
|
|
msg.Empty();
|
|
wxString footprintName = GetSelectedFootprint();
|
|
|
|
FOOTPRINT_INFO* fp = m_FootprintsList->GetFootprintInfo( footprintName );
|
|
|
|
if( fp ) // can be NULL if no netlist loaded
|
|
{
|
|
msg = wxString::Format( _( "Description: %s; Keywords: %s" ),
|
|
fp->GetDescription(),
|
|
fp->GetKeywords() );
|
|
}
|
|
|
|
SetStatusText( msg, 1 );
|
|
|
|
msg.Empty();
|
|
wxString lib;
|
|
|
|
// Choose the footprint to get the information on
|
|
if( fp )
|
|
{
|
|
// Use the footprint in the footprint viewer
|
|
lib = fp->GetLibNickname();
|
|
}
|
|
else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_COMPONENT )
|
|
{
|
|
// Use the footprint of the selected component
|
|
if( component )
|
|
lib = component->GetFPID().GetLibNickname();
|
|
}
|
|
else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_LIBRARY )
|
|
{
|
|
// Use the library that is selected
|
|
lib = m_libListBox->GetSelectedLibrary();
|
|
}
|
|
|
|
// Extract the library information
|
|
FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs( Kiway() );
|
|
|
|
if( fptbl->HasLibrary( lib ) )
|
|
msg = wxString::Format( _( "Library location: %s" ), fptbl->GetFullURI( lib ) );
|
|
else
|
|
msg = wxString::Format( _( "Library location: unknown" ) );
|
|
|
|
SetStatusText( msg, 2 );
|
|
}
|
|
|
|
|
|
bool CVPCB_MAINFRAME::LoadFootprintFiles()
|
|
{
|
|
FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs( Kiway() );
|
|
|
|
// Check if there are footprint libraries in the footprint library table.
|
|
if( !fptbl || !fptbl->GetLogicalLibs().size() )
|
|
{
|
|
wxMessageBox( _( "No PCB footprint libraries are listed in the current footprint "
|
|
"library table." ), _( "Configuration Error" ), wxOK | wxICON_ERROR );
|
|
return false;
|
|
}
|
|
|
|
WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 );
|
|
|
|
m_FootprintsList->ReadFootprintFiles( fptbl, nullptr, &progressReporter );
|
|
|
|
if( m_FootprintsList->GetErrorCount() )
|
|
{
|
|
m_FootprintsList->DisplayErrors( this );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SendMessageToEESCHEMA( bool aClearHighligntOnly )
|
|
{
|
|
if( m_netlist.IsEmpty() )
|
|
return;
|
|
|
|
// clear highlight of previously selected components (if any):
|
|
// Selecting a non existing symbol clears any previously highlighted symbols
|
|
std::string packet = "$CLEAR: \"HIGHLIGHTED\"";
|
|
|
|
if( Kiface().IsSingle() )
|
|
SendCommand( MSG_TO_SCH, packet.c_str() );
|
|
else
|
|
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
|
|
|
|
if( aClearHighligntOnly )
|
|
return;
|
|
|
|
int selection = m_compListBox->GetSelection();
|
|
|
|
if ( selection < 0 ) // Nothing selected
|
|
return;
|
|
|
|
if( m_netlist.GetComponent( selection ) == NULL )
|
|
return;
|
|
|
|
// Now highlight the selected component:
|
|
COMPONENT* component = m_netlist.GetComponent( selection );
|
|
|
|
packet = StrPrintf( "$PART: \"%s\"", TO_UTF8( component->GetReference() ) );
|
|
|
|
if( Kiface().IsSingle() )
|
|
SendCommand( MSG_TO_SCH, packet.c_str() );
|
|
else
|
|
Kiway().ExpressMail( FRAME_SCH, MAIL_CROSS_PROBE, packet, this );
|
|
}
|
|
|
|
|
|
int CVPCB_MAINFRAME::ReadSchematicNetlist( const std::string& aNetlist )
|
|
{
|
|
STRING_LINE_READER* strrdr = new STRING_LINE_READER( aNetlist, "Eeschema via Kiway" );
|
|
KICAD_NETLIST_READER netrdr( strrdr, &m_netlist );
|
|
|
|
m_netlist.Clear();
|
|
|
|
try
|
|
{
|
|
netrdr.LoadNetlist();
|
|
}
|
|
catch( const IO_ERROR& ioe )
|
|
{
|
|
wxString msg = wxString::Format( _( "Error loading schematic.\n%s" ),
|
|
ioe.What().GetData() );
|
|
wxMessageBox( msg, _( "Load Error" ), wxOK | wxICON_ERROR );
|
|
return 1;
|
|
}
|
|
|
|
// We also remove footprint name if it is "$noname" because this is a dummy name,
|
|
// not the actual name of the footprint.
|
|
for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ )
|
|
{
|
|
if( m_netlist.GetComponent( ii )->GetFPID().GetLibItemName() == std::string( "$noname" ) )
|
|
m_netlist.GetComponent( ii )->SetFPID( LIB_ID() );
|
|
}
|
|
|
|
// Sort components by reference:
|
|
m_netlist.SortByReference();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::BuildFOOTPRINTS_LISTBOX()
|
|
{
|
|
wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
|
|
|
if( m_footprintListBox == NULL )
|
|
{
|
|
m_footprintListBox = new FOOTPRINTS_LISTBOX( this, ID_CVPCB_FOOTPRINT_LIST );
|
|
m_footprintListBox->SetFont( wxFont( guiFont.GetPointSize(), wxFONTFAMILY_MODERN,
|
|
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ) );
|
|
}
|
|
|
|
m_footprintListBox->SetFootprints( *m_FootprintsList, wxEmptyString, NULL, wxEmptyString,
|
|
FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST );
|
|
DisplayStatus();
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::BuildCmpListBox()
|
|
{
|
|
wxString msg;
|
|
COMPONENT* component;
|
|
wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
|
|
|
if( m_compListBox == NULL )
|
|
{
|
|
m_compListBox = new COMPONENTS_LISTBOX( this, ID_CVPCB_COMPONENT_LIST );
|
|
m_compListBox->SetFont( wxFont( guiFont.GetPointSize(), wxFONTFAMILY_MODERN,
|
|
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ) );
|
|
}
|
|
|
|
m_compListBox->m_ComponentList.Clear();
|
|
|
|
for( unsigned i = 0; i < m_netlist.GetCount(); i++ )
|
|
{
|
|
component = m_netlist.GetComponent( i );
|
|
|
|
msg.Printf( CMP_FORMAT,
|
|
m_compListBox->GetCount() + 1,
|
|
component->GetReference(),
|
|
component->GetValue(),
|
|
component->GetFPID().Format().wx_str() );
|
|
m_compListBox->m_ComponentList.Add( msg );
|
|
}
|
|
|
|
if( m_compListBox->m_ComponentList.Count() )
|
|
{
|
|
m_compListBox->SetItemCount( m_compListBox->m_ComponentList.Count() );
|
|
m_compListBox->SetSelection( 0, true );
|
|
m_compListBox->RefreshItems( 0L, m_compListBox->m_ComponentList.Count()-1 );
|
|
m_compListBox->UpdateWidth();
|
|
}
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::BuildLIBRARY_LISTBOX()
|
|
{
|
|
wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
|
|
|
if( m_libListBox == NULL )
|
|
{
|
|
m_libListBox = new LIBRARY_LISTBOX( this, ID_CVPCB_LIBRARY_LIST );
|
|
m_libListBox->SetFont( wxFont( guiFont.GetPointSize(), wxFONTFAMILY_MODERN,
|
|
wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ) );
|
|
}
|
|
|
|
FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs( Kiway() );
|
|
|
|
if( tbl )
|
|
{
|
|
wxArrayString libNames;
|
|
|
|
std::vector< wxString > libNickNames = tbl->GetLogicalLibs();
|
|
|
|
for( const wxString& libNickName : libNickNames )
|
|
libNames.Add( libNickName );
|
|
|
|
m_libListBox->SetLibraryList( libNames );
|
|
}
|
|
}
|
|
|
|
|
|
COMPONENT* CVPCB_MAINFRAME::GetSelectedComponent()
|
|
{
|
|
int selection = m_compListBox->GetSelection();
|
|
|
|
if( selection >= 0 && selection < (int) m_netlist.GetCount() )
|
|
return m_netlist.GetComponent( selection );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SetSelectedComponent( int aIndex, bool aSkipUpdate )
|
|
{
|
|
m_skipComponentSelect = aSkipUpdate;
|
|
|
|
if( aIndex < 0 )
|
|
{
|
|
m_compListBox->DeselectAll();
|
|
}
|
|
else if( aIndex < m_compListBox->GetCount() )
|
|
{
|
|
m_compListBox->DeselectAll();
|
|
m_compListBox->SetSelection( aIndex );
|
|
SendMessageToEESCHEMA();
|
|
}
|
|
|
|
m_skipComponentSelect = false;
|
|
}
|
|
|
|
|
|
std::vector<unsigned int> CVPCB_MAINFRAME::GetComponentIndices(
|
|
CVPCB_MAINFRAME::CRITERIA aCriteria )
|
|
{
|
|
std::vector<unsigned int> idx;
|
|
int lastIdx;
|
|
|
|
// Make sure a netlist has been loaded and the box has contents
|
|
if( m_netlist.IsEmpty() || m_compListBox->GetCount() == 0 )
|
|
return idx;
|
|
|
|
switch( aCriteria )
|
|
{
|
|
case CVPCB_MAINFRAME::ALL_COMPONENTS:
|
|
idx.resize( m_netlist.GetCount() );
|
|
std::iota( idx.begin(), idx.end(), 0 );
|
|
break;
|
|
|
|
case CVPCB_MAINFRAME::SEL_COMPONENTS:
|
|
// Check to see if anything is selected
|
|
if( m_compListBox->GetSelectedItemCount() < 1 )
|
|
break;
|
|
|
|
// Get the components
|
|
lastIdx = m_compListBox->GetFirstSelected();
|
|
idx.emplace_back( lastIdx );
|
|
|
|
lastIdx = m_compListBox->GetNextSelected( lastIdx );
|
|
while( lastIdx > 0 )
|
|
{
|
|
idx.emplace_back( lastIdx );
|
|
lastIdx = m_compListBox->GetNextSelected( lastIdx );
|
|
}
|
|
break;
|
|
|
|
case CVPCB_MAINFRAME::NA_COMPONENTS:
|
|
for( unsigned int i = 0; i < m_netlist.GetCount(); i++ )
|
|
{
|
|
if( m_netlist.GetComponent( i )->GetFPID().empty() )
|
|
idx.emplace_back( i );
|
|
}
|
|
break;
|
|
|
|
case CVPCB_MAINFRAME::ASSOC_COMPONENTS:
|
|
for( unsigned int i = 0; i < m_netlist.GetCount(); i++ )
|
|
{
|
|
if( !m_netlist.GetComponent( i )->GetFPID().empty() )
|
|
idx.emplace_back( i );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
wxASSERT_MSG( false, "Invalid component selection criteria" );
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
DISPLAY_FOOTPRINTS_FRAME* CVPCB_MAINFRAME::GetFootprintViewerFrame() const
|
|
{
|
|
// returns the Footprint Viewer frame, if exists, or NULL
|
|
wxWindow* window = wxWindow::FindWindowByName( FOOTPRINTVIEWER_FRAME_NAME );
|
|
return dynamic_cast<DISPLAY_FOOTPRINTS_FRAME*>( window );
|
|
}
|
|
|
|
|
|
wxWindow* CVPCB_MAINFRAME::GetToolCanvas() const
|
|
{
|
|
return GetFootprintViewerFrame();
|
|
}
|
|
|
|
|
|
CVPCB_MAINFRAME::CONTROL_TYPE CVPCB_MAINFRAME::GetFocusedControl() const
|
|
{
|
|
if( m_libListBox->HasFocus() )
|
|
return CVPCB_MAINFRAME::CONTROL_LIBRARY;
|
|
else if( m_compListBox->HasFocus() )
|
|
return CVPCB_MAINFRAME::CONTROL_COMPONENT;
|
|
else if( m_footprintListBox->HasFocus() )
|
|
return CVPCB_MAINFRAME::CONTROL_FOOTPRINT;
|
|
|
|
return CVPCB_MAINFRAME::CONTROL_NONE;
|
|
}
|
|
|
|
|
|
wxControl* CVPCB_MAINFRAME::GetFocusedControlObject() const
|
|
{
|
|
if( m_libListBox->HasFocus() )
|
|
return m_libListBox;
|
|
else if( m_compListBox->HasFocus() )
|
|
return m_compListBox;
|
|
else if( m_footprintListBox->HasFocus() )
|
|
return m_footprintListBox;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SetFocusedControl( CVPCB_MAINFRAME::CONTROL_TYPE aLB )
|
|
{
|
|
switch( aLB )
|
|
{
|
|
case CVPCB_MAINFRAME::CONTROL_LIBRARY: m_libListBox->SetFocus(); break;
|
|
case CVPCB_MAINFRAME::CONTROL_COMPONENT: m_compListBox->SetFocus(); break;
|
|
case CVPCB_MAINFRAME::CONTROL_FOOTPRINT: m_footprintListBox->SetFocus(); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
wxString CVPCB_MAINFRAME::GetSelectedFootprint()
|
|
{
|
|
// returns the LIB_ID of the selected footprint in footprint listview
|
|
// or a empty string
|
|
return m_footprintListBox->GetSelectedFootprint();
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::SetStatusText( const wxString& aText, int aNumber )
|
|
{
|
|
switch( aNumber )
|
|
{
|
|
case 0: m_statusLine1->SetLabel( aText ); break;
|
|
case 1: m_statusLine2->SetLabel( aText ); break;
|
|
case 2: m_statusLine3->SetLabel( aText ); break;
|
|
default: wxFAIL_MSG( "Invalid status row number" ); break;
|
|
}
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::ShowChangedLanguage()
|
|
{
|
|
EDA_BASE_FRAME::ShowChangedLanguage();
|
|
ReCreateHToolbar();
|
|
DisplayStatus();
|
|
}
|
|
|
|
|
|
void CVPCB_MAINFRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
|
{
|
|
const std::string& payload = mail.GetPayload();
|
|
|
|
switch( mail.Command() )
|
|
{
|
|
case MAIL_EESCHEMA_NETLIST:
|
|
ReadNetListAndFpFiles( payload );
|
|
/* @todo
|
|
Go into SCH_EDIT_FRAME::OnOpenCvpcb( wxCommandEvent& event ) and trim GNL_ALL down.
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
; // ignore most
|
|
}
|
|
}
|