KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_footprint_users.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <algorithm>
23
24#include <wx/clipbrd.h>
25#include <wx/menu.h>
26#include <wx/types.h>
27#include <wx/wupdlock.h>
28
29#include <kiway.h>
30#include <kiway_mail.h>
31#include <string_utils.h>
32
33
34namespace
35{
36// A footprint is normally used by at most a few dozen symbols, which is all the list needs
37// to show.
38static constexpr int MAX_USER_RESULTS = 400;
39
40
42enum FOOTPRINT_USERS_MATCH_CATEGORY
43{
44 MATCH_FILTERS = 1 << 0,
45 MATCH_FOOTPRINT_FIELD = 1 << 1,
46 MATCH_PIN_MAPS = 1 << 2
47};
48
49
51enum FOOTPRINT_USERS_COLUMN
52{
53 COL_FOOTPRINT_USERS_LIBRARY = 0,
54 COL_FOOTPRINT_USERS_SYMBOL,
55 COL_FOOTPRINT_USERS_PINS,
56 COL_FOOTPRINT_USERS_FILTER,
57 COL_FOOTPRINT_USERS_FOOTPRINT_FIELD_MATCH,
58 COL_FOOTPRINT_USERS_PIN_MAP_MATCH
59};
60
61
62enum ROW_DATA_FLAGS
63{
64 ROW_DATA_FLAG_PIN_COUNT_MATCHES = 0x01,
65 ROW_DATA_FLAG_DERIVED_SYMBOL = 0x02
66};
67
68
73class FOOTPRINT_USERS_LIST_STORE : public wxDataViewListStore
74{
75public:
76 bool GetAttrByRow( unsigned int aRow, unsigned int aCol, wxDataViewItemAttr& aAttr ) const override
77 {
78 switch( aCol )
79 {
80 case COL_FOOTPRINT_USERS_SYMBOL:
81 if( GetItemData( GetItem( aRow ) ) & ROW_DATA_FLAG_DERIVED_SYMBOL )
82 {
83 aAttr.SetItalic( true );
84 return true;
85 }
86 break;
87 case COL_FOOTPRINT_USERS_PINS:
88 if( GetItemData( GetItem( aRow ) ) & ROW_DATA_FLAG_PIN_COUNT_MATCHES )
89 {
90 aAttr.SetBold( true );
91 return true;
92 }
93 break;
94 default:
95 break;
96 }
97
98 return false;
99 }
100};
101
102} // namespace
103
104
105DIALOG_FOOTPRINT_USERS::DIALOG_FOOTPRINT_USERS( wxWindow* aParent, KIWAY& aKiway, const LIB_ID& aFootprint,
106 int aFootprintPinCount ) :
108 m_kiway( aKiway ),
109 m_footprint( aFootprint ),
110 m_footprintPinCount( aFootprintPinCount ),
111 m_loadTimer( this ),
112 m_librariesReady( false )
113{
114 // Replace the default store with the pin count bolding variant before adding columns
115 FOOTPRINT_USERS_LIST_STORE* store = new FOOTPRINT_USERS_LIST_STORE();
116 m_footprintUserList->AssociateModel( store );
117 store->DecRef();
118
119 // Each row is one matching symbol; the columns say where it comes from and what makes it
120 // a match.
121 m_footprintUserList->AppendTextColumn( _( "Library" ), wxDATAVIEW_CELL_INERT, 180 );
122 m_footprintUserList->AppendTextColumn( _( "Symbol" ), wxDATAVIEW_CELL_INERT, 200 );
123 m_footprintUserList->AppendTextColumn( _( "Pins" ), wxDATAVIEW_CELL_INERT, 60 );
124 m_footprintUserList->AppendTextColumn( _( "Footprint filter" ), wxDATAVIEW_CELL_INERT, 160 );
125 m_footprintUserList->AppendToggleColumn( _( "Footprint field match" ), wxDATAVIEW_CELL_INERT, 120 );
126 m_footprintUserList->AppendTextColumn( _( "Pin map match" ), wxDATAVIEW_CELL_INERT, 160 );
127
128
129 // Double-clicking a user opens that symbol in the symbol editor.
130 m_footprintUserList->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED, &DIALOG_FOOTPRINT_USERS::onUserActivated, this );
131 m_footprintUserList->Bind( wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &DIALOG_FOOTPRINT_USERS::onUserRightClick, this );
132 m_footprintUserList->Bind( wxEVT_KEY_DOWN, &DIALOG_FOOTPRINT_USERS::onUserKeyDown, this );
133
134 // Every way of using the footprint is shown by default, and switched independently.
135 m_cbShowFilterMatches->SetValue( true );
136 m_cbShowFootprintFieldMatches->SetValue( true );
137 m_cbShowPinMapMatches->SetValue( true );
138
142
143 // The libraries the users come from are loaded in the background. Rather than blocking
144 // the dialog, the status text shows the load progress and the list is greyed out until
145 // they arrive.
146 Bind( wxEVT_TIMER, &DIALOG_FOOTPRINT_USERS::onLoadTimer, this, m_loadTimer.GetId() );
147
148 m_footprintUserList->SetFocus();
149
150 // We'll fill this status text as needed
151 m_statusText->SetLabel( wxEmptyString );
152
153 // Most likely the symbol libraries are already loaded/ing (eeschema preloads them when a
154 // project is open) but this is basically free in that case.
156
157 // Wait until the dialog has been painted before showing the results or the loading
158 // indication.
159 CallAfter(
160 [this]()
161 {
162 updateUsers();
163 } );
164
165 // Now all widgets have the size fixed, call FinishDialogSettings
167}
168
169
171{
172 if( !symbolLibrariesReady() )
173 return;
174
175 const int categories = matchCategories();
176
178 query.m_Footprint = m_footprint;
179 query.m_MatchFilters = ( categories & MATCH_FILTERS ) != 0;
180 query.m_MatchFootprintField = ( categories & MATCH_FOOTPRINT_FIELD ) != 0;
181 query.m_MatchPinMaps = ( categories & MATCH_PIN_MAPS ) != 0;
182 query.m_MaxResults = MAX_USER_RESULTS;
183
185}
186
187
189{
190 int categories = 0;
191
192 if( m_cbShowFilterMatches->GetValue() )
193 categories |= MATCH_FILTERS;
194
195 if( m_cbShowFootprintFieldMatches->GetValue() )
196 categories |= MATCH_FOOTPRINT_FIELD;
197
198 if( m_cbShowPinMapMatches->GetValue() )
199 categories |= MATCH_PIN_MAPS;
200
201 return categories;
202}
203
204
206{
207 if( m_librariesReady )
208 return true;
209
210 float progress = GetSymbolLibrariesLoadProgress( m_kiway );
211
212 if( progress >= 1.0f )
213 {
214 m_librariesReady = true;
215 showLoadIndication( false );
216 return true;
217 }
218
219 showLoadIndication( true, progress );
220 // Go and poll in #onLoadTimer
221 m_loadTimer.StartOnce( 250 );
222 return false;
223}
224
225
227{
228 std::vector<FOOTPRINT_USER_MATCH> matches = aResult.m_Matches;
229
230 const auto libThenSymbolNameCmp =
231 []( const FOOTPRINT_USER_MATCH& aLeft, const FOOTPRINT_USER_MATCH& aRight )
232 {
233 const int libCmp = StrNumCmp( aLeft.m_Symbol.GetUniStringLibNickname(),
234 aRight.m_Symbol.GetUniStringLibNickname(),
235 true );
236
237 if( libCmp != 0 )
238 return libCmp < 0;
239
241 aRight.m_Symbol.GetUniStringLibItemName(),
242 true ) < 0;
243 };
244
245 std::sort( matches.begin(), matches.end(), libThenSymbolNameCmp );
246
247 wxWindowUpdateLocker lock( m_footprintUserList );
248 m_footprintUserList->DeleteAllItems();
249
250 for( const FOOTPRINT_USER_MATCH& match : matches )
251 {
252 wxVector<wxVariant> row;
253
254 row.push_back( wxVariant( match.m_Symbol.GetUniStringLibNickname() ) );
255 row.push_back( wxVariant( match.m_Symbol.GetUniStringLibItemName() ) );
256 row.push_back( wxVariant( wxString::Format( wxS( "%d" ), match.m_PinCount ) ) );
257 row.push_back( wxVariant( match.m_MatchedFilter ) );
258 row.push_back( wxVariant( match.m_MatchesFootprintField ) );
259
260 // The pin map column names the maps bound to the footprint rather than ticking a box.
261 wxString pinMapNames;
262
263 for( const wxString& mapName : match.m_MatchedPinMaps )
264 {
265 if( !pinMapNames.IsEmpty() )
266 pinMapNames += wxS( ", " );
267
268 pinMapNames += mapName;
269 }
270
271 row.push_back( wxVariant( pinMapNames ) );
272
273 wxUIntPtr rowData = 0;
274
275 if( match.m_PinCount == m_footprintPinCount )
276 rowData |= ROW_DATA_FLAG_PIN_COUNT_MATCHES;
277
278 if( match.m_IsDerived )
279 rowData |= ROW_DATA_FLAG_DERIVED_SYMBOL;
280
281 m_footprintUserList->AppendItem( row, rowData );
282 }
283
284 if( !aResult.m_Success )
285 {
286 m_statusText->SetLabel( _( "Failed to query matching symbols." ) );
287 }
288 else if( aResult.m_IsLimited )
289 {
290 m_statusText->SetLabel(
291 wxString::Format( _( "This footprint is referenced by more than %zu symbols" ), matches.size() ) );
292 }
293 else
294 {
295 m_statusText->SetLabel(
296 wxString::Format( _( "This footprint is referenced by %zu symbols" ), matches.size() ) );
297 }
298
299 Layout();
300}
301
302
303void DIALOG_FOOTPRINT_USERS::showLoadIndication( bool aShow, float aProgress )
304{
305 m_footprintUserList->Enable( !aShow );
306
307 if( aShow )
308 {
309 m_statusText->SetLabel(
310 wxString::Format( _( "Loading symbol libraries (%d%%)..." ), static_cast<int>( aProgress * 100.0f ) ) );
311 }
312 else
313 {
314 m_footprintUserList->DeleteAllItems();
315 m_statusText->SetLabel( wxEmptyString );
316 }
317}
318
319
320void DIALOG_FOOTPRINT_USERS::onMatchTypeChanged( wxCommandEvent& aEvent )
321{
322 updateUsers();
323}
324
325
326void DIALOG_FOOTPRINT_USERS::onLoadTimer( wxTimerEvent& aEvent )
327{
328 if( !symbolLibrariesReady() )
329 return;
330
331 // Inputs may have changed while the libraries were loading.
332 updateUsers();
333}
334
335
337{
338 // Events can have wxNOT_FOUND as the row.
339 if( aRow < 0 || aRow >= static_cast<int>( m_footprintUserList->GetItemCount() ) )
340 return wxEmptyString;
341
342 const wxString library = m_footprintUserList->GetTextValue( aRow, COL_FOOTPRINT_USERS_LIBRARY );
343 const wxString symbol = m_footprintUserList->GetTextValue( aRow, COL_FOOTPRINT_USERS_SYMBOL );
344
345 // A row without a library and symbol is not a match.
346 if( library.IsEmpty() || symbol.IsEmpty() )
347 return wxEmptyString;
348
349 LIB_ID symbolId;
350
351 if( symbolId.Parse( library + wxS( ":" ) + symbol ) != -1 || !symbolId.IsValid() )
352 return wxEmptyString;
353
354 return symbolId.Format().wx_str();
355}
356
357
358void DIALOG_FOOTPRINT_USERS::openSymbolInEditor( const wxString& aSymbolName )
359{
360 m_kiway.Player( FRAME_SCH_SYMBOL_EDITOR, true );
361
362 std::string packet = aSymbolName.utf8_string();
363 m_kiway.ExpressMail( FRAME_SCH_SYMBOL_EDITOR, MAIL_SCH_EDIT_LIBID, packet, this );
364
365 // Close the dialog to avoid a modal focus fight
366 EndModal( wxID_OK );
367}
368
369
370void DIALOG_FOOTPRINT_USERS::copySymbolName( const wxString& aSymbolName )
371{
372 if( wxTheClipboard->Open() )
373 {
374 wxTheClipboard->SetData( new wxTextDataObject( aSymbolName ) );
375 wxTheClipboard->Close();
376 }
377}
378
379
380void DIALOG_FOOTPRINT_USERS::onUserActivated( wxDataViewEvent& aEvent )
381{
382 wxString symbolName = userSymbolName( m_footprintUserList->ItemToRow( aEvent.GetItem() ) );
383
384 if( !symbolName.IsEmpty() )
385 openSymbolInEditor( symbolName );
386}
387
388
389void DIALOG_FOOTPRINT_USERS::onUserRightClick( wxDataViewEvent& aEvent )
390{
391 const wxDataViewItem item = aEvent.GetItem();
392
393 // The context menu can be asked for from the empty area below the rows.
394 if( !item.IsOk() )
395 return;
396
397 wxString symbolName = userSymbolName( m_footprintUserList->ItemToRow( item ) );
398
399 if( symbolName.IsEmpty() )
400 return;
401
402 enum
403 {
404 OPEN_IN_SYMBOL_EDITOR = 1,
405 COPY_SYMBOL_NAME
406 };
407
408 wxMenu menu;
409
410 menu.Append( OPEN_IN_SYMBOL_EDITOR, _( "Open in Symbol Editor" ) );
411 menu.Append( COPY_SYMBOL_NAME, _( "Copy Symbol Name" ) + "\tCtrl+C" );
412
413 switch( GetPopupMenuSelectionFromUser( menu ) )
414 {
415 case OPEN_IN_SYMBOL_EDITOR:
416 openSymbolInEditor( symbolName );
417 break;
418 case COPY_SYMBOL_NAME:
419 copySymbolName( symbolName );
420 break;
421 default:
422 break;
423 }
424}
425
426
428{
429 if( aEvent.ControlDown() && aEvent.GetKeyCode() == 'C' )
430 {
431 wxString symbolName = userSymbolName( m_footprintUserList->GetSelectedRow() );
432
433 if( !symbolName.IsEmpty() )
434 {
435 copySymbolName( symbolName );
436 return;
437 }
438 }
439
440 aEvent.Skip();
441}
DIALOG_FOOTPRINT_USERS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Footprint users"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(750, 450), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void onMatchTypeChanged(wxCommandEvent &aEvent)
void showLoadIndication(bool aShow, float aProgress=0.0f)
void onUserActivated(wxDataViewEvent &aEvent)
void showUsers(const FOOTPRINT_USERS_RESULT &aResult)
void onUserRightClick(wxDataViewEvent &aEvent)
wxString userSymbolName(int aRow) const
Return the "library:symbol" name of a table row, or an empty string if there is none.
int matchCategories() const
Return the enabled match categories, as the bit set of FOOTPRINT_USERS_MATCH_CATEGORY.
void copySymbolName(const wxString &aSymbolName)
bool symbolLibrariesReady()
Return true once the symbol libraries are loaded, showing the progress when they are not.
void openSymbolInEditor(const wxString &aSymbolName)
void onLoadTimer(wxTimerEvent &aEvent)
void updateUsers()
Show the users for the enabled match categories, querying them when they are not cached yet.
DIALOG_FOOTPRINT_USERS(wxWindow *aParent, KIWAY &aKiway, const LIB_ID &aFootprint, int aFootprintPinCount)
void onUserKeyDown(wxKeyEvent &aEvent)
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
A minimalistic software bus for communications between various DLLs/DSOs (DSOs) within the same KiCad...
Definition kiway.h:348
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
UTF8 Format() const
Definition lib_id.cpp:132
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
wxString wx_str() const
Definition utf8.cpp:41
#define _(s)
@ FRAME_SCH_SYMBOL_EDITOR
Definition frame_type.h:31
@ MAIL_SCH_EDIT_LIBID
Definition mail_type.h:54
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
All the information needed to query the symbol libraries for the symbols using a footprint.
bool m_MatchPinMaps
When true, symbols whose pin maps are bound to the footprint are returned.
int m_MaxResults
Limits the number of returned symbols; zero means no limit.
bool m_MatchFilters
When true, symbols whose footprint filters select the footprint are returned.
bool m_MatchFootprintField
When true, symbols whose footprint field names the footprint are returned.
LIB_ID m_Footprint
The footprint to find the users of.
The outcome of a footprint users query, and the JSON encoding used to move it across the Eeschema kif...
bool m_IsLimited
True if the result set is limited by the maximum number of results.
std::vector< FOOTPRINT_USER_MATCH > m_Matches
The matching symbols, in the order the libraries were searched.
bool m_Success
False if the query could not be performed, in which case Matches is empty.
One symbol that uses a footprint, and the reason it matched.
LIB_ID m_Symbol
The "library:symbol" name of the matching symbol.
FOOTPRINT_USERS_RESULT QueryFootprintUsers(KIWAY &aKiway, const FOOTPRINT_USERS_QUERY &aQuery)
Ask the symbol libraries, for the symbols that use a given footprint (over the Eeschema kiface).
bool StartSymbolLibrariesLoad(KIWAY &aKiway)
Start loading the symbol libraries in the background.
float GetSymbolLibrariesLoadProgress(KIWAY &aKiway)
Return how far the background load of the symbol libraries has got.