summaryrefslogtreecommitdiff
path: root/sfx2
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2')
-rw-r--r--sfx2/source/view/viewsh.cxx280
1 files changed, 259 insertions, 21 deletions
diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx
index b4935b0d0ca8..9e9c106691c0 100644
--- a/sfx2/source/view/viewsh.cxx
+++ b/sfx2/source/view/viewsh.cxx
@@ -56,6 +56,7 @@
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
@@ -105,6 +106,7 @@
#include <openuriexternally.hxx>
#include <iostream>
#include <vector>
+#include <list>
#include <libxml/xmlwriter.h>
#include <toolkit/awt/vclxmenu.hxx>
#include <unordered_map>
@@ -240,6 +242,17 @@ void SAL_CALL SfxClipboardChangeListener::changedContents( const datatransfer::c
namespace
{
+struct TableSizeType
+{
+ sal_Int32 nRowCount;
+ sal_Int32 nColCount;
+};
+}
+
+typedef std::list<uno::Reference<accessibility::XAccessibleTable>> XAccessibleTableList;
+
+namespace
+{
bool hasState(const accessibility::AccessibleEventObject& aEvent, ::sal_Int64 nState)
{
@@ -258,6 +271,41 @@ bool isFocused(const accessibility::AccessibleEventObject& aEvent)
return hasState(aEvent, accessibility::AccessibleStateType::FOCUSED);
}
+// Put in rAncestorList all ancestors of xTable up to xAncestorTable or
+// up to the first not-a-table ancestor if xAncestorTable is not an ancestor.
+// xTable is included in the list, xAncestorTable is not included.
+// The list is ordered from the ancient ancestor to xTable.
+// Return true if xAncestorTable is an ancestor of xTable.
+bool getAncestorList(XAccessibleTableList& rAncestorList,
+ const uno::Reference<accessibility::XAccessibleTable>& xTable,
+ const uno::Reference<accessibility::XAccessibleTable>& xAncestorTable = uno::Reference<accessibility::XAccessibleTable>())
+{
+ uno::Reference<accessibility::XAccessibleTable> xCurrentTable = xTable;
+ while (xCurrentTable.is() && xCurrentTable != xAncestorTable)
+ {
+ rAncestorList.push_front(xCurrentTable);
+
+ uno::Reference<accessibility::XAccessibleContext> xContext(xCurrentTable, uno::UNO_QUERY);
+ xCurrentTable.clear();
+ if (xContext.is())
+ {
+ uno::Reference<accessibility::XAccessible> xParent = xContext->getAccessibleParent();
+ uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent, uno::UNO_QUERY);
+ if (xParentContext.is()
+ && xParentContext->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL)
+ {
+ uno::Reference<accessibility::XAccessible> xCellParent = xParentContext->getAccessibleParent();
+ if (xCellParent.is())
+ {
+ xCurrentTable = uno::Reference<accessibility::XAccessibleTable>(xCellParent, uno::UNO_QUERY);
+ }
+ }
+ }
+ }
+
+ return xCurrentTable.is() && xCurrentTable == xAncestorTable;
+}
+
std::string stateSetToString(::sal_Int64 stateSet)
{
static const std::string states[34] = {
@@ -476,12 +524,33 @@ void aboutParagraph(std::string msg, const uno::Reference<css::accessibility::XA
sal_Int32 nSelectionEnd = xAccText->getSelectionEnd();
aboutParagraph(msg, sText, nCaretPosition, nSelectionStart, nSelectionEnd, force);
}
+
+void aboutFocusedCellChanged(sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
+ sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
+{
+ std::stringstream inListStream;
+ inListStream << "[ ";
+ for (const auto& rTableSize: aInList)
+ {
+ inListStream << "{ rowCount: " << rTableSize.nRowCount << " colCount: " << rTableSize.nColCount << " } ";
+ }
+ inListStream << "]";
+
+ SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyFocusedCellChanged: "
+ "\n outCount: " << nOutCount
+ << "\n inList: " << inListStream.str()
+ << "\n row: " << nRow
+ << "\n column: " << nCol
+ << "\n rowSpan: " << nRowSpan
+ << "\n colSpan: " << nColSpan
+ );
+}
} // anonymous namespace
class LOKDocumentFocusListener :
public ::cppu::WeakImplHelper< accessibility::XAccessibleEventListener >
{
- static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 30;
+ static constexpr sal_Int64 MAX_ATTACHABLE_CHILDREN = 100;
const SfxViewShell* m_pViewShell;
std::unordered_set< uno::Reference< uno::XInterface > > m_aRefList;
@@ -489,6 +558,7 @@ class LOKDocumentFocusListener :
sal_Int32 m_nCaretPosition;
sal_Int32 m_nSelectionStart;
sal_Int32 m_nSelectionEnd;
+ uno::Reference<accessibility::XAccessibleTable> m_xLastTable;
OUString m_sSelectedText;
bool m_bIsEditingCell;
OUString m_sSelectedCellAddress;
@@ -552,11 +622,14 @@ public:
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);
OUString getFocusedParagraph() const;
int getCaretPosition() const;
private:
+ void paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force = false) const;
+ void paragraphPropertiesToJson(std::string& aPayload, bool force = false) const;
bool updateParagraphInfo(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
bool force, std::string msg = "");
void updateAndNotifyParagraph(const uno::Reference<css::accessibility::XAccessibleText>& xAccText,
@@ -572,21 +645,34 @@ LOKDocumentFocusListener::LOKDocumentFocusListener(const SfxViewShell* pViewShel
{
}
-OUString LOKDocumentFocusListener::getFocusedParagraph() const
+void LOKDocumentFocusListener::paragraphPropertiesToTree(boost::property_tree::ptree& aPayloadTree, bool force) const
{
- aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell);
- aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
- m_sFocusedParagraph, m_nCaretPosition, m_nSelectionStart, m_nSelectionEnd);
-
bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
- boost::property_tree::ptree aPayloadTree;
aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
aPayloadTree.put("position", m_nCaretPosition);
aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
+ if (force)
+ aPayloadTree.put("force", 1);
+}
+
+void LOKDocumentFocusListener::paragraphPropertiesToJson(std::string& aPayload, bool force) const
+{
+ boost::property_tree::ptree aPayloadTree;
+ paragraphPropertiesToTree(aPayloadTree, force);
std::stringstream aStream;
boost::property_tree::write_json(aStream, aPayloadTree);
- std::string aPayload = aStream.str();
+ aPayload = aStream.str();
+}
+
+OUString LOKDocumentFocusListener::getFocusedParagraph() const
+{
+ aboutView("LOKDocumentFocusListener::getFocusedParagraph", this, m_pViewShell);
+ aboutParagraph("LOKDocumentFocusListener::getFocusedParagraph",
+ m_sFocusedParagraph, m_nCaretPosition, m_nSelectionStart, m_nSelectionEnd);
+
+ std::string aPayload;
+ paragraphPropertiesToJson(aPayload);
OUString sRet = OUString::fromUtf8(aPayload);
return sRet;
}
@@ -620,16 +706,8 @@ int LOKDocumentFocusListener::getCaretPosition() const
void LOKDocumentFocusListener::notifyFocusedParagraphChanged(bool force)
{
aboutView("LOKDocumentFocusListener::notifyFocusedParagraphChanged", this, m_pViewShell);
- bool bLeftToRight = m_nCaretPosition == m_nSelectionEnd;
- boost::property_tree::ptree aPayloadTree;
- aPayloadTree.put("content", m_sFocusedParagraph.toUtf8().getStr());
- aPayloadTree.put("position", m_nCaretPosition);
- aPayloadTree.put("start", bLeftToRight ? m_nSelectionStart : m_nSelectionEnd);
- aPayloadTree.put("end", bLeftToRight ? m_nSelectionEnd : m_nSelectionStart);
- aPayloadTree.put("force", force ? 1 : 0);
- std::stringstream aStream;
- boost::property_tree::write_json(aStream, aPayloadTree);
- std::string aPayload = aStream.str();
+ std::string aPayload;
+ paragraphPropertiesToJson(aPayload, force);
if (m_pViewShell)
{
aboutParagraph("LOKDocumentFocusListener::notifyFocusedParagraphChanged",
@@ -672,6 +750,59 @@ void LOKDocumentFocusListener::notifyTextSelectionChanged()
}
}
+void LOKDocumentFocusListener::notifyFocusedCellChanged(
+ sal_Int32 nOutCount, const std::vector<TableSizeType>& aInList,
+ sal_Int32 nRow, sal_Int32 nCol, sal_Int32 nRowSpan, sal_Int32 nColSpan)
+{
+ aboutView("LOKDocumentFocusListener::notifyTablePositionChanged", this, m_pViewShell);
+ boost::property_tree::ptree aPayloadTree;
+ if (nOutCount > 0)
+ {
+ aPayloadTree.put("outCount", nOutCount);
+ }
+ if (aInList.size() > 0)
+ {
+ boost::property_tree::ptree aInListNode;
+ for (const auto& rTableSize: aInList)
+ {
+ boost::property_tree::ptree aTableSizeNode;
+ aTableSizeNode.put("rowCount", rTableSize.nRowCount);
+ aTableSizeNode.put("colCount", rTableSize.nColCount);
+
+ aInListNode.push_back(std::make_pair(std::string(), aTableSizeNode));
+ }
+ aPayloadTree.add_child("inList", aInListNode);
+ }
+
+ aPayloadTree.put("row", nRow);
+ aPayloadTree.put("col", nCol);
+
+ if (nRowSpan > 1)
+ {
+ aPayloadTree.put("rowSpan", nRowSpan);
+ }
+ if (nColSpan > 1)
+ {
+ aPayloadTree.put("colSpan", nColSpan);
+ }
+
+ boost::property_tree::ptree aContentNode;
+ paragraphPropertiesToTree(aContentNode);
+ aPayloadTree.add_child("paragraph", aContentNode);
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aPayloadTree);
+ std::string aPayload = aStream.str();
+ if (m_pViewShell)
+ {
+ aboutFocusedCellChanged(nOutCount, aInList, nRow, nCol, nRowSpan, nColSpan);
+ aboutParagraph("LOKDocumentFocusListener::notifyFocusedCellChanged: paragraph: ",
+ m_sFocusedParagraph, m_nCaretPosition, m_nSelectionStart, m_nSelectionEnd, false);
+
+ m_pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_A11Y_FOCUSED_CELL_CHANGED, aPayload.c_str());
+ }
+}
+
void LOKDocumentFocusListener::disposing( const lang::EventObject& aEvent )
{
// Unref the object here, but do not remove as listener since the object
@@ -726,7 +857,6 @@ void LOKDocumentFocusListener::updateAndNotifyParagraph(
notifyFocusedParagraphChanged(force);
}
-
void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventObject& aEvent )
{
aboutView("LOKDocumentFocusListener::notifyEvent", this, m_pViewShell);
@@ -748,6 +878,8 @@ void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventO
if( accessibility::AccessibleStateType::FOCUSED == nState )
{
SAL_INFO("lok.a11y", "LOKDocumentFocusListener::notifyEvent: FOCUSED");
+ uno::Reference<css::accessibility::XAccessibleText> xAccText(xAccessibleObject, uno::UNO_QUERY);
+
if (m_bIsEditingCell)
{
if (!hasState(aEvent, accessibility::AccessibleStateType::ACTIVE))
@@ -757,8 +889,114 @@ void LOKDocumentFocusListener::notifyEvent(const accessibility::AccessibleEventO
return;
}
}
- uno::Reference<css::accessibility::XAccessibleText> xAccText(xAccessibleObject, uno::UNO_QUERY);
- updateAndNotifyParagraph(xAccText, false, "STATE_CHANGED: FOCUSED");
+
+ // 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())
+ {
+ uno::Reference<accessibility::XAccessibleContext> xParentContext(xParent, uno::UNO_QUERY);
+ if (xParentContext.is()
+ && xParentContext->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL)
+ {
+ uno::Reference<accessibility::XAccessible> xCellParent = xParentContext->getAccessibleParent();
+ if (xCellParent.is())
+ {
+ uno::Reference<accessibility::XAccessibleTable> xTable(xCellParent, uno::UNO_QUERY);
+ if (xTable.is())
+ {
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ // 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");
+ }
+ }
aboutTextFormatting("LOKDocumentFocusListener::notifyEvent: STATE_CHANGED: FOCUSED", xAccText);
}
break;