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
235std::vector<std::unique_ptr<LIB_TREE_NODE>>::iterator
236SYMBOL_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( std::vector<std::unique_ptr<LIB_TREE_NODE>>::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 node->m_Desc = m_frame->GetCurSymbol()->GetShownDescription( FOR_GUI );
311
312 wxString valueStr;
313
314 if( key == wxT( "Description" ) )
315 valueStr = node->m_Desc;
316 else if( node->m_Fields.count( key ) )
317 valueStr = node->m_Fields.at( key );
318 else
319 valueStr = wxEmptyString;
320
321 valueStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
322
323 if( !aVariant.GetString().IsEmpty() )
324 {
325 if( !valueStr.IsEmpty() )
326 aVariant = valueStr + wxS( " - " ) + aVariant.GetString();
327 }
328 else
329 {
330 aVariant = valueStr;
331 }
332 }
333 break;
334 }
335}
336
337
338bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
339 wxDataViewItemAttr& aAttr ) const
340{
341 if( IsFrozen() )
342 return false;
343
344 LIB_TREE_NODE* node = ToNode( aItem );
345 wxCHECK( node, false );
346
347 // Mark both columns of unloaded libraries using grey text color (to look disabled)
348 if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY && !m_libMgr->IsLibraryLoaded( node->m_Name ) )
349 {
350 aAttr.SetColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
351 return true;
352 }
353
354 // The remaining attributes are only for the name column
355 if( aCol != NAME_COL )
356 return false;
357
358 LIB_SYMBOL* curSymbol = m_frame->GetCurSymbol();
359
360 switch( node->m_Type )
361 {
362 case LIB_TREE_NODE::TYPE::LIBRARY:
363 // mark modified libs with bold font
364 aAttr.SetBold( m_libMgr->IsLibraryModified( node->m_Name ) );
365
366 // mark the current library if it's collapsed
367 if( curSymbol && curSymbol->GetLibId().GetLibNickname() == node->m_LibId.GetLibNickname() )
368 {
369 if( !m_widget->IsExpanded( ToItem( node ) ) )
370 {
371 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
372 // proxy for "is canvas item"
373 }
374 }
375
376 break;
377
378 case LIB_TREE_NODE::TYPE::ITEM:
379 // mark modified part with bold font
380 aAttr.SetBold( m_libMgr->IsSymbolModified( node->m_Name, node->m_Parent->m_Name ) );
381
382 // mark aliases with italic font
383 aAttr.SetItalic( !node->m_IsRoot );
384
385 // mark the current (on-canvas) part
386 if( curSymbol && curSymbol->GetLibId() == node->m_LibId )
387 {
388 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
389 // proxy for "is canvas item"
390 }
391
392 break;
393
394 default:
395 return false;
396 }
397
398 return true;
399}
400
401
402bool SYMBOL_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
403{
404 LIB_TREE_NODE* node = ToNode( aItem );
405 wxCHECK( node, false );
406
407 return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetTargetLibId();
408}
409
410
411static const wxString c_previewName = wxS( "symHoverPreview" );
412
413
415 const wxDataViewItem& aItem )
416{
417 LIB_TREE_NODE* node = ToNode( aItem );
418 wxCHECK( node, /* void */ );
419
420 SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>(
421 wxWindow::FindWindowByName( c_previewName, aParent ) );
422
423 if( !preview )
424 {
425 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
426 aParent->SetSizer( mainSizer );
427
428 WX_PANEL* panel = new WX_PANEL( aParent );
429 panel->SetBorders( true, true, true, true );
431
432 wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
433 panel->SetSizer( panelSizer );
434
435 EDA_DRAW_PANEL_GAL::GAL_TYPE backend = m_frame->GetCanvas()->GetBackend();
436 preview = new SYMBOL_PREVIEW_WIDGET( panel, &m_frame->Kiway(), false, backend );
437 preview->SetName( c_previewName );
438 preview->SetLayoutDirection( wxLayout_LeftToRight );
439
440 panelSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
441 mainSizer->Add( panel, 1, wxEXPAND, 0 );
442 aParent->Layout();
443 }
444
445 preview->DisplaySymbol( node->m_LibId, node->m_Unit );
446}
447
448
450{
451 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
452
453 if( SYMBOL_PREVIEW_WIDGET* preview = dynamic_cast<SYMBOL_PREVIEW_WIDGET*>( previewWindow ) )
454 {
455 preview->GetCanvas()->SetEvtHandlerEnabled( false );
456 preview->GetCanvas()->StopDrawing();
457 }
458}
const char * name
static const COLOR4D BLACK
Definition color4d.h:403
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:119
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:188
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.
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:89
#define _(s)
void CancelPendingScroll(wxDataViewCtrl *aCtrl)
Cancel any pending scroll-to-item request on aCtrl.
Definition wxgtk/ui.cpp:530
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