diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 763b42f138..9f752c0f7d 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -353,6 +353,8 @@ set( COMMON_DLG_SRCS
     dialogs/dialog_color_picker_base.cpp
     dialogs/dialog_configure_paths.cpp
     dialogs/dialog_configure_paths_base.cpp
+    dialogs/dialog_design_block_properties.cpp
+    dialogs/dialog_design_block_properties_base.cpp
     dialogs/dialog_display_html_text_base.cpp
     dialogs/dialog_edit_library_tables.cpp
     dialogs/dialog_embed_files.cpp
@@ -431,6 +433,7 @@ set( COMMON_WIDGET_SRCS
     widgets/bitmap_toggle.cpp
     widgets/button_row_panel.cpp
     widgets/color_swatch.cpp
+    widgets/design_block_pane.cpp
     widgets/filter_combobox.cpp
     widgets/font_choice.cpp
     widgets/footprint_choice.cpp
@@ -455,6 +458,7 @@ set( COMMON_WIDGET_SRCS
     widgets/mathplot.cpp
     widgets/msgpanel.cpp
     widgets/paged_dialog.cpp
+    widgets/panel_design_block_chooser.cpp
     widgets/properties_panel.cpp
     widgets/search_pane.cpp
     widgets/search_pane_base.cpp
@@ -616,6 +620,7 @@ set( COMMON_SRCS
     lib_table_grid_tricks.cpp
     lib_tree_model.cpp
     lib_tree_model_adapter.cpp
+    design_block_tree_model_adapter.cpp
     marker_base.cpp
     origin_transforms.cpp
     printout.cpp
@@ -649,6 +654,7 @@ set( COMMON_SRCS
     tool/construction_manager.cpp
     tool/common_tools.cpp
     tool/conditional_menu.cpp
+    tool/design_block_control.cpp
     tool/edit_constraints.cpp
     tool/edit_points.cpp
     tool/editor_conditions.cpp
diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp
index 0b267de866..e6b985b4fc 100644
--- a/common/advanced_config.cpp
+++ b/common/advanced_config.cpp
@@ -98,7 +98,7 @@ static const wxChar AllowManualCanvasScale[] = wxT( "AllowManualCanvasScale" );
 static const wxChar UpdateUIEventInterval[] = wxT( "UpdateUIEventInterval" );
 static const wxChar V3DRT_BevelHeight_um[] = wxT( "V3DRT_BevelHeight_um" );
 static const wxChar V3DRT_BevelExtentFactor[] = wxT( "V3DRT_BevelExtentFactor" );
-static const wxChar EnableDesignBlocks[] = wxT( "EnableDesignBlocks" );
+static const wxChar EnablePcbDesignBlocks[] = wxT( "EnablePcbDesignBlocks" );
 static const wxChar EnableGenerators[] = wxT( "EnableGenerators" );
 static const wxChar EnableGit[] = wxT( "EnableGit" );
 static const wxChar EnableLibWithText[] = wxT( "EnableLibWithText" );
@@ -257,7 +257,7 @@ ADVANCED_CFG::ADVANCED_CFG()
     m_CompactSave               = false;
     m_UpdateUIEventInterval     = 0;
     m_ShowRepairSchematic       = false;
-    m_EnableDesignBlocks        = true;
+    m_EnablePcbDesignBlocks     = false;
     m_EnableGenerators          = false;
     m_EnableGit                 = true;
     m_EnableLibWithText         = false;
@@ -493,8 +493,8 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
                                                m_DisambiguationMenuDelay,
                                                50, 10000 ) );
 
-    configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableDesignBlocks,
-                                                &m_EnableDesignBlocks, m_EnableDesignBlocks ) );
+    configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnablePcbDesignBlocks, &m_EnablePcbDesignBlocks,
+                                                m_EnablePcbDesignBlocks ) );
 
     configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators,
                                                 &m_EnableGenerators, m_EnableGenerators ) );
diff --git a/common/design_block.h b/common/design_block.h
index 9a862411ae..533c3008b2 100644
--- a/common/design_block.h
+++ b/common/design_block.h
@@ -21,6 +21,10 @@
  * or you may write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
+
+#ifndef DESIGN_BLOCK_H
+#define DESIGN_BLOCK_H
+
 #include <kicommon.h>
 #include <lib_id.h>
 #include <nlohmann/json.hpp>
@@ -41,6 +45,9 @@ public:
     const wxString& GetSchematicFile() const { return m_schematicFile; }
     void            SetSchematicFile( const wxString& aFile ) { m_schematicFile = aFile; }
 
+    const wxString& GetBoardFile() const { return m_boardFile; }
+    void            SetBoardFile( const wxString& aFile ) { m_boardFile = aFile; }
+
     void SetFields( nlohmann::ordered_map<wxString, wxString>& aFields )
     {
         m_fields = std::move( aFields );
@@ -55,9 +62,12 @@ public:
 
 private:
     LIB_ID m_lib_id;
-    wxString m_schematicFile;  ///< File name and path for schematic symbol.
+    wxString m_schematicFile;  ///< File name and path for schematic file.
+    wxString m_boardFile;      ///< File name and path for board file
     wxString m_libDescription; ///< File name and path for documentation file.
-    wxString m_keywords;       ///< Search keywords to find footprint in library.
+    wxString m_keywords;       ///< Search keywords to find design block in library.
 
     nlohmann::ordered_map<wxString, wxString> m_fields;
 };
+
+#endif
diff --git a/common/design_block_io.cpp b/common/design_block_io.cpp
index e25acbad2e..2fd2ed875f 100644
--- a/common/design_block_io.cpp
+++ b/common/design_block_io.cpp
@@ -267,7 +267,9 @@ void DESIGN_BLOCK_IO::DesignBlockEnumerate( wxArrayString&  aDesignBlockNames,
     wxDir dir( aLibraryPath );
 
     if( !dir.IsOpened() )
-        return;
+    {
+        THROW_IO_ERROR( wxString::Format( _( "Design block '%s' does not exist." ), aLibraryPath ) );
+    }
 
     wxString dirname;
     wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadDesignBlockPathExtension );
@@ -289,17 +291,23 @@ DESIGN_BLOCK* DESIGN_BLOCK_IO::DesignBlockLoad( const wxString& aLibraryPath,
                       + FILEEXT::KiCadDesignBlockPathExtension + wxFileName::GetPathSeparator();
     wxString dbSchPath = dbPath + aDesignBlockName + wxT( "." )
                          + FILEEXT::KiCadSchematicFileExtension;
+    wxString dbPcbPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::KiCadPcbFileExtension;
     wxString dbMetadataPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::JsonFileExtension;
 
-    if( !wxFileExists( dbSchPath ) )
-        return nullptr;
+    if( !wxDir::Exists( dbPath ) )
+        THROW_IO_ERROR( wxString::Format( _( "Design block '%s' does not exist." ), dbPath ) );
 
     DESIGN_BLOCK* newDB = new DESIGN_BLOCK();
 
     // Library name needs to be empty for when we fill it in with the correct library nickname
     // one layer above
     newDB->SetLibId( LIB_ID( wxEmptyString, aDesignBlockName ) );
-    newDB->SetSchematicFile( dbSchPath );
+
+    if( wxFileExists( dbSchPath ) )
+        newDB->SetSchematicFile( dbSchPath );
+
+    if( wxFileExists( dbPcbPath ) )
+        newDB->SetBoardFile( dbPcbPath );
 
     // Parse the JSON file if it exists
     if( wxFileExists( dbMetadataPath ) )
@@ -335,16 +343,27 @@ DESIGN_BLOCK* DESIGN_BLOCK_IO::DesignBlockLoad( const wxString& aLibraryPath,
         }
         catch( ... )
         {
+            delete newDB;
             THROW_IO_ERROR( wxString::Format(
                     _( "Design block metadata file '%s' could not be read." ), dbMetadataPath ) );
         }
     }
 
-
     return newDB;
 }
 
 
+bool DESIGN_BLOCK_IO::DesignBlockExists( const wxString&                    aLibraryPath,
+                                         const wxString&                    aDesignBlockName,
+                                         const std::map<std::string, UTF8>* aProperties )
+{
+    wxString dbPath = aLibraryPath + wxFileName::GetPathSeparator() + aDesignBlockName + wxT( "." )
+                      + FILEEXT::KiCadDesignBlockPathExtension + wxFileName::GetPathSeparator();
+
+    return wxDir::Exists( dbPath );
+}
+
+
 void DESIGN_BLOCK_IO::DesignBlockSave( const wxString&                    aLibraryPath,
                                        const DESIGN_BLOCK*                aDesignBlock,
                                        const std::map<std::string, UTF8>* aProperties )
@@ -355,12 +374,23 @@ void DESIGN_BLOCK_IO::DesignBlockSave( const wxString&                    aLibra
         THROW_IO_ERROR( _( "Design block does not have a valid library ID." ) );
     }
 
-    wxFileName schematicFile( aDesignBlock->GetSchematicFile() );
-
-    if( !schematicFile.FileExists() )
+    if( aDesignBlock->GetSchematicFile().IsEmpty() && aDesignBlock->GetBoardFile().IsEmpty() )
     {
-        THROW_IO_ERROR( wxString::Format( _( "Schematic source file '%s' does not exist." ),
-                                          schematicFile.GetFullPath() ) );
+        THROW_IO_ERROR( _( "Design block does not have a schematic or board file." ) );
+    }
+
+    wxFileName schematicFile( aDesignBlock->GetSchematicFile() );
+    wxFileName boardFile( aDesignBlock->GetBoardFile() );
+
+    if( !aDesignBlock->GetSchematicFile().IsEmpty() && !schematicFile.FileExists() )
+    {
+        THROW_IO_ERROR(
+                wxString::Format( _( "Schematic source file '%s' does not exist." ), schematicFile.GetFullPath() ) );
+    }
+
+    if( !aDesignBlock->GetBoardFile().IsEmpty() && !boardFile.FileExists() )
+    {
+        THROW_IO_ERROR( wxString::Format( _( "Board source file '%s' does not exist." ), boardFile.GetFullPath() ) );
     }
 
     // Create the design block folder
@@ -378,23 +408,44 @@ void DESIGN_BLOCK_IO::DesignBlockSave( const wxString&                    aLibra
         }
     }
 
-    // The new schematic file name is based on the design block name, not the source sheet name
-    wxString dbSchematicFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName()
-                               + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension;
-
-    // If the source and destination files are the same, then we don't need to copy the file
-    // as we are just updating the metadata
-    if( schematicFile.GetFullPath() != dbSchematicFile )
+    if( !aDesignBlock->GetSchematicFile().IsEmpty() )
     {
-        // Copy the source sheet file to the design block folder, under the design block name
-        if( !wxCopyFile( schematicFile.GetFullPath(), dbSchematicFile ) )
+        // The new schematic file name is based on the design block name, not the source sheet name
+        wxString dbSchematicFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() + wxT( "." )
+                                   + FILEEXT::KiCadSchematicFileExtension;
+
+        // If the source and destination files are the same, then we don't need to copy the file
+        // as we are just updating the metadata
+        if( schematicFile.GetFullPath() != dbSchematicFile )
         {
-            THROW_IO_ERROR( wxString::Format(
-                    _( "Schematic file '%s' could not be saved as design block at '%s'." ),
-                    schematicFile.GetFullPath(), dbSchematicFile ) );
+            // Copy the source sheet file to the design block folder, under the design block name
+            if( !wxCopyFile( schematicFile.GetFullPath(), dbSchematicFile ) )
+            {
+                THROW_IO_ERROR(
+                        wxString::Format( _( "Schematic file '%s' could not be saved as design block at '%s'." ),
+                                          schematicFile.GetFullPath(), dbSchematicFile ) );
+            }
         }
     }
 
+    if( !aDesignBlock->GetBoardFile().IsEmpty() )
+    {
+        // The new Board file name is based on the design block name, not the source sheet name
+        wxString dbBoardFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName() + wxT( "." )
+                               + FILEEXT::KiCadPcbFileExtension;
+
+        // If the source and destination files are the same, then we don't need to copy the file
+        // as we are just updating the metadata
+        if( boardFile.GetFullPath() != dbBoardFile )
+        {
+            // Copy the source sheet file to the design block folder, under the design block name
+            if( !wxCopyFile( boardFile.GetFullPath(), dbBoardFile ) )
+            {
+                THROW_IO_ERROR( wxString::Format( _( "Board file '%s' could not be saved as design block at '%s'." ),
+                                                  boardFile.GetFullPath(), dbBoardFile ) );
+            }
+        }
+    }
 
     wxString dbMetadataFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName()
                               + wxT( "." ) + FILEEXT::JsonFileExtension;
diff --git a/common/design_block_io.h b/common/design_block_io.h
index 9eb2b335ce..aa1598651b 100644
--- a/common/design_block_io.h
+++ b/common/design_block_io.h
@@ -76,10 +76,7 @@ public:
     }
 
     bool DesignBlockExists( const wxString& aLibraryPath, const wxString& aDesignBlockName,
-                            const std::map<std::string, UTF8>* aProperties = nullptr )
-    {
-        return DesignBlockLoad( aLibraryPath, aDesignBlockName, true, aProperties ) != nullptr;
-    }
+                            const std::map<std::string, UTF8>* aProperties = nullptr );
 
     DESIGN_BLOCK* ImportDesignBlock( const wxString&                    aDesignBlockPath,
                                      wxString&                          aDesignBlockNameOut,
diff --git a/common/design_block_lib_table.cpp b/common/design_block_lib_table.cpp
index 774158b395..bbeeac30ec 100644
--- a/common/design_block_lib_table.cpp
+++ b/common/design_block_lib_table.cpp
@@ -73,6 +73,23 @@ void DESIGN_BLOCK_LIB_TABLE_ROW::SetType( const wxString& aType )
 }
 
 
+bool DESIGN_BLOCK_LIB_TABLE_ROW::Refresh()
+{
+    if( !plugin )
+    {
+        wxArrayString dummyList;
+
+        plugin.reset( DESIGN_BLOCK_IO_MGR::FindPlugin( type ) );
+        SetLoaded( false );
+        plugin->DesignBlockEnumerate( dummyList, GetFullURI( true ), true, GetProperties() );
+        SetLoaded( true );
+        return true;
+    }
+
+    return false;
+}
+
+
 DESIGN_BLOCK_LIB_TABLE::DESIGN_BLOCK_LIB_TABLE( DESIGN_BLOCK_LIB_TABLE* aFallBackTable ) :
         LIB_TABLE( aFallBackTable )
 {
diff --git a/eeschema/design_block_tree_model_adapter.cpp b/common/design_block_tree_model_adapter.cpp
similarity index 85%
rename from eeschema/design_block_tree_model_adapter.cpp
rename to common/design_block_tree_model_adapter.cpp
index d561352cf3..9e191f3a72 100644
--- a/eeschema/design_block_tree_model_adapter.cpp
+++ b/common/design_block_tree_model_adapter.cpp
@@ -25,30 +25,32 @@
 #include <project/project_file.h>
 #include <wx/log.h>
 #include <wx/tokenzr.h>
+#include <settings/app_settings.h>
 #include <string_utils.h>
 #include <eda_pattern_match.h>
 #include <design_block.h>
 #include <design_block_lib_table.h>
 #include <design_block_info.h>
 #include <design_block_tree_model_adapter.h>
-#include <tools/sch_design_block_control.h>
 
 wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
 DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs,
-                                         APP_SETTINGS_BASE::LIB_TREE& aSettings )
+                                         APP_SETTINGS_BASE::LIB_TREE& aSettings,
+                                         TOOL_INTERACTIVE* aContextMenuTool )
 {
-    auto* adapter = new DESIGN_BLOCK_TREE_MODEL_ADAPTER( aParent, aLibs, aSettings );
-    adapter->m_frame = aParent;
+    auto* adapter = new DESIGN_BLOCK_TREE_MODEL_ADAPTER( aParent, aLibs, aSettings, aContextMenuTool );
     return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
 }
 
 
-DESIGN_BLOCK_TREE_MODEL_ADAPTER::DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent,
-                                                                  LIB_TABLE* aLibs,
-                                                                  APP_SETTINGS_BASE::LIB_TREE& aSettings ) :
-        LIB_TREE_MODEL_ADAPTER( aParent, wxT( "pinned_design_block_libs" ), aSettings ),
+DESIGN_BLOCK_TREE_MODEL_ADAPTER::DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs,
+                                                                  APP_SETTINGS_BASE::LIB_TREE& aSettings,
+                                                                  TOOL_INTERACTIVE*            aContextMenuTool ) :
+        LIB_TREE_MODEL_ADAPTER( aParent, wxT( "pinned_design_block_libs" ),
+                                Kiface().KifaceSettings()->m_DesignBlockChooserPanel.tree ),
         m_libs( (DESIGN_BLOCK_LIB_TABLE*) aLibs ),
-        m_frame( aParent )
+        m_frame( aParent ),
+        m_contextMenuTool( aContextMenuTool )
 {
 }
 
@@ -74,8 +76,7 @@ void DESIGN_BLOCK_TREE_MODEL_ADAPTER::AddLibraries( EDA_BASE_FRAME* aParent )
         bool pinned = alg::contains( cfg->m_Session.pinned_design_block_libs, libName )
                       || alg::contains( project.m_PinnedDesignBlockLibs, libName );
 
-        DoAddLibrary( libName, library->GetDescr(), getDesignBlocks( aParent, libName ), pinned,
-                      true );
+        DoAddLibrary( libName, library->GetDescr(), getDesignBlocks( aParent, libName ), pinned, true );
     }
 
     m_tree.AssignIntrinsicRanks();
@@ -88,23 +89,20 @@ void DESIGN_BLOCK_TREE_MODEL_ADAPTER::ClearLibraries()
 }
 
 
-std::vector<LIB_TREE_ITEM*>
-DESIGN_BLOCK_TREE_MODEL_ADAPTER::getDesignBlocks( EDA_BASE_FRAME* aParent,
-                                                  const wxString& aLibName )
+std::vector<LIB_TREE_ITEM*> DESIGN_BLOCK_TREE_MODEL_ADAPTER::getDesignBlocks( EDA_BASE_FRAME* aParent,
+                                                                              const wxString& aLibName )
 {
     std::vector<LIB_TREE_ITEM*> libList;
 
     auto fullListStart = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetList().begin();
     auto fullListEnd = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetList().end();
 
-    std::unique_ptr<DESIGN_BLOCK_INFO> dummy =
-            std::make_unique<DESIGN_BLOCK_INFO_IMPL>( aLibName, wxEmptyString );
+    std::unique_ptr<DESIGN_BLOCK_INFO> dummy = std::make_unique<DESIGN_BLOCK_INFO_IMPL>( aLibName, wxEmptyString );
 
     // List is sorted, so use a binary search to find the range of footnotes for our library
     auto libBounds = std::equal_range(
             fullListStart, fullListEnd, dummy,
-            []( const std::unique_ptr<DESIGN_BLOCK_INFO>& a,
-                const std::unique_ptr<DESIGN_BLOCK_INFO>& b )
+            []( const std::unique_ptr<DESIGN_BLOCK_INFO>& a, const std::unique_ptr<DESIGN_BLOCK_INFO>& b )
             {
                 return StrNumCmp( a->GetLibNickname(), b->GetLibNickname(), false ) < 0;
             } );
@@ -148,8 +146,7 @@ wxString DESIGN_BLOCK_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, in
     catch( const IO_ERROR& ioe )
     {
         wxLogError( _( "Error loading design block %s from library '%s'." ) + wxS( "\n%s" ),
-                    aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str(),
-                    ioe.What() );
+                    aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str(), ioe.What() );
 
         return wxEmptyString;
     }
@@ -206,5 +203,5 @@ wxString DESIGN_BLOCK_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, in
 
 TOOL_INTERACTIVE* DESIGN_BLOCK_TREE_MODEL_ADAPTER::GetContextMenuTool()
 {
-    return m_frame->GetToolManager()->GetTool<SCH_DESIGN_BLOCK_CONTROL>();
+    return m_contextMenuTool;
 }
diff --git a/eeschema/design_block_tree_model_adapter.h b/common/design_block_tree_model_adapter.h
similarity index 86%
rename from eeschema/design_block_tree_model_adapter.h
rename to common/design_block_tree_model_adapter.h
index b8e46e1cbd..35f33f1579 100644
--- a/eeschema/design_block_tree_model_adapter.h
+++ b/common/design_block_tree_model_adapter.h
@@ -33,9 +33,9 @@ public:
      *
      * @param aLibs library set from which parts will be loaded
      */
-    static wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> Create( EDA_BASE_FRAME* aParent,
-                                                           LIB_TABLE*      aLibs,
-                                                           APP_SETTINGS_BASE::LIB_TREE& aSettings );
+    static wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> Create( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs,
+                                                           APP_SETTINGS_BASE::LIB_TREE& aSettings,
+                                                           TOOL_INTERACTIVE*            aContextMenuTool );
 
     void AddLibraries( EDA_BASE_FRAME* aParent );
     void ClearLibraries();
@@ -51,16 +51,17 @@ protected:
      * Constructor; takes a set of libraries to be included in the search.
      */
     DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs,
-                                     APP_SETTINGS_BASE::LIB_TREE& aSettings );
+                                     APP_SETTINGS_BASE::LIB_TREE& aSettings,
+                                     TOOL_INTERACTIVE* aContextMenuTool );
 
-    std::vector<LIB_TREE_ITEM*> getDesignBlocks( EDA_BASE_FRAME* aParent,
-                                                 const wxString& aLibName );
+    std::vector<LIB_TREE_ITEM*> getDesignBlocks( EDA_BASE_FRAME* aParent, const wxString& aLibName );
 
     PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB; }
 
 protected:
     DESIGN_BLOCK_LIB_TABLE* m_libs;
     EDA_BASE_FRAME*         m_frame;
+    TOOL_INTERACTIVE*       m_contextMenuTool;
 };
 
 #endif // DESIGN_BLOCK_TREE_MODEL_ADAPTER_H
diff --git a/eeschema/dialogs/dialog_design_block_properties.cpp b/common/dialogs/dialog_design_block_properties.cpp
similarity index 92%
rename from eeschema/dialogs/dialog_design_block_properties.cpp
rename to common/dialogs/dialog_design_block_properties.cpp
index f20fac8d8b..744aeb4d7b 100644
--- a/eeschema/dialogs/dialog_design_block_properties.cpp
+++ b/common/dialogs/dialog_design_block_properties.cpp
@@ -24,20 +24,20 @@
 
 
 #include <dialogs/dialog_design_block_properties.h>
-#include <sch_edit_frame.h>
 #include <wx/msgdlg.h>
 #include <wx/tooltip.h>
 #include <grid_tricks.h>
 #include <widgets/std_bitmap_button.h>
+#include <bitmaps.h>
 
 #include <design_block.h>
 
-DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME* aParent,
-                                                                DESIGN_BLOCK*   aDesignBlock ) :
+DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( wxWindow*     aParent,
+                                                                DESIGN_BLOCK* aDesignBlock,
+                                                                bool          aDisableName ) :
         DIALOG_DESIGN_BLOCK_PROPERTIES_BASE( aParent ), m_designBlock( aDesignBlock )
 {
-    if( !m_textName->IsEmpty() )
-        m_textName->SetEditable( false );
+    m_textName->SetEditable( !aDisableName );
 
     wxToolTip::Enable( true );
     SetupStandardButtons();
@@ -50,10 +50,11 @@ DIALOG_DESIGN_BLOCK_PROPERTIES::DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME*
 
     m_fieldsGrid->SetUseNativeColLabels();
 
-    m_fieldsGrid->PushEventHandler( new GRID_TRICKS( m_fieldsGrid, [this]( wxCommandEvent& aEvent )
-                                                       {
-                                                           OnAddField( aEvent );
-                                                       } ) );
+    m_fieldsGrid->PushEventHandler( new GRID_TRICKS( m_fieldsGrid,
+                                                     [this]( wxCommandEvent& aEvent )
+                                                     {
+                                                         OnAddField( aEvent );
+                                                     } ) );
     m_fieldsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
 }
 
