desktop/qa/desktop_lib/test_desktop_lib.cxx  |    8 
 desktop/source/lib/init.cxx                  |  144 +++--
 include/LibreOfficeKit/LibreOfficeKit.h      |    9 
 include/LibreOfficeKit/LibreOfficeKit.hxx    |   30 +
 include/LibreOfficeKit/LibreOfficeKitEnums.h |   41 +
 include/sal/log-areas.dox                    |    1 
 include/sfx2/lokhelper.hxx                   |    2 
 include/sfx2/viewsh.hxx                      |   11 
 libreofficekit/source/gtk/lokdocview.cxx     |    3 
 sfx2/source/view/lokhelper.cxx               |   17 
 sfx2/source/view/viewsh.cxx                  |  741 +++++++++++++++++++++++++++
 11 files changed, 968 insertions(+), 39 deletions(-)

New commits:
commit 14b3ad0b4e5a540f436db3467f8a4051392f8479
Author:     Marco Cecchetti <marco.cecche...@collabora.com>
AuthorDate: Thu Mar 23 08:33:16 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Jun 2 08:04:38 2023 +0200

    lok: accessibility event listener for focused paragraph
    
    LOKDocumentFocusListener keeps track of the currently focused
    paragraph.
    
    Also includes:
    
            Author: Andras Timar <andras.ti...@collabora.com>
            Date:   Mon May 15 22:06:10 2023 +0200
    
                fix unused exception parameter 'e'
    
                Change-Id: Ibfee099e4e9b724648d7500b9ebb4e8ab84989b8
    
    and:
    
            Author: Marco Cecchetti <marco.cecche...@collabora.com>
            Date:   Thu May 4 12:11:53 2023 +0200
    
                lok: a11y: focused paragraph info sent to client
    
                For the currently focused paragraph the following data is 
notified to
                client:
                paragraph content, caret position, text selection start/end
                These data is kept as an instance state so the client can 
request such
                info at any time.
    
                Change-Id: Ic1a3be0d93472300b1b6a91fb0de5bad87c031aa
    
    and:
    
            Author: Marco Cecchetti <marco.cecche...@collabora.com>
            Date:   Sun May 7 11:52:14 2023 +0200
    
                fixup! lok: accessibility event listener for focused paragraph
    
                It seems it was not a good idea using a unique_ptr as smart 
pointer
                for an instance of LOKDocumentFocusListener
    
                Change-Id: I8e6b0f48fee3c5db3c9b074a663f7f3fb96a601e
    
    Change-Id: I0fa400694f3129608228ade0b96e0b4e0aee87e2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152488
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 675ebfe82e8d..74db478c09a3 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -3641,9 +3641,15 @@ void DesktopLOKTest::testABI()
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(67), offsetof(struct 
_LibreOfficeKitDocumentClass, getEditMode));
     CPPUNIT_ASSERT_EQUAL(documentClassOffset(68),
                          offsetof(struct _LibreOfficeKitDocumentClass, 
setViewTimezone));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(69),
+                         offsetof(struct _LibreOfficeKitDocumentClass, 
setAccessibilityState));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(70),
+                         offsetof(struct _LibreOfficeKitDocumentClass, 
getA11yFocusedParagraph));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(71),
+                         offsetof(struct _LibreOfficeKitDocumentClass, 
getA11yCaretPosition));
 
     // As above
-    CPPUNIT_ASSERT_EQUAL(documentClassOffset(69), sizeof(struct 
_LibreOfficeKitDocumentClass));
+    CPPUNIT_ASSERT_EQUAL(documentClassOffset(72), sizeof(struct 
_LibreOfficeKitDocumentClass));
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest);
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 8561760febce..13922571d9ce 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1309,6 +1309,11 @@ static void 
doc_sendContentControlEvent(LibreOfficeKitDocument* pThis, const cha
 
 static void doc_setViewTimezone(LibreOfficeKitDocument* pThis, int nId, const 
char* timezone);
 
+static void doc_setAccessibilityState(LibreOfficeKitDocument* pThis, int nId, 
bool bEnabled);
+
+static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis);
+
+static int doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis);
 } // extern "C"
 
 namespace {
@@ -1361,6 +1366,45 @@ vcl::Font FindFont_FallbackToDefault(std::u16string_view 
rFontName)
     return OutputDevice::GetDefaultFont(DefaultFontType::SANS_UNICODE, 
LANGUAGE_NONE,
                                         GetDefaultFontFlags::NONE);
 }
+
+int getDocumentType (LibreOfficeKitDocument* pThis)
+{
+    SetLastExceptionMsg();
+
+    LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+    try
+    {
+        uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, 
uno::UNO_QUERY_THROW);
+
+        if 
(xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+        {
+            return LOK_DOCTYPE_SPREADSHEET;
+        }
+        else if 
(xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
+        {
+            return LOK_DOCTYPE_PRESENTATION;
+        }
+        else if 
(xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
+        {
+            return LOK_DOCTYPE_DRAWING;
+        }
+        else if (xDocument->supportsService("com.sun.star.text.TextDocument") 
|| xDocument->supportsService("com.sun.star.text.WebDocument"))
+        {
+            return LOK_DOCTYPE_TEXT;
+        }
+        else
+        {
+            SetLastExceptionMsg("unknown document type");
+        }
+    }
+    catch (const uno::Exception& exception)
+    {
+        SetLastExceptionMsg("exception: " + exception.Message);
+    }
+    return LOK_DOCTYPE_OTHER;
+}
+
 } // anonymous namespace
 
 LibLODocument_Impl::LibLODocument_Impl(uno::Reference <css::lang::XComponent> 
xComponent, int nDocumentId)
@@ -1458,6 +1502,11 @@ LibLODocument_Impl::LibLODocument_Impl(uno::Reference 
<css::lang::XComponent> xC
 
         m_pDocumentClass->setViewTimezone = doc_setViewTimezone;
 
+        m_pDocumentClass->setAccessibilityState = doc_setAccessibilityState;
+
+        m_pDocumentClass->getA11yFocusedParagraph = 
doc_getA11yFocusedParagraph;
+        m_pDocumentClass->getA11yCaretPosition = doc_getA11yCaretPosition;
+
         gDocumentClass = m_pDocumentClass;
     }
     pClass = m_pDocumentClass.get();
@@ -1764,6 +1813,9 @@ void CallbackFlushHandler::queue(const int type, 
CallbackData& aCallbackData)
         case LOK_CALLBACK_INVALIDATE_SHEET_GEOMETRY:
         case LOK_CALLBACK_REFERENCE_MARKS:
         case LOK_CALLBACK_CELL_AUTO_FILL_AREA:
+        case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
+        case LOK_CALLBACK_A11Y_CARET_CHANGED:
+        case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
         {
             const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), 
type);
             auto pos2 = toQueue2(pos);
