desktop/source/lib/init.cxx                                       |  172 
+++++++++
 lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx |   69 +++
 lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx |    2 
 linguistic/CppunitTest_linguistic_restprotocol.mk                 |   39 ++
 linguistic/Module_linguistic.mk                                   |    7 
 linguistic/qa/restprotocol.cxx                                    |  181 
++++++++++
 sc/source/ui/docshell/docsh.cxx                                   |    3 
 sc/source/ui/view/gridwin.cxx                                     |   13 
 8 files changed, 473 insertions(+), 13 deletions(-)

New commits:
commit 6c5c4f42b52486e39b6ebff94d4f25371d8bccbd
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Dec 14 11:40:16 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:45 2023 -0400

    lok:sc: do not generate extra mouseup events
    
    In tiled rendering case, the client side always will
    send the pair mousedown/mouseup events to server side,
    it is not necessary to generate extra mouseup events
    when the mouse tracking has ended, otherwise the selection
    engine will receive two mouseup events and wrong selection
    states.
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I99983de9591e26f6e5327fff63c45e682cbf1999
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144168
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>

diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 547338589298..ca863a848a38 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -2879,11 +2879,14 @@ void ScGridWindow::Tracking( const TrackingEvent& rTEvt 
)
     }
     else if ( rTEvt.IsTrackingEnded() )
     {
-        // MouseButtonUp always with matching buttons (eg for test tool, # 
63148 #)
-        // The tracking event will indicate if it was completed and not 
canceled.
-        MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
-                            rMEvt.GetMode(), nButtonDown, rMEvt.GetModifier() 
);
-        MouseButtonUp( aUpEvt );
+        if (!comphelper::LibreOfficeKit::isActive())
+        {
+            // MouseButtonUp always with matching buttons (eg for test tool, # 
63148 #)
+            // The tracking event will indicate if it was completed and not 
canceled.
+            MouseEvent aUpEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(),
+                               rMEvt.GetMode(), nButtonDown, 
rMEvt.GetModifier() );
+            MouseButtonUp( aUpEvt );
+        }
     }
     else
         MouseMove( rMEvt );
commit cfef44584f0ab1ef23d5d986b862e8097126a8ae
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Thu Jan 5 11:32:32 2023 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:45 2023 -0400

    lok: sc: avoid the shared spreadsheet dialog
    
    It is not needed the LO core shared spreadsheet
    because the online has collaborative functionality turns on
    by default.
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I57ab83eae2913522d55704ae5a115e30c9d10034
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145091
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
index 458feb6dea2a..9a5b5968d9ec 100644
--- a/sc/source/ui/docshell/docsh.cxx
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -727,7 +727,8 @@ void ScDocShell::Notify( SfxBroadcaster&, const SfxHint& 
rHint )
 #endif
 
 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
