Repository.mk                                                        |    1 
 cui/Library_cui.mk                                                   |    1 
 cui/UIConfig_cui.mk                                                  |    1 
 cui/inc/treeopt.hrc                                                  |    3 
 cui/source/options/optlanguagetool.cxx                               |   76 +
 cui/source/options/optlanguagetool.hxx                               |   44 +
 cui/source/options/treeopt.cxx                                       |    2 
 cui/uiconfig/ui/langtoolconfigpage.ui                                |  240 
++++++
 include/sfx2/pageids.hxx                                             |    1 
 include/svtools/languagetoolcfg.hxx                                  |   59 +
 include/svtools/strings.hrc                                          |    1 
 include/unotools/lingucfg.hxx                                        |    2 
 lingucomponent/Library_LanguageTool.mk                               |   48 +
 lingucomponent/Module_lingucomponent.mk                              |    1 
 lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu   |   30 
 lingucomponent/source/spellcheck/languagetool/LanguageTool.component |   26 
 lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx    |  394 
++++++++++
 lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx    |   91 ++
 officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs       |   30 
 postprocess/CustomTarget_registry.mk                                 |    1 
 postprocess/Rdb_services.mk                                          |    1 
 scripting/source/stringresource/stringresource.cxx                   |    1 
 svtools/Library_svt.mk                                               |    1 
 svtools/source/config/languagetoolcfg.cxx                            |  164 
++++
 unotools/source/config/lingucfg.cxx                                  |   21 
 25 files changed, 1239 insertions(+), 1 deletion(-)

New commits:
commit 7686dceb70881e8a3c4ac7f18ced15e5202de8c7
Author:     Mert Tumer <mert.tu...@collabora.com>
AuthorDate: Tue May 10 13:06:06 2022 +0300
Commit:     Mert Tumer <mert.tu...@collabora.com>
CommitDate: Tue Jun 14 16:13:22 2022 +0200

    LanguageTool Grammar Checker implementation
    
    Signed-off-by: Mert Tumer <mert.tu...@collabora.com>
    Change-Id: I275cbea668afc5beb5147370119631df8b6a2d46
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135464