@@ -1822,6 +1874,9 @@ void CallbackFlushHandler::queue(const int type, 
CallbackData& aCallbackData)
             case LOK_CALLBACK_SET_PART:
             case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE:
             case LOK_CALLBACK_RULER_UPDATE:
+            case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
+            case LOK_CALLBACK_A11Y_CARET_CHANGED:
+            case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
             {
                 if (removeAll(type))
                     SAL_INFO("lok", "Removed dups of [" << type << "]: [" << 
aCallbackData.getPayload() << "].");
@@ -3615,40 +3670,7 @@ static int doc_getDocumentType (LibreOfficeKitDocument* 
pThis)
     comphelper::ProfileZone aZone("doc_getDocumentType");
 
     SolarMutexGuard aGuard;
-    SetLastExceptionMsg();
-
-    LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
-
-    try
-    {
-        uno::Reference<lang::XServiceInfo> xDocument(pDocument->mxComponent, 
uno::UNO_QUERY_THROW);
-
-        if 
(xDocument->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
-        {
-            return LOK_DOCTYPE_SPREADSHEET;
-        }
-        else if 
(xDocument->supportsService("com.sun.star.presentation.PresentationDocument"))
-        {
-            return LOK_DOCTYPE_PRESENTATION;
-        }
-        else if 
(xDocument->supportsService("com.sun.star.drawing.DrawingDocument"))
-        {
-            return LOK_DOCTYPE_DRAWING;
-        }
-        else if (xDocument->supportsService("com.sun.star.text.TextDocument") 
|| xDocument->supportsService("com.sun.star.text.WebDocument"))
-        {
-            return LOK_DOCTYPE_TEXT;
-        }
-        else
-        {
-            SetLastExceptionMsg("unknown document type");
-        }
-    }
-    catch (const uno::Exception& exception)
-    {
-        SetLastExceptionMsg("exception: " + exception.Message);
-    }
-    return LOK_DOCTYPE_OTHER;
+    return getDocumentType(pThis);
 }
 
 static int doc_getParts (LibreOfficeKitDocument* pThis)
@@ -3770,6 +3792,46 @@ static char* 
doc_getPartPageRectangles(LibreOfficeKitDocument* pThis)
     return convertOUString(pDoc->getPartPageRectangles());
 }
 
+static char* doc_getA11yFocusedParagraph(LibreOfficeKitDocument* pThis)
+{
+    SolarMutexGuard aGuard;
+    SetLastExceptionMsg();
+
+    ITiledRenderable* pDoc = getTiledRenderable(pThis);
+    if (!pDoc)
+    {
+        SetLastExceptionMsg("Document doesn't support tiled rendering");
+        return nullptr;
+    }
+
+    if (SfxViewShell* pViewShell = SfxViewShell::Current())
+    {
+        return convertOUString(pViewShell->getA11yFocusedParagraph());
+
+    }
+    return nullptr;
+}
+
+static int  doc_getA11yCaretPosition(LibreOfficeKitDocument* pThis)
+{
+    SolarMutexGuard aGuard;
+    SetLastExceptionMsg();
+
+    ITiledRenderable* pDoc = getTiledRenderable(pThis);
+    if (!pDoc)
+    {
+        SetLastExceptionMsg("Document doesn't support tiled rendering");
+        return -1;
+    }
+    if (SfxViewShell* pViewShell = SfxViewShell::Current())
+    {
+        return pViewShell->getA11yCaretPosition();
+
+    }
+    return -1;
+
+}
+
 static char* doc_getPartName(LibreOfficeKitDocument* pThis, int nPart)
 {
     comphelper::ProfileZone aZone("doc_getPartName");
@@ -6395,8 +6457,6 @@ static void doc_setViewLanguage(SAL_UNUSED_PARAMETER 
LibreOfficeKitDocument* /*p
     SfxLokHelper::setViewLocale(nId, sLanguage);
 }
 
-
-
 unsigned char* doc_renderFont(LibreOfficeKitDocument* pThis,
                               const char* pFontName,
                               const char* pChar,
@@ -6938,6 +6998,18 @@ static void doc_setViewTimezone(SAL_UNUSED_PARAMETER 
LibreOfficeKitDocument* /*p
     }
 }
 
+static void doc_setAccessibilityState(SAL_UNUSED_PARAMETER 
LibreOfficeKitDocument* pThis, int nId, bool nEnabled)
+{
+    SolarMutexGuard aGuard;
+    SetLastExceptionMsg();
+
+    int nDocType = getDocumentType(pThis);
+    if (nDocType != LOK_DOCTYPE_TEXT)
+        return;
+
+    SfxLokHelper::setAccessibilityState(nId, nEnabled);
+}
+
 static char* lo_getError (LibreOfficeKit *pThis)
 {
     comphelper::ProfileZone aZone("lo_getError");
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h 
b/include/LibreOfficeKit/LibreOfficeKit.h
index 767469666fef..ba51c5afeea9 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -493,6 +493,15 @@ struct _LibreOfficeKitDocumentClass
     /// @see lok::Document::setViewTimezone().
     void (*setViewTimezone) (LibreOfficeKitDocument* pThis, int nId, const 
char* timezone);
 
+    /// @see lok::Document::setAccessibilityState().
+    void (*setAccessibilityState) (LibreOfficeKitDocument* pThis, int nId, 
bool nEnabled);
+
+    /// @see lok::Document::getA11yFocusedParagraph.
+    char* (*getA11yFocusedParagraph) (LibreOfficeKitDocument* pThis);
+
+    /// @see lok::Document::getA11yCaretPosition.
+    int (*getA11yCaretPosition) (LibreOfficeKitDocument* pThis);
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx 
b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 2946d0baaf7d..65f263c368a6 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -859,6 +859,36 @@ public:
         mpDoc->pClass->setViewTimezone(mpDoc, nId, timezone);
     }
 
+    /**
+     * Enable/Disable accessibility support for the window with the specified 
nId.
+     *
+     * @param nId a view ID, returned by createView().
+     * @param nEnabled true/false
+     */
+    void setAccessibilityState(int nId, bool nEnabled)
+    {
+        mpDoc->pClass->setAccessibilityState(mpDoc, nId, nEnabled);
+    }
+
+    /**
+     *  Get the current focused paragraph info:
+     *  {
+     *      "content": paragraph content
+     *      "start": selection start
+     *      "end": selection end
+     *  }
+     */
+    char* getA11yFocusedParagraph()
+    {
+        return mpDoc->pClass->getA11yFocusedParagraph(mpDoc);
+    }
+
+    /// Get the current text cursor position.
+    int getA11yCaretPosition()
+    {
+        return mpDoc->pClass->getA11yCaretPosition(mpDoc);
+    }
+
 #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
 };
 
diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h 
b/include/LibreOfficeKit/LibreOfficeKitEnums.h
index f57e00afa185..47b2d790738f 100644
--- a/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -922,7 +922,40 @@ typedef enum
      * Informs the LibreOfficeKit client that the background color surrounding
      * the document has changed.
     */
-   LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR = 61
+   LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR = 61,
+
+    /**
+     * Accessibility event: a paragraph get focus.
+     * The payload is a json with the following structure.
+     *
+     *   {
+     *       "content": "<paragraph text>"
+     *       "position": N
+     *   }
+     *   where N is the position of the text cursor inside the focused 
paragraph.
+     */
+    LOK_CALLBACK_A11Y_FOCUS_CHANGED = 62,
+
+    /**
+     * Accessibility event: text cursor position has changed.
+     *
+     *  {
+     *      "position": N
+     *  }
+     *  where N is the position of the text cursor inside the focused 
paragraph.
+     */
+    LOK_CALLBACK_A11Y_CARET_CHANGED = 63,
+
+    /**
+     * Accessibility event: text cursor position has changed.
+     *
+     *  {
+     *      "start": N1
+     *      "end": N2
+     *  }
+     *  where [N1,N2] is the range of the text selection inside the focused 
paragraph.
+     */
+    LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED = 64
 }
 LibreOfficeKitCallbackType;
 
