You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

705 lines
24 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "panel_symbol_chooser.h"
  25. #include <pgm_base.h>
  26. #include <kiface_base.h>
  27. #include <sch_base_frame.h>
  28. #include <project_sch.h>
  29. #include <widgets/lib_tree.h>
  30. #include <widgets/symbol_preview_widget.h>
  31. #include <widgets/footprint_preview_widget.h>
  32. #include <widgets/footprint_select_widget.h>
  33. #include <settings/settings_manager.h>
  34. #include <project/project_file.h>
  35. #include <eeschema_settings.h>
  36. #include <symbol_editor_settings.h>
  37. #include <symbol_library.h> // For SYMBOL_LIBRARY_FILTER
  38. #include <symbol_lib_table.h>
  39. #include <wx/button.h>
  40. #include <wx/clipbrd.h>
  41. #include <wx/panel.h>
  42. #include <wx/sizer.h>
  43. #include <wx/splitter.h>
  44. #include <wx/timer.h>
  45. #include <wx/wxhtml.h>
  46. #include <wx/log.h>
  47. wxString PANEL_SYMBOL_CHOOSER::g_symbolSearchString;
  48. wxString PANEL_SYMBOL_CHOOSER::g_powerSearchString;
  49. PANEL_SYMBOL_CHOOSER::PANEL_SYMBOL_CHOOSER( SCH_BASE_FRAME* aFrame, wxWindow* aParent,
  50. const SYMBOL_LIBRARY_FILTER* aFilter,
  51. std::vector<PICKED_SYMBOL>& aHistoryList,
  52. std::vector<PICKED_SYMBOL>& aAlreadyPlaced,
  53. bool aAllowFieldEdits, bool aShowFootprints,
  54. std::function<void()> aAcceptHandler,
  55. std::function<void()> aEscapeHandler ) :
  56. wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
  57. m_symbol_preview( nullptr ),
  58. m_hsplitter( nullptr ),
  59. m_vsplitter( nullptr ),
  60. m_fp_sel_ctrl( nullptr ),
  61. m_fp_preview( nullptr ),
  62. m_tree( nullptr ),
  63. m_details( nullptr ),
  64. m_frame( aFrame ),
  65. m_acceptHandler( std::move( aAcceptHandler ) ),
  66. m_escapeHandler( std::move( aEscapeHandler ) ),
  67. m_showPower( false ),
  68. m_allow_field_edits( aAllowFieldEdits ),
  69. m_show_footprints( aShowFootprints )
  70. {
  71. SYMBOL_LIB_TABLE* libs = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() );
  72. COMMON_SETTINGS::SESSION& session = Pgm().GetCommonSettings()->m_Session;
  73. PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
  74. // Make sure settings are loaded before we start running multi-threaded symbol loaders
  75. Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
  76. Pgm().GetSettingsManager().GetAppSettings<SYMBOL_EDITOR_SETTINGS>( "symbol_editor" );
  77. m_adapter = SYMBOL_TREE_MODEL_ADAPTER::Create( m_frame, libs );
  78. SYMBOL_TREE_MODEL_ADAPTER* adapter = static_cast<SYMBOL_TREE_MODEL_ADAPTER*>( m_adapter.get() );
  79. bool loaded = false;
  80. if( aFilter )
  81. {
  82. const wxArrayString& liblist = aFilter->GetAllowedLibList();
  83. for( const wxString& nickname : liblist )
  84. {
  85. if( libs->HasLibrary( nickname, true ) )
  86. {
  87. loaded = true;
  88. bool pinned = alg::contains( session.pinned_symbol_libs, nickname )
  89. || alg::contains( project.m_PinnedSymbolLibs, nickname );
  90. if( libs->FindRow( nickname )->GetIsVisible() )
  91. adapter->AddLibrary( nickname, pinned );
  92. }
  93. }
  94. adapter->AssignIntrinsicRanks();
  95. if( aFilter->GetFilterPowerSymbols() )
  96. {
  97. // HACK ALERT: when loading symbols we presume that *any* filter is a power symbol
  98. // filter. So the filter only needs to return true for libraries.
  99. static std::function<bool( LIB_TREE_NODE& )> powerFilter =
  100. []( LIB_TREE_NODE& aNode ) -> bool
  101. {
  102. return true;
  103. };
  104. adapter->SetFilter( &powerFilter );
  105. m_showPower = true;
  106. m_show_footprints = false;
  107. }
  108. }
  109. std::vector<LIB_SYMBOL> history_list_storage;
  110. std::vector<LIB_TREE_ITEM*> history_list;
  111. std::vector<LIB_SYMBOL> already_placed_storage;
  112. std::vector<LIB_TREE_ITEM*> already_placed;
  113. // Lambda to encapsulate the common logic
  114. auto processList =
  115. [&]( const std::vector<PICKED_SYMBOL>& inputList,
  116. std::vector<LIB_SYMBOL>& storageList,
  117. std::vector<LIB_TREE_ITEM*>& resultList )
  118. {
  119. storageList.reserve( inputList.size() );
  120. for( const PICKED_SYMBOL& i : inputList )
  121. {
  122. LIB_SYMBOL* symbol = m_frame->GetLibSymbol( i.LibId );
  123. if( symbol )
  124. {
  125. storageList.emplace_back( *symbol );
  126. for( const std::pair<int, wxString>& fieldDef : i.Fields )
  127. {
  128. SCH_FIELD* field = storageList.back().GetFieldById( fieldDef.first );
  129. if( field )
  130. field->SetText( fieldDef.second );
  131. }
  132. resultList.push_back( &storageList.back() );
  133. }
  134. }
  135. };
  136. // Sort the already placed list since it is potentially from multiple sessions,
  137. // but not the most recent list since we want this listed by most recent usage.
  138. std::sort( aAlreadyPlaced.begin(), aAlreadyPlaced.end(),
  139. []( PICKED_SYMBOL const& a, PICKED_SYMBOL const& b )
  140. {
  141. return a.LibId.GetLibItemName() < b.LibId.GetLibItemName();
  142. } );
  143. processList( aHistoryList, history_list_storage, history_list );
  144. processList( aAlreadyPlaced, already_placed_storage, already_placed );
  145. adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
  146. history_list, false, true );
  147. if( !aHistoryList.empty() )
  148. adapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit );
  149. adapter->DoAddLibrary( wxT( "-- " ) + _( "Already Placed" ) + wxT( " --" ), wxEmptyString,
  150. already_placed, false, true );
  151. const std::vector< wxString > libNicknames = libs->GetLogicalLibs();
  152. if( !loaded )
  153. {
  154. if( !adapter->AddLibraries( libNicknames, m_frame ) )
  155. {
  156. // loading cancelled by user
  157. m_acceptHandler();
  158. }
  159. }
  160. // -------------------------------------------------------------------------------------
  161. // Construct the actual panel
  162. //
  163. wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
  164. // Use a slightly different layout, with a details pane spanning the entire window,
  165. // if we're not showing footprints.
  166. if( m_show_footprints )
  167. {
  168. m_hsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  169. wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
  170. //Avoid the splitter window being assigned as the Parent to additional windows
  171. m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
  172. sizer->Add( m_hsplitter, 1, wxEXPAND, 5 );
  173. }
  174. else
  175. {
  176. m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  177. wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
  178. m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
  179. wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
  180. // Avoid the splitter window being assigned as the parent to additional windows.
  181. m_vsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
  182. m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
  183. wxPanel* detailsPanel = new wxPanel( m_vsplitter );
  184. wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL );
  185. detailsPanel->SetSizer( detailsSizer );
  186. m_details = new HTML_WINDOW( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize );
  187. detailsSizer->Add( m_details, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  188. detailsPanel->Layout();
  189. detailsSizer->Fit( detailsPanel );
  190. m_vsplitter->SetSashGravity( 0.5 );
  191. m_vsplitter->SetMinimumPaneSize( 20 );
  192. m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
  193. sizer->Add( m_vsplitter, 1, wxEXPAND | wxBOTTOM, 5 );
  194. }
  195. wxPanel* treePanel = new wxPanel( m_hsplitter );
  196. wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
  197. treePanel->SetSizer( treeSizer );
  198. m_tree = new LIB_TREE( treePanel, m_showPower ? wxT( "power" ) : wxT( "symbols" ),
  199. libs, m_adapter, LIB_TREE::FLAGS::ALL_WIDGETS, m_details );
  200. treeSizer->Add( m_tree, 1, wxALL | wxEXPAND, 5 );
  201. treePanel->Layout();
  202. treeSizer->Fit( treePanel );
  203. m_adapter->FinishTreeInitialization();
  204. if( m_showPower )
  205. m_tree->SetSearchString( g_powerSearchString );
  206. else
  207. m_tree->SetSearchString( g_symbolSearchString );
  208. m_hsplitter->SetSashGravity( 0.8 );
  209. m_hsplitter->SetMinimumPaneSize( 20 );
  210. m_hsplitter->SplitVertically( treePanel, constructRightPanel( m_hsplitter ) );
  211. m_dbl_click_timer = new wxTimer( this );
  212. m_open_libs_timer = new wxTimer( this );
  213. SetSizer( sizer );
  214. Layout();
  215. Bind( wxEVT_TIMER, &PANEL_SYMBOL_CHOOSER::onCloseTimer, this, m_dbl_click_timer->GetId() );
  216. Bind( wxEVT_TIMER, &PANEL_SYMBOL_CHOOSER::onOpenLibsTimer, this, m_open_libs_timer->GetId() );
  217. Bind( EVT_LIBITEM_SELECTED, &PANEL_SYMBOL_CHOOSER::onSymbolSelected, this );
  218. Bind( EVT_LIBITEM_CHOSEN, &PANEL_SYMBOL_CHOOSER::onSymbolChosen, this );
  219. Bind( wxEVT_CHAR_HOOK, &PANEL_SYMBOL_CHOOSER::OnChar, this );
  220. aFrame->Bind( wxEVT_MENU_OPEN, &PANEL_SYMBOL_CHOOSER::onMenuOpen, this );
  221. aFrame->Bind( wxEVT_MENU_CLOSE, &PANEL_SYMBOL_CHOOSER::onMenuClose, this );
  222. if( m_fp_sel_ctrl )
  223. {
  224. m_fp_sel_ctrl->Bind( EVT_FOOTPRINT_SELECTED, &PANEL_SYMBOL_CHOOSER::onFootprintSelected,
  225. this );
  226. }
  227. if( m_details )
  228. {
  229. m_details->Connect( wxEVT_CHAR_HOOK,
  230. wxKeyEventHandler( PANEL_SYMBOL_CHOOSER::OnDetailsCharHook ),
  231. nullptr, this );
  232. }
  233. // Open the user's previously opened libraries on timer expiration.
  234. // This is done on a timer because we need a gross hack to keep GTK from garbling the
  235. // display. Must be longer than the search debounce timer.
  236. m_open_libs_timer->StartOnce( 300 );
  237. }
  238. PANEL_SYMBOL_CHOOSER::~PANEL_SYMBOL_CHOOSER()
  239. {
  240. m_frame->Unbind( wxEVT_MENU_OPEN, &PANEL_SYMBOL_CHOOSER::onMenuOpen, this );
  241. m_frame->Unbind( wxEVT_MENU_CLOSE, &PANEL_SYMBOL_CHOOSER::onMenuClose, this );
  242. Unbind( wxEVT_TIMER, &PANEL_SYMBOL_CHOOSER::onCloseTimer, this );
  243. Unbind( EVT_LIBITEM_SELECTED, &PANEL_SYMBOL_CHOOSER::onSymbolSelected, this );
  244. Unbind( EVT_LIBITEM_CHOSEN, &PANEL_SYMBOL_CHOOSER::onSymbolChosen, this );
  245. Unbind( wxEVT_CHAR_HOOK, &PANEL_SYMBOL_CHOOSER::OnChar, this );
  246. // Stop the timer during destruction early to avoid potential race conditions (that do happen)
  247. m_dbl_click_timer->Stop();
  248. m_open_libs_timer->Stop();
  249. delete m_dbl_click_timer;
  250. delete m_open_libs_timer;
  251. if( m_showPower )
  252. g_powerSearchString = m_tree->GetSearchString();
  253. else
  254. g_symbolSearchString = m_tree->GetSearchString();
  255. if( m_fp_sel_ctrl )
  256. {
  257. m_fp_sel_ctrl->Unbind( EVT_FOOTPRINT_SELECTED, &PANEL_SYMBOL_CHOOSER::onFootprintSelected,
  258. this );
  259. }
  260. if( m_details )
  261. {
  262. m_details->Disconnect( wxEVT_CHAR_HOOK,
  263. wxKeyEventHandler( PANEL_SYMBOL_CHOOSER::OnDetailsCharHook ),
  264. nullptr, this );
  265. }
  266. if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
  267. {
  268. // Save any changes to column widths, etc.
  269. m_adapter->SaveSettings();
  270. cfg->m_SymChooserPanel.width = GetParent()->GetSize().x;
  271. cfg->m_SymChooserPanel.height = GetParent()->GetSize().y;
  272. cfg->m_SymChooserPanel.sash_pos_h = m_hsplitter->GetSashPosition();
  273. if( m_vsplitter )
  274. cfg->m_SymChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
  275. cfg->m_SymChooserPanel.sort_mode = m_tree->GetSortMode();
  276. }
  277. }
  278. void PANEL_SYMBOL_CHOOSER::onMenuOpen( wxMenuEvent& aEvent )
  279. {
  280. m_tree->BlockPreview( true );
  281. aEvent.Skip();
  282. }
  283. void PANEL_SYMBOL_CHOOSER::onMenuClose( wxMenuEvent& aEvent )
  284. {
  285. m_tree->BlockPreview( false );
  286. aEvent.Skip();
  287. }
  288. void PANEL_SYMBOL_CHOOSER::OnChar( wxKeyEvent& aEvent )
  289. {
  290. if( aEvent.GetKeyCode() == WXK_ESCAPE )
  291. {
  292. wxObject* eventSource = aEvent.GetEventObject();
  293. if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( eventSource ) )
  294. {
  295. // First escape cancels search string value
  296. if( textCtrl->GetValue() == m_tree->GetSearchString()
  297. && !m_tree->GetSearchString().IsEmpty() )
  298. {
  299. m_tree->SetSearchString( wxEmptyString );
  300. return;
  301. }
  302. }
  303. m_escapeHandler();
  304. }
  305. else
  306. {
  307. aEvent.Skip();
  308. }
  309. }
  310. wxPanel* PANEL_SYMBOL_CHOOSER::constructRightPanel( wxWindow* aParent )
  311. {
  312. EDA_DRAW_PANEL_GAL::GAL_TYPE backend;
  313. if( m_frame->GetCanvas() )
  314. {
  315. backend = m_frame->GetCanvas()->GetBackend();
  316. }
  317. else
  318. {
  319. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  320. EESCHEMA_SETTINGS* cfg = mgr.GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
  321. backend = (EDA_DRAW_PANEL_GAL::GAL_TYPE) cfg->m_Graphics.canvas_type;
  322. }
  323. wxPanel* panel = new wxPanel( aParent );
  324. wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
  325. m_symbol_preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), true, backend );
  326. m_symbol_preview->SetLayoutDirection( wxLayout_LeftToRight );
  327. if( m_show_footprints )
  328. {
  329. FOOTPRINT_LIST* fp_list = FOOTPRINT_LIST::GetInstance( m_frame->Kiway() );
  330. sizer->Add( m_symbol_preview, 11, wxEXPAND | wxALL, 5 );
  331. if ( fp_list )
  332. {
  333. if( m_allow_field_edits )
  334. m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( m_frame, panel, fp_list, true );
  335. m_fp_preview = new FOOTPRINT_PREVIEW_WIDGET( panel, m_frame->Kiway() );
  336. m_fp_preview->SetUserUnits( m_frame->GetUserUnits() );
  337. }
  338. if( m_fp_sel_ctrl )
  339. sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  340. if( m_fp_preview )
  341. sizer->Add( m_fp_preview, 10, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
  342. }
  343. else
  344. {
  345. sizer->Add( m_symbol_preview, 1, wxEXPAND | wxALL, 5 );
  346. }
  347. panel->SetSizer( sizer );
  348. panel->Layout();
  349. sizer->Fit( panel );
  350. return panel;
  351. }
  352. void PANEL_SYMBOL_CHOOSER::FinishSetup()
  353. {
  354. if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
  355. {
  356. auto horizPixelsFromDU =
  357. [&]( int x ) -> int
  358. {
  359. wxSize sz( x, 0 );
  360. return GetParent()->ConvertDialogToPixels( sz ).x;
  361. };
  362. EESCHEMA_SETTINGS::PANEL_SYM_CHOOSER& panelCfg = cfg->m_SymChooserPanel;
  363. int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 );
  364. int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 );
  365. GetParent()->SetSize( wxSize( w, h ) );
  366. GetParent()->Layout();
  367. // We specify the width of the right window (m_symbol_view_panel), because specify
  368. // the width of the left window does not work as expected when SetSashGravity() is called
  369. if( panelCfg.sash_pos_h < 0 )
  370. panelCfg.sash_pos_h = horizPixelsFromDU( 220 );
  371. if( panelCfg.sash_pos_v < 0 )
  372. panelCfg.sash_pos_v = horizPixelsFromDU( 230 );
  373. m_hsplitter->SetSashPosition( panelCfg.sash_pos_h );
  374. if( m_vsplitter )
  375. m_vsplitter->SetSashPosition( panelCfg.sash_pos_v );
  376. m_adapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) panelCfg.sort_mode );
  377. }
  378. if( m_fp_preview && m_fp_preview->IsInitialized() )
  379. {
  380. // This hides the GAL panel and shows the status label
  381. m_fp_preview->SetStatusText( wxEmptyString );
  382. }
  383. if( m_fp_sel_ctrl )
  384. m_fp_sel_ctrl->Load( m_frame->Kiway(), m_frame->Prj() );
  385. }
  386. void PANEL_SYMBOL_CHOOSER::OnDetailsCharHook( wxKeyEvent& e )
  387. {
  388. if( m_details && e.GetKeyCode() == 'C' && e.ControlDown() &&
  389. !e.AltDown() && !e.ShiftDown() && !e.MetaDown() )
  390. {
  391. wxString txt = m_details->SelectionToText();
  392. wxLogNull doNotLog; // disable logging of failed clipboard actions
  393. if( wxTheClipboard->Open() )
  394. {
  395. wxTheClipboard->SetData( new wxTextDataObject( txt ) );
  396. wxTheClipboard->Flush(); // Allow data to be available after closing KiCad
  397. wxTheClipboard->Close();
  398. }
  399. }
  400. else
  401. {
  402. e.Skip();
  403. }
  404. }
  405. void PANEL_SYMBOL_CHOOSER::SetPreselect( const LIB_ID& aPreselect )
  406. {
  407. m_adapter->SetPreselectNode( aPreselect, 0 );
  408. }
  409. LIB_ID PANEL_SYMBOL_CHOOSER::GetSelectedLibId( int* aUnit ) const
  410. {
  411. return m_tree->GetSelectedLibId( aUnit );
  412. }
  413. void PANEL_SYMBOL_CHOOSER::onCloseTimer( wxTimerEvent& aEvent )
  414. {
  415. // Hack because of eaten MouseUp event. See PANEL_SYMBOL_CHOOSER::onSymbolChosen
  416. // for the beginning of this spaghetti noodle.
  417. wxMouseState state = wxGetMouseState();
  418. if( state.LeftIsDown() )
  419. {
  420. // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
  421. // purpose of this timer is defeated.
  422. m_dbl_click_timer->StartOnce( PANEL_SYMBOL_CHOOSER::DBLCLICK_DELAY );
  423. }
  424. else
  425. {
  426. m_acceptHandler();
  427. }
  428. }
  429. void PANEL_SYMBOL_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent )
  430. {
  431. if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
  432. m_adapter->OpenLibs( cfg->m_LibTree.open_libs );
  433. }
  434. void PANEL_SYMBOL_CHOOSER::showFootprintFor( LIB_ID const& aLibId )
  435. {
  436. if( !m_fp_preview || !m_fp_preview->IsInitialized() )
  437. return;
  438. LIB_SYMBOL* symbol = nullptr;
  439. try
  440. {
  441. symbol = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->LoadSymbol( aLibId );
  442. }
  443. catch( const IO_ERROR& ioe )
  444. {
  445. wxLogError( _( "Error loading symbol %s from library '%s'." ) + wxS( "\n%s" ),
  446. aLibId.GetLibItemName().wx_str(),
  447. aLibId.GetLibNickname().wx_str(),
  448. ioe.What() );
  449. }
  450. if( !symbol )
  451. return;
  452. SCH_FIELD* fp_field = symbol->GetFieldById( FOOTPRINT_FIELD );
  453. wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
  454. showFootprint( fp_name );
  455. }
  456. void PANEL_SYMBOL_CHOOSER::showFootprint( wxString const& aFootprint )
  457. {
  458. if( !m_fp_preview || !m_fp_preview->IsInitialized() )
  459. return;
  460. if( aFootprint == wxEmptyString )
  461. {
  462. m_fp_preview->SetStatusText( _( "No footprint specified" ) );
  463. }
  464. else
  465. {
  466. LIB_ID lib_id;
  467. if( lib_id.Parse( aFootprint ) == -1 && lib_id.IsValid() )
  468. {
  469. m_fp_preview->ClearStatus();
  470. m_fp_preview->DisplayFootprint( lib_id );
  471. }
  472. else
  473. {
  474. m_fp_preview->SetStatusText( _( "Invalid footprint specified" ) );
  475. }
  476. }
  477. }
  478. void PANEL_SYMBOL_CHOOSER::populateFootprintSelector( LIB_ID const& aLibId )
  479. {
  480. if( !m_fp_sel_ctrl )
  481. return;
  482. m_fp_sel_ctrl->ClearFilters();
  483. LIB_SYMBOL* symbol = nullptr;
  484. if( aLibId.IsValid() )
  485. {
  486. try
  487. {
  488. symbol = PROJECT_SCH::SchSymbolLibTable( &m_frame->Prj() )->LoadSymbol( aLibId );
  489. }
  490. catch( const IO_ERROR& ioe )
  491. {
  492. wxLogError( _( "Error loading symbol %s from library '%s'." ) + wxS( "\n%s" ),
  493. aLibId.GetLibItemName().wx_str(),
  494. aLibId.GetLibNickname().wx_str(),
  495. ioe.What() );
  496. }
  497. }
  498. if( symbol != nullptr )
  499. {
  500. int pinCount = symbol->GetPins( 0 /* all units */, 1 /* single bodyStyle */ ).size();
  501. SCH_FIELD* fp_field = symbol->GetFieldById( FOOTPRINT_FIELD );
  502. wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" );
  503. m_fp_sel_ctrl->FilterByPinCount( pinCount );
  504. m_fp_sel_ctrl->FilterByFootprintFilters( symbol->GetFPFilters(), true );
  505. m_fp_sel_ctrl->SetDefaultFootprint( fp_name );
  506. m_fp_sel_ctrl->UpdateList();
  507. m_fp_sel_ctrl->Enable();
  508. }
  509. else
  510. {
  511. m_fp_sel_ctrl->UpdateList();
  512. m_fp_sel_ctrl->Disable();
  513. }
  514. }
  515. void PANEL_SYMBOL_CHOOSER::onFootprintSelected( wxCommandEvent& aEvent )
  516. {
  517. m_fp_override = aEvent.GetString();
  518. alg::delete_if( m_field_edits, []( std::pair<int, wxString> const& i )
  519. {
  520. return i.first == FOOTPRINT_FIELD;
  521. } );
  522. m_field_edits.emplace_back( std::make_pair( FOOTPRINT_FIELD, m_fp_override ) );
  523. showFootprint( m_fp_override );
  524. }
  525. void PANEL_SYMBOL_CHOOSER::onSymbolSelected( wxCommandEvent& aEvent )
  526. {
  527. LIB_TREE_NODE* node = m_tree->GetCurrentTreeNode();
  528. if( node && node->m_LibId.IsValid() )
  529. {
  530. m_symbol_preview->DisplaySymbol( node->m_LibId, node->m_Unit );
  531. if( !node->m_Footprint.IsEmpty() )
  532. showFootprint( node->m_Footprint );
  533. else
  534. showFootprintFor( node->m_LibId );
  535. populateFootprintSelector( node->m_LibId );
  536. }
  537. else
  538. {
  539. m_symbol_preview->SetStatusText( _( "No symbol selected" ) );
  540. if( m_fp_preview && m_fp_preview->IsInitialized() )
  541. m_fp_preview->SetStatusText( wxEmptyString );
  542. populateFootprintSelector( LIB_ID() );
  543. }
  544. }
  545. void PANEL_SYMBOL_CHOOSER::onSymbolChosen( wxCommandEvent& aEvent )
  546. {
  547. if( m_tree->GetSelectedLibId().IsValid() )
  548. {
  549. // Got a selection. We can't just end the modal dialog here, because wx leaks some events
  550. // back to the parent window (in particular, the MouseUp following a double click).
  551. //
  552. // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. This isn't really
  553. // feasible to bypass without a fully custom wxDataViewCtrl implementation, and even then
  554. // might not be fully possible (docs are vague). To get around this, we use a one-shot
  555. // timer to schedule the dialog close.
  556. //
  557. // See PANEL_SYMBOL_CHOOSER::onCloseTimer for the other end of this spaghetti noodle.
  558. m_dbl_click_timer->StartOnce( PANEL_SYMBOL_CHOOSER::DBLCLICK_DELAY );
  559. }
  560. }