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>
33#include <project_sch.h>
34#include <string_utils.h>
37#include <widgets/wx_panel.h>
38
39
40wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
42{
43 auto* adapter = new SYMBOL_TREE_SYNCHRONIZING_ADAPTER( aParent, aLibMgr );
44 return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
45}
46
47
49 SYMBOL_LIBRARY_MANAGER* aLibMgr ) :
50 LIB_TREE_MODEL_ADAPTER( aParent, "pinned_symbol_libs",
51 aParent->GetViewerSettingsBase()->m_LibTree ),
52 m_frame( aParent ),
53 m_libMgr( aLibMgr ),
54 m_lastSyncHash( -1 )
55{
56}
57
58
63
64
65bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::IsContainer( const wxDataViewItem& aItem ) const
66{
67 const LIB_TREE_NODE* node = ToNode( aItem );
68 return node ? node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY : true;
69}
70
71
72void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::Sync( const wxString& aForceRefresh,
73 std::function<void( int, int, const wxString& )> aProgressCallback )
74{
75 THROTTLE progressThrottle( std::chrono::milliseconds( 120 ) );
76
77 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync enter (forceRefresh='%s')" ),
78 aForceRefresh );
79
80 // Cancel any pending GtkTreeView scroll so it cannot touch rows freed by the rebuild below.
82
83 // Detach the GtkTreeView from the model before freeing any node so no deferred tick can
84 // validate stale rows.
85 ResetTreeView resetGuard( *this );
86
87 m_lastSyncHash = m_libMgr->GetHash();
88 int i = 0, max = GetLibrariesCount();
89
91
92 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync freeing/updating nodes" ) );
93
94 // Process already stored libraries
95 for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
96 {
97 const wxString& name = it->get()->m_Name;
98
99 if( progressThrottle.Ready() )
100 aProgressCallback( i, max, name );
101
102 // Check the table row directly rather than adapter->HasLibrary(), which requires the
103 // library to be fully loaded. After table reloads (e.g. adding a new library), all
104 // previously loaded libraries are cleared and not yet reloaded, so HasLibrary() would
105 // return false and incorrectly remove them from the tree.
106 //
107 // However, we must still remove nodes for libraries that failed to load (e.g. the
108 // library file was deleted), otherwise stale symbols remain because updateLibrary()
109 // skips re-enumeration when the URI-based hash is unchanged.
110 std::optional<LIBRARY_TABLE_ROW*> optRow = adapter->GetRow( name );
111 std::optional<LIB_STATUS> libStatus = adapter->GetLibraryStatus( name );
112
113 bool loadFailed = libStatus.has_value()
114 && libStatus->load_status == LOAD_STATUS::LOAD_ERROR;
115
116 if( !optRow.has_value()
117 || ( *optRow )->Disabled()
118 || ( *optRow )->Hidden()
119 || loadFailed
120 || name == aForceRefresh )
121 {
122 it = deleteLibrary( it );
123 continue;
124 }
125 else
126 {
127 updateLibrary( *static_cast<LIB_TREE_NODE_LIBRARY*>( it->get() ) );
128 }
129
130 ++it;
131 ++i;
132 }
133
134 // Look for new libraries
135 //
137 PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
138
139 for( const auto& [libName, status] : adapter->GetLibraryStatuses() )
140 {
141 if( status.load_status != LOAD_STATUS::LOADED || status.error )
142 continue;
143
144 if( m_libHashes.count( libName ) == 0 )
145 {
146 if( progressThrottle.Ready() )
147 aProgressCallback( i++, max, libName );
148
149 auto optRow = adapter->GetRow( libName );
150 wxCHECK2( optRow.has_value(), continue );
151
152 bool pinned = alg::contains( cfg->m_Session.pinned_symbol_libs, libName )
153 || alg::contains( project.m_PinnedSymbolLibs, libName );
154
155 LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( libName, ( *optRow )->Description(), pinned );
156
157 updateLibrary( lib_node );
158 }
159 }
160
161 m_tree.AssignIntrinsicRanks( m_shownColumns );
162
163 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "SymbolSyncAdapter::Sync exit" ) );
164}
165
166
168{
170
171 for( const wxString& libName : m_libMgr->GetLibraryNames() )
172 {
173 if( m_libHashes.count( libName ) == 0 )
174 ++count;
175 }
176
177 return count;
178}
179
180
182{
183 auto hashIt = m_libHashes.find( aLibNode.m_Name );
184
185 if( hashIt == m_libHashes.end() )
186 {
187 // add a new library
188 for( LIB_SYMBOL* symbol : m_libMgr->EnumerateSymbols( aLibNode.m_Name ) )
189 aLibNode.AddItem( symbol );
190 }
191 else if( hashIt->second != m_libMgr->GetLibraryHash( aLibNode.m_Name ) )
192 {
193 // update an existing library
194 std::list<LIB_SYMBOL*> symbols = m_libMgr->EnumerateSymbols( aLibNode.m_Name );
195
196 // remove the common part from the aliases list
197 for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); /**/ )
198 {
199 auto symbolIt = std::find_if( symbols.begin(), symbols.end(),
200 [&] ( const LIB_SYMBOL* a )
201 {
202 return a->GetName() == (*nodeIt)->m_LibId.GetLibItemName().wx_str();
203 } );
204
205 if( symbolIt != symbols.end() )
206 {
207 // alias exists both in the symbol tree and the library manager,
208 // update only the node data.
209 static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( *symbolIt );
210 symbols.erase( symbolIt );
211 ++nodeIt;
212 }
213 else
214 {
215 // node does not exist in the library manager, remove the corresponding node
216 nodeIt = aLibNode.m_Children.erase( nodeIt );
217 }
218 }
219
220 // now the aliases list contains only new aliases that need to be added to the tree
221 for( LIB_SYMBOL* symbol : symbols )
222 aLibNode.AddItem( symbol );
223 }
224
226
227 for( const wxString& column : adapter->GetAvailableExtraFields( aLibNode.m_Name ) )
228 addColumnIfNecessary( column );
229
231 m_libHashes[aLibNode.m_Name] = m_libMgr->GetLibraryHash( aLibNode.m_Name );
232}
233
234
235LIB_TREE_NODE::PTR_VECTOR::iterator
236SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
237{
238 LIB_TREE_NODE* node = aLibNodeIt->get();
239 m_libHashes.erase( node->m_Name );
240 return m_tree.m_Children.erase( aLibNodeIt );
241}
242
243
245{
246 if( m_frame->GetCurSymbol() )
247 return FindItem( m_frame->GetCurSymbol()->GetLibId() );
248
249 return wxDataViewItem();
250}
251
252
253void SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
254 unsigned int aCol ) const
255{
256 if( IsFrozen() )
257 {
258 aVariant = wxEmptyString;
259 return;
260 }
261
262 LIB_TREE_NODE* node = ToNode( aItem );
263 wxASSERT( node );
264
265 switch( aCol )
266 {
267 case NAME_COL:
268 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
269 node->m_Name = m_frame->GetCurSymbol()->GetLibId().GetUniStringLibItemName();
270
271 if( node->m_Pinned )
272 aVariant = GetPinningSymbol() + UnescapeString( node->m_Name );
273 else
274 aVariant = UnescapeString( node->m_Name );
275
276 // mark modified items with an asterisk
277 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
278 {
279 if( m_libMgr->IsLibraryModified( node->m_Name ) )
280 aVariant = aVariant.GetString() + " *";
281 }
282 else if( node->m_Type == LIB_TREE_NODE::TYPE::ITEM )
283 {
284 if( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) )
285 aVariant = aVariant.GetString() + " *";
286 }
287
288 break;
289
290 default:
291 if( m_colIdxMap.count( aCol ) )
292 {
293 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
294 {
296 &m_frame->Prj() );
297
298 if( auto optRow = adapter->GetRow( node->m_LibId.GetLibNickname() ) )
299 node->m_Desc = ( *optRow )->Description();
300
301 if( !m_libMgr->IsLibraryLoaded( node->m_Name ) )
302 aVariant = _( "(failed to load)" ) + wxS( " " ) + aVariant.GetString();
303 else if( m_libMgr->IsLibraryReadOnly( node->m_Name ) )
304 aVariant = _( "(read-only)" ) + wxS( " " ) + aVariant.GetString();
305 }
306
307 const wxString& key = m_colIdxMap.at( aCol );
308
309 if( m_frame->GetCurSymbol() && m_frame->GetCurSymbol()->GetLibId() == node->m_LibId )
310 {
311 node->m_Desc = m_frame->GetCurSymbol()->GetShownDescription();
312 }
313
314 wxString valueStr;
315
316 if( key == wxT( "Description" ) )
317 valueStr = node->m_Desc;
318 else if( node->m_Fields.count( key ) )
319 valueStr = node->m_Fields.at( key );
320 else
321 valueStr = wxEmptyString;
322
323 valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
324
325 if( !aVariant.GetString().IsEmpty() )
326 {
327 if( !valueStr.IsEmpty() )
328 aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
329 }
330 else
331 {
332 aVariant = valueStr;
333 }
334 }
335 break;
336 }
337}
338
339
340bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
341 wxDataViewItemAttr& aAttr ) const
342{
343 if( IsFrozen() )
344 return false;
345
346 LIB_TREE_NODE* node = ToNode( aItem );
347 wxCHECK( node, false );
348
349 // Mark both columns of unloaded libraries using grey text color (to look disabled)
350 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
351 {
352 aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
353 return true;
354 }
355
356 // The remaining attributes are only for the name column
357 if( aCol != NAME_COL )
358 return false;
359
360 LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
361
362 switch( node->m_Type )
363 {
364 case LIB_TREE_NODE::TYPE::LIBRARY:
365 // mark modified libs with bold font
366 aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
367
368 // mark the current library if it's collapsed
369 if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
370 {
371 if( !m_widget->IsExpanded( ToItem( node ) ) )
372 {
373 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
374 // proxy for "is canvas item"
375 }
376 }
377
378 break;
379
380 case LIB_TREE_NODE::TYPE::ITEM:
381 // mark modified part with bold font
382 aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
383
384 // mark aliases with italic font
385 aAttr.SetItalic( !node->m_IsRoot );
386
387 // mark the current (on-canvas) part
388 if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
389 {
390 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
391 // proxy for "is canvas item"
392 }
393
394 break;
395
396 default:
397 return false;
398 }
399
400 return true;
401}
402
403
404bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
405{
406 LIB_TREE_NODE* node = ToNode( aItem );
407 wxCHECK( node, false );
408
409 return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
410}
411
412
413static const wxString c_previewName = wxS( "symHoverPreview" );
414
415
417 const wxDataViewItem& aItem )
418{
419 LIB_TREE_NODE* node = ToNode( aItem );
420 wxCHECK( node, /* void */ );
421
422 SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
423 wxWindow::FindWindowByName( c_previewName, aParent ) );
424
425 if( !preview )
426 {
427 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
428 aParent->SetSizer( mainSizer );
429
430 WX_PANEL* panel = new WX_PANEL( aParent );
431 panel->SetBorders( true, true, true, true );
433
434 wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
435 panel->SetSizer( panelSizer );
436
437 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
438 preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
439 preview->SetName( c_previewName );
440 preview->SetLayoutDirection( wxLayout_LeftToRight );
441
442 panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
443 mainSizer->Add( panel, 1, wxEXPAND, 0 );
444 aParent->Layout();
445 }
446
447 preview->DisplaySymbol( node->m_LibId, node->m_Unit );
448}
449
450
452{
453 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
454
455 if( SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>( previewWindow ) )
456 {
457 preview->GetCanvas()->SetEvtHandlerEnabled( false );
458 preview->GetCanvas()->StopDrawing();
459 }
460}
const char * name
static const COLOR4D BLACK
Definition color4d.h:402
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:79
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:148
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 -> #SYM_TREE_NODE.
static wxDataViewItem ToItem(const LIB_TREE_NODE *aNode)
Convert #SYM_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.
PTR_VECTOR 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:528
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.
LIB_TREE_NODE::PTR_VECTOR::iterator deleteLibrary(LIB_TREE_NODE::PTR_VECTOR::iterator &aLibNodeIt)
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
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
#define _(s)
void CancelPendingScroll(wxDataViewCtrl *aCtrl)
Cancel any pending scroll-to-item request on aCtrl.
Definition wxgtk/ui.cpp:457
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