@@ -1075,6 +1108,12 @@ static inline const char* lokCallbackTypeToString(int 
nType)
         return "LOK_CALLBACK_VIEW_RENDER_STATE";
     case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR:
         return "LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR";
+    case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
+        return "LOK_CALLBACK_A11Y_FOCUS_CHANGED";
+    case LOK_CALLBACK_A11Y_CARET_CHANGED:
+        return "LOK_CALLBACK_A11Y_CARET_CHANGED";
+    case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
+        return "LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED";
     }
 
     assert(!"Unknown LibreOfficeKitCallbackType type.");
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 51b005e1bb51..f209b6244f5f 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -325,6 +325,7 @@ certain functionality.
 @li @c lok.fontsubst
 @li @c lok.tiledrendering
 @li @c lok.dialog
+@li @c lok.a11y - LOK accessibility
 
 @section l10ntools
 
diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx
index d975b4ade2bf..08d66fd538cc 100644
--- a/include/sfx2/lokhelper.hxx
+++ b/include/sfx2/lokhelper.hxx
@@ -83,6 +83,8 @@ public:
     static void setViewLanguage(int nId, const OUString& rBcp47LanguageTag);
     /// Set the default language for views.
     static void setDefaultLanguage(const OUString& rBcp47LanguageTag);
+    /// Enable/Disable AT support for the given view.
+    static void setAccessibilityState(int nId, bool nEnabled);
     /// Get the language used by the loading view (used for all save 
operations).
     static const LanguageTag & getLoadLanguage();
     /// Set the language used by the loading view (used for all save 
operations).
diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx
index a9713eb2375f..b805f1cf99d4 100644
--- a/include/sfx2/viewsh.hxx
+++ b/include/sfx2/viewsh.hxx
@@ -57,6 +57,7 @@ class SfxPrinter;
 class NotifyEvent;
 class SfxInPlaceClient;
 class SfxLokCallbackInterface;
+class LOKDocumentFocusListener;
 class SfxStoringHelper;
 class VCLXPopupMenu;
 namespace rtl { class OStringBuffer; }
@@ -173,6 +174,8 @@ friend class SfxPrinterController;
     LanguageTag                 maLOKLanguageTag;
     LanguageTag                 maLOKLocale;
     LOKDeviceFormFactor         maLOKDeviceFormFactor;
+    bool                        mbLOKAccessibilityEnabled;
+    rtl::Reference<LOKDocumentFocusListener>   mpLOKDocumentFocusListener;
     std::unordered_set<OUString>    mvLOKBlockedCommandList;
     OUString maLOKTimezone;
     bool maLOKIsTimezoneSet;
@@ -215,6 +218,9 @@ private:
     /// SfxInterface initializer.
     static void InitInterface_Impl();
 
+    LOKDocumentFocusListener& GetLOKDocumentFocusListener();
+    const LOKDocumentFocusListener& GetLOKDocumentFocusListener() const;
+
 public:
 
                                 SfxViewShell( SfxViewFrame& rFrame, 
SfxViewShellFlags nFlags );
@@ -427,6 +433,8 @@ public:
     void SetLOKLanguageTag(const OUString& rBcp47LanguageTag);
     /// Get the LibreOfficeKit language of this view.
     const LanguageTag& GetLOKLanguageTag() const { return maLOKLanguageTag; }
+    /// Enable/Disable LibreOfficeKit AT support for this view.
+    void SetLOKAccessibilityState(bool bEnabled);
 
     /// Get the LibreOfficeKit timezone of this view. See @SetLOKTimezone.
     std::pair<bool, OUString> GetLOKTimezone() const
@@ -466,6 +474,9 @@ public:
 
     StylesHighlighterColorMap& GetStylesHighlighterParaColorMap() { return 
ParaStylesColorMap; }
     StylesHighlighterColorMap& GetStylesHighlighterCharColorMap() { return 
CharStylesColorMap; }
+
+    OUString getA11yFocusedParagraph() const;
+    int getA11yCaretPosition() const;
 };
 
 #endif // INCLUDED_SFX2_VIEWSH_HXX
