desktop/source/lib/init.cxx                      |    2 
 include/LibreOfficeKit/LibreOfficeKitEnums.h     |   31 
 libreofficekit/source/gtk/lokdocview.cxx         |    2 
 sfx2/source/view/viewsh.cxx                      |  835 ++++++++++++++++-------
 svx/source/accessibility/ChildrenManagerImpl.cxx |   17 
 5 files changed, 642 insertions(+), 245 deletions(-)

New commits:
commit f3b20f31d55f304ed9d4a3eb257bf9d468fffe5f
Author:     Marco Cecchetti <marco.cecche...@collabora.com>
AuthorDate: Tue Oct 10 10:50:50 2023 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Oct 27 19:28:52 2023 +0200

    lok: a11y: impress: help screen reader to report shape selection
    
    Refactored LOKDocumentFocusListener::notifyEvent.
    
    Implemented 2 new callbacks:
    - LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE: it says the client if
    editing in a shape or cell is active or it isn't.
    - LOK_CALLBACK_A11Y_SELECTION_CHANGED: it says if a shape has been
    selected/unselected, the name of the shape and text content are sent
    too for being reported by a screen reader.
    
    (cherry picked from commit 76899152ec3d1924cc72d2b869e197a1e34a0a1a)
    
    Conflicts:
            sfx2/source/view/viewsh.cxx
    
    Change-Id: I644bcc7daeb8772805688a9bccdabd0631ce0a22
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158562
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index bccac7338367..ab4987376f4f 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1818,6 +1818,8 @@ void CallbackFlushHandler::queue(const int type, 
CallbackData& aCallbackData)
         case LOK_CALLBACK_A11Y_TEXT_SELECTION_CHANGED:
         case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED:
         case LOK_CALLBACK_COLOR_PALETTES:
+        case LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE:
+        case LOK_CALLBACK_A11Y_SELECTION_CHANGED:
         {
             const auto& pos = std::find(m_queue1.rbegin(), m_queue1.rend(), 
type);
             auto pos2 = toQueue2(pos);
diff --git a/include/LibreOfficeKit/LibreOfficeKitEnums.h 
b/include/LibreOfficeKit/LibreOfficeKitEnums.h
index 883a68ce09c6..378347108de6 100644
--- a/include/LibreOfficeKit/LibreOfficeKitEnums.h
+++ b/include/LibreOfficeKit/LibreOfficeKitEnums.h
@@ -1001,7 +1001,32 @@ typedef enum
      *   the user got in from the outer to the inner; row/column span default
      *   value is 1; paragraph is the cell text content.
      */
-    LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED = 67
+    LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED = 67,
+
+    /**
+     * Accessibility event: text editing in a shape or cell has been 
enabled/disabled
+     *
+     *  {
+     *      "cell": true/false (editing a cell ?)
+     *      "enabled": true|false
+     *      "selection": a selection description
+     *      "paragraph": focused paragraph
+     *  }
+     */
+    LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE = 68,
+
+    /**
+     * Accessibility event: a selection (of a shape/graphic, etc.) has changed
+     *
+     *  {
+     *      "cell": true/false (selected object is a cell ?)
+     *      "action": "create"|"add"|"remove"
+     *      "name": selected object name
+     *      "text": text content if any
+     *  }
+     */
+    LOK_CALLBACK_A11Y_SELECTION_CHANGED = 69
+
 }
 LibreOfficeKitCallbackType;
 
@@ -1166,6 +1191,10 @@ static inline const char* lokCallbackTypeToString(int 
nType)
         return "LOK_CALLBACK_DOCUMENT_PASSWORD_RESET";
     case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED:
         return "LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED";
+    case LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE:
+        return "LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE";
+    case LOK_CALLBACK_A11Y_SELECTION_CHANGED:
+        return "LOK_CALLBACK_A11Y_SELECTION_CHANGED";
     }
 
     assert(!"Unknown LibreOfficeKitCallbackType type.");
diff --git a/libreofficekit/source/gtk/lokdocview.cxx 
b/libreofficekit/source/gtk/lokdocview.cxx
index c3df48448815..6c7e6dbfc652 100644
--- a/libreofficekit/source/gtk/lokdocview.cxx
+++ b/libreofficekit/source/gtk/lokdocview.cxx
@@ -1494,6 +1494,8 @@ callback (gpointer pData)
     case LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED:
     case LOK_CALLBACK_COLOR_PALETTES:
     case LOK_CALLBACK_DOCUMENT_PASSWORD_RESET:
+    case LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE:
+    case LOK_CALLBACK_A11Y_SELECTION_CHANGED:
     {
         // TODO: Implement me
         break;
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index 34edb460d4e3..8f2218c66175 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -253,6 +253,29 @@ typedef 
std::list<uno::Reference<accessibility::XAccessibleTable>> XAccessibleTa
 
 namespace
 {
+constexpr
+bool isText(sal_Int16 nRole)
+{
+    return nRole == accessibility::AccessibleRole::DOCUMENT_TEXT;
+}
+
+constexpr
+bool isSpreadsheet(sal_Int16 nRole)
+{
+    return nRole == accessibility::AccessibleRole::DOCUMENT_SPREADSHEET;
+}
+
+constexpr
+bool isPresentation(sal_Int16 nRole)
+{
+    return nRole == accessibility::AccessibleRole::DOCUMENT_PRESENTATION;
+}
+
+constexpr
+bool isDocument(sal_Int16 nRole)
+{
+    return isText(nRole) || isSpreadsheet(nRole) || isPresentation(nRole);
+}
 
 bool hasState(const accessibility::AccessibleEventObject& aEvent, ::sal_Int64 
nState)
 {
@@ -271,26 +294,75 @@ bool isFocused(const 
accessibility::AccessibleEventObject& aEvent)
     return hasState(aEvent, accessibility::AccessibleStateType::FOCUSED);
 }
 
-sal_Int16 getParentRole(const uno::Reference<accessibility::XAccessible>& 
xAccObject)
+uno::Reference<accessibility::XAccessibleContext>
+getParentContext(const uno::Reference<accessibility::XAccessibleContext>& 
xContext)
 {
-    if (!xAccObject.is())
-        return 0;
+    uno::Reference<accessibility::XAccessibleContext> xParentContext;
+    uno::Reference<accessibility::XAccessible> xParent = 
xContext->getAccessibleParent();
+    if (xParent.is())
+        xParentContext = 
uno::Reference<accessibility::XAccessibleContext>(xParent, uno::UNO_QUERY);
+    return xParentContext;
+}
 
-    uno::Reference<accessibility::XAccessibleContext> xContext(xAccObject, 
uno::UNO_QUERY);
+OUString selectionEventTypeToString(sal_Int16 nEventId)
+{
+    using namespace accessibility;
+    switch(nEventId)
+    {
+        case AccessibleEventId::SELECTION_CHANGED:
+            return "create";
+        case AccessibleEventId::SELECTION_CHANGED_ADD:
+            return "add";
+        case AccessibleEventId::SELECTION_CHANGED_REMOVE:
+            return "remove";
+        default:
+            return "";
+    }
+}
+
+bool selectionHasToBeNotified(const 
uno::Reference<accessibility::XAccessibleContext>& xContext)
+{
+    sal_Int16 nRole = xContext->getAccessibleRole();
+    return
+        nRole == accessibility::AccessibleRole::GRAPHIC ||
+        nRole == accessibility::AccessibleRole::EMBEDDED_OBJECT ||
+        nRole == accessibility::AccessibleRole::SHAPE;
+}
+
+bool hasToBeActiveForEditing(sal_Int16 nRole)
+{
+    return
+        nRole == accessibility::AccessibleRole::SHAPE;
+}
+
+sal_Int16 getParentRole(const 
uno::Reference<accessibility::XAccessibleContext>& xContext)
+{
+    sal_Int16 nRole = 0;
     if (xContext.is())
     {
-        uno::Reference<accessibility::XAccessible> xParent = 
xContext->getAccessibleParent();
-        if (xParent.is())
+        uno::Reference<accessibility::XAccessibleContext> xParentContext = 
getParentContext(xContext);
+        if (xParentContext.is())
+            nRole = xParentContext->getAccessibleRole();
+    }
+    return nRole;
+}
+
+sal_Int64 getAccessibleSiblingCount(const 
Reference<accessibility::XAccessibleContext>& xContext)
+{
+    if (!xContext.is())
+        return -1;
+
+    sal_Int64 nChildCount = 0;
+    Reference<accessibility::XAccessible> xParent = 
xContext->getAccessibleParent();
+    if (xParent.is())
+    {
+        Reference<accessibility::XAccessibleContext> xParentContext = 
xParent->getAccessibleContext();
+        if (xParentContext.is())
         {
-            uno::Reference<accessibility::XAccessibleContext> 
xParentContext(xParent,
-                                                                             
uno::UNO_QUERY);
-            if (xParentContext.is())
-            {
-                return xParentContext->getAccessibleRole();
-            }
+            nChildCount = xParentContext->getAccessibleChildCount();
         }
     }
-    return 0;
+    return nChildCount - 1;
 }
 
 // Put in rAncestorList all ancestors of xTable up to xAncestorTable or
@@ -328,6 +400,37 @@ bool getAncestorList(XAccessibleTableList& rAncestorList,
     return xCurrentTable.is() && xCurrentTable == xAncestorTable;
 }
 
+void lookForParentTable(const 
uno::Reference<accessibility::XAccessibleContext>& xContext,
+                        uno::Reference<accessibility::XAccessibleTable>& 
xTable,
+                        sal_Int64& nChildIndex)
+{
+    using namespace accessibility;
+    uno::Reference<XAccessibleContext> xParentContext = 
getParentContext(xContext);
+    if (xParentContext.is() && xParentContext->getAccessibleRole() == 
AccessibleRole::TABLE_CELL)
+    {
+        uno::Reference<XAccessible> xCellParent = 
xParentContext->getAccessibleParent();
+        if (xCellParent.is())
+        {
+            xTable = uno::Reference<XAccessibleTable>(xCellParent, 
uno::UNO_QUERY);
+            if (xTable.is())
+            {
+                nChildIndex = xParentContext->getAccessibleIndexInParent();
+            }
+        }
+    }
+}
+
+OUString truncateText(OUString& sText, sal_Int32 nNewLength)
+{
+    // truncate test to given length
+    OUString sNewText = sText.copy(0, nNewLength);
+    // try to truncate at a word
+    nNewLength = sNewText.lastIndexOf(" ");
+    if (nNewLength > 0)
+        sNewText = sNewText.copy(0, nNewLength);
+    return sNewText;
+}
+
 std::string stateSetToString(::sal_Int64 stateSet)
 {
     static const std::string states[34] = {
@@ -640,7 +743,8 @@ class LOKDocumentFocusListener :
     static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 100;
 
     const SfxViewShell* m_pViewShell;
-    std::unordered_set< uno::Reference< uno::XInterface > > m_aRefList;
+    sal_Int16 m_nDocumentType;
+    std::unordered_set<uno::Reference<uno::XInterface>> m_aRefList;
     OUString m_sFocusedParagraph;
     sal_Int32 m_nCaretPosition;
     sal_Int32 m_nSelectionStart;
@@ -649,10 +753,13 @@ class LOKDocumentFocusListener :
     uno::Reference<accessibility::XAccessibleTable> m_xLastTable;
     OUString m_sSelectedText;
     bool m_bIsEditingCell;
+    // used for text content of a shape
+    bool m_bIsEditingInSelection;
     OUString m_sSelectedCellAddress;
+    uno::Reference<accessibility::XAccessible> m_xSelectedObject;
 
 public:
-    LOKDocumentFocusListener(const SfxViewShell* pViewShell);
+    explicit LOKDocumentFocusListener(const SfxViewShell* pViewShell);
 
     /// @throws lang::IndexOutOfBoundsException
     /// @throws uno::RuntimeException
@@ -707,10 +814,12 @@ public:
     // XAccessibleEventListener
     virtual void SAL_CALL notifyEvent( const 
accessibility::AccessibleEventObject& aEvent ) override;
 
+    void notifyEditingInSelectionState(bool bParagraph = true);
     void notifyFocusedParagraphChanged(bool force = false);
     void notifyCaretChanged();
     void notifyTextSelectionChanged();
     void notifyFocusedCellChanged(sal_Int32 nOutCount, const 
std::vector<TableSizeType>& aInList, sal_Int32 nRow, sal_Int32 nCol, sal_Int32 
nRowSpan, sal_Int32 nColSpan);
+    void notifySelectionChanged(const 
uno::Reference<accessibility::XAccessible>& xAccObj, const OUString& sAction);
 
     OUString getFocusedParagraph() const;
     int getCaretPosition() const;
@@ -723,15 +832,24 @@ private:
     void updateAndNotifyParagraph(const 
uno::Reference<css::accessibility::XAccessibleText>& xAccText,
                                   bool force, std::string msg = "");
     void resetParagraphInfo();
+    void onFocusedParagraphInWriterTable(const 
uno::Reference<accessibility::XAccessibleTable>& xTable,
+                                         sal_Int64& nChildIndex,
+                                         const 
uno::Reference<accessibility::XAccessibleText>& xAccText);
+    uno::Reference< accessibility::XAccessible >
+    getSelectedObject(const accessibility::AccessibleEventObject& aEvent) 
const;
+    void onShapeSelectionChanged(const Reference<accessibility::XAccessible>& 
xSelectedObject,
+                                 const OUString& sAction);
 };
 
 LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* 
pViewShell)
     : m_pViewShell(pViewShell)
+    , m_nDocumentType(0)
     , m_nCaretPosition(0)
     , m_nSelectionStart(0)
     , m_nSelectionEnd(0)
     , m_nListPrefixLength(0)
     , m_bIsEditingCell(false)
+    , m_bIsEditingInSelection(false)
 {
 }
 
@@ -777,6 +895,56 @@ int LOKDocumentFocusListener::getCaretPosition() const
     return m_nCaretPosition;
 }
 
+// notifyEditingInSelectionState
+// Used for notifying when editing becomes active/disabled for a shape
+// bParagraph: should we append currently focused paragraph ?
+// The problem is that the initially focused paragraph could not be the one 
user has clicked on,
+// when there are more than a single paragraph.
+// So in some case sending the focused paragraph could be misleading.
+void LOKDocumentFocusListener::notifyEditingInSelectionState(bool bParagraph)
+{
+    aboutView("LOKDocumentFocusListener::notifyEditingInSelectionState", this, 
m_pViewShell);
+
+    boost::property_tree::ptree aPayloadTree;
+    bool bIsCell = !m_sSelectedCellAddress.isEmpty();
+    aPayloadTree.put("cell", bIsCell ? 1 : 0);
+    if (bIsCell)
+    {
+        aPayloadTree.put("enabled", m_bIsEditingCell ? 1 : 0);
+        if (m_bIsEditingCell)
+        {
+            aPayloadTree.put("selection", m_sSelectedCellAddress);
+            if (bParagraph)
+                aPayloadTree.put("paragraph", m_sFocusedParagraph);
+        }
+    }
+    else
+    {
+        aPayloadTree.put("enabled", m_bIsEditingInSelection ? 1 : 0);
+        if (m_bIsEditingInSelection && m_xSelectedObject.is())
+        {
+            uno::Reference<accessibility::XAccessibleContext> xContext = 
m_xSelectedObject->getAccessibleContext();
+            if (xContext.is())
+            {
+                OUString sSelectionDescr = xContext->getAccessibleName();
+                sSelectionDescr = sSelectionDescr.trim();
+                aPayloadTree.put("selection", sSelectionDescr);
+                if (bParagraph)
+                    aPayloadTree.put("paragraph", m_sFocusedParagraph);
+            }
+        }
+    }
+
+    std::stringstream aStream;
+    boost::property_tree::write_json(aStream, aPayloadTree);
+    std::string aPayload = aStream.str();
+    if (m_pViewShell)
+    {
+        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEditingInSelectionState: payload: \n" << 
aPayload);
+        
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_EDITING_IN_SELECTION_STATE,
 aPayload.c_str());
+    }
+}
+
 /// notifyFocusedParagraphChanged
 //
 //  Notify content, caret position and text selection start/end for the 
focused paragraph
@@ -854,7 +1022,7 @@ void LOKDocumentFocusListener::notifyFocusedCellChanged(
     {
         aPayloadTree.put("outCount", nOutCount);
     }
-    if (aInList.size() > 0)
+    if (!aInList.empty())
     {
         boost::property_tree::ptree aInListNode;
         for (const auto& rTableSize: aInList)
@@ -891,13 +1059,100 @@ void LOKDocumentFocusListener::notifyFocusedCellChanged(
     {
         aboutFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, 
nColSpan);
         aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: 
paragraph: ",
-                       m_sFocusedParagraph, m_nCaretPosition, 
m_nSelectionStart,
-                       m_nSelectionEnd, m_nListPrefixLength, false);
+                       m_sFocusedParagraph, m_nCaretPosition, 
m_nSelectionStart, m_nSelectionEnd,
+                       m_nListPrefixLength, false);
 
         
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED,
 aPayload.c_str());
     }
 }
 
+void LOKDocumentFocusListener::notifySelectionChanged(const 
uno::Reference<accessibility::XAccessible>& xAccObj,
+                                                      const OUString& sAction)
+{
+    using namespace accessibility;
+    if (!xAccObj.is())
+        return;
+
+    aboutView("LOKDocumentFocusListener::notifySelectionChanged", this, 
m_pViewShell);
+    uno::Reference<XAccessibleContext> xContext = 
xAccObj->getAccessibleContext();
+    if (xContext.is())
+    {
+        OUString sName = xContext->getAccessibleName();
+        sName = sName.trim();
+        if (sName == "GraphicObjectShape")
+            sName = "Graphic";
+
+        // check for text content and send it with some limitations:
+        // no more than 10 paragraphs, no more than 1000 chars
+        bool bIsCell = xContext->getAccessibleRole() == 
AccessibleRole::TABLE_CELL;
+        OUString sTextContent;
+        if (sAction == "create" || sAction == "add")
+        {
+            const sal_Int64 nMaxJoinedParagraphs = 10;
+            const sal_Int32 nMaxTextContentLength = 1000;
+            if (bIsCell)
+            {
+                uno::Reference<XAccessibleText> xAccText(xAccObj, 
uno::UNO_QUERY);
+                if (xAccText.is())
+                {
+                    sTextContent = xAccText->getText();
+                    sal_Int32 nTextLength = sTextContent.getLength();
+                    if (nTextLength > nMaxTextContentLength)
+                    {
+                        sTextContent = truncateText(sTextContent, 
nMaxTextContentLength);
+                    }
+                }
+            }
+            else
+            {
+                sal_Int32 nTotalTextLength = 0;
+                sal_Int64 nChildCount = xContext->getAccessibleChildCount();
+                if (nChildCount > nMaxJoinedParagraphs)
+                    nChildCount = nMaxJoinedParagraphs;
+                for (sal_Int64 i = 0; i < nChildCount; ++i)
+                {
+                    uno::Reference<XAccessible> xChild = 
xContext->getAccessibleChild(i);
+                    uno::Reference<XAccessibleText> xAccText(xChild, 
uno::UNO_QUERY);
+                    if (!xAccText.is())
+                        continue;
+                    OUString sText = xAccText->getText();
+                    sal_Int32 nTextLength = sText.getLength();
+                    if (nTextLength < 1)
+                        continue;
+                    if (nTotalTextLength + nTextLength < nMaxTextContentLength)
+                    {
+                        nTotalTextLength += nTextLength;
+                        sTextContent += sText + " \n";
+                    }
+                    else
+                    {
+                        // truncate paragraph
+                        sal_Int32 nNewLength = nMaxTextContentLength - 
nTotalTextLength;
+                        sTextContent += truncateText(sText, nNewLength);
+                        break;
+                    }
+                }
+            }
+        }
+
+        boost::property_tree::ptree aPayloadTree;
+        aPayloadTree.put("cell", bIsCell ? 1 : 0);
+        aPayloadTree.put("action", sAction);
+        aPayloadTree.put("name", sName);
+        if (!sTextContent.isEmpty())
+            aPayloadTree.put("text", sTextContent);
+        std::stringstream aStream;
+        boost::property_tree::write_json(aStream, aPayloadTree);
+        std::string aPayload = aStream.str();
+        if (m_pViewShell)
+        {
+            SAL_INFO("lok.a11y",  
"LOKDocumentFocusListener::notifySelectionChanged: "
+                                     "action: " << sAction << ", name: " << 
sName);
+            
m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_SELECTION_CHANGED, 
aPayload.c_str());
+        }
+    }
+}
+
 void LOKDocumentFocusListener::disposing( const lang::EventObject& aEvent )
 {
     // Unref the object here, but do not remove as listener since the object
@@ -933,8 +1188,10 @@ bool LOKDocumentFocusListener::updateParagraphInfo(const 
uno::Reference<css::acc
         // backspace properly.
         if (m_nSelectionStart == m_nSelectionEnd && m_nSelectionStart != -1)
         {
-            uno::Reference<accessibility::XAccessible> xAccObject(xAccText, 
uno::UNO_QUERY);
-            if (getParentRole(xAccObject) == 
accessibility::AccessibleRole::SHAPE)
+            uno::Reference<accessibility::XAccessibleContext> 
xContext(xAccText, uno::UNO_QUERY);
+            sal_Int16 nParentRole = getParentRole(xContext);
+            if (nParentRole == accessibility::AccessibleRole::SHAPE ||
+                nParentRole == accessibility::AccessibleRole::TEXT_FRAME) // 
spreadsheet cell editing
                 m_nSelectionStart = m_nSelectionEnd = -1;
         }
 
@@ -976,161 +1233,241 @@ void LOKDocumentFocusListener::resetParagraphInfo()
     m_nListPrefixLength = 0;
 }
 
-void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventObject& aEvent )
+// For a presentation document when an accessible event of type 
SELECTION_CHANGED_XXX occurs
+// the selected (or unselected) object is put in NewValue, instead for a text 
document
+// the selected object is put in Source.
+// The following function helps to retrieve the selected object independently 
on where it has been put.
+uno::Reference< accessibility::XAccessible >
+LOKDocumentFocusListener::getSelectedObject(const 
accessibility::AccessibleEventObject& aEvent) const
+{
+    uno::Reference< accessibility::XAccessible > xSelectedObject;
+    if (isText(m_nDocumentType))
+    {
+        uno::Reference< accessibility::XAccessible > xSource(aEvent.Source, 
uno::UNO_QUERY);
+        xSelectedObject = xSource;
+    }
+    else
+    {
+        aEvent.NewValue >>= xSelectedObject;
+    }
+    return xSelectedObject;
+}
+
+void LOKDocumentFocusListener::onShapeSelectionChanged(
+    const uno::Reference<accessibility::XAccessible>& xSelectedObject,
+    const OUString& sAction)
 {
+    // when a shape is selected or unselected we could need to notify that 
text content editing
+    // is no more active, that allows on the client side to prevent default 
input.
+    resetParagraphInfo();
+    if (m_bIsEditingInSelection)
+    {
+        m_bIsEditingInSelection = false;
+        notifyEditingInSelectionState();
+    }
+    notifySelectionChanged(xSelectedObject, sAction);
+}
+
+void LOKDocumentFocusListener::onFocusedParagraphInWriterTable(
+    const uno::Reference<accessibility::XAccessibleTable>& xTable,
+    sal_Int64& nChildIndex,
+    const uno::Reference<accessibility::XAccessibleText>& xAccText
+)
+{
+    std::vector<TableSizeType> aInList;
+    sal_Int32 nOutCount = 0;
+
+    if (m_xLastTable.is())
+    {
+        if (xTable != m_xLastTable)
+        {
+            // do we get in one or more nested tables ?
+            // check if xTable is a descendant of m_xLastTable
+            XAccessibleTableList newTableAncestorList;
+            bool isLastAncestorOfNew = getAncestorList(newTableAncestorList, 
xTable, m_xLastTable);
+            bool isNewAncestorOfLast = false;
+            if (!isLastAncestorOfNew)
+            {
+                // do we get out of one or more nested tables ?
+                // check if m_xLastTable is a descendant of xTable
+                XAccessibleTableList lastTableAncestorList;
+                isNewAncestorOfLast = getAncestorList(lastTableAncestorList, 
m_xLastTable, xTable);
+                // we have to notify "out of table" for all  m_xLastTable 
ancestors up to xTable
+                // or the first not-a-table ancestor
+                nOutCount = lastTableAncestorList.size();
+            }
+            if (isLastAncestorOfNew || !isNewAncestorOfLast)
+            {
+                // we have to notify row/col count for all xTable ancestors 
starting from the ancestor
+                // which is a child of m_xLastTable (isLastAncestorOfNew) or 
the first not-a-table ancestor
+                for (const auto& ancestor: newTableAncestorList)
+                {
+                    TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
+                                              
ancestor->getAccessibleColumnCount()};
+                    aInList.push_back(aTableSize);
+                }
+            }
+        }
+    }
+    else
+    {
+        // cursor was not inside any table and gets inside one or more tables
+        // we have to notify row/col count for all xTable ancestors starting 
from first not-a-table ancestor
+        XAccessibleTableList newTableAncestorList;
+        getAncestorList(newTableAncestorList, xTable);
+        for (const auto& ancestor: newTableAncestorList)
+        {
+            TableSizeType aTableSize{ancestor->getAccessibleRowCount(),
+                                      ancestor->getAccessibleColumnCount()};
+            aInList.push_back(aTableSize);
+        }
+    }
+
+    // we have to notify current row/col of xTable and related row/col span
+    sal_Int32 nRow = xTable->getAccessibleRow(nChildIndex);
+    sal_Int32 nCol = xTable->getAccessibleColumn(nChildIndex);
+    sal_Int32 nRowSpan = xTable->getAccessibleRowExtentAt(nRow, nCol);
+    sal_Int32 nColSpan = xTable->getAccessibleColumnExtentAt(nRow, nCol);
+
+    m_xLastTable = xTable;
+    updateParagraphInfo(xAccText, false, "STATE_CHANGED: FOCUSED");
+    notifyFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, 
nColSpan);
+}
+
+void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventObject& aEvent)
+{
+    using namespace accessibility;
     aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell);
 
     try
     {
         aboutEvent("LOKDocumentFocusListener::notifyEvent", aEvent);
 
-        switch( aEvent.EventId )
+        switch (aEvent.EventId)
         {
-            case accessibility::AccessibleEventId::STATE_CHANGED:
+            case AccessibleEventId::STATE_CHANGED:
             {
-                uno::Reference< accessibility::XAccessible > xAccessibleObject 
= getAccessible(aEvent);
+                // logging
                 sal_Int64 nState = accessibility::AccessibleStateType::INVALID;
                 aEvent.NewValue >>= nState;
                 sal_Int64 nOldState = 
accessibility::AccessibleStateType::INVALID;
                 aEvent.OldValue >>= nOldState;
-                SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: "
-                         "STATE_CHANGED: nNewState: " << 
stateSetToString(nState)
-                        << " , STATE_CHANGED: nOldState: " << 
stateSetToString(nOldState));
+                SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: 
STATE_CHANGED: "
+                                     " New State: " << stateSetToString(nState)
+                                     << ", Old State: " << 
stateSetToString(nOldState));
 
-                if (accessibility::AccessibleStateType::ACTIVE == nState &&
-                    getParentRole(xAccessibleObject) == 
accessibility::AccessibleRole::SHAPE)
-                {
-                    uno::Reference<css::accessibility::XAccessibleText> 
xAccText(xAccessibleObject, uno::UNO_QUERY);
-                    updateParagraphInfo(xAccText, true, "STATE_CHANGED: 
ACTIVE");
-                    notifyFocusedParagraphChanged(true);
-                }
-                else if( accessibility::AccessibleStateType::FOCUSED == nState 
)
+                // check validity
+                uno::Reference< XAccessible > xAccessibleObject = 
getAccessible(aEvent);
+                if (!xAccessibleObject.is())
+                    return;
+                uno::Reference<XAccessibleContext> xContext(aEvent.Source, 
uno::UNO_QUERY);
+                if (!xContext)
+                    return;
+
+                sal_Int16 nRole = xContext->getAccessibleRole();
+
+                if (nRole == AccessibleRole::PARAGRAPH)
                 {
-                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: FOCUSED");
-                    uno::Reference<css::accessibility::XAccessibleText> 
xAccText(xAccessibleObject, uno::UNO_QUERY);
+                    uno::Reference<XAccessibleText> 
xAccText(xAccessibleObject, uno::UNO_QUERY);
+                    if (!xAccText.is())
+                        return;
 
-                    if (m_bIsEditingCell)
+                    switch (nState)
                     {
-                        if (!hasState(aEvent, 
accessibility::AccessibleStateType::ACTIVE))
+                        case AccessibleStateType::ACTIVE:
                         {
-                            SAL_WARN("lok.a11y",
-                                    "LOKDocumentFocusListener::notifyEvent: 
FOCUSED: Cell not ACTIVE for editing yet");
-                            return;
+                            if (!m_bIsEditingInSelection && 
hasToBeActiveForEditing(getParentRole(xContext)))
+                            {
+                                m_bIsEditingInSelection = true;
+                            }
+                            break;
                         }
-                    }
-
-                    // check if we are inside a table: in case notify table 
and current cell info
-                    bool isInsideTable = false;
-                    uno::Reference<accessibility::XAccessibleContext> 
xContext(aEvent.Source, uno::UNO_QUERY);
-                    if (xContext.is())
-                    {
-                        uno::Reference<accessibility::XAccessible> xParent = 
xContext->getAccessibleParent();
-                        if (xParent.is())
+                        case AccessibleStateType::FOCUSED:
                         {
-                            uno::Reference<accessibility::XAccessibleContext> 
xParentContext(xParent, uno::UNO_QUERY);
-                            if (xParentContext.is()
-                                    && xParentContext->getAccessibleRole() == 
accessibility::AccessibleRole::TABLE_CELL)
+                            if (m_bIsEditingInSelection && 
m_xSelectedObject.is())
+                            {
+                                updateParagraphInfo(xAccText, true, 
"STATE_CHANGED: FOCUSED");
+                                
notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
+                                notifyFocusedParagraphChanged(true);
+                                // we clear selected object so when editing is 
over but shape is
+                                // still selected, the selection event is 
notified the same to the client
+                                m_xSelectedObject.clear();
+                                return;
+                            }
+                            if (isText(m_nDocumentType))
                             {
-                                uno::Reference<accessibility::XAccessible> 
xCellParent = xParentContext->getAccessibleParent();
-                                if (xCellParent.is())
+                                // check if we are inside a table: in case 
notify table and current cell info
+                                bool isInsideTable = false;
+                                uno::Reference<XAccessibleTable> xTable;
+                                sal_Int64 nChildIndex;
+                                lookForParentTable(xContext, xTable, 
nChildIndex);
+                                if (xTable.is())
                                 {
-                                    
uno::Reference<accessibility::XAccessibleTable> xTable(xCellParent, 
uno::UNO_QUERY);
-                                    if (xTable.is())
+                                    onFocusedParagraphInWriterTable(xTable, 
nChildIndex, xAccText);
+                                    isInsideTable = true;
+                                }
+                                // paragraph is not inside any table
+                                if (!isInsideTable)
+                                {
+                                    if (m_xLastTable.is())
                                     {
+                                        // we get out one or more tables
+                                        // we have to notify "out of table" 
for all m_xLastTable ancestors
+                                        // up to the first not-a-table ancestor
+                                        XAccessibleTableList 
lastTableAncestorList;
+                                        getAncestorList(lastTableAncestorList, 
m_xLastTable);
+                                        sal_Int32 nOutCount = 
lastTableAncestorList.size();
+                                        // no more inside a table
+                                        m_xLastTable.clear();
+                                        // notify
                                         std::vector<TableSizeType> aInList;
-                                        sal_Int32 nOutCount = 0;
-
-                                        if (m_xLastTable.is())
-                                        {
-                                            if (xTable != m_xLastTable)
-                                            {
-                                                // do we get in one or more 
nested tables ?
-                                                // check if xTable is a 
descendant of m_xLastTable
-                                                XAccessibleTableList 
newTableAncestorList;
-                                                bool isLastAncestorOfNew = 
getAncestorList(newTableAncestorList, xTable, m_xLastTable);
-                                                bool isNewAncestorOfLast = 
false;
-                                                if (!isLastAncestorOfNew)
-                                                {
-                                                    // do we get out of one or 
more nested tables ?
-                                                    // check if m_xLastTable 
is a descendant of xTable
-                                                    XAccessibleTableList 
lastTableAncestorList;
-                                                    isNewAncestorOfLast = 
getAncestorList(lastTableAncestorList, m_xLastTable, xTable);
-                                                    // we have to notify "out 
of table" for all  m_xLastTable ancestors up to xTable
-                                                    // or the first 
not-a-table ancestor
-                                                    nOutCount = 
lastTableAncestorList.size();
-                                                }
-                                                if (isLastAncestorOfNew || 
!isNewAncestorOfLast)
-                                                {
-                                                    // we have to notify 
row/col count for all xTable ancestors starting from the ancestor
-                                                    // which is a child of 
m_xLastTable (isLastAncestorOfNew) or the first not-a-table ancestor
-                                                    for (const auto& ancestor: 
newTableAncestorList)
-                                                    {
-                                                        TableSizeType 
aTableSize{ancestor->getAccessibleRowCount(),
-                                                                               
  ancestor->getAccessibleColumnCount()};
-                                                        
aInList.push_back(aTableSize);
-                                                    }
-                                                }
-                                            }
-                                        }
-                                        else
-                                        {
-                                            // cursor was not inside any table 
and gets inside one or more tables
-                                            // we have to notify row/col count 
for all xTable ancestors starting from first not-a-table ancestor
-                                            XAccessibleTableList 
newTableAncestorList;
-                                            
getAncestorList(newTableAncestorList, xTable);
-                                            for (const auto& ancestor: 
newTableAncestorList)
-                                            {
-                                                TableSizeType 
aTableSize{ancestor->getAccessibleRowCount(),
-                                                                         
ancestor->getAccessibleColumnCount()};
-                                                aInList.push_back(aTableSize);
-                                            }
-                                        }
-
-                                        // we have to notify current row/col 
of xTable and related row/col span
-                                        sal_Int64 nChildIndex = 
xParentContext->getAccessibleIndexInParent();
-                                        sal_Int32 nRow = 
xTable->getAccessibleRow(nChildIndex);
-                                        sal_Int32 nCol = 
xTable->getAccessibleColumn(nChildIndex);
-                                        sal_Int32 nRowSpan = 
xTable->getAccessibleRowExtentAt(nRow, nCol);
-                                        sal_Int32 nColSpan = 
xTable->getAccessibleColumnExtentAt(nRow, nCol);
-
-                                        m_xLastTable = xTable;
                                         updateParagraphInfo(xAccText, false, 
"STATE_CHANGED: FOCUSED");
-                                        notifyFocusedCellChanged(nOutCount, 
aInList, nRow, nCol, nRowSpan, nColSpan);
-                                        isInsideTable = true;
+                                        notifyFocusedCellChanged(nOutCount, 
aInList, -1, -1, 1, 1);
+                                    }
+                                    else
+                                    {
+                                        updateAndNotifyParagraph(xAccText, 
false, "STATE_CHANGED: FOCUSED");
                                     }
                                 }
                             }
-                        }
-                    }
+                            else if (isSpreadsheet(m_nDocumentType))
+                            {
+                                if (m_bIsEditingCell)
+                                {
+                                    if (!hasState(aEvent, 
AccessibleStateType::ACTIVE))
+                                    {
+                                        SAL_WARN("lok.a11y",
+                                                 
"LOKDocumentFocusListener::notifyEvent: FOCUSED: "
+                                                 "cell not ACTIVE for editing 
yet");
+                                        return;
+                                    }
+                                    else if (m_xSelectedObject.is())
+                                    {
+                                        updateParagraphInfo(xAccText, true, 
"STATE_CHANGED: ACTIVE");
+                                        
notifyEditingInSelectionState(getAccessibleSiblingCount(xContext) == 0);
+                                        notifyFocusedParagraphChanged(true);
+                                        m_xSelectedObject.clear();
+                                        return;
+                                    }
 
-                    // paragraph is not inside any table
-                    if (!isInsideTable)
-                    {
-                        if (m_xLastTable.is())
-                        {
-                            // we get out one or more tables
-                            // we have to notify "out of table" for all 
m_xLastTable ancestors
-                            // up to the first not-a-table ancestor
-                            XAccessibleTableList lastTableAncestorList;
-                            getAncestorList(lastTableAncestorList, 
m_xLastTable);
-                            sal_Int32 nOutCount = lastTableAncestorList.size();
-                            // no more inside a table
-                            m_xLastTable.clear();
-                            // notify
-                            std::vector<TableSizeType> aInList;
-                            updateParagraphInfo(xAccText, false, 
"STATE_CHANGED: FOCUSED");
-                            notifyFocusedCellChanged(nOutCount, aInList, -1, 
-1, 1, 1);
-                        }
-                        else
-                        {
-                            updateAndNotifyParagraph(xAccText, false, 
"STATE_CHANGED: FOCUSED");
+                                    updateAndNotifyParagraph(xAccText, false, 
"STATE_CHANGED: FOCUSED");
+                                }
+                            }
+                            else if (isPresentation(m_nDocumentType))
+                            {
+                                updateAndNotifyParagraph(xAccText, false, 
"STATE_CHANGED: FOCUSED");
+                            }
+                            
aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: 
FOCUSED", xAccText);
+
+                            break;
                         }
+                        default:
+                            break;
                     }
-                    
aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: 
FOCUSED", xAccText);
                 }
                 break;
             }
-            case accessibility::AccessibleEventId::CARET_CHANGED:
+            case AccessibleEventId::CARET_CHANGED:
             {
                 sal_Int32 nNewPos = -1;
                 aEvent.NewValue >>= nNewPos;
@@ -1140,10 +1477,9 @@ void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventO
                 if (nNewPos >= 0)
                 {
                     SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: CARET_CHANGED: "
-                            "new pos: " << nNewPos << ", nOldPos: " << 
nOldPos);
+                                         "new pos: " << nNewPos << ", nOldPos: 
" << nOldPos);
 
-                    uno::Reference<css::accessibility::XAccessibleText>
-                        xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                    uno::Reference<XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
                     if (xAccText.is())
                     {
                         m_nCaretPosition = nNewPos;
@@ -1157,7 +1493,7 @@ void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventO
                         if (!isFocused(aEvent))
                         {
                             if (updateParagraphInfo(xAccText, false, 
"CARET_CHANGED"))
-                                 notifyFocusedParagraphChanged(true);
+                                notifyFocusedParagraphChanged(true);
                         }
                         else
                         {
@@ -1168,20 +1504,22 @@ void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventO
                 }
                 break;
             }
-            case accessibility::AccessibleEventId::TEXT_CHANGED:
+            case AccessibleEventId::TEXT_CHANGED:
             {
-                accessibility::TextSegment aDeletedText;
-                accessibility::TextSegment aInsertedText;
+                TextSegment aDeletedText;
+                TextSegment aInsertedText;
 
                 if (aEvent.OldValue >>= aDeletedText)
                 {
-                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: deleted text: >" << 
aDeletedText.SegmentText << "<");
+                    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 << "<");
+                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_CHANGED: "
+                                             "inserted text: >" << 
aInsertedText.SegmentText << "<");
                 }
-                uno::Reference<css::accessibility::XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                uno::Reference<XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
 
                 // When the change has been performed in another view we need 
to force
                 // paragraph content updating on the client, even if current 
editing involves composing.
@@ -1191,124 +1529,106 @@ void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventO
 
                 break;
             }
-            case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED:
+            case AccessibleEventId::TEXT_SELECTION_CHANGED:
             {
                 if (!isFocused(aEvent))
                 {
                     SAL_WARN("lok.a11y",
-                          "LOKDocumentFocusListener::notifyEvent: 
TEXT_SELECTION_CHANGED: skip non focused paragraph");
+                             "LOKDocumentFocusListener::notifyEvent: 
TEXT_SELECTION_CHANGED: "
+                             "skip non focused paragraph");
                     return;
                 }
 
-                uno::Reference<css::accessibility::XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
+                uno::Reference<XAccessibleText> 
xAccText(getAccessible(aEvent), uno::UNO_QUERY);
                 if (xAccText.is())
                 {
-                    OUString sText = xAccText->getText();
-                    m_sSelectedText = xAccText->getSelectedText();
-
                     // 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.
+                    // 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.
                     updateParagraphInfo(xAccText, false, 
"TEXT_SELECTION_CHANGED");
 
+                    m_sSelectedText = xAccText->getSelectedText();
+                    SAL_INFO("lok.a11y",
+                             "LOKDocumentFocusListener::notifyEvent: 
TEXT_SELECTION_CHANGED: selected text: >"
+                              << m_sSelectedText << "<");
+
                     // Calc: when editing a formula send the update content
-                    if (m_bIsEditingCell && !m_sSelectedCellAddress.isEmpty()
-                            && !m_sSelectedText.isEmpty() && 
sText.startsWith("="))
+                    if (m_bIsEditingCell)
                     {
-                        notifyFocusedParagraphChanged();
+                        OUString sText = xAccText->getText();
+                        if (!m_sSelectedCellAddress.isEmpty() &&
+                            !m_sSelectedText.isEmpty() && 
sText.startsWith("="))
+                        {
+                            notifyFocusedParagraphChanged();
+                        }
                     }
                     notifyTextSelectionChanged();
-
-                    aboutParagraph("LOKDocumentFocusListener::notifyEvent: 
TEXT_SELECTION_CHANGED", xAccText);
-                    SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: TEXT_SELECTION_CHANGED: selected text: 
>"
-                            << m_sSelectedText << "<");
                 }
                 break;
             }
-            case accessibility::AccessibleEventId::SELECTION_CHANGED:
+            case AccessibleEventId::SELECTION_CHANGED:
+            case AccessibleEventId::SELECTION_CHANGED_REMOVE:
             {
-                uno::Reference< accessibility::XAccessible > xNewValue;
-                aEvent.NewValue >>= xNewValue;
-                if (xNewValue.is())
-                {
-                    uno::Reference< accessibility::XAccessibleContext > 
xContext =
-                            xNewValue->getAccessibleContext();
+                uno::Reference< XAccessible > xSelectedObject = 
getSelectedObject(aEvent);
+                if (!xSelectedObject.is())
+                    return;
+                uno::Reference< XAccessibleContext > xContext = 
xSelectedObject->getAccessibleContext();
+                if (!xContext.is())
+                    return;
+
+                if (aEvent.EventId == 
AccessibleEventId::SELECTION_CHANGED_REMOVE)
+                    m_xSelectedObject.clear();
+                else if (m_xSelectedObject.is() && m_xSelectedObject == 
xSelectedObject)
+                        return; // selecting the same object; note: on editing 
selected object is cleared
+                else
+                    m_xSelectedObject = xSelectedObject;
+                SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: 
SELECTION_CHANGED: "
+                                         "m_xSelectedObject.is(): " << 
m_xSelectedObject.is());
 
-                    if (xContext.is())
+                OUString sAction = selectionEventTypeToString(aEvent.EventId);
+                sal_Int16 nRole = xContext->getAccessibleRole();
+                switch(nRole)
+                {
+                    case AccessibleRole::GRAPHIC:
+                    case AccessibleRole::EMBEDDED_OBJECT:
+                    case AccessibleRole::SHAPE:
                     {
-                        OUString sName = xContext->getAccessibleName();
-                        SAL_INFO("lok.a11y", 
"LOKDocumentFocusListener::notifyEvent: SELECTION_CHANGED: this: " << this
-                                << ", selected object: >" << sName << "<"
-                                   ", m_bIsEditingCell: " << m_bIsEditingCell);
-                        if (xContext->getAccessibleRole() == 
accessibility::AccessibleRole::SHAPE)
-                        {
-                            if (xContext->getAccessibleChildCount() > 0)
-                            {
-                                uno::Reference<accessibility::XAccessible> 
xAccChild =
-                                   xContext->getAccessibleChild(0);
-                                
uno::Reference<css::accessibility::XAccessibleText> xAccText(xAccChild, 
uno::UNO_QUERY);
-                                if (xAccText.is())
-                                {
-                                    // At present when a shape is selected 
screen reader reports editable area content
-                                    // on caret navigation even if shape 
editing is not active
-                                    resetParagraphInfo();
-                                    notifyFocusedParagraphChanged(true);
-                                }
-                            }
-                        }
-                        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();
-                            }
-                        }
+                        onShapeSelectionChanged(xSelectedObject, sAction);
+                        break;
                     }
-                }
-                break;
-            }
-            case accessibility::AccessibleEventId::SELECTION_CHANGED_REMOVE:
-            {
-                uno::Reference< accessibility::XAccessible > xNewValue;
-                aEvent.NewValue >>= xNewValue;
-                if (xNewValue.is())
-                {
-                    uno::Reference<accessibility::XAccessibleContext> xContext
-                        = xNewValue->getAccessibleContext();
-
-                    if (xContext.is())
+                    case AccessibleRole::TABLE_CELL:
                     {
-                        if (xContext->getAccessibleRole() == 
accessibility::AccessibleRole::SHAPE)
+                        notifySelectionChanged(xSelectedObject, sAction);
+
+                        if (aEvent.EventId == 
AccessibleEventId::SELECTION_CHANGED)
                         {
-                            if (xContext->getAccessibleChildCount() > 0)
+                            m_sSelectedCellAddress = 
xContext->getAccessibleName();
+                            if (m_bIsEditingCell && 
!m_sSelectedCellAddress.isEmpty())
                             {
-                                uno::Reference<accessibility::XAccessible> 
xAccChild
-                                    = xContext->getAccessibleChild(0);
-                                
uno::Reference<css::accessibility::XAccessibleText> xAccText(
-                                    xAccChild, uno::UNO_QUERY);
-                                if (xAccText.is())
+                                // 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)
                                 {
-                                    // see SELECTION_CHANGED case
-                                    resetParagraphInfo();
-                                    notifyFocusedParagraphChanged(true);
+                                    notifyFocusedParagraphChanged();
+                                    notifyTextSelectionChanged();
                                 }
                             }
                         }
+                        break;
                     }
+                    default:
+                        break;
                 }
                 break;
             }
