summaryrefslogtreecommitdiff
path: root/desktop/source/lib/init.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/source/lib/init.cxx')
-rw-r--r--desktop/source/lib/init.cxx917
1 files changed, 833 insertions, 84 deletions
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 5b4434b75e22..aa717dfe17cf 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -7,6 +7,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+#include "sfx2/lokhelper.hxx"
+#include <config_buildconfig.h>
#include <config_features.h>
#include <stdio.h>
@@ -24,6 +26,10 @@
#include <postmac.h>
#endif
+#ifdef LINUX
+#include <fcntl.h>
+#endif
+
#ifdef ANDROID
#include <osl/detail/android-bootstrap.h>
#endif
@@ -51,9 +57,11 @@
#include <rtl/strbuf.hxx>
#include <rtl/uri.hxx>
#include <svl/zforlist.hxx>
+#include <linguistic/misc.hxx>
#include <cppuhelper/bootstrap.hxx>
#include <comphelper/base64.hxx>
#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/propertysequence.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
@@ -62,11 +70,13 @@
#include <comphelper/propertyvalue.hxx>
#include <comphelper/scopeguard.hxx>
#include <comphelper/threadpool.hxx>
+#include <comphelper/servicehelper.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <com/sun/star/document/MacroExecMode.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/document/XDocumentLanguages.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/DispatchResultEvent.hpp>
#include <com/sun/star/frame/DispatchResultState.hpp>
@@ -92,8 +102,10 @@
#include <com/sun/star/xml/crypto/XCertificateCreator.hpp>
#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/linguistic2/LanguageGuessing.hpp>
#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
#include <com/sun/star/linguistic2/XSpellChecker.hpp>
+#include <com/sun/star/linguistic2/XProofreader.hpp>
#include <com/sun/star/i18n/LocaleCalendar2.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
@@ -109,7 +121,7 @@
#include <sfx2/viewfrm.hxx>
#include <sfx2/msgpool.hxx>
#include <sfx2/dispatch.hxx>
-#include <sfx2/lokcharthelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
#include <sfx2/DocumentSigner.hxx>
#include <sfx2/sidebar/SidebarDockingWindow.hxx>
#include <sfx2/sidebar/SidebarController.hxx>
@@ -128,6 +140,7 @@
#include <svtools/ctrltool.hxx>
#include <svtools/langtab.hxx>
#include <svtools/languagetoolcfg.hxx>
+#include <svtools/deeplcfg.hxx>
#include <vcl/fontcharmap.hxx>
#include <vcl/graphicfilter.hxx>
#ifdef IOS
@@ -137,6 +150,9 @@
#include <vcl/ImageTree.hxx>
#include <vcl/ITiledRenderable.hxx>
#include <vcl/dialoghelper.hxx>
+#ifdef _WIN32
+#include <vcl/BitmapReadAccess.hxx>
+#endif
#include <unicode/uchar.h>
#include <unotools/securityoptions.hxx>
#include <unotools/configmgr.hxx>
@@ -161,6 +177,7 @@
// Needed for getUndoManager()
#include <com/sun/star/document/XUndoManager.hpp>
#include <com/sun/star/document/XUndoManagerSupplier.hpp>
+#include <com/sun/star/document/XLinkTargetSupplier.hpp>
#include <editeng/sizeitem.hxx>
#include <svx/rulritem.hxx>
#include <svx/pageitem.hxx>
@@ -383,6 +400,97 @@ std::vector<beans::PropertyValue> desktop::jsonToPropertyValuesVector(const char
return aArguments;
}
+static bool extractLinks(const uno::Reference< container::XNameAccess >& xLinks, bool subcontent, OUStringBuffer& jsonText)
+{
+ const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
+
+ const sal_uLong nLinks = aNames.getLength();
+ const OUString* pNames = aNames.getConstArray();
+ const OUString aProp_LinkDisplayName( "LinkDisplayName" );
+ const OUString aProp_LinkTarget( "com.sun.star.document.LinkTarget" );
+ bool bIsTarget = false;
+ for( sal_uLong i = 0; i < nLinks; i++ )
+ {
+ uno::Any aAny;
+ OUString aLink( *pNames++ );
+
+ bool bError = false;
+ try
+ {
+ aAny = xLinks->getByName( aLink );
+ }
+ catch(const uno::Exception&)
+ {
+ // if the name of the target was invalid (like empty headings)
+ // no object can be provided
+ bError = true;
+ }
+ if(bError)
+ continue;
+
+ uno::Reference< beans::XPropertySet > xTarget;
+ if( aAny >>= xTarget )
+ {
+ try
+ {
+ // get name to display
+ aAny = xTarget->getPropertyValue( aProp_LinkDisplayName );
+ OUString aDisplayName;
+ aAny >>= aDisplayName;
+ OUString aStrDisplayname ( aDisplayName );
+
+ if (subcontent)
+ {
+ jsonText.append("\"");
+ jsonText.append(aStrDisplayname);
+ jsonText.append("\": \"");
+ jsonText.append(aLink);
+ jsonText.append("\"");
+ if (i < nLinks-1)
+ {
+ jsonText.append(", ");
+ }
+ }
+ else
+ {
+ uno::Reference< lang::XServiceInfo > xSI( xTarget, uno::UNO_QUERY );
+ bIsTarget = xSI->supportsService( aProp_LinkTarget );
+ if (i != 0)
+ {
+ if (!bIsTarget)
+ jsonText.append("}");
+ if (i < nLinks)
+ {
+ jsonText.append(", ");
+ }
+ }
+ jsonText.append("\"");
+ jsonText.append(aStrDisplayname);
+ jsonText.append("\": ");
+
+ if (bIsTarget)
+ {
+ jsonText.append("true");
+ continue;
+ }
+ jsonText.append("{");
+ }
+
+ uno::Reference< document::XLinkTargetSupplier > xLTS( xTarget, uno::UNO_QUERY );
+ if( xLTS.is() )
+ {
+ extractLinks(xLTS->getLinks(), true, jsonText);
+ }
+ }
+ catch(...)
+ {
+ SAL_WARN("lok", "extractLinks: Exception");
+ }
+ }
+ }
+ return bIsTarget;
+}
+
static void unoAnyToJson(tools::JsonWriter& rJson, const char * pNodeName, const uno::Any& anyItem)
{
auto aNode = rJson.startNode(pNodeName);
@@ -421,12 +529,26 @@ RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
{
aRet.m_aRectangle = tools::Rectangle(0, 0, SfxLokHelper::MaxTwips, SfxLokHelper::MaxTwips);
if (comphelper::LibreOfficeKit::isPartInInvalidation())
- aRet.m_nPart = std::stol(rPayload.substr(6));
+ {
+ int nSeparatorPos = rPayload.find(',', 6);
+ bool bHasMode = nSeparatorPos > 0;
+ if (bHasMode)
+ {
+ aRet.m_nPart = std::stol(rPayload.substr(6, nSeparatorPos - 6));
+ assert(rPayload.length() > o3tl::make_unsigned(nSeparatorPos));
+ aRet.m_nMode = std::stol(rPayload.substr(nSeparatorPos + 1));
+ }
+ else
+ {
+ aRet.m_nPart = std::stol(rPayload.substr(6));
+ aRet.m_nMode = 0;
+ }
+ }
return aRet;
}
- // Read '<left>, <top>, <width>, <height>[, <part>]'. C++ streams are simpler but slower.
+ // Read '<left>, <top>, <width>, <height>[, <part>, <mode>]'. C++ streams are simpler but slower.
const char* pos = rPayload.c_str();
const char* end = rPayload.c_str() + rPayload.size();
tools::Long nLeft = rtl_str_toInt64_WithLength(pos, 10, end - pos);
@@ -446,6 +568,7 @@ RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
assert(pos < end);
tools::Long nHeight = rtl_str_toInt64_WithLength(pos, 10, end - pos);
tools::Long nPart = INT_MIN;
+ tools::Long nMode = 0;
if (comphelper::LibreOfficeKit::isPartInInvalidation())
{
while( *pos != ',' )
@@ -453,10 +576,20 @@ RectangleAndPart RectangleAndPart::Create(const std::string& rPayload)
++pos;
assert(pos < end);
nPart = rtl_str_toInt64_WithLength(pos, 10, end - pos);
+
+ while( *pos && *pos != ',' )
+ ++pos;
+ if (*pos)
+ {
+ ++pos;
+ assert(pos < end);
+ nMode = rtl_str_toInt64_WithLength(pos, 10, end - pos);
+ }
}
aRet.m_aRectangle = SanitizedRectangle(nLeft, nTop, nWidth, nHeight);
aRet.m_nPart = nPart;
+ aRet.m_nMode = nMode;
return aRet;
}
@@ -878,13 +1011,21 @@ void setupSidebar(std::u16string_view sidebarDeckId = u"")
if (!pDockingWin)
return;
+ pViewFrame->ShowChildWindow( SID_SIDEBAR );
+
+ const rtl::Reference<sfx2::sidebar::SidebarController>& xController
+ = pDockingWin->GetOrCreateSidebarController();
+
+ xController->FadeIn();
+ xController->RequestOpenDeck();
+
if (!sidebarDeckId.empty())
{
- pDockingWin->GetSidebarController()->SwitchToDeck(sidebarDeckId);
+ xController->SwitchToDeck(sidebarDeckId);
}
else
{
- pDockingWin->GetSidebarController()->SwitchToDefaultDeck();
+ xController->SwitchToDefaultDeck();
}
pDockingWin->SyncUpdate();
@@ -963,6 +1104,7 @@ static void doc_selectPart(LibreOfficeKitDocument* pThis, int nPart, int nSelect
static void doc_moveSelectedParts(LibreOfficeKitDocument* pThis, int nPosition, bool bDuplicate);
static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart);
static void doc_setPartMode(LibreOfficeKitDocument* pThis, int nPartMode);
+static int doc_getEditMode(LibreOfficeKitDocument* pThis);
static void doc_paintTile(LibreOfficeKitDocument* pThis,
unsigned char* pBuffer,
const int nCanvasWidth, const int nCanvasHeight,
@@ -978,6 +1120,7 @@ static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
unsigned char* pBuffer,
const int nPart,
+ const int nMode,
const int nCanvasWidth, const int nCanvasHeight,
const int nTilePosX, const int nTilePosY,
const int nTileWidth, const int nTileHeight);
@@ -985,6 +1128,10 @@ static int doc_getTileMode(LibreOfficeKitDocument* pThis);
static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
long* pWidth,
long* pHeight);
+static void doc_getDataArea(LibreOfficeKitDocument* pThis,
+ long nTab,
+ long* pCol,
+ long* pRow);
static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
const char* pArguments);
@@ -1154,6 +1301,8 @@ static bool doc_renderSearchResult(LibreOfficeKitDocument* pThis,
static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const char* pArguments);
+static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const char* timezone);
+
} // extern "C"
namespace {
@@ -1230,6 +1379,7 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone
m_pDocumentClass->moveSelectedParts = doc_moveSelectedParts;
m_pDocumentClass->getPartName = doc_getPartName;
m_pDocumentClass->setPartMode = doc_setPartMode;
+ m_pDocumentClass->getEditMode = doc_getEditMode;
m_pDocumentClass->paintTile = doc_paintTile;
#ifdef IOS
m_pDocumentClass->paintTileToCGContext = doc_paintTileToCGContext;
@@ -1237,6 +1387,7 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone
m_pDocumentClass->paintPartTile = doc_paintPartTile;
m_pDocumentClass->getTileMode = doc_getTileMode;
m_pDocumentClass->getDocumentSize = doc_getDocumentSize;
+ m_pDocumentClass->getDataArea = doc_getDataArea;
m_pDocumentClass->initializeForRendering = doc_initializeForRendering;
m_pDocumentClass->registerCallback = doc_registerCallback;
m_pDocumentClass->postKeyEvent = doc_postKeyEvent;
@@ -1300,6 +1451,8 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone
m_pDocumentClass->sendContentControlEvent = doc_sendContentControlEvent;
+ m_pDocumentClass->setViewTimezone = doc_setViewTimezone;
+
gDocumentClass = m_pDocumentClass;
}
pClass = m_pDocumentClass.get();
@@ -1451,9 +1604,9 @@ void CallbackFlushHandler::libreOfficeKitViewCallbackWithViewId(int nType, const
queue(nType, callbackData);
}
-void CallbackFlushHandler::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart)
+void CallbackFlushHandler::libreOfficeKitViewInvalidateTilesCallback(const tools::Rectangle* pRect, int nPart, int nMode)
{
- CallbackData callbackData(pRect, nPart);
+ CallbackData callbackData(pRect, nPart, nMode);
queue(LOK_CALLBACK_INVALIDATE_TILES, callbackData);
}
@@ -1473,6 +1626,29 @@ void CallbackFlushHandler::libreOfficeKitViewUpdatedCallbackPerViewId(int nType,
setUpdatedTypePerViewId(nType, nViewId, nSourceViewId, true);
}
+void CallbackFlushHandler::dumpState(rtl::OStringBuffer &rState)
+{
+ // NB. no locking
+ rState.append("\nView:\t");
+ rState.append(static_cast<sal_Int32>(m_viewId));
+ rState.append("\n\tDisableCallbacks:\t");
+ rState.append(static_cast<sal_Int32>(m_nDisableCallbacks));
+ rState.append("\n\tStates:\n");
+ for (const auto &i : m_states)
+ {
+ rState.append("\n\t\t");
+ rState.append(static_cast<sal_Int32>(i.first));
+ rState.append("\t");
+ rState.append(i.second);
+ }
+}
+
+void CallbackFlushHandler::libreOfficeKitViewAddPendingInvalidateTiles()
+{
+ // Invoke() will call flushPendingLOKInvalidateTiles(), so just make sure the timer is active.
+ startTimer();
+}
+
void CallbackFlushHandler::queue(const int type, const char* data)
{
CallbackData callbackData(data);
@@ -1514,6 +1690,7 @@ void CallbackFlushHandler::queue(const int type, CallbackData& aCallbackData)
type != LOK_CALLBACK_TEXT_SELECTION &&
type != LOK_CALLBACK_TEXT_SELECTION_START &&
type != LOK_CALLBACK_TEXT_SELECTION_END &&
+ type != LOK_CALLBACK_MEDIA_SHAPE &&
type != LOK_CALLBACK_REFERENCE_MARKS)
{
SAL_INFO("lok", "Skipping while painting [" << type << "]: [" << aCallbackData.getPayload() << "].");
@@ -1766,14 +1943,15 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
{
auto pos2 = toQueue2(pos);
const RectangleAndPart& rcOld = pos2->getRectangleAndPart();
- if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart))
+ if (rcOld.isInfinite() && (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) &&
+ (rcOld.m_nMode == rcNew.m_nMode))
{
SAL_INFO("lok", "Skipping queue [" << type << "]: [" << aCallbackData.getPayload()
<< "] since all tiles need to be invalidated.");
return true;
}
- if (rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart)
+ if ((rcOld.m_nPart == -1 || rcOld.m_nPart == rcNew.m_nPart) && (rcOld.m_nMode == rcNew.m_nMode))
{
// If fully overlapping.
if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle))
@@ -1791,7 +1969,8 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
<< "] so removing all with part " << rcNew.m_nPart << ".");
removeAll(LOK_CALLBACK_INVALIDATE_TILES, [&rcNew](const CallbackData& elemData) {
// Remove exiting if new is all-encompassing, or if of the same part.
- return (rcNew.m_nPart == -1 || rcNew.m_nPart == elemData.getRectangleAndPart().m_nPart);
+ return ((rcNew.m_nPart == -1 || rcNew.m_nPart == elemData.getRectangleAndPart().m_nPart)
+ && (rcNew.m_nMode == elemData.getRectangleAndPart().m_nMode));
});
}
else
@@ -1801,7 +1980,8 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
SAL_INFO("lok", "Have [" << type << "]: [" << aCallbackData.getPayload() << "] so merging overlapping.");
removeAll(LOK_CALLBACK_INVALIDATE_TILES,[&rcNew](const CallbackData& elemData) {
const RectangleAndPart& rcOld = elemData.getRectangleAndPart();
- if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 && rcOld.m_nPart != rcNew.m_nPart)
+ if (rcNew.m_nPart != -1 && rcOld.m_nPart != -1 &&
+ (rcOld.m_nPart != rcNew.m_nPart || rcOld.m_nMode != rcNew.m_nMode))
{
SAL_INFO("lok", "Nothing to merge between new: "
<< rcNew.toString() << ", and old: " << rcOld.toString());
@@ -1813,7 +1993,7 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
// Don't merge unless fully overlapped.
SAL_INFO("lok", "New " << rcNew.toString() << " has " << rcOld.toString()
<< "?");
- if (rcNew.m_aRectangle.Contains(rcOld.m_aRectangle))
+ if (rcNew.m_aRectangle.Contains(rcOld.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
{
SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
<< rcOld.toString() << ".");
@@ -1825,7 +2005,7 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
// Don't merge unless fully overlapped.
SAL_INFO("lok", "Old " << rcOld.toString() << " has " << rcNew.toString()
<< "?");
- if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle))
+ if (rcOld.m_aRectangle.Contains(rcNew.m_aRectangle) && rcOld.m_nMode == rcNew.m_nMode)
{
SAL_INFO("lok", "New " << rcNew.toString() << " engulfs old "
<< rcOld.toString() << ".");
@@ -1836,7 +2016,7 @@ bool CallbackFlushHandler::processInvalidateTilesEvent(int type, CallbackData& a
{
const tools::Rectangle rcOverlap
= rcNew.m_aRectangle.GetIntersection(rcOld.m_aRectangle);
- const bool bOverlap = !rcOverlap.IsEmpty();
+ const bool bOverlap = !rcOverlap.IsEmpty() && rcOld.m_nMode == rcNew.m_nMode;
SAL_INFO("lok", "Merging " << rcNew.toString() << " & " << rcOld.toString()
<< " => " << rcOverlap.toString()
<< " Overlap: " << bOverlap);
@@ -2106,11 +2286,15 @@ void CallbackFlushHandler::enqueueUpdatedTypes()
void CallbackFlushHandler::enqueueUpdatedType( int type, const SfxViewShell* viewShell, int viewId )
{
- bool ignore = false;
- OString payload = viewShell->getLOKPayload( type, viewId, &ignore );
- if(ignore)
+ if (type == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR)
+ {
+ if (const SfxViewShell* viewShell2 = LokStarMathHelper(viewShell).GetSmViewShell())
+ viewShell = viewShell2;
+ }
+ std::optional<OString> payload = viewShell->getLOKPayload( type, viewId );
+ if(!payload)
return; // No actual payload to send.
- CallbackData callbackData(payload.getStr(), viewId);
+ CallbackData callbackData(payload->getStr(), viewId);
m_queue1.emplace_back(type);
m_queue2.emplace_back(callbackData);
SAL_INFO("lok", "Queued updated [" << type << "]: [" << callbackData.getPayload()
@@ -2330,6 +2514,9 @@ static bool lo_signDocument(LibreOfficeKit* pThis,
const unsigned char* pPrivateKeyBinary,
const int nPrivateKeyBinarySize);
+static char* lo_extractRequest(LibreOfficeKit* pThis,
+ const char* pFilePath);
+
static void lo_runLoop(LibreOfficeKit* pThis,
LibreOfficeKitPollCallback pPollCallback,
LibreOfficeKitWakeCallback pWakeCallback,
@@ -2341,6 +2528,8 @@ static void lo_sendDialogEvent(LibreOfficeKit* pThis,
static void lo_setOption(LibreOfficeKit* pThis, const char* pOption, const char* pValue);
+static void lo_dumpState(LibreOfficeKit* pThis, const char* pOptions, char** pState);
+
LibLibreOffice_Impl::LibLibreOffice_Impl()
: m_pOfficeClass( gOfficeClass.lock() )
, maThread(nullptr)
@@ -2367,6 +2556,8 @@ LibLibreOffice_Impl::LibLibreOffice_Impl()
m_pOfficeClass->runLoop = lo_runLoop;
m_pOfficeClass->sendDialogEvent = lo_sendDialogEvent;
m_pOfficeClass->setOption = lo_setOption;
+ m_pOfficeClass->dumpState = lo_dumpState;
+ m_pOfficeClass->extractRequest = lo_extractRequest;
gOfficeClass = m_pOfficeClass;
}
@@ -2492,6 +2683,14 @@ static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis,
if (!aLanguage.isEmpty() && isValidLangTag)
{
+ static bool isLoading = true;
+ if (isLoading)
+ {
+ // Capture the language used to load the document.
+ SfxLokHelper::setLoadLanguage(aLanguage);
+ isLoading = false;
+ }
+
SfxLokHelper::setDefaultLanguage(aLanguage);
// Set the LOK language tag, used for dialog tunneling.
comphelper::LibreOfficeKit::setLanguageTag(LanguageTag(aLanguage));
@@ -2505,6 +2704,27 @@ static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis,
SvNumberFormatter::resetTheCurrencyTable();
}
+ // Set the timezone, if not empty.
+ const OUString aTimezone = extractParameter(aOptions, u"Timezone");
+ if (!aTimezone.isEmpty())
+ {
+ SfxLokHelper::setDefaultTimezone(true, aTimezone);
+ }
+ else
+ {
+ // Default to the TZ envar, if set.
+ const char* tz = ::getenv("TZ");
+ if (tz)
+ {
+ SfxLokHelper::setDefaultTimezone(true,
+ OStringToOUString(tz, RTL_TEXTENCODING_UTF8));
+ }
+ else
+ {
+ SfxLokHelper::setDefaultTimezone(false, OUString());
+ }
+ }
+
const OUString aDeviceFormFactor = extractParameter(aOptions, u"DeviceFormFactor");
SfxLokHelper::setDeviceFormFactor(aDeviceFormFactor);
@@ -2547,10 +2767,15 @@ static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis,
document::MacroExecMode::NEVER_EXECUTE;
#endif
+ // set AsTemplate explicitly false to be able to load template files
+ // as regular files, otherwise we cannot save them; it will try
+ // to bring saveas dialog which cannot work with LOK case
uno::Sequence<css::beans::PropertyValue> aFilterOptions{
comphelper::makePropertyValue("FilterOptions", sFilterOptions),
comphelper::makePropertyValue("InteractionHandler", xInteraction),
- comphelper::makePropertyValue("MacroExecutionMode", nMacroExecMode)
+ comphelper::makePropertyValue("MacroExecutionMode", nMacroExecMode),
+ comphelper::makePropertyValue("AsTemplate", false),
+ comphelper::makePropertyValue("Silent", !aBatch.isEmpty())
};
/* TODO
@@ -2559,6 +2784,8 @@ static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis,
aFilterOptions[3].Value <<= nUpdateDoc;
*/
+ OutputDevice::StartTrackingFontMappingUse();
+
const int nThisDocumentId = nDocumentIdCounter++;
SfxViewShell::SetCurrentDocId(ViewShellDocId(nThisDocumentId));
uno::Reference<lang::XComponent> xComponent = xComponentLoader->loadComponentFromURL(
@@ -2582,6 +2809,104 @@ static LibreOfficeKitDocument* lo_documentLoadWithOptions(LibreOfficeKit* pThis,
int nState = doc_getSignatureState(pDocument);
pLib->mpCallback(LOK_CALLBACK_SIGNATURE_STATUS, OString::number(nState).getStr(), pLib->mpCallbackData);
}
+
+ auto aFontMappingUseData = OutputDevice::FinishTrackingFontMappingUse();
+
+ if (aFontMappingUseData.size() > 0)
+ {
+ SAL_INFO("lok.fontsubst", "================ Original substitutions:");
+ for (const auto &i : aFontMappingUseData)
+ {
+ SAL_INFO("lok.fontsubst", i.mOriginalFont);
+ for (const auto &j : i.mUsedFonts)
+ SAL_INFO("lok.fontsubst", " " << j);
+ }
+ }
+
+ // Filter out font substitutions that actually aren't any substitutions, like "Liberation
+ // Serif" -> "Liberation Serif/Regular". If even one of the "substitutions" of a font is to
+ // the same font, don't count that as a missing font.
+
+ aFontMappingUseData.erase
+ (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
+ [](OutputDevice::FontMappingUseItem x)
+ {
+ // If the original font had an empty style and one of its
+ // replacement fonts has the same family name, we assume the font is
+ // present. The root problem here is that the code that collects
+ // font substitutions tends to get just empty styles for the font
+ // that is being substituted, as vcl::Font::GetStyleName() tends to
+ // return an empty string. (Italicness is instead indicated by what
+ // vcl::Font::GetItalic() returns and boldness by what
+ // vcl::Font::GetWeight() returns.)
+
+ if (x.mOriginalFont.indexOf('/') == -1)
+ for (const auto &j : x.mUsedFonts)
+ if (j == x.mOriginalFont ||
+ j.startsWith(OUStringConcatenation(x.mOriginalFont + "/")))
+ return true;
+
+ return false;
+ }),
+ aFontMappingUseData.end());
+
+ // Filter out substitutions where a proprietary font has been substituted by a
+ // metric-compatible one. Obviously this is just a heuristic and implemented only for some
+ // well-known cases.
+
+ aFontMappingUseData.erase
+ (std::remove_if(aFontMappingUseData.begin(), aFontMappingUseData.end(),
+ [](OutputDevice::FontMappingUseItem x)
+ {
+ // Again, handle only cases where the original font does not include
+ // a style. Unclear whether there ever will be a style part included
+ // in the mOriginalFont.
+
+ if (x.mOriginalFont.indexOf('/') == -1)
+ for (const auto &j : x.mUsedFonts)
+ if ((x.mOriginalFont == "Arial" &&
+ j.startsWith("Liberation Sans/")) ||
+ (x.mOriginalFont == "Times New Roman" &&
+ j.startsWith("Liberation Serif/")) ||
+ (x.mOriginalFont == "Courier New" &&
+ j.startsWith("Liberation Mono/")) ||
+ (x.mOriginalFont == "Arial Narrow" &&
+ j.startsWith("Liberation Sans Narrow/")) ||
+ (x.mOriginalFont == "Cambria" &&
+ j.startsWith("Caladea/")) ||
+ (x.mOriginalFont == "Calibri" &&
+ j.startsWith("Carlito/")) ||
+ (x.mOriginalFont == "Palatino Linotype" &&
+ j.startsWith("P052/")) ||
+ // Perhaps a risky heuristic? If some glyphs from Symbol
+ // have been mapped to ones in OpenSymbol, don't warn
+ // that Symbol is missing.
+ (x.mOriginalFont == "Symbol" &&
+ j.startsWith("OpenSymbol/")))
+ {
+ return true;
+ }
+
+ return false;
+ }),
+ aFontMappingUseData.end());
+
+ if (aFontMappingUseData.size() > 0)
+ {
+ SAL_INFO("lok.fontsubst", "================ Pruned substitutions:");
+ for (const auto &i : aFontMappingUseData)
+ {
+ SAL_INFO("lok.fontsubst", i.mOriginalFont);
+ for (const auto &j : i.mUsedFonts)
+ SAL_INFO("lok.fontsubst", " " << j);
+ }
+ }
+
+ for (std::size_t i = 0; i < aFontMappingUseData.size(); ++i)
+ {
+ pDocument->maFontsMissing.insert(aFontMappingUseData[i].mOriginalFont);
+ }
+
return pDocument;
}
catch (const uno::Exception& exception)
@@ -2750,6 +3075,69 @@ static bool lo_signDocument(LibreOfficeKit* /*pThis*/,
return true;
}
+
+static char* lo_extractRequest(LibreOfficeKit* /*pThis*/, const char* pFilePath)
+{
+ uno::Reference<frame::XDesktop2> xComponentLoader = frame::Desktop::create(xContext);
+ uno::Reference< css::lang::XComponent > xComp;
+ OUString aURL(getAbsoluteURL(pFilePath));
+ OUString result;
+ if (!aURL.isEmpty())
+ {
+ if (xComponentLoader.is())
+ {
+ try
+ {
+ uno::Sequence<css::beans::PropertyValue> aFilterOptions(comphelper::InitPropertySequence(
+ {
+ {"Hidden", css::uno::Any(true)},
+ {"ReadOnly", css::uno::Any(true)}
+ }));
+ xComp = xComponentLoader->loadComponentFromURL( aURL, "_blank", 0, aFilterOptions );
+ }
+ catch ( const lang::IllegalArgumentException& ex )
+ {
+ SAL_WARN("lok", "lo_extractRequest: IllegalArgumentException: " << ex.Message);
+ result = "{ }";
+ return convertOUString(result);
+ }
+ catch (...)
+ {
+ SAL_WARN("lok", "lo_extractRequest: Exception on loadComponentFromURL, url= " << aURL);
+ result = "{ }";
+ return convertOUString(result);
+ }
+
+ if (xComp.is())
+ {
+ uno::Reference< document::XLinkTargetSupplier > xLTS( xComp, uno::UNO_QUERY );
+
+ if( xLTS.is() )
+ {
+ OUStringBuffer jsonText;
+ jsonText.append("{ \"Targets\": { ");
+ bool lastParentheses = extractLinks(xLTS->getLinks(), false, jsonText);
+ jsonText.append("} }");
+ if (!lastParentheses)
+ jsonText.append(" }");
+
+ OUString res(jsonText.makeStringAndClear());
+ return convertOUString(res);
+ }
+ xComp->dispose();
+ }
+ else
+ {
+ result = "{ }";
+ return convertOUString(result);
+ }
+
+ }
+ }
+ result = "{ }";
+ return convertOUString(result);
+}
+
static void lo_registerCallback (LibreOfficeKit* pThis,
LibreOfficeKitCallback pCallback,
void* pData)
@@ -3058,6 +3446,8 @@ static void doc_iniUnoCommands ()
OUString(".uno:NumberFormatCurrency"),
OUString(".uno:NumberFormatPercent"),
OUString(".uno:NumberFormatDecimal"),
+ OUString(".uno:NumberFormatIncDecimals"),
+ OUString(".uno:NumberFormatDecDecimals"),
OUString(".uno:NumberFormatDate"),
OUString(".uno:EditHeaderAndFooter"),
OUString(".uno:FrameLineColor"),
@@ -3138,6 +3528,7 @@ static void doc_iniUnoCommands ()
OUString(".uno:InsertAuthoritiesEntry"),
OUString(".uno:InsertMultiIndex"),
OUString(".uno:InsertField"),
+ OUString(".uno:PageNumberWizard"),
OUString(".uno:InsertPageNumberField"),
OUString(".uno:InsertPageCountField"),
OUString(".uno:InsertDateField"),
@@ -3176,7 +3567,13 @@ static void doc_iniUnoCommands ()
OUString(".uno:UngroupSparklines"),
OUString(".uno:FormatSparklineMenu"),
OUString(".uno:Protect"),
- OUString(".uno:UnsetCellsReadOnly")
+ OUString(".uno:UnsetCellsReadOnly"),
+ OUString(".uno:ContentControlProperties"),
+ OUString(".uno:InsertCheckboxContentControl"),
+ OUString(".uno:InsertContentControl"),
+ OUString(".uno:InsertDateContentControl"),
+ OUString(".uno:InsertDropdownContentControl"),
+ OUString(".uno:InsertPictureContentControl")
};
util::URL aCommandURL;
@@ -3198,6 +3595,20 @@ static void doc_iniUnoCommands ()
return;
}
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ if (!xSEInitializer.is())
+ {
+ SAL_WARN("lok", "iniUnoCommands: XSEInitializer is not available");
+ return;
+ }
+
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext =
+ xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ {
+ SAL_WARN("lok", "iniUnoCommands: failed to create security context");
+ }
+
SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool(pViewFrame);
uno::Reference<util::XURLTransformer> xParser(util::URLTransformer::create(xContext));
@@ -3449,6 +3860,23 @@ static void doc_setPartMode(LibreOfficeKitDocument* pThis,
}
}
+static int doc_getEditMode(LibreOfficeKitDocument* pThis)
+{
+ comphelper::ProfileZone aZone("doc_getEditMode");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return 0;
+ }
+
+ return pDoc->getEditMode();
+}
+
static void doc_paintTile(LibreOfficeKitDocument* pThis,
unsigned char* pBuffer,
const int nCanvasWidth, const int nCanvasHeight,
@@ -3471,7 +3899,7 @@ static void doc_paintTile(LibreOfficeKitDocument* pThis,
return;
}
-#if defined(UNX) && !defined(MACOSX)
+#if defined(UNX) && !defined(MACOSX) || defined(_WIN32)
// Painting of zoomed or HiDPI spreadsheets is special, we actually draw everything at 100%,
// and only set cairo's (or CoreGraphic's, in the iOS case) scale factor accordingly, so that
@@ -3508,6 +3936,36 @@ static void doc_paintTile(LibreOfficeKitDocument* pThis,
pDevice->DrawRect(aRect);
pDevice->Pop();
}
+
+#ifdef _WIN32
+ // pBuffer was not used there
+ pDevice->EnableMapMode(false);
+ BitmapEx aBmpEx = pDevice->GetBitmapEx({ 0, 0 }, { nCanvasWidth, nCanvasHeight });
+ Bitmap aBmp = aBmpEx.GetBitmap();
+ Bitmap aAlpha = aBmpEx.GetAlpha();
+ Bitmap::ScopedReadAccess sraBmp(aBmp);
+ Bitmap::ScopedReadAccess sraAlpha(aAlpha);
+
+ assert(sraBmp->Height() == nCanvasHeight);
+ assert(sraBmp->Width() == nCanvasWidth);
+ assert(!sraAlpha || sraBmp->Height() == sraAlpha->Height());
+ assert(!sraAlpha || sraBmp->Width() == sraAlpha->Width());
+ auto p = pBuffer;
+ for (tools::Long y = 0; y < sraBmp->Height(); ++y)
+ {
+ Scanline dataBmp = sraBmp->GetScanline(y);
+ Scanline dataAlpha = sraAlpha ? sraAlpha->GetScanline(y) : nullptr;
+ for (tools::Long x = 0; x < sraBmp->Width(); ++x)
+ {
+ BitmapColor color = sraBmp->GetPixelFromData(dataBmp, x);
+ sal_uInt8 alpha = dataAlpha ? sraAlpha->GetPixelFromData(dataAlpha, x).GetBlue() : 255;
+ *p++ = color.GetBlue();
+ *p++ = color.GetGreen();
+ *p++ = color.GetRed();
+ *p++ = alpha;
+ }
+ }
+#endif
#endif
#else
@@ -3548,6 +4006,7 @@ static void doc_paintTileToCGContext(LibreOfficeKitDocument* pThis,
static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
unsigned char* pBuffer,
const int nPart,
+ const int nMode,
const int nCanvasWidth, const int nCanvasHeight,
const int nTilePosX, const int nTilePosY,
const int nTileWidth, const int nTileHeight)
@@ -3557,7 +4016,7 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
SolarMutexGuard aGuard;
SetLastExceptionMsg();
- SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " ["
+ SAL_INFO( "lok.tiledrendering", "paintPartTile: painting @ " << nPart << " : " << nMode << " ["
<< nTileWidth << "x" << nTileHeight << "]@("
<< nTilePosX << ", " << nTilePosY << ") to ["
<< nCanvasWidth << "x" << nCanvasHeight << "]px" );
@@ -3565,6 +4024,13 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
int nOrigViewId = doc_getView(pThis);
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return;
+ }
+
if (nOrigViewId < 0)
{
// tile painting always needs a SfxViewShell::Current(), but actually
@@ -3595,14 +4061,19 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
{
// Text documents have a single coordinate system; don't change part.
int nOrigPart = 0;
- const bool isText = (doc_getDocumentType(pThis) == LOK_DOCTYPE_TEXT);
+ const int aType = doc_getDocumentType(pThis);
+ const bool isText = (aType == LOK_DOCTYPE_TEXT);
+ const bool isCalc = (aType == LOK_DOCTYPE_SPREADSHEET);
+ int nOrigEditMode = 0;
+ bool bPaintTextEdit = true;
int nViewId = nOrigViewId;
- int nLastNonEditorView = nViewId;
+ int nLastNonEditorView = -1;
+ int nViewMatchingMode = -1;
if (!isText)
{
// Check if just switching to another view is enough, that has
// less side-effects.
- if (nPart != doc_getPart(pThis))
+ if (nPart != doc_getPart(pThis) || nMode != pDoc->getEditMode())
{
SfxViewShell* pViewShell = SfxViewShell::GetFirst();
while (pViewShell)
@@ -3612,23 +4083,48 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
if (!bIsInEdit)
nLastNonEditorView = pViewShell->GetViewShellId().get();
- if (pViewShell->getPart() == nPart && !bIsInEdit)
+ if (pViewShell->getPart() == nPart &&
+ pViewShell->getEditMode() == nMode &&
+ !bIsInEdit)
{
nViewId = pViewShell->GetViewShellId().get();
+ nViewMatchingMode = nViewId;
nLastNonEditorView = nViewId;
doc_setView(pThis, nViewId);
break;
}
+ else if (pViewShell->getEditMode() == nMode && !bIsInEdit)
+ {
+ nViewMatchingMode = pViewShell->GetViewShellId().get();
+ }
+
pViewShell = SfxViewShell::GetNext(*pViewShell);
}
}
- // if not found view with correct part - at least avoid rendering active textbox
+ // if not found view with correct part
+ // - at least avoid rendering active textbox, This is for Impress.
+ // - prefer view with the same mode
SfxViewShell* pCurrentViewShell = SfxViewShell::Current();
- if (pCurrentViewShell && pCurrentViewShell->GetDrawView() &&
+ if (nViewMatchingMode >= 0 && nViewMatchingMode != nViewId)
+ {
+ nViewId = nViewMatchingMode;
+ doc_setView(pThis, nViewId);
+ }
+ else if (!isCalc && nLastNonEditorView >= 0 && nLastNonEditorView != nViewId &&
+ pCurrentViewShell && pCurrentViewShell->GetDrawView() &&
pCurrentViewShell->GetDrawView()->GetTextEditOutliner())
{
- doc_setView(pThis, nLastNonEditorView);
+ nViewId = nLastNonEditorView;
+ doc_setView(pThis, nViewId);
+ }
+
+ // Disable callbacks while we are painting - after setting the view
+ if (nViewId != nOrigViewId && nViewId >= 0)
+ {
+ const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
+ if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
+ handlerIt->second->disableCallbacks();
}
nOrigPart = doc_getPart(pThis);
@@ -3636,17 +4132,44 @@ static void doc_paintPartTile(LibreOfficeKitDocument* pThis,
{
doc_setPartImpl(pThis, nPart, false);
}
+
+ nOrigEditMode = pDoc->getEditMode();
+ if (nOrigEditMode != nMode)
+ {
+ SfxLokHelper::setEditMode(nMode, pDoc);
+ }
+
+ bPaintTextEdit = (nPart == nOrigPart && nMode == nOrigEditMode);
+ pDoc->setPaintTextEdit(bPaintTextEdit);
}
doc_paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight);
- if (!isText && nPart != nOrigPart)
- {
- doc_setPartImpl(pThis, nOrigPart, false);
- }
- if (!isText && nViewId != nOrigViewId)
+ if (!isText)
{
- doc_setView(pThis, nOrigViewId);
+ pDoc->setPaintTextEdit(true);
+
+ if (nMode != nOrigEditMode)
+ {
+ SfxLokHelper::setEditMode(nOrigEditMode, pDoc);
+ }
+
+ if (nPart != nOrigPart)
+ {
+ doc_setPartImpl(pThis, nOrigPart, false);
+ }
+
+ if (nViewId != nOrigViewId)
+ {
+ if (nViewId >= 0)
+ {
+ const auto handlerIt = pDocument->mpCallbackFlushHandlers.find(nViewId);
+ if (handlerIt != pDocument->mpCallbackFlushHandlers.end())
+ handlerIt->second->enableCallbacks();
+ }
+
+ doc_setView(pThis, nOrigViewId);
+ }
}
}
catch (const std::exception&)
@@ -3690,6 +4213,29 @@ static void doc_getDocumentSize(LibreOfficeKitDocument* pThis,
}
}
+static void doc_getDataArea(LibreOfficeKitDocument* pThis,
+ long nTab,
+ long* pCol,
+ long* pRow)
+{
+ comphelper::ProfileZone aZone("doc_getDataArea");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (pDoc)
+ {
+ Size aDocumentSize = pDoc->getDataArea(nTab);
+ *pCol = aDocumentSize.Width();
+ *pRow = aDocumentSize.Height();
+ }
+ else
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ }
+}
+
static void doc_initializeForRendering(LibreOfficeKitDocument* pThis,
const char* pArguments)
{
@@ -3759,6 +4305,23 @@ static void doc_registerCallback(LibreOfficeKitDocument* pThis,
pDocument->mpCallbackFlushHandlers[nView]->setViewId(pViewShell->GetViewShellId().get());
pViewShell->setLibreOfficeKitViewCallback(pDocument->mpCallbackFlushHandlers[nView].get());
}
+
+ if (pDocument->maFontsMissing.size() != 0)
+ {
+ std::string sPayload = "{ \"fontsmissing\": [ ";
+ bool bFirst = true;
+ for (const auto &f : pDocument->maFontsMissing)
+ {
+ if (bFirst)
+ bFirst = false;
+ else
+ sPayload += ", ";
+ sPayload += "\"" + std::string(f.toUtf8().getStr()) + "\"";
+ }
+ sPayload += " ] }";
+ pCallback(LOK_CALLBACK_FONTS_MISSING, sPayload.c_str(), pData);
+ pDocument->maFontsMissing.clear();
+ }
}
else
{
@@ -3908,12 +4471,12 @@ static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWi
// backspace
if (nLOKWindowId == 0)
{
- KeyEvent aEvt(8, 1283);
+ KeyEvent aEvt(8, KEY_BACKSPACE);
for (int i = 0; i < nCharBefore; ++i)
pWindow->KeyInput(aEvt);
}
else
- SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, 1283, nCharBefore - 1);
+ SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 8, KEY_BACKSPACE, nCharBefore - 1);
}
if (nCharAfter > 0)
@@ -3921,12 +4484,12 @@ static void doc_removeTextContext(LibreOfficeKitDocument* pThis, unsigned nLOKWi
// delete (forward)
if (nLOKWindowId == 0)
{
- KeyEvent aEvt(46, 1286);
+ KeyEvent aEvt(46, KEY_DELETE);
for (int i = 0; i < nCharAfter; ++i)
pWindow->KeyInput(aEvt);
}
else
- SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, 1286, nCharAfter - 1);
+ SfxLokHelper::postKeyEventAsync(pWindow, LOK_KEYEVENT_KEYINPUT, 46, KEY_DELETE, nCharAfter - 1);
}
}
@@ -4037,13 +4600,18 @@ namespace {
*/
class DispatchResultListener : public cppu::WeakImplHelper<css::frame::XDispatchResultListener>
{
- OString maCommand; ///< Command for which this is the result.
- std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call.
+ const OString maCommand; ///< Command for which this is the result.
+ const std::shared_ptr<CallbackFlushHandler> mpCallback; ///< Callback to call.
+ const std::chrono::steady_clock::time_point mSaveTime; //< The time we started saving.
+ const bool mbWasModified; //< Whether or not the document was modified before saving.
public:
- DispatchResultListener(const char* pCommand, std::shared_ptr<CallbackFlushHandler> const & pCallback)
+ DispatchResultListener(const char* pCommand,
+ std::shared_ptr<CallbackFlushHandler> const& pCallback)
: maCommand(pCommand)
, mpCallback(pCallback)
+ , mSaveTime(std::chrono::steady_clock::now())
+ , mbWasModified(SfxObjectShell::Current()->IsModified())
{
assert(mpCallback);
}
@@ -4060,6 +4628,16 @@ public:
}
unoAnyToJson(aJson, "result", rEvent.Result);
+ aJson.put("wasModified", mbWasModified);
+
+ const auto saveTime = std::chrono::time_point_cast<std::chrono::microseconds>(mSaveTime);
+ aJson.put("startUnixTimeMics",
+ static_cast<sal_Int64>(saveTime.time_since_epoch().count()));
+
+ const auto saveDuration = std::chrono::duration_cast<std::chrono::microseconds>(
+ std::chrono::steady_clock::now() - mSaveTime);
+ aJson.put("saveDurationMics", static_cast<sal_Int64>(saveDuration.count()));
+
mpCallback->queue(LOK_CALLBACK_UNO_COMMAND_RESULT, aJson.extractData());
}
@@ -4149,13 +4727,62 @@ static void lo_setOption(LibreOfficeKit* /*pThis*/, const char *pOption, const c
else
sal_detail_set_log_selector(pCurrentSalLogOverride);
}
+#ifdef LINUX
else if (strcmp(pOption, "addfont") == 0)
{
+ if (memcmp(pValue, "file://", 7) == 0)
+ pValue += 7;
+
+ int fd = open(pValue, O_RDONLY);
+ if (fd == -1)
+ {
+ std::cerr << "Could not open font file '" << pValue << "': " << strerror(errno) << std::endl;
+ return;
+ }
+
+ OUString sMagicFileName = "file:///:FD:/" + OUString::number(fd);
+
OutputDevice *pDevice = Application::GetDefaultDevice();
OutputDevice::ImplClearAllFontData(false);
- pDevice->AddTempDevFont(OUString::fromUtf8(pValue), "");
+ pDevice->AddTempDevFont(sMagicFileName, "");
OutputDevice::ImplRefreshAllFontData(false);
}
+#endif
+}
+
+static void lo_dumpState (LibreOfficeKit* pThis, const char* /* pOptions */, char** pState)
+{
+ if (!pState)
+ return;
+
+ // NB. no SolarMutexGuard since this may be caused in some extremis / deadlock
+ SetLastExceptionMsg();
+
+ *pState = nullptr;
+ OStringBuffer aState(4096*256);
+
+ LibLibreOffice_Impl* pLib = static_cast<LibLibreOffice_Impl*>(pThis);
+
+ pLib->dumpState(aState);
+
+ OString aStr = aState.makeStringAndClear();
+ *pState = strdup(aStr.getStr());
+}
+
+void LibLibreOffice_Impl::dumpState(rtl::OStringBuffer &rState)
+{
+ rState.append("LibreOfficeKit state:");
+ rState.append("\n\tLastExceptionMsg:\t");
+ rState.append(rtl::OUStringToOString(maLastExceptionMsg, RTL_TEXTENCODING_UTF8));
+ rState.append("\n\tUnipoll:\t");
+ rState.append(vcl::lok::isUnipoll() ? "yes" : "no: events on thread");
+ rState.append("\n\tOptionalFeatures:\t0x");
+ rState.append(static_cast<sal_Int64>(mOptionalFeatures), 16);
+ rState.append("\n\tCallbackData:\t0x");
+ rState.append(reinterpret_cast<sal_Int64>(mpCallback), 16);
+ // TODO: dump mInteractionMap
+ SfxLokHelper::dumpState(rState);
+ vcl::lok::dumpState(rState);
}
static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pCommand, const char* pArguments, bool bNotifyWhenFinished)
@@ -4344,7 +4971,13 @@ static void doc_postUnoCommand(LibreOfficeKitDocument* pThis, const char* pComma
aChartDispatcher->dispatch(aCommandURL, comphelper::containerToSequence(aPropertyValuesVector));
return;
}
- else if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
+ if (LokStarMathHelper aMathHelper(SfxViewShell::Current());
+ aMathHelper.GetGraphicWindow() && aCommand != ".uno:Save")
+ {
+ aMathHelper.Dispatch(aCommand, comphelper::containerToSequence(aPropertyValuesVector));
+ return;
+ }
+ if (bNotifyWhenFinished && pDocument->mpCallbackFlushHandlers.count(nView))
{
bResult = comphelper::dispatchCommand(aCommand, comphelper::containerToSequence(aPropertyValuesVector),
new DispatchResultListener(pCommand, pDocument->mpCallbackFlushHandlers[nView]));
@@ -4510,7 +5143,7 @@ static bool encodeImageAsHTML(
// Encode in base64.
auto aSeq = Sequence<sal_Int8>(reinterpret_cast<const sal_Int8*>(aRet.getStr()),
aRet.getLength());
- OUStringBuffer aBase64Data;
+ OStringBuffer aBase64Data;
comphelper::Base64::encode(aBase64Data, aSeq);
// Embed in HTML.
@@ -4521,7 +5154,7 @@ static bool encodeImageAsHTML(
+ getGenerator().toUtf8()
+ "\"/>"
"</head><body><img src=\"data:" + aMimeType + ";base64,"
- + aBase64Data.makeStringAndClear().toUtf8() + "\"/></body></html>";
+ + aBase64Data + "\"/></body></html>";
return true;
}
@@ -4957,12 +5590,28 @@ static void doc_resetSelection(LibreOfficeKitDocument* pThis)
pDoc->resetSelection();
}
+static void addLocale(boost::property_tree::ptree& rValues, css::lang::Locale const & rLocale)
+{
+ boost::property_tree::ptree aChild;
+ OUString sLanguage;
+ const LanguageTag aLanguageTag( rLocale );
+ sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
+ if (sLanguage.endsWith("}"))
+ return;
+
+ sLanguage += ";" + aLanguageTag.getBcp47(false);
+ aChild.put("", sLanguage.toUtf8());
+ rValues.push_back(std::make_pair("", aChild));
+}
+
static char* getLanguages(const char* pCommand)
{
css::uno::Sequence< css::lang::Locale > aLocales;
+ css::uno::Sequence< css::lang::Locale > aGrammarLocales;
if (xContext.is())
{
+ // SpellChecker
css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv = css::linguistic2::LinguServiceManager::create(xContext);
if (xLangSrv.is())
{
@@ -4970,24 +5619,26 @@ static char* getLanguages(const char* pCommand)
if (xSpell.is())
aLocales = xSpell->getLocales();
}
+
+ // LanguageTool
+ SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+ if (rLanguageOpts.getEnabled())
+ {
+ uno::Reference< linguistic2::XProofreader > xGC(
+ xContext->getServiceManager()->createInstanceWithContext("org.openoffice.lingu.LanguageToolGrammarChecker", xContext),
+ uno::UNO_QUERY_THROW );
+ uno::Reference< linguistic2::XSupportedLocales > xSuppLoc( xGC, uno::UNO_QUERY_THROW );
+ aGrammarLocales = xSuppLoc->getLocales();
+ }
}
boost::property_tree::ptree aTree;
aTree.put("commandName", pCommand);
boost::property_tree::ptree aValues;
- boost::property_tree::ptree aChild;
- OUString sLanguage;
- for ( css::lang::Locale const & locale : std::as_const(aLocales) )
- {
- const LanguageTag aLanguageTag( locale );
- sLanguage = SvtLanguageTable::GetLanguageString(aLanguageTag.getLanguageType());
- if (sLanguage.startsWith("{") && sLanguage.endsWith("}"))
- continue;
-
- sLanguage += ";" + aLanguageTag.getBcp47(false);
- aChild.put("", sLanguage.toUtf8());
- aValues.push_back(std::make_pair("", aChild));
- }
+ for ( css::lang::Locale const & rLocale : std::as_const(aLocales) )
+ addLocale(aValues, rLocale);
+ for ( css::lang::Locale const & rLocale : std::as_const(aGrammarLocales) )
+ addLocale(aValues, rLocale);
aTree.add_child("commandValues", aValues);
std::stringstream aStream;
boost::property_tree::write_json(aStream, aTree);
@@ -5315,6 +5966,13 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
static constexpr OStringLiteral aCellCursor(".uno:CellCursor");
static constexpr OStringLiteral aFontSubset(".uno:FontSubset&name=");
+ ITiledRenderable* pDoc = getTiledRenderable(pThis);
+ if (!pDoc)
+ {
+ SetLastExceptionMsg("Document doesn't support tiled rendering");
+ return nullptr;
+ }
+
if (!strcmp(pCommand, ".uno:LanguageStatus"))
{
return getLanguages(pCommand);
@@ -5357,13 +6015,6 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
}
else if (aCommand.startsWith(aViewRowColumnHeaders))
{
- ITiledRenderable* pDoc = getTiledRenderable(pThis);
- if (!pDoc)
- {
- SetLastExceptionMsg("Document doesn't support tiled rendering");
- return nullptr;
- }
-
tools::Rectangle aRectangle;
if (aCommand.getLength() > aViewRowColumnHeaders.getLength())
{
@@ -5409,13 +6060,6 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
}
else if (aCommand.startsWith(aSheetGeometryData))
{
- ITiledRenderable* pDoc = getTiledRenderable(pThis);
- if (!pDoc)
- {
- SetLastExceptionMsg("Document doesn't support tiled rendering");
- return nullptr;
- }
-
bool bColumns = true;
bool bRows = true;
bool bSizes = true;
@@ -5475,12 +6119,6 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
}
else if (aCommand.startsWith(aCellCursor))
{
- ITiledRenderable* pDoc = getTiledRenderable(pThis);
- if (!pDoc)
- {
- SetLastExceptionMsg("Document doesn't support tiled rendering");
- return nullptr;
- }
// Ignore command's deprecated parameters.
tools::JsonWriter aJsonWriter;
pDoc->getCellCursor(aJsonWriter);
@@ -5490,6 +6128,12 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
{
return getFontSubset(std::string_view(pCommand + aFontSubset.getLength()));
}
+ else if (pDoc->supportsCommand(INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath()))
+ {
+ tools::JsonWriter aJsonWriter;
+ pDoc->getCommandValues(aJsonWriter, aCommand);
+ return aJsonWriter.extractData();
+ }
else
{
SetLastExceptionMsg("Unknown command, no values returned");
@@ -6188,6 +6832,22 @@ static void doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const cha
pDoc->executeContentControlEvent(aMap);
}
+static void doc_setViewTimezone(SAL_UNUSED_PARAMETER LibreOfficeKitDocument* /*pThis*/, int nId,
+ const char* pTimezone)
+{
+ comphelper::ProfileZone aZone("doc_setViewTimezone");
+
+ SolarMutexGuard aGuard;
+ SetLastExceptionMsg();
+
+ // Leave the default if we get a null timezone.
+ if (pTimezone)
+ {
+ OUString sTimezone = OStringToOUString(pTimezone, RTL_TEXTENCODING_UTF8);
+ SfxLokHelper::setViewTimezone(nId, true, sTimezone);
+ }
+}
+
static char* lo_getError (LibreOfficeKit *pThis)
{
comphelper::ProfileZone aZone("lo_getError");
@@ -6282,7 +6942,8 @@ static char* lo_getVersionInfo(SAL_UNUSED_PARAMETER LibreOfficeKit* /*pThis*/)
"\"ProductName\": \"%PRODUCTNAME\", "
"\"ProductVersion\": \"%PRODUCTVERSION\", "
"\"ProductExtension\": \"%PRODUCTEXTENSION\", "
- "\"BuildId\": \"%BUILDID\" "
+ "\"BuildId\": \"%BUILDID\", "
+ "\"BuildConfig\": \"" BUILDCONFIG "\" "
"}"));
}
@@ -6400,6 +7061,8 @@ static void lo_status_indicator_callback(void *data, comphelper::LibreOfficeKit:
}
}
+void setLanguageToolConfig();
+
/// Used only by LibreOfficeKit when used by Online to pre-initialize
static void preloadData()
{
@@ -6418,6 +7081,9 @@ static void preloadData()
if(bAbort)
std::cerr << "CheckExtensionDependencies failed" << std::endl;
+ // setup LanguageTool config before spell checking init
+ setLanguageToolConfig();
+
// preload all available dictionaries
css::uno::Reference<css::linguistic2::XLinguServiceManager> xLngSvcMgr =
css::linguistic2::LinguServiceManager::create(comphelper::getProcessComponentContext());
@@ -6542,6 +7208,25 @@ static void activateNotebookbar(std::u16string_view rApp)
}
}
+void setHelpRootURL()
+{
+ const char* pHelpRootURL = ::getenv("LOK_HELP_URL");
+ if (pHelpRootURL)
+ {
+ OUString aHelpRootURL = OStringToOUString(pHelpRootURL, RTL_TEXTENCODING_UTF8);
+ try
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Help::HelpRootURL::set(aHelpRootURL, batch);
+ batch->commit();
+ }
+ catch (uno::Exception const& rException)
+ {
+ SAL_WARN("lok", "Failed to set the help root URL: " << rException.Message);
+ }
+ }
+}
+
void setCertificateDir()
{
const char* pEnvVarString = ::getenv("LO_CERTIFICATE_DATABASE_PATH");
@@ -6566,19 +7251,27 @@ void setLanguageToolConfig()
{
const char* pEnabled = ::getenv("LANGUAGETOOL_ENABLED");
const char* pBaseUrlString = ::getenv("LANGUAGETOOL_BASEURL");
- const char* pUsername = ::getenv("LANGUAGETOOL_USERNAME");
- const char* pApikey = ::getenv("LANGUAGETOOL_APIKEY");
+
if (pEnabled && pBaseUrlString)
{
+ const char* pUsername = ::getenv("LANGUAGETOOL_USERNAME");
+ const char* pApikey = ::getenv("LANGUAGETOOL_APIKEY");
+ const char* pSSLVerification = ::getenv("LANGUAGETOOL_SSL_VERIFICATION");
+ const char* pRestProtocol = ::getenv("LANGUAGETOOL_RESTPROTOCOL");
+
OUString aEnabled = OStringToOUString(pEnabled, RTL_TEXTENCODING_UTF8);
+ OUString aSSLVerification = OStringToOUString(pSSLVerification, RTL_TEXTENCODING_UTF8);
if (aEnabled != "true")
return;
OUString aBaseUrl = OStringToOUString(pBaseUrlString, RTL_TEXTENCODING_UTF8);
+ OUString aRestProtocol = pRestProtocol ? OStringToOUString(pRestProtocol, RTL_TEXTENCODING_UTF8) : "";
try
{
SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
rLanguageOpts.setBaseURL(aBaseUrl);
rLanguageOpts.setEnabled(true);
+ rLanguageOpts.setSSLVerification(aSSLVerification == "true");
+ rLanguageOpts.setRestProtocol(aRestProtocol);
if (pUsername && pApikey)
{
OUString aUsername = OStringToOUString(pUsername, RTL_TEXTENCODING_UTF8);
@@ -6586,6 +7279,31 @@ void setLanguageToolConfig()
rLanguageOpts.setUsername(aUsername);
rLanguageOpts.setApiKey(aApiKey);
}
+
+ css::uno::Reference<css::linguistic2::XLinguServiceManager2> xLangSrv =
+ css::linguistic2::LinguServiceManager::create(xContext);
+ if (xLangSrv.is())
+ {
+ css::uno::Reference<css::linguistic2::XSpellChecker> xSpell = xLangSrv->getSpellChecker();
+ if (xSpell.is())
+ {
+ Sequence<OUString> aEmpty;
+ static constexpr OUStringLiteral cSpell(SN_SPELLCHECKER);
+ Sequence<css::lang::Locale> aLocales = xSpell->getLocales();
+
+ uno::Reference<linguistic2::XProofreader> xGC(
+ xContext->getServiceManager()->createInstanceWithContext("org.openoffice.lingu.LanguageToolGrammarChecker", xContext),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<linguistic2::XSupportedLocales> xSuppLoc(xGC, uno::UNO_QUERY_THROW);
+
+ for (int itLocale = 0; itLocale < aLocales.getLength(); itLocale++)
+ {
+ // turn off spell checker if LanguageTool supports the locale already
+ if (xSuppLoc->hasLocale(aLocales[itLocale]))
+ xLangSrv->setConfiguredServices(cSpell, aLocales[itLocale], aEmpty);
+ }
+ }
+ }
}
catch(uno::Exception const& rException)
{
@@ -6594,6 +7312,28 @@ void setLanguageToolConfig()
}
}
+void setDeeplConfig()
+{
+ const char* pAPIUrlString = ::getenv("DEEPL_API_URL");
+ const char* pAuthKeyString = ::getenv("DEEPL_AUTH_KEY");
+ if (pAPIUrlString && pAuthKeyString)
+ {
+ OUString aAPIUrl = OStringToOUString(pAPIUrlString, RTL_TEXTENCODING_UTF8);
+ OUString aAuthKey = OStringToOUString(pAuthKeyString, RTL_TEXTENCODING_UTF8);
+ try
+ {
+ SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+ rDeeplOptions.setAPIUrl(aAPIUrl);
+ rDeeplOptions.setAuthKey(aAuthKey);
+ }
+ catch(uno::Exception const& rException)
+ {
+ SAL_WARN("lok", "Failed to set Deepl API settings: " << rException.Message);
+ }
+ }
+}
+
+
}
static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char* pUserProfileUrl)
@@ -6664,8 +7404,15 @@ static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char
comphelper::ProfileZone aZone("lok-init");
if (eStage == PRE_INIT)
+ {
rtl_alloc_preInit(true);
+ // Set the default timezone to the TZ envar, if set.
+ const char* tz = ::getenv("TZ");
+ SfxLokHelper::setDefaultTimezone(!!tz, tz ? OStringToOUString(tz, RTL_TEXTENCODING_UTF8)
+ : OUString());
+ }
+
if (eStage != SECOND_INIT)
comphelper::LibreOfficeKit::setActive();
@@ -6907,8 +7654,10 @@ static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const char
}
#endif
+
+ setHelpRootURL();
setCertificateDir();
- setLanguageToolConfig();
+ setDeeplConfig();
if (bNotebookbar)
{
@@ -6961,7 +7710,7 @@ int lok_preinit(const char* install_path, const char* user_profile_url)
}
SAL_JNI_EXPORT
-int lok_preinit_2(const char* install_path, const char* user_profile_url, LibLibreOffice_Impl** kit)
+int lok_preinit_2(const char* install_path, const char* user_profile_url, LibreOfficeKit** kit)
{
lok_preinit_2_called = true;
int result = lo_initialize(nullptr, install_path, user_profile_url);