@@ -86,8 +87,7 @@ bool DIALOG_DESIGN_BLOCK_PROPERTIES::TransferDataToWindow()
 
 bool DIALOG_DESIGN_BLOCK_PROPERTIES::TransferDataFromWindow()
 {
-    m_designBlock->SetLibId(
-            LIB_ID( m_designBlock->GetLibId().GetLibNickname(), m_textName->GetValue() ) );
+    m_designBlock->SetLibId( LIB_ID( m_designBlock->GetLibId().GetLibNickname(), m_textName->GetValue() ) );
     m_designBlock->SetLibDescription( m_textDescription->GetValue() );
     m_designBlock->SetKeywords( m_textKeywords->GetValue() );
 
diff --git a/eeschema/dialogs/dialog_design_block_properties.h b/common/dialogs/dialog_design_block_properties.h
similarity index 88%
rename from eeschema/dialogs/dialog_design_block_properties.h
rename to common/dialogs/dialog_design_block_properties.h
index c4dc5f1b7b..00fd46355e 100644
--- a/eeschema/dialogs/dialog_design_block_properties.h
+++ b/common/dialogs/dialog_design_block_properties.h
@@ -22,6 +22,9 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
+#ifndef DIALOG_DESIGN_BLOCK_PROPERTIES_H
+#define DIALOG_DESIGN_BLOCK_PROPERTIES_H
+
 #include <dialogs/dialog_design_block_properties_base.h>
 #include <nlohmann/json.hpp>
 
@@ -31,7 +34,7 @@ class DESIGN_BLOCK;
 class DIALOG_DESIGN_BLOCK_PROPERTIES : public DIALOG_DESIGN_BLOCK_PROPERTIES_BASE
 {
 public:
-    DIALOG_DESIGN_BLOCK_PROPERTIES( SCH_EDIT_FRAME* aParent, DESIGN_BLOCK* aDesignBlock );
+    DIALOG_DESIGN_BLOCK_PROPERTIES( wxWindow* aParent, DESIGN_BLOCK* aDesignBlock, bool aDisableName = false );
     ~DIALOG_DESIGN_BLOCK_PROPERTIES() override;
 
     bool TransferDataToWindow() override;
@@ -50,6 +53,8 @@ public:
 private:
     void AdjustGridColumns( int aWidth );
 
-    DESIGN_BLOCK* m_designBlock;
+    DESIGN_BLOCK*                             m_designBlock;
     nlohmann::ordered_map<wxString, wxString> m_fields;
 };
+
+#endif
diff --git a/eeschema/dialogs/dialog_design_block_properties_base.cpp b/common/dialogs/dialog_design_block_properties_base.cpp
similarity index 100%
rename from eeschema/dialogs/dialog_design_block_properties_base.cpp
rename to common/dialogs/dialog_design_block_properties_base.cpp
diff --git a/eeschema/dialogs/dialog_design_block_properties_base.fbp b/common/dialogs/dialog_design_block_properties_base.fbp
similarity index 100%
rename from eeschema/dialogs/dialog_design_block_properties_base.fbp
rename to common/dialogs/dialog_design_block_properties_base.fbp
diff --git a/eeschema/dialogs/dialog_design_block_properties_base.h b/common/dialogs/dialog_design_block_properties_base.h
similarity index 100%
rename from eeschema/dialogs/dialog_design_block_properties_base.h
rename to common/dialogs/dialog_design_block_properties_base.h
diff --git a/common/dialogs/panel_design_block_lib_table.cpp b/common/dialogs/panel_design_block_lib_table.cpp
index 52c93444da..28904ed141 100644
--- a/common/dialogs/panel_design_block_lib_table.cpp
+++ b/common/dialogs/panel_design_block_lib_table.cpp
@@ -633,6 +633,44 @@ bool PANEL_DESIGN_BLOCK_LIB_TABLE::verifyTables()
         }
     }
 
+    for( DESIGN_BLOCK_LIB_TABLE* table : { global_model(), project_model() } )
+    {
+        if( !table )
+            continue;
+
+        for( unsigned int r = 0; r < table->GetCount(); ++r )
+        {
+            DESIGN_BLOCK_LIB_TABLE_ROW& row = dynamic_cast<DESIGN_BLOCK_LIB_TABLE_ROW&>( table->At( r ) );
+
+            // Newly-added rows won't have set this yet
+            row.SetParent( table );
+
+            if( !row.GetIsEnabled() )
+                continue;
+
+            try
+            {
+                if( row.Refresh() )
+                {
+                    if( table == global_model() )
+                        m_parent->m_GlobalTableChanged = true;
+                    else
+                        m_parent->m_ProjectTableChanged = true;
+                }
+            }
+            catch( const IO_ERROR& ioe )
+            {
+                msg.Printf( _( "Design block library '%s' failed to load." ), row.GetNickName() );
+
+                wxWindow*       topLevelParent = wxGetTopLevelParent( this );
+                wxMessageDialog errdlg( topLevelParent, msg + wxS( "\n" ) + ioe.What(), _( "Error Loading Library" ) );
+                errdlg.ShowModal();
+
+                return true;
+            }
+        }
+    }
+
     return true;
 }
 
@@ -1214,7 +1252,7 @@ void InvokeEditDesignBlockLibTable( KIWAY* aKiway, wxWindow *aParent )
 
     std::string payload = "";
     aKiway->ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload );
-    aKiway->ExpressMail( FRAME_SCH_VIEWER, MAIL_RELOAD_LIB, payload );
+    aKiway->ExpressMail( FRAME_PCB_EDITOR, MAIL_RELOAD_LIB, payload );
 
     return;
 }
diff --git a/common/settings/app_settings.cpp b/common/settings/app_settings.cpp
index ee0d3511e4..a77e0d4871 100644
--- a/common/settings/app_settings.cpp
+++ b/common/settings/app_settings.cpp
@@ -71,6 +71,59 @@ APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaV
     m_params.emplace_back( new PARAM_LIST<wxString>( "find_replace.replace_history",
             &m_FindReplace.replace_history, {} ) );
 
+    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_h",
+            &m_DesignBlockChooserPanel.sash_pos_h, -1 ) );
+
+    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_v",
+            &m_DesignBlockChooserPanel.sash_pos_v, -1 ) );
+
+    m_params.emplace_back( new PARAM<int>( "design_block_chooser.width",
+            &m_DesignBlockChooserPanel.width, -1 ) );
+
+    m_params.emplace_back( new PARAM<int>( "design_block_chooser.height",
+            &m_DesignBlockChooserPanel.height, -1 ) );
+
+    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sort_mode",
+            &m_DesignBlockChooserPanel.sort_mode, 0 ) );
+
+    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.repeated_placement",
+            &m_DesignBlockChooserPanel.repeated_placement, false ) );
+
+    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.place_as_sheet",
+            &m_DesignBlockChooserPanel.place_as_sheet, false ) );
+
+    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.keep_annotations",
+            &m_DesignBlockChooserPanel.keep_annotations, false ) );
+
+    m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
+            "design_block_chooser.lib_tree.column_widths",
+            [&]() -> nlohmann::json
+            {
+                nlohmann::json ret = {};
+
+                for( const auto& [name, width] : m_DesignBlockChooserPanel.tree.column_widths )
+                    ret[std::string( name.ToUTF8() )] = width;
+
+                return ret;
+            },
+            [&]( const nlohmann::json& aJson )
+            {
+                if( !aJson.is_object() )
+                    return;
+
+                m_DesignBlockChooserPanel.tree.column_widths.clear();
+
+                for( const auto& entry : aJson.items() )
+                {
+                    if( !entry.value().is_number_integer() )
+                        continue;
+
+                    m_DesignBlockChooserPanel.tree.column_widths[entry.key()] = entry.value().get<int>();
+                }
+            },
+            {} ) );
+
+
     m_params.emplace_back( new PARAM<int>( "graphics.canvas_type",
             &m_Graphics.canvas_type, EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ) );
 
diff --git a/common/tool/design_block_control.cpp b/common/tool/design_block_control.cpp
new file mode 100644
index 0000000000..5dcb80bea2
--- /dev/null
+++ b/common/tool/design_block_control.cpp
@@ -0,0 +1,218 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+#include <tool/actions.h>
+#include <tool/library_editor_control.h>
+#include <tool/design_block_control.h>
+#include <widgets/design_block_pane.h>
+#include <widgets/panel_design_block_chooser.h>
+#include <dialog_design_block_properties.h>
+#include <mail_type.h>
+#include <kiway.h>
+
+
+DESIGN_BLOCK_CONTROL::~DESIGN_BLOCK_CONTROL()
+{
+}
+
+DESIGN_BLOCK_CONTROL::DESIGN_BLOCK_CONTROL( const std::string& aName ) : TOOL_INTERACTIVE( aName )
+{
+}
+
+
+void DESIGN_BLOCK_CONTROL::Reset( RESET_REASON aReason )
+{
+    m_frame = getEditFrame<EDA_DRAW_FRAME>();
+}
+
+
+void DESIGN_BLOCK_CONTROL::AddContextMenuItems( CONDITIONAL_MENU* aMenu )
+{
+    auto pinnedLib =
+            [this]( const SELECTION& aSel )
+            {
+                //
+                LIB_TREE_NODE* current = getCurrentTreeNode();
+                return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
+                       && current->m_Pinned;
+            };
+
+    auto unpinnedLib =
+            [this](const SELECTION& aSel )
+            {
+                LIB_TREE_NODE* current = getCurrentTreeNode();
+                return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
+                       && !current->m_Pinned;
+            };
+
+    auto isDesignBlock =
+            [this](const SELECTION& aSel )
+            {
+                return this->selIsDesignBlock(aSel);
+            };
+
+    aMenu->AddItem( ACTIONS::pinLibrary, unpinnedLib, 1 );
+    aMenu->AddItem( ACTIONS::unpinLibrary, pinnedLib, 1 );
+    aMenu->AddItem( ACTIONS::newLibrary, !isDesignBlock, 1 );
+    aMenu->AddSeparator( 2 );
+
+    aMenu->AddSeparator( 400 );
+    aMenu->AddItem( ACTIONS::hideLibraryTree, SELECTION_CONDITIONS::ShowAlways, 400 );
+}
+
+
+int DESIGN_BLOCK_CONTROL::PinLibrary( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( current && !current->m_Pinned )
+    {
+        m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(), PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
+        current->m_Pinned = true;
+        getDesignBlockPane()->RefreshLibs();
+        notifyOtherFrames();
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int DESIGN_BLOCK_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( current && current->m_Pinned )
+    {
+        m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(), PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
+        current->m_Pinned = false;
+        getDesignBlockPane()->RefreshLibs();
+        notifyOtherFrames();
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int DESIGN_BLOCK_CONTROL::NewLibrary( const TOOL_EVENT& aEvent )
+{
+    if( getDesignBlockPane()->CreateNewDesignBlockLibrary() != wxEmptyString )
+    {
+        notifyOtherFrames();
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int DESIGN_BLOCK_CONTROL::DeleteDesignBlock( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( getDesignBlockPane()->DeleteDesignBlockFromLibrary( current->m_LibId, true ) )
+    {
+        notifyOtherFrames();
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int DESIGN_BLOCK_CONTROL::EditDesignBlockProperties( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( getDesignBlockPane()->EditDesignBlockProperties( current->m_LibId ) )
+    {
+        notifyOtherFrames();
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int DESIGN_BLOCK_CONTROL::HideLibraryTree( const TOOL_EVENT& aEvent )
+{
+    m_frame->ToggleLibraryTree();
+    return 0;
+}
+
+
+bool DESIGN_BLOCK_CONTROL::selIsInLibrary( const SELECTION& aSel )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+    return current
+           && ( current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
+                || current->m_Type == LIB_TREE_NODE::TYPE::ITEM );
+}
+
+
+bool DESIGN_BLOCK_CONTROL::selIsDesignBlock( const SELECTION& aSel )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+    return current && current->m_Type == LIB_TREE_NODE::TYPE::ITEM;
+}
+
+
+void DESIGN_BLOCK_CONTROL::setTransitions()
+{
+    Go( &DESIGN_BLOCK_CONTROL::PinLibrary,      ACTIONS::pinLibrary.MakeEvent() );
+    Go( &DESIGN_BLOCK_CONTROL::UnpinLibrary,    ACTIONS::unpinLibrary.MakeEvent() );
+    Go( &DESIGN_BLOCK_CONTROL::NewLibrary,      ACTIONS::newLibrary.MakeEvent() );
+    Go( &DESIGN_BLOCK_CONTROL::HideLibraryTree, ACTIONS::hideLibraryTree.MakeEvent() );
+}
+
+
+LIB_ID DESIGN_BLOCK_CONTROL::getSelectedLibId()
+{
+    getDesignBlockPane()->GetSelectedLibId();
+
+    return LIB_ID();
+}
+
+
+LIB_TREE_NODE* DESIGN_BLOCK_CONTROL::getCurrentTreeNode()
+{
+    LIB_TREE* libTree = getDesignBlockPane()->GetDesignBlockPanel()->GetLibTree();
+    return libTree ? libTree->GetCurrentTreeNode() : nullptr;
+}
+
+
+void DESIGN_BLOCK_CONTROL::notifyOtherFrames()
+{
+    std::string payload = "";
+
+    for( FRAME_T frame : m_framesToNotify )
+        m_frame->Kiway().ExpressMail( frame, MAIL_RELOAD_LIB, payload );
+}
diff --git a/common/tool/design_block_control.h b/common/tool/design_block_control.h
new file mode 100644
index 0000000000..61339df080
--- /dev/null
+++ b/common/tool/design_block_control.h
@@ -0,0 +1,77 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+
+#ifndef DESIGN_BLOCK_CONTROL_H
+#define DESIGN_BLOCK_CONTROL_H
+
+#include <eda_draw_frame.h>
+#include <tool/tool_interactive.h>
+
+class DESIGN_BLOCK_PANE;
+
+/**
+ * Handle schematic design block actions in the schematic editor.
+ */
+class DESIGN_BLOCK_CONTROL : public TOOL_INTERACTIVE, public wxEvtHandler
+{
+public:
+    DESIGN_BLOCK_CONTROL( const std::string& aName );
+    virtual ~DESIGN_BLOCK_CONTROL();
+
+    /// @copydoc TOOL_INTERACTIVE::Reset()
+    void Reset( RESET_REASON aReason ) override;
+
+    void AddContextMenuItems( CONDITIONAL_MENU* aMenu );
+
+    int PinLibrary( const TOOL_EVENT& aEvent );
+    int UnpinLibrary( const TOOL_EVENT& aEvent );
+
+    int NewLibrary( const TOOL_EVENT& aEvent );
+    int DeleteLibrary( const TOOL_EVENT& aEvent );
+
+    int DeleteDesignBlock( const TOOL_EVENT& aEvent );
+    int EditDesignBlockProperties( const TOOL_EVENT& aEvent );
+
+    int HideLibraryTree( const TOOL_EVENT& aEvent );
+
+protected:
+    bool selIsInLibrary( const SELECTION& aSel );
+    bool selIsDesignBlock( const SELECTION& aSel );
+
+    LIB_ID getSelectedLibId();
+    ///< Set up handlers for various events.
+    void setTransitions() override;
+
+    virtual DESIGN_BLOCK_PANE* getDesignBlockPane() = 0;
+    LIB_TREE_NODE*             getCurrentTreeNode();
+
+    /// Notify other frames that the design block lib table has changed
+    std::vector<FRAME_T> m_framesToNotify;
+    void                 notifyOtherFrames();
+
+    EDA_DRAW_FRAME* m_frame = nullptr;
+};
+
+
+#endif
diff --git a/common/widgets/design_block_pane.cpp b/common/widgets/design_block_pane.cpp
new file mode 100644
index 0000000000..2441924a4c
--- /dev/null
+++ b/common/widgets/design_block_pane.cpp
@@ -0,0 +1,512 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+#include <design_block.h>
+#include <design_block_lib_table.h>
+#include <paths.h>
+#include <env_paths.h>
+#include <pgm_base.h>
+#include <common.h>
+#include <kidialog.h>
+#include <widgets/design_block_pane.h>
+#include <dialog_design_block_properties.h>
+#include <widgets/panel_design_block_chooser.h>
+#include <kiface_base.h>
+#include <core/kicad_algo.h>
+#include <template_fieldnames.h>
+#include <wx/button.h>
+#include <wx/checkbox.h>
+#include <wx/sizer.h>
+#include <wx/choicdlg.h>
+#include <wx/msgdlg.h>
+#include <wx/textdlg.h>
+#include <confirm.h>
+#include <wildcards_and_files_ext.h>
+#include <tool/tool_manager.h>
+
+DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( EDA_DRAW_FRAME* aParent, const LIB_ID* aPreselect,
+                                      std::vector<LIB_ID>& aHistoryList ) : WX_PANEL( aParent ), m_frame( aParent )
+{
+    m_frame->Bind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
+    m_frame->Bind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
+}
+
+
+DESIGN_BLOCK_PANE::~DESIGN_BLOCK_PANE()
+{
+    m_frame->Unbind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
+    m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
+}
+
+
+void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent )
+{
+    if( m_chooserPanel )
+        m_chooserPanel->ShowChangedLanguage();
+
+    setLabelsAndTooltips();
+
+    aEvent.Skip();
+}
+
+
+void DESIGN_BLOCK_PANE::OnClosed( wxAuiManagerEvent& aEvent )
+{
+    if( APP_SETTINGS_BASE* cfg = m_frame->config() )
+    {
+        if( IsShownOnScreen() ) // Ensure the panel is shown when trying to save its size
+            m_frame->SaveSettings( cfg );
+    }
+
+    aEvent.Skip();
+}
+
+
+void DESIGN_BLOCK_PANE::SaveSettings()
+{
+    m_chooserPanel->SaveSettings();
+}
+
+
+LIB_ID DESIGN_BLOCK_PANE::GetSelectedLibId( int* aUnit ) const
+{
+    return m_chooserPanel->GetSelectedLibId( aUnit );
+}
+
+
+void DESIGN_BLOCK_PANE::SelectLibId( const LIB_ID& aLibId )
+{
+    m_chooserPanel->SelectLibId( aLibId );
+}
+
+
+void DESIGN_BLOCK_PANE::RefreshLibs()
+{
+    m_chooserPanel->RefreshLibs();
+}
+
+
+DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg )
+{
+    DESIGN_BLOCK_LIB_TABLE* prjLibs = m_frame->Prj().DesignBlockLibs();
+
+    wxCHECK_MSG( prjLibs, nullptr, wxS( "Invalid design block library table." ) );
+
+    DESIGN_BLOCK* designBlock = nullptr;
+
+    try
+    {
+        designBlock = prjLibs->DesignBlockLoadWithOptionalNickname( aLibId, true );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        if( aShowErrorMsg )
+        {
+            wxString msg = wxString::Format( _( "Error loading design block %s from library '%s'." ),
+                                             aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str() );
+            DisplayErrorMessage( m_frame, msg, ioe.What() );
+        }
+    }
+
+    return designBlock;
+}
+
+
+DESIGN_BLOCK* DESIGN_BLOCK_PANE::GetSelectedDesignBlock( bool aUseCacheLib, bool aShowErrorMsg )
+{
+    if( !GetSelectedLibId().IsValid() )
+        return nullptr;
+
+    return GetDesignBlock( GetSelectedLibId(), aUseCacheLib, aShowErrorMsg );
+}
+
+
+wxString DESIGN_BLOCK_PANE::CreateNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName )
+{
+    return createNewDesignBlockLibrary( aLibName, aProposedName, selectDesignBlockLibTable() );
+}
+
+
+wxString DESIGN_BLOCK_PANE::createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName,
+                                                         DESIGN_BLOCK_LIB_TABLE* aTable )
+{
+    if( aTable == nullptr )
+        return wxEmptyString;
+
+    wxFileName fn;
+    bool       doAdd = false;
+    bool       isGlobal = ( aTable == &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() );
+    wxString   initialPath = aProposedName;
+
+    if( initialPath.IsEmpty() )
+        initialPath = isGlobal ? PATHS::GetDefaultUserDesignBlocksPath() : m_frame->Prj().GetProjectPath();
+
+    if( aLibName.IsEmpty() )
+    {
+        fn = initialPath;
+
+        if( !m_frame->LibraryFileBrowser( false, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(),
+                                          FILEEXT::KiCadDesignBlockLibPathExtension, false, isGlobal, initialPath ) )
+        {
+            return wxEmptyString;
+        }
+
+        doAdd = true;
+    }
+    else
+    {
+        fn = EnsureFileExtension( aLibName, FILEEXT::KiCadDesignBlockLibPathExtension );
+
+        if( !fn.IsAbsolute() )
+        {
+            fn.SetName( aLibName );
+            fn.MakeAbsolute( initialPath );
+        }
+    }
+
+    // We can save libs only using DESIGN_BLOCK_IO_MGR::KICAD_SEXP format (.pretty libraries)
+    DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T piType = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
+    wxString                                 libPath = fn.GetFullPath();
+
+    try
+    {
+        IO_RELEASER<DESIGN_BLOCK_IO> pi( DESIGN_BLOCK_IO_MGR::FindPlugin( piType ) );
+
+        bool writable = false;
+        bool exists   = false;
+
+        try
+        {
+            writable = pi->IsLibraryWritable( libPath );
+            exists   = fn.Exists();
+        }
+        catch( const IO_ERROR& )
+        {
+            // best efforts....
+        }
+
+        if( exists )
+        {
+            if( !writable )
+            {
+                wxString msg = wxString::Format( _( "Library %s is read only." ), libPath );
+                m_frame->ShowInfoBarError( msg );
+                return wxEmptyString;
+            }
+            else
+            {
+                wxString msg = wxString::Format( _( "Library %s already exists." ), libPath );
+                KIDIALOG dlg( m_frame, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
+                dlg.SetOKLabel( _( "Overwrite" ) );
+                dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
+
+                if( dlg.ShowModal() == wxID_CANCEL )
+                    return wxEmptyString;
+
+                pi->DeleteLibrary( libPath );
+            }
+        }
+
+        pi->CreateLibrary( libPath );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( m_frame, ioe.What() );
+        return wxEmptyString;
+    }
+
+    if( doAdd )
+        AddDesignBlockLibrary( libPath, aTable );
+
+    return libPath;
+}
+
+
+bool DESIGN_BLOCK_PANE::AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable )
+{
+    if( aTable == nullptr )
+        aTable = selectDesignBlockLibTable();
+
+    if( aTable == nullptr )
+        return wxEmptyString;
+
+    bool isGlobal = ( aTable == &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() );
+
+    wxFileName fn( aFilename );
+
+    if( aFilename.IsEmpty() )
+    {
+        if( !m_frame->LibraryFileBrowser( true, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(),
+                                          FILEEXT::KiCadDesignBlockLibPathExtension, true, isGlobal,
+                                          PATHS::GetDefaultUserDesignBlocksPath() ) )
+        {
+            return false;
+        }
+    }
+
+    wxString libPath = fn.GetFullPath();
+    wxString libName = fn.GetName();
+
+    if( libName.IsEmpty() )
+        return false;
+
+    // Open a dialog to ask for a description
+    wxString description = wxGetTextFromUser( _( "Enter a description for the library:" ), _( "Library Description" ),
+                                              wxEmptyString, m_frame );
+
+    DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T lib_type = DESIGN_BLOCK_IO_MGR::GuessPluginTypeFromLibPath( libPath );
+
+    if( lib_type == DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE )
+        lib_type = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
+
+    wxString type = DESIGN_BLOCK_IO_MGR::ShowType( lib_type );
+
+    // KiCad lib is our default guess.  So it might not have the .kicad_blocks extension
+    // In this case, the extension is part of the library name
+    if( lib_type == DESIGN_BLOCK_IO_MGR::KICAD_SEXP && fn.GetExt() != FILEEXT::KiCadDesignBlockLibPathExtension )
+        libName = fn.GetFullName();
+
+    // try to use path normalized to an environmental variable or project path
+    wxString normalizedPath = NormalizePath( libPath, &Pgm().GetLocalEnvVariables(), &m_frame->Prj() );
+
+    try
+    {
+        DESIGN_BLOCK_LIB_TABLE_ROW* row =
+                new DESIGN_BLOCK_LIB_TABLE_ROW( libName, normalizedPath, type, wxEmptyString, description );
+        aTable->InsertRow( row );
+
+        if( isGlobal )
+            DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable().Save( DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName() );
+        else
+            m_frame->Prj().DesignBlockLibs()->Save( m_frame->Prj().DesignBlockLibTblName() );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( m_frame, ioe.What() );
+        return false;
+    }
+
+    LIB_ID libID( libName, wxEmptyString );
+    RefreshLibs();
+    SelectLibId( libID );
+
+    return true;
+}
+
+
+bool DESIGN_BLOCK_PANE::DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm )
+{
+    if( aLibName.IsEmpty() )
+    {
+        DisplayErrorMessage( m_frame, _( "Please select a library to delete." ) );
+        return false;
+    }
+
+    if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( aLibName ) )
+    {
+        wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibName );
+        m_frame->ShowInfoBarError( msg );
+        return false;
+    }
+
+    // Confirmation
+    wxString msg = wxString::Format( _( "Delete design block library '%s' from disk? This will "
+                                        "delete all design blocks within the library." ),
+                                     aLibName.GetData() );
+
+    if( aConfirm && !IsOK( m_frame, msg ) )
+        return false;
+
+    try
+    {
+        m_frame->Prj().DesignBlockLibs()->DesignBlockLibDelete( aLibName );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( m_frame, ioe.What() );
+        return false;
+    }
+
+    msg.Printf( _( "Design block library '%s' deleted" ), aLibName.GetData() );
+    m_frame->SetStatusText( msg );
+
+    RefreshLibs();
+
+    return true;
+}
+
+
+bool DESIGN_BLOCK_PANE::DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm )
+{
+    if( !aLibId.IsValid() )
+        return false;
+
+    wxString libname = aLibId.GetLibNickname();
+    wxString dbname = aLibId.GetLibItemName();
+
+    if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) )
+    {
+        wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname );
+        m_frame->ShowInfoBarError( msg );
+        return false;
+    }
+
+    // Confirmation
+    wxString msg = wxString::Format( _( "Delete design block '%s' in library '%s' from disk?" ),
+                                     dbname.GetData(), libname.GetData() );
+
+    if( aConfirm && !IsOK( m_frame, msg ) )
+        return false;
+
+    try
+    {
+        m_frame->Prj().DesignBlockLibs()->DesignBlockDelete( libname, dbname );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( m_frame, ioe.What() );
+        return false;
+    }
+
+    msg.Printf( _( "Design block '%s' deleted from library '%s'" ), dbname.GetData(), libname.GetData() );
+
+    m_frame->SetStatusText( msg );
+
+    RefreshLibs();
+
+    return true;
+}
+
+
+bool DESIGN_BLOCK_PANE::EditDesignBlockProperties( const LIB_ID& aLibId )
+{
+    if( !aLibId.IsValid() )
+        return false;
+
+    wxString libname = aLibId.GetLibNickname();
+    wxString dbname = aLibId.GetLibItemName();
+
+    if( !m_frame->Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) )
+    {
+        wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname );
+        m_frame->ShowInfoBarError( msg );
+        return false;
+    }
+
+    DESIGN_BLOCK* designBlock = GetDesignBlock( aLibId, true, true );
+
+    if( !designBlock )
+        return false;
+
+    wxString                       originalName = designBlock->GetLibId().GetLibItemName();
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( m_frame, designBlock );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    wxString newName = designBlock->GetLibId().GetLibItemName();
+
+    try
+    {
+        if( originalName != newName )
+        {
+            if( m_frame->Prj().DesignBlockLibs()->DesignBlockExists( libname, newName ) )
+                if( !checkOverwrite( m_frame, libname, newName ) )
+                    return false;
+
+            m_frame->Prj().DesignBlockLibs()->DesignBlockSave( libname, designBlock );
+            m_frame->Prj().DesignBlockLibs()->DesignBlockDelete( libname, originalName );
+        }
+        else
+            m_frame->Prj().DesignBlockLibs()->DesignBlockSave( libname, designBlock );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( m_frame, ioe.What() );
+        return false;
+    }
+
+    RefreshLibs();
+    SelectLibId( designBlock->GetLibId() );
+
+    return true;
+}
+
+
+bool DESIGN_BLOCK_PANE::checkOverwrite( wxWindow* aFrame, wxString& libname, wxString& newName )
+{
+    wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ),
+                                     newName.GetData(), libname.GetData() );
+
+    if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) )
+        != wxID_OK )
+    {
+        return false;
+    }
+
+    return true;
+}
+
+
+DESIGN_BLOCK_LIB_TABLE* DESIGN_BLOCK_PANE::selectDesignBlockLibTable( bool aOptional )
+{
+    // If no project is loaded, always work with the global table
+    if( m_frame->Prj().IsNullProject() )
+    {
+        DESIGN_BLOCK_LIB_TABLE* ret = &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable();
+
+        if( aOptional )
+        {
+            wxMessageDialog dlg( m_frame, _( "Add the library to the global library table?" ),
+                                 _( "Add To Global Library Table" ), wxYES_NO );
+
+            if( dlg.ShowModal() != wxID_OK )
+                ret = nullptr;
+        }
+
+        return ret;
+    }
+
+    wxArrayString libTableNames;
+    libTableNames.Add( _( "Global" ) );
+    libTableNames.Add( _( "Project" ) );
+
+    wxSingleChoiceDialog dlg( m_frame, _( "Choose the Library Table to add the library to:" ),
+                              _( "Add To Library Table" ), libTableNames );
+
+    if( aOptional )
+    {
+        dlg.FindWindow( wxID_CANCEL )->SetLabel( _( "Skip" ) );
+        dlg.FindWindow( wxID_OK )->SetLabel( _( "Add" ) );
+    }
+
+    if( dlg.ShowModal() != wxID_OK )
+        return nullptr;
+
+    switch( dlg.GetSelection() )
+    {
+    case 0: return &DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable();
+    case 1: return m_frame->Prj().DesignBlockLibs();
+    default: return nullptr;
+    }
+}
diff --git a/common/widgets/design_block_pane.h b/common/widgets/design_block_pane.h
new file mode 100644
index 0000000000..452bc65160
--- /dev/null
+++ b/common/widgets/design_block_pane.h
@@ -0,0 +1,109 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+#ifndef DESIGN_BLOCK_PANE_H
+#define DESIGN_BLOCK_PANE_H
+
+#include <design_block_tree_model_adapter.h>
+#include <widgets/html_window.h>
+#include <widgets/wx_panel.h>
+#include <wx/checkbox.h>
+#include <wx/filedlgcustomize.h>
+#include <eda_draw_frame.h>
+
+
+class DESIGN_BLOCK;
+class PANEL_DESIGN_BLOCK_CHOOSER;
+
+
+class DESIGN_BLOCK_PANE : public WX_PANEL
+{
+public:
+    DESIGN_BLOCK_PANE( EDA_DRAW_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList );
+
+    ~DESIGN_BLOCK_PANE() override;
+
+    void SaveSettings();
+
+    LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const;
+    void   SelectLibId( const LIB_ID& aLibId );
+
+    /**
+     * Load design block from design block library table.
+     *
+     * @param aLibId is the design block library identifier to load.
+     * @param aUseCacheLib set to true to fall back to cache library if design block is not found in
+     *                     design block library table.
+     * @param aShowErrorMessage set to true to show any error messages.
+     * @return The design block found in the library or NULL if the design block was not found.
+     */
+    DESIGN_BLOCK* GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib, bool aShowErrorMsg );
+    DESIGN_BLOCK* GetSelectedDesignBlock( bool aUseCacheLib, bool aShowErrorMsg );
+
+    void RefreshLibs();
+
+    /**
+     * If a library name is given, creates a new design block library in the project folder
+     * with the given name. If no library name is given it prompts user for a library path,
+     * then creates a new design block library at that location.
+     * If library exists, user is warned about that, and is given a chance
+     * to abort the new creation, and in that case existing library is first deleted.
+     *
+     * @param aProposedName is the initial path and filename shown in the file chooser dialog.
+     * @return The newly created library path if library was successfully created, else
+     *         wxEmptyString because user aborted or error.
+     */
+    wxString CreateNewDesignBlockLibrary( const wxString& aLibName = wxEmptyString,
+                                          const wxString& aProposedName = wxEmptyString );
+
+    /**
+     * Add an existing library to either the global or project library table.
+     *
+     * @param aFileName the library to add; a file open dialog will be displayed if empty.
+     * @return true if successfully added.
+     */
+    bool AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable );
+
+    bool DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm );
+
+    bool DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm );
+
+    bool EditDesignBlockProperties( const LIB_ID& aLibId );
+
+    PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; }
+
+protected:
+    virtual void setLabelsAndTooltips() = 0;
+
+    virtual void OnLanguageChanged( wxCommandEvent& aEvent );
+    void         OnClosed( wxAuiManagerEvent& aEvent );
+
+    EDA_DRAW_FRAME*             m_frame = nullptr;
+    PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel = nullptr;
+
+private:
+    bool                    checkOverwrite( wxWindow* aFrame, wxString& libname, wxString& newName );
+    DESIGN_BLOCK_LIB_TABLE* selectDesignBlockLibTable( bool aOptional = false );
+    wxString                createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName,
+                                                         DESIGN_BLOCK_LIB_TABLE* aTable );
+};
+#endif
diff --git a/common/widgets/design_block_preview_widget.h b/common/widgets/design_block_preview_widget.h
new file mode 100644
index 0000000000..46b2e52141
--- /dev/null
+++ b/common/widgets/design_block_preview_widget.h
@@ -0,0 +1,66 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DESIGN_BLOCK_PREVIEW_WIDGET_H
+#define DESIGN_BLOCK_PREVIEW_WIDGET_H
+
+#include <wx/panel.h>
+#include <kiway.h>
+#include <gal_display_options_common.h>
+#include <class_draw_panel_gal.h>
+
+
+class LIB_ID;
+class DESIGN_BLOCK;
+class SCHEMATIC;
+class SCH_SHEET;
+class wxStaticText;
+class wxSizer;
+
+
+class DESIGN_BLOCK_PREVIEW_WIDGET : public wxPanel
+{
+public:
+    /**
+     * Construct a design block preview widget.
+     *
+     * @param aParent - parent window
+     */
+    DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent ) : wxPanel( aParent ) {}
+    ~DESIGN_BLOCK_PREVIEW_WIDGET() = default;
+
+
+    /**
+     * Set the contents of the status label and display it.
+     */
+    virtual void SetStatusText( const wxString& aText ) = 0;
+
+    /**
+     * Set the currently displayed design block.
+     */
+    virtual void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) = 0;
+
+protected:
+    void onSize( wxSizeEvent& aEvent );
+
+    void fitOnDrawArea(); // set the view scale to fit the item on screen and center
+};
+
+
+#endif // DESIGN_BLOCK_PREVIEW_WIDGET_H
diff --git a/eeschema/widgets/panel_design_block_chooser.cpp b/common/widgets/panel_design_block_chooser.cpp
similarity index 85%
rename from eeschema/widgets/panel_design_block_chooser.cpp
rename to common/widgets/panel_design_block_chooser.cpp
index 517a8c4f11..b2649ed3f0 100644
--- a/eeschema/widgets/panel_design_block_chooser.cpp
+++ b/common/widgets/panel_design_block_chooser.cpp
@@ -23,15 +23,18 @@
 
 #include <pgm_base.h>
 #include <design_block.h>
+#include <design_block_pane.h>
 #include <design_block_lib_table.h>
 #include <panel_design_block_chooser.h>
 #include <design_block_preview_widget.h>
 #include <kiface_base.h>
-#include <sch_edit_frame.h>
+#include <kiway_holder.h>
+#include <eda_draw_frame.h>
 #include <widgets/lib_tree.h>
 #include <settings/settings_manager.h>
 #include <project/project_file.h>
 #include <dialogs/html_message_box.h>
+#include <settings/app_settings.h>
 #include <string_utils.h>
 #include <wx/log.h>
 #include <wx/panel.h>
@@ -46,24 +49,24 @@
 wxString PANEL_DESIGN_BLOCK_CHOOSER::g_designBlockSearchString;
 
 
-PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent,
+PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( EDA_DRAW_FRAME* aFrame, DESIGN_BLOCK_PANE* aParent,
                                                         std::vector<LIB_ID>&  aHistoryList,
-                                                        std::function<void()> aSelectHandler ) :
+                                                        std::function<void()> aSelectHandler,
+                                                        TOOL_INTERACTIVE*     aContextMenuTool ) :
+
         wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
         m_dbl_click_timer( nullptr ),
         m_open_libs_timer( nullptr ),
         m_vsplitter( nullptr ),
         m_tree( nullptr ),
         m_preview( nullptr ),
+        m_parent( aParent ),
         m_frame( aFrame ),
         m_selectHandler( std::move( aSelectHandler ) ),
         m_historyList( aHistoryList )
 {
     DESIGN_BLOCK_LIB_TABLE* libs = m_frame->Prj().DesignBlockLibs();
 
-    // Make sure settings are loaded before we start running multi-threaded design block loaders
-    Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
-
     // Load design block files:
     WX_PROGRESS_REPORTER* progressReporter =
             new WX_PROGRESS_REPORTER( aParent, _( "Loading Design Block Libraries" ), 1 );
@@ -79,8 +82,8 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame,
     if( DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetErrorCount() )
         displayErrors( aFrame );
 
-    m_adapter = DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( m_frame, libs,
-        m_frame->eeconfig()->m_DesignBlockChooserPanel.tree );
+    m_adapter = DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create(
+            m_frame, libs, m_frame->config()->m_DesignBlockChooserPanel.tree, aContextMenuTool );
 
     // -------------------------------------------------------------------------------------
     // Construct the actual panel
@@ -99,27 +102,21 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame,
     wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
     treePanel->SetSizer( treeSizer );
 
-    wxPanel*    detailsPanel = new wxPanel( m_vsplitter );
-    wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL );
-    detailsPanel->SetSizer( detailsSizer );
+    m_detailsPanel = new wxPanel( m_vsplitter );
+    m_detailsSizer = new wxBoxSizer( wxVERTICAL );
+    m_detailsPanel->SetSizer( m_detailsSizer );
 
-    // Use the same draw engine type as the one used in parent frame m_frame
-    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = m_frame->GetCanvas()->GetBackend();
-    m_preview = new DESIGN_BLOCK_PREVIEW_WIDGET( detailsPanel, false, canvasType );
-
-    detailsSizer->Add( m_preview, 1, wxEXPAND, 5 );
-    detailsPanel->Layout();
-    detailsSizer->Fit( detailsPanel );
+    m_detailsPanel->Layout();
+    m_detailsSizer->Fit( m_detailsPanel );
 
     m_vsplitter->SetSashGravity( 0.5 );
     m_vsplitter->SetMinimumPaneSize( 20 );
-    m_vsplitter->SplitHorizontally( treePanel, detailsPanel );
+    m_vsplitter->SplitHorizontally( treePanel, m_detailsPanel );
 
     sizer->Add( m_vsplitter, 1, wxEXPAND, 5 );
 
 
-    m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter,
-                           LIB_TREE::FLAGS::ALL_WIDGETS, nullptr );
+    m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter, LIB_TREE::FLAGS::ALL_WIDGETS, nullptr );
 
     treeSizer->Add( m_tree, 1, wxEXPAND, 5 );
     treePanel->Layout();
@@ -137,10 +134,8 @@ PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame,
 
     Layout();
 
-    Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this,
-          m_dbl_click_timer->GetId() );
-    Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this,
-          m_open_libs_timer->GetId() );
+    Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this, m_dbl_click_timer->GetId() );
+    Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this, m_open_libs_timer->GetId() );
     Bind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this );
     Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this );
 
@@ -170,17 +165,14 @@ void PANEL_DESIGN_BLOCK_CHOOSER::SaveSettings()
 {
     g_designBlockSearchString = m_tree->GetSearchString();
 
-    if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
+    if( APP_SETTINGS_BASE* cfg = m_frame->config() )
     {
         // Save any changes to column widths, etc.
         m_adapter->SaveSettings();
 
         cfg->m_DesignBlockChooserPanel.width = GetParent()->GetSize().x;
         cfg->m_DesignBlockChooserPanel.height = GetParent()->GetSize().y;
-
-        if( m_vsplitter )
-            cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
-
+        cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
         cfg->m_DesignBlockChooserPanel.sort_mode = m_tree->GetSortMode();
     }
 }
@@ -193,6 +185,14 @@ void PANEL_DESIGN_BLOCK_CHOOSER::ShowChangedLanguage()
 }
 
 
+void PANEL_DESIGN_BLOCK_CHOOSER::SetPreviewWidget( DESIGN_BLOCK_PREVIEW_WIDGET* aPreview )
+{
+    m_preview = aPreview;
+    m_detailsSizer->Add( m_preview, 1, wxEXPAND, 5 );
+    Layout();
+}
+
+
 void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
 {
     if( aEvent.GetKeyCode() == WXK_ESCAPE )
@@ -202,8 +202,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
         if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( eventSource ) )
         {
             // First escape cancels search string value
-            if( textCtrl->GetValue() == m_tree->GetSearchString()
-                && !m_tree->GetSearchString().IsEmpty() )
+            if( textCtrl->GetValue() == m_tree->GetSearchString() && !m_tree->GetSearchString().IsEmpty() )
             {
                 m_tree->SetSearchString( wxEmptyString );
                 return;
@@ -219,7 +218,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
 
 void PANEL_DESIGN_BLOCK_CHOOSER::FinishSetup()
 {
-    if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
+    if( APP_SETTINGS_BASE* cfg = m_frame->config() )
     {
         auto horizPixelsFromDU =
                 [&]( int x ) -> int
@@ -228,7 +227,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::FinishSetup()
                     return GetParent()->ConvertDialogToPixels( sz ).x;
                 };
 
-        EESCHEMA_SETTINGS::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel;
+        APP_SETTINGS_BASE::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel;
 
         int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 );
         int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 );
@@ -259,8 +258,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::RefreshLibs( bool aProgress )
     // if a selected item is removed during the sync
     m_tree->Unselect();
 
-    DESIGN_BLOCK_TREE_MODEL_ADAPTER* adapter =
-            static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() );
+    DESIGN_BLOCK_TREE_MODEL_ADAPTER* adapter = static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() );
 
     // Clear all existing libraries then re-add
     adapter->ClearLibraries();
@@ -273,8 +271,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::RefreshLibs( bool aProgress )
     if( aProgress )
     {
         WX_PROGRESS_REPORTER progressReporter( this, _( "Updating Design Block Libraries" ), 2 );
-        DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( fpTable, nullptr,
-                                                                      &progressReporter );
+        DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadDesignBlockFiles( fpTable, nullptr, &progressReporter );
         progressReporter.Show( false );
     }
     else
@@ -336,7 +333,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer( wxTimerEvent& aEvent )
 
 void PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent )
 {
-    if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
+    if( APP_SETTINGS_BASE* cfg = m_frame->config() )
         m_adapter->OpenLibs( cfg->m_LibTree.open_libs );
 
     // Bind this now se we don't spam the event queue with EVT_LIBITEM_SELECTED events during
@@ -348,7 +345,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent )
 void PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected( wxCommandEvent& aEvent )
 {
     if( GetSelectedLibId().IsValid() )
-        m_preview->DisplayDesignBlock( m_frame->GetDesignBlock( GetSelectedLibId() ) );
+        m_preview->DisplayDesignBlock( m_parent->GetDesignBlock( GetSelectedLibId(), true, true ) );
 }
 
 
@@ -405,8 +402,8 @@ void PANEL_DESIGN_BLOCK_CHOOSER::rebuildHistoryNode()
 
     for( const LIB_ID& lib : m_historyList )
     {
-        LIB_TREE_ITEM* fp_info = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetDesignBlockInfo(
-                lib.GetLibNickname(), lib.GetLibItemName() );
+        LIB_TREE_ITEM* fp_info = DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetDesignBlockInfo( lib.GetLibNickname(),
+                                                                                             lib.GetLibItemName() );
 
         // this can be null, for example, if the design block has been deleted from a library.
         if( fp_info != nullptr )
@@ -427,7 +424,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::displayErrors( wxTopLevelWindow* aWindow )
 
     HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) );
 
-    dlg.MessageSet( _( "Errors were encountered loading footprints:" ) );
+    dlg.MessageSet( _( "Errors were encountered loading design blocks:" ) );
 
     wxString msg;
 
diff --git a/eeschema/widgets/panel_design_block_chooser.h b/common/widgets/panel_design_block_chooser.h
similarity index 74%
rename from eeschema/widgets/panel_design_block_chooser.h
rename to common/widgets/panel_design_block_chooser.h
index bad61c0efd..0984657653 100644
--- a/eeschema/widgets/panel_design_block_chooser.h
+++ b/common/widgets/panel_design_block_chooser.h
@@ -31,24 +31,27 @@ class wxPanel;
 class wxTimer;
 class wxSplitterWindow;
 
-class SCH_EDIT_FRAME;
+class EDA_DRAW_FRAME;
+class DESIGN_BLOCK_PANE;
 class DESIGN_BLOCK_PREVIEW_WIDGET;
 
 
 class PANEL_DESIGN_BLOCK_CHOOSER : public wxPanel
 {
 public:
-/**
- * Create dialog to choose design_block.
- *
- * @param aFrame  the parent frame (usually a SCH_EDIT_FRAME or DESIGN_BLOCK_CHOOSER_FRAME)
- * @param aParent the parent window (usually a DIALOG_SHIM or DESIGN_BLOCK_CHOOSER_FRAME)
- * @param aAcceptHandler a handler to be called on double-click of a footprint
- * @param aEscapeHandler a handler to be called on <ESC>
- */
-    PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent,
+    /**
+     * Panel for using design blocks.
+     *
+     * @param aFrame  the parent frame (usually a SCH_EDIT_FRAME or PCB_EDIT_FRAME)
+     * @param aParent the parent design block pane
+     * @param aAcceptHandler a handler to be called on double-click of a footprint
+     * @param aContextMenuTool the tool that will be used to provide an appropriate context menu
+     *                         for the design block actions available in that frame
+     */
+    PANEL_DESIGN_BLOCK_CHOOSER( EDA_DRAW_FRAME* aFrame, DESIGN_BLOCK_PANE* aParent,
                                 std::vector<LIB_ID>&  aHistoryList,
-                                std::function<void()> aSelectHandler );
+                                std::function<void()> aSelectHandler,
+                                TOOL_INTERACTIVE*     aContextMenuTool );
 
     ~PANEL_DESIGN_BLOCK_CHOOSER();
 
@@ -56,7 +59,9 @@ public:
 
     void OnChar( wxKeyEvent& aEvent );
 
-    void FinishSetup();
+    wxPanel* GetDetailsPanel() { return m_detailsPanel; }
+    void     SetPreviewWidget( DESIGN_BLOCK_PREVIEW_WIDGET* aPreview );
+    void     FinishSetup();
 
     void SetPreselect( const LIB_ID& aPreselect );
 
@@ -105,16 +110,19 @@ protected:
 protected:
     static wxString       g_designBlockSearchString;
 
-    wxTimer*              m_dbl_click_timer;
-    wxTimer*              m_open_libs_timer;
-    wxSplitterWindow*     m_vsplitter;
+    wxTimer*          m_dbl_click_timer;
+    wxTimer*          m_open_libs_timer;
+    wxSplitterWindow* m_vsplitter;
+    wxPanel*          m_detailsPanel;
+    wxBoxSizer*       m_detailsSizer;
 
     wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> m_adapter;
 
-    LIB_TREE*                               m_tree;
-    DESIGN_BLOCK_PREVIEW_WIDGET*            m_preview;
+    LIB_TREE*                    m_tree;
+    DESIGN_BLOCK_PREVIEW_WIDGET* m_preview;
 
-    SCH_EDIT_FRAME*       m_frame;
+    DESIGN_BLOCK_PANE*    m_parent;
+    EDA_DRAW_FRAME*       m_frame;
     std::function<void()> m_selectHandler;
 
     std::vector<LIB_ID>   m_historyList;
diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt
index 8b38c7e229..702adaac81 100644
--- a/eeschema/CMakeLists.txt
+++ b/eeschema/CMakeLists.txt
@@ -99,8 +99,6 @@ set( EESCHEMA_DLGS
     dialogs/dialog_change_symbols_base.cpp
     dialogs/dialog_database_lib_settings_base.cpp
     dialogs/dialog_database_lib_settings.cpp
-    dialogs/dialog_design_block_properties_base.cpp
-    dialogs/dialog_design_block_properties.cpp
     dialogs/dialog_edit_symbols_libid.cpp
     dialogs/dialog_edit_symbols_libid_base.cpp
     dialogs/dialog_eeschema_page_settings.cpp
@@ -278,16 +276,15 @@ set( EESCHEMA_SIM_SRCS
     )
 
 set( EESCHEMA_WIDGETS
-    widgets/design_block_pane.cpp
-    widgets/design_block_preview_widget.cpp
     widgets/hierarchy_pane.cpp
-    widgets/panel_design_block_chooser.cpp
     widgets/panel_sch_selection_filter_base.cpp
     widgets/panel_sch_selection_filter.cpp
     widgets/panel_symbol_chooser.cpp
     widgets/pin_shape_combobox.cpp
     widgets/pin_type_combobox.cpp
     widgets/symbol_diff_widget.cpp
+    widgets/sch_design_block_pane.cpp
+    widgets/sch_design_block_preview_widget.cpp
     widgets/sch_properties_panel.cpp
     widgets/sch_search_pane.cpp
     widgets/search_handlers.cpp
@@ -357,8 +354,7 @@ set( EESCHEMA_SRCS
     bus-wire-junction.cpp
     connection_graph.cpp
     cross-probing.cpp
-    design_block_tree_model_adapter.cpp
-    design_block_utils.cpp
+    sch_design_block_utils.cpp
     eeschema_config.cpp
     eeschema_helpers.cpp
     eeschema_jobs_handler.cpp
diff --git a/eeschema/cross-probing.cpp b/eeschema/cross-probing.cpp
index 7426e85761..16a0a6035c 100644
--- a/eeschema/cross-probing.cpp
+++ b/eeschema/cross-probing.cpp
@@ -41,7 +41,7 @@
 #include <tools/sch_actions.h>
 #include <tools/sch_editor_control.h>
 #include <advanced_config.h>
-#include <widgets/design_block_pane.h>
+#include <widgets/sch_design_block_pane.h>
 #include <wx/log.h>
 
 SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference,
diff --git a/eeschema/eeschema_config.cpp b/eeschema/eeschema_config.cpp
index fb78c74d0e..eb2a030ab0 100644
--- a/eeschema/eeschema_config.cpp
+++ b/eeschema/eeschema_config.cpp
@@ -34,8 +34,8 @@
 #include <sch_edit_frame.h>
 #include <sch_painter.h>
 #include <schematic.h>
-#include <widgets/design_block_pane.h>
 #include <widgets/hierarchy_pane.h>
+#include <widgets/sch_design_block_pane.h>
 #include <widgets/sch_search_pane.h>
 #include <widgets/panel_sch_selection_filter.h>
 #include <widgets/properties_panel.h>
diff --git a/eeschema/eeschema_settings.cpp b/eeschema/eeschema_settings.cpp
index 0472c79eee..1a0efdbe76 100644
--- a/eeschema/eeschema_settings.cpp
+++ b/eeschema/eeschema_settings.cpp
@@ -658,59 +658,6 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
     m_params.emplace_back( new PARAM<bool>( "symbol_chooser.place_all_units",
             &m_SymChooserPanel.place_all_units, true ) );
 
-    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_h",
-            &m_DesignBlockChooserPanel.sash_pos_h, -1 ) );
-
-    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_v",
-            &m_DesignBlockChooserPanel.sash_pos_v, -1 ) );
-
-    m_params.emplace_back( new PARAM<int>( "design_block_chooser.width",
-            &m_DesignBlockChooserPanel.width, -1 ) );
-
-    m_params.emplace_back( new PARAM<int>( "design_block_chooser.height",
-            &m_DesignBlockChooserPanel.height, -1 ) );
-
-    m_params.emplace_back( new PARAM<int>( "design_block_chooser.sort_mode",
-            &m_DesignBlockChooserPanel.sort_mode, 0 ) );
-
-    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.repeated_placement",
-            &m_DesignBlockChooserPanel.repeated_placement, false ) );
-
-    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.place_as_sheet",
-            &m_DesignBlockChooserPanel.place_as_sheet, false ) );
-
-    m_params.emplace_back( new PARAM<bool>( "design_block_chooser.keep_annotations",
-            &m_DesignBlockChooserPanel.keep_annotations, false ) );
-
-    m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>(
-            "design_block_chooser.lib_tree.column_widths",
-            [&]() -> nlohmann::json
-            {
-                nlohmann::json ret = {};
-
-                for( const auto& [name, width] : m_DesignBlockChooserPanel.tree.column_widths )
-                    ret[std::string( name.ToUTF8() )] = width;
-
-                return ret;
-            },
-            [&]( const nlohmann::json& aJson )
-            {
-                if( !aJson.is_object() )
-                    return;
-
-                m_DesignBlockChooserPanel.tree.column_widths.clear();
-
-                for( const auto& entry : aJson.items() )
-                {
-                    if( !entry.value().is_number_integer() )
-                        continue;
-
-                    m_DesignBlockChooserPanel.tree.column_widths[ entry.key() ] =
-                            entry.value().get<int>();
-                }
-            },
-            {} ) );
-
     m_params.emplace_back( new PARAM<bool>( "import_graphics.interactive_placement",
             &m_ImportGraphics.interactive_placement, true ) );
 
