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 along
17 * with this program. If not, see <http://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 <symbol_edit_frame.h>
30#include <wx/filename.h>
31#include <wx/msgdlg.h>
32#include <wx/dataview.h>
33
34
36 const wxString& aFilePath,
37 const wxString& aDestLibrary,
38 SCH_IO_MGR::SCH_FILE_T aPluginType ) :
40 m_frame( aParent ),
41 m_filePath( aFilePath ),
42 m_destLibrary( aDestLibrary ),
43 m_plugin( SCH_IO_MGR::FindPlugin( aPluginType ) ),
44 m_preview( nullptr )
45{
46 wxFileName fn( aFilePath );
47 SetTitle( wxString::Format( _( "Import Symbols from %s" ), fn.GetFullName() ) );
48
49 m_symbolList->AppendToggleColumn( wxEmptyString, wxDATAVIEW_CELL_ACTIVATABLE, 30 );
50 m_symbolList->AppendIconTextColumn( _( "Symbol" ), wxDATAVIEW_CELL_INERT, 250 );
51
52 m_symbolList->Connect( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
53 wxDataViewEventHandler( DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked ),
54 nullptr, this );
55
58 m_previewSizer->Add( m_preview, 1, wxEXPAND, 0 );
59 m_previewPanel->Layout();
60 m_unitChoice->Enable( false );
61
62 SetupStandardButtons( { { wxID_OK, _( "Import" ) } } );
63 m_sdbSizerOK->Disable();
64
67}
68
69
71{
72 m_symbolList->Disconnect( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED,
73 wxDataViewEventHandler( DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked ),
74 nullptr, this );
75}
76
77
79{
80 if( !loadSymbols() )
81 return false;
82
83 m_manager.BuildDependencyMaps();
86
87 return true;
88}
89
90
95
96
98{
99 if( !m_plugin )
100 {
101 DisplayError( this, _( "Unable to find a plugin to read this library." ) );
102 return false;
103 }
104
105 wxArrayString symbolNames;
106
107 try
108 {
109 m_plugin->EnumerateSymbolLib( symbolNames, m_filePath );
110 }
111 catch( const IO_ERROR& ioe )
112 {
114 wxString::Format( _( "Cannot read symbol library '%s'." ), m_filePath ),
115 ioe.What() );
116 return false;
117 }
118
119 if( symbolNames.empty() )
120 {
121 DisplayError( this, wxString::Format( _( "Symbol library '%s' is empty." ), m_filePath ) );
122 return false;
123 }
124
125 // Get the library manager to check for existing symbols
126 LIB_SYMBOL_LIBRARY_MANAGER& libMgr = m_frame->GetLibManager();
127 m_manager.Clear();
128
129 for( const wxString& name : symbolNames )
130 {
131 wxString parentName;
132 bool isPower = false;
133 LIB_SYMBOL* sym = nullptr;
134
135 try
136 {
137 sym = m_plugin->LoadSymbol( m_filePath, name );
138
139 if( sym )
140 {
141 parentName = sym->GetParentName();
142 isPower = sym->IsPower();
143 }
144 }
145 catch( const IO_ERROR& )
146 {
147 // Symbol failed to load - still add it to list but without full info
148 }
149
150 // Add to manager - don't pass the symbol pointer since LoadSymbol returns
151 // a cached pointer owned by the plugin, not a new allocation
152 m_manager.AddSymbol( name, parentName, isPower, nullptr );
153 }
154
155 m_manager.CheckExistingSymbols(
156 [&libMgr, this]( const wxString& name ) {
157 return libMgr.SymbolExists( name, m_destLibrary );
158 } );
159
160 return true;
161}
162
163
165{
166 m_symbolList->DeleteAllItems();
167 m_listIndices.clear();
168
169 int index = 0;
170
171 for( const wxString& name : m_manager.GetSymbolNames() )
172 {
173 if( !matchesFilter( name ) )
174 {
175 m_listIndices[name] = -1;
176 continue;
177 }
178
180
181 const SYMBOL_IMPORT_INFO* info = m_manager.GetSymbolInfo( name );
182
183 if( !info )
184 continue;
185
186 wxVector<wxVariant> data;
187 wxIcon icon;
188
189 // Checkbox column - show checked for both manual and auto-selected
190 bool isChecked = info->m_checked || info->m_autoSelected;
191 data.push_back( wxVariant( isChecked ) );
192
193 if( info->m_isPower )
194 {
195 wxBitmap bmp = KiBitmap( BITMAPS::add_power );
196 icon.CopyFromBitmap( bmp );
197 }
198 else if( info->m_existsInDest )
199 {
200 wxBitmap bmp = KiBitmap( BITMAPS::small_warning );
201 icon.CopyFromBitmap( bmp );
202 }
203 else if( !info->m_parentName.IsEmpty() )
204 {
205 wxBitmap bmp = KiBitmap( BITMAPS::tree_nosel );
206 icon.CopyFromBitmap( bmp );
207 }
208
209 wxDataViewIconText iconText( name, icon );
210 data.push_back( wxVariant( iconText ) );
211
212 m_symbolList->AppendItem( data );
213 index++;
214 }
215
217}
218
219
221{
222 if( m_selectedSymbol.IsEmpty() )
223 {
224 m_preview->DisplayPart( nullptr, 0 );
225 m_unitChoice->Clear();
226 m_unitChoice->Enable( false );
227 return;
228 }
229
230 // Load symbol from plugin for preview (returns cached pointer)
231 LIB_SYMBOL* sym = nullptr;
232
233 try
234 {
235 sym = m_plugin->LoadSymbol( m_filePath, m_selectedSymbol );
236 }
237 catch( const IO_ERROR& )
238 {
239 m_preview->DisplayPart( nullptr, 0 );
240 return;
241 }
242
243 if( !sym )
244 {
245 m_preview->DisplayPart( nullptr, 0 );
246 return;
247 }
248
249 int unitCount = std::max( sym->GetUnitCount(), 1 );
250
251 // Update unit choice if count changed
252 m_unitChoice->Enable( unitCount > 1 );
253 m_unitChoice->Clear();
254
255 if( unitCount > 1 )
256 {
257 for( int ii = 0; ii < unitCount; ii++ )
258 m_unitChoice->Append( sym->GetUnitDisplayName( ii + 1, true ) );
259
260 m_unitChoice->SetSelection( 0 );
261 }
262
263 int selectedUnit = ( m_unitChoice->GetSelection() != wxNOT_FOUND )
264 ? m_unitChoice->GetSelection() + 1
265 : 1;
266
267 // For derived symbols, we need to flatten to show properly
268 std::unique_ptr<LIB_SYMBOL> flattenedSym;
269
270 if( sym->IsDerived() || !sym->GetParentName().IsEmpty() )
271 {
272 try
273 {
274 flattenedSym = sym->Flatten();
275 m_preview->DisplayPart( flattenedSym.get(), selectedUnit );
276 }
277 catch( const IO_ERROR& )
278 {
279 // show unflattened symbol as fallback
280 m_preview->DisplayPart( sym, selectedUnit );
281 }
282 }
283 else
284 {
285 m_preview->DisplayPart( sym, selectedUnit );
286 }
287}
288
289
291{
292 int manualCount = m_manager.GetManualSelectionCount();
293 int autoCount = m_manager.GetAutoSelectionCount();
294
295 wxString status;
296
297 if( autoCount > 0 )
298 {
299 status = wxString::Format( _( "%d symbols selected, %d parents auto-included" ),
300 manualCount, autoCount );
301 }
302 else
303 {
304 status = wxString::Format( _( "%d symbols selected" ), manualCount );
305 }
306
307 m_statusLine->SetLabel( status );
308}
309
310
312{
313 bool hasSelection = !m_manager.GetSymbolsToImport().empty();
314 m_sdbSizerOK->Enable( hasSelection );
315}
316
317
319{
320 m_filterString = m_searchCtrl->GetValue().Lower();
321 refreshList();
322}
323
324
326{
327 int row = m_symbolList->GetSelectedRow();
328
329 if( row == wxNOT_FOUND )
330 {
331 m_selectedSymbol.clear();
333 return;
334 }
335
336 for( const auto& [name, listIndex] : m_listIndices )
337 {
338 if( listIndex == row )
339 {
342 return;
343 }
344 }
345}
346
347
348void DIALOG_IMPORT_SYMBOL_SELECT::onItemChecked( wxDataViewEvent& event )
349{
350 if( event.GetColumn() != COL_CHECKBOX )
351 return;
352
353 int row = m_symbolList->ItemToRow( event.GetItem() );
354
355 if( row == wxNOT_FOUND )
356 return;
357
358 for( const auto& [name, listIndex] : m_listIndices )
359 {
360 if( listIndex == row )
361 {
362 wxVariant value;
363 m_symbolList->GetValue( value, row, COL_CHECKBOX );
364 bool newState = value.GetBool();
365
366 toggleSymbolSelection( name, newState );
367 return;
368 }
369 }
370}
371
372
373bool DIALOG_IMPORT_SYMBOL_SELECT::toggleSymbolSelection( const wxString& aSymbolName, bool aChecked )
374{
375 if( aChecked )
376 {
377 std::vector<wxString> changed = m_manager.SetSymbolSelected( aSymbolName, true );
378
379 for( const wxString& changedName : changed )
380 {
381 auto it = m_listIndices.find( changedName );
382
383 if( it != m_listIndices.end() && it->second >= 0 )
384 {
385 m_symbolList->GetStore()->SetValueByRow( true, it->second, COL_CHECKBOX );
386 }
387 }
388 }
389 else
390 {
391 m_manager.DeselectWithDescendants( aSymbolName );
392
393 for( const wxString& name : m_manager.GetSymbolNames() )
394 {
395 auto it = m_listIndices.find( name );
396
397 if( it != m_listIndices.end() && it->second >= 0 )
398 {
399 const SYMBOL_IMPORT_INFO* info = m_manager.GetSymbolInfo( name );
400
401 if( info )
402 {
403 bool shouldBeChecked = info->m_checked || info->m_autoSelected;
404 m_symbolList->GetStore()->SetValueByRow( shouldBeChecked, it->second, COL_CHECKBOX );
405 }
406 }
407 }
408 }
409
412 return true;
413}
414
415
416bool DIALOG_IMPORT_SYMBOL_SELECT::matchesFilter( const wxString& aSymbolName ) const
417{
419}
420
421
422void DIALOG_IMPORT_SYMBOL_SELECT::OnSelectAll( wxCommandEvent& event )
423{
424 m_manager.SelectAll( [this]( const wxString& name ) { return matchesFilter( name ); } );
425
426 refreshList();
429}
430
431
432void DIALOG_IMPORT_SYMBOL_SELECT::OnSelectNone( wxCommandEvent& event )
433{
434 m_manager.DeselectAll( [this]( const wxString& name ) { return matchesFilter( name ); } );
435
436 refreshList();
439}
440
441
443{
444 if( m_selectedSymbol.IsEmpty() )
445 return;
446
447 int selectedUnit = ( m_unitChoice->GetSelection() != wxNOT_FOUND )
448 ? m_unitChoice->GetSelection() + 1
449 : 1;
450
451 // Load symbol from plugin for preview (returns cached pointer)
452 LIB_SYMBOL* sym = nullptr;
453
454 try
455 {
456 sym = m_plugin->LoadSymbol( m_filePath, m_selectedSymbol );
457 }
458 catch( const IO_ERROR& )
459 {
460 return;
461 }
462
463 if( !sym )
464 return;
465
466 // For derived symbols, we need to flatten to show properly
467 std::unique_ptr<LIB_SYMBOL> flattenedSym;
468
469 if( sym->IsDerived() || !sym->GetParentName().IsEmpty() )
470 {
471 try
472 {
473 flattenedSym = sym->Flatten();
474 m_preview->DisplayPart( flattenedSym.get(), selectedUnit );
475 }
476 catch( const IO_ERROR& )
477 {
478 m_preview->DisplayPart( sym, selectedUnit );
479 }
480 }
481 else
482 {
483 m_preview->DisplayPart( sym, selectedUnit );
484 }
485}
486
487
489{
490 return m_manager.GetSymbolsToImport();
491}
492
493
495{
496 m_conflictResolutions.clear();
497
498 std::vector<wxString> conflicts = m_manager.GetConflicts();
499
500 if( conflicts.empty() )
501 return true;
502
503 wxDialog dlg( this, wxID_ANY, _( "Resolve Import Conflicts" ),
504 wxDefaultPosition, wxSize( 500, 400 ),
505 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );
506
507 wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
508
509 wxStaticText* label = new wxStaticText( &dlg, wxID_ANY,
510 _( "The following symbols already exist in the destination library. "
511 "Choose how to handle each conflict:" ) );
512 label->Wrap( 450 );
513 mainSizer->Add( label, 0, wxALL | wxEXPAND, 10 );
514
515 wxDataViewListCtrl* conflictList = new wxDataViewListCtrl( &dlg, wxID_ANY );
516 conflictList->AppendTextColumn( _( "Symbol" ), wxDATAVIEW_CELL_INERT, 200 );
517 conflictList->AppendTextColumn( _( "Action" ), wxDATAVIEW_CELL_EDITABLE, 100 );
518
519 for( const wxString& name : conflicts )
520 {
521 wxVector<wxVariant> data;
522 data.push_back( wxVariant( name ) );
523 data.push_back( wxVariant( _( "Overwrite" ) ) );
524 conflictList->AppendItem( data );
525
527 }
528
529 mainSizer->Add( conflictList, 1, wxALL | wxEXPAND, 10 );
530
531 wxBoxSizer* actionSizer = new wxBoxSizer( wxHORIZONTAL );
532 wxButton* skipAllBtn = new wxButton( &dlg, wxID_ANY, _( "Skip All" ) );
533 wxButton* overwriteAllBtn = new wxButton( &dlg, wxID_ANY, _( "Overwrite All" ) );
534 actionSizer->Add( skipAllBtn, 0, wxRIGHT, 5 );
535 actionSizer->Add( overwriteAllBtn, 0 );
536 mainSizer->Add( actionSizer, 0, wxLEFT | wxRIGHT | wxBOTTOM, 10 );
537
538 wxStdDialogButtonSizer* btnSizer = new wxStdDialogButtonSizer();
539 btnSizer->AddButton( new wxButton( &dlg, wxID_OK, _( "Import" ) ) );
540 btnSizer->AddButton( new wxButton( &dlg, wxID_CANCEL ) );
541 btnSizer->Realize();
542 mainSizer->Add( btnSizer, 0, wxALL | wxEXPAND, 10 );
543
544 dlg.SetSizer( mainSizer );
545
546 skipAllBtn->Bind( wxEVT_BUTTON, [&]( wxCommandEvent& ) {
547 for( size_t i = 0; i < conflicts.size(); i++ )
548 {
549 conflictList->SetTextValue( _( "Skip" ), i, 1 );
551 }
552 } );
553
554 overwriteAllBtn->Bind( wxEVT_BUTTON, [&]( wxCommandEvent& ) {
555 for( size_t i = 0; i < conflicts.size(); i++ )
556 {
557 conflictList->SetTextValue( _( "Overwrite" ), i, 1 );
559 }
560 } );
561
562 conflictList->Bind( wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [&]( wxDataViewEvent& evt ) {
563 int row = conflictList->ItemToRow( evt.GetItem() );
564
565 if( row >= 0 && row < (int) conflicts.size() )
566 {
567 wxString action = conflictList->GetTextValue( row, 1 );
568 m_conflictResolutions[conflicts[row]] =
569 ( action == _( "Skip" ) ) ? CONFLICT_RESOLUTION::SKIP
571 }
572 } );
573
574 return dlg.ShowModal() == wxID_OK;
575}
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:104
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:82
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:83
bool IsPower() const override
bool IsDerived() const
Definition lib_symbol.h:203
const wxString & GetParentName() const
Definition lib_symbol.h:854
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).
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:202
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:177
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.