From 895c977dd3d27fa9d4635025ecbdd667cb0eea46 Mon Sep 17 00:00:00 2001
From: Mike Williams <mike@mikebwilliams.com>
Date: Tue, 10 Sep 2024 13:56:38 -0400
Subject: [PATCH] design blocks: move all loading out the main kicad frame

This lets standalone eeschema at least use the global table if a project
is not loaded, similar to how symbols/footprints work.
---
 common/CMakeLists.txt                         |   1 +
 common/design_block_info.h                    |   3 -
 common/design_block_info_impl.cpp             |  80 ----------
 common/design_block_info_impl.h               |   3 -
 ...g_global_design_block_lib_table_config.cpp | 142 ++++++++++++++++++
 ...log_global_design_block_lib_table_config.h |  37 +++++
 eeschema/eeschema.cpp                         |  50 +++++-
 kicad/kicad.cpp                               |  23 ---
 kicad/pgm_kicad.h                             |   3 -
 9 files changed, 229 insertions(+), 113 deletions(-)
 create mode 100644 common/dialogs/dialog_global_design_block_lib_table_config.cpp
 create mode 100644 common/dialogs/dialog_global_design_block_lib_table_config.h

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index dcb77f1105..efee34a557 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -326,6 +326,7 @@ set( COMMON_DLG_SRCS
     dialogs/dialog_embed_files.cpp
     dialogs/dialog_global_lib_table_config.cpp
     dialogs/dialog_global_lib_table_config_base.cpp
+    dialogs/dialog_global_design_block_lib_table_config.cpp
     dialogs/dialog_grid_settings.cpp
     dialogs/dialog_grid_settings_base.cpp
     dialogs/dialog_hotkey_list.cpp
diff --git a/common/design_block_info.h b/common/design_block_info.h
index 9d8862bce4..6bf05d70fd 100644
--- a/common/design_block_info.h
+++ b/common/design_block_info.h
@@ -146,9 +146,6 @@ public:
 
     virtual ~DESIGN_BLOCK_LIST() {}
 
-    virtual void WriteCacheToFile( const wxString& aFilePath ){};
-    virtual void ReadCacheFromFile( const wxString& aFilePath ){};
-
     /**
      * @return the number of items stored in list
      */
diff --git a/common/design_block_info_impl.cpp b/common/design_block_info_impl.cpp
index 84eff9a1fb..dc3abbc156 100644
--- a/common/design_block_info_impl.cpp
+++ b/common/design_block_info_impl.cpp
@@ -294,83 +294,3 @@ DESIGN_BLOCK_LIST_IMPL::DESIGN_BLOCK_LIST_IMPL() :
         m_list_timestamp( 0 ), m_progress_reporter( nullptr ), m_cancelled( false )
 {
 }
-
-
-void DESIGN_BLOCK_LIST_IMPL::WriteCacheToFile( const wxString& aFilePath )
-{
-    wxFileName          tmpFileName = wxFileName::CreateTempFileName( aFilePath );
-    wxFFileOutputStream outStream( tmpFileName.GetFullPath() );
-    wxTextOutputStream  txtStream( outStream );
-
-    if( !outStream.IsOk() )
-    {
-        return;
-    }
-
-    txtStream << wxString::Format( wxT( "%lld" ), m_list_timestamp ) << endl;
-
-    for( std::unique_ptr<DESIGN_BLOCK_INFO>& dbinfo : m_list )
-    {
-        txtStream << dbinfo->GetLibNickname() << endl;
-        txtStream << dbinfo->GetName() << endl;
-        txtStream << EscapeString( dbinfo->GetDesc(), CTX_LINE ) << endl;
-        txtStream << EscapeString( dbinfo->GetKeywords(), CTX_LINE ) << endl;
-        txtStream << wxString::Format( wxT( "%d" ), dbinfo->GetOrderNum() ) << endl;
-    }
-
-    txtStream.Flush();
-    outStream.Close();
-
-    // Preserve the permissions of the current file
-    KIPLATFORM::IO::DuplicatePermissions( aFilePath, tmpFileName.GetFullPath() );
-
-    if( !wxRenameFile( tmpFileName.GetFullPath(), aFilePath, true ) )
-    {
-        // cleanup in case rename failed
-        // its also not the end of the world since this is just a cache file
-        wxRemoveFile( tmpFileName.GetFullPath() );
-    }
-}
-
-
-void DESIGN_BLOCK_LIST_IMPL::ReadCacheFromFile( const wxString& aFilePath )
-{
-    wxTextFile cacheFile( aFilePath );
-
-    m_list_timestamp = 0;
-    m_list.clear();
-
-    try
-    {
-        if( cacheFile.Exists() && cacheFile.Open() )
-        {
-            cacheFile.GetFirstLine().ToLongLong( &m_list_timestamp );
-
-            while( cacheFile.GetCurrentLine() + 6 < cacheFile.GetLineCount() )
-            {
-                wxString libNickname = cacheFile.GetNextLine();
-                wxString name = cacheFile.GetNextLine();
-                wxString desc = UnescapeString( cacheFile.GetNextLine() );
-                wxString keywords = UnescapeString( cacheFile.GetNextLine() );
-                int      orderNum = wxAtoi( cacheFile.GetNextLine() );
-
-                DESIGN_BLOCK_INFO_IMPL* dbinfo =
-                        new DESIGN_BLOCK_INFO_IMPL( libNickname, name, desc, keywords, orderNum );
-
-                m_list.emplace_back( std::unique_ptr<DESIGN_BLOCK_INFO>( dbinfo ) );
-            }
-        }
-    }
-    catch( ... )
-    {
-        // whatever went wrong, invalidate the cache
-        m_list_timestamp = 0;
-    }
-
-    // Sanity check: an empty list is very unlikely to be correct.
-    if( m_list.size() == 0 )
-        m_list_timestamp = 0;
-
-    if( cacheFile.IsOpened() )
-        cacheFile.Close();
-}
diff --git a/common/design_block_info_impl.h b/common/design_block_info_impl.h
index 0f1f6bc4ce..cd056b3bd1 100644
--- a/common/design_block_info_impl.h
+++ b/common/design_block_info_impl.h
@@ -83,9 +83,6 @@ public:
     DESIGN_BLOCK_LIST_IMPL();
     virtual ~DESIGN_BLOCK_LIST_IMPL(){};
 
-    void WriteCacheToFile( const wxString& aFilePath ) override;
-    void ReadCacheFromFile( const wxString& aFilePath ) override;
-
     bool ReadDesignBlockFiles( DESIGN_BLOCK_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
                                PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
 
diff --git a/common/dialogs/dialog_global_design_block_lib_table_config.cpp b/common/dialogs/dialog_global_design_block_lib_table_config.cpp
new file mode 100644
index 0000000000..ebd8c77cad
--- /dev/null
+++ b/common/dialogs/dialog_global_design_block_lib_table_config.cpp
@@ -0,0 +1,142 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2019 Wayne Stambaugh <stambaughw@gmail.com>
+ * Copyright (C) 2017-2019 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 "dialog_global_design_block_lib_table_config.h"
+
+#include <confirm.h>
+#include <kiface_base.h>
+#include <kiway.h>
+#include <macros.h>
+
+#include "design_block_lib_table.h"
+
+
+DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG(
+        wxWindow* aParent ) :
+        DIALOG_GLOBAL_LIB_TABLE_CONFIG( aParent, _( "design block" ), KIWAY::FACE_SCH )
+{
+}
+
+
+DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::~DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG()
+{
+}
+
+
+wxFileName DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::GetGlobalTableFileName()
+{
+    return DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName();
+}
+
+
+bool DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG::TransferDataFromWindow()
+{
+    // Create an empty table if requested by the user.
+    if( m_emptyRb->GetValue() )
+    {
+        DESIGN_BLOCK_LIB_TABLE emptyTable;
+
+        try
+        {
+            emptyTable.Save( DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName() );
+        }
+        catch( const IO_ERROR& ioe )
+        {
+            DisplayError( this,
+                          wxString::Format( _( "Error creating design block library table '%s'.\n"
+                                               "%s" ),
+                                            DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName(),
+                                            ioe.What() ) );
+            return false;
+        }
+    }
+    else
+    {
+        wxString fileName = m_filePicker1->GetPath();
+
+        if( fileName.IsEmpty() )
+        {
+            DisplayError( this, _( "Please select a design block library table file." ) );
+            return false;
+        }
+
+        wxFileName fn = fileName;
+
+        // Make sure the design block library table to copy actually exists.
+        if( !fn.FileExists() )
+        {
+            DisplayError( this, wxString::Format( _( "File '%s' not found." ), fn.GetFullPath() ) );
+            return false;
+        }
+
+        // Make sure the design block library table to copy is a valid design block library table file.
+        DESIGN_BLOCK_LIB_TABLE tmpTable;
+
+        try
+        {
+            tmpTable.Load( fn.GetFullPath() );
+        }
+        catch( const IO_ERROR& ioe )
+        {
+            DisplayError( this,
+                          wxString::Format( _( "Error reading design block library table '%s'.\n"
+                                               "%s" ),
+                                            fn.GetFullPath(), ioe.What() ) );
+            return false;
+        }
+
+        // Create the config path if it doesn't already exist.
+        wxFileName designBlockTableFileName = DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName();
+
+        if( !designBlockTableFileName.DirExists()
+            && !designBlockTableFileName.Mkdir( 0x777, wxPATH_MKDIR_FULL ) )
+        {
+            DisplayError( this, wxString::Format( _( "Cannot create global library table '%s'." ),
+                                                  designBlockTableFileName.GetPath() ) );
+            return false;
+        }
+
+        // Copy the global design block library table file to the user config.
+        if( !::wxCopyFile( fn.GetFullPath(), designBlockTableFileName.GetFullPath() ) )
+        {
+            DisplayError(
+                    this,
+                    wxString::Format( _( "Error copying global design block library table '%s' "
+                                         "to '%s'." ),
+                                      fn.GetFullPath(), designBlockTableFileName.GetFullPath() ) );
+            return false;
+        }
+    }
+
+    // Load the successfully copied design block library table file.  This should not fail since the
+    // file was tested above.  Check for failure anyway to keep the compiler from complaining.
+    try
+    {
+        if( !DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable(
+                    DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() ) )
+            return false;
+    }
+    catch( const IO_ERROR& )
+    {
+        return false;
+    }
+
+    return true;
+}
diff --git a/common/dialogs/dialog_global_design_block_lib_table_config.h b/common/dialogs/dialog_global_design_block_lib_table_config.h
new file mode 100644
index 0000000000..1cd213a430
--- /dev/null
+++ b/common/dialogs/dialog_global_design_block_lib_table_config.h
@@ -0,0 +1,37 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017-2024 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 _DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG_H_
+#define _DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG_H_
+
+#include <dialogs/dialog_global_lib_table_config.h>
+
+
+class DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG : public DIALOG_GLOBAL_LIB_TABLE_CONFIG
+{
+public:
+    DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG( wxWindow* aParent );
+    virtual ~DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG();
+
+    bool TransferDataFromWindow() override;
+
+    virtual wxFileName GetGlobalTableFileName() override;
+};
+
+#endif // _DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG_H_
diff --git a/eeschema/eeschema.cpp b/eeschema/eeschema.cpp
index 3d6980963c..62342ef68d 100644
--- a/eeschema/eeschema.cpp
+++ b/eeschema/eeschema.cpp
@@ -33,10 +33,12 @@
 #include "eeschema_helpers.h"
 #include <eeschema_settings.h>
 #include <sch_edit_frame.h>
+#include <design_block_lib_table.h>
 #include <symbol_edit_frame.h>
 #include <symbol_viewer_frame.h>
 #include <symbol_chooser_frame.h>
 #include <symbol_lib_table.h>
+#include <dialogs/dialog_global_design_block_lib_table_config.h>
 #include <dialogs/dialog_global_sym_lib_table_config.h>
 #include <dialogs/panel_grid_settings.h>
 #include <dialogs/panel_simulator_preferences.h>
@@ -346,6 +348,7 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
 
 private:
     bool loadGlobalLibTable();
+    bool loadGlobalDesignBlockLibTable();
 
     std::unique_ptr<EESCHEMA_JOBS_HANDLER> m_jobHandler;
 
@@ -386,7 +389,7 @@ bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 
     start_common( aCtlBits );
 
-    if( !loadGlobalLibTable() )
+    if( !loadGlobalLibTable() || !loadGlobalDesignBlockLibTable() )
     {
         // we didnt get anywhere deregister the settings
         aProgram->GetSettingsManager().FlushAndRelease( symSettings, false );
@@ -456,6 +459,51 @@ bool IFACE::loadGlobalLibTable()
 }
 
 
+bool IFACE::loadGlobalDesignBlockLibTable()
+{
+    wxFileName fn = DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName();
+
+    if( !fn.FileExists() )
+    {
+        if( !( m_start_flags & KFCTL_CLI ) )
+        {
+            // Ensure the splash screen does not hide the dialog:
+            Pgm().HideSplash();
+
+            DIALOG_GLOBAL_DESIGN_BLOCK_LIB_TABLE_CONFIG dbDialog( nullptr );
+
+            if( dbDialog.ShowModal() != wxID_OK )
+                return false;
+        }
+    }
+    else
+    {
+        try
+        {
+            // The global table is not related to a specific project.  All projects
+            // will use the same global table.  So the KIFACE::OnKifaceStart() contract
+            // of avoiding anything project specific is not violated here.
+            if( !DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable(
+                        DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() ) )
+                return false;
+        }
+        catch( const IO_ERROR& ioe )
+        {
+            // if we are here, a incorrect global design block library table was found.
+            // Incorrect global design block library table is not a fatal error:
+            // the user just has to edit the (partially) loaded table.
+            wxString msg = _(
+                    "An error occurred attempting to load the global design block library table.\n"
+                    "Please edit this global design block library table in Preferences menu." );
+
+            DisplayErrorMessage( nullptr, msg, ioe.What() );
+        }
+    }
+
+    return true;
+}
+
+
 void IFACE::OnKifaceEnd()
 {
     end_common();
diff --git a/kicad/kicad.cpp b/kicad/kicad.cpp
index 05f5936a7b..707e31aa8b 100644
--- a/kicad/kicad.cpp
+++ b/kicad/kicad.cpp
@@ -62,8 +62,6 @@
 #include <api/api_server.h>
 #endif
 
-#include <design_block_lib_table.h>
-
 // a dummy to quiet linking with EDA_BASE_FRAME::config();
 #include <kiface_base.h>
 KIFACE_BASE& Kiface()
@@ -355,27 +353,6 @@ bool PGM_KICAD::OnPgmInit()
         }
     }
 
-    try
-    {
-        DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable( DESIGN_BLOCK_LIB_TABLE::GetGlobalLibTable() );
-    }
-    catch( const IO_ERROR& ioe )
-    {
-        // if we are here, a incorrect global design block library table was found.
-        // Incorrect global design block library table is not a fatal error:
-        // the user just has to edit the (partially) loaded table.
-        wxString msg = _( "An error occurred attempting to load the global design block library "
-                          "table.\n"
-                          "Please edit this global design block library table in Preferences "
-                          "menu." );
-
-        DisplayErrorMessage( nullptr, msg, ioe.What() );
-    }
-
-    if( managerFrame->IsProjectActive() && DESIGN_BLOCK_LIB_TABLE::GetGlobalList().GetCount() == 0 )
-        DESIGN_BLOCK_LIB_TABLE::GetGlobalList().ReadCacheFromFile(
-                Prj().GetProjectPath() + wxT( "design-block-info-cache" ) );
-
     frame->Show( true );
     frame->Raise();
 
diff --git a/kicad/pgm_kicad.h b/kicad/pgm_kicad.h
index d131e0b4c9..12e98c7451 100644
--- a/kicad/pgm_kicad.h
+++ b/kicad/pgm_kicad.h
@@ -28,9 +28,6 @@
 #include <pgm_base.h>
 #include <bin_mod.h>
 
-class DESIGN_BLOCK_LIB_TABLE;
-class DESIGN_BLOCK_LIST_IMPL;
-
 /**
  * PGM_KICAD
  * extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP