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!" );
