comphelper/source/misc/lok.cxx               |   24 ++++++++++++++++++++++++
 desktop/qa/desktop_lib/test_desktop_lib.cxx  |    3 ++-
 desktop/source/lib/init.cxx                  |   11 +++++++++++
 include/LibreOfficeKit/LibreOfficeKit.h      |    4 ++++
 include/LibreOfficeKit/LibreOfficeKit.hxx    |    8 ++++++++
 include/LibreOfficeKit/LibreOfficeKitTypes.h |    6 ++++++
 include/comphelper/lok.hxx                   |    4 ++++
 sfx2/source/doc/guisaveas.cxx                |   19 ++++++-------------
 8 files changed, 65 insertions(+), 14 deletions(-)

New commits:
commit 8629ac94ef6a0283d7e6d48b7c58e0c54c42fbe8
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Dec 9 14:17:42 2025 +0100
Commit:     Miklos Vajna <[email protected]>
CommitDate: Wed Dec 10 11:12:28 2025 +0100

    cool#13770 lok: add a file save dialog callback
    
    Commit f4b9d6f024b27c4d8ddaabfe0eaa0b404137e3cf (Add horrible hack to
    ask the filename when exporting PDF in CODA-W, 2025-12-02) added a
    callback from core to the LOK client to trigger a file picker during PDF
    export.
    
    The problem is that in its current form, this callback is
    Windows-specific so other platforms (Linux, macOS) can't provide their
    implementation in a straightforward way.
    
    Fix the problem by reworkig this, so it works similar to the anyInput
    callback, which is cross-platform already.
    
    The behavior for the case when the new callback is not registered is
    meant to be unchanged.
    
    Change-Id: I27934774889fc827772b2665799eca358dd62e55

diff --git a/comphelper/source/misc/lok.cxx b/comphelper/source/misc/lok.cxx
index 189a01aad621..903ba9ffec8a 100644
--- a/comphelper/source/misc/lok.cxx
+++ b/comphelper/source/misc/lok.cxx
@@ -14,6 +14,9 @@
 #include <osl/process.h>
 #include <i18nlangtag/languagetag.hxx>
 #include <sal/log.hxx>
+#ifdef _WIN32
+#include <tools/UnixWrappers.h>
+#endif
 
 #include <iostream>
 
@@ -51,6 +54,8 @@ static std::function<bool(void*, int)> g_pAnyInputCallback;
 static void* g_pAnyInputCallbackData;
 static std::function<int()> g_pMostUrgentPriorityGetter;
 
+static std::function<void(const char*, char*, size_t)> 
g_pFileSaveDialogCallback;
+
 static std::function<void(int)> g_pViewSetter;
 static std::function<int()> g_pViewGetter;
 
@@ -373,6 +378,25 @@ bool anyInput()
     return bRet;
 }
 
