KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_editor.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 (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2008 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <pgm_base.h>
27#include <clipboard.h>
28#include <confirm.h>
29#include <kidialog.h>
30#include <kiway.h>
31#include <widgets/wx_infobar.h>
33#include <symbol_edit_frame.h>
34#include <symbol_library.h>
35#include <template_fieldnames.h>
37#include <symbol_lib_table.h>
39#include <symbol_tree_pane.h>
41#include <widgets/lib_tree.h>
45#include <eda_list_dialog.h>
46#include <wx/clipbrd.h>
47#include <wx/filedlg.h>
48#include <wx/log.h>
49#include <project_sch.h>
50#include <string_utils.h>
51#include "symbol_saveas_type.h"
54
55
57{
58 wxString title;
59
61 {
63 title = wxT( "*" );
64
65 title += m_reference;
66 title += wxS( " " ) + _( "[from schematic]" );
67 }
68 else if( GetCurSymbol() )
69 {
71 title = wxT( "*" );
72
73 title += UnescapeString( GetCurSymbol()->GetLibId().Format() );
74
75 if( m_libMgr && m_libMgr->LibraryExists( GetCurLib() ) && m_libMgr->IsLibraryReadOnly( GetCurLib() ) )
76 title += wxS( " " ) + _( "[Read Only Library]" );
77 }
78 else
79 {
80 title = _( "[no symbol loaded]" );
81 }
82
83 title += wxT( " \u2014 " ) + _( "Symbol Editor" );
84 SetTitle( title );
85}
86
87
88void SYMBOL_EDIT_FRAME::SelectActiveLibrary( const wxString& aLibrary )
89{
90 wxString selectedLib = aLibrary;
91
92 if( selectedLib.empty() )
93 selectedLib = SelectLibrary( _( "Select Symbol Library" ), _( "Library:" ) );
94
95 if( !selectedLib.empty() )
96 SetCurLib( selectedLib );
97
99}
100
101
103{
104 if( GetCurSymbol() )
105 {
107 {
108 SCH_EDIT_FRAME* schframe = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, false );
109
110 if( !schframe ) // happens when the schematic editor has been closed
111 {
112 DisplayErrorMessage( this, _( "No schematic currently open." ) );
113 return false;
114 }
115 else
116 {
118 GetScreen()->SetContentModified( false );
119 return true;
120 }
121 }
122 else
123 {
124 const wxString& libName = GetCurSymbol()->GetLibId().GetLibNickname();
125
126 if( m_libMgr->IsLibraryReadOnly( libName ) )
127 {
128 wxString msg = wxString::Format( _( "Symbol library '%s' is not writable." ),
129 libName );
130 wxString msg2 = _( "You must save to a different location." );
131
132 if( OKOrCancelDialog( this, _( "Warning" ), msg, msg2 ) == wxID_OK )
133 return saveLibrary( libName, true );
134 }
135 else
136 {
137 return saveLibrary( libName, false );
138 }
139 }
140 }
141
142 return false;
143}
144
145
146bool SYMBOL_EDIT_FRAME::LoadSymbol( const LIB_ID& aLibId, int aUnit, int aBodyStyle )
147{
148 LIB_ID libId = aLibId;
149
150 // Some libraries can't be edited, so load the underlying chosen symbol
151 if( SYMBOL_LIB_TABLE_ROW* lib = m_libMgr->GetLibrary( aLibId.GetLibNickname() ) )
152 {
153 if( lib->SchLibType() == SCH_IO_MGR::SCH_DATABASE
154 || lib->SchLibType() == SCH_IO_MGR::SCH_CADSTAR_ARCHIVE
155 || lib->SchLibType() == SCH_IO_MGR::SCH_HTTP )
156
157 {
158 try
159 {
160 LIB_SYMBOL* readOnlySym = PROJECT_SCH::SchSymbolLibTable( &Prj() )->LoadSymbol( aLibId );
161
162 if( readOnlySym && readOnlySym->GetSourceLibId().IsValid() )
163 libId = readOnlySym->GetSourceLibId();
164 }
165 catch( const IO_ERROR& ioe )
166 {
167 wxString msg;
168
169 msg.Printf( _( "Error loading symbol %s from library '%s'." ),
170 aLibId.GetUniStringLibId(), aLibId.GetUniStringLibItemName() );
171 DisplayErrorMessage( this, msg, ioe.What() );
172 return false;
173 }
174 }
175 }
176
178 && GetCurSymbol()->GetLibId() == libId
179 && GetUnit() == aUnit
180 && GetBodyStyle() == aBodyStyle )
181 {
182 return true;
183 }
184
186 {
187 if( !HandleUnsavedChanges( this, _( "The current symbol has been modified. Save changes?" ),
188 [&]() -> bool
189 {
190 return saveCurrentSymbol();
191 } ) )
192 {
193 return false;
194 }
195 }
196
198
199 if( LoadSymbolFromCurrentLib( libId.GetLibItemName(), aUnit, aBodyStyle ) )
200 {
201 m_treePane->GetLibTree()->SelectLibId( libId );
202 m_treePane->GetLibTree()->ExpandLibId( libId );
203
204 m_centerItemOnIdle = libId;
205 Bind( wxEVT_IDLE, &SYMBOL_EDIT_FRAME::centerItemIdleHandler, this );
206 setSymWatcher( &libId );
207
208 return true;
209 }
210
211 return false;
212}
213
214
216{
217 m_treePane->GetLibTree()->CenterLibId( m_centerItemOnIdle );
218 Unbind( wxEVT_IDLE, &SYMBOL_EDIT_FRAME::centerItemIdleHandler, this );
219}
220
221
222bool SYMBOL_EDIT_FRAME::LoadSymbolFromCurrentLib( const wxString& aSymbolName, int aUnit,
223 int aBodyStyle )
224{
225 LIB_SYMBOL* symbol = nullptr;
226
227 try
228 {
229 symbol = PROJECT_SCH::SchSymbolLibTable( &Prj() )->LoadSymbol( GetCurLib(), aSymbolName );
230 }
231 catch( const IO_ERROR& ioe )
232 {
233 wxString msg;
234
235 msg.Printf( _( "Error loading symbol %s from library '%s'." ),
236 aSymbolName,
237 GetCurLib() );
238 DisplayErrorMessage( this, msg, ioe.What() );
239 return false;
240 }
241
242 if( !symbol || !LoadOneLibrarySymbolAux( symbol, GetCurLib(), aUnit, aBodyStyle ) )
243 return false;
244
245 // Enable synchronized pin edit mode for symbols with interchangeable units
247
250
252
253 return true;
254}
255
256
257bool SYMBOL_EDIT_FRAME::LoadOneLibrarySymbolAux( LIB_SYMBOL* aEntry, const wxString& aLibrary,
258 int aUnit, int aBodyStyle )
259{
260 bool rebuildMenuAndToolbar = false;
261
262 if( !aEntry || aLibrary.empty() )
263 return false;
264
265 if( aEntry->GetName().IsEmpty() )
266 {
267 wxLogWarning( "Symbol in library '%s' has empty name field.", aLibrary );
268 return false;
269 }
270
272
273 // Symbols from the schematic are edited in place and not managed by the library manager.
275 {
276 delete m_symbol;
277 m_symbol = nullptr;
278
279 SCH_SCREEN* screen = GetScreen();
280 delete screen;
283 rebuildMenuAndToolbar = true;
284 }
285
286 LIB_SYMBOL* lib_symbol = m_libMgr->GetBufferedSymbol( aEntry->GetName(), aLibrary );
287 wxCHECK( lib_symbol, false );
288
289 m_unit = aUnit > 0 ? aUnit : 1;
290 m_bodyStyle = aBodyStyle > 0 ? aBodyStyle : 1;
291
292 // The buffered screen for the symbol
293 SCH_SCREEN* symbol_screen = m_libMgr->GetScreen( lib_symbol->GetName(), aLibrary );
294
295 SetScreen( symbol_screen );
296 SetCurSymbol( new LIB_SYMBOL( *lib_symbol ), true );
297 SetCurLib( aLibrary );
298
299 if( rebuildMenuAndToolbar )
300 {
303 GetInfoBar()->Dismiss();
304 }
305
306 UpdateTitle();
308
310
311 if( !IsSymbolFromSchematic() )
312 {
313 LIB_ID libId = GetCurSymbol()->GetLibId();
314 setSymWatcher( &libId );
315 }
316
317 // Let tools add things to the view if necessary
318 if( m_toolManager )
320
322
323 // Display the document information based on the entry selected just in
324 // case the entry is an alias.
326 Refresh();
327
328 return true;
329}
330
331
333{
334 saveAllLibraries( false );
335 m_treePane->GetLibTree()->RefreshLibTree();
336}
337
338
339void SYMBOL_EDIT_FRAME::CreateNewSymbol( const wxString& aInheritFrom )
340{
342
343 wxString lib = getTargetLib();
344
345 if( !m_libMgr->LibraryExists( lib ) )
346 {
347 lib = SelectLibrary( _( "New Symbol" ), _( "Create symbol in library:" ) );
348
349 if( !m_libMgr->LibraryExists( lib ) )
350 return;
351 }
352
353 const auto validator =
354 [&]( wxString newName ) -> bool
355 {
356 if( newName.IsEmpty() )
357 {
358 wxMessageBox( _( "Symbol must have a name." ) );
359 return false;
360 }
361
362 if( !lib.empty() && m_libMgr->SymbolNameInUse( newName, lib ) )
363 {
364 wxString msg;
365
366 msg.Printf( _( "Symbol '%s' already exists in library '%s'." ),
367 UnescapeString( newName ),
368 lib );
369
370 KIDIALOG errorDlg( this, msg, _( "Confirmation" ),
371 wxOK | wxCANCEL | wxICON_WARNING );
372
373 errorDlg.SetOKLabel( _( "Overwrite" ) );
374
375 return errorDlg.ShowModal() == wxID_OK;
376 }
377
378 return true;
379 };
380
381 wxArrayString symbolNamesInLib;
382 m_libMgr->GetSymbolNames( lib, symbolNamesInLib );
383
384 DIALOG_LIB_NEW_SYMBOL dlg( this, symbolNamesInLib, aInheritFrom, validator );
385
386 dlg.SetMinSize( dlg.GetSize() );
387
388 if( dlg.ShowModal() == wxID_CANCEL )
389 return;
390
392
393 props.name = dlg.GetName();
395 props.reference = dlg.GetReference();
396 props.unitCount = dlg.GetUnitCount();
397 props.pinNameInside = dlg.GetPinNameInside();
399 props.powerSymbol = dlg.GetPowerSymbol();
400 props.showPinNumber = dlg.GetShowPinNumber();
401 props.showPinName = dlg.GetShowPinName();
403 props.includeInBom = dlg.GetIncludeInBom();
404 props.includeOnBoard = dlg.GetIncludeOnBoard();
406 props.keepFootprint = dlg.GetKeepFootprint();
407 props.keepDatasheet = dlg.GetKeepDatasheet();
410
411 m_libMgr->CreateNewSymbol( lib, props );
412 SyncLibraries( false );
413 LoadSymbol( props.name, lib, 1 );
414}
415
416
418{
419 wxString libName;
420
421 if( IsLibraryTreeShown() )
423
424 if( libName.empty() )
425 {
427 }
428 else if( m_libMgr->IsLibraryReadOnly( libName ) )
429 {
430 wxString msg = wxString::Format( _( "Symbol library '%s' is not writable." ),
431 libName );
432 wxString msg2 = _( "You must save to a different location." );
433
434 if( OKOrCancelDialog( this, _( "Warning" ), msg, msg2 ) == wxID_OK )
435 saveLibrary( libName, true );
436 }
437 else
438 {
439 saveLibrary( libName, false );
440 }
441
442 if( IsLibraryTreeShown() )
443 m_treePane->GetLibTree()->RefreshLibTree();
444
445 UpdateTitle();
446}
447
448
450{
451 const wxString& libName = GetTargetLibId().GetLibNickname();
452
453 if( !libName.IsEmpty() )
454 {
455 saveLibrary( libName, true );
456 m_treePane->GetLibTree()->RefreshLibTree();
457 }
458}
459
460
462{
463 saveSymbolCopyAs( aOpenCopy );
464
465 m_treePane->GetLibTree()->RefreshLibTree();
466}
467
468
478static std::vector<LIB_SYMBOL_SPTR> GetParentChain( const LIB_SYMBOL& aSymbol, bool aIncludeLeaf = true )
479{
480 std::vector<LIB_SYMBOL_SPTR> chain;
481 LIB_SYMBOL_SPTR sym = aSymbol.SharedPtr();
482
483 if( aIncludeLeaf )
484 chain.push_back( sym );
485
486 while( sym->IsDerived() )
487 {
488 LIB_SYMBOL_SPTR parent = sym->GetParent().lock();
489 chain.push_back( parent );
490 sym = parent;
491 }
492
493 return chain;
494}
495
496
506static std::pair<bool, bool> CheckSavingIntoOwnInheritance( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr,
507 LIB_SYMBOL& aSymbol,
508 const wxString& aNewSymbolName,
509 const wxString& aNewLibraryName )
510{
511 const wxString& oldLibraryName = aSymbol.GetLibId().GetLibNickname();
512
513 // Cannot be intersecting if in different libs
514 if( aNewLibraryName != oldLibraryName )
515 return { false, false };
516
517 // Or if the target symbol doesn't exist
518 if( !aLibMgr.SymbolNameInUse( aNewSymbolName, aNewLibraryName ) )
519 return { false, false };
520
521 bool inAncestry = false;
522 bool inDescendents = false;
523
524 {
525 const std::vector<LIB_SYMBOL_SPTR> parentChainFromUs = GetParentChain( aSymbol, true );
526
527 // Ignore the leaf symbol (0) - that must match
528 for( size_t i = 1; i < parentChainFromUs.size(); ++i )
529 {
530 // Attempting to overwrite a symbol in the parental chain
531 if( parentChainFromUs[i]->GetName() == aNewSymbolName )
532 {
533 inAncestry = true;
534 break;
535 }
536 }
537 }
538
539 {
540 LIB_SYMBOL* targetSymbol = aLibMgr.GetSymbol( aNewSymbolName, aNewLibraryName );
541 const std::vector<LIB_SYMBOL_SPTR> parentChainFromTarget = GetParentChain( *targetSymbol, true );
542 const wxString oldSymbolName = aSymbol.GetName();
543
544 // Ignore the leaf symbol - it'll match if we're saving the symbol
545 // to the same name, and that would be OK
546 for( size_t i = 1; i < parentChainFromTarget.size(); ++i )
547 {
548 if( parentChainFromTarget[i]->GetName() == oldSymbolName )
549 {
550 inDescendents = true;
551 break;
552 }
553 }
554 }
555
556 return { inAncestry, inDescendents };
557}
558
559
567static std::vector<wxString> CheckForParentalChainConflicts( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr,
568 LIB_SYMBOL& aSymbol,
569 bool aFlattenSymbol,
570 const wxString& newSymbolName,
571 const wxString& newLibraryName )
572{
573 std::vector<wxString> conflicts;
574 const wxString& oldLibraryName = aSymbol.GetLibId().GetLibNickname();
575
576 if( newLibraryName == oldLibraryName || aFlattenSymbol )
577 {
578 // Saving into the same library - the only conflict could be the symbol itself
579 // Different library and flattening - ditto
580 if( aLibMgr.SymbolNameInUse( newSymbolName, newLibraryName ) )
581 conflicts.push_back( newSymbolName );
582 }
583 else
584 {
585 // In a different library with parents - check the whole chain
586 const std::vector<LIB_SYMBOL_SPTR> parentChain = GetParentChain( aSymbol, true );
587
588 for( size_t i = 0; i < parentChain.size(); ++i )
589 {
590 if( i == 0 )
591 {
592 // This is the leaf symbol which the user actually named
593 if( aLibMgr.SymbolNameInUse( newSymbolName, newLibraryName ) )
594 conflicts.push_back( newSymbolName );
595 }
596 else
597 {
598 LIB_SYMBOL_SPTR chainSymbol = parentChain[i];
599
600 if( aLibMgr.SymbolNameInUse( chainSymbol->GetName(), newLibraryName ) )
601 conflicts.push_back( chainSymbol->GetName() );
602 }
603 }
604 }
605
606 return conflicts;
607}
608
609
617{
618public:
620 {
621 // Just overwrite any existing symbols in the target library
623 // Add a suffix until we find a name that doesn't conflict
625 // Could have a mode that asks for every one, be then we'll need a fancier
626 // SAVE_SYMBOL_AS_DIALOG subdialog with Overwrite/Rename/Prompt/Cancel
627 // PROMPT
628 };
629
630 SYMBOL_SAVE_AS_HANDLER( LIB_SYMBOL_LIBRARY_MANAGER& aLibMgr, CONFLICT_STRATEGY aStrategy, bool aValueFollowsName ) :
631 m_libMgr( aLibMgr ),
632 m_strategy( aStrategy ),
633 m_valueFollowsName( aValueFollowsName )
634 {
635 }
636
637 bool DoSave( LIB_SYMBOL& symbol, const wxString& aNewSymName, const wxString& aNewLibName, bool aFlattenSymbol )
638 {
639 std::unique_ptr<LIB_SYMBOL> flattenedSymbol; // for ownership
640 std::vector<LIB_SYMBOL_SPTR> parentChain;
641
642 const bool sameLib = aNewLibName == symbol.GetLibId().GetLibNickname().wx_str();
643
644 if( aFlattenSymbol )
645 {
646 // If we're not copying parent symbols, we need to flatten the symbol
647 // and only save that.
648 flattenedSymbol = symbol.Flatten();
649 wxCHECK( flattenedSymbol, false );
650
651 parentChain.push_back( flattenedSymbol->SharedPtr() );
652 }
653 else if( sameLib )
654 {
655 // If we're saving into the same library, we don't need to check the parental chain
656 // because we can just keep the same parent symbol
657 parentChain.push_back( symbol.SharedPtr() );
658 }
659 else
660 {
661 // Need to copy all parent symbols
662 parentChain = GetParentChain( symbol, true );
663 }
664
665 std::vector<wxString> newNames;
666
667 // Iterate backwards (i.e. from the root down)
668 for( int i = (int) parentChain.size() - 1; i >= 0; --i )
669 {
670 LIB_SYMBOL_SPTR& oldSymbol = parentChain[i];
671
672 LIB_SYMBOL new_symbol( *oldSymbol );
673
674 wxString newName;
675 if( i == 0 )
676 {
677 // This is the leaf symbol which the user actually named
678 newName = aNewSymName;
679 }
680 else
681 {
682 // Somewhere in the inheritance chain, use the conflict resolution strategy
683 newName = oldSymbol->GetName();
684 }
685
686 newName = resolveConflict( newName, aNewLibName );
687 new_symbol.SetName( newName );
688
690 new_symbol.GetValueField().SetText( newName );
691
692 if( i == (int) parentChain.size() - 1 )
693 {
694 // This is the root symbol
695 // Nothing extra to do, it's just a simple symbol with no parents
696 }
697 else
698 {
699 // Get the buffered new copy in the new library (with the name we gave it)
700 LIB_SYMBOL* newParent = m_libMgr.GetSymbol( newNames.back(), aNewLibName );
701
702 // We should have stored this already, why didn't we get it back?
703 wxASSERT( newParent );
704 new_symbol.SetParent( newParent );
705 }
706
707 newNames.push_back( newName );
708 m_libMgr.UpdateSymbol( &new_symbol, aNewLibName );
709 }
710
711 return true;
712 }
713
714private:
715 wxString resolveConflict( const wxString& proposed, const wxString& aNewLibName ) const
716 {
717 switch( m_strategy )
718 {
720 {
721 // In an overwrite strategy, we don't care about conflicts
722 return proposed;
723 }
725 {
726 // In a rename strategy, we need to find a name that doesn't conflict
727 int suffix = 1;
728
729 while( true )
730 {
731 wxString newName = wxString::Format( "%s_%d", proposed, suffix );
732
733 if( !m_libMgr.SymbolNameInUse( newName, aNewLibName ) )
734 return newName;
735
736 ++suffix;
737 }
738 break;
739 }
740 // No default
741 }
742
743 wxFAIL_MSG( "Invalid conflict strategy" );
744 return "";
745 }
746
750};
751
752
759
760
762{
763public:
764 using SymLibNameValidator = std::function<int( const wxString& libName, const wxString& symbolName )>;
765
773
775 PARAMS& aParams,
776 SymLibNameValidator aValidator,
777 const std::vector<wxString>& aParentSymbolNames ) :
778 EDA_LIST_DIALOG( aParent, _( "Save Symbol As" ), false ),
779 m_validator( std::move( aValidator ) ),
780 m_params( aParams )
781 {
783 std::vector<wxString> libNicknames = tbl->GetLogicalLibs();
784 wxArrayString headers;
785 std::vector<wxArrayString> itemsToDisplay;
786
787 if( aParentSymbolNames.size() )
788 {
789 // This is a little trick to word - when saving to another library, "copy parents" makes sense,
790 // but when in the same library, the parents will be untouched in any case.
791 const wxString aParentNames = AccumulateDescriptions( aParentSymbolNames );
793 wxString::Format( "Flatten/remove symbol inheritance (current parent symbols: %s)", aParentNames ),
794 &m_params.m_FlattenSymbol );
795 }
796
797 aParent->GetLibraryItemsForListDialog( headers, itemsToDisplay );
798 initDialog( headers, itemsToDisplay, m_params.m_LibraryName );
799
800 SetListLabel( _( "Save in library:" ) );
801 SetOKLabel( _( "Save" ) );
802
803 wxBoxSizer* bNameSizer = new wxBoxSizer( wxHORIZONTAL );
804
805 wxStaticText* label = new wxStaticText( this, wxID_ANY, _( "Name:" ) );
806 bNameSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxLEFT, 5 );
807
808 m_symbolNameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString );
809 bNameSizer->Add( m_symbolNameCtrl, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
810
811 wxButton* newLibraryButton = new wxButton( this, ID_MAKE_NEW_LIBRARY, _( "New Library..." ) );
812 m_ButtonsSizer->Prepend( 80, 20 );
813 m_ButtonsSizer->Prepend( newLibraryButton, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT, 10 );
814
815 GetSizer()->Prepend( bNameSizer, 0, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5 );
816
817 Bind( wxEVT_BUTTON,
818 [this]( wxCommandEvent& )
819 {
820 EndModal( ID_MAKE_NEW_LIBRARY );
822
823 // Move nameTextCtrl to the head of the tab-order
824 if( GetChildren().DeleteObject( m_symbolNameCtrl ) )
825 GetChildren().Insert( m_symbolNameCtrl );
826
828
830
831 Layout();
832 GetSizer()->Fit( this );
833
834 Centre();
835 }
836
837protected:
838 wxString getSymbolName() const
839 {
840 wxString symbolName = m_symbolNameCtrl->GetValue();
841 symbolName.Trim( true );
842 symbolName.Trim( false );
843 symbolName.Replace( " ", "_" );
844 return EscapeString( symbolName, CTX_LIBID );
845 }
846
847 bool TransferDataToWindow() override
848 {
849 m_symbolNameCtrl->SetValue( UnescapeString( m_params.m_SymbolName ) );
850 return true;
851 }
852
854 {
855 // This updates m_params.m_FlattenSymbol
856 // Do this now, so the validator can use it
858
859 m_params.m_SymbolName = getSymbolName();
860 m_params.m_LibraryName = GetTextSelection();
861
862 int ret = m_validator( m_params.m_LibraryName, m_params.m_SymbolName );
863
864 if( ret == wxID_CANCEL )
865 return false;
866
867 if( ret == ID_OVERWRITE_CONFLICTS )
869 else if( ret == ID_RENAME_CONFLICTS )
871
872 return true;
873 }
874
875private:
876 wxTextCtrl* m_symbolNameCtrl;
879};
880
881
883{
884 LIB_SYMBOL* symbol = getTargetSymbol();
885
886 if( !symbol )
887 return;
888
889 LIB_ID old_lib_id = symbol->GetLibId();
890 wxString symbolName = old_lib_id.GetLibItemName();
891 wxString libraryName = old_lib_id.GetLibNickname();
892 bool valueFollowsName = symbol->GetValueField().GetText() == symbolName;
893 wxString msg;
894 bool done = false;
895 bool flattenSymbol = false;
896
897 // This is the function that will be called when the user clicks OK in the dialog and checks
898 // if the proposed name has problems, and asks for clarification.
899 const auto dialogValidatorFunc =
900 [&]( const wxString& newLib, const wxString& newName ) -> int
901 {
902 if( newLib.IsEmpty() )
903 {
904 wxMessageBox( _( "A library must be specified." ) );
905 return wxID_CANCEL;
906 }
907
908 if( newName.IsEmpty() )
909 {
910 wxMessageBox( _( "Symbol must have a name." ) );
911 return wxID_CANCEL;
912 }
913
914 if( m_libMgr->IsLibraryReadOnly( newLib ) )
915 {
916 msg = wxString::Format( _( "Library '%s' is read-only. Choose a "
917 "different library to save the symbol '%s' to." ),
918 newLib,
919 UnescapeString( newName ) );
920 wxMessageBox( msg );
921 return wxID_CANCEL;
922 }
923
928 const auto& [inAncestry, inDescendents] = CheckSavingIntoOwnInheritance( *m_libMgr, *symbol,
929 newName, newLib );
930
931 if( inAncestry )
932 {
933 msg = wxString::Format( _( "Symbol '%s' cannot replace another symbol '%s' "
934 "that it descends from" ),
935 symbolName,
936 UnescapeString( newName ) );
937 wxMessageBox( msg );
938 return wxID_CANCEL;
939 }
940
941 if( inDescendents )
942 {
943 msg = wxString::Format( _( "Symbol '%s' cannot replace another symbol '%s' "
944 "that is a descendent of it." ),
945 symbolName,
946 UnescapeString( newName ) );
947 wxMessageBox( msg );
948 return wxID_CANCEL;
949 }
950
951 const std::vector<wxString> conflicts =
952 CheckForParentalChainConflicts( *m_libMgr, *symbol, flattenSymbol, newName, newLib );
953
954 if( conflicts.size() == 1 && conflicts.front() == newName )
955 {
956 // The simplest case is when the symbol itself has a conflict
957 msg = wxString::Format( _( "Symbol '%s' already exists in library '%s'. "
958 "Do you want to overwrite it?" ),
959 UnescapeString( newName ),
960 newLib );
961
962 KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
963 errorDlg.SetOKLabel( _( "Overwrite" ) );
964
965 return errorDlg.ShowModal() == wxID_OK ? ID_OVERWRITE_CONFLICTS : (int) wxID_CANCEL;
966 }
967 else if( !conflicts.empty() )
968 {
969 // If there are conflicts in the parental chain, we need to ask the user
970 // if they want to overwrite all of them.
971 // A more complex UI might allow the user to re-parent the symbol to an
972 // existing symbol in the target lib, or rename all the parents somehow.
973 msg = wxString::Format( _( "The following symbols in the inheritance chain of "
974 "'%s' already exist in library '%s':\n" ),
975 UnescapeString( symbolName ),
976 newLib );
977
978 for( const wxString& conflict : conflicts )
979 msg += wxString::Format( " %s\n", conflict );
980
981 msg += _( "\nDo you want to overwrite all of them, or rename the new symbols?" );
982
983 KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxYES_NO | wxCANCEL | wxICON_WARNING );
984 errorDlg.SetYesNoCancelLabels( _( "Overwrite All" ), _( "Rename All" ), _( "Cancel" ) );
985
986 switch( errorDlg.ShowModal() )
987 {
988 case wxID_YES: return ID_OVERWRITE_CONFLICTS;
989 case wxID_NO: return ID_RENAME_CONFLICTS;
990 default: return wxID_CANCEL;
991 }
992 }
993
994 return wxID_OK;
995 };
996
998
999 std::vector<wxString> parentSymbolNames;
1000 if( symbol->IsDerived() )
1001 {
1002 // The parents are everything but the leaf symbol
1003 std::vector<std::shared_ptr<LIB_SYMBOL>> parentChain = GetParentChain( *symbol, false );
1004
1005 for( const auto& parent : parentChain )
1006 parentSymbolNames.push_back( parent->GetName() );
1007 }
1008
1010 symbolName,
1011 libraryName,
1012 flattenSymbol,
1013 strategy,
1014 };
1015
1016 // Keep asking the user for a new name until they give a valid one or cancel the operation
1017 while( !done )
1018 {
1019 SAVE_SYMBOL_AS_DIALOG dlg( this, params, dialogValidatorFunc, parentSymbolNames );
1020
1021 int ret = dlg.ShowModal();
1022
1023 switch( ret )
1024 {
1025 case wxID_CANCEL:
1026 return;
1027
1028 case wxID_OK: // No conflicts
1031 {
1032 done = true;
1033 break;
1034 }
1036 {
1037 wxFileName newLibrary( AddLibraryFile( true ) );
1038 params.m_LibraryName = newLibrary.GetName();
1039
1040 // Go round again to ask for the symbol name
1041 break;
1042 }
1043
1044 default:
1045 break;
1046 }
1047 }
1048
1049 SYMBOL_SAVE_AS_HANDLER saver( *m_libMgr, params.m_ConflictStrategy, valueFollowsName );
1050
1051 saver.DoSave( *symbol, params.m_SymbolName, params.m_LibraryName, params.m_FlattenSymbol );
1052
1053 SyncLibraries( false );
1054
1055 if( aOpenCopy )
1056 LoadSymbol( params.m_SymbolName, params.m_LibraryName, 1 );
1057}
1058
1059
1061{
1062 wxString msg;
1063 LIB_SYMBOL* symbol = getTargetSymbol();
1064
1065 if( !symbol )
1066 {
1067 ShowInfoBarError( _( "There is no symbol selected to save." ) );
1068 return;
1069 }
1070
1071 wxFileName fn;
1072
1073 fn.SetName( symbol->GetName().Lower() );
1075
1076 wxFileDialog dlg( this, _( "Export Symbol" ), m_mruPath, fn.GetFullName(),
1078
1079 if( dlg.ShowModal() == wxID_CANCEL )
1080 return;
1081
1083
1084 fn = dlg.GetPath();
1085 fn.MakeAbsolute();
1086
1087 wxString libraryName;
1088 std::unique_ptr<LIB_SYMBOL> flattenedSymbol = symbol->Flatten();
1089
1090 for( const wxString& candidate : m_libMgr->GetLibraryNames() )
1091 {
1092 if( m_libMgr->GetLibrary( candidate )->GetFullURI( true ) == fn.GetFullPath() )
1093 libraryName = candidate;
1094 }
1095
1096 if( !libraryName.IsEmpty() )
1097 {
1098 SYMBOL_SAVE_AS_HANDLER saver( *m_libMgr, strategy, false );
1099
1100 if( m_libMgr->IsLibraryReadOnly( libraryName ) )
1101 {
1102 msg = wxString::Format( _( "Library '%s' is read-only." ), libraryName );
1103 DisplayError( this, msg );
1104 return;
1105 }
1106
1107 if( m_libMgr->SymbolNameInUse( symbol->GetName(), libraryName ) )
1108 {
1109 msg = wxString::Format( _( "Symbol '%s' already exists in library '%s'." ),
1110 symbol->GetName(), libraryName );
1111
1112 KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
1113 errorDlg.SetOKLabel( _( "Overwrite" ) );
1114 errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
1115
1116 if( errorDlg.ShowModal() == wxID_CANCEL )
1117 return;
1118 }
1119
1120 saver.DoSave( *flattenedSymbol, symbol->GetName(), libraryName, false );
1121
1122 SyncLibraries( false );
1123 return;
1124 }
1125
1126 LIB_SYMBOL* old_symbol = nullptr;
1127 SCH_IO_MGR::SCH_FILE_T pluginType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
1128
1129 if( pluginType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1130 pluginType = SCH_IO_MGR::SCH_KICAD;
1131
1132 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( pluginType ) );
1133
1134 if( fn.FileExists() )
1135 {
1136 try
1137 {
1138 old_symbol = pi->LoadSymbol( fn.GetFullPath(), symbol->GetName() );
1139 }
1140 catch( const IO_ERROR& ioe )
1141 {
1142 msg.Printf( _( "Error occurred attempting to load symbol library file '%s'." ),
1143 fn.GetFullPath() );
1144 DisplayErrorMessage( this, msg, ioe.What() );
1145 return;
1146 }
1147
1148 if( old_symbol )
1149 {
1150 msg.Printf( _( "Symbol %s already exists in library '%s'." ),
1151 UnescapeString( symbol->GetName() ),
1152 fn.GetFullName() );
1153
1154 KIDIALOG errorDlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
1155 errorDlg.SetOKLabel( _( "Overwrite" ) );
1156 errorDlg.DoNotShowCheckbox( __FILE__, __LINE__ );
1157
1158 if( errorDlg.ShowModal() == wxID_CANCEL )
1159 return;
1160 }
1161 }
1162
1163 if( !fn.IsDirWritable() )
1164 {
1165 msg.Printf( _( "Insufficient permissions to save library '%s'." ), fn.GetFullPath() );
1166 DisplayError( this, msg );
1167 return;
1168 }
1169
1170 try
1171 {
1172 if( !fn.FileExists() )
1173 pi->CreateLibrary( fn.GetFullPath() );
1174
1175 // The flattened symbol is most likely what the user would want. As some point in
1176 // the future as more of the symbol library inheritance is implemented, this may have
1177 // to be changes to save symbols of inherited symbols.
1178 pi->SaveSymbol( fn.GetFullPath(), flattenedSymbol.release() );
1179 }
1180 catch( const IO_ERROR& ioe )
1181 {
1182 msg.Printf( _( "Failed to create symbol library file '%s'." ), fn.GetFullPath() );
1183 DisplayErrorMessage( this, msg, ioe.What() );
1184 msg.Printf( _( "Error creating symbol library '%s'." ), fn.GetFullName() );
1185 SetStatusText( msg );
1186 return;
1187 }
1188
1189 m_mruPath = fn.GetPath();
1190
1191 msg.Printf( _( "Symbol %s saved to library '%s'." ),
1192 UnescapeString( symbol->GetName() ),
1193 fn.GetFullPath() );
1194 SetStatusText( msg );
1195}
1196
1197
1199{
1200 wxCHECK( m_symbol, /* void */ );
1201
1202 wxString lib = GetCurLib();
1203
1204 if( !lib.IsEmpty() && aOldName && *aOldName != m_symbol->GetName() )
1205 {
1206 // Test the current library for name conflicts
1207 if( m_libMgr->SymbolNameInUse( m_symbol->GetName(), lib ) )
1208 {
1209 wxString msg = wxString::Format( _( "Symbol name '%s' already in use." ),
1210 UnescapeString( m_symbol->GetName() ) );
1211
1212 DisplayErrorMessage( this, msg );
1213 m_symbol->SetName( *aOldName );
1214 }
1215 else
1216 {
1217 m_libMgr->UpdateSymbolAfterRename( m_symbol, *aOldName, lib );
1218 }
1219
1220 // Reselect the renamed symbol
1221 m_treePane->GetLibTree()->SelectLibId( LIB_ID( lib, m_symbol->GetName() ) );
1222 }
1223
1225 UpdateTitle();
1226
1227 // N.B. The view needs to be rebuilt first as the Symbol Properties change may invalidate
1228 // the view pointers by rebuilting the field table
1229 RebuildView();
1231
1232 OnModify();
1233}
1234
1235
1237{
1238 std::vector<LIB_ID> toDelete = GetSelectedLibIds();
1239
1240 if( toDelete.empty() )
1241 toDelete.emplace_back( GetTargetLibId() );
1242
1243 for( LIB_ID& libId : toDelete )
1244 {
1245 if( m_libMgr->IsSymbolModified( libId.GetLibItemName(), libId.GetLibNickname() )
1246 && !IsOK( this, wxString::Format( _( "The symbol '%s' has been modified.\n"
1247 "Do you want to remove it from the library?" ),
1248 libId.GetUniStringLibItemName() ) ) )
1249 {
1250 continue;
1251 }
1252
1253 wxArrayString derived;
1254
1255 if( m_libMgr->GetDerivedSymbolNames( libId.GetLibItemName(), libId.GetLibNickname(), derived ) > 0 )
1256 {
1257 wxString msg = _( "Deleting a base symbol will delete all symbols derived from it.\n\n" );
1258
1259 msg += libId.GetLibItemName().wx_str() + _( " (base)\n" );
1260
1261 for( const wxString& name : derived )
1262 msg += name + wxT( "\n" );
1263
1264 KICAD_MESSAGE_DIALOG_BASE dlg( this, msg, _( "Warning" ), wxYES_NO | wxICON_WARNING | wxCENTER );
1265 dlg.SetExtendedMessage( wxT( " " ) );
1266 dlg.SetYesNoLabels( _( "Delete All Listed Symbols" ), _( "Cancel" ) );
1267
1268 if( dlg.ShowModal() == wxID_NO )
1269 continue;
1270 }
1271
1272 if( IsCurrentSymbol( libId ) )
1273 emptyScreen();
1274
1275 m_libMgr->RemoveSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
1276 }
1277
1278 m_treePane->GetLibTree()->RefreshLibTree();
1279}
1280
1281
1283{
1284 std::vector<LIB_ID> symbols;
1285
1286 if( GetTreeLIBIDs( symbols ) == 0 )
1287 return;
1288
1289 STRING_FORMATTER formatter;
1290
1291 for( LIB_ID& libId : symbols )
1292 {
1293 LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(),
1294 libId.GetLibNickname() );
1295
1296 if( !symbol )
1297 continue;
1298
1299 std::unique_ptr<LIB_SYMBOL> tmp = symbol->Flatten();
1300 SCH_IO_KICAD_SEXPR::FormatLibSymbol( tmp.get(), formatter );
1301 }
1302
1303 std::string prettyData = formatter.GetString();
1304 KICAD_FORMAT::Prettify( prettyData, true );
1305
1306 wxLogNull doNotLog; // disable logging of failed clipboard actions
1307
1308 auto clipboard = wxTheClipboard;
1309 wxClipboardLocker clipboardLock( clipboard );
1310
1311 if( !clipboardLock || !clipboard->IsOpened() )
1312 return;
1313
1314 auto data = new wxTextDataObject( wxString( prettyData.c_str(), wxConvUTF8 ) );
1315 clipboard->SetData( data );
1316
1317 clipboard->Flush();
1318}
1319
1320
1321void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
1322{
1323 LIB_ID libId = GetTargetLibId();
1324 wxString lib = libId.GetLibNickname();
1325
1326 if( !m_libMgr->LibraryExists( lib ) )
1327 return;
1328
1329 std::vector<LIB_SYMBOL*> newSymbols;
1330
1331 if( aFromClipboard )
1332 {
1333 std::string clipboardData = GetClipboardUTF8();
1334
1335 try
1336 {
1337 newSymbols = SCH_IO_KICAD_SEXPR::ParseLibSymbols( clipboardData, "Clipboard" );
1338 }
1339 catch( IO_ERROR& e )
1340 {
1341 wxLogMessage( wxS( "Can not paste: %s" ), e.Problem() );
1342 }
1343 }
1344 else if( LIB_SYMBOL* srcSymbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(), lib ) )
1345 {
1346 newSymbols.emplace_back( new LIB_SYMBOL( *srcSymbol ) );
1347
1348 // Derive from same parent.
1349 if( srcSymbol->IsDerived() )
1350 {
1351 if( std::shared_ptr<LIB_SYMBOL> srcParent = srcSymbol->GetParent().lock() )
1352 newSymbols.back()->SetParent( srcParent.get() );
1353 }
1354 }
1355
1356 if( newSymbols.empty() )
1357 return;
1358
1359 for( LIB_SYMBOL* symbol : newSymbols )
1360 {
1361 ensureUniqueName( symbol, lib );
1362 m_libMgr->UpdateSymbol( symbol, lib );
1363
1364 LoadOneLibrarySymbolAux( symbol, lib, GetUnit(), GetBodyStyle() );
1365 }
1366
1367 SyncLibraries( false );
1368 m_treePane->GetLibTree()->SelectLibId( LIB_ID( lib, newSymbols[0]->GetName() ) );
1369
1370 for( LIB_SYMBOL* symbol : newSymbols )
1371 delete symbol;
1372}
1373
1374
1375void SYMBOL_EDIT_FRAME::ensureUniqueName( LIB_SYMBOL* aSymbol, const wxString& aLibrary )
1376{
1377 if( aSymbol )
1378 {
1379 int i = 1;
1380 wxString newName = aSymbol->GetName();
1381
1382 // Append a number to the name until the name is unique in the library.
1383 while( m_libMgr->SymbolNameInUse( newName, aLibrary ) )
1384 newName.Printf( "%s_%d", aSymbol->GetName(), i++ );
1385
1386 aSymbol->SetName( newName );
1387 }
1388}
1389
1390
1391void SYMBOL_EDIT_FRAME::Revert( bool aConfirm )
1392{
1393 LIB_ID libId = GetTargetLibId();
1394 const wxString& libName = libId.GetLibNickname();
1395
1396 // Empty if this is the library itself that is selected.
1397 const wxString& symbolName = libId.GetLibItemName();
1398
1399 wxString msg = wxString::Format( _( "Revert '%s' to last version saved?" ),
1400 symbolName.IsEmpty() ? libName : symbolName );
1401
1402 if( aConfirm && !ConfirmRevertDialog( this, msg ) )
1403 return;
1404
1405 bool reload_currentSymbol = false;
1406 wxString curr_symbolName = symbolName;
1407
1408 if( GetCurSymbol() )
1409 {
1410 // the library itself is reverted: the current symbol will be reloaded only if it is
1411 // owned by this library
1412 if( symbolName.IsEmpty() )
1413 {
1414 LIB_ID curr_libId = GetCurSymbol()->GetLibId();
1415 reload_currentSymbol = libName == curr_libId.GetLibNickname().wx_str();
1416
1417 if( reload_currentSymbol )
1418 curr_symbolName = curr_libId.GetUniStringLibItemName();
1419 }
1420 else
1421 {
1422 reload_currentSymbol = IsCurrentSymbol( libId );
1423 }
1424 }
1425
1426 int unit = m_unit;
1427
1428 if( reload_currentSymbol )
1429 emptyScreen();
1430
1431 if( symbolName.IsEmpty() )
1432 {
1433 m_libMgr->RevertLibrary( libName );
1434 }
1435 else
1436 {
1437 libId = m_libMgr->RevertSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
1438
1439 m_treePane->GetLibTree()->SelectLibId( libId );
1440 m_libMgr->ClearSymbolModified( libId.GetLibItemName(), libId.GetLibNickname() );
1441 }
1442
1443 if( reload_currentSymbol && m_libMgr->SymbolExists( curr_symbolName, libName ) )
1444 LoadSymbol( curr_symbolName, libName, unit );
1445
1446 m_treePane->Refresh();
1447}
1448
1449
1451{
1452 wxCHECK_RET( m_libMgr, "Library manager object not created." );
1453
1454 Revert( false );
1455 m_libMgr->RevertAll();
1456}
1457
1458
1459void SYMBOL_EDIT_FRAME::LoadSymbol( const wxString& aAlias, const wxString& aLibrary, int aUnit )
1460{
1462 {
1463 if( !HandleUnsavedChanges( this, _( "The current symbol has been modified. Save changes?" ),
1464 [&]() -> bool
1465 {
1466 return saveCurrentSymbol();
1467 } ) )
1468 {
1469 return;
1470 }
1471 }
1472
1473 LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( aAlias, aLibrary );
1474
1475 if( !symbol )
1476 {
1477 DisplayError( this, wxString::Format( _( "Symbol %s not found in library '%s'." ),
1478 aAlias,
1479 aLibrary ) );
1480 m_treePane->GetLibTree()->RefreshLibTree();
1481 return;
1482 }
1483
1484 // Optimize default edit options for this symbol
1485 // Usually if units are locked, graphic items are specific to each unit
1486 // and if units are interchangeable, graphic items are common to units
1488 tools->SetDrawSpecificUnit( symbol->UnitsLocked() );
1489
1490 LoadOneLibrarySymbolAux( symbol, aLibrary, aUnit, 0 );
1491}
1492
1493
1494bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
1495{
1496 wxFileName fn;
1497 wxString msg;
1499 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD;
1500 PROJECT& prj = Prj();
1501
1503
1504 if( !aNewFile && ( aLibrary.empty() || !PROJECT_SCH::SchSymbolLibTable( &prj )->HasLibrary( aLibrary ) ) )
1505 {
1506 ShowInfoBarError( _( "No library specified." ) );
1507 return false;
1508 }
1509
1510 if( aNewFile )
1511 {
1512 SEARCH_STACK* search = PROJECT_SCH::SchSearchS( &prj );
1513
1514 // Get a new name for the library
1515 wxString default_path = prj.GetRString( PROJECT::SCH_LIB_PATH );
1516
1517 if( !default_path )
1518 default_path = search->LastVisitedPath();
1519
1520 fn.SetName( aLibrary );
1522
1523 wxString wildcards = FILEEXT::KiCadSymbolLibFileWildcard();
1524
1525 wxFileDialog dlg( this, wxString::Format( _( "Save Library '%s' As..." ), aLibrary ), default_path,
1526 fn.GetFullName(), wildcards, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1527
1528 SYMBOL_LIBRARY_SAVE_AS_FILEDLG_HOOK saveAsHook( type );
1529 dlg.SetCustomizeHook( saveAsHook );
1530
1531 if( dlg.ShowModal() == wxID_CANCEL )
1532 return false;
1533
1534 fn = dlg.GetPath();
1535
1536 prj.SetRString( PROJECT::SCH_LIB_PATH, fn.GetPath() );
1537
1538 if( fn.GetExt().IsEmpty() )
1540
1541 type = saveAsHook.GetOption();
1542 }
1543 else
1544 {
1545 fn = PROJECT_SCH::SchSymbolLibTable( &prj )->GetFullURI( aLibrary );
1547
1548 if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1549 fileType = SCH_IO_MGR::SCH_KICAD;
1550 }
1551
1552 // Verify the user has write privileges before attempting to save the library file.
1553 if( !aNewFile && m_libMgr->IsLibraryReadOnly( aLibrary ) )
1554 return false;
1555
1556 ClearMsgPanel();
1557
1558 // Copy .kicad_symb file to .bak.
1559 if( !backupFile( fn, "bak" ) )
1560 return false;
1561
1562 if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath(), fileType ) )
1563 {
1564 msg.Printf( _( "Failed to save changes to symbol library file '%s'." ),
1565 fn.GetFullPath() );
1566 DisplayErrorMessage( this, _( "Error Saving Library" ), msg );
1567 return false;
1568 }
1569
1570 if( !aNewFile )
1571 {
1572 m_libMgr->ClearLibraryModified( aLibrary );
1573
1574 // Update the library modification time so that we don't reload based on the watcher
1575 if( aLibrary == getTargetLib() )
1576 SetSymModificationTime( fn.GetModificationTime() );
1577 }
1578 else
1579 {
1580 bool resyncLibTree = false;
1581 wxString originalLibNickname = getTargetLib();
1582 wxString forceRefresh;
1583
1584 switch( type )
1585 {
1587 resyncLibTree = replaceLibTableEntry( originalLibNickname, fn.GetFullPath() );
1588 forceRefresh = originalLibNickname;
1589 break;
1590
1592 resyncLibTree = addLibTableEntry( fn.GetFullPath() );
1593 break;
1594
1596 resyncLibTree = addLibTableEntry( fn.GetFullPath(), PROJECT_LIB_TABLE );
1597 break;
1598
1599 default:
1600 break;
1601 }
1602
1603 if( resyncLibTree )
1604 {
1606 SyncLibraries( true, false, forceRefresh );
1608 }
1609 }
1610
1611 ClearMsgPanel();
1612 msg.Printf( _( "Symbol library file '%s' saved." ), fn.GetFullPath() );
1614
1615 return true;
1616}
1617
1618
1619bool SYMBOL_EDIT_FRAME::saveAllLibraries( bool aRequireConfirmation )
1620{
1621 wxString msg, msg2;
1622 bool doSave = true;
1623 int dirtyCount = 0;
1624 bool applyToAll = false;
1625 bool retv = true;
1626
1627 for( const wxString& libNickname : m_libMgr->GetLibraryNames() )
1628 {
1629 if( m_libMgr->IsLibraryModified( libNickname ) )
1630 dirtyCount++;
1631 }
1632
1633 for( const wxString& libNickname : m_libMgr->GetLibraryNames() )
1634 {
1635 if( m_libMgr->IsLibraryModified( libNickname ) )
1636 {
1637 if( aRequireConfirmation && !applyToAll )
1638 {
1639 msg.Printf( _( "Save changes to '%s' before closing?" ), libNickname );
1640
1641 switch( UnsavedChangesDialog( this, msg, dirtyCount > 1 ? &applyToAll : nullptr ) )
1642 {
1643 case wxID_YES: doSave = true; break;
1644 case wxID_NO: doSave = false; break;
1645 default:
1646 case wxID_CANCEL: return false;
1647 }
1648 }
1649
1650 if( doSave )
1651 {
1652 // If saving under existing name fails then do a Save As..., and if that
1653 // fails then cancel close action.
1654 if( m_libMgr->IsLibraryReadOnly( libNickname ) )
1655 {
1656 msg.Printf( _( "Symbol library '%s' is not writable." ), libNickname );
1657 msg2 = _( "You must save to a different location." );
1658
1659 if( dirtyCount == 1 )
1660 {
1661 if( OKOrCancelDialog( this, _( "Warning" ), msg, msg2 ) != wxID_OK )
1662 {
1663 retv = false;
1664 continue;
1665 }
1666 }
1667 else
1668 {
1669 m_infoBar->Dismiss();
1670 m_infoBar->ShowMessageFor( msg + wxS( " " ) + msg2,
1671 2000, wxICON_EXCLAMATION );
1672
1673 while( m_infoBar->IsShownOnScreen() )
1674 wxSafeYield();
1675
1676 retv = false;
1677 continue;
1678 }
1679 }
1680 else if( saveLibrary( libNickname, false ) )
1681 {
1682 continue;
1683 }
1684
1685 if( !saveLibrary( libNickname, true ) )
1686 retv = false;
1687 }
1688 }
1689 }
1690
1691 UpdateTitle();
1692 return retv;
1693}
1694
1695
1697{
1699
1700 if( !m_symbol )
1701 return;
1702
1703 wxString msg = m_symbol->GetName();
1704
1705 AppendMsgPanel( _( "Name" ), UnescapeString( msg ), 8 );
1706
1707 if( m_symbol->IsDerived() )
1708 {
1709 LIB_SYMBOL_SPTR parent = m_symbol->GetParent().lock();
1710
1711 msg = parent ? parent->GetName() : _( "Undefined!" );
1712 AppendMsgPanel( _( "Parent" ), UnescapeString( msg ), 8 );
1713 }
1714
1715 if( m_symbol->IsGlobalPower() )
1716 msg = _( "Power Symbol" );
1717 else if( m_symbol->IsLocalPower() )
1718 msg = _( "Power Symbol (Local)" );
1719 else
1720 msg = _( "Symbol" );
1721
1722 AppendMsgPanel( _( "Type" ), msg, 8 );
1723 AppendMsgPanel( _( "Description" ), m_symbol->GetDescription(), 8 );
1724 AppendMsgPanel( _( "Keywords" ), m_symbol->GetKeyWords() );
1725 AppendMsgPanel( _( "Datasheet" ), m_symbol->GetDatasheetField().GetText() );
1726}
const char * name
static TOOL_ACTION cancelInteractive
Definition actions.h:72
static TOOL_ACTION zoomFitScreen
Definition actions.h:141
void SetContentModified(bool aModified=true)
Definition base_screen.h:59
wxString GetParentSymbolName() const
wxString GetName() const override
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={})
int ShowModal() override
virtual void ClearUndoRedoList()
Clear the undo and redo list using ClearUndoORRedoList()
WX_INFOBAR * m_infoBar
virtual void RecreateToolbars()
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
void ReCreateMenuBar()
Recreate the menu bar.
WX_INFOBAR * GetInfoBar()
virtual void ClearMsgPanel()
Clear all messages from the message panel.
void AppendMsgPanel(const wxString &aTextUpper, const wxString &aTextLower, int aPadding=6)
Append a message to the message panel.
void SetOKLabel(const wxString &aLabel)
void initDialog(const wxArrayString &aItemHeaders, const std::vector< wxArrayString > &aItemList, const wxString &aPreselectText)
wxString GetTextSelection(int aColumn=0)
Return the selected text from aColumn in the wxListCtrl in the dialog.
void SetListLabel(const wxString &aLabel)
void AddExtraCheckbox(const wxString &aLabel, bool *aValuePtr)
Add a checkbox value to the dialog.
void GetExtraCheckboxValues()
Fills in the value pointers from the checkboxes after the dialog has run.
EDA_LIST_DIALOG(wxWindow *aParent, const wxString &aTitle, const wxArrayString &aItemHeaders, const std::vector< wxArrayString > &aItemList, const wxString &aPreselectText=wxEmptyString, bool aSortList=true)
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
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()
virtual const wxString Problem() const
what was the problem?
Helper class to create more flexible dialogs, including 'do not show again' checkbox handling.
Definition kidialog.h:42
void DoNotShowCheckbox(wxString file, int line)
Shows the 'do not show again' checkbox.
Definition kidialog.cpp:55
int ShowModal() override
Definition kidialog.cpp:93
void UpdateAllItems(int aUpdateFlags)
Update all items in the view according to the given flags.
Definition view.cpp:1561
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition kiway.cpp:395
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
wxString GetUniStringLibId() const
Definition lib_id.h:148
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:112
const wxString GetUniStringLibNickname() const
Definition lib_id.h:88
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
Symbol library management helper that is specific to the symbol library editor frame.
Define a library symbol object.
Definition lib_symbol.h:87
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:156
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition lib_symbol.h:293
bool IsDerived() const
Definition lib_symbol.h:208
LIB_ID GetSourceLibId() const
Definition lib_symbol.h:159
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:150
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:338
bool IsMultiUnit() const override
Definition lib_symbol.h:748
LIB_SYMBOL_SPTR SharedPtr() const
http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/sp_techniques.html#weak_without_shared.
Definition lib_symbol.h:97
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
LIB_SYMBOL_REF & GetParent()
Definition lib_symbol.h:119
virtual void SetName(const wxString &aName)
std::vector< wxString > GetLogicalLibs()
Return the logical library names, all of them that are pertinent to a look up done on this LIB_TABLE.
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
static SYMBOL_LIB_TABLE * SchSymbolLibTable(PROJECT *aProject)
Accessor for project symbol library table.
static SEARCH_STACK * SchSearchS(PROJECT *aProject)
Accessor for Eeschema search stack.
Container for project specific data.
Definition project.h:65
@ SCH_LIB_PATH
Definition project.h:220
virtual void SetRString(RSTRING_T aStringId, const wxString &aString)
Store a "retained string", which is any session and project specific string identified in enum RSTRIN...
Definition project.cpp:334
virtual const wxString & GetRString(RSTRING_T aStringId)
Return a "retained string", which is any session and project specific string identified in enum RSTRI...
Definition project.cpp:345
bool TransferDataFromWindow() override
std::function< int(const wxString &libName, const wxString &symbolName)> SymLibNameValidator
bool TransferDataToWindow() override
SymLibNameValidator m_validator
SAVE_SYMBOL_AS_DIALOG(SYMBOL_EDIT_FRAME *aParent, PARAMS &aParams, SymLibNameValidator aValidator, const std::vector< wxString > &aParentSymbolNames)
wxString getSymbolName() const
wxTextCtrl * m_symbolNameCtrl
SCH_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
SCH_DRAW_PANEL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
void GetLibraryItemsForListDialog(wxArrayString &aHeaders, std::vector< wxArrayString > &aItemsToDisplay)
wxString SelectLibrary(const wxString &aDialogTitle, const wxString &aListLabel, const std::vector< std::pair< wxString, bool * > > &aExtraCheckboxes={})
Display a list of loaded libraries and allows the user to select a library.
void SetSymModificationTime(const wxDateTime &aTime)
Set the modification time of the symbol library table file.
void setSymWatcher(const LIB_ID *aSymbol)
Creates (or removes) a watcher on the specified symbol library.
KIGFX::SCH_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
Schematic editor (Eeschema) main window.
void SaveSymbolToSchematic(const LIB_SYMBOL &aSymbol, const KIID &aSchematicSymbolUUID)
Update a schematic symbol from a LIB_SYMBOL.
void SetText(const wxString &aText) override
static void FormatLibSymbol(LIB_SYMBOL *aPart, OUTPUTFORMATTER &aFormatter)
static std::vector< LIB_SYMBOL * > ParseLibSymbols(std::string &aSymbolText, std::string aSource, int aFileVersion=SEXPR_SCHEMATIC_FILE_VERSION)
static SCH_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a symbol library using the file extension of aLibPath.
Look for files in a number of paths.
const wxString LastVisitedPath(const wxString &aSubPathToSearch=wxEmptyString)
A quirky function inherited from old code that seems to serve particular needs in the UI.
Implement an OUTPUTFORMATTER to a memory buffer.
Definition richio.h:449
const std::string & GetString()
Definition richio.h:472
The symbol library editor main window.
void ClearMsgPanel() override
Clear all messages from the message panel.
void UpdateAfterSymbolProperties(wxString *aOldName=nullptr)
void SaveAll()
Save all modified symbols and libraries.
bool addLibTableEntry(const wxString &aLibFile, TABLE_SCOPE aScope=GLOBAL_LIB_TABLE)
Add aLibFile to the symbol library table defined by aScope.
bool IsLibraryTreeShown() const override
wxString getTargetLib() const
bool IsCurrentSymbol(const LIB_ID &aLibId) const
Restore the empty editor screen, without any symbol or library selected.
bool backupFile(const wxFileName &aOriginalFile, const wxString &aBackupExt)
Return currently edited symbol.
void RebuildSymbolUnitAndBodyStyleLists()
int GetTreeLIBIDs(std::vector< LIB_ID > &aSelection) const
LIB_ID GetTreeLIBID(int *aUnit=nullptr) const
Return the LIB_ID of the library or symbol selected in the symbol tree.
LIB_SYMBOL_LIBRARY_MANAGER * m_libMgr
wxString GetCurLib() const
The nickname of the current library being edited and empty string if none.
void Save()
Save the selected symbol or library.
void LoadSymbol(const wxString &aLibrary, const wxString &aSymbol, int Unit)
bool m_SyncPinEdit
Set to true to synchronize pins at the same position when editing symbols with multiple units or mult...
void Revert(bool aConfirm=true)
Revert unsaved changes in a symbol, restoring to the last saved state.
void centerItemIdleHandler(wxIdleEvent &aEvent)
bool replaceLibTableEntry(const wxString &aLibNickname, const wxString &aLibFile)
Replace the file path of the symbol library table entry aLibNickname with aLibFile.
bool IsSymbolFromSchematic() const
void SetScreen(BASE_SCREEN *aScreen) override
void DuplicateSymbol(bool aFromClipboard)
Insert a duplicate symbol.
SCH_SCREEN * m_dummyScreen
< Helper screen used when no symbol is loaded
void saveSymbolCopyAs(bool aOpenCopy)
void SetCurSymbol(LIB_SYMBOL *aSymbol, bool aUpdateZoom)
Take ownership of aSymbol and notes that it is the one currently being edited.
KIID m_schematicSymbolUUID
RefDes of the symbol (only valid if symbol was loaded from schematic)
std::vector< LIB_ID > GetSelectedLibIds() const
void SyncLibraries(bool aShowProgress, bool aPreloadCancelled=false, const wxString &aForceRefresh=wxEmptyString)
Synchronize the library manager to the symbol library table, and then the symbol tree to the library ...
LIB_SYMBOL * GetCurSymbol() const
Return the current symbol being edited or NULL if none selected.
void UpdateSymbolMsgPanelInfo()
Display the documentation of the selected symbol.
LIB_ID GetTargetLibId() const override
Return either the symbol selected in the symbol tree (if context menu is active) or the symbol on the...
bool saveLibrary(const wxString &aLibrary, bool aNewFile)
Save the changes to the current library.
void SelectActiveLibrary(const wxString &aLibrary=wxEmptyString)
Set the current active library to aLibrary.
int m_bodyStyle
Flag if the symbol being edited was loaded directly from a schematic.
bool saveAllLibraries(bool aRequireConfirmation)
Save the current symbol.
void UpdateMsgPanel() override
Redraw the message panel.
void CreateNewSymbol(const wxString &newName=wxEmptyString)
Create a new symbol in the selected library.
wxString SetCurLib(const wxString &aLibNickname)
Set the current library nickname and returns the old library nickname.
void UpdateTitle()
Update the main window title bar with the current library name and read only status of the library.
bool LoadSymbolFromCurrentLib(const wxString &aSymbolName, int aUnit=0, int aBodyStyle=0)
Load a symbol from the current active library, optionally setting the selected unit and convert.
SYMBOL_TREE_PANE * m_treePane
bool LoadOneLibrarySymbolAux(LIB_SYMBOL *aLibEntry, const wxString &aLibrary, int aUnit, int aBodyStyle)
Create a copy of aLibEntry into memory.
bool saveCurrentSymbol()
Store the currently modified symbol in the library manager buffer.
void SaveSymbolCopyAs(bool aOpenCopy)
Save the currently selected symbol to a new name and/or location.
wxString AddLibraryFile(bool aCreateNew)
Create or add an existing library to the symbol library table.
void ensureUniqueName(LIB_SYMBOL *aSymbol, const wxString &aLibrary)
void OnModify() override
Must be called after a schematic change in order to set the "modify" flag of the current symbol.
void SaveLibraryAs()
Save the currently selected library to a new file.
bool IsContentModified() const override
Get if any symbols or libraries have been modified but not saved.
LIB_SYMBOL * getTargetSymbol() const
Return either the library selected in the symbol tree, if context menu is active or the library that ...
bool SymbolNameInUse(const wxString &aName, const wxString &aLibrary)
Return true if the symbol name is already in use in the specified library.
LIB_SYMBOL * GetSymbol(const wxString &aSymbolName, const wxString &aLibrary) const
Return either an alias of a working LIB_SYMBOL copy, or alias of the original symbol if there is no w...
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_IO object i...
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
This is a class that handles state involved in saving a symbol copy as a new symbol.
CONFLICT_STRATEGY m_strategy
wxString resolveConflict(const wxString &proposed, const wxString &aNewLibName) const
SYMBOL_SAVE_AS_HANDLER(LIB_SYMBOL_LIBRARY_MANAGER &aLibMgr, CONFLICT_STRATEGY aStrategy, bool aValueFollowsName)
LIB_SYMBOL_LIBRARY_MANAGER & m_libMgr
bool DoSave(LIB_SYMBOL &symbol, const wxString &aNewSymName, const wxString &aNewLibName, bool aFlattenSymbol)
TOOL_MANAGER * m_toolManager
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition tool_base.h:80
wxString wx_str() const
Definition utf8.cpp:45
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
std::string GetClipboardUTF8()
Return the information currently stored in the system clipboard.
Definition clipboard.cpp:58
int OKOrCancelDialog(wxWindow *aParent, const wxString &aWarning, const wxString &aMessage, const wxString &aDetailedMessage, const wxString &aOKLabel, const wxString &aCancelLabel, bool *aApplyToAll)
Display a warning dialog with aMessage and returns the user response.
Definition confirm.cpp:142
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Display a yes/no dialog with aMessage and returns the user response.
Definition confirm.cpp:251
bool HandleUnsavedChanges(wxWindow *aParent, const wxString &aMessage, const std::function< bool()> &aSaveFunction)
Display a dialog with Save, Cancel and Discard Changes buttons.
Definition confirm.cpp:129
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:194
int UnsavedChangesDialog(wxWindow *parent, const wxString &aMessage, bool *aApplyToAll)
A specialized version of HandleUnsavedChanges which handles an apply-to-all checkbox.
Definition confirm.cpp:64
bool ConfirmRevertDialog(wxWindow *parent, const wxString &aMessage)
Display a confirmation dialog for a revert action.
Definition confirm.cpp:118
void DisplayError(wxWindow *aParent, const wxString &aText)
Display an error or warning message box with aMessage.
Definition confirm.cpp:169
This file is part of the common library.
#define KICAD_MESSAGE_DIALOG_BASE
Definition confirm.h:46
#define _(s)
@ FRAME_SCH
Definition frame_type.h:34
static const std::string KiCadSymbolLibFileExtension
static wxString KiCadSymbolLibFileWildcard()
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
PROJECT & Prj()
Definition kicad.cpp:612
std::shared_ptr< LIB_SYMBOL > LIB_SYMBOL_SPTR
shared pointer to LIB_SYMBOL
Definition lib_symbol.h:54
void Prettify(std::string &aSource, bool aCompactSave)
@ ALL
All except INITIAL_ADD.
Definition view_item.h:59
STL namespace.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition ptree.cpp:198
KIWAY Kiway(KFCTL_STANDALONE)
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
wxString UnescapeString(const wxString &aSource)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
SYMBOL_SAVE_AS_HANDLER::CONFLICT_STRATEGY m_ConflictStrategy
SAVE_AS_IDS
@ ID_OVERWRITE_CONFLICTS
@ ID_RENAME_CONFLICTS
@ ID_MAKE_NEW_LIBRARY
static std::vector< wxString > CheckForParentalChainConflicts(LIB_SYMBOL_LIBRARY_MANAGER &aLibMgr, LIB_SYMBOL &aSymbol, bool aFlattenSymbol, const wxString &newSymbolName, const wxString &newLibraryName)
Get a list of all the symbols in the parental chain of a symbol that have conflicts when transposed t...
static std::pair< bool, bool > CheckSavingIntoOwnInheritance(LIB_SYMBOL_LIBRARY_MANAGER &aLibMgr, LIB_SYMBOL &aSymbol, const wxString &aNewSymbolName, const wxString &aNewLibraryName)
Check if a planned overwrite would put a symbol into it's own inheritance chain.
static std::vector< LIB_SYMBOL_SPTR > GetParentChain(const LIB_SYMBOL &aSymbol, bool aIncludeLeaf=true)
Get a list of all the symbols in the parental chain of a symbol, with the "leaf" symbol at the start ...
Definition for symbol library class.
SYMBOL_SAVEAS_TYPE
const SHAPE_LINE_CHAIN chain
Definition of file extensions used in Kicad.