diff --git a/libreofficekit/source/gtk/lokdocview.cxx 
b/libreofficekit/source/gtk/lokdocview.cxx
index 76f94293956b..6d810f2d1e3f 100644
--- a/libreofficekit/source/gtk/lokdocview.cxx
+++ b/libreofficekit/source/gtk/lokdocview.cxx
@@ -1488,6 +1488,9 @@ callback (gpointer pData)
     case LOK_CALLBACK_EXPORT_FILE:
     case LOK_CALLBACK_VIEW_RENDER_STATE:
     case LOK_CALLBACK_APPLICATION_BACKGROUND_COLOR:
+    case LOK_CALLBACK_A11Y_FOCUS_CHANGED:
+    case LOK_CALLBACK_A11Y_CARET_CHANGED:
+    case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
     {
         // TODO: Implement me
         break;
diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx
index ad78f01e7398..356925a6a341 100644
--- a/sfx2/source/view/lokhelper.cxx
+++ b/sfx2/source/view/lokhelper.cxx
@@ -145,10 +145,11 @@ void SfxLokHelper::destroyView(int nId)
     const ViewShellId nViewShellId(nId);
     std::vector<SfxViewShell*>& rViewArr = pApp->GetViewShells_Impl();
 
-    for (const SfxViewShell* pViewShell : rViewArr)
+    for (SfxViewShell* pViewShell : rViewArr)
     {
         if (pViewShell->GetViewShellId() == nViewShellId)
         {
+            pViewShell->SetLOKAccessibilityState(false);
             SfxViewFrame& rViewFrame = pViewShell->GetViewFrame();
             SfxRequest aRequest(rViewFrame, SID_CLOSEWIN);
             rViewFrame.Exec_Impl(aRequest);
@@ -312,6 +313,20 @@ void SfxLokHelper::setViewLanguage(int nId, const 
OUString& rBcp47LanguageTag)
     }
 }
 
+void SfxLokHelper::setAccessibilityState(int nId, bool nEnabled)
+{
+    std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
+
+    for (SfxViewShell* pViewShell : rViewArr)
+    {
+        if (pViewShell->GetViewShellId() == ViewShellId(nId))
+        {
+            pViewShell->SetLOKAccessibilityState(nEnabled);
+            return;
+        }
+    }
+}
+
 void SfxLokHelper::setViewLocale(int nId, const OUString& rBcp47LanguageTag)
 {
     std::vector<SfxViewShell*>& rViewArr = SfxGetpApp()->GetViewShells_Impl();
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index 4e414d9c65bf..a818a98380bb 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -19,6 +19,8 @@
 
 #include <config_features.h>
 
+#include <boost/property_tree/json_parser.hpp>
+
 #include <sal/log.hxx>
 #include <svl/stritem.hxx>
 #include <svl/eitem.hxx>
@@ -46,9 +48,22 @@
 #include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
 #include <com/sun/star/view/XRenderable.hpp>
 #include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
 #include <cppuhelper/implbase.hxx>
 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
 
+#include <cppuhelper/weakref.hxx>
+
+#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+
 #include <comphelper/diagnose_ex.hxx>
 #include <tools/urlobj.hxx>
 #include <unotools/tempfile.hxx>
@@ -221,6 +236,664 @@ void SAL_CALL 
SfxClipboardChangeListener::changedContents( const datatransfer::c
         delete pInfo;
 }
 
+class LOKDocumentFocusListener :
+    public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener >
+{
+    static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 30;
+
+    const SfxViewShell* m_pViewShell;
+    std::set< uno::Reference< uno::XInterface > > m_aRefList;
+    OUString m_sFocusedParagraph;
+    bool m_bFocusedParagraphNotified;
+    sal_Int32 m_nCaretPosition;
+    sal_Int32 m_nSelectionStart;
+    sal_Int32 m_nSelectionEnd;
+    OUString m_sSelectedText;
+    bool m_bIsEditingCell;
+    OUString m_sSelectedCellAddress;
+
+public:
+    LOKDocumentFocusListener(const SfxViewShell* pViewShell);
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void attachRecursive(
+        const uno::Reference< accessibility::XAccessible >& xAccessible
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void attachRecursive(
+        const uno::Reference< accessibility::XAccessible >& xAccessible,
+        const uno::Reference< accessibility::XAccessibleContext >& xContext
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void attachRecursive(
+        const uno::Reference< accessibility::XAccessible >& xAccessible,
+        const uno::Reference< accessibility::XAccessibleContext >& xContext,
+        const sal_Int64 nStateSet
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void detachRecursive(
+        const uno::Reference< accessibility::XAccessible >& xAccessible
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void detachRecursive(
+        const uno::Reference< accessibility::XAccessibleContext >& xContext
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    void detachRecursive(
+        const uno::Reference< accessibility::XAccessibleContext >& xContext,
+        const sal_Int64 nStateSet
+    );
+
+    /// @throws lang::IndexOutOfBoundsException
+    /// @throws uno::RuntimeException
+    static uno::Reference< accessibility::XAccessible > getAccessible(const 
lang::EventObject& aEvent );
+
+    // XEventListener
+    virtual void SAL_CALL disposing( const lang::EventObject& Source ) 
override;
+
+    // XAccessibleEventListener
+    virtual void SAL_CALL notifyEvent( const 
accessibility::AccessibleEventObject& aEvent ) override;
+
+    void notifyFocusedParagraphChanged();
+    void notifyCaretChanged();
+    void notifyTextSelectionChanged();
+
+    OUString getFocusedParagraph() const;
+    int getCaretPosition() const;
+};
+
+LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* 
pViewShell)
+    : m_pViewShell(pViewShell)
+    , m_bFocusedParagraphNotified(false)
+    , m_nCaretPosition(0)
+    , m_nSelectionStart(0)
+    , m_nSelectionEnd(0)
+    , m_bIsEditingCell(false)
+{
+}
+
+OUString LOKDocumentFocusListener::getFocusedParagraph() const
+{
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getFocusedParagraph: " << 
m_sFocusedParagraph);
+    const_cast<LOKDocumentFocusListener*>(this)->m_bFocusedParagraphNotified = 
true;
+
+    sal_Int32 nSelectionStart = m_nSelectionStart;
+    sal_Int32 nSelectionEnd = m_nSelectionEnd;
+    if (nSelectionStart < 0 || nSelectionEnd < 0)
+        nSelectionStart = nSelectionEnd = m_nCaretPosition;
+
+    boost::property_tree::ptree aPayloadTree;
+    aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
+    aPayloadTree.put("start", nSelectionStart);
+    aPayloadTree.put("end", nSelectionEnd);
+    std::stringstream aStream;
+    boost::property_tree::write_json(aStream, aPayloadTree);
+    std::string aPayload = aStream.str();
+    OUString sRet = OUString::fromUtf8(aPayload);
+    return sRet;
+}
+
+int LOKDocumentFocusListener::getCaretPosition() const
+{
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::getCaretPosition: " << 
m_nCaretPosition);
+    return m_nCaretPosition;
+}
+
+void LOKDocumentFocusListener::notifyFocusedParagraphChanged()
+{
+    boost::property_tree::ptree aPayloadTree;
+    aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
+    aPayloadTree.put("position", m_nCaretPosition);
+    std::stringstream aStream;
+    boost::property_tree::write_json(aStream, aPayloadTree);
+    std::string aPayload = aStream.str();
+    if (m_pViewShell)
+    {
+        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyFocusedParagraphChanged: " << 
m_sFocusedParagraph);
+        m_bFocusedParagraphNotified = true;
+        const char* pPayload = aPayload.c_str();
+        
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUS_CHANGED, 
pPayload);
+    }
+}
+
+void LOKDocumentFocusListener::notifyCaretChanged()
+{
+    boost::property_tree::ptree aPayloadTree;
+    aPayloadTree.put("position", m_nCaretPosition);
+    std::stringstream aStream;
+    boost::property_tree::write_json(aStream, aPayloadTree);
+    std::string aPayload = aStream.str();
+    if (m_pViewShell)
+    {
+        SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyCaretChanged: " 
<< m_nCaretPosition);
+        const char* pPayload = aPayload.c_str();
+        
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_CARET_CHANGED, 
pPayload);
+    }
+}
+
+void LOKDocumentFocusListener::notifyTextSelectionChanged()
+{
+    boost::property_tree::ptree aPayloadTree;
+    aPayloadTree.put("start", m_nSelectionStart);
+    aPayloadTree.put("end", m_nSelectionEnd);
+    std::stringstream aStream;
+    boost::property_tree::write_json(aStream, aPayloadTree);
+    std::string aPayload = aStream.str();
+    if (m_pViewShell)
+    {
+        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyTextSelectionChanged: start: " << 
m_nSelectionStart << ", end: " << m_nSelectionEnd);
+        const char* pPayload = aPayload.c_str();
+        
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED,
 pPayload);
