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 if( m_libMgr->HasDerivedSymbols( libId.GetLibItemName(), libId.GetLibNickname() ) )
1254 {
1255 wxString msg;
1256
1257 msg.Printf(
1258 _( "The symbol %s is used to derive other symbols.\n"
1259 "Deleting this symbol will delete all of the symbols derived from it.\n\n"
1260 "Do you wish to delete this symbol and all of its derivatives?" ),
1261 libId.GetLibItemName().wx_str() );
1262
1263 wxMessageDialog::ButtonLabel yesButtonLabel( _( "Delete Symbol" ) );
1264 wxMessageDialog::ButtonLabel noButtonLabel( _( "Keep Symbol" ) );
1265
1266 wxMessageDialog dlg( this, msg, _( "Warning" ),
1267 wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
1268 dlg.SetYesNoLabels( yesButtonLabel, noButtonLabel );
1269
1270 if( dlg.ShowModal() == wxID_NO )
1271 continue;
1272 }
1273
1274 if( IsCurrentSymbol( libId ) )
1275 emptyScreen();
1276
1277 m_libMgr->RemoveSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
1278 }
1279
1280 m_treePane->GetLibTree()->RefreshLibTree();
1281}
1282
1283
1285{
1286 std::vector<LIB_ID> symbols;
1287
1288 if( GetTreeLIBIDs( symbols ) == 0 )
1289 return;
1290
1291 STRING_FORMATTER formatter;
1292
1293 for( LIB_ID& libId : symbols )
1294 {
1295 LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(),
1296 libId.GetLibNickname() );
1297
1298 if( !symbol )
1299 continue;
1300
1301 std::unique_ptr<LIB_SYMBOL> tmp = symbol->Flatten();
1302 SCH_IO_KICAD_SEXPR::FormatLibSymbol( tmp.get(), formatter );
1303 }
1304
1305 std::string prettyData = formatter.GetString();
1306 KICAD_FORMAT::Prettify( prettyData, true );
1307
1308 wxLogNull doNotLog; // disable logging of failed clipboard actions
1309
1310 auto clipboard = wxTheClipboard;
1311 wxClipboardLocker clipboardLock( clipboard );
1312
1313 if( !clipboardLock || !clipboard->IsOpened() )
1314 return;
1315
1316 auto data = new wxTextDataObject( wxString( prettyData.c_str(), wxConvUTF8 ) );
1317 clipboard->SetData( data );
1318
1319 clipboard->Flush();
1320}
1321
1322
1323void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
1324{
1325 LIB_ID libId = GetTargetLibId();
1326 wxString lib = libId.GetLibNickname();
1327
1328 if( !m_libMgr->LibraryExists( lib ) )
1329 return;
1330
1331 std::vector<LIB_SYMBOL*> newSymbols;
1332
1333 if( aFromClipboard )
1334 {
1335 std::string clipboardData = GetClipboardUTF8();
1336
1337 try
1338 {
1339 newSymbols = SCH_IO_KICAD_SEXPR::ParseLibSymbols( clipboardData, "Clipboard" );
1340 }
1341 catch( IO_ERROR& e )
1342 {
1343 wxLogMessage( wxS( "Can not paste: %s" ), e.Problem() );
1344 }
1345 }
1346 else if( LIB_SYMBOL* srcSymbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(), lib ) )
1347 {
1348 newSymbols.emplace_back( new LIB_SYMBOL( *srcSymbol ) );
1349
1350 // Derive from same parent.
1351 if( srcSymbol->IsDerived() )
1352 {
1353 if( std::shared_ptr<LIB_SYMBOL> srcParent = srcSymbol->GetParent().lock() )
1354 newSymbols.back()->SetParent( srcParent.get() );
1355 }
1356 }
1357
1358 if( newSymbols.empty() )
1359 return;
1360
1361 for( LIB_SYMBOL* symbol : newSymbols )
1362 {
1363 ensureUniqueName( symbol, lib );
1364 m_libMgr->UpdateSymbol( symbol, lib );
1365
1366 LoadOneLibrarySymbolAux( symbol, lib, GetUnit(), GetBodyStyle() );
1367 }
1368
1369 SyncLibraries( false );
1370 m_treePane->GetLibTree()->SelectLibId( LIB_ID( lib, newSymbols[0]->GetName() ) );
1371
1372 for( LIB_SYMBOL* symbol : newSymbols )
1373 delete symbol;
1374}
1375
1376
1377void SYMBOL_EDIT_FRAME::ensureUniqueName( LIB_SYMBOL* aSymbol, const wxString& aLibrary )
1378{
1379 if( aSymbol )
1380 {
1381 int i = 1;
1382 wxString newName = aSymbol->GetName();
1383
1384 // Append a number to the name until the name is unique in the library.
1385 while( m_libMgr->SymbolNameInUse( newName, aLibrary ) )
1386 newName.Printf( "%s_%d", aSymbol->GetName(), i++ );
1387
1388 aSymbol->SetName( newName );
1389 }
1390}
1391
1392
1393void SYMBOL_EDIT_FRAME::Revert( bool aConfirm )
1394{
1395 LIB_ID libId = GetTargetLibId();
1396 const wxString& libName = libId.GetLibNickname();
1397
1398 // Empty if this is the library itself that is selected.
1399 const wxString& symbolName = libId.GetLibItemName();
1400
1401 wxString msg = wxString::Format( _( "Revert '%s' to last version saved?" ),
1402 symbolName.IsEmpty() ? libName : symbolName );
1403
1404 if( aConfirm && !ConfirmRevertDialog( this, msg ) )
1405 return;
1406
1407 bool reload_currentSymbol = false;
1408 wxString curr_symbolName = symbolName;
1409
1410 if( GetCurSymbol() )
1411 {
1412 // the library itself is reverted: the current symbol will be reloaded only if it is
1413 // owned by this library
1414 if( symbolName.IsEmpty() )
1415 {
1416 LIB_ID curr_libId = GetCurSymbol()->GetLibId();
1417 reload_currentSymbol = libName == curr_libId.GetLibNickname().wx_str();
1418
1419 if( reload_currentSymbol )
1420 curr_symbolName = curr_libId.GetUniStringLibItemName();
1421 }
1422 else
1423 {
1424 reload_currentSymbol = IsCurrentSymbol( libId );
1425 }
1426 }
1427
1428 int unit = m_unit;
1429
1430 if( reload_currentSymbol )
1431 emptyScreen();
1432
1433 if( symbolName.IsEmpty() )
1434 {
1435 m_libMgr->RevertLibrary( libName );
1436 }
1437 else
1438 {
1439 libId = m_libMgr->RevertSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
1440
1441 m_treePane->GetLibTree()->SelectLibId( libId );
1442 m_libMgr->ClearSymbolModified( libId.GetLibItemName(), libId.GetLibNickname() );
1443 }
1444
1445 if( reload_currentSymbol && m_libMgr->SymbolExists( curr_symbolName, libName ) )
1446 LoadSymbol( curr_symbolName, libName, unit );
1447
1448 m_treePane->Refresh();
1449}
1450
1451
1453{
1454 wxCHECK_RET( m_libMgr, "Library manager object not created." );
1455
1456 Revert( false );
1457 m_libMgr->RevertAll();
1458}
1459
1460
1461void SYMBOL_EDIT_FRAME::LoadSymbol( const wxString& aAlias, const wxString& aLibrary, int aUnit )
1462{
1464 {
1465 if( !HandleUnsavedChanges( this, _( "The current symbol has been modified. Save changes?" ),
1466 [&]() -> bool
1467 {
1468 return saveCurrentSymbol();
1469 } ) )
1470 {
1471 return;
1472 }
1473 }
1474
1475 LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( aAlias, aLibrary );
1476
1477 if( !symbol )
1478 {
1479 DisplayError( this, wxString::Format( _( "Symbol %s not found in library '%s'." ),
1480 aAlias,
1481 aLibrary ) );
1482 m_treePane->GetLibTree()->RefreshLibTree();
1483 return;
1484 }
1485
1486 // Optimize default edit options for this symbol
1487 // Usually if units are locked, graphic items are specific to each unit
1488 // and if units are interchangeable, graphic items are common to units
1490 tools->SetDrawSpecificUnit( symbol->UnitsLocked() );
1491
1492 LoadOneLibrarySymbolAux( symbol, aLibrary, aUnit, 0 );
1493}
1494
1495
1496bool SYMBOL_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
1497{
1498 wxFileName fn;
1499 wxString msg;
1501 SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD;
1502 PROJECT& prj = Prj();
1503
1505
1506 if( !aNewFile && ( aLibrary.empty() || !PROJECT_SCH::SchSymbolLibTable( &prj )->HasLibrary( aLibrary ) ) )
1507 {
1508 ShowInfoBarError( _( "No library specified." ) );
1509 return false;
1510 }
1511
1512 if( aNewFile )
1513 {
1514 SEARCH_STACK* search = PROJECT_SCH::SchSearchS( &prj );
1515
1516 // Get a new name for the library
1517 wxString default_path = prj.GetRString( PROJECT::SCH_LIB_PATH );
1518
1519 if( !default_path )
1520 default_path = search->LastVisitedPath();
1521
1522 fn.SetName( aLibrary );
1524
1525 wxString wildcards = FILEEXT::KiCadSymbolLibFileWildcard();
1526
1527 wxFileDialog dlg( this, wxString::Format( _( "Save Library '%s' As..." ), aLibrary ), default_path,
1528 fn.GetFullName(), wildcards, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
1529
1530 SYMBOL_LIBRARY_SAVE_AS_FILEDLG_HOOK saveAsHook( type );
1531 dlg.SetCustomizeHook( saveAsHook );
1532
1533 if( dlg.ShowModal() == wxID_CANCEL )
1534 return false;
1535
1536 fn = dlg.GetPath();
1537
1538 prj.SetRString( PROJECT::SCH_LIB_PATH, fn.GetPath() );
1539
1540 if( fn.GetExt().IsEmpty() )
1542
1543 type = saveAsHook.GetOption();
1544 }
1545 else
1546 {
1547 fn = PROJECT_SCH::SchSymbolLibTable( &prj )->GetFullURI( aLibrary );
1549
1550 if( fileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
1551 fileType = SCH_IO_MGR::SCH_KICAD;
1552 }
1553
1554 // Verify the user has write privileges before attempting to save the library file.
1555 if( !aNewFile && m_libMgr->IsLibraryReadOnly( aLibrary ) )
1556 return false;
1557
1558 ClearMsgPanel();
1559
1560 // Copy .kicad_symb file to .bak.
1561 if( !backupFile( fn, "bak" ) )
1562 return false;
1563
1564 if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath(), fileType ) )
1565 {
1566 msg.Printf( _( "Failed to save changes to symbol library file '%s'." ),
1567 fn.GetFullPath() );
1568 DisplayErrorMessage( this, _( "Error Saving Library" ), msg );
1569 return false;
1570 }
1571
1572 if( !aNewFile )
1573 {
1574 m_libMgr->ClearLibraryModified( aLibrary );
1575
1576 // Update the library modification time so that we don't reload based on the watcher
1577 if( aLibrary == getTargetLib() )
1578 SetSymModificationTime( fn.GetModificationTime() );
1579 }
1580 else
1581 {
1582 bool resyncLibTree = false;
1583 wxString originalLibNickname = getTargetLib();
1584 wxString forceRefresh;
1585
1586 switch( type )
1587 {
1589 resyncLibTree = replaceLibTableEntry( originalLibNickname, fn.GetFullPath() );
1590 forceRefresh = originalLibNickname;
1591 break;
1592
1594 resyncLibTree = addLibTableEntry( fn.GetFullPath() );
1595 break;
1596
1598 resyncLibTree = addLibTableEntry( fn.GetFullPath(), PROJECT_LIB_TABLE );
1599 break;
1600
1601 default:
1602 break;
1603 }
1604
1605 if( resyncLibTree )
1606 {
1608 SyncLibraries( true, false, forceRefresh );
1610 }
1611 }
1612
1613 ClearMsgPanel();
1614 msg.Printf( _( "Symbol library file '%s' saved." ), fn.GetFullPath() );
1616
1617 return true;
1618}
1619
1620
1621bool SYMBOL_EDIT_FRAME::saveAllLibraries( bool aRequireConfirmation )
1622{
1623 wxString msg, msg2;
1624 bool doSave = true;
1625 int dirtyCount = 0;
1626 bool applyToAll = false;
1627 bool retv = true;
1628
1629 for( const wxString& libNickname : m_libMgr->GetLibraryNames() )
1630 {
1631 if( m_libMgr->IsLibraryModified( libNickname ) )
1632 dirtyCount++;
1633 }
1634
1635 for( const wxString& libNickname : m_libMgr->GetLibraryNames() )
1636 {
1637 if( m_libMgr->IsLibraryModified( libNickname ) )
1638 {
1639 if( aRequireConfirmation && !applyToAll )
1640 {
1641 msg.Printf( _( "Save changes to '%s' before closing?" ), libNickname );
1642
1643 switch( UnsavedChangesDialog( this, msg, dirtyCount > 1 ? &applyToAll : nullptr ) )
1644 {
1645 case wxID_YES: doSave = true; break;
1646 case wxID_NO: doSave = false; break;
1647 default:
1648 case wxID_CANCEL: return false;
1649 }
1650 }
1651
1652 if( doSave )
1653 {
1654 // If saving under existing name fails then do a Save As..., and if that
1655 // fails then cancel close action.
1656 if( m_libMgr->IsLibraryReadOnly( libNickname ) )
1657 {
1658 msg.Printf( _( "Symbol library '%s' is not writable." ), libNickname );
1659 msg2 = _( "You must save to a different location." );
1660
1661 if( dirtyCount == 1 )
1662 {
1663 if( OKOrCancelDialog( this, _( "Warning" ), msg, msg2 ) != wxID_OK )
1664 {
1665 retv = false;
1666 continue;
1667 }
1668 }
1669 else
1670 {
1671 m_infoBar->Dismiss();
1672 m_infoBar->ShowMessageFor( msg + wxS( " " ) + msg2,
1673 2000, wxICON_EXCLAMATION );
1674
1675 while( m_infoBar->IsShownOnScreen() )
1676 wxSafeYield();
1677
1678 retv = false;
1679 continue;
1680 }
1681 }
1682 else if( saveLibrary( libNickname, false ) )
1683 {
1684 continue;
1685 }
1686
1687 if( !saveLibrary( libNickname, true ) )
1688 retv = false;
1689 }
1690 }
1691 }
1692
1693 UpdateTitle();
1694 return retv;
1695}
1696
1697
1699{
1701
1702 if( !m_symbol )
1703 return;
1704
1705 wxString msg = m_symbol->GetName();
1706
1707 AppendMsgPanel( _( "Name" ), UnescapeString( msg ), 8 );
1708
1709 if( m_symbol->IsDerived() )
1710 {
1711 LIB_SYMBOL_SPTR parent = m_symbol->GetParent().lock();
1712
1713 msg = parent ? parent->GetName() : _( "Undefined!" );
1714 AppendMsgPanel( _( "Parent" ), UnescapeString( msg ), 8 );
1715 }
1716
1717 if( m_symbol->IsGlobalPower() )
1718 msg = _( "Power Symbol" );
1719 else if( m_symbol->IsLocalPower() )
1720 msg = _( "Power Symbol (Local)" );
1721 else
1722 msg = _( "Symbol" );
1723
1724 AppendMsgPanel( _( "Type" ), msg, 8 );
1725 AppendMsgPanel( _( "Description" ), m_symbol->GetDescription(), 8 );
1726 AppendMsgPanel( _( "Keywords" ), m_symbol->GetKeyWords() );
1727 AppendMsgPanel( _( "Datasheet" ), m_symbol->GetDatasheetField().GetText() );
1728}
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:97
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:85
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:154
bool UnitsLocked() const
Check whether symbol units are interchangeable.
Definition lib_symbol.h:291
bool IsDerived() const
Definition lib_symbol.h:206
LIB_ID GetSourceLibId() const
Definition lib_symbol.h:157
void SetParent(LIB_SYMBOL *aParent=nullptr)
wxString GetName() const override
Definition lib_symbol.h:148
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:336
bool IsMultiUnit() const override
Definition lib_symbol.h:590
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:95
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
LIB_SYMBOL_REF & GetParent()
Definition lib_symbol.h:117
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 _(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:52
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.