-            case accessibility::AccessibleEventId::CHILD:
+            case AccessibleEventId::CHILD:
             {
                 uno::Reference< accessibility::XAccessible > xChild;
                 if( (aEvent.OldValue >>= xChild) && xChild.is() )
@@ -1319,17 +1639,19 @@ void LOKDocumentFocusListener::notifyEvent(const 
accessibility::AccessibleEventO
 
                 break;
             }
-            case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN:
+            case 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");
+        SAL_WARN("lok.a11y",
+                 "LOKDocumentFocusListener::notifyEvent:Focused object has 
invalid index in parent");
     }
 }
 
@@ -1340,6 +1662,9 @@ uno::Reference< accessibility::XAccessible > 
LOKDocumentFocusListener::getAccess
     if( xAccessible.is() )
         return xAccessible;
 
+    SAL_WARN("lok.a11y",
+             "LOKDocumentFocusListener::getAccessible: Event source doesn't 
implement XAccessible.");
+
     uno::Reference< accessibility::XAccessibleContext > 
xContext(aEvent.Source, uno::UNO_QUERY);
 
     if( xContext.is() )
@@ -1355,6 +1680,9 @@ uno::Reference< accessibility::XAccessible > 
LOKDocumentFocusListener::getAccess
         }
     }
 
