From e8e0b07223c53236067a90640d82d0382fefb309 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Tue, 28 Jan 2025 14:58:28 +0000
Subject: [PATCH] Honour on-the-fly language changes.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/19773
---
 common/lib_tree_model.cpp                     | 29 +++++----
 common/lib_tree_model_adapter.cpp             | 27 ++++++--
 eeschema/sch_edit_frame.cpp                   |  8 +++
 eeschema/widgets/design_block_pane.cpp        | 61 ++++++++++++++++---
 eeschema/widgets/design_block_pane.h          |  7 +++
 .../widgets/panel_design_block_chooser.cpp    | 19 +++---
 eeschema/widgets/panel_design_block_chooser.h | 15 +++--
 eeschema/widgets/panel_symbol_chooser.cpp     |  6 +-
 include/lib_tree_model.h                      |  5 +-
 include/lib_tree_model_adapter.h              | 17 ++----
 pcbnew/widgets/panel_footprint_chooser.cpp    |  3 +-
 11 files changed, 142 insertions(+), 55 deletions(-)

diff --git a/common/lib_tree_model.cpp b/common/lib_tree_model.cpp
index 10705ccabe..61e8c19d28 100644
--- a/common/lib_tree_model.cpp
+++ b/common/lib_tree_model.cpp
@@ -87,17 +87,16 @@ bool LIB_TREE_NODE::Compare( LIB_TREE_NODE const& aNode1, LIB_TREE_NODE const& a
         return aNode1.m_Type < aNode2.m_Type;
 
     // Recently used sorts at top
-    if( aNode1.m_Name.StartsWith( wxT( "-- " ) ) )
+    if( aNode1.m_IsRecentlyUsedGroup )
     {
-        if( aNode2.m_Name.StartsWith( wxT( "-- " ) ) )
+        if( aNode2.m_IsRecentlyUsedGroup )
         {
-            // Make sure -- Recently Used is always at the top
-            // Start by checking the name of aNode2, because we
-            // want to satisfy the irreflexive property of the
-            // strict weak ordering.
-            if( aNode2.m_Name.StartsWith( wxT( "-- Recently Used" ) ) )
+            // Make sure "-- Recently Used" is always at the top
+            // Start by checking the name of aNode2, because we want to satisfy the irreflexive
+            // property of the strict weak ordering.
+            if( aNode2.m_IsRecentlyUsedGroup )
                 return false;
-            else if( aNode1.m_Name.StartsWith( wxT( "-- Recently Used" ) ) )
+            else if( aNode1.m_IsRecentlyUsedGroup )
                 return true;
 
             return aNode1.m_IntrinsicRank > aNode2.m_IntrinsicRank;
@@ -136,7 +135,9 @@ LIB_TREE_NODE::LIB_TREE_NODE()
       m_Pinned( false ),
       m_PinCount( 0 ),
       m_Unit( 0 ),
-      m_IsRoot( false )
+      m_IsRoot( false ),
+      m_IsRecentlyUsedGroup( false ),
+      m_IsAlreadyPlacedGroup( false )
 {}
 
 
@@ -359,12 +360,18 @@ LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxStri
 }
 
 
-void LIB_TREE_NODE_ROOT::RemoveLib( wxString const& aName )
+void LIB_TREE_NODE_ROOT::RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup )
 {
     m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
                                       [&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
                                       {
-                                          return aNode->m_Name == aName;
+                                          if( aRecentlyUsedGroup && aNode->m_IsRecentlyUsedGroup )
+                                              return true;
+
+                                          if( aAlreadyPlacedGroup && aNode->m_IsAlreadyPlacedGroup )
+                                              return true;
+
+                                          return false;
                                       } ),
                       m_Children.end() );
 }
