2014-02-14 08:05:04 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
|
2023-05-01 20:26:29 +00:00
|
|
|
* Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
2014-02-14 08:05:04 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2017-02-19 02:39:55 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
#include <pgm_base.h>
|
|
|
|
#include <symbol_library.h> // For SYMBOL_LIBRARY_FILTER
|
2023-09-28 13:09:45 +00:00
|
|
|
#include <panel_symbol_chooser.h>
|
2021-09-14 22:45:14 +00:00
|
|
|
#include <kiface_base.h>
|
2014-02-14 08:05:04 +00:00
|
|
|
#include <sch_base_frame.h>
|
2023-09-28 03:04:53 +00:00
|
|
|
#include <project_sch.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <widgets/lib_tree.h>
|
2018-08-28 13:26:39 +00:00
|
|
|
#include <widgets/symbol_preview_widget.h>
|
2023-09-28 17:18:19 +00:00
|
|
|
#include <widgets/footprint_preview_widget.h>
|
|
|
|
#include <widgets/footprint_select_widget.h>
|
2023-09-28 13:09:45 +00:00
|
|
|
#include <settings/settings_manager.h>
|
|
|
|
#include <project/project_file.h>
|
2023-09-28 17:18:19 +00:00
|
|
|
#include <eeschema_settings.h>
|
2023-09-28 13:09:45 +00:00
|
|
|
#include <symbol_editor_settings.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <wx/button.h>
|
2019-08-06 06:18:51 +00:00
|
|
|
#include <wx/clipbrd.h>
|
2020-01-13 01:44:19 +00:00
|
|
|
#include <wx/panel.h>
|
|
|
|
#include <wx/sizer.h>
|
|
|
|
#include <wx/splitter.h>
|
|
|
|
#include <wx/timer.h>
|
2021-06-03 11:49:49 +00:00
|
|
|
#include <wx/wxhtml.h>
|
2023-09-28 13:09:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
wxString PANEL_SYMBOL_CHOOSER::g_symbolSearchString;
|
|
|
|
wxString PANEL_SYMBOL_CHOOSER::g_powerSearchString;
|
|
|
|
|
|
|
|
|
|
|
|
PANEL_SYMBOL_CHOOSER::PANEL_SYMBOL_CHOOSER( SCH_BASE_FRAME* aFrame, wxWindow* aParent,
|
|
|
|
const SYMBOL_LIBRARY_FILTER* aFilter,
|
2021-03-13 22:13:47 +00:00
|
|
|
std::vector<PICKED_SYMBOL>& aHistoryList,
|
|
|
|
std::vector<PICKED_SYMBOL>& aAlreadyPlaced,
|
2023-09-28 13:09:45 +00:00
|
|
|
bool aAllowFieldEdits, bool aShowFootprints,
|
|
|
|
std::function<void()> aCloseHandler ) :
|
|
|
|
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
|
|
|
|
m_symbol_preview( nullptr ),
|
|
|
|
m_hsplitter( nullptr ),
|
|
|
|
m_vsplitter( nullptr ),
|
|
|
|
m_fp_sel_ctrl( nullptr ),
|
|
|
|
m_fp_preview( nullptr ),
|
|
|
|
m_tree( nullptr ),
|
|
|
|
m_details( nullptr ),
|
|
|
|
m_frame( aFrame ),
|
|
|
|
m_closeHandler( std::move( aCloseHandler ) ),
|
|
|
|
m_showPower( false ),
|
|
|
|
m_allow_field_edits( aAllowFieldEdits ),
|
|
|
|
m_show_footprints( aShowFootprints )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
SYMBOL_LIB_TABLE* libs = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() );
|
|
|
|
COMMON_SETTINGS::SESSION& session = Pgm().GetCommonSettings()->m_Session;
|
|
|
|
PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
|
2022-06-04 21:15:12 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
// Make sure settings are loaded before we start running multi-threaded symbol loaders
|
|
|
|
Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
|
|
|
|
Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
|
|
|
|
|
|
|
|
m_adapter = SYMBOL_TREE_MODEL_ADAPTER::Create( m_frame, libs );
|
|
|
|
SYMBOL_TREE_MODEL_ADAPTER* adapter = static_cast<SYMBOL_TREE_MODEL_ADAPTER*>( m_adapter.get() );
|
|
|
|
bool loaded = false;
|
|
|
|
|
|
|
|
if( aFilter )
|
|
|
|
{
|
|
|
|
const wxArrayString& liblist = aFilter->GetAllowedLibList();
|
|
|
|
|
|
|
|
for( const wxString& nickname : liblist )
|
|
|
|
{
|
|
|
|
if( libs->HasLibrary( nickname, true ) )
|
|
|
|
{
|
|
|
|
loaded = true;
|
|
|
|
|
|
|
|
bool pinned = alg::contains( session.pinned_symbol_libs, nickname )
|
|
|
|
|| alg::contains( project.m_PinnedSymbolLibs, nickname );
|
|
|
|
|
|
|
|
if( libs->FindRow( nickname )->GetIsVisible() )
|
|
|
|
adapter->AddLibrary( nickname, pinned );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
adapter->AssignIntrinsicRanks();
|
|
|
|
|
|
|
|
if( aFilter->GetFilterPowerSymbols() )
|
|
|
|
{
|
2023-10-02 15:24:31 +00:00
|
|
|
// HACK ALERT: the only filter ever used for symbols is the power filter, so we
|
2023-11-07 23:53:09 +00:00
|
|
|
// just look for the function pointer being non-null. (What the function does is
|
|
|
|
// therefore immaterial as it's never called.)
|
2023-10-02 15:24:31 +00:00
|
|
|
static std::function<int( LIB_TREE_NODE& )> powerFilter =
|
|
|
|
[]( LIB_TREE_NODE& ) -> int
|
2023-09-29 16:02:57 +00:00
|
|
|
{
|
2023-10-02 15:24:31 +00:00
|
|
|
return 0;
|
2023-09-29 16:02:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
adapter->SetFilter( &powerFilter );
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
m_showPower = true;
|
|
|
|
m_show_footprints = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<LIB_SYMBOL> history_list_storage;
|
|
|
|
std::vector<LIB_TREE_ITEM*> history_list;
|
2021-03-13 22:13:47 +00:00
|
|
|
std::vector<LIB_SYMBOL> already_placed_storage;
|
|
|
|
std::vector<LIB_TREE_ITEM*> already_placed;
|
2023-09-28 13:09:45 +00:00
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
// Lambda to encapsulate the common logic
|
|
|
|
auto processList = [&]( const std::vector<PICKED_SYMBOL>& inputList,
|
|
|
|
std::vector<LIB_SYMBOL>& storageList,
|
|
|
|
std::vector<LIB_TREE_ITEM*>& resultList )
|
2023-09-28 13:09:45 +00:00
|
|
|
{
|
2021-03-13 22:13:47 +00:00
|
|
|
storageList.reserve( inputList.size() );
|
2023-09-28 13:09:45 +00:00
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
for( const PICKED_SYMBOL& i : inputList )
|
2023-09-28 13:09:45 +00:00
|
|
|
{
|
2021-03-13 22:13:47 +00:00
|
|
|
LIB_SYMBOL* symbol = m_frame->GetLibSymbol( i.LibId );
|
2023-09-28 13:09:45 +00:00
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
if( symbol )
|
2023-09-28 13:09:45 +00:00
|
|
|
{
|
2021-03-13 22:13:47 +00:00
|
|
|
storageList.emplace_back( *symbol );
|
2023-09-28 13:09:45 +00:00
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
for( const std::pair<int, wxString>& fieldDef : i.Fields )
|
|
|
|
{
|
|
|
|
LIB_FIELD* field = storageList.back().GetFieldById( fieldDef.first );
|
2023-09-28 13:09:45 +00:00
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
if( field )
|
|
|
|
field->SetText( fieldDef.second );
|
|
|
|
}
|
|
|
|
|
|
|
|
resultList.push_back( &storageList.back() );
|
|
|
|
}
|
2023-09-28 13:09:45 +00:00
|
|
|
}
|
2021-03-13 22:13:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
processList( aHistoryList, history_list_storage, history_list );
|
|
|
|
processList( aAlreadyPlaced, already_placed_storage, already_placed );
|
2023-09-28 13:09:45 +00:00
|
|
|
|
|
|
|
adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
|
|
|
|
history_list, false, true );
|
|
|
|
|
|
|
|
if( !aHistoryList.empty() )
|
|
|
|
adapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit );
|
|
|
|
|
2021-03-13 22:13:47 +00:00
|
|
|
adapter->DoAddLibrary( wxT( "-- " ) + _( "Already Placed" ) + wxT( " --" ), wxEmptyString,
|
|
|
|
already_placed, false, true );
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
const std::vector< wxString > libNicknames = libs->GetLogicalLibs();
|
|
|
|
|
|
|
|
if( !loaded )
|
|
|
|
{
|
|
|
|
if( !adapter->AddLibraries( libNicknames, m_frame ) )
|
|
|
|
{
|
|
|
|
// loading cancelled by user
|
|
|
|
m_closeHandler();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------
|
2023-09-28 17:18:19 +00:00
|
|
|
// Construct the actual panel
|
2023-09-28 13:09:45 +00:00
|
|
|
//
|
2020-10-25 21:27:06 +00:00
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
|
2017-02-24 19:27:49 +00:00
|
|
|
|
2018-01-03 02:52:35 +00:00
|
|
|
// Use a slightly different layout, with a details pane spanning the entire window,
|
|
|
|
// if we're not showing footprints.
|
2020-10-25 21:27:06 +00:00
|
|
|
if( m_show_footprints )
|
2018-07-25 23:16:34 +00:00
|
|
|
{
|
|
|
|
m_hsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
2020-12-20 12:34:48 +00:00
|
|
|
wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
|
2018-01-03 02:52:35 +00:00
|
|
|
|
2018-07-25 23:16:34 +00:00
|
|
|
//Avoid the splitter window being assigned as the Parent to additional windows
|
|
|
|
m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
|
2018-01-03 02:52:35 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_hsplitter, 1, wxEXPAND, 5 );
|
2018-07-25 23:16:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
2020-12-20 12:34:48 +00:00
|
|
|
wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
|
2018-06-06 04:22:13 +00:00
|
|
|
|
2018-07-25 23:16:34 +00:00
|
|
|
m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
2020-12-20 12:34:48 +00:00
|
|
|
wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
|
2018-01-03 02:52:35 +00:00
|
|
|
|
2021-07-16 20:13:26 +00:00
|
|
|
// Avoid the splitter window being assigned as the parent to additional windows.
|
2020-12-20 12:34:48 +00:00
|
|
|
m_vsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
|
2018-07-25 23:16:34 +00:00
|
|
|
m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
|
2014-02-22 12:39:59 +00:00
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
wxPanel* detailsPanel = new wxPanel( m_vsplitter );
|
|
|
|
wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL );
|
2018-07-25 23:16:34 +00:00
|
|
|
detailsPanel->SetSizer( detailsSizer );
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
m_details = new HTML_WINDOW( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize );
|
|
|
|
detailsSizer->Add( m_details, 1, wxEXPAND, 5 );
|
2018-07-25 23:16:34 +00:00
|
|
|
detailsPanel->Layout();
|
|
|
|
detailsSizer->Fit( detailsPanel );
|
|
|
|
|
|
|
|
m_vsplitter->SetSashGravity( 0.5 );
|
|
|
|
m_vsplitter->SetMinimumPaneSize( 20 );
|
|
|
|
m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_vsplitter, 1, wxEXPAND, 5 );
|
2018-07-25 23:16:34 +00:00
|
|
|
}
|
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
wxPanel* treePanel = new wxPanel( m_hsplitter );
|
|
|
|
wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
|
|
|
|
treePanel->SetSizer( treeSizer );
|
|
|
|
|
2022-09-15 13:32:27 +00:00
|
|
|
m_tree = new LIB_TREE( treePanel, m_showPower ? wxT( "power" ) : wxT( "symbols" ),
|
2023-09-28 13:09:45 +00:00
|
|
|
libs, m_adapter, LIB_TREE::FLAGS::ALL_WIDGETS, m_details );
|
2020-12-20 12:34:48 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
treeSizer->Add( m_tree, 1, wxEXPAND, 5 );
|
2020-12-20 12:34:48 +00:00
|
|
|
treePanel->Layout();
|
|
|
|
treeSizer->Fit( treePanel );
|
2018-07-25 23:16:34 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
m_adapter->FinishTreeInitialization();
|
2021-04-01 09:49:05 +00:00
|
|
|
|
2022-06-04 21:15:12 +00:00
|
|
|
if( m_showPower )
|
|
|
|
m_tree->SetSearchString( g_powerSearchString );
|
|
|
|
else
|
|
|
|
m_tree->SetSearchString( g_symbolSearchString );
|
2022-04-16 22:55:19 +00:00
|
|
|
|
2018-07-25 23:16:34 +00:00
|
|
|
m_hsplitter->SetSashGravity( 0.8 );
|
|
|
|
m_hsplitter->SetMinimumPaneSize( 20 );
|
2023-09-28 17:18:19 +00:00
|
|
|
m_hsplitter->SplitVertically( treePanel, constructRightPanel( m_hsplitter ) );
|
2018-01-03 02:52:35 +00:00
|
|
|
|
2018-07-25 23:16:34 +00:00
|
|
|
m_dbl_click_timer = new wxTimer( this );
|
|
|
|
|
2017-03-08 17:00:07 +00:00
|
|
|
SetSizer( sizer );
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
Layout();
|
2018-01-06 02:33:41 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
Bind( wxEVT_TIMER, &PANEL_SYMBOL_CHOOSER::onCloseTimer, this, m_dbl_click_timer->GetId() );
|
|
|
|
Bind( EVT_LIBITEM_SELECTED, &PANEL_SYMBOL_CHOOSER::onSymbolSelected, this );
|
|
|
|
Bind( EVT_LIBITEM_CHOSEN, &PANEL_SYMBOL_CHOOSER::onSymbolChosen, this );
|
2019-01-25 19:18:22 +00:00
|
|
|
|
|
|
|
if( m_fp_sel_ctrl )
|
2020-12-08 17:44:44 +00:00
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
m_fp_sel_ctrl->Bind( EVT_FOOTPRINT_SELECTED, &PANEL_SYMBOL_CHOOSER::onFootprintSelected,
|
2020-12-08 18:30:14 +00:00
|
|
|
this );
|
2020-12-08 17:44:44 +00:00
|
|
|
}
|
2019-08-06 06:18:51 +00:00
|
|
|
|
2019-08-07 16:06:27 +00:00
|
|
|
if( m_details )
|
2020-12-08 17:44:44 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
m_details->Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( PANEL_SYMBOL_CHOOSER::OnCharHook ),
|
2021-07-16 20:13:26 +00:00
|
|
|
nullptr, this );
|
2020-12-08 17:44:44 +00:00
|
|
|
}
|
2017-03-08 17:00:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
PANEL_SYMBOL_CHOOSER::~PANEL_SYMBOL_CHOOSER()
|
2017-03-23 15:52:05 +00:00
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
Unbind( wxEVT_TIMER, &PANEL_SYMBOL_CHOOSER::onCloseTimer, this );
|
|
|
|
Unbind( EVT_LIBITEM_SELECTED, &PANEL_SYMBOL_CHOOSER::onSymbolSelected, this );
|
|
|
|
Unbind( EVT_LIBITEM_CHOSEN, &PANEL_SYMBOL_CHOOSER::onSymbolChosen, this );
|
2018-06-08 23:50:06 +00:00
|
|
|
|
2021-07-11 23:17:24 +00:00
|
|
|
// Stop the timer during destruction early to avoid potential race conditions (that do happen)
|
|
|
|
m_dbl_click_timer->Stop();
|
|
|
|
delete m_dbl_click_timer;
|
|
|
|
|
2022-06-04 21:15:12 +00:00
|
|
|
if( m_showPower )
|
|
|
|
g_powerSearchString = m_tree->GetSearchString();
|
|
|
|
else
|
|
|
|
g_symbolSearchString = m_tree->GetSearchString();
|
2022-04-16 22:55:19 +00:00
|
|
|
|
2018-06-08 23:50:06 +00:00
|
|
|
if( m_fp_sel_ctrl )
|
2020-12-08 18:30:14 +00:00
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
m_fp_sel_ctrl->Unbind( EVT_FOOTPRINT_SELECTED, &PANEL_SYMBOL_CHOOSER::onFootprintSelected,
|
2020-12-08 18:30:14 +00:00
|
|
|
this );
|
|
|
|
}
|
2018-06-08 23:50:06 +00:00
|
|
|
|
2019-08-07 16:06:27 +00:00
|
|
|
if( m_details )
|
2020-12-08 18:30:14 +00:00
|
|
|
{
|
2019-08-07 16:06:27 +00:00
|
|
|
m_details->Disconnect( wxEVT_CHAR_HOOK,
|
2023-09-28 13:09:45 +00:00
|
|
|
wxKeyEventHandler( PANEL_SYMBOL_CHOOSER::OnCharHook ), nullptr,
|
2021-07-16 20:13:26 +00:00
|
|
|
this );
|
2020-12-08 18:30:14 +00:00
|
|
|
}
|
2019-08-06 06:18:51 +00:00
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
|
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
cfg->m_SymChooserPanel.width = GetParent()->GetSize().x;
|
|
|
|
cfg->m_SymChooserPanel.height = GetParent()->GetSize().y;
|
2019-08-26 10:57:07 +00:00
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
cfg->m_SymChooserPanel.sash_pos_h = m_hsplitter->GetSashPosition();
|
2018-07-25 23:16:34 +00:00
|
|
|
|
2020-12-20 12:34:48 +00:00
|
|
|
if( m_vsplitter )
|
|
|
|
cfg->m_SymChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
|
2023-05-01 20:26:29 +00:00
|
|
|
|
|
|
|
cfg->m_SymChooserPanel.sort_mode = m_tree->GetSortMode();
|
2020-12-20 12:34:48 +00:00
|
|
|
}
|
2017-03-23 15:52:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
wxPanel* PANEL_SYMBOL_CHOOSER::constructRightPanel( wxWindow* aParent )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
EDA_DRAW_PANEL_GAL::GAL_TYPE backend;
|
2017-03-08 17:00:07 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
if( m_frame->GetCanvas() )
|
|
|
|
{
|
|
|
|
backend = m_frame->GetCanvas()->GetBackend();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
|
|
|
|
backend = (EDA_DRAW_PANEL_GAL::GAL_TYPE) cfg->m_Graphics.canvas_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxPanel* panel = new wxPanel( aParent );
|
|
|
|
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
|
|
|
|
|
|
|
|
m_symbol_preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), true, backend );
|
2018-07-27 20:47:51 +00:00
|
|
|
m_symbol_preview->SetLayoutDirection( wxLayout_LeftToRight );
|
2017-03-08 17:00:07 +00:00
|
|
|
|
2018-01-03 02:52:35 +00:00
|
|
|
if( m_show_footprints )
|
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
FOOTPRINT_LIST* fp_list = FOOTPRINT_LIST::GetInstance( m_frame->Kiway() );
|
2018-05-30 07:51:47 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_symbol_preview, 11, wxEXPAND | wxBOTTOM, 5 );
|
2017-03-08 17:00:07 +00:00
|
|
|
|
2018-10-12 21:42:59 +00:00
|
|
|
if ( fp_list )
|
|
|
|
{
|
|
|
|
if( m_allow_field_edits )
|
2023-09-28 13:09:45 +00:00
|
|
|
m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( m_frame, panel, fp_list, true );
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
m_fp_preview = new FOOTPRINT_PREVIEW_WIDGET( panel, m_frame->Kiway() );
|
|
|
|
m_fp_preview->SetUserUnits( m_frame->GetUserUnits() );
|
2018-10-12 21:42:59 +00:00
|
|
|
}
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2018-01-03 02:52:35 +00:00
|
|
|
if( m_fp_sel_ctrl )
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxTOP | wxBOTTOM, 4 );
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2018-10-12 21:42:59 +00:00
|
|
|
if( m_fp_preview )
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_fp_preview, 10, wxEXPAND, 5 );
|
2018-01-03 02:52:35 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
sizer->Add( m_symbol_preview, 1, wxEXPAND, 5 );
|
2018-01-03 02:52:35 +00:00
|
|
|
}
|
2017-03-08 17:00:07 +00:00
|
|
|
|
|
|
|
panel->SetSizer( sizer );
|
|
|
|
panel->Layout();
|
|
|
|
sizer->Fit( panel );
|
|
|
|
|
|
|
|
return panel;
|
2014-02-24 10:52:08 +00:00
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2017-03-08 17:00:07 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::FinishSetup()
|
2017-01-22 22:29:16 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
|
|
|
|
{
|
|
|
|
auto horizPixelsFromDU =
|
|
|
|
[&]( int x ) -> int
|
|
|
|
{
|
|
|
|
wxSize sz( x, 0 );
|
|
|
|
return GetParent()->ConvertDialogToPixels( sz ).x;
|
|
|
|
};
|
|
|
|
|
|
|
|
EESCHEMA_SETTINGS::PANEL_SYM_CHOOSER& panelCfg = cfg->m_SymChooserPanel;
|
|
|
|
|
|
|
|
int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 );
|
|
|
|
int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 );
|
|
|
|
|
|
|
|
GetParent()->SetSize( wxSize( w, h ) );
|
|
|
|
GetParent()->Layout();
|
|
|
|
|
|
|
|
// We specify the width of the right window (m_symbol_view_panel), because specify
|
|
|
|
// the width of the left window does not work as expected when SetSashGravity() is called
|
|
|
|
|
|
|
|
if( panelCfg.sash_pos_h < 0 )
|
|
|
|
panelCfg.sash_pos_h = horizPixelsFromDU( 220 );
|
|
|
|
|
|
|
|
if( panelCfg.sash_pos_v < 0 )
|
|
|
|
panelCfg.sash_pos_v = horizPixelsFromDU( 230 );
|
|
|
|
|
|
|
|
m_hsplitter->SetSashPosition( panelCfg.sash_pos_h );
|
|
|
|
|
|
|
|
if( m_vsplitter )
|
|
|
|
m_vsplitter->SetSashPosition( panelCfg.sash_pos_v );
|
|
|
|
|
|
|
|
m_adapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) panelCfg.sort_mode );
|
|
|
|
}
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
if( m_fp_preview && m_fp_preview->IsInitialized() )
|
2017-03-08 17:00:07 +00:00
|
|
|
{
|
|
|
|
// This hides the GAL panel and shows the status label
|
2018-07-27 20:47:51 +00:00
|
|
|
m_fp_preview->SetStatusText( wxEmptyString );
|
2017-03-08 17:00:07 +00:00
|
|
|
}
|
2018-06-08 23:50:06 +00:00
|
|
|
|
|
|
|
if( m_fp_sel_ctrl )
|
2023-09-28 13:09:45 +00:00
|
|
|
m_fp_sel_ctrl->Load( m_frame->Kiway(), m_frame->Prj() );
|
2018-04-16 22:18:02 +00:00
|
|
|
}
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2018-04-16 22:18:02 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::OnCharHook( wxKeyEvent& e )
|
2019-08-06 06:18:51 +00:00
|
|
|
{
|
2019-08-07 16:06:27 +00:00
|
|
|
if( m_details && e.GetKeyCode() == 'C' && e.ControlDown() &&
|
|
|
|
!e.AltDown() && !e.ShiftDown() && !e.MetaDown() )
|
2019-08-06 06:18:51 +00:00
|
|
|
{
|
|
|
|
wxString txt = m_details->SelectionToText();
|
2021-01-29 19:13:12 +00:00
|
|
|
wxLogNull doNotLog; // disable logging of failed clipboard actions
|
2019-08-06 06:18:51 +00:00
|
|
|
|
|
|
|
if( wxTheClipboard->Open() )
|
|
|
|
{
|
|
|
|
wxTheClipboard->SetData( new wxTextDataObject( txt ) );
|
2021-05-01 22:00:08 +00:00
|
|
|
wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
|
2019-08-06 06:18:51 +00:00
|
|
|
wxTheClipboard->Close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
e.Skip();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::SetPreselect( const LIB_ID& aPreselect )
|
2018-04-16 22:18:02 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
m_adapter->SetPreselectNode( aPreselect, 0 );
|
2017-01-22 22:29:16 +00:00
|
|
|
}
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
LIB_ID PANEL_SYMBOL_CHOOSER::GetSelectedLibId( int* aUnit ) const
|
2014-02-24 10:52:08 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
return m_tree->GetSelectedLibId( aUnit );
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::onCloseTimer( wxTimerEvent& aEvent )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
// Hack because of eaten MouseUp event. See PANEL_SYMBOL_CHOOSER::onSymbolChosen
|
2023-09-28 13:09:45 +00:00
|
|
|
// for the beginning of this spaghetti noodle.
|
2017-02-24 01:29:03 +00:00
|
|
|
|
2023-09-28 13:09:45 +00:00
|
|
|
wxMouseState state = wxGetMouseState();
|
2017-02-24 01:29:03 +00:00
|
|
|
|
|
|
|
if( state.LeftIsDown() )
|
|
|
|
{
|
|
|
|
// Mouse hasn't been raised yet, so fire the timer again. Otherwise the
|
|
|
|
// purpose of this timer is defeated.
|
2023-09-28 17:18:19 +00:00
|
|
|
m_dbl_click_timer->StartOnce( PANEL_SYMBOL_CHOOSER::DBLCLICK_DELAY );
|
2017-02-24 01:29:03 +00:00
|
|
|
}
|
2017-02-24 01:59:15 +00:00
|
|
|
else
|
2017-02-24 01:29:03 +00:00
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
m_closeHandler();
|
2017-02-24 01:29:03 +00:00
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 01:59:15 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::showFootprintFor( LIB_ID const& aLibId )
|
2017-02-19 02:39:55 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
if( !m_fp_preview || !m_fp_preview->IsInitialized() )
|
2017-02-19 02:39:55 +00:00
|
|
|
return;
|
|
|
|
|
2021-06-10 18:51:46 +00:00
|
|
|
LIB_SYMBOL* symbol = nullptr;
|
2017-09-15 14:17:44 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
symbol = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->LoadSymbol( aLibId );
|
2017-09-15 14:17:44 +00:00
|
|
|
}
|
|
|
|
catch( const IO_ERROR& ioe )
|
|
|
|
{
|
2021-06-26 19:21:30 +00:00
|
|
|
wxLogError( _( "Error loading symbol %s from library '%s'." ) + wxS( "\n%s" ),
|
|
|
|
aLibId.GetLibItemName().wx_str(),
|
|
|
|
aLibId.GetLibNickname().wx_str(),
|
|
|
|
ioe.What() );
|
2017-09-15 14:17:44 +00:00
|
|
|
}
|
|
|
|
|
2019-11-06 19:15:42 +00:00
|
|
|
if( !symbol )
|
2017-09-15 14:17:44 +00:00
|
|
|
return;
|
|
|
|
|
2021-02-28 13:28:23 +00:00
|
|
|
LIB_FIELD* fp_field = symbol->GetFieldById( FOOTPRINT_FIELD );
|
2017-03-23 00:59:25 +00:00
|
|
|
wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
showFootprint( fp_name );
|
2017-03-23 00:59:25 +00:00
|
|
|
}
|
2017-02-19 02:39:55 +00:00
|
|
|
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::showFootprint( wxString const& aFootprint )
|
2017-03-23 00:59:25 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
if( !m_fp_preview || !m_fp_preview->IsInitialized() )
|
2018-01-03 02:52:35 +00:00
|
|
|
return;
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
if( aFootprint == wxEmptyString )
|
2017-02-19 02:39:55 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
m_fp_preview->SetStatusText( _( "No footprint specified" ) );
|
2017-03-23 00:59:25 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-15 14:17:44 +00:00
|
|
|
LIB_ID lib_id;
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
if( lib_id.Parse( aFootprint ) == -1 && lib_id.IsValid() )
|
2017-09-15 14:17:44 +00:00
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
m_fp_preview->ClearStatus();
|
|
|
|
m_fp_preview->DisplayFootprint( lib_id );
|
2017-09-15 14:17:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-07-27 20:47:51 +00:00
|
|
|
m_fp_preview->SetStatusText( _( "Invalid footprint specified" ) );
|
2017-09-15 14:17:44 +00:00
|
|
|
}
|
2017-03-23 00:59:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::populateFootprintSelector( LIB_ID const& aLibId )
|
2017-03-23 00:59:25 +00:00
|
|
|
{
|
|
|
|
if( !m_fp_sel_ctrl )
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_fp_sel_ctrl->ClearFilters();
|
|
|
|
|
2021-06-10 18:51:46 +00:00
|
|
|
LIB_SYMBOL* symbol = nullptr;
|
2017-09-15 14:17:44 +00:00
|
|
|
|
2017-10-06 18:07:43 +00:00
|
|
|
if( aLibId.IsValid() )
|
2017-09-15 14:17:44 +00:00
|
|
|
{
|
2017-10-06 18:07:43 +00:00
|
|
|
try
|
|
|
|
{
|
2023-09-28 13:09:45 +00:00
|
|
|
symbol = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->LoadSymbol( aLibId );
|
2017-10-06 18:07:43 +00:00
|
|
|
}
|
|
|
|
catch( const IO_ERROR& ioe )
|
|
|
|
{
|
2021-06-26 19:21:30 +00:00
|
|
|
wxLogError( _( "Error loading symbol %s from library '%s'." ) + wxS( "\n%s" ),
|
|
|
|
aLibId.GetLibItemName().wx_str(),
|
|
|
|
aLibId.GetLibNickname().wx_str(),
|
|
|
|
ioe.What() );
|
2017-10-06 18:07:43 +00:00
|
|
|
}
|
2017-09-15 14:17:44 +00:00
|
|
|
}
|
|
|
|
|
2019-11-06 19:15:42 +00:00
|
|
|
if( symbol != nullptr )
|
2017-03-23 00:59:25 +00:00
|
|
|
{
|
|
|
|
LIB_PINS temp_pins;
|
2021-02-28 13:28:23 +00:00
|
|
|
LIB_FIELD* fp_field = symbol->GetFieldById( FOOTPRINT_FIELD );
|
2017-03-23 00:59:25 +00:00
|
|
|
wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
|
|
|
|
|
2023-08-31 10:36:07 +00:00
|
|
|
// All units, but only a single De Morgan variant.
|
|
|
|
if( symbol->HasConversion() )
|
|
|
|
symbol->GetPins( temp_pins, 0, 1 );
|
|
|
|
else
|
|
|
|
symbol->GetPins( temp_pins );
|
2017-03-23 00:59:25 +00:00
|
|
|
|
|
|
|
m_fp_sel_ctrl->FilterByPinCount( temp_pins.size() );
|
2020-12-14 13:47:32 +00:00
|
|
|
m_fp_sel_ctrl->FilterByFootprintFilters( symbol->GetFPFilters(), true );
|
2017-03-23 00:59:25 +00:00
|
|
|
m_fp_sel_ctrl->SetDefaultFootprint( fp_name );
|
|
|
|
m_fp_sel_ctrl->UpdateList();
|
|
|
|
m_fp_sel_ctrl->Enable();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_fp_sel_ctrl->UpdateList();
|
|
|
|
m_fp_sel_ctrl->Disable();
|
2017-02-19 02:39:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::onFootprintSelected( wxCommandEvent& aEvent )
|
2017-03-23 00:59:25 +00:00
|
|
|
{
|
|
|
|
m_fp_override = aEvent.GetString();
|
|
|
|
|
2021-10-01 10:52:34 +00:00
|
|
|
alg::delete_if( m_field_edits, []( std::pair<int, wxString> const& i )
|
|
|
|
{
|
|
|
|
return i.first == FOOTPRINT_FIELD;
|
|
|
|
} );
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2020-11-12 21:31:41 +00:00
|
|
|
m_field_edits.emplace_back( std::make_pair( FOOTPRINT_FIELD, m_fp_override ) );
|
2017-03-23 00:59:25 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
showFootprint( m_fp_override );
|
2017-03-23 00:59:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::onSymbolSelected( wxCommandEvent& aEvent )
|
2017-06-25 21:13:39 +00:00
|
|
|
{
|
2022-06-05 17:58:27 +00:00
|
|
|
LIB_TREE_NODE* node = m_tree->GetCurrentTreeNode();
|
2017-09-15 14:17:44 +00:00
|
|
|
|
2022-06-05 17:58:27 +00:00
|
|
|
if( node && node->m_LibId.IsValid() )
|
2017-06-25 21:13:39 +00:00
|
|
|
{
|
2022-06-05 17:58:27 +00:00
|
|
|
m_symbol_preview->DisplaySymbol( node->m_LibId, node->m_Unit );
|
|
|
|
|
|
|
|
if( !node->m_Footprint.IsEmpty() )
|
2023-09-28 17:18:19 +00:00
|
|
|
showFootprint( node->m_Footprint );
|
2022-06-05 17:58:27 +00:00
|
|
|
else
|
2023-09-28 17:18:19 +00:00
|
|
|
showFootprintFor( node->m_LibId );
|
2018-08-28 13:26:39 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
populateFootprintSelector( node->m_LibId );
|
2017-06-25 21:13:39 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-08-28 13:26:39 +00:00
|
|
|
m_symbol_preview->SetStatusText( _( "No symbol selected" ) );
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
if( m_fp_preview && m_fp_preview->IsInitialized() )
|
|
|
|
m_fp_preview->SetStatusText( wxEmptyString );
|
2017-06-25 21:13:39 +00:00
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
populateFootprintSelector( LIB_ID() );
|
2017-06-25 21:13:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-28 17:18:19 +00:00
|
|
|
void PANEL_SYMBOL_CHOOSER::onSymbolChosen( wxCommandEvent& aEvent )
|
2017-06-25 21:13:39 +00:00
|
|
|
{
|
2017-09-15 14:17:44 +00:00
|
|
|
if( m_tree->GetSelectedLibId().IsValid() )
|
2017-06-25 21:13:39 +00:00
|
|
|
{
|
2023-09-28 17:18:19 +00:00
|
|
|
// Got a selection. We can't just end the modal dialog here, because wx leaks some events
|
|
|
|
// back to the parent window (in particular, the MouseUp following a double click).
|
2017-06-25 21:13:39 +00:00
|
|
|
//
|
2023-09-28 17:18:19 +00:00
|
|
|
// NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. This isn't really
|
|
|
|
// feasible to bypass without a fully custom wxDataViewCtrl implementation, and even then
|
|
|
|
// might not be fully possible (docs are vague). To get around this, we use a one-shot
|
2017-06-25 21:13:39 +00:00
|
|
|
// timer to schedule the dialog close.
|
|
|
|
//
|
2023-09-28 17:18:19 +00:00
|
|
|
// See PANEL_SYMBOL_CHOOSER::onCloseTimer for the other end of this spaghetti noodle.
|
|
|
|
m_dbl_click_timer->StartOnce( PANEL_SYMBOL_CHOOSER::DBLCLICK_DELAY );
|
2017-06-25 21:13:39 +00:00
|
|
|
}
|
|
|
|
}
|