diff --git a/eeschema/eeschema_settings.h b/eeschema/eeschema_settings.h
index a44278f315..fe5d964950 100644
--- a/eeschema/eeschema_settings.h
+++ b/eeschema/eeschema_settings.h
@@ -285,21 +285,6 @@ public:
         bool place_all_units;
     };
 
-    struct PANEL_DESIGN_BLOCK_CHOOSER
-    {
-        int  sash_pos_h;
-        int  sash_pos_v;
-        int  width;
-        int  height;
-        int  sort_mode;
-        bool repeated_placement;
-        bool place_as_sheet;
-        bool keep_annotations;
-
-        // For saving tree columns and widths
-        LIB_TREE tree;
-    };
-
     struct DIALOG_IMPORT_GRAPHICS
     {
         bool     interactive_placement;
@@ -390,8 +375,6 @@ public:
 
     PANEL_SYM_CHOOSER m_SymChooserPanel;
 
-    PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel;
-
     DIALOG_IMPORT_GRAPHICS m_ImportGraphics;
 
     SELECTION m_Selection;
diff --git a/eeschema/menubar.cpp b/eeschema/menubar.cpp
index a0c39508e8..874da3889a 100644
--- a/eeschema/menubar.cpp
+++ b/eeschema/menubar.cpp
@@ -187,9 +187,7 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
     if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
         showHidePanels->Add( SCH_ACTIONS::showNetNavigator, ACTION_MENU::CHECK );
 
-    if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks )
-        showHidePanels->Add( SCH_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK,
-                             _( "Design Blocks" ) );
+    showHidePanels->Add( SCH_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK, _( "Design Blocks" ) );
 
     viewMenu->Add( showHidePanels );
 
@@ -334,8 +332,7 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
 
     prefsMenu->Add( ACTIONS::configurePaths );
     prefsMenu->Add( ACTIONS::showSymbolLibTable );
-    if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks )
-        prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
+    prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
     prefsMenu->Add( ACTIONS::openPreferences );
 
     prefsMenu->AppendSeparator();
diff --git a/eeschema/sch_design_block_utils.cpp b/eeschema/sch_design_block_utils.cpp
new file mode 100644
index 0000000000..8db9803fce
--- /dev/null
+++ b/eeschema/sch_design_block_utils.cpp
@@ -0,0 +1,446 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+#include <pgm_base.h>
+#include <kiway.h>
+#include <design_block.h>
+#include <design_block_lib_table.h>
+#include <sch_design_block_pane.h>
+#include <sch_edit_frame.h>
+#include <wx/choicdlg.h>
+#include <wx/msgdlg.h>
+#include <wx/textdlg.h>
+#include <wildcards_and_files_ext.h>
+#include <paths.h>
+#include <env_paths.h>
+#include <common.h>
+#include <kidialog.h>
+#include <confirm.h>
+#include <tool/tool_manager.h>
+#include <sch_selection_tool.h>
+#include <dialogs/dialog_design_block_properties.h>
+#include <nlohmann/json.hpp>
+
+bool checkOverwriteDb( wxWindow* aFrame, wxString& libname, wxString& newName )
+{
+    wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), newName.GetData(),
+                                     libname.GetData() );
+
+    if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) )
+        != wxID_OK )
+    {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool checkOverwriteDbSchematic( wxWindow* aFrame, const LIB_ID& aLibId )
+{
+    wxString msg =
+            wxString::Format( _( "Design block '%s' already has a schematic." ), aLibId.GetUniStringLibItemName() );
+
+    if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing schematic?" ), _( "Overwrite" ) )
+        != wxID_OK )
+    {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool SCH_EDIT_FRAME::SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath )
+{
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    // Just block all attempts to create design blocks with nested sheets at this point
+    std::vector<SCH_ITEM*> sheets;
+    aSheetPath.LastScreen()->GetSheets( &sheets );
+
+    if( !sheets.empty() )
+    {
+        DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK blk;
+    wxFileName   fn = wxFileNameFromPath( aSheetPath.Last()->GetName() );
+
+    blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
+
+    // Copy all fields from the sheet to the design block
+    std::vector<SCH_FIELD>&                   shFields = aSheetPath.Last()->GetFields();
+    nlohmann::ordered_map<wxString, wxString> dbFields;
+
+    for( SCH_FIELD& f : shFields )
+    {
+        if( f.GetId() == FIELD_T::SHEET_NAME || f.GetId() == FIELD_T::SHEET_FILENAME )
+            continue;
+
+        dbFields[f.GetCanonicalName()] = f.GetText();
+    }
+
+    blk.SetFields( dbFields );
+
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    wxString libName = blk.GetLibId().GetLibNickname();
+    wxString newName = blk.GetLibId().GetLibItemName();
+
+    if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
+    {
+        return false;
+    }
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+    if( !saveSchematicFile( aSheetPath.Last(), tempFile ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk.SetSchematicFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk.GetLibId() );
+
+    return success;
+}
+
+
+bool SCH_EDIT_FRAME::SaveSheetToDesignBlock( const LIB_ID& aLibId, SCH_SHEET_PATH& aSheetPath )
+{
+    // Make sure the user has selected a library to save into
+    if( !Prj().DesignBlockLibs()->DesignBlockExists( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) )
+    {
+        DisplayErrorMessage( this, _( "Please select a design block to save the schematic to." ) );
+        return false;
+    }
+
+    // Just block all attempts to create design blocks with nested sheets at this point
+    std::vector<SCH_ITEM*> sheets;
+    aSheetPath.LastScreen()->GetSheets( &sheets );
+
+    if( !sheets.empty() )
+    {
+        DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK* blk = nullptr;
+
+    try
+    {
+        blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+        return false;
+    }
+
+    if( !blk->GetSchematicFile().IsEmpty() && !checkOverwriteDbSchematic( this, aLibId ) )
+    {
+        return false;
+    }
+
+    // Copy all fields from the sheet to the design block.
+    // Note: this will overwrite any existing fields in the design block, but
+    // will leave extra fields not in this source sheet alone.
+    std::vector<SCH_FIELD>&                   shFields = aSheetPath.Last()->GetFields();
+    nlohmann::ordered_map<wxString, wxString> dbFields = blk->GetFields();
+
+    for( SCH_FIELD& f : shFields )
+    {
+        if( f.GetId() == FIELD_T::SHEET_NAME || f.GetId() == FIELD_T::SHEET_FILENAME )
+            continue;
+
+        dbFields[f.GetCanonicalName()] = f.GetText();
+    }
+
+    blk->SetFields( dbFields );
+
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, blk, true );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+    if( !saveSchematicFile( aSheetPath.Last(), tempFile ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk->SetSchematicFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk )
+                  == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk->GetLibId() );
+
+    return success;
+}
+
+
+bool SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
+{
+    // Get all selected items
+    SCH_SELECTION selection = m_toolManager->GetTool<SCH_SELECTION_TOOL>()->GetSelection();
+
+    if( selection.Empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
+        return false;
+    }
+
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    // Just block all attempts to create design blocks with nested sheets at this point
+    if( selection.HasType( SCH_SHEET_T ) )
+    {
+        if( selection.Size() == 1 )
+        {
+            SCH_SHEET*     sheet = static_cast<SCH_SHEET*>( selection.Front() );
+            SCH_SHEET_PATH curPath = GetCurrentSheet();
+
+            curPath.push_back( sheet );
+            SaveSheetAsDesignBlock( aLibraryName, curPath );
+        }
+        else
+            DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) );
+
+        return false;
+    }
+
+    DESIGN_BLOCK blk;
+    wxFileName   fn = wxFileNameFromPath( GetScreen()->GetFileName() );
+
+    blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
+
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    wxString libName = blk.GetLibId().GetLibNickname();
+    wxString newName = blk.GetLibId().GetLibItemName();
+
+    if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
+    {
+        return false;
+    }
+
+    // Create a temporary screen
+    SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic );
+
+    // Copy the selected items to the temporary screen
+    for( EDA_ITEM* item : selection )
+    {
+        EDA_ITEM* copy = item->Clone();
+        tempScreen->Append( static_cast<SCH_ITEM*>( copy ) );
+    }
+
+    // Create a sheet for the temporary screen
+    SCH_SHEET* tempSheet = new SCH_SHEET( m_schematic );
+    tempSheet->SetScreen( tempScreen );
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+    if( !saveSchematicFile( tempSheet, tempFile ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk.SetSchematicFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporaries
+    wxRemoveFile( tempFile );
+    // This will also delete the screen
+    delete tempSheet;
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk.GetLibId() );
+
+    return success;
+}
+
+
+bool SCH_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId )
+{
+    // Get all selected items
+    SCH_SELECTION selection = m_toolManager->GetTool<SCH_SELECTION_TOOL>()->GetSelection();
+
+    if( selection.Empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
+        return false;
+    }
+
+    // Make sure the user has selected a library to save into
+    if( !Prj().DesignBlockLibs()->DesignBlockExists( aLibId.GetLibNickname(), aLibId.GetLibItemName() ) )
+    {
+        DisplayErrorMessage( this, _( "Please select a design block to save the schematic to." ) );
+        return false;
+    }
+
+    // Just block all attempts to create design blocks with nested sheets at this point
+    if( selection.HasType( SCH_SHEET_T ) )
+    {
+        if( selection.Size() == 1 )
+        {
+            SCH_SHEET*     sheet = static_cast<SCH_SHEET*>( selection.Front() );
+            SCH_SHEET_PATH curPath = GetCurrentSheet();
+
+            curPath.push_back( sheet );
+            SaveSheetToDesignBlock( aLibId, curPath );
+        }
+        else
+            DisplayErrorMessage( this, _( "Design blocks with nested sheets are not supported." ) );
+
+        return false;
+    }
+
+    DESIGN_BLOCK* blk = nullptr;
+
+    try
+    {
+        blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+        return false;
+    }
+
+    if( !blk->GetSchematicFile().IsEmpty() && !checkOverwriteDbSchematic( this, aLibId ) )
+    {
+        return false;
+    }
+
+    // Create a temporary screen
+    SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic );
+
+    // Copy the selected items to the temporary screen
+    for( EDA_ITEM* item : selection )
+    {
+        EDA_ITEM* copy = item->Clone();
+        tempScreen->Append( static_cast<SCH_ITEM*>( copy ) );
+    }
+
+    // Create a sheet for the temporary screen
+    SCH_SHEET* tempSheet = new SCH_SHEET( m_schematic );
+    tempSheet->SetScreen( tempScreen );
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+    if( !saveSchematicFile( tempSheet, tempFile ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary schematic file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk->SetSchematicFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk )
+                  == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporaries
+    wxRemoveFile( tempFile );
+    // This will also delete the screen
+    delete tempSheet;
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk->GetLibId() );
+
+    return success;
+}
diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp
index 5e2cdf1e2b..aeded91d31 100644
--- a/eeschema/sch_edit_frame.cpp
+++ b/eeschema/sch_edit_frame.cpp
@@ -34,7 +34,7 @@
 #include <dialogs/dialog_schematic_find.h>
 #include <dialogs/dialog_book_reporter.h>
 #include <dialogs/dialog_symbol_fields_table.h>
-#include <widgets/design_block_pane.h>
+#include <widgets/sch_design_block_pane.h>
 #include <eeschema_id.h>
 #include <executable_names.h>
 #include <gal/graphics_abstraction_layer.h>
@@ -214,7 +214,7 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     m_propertiesPanel->SetSplitterProportion( eeconfig()->m_AuiPanels.properties_splitter );
 
     m_selectionFilterPanel = new PANEL_SCH_SELECTION_FILTER( this );
-    m_designBlocksPane = new DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList );
+    m_designBlocksPane = new SCH_DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList );
 
     m_auimgr.SetManagedWindow( this );
 
diff --git a/eeschema/sch_edit_frame.h b/eeschema/sch_edit_frame.h
index f90a934326..a59147a890 100644
--- a/eeschema/sch_edit_frame.h
+++ b/eeschema/sch_edit_frame.h
@@ -56,8 +56,7 @@ class SCH_FIELD;
 class SCH_JUNCTION;
 class SCHEMATIC;
 class SCH_COMMIT;
-class DESIGN_BLOCK;
-class DESIGN_BLOCK_PANE;
+class SCH_DESIGN_BLOCK_PANE;
 class DIALOG_BOOK_REPORTER;
 class DIALOG_ERC;
 class DIALOG_SYMBOL_FIELDS_TABLE;
@@ -761,53 +760,15 @@ public:
      */
     bool CreateArchiveLibrary( const wxString& aFileName );
 
-    /**
-     * If a library name is given, creates a new design block library in the project folder
-     * with the given name.
-     *
-     * If no library name is given it prompts user for a library path, then creates a new design
-     * block library at that location.  If library exists, user is warned about that, and is given
-     * a chance to abort the new creation, and in that case existing library is first deleted.
-     *
-     * @param aProposedName is the initial path and filename shown in the file chooser dialog.
-     * @return The newly created library path if library was successfully created, else
-     *         wxEmptyString because user aborted or error.
-     */
-    wxString CreateNewDesignBlockLibrary( const wxString& aLibName = wxEmptyString,
-                                          const wxString& aProposedName = wxEmptyString );
+    bool SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath );
 
-    /**
-     * Add an existing library to either the global or project library table.
-     *
-     * @param aFileName the library to add; a file open dialog will be displayed if empty.
-     * @return true if successfully added.
-     */
-    bool AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable );
+    bool SaveSelectionAsDesignBlock( const wxString& aLibraryName );
 
-    void SaveSheetAsDesignBlock( const wxString& aLibraryName, SCH_SHEET_PATH& aSheetPath );
+    bool SaveSheetToDesignBlock( const LIB_ID& aLibId, SCH_SHEET_PATH& aSheetPath );
 
-    void SaveSelectionAsDesignBlock( const wxString& aLibraryName );
+    bool SaveSelectionToDesignBlock( const LIB_ID& aLibId );
 
-    bool DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm );
-
-    bool DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm );
-
-    bool EditDesignBlockProperties( const LIB_ID& aLibId );
-
-
-    /**
-     * Load design block from design block library table.
-     *
-     * @param aLibId is the design block library identifier to load.
-     * @param aUseCacheLib set to true to fall back to cache library if design block is not found in
-     *                     design block library table.
-     * @param aShowErrorMessage set to true to show any error messages.
-     * @return The design block found in the library or NULL if the design block was not found.
-     */
-    DESIGN_BLOCK* GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib = false,
-                                  bool aShowErrorMsg = false );
-
-    DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; }
+    SCH_DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; }
 
     void SetNetListerCommand( const wxString& aCommand ) { m_netListerCommand = aCommand; }
 
@@ -1001,20 +962,6 @@ protected:
     void onPluginAvailabilityChanged( wxCommandEvent& aEvt );
 #endif
 
-    /**
-     * Prompt user to select global or project library tables.
-     *
-     * @return Pointer to library table selected or nullptr if none selected/canceled.
-     */
-    DESIGN_BLOCK_LIB_TABLE* selectDesignBlockLibTable( bool aOptional = false );
-
-    /**
-     * Create a new library in the given table (presumed to be either the global or project
-     * library table).
-     */
-    wxString createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName,
-                                          DESIGN_BLOCK_LIB_TABLE* aTable );
-
 private:
     // Called when resizing the Hierarchy Navigator panel
     void OnResizeHierarchyNavigator( wxSizeEvent& aEvent );
@@ -1133,7 +1080,7 @@ private:
 
     std::vector<LIB_ID> m_designBlockHistoryList;
 
-    DESIGN_BLOCK_PANE* m_designBlocksPane;
+    SCH_DESIGN_BLOCK_PANE* m_designBlocksPane;
 
 #ifdef KICAD_IPC_API
     std::unique_ptr<API_HANDLER_SCH> m_apiHandler;
diff --git a/eeschema/toolbars_sch_editor.cpp b/eeschema/toolbars_sch_editor.cpp
index 8db9a17cb4..058e47dfc8 100644
--- a/eeschema/toolbars_sch_editor.cpp
+++ b/eeschema/toolbars_sch_editor.cpp
@@ -37,9 +37,9 @@
 #include <tool/action_toolbar.h>
 #include <tools/sch_actions.h>
 #include <tools/sch_selection_tool.h>
-#include <widgets/design_block_pane.h>
 #include <widgets/hierarchy_pane.h>
 #include <widgets/wx_aui_utils.h>
+#include <widgets/sch_design_block_pane.h>
 #include <widgets/sch_properties_panel.h>
 #include <widgets/sch_search_pane.h>
 #include <toolbars_sch_editor.h>
@@ -229,5 +229,4 @@ void SCH_EDIT_FRAME::configureToolbars()
         };
 
     RegisterCustomToolbarControlFactory( ACTION_TOOLBAR_CONTROLS::ipcScripting, pluginControlFactory );
-
-}
\ No newline at end of file
+}
diff --git a/eeschema/tools/sch_actions.cpp b/eeschema/tools/sch_actions.cpp
index f2f5276218..b0c9bedec6 100644
--- a/eeschema/tools/sch_actions.cpp
+++ b/eeschema/tools/sch_actions.cpp
@@ -31,6 +31,8 @@
 #include <sch_line_wire_bus_tool.h>
 #include <tool/tool_action.h>
 
+class DESIGN_BLOCK;
+
 // Actions, being statically-defined, require specialized I18N handling.  We continue to
 // use the _() macro so that string harvesting by the I18N framework doesn't have to be
 // specialized, but we don't translate on initialization and instead do it in the getters.
@@ -164,6 +166,20 @@ TOOL_ACTION SCH_ACTIONS::saveSelectionAsDesignBlock( TOOL_ACTION_ARGS()
         .Tooltip( _( "Create a new design block from the current selection" ) )
         .Icon( BITMAPS::new_component ) );
 
+TOOL_ACTION SCH_ACTIONS::saveSheetToDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "eeschema.SchDesignBlockControl.saveSheetToDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Current Sheet to Design Block..." ) )
+        .Tooltip( _( "Add current sheet to design block" ) )
+        .Icon( BITMAPS::save ) );
+
+TOOL_ACTION SCH_ACTIONS::saveSelectionToDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "eeschema.SchDesignBlockControl.saveSelectionToDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Selection to Design Block..." ) )
+        .Tooltip( _( "Add current selection to design block" ) )
+        .Icon( BITMAPS::save ) );
+
 TOOL_ACTION SCH_ACTIONS::deleteDesignBlock( TOOL_ACTION_ARGS()
         .Name( "eeschema.SchDesignBlockControl.saveDeleteDesignBlock" )
         .Scope( AS_GLOBAL )
diff --git a/eeschema/tools/sch_actions.h b/eeschema/tools/sch_actions.h
index 78e149820d..670577cf8f 100644
--- a/eeschema/tools/sch_actions.h
+++ b/eeschema/tools/sch_actions.h
@@ -27,6 +27,7 @@
 #include <tool/tool_action.h>
 #include <tool/actions.h>
 
+class DESIGN_BLOCK;
 class SCH_SYMBOL;
 class TOOL_EVENT;
 class TOOL_MANAGER;
@@ -202,6 +203,8 @@ public:
     static TOOL_ACTION showDesignBlockPanel;
     static TOOL_ACTION saveSheetAsDesignBlock;
     static TOOL_ACTION saveSelectionAsDesignBlock;
+    static TOOL_ACTION saveSheetToDesignBlock;
+    static TOOL_ACTION saveSelectionToDesignBlock;
     static TOOL_ACTION deleteDesignBlock;
     static TOOL_ACTION editDesignBlockProperties;
 
diff --git a/eeschema/tools/sch_design_block_control.cpp b/eeschema/tools/sch_design_block_control.cpp
index 77e044ffb1..1dc85e0119 100644
--- a/eeschema/tools/sch_design_block_control.cpp
+++ b/eeschema/tools/sch_design_block_control.cpp
@@ -20,112 +20,63 @@
  * or you may write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
-#include <tool/library_editor_control.h>
-#include <sch_design_block_control.h>
-#include <design_block_pane.h>
-#include <panel_design_block_chooser.h>
 #include <dialog_design_block_properties.h>
 #include <sch_actions.h>
+#include <sch_selection_tool.h>
+#include <sch_design_block_control.h>
+#include <sch_edit_frame.h>
+#include <tool/tool_manager.h>
+#include <widgets/sch_design_block_pane.h>
+#include <widgets/panel_design_block_chooser.h>
+
+
+SCH_DESIGN_BLOCK_CONTROL::~SCH_DESIGN_BLOCK_CONTROL()
+{
+}
+
 
 bool SCH_DESIGN_BLOCK_CONTROL::Init()
 {
     m_editFrame     = getEditFrame<SCH_EDIT_FRAME>();
     m_frame         = m_editFrame;
-    m_selectionTool = m_toolMgr->GetTool<SCH_SELECTION_TOOL>();
-
-    auto pinnedLib =
-            [this]( const SELECTION& aSel )
-            {
-                //
-                LIB_TREE_NODE* current = getCurrentTreeNode();
-                return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
-                       && current->m_Pinned;
-            };
-    auto unpinnedLib =
-            [this](const SELECTION& aSel )
-            {
-                LIB_TREE_NODE* current = getCurrentTreeNode();
-                return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
-                       && !current->m_Pinned;
-            };
+    m_framesToNotify = { FRAME_PCB_EDITOR };
 
     auto isInLibrary =
             [this](const SELECTION& aSel )
             {
-                LIB_TREE_NODE* current = getCurrentTreeNode();
-                return current
-                       && ( current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
-                            || current->m_Type == LIB_TREE_NODE::TYPE::ITEM );
+                return this->selIsInLibrary(aSel);
             };
 
     auto isDesignBlock =
             [this](const SELECTION& aSel )
             {
-                LIB_TREE_NODE* current = getCurrentTreeNode();
-                return current && current->m_Type == LIB_TREE_NODE::TYPE::ITEM;
+                return this->selIsDesignBlock(aSel);
+            };
+
+    auto hasSelection =
+            [this](const SELECTION& aSel )
+            {
+                return !m_editFrame->GetCurrentSelection().Empty();
             };
 
     CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
-
-    ctxMenu.AddItem( ACTIONS::pinLibrary,                     unpinnedLib, 1 );
-    ctxMenu.AddItem( ACTIONS::unpinLibrary,                   pinnedLib, 1 );
-    ctxMenu.AddItem( ACTIONS::newLibrary,                     !isDesignBlock, 1 );
-    ctxMenu.AddSeparator( 1 );
+    AddContextMenuItems( &ctxMenu );
 
     ctxMenu.AddItem( SCH_ACTIONS::placeDesignBlock,           isDesignBlock, 50 );
     ctxMenu.AddSeparator( 50 );
 
     ctxMenu.AddItem( SCH_ACTIONS::editDesignBlockProperties,  isDesignBlock, 100 );
     ctxMenu.AddItem( SCH_ACTIONS::saveSheetAsDesignBlock,     isInLibrary, 100 );
-    ctxMenu.AddItem( SCH_ACTIONS::saveSelectionAsDesignBlock, isInLibrary, 100 );
+    ctxMenu.AddItem( SCH_ACTIONS::saveSelectionAsDesignBlock, isInLibrary && hasSelection, 100 );
+    ctxMenu.AddItem( SCH_ACTIONS::saveSheetToDesignBlock,     isDesignBlock, 100 );
+    ctxMenu.AddItem( SCH_ACTIONS::saveSelectionToDesignBlock, isDesignBlock && hasSelection, 100 );
     ctxMenu.AddItem( SCH_ACTIONS::deleteDesignBlock,          isDesignBlock, 100 );
     ctxMenu.AddSeparator( 100 );
 
-    ctxMenu.AddItem( ACTIONS::hideLibraryTree,                SELECTION_CONDITIONS::ShowAlways, 400 );
-
     return true;
 }
 
 
-int SCH_DESIGN_BLOCK_CONTROL::PinLibrary( const TOOL_EVENT& aEvent )
-{
-    LIB_TREE_NODE* current = getCurrentTreeNode();
-
-    if( current && !current->m_Pinned )
-    {
-        m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(),
-                                   PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
-        current->m_Pinned = true;
-        getDesignBlockPane()->RefreshLibs();
-    }
-
-    return 0;
-}
-
-
-int SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent )
-{
-    LIB_TREE_NODE* current = getCurrentTreeNode();
-
-    if( current && current->m_Pinned )
-    {
-        m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(),
-                                     PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
-        current->m_Pinned = false;
-        getDesignBlockPane()->RefreshLibs();
-    }
-
-    return 0;
-}
-
-
-int SCH_DESIGN_BLOCK_CONTROL::NewLibrary( const TOOL_EVENT& aEvent )
-{
-    m_editFrame->CreateNewDesignBlockLibrary();
-    return 0;
-}
-
-
 int SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent )
 {
     LIB_TREE_NODE* current = getCurrentTreeNode();
@@ -133,8 +84,10 @@ int SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent )
     if( !current )
         return -1;
 
