KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_design_block_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <pgm_base.h>
25#include <design_block.h>
29#include <kiface_base.h>
30#include <sch_edit_frame.h>
31#include <widgets/lib_tree.h>
35#include <string_utils.h>
36#include <wx/log.h>
37#include <wx/panel.h>
38#include <wx/sizer.h>
39#include <wx/splitter.h>
40#include <wx/timer.h>
41#include <wx/wxhtml.h>
42#include <wx/msgdlg.h>
44
45
47
48
50 std::vector<LIB_ID>& aHistoryList,
51 std::function<void()> aSelectHandler ) :
52 wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
53 m_dbl_click_timer( nullptr ),
54 m_open_libs_timer( nullptr ),
55 m_vsplitter( nullptr ),
56 m_tree( nullptr ),
57 m_preview( nullptr ),
58 m_frame( aFrame ),
59 m_selectHandler( std::move( aSelectHandler ) ),
60 m_historyList( aHistoryList )
61{
63
64 // Make sure settings are loaded before we start running multi-threaded design block loaders
66
67 // Load design block files:
68 WX_PROGRESS_REPORTER* progressReporter =
69 new WX_PROGRESS_REPORTER( aParent, _( "Loading Design Block Libraries" ), 1 );
70 DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( libs, nullptr, progressReporter );
71
72 // Force immediate deletion of the WX_PROGRESS_REPORTER. Do not use Destroy(), or use
73 // Destroy() followed by wxSafeYield() because on Windows, APP_PROGRESS_DIALOG and
74 // WX_PROGRESS_REPORTER have some side effects on the event loop manager. For instance, a
75 // subsequent call to ShowModal() or ShowQuasiModal() for a dialog following the use of a
76 // WX_PROGRESS_REPORTER results in incorrect modal or quasi modal behavior.
77 delete progressReporter;
78
79 if( DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetErrorCount() )
80 displayErrors( aFrame );
81
84
85 // -------------------------------------------------------------------------------------
86 // Construct the actual panel
87 //
88
89 wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
90
91 m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
92 wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
93
94
95 // Avoid the splitter window being assigned as the parent to additional windows.
96 m_vsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
97
98 wxPanel* treePanel = new wxPanel( m_vsplitter );
99 wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
100 treePanel->SetSizer( treeSizer );
101
102 wxPanel* detailsPanel = new wxPanel( m_vsplitter );
103 wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL );
104 detailsPanel->SetSizer( detailsSizer );
105
106 m_preview = new DESIGN_BLOCK_PREVIEW_WIDGET( detailsPanel, false,
108 detailsSizer->Add( m_preview, 1, wxEXPAND, 5 );
109 detailsPanel->Layout();
110 detailsSizer->Fit( detailsPanel );
111
112 m_vsplitter->SetSashGravity( 0.5 );
113 m_vsplitter->SetMinimumPaneSize( 20 );
114 m_vsplitter->SplitHorizontally( treePanel, detailsPanel );
115
116 sizer->Add( m_vsplitter, 1, wxEXPAND, 5 );
117
118
119 m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter,
121
122 treeSizer->Add( m_tree, 1, wxEXPAND, 5 );
123 treePanel->Layout();
124 treeSizer->Fit( treePanel );
125
126 RefreshLibs();
127 m_adapter->FinishTreeInitialization();
128
130
131 m_dbl_click_timer = new wxTimer( this );
132 m_open_libs_timer = new wxTimer( this );
133
134 SetSizer( sizer );
135
136 Layout();
137
138 Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this,
139 m_dbl_click_timer->GetId() );
140 Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this,
141 m_open_libs_timer->GetId() );
142 Bind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this );
143 Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this );
144
145 // Open the user's previously opened libraries on timer expiration.
146 // This is done on a timer because we need a gross hack to keep GTK from garbling the
147 // display. Must be longer than the search debounce timer.
148 m_open_libs_timer->StartOnce( 300 );
149}
150
151
153{
154 Unbind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this );
155 Unbind( EVT_LIBITEM_SELECTED, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected, this );
156 Unbind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this );
157 Unbind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this );
158
159 // Stop the timer during destruction early to avoid potential race conditions (that do happen)
160 m_dbl_click_timer->Stop();
161 m_open_libs_timer->Stop();
162 delete m_dbl_click_timer;
163 delete m_open_libs_timer;
164}
165
166
168{
170
171 if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
172 {
173 // Save any changes to column widths, etc.
174 m_adapter->SaveSettings();
175
176 cfg->m_DesignBlockChooserPanel.width = GetParent()->GetSize().x;
177 cfg->m_DesignBlockChooserPanel.height = GetParent()->GetSize().y;
178
179 if( m_vsplitter )
180 cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
181
182 cfg->m_DesignBlockChooserPanel.sort_mode = m_tree->GetSortMode();
183 }
184}
185
186
188{
189 if( m_tree )
191}
192
193
194void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
195{
196 if( aEvent.GetKeyCode() == WXK_ESCAPE )
197 {
198 wxObject* eventSource = aEvent.GetEventObject();
199
200 if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( eventSource ) )
201 {
202 // First escape cancels search string value
203 if( textCtrl->GetValue() == m_tree->GetSearchString()
204 && !m_tree->GetSearchString().IsEmpty() )
205 {
206 m_tree->SetSearchString( wxEmptyString );
207 return;
208 }
209 }
210 }
211 else
212 {
213 aEvent.Skip();
214 }
215}
216
217
219{
220 if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
221 {
222 auto horizPixelsFromDU =
223 [&]( int x ) -> int
224 {
225 wxSize sz( x, 0 );
226 return GetParent()->ConvertDialogToPixels( sz ).x;
227 };
228
229 EESCHEMA_SETTINGS::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel;
230
231 int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 );
232 int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 );
233
234 GetParent()->SetSize( wxSize( w, h ) );
235 GetParent()->Layout();
236
237 // We specify the width of the right window (m_design_block_view_panel), because specify
238 // the width of the left window does not work as expected when SetSashGravity() is called
239
240 if( panelCfg.sash_pos_h < 0 )
241 panelCfg.sash_pos_h = horizPixelsFromDU( 220 );
242
243 if( panelCfg.sash_pos_v < 0 )
244 panelCfg.sash_pos_v = horizPixelsFromDU( 230 );
245
246 if( m_vsplitter )
247 m_vsplitter->SetSashPosition( panelCfg.sash_pos_v );
248
249 m_adapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) panelCfg.sort_mode );
250 }
251}
252
253
255{
256 // Unselect before syncing to avoid null reference in the adapter
257 // if a selected item is removed during the sync
258 m_tree->Unselect();
259
261 static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() );
262
263 // Clear all existing libraries then re-add
264 adapter->ClearLibraries();
265
266 // Read the libraries from disk if they've changed
268 adapter->SetLibTable( fpTable );
269
270 // Sync FOOTPRINT_INFO list to the libraries on disk
271 if( aProgress )
272 {
273 WX_PROGRESS_REPORTER progressReporter( this, _( "Updating Design Block Libraries" ), 2 );
275 &progressReporter );
276 progressReporter.Show( false );
277 }
278 else
279 {
280 DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( fpTable, nullptr, nullptr );
281 }
282
284
285 if( !m_historyList.empty() )
286 adapter->SetPreselectNode( m_historyList[0], 0 );
287
288 adapter->AddLibraries( m_frame );
289
290 m_tree->Regenerate( true );
291}
292
293
295{
296 m_adapter->SetPreselectNode( aPreselect, 0 );
297}
298
299
301{
302 return m_tree->GetSelectedLibId( aUnit );
303}
304
305
307{
308 m_tree->CenterLibId( aLibId );
309 m_tree->SelectLibId( aLibId );
310}
311
312
314{
315 // Hack because of eaten MouseUp event. See PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen
316 // for the beginning of this spaghetti noodle.
317
318 wxMouseState state = wxGetMouseState();
319
320 if( state.LeftIsDown() )
321 {
322 // Mouse hasn't been raised yet, so fire the timer again. Otherwise the
323 // purpose of this timer is defeated.
325 }
326 else
327 {
331 }
332}
333
334
336{
337 if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
338 m_adapter->OpenLibs( cfg->m_LibTree.open_libs );
339
340 // Bind this now se we don't spam the event queue with EVT_LIBITEM_SELECTED events during
341 // the initial load.
342 Bind( EVT_LIBITEM_SELECTED, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected, this );
343}
344
345
347{
348 if( GetSelectedLibId().IsValid() )
350}
351
352
354{
356 {
357 // Got a selection. We can't just end the modal dialog here, because wx leaks some events
358 // back to the parent window (in particular, the MouseUp following a double click).
359 //
360 // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. This isn't really
361 // feasible to bypass without a fully custom wxDataViewCtrl implementation, and even then
362 // might not be fully possible (docs are vague). To get around this, we use a one-shot
363 // timer to schedule the dialog close.
364 //
365 // See PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer for the other end of this spaghetti noodle.
367 }
368}
369
371{
372 LIB_ID savedId = GetSelectedLibId();
373
374 m_tree->Unselect();
375
376 // Remove duplicates
377 for( int i = (int) m_historyList.size() - 1; i >= 0; --i )
378 {
379 if( m_historyList[i] == aLibId )
380 m_historyList.erase( m_historyList.begin() + i );
381 }
382
383 // Add the new name at the beginning of the history list
384 m_historyList.insert( m_historyList.begin(), aLibId );
385
386 // Remove extra names
387 while( m_historyList.size() >= 8 )
388 m_historyList.pop_back();
389
391 m_tree->Regenerate( true );
392
393 SelectLibId( savedId );
394}
395
396
398{
399 m_adapter->RemoveGroup( true, false );
400
401 // Build the history list
402 std::vector<LIB_TREE_ITEM*> historyInfos;
403
404 for( const LIB_ID& lib : m_historyList )
405 {
407 lib.GetLibNickname(), lib.GetLibItemName() );
408
409 // this can be null, for example, if the design block has been deleted from a library.
410 if( fp_info != nullptr )
411 historyInfos.push_back( fp_info );
412 }
413
414 m_adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
415 historyInfos, false, true )
416 .m_IsRecentlyUsedGroup = true;
417}
418
419
420void PANEL_DESIGN_BLOCK_CHOOSER::displayErrors( wxTopLevelWindow* aWindow )
421{
422 // @todo: go to a more HTML !<table>! ? centric output, possibly with recommendations
423 // for remedy of errors. Add numeric error codes to PARSE_ERROR, and switch on them for
424 // remedies, etc. Full access is provided to everything in every exception!
425
426 HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) );
427
428 dlg.MessageSet( _( "Errors were encountered loading footprints:" ) );
429
430 wxString msg;
431
432 while( std::unique_ptr<IO_ERROR> error = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().PopError() )
433 {
434 wxString tmp = EscapeHTML( error->Problem() );
435
436 // Preserve new lines in error messages so queued errors don't run together.
437 tmp.Replace( wxS( "\n" ), wxS( "<BR>" ) );
438 msg += wxT( "<p>" ) + tmp + wxT( "</p>" );
439 }
440
441 dlg.AddHTML_Text( msg );
442
443 dlg.ShowModal();
444}
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static DESIGN_BLOCK_LIST_IMPL & GetGlobalList()
bool ReadDesignBlockFiles(DESIGN_BLOCK_LIB_TABLE *aTable, const wxString *aNickname=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr) override
Read all the design blocks provided by the combination of aTable and aNickname.
DESIGN_BLOCK_INFO * GetDesignBlockInfo(const wxString &aDesignBlockName)
Get info for a design block by id.
void DisplayDesignBlock(DESIGN_BLOCK *aDesignBlock)
Set the currently displayed symbol.
static wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > Create(EDA_BASE_FRAME *aParent, LIB_TABLE *aLibs, APP_SETTINGS_BASE::LIB_TREE &aSettings)
Factory function: create a model adapter in a reference-counting container.
void SetLibTable(DESIGN_BLOCK_LIB_TABLE *aLibs)
int ShowModal() override
@ GAL_TYPE_OPENGL
OpenGL implementation.
void SetFocus() override
PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel
void MessageSet(const wxString &message)
Add a message (in bold) to message list.
void AddHTML_Text(const wxString &message)
Add HTML text (without any change) to message list.
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
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.
Widget displaying a tree of symbols with optional search text control and description panel.
Definition: lib_tree.h:49
void CenterLibId(const LIB_ID &aLibId)
Ensure that an item is visible (preferably centered).
Definition: lib_tree.cpp:355
void ShowChangedLanguage()
Definition: lib_tree.cpp:291
void SelectLibId(const LIB_ID &aLibId)
Select an item in the tree widget.
Definition: lib_tree.cpp:349
LIB_TREE_MODEL_ADAPTER::SORT_MODE GetSortMode() const
Definition: lib_tree.h:142
wxString GetSearchString() const
Definition: lib_tree.cpp:393
void Unselect()
Unselect currently selected item in wxDataViewCtrl.
Definition: lib_tree.cpp:361
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:301
@ ALL_WIDGETS
Definition: lib_tree.h:58
void SetSearchString(const wxString &aSearchString)
Save/restore search string.
Definition: lib_tree.cpp:387
void Regenerate(bool aKeepState)
Regenerate the tree.
Definition: lib_tree.cpp:428
DESIGN_BLOCK_PREVIEW_WIDGET * m_preview
std::function< void()> m_selectHandler
void onOpenLibsTimer(wxTimerEvent &aEvent)
void addDesignBlockToHistory(const LIB_ID &aLibId)
void SetPreselect(const LIB_ID &aPreselect)
void onCloseTimer(wxTimerEvent &aEvent)
void displayErrors(wxTopLevelWindow *aWindow)
LIB_ID GetSelectedLibId(int *aUnit=nullptr) const
To be called after this dialog returns from ShowModal().
PANEL_DESIGN_BLOCK_CHOOSER(SCH_EDIT_FRAME *aFrame, wxWindow *aParent, std::vector< LIB_ID > &aHistoryList, std::function< void()> aSelectHandler)
Create dialog to choose design_block.
void RefreshLibs(bool aProgress=false)
wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > m_adapter
void onDesignBlockSelected(wxCommandEvent &aEvent)
void onDesignBlockChosen(wxCommandEvent &aEvent)
Handle the selection of an item.
void SelectLibId(const LIB_ID &aLibId)
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:125
virtual DESIGN_BLOCK_LIB_TABLE * DesignBlockLibs()
Return the table of design block libraries.
Definition: project.cpp:429
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
EESCHEMA_SETTINGS * eeconfig() const
Schematic editor (Eeschema) main window.
DESIGN_BLOCK * GetDesignBlock(const LIB_ID &aLibId, bool aUseCacheLib=false, bool aShowErrorMsg=false)
Load design block from design block library table.
T * GetAppSettings(const wxString &aFilename)
Return a handle to the a given settings by type.
Multi-thread safe progress reporter dialog, intended for use of tasks that parallel reporting back of...
#define _(s)
STL namespace.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.