+void setFileSaveDialogCallback(const std::function<void(const char*, char*, 
size_t)>& pFileSaveDialogCallback)
+{
+    g_pFileSaveDialogCallback = pFileSaveDialogCallback;
+}
+
+bool fileSaveDialog(const OUString& rSuggested, OUString& rResult)
+{
+    if (!g_pFileSaveDialogCallback)
+    {
+        return false;
+    }
+
+    OString aSuggested = rSuggested.toUtf8();
+    char aResult[PATH_MAX];
+    g_pFileSaveDialogCallback(aSuggested.getStr(), aResult, PATH_MAX);
+    rResult = OUString::fromUtf8(aResult);
+    return true;
+}
+
 void setViewSetter(std::function<void(int)> pViewSetter)
 {
     g_pViewSetter = pViewSetter;
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 0186e9ba9e4e..755058bf1f0e 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -4217,10 +4217,11 @@ void DesktopLOKTest::testABI()
     CPPUNIT_ASSERT_EQUAL(classOffset(23), offsetof(struct 
_LibreOfficeKitClass, extractDocumentStructureRequest));
     CPPUNIT_ASSERT_EQUAL(classOffset(24), offsetof(struct 
_LibreOfficeKitClass, registerAnyInputCallback));
     CPPUNIT_ASSERT_EQUAL(classOffset(25), offsetof(struct 
_LibreOfficeKitClass, getDocsCount));
+    CPPUNIT_ASSERT_EQUAL(classOffset(26), offsetof(struct 
_LibreOfficeKitClass, registerFileSaveDialogCallback));
 
     // When extending LibreOfficeKit with a new function pointer,  add new 
assert for the offsetof the
     // new function pointer and bump this assert for the size of the class.
-    CPPUNIT_ASSERT_EQUAL(classOffset(26), sizeof(struct _LibreOfficeKitClass));
+    CPPUNIT_ASSERT_EQUAL(classOffset(27), sizeof(struct _LibreOfficeKitClass));
 
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(0), offsetof(struct 
_LibreOfficeKitDocumentClass, destroy));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(1), offsetof(struct 
_LibreOfficeKitDocumentClass, saveAs));
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 3727f53041be..015409792174 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -2743,6 +2743,9 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* 
pThis,
                        LibreOfficeKitAnyInputCallback pAnyInputCallback,
                        void* pData);
 
+static void lo_registerFileSaveDialogCallback(LibreOfficeKit* pThis,
+                       LibreOfficeKitFileSaveDialogCallback 
pFileSaveDialogCallback);
+
 static void lo_sendDialogEvent(LibreOfficeKit* pThis,
                                unsigned long long int nLOKWindowId,
                                const char* pArguments);
@@ -2792,6 +2795,7 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
         m_pOfficeClass->setForkedChild = lo_setForkedChild;
         m_pOfficeClass->extractDocumentStructureRequest = 
lo_extractDocumentStructureRequest;
         m_pOfficeClass->registerAnyInputCallback = lo_registerAnyInputCallback;
+        m_pOfficeClass->registerFileSaveDialogCallback = 
lo_registerFileSaveDialogCallback;
         m_pOfficeClass->getDocsCount = lo_getDocsCount;
 
         gOfficeClass = m_pOfficeClass;
@@ -7935,6 +7939,13 @@ static void lo_registerAnyInputCallback(LibreOfficeKit* 
/*pThis*/,
     });
 }
 
+static void lo_registerFileSaveDialogCallback(LibreOfficeKit* /*pThis*/,
+                       LibreOfficeKitFileSaveDialogCallback 
pFileSaveDialogCallback)
+{
+    SolarMutexGuard aGuard;
+    
comphelper::LibreOfficeKit::setFileSaveDialogCallback(pFileSaveDialogCallback);
+}
+
 static int lo_getDocsCount(LibreOfficeKit* /*pThis*/)
 {
     SolarMutexGuard aGuard;
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index 8d6ec5fa2646..4c94cb6041ad 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -179,6 +179,10 @@ struct _LibreOfficeKitClass
 
     /// @see lok::Office::getDocsCount().
     int (*getDocsCount) (LibreOfficeKit* pThis);
+
+    /// @see lok::Office::registerFileSaveDialogCallback()
+    void (*registerFileSaveDialogCallback)(LibreOfficeKit* pThis,
+            LibreOfficeKitFileSaveDialogCallback pCallback);
 };
 
 #define LIBREOFFICEKIT_DOCUMENT_HAS(pDoc,member) 
LIBREOFFICEKIT_HAS_MEMBER(LibreOfficeKitDocumentClass,member,(pDoc)->pClass->nSize)
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 77facfb1867f..1b608d702504 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -1325,6 +1325,14 @@ public:
         return mpThis->pClass->getDocsCount(mpThis);
     }
 