diff --git a/Repository.mk b/Repository.mk
index 55a8d77f297d..6dd4aebbe9cd 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -319,6 +319,7 @@ $(eval $(call 
gb_Helper_register_libraries_for_install,OOOLIBS,ogltrans, \
 
 $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo, \
        acc \
+       LanguageTool \
        $(call gb_Helper_optional,AVMEDIA,avmedia) \
        $(if $(filter MACOSX,$(OS)),\
                avmediaMacAVF \
diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index 88e75ee1faef..1e8023cc819c 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -180,6 +180,7 @@ $(eval $(call gb_Library_add_exception_objects,cui,\
     cui/source/options/optgdlg \
     cui/source/options/optgenrl \
     cui/source/options/opthtml \
+    cui/source/options/optlanguagetool \
     cui/source/options/optinet2 \
     cui/source/options/optjava \
     cui/source/options/optjsearch \
diff --git a/cui/UIConfig_cui.mk b/cui/UIConfig_cui.mk
index c70cbd94588c..a4ad452bdfee 100644
--- a/cui/UIConfig_cui.mk
+++ b/cui/UIConfig_cui.mk
@@ -138,6 +138,7 @@ $(eval $(call gb_UIConfig_add_uifiles,cui,\
        cui/uiconfig/ui/optfontspage \
        cui/uiconfig/ui/optgeneralpage \
        cui/uiconfig/ui/opthtmlpage \
+       cui/uiconfig/ui/langtoolconfigpage \
        cui/uiconfig/ui/optionsdialog \
        cui/uiconfig/ui/optjsearchpage \
        cui/uiconfig/ui/optlanguagespage \
diff --git a/cui/inc/treeopt.hrc b/cui/inc/treeopt.hrc
index c7ee66d7e8d0..952b79ea92d4 100644
--- a/cui/inc/treeopt.hrc
+++ b/cui/inc/treeopt.hrc
@@ -54,7 +54,8 @@ const std::pair<TranslateId, sal_uInt16> 
SID_LANGUAGE_OPTIONS_RES[] =
     { NC_("SID_LANGUAGE_OPTIONS_RES", "Writing Aids"), RID_SFXPAGE_LINGU },
     { NC_("SID_LANGUAGE_OPTIONS_RES", "Searching in Japanese"),  
RID_SVXPAGE_JSEARCH_OPTIONS },
     { NC_("SID_LANGUAGE_OPTIONS_RES", "Asian Layout"),  
RID_SVXPAGE_ASIAN_LAYOUT },
-    { NC_("SID_LANGUAGE_OPTIONS_RES", "Complex Text Layout"),  
RID_SVXPAGE_OPTIONS_CTL }
+    { NC_("SID_LANGUAGE_OPTIONS_RES", "Complex Text Layout"),  
RID_SVXPAGE_OPTIONS_CTL },
+    { NC_("SID_LANGUAGE_OPTIONS_RES", "LanguageTool Server Settings"),  
RID_SVXPAGE_LANGTOOL_OPTIONS }
 };
 
 const std::pair<TranslateId, sal_uInt16> SID_INET_DLG_RES[] =
diff --git a/cui/source/options/optlanguagetool.cxx 
b/cui/source/options/optlanguagetool.cxx
new file mode 100644
index 000000000000..38807bc337d3
--- /dev/null
+++ b/cui/source/options/optlanguagetool.cxx
@@ -0,0 +1,76 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "optlanguagetool.hxx"
+#include <svtools/languagetoolcfg.hxx>
+#include <sal/log.hxx>
+
+OptLanguageToolTabPage::OptLanguageToolTabPage(weld::Container* pPage,
+                                               weld::DialogController* 
pController,
+                                               const SfxItemSet& rSet)
+    : SfxTabPage(pPage, pController, "cui/ui/langtoolconfigpage.ui", 
"OptLangToolPage", &rSet)
+    , m_xBaseURLED(m_xBuilder->weld_entry("baseurl"))
+    , m_xUsernameED(m_xBuilder->weld_entry("username"))
+    , m_xApiKeyED(m_xBuilder->weld_entry("apikey"))
+    , m_xActivateBox(m_xBuilder->weld_check_button("activate"))
+    , m_xApiSettingsFrame(m_xBuilder->weld_frame("apisettings"))
+{
+    m_xActivateBox->connect_toggled(LINK(this, OptLanguageToolTabPage, 
CheckHdl));
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    EnableControls(rLanguageOpts.getEnabled());
+}
+
+OptLanguageToolTabPage::~OptLanguageToolTabPage() {}
+
+void OptLanguageToolTabPage::EnableControls(bool bEnable)
+{
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    rLanguageOpts.setEnabled(bEnable);
+    m_xApiSettingsFrame->set_visible(bEnable);
+    m_xActivateBox->set_active(bEnable);
+}
+
+IMPL_LINK_NOARG(OptLanguageToolTabPage, CheckHdl, weld::Toggleable&, void)
+{
+    EnableControls(m_xActivateBox->get_active());
+}
+
+void OptLanguageToolTabPage::Reset(const SfxItemSet*)
+{
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    m_xBaseURLED->set_text(rLanguageOpts.getBaseURL());
+    m_xUsernameED->set_text(rLanguageOpts.getUsername());
+    m_xApiKeyED->set_text(rLanguageOpts.getApiKey());
+}
+
+bool OptLanguageToolTabPage::FillItemSet(SfxItemSet*)
+{
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    rLanguageOpts.setBaseURL(m_xBaseURLED->get_text());
+    rLanguageOpts.setUsername(m_xUsernameED->get_text());
+    rLanguageOpts.setApiKey(m_xApiKeyED->get_text());
+    return false;
+}
+
+std::unique_ptr<SfxTabPage> OptLanguageToolTabPage::Create(weld::Container* 
pPage,
+                                                           
weld::DialogController* pController,
+                                                           const SfxItemSet* 
rAttrSet)
+{
+    return std::make_unique<OptLanguageToolTabPage>(pPage, pController, 
*rAttrSet);
+}
diff --git a/cui/source/options/optlanguagetool.hxx 
b/cui/source/options/optlanguagetool.hxx
new file mode 100644
index 000000000000..46a60ecbe103
--- /dev/null
+++ b/cui/source/options/optlanguagetool.hxx
@@ -0,0 +1,44 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <sfx2/tabdlg.hxx>
+
+class OptLanguageToolTabPage : public SfxTabPage
+{
+public:
+    OptLanguageToolTabPage(weld::Container* pPage, weld::DialogController* 
pController,
+                           const SfxItemSet& rSet);
+    virtual ~OptLanguageToolTabPage() override;
+    static std::unique_ptr<SfxTabPage>
+    Create(weld::Container* pPage, weld::DialogController* pController, const 
SfxItemSet* rAttrSet);
+
+    virtual bool FillItemSet(SfxItemSet* rSet) override;
+    virtual void Reset(const SfxItemSet* rSet) override;
+
+private:
+    std::unique_ptr<weld::Entry> m_xBaseURLED;
+    std::unique_ptr<weld::Entry> m_xUsernameED;
+    std::unique_ptr<weld::Entry> m_xApiKeyED;
+    std::unique_ptr<weld::CheckButton> m_xActivateBox;
+    std::unique_ptr<weld::Frame> m_xApiSettingsFrame;
+
+    void EnableControls(bool bEnable);
+
+    DECL_LINK(CheckHdl, weld::Toggleable&, void);
+};
\ No newline at end of file
diff --git a/cui/source/options/treeopt.cxx b/cui/source/options/treeopt.cxx
index a3d2fc54819d..e3029ca45eaf 100644
--- a/cui/source/options/treeopt.cxx
+++ b/cui/source/options/treeopt.cxx
@@ -63,6 +63,7 @@
 #include "personalization.hxx"
 #include <treeopt.hxx>
 #include "optbasic.hxx"
+#include "optlanguagetool.hxx"
 
 #include <com/sun/star/awt/XContainerWindowEventHandler.hpp>
 #include <com/sun/star/awt/ContainerWindowProvider.hpp>
@@ -297,6 +298,7 @@ static std::unique_ptr<SfxTabPage> 
CreateGeneralTabPage(sal_uInt16 nId, weld::Co
         case SID_SB_DBREGISTEROPTIONS:              fnCreate = 
&svx::DbRegistrationOptionsPage::Create; break;
         case RID_SVXPAGE_ACCESSIBILITYCONFIG:       fnCreate = 
&SvxAccessibilityOptionsTabPage::Create; break;
         case RID_SVXPAGE_OPTIONS_CTL:               fnCreate = 
&SvxCTLOptionsPage::Create ; break;
+        case RID_SVXPAGE_LANGTOOL_OPTIONS:          fnCreate = 
&OptLanguageToolTabPage::Create ; break;
         case RID_SVXPAGE_OPTIONS_JAVA:              fnCreate = 
&SvxJavaOptionsPage::Create ; break;
 #if HAVE_FEATURE_OPENCL
         case RID_SVXPAGE_OPENCL:                    fnCreate = 
&SvxOpenCLTabPage::Create ; break;
diff --git a/cui/uiconfig/ui/langtoolconfigpage.ui 
b/cui/uiconfig/ui/langtoolconfigpage.ui
new file mode 100644
index 000000000000..7b822325e3f1
--- /dev/null
+++ b/cui/uiconfig/ui/langtoolconfigpage.ui
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="cui">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkBox" id="OptLangToolPage">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="spacing">12</property>
+    <child>
+      <object class="GtkFrame">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">none</property>
+        <child>
+          <object class="GtkGrid" id="grid0">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <child>
+              <object class="GtkLabel" id="disclaimer">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="margin_top">6</property>
+                <property name="margin_bottom">4</property>
+                <property name="label" translatable="yes" 
context="langtoolconfigpage|disclaimer">If you enable this, the data will be 
sent to an external server.</property>
+                <property name="wrap">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLinkButton" id="policy">
+                <property name="label" translatable="yes" 
context="langtoolconfigpage|policy">Please read the privacy policy</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="halign">start</property>
+                <property name="valign">start</property>
+                <property name="relief">none</property>
+                <property 
name="uri">https://languagetool.org/legal/privacy</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="activate">
+                <property name="label" translatable="yes" 
context="langtoolconfigpage|activate">Enable LanguageTool</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="halign">start</property>
+                <property name="margin_top">6</property>
+                <property name="margin_bottom">9</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame" id="apisettings">
+                <property name="can_focus">False</property>
+                <property name="margin_top">10</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkGrid" id="grid1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="row_spacing">5</property>
+                    <property name="column_spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="base">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|base">Base URL:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">baseurl</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="baseurl">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="usernamelbl">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|usernamelbl">User name:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">username</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="apikeylbl">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|apikeylbl">API key:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">apikey</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="username">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">2</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkEntry" id="apikey">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">4</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="urldesc">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|urldesc">Please use the base URL e.g. without 
"/check" at the end.</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="usernamedesc">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|usernamedesc">Your LanguageTool account's username 
for premium usage.</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">3</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="apikeydesc">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">start</property>
+                        <property name="label" translatable="yes" 
context="langtoolconfigpage|apikeydesc">Your LanguageTool account's api key for 
premium usage.</property>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">5</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel" id="apisettingsheader">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_bottom">10</property>
+                    <property name="label" translatable="yes" 
context="langtoolconfigpage|apisettingsheader">API Settings</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">3</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="label">
+          <object class="GtkLabel" id="langtoolsettings">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_top">4</property>
+            <property name="label" translatable="yes" 
context="langtoolconfigpage|langtoolsettings">LanguageTool API 
Options</property>
+            <attributes>
+              <attribute name="style" value="normal"/>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+  </object>
+</interface>
diff --git a/include/sfx2/pageids.hxx b/include/sfx2/pageids.hxx
index 1dcba1219c31..9371848b784a 100644
--- a/include/sfx2/pageids.hxx
+++ b/include/sfx2/pageids.hxx
@@ -56,6 +56,7 @@
 #define RID_SVXPAGE_PERSONALIZATION         (RID_SVX_START + 247)
 #define RID_SVXPAGE_COLORCONFIG             (RID_SVX_START + 249)
 #define RID_SVXPAGE_BASICIDE_OPTIONS        (RID_SVX_START + 209)
+#define RID_SVXPAGE_LANGTOOL_OPTIONS        (RID_SVX_START + 210)
 
 // Resource-Id's ------------------------------------------------------------
 
diff --git a/include/svtools/languagetoolcfg.hxx 
b/include/svtools/languagetoolcfg.hxx
new file mode 100644
index 000000000000..7578ad6ba281
--- /dev/null
+++ b/include/svtools/languagetoolcfg.hxx
@@ -0,0 +1,59 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+#include <unotools/configitem.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <svtools/svtdllapi.h>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+struct LanguageToolOptions_Impl;
+
+class SVT_DLLPUBLIC SvxLanguageToolOptions final : public utl::ConfigItem
+{
+public:
+    SvxLanguageToolOptions();
+    virtual ~SvxLanguageToolOptions() override;
+
+    virtual void Notify(const css::uno::Sequence<OUString>& _rPropertyNames) 
override;
+    static SvxLanguageToolOptions& Get();
+
+    const OUString& getBaseURL() const;
+    void setBaseURL(const OUString& rVal);
+
+    const OUString& getUsername() const;
+    void setUsername(const OUString& rVal);
+
+    OUString getLocaleListURL() const;
+    OUString getCheckerURL() const;
+
+    const OUString& getApiKey() const;
+    void setApiKey(const OUString& rVal);
+
+    bool getEnabled() const;
+    void setEnabled(bool enabled);
+
+private:
+    std::unique_ptr<LanguageToolOptions_Impl> pImpl;
+    void Load(const css::uno::Sequence<OUString>& rPropertyNames);
+    virtual void ImplCommit() override;
+    static const Sequence<OUString>& GetPropertyNames();
+};
diff --git a/include/svtools/strings.hrc b/include/svtools/strings.hrc
index 80d8d2e7fa5c..eb6af564eb4f 100644
--- a/include/svtools/strings.hrc
+++ b/include/svtools/strings.hrc
@@ -338,5 +338,6 @@
 #define STR_DESCRIPTION_LIBHYPHEN               
NC_("STR_DESCRIPTION_LIBHYPHEN", "Libhyphen Hyphenator")
 #define STR_DESCRIPTION_MYTHES                  NC_("STR_DESCRIPTION_MYTHES", 
"MyThes Thesaurus")
 #define STR_DESCRIPTION_IGNOREALLLIST           
NC_("STR_DESCRIPTION_IGNOREALLLIST", "List of Ignored Words")
+#define STR_DESCRIPTION_LANGUAGETOOL            
NC_("STR_DESCRIPTION_LANGUAGETOOL", "LanguageTool Remote Grammar Checker")
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/unotools/lingucfg.hxx b/include/unotools/lingucfg.hxx
index 20c678b96b4f..d3e00588ead4 100644
--- a/include/unotools/lingucfg.hxx
+++ b/include/unotools/lingucfg.hxx
@@ -192,6 +192,8 @@ public:
 
     bool GetDictionaryEntry( const OUString &rNodeName, 
SvtLinguConfigDictionaryEntry &rDicEntry ) const;
 
+    bool GetLocaleListFor( const OUString &rSetName, const OUString 
&rSetEntry, css::uno::Sequence< OUString > &rFormatList ) const;
+
     css::uno::Sequence< OUString > GetDisabledDictionaries() const;
 
     std::vector< SvtLinguConfigDictionaryEntry > 
GetActiveDictionariesByFormat( std::u16string_view rFormatName ) const;
diff --git a/lingucomponent/Library_LanguageTool.mk 
b/lingucomponent/Library_LanguageTool.mk
new file mode 100644
index 000000000000..98ff7cf7edf2
--- /dev/null
+++ b/lingucomponent/Library_LanguageTool.mk
@@ -0,0 +1,48 @@
+# -*- 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_Library_Library,LanguageTool))
+
+$(eval $(call 
gb_Library_set_componentfile,LanguageTool,lingucomponent/source/spellcheck/languagetool/LanguageTool,services))
+
+$(eval $(call gb_Library_set_include,LanguageTool,\
+       $$(INCLUDE) \
+       -I$(SRCDIR)/lingucomponent/source/lingutil \
+))
+
+$(eval $(call gb_Library_use_sdk_api,LanguageTool))
+
+$(eval $(call gb_Library_use_libraries,LanguageTool,\
+       comphelper \
+       cppu \
+       cppuhelper \
+       i18nlangtag \
+       svt \
+       lng \
+       sal \
+       tl \
+       utl \
+))
+
+$(eval $(call gb_Library_use_static_libraries,LanguageTool,\
+       ulingu \
+))
+
+$(eval $(call gb_Library_use_externals,LanguageTool,\
+       boost_headers \
+       icuuc \
+       curl \
+))
+
+$(eval $(call gb_Library_add_exception_objects,LanguageTool,\
+       lingucomponent/source/spellcheck/languagetool/languagetoolimp \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/lingucomponent/Module_lingucomponent.mk 
b/lingucomponent/Module_lingucomponent.mk
index 2bde5d5391e5..c77a7c5df0a0 100644
--- a/lingucomponent/Module_lingucomponent.mk
+++ b/lingucomponent/Module_lingucomponent.mk
@@ -18,6 +18,7 @@ $(eval $(call gb_Module_add_targets,lingucomponent,\
        Library_spell \
        StaticLibrary_ulingu \
        Library_numbertext \
+       Library_LanguageTool \
 ))
 
 # vim: set noet sw=4 ts=4:
diff --git a/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu 
b/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu
new file mode 100644
index 000000000000..ce3d6033d0c2
--- /dev/null
+++ b/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu
@@ -0,0 +1,30 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+-->
+<oor:component-data oor:name="Linguistic" oor:package="org.openoffice.Office" 
xmlns:install="http://openoffice.org/2004/installation"; 
xmlns:oor="http://openoffice.org/2001/registry"; 
xmlns:xs="http://www.w3.org/2001/XMLSchema"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+    <node oor:name="ServiceManager">
+        <node oor:name="GrammarCheckers">
+            <node oor:name="org.openoffice.lingu.LanguageToolGrammarChecker" 
oor:op="fuse">
+                <prop oor:name="Locales" oor:type="oor:string-list">
+                    <value>ar ast-ES be-BY br-FR ca-ES ca-ES-valencia zh-CN 
da-DK nl nl-BE en en-AU en-CA en-CA en-GB en-NZ en-ZA en-US fr gl-ES de de-AT 
de-DE de-DE de-CH el-GR ga-IE it ja-JP km-KH nb no fa pl-PL pt pt-AO pt-BR 
pt-MZ pt-PT ro-RO ru-RU de-DE-x-simple-language sk-SK sl-SI es es-AR sv tl-PH 
ta-IN uk-UA</value>
+                </prop>
+            </node>
+        </node>
+    </node>
+</oor:component-data>
+
diff --git 
a/lingucomponent/source/spellcheck/languagetool/LanguageTool.component 
b/lingucomponent/source/spellcheck/languagetool/LanguageTool.component
new file mode 100644
index 000000000000..9f7eb3d08789
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/LanguageTool.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+    xmlns="http://openoffice.org/2010/uno-components";>
+  <implementation name="org.openoffice.lingu.LanguageToolGrammarChecker"
+    constructor="lingucomponent_LanguageToolGrammarChecker_get_implementation" 
single-instance="true">
+    <service name="com.sun.star.linguistic2.Proofreader"/>
+  </implementation>
+</component>
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
new file mode 100644
index 000000000000..5479e67e82c4
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
@@ -0,0 +1,394 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include "languagetoolimp.hxx"
+
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/resmgr.hxx>
+
+#include <vector>
+#include <set>
+#include <string.h>
+
+#include <curl/curl.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <algorithm>
+#include <string_view>
+#include <svtools/languagetoolcfg.hxx>
+#include <tools/color.hxx>
+#include <tools/long.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <unotools/lingucfg.hxx>
+#include <osl/mutex.hxx>
+
+using namespace osl;
+using namespace com::sun::star;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::linguistic2;
+using namespace linguistic;
+
+#define COL_ORANGE Color(0xD1, 0x68, 0x20)
+
+namespace
+{
+PropertyValue lcl_MakePropertyValue(const OUString& rName, uno::Any& rValue)
+{
+    return PropertyValue(rName, -1, rValue, 
css::beans::PropertyState_DIRECT_VALUE);
+}
+
+Sequence<PropertyValue> lcl_GetLineColorPropertyFromErrorId(const std::string& 
rErrorId)
+{
+    uno::Any aColor;
+    if (rErrorId == "TYPOS")
+    {
+        aColor <<= COL_LIGHTRED;
+    }
+    else if (rErrorId == "STYLE")
+    {
+        aColor <<= COL_LIGHTBLUE;
+    }
+    else
+    {
+        // Same color is used for other errorId's such as GRAMMAR, TYPOGRAPHY..
+        aColor <<= COL_ORANGE;
+    }
+    Sequence<PropertyValue> aProperties{ lcl_MakePropertyValue("LineColor", 
aColor) };
+    return aProperties;
+}
+}
+
+LanguageToolGrammarChecker::LanguageToolGrammarChecker()
+    : mCachedResults(MAX_CACHE_SIZE)
+{
+}
+
+LanguageToolGrammarChecker::~LanguageToolGrammarChecker() {}
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::isSpellChecker() { return false; 
}
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::hasLocale(const Locale& rLocale)
+{
+    bool bRes = false;
+    if (!m_aSuppLocales.hasElements())
+        getLocales();
+
+    for (auto const& suppLocale : std::as_const(m_aSuppLocales))
+    {
+        if (rLocale == suppLocale)
+        {
+            bRes = true;
+            break;
+        }
+    }
+
+    return bRes;
+}
+
+Sequence<Locale> SAL_CALL LanguageToolGrammarChecker::getLocales()
+{
+    MutexGuard  aGuard( GetLinguMutex() );
+
+    if (m_aSuppLocales.hasElements())
+        return m_aSuppLocales;
+
+    SvtLinguConfig aLinguCfg;
+    uno::Sequence< OUString > aLocaleList;
+    aLinguCfg.GetLocaleListFor( "GrammarCheckers",
+            "org.openoffice.lingu.LanguageToolGrammarChecker", aLocaleList );
+
+    auto nLength = aLocaleList.getLength();
+    m_aSuppLocales.realloc(nLength);
+    auto pArray = m_aSuppLocales.getArray();
+    auto pLocaleList = aLocaleList.getArray();
+
+    for (auto i = 0; i < nLength; i++)
+    {
+        pArray[i] = LanguageTag::convertToLocale(pLocaleList[i]);
+    }
+
+    return m_aSuppLocales;
+}
+
+// Callback to get the response data from server.
+static size_t WriteCallback(void* ptr, size_t size, size_t nmemb, void* userp)
+{
+    if (!userp)
+        return 0;
+
+    std::string* response = static_cast<std::string*>(userp);
+    size_t real_size = size * nmemb;
+    response->append(static_cast<char*>(ptr), real_size);
+    return real_size;
+}
+
+ProofreadingResult SAL_CALL LanguageToolGrammarChecker::doProofreading(
+    const OUString& aDocumentIdentifier, const OUString& aText, const Locale& 
aLocale,
+    sal_Int32 nStartOfSentencePosition, sal_Int32 
nSuggestedBehindEndOfSentencePosition,
+    const Sequence<PropertyValue>& aProperties)
+{
+    // ProofreadingResult declared here instead of parseHttpJSONResponse 
because of the early exists.
+    ProofreadingResult xRes;
+    xRes.aDocumentIdentifier = aDocumentIdentifier;
+    xRes.aText = aText;
+    xRes.aLocale = aLocale;
+    xRes.nStartOfSentencePosition = nStartOfSentencePosition;
+    xRes.nBehindEndOfSentencePosition = nSuggestedBehindEndOfSentencePosition;
+    xRes.aProperties = Sequence<PropertyValue>();
+    xRes.xProofreader = this;
+    xRes.aErrors = Sequence<SingleProofreadingError>();
+
+    if (aText.isEmpty())
+    {
+        return xRes;
+    }
+
+    if (nStartOfSentencePosition != 0)
+    {
+        return xRes;
+    }
+
+    xRes.nStartOfNextSentencePosition = aText.getLength();
+
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    if (rLanguageOpts.getEnabled() == false)
+    {
+        return xRes;
+    }
+
+    OString checkerURL = OUStringToOString(rLanguageOpts.getCheckerURL(), 
RTL_TEXTENCODING_UTF8);
+    if (checkerURL.isEmpty())
+    {
+        return xRes;
+    }
+
+    if (aProperties.getLength() > 0 && aProperties[0].Name == "Update")
+    {
+        // locale changed
+        xRes.aText = "";
+        return xRes;
+    }
+
+    sal_Int32 spaceIndex = std::min(xRes.nStartOfNextSentencePosition, 
aText.getLength() - 1);
+    while (spaceIndex < aText.getLength() && aText[spaceIndex] == ' ')
+    {
+        xRes.nStartOfNextSentencePosition += 1;
+        spaceIndex = xRes.nStartOfNextSentencePosition;
+    }
+    if (xRes.nStartOfNextSentencePosition == 
nSuggestedBehindEndOfSentencePosition
+        && spaceIndex < aText.getLength())
+    {
+        xRes.nStartOfNextSentencePosition
+            = std::min(nSuggestedBehindEndOfSentencePosition + 1, 
aText.getLength());
+    }
+    xRes.nBehindEndOfSentencePosition
+        = std::min(xRes.nStartOfNextSentencePosition, aText.getLength());
+
+    auto cachedResult = mCachedResults.find(aText);
+    if (cachedResult != mCachedResults.end())
+    {
+        xRes.aErrors = cachedResult->second;
+        return xRes;
+    }
+
+    tools::Long http_code = 0;
+    OUString langTag(aLocale.Language + "-" + aLocale.Country);
+    OString postData(OUStringToOString(
+        OUStringConcatenation("text=" + aText + "&language=" + langTag), 
RTL_TEXTENCODING_UTF8));
+    const std::string response_body
+        = makeHttpRequest(checkerURL, HTTP_METHOD::HTTP_POST, postData, 
http_code);
+
+    if (http_code != 200)
+    {
+        return xRes;
+    }
+
+    if (response_body.length() <= 0)
+    {
+        return xRes;
+    }
+
+    parseProofreadingJSONResponse(xRes, response_body);
+    // cache the result
+    mCachedResults.insert(
+        std::pair<OUString, Sequence<SingleProofreadingError>>(aText, 
xRes.aErrors));
+    return xRes;
+}
+
+/*
+    rResult is both input and output
+    aJSONBody is the response body from the HTTP Request to LanguageTool API
+*/
+void 
LanguageToolGrammarChecker::parseProofreadingJSONResponse(ProofreadingResult& 
rResult,
+                                                               
std::string_view aJSONBody)
+{
+    boost::property_tree::ptree root;
+    std::stringstream aStream(aJSONBody.data());
+    boost::property_tree::read_json(aStream, root);
+    boost::property_tree::ptree& matches = root.get_child("matches");
+    size_t matchSize = matches.size();
+
+    if (matchSize <= 0)
+    {
+        return;
+    }
+    Sequence<SingleProofreadingError> aErrors(matchSize);
+    auto pErrors = aErrors.getArray();
+    size_t i = 0;
+    for (auto it1 = matches.begin(); it1 != matches.end(); it1++, i++)
+    {
+        const boost::property_tree::ptree& match = it1->second;
+        int offset = match.get<int>("offset");
+        int length = match.get<int>("length");
+        const std::string shortMessage = match.get<std::string>("message");
+        const std::string message = match.get<std::string>("shortMessage");
+
+        // Parse the error category for Line Color
+        const boost::property_tree::ptree& rule = match.get_child("rule");
+        const boost::property_tree::ptree& ruleCategory = 
rule.get_child("category");
+        const std::string errorCategoryId = 
ruleCategory.get<std::string>("id");
+
+        OUString aShortComment(shortMessage.c_str(), shortMessage.length(), 
RTL_TEXTENCODING_UTF8);
+        OUString aFullComment(message.c_str(), message.length(), 
RTL_TEXTENCODING_UTF8);
+
+        pErrors[i].nErrorStart = offset;
+        pErrors[i].nErrorLength = length;
+        pErrors[i].nErrorType = PROOFREADING_ERROR;
+        pErrors[i].aShortComment = aShortComment;
+        pErrors[i].aFullComment = aFullComment;
+        pErrors[i].aProperties = 
lcl_GetLineColorPropertyFromErrorId(errorCategoryId);
+        ;
+        const boost::property_tree::ptree& replacements = 
match.get_child("replacements");
+        int suggestionSize = replacements.size();
+
+        if (suggestionSize <= 0)
+        {
+            continue;
+        }
+        pErrors[i].aSuggestions.realloc(std::min(suggestionSize, 
MAX_SUGGESTIONS_SIZE));
+        auto pSuggestions = pErrors[i].aSuggestions.getArray();
+        // Limit suggestions to avoid crash on context menu popup:
+        // (soffice:17251): Gdk-CRITICAL **: 17:00:21.277: 
../../../../../gdk/wayland/gdkdisplay-wayland.c:1399: Unable to create Cairo 
image
+        // surface: invalid value (typically too big) for the size of the 
input (surface, pattern, etc.)
+        int j = 0;
+        for (auto it2 = replacements.begin(); it2 != replacements.end() && j < 
MAX_SUGGESTIONS_SIZE;
+             it2++, j++)
+        {
+            const boost::property_tree::ptree& replacement = it2->second;
+            std::string replacementStr = replacement.get<std::string>("value");
+            pSuggestions[j]
+                = OUString(replacementStr.c_str(), replacementStr.length(), 
RTL_TEXTENCODING_UTF8);
+        }
+    }
+    rResult.aErrors = aErrors;
+}
+
+std::string LanguageToolGrammarChecker::makeHttpRequest(std::string_view aURL, 
HTTP_METHOD method,
+                                                        const OString& 
aPostData,
+                                                        tools::Long& 
nStatusCode)
+{
+    std::unique_ptr<CURL, std::function<void(CURL*)>> curl(curl_easy_init(),
+                                                           [](CURL* p) { 
curl_easy_cleanup(p); });
+    if (!curl)
+        return {}; // empty string
+
+    bool isPremium = false;
+    SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+    OString apiKey = OUStringToOString(rLanguageOpts.getApiKey(), 
RTL_TEXTENCODING_UTF8);
+    OString username = OUStringToOString(rLanguageOpts.getUsername(), 
RTL_TEXTENCODING_UTF8);
+    OString premiumPostData;
+    if (!apiKey.isEmpty() && !username.isEmpty())
+    {
+        isPremium = true;
+    }
+
+    std::string response_body;
+    curl_easy_setopt(curl.get(), CURLOPT_URL, aURL.data());
+
+    curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
+    // curl_easy_setopt(curl.get(), CURLOPT_VERBOSE, 1L);
+
+    curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
+    curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, 
static_cast<void*>(&response_body));
+    curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER, false);
+    curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST, false);
+    curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, CURL_TIMEOUT);
+
+    if (method == HTTP_METHOD::HTTP_POST)
+    {
+        curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);
+        if (isPremium == false)
+        {
+            curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, 
aPostData.getStr());
+        }
+        else
+        {
+            premiumPostData = aPostData + "&username=" + username + "&apiKey=" 
+ apiKey;
+            curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, 
premiumPostData.getStr());
+        }
+    }
+
+    /*CURLcode cc = */
+    curl_easy_perform(curl.get());
+    curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &nStatusCode);
+    return response_body;
+}
+
+void SAL_CALL LanguageToolGrammarChecker::ignoreRule(const OUString& 
/*aRuleIdentifier*/,
+                                                     const Locale& /*aLocale*/
+)
+{
+}
+void SAL_CALL LanguageToolGrammarChecker::resetIgnoreRules() {}
+
+OUString SAL_CALL LanguageToolGrammarChecker::getServiceDisplayName(const 
Locale& rLocale)
+{
+    std::locale loc(Translate::Create("svt", LanguageTag(rLocale)));
+    return Translate::get(STR_DESCRIPTION_LANGUAGETOOL, loc);
+}
+
+OUString SAL_CALL LanguageToolGrammarChecker::getImplementationName()
+{
+    return "org.openoffice.lingu.LanguageToolGrammarChecker";
+}
+
+sal_Bool SAL_CALL LanguageToolGrammarChecker::supportsService(const OUString& 
ServiceName)
+{
+    return cppu::supportsService(this, ServiceName);
+}
+
+Sequence<OUString> SAL_CALL 
LanguageToolGrammarChecker::getSupportedServiceNames()
+{
+    return { SN_GRAMMARCHECKER };
+}
+
+void SAL_CALL LanguageToolGrammarChecker::initialize(const Sequence<Any>& 
/*rArguments*/) {}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+lingucomponent_LanguageToolGrammarChecker_get_implementation(
+    css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+    return cppu::acquire(static_cast<cppu::OWeakObject*>(new 
LanguageToolGrammarChecker()));
+}
\ No newline at end of file
diff --git a/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx 
b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
new file mode 100644
index 000000000000..e0dadfecab68
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx
@@ -0,0 +1,91 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#pragma once
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceDisplayName.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <com/sun/star/linguistic2/XProofreader.hpp>
+#include <com/sun/star/linguistic2/ProofreadingResult.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <linguistic/misc.hxx>
+#include <string_view>
+#include <o3tl/lru_map.hxx>
+#include <tools/long.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::linguistic2;
+
+// Magical numbers
+#define MAX_CACHE_SIZE 10
+#define MAX_SUGGESTIONS_SIZE 10
+#define PROOFREADING_ERROR 2
+#define CURL_TIMEOUT 10L
+
+enum class HTTP_METHOD
+{
+    HTTP_GET,
+    HTTP_POST
+};
+
+class LanguageToolGrammarChecker
+    : public cppu::WeakImplHelper<XProofreader, XInitialization, XServiceInfo, 
XServiceDisplayName>
+{
+    Sequence<Locale> m_aSuppLocales;
+    o3tl::lru_map<OUString, Sequence<SingleProofreadingError>> mCachedResults;
+    LanguageToolGrammarChecker(const LanguageToolGrammarChecker&) = delete;
+    LanguageToolGrammarChecker& operator=(const LanguageToolGrammarChecker&) = 
delete;
+    static void parseProofreadingJSONResponse(ProofreadingResult& rResult,
+                                              std::string_view aJSONBody);
+    static std::string makeHttpRequest(std::string_view aURL, HTTP_METHOD 
method,
+                                       const OString& aPostData, tools::Long& 
nStatusCode);
+
+public:
+    LanguageToolGrammarChecker();
+    virtual ~LanguageToolGrammarChecker() override;
+
+    // XSupportedLocales
+    virtual Sequence<Locale> SAL_CALL getLocales() override;
+    virtual sal_Bool SAL_CALL hasLocale(const Locale& rLocale) override;
+
+    // XProofReader
+    virtual sal_Bool SAL_CALL isSpellChecker() override;
+    virtual ProofreadingResult SAL_CALL doProofreading(
+        const OUString& aDocumentIdentifier, const OUString& aText, const 
Locale& aLocale,
+        sal_Int32 nStartOfSentencePosition, sal_Int32 
nSuggestedBehindEndOfSentencePosition,
+        const Sequence<PropertyValue>& aProperties) override;
+
+    virtual void SAL_CALL ignoreRule(const OUString& aRuleIdentifier,
+                                     const Locale& aLocale) override;
+    virtual void SAL_CALL resetIgnoreRules() override;
+
+    // XServiceDisplayName
+    virtual OUString SAL_CALL getServiceDisplayName(const Locale& rLocale) 
override;
+
+    // XInitialization
+    virtual void SAL_CALL initialize(const Sequence<Any>& rArguments) override;
+
+    // XServiceInfo
+    virtual OUString SAL_CALL getImplementationName() override;
+    virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) 
override;
+    virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
\ No newline at end of file
diff --git a/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
index 46e020f842ea..8a9239e570f4 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
@@ -406,6 +406,36 @@
         </info>
         <value>true</value>
       </prop>
