diff --git a/eeschema/class_libentry.h b/eeschema/class_libentry.h index 695b612711..040a6e3048 100644 --- a/eeschema/class_libentry.h +++ b/eeschema/class_libentry.h @@ -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 ); diff --git a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp index a7102cf673..481e16e19a 100644 --- a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp +++ b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp @@ -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 components; diff --git a/eeschema/dialogs/dialog_edit_components_libid.cpp b/eeschema/dialogs/dialog_edit_components_libid.cpp index 861df2635d..851cc96572 100644 --- a/eeschema/dialogs/dialog_edit_components_libid.cpp +++ b/eeschema/dialogs/dialog_edit_components_libid.cpp @@ -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(); } } diff --git a/eeschema/dialogs/dialog_symbol_remap.cpp b/eeschema/dialogs/dialog_symbol_remap.cpp index 5f92e3aff0..f078d25179 100644 --- a/eeschema/dialogs/dialog_symbol_remap.cpp +++ b/eeschema/dialogs/dialog_symbol_remap.cpp @@ -6,7 +6,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Wayne Stambaugh - * 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; } } diff --git a/eeschema/dialogs/panel_sym_lib_table.cpp b/eeschema/dialogs/panel_sym_lib_table.cpp index adf6d5c699..f242a557b6 100644 --- a/eeschema/dialogs/panel_sym_lib_table.cpp +++ b/eeschema/dialogs/panel_sym_lib_table.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Wayne Stambaugh - * 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(); diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp index 901b804e1e..6ec23b1c73 100644 --- a/eeschema/eeschema_settings.cpp +++ b/eeschema/eeschema_settings.cpp @@ -68,7 +68,11 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() : APP_SETTINGS_BASE( "eeschema", eeschema m_params.emplace_back( new PARAM( "appearance.show_page_limits", &m_Appearance.show_page_limits, true ) ); - m_params.emplace_back( new PARAM( "appearance.show_sheet_filename_case_sensitivity_dialog", + m_params.emplace_back( new PARAM( "appearance.show_sexpr_file_convert_warning", + &m_Appearance.show_sexpr_file_convert_warning, true ) ); + + m_params.emplace_back( + new PARAM( "appearance.show_sheet_filename_case_sensitivity_dialog", &m_Appearance.show_sheet_filename_case_sensitivity_dialog, true ) ); m_params.emplace_back( new PARAM( "autoplace_fields.enable", @@ -529,4 +533,4 @@ bool EESCHEMA_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg ) libedit->Load(); return ret; -} \ No newline at end of file +} diff --git a/eeschema/eeschema_settings.h b/eeschema/eeschema_settings.h index cf6df58b59..6e55175630 100644 --- a/eeschema/eeschema_settings.h +++ b/eeschema/eeschema_settings.h @@ -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; }; diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 7496156eec..0a8658bfe6 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -54,6 +54,7 @@ #include #include + 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& aFileSet, int aCtl ) { // implement the pseudo code from KIWAY_PLAYER.h: - wxString msg; + auto cfg = dynamic_cast( Kiface().KifaceSettings() ); + // This is for python: if( aFileSet.size() != 1 ) { @@ -208,7 +219,7 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector& 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& 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& 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& 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& 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& 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( 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& 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( 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(); diff --git a/eeschema/lib_item.cpp b/eeschema/lib_item.cpp index 764cdd45fa..ec1ef93196 100644 --- a/eeschema/lib_item.cpp +++ b/eeschema/lib_item.cpp @@ -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; } diff --git a/eeschema/libedit/libedit.cpp b/eeschema/libedit/libedit.cpp index 1250c1f321..50bb012723 100644 --- a/eeschema/libedit/libedit.cpp +++ b/eeschema/libedit/libedit.cpp @@ -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() ); diff --git a/eeschema/project_rescue.cpp b/eeschema/project_rescue.cpp index ba12e82dd0..a7e9842108 100644 --- a/eeschema/project_rescue.cpp +++ b/eeschema/project_rescue.cpp @@ -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; } diff --git a/eeschema/sch_component.cpp b/eeschema/sch_component.cpp index 912e47e521..7e23640437 100644 --- a/eeschema/sch_component.cpp +++ b/eeschema/sch_component.cpp @@ -23,28 +23,22 @@ */ #include -#include -#include #include #include -#include #include #include #include #include #include -#include #include #include #include #include #include #include -#include #include #include -#include #include @@ -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( 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& 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() ) diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index b41786f9dc..cb818eab49 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -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& aComponents, SYMBOL_LIB_TABLE& aLibTable, - PART_LIB* aCacheLib = NULL ); - int GetUnit() const { return m_unit; } /** diff --git a/eeschema/sch_eagle_plugin.cpp b/eeschema/sch_eagle_plugin.cpp index 5c241aff1e..21da339a25 100644 --- a/eeschema/sch_eagle_plugin.cpp +++ b/eeschema/sch_eagle_plugin.cpp @@ -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 * @author Maciej Suminski @@ -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 pins; component->GetPins( pins ); for( const auto& pin : pins ) m_connPoints[component->GetPinPhysicalPosition( pin )].emplace( pin ); - component->ClearFlags(); screen->Append( component.release() ); diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp index fd912fba4e..0612bd016d 100644 --- a/eeschema/sch_edit_frame.cpp +++ b/eeschema/sch_edit_frame.cpp @@ -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 ); diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index 2156fa5ba4..0eb0e6bc45 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras wanadoo.fr * Copyright (C) 2008 Wayne Stambaugh - * 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 diff --git a/eeschema/sch_io_mgr.cpp b/eeschema/sch_io_mgr.cpp index 37a0c457a5..1377aa4030 100644 --- a/eeschema/sch_io_mgr.cpp +++ b/eeschema/sch_io_mgr.cpp @@ -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 ) diff --git a/eeschema/sch_screen.cpp b/eeschema/sch_screen.cpp index 9ddd82b6a8..71277197c6 100644 --- a/eeschema/sch_screen.cpp +++ b/eeschema/sch_screen.cpp @@ -39,11 +39,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -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( 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( aItem ); + + bool removeUnusedLibSymbol = true; + + for( SCH_ITEM* item : Items().OfType( SCH_COMPONENT_T ) ) + { + SCH_COMPONENT* symbol = static_cast( 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 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( 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 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( 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( 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 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 ); diff --git a/eeschema/sch_screen.h b/eeschema/sch_screen.h index b15da5a41e..cac52e3e05 100644 --- a/eeschema/sch_screen.h +++ b/eeschema/sch_screen.h @@ -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 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& 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(); diff --git a/eeschema/sch_sexpr_parser.cpp b/eeschema/sch_sexpr_parser.cpp index b97d93a09e..5b8b330f4f 100644 --- a/eeschema/sch_sexpr_parser.cpp +++ b/eeschema/sch_sexpr_parser.cpp @@ -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 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 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( 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 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( 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" ) { diff --git a/eeschema/sch_sexpr_parser.h b/eeschema/sch_sexpr_parser.h index 7715d6519f..0d65e2866b 100644 --- a/eeschema/sch_sexpr_parser.h +++ b/eeschema/sch_sexpr_parser.h @@ -206,7 +206,7 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER void parseSchSymbolInstances( std::unique_ptr& 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(); diff --git a/eeschema/sch_sexpr_plugin.cpp b/eeschema/sch_sexpr_plugin.cpp index 1574f8c4ce..745d6727c2 100644 --- a/eeschema/sch_sexpr_plugin.cpp +++ b/eeschema/sch_sexpr_plugin.cpp @@ -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(); diff --git a/eeschema/schematic.keywords b/eeschema/schematic.keywords index a5883c92a4..9a7b2bdff8 100644 --- a/eeschema/schematic.keywords +++ b/eeschema/schematic.keywords @@ -51,6 +51,8 @@ label left length lib_id +lib_name +lib_symbols line mid mirror diff --git a/eeschema/sheet.cpp b/eeschema/sheet.cpp index b91e131b67..e245527da7 100644 --- a/eeschema/sheet.cpp +++ b/eeschema/sheet.cpp @@ -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; } diff --git a/eeschema/tools/lib_control.cpp b/eeschema/tools/lib_control.cpp index 3c1aa97fc7..0a0ca19482 100644 --- a/eeschema/tools/lib_control.cpp +++ b/eeschema/tools/lib_control.cpp @@ -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 ); diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp index 724aa73deb..fb03c46a24 100644 --- a/eeschema/tools/sch_drawing_tools.cpp +++ b/eeschema/tools/sch_drawing_tools.cpp @@ -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 ); diff --git a/eeschema/tools/sch_editor_control.cpp b/eeschema/tools/sch_editor_control.cpp index 60d4e2ae98..deacad4726 100644 --- a/eeschema/tools/sch_editor_control.cpp +++ b/eeschema/tools/sch_editor_control.cpp @@ -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 { diff --git a/kicad/kicad_manager_frame.cpp b/kicad/kicad_manager_frame.cpp index 281ba36fab..411fcce721 100644 --- a/kicad/kicad_manager_frame.cpp +++ b/kicad/kicad_manager_frame.cpp @@ -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(); } diff --git a/kicad/kicad_manager_frame.h b/kicad/kicad_manager_frame.h index e7fd874603..7f5a868fe0 100644 --- a/kicad/kicad_manager_frame.h +++ b/kicad/kicad_manager_frame.h @@ -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(); diff --git a/kicad/tools/kicad_manager_control.cpp b/kicad/tools/kicad_manager_control.cpp index 51d1126e6e..fdb1b5d52c 100644 --- a/kicad/tools/kicad_manager_control.cpp +++ b/kicad/tools/kicad_manager_control.cpp @@ -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 ) { diff --git a/qa/eeschema/CMakeLists.txt b/qa/eeschema/CMakeLists.txt index c5e3efa58b..90f099e84a 100644 --- a/qa/eeschema/CMakeLists.txt +++ b/qa/eeschema/CMakeLists.txt @@ -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 ) diff --git a/qa/eeschema/test_sch_symbol.cpp b/qa/eeschema/test_sch_symbol.cpp new file mode 100644 index 0000000000..759e94e79f --- /dev/null +++ b/qa/eeschema/test_sch_symbol.cpp @@ -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 + +// Code under test +#include + +#include + +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()