+    /**
+     * Registers a callback that can display an interactive file save dialog.
+     */
+    void registerFileSaveDialogCallback(LibreOfficeKitFileSaveDialogCallback 
pCallback)
+    {
+        return mpThis->pClass->registerFileSaveDialogCallback(mpThis, 
pCallback);
+    }
+
     /**
      * Frees the memory pointed to by pFree.
      *
diff --git a/include/LibreOfficeKit/LibreOfficeKitTypes.h 
b/include/LibreOfficeKit/LibreOfficeKitTypes.h
index 939766567607..167b4f65ca8d 100644
--- a/include/LibreOfficeKit/LibreOfficeKitTypes.h
+++ b/include/LibreOfficeKit/LibreOfficeKitTypes.h
@@ -10,6 +10,8 @@
 #ifndef INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H
 #define INCLUDED_LIBREOFFICEKIT_LIBREOFFICEKIT_TYPES_H
 
+#include <stddef.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -28,6 +30,10 @@ typedef void (*LibreOfficeKitWakeCallback)(void* pData);
 /// @see lok::Office::registerAnyInputCallback()
 typedef bool (*LibreOfficeKitAnyInputCallback)(void* pData, int 
nMostUrgentPriority);
 
+/// @see lok::Office::registerFileSaveDialogCallback()
+typedef void (*LibreOfficeKitFileSaveDialogCallback)(const char* 
pSuggestedUri, char* pResultUri,
+                                                     size_t nResultUri);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/comphelper/lok.hxx b/include/comphelper/lok.hxx
index 9a992f6901cd..6e301a200743 100644
--- a/include/comphelper/lok.hxx
+++ b/include/comphelper/lok.hxx
@@ -150,6 +150,10 @@ setAnyInputCallback(const std::function<bool(void*, int)>& 
pAnyInputCallback, vo
                     std::function<int()> pMostUrgentPriorityGetter);
 COMPHELPER_DLLPUBLIC bool anyInput();
 
+COMPHELPER_DLLPUBLIC void setFileSaveDialogCallback(
+    const std::function<void(const char*, char*, size_t)>& 
pFileSaveDialogCallback);
+COMPHELPER_DLLPUBLIC bool fileSaveDialog(const OUString& rSuggested, OUString& 
rResult);
+
 // These allow setting callbacks, so that set/get of a LOK view is possible 
even in code that is
 // below sfx2.
 COMPHELPER_DLLPUBLIC void setViewSetter(std::function<void(int)> pViewSetter);
diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx
index 2407b99abfdb..43c1e0f210a9 100644
--- a/sfx2/source/doc/guisaveas.cxx
+++ b/sfx2/source/doc/guisaveas.cxx
@@ -1747,25 +1747,18 @@ bool 
SfxStoringHelper::FinishGUIStoreModel(::comphelper::SequenceAsHashMap::cons
     {
         OUString aFileName;
         aFileNameIter->second >>= aFileName;
-#ifdef _WIN32
         if (comphelper::LibreOfficeKit::isActive())
         {
-            // FIXME: Horrible hack. In CODA-W, we didn't actually
-            // display any dialog yet, so call into a function in
-            // CODA.cpp.
-            typedef void (*ofd_t)(const std::wstring& suggestedURI, 
std::string& result);
-            // Use mangled name to catch changes in parameters...
-            ofd_t ofd = (ofd_t)GetProcAddress(GetModuleHandle(NULL), 
"?output_file_dialog_from_core@@YAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@AEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z");
-            if (ofd != NULL)
+            // In the LOK case, we didn't actually display any dialog yet, so 
invoke a callback if
+            // that's set.
+            OUString aNewURI;
+            if (comphelper::LibreOfficeKit::fileSaveDialog(aFileName, aNewURI))
             {
-                std::string newURI;
-                (*ofd)(std::wstring(o3tl::toW(aFileName)), newURI);
-                if (newURI == "")
+                if (aNewURI.isEmpty())
                     return false;
-                aFileName = OUString::fromUtf8(newURI.c_str());
+                aFileName = aNewURI;
             }
         }
-#endif
 
         aURL.SetURL( aFileName );
         DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "Illegal 
URL!" );

Reply via email to