Rebased ref, commits from common ancestor:
commit 140ac17f44f51edf5929d617bc0d10a8ee4c5588
Author:     Mert Tumer <mert.tu...@collabora.com>
AuthorDate: Tue Jul 5 12:03:27 2022 +0300
Commit:     Mert Tumer <mert.tu...@collabora.com>
CommitDate: Wed Jul 20 22:25:26 2022 +0300

    new uno command uno:Translate with deepl api
    
    New Uno command added for translation
    right now it is only using deepl translation api
    
    There's a section in the options > language settings
    for setting up the api url and auth key
    
    uno:Translate is a menu button under Format tab
    which will bring up Language Selection dialog for translation.
    
    DeepL can accept html as the input for translation, this new
    feature leverages that by exporting paragraphs/selections to
    html and paste them back without losing the formatting (in theory)
    This works good in general but we may lose formatting in very complex
    styled sentences.
    
    Translation works in two ways;
    1) Whole document
    when there is no selection, it assumes that we want to translate whole
    document. Each paragraphs is sent one by one so that the output timeout
    can be minimum for each paragraph.
    2) Selection
    
    Signed-off-by: Mert Tumer <mert.tu...@collabora.com>
    Change-Id: Ia2d3ab2f6757faf565b939e1d670a7dedac33390

