Make the new schematic and symbol library file formats the default.

This is a very large and potentially disruptive change so this will be an
unusually long and detailed commit message.

The new file formats are now the default in both the schematic and symbol
library editors.  Existing symbol libraries will be saved in their current
format until new features are added to library symbols.  Once this happens,
both the legacy schematic and symbol file formats will be no longer be
savable and existing libraries will have to be converted.  Saving to the
legacy file formats is still available for round robin testing and should
not be used for normal editing.

When loading the legacy schematic file, it is imperative that the schematic
library symbols are rescued and/or remapped to valid library identifiers.
Otherwise, there will be no way to link to the original library symbol and
the user will be required manually set the library identifier.  The cached
symbol will be saved in the schematic file so the last library symbol in
the cache will still be used but there will be no way to update it from the
original library.

The next save after loading a legacy schematic file will be converted to
the s-expression file format.  Schematics with hierarchical sheets will
automatically have all sheet file name extensions changed to .kicad_sym
and saved to the new format as well.

Appending schematics requires that the schematic to append has already been
converted to the new file format.  This is required to ensure that library
symbols are guaranteed to be valid for the appended schematic.

The schematic symbol library symbol link resolution has been moved out of
the SCH_COMPONENT object and move into the SCH_SCREEN object that owns the
symbol.  This was done to ensure that there is a single place where the
library symbol links get resolved rather than the dozen or so different
code paths that previously existed.  It also removes the necessity of the
SCH_COMPONENT object of requiring any knowledge of the symbol library table
and/or the cache library.

When opening an s-expression schematic, the legacy cache library is not
loaded so any library symbols not rescued cannot be loaded.  Broken library
symbol links will have to be manually resolved by adding the cache library
to the symbol library table and changing the links in the schematic symbol.

Now that the library symbols are embedded in the schematic file, the
SCH_SCREEN object maintains the list of library symbols for the schematic
automatically.  No external manipulation of this library cache should ever
occur.

ADDED: S-expression schematic and symbol library file formats.
This commit is contained in:
Wayne Stambaugh 2020-04-16 12:43:50 -04:00
parent 12d1d3a844
commit 7183e9f97e
32 changed files with 882 additions and 443 deletions

View File

@ -579,6 +579,7 @@ public:
bool operator==( const LIB_PART* aPart ) const { return this == aPart; }
bool operator==( const LIB_PART& aPart ) const { return Compare( aPart ) == 0; }
bool operator!=( const LIB_PART& aPart ) const { return Compare( aPart ) != 0; }
const LIB_PART& operator=( const LIB_PART& aPart );

View File

@ -1,7 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2004-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -398,6 +398,8 @@ bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::TransferDataFromWindow()
if( !wxDialog::TransferDataFromWindow() ) // Calls our Validate() method.
return false;
wxString msg;
// save old cmp in undo list if not already in edit, or moving ...
if( m_cmp->GetEditFlags() == 0 )
GetParent()->SaveCopyInUndoList( m_cmp, UR_CHANGED );
@ -407,8 +409,27 @@ bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::TransferDataFromWindow()
// Library symbol identifier
LIB_ID id;
id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true );
m_cmp->SetLibId( id, Prj().SchSymbolLibTable(), Prj().SchLibs()->GetCacheLibrary() );
if( id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true ) >= 0 )
{
msg.Printf( _( "'%s' is not a valid library indentifier." ),
m_libraryNameTextCtrl->GetValue() );
DisplayError( this, msg );
return false;
}
LIB_PART* libSymbol = Prj().SchSymbolLibTable()->LoadSymbol( id );
if( !libSymbol )
{
msg.Printf( _( "Symbol '%s' not found in symbol library '%s'." ),
id.GetLibItemName().wx_str(), id.GetLibNickname().wx_str() );
DisplayError( this, msg );
return false;
}
m_cmp->SetLibSymbol( new LIB_PART( *libSymbol ) );
m_cmp->SetLibId( id );
// For symbols with multiple shapes (De Morgan representation) Set the selected shape:
if( m_cbAlternateSymbol->IsEnabled() && m_cbAlternateSymbol->GetValue() )
@ -466,8 +487,8 @@ bool DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::TransferDataFromWindow()
// grid needs to be notified about the size change,
// as it still accesses the data on close (size event)
wxGridTableMessage msg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, i, 1 );
m_grid->ProcessTableMessage( msg );
wxGridTableMessage gridMsg( m_fields, wxGRIDTABLE_NOTIFY_ROWS_DELETED, i, 1 );
m_grid->ProcessTableMessage( gridMsg );
i--;
break;
}
@ -622,7 +643,9 @@ void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnMoveDown( wxCommandEvent& event )
m_grid->MakeCellVisible( m_grid->GetGridCursorRow(), m_grid->GetGridCursorCol() );
}
else
{
wxBell();
}
}
@ -631,12 +654,33 @@ void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::UpdateFieldsFromLibrary( wxCommandEvent
if( !m_grid->CommitPendingChanges() )
return;
LIB_ID id;
wxString msg;
SCH_COMPONENT copy( *m_cmp );
copy.SetFields( *m_fields );
LIB_ID id;
id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true );
copy.SetLibId( id, Prj().SchSymbolLibTable(), Prj().SchLibs()->GetCacheLibrary() );
if( id.Parse( m_libraryNameTextCtrl->GetValue(), LIB_ID::ID_SCH, true ) >= 0 )
{
msg.Printf( _( "'%s' is not a valid library indentifier." ),
m_libraryNameTextCtrl->GetValue() );
DisplayError( this, msg );
return;
}
LIB_PART* libSymbol = Prj().SchSymbolLibTable()->LoadSymbol( id );
if( !libSymbol )
{
msg.Printf( _( "Symbol '%s' not found in symbol library '%s'." ),
id.GetLibItemName().wx_str(), id.GetLibNickname().wx_str() );
DisplayError( this, msg );
return;
}
m_cmp->SetLibSymbol( new LIB_PART( *libSymbol ) );
// Update the requested fields in the component copy
std::list<SCH_COMPONENT*> components;

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2017 Jean-Pierre Charras, jp.charras@wanadoo.fr
* Copyright 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -774,7 +774,7 @@ bool DIALOG_EDIT_COMPONENTS_LIBID::TransferDataFromWindow()
{
m_isModified = true;
SCH_SCREENS schematic;
schematic.UpdateSymbolLinks( true );
schematic.UpdateSymbolLinks();
}
return true;
@ -807,7 +807,7 @@ void DIALOG_EDIT_COMPONENTS_LIBID::revertChanges()
if( change )
{
SCH_SCREENS schematic;
schematic.UpdateSymbolLinks( true );
schematic.UpdateSymbolLinks();
m_parent->GetCanvas()->Refresh();
}
}

View File

@ -6,7 +6,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Wayne Stambaugh <stambaughw@verizon.net>
* Copyright (C) 2017-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -269,7 +269,7 @@ void DIALOG_SYMBOL_REMAP::remapSymbolsToLibTable( REPORTER& aReporter )
}
aReporter.Report( _( "Symbol library table mapping complete!" ), RPT_SEVERITY_INFO );
schematic.UpdateSymbolLinks( true );
schematic.UpdateSymbolLinks();
}
@ -308,7 +308,7 @@ bool DIALOG_SYMBOL_REMAP::remapSymbolToLibTable( SCH_COMPONENT* aSymbol )
id.SetLibNickname( row->GetNickName() );
// Don't resolve symbol library links now.
aSymbol->SetLibId( id, nullptr, nullptr );
aSymbol->SetLibId( id );
return true;
}
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2017-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -243,8 +243,8 @@ PANEL_SYM_LIB_TABLE::PANEL_SYM_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent,
wxArrayString pluginChoices;
pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_KICAD ) );
pluginChoices.Add( SCH_IO_MGR::ShowType( SCH_IO_MGR::SCH_LEGACY ) );
populateEnvironReadOnlyTable();
@ -840,10 +840,6 @@ void InvokeSchEditSymbolLibTable( KIWAY* aKiway, wxWindow *aParent )
}
}
SCH_SCREENS schematic;
schematic.UpdateSymbolLinks( true ); // Update all symbol library links for all sheets.
if( schEditor )
schEditor->SyncView();

View File