-    m_editFrame->SaveSheetAsDesignBlock( current->m_LibId.GetLibNickname(),
-                                         m_editFrame->GetCurrentSheet() );
+    if( !m_editFrame->SaveSheetAsDesignBlock( current->m_LibId.GetLibNickname(), m_editFrame->GetCurrentSheet() ) )
+        return -1;
+
+    notifyOtherFrames();
 
     return 0;
 }
@@ -147,67 +100,57 @@ int SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock( const TOOL_EVENT& aEve
     if( !current )
         return -1;
 
-    m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() );
+    if( !m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() ) )
+        return -1;
+
+    notifyOtherFrames();
 
     return 0;
 }
 
 
-int SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock( const TOOL_EVENT& aEvent )
+int SCH_DESIGN_BLOCK_CONTROL::SaveSheetToDesignBlock( const TOOL_EVENT& aEvent )
 {
     LIB_TREE_NODE* current = getCurrentTreeNode();
 
     if( !current )
         return -1;
 
-    m_editFrame->DeleteDesignBlockFromLibrary( current->m_LibId, true );
+    if( !m_editFrame->SaveSheetToDesignBlock( current->m_LibId, m_editFrame->GetCurrentSheet() ) )
+        return -1;
+
+    notifyOtherFrames();
 
     return 0;
 }
 
 
-int SCH_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties( const TOOL_EVENT& aEvent )
+int SCH_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent )
 {
     LIB_TREE_NODE* current = getCurrentTreeNode();
 
     if( !current )
         return -1;
 
-    if( m_editFrame->EditDesignBlockProperties( current->m_LibId ) )
-        return 0;
+    if( !m_editFrame->SaveSelectionToDesignBlock( current->m_LibId ) )
+        return -1;
 
-    return -1;
-}
+    notifyOtherFrames();
 
-
-int SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree( const TOOL_EVENT& aEvent )
-{
-    m_editFrame->ToggleLibraryTree();
     return 0;
 }
 
 
 void SCH_DESIGN_BLOCK_CONTROL::setTransitions()
 {
-    Go( &SCH_DESIGN_BLOCK_CONTROL::PinLibrary,                  ACTIONS::pinLibrary.MakeEvent() );
-    Go( &SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary,                ACTIONS::unpinLibrary.MakeEvent() );
-
-    Go( &SCH_DESIGN_BLOCK_CONTROL::NewLibrary,                  ACTIONS::newLibrary.MakeEvent() );
+    DESIGN_BLOCK_CONTROL::setTransitions();
 
     Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock,      SCH_ACTIONS::saveSheetAsDesignBlock.MakeEvent() );
     Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock,  SCH_ACTIONS::saveSelectionAsDesignBlock.MakeEvent() );
+    Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSheetToDesignBlock,      SCH_ACTIONS::saveSheetToDesignBlock.MakeEvent() );
+    Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock,  SCH_ACTIONS::saveSelectionToDesignBlock.MakeEvent() );
     Go( &SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock,           SCH_ACTIONS::deleteDesignBlock.MakeEvent() );
     Go( &SCH_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties,   SCH_ACTIONS::editDesignBlockProperties.MakeEvent() );
-
-    Go( &SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree,             ACTIONS::hideLibraryTree.MakeEvent() );
-}
-
-
-LIB_ID SCH_DESIGN_BLOCK_CONTROL::getSelectedLibId()
-{
-    m_editFrame->GetDesignBlockPane()->GetSelectedLibId();
-
-    return LIB_ID();
 }
 
 
@@ -215,10 +158,3 @@ DESIGN_BLOCK_PANE* SCH_DESIGN_BLOCK_CONTROL::getDesignBlockPane()
 {
     return m_editFrame->GetDesignBlockPane();
 }
-
-
-LIB_TREE_NODE* SCH_DESIGN_BLOCK_CONTROL::getCurrentTreeNode()
-{
-    LIB_TREE* libTree = getDesignBlockPane()->GetDesignBlockPanel()->GetLibTree();
-    return libTree ? libTree->GetCurrentTreeNode() : nullptr;
-}
diff --git a/eeschema/tools/sch_design_block_control.h b/eeschema/tools/sch_design_block_control.h
index cb71680a70..fa0235b2f2 100644
--- a/eeschema/tools/sch_design_block_control.h
+++ b/eeschema/tools/sch_design_block_control.h
@@ -25,42 +25,35 @@
 #ifndef SCH_DESIGN_BLOCK_CONTROL_H
 #define SCH_DESIGN_BLOCK_CONTROL_H
 
-#include <sch_base_frame.h>
-#include <tools/sch_tool_base.h>
+#include <tool/design_block_control.h>
+
+class DESIGN_BLOCK_PANE;
+class SCH_EDIT_FRAME;
 
 /**
- * Handle schematic design block actions in the schematic editor.
+ * Handle design block actions in the schematic editor.
  */
-class SCH_DESIGN_BLOCK_CONTROL : public wxEvtHandler, public SCH_TOOL_BASE<SCH_BASE_FRAME>
+class SCH_DESIGN_BLOCK_CONTROL : public DESIGN_BLOCK_CONTROL
 {
 public:
-    SCH_DESIGN_BLOCK_CONTROL() :
-            SCH_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.SchDesignBlockControl" )
-    {}
+    SCH_DESIGN_BLOCK_CONTROL() : DESIGN_BLOCK_CONTROL( "eeschema.SchDesignBlockControl" ) {}
+    virtual ~SCH_DESIGN_BLOCK_CONTROL();
 
     /// @copydoc TOOL_INTERACTIVE::Init()
     bool Init() override;
 
-    int PinLibrary( const TOOL_EVENT& aEvent );
-    int UnpinLibrary( const TOOL_EVENT& aEvent );
-
-    int NewLibrary( const TOOL_EVENT& aEvent );
-    int DeleteLibrary( const TOOL_EVENT& aEvent );
-
     int SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent );
     int SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent );
-    int DeleteDesignBlock( const TOOL_EVENT& aEvent );
-    int EditDesignBlockProperties( const TOOL_EVENT& aEvent );
 
-    int HideLibraryTree( const TOOL_EVENT& aEvent );
+    int SaveSheetToDesignBlock( const TOOL_EVENT& aEvent );
+    int SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent );
 
 private:
     LIB_ID getSelectedLibId();
     ///< Set up handlers for various events.
     void setTransitions() override;
 
-    DESIGN_BLOCK_PANE* getDesignBlockPane();
-    LIB_TREE_NODE*     getCurrentTreeNode();
+    DESIGN_BLOCK_PANE* getDesignBlockPane() override;
 
     SCH_EDIT_FRAME*    m_editFrame = nullptr;
 };
diff --git a/eeschema/tools/sch_drawing_tools.cpp b/eeschema/tools/sch_drawing_tools.cpp
index a96e2d34cb..726cac581c 100644
--- a/eeschema/tools/sch_drawing_tools.cpp
+++ b/eeschema/tools/sch_drawing_tools.cpp
@@ -40,7 +40,7 @@
 #include <sch_edit_frame.h>
 #include <pgm_base.h>
 #include <design_block.h>
-#include <design_block_pane.h>
+#include <widgets/sch_design_block_pane.h>
 #include <eeschema_id.h>
 #include <confirm.h>
 #include <view/view_controls.h>
@@ -654,8 +654,8 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent )
     {
         if( m_frame->GetDesignBlockPane()->GetSelectedLibId().IsValid() )
         {
-            designBlock =
-                    m_frame->GetDesignBlock( m_frame->GetDesignBlockPane()->GetSelectedLibId() );
+            designBlock = m_frame->GetDesignBlockPane()->GetDesignBlock(
+                    m_frame->GetDesignBlockPane()->GetSelectedLibId(), true, true );
 
             if( !designBlock )
                 return 0;
@@ -826,7 +826,7 @@ int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent )
 
                 sheetFileName = dlg.GetPath();
 
-                m_frame->UpdateDesignBlockOptions();
+                m_frame->GetDesignBlockPane()->UpdateCheckboxes();
             }
 
             if( sheetFileName.IsEmpty() )
diff --git a/eeschema/widgets/design_block_pane.h b/eeschema/widgets/design_block_pane.h
deleted file mode 100644
index af9a51ecdf..0000000000
--- a/eeschema/widgets/design_block_pane.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * This program source code file is part of KiCad, a free EDA CAD application.
- *
- * Copyright The 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 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
- */
-#ifndef DESIGN_BLOCK_PANE_H
-#define DESIGN_BLOCK_PANE_H
-
-#include <design_block_tree_model_adapter.h>
-#include <widgets/html_window.h>
-#include <widgets/wx_panel.h>
-#include <wx/checkbox.h>
-#include <wx/filedlgcustomize.h>
-#include <eeschema_settings.h>
-
-
-class SCH_EDIT_FRAME;
-class PANEL_DESIGN_BLOCK_CHOOSER;
-
-
-class DESIGN_BLOCK_PANE : public WX_PANEL
-{
-public:
-    /**
-     * Create dialog to choose design_block.
-     *
-     * @param aParent   a SCH_BASE_FRAME parent window.
-     * @param aAllowFieldEdits  if false, all functions that allow the user to edit fields
-     *                          (currently just footprint selection) will not be available.
-     * @param aShowFootprints   if false, all footprint preview and selection features are
-     *                          disabled. This forces aAllowFieldEdits false too.
-     */
-    DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
-                       std::vector<LIB_ID>& aHistoryList );
-
-    ~DESIGN_BLOCK_PANE() override;
-
-    void SaveSettings();
-
-    /**
-     * To be called after this dialog returns from ShowModal().
-     *
-     * For multi-unit design_blocks, if the user selects the design_block itself rather than picking
-     * an individual unit, 0 will be returned in aUnit.
-     * Beware that this is an invalid unit number - this should be replaced with whatever
-     * default is desired (usually 1).
-     *
-     * @param aUnit if not NULL, the selected unit is filled in here.
-     * @return the #LIB_ID of the design_block that has been selected.
-     */
-    LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const;
-    void   SelectLibId( const LIB_ID& aLibId );
-
-    void RefreshLibs();
-
-    /* Handler for checkbox events */
-    void OnCheckBox( wxCommandEvent& aEvent );
-    void UpdateCheckboxes();
-
-    void OnSaveSheetAsDesignBlock( wxCommandEvent& aEvent );
-    void OnSaveSelectionAsDesignBlock( wxCommandEvent& aEvent );
-
-    void OnDeleteLibrary( wxCommandEvent& aEvent );
-    void OnDeleteDesignBlock( wxCommandEvent& aEvent );
-
-    PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; }
-
-protected:
-    void setLabelsAndTooltips();
-
-    virtual void OnLanguageChanged( wxCommandEvent& aEvent );
-    void OnClosed( wxAuiManagerEvent& aEvent );
-
-protected:
-    PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel;
-
-    wxCheckBox*                 m_repeatedPlacement;
-    wxCheckBox*                 m_placeAsSheet;
-    wxCheckBox*                 m_keepAnnotations;
-
-    SCH_EDIT_FRAME*             m_frame;
-};
-
-
-// This is a helper class for the file dialog to allow the user to choose similar options
-// as the design block chooser when importing a sheet.
-class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook
-{
-public:
-    FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings );
-
-    virtual void AddCustomControls( wxFileDialogCustomize& customizer ) override;
-
-    virtual void TransferDataFromCustomControls() override;
-
-private:
-    EESCHEMA_SETTINGS* m_settings;
-
-    wxFileDialogCheckBox* m_cbRepeatedPlacement;
-    wxFileDialogCheckBox* m_cbPlaceAsSheet;
-    wxFileDialogCheckBox* m_cbKeepAnnotations;
-
-    wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS );
-};
-
-#endif
diff --git a/eeschema/widgets/design_block_pane.cpp b/eeschema/widgets/sch_design_block_pane.cpp
similarity index 68%
rename from eeschema/widgets/design_block_pane.cpp
rename to eeschema/widgets/sch_design_block_pane.cpp
index 459d7bdf39..508e2e7e9b 100644
--- a/eeschema/widgets/design_block_pane.cpp
+++ b/eeschema/widgets/sch_design_block_pane.cpp
@@ -22,7 +22,8 @@
  */
 
 #include <design_block.h>
-#include <widgets/design_block_pane.h>
+#include <widgets/sch_design_block_pane.h>
+#include <widgets/sch_design_block_preview_widget.h>
 #include <widgets/panel_design_block_chooser.h>
 #include <kiface_base.h>
 #include <sch_edit_frame.h>
@@ -33,6 +34,7 @@
 #include <wx/sizer.h>
 #include <sch_actions.h>
 #include <tool/tool_manager.h>
+#include <tools/sch_design_block_control.h>
 
 
 // Do not make these static wxStrings; they need to respond to language changes
@@ -40,20 +42,24 @@
 #define PLACE_AS_SHEET     _( "Place as sheet" )
 #define KEEP_ANNOTATIONS   _( "Keep annotations" )
 
-DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
-                                      std::vector<LIB_ID>& aHistoryList ) :
-        WX_PANEL( aParent ),
-        m_frame( aParent )
+SCH_DESIGN_BLOCK_PANE::SCH_DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
+                                              std::vector<LIB_ID>& aHistoryList ) :
+        DESIGN_BLOCK_PANE( aParent, aPreselect, aHistoryList )
 {
     wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
 
     m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList,
-            // Accept handler
-            [this]()
-            {
-                m_frame->GetToolManager()->RunAction( SCH_ACTIONS::placeDesignBlock );
-            } );
-
+                                                     [aParent]()
+                                                     {
+                                                         aParent->GetToolManager()->RunAction(
+                                                                 SCH_ACTIONS::placeDesignBlock );
+                                                     },
+                                                        aParent->GetToolManager()->GetTool<SCH_DESIGN_BLOCK_CONTROL>()
+                                                     );
+    // Use the same draw engine type as the one used in parent frame m_frame
+    EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = m_frame->GetCanvas()->GetBackend();
+    m_chooserPanel->SetPreviewWidget(
+            new SCH_DESIGN_BLOCK_PREVIEW_WIDGET( m_chooserPanel->GetDetailsPanel(), canvasType, true ) );
     sizer->Add( m_chooserPanel, 1, wxEXPAND, 5 );
 
     if( aPreselect && aPreselect->IsValid() )
@@ -70,13 +76,13 @@ DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPr
     UpdateCheckboxes();
 
     // Set all checkbox handlers to the same function
-    m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
-    m_placeAsSheet->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
-    m_keepAnnotations->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
+    m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this );
+    m_placeAsSheet->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this );
+    m_keepAnnotations->Bind( wxEVT_CHECKBOX, &SCH_DESIGN_BLOCK_PANE::OnCheckBox, this );
 
-    cbSizer->Add( m_repeatedPlacement, 0, wxTOP|wxLEFT, 2 );
-    cbSizer->Add( m_placeAsSheet, 0, wxTOP|wxLEFT, 2 );
-    cbSizer->Add( m_keepAnnotations, 0, wxTOP|wxLEFT|wxBOTTOM, 2 );
+    cbSizer->Add( m_repeatedPlacement, 0, wxTOP | wxLEFT, 2 );
+    cbSizer->Add( m_placeAsSheet, 0, wxTOP | wxLEFT, 2 );
+    cbSizer->Add( m_keepAnnotations, 0, wxTOP | wxLEFT | wxBOTTOM, 2 );
 
     sizer->Add( cbSizer, 0, wxEXPAND, 5 );
     SetSizer( sizer );
@@ -85,31 +91,10 @@ DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPr
     Layout();
 
     Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, m_chooserPanel );
-    m_frame->Bind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
-    m_frame->Bind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
 }
 
 
-DESIGN_BLOCK_PANE::~DESIGN_BLOCK_PANE()
-{
-    m_frame->Unbind( wxEVT_AUI_PANE_CLOSE, &DESIGN_BLOCK_PANE::OnClosed, this );
-    m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
-}
-
-
-void DESIGN_BLOCK_PANE::OnClosed( wxAuiManagerEvent &aEvent )
-{
-    if( APP_SETTINGS_BASE* cfg = m_frame->config() )
-    {
-        if( IsShownOnScreen() )     // Ensure the panel is shown when trying to save its size
-            m_frame->SaveSettings( cfg );
-    }
-
-    aEvent.Skip();
-}
-
-
-void DESIGN_BLOCK_PANE::setLabelsAndTooltips()
+void SCH_DESIGN_BLOCK_PANE::setLabelsAndTooltips()
 {
     if( m_repeatedPlacement )
     {
@@ -134,18 +119,7 @@ void DESIGN_BLOCK_PANE::setLabelsAndTooltips()
 }
 
 
-void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent )
-{
-    if( m_chooserPanel )
-        m_chooserPanel->ShowChangedLanguage();
-
-    setLabelsAndTooltips();
-
-    aEvent.Skip();
-}
-
-
-void DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent )
+void SCH_DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent )
 {
     if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
     {
@@ -156,7 +130,7 @@ void DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent )
 }
 
 
-void DESIGN_BLOCK_PANE::UpdateCheckboxes()
+void SCH_DESIGN_BLOCK_PANE::UpdateCheckboxes()
 {
     if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
     {
@@ -167,34 +141,8 @@ void DESIGN_BLOCK_PANE::UpdateCheckboxes()
 }
 
 
-void DESIGN_BLOCK_PANE::SaveSettings()
-{
-    m_chooserPanel->SaveSettings();
-}
-
-
-LIB_ID DESIGN_BLOCK_PANE::GetSelectedLibId( int* aUnit ) const
-{
-    return m_chooserPanel->GetSelectedLibId( aUnit );
-}
-
-
-void DESIGN_BLOCK_PANE::SelectLibId( const LIB_ID& aLibId )
-{
-    m_chooserPanel->SelectLibId( aLibId );
-}
-
-
-void DESIGN_BLOCK_PANE::RefreshLibs()
-{
-    m_chooserPanel->RefreshLibs();
-}
-
-
 FILEDLG_IMPORT_SHEET_CONTENTS::FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings ) :
-    m_cbRepeatedPlacement( nullptr ),
-    m_cbPlaceAsSheet( nullptr ),
-    m_cbKeepAnnotations( nullptr )
+        m_cbRepeatedPlacement( nullptr ), m_cbPlaceAsSheet( nullptr ), m_cbKeepAnnotations( nullptr )
 {
     wxASSERT( aSettings );
     m_settings = aSettings;
diff --git a/eeschema/widgets/sch_design_block_pane.h b/eeschema/widgets/sch_design_block_pane.h
new file mode 100644
index 0000000000..f4f8e863fc
--- /dev/null
+++ b/eeschema/widgets/sch_design_block_pane.h
@@ -0,0 +1,79 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+#ifndef SCH_DESIGN_BLOCK_PANE_H
+#define SCH_DESIGN_BLOCK_PANE_H
+
+#include <widgets/design_block_pane.h>
+#include <design_block_tree_model_adapter.h>
+#include <widgets/html_window.h>
+#include <widgets/wx_panel.h>
+#include <wx/checkbox.h>
+#include <wx/filedlgcustomize.h>
+#include <eeschema_settings.h>
+
+
+class SCH_EDIT_FRAME;
+class PANEL_DESIGN_BLOCK_CHOOSER;
+
+
+class SCH_DESIGN_BLOCK_PANE : public DESIGN_BLOCK_PANE
+{
+public:
+    SCH_DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList );
+
+    /* Handler for checkbox events */
+    void OnCheckBox( wxCommandEvent& aEvent );
+    void UpdateCheckboxes();
+
+protected:
+    void setLabelsAndTooltips() override;
+
+protected:
+    wxCheckBox* m_repeatedPlacement;
+    wxCheckBox* m_placeAsSheet;
+    wxCheckBox* m_keepAnnotations;
+};
+
+
+// This is a helper class for the file dialog to allow the user to choose similar options
+// as the design block chooser when importing a sheet.
+class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook
+{
+public:
+    FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings );
+
+    void AddCustomControls( wxFileDialogCustomize& customizer ) override;
+
+    void TransferDataFromCustomControls() override;
+
+private:
+    EESCHEMA_SETTINGS* m_settings;
+
+    wxFileDialogCheckBox* m_cbRepeatedPlacement;
+    wxFileDialogCheckBox* m_cbPlaceAsSheet;
+    wxFileDialogCheckBox* m_cbKeepAnnotations;
+
+    wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS );
+};
+
+#endif
diff --git a/eeschema/widgets/design_block_preview_widget.cpp b/eeschema/widgets/sch_design_block_preview_widget.cpp
similarity index 82%
rename from eeschema/widgets/design_block_preview_widget.cpp
rename to eeschema/widgets/sch_design_block_preview_widget.cpp
index 4a048ab0df..56d64a2ed6 100644
--- a/eeschema/widgets/design_block_preview_widget.cpp
+++ b/eeschema/widgets/sch_design_block_preview_widget.cpp
@@ -17,7 +17,6 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "design_block_preview_widget.h"
 #include <schematic.h>
 #include <sch_sheet.h>
 #include <sch_view.h>
@@ -35,13 +34,16 @@
 #include <eeschema_settings.h>
 #include <eeschema_helpers.h>
 #include <settings/settings_manager.h>
+#include <widgets/sch_design_block_preview_widget.h>
 #include <wx/log.h>
 #include <wx/stattext.h>
+#include <wx/panel.h>
 
 
-DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus,
-                                              EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType ) :
-        wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ),
+SCH_DESIGN_BLOCK_PREVIEW_WIDGET::SCH_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent,
+                                                                 EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType,
+                                                                 bool aIncludeStatus ) :
+        DESIGN_BLOCK_PREVIEW_WIDGET( aParent ),
         m_preview( nullptr ),
         m_status( nullptr ),
         m_statusPanel( nullptr ),
@@ -58,14 +60,13 @@ DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, boo
     EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = aCanvasType;
 
     // Allows only a CAIRO or OPENGL canvas:
-    if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL
-        && canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK )
+    if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL && canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK )
     {
         canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
     }
 
-    m_preview = new SCH_PREVIEW_PANEL( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ),
-                                       m_galDisplayOptions, canvasType );
+    m_preview = new SCH_PREVIEW_PANEL( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), m_galDisplayOptions,
+                                       canvasType );
     m_preview->SetStealsFocus( false );
     m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
     m_preview->GetGAL()->SetAxesEnabled( false );
@@ -131,11 +132,11 @@ DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, boo
     SetSizer( m_outerSizer );
     Layout();
 
-    Connect( wxEVT_SIZE, wxSizeEventHandler( DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this );
+    Connect( wxEVT_SIZE, wxSizeEventHandler( SCH_DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this );
 }
 
 
-DESIGN_BLOCK_PREVIEW_WIDGET::~DESIGN_BLOCK_PREVIEW_WIDGET()
+SCH_DESIGN_BLOCK_PREVIEW_WIDGET::~SCH_DESIGN_BLOCK_PREVIEW_WIDGET()
 {
     if( m_previewItem )
         m_preview->GetView()->Remove( m_previewItem );
@@ -144,7 +145,7 @@ DESIGN_BLOCK_PREVIEW_WIDGET::~DESIGN_BLOCK_PREVIEW_WIDGET()
 }
 
 
-void DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
+void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
 {
     wxCHECK( m_statusPanel, /* void */ );
 
@@ -155,7 +156,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
 }
 
 
-void DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
+void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
 {
     if( m_previewItem )
     {
@@ -167,7 +168,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
 }
 
 
-void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
+void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
 {
     if( !m_previewItem )
         return;
@@ -179,8 +180,8 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
     view->SetScale( 1.0 );
     VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false );
     // Calculate the draw scale to fit the drawing area
-    double scale = std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ),
-                             fabs( clientSize.y / m_itemBBox.GetHeight() ) );
+    double scale =
+            std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), fabs( clientSize.y / m_itemBBox.GetHeight() ) );
 
     // Above calculation will yield an exact fit; add a bit of whitespace around block
     scale /= 1.2;
@@ -191,7 +192,7 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
 }
 
 
-void DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock )
+void SCH_DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock )
 {
     KIGFX::VIEW* view = m_preview->GetView();
 
@@ -202,10 +203,10 @@ void DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock
         m_previewItem = nullptr;
     }
 