diff --git a/common/lib_tree_model_adapter.cpp b/common/lib_tree_model_adapter.cpp
index 598b68b579..f4f558a698 100644
--- a/common/lib_tree_model_adapter.cpp
+++ b/common/lib_tree_model_adapter.cpp
@@ -232,9 +232,10 @@ LIB_TREE_NODE_LIBRARY& LIB_TREE_MODEL_ADAPTER::DoAddLibraryNode( const wxString&
 }
 
 
-void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( const wxString& aNodeName, const wxString& aDesc,
-                                           const std::vector<LIB_TREE_ITEM*>& aItemList,
-                                           bool pinned, bool presorted )
+LIB_TREE_NODE_LIBRARY& LIB_TREE_MODEL_ADAPTER::DoAddLibrary( const wxString& aNodeName,
+                                                             const wxString& aDesc,
+                                                             const std::vector<LIB_TREE_ITEM*>& aItemList,
+                                                             bool pinned, bool presorted )
 {
     LIB_TREE_NODE_LIBRARY& lib_node = DoAddLibraryNode( aNodeName, aDesc, pinned );
 
@@ -242,12 +243,14 @@ void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( const wxString& aNodeName, const wxSt
         lib_node.AddItem( item );
 
     lib_node.AssignIntrinsicRanks( presorted );
+
+    return lib_node;
 }
 
 
-void LIB_TREE_MODEL_ADAPTER::DoRemoveLibrary( const wxString& aNodeName )
+void LIB_TREE_MODEL_ADAPTER::RemoveGroup( bool aRecentGroup, bool aPlacedGroup )
 {
-    m_tree.RemoveLib( aNodeName );
+    m_tree.RemoveGroup( aRecentGroup, aPlacedGroup );
 }
 
 
@@ -409,6 +412,20 @@ void LIB_TREE_MODEL_ADAPTER::UnpinLibrary( LIB_TREE_NODE* aTreeNode )
 }
 
 