+    }
+}
+
+void LOKDocumentFocusListener::disposing( const lang::EventObject& aEvent )
+{
+    // Unref the object here, but do not remove as listener since the object
+    // might no longer be in a state that safely allows this.
+    if( aEvent.Source.is() )
+        m_aRefList.erase(aEvent.Source);
+
+}
+
+namespace
+{
+bool hasState(const accessibility::AccessibleEventObject& aEvent, ::sal_Int64 
nState)
+{
+    bool res = false;
+    uno::Reference< accessibility::XAccessibleContext > 
xContext(aEvent.Source, uno::UNO_QUERY);
+    if (xContext.is())
+    {
+        ::sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+        res = (nStateSet & nState) != 0;
+    }
+    return res;
+}
+
+bool isFocused(const accessibility::AccessibleEventObject& aEvent)
+{
+    return hasState(aEvent, accessibility::AccessibleStateType::FOCUSED);
+}
+} // anonymous namespace
+
+void LOKDocumentFocusListener::notifyEvent( const 
accessibility::AccessibleEventObject& aEvent )
+{
+    try
+    {
+        switch( aEvent.EventId )
+        {
+            case accessibility::AccessibleEventId::STATE_CHANGED:
+            {
+                uno::Reference< accessibility::XAccessible > xAccStateChanged 
= getAccessible(aEvent);
+                sal_Int64 nState = accessibility::AccessibleStateType::INVALID;
+                aEvent.NewValue >>= nState;
+                SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: 
STATE_CHANGED: XAccessible: " << xAccStateChanged.get() << ", nState: " << 
nState);
+
+                if( accessibility::AccessibleStateType::FOCUSED == nState )
+                {
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: FOCUSED");
+
+                    if (m_bIsEditingCell)
+                    {
+                        if (!hasState(aEvent, 
accessibility::AccessibleStateType::ACTIVE))
+                        {
+                            SAL_WARN("lok.a11y",
+                                    "LOKDocumentFocusListener::notifyEvent: 
FOCUSED: Cell not ACTIVE for editing yet");
+                            return;
+                        }
+                    }
+                    uno::Reference<css::accessibility::XAccessibleText> 
xAccText(xAccStateChanged, uno::UNO_QUERY);
+                    if( xAccText.is() )
+                    {
+                        OUString sText = xAccText->getText();
+                        sal_Int32 nLength = sText.getLength();
+                        sal_Int32 nCaretPosition = 
xAccText->getCaretPosition();
+                        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: xAccText: " << xAccText.get()
+                                << ", text: >" << sText << "<, caret pos: " << 
nCaretPosition);
+
+                        if (nLength)
+                        {
+                            
css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
+                                xAccTextAttr(xAccText, uno::UNO_QUERY);
+                            css::uno::Sequence< OUString > 
aRequestedAttributes;
+
+                            sal_Int32 nPos = 0;
+                            while (nPos < nLength)
+                            {
+                                css::accessibility::TextSegment aTextSegment =
+                                        xAccText->getTextAtIndex(nPos, 
css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
+                                SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: "
+                                        "text segment: '" << 
aTextSegment.SegmentText
+                                        << "', start: " << 
aTextSegment.SegmentStart
+                                        << ", end: " << 
aTextSegment.SegmentEnd);
+
+                                css::uno::Sequence< css::beans::PropertyValue 
> aRunAttributeList;
+                                if (xAccTextAttr.is())
+                                {
+                                    aRunAttributeList = 
xAccTextAttr->getRunAttributes(nPos, aRequestedAttributes);
+                                }
+                                else
+                                {
+                                    aRunAttributeList = 
xAccText->getCharacterAttributes(nPos, aRequestedAttributes);
+                                }
+
+                                sal_Int32 nSize = 
aRunAttributeList.getLength();
+                                SAL_INFO("lok.a11y",
+                                         
"LOKDocumentFocusListener::notifyEvent: attribute list size: " << nSize);
+                                if (nSize)
+                                {
+                                    OUString sValue;
+                                    OUString sAttributes = "{ ";
+                                    for (const auto& attribute: 
aRunAttributeList)
+                                    {
+                                        if (attribute.Name.isEmpty())
+                                            continue;
+
+                                        if (attribute.Name == "CharHeight" || 
attribute.Name == "CharWeight")
+                                        {
+                                            float fValue;
+                                            attribute.Value >>= fValue;
+                                            sValue = OUString::number(fValue);
+                                        }
+                                        else if (attribute.Name == 
"CharPosture")
+                                        {
+                                            awt::FontSlant nValue;
+                                            attribute.Value >>= nValue;
+                                            sValue = 
OUString::number(static_cast<unsigned int>(nValue));
+                                        }
+                                        else if (attribute.Name == 
"CharUnderline")
+                                        {
+                                            sal_Int16 nValue;
+                                            attribute.Value >>= nValue;
+                                            sValue = OUString::number(nValue);
+                                        }
+                                        else if (attribute.Name == 
"CharFontName")
+                                        {
+                                            attribute.Value >>= sValue;
+                                        }
+                                        else if (attribute.Name == "Rsid")
+                                        {
+                                            sal_uInt32 nValue;
+                                            attribute.Value >>= nValue;
+                                            sValue = OUString::number(nValue);
+                                        }
+
+                                        if (!sValue.isEmpty())
+                                        {
+                                            if (sAttributes != "{ ")
+                                                sAttributes += ", ";
+                                            sAttributes += attribute.Name + ": 
" + sValue;
+                                            sValue = "";
+                                        }
+                                    }
+                                    sAttributes += " }";
+                                    SAL_INFO("lok.a11y",
+                                             
"LOKDocumentFocusListener::notifyEvent: attributes: " << sAttributes);
+                                }
+                                nPos = aTextSegment.SegmentEnd + 1;
+                            }
+                        }
+                        if (!m_bFocusedParagraphNotified || 
m_sFocusedParagraph != sText)
+                        {
+                            m_sFocusedParagraph = sText;
+                            m_nCaretPosition = nCaretPosition;
+                            notifyFocusedParagraphChanged();
+                        }
+                    }
+                }
+
+                break;
+            }
+
+            case accessibility::AccessibleEventId::CARET_CHANGED:
+            {
+                if (!isFocused(aEvent))
+                {
+                    SAL_WARN("lok.a11y",
+                             "LOKDocumentFocusListener::notifyEvent: 
CARET_CHANGED: skip non focused paragraph");
+                    return;
+                }
+
+                sal_Int32 nNewPos = -1;
+                aEvent.NewValue >>= nNewPos;
+                sal_Int32 nOldPos = -1;
+                aEvent.OldValue >>= nOldPos;
+
+                if (nNewPos >= 0)
+                {
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: new pos: " << nNewPos << 
", nOldPos: " << nOldPos);
+                    uno::Reference<css::accessibility::XAccessibleText>
+                        xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                    if( xAccText.is() )
+                    {
+                        OUString sText = xAccText->getText();
+                        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: xAccText: " << 
xAccText.get() << ", text: >" << sText << "<");
+
+                        m_nCaretPosition = nNewPos;
+                        m_nSelectionStart = m_nSelectionEnd = m_nCaretPosition;
+                        notifyCaretChanged();
+                    }
+                }
+
+                break;
+            }
+
+            case accessibility::AccessibleEventId::TEXT_CHANGED:
+            {
+                if (!isFocused(aEvent))
+                {
+                    SAL_WARN("lok.a11y",
+                             "LOKDocumentFocusListener::notifyEvent: 
TEXT_CHANGED: skip non focused paragraph");
+                    return;
+                }
+
+                accessibility::TextSegment aDeletedText;
+                accessibility::TextSegment aInsertedText;
+
+                if (aEvent.OldValue >>= aDeletedText)
+                {
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: deleted text: >" << 
aDeletedText.SegmentText << "<");
+                }
+                if (aEvent.NewValue >>= aInsertedText)
+                {
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: inserted text: >" << 
aInsertedText.SegmentText << "<");
+                }
+                uno::Reference<css::accessibility::XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                if (xAccText.is())
+                {
+                    OUString sText = xAccText->getText();
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
+                            "xAccText: " << xAccText.get() << ", text: >" << 
sText << "<");
+                    m_sFocusedParagraph = sText;
+                    m_bFocusedParagraphNotified = false;
+                }
+
+                break;
+            }
+            case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED:
+            {
+                if (!isFocused(aEvent))
+                {
+                    SAL_WARN("lok.a11y",
+                          "LOKDocumentFocusListener::notifyEvent: 
TEXT_SELECTION_CHANGED: skip non focused paragraph");
+                    return;
+                }
+
+                uno::Reference<css::accessibility::XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                if (xAccText.is())
+                {
+                    OUString sText = xAccText->getText();
+                    sal_Int32 nSelectionStart = xAccText->getSelectionStart();
+                    sal_Int32 nSelectionEnd = xAccText->getSelectionEnd();
+                    m_sSelectedText = xAccText->getSelectedText();
+
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: "
+                            "\n  xAccText: " << xAccText.get() << ", text: >" 
<< sText << "<"
+                            "\n  start: " << nSelectionStart << ", end: " << 
nSelectionEnd
+                            << "\n  selected text: >" << m_sSelectedText << 
"<");
+
+                    // This should not be risky since selection start/end are 
set also on CARET_CHANGED event
+                    if (nSelectionStart == m_nSelectionStart && nSelectionEnd 
== m_nSelectionEnd)
+                        return;
+
+                    // We send a message to client also when start/end are -1, 
in this way the client knows
+                    // if a text selection object exists or not. That's needed 
because of the odd behavior
+                    // occurring when <backspace>/<delete> are hit and a text 
selection is empty but it still exists.
+                    // Such keys delete the empty selection instead of the 
previous/next char.
+                    m_nSelectionStart = nSelectionStart;
+                    m_nSelectionEnd = nSelectionEnd;
+
+                    // Calc: when editing a formula send the update content
+                    if (m_bIsEditingCell && !m_sSelectedCellAddress.isEmpty()
+                            && !m_sSelectedText.isEmpty() && 
sText.startsWith("="))
+                    {
+                        notifyFocusedParagraphChanged();
+                    }
+                    notifyTextSelectionChanged();
+                }
+
+                break;
+            }
+            case accessibility::AccessibleEventId::SELECTION_CHANGED:
+            {
+                uno::Reference< accessibility::XAccessible > xNewValue;
+                aEvent.NewValue >>= xNewValue;
+                if (xNewValue.is())
+                {
+                    uno::Reference< accessibility::XAccessibleContext > 
xContext =
+                            xNewValue->getAccessibleContext();
+
+                    if (xContext.is())
+                    {
+                        OUString sName = xContext->getAccessibleName();
+                        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: this: " << this
+                                << ", selected cell address: >" << sName << "<"
+                                ", m_bIsEditingCell: " << m_bIsEditingCell);
+                        if (m_bIsEditingCell && !sName.isEmpty())
+                        {
+                            m_sSelectedCellAddress = sName;
+                            // Check cell address: "$Sheet1.A10".
+                            // On cell editing SELECTION_CHANGED is not 
emitted when selection is expanded.
+                            // So selection can't be a cell range.
+                            sal_Int32 nDotIndex = m_sSelectedText.indexOf('.');
+                            OUString sCellAddress = 
m_sSelectedText.copy(nDotIndex + 1);
+                            SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: "
+                                    "cell address: >" << sCellAddress << "<");
+                            if (m_sSelectedCellAddress == sCellAddress)
+                            {
+                                notifyFocusedParagraphChanged();
+                                notifyTextSelectionChanged();
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+            case accessibility::AccessibleEventId::CHILD:
+            {
+                uno::Reference< accessibility::XAccessible > xChild;
+                if( (aEvent.OldValue >>= xChild) && xChild.is() )
+                    detachRecursive(xChild);
+
+                if( (aEvent.NewValue >>= xChild) && xChild.is() )
+                    attachRecursive(xChild);
+
+                break;
+            }
+
+            case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+                SAL_INFO("lok.a11y", "Invalidate all children called");
+                break;
+
+            default:
+                break;
+        }
+    }
+    catch( const lang::IndexOutOfBoundsException& )
+    {
+        SAL_WARN("lok.a11y", "Focused object has invalid index in parent");
+    }
+}
+
+uno::Reference< accessibility::XAccessible > 
LOKDocumentFocusListener::getAccessible(const lang::EventObject& aEvent )
+{
+    uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, 
uno::UNO_QUERY);
+
+    if( xAccessible.is() )
+        return xAccessible;
+
+    uno::Reference< accessibility::XAccessibleContext > 
xContext(aEvent.Source, uno::UNO_QUERY);
+
+    if( xContext.is() )
+    {
+        uno::Reference< accessibility::XAccessible > xParent( 
xContext->getAccessibleParent() );
+        if( xParent.is() )
+        {
+            uno::Reference< accessibility::XAccessibleContext > 
xParentContext( xParent->getAccessibleContext() );
+            if( xParentContext.is() )
+            {
+                return xParentContext->getAccessibleChild( 
xContext->getAccessibleIndexInParent() );
+            }
+        }
+    }
+
+    return uno::Reference< accessibility::XAccessible >();
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+    const uno::Reference< accessibility::XAccessible >& xAccessible
+)
+{
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(1): 
xAccessible: " << xAccessible.get());
+
+    uno::Reference< accessibility::XAccessibleContext > xContext =
+        xAccessible->getAccessibleContext();
+
+    if( xContext.is() )
+        attachRecursive(xAccessible, xContext);
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+    const uno::Reference< accessibility::XAccessible >& xAccessible,
+    const uno::Reference< accessibility::XAccessibleContext >& xContext
+)
+{
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(2): 
xAccessible: " << xAccessible.get()
+            << ", role: " << xContext->getAccessibleRole()
+            << ", name: " << xContext->getAccessibleName()
+            << ", parent: " << xContext->getAccessibleParent().get()
+            << ", child count: " << xContext->getAccessibleChildCount());
+
+    sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+
+    if (!m_bIsEditingCell)
+    {
+        ::rtl::OUString sName = xContext->getAccessibleName();
+        m_bIsEditingCell = sName.startsWith("Cell");
+    }
+
+    attachRecursive(xAccessible, xContext, nStateSet);
+}
+
+void LOKDocumentFocusListener::attachRecursive(
+    const uno::Reference< accessibility::XAccessible >& xAccessible,
+    const uno::Reference< accessibility::XAccessibleContext >& xContext,
+    const sal_Int64 nStateSet
+)
+{
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #1: 
this: " << this
+            << ", xAccessible: " << xAccessible.get()
+            << ", role: " << xContext->getAccessibleRole()
+            << ", name: " << xContext->getAccessibleName()
+            << ", parent: " << xContext->getAccessibleParent().get()
+            << ", child count: " << xContext->getAccessibleChildCount());
+
+    uno::Reference< accessibility::XAccessibleEventBroadcaster > 
xBroadcaster(xContext, uno::UNO_QUERY);
+
+    if (!xBroadcaster.is())
+        return;
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #2: 
xBroadcaster.is()");
+    // If not already done, add the broadcaster to the list and attach as 
listener.
+    const uno::Reference< uno::XInterface >& xInterface = xBroadcaster;
+    if( m_aRefList.insert(xInterface).second )
+    {
+        SAL_INFO("lok.a11y", "LOKDocumentFocusListener::attachRecursive(3) #3: 
m_aRefList.insert(xInterface).second");
+        xBroadcaster->addAccessibleEventListener(static_cast< 
accessibility::XAccessibleEventListener *>(this));
+
+
+        if( !(nStateSet & 
accessibility::AccessibleStateType::MANAGES_DESCENDANTS) )
+        {
+            sal_Int64 nmax = xContext->getAccessibleChildCount();
+            if( nmax > MAX_ATTACHABLE_CHILDREN )
+                nmax = MAX_ATTACHABLE_CHILDREN;
+
+            for( sal_Int64 n = 0; n < nmax; n++ )
+            {
+                uno::Reference< accessibility::XAccessible > xChild( 
xContext->getAccessibleChild( n ) );
+
+                if( xChild.is() )
+                    attachRecursive(xChild);
+            }
+        }
+    }
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+    const uno::Reference< accessibility::XAccessible >& xAccessible
+)
+{
+    uno::Reference< accessibility::XAccessibleContext > xContext =
+        xAccessible->getAccessibleContext();
+
+    if( xContext.is() )
+        detachRecursive(xContext);
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+    const uno::Reference< accessibility::XAccessibleContext >& xContext
+)
+{
+    sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+
+    SAL_INFO("lok.a11y", "LOKDocumentFocusListener::detachRecursive(2): this: 
" << this
+            << ", name: " << xContext->getAccessibleName()
+            << ", parent: " << xContext->getAccessibleParent().get()
+            << ", child count: " << xContext->getAccessibleChildCount());
+
+    if (m_bIsEditingCell)
+    {
+        ::rtl::OUString sName = xContext->getAccessibleName();
+        m_bIsEditingCell = !sName.startsWith("Cell");
+        if (!m_bIsEditingCell)
+        {
+            m_sFocusedParagraph = "";
+            m_nCaretPosition = 0;
+            notifyFocusedParagraphChanged();
+        }
+    }
+
+    detachRecursive(xContext, nStateSet);
+}
+
+void LOKDocumentFocusListener::detachRecursive(
+    const uno::Reference< accessibility::XAccessibleContext >& xContext,
+    const sal_Int64 nStateSet
+)
+{
+    uno::Reference< accessibility::XAccessibleEventBroadcaster > 
xBroadcaster(xContext, uno::UNO_QUERY);
+
+    if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) )
+    {
+        xBroadcaster->removeAccessibleEventListener(static_cast< 
accessibility::XAccessibleEventListener *>(this));
+
+        if( !( nStateSet & 
accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) )
+        {
+            sal_Int64 nmax = xContext->getAccessibleChildCount();
+            if( nmax > MAX_ATTACHABLE_CHILDREN )
+                nmax = MAX_ATTACHABLE_CHILDREN;
+
+            for( sal_Int64 n = 0; n < nmax; n++ )
+            {
+                uno::Reference< accessibility::XAccessible > xChild( 
xContext->getAccessibleChild( n ) );
+
+                if( xChild.is() )
+                    detachRecursive(xChild);
+            }
+        }
+    }
+}
+
 sal_uInt32 SfxViewShell_Impl::m_nLastViewShellId = 0;
 
 SfxViewShell_Impl::SfxViewShell_Impl(SfxViewShellFlags const nFlags, 
ViewShellDocId nDocId)
@@ -1074,6 +1747,7 @@ SfxViewShell::SfxViewShell
 ,   maLOKLanguageTag(LANGUAGE_NONE)
 ,   maLOKLocale(LANGUAGE_NONE)
 ,   maLOKDeviceFormFactor(LOKDeviceFormFactor::UNKNOWN)
