KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_import_symbol_select.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * 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 <bitmaps.h>
23#include <confirm.h>
24#include <kidialog.h>
25#include <kiway.h>
26#include <lib_symbol.h>
28#include <reporter.h>
29#include <symbol_edit_frame.h>
31#include <wx/filename.h>
32#include <wx/msgdlg.h>
33#include <wx/dataview.h>
34
35
36namespace
37{
38
40constexpr wxUIntPtr ROW_DATA_DERIVED = 1;
41
43constexpr unsigned int SYMBOL_COLUMN = 1;
44
45
53class IMPORT_SYMBOL_LIST_STORE : public wxDataViewListStore
54{
55public:
56 bool GetAttrByRow( unsigned int aRow, unsigned int aCol,
57 wxDataViewItemAttr& aAttr ) const override
58 {
59 if( aCol == SYMBOL_COLUMN && GetItemData( GetItem( aRow ) ) == ROW_DATA_DERIVED )
60 {
61 aAttr.SetItalic( true );
62 return true;
63 }
64
65 return false;
66 }
67};
68
69} // namespace
70
71
73 const wxString& aFilePath,
74 const wxString& aDestLibrary,
75 SCH_IO_MGR::SCH_FILE_T aPluginType ) :
77 m_frame( aParent ),
78 m_filePath( aFilePath ),
79 m_destLibrary( aDestLibrary ),
80 m_plugin( SCH_IO_MGR::FindPlugin( aPluginType ) ),
81 m_preview( nullptr )
82{
83 // This is a direct user action, so surface any import warnings the plugin reports.
84 if( m_plugin )
85 m_plugin->SetReporter( &WXLOG_REPORTER::GetInstance() );
86
87 wxFileName fn( aFilePath );
88 SetTitle( wxString::Format( _( "Import Symbols from %s" ), fn.GetFullName() ) );
89
90 // Replace the default store with our italicizing variant before adding columns
91 // so the columns are bound to the new model.
92 IMPORT_SYMBOL_LIST_STORE* store = new IMPORT_SYMBOL_LIST_STORE();
93 m_symbolList->AssociateModel( store );
94 store->DecRef();
95
96 m_symbolList->AppendToggleColumn( wxEmptyString, wxDATAVIEW_CELL_ACTIVATABLE, 30 );
97 m_symbolList->AppendIconTextColumn( _( "Symbol" ), wxDATAVIEW_CELL_INERT, 250 );
98
99 m_symbolList->Connect( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
100 wxDataViewEventHandler( DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked ),
101 nullptr, this );
102
105 m_previewSizer->Add( m_preview, 1, wxEXPAND, 0 );
106 m_previewPanel->Layout();
107 m_unitChoice->Enable( false );
108
109 SetupStandardButtons( { { wxID_OK, _( "Import" ) } } );
110 m_sdbSizerOK->Disable();
111
114}
115
116
118{
119 m_symbolList->Disconnect( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
120 wxDataViewEventHandler( DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked ),
121 nullptr, this );
122}
123
124
126{
127 if( !loadSymbols() )
128 return false;
129
130 m_manager.BuildDependencyMaps();
131 refreshList();
133
134 return true;
135}
136
137
142
143
145{
146 if( !m_plugin )
147 {
148 DisplayError( this, _( "Unable to find a plugin to read this library." ) );
149 return false;
150 }
151
152 wxArrayString symbolNames;
153
154 try
155 {
156 m_plugin->EnumerateSymbolLib( symbolNames, m_filePath );
157 }
158 catch( const IO_ERROR& ioe )
159 {
161 wxString::Format( _( "Cannot read symbol library '%s'." ), m_filePath ),
162 ioe.What() );
163 return false;
164 }
165
166 if( symbolNames.empty() )
167 {
168 DisplayError( this, wxString::Format( _( "Symbol library '%s' is empty." ), m_filePath ) );
169 return false;
170 }
171
172 // Get the library manager to check for existing symbols
173 LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
174 m_manager.Clear();
175
176 for( const wxString& name : symbolNames )
177 {
178 wxString parentName;
179 bool isPower = false;
180 LIB_SYMBOL* sym = nullptr;
181
182 try
183 {
184 sym = m_plugin->LoadSymbol( m_filePath, name );
185
186 if( sym )
187 {
188 parentName = sym->GetParentName();
189 isPower = sym->IsPower();
190 }
191 }
192 catch( const IO_ERROR& )
193 {
194 // Symbol failed to load - still add it to list but without full info
195 }
196
197 // Add to manager - don't pass the symbol pointer since LoadSymbol returns
198 // a cached pointer owned by the plugin, not a new allocation
199 m_manager.AddSymbol( name, parentName, isPower, nullptr );
200 }
201
202 m_manager.CheckExistingSymbols(
203 [&libMgr, this]( const wxString& name ) {
204 return libMgr.SymbolExists( name, m_destLibrary );
205 } );
206
207 return true;
208}
209
210
212{
213 m_symbolList->DeleteAllItems();
214 m_listIndices.clear();
215
216 int index = 0;
217
218 for( const wxString& name : m_manager.GetSymbolNames() )
219 {
220 if( !matchesFilter( name ) )
221 {
222 m_listIndices[name] = -1;
223 continue;
224 }
225
227
228 const SYMBOL_IMPORT_INFO* info = m_manager.GetSymbolInfo( name );
229
230 if( !info )
231 continue;
232
233 wxVector<wxVariant> data;
234 wxIcon icon;
235
236 // Checkbox column - show checked for both manual and auto-selected
237 bool isChecked = info->m_checked || info->m_autoSelected;
238 data.push_back( wxVariant( isChecked ) );
239
240 if( info->m_isPower )
241 {
242 wxBitmap bmp = KiBitmap( BITMAPS::add_power );
243 icon.CopyFromBitmap( bmp );
244 }
245 else if( info->m_existsInDest )
246 {
247 wxBitmap bmp = KiBitmap( BITMAPS::small_warning );
248 icon.CopyFromBitmap( bmp );
249 }
250 // Derived (non-root) symbols are indicated by italic text via the custom
251 // store's GetAttrByRow override, matching the symbol library tree.
252
253 wxDataViewIconText iconText( name, icon );
254 data.push_back( wxVariant( iconText ) );
255
256 // Tag derived (non-root) rows so the store italicizes them, matching the
257 // convention used by the symbol library tree.
258 wxUIntPtr rowData = info->m_parentName.IsEmpty() ? 0 : ROW_DATA_DERIVED;
259 m_symbolList->AppendItem( data, rowData );
260 index++;
261 }
262
264}
265
266
268{
269 if( m_selectedSymbol.IsEmpty() )
270 {
271 m_preview->DisplayPart( nullptr, 0 );
272 m_unitChoice->Clear();
273 m_unitChoice->Enable( false );
274 return;
275 }
276
277 // Load symbol from plugin for preview (returns cached pointer)
278 LIB_SYMBOL* sym = nullptr;
279
280 try
281 {
282 sym = m_plugin->LoadSymbol( m_filePath, m_selectedSymbol );
283 }
284 catch( const IO_ERROR& )
285 {
286 m_preview->DisplayPart( nullptr, 0 );
287 return;
288 }
289
290 if( !sym )
291 {
292 m_preview->DisplayPart( nullptr, 0 );
293 return;
294 }
295
296 int unitCount = std::max( sym->GetUnitCount(), 1 );
297
298 // Update unit choice if count changed
299 m_unitChoice->Enable( unitCount > 1 );
300 m_unitChoice->Clear();
301
302 if( unitCount > 1 )
303 {
304 for( int ii = 0; ii < unitCount; ii++ )
305 m_unitChoice->Append( sym->GetUnitDisplayName( ii + 1, true ) );
306
307 m_unitChoice->SetSelection( 0 );
308 }
309
310 int selectedUnit = ( m_unitChoice->GetSelection() != wxNOT_FOUND )
311 ? m_unitChoice->GetSelection() + 1
312 : 1;
313
314 // For derived symbols, we need to flatten to show properly
315 std::unique_ptr<LIB_SYMBOL> flattenedSym;
316
317 if( sym->IsDerived() || !sym->GetParentName().IsEmpty() )
318 {
319 try
320 {
321 flattenedSym = sym->Flatten();
322 m_preview->DisplayPart( flattenedSym.get(), selectedUnit );
323 }
324 catch( const IO_ERROR& )
325 {
326 // show unflattened symbol as fallback
327 m_preview->DisplayPart( sym, selectedUnit );
328 }
329 }
330 else
331 {
332 m_preview->DisplayPart( sym, selectedUnit );
333 }
334}
335
336
338{
339 int manualCount = m_manager.GetManualSelectionCount();
340 int autoCount = m_manager.GetAutoSelectionCount();
341
342 wxString status;
343
344 if( autoCount > 0 )
345 {
346 status = wxString::Format( _( "%d symbols selected, %d parents auto-included" ),
347 manualCount, autoCount );
348 }
349 else
350 {
351 status = wxString::Format( _( "%d symbols selected" ), manualCount );
352 }
353
354 m_statusLine->SetLabel( status );
355}
356
357
359{
360 bool hasSelection = !m_manager.GetSymbolsToImport().empty();
361 m_sdbSizerOK->Enable( hasSelection );
362}
363
364
366{
367 m_filterString = m_searchCtrl->GetValue().Lower();
368 refreshList();
369}
370
371
373{
374 int row = m_symbolList->GetSelectedRow();
375
376 if( row == wxNOT_FOUND )
377 {
378 m_selectedSymbol.clear();
380 return;
381 }
382
383 for( const auto& [name, listIndex] : m_listIndices )
384 {
385 if( listIndex == row )
386 {
389 return;
390 }
391 }
392}
393
394
395void DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked( wxDataViewEvent& event )
396{
397 if( event.GetColumn() != COL_CHECKBOX )
398 return;
399
400 int row = m_symbolList->ItemToRow( event.GetItem() );
401
402 if( row == wxNOT_FOUND )
403 return;
404
405 for( const auto& [name, listIndex] : m_listIndices )
406 {
407 if( listIndex == row )
408 {
409 wxVariant value;
410 m_symbolList->GetValue( value, row, COL_CHECKBOX );
411 bool newState = value.GetBool();
412
413 toggleSymbolSelection( name, newState );
414 return;
415 }
416 }
417}
418
419
420bool DIALOG_IMPORT_SYMBOL_SELECT::toggleSymbolSelection( const wxString& aSymbolName, bool aChecked )
421{
422 if( aChecked )
423 {
424 std::vector<wxString> changed = m_manager.SetSymbolSelected( aSymbolName, true );
425
426 for( const wxString& changedName : changed )
427 {
428 auto it = m_listIndices.find( changedName );
429
430 if( it != m_listIndices.end() && it->second >= 0 )
431 {
432 m_symbolList->GetStore()->SetValueByRow( true, it->second, COL_CHECKBOX );
433 }
434 }
435 }
436 else
437 {
438 m_manager.DeselectWithDescendants( aSymbolName );
439
440 for( const wxString& name : m_manager.GetSymbolNames() )
441 {
442 auto it = m_listIndices.find( name );
443
444 if( it != m_listIndices.end() && it->second >= 0 )
445 {
446 const SYMBOL_IMPORT_INFO* info = m_manager.GetSymbolInfo( name );
447
448 if( info )
449 {
450 bool shouldBeChecked = info->m_checked || info->m_autoSelected;
451 m_symbolList->GetStore()->SetValueByRow( shouldBeChecked, it->second, COL_CHECKBOX );
452 }
453 }
454 }
455 }
456
459 return true;
460}
461
462
463bool DIALOG_IMPORT_SYMBOL_SELECT::matchesFilter( const wxString& aSymbolName ) const
464{
466}
467
468
469void DIALOG_IMPORT_SYMBOL_SELECT::OnSelectAll( wxCommandEvent& event )
470{
471 m_manager.SelectAll( [this]( const wxString& name ) { return matchesFilter( name ); } );
472
473 refreshList();
476}
477
478
479void DIALOG_IMPORT_SYMBOL_SELECT::OnSelectNone( wxCommandEvent& event )
480{
481 m_manager.DeselectAll( [this]( const wxString& name ) { return matchesFilter( name ); } );
482
483 refreshList();
486}
487
488
490{
491 if( m_selectedSymbol.IsEmpty() )
492 return;
493
494 int selectedUnit = ( m_unitChoice->GetSelection() != wxNOT_FOUND )
495 ? m_unitChoice->GetSelection() + 1
496 : 1;
497
498 // Load symbol from plugin for preview (returns cached pointer)
499 LIB_SYMBOL* sym = nullptr;
500
501 try
502 {
503 sym = m_plugin->LoadSymbol( m_filePath, m_selectedSymbol );
504 }
505 catch( const IO_ERROR& )
506 {
507 return;
508 }
509
510 if( !sym )
511 return;
512
513 // For derived symbols, we need to flatten to show properly
514 std::unique_ptr<LIB_SYMBOL> flattenedSym;
515
516 if( sym->IsDerived() || !sym->GetParentName().IsEmpty() )
517 {
518 try
519 {
520 flattenedSym = sym->Flatten();
521 m_preview->DisplayPart( flattenedSym.get(), selectedUnit );
522 }
523 catch( const IO_ERROR& )
524 {
525 m_preview->DisplayPart( sym, selectedUnit );
526 }
527 }
528 else
529 {
530 m_preview->DisplayPart( sym, selectedUnit );
531 }
532}
533
534
536{
537 return m_manager.GetSymbolsToImport();
538}
539
540
542{
543 m_conflictResolutions.clear();
544
545 std::vector<wxString> conflicts = m_manager.GetConflicts();
546
547 if( conflicts.empty() )
548 return true;
549
550 wxDialog dlg( this, wxID_ANY, _( "Resolve Import Conflicts" ),
551 wxDefaultPosition, wxSize( 500, 400 ),
552 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );
553
554 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
555
556 wxStaticText* label = new wxStaticText( &dlg, wxID_ANY,
557 _( "The following symbols already exist in the destination library. "
558 "Choose how to handle each conflict:" ) );
559 label->Wrap( 450 );
560 mainSizer->Add( label, 0, wxALL | wxEXPAND, 10 );
561
562 wxDataViewListCtrl* conflictList = new wxDataViewListCtrl( &dlg, wxID_ANY );
563 conflictList->AppendTextColumn( _( "Symbol" ), wxDATAVIEW_CELL_INERT, 200 );
564 conflictList->AppendTextColumn( _( "Action" ), wxDATAVIEW_CELL_EDITABLE, 100 );
565
566 for( const wxString& name : conflicts )
567 {
568 wxVector<wxVariant> data;
569 data.push_back( wxVariant( name ) );
570 data.push_back( wxVariant( _( "Overwrite" ) ) );
571 conflictList->AppendItem( data );
572
574 }
575
576 mainSizer->Add( conflictList, 1, wxALL | wxEXPAND, 10 );
577
578 wxBoxSizer* actionSizer = new wxBoxSizer( wxHORIZONTAL );
579 wxButton* skipAllBtn = new wxButton( &dlg, wxID_ANY, _( "Skip All" ) );
580 wxButton* overwriteAllBtn = new wxButton( &dlg, wxID_ANY, _( "Overwrite All" ) );
581 actionSizer->Add( skipAllBtn, 0, wxRIGHT, 5 );
582 actionSizer->Add( overwriteAllBtn, 0 );
583 mainSizer->Add( actionSizer, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10 );
584
585 wxStdDialogButtonSizer* btnSizer = new wxStdDialogButtonSizer();
586 btnSizer->AddButton( new wxButton( &dlg, wxID_OK, _( "Import" ) ) );
587 btnSizer->AddButton( new wxButton( &dlg, wxID_CANCEL ) );
588 btnSizer->Realize();
589 mainSizer->Add( btnSizer, 0, wxALL | wxEXPAND, 10 );
590
591 dlg.SetSizer( mainSizer );
592
593 skipAllBtn->Bind( wxEVT_BUTTON, [&]( wxCommandEvent& ) {
594 for( size_t i = 0; i < conflicts.size(); i++ )
595 {
596 conflictList->SetTextValue( _( "Skip" ), i, 1 );
598 }
599 } );
600
601 overwriteAllBtn->Bind( wxEVT_BUTTON, [&]( wxCommandEvent& ) {
602 for( size_t i = 0; i < conflicts.size(); i++ )
603 {
604 conflictList->SetTextValue( _( "Overwrite" ), i, 1 );
606 }
607 } );
608
609 conflictList->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [&]( wxDataViewEvent& evt ) {
610 int row = conflictList->ItemToRow( evt.GetItem() );
611
612 if( row >= 0 && row < (int) conflicts.size() )
613 {
614 wxString action = conflictList->GetTextValue( row, 1 );
615 m_conflictResolutions[conflicts[row]] =
616 ( action == _( "Skip" ) ) ? CONFLICT_RESOLUTION::SKIP
618 }
619 } );
620
621 return dlg.ShowModal() == wxID_OK;
622}
int index
const char * name
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition bitmap.cpp:100
DIALOG_IMPORT_SYMBOL_SELECT_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Import Symbols from %s"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(900, 650), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
bool loadSymbols()
Load symbols from the source file and populate the manager.
SYMBOL_IMPORT_MANAGER m_manager
Manager for symbol selection logic.
void refreshList()
Refresh the list control based on current filter and selections.
std::vector< wxString > GetSelectedSymbols() const
Get the list of symbols selected for import.
bool toggleSymbolSelection(const wxString &aSymbolName, bool aChecked)
Toggle selection state for a symbol.
wxString m_filterString
Current filter string.
wxString m_selectedSymbol
Currently selected symbol for preview.
bool matchesFilter(const wxString &aSymbolName) const
Check if a symbol matches the current filter.
std::map< wxString, CONFLICT_RESOLUTION > m_conflictResolutions
Conflict resolutions chosen by user.
void OnSymbolSelected(wxDataViewEvent &event) override
bool resolveConflicts()
Show conflict resolution dialog.
void updateImportButton()
Update import button enabled state based on selection.
void updateStatusLine()
Update the status line with selection counts.
void OnFilterTextChanged(wxCommandEvent &event) override
void OnSelectAll(wxCommandEvent &event) override
std::map< wxString, int > m_listIndices
Map from symbol name to list index (UI-only, -1 if filtered out)
void OnSelectNone(wxCommandEvent &event) override
void onItemChecked(wxDataViewEvent &event)
Handle checkbox toggle in the list.
void OnUnitChanged(wxCommandEvent &event) override
DIALOG_IMPORT_SYMBOL_SELECT(SYMBOL_EDIT_FRAME *aParent, const wxString &aFilePath, const wxString &aDestLibrary, SCH_IO_MGR::SCH_FILE_T aPluginType)
void updatePreview()
Update the preview for the currently selected symbol.
IO_RELEASER< SCH_IO > m_plugin
Plugin kept alive for symbol access during dialog lifetime.
void SetInitialFocus(wxWindow *aWindow)
Sets the window (usually a wxTextCtrl) that should be focused when the dialog is shown.
Definition dialog_shim.h:94
void SetupStandardButtons(std::map< int, wxString > aLabels={})
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
@ GAL_TYPE_OPENGL
OpenGL implementation.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Symbol library management helper that is specific to the symbol library editor frame.
Define a library symbol object.
Definition lib_symbol.h:114
bool IsPower() const override
bool IsDerived() const
Definition lib_symbol.h:231
const wxString & GetParentName() const
Definition lib_symbol.h:953
int GetUnitCount() const override
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
wxString GetUnitDisplayName(int aUnit, bool aLabel) const override
Return the user-defined display name for aUnit for symbols with units.
A factory which returns an instance of a SCH_IO.
Definition sch_io_mgr.h:50
The symbol library editor main window.
static bool MatchesFilter(const wxString &aSymbolName, const wxString &aFilter)
Check if a symbol name matches a filter string (case-insensitive contains).
bool SymbolExists(const wxString &aSymbolName, const wxString &aLibrary) const
Return true if symbol with a specific alias exists in library (either original one or buffered).
static REPORTER & GetInstance()
Definition reporter.cpp:279
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:192
This file is part of the common library.
#define _(s)
Information about a symbol available for import.
@ OVERWRITE
Overwrite existing symbol.
@ SKIP
Don't import this symbol.