@ -68,7 +68,11 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : APP_SETTINGS_BASE( "eeschema", eeschema
m_params.emplace_back( new PARAM<bool>( "appearance.show_page_limits",
&m_Appearance.show_page_limits, true ) );
m_params.emplace_back( new PARAM<bool>( "appearance.show_sheet_filename_case_sensitivity_dialog",
m_params.emplace_back( new PARAM<bool>( "appearance.show_sexpr_file_convert_warning",
&m_Appearance.show_sexpr_file_convert_warning, true ) );
m_params.emplace_back(
new PARAM<bool>( "appearance.show_sheet_filename_case_sensitivity_dialog",
&m_Appearance.show_sheet_filename_case_sensitivity_dialog, true ) );
m_params.emplace_back( new PARAM<bool>( "autoplace_fields.enable",

View File

@ -46,6 +46,7 @@ public:
bool show_hidden_fields;
bool show_illegal_symbol_lib_dialog;
bool show_page_limits;
bool show_sexpr_file_convert_warning;
bool show_sheet_filename_case_sensitivity_dialog;
};

View File

@ -54,6 +54,7 @@
#include <tool/actions.h>
#include <netlist.h>
bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
bool aCreateBackupFile )
{
@ -73,9 +74,9 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
if( aSaveUnderNewName )
{
wxString wildcards = LegacySchematicFileWildcard();
wxString wildcards = KiCadSchematicFileWildcard();
wildcards += "|" + KiCadSchematicFileWildcard();
wildcards += "|" + LegacySchematicFileWildcard();
wxFileDialog dlg( this, _( "Schematic Files" ), wxPathOnly( Prj().GetProjectFullName() ),
schematicFileName.GetFullName(), wildcards,
@ -86,10 +87,10 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
schematicFileName = dlg.GetPath();
if( dlg.GetFilterIndex() == 0
if( dlg.GetFilterIndex() == 1
&& schematicFileName.GetExt() != LegacySchematicFileExtension )
schematicFileName.SetExt( LegacySchematicFileExtension );
else if( dlg.GetFilterIndex() == 1
else if( dlg.GetFilterIndex() == 0
&& schematicFileName.GetExt() != KiCadSchematicFileExtension )
schematicFileName.SetExt( KiCadSchematicFileExtension );
}
@ -111,7 +112,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
if( !wxRenameFile( schematicFileName.GetFullPath(), backupFileName.GetFullPath() ) )
{
msg.Printf( _( "Could not save backup of file \"%s\"" ),
GetChars( schematicFileName.GetFullPath() ) );
schematicFileName.GetFullPath() );
DisplayError( this, msg );
}
}
@ -132,10 +133,10 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error saving schematic file \"%s\".\n%s" ),
GetChars( schematicFileName.GetFullPath() ), GetChars( ioe.What() ) );
schematicFileName.GetFullPath(), ioe.What() );
DisplayError( this, msg );
msg.Printf( _( "Failed to save \"%s\"" ), GetChars( schematicFileName.GetFullPath() ) );
msg.Printf( _( "Failed to save \"%s\"" ), schematicFileName.GetFullPath() );
AppendMsgPanel( wxEmptyString, msg, CYAN );
success = false;
@ -166,7 +167,7 @@ bool SCH_EDIT_FRAME::SaveEEFile( SCH_SCREEN* aScreen, bool aSaveUnderNewName,
aScreen->ClrSave();
aScreen->ClrModify();
msg.Printf( _( "File %s saved" ), GetChars( aScreen->GetFileName() ) );
msg.Printf( _( "File %s saved" ), aScreen->GetFileName() );
SetStatusText( msg, 0 );
}
else
@ -183,7 +184,16 @@ void SCH_EDIT_FRAME::Save_File( bool doSaveAs )
if( doSaveAs )
{
if( SaveEEFile( NULL, true ) )
CreateArchiveLibraryCacheFile( true );
{
SCH_SCREEN* screen = GetScreen();
wxCHECK( screen, /* void */ );
wxFileName fn = screen->GetFileName();
if( fn.GetExt() == LegacySchematicFileExtension )
CreateArchiveLibraryCacheFile( true );
}
}
else
{
@ -197,9 +207,10 @@ void SCH_EDIT_FRAME::Save_File( bool doSaveAs )
bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, int aCtl )
{
// implement the pseudo code from KIWAY_PLAYER.h:
wxString msg;
auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
// This is for python:
if( aFileSet.size() != 1 )
{
@ -208,7 +219,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
return false;
}
wxString fullFileName( aFileSet[0] );
wxString fullFileName( aFileSet[0] );
// We insist on caller sending us an absolute path, if it does not, we say it's a bug.
wxASSERT_MSG( wxFileName( fullFileName ).IsAbsolute(), wxT( "Path is not absolute!" ) );
@ -258,6 +269,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
SetStatusText( wxEmptyString );
ClearMsgPanel();
SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( fullFileName );
// PROJECT::SetProjectFullName() is an impactful function. It should only be
// called under carefully considered circumstances.
@ -265,20 +278,28 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
// it knows what consequences that will have on other KIFACEs running and using
// this same PROJECT. It can be very harmful if that calling code is stupid.
// Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
// They are already saved in the kiface project object.
if( pro.GetFullPath() != Prj().GetProjectFullName()
|| !Prj().GetElem( PROJECT::ELEM_SCH_PART_LIBS ) )
if( schFileType == SCH_IO_MGR::SCH_LEGACY )
{
Prj().SetProjectFullName( pro.GetFullPath() );
// Don't reload the symbol libraries if we are just launching Eeschema from KiCad again.
// They are already saved in the kiface project object.
if( pro.GetFullPath() != Prj().GetProjectFullName()
|| !Prj().GetElem( PROJECT::ELEM_SCH_PART_LIBS ) )
{
Prj().SetProjectFullName( pro.GetFullPath() );
// load the libraries here, not in SCH_SCREEN::Draw() which is a context
// that will not tolerate DisplayError() dialog since we're already in an
// event handler in there.
// And when a schematic file is loaded, we need these libs to initialize
// some parameters (links to PART LIB, dangling ends ...)
// load the libraries here, not in SCH_SCREEN::Draw() which is a context
// that will not tolerate DisplayError() dialog since we're already in an
// event handler in there.
// And when a schematic file is loaded, we need these libs to initialize
// some parameters (links to PART LIB, dangling ends ...)
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
Prj().SchLibs();
}
}
else
{
// No legacy symbol libraries including the cache are loaded with the new file format.
Prj().SetElem( PROJECT::ELEM_SCH_PART_LIBS, NULL );
Prj().SchLibs();
}
LoadProjectFile();
@ -300,7 +321,6 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
delete g_RootSheet; // Delete the current project.
g_RootSheet = NULL; // Force CreateScreens() to build new empty project on load failure.
SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( fullFileName );
SCH_PLUGIN* plugin = SCH_IO_MGR::FindPlugin( schFileType );
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( plugin );
@ -332,10 +352,10 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
m_toolManager->RunAction( ACTIONS::zoomFitScreen, true );
msg.Printf( _( "Error loading schematic file \"%s\".\n%s" ),
GetChars( fullFileName ), GetChars( ioe.What() ) );
fullFileName, ioe.What() );
DisplayError( this, msg );
msg.Printf( _( "Failed to load \"%s\"" ), GetChars( fullFileName ) );
msg.Printf( _( "Failed to load \"%s\"" ), fullFileName );
AppendMsgPanel( wxEmptyString, msg, CYAN );
return false;
@ -358,61 +378,84 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
SCH_SCREENS schematic;
// Convert old projects over to use symbol library table.
if( schematic.HasNoFullyDefinedLibIds() )
// LIB_ID checks and symbol rescue only apply to the legacy file formats.
if( schFileType == SCH_IO_MGR::SCH_LEGACY )
{
DIALOG_SYMBOL_REMAP dlgRemap( this );
dlgRemap.ShowQuasiModal();
}
else
{
// Double check to ensure no legacy library list entries have been
// added to the projec file symbol library list.
wxString paths;
wxArrayString libNames;
PART_LIBS::LibNamesAndPaths( &Prj(), false, &paths, &libNames );
if( !libNames.IsEmpty() )
// Convert old projects over to use symbol library table.
if( schematic.HasNoFullyDefinedLibIds() )
{
if( eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog )
{
wxRichMessageDialog invalidLibDlg(
this,
_( "Illegal entry found in project file symbol library list." ),
_( "Project Load Warning" ),
wxOK | wxCENTER | wxICON_EXCLAMATION );
invalidLibDlg.SetExtendedMessage(
_( "Symbol libraries defined in the project file symbol library list "
"are no longer supported and will be\nremoved. This may cause "
"broken symbol library links under certain conditions." ) );
invalidLibDlg.ShowCheckBox( _( "Do not show this dialog again." ) );
invalidLibDlg.ShowModal();
DIALOG_SYMBOL_REMAP dlgRemap( this );
eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog =
!invalidLibDlg.IsCheckBoxChecked();
dlgRemap.ShowQuasiModal();
}
else
{
// Double check to ensure no legacy library list entries have been
// added to the projec file symbol library list.
wxString paths;
wxArrayString libNames;
PART_LIBS::LibNamesAndPaths( &Prj(), false, &paths, &libNames );
if( !libNames.IsEmpty() )
{
if( eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog )
{
wxRichMessageDialog invalidLibDlg(
this,
_( "Illegal entry found in project file symbol library list." ),
_( "Project Load Warning" ),
wxOK | wxCENTER | wxICON_EXCLAMATION );
invalidLibDlg.ShowDetailedText(
_( "Symbol libraries defined in the project file symbol library "
"list are no longer supported and will be\nremoved. This may "
"cause broken symbol library links under certain conditions." ) );
invalidLibDlg.ShowCheckBox( _( "Do not show this dialog again." ) );
invalidLibDlg.ShowModal();
eeconfig()->m_Appearance.show_illegal_symbol_lib_dialog =
!invalidLibDlg.IsCheckBoxChecked();
}
libNames.Clear();
paths.Clear();
PART_LIBS::LibNamesAndPaths( &Prj(), true, &paths, &libNames );
}
libNames.Clear();
paths.Clear();
PART_LIBS::LibNamesAndPaths( &Prj(), true, &paths, &libNames );
if( !cfg || !cfg->m_RescueNeverShow )
RescueSymbolLibTableProject( false );
}
// Check to see whether some old library parts need to be rescued
// Only do this if RescueNeverShow was not set.
auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
// Update all symbol library links for all sheets.
schematic.UpdateSymbolLinks();
if( !cfg || !cfg->m_RescueNeverShow )
RescueSymbolLibTableProject( false );
if( !cfg || cfg->m_Appearance.show_sexpr_file_convert_warning )
{
wxRichMessageDialog newFileFormatDlg(
this,
_( "The schematic file will be converted to the new file format on save." ),
_( "Project Load Warning" ),
wxOK | wxCENTER | wxICON_EXCLAMATION );
newFileFormatDlg.ShowDetailedText(
_( "This schematic was saved in the legacy file format which is no "
"longer supported and will be saved\nusing the new file format. The "
"new file format cannot be opened with previous versions of KiCad." ) );
newFileFormatDlg.ShowCheckBox( _( "Do not show this dialog again." ) );
newFileFormatDlg.ShowModal();
cfg->m_Appearance.show_sexpr_file_convert_warning =
!newFileFormatDlg.IsCheckBoxChecked();
}
// Allow the schematic to be saved to new file format without making any edits.
OnModify();
}
else // S-expression schematic.
{
for( SCH_SCREEN* screen = schematic.GetFirst(); screen; screen = schematic.GetNext() )
screen->UpdateLocalLibSymbolLinks();
}
g_ConnectionGraph->Reset();
// Update all symbol library links for all sheets.
// NOTE: calls RecalculateConnections( GLOBAL_CLEANUP )
schematic.UpdateSymbolLinks( true ); // Update all symbol library links for all sheets.
SetScreen( g_CurrentSheet->LastScreen() );
// Migrate conflicting bus definitions
@ -426,8 +469,8 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
GetScreen()->TestDanglingEnds(); // Only perform the dangling end test on root sheet.
RecalculateConnections( GLOBAL_CLEANUP );
GetScreen()->ClearUndoORRedoList( GetScreen()->m_UndoList, 1 );
GetScreen()->m_Initialized = true;
}
@ -476,7 +519,7 @@ bool SCH_EDIT_FRAME::AppendSchematic()
wxString path = wxPathOnly( Prj().GetProjectFullName() );
wxFileDialog dlg( this, _( "Append Schematic" ), path, wxEmptyString,
LegacySchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( dlg.ShowModal() == wxID_CANCEL )
return false;
@ -548,6 +591,7 @@ bool SCH_EDIT_FRAME::SaveProject()
SCH_SCREEN* screen;
SCH_SCREENS screenList;
bool success = true;
bool updateFileType = false;
// I want to see it in the debugger, show me the string! Can't do that with wxFileName.
wxString fileName = Prj().AbsolutePath( g_RootSheet->GetFileName() );
@ -561,9 +605,35 @@ bool SCH_EDIT_FRAME::SaveProject()
}
for( screen = screenList.GetFirst(); screen; screen = screenList.GetNext() )
success &= SaveEEFile( screen );
{
// Convert legacy schematics file name extensions for the new format.
wxFileName tmpFn = screen->GetFileName();
CreateArchiveLibraryCacheFile();
if( tmpFn.GetExt() != KiCadSchematicFileExtension )
{
updateFileType = true;
tmpFn.SetExt( KiCadSchematicFileExtension );
for( auto item : screen->Items().OfType( SCH_SHEET_T ) )
{
SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
wxFileName sheetFileName = sheet->GetFileName();
if( sheetFileName.GetExt() == KiCadSchematicFileExtension )
continue;
sheetFileName.SetExt( KiCadSchematicFileExtension );
sheet->SetFileName( sheetFileName.GetFullPath() );
}
screen->SetFileName( tmpFn.GetFullPath() );
}
success &= SaveEEFile( screen );
}
if( updateFileType )
UpdateFileHistory( g_RootSheet->GetScreen()->GetFileName() );
// Save the sheet name map to the project file
wxString configFile = Prj().GetProjectFullName();

View File

@ -85,6 +85,9 @@ int LIB_ITEM::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareF
if( !( aCompareFlags & COMPARE_FLAGS::UNIT ) && m_Convert != aOther.m_Convert )
return m_Convert - m_Convert;
if( m_Fill != aOther.m_Fill )
return m_Fill - aOther.m_Fill;
return 0;
}

View File

@ -786,7 +786,7 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
{
wxFileName fn;
wxString msg;
SCH_IO_MGR::SCH_FILE_T aFileType = SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY;
SCH_IO_MGR::SCH_FILE_T fileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD;
PROJECT& prj = Prj();
m_toolManager->RunAction( ACTIONS::cancelInteractive, true );
@ -808,10 +808,10 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
default_path = search->LastVisitedPath();
fn.SetName( aLibrary );
fn.SetExt( SchematicLibraryFileExtension );
fn.SetExt( KiCadSymbolLibFileExtension );
wxString wildcards = SchematicLibraryFileWildcard();
wildcards += "|" + KiCadSymbolLibFileWildcard();
wxString wildcards = KiCadSymbolLibFileWildcard();
wildcards += "|" + SchematicLibraryFileWildcard();
wxFileDialog dlg( this, wxString::Format( _( "Save Library \"%s\" As..." ), aLibrary ),
default_path, fn.GetFullName(), wildcards,
@ -823,19 +823,20 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
fn = dlg.GetPath();
// Update the file extension and plugin if a different library type was selected.
if( dlg.GetFilterIndex() == 0 )
if( dlg.GetFilterIndex() == 1 )
{
fn.SetExt( SchematicLibraryFileExtension );
fileType = SCH_IO_MGR::SCH_FILE_T::SCH_LEGACY;
}
else
{
fn.SetExt( KiCadSymbolLibFileExtension );
aFileType = SCH_IO_MGR::SCH_FILE_T::SCH_KICAD;
}
}
else
{
fn = prj.SchSymbolLibTable()->GetFullURI( aLibrary );
fileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( fn.GetFullPath() );
}
wxFileName docFileName = fn;
@ -855,7 +856,7 @@ bool LIB_EDIT_FRAME::saveLibrary( const wxString& aLibrary, bool aNewFile )
if( !backupFile( docFileName, "bck" ) )
return false;
if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath(), aFileType ) )
if( !m_libMgr->SaveLibrary( aLibrary, fn.GetFullPath(), fileType ) )
{
msg.Printf( _( "Failed to save changes to symbol library file \"%s\"" ),
fn.GetFullPath() );

View File

@ -573,8 +573,7 @@ bool SCH_EDIT_FRAME::rescueProject( RESCUER& aRescuer, bool aRunningOnDemand )
{
SCH_SCREENS schematic;
schematic.UpdateSymbolLinks( true );
schematic.UpdateSymbolLinks();
g_ConnectionGraph->Reset();
RecalculateConnections( GLOBAL_CLEANUP );
}
@ -885,7 +884,7 @@ bool SYMBOL_LIB_TABLE_RESCUER::WriteRescueLibrary( wxWindow *aParent )
// Update the schematic symbol library links since the library list has changed.
SCH_SCREENS schematic;
schematic.UpdateSymbolLinks( true );
schematic.UpdateSymbolLinks();
return true;
}