+,   mbLOKAccessibilityEnabled(false)
 {
     SetMargin( rViewFrame.GetMargin_Impl() );
 
@@ -1126,6 +1800,18 @@ SfxViewShell::~SfxViewShell()
         pFrameWin->ReleaseLOKNotifier();
 }
 
+OUString SfxViewShell::getA11yFocusedParagraph() const
+{
+    const LOKDocumentFocusListener& rDocFocusListener = 
GetLOKDocumentFocusListener();
+    return rDocFocusListener.getFocusedParagraph();
+}
+
+int SfxViewShell::getA11yCaretPosition() const
+{
+    const LOKDocumentFocusListener& rDocFocusListener = 
GetLOKDocumentFocusListener();
+    return rDocFocusListener.getCaretPosition();
+}
+
 bool SfxViewShell::PrepareClose
 (
     bool bUI     // TRUE: Allow Dialog and so on, FALSE: silent-mode
@@ -1645,6 +2331,61 @@ void SfxViewShell::SetLOKLanguageTag(const OUString& 
rBcp47LanguageTag)
         maLOKLanguageTag = aFallbackTag;
 }
 
+LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener()
+{
+    if (mpLOKDocumentFocusListener)
+        return *mpLOKDocumentFocusListener;
+
+    mpLOKDocumentFocusListener = new LOKDocumentFocusListener(this);
+    return *mpLOKDocumentFocusListener;
+}
+
+const LOKDocumentFocusListener& SfxViewShell::GetLOKDocumentFocusListener() 
const
+{
+    return const_cast<SfxViewShell*>(this)->GetLOKDocumentFocusListener();
+}
+
+void SfxViewShell::SetLOKAccessibilityState(bool bEnabled)
+{
+    if (bEnabled == mbLOKAccessibilityEnabled)
+        return;
+    mbLOKAccessibilityEnabled = bEnabled;
+
+    LOKDocumentFocusListener& rDocumentFocusListener = 
GetLOKDocumentFocusListener();
+
+    if (!pWindow)
+        return;
+
+    uno::Reference< accessibility::XAccessible > xAccessible =
+        pWindow->GetAccessible();
+
+    if (!xAccessible.is())
+        return;
+
+    if (mbLOKAccessibilityEnabled)
+    {
+        try
+        {
+            rDocumentFocusListener.attachRecursive(xAccessible);
+        }
+        catch (const uno::Exception&)
+        {
+            SAL_WARN("lok.a11y", "Exception caught processing 
LOKDocumentFocusListener::attachRecursive");
+        }
+    }
+    else
+    {
+        try
+        {
+            rDocumentFocusListener.detachRecursive(xAccessible);
+        }
+        catch (const uno::Exception&)
+        {
+            SAL_WARN("lok.a11y", "Exception caught processing 
LOKDocumentFocusListener::detachRecursive");
+        }
+    }
+}
+
 void SfxViewShell::SetLOKLocale(const OUString& rBcp47LanguageTag)
 {
     maLOKLocale = LanguageTag(rBcp47LanguageTag, true).makeFallback();

Reply via email to