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
104 m_frame->GetCanvas()->GetBackend() );
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...
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:241
const wxString & GetParentName() const
Definition lib_symbol.h:995
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:324
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.