KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_footprint_chooser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2014 Henner Zeller <[email protected]>
5 * Copyright (C) 2023 CERN
6 * Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <wx/button.h>
28#include <wx/panel.h>
29#include <wx/sizer.h>
30#include <wx/splitter.h>
31#include <wx/timer.h>
32#include <wx/wxhtml.h>
33#include <pcb_base_frame.h>
34#include <pcbnew_settings.h>
35#include <pgm_base.h>
36#include <fp_lib_table.h>
38#include <widgets/lib_tree.h>
41#include <footprint_info_impl.h>
42#include <project_pcb.h>
43
44
46 const wxArrayString& aFootprintHistoryList,
47 std::function<bool( LIB_TREE_NODE& )> aFilter,
48 std::function<void()> aCloseHandler ) :
49 wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
50 m_hsplitter( nullptr ),
51 m_vsplitter( nullptr ),
52 m_frame( aFrame ),
53 m_filter( std::move( aFilter ) ),
54 m_closeHandler( std::move( aCloseHandler ) )
55{
56 FP_LIB_TABLE* fpTable = PROJECT_PCB::PcbFootprintLibs( &aFrame->Prj() );
57
58 // Load footprint files:
59 WX_PROGRESS_REPORTER* progressReporter = new WX_PROGRESS_REPORTER( aParent,
60 _( "Loading Footprint Libraries" ), 3 );
61 GFootprintList.ReadFootprintFiles( fpTable, nullptr, progressReporter );
62
63 // Force immediate deletion of the WX_PROGRESS_REPORTER. Do not use Destroy(), or use
64 // Destroy() followed by wxSafeYield() because on Windows, APP_PROGRESS_DIALOG and
65 // WX_PROGRESS_REPORTER have some side effects on the event loop manager. For instance, a
66 // subsequent call to ShowModal() or ShowQuasiModal() for a dialog following the use of a
67 // WX_PROGRESS_REPORTER results in incorrect modal or quasi modal behavior.
68 delete progressReporter;
69
72
73 m_adapter = FP_TREE_MODEL_ADAPTER::Create( aFrame, fpTable );
74 FP_TREE_MODEL_ADAPTER* adapter = static_cast<FP_TREE_MODEL_ADAPTER*>( m_adapter.get() );
75
76 std::vector<LIB_TREE_ITEM*> historyInfos;
77
78 for( const wxString& item : aFootprintHistoryList )
79 {
81
82 // this can be null, for example, if the footprint has been deleted from a library.
83 if( fp_info != nullptr )
84 historyInfos.push_back( fp_info );
85 }
86
87 adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
88 historyInfos, false, true );
89
90 if( historyInfos.size() )
91 adapter->SetPreselectNode( historyInfos[0]->GetLibId(), 0 );
92
93 adapter->SetFilter( &m_filter );
94 adapter->AddLibraries( m_frame );
95
96 // -------------------------------------------------------------------------------------
97 // Construct the actual panel
98 //
99
100 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
101 HTML_WINDOW* details = nullptr;
102
103 m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
104 wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
105
106 m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize,
107 wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
108
109 //Avoid the splitter window being assigned as the Parent to additional windows
110 m_vsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
111 m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
112
113 auto detailsPanel = new wxPanel( m_vsplitter );
114 auto detailsSizer = new wxBoxSizer( wxVERTICAL );
115 detailsPanel->SetSizer( detailsSizer );
116
117 details = new HTML_WINDOW( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize );
118 detailsSizer->Add( details, 1, wxEXPAND, 5 );
119 detailsPanel->Layout();
120 detailsSizer->Fit( detailsPanel );
121
122 m_vsplitter->SetSashGravity( 0.5 );
123 m_vsplitter->SetMinimumPaneSize( 20 );
124 m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel );
125
126 sizer->Add( m_vsplitter, 1, wxEXPAND, 5 );
127
128 m_tree = new LIB_TREE( m_hsplitter, wxT( "footprints" ), fpTable, m_adapter,
130
131 m_hsplitter->SetSashGravity( 0.8 );
132 m_hsplitter->SetMinimumPaneSize( 20 );
133
134 wxPanel* rightPanel = new wxPanel( m_hsplitter );
135 wxBoxSizer* rightPanelSizer = new wxBoxSizer( wxVERTICAL );
136
139 rightPanelSizer->Add( m_preview_ctrl, 1, wxEXPAND, 5 );
140
141 rightPanel->SetSizer( rightPanelSizer );
142 rightPanel->Layout();
143 rightPanelSizer->Fit( rightPanel );
144
145 m_hsplitter->SplitVertically( m_tree, rightPanel );
146
147 m_dbl_click_timer = new wxTimer( this );
148
149 SetSizer( sizer );
150
151 m_adapter->FinishTreeInitialization();
152
153 Bind( wxEVT_TIMER, &PANEL_FOOTPRINT_CHOOSER::onCloseTimer, this, m_dbl_click_timer->GetId() );
154 Bind( EVT_LIBITEM_SELECTED, &PANEL_FOOTPRINT_CHOOSER::onFootprintSelected, this );
155 Bind( EVT_LIBITEM_CHOSEN, &PANEL_FOOTPRINT_CHOOSER::onFootprintChosen, this );
156
157 Layout();
158}
159
160
162{
163 Unbind( wxEVT_TIMER, &PANEL_FOOTPRINT_CHOOSER::onCloseTimer, this );
164 Unbind( EVT_LIBITEM_SELECTED, &PANEL_FOOTPRINT_CHOOSER::onFootprintSelected, this );
165 Unbind( EVT_LIBITEM_CHOSEN, &PANEL_FOOTPRINT_CHOOSER::onFootprintChosen, this );
166
167 // I am not sure the following two lines are necessary, but they will not hurt anyone
168 m_dbl_click_timer->Stop();
169 delete m_dbl_click_timer;
170
171 PCBNEW_SETTINGS* cfg = nullptr;
172 try
173 {
174 cfg = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>();
175 }
176 catch( const std::runtime_error& e )
177 {
178 wxFAIL_MSG( e.what() );
179 }
180
181 if( cfg )
182 {
183 // Save any changes to column widths, etc.
184 m_adapter->SaveSettings();
185
186 cfg->m_FootprintChooser.width = GetParent()->GetSize().x;
187 cfg->m_FootprintChooser.height = GetParent()->GetSize().y;
188 cfg->m_FootprintChooser.sash_h = m_hsplitter->GetSashPosition();
189
190 if( m_vsplitter )
191 cfg->m_FootprintChooser.sash_v = m_vsplitter->GetSashPosition();
192
194 }
195}
196
197
199{
200 if( PCBNEW_SETTINGS* settings = Pgm().GetSettingsManager().GetAppSettings<PCBNEW_SETTINGS>() )
201 {
202 auto horizPixelsFromDU =
203 [&]( int x ) -> int
204 {
205 wxSize sz( x, 0 );
206 return GetParent()->ConvertDialogToPixels( sz ).x;
207 };
208
209 PCBNEW_SETTINGS::FOOTPRINT_CHOOSER& cfg = settings->m_FootprintChooser;
210
211 int w = cfg.width < 40 ? horizPixelsFromDU( 440 ) : cfg.width;
212 int h = cfg.height < 40 ? horizPixelsFromDU( 340 ) : cfg.height;
213
214 GetParent()->SetSize( wxSize( w, h ) );
215 GetParent()->Layout();
216
217 // We specify the width of the right window (m_symbol_view_panel), because specify
218 // the width of the left window does not work as expected when SetSashGravity() is called
219 if( cfg.sash_h < 0 )
220 cfg.sash_h = horizPixelsFromDU( 220 );
221
222 m_hsplitter->SetSashPosition( cfg.sash_h );
223
224 if( cfg.sash_v < 0 )
225 cfg.sash_v = horizPixelsFromDU( 230 );
226
227 if( m_vsplitter )
228 m_vsplitter->SetSashPosition( cfg.sash_v );
229
231 }
232}
233
234
236{
237 m_adapter->SetPreselectNode( aPreselect, 0 );
238}
239
240
242{
243 return m_tree->GetSelectedLibId();
244}
245
246
247void PANEL_FOOTPRINT_CHOOSER::onCloseTimer( wxTimerEvent& aEvent )
248{
249 // Hack because of eaten MouseUp event. See PANEL_FOOTPRINT_CHOOSER::onFootprintChosen
250 // for the beginning of this spaghetti noodle.
251
252 auto state = wxGetMouseState();
253
254 if( state.LeftIsDown() )
255 {
256 // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
257 // purpose of this timer is defeated.
259 }
260 else
261 {
263 }
264}
265
266
268{
270 return;
271
272 LIB_ID lib_id = m_tree->GetSelectedLibId();
273
274 if( !lib_id.IsValid() )
275 {
276 m_preview_ctrl->SetStatusText( _( "No footprint selected" ) );
277 }
278 else
279 {
282 }
283}
284
285
286void PANEL_FOOTPRINT_CHOOSER::onFootprintChosen( wxCommandEvent& aEvent )
287{
289 {
290 // Got a selection. We can't just end the modal dialog here, because wx leaks some
291 // events back to the parent window (in particular, the MouseUp following a double click).
292 //
293 // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. This isn't really
294 // feasible to bypass without a fully custom wxDataViewCtrl implementation, and even then
295 // might not be fully possible (docs are vague). To get around this, we use a one-shot
296 // timer to schedule the dialog close.
297 //
298 // See PANEL_FOOTPRINT_CHOOSER::onCloseTimer for the other end of this spaghetti noodle.
300 }
301}
bool ReadFootprintFiles(FP_LIB_TABLE *aTable, const wxString *aNickname=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Read all the footprints provided by the combination of aTable and aNickname.
void DisplayErrors(wxTopLevelWindow *aCaller=nullptr)
unsigned GetErrorCount() const
FOOTPRINT_INFO * GetFootprintInfo(const wxString &aFootprintName)
Get info for a footprint by id.
void DisplayFootprint(const LIB_ID &aFPID)
Set the currently displayed footprint.
void SetUserUnits(EDA_UNITS aUnits)
Set the units for the preview.
bool IsInitialized() const
Return whether the widget initialized properly.
void SetStatusText(const wxString &aText)
Set the contents of the status label and display it.
void ClearStatus()
Clear the contents of the status label and hide it.
static wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > Create(EDA_BASE_FRAME *aParent, LIB_TABLE *aLibs)
Factory function: create a model adapter in a reference-counting container.
void AddLibraries(EDA_BASE_FRAME *aParent)
Add dark theme support to wxHtmlWindow.
Definition: html_window.h:34
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition: lib_id.h:172
A mix-in to provide polymorphism between items stored in libraries (symbols, aliases and footprints).
Definition: lib_tree_item.h:41
void SetPreselectNode(const LIB_ID &aLibId, int aUnit)
Set the symbol name to be selected if there are no search results.
void DoAddLibrary(const wxString &aNodeName, const wxString &aDesc, const std::vector< LIB_TREE_ITEM * > &aItemList, bool pinned, bool presorted)
Add the given list of symbols by alias.
void SetFilter(std::function< bool(LIB_TREE_NODE &aNode)> *aFilter)
Set the symbol filter type.
Model class in the component selector Model-View-Adapter (mediated MVC) architecture.
Widget displaying a tree of symbols with optional search text control and description panel....
Definition: lib_tree.h:49
LIB_TREE_MODEL_ADAPTER::SORT_MODE GetSortMode() const
Definition: lib_tree.h:137
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
For multi-unit symbols, if the user selects the symbol itself rather than picking an individual unit,...
Definition: lib_tree.cpp:271
@ ALL_WIDGETS
Definition: lib_tree.h:57
void onFootprintSelected(wxCommandEvent &aEvent)
wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > m_adapter
void onCloseTimer(wxTimerEvent &aEvent)
FOOTPRINT_PREVIEW_WIDGET * m_preview_ctrl
std::function< void()> m_closeHandler
static constexpr int DblClickDelay
std::function< bool(LIB_TREE_NODE &)> m_filter
PANEL_FOOTPRINT_CHOOSER(PCB_BASE_FRAME *aFrame, wxTopLevelWindow *aParent, const wxArrayString &aFootprintHistoryList, std::function< bool(LIB_TREE_NODE &)> aFilter, std::function< void()> aCloseHandler)
Create dialog to choose component.
void SetPreselect(const LIB_ID &aPreselect)
void onFootprintChosen(wxCommandEvent &aEvent)
Handle the selection of an item.
LIB_ID GetSelectedLibId() const
To be called after this dialog returns from ShowModal().
FOOTPRINT_CHOOSER m_FootprintChooser
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static FP_LIB_TABLE * PcbFootprintLibs(PROJECT *aProject)
Return the table of footprint libraries without Kiway.
Definition: project_pcb.cpp:37
EDA_UNITS GetUserUnits() const
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
#define _(s)
FOOTPRINT_LIST_IMPL GFootprintList
The global footprint info table.
Definition: cvpcb.cpp:140
STL namespace.
SETTINGS_MANAGER * GetSettingsManager()
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119