+    SAL_WARN("lok.a11y",
+             "LOKDocumentFocusListener::getAccessible: Can't get any 
accessible object from event source.");
+
     return uno::Reference< accessibility::XAccessible >();
 }
 
@@ -1421,8 +1749,20 @@ void LOKDocumentFocusListener::attachRecursive(
         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) )
+        if (isDocument(xContext->getAccessibleRole()))
+        {
+            m_nDocumentType = xContext->getAccessibleRole();
+        }
+
+        if (!(nStateSet & 
accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
         {
+            if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
+                selectionHasToBeNotified(xContext))
+            {
+                uno::Reference< accessibility::XAccessible > xAccObj(xContext, 
uno::UNO_QUERY);
+                onShapeSelectionChanged(xAccObj, "create");
+            }
+
             sal_Int64 nmax = xContext->getAccessibleChildCount();
             if( nmax > MAX_ATTACHABLE_CHILDREN )
                 nmax = MAX_ATTACHABLE_CHILDREN;
@@ -1446,7 +1786,7 @@ void LOKDocumentFocusListener::attachRecursive(
             // Here we update the paragraph info related to the focused 
paragraph,
             // later when afterCallbackRegistered is executed we notify the 
paragraph content.
             sal_Int64 nChildCount = xContext->getAccessibleChildCount();
-            if (nChildCount > 0)
+            if (nChildCount > 0 && nChildCount < 10)
             {
                 for (sal_Int64 n = 0; n < nChildCount; ++n)
                 {
@@ -1519,20 +1859,27 @@ void LOKDocumentFocusListener::detachRecursive(
 {
     uno::Reference< accessibility::XAccessibleEventBroadcaster > 
xBroadcaster(xContext, uno::UNO_QUERY);
 
-    if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) )
+    if (xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster))
     {
         xBroadcaster->removeAccessibleEventListener(static_cast< 
accessibility::XAccessibleEventListener *>(this));
 
-        if( bForce || !( nStateSet & 
accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) )
+        if ((nStateSet & accessibility::AccessibleStateType::SELECTED) &&
+            selectionHasToBeNotified(xContext))
+        {
+            uno::Reference< accessibility::XAccessible > xAccObj(xContext, 
uno::UNO_QUERY);
+            onShapeSelectionChanged(xAccObj, "delete");
+        }
+
+        if (bForce || !(nStateSet & 
accessibility::AccessibleStateType::MANAGES_DESCENDANTS))
         {
             sal_Int64 nmax = xContext->getAccessibleChildCount();
-            if( nmax > MAX_ATTACHABLE_CHILDREN )
+            if (nmax > MAX_ATTACHABLE_CHILDREN)
                 nmax = MAX_ATTACHABLE_CHILDREN;
-            for( sal_Int64 n = 0; n < nmax; n++ )
+            for (sal_Int64 n = 0; n < nmax; n++)
             {
-                uno::Reference< accessibility::XAccessible > xChild( 
xContext->getAccessibleChild( n ) );
+                uno::Reference< accessibility::XAccessible > 
xChild(xContext->getAccessibleChild(n));
 
-                if( xChild.is() )
+                if (xChild.is())
                     detachRecursive(xChild);
             }
         }
diff --git a/svx/source/accessibility/ChildrenManagerImpl.cxx 
b/svx/source/accessibility/ChildrenManagerImpl.cxx
index cc107bc4b0df..96e27c079e9b 100644
--- a/svx/source/accessibility/ChildrenManagerImpl.cxx
+++ b/svx/source/accessibility/ChildrenManagerImpl.cxx
@@ -1008,6 +1008,23 @@ void ChildrenManagerImpl::UpdateSelection()
         }
     }
 
+    // We need to know when text content is no more edited but shape is still 
selected.
+    // For instance when ESC is pressed.
+    // The only difference is provided by nSelectedChildCount: on editing is 
equal to 1.
+    // In the following a shape get selected, but it was already selected, 
anyway editing is no more active.
+    if (comphelper::LibreOfficeKit::isActive() && pNewFocusedShape && 
nAddSelect == 0)
+    {
+        sal_Int64 nChildCount = pNewFocusedShape->getAccessibleChildCount();
+        sal_Int64 nSelectedChildCount = 
pNewFocusedShape->getSelectedAccessibleChildCount();
+        if (nChildCount > 0 && nSelectedChildCount == 0)
+        {
+            Reference< XAccessible > xShape(pNewFocusedShape);
+            uno::Any anyShape;
+            anyShape <<= xShape;
+            
mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any(),
 -1);
+        }
+    }
+
     // Remember whether there is a shape that now has the focus.
     mpFocusedShape = pNewFocusedShape;
 }

Reply via email to