+void LIB_TREE_MODEL_ADAPTER::ShowChangedLanguage()
+{
+    recreateColumns();
+
+    for( const std::unique_ptr<LIB_TREE_NODE>& lib: m_tree.m_Children )
+    {
+        if( lib->m_IsRecentlyUsedGroup )
+            lib->m_Name = wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" );
+        else if( lib->m_IsAlreadyPlacedGroup )
+            lib->m_Name = wxT( "-- " ) + _( "Already Placed" ) + wxT( " --" );
+    }
+}
+
+
 wxDataViewColumn* LIB_TREE_MODEL_ADAPTER::doAddColumn( const wxString& aHeader, bool aTranslate )
 {
     wxString translatedHeader = aTranslate ? wxGetTranslation( aHeader ) : aHeader;
diff --git a/eeschema/sch_edit_frame.cpp b/eeschema/sch_edit_frame.cpp
index 71118780bf..29224da69d 100644
--- a/eeschema/sch_edit_frame.cpp
+++ b/eeschema/sch_edit_frame.cpp
@@ -2196,9 +2196,17 @@ void SCH_EDIT_FRAME::ShowChangedLanguage()
     // tooltips in toolbars
     RecreateToolbars();
 
+    // For some obscure reason, the AUI manager hides the first modified pane.
+    // So force show panes
+    wxAuiPaneInfo& design_blocks_pane_info = m_auimgr.GetPane( m_designBlocksPane );
+    bool panel_shown = design_blocks_pane_info.IsShown();
+    design_blocks_pane_info.Caption( _( "Design Blocks" ) );
+    design_blocks_pane_info.Show( panel_shown );
+
     m_auimgr.GetPane( m_hierarchy ).Caption( _( "Schematic Hierarchy" ) );
     m_auimgr.GetPane( m_selectionFilterPanel ).Caption( _( "Selection Filter" ) );
     m_auimgr.GetPane( m_propertiesPanel ).Caption( _( "Properties" ) );
+    m_auimgr.GetPane( m_designBlocksPane ).Caption( _( "Design Blocks" ) );
     m_auimgr.Update();
     m_hierarchy->UpdateHierarchyTree();
 
diff --git a/eeschema/widgets/design_block_pane.cpp b/eeschema/widgets/design_block_pane.cpp
index f88017d54e..d949d973e7 100644
--- a/eeschema/widgets/design_block_pane.cpp
+++ b/eeschema/widgets/design_block_pane.cpp
@@ -37,13 +37,16 @@
 #include <ee_actions.h>
 #include <tool/tool_manager.h>
 
-static const wxString REPEATED_PLACEMENT = _( "Place repeated copies" );
-static const wxString PLACE_AS_SHEET = _( "Place as sheet" );
-static const wxString KEEP_ANNOTATIONS = _( "Keep annotations" );
+
+// Do not make these static wxStrings; they need to respond to language changes
+#define REPEATED_PLACEMENT _( "Place repeated copies" )
+#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 )
+        WX_PANEL( aParent ),
+        m_frame( aParent )
 {
     wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
     m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList,
@@ -62,14 +65,9 @@ DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPr
     wxBoxSizer* cbSizer = new wxBoxSizer( wxVERTICAL );
 
     m_repeatedPlacement = new wxCheckBox( this, wxID_ANY, REPEATED_PLACEMENT );
-    m_repeatedPlacement->SetToolTip( _( "Place copies of the design block on subsequent clicks." ) );
-
     m_placeAsSheet = new wxCheckBox( this, wxID_ANY, PLACE_AS_SHEET );
-    m_placeAsSheet->SetToolTip( _( "Place the design block as a new sheet." ) );
-
     m_keepAnnotations = new wxCheckBox( this, wxID_ANY, KEEP_ANNOTATIONS );
-    m_keepAnnotations->SetToolTip( _( "Preserve reference designators in the source schematic. "
-                                      "Otherwise, clear then reannotate according to settings." ) );
+    setLabelsAndTooltips();
     UpdateCheckboxes();
 
     // Set all checkbox handlers to the same function
@@ -88,6 +86,49 @@ 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 );
+}
+
+
+DESIGN_BLOCK_PANE::~DESIGN_BLOCK_PANE()
+{
+    m_frame->Unbind( EDA_LANG_CHANGED, &DESIGN_BLOCK_PANE::OnLanguageChanged, this );
+}
+
+
+void 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_placeAsSheet )
+    {
+        m_placeAsSheet->SetLabel( PLACE_AS_SHEET );
+        m_placeAsSheet->SetToolTip( _( "Place the design block as a new sheet." ) );
+    }
+
+    if( m_keepAnnotations )
+    {
+        m_keepAnnotations->SetLabel( KEEP_ANNOTATIONS );
+        m_keepAnnotations->SetToolTip( _( "Preserve reference designators in the source "
+                                          "schematic. Otherwise, clear then reannotate according "
+                                          "to settings." ) );
+    }
+}
+
+
+void DESIGN_BLOCK_PANE::OnLanguageChanged( wxCommandEvent& aEvent )
+{
+    if( m_chooserPanel )
+        m_chooserPanel->ShowChangedLanguage();
+
+    setLabelsAndTooltips();
+
+    aEvent.Skip();
 }
 
 
diff --git a/eeschema/widgets/design_block_pane.h b/eeschema/widgets/design_block_pane.h
index 07a4505604..99d48e847e 100644
--- a/eeschema/widgets/design_block_pane.h
+++ b/eeschema/widgets/design_block_pane.h
@@ -50,6 +50,8 @@ public:
     DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
                        std::vector<LIB_ID>& aHistoryList );
 