View File

@ -23,28 +23,22 @@
*/
#include <fctsys.h>
#include <pgm_base.h>
#include <sch_draw_panel.h>
#include <gr_basic.h>
#include <kicad_string.h>
#include <richio.h>
#include <sch_edit_frame.h>
#include <plotter.h>
#include <msgpanel.h>
#include <bitmaps.h>
#include <general.h>
#include <class_library.h>
#include <lib_rectangle.h>
#include <lib_pin.h>
#include <lib_text.h>
#include <sch_component.h>
#include <sch_sheet.h>
#include <sch_sheet_path.h>
#include <sch_legacy_plugin.h>
#include <netlist_object.h>
#include <lib_item.h>
#include <symbol_lib_table.h>
#include <dialogs/dialog_schematic_find.h>
@ -134,14 +128,11 @@ SCH_COMPONENT::SCH_COMPONENT( LIB_PART& aPart, LIB_ID aLibId, SCH_SHEET_PATH* sh
part = aPart.Flatten();
part->SetParent();
m_part.reset( part.release() );
SetLibSymbol( part.release() );
// Copy fields from the library component
UpdateFields( true, true );
// Update the pin locations
UpdatePins();
// Update the reference -- just the prefix for now.
if( sheet )
SetRef( sheet, aPart.GetReferenceField().GetText() + wxT( "?" ) );
@ -149,6 +140,7 @@ SCH_COMPONENT::SCH_COMPONENT( LIB_PART& aPart, LIB_ID aLibId, SCH_SHEET_PATH* sh
m_prefix = aPart.GetReferenceField().GetText() + wxT( "?" );
}
SCH_COMPONENT::SCH_COMPONENT(
LIB_PART& aPart, SCH_SHEET_PATH* aSheet, COMPONENT_SELECTION& aSel, const wxPoint& pos )
: SCH_COMPONENT( aPart, aSel.LibId, aSheet, aSel.Unit, aSel.Convert, pos )
@ -175,7 +167,7 @@ SCH_COMPONENT::SCH_COMPONENT( const SCH_COMPONENT& aComponent ) :
m_isInNetlist = aComponent.m_isInNetlist;
if( aComponent.m_part )
m_part.reset( new LIB_PART( *aComponent.m_part.get() ) );
SetLibSymbol( new LIB_PART( *aComponent.m_part.get() ) );
const_cast<KIID&>( m_Uuid ) = aComponent.m_Uuid;
@ -188,8 +180,6 @@ SCH_COMPONENT::SCH_COMPONENT( const SCH_COMPONENT& aComponent ) :
for( SCH_FIELD& field : m_Fields )
field.SetParent( this );
UpdatePins();
m_fieldsAutoplaced = aComponent.m_fieldsAutoplaced;
}
@ -236,61 +226,28 @@ void SCH_COMPONENT::ViewGetLayers( int aLayers[], int& aCount ) const
}
void SCH_COMPONENT::SetLibId( const LIB_ID& aLibId, PART_LIBS* aLibs )
void SCH_COMPONENT::SetLibId( const LIB_ID& aLibId )
{
if( m_lib_id != aLibId )
{
m_lib_id = aLibId;
SetModified();
if( aLibs )
{
Resolve( aLibs );
}
else
{
m_part.reset();
m_pins.clear();
m_pinMap.clear();
}
}
}
void SCH_COMPONENT::SetLibId( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aSymLibTable,
PART_LIB* aCacheLib )
wxString SCH_COMPONENT::GetSchSymbolLibraryName() const
{
if( m_lib_id == aLibId )
return;
if( !m_schLibSymbolName.IsEmpty() )
return m_schLibSymbolName;
else
return m_lib_id.Format().wx_str();
}
m_lib_id = aLibId;
SetModified();
std::unique_ptr< LIB_PART > symbol;
if( aSymLibTable && aSymLibTable->HasLibrary( m_lib_id.GetLibNickname() ) )
{
LIB_PART* tmp = aSymLibTable->LoadSymbol( m_lib_id );
if( tmp )
{
symbol = tmp->Flatten();
symbol->SetParent();
}
}
if( !symbol && aCacheLib )
{
LIB_PART* tmp = aCacheLib->FindPart( m_lib_id.Format().wx_str() );
if( tmp )
{
symbol = tmp->Flatten();
symbol->SetParent();
}
}
m_part.reset( symbol.release() );
void SCH_COMPONENT::SetLibSymbol( LIB_PART* aLibSymbol )
{
m_part.reset( aLibSymbol );
UpdatePins();
}
@ -317,138 +274,6 @@ wxString SCH_COMPONENT::GetDatasheet() const
}
bool SCH_COMPONENT::Resolve( PART_LIBS* aLibs )
{
// I've never been happy that the actual individual PART_LIB is left up to
// flimsy search path ordering. None-the-less find a part based on that design:
if( LIB_PART* part = aLibs->FindLibPart( m_lib_id ) )
{
std::unique_ptr< LIB_PART > flattenedPart = part->Flatten();
flattenedPart->SetParent();
m_part.reset( flattenedPart.release() );
UpdatePins();
return true;
}
return false;
}
bool SCH_COMPONENT::Resolve( SYMBOL_LIB_TABLE& aLibTable, PART_LIB* aCacheLib )
{
std::unique_ptr< LIB_PART > part;
try
{
// We want a full symbol not just the top level child symbol.
PROPERTIES props;
props[ SCH_LEGACY_PLUGIN::PropNoDocFile ] = "";
// LIB_TABLE_BASE::LoadSymbol() throws an IO_ERROR if the the library nickname
// is not found in the table so check if the library still exists in the table
// before attempting to load the symbol.
if( m_lib_id.IsValid() && aLibTable.HasLibrary( m_lib_id.GetLibNickname() ) )
{
LIB_PART* tmp = aLibTable.LoadSymbol( m_lib_id );
if( tmp )
{
part = tmp->Flatten();
part->SetParent();
}
}
// Fall back to cache library. This is temporary until the new schematic file
// format is implemented.
if( !part && aCacheLib )
{
wxString libId = m_lib_id.Format().wx_str();
libId.Replace( ":", "_" );
wxLogTrace( traceSymbolResolver,
"Library symbol %s not found falling back to cache library.",
m_lib_id.Format().wx_str() );
LIB_PART* tmp = aCacheLib->FindPart( libId );
if( tmp )
{
part = tmp->Flatten();
part->SetParent();
}
}
if( part )
{
m_part.reset( part.release() );
UpdatePins();
return true;
}
}
catch( const IO_ERROR& ioe )
{
wxLogTrace( traceSymbolResolver, "I/O error %s resolving library symbol %s", ioe.What(),
m_lib_id.Format().wx_str() );
}
wxLogTrace( traceSymbolResolver, "Cannot resolve library symbol %s",
m_lib_id.Format().wx_str() );
m_part.reset();
UpdatePins(); // This will clear the pin map and library symbol pin pointers.
return false;
}
// Helper sort function, used in SCH_COMPONENT::ResolveAll, to sort sch component by lib_id
static bool sort_by_libid( const SCH_COMPONENT* ref, SCH_COMPONENT* cmp )
{
if( ref->GetLibId() == cmp->GetLibId() )
{
if( ref->GetUnit() == cmp->GetUnit() )
return ref->GetConvert() < cmp->GetConvert();
return ref->GetUnit() < cmp->GetUnit();
}
return ref->GetLibId() < cmp->GetLibId();
}
void SCH_COMPONENT::ResolveAll(
std::vector<SCH_COMPONENT*>& aComponents, SYMBOL_LIB_TABLE& aLibTable, PART_LIB* aCacheLib )
{
// sort it by lib part. Cmp will be grouped by same lib part.
std::sort( aComponents.begin(), aComponents.end(), sort_by_libid );
LIB_ID curr_libid;
for( unsigned ii = 0; ii < aComponents.size(); ++ii )
{
SCH_COMPONENT* cmp = aComponents[ii];
curr_libid = cmp->m_lib_id;
cmp->Resolve( aLibTable, aCacheLib );
cmp->UpdatePins();
// Propagate the m_part pointer to other members using the same lib_id
for( unsigned jj = ii + 1; jj < aComponents.size(); ++jj )
{
SCH_COMPONENT* next_cmp = aComponents[jj];
if( curr_libid != next_cmp->m_lib_id )
break;
if( cmp->m_part )
next_cmp->m_part.reset( new LIB_PART( *cmp->m_part.get() ) );
next_cmp->UpdatePins();
ii = jj;
}
}
}
void SCH_COMPONENT::UpdatePins()
{
m_pins.clear();
@ -1140,7 +965,7 @@ void SCH_COMPONENT::SetOrientation( int aOrientation )
default:
transform = false;
wxMessageBox( wxT( "SetRotateMiroir() error: ill value" ) );
wxFAIL_MSG( "Invalid schematic symbol orientation type." );
break;
}
@ -1197,7 +1022,7 @@ int SCH_COMPONENT::GetOrientation()
}
// Error: orientation not found in list (should not happen)
wxMessageBox( wxT( "Component orientation matrix internal error" ) );
wxFAIL_MSG( "Schematic symbol orientation matrix internal error." );
m_transform = transform;
return CMP_NORMAL;
@ -1313,15 +1138,15 @@ void SCH_COMPONENT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, MSG_PANEL_ITEMS& aL
aList.push_back( MSG_PANEL_ITEM( _( "Alias of" ), msg, BROWN ) );
}
if( m_part->GetLib() && m_part->GetLib()->IsCache() )
aList.push_back( MSG_PANEL_ITEM( _( "Library" ),
m_part->GetLib()->GetLogicalName(), RED ) );
else if( !m_lib_id.GetLibNickname().empty() )
{
aList.push_back( MSG_PANEL_ITEM( _( "Library" ), m_lib_id.GetLibNickname(),
BROWN ) );
}
else
{
aList.push_back( MSG_PANEL_ITEM( _( "Library" ), _( "Undefined!!!" ), RED ) );
}
// Display the current associated footprint, if exists.
if( !GetField( FOOTPRINT )->IsVoid() )