-    if( aDesignBlock )
+    if( aDesignBlock && wxFileExists( aDesignBlock->GetSchematicFile() ) )
     {
-        m_previewItem = EESCHEMA_HELPERS::LoadSchematic( aDesignBlock->GetSchematicFile(),
-                                                         SCH_IO_MGR::SCH_KICAD, false, true );
+        m_previewItem =
+                EESCHEMA_HELPERS::LoadSchematic( aDesignBlock->GetSchematicFile(), SCH_IO_MGR::SCH_KICAD, false, true );
         BOX2I bBox;
 
         if( m_previewItem )
diff --git a/eeschema/widgets/design_block_preview_widget.h b/eeschema/widgets/sch_design_block_preview_widget.h
similarity index 74%
rename from eeschema/widgets/design_block_preview_widget.h
rename to eeschema/widgets/sch_design_block_preview_widget.h
index da95f38581..0b20600dc4 100644
--- a/eeschema/widgets/design_block_preview_widget.h
+++ b/eeschema/widgets/sch_design_block_preview_widget.h
@@ -17,10 +17,11 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef DESIGN_BLOCK_PREVIEW_WIDGET_H
-#define DESIGN_BLOCK_PREVIEW_WIDGET_H
+#ifndef SCH_DESIGN_BLOCK_PREVIEW_WIDGET_H
+#define SCH_DESIGN_BLOCK_PREVIEW_WIDGET_H
 
 #include <wx/panel.h>
+#include <widgets/design_block_preview_widget.h>
 #include <kiway.h>
 #include <gal_display_options_common.h>
 #include <class_draw_panel_gal.h>
@@ -34,29 +35,27 @@ class wxStaticText;
 class wxSizer;
 
 
-class DESIGN_BLOCK_PREVIEW_WIDGET : public wxPanel
+class SCH_DESIGN_BLOCK_PREVIEW_WIDGET : public DESIGN_BLOCK_PREVIEW_WIDGET
 {
 public:
     /**
-     * Construct a symbol preview widget.
+     * Construct a schematic design block preview widget.
      *
      * @param aParent - parent window
-     * @param aCanvasType = the type of canvas (GAL_TYPE_OPENGL or GAL_TYPE_CAIRO only)
      */
-    DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus,
-                                 EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType );
+    SCH_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType, bool aIncludeStatus );
 
-    ~DESIGN_BLOCK_PREVIEW_WIDGET() override;
+    ~SCH_DESIGN_BLOCK_PREVIEW_WIDGET() override;
 
     /**
      * Set the contents of the status label and display it.
      */
-    void SetStatusText( const wxString& aText );
+    void SetStatusText( const wxString& aText ) override;
 
     /**
      * Set the currently displayed symbol.
      */
-    void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock );
+    void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) override;
 
 protected:
     void onSize( wxSizeEvent& aEvent );
@@ -78,4 +77,4 @@ protected:
 };
 
 
-#endif // DESIGN_BLOCK_PREVIEW_WIDGET_H
+#endif
diff --git a/include/advanced_config.h b/include/advanced_config.h
index 0e0fec8ea8..3e0f55d6a4 100644
--- a/include/advanced_config.h
+++ b/include/advanced_config.h
@@ -471,13 +471,13 @@ public:
     int m_DisambiguationMenuDelay;
 
     /**
-     * Enable the new Design Blocks feature
+     * Enable the new PCB Design Blocks feature
      *
-     * Setting name: "EnableDesignBlocks"
+     * Setting name: "EnablePcbDesignBlocks"
      * Valid values: true or false
      * Default value: false
      */
-    bool m_EnableDesignBlocks;
+    bool m_EnablePcbDesignBlocks;
 
     /**
      * Enable support for generators.
diff --git a/include/design_block_lib_table.h b/include/design_block_lib_table.h
index dbbfbbfbc7..0c42bd92fe 100644
--- a/include/design_block_lib_table.h
+++ b/include/design_block_lib_table.h
@@ -79,6 +79,14 @@ public:
 
     DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T GetFileType() { return type; }
 
+    /**
+     * Attempt to reload the library.
+     *
+     * @return true if a reload was required
+     * @throw IO_ERROR if the reload was unsuccessful.
+     */
+    bool Refresh() override;
+
 protected:
     DESIGN_BLOCK_LIB_TABLE_ROW( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) :
             LIB_TABLE_ROW( aRow ),
diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h
index 390072f590..b127b5198f 100644
--- a/include/settings/app_settings.h
+++ b/include/settings/app_settings.h
@@ -135,6 +135,21 @@ public:
         std::vector<wxString>   open_libs;     ///< list of libraries the user has open in the tree.
     };
 
+    struct PANEL_DESIGN_BLOCK_CHOOSER
+    {
+        int  sash_pos_h;
+        int  sash_pos_v;
+        int  width;
+        int  height;
+        int  sort_mode;
+        bool repeated_placement;
+        bool place_as_sheet;
+        bool keep_annotations;
+
+        // For saving tree columns and widths
+        LIB_TREE tree;
+    };
+
     struct PRINTING
     {
         bool             background;    ///< Whether or not to print background color.
@@ -179,6 +194,8 @@ public:
 
     FIND_REPLACE m_FindReplace;
 
+    PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel;
+
     GRAPHICS m_Graphics;
 
     COLOR_PICKER m_ColorPicker;
diff --git a/kicad/menubar.cpp b/kicad/menubar.cpp
index be9eae9ba0..69df89106b 100644
--- a/kicad/menubar.cpp
+++ b/kicad/menubar.cpp
@@ -208,8 +208,7 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar()
     prefsMenu->Add( ACTIONS::configurePaths );
     prefsMenu->Add( ACTIONS::showSymbolLibTable );
     prefsMenu->Add( ACTIONS::showFootprintLibTable );
-    if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks )
-        prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
+    prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
     prefsMenu->Add( ACTIONS::openPreferences );
 
     prefsMenu->AppendSeparator();
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index fd553b4bf8..40e6cf26a6 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -356,6 +356,7 @@ set( PCBNEW_CLASS_SRCS
     menubar_footprint_editor.cpp
     menubar_pcb_editor.cpp
     pcb_base_edit_frame.cpp
+    pcb_design_block_utils.cpp
     pcb_layer_box_selector.cpp
     pcb_edit_frame.cpp
     pcb_plotter.cpp
@@ -399,6 +400,7 @@ set( PCBNEW_CLASS_SRCS
     tools/item_modification_routine.cpp
     tools/pad_tool.cpp
     tools/pcb_control.cpp
+    tools/pcb_design_block_control.cpp
     tools/pcb_picker_tool.cpp
     tools/pcb_selection.cpp
     tools/pcb_selection_conditions.cpp
@@ -423,6 +425,8 @@ set( PCBNEW_CLASS_SRCS
     widgets/panel_footprint_chooser.cpp
     widgets/panel_selection_filter.cpp
     widgets/panel_selection_filter_base.cpp
+    widgets/pcb_design_block_pane.cpp
+    widgets/pcb_design_block_preview_widget.cpp
     widgets/pcb_properties_panel.cpp
     widgets/pcb_search_pane.cpp
     widgets/search_handlers.cpp
diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp
index 9a92f535a7..6955b87c9a 100644
--- a/pcbnew/board.cpp
+++ b/pcbnew/board.cpp
@@ -944,6 +944,12 @@ BOARD_DESIGN_SETTINGS& BOARD::GetDesignSettings() const
 }
 
 
+void BOARD::SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings )
+{
+    *m_designSettings = aSettings;
+}
+
+
 int BOARD::GetMaxClearanceValue() const
 {
     if( !m_maxClearanceValue.has_value() )
diff --git a/pcbnew/board.h b/pcbnew/board.h
index b910fa5326..2385eb3045 100644
--- a/pcbnew/board.h
+++ b/pcbnew/board.h
@@ -708,6 +708,7 @@ public:
      * @return the BOARD_DESIGN_SETTINGS for this BOARD
      */
     BOARD_DESIGN_SETTINGS& GetDesignSettings() const;
+    void                   SetDesignSettings( const BOARD_DESIGN_SETTINGS& aSettings );
 
     BOARD_STACKUP GetStackupOrDefault() const;
 
diff --git a/pcbnew/cross-probing.cpp b/pcbnew/cross-probing.cpp
index f31f1b881c..b94ea40146 100644
--- a/pcbnew/cross-probing.cpp
+++ b/pcbnew/cross-probing.cpp
@@ -54,6 +54,7 @@
 #include <tools/pcb_actions.h>
 #include <tools/pcb_selection_tool.h>
 #include <netlist_reader/netlist_reader.h>
+#include <widgets/pcb_design_block_pane.h>
 #include <wx/log.h>
 
 /* Execute a remote command sent via a socket on port KICAD_PCB_PORT_SERVICE_NUMBER
@@ -718,6 +719,10 @@ void PCB_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
         GetToolManager()->RunAction( ACTIONS::pluginsReload );
         break;
 
+    case MAIL_RELOAD_LIB:
+        m_designBlocksPane->RefreshLibs();
+        break;
+
     // many many others.
     default:
         ;
diff --git a/pcbnew/files.cpp b/pcbnew/files.cpp
index bdab0e9bdc..717c5611be 100644
--- a/pcbnew/files.cpp
+++ b/pcbnew/files.cpp
@@ -441,7 +441,7 @@ bool PCB_EDIT_FRAME::Files_io_from_id( int id )
         {
             if( id == ID_COPY_BOARD_AS )
             {
-                success = SavePcbCopy( filename, createProject );
+                success = SavePcbCopy( EnsureFileExtension( filename, FILEEXT::KiCadPcbFileExtension ), createProject );
             }
             else
             {
@@ -1135,7 +1135,7 @@ bool PCB_EDIT_FRAME::SavePcbFile( const wxString& aFileName, bool addToHistory,
 
 bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName, bool aCreateProject, bool aHeadless )
 {
-    wxFileName pcbFileName( EnsureFileExtension( aFileName, FILEEXT::KiCadPcbFileExtension ) );
+    wxFileName pcbFileName( aFileName );
 
     if( !IsWritable( pcbFileName ) )
     {
@@ -1194,12 +1194,6 @@ bool PCB_EDIT_FRAME::SavePcbCopy( const wxString& aFileName, bool aCreateProject
                                               rulesFile.GetFullPath() ) );
     }
 
-    if( !aHeadless )
-    {
-        DisplayInfoMessage( this, wxString::Format( _( "Board copied to:\n%s" ),
-                                                    pcbFileName.GetFullPath() ) );
-    }
-
     return true;
 }
 
diff --git a/pcbnew/menubar_pcb_editor.cpp b/pcbnew/menubar_pcb_editor.cpp
index f932116509..d192f55b08 100644
--- a/pcbnew/menubar_pcb_editor.cpp
+++ b/pcbnew/menubar_pcb_editor.cpp
@@ -234,6 +234,10 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
     showHidePanels->Add( PCB_ACTIONS::showSearch,                 ACTION_MENU::CHECK );
     showHidePanels->Add( PCB_ACTIONS::showLayersManager,          ACTION_MENU::CHECK );
     showHidePanels->Add( PCB_ACTIONS::showNetInspector,           ACTION_MENU::CHECK );
+
+    if( ADVANCED_CFG::GetCfg().m_EnablePcbDesignBlocks )
+        showHidePanels->Add( PCB_ACTIONS::showDesignBlockPanel, ACTION_MENU::CHECK, _( "Design Blocks" ) );
+
     viewMenu->Add( showHidePanels );
 
     viewMenu->AppendSeparator();
@@ -465,6 +469,8 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
 
     prefsMenu->Add( ACTIONS::configurePaths );
     prefsMenu->Add( ACTIONS::showFootprintLibTable );
+    if( ADVANCED_CFG::GetCfg().m_EnablePcbDesignBlocks )
+        prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
     prefsMenu->Add( ACTIONS::openPreferences );
 
     prefsMenu->AppendSeparator();
diff --git a/pcbnew/pcb_design_block_utils.cpp b/pcbnew/pcb_design_block_utils.cpp
new file mode 100644
index 0000000000..8499cf8740
--- /dev/null
+++ b/pcbnew/pcb_design_block_utils.cpp
@@ -0,0 +1,422 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+#include <pgm_base.h>
+#include <kiway.h>
+#include <board_commit.h>
+#include <design_block.h>
+#include <design_block_lib_table.h>
+#include <widgets/pcb_design_block_pane.h>
+#include <pcb_edit_frame.h>
+#include <pcb_io/pcb_io.h>
+#include <pcb_io/pcb_io_mgr.h>
+#include <wx/choicdlg.h>
+#include <wx/msgdlg.h>
+#include <wx/textdlg.h>
+#include <wildcards_and_files_ext.h>
+#include <paths.h>
+#include <env_paths.h>
+#include <common.h>
+#include <confirm.h>
+#include <kidialog.h>
+#include <locale_io.h>
+#include <netinfo.h>
+#include <tool/tool_manager.h>
+#include <tools/pcb_selection_tool.h>
+#include <dialogs/dialog_design_block_properties.h>
+#include <nlohmann/json.hpp>
+
+bool checkOverwriteDb( wxWindow* aFrame, wxString& libname, wxString& newName )
+{
+    wxString msg = wxString::Format( _( "Design block '%s' already exists in library '%s'." ), newName.GetData(),
+                                     libname.GetData() );
+
+    if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing design block?" ), _( "Overwrite" ) )
+        != wxID_OK )
+    {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool checkOverwriteDbLayout( wxWindow* aFrame, const LIB_ID& aLibId )
+{
+    wxString msg = wxString::Format( _( "Design block '%s' already has a layout." ), aLibId.GetUniStringLibItemName() );
+
+    if( OKOrCancelDialog( aFrame, _( "Confirmation" ), msg, _( "Overwrite existing layout?" ), _( "Overwrite" ) )
+        != wxID_OK )
+    {
+        return false;
+    }
+
+    return true;
+}
+
+
+bool PCB_EDIT_FRAME::saveBoardAsFile( BOARD* aBoard, const wxString& aFileName, bool aHeadless )
+{
+    // Ensure the "C" locale is temporary set, before saving any file
+    // It also avoid wxWidget alerts about locale issues, later, when using Python 3
+    LOCALE_IO dummy;
+
+    wxFileName pcbFileName( aFileName );
+
+    if( !IsWritable( pcbFileName ) )
+    {
+        if( !aHeadless )
+        {
+            DisplayError( this, wxString::Format( _( "Insufficient permissions to write file '%s'." ),
+                                                  pcbFileName.GetFullPath() ) );
+        }
+        return false;
+    }
+
+    try
+    {
+        IO_RELEASER<PCB_IO> pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) );
+
+        wxASSERT( pcbFileName.IsAbsolute() );
+
+        pi->SaveBoard( pcbFileName.GetFullPath(), aBoard, nullptr );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        if( !aHeadless )
+        {
+            DisplayError( this, wxString::Format( _( "Error saving board file '%s'.\n%s" ), pcbFileName.GetFullPath(),
+                                                  ioe.What() ) );
+        }
+
+        return false;
+    }
+
+    return true;
+}
+
+
+bool PCB_EDIT_FRAME::SaveBoardAsDesignBlock( const wxString& aLibraryName )
+{
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK blk;
+    wxFileName   fn = wxFileNameFromPath( GetBoard()->GetFileName() );
+
+    blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
+
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    wxString libName = blk.GetLibId().GetLibNickname();
+    wxString newName = blk.GetLibId().GetLibItemName();
+
+    if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
+    {
+        return false;
+    }
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+
+    if( !SavePcbCopy( tempFile, false, false ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk.SetBoardFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk.GetLibId() );
+
+    return success;
+}
+
+
+bool PCB_EDIT_FRAME::SaveBoardToDesignBlock( const LIB_ID& aLibId )
+{
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK* blk = nullptr;
+
+    try
+    {
+        blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+        return false;
+    }
+
+    if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
+    {
+        return false;
+    }
+
+    // Save a temporary copy of the schematic file, as the plugin is just going to move it
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+
+    if( !SavePcbCopy( tempFile, false, false ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk->SetBoardFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk )
+                  == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk->GetLibId() );
+
+    return success;
+}
+
+
+bool PCB_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
+{
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    // Get all selected items
+    PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
+
+    if( selection.Empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK blk;
+    wxFileName   fn = wxFileNameFromPath( GetBoard()->GetFileName() );
+
+    blk.SetLibId( LIB_ID( aLibraryName, fn.GetName() ) );
+
+    DIALOG_DESIGN_BLOCK_PROPERTIES dlg( this, &blk );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return false;
+
+    wxString libName = blk.GetLibId().GetLibNickname();
+    wxString newName = blk.GetLibId().GetLibItemName();
+
+    if( Prj().DesignBlockLibs()->DesignBlockExists( libName, newName ) && !checkOverwriteDb( this, libName, newName ) )
+    {
+        return false;
+    }
+
+    // Create a temporary board
+    BOARD* tempBoard = new BOARD();
+    tempBoard->SetDesignSettings( GetBoard()->GetDesignSettings() );
+    tempBoard->SetProject( &Prj(), true );
+    tempBoard->SynchronizeProperties();
+
+    // Copy all net info into the new board
+    for( NETINFO_ITEM* netInfo : GetBoard()->GetNetInfo() )
+    {
+        EDA_ITEM* copy = netInfo->Clone();
+        tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false );
+    }
+
+    // Copy the selected items to the temporary board
+    for( EDA_ITEM* item : selection )
+    {
+        EDA_ITEM* copy = item->Clone();
+        tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false );
+    }
+
+    // Rebuild connectivity, remove any unused nets
+    tempBoard->BuildListOfNets();
+    tempBoard->BuildConnectivity();
+    tempBoard->RemoveUnusedNets( nullptr );
+
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+
+    if( !saveBoardAsFile( tempBoard, tempFile, false ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk.SetBoardFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk ) == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk.GetLibId() );
+
+    return success;
+}
+
+
+bool PCB_EDIT_FRAME::SaveSelectionToDesignBlock( const LIB_ID& aLibId )
+{
+    // Make sure the user has selected a library to save into
+    if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select a library to save the design block to." ) );
+        return false;
+    }
+
+    // Get all selected items
+    PCB_SELECTION selection = m_toolManager->GetTool<PCB_SELECTION_TOOL>()->GetSelection();
+
+    if( selection.Empty() )
+    {
+        DisplayErrorMessage( this, _( "Please select some items to save as a design block." ) );
+        return false;
+    }
+
+    DESIGN_BLOCK* blk = nullptr;
+
+    try
+    {
+        blk = Prj().DesignBlockLibs()->DesignBlockLoad( aLibId.GetLibNickname(), aLibId.GetLibItemName() );
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+        return false;
+    }
+
+    if( !blk->GetBoardFile().IsEmpty() && !checkOverwriteDbLayout( this, aLibId ) )
+    {
+        return false;
+    }
+
+    // Create a temporary board
+    BOARD* tempBoard = new BOARD();
+    tempBoard->SetDesignSettings( GetBoard()->GetDesignSettings() );
+    tempBoard->SetProject( &Prj(), true );
+    tempBoard->SynchronizeProperties();
+
+    // Copy all net info into the new board
+    for( NETINFO_ITEM* netInfo : GetBoard()->GetNetInfo() )
+    {
+        EDA_ITEM* copy = netInfo->Clone();
+        tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false );
+    }
+
+    // Copy the selected items to the temporary board
+    for( EDA_ITEM* item : selection )
+    {
+        EDA_ITEM* copy = item->Clone();
+        tempBoard->Add( static_cast<BOARD_ITEM*>( copy ), ADD_MODE::APPEND, false );
+    }
+
+    // Rebuild connectivity, remove any unused nets
+    tempBoard->BuildListOfNets();
+    tempBoard->BuildConnectivity();
+    tempBoard->RemoveUnusedNets( nullptr );
+
+    wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
+
+    if( !saveBoardAsFile( tempBoard, tempFile, false ) )
+    {
+        DisplayErrorMessage( this, _( "Error saving temporary board file to create design block." ) );
+        wxRemoveFile( tempFile );
+        return false;
+    }
+
+    blk->SetBoardFile( tempFile );
+
+    bool success = false;
+
+    try
+    {
+        success = Prj().DesignBlockLibs()->DesignBlockSave( aLibId.GetLibNickname(), blk )
+                  == DESIGN_BLOCK_LIB_TABLE::SAVE_OK;
+    }
+    catch( const IO_ERROR& ioe )
+    {
+        DisplayError( this, ioe.What() );
+    }
+
+    // Clean up the temporary file
+    wxRemoveFile( tempFile );
+
+    m_designBlocksPane->RefreshLibs();
+    m_designBlocksPane->SelectLibId( blk->GetLibId() );
+
+    return success;
+}
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index e7603021ab..bf69df5243 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -81,6 +81,7 @@
 #include <tools/convert_tool.h>
 #include <tools/drawing_tool.h>
 #include <tools/pcb_control.h>
+#include <tools/pcb_design_block_control.h>
 #include <tools/board_editor_control.h>
 #include <tools/board_inspection_tool.h>
 #include <tools/pcb_editor_conditions.h>
@@ -104,6 +105,7 @@
 #include <dialog_drc.h>     // for DIALOG_DRC_WINDOW_NAME definition
 #include <ratsnest/ratsnest_view_item.h>
 #include <widgets/appearance_controls.h>
+#include <widgets/pcb_design_block_pane.h>
 #include <widgets/pcb_search_pane.h>
 #include <widgets/wx_infobar.h>
 #include <widgets/panel_selection_filter.h>
@@ -205,6 +207,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     m_inspectConstraintsDlg( nullptr ),
     m_footprintDiffDlg( nullptr ),
     m_boardSetupDlg( nullptr ),
+    m_designBlocksPane( nullptr ),
     m_importProperties( nullptr ),
     m_eventCounterTimer( nullptr )
 {
@@ -292,6 +295,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     m_appearancePanel = new APPEARANCE_CONTROLS( this, GetCanvas() );
     m_searchPane = new PCB_SEARCH_PANE( this );
     m_netInspectorPanel = new PCB_NET_INSPECTOR_PANEL( this, this );
+    m_designBlocksPane = new PCB_DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList );
 
     m_auimgr.SetManagedWindow( this );
 
@@ -341,6 +345,20 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
                       .FloatingSize( m_selectionFilterPanel->GetBestSize() )
                       .CloseButton( false ) );
 
+    m_auimgr.AddPane( m_designBlocksPane, EDA_PANE().Name( DesignBlocksPaneName() )
+                        .Right().Layer( 5 )
+                        .Caption( _( "Design Blocks" ) )
+                        .CaptionVisible( true )
+                        .PaneBorder( true )
+                        .TopDockable( false )
+                        .BottomDockable( false )
+                        .CloseButton( true )
+                        .MinSize( FromDIP( wxSize( 240, 60 ) ) )
+                        .BestSize( FromDIP( wxSize( 300, 200 ) ) )
+                        .FloatingSize( FromDIP( wxSize( 800, 600 ) ) )
+                        .FloatingPosition( FromDIP( wxPoint( 50, 200 ) ) )
+                        .Show( true ) );
+
     m_auimgr.AddPane( m_propertiesPanel, EDA_PANE().Name( PropertiesPaneName() )
                       .Left().Layer( 5 )
                       .Caption( _( "Properties" ) ).PaneBorder( false )
@@ -378,6 +396,7 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     m_auimgr.GetPane( PropertiesPaneName() ).Show( GetPcbNewSettings()->m_AuiPanels.show_properties );
     m_auimgr.GetPane( NetInspectorPanelName() ).Show( m_show_net_inspector );
     m_auimgr.GetPane( SearchPaneName() ).Show( m_show_search );
+    m_auimgr.GetPane( DesignBlocksPaneName() ).Show( GetPcbNewSettings()->m_AuiPanels.design_blocks_show );
 
     // The selection filter doesn't need to grow in the vertical direction when docked
     m_auimgr.GetPane( "SelectionFilter" ).dock_proportion = 0;
@@ -388,6 +407,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
     {
         wxAuiPaneInfo& layersManager = m_auimgr.GetPane( wxS( "LayersManager" ) );
         SetAuiPaneSize( m_auimgr, layersManager, aui_cfg.right_panel_width, -1 );
+
+        wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() );
+        SetAuiPaneSize( m_auimgr, designBlocksPane, aui_cfg.design_blocks_panel_docked_width, -1 );
     }
 
     if( aui_cfg.properties_panel_width > 0 && m_propertiesPanel )
@@ -741,6 +763,7 @@ void PCB_EDIT_FRAME::setupTools()
     m_toolManager->RegisterTool( new DRAWING_TOOL );
     m_toolManager->RegisterTool( new PCB_POINT_EDITOR );
     m_toolManager->RegisterTool( new PCB_CONTROL );
+    m_toolManager->RegisterTool( new PCB_DESIGN_BLOCK_CONTROL );
     m_toolManager->RegisterTool( new BOARD_EDITOR_CONTROL );
     m_toolManager->RegisterTool( new BOARD_INSPECTION_TOOL );
     m_toolManager->RegisterTool( new BOARD_REANNOTATE_TOOL );
@@ -860,6 +883,13 @@ void PCB_EDIT_FRAME::setupUIConditions()
 
     mgr->SetConditions( ACTIONS::toggleBoundingBoxes, CHECK( cond.BoundingBoxes() ) );
 
+    auto hasElements =
+            [ this ] ( const SELECTION& aSel )
+            {
+                return GetBoard() &&
+                        ( !GetBoard()->IsEmpty() || !SELECTION_CONDITIONS::Idle( aSel ) );
+            };
+
     auto constrainedDrawingModeCond =
             [this]( const SELECTION& )
             {
@@ -896,6 +926,12 @@ void PCB_EDIT_FRAME::setupUIConditions()
                 return m_auimgr.GetPane( SearchPaneName() ).IsShown();
             };
 
+    auto designBlockCond =
+            [ this ] (const SELECTION& aSel )
+            {
+                return m_auimgr.GetPane( DesignBlocksPaneName() ).IsShown();
+            };
+
     auto highContrastCond =
             [this] ( const SELECTION& )
             {
@@ -939,6 +975,10 @@ void PCB_EDIT_FRAME::setupUIConditions()
     mgr->SetConditions( ACTIONS::showProperties,           CHECK( propertiesCond ) );
     mgr->SetConditions( PCB_ACTIONS::showNetInspector,     CHECK( netInspectorCond ) );
     mgr->SetConditions( PCB_ACTIONS::showSearch,           CHECK( searchPaneCond ) );
+    mgr->SetConditions( PCB_ACTIONS::showDesignBlockPanel, CHECK( designBlockCond ) );
+
+    mgr->SetConditions( PCB_ACTIONS::saveBoardAsDesignBlock,     ENABLE( hasElements ) );
+    mgr->SetConditions( PCB_ACTIONS::saveSelectionAsDesignBlock, ENABLE( SELECTION_CONDITIONS::NotEmpty ) );
 
     auto isArcKeepCenterMode =
             [this]( const SELECTION& )
@@ -1057,6 +1097,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
     CURRENT_EDIT_TOOL( ACTIONS::embeddedFiles );
     CURRENT_EDIT_TOOL( ACTIONS::deleteTool );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::placeFootprint );
+    CURRENT_EDIT_TOOL( PCB_ACTIONS::placeDesignBlock );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::routeSingleTrack);
     CURRENT_EDIT_TOOL( PCB_ACTIONS::routeDiffPair );
     CURRENT_EDIT_TOOL( PCB_ACTIONS::tuneSingleTrack);
@@ -1499,6 +1540,19 @@ void PCB_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
             cfg->m_AuiPanels.appearance_expand_layer_display = m_appearancePanel->IsLayerOptionsExpanded();
             cfg->m_AuiPanels.appearance_expand_net_display   = m_appearancePanel->IsNetOptionsExpanded();
         }
+
+        wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() );
+        cfg->m_AuiPanels.design_blocks_show = designBlocksPane.IsShown();
+
+        if( designBlocksPane.IsDocked() )
+            cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x;
+        else
+        {
+            cfg->m_AuiPanels.design_blocks_panel_float_height = designBlocksPane.floating_size.y;
+            cfg->m_AuiPanels.design_blocks_panel_float_width = designBlocksPane.floating_size.x;
+        }
+
+        m_designBlocksPane->SaveSettings();
     }
 }
 
diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h
index d3d1b1073b..36c31daf06 100644
--- a/pcbnew/pcb_edit_frame.h
+++ b/pcbnew/pcb_edit_frame.h
@@ -58,6 +58,7 @@ class BOARD_NETLIST_UPDATER;
 class ACTION_MENU;
 class TOOL_ACTION;
 class DIALOG_BOARD_SETUP;
+class PCB_DESIGN_BLOCK_PANE;
 
 #ifdef KICAD_IPC_API
 class KICAD_API_SERVER;
@@ -318,6 +319,8 @@ public:
     bool IsSearchPaneShown() { return m_auimgr.GetPane( SearchPaneName() ).IsShown(); }
     void FocusSearch();
 
+    void ToggleLibraryTree() override;
+
     /**
      * Create an ASCII footprint position file.
      *
@@ -465,6 +468,16 @@ public:
      */
     void RecreateCmpFileFromBoard( wxCommandEvent& aEvent );
 
+    bool SaveBoardAsDesignBlock( const wxString& aLibraryName );
+
+    bool SaveSelectionAsDesignBlock( const wxString& aLibraryName );
+
+    bool SaveBoardToDesignBlock( const LIB_ID& aLibId );
+
+    bool SaveSelectionToDesignBlock( const LIB_ID& aLibId );
+
+    PCB_DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; }
+
     /**
      * Save footprints in a library:
      *
@@ -829,6 +842,18 @@ protected:
     bool importFile( const wxString& aFileName, int aFileType,
                      const std::map<std::string, UTF8>* aProperties = nullptr );
 
+    /**
+     * @brief Save a board object to a file
+     *
+     * @param aBoard The board object to save
+     * @param aFileName The file name to save the board to
+     * @param aHeadless If true, suppresses informational output (e.g. to be used from the API)
+     *
+     * @return
+     */
+    bool saveBoardAsFile( BOARD* aBoard, const wxString& aFileName, bool aHeadless = false );
+
+
     bool canCloseWindow( wxCloseEvent& aCloseEvent ) override;
     void doCloseWindow() override;
 
@@ -881,6 +906,9 @@ private:
     DIALOG_BOOK_REPORTER* m_footprintDiffDlg;
     DIALOG_BOARD_SETUP*   m_boardSetupDlg;
 
+    std::vector<LIB_ID>    m_designBlockHistoryList;
+    PCB_DESIGN_BLOCK_PANE* m_designBlocksPane;
+
     const std::map<std::string, UTF8>* m_importProperties; // Properties used for non-KiCad import.
 
     /**
diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp
index 3db86a2175..19ac27c08e 100644
--- a/pcbnew/pcbnew_settings.cpp
+++ b/pcbnew/pcbnew_settings.cpp
@@ -132,6 +132,17 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
     m_params.emplace_back( new PARAM<bool>( "aui.show_net_inspector",
             &m_AuiPanels.show_net_inspector, false ) );
 
+    m_params.emplace_back( new PARAM<bool>( "aui.design_blocks_show", &m_AuiPanels.design_blocks_show, false ) );
+
+    m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_docked_width",
+                                           &m_AuiPanels.design_blocks_panel_docked_width, -1 ) );
+
+    m_params.emplace_back(
+            new PARAM<int>( "aui.design_blocks_panel_float_width", &m_AuiPanels.design_blocks_panel_float_width, -1 ) );
+
+    m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_float_height",
+                                           &m_AuiPanels.design_blocks_panel_float_height, -1 ) );
+
     m_params.emplace_back( new PARAM<int>( "footprint_chooser.width",
             &m_FootprintChooser.width, -1 ) );
 
diff --git a/pcbnew/pcbnew_settings.h b/pcbnew/pcbnew_settings.h
index 102f4064b6..29d4325f89 100644
--- a/pcbnew/pcbnew_settings.h
+++ b/pcbnew/pcbnew_settings.h
@@ -155,6 +155,10 @@ public:
         bool  show_properties;
         bool  show_search;
         bool  show_net_inspector;
+        bool  design_blocks_show;
+        int   design_blocks_panel_docked_width;
+        int   design_blocks_panel_float_width;
+        int   design_blocks_panel_float_height;
     };
 
     struct DIALOG_CLEANUP
diff --git a/pcbnew/toolbars_pcb_editor.cpp b/pcbnew/toolbars_pcb_editor.cpp
index d6b143861c..65e79ef0bd 100644
--- a/pcbnew/toolbars_pcb_editor.cpp
+++ b/pcbnew/toolbars_pcb_editor.cpp
@@ -50,6 +50,7 @@
 #include <tools/pcb_actions.h>
 #include <tools/pcb_selection_tool.h>
 #include <widgets/appearance_controls.h>
+#include <widgets/pcb_design_block_pane.h>
 #include <widgets/layer_box_selector.h>
 #include <widgets/layer_presentation.h>
 #include <widgets/pcb_properties_panel.h>
@@ -701,3 +702,44 @@ void PCB_EDIT_FRAME::OnUpdateSelectViaSize( wxUpdateUIEvent& aEvent )
             m_SelViaSizeBox->SetSelection( sel );
     }
 }
+
+
+void PCB_EDIT_FRAME::ToggleLibraryTree()
+{
+    PCBNEW_SETTINGS* cfg = GetPcbNewSettings();
+
+    wxCHECK( cfg, /* void */ );
+
+    wxAuiPaneInfo& db_library_pane = m_auimgr.GetPane( DesignBlocksPaneName() );
+
+    db_library_pane.Show( !db_library_pane.IsShown() );
+
+    if( db_library_pane.IsShown() )
+    {
+        if( db_library_pane.IsFloating() )
+        {
+            db_library_pane.FloatingSize( cfg->m_AuiPanels.design_blocks_panel_float_width,
+                                          cfg->m_AuiPanels.design_blocks_panel_float_height );
+            m_auimgr.Update();
+        }
+        else if( cfg->m_AuiPanels.design_blocks_panel_docked_width > 0 )
+        {
+            // SetAuiPaneSize also updates m_auimgr
+            SetAuiPaneSize( m_auimgr, db_library_pane, cfg->m_AuiPanels.design_blocks_panel_docked_width, -1 );
+        }
+    }
+    else
+    {
+        if( db_library_pane.IsFloating() )
+        {
+            cfg->m_AuiPanels.design_blocks_panel_float_width = db_library_pane.floating_size.x;
+            cfg->m_AuiPanels.design_blocks_panel_float_height = db_library_pane.floating_size.y;
+        }
+        else
+        {
+            cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x;
+        }
+
+        m_auimgr.Update();
+    }
+}
diff --git a/pcbnew/tools/board_editor_control.cpp b/pcbnew/tools/board_editor_control.cpp
index 201039edd3..03047911fa 100644
--- a/pcbnew/tools/board_editor_control.cpp
+++ b/pcbnew/tools/board_editor_control.cpp
@@ -709,6 +709,13 @@ int BOARD_EDITOR_CONTROL::ToggleNetInspector( const TOOL_EVENT& aEvent )
 }
 
 
