diff options
-rw-r--r-- | desktop/source/lib/init.cxx | 2 | ||||
-rw-r--r-- | include/LibreOfficeKit/LibreOfficeKitEnums.h | 31 | ||||
-rw-r--r-- | libreofficekit/source/gtk/lokdocview.cxx | 2 | ||||
-rw-r--r-- | sfx2/source/view/viewsh.cxx | 835 | ||||
-rw-r--r-- | svx/source/accessibility/ChildrenManagerImpl.cxx | 17 |
5 files changed, 642 insertions, 245 deletions
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; } |