+      <group oor:name="LanguageTool">
+        <info>
+          <desc>Contains grammar checking relevant settings.</desc>
+        </info>
+          <prop oor:name="BaseURL" oor:type="xs:string">
+            <info>
+              <desc>LanguageTool Grammar Checker URL</desc>
+              <label>URL for automatic grammar checking</label>
+            </info>
+          </prop>
+          <prop oor:name="Username" oor:type="xs:string">
+            <info>
+              <desc>LanguageTool Grammar Checker Username for private 
API</desc>
+              <label>Locale List URL for automatic grammar checking</label>
+            </info>
+          </prop>
+          <prop oor:name="ApiKey" oor:type="xs:string">
+            <info>
+              <desc>LanguageTool Grammar Checker Api Key for private API</desc>
+              <label>Api Key for automatic grammar checking</label>
+            </info>
+          </prop>
+          <prop oor:name="IsEnabled" oor:type="xs:boolean" 
oor:nillable="false">
+            <info>
+              <desc>Enable or disable LanguageTool Remote Grammar 
Checker</desc>
+              <label>LanguageTool Grammar Checker</label>
+            </info>
+            <value>false</value>
+          </prop>
+      </group>
     </group>
     <group oor:name="Hyphenation">
       <info>
diff --git a/postprocess/CustomTarget_registry.mk 
b/postprocess/CustomTarget_registry.mk
index 772bd3a2ff17..6eccddcc08ed 100644
--- a/postprocess/CustomTarget_registry.mk
+++ b/postprocess/CustomTarget_registry.mk
@@ -116,6 +116,7 @@ postprocess_DEPS_lingucomponent := main
 postprocess_FILES_lingucomponent := \
        
