desktop/source/lib/init.cxx   |   27 +++++++++++++++++++++++++++
 include/vcl/svapp.hxx         |    6 ++++++
 vcl/inc/salinst.hxx           |    4 ++++
 vcl/source/window/builder.cxx |   37 +++++++++++++++++++++++++++++++++++--
 4 files changed, 72 insertions(+), 2 deletions(-)

New commits:
commit 836ff0a0ffb70c7cafd61b3da0bfa2170d1d442f
Author:     Caolán McNamara <[email protected]>
AuthorDate: Fri Dec 5 20:22:22 2025 +0000
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Dec 8 10:43:58 2025 +0100

    Collect .ui use coverage information
    
    bootstrap reporting of what .ui files were used in a session.
    
    Change-Id: I19b004482a3892898a9738e5e98ee084ab8cdcec
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195113
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index ab956e1ffc89..fdf7477db984 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -5621,6 +5621,33 @@ static void doc_postUnoCommand(LibreOfficeKitDocument* 
pThis, const char* pComma
         hideSidebar();
         return;
     }
+    else if (gImpl && aCommand == ".uno:UICoverage")
+    {
+        for (const beans::PropertyValue& rPropValue : aPropertyValuesVector)
+        {
+            if (rPropValue.Name == "Report")
+            {
+                bool report(true);
+                rPropValue.Value >>= report;
+                if (report)
+                {
+                    tools::JsonWriter aJson;
+                    aJson.put("commandName", aCommand);
+                    aJson.put("success", true);
+                    aJson.put("result", Application::UICoverageReport());
+                    
pDocument->mpCallbackFlushHandlers[nView]->queue(LOK_CALLBACK_UNO_COMMAND_RESULT,
 aJson.finishAndGetAsOString());
+                }
+            }
+            else if (rPropValue.Name == "Track")
+            {
+                bool track(false);
+                rPropValue.Value >>= track;
+                Application::EnableUICoverage(track);
+            }
+        }
+
+        return;
+    }
 
     if (aCommand != ".uno:Save")
     {
diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx
index 8f8a86f7e67b..829edf7f5251 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -1234,6 +1234,12 @@ public:
     /** Determines if safe mode is enabled */
     static bool                 IsSafeModeEnabled();
 
+    /** Collect what .ui files are used*/
+    static void                 EnableUICoverage(bool bEnable);
+
+    /** Report on what .ui files were used*/
+    static OString              UICoverageReport();
+
     ///@}
 
     /** Get the desktop environment the process is currently running in
diff --git a/vcl/inc/salinst.hxx b/vcl/inc/salinst.hxx
index f3af5cf3794d..20d5d2ba3f46 100644
--- a/vcl/inc/salinst.hxx
+++ b/vcl/inc/salinst.hxx
@@ -21,6 +21,7 @@
 #define INCLUDED_VCL_INC_SALINST_HXX
 
 #include <sal/types.h>
+#include <o3tl/sorted_vector.hxx>
 #include <rtl/ref.hxx>
 #include <vcl/dllapi.h>
 #include <vcl/salgtype.hxx>
@@ -78,6 +79,7 @@ private:
     rtl::Reference< vcl::DisplayConnectionDispatch > m_pEventInst;
     const std::unique_ptr<comphelper::SolarMutex> m_pYieldMutex;
     css::uno::Reference<css::uno::XInterface> m_clipboard;
+    o3tl::sorted_vector<OUString> m_usedUI;
 
 protected:
     bool m_bSupportsBitmap32 = false;
@@ -94,6 +96,8 @@ public:
     bool supportsBitmap32() const { return m_bSupportsBitmap32; }
     bool supportsOpenGL() const { return m_bSupportsOpenGL; }
 
+    o3tl::sorted_vector<OUString>& getUsedUIList() { return m_usedUI; }
+
     //called directly after Application::Init
     virtual void            AfterAppInit() {}
     virtual bool            SVMainHook(int*) { return false; }
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 6a04071705ed..080e0b4c4d86 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -183,8 +183,36 @@ namespace
 
 }
 
+static bool bEnableUICoverage = false;
+
+void Application::EnableUICoverage(bool bEnable)
+{
+    bEnableUICoverage = bEnable;
+    if (!bEnableUICoverage)
+        ImplGetSVData()->mpDefInst->getUsedUIList().clear();
+}
+
+OString Application::UICoverageReport()
+{
+    tools::JsonWriter aJson;
+
+    const auto& entries = ImplGetSVData()->mpDefInst->getUsedUIList();
+    {
+        auto childrenNode = aJson.startArray("used");
+        for (const auto& entry : entries)
+            aJson.putSimpleValue(entry);
+    }
+
+    return aJson.finishAndGetAsOString();
+}
+
 std::unique_ptr<weld::Builder> Application::CreateBuilder(weld::Widget* 
pParent, const OUString &rUIFile, bool bMobile, sal_uInt64 nLOKWindowId)
 {
+    ImplSVData* pSVData = ImplGetSVData();
+
+    if (bEnableUICoverage)
+        pSVData->mpDefInst->getUsedUIList().insert(rUIFile);
+
     if (comphelper::LibreOfficeKit::isActive() && 
!jsdialog::isIgnored(rUIFile))
     {
         if (jsdialog::isBuilderEnabledForSidebar(rUIFile))
@@ -206,11 +234,16 @@ std::unique_ptr<weld::Builder> 
Application::CreateBuilder(weld::Widget* pParent,
             SAL_WARN("vcl", "UI file not enabled for JSDialogs: " << rUIFile);
     }
 
-    return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile);
+    return pSVData->mpDefInst->CreateBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile);
 }
 
 std::unique_ptr<weld::Builder> Application::CreateInterimBuilder(vcl::Window* 
pParent, const OUString &rUIFile, bool bAllowCycleFocusOut, sal_uInt64 
nLOKWindowId)
 {
+    ImplSVData* pSVData = ImplGetSVData();
+
+    if (bEnableUICoverage)
+        pSVData->mpDefInst->getUsedUIList().insert(rUIFile);
+
     if (comphelper::LibreOfficeKit::isActive() && 
!jsdialog::isIgnored(rUIFile))
     {
         // Notebookbar sub controls
@@ -226,7 +259,7 @@ std::unique_ptr<weld::Builder> 
Application::CreateInterimBuilder(vcl::Window* pP
             SAL_WARN("vcl", "UI file not enabled for JSDialogs: " << rUIFile);
     }
 
-    return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId);
+    return pSVData->mpDefInst->CreateInterimBuilder(pParent, 
AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId);
 }
 
 weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, 
VclMessageType eMessageType,

Reply via email to