-                    if ( IsDocShared() && !SC_MOD()->IsInSharedDocLoading() )
+                    if ( IsDocShared() && !SC_MOD()->IsInSharedDocLoading()
+                         && !comphelper::LibreOfficeKit::isActive() )
                     {
                         ScAppOptions aAppOptions = SC_MOD()->GetAppOptions();
                         if ( aAppOptions.GetShowSharedDocumentWarning() )
commit e78c76a250a90e553978c68c402540869b53fa8c
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Jan 17 11:02:13 2023 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:45 2023 -0400

    lok: fix query language status
    
    The Calc and Impress return language status
    as a SfxStringItem, but Writer is a SfxListStringItem.
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I88f77288e422a8f80473f0e33257078cfaa704b8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145675
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146479
    Tested-by: Jenkins

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 49a3a2f1a49e..9e36d464b75f 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -5393,22 +5393,34 @@ static void getDocLanguages(LibreOfficeKitDocument* 
pThis, uno::Sequence<lang::L
     css::uno::Any aLangStatus;
     pDispatcher->QueryState(SID_LANGUAGE_STATUS, aLangStatus);
 
-    Sequence<OUString> aSeqLang;
-    if (!(aLangStatus >>= aSeqLang))
-        return;
+    OUString sCurrent;
+    OUString sKeyboard;
+    OUString sGuessText;
+    SvtScriptType eScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN
+        | SvtScriptType::COMPLEX;
 
-    // (aSeqLang[0] == "Current Language",  aSeqLang[1] == "Script Type",
-    //  aSeqLang[2] == "Keyboard Language", aSeqLang[3] == "Guess Text Lang")
-    if (aSeqLang.getLength() != 4)
-        return;
+    Sequence<OUString> aSeqLang;
+    if (aLangStatus >>= aSeqLang)
+    {
+        if (aSeqLang.getLength() == 4)
+        {
+            sCurrent = aSeqLang[0];
+            eScriptType = static_cast<SvtScriptType>(aSeqLang[1].toInt32());
+            sKeyboard = aSeqLang[1];
+            sGuessText = aSeqLang[2];
+        }
+    }
+    else
+    {
+        aLangStatus >>= sCurrent;
+    }
 
     LanguageType nLangType;
     std::set<LanguageType> aLangItems;
-    SvtScriptType eScriptType = 
static_cast<SvtScriptType>(aSeqLang[1].toInt32());
 
-    if (!aSeqLang[0].isEmpty())
+    if (!sCurrent.isEmpty())
     {
-        nLangType = SvtLanguageTable::GetLanguageType(aSeqLang[0]);
+        nLangType = SvtLanguageTable::GetLanguageType(sCurrent);
         if (nLangType != LANGUAGE_DONTKNOW)
         {
             aLangItems.insert(nLangType);
@@ -5430,9 +5442,9 @@ static void getDocLanguages(LibreOfficeKitDocument* 
pThis, uno::Sequence<lang::L
         aLangItems.insert(nLangType);
     }
 
-    if (!aSeqLang[2].isEmpty())
+    if (!sKeyboard.isEmpty())
     {
-        nLangType = SvtLanguageTable::GetLanguageType(aSeqLang[2]);
+        nLangType = SvtLanguageTable::GetLanguageType(sKeyboard);
         if (nLangType != LANGUAGE_DONTKNOW &&
             (eScriptType & 
SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
         {
@@ -5440,7 +5452,7 @@ static void getDocLanguages(LibreOfficeKitDocument* 
pThis, uno::Sequence<lang::L
         }
     }
 
-    if (!aSeqLang[3].isEmpty())
+    if (!sGuessText.isEmpty())
     {
         Reference<linguistic2::XLanguageGuessing> xLangGuesser;
         try
@@ -5453,9 +5465,10 @@ static void getDocLanguages(LibreOfficeKitDocument* 
pThis, uno::Sequence<lang::L
 
         if (xLangGuesser.is())
         {
-            lang::Locale aLocale = 
xLangGuesser->guessPrimaryLanguage(aSeqLang[3], 0,
-                                                                      
aSeqLang[3].getLength());
-            nLangType = LanguageTag(aLocale).makeFallback().getLanguageType();
+            lang::Locale aLocale = 
xLangGuesser->guessPrimaryLanguage(sGuessText, 0,
+                                                                      
sGuessText.getLength());
+            LanguageTag aLanguageTag(aLocale);
+            nLangType = aLanguageTag.getLanguageType(false);
             if (nLangType != LANGUAGE_DONTKNOW &&
                 (eScriptType & 
SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
             {
commit dabccba088e5f504a241ff0c6ba1ebd9f1f61dfe
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Jan 11 16:14:57 2023 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:45 2023 -0400

    lok: add function getDocLanguages
    
    If the spell checker is disabled to use a remote
    spelling feature, the getLanguages function is obsolete.
    
    In order to not break stable functions, introduce the
    getDocLanguages for the remote spelling feature.
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: Ic210f31eddd3208b29d073ff35ba4fa2d98ea772
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145363
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>
    (cherry picked from commit bb0c63cc73998a0f67ae4c3e59acea3827a6e11c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146399
    Tested-by: Jenkins

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 2d71b7b435d5..49a3a2f1a49e 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -73,6 +73,7 @@
 #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>
@@ -98,6 +99,7 @@
 #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/i18n/LocaleCalendar2.hpp>
@@ -5378,6 +5380,119 @@ static void 
doc_setGraphicSelection(LibreOfficeKitDocument* pThis, int nType, in
     pDoc->setGraphicSelection(nType, nX, nY);
 }
 
+static void getDocLanguages(LibreOfficeKitDocument* pThis, 
uno::Sequence<lang::Locale>& rSeq)
+{
+    SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+    if (!pViewFrame)
+        return;
+
+    SfxDispatcher* pDispatcher = pViewFrame->GetBindings().GetDispatcher();
+    if (!pDispatcher)
+        return;
+
+    css::uno::Any aLangStatus;
+    pDispatcher->QueryState(SID_LANGUAGE_STATUS, aLangStatus);
+
+    Sequence<OUString> aSeqLang;
+    if (!(aLangStatus >>= aSeqLang))
+        return;
+
+    // (aSeqLang[0] == "Current Language",  aSeqLang[1] == "Script Type",
+    //  aSeqLang[2] == "Keyboard Language", aSeqLang[3] == "Guess Text Lang")
+    if (aSeqLang.getLength() != 4)
+        return;
+
+    LanguageType nLangType;
+    std::set<LanguageType> aLangItems;
+    SvtScriptType eScriptType = 
static_cast<SvtScriptType>(aSeqLang[1].toInt32());
+
+    if (!aSeqLang[0].isEmpty())
+    {
+        nLangType = SvtLanguageTable::GetLanguageType(aSeqLang[0]);
+        if (nLangType != LANGUAGE_DONTKNOW)
+        {
+            aLangItems.insert(nLangType);
+        }
+    }
+
+    const AllSettings& rAllSettings = Application::GetSettings();
+    nLangType = rAllSettings.GetLanguageTag().getLanguageType();
+    if (nLangType != LANGUAGE_DONTKNOW &&
+        (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
+    {
+        aLangItems.insert(nLangType);
+    }
+
+    nLangType = rAllSettings.GetUILanguageTag().getLanguageType();
+    if (nLangType != LANGUAGE_DONTKNOW &&
+        (eScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
+    {
+        aLangItems.insert(nLangType);
+    }
+
+    if (!aSeqLang[2].isEmpty())
+    {
+        nLangType = SvtLanguageTable::GetLanguageType(aSeqLang[2]);
+        if (nLangType != LANGUAGE_DONTKNOW &&
+            (eScriptType & 
SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
+        {
+            aLangItems.insert(nLangType);
+        }
+    }
+
+    if (!aSeqLang[3].isEmpty())
+    {
+        Reference<linguistic2::XLanguageGuessing> xLangGuesser;
+        try
+        {
+            xLangGuesser = linguistic2::LanguageGuessing::create(xContext);
+        }
+        catch(...)
+        {
+        }
+
+        if (xLangGuesser.is())
+        {
+            lang::Locale aLocale = 
xLangGuesser->guessPrimaryLanguage(aSeqLang[3], 0,
+                                                                      
aSeqLang[3].getLength());
+            nLangType = LanguageTag(aLocale).makeFallback().getLanguageType();
+            if (nLangType != LANGUAGE_DONTKNOW &&
+                (eScriptType & 
SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
+            {
+                aLangItems.insert(nLangType);
+            }
+        }
+    }
+
+    LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+    Reference<document::XDocumentLanguages> 
xDocumentLanguages(pDocument->mxComponent, UNO_QUERY);
+    if (xDocumentLanguages.is())
+    {
+        const Sequence<lang::Locale> 
aLocales(xDocumentLanguages->getDocumentLanguages(
+                                                  
static_cast<sal_Int16>(eScriptType), 64));
+
+        for (const lang::Locale& aLocale : aLocales)
+        {
+            nLangType = SvtLanguageTable::GetLanguageType(aLocale.Language);
+            if (nLangType != LANGUAGE_DONTKNOW &&
+                (eScriptType & 
SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType)))
+            {
+                aLangItems.insert(nLangType);
+            }
+        }
+    }
+
+    int nLocale = 0;
+    Sequence<lang::Locale> aLocales(aLangItems.size());
+    auto pLocales = aLocales.getArray();
+    for (const LanguageType& itLang : aLangItems)
+    {
+        pLocales[nLocale++] = LanguageTag::convertToLocale(itLang);
+    }
+
+    rSeq = aLocales;
+}
+
 static void doc_resetSelection(LibreOfficeKitDocument* pThis)
 {
     comphelper::ProfileZone aZone("doc_resetSelection");
@@ -5395,7 +5510,7 @@ static void doc_resetSelection(LibreOfficeKitDocument* 
pThis)
     pDoc->resetSelection();
 }
 
-static char* getLanguages(const char* pCommand)
+static char* getLanguages(LibreOfficeKitDocument* pThis, const char* pCommand)
 {
     css::uno::Sequence< css::lang::Locale > aLocales;
 
@@ -5408,6 +5523,16 @@ static char* getLanguages(const char* pCommand)
             if (xSpell.is())
                 aLocales = xSpell->getLocales();
         }
+
+        /* FIXME: To obtain the document languages the spell checker can be 
disabled,
+           so a future re-work of the getLanguages function is needed in favor 
to use
+           getDocLanguages */
+        if (!aLocales.hasElements())
+        {
+            uno::Sequence< css::lang::Locale > aSeq;
+            getDocLanguages(pThis, aSeq);
+            aLocales = aSeq;
+        }
     }
 
     boost::property_tree::ptree aTree;
@@ -5764,7 +5889,7 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* 
pThis, const char* pCo
 
     if (!strcmp(pCommand, ".uno:LanguageStatus"))
     {
-        return getLanguages(pCommand);
+        return getLanguages(pThis, pCommand);
     }
     else if (!strcmp(pCommand, ".uno:CharFontName"))
     {
commit 44500d0beba79d7e565b1ed56152188a69960155
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Fri Dec 23 13:57:01 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:44 2023 -0400

    lok: disable spell if remote language tool enabled
    
    .
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I73158c3feaa84af4d713d3f1b1fc9a60dcf95463
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144785
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145601
    Tested-by: Jenkins

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 43797b5b63c9..2d71b7b435d5 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -54,6 +54,8 @@
 #include <rtl/bootstrap.hxx>
 #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>
@@ -7063,6 +7065,23 @@ 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;
+                    Sequence<css::lang::Locale> aLocales = 
xSpell->getLocales();
+
+                    for (int itLocale = 0; itLocale < aLocales.getLength(); 
itLocale++)
+                    {
+                        xLangSrv->setConfiguredServices(SN_SPELLCHECKER, 
aLocales[itLocale], aEmpty);
+                    }
+                }
+            }
         }
         catch(uno::Exception const& rException)
         {
commit 7fc55467456bac81dea0a1db816472ece064b198
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Thu Nov 24 18:35:08 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:44 2023 -0400

    linguistic: add REST API protocol unit test
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I4768f8bb5bfa572d222fa5610f95c99169e6e390
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143249
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145600
    Tested-by: Jenkins

diff --git a/linguistic/CppunitTest_linguistic_restprotocol.mk 
b/linguistic/CppunitTest_linguistic_restprotocol.mk
new file mode 100644
index 000000000000..4e56286efe0d
--- /dev/null
+++ b/linguistic/CppunitTest_linguistic_restprotocol.mk
@@ -0,0 +1,39 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,linguistic_restprotocol))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,linguistic_restprotocol, \
+    linguistic/qa/restprotocol \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,linguistic_restprotocol, \
+    comphelper \
+    cppu \
+    cppuhelper \
+    sal \
+    svt \
+    utl \
+    test \
+    unotest \
+))
+
+$(eval $(call gb_CppunitTest_use_api,linguistic_restprotocol,\
+    udkapi \
+    offapi \
+    oovbaapi \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,linguistic_restprotocol))
+
+$(eval $(call gb_CppunitTest_use_ure,linguistic_restprotocol))
+
+$(eval $(call gb_CppunitTest_use_rdb,linguistic_restprotocol,services))
+
+# vim: set noet sw=4 ts=4:
diff --git a/linguistic/Module_linguistic.mk b/linguistic/Module_linguistic.mk
index 956c1bbc2e80..b5366b97e611 100644
--- a/linguistic/Module_linguistic.mk
+++ b/linguistic/Module_linguistic.mk
@@ -16,9 +16,16 @@ $(eval $(call gb_Module_add_targets,linguistic,\
 #$(eval $(call gb_Module_add_check_targets,linguistic,\
 #))
 
+ifeq ($(OS),LINUX)
 $(eval $(call gb_Module_add_subsequentcheck_targets,linguistic,\
     JunitTest_linguistic_unoapi \
+    CppunitTest_linguistic_restprotocol \
 ))
+else
+$(eval $(call gb_Module_add_subsequentcheck_targets,linguistic,\
+    JunitTest_linguistic_unoapi \
+))
+endif
 
 # was disabled in old build system
 # JunitTest_linguistic_complex \
diff --git a/linguistic/qa/restprotocol.cxx b/linguistic/qa/restprotocol.cxx
new file mode 100644
index 000000000000..61c04185c757
--- /dev/null
+++ b/linguistic/qa/restprotocol.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/strbuf.hxx>
+#include <osl/socket.hxx>
+#include <osl/thread.hxx>
+#include <svtools/languagetoolcfg.hxx>
+#include <unotest/bootstrapfixturebase.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/connection/XAcceptor.hpp>
+#include <com/sun/star/connection/XConnector.hpp>
+#include <com/sun/star/linguistic2/XProofreader.hpp>
+#include <com/sun/star/linguistic2/ProofreadingResult.hpp>
+
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+class MockServerThread : public ::osl::Thread
+{
+public:
+    MockServerThread()
+        : m_aSocketAddr("localhost", 2022)
+    {
+    }
+
+    virtual void SAL_CALL run()
+    {
+        if (m_aAcceptorSocket.acceptConnection(m_aStreamSocket) != 
osl_Socket_Ok)
+        {
+            return;
+        }
+
+        sal_Int32 nReadBytes;
+        Sequence<sal_Int8> aBuffer(512);
+        sal_Int32 nTcpNoDelay = sal_Int32(true);
+        m_aStreamSocket.setOption(osl_Socket_OptionTcpNoDelay, &nTcpNoDelay, 
sizeof(nTcpNoDelay),
+                                  osl_Socket_LevelTcp);
+
+        nReadBytes = m_aStreamSocket.recv(aBuffer.getArray(), 
aBuffer.getLength());
+        if (nReadBytes)
+        {
+            std::string aText(reinterpret_cast<const 
char*>(aBuffer.getConstArray()), nReadBytes);
+
+            if (aText.find("POST /api/check") == std::string::npos)
+            {
+                NotFound();
+            }
+            else if (aText.find("Content-Type: application/json") == 
std::string::npos)
+            {
+                NotFound();
+            }
+            else
+            {
+                ResponseOK();
+            }
+        }
+    }
+
+    void ResponseOK()
+    {
+        OString aResponse(
+            "HTTP/1.1 200 OK\r\n"
+            "Server: MockServer\r\n"
+            "Cache-Control: no-cache\r\n"
+            "Content-Type: application/json\r\n"
+            "\r\n"
+            
"{\"check-positions\":[{\"offset\":15,\"length\":6,\"errorcode\":4711,\"type\":"
+            "\"orth\","
+            "\"severity\":1,\"proposals\":[\"Entwurf\",\"Entw\u00fcrfe\"]},"
+            
"{\"offset\":22,\"length\":3,\"errorcode\":8221,\"type\":\"orth\",\"severity\":1}]}");
+
+        m_aStreamSocket.write(aResponse.getStr(), aResponse.getLength());
+        m_aStreamSocket.close();
+    }
+
+    void NotFound()
+    {
+        OString aResponse("HTTP/1.1 404 Not Found\r\n"
+                          "Connection: Closed\r\n"
+                          "\r\n");
+
+        m_aStreamSocket.write(aResponse.getStr(), aResponse.getLength());
+        m_aStreamSocket.close();
+    }
+
+    void stop()
+    {
+        m_aAcceptorSocket.close();
+        join();
+    }
+
+    void init()
+    {
+        m_aAcceptorSocket.setOption(osl_Socket_OptionReuseAddr, 1);
+        CPPUNIT_ASSERT(m_aAcceptorSocket.bind(m_aSocketAddr));
+        CPPUNIT_ASSERT(m_aAcceptorSocket.listen());
+    }
+
+private:
+    ::osl::SocketAddr m_aSocketAddr;
+    ::osl::AcceptorSocket m_aAcceptorSocket;
+    ::osl::StreamSocket m_aStreamSocket;
+};
+}
+
+MockServerThread aMockServer;
+
+class TestRestProtocol : public test::BootstrapFixtureBase
+{
+public:
+    virtual void setUp() override;
+    virtual void tearDown() override;
+
+private:
+    CPPUNIT_TEST_SUITE(TestRestProtocol);
+    CPPUNIT_TEST(testProofreading);
+    CPPUNIT_TEST_SUITE_END();
+
+    void testProofreading();
+};
+
+void TestRestProtocol::testProofreading()
+{
+    css::lang::Locale aLocale("en", "US", "");
+    Sequence<::com::sun::star::beans::PropertyValue> aProperties;
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    rLanguageOpts.setBaseURL("http://127.0.0.1:2022/api";);
+    rLanguageOpts.setUsername("hcastro");
+    rLanguageOpts.setApiKey("hcvhcvhcv");
+    rLanguageOpts.setEnabled(true);
+    rLanguageOpts.setSSLVerification(false);
+    rLanguageOpts.setRestProtocol("duden");
+    CPPUNIT_ASSERT_EQUAL(OUString("duden"), rLanguageOpts.getRestProtocol());
+
+    Reference<::com::sun::star::linguistic2::XProofreader> xProofreader(
+        m_xSFactory->createInstance("com.sun.star.linguistic2.Proofreader"), 
UNO_QUERY);
+    CPPUNIT_ASSERT(xProofreader.is());
+
+    com::sun::star::linguistic2::ProofreadingResult aResult
+        = xProofreader->doProofreading("id", "ths is a tst", aLocale, 0, 0, 
aProperties);
+
+    CPPUNIT_ASSERT_EQUAL(2, aResult.aErrors.getLength());
+}
+
+void TestRestProtocol::setUp()
+{
+    test::BootstrapFixtureBase::setUp();
+
+    aMockServer.init();
+    aMockServer.create();
+    osl::Thread::wait(std::chrono::seconds(1));
+}
+
+void TestRestProtocol::tearDown()
+{
+    aMockServer.stop();
+
+    test::BootstrapFixtureBase::tearDown();
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestRestProtocol);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 38a91df12b75bdcb263535c84a62f4446168a4cf
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Tue Nov 22 11:39:45 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:44 2023 -0400

    lok: add Rest Protocol property
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I7353e16dbf835684e5ce5963387359924352937b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143124
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145599
    Tested-by: Jenkins

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index ea5f556d7f65..43797b5b63c9 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -7035,22 +7035,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");
-    const char* pSSLVerification = ::getenv("LANGUAGETOOL_SSL_VERIFICATION");
+
     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 = 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);
commit 34d5485bb00a9b0e5c52add5785e2c4fa6d72496
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Mon Nov 21 21:38:40 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Mon Feb 6 16:33:43 2023 -0400

    lingucomponent: implement custom parse response rest protocol
    
    Response:
    
    HTTP/1.1 200 OK
    Transfer encoding: chunked
    Content-Type: application/json
    {
    "hyphenation-positions":[
    {"offset":15,"quality":0},
    {"offset":20,"quality":1}
    ],
    }, "check-positions":[
    {"offset":15,"length":6,"errorcode":4711,"type":"orth","severity":1,"propos
    als":["Entwurf","Entw\u00fcrfe"]},
    {"offset":22,"length":3,"errorcode":8221,"type":"orth","severity":1}
    ]
    }
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I87228237f23306fb367edab1e21ce002aaf13f2c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143108
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145598
    Tested-by: Jenkins

diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
index 12d39ef8af93..2f06e054a5bd 100644
--- a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
@@ -62,7 +62,7 @@ namespace
 Sequence<PropertyValue> lcl_GetLineColorPropertyFromErrorId(const std::string& 
rErrorId)
 {
     Color aColor;
-    if (rErrorId == "TYPOS")
+    if (rErrorId == "TYPOS" || rErrorId == "orth")
     {
         aColor = COL_LIGHTRED;
     }
@@ -261,13 +261,78 @@ ProofreadingResult SAL_CALL 
LanguageToolGrammarChecker::doProofreading(
         return xRes;
     }
 
-    parseProofreadingJSONResponse(xRes, response_body);
+    if (rLanguageOpts.getRestProtocol() == sDuden)
+    {
+        parseDudenResponse(xRes, response_body);
+    }
+    else
+    {
+        parseProofreadingJSONResponse(xRes, response_body);
+    }
     // cache the result
     mCachedResults.insert(
         std::pair<OUString, Sequence<SingleProofreadingError>>(aText, 
xRes.aErrors));
     return xRes;
 }
 
+void LanguageToolGrammarChecker::parseDudenResponse(ProofreadingResult& 
rResult,
+                                                    std::string_view aJSONBody)
+{
+    size_t nSize;
+    int nProposalSize;
+    boost::property_tree::ptree aRoot;
+    std::stringstream aStream(aJSONBody.data());
+    boost::property_tree::read_json(aStream, aRoot);
+
+    const boost::optional<boost::property_tree::ptree&> aPositions
+        = aRoot.get_child_optional("check-positions");
+    if (!aPositions || !(nSize = aPositions.get().size()))
+    {
+        return;
+    }
+
+    Sequence<SingleProofreadingError> aChecks(nSize);
+    auto pChecks = aChecks.getArray();
+    size_t nIndex1 = 0, nIndex2 = 0;
+    auto itPos = aPositions.get().begin();
+    while (itPos != aPositions.get().end())
+    {
+        const boost::property_tree::ptree& rTree = itPos->second;
+        const std::string sType = rTree.get<std::string>("type", "");
+        const int nOffset = rTree.get<int>("offset", 0);
+        const int nLength = rTree.get<int>("length", 0);
+
+        pChecks[nIndex1].nErrorStart = nOffset;
+        pChecks[nIndex1].nErrorLength = nLength;
+        pChecks[nIndex1].nErrorType = PROOFREADING_ERROR;
+        //pChecks[nIndex1].aShortComment = ??
+        //pChecks[nIndex1].aFullComment = ??
+        pChecks[nIndex1].aProperties = 
lcl_GetLineColorPropertyFromErrorId(sType);
+
+        const boost::optional<const boost::property_tree::ptree&> aProposals
+            = rTree.get_child_optional("proposals");
+        if (aProposals && (nProposalSize = aProposals.get().size()))
+        {
+            pChecks[nIndex1].aSuggestions.realloc(std::min(nProposalSize, 
MAX_SUGGESTIONS_SIZE));
+
+            nIndex2 = 0;
+            auto itProp = aProposals.get().begin();
+            auto pSuggestions = pChecks[nIndex1].aSuggestions.getArray();
+            while (itProp != aProposals.get().end() && nIndex2 < 
MAX_SUGGESTIONS_SIZE)
+            {
+                pSuggestions[nIndex2++]
+                    = OStringToOUString(itProp->second.data(), 
RTL_TEXTENCODING_UTF8);
+                itProp++;
+            }
+        }
+
+        nIndex1++;
+        itPos++;
+    }
+
+    rResult.aErrors = aChecks;
+}
+
 /*
     rResult is both input and output
     aJSONBody is the response body from the HTTP Request to LanguageTool API
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
index c3b79c95a43c..adfbe121c981 100644
--- a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
@@ -51,6 +51,8 @@ class LanguageToolGrammarChecker
         mCachedResults;
     LanguageToolGrammarChecker(const LanguageToolGrammarChecker&) = delete;
     LanguageToolGrammarChecker& operator=(const LanguageToolGrammarChecker&) = 
delete;
+    static void parseDudenResponse(css::linguistic2::ProofreadingResult& 
rResult,
+                                   std::string_view aJSONBody);
     static void 
parseProofreadingJSONResponse(css::linguistic2::ProofreadingResult& rResult,
                                               std::string_view aJSONBody);
     static std::string makeDudenHttpRequest(std::string_view aURL, HTTP_METHOD 
method,

Reply via email to