diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 22e6ddce2d..1dd0c3b9f3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -225,6 +225,8 @@ set( COMMON_DLG_SRCS dialogs/dialog_grid_settings_base.cpp dialogs/dialog_hotkey_list.cpp dialogs/dialog_HTML_reporter_base.cpp + dialogs/dialog_import_choose_project.cpp + dialogs/dialog_import_choose_project_base.cpp dialogs/dialog_locked_items_query.cpp dialogs/dialog_locked_items_query_base.cpp dialogs/dialog_migrate_settings.cpp diff --git a/common/dialogs/dialog_import_choose_project.cpp b/common/dialogs/dialog_import_choose_project.cpp new file mode 100644 index 0000000000..55d6034fb3 --- /dev/null +++ b/common/dialogs/dialog_import_choose_project.cpp @@ -0,0 +1,112 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 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 + * Free Software Foundation, either version 3 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, see . + */ + +#include + +#include +#include + + +DIALOG_IMPORT_CHOOSE_PROJECT::DIALOG_IMPORT_CHOOSE_PROJECT( + wxWindow* aParent, const std::vector& aProjectDesc ) : + DIALOG_IMPORT_CHOOSE_PROJECT_BASE( aParent ) +{ + m_project_desc = aProjectDesc; + + // Initialize columns in the wxListCtrl elements: + int comboNameColId = m_listCtrl->AppendColumn( _( "Project Name" ) ); + int pcbNameColId = m_listCtrl->AppendColumn( _( "PCB" ) ); + int schNameColId = m_listCtrl->AppendColumn( _( "Schematic" ) ); + + // Load the project/PCB/schematic names + int row = 0; + + auto convertName = []( const wxString& aName, const wxString& aId ) -> wxString + { + if( aId.empty() ) + return wxEmptyString; + + return aName; + }; + + for( const IMPORT_PROJECT_DESC& desc : m_project_desc ) + { + m_listCtrl->InsertItem( row, convertName( desc.ComboName, desc.ComboId ) ); + m_listCtrl->SetItem( row, pcbNameColId, convertName( desc.PCBName, desc.PCBId ) ); + m_listCtrl->SetItem( row, schNameColId, + convertName( desc.SchematicName, desc.SchematicId ) ); + + ++row; + } + + // Auto select the first item to improve ease-of-use + m_listCtrl->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); + + m_listCtrl->SetColumnWidth( comboNameColId, wxLIST_AUTOSIZE_USEHEADER ); + m_listCtrl->SetColumnWidth( pcbNameColId, wxLIST_AUTOSIZE_USEHEADER ); + m_listCtrl->SetColumnWidth( schNameColId, wxLIST_AUTOSIZE_USEHEADER ); + + SetupStandardButtons(); + + Fit(); + finishDialogSettings(); +} + + +void DIALOG_IMPORT_CHOOSE_PROJECT::onItemActivated( wxListEvent& event ) +{ + EndModal( wxID_OK ); +} + + +void DIALOG_IMPORT_CHOOSE_PROJECT::onClose( wxCloseEvent& event ) +{ + EndModal( wxID_CANCEL ); +} + + +std::vector DIALOG_IMPORT_CHOOSE_PROJECT::GetProjectSelections() +{ + std::vector result; + + long selected = -1; + + do + { + selected = m_listCtrl->GetNextItem( selected, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED ); + + if( selected != -1 && selected < long( m_project_desc.size() ) ) + result.emplace_back( m_project_desc[selected] ); + + } while( selected != -1 ); + + return result; +} + + +std::vector DIALOG_IMPORT_CHOOSE_PROJECT::GetSelectionsModal( + wxWindow* aParent, const std::vector& aProjectDesc ) +{ + DIALOG_IMPORT_CHOOSE_PROJECT dlg( aParent, aProjectDesc ); + + if( dlg.ShowModal() != wxID_OK ) + return {}; + + return dlg.GetProjectSelections(); +} diff --git a/common/dialogs/dialog_import_choose_project.h b/common/dialogs/dialog_import_choose_project.h new file mode 100644 index 0000000000..7333f09c93 --- /dev/null +++ b/common/dialogs/dialog_import_choose_project.h @@ -0,0 +1,53 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 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 + * Free Software Foundation, either version 3 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, see . + */ + +#ifndef DIALOG_IMPORT_CHOOSE_PROJECT_H +#define DIALOG_IMPORT_CHOOSE_PROJECT_H + +#include "dialog_import_choose_project_base.h" +#include + + +class DIALOG_IMPORT_CHOOSE_PROJECT : public DIALOG_IMPORT_CHOOSE_PROJECT_BASE +{ +public: + DIALOG_IMPORT_CHOOSE_PROJECT( wxWindow* aParent, + const std::vector& aProjectDesc ); + + /** + * Create and show a dialog (modal) and returns the data from it after completion. If the + * dialog is closed or cancel is pressed, returns an empty vector. + * + * @param aParent Parent window for the invoked dialog. + * @param aProjectDesc are project descriptors. + */ + static std::vector + GetSelectionsModal( wxWindow* aParent, const std::vector& aProjectDesc ); + + void onItemActivated( wxListEvent& event ) override; + + void onClose( wxCloseEvent& event ) override; + + std::vector GetProjectSelections(); + +private: + std::vector m_project_desc; +}; + +#endif // DIALOG_IMPORT_CHOOSE_PROJECT_H diff --git a/common/dialogs/dialog_import_choose_project_base.cpp b/common/dialogs/dialog_import_choose_project_base.cpp new file mode 100644 index 0000000000..9f02fa20c9 --- /dev/null +++ b/common/dialogs/dialog_import_choose_project_base.cpp @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_import_choose_project_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_IMPORT_CHOOSE_PROJECT_BASE::DIALOG_IMPORT_CHOOSE_PROJECT_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + + bSizerMain = new wxBoxSizer( wxVERTICAL ); + + m_titleText = new wxStaticText( this, wxID_ANY, _("This project file contains multiple PCB+Schematic combinations.\nChoose which one should be imported to KiCad."), wxDefaultPosition, wxDefaultSize, 0 ); + m_titleText->Wrap( -1 ); + bSizerMain->Add( m_titleText, 0, wxALL, 5 ); + + m_listCtrl = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VRULES ); + bSizerMain->Add( m_listCtrl, 1, wxALL|wxEXPAND, 5 ); + + wxBoxSizer* bSizerBottom; + bSizerBottom = new wxBoxSizer( wxHORIZONTAL ); + + m_sdbSizer = new wxStdDialogButtonSizer(); + m_sdbSizerOK = new wxButton( this, wxID_OK ); + m_sdbSizer->AddButton( m_sdbSizerOK ); + m_sdbSizer->Realize(); + + bSizerBottom->Add( m_sdbSizer, 1, wxEXPAND, 5 ); + + + bSizerMain->Add( bSizerBottom, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxTOP, 5 ); + + + this->SetSizer( bSizerMain ); + this->Layout(); + bSizerMain->Fit( this ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_IMPORT_CHOOSE_PROJECT_BASE::onClose ) ); + m_listCtrl->Connect( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler( DIALOG_IMPORT_CHOOSE_PROJECT_BASE::onItemActivated ), NULL, this ); +} + +DIALOG_IMPORT_CHOOSE_PROJECT_BASE::~DIALOG_IMPORT_CHOOSE_PROJECT_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_IMPORT_CHOOSE_PROJECT_BASE::onClose ) ); + m_listCtrl->Disconnect( wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler( DIALOG_IMPORT_CHOOSE_PROJECT_BASE::onItemActivated ), NULL, this ); + +} diff --git a/common/dialogs/dialog_import_choose_project_base.fbp b/common/dialogs/dialog_import_choose_project_base.fbp new file mode 100644 index 0000000000..8860e6b4c6 --- /dev/null +++ b/common/dialogs/dialog_import_choose_project_base.fbp @@ -0,0 +1,221 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_import_choose_project_base + 1000 + none + + + 1 + dialog_import_choose_project_base + + . + + 1 + 1 + 1 + 1 + UI + 0 + 1 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + -1,-1 + DIALOG_IMPORT_CHOOSE_PROJECT_BASE + + -1,-1 + wxCAPTION|wxCLOSE_BOX|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Choose Project to Import + + 0 + + + + onClose + + + bSizerMain + wxVERTICAL + protected + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + This project file contains multiple PCB+Schematic combinations. Choose which one should be imported to KiCad. + 0 + + 0 + + + 0 + + 1 + m_titleText + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_listCtrl + 1 + + + protected + 1 + + Resizable + 1 + + wxLC_HRULES|wxLC_REPORT|wxLC_SINGLE_SEL|wxLC_VRULES + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onItemActivated + + + + 5 + wxBOTTOM|wxEXPAND|wxLEFT|wxTOP + 0 + + + bSizerBottom + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer + protected + + + + + + + + diff --git a/common/dialogs/dialog_import_choose_project_base.h b/common/dialogs/dialog_import_choose_project_base.h new file mode 100644 index 0000000000..3cf79f4bec --- /dev/null +++ b/common/dialogs/dialog_import_choose_project_base.h @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b3) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_IMPORT_CHOOSE_PROJECT_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_IMPORT_CHOOSE_PROJECT_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxBoxSizer* bSizerMain; + wxStaticText* m_titleText; + wxListCtrl* m_listCtrl; + wxStdDialogButtonSizer* m_sdbSizer; + wxButton* m_sdbSizerOK; + + // Virtual event handlers, override them in your derived class + virtual void onClose( wxCloseEvent& event ) { event.Skip(); } + virtual void onItemActivated( wxListEvent& event ) { event.Skip(); } + + + public: + + DIALOG_IMPORT_CHOOSE_PROJECT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Choose Project to Import"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxCAPTION|wxCLOSE_BOX|wxRESIZE_BORDER ); + + ~DIALOG_IMPORT_CHOOSE_PROJECT_BASE(); + +}; + diff --git a/common/plugins/common/plugin_common_choose_project.h b/common/plugins/common/plugin_common_choose_project.h new file mode 100644 index 0000000000..df3fe3e231 --- /dev/null +++ b/common/plugins/common/plugin_common_choose_project.h @@ -0,0 +1,73 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2023 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 + * Free Software Foundation, either version 3 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, see . + */ + +#ifndef PLUGIN_COMMON_CHOOSE_PROJECT_H +#define PLUGIN_COMMON_CHOOSE_PROJECT_H + +#include +#include + +/** + * @brief Describes how non-KiCad boards and schematics should be imported as KiCad projects + */ +struct IMPORT_PROJECT_DESC +{ + wxString ComboName; + wxString PCBName; + wxString SchematicName; + + wxString ComboId; + wxString PCBId; + wxString SchematicId; + + IMPORT_PROJECT_DESC() {} +}; + +/** + * @brief Pointer to a function that takes descriptions of the source projects + * and removes the ones that are not needed, or clears their ID fields. + */ +using CHOOSE_PROJECT_HANDLER = std::function( const std::vector& )>; + + +/** + * @brief Plugin class for import plugins that support choosing a project + */ +class PROJECT_CHOOSER_PLUGIN +{ +public: + /** + * @brief Register a different handler to be called when a non-KiCad project + * contains multiple PCB+Schematic combinations. + * + * The function is marked as virtual, so the plugins can implement extra + * logic (e.g., enable warnings or checks) + */ + virtual void RegisterChooseProjectCallback( CHOOSE_PROJECT_HANDLER aChooseProjectHandler ) + { + m_choose_project_handler = aChooseProjectHandler; + } + + virtual ~PROJECT_CHOOSER_PLUGIN() = default; + +protected: + CHOOSE_PROJECT_HANDLER m_choose_project_handler; ///< Callback to choose projects to import +}; + +#endif // PLUGIN_COMMON_CHOOSE_PROJECT_H diff --git a/common/plugins/easyedapro/easyedapro_import_utils.cpp b/common/plugins/easyedapro/easyedapro_import_utils.cpp index 5795fe57ee..c7412bc09e 100644 --- a/common/plugins/easyedapro/easyedapro_import_utils.cpp +++ b/common/plugins/easyedapro/easyedapro_import_utils.cpp @@ -23,10 +23,14 @@ */ #include "easyedapro_import_utils.h" +#include "easyedapro_parser.h" + +#include #include #include #include +#include #include #include @@ -61,6 +65,81 @@ LIB_ID EASYEDAPRO::ToKiCadLibID( const wxString& aLibName, const wxString& aLibR } +std::vector +EASYEDAPRO::ProjectToSelectorDialog( const nlohmann::json& aProject, bool aPcbOnly, bool aSchOnly ) +{ + std::vector result; + + std::map prjSchematics = aProject.at( "schematics" ); + std::map prjBoards = aProject.at( "boards" ); + std::map prjPcbNames = aProject.at( "pcbs" ); + + for( const auto& [prjName, board] : prjBoards ) + { + IMPORT_PROJECT_DESC desc; + desc.ComboName = desc.ComboId = prjName; + desc.PCBId = board.pcb; + desc.SchematicId = board.schematic; + + auto pcbNameIt = prjPcbNames.find( desc.PCBId ); + if( pcbNameIt != prjPcbNames.end() ) + { + desc.PCBName = pcbNameIt->second; + + if( desc.PCBName.empty() ) + desc.PCBName = pcbNameIt->first; + + prjPcbNames.erase( pcbNameIt ); + } + + auto schIt = prjSchematics.find( desc.SchematicId ); + if( schIt != prjSchematics.end() ) + { + desc.SchematicName = schIt->second.name; + + if( desc.SchematicName.empty() ) + desc.SchematicName = schIt->first; + + prjSchematics.erase( schIt ); + } + + result.emplace_back( desc ); + } + + if( !aSchOnly ) + { + for( const auto& [pcbId, pcbName] : prjPcbNames ) + { + IMPORT_PROJECT_DESC desc; + desc.PCBId = pcbId; + desc.PCBName = pcbName; + + if( desc.PCBName.empty() ) + desc.PCBName = pcbId; + + result.emplace_back( desc ); + } + } + + if( !aPcbOnly ) + { + for( const auto& [schId, schData] : prjSchematics ) + { + IMPORT_PROJECT_DESC desc; + desc.SchematicId = schId; + desc.SchematicName = schData.name; + + if( desc.SchematicName.empty() ) + desc.SchematicName = schId; + + result.emplace_back( desc ); + } + } + + return result; +} + + nlohmann::json EASYEDAPRO::ReadProjectFile( const wxString& aZipFileName ) { std::shared_ptr entry; diff --git a/common/plugins/easyedapro/easyedapro_import_utils.h b/common/plugins/easyedapro/easyedapro_import_utils.h index bd19c3489e..f61adfa5bd 100644 --- a/common/plugins/easyedapro/easyedapro_import_utils.h +++ b/common/plugins/easyedapro/easyedapro_import_utils.h @@ -30,6 +30,8 @@ #include #include +struct IMPORT_PROJECT_DESC; + #define EASY_IT_CONTINUE return false #define EASY_IT_BREAK return true @@ -40,6 +42,10 @@ wxString ShortenLibName( wxString aProjectName ); LIB_ID ToKiCadLibID( const wxString& aLibName, const wxString& aLibReference ); +std::vector ProjectToSelectorDialog( const nlohmann::json& aProject, + bool aPcbOnly = false, + bool aSchOnly = false ); + nlohmann::json ReadProjectFile( const wxString& aZipFileName ); void IterateZipFiles( diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp index 3f2837c02b..7add8108c5 100644 --- a/eeschema/cross-probing.cpp +++ b/eeschema/cross-probing.cpp @@ -941,14 +941,22 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) case MAIL_IMPORT_FILE: { - // Extract file format type and path (plugin type and path separated with \n) - size_t split = payload.find( '\n' ); - wxCHECK( split != std::string::npos, /*void*/ ); + // Extract file format type and path (plugin type, path and properties keys, values separated with \n) + std::stringstream ss( payload ); + char delim = '\n'; + + std::string formatStr; + wxCHECK( std::getline( ss, formatStr, delim ), /* void */ ); + + std::string fnameStr; + wxCHECK( std::getline( ss, fnameStr, delim ), /* void */ ); + wxASSERT( !fnameStr.empty() ); + int importFormat; try { - importFormat = std::stoi( payload.substr( 0, split ) ); + importFormat = std::stoi( formatStr ); } catch( std::invalid_argument& ) { @@ -956,11 +964,23 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) importFormat = -1; } - std::string path = payload.substr( split + 1 ); - wxASSERT( !path.empty() ); + STRING_UTF8_MAP props; + + do + { + std::string key, value; + + if( !std::getline( ss, key, delim ) ) + break; + + std::getline( ss, value, delim ); // We may want an empty string as value + + props.emplace( key, value ); + + } while( true ); if( importFormat >= 0 ) - importFile( path, importFormat ); + importFile( fnameStr, importFormat, props.empty() ? nullptr : &props ); break; } diff --git a/eeschema/files-io.cpp b/eeschema/files-io.cpp index 4fb5404c3b..59ca2cc3f6 100644 --- a/eeschema/files-io.cpp +++ b/eeschema/files-io.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1282,7 +1284,8 @@ bool SCH_EDIT_FRAME::doAutoSave() } -void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) +bool SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType, + const STRING_UTF8_MAP* aProperties ) { wxFileName filename( aFileName ); wxFileName newfilename; @@ -1299,7 +1302,7 @@ void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) case SCH_IO_MGR::SCH_EASYEDAPRO: { // We insist on caller sending us an absolute path, if it does not, we say it's a bug. - wxCHECK_MSG( filename.IsAbsolute(), /*void*/, + wxCHECK_MSG( filename.IsAbsolute(), false, wxS( "Import schematic: path is not absolute!" ) ); try @@ -1308,36 +1311,57 @@ void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) DIALOG_HTML_REPORTER errorReporter( this ); WX_PROGRESS_REPORTER progressReporter( this, _( "Importing Schematic" ), 1 ); + PROJECT_CHOOSER_PLUGIN* projectChooserPlugin = + dynamic_cast( (SCH_PLUGIN*) pi ); + + if( projectChooserPlugin ) + { + projectChooserPlugin->RegisterChooseProjectCallback( + std::bind( DIALOG_IMPORT_CHOOSE_PROJECT::GetSelectionsModal, this, + std::placeholders::_1 ) ); + } + pi->SetReporter( errorReporter.m_Reporter ); pi->SetProgressReporter( &progressReporter ); - Schematic().SetRoot( pi->LoadSchematicFile( aFileName, &Schematic() ) ); - if( errorReporter.m_Reporter->HasMessage() ) + SCH_SHEET* loadedSheet = + pi->LoadSchematicFile( aFileName, &Schematic(), nullptr, aProperties ); + + if( loadedSheet ) { - errorReporter.m_Reporter->Flush(); // Build HTML messages - errorReporter.ShowModal(); - } + Schematic().SetRoot( loadedSheet ); + + if( errorReporter.m_Reporter->HasMessage() ) + { + errorReporter.m_Reporter->Flush(); // Build HTML messages + errorReporter.ShowModal(); + } - // Non-KiCad schematics do not use a drawing-sheet (or if they do, it works differently - // to KiCad), so set it to an empty one - DS_DATA_MODEL& drawingSheet = DS_DATA_MODEL::GetTheInstance(); - drawingSheet.SetEmptyLayout(); - BASE_SCREEN::m_DrawingSheetFileName = "empty.kicad_wks"; + // Non-KiCad schematics do not use a drawing-sheet (or if they do, it works differently + // to KiCad), so set it to an empty one + DS_DATA_MODEL& drawingSheet = DS_DATA_MODEL::GetTheInstance(); + drawingSheet.SetEmptyLayout(); + BASE_SCREEN::m_DrawingSheetFileName = "empty.kicad_wks"; - newfilename.SetPath( Prj().GetProjectPath() ); - newfilename.SetName( Prj().GetProjectName() ); - newfilename.SetExt( KiCadSchematicFileExtension ); + newfilename.SetPath( Prj().GetProjectPath() ); + newfilename.SetName( Prj().GetProjectName() ); + newfilename.SetExt( KiCadSchematicFileExtension ); - SetScreen( GetCurrentSheet().LastScreen() ); + SetScreen( GetCurrentSheet().LastScreen() ); - Schematic().Root().SetFileName( newfilename.GetFullName() ); - GetScreen()->SetFileName( newfilename.GetFullPath() ); - GetScreen()->SetContentModified(); + Schematic().Root().SetFileName( newfilename.GetFullName() ); + GetScreen()->SetFileName( newfilename.GetFullPath() ); + GetScreen()->SetContentModified(); - RecalculateConnections( nullptr, GLOBAL_CLEANUP ); + RecalculateConnections( nullptr, GLOBAL_CLEANUP ); - // Only perform the dangling end test on root sheet. - GetScreen()->TestDanglingEnds(); + // Only perform the dangling end test on root sheet. + GetScreen()->TestDanglingEnds(); + } + else + { + CreateScreens(); + } } catch( const IO_ERROR& ioe ) { @@ -1398,6 +1422,7 @@ void SCH_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) break; } + return true; } diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h index ccab77f577..5998b118e8 100644 --- a/eeschema/sch_edit_frame.h +++ b/eeschema/sch_edit_frame.h @@ -955,7 +955,8 @@ private: * @param full filepath of file to be imported. * @param aFileType SCH_FILE_T value for file type */ - void importFile( const wxString& aFileName, int aFileType ); + bool importFile( const wxString& aFileName, int aFileType, + const STRING_UTF8_MAP* aProperties = nullptr ); /** * Save \a aSheet to a schematic file. diff --git a/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.cpp b/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.cpp index be7fe0ef05..5db11c5fb1 100644 --- a/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.cpp +++ b/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.cpp @@ -416,19 +416,27 @@ SCH_SHEET* SCH_EASYEDAPRO_PLUGIN::LoadSchematicFile( const wxString& aFileName, nlohmann::json project = EASYEDAPRO::ReadProjectFile( aFileName ); std::map prjSchematics = project.at( "schematics" ); - std::map prjBoards = project.at( "boards" ); - std::map prjPcbNames = project.at( "pcbs" ); wxString schematicToLoad; - if( prjBoards.size() > 0 ) + if( aProperties && aProperties->Exists( "sch_id" ) ) { - EASYEDAPRO::PRJ_BOARD boardToLoad = prjBoards.begin()->second; - schematicToLoad = boardToLoad.schematic; + schematicToLoad = aProperties->at( "sch_id" ); } - else if( prjSchematics.size() > 0 ) + else { - schematicToLoad = prjSchematics.begin()->first; + if( prjSchematics.size() == 1 ) + { + schematicToLoad = prjSchematics.begin()->first; + } + else + { + std::vector chosen = m_choose_project_handler( + EASYEDAPRO::ProjectToSelectorDialog( project, false, true ) ); + + if( chosen.size() > 0 ) + schematicToLoad = chosen[0].SchematicId; + } } if( schematicToLoad.empty() ) diff --git a/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.h b/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.h index fe2db0d70b..dfb3c581f4 100644 --- a/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.h +++ b/eeschema/sch_plugins/easyedapro/sch_easyedapro_plugin.h @@ -27,13 +27,14 @@ #include #include +#include class SCH_SHEET; class SCH_SCREEN; -class SCH_EASYEDAPRO_PLUGIN : public SCH_PLUGIN +class SCH_EASYEDAPRO_PLUGIN : public SCH_PLUGIN, public PROJECT_CHOOSER_PLUGIN { public: SCH_EASYEDAPRO_PLUGIN(); diff --git a/kicad/import_proj.cpp b/kicad/import_proj.cpp index 4288b29efc..370ffaae57 100644 --- a/kicad/import_proj.cpp +++ b/kicad/import_proj.cpp @@ -19,9 +19,6 @@ */ #include "import_proj.h" -#include -#include -#include #include #include #include @@ -29,6 +26,20 @@ #include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + IMPORT_PROJ_HELPER::IMPORT_PROJ_HELPER( KICAD_MANAGER_FRAME* aFrame, const std::vector& aSchFileExtensions, @@ -137,7 +148,13 @@ void IMPORT_PROJ_HELPER::ImportIndividualFile( KICAD_T aFT, int aImportedFileTyp KIWAY_PLAYER* frame = m_frame->Kiway().Player( frame_type, true ); - std::string packet = StrPrintf( "%d\n%s", aImportedFileType, TO_UTF8( appImportFile ) ); + std::stringstream ss; + ss << aImportedFileType << '\n' << TO_UTF8( appImportFile ); + + for( const auto& [key, value] : m_properties ) + ss << '\n' << key << '\n' << value; + + std::string packet = ss.str(); frame->Kiway().ExpressMail( frame_type, MAIL_IMPORT_FILE, packet, m_frame ); if( !frame->IsShownOnScreen() ) @@ -151,8 +168,52 @@ void IMPORT_PROJ_HELPER::ImportIndividualFile( KICAD_T aFT, int aImportedFileTyp } +void IMPORT_PROJ_HELPER::EasyEDAProProjectHandler() +{ + wxFileName fname = m_InputFile; + + if( fname.GetExt() == wxS( "epro" ) || fname.GetExt() == wxS( "zip" ) ) + { + nlohmann::json project = EASYEDAPRO::ReadProjectFile( fname.GetFullPath() ); + + std::map prjSchematics = project.at( "schematics" ); + std::map prjBoards = project.at( "boards" ); + std::map prjPcbNames = project.at( "pcbs" ); + + std::vector toImport = + EASYEDAPRO::ProjectToSelectorDialog( project, false, false ); + + if( toImport.size() > 1 ) + { + toImport = DIALOG_IMPORT_CHOOSE_PROJECT::GetSelectionsModal( m_frame, toImport ); + } + + if( toImport.size() == 1 ) + { + const IMPORT_PROJECT_DESC& desc = toImport[0]; + + m_properties["pcb_id"] = desc.PCBId; + m_properties["sch_id"] = desc.SchematicId; + } + else + { + m_properties["pcb_id"] = ""; + m_properties["sch_id"] = ""; + } + } +} + + void IMPORT_PROJ_HELPER::ImportFiles( int aImportedSchFileType, int aImportedPcbFileType ) { + m_properties.clear(); + + if( aImportedSchFileType == SCH_IO_MGR::SCH_EASYEDAPRO + || aImportedPcbFileType == IO_MGR::EASYEDAPRO ) + { + EasyEDAProProjectHandler(); + } + ImportIndividualFile( SCHEMATIC_T, aImportedSchFileType ); ImportIndividualFile( PCB_T, aImportedPcbFileType ); } \ No newline at end of file diff --git a/kicad/import_proj.h b/kicad/import_proj.h index ecf473df10..6379e234ef 100644 --- a/kicad/import_proj.h +++ b/kicad/import_proj.h @@ -3,6 +3,7 @@ #include #include +#include class KICAD_MANAGER_FRAME; @@ -36,6 +37,8 @@ public: private: KICAD_MANAGER_FRAME* m_frame; + STRING_UTF8_MAP m_properties; + std::vector m_copiedSchPaths; std::vector m_copiedPcbPaths; @@ -44,6 +47,8 @@ private: void OutputCopyError( const wxFileName& aSrc, const wxFileName& aFileCopy ); void ImportIndividualFile( KICAD_T aKicad_T, int aImportedFileType ); + + void EasyEDAProProjectHandler(); }; #endif \ No newline at end of file diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp index c85fd8ac34..7802b8b6ff 100644 --- a/pcbnew/cross-probing.cpp +++ b/pcbnew/cross-probing.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -673,14 +674,22 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) case MAIL_IMPORT_FILE: { - // Extract file format type and path (plugin type and path separated with \n) - size_t split = payload.find( '\n' ); - wxCHECK( split != std::string::npos, /*void*/ ); + // Extract file format type and path (plugin type, path and properties keys, values separated with \n) + std::stringstream ss( payload ); + char delim = '\n'; + + std::string formatStr; + wxCHECK( std::getline( ss, formatStr, delim ), /* void */ ); + + std::string fnameStr; + wxCHECK( std::getline( ss, fnameStr, delim ), /* void */ ); + wxASSERT( !fnameStr.empty() ); + int importFormat; try { - importFormat = std::stoi( payload.substr( 0, split ) ); + importFormat = std::stoi( formatStr ); } catch( std::invalid_argument& ) { @@ -688,11 +697,23 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) importFormat = -1; } - std::string path = payload.substr( split + 1 ); - wxASSERT( !path.empty() ); + STRING_UTF8_MAP props; + + std::string key, value; + do + { + if( !std::getline( ss, key, delim ) ) + break; + + if( !std::getline( ss, value, delim ) ) + break; + + props.emplace( key, value ); + + } while( true ); if( importFormat >= 0 ) - importFile( path, importFormat ); + importFile( fnameStr, importFormat, props.empty() ? nullptr : &props ); break; } diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp index 9af6de3f10..2383d099f7 100644 --- a/pcbnew/files.cpp +++ b/pcbnew/files.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ #include #include #include +#include +#include #include #include "footprint_info_impl.h" #include @@ -609,6 +612,16 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in std::bind( DIALOG_IMPORTED_LAYERS::GetMapModal, this, std::placeholders::_1 ) ); } + PROJECT_CHOOSER_PLUGIN* projectChooserPlugin = + dynamic_cast( (PLUGIN*) pi ); + + if( projectChooserPlugin ) + { + projectChooserPlugin->RegisterChooseProjectCallback( + std::bind( DIALOG_IMPORT_CHOOSE_PROJECT::GetSelectionsModal, this, + std::placeholders::_1 ) ); + } + // This will rename the file if there is an autosave and the user want to recover CheckForAutoSaveFile( fullFileName ); @@ -624,6 +637,9 @@ bool PCB_EDIT_FRAME::OpenProjectFiles( const std::vector& aFileSet, in STRING_UTF8_MAP props; + if( m_importProperties ) + props.insert( m_importProperties->begin(), m_importProperties->end() ); + // EAGLE_PLUGIN can use this info to center the BOARD, but it does not yet. props["page_width"] = std::to_string( GetPageSizeIU().x ); props["page_height"] = std::to_string( GetPageSizeIU().y ); @@ -1174,8 +1190,11 @@ bool PCB_EDIT_FRAME::doAutoSave() } -bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) +bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType, + const STRING_UTF8_MAP* aProperties ) { + m_importProperties = aProperties; + switch( (IO_MGR::PCB_FILE_T) aFileType ) { case IO_MGR::CADSTAR_PCB_ARCHIVE: @@ -1188,6 +1207,8 @@ bool PCB_EDIT_FRAME::importFile( const wxString& aFileName, int aFileType ) default: break; } + m_importProperties = nullptr; + return false; } diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index a8018e7214..1b53d9a78b 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -193,7 +193,8 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_inspectClearanceDlg( nullptr ), m_inspectConstraintsDlg( nullptr ), m_footprintDiffDlg( nullptr ), - m_netInspectorDlg( nullptr ) + m_netInspectorDlg( nullptr ), + m_importProperties( nullptr ) { m_maximizeByDefault = true; m_showBorderAndTitleBlock = true; // true to display sheet references diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index 0eed1e1608..7ddeb5f271 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -57,6 +57,7 @@ class FP_LIB_TABLE; class BOARD_NETLIST_UPDATER; class ACTION_MENU; class TOOL_ACTION; +class STRING_UTF8_MAP; enum LAST_PATH_TYPE : unsigned int; @@ -506,17 +507,6 @@ public: */ void OnExportHyperlynx( wxCommandEvent& event ); - /** - * run teardrop tool - */ - void OnRunTeardropTool( wxCommandEvent& event ); - - /** - * Remove all teardrops - */ - void OnRemoveTeardropTool( wxCommandEvent& event ); - - /** * Create an IDF3 compliant BOARD (*.emn) and LIBRARY (*.emp) file. * @@ -572,8 +562,6 @@ public: bool resetTextEffects = true, bool resetFabricationAttrs = true, bool reset3DModels = true, bool* aUpdated = nullptr ); - bool FootprintMatchesLibrary(); - /** * Install the corresponding dialog editor for the given item. * @@ -804,7 +792,8 @@ protected: * @param full file path of file to be imported. * @param aFileType PCB_FILE_T value for file type */ - bool importFile( const wxString& aFileName, int aFileType ); + bool importFile( const wxString& aFileName, int aFileType, + const STRING_UTF8_MAP* aProperties = nullptr ); bool canCloseWindow( wxCloseEvent& aCloseEvent ) override; void doCloseWindow() override; @@ -859,6 +848,8 @@ private: DIALOG_BOOK_REPORTER* m_footprintDiffDlg; DIALOG_NET_INSPECTOR* m_netInspectorDlg; + const STRING_UTF8_MAP* m_importProperties; // Properties used for non-KiCad import. + /** * Keep track of viewport so that track net labels can be adjusted when it changes. */ diff --git a/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.cpp b/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.cpp index 444492c0b8..19060dfbf3 100644 --- a/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.cpp +++ b/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.cpp @@ -43,6 +43,7 @@ #include #include #include +#include struct EASYEDAPRO_PLUGIN::PRJ_DATA @@ -125,23 +126,31 @@ BOARD* EASYEDAPRO_PLUGIN::LoadBoard( const wxString& aFileName, BOARD* aAppendTo { nlohmann::json project = EASYEDAPRO::ReadProjectFile( aFileName ); - std::map prjSchematics = project.at( "schematics" ); - std::map prjBoards = project.at( "boards" ); - std::map prjPcbNames = project.at( "pcbs" ); - wxString pcbToLoad; - if( prjBoards.size() > 0 ) + if( m_props && m_props->Exists( "pcb_id" ) ) { - EASYEDAPRO::PRJ_BOARD boardToLoad = prjBoards.begin()->second; - pcbToLoad = boardToLoad.pcb; + pcbToLoad = m_props->at( "pcb_id" ); } - else if( prjPcbNames.size() > 0 ) + else { - pcbToLoad = prjPcbNames.begin()->first; + std::map prjPcbNames = project.at( "pcbs" ); + + if( prjPcbNames.size() == 1 ) + { + pcbToLoad = prjPcbNames.begin()->first; + } + else + { + std::vector chosen = m_choose_project_handler( + EASYEDAPRO::ProjectToSelectorDialog( project, true, false ) ); + + if( chosen.size() > 0 ) + pcbToLoad = chosen[0].PCBId; + } } - if( prjPcbNames.empty() ) + if( pcbToLoad.empty() ) return nullptr; LoadAllDataFromProject( aFileName, project ); diff --git a/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.h b/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.h index d07b109122..924494742c 100644 --- a/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.h +++ b/pcbnew/plugins/easyedapro/pcb_easyedapro_plugin.h @@ -27,9 +27,10 @@ #include #include +#include -class EASYEDAPRO_PLUGIN : public PLUGIN +class EASYEDAPRO_PLUGIN : public PLUGIN, public PROJECT_CHOOSER_PLUGIN { public: const wxString PluginName() const override