diff --git a/cui/Library_cui.mk b/cui/Library_cui.mk
index d455a64ab266..c4c0a52b2ef4 100644
--- a/cui/Library_cui.mk
+++ b/cui/Library_cui.mk
@@ -181,6 +181,7 @@ $(eval $(call gb_Library_add_exception_objects,cui,\
     cui/source/options/optgenrl \
     cui/source/options/opthtml \
     cui/source/options/optlanguagetool \
+    cui/source/options/optdeepl \
     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 806779daaa9d..0ed879e2b228 100644
--- a/cui/UIConfig_cui.mk
+++ b/cui/UIConfig_cui.mk
@@ -139,6 +139,7 @@ $(eval $(call gb_UIConfig_add_uifiles,cui,\
        cui/uiconfig/ui/optgeneralpage \
        cui/uiconfig/ui/opthtmlpage \
        cui/uiconfig/ui/langtoolconfigpage \
+       cui/uiconfig/ui/deepltabpage \
        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 952b79ea92d4..6d5bc4004a53 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", "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", "LanguageTool Server Settings"),  
RID_SVXPAGE_LANGTOOL_OPTIONS }
+    { NC_("SID_LANGUAGE_OPTIONS_RES", "LanguageTool Server Settings"),  
RID_SVXPAGE_LANGTOOL_OPTIONS },
+    { NC_("SID_LANGUAGE_OPTIONS_RES", "DeepL Server Settings"),  
RID_SVXPAGE_DEEPL_OPTIONS }
 };
 
 const std::pair<TranslateId, sal_uInt16> SID_INET_DLG_RES[] =
diff --git a/cui/source/options/optdeepl.cxx b/cui/source/options/optdeepl.cxx
new file mode 100644
index 000000000000..94b48ccc2f43
--- /dev/null
+++ b/cui/source/options/optdeepl.cxx
@@ -0,0 +1,55 @@
+/* -*- 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 "optdeepl.hxx"
+#include <svtools/deeplcfg.hxx>
+
+OptDeeplTabPage::OptDeeplTabPage(weld::Container* pPage,
+                                               weld::DialogController* 
pController,
+                                               const SfxItemSet& rSet)
+    : SfxTabPage(pPage, pController, "cui/ui/deepltabpage.ui", "OptDeeplPage", 
&rSet)
+    , m_xAPIUrl(m_xBuilder->weld_entry("apiurl"))
+    , m_xAuthKey(m_xBuilder->weld_entry("authkey"))
+{
+
+}
+
+OptDeeplTabPage::~OptDeeplTabPage() {}
+
+void OptDeeplTabPage::Reset(const SfxItemSet*)
+{
+    SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+    m_xAPIUrl->set_text(rDeeplOptions.getAPIUrl());
+    m_xAuthKey->set_text(rDeeplOptions.getAuthKey());
+}
+
+bool OptDeeplTabPage::FillItemSet(SfxItemSet*)
+{
+    SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+    rDeeplOptions.setAPIUrl(m_xAPIUrl->get_text());
+    rDeeplOptions.setAuthKey(m_xAuthKey->get_text());
+    return false;
+}
+
+std::unique_ptr<SfxTabPage> OptDeeplTabPage::Create(weld::Container* pPage,
+                                                           
weld::DialogController* pController,
+                                                           const SfxItemSet* 
rAttrSet)
+{
+    return std::make_unique<OptDeeplTabPage>(pPage, pController, *rAttrSet);
+}
diff --git a/cui/source/options/optdeepl.hxx b/cui/source/options/optdeepl.hxx
new file mode 100644
index 000000000000..124f9494c4aa
--- /dev/null
+++ b/cui/source/options/optdeepl.hxx
@@ -0,0 +1,37 @@
+/* -*- 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 OptDeeplTabPage : public SfxTabPage
+{
+public:
+    OptDeeplTabPage(weld::Container* pPage, weld::DialogController* 
pController,
+                           const SfxItemSet& rSet);
+    virtual ~OptDeeplTabPage() 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_xAPIUrl;
+    std::unique_ptr<weld::Entry> m_xAuthKey;
+};
diff --git a/cui/source/options/treeopt.cxx b/cui/source/options/treeopt.cxx
index e3029ca45eaf..1ab88a7e5381 100644
--- a/cui/source/options/treeopt.cxx
+++ b/cui/source/options/treeopt.cxx
@@ -64,6 +64,7 @@
 #include <treeopt.hxx>
 #include "optbasic.hxx"
 #include "optlanguagetool.hxx"
+#include "optdeepl.hxx"
 
 #include <com/sun/star/awt/XContainerWindowEventHandler.hpp>
 #include <com/sun/star/awt/ContainerWindowProvider.hpp>
@@ -299,6 +300,7 @@ static std::unique_ptr<SfxTabPage> 
CreateGeneralTabPage(sal_uInt16 nId, weld::Co
         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_DEEPL_OPTIONS:             fnCreate = 
&OptDeeplTabPage::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/deepltabpage.ui b/cui/uiconfig/ui/deepltabpage.ui
new file mode 100644
index 000000000000..f17ff52a787e
--- /dev/null
+++ b/cui/uiconfig/ui/deepltabpage.ui
@@ -0,0 +1,124 @@
+<?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="OptDeeplPage">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <child>
+      <object class="GtkGrid" id="grid1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="column_homogeneous">True</property>
+        <child>
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">start</property>
+            <property name="xpad">5</property>
+            <property name="ypad">5</property>
+            <property name="label" translatable="yes" 
context="deepltabpage|label1">DeepL API Options</property>
+            <property name="use_underline">True</property>
+            <property name="mnemonic_widget">grid1</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLinkButton" id="privacy">
+            <property name="label" translatable="yes" 
context="deepltabpage|privacy">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="relief">none</property>
+            <property name="uri">https://www.deepl.com/privacy/</property>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">5</property>
+            <property name="margin_right">5</property>
+            <property name="margin_top">5</property>
+            <property name="margin_bottom">5</property>
+            <property name="row_spacing">6</property>
+            <property name="column_spacing">5</property>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="margin_left">5</property>
+                <property name="label" translatable="yes" 
context="deepltabpage|privacy">API URL:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">apiurl</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="margin_right">5</property>
+                <property name="label" translatable="yes" 
context="deepltabpage|label3">Auth Key:</property>
+                <property name="use_underline">True</property>
+                <property name="mnemonic_widget">authkey</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="apiurl">
+                <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="GtkEntry" id="authkey">
+                <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">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="left_attach">0</property>
+            <property name="top_attach">2</property>
+          </packing>
+        </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/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 07276ebd1eea..0bab549c62fd 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -128,6 +128,7 @@
 #include <svtools/ctrltool.hxx>
 #include <svtools/langtab.hxx>
 #include <svtools/languagetoolcfg.hxx>
+#include <svtools/deeplcfg.hxx>
 #include <vcl/fontcharmap.hxx>
 #include <vcl/graphicfilter.hxx>
 #ifdef IOS
@@ -6597,6 +6598,28 @@ void setLanguageToolConfig()
     }
 }
 
+void setDeeplConfig()
+{
+    const char* pAPIUrlString = ::getenv("DEEPL_API_URL");
+    const char* pAuthKeyString = ::getenv("DEEPL_AUTH_KEY");
+    if (pAPIUrlString && pAuthKeyString)
+    {
+        OUString aAPIUrl = OStringToOUString(pAPIUrlString, 
RTL_TEXTENCODING_UTF8);
+        OUString aAuthKey = OStringToOUString(pAuthKeyString, 
RTL_TEXTENCODING_UTF8);
+        try
+        {
+            SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+            rDeeplOptions.setAPIUrl(aAPIUrl);
+            rDeeplOptions.setAuthKey(aAuthKey);
+        }
+        catch(uno::Exception const& rException)
+        {
+            SAL_WARN("lok", "Failed to set Deepl API settings: " << 
rException.Message);
+        }
+    }
+}
+
+
 }
 
 static int lo_initialize(LibreOfficeKit* pThis, const char* pAppPath, const 
char* pUserProfileUrl)
@@ -6912,6 +6935,7 @@ static int lo_initialize(LibreOfficeKit* pThis, const 
char* pAppPath, const char
 
     setCertificateDir();
     setLanguageToolConfig();
+    setDeeplConfig();
 
     if (bNotebookbar)
     {
diff --git a/include/sfx2/pageids.hxx b/include/sfx2/pageids.hxx
index 9371848b784a..1464ec40c55a 100644
--- a/include/sfx2/pageids.hxx
+++ b/include/sfx2/pageids.hxx
@@ -57,6 +57,7 @@
 #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)
+#define RID_SVXPAGE_DEEPL_OPTIONS           (RID_SVX_START + 211)
 
 // Resource-Id's ------------------------------------------------------------
 
diff --git a/include/sfx2/sfxsids.hrc b/include/sfx2/sfxsids.hrc
index 39a3ec5bf9d6..1937368ba2b9 100644
--- a/include/sfx2/sfxsids.hrc
+++ b/include/sfx2/sfxsids.hrc
@@ -208,6 +208,7 @@ class SvxSearchItem;
 #define SID_VIEW_DATA_SOURCE_BROWSER        (SID_SFX_START + 1660)
 #define SID_UNPACK                          (SID_SFX_START + 1662)
 // (SID_SFX_START + 1663) used further down
+#define SID_ATTR_TARGETLANG_STR             (SID_SFX_START + 1664)
     // FREE
 #define SID_OUTPUTSTREAM                    (SID_SFX_START + 1666)
 #define SID_IMAGE_ORIENTATION               (SID_SFX_START + 1667)
diff --git a/include/svtools/deeplcfg.hxx b/include/svtools/deeplcfg.hxx
new file mode 100644
index 000000000000..b8e8ca4d7cd4
--- /dev/null
+++ b/include/svtools/deeplcfg.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 DeeplOptions_Impl;
+
+class SVT_DLLPUBLIC SvxDeeplOptions final : public utl::ConfigItem
+{
+public:
+    SvxDeeplOptions();
+    virtual ~SvxDeeplOptions() override;
+
+    virtual void Notify(const css::uno::Sequence<OUString>& _rPropertyNames) 
override;
+    static SvxDeeplOptions& Get();
+
+    const OUString getAPIUrl() const;
+    void setAPIUrl(const OUString& rVal);
+
+    const OUString& getAuthKey() const;
+    void setAuthKey(const OUString& rVal);
+
+private:
+    std::unique_ptr<DeeplOptions_Impl> pImpl;
+    void Load(const css::uno::Sequence<OUString>& rPropertyNames);
+    virtual void ImplCommit() override;
+    static const Sequence<OUString>& GetPropertyNames();
+};
diff --git a/include/svx/svxids.hrc b/include/svx/svxids.hrc
index 573f036128c8..f8e162a973a6 100644
--- a/include/svx/svxids.hrc
+++ b/include/svx/svxids.hrc
@@ -570,7 +570,7 @@ class SdrAngleItem;
 #define SID_FM_FILECONTROL                              ( SID_SVX_START + 605 )
 //( SID_SVX_START + 606 ) is used by SID_DRAWTBX_REDACTED_EXPORT
 #define SID_FM_NAVIGATIONBAR                            ( SID_SVX_START + 607 )
-//FREE
+#define SID_FM_TRANSLATE                                ( SID_SVX_START + 608 )
 //FREE
 #define SID_FM_DELETEROWS                               ( SID_SVX_START + 610 )
 //FREE
diff --git a/include/vcl/htmltransferable.hxx b/include/vcl/htmltransferable.hxx
new file mode 100644
index 000000000000..d7f8f71bdb46
--- /dev/null
+++ b/include/vcl/htmltransferable.hxx
@@ -0,0 +1,50 @@
+/* -*- 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/datatransfer/XTransferable.hpp>
+#include <cppuhelper/weak.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/dllapi.h>
+
+namespace vcl::unohelper
+{
+class VCL_DLLPUBLIC HtmlTransferable final : public 
css::datatransfer::XTransferable,
+                                             public ::cppu::OWeakObject
+{
+private:
+    OString data;
+
+public:
+    HtmlTransferable(OString sData);
+    virtual ~HtmlTransferable() override;
+
+    // css::uno::XInterface
+    css::uno::Any SAL_CALL queryInterface(const css::uno::Type& rType) 
override;
+    void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); }
+    void SAL_CALL release() noexcept override { OWeakObject::release(); }
+
+    // css::datatransfer::XTransferable
+    css::uno::Any SAL_CALL getTransferData(const 
css::datatransfer::DataFlavor& aFlavor) override;
+    css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL 
getTransferDataFlavors() override;
+    sal_Bool SAL_CALL isDataFlavorSupported(const 
css::datatransfer::DataFlavor& aFlavor) override;
+};
+
+} // namespace vcl::unohelper
diff --git 
a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu 
b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
index 37996759a90d..59c46c8472b4 100644
--- a/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
+++ b/officecfg/registry/data/org/openoffice/Office/UI/WriterCommands.xcu
@@ -1333,6 +1333,14 @@
           <value>1</value>
         </prop>
       </node>
+      <node oor:name=".uno:Translate" oor:op="replace">
+        <prop oor:name="Label" oor:type="xs:string">
+          <value xml:lang="en-US">Translate...</value>
+        </prop>
+        <prop oor:name="Properties" oor:type="xs:int">
+          <value>1</value>
+        </prop>
+      </node>
       <node oor:name=".uno:FormatColumns" oor:op="replace">
         <prop oor:name="Label" oor:type="xs:string">
           <value xml:lang="en-US">Co~lumns...</value>
diff --git a/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs 
b/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
index a3fc5f1296c8..c80db6c6aa09 100644
--- a/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
+++ b/officecfg/registry/schema/org/openoffice/Office/Linguistic.xcs
@@ -444,6 +444,28 @@
           </prop>
       </group>
     </group>
+    <group oor:name="Translation">
+      <info>
+        <desc>Contains translation relevant settings.</desc>
+      </info>
+      <group oor:name="Deepl">
+        <info>
+          <desc>Contains DeepL API relevant settings.</desc>
+        </info>
+        <prop oor:name="ApiURL" oor:type="xs:string">
+          <info>
+            <desc>Deepl Translator API URL</desc>
+            <label>URL for the Deepl translator api</label>
+          </info>
+        </prop>
+        <prop oor:name="AuthKey" oor:type="xs:string">
+          <info>
+            <desc>Deepl Translator API URL Authkey</desc>
+            <label>Auth key for the Deepl translator api</label>
+          </info>
+        </prop>
+      </group>
+    </group>
     <group oor:name="Hyphenation">
       <info>
         <desc>Contains hyphenation relevant settings.</desc>
diff --git a/svtools/Library_svt.mk b/svtools/Library_svt.mk
index 94b4f8848ba5..895c34780443 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/fontsubstconfig \
     svtools/source/config/htmlcfg \
     svtools/source/config/languagetoolcfg \
+    svtools/source/config/deeplcfg \
     svtools/source/config/itemholder2 \
     svtools/source/config/miscopt \
     svtools/source/config/slidesorterbaropt \
diff --git a/svtools/source/config/deeplcfg.cxx 
b/svtools/source/config/deeplcfg.cxx
new file mode 100644
index 000000000000..edace497a482
--- /dev/null
+++ b/svtools/source/config/deeplcfg.cxx
@@ -0,0 +1,128 @@
+/* -*- 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/deeplcfg.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/debug.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+struct DeeplOptions_Impl
+{
+    OUString sAPIUrl;
+    OUString sAuthKey;
+};
+
+const Sequence<OUString>& SvxDeeplOptions::GetPropertyNames()
+{
+    static Sequence<OUString> const aNames{
+        "Deepl/ApiURL",
+        "Deepl/AuthKey",
+    };
+    return aNames;
+}
+
+const OUString SvxDeeplOptions::getAPIUrl() const { return pImpl->sAPIUrl; }
+
+void SvxDeeplOptions::setAPIUrl(const OUString& rVal)
+{
+    pImpl->sAPIUrl = rVal;
+    SetModified();
+}
+
+const OUString& SvxDeeplOptions::getAuthKey() const { return pImpl->sAuthKey; }
+
+void SvxDeeplOptions::setAuthKey(const OUString& rVal)
+{
+    pImpl->sAuthKey = rVal;
+    SetModified();
+}
+
+namespace
+{
+class theSvxDeeplOptions
+    : public rtl::Static<SvxDeeplOptions, theSvxDeeplOptions>
+{
+};
+}
+
+SvxDeeplOptions& SvxDeeplOptions::Get() { return theSvxDeeplOptions::get(); }
+
+SvxDeeplOptions::SvxDeeplOptions()
+    : ConfigItem("Office.Linguistic/Translation")
+    , pImpl(new DeeplOptions_Impl)
+{
+    Load(GetPropertyNames());
+}
+
+SvxDeeplOptions::~SvxDeeplOptions() {}
+void SvxDeeplOptions::Notify(const css::uno::Sequence<OUString>&)
+{
+    Load(GetPropertyNames());
+}
+
+void SvxDeeplOptions::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->sAPIUrl;
+                break;
+            case 1:
+                pValues[nProp] >>= pImpl->sAuthKey;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+void SvxDeeplOptions::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->sAPIUrl;
+                break;
+            case 1:
+                pValues[nProp] <<= pImpl->sAuthKey;
+                break;
+            default:
+                break;
+        }
+    }
+    PutProperties(aNames, aValues);
+}
\ No newline at end of file
diff --git a/svx/sdi/svx.sdi b/svx/sdi/svx.sdi
index 9ab00de7e881..ac886aaa0cd4 100644
--- a/svx/sdi/svx.sdi
+++ b/svx/sdi/svx.sdi
@@ -1596,6 +1596,23 @@ SfxBoolItem NavigationBar SID_FM_NAVIGATIONBAR
     GroupId = SfxGroupId::Controls;
 ]
 
+SfxBoolItem Translate SID_FM_TRANSLATE
+(SfxStringItem TargetLang SID_ATTR_TARGETLANG_STR)
+[
+    AutoUpdate = FALSE,
+    FastCall = TRUE,
+    ReadOnlyDoc = FALSE,
+    Toggle = FALSE,
+    Container = FALSE,
+    RecordAbsolute = FALSE,
+    RecordPerSet;
+
+    AccelConfig = FALSE,
+    MenuConfig = FALSE,
+    ToolBoxConfig = FALSE,
+    GroupId = SfxGroupId::Format;
+]
+
 
 SfxBoolItem Combobox SID_INSERT_COMBOBOX
 
diff --git a/sw/Library_sw.mk b/sw/Library_sw.mk
index a49d53d0509f..5eaf52ed9be7 100644
--- a/sw/Library_sw.mk
+++ b/sw/Library_sw.mk
@@ -91,6 +91,7 @@ $(eval $(call gb_Library_use_externals,sw,\
        icuuc \
        icu_headers \
        libxml2 \
+    curl \
 ))
 
 $(eval $(call gb_Library_add_exception_objects,sw,\
@@ -687,6 +688,7 @@ $(eval $(call gb_Library_add_exception_objects,sw,\
     sw/source/uibase/shells/grfsh \
     sw/source/uibase/shells/grfshex \
     sw/source/uibase/shells/langhelper \
+    sw/source/uibase/shells/translatehelper \
     sw/source/uibase/shells/listsh \
     sw/source/uibase/shells/mediash \
     sw/source/uibase/shells/navsh \
diff --git a/sw/Library_swui.mk b/sw/Library_swui.mk
index 4c1d9614f56c..f4cd9cbf5432 100644
--- a/sw/Library_swui.mk
+++ b/sw/Library_swui.mk
@@ -150,6 +150,7 @@ $(eval $(call gb_Library_add_exception_objects,swui,\
     sw/source/ui/misc/pgfnote \
     sw/source/ui/misc/pggrid \
     sw/source/ui/misc/srtdlg \
+    sw/source/ui/misc/translatelangselect \
     sw/source/ui/misc/swmodalredlineacceptdlg \
     sw/source/ui/misc/titlepage \
     sw/source/ui/table/colwd \
diff --git a/sw/UIConfig_swriter.mk b/sw/UIConfig_swriter.mk
index 73abd66dee9b..7d2fc662ae22 100644
--- a/sw/UIConfig_swriter.mk
+++ b/sw/UIConfig_swriter.mk
@@ -178,6 +178,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/swriter,\
        sw/uiconfig/swriter/ui/insertautotextdialog \
        sw/uiconfig/swriter/ui/insertbookmark \
        sw/uiconfig/swriter/ui/insertbreak \
+       sw/uiconfig/swriter/ui/translationdialog \
        sw/uiconfig/swriter/ui/insertcaption \
        sw/uiconfig/swriter/ui/insertdbcolumnsdialog \
        sw/uiconfig/swriter/ui/insertfootnote \
diff --git a/sw/inc/swabstdlg.hxx b/sw/inc/swabstdlg.hxx
index 0963de35da21..a841ce5d0ffd 100644
--- a/sw/inc/swabstdlg.hxx
+++ b/sw/inc/swabstdlg.hxx
@@ -375,6 +375,18 @@ public:
     virtual sal_uInt16          GetRestartPage() const = 0;
 };
 
+class SwLanguageListItem;
+
+class AbstractSwTranslateLangSelectDlg
+{
+protected:
+    virtual ~AbstractSwTranslateLangSelectDlg() = default;
+public:
+    virtual std::shared_ptr<weld::DialogController> getDialogController() = 0;
+    virtual std::optional<SwLanguageListItem> GetSelectedLanguage() = 0;
+};
+
+
 class SwAbstractDialogFactory
 {
 public:
@@ -398,6 +410,7 @@ public:
     CreateSwContentControlListItemDlg(weld::Window* pParent, 
SwContentControlListItem& rItem) = 0;
 
     virtual std::shared_ptr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window 
*pParent, SwWrtShell &rSh) = 0;
+    virtual std::shared_ptr<AbstractSwTranslateLangSelectDlg> 
CreateSwTranslateLangSelectDlg(weld::Window *pParent, SwWrtShell &rSh) = 0;
     virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) = 0;
     virtual VclPtr<SfxAbstractTabDialog>  CreateSwCharDlg(weld::Window* 
pParent, SwView& pVw, const SfxItemSet& rCoreSet,
         SwCharDlgMode nDialogMode, const OUString* pFormatStr = nullptr) = 0;
diff --git a/sw/sdi/_textsh.sdi b/sw/sdi/_textsh.sdi
index 84750b937715..128591f1673d 100644
--- a/sw/sdi/_textsh.sdi
+++ b/sw/sdi/_textsh.sdi
@@ -1796,5 +1796,11 @@ interface BaseText
         StateMethod = GetState ;
     ]
 
+    SID_FM_TRANSLATE
+    [
+        ExecMethod = Execute ;
+        StateMethod = GetState ;
+    ]
+
 }  // end of interface text
 
diff --git a/sw/source/ui/dialog/swdlgfact.cxx 
b/sw/source/ui/dialog/swdlgfact.cxx
index 17f0ed1d3a03..af968ea90cab 100644
--- a/sw/source/ui/dialog/swdlgfact.cxx
+++ b/sw/source/ui/dialog/swdlgfact.cxx
@@ -90,6 +90,7 @@
 #include <uiborder.hxx>
 #include <mmresultdialogs.hxx>
 #include <formatlinebreak.hxx>
+#include <translatelangselect.hxx>
 
 using namespace ::com::sun::star;
 using namespace css::frame;
@@ -802,6 +803,12 @@ sal_uInt16 AbstractMailMergeWizard_Impl::GetRestartPage() 
const
     return m_xDlg->GetRestartPage();
 }
 
+std::optional<SwLanguageListItem> 
AbstractSwTranslateLangSelectDlg_Impl::GetSelectedLanguage()
+{
+    SwTranslateLangSelectDlg* pDlg = 
dynamic_cast<SwTranslateLangSelectDlg*>(m_xDlg.get());
+    return pDlg->GetSelectedLanguage();
+}
+
 VclPtr<AbstractSwInsertAbstractDlg> 
SwAbstractDialogFactory_Impl::CreateSwInsertAbstractDlg(weld::Window* pParent)
 {
     return 
VclPtr<AbstractSwInsertAbstractDlg_Impl>::Create(std::make_unique<SwInsertAbstractDlg>(pParent));
@@ -861,6 +868,11 @@ std::shared_ptr<AbstractSwBreakDlg> 
SwAbstractDialogFactory_Impl::CreateSwBreakD
     return 
std::make_shared<AbstractSwBreakDlg_Impl>(std::make_unique<SwBreakDlg>(pParent, 
rSh));
 }
 
+std::shared_ptr<AbstractSwTranslateLangSelectDlg> 
SwAbstractDialogFactory_Impl::CreateSwTranslateLangSelectDlg(weld::Window* 
pParent, SwWrtShell &rSh)
+{
+    return 
std::make_shared<AbstractSwTranslateLangSelectDlg_Impl>(std::make_unique<SwTranslateLangSelectDlg>(pParent,
 rSh));
+}
+
 VclPtr<VclAbstractDialog> 
SwAbstractDialogFactory_Impl::CreateSwChangeDBDlg(SwView& rVw)
 {
 #if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
diff --git a/sw/source/ui/dialog/swdlgfact.hxx 
b/sw/source/ui/dialog/swdlgfact.hxx
index 8690d9db8129..9ea4bc91b6c0 100644
--- a/sw/source/ui/dialog/swdlgfact.hxx
+++ b/sw/source/ui/dialog/swdlgfact.hxx
@@ -187,6 +187,19 @@ public:
     virtual std::shared_ptr<weld::DialogController> getDialogController() 
override { return m_xDlg; }
 };
 
+class AbstractSwTranslateLangSelectDlg_Impl : public 
AbstractSwTranslateLangSelectDlg
+{
+    std::shared_ptr<weld::DialogController> m_xDlg;
+public:
+    explicit 
AbstractSwTranslateLangSelectDlg_Impl(std::shared_ptr<weld::DialogController> p)
+        : m_xDlg(std::move(p))
+    {
+    }
+
+    virtual std::shared_ptr<weld::DialogController> getDialogController() 
override { return m_xDlg; }
+    virtual std::optional<SwLanguageListItem> GetSelectedLanguage() override;
+};
+
 class AbstractSwTableWidthDlg_Impl : public VclAbstractDialog
 {
     std::unique_ptr<SwTableWidthDlg> m_xDlg;
@@ -684,6 +697,7 @@ public:
                                       SwContentControlListItem& rItem) 
override;
 
     virtual std::shared_ptr<AbstractSwBreakDlg> CreateSwBreakDlg(weld::Window 
*pParent, SwWrtShell &rSh) override;
+    virtual std::shared_ptr<AbstractSwTranslateLangSelectDlg> 
CreateSwTranslateLangSelectDlg(weld::Window *pParent, SwWrtShell &rSh) override;
     virtual VclPtr<VclAbstractDialog> CreateSwChangeDBDlg(SwView& rVw) 
override;
     virtual VclPtr<SfxAbstractTabDialog>  CreateSwCharDlg(weld::Window* 
pParent, SwView& pVw, const SfxItemSet& rCoreSet,
         SwCharDlgMode nDialogMode, const OUString* pFormatStr = nullptr) 
override;
diff --git a/sw/source/ui/misc/translatelangselect.cxx 
b/sw/source/ui/misc/translatelangselect.cxx
new file mode 100644
index 000000000000..cf6a32935322
--- /dev/null
+++ b/sw/source/ui/misc/translatelangselect.cxx
@@ -0,0 +1,171 @@
+/* -*- 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 <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <uitool.hxx>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <translatelangselect.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <sal/log.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <svtools/deeplcfg.hxx>
+
+int SwTranslateLangSelectDlg::selectedLangIdx = -1;
+SwTranslateLangSelectDlg::SwTranslateLangSelectDlg(weld::Window* pParent, 
SwWrtShell& rSh)
+    : GenericDialogController(pParent, 
"modules/swriter/ui/translationdialog.ui",
+                              "LanguageSelectDialog")
+    , rWrtSh(rSh)
+    , m_xLanguageListBox(m_xBuilder->weld_combo_box("combobox1"))
+    , m_xBtnCancel(m_xBuilder->weld_button("cancel"))
+    , m_xBtnTranslate(m_xBuilder->weld_button("translate"))
+    , m_xLanguageVec({
+          SwLanguageListItem("BG", "Bulgarian"),
+          SwLanguageListItem("CS", "Czech"),
+          SwLanguageListItem("DA", "Danish"),
+          SwLanguageListItem("DE", "German"),
+          SwLanguageListItem("EL", "Greek"),
+          SwLanguageListItem("EN-GB", "English (British)"),
+          SwLanguageListItem("EN-US", "English (American)"),
+          SwLanguageListItem("ET", "Estonian"),
+          SwLanguageListItem("FI", "Finnish"),
+          SwLanguageListItem("FR", "French"),
+          SwLanguageListItem("HU", "Hungarian"),
+          SwLanguageListItem("ID", "Indonesian"),
+          SwLanguageListItem("IT", "Italian"),
+          SwLanguageListItem("JA", "Japanese"),
+          SwLanguageListItem("LT", "Lithuanian"),
+          SwLanguageListItem("LV", "Dutch"),
+          SwLanguageListItem("PL", "Polish"),
+          SwLanguageListItem("PT-BR", "Portuguese (Brazilian)"),
+          SwLanguageListItem("PT-PT", "Portuguese (European)"),
+          SwLanguageListItem("RO", "Romanian"),
+          SwLanguageListItem("RU", "Russian"),
+          SwLanguageListItem("SK", "Slovak"),
+          SwLanguageListItem("SL", "Slovenian"),
+          SwLanguageListItem("SV", "Swedish"),
+          SwLanguageListItem("TR", "Turkish"),
+          SwLanguageListItem("ZH", "Chinese (simplified)"),
+      })
+{
+    m_xLanguageListBox->connect_changed(LINK(this, SwTranslateLangSelectDlg, 
LangSelectHdl));
+    m_xBtnCancel->connect_clicked(LINK(this, SwTranslateLangSelectDlg, 
LangSelectCancelHdl));
+    m_xBtnTranslate->connect_clicked(LINK(this, SwTranslateLangSelectDlg, 
LangSelectTranslateHdl));
+    for (const auto& item : m_xLanguageVec)
+    {
+        m_xLanguageListBox->append_text(OStringToOUString(item.getName(), 
RTL_TEXTENCODING_UTF8));
+    }
+
+    if (SwTranslateLangSelectDlg::selectedLangIdx != -1)
+    {
+        
m_xLanguageListBox->set_active(SwTranslateLangSelectDlg::selectedLangIdx);
+    }
+}
+
+std::optional<SwLanguageListItem> 
SwTranslateLangSelectDlg::GetSelectedLanguage()
+{
+    if (SwTranslateLangSelectDlg::selectedLangIdx != -1)
+    {
+        return m_xLanguageVec.at(SwTranslateLangSelectDlg::selectedLangIdx);
+    }
+
+    return {};
+}
+
+IMPL_LINK(SwTranslateLangSelectDlg, LangSelectHdl, weld::ComboBox&, rBox, void)
+{
+    const auto selected = rBox.get_active();
+    SwTranslateLangSelectDlg::selectedLangIdx = selected;
+}
+
+IMPL_LINK(SwTranslateLangSelectDlg, LangSelectCancelHdl, weld::Button&, 
rButton, void)
+{
+    (void)rButton;
+    m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK(SwTranslateLangSelectDlg, LangSelectTranslateHdl, weld::Button&, 
rButton, void)
+{
+    (void)rButton;
+
+    if (SwTranslateLangSelectDlg::selectedLangIdx == -1)
+    {
+        m_xDialog->response(RET_CANCEL);
+        return;
+    }
+
+    SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+    if (rDeeplOptions.getAPIUrl().isEmpty() || 
rDeeplOptions.getAuthKey().isEmpty())
+    {
+        SAL_WARN("langselectdlg", "API options are not set");
+        m_xDialog->response(RET_CANCEL);
+        return;
+    }
+    const OString aAPIUrl
+        = OUStringToOString(OUString(rDeeplOptions.getAPIUrl() + 
"?tag_handling=html"),
+                            RTL_TEXTENCODING_UTF8)
+              .trim();
+    const OString aAuthKey
+        = OUStringToOString(rDeeplOptions.getAuthKey(), 
RTL_TEXTENCODING_UTF8).trim();
+    const auto aTargetLang
+        = 
m_xLanguageVec.at(SwTranslateLangSelectDlg::selectedLangIdx).getLanguage();
+    auto const& pNodes = rWrtSh.GetNodes();
+    SwNode* pNode = nullptr;
+    if (rWrtSh.HasSelection())
+    {
+        const auto aOut = 
SwTranslateHelper::ExportPaMToHTML(rWrtSh.GetCursor(), false);
+        const auto aTranslatedOut
+            = SwTranslateHelper::Translate(aTargetLang, aAPIUrl, aAuthKey, 
aOut);
+        SwTranslateHelper::PasteHTMLToPaM(rWrtSh, rWrtSh.GetCursor(), 
aTranslatedOut, false);
+        m_xDialog->response(RET_OK);
+        return;
+    }
+
+    for (SwNodeOffset n(0);; ++n)
+    {
+        if (n >= rWrtSh.GetNodes().Count())
+            break;
+
+        if (!pNodes[n])
+            break;
+
+        pNode = pNodes[n];
+        if (pNode->IsTextNode())
+        {
+            if (pNode->GetTextNode()->GetText().isEmpty())
+                continue;
+
+            auto cursor
+                = Writer::NewUnoCursor(*rWrtSh.GetDoc(), pNode->GetIndex(), 
pNode->GetIndex());
+
+            const auto aOut = SwTranslateHelper::ExportPaMToHTML(cursor.get(), 
true);
+            const auto aTranslatedOut
+                = SwTranslateHelper::Translate(aTargetLang, aAPIUrl, aAuthKey, 
aOut);
+            SwTranslateHelper::PasteHTMLToPaM(rWrtSh, cursor.get(), 
aTranslatedOut, true);
+        }
+    }
+    m_xDialog->response(RET_OK);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/inc/translatehelper.hxx 
b/sw/source/uibase/inc/translatehelper.hxx
new file mode 100644
index 000000000000..7af8541a8186
--- /dev/null
+++ b/sw/source/uibase/inc/translatehelper.hxx
@@ -0,0 +1,34 @@
+/* -*- 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 "swtypes.hxx"
+
+class SwWrtShell;
+class SwPaM;
+class SwNode;
+class SwTextNode;
+
+namespace SwTranslateHelper
+{
+SW_DLLPUBLIC extern OString ExportPaMToHTML(SwPaM* pCursor, bool bReplacePTag);
+SW_DLLPUBLIC extern OString Translate(const OString& rTargetLang, const 
OString& rAPIUrl,
+                                      const OString& rAuthKey, const OString& 
rData);
+SW_DLLPUBLIC extern void PasteHTMLToPaM(SwWrtShell& rWrtSh, SwPaM* pCursor, 
const OString& rData,
+                                        bool bSetSelection);
+}
\ No newline at end of file
diff --git a/sw/source/uibase/inc/translatelangselect.hxx 
b/sw/source/uibase/inc/translatelangselect.hxx
new file mode 100644
index 000000000000..3eac4d1269d7
--- /dev/null
+++ b/sw/source/uibase/inc/translatelangselect.hxx
@@ -0,0 +1,64 @@
+
+/* -*- 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 <vcl/weld.hxx>
+#include <rtl/string.h>
+#include <vector>
+#include <optional>
+#include <translatehelper.hxx>
+
+class SwWrtShell;
+
+class SwLanguageListItem final
+{
+public:
+    SwLanguageListItem(const OString& sLanguage, const OString& sName)
+        : m_sLanguage(sLanguage)
+        , m_sName(sName)
+    {
+    }
+    const OString& getLanguage() const { return m_sLanguage; }
+    const OString& getName() const { return m_sName; }
+
+private:
+    const OString m_sLanguage;
+    const OString m_sName;
+};
+
+class SwTranslateLangSelectDlg final : public weld::GenericDialogController
+{
+public:
+    static int selectedLangIdx;
+    SwTranslateLangSelectDlg(weld::Window* pParent, SwWrtShell& rSh);
+    std::optional<SwLanguageListItem> GetSelectedLanguage();
+
+private:
+    SwWrtShell& rWrtSh;
+    std::unique_ptr<weld::ComboBox> m_xLanguageListBox;
+    std::unique_ptr<weld::Button> m_xBtnCancel;
+    std::unique_ptr<weld::Button> m_xBtnTranslate;
+    std::vector<SwLanguageListItem> m_xLanguageVec;
+
+    DECL_LINK(LangSelectHdl, weld::ComboBox&, void);
+    DECL_LINK(LangSelectCancelHdl, weld::Button&, void);
+    DECL_LINK(LangSelectTranslateHdl, weld::Button&, void);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/shells/textsh1.cxx 
b/sw/source/uibase/shells/textsh1.cxx
index 1fee0e9759d5..9278c637587d 100644
--- a/sw/source/uibase/shells/textsh1.cxx
+++ b/sw/source/uibase/shells/textsh1.cxx
@@ -78,6 +78,8 @@
 #include <editeng/acorrcfg.hxx>
 #include <swabstdlg.hxx>
 #include <sfx2/sfxdlg.hxx>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
 #include <com/sun/star/container/XNameContainer.hpp>
 #include <com/sun/star/beans/XPropertySet.hpp>
 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
@@ -103,6 +105,20 @@
 #include <bookmark.hxx>
 #include <linguistic/misc.hxx>
 #include <authfld.hxx>
+#include <unoparagraph.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <curl/curl.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <translatelangselect.hxx>
+#include <swwait.hxx>
+#include <svtools/deeplcfg.hxx>
+#include <vcl/htmltransferable.hxx>
+#include <vcl/scheduler.hxx>
+#include <txtfrm.hxx>
+#include <translatehelper.hxx>
+
 
 using namespace ::com::sun::star;
 using namespace com::sun::star::beans;
@@ -1503,6 +1519,40 @@ void SwTextShell::Execute(SfxRequest &rReq)
         }
     }
     break;
+    case SID_FM_TRANSLATE:
+    {
+        const SfxPoolItem* pTargetLangStringItem = nullptr;
+        if (pArgs && SfxItemState::SET == 
pArgs->GetItemState(SID_ATTR_TARGETLANG_STR, false, &pTargetLangStringItem))
+        {
+            SvxDeeplOptions& rDeeplOptions = SvxDeeplOptions::Get();
+            if (rDeeplOptions.getAPIUrl().isEmpty() || 
rDeeplOptions.getAuthKey().isEmpty())
+            {
+                SAL_WARN("translate", "API options are not set");
+                break;
+            }
+            const OString aAPIUrl = 
OUStringToOString(OUString(rDeeplOptions.getAPIUrl() + "?tag_handling=html"), 
RTL_TEXTENCODING_UTF8).trim();
+            const OString aAuthKey = 
OUStringToOString(rDeeplOptions.getAuthKey(), RTL_TEXTENCODING_UTF8).trim();
+            OString aTargetLang = OUStringToOString(static_cast<const 
SfxStringItem*>(pTargetLangStringItem)->GetValue(), RTL_TEXTENCODING_UTF8);
+            SwPaM *pPaM = nullptr;
+            bool hasSelection = false;
+            if (rWrtSh.HasSelection())
+            {
+                pPaM = rWrtSh.GetCursor();
+                hasSelection = true;
+            }
+            const auto aOut = SwTranslateHelper::ExportPaMToHTML(pPaM, 
!hasSelection);
+            const auto aTranslatedOut = 
SwTranslateHelper::Translate(aTargetLang, aAPIUrl, aAuthKey, aOut);
+            SwTranslateHelper::PasteHTMLToPaM(rWrtSh, pPaM, aTranslatedOut, 
!hasSelection);
+        }
+        else
+        {
+            SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
+            std::shared_ptr<AbstractSwTranslateLangSelectDlg> 
pAbstractDialog(pFact->CreateSwTranslateLangSelectDlg(GetView().GetFrameWeld(), 
rWrtSh));
+            std::shared_ptr<weld::DialogController> 
pDialogController(pAbstractDialog->getDialogController());
+            weld::DialogController::runAsync(pDialogController, [] (sal_Int32 
/*nResult*/) { });
+        }
+    }
+    break;
     case SID_SPELLCHECK_IGNORE:
     {
         SwPaM *pPaM = rWrtSh.GetCursor();
diff --git a/sw/source/uibase/shells/translatehelper.cxx 
b/sw/source/uibase/shells/translatehelper.cxx
new file mode 100644
index 000000000000..dac2ae58ab69
--- /dev/null
+++ b/sw/source/uibase/shells/translatehelper.cxx
@@ -0,0 +1,139 @@
+/* -*- 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 <wrtsh.hxx>
+#include <pam.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <translatehelper.hxx>
+#include <sal/log.hxx>
+#include <rtl/string.h>
+#include <shellio.hxx>
+#include <vcl/scheduler.hxx>
+#include <vcl/svapp.hxx>
+#include <curl/curl.h>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/json_parser.hpp>
+#include <vcl/htmltransferable.hxx>
+#include <vcl/transfer.hxx>
+#include <swdtflvr.hxx>
+
+namespace SwTranslateHelper
+{
+OString Translate(const OString& rTargetLang, const OString& rAPIUrl, const 
OString& rAuthKey,
+                  const OString& rData)
+{
+    std::unique_ptr<CURL, std::function<void(CURL*)>> curl(curl_easy_init(),
+                                                           [](CURL* p) { 
curl_easy_cleanup(p); });
+    curl_easy_setopt(curl.get(), CURLOPT_URL, rAPIUrl.getStr());
+    curl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);
+    // todo add timeout
+    std::string response_body;
+    curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION,
+                     +[](void* buffer, size_t size, size_t nmemb, void* userp) 
-> size_t {
+                         if (!userp)
+                             return 0;
+                         std::string* response = 
static_cast<std::string*>(userp);
+                         size_t real_size = size * nmemb;
+                         response->append(static_cast<char*>(buffer), 
real_size);
+                         return real_size;
+                     });
+    curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, 
static_cast<void*>(&response_body));
+
+    // pass tis by parameter as well?
+    OString aPostData("auth_key=" + rAuthKey + "&target_lang=" + rTargetLang + 
"&text="
+                      + OString(curl_easy_escape(curl.get(), rData.getStr(), 
rData.getLength())));
+
+    curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, aPostData.getStr());
+    CURLcode cc = curl_easy_perform(curl.get());
+    if (cc != CURLE_OK)
+    {
+        SAL_WARN("translatehelper",
+                 "CURL perform returned with error: " << 
static_cast<sal_Int32>(cc));
+        return {};
+    }
+    tools::Long nStatusCode;
+    curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &nStatusCode);
+    if (nStatusCode != 200)
+    {
+        SAL_WARN("translatehelper", "CURL request returned with status code: " 
<< nStatusCode);
+        return {};
+    }
+    // parse the response
+    boost::property_tree::ptree root;
+    std::stringstream aStream(response_body.data());
+    boost::property_tree::read_json(aStream, root);
+    boost::property_tree::ptree& translations = root.get_child("translations");
+    size_t size = translations.size();
+    if (size <= 0)
+    {
+        SAL_WARN("translatehelper", "API did not return any translations");
+    }
+    // take the first one
+    const boost::property_tree::ptree& translation = 
translations.begin()->second;
+    const std::string text = translation.get<std::string>("text");
+    return OString(text);
+}
+
+OString ExportPaMToHTML(SwPaM* pCursor, bool bReplacePTag)
+{
+    OString aResult;
+    WriterRef xWrt;
+    GetHTMLWriter(OUString("NoLineLimit,SkipHeaderFooter"), OUString(), xWrt);
+    if (pCursor != nullptr)
+    {
+        SvMemoryStream aMemoryStream;
+        SwWriter aWriter(aMemoryStream, *pCursor);
+        ErrCode nError = aWriter.Write(xWrt);
+        if (nError.IsError())
+        {
+            SAL_WARN("translatehelper", "failed to export selection to HTML");
+            return {};
+        }
+        aResult
+            = OString(static_cast<const char*>(aMemoryStream.GetData()), 
aMemoryStream.GetSize());
+        if (bReplacePTag)
+        {
+            aResult = aResult.replaceAll("<p", "<span");
+            aResult = aResult.replaceAll("</p>", "</span>");
+        }
+        return aResult;
+    }
+    return {};
+}
+
+void PasteHTMLToPaM(SwWrtShell& rWrtSh, SwPaM* pCursor, const OString& rData, 
bool bSetSelection)
+{
+    rtl::Reference<vcl::unohelper::HtmlTransferable> pHtmlTransferable
+        = new vcl::unohelper::HtmlTransferable(rData);
+    if (pHtmlTransferable.is())
+    {
+        TransferableDataHelper aDataHelper(pHtmlTransferable);
+        if (aDataHelper.GetXTransferable().is()
+            && SwTransferable::IsPasteSpecial(rWrtSh, aDataHelper))
+        {
+            if (bSetSelection)
+            {
+                rWrtSh.SetSelection(*pCursor);
+            }
+            SwTransferable::Paste(rWrtSh, aDataHelper);
+            rWrtSh.KillSelection(nullptr, false);
+        }
+    }
+}
+}
\ No newline at end of file
diff --git a/sw/uiconfig/sglobal/menubar/menubar.xml 
b/sw/uiconfig/sglobal/menubar/menubar.xml
index 38f6708c1a55..709c492df4f3 100644
--- a/sw/uiconfig/sglobal/menubar/menubar.xml
+++ b/sw/uiconfig/sglobal/menubar/menubar.xml
@@ -446,6 +446,7 @@
       <menu:menuseparator/>
       <menu:menuitem menu:id=".uno:PageDialog"/>
       <menu:menuitem menu:id=".uno:TitlePageDialog" menu:style="text"/>
+      <menu:menuitem menu:id=".uno:Translate" menu:style="text"/>
       <menu:menuitem menu:id=".uno:FormatAllNotes" menu:style="text"/>
       <menu:menuitem menu:id=".uno:RubyDialog" menu:style="text"/>
       <menu:menuitem menu:id=".uno:FormatColumns" menu:style="text"/>
diff --git a/sw/uiconfig/swform/menubar/menubar.xml 
b/sw/uiconfig/swform/menubar/menubar.xml
index 0969d09e6ef6..861cded1a732 100644
--- a/sw/uiconfig/swform/menubar/menubar.xml
+++ b/sw/uiconfig/swform/menubar/menubar.xml
@@ -396,6 +396,7 @@
       <menu:menuseparator/>
       <menu:menuitem menu:id=".uno:PageDialog"/>
       <menu:menuitem menu:id=".uno:TitlePageDialog" menu:style="text"/>
+      <menu:menuitem menu:id=".uno:Translate" menu:style="text"/>
       <menu:menuitem menu:id=".uno:FormatAllNotes" menu:style="text"/>
       <menu:menuitem menu:id=".uno:RubyDialog" menu:style="text"/>
       <menu:menuitem menu:id=".uno:FormatColumns" menu:style="text"/>
diff --git a/sw/uiconfig/swriter/menubar/menubar.xml 
b/sw/uiconfig/swriter/menubar/menubar.xml
index 3f257b9f0ebf..4658564da415 100644
--- a/sw/uiconfig/swriter/menubar/menubar.xml
+++ b/sw/uiconfig/swriter/menubar/menubar.xml
@@ -457,6 +457,7 @@
       <menu:menuseparator/>
       <menu:menuitem menu:id=".uno:PageDialog"/>
       <menu:menuitem menu:id=".uno:TitlePageDialog"/>
+      <menu:menuitem menu:id=".uno:Translate"/>
       <menu:menuitem menu:id=".uno:FormatAllNotes" menu:style="text"/>
       <menu:menuitem menu:id=".uno:RubyDialog" menu:style="text"/>
       <menu:menuitem menu:id=".uno:FormatColumns"/>
diff --git a/sw/uiconfig/swriter/ui/translationdialog.ui 
b/sw/uiconfig/swriter/ui/translationdialog.ui
new file mode 100644
index 000000000000..daaeb406aa29
--- /dev/null
+++ b/sw/uiconfig/swriter/ui/translationdialog.ui
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.2 -->
+<interface domain="sw">
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkDialog" id="LanguageSelectDialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="title" translatable="yes">Language Selection</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="default_width">0</property>
+    <property name="default_height">0</property>
+    <property name="type_hint">dialog</property>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="action-area1">
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel">
+                <property name="label">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="translate">
+                <property name="label">_OK</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="grid1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="row_spacing">5</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">Select language to 
translate</property>
+                <property name="mnemonic_widget">combobox1</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBoxText" id="combobox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancel</action-widget>
+      <action-widget response="-5">translate</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 90d432ee559b..fe0e0998ed4e 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -408,6 +408,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/app/svmain \
     vcl/source/app/timer \
     vcl/source/app/unohelp2 \
+    vcl/source/app/htmltransferable \
     vcl/source/app/unohelp \
     vcl/source/app/vclevent \
     vcl/source/app/watchdog \
diff --git a/vcl/jsdialog/enabled.cxx b/vcl/jsdialog/enabled.cxx
index 8bc49e8bda20..6c6d94e85fa4 100644
--- a/vcl/jsdialog/enabled.cxx
+++ b/vcl/jsdialog/enabled.cxx
@@ -60,7 +60,8 @@ bool isBuilderEnabled(std::u16string_view rUIFile, bool 
bMobile)
         || rUIFile == u"svx/ui/accessibilitycheckentry.ui"
         || rUIFile == u"cui/ui/widgettestdialog.ui"
         || rUIFile == u"modules/swriter/ui/contentcontroldlg.ui"
-        || rUIFile == u"modules/swriter/ui/contentcontrollistitemdlg.ui")
+        || rUIFile == u"modules/swriter/ui/contentcontrollistitemdlg.ui"
+        || rUIFile == u"modules/swriter/ui/translationdialog.ui")
     {
         return true;
     }
diff --git a/vcl/source/app/htmltransferable.cxx 
b/vcl/source/app/htmltransferable.cxx
new file mode 100644
index 000000000000..24f65fe929b1
--- /dev/null
+++ b/vcl/source/app/htmltransferable.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 <vcl/htmltransferable.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace ::com::sun::star;
+
+namespace vcl::unohelper
+{
+HtmlTransferable::HtmlTransferable(OString sData)
+    : data(sData)
+{
+}
+
+HtmlTransferable::~HtmlTransferable() {}
+
+// css::uno::XInterface
+uno::Any HtmlTransferable::queryInterface(const uno::Type& rType)
+{
+    uno::Any aRet = ::cppu::queryInterface(rType, 
static_cast<datatransfer::XTransferable*>(this));
+    return (aRet.hasValue() ? aRet : OWeakObject::queryInterface(rType));
+}
+
+// css::datatransfer::XTransferable
+uno::Any HtmlTransferable::getTransferData(const datatransfer::DataFlavor& 
rFlavor)
+{
+    SotClipboardFormatId nT = SotExchange::GetFormat(rFlavor);
+    if (nT != SotClipboardFormatId::HTML)
+    {
+        throw datatransfer::UnsupportedFlavorException();
+    }
+    size_t size = data.getLength();
+    uno::Sequence<sal_Int8> sData(size);
+    std::memcpy(sData.getArray(), data.getStr(), size);
+    return uno::Any(sData);
+}
+
+uno::Sequence<datatransfer::DataFlavor> 
HtmlTransferable::getTransferDataFlavors()
+{
+    uno::Sequence<datatransfer::DataFlavor> aDataFlavors(1);
+    auto ref = aDataFlavors.getArray()[0];
+    ref.MimeType = "text/html";
+    ref.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get();
+    SotExchange::GetFormatDataFlavor(SotClipboardFormatId::HTML, 
aDataFlavors.getArray()[0]);
+    return aDataFlavors;
+}
+
+sal_Bool HtmlTransferable::isDataFlavorSupported(const 
datatransfer::DataFlavor& rFlavor)
+{
+    SotClipboardFormatId nT = SotExchange::GetFormat(rFlavor);
+    return (nT == SotClipboardFormatId::HTML);
+}
+
+} // namespace vcl::unohelper
commit d5b0d5b1aa379d7cc758f6206ac9b3143868f75e
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Jul 18 08:21:43 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jul 18 09:05:57 2022 +0200

    sw: fix crash in SwTextShell::Execute()
    
    Crashreport signature:
    
            SwTextShell::Execute(SfxRequest&)
                    sw/source/uibase/shells/textsh1.cxx:1540
            SfxDispatcher::Call_Impl(SfxShell&, SfxSlot const&, SfxRequest&, 
bool)
                    sfx2/source/control/dispatch.cxx:256
            SfxDispatcher::Execute(unsigned short, SfxCallMode, SfxItemSet 
const*, SfxItemSet const*, unsigned short)
                    sfx2/source/control/dispatch.cxx:811
            SfxDispatchController_Impl::dispatch(com::sun::star::util::URL 
const&, com::sun::star::uno::Sequence<com::sun::star::beans::PropertyValue> 
const&, 
com::sun::star::uno::Reference<com::sun::star::frame::XDispatchResultListener> 
const&)
                    sfx2/source/control/unoctitm.cxx:671
    
    This is the Grammar case, the Spelling case already checked for an empty
    xDictionary reference.
    
    Change-Id: If1f88e4bdf2d68d877fbb0bd89d0cadbd493771f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137161
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/source/uibase/shells/textsh1.cxx 
b/sw/source/uibase/shells/textsh1.cxx
index 8d7c07e104ca..1fee0e9759d5 100644
--- a/sw/source/uibase/shells/textsh1.cxx
+++ b/sw/source/uibase/shells/textsh1.cxx
@@ -1536,9 +1536,12 @@ void SwTextShell::Execute(SfxRequest &rReq)
                     SwPaM *pPaM = rWrtSh.GetCursor();
                     if (pPaM)
                         SwEditShell::IgnoreGrammarErrorAt( *pPaM );
-                    // refresh the layout of all paragraphs (workaround to 
launch a dictionary event)
-                    xDictionary->setActive(false);
-                    xDictionary->setActive(true);
+                    if (xDictionary.is())
+                    {
+                        // refresh the layout of all paragraphs (workaround to 
launch a dictionary event)
+                        xDictionary->setActive(false);
+                        xDictionary->setActive(true);
+                    }
                 }
                 catch( const uno::Exception& )
                 {
commit 5bc905f997d94e02e0fb16b19e57b42ae97e4076
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Jul 15 11:37:58 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jul 18 08:08:19 2022 +0200

    sw content control, dropdown: allow selecting via the keyboard
    
    It was not possible to select an entry from a content control dropdown
    using the keyboard, which breaks accessibility.
    
    This had the benefit that the mouse handler code could contain the popup
    start calls, but Word can do this with alt-down arrow, so make sense to
    add it on our side as well.
    
    Fix the problem by adding SwContentControl::ShouldOpenPopup(), which
    knows that dropdowns want a popup with alt-down, and then connecting
    SwEditWin::KeyInput() to it.
    
    Date content controls probably will want something similar, but that's
    not yet done in this commit.
    
    (cherry picked from commit d65e85178abdd4f1bf068f161e49204e068bb5da)
    
    Conflicts:
            sw/inc/formatcontentcontrol.hxx
    
    Change-Id: I6853d3a1661e4d826b96b1b5cb938909875af2ad
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137107
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx
index 2dd27529810a..fb6e00106691 100644
--- a/sw/inc/crsrsh.hxx
+++ b/sw/inc/crsrsh.hxx
@@ -59,6 +59,7 @@ class SwBlockCursor;
 class SwPostItField;
 class SwTextField;
 class SwTextFootnote;
+class SwTextContentControl;
 
 namespace i18nutil {
     struct SearchOptions2;
@@ -717,7 +718,7 @@ public:
         const bool bIncludeInputFieldAtStart );
     SwField* GetCurField( const bool bIncludeInputFieldAtStart = false ) const;
     bool CursorInsideInputField() const;
-    bool CursorInsideContentControl() const;
+    SwTextContentControl* CursorInsideContentControl() const;
     static bool PosInsideInputField( const SwPosition& rPos );
     bool DocPtInsideInputField( const Point& rDocPt ) const;
     static sal_Int32 StartOfInputFieldAtPos( const SwPosition& rPos );
diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 24d95d5d01fa..b40c9cdba0d7 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -28,6 +28,10 @@
 #include "calbck.hxx"
 #include "swdllapi.h"
 
+namespace vcl
+{
+class KeyCode;
+}
 class SwContentControl;
 class SwTextContentControl;
 class SwTextNode;
@@ -274,6 +278,9 @@ public:
     /// Should this character (during key input) interact with the content 
control?
     bool IsInteractingCharacter(sal_Unicode cCh);
 
+    /// Given rKeyCode as a keyboard event, should a popup be opened for this 
content control?
+    bool ShouldOpenPopup(const vcl::KeyCode& rKeyCode);
+
     virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
 
     void SetDataBindingPrefixMappings(const OUString& 
rDataBindingPrefixMappings)
diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx
index c5d97fc023c1..6126f5ffa0c0 100644
--- a/sw/inc/viscrs.hxx
+++ b/sw/inc/viscrs.hxx
@@ -115,6 +115,8 @@ public:
 
     void SetShowContentControlOverlay(const bool bShow) { 
m_bShowContentControlOverlay = bShow; }
 
+    VclPtr<SwContentControlButton> GetContentControlButton() const;
+
     const SwCursorShell* GetShell() const { return m_pCursorShell; }
     // check current MapMode of the shell and set possibly the static members.
     // Optional set the parameters pX, pY
diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx
index 55fece606c4c..7791cf82bbc3 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -249,6 +249,27 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, 
testCheckboxContentControlKeyboard)
     CPPUNIT_ASSERT(pContentControl->GetChecked());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testDropdownContentControlKeyboard)
+{
+    // Given an already selected dropdown content control:
+    SwDoc* pDoc = createSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
+
+    // When checking if alt-down should open a popup:
+    SwTextContentControl* pTextContentControl = 
pWrtShell->CursorInsideContentControl();
+    auto& rFormatContentControl
+        = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+    std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
+    vcl::KeyCode aKeyCode(KEY_DOWN, KEY_MOD2);
+    bool bShouldOpen = pContentControl->ShouldOpenPopup(aKeyCode);
+
+    // Then make sure that the answer is yes for dropdowns:
+    // Without the accompanying fix in place, this test would have failed, the 
dropdown popup was
+    // mouse-only.
+    CPPUNIT_ASSERT(bShouldOpen);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/contentcontrolbutton.cxx 
b/sw/source/core/crsr/contentcontrolbutton.cxx
index 34cbd38e663a..108a6fe7eade 100644
--- a/sw/source/core/crsr/contentcontrolbutton.cxx
+++ b/sw/source/core/crsr/contentcontrolbutton.cxx
@@ -79,7 +79,9 @@ void SwContentControlButton::CalcPosAndSize(const SwRect& 
rPortionPaintArea)
     }
 }
 
-void SwContentControlButton::MouseButtonDown(const MouseEvent&)
+void SwContentControlButton::MouseButtonDown(const MouseEvent&) { 
StartPopup(); }
+
+void SwContentControlButton::StartPopup()
 {
     LaunchPopup();
     Invalidate();
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
index 73d12ce79c3a..463a1d05dada 100644
--- a/sw/source/core/crsr/crstrvl.cxx
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -1002,7 +1002,7 @@ bool SwCursorShell::CursorInsideInputField() const
     return false;
 }
 
-bool SwCursorShell::CursorInsideContentControl() const
+SwTextContentControl* SwCursorShell::CursorInsideContentControl() const
 {
     for (SwPaM& rCursor : GetCursor()->GetRingContainer())
     {
@@ -1014,13 +1014,13 @@ bool SwCursorShell::CursorInsideContentControl() const
         }
 
         sal_Int32 nIndex = pStart->nContent.GetIndex();
-        if (pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, 
SwTextNode::PARENT))
+        if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, 
RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
         {
-            return true;
+            return static_txtattr_cast<SwTextContentControl*>(pAttr);
         }
     }
 
-    return false;
+    return nullptr;
 }
 
 bool SwCursorShell::PosInsideInputField( const SwPosition& rPos )
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
index 79b8ab934b93..797bd8ecb7d8 100644
--- a/sw/source/core/crsr/viscrs.cxx
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -797,6 +797,11 @@ void SwSelPaintRects::HighlightContentControl()
     }
 }
 
+VclPtr<SwContentControlButton> SwSelPaintRects::GetContentControlButton() const
+{
+    return m_pContentControlButton;
+}
+
 void SwSelPaintRects::Invalidate( const SwRect& rRect )
 {
     size_type nSz = size();
diff --git a/sw/source/core/inc/contentcontrolbutton.hxx 
b/sw/source/core/inc/contentcontrolbutton.hxx
index a921680ed7d4..cd63bddd4e69 100644
--- a/sw/source/core/inc/contentcontrolbutton.hxx
+++ b/sw/source/core/inc/contentcontrolbutton.hxx
@@ -29,6 +29,8 @@ public:
 
     virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
     DECL_LINK(PopupModeEndHdl, weld::Popover&, void);
+    /// Shared MouseButtonDown() and KeyInput() code.
+    void StartPopup();
 
     virtual void Paint(vcl::RenderContext& rRenderContext, const 
tools::Rectangle& rRect) override;
     virtual WindowHitTest ImplHitTest(const Point& rFramePos) override;
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index a43ddbe4c240..2f4552f2f758 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -25,6 +25,7 @@
 #include <comphelper/propertyvalue.hxx>
 #include <comphelper/sequenceashashmap.hxx>
 #include <svl/numformat.hxx>
+#include <vcl/keycod.hxx>
 
 #include <ndtxt.hxx>
 #include <textcontentcontrol.hxx>
@@ -311,6 +312,17 @@ bool SwContentControl::IsInteractingCharacter(sal_Unicode 
cCh)
     return false;
 }
 
+bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode)
+{
+    if (HasListItems())
+    {
+        // Alt-down opens the popup.
+        return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN;
+    }
+
+    return false;
+}
+
 void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
 {
     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));
diff --git a/sw/source/uibase/docvw/edtwin.cxx 
b/sw/source/uibase/docvw/edtwin.cxx
index d14de955ed56..c765e3b42d7e 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -146,6 +146,7 @@
 #include <txtfrm.hxx>
 #include <strings.hrc>
 #include <textcontentcontrol.hxx>
+#include <contentcontrolbutton.hxx>
 
 using namespace sw::mark;
 using namespace ::com::sun::star;
@@ -1518,6 +1519,26 @@ void SwEditWin::KeyInput(const KeyEvent &rKEvt)
         return;
     }
 
+    if (SwTextContentControl* pTextContentControl = 
rSh.CursorInsideContentControl())
+    {
+        // Check if this combination of rKeyCode and pTextContentControl 
should open a popup.
+        const SwFormatContentControl& rFormatContentControl = 
pTextContentControl->GetContentControl();
+        std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
+        if (pContentControl->ShouldOpenPopup(rKeyCode))
+        {
+            SwShellCursor* pCursor = rSh.GetCursor_();
+            if (pCursor)
+            {
+                VclPtr<SwContentControlButton> pContentControlButton = 
pCursor->GetContentControlButton();
+                if (pContentControlButton)
+                {
+                    pContentControlButton->StartPopup();
+                    return;
+                }
+            }
+        }
+    }
+
     const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat();
     if( pFlyFormat )
     {
commit f74bbe7397ad8e05a4d201e4d014fdad8acf12a4
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Jul 14 15:52:37 2022 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jul 18 08:08:07 2022 +0200

    sw content control, checkbox: allow toggling via the keyboard
    
    Toggling a checkbox content control was only possible by clicking on it
    with the mouse, which breaks accessiblity.
    
    Trying to type into a checkbox content control triggered the read-only
    popup (which is good), but there was no special handling for the space
    character, which is meant to toggle the checkbox.
    
    Fix the problem by adding a way to query if the current keycode is meant
    to interact with the content control, and if so, invoke
    GotoContentControl() from SwEditWin::KeyInput(), similar to how the
    click handler already did this already.
    
    This only handles checkboxes, but other types can be addressed in a
    follow-up commits similarly.
    
    (cherry picked from commit c6eb8713438d1ba791fc858c8dc6e3d4d6583e0a)
    
    Conflicts:
            sw/inc/formatcontentcontrol.hxx
    
    Change-Id: I5c88f2e2f1c2d0f4b28f2ce0b6b1c75b14b7d67c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137106
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx
index 3bb9f6d64489..24d95d5d01fa 100644
--- a/sw/inc/formatcontentcontrol.hxx
+++ b/sw/inc/formatcontentcontrol.hxx
@@ -271,6 +271,9 @@ public:
 
     std::optional<double> GetSelectedDate() const { return m_oSelectedDate; }
 
+    /// Should this character (during key input) interact with the content 
control?
+    bool IsInteractingCharacter(sal_Unicode cCh);
+
     virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
 
     void SetDataBindingPrefixMappings(const OUString& 
rDataBindingPrefixMappings)
diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx
index ad44a12f7c9b..55fece606c4c 100644
--- a/sw/qa/core/txtnode/txtnode.cxx
+++ b/sw/qa/core/txtnode/txtnode.cxx
@@ -25,6 +25,11 @@
 #include <unotxdoc.hxx>
 #include <docsh.hxx>
 #include <formatcontentcontrol.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <txatbase.hxx>
+#include <ndtxt.hxx>
+#include <textcontentcontrol.hxx>
 
 constexpr OUStringLiteral DATA_DIRECTORY = u"/sw/qa/core/txtnode/data/";
 
@@ -220,6 +225,30 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, 
testInsertDropDownContentControlTwice)
     pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testCheckboxContentControlKeyboard)
+{
+    // Given an already selected checkbox content control:
+    SwDoc* pDoc = createSwDoc();
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->InsertContentControl(SwContentControlType::CHECKBOX);
+    SwEditWin& rEditWin = pWrtShell->GetView().GetEditWin();
+
+    // When pressing space on the keyboard:
+    KeyEvent aKeyEvent(' ', KEY_SPACE);
+    rEditWin.KeyInput(aKeyEvent);
+
+    // Then make sure the state is toggled:
+    SwTextNode* pTextNode = pWrtShell->GetCursor()->GetNode().GetTextNode();
+    SwTextAttr* pAttr = pTextNode->GetTextAttrForCharAt(0, 
RES_TXTATR_CONTENTCONTROL);
+    auto pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+    auto& rFormatContentControl
+        = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr());
+    std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
+    // Without the accompanying fix in place, this test would have failed, 
because the state
+    // remained unchanged.
+    CPPUNIT_ASSERT(pContentControl->GetChecked());
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx 
b/sw/source/core/txtnode/attrcontentcontrol.cxx
index f70a36b49de2..a43ddbe4c240 100644
--- a/sw/source/core/txtnode/attrcontentcontrol.cxx
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -301,6 +301,16 @@ double SwContentControl::GetCurrentDateValue() const
     return dCurrentDate;
 }
 
+bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh)
+{
+    if (GetCheckbox())
+    {
+        return cCh == ' ';
+    }
+
+    return false;
+}
+
 void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
 {
     (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));
diff --git a/sw/source/uibase/docvw/edtwin.cxx 
b/sw/source/uibase/docvw/edtwin.cxx
index a85d93c2f05f..d14de955ed56 100644
--- a/sw/source/uibase/docvw/edtwin.cxx
+++ b/sw/source/uibase/docvw/edtwin.cxx
@@ -2398,6 +2398,29 @@ KEYINPUT_CHECKTABLE_INSDEL:
             aCh = '\t';
             [[fallthrough]];
         case SwKeyState::InsChar:
+            if (rSh.CursorInsideContentControl())
+            {
+                const SwPosition* pStart = rSh.GetCursor()->Start();
+                SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+                if (pTextNode)
+                {
+                    sal_Int32 nIndex = pStart->nContent.GetIndex();
+                    SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, 
RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+                    if (pAttr)
+                    {
+                        auto pTextContentControl = 
static_txtattr_cast<SwTextContentControl*>(pAttr);
+                        const SwFormatContentControl& rFormatContentControl = 
pTextContentControl->GetContentControl();
+                        std::shared_ptr<SwContentControl> pContentControl = 
rFormatContentControl.GetContentControl();
+                        if (pContentControl->IsInteractingCharacter(aCh))
+                        {
+                            rSh.GotoContentControl(rFormatContentControl);
+                            eKeyState = SwKeyState::End;
+                            break;
+                        }
+                    }
+                }
+            }
+
             if (rSh.GetChar(false)==CH_TXT_ATR_FORMELEMENT)
             {
                 ::sw::mark::ICheckboxFieldmark* pFieldmark =
commit 05dd16dd9d788372d4f0a585b3d64ce5b9207e0e
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Mon Jul 4 14:57:18 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Wed Jul 13 17:05:42 2022 +0200

    sc: lok: fix offset other edit view
    
    Multiple view case.
    
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Change-Id: I8049f2f36438e77beb5fe41399007ced0d500efa
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136812
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Ashod Nakashian <a...@collabora.com>
    (cherry picked from commit 59bee366f9df6df26cc9b92b19997fc7f5112c7d)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136917
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137029

diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index 3f7c08f401bd..d105a47a4483 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -1166,8 +1166,9 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, 
const ScTableInfo& rTableI
                             // to be tweaked temporarily to match the current 
view's zoom.
                             SuppressEditViewMessagesGuard 
aGuard(*pOtherEditView);
 
-                            
pOtherEditView->SetOutputArea(rDevice.PixelToLogic(aEditRect));
-                            
pOtherEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice);
+                            aEditRect = rDevice.PixelToLogic(aEditRect);
+                            
aEditRect.Intersection(pOtherEditView->GetOutputArea());
+                            pOtherEditView->Paint(aEditRect, &rDevice);
 
                             // Rollback the mapmode and 'output area'.
                             rOtherWin.SetMapMode(aOrigMapMode);
commit 5a0839e60ac75869ad49685ca74ad6c6b49ef925
Author:     Henry Castro <hcas...@collabora.com>
AuthorDate: Wed Apr 20 08:14:39 2022 -0400
Commit:     Henry Castro <hcas...@collabora.com>
CommitDate: Wed Jul 13 17:05:07 2022 +0200

    sc: lok: fix offset edit output area
    
    When focus is received, the edit view output area
    rectangle is calculated to draw the text, after
    EditGrowX and EditGrowY checks, so intercept the
    rectangle to not alter the text positions.
    
    Change-Id: I1439fc3d436bd5f4d03b13bd6bb56a85012a2655
    Signed-off-by: Henry Castro <hcas...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133214
    Reviewed-by: Ashod Nakashian <a...@collabora.com>
    (cherry picked from commit 037ec953d230aef045034b59d8315e6c41dc6687)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136916
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137028

diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index e51feeda0645..3f7c08f401bd 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -1271,7 +1271,6 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, 
const ScTableInfo& rTableI
             // So they need to be in the same coordinates/units. This is tied 
to the mapmode of the gridwin
             // attached to the EditView, so we have to change its mapmode too 
(temporarily). We save the
             // original mapmode and 'output area' and roll them back when we 
finish painting to rDevice.
-            const tools::Rectangle 
aOrigOutputArea(pEditView->GetOutputArea()); // Not in pixels.
             const MapMode aOrigMapMode = GetMapMode();
             SetMapMode(rDevice.GetMapMode());
 
@@ -1280,8 +1279,9 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, 
const ScTableInfo& rTableI
             // cursor-messaging done in the non print-twips mode)
             SuppressEditViewMessagesGuard aGuard(*pEditView);
 
-            pEditView->SetOutputArea(rDevice.PixelToLogic(aEditRect));
-            pEditView->Paint(rDevice.PixelToLogic(aEditRect), &rDevice);
+            aEditRect = rDevice.PixelToLogic(aEditRect);
+            aEditRect.Intersection(pEditView->GetOutputArea());
+            pEditView->Paint(aEditRect, &rDevice);
 
             // EditView will do the cursor notifications correctly if we're in
             // print-twips messaging mode.
@@ -1311,7 +1311,6 @@ void ScGridWindow::DrawContent(OutputDevice &rDevice, 
const ScTableInfo& rTableI
 
             // Rollback the mapmode and 'output area'.
             SetMapMode(aOrigMapMode);
-            pEditView->SetOutputArea(aOrigOutputArea);
         }
         else
         {
commit 0d3bc933855232bd9fdb41c98f1fb414c4d80a86
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Mon Jul 11 23:38:00 2022 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Tue Jul 12 09:08:25 2022 +0200

    sdext: these tests won't run without poppler
    
    Change-Id: I61deb7d5271bf87ac1bb3aad54c430ace8fd3f9f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136966
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Andras Timar <andras.ti...@collabora.com>

diff --git a/sdext/source/pdfimport/test/tests.cxx 
b/sdext/source/pdfimport/test/tests.cxx
index c8c3b9e28a8f..cbca5c30122d 100644
--- a/sdext/source/pdfimport/test/tests.cxx
+++ b/sdext/source/pdfimport/test/tests.cxx
@@ -585,6 +585,7 @@ namespace
 
         void testTdf78427_FontFeatures()
         {
+#if HAVE_FEATURE_POPPLER
             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new 
pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
             xAdaptor->setTreeVisitorFactory(createDrawTreeVisitorFactory());
 
@@ -711,10 +712,12 @@ namespace
             assertXPath(pXmlDoc, xpath, "font-weight", "normal");
             assertXPathNoAttribute(pXmlDoc, xpath, "font-style");
             assertXPath(pXmlDoc, xpath, "text-outline", "true");
+#endif
         }
 
         void testTdf78427_FontWeight_MyraidProSemibold() // Related to 
attachment 155937.
         {
+#if HAVE_FEATURE_POPPLER
             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new 
pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
             xAdaptor->setTreeVisitorFactory(createDrawTreeVisitorFactory());
 
@@ -744,10 +747,12 @@ namespace
                 "\"]/style:text-properties";
             // the font-weight and font-style should be 300 (Light)
             assertXPath(pXmlDoc, xpath, "font-weight", "300");
+#endif
         }
 
         void testTdf143959_nameFromFontFile()
         {
+#if HAVE_FEATURE_POPPLER
             rtl::Reference<pdfi::PDFIRawAdaptor> xAdaptor(new 
pdfi::PDFIRawAdaptor(OUString(), getComponentContext()));
             xAdaptor->setTreeVisitorFactory(createDrawTreeVisitorFactory());
 
@@ -777,6 +782,7 @@ namespace
                                  getXPath(pXmlDoc, xpath, 
"font-family").replaceAll(u" ", u""));
             CPPUNIT_ASSERT_EQUAL(OUString("bold"),
                                  getXPath(pXmlDoc, xpath, "font-weight"));
+#endif
         }
 
         CPPUNIT_TEST_SUITE(PDFITest);
commit fe4468db8de113d2844a8733dd094506af778b51
Author:     Andras Timar <andras.ti...@collabora.com>
AuthorDate: Mon Jul 11 19:06:49 2022 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jul 11 19:06:49 2022 +0200

    Bump version to 22.05.4.1
    
    Change-Id: I613ba29dfcdcf977677ede8c74e78272bcb79ab7

diff --git a/configure.ac b/configure.ac
index acfd70ed4859..f2284ec730ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ dnl in order to create a configure script.
 # several non-alphanumeric characters, those are split off and used only for 
the
 # ABOUTBOXPRODUCTVERSIONSUFFIX in openoffice.lst. Why that is necessary, no 
idea.
 
-AC_INIT([Collabora Office],[22.05.3.1],[],[],[https://collaboraoffice.com/])
+AC_INIT([Collabora Office],[22.05.4.1],[],[],[https://collaboraoffice.com/])
 
 dnl libnumbertext needs autoconf 2.68, but that can pick up autoconf268 just 
fine if it is installed
 dnl whereas aclocal (as run by autogen.sh) insists on using autoconf and fails 
hard
commit d952a53e65e1ec7a7be3b99fd8895b8e199569c2
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Fri Jul 8 17:32:49 2022 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jul 11 19:04:35 2022 +0200

    tdf#149649 sw_fieldmarkhide: delete any fieldmarks overlapping cells
    
    The DOCX bugdoc has a field that starts in the first cell of a table, but
    ends outside the table.
    
    [  28]  0x3690e10           TableNode ,
    [  29]   0x78d6f80          StartNode ,
    [  30]    0x6cfb408          TextNode "\a FORMTEXT \003Data File",
    [  31]   0x6bf9620            EndNode ,
    
    [ 631]  0x779c768            TextNode "",
    [ 632]  0x69bd5f8            TextNode "\b",
    [ 633] 0x656f150              EndNode },
    
    This triggers an assert in layout:
    soffice.bin: sw/source/core/layout/frmtool.cxx:1971: void 
InsertCnt_(SwLayoutFrame*, SwDoc*, SwNodeOffset, bool, SwNodeOffset, SwFrame*, 
sw::FrameMode): Assertion `!pLayout->HasMergedParas() || 
pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden' failed.
    
    This bad documnet model is created from writerfilter in a call to
    SwXText::convertToTable(), so add some preventive code there.
    
    The end of the field is erroneously also at the end of the body instead
    of a few paragraphs below the 1st table, because in PopFieldContext() the
    xTextAppend->createTextCursorByRange(pContext->GetStartRange()) throws,
    due to the bad document model.
    
    It turns out that Word can actually load this document, but the
    behaviour is rather funny and would be difficult to replicate...
    
    Change-Id: I20b9293db8888511bc0066c775d54fc59fcaa349
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136906
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    (cherry picked from commit 436ae10ec546391ce21875c69b0ec4bb3a06fa1f)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136921
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git a/sw/source/core/unocore/unotext.cxx 
b/sw/source/core/unocore/unotext.cxx
index 8b0f605640e6..e980679adb9e 100644
--- a/sw/source/core/unocore/unotext.cxx
+++ b/sw/source/core/unocore/unotext.cxx
@@ -61,6 +61,7 @@
 #include <doc.hxx>
 #include <IDocumentRedlineAccess.hxx>
 #include <IDocumentUndoRedo.hxx>
+#include <bookmark.hxx>
 #include <redline.hxx>
 #include <swundo.hxx>
 #include <section.hxx>
@@ -2008,6 +2009,65 @@ void SwXText::Impl::ConvertCell(
     SwNodeRange aCellRange(aStartCellPam.Start()->nNode,
             aEndCellPam.End()->nNode);
     rRowNodes.push_back(aCellRange); // note: invalidates pLastCell!
+
+    // tdf#149649 delete any fieldmarks overlapping the cell
+    IDocumentMarkAccess & rIDMA(*m_pDoc->getIDocumentMarkAccess());
+    while (::sw::mark::IFieldmark *const pMark = 
rIDMA.getFieldmarkFor(*aStartCellPam.Start()))
+    {
+        if (pMark->GetMarkEnd() <= *aEndCellPam.End())
+        {
+            if (pMark->GetMarkStart() < *aStartCellPam.Start())
+            {
+                SAL_INFO("sw.uno", "deleting fieldmark overlapping table 
cell");
+                rIDMA.deleteMark(pMark);
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
+            if (*aStartCellPam.Start() <= sepPos && sepPos <= 
*aEndCellPam.End())
+            {
+                SAL_INFO("sw.uno", "deleting fieldmark with separator in table 
cell");
+                rIDMA.deleteMark(pMark);
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+    while (::sw::mark::IFieldmark *const pMark = 
rIDMA.getFieldmarkFor(*aEndCellPam.End()))
+    {
+        if (*aStartCellPam.Start() <= pMark->GetMarkStart())
+        {
+            if (*aEndCellPam.End() < pMark->GetMarkEnd())
+            {
+                SAL_INFO("sw.uno", "deleting fieldmark overlapping table 
cell");
+                rIDMA.deleteMark(pMark);
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
+            if (*aStartCellPam.Start() <= sepPos && sepPos <= 
*aEndCellPam.End())
+            {
+                SAL_INFO("sw.uno", "deleting fieldmark with separator in table 
cell");
+                rIDMA.deleteMark(pMark);
+            }
+            else
+            {
+                break;
+            }
+       }
+    }
 }
 
 typedef uno::Sequence< text::TableColumnSeparator > TableColumnSeparators;
commit d81c4bd0862a00be0d509571c35d30c935d0356e
Author:     Stephan Bergmann <sberg...@redhat.com>
AuthorDate: Fri Jul 8 16:47:01 2022 +0200
Commit:     Andras Timar <andras.ti...@collabora.com>
CommitDate: Mon Jul 11 19:04:35 2022 +0200

    rhbz#2104545: Only call utl::IsYounger when its result is actually used
    
    ...as it may be expensive, or even throw (uncaught) exceptions (as 
apparently
    happened at rhbz#2104545, throwing some css::uno::RuntimeException while 
aMedObj
    was an sftp URL).
    
    The two branches in the if statement's condition that will now potentially 
call
    physObjIsOlder are disjoint (one for aMedObj being a file URL, the other for
    aMedObj being any WebDAV-related URL), so there is no chance that this 
change
    accidentally causes utl::IsYounger to be called more often than it used to 
be
    called.
    
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136904
    Tested-by: Jenkins
    Reviewed-by: Stephan Bergmann <sberg...@redhat.com>
    (cherry picked from commit 27ffdcf096a7e9863489599dd80528b088d1e9b8)
    Conflicts:
            sfx2/source/view/viewfrm.cxx
    
    Change-Id: I29a5f18a12a8b83ec603366db26451175b5622c9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/136909
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
index f8940b3ae8b4..2b58dd5a44bc 100644
--- a/sfx2/source/view/viewfrm.cxx
+++ b/sfx2/source/view/viewfrm.cxx

... etc. - the rest is truncated

Reply via email to