+int BOARD_EDITOR_CONTROL::ToggleLibraryTree( const TOOL_EVENT& aEvent )
+{
+    getEditFrame<PCB_EDIT_FRAME>()->ToggleLibraryTree();
+    return 0;
+}
+
+
 int BOARD_EDITOR_CONTROL::ToggleSearch( const TOOL_EVENT& aEvent )
 {
     getEditFrame<PCB_EDIT_FRAME>()->ToggleSearch();
@@ -1771,6 +1778,7 @@ void BOARD_EDITOR_CONTROL::setTransitions()
     Go( &BOARD_EDITOR_CONTROL::ToggleLayersManager,    PCB_ACTIONS::showLayersManager.MakeEvent() );
     Go( &BOARD_EDITOR_CONTROL::ToggleProperties,       ACTIONS::showProperties.MakeEvent() );
     Go( &BOARD_EDITOR_CONTROL::ToggleNetInspector,     PCB_ACTIONS::showNetInspector.MakeEvent() );
+    Go( &BOARD_EDITOR_CONTROL::ToggleLibraryTree,      PCB_ACTIONS::showDesignBlockPanel.MakeEvent() );
     Go( &BOARD_EDITOR_CONTROL::ToggleSearch,           PCB_ACTIONS::showSearch.MakeEvent() );
     Go( &BOARD_EDITOR_CONTROL::TogglePythonConsole,    PCB_ACTIONS::showPythonConsole.MakeEvent() );
     Go( &BOARD_EDITOR_CONTROL::RepairBoard,            PCB_ACTIONS::repairBoard.MakeEvent() );
diff --git a/pcbnew/tools/board_editor_control.h b/pcbnew/tools/board_editor_control.h
index bc3e7d1e68..10c30ad940 100644
--- a/pcbnew/tools/board_editor_control.h
+++ b/pcbnew/tools/board_editor_control.h
@@ -82,6 +82,7 @@ public:
     int ToggleNetInspector( const TOOL_EVENT& aEvent );
     int ToggleSearch( const TOOL_EVENT& aEvent );
     int TogglePythonConsole( const TOOL_EVENT& aEvent );
+    int ToggleLibraryTree( const TOOL_EVENT& aEvent );
 
     // Track & via size control
     int TrackWidthInc( const TOOL_EVENT& aEvent );
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index e5e4cc052e..ff02f62c76 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -444,6 +444,65 @@ TOOL_ACTION PCB_ACTIONS::runDRC( TOOL_ACTION_ARGS()
         .Tooltip( _( "Show the design rules checker window" ) )
         .Icon( BITMAPS::erc ) );
 
+// PCB_DESIGN_BLOCK_CONTROL
+TOOL_ACTION PCB_ACTIONS::placeDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.InteractiveDrawing.placeDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .DefaultHotkey( MD_SHIFT + 'B' )
+        .FriendlyName( _( "Place Design Block" ) )
+        .Tooltip( _( "Add selected design block to current board" ) )
+        .Icon( BITMAPS::add_component )
+        .Flags( AF_ACTIVATE )
+        .Parameter<DESIGN_BLOCK*>( nullptr ) );
+
+TOOL_ACTION PCB_ACTIONS::showDesignBlockPanel( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.showDesignBlockPanel" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Design Blocks" ) )
+        .Tooltip( _( "Show/hide design blocks library" ) )
+        .Icon( BITMAPS::search_tree ) );
+
+TOOL_ACTION PCB_ACTIONS::saveBoardAsDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.saveBoardAsDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Current Board as Design Block..." ) )
+        .Tooltip( _( "Create a new design block from the current board" ) )
+        .Icon( BITMAPS::new_component ) );
+
+TOOL_ACTION PCB_ACTIONS::saveSelectionAsDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.saveSelectionAsDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Selection as Design Block..." ) )
+        .Tooltip( _( "Create a new design block from the current selection" ) )
+        .Icon( BITMAPS::new_component ) );
+
+TOOL_ACTION PCB_ACTIONS::saveBoardToDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.saveBoardToDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Current Board to Design Block..." ) )
+        .Tooltip( _( "Add current board to design block" ) )
+        .Icon( BITMAPS::save ) );
+
+TOOL_ACTION PCB_ACTIONS::saveSelectionToDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.saveSelectionToDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Save Selection to Design Block..." ) )
+        .Tooltip( _( "Add current selection to design block" ) )
+        .Icon( BITMAPS::save ) );
+
+TOOL_ACTION PCB_ACTIONS::deleteDesignBlock( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.saveDeleteDesignBlock" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Delete Design Block" ) )
+        .Tooltip( _( "Remove the selected design block from its library" ) )
+        .Icon( BITMAPS::trash ) );
+
+TOOL_ACTION PCB_ACTIONS::editDesignBlockProperties( TOOL_ACTION_ARGS()
+        .Name( "pcbnew.PcbDesignBlockControl.editDesignBlockProperties" )
+        .Scope( AS_GLOBAL )
+        .FriendlyName( _( "Properties..." ) )
+        .Tooltip( _( "Edit properies of design block" ) )
+        .Icon( BITMAPS::edit ) );
 
 // EDIT_TOOL
 //
diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
index 356c732108..44efb1ebdd 100644
--- a/pcbnew/tools/pcb_actions.h
+++ b/pcbnew/tools/pcb_actions.h
@@ -39,6 +39,7 @@ enum class ZONE_MODE
     GRAPHIC_POLYGON
 };
 
+class DESIGN_BLOCK;
 
 /**
  * Gather all the actions that are shared by tools.
@@ -459,6 +460,16 @@ public:
     static TOOL_ACTION showPythonConsole;
     static TOOL_ACTION zonesManager;
 
+    // Design Block management
+    static TOOL_ACTION placeDesignBlock;
+    static TOOL_ACTION showDesignBlockPanel;
+    static TOOL_ACTION saveBoardAsDesignBlock;
+    static TOOL_ACTION saveSelectionAsDesignBlock;
+    static TOOL_ACTION saveBoardToDesignBlock;
+    static TOOL_ACTION saveSelectionToDesignBlock;
+    static TOOL_ACTION deleteDesignBlock;
+    static TOOL_ACTION editDesignBlockProperties;
+
     // Footprint editor tools
 
     // We don't use ACTION::new here because we need to distinguish between New Library
diff --git a/pcbnew/tools/pcb_control.cpp b/pcbnew/tools/pcb_control.cpp
index 822848dc95..d97afa9861 100644
--- a/pcbnew/tools/pcb_control.cpp
+++ b/pcbnew/tools/pcb_control.cpp
@@ -40,6 +40,7 @@
 #include <board_design_settings.h>
 #include <board_item.h>
 #include <clipboard.h>
+#include <design_block.h>
 #include <dialogs/dialog_paste_special.h>
 #include <pcb_dimension.h>
 #include <gal/graphics_abstraction_layer.h>
@@ -69,6 +70,7 @@
 #include <footprint_editor_settings.h>
 #include <footprint_viewer_frame.h>
 #include <widgets/appearance_controls.h>
+#include <widgets/pcb_design_block_pane.h>
 #include <widgets/wx_progress_reporters.h>
 #include <widgets/wx_infobar.h>
 #include <wx/hyperlink.h>
@@ -1306,6 +1308,48 @@ int PCB_CONTROL::AppendBoardFromFile( const TOOL_EVENT& aEvent )
 }
 
 
+int PCB_CONTROL::AppendDesignBlock( const TOOL_EVENT& aEvent )
+{
+    PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
+
+    if( !editFrame )
+        return 1;
+
+    DESIGN_BLOCK* designBlock = nullptr;
+
+    if( !editFrame->GetDesignBlockPane()->GetSelectedLibId().IsValid() )
+        return 1;
+
+
+    designBlock = editFrame->GetDesignBlockPane()->GetDesignBlock( editFrame->GetDesignBlockPane()->GetSelectedLibId(),
+                                                                   true, true );
+
+    if( !designBlock || designBlock->GetBoardFile().IsEmpty() )
+        return 1;
+
+
+    PCB_IO_MGR::PCB_FILE_T pluginType = PCB_IO_MGR::KICAD_SEXP;
+    IO_RELEASER<PCB_IO>    pi( PCB_IO_MGR::PluginFind( pluginType ) );
+
+    if( !pi )
+        return 1;
+
+    bool repeatPlacement = false;
+
+    if( APP_SETTINGS_BASE* cfg = editFrame->config() )
+        repeatPlacement = cfg->m_DesignBlockChooserPanel.repeated_placement;
+
+    int ret = 0;
+
+    do
+    {
+        ret = AppendBoard( *pi, designBlock->GetBoardFile() );
+    } while( repeatPlacement && ret == 0 );
+
+    return ret;
+}
+
+
 template<typename T>
 static void moveUnflaggedItems( const std::deque<T>& aList, std::vector<BOARD_ITEM*>& aTarget,
                                 bool aIsNew )
@@ -1477,7 +1521,7 @@ bool PCB_CONTROL::placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM
 }
 
 
-int PCB_CONTROL::AppendBoard( PCB_IO& pi, wxString& fileName )
+int PCB_CONTROL::AppendBoard( PCB_IO& pi, const wxString& fileName )
 {
     PCB_EDIT_FRAME* editFrame = dynamic_cast<PCB_EDIT_FRAME*>( m_frame );
 
@@ -1585,15 +1629,64 @@ int PCB_CONTROL::AppendBoard( PCB_IO& pi, wxString& fileName )
     brd->SetEnabledLayers( enabledLayers );
     brd->SetVisibleLayers( enabledLayers );
 
+    int ret = 0;
+
+    bool placeAsGroup = editFrame->config() ? editFrame->config()->m_DesignBlockChooserPanel.place_as_sheet : false;
+
     if( placeBoardItems( &commit, brd, false, false /* Don't reannotate dupes on Append Board */ ) )
+    {
         commit.Push( _( "Append Board" ) );
+
+        if( placeAsGroup )
+        {
+            BOARD_COMMIT grpCommit( m_toolMgr );
+            PCB_GROUP*   group = new PCB_GROUP( brd );
+
+            // Get the selection tool selection
+            PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
+            PCB_SELECTION       selection = selTool->GetSelection();
+
+            for( EDA_ITEM* eda_item : selection )
+            {
+                if( eda_item->IsBOARD_ITEM() )
+                {
+                    if( static_cast<BOARD_ITEM*>( eda_item )->IsLocked() )
+                        group->SetLocked( true );
+                }
+            }
+
+            grpCommit.Add( group );
+
+            for( EDA_ITEM* eda_item : selection )
+            {
+                if( eda_item->IsBOARD_ITEM() && !static_cast<BOARD_ITEM*>( eda_item )->GetParentFootprint() )
+                {
+                    grpCommit.Stage( static_cast<BOARD_ITEM*>( eda_item ), CHT_GROUP );
+                }
+            }
+
+            grpCommit.Push( _( "Group Items" ), APPEND_UNDO );
+
+            selTool->ClearSelection();
+            selTool->select( group );
+
+            m_toolMgr->PostEvent( EVENTS::SelectedItemsModified );
+            m_frame->OnModify();
+            m_frame->Refresh();
+        }
+
+        ret = 0;
+    }
     else
+    {
         commit.Revert();
+        ret = 1;
+    }
 
     // Refresh the UI for the updated board properties
     editFrame->GetAppearancePanel()->OnBoardChanged();
 
-    return 0;
+    return ret;
 }
 
 
@@ -2093,6 +2186,7 @@ void PCB_CONTROL::setTransitions()
     Go( &PCB_CONTROL::InteractiveDelete,    ACTIONS::deleteTool.MakeEvent() );
 
     // Append control
+    Go( &PCB_CONTROL::AppendDesignBlock,    PCB_ACTIONS::placeDesignBlock.MakeEvent() );
     Go( &PCB_CONTROL::AppendBoardFromFile,  PCB_ACTIONS::appendBoard.MakeEvent() );
     Go( &PCB_CONTROL::DdAppendBoard,        PCB_ACTIONS::ddAppendBoard.MakeEvent() );
 
@@ -2110,4 +2204,4 @@ void PCB_CONTROL::setTransitions()
     Go( &PCB_CONTROL::DdAddLibrary,         ACTIONS::ddAddLibrary.MakeEvent() );
     Go( &PCB_CONTROL::DdImportFootprint,    PCB_ACTIONS::ddImportFootprint.MakeEvent() );
 }
-// clang-format on
\ No newline at end of file
+// clang-format on
diff --git a/pcbnew/tools/pcb_control.h b/pcbnew/tools/pcb_control.h
index 300a48533a..b0caf56ca3 100644
--- a/pcbnew/tools/pcb_control.h
+++ b/pcbnew/tools/pcb_control.h
@@ -106,7 +106,8 @@ public:
     int InteractiveDelete( const TOOL_EVENT& aEvent );
     int Paste( const TOOL_EVENT& aEvent );
     int AppendBoardFromFile( const TOOL_EVENT& aEvent );
-    int AppendBoard( PCB_IO& pi, wxString& fileName );
+    int AppendDesignBlock( const TOOL_EVENT& aEvent );
+    int AppendBoard( PCB_IO& pi, const wxString& fileName );
     int UpdateMessagePanel( const TOOL_EVENT& aEvent );
 
     int FlipPcbView( const TOOL_EVENT& aEvent );