View File

@ -93,8 +93,7 @@ struct COMPONENT_INSTANCE_REFERENCE
/**
* SCH_COMPONENT
* describes a real schematic component
* Schematic symbol object.
*/
class SCH_COMPONENT : public SCH_ITEM
{
@ -115,6 +114,17 @@ private:
///< what the component is. Determined, upon placement, from the
///< library component. Created upon file load, by the first
///< non-digits in the reference fields.
/**
* The name used to look up a symbol in the symbol library embedded in a schematic.
*
* By default this is the same as #LIB_ID::GetLibItemName(). However, schematics
* allow for multiple variants of the same library symbol. Set this member In order
* to preserve the link to the original symbol library. If empty, the return of
* #LIB_ID::GetLibItemName() should be used.
*/
wxString m_schLibSymbolName;
TRANSFORM m_transform; ///< The rotation/mirror transformation matrix.
SCH_FIELDS m_Fields; ///< Variable length list of fields.
@ -199,13 +209,42 @@ public:
*/
bool IsMovableFromAnchorPoint() override { return true; }
void SetLibId( const LIB_ID& aName, PART_LIBS* aLibs=NULL );
void SetLibId( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aSymLibTable, PART_LIB* aCacheLib );
void SetLibId( const LIB_ID& aName );
const LIB_ID& GetLibId() const { return m_lib_id; }
/**
* The name of the symbol in the schematic library symbol list.
*
* @note See #SCH_SCREEN::m_libSymbols
*
* The name of the schematic symbol list entry can vary from the item name in the #LIB_ID
* object because the library symbol may have changed so a new name has to be generated
* but the original symbol library link has to be preserved in order to update it from
* the library at some point in the future. If this name is empty, then the library item
* name from #LIB_ID is used.
*/
void SetSchSymbolLibraryName( const wxString& aName ) { m_schLibSymbolName = aName; }
wxString GetSchSymbolLibraryName() const;
bool UseLibIdLookup() const { return m_schLibSymbolName.IsEmpty(); }
std::unique_ptr< LIB_PART >& GetPartRef() { return m_part; }
/**
* Set this schematic symbol library symbol reference to \a aLibSymbol
*
* The schematic symbol object owns \a aLibSymbol and the pin list will be updated
* accordingly. The #LIB_ID object must be valid ( have both a library nickname and
* symbol name ) in order to set the schematic symbol #LIB_ID. Otherwise an assertion
* will be raised in debug builds and the library symbol will be cleared. The new file
* format will no longer require a cache library so all library symbols must be valid.
*
* @note This is the only way to publicly set the library symbol for a schematic
* symbol except for the ctors that take a LIB_PART reference. All previous
* public resolvers have been deprecated.
*/
void SetLibSymbol( LIB_PART* aLibSymbol );
/**
* Return information about the aliased parts
*/
@ -216,18 +255,6 @@ public:
*/
wxString GetDatasheet() const;
/**
* Assigns the current #LIB_PART from \a aLibs which this symbol is based on.
*
* @param aLibs is the current set of LIB_PARTs to choose from.
*/
bool Resolve( PART_LIBS* aLibs );
bool Resolve( SYMBOL_LIB_TABLE& aLibTable, PART_LIB* aCacheLib = NULL );
static void ResolveAll( std::vector<SCH_COMPONENT*>& aComponents, SYMBOL_LIB_TABLE& aLibTable,
PART_LIB* aCacheLib = NULL );
int GetUnit() const { return m_unit; }
/**

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 CERN
* Copyright (C) 2017-2019 Kicad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2020 Kicad Developers, see AUTHORS.txt for contributors.
*
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
* @author Maciej Suminski <maciej.suminski@cern.ch>
@ -1275,17 +1275,20 @@ void SCH_EAGLE_PLUGIN::loadInstance( wxXmlNode* aInstanceNode )
component->GetField( REFERENCE )->SetVisible( false );
}
// Save the pin positions
auto& schLibTable = *m_kiway->Prj().SchSymbolLibTable();
wxCHECK( component->Resolve( schLibTable ), /*void*/ );
SYMBOL_LIB_TABLE& schLibTable = *m_kiway->Prj().SchSymbolLibTable();
LIB_PART* libSymbol = schLibTable.LoadSymbol( component->GetLibId() );
wxCHECK( libSymbol, /*void*/ );
component->SetLibSymbol( libSymbol );
std::vector<LIB_PIN*> pins;
component->GetPins( pins );
for( const auto& pin : pins )
m_connPoints[component->GetPinPhysicalPosition( pin )].emplace( pin );
component->ClearFlags();
screen->Append( component.release() );

