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 @@
+
+
+
+
+
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