$(SRCDIR)/lingucomponent/config/Linguistic-lingucomponent-hyphenator.xcu \
        
$(SRCDIR)/lingucomponent/config/Linguistic-lingucomponent-spellchecker.xcu \
+       
$(SRCDIR)/lingucomponent/config/Linguistic-lingucomponent-grammarchecker.xcu \
        $(SRCDIR)/lingucomponent/config/Linguistic-lingucomponent-thesaurus.xcu 
\
 
 postprocess_FILES_main := \
diff --git a/postprocess/Rdb_services.mk b/postprocess/Rdb_services.mk
index 47c340b020a7..6fe7e707c579 100644
--- a/postprocess/Rdb_services.mk
+++ b/postprocess/Rdb_services.mk
@@ -94,6 +94,7 @@ $(eval $(call gb_Rdb_add_components,services,\
        lingucomponent/source/hyphenator/hyphen/hyphen \
        lingucomponent/source/languageguessing/guesslang \
        lingucomponent/source/spellcheck/spell/spell \
+       lingucomponent/source/spellcheck/languagetool/LanguageTool \
        lingucomponent/source/thesaurus/libnth/lnth \
        lingucomponent/source/numbertext/numbertext \
        linguistic/source/lng \
diff --git a/scripting/source/stringresource/stringresource.cxx 
b/scripting/source/stringresource/stringresource.cxx
index 74d491704fcd..035d73afe68d 100644
--- a/scripting/source/stringresource/stringresource.cxx
+++ b/scripting/source/stringresource/stringresource.cxx
@@ -37,6 +37,7 @@
 #include <rtl/ustrbuf.hxx>
 #include <tools/urlobj.hxx>
 #include <i18nlangtag/languagetag.hxx>
+#include <sal/log.hxx>
 
 using namespace ::com::sun::star;
 using namespace ::com::sun::star::lang;
diff --git a/svtools/Library_svt.mk b/svtools/Library_svt.mk
index 625b8e38f9cb..94b4f8848ba5 100644
--- a/svtools/Library_svt.mk
+++ b/svtools/Library_svt.mk
@@ -82,6 +82,7 @@ $(eval $(call gb_Library_add_exception_objects,svt,\
     svtools/source/config/extcolorcfg \
     svtools/source/config/fontsubstconfig \
     svtools/source/config/htmlcfg \
+    svtools/source/config/languagetoolcfg \
     svtools/source/config/itemholder2 \
     svtools/source/config/miscopt \
     svtools/source/config/slidesorterbaropt \
diff --git a/svtools/source/config/languagetoolcfg.cxx 
b/svtools/source/config/languagetoolcfg.cxx
new file mode 100644
index 000000000000..9f81c8e787f1
--- /dev/null
+++ b/svtools/source/config/languagetoolcfg.cxx
@@ -0,0 +1,164 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <sal/config.h>
+#include <svtools/languagetoolcfg.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/debug.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+struct LanguageToolOptions_Impl
+{
+    OUString sBaseURL;
+    OUString sUsername;
+    OUString sApiKey;
+    bool bEnabled;
+};
+
+const Sequence<OUString>& SvxLanguageToolOptions::GetPropertyNames()
+{
+    static Sequence<OUString> const aNames{
+        "LanguageTool/BaseURL",
+        "LanguageTool/Username",
+        "LanguageTool/ApiKey",
+        "LanguageTool/IsEnabled",
+    };
+    return aNames;
+}
+
+const OUString& SvxLanguageToolOptions::getBaseURL() const { return 
pImpl->sBaseURL; }
+
+void SvxLanguageToolOptions::setBaseURL(const OUString& rVal)
+{
+    pImpl->sBaseURL = rVal;
+    SetModified();
+}
+
+const OUString& SvxLanguageToolOptions::getUsername() const { return 
pImpl->sUsername; }
+
+void SvxLanguageToolOptions::setUsername(const OUString& rVal)
+{
+    pImpl->sUsername = rVal;
+    SetModified();
+}
+
+OUString SvxLanguageToolOptions::getLocaleListURL() const { return 
pImpl->sBaseURL + "/languages"; }
+
+OUString SvxLanguageToolOptions::getCheckerURL() const { return 
pImpl->sBaseURL + "/check"; }
+
+const OUString& SvxLanguageToolOptions::getApiKey() const { return 
pImpl->sApiKey; }
+
+void SvxLanguageToolOptions::setApiKey(const OUString& rVal)
+{
+    pImpl->sApiKey = rVal;
+    SetModified();
+}
+
+bool SvxLanguageToolOptions::getEnabled() const { return pImpl->bEnabled; }
+
+void SvxLanguageToolOptions::setEnabled(bool bEnabled)
+{
+    pImpl->bEnabled = bEnabled;
+    SetModified();
+}
+
+namespace
+{
+class theSvxLanguageToolOptions
+    : public rtl::Static<SvxLanguageToolOptions, theSvxLanguageToolOptions>
+{
+};
+}
+
+SvxLanguageToolOptions& SvxLanguageToolOptions::Get() { return 
theSvxLanguageToolOptions::get(); }
+
+SvxLanguageToolOptions::SvxLanguageToolOptions()
+    : ConfigItem("Office.Linguistic/GrammarChecking")
+    , pImpl(new LanguageToolOptions_Impl)
+{
+    Load(GetPropertyNames());
+}
+
+SvxLanguageToolOptions::~SvxLanguageToolOptions() {}
+void SvxLanguageToolOptions::Notify(const css::uno::Sequence<OUString>&)
+{
+    Load(GetPropertyNames());
+}
+
+void SvxLanguageToolOptions::Load(const css::uno::Sequence<OUString>& aNames)
+{
+    Sequence<Any> aValues = GetProperties(aNames);
+    const Any* pValues = aValues.getConstArray();
+    DBG_ASSERT(aValues.getLength() == aNames.getLength(), "GetProperties 
failed");
+    if (aValues.getLength() != aNames.getLength())
+        return;
+    for (int nProp = 0; nProp < aNames.getLength(); nProp++)
+    {
+        if (!pValues[nProp].hasValue())
+            continue;
+        switch (nProp)
+        {
+            case 0:
+                pValues[nProp] >>= pImpl->sBaseURL;
+                break;
+            case 1:
+                pValues[nProp] >>= pImpl->sUsername;
+                break;
+            case 2:
+                pValues[nProp] >>= pImpl->sApiKey;
+                break;
+            case 3:
+                pValues[nProp] >>= pImpl->bEnabled;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+void SvxLanguageToolOptions::ImplCommit()
+{
+    const Sequence<OUString>& aNames = GetPropertyNames();
+    Sequence<Any> aValues(aNames.getLength());
+    Any* pValues = aValues.getArray();
+    for (int nProp = 0; nProp < aNames.getLength(); nProp++)
+    {
+        switch (nProp)
+        {
+            case 0:
+                pValues[nProp] <<= pImpl->sBaseURL;
+                break;
+            case 1:
+                pValues[nProp] <<= pImpl->sUsername;
+                break;
+            case 2:
+                pValues[nProp] <<= pImpl->sApiKey;
+                break;
+            case 3:
+                pValues[nProp] <<= pImpl->bEnabled;
+                break;
+            default:
+                break;
+        }
+    }
+    PutProperties(aNames, aValues);
+}
\ No newline at end of file
diff --git a/unotools/source/config/lingucfg.cxx 
b/unotools/source/config/lingucfg.cxx
index f37fffb5734a..f65340253401 100644
--- a/unotools/source/config/lingucfg.cxx
+++ b/unotools/source/config/lingucfg.cxx
@@ -929,6 +929,27 @@ bool SvtLinguConfig::GetSupportedDictionaryFormatsFor(
     return bSuccess;
 }
 
+bool SvtLinguConfig::GetLocaleListFor( const OUString &rSetName, const 
OUString &rSetEntry, css::uno::Sequence< OUString > &rLocaleList ) const
+{
+    if (rSetName.isEmpty() || rSetEntry.isEmpty())
+        return false;
+    bool bSuccess = false;
+    try
+    {
+        uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), 
uno::UNO_QUERY_THROW );
+        xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+        xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY_THROW );
+        xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY_THROW );
+        if (xNA->getByName( "Locales" ) >>= rLocaleList)
+            bSuccess = true;
+        DBG_ASSERT( rLocaleList.hasElements(), "Locale list is empty" );
+    }
+    catch (uno::Exception &)
+    {
+    }
+    return bSuccess;
+}
+
 static bool lcl_GetFileUrlFromOrigin(
     OUString /*out*/ &rFileUrl,
     const OUString &rOrigin )

Reply via email to