@@ -142,7 +143,7 @@ private:
      * @param aAnchorAtOrigin = true if the items are translated so that the anchor is {0, 0}
      *                        (if false, the top-left item's origin will be used)
      * @param aReannotateDuplicates = true to reannotate any footprints with a designator
-                                      that already exist in the board.
+     *                                that already exist in the board.
      */
     bool placeBoardItems( BOARD_COMMIT* aCommit, std::vector<BOARD_ITEM*>& aItems, bool aIsNew,
                           bool aAnchorAtOrigin, bool aReannotateDuplicates );
diff --git a/pcbnew/tools/pcb_design_block_control.cpp b/pcbnew/tools/pcb_design_block_control.cpp
new file mode 100644
index 0000000000..aa34640cfd
--- /dev/null
+++ b/pcbnew/tools/pcb_design_block_control.cpp
@@ -0,0 +1,165 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+#include <dialog_design_block_properties.h>
+#include <pcb_edit_frame.h>
+#include <tool/tool_manager.h>
+#include <tools/pcb_actions.h>
+#include <tools/pcb_selection_tool.h>
+#include <tools/pcb_design_block_control.h>
+#include <widgets/pcb_design_block_pane.h>
+#include <widgets/panel_design_block_chooser.h>
+
+
+PCB_DESIGN_BLOCK_CONTROL::~PCB_DESIGN_BLOCK_CONTROL()
+{
+}
+
+
+bool PCB_DESIGN_BLOCK_CONTROL::Init()
+{
+    m_editFrame     = getEditFrame<PCB_EDIT_FRAME>();
+    m_frame         = m_editFrame;
+    m_framesToNotify = { FRAME_SCH };
+
+    auto isInLibrary =
+            [this](const SELECTION& aSel )
+            {
+                return this->selIsInLibrary(aSel);
+            };
+
+    auto isDesignBlock =
+            [this](const SELECTION& aSel )
+            {
+                return this->selIsDesignBlock(aSel);
+            };
+
+    auto hasSelection =
+            [this](const SELECTION& aSel )
+            {
+                return !m_editFrame->GetCurrentSelection().Empty();
+            };
+
+    CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
+    AddContextMenuItems( &ctxMenu );
+
+    ctxMenu.AddItem( PCB_ACTIONS::placeDesignBlock, isDesignBlock, 50 );
+    ctxMenu.AddSeparator( 50 );
+
+    ctxMenu.AddItem( PCB_ACTIONS::editDesignBlockProperties,  isDesignBlock, 100 );
+    ctxMenu.AddItem( PCB_ACTIONS::saveBoardAsDesignBlock,     isInLibrary, 100 );
+    ctxMenu.AddItem( PCB_ACTIONS::saveSelectionAsDesignBlock, isInLibrary && hasSelection, 100 );
+    ctxMenu.AddItem( PCB_ACTIONS::saveBoardToDesignBlock,     isDesignBlock, 100 );
+    ctxMenu.AddItem( PCB_ACTIONS::saveSelectionToDesignBlock, isDesignBlock && hasSelection, 100 );
+    ctxMenu.AddItem( PCB_ACTIONS::deleteDesignBlock,          isDesignBlock, 100 );
+    ctxMenu.AddSeparator( 100 );
+
+    return true;
+}
+
+
+int PCB_DESIGN_BLOCK_CONTROL::SaveBoardAsDesignBlock( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( !m_editFrame->SaveBoardAsDesignBlock( current->m_LibId.GetLibNickname() ) )
+        return -1;
+
+    notifyOtherFrames();
+
+    return 0;
+}
+
+
+int PCB_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( !m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() ) )
+        return -1;
+
+    notifyOtherFrames();
+
+    return 0;
+}
+
+
+int PCB_DESIGN_BLOCK_CONTROL::SaveBoardToDesignBlock( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( !m_editFrame->SaveBoardToDesignBlock( current->m_LibId ) )
+        return -1;
+
+    notifyOtherFrames();
+
+    return 0;
+}
+
+
+int PCB_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent )
+{
+    LIB_TREE_NODE* current = getCurrentTreeNode();
+
+    if( !current )
+        return -1;
+
+    if( !m_editFrame->SaveSelectionToDesignBlock( current->m_LibId ) )
+        return -1;
+
+    notifyOtherFrames();
+
+    return 0;
+}
+
+void PCB_DESIGN_BLOCK_CONTROL::setTransitions()
+{
+    Go( &PCB_DESIGN_BLOCK_CONTROL::SaveBoardAsDesignBlock,      PCB_ACTIONS::saveBoardAsDesignBlock.MakeEvent() );
+    Go( &PCB_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock,  PCB_ACTIONS::saveSelectionAsDesignBlock.MakeEvent() );
+    Go( &PCB_DESIGN_BLOCK_CONTROL::SaveBoardToDesignBlock,      PCB_ACTIONS::saveBoardToDesignBlock.MakeEvent() );
+    Go( &PCB_DESIGN_BLOCK_CONTROL::SaveSelectionToDesignBlock,  PCB_ACTIONS::saveSelectionToDesignBlock.MakeEvent() );
+    Go( &PCB_DESIGN_BLOCK_CONTROL::DeleteDesignBlock,           PCB_ACTIONS::deleteDesignBlock.MakeEvent() );
+    Go( &PCB_DESIGN_BLOCK_CONTROL::EditDesignBlockProperties,   PCB_ACTIONS::editDesignBlockProperties.MakeEvent() );
+}
+
+
+LIB_ID PCB_DESIGN_BLOCK_CONTROL::getSelectedLibId()
+{
+    getDesignBlockPane()->GetSelectedLibId();
+
+    return LIB_ID();
+}
+
+
+DESIGN_BLOCK_PANE* PCB_DESIGN_BLOCK_CONTROL::getDesignBlockPane()
+{
+    return m_editFrame->GetDesignBlockPane();
+}
diff --git a/pcbnew/tools/pcb_design_block_control.h b/pcbnew/tools/pcb_design_block_control.h
new file mode 100644
index 0000000000..69c9e14c1d
--- /dev/null
+++ b/pcbnew/tools/pcb_design_block_control.h
@@ -0,0 +1,62 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+
+#ifndef PCB_DESIGN_BLOCK_CONTROL_H
+#define PCB_DESIGN_BLOCK_CONTROL_H
+
+#include <tool/design_block_control.h>
+
+class DESIGN_BLOCK_PANE;
+class PCB_EDIT_FRAME;
+
+/**
+ * Handle design block actions in the PCB editor.
+ */
+class PCB_DESIGN_BLOCK_CONTROL : public DESIGN_BLOCK_CONTROL
+{
+public:
+    PCB_DESIGN_BLOCK_CONTROL() : DESIGN_BLOCK_CONTROL( "pcbnew.PcbDesignBlockControl" ) {}
+    virtual ~PCB_DESIGN_BLOCK_CONTROL();
+
+    /// @copydoc TOOL_INTERACTIVE::Init()
+    bool Init() override;
+
+    int SaveBoardAsDesignBlock( const TOOL_EVENT& aEvent );
+    int SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent );
+
+    int SaveBoardToDesignBlock( const TOOL_EVENT& aEvent );
+    int SaveSelectionToDesignBlock( const TOOL_EVENT& aEvent );
+
+private:
+    LIB_ID getSelectedLibId();
+    ///< Set up handlers for various events.
+    void setTransitions() override;
+
+    DESIGN_BLOCK_PANE* getDesignBlockPane() override;
+
+    PCB_EDIT_FRAME* m_editFrame = nullptr;
+};
+
+
+#endif
diff --git a/pcbnew/widgets/pcb_design_block_pane.cpp b/pcbnew/widgets/pcb_design_block_pane.cpp
new file mode 100644
index 0000000000..0f3bebed75
--- /dev/null
+++ b/pcbnew/widgets/pcb_design_block_pane.cpp
@@ -0,0 +1,164 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+
+#include <design_block.h>
+#include <widgets/pcb_design_block_pane.h>
+#include <widgets/pcb_design_block_preview_widget.h>
+#include <widgets/panel_design_block_chooser.h>
+#include <pcbnew_settings.h>
+#include <kiface_base.h>
+#include <pcb_edit_frame.h>
+#include <core/kicad_algo.h>
+#include <template_fieldnames.h>
+#include <wx/button.h>
+#include <wx/checkbox.h>
+#include <wx/sizer.h>
+#include <confirm.h>
+#include <wildcards_and_files_ext.h>
+#include <tool/tool_manager.h>
+#include <tools/pcb_actions.h>
+#include <tools/pcb_design_block_control.h>
+
+// Do not make these static wxStrings; they need to respond to language changes
+#define REPEATED_PLACEMENT _( "Place repeated copies" )
+#define PLACE_AS_GROUP     _( "Place as group" )
+#define KEEP_ANNOTATIONS   _( "Keep annotations" )
+
+PCB_DESIGN_BLOCK_PANE::PCB_DESIGN_BLOCK_PANE( PCB_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
+                                      std::vector<LIB_ID>& aHistoryList ) :
+        DESIGN_BLOCK_PANE( aParent, aPreselect, aHistoryList )
+{
+    wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
+    m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList,
+                                                     [aParent]()
+                                                     {
+                                                         aParent->GetToolManager()->RunAction(
+                                                                 PCB_ACTIONS::placeDesignBlock );
+                                                     },
+                                                        aParent->GetToolManager()->GetTool<PCB_DESIGN_BLOCK_CONTROL>()
+                                                     );
+    m_chooserPanel->SetPreviewWidget(new PCB_DESIGN_BLOCK_PREVIEW_WIDGET( m_chooserPanel->GetDetailsPanel(), aParent ) );
+    sizer->Add( m_chooserPanel, 1, wxEXPAND, 5 );
+
+    if( aPreselect && aPreselect->IsValid() )
+        m_chooserPanel->SetPreselect( *aPreselect );
+
+    SetName( wxT( "Design Blocks" ) );
+
+    wxBoxSizer* cbSizer = new wxBoxSizer( wxVERTICAL );
+
+    m_repeatedPlacement = new wxCheckBox( this, wxID_ANY, REPEATED_PLACEMENT );
+    m_placeAsGroup = new wxCheckBox( this, wxID_ANY, PLACE_AS_GROUP );
+    m_keepAnnotations = new wxCheckBox( this, wxID_ANY, KEEP_ANNOTATIONS );
+    setLabelsAndTooltips();
+    UpdateCheckboxes();
+
+    // Set all checkbox handlers to the same function
+    m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this );
+    m_placeAsGroup->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this );
+    m_keepAnnotations->Bind( wxEVT_CHECKBOX, &PCB_DESIGN_BLOCK_PANE::OnCheckBox, this );
+
+    cbSizer->Add( m_repeatedPlacement, 0, wxTOP | wxLEFT, 2 );
+    cbSizer->Add( m_placeAsGroup, 0, wxTOP | wxLEFT, 2 );
+    cbSizer->Add( m_keepAnnotations, 0, wxTOP | wxLEFT | wxBOTTOM, 2 );
+
+    sizer->Add( cbSizer, 0, wxEXPAND, 5 );
+    SetSizer( sizer );
+
+    m_chooserPanel->FinishSetup();
+    Layout();
+
+    Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, m_chooserPanel );
+}
+
+
+void PCB_DESIGN_BLOCK_PANE::setLabelsAndTooltips()
+{
+    if( m_repeatedPlacement )
+    {
+        m_repeatedPlacement->SetLabel( REPEATED_PLACEMENT );
+        m_repeatedPlacement->SetToolTip( _( "Place copies of the design block on subsequent "
+                                            "clicks." ) );
+    }
+
+    if( m_placeAsGroup )
+    {
+        m_placeAsGroup->SetLabel( PLACE_AS_GROUP );
+        m_placeAsGroup->SetToolTip( _( "Place the design block as a group." ) );
+    }
+
+    if( m_keepAnnotations )
+    {
+        m_keepAnnotations->SetLabel( KEEP_ANNOTATIONS );
+        m_keepAnnotations->SetToolTip( _( "Preserve reference designators in the source "
+                                          "layout. Otherwise, clear them." ) );
+    }
+}
+
+void PCB_DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent )
+{
+    if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) )
+    {
+        cfg->m_DesignBlockChooserPanel.repeated_placement = m_repeatedPlacement->GetValue();
+        cfg->m_DesignBlockChooserPanel.place_as_sheet = m_placeAsGroup->GetValue();
+        cfg->m_DesignBlockChooserPanel.keep_annotations = m_keepAnnotations->GetValue();
+    }
+}
+
+
+void PCB_DESIGN_BLOCK_PANE::UpdateCheckboxes()
+{
+    if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) )
+    {
+        m_repeatedPlacement->SetValue( cfg->m_DesignBlockChooserPanel.repeated_placement );
+        m_placeAsGroup->SetValue( cfg->m_DesignBlockChooserPanel.place_as_sheet );
+        m_keepAnnotations->SetValue( cfg->m_DesignBlockChooserPanel.keep_annotations );
+    }
+}
+
+
+FILEDLG_IMPORT_SHEET_CONTENTS::FILEDLG_IMPORT_SHEET_CONTENTS( PCBNEW_SETTINGS* aSettings ) :
+        m_cbRepeatedPlacement( nullptr ), m_cbPlaceAsSheet( nullptr ), m_cbKeepAnnotations( nullptr )
+{
+    wxASSERT( aSettings );
+    m_settings = aSettings;
+};
+
+
+void FILEDLG_IMPORT_SHEET_CONTENTS::TransferDataFromCustomControls()
+{
+    m_settings->m_DesignBlockChooserPanel.repeated_placement = m_cbRepeatedPlacement->GetValue();
+    m_settings->m_DesignBlockChooserPanel.place_as_sheet = m_cbPlaceAsSheet->GetValue();
+    m_settings->m_DesignBlockChooserPanel.keep_annotations = m_cbKeepAnnotations->GetValue();
+}
+
+
+void FILEDLG_IMPORT_SHEET_CONTENTS::AddCustomControls( wxFileDialogCustomize& customizer )
+{
+    m_cbRepeatedPlacement = customizer.AddCheckBox( REPEATED_PLACEMENT );
+    m_cbRepeatedPlacement->SetValue( m_settings->m_DesignBlockChooserPanel.repeated_placement );
+    m_cbPlaceAsSheet = customizer.AddCheckBox( PLACE_AS_GROUP );
+    m_cbPlaceAsSheet->SetValue( m_settings->m_DesignBlockChooserPanel.place_as_sheet );
+    m_cbKeepAnnotations = customizer.AddCheckBox( KEEP_ANNOTATIONS );
+    m_cbKeepAnnotations->SetValue( m_settings->m_DesignBlockChooserPanel.keep_annotations );
+}
diff --git a/pcbnew/widgets/pcb_design_block_pane.h b/pcbnew/widgets/pcb_design_block_pane.h
new file mode 100644
index 0000000000..c9c8b20d63
--- /dev/null
+++ b/pcbnew/widgets/pcb_design_block_pane.h
@@ -0,0 +1,80 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 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
+ */
+#ifndef PCB_DESIGN_BLOCK_PANE_H
+#define PCB_DESIGN_BLOCK_PANE_H
+
+#include <widgets/design_block_pane.h>
+#include <design_block_tree_model_adapter.h>
+#include <widgets/html_window.h>
+#include <widgets/wx_panel.h>
+#include <wx/checkbox.h>
+#include <wx/filedlgcustomize.h>
+#include <pcbnew_settings.h>
+
+
+class PCB_EDIT_FRAME;
+class PANEL_DESIGN_BLOCK_CHOOSER;
+
+
+class PCB_DESIGN_BLOCK_PANE : public DESIGN_BLOCK_PANE
+{
+public:
+    PCB_DESIGN_BLOCK_PANE( PCB_EDIT_FRAME* aParent, const LIB_ID* aPreselect, std::vector<LIB_ID>& aHistoryList );
+
+    void OnCheckBox( wxCommandEvent& aEvent );
+    void UpdateCheckboxes();
+
+    void OnSaveSheetAsDesignBlock( wxCommandEvent& aEvent );
+    void OnSaveSelectionAsDesignBlock( wxCommandEvent& aEvent );
+
+protected:
+    void setLabelsAndTooltips() override;
+
+    wxCheckBox* m_repeatedPlacement;
+    wxCheckBox* m_placeAsGroup;
+    wxCheckBox* m_keepAnnotations;
+};
+
+
+// This is a helper class for the file dialog to allow the user to choose similar options
+// as the design block chooser when importing a sheet.
+class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook
+{
+public:
+    FILEDLG_IMPORT_SHEET_CONTENTS( PCBNEW_SETTINGS* aSettings );
+
+    void AddCustomControls( wxFileDialogCustomize& customizer ) override;
+
+    void TransferDataFromCustomControls() override;
+
+private:
+    PCBNEW_SETTINGS* m_settings;
+
+    wxFileDialogCheckBox* m_cbRepeatedPlacement;
+    wxFileDialogCheckBox* m_cbPlaceAsSheet;
+    wxFileDialogCheckBox* m_cbKeepAnnotations;
+
+    wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS );
+};
+
+#endif
diff --git a/pcbnew/widgets/pcb_design_block_preview_widget.cpp b/pcbnew/widgets/pcb_design_block_preview_widget.cpp
new file mode 100644
index 0000000000..383f617d45
--- /dev/null
+++ b/pcbnew/widgets/pcb_design_block_preview_widget.cpp
@@ -0,0 +1,250 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <board.h>
+#include <confirm.h>
+#include <pcb_view.h>
+#include <pcb_screen.h>
+#include <gal/gal_display_options.h>
+#include <gal/graphics_abstraction_layer.h>
+#include <math/vector2wx.h>
+#include <design_block_lib_table.h>
+#include <design_block.h>
+#include <footprint_preview_panel.h>
+#include <pgm_base.h>
+#include <pcb_painter.h>
+#include <pcb_edit_frame.h>
+#include <pcb_field.h>
+#include <pcb_io/pcb_io.h>
+#include <pcb_io/pcb_io_mgr.h>
+#include <project_pcb.h>
+#include <pcbnew_settings.h>
+//#include <pcb_helpers.h>
+#include <settings/settings_manager.h>
+#include <widgets/pcb_design_block_preview_widget.h>
+#include <widgets/wx_progress_reporters.h>
+#include <wx/log.h>
+#include <wx/stattext.h>
+#include <wx/panel.h>
+
+
+PCB_DESIGN_BLOCK_PREVIEW_WIDGET::PCB_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, PCB_EDIT_FRAME* aFrame ) :
+        DESIGN_BLOCK_PREVIEW_WIDGET( aParent ), m_preview( nullptr ), m_status( nullptr ), m_statusPanel( nullptr ),
+        m_statusSizer( nullptr ), m_previewItem( nullptr )
+{
+    SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
+    COMMON_SETTINGS*  common_settings = Pgm().GetCommonSettings();
+    PCBNEW_SETTINGS*  app_settings = mgr.GetAppSettings<PCBNEW_SETTINGS>( "pcbnew" );
+
+    m_galDisplayOptions.ReadConfig( *common_settings, app_settings->m_Window, this );
+    m_galDisplayOptions.m_forceDisplayCursor = false;
+
+    m_preview = FOOTPRINT_PREVIEW_PANEL::New( &aFrame->Kiway(), this, aFrame );
+    m_preview->SetStealsFocus( false );
+    m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
+    m_preview->GetGAL()->SetAxesEnabled( false );
+
+    // Do not display the grid: the look is not good for a small canvas area.
+    // But mainly, due to some strange bug I (JPC) was unable to fix, the grid creates
+    // strange artifacts on Windows when Pcb is run from KiCad manager (but not in
+    // stand alone...).
+    m_preview->GetGAL()->SetGridVisibility( true );
+
+    // Early initialization of the canvas background color,
+    // before any OnPaint event is fired for the canvas using a wrong bg color
+    KIGFX::VIEW* view = m_preview->GetView();
+    auto         settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
+
+    if( auto* theme = Pgm().GetSettingsManager().GetColorSettings( app_settings->m_ColorTheme ) )
+        settings->LoadColors( theme );
+
+    const COLOR4D& backgroundColor = settings->GetBackgroundColor();
+    const COLOR4D& foregroundColor = settings->GetCursorColor();
+
+    m_preview->GetGAL()->SetClearColor( backgroundColor );
+
+    m_outerSizer = new wxBoxSizer( wxVERTICAL );
+
+    m_statusPanel = new wxPanel( this );
+    m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
+    m_status = new wxStaticText( m_statusPanel, wxID_ANY, wxEmptyString );
+    m_status->SetForegroundColour( settings->GetLayerColor( LAYER_REFERENCEPART ).ToColour() );
+    m_statusSizer = new wxBoxSizer( wxVERTICAL );
+    m_statusSizer->Add( 0, 0, 1 ); // add a spacer
+    m_statusSizer->Add( m_status, 0, wxALIGN_CENTER );
+    m_statusSizer->Add( 0, 0, 1 ); // add a spacer
+    m_statusPanel->SetSizer( m_statusSizer );
+
+    // Give the status panel the same color scheme as the canvas so it isn't jarring when
+    // switched to.
+    m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
+    m_statusPanel->SetForegroundColour( foregroundColor.ToColour() );
+
+    // Give the preview panel a small top border to align its top with the status panel,
+    // and give the status panel a small bottom border to align its bottom with the preview
+    // panel.
+    m_outerSizer->Add( m_preview, 1, wxTOP | wxEXPAND, 5 );
+    m_outerSizer->Add( m_statusPanel, 1, wxBOTTOM | wxEXPAND, 5 );
+
+    // Hide the status panel to start
+    m_statusPanel->Hide();
+
+    SetSizer( m_outerSizer );
+    Layout();
+
+    Connect( wxEVT_SIZE, wxSizeEventHandler( PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this );
+}
+
+
+PCB_DESIGN_BLOCK_PREVIEW_WIDGET::~PCB_DESIGN_BLOCK_PREVIEW_WIDGET()
+{
+    if( m_previewItem )
+        m_preview->GetView()->Remove( m_previewItem );
+
+    delete m_previewItem;
+}
+
+
+void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
+{
+    wxCHECK( m_statusPanel, /* void */ );
+
+    m_status->SetLabel( aText );
+    m_preview->Hide();
+    m_statusPanel->Show();
+    Layout();
+}
+
+
+void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
+{
+    if( m_previewItem )
+    {
+        fitOnDrawArea();
+        m_preview->ForceRefresh();
+    }
+
+    aEvent.Skip();
+}
+
+
+void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
+{
+    if( !m_previewItem )
+        return;
+
+    // set the view scale to fit the item on screen
+    KIGFX::VIEW* view = m_preview->GetView();
+
+    // Calculate the drawing area size, in internal units, for a scaling factor = 1.0
+    view->SetScale( 1.0 );
+    VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false );
+    // Calculate the draw scale to fit the drawing area
+    double scale =
+            std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ), fabs( clientSize.y / m_itemBBox.GetHeight() ) );
+
+    // Above calculation will yield an exact fit; add a bit of whitespace around block
+    scale /= 1.2;
+
+    // Now fix the best scale
+    view->SetScale( scale );
+    view->SetCenter( m_itemBBox.Centre() );
+}
+
+
+void PCB_DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock )
+{
+    KIGFX::VIEW* view = m_preview->GetView();
+
+    if( m_previewItem )
+    {
+        view->Clear();
+        delete m_previewItem;
+        m_previewItem = nullptr;
+    }
+
+    if( aDesignBlock && wxFileExists( aDesignBlock->GetBoardFile() ) )
+    {
+        try
+        {
+            IO_RELEASER<PCB_IO>  pi( PCB_IO_MGR::PluginFind( PCB_IO_MGR::KICAD_SEXP ) );
+            WX_PROGRESS_REPORTER progressReporter( this, _( "Loading PCB" ), 1 );
+
+            pi->SetProgressReporter( &progressReporter );
+
+            m_previewItem = pi->LoadBoard( aDesignBlock->GetBoardFile(), nullptr );
+        }
+        catch( const IO_ERROR& ioe )
+        {
+            // You wouldn't think boardFn.GetFullPath() would throw, but we get a stack buffer
+            // underflow from ASAN.  While it's probably an ASAN error, a second try/catch doesn't
+            // cost us much.
+            try
+            {
+                if( ioe.Problem() != wxT( "CANCEL" ) )
+                {
+                    wxString msg =
+                            wxString::Format( _( "Error loading board file:\n%s" ), aDesignBlock->GetBoardFile() );
+                    DisplayErrorMessage( this, msg, ioe.What() );
+                }
+            }
+            catch( ... )
+            {
+                // That was already our best-efforts
+            }
+        }
+
+
+        BOX2I bBox;
+
+        if( m_previewItem )
+        {
+            for( BOARD_ITEM* item : m_previewItem->GetItemSet() )
+            {
+                view->Add( item );
+
+                if( item->Type() == PCB_FIELD_T )
+                {
+                    if( !static_cast<const PCB_FIELD*>( item )->IsVisible() )
+                        continue;
+                }
+
+                bBox.Merge( item->GetBoundingBox() );
+            }
+        }
+
+        m_itemBBox = bBox;
+
+        if( !m_preview->IsShownOnScreen() )
+        {
+            m_preview->Show();
+
+            if( m_statusPanel )
+                m_statusPanel->Hide();
+
+            Layout(); // Ensure panel size is up to date.
+        }
+
+        // Calculate the draw scale to fit the drawing area
+        fitOnDrawArea();
+    }
+
+    m_preview->ForceRefresh();
+    m_preview->Show();
+}
diff --git a/pcbnew/widgets/pcb_design_block_preview_widget.h b/pcbnew/widgets/pcb_design_block_preview_widget.h
new file mode 100644
index 0000000000..7a80ec3e7e
--- /dev/null
+++ b/pcbnew/widgets/pcb_design_block_preview_widget.h
@@ -0,0 +1,80 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright The 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PCB_DESIGN_BLOCK_PREVIEW_WIDGET_H
+#define PCB_DESIGN_BLOCK_PREVIEW_WIDGET_H
+
+#include <wx/panel.h>
+#include <widgets/design_block_preview_widget.h>
+#include <kiway.h>
+#include <gal_display_options_common.h>
+#include <class_draw_panel_gal.h>
+
+
+class LIB_ID;
+class DESIGN_BLOCK;
+class SCHEMATIC;
+class PCB_SHEET;
+class wxStaticText;
+class wxSizer;
+
+
+class PCB_DESIGN_BLOCK_PREVIEW_WIDGET : public DESIGN_BLOCK_PREVIEW_WIDGET
+{
+public:
+    /**
+     * Construct a schematic design block preview widget.
+     *
+     * @param aParent - parent window
+     */
+    PCB_DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, PCB_EDIT_FRAME* aFrame );
+
+    ~PCB_DESIGN_BLOCK_PREVIEW_WIDGET() override;
+
+    /**
+     * Set the contents of the status label and display it.
+     */
+    void SetStatusText( const wxString& aText ) override;
+
+    /**
+     * Set the currently displayed symbol.
+     */
+    void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock ) override;
+
+protected:
+    void onSize( wxSizeEvent& aEvent );
+
+    void fitOnDrawArea();    // set the view scale to fit the item on screen and center
+
+    GAL_DISPLAY_OPTIONS_IMPL   m_galDisplayOptions;
+    EDA_DRAW_PANEL_GAL*        m_preview;
+
+    wxStaticText*              m_status;
+    wxPanel*                   m_statusPanel;
+    wxSizer*                   m_statusSizer;
+    wxSizer*                   m_outerSizer;
+
+    BOARD*                     m_previewItem;
+
+    /// The bounding box of the current item
+    BOX2I                      m_itemBBox;
+};
+
+
+#endif