KiCad PCB EDA Suite
Loading...
Searching...
No Matches
fp_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
22#include <pgm_base.h>
23#include <kiplatform/ui.h>
30#include <project_pcb.h>
31#include <string_utils.h>
32#include <board.h>
33#include <footprint.h>
34#include <tool/tool_manager.h>
36
37#include <map>
38
39#include <wx/log.h>
40#include <wx/settings.h>
41
42
43wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
45{
46 auto* adapter = new FP_TREE_SYNCHRONIZING_ADAPTER( aFrame, aLibs );
47 return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
48}
49
50
57
58
63
64
65bool FP_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
73{
74 m_libs = aLibs;
75
76 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "FpSyncAdapter::Sync enter" ) );
77
78 // The work below frees nodes while the caller yields the event loop, so a GtkTreeView scroll
79 // queued earlier would point at freed rows and crash the next frame-clock tick. Drop it first.
81
82 // Detach the GtkTreeView from the model before freeing any node so the frame-clock tick during
83 // the caller's yield has no stale rows to validate. The RAII guard re-attaches on all paths.
84 ResetTreeView resetGuard( *this );
85
86 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "FpSyncAdapter::Sync freeing/updating nodes" ) );
87
88 // Process already stored libraries
89 for( auto it = m_tree.m_Children.begin(); it != m_tree.m_Children.end(); )
90 {
91 const wxString& name = it->get()->m_Name;
92
93 try
94 {
95 // Check the table row directly
96 std::optional<LIBRARY_TABLE_ROW*> optRow = m_libs->GetRow( name );
97 std::optional<LIB_STATUS> libStatus = m_libs->GetLibraryStatus( name );
98
99 bool loadFailed = libStatus.has_value()
100 && libStatus->load_status == LOAD_STATUS::LOAD_ERROR;
101
102 if( !optRow.has_value()
103 || ( *optRow )->Disabled()
104 || ( *optRow )->Hidden()
105 || loadFailed )
106 {
107 it = deleteLibrary( it );
108 continue;
109 }
110
111 updateLibrary( *static_cast<LIB_TREE_NODE_LIBRARY*>( it->get() ) );
112 }
113 catch( ... )
114 {
115 // If the library isn't found, remove it
116 it = deleteLibrary( it );
117 continue;
118 }
119
120 ++it;
121 }
122
123 // Look for new libraries
125 PROJECT_FILE& project = m_frame->Prj().GetProjectFile();
126 size_t count = m_libMap.size();
127
128 for( const auto& [libName, status] : m_libs->GetLibraryStatuses() )
129 {
130 if( status.load_status != LOAD_STATUS::LOADED || status.error )
131 continue;
132
133 if( m_libMap.count( libName ) != 0 )
134 continue;
135
136 std::optional<LIBRARY_TABLE_ROW*> optRow = m_libs->GetRow( libName );
137
138 if( !optRow.has_value() || ( *optRow )->Disabled() || ( *optRow )->Hidden() )
139 continue;
140
141 bool pinned = alg::contains( cfg->m_Session.pinned_fp_libs, libName )
142 || alg::contains( project.m_PinnedFootprintLibs, libName );
143
144 std::vector<FOOTPRINT*> footprints = m_libs->GetFootprints( libName, true );
145 std::vector<LIB_TREE_ITEM*> treeItems;
146 treeItems.reserve( footprints.size() );
147
148 for( FOOTPRINT* fp : footprints )
149 treeItems.push_back( fp );
150
151 DoAddLibrary( libName, ( *optRow )->Description(), treeItems, pinned, true );
152 m_libMap.insert( libName );
153 }
154
155 if( m_libMap.size() > count )
156 m_tree.AssignIntrinsicRanks( m_shownColumns );
157
158 wxLogTrace( wxT( "KICAD_TABS_DBG" ), wxT( "FpSyncAdapter::Sync exit" ) );
159}
160
161
163{
164 return m_libs->GetLibraryNames().size();
165}
166
167
169{
170 // Re-enumerate from disk if the library has changed since it was last preloaded.
171 // This picks up external modifications such as git branch switches.
172 m_libs->RefreshLibraryIfChanged( aLibNode.m_Name );
173
174 std::vector<FOOTPRINT*> footprints = m_libs->GetFootprints( aLibNode.m_Name, true );
175
176 // Build a map of footprint names for quick lookup
177 std::map<wxString, FOOTPRINT*> fpMap;
178
179 for( FOOTPRINT* fp : footprints )
180 fpMap[fp->GetFPID().GetLibItemName()] = fp;
181
182 // Remove items that no longer exist
183 for( auto nodeIt = aLibNode.m_Children.begin(); nodeIt != aLibNode.m_Children.end(); )
184 {
185 auto fpIt = fpMap.find( (*nodeIt)->m_Name );
186
187 if( fpIt != fpMap.end() )
188 {
189 static_cast<LIB_TREE_NODE_ITEM*>( nodeIt->get() )->Update( fpIt->second );
190 fpMap.erase( fpIt );
191 ++nodeIt;
192 }
193 else
194 {
195 nodeIt = aLibNode.m_Children.erase( nodeIt );
196 }
197 }
198
199 // Add new items
200 for( auto& [name, fp] : fpMap )
201 aLibNode.AddItem( fp );
202
204 m_libMap.insert( aLibNode.m_Name );
205}
206
207
208LIB_TREE_NODE::PTR_VECTOR::iterator
209FP_TREE_SYNCHRONIZING_ADAPTER::deleteLibrary( LIB_TREE_NODE::PTR_VECTOR::iterator& aLibNodeIt )
210{
211 LIB_TREE_NODE* node = aLibNodeIt->get();
212 m_libMap.erase( node->m_Name );
213 auto it = m_tree.m_Children.erase( aLibNodeIt );
214 return it;
215}
216
217
219{
220 return FindItem( m_frame->GetLoadedFPID() );
221}
222
223
224void FP_TREE_SYNCHRONIZING_ADAPTER::GetValue( wxVariant& aVariant, wxDataViewItem const& aItem,
225 unsigned int aCol ) const
226{
227 if( IsFrozen() )
228 {
229 aVariant = wxEmptyString;
230 return;
231 }
232
233 LIB_TREE_NODE* node = ToNode( aItem );
234
235 switch( aCol )
236 {
237 case NAME_COL:
238 {
239 if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
240 {
241 // Do not use GetLoadedFPID(); it returns m_footprintNameWhenLoaded.
242 node->m_Name =
243 m_frame->GetBoard()->GetFirstFootprint()->GetFPID().GetUniStringLibItemName();
244
245 // mark modified part with an asterisk
246 if( m_frame->GetScreen()->IsContentModified() )
247 aVariant = node->m_Name + wxT( " *" );
248 else
249 aVariant = node->m_Name;
250 }
251 else if( node->m_Pinned )
252 {
253 aVariant = GetPinningSymbol() + node->m_Name;
254 }
255 else
256 {
257 aVariant = node->m_Name;
258 }
259
260 break;
261 }
262
263 case DESC_COL:
264 {
265 if( node->m_LibId == m_frame->GetLoadedFPID() && !m_frame->IsCurrentFPFromBoard() )
266 {
267 node->m_Desc = m_frame->GetBoard()->GetFirstFootprint()->GetLibDescription();
268 }
269 else if( node->m_Type == LIB_TREE_NODE::TYPE::LIBRARY )
270 {
271 if( std::optional<wxString> optDesc = PROJECT_PCB::FootprintLibAdapter( &m_frame->Prj() )->
272 GetLibraryDescription( node->m_LibId.GetLibNickname() ) )
273 {
274 node->m_Desc = *optDesc;
275 }
276 }
277
278 wxString descStr = UnescapeString( node->m_Desc );
279 descStr.Replace( wxS( "\n" ), wxS( " " ) ); // Clear line breaks
280
281 aVariant = descStr;
282 break;
283 }
284
285 default: // column == -1 is used for default Compare function
286 aVariant = node->m_Name;
287 break;
288 }
289}
290
291
292bool FP_TREE_SYNCHRONIZING_ADAPTER::GetAttr( wxDataViewItem const& aItem, unsigned int aCol,
293 wxDataViewItemAttr& aAttr ) const
294{
295 if( IsFrozen() )
296 return false;
297
298 // change attributes only for the name field
299 if( aCol != 0 )
300 return false;
301
302 // don't link to a board footprint, even if the FPIDs match
303 if( m_frame->IsCurrentFPFromBoard() )
304 return false;
305
306 LIB_TREE_NODE* node = ToNode( aItem );
307 wxCHECK( node, false );
308
309 switch( node->m_Type )
310 {
311 case LIB_TREE_NODE::TYPE::LIBRARY:
312 if( node->m_Name == m_frame->GetLoadedFPID().GetLibNickname().wx_str() )
313 {
314 // mark the current library if it's collapsed
315 if( !m_widget->IsExpanded( ToItem( node ) ) )
316 {
317 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
318 // proxy for "is canvas item"
319 }
320
321 // mark modified libs with bold font
322 if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
323 aAttr.SetBold( true );
324 }
325 break;
326
327 case LIB_TREE_NODE::TYPE::ITEM:
328 if( node->m_LibId == m_frame->GetLoadedFPID() )
329 {
330 // mark the current (on-canvas) part
331 aAttr.SetStrikethrough( true ); // LIB_TREE_RENDERER uses strikethrough as a
332 // proxy for "is canvas item"
333
334 // mark modified part with bold font
335 if( m_frame->GetScreen()->IsContentModified() && !m_frame->IsCurrentFPFromBoard() )
336 aAttr.SetBold( true );
337 }
338 break;
339
340 default:
341 return false;
342 }
343
344 return true;
345}
346
347
348bool FP_TREE_SYNCHRONIZING_ADAPTER::HasPreview( const wxDataViewItem& aItem )
349{
350 LIB_TREE_NODE* node = ToNode( aItem );
351 wxCHECK( node, false );
352
353 return node->m_Type == LIB_TREE_NODE::TYPE::ITEM && node->m_LibId != m_frame->GetLoadedFPID();
354}
355
356
357static const wxString c_previewName = wxS( "fpHoverPreview" );
358
359
360void FP_TREE_SYNCHRONIZING_ADAPTER::ShowPreview( wxWindow* aParent, const wxDataViewItem& aItem )
361{
362 LIB_TREE_NODE* node = ToNode( aItem );
363 wxCHECK( node, /* void */ );
364
365 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
366 FOOTPRINT_PREVIEW_PANEL* preview = dynamic_cast<FOOTPRINT_PREVIEW_PANEL*>( previewWindow );
367
368 if( !preview )
369 {
370 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
371 aParent->SetSizer( mainSizer );
372
373 preview = FOOTPRINT_PREVIEW_PANEL::New( &m_frame->Kiway(), aParent, m_frame );
374
375 preview->SetName( c_previewName );
376 preview->GetGAL()->SetAxesEnabled( false );
377
378 mainSizer->Add( preview, 1, wxEXPAND | wxALL, 1 );
379 aParent->Layout();
380 }
381
382 preview->DisplayFootprint( node->m_LibId );
383}
384
385
387{
388 wxWindow* previewWindow = wxWindow::FindWindowByName( c_previewName, aParent );
389
390 if( FOOTPRINT_PREVIEW_PANEL* preview = dynamic_cast<FOOTPRINT_PREVIEW_PANEL*>( previewWindow ) )
391 {
392 preview->GetCanvas()->SetEvtHandlerEnabled( false );
393 preview->GetCanvas()->StopDrawing();
394 }
395}
const char * name
KIGFX::GAL * GetGAL() const
Return a pointer to the GAL instance used in the panel.
Module editor specific tools.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
Panel that renders a single footprint via Cairo GAL, meant to be exported through Kiface.
bool DisplayFootprint(const LIB_ID &aFPID) override
Set the currently displayed footprint.
static FOOTPRINT_PREVIEW_PANEL * New(KIWAY *aKiway, wxWindow *aParent, UNITS_PROVIDER *aUnitsProvider)
FOOTPRINT_LIBRARY_ADAPTER * m_libs
FP_TREE_MODEL_ADAPTER(PCB_BASE_FRAME *aParent, FOOTPRINT_LIBRARY_ADAPTER *aLibs)
Constructor; takes a set of libraries to be included in the search.
FP_TREE_SYNCHRONIZING_ADAPTER(FOOTPRINT_EDIT_FRAME *aFrame, FOOTPRINT_LIBRARY_ADAPTER *aLibs)
bool HasPreview(const wxDataViewItem &aItem) override
void Sync(FOOTPRINT_LIBRARY_ADAPTER *aLibs)
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(FOOTPRINT_EDIT_FRAME *aFrame, FOOTPRINT_LIBRARY_ADAPTER *aLibs)
int GetLibrariesCount() const override
Return the number of libraries loaded in the tree.
void ShutdownPreview(wxWindow *aParent) override
bool IsContainer(const wxDataViewItem &aItem) const override
LIB_TREE_NODE::PTR_VECTOR::iterator deleteLibrary(LIB_TREE_NODE::PTR_VECTOR::iterator &aLibNodeIt)
TOOL_INTERACTIVE * GetContextMenuTool() override
void ShowPreview(wxWindow *aParent, const wxDataViewItem &aItem) override
void GetValue(wxVariant &aVariant, wxDataViewItem const &aItem, unsigned int aCol) const override
void SetAxesEnabled(bool aAxesEnabled)
Enable drawing the axes.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
RAII guard that detaches the GtkTreeView from the model across a tree rebuild so a deferred frame-clo...
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.
@ DESC_COL
Library or library description column.
std::vector< wxString > m_shownColumns
LIB_TREE_NODE_LIBRARY & DoAddLibrary(const wxString &aNodeName, const wxString &aDesc, const std::vector< LIB_TREE_ITEM * > &aItemList, bool pinned, bool presorted)
Add the given list of symbols by alias.
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.
PTR_VECTOR m_Children
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 FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
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_fp_libs
static const wxString c_previewName