KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_tree_synchronizing_adapter.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) 2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
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 3
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, see <https://www.gnu.org/licenses/>.
20 */
21
23
24#include <wx/log.h>
25#include <wx/settings.h>
26
27#include <core/throttle.h>
28#include <kiplatform/ui.h>
29#include <pgm_base.h>
31#include <symbol_edit_frame.h>
35#include <tool/tool_manager.h>
36#include <project_sch.h>
37#include <string_utils.h>
40#include <widgets/wx_panel.h>
41
42
43wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
45{
46 auto* adapter = new SYMBOL_TREE_SYNCHRONIZING_ADAPTER( aParent, aLibMgr );
47 return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
48}
49
50
52 SYMBOL_LIBRARY_MANAGER* aLibMgr ) :
53 LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs",
54 aParent->GetViewerSettingsBase()->m_LibTree ),
55 m_frame( aParent ),
56 m_libMgr( aLibMgr ),
57 m_lastSyncHash( -1 )
58{
59}
60
61
66
67
68bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
69{
70 const LIB_TREE_NODE* node = ToNode( aItem );
71 return node ? node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY : true;
72}
73
74
75void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( const wxString& aForceRefresh,
76 std::function<void( int, int, const wxString& )> aProgressCallback )
77{
78 THROTTLE progressThrottle( std::chrono::milliseconds( 120 ) );
79
80 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync enter (forceRefresh='%s')" ),
81 aForceRefresh );
82
83 // Cancel any pending GtkTreeView scroll so it cannot touch rows freed by the rebuild below.
85
86 // Detach the GtkTreeView from the model before freeing any node so no deferred tick can
87 // validate stale rows.
88 ResetTreeView resetGuard( *this );
89
90 m_lastSyncHash = m_libMgr->GetHash();
91 int i = 0, max = GetLibrariesCount();
92
94
95 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync freeing/updating nodes" ) );
96
97 // Process already stored libraries
98 for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
99 {
100 const wxString& name = it->get()->m_Name;
101
102 if( progressThrottle.Ready() )
103 aProgressCallback( i, max, name );
104
105 // Check the table row directly rather than adapter->HasLibrary(), which requires the
106 // library to be fully loaded. After table reloads (e.g. adding a new library), all
107 // previously loaded libraries are cleared and not yet reloaded, so HasLibrary() would
108 // return false and incorrectly remove them from the tree.
109 //
110 // However, we must still remove nodes for libraries that failed to load (e.g. the
111 // library file was deleted), otherwise stale symbols remain because updateLibrary()
112 // skips re-enumeration when the URI-based hash is unchanged.
113 std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( name );
114 std::optional<LIB_STATUS> libStatus = adapter->GetLibraryStatus( name );
115
116 bool loadFailed = libStatus.has_value()
117 && libStatus->load_status == LOAD_STATUS::LOAD_ERROR;
118
119 if( !optRow.has_value()
120 || ( *optRow )->Disabled()
121 || ( *optRow )->Hidden()
122 || loadFailed
123 || name == aForceRefresh )
124 {
125 it = deleteLibrary( it );
126 continue;
127 }
128 else
129 {
130 updateLibrary( *static_cast<LIB_TREE_NODE_LIBRARY*>( it->get() ) );
131 }
132
133 ++it;
134 ++i;
135 }
136
137 // Look for new libraries
138 //
140 PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
141
142 for( const auto& [libName, status] : adapter->GetLibraryStatuses() )
143 {
144 if( status.load_status != LOAD_STATUS::LOADED || status.error )
145 continue;
146
147 if( m_libHashes.count( libName ) == 0 )
148 {
149 if( progressThrottle.Ready() )
150 aProgressCallback( i++, max, libName );
151
152 std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( libName );
153 wxCHECK2( optRow.has_value(), continue );
154
155 if( optRow.value()->Hidden() )
156 continue;
157
158 bool pinned = alg::contains( cfg->m_Session.pinned_symbol_libs, libName )
159 || alg::contains( project.m_PinnedSymbolLibs, libName );
160
161 wxString description = optRow.value()->Description();
162
163 LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( libName, description, pinned );
164
165 updateLibrary( lib_node );
166 }
167 }
168
169 m_tree.AssignIntrinsicRanks( m_shownColumns );
170
171 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync exit" ) );
172}
173
174
176{
178
179 for( const wxString& libName : m_libMgr->GetLibraryNames() )
180 {
181 if( m_libHashes.count( libName ) == 0 )
182 ++count;
183 }
184
185 return count;
186}
187
188
190{
191 auto hashIt = m_libHashes.find( aLibNode.m_Name );
192
193 if( hashIt == m_libHashes.end() )
194 {
195 // add a new library
196 for( LIB_SYMBOL* symbol : m_libMgr->EnumerateSymbols( aLibNode.m_Name ) )
197 aLibNode.AddItem( symbol );
198 }
199 else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.m_Name ) )
200 {
201 // update an existing library
202 std::list<LIB_SYMBOL*> symbols = m_libMgr->EnumerateSymbols( aLibNode.m_Name );
203
204 // remove the common part from the aliases list
205 for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); /**/ )
206 {
207 auto symbolIt = std::find_if( symbols.begin(), symbols.end(),
208 [&] ( const LIB_SYMBOL* a )
209 {
210 return a->GetName() == (*nodeIt)->m_LibId.GetLibItemName().wx_str();
211 } );
212
213 if( symbolIt != symbols.end() )
214 {
215 // alias exists both in the symbol tree and the library manager,
216 // update only the node data.
217 static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( *symbolIt );
218 symbols.erase( symbolIt );
219 ++nodeIt;
220 }
221 else
222 {
223 // node does not exist in the library manager, remove the corresponding node
224 nodeIt = aLibNode.m_Children.erase( nodeIt );
225 }
226 }
227
228 // now the aliases list contains only new aliases that need to be added to the tree
229 for( LIB_SYMBOL* symbol : symbols )
230 aLibNode.AddItem( symbol );
231 }
232
234
235 for( const wxString& column : adapter->GetAvailableExtraFields( aLibNode.m_Name ) )
236 addColumnIfNecessary( column );
237
239 m_libHashes[aLibNode.m_Name] = m_libMgr->GetLibraryHash( aLibNode.m_Name );
240}
241
242
243std::vector<std::unique_ptr<LIB_TREE_NODE>>::iterator
244SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( std::vector<std::unique_ptr<LIB_TREE_NODE>>::iterator& aLibNodeIt )
245{
246 LIB_TREE_NODE* node = aLibNodeIt->get();
247 m_libHashes.erase( node->m_Name );
248 return m_tree.m_Children.erase( aLibNodeIt );
249}
250
251
253{
254 if( m_frame->GetCurSymbol() )
255 return FindItem( m_frame->GetCurSymbol()->GetLibId() );
256
257 return wxDataViewItem();
258}
259
260
261void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
262 unsigned int aCol ) const
263{
264 if( IsFrozen() )
265 {
266 aVariant = wxEmptyString;
267 return;
268 }
269
270 LIB_TREE_NODE* node = ToNode( aItem );
271 wxASSERT( node );
272
273 switch( aCol )
274 {
275 case NAME_COL:
276 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
277 node->m_Name = m_frame->GetCurSymbol()->GetLibId().GetUniStringLibItemName();
278
279 if( node->m_Pinned )
280 aVariant = GetPinningSymbol() + UnescapeString( node->m_Name );
281 else
282 aVariant = UnescapeString( node->m_Name );
283
284 // mark modified items with an asterisk
285 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
286 {
287 if( m_libMgr->IsLibraryModified( node->m_Name ) )
288 aVariant = aVariant.GetString() + " *";
289 }
290 else if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
291 {
292 if( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) )
293 aVariant = aVariant.GetString() + " *";
294 }
295
296 break;
297
298 default:
299 if( m_colIdxMap.count( aCol ) )
300 {
301 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
302 {
304 &m_frame->Prj() );
305
306 if( auto optRow = adapter->GetRow( node->m_LibId.GetLibNickname() ) )
307 node->m_Desc = ( *optRow )->Description();
308
309 if( !m_libMgr->IsLibraryLoaded( node->m_Name ) )
310 aVariant = _( "(failed to load)" ) + wxS( " " ) + aVariant.GetString();
311 else if( m_libMgr->IsLibraryReadOnly( node->m_Name ) )
312 aVariant = _( "(read-only)" ) + wxS( " " ) + aVariant.GetString();
313 }
314
315 const wxString& key = m_colIdxMap.at( aCol );
316
317 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
318 node->m_Desc = m_frame->GetCurSymbol()->GetShownDescription( FOR_GUI );
319
320 wxString valueStr;
321
322 if( key == wxT( "Description" ) )
323 valueStr = node->m_Desc;
324 else if( node->m_Fields.count( key ) )
325 valueStr = node->m_Fields.at( key );
326 else
327 valueStr = wxEmptyString;
328
329 valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
330
331 if( !aVariant.GetString().IsEmpty() )
332 {
333 if( !valueStr.IsEmpty() )
334 aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
335 }
336 else
337 {
338 aVariant = valueStr;
339 }
340 }
341 break;
342 }
343}
344
345
346bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
347 wxDataViewItemAttr& aAttr ) const
348{
349 if( IsFrozen() )
350 return false;
351
352 LIB_TREE_NODE* node = ToNode( aItem );
353 wxCHECK( node, false );
354
355 // Mark both columns of unloaded libraries using grey text color (to look disabled)
356 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
357 {
358 aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
359 return true;
360 }
361
362 // The remaining attributes are only for the name column
363 if( aCol != NAME_COL )
364 return false;
365
366 LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
367
368 switch( node->m_Type )
369 {
370 case LIB_TREE_NODE::TYPE::LIBRARY:
371 // mark modified libs with bold font
372 aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
373
374 // mark the current library if it's collapsed
375 if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
376 {
377 if( !m_widget->IsExpanded( ToItem( node ) ) )
378 {
379 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
380 // proxy for "is canvas item"
381 }
382 }
383
384 break;
385
386 case LIB_TREE_NODE::TYPE::ITEM:
387 // mark modified part with bold font
388 aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
389
390 // mark aliases with italic font
391 aAttr.SetItalic( !node->m_IsRoot );
392
393 // mark the current (on-canvas) part
394 if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
395 {
396 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
397 // proxy for "is canvas item"
398 }
399
400 break;
401
402 default:
403 return false;
404 }
405
406 return true;
407}
408
409
410bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
411{
412 LIB_TREE_NODE* node = ToNode( aItem );
413 wxCHECK( node, false );
414
415 return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
416}
417
418
419static const wxString c_previewName = wxS( "symHoverPreview" );
420
421
423 const wxDataViewItem& aItem )
424{
425 LIB_TREE_NODE* node = ToNode( aItem );
426 wxCHECK( node, /* void */ );
427
428 SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
429 wxWindow::FindWindowByName( c_previewName, aParent ) );
430
431 if( !preview )
432 {
433 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
434 aParent->SetSizer( mainSizer );
435
436 WX_PANEL* panel = new WX_PANEL( aParent );
437 panel->SetBorders( true, true, true, true );
439
440 wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
441 panel->SetSizer( panelSizer );
442
443 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
444 preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
445 preview->SetName( c_previewName );
446 preview->SetLayoutDirection( wxLayout_LeftToRight );
447
448 panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
449 mainSizer->Add( panel, 1, wxEXPAND, 0 );
450 aParent->Layout();
451 }
452
453 preview->DisplaySymbol( node->m_LibId, node->m_Unit );
454}
455
456
458{
459 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
460
461 if( SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>( previewWindow ) )
462 {
463 preview->GetCanvas()->SetEvtHandlerEnabled( false );
464 preview->GetCanvas()->StopDrawing();
465 }
466}
const char * name
static const COLOR4D BLACK
Definition color4d.h:404
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const
Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::vector< std::pair< wxString, LIB_STATUS > > GetLibraryStatuses() const
Returns a list of all library nicknames and their status (even if they failed to load)
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Define a library symbol object.
Definition lib_symbol.h:114
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
RAII guard that detaches the GtkTreeView from the model across a tree rebuild so a deferred frame-clo...
LIB_TREE_MODEL_ADAPTER(EDA_BASE_FRAME *aParent, const wxString &aPinnedKey, APP_SETTINGS_BASE::LIB_TREE &aSettingsStruct)
Create the adapter.
void addColumnIfNecessary(const wxString &aHeader)
static LIB_TREE_NODE * ToNode(wxDataViewItem aItem)
Convert wxDataViewItem -> LIB_TREE_NODE.
static wxDataViewItem ToItem(const LIB_TREE_NODE *aNode)
Convert LIB_TREE_NODE -> wxDataViewItem.
static const wxString GetPinningSymbol()
@ NAME_COL
Library or library item name column.
std::map< unsigned, wxString > m_colIdxMap
virtual int GetLibrariesCount() const
Return the number of libraries loaded in the tree.
std::vector< wxString > m_shownColumns
LIB_TREE_NODE_LIBRARY & DoAddLibraryNode(const wxString &aNodeName, const wxString &aDesc, bool pinned)
wxDataViewItem FindItem(const LIB_ID &aLibId)
Returns tree item corresponding to part.
Node type: LIB_ID.
void Update(LIB_TREE_ITEM *aItem)
Update the node using data from a LIB_ALIAS object.
Node type: library.
LIB_TREE_NODE_ITEM & AddItem(LIB_TREE_ITEM *aItem)
Construct a new alias node, add it to this library, and return it.
Model class in the component selector Model-View-Adapter (mediated MVC) architecture.
std::map< wxString, wxString > m_Fields
List of weighted search terms.
std::vector< std::unique_ptr< LIB_TREE_NODE > > m_Children
LIB_TREE_NODE * m_Parent
void AssignIntrinsicRanks(const std::vector< wxString > &aShownColumns, bool presorted=false)
Store intrinsic ranks on all children of this node.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:546
The backing store for a PROJECT, in JSON format.
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Handle actions for the various symbol editor and viewers.
The symbol library editor main window.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::vector< wxString > GetAvailableExtraFields(const wxString &aNickname)
Returns a list of additional (non-mandatory) symbol fields present in the given library.
Class to handle modifications to the symbol libraries.
void DisplaySymbol(const LIB_ID &aSymbolID, int aUnit, int aBodyStyle=0)
Set the currently displayed symbol.
void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
bool GetAttr(wxDataViewItem const &aItem, unsigned int aCol, wxDataViewItemAttr &aAttr) const override
void updateLibrary(LIB_TREE_NODE_LIBRARY &aLibNode)
static wxObjectDataPtr< LIB_TREE_MODEL_ADAPTER > Create(SYMBOL_EDIT_FRAME *aParent, SYMBOL_LIBRARY_MANAGER *aLibs)
void ShowPreview(wxWindow *aParent, const wxDataViewItem &aItem) override
std::vector< std::unique_ptr< LIB_TREE_NODE > >::iterator deleteLibrary(std::vector< std::unique_ptr< LIB_TREE_NODE > >::iterator &aLibNodeIt)
int GetLibrariesCount() const override
Return the number of libraries loaded in the tree.
int m_lastSyncHash
SYMBOL_LIBRARY_MANAGER hash value returned in the last synchronization.
void Sync(const wxString &aForceRefresh, std::function< void(int, int, const wxString &)> aProgressCallback)
bool IsContainer(const wxDataViewItem &aItem) const override
bool HasPreview(const wxDataViewItem &aItem) override
std::map< wxString, int > m_libHashes
Hashes to decide whether a library needs an update.
SYMBOL_TREE_SYNCHRONIZING_ADAPTER(SYMBOL_EDIT_FRAME *aParent, SYMBOL_LIBRARY_MANAGER *aLibMgr)
Rate-limiter that fires at most once per interval.
Definition throttle.h:31
bool Ready()
Definition throttle.h:44
void SetBorders(bool aLeft, bool aRight, bool aTop, bool aBottom)
Definition wx_panel.h:35
void SetBorderColor(const KIGFX::COLOR4D &aColor)
Definition wx_panel.h:43
@ FOR_GUI
Definition common.h:91
#define _(s)
void CancelPendingScroll(wxDataViewCtrl *aCtrl)
Cancel any pending scroll-to-item request on aCtrl.
Definition wxgtk/ui.cpp:542
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
wxString UnescapeString(const wxString &aSource)
std::vector< wxString > pinned_symbol_libs
static const wxString c_previewName