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