summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMert Tumer <mert.tumer@collabora.com>2022-05-10 13:06:06 +0300
committerChristian Lohmaier <lohmaier+LibreOffice@googlemail.com>2022-06-10 13:52:17 +0200
commitd952df361a5e190246d7a0738ae5347b82cd57b5 (patch)
tree8ab9d944b34ea93b8cdc69494f51fd41e4636ed5
parent044df303665606c778748fcfc0bfc47ce65ee914 (diff)
LanguageTool Grammar Checker implementation
Signed-off-by: Mert Tumer <mert.tumer@collabora.com> Change-Id: I275cbea668afc5beb5147370119631df8b6a2d46 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135178 Tested-by: Jenkins Reviewed-by: Christian Lohmaier <lohmaier+LibreOffice@googlemail.com>
-rw-r--r--Repository.mk1
-rw-r--r--cui/Library_cui.mk1
-rw-r--r--cui/UIConfig_cui.mk1
-rw-r--r--cui/inc/treeopt.hrc3
-rw-r--r--cui/source/options/optlanguagetool.cxx76
-rw-r--r--cui/source/options/optlanguagetool.hxx44
-rw-r--r--cui/source/options/treeopt.cxx2
-rw-r--r--cui/uiconfig/ui/langtoolconfigpage.ui240
-rw-r--r--include/sfx2/pageids.hxx1
-rw-r--r--include/svtools/languagetoolcfg.hxx59
-rw-r--r--include/svtools/strings.hrc1
-rw-r--r--lingucomponent/Library_LanguageTool.mk48
-rw-r--r--lingucomponent/Module_lingucomponent.mk1
-rw-r--r--lingucomponent/source/spellcheck/languagetool/LanguageTool.component26
-rw-r--r--lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx407
-rw-r--r--lingucomponent/source/spellcheck/languagetool/languagetoolimp.hxx91
-rw-r--r--officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs30
-rw-r--r--scripting/source/stringresource/stringresource.cxx1
-rw-r--r--svtools/Library_svt.mk1
-rw-r--r--svtools/source/config/languagetoolcfg.cxx164
20 files changed, 1197 insertions, 1 deletions
diff --git a/Repository.mk b/Repository.mk
index 4004cbe142df..5ee1546b2504 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -368,6 +368,7 @@ endif
$(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo, \
avmedia \
+ LanguageTool \
$(call gb_Helper_optional,AVMEDIA, \
$(if $(filter MACOSX,$(OS)),\
avmediaMacAVF \
diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index 8f8e73afe6cd..cb26653b395d 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -187,6 +187,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 eda70a1dd29c..cdedcc15a857 100644
--- a/cui/UIConfig_cui.mk
+++ b/cui/UIConfig_cui.mk
@@ -143,6 +143,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 a3033143983b..f4fc28c5725b 100644
--- a/cui/inc/treeopt.hrc
+++ b/cui/inc/treeopt.hrc
@@ -55,7 +55,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 812d24511a5a..5973f422a7ae 100644
--- a/cui/source/options/treeopt.cxx
+++ b/cui/source/options/treeopt.cxx
@@ -62,6 +62,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>
@@ -296,6 +297,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 22c8c6a3e16f..238856e13ccc 100644
--- a/include/svtools/strings.hrc
+++ b/include/svtools/strings.hrc
@@ -342,5 +342,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/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 9f19dcedbc67..13f1a829d98a 100644
--- a/lingucomponent/Module_lingucomponent.mk
+++ b/lingucomponent/Module_lingucomponent.mk
@@ -28,6 +28,7 @@ endif
$(eval $(call gb_Module_add_targets,lingucomponent,\
$(if $(filter iOS MACOSX,$(OS)),Library_MacOSXSpell) \
Library_numbertext \
+ Library_LanguageTool \
))
# vim: set noet sw=4 ts=4:
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..06b4fcb64175
--- /dev/null
+++ b/lingucomponent/source/spellcheck/languagetool/languagetoolimp.cxx
@@ -0,0 +1,407 @@
+/* -*- 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 <sal/log.hxx>
+#include <svtools/languagetoolcfg.hxx>
+#include <tools/color.hxx>
+#include <tools/long.hxx>
+#include <com/sun/star/uno/Any.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()
+{
+ if (m_aSuppLocales.hasElements())
+ return m_aSuppLocales;
+ SvxLanguageToolOptions& rLanguageOpts = SvxLanguageToolOptions::Get();
+ OString localeUrl = OUStringToOString(rLanguageOpts.getLocaleListURL(), RTL_TEXTENCODING_UTF8);
+ if (localeUrl.isEmpty())
+ {
+ return m_aSuppLocales;
+ }
+ tools::Long statusCode = 0;
+ std::string response = makeHttpRequest(localeUrl, HTTP_METHOD::HTTP_GET, OString(), statusCode);
+ if (statusCode != 200)
+ {
+ return m_aSuppLocales;
+ }
+ if (response.empty())
+ {
+ return m_aSuppLocales;
+ }
+ boost::property_tree::ptree root;
+ std::stringstream aStream(response);
+ boost::property_tree::read_json(aStream, root);
+
+ size_t length = root.size();
+ m_aSuppLocales.realloc(length);
+ auto pArray = m_aSuppLocales.getArray();
+ int i = 0;
+ for (auto it = root.begin(); it != root.end(); it++, i++)
+ {
+ boost::property_tree::ptree& localeItem = it->second;
+ const std::string longCode = localeItem.get<std::string>("longCode");
+ Locale aLocale = LanguageTag::convertToLocale(
+ OUString(longCode.c_str(), longCode.length(), RTL_TEXTENCODING_UTF8));
+ pArray[i] = aLocale;
+ }
+ 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 400f749de315..fe9365d036dc 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
@@ -377,6 +377,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/scripting/source/stringresource/stringresource.cxx b/scripting/source/stringresource/stringresource.cxx
index d9a2e377e5ec..06b52b0377c7 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 643481368caa..5c814cfcb7e1 100644
--- a/svtools/Library_svt.mk
+++ b/svtools/Library_svt.mk
@@ -83,6 +83,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