View File

@ -768,7 +768,7 @@ void SCH_EDIT_FRAME::NewProject()
{
// Enforce the extension, wxFileDialog is inept.
wxFileName create_me = dlg.GetPath();
create_me.SetExt( LegacySchematicFileExtension );
create_me.SetExt( KiCadSchematicFileExtension );
if( create_me.FileExists() )
{
@ -790,9 +790,9 @@ void SCH_EDIT_FRAME::NewProject()
void SCH_EDIT_FRAME::LoadProject()
{
wxString pro_dir = m_mruPath;
wxString wildcards = LegacySchematicFileWildcard();
wxString wildcards = KiCadSchematicFileWildcard();
wildcards += "|" + KiCadSchematicFileWildcard();
wildcards += "|" + LegacySchematicFileWildcard();
wxFileDialog dlg( this, _( "Open Schematic" ), pro_dir, wxEmptyString,
wildcards, wxFD_OPEN | wxFD_FILE_MUST_EXIST );

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2015 Jean-Pierre Charras, jp.charras wanadoo.fr
* Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
* Copyright (C) 2004-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2004-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -63,6 +63,7 @@ class wxFindReplaceData;
class RESCUER;
class HIERARCHY_NAVIG_DLG;
// @todo Move this to transform alone with all of the transform manipulation code.
/// enum used in RotationMiroir()
enum COMPONENT_ORIENTATION_T {
CMP_NORMAL, // Normal orientation, no rotation or mirror

View File

@ -150,7 +150,7 @@ const wxString SCH_IO_MGR::GetLibraryFileExtension( SCH_FILE_T aFileType )
SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath )
{
SCH_FILE_T ret = SCH_LEGACY; // default guess, unless detected otherwise.
SCH_FILE_T ret = SCH_KICAD; // default guess, unless detected otherwise.
wxFileName fn( aLibPath );
if( fn.GetExt() == SchematicLibraryFileExtension )
@ -168,7 +168,7 @@ SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& a
SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromSchPath( const wxString& aSchematicPath )
{
SCH_FILE_T ret = SCH_LEGACY; // default guess, unless detected otherwise.
SCH_FILE_T ret = SCH_KICAD; // default guess, unless detected otherwise.
wxFileName fn( aSchematicPath );
if( fn.GetExt() == LegacySchematicFileExtension )

View File

@ -39,11 +39,13 @@
#include <pgm_base.h>
#include <plotter.h>
#include <project.h>
#include <reporter.h>
#include <sch_draw_panel.h>
#include <sch_edit_frame.h>
#include <sch_item.h>
#include <class_library.h>
#include <class_libentry.h>
#include <connection_graph.h>
#include <lib_pin.h>
#include <netlist_object.h>
@ -146,11 +148,21 @@ SCH_SCREEN::SCH_SCREEN( KIWAY* aKiway ) :
SCH_SCREEN::~SCH_SCREEN()
{
clearLibSymbols();
ClearUndoRedoList();
FreeDrawList();
}
void SCH_SCREEN::clearLibSymbols()
{
for( auto libSymbol : m_libSymbols )
delete libSymbol.second;
m_libSymbols.clear();
}
void SCH_SCREEN::IncRefCount()
{
m_refCount++;
@ -169,6 +181,47 @@ void SCH_SCREEN::Append( SCH_ITEM* aItem )
{
if( aItem->Type() != SCH_SHEET_PIN_T && aItem->Type() != SCH_FIELD_T )
{
if( aItem->Type() == SCH_COMPONENT_T )
{
SCH_COMPONENT* symbol = static_cast<SCH_COMPONENT*>( aItem );
if( symbol->GetPartRef() )
{
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
if( it == m_libSymbols.end() )
{
m_libSymbols[symbol->GetSchSymbolLibraryName()] =
new LIB_PART( *symbol->GetPartRef() );
}
else
{
// The original library symbol may have changed since the last time
// it was added to the schematic. If it has changed, then a new name
// must be created for the library symbol list to prevent all of the
// other schematic symbols referencing that library symbol from changing.
LIB_PART* foundSymbol = it->second;
if( *foundSymbol != *symbol->GetPartRef() )
{
int cnt = 1;
wxString newName;
newName.Printf( "%s_%d", symbol->GetLibId().Format().wx_str(), cnt );
while( m_libSymbols.find( newName ) != m_libSymbols.end() )
{
cnt += 1;
newName.Printf( "%s_%d", symbol->GetLibId().Format().wx_str(), cnt );
}
symbol->SetSchSymbolLibraryName( newName );
m_libSymbols[newName] = new LIB_PART( *symbol->GetPartRef() );
}
}
}
}
m_rtree.insert( aItem );
--m_modification_sync;
}
@ -191,9 +244,14 @@ void SCH_SCREEN::Append( SCH_SCREEN* aScreen )
void SCH_SCREEN::Clear( bool aFree )
{
if( aFree )
{
FreeDrawList();
clearLibSymbols();
}
else
{
m_rtree.clear();
}
// Clear the project settings
m_ScreenNumber = m_NumberOfScreens = 1;
@ -230,7 +288,39 @@ void SCH_SCREEN::Update( SCH_ITEM* aItem )
bool SCH_SCREEN::Remove( SCH_ITEM* aItem )
{
return m_rtree.remove( aItem );
bool retv = m_rtree.remove( aItem );
// Check if the library symbol for the removed schematic symbol is still required.
if( retv && aItem->Type() == SCH_COMPONENT_T )
{
SCH_COMPONENT* removedSymbol = static_cast<SCH_COMPONENT*>( aItem );
bool removeUnusedLibSymbol = true;
for( SCH_ITEM* item : Items().OfType( SCH_COMPONENT_T ) )
{
SCH_COMPONENT* symbol = static_cast<SCH_COMPONENT*>( item );
if( removedSymbol->GetSchSymbolLibraryName() == symbol->GetSchSymbolLibraryName() )
{
removeUnusedLibSymbol = false;
break;
}
}
if( removeUnusedLibSymbol )
{
auto it = m_libSymbols.find( removedSymbol->GetSchSymbolLibraryName() );
if( it != m_libSymbols.end() )
{
delete it->second;
m_libSymbols.erase( it );
}
}
}
return retv;
}
@ -250,10 +340,8 @@ void SCH_SCREEN::DeleteItem( SCH_ITEM* aItem )
sheet->RemovePin( sheetPin );
return;
}
else
{
delete aItem;
}
delete aItem;
}
@ -481,42 +569,174 @@ bool SCH_SCREEN::IsTerminalPoint( const wxPoint& aPosition, int aLayer )
}
void SCH_SCREEN::UpdateSymbolLinks( bool aForce )
void SCH_SCREEN::UpdateSymbolLinks( REPORTER* aReporter )
{
// Initialize or reinitialize the pointer to the LIB_PART for each component
// found in m_drawList, but only if needed (change in lib or schematic)
// therefore the calculation time is usually very low.
if( !IsEmpty() )
wxString msg;
std::unique_ptr< LIB_PART > libSymbol;
std::vector<SCH_COMPONENT*> symbols;
SYMBOL_LIB_TABLE* libs = Prj().SchSymbolLibTable();
// This will be a nullptr if an s-expression schematic is loaded.
PART_LIBS* legacyLibs = Prj().SchLibs();
for( auto item : Items().OfType( SCH_COMPONENT_T ) )
symbols.push_back( static_cast<SCH_COMPONENT*>( item ) );
// Remove them from the R tree. There bounding box size may change.
for( auto symbol : symbols )
Remove( symbol );
// Clear all existing symbol links.
clearLibSymbols();
for( auto symbol : symbols )
{
std::vector<SCH_COMPONENT*> cmps;
SYMBOL_LIB_TABLE* libs = Prj().SchSymbolLibTable();
int mod_hash = libs->GetModifyHash();
LIB_PART* tmp = nullptr;
libSymbol.reset();
for( SCH_ITEM* aItem : Items().OfType( SCH_COMPONENT_T ) )
cmps.push_back( static_cast<SCH_COMPONENT*>( aItem ) );
// If the symbol is already in the internal library, map the symbol to it.
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
for( SCH_COMPONENT* cmp : cmps )
Remove( cmp );
// Must we resolve?
if( (m_modification_sync != mod_hash) || aForce )
if( ( it != m_libSymbols.end() ) )
{
SCH_COMPONENT::ResolveAll( cmps, *libs, Prj().SchLibs()->GetCacheLibrary() );
if( aReporter )
{
msg.Printf( _( "Setting schematic symbol '%s %s' library identifier "
"to '%s'. " ),
symbol->GetField( REFERENCE )->GetText(),
symbol->GetField( VALUE )->GetText(),
symbol->GetLibId().Format().wx_str() );
aReporter->ReportTail( msg, RPT_SEVERITY_INFO );
}
m_modification_sync = mod_hash; // note the last mod_hash
// Internal library symbols are flattens so just make a copy.
symbol->GetPartRef().reset( new LIB_PART( *it->second ) );
continue;
}
if( !symbol->GetLibId().IsValid() )
{
if( aReporter )
{
msg.Printf( _( "Schematic symbol reference '%s' library identifier is not "
"valid. Unable to link library symbol." ),
symbol->GetLibId().Format().wx_str() );
aReporter->ReportTail( msg, RPT_SEVERITY_WARNING );
}
continue;
}
// LIB_TABLE_BASE::LoadSymbol() throws an IO_ERROR if the the library nickname
// is not found in the table so check if the library still exists in the table
// before attempting to load the symbol.
if( !libs->HasLibrary( symbol->GetLibId().GetLibNickname() ) && !legacyLibs )
{
if( aReporter )
{
msg.Printf( _( "Symbol library '%s' not found and no fallback cache "
"library avaiable. Unable to link library symbol." ),
symbol->GetLibId().GetLibNickname().wx_str() );
aReporter->ReportTail( msg, RPT_SEVERITY_WARNING );
}
continue;
}
if( libs->HasLibrary( symbol->GetLibId().GetLibNickname() ) )
{
try
{
tmp = libs->LoadSymbol( symbol->GetLibId() );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "I/O error %s resolving library symbol %s" ), ioe.What(),
symbol->GetLibId().Format().wx_str() );
aReporter->ReportTail( msg, RPT_SEVERITY_ERROR );
}
}
if( !tmp && legacyLibs )
{
// If here, only the cache library should be loaded if the loaded schematic
// is the legacy file format.
wxCHECK2( legacyLibs->GetLibraryCount() == 1, continue );
PART_LIB& legacyCacheLib = legacyLibs->at( 0 );
// ...and it better be the cache library.
wxCHECK2( legacyCacheLib.IsCache(), continue );
wxString id = symbol->GetLibId().Format();
id.Replace( ':', '_' );
if( aReporter )
{
msg.Printf( _( "Falling back to cache to set symbol '%s:%s' link '%s'." ),
symbol->GetField( REFERENCE )->GetText(),
symbol->GetField( VALUE )->GetText(),
id );
aReporter->ReportTail( msg, RPT_SEVERITY_WARNING );
}
tmp = legacyCacheLib.FindPart( id );
}
if( tmp )
{
// We want a full symbol not just the top level child symbol.
libSymbol = tmp->Flatten();
libSymbol->SetParent();
m_libSymbols.insert( { symbol->GetSchSymbolLibraryName(),
new LIB_PART( *libSymbol.get() ) } );
if( aReporter )
{
msg.Printf( _( "Setting schematic symbol '%s %s' library identifier to '%s'. " ),
symbol->GetField( REFERENCE )->GetText(),
symbol->GetField( VALUE )->GetText(),
symbol->GetLibId().Format().wx_str() );
aReporter->ReportTail( msg, RPT_SEVERITY_INFO );
}
}
// Resolving will update the pin caches but we must ensure that this happens
// even if the libraries don't change.
else
{
for( SCH_COMPONENT* cmp : cmps )
cmp->UpdatePins();
if( aReporter )
{
msg.Printf( _( "No library symbol found for schematic symbol '%s %s'. " ),
symbol->GetField( REFERENCE )->GetText(),
symbol->GetField( VALUE )->GetText() );
aReporter->ReportTail( msg, RPT_SEVERITY_ERROR );
}
}
// Changing the symbol may adjust the bbox of the symbol. This re-inserts the
// item with the new bbox
for( SCH_COMPONENT* cmp : cmps )
Append( cmp );
symbol->SetLibSymbol( libSymbol.release() );
}
// Changing the symbol may adjust the bbox of the symbol. This re-inserts the
// item with the new bbox
for( auto symbol : symbols )
Append( symbol );
}
void SCH_SCREEN::UpdateLocalLibSymbolLinks()
{
for( auto item : Items().OfType( SCH_COMPONENT_T ) )
{
SCH_COMPONENT* symbol = static_cast<SCH_COMPONENT*>( item );
auto it = m_libSymbols.find( symbol->GetSchSymbolLibraryName() );
LIB_PART* libSymbol = nullptr;
if( it != m_libSymbols.end() )
libSymbol = new LIB_PART( *it->second );
symbol->SetLibSymbol( libSymbol );
}
}
@ -932,6 +1152,24 @@ bool SCH_SCREEN::SetComponentFootprint( SCH_SHEET_PATH* aSheetPath, const wxStri
}
void SCH_SCREEN::AddLibSymbol( LIB_PART* aLibSymbol )
{
wxCHECK( aLibSymbol, /* void */ );
wxString libSymbolName = aLibSymbol->GetLibId().Format().wx_str();
auto it = m_libSymbols.find( libSymbolName );
if( it != m_libSymbols.end() )
{
delete it->second;
m_libSymbols.erase( it );
}
m_libSymbols[libSymbolName] = aLibSymbol;
}
void SCH_SCREEN::AddBusAlias( std::shared_ptr<BUS_ALIAS> aAlias )
{
m_aliases.insert( aAlias );
@ -1185,10 +1423,10 @@ void SCH_SCREENS::DeleteAllMarkers( enum MARKER_BASE::TYPEMARKER aMarkerType )
}
void SCH_SCREENS::UpdateSymbolLinks( bool aForce )
void SCH_SCREENS::UpdateSymbolLinks( REPORTER* aReporter )
{
for( SCH_SCREEN* screen = GetFirst(); screen; screen = GetNext() )
screen->UpdateSymbolLinks( aForce );
screen->UpdateSymbolLinks( aReporter );
SCH_SHEET_LIST sheets( g_RootSheet );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -51,11 +51,13 @@
class BUS_ALIAS;
class LIB_PART;
class LIB_PIN;
class SCH_COMPONENT;
class SCH_LINE;
class SCH_TEXT;
class PLOTTER;
class REPORTER;
class SCH_SHEET_LIST;
enum SCH_LINE_TEST_T
@ -115,6 +117,11 @@ private:
/// List of bus aliases stored in this screen
std::unordered_set< std::shared_ptr< BUS_ALIAS > > m_aliases;
/// Library symbols required for this schematic.
std::map<wxString, LIB_PART*> m_libSymbols;
void clearLibSymbols();
public:
/**
@ -221,19 +228,27 @@ public:
void Place( SCH_EDIT_FRAME* frame, wxDC* DC ) { };
/**
* Initialize or reinitialize the weak reference to the #LIB_PART for each #SCH_COMPONENT
* found in m_drawList.
* Initialize the #LIB_PART reference for each #SCH_COMPONENT found in this schematic
* from the project #SYMBOL_LIB_TABLE.
*
* It must be called from:
* - Draw function
* - when loading a schematic file
* - before creating a netlist (in case a library is modified)
* - whenever a symbol library is modified
* - whenever the symbol library table is modified.
* Symbol library links are set using the symbol library table and will fall back to
* the cache only if the cache is loaded. The cache should only be loaded when opening
* legacy schematic files.
*
* @param aForce true forces a refresh even if the library modification has hasn't changed.
* @note This should only be called when the user specifically requests all library symbol
* links to be updated or when the legacy schematic is opened for the last time. All
* subsequent schematic loads with the new s-expression will contain the library
* symbols and should call #UpdateLocalLibSymbolLinks.
*
* @param aReporter Optional #REPORTER object to write status and error messages into.
*/
void UpdateSymbolLinks( bool aForce = false );
void UpdateSymbolLinks( REPORTER* aReporter = nullptr );
/**
* Initialize the #LIB_PART reference for each #SCH_COMPONENT found in this schematic
* with the local project library symbols
*/
void UpdateLocalLibSymbolLinks();
/**
* Print all the items in the screen to \a aDC.
@ -449,6 +464,25 @@ public:
bool SetComponentFootprint( SCH_SHEET_PATH* aSheetPath, const wxString& aReference,
const wxString& aFootPrint, bool aSetVisible );
/**
* Fetch a list of unique #LIB_PART object pointers required to properly render each
* #SCH_COMPONENT in this schematic.
*
* @return The list of unique #LIB_PART object pointers.
*/
std::map<wxString, LIB_PART*>& GetLibSymbols() { return m_libSymbols; }
/**
* Add \a aLibSymbol to the the library symbol map.
*
* The symbol is mapped to the result of #LIB_ID::Format(). If a symbol is already
* mapped, the existing symbol is replaced with \a aLibSymbol. The screen object takes
* ownership of the pointer.
*
* @param aLibSymbol A pointer the #LIB_PART to be added to the symbol map.
*/
void AddLibSymbol( LIB_PART* aLibSymbol );
/**
* Adds a bus alias definition (and transfers ownership of the pointer)
*/
@ -548,17 +582,17 @@ public:
void DeleteMarker( SCH_MARKER* aMarker );
/**
* Initialize or reinitialize the weak reference to the #LIB_PART for each #SCH_COMPONENT
* found in the full schematic.
* Initialize the #LIB_PART reference for each #SCH_COMPONENT found in the full schematic.
*
* It must be called from:
* - draw functions
* - when loading a schematic file
* - before creating a netlist (in case a library is modified)
* - whenever any of the libraries are modified.
* - whenever the symbol library table is modified.
* @note This should only be called when the user specifically requests all library symbol
* links to be update or when the legacy schematic is opened for the last time. All
* subsequent schematic loads with the new s-expression will contain the library
* symbols.
*
* @param aReporter An optional #REPORTER object pointer to write warning and error
* messages into.
*/
void UpdateSymbolLinks( bool aForce = false );
void UpdateSymbolLinks( REPORTER* aReporter = nullptr );
void TestDanglingEnds();

View File

@ -106,15 +106,15 @@ void SCH_SEXPR_PARSER::ParseLib( LIB_PART_MAP& aSymbolLibMap )
}
LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap, bool aIsSchematicLib )
{
wxCHECK_MSG( CurTok() == T_symbol, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
T token;
long tmp;
wxString error;
wxString name;
wxString error;
LIB_ITEM* item;
std::unique_ptr<LIB_PART> symbol( new LIB_PART( wxEmptyString ) );
@ -133,15 +133,18 @@ LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
name = FromUTF8();
if( name.IsEmpty() )
LIB_ID id;
if( id.Parse( name, LIB_ID::ID_SCH ) >= 0 )
{
error.Printf( _( "Empty symbol name in\nfile: \"%s\"\nline: %d\noffset: %d" ),
error.Printf( _( "Invalid library identifier in\nfile: \"%s\"\nline: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
m_symbolName = name;
symbol->SetName( name );
m_symbolName = id.GetLibItemName().wx_str();
symbol->SetName( m_symbolName );
symbol->SetLibId( id );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
@ -152,6 +155,11 @@ LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
switch( token )
{
case T_power:
symbol->SetPower();
NeedRIGHT();
break;
case T_pin_names:
parsePinNames( symbol );
break;
@ -215,7 +223,8 @@ LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
if( !name.StartsWith( m_symbolName ) )
{
error.Printf(
_( "Invalid symbol unit name prefix %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
_( "Invalid symbol unit name prefix %s in\nfile: \"%s\"\n"
"line: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
@ -227,7 +236,8 @@ LIB_PART* SCH_SEXPR_PARSER::ParseSymbol( LIB_PART_MAP& aSymbolLibMap )
if( tokenizer.CountTokens() != 2 )
{
error.Printf(
_( "Invalid symbol unit name suffix %s in\nfile: \"%s\"\nline: %d\noffset: %d" ),
_( "Invalid symbol unit name suffix %s in\nfile: \"%s\"\n"
"line: %d\noffset: %d" ),
name.c_str(), CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
@ -1615,7 +1625,7 @@ void SCH_SEXPR_PARSER::parseTITLE_BLOCK( TITLE_BLOCK& aTitleBlock )
}
SCH_FIELD* SCH_SEXPR_PARSER::parseSchField()
SCH_FIELD* SCH_SEXPR_PARSER::parseSchField( SCH_COMPONENT* aParentSymbol )
{
wxCHECK_MSG( CurTok() == T_property, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) +
@ -1656,7 +1666,7 @@ SCH_FIELD* SCH_SEXPR_PARSER::parseSchField()
value = FromUTF8();
std::unique_ptr<SCH_FIELD> field( new SCH_FIELD( wxDefaultPosition, MANDATORY_FIELDS,
nullptr, name ) );
aParentSymbol, name ) );
field->SetText( value );
field->SetVisible( true );
@ -1882,6 +1892,32 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
break;
}
case T_lib_symbols:
{
// Dummy map. No derived symbols are allowed in the library cache.
LIB_PART_MAP symbolLibMap;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_symbol:
aScreen->AddLibSymbol( ParseSymbol( symbolLibMap, true ) );
break;
default:
Expecting( "symbol" );
}
}
break;
}
case T_symbol:
aScreen->Append( static_cast<SCH_ITEM*>( parseSchematicSymbol() ) );
break;
@ -1924,6 +1960,8 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SCREEN* aScreen )
"bus, text, label, global_label, or hierarchical_label" );
}
}
aScreen->UpdateLocalLibSymbolLinks();
}
@ -1933,10 +1971,12 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a symbol." ) );
T token;
int orientation = CMP_ORIENT_0;
wxString tmp;
wxString error;
wxString libName;
SCH_FIELD* field;
std::unique_ptr<SCH_COMPONENT> symbol( new SCH_COMPONENT() );
TRANSFORM transform;
m_fieldId = MANDATORY_FIELDS;
@ -1949,6 +1989,25 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
switch( token )
{
case T_lib_name:
{
LIB_ID libId;
token = NextTok();
if( !IsSymbol( token ) )
{
error.Printf( _( "Invalid symbol library name in\nfile: \"%s\"\n"
"line: %d\noffset: %d" ),
CurSource().c_str(), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
libName = FromUTF8();
NeedRIGHT();
break;
}
case T_lib_id:
{
token = NextTok();
@ -1956,18 +2015,17 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
if( !IsSymbol( token ) && token != T_NUMBER )
Expecting( "symbol|number" );
LIB_ID id;
wxString text = FromUTF8();
LIB_ID libId;
if( !text.IsEmpty() && id.Parse( text, LIB_ID::ID_SCH, true ) >= 0 )
if( libId.Parse( FromUTF8(), LIB_ID::ID_SCH ) >= 0 )
{
tmp.Printf( _( "Invalid symbol library ID in\nfile: \"%s\"\nline: %d\n"
"offset: %d" ),
GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( tmp );
error.Printf( _( "Invalid symbol library ID in\nfile: \"%s\"\nline: %d\n"
"offset: %d" ),
GetChars( CurSource() ), CurLineNumber(), CurOffset() );
THROW_IO_ERROR( error );
}
symbol->SetLibId( id );
symbol->SetLibId( libId );
NeedRIGHT();
break;
}
@ -1977,13 +2035,14 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
switch( static_cast<int>( parseDouble( "symbol orientation" ) ) )
{
case 0: orientation = CMP_ORIENT_0; break;
case 90: orientation = CMP_ORIENT_90; break;
case 180: orientation = CMP_ORIENT_180; break;
case 270: orientation = CMP_ORIENT_270; break;
case 0: transform = TRANSFORM(); break;
case 90: transform = TRANSFORM( 0, -1, -1, 0 ); break;
case 180: transform = TRANSFORM( -1, 0, 0, 1 ); break;
case 270: transform = TRANSFORM( 0, 1, 1, 0 ); break;
default: Expecting( "0, 90, 180, or 270" );
}
symbol->SetTransform( transform );
NeedRIGHT();
break;
@ -1991,9 +2050,9 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
token = NextTok();
if( token == T_x )
orientation |= CMP_MIRROR_X;
symbol->SetOrientation( CMP_MIRROR_X );
else if( token == T_y )
orientation |= CMP_MIRROR_Y;
symbol->SetOrientation( CMP_MIRROR_Y );
else
Expecting( "x or y" );
@ -2013,8 +2072,9 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
case T_property:
{
field = parseSchField();
field->SetParent( symbol.get() );
// The field parent symbol must be set and it's orientation must be set before
// the field positions are set.
field = parseSchField( symbol.get() );
if( field->GetId() == REFERENCE )
{
@ -2039,11 +2099,12 @@ SCH_COMPONENT* SCH_SEXPR_PARSER::parseSchematicSymbol()
break;
default:
Expecting( "lib_id, at, mirror, uuid, property, or instances" );
Expecting( "lib_id, lib_name, at, mirror, uuid, property, or instances" );
}
}
symbol->SetOrientation( orientation );
if( !libName.IsEmpty() && ( symbol->GetLibId().Format().wx_str() != libName ) )
symbol->SetSchSymbolLibraryName( libName );
return symbol.release();
}
@ -2167,7 +2228,7 @@ SCH_SHEET* SCH_SEXPR_PARSER::parseSheet()
break;
case T_property:
field = parseSchField();
field = parseSchField( nullptr );
if( field->GetName() == "ki_sheet_name" )
{

View File

@ -206,7 +206,7 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
void parseSchSymbolInstances( std::unique_ptr<SCH_COMPONENT>& aSymbol );
SCH_SHEET_PIN* parseSchSheetPin( SCH_SHEET* aSheet );
SCH_FIELD* parseSchField();
SCH_FIELD* parseSchField( SCH_COMPONENT* aParentSymbol );
SCH_COMPONENT* parseSchematicSymbol();
SCH_BITMAP* parseImage();
SCH_SHEET* parseSheet();
@ -221,7 +221,7 @@ public:
void ParseLib( LIB_PART_MAP& aSymbolLibMap );
LIB_PART* ParseSymbol( LIB_PART_MAP& aSymbolLibMap );
LIB_PART* ParseSymbol( LIB_PART_MAP& aSymbolLibMap, bool aIsSchematicLib = false );
LIB_ITEM* ParseDrawItem();

View File

@ -391,7 +391,7 @@ public:
static LIB_PART* LoadPart( LINE_READER& aReader, int aMajorVersion, int aMinorVersion,
LIB_PART_MAP* aMap = nullptr );
static void SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter,
int aNestLevel = 0, LIB_PART_MAP* aMap = nullptr );
int aNestLevel = 0, const wxString& aLibName = wxEmptyString );
};
@ -604,12 +604,18 @@ void SCH_SEXPR_PLUGIN::Format( SCH_SCREEN* aScreen )
SEXPR_SCHEMATIC_FILE_VERSION,
m_out->Quotew( GetBuildVersion() ).c_str() );
// @todo save cache library here.
aScreen->GetPageSettings().Format( m_out, 1, 0 );
m_out->Print( 0, "\n" );
aScreen->GetTitleBlock().Format( m_out, 1, 0 );
// Save cache library.
m_out->Print( 1, "(lib_symbols\n" );
for( auto libSymbol : aScreen->GetLibSymbols() )
SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( libSymbol.second, *m_out, 2, libSymbol.first );
m_out->Print( 1, ")\n\n" );
// @todo save schematic instance information (page #).
for( const auto& alias : aScreen->GetBusAliases() )
@ -786,8 +792,14 @@ void SCH_SEXPR_PLUGIN::saveSymbol( SCH_COMPONENT* aSymbol, int aNestLevel )
else
angle = 0.0;
m_out->Print( aNestLevel, "(symbol (lib_id %s) (at %s %s %s)",
m_out->Quotew( libName ).c_str(),
m_out->Print( aNestLevel, "(symbol" );
if( !aSymbol->UseLibIdLookup() )
m_out->Print( 0, " (lib_name %s)",
m_out->Quotew( aSymbol->GetSchSymbolLibraryName() ).c_str() );
m_out->Print( 0, " (lib_id %s) (at %s %s %s)",
m_out->Quotew( aSymbol->GetLibId().Format().wx_str() ).c_str(),
FormatInternalUnits( aSymbol->GetPosition().x ).c_str(),
FormatInternalUnits( aSymbol->GetPosition().y ).c_str(),
FormatAngle( angle * 10.0 ).c_str() );
@ -875,13 +887,14 @@ void SCH_SEXPR_PLUGIN::saveField( SCH_FIELD* aField, int aNestLevel )
// For some reason (bug in legacy parser?) the field ID for non-mandatory fields is -1 so
// check for this in order to correctly use the field name.
if( aField->GetId() >= 0 && aField->GetId() < MANDATORY_FIELDS )
fieldName = "ki_" + TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId() ).Lower();
fieldName = TEMPLATE_FIELDNAME::GetDefaultFieldName( aField->GetId() );
else
fieldName = aField->GetName();
m_out->Print( aNestLevel, "(property %s %s (at %s %s %s)",
m_out->Print( aNestLevel, "(property %s %s (id %d) (at %s %s %s)",
m_out->Quotew( fieldName ).c_str(),
m_out->Quotew( aField->GetText() ).c_str(),
aField->GetId(),
FormatInternalUnits( aField->GetPosition().x ).c_str(),
FormatInternalUnits( aField->GetPosition().y ).c_str(),
FormatAngle( aField->GetTextAngleDegrees() * 10.0 ).c_str() );
@ -1130,7 +1143,7 @@ void SCH_SEXPR_PLUGIN::saveText( SCH_TEXT* aText, int aNestLevel )
|| ( aText->GetTextHeight() != Mils2iu( DEFAULT_SIZE_TEXT ) ) )
{
m_out->Print( 0, "\n" );
aText->Format( m_out, aNestLevel + 1, 0 );
aText->Format( m_out, aNestLevel, 0 );
m_out->Print( aNestLevel, ")\n" ); // Closes text token with font effects.
}
else
@ -1394,17 +1407,30 @@ void SCH_SEXPR_PLUGIN_CACHE::Save()
void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFormatter,
int aNestLevel, LIB_PART_MAP* aMap )
int aNestLevel, const wxString& aLibName )
{
wxCHECK_RET( aSymbol, "Invalid LIB_PART pointer." );
std::string name = aFormatter.Quotew( aSymbol->GetLibId().Format().wx_str() );
std::string unitName = aSymbol->GetLibId().GetLibItemName();
if( !aLibName.IsEmpty() )
{
name = aFormatter.Quotew( aLibName );
LIB_ID unitId;
wxCHECK2( unitId.Parse( aLibName, LIB_ID::ID_SCH ) < 0, /* do nothing */ );
unitName = unitId.GetLibItemName();
}
if( aSymbol->IsRoot() )
{
aFormatter.Print( aNestLevel, "(symbol %s",
aFormatter.Quotew( aSymbol->GetName() ).c_str() );
aFormatter.Print( aNestLevel, "(symbol %s", name.c_str() );
if( aSymbol->IsPower() )
aFormatter.Print( 0, " power" );
aFormatter.Print( 0, " (power)" );
// TODO: add uuid token here.
@ -1461,8 +1487,7 @@ void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFo
for( auto unit : units )
{
aFormatter.Print( aNestLevel + 1, "(symbol \"%s_%d_%d\"\n",
TO_UTF8( aSymbol->GetName() ),
unit.m_unit, unit.m_convert );
unitName.c_str(), unit.m_unit, unit.m_convert );
for( auto item : unit.m_items )
saveSymbolDrawItem( item, aFormatter, aNestLevel + 2 );
@ -1477,7 +1502,7 @@ void SCH_SEXPR_PLUGIN_CACHE::SaveSymbol( LIB_PART* aSymbol, OUTPUTFORMATTER& aFo
wxASSERT( parent );
aFormatter.Print( aNestLevel, "(symbol %s (extends %s)\n",
aFormatter.Quotew( aSymbol->GetName() ).c_str(),
name.c_str(),
aFormatter.Quotew( parent->GetName() ).c_str() );
LIB_FIELD tmp = parent->GetValueField();

View File

@ -51,6 +51,8 @@ label
left
length
lib_id
lib_name
lib_symbols
line
mid
mirror

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2004-2019 KiCad Developers, see change_log.txt for contributors.
* Copyright (C) 2004-2020 KiCad Developers, see change_log.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -111,7 +111,8 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
wxFileName currentSheetFileName;
bool libTableChanged = false;
SCH_SCREEN* currentScreen = aHierarchy->LastScreen();
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_LEGACY ) );
SCH_IO_MGR::SCH_FILE_T schFileType = SCH_IO_MGR::GuessPluginTypeFromSchPath( aFileName );
SCH_PLUGIN::SCH_PLUGIN_RELEASER pi( SCH_IO_MGR::FindPlugin( schFileType ) );
std::unique_ptr< SCH_SHEET> newSheet( new SCH_SHEET );
wxFileName fileName( aFileName );
@ -469,9 +470,6 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aHier
SCH_SCREENS allScreens;
allScreens.ReplaceDuplicateTimeStamps();
SCH_SCREENS screens( aSheet );
screens.UpdateSymbolLinks( true );
return true;
}

View File

@ -440,10 +440,9 @@ int LIB_CONTROL::AddSymbolToSchematic( const TOOL_EVENT& aEvent )
return 0;
}
SCH_COMPONENT* comp = new SCH_COMPONENT( *part, libId, g_CurrentSheet, unit, convert );
wxCHECK( part->GetLibId().IsValid(), 0 );
// Be sure the link to the corresponding LIB_PART is OK:
comp->Resolve( *m_frame->Prj().SchSymbolLibTable() );
SCH_COMPONENT* comp = new SCH_COMPONENT( *part, libId, g_CurrentSheet, unit, convert );
if( schframe->eeconfig()->m_AutoplaceFields.enable )
comp->AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -174,9 +174,6 @@ int SCH_DRAWING_TOOLS::PlaceComponent( const TOOL_EVENT& aEvent )
component = new SCH_COMPONENT( *part, g_CurrentSheet, sel, (wxPoint) cursorPos );
component->SetFlags( IS_NEW | IS_MOVED );
// Be sure the link to the corresponding LIB_PART is OK:
component->Resolve( *m_frame->Prj().SchSymbolLibTable() );
if( m_frame->eeconfig()->m_AutoplaceFields.enable )
component->AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -1174,9 +1174,7 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
paste_screen.Clear( false );
// Now we can resolve the components and add everything to the screen, view, etc.
//
SYMBOL_LIB_TABLE* symLibTable = m_frame->Prj().SchSymbolLibTable();
PART_LIB* partLib = m_frame->Prj().SchLibs()->GetCacheLibrary();
for( unsigned i = 0; i < loadedItems.size(); ++i )
{
@ -1196,8 +1194,19 @@ int SCH_EDITOR_CONTROL::Paste( const TOOL_EVENT& aEvent )
component->SetUnit( unit );
}
component->Resolve( *symLibTable, partLib );
component->UpdatePins();
LIB_PART* libSymbol = symLibTable->LoadSymbol( component->GetLibId() );
if( libSymbol )
{
component->SetLibSymbol( libSymbol );
}
else
{
DisplayError( m_frame,
wxString::Format( _( "Symbol '%s' not found in library '%s'." ),
component->GetLibId().GetLibItemName().wx_str(),
component->GetLibId().GetLibNickname().wx_str() ) );
}
}
else
{

View File

@ -225,6 +225,15 @@ const wxString KICAD_MANAGER_FRAME::SchFileName()
{
wxFileName fn( GetProjectFileName() );
fn.SetExt( KiCadSchematicFileExtension );
return fn.GetFullPath();
}
const wxString KICAD_MANAGER_FRAME::SchLegacyFileName()
{
wxFileName fn( GetProjectFileName() );
fn.SetExt( LegacySchematicFileExtension );
return fn.GetFullPath();
}

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN (www.cern.ch)
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -156,6 +156,7 @@ public:
// read only accessors
const wxString SchFileName();
const wxString SchLegacyFileName();
const wxString PcbFileName();
const wxString PcbLegacyFileName();

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019 CERN
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -612,7 +612,13 @@ int KICAD_MANAGER_CONTROL::ShowPlayer( const TOOL_EVENT& aEvent )
if( playerType == FRAME_SCH )
{
filepath = m_frame->SchFileName();
wxFileName kicad_schematic( m_frame->SchFileName() );
wxFileName legacy_schematic( m_frame->SchLegacyFileName() );
if( !legacy_schematic.FileExists() || kicad_schematic.FileExists() )
filepath = kicad_schematic.GetFullPath();
else
filepath = legacy_schematic.GetFullPath();
}
else if( playerType == FRAME_PCB_EDITOR )
{

View File

@ -48,6 +48,7 @@ set( QA_EESCHEMA_SRCS
test_sch_rtree.cpp
test_sch_sheet.cpp
test_sch_sheet_path.cpp
test_sch_symbol.cpp
)

View File

@ -0,0 +1,84 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 KiCad Developers, see CHANGELOG.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file
* Test suite for SCH_COMPONENT object.
*/
#include <unit_test_utils/unit_test_utils.h>
// Code under test
#include <sch_component.h>
#include <sch_edit_frame.h>
class TEST_SCH_SYMBOL_FIXTURE
{
public:
TEST_SCH_SYMBOL_FIXTURE()
{
}
///> #SCH_COMPONENT object with no extra data set.
SCH_COMPONENT m_symbol;
};
/**
* Declare the test suite
*/
BOOST_FIXTURE_TEST_SUITE( SchSymbol, TEST_SCH_SYMBOL_FIXTURE )
/**
* Check that we can get the default properties as expected.
*/
BOOST_AUTO_TEST_CASE( DefaultProperties )
{
}
/**
* Test the orientation tranform changes.
*/
BOOST_AUTO_TEST_CASE( Orientation )
{
TRANSFORM t = m_symbol.GetTransform();
wxLogDebug( "Angle 0: x1 = %d, y1 = %d, x2 = %d, y2 = %d", t.x1, t.y1, t.x2, t.y2 );
m_symbol.SetOrientation( CMP_ORIENT_90 );
t = m_symbol.GetTransform();
wxLogDebug( "Angle 90: x1 = %d, y1 = %d, x2 = %d, y2 = %d", t.x1, t.y1, t.x2, t.y2 );
m_symbol.SetTransform( TRANSFORM() );
m_symbol.SetOrientation( CMP_ORIENT_180 );
t = m_symbol.GetTransform();
wxLogDebug( "Angle 180: x1 = %d, y1 = %d, x2 = %d, y2 = %d", t.x1, t.y1, t.x2, t.y2 );
m_symbol.SetTransform( TRANSFORM() );
m_symbol.SetOrientation( CMP_ORIENT_270 );
t = m_symbol.GetTransform();
wxLogDebug( "Angle 270: x1 = %d, y1 = %d, x2 = %d, y2 = %d", t.x1, t.y1, t.x2, t.y2 );
}
BOOST_AUTO_TEST_SUITE_END()