Replace GFootprintList with KIWAY-based filter
The symbol chooser's footprint dropdown now fetches filtered results from pcbnew via KIWAY instead of requiring direct access to FOOTPRINT_LIST. Filter criteria (pin count, footprint patterns) are serialized as JSON, sent to pcbnew, and matching footprints are returned as a JSON array. The filtering uses preloaded footprints from the adapter rather than re-reading from disk, which eliminates the main performance bottleneck when selecting symbols.
This commit is contained in:
@@ -31,13 +31,8 @@
|
||||
#include <footprint_info.h>
|
||||
#include <dialogs/html_message_box.h>
|
||||
#include <string_utils.h>
|
||||
#include <kiface_ids.h>
|
||||
#include <kiway.h>
|
||||
#include <lib_id.h>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <kiface_base.h>
|
||||
|
||||
FOOTPRINT_INFO* FOOTPRINT_LIST::GetFootprintInfo( const wxString& aLibNickname,
|
||||
const wxString& aFootprintName )
|
||||
@@ -152,32 +147,3 @@ wxString FOOTPRINT_LIST::GetErrorMessages()
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
static FOOTPRINT_LIST* get_instance_from_id( KIWAY& aKiway, int aId )
|
||||
{
|
||||
void* ptr = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
ptr = Kiface().IfaceOrAddress( aId );
|
||||
|
||||
if( !ptr )
|
||||
{
|
||||
KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB );
|
||||
ptr = kiface->IfaceOrAddress( aId );
|
||||
}
|
||||
|
||||
return static_cast<FOOTPRINT_LIST*>( ptr );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FOOTPRINT_LIST* FOOTPRINT_LIST::GetInstance( KIWAY& aKiway )
|
||||
{
|
||||
return get_instance_from_id( aKiway, KIFACE_FOOTPRINT_LIST );
|
||||
}
|
||||
|
||||
@@ -19,29 +19,23 @@
|
||||
|
||||
#include <eda_draw_frame.h>
|
||||
#include <kiway.h>
|
||||
#include <kiway_player.h>
|
||||
#include <project.h>
|
||||
#include <project_pcb.h>
|
||||
#include <kiface_ids.h>
|
||||
#include <widgets/footprint_choice.h>
|
||||
#include <widgets/footprint_select_widget.h>
|
||||
#include <widgets/wx_progress_reporters.h>
|
||||
#include <progress_reporter.h>
|
||||
#include <footprint_info_impl.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/wupdlock.h>
|
||||
|
||||
|
||||
wxDEFINE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent );
|
||||
|
||||
FOOTPRINT_SELECT_WIDGET::FOOTPRINT_SELECT_WIDGET( EDA_DRAW_FRAME* aFrame, wxWindow* aParent,
|
||||
FOOTPRINT_LIST* aFpList, bool aUpdate,
|
||||
int aMaxItems ) :
|
||||
wxPanel( aParent ),
|
||||
m_update( aUpdate ),
|
||||
m_max_items( aMaxItems ),
|
||||
m_fp_list( aFpList ),
|
||||
m_frame( aFrame )
|
||||
wxPanel( aParent ),
|
||||
m_max_items( aMaxItems ),
|
||||
m_pin_count( 0 ),
|
||||
m_zero_filter( true ),
|
||||
m_kiway( nullptr )
|
||||
{
|
||||
m_zero_filter = true;
|
||||
m_sizer = new wxBoxSizer( wxVERTICAL );
|
||||
m_fp_sel_ctrl = new FOOTPRINT_CHOICE( this, wxID_ANY );
|
||||
m_sizer->Add( m_fp_sel_ctrl, 1, wxEXPAND, 5 );
|
||||
@@ -56,26 +50,7 @@ FOOTPRINT_SELECT_WIDGET::FOOTPRINT_SELECT_WIDGET( EDA_DRAW_FRAME* aFrame, wxWind
|
||||
|
||||
void FOOTPRINT_SELECT_WIDGET::Load( KIWAY& aKiway, PROJECT& aProject )
|
||||
{
|
||||
m_fp_list = FOOTPRINT_LIST::GetInstance( aKiway );
|
||||
wxCHECK_MSG( m_fp_list, /* void */, "Failed to get the footprint list from the KiWay" );
|
||||
|
||||
if( m_fp_list->GetCount() == 0 )
|
||||
{
|
||||
WX_PROGRESS_REPORTER progressReporter( m_frame, _( "Load Footprint Libraries" ), 1,
|
||||
PR_CAN_ABORT );
|
||||
|
||||
// If the fp-info-cache is empty (or, more likely, hasn't been created in a new
|
||||
// project yet), load footprints the hard way.
|
||||
FOOTPRINT_LIBRARY_ADAPTER* footprints = aProject.FootprintLibAdapter( aKiway );
|
||||
FOOTPRINT_LIST_IMPL& fpList = static_cast<FOOTPRINT_LIST_IMPL&>( *m_fp_list );
|
||||
|
||||
fpList.ReadFootprintFiles( footprints, nullptr, &progressReporter );
|
||||
}
|
||||
|
||||
m_fp_filter.SetList( *m_fp_list );
|
||||
|
||||
if( m_update )
|
||||
UpdateList();
|
||||
m_kiway = &aKiway;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +73,8 @@ void FOOTPRINT_SELECT_WIDGET::OnComboBox( wxCommandEvent& aEvent )
|
||||
|
||||
void FOOTPRINT_SELECT_WIDGET::ClearFilters()
|
||||
{
|
||||
m_fp_filter.ClearFilters();
|
||||
m_pin_count = 0;
|
||||
m_filters.Clear();
|
||||
m_default_footprint.Clear();
|
||||
m_zero_filter = false;
|
||||
}
|
||||
@@ -106,7 +82,7 @@ void FOOTPRINT_SELECT_WIDGET::ClearFilters()
|
||||
|
||||
void FOOTPRINT_SELECT_WIDGET::FilterByPinCount( int aPinCount )
|
||||
{
|
||||
m_fp_filter.FilterByPinCount( aPinCount );
|
||||
m_pin_count = aPinCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +90,7 @@ void FOOTPRINT_SELECT_WIDGET::FilterByFootprintFilters( wxArrayString const& aFi
|
||||
bool aZeroFilters )
|
||||
{
|
||||
m_zero_filter = ( aZeroFilters && aFilters.size() == 0 );
|
||||
m_fp_filter.FilterByFootprintFilters( aFilters );
|
||||
m_filters = aFilters;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,34 +102,84 @@ void FOOTPRINT_SELECT_WIDGET::SetDefaultFootprint( wxString const& aFp )
|
||||
|
||||
bool FOOTPRINT_SELECT_WIDGET::UpdateList()
|
||||
{
|
||||
int n_items = 0;
|
||||
|
||||
if( !m_fp_list )
|
||||
if( !m_kiway )
|
||||
return false;
|
||||
|
||||
wxWindowUpdateLocker lock( m_fp_sel_ctrl );
|
||||
m_fp_sel_ctrl->Clear();
|
||||
|
||||
// Be careful adding items! "Default" must occupy POS_DEFAULT,
|
||||
// "Other" must occupy POS_OTHER, and the separator must occupy POS_SEPARATOR.
|
||||
m_fp_sel_ctrl->Append( m_default_footprint.IsEmpty() ?
|
||||
_( "No default footprint" ) :
|
||||
wxS( "[" ) + _( "Default" ) + wxS( "] " ) + m_default_footprint,
|
||||
new wxStringClientData( m_default_footprint ) );
|
||||
// Add the default footprint entry at the top
|
||||
wxString defaultLabel = m_default_footprint.IsEmpty()
|
||||
? _( "No default footprint" )
|
||||
: wxS( "[" ) + _( "Default" ) + wxS( "] " ) + m_default_footprint;
|
||||
|
||||
if( !m_zero_filter )
|
||||
m_fp_sel_ctrl->Append( defaultLabel, new wxStringClientData( m_default_footprint ) );
|
||||
|
||||
// If zero_filter is set and we have no filters, show no footprints
|
||||
if( m_zero_filter && m_filters.IsEmpty() && m_pin_count == 0 )
|
||||
{
|
||||
for( FOOTPRINT_INFO& fpinfo : m_fp_filter )
|
||||
SelectDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build JSON request for pcbnew
|
||||
using json = nlohmann::json;
|
||||
json request;
|
||||
request["pin_count"] = m_pin_count;
|
||||
request["zero_filters"] = m_zero_filter;
|
||||
request["max_results"] = m_max_items;
|
||||
|
||||
json filtersArray = json::array();
|
||||
|
||||
for( const wxString& filter : m_filters )
|
||||
filtersArray.push_back( filter.ToStdString() );
|
||||
|
||||
request["filters"] = filtersArray;
|
||||
|
||||
// Get the filter function from pcbnew via KIWAY
|
||||
try
|
||||
{
|
||||
KIFACE* kiface = m_kiway->KiFACE( KIWAY::FACE_PCB );
|
||||
|
||||
if( !kiface )
|
||||
{
|
||||
wxString display_name( fpinfo.GetLibNickname() + wxS( ":" ) +
|
||||
fpinfo.GetFootprintName() );
|
||||
|
||||
m_fp_sel_ctrl->Append( display_name, new wxStringClientData( display_name ) );
|
||||
++n_items;
|
||||
|
||||
if( n_items >= m_max_items )
|
||||
break;
|
||||
SelectDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
void* funcPtr = kiface->IfaceOrAddress( KIFACE_FILTER_FOOTPRINTS );
|
||||
|
||||
if( !funcPtr )
|
||||
{
|
||||
SelectDefault();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Call the filter function
|
||||
using FilterFunc = wxString ( * )( const wxString& );
|
||||
FilterFunc filterFootprints = reinterpret_cast<FilterFunc>( funcPtr );
|
||||
|
||||
wxString requestStr = wxString::FromUTF8( request.dump() );
|
||||
wxString responseStr = filterFootprints( requestStr );
|
||||
|
||||
// Parse the response
|
||||
json response = json::parse( responseStr.ToStdString() );
|
||||
|
||||
if( response.is_array() )
|
||||
{
|
||||
for( const auto& item : response )
|
||||
{
|
||||
if( item.is_string() )
|
||||
{
|
||||
wxString fpName = wxString::FromUTF8( item.get<std::string>() );
|
||||
m_fp_sel_ctrl->Append( fpName, new wxStringClientData( fpName ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
// JSON parsing or other error - just show default
|
||||
}
|
||||
|
||||
SelectDefault();
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
#include <settings/settings_manager.h>
|
||||
#include <settings/cvpcb_settings.h>
|
||||
#include <display_footprints_frame.h>
|
||||
#include <footprint_info_impl.h>
|
||||
#include <kiplatform/ui.h>
|
||||
#include <listboxes.h>
|
||||
#include <tools/cvpcb_actions.h>
|
||||
@@ -81,7 +82,7 @@ CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
||||
m_cannotClose = false;
|
||||
m_skipComponentSelect = false;
|
||||
m_filteringOptions = FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST;
|
||||
m_FootprintsList = FOOTPRINT_LIST::GetInstance( Kiway() );
|
||||
m_FootprintsList = new FOOTPRINT_LIST_IMPL();
|
||||
m_initialized = false;
|
||||
m_aboutTitle = _( "Assign Footprints" );
|
||||
|
||||
@@ -241,6 +242,7 @@ CVPCB_MAINFRAME::~CVPCB_MAINFRAME()
|
||||
delete m_actions;
|
||||
delete m_toolManager;
|
||||
delete m_toolDispatcher;
|
||||
delete m_FootprintsList;
|
||||
|
||||
m_auimgr.UnInit();
|
||||
}
|
||||
|
||||
@@ -384,21 +384,13 @@ wxPanel* PANEL_SYMBOL_CHOOSER::constructRightPanel( wxWindow* aParent )
|
||||
|
||||
if( m_show_footprints )
|
||||
{
|
||||
FOOTPRINT_LIST* fp_list = FOOTPRINT_LIST::GetInstance( m_frame->Kiway() );
|
||||
|
||||
sizer->Add( m_symbol_preview, 11, wxEXPAND | wxALL, 5 );
|
||||
|
||||
if ( fp_list )
|
||||
{
|
||||
if( m_allow_field_edits )
|
||||
m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( m_frame, panel, fp_list, true );
|
||||
m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( m_frame, panel );
|
||||
sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxLEFT | wxRIGHT, 5 );
|
||||
|
||||
m_fp_preview = new FOOTPRINT_PREVIEW_WIDGET( panel, m_frame->Kiway() );
|
||||
m_fp_preview->SetUserUnits( m_frame->GetUserUnits() );
|
||||
}
|
||||
|
||||
if( m_fp_sel_ctrl )
|
||||
sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
|
||||
m_fp_preview = new FOOTPRINT_PREVIEW_WIDGET( panel, m_frame->Kiway() );
|
||||
m_fp_preview->SetUserUnits( m_frame->GetUserUnits() );
|
||||
|
||||
if( m_fp_preview )
|
||||
sizer->Add( m_fp_preview, 10, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
|
||||
|
||||
@@ -169,9 +169,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual void WriteCacheToFile( const wxString& aFilePath ) {};
|
||||
virtual void ReadCacheFromFile( const wxString& aFilePath ){};
|
||||
|
||||
/**
|
||||
* @return the number of items stored in list
|
||||
*/
|
||||
@@ -256,15 +253,6 @@ public:
|
||||
|
||||
FOOTPRINT_LIBRARY_ADAPTER* GetAdapter() const { return m_adapter; }
|
||||
|
||||
/**
|
||||
* Factory function to return a #FOOTPRINT_LIST via Kiway.
|
||||
*
|
||||
* This is not guaranteed to succeed and will return null if the kiface is not available.
|
||||
*
|
||||
* @param aKiway active kiway instance.
|
||||
*/
|
||||
static FOOTPRINT_LIST* GetInstance( KIWAY& aKiway );
|
||||
|
||||
protected:
|
||||
FOOTPRINT_LIBRARY_ADAPTER* m_adapter; ///< no ownership
|
||||
|
||||
|
||||
@@ -31,14 +31,13 @@ enum KIFACE_ADDR_ID : int
|
||||
{
|
||||
KIFACE_ID_INVALID,
|
||||
|
||||
/**
|
||||
* Return a pointer to the global instance of FOOTPRINT_LIST from pcbnew.
|
||||
* Type is FOOTPRINT_LIST*
|
||||
* Caller does NOT own.
|
||||
*/
|
||||
KIFACE_FOOTPRINT_LIST,
|
||||
KIFACE_FOOTPRINT_LIBRARY_ADAPTER,
|
||||
|
||||
/// Function pointer type: wxString (*)(const wxString& aFilterJson)
|
||||
/// Input JSON: {"pin_count": N, "filters": ["pattern1", ...], "zero_filters": bool, "max_results": N}
|
||||
/// Output JSON: ["lib:footprint1", "lib:footprint2", ...]
|
||||
KIFACE_FILTER_FOOTPRINTS,
|
||||
|
||||
KIFACE_LOAD_SCHEMATIC,
|
||||
KIFACE_NETLIST_SCHEMATIC,
|
||||
KIFACE_SCRIPTING_LEGACY,
|
||||
|
||||
@@ -20,15 +20,14 @@
|
||||
#ifndef FOOTPRINT_SELECT_WIDGET_H
|
||||
#define FOOTPRINT_SELECT_WIDGET_H
|
||||
|
||||
#include <footprint_filter.h>
|
||||
#include <footprint_info.h>
|
||||
#include <vector>
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/panel.h>
|
||||
|
||||
class KIWAY;
|
||||
class PROJECT;
|
||||
class FOOTPRINT_CHOICE;
|
||||
class wxWindow;
|
||||
class EDA_DRAW_FRAME;
|
||||
|
||||
/**
|
||||
* This event is fired when a footprint is selected. The string data of the
|
||||
@@ -42,36 +41,21 @@ public:
|
||||
/**
|
||||
* Construct a footprint selector widget.
|
||||
*
|
||||
* This requires references to an external footprint loader, and an external
|
||||
* unique_ptr-to-FOOTPRINT_LIST. The latter will be populated with a
|
||||
* FOOTPRINT_LIST instance the first time Load() is called.
|
||||
*
|
||||
* The reason for this is that footprint loading tends to be very expensive,
|
||||
* especially when using online libraries. The caller is expected to keep
|
||||
* these objects around (e.g. they may be statics on the dialog this
|
||||
* FOOTPRINT_SELECT_WIDGET is created in) so footprints do not have to be
|
||||
* loaded more than once.
|
||||
*
|
||||
* @param aFrame - parent frame for context
|
||||
* @param aParent - parent window
|
||||
* @param aFpList - FOOTPRINT_LIST container
|
||||
* @param aUpdate - whether to call UpdateList() automatically when finished loading
|
||||
* @param aMaxItems - maximum number of filter items to display, in addition to
|
||||
* Default and Other
|
||||
* @param aMaxItems - maximum number of filter items to display
|
||||
*/
|
||||
FOOTPRINT_SELECT_WIDGET( EDA_DRAW_FRAME* aFrame, wxWindow* aParent, FOOTPRINT_LIST* aFpList,
|
||||
bool aUpdate = true, int aMaxItems = 400 );
|
||||
FOOTPRINT_SELECT_WIDGET( EDA_DRAW_FRAME* aFrame, wxWindow* aParent, int aMaxItems = 400 );
|
||||
|
||||
virtual ~FOOTPRINT_SELECT_WIDGET()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Start loading. This function returns immediately; footprints will
|
||||
* continue to load in the background.
|
||||
* Initialize the widget with a KIWAY for cross-module communication.
|
||||
*
|
||||
* @param aKiway - active kiway instance. This is cached for use when "Other"
|
||||
* is selected.
|
||||
* @param aProject - current project
|
||||
* @param aKiway - active kiway instance used to fetch filtered footprints from pcbnew
|
||||
* @param aProject - current project (unused, kept for API compatibility)
|
||||
*/
|
||||
void Load( KIWAY& aKiway, PROJECT& aProject );
|
||||
|
||||
@@ -104,11 +88,10 @@ public:
|
||||
void SetDefaultFootprint( const wxString& aFp );
|
||||
|
||||
/**
|
||||
* Update the contents of the list to match the filters. Has no effect if
|
||||
* the footprint list has not been loaded yet. The "default" footprint will be
|
||||
* selected.
|
||||
* Update the contents of the list by fetching filtered footprints from pcbnew.
|
||||
* The "default" footprint will be selected.
|
||||
*
|
||||
* @return true if the footprint list has been loaded (and the list was updated)
|
||||
* @return true if the list was updated successfully
|
||||
*/
|
||||
bool UpdateList();
|
||||
|
||||
@@ -123,17 +106,18 @@ public:
|
||||
virtual bool Enable( bool aEnable = true ) override;
|
||||
|
||||
private:
|
||||
FOOTPRINT_CHOICE* m_fp_sel_ctrl;
|
||||
wxSizer* m_sizer;
|
||||
FOOTPRINT_CHOICE* m_fp_sel_ctrl;
|
||||
wxSizer* m_sizer;
|
||||
|
||||
bool m_update;
|
||||
int m_max_items;
|
||||
wxString m_default_footprint;
|
||||
int m_max_items;
|
||||
wxString m_default_footprint;
|
||||
|
||||
FOOTPRINT_LIST* m_fp_list;
|
||||
FOOTPRINT_FILTER m_fp_filter;
|
||||
bool m_zero_filter;
|
||||
EDA_DRAW_FRAME* m_frame;
|
||||
// Filter parameters
|
||||
int m_pin_count;
|
||||
wxArrayString m_filters;
|
||||
bool m_zero_filter;
|
||||
|
||||
KIWAY* m_kiway;
|
||||
|
||||
void OnComboBox( wxCommandEvent& aEvent );
|
||||
};
|
||||
|
||||
@@ -297,15 +297,3 @@ FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() :
|
||||
m_cancelled( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void FOOTPRINT_LIST_IMPL::WriteCacheToFile( const wxString& aFilePath )
|
||||
{
|
||||
// Cache disabled; no longer needed with async library loading
|
||||
}
|
||||
|
||||
|
||||
void FOOTPRINT_LIST_IMPL::ReadCacheFromFile( const wxString& aFilePath )
|
||||
{
|
||||
// Cache disabled; no longer needed with async library loading
|
||||
}
|
||||
|
||||
@@ -87,9 +87,6 @@ public:
|
||||
FOOTPRINT_LIST_IMPL();
|
||||
virtual ~FOOTPRINT_LIST_IMPL() {};
|
||||
|
||||
void WriteCacheToFile( const wxString& aFilePath ) override;
|
||||
void ReadCacheFromFile( const wxString& aFilePath ) override;
|
||||
|
||||
bool ReadFootprintFiles( FOOTPRINT_LIBRARY_ADAPTER* aAdapter, const wxString* aNickname = nullptr,
|
||||
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
|
||||
|
||||
@@ -127,7 +124,5 @@ private:
|
||||
std::mutex m_loadInProgress;
|
||||
};
|
||||
|
||||
extern FOOTPRINT_LIST_IMPL GFootprintList; // KIFACE scope.
|
||||
|
||||
|
||||
#endif // FOOTPRINT_INFO_IMPL_H
|
||||
|
||||
@@ -38,6 +38,10 @@ std::map<wxString, LIB_DATA> FOOTPRINT_LIBRARY_ADAPTER::GlobalLibraries;
|
||||
|
||||
std::shared_mutex FOOTPRINT_LIBRARY_ADAPTER::GlobalLibraryMutex;
|
||||
|
||||
std::map<wxString, std::vector<std::unique_ptr<FOOTPRINT>>> FOOTPRINT_LIBRARY_ADAPTER::PreloadedFootprints;
|
||||
|
||||
std::shared_mutex FOOTPRINT_LIBRARY_ADAPTER::PreloadedFootprintsMutex;
|
||||
|
||||
|
||||
FOOTPRINT_LIBRARY_ADAPTER::FOOTPRINT_LIBRARY_ADAPTER( LIBRARY_MANAGER& aManager ) :
|
||||
LIBRARY_MANAGER_ADAPTER( aManager )
|
||||
@@ -53,9 +57,45 @@ wxString FOOTPRINT_LIBRARY_ADAPTER::GlobalPathEnvVariableName()
|
||||
|
||||
void FOOTPRINT_LIBRARY_ADAPTER::enumerateLibrary( LIB_DATA* aLib )
|
||||
{
|
||||
wxArrayString dummyList;
|
||||
wxArrayString namesAS;
|
||||
std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
|
||||
pcbplugin( aLib )->FootprintEnumerate( dummyList, getUri( aLib->row ), false, &options );
|
||||
PCB_IO* plugin = pcbplugin( aLib );
|
||||
wxString uri = getUri( aLib->row );
|
||||
wxString nickname = aLib->row->Nickname();
|
||||
|
||||
plugin->FootprintEnumerate( namesAS, uri, false, &options );
|
||||
|
||||
std::vector<std::unique_ptr<FOOTPRINT>> footprints;
|
||||
footprints.reserve( namesAS.size() );
|
||||
|
||||
for( const wxString& footprintName : namesAS )
|
||||
{
|
||||
FOOTPRINT* footprint = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
footprint = plugin->FootprintLoad( uri, footprintName, false, &options );
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
wxLogTrace( traceLibraries, "FP: Exception loading footprint %s from %s: %s",
|
||||
footprintName, nickname, e.What() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !footprint )
|
||||
continue;
|
||||
|
||||
LIB_ID id = footprint->GetFPID();
|
||||
id.SetLibNickname( nickname );
|
||||
footprint->SetFPID( id );
|
||||
footprints.emplace_back( footprint );
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock lock( PreloadedFootprintsMutex );
|
||||
PreloadedFootprints[nickname] = std::move( footprints );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,97 +140,21 @@ std::vector<FOOTPRINT*> FOOTPRINT_LIBRARY_ADAPTER::GetFootprints( const wxString
|
||||
{
|
||||
std::vector<FOOTPRINT*> footprints;
|
||||
|
||||
std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
|
||||
std::shared_lock lock( PreloadedFootprintsMutex );
|
||||
auto it = PreloadedFootprints.find( aNickname );
|
||||
|
||||
if( !maybeLib )
|
||||
if( it == PreloadedFootprints.end() )
|
||||
return footprints;
|
||||
|
||||
const LIB_DATA* lib = *maybeLib;
|
||||
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
|
||||
wxArrayString namesAS;
|
||||
footprints.reserve( it->second.size() );
|
||||
|
||||
try
|
||||
{
|
||||
pcbplugin( lib )->FootprintEnumerate( namesAS, getUri( lib->row ), true, &options );
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
wxLogTrace( traceLibraries, "FP: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
|
||||
}
|
||||
|
||||
for( const wxString& footprintName : namesAS )
|
||||
{
|
||||
FOOTPRINT* footprint = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
footprint = pcbplugin( lib )->FootprintLoad( getUri( lib->row ),footprintName, false, &options );
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
wxLogTrace( traceLibraries, "FP: Exception loading footprint from %s: %s", lib->row->Nickname(), e.What() );
|
||||
continue;
|
||||
}
|
||||
|
||||
wxCHECK2( footprint, continue );
|
||||
|
||||
LIB_ID id = footprint->GetFPID();
|
||||
id.SetLibNickname( lib->row->Nickname() );
|
||||
footprint->SetFPID( id );
|
||||
footprints.emplace_back( footprint );
|
||||
}
|
||||
for( const auto& fp : it->second )
|
||||
footprints.push_back( fp.get() );
|
||||
|
||||
return footprints;
|
||||
}
|
||||
|
||||
|
||||
std::vector<const FOOTPRINT*> FOOTPRINT_LIBRARY_ADAPTER::GetCachedFootprints( const wxString& aNickname,
|
||||
bool aBestEfforts )
|
||||
{
|
||||
std::vector<const FOOTPRINT*> footprints;
|
||||
|
||||
std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
|
||||
|
||||
if( !maybeLib )
|
||||
return footprints;
|
||||
|
||||
const LIB_DATA* lib = *maybeLib;
|
||||
std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
|
||||
wxArrayString namesAS;
|
||||
|
||||
try
|
||||
{
|
||||
pcbplugin( lib )->FootprintEnumerate( namesAS, getUri( lib->row ), true, &options );
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
wxLogTrace( traceLibraries, "FP: Exception enumerating library %s: %s",
|
||||
lib->row->Nickname(), e.What() );
|
||||
}
|
||||
|
||||
for( const wxString& footprintName : namesAS )
|
||||
{
|
||||
const FOOTPRINT* footprint = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
footprint = pcbplugin( lib )->GetEnumeratedFootprint( getUri( lib->row ), footprintName,
|
||||
&options );
|
||||
}
|
||||
catch( IO_ERROR& e )
|
||||
{
|
||||
wxLogTrace( traceLibraries, "FP: Exception getting cached footprint from %s: %s",
|
||||
lib->row->Nickname(), e.What() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( footprint )
|
||||
footprints.emplace_back( footprint );
|
||||
}
|
||||
|
||||
return footprints;
|
||||
}
|
||||
|
||||
|
||||
std::vector<wxString> FOOTPRINT_LIBRARY_ADAPTER::GetFootprintNames( const wxString& aNickname, bool aBestEfforts )
|
||||
{
|
||||
|
||||
@@ -58,19 +58,6 @@ public:
|
||||
*/
|
||||
std::vector<FOOTPRINT*> GetFootprints( const wxString& aNickname, bool aBestEfforts = false );
|
||||
|
||||
/**
|
||||
* Retrieves const pointers to cached footprints without cloning. This is much faster than
|
||||
* GetFootprints() for read-only access (e.g., populating tree views with metadata).
|
||||
*
|
||||
* The returned pointers are owned by the library plugin cache and remain valid as long as
|
||||
* the library is loaded. Callers must NOT delete or modify the returned footprints.
|
||||
*
|
||||
* @param aNickname is the library to query
|
||||
* @param aBestEfforts if true, don't throw on errors, just return a smaller or empty list.
|
||||
* @return const pointers to cached footprints
|
||||
*/
|
||||
std::vector<const FOOTPRINT*> GetCachedFootprints( const wxString& aNickname,
|
||||
bool aBestEfforts = false );
|
||||
|
||||
/**
|
||||
* Retrieves a list of footprint names contained in a given loaded library
|
||||
@@ -191,6 +178,11 @@ private:
|
||||
|
||||
static std::map<wxString, LIB_DATA> GlobalLibraries;
|
||||
static std::shared_mutex GlobalLibraryMutex;
|
||||
|
||||
/// Storage for preloaded footprints, indexed by library nickname.
|
||||
/// These are cloned during library enumeration so GetFootprints() returns instantly.
|
||||
static std::map<wxString, std::vector<std::unique_ptr<FOOTPRINT>>> PreloadedFootprints;
|
||||
static std::shared_mutex PreloadedFootprintsMutex;
|
||||
};
|
||||
|
||||
#endif //FOOTPRINT_LIBRARY_ADAPTER_H
|
||||
|
||||
@@ -485,22 +485,16 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
|
||||
if( !getCurNickname() )
|
||||
setCurFootprintName( wxEmptyString );
|
||||
|
||||
auto fp_info_list = FOOTPRINT_LIST::GetInstance( Kiway() );
|
||||
|
||||
FOOTPRINT_LIBRARY_ADAPTER* adapter = PROJECT_PCB::FootprintLibAdapter( &Prj() );
|
||||
wxString nickname = getCurNickname();
|
||||
|
||||
fp_info_list->ReadFootprintFiles( PROJECT_PCB::FootprintLibAdapter( &Prj() ), !nickname ? nullptr : &nickname );
|
||||
if( !nickname )
|
||||
return;
|
||||
|
||||
if( fp_info_list->GetErrorCount() )
|
||||
{
|
||||
if( KISTATUSBAR* statusBar = dynamic_cast<KISTATUSBAR*>( GetStatusBar() ) )
|
||||
statusBar->SetLoadWarningMessages( fp_info_list->GetErrorMessages() );
|
||||
std::vector<FOOTPRINT*> footprints = adapter->GetFootprints( nickname, true );
|
||||
|
||||
// For footprint libraries that support one footprint per file, there may have been
|
||||
// valid footprints read so show the footprints that loaded properly.
|
||||
if( fp_info_list->GetList().empty() )
|
||||
return;
|
||||
}
|
||||
if( footprints.empty() )
|
||||
return;
|
||||
|
||||
std::set<wxString> excludes;
|
||||
|
||||
@@ -513,24 +507,26 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
|
||||
const wxString filterTerm = tokenizer.GetNextToken().Lower();
|
||||
EDA_COMBINED_MATCHER matcher( filterTerm, CTX_LIBITEM );
|
||||
|
||||
for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() )
|
||||
for( FOOTPRINT* footprint : footprints )
|
||||
{
|
||||
std::vector<SEARCH_TERM> searchTerms = footprint->GetSearchTerms();
|
||||
int matched = matcher.ScoreTerms( searchTerms );
|
||||
|
||||
if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)footprint->GetPadCount() )
|
||||
if( filterTerm.IsNumber() && wxAtoi( filterTerm ) == (int)footprint->GetPadCount( DO_NOT_INCLUDE_NPTH ) )
|
||||
matched++;
|
||||
|
||||
if( !matched )
|
||||
excludes.insert( footprint->GetFootprintName() );
|
||||
excludes.insert( footprint->GetFPID().GetLibItemName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( const std::unique_ptr<FOOTPRINT_INFO>& footprint : fp_info_list->GetList() )
|
||||
for( FOOTPRINT* footprint : footprints )
|
||||
{
|
||||
if( !excludes.count( footprint->GetFootprintName() ) )
|
||||
m_fpList->Append( footprint->GetFootprintName() );
|
||||
wxString fpName = footprint->GetFPID().GetLibItemName();
|
||||
|
||||
if( !excludes.count( fpName ) )
|
||||
m_fpList->Append( fpName );
|
||||
}
|
||||
|
||||
int index = wxNOT_FOUND;
|
||||
|
||||
@@ -60,14 +60,12 @@ void FP_TREE_MODEL_ADAPTER::AddLibraries( EDA_BASE_FRAME* aParent )
|
||||
bool pinned = alg::contains( cfg->m_Session.pinned_fp_libs, libName )
|
||||
|| alg::contains( project.m_PinnedFootprintLibs, libName );
|
||||
|
||||
// Use cached footprints (no cloning) for faster tree population. The const_cast is safe
|
||||
// because DoAddLibrary only reads metadata from the footprints, it doesn't modify them.
|
||||
std::vector<const FOOTPRINT*> footprints = m_libs->GetCachedFootprints( libName, true );
|
||||
std::vector<FOOTPRINT*> footprints = m_libs->GetFootprints( libName, true );
|
||||
std::vector<LIB_TREE_ITEM*> treeItems;
|
||||
treeItems.reserve( footprints.size() );
|
||||
|
||||
for( const FOOTPRINT* fp : footprints )
|
||||
treeItems.push_back( const_cast<FOOTPRINT*>( fp ) );
|
||||
for( FOOTPRINT* fp : footprints )
|
||||
treeItems.push_back( fp );
|
||||
|
||||
DoAddLibrary( libName, *m_libs->GetLibraryDescription( libName ), treeItems, pinned, true );
|
||||
}
|
||||
|
||||
@@ -120,13 +120,12 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::Sync( FOOTPRINT_LIBRARY_ADAPTER* aLibs )
|
||||
bool pinned = alg::contains( cfg->m_Session.pinned_fp_libs, libName )
|
||||
|| alg::contains( project.m_PinnedFootprintLibs, libName );
|
||||
|
||||
// Use cached footprints (no cloning) for faster tree population
|
||||
std::vector<const FOOTPRINT*> footprints = m_libs->GetCachedFootprints( libName, true );
|
||||
std::vector<FOOTPRINT*> footprints = m_libs->GetFootprints( libName, true );
|
||||
std::vector<LIB_TREE_ITEM*> treeItems;
|
||||
treeItems.reserve( footprints.size() );
|
||||
|
||||
for( const FOOTPRINT* fp : footprints )
|
||||
treeItems.push_back( const_cast<FOOTPRINT*>( fp ) );
|
||||
for( FOOTPRINT* fp : footprints )
|
||||
treeItems.push_back( fp );
|
||||
|
||||
DoAddLibrary( libName, *optDesc, treeItems, pinned, true );
|
||||
|
||||
@@ -148,13 +147,12 @@ int FP_TREE_SYNCHRONIZING_ADAPTER::GetLibrariesCount() const
|
||||
|
||||
void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNode )
|
||||
{
|
||||
// Use cached footprints (no cloning) for faster updates
|
||||
std::vector<const FOOTPRINT*> footprints = m_libs->GetCachedFootprints( aLibNode.m_Name, true );
|
||||
std::vector<FOOTPRINT*> footprints = m_libs->GetFootprints( aLibNode.m_Name, true );
|
||||
|
||||
// Build a map of footprint names for quick lookup
|
||||
std::map<wxString, const FOOTPRINT*> fpMap;
|
||||
std::map<wxString, FOOTPRINT*> fpMap;
|
||||
|
||||
for( const FOOTPRINT* fp : footprints )
|
||||
for( FOOTPRINT* fp : footprints )
|
||||
fpMap[fp->GetFPID().GetLibItemName()] = fp;
|
||||
|
||||
// Remove items that no longer exist
|
||||
@@ -164,9 +162,7 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNo
|
||||
|
||||
if( fpIt != fpMap.end() )
|
||||
{
|
||||
// const_cast is safe because Update() only reads metadata from the footprint
|
||||
static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update(
|
||||
const_cast<FOOTPRINT*>( fpIt->second ) );
|
||||
static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( fpIt->second );
|
||||
fpMap.erase( fpIt );
|
||||
++nodeIt;
|
||||
}
|
||||
@@ -176,9 +172,9 @@ void FP_TREE_SYNCHRONIZING_ADAPTER::updateLibrary( LIB_TREE_NODE_LIBRARY& aLibNo
|
||||
}
|
||||
}
|
||||
|
||||
// Add new items (const_cast is safe because AddItem only reads metadata)
|
||||
// Add new items
|
||||
for( auto& [name, fp] : fpMap )
|
||||
aLibNode.AddItem( const_cast<FOOTPRINT*>( fp ) );
|
||||
aLibNode.AddItem( fp );
|
||||
|
||||
aLibNode.AssignIntrinsicRanks( m_shownColumns );
|
||||
m_libMap.insert( aLibNode.m_Name );
|
||||
|
||||
+136
-10
@@ -48,6 +48,8 @@
|
||||
#include <footprint_wizard_frame.h>
|
||||
#include <footprint_preview_panel.h>
|
||||
#include <footprint_info_impl.h>
|
||||
#include <footprint.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <dialogs/dialog_configure_paths.h>
|
||||
#include <dialogs/panel_grid_settings.h>
|
||||
#include <panel_display_options.h>
|
||||
@@ -85,6 +87,133 @@
|
||||
|
||||
extern "C" PyObject* PyInit__pcbnew( void );
|
||||
|
||||
|
||||
/**
|
||||
* Filter footprints based on criteria passed as JSON.
|
||||
*
|
||||
* Input JSON format:
|
||||
* {"pin_count": N, "filters": ["pattern1", ...], "zero_filters": bool, "max_results": N}
|
||||
*
|
||||
* Output JSON format:
|
||||
* ["lib:footprint1", "lib:footprint2", ...]
|
||||
*
|
||||
* @param aFilterJson JSON string with filter parameters
|
||||
* @return JSON string with array of matching footprint LIB_IDs
|
||||
*/
|
||||
static wxString filterFootprints( const wxString& aFilterJson )
|
||||
{
|
||||
using json = nlohmann::json;
|
||||
|
||||
try
|
||||
{
|
||||
json input = json::parse( aFilterJson.ToStdString() );
|
||||
|
||||
int pinCount = input.value( "pin_count", 0 );
|
||||
bool zeroFilters = input.value( "zero_filters", true );
|
||||
int maxResults = input.value( "max_results", 400 );
|
||||
|
||||
wxArrayString filters;
|
||||
|
||||
if( input.contains( "filters" ) && input["filters"].is_array() )
|
||||
{
|
||||
for( const auto& f : input["filters"] )
|
||||
{
|
||||
if( f.is_string() )
|
||||
filters.Add( wxString::FromUTF8( f.get<std::string>() ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool hasFilters = ( pinCount > 0 || !filters.IsEmpty() );
|
||||
|
||||
if( zeroFilters && !hasFilters )
|
||||
return wxS( "[]" );
|
||||
|
||||
PROJECT* project = nullptr;
|
||||
|
||||
if( wxTheApp )
|
||||
{
|
||||
wxWindow* focus = wxWindow::FindFocus();
|
||||
wxWindow* top = focus ? wxGetTopLevelParent( focus ) : wxTheApp->GetTopWindow();
|
||||
|
||||
if( top )
|
||||
{
|
||||
if( KIWAY_HOLDER* holder = dynamic_cast<KIWAY_HOLDER*>( top ) )
|
||||
project = &holder->Prj();
|
||||
}
|
||||
}
|
||||
|
||||
if( !project )
|
||||
project = &Pgm().GetSettingsManager().Prj();
|
||||
|
||||
FOOTPRINT_LIBRARY_ADAPTER* adapter = PROJECT_PCB::FootprintLibAdapter( project );
|
||||
|
||||
if( !adapter )
|
||||
return wxS( "[]" );
|
||||
|
||||
adapter->AsyncLoad();
|
||||
adapter->BlockUntilLoaded();
|
||||
|
||||
// Iterate through preloaded footprints directly instead of re-reading from disk
|
||||
json output = json::array();
|
||||
int count = 0;
|
||||
|
||||
for( const wxString& nickname : adapter->GetLibraryNames() )
|
||||
{
|
||||
std::vector<FOOTPRINT*> footprints = adapter->GetFootprints( nickname, true );
|
||||
|
||||
for( FOOTPRINT* fp : footprints )
|
||||
{
|
||||
if( !fp )
|
||||
continue;
|
||||
|
||||
// Pin count filter
|
||||
if( pinCount > 0 )
|
||||
{
|
||||
int fpPadCount = fp->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
|
||||
|
||||
if( fpPadCount != pinCount )
|
||||
continue;
|
||||
}
|
||||
|
||||
// Footprint filter patterns
|
||||
if( !filters.IsEmpty() )
|
||||
{
|
||||
wxString fpName = fp->GetFPID().GetLibItemName();
|
||||
bool matches = false;
|
||||
|
||||
for( const wxString& filter : filters )
|
||||
{
|
||||
if( fpName.Matches( filter ) )
|
||||
{
|
||||
matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !matches )
|
||||
continue;
|
||||
}
|
||||
|
||||
wxString libId = fp->GetFPID().Format();
|
||||
output.push_back( libId.ToStdString() );
|
||||
|
||||
if( ++count >= maxResults )
|
||||
break;
|
||||
}
|
||||
|
||||
if( count >= maxResults )
|
||||
break;
|
||||
}
|
||||
|
||||
return wxString::FromUTF8( output.dump() );
|
||||
}
|
||||
catch( const std::exception& e )
|
||||
{
|
||||
return wxS( "[]" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace PCB {
|
||||
|
||||
static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
||||
@@ -367,10 +496,6 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
||||
{
|
||||
switch( aDataId )
|
||||
{
|
||||
// Return a pointer to the global instance of the footprint list.
|
||||
case KIFACE_FOOTPRINT_LIST:
|
||||
return (void*) &GFootprintList;
|
||||
|
||||
case KIFACE_FOOTPRINT_LIBRARY_ADAPTER:
|
||||
{
|
||||
// This is the mechanism by which FOOTPRINT_SELECT_WIDGET can get access to the adapter
|
||||
@@ -395,6 +520,13 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
|
||||
return PROJECT_PCB::FootprintLibAdapter( project );
|
||||
}
|
||||
|
||||
case KIFACE_FILTER_FOOTPRINTS:
|
||||
{
|
||||
// Return function pointer for filtering footprints
|
||||
// Signature: wxString (*)(const wxString& aFilterJson)
|
||||
return reinterpret_cast<void*>( &filterFootprints );
|
||||
}
|
||||
|
||||
case KIFACE_SCRIPTING_LEGACY:
|
||||
return reinterpret_cast<void*>( PyInit__pcbnew );
|
||||
|
||||
@@ -446,12 +578,6 @@ KIFACE_API KIFACE* KIFACE_GETTER( int* aKIFACEversion, int aKiwayVersion, PGM_BA
|
||||
}
|
||||
|
||||
|
||||
/// The global footprint info table. This is performance-intensive to build so we
|
||||
/// keep a hash-stamped global version. Any deviation from the request vs. stored
|
||||
/// hash will result in it being rebuilt.
|
||||
FOOTPRINT_LIST_IMPL GFootprintList;
|
||||
|
||||
|
||||
bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
|
||||
{
|
||||
// This is process-level-initialization, not project-level-initialization of the DSO.
|
||||
|
||||
Reference in New Issue
Block a user