+    ~DESIGN_BLOCK_PANE() override;
+
     /**
      * To be called after this dialog returns from ShowModal().
      *
@@ -78,6 +80,11 @@ public:
 
     PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; }
 
+protected:
+    void setLabelsAndTooltips();
+
+    virtual void OnLanguageChanged( wxCommandEvent& aEvent );
+
 protected:
     PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel;
 
diff --git a/eeschema/widgets/panel_design_block_chooser.cpp b/eeschema/widgets/panel_design_block_chooser.cpp
index ceca6d3225..3ebb03c88c 100644
--- a/eeschema/widgets/panel_design_block_chooser.cpp
+++ b/eeschema/widgets/panel_design_block_chooser.cpp
@@ -28,15 +28,11 @@
 #include <design_block_preview_widget.h>
 #include <kiface_base.h>
 #include <sch_edit_frame.h>
-#include <project_sch.h>
 #include <widgets/lib_tree.h>
 #include <settings/settings_manager.h>
 #include <project/project_file.h>
-#include <eeschema_settings.h>
 #include <dialogs/html_message_box.h>
 #include <string_utils.h>
-#include <wx/button.h>
-#include <wx/clipbrd.h>
 #include <wx/log.h>
 #include <wx/panel.h>
 #include <wx/sizer.h>
@@ -182,6 +178,13 @@ PANEL_DESIGN_BLOCK_CHOOSER::~PANEL_DESIGN_BLOCK_CHOOSER()
 }
 
 
+void PANEL_DESIGN_BLOCK_CHOOSER::ShowChangedLanguage()
+{
+    if( m_tree )
+        m_tree->ShowChangedLanguage();
+}
+
+
 void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
 {
     if( aEvent.GetKeyCode() == WXK_ESCAPE )
@@ -386,9 +389,7 @@ void PANEL_DESIGN_BLOCK_CHOOSER::addDesignBlockToHistory( const LIB_ID& aLibId )
 
 void PANEL_DESIGN_BLOCK_CHOOSER::rebuildHistoryNode()
 {
-    wxString history = wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" );
-
-    m_adapter->DoRemoveLibrary( history );
+    m_adapter->RemoveGroup( true, false );
 
     // Build the history list
     std::vector<LIB_TREE_ITEM*> historyInfos;
@@ -403,7 +404,9 @@ void PANEL_DESIGN_BLOCK_CHOOSER::rebuildHistoryNode()
             historyInfos.push_back( fp_info );
     }
 
-    m_adapter->DoAddLibrary( history, wxEmptyString, historyInfos, false, true );
+    m_adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
+                             historyInfos, false, true )
+            .m_IsRecentlyUsedGroup = true;
 }
 
 
diff --git a/eeschema/widgets/panel_design_block_chooser.h b/eeschema/widgets/panel_design_block_chooser.h
index 307fb46cde..19e5ab8673 100644
--- a/eeschema/widgets/panel_design_block_chooser.h
+++ b/eeschema/widgets/panel_design_block_chooser.h
@@ -76,6 +76,8 @@ public:
 
     LIB_TREE* GetLibTree() { return m_tree; }
 
+    void ShowChangedLanguage();
+
 protected:
     static constexpr int DBLCLICK_DELAY = 100; // milliseconds
 
@@ -98,21 +100,22 @@ protected:
 
     void displayErrors( wxTopLevelWindow* aWindow );
 
+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;
 
     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;
     std::function<void()> m_selectHandler;
 
-    std::vector<LIB_ID> m_historyList;
+    std::vector<LIB_ID>   m_historyList;
 };
 
 #endif /* PANEL_DESIGN_BLOCK_CHOOSER_H */
diff --git a/eeschema/widgets/panel_symbol_chooser.cpp b/eeschema/widgets/panel_symbol_chooser.cpp
index 4c3f98fdf0..99a7069923 100644
--- a/eeschema/widgets/panel_symbol_chooser.cpp
+++ b/eeschema/widgets/panel_symbol_chooser.cpp
@@ -169,13 +169,15 @@ PANEL_SYMBOL_CHOOSER::PANEL_SYMBOL_CHOOSER( SCH_BASE_FRAME* aFrame, wxWindow* aP
     processList( aAlreadyPlaced, already_placed_storage, already_placed );
 
     adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
-                           history_list, false, true );
+                           history_list, false, true )
+            .m_IsRecentlyUsedGroup = true;
 
     if( !aHistoryList.empty() )
         adapter->SetPreselectNode( aHistoryList[0].LibId, aHistoryList[0].Unit );
 
     adapter->DoAddLibrary( wxT( "-- " ) + _( "Already Placed" ) + wxT( " --" ), wxEmptyString,
-                           already_placed, false, true );
+                           already_placed, false, true )
+            .m_IsAlreadyPlacedGroup = true;
 
     const std::vector< wxString > libNicknames = libs->GetLogicalLibs();
 
