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
272 if( !node )
273 {
274 aVariant = wxEmptyString;
275 return;
276 }
277
278 switch( aCol )
279 {
280 case NAME_COL:
281 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
282 node->m_Name = m_frame->GetCurSymbol()->GetLibId().GetUniStringLibItemName();
283
284 if( node->m_Pinned )
285 aVariant = GetPinningSymbol() + UnescapeString( node->m_Name );
286 else
287 aVariant = UnescapeString( node->m_Name );
288
289 // mark modified items with an asterisk
290 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
291 {
292 if( m_libMgr->IsLibraryModified( node->m_Name ) )
293 aVariant = aVariant.GetString() + " *";
294 }
295 else if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
296 {
297 if( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) )
298 aVariant = aVariant.GetString() + " *";
299 }
300
301 break;
302
303 default:
304 if( m_colIdxMap.count( aCol ) )
305 {
306 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
307 {
309 &m_frame->Prj() );
310
311 if( auto optRow = adapter->GetRow( node->m_LibId.GetLibNickname() ) )
312 node->m_Desc = ( *optRow )->Description();
313
314 if( !m_libMgr->IsLibraryLoaded( node->m_Name ) )
315 aVariant = _( "(failed to load)" ) + wxS( " " ) + aVariant.GetString();
316 else if( m_libMgr->IsLibraryReadOnly( node->m_Name ) )
317 aVariant = _( "(read-only)" ) + wxS( " " ) + aVariant.GetString();
318 }
319
320 const wxString& key = m_colIdxMap.at( aCol );
321
322 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
323 node->m_Desc = m_frame->GetCurSymbol()->GetShownDescription( FOR_GUI );
324
325 wxString valueStr;
326
327 if( key == wxT( "Description" ) )
328 valueStr = node->m_Desc;
329 else if( node->m_Fields.count( key ) )
330 valueStr = node->m_Fields.at( key );
331 else
332 valueStr = wxEmptyString;
333
334 valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
335
336 if( !aVariant.GetString().IsEmpty() )
337 {
338 if( !valueStr.IsEmpty() )
339 aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
340 }
341 else
342 {
343 aVariant = valueStr;
344 }
345 }
346 break;
347 }
348}
349
350
351bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
352 wxDataViewItemAttr& aAttr ) const
353{
354 if( IsFrozen() )
355 return false;
356
357 LIB_TREE_NODE* node = ToNode( aItem );
358 wxCHECK( node, false );
359
360 // Mark both columns of unloaded libraries using grey text color (to look disabled)
361 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
362 {
363 aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
364 return true;
365 }
366
367 // The remaining attributes are only for the name column
368 if( aCol != NAME_COL )
369 return false;
370
371 LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
372
373 switch( node->m_Type )
374 {
375 case LIB_TREE_NODE::TYPE::LIBRARY:
376 // mark modified libs with bold font
377 aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
378
379 // mark the current library if it's collapsed
380 if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
381 {
382 if( !m_widget->IsExpanded( ToItem( node ) ) )
383 {
384 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
385 // proxy for "is canvas item"
386 }
387 }
388
389 break;
390
391 case LIB_TREE_NODE::TYPE::ITEM:
392 // mark modified part with bold font
393 aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
394
395 // mark aliases with italic font
396 aAttr.SetItalic( !node->m_IsRoot );
397
398 // mark the current (on-canvas) part
399 if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
400 {
401 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
402 // proxy for "is canvas item"
403 }
404
405 break;
406
407 default:
408 return false;
409 }
410
411 return true;
412}
413
414
415bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
416{
417 LIB_TREE_NODE* node = ToNode( aItem );
418 wxCHECK( node, false );
419
420 return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
421}
422
423
424static const wxString c_previewName = wxS( "symHoverPreview" );
425
426
428 const wxDataViewItem& aItem )
429{
430 LIB_TREE_NODE* node = ToNode( aItem );
431 wxCHECK( node, /* void */ );
432
433 SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
434 wxWindow::FindWindowByName( c_previewName, aParent ) );
435
436 if( !preview )
437 {
438 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
439 aParent->SetSizer( mainSizer );
440
441 WX_PANEL* panel = new WX_PANEL( aParent );
442 panel->SetBorders( true, true, true, true );
444
445 wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
446 panel->SetSizer( panelSizer );
447
448 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
449 preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
450 preview->SetName( c_previewName );
451 preview->SetLayoutDirection( wxLayout_LeftToRight );
452
453 panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
454 mainSizer->Add( panel, 1, wxEXPAND, 0 );
455 aParent->Layout();
456 }
457
458 preview->DisplaySymbol( node->m_LibId, node->m_Unit );
459}
460
461
463{
464 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
465
466 if( SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>( previewWindow ) )
467 {
468 preview->GetCanvas()->SetEvtHandlerEnabled( false );
469 preview->GetCanvas()->StopDrawing();
470 }
471}
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:562
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:93
#define _(s)
void CancelPendingScroll(wxDataViewCtrl *aCtrl)
Cancel any pending scroll-to-item request on aCtrl.
Definition wxgtk/ui.cpp:649
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