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