diff --git a/include/lib_tree_model.h b/include/lib_tree_model.h
index e0a0b602f0..4ebcb087ca 100644
--- a/include/lib_tree_model.h
+++ b/include/lib_tree_model.h
@@ -147,6 +147,9 @@ public:
     LIB_ID      m_LibId;       // LIB_ID determined by the parent library nickname and alias name.
     int         m_Unit;        // Actual unit, or zero
     bool        m_IsRoot;      // Indicates if the symbol is a root symbol instead of an alias.
+
+    bool        m_IsRecentlyUsedGroup;
+    bool        m_IsAlreadyPlacedGroup;
 };
 
 
@@ -288,7 +291,7 @@ public:
     /**
      * Remove a library node from the root.
      */
-    void RemoveLib( wxString const& aName );
+    void RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup );
 
     /**
      * Clear the tree
diff --git a/include/lib_tree_model_adapter.h b/include/lib_tree_model_adapter.h
index 418e27ac70..82a594ba35 100644
--- a/include/lib_tree_model_adapter.h
+++ b/include/lib_tree_model_adapter.h
@@ -181,16 +181,14 @@ public:
      * @param aDesc        the description field of the parent node
      * @param aItemList    list of symbols
      */
-    void DoAddLibrary( const wxString& aNodeName, const wxString& aDesc,
-                       const std::vector<LIB_TREE_ITEM*>& aItemList,
-                       bool pinned, bool presorted );
+    LIB_TREE_NODE_LIBRARY& DoAddLibrary( const wxString& aNodeName, const wxString& aDesc,
+                                         const std::vector<LIB_TREE_ITEM*>& aItemList,
+                                         bool pinned, bool presorted );
 
     /**
-     * Remove the library by name.
-     *
-     * @param aNodeName    the name of the library to remove
+     * Remove one of the system groups from the library.
      */
-    void DoRemoveLibrary( const wxString& aNodeName );
+    void RemoveGroup( bool aRecentlyUsedGroup, bool aAlreadyPlacedGroup );
 
     std::vector<wxString> GetAvailableColumns() const { return m_availableColumns; }
 
@@ -318,10 +316,7 @@ public:
     void PinLibrary( LIB_TREE_NODE* aTreeNode );
     void UnpinLibrary( LIB_TREE_NODE* aTreeNode );
 
-    void ShowChangedLanguage()
-    {
-        recreateColumns();
-    }
+    void ShowChangedLanguage();
 
 protected:
     /**
diff --git a/pcbnew/widgets/panel_footprint_chooser.cpp b/pcbnew/widgets/panel_footprint_chooser.cpp
index 1800c6b39d..cce079af7c 100644
--- a/pcbnew/widgets/panel_footprint_chooser.cpp
+++ b/pcbnew/widgets/panel_footprint_chooser.cpp
@@ -94,7 +94,8 @@ PANEL_FOOTPRINT_CHOOSER::PANEL_FOOTPRINT_CHOOSER( PCB_BASE_FRAME* aFrame, wxTopL
     }
 
     adapter->DoAddLibrary( wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" ), wxEmptyString,
-                           historyInfos, false, true );
+                           historyInfos, false, true )
+            .m_IsRecentlyUsedGroup = true;
 
     if( historyInfos.size() )
         adapter->SetPreselectNode( historyInfos[0]->GetLIB_ID(), 0 );