summaryrefslogtreecommitdiffstats
path: root/sw/source/core/access
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/core/access
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/access')
-rw-r--r--sw/source/core/access/AccessibilityCheck.cxx1703
-rw-r--r--sw/source/core/access/AccessibilityIssue.cxx390
-rw-r--r--sw/source/core/access/acccell.cxx454
-rw-r--r--sw/source/core/access/acccell.hxx133
-rw-r--r--sw/source/core/access/acccontext.cxx1529
-rw-r--r--sw/source/core/access/acccontext.hxx359
-rw-r--r--sw/source/core/access/accdoc.cxx720
-rw-r--r--sw/source/core/access/accdoc.hxx173
-rw-r--r--sw/source/core/access/accembedded.cxx121
-rw-r--r--sw/source/core/access/accembedded.hxx72
-rw-r--r--sw/source/core/access/accfootnote.cxx120
-rw-r--r--sw/source/core/access/accfootnote.hxx65
-rw-r--r--sw/source/core/access/accframe.cxx479
-rw-r--r--sw/source/core/access/accframe.hxx160
-rw-r--r--sw/source/core/access/accframebase.cxx365
-rw-r--r--sw/source/core/access/accframebase.hxx65
-rw-r--r--sw/source/core/access/accfrmobj.cxx401
-rw-r--r--sw/source/core/access/accfrmobj.hxx94
-rw-r--r--sw/source/core/access/accfrmobjmap.cxx149
-rw-r--r--sw/source/core/access/accfrmobjmap.hxx126
-rw-r--r--sw/source/core/access/accfrmobjslist.cxx163
-rw-r--r--sw/source/core/access/accfrmobjslist.hxx131
-rw-r--r--sw/source/core/access/accgraphic.cxx75
-rw-r--r--sw/source/core/access/accgraphic.hxx57
-rw-r--r--sw/source/core/access/accheaderfooter.cxx112
-rw-r--r--sw/source/core/access/accheaderfooter.hxx64
-rw-r--r--sw/source/core/access/acchyperlink.cxx236
-rw-r--r--sw/source/core/access/acchyperlink.hxx75
-rw-r--r--sw/source/core/access/acchypertextdata.cxx46
-rw-r--r--sw/source/core/access/acchypertextdata.hxx54
-rw-r--r--sw/source/core/access/accmap.cxx3368
-rw-r--r--sw/source/core/access/accnotextframe.cxx299
-rw-r--r--sw/source/core/access/accnotextframe.hxx125
-rw-r--r--sw/source/core/access/accnotexthyperlink.cxx234
-rw-r--r--sw/source/core/access/accnotexthyperlink.hxx65
-rw-r--r--sw/source/core/access/accpage.cxx158
-rw-r--r--sw/source/core/access/accpage.hxx77
-rw-r--r--sw/source/core/access/accpara.cxx3531
-rw-r--r--sw/source/core/access/accpara.hxx400
-rw-r--r--sw/source/core/access/accportions.cxx752
-rw-r--r--sw/source/core/access/accportions.hxx171
-rw-r--r--sw/source/core/access/accpreview.cxx76
-rw-r--r--sw/source/core/access/accpreview.hxx68
-rw-r--r--sw/source/core/access/accselectionhelper.cxx342
-rw-r--r--sw/source/core/access/accselectionhelper.hxx73
-rw-r--r--sw/source/core/access/acctable.cxx1722
-rw-r--r--sw/source/core/access/acctable.hxx279
-rw-r--r--sw/source/core/access/acctextframe.cxx304
-rw-r--r--sw/source/core/access/acctextframe.hxx113
-rw-r--r--sw/source/core/access/parachangetrackinginfo.cxx205
-rw-r--r--sw/source/core/access/parachangetrackinginfo.hxx51
-rw-r--r--sw/source/core/access/textmarkuphelper.cxx231
-rw-r--r--sw/source/core/access/textmarkuphelper.hxx76
53 files changed, 21411 insertions, 0 deletions
diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx
new file mode 100644
index 0000000000..e4eaa089ef
--- /dev/null
+++ b/sw/source/core/access/AccessibilityCheck.cxx
@@ -0,0 +1,1703 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <AccessibilityCheck.hxx>
+#include <AccessibilityIssue.hxx>
+#include <AccessibilityCheckStrings.hrc>
+#include <strings.hrc>
+#include <ndnotxt.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <svx/svdpage.hxx>
+#include <sortedobjs.hxx>
+#include <swtable.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <unoparagraph.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/langitem.hxx>
+#include <calbck.hxx>
+#include <charatr.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <txtfrm.hxx>
+#include <svl/itemiter.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/vector_utils.hxx>
+#include <svx/swframetypes.hxx>
+#include <fmtanchr.hxx>
+#include <dcontact.hxx>
+#include <unotext.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdasitm.hxx>
+#include <ndgrf.hxx>
+#include <svl/fstathelper.hxx>
+#include <osl/file.h>
+
+namespace sw
+{
+namespace
+{
+SwTextNode* lclSearchNextTextNode(SwNode* pCurrent)
+{
+ SwTextNode* pTextNode = nullptr;
+
+ auto nIndex = pCurrent->GetIndex();
+ auto nCount = pCurrent->GetNodes().Count();
+
+ nIndex++; // go to next node
+
+ while (pTextNode == nullptr && nIndex < nCount)
+ {
+ auto pNode = pCurrent->GetNodes()[nIndex];
+ if (pNode->IsTextNode())
+ pTextNode = pNode->GetTextNode();
+ nIndex++;
+ }
+
+ return pTextNode;
+}
+
+std::shared_ptr<sw::AccessibilityIssue>
+lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText,
+ sfx::AccessibilityIssueID eIssue = sfx::AccessibilityIssueID::UNSPECIFIED)
+{
+ auto pIssue = std::make_shared<sw::AccessibilityIssue>(eIssue);
+ pIssue->m_aIssueText = rText;
+ rIssueCollection.getIssues().push_back(pIssue);
+ return pIssue;
+}
+
+class NodeCheck : public BaseCheck
+{
+public:
+ NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : BaseCheck(rIssueCollection)
+ {
+ }
+
+ virtual void check(SwNode* pCurrent) = 0;
+};
+
+// Check NoTextNodes: Graphic, OLE for alt (title) text
+class NoTextNodeAltTextCheck : public NodeCheck
+{
+ void checkNoTextNode(SwNoTextNode* pNoTextNode)
+ {
+ if (!pNoTextNode)
+ return;
+
+ const SwFrameFormat* pFrameFormat = pNoTextNode->GetFlyFormat();
+ if (!pFrameFormat)
+ return;
+
+ // linked graphic with broken link
+ if (pNoTextNode->IsGrfNode() && pNoTextNode->GetGrfNode()->IsLinkedFile())
+ {
+ OUString sURL(pNoTextNode->GetGrfNode()->GetGraphic().getOriginURL());
+ if (!FStatHelper::IsDocument(sURL))
+ {
+ INetURLObject aURL(sURL);
+ OUString aSystemPath = sURL;
+
+ // abbreviate URL
+ if (aURL.GetProtocol() == INetProtocol::File)
+ {
+ OUString aAbbreviatedPath;
+ aSystemPath = aURL.getFSysPath(FSysStyle::Detect);
+ osl_abbreviateSystemPath(aSystemPath.pData, &aAbbreviatedPath.pData, 46,
+ nullptr);
+ sURL = aAbbreviatedPath;
+ }
+
+ OUString sIssueText = SwResId(STR_LINKED_GRAPHIC)
+ .replaceAll("%OBJECT_NAME%", pFrameFormat->GetName())
+ .replaceFirst("%LINK%", sURL);
+
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::LINKED_GRAPHIC);
+ pIssue->setDoc(pNoTextNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::LINKED);
+ pIssue->setObjectID(pFrameFormat->GetName());
+ pIssue->setNode(pNoTextNode);
+ pIssue->setAdditionalInfo({ aSystemPath });
+ }
+ }
+
+ if (!pNoTextNode->GetTitle().isEmpty() || !pNoTextNode->GetDescription().isEmpty())
+ return;
+
+ OUString sIssueText
+ = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", pFrameFormat->GetName());
+
+ if (pNoTextNode->IsOLENode())
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::NO_ALT_OLE);
+ pIssue->setDoc(pNoTextNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::OLE);
+ pIssue->setObjectID(pFrameFormat->GetName());
+ }
+ else if (pNoTextNode->IsGrfNode())
+ {
+ const SfxBoolItem* pIsDecorItem = pFrameFormat->GetItemIfSet(RES_DECORATIVE);
+ if (!(pIsDecorItem && pIsDecorItem->GetValue()))
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::NO_ALT_GRAPHIC);
+ pIssue->setDoc(pNoTextNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::GRAPHIC);
+ pIssue->setObjectID(pFrameFormat->GetName());
+ pIssue->setNode(pNoTextNode);
+ }
+ }
+ }
+
+public:
+ NoTextNodeAltTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (pCurrent->GetNodeType() & SwNodeType::NoTextMask)
+ {
+ SwNoTextNode* pNoTextNode = pCurrent->GetNoTextNode();
+ if (pNoTextNode)
+ checkNoTextNode(pNoTextNode);
+ }
+ }
+};
+
+// Check Table node if the table is merged and split.
+class TableNodeMergeSplitCheck : public NodeCheck
+{
+private:
+ void addTableIssue(SwTable const& rTable, SwDoc& rDoc)
+ {
+ const SwTableFormat* pFormat = rTable.GetFrameFormat();
+ OUString sName = pFormat->GetName();
+ OUString sIssueText = SwResId(STR_TABLE_MERGE_SPLIT).replaceAll("%OBJECT_NAME%", sName);
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::TABLE_MERGE_SPLIT);
+ pIssue->setDoc(rDoc);
+ pIssue->setIssueObject(IssueObject::TABLE);
+ pIssue->setObjectID(sName);
+ }
+
+ void checkTableNode(SwTableNode* pTableNode)
+ {
+ if (!pTableNode)
+ return;
+
+ SwTable const& rTable = pTableNode->GetTable();
+ SwDoc& rDoc = pTableNode->GetDoc();
+ if (rTable.IsTableComplex())
+ {
+ addTableIssue(rTable, rDoc);
+ }
+ else
+ {
+ if (rTable.GetTabLines().size() > 1)
+ {
+ int i = 0;
+ size_t nFirstLineSize = 0;
+ bool bAllColumnsSameSize = true;
+ bool bCellSpansOverMoreRows = false;
+
+ for (SwTableLine const* pTableLine : rTable.GetTabLines())
+ {
+ if (i == 0)
+ {
+ nFirstLineSize = pTableLine->GetTabBoxes().size();
+ }
+ else
+ {
+ size_t nLineSize = pTableLine->GetTabBoxes().size();
+ if (nFirstLineSize != nLineSize)
+ {
+ bAllColumnsSameSize = false;
+ }
+ }
+ i++;
+
+ // Check for row span in each table box (cell)
+ for (SwTableBox const* pBox : pTableLine->GetTabBoxes())
+ {
+ if (pBox->getRowSpan() > 1)
+ bCellSpansOverMoreRows = true;
+ }
+ }
+ if (!bAllColumnsSameSize || bCellSpansOverMoreRows)
+ {
+ addTableIssue(rTable, rDoc);
+ }
+ }
+ }
+ }
+
+public:
+ TableNodeMergeSplitCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (pCurrent->GetNodeType() & SwNodeType::Table)
+ {
+ SwTableNode* pTableNode = pCurrent->GetTableNode();
+ if (pTableNode)
+ checkTableNode(pTableNode);
+ }
+ }
+};
+
+class TableFormattingCheck : public NodeCheck
+{
+private:
+ void checkTableNode(SwTableNode* pTableNode)
+ {
+ if (!pTableNode)
+ return;
+
+ const SwTable& rTable = pTableNode->GetTable();
+ if (!rTable.IsTableComplex())
+ {
+ size_t nEmptyBoxes = 0;
+ size_t nBoxCount = 0;
+ for (const SwTableLine* pTableLine : rTable.GetTabLines())
+ {
+ nBoxCount += pTableLine->GetTabBoxes().size();
+ for (const SwTableBox* pBox : pTableLine->GetTabBoxes())
+ if (pBox->IsEmpty())
+ ++nEmptyBoxes;
+ }
+ // If more than half of the boxes are empty we can assume that it is used for formatting
+ if (nEmptyBoxes > nBoxCount / 2)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TABLE_FORMATTING),
+ sfx::AccessibilityIssueID::TABLE_FORMATTING);
+
+ pIssue->setDoc(pTableNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::TABLE);
+ if (const SwTableFormat* pFormat = rTable.GetFrameFormat())
+ pIssue->setObjectID(pFormat->GetName());
+ }
+ }
+ }
+
+public:
+ TableFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (pCurrent->GetNodeType() & SwNodeType::Table)
+ {
+ SwTableNode* pTableNode = pCurrent->GetTableNode();
+ if (pTableNode)
+ checkTableNode(pTableNode);
+ }
+ }
+};
+
+class NumberingCheck : public NodeCheck
+{
+private:
+ const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{
+ { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" },
+ { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" }
+ };
+
+public:
+ NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pCurrentTextNode = pCurrent->GetTextNode();
+ SwTextNode* pNextTextNode = lclSearchNextTextNode(pCurrent);
+
+ if (!pNextTextNode)
+ return;
+
+ for (auto& rPair : m_aNumberingCombinations)
+ {
+ if (pCurrentTextNode->GetText().startsWith(rPair.first)
+ && pNextTextNode->GetText().startsWith(rPair.second))
+ {
+ OUString sNumbering = rPair.first + " " + rPair.second + "...";
+ OUString sIssueText
+ = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering);
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::MANUAL_NUMBERING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ }
+ }
+ }
+};
+
+class HyperlinkCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange, SwTextNode* pTextNode,
+ sal_Int32 nStart)
+ {
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (!xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL"))
+ return;
+
+ OUString sHyperlink;
+ xProperties->getPropertyValue("HyperLinkURL") >>= sHyperlink;
+ if (!sHyperlink.isEmpty())
+ {
+ OUString sText = xTextRange->getString();
+ INetURLObject aHyperlink(sHyperlink);
+ std::shared_ptr<sw::AccessibilityIssue> pIssue;
+ if (aHyperlink.GetProtocol() != INetProtocol::NotValid
+ && INetURLObject(sText) == aHyperlink)
+ {
+ OUString sIssueText
+ = SwResId(STR_HYPERLINK_TEXT_IS_LINK).replaceFirst("%LINK%", sHyperlink);
+ pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::HYPERLINK_IS_TEXT);
+ }
+ else if (sText.getLength() <= 5)
+ {
+ pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HYPERLINK_TEXT_IS_SHORT),
+ sfx::AccessibilityIssueID::HYPERLINK_SHORT);
+ }
+
+ if (pIssue)
+ {
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(nStart);
+ pIssue->setEnd(nStart + sText.getLength());
+ }
+ }
+ }
+
+public:
+ HyperlinkCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ rtl::Reference<SwXParagraph> xParagraph
+ = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode, nullptr);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xRunEnum = xParagraph->createEnumeration();
+ sal_Int32 nStart = 0;
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ {
+ checkTextRange(xRun, pTextNode, nStart);
+ nStart += xRun->getString().getLength();
+ }
+ }
+ }
+};
+
+// Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
+double calculateRelativeLuminance(Color const& rColor)
+{
+ // Convert to BColor which has R, G, B colors components
+ // represented by a floating point number from [0.0, 1.0]
+ const basegfx::BColor aBColor = rColor.getBColor();
+
+ double r = aBColor.getRed();
+ double g = aBColor.getGreen();
+ double b = aBColor.getBlue();
+
+ // Calculate the values according to the described algorithm
+ r = (r <= 0.03928) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
+ g = (g <= 0.03928) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
+ b = (b <= 0.03928) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+}
+
+// TODO move to common color tools (BColorTools maybe)
+// Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
+double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
+{
+ const double fLuminance1 = calculateRelativeLuminance(rColor1);
+ const double fLuminance2 = calculateRelativeLuminance(rColor2);
+ const std::pair<const double, const double> aMinMax = std::minmax(fLuminance1, fLuminance2);
+
+ // (L1 + 0.05) / (L2 + 0.05)
+ // L1 is the lighter color (greater luminance value)
+ // L2 is the darker color (smaller luminance value)
+ return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
+}
+
+class TextContrastCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
+ uno::Reference<text::XTextContent> const& xParagraph, SwTextNode* pTextNode,
+ sal_Int32 nTextStart)
+ {
+ if (xTextRange->getString().isEmpty())
+ return;
+
+ Color nParaBackColor(COL_AUTO);
+ uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY);
+ if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor))
+ {
+ SAL_WARN("sw.a11y", "ParaBackColor void");
+ return;
+ }
+
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (!xProperties.is())
+ return;
+
+ // Foreground color
+ sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized
+ if (!(xProperties->getPropertyValue("CharColor") >>= nCharColor))
+ { // not sure this is impossible, can the default be void?
+ SAL_WARN("sw.a11y", "CharColor void");
+ return;
+ }
+
+ const SwPageDesc* pPageDescription = pTextNode->FindPageDesc();
+ if (!pPageDescription)
+ return;
+ const SwFrameFormat& rPageFormat = pPageDescription->GetMaster();
+ const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
+
+ const XFillStyleItem* pXFillStyleItem(
+ rPageSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
+ Color aPageBackground(COL_AUTO);
+
+ if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID)
+ {
+ const XFillColorItem* rXFillColorItem
+ = rPageSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false);
+ aPageBackground = rXFillColorItem->GetColorValue();
+ }
+
+ Color nCharBackColor(COL_AUTO);
+
+ if (!(xProperties->getPropertyValue("CharBackColor") >>= nCharBackColor))
+ {
+ SAL_WARN("sw.a11y", "CharBackColor void");
+ return;
+ }
+ // Determine the background color
+ // Try Character background (highlight)
+ Color aBackgroundColor(nCharBackColor);
+
+ // If not character background color, try paragraph background color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = nParaBackColor;
+ else
+ {
+ OUString sCharStyleName;
+ Color nCharStyleBackColor(COL_AUTO);
+ if (xProperties->getPropertyValue("CharStyleName") >>= sCharStyleName)
+ {
+ try
+ {
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(
+ pTextNode->GetDoc().GetDocShell()->GetModel(), uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xCont
+ = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(
+ xCont->getByName("CharacterStyles"), uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xInfo(
+ xStyleFamily->getByName(sCharStyleName), uno::UNO_QUERY);
+ xInfo->getPropertyValue("CharBackColor") >>= nCharStyleBackColor;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ else
+ {
+ SAL_WARN("sw.a11y", "CharStyleName void");
+ }
+
+ if (aBackgroundColor != nCharStyleBackColor)
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(nTextStart);
+ pIssue->setEnd(nTextStart + xTextRange->getString().getLength());
+ }
+ }
+
+ Color aForegroundColor(ColorTransparency, nCharColor);
+ if (aForegroundColor == COL_AUTO)
+ return;
+
+ // If not paragraph background color, try page color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = aPageBackground;
+
+ // If not page color, assume white background color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = COL_WHITE;
+
+ double fContrastRatio = calculateContrastRatio(aForegroundColor, aBackgroundColor);
+ if (fContrastRatio < 4.5)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_CONTRAST));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ pIssue->setDoc(pTextNode->GetDoc());
+ pIssue->setStart(nTextStart);
+ pIssue->setEnd(nTextStart + xTextRange->getString().getLength());
+ }
+ }
+
+public:
+ TextContrastCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ rtl::Reference<SwXParagraph> xParagraph
+ = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode, nullptr);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xRunEnum = xParagraph->createEnumeration();
+ sal_Int32 nStart = 0;
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ {
+ checkTextRange(xRun, xParagraph, pTextNode, nStart);
+ nStart += xRun->getString().getLength();
+ }
+ }
+ }
+};
+
+class TextFormattingCheck : public NodeCheck
+{
+public:
+ TextFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
+ {
+ const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
+ SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
+ const SfxPoolItem* pItem = aItemIter.GetCurItem();
+ std::vector<OUString> aFormattings;
+ while (pItem)
+ {
+ OUString sFormattingType;
+ switch (pItem->Which())
+ {
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_WEIGHT:
+ sFormattingType = "Weight";
+ break;
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CTL_POSTURE:
+ sFormattingType = "Posture";
+ break;
+
+ case RES_CHRATR_SHADOWED:
+ sFormattingType = "Shadowed";
+ break;
+
+ case RES_CHRATR_COLOR:
+ sFormattingType = "Font Color";
+ break;
+
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CTL_FONTSIZE:
+ sFormattingType = "Font Size";
+ break;
+
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CTL_FONT:
+ sFormattingType = "Font";
+ break;
+
+ case RES_CHRATR_EMPHASIS_MARK:
+ sFormattingType = "Emphasis Mark";
+ break;
+
+ case RES_CHRATR_UNDERLINE:
+ sFormattingType = "Underline";
+ break;
+
+ case RES_CHRATR_OVERLINE:
+ sFormattingType = "Overline";
+ break;
+
+ case RES_CHRATR_CROSSEDOUT:
+ sFormattingType = "Strikethrough";
+ break;
+
+ case RES_CHRATR_RELIEF:
+ sFormattingType = "Relief";
+ break;
+
+ case RES_CHRATR_CONTOUR:
+ sFormattingType = "Outline";
+ break;
+ default:
+ break;
+ }
+ if (!sFormattingType.isEmpty())
+ aFormattings.push_back(sFormattingType);
+ pItem = aItemIter.NextItem();
+ }
+ if (aFormattings.empty())
+ return;
+
+ o3tl::remove_duplicates(aFormattings);
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(pTextAttr->GetStart());
+ pIssue->setEnd(pTextAttr->GetAnyEnd());
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (pTextNode->HasHints())
+ {
+ SwpHints& rHints = pTextNode->GetSwpHints();
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ {
+ const SwTextAttr* pTextAttr = rHints.Get(i);
+ if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
+ {
+ checkAutoFormat(pTextNode, pTextAttr);
+ }
+ }
+ }
+ else if (pTextNode->HasSwAttrSet())
+ {
+ // Paragraph doesn't have hints but the entire paragraph might have char attributes
+ auto& aSwAttrSet = pTextNode->GetSwAttrSet();
+ auto nParagraphLength = pTextNode->GetText().getLength();
+ if (nParagraphLength == 0)
+ return;
+ if (aSwAttrSet.GetItem(RES_CHRATR_WEIGHT, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CJK_WEIGHT, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CTL_WEIGHT, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_POSTURE, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CJK_POSTURE, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CTL_POSTURE, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_SHADOWED, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_COLOR, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_EMPHASIS_MARK, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_UNDERLINE, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_OVERLINE, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CROSSEDOUT, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_RELIEF, false)
+ || aSwAttrSet.GetItem(RES_CHRATR_CONTOUR, false))
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setEnd(nParagraphLength);
+ }
+ }
+ }
+};
+
+class NewlineSpacingCheck : public NodeCheck
+{
+private:
+ static SwTextNode* getPrevTextNode(SwNode* pCurrent)
+ {
+ SwTextNode* pTextNode = nullptr;
+
+ auto nIndex = pCurrent->GetIndex();
+
+ nIndex--; // go to previous node
+
+ while (pTextNode == nullptr && nIndex >= SwNodeOffset(0))
+ {
+ auto pNode = pCurrent->GetNodes()[nIndex];
+ if (pNode->IsTextNode())
+ pTextNode = pNode->GetTextNode();
+ nIndex--;
+ }
+
+ return pTextNode;
+ }
+
+public:
+ NewlineSpacingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ // Don't count empty table box text nodes
+ if (pCurrent->GetTableBox())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ auto nParagraphLength = pTextNode->GetText().getLength();
+ if (nParagraphLength == 0)
+ {
+ SwTextNode* pPrevTextNode = getPrevTextNode(pCurrent);
+ if (!pPrevTextNode)
+ return;
+ if (pPrevTextNode->GetText().getLength() == 0)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ }
+ }
+ else
+ {
+ // Check for excess lines inside this paragraph
+ const OUString& sParagraphText = pTextNode->GetText();
+ int nLineCount = 0;
+ for (sal_Int32 i = 0; i < nParagraphLength; i++)
+ {
+ auto aChar = sParagraphText[i];
+ if (aChar == '\n')
+ {
+ nLineCount++;
+ // Looking for 2 newline characters and above as one can be part of the line
+ // break after a sentence
+ if (nLineCount > 2)
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_NEWLINES_SPACE),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(i);
+ pIssue->setEnd(i);
+ }
+ }
+ // Don't count carriage return as normal character
+ else if (aChar != '\r')
+ {
+ nLineCount = 0;
+ }
+ }
+ }
+ }
+};
+
+class SpaceSpacingCheck : public NodeCheck
+{
+public:
+ SpaceSpacingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ auto nParagraphLength = pTextNode->GetText().getLength();
+ const OUString& sParagraphText = pTextNode->GetText();
+ sal_Int32 nSpaceCount = 0;
+ sal_Int32 nSpaceStart = 0;
+ sal_Int32 nTabCount = 0;
+ bool bNonSpaceFound = false;
+ bool bPreviousWasChar = false;
+ for (sal_Int32 i = 0; i < nParagraphLength; i++)
+ {
+ switch (sParagraphText[i])
+ {
+ case ' ':
+ {
+ if (bNonSpaceFound)
+ {
+ nSpaceCount++;
+ if (nSpaceCount == 2)
+ nSpaceStart = i;
+ }
+ break;
+ }
+ case '\t':
+ {
+ if (bPreviousWasChar)
+ {
+ ++nTabCount;
+ bPreviousWasChar = false;
+ if (nTabCount == 2)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection,
+ SwResId(STR_AVOID_TABS_FORMATTING),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(0);
+ pIssue->setEnd(nParagraphLength);
+ }
+ }
+ break;
+ }
+ default:
+ {
+ if (nSpaceCount >= 2)
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_SPACES_SPACE),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(nSpaceStart);
+ pIssue->setEnd(nSpaceStart + nSpaceCount - 1);
+ }
+ bNonSpaceFound = true;
+ bPreviousWasChar = true;
+ nSpaceCount = 0;
+ break;
+ }
+ }
+ }
+ }
+};
+
+class FakeFootnoteCheck : public NodeCheck
+{
+private:
+ void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
+ {
+ const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
+ SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
+ const SfxPoolItem* pItem = aItemIter.GetCurItem();
+ while (pItem)
+ {
+ if (pItem->Which() == RES_CHRATR_ESCAPEMENT)
+ {
+ auto pEscapementItem = static_cast<const SvxEscapementItem*>(pItem);
+ if (pEscapementItem->GetEscapement() == SvxEscapement::Superscript
+ && pTextAttr->GetStart() == 0 && pTextAttr->GetAnyEnd() == 1)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_FOOTNOTES),
+ sfx::AccessibilityIssueID::FAKE_FOOTNOTE);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(0);
+ pIssue->setEnd(pTextNode->GetText().getLength());
+ break;
+ }
+ }
+ pItem = aItemIter.NextItem();
+ }
+ }
+
+public:
+ FakeFootnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (pTextNode->GetText().getLength() == 0)
+ return;
+
+ if (pTextNode->GetText()[0] == '*')
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_FOOTNOTES),
+ sfx::AccessibilityIssueID::FAKE_FOOTNOTE);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(0);
+ pIssue->setEnd(pTextNode->GetText().getLength());
+ }
+ else if (pTextNode->HasHints())
+ {
+ SwpHints& rHints = pTextNode->GetSwpHints();
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ {
+ const SwTextAttr* pTextAttr = rHints.Get(i);
+ if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
+ {
+ checkAutoFormat(pTextNode, pTextAttr);
+ }
+ }
+ }
+ }
+};
+
+class FakeCaptionCheck : public NodeCheck
+{
+public:
+ FakeCaptionCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ const OUString& sText = pTextNode->GetText();
+
+ if (sText.getLength() == 0)
+ return;
+
+ // Check if it's a real caption
+ if (const SwNode* pStartFly = pCurrent->FindFlyStartNode())
+ {
+ const SwFrameFormat* pFormat = pStartFly->GetFlyFormat();
+ if (!pFormat || pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ return;
+ }
+
+ auto aIter = SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(*pTextNode);
+ auto nCount = 0;
+ for (auto aTextFrame = aIter.First(); aTextFrame; aTextFrame = aIter.Next())
+ {
+ auto aObjects = aTextFrame->GetDrawObjs();
+ if (aObjects)
+ nCount += aObjects->size();
+
+ if (nCount > 1)
+ return;
+ }
+
+ // Check that there's exactly 1 image anchored in this node
+ if (nCount == 1)
+ {
+ OString sTemp;
+ sText.convertToString(&sTemp, RTL_TEXTENCODING_ASCII_US, 0);
+ if (sText.startsWith(SwResId(STR_POOLCOLL_LABEL))
+ || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_ABB))
+ || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_TABLE))
+ || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FRAME))
+ || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_DRAWING))
+ || sText.startsWith(SwResId(STR_POOLCOLL_LABEL_FIGURE)))
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FAKE_CAPTIONS),
+ sfx::AccessibilityIssueID::FAKE_CAPTION);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(0);
+ pIssue->setEnd(sText.getLength());
+ }
+ }
+ }
+};
+
+class BlinkingTextCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange, SwTextNode* pTextNode,
+ sal_Int32 nStart)
+ {
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (xProperties.is() && xProperties->getPropertySetInfo()->hasPropertyByName("CharFlash"))
+ {
+ bool bBlinking = false;
+ xProperties->getPropertyValue("CharFlash") >>= bBlinking;
+
+ if (bBlinking)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_BLINKING));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ pIssue->setDoc(pTextNode->GetDoc());
+ pIssue->setStart(nStart);
+ pIssue->setEnd(nStart + xTextRange->getString().getLength());
+ }
+ }
+ }
+
+public:
+ BlinkingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ rtl::Reference<SwXParagraph> xParagraph
+ = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode, nullptr);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xRunEnum = xParagraph->createEnumeration();
+ sal_Int32 nStart = 0;
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ {
+ checkTextRange(xRun, pTextNode, nStart);
+ nStart += xRun->getString().getLength();
+ }
+ }
+ }
+};
+
+class HeaderCheck : public NodeCheck
+{
+private:
+ int m_nPreviousLevel;
+
+public:
+ HeaderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ , m_nPreviousLevel(0)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ SwTextFormatColl* pCollection = pTextNode->GetTextColl();
+ if (!pCollection->IsAssignedToListLevelOfOutlineStyle())
+ return;
+
+ int nLevel = pCollection->GetAssignedOutlineStyleLevel();
+ assert(nLevel >= 0);
+ if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ }
+ m_nPreviousLevel = nLevel;
+ }
+};
+
+// ISO 142891-1 : 7.14
+class NonInteractiveFormCheck : public NodeCheck
+{
+public:
+ NonInteractiveFormCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ const auto& text = pTextNode->GetText();
+
+ // Series of tests to detect if there are fake forms in the text.
+ bool bCheck = text.indexOf("___") == -1; // Repeated underscores.
+
+ if (bCheck)
+ bCheck = text.indexOf("....") == -1; // Repeated dots.
+
+ if (bCheck)
+ bCheck = text.indexOf(u"……") == -1; // Repeated ellipsis.
+
+ if (bCheck)
+ bCheck = text.indexOf(u"….") == -1; // A dot after an ellipsis.
+
+ if (bCheck)
+ bCheck = text.indexOf(u".…") == -1; // An ellipsis after a dot.
+
+ // Checking if all the tests are passed successfully. If not, adding a warning.
+ if (!bCheck)
+ {
+ sal_Int32 nStart = 0;
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_NON_INTERACTIVE_FORMS));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ pIssue->setDoc(pTextNode->GetDoc());
+ pIssue->setStart(nStart);
+ pIssue->setEnd(nStart + text.getLength());
+ }
+ }
+};
+
+/// Check for floating text frames, as it causes problems with reading order.
+class FloatingTextCheck : public NodeCheck
+{
+public:
+ FloatingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ // if node is a text-node and if it has text, we proceed. Otherwise - return.
+ const SwTextNode* textNode = pCurrent->GetTextNode();
+ if (!textNode || textNode->GetText().isEmpty())
+ return;
+
+ // If a node is in fly and if it is not anchored as char, throw warning.
+ const SwNode* pStartFly = pCurrent->FindFlyStartNode();
+ if (!pStartFly)
+ return;
+
+ const SwFrameFormat* pFormat = pStartFly->GetFlyFormat();
+ if (!pFormat)
+ return;
+
+ if (pFormat->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ SwNodeIndex aCurrentIdx(*pCurrent);
+ SwNodeIndex aIdx(*pStartFly);
+ SwNode* pFirstTextNode = &aIdx.GetNode();
+ SwNodeOffset nEnd = pStartFly->EndOfSectionIndex();
+ while (aIdx < nEnd)
+ {
+ if (pFirstTextNode->IsContentNode() && pFirstTextNode->IsTextNode())
+ {
+ if (aIdx == aCurrentIdx)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_FLOATING_TEXT));
+ pIssue->setIssueObject(IssueObject::TEXTFRAME);
+ pIssue->setObjectID(pFormat->GetName());
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ }
+ break;
+ }
+ ++aIdx;
+ pFirstTextNode = &aIdx.GetNode();
+ }
+ }
+ }
+};
+
+/// Heading paragraphs (with outline levels > 0) are not allowed in tables
+class TableHeadingCheck : public NodeCheck
+{
+private:
+ // Boolean indicating if heading-in-table warning is already triggered.
+ bool m_bPrevPassed;
+
+public:
+ TableHeadingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ , m_bPrevPassed(true)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!m_bPrevPassed)
+ return;
+
+ const SwTextNode* textNode = pCurrent->GetTextNode();
+
+ if (textNode && textNode->GetAttrOutlineLevel() != 0)
+ {
+ const SwTableNode* parentTable = pCurrent->FindTableNode();
+
+ if (parentTable)
+ {
+ m_bPrevPassed = false;
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_HEADING_IN_TABLE));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ }
+ }
+ }
+};
+
+/// Checking if headings are ordered correctly.
+class HeadingOrderCheck : public NodeCheck
+{
+public:
+ HeadingOrderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ const SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (!pTextNode)
+ return;
+
+ // If outline level stands for heading level...
+ const int currentLevel = pTextNode->GetAttrOutlineLevel();
+ if (!currentLevel)
+ return;
+
+ // ... and if is bigger than previous by more than 1, warn.
+ if (currentLevel - m_prevLevel > 1)
+ {
+ // Preparing and posting a warning.
+ OUString resultString;
+ if (!m_prevLevel)
+ {
+ resultString = SwResId(STR_HEADING_START);
+ }
+ else
+ {
+ resultString = SwResId(STR_HEADING_ORDER);
+ resultString
+ = resultString.replaceAll("%LEVEL_PREV%", OUString::number(m_prevLevel));
+ }
+ resultString
+ = resultString.replaceAll("%LEVEL_CURRENT%", OUString::number(currentLevel));
+ auto pIssue = lclAddIssue(m_rIssueCollection, resultString);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ }
+
+ // Updating previous level.
+ m_prevLevel = currentLevel;
+ }
+
+private:
+ // Previous heading level to compare with.
+ int m_prevLevel = 0;
+};
+
+/// Checking content controls in header or footer
+class ContentControlCheck : public NodeCheck
+{
+public:
+ ContentControlCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsContentNode())
+ return;
+
+ const SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (pTextNode)
+ {
+ if (pCurrent->FindHeaderStartNode() || pCurrent->FindFooterStartNode())
+ {
+ const SwpHints* pHts = pTextNode->GetpSwpHints();
+ if (pHts)
+ {
+ for (size_t i = 0; i < pHts->Count(); ++i)
+ {
+ const SwTextAttr* pHt = pHts->Get(i);
+ if (pHt->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection,
+ SwResId(STR_CONTENT_CONTROL_IN_HEADER_OR_FOOTER));
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setDoc(pCurrent->GetDoc());
+ pIssue->setNode(pCurrent);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+};
+
+class DocumentCheck : public BaseCheck
+{
+public:
+ DocumentCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : BaseCheck(rIssueCollection)
+ {
+ }
+
+ virtual void check(SwDoc* pDoc) = 0;
+};
+
+// Check default language
+class DocumentDefaultLanguageCheck : public DocumentCheck
+{
+public:
+ DocumentDefaultLanguageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ // TODO maybe - also check RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE if CJK or CTL are enabled
+ const SvxLanguageItem& rLang = pDoc->GetDefault(RES_CHRATR_LANGUAGE);
+ LanguageType eLanguage = rLang.GetLanguage();
+ if (eLanguage == LANGUAGE_NONE)
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_DEFAULT_LANGUAGE),
+ sfx::AccessibilityIssueID::DOCUMENT_LANGUAGE);
+ pIssue->setIssueObject(IssueObject::LANGUAGE_NOT_SET);
+ pIssue->setObjectID(OUString());
+ pIssue->setDoc(*pDoc);
+ }
+ else
+ {
+ for (SwTextFormatColl* pTextFormatCollection : *pDoc->GetTextFormatColls())
+ {
+ const SwAttrSet& rAttrSet = pTextFormatCollection->GetAttrSet();
+ if (rAttrSet.GetLanguage(false).GetLanguage() == LANGUAGE_NONE)
+ {
+ OUString sName = pTextFormatCollection->GetName();
+ OUString sIssueText
+ = SwResId(STR_STYLE_NO_LANGUAGE).replaceAll("%STYLE_NAME%", sName);
+
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::STYLE_LANGUAGE);
+ pIssue->setIssueObject(IssueObject::LANGUAGE_NOT_SET);
+ pIssue->setObjectID(sName);
+ pIssue->setDoc(*pDoc);
+ }
+ }
+ }
+ }
+};
+
+class DocumentTitleCheck : public DocumentCheck
+{
+public:
+ DocumentTitleCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ SwDocShell* pShell = pDoc->GetDocShell();
+ if (!pShell)
+ return;
+
+ const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pShell->GetModel(),
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<document::XDocumentProperties> xDocumentProperties(
+ xDPS->getDocumentProperties());
+ OUString sTitle = xDocumentProperties->getTitle();
+ if (o3tl::trim(sTitle).empty())
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_TITLE),
+ sfx::AccessibilityIssueID::DOCUMENT_TITLE);
+ pIssue->setDoc(*pDoc);
+ pIssue->setIssueObject(IssueObject::DOCUMENT_TITLE);
+ }
+ }
+};
+
+class FootnoteEndnoteCheck : public DocumentCheck
+{
+public:
+ FootnoteEndnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ for (SwTextFootnote* pTextFootnote : pDoc->GetFootnoteIdxs())
+ {
+ SwFormatFootnote const& rFootnote = pTextFootnote->GetFootnote();
+ auto pIssue = lclAddIssue(m_rIssueCollection, rFootnote.IsEndNote()
+ ? SwResId(STR_AVOID_ENDNOTES)
+ : SwResId(STR_AVOID_FOOTNOTES));
+ pIssue->setDoc(*pDoc);
+ pIssue->setIssueObject(IssueObject::FOOTENDNOTE);
+ pIssue->setTextFootnote(pTextFootnote);
+ }
+ }
+};
+
+class BackgroundImageCheck : public DocumentCheck
+{
+public:
+ BackgroundImageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+ void check(SwDoc* pDoc) override
+ {
+ uno::Reference<lang::XComponent> xDoc = pDoc->GetDocShell()->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xDoc, uno::UNO_QUERY);
+ if (!xStyleFamiliesSupplier.is())
+ return;
+ uno::Reference<container::XNameAccess> xStyleFamilies
+ = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"),
+ uno::UNO_QUERY);
+ if (!xStyleFamily.is())
+ return;
+ const uno::Sequence<OUString>& xStyleFamilyNames = xStyleFamily->getElementNames();
+ for (const OUString& rStyleFamilyName : xStyleFamilyNames)
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(
+ xStyleFamily->getByName(rStyleFamilyName), uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ continue;
+ auto aFillStyleContainer = xPropertySet->getPropertyValue("FillStyle");
+ if (aFillStyleContainer.has<drawing::FillStyle>())
+ {
+ drawing::FillStyle aFillStyle = aFillStyleContainer.get<drawing::FillStyle>();
+ if (aFillStyle == drawing::FillStyle_BITMAP)
+ {
+ auto pIssue
+ = lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_BACKGROUND_IMAGES),
+ sfx::AccessibilityIssueID::DOCUMENT_BACKGROUND);
+
+ pIssue->setDoc(*pDoc);
+ pIssue->setIssueObject(IssueObject::DOCUMENT_BACKGROUND);
+ }
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+// Check Shapes, TextBox
+void AccessibilityCheck::checkObject(SwNode* pCurrent, SdrObject* pObject)
+{
+ if (!pObject)
+ return;
+
+ // Check for fontworks.
+ if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>(pObject))
+ {
+ const SdrCustomShapeGeometryItem& rGeometryItem
+ = pCustomShape->GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY);
+
+ if (const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type"))
+ if (pAny->get<OUString>().startsWith("fontwork-"))
+ lclAddIssue(m_aIssueCollection, SwResId(STR_FONTWORKS));
+ }
+
+ // Checking if there is floating Writer text draw object and if so, throwing a warning.
+ // (Floating objects with text create problems with reading order)
+ if (pObject->HasText()
+ && FindFrameFormat(pObject)->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ auto pIssue = lclAddIssue(m_aIssueCollection, SwResId(STR_FLOATING_TEXT));
+ pIssue->setIssueObject(IssueObject::TEXTFRAME);
+ pIssue->setObjectID(pObject->GetName());
+ pIssue->setDoc(*m_pDoc);
+ if (pCurrent)
+ pIssue->setNode(pCurrent);
+ }
+
+ const SdrObjKind nObjId = pObject->GetObjIdentifier();
+ const SdrInventor nInv = pObject->GetObjInventor();
+
+ if (nObjId == SdrObjKind::CustomShape || nObjId == SdrObjKind::Text
+ || nObjId == SdrObjKind::Media || nObjId == SdrObjKind::Group
+ || nObjId == SdrObjKind::Graphic || nInv == SdrInventor::FmForm)
+ {
+ if (pObject->GetTitle().isEmpty() && pObject->GetDescription().isEmpty())
+ {
+ OUString sName = pObject->GetName();
+ OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
+ auto pIssue = lclAddIssue(m_aIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::NO_ALT_SHAPE);
+ // Set FORM Issue for Form objects because of the design mode
+ if (nInv == SdrInventor::FmForm)
+ pIssue->setIssueObject(IssueObject::FORM);
+ else
+ pIssue->setIssueObject(IssueObject::SHAPE);
+
+ pIssue->setObjectID(pObject->GetName());
+ pIssue->setDoc(*m_pDoc);
+ if (pCurrent)
+ pIssue->setNode(pCurrent);
+ }
+ }
+}
+
+void AccessibilityCheck::init()
+{
+ if (m_aDocumentChecks.empty())
+ {
+ m_aDocumentChecks.emplace_back(new DocumentDefaultLanguageCheck(m_aIssueCollection));
+ m_aDocumentChecks.emplace_back(new DocumentTitleCheck(m_aIssueCollection));
+ m_aDocumentChecks.emplace_back(new FootnoteEndnoteCheck(m_aIssueCollection));
+ m_aDocumentChecks.emplace_back(new BackgroundImageCheck(m_aIssueCollection));
+ }
+
+ if (m_aNodeChecks.empty())
+ {
+ m_aNodeChecks.emplace_back(new NoTextNodeAltTextCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new TableNodeMergeSplitCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new TableFormattingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new NumberingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new HyperlinkCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new TextContrastCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new BlinkingTextCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new HeaderCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new TextFormattingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new NonInteractiveFormCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new FloatingTextCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new TableHeadingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new HeadingOrderCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new NewlineSpacingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new SpaceSpacingCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new FakeFootnoteCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new FakeCaptionCheck(m_aIssueCollection));
+ m_aNodeChecks.emplace_back(new ContentControlCheck(m_aIssueCollection));
+ }
+}
+
+void AccessibilityCheck::checkNode(SwNode* pNode)
+{
+ if (m_pDoc == nullptr || pNode == nullptr)
+ return;
+
+ init();
+
+ for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
+ {
+ auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get());
+ if (pNodeCheck)
+ pNodeCheck->check(pNode);
+ }
+}
+
+void AccessibilityCheck::checkDocumentProperties()
+{
+ if (m_pDoc == nullptr)
+ return;
+
+ init();
+
+ for (std::shared_ptr<BaseCheck>& rpDocumentCheck : m_aDocumentChecks)
+ {
+ auto pDocumentCheck = dynamic_cast<DocumentCheck*>(rpDocumentCheck.get());
+ if (pDocumentCheck)
+ pDocumentCheck->check(m_pDoc);
+ }
+}
+
+void AccessibilityCheck::check()
+{
+ if (m_pDoc == nullptr)
+ return;
+
+ init();
+
+ checkDocumentProperties();
+
+ auto const& pNodes = m_pDoc->GetNodes();
+ SwNode* pNode = nullptr;
+ for (SwNodeOffset n(0); n < pNodes.Count(); ++n)
+ {
+ pNode = pNodes[n];
+ if (pNode)
+ {
+ for (std::shared_ptr<BaseCheck>& rpNodeCheck : m_aNodeChecks)
+ {
+ auto pNodeCheck = dynamic_cast<NodeCheck*>(rpNodeCheck.get());
+ if (pNodeCheck)
+ pNodeCheck->check(pNode);
+ }
+
+ for (SwFrameFormat* const& pFrameFormat : pNode->GetAnchoredFlys())
+ {
+ SdrObject* pObject = pFrameFormat->FindSdrObject();
+ if (pObject)
+ checkObject(pNode, pObject);
+ }
+ }
+ }
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/AccessibilityIssue.cxx b/sw/source/core/access/AccessibilityIssue.cxx
new file mode 100644
index 0000000000..346d8bb293
--- /dev/null
+++ b/sw/source/core/access/AccessibilityIssue.cxx
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/dispatchcommand.hxx>
+
+#include <AccessibilityIssue.hxx>
+#include <AccessibilityCheckStrings.hrc>
+#include <drawdoc.hxx>
+#include <edtwin.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <OnlineAccessibilityCheck.hxx>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svxdlg.hxx>
+
+#include <svx/svdview.hxx>
+#include <flyfrm.hxx>
+#include <txatbase.hxx>
+#include <txtfrm.hxx>
+
+namespace sw
+{
+AccessibilityIssue::AccessibilityIssue(sfx::AccessibilityIssueID eIssueID)
+ : sfx::AccessibilityIssue(eIssueID)
+ , m_eIssueObject(IssueObject::UNKNOWN)
+ , m_pDoc(nullptr)
+ , m_pNode(nullptr)
+ , m_pTextFootnote(nullptr)
+ , m_nStart(0)
+ , m_nEnd(0)
+{
+}
+
+void AccessibilityIssue::setIssueObject(IssueObject eIssueObject) { m_eIssueObject = eIssueObject; }
+
+void AccessibilityIssue::setDoc(SwDoc& rDoc) { m_pDoc = &rDoc; }
+
+void AccessibilityIssue::setObjectID(OUString const& rID) { m_sObjectID = rID; }
+
+bool AccessibilityIssue::canGotoIssue() const
+{
+ if (m_pDoc && m_eIssueObject != IssueObject::UNKNOWN
+ && m_eIssueObject != IssueObject::DOCUMENT_TITLE
+ && m_eIssueObject != IssueObject::DOCUMENT_BACKGROUND
+ && m_eIssueObject != IssueObject::LANGUAGE_NOT_SET)
+ return true;
+ return false;
+}
+
+void AccessibilityIssue::gotoIssue() const
+{
+ if (!m_pDoc)
+ return;
+
+ /* Copying the issueobject because the EnterSelFrameMode ends up calling some sidebar functions
+ that recreate the list of a11y issues and the AccessibilityIssue objects are stored by value in a vector
+ and the vector is being mutated there and so the instance is overwritten with something else. */
+ AccessibilityIssue TempIssueObject(*this);
+
+ SwWrtShell* pWrtShell = TempIssueObject.m_pDoc->GetDocShell()->GetWrtShell();
+
+ pWrtShell->AssureStdMode();
+
+ switch (TempIssueObject.m_eIssueObject)
+ {
+ case IssueObject::LINKED:
+ case IssueObject::GRAPHIC:
+ case IssueObject::OLE:
+ case IssueObject::TEXTFRAME:
+ {
+ bool bSelected = pWrtShell->GotoFly(TempIssueObject.m_sObjectID, FLYCNTTYPE_ALL, true);
+
+ // bring issue to attention
+ if (bSelected)
+ {
+ if (const SwFlyFrameFormat* pFlyFormat
+ = m_pDoc->FindFlyByName(TempIssueObject.m_sObjectID, SwNodeType::NONE))
+ {
+ if (SwFlyFrame* pFlyFrame
+ = SwIterator<SwFlyFrame, SwFormat>(*pFlyFormat).First())
+ {
+ pWrtShell->GetView().BringToAttention(pFlyFrame->getFrameArea().SVRect());
+ }
+ }
+ }
+
+ if (bSelected && pWrtShell->IsFrameSelected())
+ {
+ pWrtShell->HideCursor();
+ pWrtShell->EnterSelFrameMode();
+ }
+
+ if (!bSelected && TempIssueObject.m_eIssueObject == IssueObject::TEXTFRAME)
+ {
+ pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
+
+ // bring issue to attention
+ if (SdrPage* pPage
+ = pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+ {
+ if (SdrObject* pObj = pPage->GetObjByName(TempIssueObject.m_sObjectID))
+ {
+ pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+ }
+ }
+ }
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::SHAPE:
+ {
+ if (pWrtShell->IsFrameSelected())
+ pWrtShell->LeaveSelFrameMode();
+ pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
+
+ // bring issue to attention
+ if (SdrPage* pPage
+ = pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+ {
+ if (SdrObject* pObj = pPage->GetObjByName(TempIssueObject.m_sObjectID))
+ {
+ pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::FORM:
+ {
+ bool bIsDesignMode = pWrtShell->GetView().GetFormShell()->IsDesignMode();
+ if (bIsDesignMode || (!bIsDesignMode && pWrtShell->WarnSwitchToDesignModeDialog()))
+ {
+ if (!bIsDesignMode)
+ pWrtShell->GetView().GetFormShell()->SetDesignMode(true);
+ pWrtShell->GotoDrawingObject(TempIssueObject.m_sObjectID);
+
+ // bring issue to attention
+ if (SdrPage* pPage
+ = pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0))
+ {
+ if (SdrObject* pObj = pPage->GetObjByName(TempIssueObject.m_sObjectID))
+ {
+ pWrtShell->GetView().BringToAttention(pObj->GetLogicRect());
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ }
+ break;
+ case IssueObject::TABLE:
+ {
+ pWrtShell->GotoTable(TempIssueObject.m_sObjectID);
+
+ // bring issue to attention
+ if (SwTable* pTmpTable = SwTable::FindTable(
+ TempIssueObject.m_pDoc->FindTableFormatByName(TempIssueObject.m_sObjectID)))
+ {
+ if (SwTableNode* pTableNode = pTmpTable->GetTableNode())
+ {
+ pWrtShell->GetView().BringToAttention(pTableNode);
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::TEXT:
+ {
+ SwContentNode* pContentNode = TempIssueObject.m_pNode->GetContentNode();
+ SwPosition aPoint(*pContentNode, TempIssueObject.m_nStart);
+ SwPosition aMark(*pContentNode, TempIssueObject.m_nEnd);
+ pWrtShell->StartAllAction();
+ SwPaM* pPaM = pWrtShell->GetCursor();
+ *pPaM->GetPoint() = aPoint;
+ pPaM->SetMark();
+ *pPaM->GetMark() = aMark;
+ pWrtShell->EndAllAction();
+
+ // bring issue to attention
+ pWrtShell->GetView().BringToAttention(pContentNode);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::FOOTENDNOTE:
+ {
+ if (TempIssueObject.m_pTextFootnote)
+ {
+ pWrtShell->GotoFootnoteAnchor(*TempIssueObject.m_pTextFootnote);
+
+ // bring issue to attention
+ const SwTextNode& rTextNode = TempIssueObject.m_pTextFootnote->GetTextNode();
+ if (SwTextFrame* pFrame
+ = static_cast<SwTextFrame*>(rTextNode.getLayoutFrame(pWrtShell->GetLayout())))
+ {
+ auto nStart = TempIssueObject.m_pTextFootnote->GetStart();
+ auto nEnd = nStart + 1;
+ SwPosition aStartPos(rTextNode, nStart), aEndPos(rTextNode, nEnd);
+ SwRect aStartCharRect, aEndCharRect;
+ pFrame->GetCharRect(aStartCharRect, aStartPos);
+ pFrame->GetCharRect(aEndCharRect, aEndPos);
+ tools::Rectangle aRect(aStartCharRect.Left() - 50, aStartCharRect.Top(),
+ aEndCharRect.Right() + 50, aStartCharRect.Bottom());
+ pWrtShell->GetView().BringToAttention(aRect);
+ }
+ }
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ default:
+ break;
+ }
+ pWrtShell->GetView().GetEditWin().GrabFocus();
+}
+
+bool AccessibilityIssue::canQuickFixIssue() const
+{
+ return m_eIssueObject == IssueObject::GRAPHIC || m_eIssueObject == IssueObject::OLE
+ || m_eIssueObject == IssueObject::SHAPE || m_eIssueObject == IssueObject::FORM
+ || m_eIssueObject == IssueObject::DOCUMENT_TITLE
+ || m_eIssueObject == IssueObject::DOCUMENT_BACKGROUND
+ || m_eIssueObject == IssueObject::LANGUAGE_NOT_SET;
+}
+
+void AccessibilityIssue::quickFixIssue() const
+{
+ if (!m_pDoc)
+ return;
+
+ if (canGotoIssue())
+ gotoIssue();
+
+ switch (m_eIssueObject)
+ {
+ case IssueObject::GRAPHIC:
+ case IssueObject::OLE:
+ {
+ SwFlyFrameFormat* pFlyFormat
+ = const_cast<SwFlyFrameFormat*>(m_pDoc->FindFlyByName(m_sObjectID));
+ if (pFlyFormat)
+ {
+ OUString aDescription(pFlyFormat->GetObjDescription());
+ OUString aTitle(pFlyFormat->GetObjTitle());
+ bool isDecorative(pFlyFormat->IsDecorative());
+
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg(
+ pFact->CreateSvxObjectTitleDescDialog(pWrtShell->GetView().GetFrameWeld(),
+ aTitle, aDescription, isDecorative));
+
+ if (pDlg->Execute() == RET_OK)
+ {
+ pDlg->GetTitle(aTitle);
+ pDlg->GetDescription(aDescription);
+ pDlg->IsDecorative(isDecorative);
+
+ m_pDoc->SetFlyFrameTitle(*pFlyFormat, aTitle);
+ m_pDoc->SetFlyFrameDescription(*pFlyFormat, aDescription);
+ m_pDoc->SetFlyFrameDecorative(*pFlyFormat, isDecorative);
+
+ pWrtShell->SetModified();
+ }
+ }
+ }
+ break;
+ case IssueObject::SHAPE:
+ case IssueObject::FORM:
+ {
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ auto pPage = pWrtShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
+ SdrObject* pObj = pPage->GetObjByName(m_sObjectID);
+ if (pObj)
+ {
+ OUString aTitle(pObj->GetTitle());
+ OUString aDescription(pObj->GetDescription());
+ bool isDecorative(pObj->IsDecorative());
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxObjectTitleDescDialog> pDlg(
+ pFact->CreateSvxObjectTitleDescDialog(pWrtShell->GetView().GetFrameWeld(),
+ aTitle, aDescription, isDecorative));
+
+ if (RET_OK == pDlg->Execute())
+ {
+ pDlg->GetTitle(aTitle);
+ pDlg->GetDescription(aDescription);
+ pDlg->IsDecorative(isDecorative);
+
+ pObj->SetTitle(aTitle);
+ pObj->SetDescription(aDescription);
+ pObj->SetDecorative(isDecorative);
+
+ pWrtShell->SetModified();
+ }
+ }
+ }
+ break;
+ case IssueObject::DOCUMENT_TITLE:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ ScopedVclPtr<AbstractSvxNameDialog> aNameDialog(pFact->CreateSvxNameDialog(
+ pWrtShell->GetView().GetFrameWeld(), OUString(),
+ SwResId(STR_DOCUMENT_TITLE_DLG_DESC), SwResId(STR_DOCUMENT_TITLE_DLG_TITLE)));
+ if (aNameDialog->Execute() == RET_OK)
+ {
+ SwDocShell* pShell = m_pDoc->GetDocShell();
+ if (!pShell)
+ return;
+
+ const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pShell->GetModel(), uno::UNO_QUERY_THROW);
+ const uno::Reference<document::XDocumentProperties> xDocumentProperties(
+ xDPS->getDocumentProperties());
+ OUString sName;
+ aNameDialog->GetName(sName);
+ xDocumentProperties->setTitle(sName);
+
+ m_pDoc->getOnlineAccessibilityCheck()->resetAndQueueDocumentLevel();
+ }
+ }
+ break;
+ case IssueObject::DOCUMENT_BACKGROUND:
+ {
+ uno::Reference<frame::XModel> xModel(m_pDoc->GetDocShell()->GetModel(),
+ uno::UNO_QUERY_THROW);
+
+ comphelper::dispatchCommand(".uno:PageAreaDialog",
+ xModel->getCurrentController()->getFrame(), {});
+ }
+ break;
+ case IssueObject::LANGUAGE_NOT_SET:
+ {
+ uno::Reference<frame::XModel> xModel(m_pDoc->GetDocShell()->GetModel(),
+ uno::UNO_QUERY_THROW);
+
+ if (m_sObjectID.isEmpty())
+ {
+ // open the dialog "Tools/Options/Languages and Locales - General"
+ uno::Sequence<beans::PropertyValue> aArgs{ comphelper::makePropertyValue(
+ "Language", OUString("*")) };
+
+ comphelper::dispatchCommand(".uno:LanguageStatus",
+ xModel->getCurrentController()->getFrame(), aArgs);
+ }
+ else
+ {
+ uno::Sequence<beans::PropertyValue> aArgs{
+ comphelper::makePropertyValue("Param", m_sObjectID),
+ comphelper::makePropertyValue("Family", sal_Int16(SfxStyleFamily::Para))
+ };
+
+ comphelper::dispatchCommand(".uno:EditStyleFont",
+ xModel->getCurrentController()->getFrame(), aArgs);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (m_pNode)
+ m_pDoc->getOnlineAccessibilityCheck()->resetAndQueue(m_pNode);
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccell.cxx b/sw/source/core/access/acccell.cxx
new file mode 100644
index 0000000000..8b15fa0955
--- /dev/null
+++ b/sw/source/core/access/acccell.cxx
@@ -0,0 +1,454 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <swtable.hxx>
+#include <crsrsh.hxx>
+#include <viscrs.hxx>
+#include "accfrmobj.hxx"
+#include "accfrmobjslist.hxx"
+#include <frmfmt.hxx>
+#include <cellatr.hxx>
+#include <accmap.hxx>
+#include "acccell.hxx"
+
+#include <cfloat>
+#include <string_view>
+
+#include <editeng/brushitem.hxx>
+#include <swatrset.hxx>
+#include <frmatr.hxx>
+#include "acctable.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace sw::access;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleCellView";
+
+bool SwAccessibleCell::IsSelected()
+{
+ bool bRet = false;
+
+ assert(GetMap());
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
+ {
+ if( pCSh->IsTableMode() )
+ {
+ const SwCellFrame *pCFrame =
+ static_cast< const SwCellFrame * >( GetFrame() );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ SwSelBoxes const& rBoxes(pCSh->GetTableCursor()->GetSelectedBoxes());
+ bRet = rBoxes.find(pBox) != rBoxes.end();
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleCell::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // SELECTABLE
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( dynamic_cast<const SwCursorShell*>( pVSh) != nullptr )
+ rStateSet |= AccessibleStateType::SELECTABLE;
+ //Add resizable state to table cell.
+ rStateSet |= AccessibleStateType::RESIZABLE;
+
+ if (IsDisposing()) // tdf#135098
+ return;
+
+ // SELECTED
+ if( IsSelected() )
+ {
+ rStateSet |= AccessibleStateType::SELECTED;
+ SAL_WARN_IF(!m_bIsSelected, "sw.a11y", "bSelected out of sync");
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+}
+
+SwAccessibleCell::SwAccessibleCell(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwCellFrame *pCellFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::TABLE_CELL, pCellFrame )
+ , m_aSelectionHelper( *this )
+ , m_bIsSelected( false )
+{
+ OUString sBoxName( pCellFrame->GetTabBox()->GetName() );
+ SetName( sBoxName );
+
+ m_bIsSelected = IsSelected();
+
+ css::uno::Reference<css::accessibility::XAccessible> xTableReference(
+ getAccessibleParentImpl());
+ css::uno::Reference<css::accessibility::XAccessibleContext> xContextTable(
+ xTableReference, css::uno::UNO_QUERY);
+ SAL_WARN_IF(
+ (!xContextTable.is()
+ || xContextTable->getAccessibleRole() != AccessibleRole::TABLE),
+ "sw.a11y", "bad accessible context");
+ m_pAccTable = static_cast<SwAccessibleTable *>(xTableReference.get());
+}
+
+bool SwAccessibleCell::InvalidateMyCursorPos()
+{
+ bool bNew = IsSelected();
+ bool bOld;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOld = m_bIsSelected;
+ m_bIsSelected = bNew;
+ }
+ if( bNew )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ bool bChanged = bOld != bNew;
+ if( bChanged )
+ {
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bNew );
+ if (m_pAccTable.is())
+ {
+ m_pAccTable->AddSelectionCell(this,bNew);
+ }
+ }
+ return bChanged;
+}
+
+bool SwAccessibleCell::InvalidateChildrenCursorPos( const SwFrame *pFrame )
+{
+ bool bChanged = false;
+
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *GetMap() );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ if( rLower.IsAccessible( GetMap()->GetShell()->IsPreview() ) )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pLower, false ) );
+ if( xAccImpl.is() )
+ {
+ assert(xAccImpl->GetFrame()->IsCellFrame());
+ bChanged = static_cast< SwAccessibleCell *>(
+ xAccImpl.get() )->InvalidateMyCursorPos();
+ }
+ else
+ bChanged = true; // If the context is not know we
+ // don't know whether the selection
+ // changed or not.
+ }
+ else
+ {
+ // This is a box with sub rows.
+ bChanged |= InvalidateChildrenCursorPos( pLower );
+ }
+ }
+ ++aIter;
+ }
+
+ return bChanged;
+}
+
+void SwAccessibleCell::InvalidateCursorPos_()
+{
+ if (IsSelected())
+ {
+ const SwAccessibleChild aChild( GetChild( *(GetMap()), 0 ) );
+ if( aChild.IsValid() && aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl( GetMap()->GetContextImpl( aChild.GetSwFrame()) );
+ if (xChildImpl.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.NewValue <<= AccessibleStateType::FOCUSED;
+ xChildImpl->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ assert(pParent->IsTabFrame());
+ const SwTabFrame *pTabFrame = static_cast< const SwTabFrame * >( pParent );
+ if( pTabFrame->IsFollow() )
+ pTabFrame = pTabFrame->FindMaster();
+
+ while( pTabFrame )
+ {
+ InvalidateChildrenCursorPos( pTabFrame );
+ pTabFrame = pTabFrame->GetFollow();
+ }
+ if (m_pAccTable.is())
+ {
+ m_pAccTable->FireSelectionEvent();
+ }
+}
+
+bool SwAccessibleCell::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+SwAccessibleCell::~SwAccessibleCell()
+{
+}
+
+OUString SAL_CALL SwAccessibleCell::getAccessibleDescription()
+{
+ return GetName();
+}
+
+OUString SAL_CALL SwAccessibleCell::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleCell::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleCell::getSupportedServiceNames()
+{
+ return { "com.sun.star.table.AccessibleCellView", sAccessibleServiceName };
+}
+
+void SwAccessibleCell::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ if( xAccImpl.is() )
+ xAccImpl->DisposeChild(SwAccessibleChild(GetFrame()), bRecursive, bCanSkipInvisible);
+ SwAccessibleContext::Dispose( bRecursive );
+}
+
+void SwAccessibleCell::InvalidatePosOrSize( const SwRect& rOldBox )
+{
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ if( xAccImpl.is() )
+ xAccImpl->InvalidateChildPosOrSize( SwAccessibleChild(GetFrame()), rOldBox );
+ SwAccessibleContext::InvalidatePosOrSize( rOldBox );
+}
+
+// XAccessibleInterface
+
+uno::Any SwAccessibleCell::queryInterface( const uno::Type& rType )
+{
+ if (rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Any aR;
+ aR <<= uno::Reference<XAccessibleExtendedAttributes>(this);
+ return aR;
+ }
+
+ if (rType == cppu::UnoType<XAccessibleSelection>::get())
+ {
+ uno::Any aR;
+ aR <<= uno::Reference<XAccessibleSelection>(this);
+ return aR;
+ }
+ if ( rType == ::cppu::UnoType<XAccessibleValue>::get() )
+ {
+ uno::Reference<XAccessibleValue> xValue = this;
+ uno::Any aRet;
+ aRet <<= xValue;
+ return aRet;
+ }
+ else
+ {
+ return SwAccessibleContext::queryInterface( rType );
+ }
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleCell::getTypes()
+{
+ return cppu::OTypeCollection(
+ ::cppu::UnoType<XAccessibleValue>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleValue
+
+SwFrameFormat* SwAccessibleCell::GetTableBoxFormat() const
+{
+ assert(GetFrame());
+ assert(GetFrame()->IsCellFrame());
+
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>( GetFrame() );
+ return pCellFrame->GetTabBox()->GetFrameFormat();
+}
+
+//Implement TableCell currentValue
+uno::Any SwAccessibleCell::getCurrentValue( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return uno::Any( GetTableBoxFormat()->GetTableBoxValue().GetValue() );
+}
+
+sal_Bool SwAccessibleCell::setCurrentValue( const uno::Any& aNumber )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ double fValue = 0;
+ bool bValid = (aNumber >>= fValue);
+ if( bValid )
+ {
+ SwTableBoxValue aValue( fValue );
+ GetTableBoxFormat()->SetFormatAttr( aValue );
+ }
+ return bValid;
+}
+
+uno::Any SwAccessibleCell::getMaximumValue( )
+{
+ return uno::Any(DBL_MAX);
+}
+
+uno::Any SwAccessibleCell::getMinimumValue( )
+{
+ return uno::Any(-DBL_MAX);
+}
+
+uno::Any SwAccessibleCell::getMinimumIncrement( )
+{
+ return uno::Any();
+}
+
+css::uno::Any SAL_CALL SwAccessibleCell::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ css::uno::Any strRet;
+ SwFrameFormat *pFrameFormat = GetTableBoxFormat();
+ assert(pFrameFormat);
+
+ const SwTableBoxFormula& tbl_formula = pFrameFormat->GetTableBoxFormula();
+
+ OUString strFormula = tbl_formula.GetFormula()
+ .replaceAll(u"\\", u"\\\\")
+ .replaceAll(u";", u"\\;")
+ .replaceAll(u"=", u"\\=")
+ .replaceAll(u",", u"\\,")
+ .replaceAll(u":", u"\\:");
+ OUString strFor = "Formula:" + strFormula + ";";
+ strRet <<= strFor;
+
+ return strRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleCell::getBackground()
+{
+ SolarMutexGuard g;
+
+ const SvxBrushItem &rBack = GetFrame()->GetAttrSet()->GetBackground();
+ Color crBack = rBack.GetColor();
+
+ if (COL_AUTO == crBack)
+ {
+ uno::Reference<XAccessible> xAccDoc = getAccessibleParent();
+ if (xAccDoc.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponentDoc(xAccDoc, uno::UNO_QUERY);
+ if (xComponentDoc.is())
+ {
+ crBack = Color(ColorTransparency, xComponentDoc->getBackground());
+ }
+ }
+ }
+ return sal_Int32(crBack);
+}
+
+// XAccessibleSelection
+void SwAccessibleCell::selectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ m_aSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleCell::isAccessibleChildSelected(
+ sal_Int64 nChildIndex )
+{
+ return m_aSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleCell::clearAccessibleSelection( )
+{
+}
+
+void SwAccessibleCell::selectAllAccessibleChildren( )
+{
+ m_aSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int64 SwAccessibleCell::getSelectedAccessibleChildCount( )
+{
+ return m_aSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleCell::getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ return m_aSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+void SwAccessibleCell::deselectAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ m_aSelectionHelper.deselectAccessibleChild(nSelectedChildIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccell.hxx b/sw/source/core/access/acccell.hxx
new file mode 100644
index 0000000000..5d607d4c45
--- /dev/null
+++ b/sw/source/core/access/acccell.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/XAccessibleValue.hpp>
+#include "accselectionhelper.hxx"
+
+class SwCellFrame;
+class SwAccessibleTable;
+class SwFrameFormat;
+
+class SwAccessibleCell : public SwAccessibleContext,
+ public css::accessibility::XAccessibleValue,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ // Implementation for XAccessibleSelection interface
+ SwAccessibleSelectionHelper m_aSelectionHelper;
+ bool m_bIsSelected; // protected by base class mutex
+
+ bool IsSelected();
+
+ bool InvalidateMyCursorPos();
+ bool InvalidateChildrenCursorPos( const SwFrame *pFrame );
+
+ rtl::Reference<SwAccessibleTable> m_pAccTable;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets SELECTABLE(1) and SELECTED(+)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+
+ virtual void InvalidateCursorPos_() override;
+
+ virtual ~SwAccessibleCell() override;
+
+public:
+ SwAccessibleCell(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwCellFrame *pCellFrame);
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual void InvalidatePosOrSize( const SwRect& rFrame ) override;
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleValue).
+
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleExtendedAttributes
+ css::uno::Any SAL_CALL getExtendedAttributes() override ;
+private:
+ SwFrameFormat* GetTableBoxFormat() const;
+
+public:
+ // XAccessibleValue
+ virtual css::uno::Any SAL_CALL getCurrentValue( ) override;
+ virtual sal_Bool SAL_CALL setCurrentValue( const css::uno::Any& aNumber ) override;
+ virtual css::uno::Any SAL_CALL getMaximumValue( ) override;
+ virtual css::uno::Any SAL_CALL getMinimumValue( ) override;
+ virtual css::uno::Any SAL_CALL getMinimumIncrement( ) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild( sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int64 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccontext.cxx b/sw/source/core/access/acccontext.cxx
new file mode 100644
index 0000000000..46c4f4a825
--- /dev/null
+++ b/sw/source/core/access/acccontext.cxx
@@ -0,0 +1,1529 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/window.hxx>
+#include <swtypes.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <viewsh.hxx>
+#include <crsrsh.hxx>
+#include <fesh.hxx>
+#include <wrtsh.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <dflyobj.hxx>
+#include <pam.hxx>
+#include <accmap.hxx>
+#include "accfrmobjslist.hxx"
+#include "acccontext.hxx"
+#include <svx/AccessibleShape.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <PostItMgr.hxx>
+
+using namespace sw::access;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+void SwAccessibleContext::InitStates()
+{
+ m_isShowingState = GetMap() && IsShowing( *(GetMap()) );
+
+ SwViewShell *pVSh = GetMap()->GetShell();
+ m_isEditableState = pVSh && IsEditable( pVSh );
+ m_isOpaqueState = pVSh && IsOpaque( pVSh );
+ m_isDefuncState = false;
+}
+
+void SwAccessibleContext::SetParent( SwAccessibleContext *pParent )
+{
+ std::scoped_lock aGuard( m_Mutex );
+
+ uno::Reference < XAccessible > xParent( pParent );
+ m_xWeakParent = xParent;
+}
+
+uno::Reference< XAccessible > SwAccessibleContext::GetWeakParent() const
+{
+ std::scoped_lock aGuard( m_Mutex );
+
+ uno::Reference< XAccessible > xParent( m_xWeakParent );
+ return xParent;
+}
+
+vcl::Window *SwAccessibleContext::GetWindow()
+{
+ vcl::Window *pWin = nullptr;
+
+ if( GetMap() )
+ {
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( pVSh )
+ pWin = pVSh->GetWin();
+
+ OSL_ENSURE( pWin, "no window" );
+ }
+
+ return pWin;
+}
+
+// get SwViewShell from accessibility map, and cast to cursor shell
+SwCursorShell* SwAccessibleContext::GetCursorShell()
+{
+ SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
+ OSL_ENSURE( pViewShell, "no view shell" );
+ return dynamic_cast<SwCursorShell*>( pViewShell);
+}
+
+const SwCursorShell* SwAccessibleContext::GetCursorShell() const
+{
+ // just like non-const GetCursorShell
+ const SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
+ OSL_ENSURE( pViewShell, "no view shell" );
+ return dynamic_cast<const SwCursorShell*>( pViewShell);
+}
+
+namespace {
+
+enum class Action { NONE, SCROLLED, SCROLLED_WITHIN,
+ SCROLLED_IN, SCROLLED_OUT };
+
+}
+
+void SwAccessibleContext::ChildrenScrolled( const SwFrame *pFrame,
+ const SwRect& rOldVisArea )
+{
+ const SwRect& rNewVisArea = GetVisArea();
+ const bool bVisibleChildrenOnly = SwAccessibleChild( pFrame ).IsVisibleChildrenOnly();
+
+ const SwAccessibleChildSList aList( *pFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ while( aIter != aList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwRect aBox( rLower.GetBox( *(GetMap()) ) );
+ if( rLower.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ Action eAction = Action::NONE;
+ if( aBox.Overlaps( rNewVisArea ) )
+ {
+ if( aBox.Overlaps( rOldVisArea ) )
+ {
+ eAction = Action::SCROLLED_WITHIN;
+ }
+ else
+ {
+ if ( bVisibleChildrenOnly &&
+ !rLower.AlwaysIncludeAsChild() )
+ {
+ eAction = Action::SCROLLED_IN;
+ }
+ else
+ {
+ eAction = Action::SCROLLED;
+ }
+ }
+ }
+ else if( aBox.Overlaps( rOldVisArea ) )
+ {
+ if ( bVisibleChildrenOnly &&
+ !rLower.AlwaysIncludeAsChild() )
+ {
+ eAction = Action::SCROLLED_OUT;
+ }
+ else
+ {
+ eAction = Action::SCROLLED;
+ }
+ }
+ else if( !bVisibleChildrenOnly ||
+ rLower.AlwaysIncludeAsChild() )
+ {
+ // This wouldn't be required if the SwAccessibleFrame,
+ // wouldn't know about the visible area.
+ eAction = Action::SCROLLED;
+ }
+ if( Action::NONE != eAction )
+ {
+ if ( rLower.GetSwFrame() )
+ {
+ OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
+ const SwFrame* pLower( rLower.GetSwFrame() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( pLower );
+ if( xAccImpl.is() )
+ {
+ switch( eAction )
+ {
+ case Action::SCROLLED:
+ xAccImpl->Scrolled( rOldVisArea );
+ break;
+ case Action::SCROLLED_WITHIN:
+ xAccImpl->ScrolledWithin( rOldVisArea );
+ break;
+ case Action::SCROLLED_IN:
+ xAccImpl->ScrolledIn();
+ break;
+ case Action::SCROLLED_OUT:
+ xAccImpl->ScrolledOut( rOldVisArea );
+ break;
+ case Action::NONE:
+ break;
+ }
+ }
+ else
+ {
+ ChildrenScrolled( pLower, rOldVisArea );
+ }
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rLower.GetDrawObject(),
+ this );
+ if( xAccImpl.is() )
+ {
+ switch( eAction )
+ {
+ case Action::SCROLLED:
+ case Action::SCROLLED_WITHIN:
+ xAccImpl->ViewForwarderChanged();
+ break;
+ case Action::SCROLLED_IN:
+ ScrolledInShape( xAccImpl.get() );
+ break;
+ case Action::SCROLLED_OUT:
+ {
+ xAccImpl->ViewForwarderChanged();
+ // this DisposeShape call was removed by
+ // IAccessibility2 implementation
+ // without giving any reason why
+ DisposeShape( rLower.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case Action::NONE:
+ break;
+ }
+ }
+ }
+ else if ( rLower.GetWindow() )
+ {
+ // nothing to do - as such children are always included as children.
+ OSL_ENSURE( rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
+ }
+ }
+ }
+ else if ( rLower.GetSwFrame() &&
+ ( !bVisibleChildrenOnly ||
+ aBox.Overlaps( rOldVisArea ) ||
+ aBox.Overlaps( rNewVisArea ) ) )
+ {
+ // There are no unaccessible SdrObjects that need to be notified
+ ChildrenScrolled( rLower.GetSwFrame(), rOldVisArea );
+ }
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::Scrolled( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ bool bIsOldShowingState;
+ bool bIsNewShowingState = IsShowing( *(GetMap()) );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldShowingState = m_isShowingState;
+ m_isShowingState = bIsNewShowingState;
+ }
+
+ if( bIsOldShowingState != bIsNewShowingState )
+ FireStateChangedEvent( AccessibleStateType::SHOWING,
+ bIsNewShowingState );
+}
+
+void SwAccessibleContext::ScrolledWithin( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ FireVisibleDataEvent();
+}
+
+void SwAccessibleContext::ScrolledIn()
+{
+ // This accessible should be freshly created, because it
+ // was not visible before. Therefore, its visible area must already
+ // reflect the scrolling.
+ OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
+ "Visible area of child is wrong. Did it exist already?" );
+
+ // Send child event at parent. That's all we have to do here.
+ const SwFrame* pParent = GetParent();
+ ::rtl::Reference< SwAccessibleContext > xParentImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ uno::Reference < XAccessibleContext > xThis( this );
+ if( !xParentImpl.is() )
+ return;
+
+ SetParent( xParentImpl.get() );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xThis;
+ aEvent.IndexHint = -1;
+
+ xParentImpl->FireAccessibleEvent( aEvent );
+
+ if( HasCursor() )
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ {
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
+ }
+ }
+}
+
+void SwAccessibleContext::ScrolledOut( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ // First of all, update the children. That's required to dispose
+ // all children that are existing only if they are visible. They
+ // are not disposed by the recursive Dispose call that follows later on,
+ // because this call will only dispose children that are in the
+ // new visible area. The children we want to dispose however are in the
+ // old visible area all.
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ // Broadcast a state changed event for the showing state.
+ // It might be that the child is freshly created just to send
+ // the child event. In this case no listener will exist.
+ FireStateChangedEvent( AccessibleStateType::SHOWING, false );
+
+ // this Dispose call was removed by IAccessibility2 implementation
+ // without giving any reason why - without it we get stale
+ // entries in SwAccessibleMap::mpFrameMap.
+ Dispose(true);
+}
+
+// #i27301# - use new type definition for <_nStates>
+void SwAccessibleContext::InvalidateChildrenStates( const SwFrame* _pFrame,
+ AccessibleStates _nStates )
+{
+ const SwAccessibleChildSList aVisList( GetVisArea(), *_pFrame, *(GetMap()) );
+
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame* pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ if( rLower.IsAccessible( GetShell()->IsPreview() ) )
+ xAccImpl = GetMap()->GetContextImpl( pLower, false );
+ if( xAccImpl.is() )
+ xAccImpl->InvalidateStates( _nStates );
+ else
+ InvalidateChildrenStates( pLower, _nStates );
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ // TODO: SdrObjects
+ }
+ else if ( rLower.GetWindow() )
+ {
+ // nothing to do ?
+ }
+
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::DisposeChildren(const SwFrame *pFrame,
+ bool bRecursive,
+ bool bCanSkipInvisible)
+{
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame* pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ // tdf#117601 dispose the darn thing if it ever was accessible
+ ::rtl::Reference<SwAccessibleContext> xAccImpl = GetMap()->GetContextImpl(pLower, false);
+ if( xAccImpl.is() )
+ xAccImpl->Dispose( bRecursive );
+ else
+ {
+ // it's possible that the xAccImpl *does* exist with a
+ // ref-count of 0 and blocked in its dtor in another thread -
+ // this call here could be from SwAccessibleMap dtor so
+ // remove it from any maps now!
+ GetMap()->RemoveContext(pLower);
+ // in this case the context will check with a weak_ptr
+ // that the map is still alive so it's not necessary
+ // to clear its m_pMap here.
+ if (bRecursive)
+ {
+ DisposeChildren(pLower, bRecursive, bCanSkipInvisible);
+ }
+ }
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl(
+ GetMap()->GetContextImpl( rLower.GetDrawObject(),
+ this, false ) );
+ if( xAccImpl.is() )
+ DisposeShape( rLower.GetDrawObject(), xAccImpl.get() );
+ }
+ else if ( rLower.GetWindow() )
+ {
+ DisposeChild(rLower, false, bCanSkipInvisible);
+ }
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::InvalidateContent_( bool )
+{
+}
+
+void SwAccessibleContext::InvalidateCursorPos_()
+{
+}
+
+void SwAccessibleContext::InvalidateFocus_()
+{
+}
+
+void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject& rEvent )
+{
+ if( !GetFrame() )
+ {
+ SAL_INFO("sw.a11y", "SwAccessibleContext::FireAccessibleEvent called for already disposed frame?");
+ return;
+ }
+
+ if( !rEvent.Source.is() )
+ {
+ uno::Reference < XAccessibleContext > xThis( this );
+ rEvent.Source = xThis;
+ }
+
+ if (m_nClientId)
+ comphelper::AccessibleEventNotifier::addEvent( m_nClientId, rEvent );
+}
+
+void SwAccessibleContext::FireVisibleDataEvent()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+void SwAccessibleContext::FireStateChangedEvent( sal_Int64 nState,
+ bool bNewState )
+{
+ AccessibleEventObject aEvent;
+
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ if( bNewState )
+ aEvent.NewValue <<= nState;
+ else
+ aEvent.OldValue <<= nState;
+
+ FireAccessibleEvent( aEvent );
+}
+
+void SwAccessibleContext::GetStates( sal_Int64& rStateSet )
+{
+ SolarMutexGuard aGuard;
+
+ // SHOWING
+ if (m_isShowingState)
+ rStateSet |= AccessibleStateType::SHOWING;
+
+ // EDITABLE
+ if (m_isEditableState)
+ //Set editable state to graphic and other object when the document is editable
+ {
+ rStateSet |= AccessibleStateType::EDITABLE;
+ rStateSet |= AccessibleStateType::RESIZABLE;
+ rStateSet |= AccessibleStateType::MOVEABLE;
+ }
+ // ENABLED
+ rStateSet |= AccessibleStateType::ENABLED;
+
+ // OPAQUE
+ if (m_isOpaqueState)
+ rStateSet |= AccessibleStateType::OPAQUE;
+
+ // VISIBLE
+ rStateSet |= AccessibleStateType::VISIBLE;
+
+ if (m_isDefuncState)
+ rStateSet |= AccessibleStateType::DEFUNC;
+}
+
+bool SwAccessibleContext::IsEditableState()
+{
+ bool bRet;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bRet = m_isEditableState;
+ }
+
+ return bRet;
+}
+
+bool SwAccessibleContext::IsDisposed() const
+{
+ return !(GetFrame() && GetMap());
+}
+
+void SwAccessibleContext::ThrowIfDisposed()
+{
+ if (IsDisposed())
+ {
+ throw lang::DisposedException("object is nonfunctional",
+ getXWeak());
+ }
+}
+
+SwAccessibleContext::SwAccessibleContext(std::shared_ptr<SwAccessibleMap> const& pMap,
+ sal_Int16 const nRole,
+ const SwFrame *pF )
+ : SwAccessibleFrame( pMap->GetVisArea(), pF,
+ pMap->GetShell()->IsPreview() )
+ , m_pMap(pMap.get())
+ , m_wMap(pMap)
+ , m_nClientId(0)
+ , m_nRole(nRole)
+ , m_isDisposing( false )
+ , m_isRegisteredAtAccessibleMap( true )
+ , m_isSelectedInDoc(false)
+{
+ InitStates();
+}
+
+SwAccessibleContext::~SwAccessibleContext()
+{
+ // must have for 2 reasons: 2. as long as this thread has SolarMutex
+ // another thread cannot destroy the SwAccessibleMap so our temporary
+ // taking a hard ref to SwAccessibleMap won't delay its destruction
+ SolarMutexGuard aGuard;
+ // must check with weak_ptr that m_pMap is still alive
+ std::shared_ptr<SwAccessibleMap> pMap(m_wMap.lock());
+ if (m_isRegisteredAtAccessibleMap && GetFrame() && pMap)
+ {
+ pMap->RemoveContext( GetFrame() );
+ }
+}
+
+uno::Reference< XAccessibleContext > SAL_CALL
+ SwAccessibleContext::getAccessibleContext()
+{
+ uno::Reference < XAccessibleContext > xRet( this );
+ return xRet;
+}
+
+sal_Int64 SAL_CALL SwAccessibleContext::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return m_isDisposing ? 0 : GetChildCount( *(GetMap()) );
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleContext::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if (nIndex < 0 || nIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ const SwAccessibleChild aChild( GetChild( *(GetMap()), nIndex ) );
+ if( !aChild.IsValid() )
+ {
+ uno::Reference < XAccessibleContext > xThis( this );
+ lang::IndexOutOfBoundsException aExcept(
+ "index out of bounds",
+ xThis );
+ throw aExcept;
+ }
+
+ uno::Reference< XAccessible > xChild;
+ if( aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ GetMap()->GetContextImpl( aChild.GetSwFrame(), !m_isDisposing ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( this );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ GetMap()->GetContextImpl( aChild.GetDrawObject(),
+ this, !m_isDisposing) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ else if ( aChild.GetWindow() )
+ {
+ xChild = aChild.GetWindow()->GetAccessible();
+ }
+
+ return xChild;
+}
+
+css::uno::Sequence<uno::Reference<XAccessible>> SAL_CALL
+ SwAccessibleContext::getAccessibleChildren()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ std::list< sw::access::SwAccessibleChild > aChildren;
+ GetChildren( *GetMap(), aChildren );
+
+ std::vector<uno::Reference<XAccessible>> aRet;
+ aRet.reserve(aChildren.size());
+ for (const auto & rSwChild : aChildren)
+ {
+ uno::Reference< XAccessible > xChild;
+ if( rSwChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ GetMap()->GetContextImpl( rSwChild.GetSwFrame(), !m_isDisposing ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( this );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( rSwChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ GetMap()->GetContextImpl( rSwChild.GetDrawObject(),
+ this, !m_isDisposing) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ else if ( rSwChild.GetWindow() )
+ {
+ xChild = rSwChild.GetWindow()->GetAccessible();
+ }
+ aRet.push_back(xChild);
+ }
+ return comphelper::containerToSequence(aRet);
+}
+
+uno::Reference< XAccessible> SwAccessibleContext::getAccessibleParentImpl()
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrame *pUpper = GetParent();
+ OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
+
+ uno::Reference< XAccessible > xAcc;
+ if( pUpper )
+ xAcc = GetMap()->GetContext( pUpper, !m_isDisposing );
+
+ OSL_ENSURE( xAcc.is() || m_isDisposing, "no parent found" );
+
+ // Remember the parent as weak ref.
+ {
+ std::scoped_lock aWeakParentGuard( m_Mutex );
+ m_xWeakParent = xAcc;
+ }
+
+ return xAcc;
+}
+
+uno::Reference< XAccessible> SAL_CALL SwAccessibleContext::getAccessibleParent()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return getAccessibleParentImpl();
+}
+
+sal_Int64 SAL_CALL SwAccessibleContext::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pUpper = GetParent();
+ OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
+
+ sal_Int64 nIndex = -1;
+ if( pUpper )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl(pUpper, !m_isDisposing) );
+ OSL_ENSURE( xAccImpl.is() || m_isDisposing, "no parent found" );
+ if( xAccImpl.is() )
+ nIndex = xAccImpl->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
+ }
+
+ return nIndex;
+}
+
+sal_Int16 SAL_CALL SwAccessibleContext::getAccessibleRole()
+{
+ return m_nRole;
+}
+
+OUString SAL_CALL SwAccessibleContext::getAccessibleName()
+{
+ return m_sName;
+}
+
+uno::Reference< XAccessibleRelationSet> SAL_CALL
+ SwAccessibleContext::getAccessibleRelationSet()
+{
+ // by default there are no relations
+ uno::Reference< XAccessibleRelationSet> xRet( new utl::AccessibleRelationSetHelper() );
+ return xRet;
+}
+
+sal_Int64 SAL_CALL SwAccessibleContext::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int64 nStateSet = 0;
+
+ if (m_isSelectedInDoc)
+ nStateSet |= AccessibleStateType::SELECTED;
+
+ GetStates( nStateSet );
+
+ return nStateSet;
+}
+
+lang::Locale SAL_CALL SwAccessibleContext::getLocale()
+{
+ SolarMutexGuard aGuard;
+
+ lang::Locale aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
+ return aLoc;
+}
+
+void SAL_CALL SwAccessibleContext::addAccessibleEventListener(
+ const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ if (!m_nClientId)
+ m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener );
+ }
+}
+
+void SAL_CALL SwAccessibleContext::removeAccessibleEventListener(
+ const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (!(xListener.is() && m_nClientId))
+ return;
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ comphelper::AccessibleEventNotifier::revokeClient( m_nClientId );
+ m_nClientId = 0;
+ }
+}
+
+static bool lcl_PointInRectangle(const awt::Point & aPoint,
+ const awt::Rectangle & aRect)
+{
+ tools::Long nDiffX = aPoint.X - aRect.X;
+ tools::Long nDiffY = aPoint.Y - aRect.Y;
+
+ return
+ nDiffX >= 0 && nDiffX < aRect.Width && nDiffY >= 0 &&
+ nDiffY < aRect.Height;
+
+}
+
+sal_Bool SAL_CALL SwAccessibleContext::containsPoint(
+ const awt::Point& aPoint )
+{
+ awt::Rectangle aPixBounds = getBoundsImpl(true);
+ aPixBounds.X = 0;
+ aPixBounds.Y = 0;
+
+ return lcl_PointInRectangle(aPoint, aPixBounds);
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleContext::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ uno::Reference< XAccessible > xAcc;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to parent
+ if( !GetFrame()->IsRootFrame() )
+ {
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
+ Point aPixPos( GetMap()->CoreToPixel( aLogBounds ).TopLeft() );
+ aPixPoint.setX(aPixPoint.getX() + aPixPos.getX());
+ aPixPoint.setY(aPixPoint.getY() + aPixPos.getY());
+ }
+
+ const SwAccessibleChild aChild( GetChildAtPixel( aPixPoint, *(GetMap()) ) );
+ if( aChild.GetSwFrame() )
+ {
+ xAcc = GetMap()->GetContext( aChild.GetSwFrame() );
+ }
+ else if( aChild.GetDrawObject() )
+ {
+ xAcc = GetMap()->GetContext( aChild.GetDrawObject(), this );
+ }
+ else if ( aChild.GetWindow() )
+ {
+ xAcc = aChild.GetWindow()->GetAccessible();
+ }
+
+ return xAcc;
+}
+
+/**
+ Get bounding box.
+
+ There are two modes.
+
+ - relative
+
+ Return bounding box relative to parent if parent is no root
+ frame. Otherwise return the absolute bounding box.
+
+ - absolute
+
+ Return the absolute bounding box.
+
+ @param bRelative
+ true: Use relative mode.
+ false: Use absolute mode.
+*/
+awt::Rectangle SwAccessibleContext::getBoundsImpl(bool bRelative)
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pParent = GetParent();
+ OSL_ENSURE( pParent, "no Parent found" );
+ vcl::Window *pWin = GetWindow();
+
+ if (!pParent)
+ {
+ throw uno::RuntimeException("no Parent", getXWeak());
+ }
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
+ tools::Rectangle aPixBounds( 0, 0, 0, 0 );
+ if( GetFrame()->IsPageFrame() &&
+ static_cast < const SwPageFrame * >( GetFrame() )->IsEmptyPage() )
+ {
+ OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
+ if( GetShell()->IsPreview() )
+ {
+ // adjust method call <GetMap()->GetPreviewPageSize()>
+ sal_uInt16 nPageNum =
+ static_cast < const SwPageFrame * >( GetFrame() )->GetPhyPageNum();
+ aLogBounds.SSize( GetMap()->GetPreviewPageSize( nPageNum ) );
+ }
+ }
+ if( !aLogBounds.IsEmpty() )
+ {
+ aPixBounds = GetMap()->CoreToPixel( aLogBounds );
+ if( !pParent->IsRootFrame() && bRelative)
+ {
+ SwRect aParentLogBounds( GetBounds( *(GetMap()), pParent ) ); // twip rel to doc root
+ Point aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds ).TopLeft() );
+ aPixBounds.Move( -aParentPixPos.getX(), -aParentPixPos.getY() );
+ }
+ }
+
+ awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
+ aPixBounds.GetWidth(), aPixBounds.GetHeight() );
+
+ return aBox;
+}
+
+awt::Rectangle SAL_CALL SwAccessibleContext::getBounds()
+{
+ return getBoundsImpl(true);
+}
+
+awt::Point SAL_CALL SwAccessibleContext::getLocation()
+{
+ awt::Rectangle aRect = getBoundsImpl(true);
+ awt::Point aPoint(aRect.X, aRect.Y);
+
+ return aPoint;
+}
+
+awt::Point SAL_CALL SwAccessibleContext::getLocationOnScreen()
+{
+ awt::Rectangle aRect = getBoundsImpl(false);
+
+ Point aPixPos(aRect.X, aRect.Y);
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ AbsoluteScreenPixelPoint aPixPosAbs = pWin->OutputToAbsoluteScreenPixel(aPixPos);
+ awt::Point aPoint(aPixPosAbs.getX(), aPixPosAbs.getY());
+
+ return aPoint;
+}
+
+awt::Size SAL_CALL SwAccessibleContext::getSize()
+{
+ awt::Rectangle aRect = getBoundsImpl(false);
+ awt::Size aSize( aRect.Width, aRect.Height );
+
+ return aSize;
+}
+
+void SAL_CALL SwAccessibleContext::grabFocus()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( GetFrame()->IsFlyFrame() )
+ {
+ const SdrObject *pObj =
+ static_cast < const SwFlyFrame * >( GetFrame() )->GetVirtDrawObj();
+ if( pObj )
+ Select( const_cast < SdrObject * >( pObj ), false );
+ }
+ else
+ {
+ const SwContentFrame *pCFrame = nullptr;
+ if( GetFrame()->IsContentFrame() )
+ pCFrame = static_cast< const SwContentFrame * >( GetFrame() );
+ else if( GetFrame()->IsLayoutFrame() )
+ pCFrame = static_cast< const SwLayoutFrame * >( GetFrame() )->ContainsContent();
+
+ if( pCFrame && pCFrame->IsTextFrame() )
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pCFrame );
+ const SwTextNode *pTextNd = pTextFrame->GetTextNodeFirst();
+ assert(pTextNd); // can it actually be null? probably not=>simplify
+ if( pTextNd )
+ {
+ // create pam for selection
+ SwPosition const aStartPos(pTextFrame->MapViewToModelPos(pTextFrame->GetOffset()));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ Select( aPaM );
+ }
+ }
+ }
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getForeground()
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getBackground()
+{
+ return sal_Int32(COL_WHITE);
+}
+
+sal_Bool SAL_CALL SwAccessibleContext::supportsService (const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+void SwAccessibleContext::DisposeShape( const SdrObject *pObj,
+ ::accessibility::AccessibleShape *pAccImpl )
+{
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl( pAccImpl );
+ if( !xAccImpl.is() )
+ xAccImpl = GetMap()->GetContextImpl( pObj, this );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc( xAccImpl );
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ FireAccessibleEvent( aEvent );
+
+ GetMap()->RemoveContext( pObj );
+ xAccImpl->dispose();
+}
+
+void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl )
+{
+ if(nullptr == pAccImpl)
+ {
+ return ;
+ }
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc( pAccImpl );
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ FireAccessibleEvent( aEvent );
+
+ if( !pAccImpl->GetState( AccessibleStateType::FOCUSED ) )
+ return;
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ {
+ AccessibleEventObject aStateChangedEvent;
+ aStateChangedEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aStateChangedEvent.NewValue <<= AccessibleStateType::FOCUSED;
+ aStateChangedEvent.Source = xAcc;
+
+ FireAccessibleEvent( aStateChangedEvent );
+ }
+}
+
+void SwAccessibleContext::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
+ OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
+ "invalid visible area for dispose" );
+
+ m_isDisposing = true;
+
+ // dispose children
+ if( bRecursive )
+ DisposeChildren(GetFrame(), bRecursive, bCanSkipInvisible);
+
+ // get parent
+ uno::Reference< XAccessible > xParent( GetWeakParent() );
+ uno::Reference < XAccessibleContext > xThis( this );
+
+ // send child event at parent
+ if( xParent.is() )
+ {
+ SwAccessibleContext *pAcc = static_cast<SwAccessibleContext *>(xParent.get());
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xThis;
+ aEvent.IndexHint = -1;
+ pAcc->FireAccessibleEvent( aEvent );
+ }
+
+ // set defunc state (it's not required to broadcast a state changed
+ // event if the object is disposed afterwards)
+ {
+ std::scoped_lock aDefuncStateGuard( m_Mutex );
+ m_isDefuncState = true;
+ }
+
+ // broadcast dispose event
+ if (m_nClientId)
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this );
+ m_nClientId = 0;
+ }
+
+ RemoveFrameFromAccessibleMap();
+ ClearFrame();
+ m_pMap = nullptr;
+ m_wMap.reset();
+
+ m_isDisposing = false;
+}
+
+void SwAccessibleContext::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !bCanSkipInvisible ||
+ rChildFrameOrObj.AlwaysIncludeAsChild() ||
+ IsShowing( *(GetMap()), rChildFrameOrObj ) ||
+ !SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
+ {
+ // If the object could have existed before, then there is nothing to do,
+ // because no wrapper exists now and therefore no one is interested to
+ // get notified of the movement.
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame(), false );
+ if (xAccImpl)
+ xAccImpl->Dispose( bRecursive );
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this, false );
+ if (xAccImpl)
+ DisposeShape( rChildFrameOrObj.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc =
+ rChildFrameOrObj.GetWindow()->GetAccessible();
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ else if( bRecursive && rChildFrameOrObj.GetSwFrame() )
+ DisposeChildren(rChildFrameOrObj.GetSwFrame(), bRecursive, bCanSkipInvisible);
+}
+
+void SwAccessibleContext::InvalidatePosOrSize( const SwRect& )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( GetFrame() && !GetFrame()->getFrameArea().IsEmpty(), "context should have a size" );
+
+ bool bIsOldShowingState;
+ bool bIsNewShowingState = IsShowing( *(GetMap()) );
+ {
+ std::scoped_lock aShowingStateGuard( m_Mutex );
+ bIsOldShowingState = m_isShowingState;
+ m_isShowingState = bIsNewShowingState;
+ }
+
+ if( bIsOldShowingState != bIsNewShowingState )
+ {
+ FireStateChangedEvent( AccessibleStateType::SHOWING,
+ bIsNewShowingState );
+ }
+ else if( bIsNewShowingState )
+ {
+ // The frame stays visible -> broadcast event
+ FireVisibleDataEvent();
+ }
+
+ // note: InvalidatePosOrSize must call InvalidateContent_ so that
+ // SwAccessibleParagraph updates its portions, or dispose it
+ // (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
+ if( !bIsNewShowingState &&
+ SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
+ {
+ // this Dispose call was removed by IAccessibility2 implementation
+ // without giving any reason why - without it we get stale
+ // entries in SwAccessibleMap::mpFrameMap.
+ Dispose(true);
+ }
+ else
+ {
+ InvalidateContent_( true );
+ }
+}
+
+void SwAccessibleContext::InvalidateChildPosOrSize(
+ const SwAccessibleChild& rChildFrameOrObj,
+ const SwRect& rOldFrame )
+{
+ SolarMutexGuard aGuard;
+
+ // this happens during layout, e.g. when a page is deleted and next page's
+ // header/footer moves backward such an event is generated
+ SAL_INFO_IF(rChildFrameOrObj.GetSwFrame() &&
+ rChildFrameOrObj.GetSwFrame()->getFrameArea().IsEmpty(),
+ "sw.a11y", "child context should have a size");
+
+ if ( rChildFrameOrObj.AlwaysIncludeAsChild() )
+ {
+ // nothing to do;
+ return;
+ }
+
+ const bool bVisibleChildrenOnly = SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
+ const bool bNew = rOldFrame.IsEmpty() ||
+ ( rOldFrame.Left() == 0 && rOldFrame.Top() == 0 );
+ if( IsShowing( *(GetMap()), rChildFrameOrObj ) )
+ {
+ // If the object could have existed before, then there is nothing to do,
+ // because no wrapper exists now and therefore no one is interested to
+ // get notified of the movement.
+ if( bNew || (bVisibleChildrenOnly && !IsShowing( rOldFrame )) )
+ {
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ // The frame becomes visible. A child event must be send.
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
+ xAccImpl->ScrolledIn();
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this );
+ // #i37790#
+ if ( xAccImpl.is() )
+ {
+ ScrolledInShape( xAccImpl.get() );
+ }
+ else
+ {
+ OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
+ }
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= rChildFrameOrObj.GetWindow()->GetAccessible();
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ else
+ {
+ // If the frame was visible before, then a child event for the parent
+ // needs to be send. However, there is no wrapper existing, and so
+ // no notifications for grandchildren are required. If the are
+ // grandgrandchildren, they would be notified by the layout.
+ if( bVisibleChildrenOnly &&
+ !bNew && IsShowing( rOldFrame ) )
+ {
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
+ xAccImpl->SetParent( this );
+ xAccImpl->Dispose( true );
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this );
+ DisposeShape( rChildFrameOrObj.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
+ }
+ }
+ }
+}
+
+void SwAccessibleContext::InvalidateContent()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateContent_( false );
+}
+
+void SwAccessibleContext::InvalidateCursorPos()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateCursorPos_();
+}
+
+void SwAccessibleContext::InvalidateFocus()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateFocus_();
+}
+
+// #i27301# - use new type definition for <_nStates>
+void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates )
+{
+ if( !GetMap() )
+ return;
+
+ SwViewShell *pVSh = GetMap()->GetShell();
+ if( pVSh )
+ {
+ if( _nStates & AccessibleStates::EDITABLE )
+ {
+ bool bIsOldEditableState;
+ bool bIsNewEditableState = IsEditable( pVSh );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldEditableState = m_isEditableState;
+ m_isEditableState = bIsNewEditableState;
+ }
+
+ if( bIsOldEditableState != bIsNewEditableState )
+ FireStateChangedEvent( AccessibleStateType::EDITABLE,
+ bIsNewEditableState );
+ }
+ if( _nStates & AccessibleStates::OPAQUE )
+ {
+ bool bIsOldOpaqueState;
+ bool bIsNewOpaqueState = IsOpaque( pVSh );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldOpaqueState = m_isOpaqueState;
+ m_isOpaqueState = bIsNewOpaqueState;
+ }
+
+ if( bIsOldOpaqueState != bIsNewOpaqueState )
+ FireStateChangedEvent( AccessibleStateType::OPAQUE,
+ bIsNewOpaqueState );
+ }
+ }
+
+ InvalidateChildrenStates( GetFrame(), _nStates );
+}
+
+void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType )
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = nType;
+
+ FireAccessibleEvent( aEvent );
+}
+
+/** #i27301# - text selection has changed */
+void SwAccessibleContext::InvalidateTextSelection()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+/** #i88069# - attributes has changed */
+void SwAccessibleContext::InvalidateAttr()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_ATTRIBUTE_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+bool SwAccessibleContext::HasCursor()
+{
+ return false;
+}
+
+bool SwAccessibleContext::Select( SwPaM *pPaM, SdrObject *pObj,
+ bool bAdd )
+{
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( !pCursorShell )
+ return false;
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ // Get rid of activated OLE object
+ if( pFEShell )
+ pFEShell->FinishOLEObj();
+
+ SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(pCursorShell);
+
+ bool bRet = false;
+ if( pObj )
+ {
+ if( pFEShell )
+ {
+ sal_uInt8 nFlags = bAdd ? SW_ADD_SELECT : 0;
+ pFEShell->SelectObj( Point(), nFlags, pObj );
+ bRet = true;
+ }
+ }
+ else if( pPaM )
+ {
+ // Get rid of frame selection. If there is one, make text cursor
+ // visible again.
+ bool bCallShowCursor = false;
+ if( pFEShell && (pFEShell->IsFrameSelected() ||
+ pFEShell->IsObjSelected()) )
+ {
+ Point aPt( LONG_MIN, LONG_MIN );
+ pFEShell->SelectObj( aPt );
+ bCallShowCursor = true;
+ }
+ pCursorShell->KillPams();
+ if( pWrtShell && pPaM->HasMark() )
+ // We have to do this or SwWrtShell can't figure out that it needs
+ // to kill the selection later, when the user moves the cursor.
+ pWrtShell->SttSelect();
+ pCursorShell->SetSelection( *pPaM );
+ if( pPaM->HasMark() && *pPaM->GetPoint() == *pPaM->GetMark())
+ // Setting a "Selection" that starts and ends at the same spot
+ // should remove the selection rather than create an empty one, so
+ // that we get defined behavior if accessibility sets the cursor
+ // later.
+ pCursorShell->ClearMark();
+ if( bCallShowCursor )
+ pCursorShell->ShowCursor();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+OUString SwAccessibleContext::GetResource(TranslateId pResId,
+ const OUString *pArg1,
+ const OUString *pArg2)
+{
+ OUString sStr = SwResId(pResId);
+
+ if( pArg1 )
+ {
+ sStr = sStr.replaceFirst( "$(ARG1)", *pArg1 );
+ }
+ if( pArg2 )
+ {
+ sStr = sStr.replaceFirst( "$(ARG2)", *pArg2 );
+ }
+
+ return sStr;
+}
+
+void SwAccessibleContext::RemoveFrameFromAccessibleMap()
+{
+ assert(m_refCount > 0); // must be alive to do this without using m_wMap
+ if (m_isRegisteredAtAccessibleMap && GetFrame() && GetMap())
+ GetMap()->RemoveContext( GetFrame() );
+}
+
+bool SwAccessibleContext::HasAdditionalAccessibleChildren()
+{
+ bool bRet( false );
+
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ bRet = pPostItMgr->HasFrameConnectedSidebarWins( *(GetFrame()) );
+ }
+ }
+
+ return bRet;
+}
+
+/** #i88070# - get additional accessible child by index */
+vcl::Window* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex )
+{
+ vcl::Window* pAdditionalAccessibleChild( nullptr );
+
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ pAdditionalAccessibleChild =
+ pPostItMgr->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex );
+ }
+ }
+
+ return pAdditionalAccessibleChild;
+}
+
+/** #i88070# - get all additional accessible children */
+void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren )
+{
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ pPostItMgr->GetAllSidebarWinForFrame( *(GetFrame()), pChildren );
+ }
+ }
+}
+
+bool SwAccessibleContext::SetSelectedState(bool const bSelected)
+{
+ if (m_isSelectedInDoc != bSelected)
+ {
+ m_isSelectedInDoc = bSelected;
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bSelected );
+ return true;
+ }
+ return false;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccontext.hxx b/sw/source/core/access/acccontext.hxx
new file mode 100644
index 0000000000..790b7c4911
--- /dev/null
+++ b/sw/source/core/access/acccontext.hxx
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "accframe.hxx"
+#include <accmap.hxx>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext3.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <memory>
+#include <mutex>
+
+namespace vcl { class Window; }
+class SwCursorShell;
+class SdrObject;
+class SwPaM;
+namespace accessibility {
+ class AccessibleShape;
+}
+
+inline constexpr OUString sAccessibleServiceName = u"com.sun.star.accessibility.Accessible"_ustr;
+
+class SwAccessibleContext :
+ public ::cppu::WeakImplHelper<
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleContext3,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::lang::XServiceInfo
+ >,
+ public SwAccessibleFrame
+{
+ // The implements for the XAccessibleSelection interface has been
+ // 'externalized' and wants access to the protected members like
+ // GetMap, GetChild, GetParent, and GetFrame.
+ friend class SwAccessibleSelectionHelper;
+#if OSL_DEBUG_LEVEL > 0
+ friend class SwAccessibleMap;
+#endif
+
+protected:
+ mutable std::mutex m_Mutex;
+
+private:
+ OUString m_sName; // immutable outside constructor
+
+ // The parent if it has been retrieved. This is always an
+ // SwAccessibleContext. (protected by Mutex)
+ css::uno::WeakReference <
+ css::accessibility::XAccessible > m_xWeakParent;
+
+ SwAccessibleMap *m_pMap; // must be protected by solar mutex
+ /// note: the m_pMap is guaranteed to be valid until we hit the
+ /// dtor ~SwAccessibleContext, then m_wMap must be checked if it's still
+ /// alive, after locking SolarMutex (alternatively, Dispose clears m_pMap)
+ std::weak_ptr<SwAccessibleMap> m_wMap;
+
+ sal_uInt32 m_nClientId; // client id in the AccessibleEventNotifier queue
+ sal_Int16 m_nRole; // immutable outside constructor
+
+ // The current states (protected by mutex)
+ bool m_isShowingState : 1;
+ bool m_isEditableState : 1;
+ bool m_isOpaqueState : 1;
+ bool m_isDefuncState : 1;
+
+ // Are we currently disposing that object (protected by solar mutex)?
+ bool m_isDisposing : 1;
+
+ // #i85634# - boolean, indicating if the accessible context is
+ // in general registered at the accessible map.
+ bool m_isRegisteredAtAccessibleMap;
+
+ void InitStates();
+
+protected:
+ void SetName( const OUString& rName ) { m_sName = rName; }
+ sal_Int16 GetRole() const
+ {
+ return m_nRole;
+ }
+ //This flag is used to mark the object's selected state.
+ bool m_isSelectedInDoc;
+ void SetParent( SwAccessibleContext *pParent );
+ css::uno::Reference< css::accessibility::XAccessible> GetWeakParent() const;
+
+ bool IsDisposing() const { return m_isDisposing; }
+
+ vcl::Window *GetWindow();
+ SwAccessibleMap *GetMap() { return m_pMap; }
+ const SwAccessibleMap *GetMap() const { return m_pMap; }
+
+ /** convenience method to get the SwViewShell through accessibility map */
+ SwViewShell* GetShell()
+ {
+ return GetMap()->GetShell();
+ }
+ const SwViewShell* GetShell() const
+ {
+ return GetMap()->GetShell();
+ }
+
+ /** convenience method to get SwCursorShell through accessibility map
+ * @returns SwCursorShell, or NULL if none is found */
+ SwCursorShell* GetCursorShell();
+ const SwCursorShell* GetCursorShell() const;
+
+ // Notify all children that the visible area has changed.
+ // The SwFrame might belong to the current object or to any other child or
+ // grandchild.
+ void ChildrenScrolled( const SwFrame *pFrame, const SwRect& rOldVisArea );
+
+ // The context's showing state changed. May only be called for context that
+ // exist even if they aren't visible.
+ void Scrolled( const SwRect& rOldVisArea );
+
+ // A child has been moved while setting the visible area
+ void ScrolledWithin( const SwRect& rOldVisArea );
+
+ // The has been added while setting the visible area
+ void ScrolledIn();
+
+ // The context has to be removed while setting the visible area
+ void ScrolledOut( const SwRect& rOldVisArea );
+
+ // Invalidate the states of all children of the specified SwFrame. The
+ // SwFrame might belong the current object or to any child or grandchild!
+ // #i27301# - use new type definition for <_nStates>
+ void InvalidateChildrenStates( const SwFrame* _pFrame,
+ AccessibleStates _nStates );
+
+ // Dispose children of the specified SwFrame. The SwFrame might belong to
+ // the current object or to any other child or grandchild.
+ void DisposeChildren(const SwFrame *pFrame,
+ bool bRecursive, bool bCanSkipInvisible);
+
+ void DisposeShape( const SdrObject *pObj,
+ ::accessibility::AccessibleShape *pAccImpl );
+ void ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl );
+
+ virtual void InvalidateContent_( bool bVisibleDataFired );
+
+ virtual void InvalidateCursorPos_();
+ virtual void InvalidateFocus_();
+
+public:
+ void FireAccessibleEvent( css::accessibility::AccessibleEventObject& rEvent );
+
+protected:
+ // broadcast visual data event
+ void FireVisibleDataEvent();
+
+ // broadcast state change event
+ void FireStateChangedEvent( sal_Int64 nState, bool bNewState );
+
+ // Set states for getAccessibleStateSet.
+ // This base class sets DEFUNC(0/1), EDITABLE(0/1), ENABLED(1),
+ // SHOWING(0/1), OPAQUE(0/1) and VISIBLE(1).
+ virtual void GetStates( sal_Int64& rStateSet );
+
+ bool IsEditableState();
+
+ /// @throws css::uno::RuntimeException
+ css::awt::Rectangle
+ getBoundsImpl(bool bRelative);
+
+ // #i85634#
+ void NotRegisteredAtAccessibleMap()
+ {
+ m_isRegisteredAtAccessibleMap = false;
+ }
+ void RemoveFrameFromAccessibleMap();
+
+ void ThrowIfDisposed();
+
+ virtual ~SwAccessibleContext() override;
+
+ // Return a reference to the parent.
+ css::uno::Reference< css::accessibility::XAccessible>
+ getAccessibleParentImpl();
+
+public:
+ SwAccessibleContext( std::shared_ptr<SwAccessibleMap> const& pMap,
+ sal_Int16 nRole, const SwFrame *pFrame );
+
+ // XAccessible
+
+ // Return the XAccessibleContext.
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext> SAL_CALL
+ getAccessibleContext() override;
+
+ // XAccessibleContext
+
+ // Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ // Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int64 nIndex) override;
+
+ virtual css::uno::Sequence<css::uno::Reference< css::accessibility::XAccessible>> SAL_CALL
+ getAccessibleChildren() override;
+
+ // Return a reference to the parent.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleParent() override;
+
+ // Return this objects index among the parents children.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+ // Return this object's role.
+ virtual sal_Int16 SAL_CALL
+ getAccessibleRole() override;
+
+ // getAccessibleDescription() is abstract
+
+ // Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ // Return NULL to indicate that an empty relation set.
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ // Return the set of current states.
+ virtual sal_Int64 SAL_CALL getAccessibleStateSet() override;
+
+ /** Return the parents locale or throw exception if this object has no
+ parent yet/anymore. */
+ virtual css::lang::Locale SAL_CALL
+ getLocale() override;
+
+ // XAccessibleEventBroadcaster
+
+ virtual void SAL_CALL addAccessibleEventListener(
+ const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener(
+ const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+
+ virtual css::awt::Point SAL_CALL getLocation() override;
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+
+ virtual void SAL_CALL grabFocus() override;
+
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XServiceInfo
+
+ // getImplementationName() and getSupportedServiceNames are abstract
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // thread safe C++ interface
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true);
+
+ // The child object is not visible any longer and should be destroyed
+ virtual void DisposeChild(const sw::access::SwAccessibleChild& rFrameOrObj, bool bRecursive, bool bCanSkipInvisible);
+
+ // The object has been moved by the layout
+ virtual void InvalidatePosOrSize( const SwRect& rFrame );
+
+ // The child object has been moved by the layout
+ virtual void InvalidateChildPosOrSize( const sw::access::SwAccessibleChild& rFrameOrObj,
+ const SwRect& rFrame );
+
+ // The content may have changed (but it hasn't to have changed)
+ void InvalidateContent();
+
+ // The caretPos has changed
+ void InvalidateCursorPos();
+
+ // The Focus state has changed
+ void InvalidateFocus();
+
+ // Check states
+ // #i27301# - use new type definition for <_nStates>
+ void InvalidateStates( AccessibleStates _nStates );
+
+ // the XAccessibleRelationSet may have changed
+ void InvalidateRelation( sal_uInt16 nType );
+
+ void InvalidateTextSelection(); // #i27301# - text selection has changed
+ void InvalidateAttr(); // #i88069# - attributes has changed
+
+ bool HasAdditionalAccessibleChildren();
+
+ // #i88070# - get additional child by index
+ vcl::Window* GetAdditionalAccessibleChild( const sal_Int32 nIndex );
+
+ // #i88070# - get all additional accessible children
+ void GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren );
+
+ const OUString& GetName() const { return m_sName; }
+
+ virtual bool HasCursor(); // required by map to remember that object
+
+ bool Select( SwPaM *pPaM, SdrObject *pObj, bool bAdd );
+ bool Select( SwPaM& rPaM )
+ {
+ return Select( &rPaM, nullptr, false );
+ }
+ bool Select( SdrObject *pObj, bool bAdd )
+ {
+ return Select( nullptr, pObj, bAdd );
+ }
+
+ //This method is used to update the selected state and fire the selected state changed event.
+ virtual bool SetSelectedState(bool bSelected);
+ bool IsSelectedInDoc() const { return m_isSelectedInDoc; }
+
+ bool IsDisposed() const;
+
+ static OUString GetResource(TranslateId pResId,
+ const OUString *pArg1 = nullptr,
+ const OUString *pArg2 = nullptr);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accdoc.cxx b/sw/source/core/access/accdoc.cxx
new file mode 100644
index 0000000000..a1cf511b8b
--- /dev/null
+++ b/sw/source/core/access/accdoc.cxx
@@ -0,0 +1,720 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/window.hxx>
+#include <rootfrm.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <accmap.hxx>
+#include "accdoc.hxx"
+#include <strings.hrc>
+#include <pagefrm.hxx>
+
+#include <swatrset.hxx>
+#include <docsh.hxx>
+#include <crsrsh.hxx>
+#include <fesh.hxx>
+#include <fmtclds.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <swmodule.hxx>
+#include <svtools/colorcfg.hxx>
+
+#include <fmtanchr.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <dcontact.hxx>
+#include <svx/svdmark.hxx>
+constexpr OUString sServiceName = u"com.sun.star.text.AccessibleTextDocumentView"_ustr;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using lang::IndexOutOfBoundsException;
+
+// SwAccessibleDocumentBase: base class for SwAccessibleDocument and
+// SwAccessiblePreview
+
+SwAccessibleDocumentBase::SwAccessibleDocumentBase(
+ std::shared_ptr<SwAccessibleMap> const& pMap)
+ : SwAccessibleContext(pMap, AccessibleRole::DOCUMENT_TEXT,
+ pMap->GetShell()->GetLayout())
+ , mxParent(pMap->GetShell()->GetWin()->GetAccessibleParentWindow()->GetAccessible())
+ , mpChildWin(nullptr)
+{
+}
+
+SwAccessibleDocumentBase::~SwAccessibleDocumentBase()
+{
+}
+
+void SwAccessibleDocumentBase::SetVisArea()
+{
+ SolarMutexGuard aGuard;
+
+ SwRect aOldVisArea( GetVisArea() );
+ const SwRect& rNewVisArea = GetMap()->GetVisArea();
+ if( aOldVisArea != rNewVisArea )
+ {
+ SwAccessibleFrame::SetVisArea( GetMap()->GetVisArea() );
+ // #i58139# - showing state of document view needs also be updated.
+ // Thus, call method <Scrolled(..)> instead of <ChildrenScrolled(..)>
+ // ChildrenScrolled( GetFrame(), aOldVisArea );
+ Scrolled( aOldVisArea );
+ }
+}
+
+void SwAccessibleDocumentBase::AddChild( vcl::Window *pWin, bool bFireEvent )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( !mpChildWin, "only one child window is supported" );
+ if( !mpChildWin )
+ {
+ mpChildWin = pWin;
+
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= mpChildWin->GetAccessible();
+ aEvent.IndexHint = -1;
+ FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleDocumentBase::RemoveChild( vcl::Window *pWin )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( !mpChildWin || pWin == mpChildWin, "invalid child window to remove" );
+ if( mpChildWin && pWin == mpChildWin )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= mpChildWin->GetAccessible();
+ aEvent.IndexHint = -1;
+ FireAccessibleEvent( aEvent );
+
+ mpChildWin = nullptr;
+ }
+}
+
+sal_Int64 SAL_CALL SwAccessibleDocumentBase::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ // ThrowIfDisposed is called by parent
+
+ sal_Int64 nChildren = SwAccessibleContext::getAccessibleChildCount();
+ if( !IsDisposing() && mpChildWin )
+ nChildren++;
+
+ return nChildren;
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleDocumentBase::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpChildWin )
+ {
+ ThrowIfDisposed();
+
+ if ( nIndex == GetChildCount( *(GetMap()) ) )
+ {
+ return mpChildWin->GetAccessible();
+ }
+ }
+
+ return SwAccessibleContext::getAccessibleChild( nIndex );
+}
+
+uno::Reference< XAccessible> SAL_CALL SwAccessibleDocumentBase::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int64 SAL_CALL SwAccessibleDocumentBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XAccessibleContext > xAcc( mxParent->getAccessibleContext() );
+ uno::Reference < XAccessible > xThis( this );
+ sal_Int64 nCount = xAcc->getAccessibleChildCount();
+
+ for( sal_Int64 i=0; i < nCount; i++ )
+ {
+ try
+ {
+ if( xAcc->getAccessibleChild( i ) == xThis )
+ return i;
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+OUString SAL_CALL SwAccessibleDocumentBase::getAccessibleDescription()
+{
+ return GetResource( STR_ACCESS_DOC_DESC );
+}
+
+OUString SAL_CALL SwAccessibleDocumentBase::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString sAccName = GetResource( STR_ACCESS_DOC_WORDPROCESSING );
+ SwDoc *pDoc = GetMap() ? GetShell()->GetDoc() : nullptr;
+ if ( pDoc )
+ {
+ OUString sFileName = pDoc->getDocAccTitle();
+ if ( sFileName.isEmpty() )
+ {
+ SwDocShell* pDocSh = pDoc->GetDocShell();
+ if ( pDocSh )
+ {
+ sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME );
+ }
+ }
+
+ if ( !sFileName.isEmpty() )
+ {
+ sAccName = sFileName + " - " + sAccName;
+ }
+ }
+
+ return sAccName;
+}
+
+awt::Rectangle SAL_CALL SwAccessibleDocumentBase::getBounds()
+{
+ try
+ {
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ tools::Rectangle aPixBounds( pWin->GetWindowExtentsRelative( *pWin->GetAccessibleParentWindow() ) );
+ awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
+ aPixBounds.GetWidth(), aPixBounds.GetHeight() );
+
+ return aBox;
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return awt::Rectangle();
+ }
+}
+
+awt::Point SAL_CALL SwAccessibleDocumentBase::getLocation()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ Point aPixPos( pWin->GetWindowExtentsRelative( *pWin->GetAccessibleParentWindow() ).TopLeft() );
+ awt::Point aLoc( aPixPos.getX(), aPixPos.getY() );
+
+ return aLoc;
+}
+
+css::awt::Point SAL_CALL SwAccessibleDocumentBase::getLocationOnScreen()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ Point aPixPos( pWin->GetWindowExtentsAbsolute().TopLeft() );
+ awt::Point aLoc( aPixPos.getX(), aPixPos.getY() );
+
+ return aLoc;
+}
+
+css::awt::Size SAL_CALL SwAccessibleDocumentBase::getSize()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ Size aPixSize( pWin->GetWindowExtentsAbsolute().GetSize() );
+ awt::Size aSize( aPixSize.Width(), aPixSize.Height() );
+
+ return aSize;
+}
+
+sal_Bool SAL_CALL SwAccessibleDocumentBase::containsPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ tools::Rectangle aPixBounds( pWin->GetWindowExtentsAbsolute() );
+ aPixBounds.Move(-aPixBounds.Left(), -aPixBounds.Top());
+
+ Point aPixPoint( aPoint.X, aPoint.Y );
+ return aPixBounds.Contains( aPixPoint );
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleDocumentBase::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpChildWin )
+ {
+ ThrowIfDisposed();
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+ if (pWin->isDisposed()) // tdf#147967
+ return nullptr;
+
+ Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to window
+ if( mpChildWin->GetWindowExtentsRelative( *pWin ).Contains( aPixPoint ) )
+ return mpChildWin->GetAccessible();
+ }
+
+ return SwAccessibleContext::getAccessibleAtPoint( aPoint );
+}
+
+// SwAccessibleDocument
+
+void SwAccessibleDocument::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // MULTISELECTABLE
+ rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+}
+
+SwAccessibleDocument::SwAccessibleDocument(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap)
+ : SwAccessibleDocumentBase(pInitMap)
+ , maSelectionHelper(*this)
+{
+ SetName(pInitMap->GetDocName());
+ vcl::Window *pWin = pInitMap->GetShell()->GetWin();
+ if( pWin )
+ {
+ pWin->AddChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+ sal_uInt16 nCount = pWin->GetChildCount();
+ for( sal_uInt16 i=0; i < nCount; i++ )
+ {
+ vcl::Window* pChildWin = pWin->GetChild( i );
+ if( pChildWin &&
+ AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ AddChild( pChildWin, false );
+ }
+ }
+}
+
+SwAccessibleDocument::~SwAccessibleDocument()
+{
+ vcl::Window *pWin = GetMap() ? GetMap()->GetShell()->GetWin() : nullptr;
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+}
+
+void SwAccessibleDocument::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
+
+ vcl::Window *pWin = GetMap() ? GetMap()->GetShell()->GetWin() : nullptr;
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+IMPL_LINK( SwAccessibleDocument, WindowChildEventListener, VclWindowEvent&, rEvent, void )
+{
+ OSL_ENSURE( rEvent.GetWindow(), "Window???" );
+ switch ( rEvent.GetId() )
+ {
+ case VclEventId::WindowShow: // send create on show for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast< vcl::Window* >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ AddChild( pChildWin );
+ }
+ }
+ break;
+ case VclEventId::WindowHide: // send destroy on hide for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast< vcl::Window* >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ RemoveChild( pChildWin );
+ }
+ }
+ break;
+ case VclEventId::ObjectDying: // send destroy on hide for direct accessible children
+ {
+ vcl::Window* pChildWin = rEvent.GetWindow();
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ RemoveChild( pChildWin );
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+OUString SAL_CALL SwAccessibleDocument::getImplementationName()
+{
+ return u"com.sun.star.comp.Writer.SwAccessibleDocumentView"_ustr;
+}
+
+sal_Bool SAL_CALL SwAccessibleDocument::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleDocument::getSupportedServiceNames()
+{
+ return { sServiceName, sAccessibleServiceName };
+}
+
+// XInterface
+
+uno::Any SwAccessibleDocument::queryInterface(
+ const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleSelection>::get() )
+ {
+ uno::Reference<XAccessibleSelection> aSelect = this;
+ aRet <<= aSelect;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Reference<XAccessibleExtendedAttributes> aAttribute = this;
+ aRet <<= aAttribute;
+ }
+ else
+ aRet = SwAccessibleContext::queryInterface( rType );
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleDocument::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleSelection>::get(),
+ SwAccessibleDocumentBase::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleDocument::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleSelection
+
+void SwAccessibleDocument::selectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ maSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleDocument::isAccessibleChildSelected(
+ sal_Int64 nChildIndex )
+{
+ return maSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleDocument::clearAccessibleSelection( )
+{
+}
+
+void SwAccessibleDocument::selectAllAccessibleChildren( )
+{
+ maSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int64 SwAccessibleDocument::getSelectedAccessibleChildCount( )
+{
+ return maSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleDocument::getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ return maSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+// index has to be treated as global child index.
+void SwAccessibleDocument::deselectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ maSelectionHelper.deselectAccessibleChild( nChildIndex );
+}
+
+uno::Any SAL_CALL SwAccessibleDocument::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ uno::Any anyAttribute;
+ SwDoc *pDoc = GetMap() ? GetShell()->GetDoc() : nullptr;
+
+ if (!pDoc)
+ return anyAttribute;
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( !pCursorShell )
+ return anyAttribute;
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ if( pFEShell )
+ {
+ OUString sDisplay;
+ sal_uInt16 nPage, nLogPage;
+ pFEShell->GetPageNumber(-1,true,nPage,nLogPage,sDisplay);
+
+ OUString sValue = "page-name:" + sDisplay +
+ ";page-number:" +
+ OUString::number( nPage ) +
+ ";total-pages:" +
+ OUString::number( pCursorShell->GetPageCnt() ) + ";";
+
+ // cursor position relative to the page
+ Point aCursorPagePos = pFEShell->GetCursorPagePos();
+ sValue += "cursor-position-in-page-horizontal:" + OUString::number(aCursorPagePos.getX())
+ + ";cursor-position-in-page-vertical:" + OUString::number(aCursorPagePos.getY()) + ";";
+
+ SwContentFrame* pCurrFrame = pCursorShell->GetCurrFrame();
+ SwPageFrame* pCurrPage=static_cast<SwFrame*>(pCurrFrame)->FindPageFrame();
+ sal_Int32 nLineNum = 0;
+ SwTextFrame* pTextFrame = nullptr;
+ SwTextFrame* pCurrTextFrame = nullptr;
+ pTextFrame = static_cast< SwTextFrame* >(pCurrPage->ContainsContent());
+ if (pCurrFrame->IsInFly())//such as, graphic,chart
+ {
+ SwFlyFrame *pFlyFrame = pCurrFrame->FindFlyFrame();
+ const SwFormatAnchor& rAnchor = pFlyFrame->GetFormat()->GetAnchor();
+ RndStdIds eAnchorId = rAnchor.GetAnchorId();
+ if(eAnchorId == RndStdIds::FLY_AS_CHAR)
+ {
+ const SwFrame *pSwFrame = pFlyFrame->GetAnchorFrame();
+ if(pSwFrame->IsTextFrame())
+ pCurrTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pSwFrame));
+ }
+ }
+ else
+ {
+ assert(dynamic_cast<SwTextFrame*>(pCurrFrame));
+ pCurrTextFrame = static_cast<SwTextFrame* >(pCurrFrame);
+ }
+ //check whether the text frame where the Graph/OLE/Frame anchored is in the Header/Footer
+ SwFrame* pFrame = pCurrTextFrame;
+ while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
+ pFrame = pFrame->GetUpper();
+ if ( pFrame )
+ pCurrTextFrame = nullptr;
+ //check shape
+ if(pCursorShell->Imp()->GetDrawView())
+ {
+ const SdrMarkList &rMrkList = pCursorShell->Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark(i)->GetMarkedSdrObj();
+ SwFrameFormat* pFormat = static_cast<SwDrawContact*>(pObj->GetUserCall())->GetFormat();
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if( RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId() )
+ pCurrTextFrame = nullptr;
+ }
+ }
+ //calculate line number
+ if (pCurrTextFrame && pTextFrame)
+ {
+ if (!(pCurrTextFrame->IsInTab() || pCurrTextFrame->IsInFootnote()))
+ {
+ while( pTextFrame && pTextFrame != pCurrTextFrame )
+ {
+ //check header/footer
+ pFrame = pTextFrame;
+ while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
+ pFrame = pFrame->GetUpper();
+ if ( pFrame )
+ {
+ pTextFrame = static_cast< SwTextFrame*>(pTextFrame->GetNextContentFrame());
+ continue;
+ }
+ if (!(pTextFrame->IsInTab() || pTextFrame->IsInFootnote() || pTextFrame->IsInFly()))
+ nLineNum += pTextFrame->GetThisLines();
+ pTextFrame = static_cast< SwTextFrame* >(pTextFrame ->GetNextContentFrame());
+ }
+ SwPaM* pCaret = pCursorShell->GetCursor();
+ if (!pCurrTextFrame->IsEmpty() && pCaret)
+ {
+ assert(pCurrTextFrame->IsTextFrame());
+ const SwPosition* pPoint = nullptr;
+ if (pCurrTextFrame->IsInFly())
+ {
+ SwFlyFrame *pFlyFrame = pCurrTextFrame->FindFlyFrame();
+ const SwFormatAnchor& rAnchor = pFlyFrame->GetFormat()->GetAnchor();
+ pPoint = rAnchor.GetContentAnchor();
+ SwContentNode *const pNode(pPoint->GetNode().GetContentNode());
+ pCurrTextFrame = pNode
+ ? static_cast<SwTextFrame*>(pNode->getLayoutFrame(
+ pCurrTextFrame->getRootFrame(), pPoint))
+ : nullptr;
+ }
+ else
+ pPoint = pCaret->GetPoint();
+ if (pCurrTextFrame)
+ {
+ TextFrameIndex const nActPos(pCurrTextFrame->MapModelToViewPos(*pPoint));
+ nLineNum += pCurrTextFrame->GetLineCount( nActPos );
+ }
+ }
+ else
+ ++nLineNum;
+ }
+ }
+
+ sValue += "line-number:" + OUString::number( nLineNum ) + ";";
+
+ SwFrame* pCurrCol=static_cast<SwFrame*>(pCurrFrame)->FindColFrame();
+
+ sValue += "column-number:";
+
+ int nCurrCol = 1;
+ if(pCurrCol!=nullptr)
+ {
+ //SwLayoutFrame* pParent = pCurrCol->GetUpper();
+ SwFrame* pCurrPageCol=static_cast<SwFrame*>(pCurrFrame)->FindColFrame();
+ while(pCurrPageCol && pCurrPageCol->GetUpper() && pCurrPageCol->GetUpper()->IsPageFrame())
+ {
+ pCurrPageCol = pCurrPageCol->GetUpper();
+ }
+
+ SwLayoutFrame* pParent = pCurrPageCol->GetUpper();
+
+ if(pParent!=nullptr)
+ {
+ SwFrame* pCol = pParent->Lower();
+ while(pCol&&(pCol!=pCurrPageCol))
+ {
+ pCol = pCol->GetNext();
+ ++nCurrCol;
+ }
+ }
+ }
+ sValue += OUString::number( nCurrCol ) + ";";
+
+ const SwFormatCol &rFormatCol=pCurrPage->GetAttrSet()->GetCol();
+ sal_uInt16 nColCount=rFormatCol.GetNumCols();
+ nColCount = nColCount>0?nColCount:1;
+ sValue += "total-columns:" + OUString::number( nColCount ) + ";";
+
+ SwSectionFrame* pCurrSctFrame=static_cast<SwFrame*>(pCurrFrame)->FindSctFrame();
+ if(pCurrSctFrame!=nullptr && pCurrSctFrame->GetSection()!=nullptr )
+ {
+ OUString sectionName = pCurrSctFrame->GetSection()->GetSectionName();
+
+ sectionName = sectionName.replaceFirst( "\\" , "\\\\" );
+ sectionName = sectionName.replaceFirst( "=" , "\\=" );
+ sectionName = sectionName.replaceFirst( ";" , "\\;" );
+ sectionName = sectionName.replaceFirst( "," , "\\," );
+ sectionName = sectionName.replaceFirst( ":" , "\\:" );
+
+ sValue += "section-name:" + sectionName + ";";
+
+ //section-columns-number
+
+ nCurrCol = 1;
+
+ if(pCurrCol!=nullptr)
+ {
+ SwLayoutFrame* pParent = pCurrCol->GetUpper();
+ if(pParent!=nullptr)
+ {
+ SwFrame* pCol = pParent->Lower();
+ while(pCol&&(pCol!=pCurrCol))
+ {
+ pCol = pCol->GetNext();
+ nCurrCol +=1;
+ }
+ }
+ }
+ sValue += "section-columns-number:" +
+ OUString::number( nCurrCol ) + ";";
+
+ //section-total-columns
+ const SwFormatCol &rFormatSctCol=pCurrSctFrame->GetAttrSet()->GetCol();
+ sal_uInt16 nSctColCount=rFormatSctCol.GetNumCols();
+ nSctColCount = nSctColCount>0?nSctColCount:1;
+ sValue += "section-total-columns:" +
+ OUString::number( nSctColCount ) + ";";
+ }
+
+ anyAttribute <<= sValue;
+ }
+ return anyAttribute;
+}
+
+sal_Int32 SAL_CALL SwAccessibleDocument::getBackground()
+{
+ SolarMutexGuard aGuard;
+ return sal_Int32(SW_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accdoc.hxx b/sw/source/core/access/accdoc.hxx
new file mode 100644
index 0000000000..735dc822cd
--- /dev/null
+++ b/sw/source/core/access/accdoc.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include "accselectionhelper.hxx"
+#include <vcl/window.hxx>
+
+// base class for SwAccessibleDocument (in this same header file) and
+// SwAccessiblePreview
+class SwAccessibleDocumentBase : public SwAccessibleContext
+{
+ css::uno::Reference< css::accessibility::XAccessible> mxParent;
+
+ VclPtr<vcl::Window> mpChildWin; // protected by solar mutex
+
+ using SwAccessibleFrame::SetVisArea;
+
+protected:
+ virtual ~SwAccessibleDocumentBase() override;
+
+public:
+ SwAccessibleDocumentBase(std::shared_ptr<SwAccessibleMap> const& pInitMap);
+
+ void SetVisArea();
+
+ void AddChild( vcl::Window *pWin, bool bFireEvent = true );
+ void RemoveChild( vcl::Window *pWin );
+
+ // XAccessibleContext
+
+ // Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ // Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int64 nIndex) override;
+
+ // Return a reference to the parent.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleParent() override;
+
+ // Return this objects index among the parents children.
+ virtual sal_Int64 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+ // Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ virtual OUString SAL_CALL getAccessibleName() override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+
+ virtual css::awt::Point SAL_CALL getLocation() override;
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+};
+
+/**
+ * access to an accessible Writer document
+ */
+class SwAccessibleDocument : public SwAccessibleDocumentBase,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ // Implementation for XAccessibleSelection interface
+ SwAccessibleSelectionHelper maSelectionHelper;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTISELECTABLE(1)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+
+ virtual ~SwAccessibleDocument() override;
+
+public:
+ SwAccessibleDocument(std::shared_ptr<SwAccessibleMap> const& pInitMap);
+
+ DECL_LINK( WindowChildEventListener, VclWindowEvent&, void );
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XInterface
+
+ // XInterface is inherited through SwAccessibleContext and
+ // XAccessibleSelection. These methods are needed to avoid
+ // ambiguities.
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleSelection
+
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int64 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override;
+
+ // thread safe C++ interface
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accembedded.cxx b/sw/source/core/access/accembedded.cxx
new file mode 100644
index 0000000000..1eb54cf258
--- /dev/null
+++ b/sw/source/core/access/accembedded.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <flyfrm.hxx>
+#include "accembedded.hxx"
+#include <cntfrm.hxx>
+#include <notxtfrm.hxx>
+#include <ndole.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleEmbeddedObject";
+
+SwAccessibleEmbeddedObject::SwAccessibleEmbeddedObject(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleNoTextFrame( pInitMap, AccessibleRole::EMBEDDED_OBJECT, pFlyFrame )
+{
+}
+
+SwAccessibleEmbeddedObject::~SwAccessibleEmbeddedObject()
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ SwAccessibleEmbeddedObject::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = SwAccessibleNoTextFrame::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleExtendedAttributes* >(this) );
+ return aReturn;
+}
+
+void SAL_CALL
+ SwAccessibleEmbeddedObject::acquire()
+ noexcept
+{
+ SwAccessibleNoTextFrame::acquire ();
+}
+
+void SAL_CALL
+ SwAccessibleEmbeddedObject::release()
+ noexcept
+{
+ SwAccessibleNoTextFrame::release ();
+}
+
+OUString SAL_CALL SwAccessibleEmbeddedObject::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleEmbeddedObject::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextEmbeddedObject", sAccessibleServiceName };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleEmbeddedObject::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleExtendedAttributes
+css::uno::Any SAL_CALL SwAccessibleEmbeddedObject::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ css::uno::Any strRet;
+ OUString style;
+ SwFlyFrame* pFFrame = getFlyFrame();
+
+ if( pFFrame )
+ {
+ style = "style:";
+ SwContentFrame* pCFrame;
+ pCFrame = pFFrame->ContainsContent();
+ if( pCFrame )
+ {
+ assert(pCFrame->IsNoTextFrame());
+ SwContentNode *const pCNode = static_cast<SwNoTextFrame*>(pCFrame)->GetNode();
+ if( pCNode )
+ {
+ style += static_cast<SwOLENode*>(pCNode)->GetOLEObj().GetStyleString();
+ }
+ }
+ style += ";";
+ }
+ strRet <<= style;
+ return strRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accembedded.hxx b/sw/source/core/access/accembedded.hxx
new file mode 100644
index 0000000000..ce82af9e2b
--- /dev/null
+++ b/sw/source/core/access/accembedded.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "accnotextframe.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+
+class SwAccessibleEmbeddedObject : public SwAccessibleNoTextFrame
+ , public css::accessibility::XAccessibleExtendedAttributes
+
+{
+protected:
+ virtual ~SwAccessibleEmbeddedObject() override;
+
+public:
+ SwAccessibleEmbeddedObject(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame );
+
+ // XInterface
+
+ virtual css::uno::Any SAL_CALL
+ queryInterface (const css::uno::Type & rType) override;
+
+ virtual void SAL_CALL
+ acquire()
+ noexcept override;
+
+ virtual void SAL_CALL
+ release()
+ noexcept override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleExtendedAttributes
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override ;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfootnote.cxx b/sw/source/core/access/accfootnote.cxx
new file mode 100644
index 0000000000..6fb7ebca86
--- /dev/null
+++ b/sw/source/core/access/accfootnote.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <ftnfrm.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <viewsh.hxx>
+#include <accmap.hxx>
+#include "accfootnote.hxx"
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationNameFootnote
+ = u"com.sun.star.comp.Writer.SwAccessibleFootnoteView";
+constexpr OUStringLiteral sImplementationNameEndnote
+ = u"com.sun.star.comp.Writer.SwAccessibleEndnoteView";
+
+SwAccessibleFootnote::SwAccessibleFootnote(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ bool bIsEndnote,
+ const SwFootnoteFrame *pFootnoteFrame ) :
+ SwAccessibleContext( pInitMap,
+ bIsEndnote ? AccessibleRole::END_NOTE : AccessibleRole::FOOTNOTE,
+ pFootnoteFrame )
+{
+ TranslateId pResId = bIsEndnote ? STR_ACCESS_ENDNOTE_NAME
+ : STR_ACCESS_FOOTNOTE_NAME;
+
+ OUString sArg;
+ const SwTextFootnote *pTextFootnote =
+ static_cast< const SwFootnoteFrame *>( GetFrame() )->GetAttr();
+ if( pTextFootnote )
+ {
+ const SwDoc *pDoc = GetShell()->GetDoc();
+ sArg = pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, pFootnoteFrame->getRootFrame());
+ }
+
+ SetName(GetResource(pResId, &sArg));
+}
+
+SwAccessibleFootnote::~SwAccessibleFootnote()
+{
+}
+
+OUString SAL_CALL SwAccessibleFootnote::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ TranslateId pResId = AccessibleRole::END_NOTE == GetRole()
+ ? STR_ACCESS_ENDNOTE_DESC
+ : STR_ACCESS_FOOTNOTE_DESC ;
+
+ OUString sArg;
+ const SwTextFootnote *pTextFootnote =
+ static_cast< const SwFootnoteFrame *>( GetFrame() )->GetAttr();
+ if( pTextFootnote )
+ {
+ const SwDoc *pDoc = GetMap()->GetShell()->GetDoc();
+ sArg = pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, GetFrame()->getRootFrame());
+ }
+
+ return GetResource(pResId, &sArg);
+}
+
+OUString SAL_CALL SwAccessibleFootnote::getImplementationName()
+{
+ if( AccessibleRole::END_NOTE == GetRole() )
+ return sImplementationNameEndnote;
+ else
+ return sImplementationNameFootnote;
+}
+
+sal_Bool SAL_CALL SwAccessibleFootnote::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleFootnote::getSupportedServiceNames()
+{
+ return { (AccessibleRole::END_NOTE == GetRole())?OUString("com.sun.star.text.AccessibleEndnoteView"):OUString("com.sun.star.text.AccessibleFootnoteView"),
+ sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleFootnote::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+bool SwAccessibleFootnote::IsEndnote( const SwFootnoteFrame *pFootnoteFrame )
+{
+ const SwTextFootnote *pTextFootnote = pFootnoteFrame ->GetAttr();
+ return pTextFootnote && pTextFootnote->GetFootnote().IsEndNote() ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfootnote.hxx b/sw/source/core/access/accfootnote.hxx
new file mode 100644
index 0000000000..ecc852cd58
--- /dev/null
+++ b/sw/source/core/access/accfootnote.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include "acccontext.hxx"
+
+class SwAccessibleMap;
+class SwFootnoteFrame;
+
+class SwAccessibleFootnote : public SwAccessibleContext
+{
+protected:
+ virtual ~SwAccessibleFootnote() override;
+
+public:
+ SwAccessibleFootnote( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ bool bIsEndnote,
+ const SwFootnoteFrame *pFootnoteFrame );
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ static bool IsEndnote( const SwFootnoteFrame *pFrame );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframe.cxx b/sw/source/core/access/accframe.cxx
new file mode 100644
index 0000000000..637c379825
--- /dev/null
+++ b/sw/source/core/access/accframe.cxx
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/brushitem.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <pagefrm.hxx>
+#include <pagedesc.hxx>
+#include <fldbas.hxx>
+#include <accmap.hxx>
+#include "accfrmobjslist.hxx"
+#include "accfrmobjmap.hxx"
+#include "accframe.hxx"
+
+using namespace sw::access;
+
+// Regarding visibility (or in terms of accessibility: regarding the showing
+// state): A frame is visible and therefore contained in the tree if its frame
+// size overlaps with the visible area. The bounding box however is the
+// frame's paint area.
+sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bInPagePreview )
+{
+ sal_Int32 nCount = 0;
+
+ const SwAccessibleChildSList aVisList( rVisArea, *pFrame, rAccMap );
+
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ nCount++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ nCount += GetChildCount( rAccMap,
+ rVisArea, rLower.GetSwFrame(),
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+
+ return nCount;
+}
+
+SwAccessibleChild SwAccessibleFrame::GetChild(
+ SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ sal_Int32& rPos,
+ bool bInPagePreview )
+{
+ SwAccessibleChild aRet;
+
+ if( rPos >= 0 )
+ {
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( 0 == rPos )
+ aRet = rLower;
+ else
+ rPos--;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChild( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rPos,
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( 0 == rPos )
+ aRet = rLower;
+ else
+ rPos--;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChild( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rPos,
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ }
+
+ return aRet;
+}
+
+bool SwAccessibleFrame::GetChildIndex(
+ SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const SwAccessibleChild& rChild,
+ sal_Int32& rPos,
+ bool bInPagePreview )
+{
+ bool bFound = false;
+
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() && !bFound )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( rChild == rLower )
+ bFound = true;
+ else
+ rPos++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ bFound = GetChildIndex( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rChild,
+ rPos, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !bFound )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( rChild == rLower )
+ bFound = true;
+ else
+ rPos++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ bFound = GetChildIndex( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rChild,
+ rPos, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+
+ return bFound;
+}
+
+SwAccessibleChild SwAccessibleFrame::GetChildAtPixel( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const Point& rPixPos,
+ bool bInPagePreview,
+ SwAccessibleMap& rAccMap )
+{
+ SwAccessibleChild aRet;
+
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here, and we have to reverse iterate,
+ // because objects in front should be returned.
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_reverse_iterator aRIter( aVisMap.crbegin() );
+ while( aRIter != aVisMap.crend() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = (*aRIter).second;
+ // A frame is returned if it's frame size is inside the visarea
+ // and the position is inside the frame's paint area.
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
+ if( !aLogBounds.IsEmpty() )
+ {
+ tools::Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds ) );
+ if( aPixBounds.Contains( rPixPos ) )
+ aRet = rLower;
+ }
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrame()), rPixPos,
+ bInPagePreview, rAccMap );
+ }
+ ++aRIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order. Moreover, we can iterate forward,
+ // because the lowers don't overlap!
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ // A frame is returned if it's frame size is inside the visarea
+ // and the position is inside the frame's paint area.
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
+ if( !aLogBounds.IsEmpty() )
+ {
+ tools::Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds ) );
+ if( aPixBounds.Contains( rPixPos ) )
+ aRet = rLower;
+ }
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrame()), rPixPos,
+ bInPagePreview, rAccMap );
+ }
+ ++aIter;
+ }
+ }
+
+ return aRet;
+}
+
+void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ std::list< SwAccessibleChild >& rChildren,
+ bool bInPagePreview )
+{
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ rChildren.push_back( rLower );
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrame()),
+ rChildren, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ rChildren.push_back( rLower );
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrame()),
+ rChildren, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+}
+
+SwRect SwAccessibleFrame::GetBounds( const SwAccessibleMap& rAccMap,
+ const SwFrame *pFrame )
+{
+ if( !pFrame )
+ pFrame = GetFrame();
+
+ SwAccessibleChild aFrame( pFrame );
+ SwRect aBounds( aFrame.GetBounds( rAccMap ).Intersection( maVisArea ) );
+ return aBounds;
+}
+
+bool SwAccessibleFrame::IsEditable( SwViewShell const *pVSh ) const
+{
+ const SwFrame *pFrame = GetFrame();
+ if( !pFrame )
+ return false;
+
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( pVSh && (pVSh->GetViewOptions()->IsReadonly() ||
+ pVSh->IsPreview()) )
+ return false;
+
+ if( !pFrame->IsRootFrame() && pFrame->IsProtected() )
+ return false;
+
+ return true;
+}
+
+bool SwAccessibleFrame::IsOpaque( SwViewShell const *pVSh ) const
+{
+ SwAccessibleChild aFrame( GetFrame() );
+ if( !aFrame.GetSwFrame() )
+ return false;
+
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( !pVSh )
+ return false;
+
+ const SwViewOption *pVOpt = pVSh->GetViewOptions();
+ do
+ {
+ const SwFrame *pFrame = aFrame.GetSwFrame();
+ if( pFrame->IsRootFrame() )
+ return true;
+
+ if( pFrame->IsPageFrame() && !pVOpt->IsPageBack() )
+ return false;
+
+ const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
+ if( !rBack.GetColor().IsTransparent() ||
+ rBack.GetGraphicPos() != GPOS_NONE )
+ return true;
+
+ // If a fly frame has a transparent background color, we have to consider the background.
+ // But a background color "no fill"/"auto fill" should *not* be considered.
+ if( pFrame->IsFlyFrame() &&
+ rBack.GetColor().IsTransparent() &&
+ rBack.GetColor() != COL_TRANSPARENT
+ )
+ return true;
+
+ if( pFrame->IsSctFrame() )
+ {
+ const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
+ if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
+ SectionType::ToxContent == pSection->GetType() ) &&
+ !pVOpt->IsReadonly() &&
+ pVOpt->IsIndexShadings() )
+ return true;
+ }
+ if( pFrame->IsFlyFrame() )
+ aFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ else
+ aFrame = pFrame->GetUpper();
+ } while( aFrame.GetSwFrame() && !aFrame.IsAccessible( IsInPagePreview() ) );
+
+ return false;
+}
+
+SwAccessibleFrame::SwAccessibleFrame( const SwRect& rVisArea,
+ const SwFrame *pF,
+ bool bIsPagePreview ) :
+ maVisArea( rVisArea ),
+ mpFrame( pF ),
+ mbIsInPagePreview( bIsPagePreview )
+{
+ assert(mpFrame);
+}
+
+SwAccessibleFrame::~SwAccessibleFrame()
+{
+}
+
+const SwFrame* SwAccessibleFrame::GetParent( const SwAccessibleChild& rFrameOrObj,
+ bool bInPagePreview )
+{
+ return rFrameOrObj.GetParent( bInPagePreview );
+}
+
+OUString SwAccessibleFrame::GetFormattedPageNumber() const
+{
+ sal_uInt16 nPageNum = GetFrame()->GetVirtPageNum();
+ SvxNumType nFormat = GetFrame()->FindPageFrame()->GetPageDesc()
+ ->GetNumType().GetNumberingType();
+ if( SVX_NUM_NUMBER_NONE == nFormat )
+ nFormat = SVX_NUM_ARABIC;
+
+ OUString sRet( FormatNumber( nPageNum, nFormat ) );
+ return sRet;
+}
+
+sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap ) const
+{
+ return GetChildCount( rAccMap, maVisArea, mpFrame, IsInPagePreview() );
+}
+
+sw::access::SwAccessibleChild SwAccessibleFrame::GetChild(
+ SwAccessibleMap& rAccMap,
+ sal_Int32 nPos ) const
+{
+ return SwAccessibleFrame::GetChild( rAccMap, maVisArea, *mpFrame, nPos, IsInPagePreview() );
+}
+
+sal_Int32 SwAccessibleFrame::GetChildIndex( SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rChild ) const
+{
+ sal_Int32 nPos = 0;
+ return GetChildIndex( rAccMap, maVisArea, *mpFrame, rChild, nPos, IsInPagePreview() )
+ ? nPos
+ : -1;
+}
+
+sw::access::SwAccessibleChild SwAccessibleFrame::GetChildAtPixel(
+ const Point& rPos,
+ SwAccessibleMap& rAccMap ) const
+{
+ return GetChildAtPixel( maVisArea, *mpFrame, rPos, IsInPagePreview(), rAccMap );
+}
+
+void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
+ std::list< sw::access::SwAccessibleChild >& rChildren ) const
+{
+ GetChildren( rAccMap, maVisArea, *mpFrame, rChildren, IsInPagePreview() );
+}
+
+bool SwAccessibleFrame::IsShowing( const SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rFrameOrObj ) const
+{
+ return IsShowing( rFrameOrObj.GetBox( rAccMap ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframe.hxx b/sw/source/core/access/accframe.hxx
new file mode 100644
index 0000000000..c069292e11
--- /dev/null
+++ b/sw/source/core/access/accframe.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <swrect.hxx>
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#include <list>
+#include "accfrmobj.hxx"
+
+class SwAccessibleMap;
+class SwFrame;
+class SwViewShell;
+
+// Any method of this class must be called with an acquired solar mutex!
+
+class SwAccessibleFrame
+{
+ SwRect maVisArea;
+ const SwFrame* mpFrame;
+ const bool mbIsInPagePreview;
+
+protected:
+ // #i77106# - method needs to be called by new class <SwAccessibleTableColHeaders>
+ static sal_Int32 GetChildCount( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bInPagePreviewr );
+
+// private:
+ static sw::access::SwAccessibleChild GetChild( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ sal_Int32& rPos,
+ bool bInPagePreview);
+
+ static bool GetChildIndex( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const sw::access::SwAccessibleChild& rChild,
+ sal_Int32& rPos,
+ bool bInPagePreview );
+
+ static sw::access::SwAccessibleChild GetChildAtPixel( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const Point& rPos,
+ bool bInPagePreview,
+ SwAccessibleMap& rAccMap );
+
+ static void GetChildren( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ std::list< sw::access::SwAccessibleChild >& rChildren,
+ bool bInPagePreview );
+
+ bool IsEditable( SwViewShell const *pVSh ) const;
+
+ bool IsOpaque( SwViewShell const *pVSh ) const;
+
+public:
+ bool IsShowing( const SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rFrameOrObj ) const;
+ inline bool IsShowing( const SwRect& rFrame ) const;
+ inline bool IsShowing( const SwAccessibleMap& rAccMap ) const;
+
+protected:
+ bool IsInPagePreview() const
+ {
+ return mbIsInPagePreview;
+ }
+
+ void ClearFrame()
+ {
+ mpFrame = nullptr;
+ }
+
+ SwAccessibleFrame( const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bIsPagePreview );
+ virtual ~SwAccessibleFrame();
+public:
+ // Return the SwFrame this context is attached to.
+ const SwFrame* GetFrame() const { return mpFrame; };
+
+ static const SwFrame* GetParent( const sw::access::SwAccessibleChild& rFrameOrObj,
+ bool bInPagePreview );
+
+ sal_Int32 GetChildIndex( SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rChild ) const;
+
+protected:
+ // Return the bounding box of the frame clipped to the visible area.
+ // If no frame is specified, use this' frame.
+ SwRect GetBounds( const SwAccessibleMap& rAccMap,
+ const SwFrame *pFrame = nullptr );
+
+ // Return the upper that has a context attached. This might be
+ // another one than the immediate upper.
+ inline const SwFrame *GetParent() const;
+
+ // Return the lower count or the nth lower, there the lowers have a
+ // not be same one as the SwFrame's lowers
+ sal_Int32 GetChildCount( SwAccessibleMap& rAccMap ) const;
+ sw::access::SwAccessibleChild GetChild( SwAccessibleMap& rAccMap,
+ sal_Int32 nPos ) const;
+ sw::access::SwAccessibleChild GetChildAtPixel( const Point& rPos,
+ SwAccessibleMap& rAccMap ) const;
+ void GetChildren( SwAccessibleMap& rAccMap,
+ std::list< sw::access::SwAccessibleChild >& rChildren ) const;
+
+ void SetVisArea( const SwRect& rNewVisArea )
+ {
+ maVisArea = rNewVisArea;
+ }
+
+ const SwRect& GetVisArea() const
+ {
+ return maVisArea;
+ }
+
+ OUString GetFormattedPageNumber() const;
+};
+
+inline bool SwAccessibleFrame::IsShowing( const SwRect& rFrame ) const
+{
+ return !rFrame.IsEmpty() && rFrame.Overlaps( maVisArea );
+}
+
+inline bool SwAccessibleFrame::IsShowing( const SwAccessibleMap& rAccMap ) const
+{
+ sw::access::SwAccessibleChild aFrameOrObj( GetFrame() );
+ return IsShowing( rAccMap, aFrameOrObj );
+}
+
+inline const SwFrame *SwAccessibleFrame::GetParent() const
+{
+ sw::access::SwAccessibleChild aFrameOrObj( GetFrame() );
+ return GetParent( aFrameOrObj, IsInPagePreview() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframebase.cxx b/sw/source/core/access/accframebase.cxx
new file mode 100644
index 0000000000..fc94259b5b
--- /dev/null
+++ b/sw/source/core/access/accframebase.cxx
@@ -0,0 +1,365 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <ndindex.hxx>
+#include <fesh.hxx>
+#include <hints.hxx>
+#include <accmap.hxx>
+#include "accframebase.hxx"
+
+#include <crsrsh.hxx>
+#include <notxtfrm.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <fmtanchr.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+bool SwAccessibleFrameBase::IsSelected()
+{
+ bool bRet = false;
+
+ assert(GetMap());
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
+ {
+ const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
+ if( pFlyFrame == GetFrame() )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SwAccessibleFrameBase::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+
+ if (dynamic_cast<const SwFEShell*>(pVSh))
+ {
+ // SELECTABLE
+ rStateSet |= AccessibleStateType::SELECTABLE;
+ // FOCUSABLE
+ rStateSet |= AccessibleStateType::FOCUSABLE;
+ }
+
+ // SELECTED and FOCUSED
+ if( IsSelected() )
+ {
+ rStateSet |= AccessibleStateType::SELECTED;
+ SAL_WARN_IF(!m_bIsSelected, "sw.a11y", "bSelected out of sync");
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet |= AccessibleStateType::FOCUSED;
+ }
+ if( GetSelectedState() )
+ rStateSet |= AccessibleStateType::SELECTED;
+}
+
+SwNodeType SwAccessibleFrameBase::GetNodeType( const SwFlyFrame *pFlyFrame )
+{
+ SwNodeType nType = SwNodeType::Text;
+ if( pFlyFrame->Lower() )
+ {
+ if( pFlyFrame->Lower()->IsNoTextFrame() )
+ {
+ const SwNoTextFrame *const pContentFrame =
+ static_cast<const SwNoTextFrame *>(pFlyFrame->Lower());
+ nType = pContentFrame->GetNode()->GetNodeType();
+ }
+ }
+ else
+ {
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ if( pNdIdx )
+ {
+ const SwContentNode *pCNd =
+ (pNdIdx->GetNodes())[pNdIdx->GetIndex()+1]->GetContentNode();
+ if( pCNd )
+ nType = pCNd->GetNodeType();
+ }
+ }
+
+ return nType;
+}
+
+SwAccessibleFrameBase::SwAccessibleFrameBase(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleContext( pInitMap, nInitRole, pFlyFrame ),
+ m_bIsSelected( false )
+{
+ const SwFrameFormat* pFrameFormat = pFlyFrame->GetFormat();
+
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+
+ SetName( pFrameFormat->GetName() );
+
+ m_bIsSelected = IsSelected();
+}
+
+void SwAccessibleFrameBase::InvalidateCursorPos_()
+{
+ bool bNewSelected = IsSelected();
+ bool bOldSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldSelected = m_bIsSelected;
+ m_bIsSelected = bNewSelected;
+ }
+
+ if( bNewSelected )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ if( bOldSelected == bNewSelected )
+ return;
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() && bNewSelected )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ if( pWin && pWin->HasFocus() && !bNewSelected )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ if(!bNewSelected)
+ return;
+
+ uno::Reference< XAccessible > xParent( GetWeakParent() );
+ if( xParent.is() )
+ {
+ SwAccessibleContext *pAcc =
+ static_cast <SwAccessibleContext *>( xParent.get() );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ uno::Reference< XAccessible > xChild(this);
+ aEvent.NewValue <<= xChild;
+ pAcc->FireAccessibleEvent( aEvent );
+ }
+}
+
+void SwAccessibleFrameBase::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( !pWin )
+ return;
+
+ bool bSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bSelected = m_bIsSelected;
+ }
+ assert(bSelected && "focus object should be selected");
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && bSelected );
+}
+
+bool SwAccessibleFrameBase::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+SwAccessibleFrameBase::~SwAccessibleFrameBase()
+{
+}
+
+void SwAccessibleFrameBase::Notify(const SfxHint& rHint)
+{
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(GetFrame());
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ }
+ else if (rHint.GetId() == SfxHintId::SwNameChanged && pFlyFrame)
+ {
+ auto rNameChanged = static_cast<const sw::NameChanged&>(rHint);
+ const SwFrameFormat* pFrameFormat = pFlyFrame->GetFormat();
+
+ const OUString sOldName( GetName() );
+ assert( rNameChanged.m_sOld == sOldName);
+
+ SetName( pFrameFormat->GetName() );
+ assert( rNameChanged.m_sNew == GetName());
+
+ if( sOldName != GetName() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldName;
+ aEvent.NewValue <<= GetName();
+ FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleFrameBase::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+//Get the selection cursor of the document.
+SwPaM* SwAccessibleFrameBase::GetCursor()
+{
+ // get the cursor shell; if we don't have any, we don't have a
+ // cursor/selection either
+ SwPaM* pCursor = nullptr;
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr && !pCursorShell->IsTableMode() )
+ {
+ SwFEShell *pFESh = dynamic_cast<SwFEShell*>( pCursorShell);
+ if( !pFESh ||
+ !(pFESh->IsFrameSelected() || pFESh->IsObjSelected() > 0) )
+ {
+ // get the selection, and test whether it affects our text node
+ pCursor = pCursorShell->GetCursor( false /* ??? */ );
+ }
+ }
+
+ return pCursor;
+}
+
+//Return the selected state of the object.
+//when the object's anchor are in the selection cursor, we should return true.
+bool SwAccessibleFrameBase::GetSelectedState( )
+{
+ SolarMutexGuard aGuard;
+
+ if(GetMap()->IsDocumentSelAll())
+ {
+ return true;
+ }
+
+ // SELECTED.
+ SwFlyFrame* pFlyFrame = getFlyFrame();
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+ if( !pPos )
+ return false;
+ int nIndex = pPos->GetContentIndex();
+ if( pPos->GetNode().GetTextNode() )
+ {
+ SwPaM* pCursor = GetCursor();
+ if( pCursor != nullptr )
+ {
+ const SwTextNode* pNode = pPos->GetNode().GetTextNode();
+ SwNodeOffset nHere = pNode->GetIndex();
+
+ // iterate over ring
+ SwPaM* pRingStart = pCursor;
+ do
+ {
+ // ignore, if no mark
+ if( pCursor->HasMark() )
+ {
+ // check whether nHere is 'inside' pCursor
+ SwPosition* pStart = pCursor->Start();
+ SwNodeOffset nStartIndex = pStart->GetNodeIndex();
+ SwPosition* pEnd = pCursor->End();
+ SwNodeOffset nEndIndex = pEnd->GetNodeIndex();
+ if( ( nHere >= nStartIndex ) && (nHere <= nEndIndex) )
+ {
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ if( ((nHere == nStartIndex) && (nIndex >= pStart->GetContentIndex())) || (nHere > nStartIndex) )
+ if( ((nHere == nEndIndex) && (nIndex < pEnd->GetContentIndex())) || (nHere < nEndIndex) )
+ return true;
+ }
+ else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+ return true;
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ if (IsDestroyFrameAnchoredAtChar(*pPos, *pStart, *pEnd))
+ {
+ return true;
+ }
+ }
+ break;
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+
+ // next PaM in ring
+ pCursor = pCursor->GetNext();
+ }
+ while( pCursor != pRingStart );
+ }
+ }
+ return false;
+}
+
+SwFlyFrame* SwAccessibleFrameBase::getFlyFrame() const
+{
+ SwFlyFrame* pFlyFrame = nullptr;
+
+ const SwFrame* pFrame = GetFrame();
+ assert(pFrame);
+ if( pFrame->IsFlyFrame() )
+ {
+ pFlyFrame = static_cast<SwFlyFrame*>( const_cast<SwFrame*>( pFrame ) );
+ }
+
+ return pFlyFrame;
+}
+
+bool SwAccessibleFrameBase::SetSelectedState( bool )
+{
+ bool bParaSelected = GetSelectedState() || IsSelected();
+
+ if (m_isSelectedInDoc != bParaSelected)
+ {
+ m_isSelectedInDoc = bParaSelected;
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bParaSelected );
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframebase.hxx b/sw/source/core/access/accframebase.hxx
new file mode 100644
index 0000000000..1487073c3b
--- /dev/null
+++ b/sw/source/core/access/accframebase.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAMEBASE_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAMEBASE_HXX
+
+#include "acccontext.hxx"
+#include <svl/listener.hxx>
+#include <ndtyp.hxx>
+
+class SwFlyFrame;
+
+class SwAccessibleFrameBase : public SwAccessibleContext, public SvtListener
+{
+ bool m_bIsSelected; // protected by base class mutex
+ bool IsSelected();
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets SELECTABLE(1), SELECTED(+),
+ // FOCUSABLE(1) and FOCUSED(+)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+ SwFlyFrame* getFlyFrame() const;
+ bool GetSelectedState( );
+ SwPaM* GetCursor();
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessibleFrameBase() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleFrameBase(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame *pFlyFrame );
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ static SwNodeType GetNodeType( const SwFlyFrame *pFlyFrame );
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+ virtual bool SetSelectedState( bool bSelected ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobj.cxx b/sw/source/core/access/accfrmobj.cxx
new file mode 100644
index 0000000000..f78dc2d155
--- /dev/null
+++ b/sw/source/core/access/accfrmobj.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "accfrmobj.hxx"
+
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <pagefrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <dflyobj.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <dcontact.hxx>
+
+#include <vcl/window.hxx>
+
+namespace sw::access {
+
+SwAccessibleChild::SwAccessibleChild()
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{}
+
+SwAccessibleChild::SwAccessibleChild( const SdrObject* pDrawObj )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pDrawObj );
+}
+
+SwAccessibleChild::SwAccessibleChild( const SwFrame* pFrame )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pFrame );
+}
+
+SwAccessibleChild::SwAccessibleChild( vcl::Window* pWindow )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pWindow );
+}
+
+SwAccessibleChild::SwAccessibleChild( const SwFrame* pFrame,
+ const SdrObject* pDrawObj,
+ vcl::Window* pWindow )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ if ( pFrame )
+ {
+ Init( pFrame );
+ }
+ else if ( pDrawObj )
+ {
+ Init( pDrawObj );
+ }
+ else if ( pWindow )
+ {
+ Init( pWindow );
+ }
+ OSL_ENSURE( (!pFrame || pFrame == mpFrame) &&
+ (!pDrawObj || pDrawObj == mpDrawObj) &&
+ (!pWindow || pWindow == mpWindow),
+ "invalid frame/object/window combination" );
+
+}
+
+SwAccessibleChild::~SwAccessibleChild() = default;
+
+void SwAccessibleChild::Init( const SdrObject* pDrawObj )
+{
+ mpDrawObj = pDrawObj;
+ const SwVirtFlyDrawObj* pFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(mpDrawObj);
+ mpFrame = pFlyDrawObj ? pFlyDrawObj->GetFlyFrame() : nullptr;
+ mpWindow = nullptr;
+}
+
+void SwAccessibleChild::Init( const SwFrame* pFrame )
+{
+ mpFrame = pFrame;
+ mpDrawObj = mpFrame && mpFrame->IsFlyFrame()
+ ? static_cast < const SwFlyFrame * >( mpFrame )->GetVirtDrawObj()
+ : nullptr;
+ mpWindow = nullptr;
+}
+
+void SwAccessibleChild::Init( vcl::Window* pWindow )
+{
+ mpWindow = pWindow;
+ mpFrame = nullptr;
+ mpDrawObj = nullptr;
+}
+
+bool SwAccessibleChild::IsAccessible( bool bPagePreview ) const
+{
+ bool bRet( false );
+
+ if ( mpFrame )
+ {
+ bRet = mpFrame->IsAccessibleFrame() &&
+ ( !mpFrame->IsCellFrame() ||
+ static_cast<const SwCellFrame *>( mpFrame )->GetTabBox()->GetSttNd() != nullptr ) &&
+ !mpFrame->IsInCoveredCell() &&
+ ( bPagePreview ||
+ !mpFrame->IsPageFrame() );
+ }
+ else if ( mpDrawObj )
+ {
+ bRet = true;
+ }
+ else if ( mpWindow )
+ {
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool SwAccessibleChild::IsBoundAsChar() const
+{
+ bool bRet( false );
+
+ if ( mpFrame )
+ {
+ bRet = mpFrame->IsFlyFrame() &&
+ static_cast< const SwFlyFrame *>(mpFrame)->IsFlyInContentFrame();
+ }
+ else if ( mpDrawObj )
+ {
+ const SwFrameFormat* pFrameFormat = ::FindFrameFormat( mpDrawObj );
+ bRet = pFrameFormat
+ && (RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId());
+ }
+ else if ( mpWindow )
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( const SdrObject* pDrawObj )
+{
+ Init( pDrawObj );
+ return *this;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( const SwFrame* pFrame )
+{
+ Init( pFrame );
+ return *this;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( vcl::Window* pWindow )
+{
+ Init( pWindow );
+ return *this;
+}
+
+bool SwAccessibleChild::IsValid() const
+{
+ return mpFrame != nullptr ||
+ mpDrawObj != nullptr ||
+ mpWindow != nullptr;
+}
+
+bool SwAccessibleChild::IsVisibleChildrenOnly() const
+{
+ bool bRet( false );
+
+ if ( !mpFrame )
+ {
+ bRet = true;
+ }
+ else
+ {
+ bRet = mpFrame->IsRootFrame() ||
+ !( mpFrame->IsTabFrame() ||
+ mpFrame->IsInTab() ||
+ ( IsBoundAsChar() &&
+ static_cast<const SwFlyFrame*>(mpFrame)->GetAnchorFrame()->IsInTab() ) );
+ }
+
+ return bRet;
+}
+
+SwRect SwAccessibleChild::GetBox( const SwAccessibleMap& rAccMap ) const
+{
+ SwRect aBox;
+
+ if ( mpFrame )
+ {
+ if ( mpFrame->IsPageFrame() &&
+ static_cast< const SwPageFrame * >( mpFrame )->IsEmptyPage() )
+ {
+ aBox = SwRect( mpFrame->getFrameArea().Left(), mpFrame->getFrameArea().Top()-1, 1, 1 );
+ }
+ else if ( mpFrame->IsTabFrame() )
+ {
+ aBox = mpFrame->getFrameArea();
+ aBox.Intersection( mpFrame->GetUpper()->getFrameArea() );
+ }
+ else
+ {
+ aBox = mpFrame->getFrameArea();
+ }
+ }
+ else if( mpDrawObj )
+ {
+ const SwContact* const pContact = ::GetUserCall(mpDrawObj);
+ // assume that a) the SwVirt* objects that don't have this are handled
+ // by the mpFrame case above b) for genuine SdrObject this must be set
+ // if it's connected to layout
+ assert(dynamic_cast<SwDrawContact const*>(pContact));
+ SwPageFrame const*const pPage(const_cast<SwAnchoredObject *>(
+ pContact->GetAnchoredObj(mpDrawObj))->FindPageFrameOfAnchor());
+ if (pPage) // may end up here with partial layout -> not visible
+ {
+ aBox = SwRect( mpDrawObj->GetCurrentBoundRect() );
+ // tdf#91260 drawing object may be partially off-page
+ aBox.Intersection(pPage->getFrameArea());
+ }
+ }
+ else if ( mpWindow )
+ {
+ vcl::Window *pWin = rAccMap.GetShell()->GetWin();
+ if (pWin)
+ {
+ aBox = SwRect( pWin->PixelToLogic(
+ tools::Rectangle( mpWindow->GetPosPixel(),
+ mpWindow->GetSizePixel() ) ) );
+ }
+ }
+
+ return aBox;
+}
+
+SwRect SwAccessibleChild::GetBounds( const SwAccessibleMap& rAccMap ) const
+{
+ SwRect aBound;
+
+ if( mpFrame )
+ {
+ if( mpFrame->IsPageFrame() &&
+ static_cast< const SwPageFrame * >( mpFrame )->IsEmptyPage() )
+ {
+ aBound = SwRect( mpFrame->getFrameArea().Left(), mpFrame->getFrameArea().Top()-1, 0, 0 );
+ }
+ else
+ aBound = mpFrame->GetPaintArea();
+ }
+ else if( mpDrawObj )
+ {
+ aBound = GetBox( rAccMap );
+ }
+ else if ( mpWindow )
+ {
+ aBound = GetBox( rAccMap );
+ }
+
+ return aBound;
+}
+
+bool SwAccessibleChild::AlwaysIncludeAsChild() const
+{
+ bool bAlwaysIncludedAsChild( false );
+
+ if ( mpWindow )
+ {
+ bAlwaysIncludedAsChild = true;
+ }
+
+ return bAlwaysIncludedAsChild;
+}
+
+const SwFrame* SwAccessibleChild::GetParent( const bool bInPagePreview ) const
+{
+ const SwFrame* pParent( nullptr );
+
+ if ( mpFrame )
+ {
+ if( mpFrame->IsFlyFrame() )
+ {
+ const SwFlyFrame* pFly = static_cast< const SwFlyFrame *>( mpFrame );
+ if( pFly->IsFlyInContentFrame() )
+ {
+ // For RndStdIds::FLY_AS_CHAR the parent is the anchor
+ pParent = pFly->GetAnchorFrame();
+ OSL_ENSURE( SwAccessibleChild( pParent ).IsAccessible( bInPagePreview ),
+ "parent is not accessible" );
+ }
+ else
+ {
+ // In any other case the parent is the root frm
+ // (in page preview, the page frame)
+ if( bInPagePreview )
+ pParent = pFly->FindPageFrame();
+ else
+ pParent = pFly->getRootFrame();
+ }
+ }
+ else
+ {
+ SwAccessibleChild aUpper( mpFrame->GetUpper() );
+ while( aUpper.GetSwFrame() && !aUpper.IsAccessible(bInPagePreview) )
+ {
+ aUpper = aUpper.GetSwFrame()->GetUpper();
+ }
+ pParent = aUpper.GetSwFrame();
+ }
+ }
+ else if( mpDrawObj )
+ {
+ const SwDrawContact *pContact =
+ static_cast< const SwDrawContact* >( GetUserCall( mpDrawObj ) );
+ OSL_ENSURE( pContact, "sdr contact is missing" );
+ if( pContact )
+ {
+ const SwFrameFormat *pFrameFormat = pContact->GetFormat();
+ OSL_ENSURE( pFrameFormat, "frame format is missing" );
+ if( pFrameFormat && RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId() )
+ {
+ // For RndStdIds::FLY_AS_CHAR the parent is the anchor
+ pParent = pContact->GetAnchorFrame();
+ OSL_ENSURE( SwAccessibleChild( pParent ).IsAccessible( bInPagePreview ),
+ "parent is not accessible" );
+
+ }
+ else
+ {
+ // In any other case the parent is the root frm
+ SwFrame const*const pAnchor(pContact->GetAnchorFrame());
+ if (pAnchor) // null if object removed from layout
+ {
+ if (bInPagePreview)
+ pParent = pAnchor->FindPageFrame();
+ else
+ pParent = pAnchor->getRootFrame();
+ }
+ }
+ }
+ }
+ else if ( mpWindow )
+ {
+ css::uno::Reference < css::accessibility::XAccessible > xAcc =
+ mpWindow->GetAccessible();
+ if ( xAcc.is() )
+ {
+ css::uno::Reference < css::accessibility::XAccessibleContext > xAccContext =
+ xAcc->getAccessibleContext();
+ if ( xAccContext.is() )
+ {
+ css::uno::Reference < css::accessibility::XAccessible > xAccParent =
+ xAccContext->getAccessibleParent();
+ if ( xAccParent.is() )
+ {
+ SwAccessibleContext* pAccParentImpl =
+ dynamic_cast< SwAccessibleContext *>( xAccParent.get() );
+ if ( pAccParentImpl )
+ {
+ pParent = pAccParentImpl->GetFrame();
+ }
+ }
+ }
+ }
+ }
+
+ return pParent;
+}
+
+} // namespace sw::access
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobj.hxx b/sw/source/core/access/accfrmobj.hxx
new file mode 100644
index 0000000000..5e2cc36f87
--- /dev/null
+++ b/sw/source/core/access/accfrmobj.hxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJ_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJ_HXX
+
+#include <vcl/vclptr.hxx>
+
+class SwAccessibleMap;
+class SwFrame;
+class SdrObject;
+namespace vcl { class Window; }
+class SwRect;
+
+namespace sw::access {
+
+class SwAccessibleChild
+{
+ public:
+ SwAccessibleChild();
+ ~SwAccessibleChild();
+ explicit SwAccessibleChild( const SdrObject* pDrawObj );
+ explicit SwAccessibleChild( const SwFrame* pFrame );
+ explicit SwAccessibleChild( vcl::Window* pWindow );
+ SwAccessibleChild( const SwFrame* pFrame,
+ const SdrObject* pDrawObj,
+ vcl::Window* pWindow );
+
+ SwAccessibleChild(SwAccessibleChild const &) = default;
+ SwAccessibleChild(SwAccessibleChild &&) = default;
+ SwAccessibleChild & operator =(SwAccessibleChild const &) = default;
+ SwAccessibleChild & operator =(SwAccessibleChild &&) = default;
+
+ SwAccessibleChild& operator=( const SdrObject* pDrawObj );
+ SwAccessibleChild& operator=( const SwFrame* pFrame );
+ SwAccessibleChild& operator=( vcl::Window* pWindow );
+
+ bool operator==( const SwAccessibleChild& r ) const
+ {
+ return mpFrame == r.mpFrame &&
+ mpDrawObj == r.mpDrawObj &&
+ mpWindow == r.mpWindow;
+ }
+
+ bool IsValid() const;
+
+ const SwFrame* GetSwFrame() const { return mpFrame; }
+ const SdrObject* GetDrawObject() const { return mpDrawObj; }
+ vcl::Window* GetWindow() const { return mpWindow; }
+
+ const SwFrame* GetParent( const bool bInPagePreview ) const;
+
+ bool IsAccessible( bool bPagePreview ) const;
+ bool IsBoundAsChar() const;
+
+ bool IsVisibleChildrenOnly() const;
+ SwRect GetBox( const SwAccessibleMap& rAccMap ) const;
+ SwRect GetBounds( const SwAccessibleMap& rAccMap ) const;
+
+ /** indicating, if accessible child is included even, if the corresponding
+ object is not visible. */
+ bool AlwaysIncludeAsChild() const;
+
+ private:
+ const SwFrame* mpFrame;
+ const SdrObject* mpDrawObj;
+ VclPtr<vcl::Window> mpWindow;
+
+ void Init( const SdrObject* pDrawObj );
+ void Init( const SwFrame* pFrame );
+ void Init( vcl::Window* pWindow );
+};
+
+} // eof of namespace sw::access
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjmap.cxx b/sw/source/core/access/accfrmobjmap.cxx
new file mode 100644
index 0000000000..5da8dad1ad
--- /dev/null
+++ b/sw/source/core/access/accfrmobjmap.cxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "accfrmobjmap.hxx"
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pagefrm.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+
+#include <svx/svdobj.hxx>
+
+using namespace sw::access;
+
+SwAccessibleChildMap::SwAccessibleChildMap( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : mnHellId( rAccMap.GetShell()->GetDoc()->getIDocumentDrawModelAccess().GetHellId() )
+ , mnControlsId( rAccMap.GetShell()->GetDoc()->getIDocumentDrawModelAccess().GetControlsId() )
+{
+ const bool bVisibleChildrenOnly = SwAccessibleChild( &rFrame ).IsVisibleChildrenOnly();
+
+ sal_uInt32 nPos = 0;
+ SwAccessibleChild aLower( rFrame.GetLower() );
+ while( aLower.GetSwFrame() )
+ {
+ if ( !bVisibleChildrenOnly ||
+ aLower.AlwaysIncludeAsChild() ||
+ aLower.GetBox( rAccMap ).Overlaps( rVisArea ) )
+ {
+ insert( nPos++, SwAccessibleChildMapKey::TEXT, aLower );
+ }
+
+ aLower = aLower.GetSwFrame()->GetNext();
+ }
+
+ if ( rFrame.IsPageFrame() )
+ {
+ OSL_ENSURE( bVisibleChildrenOnly, "page frame within tab frame???" );
+ const SwPageFrame *pPgFrame =
+ static_cast< const SwPageFrame * >( &rFrame );
+ const SwSortedObjs *pObjs = pPgFrame->GetSortedObjs();
+ if ( pObjs )
+ {
+ for(const SwAnchoredObject* pObj : *pObjs)
+ {
+ aLower = pObj->GetDrawObj();
+ if ( aLower.GetBox( rAccMap ).Overlaps( rVisArea ) )
+ {
+ insert( aLower.GetDrawObject(), aLower );
+ }
+ }
+ }
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs *pObjs = rFrame.GetDrawObjs();
+ if ( pObjs )
+ {
+ for(const SwAnchoredObject* pObj : *pObjs)
+ {
+ aLower = pObj->GetDrawObj();
+ if ( aLower.IsBoundAsChar() &&
+ ( !bVisibleChildrenOnly ||
+ aLower.AlwaysIncludeAsChild() ||
+ aLower.GetBox( rAccMap ).Overlaps( rVisArea ) ) )
+ {
+ insert( aLower.GetDrawObject(), aLower );
+ }
+ }
+ }
+
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ rAccMap.GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ if ( pAccImpl &&
+ pAccImpl->HasAdditionalAccessibleChildren() )
+ {
+ std::vector< vcl::Window* > aAdditionalChildren;
+ pAccImpl->GetAdditionalAccessibleChildren( &aAdditionalChildren );
+
+ sal_Int32 nCounter( 0 );
+ for ( const auto& rpChild : aAdditionalChildren )
+ {
+ aLower = rpChild;
+ insert( ++nCounter, SwAccessibleChildMapKey::XWINDOW, aLower );
+ }
+ }
+ }
+ }
+ }
+}
+
+std::pair< SwAccessibleChildMap::iterator, bool > SwAccessibleChildMap::insert(
+ const sal_uInt32 nPos,
+ const SwAccessibleChildMapKey::LayerId eLayerId,
+ const SwAccessibleChild& rLower )
+{
+ SwAccessibleChildMapKey aKey( eLayerId, nPos );
+ return emplace( aKey, rLower );
+}
+
+std::pair< SwAccessibleChildMap::iterator, bool > SwAccessibleChildMap::insert(
+ const SdrObject *pObj,
+ const SwAccessibleChild& rLower )
+{
+ const SdrLayerID nLayer = pObj->GetLayer();
+ SwAccessibleChildMapKey::LayerId eLayerId =
+ (mnHellId == nLayer)
+ ? SwAccessibleChildMapKey::HELL
+ : ( (mnControlsId == nLayer)
+ ? SwAccessibleChildMapKey::CONTROLS
+ : SwAccessibleChildMapKey::HEAVEN );
+ SwAccessibleChildMapKey aKey( eLayerId, pObj->GetOrdNum() );
+ return emplace( aKey, rLower );
+}
+
+bool SwAccessibleChildMap::IsSortingRequired( const SwFrame& rFrame )
+{
+ return ( rFrame.IsPageFrame() &&
+ static_cast< const SwPageFrame& >( rFrame ).GetSortedObjs() ) ||
+ ( rFrame.IsTextFrame() &&
+ rFrame.GetDrawObjs() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjmap.hxx b/sw/source/core/access/accfrmobjmap.hxx
new file mode 100644
index 0000000000..ed4ecafece
--- /dev/null
+++ b/sw/source/core/access/accfrmobjmap.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJMAP_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJMAP_HXX
+
+#include <tools/gen.hxx>
+#include <svx/svdtypes.hxx>
+#include "accfrmobj.hxx"
+#include <map>
+
+class SwAccessibleMap;
+class SwRect;
+class SwFrame;
+class SdrObject;
+
+class SwAccessibleChildMapKey
+{
+public:
+ enum LayerId { INVALID, HELL, TEXT, HEAVEN, CONTROLS, XWINDOW };
+
+ SwAccessibleChildMapKey()
+ : m_eLayerId( INVALID )
+ , m_nOrdNum( 0 )
+ , m_nPosNum( 0, 0 )
+ {}
+
+ SwAccessibleChildMapKey( LayerId eId, sal_uInt32 nOrd )
+ : m_eLayerId( eId )
+ , m_nOrdNum( nOrd )
+ , m_nPosNum( 0, 0 )
+ {}
+
+ bool operator()( const SwAccessibleChildMapKey& r1,
+ const SwAccessibleChildMapKey& r2 ) const
+ {
+ if(r1.m_eLayerId == r2.m_eLayerId)
+ {
+ if(r1.m_nPosNum == r2.m_nPosNum)
+ return r1.m_nOrdNum < r2.m_nOrdNum;
+ else
+ {
+ if(r1.m_nPosNum.getY() == r2.m_nPosNum.getY())
+ return r1.m_nPosNum.getX() < r2.m_nPosNum.getX();
+ else
+ return r1.m_nPosNum.getY() < r2.m_nPosNum.getY();
+ }
+ }
+ else
+ return r1.m_eLayerId < r2.m_eLayerId;
+ }
+
+ /* MT: Need to get this position parameter stuff in dev300 somehow...
+ //This methods are used to insert an object to the map, adding a position parameter.
+ std::pair< iterator, bool > insert( sal_uInt32 nOrd, Point nPos,
+ const SwFrameOrObj& rLower );
+ std::pair< iterator, bool > insert( const SdrObject *pObj,
+ const SwFrameOrObj& rLower,
+ const SwDoc *pDoc,
+ Point nPos);
+ */
+
+private:
+ LayerId m_eLayerId;
+ sal_uInt32 m_nOrdNum;
+ Point m_nPosNum;
+};
+
+
+class SwAccessibleChildMap
+{
+public:
+ typedef SwAccessibleChildMapKey key_type;
+ typedef sw::access::SwAccessibleChild mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwAccessibleChildMapKey key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_reverse_iterator const_reverse_iterator;
+
+private:
+ const SdrLayerID mnHellId;
+ const SdrLayerID mnControlsId;
+ std::map<key_type,mapped_type,key_compare> maMap;
+
+ std::pair< iterator, bool > insert( const sal_uInt32 nPos,
+ const SwAccessibleChildMapKey::LayerId eLayerId,
+ const sw::access::SwAccessibleChild& rLower );
+ std::pair< iterator, bool > insert( const SdrObject* pObj,
+ const sw::access::SwAccessibleChild& rLower );
+
+public:
+ SwAccessibleChildMap( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap );
+
+ static bool IsSortingRequired( const SwFrame& rFrame );
+
+ const_iterator cbegin() const { return maMap.cbegin(); }
+ const_iterator cend() const { return maMap.cend(); }
+ const_reverse_iterator crbegin() const { return maMap.crbegin(); }
+ const_reverse_iterator crend() const { return maMap.crend(); }
+
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjslist.cxx b/sw/source/core/access/accfrmobjslist.cxx
new file mode 100644
index 0000000000..c62987798c
--- /dev/null
+++ b/sw/source/core/access/accfrmobjslist.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "accfrmobjslist.hxx"
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <pagefrm.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+
+using namespace ::sw::access;
+
+SwAccessibleChildSList_const_iterator::SwAccessibleChildSList_const_iterator(
+ const SwAccessibleChildSList& rLst,
+ SwAccessibleMap& rAccMap )
+ : m_rList( rLst ),
+ m_aCurr( m_rList.GetFrame().GetLower() ),
+ m_nNextObj( 0 )
+{
+ if( !m_aCurr.GetSwFrame() )
+ {
+ const SwFrame& rFrame = m_rList.GetFrame();
+ if( rFrame.IsPageFrame() )
+ {
+ const SwPageFrame& rPgFrame = static_cast< const SwPageFrame& >( rFrame );
+ const SwSortedObjs *pObjs = rPgFrame.GetSortedObjs();
+ if( pObjs && pObjs->size() )
+ {
+ m_aCurr = (*pObjs)[m_nNextObj++]->GetDrawObj();
+ }
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs *pObjs = rFrame.GetDrawObjs();
+ if ( pObjs && pObjs->size() )
+ {
+ m_aCurr = (*pObjs)[m_nNextObj++]->GetDrawObj();
+ while( m_aCurr.IsValid() && !m_aCurr.IsBoundAsChar() )
+ {
+ m_aCurr = (m_nNextObj < pObjs->size())
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ }
+ if ( !m_aCurr.IsValid() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ rAccMap.GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ m_aCurr = SwAccessibleChild( pAccImpl->GetAdditionalAccessibleChild( 0 ) );
+ ++m_nNextObj;
+ }
+ }
+ }
+ }
+
+ if( m_rList.IsVisibleChildrenOnly() )
+ {
+ // Find the first visible
+ while( m_aCurr.IsValid() &&
+ !m_aCurr.AlwaysIncludeAsChild() &&
+ !m_aCurr.GetBox( rAccMap ).Overlaps( m_rList.GetVisArea() ) )
+ {
+ next();
+ }
+ }
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::next()
+{
+ bool bNextTaken( true );
+ if( m_aCurr.GetDrawObject() || m_aCurr.GetWindow() )
+ {
+ bNextTaken = false;
+ }
+ else if( m_aCurr.GetSwFrame() )
+ {
+ m_aCurr = m_aCurr.GetSwFrame()->GetNext();
+ if( !m_aCurr.GetSwFrame() )
+ {
+ bNextTaken = false;
+ }
+ }
+
+ if( !bNextTaken )
+ {
+ const SwFrame& rFrame = m_rList.GetFrame();
+ if( rFrame.IsPageFrame() )
+ {
+ const SwPageFrame& rPgFrame = static_cast< const SwPageFrame& >( rFrame );
+ const SwSortedObjs *pObjs = rPgFrame.GetSortedObjs();
+ m_aCurr = ( pObjs && m_nNextObj < pObjs->size() )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs* pObjs = rFrame.GetDrawObjs();
+ const size_t nObjsCount = pObjs ? pObjs->size() : 0;
+ m_aCurr = ( pObjs && m_nNextObj < nObjsCount )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ while( m_aCurr.IsValid() && !m_aCurr.IsBoundAsChar() )
+ {
+ m_aCurr = ( m_nNextObj < nObjsCount )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ if ( !m_aCurr.IsValid() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ m_rList.GetAccMap().GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ m_aCurr = SwAccessibleChild( pAccImpl->GetAdditionalAccessibleChild( m_nNextObj - nObjsCount ) );
+ ++m_nNextObj;
+ }
+ }
+ }
+ }
+
+ return *this;
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::next_visible()
+{
+ next();
+ while( m_aCurr.IsValid() &&
+ !m_aCurr.AlwaysIncludeAsChild() &&
+ !m_aCurr.GetBox( m_rList.GetAccMap() ).Overlaps( m_rList.GetVisArea() ) )
+ {
+ next();
+ }
+
+ return *this;
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::operator++()
+{
+ return m_rList.IsVisibleChildrenOnly() ? next_visible() : next();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjslist.hxx b/sw/source/core/access/accfrmobjslist.hxx
new file mode 100644
index 0000000000..9df293d71e
--- /dev/null
+++ b/sw/source/core/access/accfrmobjslist.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJSLIST_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJSLIST_HXX
+
+#include "accfrmobj.hxx"
+#include <swrect.hxx>
+
+class SwAccessibleMap;
+class SwAccessibleChildSList;
+
+class SwAccessibleChildSList_const_iterator
+{
+private:
+ friend class SwAccessibleChildSList;
+
+ const SwAccessibleChildSList& m_rList; // The frame we are iterating over
+ sw::access::SwAccessibleChild m_aCurr; // The current object
+ size_t m_nNextObj; // The index of the current sdr object
+
+ SwAccessibleChildSList_const_iterator( const SwAccessibleChildSList& rLst )
+ : m_rList( rLst )
+ , m_nNextObj( 0 )
+ {}
+
+ SwAccessibleChildSList_const_iterator( const SwAccessibleChildSList& rLst,
+ SwAccessibleMap& rAccMap );
+
+ SwAccessibleChildSList_const_iterator& next();
+ SwAccessibleChildSList_const_iterator& next_visible();
+
+public:
+ bool operator==( const SwAccessibleChildSList_const_iterator& r ) const
+ {
+ return m_aCurr == r.m_aCurr;
+ }
+
+ bool operator!=(
+ const SwAccessibleChildSList_const_iterator& r ) const
+ {
+ return !(*this == r);
+ }
+
+ SwAccessibleChildSList_const_iterator& operator++();
+
+ const sw::access::SwAccessibleChild& operator*() const
+ {
+ return m_aCurr;
+ }
+};
+
+// An iterator to iterate over a frame's child in any order
+class SwAccessibleChildSList
+{
+ const SwRect maVisArea;
+ const SwFrame& mrFrame;
+ const bool mbVisibleChildrenOnly;
+ SwAccessibleMap& mrAccMap;
+
+public:
+ typedef SwAccessibleChildSList_const_iterator const_iterator;
+
+ SwAccessibleChildSList( const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : maVisArea()
+ , mrFrame( rFrame )
+ , mbVisibleChildrenOnly( false )
+ , mrAccMap( rAccMap )
+ {}
+
+ SwAccessibleChildSList( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : maVisArea( rVisArea )
+ , mrFrame( rFrame )
+ , mbVisibleChildrenOnly( sw::access::SwAccessibleChild( &rFrame ).IsVisibleChildrenOnly() )
+ , mrAccMap( rAccMap )
+ {
+ }
+
+ const_iterator begin() const
+ {
+ return SwAccessibleChildSList_const_iterator( *this, mrAccMap );
+ }
+
+ const_iterator end() const
+ {
+ return SwAccessibleChildSList_const_iterator( *this );
+ }
+
+ const SwFrame& GetFrame() const
+ {
+ return mrFrame;
+ }
+
+ bool IsVisibleChildrenOnly() const
+ {
+ return mbVisibleChildrenOnly;
+ }
+
+ const SwRect& GetVisArea() const
+ {
+ return maVisArea;
+ }
+
+ SwAccessibleMap& GetAccMap() const
+ {
+ return mrAccMap;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accgraphic.cxx b/sw/source/core/access/accgraphic.cxx
new file mode 100644
index 0000000000..d6d115d780
--- /dev/null
+++ b/sw/source/core/access/accgraphic.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <flyfrm.hxx>
+#include <fmturl.hxx>
+#include "accgraphic.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+SwAccessibleGraphic::SwAccessibleGraphic(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleNoTextFrame( pInitMap, AccessibleRole::GRAPHIC, pFlyFrame )
+{
+}
+
+SwAccessibleGraphic::~SwAccessibleGraphic()
+{
+}
+
+OUString SAL_CALL SwAccessibleGraphic::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleGraphic";
+}
+
+sal_Bool SAL_CALL SwAccessibleGraphic::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleGraphic::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextGraphicObject", sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleGraphic::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// Return this object's role.
+sal_Int16 SAL_CALL SwAccessibleGraphic::getAccessibleRole()
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if (aURL.GetMap())
+ return AccessibleRole::IMAGE_MAP;
+ return AccessibleRole::GRAPHIC;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accgraphic.hxx b/sw/source/core/access/accgraphic.hxx
new file mode 100644
index 0000000000..dc21201bc0
--- /dev/null
+++ b/sw/source/core/access/accgraphic.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCGRAPHIC_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCGRAPHIC_HXX
+
+#include "accnotextframe.hxx"
+
+class SwAccessibleGraphic : public SwAccessibleNoTextFrame
+{
+protected:
+ virtual ~SwAccessibleGraphic() override;
+
+public:
+ SwAccessibleGraphic(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame *pFlyFrame );
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+ /// Return this object's role.
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accheaderfooter.cxx b/sw/source/core/access/accheaderfooter.cxx
new file mode 100644
index 0000000000..d07b2c8025
--- /dev/null
+++ b/sw/source/core/access/accheaderfooter.cxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <hffrm.hxx>
+#include "accheaderfooter.hxx"
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationNameHeader
+ = u"com.sun.star.comp.Writer.SwAccessibleHeaderView";
+constexpr OUStringLiteral sImplementationNameFooter
+ = u"com.sun.star.comp.Writer.SwAccessibleFooterView";
+
+SwAccessibleHeaderFooter::SwAccessibleHeaderFooter(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwHeaderFrame* pHdFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::HEADER, pHdFrame )
+{
+ OUString sArg( OUString::number( pHdFrame->GetPhyPageNum() ) );
+ SetName( GetResource( STR_ACCESS_HEADER_NAME, &sArg ) );
+}
+
+SwAccessibleHeaderFooter::SwAccessibleHeaderFooter(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFooterFrame* pFtFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::FOOTER, pFtFrame )
+{
+ OUString sArg( OUString::number( pFtFrame->GetPhyPageNum() ) );
+ SetName( GetResource( STR_ACCESS_FOOTER_NAME, &sArg ) );
+}
+
+SwAccessibleHeaderFooter::~SwAccessibleHeaderFooter()
+{
+}
+
+OUString SAL_CALL SwAccessibleHeaderFooter::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ TranslateId pResId = AccessibleRole::HEADER == GetRole()
+ ? STR_ACCESS_HEADER_DESC
+ : STR_ACCESS_FOOTER_DESC ;
+
+ OUString sArg( GetFormattedPageNumber() );
+
+ return GetResource(pResId, &sArg);
+}
+
+OUString SAL_CALL SwAccessibleHeaderFooter::getImplementationName()
+{
+ if( AccessibleRole::HEADER == GetRole() )
+ return sImplementationNameHeader;
+ else
+ return sImplementationNameFooter;
+}
+
+sal_Bool SAL_CALL SwAccessibleHeaderFooter::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleHeaderFooter::getSupportedServiceNames()
+{
+ return { (AccessibleRole::HEADER == GetRole())?OUString("com.sun.star.text.AccessibleHeaderView"):OUString("com.sun.star.text.AccessibleFooterView"),
+ sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleHeaderFooter::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Int32 SAL_CALL SwAccessibleHeaderFooter::getBackground()
+{
+ Reference< XAccessible > xParent = getAccessibleParent();
+ if (xParent.is())
+ {
+ Reference< XAccessibleComponent > xAccContext (xParent,UNO_QUERY);
+ if(xAccContext.is())
+ {
+ return xAccContext->getBackground();
+ }
+ }
+ return SwAccessibleContext::getBackground();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accheaderfooter.hxx b/sw/source/core/access/accheaderfooter.hxx
new file mode 100644
index 0000000000..821ed98834
--- /dev/null
+++ b/sw/source/core/access/accheaderfooter.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHEADERFOOTER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHEADERFOOTER_HXX
+
+#include "acccontext.hxx"
+
+class SwHeaderFrame;
+class SwFooterFrame;
+
+class SwAccessibleHeaderFooter : public SwAccessibleContext
+{
+protected:
+ virtual ~SwAccessibleHeaderFooter() override;
+
+public:
+ SwAccessibleHeaderFooter(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwHeaderFrame* pHdFrame);
+ SwAccessibleHeaderFooter(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFooterFrame* pFtFrame);
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchyperlink.cxx b/sw/source/core/access/acchyperlink.cxx
new file mode 100644
index 0000000000..76a2721ec1
--- /dev/null
+++ b/sw/source/core/access/acchyperlink.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/accessiblekeybindinghelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/document/XLinkTargetSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <swurl.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/svapp.hxx>
+#include <txtinet.hxx>
+#include "accpara.hxx"
+#include "acchyperlink.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+
+SwAccessibleHyperlink::SwAccessibleHyperlink(const SwTextAttr & rTextAttr,
+ SwAccessibleParagraph & rAccPara,
+ sal_Int32 const nStt, sal_Int32 const nEnd)
+ : m_pHyperlink(const_cast<SwFormatINetFormat*>(&rTextAttr.GetINetFormat()))
+ , m_xParagraph(&rAccPara)
+ , m_nStartIndex( nStt )
+ , m_nEndIndex( nEnd )
+{
+ StartListening(m_pHyperlink->GetNotifier());
+}
+
+SwAccessibleHyperlink::~SwAccessibleHyperlink()
+{
+ Invalidate(); // with SolarMutex!
+}
+
+// when the pool item dies, invalidate! this is the only reason for Listener...
+void SwAccessibleHyperlink::Notify(SfxHint const& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ Invalidate();
+ }
+}
+
+// both the parent SwAccessibleParagraph and the pool-item must be valid
+const SwFormatINetFormat *SwAccessibleHyperlink::GetTextAttr() const
+{
+ return (m_xParagraph.is() && m_xParagraph->GetMap())
+ ? m_pHyperlink
+ : nullptr;
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getAccessibleActionCount()
+{
+ return isValid() ? 1 : 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleHyperlink::doAccessibleAction( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ bool bRet = false;
+
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ SwFormatINetFormat const*const pINetFormat = GetTextAttr();
+ if (pINetFormat && !pINetFormat->GetValue().isEmpty())
+ {
+ SwViewShell *pVSh = m_xParagraph->GetShell();
+ if (pVSh)
+ {
+ LoadURL(*pVSh, pINetFormat->GetValue(), LoadUrlFlags::NONE,
+ pINetFormat->GetTargetFrame());
+ const SwTextINetFormat *const pTextAttr = pINetFormat->GetTextINetFormat();
+ if (pTextAttr)
+ {
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited(true);
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid(true);
+ }
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleHyperlink::getAccessibleActionDescription(
+ sal_Int32 nIndex )
+{
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+
+ SolarMutexGuard g;
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ return pINetFormat->GetValue();
+ }
+
+ return OUString();
+}
+
+uno::Reference< XAccessibleKeyBinding > SAL_CALL
+ SwAccessibleHyperlink::getAccessibleActionKeyBinding( sal_Int32 )
+{
+ uno::Reference< XAccessibleKeyBinding > xKeyBinding;
+
+ if( isValid() )
+ {
+ rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper =
+ new ::comphelper::OAccessibleKeyBindingHelper();
+ xKeyBinding = pKeyBindingHelper;
+
+ awt::KeyStroke aKeyStroke;
+ aKeyStroke.Modifiers = 0;
+ aKeyStroke.KeyCode = KEY_RETURN;
+ aKeyStroke.KeyChar = 0;
+ aKeyStroke.KeyFunc = 0;
+ pKeyBindingHelper->AddKeyBinding( aKeyStroke );
+ }
+
+ return xKeyBinding;
+}
+
+// XAccessibleHyperlink
+uno::Any SAL_CALL SwAccessibleHyperlink::getAccessibleActionAnchor(
+ sal_Int32 nIndex)
+{
+ SolarMutexGuard g;
+
+ uno::Any aRet;
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ OUString text( m_xParagraph->GetString() );
+ OUString retText = text.copy(m_nStartIndex, m_nEndIndex - m_nStartIndex);
+ aRet <<= retText;
+ return aRet;
+}
+
+uno::Any SAL_CALL SwAccessibleHyperlink::getAccessibleActionObject(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ OUString retText;
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ retText = pINetFormat->GetValue();
+ }
+ uno::Any aRet;
+ aRet <<= retText;
+ return aRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getStartIndex()
+{
+ return m_nStartIndex;
+}
+
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getEndIndex()
+{
+ return m_nEndIndex;
+}
+
+sal_Bool SAL_CALL SwAccessibleHyperlink::isValid( )
+{
+ SolarMutexGuard aGuard;
+ if (m_xParagraph.is())
+ {
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ OUString const sText(pINetFormat->GetValue());
+ sal_Int32 nPos = sText.indexOf("#");
+ if (nPos==0)//document link
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+ if( ! xFactory.is() )
+ return false;
+ uno::Reference< css::frame::XDesktop > xDesktop( xFactory->createInstance( "com.sun.star.frame.Desktop" ),
+ uno::UNO_QUERY );
+ if( !xDesktop.is() )
+ return false;
+ uno::Reference< lang::XComponent > xComp = xDesktop->getCurrentComponent();
+ if( !xComp.is() )
+ return false;
+ uno::Reference< css::document::XLinkTargetSupplier > xLTS(xComp, uno::UNO_QUERY);
+ if ( !xLTS.is())
+ return false;
+
+ uno::Reference< css::container::XNameAccess > xLinks = xLTS->getLinks();
+ uno::Reference< css::container::XNameAccess > xSubLinks;
+ const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
+
+ for( const OUString& aLink : aNames )
+ {
+ uno::Any aAny = xLinks->getByName( aLink );
+ aAny >>= xSubLinks;
+ if (xSubLinks->hasByName(sText.copy(1)) )
+ return true;
+ }
+ }
+ else//internet
+ return true;
+ }
+ }//xpara valid
+ return false;
+}
+
+void SwAccessibleHyperlink::Invalidate()
+{
+ SolarMutexGuard aGuard;
+ m_xParagraph = nullptr;
+ m_pHyperlink = nullptr;
+ EndListeningAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchyperlink.hxx b/sw/source/core/access/acchyperlink.hxx
new file mode 100644
index 0000000000..68116d99d8
--- /dev/null
+++ b/sw/source/core/access/acchyperlink.hxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERLINK_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERLINK_HXX
+
+#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/listener.hxx>
+
+class SwFormatINetFormat;
+class SwAccessibleParagraph;
+class SwTextAttr;
+
+class SwAccessibleHyperlink
+ : public ::cppu::WeakImplHelper<css::accessibility::XAccessibleHyperlink>
+ , public SvtListener
+{
+ friend class SwAccessibleParagraph;
+ friend class SwAccessibleHyperTextData;
+ SwFormatINetFormat * m_pHyperlink;
+ ::rtl::Reference< SwAccessibleParagraph > m_xParagraph;
+ sal_Int32 m_nStartIndex;
+ sal_Int32 m_nEndIndex;
+
+ SwAccessibleHyperlink(const SwTextAttr &,
+ SwAccessibleParagraph &,
+ sal_Int32 nStt, sal_Int32 nEnd );
+ virtual ~SwAccessibleHyperlink() override;
+
+ const SwFormatINetFormat* GetTextAttr() const;
+ void Invalidate();
+
+ virtual void Notify(SfxHint const& rHint) override;
+
+public:
+ // XAccessibleAction
+ virtual sal_Int32 SAL_CALL getAccessibleActionCount() override;
+ virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override;
+ virtual OUString SAL_CALL getAccessibleActionDescription(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleKeyBinding > SAL_CALL
+ getAccessibleActionKeyBinding( sal_Int32 nIndex ) override;
+
+ // XAccessibleHyperlink
+ virtual css::uno::Any SAL_CALL getAccessibleActionAnchor(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Any SAL_CALL getAccessibleActionObject(
+ sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getStartIndex() override;
+ virtual sal_Int32 SAL_CALL getEndIndex() override;
+ virtual sal_Bool SAL_CALL isValid( ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchypertextdata.cxx b/sw/source/core/access/acchypertextdata.cxx
new file mode 100644
index 0000000000..918bcc6313
--- /dev/null
+++ b/sw/source/core/access/acchypertextdata.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "acchyperlink.hxx"
+#include "acchypertextdata.hxx"
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+SwAccessibleHyperTextData::SwAccessibleHyperTextData()
+{
+}
+
+SwAccessibleHyperTextData::~SwAccessibleHyperTextData()
+{
+ iterator aIter = begin();
+ while( aIter != end() )
+ {
+ Reference < XAccessibleHyperlink > xTmp = (*aIter).second;
+ if( xTmp.is() )
+ {
+ SwAccessibleHyperlink *pTmp =
+ static_cast< SwAccessibleHyperlink * >( xTmp.get() );
+ pTmp->Invalidate();
+ }
+ ++aIter;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchypertextdata.hxx b/sw/source/core/access/acchypertextdata.hxx
new file mode 100644
index 0000000000..01833885a0
--- /dev/null
+++ b/sw/source/core/access/acchypertextdata.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERTEXTDATA_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERTEXTDATA_HXX
+
+#include <cppuhelper/weakref.hxx>
+#include <map>
+
+class SwTextAttr;
+namespace com::sun::star::accessibility {
+ class XAccessibleHyperlink;
+}
+
+class SwAccessibleHyperTextData
+{
+public:
+ typedef const SwTextAttr * key_type;
+ typedef css::uno::WeakReference< css::accessibility::XAccessibleHyperlink > mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::less< const SwTextAttr * > key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+private:
+ std::map<key_type,mapped_type,key_compare> maMap;
+public:
+ SwAccessibleHyperTextData();
+ ~SwAccessibleHyperTextData();
+
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accmap.cxx b/sw/source/core/access/accmap.cxx
new file mode 100644
index 0000000000..0df41e3a28
--- /dev/null
+++ b/sw/source/core/access/accmap.cxx
@@ -0,0 +1,3368 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ref.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <utility>
+#include <vcl/window.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomod.hxx>
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+#include <list>
+#include <vector>
+#include <accmap.hxx>
+#include "acccontext.hxx"
+#include "accdoc.hxx"
+#include <strings.hrc>
+#include "accpreview.hxx"
+#include "accpage.hxx"
+#include "accpara.hxx"
+#include "accheaderfooter.hxx"
+#include "accfootnote.hxx"
+#include "acctextframe.hxx"
+#include "accgraphic.hxx"
+#include "accembedded.hxx"
+#include "acccell.hxx"
+#include "acctable.hxx"
+#include <fesh.hxx>
+#include <istype.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <hffrm.hxx>
+#include <ftnfrm.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <ndtyp.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdpage.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <pagepreviewlayout.hxx>
+#include <dcontact.hxx>
+#include <svx/svdmark.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <dflyobj.hxx>
+#include <prevwpage.hxx>
+#include <calbck.hxx>
+#include <undobj.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::sw::access;
+
+class SwAccessibleContextMap_Impl
+{
+public:
+ typedef const SwFrame * key_type;
+ typedef unotools::WeakReference < SwAccessibleContext > mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::unordered_map<key_type, mapped_type>::iterator iterator;
+ typedef std::unordered_map<key_type, mapped_type>::const_iterator const_iterator;
+private:
+ std::unordered_map <key_type, mapped_type> maMap;
+public:
+
+#if OSL_DEBUG_LEVEL > 0
+ bool mbLocked;
+#endif
+
+ SwAccessibleContextMap_Impl()
+#if OSL_DEBUG_LEVEL > 0
+ : mbLocked( false )
+#endif
+ {}
+
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ bool empty() const { return maMap.empty(); }
+ void clear() { maMap.clear(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+class SwDrawModellListener_Impl : public SfxListener,
+ public ::cppu::WeakImplHelper< document::XShapeEventBroadcaster >
+{
+ mutable std::mutex maListenerMutex;
+ ::comphelper::OInterfaceContainerHelper4<css::document::XEventListener> maEventListeners;
+ std::unordered_multimap<css::uno::Reference< css::drawing::XShape >, css::uno::Reference< css::document::XShapeEventListener >> maShapeListeners;
+ SdrModel *mpDrawModel;
+protected:
+ virtual ~SwDrawModellListener_Impl() override;
+
+public:
+ explicit SwDrawModellListener_Impl( SdrModel *pDrawModel );
+
+ // css::document::XEventBroadcaster
+ virtual void SAL_CALL addEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ // css::document::XShapeEventBroadcaster
+ virtual void SAL_CALL addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+ virtual void SAL_CALL removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ void Dispose();
+};
+
+}
+
+SwDrawModellListener_Impl::SwDrawModellListener_Impl( SdrModel *pDrawModel ) :
+ mpDrawModel( pDrawModel )
+{
+ StartListening( *mpDrawModel );
+}
+
+SwDrawModellListener_Impl::~SwDrawModellListener_Impl()
+{
+ Dispose();
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock g(maListenerMutex);
+ maEventListeners.addInterface( g, xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock g(maListenerMutex);
+ maEventListeners.removeInterface( g, xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ std::unique_lock aGuard(maListenerMutex);
+ maShapeListeners.emplace(xShape, xListener);
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ std::unique_lock aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ if (it->second == xListener)
+ {
+ maShapeListeners.erase(it);
+ return;
+ }
+}
+
+void SwDrawModellListener_Impl::Notify( SfxBroadcaster& /*rBC*/,
+ const SfxHint& rHint )
+{
+ // do not broadcast notifications for writer fly frames, because there
+ // are no shapes that need to know about them.
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint *pSdrHint = static_cast<const SdrHint*>( &rHint );
+ const SdrObject* pObj = pSdrHint->GetObject();
+ if (pObj &&
+ ( dynamic_cast< const SwFlyDrawObj* >(pObj) ||
+ dynamic_cast< const SwVirtFlyDrawObj* >(pObj) ||
+ pObj->GetObjIdentifier() == SdrObjKind::NewFrame ) )
+ {
+ return;
+ }
+
+ OSL_ENSURE( mpDrawModel, "draw model listener is disposed" );
+ if( !mpDrawModel )
+ return;
+
+ document::EventObject aEvent;
+ if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
+ return;
+
+ {
+ std::unique_lock g(maListenerMutex);
+ ::comphelper::OInterfaceIteratorHelper4 aIter( g, maEventListeners );
+ g.unlock();
+ while( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->notifyEvent( aEvent );
+ }
+ catch( uno::RuntimeException const & )
+ {
+ TOOLS_WARN_EXCEPTION("sw.a11y", "Runtime exception caught while notifying shape");
+ }
+ }
+ }
+
+ // right now, we're only handling the specific event necessary to fix this performance problem
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
+ {
+ auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
+ uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
+ std::unique_lock aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ it->second->notifyShapeEvent(aEvent);
+ }
+}
+
+void SwDrawModellListener_Impl::Dispose()
+{
+ if (mpDrawModel != nullptr) {
+ EndListening( *mpDrawModel );
+ }
+ mpDrawModel = nullptr;
+}
+
+typedef std::pair < const SdrObject *, ::rtl::Reference < ::accessibility::AccessibleShape > > SwAccessibleObjShape_Impl;
+
+class SwAccessibleShapeMap_Impl
+{
+public:
+
+ typedef const SdrObject * key_type;
+ typedef unotools::WeakReference<::accessibility::AccessibleShape> mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::map<key_type, mapped_type>::iterator iterator;
+ typedef std::map<key_type, mapped_type>::const_iterator const_iterator;
+
+private:
+
+ ::accessibility::AccessibleShapeTreeInfo maInfo;
+ std::map<key_type, mapped_type> maMap;
+
+public:
+
+ explicit SwAccessibleShapeMap_Impl( SwAccessibleMap const *pMap )
+ {
+ maInfo.SetSdrView( pMap->GetShell()->GetDrawView() );
+ maInfo.SetWindow( pMap->GetShell()->GetWin() );
+ maInfo.SetViewForwarder( pMap );
+ uno::Reference < document::XShapeEventBroadcaster > xModelBroadcaster =
+ new SwDrawModellListener_Impl(
+ pMap->GetShell()->getIDocumentDrawModelAccess().GetOrCreateDrawModel() );
+ maInfo.SetModelBroadcaster( xModelBroadcaster );
+ }
+
+ ~SwAccessibleShapeMap_Impl();
+
+ const ::accessibility::AccessibleShapeTreeInfo& GetInfo() const { return maInfo; }
+
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> Copy( size_t& rSize,
+ const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelShape ) const;
+
+ iterator end() { return maMap.end(); }
+ const_iterator cbegin() const { return maMap.cbegin(); }
+ const_iterator cend() const { return maMap.cend(); }
+ bool empty() const { return maMap.empty(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+SwAccessibleShapeMap_Impl::~SwAccessibleShapeMap_Impl()
+{
+ uno::Reference < document::XEventBroadcaster > xBrd( maInfo.GetModelBroadcaster() );
+ if( xBrd.is() )
+ static_cast < SwDrawModellListener_Impl * >( xBrd.get() )->Dispose();
+}
+
+std::unique_ptr<SwAccessibleObjShape_Impl[]>
+ SwAccessibleShapeMap_Impl::Copy(
+ size_t& rSize, const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelStart ) const
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+
+ size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+ rSize = maMap.size();
+
+ if( rSize > 0 )
+ {
+ pShapes.reset(new SwAccessibleObjShape_Impl[rSize]);
+
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ pSelShape = &(pShapes[rSize]);
+ for( const auto& rEntry : maMap )
+ {
+ const SdrObject *pObj = rEntry.first;
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( rEntry.second );
+ if( nSelShapes && pFESh && pFESh->IsObjSelected( *pObj ) )
+ {
+ // selected objects are inserted from the back
+ --pSelShape;
+ pSelShape->first = pObj;
+ pSelShape->second = xAcc.get();
+ --nSelShapes;
+ }
+ else
+ {
+ pShape->first = pObj;
+ pShape->second = xAcc.get();
+ ++pShape;
+ }
+ }
+ assert(pSelShape == pShape);
+ }
+
+ if( pSelStart )
+ *pSelStart = pSelShape;
+
+ return pShapes;
+}
+
+struct SwAccessibleEvent_Impl
+{
+public:
+ enum EventType { CARET_OR_STATES,
+ INVALID_CONTENT,
+ POS_CHANGED,
+ CHILD_POS_CHANGED,
+ SHAPE_SELECTION,
+ DISPOSE,
+ INVALID_ATTR };
+
+private:
+ SwRect maOldBox; // the old bounds for CHILD_POS_CHANGED
+ // and POS_CHANGED
+ uno::WeakReference < XAccessible > mxAcc; // The object that fires the event
+ SwAccessibleChild maFrameOrObj; // the child for CHILD_POS_CHANGED and
+ // the same as xAcc for any other
+ // event type
+ EventType meType; // The event type
+ AccessibleStates mnStates; // check states or update caret pos
+
+public:
+ const SwFrame* mpParentFrame; // The object that fires the event
+ bool IsNoXaccParentFrame() const
+ {
+ return CHILD_POS_CHANGED == meType && mpParentFrame != nullptr;
+ }
+
+public:
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ SwAccessibleChild aFrameOrObj )
+ : mxAcc( pA ),
+ maFrameOrObj(std::move( aFrameOrObj )),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {}
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleChild aFrameOrObj )
+ : maFrameOrObj(std::move( aFrameOrObj )),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::DISPOSE == meType &&
+ "wrong event constructor, DISPOSE only");
+ }
+
+ explicit SwAccessibleEvent_Impl( EventType eT )
+ : meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::SHAPE_SELECTION == meType &&
+ "wrong event constructor, SHAPE_SELECTION only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ SwAccessibleChild aFrameOrObj,
+ const SwRect& rR )
+ : maOldBox( rR ),
+ mxAcc( pA ),
+ maFrameOrObj(std::move( aFrameOrObj )),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert((SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType ||
+ SwAccessibleEvent_Impl::POS_CHANGED == meType) &&
+ "wrong event constructor, (CHILD_)POS_CHANGED only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ SwAccessibleChild aFrameOrObj,
+ const AccessibleStates _nStates )
+ : mxAcc( pA ),
+ maFrameOrObj(std::move( aFrameOrObj )),
+ meType( eT ),
+ mnStates( _nStates ),
+ mpParentFrame( nullptr )
+ {
+ assert( SwAccessibleEvent_Impl::CARET_OR_STATES == meType &&
+ "wrong event constructor, CARET_OR_STATES only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT, const SwFrame *pParentFrame,
+ SwAccessibleChild aFrameOrObj, const SwRect& rR ) :
+ maOldBox( rR ),
+ maFrameOrObj(std::move( aFrameOrObj )),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( pParentFrame )
+ {
+ assert( SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType &&
+ "wrong event constructor, CHILD_POS_CHANGED only" );
+ }
+
+ // <SetType(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetType( EventType eT )
+ {
+ meType = eT;
+ }
+ EventType GetType() const
+ {
+ return meType;
+ }
+
+ ::rtl::Reference < SwAccessibleContext > GetContext() const
+ {
+ uno::Reference < XAccessible > xTmp( mxAcc );
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xTmp.get() ) );
+
+ return xAccImpl;
+ }
+
+ const SwRect& GetOldBox() const
+ {
+ return maOldBox;
+ }
+ // <SetOldBox(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetOldBox( const SwRect& rOldBox )
+ {
+ maOldBox = rOldBox;
+ }
+
+ const SwAccessibleChild& GetFrameOrObj() const
+ {
+ return maFrameOrObj;
+ }
+
+ // <SetStates(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetStates( AccessibleStates _nStates )
+ {
+ mnStates |= _nStates;
+ }
+
+ bool IsUpdateCursorPos() const
+ {
+ return bool(mnStates & AccessibleStates::CARET);
+ }
+ bool IsInvalidateStates() const
+ {
+ return bool(mnStates & (AccessibleStates::EDITABLE | AccessibleStates::OPAQUE));
+ }
+ bool IsInvalidateRelation() const
+ {
+ return bool(mnStates & (AccessibleStates::RELATION_FROM | AccessibleStates::RELATION_TO));
+ }
+ bool IsInvalidateTextSelection() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_SELECTION_CHANGED );
+ }
+
+ bool IsInvalidateTextAttrs() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ }
+
+ AccessibleStates GetStates() const
+ {
+ return mnStates;
+ }
+
+ AccessibleStates GetAllStates() const
+ {
+ return mnStates;
+ }
+};
+
+class SwAccessibleEventList_Impl
+{
+ std::list<SwAccessibleEvent_Impl> maEvents;
+ bool mbFiring;
+
+public:
+ SwAccessibleEventList_Impl()
+ : mbFiring( false )
+ {}
+
+ void SetFiring()
+ {
+ mbFiring = true;
+ }
+ bool IsFiring() const
+ {
+ return mbFiring;
+ }
+
+ void MoveMissingXAccToEnd();
+
+ size_t size() const { return maEvents.size(); }
+ std::list<SwAccessibleEvent_Impl>::iterator begin() { return maEvents.begin(); }
+ std::list<SwAccessibleEvent_Impl>::iterator end() { return maEvents.end(); }
+ std::list<SwAccessibleEvent_Impl>::iterator insert( const std::list<SwAccessibleEvent_Impl>::iterator& aIter,
+ const SwAccessibleEvent_Impl& rEvent )
+ {
+ return maEvents.insert( aIter, rEvent );
+ }
+ std::list<SwAccessibleEvent_Impl>::iterator erase( const std::list<SwAccessibleEvent_Impl>::iterator& aPos )
+ {
+ return maEvents.erase( aPos );
+ }
+};
+
+// see comment in SwAccessibleMap::InvalidatePosOrSize()
+// last case "else if(pParent)" for why this surprising hack exists
+void SwAccessibleEventList_Impl::MoveMissingXAccToEnd()
+{
+ size_t nSize = size();
+ if (nSize < 2 )
+ {
+ return;
+ }
+ SwAccessibleEventList_Impl lstEvent;
+ for (auto li = begin(); li != end(); )
+ {
+ if (li->IsNoXaccParentFrame())
+ {
+ lstEvent.insert(lstEvent.end(), *li);
+ li = erase(li);
+ }
+ else
+ ++li;
+ }
+ assert(size() + lstEvent.size() == nSize);
+ maEvents.insert(end(),lstEvent.begin(),lstEvent.end());
+ assert(size() == nSize);
+}
+
+namespace {
+
+struct SwAccessibleChildFunc
+{
+ bool operator()( const SwAccessibleChild& r1,
+ const SwAccessibleChild& r2 ) const
+ {
+ const void *p1 = r1.GetSwFrame()
+ ? static_cast < const void * >( r1.GetSwFrame())
+ : ( r1.GetDrawObject()
+ ? static_cast < const void * >( r1.GetDrawObject() )
+ : static_cast < const void * >( r1.GetWindow() ) );
+ const void *p2 = r2.GetSwFrame()
+ ? static_cast < const void * >( r2.GetSwFrame())
+ : ( r2.GetDrawObject()
+ ? static_cast < const void * >( r2.GetDrawObject() )
+ : static_cast < const void * >( r2.GetWindow() ) );
+ return p1 < p2;
+ }
+};
+
+}
+
+class SwAccessibleEventMap_Impl
+{
+public:
+ typedef SwAccessibleChild key_type;
+ typedef std::list<SwAccessibleEvent_Impl>::iterator mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwAccessibleChildFunc key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map <key_type,mapped_type,key_compare> maMap;
+public:
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+struct SwAccessibleParaSelection
+{
+ TextFrameIndex nStartOfSelection;
+ TextFrameIndex nEndOfSelection;
+
+ SwAccessibleParaSelection(const TextFrameIndex nStartOfSelection_,
+ const TextFrameIndex nEndOfSelection_)
+ : nStartOfSelection(nStartOfSelection_)
+ , nEndOfSelection(nEndOfSelection_)
+ {}
+};
+
+struct SwXAccWeakRefComp
+{
+ bool operator()( const unotools::WeakReference<SwAccessibleContext>& _rXAccWeakRef1,
+ const unotools::WeakReference<SwAccessibleContext>& _rXAccWeakRef2 ) const
+ {
+ return _rXAccWeakRef1.get() < _rXAccWeakRef2.get();
+ }
+};
+
+}
+
+class SwAccessibleSelectedParas_Impl
+{
+public:
+ typedef unotools::WeakReference < SwAccessibleContext > key_type;
+ typedef SwAccessibleParaSelection mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwXAccWeakRefComp key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map<key_type,mapped_type,key_compare> maMap;
+public:
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+// helper class that stores preview data
+class SwAccPreviewData
+{
+ typedef std::vector<tools::Rectangle> Rectangles;
+ Rectangles maPreviewRects;
+ Rectangles maLogicRects;
+
+ SwRect maVisArea;
+ Fraction maScale;
+
+ const SwPageFrame *mpSelPage;
+
+ /** adjust logic page rectangle to its visible part
+
+ @param _iorLogicPgSwRect
+ input/output parameter - reference to the logic page rectangle, which
+ has to be adjusted.
+
+ @param _rPreviewPgSwRect
+ input parameter - constant reference to the corresponding preview page
+ rectangle; needed to determine the visible part of the logic page rectangle.
+
+ @param _rPreviewWinSize
+ input parameter - constant reference to the preview window size in TWIP;
+ needed to determine the visible part of the logic page rectangle
+ */
+ static void AdjustLogicPgRectToVisibleArea( SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize );
+
+public:
+ SwAccPreviewData();
+
+ void Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize );
+
+ void InvalidateSelection( const SwPageFrame* _pSelectedPageFrame );
+
+ const SwRect& GetVisArea() const { return maVisArea;}
+
+ /** Adjust the MapMode so that the preview page appears at the
+ * proper position. rPoint identifies the page for which the
+ * MapMode should be adjusted. If bFromPreview is true, rPoint is
+ * a preview coordinate; else it's a document coordinate. */
+ void AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const;
+
+ const SwPageFrame *GetSelPage() const { return mpSelPage; }
+
+ void DisposePage(const SwPageFrame *pPageFrame );
+};
+
+SwAccPreviewData::SwAccPreviewData() :
+ mpSelPage( nullptr )
+{
+}
+
+void SwAccPreviewData::Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // store preview scaling, maximal preview page size and selected page
+ maScale = _rScale;
+ mpSelPage = _pSelectedPageFrame;
+
+ // prepare loop on preview pages
+ maPreviewRects.clear();
+ maLogicRects.clear();
+ SwAccessibleChild aPage;
+ maVisArea.Clear();
+
+ // loop on preview pages to calculate <maPreviewRects>, <maLogicRects> and
+ // <maVisArea>
+ for ( auto & rpPreviewPage : _rPreviewPages )
+ {
+ aPage = rpPreviewPage->pPage;
+
+ // add preview page rectangle to <maPreviewRects>
+ tools::Rectangle aPreviewPgRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize );
+ maPreviewRects.push_back( aPreviewPgRect );
+
+ // add logic page rectangle to <maLogicRects>
+ SwRect aLogicPgSwRect( aPage.GetBox( rAccMap ) );
+ tools::Rectangle aLogicPgRect( aLogicPgSwRect.SVRect() );
+ maLogicRects.push_back( aLogicPgRect );
+ // union visible area with visible part of logic page rectangle
+ if ( rpPreviewPage->bVisible )
+ {
+ if ( !rpPreviewPage->pPage->IsEmptyPage() )
+ {
+ AdjustLogicPgRectToVisibleArea( aLogicPgSwRect,
+ SwRect( aPreviewPgRect ),
+ _rPreviewWinSize );
+ }
+ if ( maVisArea.IsEmpty() )
+ maVisArea = aLogicPgSwRect;
+ else
+ maVisArea.Union( aLogicPgSwRect );
+ }
+ }
+}
+
+void SwAccPreviewData::InvalidateSelection( const SwPageFrame* _pSelectedPageFrame )
+{
+ mpSelPage = _pSelectedPageFrame;
+ assert(mpSelPage);
+}
+
+namespace {
+
+struct ContainsPredicate
+{
+ const Point& mrPoint;
+ explicit ContainsPredicate( const Point& rPoint ) : mrPoint(rPoint) {}
+ bool operator() ( const tools::Rectangle& rRect ) const
+ {
+ return rRect.Contains( mrPoint );
+ }
+};
+
+}
+
+void SwAccPreviewData::AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const
+{
+ // adjust scale
+ rMapMode.SetScaleX( maScale );
+ rMapMode.SetScaleY( maScale );
+
+ // find proper rectangle
+ Rectangles::const_iterator aBegin = maLogicRects.begin();
+ Rectangles::const_iterator aEnd = maLogicRects.end();
+ Rectangles::const_iterator aFound = std::find_if( aBegin, aEnd,
+ ContainsPredicate( rPoint ) );
+
+ if( aFound != aEnd )
+ {
+ // found! set new origin
+ Point aPoint = (maPreviewRects.begin() + (aFound - aBegin))->TopLeft();
+ aPoint -= (maLogicRects.begin() + (aFound-aBegin))->TopLeft();
+ rMapMode.SetOrigin( aPoint );
+ }
+ // else: don't adjust MapMode
+}
+
+void SwAccPreviewData::DisposePage(const SwPageFrame *pPageFrame )
+{
+ if( mpSelPage == pPageFrame )
+ mpSelPage = nullptr;
+}
+
+// adjust logic page rectangle to its visible part
+void SwAccPreviewData::AdjustLogicPgRectToVisibleArea(
+ SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize )
+{
+ // determine preview window rectangle
+ const SwRect aPreviewWinSwRect( Point( 0, 0 ), _rPreviewWinSize );
+ // calculate visible preview page rectangle
+ SwRect aVisPreviewPgSwRect( _rPreviewPgSwRect );
+ aVisPreviewPgSwRect.Intersection( aPreviewWinSwRect );
+ // adjust logic page rectangle
+ SwTwips nTmpDiff;
+ // left
+ nTmpDiff = aVisPreviewPgSwRect.Left() - _rPreviewPgSwRect.Left();
+ _iorLogicPgSwRect.AddLeft( nTmpDiff );
+ // top
+ nTmpDiff = aVisPreviewPgSwRect.Top() - _rPreviewPgSwRect.Top();
+ _iorLogicPgSwRect.AddTop( nTmpDiff );
+ // right
+ nTmpDiff = _rPreviewPgSwRect.Right() - aVisPreviewPgSwRect.Right();
+ _iorLogicPgSwRect.AddRight( - nTmpDiff );
+ // bottom
+ nTmpDiff = _rPreviewPgSwRect.Bottom() - aVisPreviewPgSwRect.Bottom();
+ _iorLogicPgSwRect.AddBottom( - nTmpDiff );
+}
+
+static bool AreInSameTable( const rtl::Reference< SwAccessibleContext >& rAcc,
+ const SwFrame *pFrame )
+{
+ bool bRet = false;
+
+ if( pFrame && pFrame->IsCellFrame() && rAcc.is() )
+ {
+ // Is it in the same table? We check that
+ // by comparing the last table frame in the
+ // follow chain, because that's cheaper than
+ // searching the first one.
+ if( rAcc->GetFrame()->IsCellFrame() )
+ {
+ const SwTabFrame *pTabFrame1 = rAcc->GetFrame()->FindTabFrame();
+ if (pTabFrame1)
+ {
+ while (pTabFrame1->GetFollow())
+ pTabFrame1 = pTabFrame1->GetFollow();
+ }
+
+ const SwTabFrame *pTabFrame2 = pFrame->FindTabFrame();
+ if (pTabFrame2)
+ {
+ while (pTabFrame2->GetFollow())
+ pTabFrame2 = pTabFrame2->GetFollow();
+ }
+
+ bRet = (pTabFrame1 == pTabFrame2);
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleMap::FireEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ ::rtl::Reference < SwAccessibleContext > xAccImpl( rEvent.GetContext() );
+ if (!xAccImpl.is() && rEvent.mpParentFrame != nullptr)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( rEvent.mpParentFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ rtl::Reference < SwAccessibleContext > xContext( (*aIter).second.get() );
+ if (xContext.is() && (xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH
+ || xContext->getAccessibleRole() == AccessibleRole::BLOCK_QUOTE))
+ {
+ xAccImpl = xContext.get();
+ }
+ }
+ }
+ if( SwAccessibleEvent_Impl::SHAPE_SELECTION == rEvent.GetType() )
+ {
+ DoInvalidateShapeSelection();
+ }
+ else if( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ if ( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ rEvent.IsInvalidateTextAttrs() )
+ {
+ xAccImpl->InvalidateAttr();
+ }
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ xAccImpl->InvalidateContent();
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ xAccImpl->InvalidatePosOrSize( rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ xAccImpl->InvalidateChildPosOrSize( rEvent.GetFrameOrObj(),
+ rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ assert(!"dispose event has been stored");
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ // nothing to do here - handled above
+ break;
+ default:
+ break;
+ }
+ if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ if( rEvent.IsUpdateCursorPos() )
+ xAccImpl->InvalidateCursorPos();
+ if( rEvent.IsInvalidateStates() )
+ xAccImpl->InvalidateStates( rEvent.GetStates() );
+ if( rEvent.IsInvalidateRelation() )
+ {
+ // both events CONTENT_FLOWS_FROM_RELATION_CHANGED and
+ // CONTENT_FLOWS_TO_RELATION_CHANGED are possible
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_FROM )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED );
+ }
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_TO )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+ }
+
+ if ( rEvent.IsInvalidateTextSelection() )
+ {
+ xAccImpl->InvalidateTextSelection();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::AppendEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ osl::MutexGuard aGuard( maEventMutex );
+
+ if( !mpEvents )
+ mpEvents.reset(new SwAccessibleEventList_Impl);
+ if( !mpEventMap )
+ mpEventMap.reset(new SwAccessibleEventMap_Impl);
+
+ if( mpEvents->IsFiring() )
+ {
+ // While events are fired new ones are generated. They have to be fired
+ // now. This does not work for DISPOSE events!
+ OSL_ENSURE( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE,
+ "dispose event while firing events" );
+ FireEvent( rEvent );
+ }
+ else
+ {
+
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( rEvent.GetFrameOrObj() );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent( *(*aIter).second );
+ assert( aEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ "dispose events should not be stored" );
+ bool bAppendEvent = true;
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::CARET_OR_STATES:
+ // A CARET_OR_STATES event is added to any other
+ // event only. It is broadcasted after any other event, so the
+ // event should be put to the back.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ break;
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ // An INVALID_CONTENT event overwrites a CARET_OR_STATES
+ // event (but keeps its flags) and it is contained in a
+ // POS_CHANGED event.
+ // Therefore, the event's type has to be adapted and the event
+ // has to be put at the end.
+ //
+ // fdo#56031 An INVALID_CONTENT event overwrites a INVALID_ATTR
+ // event and overwrites its flags
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES )
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ else if ( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR )
+ {
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ }
+
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ // A pos changed event overwrites CARET_STATES (keeping its
+ // flags) as well as INVALID_CONTENT. The old box position
+ // has to be stored however if the old event is not a
+ // POS_CHANGED itself.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() != SwAccessibleEvent_Impl::POS_CHANGED )
+ aEvent.SetOldBox( rEvent.GetOldBox() );
+ aEvent.SetType( SwAccessibleEvent_Impl::POS_CHANGED );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ // CHILD_POS_CHANGED events can only follow CHILD_POS_CHANGED
+ // events. The only action that needs to be done again is
+ // to put the old event to the back. The new one cannot be used,
+ // because we are interested in the old frame bounds.
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::SHAPE_SELECTION:
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::SHAPE_SELECTION,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ // DISPOSE events overwrite all others. They are not stored
+ // but executed immediately to avoid broadcasting of
+ // nonfunctional objects. So what needs to be done here is to
+ // remove all events for the frame in question.
+ bAppendEvent = false;
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ // tdf#150708 if the old is CARET_OR_STATES then try updating it
+ // with the additional states
+ if (aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES)
+ aEvent.SetStates(rEvent.GetAllStates());
+ else
+ {
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR,
+ "invalid event combination" );
+ }
+ break;
+ }
+ if( bAppendEvent )
+ {
+ mpEvents->erase( (*aIter).second );
+ (*aIter).second = mpEvents->insert( mpEvents->end(), aEvent );
+ }
+ else
+ {
+ mpEvents->erase( (*aIter).second );
+ mpEventMap->erase( aIter );
+ }
+ }
+ else if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ mpEventMap->emplace( rEvent.GetFrameOrObj(),
+ mpEvents->insert( mpEvents->end(), rEvent ) );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition(
+ const uno::Reference< XAccessible >& rAcc )
+{
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( rAcc.get() );
+ assert(pAccImpl);
+ assert(pAccImpl->GetFrame());
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::CARET );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ // While firing events the current frame might have
+ // been disposed because it moved out of the visible area.
+ // Setting the cursor for such frames is useless and even
+ // causes asserts.
+ if( pAccImpl->GetFrame() )
+ pAccImpl->InvalidateCursorPos();
+ }
+}
+
+void SwAccessibleMap::InvalidateShapeSelection()
+{
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::SHAPE_SELECTION );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ DoInvalidateShapeSelection();
+ }
+}
+
+//This method should implement the following functions:
+//1.find the shape objects and set the selected state.
+//2.find the Swframe objects and set the selected state.
+//3.find the paragraph objects and set the selected state.
+void SwAccessibleMap::InvalidateShapeInParaSelection()
+{
+ DBG_TESTSOLARMUTEX();
+
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>(pVSh);
+ SwPaM* pCursor = pFESh ? pFESh->GetCursor( false /* ??? */ ) : nullptr;
+
+ //const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+
+ bool bIsSelAll =IsDocumentSelAll();
+
+ if( mpShapeMap )
+ {
+ //Checked for shapes.
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+
+ if( bIsSelAll)
+ {
+ while( aIter != aEndIter )
+ {
+ rtl::Reference<::accessibility::AccessibleShape> xAcc( (*aIter).second );
+ if( xAcc.is() )
+ xAcc->SetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ }
+ }
+ else
+ {
+ while( aIter != aEndIter )
+ {
+ const SwFrameFormat *pFrameFormat = (*aIter).first ? ::FindFrameFormat( (*aIter).first ) : nullptr;
+ if( !pFrameFormat )
+ {
+ ++aIter;
+ continue;
+ }
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwNode *pAnchorNode = rAnchor.GetAnchorNode();
+
+ if(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if(xAcc.is())
+ xAcc->ResetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ continue;
+ }
+
+ if( !pAnchorNode )
+ {
+ ++aIter;
+ continue;
+ }
+ if( pAnchorNode->GetTextNode() )
+ {
+ sal_Int32 nIndex = rAnchor.GetAnchorContentOffset();
+ bool bMarked = false;
+ if( pCursor != nullptr )
+ {
+ const SwTextNode* pNode = pAnchorNode->GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pVSh->GetLayout())));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ SwNodeOffset nHere = pNode->GetIndex();
+
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ bMarked = true;
+ // check whether nHere is 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->GetNodeIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->GetNodeIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ if( ( ((nHere == nStartIndex) && (nIndex >= pStart->GetContentIndex())) || (nHere > nStartIndex) )
+ &&( ((nHere == nEndIndex) && (nIndex < pEnd->GetContentIndex())) || (nHere < nEndIndex) ) )
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ xAcc->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ xAcc->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ rtl::Reference<::accessibility::AccessibleShape> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsSelectFrameAnchoredAtPara(*rAnchor.GetContentAnchor(), *pStart, *pEnd))
+ {
+ xAcc->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ xAcc->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ rtl::Reference<::accessibility::AccessibleShape> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsDestroyFrameAnchoredAtChar(*rAnchor.GetContentAnchor(), *pStart, *pEnd))
+ {
+ xAcc->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ xAcc->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !bMarked )
+ {
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ size_t nNumShapes = nShapes;
+ while( nNumShapes )
+ {
+ if( pShape < pSelShape && (pShape->first==(*aIter).first) )
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if(xAcc.is())
+ xAcc->ResetState( AccessibleStateType::SELECTED );
+ }
+ --nNumShapes;
+ ++pShape;
+ }
+ }
+ }
+
+ ++aIter;
+ }//while( aIter != aEndIter )
+ }//else
+ }
+
+ pShapes.reset();
+
+ //Checked for FlyFrame
+ if (mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ const SwFrame *pFrame = (*aIter).first;
+ if(pFrame->IsFlyFrame())
+ {
+ rtl::Reference < SwAccessibleContext > xAcc = (*aIter).second;
+
+ if(xAcc.is())
+ {
+ SwAccessibleFrameBase *pAccFrame = static_cast< SwAccessibleFrameBase * >(xAcc.get());
+ bool bFrameChanged = pAccFrame->SetSelectedState( true );
+ if (bFrameChanged)
+ {
+ const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame * >( pFrame );
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xAccParent = pAccFrame->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference< XAccessibleContext > xAccContext = xAccParent->getAccessibleContext();
+ if(xAccContext.is() && (xAccContext->getAccessibleRole() == AccessibleRole::PARAGRAPH ||
+ xAccContext->getAccessibleRole() == AccessibleRole::BLOCK_QUOTE))
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xAccContext.get());
+ if(pAccFrame->IsSelectedInDoc())
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ else if(m_setParaAdd.count(pAccPara) == 0)
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ++aIter;
+ }
+ }
+
+ typedef std::vector< SwAccessibleContext* > VEC_PARA;
+ VEC_PARA vecAdd;
+ VEC_PARA vecRemove;
+ //Checked for Paras.
+ bool bMarkChanged = false;
+ SwAccessibleContextMap_Impl mapTemp;
+ if( pCursor != nullptr )
+ {
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ if( rTmpCursor.HasMark() )
+ {
+ SwNodeIndex nStartIndex( rTmpCursor.Start()->GetNode() );
+ SwNodeIndex nEndIndex( rTmpCursor.End()->GetNode() );
+ for (; nStartIndex <= nEndIndex; ++nStartIndex)
+ {
+ SwFrame *pFrame = nullptr;
+ if(nStartIndex.GetNode().IsContentNode())
+ {
+ SwContentNode* pCNd = static_cast<SwContentNode*>(&(nStartIndex.GetNode()));
+ pFrame = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First();
+ if (mapTemp.find(pFrame) != mapTemp.end())
+ {
+ continue; // sw_redlinehide: once is enough
+ }
+ }
+ else if( nStartIndex.GetNode().IsTableNode() )
+ {
+ SwTableNode * pTable = static_cast<SwTableNode *>(&(nStartIndex.GetNode()));
+ SwTableFormat* pFormat = pTable->GetTable().GetFrameFormat();
+ pFrame = SwIterator<SwFrame, SwTableFormat>(*pFormat).First();
+ }
+
+ if( pFrame && mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ rtl::Reference < SwAccessibleContext > xAcc = (*aIter).second;
+ bool isChanged = false;
+ if( xAcc.is() )
+ {
+ isChanged = xAcc->SetSelectedState( true );
+ }
+ if(!isChanged)
+ {
+ SwAccessibleContextMap_Impl::iterator aEraseIter = mpSelectedFrameMap->find( pFrame );
+ if(aEraseIter != mpSelectedFrameMap->end())
+ mpSelectedFrameMap->erase(aEraseIter);
+ }
+ else
+ {
+ bMarkChanged = true;
+ vecAdd.push_back(xAcc.get());
+ }
+
+ mapTemp.emplace( pFrame, xAcc );
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !mpSelectedFrameMap )
+ mpSelectedFrameMap.reset( new SwAccessibleContextMap_Impl );
+ if( !mpSelectedFrameMap->empty() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpSelectedFrameMap->begin();
+ while( aIter != mpSelectedFrameMap->end() )
+ {
+ rtl::Reference < SwAccessibleContext > xAcc = (*aIter).second;
+ if(xAcc.is())
+ xAcc->SetSelectedState( false );
+ ++aIter;
+ vecRemove.push_back(xAcc.get());
+ }
+ bMarkChanged = true;
+ mpSelectedFrameMap->clear();
+ }
+
+ SwAccessibleContextMap_Impl::iterator aIter = mapTemp.begin();
+ while( aIter != mapTemp.end() )
+ {
+ mpSelectedFrameMap->emplace( (*aIter).first, (*aIter).second );
+ ++aIter;
+ }
+ mapTemp.clear();
+
+ if( !(bMarkChanged && mpFrameMap))
+ return;
+
+ for (SwAccessibleContext* pAccPara : vecAdd)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ for (SwAccessibleContext* pAccPara : vecRemove)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+//Merge with DoInvalidateShapeFocus
+void SwAccessibleMap::DoInvalidateShapeSelection(bool bInvalidateFocusMode /*=false*/)
+{
+ DBG_TESTSOLARMUTEX();
+
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>(pVSh);
+ const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ //when InvalidateFocus Call this function ,and the current selected shape count is not 1 ,
+ //return
+ if (bInvalidateFocusMode && nSelShapes != 1)
+ {
+ return;
+ }
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+
+ if( !pShapes )
+ return;
+
+ typedef std::vector< ::rtl::Reference < ::accessibility::AccessibleShape > > VEC_SHAPE;
+ VEC_SHAPE vecxShapeAdd;
+ VEC_SHAPE vecxShapeRemove;
+ int nCountSelectedShape=0;
+
+ vcl::Window *pWin = GetShell()->GetWin();
+ bool bFocused = pWin && pWin->HasFocus();
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ int nShapeCount = nShapes;
+ while( nShapeCount )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape < pSelShape )
+ {
+ if(pShape->second->ResetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeRemove.push_back(pShape->second);
+ }
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+ }
+ }
+ --nShapeCount;
+ ++pShape;
+ }
+
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, uno::Any(), uno::Any(), -1);
+ }
+ }
+
+ pShape = pShapes.get();
+
+ while( nShapes )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape >= pSelShape )
+ {
+ //first fire focus event
+ if( bFocused && 1 == nSelShapes )
+ pShape->second->SetState( AccessibleStateType::FOCUSED );
+ else
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+
+ if(pShape->second->SetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeAdd.push_back(pShape->second);
+ }
+ ++nCountSelectedShape;
+ }
+ }
+
+ --nShapes;
+ ++pShape;
+ }
+
+ const unsigned int SELECTION_WITH_NUM = 10;
+ if (vecxShapeAdd.size() > SELECTION_WITH_NUM )
+ {
+ uno::Reference< XAccessible > xDoc = GetDocumentView( );
+ SwAccessibleContext * pCont = static_cast<SwAccessibleContext *>(xDoc.get());
+ if (pCont)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ pCont->FireAccessibleEvent(aEvent);
+ }
+ }
+ else
+ {
+ short nEventID = AccessibleEventId::SELECTION_CHANGED_ADD;
+ if (nCountSelectedShape <= 1 && vecxShapeAdd.size() == 1 )
+ {
+ nEventID = AccessibleEventId::SELECTION_CHANGED;
+ }
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(nEventID, uno::Any(), uno::Any(), -1);
+ }
+ }
+ }
+
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(pAccShape->GetXShape());
+ SwFrameFormat *pFrameFormat = pObj ? FindFrameFormat( pObj ) : nullptr;
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ if (xPara.is())
+ {
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && (xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH
+ || xParaContext->getAccessibleRole() == AccessibleRole::BLOCK_QUOTE))
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (pAccPara)
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape && !pAccShape->IsDisposed())
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && (xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH
+ || xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH))
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (m_setParaAdd.count(pAccPara) == 0 )
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+}
+
+SwAccessibleMap::SwAccessibleMap( SwViewShell *pSh ) :
+ mpVSh( pSh ),
+ mbShapeSelected( false ),
+ maDocName(SwAccessibleContext::GetResource(STR_ACCESS_DOC_NAME))
+{
+ pSh->GetLayout()->AddAccessibleShell();
+}
+
+SwAccessibleMap::~SwAccessibleMap()
+{
+ DBG_TESTSOLARMUTEX();
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ if( mpFrameMap )
+ {
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( !xAcc.is() )
+ assert(false); // let's hope this can't happen? the vcl::Window apparently owns the top-level
+ //xAcc = new SwAccessibleDocument(shared_from_this());
+ }
+
+ if(xAcc.is())
+ {
+ SwAccessibleDocumentBase *const pAcc =
+ static_cast<SwAccessibleDocumentBase *>(xAcc.get());
+ pAcc->Dispose( true );
+ }
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ rtl::Reference < SwAccessibleContext > xTmp = (*aIter).second;
+ if( xTmp.is() )
+ assert(xTmp->GetMap() == nullptr); // must be disposed
+ ++aIter;
+ }
+ }
+#endif
+ assert((!mpFrameMap || mpFrameMap->empty()) &&
+ "Frame map should be empty after disposing the root frame");
+ assert((!mpShapeMap || mpShapeMap->empty()) &&
+ "Object map should be empty after disposing the root frame");
+ mpFrameMap.reset();
+ mpShapeMap.reset();
+ mvShapes.clear();
+ mpSelectedParas.reset();
+
+ mpPreview.reset();
+
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ assert(!mpEvents);
+ assert(!mpEventMap);
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ mpVSh->GetLayout()->RemoveAccessibleShell();
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView_(
+ bool bPagePreview )
+{
+ DBG_TESTSOLARMUTEX();
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ bool bSetVisArea = false;
+
+ if( !mpFrameMap )
+ {
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ assert(!mpFrameMap->mbLocked);
+ mpFrameMap->mbLocked = true;
+#endif
+
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( xAcc.is() )
+ {
+ bSetVisArea = true; // Set VisArea when map mutex is not locked
+ }
+ else
+ {
+ if( bPagePreview )
+ xAcc = new SwAccessiblePreview(shared_from_this());
+ else
+ xAcc = new SwAccessibleDocument(shared_from_this());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc.get();
+ }
+ else
+ {
+ mpFrameMap->emplace( pRootFrame, xAcc );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+
+ if( bSetVisArea )
+ {
+ SwAccessibleDocumentBase *pAcc =
+ static_cast< SwAccessibleDocumentBase * >( xAcc.get() );
+ pAcc->SetVisArea();
+ }
+
+ return xAcc;
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView( )
+{
+ return GetDocumentView_( false );
+}
+
+uno::Reference<XAccessible> SwAccessibleMap::GetDocumentPreview(
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // create & update preview data object
+ if( mpPreview == nullptr )
+ mpPreview.reset( new SwAccPreviewData() );
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ return xAcc;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext( const SwFrame *pFrame,
+ bool bCreate )
+{
+ DBG_TESTSOLARMUTEX();
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ rtl::Reference < SwAccessibleContext > xOldCursorAcc;
+ bool bOldShapeSelected = false;
+
+ if( !mpFrameMap && bCreate )
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ rtl::Reference<SwAccessibleContext> pAcc;
+ switch( pFrame->GetType() )
+ {
+ case SwFrameType::Txt:
+ pAcc = new SwAccessibleParagraph(shared_from_this(),
+ static_cast< const SwTextFrame& >( *pFrame ) );
+ break;
+ case SwFrameType::Header:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwHeaderFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Footer:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwFooterFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Ftn:
+ {
+ const SwFootnoteFrame *pFootnoteFrame =
+ static_cast < const SwFootnoteFrame * >( pFrame );
+ bool bIsEndnote =
+ SwAccessibleFootnote::IsEndnote( pFootnoteFrame );
+ pAcc = new SwAccessibleFootnote(shared_from_this(), bIsEndnote,
+ /*(bIsEndnote ? mnEndnote++ : mnFootnote++),*/
+ pFootnoteFrame );
+ }
+ break;
+ case SwFrameType::Fly:
+ {
+ const SwFlyFrame *pFlyFrame =
+ static_cast < const SwFlyFrame * >( pFrame );
+ switch( SwAccessibleFrameBase::GetNodeType( pFlyFrame ) )
+ {
+ case SwNodeType::Grf:
+ pAcc = new SwAccessibleGraphic(shared_from_this(), pFlyFrame );
+ break;
+ case SwNodeType::Ole:
+ pAcc = new SwAccessibleEmbeddedObject(shared_from_this(), pFlyFrame );
+ break;
+ default:
+ pAcc = new SwAccessibleTextFrame(shared_from_this(), *pFlyFrame );
+ break;
+ }
+ }
+ break;
+ case SwFrameType::Cell:
+ pAcc = new SwAccessibleCell(shared_from_this(),
+ static_cast< const SwCellFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Tab:
+ pAcc = new SwAccessibleTable(shared_from_this(),
+ static_cast< const SwTabFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Page:
+ OSL_ENSURE( GetShell()->IsPreview(),
+ "accessible page frames only in PagePreview" );
+ pAcc = new SwAccessiblePage(shared_from_this(), pFrame);
+ break;
+ default: break;
+ }
+ xAcc = pAcc;
+ assert(xAcc.is());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc.get();
+ }
+ else
+ {
+ mpFrameMap->emplace( pFrame, xAcc );
+ }
+
+ if( pAcc->HasCursor() &&
+ !AreInSameTable( mxCursorContext, pFrame ) )
+ {
+ // If the new context has the focus, and if we know
+ // another context that had the focus, then the focus
+ // just moves from the old context to the new one. We
+ // then have to send a focus event and a caret event for
+ // the old context. We have to do that now,
+ // because after we have left this method, anyone might
+ // call getStates for the new context and will get a
+ // focused state then. Sending the focus changes event
+ // after that seems to be strange. However, we cannot
+ // send a focus event for the new context now, because
+ // no one except us knows it. In any case, we remember
+ // the new context as the one that has the focus
+ // currently.
+
+ xOldCursorAcc = mxCursorContext;
+ mxCursorContext = xAcc.get();
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = false;
+ }
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+ if( bOldShapeSelected )
+ InvalidateShapeSelection();
+
+ return xAcc;
+}
+
+::rtl::Reference < SwAccessibleContext > SwAccessibleMap::GetContextImpl(
+ const SwFrame *pFrame,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pFrame, bCreate ) );
+
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast< SwAccessibleContext * >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ DBG_TESTSOLARMUTEX();
+
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc;
+ uno::Reference < XAccessible > xOldCursorAcc;
+
+ if( !mpShapeMap && bCreate )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ rtl::Reference< ::accessibility::AccessibleShape> pAcc;
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ uno::Reference < XAccessible > xParent( pParentImpl );
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+
+ pAcc = rShapeTypeHandler.CreateAccessibleObject(
+ aShapeInfo, mpShapeMap->GetInfo() );
+ }
+ xAcc = pAcc.get();
+ assert(xAcc.is());
+ pAcc->Init();
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc.get();
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ // TODO: focus!!!
+ AddGroupContext(pObj, xAcc);
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+
+ return xAcc;
+}
+
+bool SwAccessibleMap::IsInSameLevel(const SdrObject* pObj, const SwFEShell* pFESh)
+{
+ if (pFESh)
+ return pFESh->IsObjSameLevelWithMarked(pObj);
+ return false;
+}
+
+void SwAccessibleMap::AddShapeContext(const SdrObject *pObj, rtl::Reference < ::accessibility::AccessibleShape > const & xAccShape)
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( mpShapeMap )
+ {
+ mpShapeMap->emplace( pObj, xAccShape );
+ }
+}
+
+//Added by yanjun for sym2_6407
+void SwAccessibleMap::RemoveGroupContext(const SdrObject *pParentObj)
+{
+ DBG_TESTSOLARMUTEX();
+
+ // TODO: Why are sub-shapes of group shapes even added to our map?
+ // Doesn't the AccessibleShape of the top-level shape create them
+ // on demand anyway? Why does SwAccessibleMap need to know them?
+ // We cannot rely on getAccessibleChild here to remove the sub-shapes
+ // from mpShapes because the top-level shape may not only be disposed here
+ // but also by visibility checks in svx, then it doesn't return children.
+ if (mpShapeMap && pParentObj && pParentObj->IsGroupObject())
+ {
+ if (SdrObjList *const pChildren = pParentObj->GetSubList())
+ for (const rtl::Reference<SdrObject>& pChild : *pChildren)
+ {
+ assert(pChild);
+ RemoveContext(pChild.get());
+ }
+ }
+}
+//End
+
+void SwAccessibleMap::AddGroupContext(const SdrObject *pParentObj, uno::Reference < XAccessible > const & xAccParent)
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( !mpShapeMap )
+ return;
+
+ //here get all the sub list.
+ if (!pParentObj->IsGroupObject())
+ return;
+
+ if (!xAccParent.is())
+ return;
+
+ uno::Reference < XAccessibleContext > xContext = xAccParent->getAccessibleContext();
+ if (!xContext.is())
+ return;
+
+ sal_Int64 nChildren = xContext->getAccessibleChildCount();
+ for(sal_Int64 i = 0; i<nChildren; i++)
+ {
+ uno::Reference < XAccessible > xChild = xContext->getAccessibleChild(i);
+ if (xChild.is())
+ {
+ uno::Reference < XAccessibleContext > xChildContext = xChild->getAccessibleContext();
+ if (xChildContext.is())
+ {
+ short nRole = xChildContext->getAccessibleRole();
+ if (nRole == AccessibleRole::SHAPE)
+ {
+ ::accessibility::AccessibleShape* pAccShape = static_cast < ::accessibility::AccessibleShape* >( xChild.get());
+ uno::Reference < drawing::XShape > xShape = pAccShape->GetXShape();
+ if (xShape.is())
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ AddShapeContext(pObj, pAccShape);
+ AddGroupContext(pObj,xChild);
+ }
+ }
+ }
+ }
+ }
+}
+
+::rtl::Reference < ::accessibility::AccessibleShape > SwAccessibleMap::GetContextImpl(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pObj, pParentImpl, bCreate ) );
+
+ ::rtl::Reference < ::accessibility::AccessibleShape > xAccImpl(
+ static_cast< ::accessibility::AccessibleShape* >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+void SwAccessibleMap::RemoveContext( const SwFrame *pFrame )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( !mpFrameMap )
+ return;
+
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pFrame );
+ if( aIter == mpFrameMap->end() )
+ return;
+
+ mpFrameMap->erase( aIter );
+
+ if (mpSelectedFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aSelectedIter = mpSelectedFrameMap->find(pFrame);
+ if (aSelectedIter != mpSelectedFrameMap->end())
+ mpSelectedFrameMap->erase(aSelectedIter);
+ }
+
+ // Remove reference to old caret object. Though mxCursorContext
+ // is a weak reference and cleared automatically, clearing it
+ // directly makes sure to not keep a non-functional object.
+ rtl::Reference < SwAccessibleContext > xOldAcc( mxCursorContext );
+ if( xOldAcc.is() )
+ {
+ SwAccessibleContext *pOldAccImpl = xOldAcc.get();
+ OSL_ENSURE( pOldAccImpl->GetFrame(), "old caret context is disposed" );
+ if( pOldAccImpl->GetFrame() == pFrame )
+ {
+ xOldAcc.clear(); // get an empty ref
+ mxCursorContext = xOldAcc.get();
+ }
+ }
+
+ if( mpFrameMap->empty() )
+ {
+ mpFrameMap.reset();
+ }
+}
+
+void SwAccessibleMap::RemoveContext( const SdrObject *pObj )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if( !mpShapeMap )
+ return;
+
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter == mpShapeMap->end() )
+ return;
+
+ rtl::Reference < ::accessibility::AccessibleShape > xTempHold( (*aIter).second );
+ mpShapeMap->erase( aIter );
+ RemoveGroupContext(pObj);
+ // The shape selection flag is not cleared, but one might do
+ // so but has to make sure that the removed context is the one
+ // that is selected.
+
+ if( mpShapeMap && mpShapeMap->empty() )
+ {
+ mpShapeMap.reset();
+ }
+}
+
+bool SwAccessibleMap::Contains(const SwFrame *pFrame) const
+{
+ return (pFrame && mpFrameMap && mpFrameMap->find(pFrame) != mpFrameMap->end());
+}
+
+void SwAccessibleMap::A11yDispose( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ bool bRecursive,
+ bool bCanSkipInvisible )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+
+ // Indeed, the following assert checks the frame's accessible flag,
+ // because that's the one that is evaluated in the layout. The frame
+ // might not be accessible anyway. That's the case for cell frames that
+ // contain further cells.
+ OSL_ENSURE( !aFrameOrObj.GetSwFrame() || aFrameOrObj.GetSwFrame()->IsAccessibleFrame(),
+ "non accessible frame should be disposed" );
+
+ if (!(aFrameOrObj.IsAccessible(GetShell()->IsPreview())
+ // fdo#87199 dispose the darn thing if it ever was accessible
+ || Contains(pFrame)))
+ return;
+
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ ::rtl::Reference< ::accessibility::AccessibleShape > xShapeAccImpl;
+ // get accessible context for frame
+ {
+ // First of all look for an accessible context for a frame
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAccImpl = (*aIter).second;
+ }
+ if( !xAccImpl.is() && mpFrameMap )
+ {
+ // If there is none, look if the parent is accessible.
+ const SwFrame *pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ xParentAccImpl = (*aIter).second;
+ }
+ }
+ if( !xParentAccImpl.is() && !aFrameOrObj.GetSwFrame() && mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter =
+ mpShapeMap->find( aFrameOrObj.GetDrawObject() );
+ if( aIter != mpShapeMap->end() )
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ xShapeAccImpl = xAcc;
+ }
+ }
+ if( pObj && GetShell()->ActionPend() &&
+ (xParentAccImpl.is() || xShapeAccImpl.is()) )
+ {
+ // Keep a reference to the XShape to avoid that it
+ // is deleted with a SwFrameFormat::SwClientNotify.
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ mvShapes.push_back( xShape );
+ }
+ }
+ }
+
+ // remove events stored for the frame
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( aFrameOrObj );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::DISPOSE, aFrameOrObj );
+ AppendEvent( aEvent );
+ }
+ }
+ }
+
+ // If the frame is accessible and there is a context for it, dispose
+ // the frame. If the frame is no context for it but disposing should
+ // take place recursive, the frame's children have to be disposed
+ // anyway, so we have to create the context then.
+ if( xAccImpl.is() )
+ {
+ xAccImpl->Dispose( bRecursive );
+ }
+ else if( xParentAccImpl.is() )
+ {
+ // If the frame is a cell frame, the table must be notified.
+ // If we are in an action, a table model change event will
+ // be broadcasted at the end of the action to give the table
+ // a chance to generate a single table change event.
+
+ xParentAccImpl->DisposeChild( aFrameOrObj, bRecursive, bCanSkipInvisible );
+ }
+ else if( xShapeAccImpl.is() )
+ {
+ RemoveContext( aFrameOrObj.GetDrawObject() );
+ xShapeAccImpl->dispose();
+ }
+
+ if( mpPreview && pFrame && pFrame->IsPageFrame() )
+ mpPreview->DisposePage( static_cast< const SwPageFrame *>( pFrame ) );
+}
+
+void SwAccessibleMap::InvalidatePosOrSize( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ const SwRect& rOldBox )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ const SwFrame *pParent =nullptr;
+ if( mpFrameMap )
+ {
+ if( aFrameOrObj.GetSwFrame() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ // If there is an accessible object already it is
+ // notified directly.
+ xAccImpl = (*aIter).second;
+ }
+ }
+ if( !xAccImpl.is() )
+ {
+ // Otherwise we look if the parent is accessible.
+ // If not, there is nothing to do.
+ pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ xParentAccImpl = (*aIter).second;
+ }
+ }
+ }
+
+ if( xAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::POS_CHANGED, xAccImpl.get(),
+ aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ if (xAccImpl->GetFrame()) // not if disposed by FireEvents()
+ {
+ xAccImpl->InvalidatePosOrSize(rOldBox);
+ }
+ }
+ }
+ else if( xParentAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ assert(pParent);
+ // tdf#99722 faster not to buffer events that won't be sent
+ if (!SwAccessibleChild(pParent).IsVisibleChildrenOnly()
+ || xParentAccImpl->IsShowing(rOldBox)
+ || xParentAccImpl->IsShowing(*this, aFrameOrObj))
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ xParentAccImpl.get(), aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ }
+ else
+ {
+ FireEvents();
+ xParentAccImpl->InvalidateChildPosOrSize( aFrameOrObj,
+ rOldBox );
+ }
+ }
+ else if(pParent)
+ {
+/*
+For child graphic and its parent paragraph,if split 2 graphic to 2 paragraph,
+will delete one graphic swfrm and new create 1 graphic swfrm ,
+then the new paragraph and the new graphic SwFrame will add .
+but when add graphic SwFrame ,the accessible of the new Paragraph is not created yet.
+so the new graphic accessible 'parent is NULL,
+so run here: save the parent's SwFrame not the accessible object parent,
+*/
+ bool bIsValidFrame = false;
+ bool bIsTextParent = false;
+ if (aFrameOrObj.GetSwFrame())
+ {
+ if (SwFrameType::Fly == pFrame->GetType())
+ {
+ bIsValidFrame =true;
+ }
+ }
+ else if(pObj)
+ {
+ if (SwFrameType::Txt == pParent->GetType())
+ {
+ bIsTextParent =true;
+ }
+ }
+ if( bIsValidFrame || bIsTextParent )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ pParent, aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ OSL_ENSURE(false,"");
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateContent( const SwFrame *pFrame )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ if (!mpFrameMap)
+ return;
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() )
+ return;
+
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::INVALID_CONTENT, xAcc.get(),
+ std::move(aFrameOrObj) );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ xAcc->InvalidateContent();
+ }
+}
+
+void SwAccessibleMap::InvalidateAttr( const SwTextFrame& rTextFrame )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SwAccessibleChild aFrameOrObj( &rTextFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ if (!mpFrameMap)
+ return;
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() )
+ return;
+
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::INVALID_ATTR,
+ xAcc.get(), std::move(aFrameOrObj) );
+ aEvent.SetStates( AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ xAcc->InvalidateAttr();
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition( const SwFrame *pFrame )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SwAccessibleChild aFrameOrObj( pFrame );
+ bool bShapeSelected = false;
+ const SwViewShell *pVSh = GetShell();
+ if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
+ {
+ if( pCSh->IsTableMode() )
+ {
+ while( aFrameOrObj.GetSwFrame() && !aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ }
+ else if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
+ {
+ const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ OSL_ENSURE( !pFrame || pFrame->FindFlyFrame() == pFlyFrame,
+ "cursor is not contained in fly frame" );
+ aFrameOrObj = pFlyFrame;
+ }
+ else if( pFESh->IsObjSelected() > 0 )
+ {
+ bShapeSelected = true;
+ aFrameOrObj = static_cast<const SwFrame *>( nullptr );
+ }
+ }
+ }
+
+ OSL_ENSURE( bShapeSelected || aFrameOrObj.IsAccessible(GetShell()->IsPreview()),
+ "frame is not accessible" );
+
+ rtl::Reference < SwAccessibleContext > xOldAcc;
+ rtl::Reference < SwAccessibleContext > xAcc;
+ bool bOldShapeSelected = false;
+
+ {
+ xOldAcc = mxCursorContext;
+ mxCursorContext = xAcc.get(); // clear reference
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = bShapeSelected;
+
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ else
+ {
+ SwRect rcEmpty;
+ const SwTabFrame* pTabFrame = aFrameOrObj.GetSwFrame()->FindTabFrame();
+ if (pTabFrame)
+ {
+ InvalidatePosOrSize(pTabFrame, nullptr, nullptr, rcEmpty);
+ }
+ else
+ {
+ InvalidatePosOrSize(aFrameOrObj.GetSwFrame(), nullptr, nullptr, rcEmpty);
+ }
+
+ aIter = mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+
+ // For cells, some extra thoughts are necessary,
+ // because invalidating the cursor for one cell
+ // invalidates the cursor for all cells of the same
+ // table. For this reason, we don't want to
+ // invalidate the cursor for the old cursor object
+ // and the new one if they are within the same table,
+ // because this would result in doing the work twice.
+ // Moreover, we have to make sure to invalidate the
+ // cursor even if the current cell has no accessible object.
+ // If the old cursor objects exists and is in the same
+ // table, it's the best choice, because using it avoids
+ // an unnecessary cursor invalidation cycle when creating
+ // a new object for the current cell.
+ if( aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ {
+ if( xOldAcc.is() &&
+ AreInSameTable( xOldAcc, aFrameOrObj.GetSwFrame() ) )
+ {
+ if( xAcc.is() )
+ xOldAcc = xAcc; // avoid extra invalidation
+ else
+ xAcc = xOldAcc; // make sure at least one
+ }
+ if( !xAcc.is() )
+ xAcc = GetContextImpl( aFrameOrObj.GetSwFrame() );
+ }
+ }
+ else if (bShapeSelected)
+ {
+ const SwFEShell *pFESh = static_cast< const SwFEShell * >( pVSh );
+ const SdrMarkList *pMarkList = pFESh->GetMarkList();
+ if (pMarkList != nullptr && pMarkList->GetMarkCount() == 1)
+ {
+ SdrObject *pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj();
+ ::rtl::Reference < ::accessibility::AccessibleShape > pAccShapeImpl = GetContextImpl(pObj,nullptr,false);
+ if (!pAccShapeImpl.is())
+ {
+ while (pObj && pObj->getParentSdrObjectFromSdrObject())
+ {
+ pObj = pObj->getParentSdrObjectFromSdrObject();
+ }
+ if (pObj != nullptr)
+ {
+ const SwFrame *pParent = SwAccessibleFrame::GetParent( SwAccessibleChild(pObj), GetShell()->IsPreview() );
+ if( pParent )
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl = GetContextImpl(pParent,false);
+ if (!xParentAccImpl.is())
+ {
+ const SwTabFrame* pTabFrame = pParent->FindTabFrame();
+ if (pTabFrame)
+ {
+ //The Table should not add in acc.because the "pParent" is not add to acc .
+ uno::Reference< XAccessible> xAccParentTab = GetContext(pTabFrame);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pTabFrame), GetShell()->IsPreview() );
+ if (pParentRoot)
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccParentTab;
+ aEvent.IndexHint = -1;
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+
+ //Get "pParent" acc again.
+ xParentAccImpl = GetContextImpl(pParent,false);
+ }
+ else
+ {
+ //directly create this acc para .
+ xParentAccImpl = GetContextImpl(pParent);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pParent), GetShell()->IsPreview() );
+
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= uno::Reference< XAccessible>(xParentAccImpl);
+ aEvent.IndexHint = -1;
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ if (xParentAccImpl.is())
+ {
+ uno::Reference< XAccessible> xAccShape =
+ GetContext(pObj,xParentAccImpl.get());
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccShape;
+ aEvent.IndexHint = -1;
+ xParentAccImpl->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_setParaAdd.clear();
+ m_setParaRemove.clear();
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( bOldShapeSelected || bShapeSelected )
+ InvalidateShapeSelection();
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+
+ InvalidateShapeInParaSelection();
+
+ for (SwAccessibleParagraph* pAccPara : m_setParaRemove)
+ {
+ if (pAccPara && !pAccPara->IsDisposed() &&
+ pAccPara->getSelectedAccessibleChildCount() == 0 &&
+ pAccPara->getSelectedText().getLength() == 0)
+ {
+ if(pAccPara->SetSelectedState(false))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ for (SwAccessibleParagraph* pAccPara : m_setParaAdd)
+ {
+ if(pAccPara && pAccPara->SetSelectedState(true))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateFocus()
+{
+ DBG_TESTSOLARMUTEX();
+
+ if(GetShell()->IsPreview())
+ {
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ if (xAcc)
+ {
+ SwAccessiblePreview *pAccPreview = static_cast<SwAccessiblePreview *>(xAcc.get());
+ if (pAccPreview)
+ {
+ pAccPreview->InvalidateFocus();
+ return ;
+ }
+ }
+ }
+
+ rtl::Reference < SwAccessibleContext > xAcc = mxCursorContext;
+ if( xAcc.is() )
+ {
+ xAcc->InvalidateFocus();
+ }
+ else
+ {
+ DoInvalidateShapeSelection(true);
+ }
+}
+
+void SwAccessibleMap::SetCursorContext(
+ const ::rtl::Reference < SwAccessibleContext >& rCursorContext )
+{
+ DBG_TESTSOLARMUTEX();
+ mxCursorContext = rCursorContext.get();
+}
+
+void SwAccessibleMap::InvalidateEditableStates( const SwFrame* _pFrame )
+{
+ // Start with the frame or the first upper that is accessible
+ SwAccessibleChild aFrameOrObj( _pFrame );
+ while( aFrameOrObj.GetSwFrame() &&
+ !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ if( !aFrameOrObj.GetSwFrame() )
+ aFrameOrObj = GetShell()->GetLayout();
+
+ uno::Reference< XAccessible > xAcc( GetContext( aFrameOrObj.GetSwFrame() ) );
+ SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::EDITABLE );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateStates( AccessibleStates::EDITABLE );
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet_( const SwFrame* pFrame,
+ bool bFrom )
+{
+ DBG_TESTSOLARMUTEX();
+
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ if (!mpFrameMap)
+ return;
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+
+ // deliver event directly, or queue event
+ if( !xAcc.is() )
+ return;
+
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ xAcc.get(), SwAccessibleChild(pFrame),
+ ( bFrom
+ ? AccessibleStates::RELATION_FROM
+ : AccessibleStates::RELATION_TO ) );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ xAcc->InvalidateRelation( bFrom
+ ? AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
+ : AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet( const SwFrame* pMaster,
+ const SwFrame* pFollow )
+{
+ InvalidateRelationSet_( pMaster, false );
+ InvalidateRelationSet_( pFollow, true );
+}
+
+// invalidation of CONTENT_FLOW_FROM/_TO relation of a paragraph
+void SwAccessibleMap::InvalidateParaFlowRelation( const SwTextFrame& _rTextFrame,
+ const bool _bFrom )
+{
+ InvalidateRelationSet_( &_rTextFrame, _bFrom );
+}
+
+// invalidation of text selection of a paragraph
+void SwAccessibleMap::InvalidateParaTextSelection( const SwTextFrame& _rTextFrame )
+{
+ DBG_TESTSOLARMUTEX();
+
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( &_rTextFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ if (!mpFrameMap)
+ return;
+
+ rtl::Reference < SwAccessibleContext > xAcc;
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+
+ // deliver event directly, or queue event
+ if( !xAcc.is() )
+ return;
+
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CARET_OR_STATES,
+ xAcc.get(),
+ SwAccessibleChild( &_rTextFrame ),
+ AccessibleStates::TEXT_SELECTION_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ xAcc->InvalidateTextSelection();
+ }
+}
+
+sal_Int32 SwAccessibleMap::GetChildIndex( const SwFrame& rParentFrame,
+ vcl::Window& rChild ) const
+{
+ DBG_TESTSOLARMUTEX();
+
+ sal_Int32 nIndex( -1 );
+
+ SwAccessibleChild aFrameOrObj( &rParentFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ rtl::Reference < SwAccessibleContext > xAcc;
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xAcc.is() )
+ nIndex = xAcc->GetChildIndex( const_cast<SwAccessibleMap&>(*this),
+ SwAccessibleChild( &rChild ) );
+ }
+
+ return nIndex;
+}
+
+void SwAccessibleMap::UpdatePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ DBG_TESTSOLARMUTEX();
+ assert(GetShell()->IsPreview() && "no preview?");
+ assert(mpPreview != nullptr && "no preview data?");
+
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ // propagate change of VisArea through the document's
+ // accessibility tree; this will also send appropriate scroll
+ // events
+ SwAccessibleContext* pDoc =
+ GetContextImpl( GetShell()->GetLayout() ).get();
+ static_cast<SwAccessibleDocumentBase*>( pDoc )->SetVisArea();
+
+ rtl::Reference < SwAccessibleContext > xOldAcc = mxCursorContext;
+ rtl::Reference < SwAccessibleContext > xAcc;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+void SwAccessibleMap::InvalidatePreviewSelection( sal_uInt16 nSelPage )
+{
+ DBG_TESTSOLARMUTEX();
+ assert(GetShell()->IsPreview());
+ assert(mpPreview != nullptr);
+
+ mpPreview->InvalidateSelection( GetShell()->GetLayout()->GetPageByPageNum( nSelPage ) );
+
+ rtl::Reference < SwAccessibleContext > xOldAcc = mxCursorContext;
+ rtl::Reference < SwAccessibleContext > xAcc;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+bool SwAccessibleMap::IsPageSelected( const SwPageFrame *pPageFrame ) const
+{
+ return mpPreview && mpPreview->GetSelPage() == pPageFrame;
+}
+
+void SwAccessibleMap::FireEvents()
+{
+ DBG_TESTSOLARMUTEX();
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ if (mpEvents->IsFiring())
+ {
+ return; // prevent recursive FireEvents()
+ }
+
+ mpEvents->SetFiring();
+ mpEvents->MoveMissingXAccToEnd();
+ for( auto const& aEvent : *mpEvents )
+ FireEvent(aEvent);
+
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ }
+ mvShapes.clear();
+}
+
+tools::Rectangle SwAccessibleMap::GetVisibleArea() const
+{
+ return o3tl::convert( GetVisArea().SVRect(), o3tl::Length::twip, o3tl::Length::mm100 );
+}
+
+// Convert a MM100 value relative to the document root into a pixel value
+// relative to the screen!
+Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const
+{
+ Point aPoint = o3tl::toTwips( rPoint, o3tl::Length::mm100 );
+ if (const vcl::Window* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( aPoint, aMapMode );
+ aPoint = pWin->LogicToPixel( aPoint, aMapMode );
+ aPoint = Point(pWin->OutputToAbsoluteScreenPixel( aPoint ));
+ }
+
+ return aPoint;
+}
+
+Size SwAccessibleMap::LogicToPixel( const Size& rSize ) const
+{
+ Size aSize( o3tl::toTwips( rSize, o3tl::Length::mm100 ) );
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( Point(0,0), aMapMode );
+ aSize = pWin->LogicToPixel( aSize, aMapMode );
+ }
+
+ return aSize;
+}
+
+bool SwAccessibleMap::ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const uno::Reference< drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/,
+ const ::accessibility::AccessibleShapeTreeInfo& /*_rShapeTreeInfo*/
+ )
+{
+ DBG_TESTSOLARMUTEX();
+
+ const SdrObject *pObj = nullptr;
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter && !pObj )
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if( xAcc.get() == pCurrentChild )
+ {
+ pObj = (*aIter).first;
+ }
+ ++aIter;
+ }
+ }
+ if( !pObj )
+ return false;
+
+ uno::Reference < drawing::XShape > xShape( _rxShape ); // keep reference to shape, because
+ // we might be the only one that
+ // holds it.
+ // Also get keep parent.
+ uno::Reference < XAccessible > xParent( pCurrentChild->getAccessibleParent() );
+ pCurrentChild = nullptr; // will be released by dispose
+ A11yDispose( nullptr, pObj, nullptr );
+
+ if( !mpShapeMap )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+
+ // create the new child
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+ rtl::Reference< ::accessibility::AccessibleShape> pReplacement(
+ rShapeTypeHandler.CreateAccessibleObject (
+ aShapeInfo, mpShapeMap->GetInfo() ));
+
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( pReplacement );
+ if( xAcc.is() )
+ {
+ pReplacement->Init();
+
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc.get();
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ }
+
+ SwRect aEmptyRect;
+ InvalidatePosOrSize( nullptr, pObj, nullptr, aEmptyRect );
+
+ return true;
+}
+
+//Get the accessible control shape from the model object, here model object is with XPropertySet type
+::accessibility::AccessibleControlShape * SwAccessibleMap::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter)
+ {
+ rtl::Reference < ::accessibility::AccessibleShape > xAcc( (*aIter).second );
+ if(xAcc && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (xAcc->GetXShape()) == ::accessibility::DRAWING_CONTROL)
+ {
+ ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(xAcc.get());
+ if (pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ ++aIter;
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Reference< XAccessible >
+ SwAccessibleMap::GetAccessibleCaption (const css::uno::Reference< css::drawing::XShape >&)
+{
+ return nullptr;
+}
+
+Point SwAccessibleMap::PixelToCore( const Point& rPoint ) const
+{
+ Point aPoint;
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( rPoint, aMapMode );
+ aPoint = pWin->PixelToLogic( rPoint, aMapMode );
+ }
+ return aPoint;
+}
+
+static tools::Long lcl_CorrectCoarseValue(tools::Long aCoarseValue, tools::Long aFineValue,
+ tools::Long aRefValue, bool bToLower)
+{
+ tools::Long aResult = aCoarseValue;
+
+ if (bToLower)
+ {
+ if (aFineValue < aRefValue)
+ aResult -= 1;
+ }
+ else
+ {
+ if (aFineValue > aRefValue)
+ aResult += 1;
+ }
+
+ return aResult;
+}
+
+static void lcl_CorrectRectangle(tools::Rectangle & rRect,
+ const tools::Rectangle & rSource,
+ const tools::Rectangle & rInGrid)
+{
+ rRect.SetLeft( lcl_CorrectCoarseValue(rRect.Left(), rSource.Left(),
+ rInGrid.Left(), false) );
+ rRect.SetTop( lcl_CorrectCoarseValue(rRect.Top(), rSource.Top(),
+ rInGrid.Top(), false) );
+ rRect.SetRight( lcl_CorrectCoarseValue(rRect.Right(), rSource.Right(),
+ rInGrid.Right(), true) );
+ rRect.SetBottom( lcl_CorrectCoarseValue(rRect.Bottom(), rSource.Bottom(),
+ rInGrid.Bottom(), true) );
+}
+
+tools::Rectangle SwAccessibleMap::CoreToPixel( const SwRect& rRect ) const
+{
+ tools::Rectangle aRect;
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( rRect.TopLeft(), aMapMode );
+ aRect = pWin->LogicToPixel( rRect.SVRect(), aMapMode );
+
+ tools::Rectangle aTmpRect = pWin->PixelToLogic( aRect, aMapMode );
+ lcl_CorrectRectangle(aRect, rRect.SVRect(), aTmpRect);
+ }
+
+ return aRect;
+}
+
+/** get mapping mode for LogicToPixel and PixelToLogic conversions
+
+ Method returns mapping mode of current output device and adjusts it,
+ if the shell is in page/print preview.
+ Necessary, because <PreviewAdjust(..)> changes mapping mode at current
+ output device for mapping logic document positions to page preview window
+ positions and vice versa and doesn't take care to recover its changes.
+*/
+void SwAccessibleMap::GetMapMode( const Point& _rPoint,
+ MapMode& _orMapMode ) const
+{
+ MapMode aMapMode = GetShell()->GetWin()->GetMapMode();
+ if( GetShell()->IsPreview() )
+ {
+ assert(mpPreview != nullptr);
+ mpPreview->AdjustMapMode( aMapMode, _rPoint );
+ }
+ _orMapMode = aMapMode;
+}
+
+Size SwAccessibleMap::GetPreviewPageSize(sal_uInt16 const nPreviewPageNum) const
+{
+ assert(mpVSh->IsPreview());
+ assert(mpPreview != nullptr);
+ return mpVSh->PagePreviewLayout()->GetPreviewPageSizeByPageNum(nPreviewPageNum);
+}
+
+/** method to build up a new data structure of the accessible paragraphs,
+ which have a selection
+ Important note: method has to be used inside a mutual exclusive section
+*/
+std::unique_ptr<SwAccessibleSelectedParas_Impl> SwAccessibleMap::BuildSelectedParas()
+{
+ // no accessible contexts, no selection
+ if ( !mpFrameMap )
+ {
+ return nullptr;
+ }
+
+ // get cursor as an instance of its base class <SwPaM>
+ SwPaM* pCursor( nullptr );
+ {
+ SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>(GetShell());
+ if ( pCursorShell )
+ {
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ if ( !pFEShell ||
+ ( !pFEShell->IsFrameSelected() &&
+ pFEShell->IsObjSelected() == 0 ) )
+ {
+ // get cursor without updating an existing table cursor.
+ pCursor = pCursorShell->GetCursor( false );
+ }
+ }
+ }
+ // no cursor, no selection
+ if ( !pCursor )
+ {
+ return nullptr;
+ }
+
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pRetSelectedParas;
+
+ // loop on all cursors
+ SwPaM* pRingStart = pCursor;
+ do {
+
+ // for a selection the cursor has to have a mark.
+ // for safety reasons assure that point and mark are in text nodes
+ if ( pCursor->HasMark() &&
+ pCursor->GetPoint()->GetNode().IsTextNode() &&
+ pCursor->GetMark()->GetNode().IsTextNode() )
+ {
+ auto [pStartPos, pEndPos] = pCursor->StartEnd(); // SwPosition*
+ // loop on all text nodes inside the selection
+ SwNodeIndex aIdx( pStartPos->GetNode() );
+ for ( ; aIdx.GetIndex() <= pEndPos->GetNodeIndex(); ++aIdx )
+ {
+ SwTextNode* pTextNode( aIdx.GetNode().GetTextNode() );
+ if ( pTextNode )
+ {
+ // loop on all text frames registered at the text node.
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
+ for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame; pTextFrame = aIter.Next() )
+ {
+ unotools::WeakReference < SwAccessibleContext > xWeakAcc;
+ SwAccessibleContextMap_Impl::iterator aMapIter =
+ mpFrameMap->find( pTextFrame );
+ if( aMapIter != mpFrameMap->end() )
+ {
+ xWeakAcc = (*aMapIter).second;
+ SwAccessibleParaSelection aDataEntry(
+ sw::FrameContainsNode(*pTextFrame, pStartPos->GetNodeIndex())
+ ? pTextFrame->MapModelToViewPos(*pStartPos)
+ : TextFrameIndex(0),
+
+ sw::FrameContainsNode(*pTextFrame, pEndPos->GetNodeIndex())
+ ? pTextFrame->MapModelToViewPos(*pEndPos)
+ : TextFrameIndex(COMPLETE_STRING));
+ if ( !pRetSelectedParas )
+ {
+ pRetSelectedParas.reset(
+ new SwAccessibleSelectedParas_Impl);
+ }
+ // sw_redlinehide: should be idempotent for multiple nodes in a merged para
+ pRetSelectedParas->emplace( xWeakAcc, aDataEntry );
+ }
+ }
+ }
+ }
+ }
+
+ // prepare next turn: get next cursor in ring
+ pCursor = pCursor->GetNext();
+ } while ( pCursor != pRingStart );
+
+ return pRetSelectedParas;
+}
+
+void SwAccessibleMap::InvalidateTextSelectionOfAllParas()
+{
+ DBG_TESTSOLARMUTEX();
+
+ // keep previously known selected paragraphs
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pPrevSelectedParas( std::move(mpSelectedParas) );
+
+ // determine currently selected paragraphs
+ mpSelectedParas = BuildSelectedParas();
+
+ // compare currently selected paragraphs with the previously selected
+ // paragraphs and submit corresponding TEXT_SELECTION_CHANGED events.
+ // first, search for new and changed selections.
+ // on the run remove selections from previously known ones, if they are
+ // also in the current ones.
+ if ( mpSelectedParas )
+ {
+ SwAccessibleSelectedParas_Impl::iterator aIter = mpSelectedParas->begin();
+ for ( ; aIter != mpSelectedParas->end(); ++aIter )
+ {
+ bool bSubmitEvent( false );
+ if ( !pPrevSelectedParas )
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ else
+ {
+ SwAccessibleSelectedParas_Impl::iterator aPrevSelected =
+ pPrevSelectedParas->find( (*aIter).first );
+ if ( aPrevSelected != pPrevSelectedParas->end() )
+ {
+ // check, if selection has changed
+ if ( (*aIter).second.nStartOfSelection !=
+ (*aPrevSelected).second.nStartOfSelection ||
+ (*aIter).second.nEndOfSelection !=
+ (*aPrevSelected).second.nEndOfSelection )
+ {
+ // changed selection
+ bSubmitEvent = true;
+ }
+ pPrevSelectedParas->erase( aPrevSelected );
+ }
+ else
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ }
+
+ if ( bSubmitEvent )
+ {
+ rtl::Reference < SwAccessibleContext > xAcc( (*aIter).first );
+ if ( xAcc.is() && xAcc->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame = xAcc->GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+ }
+ }
+
+ // second, handle previous selections - after the first step the data
+ // structure of the previously known only contains the 'old' selections
+ if ( !pPrevSelectedParas )
+ return;
+
+ SwAccessibleSelectedParas_Impl::iterator aIter = pPrevSelectedParas->begin();
+ for ( ; aIter != pPrevSelectedParas->end(); ++aIter )
+ {
+ rtl::Reference < SwAccessibleContext > xAcc( (*aIter).first );
+ if ( xAcc.is() && xAcc->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame = xAcc->GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+}
+
+const SwRect& SwAccessibleMap::GetVisArea() const
+{
+ assert(!GetShell()->IsPreview() || (mpPreview != nullptr));
+
+ return GetShell()->IsPreview()
+ ? mpPreview->GetVisArea()
+ : GetShell()->VisArea();
+}
+
+bool SwAccessibleMap::IsDocumentSelAll()
+{
+ return GetShell()->GetDoc()->IsPrepareSelAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotextframe.cxx b/sw/source/core/access/accnotextframe.cxx
new file mode 100644
index 0000000000..d3f7c38bae
--- /dev/null
+++ b/sw/source/core/access/accnotextframe.cxx
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <frmfmt.hxx>
+#include <ndnotxt.hxx>
+#include <flyfrm.hxx>
+#include <notxtfrm.hxx>
+#include <hints.hxx>
+#include "accnotextframe.hxx"
+#include <fmturl.hxx>
+#include "accnotexthyperlink.hxx"
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using utl::AccessibleRelationSetHelper;
+
+const SwNoTextNode *SwAccessibleNoTextFrame::GetNoTextNode() const
+{
+ const SwNoTextNode *pNd = nullptr;
+ const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame *>( GetFrame() );
+ if( pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame() )
+ {
+ const SwNoTextFrame *pContentFrame =
+ static_cast<const SwNoTextFrame *>(pFlyFrame->Lower());
+ const SwContentNode* pSwContentNode = pContentFrame->GetNode();
+ if(pSwContentNode != nullptr)
+ {
+ pNd = pSwContentNode->GetNoTextNode();
+ }
+ }
+
+ return pNd;
+}
+
+SwAccessibleNoTextFrame::SwAccessibleNoTextFrame(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleFrameBase( pInitMap, nInitRole, pFlyFrame )
+{
+ const SwNoTextNode* pNd = GetNoTextNode();
+ // #i73249#
+ // consider new attributes Title and Description
+ if( pNd )
+ {
+ StartListening(const_cast<SwNoTextNode*>(pNd)->GetNotifier());
+ msTitle = pNd->GetTitle();
+
+ msDesc = pNd->GetDescription();
+ if ( msDesc.isEmpty() &&
+ msTitle != GetName() )
+ {
+ msDesc = msTitle;
+ }
+ }
+}
+
+SwAccessibleNoTextFrame::~SwAccessibleNoTextFrame()
+{
+}
+
+void SwAccessibleNoTextFrame::Notify(const SfxHint& rHint)
+{
+ const SwNoTextNode* pNd = GetNoTextNode();
+ switch(rHint.GetId())
+ {
+ case SfxHintId::Dying:
+ EndListeningAll();
+ return;
+ default:
+ return;
+ case SfxHintId::SwTitleChanged:
+ {
+ auto rTitleChanged = static_cast<const sw::TitleChanged&>(rHint);
+ if(rTitleChanged.m_sOld == rTitleChanged.m_sNew)
+ break;
+ msTitle = rTitleChanged.m_sNew;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= rTitleChanged.m_sOld;
+ aEvent.NewValue <<= msTitle;
+ FireAccessibleEvent(aEvent);
+
+ if(!pNd->GetDescription().isEmpty())
+ break;
+ [[fallthrough]];
+ }
+ case SfxHintId::SwDescriptionChanged:
+ if(pNd && GetFrame())
+ {
+ const OUString sOldDesc(msDesc);
+ msDesc = pNd->GetDescription();
+ if(msDesc.isEmpty() && msTitle != GetName())
+ msDesc = msTitle;
+ if(msDesc == sOldDesc)
+ return;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= msDesc;
+ FireAccessibleEvent(aEvent);
+ }
+ return;
+ }
+}
+
+void SwAccessibleNoTextFrame::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleFrameBase::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+// #i73249#
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if ( !msTitle.isEmpty() )
+ {
+ return msTitle;
+ }
+
+ return SwAccessibleFrameBase::getAccessibleName();
+}
+
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return msDesc;
+}
+
+// XInterface
+
+uno::Any SAL_CALL SwAccessibleNoTextFrame::queryInterface( const uno::Type& aType )
+{
+ if( aType ==
+ ::cppu::UnoType<XAccessibleImage>::get() )
+ {
+ uno::Reference<XAccessibleImage> xImage = this;
+ return uno::Any(xImage);
+ }
+ else if ( aType == cppu::UnoType<XAccessibleHypertext>::get())
+ {
+ uno::Reference<XAccessibleHypertext> aAccHypertext = this;
+ return uno::Any( aAccHypertext );
+ }
+ else
+ return SwAccessibleContext::queryInterface( aType );
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleNoTextFrame::getTypes()
+{
+ return cppu::OTypeCollection(
+ ::cppu::UnoType<XAccessibleImage>::get(),
+ SwAccessibleFrameBase::getTypes() ).getTypes();
+}
+
+/// XAccessibleImage
+/** implementation of the XAccessibleImage methods is a no-brainer, as
+ all relevant information is already accessible through other
+ methods. So we just delegate to those. */
+
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageDescription()
+{
+ return getAccessibleDescription();
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageHeight( )
+{
+ return getSize().Height;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageWidth( )
+{
+ return getSize().Width;
+}
+
+// XAccessibleText
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getCaretPosition( ){return 0;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::setCaretPosition( sal_Int32 ){return false;}
+sal_Unicode SAL_CALL SwAccessibleNoTextFrame::getCharacter( sal_Int32 ){return 0;}
+css::uno::Sequence< css::beans::PropertyValue > SAL_CALL SwAccessibleNoTextFrame::getCharacterAttributes( sal_Int32 , const css::uno::Sequence< OUString >& )
+{
+ return uno::Sequence<beans::PropertyValue>();
+}
+css::awt::Rectangle SAL_CALL SwAccessibleNoTextFrame::getCharacterBounds( sal_Int32 )
+{
+ return css::awt::Rectangle(0, 0, 0, 0 );
+}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getCharacterCount( ){return 0;}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getIndexAtPoint( const css::awt::Point& ){return 0;}
+OUString SAL_CALL SwAccessibleNoTextFrame::getSelectedText( ){return OUString();}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getSelectionStart( ){return 0;}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getSelectionEnd( ){return 0;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::setSelection( sal_Int32 , sal_Int32 ){return true;}
+OUString SAL_CALL SwAccessibleNoTextFrame::getText( ){return OUString();}
+OUString SAL_CALL SwAccessibleNoTextFrame::getTextRange( sal_Int32 , sal_Int32 ){return OUString();}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextAtIndex( sal_Int32 , sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextBeforeIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextBehindIndex( sal_Int32 , sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::copyText( sal_Int32, sal_Int32 ){return true;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ){return false;}
+
+// XAccessibleHyperText
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getHyperLinkCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if(aURL.GetMap() || !aURL.GetURL().isEmpty())
+ nCount = 1;
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ SwAccessibleNoTextFrame::getHyperLink( sal_Int32 nLinkIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if( nLinkIndex > 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ if( aURL.GetMap() || !aURL.GetURL().isEmpty() )
+ {
+ if ( !m_xHyperlink.is() )
+ {
+ m_xHyperlink = new SwAccessibleNoTextHyperlink( this, GetFrame() );
+ }
+
+ return m_xHyperlink;
+ }
+
+ return nullptr;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getHyperLinkIndex( sal_Int32 )
+{
+ return 0;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleNoTextFrame::getAccessibleRelationSet( )
+{
+ return new AccessibleRelationSetHelper();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotextframe.hxx b/sw/source/core/access/accnotextframe.hxx
new file mode 100644
index 0000000000..13680ffa5f
--- /dev/null
+++ b/sw/source/core/access/accnotextframe.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTFRAME_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTFRAME_HXX
+
+#include "accframebase.hxx"
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessibleImage.hpp>
+#include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
+
+class SwFlyFrame;
+class SwNoTextNode;
+
+class SwAccessibleNoTextFrame : public SwAccessibleFrameBase,
+ public css::accessibility::XAccessibleImage,
+ public css::accessibility::XAccessibleHypertext//Added by yangzhh for HyperLink
+{
+ friend class SwAccessibleNoTextHyperlink;
+ css::uno::Reference< css::accessibility::XAccessibleHyperlink > m_xHyperlink;
+ OUString msTitle;
+ OUString msDesc;
+
+protected:
+ virtual ~SwAccessibleNoTextFrame() override;
+
+ const SwNoTextNode *GetNoTextNode() const;
+
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleNoTextFrame( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame *pFlyFrame );
+
+ // XAccessibleContext
+
+ // #i73249# - Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleImage.
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XAccessibleImage
+ virtual OUString SAL_CALL
+ getAccessibleImageDescription( ) override;
+
+ virtual sal_Int32 SAL_CALL
+ getAccessibleImageHeight( ) override;
+
+ virtual sal_Int32 SAL_CALL
+ getAccessibleImageWidth( ) override;
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual sal_Int32 SAL_CALL getCaretPosition( ) override;
+ virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;//Shen Zhen Jie changed sal_Unicode to sal_uInt32
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd( ) override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XAccessibleHypertext
+ virtual sal_Int32 SAL_CALL getHyperLinkCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleHyperlink >
+ SAL_CALL getHyperLink( sal_Int32 nLinkIndex ) override;
+ virtual sal_Int32 SAL_CALL getHyperLinkIndex( sal_Int32 nCharIndex ) override;
+
+ SwAccessibleMap *GetAccessibleMap(){ return GetMap();}
+
+public:
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotexthyperlink.cxx b/sw/source/core/access/accnotexthyperlink.cxx
new file mode 100644
index 0000000000..36ded73900
--- /dev/null
+++ b/sw/source/core/access/accnotexthyperlink.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <comphelper/accessiblekeybindinghelper.hxx>
+#include <swurl.hxx>
+#include <vcl/svapp.hxx>
+#include <frmfmt.hxx>
+
+#include "accnotexthyperlink.hxx"
+
+#include <fmturl.hxx>
+
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/keycodes.hxx>
+
+#include <accmap.hxx>
+
+using namespace css;
+using namespace css::lang;
+using namespace css::uno;
+using namespace css::accessibility;
+
+SwAccessibleNoTextHyperlink::SwAccessibleNoTextHyperlink( SwAccessibleNoTextFrame *p, const SwFrame *aFrame ) :
+ mxFrame( p ),
+ mpFrame( aFrame )
+{
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionCount()
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ return pMap->GetIMapObjectCount();
+ }
+ else if( !aURL.GetURL().isEmpty() )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextHyperlink::doAccessibleAction( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ bool bRet = false;
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ {
+ SwViewShell *pVSh = mxFrame->GetShell();
+ if( pVSh )
+ {
+ LoadURL( *pVSh, pMapObj->GetURL(), LoadUrlFlags::NONE,
+ pMapObj->GetTarget() );
+ bRet = true;
+ }
+ }
+ }
+ else if (!aURL.GetURL().isEmpty())
+ {
+ SwViewShell *pVSh = mxFrame->GetShell();
+ if( pVSh )
+ {
+ LoadURL( *pVSh, aURL.GetURL(), LoadUrlFlags::NONE,
+ aURL.GetTargetFrameName() );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionDescription(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ OUString sDesc;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetDesc().isEmpty())
+ sDesc = pMapObj->GetDesc();
+ else if (!pMapObj->GetURL().isEmpty())
+ sDesc = pMapObj->GetURL();
+ }
+ else if( !aURL.GetURL().isEmpty() )
+ sDesc = aURL.GetName();
+
+ return sDesc;
+}
+
+Reference< XAccessibleKeyBinding > SAL_CALL
+ SwAccessibleNoTextHyperlink::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ Reference< XAccessibleKeyBinding > xKeyBinding;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ bool bIsValid = false;
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ bIsValid = true;
+ }
+ else if (!aURL.GetURL().isEmpty())
+ bIsValid = true;
+
+ if(bIsValid)
+ {
+ rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper =
+ new ::comphelper::OAccessibleKeyBindingHelper();
+ xKeyBinding = pKeyBindingHelper;
+
+ css::awt::KeyStroke aKeyStroke;
+ aKeyStroke.Modifiers = 0;
+ aKeyStroke.KeyCode = KEY_RETURN;
+ aKeyStroke.KeyChar = 0;
+ aKeyStroke.KeyFunc = 0;
+ pKeyBindingHelper->AddKeyBinding( aKeyStroke );
+ }
+
+ return xKeyBinding;
+}
+
+// XAccessibleHyperlink
+Any SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionAnchor(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ Any aRet;
+ //SwFrame* pAnchor = static_cast<SwFlyFrame*>(mpFrame)->GetAnchor();
+ Reference< XAccessible > xAnchor = mxFrame->GetAccessibleMap()->GetContext(mpFrame);
+ //SwAccessibleNoTextFrame* pFrame = xFrame.get();
+ //Reference< XAccessible > xAnchor = (XAccessible*)pFrame;
+ aRet <<= xAnchor;
+ return aRet;
+}
+
+Any SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionObject(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ OUString retText;
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ retText = pMapObj->GetURL();
+ }
+ else if ( !aURL.GetURL().isEmpty() )
+ retText = aURL.GetURL();
+
+ Any aRet;
+ aRet <<= retText;
+ return aRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getStartIndex()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getEndIndex()
+{
+ return 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextHyperlink::isValid( )
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+
+ if( aURL.GetMap() || !aURL.GetURL().isEmpty() )
+ return true;
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotexthyperlink.hxx b/sw/source/core/access/accnotexthyperlink.hxx
new file mode 100644
index 0000000000..b986abc87e
--- /dev/null
+++ b/sw/source/core/access/accnotexthyperlink.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTHYPERLINK_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTHYPERLINK_HXX
+
+#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <layfrm.hxx>
+
+#include "accnotextframe.hxx"
+
+class SwAccessibleNoTextHyperlink :
+ public ::cppu::WeakImplHelper<
+ css::accessibility::XAccessibleHyperlink >
+{
+ friend class SwAccessibleNoTextFrame;
+
+ ::rtl::Reference< SwAccessibleNoTextFrame > mxFrame;
+ const SwFrame *mpFrame;
+
+ SwFrameFormat *GetFormat()
+ {
+ return const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(mpFrame))->GetFormat();
+ }
+public:
+
+ SwAccessibleNoTextHyperlink( SwAccessibleNoTextFrame *p, const SwFrame* aFrame );
+
+ // XAccessibleAction
+ virtual sal_Int32 SAL_CALL getAccessibleActionCount() override;
+ virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override;
+ virtual OUString SAL_CALL getAccessibleActionDescription(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL
+ getAccessibleActionKeyBinding( sal_Int32 nIndex ) override;
+
+ // XAccessibleHyperlink
+ virtual css::uno::Any SAL_CALL getAccessibleActionAnchor(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Any SAL_CALL getAccessibleActionObject(
+ sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getStartIndex() override;
+ virtual sal_Int32 SAL_CALL getEndIndex() override;
+ virtual sal_Bool SAL_CALL isValid( ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpage.cxx b/sw/source/core/access/accpage.cxx
new file mode 100644
index 0000000000..b3427559b3
--- /dev/null
+++ b/sw/source/core/access/accpage.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/window.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include "accpage.hxx"
+
+#include <strings.hrc>
+#include <pagefrm.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using uno::Sequence;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessiblePageView";
+
+bool SwAccessiblePage::IsSelected()
+{
+ return GetMap()->IsPageSelected( static_cast < const SwPageFrame * >( GetFrame() ) );
+}
+
+void SwAccessiblePage::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // FOCUSABLE
+ rStateSet |= AccessibleStateType::FOCUSABLE;
+
+ // FOCUSED
+ if( IsSelected() )
+ {
+ OSL_ENSURE( m_bIsSelected, "bSelected out of sync" );
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet |= AccessibleStateType::FOCUSED;
+ }
+}
+
+void SwAccessiblePage::InvalidateCursorPos_()
+{
+ bool bNewSelected = IsSelected();
+ bool bOldSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldSelected = m_bIsSelected;
+ m_bIsSelected = bNewSelected;
+ }
+
+ if( bNewSelected )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ if( bOldSelected != bNewSelected )
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ }
+}
+
+void SwAccessiblePage::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( !pWin )
+ return;
+
+ bool bSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bSelected = m_bIsSelected;
+ }
+ OSL_ENSURE( bSelected, "focus object should be selected" );
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && bSelected );
+}
+
+SwAccessiblePage::SwAccessiblePage(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFrame* pFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::PANEL, pFrame )
+ , m_bIsSelected( false )
+{
+ assert(pFrame != nullptr);
+ assert(pInitMap != nullptr);
+ assert(pFrame->IsPageFrame());
+
+ OUString sPage = OUString::number(
+ static_cast<const SwPageFrame*>( GetFrame() )->GetPhyPageNum() );
+ SetName( GetResource( STR_ACCESS_PAGE_NAME, &sPage ) );
+}
+
+SwAccessiblePage::~SwAccessiblePage()
+{
+}
+
+bool SwAccessiblePage::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+OUString SwAccessiblePage::getImplementationName( )
+{
+ return sImplementationName;
+}
+
+sal_Bool SwAccessiblePage::supportsService( const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence<OUString> SwAccessiblePage::getSupportedServiceNames( )
+{
+ return { "com.sun.star.text.AccessiblePageView", sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessiblePage::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString SwAccessiblePage::getAccessibleDescription( )
+{
+ ThrowIfDisposed();
+
+ OUString sArg( GetFormattedPageNumber() );
+ return GetResource( STR_ACCESS_PAGE_DESC, &sArg );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpage.hxx b/sw/source/core/access/accpage.hxx
new file mode 100644
index 0000000000..9c4d06ba53
--- /dev/null
+++ b/sw/source/core/access/accpage.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPAGE_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPAGE_HXX
+
+#include "acccontext.hxx"
+
+/**
+ * accessibility implementation for the page (SwPageFrame)
+ * The page is _only_ visible in the page preview. For the regular
+ * document view, it doesn't make sense to add this additional element
+ * into the hierarchy. For the page preview, however, the page is the
+ * important.
+ */
+class SwAccessiblePage : public SwAccessibleContext
+{
+ bool m_bIsSelected; // protected by base class mutex
+
+ bool IsSelected();
+
+ using SwAccessibleFrame::GetBounds;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets
+ // FOCUSABLE(1) and FOCUSED(+)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessiblePage() override;
+
+public:
+ // convenience constructor to avoid typecast;
+ // may only be called with SwPageFrame argument
+ SwAccessiblePage(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFrame* pFrame);
+
+ // XAccessibleContext methods that need to be overridden
+
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService (
+ const OUString& sServiceName) override;
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ virtual bool HasCursor() override; // required by map to remember that object
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpara.cxx b/sw/source/core/access/accpara.cxx
new file mode 100644
index 0000000000..f0bd0f0653
--- /dev/null
+++ b/sw/source/core/access/accpara.cxx
@@ -0,0 +1,3531 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <numeric>
+#include <txtfrm.hxx>
+#include <flyfrm.hxx>
+#include <mdiexp.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <unotextrange.hxx>
+#include <unocrsrhelper.hxx>
+#include <crstate.hxx>
+#include <accmap.hxx>
+#include <fesh.hxx>
+#include <viewopt.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <breakit.hxx>
+#include "accpara.hxx"
+#include "accportions.hxx"
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <unocrsr.hxx>
+#include <unoport.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include "acchyperlink.hxx"
+#include "acchypertextdata.hxx"
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <comphelper/accessibletexthelper.hxx>
+#include <algorithm>
+#include <docufld.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <modcfg.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <swmodule.hxx>
+#include <redline.hxx>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <wrong.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <swatrset.hxx>
+#include <unosett.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <viewimp.hxx>
+#include "textmarkuphelper.hxx"
+#include "parachangetrackinginfo.hxx"
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <svx/colorwindow.hxx>
+#include <o3tl/string_view.hxx>
+#include <editeng/editids.hrc>
+
+#include <reffld.hxx>
+#include <flddat.hxx>
+#include "../../uibase/inc/fldmgr.hxx"
+#include <fldbas.hxx> // SwField
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::container;
+
+using beans::PropertyValue;
+using beans::UnknownPropertyException;
+using beans::PropertyState_DIRECT_VALUE;
+
+using std::max;
+using std::min;
+using std::sort;
+
+namespace com::sun::star::text {
+ class XText;
+}
+
+constexpr OUString sServiceName = u"com.sun.star.text.AccessibleParagraphView"_ustr;
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleParagraphView";
+
+OUString const & SwAccessibleParagraph::GetString()
+{
+ return GetPortionData().GetAccessibleString();
+}
+
+OUString SwAccessibleParagraph::GetDescription()
+{
+ return OUString(); // provide empty description for paragraphs
+}
+
+sal_Int32 SwAccessibleParagraph::GetCaretPos()
+{
+ sal_Int32 nRet = -1;
+
+ // get the selection's point, and test whether it's in our node
+ // #i27301# - consider adjusted method signature
+ SwPaM* pCaret = GetCursor( false ); // caret is first PaM in PaM-ring
+
+ if( pCaret != nullptr )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ assert(pTextFrame);
+
+ // check whether the point points into 'our' node
+ SwPosition* pPoint = pCaret->GetPoint();
+ if (sw::FrameContainsNode(*pTextFrame, pPoint->GetNodeIndex()))
+ {
+ // same node? Then check whether it's also within 'our' part
+ // of the paragraph
+ const TextFrameIndex nIndex = pTextFrame->MapModelToViewPos(*pPoint);
+ if(!GetPortionData().IsValidCorePosition( nIndex ) ||
+ (GetPortionData().IsZeroCorePositionData()
+ && nIndex == TextFrameIndex(0)))
+ {
+ bool bFormat = pTextFrame->HasPara();
+ if(bFormat)
+ {
+ ClearPortionData();
+ UpdatePortionData();
+ }
+ }
+ if( GetPortionData().IsValidCorePosition( nIndex ) )
+ {
+ // Yes, it's us!
+ // consider that cursor/caret is in front of the list label
+ if ( pCaret->IsInFrontOfLabel() )
+ {
+ nRet = 0;
+ }
+ else
+ {
+ nRet = GetPortionData().GetAccessiblePosition( nIndex );
+ }
+
+ OSL_ENSURE( nRet >= 0, "invalid cursor?" );
+ OSL_ENSURE( nRet <= GetPortionData().GetAccessibleString().
+ getLength(), "invalid cursor?" );
+ }
+ // else: in this paragraph, but in different frame
+ }
+ // else: not in this paragraph
+ }
+ // else: no cursor -> no caret
+
+ return nRet;
+}
+
+// #i27301# - new parameter <_bForSelection>
+SwPaM* SwAccessibleParagraph::GetCursor( const bool _bForSelection )
+{
+ // get the cursor shell; if we don't have any, we don't have a
+ // cursor/selection either
+ SwPaM* pCursor = nullptr;
+ SwCursorShell* pCursorShell = SwAccessibleParagraph::GetCursorShell();
+ // #i27301# - if cursor is retrieved for selection, the cursors for
+ // a table selection has to be returned.
+ if ( pCursorShell != nullptr &&
+ ( _bForSelection || !pCursorShell->IsTableMode() ) )
+ {
+ SwFEShell *pFESh = dynamic_cast<SwFEShell*>(pCursorShell);
+ if( !pFESh ||
+ !(pFESh->IsFrameSelected() || pFESh->IsObjSelected() > 0) )
+ {
+ // get the selection, and test whether it affects our text node
+ pCursor = pCursorShell->GetCursor( false /* ??? */ );
+ }
+ }
+
+ return pCursor;
+}
+
+bool SwAccessibleParagraph::IsHeading() const
+{
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *pTextNd = pFrame->GetTextNodeForParaProps();
+ return pTextNd->IsOutline();
+}
+
+void SwAccessibleParagraph::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // MULTILINE
+ rStateSet |= AccessibleStateType::MULTI_LINE;
+
+ if (GetCursorShell())
+ {
+ // MULTISELECTABLE
+ rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ // FOCUSABLE
+ rStateSet |= AccessibleStateType::FOCUSABLE;
+ }
+
+ // FOCUSED (simulates node index of cursor)
+ SwPaM* pCaret = GetCursor( false ); // #i27301# - consider adjusted method signature
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ assert(pFrame);
+ if (pCaret != nullptr &&
+ sw::FrameContainsNode(*pFrame, pCaret->GetPoint()->GetNodeIndex()) &&
+ m_nOldCaretPos != -1)
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet |= AccessibleStateType::FOCUSED;
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+}
+
+void SwAccessibleParagraph::InvalidateContent_( bool bVisibleDataFired )
+{
+ OUString sOldText( GetString() );
+
+ ClearPortionData();
+
+ const OUString sText = GetString();
+
+ if( sText != sOldText )
+ {
+ // The text is changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_CHANGED;
+
+ // determine exact changes between sOldText and sText
+ (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(sOldText, sText,
+ aEvent.OldValue,
+ aEvent.NewValue);
+
+ FireAccessibleEvent( aEvent );
+ uno::Reference< XAccessible > xparent = getAccessibleParent();
+ uno::Reference< XAccessibleContext > xAccContext(xparent,uno::UNO_QUERY);
+ if (xAccContext.is() && xAccContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
+ {
+ SwAccessibleContext* pPara = static_cast< SwAccessibleContext* >(xparent.get());
+ if(pPara)
+ {
+ AccessibleEventObject aParaEvent;
+ aParaEvent.EventId = AccessibleEventId::VALUE_CHANGED;
+ pPara->FireAccessibleEvent(aParaEvent);
+ }
+ }
+ }
+ else if( !bVisibleDataFired )
+ {
+ FireVisibleDataEvent();
+ }
+
+ bool bNewIsBlockQuote = IsBlockQuote();
+ bool bNewIsHeading = IsHeading();
+ //Get the real heading level, Heading1 ~ Heading10
+ m_nHeadingLevel = GetRealHeadingLevel();
+ bool bOldIsBlockQuote;
+ bool bOldIsHeading;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldIsBlockQuote = m_bIsBlockQuote;
+ bOldIsHeading = m_bIsHeading;
+ m_bIsBlockQuote = bNewIsBlockQuote;
+ if( m_bIsHeading != bNewIsHeading )
+ m_bIsHeading = bNewIsHeading;
+ }
+
+ if (bNewIsBlockQuote != bOldIsBlockQuote || bNewIsHeading != bOldIsHeading)
+ {
+ // The role has changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ROLE_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+ }
+
+ if( sText == sOldText )
+ return;
+
+ OUString sNewDesc( GetDescription() );
+ OUString sOldDesc;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ sOldDesc = m_sDesc;
+ if( m_sDesc != sNewDesc )
+ m_sDesc = sNewDesc;
+ }
+
+ if( sNewDesc != sOldDesc )
+ {
+ // The text is changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= sNewDesc;
+
+ FireAccessibleEvent( aEvent );
+ }
+}
+
+void SwAccessibleParagraph::InvalidateCursorPos_()
+{
+ // The text is changed
+ sal_Int32 nNew = GetCaretPos();
+ sal_Int32 nOld;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ nOld = m_nOldCaretPos;
+ m_nOldCaretPos = nNew;
+ }
+ if( -1 != nNew )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ vcl::Window *pWin = GetWindow();
+ if( nOld == nNew )
+ return;
+
+ // The cursor's node position is simulated by the focus!
+ if( pWin && pWin->HasFocus() && -1 == nOld )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CARET_CHANGED;
+ aEvent.OldValue <<= nOld;
+ aEvent.NewValue <<= nNew;
+
+ FireAccessibleEvent( aEvent );
+
+ if( pWin && pWin->HasFocus() && -1 == nNew )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, false );
+ //To send TEXT_SELECTION_CHANGED event
+ sal_Int32 nStart=0;
+ sal_Int32 nEnd =0;
+ bool bCurSelection = GetSelection(nStart,nEnd);
+ if(m_bLastHasSelection || bCurSelection )
+ {
+ aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;
+ aEvent.OldValue.clear();
+ aEvent.NewValue.clear();
+ FireAccessibleEvent(aEvent);
+ }
+ m_bLastHasSelection =bCurSelection;
+
+}
+
+void SwAccessibleParagraph::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( pWin )
+ {
+ sal_Int32 nPos;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ nPos = m_nOldCaretPos;
+ }
+ OSL_ENSURE( nPos != -1, "focus object should be selected" );
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && nPos != -1 );
+ }
+}
+
+SwAccessibleParagraph::SwAccessibleParagraph(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTextFrame& rTextFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::PARAGRAPH, &rTextFrame )
+ , m_nOldCaretPos( -1 )
+ , m_bIsBlockQuote(false)
+ , m_bIsHeading( false )
+ //Get the real heading level, Heading1 ~ Heading10
+ , m_nHeadingLevel (-1)
+ , m_aSelectionHelper( *this )
+ , mpParaChangeTrackInfo( new SwParaChangeTrackingInfo( rTextFrame ) ) // #i108125#
+ , m_bLastHasSelection(false) //To add TEXT_SELECTION_CHANGED event
+{
+ StartListening(const_cast<SwTextFrame&>(rTextFrame));
+ m_bIsBlockQuote = IsBlockQuote();
+ m_bIsHeading = IsHeading();
+ //Get the real heading level, Heading1 ~ Heading10
+ m_nHeadingLevel = GetRealHeadingLevel();
+ SetName( OUString() ); // set an empty accessibility name for paragraphs
+}
+
+SwAccessibleParagraph::~SwAccessibleParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ m_pPortionData.reset();
+ m_pHyperTextData.reset();
+ mpParaChangeTrackInfo.reset(); // #i108125#
+ EndListeningAll();
+}
+
+bool SwAccessibleParagraph::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_nOldCaretPos != -1;
+}
+
+void SwAccessibleParagraph::UpdatePortionData()
+{
+ // obtain the text frame
+ const SwTextFrame* pFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ OSL_ENSURE( pFrame != nullptr, "The text frame has vanished!" );
+ if (!pFrame)
+ ClearPortionData();
+ else
+ {
+ OSL_ENSURE( pFrame->IsTextFrame(), "The text frame has mutated!" );
+ // build new portion data
+ m_pPortionData.reset( new SwAccessiblePortionData(
+ pFrame, GetMap()->GetShell()->GetViewOptions()) );
+ pFrame->VisitPortions( *m_pPortionData );
+ }
+ OSL_ENSURE( m_pPortionData != nullptr, "UpdatePortionData() failed" );
+}
+
+void SwAccessibleParagraph::ClearPortionData()
+{
+ m_pPortionData.reset();
+ m_pHyperTextData.reset();
+}
+
+void SwAccessibleParagraph::ExecuteAtViewShell( sal_uInt16 nSlot )
+{
+ OSL_ENSURE( GetMap() != nullptr, "no map?" );
+ SwViewShell* pViewShell = GetMap()->GetShell();
+
+ OSL_ENSURE( pViewShell != nullptr, "View shell expected!" );
+ SfxViewShell* pSfxShell = pViewShell->GetSfxViewShell();
+
+ OSL_ENSURE( pSfxShell != nullptr, "SfxViewShell shell expected!" );
+ if( !pSfxShell )
+ return;
+
+ SfxViewFrame& rFrame = pSfxShell->GetViewFrame();
+ SfxDispatcher *pDispatcher = rFrame.GetDispatcher();
+ OSL_ENSURE( pDispatcher != nullptr, "Dispatcher expected!" );
+ if( !pDispatcher )
+ return;
+
+ pDispatcher->Execute( nSlot );
+}
+
+rtl::Reference<SwXTextPortion> SwAccessibleParagraph::CreateUnoPortion(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ OSL_ENSURE( (IsValidChar(nStartIndex, GetString().getLength()) &&
+ (nEndIndex == -1)) ||
+ IsValidRange(nStartIndex, nEndIndex, GetString().getLength()),
+ "please check parameters before calling this method" );
+
+ const TextFrameIndex nStart = GetPortionData().GetCoreViewPosition(nStartIndex);
+ const TextFrameIndex nEnd = (nEndIndex == -1)
+ ? (nStart + TextFrameIndex(1))
+ : GetPortionData().GetCoreViewPosition(nEndIndex);
+
+ // create UNO cursor
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nStart));
+ auto pUnoCursor(const_cast<SwDoc&>(pFrame->GetDoc()).CreateUnoCursor(aStartPos));
+ pUnoCursor->SetMark();
+ *pUnoCursor->GetMark() = pFrame->MapViewToModelPos(nEnd);
+
+ // create a (dummy) text portion to be returned
+ uno::Reference<text::XText> aEmpty;
+ return new SwXTextPortion ( pUnoCursor.get(), aEmpty, PORTION_TEXT);
+}
+
+// range checking for parameter
+
+bool SwAccessibleParagraph::IsValidChar(
+ sal_Int32 nPos, sal_Int32 nLength)
+{
+ return (nPos >= 0) && (nPos < nLength);
+}
+
+bool SwAccessibleParagraph::IsValidPosition(
+ sal_Int32 nPos, sal_Int32 nLength)
+{
+ return (nPos >= 0) && (nPos <= nLength);
+}
+
+bool SwAccessibleParagraph::IsValidRange(
+ sal_Int32 nBegin, sal_Int32 nEnd, sal_Int32 nLength)
+{
+ return IsValidPosition(nBegin, nLength) && IsValidPosition(nEnd, nLength);
+}
+
+//the function is to check whether the position is in a redline range.
+const SwRangeRedline* SwAccessibleParagraph::GetRedlineAtIndex()
+{
+ const SwRangeRedline* pRedline = nullptr;
+ SwPaM* pCrSr = GetCursor( true );
+ if ( pCrSr )
+ {
+ SwPosition* pStart = pCrSr->Start();
+ pRedline = pStart->GetDoc().getIDocumentRedlineAccess().GetRedline(*pStart, nullptr);
+ }
+
+ return pRedline;
+}
+
+// text boundaries
+
+bool SwAccessibleParagraph::GetCharBoundary(
+ i18n::Boundary& rBound,
+ std::u16string_view text,
+ sal_Int32 nPos )
+{
+ if( GetPortionData().FillBoundaryIFDateField( rBound, nPos) )
+ return true;
+
+ auto nPosEnd = nPos;
+ o3tl::iterateCodePoints(text, &nPosEnd);
+
+ rBound.startPos = nPos;
+ rBound.endPos = nPosEnd;
+
+ return true;
+}
+
+bool SwAccessibleParagraph::GetWordBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ // now ask the Break-Iterator for the word
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // get locale for this position
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const TextFrameIndex nCorePos = GetPortionData().GetCoreViewPosition(nPos);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(pFrame->GetLangOfChar(nCorePos, 0, true));
+
+ // which type of word are we interested in?
+ // (DICTIONARY_WORD includes punctuation, ANY_WORD doesn't.)
+ const sal_Int16 nWordType = i18n::WordType::ANY_WORD;
+
+ // get word boundary, as the Break-Iterator sees fit.
+ rBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nPos, aLocale, nWordType, true );
+
+ return true;
+}
+
+bool SwAccessibleParagraph::GetSentenceBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ const sal_Unicode* pStr = rText.getStr();
+ while( nPos < rText.getLength() && pStr[nPos] == u' ' )
+ nPos++;
+
+ GetPortionData().GetSentenceBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetLineBoundary(
+ i18n::Boundary& rBound,
+ std::u16string_view aText,
+ sal_Int32 nPos )
+{
+ if( sal_Int32(aText.size()) == nPos )
+ GetPortionData().GetLastLineBoundary( rBound );
+ else
+ GetPortionData().GetLineBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetParagraphBoundary(
+ i18n::Boundary& rBound,
+ std::u16string_view aText )
+{
+ rBound.startPos = 0;
+ rBound.endPos = aText.size();
+ return true;
+}
+
+bool SwAccessibleParagraph::GetAttributeBoundary(
+ i18n::Boundary& rBound,
+ sal_Int32 nPos )
+{
+ GetPortionData().GetAttributeBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetGlyphBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ // ask the Break-Iterator for the glyph by moving one cell
+ // forward, and then one cell back
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // get locale for this position
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const TextFrameIndex nCorePos = GetPortionData().GetCoreViewPosition(nPos);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(pFrame->GetLangOfChar(nCorePos, 0, true));
+
+ // get word boundary, as the Break-Iterator sees fit.
+ const sal_Int16 nIterMode = i18n::CharacterIteratorMode::SKIPCELL;
+ sal_Int32 nDone = 0;
+ rBound.endPos = g_pBreakIt->GetBreakIter()->nextCharacters(
+ rText, nPos, aLocale, nIterMode, 1, nDone );
+ rBound.startPos = g_pBreakIt->GetBreakIter()->previousCharacters(
+ rText, rBound.endPos, aLocale, nIterMode, 1, nDone );
+ bool bRet = ((rBound.startPos <= nPos) && (nPos <= rBound.endPos));
+ OSL_ENSURE( rBound.startPos <= nPos, "start pos too high" );
+ OSL_ENSURE( rBound.endPos >= nPos, "end pos too low" );
+
+ return bRet;
+}
+
+bool SwAccessibleParagraph::GetTextBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos,
+ sal_Int16 nTextType )
+{
+ // error checking
+ if( !( AccessibleTextType::LINE == nTextType
+ ? IsValidPosition( nPos, rText.getLength() )
+ : IsValidChar( nPos, rText.getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ bool bRet;
+
+ switch( nTextType )
+ {
+ case AccessibleTextType::WORD:
+ bRet = GetWordBoundary(rBound, rText, nPos);
+ break;
+
+ case AccessibleTextType::SENTENCE:
+ bRet = GetSentenceBoundary( rBound, rText, nPos );
+ break;
+
+ case AccessibleTextType::PARAGRAPH:
+ bRet = GetParagraphBoundary( rBound, rText );
+ break;
+
+ case AccessibleTextType::CHARACTER:
+ bRet = GetCharBoundary( rBound, rText, nPos );
+ break;
+
+ case AccessibleTextType::LINE:
+ //Solve the problem of returning wrong LINE and PARAGRAPH
+ if((nPos == rText.getLength()) && nPos > 0)
+ bRet = GetLineBoundary( rBound, rText, nPos - 1);
+ else
+ bRet = GetLineBoundary( rBound, rText, nPos );
+ break;
+
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ bRet = GetAttributeBoundary( rBound, nPos );
+ break;
+
+ case AccessibleTextType::GLYPH:
+ bRet = GetGlyphBoundary( rBound, rText, nPos );
+ break;
+
+ default:
+ throw lang::IllegalArgumentException( );
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleParagraph::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ std::scoped_lock aGuard2( m_Mutex );
+ if( m_sDesc.isEmpty() )
+ m_sDesc = GetDescription();
+
+ return m_sDesc;
+}
+
+lang::Locale SAL_CALL SwAccessibleParagraph::getLocale()
+{
+ SolarMutexGuard aGuard;
+
+ const SwTextFrame *pTextFrame = GetFrame()->DynCastTextFrame();
+ if( !pTextFrame )
+ {
+ throw uno::RuntimeException("no SwTextFrame", getXWeak());
+ }
+
+ lang::Locale aLoc(g_pBreakIt->GetLocale(pTextFrame->GetLangOfChar(TextFrameIndex(0), 0, true)));
+
+ return aLoc;
+}
+
+// #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleParagraph::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ rtl::Reference<utl::AccessibleRelationSetHelper> pHelper = new utl::AccessibleRelationSetHelper();
+
+ const SwTextFrame* pTextFrame = GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleParagraph::getAccessibleRelationSet()> - missing text frame");
+ if ( pTextFrame )
+ {
+ const SwContentFrame* pPrevContentFrame( pTextFrame->FindPrevCnt() );
+ if ( pPrevContentFrame )
+ {
+ uno::Sequence< uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pPrevContentFrame ) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_FROM,
+ aSequence );
+ pHelper->AddRelation( aAccRel );
+ }
+
+ const SwContentFrame* pNextContentFrame( pTextFrame->FindNextCnt( true ) );
+ if ( pNextContentFrame )
+ {
+ uno::Sequence< uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pNextContentFrame ) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_TO,
+ aSequence );
+ pHelper->AddRelation( aAccRel );
+ }
+ }
+
+ return pHelper;
+}
+
+void SAL_CALL SwAccessibleParagraph::grabFocus()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // get cursor shell
+ SwCursorShell *pCursorSh = GetCursorShell();
+ SwPaM *pCursor = GetCursor( false ); // #i27301# - consider new method signature
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+
+ if (pCursorSh != nullptr &&
+ ( pCursor == nullptr ||
+ !sw::FrameContainsNode(*pTextFrame, pCursor->GetPoint()->GetNodeIndex()) ||
+ !pTextFrame->IsInside(pTextFrame->MapModelToViewPos(*pCursor->GetPoint()))))
+ {
+ // create pam for selection
+ SwPosition const aStartPos(pTextFrame->MapViewToModelPos(pTextFrame->GetOffset()));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ Select( aPaM );
+
+ }
+
+ // ->#i13955#
+ vcl::Window * pWindow = GetWindow();
+
+ if (pWindow != nullptr)
+ pWindow->GrabFocus();
+ // <-#i13955#
+}
+
+// #i71385#
+static bool lcl_GetBackgroundColor( Color & rColor,
+ const SwFrame* pFrame,
+ SwCursorShell* pCursorSh )
+{
+ const SvxBrushItem* pBackgroundBrush = nullptr;
+ std::optional<Color> xSectionTOXColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( pFrame &&
+ pFrame->GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false ) )
+ {
+ if ( xSectionTOXColor )
+ {
+ rColor = *xSectionTOXColor;
+ return true;
+ }
+ else
+ {
+ rColor = pBackgroundBrush->GetColor();
+ return true;
+ }
+ }
+ else if ( pCursorSh )
+ {
+ rColor = pCursorSh->Imp()->GetRetoucheColor();
+ return true;
+ }
+
+ return false;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getForeground()
+{
+ SolarMutexGuard g;
+
+ Color aBackgroundCol;
+
+ if ( lcl_GetBackgroundColor( aBackgroundCol, GetFrame(), GetCursorShell() ) )
+ {
+ if ( aBackgroundCol.IsDark() )
+ {
+ return sal_Int32(COL_WHITE);
+ }
+ else
+ {
+ return sal_Int32(COL_BLACK);
+ }
+ }
+
+ return SwAccessibleContext::getForeground();
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getBackground()
+{
+ SolarMutexGuard g;
+
+ Color aBackgroundCol;
+
+ if ( lcl_GetBackgroundColor( aBackgroundCol, GetFrame(), GetCursorShell() ) )
+ {
+ return sal_Int32(aBackgroundCol);
+ }
+
+ return SwAccessibleContext::getBackground();
+}
+
+OUString SAL_CALL SwAccessibleParagraph::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::supportsService(
+ const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleParagraph::getSupportedServiceNames()
+{
+ return { sServiceName, sAccessibleServiceName };
+}
+
+static uno::Sequence< OUString > const & getAttributeNames()
+{
+ static uno::Sequence< OUString > const aNames
+ {
+ // Add the font name to attribute list
+ // sorted list of strings
+ UNO_NAME_CHAR_BACK_COLOR,
+ UNO_NAME_CHAR_COLOR,
+ UNO_NAME_CHAR_CONTOURED,
+ UNO_NAME_CHAR_EMPHASIS,
+ UNO_NAME_CHAR_ESCAPEMENT,
+ UNO_NAME_CHAR_FONT_NAME,
+ UNO_NAME_CHAR_HEIGHT,
+ UNO_NAME_CHAR_POSTURE,
+ UNO_NAME_CHAR_SHADOWED,
+ UNO_NAME_CHAR_STRIKEOUT,
+ UNO_NAME_CHAR_UNDERLINE,
+ UNO_NAME_CHAR_UNDERLINE_COLOR,
+ UNO_NAME_CHAR_WEIGHT,
+ };
+ return aNames;
+}
+
+static uno::Sequence< OUString > const & getSupplementalAttributeNames()
+{
+ static uno::Sequence< OUString > const aNames
+ {
+ // sorted list of strings
+ UNO_NAME_NUMBERING_LEVEL,
+ UNO_NAME_NUMBERING,
+ UNO_NAME_NUMBERING_RULES,
+ UNO_NAME_PARA_ADJUST,
+ UNO_NAME_PARA_BOTTOM_MARGIN,
+ UNO_NAME_PARA_FIRST_LINE_INDENT,
+ UNO_NAME_PARA_LEFT_MARGIN,
+ UNO_NAME_PARA_LINE_SPACING,
+ UNO_NAME_PARA_RIGHT_MARGIN,
+ UNO_NAME_TABSTOPS,
+ };
+ return aNames;
+}
+
+// XInterface
+
+uno::Any SwAccessibleParagraph::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleText>::get())
+ {
+ uno::Reference<XAccessibleText> aAccText = static_cast<XAccessibleText *>(*this); // resolve ambiguity
+ aRet <<= aAccText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleEditableText>::get())
+ {
+ uno::Reference<XAccessibleEditableText> aAccEditText = this;
+ aRet <<= aAccEditText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleSelection>::get())
+ {
+ uno::Reference<XAccessibleSelection> aAccSel = this;
+ aRet <<= aAccSel;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleHypertext>::get())
+ {
+ uno::Reference<XAccessibleHypertext> aAccHyp = this;
+ aRet <<= aAccHyp;
+ }
+ // #i63870#
+ // add interface com::sun:star:accessibility::XAccessibleTextAttributes
+ else if ( rType == cppu::UnoType<XAccessibleTextAttributes>::get())
+ {
+ uno::Reference<XAccessibleTextAttributes> aAccTextAttr = this;
+ aRet <<= aAccTextAttr;
+ }
+ // #i89175#
+ // add interface com::sun:star:accessibility::XAccessibleTextMarkup
+ else if ( rType == cppu::UnoType<XAccessibleTextMarkup>::get())
+ {
+ uno::Reference<XAccessibleTextMarkup> aAccTextMarkup = this;
+ aRet <<= aAccTextMarkup;
+ }
+ // add interface com::sun:star:accessibility::XAccessibleMultiLineText
+ else if ( rType == cppu::UnoType<XAccessibleMultiLineText>::get())
+ {
+ uno::Reference<XAccessibleMultiLineText> aAccMultiLineText = this;
+ aRet <<= aAccMultiLineText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleTextSelection>::get())
+ {
+ uno::Reference< css::accessibility::XAccessibleTextSelection > aTextExtension = this;
+ aRet <<= aTextExtension;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Reference<XAccessibleExtendedAttributes> xAttr = this;
+ aRet <<= xAttr;
+ }
+ else
+ {
+ aRet = SwAccessibleContext::queryInterface(rType);
+ }
+
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleParagraph::getTypes()
+{
+ // #i63870# - add type accessibility::XAccessibleTextAttributes
+ // #i89175# - add type accessibility::XAccessibleTextMarkup and
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleEditableText>::get(),
+ cppu::UnoType<XAccessibleTextAttributes>::get(),
+ ::cppu::UnoType<XAccessibleSelection>::get(),
+ cppu::UnoType<XAccessibleTextMarkup>::get(),
+ cppu::UnoType<XAccessibleMultiLineText>::get(),
+ cppu::UnoType<XAccessibleHypertext>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleParagraph::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleText
+
+sal_Int32 SwAccessibleParagraph::getCaretPosition()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nRet = GetCaretPos();
+ {
+ std::scoped_lock aOldCaretPosGuard( m_Mutex );
+ OSL_ENSURE( nRet == m_nOldCaretPos, "caret pos out of sync" );
+ m_nOldCaretPos = nRet;
+ }
+ if( -1 != nRet )
+ {
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ return nRet;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ bool bRet = false;
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nFrameIndex(GetPortionData().GetCoreViewPosition(nIndex));
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nFrameIndex));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ bRet = Select( aPaM );
+ }
+
+ return bRet;
+}
+
+sal_Unicode SwAccessibleParagraph::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ OUString sText( GetString() );
+
+ // return character (if valid)
+ if( !IsValidChar(nIndex, sText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ return sText[nIndex];
+}
+
+css::uno::Sequence< css::style::TabStop > SwAccessibleParagraph::GetCurrentTabStop( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /* #i12332# The position after the string needs special treatment.
+ IsValidChar -> IsValidPosition
+ */
+ if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ /* #i12332# */
+ bool bBehindText = false;
+ if ( nIndex == GetString().getLength() )
+ bBehindText = true;
+
+ // get model position & prepare GetCharRect() arguments
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bRealHeight = true;
+ aMoveState.m_bRealWidth = true;
+ SwSpecialPos aSpecialPos;
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+
+ /* #i12332# FillSpecialPos does not accept nIndex ==
+ GetString().getLength(). In that case nPos is set to the
+ length of the string in the core. This way GetCharRect
+ returns the rectangle for a cursor at the end of the
+ paragraph. */
+ const TextFrameIndex nPos = bBehindText
+ ? TextFrameIndex(pFrame->GetText().getLength())
+ : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
+
+ // call GetCharRect
+ SwRect aCoreRect;
+ SwPosition aPosition(pFrame->MapViewToModelPos(nPos));
+ GetFrame()->GetCharRect( aCoreRect, aPosition, &aMoveState );
+
+ // already get the caret position
+ css::uno::Sequence< css::style::TabStop > tabs;
+ const sal_Int32 nStrLen = pFrame->GetText().getLength();
+ if( nStrLen > 0 )
+ {
+ SwFrame* pTFrame = const_cast<SwFrame*>(GetFrame());
+ tabs = pTFrame->GetTabStopInfo(aCoreRect.Left());
+ }
+
+ if( tabs.hasElements() )
+ {
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ SwRect aTmpRect(0, 0, tabs[0].Position, 0);
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aTmpRect ));
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.X(), -aFramePixPos.Y() );
+
+ tabs.getArray()[0].Position = aScreenRect.GetWidth();
+ }
+
+ return tabs;
+}
+
+namespace {
+
+struct IndexCompare
+{
+ const PropertyValue* pValues;
+ explicit IndexCompare( const PropertyValue* pVals ) : pValues(pVals) {}
+ bool operator() ( sal_Int32 a, sal_Int32 b ) const
+ {
+ return (pValues[a].Name < pValues[b].Name);
+ }
+};
+
+}
+
+OUString SwAccessibleParagraph::GetFieldTypeNameAtIndex(sal_Int32 nIndex)
+{
+ OUString strTypeName;
+ SwFieldMgr aMgr;
+ SwTextField* pTextField = nullptr;
+ sal_Int32 nFieldIndex = GetPortionData().GetFieldIndex(nIndex);
+ if (nFieldIndex >= 0)
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ sw::MergedAttrIter iter(*pFrame);
+ while (SwTextAttr const*const pHt = iter.NextAttr())
+ {
+ if ((pHt->Which() == RES_TXTATR_FIELD
+ || pHt->Which() == RES_TXTATR_ANNOTATION
+ || pHt->Which() == RES_TXTATR_INPUTFIELD)
+ && (nFieldIndex-- == 0))
+ {
+ pTextField = const_cast<SwTextField*>(
+ static_txtattr_cast<SwTextField const*>(pHt));
+ break;
+ }
+ else if (pHt->Which() == RES_TXTATR_REFMARK
+ && (nFieldIndex-- == 0))
+ {
+ strTypeName = "set reference";
+ }
+ }
+ }
+ if (pTextField)
+ {
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ if (pField)
+ {
+ strTypeName = SwFieldType::GetTypeStr(pField->GetTypeId());
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ OUString sEntry;
+ sal_uInt32 subType = 0;
+ switch (nWhich)
+ {
+ case SwFieldIds::DocStat:
+ subType = static_cast<const SwDocStatField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::GetRef:
+ {
+ switch( pField->GetSubType() )
+ {
+ case REF_BOOKMARK:
+ {
+ const SwGetRefField* pRefField = dynamic_cast<const SwGetRefField*>(pField);
+ if ( pRefField && pRefField->IsRefToHeadingCrossRefBookmark() )
+ sEntry = "Headings";
+ else if ( pRefField && pRefField->IsRefToNumItemCrossRefBookmark() )
+ sEntry = "Numbered Paragraphs";
+ else
+ sEntry = "Bookmarks";
+ }
+ break;
+ case REF_FOOTNOTE:
+ sEntry = "Footnotes";
+ break;
+ case REF_ENDNOTE:
+ sEntry = "Endnotes";
+ break;
+ case REF_SETREFATTR:
+ sEntry = "Insert Reference";
+ break;
+ case REF_SEQUENCEFLD:
+ sEntry = static_cast<const SwGetRefField*>(pField)->GetSetRefName();
+ break;
+ case REF_STYLE:
+ sEntry = "StyleRef";
+ break;
+ }
+ //Get format string
+ strTypeName = sEntry;
+ // <pField->GetFormat() >= 0> is always true as <pField->GetFormat()> is unsigned
+// if (pField->GetFormat() >= 0)
+ {
+ sEntry = aMgr.GetFormatStr( pField->GetTypeId(), pField->GetFormat() );
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ break;
+ case SwFieldIds::DateTime:
+ subType = static_cast<const SwDateTimeField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::JumpEdit:
+ {
+ const sal_uInt32 nFormat= pField->GetFormat();
+ const sal_uInt16 nSize = aMgr.GetFormatCount(pField->GetTypeId(), false);
+ if (nFormat < nSize)
+ {
+ sEntry = aMgr.GetFormatStr(pField->GetTypeId(), nFormat);
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ break;
+ case SwFieldIds::ExtUser:
+ subType = static_cast<const SwExtUserField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::SetExp:
+ {
+ sEntry = pField->GetTyp()->GetName();
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ break;
+ case SwFieldIds::DocInfo:
+ subType = pField->GetSubType();
+ subType &= 0x00ff;
+ break;
+ case SwFieldIds::RefPageSet:
+ {
+ const SwRefPageSetField* pRPld = static_cast<const SwRefPageSetField*>(pField);
+ bool bOn = pRPld->IsOn();
+ strTypeName += "-";
+ if (bOn)
+ strTypeName += "on";
+ else
+ strTypeName += "off";
+ }
+ break;
+ case SwFieldIds::Author:
+ {
+ strTypeName += "-" + aMgr.GetFormatStr(pField->GetTypeId(), pField->GetFormat() & 0xff);
+ }
+ break;
+ default: break;
+ }
+ if (subType > 0 || nWhich == SwFieldIds::DocInfo || nWhich == SwFieldIds::ExtUser || nWhich == SwFieldIds::DocStat)
+ {
+ std::vector<OUString> aLst;
+ aMgr.GetSubTypes(pField->GetTypeId(), aLst);
+ if (subType < aLst.size())
+ sEntry = aLst[subType];
+ if (sEntry.getLength() > 0)
+ {
+ if (nWhich == SwFieldIds::DocInfo)
+ {
+ strTypeName = sEntry;
+ sal_uInt16 nSize = aMgr.GetFormatCount(pField->GetTypeId(), false);
+ const sal_uInt16 nExSub = pField->GetSubType() & 0xff00;
+ if (nSize > 0 && nExSub > 0)
+ {
+ //Get extra subtype string
+ strTypeName += "-";
+ sEntry = aMgr.GetFormatStr(pField->GetTypeId(), nExSub/0x0100-1);
+ strTypeName += sEntry;
+ }
+ }
+ else
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ }
+ }
+ return strTypeName;
+}
+
+// #i63870# - re-implement method on behalf of methods
+// <_getDefaultAttributesImpl(..)> and <_getRunAttributesImpl(..)>
+uno::Sequence<PropertyValue> SwAccessibleParagraph::getCharacterAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if (!IsValidPosition(nIndex, rText.getLength()))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bSupplementalMode = false;
+ uno::Sequence< OUString > aNames = aRequestedAttributes;
+ if (!aNames.hasElements())
+ {
+ bSupplementalMode = true;
+ aNames = getAttributeNames();
+ }
+ // retrieve default character attributes
+ tAccParaPropValMap aDefAttrSeq;
+ _getDefaultAttributesImpl( aNames, aDefAttrSeq, true );
+
+ // retrieved run character attributes
+ tAccParaPropValMap aRunAttrSeq;
+ _getRunAttributesImpl( nIndex, aNames, aRunAttrSeq );
+
+ // this allows to request one or more supplemental attributes, only
+ bSupplementalMode = bSupplementalMode || aDefAttrSeq.empty() || aRunAttrSeq.empty();
+
+ // merge default and run attributes
+ std::vector< PropertyValue > aValues( aDefAttrSeq.size() );
+ sal_Int32 i = 0;
+ for ( const auto& rDefEntry : aDefAttrSeq )
+ {
+ tAccParaPropValMap::const_iterator aRunIter =
+ aRunAttrSeq.find( rDefEntry.first );
+ if ( aRunIter != aRunAttrSeq.end() )
+ {
+ aValues[i] = aRunIter->second;
+ }
+ else
+ {
+ aValues[i] = rDefEntry.second;
+ }
+ ++i;
+ }
+ if( bSupplementalMode )
+ {
+ uno::Sequence< OUString > aSupplementalNames = aRequestedAttributes;
+ if (!aSupplementalNames.hasElements())
+ aSupplementalNames = getSupplementalAttributeNames();
+
+ tAccParaPropValMap aSupplementalAttrSeq;
+ _getSupplementalAttributesImpl( aSupplementalNames, aSupplementalAttrSeq );
+
+ aValues.resize( aValues.size() + aSupplementalAttrSeq.size() );
+
+ for ( const auto& rSupplementalEntry : aSupplementalAttrSeq )
+ {
+ aValues[i] = rSupplementalEntry.second;
+ ++i;
+ }
+
+ _correctValues( nIndex, aValues );
+
+ aValues.emplace_back();
+
+ OUString strTypeName = GetFieldTypeNameAtIndex(nIndex);
+ if (!strTypeName.isEmpty())
+ {
+ aValues.emplace_back();
+ PropertyValue& rValueFT = aValues.back();
+ rValueFT.Name = "FieldType";
+ rValueFT.Value <<= strTypeName.toAsciiLowerCase();
+ rValueFT.Handle = -1;
+ rValueFT.State = PropertyState_DIRECT_VALUE;
+ }
+
+ //sort property values
+ // build sorted index array
+ sal_Int32 nLength = aValues.size();
+ std::vector<sal_Int32> aIndices;
+ aIndices.reserve(nLength);
+ for (i = 0; i < nLength; ++i)
+ aIndices.push_back(i);
+ std::sort(aIndices.begin(), aIndices.end(), IndexCompare(aValues.data()));
+ // create sorted sequences according to index array
+ uno::Sequence<PropertyValue> aNewValues( nLength );
+ PropertyValue* pNewValues = aNewValues.getArray();
+ for (i = 0; i < nLength; ++i)
+ {
+ pNewValues[i] = aValues[aIndices[i]];
+ }
+ return aNewValues;
+ }
+
+ return comphelper::containerToSequence(aValues);
+}
+
+static void SetPutRecursive(SfxItemSet &targetSet, const SfxItemSet &sourceSet)
+{
+ const SfxItemSet *const pParentSet = sourceSet.GetParent();
+ if (pParentSet)
+ SetPutRecursive(targetSet, *pParentSet);
+ targetSet.Put(sourceSet);
+}
+
+// #i63870#
+void SwAccessibleParagraph::_getDefaultAttributesImpl(
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rDefAttrSeq,
+ const bool bOnlyCharAttrs )
+{
+ // retrieve default attributes
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps());
+ std::optional<SfxItemSet> pSet;
+ if ( !bOnlyCharAttrs )
+ {
+ pSet.emplace( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_PARATR_BEGIN, RES_PARATR_END - 1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END - 1> );
+ }
+ else
+ {
+ pSet.emplace( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1> );
+ }
+ // #i82637# - From the perspective of the a11y API the default character
+ // attributes are the character attributes, which are set at the paragraph style
+ // of the paragraph. The character attributes set at the automatic paragraph
+ // style of the paragraph are treated as run attributes.
+ // pTextNode->SwContentNode::GetAttr( *pSet );
+ // get default paragraph attributes, if needed, and merge these into <pSet>
+ if ( !bOnlyCharAttrs )
+ {
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END - 1>
+ aParaSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+ pTextNode->SwContentNode::GetAttr( aParaSet );
+ pSet->Put( aParaSet );
+ }
+ // get default character attributes and merge these into <pSet>
+ OSL_ENSURE( pTextNode->GetTextColl(),
+ "<SwAccessibleParagraph::_getDefaultAttributesImpl(..)> - missing paragraph style. Serious defect!" );
+ if ( pTextNode->GetTextColl() )
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>
+ aCharSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+ SetPutRecursive( aCharSet, pTextNode->GetTextColl()->GetAttrSet() );
+ pSet->Put( aCharSet );
+ }
+
+ // build-up sequence containing the run attributes <rDefAttrSeq>
+ tAccParaPropValMap aDefAttrSeq;
+ {
+ const SfxItemPropertyMap& rPropMap =
+ aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap();
+ for ( const auto pEntry : rPropMap.getPropertyEntries() )
+ {
+ const SfxPoolItem* pItem = pSet->GetItem( pEntry->nWID );
+ if ( pItem )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = pEntry->aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+
+ // #i72800#
+ // add property value entry for the paragraph style
+ if ( !bOnlyCharAttrs && pTextNode->GetTextColl() )
+ {
+ if ( aDefAttrSeq.find( UNO_NAME_PARA_STYLE_NAME ) == aDefAttrSeq.end() )
+ {
+ PropertyValue rPropVal;
+ rPropVal.Name = UNO_NAME_PARA_STYLE_NAME;
+ uno::Any aVal( uno::Any( pTextNode->GetTextColl()->GetName() ) );
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+
+ // #i73371#
+ // resolve value text::WritingMode2::PAGE of property value entry WritingMode
+ if ( !bOnlyCharAttrs && GetFrame() )
+ {
+ tAccParaPropValMap::iterator aIter = aDefAttrSeq.find( UNO_NAME_WRITING_MODE );
+ if ( aIter != aDefAttrSeq.end() )
+ {
+ PropertyValue rPropVal( aIter->second );
+ sal_Int16 nVal = rPropVal.Value.get<sal_Int16>();
+ if ( nVal == text::WritingMode2::PAGE )
+ {
+ const SwFrame* pUpperFrame( GetFrame()->GetUpper() );
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->GetType() &
+ ( SwFrameType::Page | SwFrameType::Fly | SwFrameType::Section | SwFrameType::Tab | SwFrameType::Cell ) )
+ {
+ if ( pUpperFrame->IsVertical() )
+ {
+ nVal = text::WritingMode2::TB_RL;
+ }
+ else if ( pUpperFrame->IsRightToLeft() )
+ {
+ nVal = text::WritingMode2::RL_TB;
+ }
+ else
+ {
+ nVal = text::WritingMode2::LR_TB;
+ }
+ rPropVal.Value <<= nVal;
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ break;
+ }
+
+ if ( pUpperFrame->IsFlyFrame() )
+ {
+ pUpperFrame = static_cast<const SwFlyFrame*>(pUpperFrame)->GetAnchorFrame();
+ }
+ else
+ {
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !aRequestedAttributes.hasElements() )
+ {
+ rDefAttrSeq = aDefAttrSeq;
+ }
+ else
+ {
+ for( const OUString& rReqAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::const_iterator const aIter = aDefAttrSeq.find( rReqAttr );
+ if ( aIter != aDefAttrSeq.end() )
+ {
+ rDefAttrSeq[ aIter->first ] = aIter->second;
+ }
+ }
+ }
+}
+
+uno::Sequence< PropertyValue > SwAccessibleParagraph::getDefaultAttributes(
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ tAccParaPropValMap aDefAttrSeq;
+ _getDefaultAttributesImpl( aRequestedAttributes, aDefAttrSeq );
+
+ // #i92233#
+ static constexpr OUString sMMToPixelRatio = u"MMToPixelRatio"_ustr;
+ bool bProvideMMToPixelRatio( !aRequestedAttributes.hasElements() ||
+ (comphelper::findValue(aRequestedAttributes, sMMToPixelRatio) != -1) );
+
+ uno::Sequence< PropertyValue > aValues( aDefAttrSeq.size() +
+ ( bProvideMMToPixelRatio ? 1 : 0 ) );
+ auto pValues = aValues.getArray();
+ std::transform(aDefAttrSeq.begin(), aDefAttrSeq.end(), pValues,
+ [](const auto& rEntry) -> PropertyValue { return rEntry.second; });
+
+ // #i92233#
+ if ( bProvideMMToPixelRatio )
+ {
+ PropertyValue rPropVal;
+ rPropVal.Name = sMMToPixelRatio;
+ const Size a100thMMSize( 1000, 1000 );
+ const Size aPixelSize = GetMap()->LogicToPixel( a100thMMSize );
+ const float fRatio = (static_cast<float>(a100thMMSize.Width())/100)/aPixelSize.Width();
+ rPropVal.Value <<= fRatio;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+ pValues[ aValues.getLength() - 1 ] = rPropVal;
+ }
+
+ return aValues;
+}
+
+void SwAccessibleParagraph::_getRunAttributesImpl(
+ const sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rRunAttrSeq )
+{
+ // create PaM for character at position <nIndex>
+ std::optional<SwPaM> pPaM;
+ const TextFrameIndex nCorePos(GetPortionData().GetCoreViewPosition(nIndex));
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPosition const aModelPos(pFrame->MapViewToModelPos(nCorePos));
+ SwTextNode *const pTextNode(aModelPos.GetNode().GetTextNode());
+ {
+ SwPosition const aEndPos(*pTextNode,
+ aModelPos.GetContentIndex() == pTextNode->Len()
+ ? pTextNode->Len() // ???
+ : aModelPos.GetContentIndex() + 1);
+ pPaM.emplace(aModelPos, aEndPos);
+ }
+
+ // retrieve character attributes for the created PaM <pPaM>
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aSet( pPaM->GetDoc().GetAttrPool() );
+ // #i82637#
+ // From the perspective of the a11y API the character attributes, which
+ // are set at the automatic paragraph style of the paragraph, are treated
+ // as run attributes.
+ // SwXTextCursor::GetCursorAttr( *pPaM, aSet, sal_True, sal_True );
+ // get character attributes from automatic paragraph style and merge these into <aSet>
+ {
+ if ( pTextNode->HasSwAttrSet() )
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aAutomaticParaStyleCharAttrs( pPaM->GetDoc().GetAttrPool());
+ aAutomaticParaStyleCharAttrs.Put( *(pTextNode->GetpSwAttrSet()), false );
+ aSet.Put( aAutomaticParaStyleCharAttrs );
+ }
+ }
+ // get character attributes at <pPaM> and merge these into <aSet>
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aCharAttrsAtPaM( pPaM->GetDoc().GetAttrPool() );
+ SwUnoCursorHelper::GetCursorAttr(*pPaM, aCharAttrsAtPaM, true);
+ aSet.Put( aCharAttrsAtPaM );
+ }
+
+ // build-up sequence containing the run attributes <rRunAttrSeq>
+ {
+ tAccParaPropValMap aRunAttrSeq;
+ {
+ tAccParaPropValMap aDefAttrSeq;
+ uno::Sequence< OUString > aDummy;
+ _getDefaultAttributesImpl( aDummy, aDefAttrSeq, true ); // #i82637#
+
+ const SfxItemPropertyMap& rPropMap =
+ aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap();
+ for ( const auto pEntry : rPropMap.getPropertyEntries() )
+ {
+ const SfxPoolItem* pItem( nullptr );
+ // #i82637# - Found character attributes, whose value equals the value of
+ // the corresponding default character attributes, are excluded.
+ if ( aSet.GetItemState( pEntry->nWID, true, &pItem ) == SfxItemState::SET )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = pEntry->aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = PropertyState_DIRECT_VALUE;
+
+ tAccParaPropValMap::const_iterator aDefIter =
+ aDefAttrSeq.find( rPropVal.Name );
+ if ( aDefIter == aDefAttrSeq.end() ||
+ rPropVal.Value != aDefIter->second.Value )
+ {
+ aRunAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+ }
+ }
+
+ if ( !aRequestedAttributes.hasElements() )
+ {
+ rRunAttrSeq = aRunAttrSeq;
+ }
+ else
+ {
+ for( const OUString& rReqAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::iterator aIter = aRunAttrSeq.find( rReqAttr );
+ if ( aIter != aRunAttrSeq.end() )
+ {
+ rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
+ }
+ }
+ }
+ }
+}
+
+uno::Sequence< PropertyValue > SwAccessibleParagraph::getRunAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ {
+ const OUString& rText = GetString();
+ if (!IsValidPosition(nIndex, rText.getLength()))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+ }
+
+ tAccParaPropValMap aRunAttrSeq;
+ _getRunAttributesImpl( nIndex, aRequestedAttributes, aRunAttrSeq );
+
+ return comphelper::mapValuesToSequence( aRunAttrSeq );
+}
+
+void SwAccessibleParagraph::_getSupplementalAttributesImpl(
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rSupplementalAttrSeq )
+{
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps());
+ SfxItemSetFixed<
+ RES_PARATR_LINESPACING, RES_PARATR_ADJUST,
+ RES_PARATR_TABSTOP, RES_PARATR_TABSTOP,
+ RES_PARATR_NUMRULE, RES_PARATR_NUMRULE,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1,
+ RES_MARGIN_FIRSTLINE, RES_MARGIN_RIGHT,
+ RES_UL_SPACE, RES_UL_SPACE>
+ aSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+
+ if ( pTextNode->HasBullet() || pTextNode->HasNumber() )
+ {
+ aSet.Put( pTextNode->GetAttr(RES_PARATR_LIST_LEVEL) );
+ aSet.Put( pTextNode->GetAttr(RES_PARATR_LIST_ISCOUNTED) );
+ }
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_UL_SPACE) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_MARGIN_FIRSTLINE) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_MARGIN_TEXTLEFT) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_MARGIN_RIGHT) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_PARATR_ADJUST) );
+
+ tAccParaPropValMap aSupplementalAttrSeq;
+ {
+ std::span<const SfxItemPropertyMapEntry> pPropMap(
+ aSwMapProvider.GetPropertyMapEntries( PROPERTY_MAP_ACCESSIBILITY_TEXT_ATTRIBUTE ) );
+ for (const auto & rEntry : pPropMap)
+ {
+ // For a paragraph, list level property is not set but when queried the returned default
+ // value is 0, exactly the same value of top level list item; that prevents using
+ // list level property for discerning simple paragraph from list item;
+ // the following check allows not to return the list level property at all
+ // when we are dealing with a simple paragraph
+ if ((rEntry.nWID == RES_PARATR_LIST_LEVEL || rEntry.nWID == RES_PARATR_LIST_ISCOUNTED) &&
+ !aSet.HasItem( rEntry.nWID ))
+ continue;
+
+ const SfxPoolItem* pItem = aSet.GetItem( rEntry.nWID );
+ if ( pItem )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, rEntry.nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = rEntry.aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aSupplementalAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+ }
+
+ for( const OUString& rSupplementalAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::const_iterator const aIter = aSupplementalAttrSeq.find( rSupplementalAttr );
+ if ( aIter != aSupplementalAttrSeq.end() )
+ {
+ rSupplementalAttrSeq[ aIter->first ] = aIter->second;
+ }
+ }
+}
+
+void SwAccessibleParagraph::_correctValues( const sal_Int32 nIndex,
+ std::vector< PropertyValue >& rValues)
+{
+ PropertyValue ChangeAttr, ChangeAttrColor;
+
+ const SwRangeRedline* pRedline = GetRedlineAtIndex();
+ if ( pRedline )
+ {
+
+ const SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig();
+ AuthorCharAttr aChangeAttr;
+ if ( pOpt )
+ {
+ switch( pRedline->GetType())
+ {
+ case RedlineType::Insert:
+ aChangeAttr = pOpt->GetInsertAuthorAttr();
+ break;
+ case RedlineType::Delete:
+ aChangeAttr = pOpt->GetDeletedAuthorAttr();
+ break;
+ case RedlineType::Format:
+ aChangeAttr = pOpt->GetFormatAuthorAttr();
+ break;
+ default: break;
+ }
+ }
+ switch( aChangeAttr.m_nItemId )
+ {
+ case SID_ATTR_CHAR_WEIGHT:
+ ChangeAttr.Name = UNO_NAME_CHAR_WEIGHT;
+ ChangeAttr.Value <<= awt::FontWeight::BOLD;
+ break;
+ case SID_ATTR_CHAR_POSTURE:
+ ChangeAttr.Name = UNO_NAME_CHAR_POSTURE;
+ ChangeAttr.Value <<= awt::FontSlant_ITALIC; //char posture
+ break;
+ case SID_ATTR_CHAR_STRIKEOUT:
+ ChangeAttr.Name = UNO_NAME_CHAR_STRIKEOUT;
+ ChangeAttr.Value <<= awt::FontStrikeout::SINGLE; //char strikeout
+ break;
+ case SID_ATTR_CHAR_UNDERLINE:
+ ChangeAttr.Name = UNO_NAME_CHAR_UNDERLINE;
+ ChangeAttr.Value <<= aChangeAttr.m_nAttr; //underline line
+ break;
+ }
+ if( aChangeAttr.m_nColor != COL_NONE_COLOR )
+ {
+ if( aChangeAttr.m_nItemId == SID_ATTR_BRUSH )
+ {
+ ChangeAttrColor.Name = UNO_NAME_CHAR_BACK_COLOR;
+ if( aChangeAttr.m_nColor == COL_TRANSPARENT )//char backcolor
+ ChangeAttrColor.Value <<= COL_BLUE;
+ else
+ ChangeAttrColor.Value <<= aChangeAttr.m_nColor;
+ }
+ else
+ {
+ ChangeAttrColor.Name = UNO_NAME_CHAR_COLOR;
+ if( aChangeAttr.m_nColor == COL_TRANSPARENT )//char color
+ ChangeAttrColor.Value <<= COL_BLUE;
+ else
+ ChangeAttrColor.Value <<= aChangeAttr.m_nColor;
+ }
+ }
+ }
+
+ // sw_redlinehide: this function only needs SwWrongList for 1 character,
+ // and the end is excluded by InWrongWord(),
+ // so it ought to work to just pick the wrong-list/node that contains
+ // the character following the given nIndex
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nCorePos(GetPortionData().GetCoreViewPosition(nIndex));
+ std::pair<SwTextNode*, sal_Int32> pos(pFrame->MapViewToModel(nCorePos));
+ if (pos.first->Len() == pos.second
+ && nCorePos != TextFrameIndex(pFrame->GetText().getLength()))
+ {
+ pos = pFrame->MapViewToModel(nCorePos + TextFrameIndex(1)); // try this one instead
+ assert(pos.first->Len() != pos.second);
+ }
+
+ sal_Int32 nValues = rValues.size();
+ for (sal_Int32 i = 0; i < nValues; ++i)
+ {
+ PropertyValue& rValue = rValues[i];
+
+ if (rValue.Name == ChangeAttr.Name )
+ {
+ rValue.Value = ChangeAttr.Value;
+ continue;
+ }
+
+ if (rValue.Name == ChangeAttrColor.Name )
+ {
+ rValue.Value = ChangeAttrColor.Value;
+ continue;
+ }
+
+ //back color
+ if (rValue.Name == UNO_NAME_CHAR_BACK_COLOR)
+ {
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crBack = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+ if (COL_AUTO == Color(ColorTransparency, crBack))
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ crBack = static_cast<sal_uInt32>(xComponent->getBackground());
+ }
+ rValue.Value <<= crBack;
+ }
+ continue;
+ }
+
+ //char color
+ if (rValue.Name == UNO_NAME_CHAR_COLOR)
+ {
+ if( GetPortionData().IsInGrayPortion( nIndex ) )
+ rValue.Value <<= GetCursorShell()->GetViewOptions()->GetFieldShadingsColor();
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crChar = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+
+ if( COL_AUTO == Color(ColorTransparency, crChar) )
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crChar = sal_uInt32(cr.IsDark() ? COL_WHITE : COL_BLACK);
+ rValue.Value <<= crChar;
+ }
+ }
+ continue;
+ }
+
+ // UnderLineColor
+ if (rValue.Name == UNO_NAME_CHAR_UNDERLINE_COLOR)
+ {
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crUnderline = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+ if ( COL_AUTO == Color(ColorTransparency, crUnderline) )
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crUnderline = sal_uInt32(cr.IsDark() ? COL_WHITE : COL_BLACK);
+ rValue.Value <<= crUnderline;
+ }
+ }
+
+ continue;
+ }
+
+ //tab stop
+ if (rValue.Name == UNO_NAME_TABSTOPS)
+ {
+ css::uno::Sequence< css::style::TabStop > tabs = GetCurrentTabStop( nIndex );
+ if( !tabs.hasElements() )
+ {
+ css::style::TabStop ts;
+ css::awt::Rectangle rc0 = getCharacterBounds(0);
+ css::awt::Rectangle rc1 = getCharacterBounds(nIndex);
+ if( rc1.X - rc0.X >= 48 )
+ ts.Position = (rc1.X - rc0.X) - (rc1.X - rc0.X - 48)% 47 + 47;
+ else
+ ts.Position = 48;
+ ts.DecimalChar = ' ';
+ ts.FillChar = ' ';
+ ts.Alignment = css::style::TabAlign_LEFT;
+ tabs = { ts };
+ }
+ rValue.Value <<= tabs;
+ continue;
+ }
+
+ //footnote & endnote
+ if (rValue.Name == UNO_NAME_CHAR_ESCAPEMENT)
+ {
+ if ( GetPortionData().IsIndexInFootnode(nIndex) )
+ {
+ rValue.Value <<= sal_Int32(101);
+ }
+ continue;
+ }
+ }
+}
+
+awt::Rectangle SwAccessibleParagraph::getCharacterBounds(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i12332# The position after the string needs special treatment.
+ // IsValidChar -> IsValidPosition
+ if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ // #i12332#
+ bool bBehindText = false;
+ if ( nIndex == GetString().getLength() )
+ bBehindText = true;
+
+ // get model position & prepare GetCharRect() arguments
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bRealHeight = true;
+ aMoveState.m_bRealWidth = true;
+ SwSpecialPos aSpecialPos;
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+
+ /** #i12332# FillSpecialPos does not accept nIndex ==
+ GetString().getLength(). In that case nPos is set to the
+ length of the string in the core. This way GetCharRect
+ returns the rectangle for a cursor at the end of the
+ paragraph. */
+ const TextFrameIndex nPos = bBehindText
+ ? TextFrameIndex(pFrame->GetText().getLength())
+ : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
+
+ // call GetCharRect
+ SwRect aCoreRect;
+ SwPosition aPosition(pFrame->MapViewToModelPos(nPos));
+ GetFrame()->GetCharRect( aCoreRect, aPosition, &aMoveState );
+
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCoreRect ));
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.getX(), -aFramePixPos.getY() );
+
+ // convert into AWT Rectangle
+ return awt::Rectangle(
+ aScreenRect.Left(), aScreenRect.Top(),
+ aScreenRect.GetWidth(), aScreenRect.GetHeight() );
+}
+
+sal_Int32 SwAccessibleParagraph::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetString().getLength();
+}
+
+sal_Int32 SwAccessibleParagraph::getIndexAtPoint( const awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // construct Point (translate into layout coordinates)
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+ Point aPoint( rPoint.X, rPoint.Y );
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
+ Point aPixPos( GetMap()->CoreToPixel( aLogBounds ).TopLeft() );
+ aPoint.setX(aPoint.getX() + aPixPos.getX());
+ aPoint.setY(aPoint.getY() + aPixPos.getY());
+ Point aCorePoint( GetMap()->PixelToCore( aPoint ) );
+ if( !aLogBounds.Contains( aCorePoint ) )
+ {
+ // #i12332# rPoint is may also be in rectangle returned by
+ // getCharacterBounds(getCharacterCount()
+
+ awt::Rectangle aRectEndPos =
+ getCharacterBounds(getCharacterCount());
+
+ if (rPoint.X - aRectEndPos.X >= 0 &&
+ rPoint.X - aRectEndPos.X < aRectEndPos.Width &&
+ rPoint.Y - aRectEndPos.Y >= 0 &&
+ rPoint.Y - aRectEndPos.Y < aRectEndPos.Height)
+ return getCharacterCount();
+
+ return -1;
+ }
+
+ // ask core for position
+ OSL_ENSURE( GetFrame() != nullptr, "The text frame has vanished!" );
+ OSL_ENSURE( GetFrame()->IsTextFrame(), "The text frame has mutated!" );
+ const SwTextFrame* pFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ // construct SwPosition (where GetModelPositionForViewPoint() will put the result into)
+ SwTextNode* pNode = const_cast<SwTextNode*>(pFrame->GetTextNodeFirst());
+ SwPosition aPos(*pNode, 0);
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bPosMatchesBounds = true;
+ const bool bSuccess = pFrame->GetModelPositionForViewPoint( &aPos, aCorePoint, &aMoveState );
+
+ TextFrameIndex nIndex = pFrame->MapModelToViewPos(aPos);
+ if (TextFrameIndex(0) < nIndex)
+ {
+ assert(bSuccess);
+ SwRect aResultRect;
+ pFrame->GetCharRect( aResultRect, aPos );
+ bool bVert = pFrame->IsVertical();
+ bool bR2L = pFrame->IsRightToLeft();
+
+ if ( (!bVert && aResultRect.Pos().getX() > aCorePoint.getX()) ||
+ ( bVert && aResultRect.Pos().getY() > aCorePoint.getY()) ||
+ ( bR2L && aResultRect.Right() < aCorePoint.getX()) )
+ {
+ SwPosition aPosPrev(pFrame->MapViewToModelPos(nIndex - TextFrameIndex(1)));
+ SwRect aResultRectPrev;
+ pFrame->GetCharRect( aResultRectPrev, aPosPrev );
+ if ( (!bVert && aResultRectPrev.Pos().getX() < aCorePoint.getX() && aResultRect.Pos().getY() == aResultRectPrev.Pos().getY()) ||
+ ( bVert && aResultRectPrev.Pos().getY() < aCorePoint.getY() && aResultRect.Pos().getX() == aResultRectPrev.Pos().getX()) ||
+ ( bR2L && aResultRectPrev.Right() > aCorePoint.getX() && aResultRect.Pos().getY() == aResultRectPrev.Pos().getY()) )
+ {
+ --nIndex;
+ }
+ }
+ }
+
+ return bSuccess
+ ? GetPortionData().GetAccessiblePosition(nIndex)
+ : -1;
+}
+
+OUString SwAccessibleParagraph::getSelectedText()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ bool bSelected = GetSelection( nStart, nEnd );
+ return bSelected
+ ? GetString().copy( nStart, nEnd - nStart )
+ : OUString();
+}
+
+sal_Int32 SwAccessibleParagraph::getSelectionStart()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ GetSelection( nStart, nEnd );
+ return nStart;
+}
+
+sal_Int32 SwAccessibleParagraph::getSelectionEnd()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ GetSelection( nStart, nEnd );
+ return nEnd;
+}
+
+sal_Bool SwAccessibleParagraph::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ bool bRet = false;
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nStart(GetPortionData().GetCoreViewPosition(nStartIndex));
+ TextFrameIndex const nEnd(GetPortionData().GetCoreViewPosition(nEndIndex));
+ SwPaM aPaM(pFrame->MapViewToModelPos(nStart));
+ aPaM.SetMark();
+ *aPaM.GetPoint() = pFrame->MapViewToModelPos(nEnd);
+
+ // set PaM at cursor shell
+ bRet = Select( aPaM );
+ }
+
+ return bRet;
+}
+
+OUString SwAccessibleParagraph::getText()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetString();
+}
+
+OUString SwAccessibleParagraph::getTextRange(
+ sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ OUString sText( GetString() );
+
+ if ( !IsValidRange( nStartIndex, nEndIndex, sText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ OrderRange( nStartIndex, nEndIndex );
+ return sText.copy(nStartIndex, nEndIndex-nStartIndex );
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ const OUString rText = GetString();
+ // implement the silly specification that first position after
+ // text must return an empty string, rather than throwing an
+ // IndexOutOfBoundsException, except for LINE, where the last
+ // line is returned
+ if( nIndex == rText.getLength() && AccessibleTextType::LINE != nTextType )
+ return aResult;
+
+ // with error checking
+ i18n::Boundary aBound;
+ bool bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ OSL_ENSURE( aBound.startPos >= 0, "illegal boundary" );
+ OSL_ENSURE( aBound.startPos <= aBound.endPos, "illegal boundary" );
+
+ // return word (if present)
+ if ( bWord )
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+
+ return aResult;
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString rText = GetString();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ //If nIndex = 0, then nobefore text so return -1 directly.
+ if( nIndex == 0 )
+ return aResult;
+ //Tab will be return when call WORDTYPE
+
+ // get starting pos
+ i18n::Boundary aBound;
+ if (nIndex == rText.getLength())
+ aBound.startPos = aBound.endPos = nIndex;
+ else
+ {
+ bool bTmp = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ if ( ! bTmp )
+ aBound.startPos = aBound.endPos = nIndex;
+ }
+
+ // now skip to previous word
+ if (nTextType == AccessibleTextType::WORD || nTextType == AccessibleTextType::SENTENCE)
+ {
+ i18n::Boundary preBound = aBound;
+ while(preBound.startPos==aBound.startPos && nIndex > 0)
+ {
+ nIndex = min(nIndex, preBound.startPos);
+ if (nIndex <= 0) break;
+ rText.iterateCodePoints(&nIndex, -1);
+ GetTextBoundary( preBound, rText, nIndex, nTextType );
+ }
+ //if (nIndex>0)
+ if (nIndex>=0)
+ //Tab will be return when call WORDTYPE
+ {
+ aResult.SegmentText = rText.copy( preBound.startPos, preBound.endPos - preBound.startPos );
+ aResult.SegmentStart = preBound.startPos;
+ aResult.SegmentEnd = preBound.endPos;
+ }
+ }
+ else
+ {
+ bool bWord = false;
+ while( !bWord )
+ {
+ nIndex = min(nIndex, aBound.startPos);
+ if (nIndex > 0)
+ {
+ rText.iterateCodePoints(&nIndex, -1);
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ }
+ else
+ break; // exit if beginning of string is reached
+ }
+
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+ }
+ return aResult;
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ const OUString rText = GetString();
+
+ // implement the silly specification that first position after
+ // text must return an empty string, rather than throwing an
+ // IndexOutOfBoundsException
+ if( nIndex == rText.getLength() )
+ return aResult;
+
+ // get first word, then skip to next word
+ i18n::Boundary aBound;
+ GetTextBoundary( aBound, rText, nIndex, nTextType );
+ bool bWord = false;
+ while( !bWord )
+ {
+ nIndex = max( sal_Int32(nIndex+1), aBound.endPos );
+ if( nIndex < rText.getLength() )
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ else
+ break; // exit if end of string is reached
+ }
+
+ if ( bWord )
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+
+/*
+ sal_Bool bWord = sal_False;
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ if (nTextType == AccessibleTextType::WORD)
+ {
+ Boundary nexBound=aBound;
+
+ // real current word
+ if( nIndex <= aBound.endPos && nIndex >= aBound.startPos )
+ {
+ while(nexBound.endPos==aBound.endPos&&nIndex<rText.getLength())
+ {
+ // nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) + 1;
+ nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) ;
+ const sal_Unicode* pStr = rText.getStr();
+ if (pStr)
+ {
+ if( pStr[nIndex] == sal_Unicode(' ') )
+ nIndex++;
+ }
+ if( nIndex < rText.getLength() )
+ {
+ bWord = GetTextBoundary( nexBound, rText, nIndex, nTextType );
+ }
+ }
+ }
+
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( nexBound.startPos, nexBound.endPos - nexBound.startPos );
+ aResult.SegmentStart = nexBound.startPos;
+ aResult.SegmentEnd = nexBound.endPos;
+ }
+
+ }
+ else
+ {
+ bWord = sal_False;
+ while( !bWord )
+ {
+ nIndex = max( (sal_Int32)(nIndex+1), aBound.endPos );
+ if( nIndex < rText.getLength() )
+ {
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ }
+ else
+ break; // exit if end of string is reached
+ }
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+ }
+*/
+ return aResult;
+}
+
+sal_Bool SwAccessibleParagraph::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // select and copy (through dispatch mechanism)
+ setSelection( nStartIndex, nEndIndex );
+ ExecuteAtViewShell( SID_COPY );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::scrollSubstringTo( sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex, AccessibleScrollType aScrollType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
+ throw lang::IndexOutOfBoundsException();
+
+ vcl::Window *pWin = GetWindow();
+ if ( ! pWin )
+ throw uno::RuntimeException("no Window", getXWeak());
+
+ /* Start and end character bounds, in pixels, relative to the paragraph */
+ awt::Rectangle startR, endR;
+ startR = getCharacterBounds(nStartIndex);
+ endR = getCharacterBounds(nEndIndex);
+
+ /* Adjust points to fit the bounding box of both bounds. */
+ Point sP(std::min(startR.X, endR.X), startR.Y);
+ Point eP(std::max(startR.X + startR.Width, endR.X + endR.Width), endR.Y + endR.Height);
+
+ /* Offset the values relative to the view shell frame */
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ sP += aFramePixPos;
+ eP += aFramePixPos;
+
+ Point startPoint(GetMap()->PixelToCore(sP));
+ Point endPoint(GetMap()->PixelToCore(eP));
+
+ switch (aScrollType)
+ {
+#ifdef notyet
+ case AccessibleScrollType_SCROLL_TOP_LEFT:
+ break;
+ case AccessibleScrollType_SCROLL_BOTTOM_RIGHT:
+ break;
+ case AccessibleScrollType_SCROLL_TOP_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_BOTTOM_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_LEFT_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_RIGHT_EDGE:
+ break;
+#endif
+ case AccessibleScrollType_SCROLL_ANYWHERE:
+ break;
+ default:
+ return false;
+ }
+
+ const SwRect aRect(startPoint, endPoint);
+ SwViewShell* pViewShell = GetMap()->GetShell();
+ OSL_ENSURE( pViewShell != nullptr, "View shell expected!" );
+
+ ScrollMDI(pViewShell, aRect, USHRT_MAX, USHRT_MAX);
+
+ return true;
+}
+
+// XAccessibleEditableText
+
+sal_Bool SwAccessibleParagraph::cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( !IsEditableState() )
+ return false;
+
+ // select and cut (through dispatch mechanism)
+ setSelection( nStartIndex, nEndIndex );
+ ExecuteAtViewShell( SID_CUT );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::pasteText( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( !IsEditableState() )
+ return false;
+
+ // select and paste (through dispatch mechanism)
+ setSelection( nIndex, nIndex );
+ ExecuteAtViewShell( SID_PASTE );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ return replaceText( nStartIndex, nEndIndex, OUString() );
+}
+
+sal_Bool SwAccessibleParagraph::insertText( const OUString& sText, sal_Int32 nIndex )
+{
+ return replaceText( nIndex, nIndex, sText );
+}
+
+sal_Bool SwAccessibleParagraph::replaceText(
+ sal_Int32 nStartIndex, sal_Int32 nEndIndex,
+ const OUString& sReplacement )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if( !IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ if( !IsEditableState() )
+ return false;
+
+ // translate positions
+ TextFrameIndex nStart;
+ TextFrameIndex nEnd;
+ bool bSuccess = GetPortionData().GetEditableRange(
+ nStartIndex, nEndIndex, nStart, nEnd );
+
+ // edit only if the range is editable
+ if( bSuccess )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ // create SwPosition for nStartIndex
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nStart));
+
+ // create SwPosition for nEndIndex
+ SwPosition aEndPos(pFrame->MapViewToModelPos(nEnd));
+
+ // now create XTextRange as helper and set string
+ const rtl::Reference<SwXTextRange> xRange(
+ SwXTextRange::CreateXTextRange(
+ const_cast<SwDoc&>(pFrame->GetDoc()), aStartPos, &aEndPos));
+ xRange->setString(sReplacement);
+
+ // delete portion data
+ ClearPortionData();
+ }
+
+ return bSuccess;
+
+}
+
+sal_Bool SwAccessibleParagraph::setAttributes(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex,
+ const uno::Sequence<PropertyValue>& rAttributeSet )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if( ! IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ if( !IsEditableState() )
+ return false;
+
+ // create a (dummy) text portion for the sole purpose of calling
+ // setPropertyValue on it
+ rtl::Reference<SwXTextPortion> xPortion = CreateUnoPortion( nStartIndex,
+ nEndIndex );
+
+ // build sorted index array
+ sal_Int32 nLength = rAttributeSet.getLength();
+ const PropertyValue* pPairs = rAttributeSet.getConstArray();
+ std::vector<sal_Int32> aIndices(nLength);
+ std::iota(aIndices.begin(), aIndices.end(), 0);
+ std::sort(aIndices.begin(), aIndices.end(), IndexCompare(pPairs));
+
+ // create sorted sequences according to index array
+ uno::Sequence< OUString > aNames( nLength );
+ OUString* pNames = aNames.getArray();
+ uno::Sequence< uno::Any > aValues( nLength );
+ uno::Any* pValues = aValues.getArray();
+ for (sal_Int32 i = 0; i < nLength; ++i)
+ {
+ const PropertyValue& rVal = pPairs[aIndices[i]];
+ pNames[i] = rVal.Name;
+ pValues[i] = rVal.Value;
+ }
+ aIndices.clear();
+
+ // now set the values
+ bool bRet = true;
+ try
+ {
+ xPortion->setPropertyValues( aNames, aValues );
+ }
+ catch (const UnknownPropertyException&)
+ {
+ // error handling through return code!
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+sal_Bool SwAccessibleParagraph::setText( const OUString& sText )
+{
+ return replaceText(0, GetString().getLength(), sText);
+}
+
+// XAccessibleSelection
+
+void SwAccessibleParagraph::selectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleParagraph::isAccessibleChildSelected(
+ sal_Int64 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleParagraph::clearAccessibleSelection( )
+{
+ ThrowIfDisposed();
+}
+
+void SwAccessibleParagraph::selectAllAccessibleChildren( )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int64 SwAccessibleParagraph::getSelectedAccessibleChildCount( )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleParagraph::getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+// index has to be treated as global child index.
+void SwAccessibleParagraph::deselectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.deselectAccessibleChild( nChildIndex );
+}
+
+// XAccessibleHypertext
+
+namespace {
+
+class SwHyperlinkIter_Impl
+{
+ SwTextFrame const& m_rFrame;
+ sw::MergedAttrIter m_Iter;
+ TextFrameIndex m_nStt;
+ TextFrameIndex m_nEnd;
+
+public:
+ explicit SwHyperlinkIter_Impl(const SwTextFrame & rTextFrame);
+ const SwTextAttr *next(SwTextNode const** ppNode = nullptr);
+
+ TextFrameIndex startIdx() const { return m_nStt; }
+ TextFrameIndex endIdx() const { return m_nEnd; }
+};
+
+}
+
+SwHyperlinkIter_Impl::SwHyperlinkIter_Impl(const SwTextFrame & rTextFrame)
+ : m_rFrame(rTextFrame)
+ , m_Iter(rTextFrame)
+ , m_nStt(rTextFrame.GetOffset())
+{
+ const SwTextFrame *const pFollFrame = rTextFrame.GetFollow();
+ m_nEnd = pFollFrame ? pFollFrame->GetOffset() : TextFrameIndex(rTextFrame.GetText().getLength());
+}
+
+const SwTextAttr *SwHyperlinkIter_Impl::next(SwTextNode const** ppNode)
+{
+ const SwTextAttr *pAttr = nullptr;
+ if (ppNode)
+ {
+ *ppNode = nullptr;
+ }
+
+ SwTextNode const* pNode(nullptr);
+ while (SwTextAttr const*const pHt = m_Iter.NextAttr(&pNode))
+ {
+ if (RES_TXTATR_INETFMT == pHt->Which())
+ {
+ const TextFrameIndex nHtStt(m_rFrame.MapModelToView(pNode, pHt->GetStart()));
+ const TextFrameIndex nHtEnd(m_rFrame.MapModelToView(pNode, pHt->GetAnyEnd()));
+ if (nHtEnd > nHtStt &&
+ ((nHtStt >= m_nStt && nHtStt < m_nEnd) ||
+ (nHtEnd > m_nStt && nHtEnd <= m_nEnd)))
+ {
+ pAttr = pHt;
+ if (ppNode)
+ {
+ *ppNode = pNode;
+ }
+ break;
+ }
+ }
+ }
+
+ return pAttr;
+};
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+ // #i77108# - provide hyperlinks also in editable documents.
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aIter(*pTextFrame);
+ while( aIter.next() )
+ nCount++;
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ SwAccessibleParagraph::getHyperLink( sal_Int32 nLinkIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ uno::Reference< XAccessibleHyperlink > xRet;
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aHIter(*pTextFrame);
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr* pHt = const_cast<SwTextAttr*>(aHIter.next(&pNode));
+ for (sal_Int32 nTIndex = 0; pHt && nTIndex <= nLinkIndex; ++nTIndex)
+ {
+ if( nTIndex == nLinkIndex )
+ { // found
+ if (!m_pHyperTextData)
+ m_pHyperTextData.reset( new SwAccessibleHyperTextData );
+ SwAccessibleHyperTextData::iterator aIter =
+ m_pHyperTextData ->find( pHt );
+ if (aIter != m_pHyperTextData->end())
+ {
+ xRet = (*aIter).second;
+ }
+ if (!xRet.is())
+ {
+ TextFrameIndex const nHintStart(pTextFrame->MapModelToView(pNode, pHt->GetStart()));
+ TextFrameIndex const nHintEnd(pTextFrame->MapModelToView(pNode, pHt->GetAnyEnd()));
+ const sal_Int32 nTmpHStt = GetPortionData().GetAccessiblePosition(
+ max(aHIter.startIdx(), nHintStart));
+ const sal_Int32 nTmpHEnd = GetPortionData().GetAccessiblePosition(
+ min(aHIter.endIdx(), nHintEnd));
+ xRet = new SwAccessibleHyperlink(*pHt,
+ *this, nTmpHStt, nTmpHEnd );
+ if (aIter != m_pHyperTextData->end())
+ {
+ (*aIter).second = xRet;
+ }
+ else
+ {
+ m_pHyperTextData->emplace( pHt, xRet );
+ }
+ }
+ break;
+ }
+
+ // iterate next hyperlink
+ pHt = const_cast<SwTextAttr*>(aHIter.next(&pNode));
+ }
+ if( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkIndex( sal_Int32 nCharIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nCharIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ sal_Int32 nRet = -1;
+ // #i77108#
+ {
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aHIter(*pTextFrame);
+
+ const TextFrameIndex nIdx = GetPortionData().GetCoreViewPosition(nCharIndex);
+ sal_Int32 nPos = 0;
+ SwTextNode const* pNode(nullptr);
+ const SwTextAttr *pHt = aHIter.next(&pNode);
+ while (pHt && (nIdx < pTextFrame->MapModelToView(pNode, pHt->GetStart())
+ || nIdx >= pTextFrame->MapModelToView(pNode, pHt->GetAnyEnd())))
+ {
+ pHt = aHIter.next(&pNode);
+ nPos++;
+ }
+
+ if( pHt )
+ nRet = nPos;
+ }
+
+ if (nRet == -1)
+ throw lang::IndexOutOfBoundsException();
+ return nRet;
+}
+
+// #i71360#, #i108125# - adjustments for change tracking text markup
+sal_Int32 SAL_CALL SwAccessibleParagraph::getTextMarkupCount( sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkupCount( nTextMarkupType );
+}
+
+//MSAA Extension Implementation in app module
+sal_Bool SAL_CALL SwAccessibleParagraph::scrollToPosition( const css::awt::Point&, sal_Bool )
+{
+ return false;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSelectedPortionCount( )
+{
+ SolarMutexGuard g;
+
+ sal_Int32 nSelected = 0;
+ SwPaM* pCursor = GetCursor( true );
+ if( pCursor != nullptr )
+ {
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->GetNodeIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->GetNodeIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ nSelected++;
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ }
+ }
+ return nSelected;
+
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionStart( sal_Int32 nSelectedPortionIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart=-1, nEnd=-1;
+ /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
+ return nStart;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart=-1, nEnd=-1;
+ /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
+ return nEnd;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::removeSelection( sal_Int32 selectionIndex )
+{
+ SolarMutexGuard g;
+
+ if(selectionIndex < 0) return false;
+
+ sal_Int32 nSelected = selectionIndex;
+
+ // get the selection, and test whether it affects our text node
+ SwPaM* pCursor = GetCursor( true );
+
+ if( pCursor != nullptr )
+ {
+ bool bRet = false;
+
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ SwPaM* pRingStart = pCursor;
+ do
+ {
+ // ignore, if no mark
+ if( pCursor->HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = pCursor->Start();
+ SwNodeOffset nStartIndex = pStart->GetNodeIndex();
+ SwPosition* pEnd = pCursor->End();
+ SwNodeOffset nEndIndex = pEnd->GetNodeIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if( nSelected == 0 )
+ {
+ pCursor->MoveTo(nullptr);
+ delete pCursor;
+ bRet = true;
+ }
+ else
+ {
+ nSelected--;
+ }
+ }
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ if(!bRet)
+ pCursor = pCursor->GetNext();
+ }
+ while( !bRet && (pCursor != pRingStart) );
+ }
+ return true;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::addSelection( sal_Int32, sal_Int32 startOffset, sal_Int32 endOffset)
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( startOffset, endOffset, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ sal_Int32 nSelectedCount = getSelectedPortionCount();
+ for ( sal_Int32 i = nSelectedCount ; i >= 0 ; i--)
+ {
+ sal_Int32 nStart, nEnd;
+ bool bSelected = GetSelectionAtIndex(&i, nStart, nEnd );
+ if(bSelected)
+ {
+ if(nStart <= nEnd )
+ {
+ if (( startOffset>=nStart && startOffset <=nEnd ) || //startOffset in a selection
+ ( endOffset>=nStart && endOffset <=nEnd ) || //endOffset in a selection
+ ( startOffset <= nStart && endOffset >=nEnd) || //start and end include the old selection
+ ( startOffset >= nStart && endOffset <=nEnd) )
+ {
+ removeSelection(i);
+ }
+
+ }
+ else
+ {
+ if (( startOffset>=nEnd && startOffset <=nStart ) || //startOffset in a selection
+ ( endOffset>=nEnd && endOffset <=nStart ) || //endOffset in a selection
+ ( startOffset <= nStart && endOffset >=nEnd) || //start and end include the old selection
+ ( startOffset >= nStart && endOffset <=nEnd) )
+
+ {
+ removeSelection(i);
+ }
+ }
+ }
+
+ }
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ pCursorShell->StartAction();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPaM* aPaM = pCursorShell->CreateCursor();
+ aPaM->SetMark();
+ *aPaM->GetPoint() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(startOffset));
+ *aPaM->GetMark() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(endOffset));
+ pCursorShell->EndAction();
+ }
+
+ return 0;
+}
+
+/*accessibility::*/TextSegment SAL_CALL
+ SwAccessibleParagraph::getTextMarkup( sal_Int32 nTextMarkupIndex,
+ sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkup( nTextMarkupIndex, nTextMarkupType );
+}
+
+uno::Sequence< /*accessibility::*/TextSegment > SAL_CALL
+ SwAccessibleParagraph::getTextMarkupAtIndex( sal_Int32 nCharIndex,
+ sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ const sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nCharIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkupAtIndex( nCharIndex, nTextMarkupType );
+}
+
+// #i89175#
+sal_Int32 SAL_CALL SwAccessibleParagraph::getLineNumberAtIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ const sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ const sal_Int32 nLineNo = GetPortionData().GetLineNo( nIndex );
+ return nLineNo;
+}
+
+/*accessibility::*/TextSegment SAL_CALL
+ SwAccessibleParagraph::getTextAtLineNumber( sal_Int32 nLineNo )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ if ( nLineNo < 0 ||
+ nLineNo >= GetPortionData().GetLineCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ i18n::Boundary aLineBound;
+ GetPortionData().GetBoundaryOfLine( nLineNo, aLineBound );
+
+ /*accessibility::*/TextSegment aTextAtLine;
+ const OUString rText = GetString();
+ aTextAtLine.SegmentText = rText.copy( aLineBound.startPos,
+ aLineBound.endPos - aLineBound.startPos );
+ aTextAtLine.SegmentStart = aLineBound.startPos;
+ aTextAtLine.SegmentEnd = aLineBound.endPos;
+
+ return aTextAtLine;
+}
+
+/*accessibility::*/TextSegment SAL_CALL SwAccessibleParagraph::getTextAtLineWithCaret()
+{
+ SolarMutexGuard g;
+
+ const sal_Int32 nLineNoOfCaret = getNumberOfLineWithCaret();
+
+ if ( nLineNoOfCaret >= 0 &&
+ nLineNoOfCaret < GetPortionData().GetLineCount() )
+ {
+ return getTextAtLineNumber( nLineNoOfCaret );
+ }
+
+ return /*accessibility::*/TextSegment();
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getNumberOfLineWithCaret()
+{
+ SolarMutexGuard g;
+
+ const sal_Int32 nCaretPos = getCaretPosition();
+ const sal_Int32 nLength = GetString().getLength();
+ if ( !IsValidPosition( nCaretPos, nLength ) )
+ {
+ return -1;
+ }
+
+ sal_Int32 nLineNo = GetPortionData().GetLineNo( nCaretPos );
+
+ // special handling for cursor positioned at end of text line via End key
+ if ( nCaretPos != 0 )
+ {
+ i18n::Boundary aLineBound;
+ GetPortionData().GetBoundaryOfLine( nLineNo, aLineBound );
+ if ( nCaretPos == aLineBound.startPos )
+ {
+ SwCursorShell* pCursorShell = SwAccessibleParagraph::GetCursorShell();
+ if ( pCursorShell != nullptr )
+ {
+ const awt::Rectangle aCharRect = getCharacterBounds( nCaretPos );
+
+ const SwRect& aCursorCoreRect = pCursorShell->GetCharRect();
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", getXWeak());
+ }
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCursorCoreRect ));
+
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.getX(), -aFramePixPos.getY() );
+
+ // convert into AWT Rectangle
+ const awt::Rectangle aCursorRect( aScreenRect.Left(),
+ aScreenRect.Top(),
+ aScreenRect.GetWidth(),
+ aScreenRect.GetHeight() );
+
+ if ( aCharRect.X != aCursorRect.X ||
+ aCharRect.Y != aCursorRect.Y )
+ {
+ --nLineNo;
+ }
+ }
+ }
+ }
+
+ return nLineNo;
+}
+
+// #i108125#
+void SwAccessibleParagraph::Notify(SfxBroadcaster&, const SfxHint&)
+{
+ mpParaChangeTrackInfo->reset();
+}
+
+bool SwAccessibleParagraph::GetSelectionAtIndex(
+ sal_Int32 * pSelection, sal_Int32& nStart, sal_Int32& nEnd)
+{
+ if (pSelection && *pSelection < 0) return false;
+
+ bool bRet = false;
+ nStart = -1;
+ nEnd = -1;
+
+ // get the selection, and test whether it affects our text node
+ SwPaM* pCursor = GetCursor( true );
+ if( pCursor != nullptr )
+ {
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->GetNodeIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->GetNodeIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if (!pSelection || *pSelection == 0)
+ {
+ // translate start and end positions
+
+ // start position
+ sal_Int32 nLocalStart = -1;
+ if (nStartIndex < nFirstNode)
+ {
+ // selection starts in previous node:
+ // then our local selection starts with the paragraph
+ nLocalStart = 0;
+ }
+ else
+ {
+ assert(FrameContainsNode(*pFrame, nStartIndex));
+
+ // selection starts in this node:
+ // then check whether it's before or inside our part of
+ // the paragraph, and if so, get the proper position
+ const TextFrameIndex nCoreStart =
+ pFrame->MapModelToViewPos(*pStart);
+ if( nCoreStart <
+ GetPortionData().GetFirstValidCorePosition() )
+ {
+ nLocalStart = 0;
+ }
+ else if( nCoreStart <=
+ GetPortionData().GetLastValidCorePosition() )
+ {
+ SAL_WARN_IF(
+ !GetPortionData().IsValidCorePosition(
+ nCoreStart),
+ "sw.a11y",
+ "problem determining valid core position");
+
+ nLocalStart =
+ GetPortionData().GetAccessiblePosition(
+ nCoreStart );
+ }
+ }
+
+ // end position
+ sal_Int32 nLocalEnd = -1;
+ if (nLastNode < nEndIndex)
+ {
+ // selection ends in following node:
+ // then our local selection extends to the end
+ nLocalEnd = GetPortionData().GetAccessibleString().
+ getLength();
+ }
+ else
+ {
+ assert(FrameContainsNode(*pFrame, nEndIndex));
+
+ // selection ends in this node: then select everything
+ // before our part of the node
+ const TextFrameIndex nCoreEnd =
+ pFrame->MapModelToViewPos(*pEnd);
+ if( nCoreEnd >
+ GetPortionData().GetLastValidCorePosition() )
+ {
+ // selection extends beyond out part of this para
+ nLocalEnd = GetPortionData().GetAccessibleString().
+ getLength();
+ }
+ else if( nCoreEnd >=
+ GetPortionData().GetFirstValidCorePosition() )
+ {
+ // selection is inside our part of this para
+ SAL_WARN_IF(
+ !GetPortionData().IsValidCorePosition(
+ nCoreEnd),
+ "sw.a11y",
+ "problem determining valid core position");
+
+ nLocalEnd = GetPortionData().GetAccessiblePosition(
+ nCoreEnd );
+ }
+ }
+
+ if( ( nLocalStart != -1 ) && ( nLocalEnd != -1 ) )
+ {
+ nStart = nLocalStart;
+ nEnd = nLocalEnd;
+ bRet = true;
+ }
+ } // if hit the index
+ else
+ {
+ --*pSelection;
+ }
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ if(bRet)
+ break;
+ }
+ }
+ // else: nocursor -> no selection
+
+ if (pSelection && bRet)
+ {
+ sal_Int32 nCaretPos = GetCaretPos();
+ if( nStart == nCaretPos )
+ std::swap( nStart, nEnd );
+ }
+ return bRet;
+}
+
+sal_Int16 SAL_CALL SwAccessibleParagraph::getAccessibleRole()
+{
+ SolarMutexGuard g;
+ //Get the real heading level, Heading1 ~ Heading10
+ if (m_nHeadingLevel > 0)
+ return AccessibleRole::HEADING;
+ if (m_bIsBlockQuote)
+ return AccessibleRole::BLOCK_QUOTE;
+ else
+ return AccessibleRole::PARAGRAPH;
+}
+
+//Get the real heading level, Heading1 ~ Heading10
+sal_Int32 SwAccessibleParagraph::GetRealHeadingLevel()
+{
+ uno::Reference< css::beans::XPropertySet > xPortion = CreateUnoPortion( 0, 0 );
+ uno::Any styleAny = xPortion->getPropertyValue( "ParaStyleName" );
+ OUString sValue;
+ if (styleAny >>= sValue)
+ {
+ sal_Int32 length = sValue.getLength();
+ if (length == 9 || length == 10)
+ {
+ if (sValue.startsWith("Heading"))
+ {
+ std::u16string_view intStr = sValue.subView(8);
+ sal_Int32 headingLevel = o3tl::toInt32(intStr);
+ return headingLevel;
+ }
+ }
+ }
+ return -1;
+}
+
+bool SwAccessibleParagraph::IsBlockQuote()
+{
+ uno::Reference<css::beans::XPropertySet> xPortion = CreateUnoPortion(0, 0);
+ uno::Any aStyleAny = xPortion->getPropertyValue("ParaStyleName");
+ OUString sValue;
+ if (aStyleAny >>= sValue)
+ return sValue == "Quotations";
+ return false;
+}
+
+uno::Any SAL_CALL SwAccessibleParagraph::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ OUString strHeading;
+ if (m_nHeadingLevel >= 0)
+ {
+ // report heading level using the "level" object attribute as specified in ARIA,
+ // maps to attributes of the same name for AT-SPI, IAccessible2, UIA
+ // https://www.w3.org/TR/core-aam-1.2/#ariaLevelHeading
+ strHeading = "level:" + OUString::number(m_nHeadingLevel) + ";";
+ }
+
+ return uno::Any(strHeading);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpara.hxx b/sw/source/core/access/accpara.hxx
new file mode 100644
index 0000000000..890604a50e
--- /dev/null
+++ b/sw/source/core/access/accpara.hxx
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPARA_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPARA_HXX
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
+#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
+#include "accselectionhelper.hxx"
+#include <unordered_map>
+#include <svl/lstner.hxx>
+
+class SwTextFrame;
+class SwPaM;
+class SwAccessiblePortionData;
+class SwAccessibleHyperTextData;
+class SwRangeRedline;
+class SwXTextPortion;
+class SwParaChangeTrackingInfo; //#i108125#
+
+namespace com::sun::star {
+ namespace i18n { struct Boundary; }
+ namespace accessibility { class XAccessibleHyperlink; }
+ namespace style { struct TabStop; }
+}
+
+typedef std::unordered_map< OUString,
+ css::beans::PropertyValue > tAccParaPropValMap;
+
+class SwAccessibleParagraph :
+ public SfxListener,
+ public SwAccessibleContext,
+ public css::accessibility::XAccessibleEditableText,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleHypertext,
+ public css::accessibility::XAccessibleTextMarkup,
+ public css::accessibility::XAccessibleMultiLineText,
+ public css::accessibility::XAccessibleTextAttributes,
+ public css::accessibility::XAccessibleTextSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ friend class SwAccessibleHyperlink;
+
+ OUString m_sDesc; // protected by base classes mutex
+
+ // data for this paragraph's text portions; this contains the
+ // mapping from the core 'model string' to the accessible text
+ // string.
+ // pPortionData may be NULL; it should only be accessed through the
+ // Get/Clear/Has/UpdatePortionData() methods
+ std::unique_ptr<SwAccessiblePortionData> m_pPortionData;
+ std::unique_ptr<SwAccessibleHyperTextData> m_pHyperTextData;
+
+ sal_Int32 m_nOldCaretPos; // The 'old' caret pos. It's only valid as long
+ // as the cursor is inside this object (protected by
+ // mutex)
+
+ bool m_bIsBlockQuote;
+ bool m_bIsHeading; // protected by base classes mutex
+ sal_Int32 m_nHeadingLevel;
+
+ // implementation for XAccessibleSelection
+ SwAccessibleSelectionHelper m_aSelectionHelper;
+
+ std::unique_ptr<SwParaChangeTrackingInfo> mpParaChangeTrackInfo; // #i108125#
+
+ // XAccessibleComponent
+ bool m_bLastHasSelection;
+
+ /// get the (accessible) text string (requires frame; check before)
+ OUString const & GetString();
+
+ static OUString GetDescription();
+
+ // get the current care position
+ sal_Int32 GetCaretPos();
+
+ // determine the current selection. Fill the values with
+ // -1 if there is no selection in the this paragraph
+ // @param pSelection (optional) check only Nth selection in ring
+ bool GetSelectionAtIndex(sal_Int32 * pSelection, sal_Int32& nStart, sal_Int32& nEnd);
+ bool GetSelection(sal_Int32& nStart, sal_Int32& nEnd) {
+ return GetSelectionAtIndex(nullptr, nStart, nEnd);
+ }
+
+ // helper for GetSelection and getCaretPosition
+ // #i27301# - add parameter <_bForSelection>, which indicates,
+ // if the cursor is retrieved for selection or for caret position.
+ SwPaM* GetCursor( const bool _bForSelection );
+
+ // for cut/copy/paste: execute a particular slot at the view shell
+ void ExecuteAtViewShell( sal_uInt16 nSlot );
+
+ // helper method for get/setAttributes
+ // (for the special case of (nEndIndex==-1) a single character will
+ // be selected)
+ rtl::Reference<SwXTextPortion> CreateUnoPortion( sal_Int32 nStart, sal_Int32 nEnd );
+
+ // methods for checking the parameter range:
+
+ // does nPos point to a char?
+ static bool IsValidChar(sal_Int32 nPos, sal_Int32 nLength);
+
+ // does nPos point to a position? (may be behind the last character)
+ static bool IsValidPosition(sal_Int32 nPos, sal_Int32 nLength);
+
+ // is nBegin...nEnd a valid range? (nEnd points past the last character)
+ static bool IsValidRange(sal_Int32 nBegin, sal_Int32 nEnd, sal_Int32 nLength);
+
+ // Ensure ordered range (i.e. nBegin is smaller then nEnd)
+ static void OrderRange(sal_Int32& nBegin, sal_Int32& nEnd)
+ {
+ if( nBegin > nEnd )
+ std::swap( nBegin, nEnd );
+ }
+
+ const SwRangeRedline* GetRedlineAtIndex();
+ OUString GetFieldTypeNameAtIndex(sal_Int32 nIndex);
+
+ // #i63870#
+ void _getDefaultAttributesImpl(
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rDefAttrSeq,
+ const bool bOnlyCharAttrs = false );
+ void _getRunAttributesImpl(
+ const sal_Int32 nIndex,
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rRunAttrSeq );
+
+ void _getSupplementalAttributesImpl(
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rSupplementalAttrSeq );
+
+ void _correctValues(
+ const sal_Int32 nIndex,
+ std::vector< css::beans::PropertyValue >& rValues );
+
+public:
+ bool IsHeading() const;
+
+protected:
+
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTILINE(1), MULTISELECTABLE(+),
+ // FOCUSABLE(+) and FOCUSED(+)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+
+ virtual void InvalidateContent_( bool bVisibleDataFired ) override;
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessibleParagraph() override;
+
+ // handling of data for the text portions
+
+ // force update of new portion data
+ /// @throws css::uno::RuntimeException
+ void UpdatePortionData();
+
+ // remove the current portion data
+ void ClearPortionData();
+
+ // get portion data; update if necessary
+ /// @throws css::uno::RuntimeException
+ SwAccessiblePortionData& GetPortionData()
+ {
+ if( m_pPortionData == nullptr )
+ UpdatePortionData();
+ return *m_pPortionData;
+ }
+
+ //helpers for word boundaries
+
+ bool GetCharBoundary( css::i18n::Boundary& rBound,
+ std::u16string_view text,
+ sal_Int32 nPos );
+ bool GetWordBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+ bool GetSentenceBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+ bool GetLineBoundary( css::i18n::Boundary& rBound,
+ std::u16string_view aText,
+ sal_Int32 nPos );
+ static bool GetParagraphBoundary( css::i18n::Boundary& rBound,
+ std::u16string_view aText );
+ bool GetAttributeBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos );
+ bool GetGlyphBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+
+ // get boundaries of word/sentence/etc. for specified text type
+ // Does all argument checking, and then delegates to helper methods above.
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool GetTextBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos,
+ sal_Int16 aTextType );
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+
+public:
+
+ SwAccessibleParagraph( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTextFrame& rTextFrame );
+
+ inline operator css::accessibility::XAccessibleText *();
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ css::uno::Sequence< css::style::TabStop > GetCurrentTabStop( sal_Int32 nIndex );
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+
+ // XAccessibleContext
+
+ // Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // Return the parents locale or throw exception if this object has no
+ // parent yet/anymore.
+ virtual css::lang::Locale SAL_CALL
+ getLocale() override;
+
+ // #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ // XAccessibleComponent
+
+ virtual void SAL_CALL grabFocus() override;
+ // #i71385#
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleEditableText).
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleText
+ virtual sal_Int32 SAL_CALL getCaretPosition() override;
+ virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart() override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd() override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XAccessibleEditableText
+ virtual sal_Bool SAL_CALL cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL pasteText( sal_Int32 nIndex ) override;
+ virtual sal_Bool SAL_CALL deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL insertText( const OUString& sText, sal_Int32 nIndex ) override;
+ virtual sal_Bool SAL_CALL replaceText( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const OUString& sReplacement ) override;
+ virtual sal_Bool SAL_CALL setAttributes( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const css::uno::Sequence< css::beans::PropertyValue >& aAttributeSet ) override;
+ virtual sal_Bool SAL_CALL setText( const OUString& sText ) override;
+
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int64 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ // XAccessibleHypertext
+ virtual sal_Int32 SAL_CALL getHyperLinkCount() override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleHyperlink >
+ SAL_CALL getHyperLink( sal_Int32 nLinkIndex ) override;
+ virtual sal_Int32 SAL_CALL getHyperLinkIndex( sal_Int32 nCharIndex ) override;
+
+ // #i71360#
+ // XAccessibleTextMarkup
+ virtual sal_Int32 SAL_CALL getTextMarkupCount( sal_Int32 nTextMarkupType ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextMarkup( sal_Int32 nTextMarkupIndex,
+ sal_Int32 nTextMarkupType ) override;
+
+ virtual css::uno::Sequence< css::accessibility::TextSegment > SAL_CALL
+ getTextMarkupAtIndex( sal_Int32 nCharIndex,
+ sal_Int32 nTextMarkupType ) override;
+
+ // XAccessibleTextSelection
+ virtual sal_Bool SAL_CALL scrollToPosition( const css::awt::Point& aPoint, sal_Bool isLeftTop ) override;
+ virtual sal_Int32 SAL_CALL getSelectedPortionCount( ) override;
+ virtual sal_Int32 SAL_CALL getSeletedPositionStart( sal_Int32 nSelectedPortionIndex ) override;
+ virtual sal_Int32 SAL_CALL getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex ) override;
+ virtual sal_Bool SAL_CALL removeSelection( sal_Int32 selectionIndex ) override;
+ virtual sal_Int32 SAL_CALL addSelection( sal_Int32 selectionIndex, sal_Int32 startOffset, sal_Int32 endOffset) override;
+ // XAccessibleExtendedAttributes
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override ;
+ sal_Int32 GetRealHeadingLevel();
+ bool IsBlockQuote();
+
+ // #i89175#
+ // XAccessibleMultiLineText
+ virtual sal_Int32 SAL_CALL getLineNumberAtIndex( sal_Int32 nIndex ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextAtLineNumber( sal_Int32 nLineNo ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextAtLineWithCaret() override;
+
+ virtual sal_Int32 SAL_CALL getNumberOfLineWithCaret() override;
+
+ // #i63870#
+ // XAccessibleTextAttributes
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getDefaultAttributes( const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRunAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+};
+
+inline SwAccessibleParagraph::operator css::accessibility::XAccessibleText *()
+{
+ return static_cast< css::accessibility::XAccessibleEditableText * >( this );
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accportions.cxx b/sw/source/core/access/accportions.cxx
new file mode 100644
index 0000000000..1cc1b73d6a
--- /dev/null
+++ b/sw/source/core/access/accportions.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "accportions.hxx"
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <txttypes.hxx>
+
+// for portion replacement in Special()
+#include <viewopt.hxx>
+
+// for GetWordBoundary(...), GetSentenceBoundary(...):
+#include <breakit.hxx>
+#include <txtfrm.hxx>
+
+// for FillSpecialPos(...)
+#include <crstate.hxx>
+
+using namespace ::com::sun::star;
+
+using i18n::Boundary;
+
+// 'portion type' for terminating portions
+#define POR_TERMINATE PortionType::NONE
+
+// portion attributes
+#define PORATTR_SPECIAL 1
+#define PORATTR_READONLY 2
+#define PORATTR_GRAY 4
+#define PORATTR_TERM 128
+
+/// returns the index of the first position whose value is smaller
+/// or equal, and whose following value is equal or larger
+template<typename T>
+static size_t FindBreak(const std::vector<T>& rPositions, T nValue);
+
+/// like FindBreak, but finds the last equal or larger position
+template<typename T>
+static size_t FindLastBreak(const std::vector<T>& rPositions, T nValue);
+
+
+SwAccessiblePortionData::SwAccessiblePortionData(
+ const SwTextFrame *const pTextFrame,
+ const SwViewOption* pViewOpt ) :
+ m_pTextFrame(pTextFrame),
+ m_nViewPosition( 0 ),
+ m_pViewOptions( pViewOpt ),
+ m_nBeforePortions( 0 ),
+ m_bFinished( false )
+{
+ OSL_ENSURE( m_pTextFrame != nullptr, "Need SwTextFrame!" );
+
+ // reserve some space to reduce memory allocations
+ m_aLineBreaks.reserve( 5 );
+ m_ViewPositions.reserve( 10 );
+ m_aAccessiblePositions.reserve( 10 );
+
+ // always include 'first' line-break position
+ m_aLineBreaks.push_back( 0 );
+}
+
+SwAccessiblePortionData::~SwAccessiblePortionData()
+{
+}
+
+void SwAccessiblePortionData::Text(TextFrameIndex const nLength,
+ PortionType nType)
+{
+ OSL_ENSURE((m_nViewPosition + nLength) <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "portion exceeds model string!" );
+
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // ignore zero-length portions
+ if (nLength == TextFrameIndex(0))
+ return;
+
+ // store 'old' positions
+ m_ViewPositions.push_back( m_nViewPosition );
+ m_aAccessiblePositions.push_back( m_aBuffer.getLength() );
+
+ // store portion attributes
+ sal_uInt8 nAttr = IsGrayPortionType(nType) ? PORATTR_GRAY : 0;
+ m_aPortionAttrs.push_back( nAttr );
+
+ // update buffer + nViewPosition
+ m_aBuffer.append(m_pTextFrame->GetText().subView(sal_Int32(m_nViewPosition), sal_Int32(nLength)));
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::Special(
+ TextFrameIndex const nLength, const OUString& rText, PortionType nType)
+{
+ OSL_ENSURE(m_nViewPosition >= TextFrameIndex(0), "illegal position");
+ OSL_ENSURE((m_nViewPosition + nLength) <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "portion exceeds model string!" );
+
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // construct string with representation; either directly from
+ // rText, or use resources for special case portions
+ OUString sDisplay;
+ switch( nType )
+ {
+ case PortionType::PostIts:
+ case PortionType::FlyCnt:
+ sDisplay = OUString(u'\xfffc');
+ break;
+ case PortionType::Field:
+ case PortionType::Hidden:
+ case PortionType::Combined:
+ case PortionType::IsoRef:
+ // When the field content is empty, input a special character.
+ if (rText.isEmpty())
+ sDisplay = OUString(u'\xfffc');
+ else
+ sDisplay = rText;
+ m_aFieldPosition.push_back(m_aBuffer.getLength());
+ m_aFieldPosition.push_back(m_aBuffer.getLength() + rText.getLength());
+ break;
+ case PortionType::FootnoteNum:
+ break;
+ case PortionType::Footnote:
+ {
+ sDisplay = rText;
+ sal_Int32 nStart=m_aBuffer.getLength();
+ sal_Int32 nEnd=nStart + rText.getLength();
+ m_vecPairPos.emplace_back(nStart,nEnd);
+ break;
+ }
+ break;
+ case PortionType::Number:
+ case PortionType::Bullet:
+ sDisplay = rText + " ";
+ break;
+ // There should probably be some special treatment to graphical bullets
+ case PortionType::GrfNum:
+ break;
+ // #i111768# - apply patch from kstribley:
+ // Include the control characters.
+ case PortionType::ControlChar:
+ sDisplay = rText + OUStringChar(m_pTextFrame->GetText()[sal_Int32(m_nViewPosition)]);
+ break;
+ case PortionType::Bookmark:
+ if ( m_pViewOptions->IsShowBookmarks() )
+ sDisplay = rText + " ";
+ break;
+ default:
+ sDisplay = rText;
+ break;
+ }
+
+ // ignore zero/zero portions (except for terminators)
+ if ((nLength == TextFrameIndex(0)) && (sDisplay.getLength() == 0) && (nType != POR_TERMINATE))
+ return;
+
+ // special treatment for zero length portion at the beginning:
+ // count as 'before' portion
+ if ((nLength == TextFrameIndex(0)) && (m_nViewPosition == TextFrameIndex(0)))
+ m_nBeforePortions++;
+
+ // store the 'old' positions
+ m_ViewPositions.push_back( m_nViewPosition );
+ m_aAccessiblePositions.push_back( m_aBuffer.getLength() );
+
+ // store portion attributes
+ sal_uInt8 nAttr = PORATTR_SPECIAL;
+ if( IsGrayPortionType(nType) ) nAttr |= PORATTR_GRAY;
+ if (nLength == TextFrameIndex(0)) nAttr |= PORATTR_READONLY;
+ if( nType == POR_TERMINATE ) nAttr |= PORATTR_TERM;
+ m_aPortionAttrs.push_back( nAttr );
+
+ // update buffer + nViewPosition
+ m_aBuffer.append( sDisplay );
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::LineBreak()
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ m_aLineBreaks.push_back( m_aBuffer.getLength() );
+}
+
+void SwAccessiblePortionData::Skip(TextFrameIndex const nLength)
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+ OSL_ENSURE( m_ViewPositions.empty(), "Never Skip() after portions" );
+ OSL_ENSURE(nLength <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "skip exceeds model string!" );
+
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::Finish()
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // include terminator values: always include two 'last character'
+ // markers in the position arrays to make sure we always find one
+ // position before the end
+ Special( TextFrameIndex(0), OUString(), POR_TERMINATE );
+ Special( TextFrameIndex(0), OUString(), POR_TERMINATE );
+ LineBreak();
+ LineBreak();
+
+ m_sAccessibleString = m_aBuffer.makeStringAndClear();
+ m_bFinished = true;
+}
+
+bool SwAccessiblePortionData::IsPortionAttrSet(
+ size_t nPortionNo, sal_uInt8 nAttr ) const
+{
+ OSL_ENSURE( nPortionNo < m_aPortionAttrs.size(),
+ "Illegal portion number" );
+ return (m_aPortionAttrs[nPortionNo] & nAttr) != 0;
+}
+
+bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const
+{
+ return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL);
+}
+
+bool SwAccessiblePortionData::IsGrayPortionType( PortionType nType ) const
+{
+ // gray portions?
+ // Compare with: inftxt.cxx, SwTextPaintInfo::DrawViewOpt(...)
+ bool bGray = false;
+ switch( nType )
+ {
+ case PortionType::Footnote:
+ case PortionType::IsoRef:
+ case PortionType::Ref:
+ case PortionType::QuoVadis:
+ case PortionType::Number:
+ case PortionType::Field:
+ case PortionType::InputField:
+ case PortionType::IsoTox:
+ case PortionType::Tox:
+ case PortionType::Hidden:
+ bGray = !m_pViewOptions->IsPagePreview() &&
+ !m_pViewOptions->IsReadonly() && m_pViewOptions->IsFieldShadings();
+ break;
+ case PortionType::Tab: bGray = m_pViewOptions->IsTab(); break;
+ case PortionType::SoftHyphen: bGray = m_pViewOptions->IsSoftHyph(); break;
+ case PortionType::Blank: bGray = m_pViewOptions->IsHardBlank(); break;
+ default:
+ break; // bGray is false
+ }
+ return bGray;
+}
+
+const OUString& SwAccessiblePortionData::GetAccessibleString() const
+{
+ OSL_ENSURE( m_bFinished, "Shouldn't call this before we are done!" );
+
+ return m_sAccessibleString;
+}
+
+void SwAccessiblePortionData::GetLineBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos ) const
+{
+ FillBoundary( rBound, m_aLineBreaks,
+ FindBreak( m_aLineBreaks, nPos ) );
+}
+
+// #i89175#
+sal_Int32 SwAccessiblePortionData::GetLineCount() const
+{
+ size_t nBreaks = m_aLineBreaks.size();
+ // A non-empty paragraph has at least 4 breaks: one for each line3 and
+ // 3 additional ones.
+ // An empty paragraph has 3 breaks.
+ // Less than 3 breaks is an error case.
+ sal_Int32 nLineCount = ( nBreaks > 3 )
+ ? nBreaks - 3
+ : ( ( nBreaks == 3 ) ? 1 : 0 );
+ return nLineCount;
+}
+
+sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const
+{
+ sal_Int32 nLineNo = FindBreak( m_aLineBreaks, nPos );
+
+ // handling of position after last character
+ const sal_Int32 nLineCount( GetLineCount() );
+ if ( nLineNo >= nLineCount )
+ {
+ nLineNo = nLineCount - 1;
+ }
+
+ return nLineNo;
+}
+
+void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo,
+ i18n::Boundary& rLineBound )
+{
+ FillBoundary( rLineBound, m_aLineBreaks, nLineNo );
+}
+
+void SwAccessiblePortionData::GetLastLineBoundary(
+ Boundary& rBound ) const
+{
+ OSL_ENSURE( m_aLineBreaks.size() >= 2, "need min + max value" );
+
+ // The last two positions except the two delimiters are the ones
+ // we are looking for, except for empty paragraphs (nBreaks==3)
+ size_t nBreaks = m_aLineBreaks.size();
+ FillBoundary( rBound, m_aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 );
+}
+
+TextFrameIndex SwAccessiblePortionData::GetCoreViewPosition(sal_Int32 const nPos) const
+{
+ OSL_ENSURE( nPos >= 0, "illegal position" );
+ OSL_ENSURE( nPos <= m_sAccessibleString.getLength(), "illegal position" );
+
+ // find the portion number
+ size_t nPortionNo = FindBreak( m_aAccessiblePositions, nPos );
+
+ // get core view portion size
+ TextFrameIndex nStartPos = m_ViewPositions[nPortionNo];
+
+ // if it's a non-special portion, move into the portion, else
+ // return the portion start
+ if( ! IsSpecialPortion( nPortionNo ) )
+ {
+ // text portions have to be of the same width
+ OSL_ENSURE( sal_Int32(m_ViewPositions[nPortionNo+1] - nStartPos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "accessibility portion disagrees with text model" );
+
+ nStartPos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+ }
+ // else: return nStartPos unmodified
+
+ OSL_ENSURE(nStartPos >= TextFrameIndex(0), "There's something weird in number of characters of SwTextFrame");
+ return nStartPos;
+}
+
+void SwAccessiblePortionData::FillBoundary(
+ Boundary& rBound,
+ const AccessiblePositions& rPositions,
+ size_t nPos )
+{
+ rBound.startPos = rPositions[nPos];
+ rBound.endPos = rPositions[nPos+1];
+}
+
+template<typename T>
+static size_t FindBreak(const std::vector<T>& rPositions, T const nValue)
+{
+ OSL_ENSURE( rPositions.size() >= 2, "need min + max value" );
+ OSL_ENSURE( rPositions[0] <= nValue, "need min value" );
+ OSL_ENSURE( rPositions[rPositions.size()-1] >= nValue,
+ "need first terminator value" );
+ OSL_ENSURE( rPositions[rPositions.size()-2] >= nValue,
+ "need second terminator value" );
+
+ size_t nMin = 0;
+ size_t nMax = rPositions.size()-2;
+
+ // loop until no more than two candidates are left
+ while( nMin+1 < nMax )
+ {
+ // check loop invariants
+ OSL_ENSURE( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) ||
+ ( (nMin != 0) && (rPositions[nMin] < nValue) ),
+ "minvalue not minimal" );
+ OSL_ENSURE( nValue <= rPositions[nMax], "max value not maximal" );
+
+ // get middle (and ensure progress)
+ size_t nMiddle = (nMin + nMax)/2;
+ OSL_ENSURE( nMin < nMiddle, "progress?" );
+ OSL_ENSURE( nMiddle < nMax, "progress?" );
+
+ // check array
+ OSL_ENSURE( rPositions[nMin] <= rPositions[nMiddle],
+ "garbled positions array" );
+ OSL_ENSURE( rPositions[nMiddle] <= rPositions[nMax],
+ "garbled positions array" );
+
+ if( nValue > rPositions[nMiddle] )
+ nMin = nMiddle;
+ else
+ nMax = nMiddle;
+ }
+
+ // only two are left; we only need to check which one is the winner
+ OSL_ENSURE( (nMax == nMin) || (nMax == nMin+1), "only two left" );
+ if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) )
+ nMin = nMin+1;
+
+ // finally, check to see whether the returned value is the 'right' position
+ OSL_ENSURE( rPositions[nMin] <= nValue, "not smaller or equal" );
+ OSL_ENSURE( nValue <= rPositions[nMin+1], "not equal or larger" );
+ OSL_ENSURE( (nMin == 0) || (rPositions[nMin-1] <= nValue),
+ "earlier value should have been returned" );
+
+ OSL_ENSURE( nMin < rPositions.size()-1,
+ "shouldn't return last position (due to terminator values)" );
+
+ return nMin;
+}
+
+template<typename T>
+static size_t FindLastBreak(const std::vector<T>& rPositions, T const nValue)
+{
+ size_t nResult = FindBreak( rPositions, nValue );
+
+ // skip 'zero-length' portions
+ // #i70538# consider size of <rPosition> and ignore last entry
+ while ( nResult < rPositions.size() - 2 &&
+ rPositions[nResult+1] <= nValue )
+ {
+ nResult++;
+ }
+
+ return nResult;
+}
+
+void SwAccessiblePortionData::GetSentenceBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos )
+{
+ OSL_ENSURE( nPos >= 0, "illegal position; check before" );
+ OSL_ENSURE( nPos < m_sAccessibleString.getLength(), "illegal position" );
+
+ if( m_pSentences == nullptr )
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ m_pSentences.reset(new AccessiblePositions);
+ m_pSentences->reserve(10);
+
+ // use xBreak->endOfSentence to iterate over all words; store
+ // positions in pSentences
+ sal_Int32 nCurrent = 0;
+ sal_Int32 nLength = m_sAccessibleString.getLength();
+ do
+ {
+ m_pSentences->push_back( nCurrent );
+
+ const TextFrameIndex nFramePos = GetCoreViewPosition(nCurrent);
+
+ sal_Int32 nNew = g_pBreakIt->GetBreakIter()->endOfSentence(
+ m_sAccessibleString, nCurrent,
+ g_pBreakIt->GetLocale(m_pTextFrame->GetLangOfChar(nFramePos, 0, true))) + 1;
+
+ if( (nNew < 0) && (nNew > nLength) )
+ nNew = nLength;
+ else if (nNew <= nCurrent)
+ nNew = nCurrent + 1; // ensure forward progress
+
+ nCurrent = nNew;
+ }
+ while (nCurrent < nLength);
+
+ // finish with two terminators
+ m_pSentences->push_back( nLength );
+ m_pSentences->push_back( nLength );
+ }
+
+ FillBoundary( rBound, *m_pSentences, FindBreak( *m_pSentences, nPos ) );
+}
+
+void SwAccessiblePortionData::GetAttributeBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos) const
+{
+ OSL_ENSURE( m_pTextFrame != nullptr, "Need SwTextNode!" );
+
+ // attribute boundaries can only occur on portion boundaries
+ FillBoundary( rBound, m_aAccessiblePositions,
+ FindBreak( m_aAccessiblePositions, nPos ) );
+}
+
+sal_Int32 SwAccessiblePortionData::GetAccessiblePosition(TextFrameIndex const nPos) const
+{
+ OSL_ENSURE(nPos <= TextFrameIndex(m_pTextFrame->GetText().getLength()), "illegal position");
+
+ // find the portion number
+ // #i70538# - consider "empty" model portions - e.g. number portion
+ size_t nPortionNo = FindLastBreak( m_ViewPositions, nPos );
+
+ sal_Int32 nRet = m_aAccessiblePositions[nPortionNo];
+
+ // if the view portion has more than one position, go into it;
+ // else return that position
+ TextFrameIndex nStartPos = m_ViewPositions[nPortionNo];
+ TextFrameIndex nEndPos = m_ViewPositions[nPortionNo+1];
+ if (!IsSpecialPortion(nPortionNo))
+ {
+ // text portions have to be of the same width
+ OSL_ENSURE( sal_Int32(nEndPos - nStartPos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "accessibility portion disagrees with text model" );
+
+ TextFrameIndex nWithinPortion = nPos - m_ViewPositions[nPortionNo];
+ nRet += sal_Int32(nWithinPortion);
+ }
+ // else: return nRet unmodified
+
+ OSL_ENSURE( (nRet >= 0) && (nRet <= m_sAccessibleString.getLength()),
+ "too long!" );
+ return nRet;
+}
+
+TextFrameIndex SwAccessiblePortionData::FillSpecialPos(
+ sal_Int32 nPos,
+ SwSpecialPos& rPos,
+ SwSpecialPos*& rpPos ) const
+{
+ size_t nPortionNo = FindLastBreak( m_aAccessiblePositions, nPos );
+
+ SwSPExtendRange nExtend(SwSPExtendRange::NONE);
+ sal_Int32 nRefPos(0);
+ TextFrameIndex nCorePos(0);
+
+ if( nPortionNo < m_nBeforePortions )
+ {
+ nExtend = SwSPExtendRange::BEFORE;
+ rpPos = &rPos;
+ }
+ else
+ {
+ TextFrameIndex nCoreEndPos = m_ViewPositions[nPortionNo+1];
+ nCorePos = m_ViewPositions[nPortionNo];
+
+ // skip backwards over zero-length portions, since GetCharRect()
+ // counts all model-zero-length portions as belonging to the
+ // previous portion
+ size_t nCorePortionNo = nPortionNo;
+ while (nCorePos == nCoreEndPos)
+ {
+ nCorePortionNo--;
+ nCoreEndPos = nCorePos;
+ nCorePos = m_ViewPositions[nCorePortionNo];
+
+ OSL_ENSURE( nCorePos >= TextFrameIndex(0), "Can't happen." );
+ OSL_ENSURE( nCorePortionNo >= m_nBeforePortions, "Can't happen." );
+ }
+ OSL_ENSURE( nCorePos != nCoreEndPos,
+ "portion with core-representation expected" );
+
+ // if we have anything except plain text, compute nExtend + nRefPos
+ if (IsSpecialPortion(nCorePortionNo))
+ {
+ // case 1: a non-text portion
+ // reference position is the first accessibility for our
+ // core portion
+ nRefPos = m_aAccessiblePositions[ nCorePortionNo ];
+ nExtend = SwSPExtendRange::NONE;
+ rpPos = &rPos;
+ }
+ else if(nPortionNo != nCorePortionNo)
+ {
+ // case 2: a multi-character (text!) portion, followed by
+ // zero-length portions
+ // reference position is the first character of the next
+ // portion, and we are 'behind'
+ nRefPos = m_aAccessiblePositions[ nCorePortionNo+1 ];
+ nExtend = SwSPExtendRange::BEHIND;
+ rpPos = &rPos;
+ }
+ else
+ {
+ // case 3: regular text portion
+ OSL_ENSURE( sal_Int32(nCoreEndPos - nCorePos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "text portion expected" );
+
+ nCorePos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+ rpPos = nullptr;
+ }
+ }
+ if( rpPos != nullptr )
+ {
+ OSL_ENSURE( rpPos == &rPos, "Yes!" );
+ OSL_ENSURE( nRefPos <= nPos, "wrong reference" );
+
+ // get the line number, and adjust nRefPos for the line
+ // (if necessary)
+ size_t nRefLine = FindBreak( m_aLineBreaks, nRefPos );
+ size_t nMyLine = FindBreak( m_aLineBreaks, nPos );
+ sal_Int32 nLineOffset = o3tl::narrowing<sal_Int32>( nMyLine - nRefLine );
+ if( nLineOffset != 0 )
+ nRefPos = m_aLineBreaks[ nMyLine ];
+
+ // fill char offset and 'special position'
+ rPos.nCharOfst = nPos - nRefPos;
+ rPos.nExtendRange = nExtend;
+ rPos.nLineOfst = nLineOffset;
+ }
+
+ return nCorePos;
+}
+
+bool SwAccessiblePortionData::FillBoundaryIFDateField( css::i18n::Boundary& rBound, const sal_Int32 nPos )
+{
+ if( m_aFieldPosition.size() < 2 )
+ return false;
+ for( size_t i = 0; i < m_aFieldPosition.size() - 1; i += 2 )
+ {
+ if( nPos < m_aFieldPosition[ i + 1 ] && nPos >= m_aFieldPosition[ i ] )
+ {
+ rBound.startPos = m_aFieldPosition[i];
+ rBound.endPos = m_aFieldPosition[i + 1];
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwAccessiblePortionData::AdjustAndCheck(
+ sal_Int32 nPos,
+ size_t& nPortionNo,
+ TextFrameIndex& rCorePos,
+ bool& bEdit) const
+{
+ // find portion and get mode position
+ nPortionNo = FindBreak( m_aAccessiblePositions, nPos );
+ rCorePos = m_ViewPositions[ nPortionNo ];
+
+ // for special portions, make sure we're on a portion boundary
+ // for text portions, add the in-portion offset
+ if( IsSpecialPortion( nPortionNo ) )
+ bEdit &= nPos == m_aAccessiblePositions[nPortionNo];
+ else
+ rCorePos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+}
+
+bool SwAccessiblePortionData::GetEditableRange(
+ sal_Int32 nStart, sal_Int32 nEnd,
+ TextFrameIndex& rCoreStart, TextFrameIndex& rCoreEnd) const
+{
+ bool bIsEditable = true;
+
+ // get start and end portions
+ size_t nStartPortion, nEndPortion;
+ AdjustAndCheck( nStart, nStartPortion, rCoreStart, bIsEditable );
+ AdjustAndCheck( nEnd, nEndPortion, rCoreEnd, bIsEditable );
+
+ // iterate over portions, and make sure there is no read-only portion
+ // in-between
+ size_t nLastPortion = nEndPortion;
+
+ // don't count last portion if we're in front of a special portion
+ if( IsSpecialPortion(nLastPortion) )
+ {
+ if (nLastPortion > 0)
+ nLastPortion--;
+ else
+ // special case: because size_t is usually unsigned, we can't just
+ // decrease nLastPortion to -1 (which would normally do the job, so
+ // this whole if wouldn't be needed). Instead, we'll do this
+ // special case and just increase the start portion beyond the last
+ // portion to make sure the loop below will have zero iteration.
+ nStartPortion = nLastPortion + 1;
+ }
+
+ for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor++ )
+ {
+ bIsEditable &= ! IsPortionAttrSet(nPor, PORATTR_READONLY);
+ }
+
+ return bIsEditable;
+}
+
+bool SwAccessiblePortionData::IsValidCorePosition(TextFrameIndex const nPos) const
+{
+ // a position is valid if it's within the core view positions that we know
+ return (m_ViewPositions[0] <= nPos) && (nPos <= m_ViewPositions.back());
+}
+
+bool SwAccessiblePortionData::IsZeroCorePositionData()
+{
+ if (m_ViewPositions.empty()) return true;
+ return m_ViewPositions[0] == TextFrameIndex(0)
+ && m_ViewPositions.back() == TextFrameIndex(0);
+}
+
+bool SwAccessiblePortionData::IsIndexInFootnode(sal_Int32 nIndex)
+{
+ for (const auto & pairPos : m_vecPairPos)
+ {
+ if(nIndex >= pairPos.first && nIndex < pairPos.second )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwAccessiblePortionData::IsInGrayPortion( sal_Int32 nPos )
+{
+// return IsGrayPortion( FindBreak( aAccessiblePositions, nPos ) );
+ return IsPortionAttrSet( FindBreak( m_aAccessiblePositions, nPos ),
+ PORATTR_GRAY );
+}
+
+sal_Int32 SwAccessiblePortionData::GetFieldIndex(sal_Int32 nPos) const
+{
+ sal_Int32 nIndex = -1;
+ if( m_aFieldPosition.size() >= 2 )
+ {
+ for( size_t i = 0; i < m_aFieldPosition.size() - 1; i += 2 )
+ {
+ if( nPos <= m_aFieldPosition[ i + 1 ] && nPos >= m_aFieldPosition[ i ] )
+ {
+ nIndex = i/2;
+ break;
+ }
+ }
+ }
+ return nIndex;
+}
+
+TextFrameIndex SwAccessiblePortionData::GetFirstValidCorePosition() const
+{
+ return m_ViewPositions[0];
+}
+
+TextFrameIndex SwAccessiblePortionData::GetLastValidCorePosition() const
+{
+ return m_ViewPositions.back();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accportions.hxx b/sw/source/core/access/accportions.hxx
new file mode 100644
index 0000000000..01725c0007
--- /dev/null
+++ b/sw/source/core/access/accportions.hxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPORTIONS_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPORTIONS_HXX
+
+#include <SwPortionHandler.hxx>
+#include <sal/types.h>
+#include <rtl/ustrbuf.hxx>
+#include <memory>
+#include <vector>
+
+class SwTextFrame;
+struct SwSpecialPos;
+class SwViewOption;
+namespace com::sun::star {
+ namespace i18n { struct Boundary; }
+}
+
+/**
+ * collect text portion data from the layout through SwPortionHandler interface
+ */
+class SwAccessiblePortionData : public SwPortionHandler
+{
+ // the frame this portion is referring to
+ SwTextFrame const* m_pTextFrame;
+
+ // variables used while collecting the data
+ OUStringBuffer m_aBuffer;
+ TextFrameIndex m_nViewPosition;
+ const SwViewOption* m_pViewOptions;
+
+ /// the accessible string
+ /// note that the content is different both from the string in the text
+ /// node(s) as well as the string in the text frame, so there are 3
+ /// different index spaces involved.
+ OUString m_sAccessibleString;
+
+ // positions array
+ // instances of Position_t must always include the minimum and
+ // maximum positions as first/last elements (to simplify the
+ // algorithms)
+ typedef std::vector<sal_Int32> AccessiblePositions;
+ typedef std::vector<TextFrameIndex> FramePositions;
+
+ AccessiblePositions m_aLineBreaks; /// position of line breaks
+ FramePositions m_ViewPositions; /// position of portion breaks in the core view
+ AccessiblePositions m_aAccessiblePositions; /// portion breaks in m_sAccessibleString
+ AccessiblePositions m_aFieldPosition;
+
+ std::vector<sal_uInt8> m_aPortionAttrs; /// additional portion attributes
+
+ std::unique_ptr<AccessiblePositions> m_pSentences; /// positions of sentence breaks
+
+ size_t m_nBeforePortions; /// # of portions before first core character
+ bool m_bFinished;
+
+ /// fill the boundary with the values from rPositions[nPos]
+ static void FillBoundary(css::i18n::Boundary& rBound,
+ const AccessiblePositions& rPositions,
+ size_t nPos );
+
+ /// Access to portion attributes
+ bool IsPortionAttrSet( size_t nPortionNo, sal_uInt8 nAttr ) const;
+ bool IsSpecialPortion( size_t nPortionNo ) const;
+ bool IsGrayPortionType( PortionType nType ) const;
+
+ // helper method for GetEditableRange(...):
+ void AdjustAndCheck( sal_Int32 nPos, size_t& nPortionNo,
+ TextFrameIndex& rCorePos, bool& bEdit) const;
+
+public:
+ SwAccessiblePortionData( const SwTextFrame* pTextFrame,
+ const SwViewOption* pViewOpt );
+ virtual ~SwAccessiblePortionData() override;
+
+ // SwPortionHandler methods
+ virtual void Text(TextFrameIndex nLength, PortionType nType) override;
+ virtual void Special(TextFrameIndex nLength, const OUString& rText, PortionType nType) override;
+ virtual void LineBreak() override;
+ virtual void Skip(TextFrameIndex nLength) override;
+ virtual void Finish() override;
+
+ bool FillBoundaryIFDateField( css::i18n::Boundary& rBound, const sal_Int32 nPos );
+ bool IsIndexInFootnode(sal_Int32 nIndex);
+ bool IsInGrayPortion( sal_Int32 nPos );
+ sal_Int32 GetFieldIndex(sal_Int32 nPos) const;
+
+ bool IsZeroCorePositionData();
+
+ // access to the portion data
+
+ /// get the text string, as presented by the layout
+ const OUString& GetAccessibleString() const;
+
+ /// get the start & end positions of the sentence
+ void GetLineBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos ) const;
+
+ // get start and end position of the last line
+ void GetLastLineBoundary( css::i18n::Boundary& rBound ) const;
+
+ /// Determine whether this core position is valid for these portions.
+ /// (A paragraph may be split into several frames, e.g. at page
+ /// boundaries. In this case, only part of a paragraph is represented
+ /// through this object. This method determines whether one particular
+ /// position is valid for this object or not.)
+ bool IsValidCorePosition(TextFrameIndex nPos) const;
+ TextFrameIndex GetFirstValidCorePosition() const;
+ TextFrameIndex GetLastValidCorePosition() const;
+
+ /// get the position in the accessibility string for a given view position
+ sal_Int32 GetAccessiblePosition(TextFrameIndex nPos) const;
+
+ // #i89175#
+ sal_Int32 GetLineCount() const;
+ sal_Int32 GetLineNo( const sal_Int32 nPos ) const;
+ void GetBoundaryOfLine( const sal_Int32 nLineNo,
+ css::i18n::Boundary& rLineBound );
+
+ /// get the position in the core view string for a given
+ /// (accessibility) position
+ TextFrameIndex GetCoreViewPosition(sal_Int32 nPos) const;
+
+ /// fill a SwSpecialPos structure, suitable for calling
+ /// SwTextFrame->GetCharRect
+ /// Returns the core position, and fills rpPos either with NULL or
+ /// with the &rPos, after putting the appropriate data into it.
+ TextFrameIndex FillSpecialPos(sal_Int32 nPos,
+ SwSpecialPos& rPos,
+ SwSpecialPos*& rpPos ) const;
+
+ // get boundaries of words/sentences. The data structures are
+ // created on-demand.
+ void GetSentenceBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos );
+
+ // get (a) boundary for attribute change
+ void GetAttributeBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos ) const;
+
+ /// Convert start and end positions into core positions.
+ /// @returns true if 'special' portions are included either completely
+ /// or not at all. This can be used to test whether editing
+ /// that range would be legal
+ bool GetEditableRange( sal_Int32 nStart, sal_Int32 nEnd,
+ TextFrameIndex& rCoreStart, TextFrameIndex& rCoreEnd) const;
+
+private:
+ std::vector< std::pair<sal_Int32,sal_Int32> > m_vecPairPos;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpreview.cxx b/sw/source/core/access/accpreview.cxx
new file mode 100644
index 0000000000..503cc543ec
--- /dev/null
+++ b/sw/source/core/access/accpreview.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <strings.hrc>
+#include "accpreview.hxx"
+
+constexpr OUStringLiteral sImplementationName
+ = u"com.sun.star.comp.Writer.SwAccessibleDocumentPageView";
+
+using ::com::sun::star::uno::Sequence;
+
+SwAccessiblePreview::SwAccessiblePreview(std::shared_ptr<SwAccessibleMap> const& pMap)
+ : SwAccessibleDocumentBase(pMap)
+{
+ SetName( GetResource( STR_ACCESS_PREVIEW_DOC_NAME ) );
+}
+
+SwAccessiblePreview::~SwAccessiblePreview()
+{
+}
+
+OUString SwAccessiblePreview::getImplementationName( )
+{
+ return sImplementationName;
+}
+
+sal_Bool SwAccessiblePreview::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence<OUString> SwAccessiblePreview::getSupportedServiceNames( )
+{
+ return {"com.sun.star.text.AccessibleTextDocumentPageView",
+ sAccessibleServiceName};
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessiblePreview::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString SAL_CALL SwAccessiblePreview::getAccessibleDescription()
+{
+ return GetResource( STR_ACCESS_PREVIEW_DOC_NAME );
+}
+
+OUString SAL_CALL SwAccessiblePreview::getAccessibleName()
+{
+ return SwAccessibleDocumentBase::getAccessibleName() + " " + GetResource( STR_ACCESS_PREVIEW_DOC_SUFFIX );
+}
+
+void SwAccessiblePreview::InvalidateFocus_()
+{
+ FireStateChangedEvent( css::accessibility::AccessibleStateType::FOCUSED, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpreview.hxx b/sw/source/core/access/accpreview.hxx
new file mode 100644
index 0000000000..a60254f6a1
--- /dev/null
+++ b/sw/source/core/access/accpreview.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPREVIEW_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPREVIEW_HXX
+
+#include "accdoc.hxx"
+
+/**
+ * accessibility implementation for the page preview.
+ * The children of the page preview are the pages that are visible in the
+ * preview.
+ *
+ * The vast majority of the implementation logic is inherited from
+ * SwAccessibleDocumentBase.
+ */
+class SwAccessiblePreview : public SwAccessibleDocumentBase
+{
+ virtual ~SwAccessiblePreview() override;
+
+public:
+ SwAccessiblePreview(std::shared_ptr<SwAccessibleMap> const& pMap);
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ OUString SAL_CALL getAccessibleDescription() override;
+ OUString SAL_CALL getAccessibleName() override;
+ virtual void InvalidateFocus_() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accselectionhelper.cxx b/sw/source/core/access/accselectionhelper.cxx
new file mode 100644
index 0000000000..fb56e71e35
--- /dev/null
+++ b/sw/source/core/access/accselectionhelper.cxx
@@ -0,0 +1,342 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include "accselectionhelper.hxx"
+
+#include "acccontext.hxx"
+#include <accmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <viewsh.hxx>
+#include <fesh.hxx>
+#include <vcl/svapp.hxx>
+#include <flyfrm.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <fmtanchr.hxx>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+using ::com::sun::star::accessibility::XAccessible;
+using ::com::sun::star::accessibility::XAccessibleContext;
+using ::com::sun::star::accessibility::XAccessibleSelection;
+
+using namespace ::sw::access;
+
+SwAccessibleSelectionHelper::SwAccessibleSelectionHelper(
+ SwAccessibleContext& rContext ) :
+ m_rContext( rContext )
+{
+}
+
+SwFEShell* SwAccessibleSelectionHelper::GetFEShell()
+{
+ OSL_ENSURE( m_rContext.GetMap() != nullptr, "no map?" );
+ SwViewShell* pViewShell = m_rContext.GetMap()->GetShell();
+ OSL_ENSURE( pViewShell != nullptr,
+ "No view shell? Then what are you looking at?" );
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>( pViewShell );
+
+ return pFEShell;
+}
+
+void SwAccessibleSelectionHelper::throwIndexOutOfBoundsException()
+{
+ Reference < XAccessibleContext > xThis( &m_rContext );
+ Reference < XAccessibleSelection >xSelThis( xThis, UNO_QUERY );
+ lang::IndexOutOfBoundsException aExcept(
+ "index out of bounds",
+ xSelThis );
+ throw aExcept;
+}
+
+// XAccessibleSelection
+void SwAccessibleSelectionHelper::selectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if (nChildIndex < 0 || nChildIndex >= m_rContext.GetChildCount(*(m_rContext.GetMap())))
+ throwIndexOutOfBoundsException();
+
+ // Get the respective child as SwFrame (also do index checking), ...
+ const SwAccessibleChild aChild = m_rContext.GetChild( *(m_rContext.GetMap()),
+ nChildIndex );
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ // we can only select fly frames, so we ignore (should: return
+ // false) all other attempts at child selection
+ if (GetFEShell())
+ {
+ const SdrObject *pObj = aChild.GetDrawObject();
+ if( pObj )
+ m_rContext.Select( const_cast< SdrObject *>( pObj ), nullptr==aChild.GetSwFrame());
+ }
+ // no frame shell, or no frame, or no fly frame -> can't select
+}
+
+//When the selected state of the SwFrameOrObj is set, return true.
+static bool lcl_getSelectedState(const SwAccessibleChild& aChild,
+ SwAccessibleContext* pContext,
+ SwAccessibleMap* pMap)
+{
+ Reference< XAccessible > xAcc;
+ if ( aChild.GetSwFrame() )
+ {
+ xAcc = pMap->GetContext( aChild.GetSwFrame(), false );
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ xAcc = pMap->GetContext( aChild.GetDrawObject(), pContext, false );
+ }
+
+ if( xAcc.is() )
+ {
+ Reference< XAccessibleContext > pRContext = xAcc->getAccessibleContext();
+ if(!pRContext.is())
+ return false;
+ sal_Int64 nRStateSet = pRContext->getAccessibleStateSet();
+ if(nRStateSet & AccessibleStateType::SELECTED)
+ return true;
+ }
+ return false;
+}
+
+bool SwAccessibleSelectionHelper::isAccessibleChildSelected(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if (nChildIndex < 0 || nChildIndex >= m_rContext.GetChildCount(*(m_rContext.GetMap())))
+ throwIndexOutOfBoundsException();
+
+ // Get the respective child as SwFrame (also do index checking), ...
+ const SwAccessibleChild aChild = m_rContext.GetChild( *(m_rContext.GetMap()),
+ nChildIndex );
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ // ... and compare to the currently selected frame
+ bool bRet = false;
+ if (const SwFEShell* pFEShell = GetFEShell())
+ {
+ if ( aChild.GetSwFrame() != nullptr )
+ {
+ bRet = (pFEShell->GetSelectedFlyFrame() == aChild.GetSwFrame());
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ bRet = pFEShell->IsObjSelected( *aChild.GetDrawObject() );
+ }
+ //If the SwFrameOrObj is not selected directly in the UI, we should check whether it is selected in the selection cursor.
+ if( !bRet )
+ {
+ if( lcl_getSelectedState( aChild, &m_rContext, m_rContext.GetMap() ) )
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleSelectionHelper::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+
+ // We can select only one. So iterate over the children to find
+ // the first we can select, and select it.
+
+ SwFEShell* pFEShell = GetFEShell();
+ if (!pFEShell)
+ return;
+
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ const SdrObject* pObj = rChild.GetDrawObject();
+ const SwFrame* pFrame = rChild.GetSwFrame();
+ if( pObj && !(pFrame != nullptr && pFEShell->IsObjSelected()) )
+ {
+ m_rContext.Select( const_cast< SdrObject *>( pObj ), nullptr==pFrame );
+ if( pFrame )
+ break;
+ }
+ }
+}
+
+sal_Int64 SwAccessibleSelectionHelper::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int64 nCount = 0;
+ // Only one frame can be selected at a time, and we only frames
+ // for selectable children.
+ if (const SwFEShell* pFEShell = GetFEShell())
+ {
+ const SwFlyFrame* pFlyFrame = pFEShell->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ nCount = 1;
+ }
+ else
+ {
+ const size_t nSelObjs = pFEShell->IsObjSelected();
+ if( nSelObjs > 0 )
+ {
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
+ SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview())
+ == m_rContext.GetFrame() &&
+ pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+ {
+ nCount++;
+ }
+ if (o3tl::make_unsigned(nCount) >= nSelObjs)
+ break;
+ }
+ }
+ }
+ //If the SwFrameOrObj is not selected directly in the UI,
+ //we should check whether it is selected in the selection cursor.
+ if( nCount == 0 )
+ {
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+ nCount = static_cast<sal_Int32>(std::count_if(aChildren.begin(), aChildren.end(),
+ [this](const SwAccessibleChild& aChild) { return lcl_getSelectedState(aChild, &m_rContext, m_rContext.GetMap()); }));
+ }
+ }
+ return nCount;
+}
+
+Reference<XAccessible> SwAccessibleSelectionHelper::getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ // Since the index is relative to the selected children, and since
+ // there can be at most one selected frame child, the index must
+ // be 0, and a selection must exist, otherwise we have to throw an
+ // lang::IndexOutOfBoundsException
+ SwFEShell* pFEShell = GetFEShell();
+ if (!pFEShell)
+ throwIndexOutOfBoundsException();
+
+ SwAccessibleChild aChild;
+ const SwFlyFrame *pFlyFrame = pFEShell->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ if( 0 == nSelectedChildIndex )
+ {
+ if(SwAccessibleFrame::GetParent( SwAccessibleChild(pFlyFrame), m_rContext.IsInPagePreview()) == m_rContext.GetFrame() )
+ {
+ aChild = pFlyFrame;
+ }
+ else
+ {
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ const SwFrame *pParaFrame = SwAccessibleFrame::GetParent( SwAccessibleChild(pFlyFrame), m_rContext.IsInPagePreview() );
+ aChild = pParaFrame;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ const size_t nSelObjs = pFEShell->IsObjSelected();
+ if( 0 == nSelObjs || o3tl::make_unsigned(nSelectedChildIndex) >= nSelObjs )
+ throwIndexOutOfBoundsException();
+
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
+ SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview()) ==
+ m_rContext.GetFrame() &&
+ pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ aChild = rChild;
+ else
+ --nSelectedChildIndex;
+ }
+ if (aChild.IsValid())
+ break;
+ }
+ }
+
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ OSL_ENSURE( m_rContext.GetMap() != nullptr, "We need the map." );
+ Reference< XAccessible > xChild;
+ if( aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ m_rContext.GetMap()->GetContextImpl( aChild.GetSwFrame() ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( &m_rContext );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ m_rContext.GetMap()->GetContextImpl( aChild.GetDrawObject(),
+ &m_rContext ) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ return xChild;
+}
+
+// index has to be treated as global child index.
+void SwAccessibleSelectionHelper::deselectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard g;
+
+ if( nChildIndex < 0 ||
+ nChildIndex >= m_rContext.GetChildCount( *(m_rContext.GetMap()) ) )
+ throwIndexOutOfBoundsException();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accselectionhelper.hxx b/sw/source/core/access/accselectionhelper.hxx
new file mode 100644
index 0000000000..90cfc303e9
--- /dev/null
+++ b/sw/source/core/access/accselectionhelper.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCSELECTIONHELPER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCSELECTIONHELPER_HXX
+
+#include <sal/types.h>
+#include <com/sun/star/uno/Reference.h>
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+class SwAccessibleContext;
+class SwFEShell;
+
+class SwAccessibleSelectionHelper
+{
+ /// the context on which this helper works
+ SwAccessibleContext& m_rContext;
+
+ /// get FE-Shell
+ SwFEShell* GetFEShell();
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ void throwIndexOutOfBoundsException();
+
+public:
+ SwAccessibleSelectionHelper( SwAccessibleContext& rContext );
+
+ // XAccessibleSelection
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ void selectAccessibleChild(
+ sal_Int64 nChildIndex );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ bool isAccessibleChildSelected(
+ sal_Int64 nChildIndex );
+ /// @throws css::uno::RuntimeException
+ void selectAllAccessibleChildren( );
+ /// @throws css::uno::RuntimeException
+ sal_Int64 getSelectedAccessibleChildCount( );
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::accessibility::XAccessible > getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex );
+ // index has to be treated as global child index.
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ void deselectAccessibleChild(
+ sal_Int64 nChildIndex );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctable.cxx b/sw/source/core/access/acctable.cxx
new file mode 100644
index 0000000000..3c8beaf9a0
--- /dev/null
+++ b/sw/source/core/access/acctable.cxx
@@ -0,0 +1,1722 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <vector>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <frmfmt.hxx>
+#include <tabfrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <crsrsh.hxx>
+#include <viscrs.hxx>
+#include "accfrmobjslist.hxx"
+#include <accmap.hxx>
+#include <strings.hrc>
+#include "acctable.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+
+#include <editeng/brushitem.hxx>
+#include <swatrset.hxx>
+#include <frmatr.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::sw::access;
+
+typedef o3tl::sorted_vector< sal_Int32 > Int32Set_Impl;
+
+const unsigned int SELECTION_WITH_NUM = 10;
+
+namespace {
+
+class SwAccTableSelHandler_Impl
+{
+public:
+ virtual void Unselect( sal_Int32 nRowOrCol, sal_Int32 nExt ) = 0;
+
+protected:
+ ~SwAccTableSelHandler_Impl() {}
+};
+
+}
+
+class SwAccessibleTableData_Impl
+{
+ SwAccessibleMap& mrAccMap;
+ Int32Set_Impl maRows;
+ Int32Set_Impl maColumns;
+ Point maTabFramePos;
+ const SwTabFrame *mpTabFrame;
+ bool mbIsInPagePreview;
+ bool mbOnlyTableColumnHeader;
+
+ void CollectData( const SwFrame *pFrame );
+
+ bool FindCell( const Point& rPos, const SwFrame *pFrame ,
+ bool bExact, const SwFrame *& rFrame ) const;
+
+ void GetSelection( const Point& rTabPos, const SwRect& rArea,
+ const SwSelBoxes& rSelBoxes, const SwFrame *pFrame,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const;
+
+ // #i77106#
+ bool IncludeRow( const SwFrame& rFrame ) const
+ {
+ return !mbOnlyTableColumnHeader ||
+ mpTabFrame->IsInHeadline( rFrame );
+ }
+public:
+ // #i77106# - add third optional parameter <bOnlyTableColumnHeader>, default value <false>
+ SwAccessibleTableData_Impl( SwAccessibleMap& rAccMap,
+ const SwTabFrame *pTabFrame,
+ bool bIsInPagePreview,
+ bool bOnlyTableColumnHeader = false );
+
+ const Int32Set_Impl& GetRows() const { return maRows; }
+ const Int32Set_Impl& GetColumns() const { return maColumns; }
+
+ inline Int32Set_Impl::const_iterator GetRowIter( sal_Int32 nRow ) const;
+ inline Int32Set_Impl::const_iterator GetColumnIter( sal_Int32 nCol ) const;
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ const SwFrame *GetCell( sal_Int32 nRow, sal_Int32 nColumn, SwAccessibleTable *pThis ) const;
+ const SwFrame *GetCellAtPos( sal_Int32 nLeft, sal_Int32 nTop ) const;
+ inline sal_Int32 GetRowCount() const;
+ inline sal_Int32 GetColumnCount() const;
+ bool CompareExtents( const SwAccessibleTableData_Impl& r ) const;
+
+ void GetSelection( sal_Int32 nStart, sal_Int32 nEnd,
+ const SwSelBoxes& rSelBoxes,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const;
+
+ /// @throws lang::IndexOutOfBoundsException
+ void CheckRowAndCol( sal_Int32 nRow, sal_Int32 nCol,
+ SwAccessibleTable *pThis ) const;
+
+ const Point& GetTablePos() const { return maTabFramePos; }
+ void SetTablePos( const Point& rPos ) { maTabFramePos = rPos; }
+};
+
+void SwAccessibleTableData_Impl::CollectData( const SwFrame *pFrame )
+{
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ if( pLower->IsRowFrame() )
+ {
+ // #i77106#
+ if ( IncludeRow( *pLower ) )
+ {
+ maRows.insert( pLower->getFrameArea().Top() - maTabFramePos.getY() );
+ CollectData( pLower );
+ }
+ }
+ else if( pLower->IsCellFrame() &&
+ rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ maColumns.insert( pLower->getFrameArea().Left() - maTabFramePos.getX() );
+ }
+ else
+ {
+ CollectData( pLower );
+ }
+ }
+ ++aIter;
+ }
+}
+
+bool SwAccessibleTableData_Impl::FindCell(
+ const Point& rPos, const SwFrame *pFrame, bool bExact,
+ const SwFrame *& rRet ) const
+{
+ bool bFound = false;
+
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( !bFound && aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ OSL_ENSURE( pLower, "child should be a frame" );
+ if( pLower )
+ {
+ if( rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ OSL_ENSURE( pLower->IsCellFrame(), "lower is not a cell frame" );
+ const SwRect& rFrame = pLower->getFrameArea();
+ if( rFrame.Right() >= rPos.X() && rFrame.Bottom() >= rPos.Y() )
+ {
+ // We have found the cell
+ OSL_ENSURE( rFrame.Left() <= rPos.X() && rFrame.Top() <= rPos.Y(),
+ "find frame moved to far!" );
+ bFound = true;
+ if( !bExact ||
+ (rFrame.Top() == rPos.Y() && rFrame.Left() == rPos.Y() ) )
+ {
+ rRet = pLower;
+ }
+ }
+ }
+ else
+ {
+ // #i77106#
+ if ( !pLower->IsRowFrame() ||
+ IncludeRow( *pLower ) )
+ {
+ bFound = FindCell( rPos, pLower, bExact, rRet );
+ }
+ }
+ }
+ ++aIter;
+ }
+
+ return bFound;
+}
+
+void SwAccessibleTableData_Impl::GetSelection(
+ const Point& rTabPos,
+ const SwRect& rArea,
+ const SwSelBoxes& rSelBoxes,
+ const SwFrame *pFrame,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const
+{
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ OSL_ENSURE( pLower, "child should be a frame" );
+ const SwRect& rBox = rLower.GetBox( mrAccMap );
+ if( pLower && rBox.Overlaps( rArea ) )
+ {
+ if( rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ OSL_ENSURE( pLower->IsCellFrame(), "lower is not a cell frame" );
+ const SwCellFrame *pCFrame =
+ static_cast < const SwCellFrame * >( pLower );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ if( rSelBoxes.find( pBox ) == rSelBoxes.end() )
+ {
+ const Int32Set_Impl rRowsOrCols =
+ bColumns ? maColumns : maRows;
+
+ sal_Int32 nPos = bColumns ? (rBox.Left() - rTabPos.X())
+ : (rBox.Top() - rTabPos.Y());
+ Int32Set_Impl::const_iterator aSttRowOrCol(
+ rRowsOrCols.lower_bound( nPos ) );
+ sal_Int32 nRowOrCol =
+ static_cast< sal_Int32 >( std::distance(
+ rRowsOrCols.begin(), aSttRowOrCol ) );
+
+ nPos = bColumns ? (rBox.Right() - rTabPos.X())
+ : (rBox.Bottom() - rTabPos.Y());
+ Int32Set_Impl::const_iterator aEndRowOrCol(
+ rRowsOrCols.upper_bound( nPos ) );
+ sal_Int32 nExt =
+ static_cast< sal_Int32 >( std::distance(
+ aSttRowOrCol, aEndRowOrCol ) );
+
+ rSelHdl.Unselect( nRowOrCol, nExt );
+ }
+ }
+ else
+ {
+ // #i77106#
+ if ( !pLower->IsRowFrame() ||
+ IncludeRow( *pLower ) )
+ {
+ GetSelection( rTabPos, rArea, rSelBoxes, pLower, rSelHdl,
+ bColumns );
+ }
+ }
+ }
+ ++aIter;
+ }
+}
+
+const SwFrame *SwAccessibleTableData_Impl::GetCell(
+ sal_Int32 nRow, sal_Int32 nColumn,
+ SwAccessibleTable *pThis ) const
+{
+ CheckRowAndCol( nRow, nColumn, pThis );
+
+ Int32Set_Impl::const_iterator aSttCol( GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow( GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetCellAtPos( *aSttCol, *aSttRow );
+
+ return pCellFrame;
+}
+
+void SwAccessibleTableData_Impl::GetSelection(
+ sal_Int32 nStart, sal_Int32 nEnd,
+ const SwSelBoxes& rSelBoxes,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const
+{
+ SwRect aArea( mpTabFrame->getFrameArea() );
+ Point aPos( aArea.Pos() );
+
+ const Int32Set_Impl& rRowsOrColumns = bColumns ? maColumns : maRows;
+ if( nStart > 0 )
+ {
+ Int32Set_Impl::const_iterator aStt( rRowsOrColumns.begin() );
+ std::advance( aStt,
+ static_cast< Int32Set_Impl::difference_type >( nStart ) );
+ if( bColumns )
+ aArea.Left( *aStt + aPos.getX() );
+ else
+ aArea.Top( *aStt + aPos.getY() );
+ }
+ if( nEnd < static_cast< sal_Int32 >( rRowsOrColumns.size() ) )
+ {
+ Int32Set_Impl::const_iterator aEnd( rRowsOrColumns.begin() );
+ std::advance( aEnd,
+ static_cast< Int32Set_Impl::difference_type >( nEnd ) );
+ if( bColumns )
+ aArea.Right( *aEnd + aPos.getX() - 1 );
+ else
+ aArea.Bottom( *aEnd + aPos.getY() - 1 );
+ }
+
+ GetSelection( aPos, aArea, rSelBoxes, mpTabFrame, rSelHdl, bColumns );
+}
+
+const SwFrame *SwAccessibleTableData_Impl::GetCellAtPos(
+ sal_Int32 nLeft, sal_Int32 nTop ) const
+{
+ Point aPos( mpTabFrame->getFrameArea().Pos() );
+ aPos.Move( nLeft, nTop );
+ const SwFrame *pRet = nullptr;
+ FindCell( aPos, mpTabFrame, false/*bExact*/, pRet );
+
+ return pRet;
+}
+
+inline sal_Int32 SwAccessibleTableData_Impl::GetRowCount() const
+{
+ sal_Int32 count = static_cast< sal_Int32 >( maRows.size() ) ;
+ count = (count <=0)? 1:count;
+ return count;
+}
+
+inline sal_Int32 SwAccessibleTableData_Impl::GetColumnCount() const
+{
+ return static_cast< sal_Int32 >( maColumns.size() );
+}
+
+bool SwAccessibleTableData_Impl::CompareExtents(
+ const SwAccessibleTableData_Impl& rCmp ) const
+{
+ return maRows == rCmp.maRows
+ && maColumns == rCmp.maColumns;
+}
+
+SwAccessibleTableData_Impl::SwAccessibleTableData_Impl( SwAccessibleMap& rAccMap,
+ const SwTabFrame *pTabFrame,
+ bool bIsInPagePreview,
+ bool bOnlyTableColumnHeader )
+ : mrAccMap( rAccMap )
+ , maTabFramePos( pTabFrame->getFrameArea().Pos() )
+ , mpTabFrame( pTabFrame )
+ , mbIsInPagePreview( bIsInPagePreview )
+ , mbOnlyTableColumnHeader( bOnlyTableColumnHeader )
+{
+ CollectData( mpTabFrame );
+}
+
+inline Int32Set_Impl::const_iterator SwAccessibleTableData_Impl::GetRowIter(
+ sal_Int32 nRow ) const
+{
+ Int32Set_Impl::const_iterator aCol( GetRows().begin() );
+ if( nRow > 0 )
+ {
+ std::advance( aCol,
+ static_cast< Int32Set_Impl::difference_type >( nRow ) );
+ }
+ return aCol;
+}
+
+inline Int32Set_Impl::const_iterator SwAccessibleTableData_Impl::GetColumnIter(
+ sal_Int32 nColumn ) const
+{
+ Int32Set_Impl::const_iterator aCol = GetColumns().begin();
+ if( nColumn > 0 )
+ {
+ std::advance( aCol,
+ static_cast< Int32Set_Impl::difference_type >( nColumn ) );
+ }
+ return aCol;
+}
+
+void SwAccessibleTableData_Impl::CheckRowAndCol(
+ sal_Int32 nRow, sal_Int32 nCol, SwAccessibleTable *pThis ) const
+{
+ if( ( nRow < 0 || o3tl::make_unsigned(nRow) >= maRows.size() ) ||
+ ( nCol < 0 || o3tl::make_unsigned(nCol) >= maColumns.size() ) )
+ {
+ uno::Reference < XAccessibleTable > xThis( pThis );
+ lang::IndexOutOfBoundsException aExcept(
+ "row or column index out of range",
+ xThis );
+ throw aExcept;
+ }
+}
+
+namespace {
+
+class SwAccSingleTableSelHandler_Impl : public SwAccTableSelHandler_Impl
+{
+ bool m_bSelected;
+
+public:
+
+ inline SwAccSingleTableSelHandler_Impl();
+
+ virtual ~SwAccSingleTableSelHandler_Impl() {}
+
+ bool IsSelected() const { return m_bSelected; }
+
+ virtual void Unselect( sal_Int32, sal_Int32 ) override;
+};
+
+}
+
+inline SwAccSingleTableSelHandler_Impl::SwAccSingleTableSelHandler_Impl() :
+ m_bSelected( true )
+{
+}
+
+void SwAccSingleTableSelHandler_Impl::Unselect( sal_Int32, sal_Int32 )
+{
+ m_bSelected = false;
+}
+
+namespace {
+
+class SwAccAllTableSelHandler_Impl : public SwAccTableSelHandler_Impl
+
+{
+ std::vector< bool > m_aSelected;
+ sal_Int32 m_nCount;
+
+public:
+ explicit SwAccAllTableSelHandler_Impl(sal_Int32 nSize)
+ : m_aSelected(nSize, true)
+ , m_nCount(nSize)
+ {
+ }
+
+ uno::Sequence < sal_Int32 > GetSelSequence();
+
+ virtual void Unselect( sal_Int32 nRowOrCol, sal_Int32 nExt ) override;
+ virtual ~SwAccAllTableSelHandler_Impl();
+};
+
+}
+
+SwAccAllTableSelHandler_Impl::~SwAccAllTableSelHandler_Impl()
+{
+}
+
+uno::Sequence < sal_Int32 > SwAccAllTableSelHandler_Impl::GetSelSequence()
+{
+ OSL_ENSURE( m_nCount >= 0, "underflow" );
+ uno::Sequence < sal_Int32 > aRet( m_nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = m_aSelected.size();
+ for( size_t i=0; i < nSize && nPos < m_nCount; i++ )
+ {
+ if( m_aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ OSL_ENSURE( nPos == m_nCount, "count is wrong" );
+
+ return aRet;
+}
+
+void SwAccAllTableSelHandler_Impl::Unselect( sal_Int32 nRowOrCol,
+ sal_Int32 nExt )
+{
+ OSL_ENSURE( o3tl::make_unsigned( nRowOrCol ) < m_aSelected.size(),
+ "index too large" );
+ OSL_ENSURE( o3tl::make_unsigned( nRowOrCol+nExt ) <= m_aSelected.size(),
+ "extent too large" );
+ while( nExt )
+ {
+ if( m_aSelected[static_cast< size_t >( nRowOrCol )] )
+ {
+ m_aSelected[static_cast< size_t >( nRowOrCol )] = false;
+ m_nCount--;
+ }
+ nExt--;
+ nRowOrCol++;
+ }
+}
+
+const SwSelBoxes *SwAccessibleTable::GetSelBoxes() const
+{
+ const SwSelBoxes *pSelBoxes = nullptr;
+ const SwCursorShell *pCSh = GetCursorShell();
+ if( (pCSh != nullptr) && pCSh->IsTableMode() )
+ {
+ pSelBoxes = &pCSh->GetTableCursor()->GetSelectedBoxes();
+ }
+
+ return pSelBoxes;
+}
+
+void SwAccessibleTable::FireTableChangeEvent(
+ const SwAccessibleTableData_Impl& rTableData )
+{
+ AccessibleTableModelChange aModelChange;
+ aModelChange.Type = AccessibleTableModelChangeType::UPDATE;
+ aModelChange.FirstRow = 0;
+ aModelChange.LastRow = rTableData.GetRowCount() - 1;
+ aModelChange.FirstColumn = 0;
+ aModelChange.LastColumn = rTableData.GetColumnCount() - 1;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_MODEL_CHANGED;
+ aEvent.NewValue <<= aModelChange;
+
+ FireAccessibleEvent( aEvent );
+}
+
+const SwTableBox* SwAccessibleTable::GetTableBox( sal_Int64 nChildIndex ) const
+{
+ OSL_ENSURE( nChildIndex >= 0, "Illegal child index." );
+ OSL_ENSURE( nChildIndex < const_cast<SwAccessibleTable*>(this)->getAccessibleChildCount(), "Illegal child index." ); // #i77106#
+
+ const SwTableBox* pBox = nullptr;
+
+ // get table box for 'our' table cell
+ SwAccessibleChild aCell( GetChild( *const_cast<SwAccessibleMap*>(GetMap()), nChildIndex ) );
+ if( aCell.GetSwFrame() )
+ {
+ const SwFrame* pChildFrame = aCell.GetSwFrame();
+ if( (pChildFrame != nullptr) && pChildFrame->IsCellFrame() )
+ {
+ const SwCellFrame* pCellFrame =
+ static_cast<const SwCellFrame*>( pChildFrame );
+ pBox = pCellFrame->GetTabBox();
+ }
+ }
+
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+ return pBox;
+}
+
+bool SwAccessibleTable::IsChildSelected( sal_Int64 nChildIndex ) const
+{
+ bool bRet = false;
+ const SwSelBoxes* pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+ bRet = pSelBoxes->find( const_cast<SwTableBox*>( pBox ) ) != pSelBoxes->end();
+ }
+
+ return bRet;
+}
+
+sal_Int64 SwAccessibleTable::GetIndexOfSelectedChild(
+ sal_Int64 nSelectedChildIndex ) const
+{
+ // iterate over all children to n-th isAccessibleChildSelected()
+ sal_Int64 nChildren = const_cast<SwAccessibleTable*>(this)->getAccessibleChildCount(); // #i77106#
+ if( nSelectedChildIndex >= nChildren )
+ return -1;
+
+ sal_Int64 n = 0;
+ while( n < nChildren )
+ {
+ if( IsChildSelected( n ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ break;
+ else
+ --nSelectedChildIndex;
+ }
+ ++n;
+ }
+
+ return n < nChildren ? n : -1;
+}
+
+void SwAccessibleTable::GetStates( sal_Int64& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+ //Add resizable state to table
+ rStateSet |= AccessibleStateType::RESIZABLE;
+ // MULTISELECTABLE
+ rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell )
+ rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+}
+
+SwAccessibleTable::SwAccessibleTable(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTabFrame* pTabFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::TABLE, pTabFrame )
+{
+ const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat();
+ if(pFrameFormat)
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+
+ SetName( pFrameFormat->GetName() + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ const OUString sArg1( static_cast< const SwTabFrame * >( GetFrame() )->GetFormat()->GetName() );
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ m_sDesc = GetResource( STR_ACCESS_TABLE_DESC, &sArg1, &sArg2 );
+ UpdateTableData();
+}
+
+SwAccessibleTable::~SwAccessibleTable()
+{
+ SolarMutexGuard aGuard;
+
+ mpTableData.reset();
+}
+
+void SwAccessibleTable::Notify(const SfxHint& rHint)
+{
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(GetFrame());
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ }
+ else if (rHint.GetId() == SfxHintId::SwNameChanged && pTabFrame)
+ {
+ const SwFrameFormat *pFrameFormat = pTabFrame->GetFormat();
+ const OUString sOldName( GetName() );
+ const OUString sNewTabName = pFrameFormat->GetName();
+
+ SetName( sNewTabName + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ if( sOldName != GetName() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldName;
+ aEvent.NewValue <<= GetName();
+ FireAccessibleEvent( aEvent );
+ }
+
+ const OUString sOldDesc( m_sDesc );
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ m_sDesc = GetResource( STR_ACCESS_TABLE_DESC, &sNewTabName, &sArg2 );
+ if( m_sDesc != sOldDesc )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= m_sDesc;
+ FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+uno::Any SwAccessibleTable::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleTable>::get() )
+ {
+ uno::Reference<XAccessibleTable> xThis( this );
+ aRet <<= xThis;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleSelection>::get() )
+ {
+ uno::Reference<XAccessibleSelection> xSelection( this );
+ aRet <<= xSelection;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleTableSelection>::get() )
+ {
+ uno::Reference<XAccessibleTableSelection> xTableExtent( this );
+ aRet <<= xTableExtent;
+ }
+ else
+ {
+ aRet = SwAccessibleContext::queryInterface(rType);
+ }
+
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleTable::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleSelection>::get(),
+ cppu::UnoType<XAccessibleTable>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleTable::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// #i77106#
+std::unique_ptr<SwAccessibleTableData_Impl> SwAccessibleTable::CreateNewTableData()
+{
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ return std::unique_ptr<SwAccessibleTableData_Impl>(new SwAccessibleTableData_Impl( *GetMap(), pTabFrame, IsInPagePreview() ));
+}
+
+void SwAccessibleTable::UpdateTableData()
+{
+ // #i77106# - usage of new method <CreateNewTableData()>
+ mpTableData = CreateNewTableData();
+}
+
+void SwAccessibleTable::ClearTableData()
+{
+ mpTableData.reset();
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return m_sDesc;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetTableData().GetRowCount();
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetTableData().GetColumnCount();
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleRowDescription(
+ sal_Int32 nRow )
+{
+ // #i87532# - determine table cell in <nRow>th row and
+ // in first column of row header table and return its text content.
+ OUString sRowDesc;
+
+ GetTableData().CheckRowAndCol(nRow, 0, this);
+
+ uno::Reference< XAccessibleTable > xTableRowHeader = getAccessibleRowHeaders();
+ if ( xTableRowHeader.is() )
+ {
+ uno::Reference< XAccessible > xRowHeaderCell =
+ xTableRowHeader->getAccessibleCellAt( nRow, 0 );
+ OSL_ENSURE( xRowHeaderCell.is(),
+ "<SwAccessibleTable::getAccessibleRowDescription(..)> - missing row header cell -> serious issue." );
+ uno::Reference< XAccessibleContext > xRowHeaderCellContext =
+ xRowHeaderCell->getAccessibleContext();
+ const sal_Int64 nCellChildCount( xRowHeaderCellContext->getAccessibleChildCount() );
+ for ( sal_Int64 nChildIndex = 0; nChildIndex < nCellChildCount; ++nChildIndex )
+ {
+ uno::Reference< XAccessible > xChild = xRowHeaderCellContext->getAccessibleChild( nChildIndex );
+ uno::Reference< XAccessibleText > xChildText( xChild, uno::UNO_QUERY );
+ if ( xChildText.is() )
+ {
+ sRowDesc += xChildText->getText();
+ }
+ }
+ }
+
+ return sRowDesc;
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleColumnDescription(
+ sal_Int32 nColumn )
+{
+ // #i87532# - determine table cell in first row and
+ // in <nColumn>th column of column header table and return its text content.
+ OUString sColumnDesc;
+
+ GetTableData().CheckRowAndCol(0, nColumn, this);
+
+ uno::Reference< XAccessibleTable > xTableColumnHeader = getAccessibleColumnHeaders();
+ if ( xTableColumnHeader.is() )
+ {
+ uno::Reference< XAccessible > xColumnHeaderCell =
+ xTableColumnHeader->getAccessibleCellAt( 0, nColumn );
+ OSL_ENSURE( xColumnHeaderCell.is(),
+ "<SwAccessibleTable::getAccessibleColumnDescription(..)> - missing column header cell -> serious issue." );
+ uno::Reference< XAccessibleContext > xColumnHeaderCellContext =
+ xColumnHeaderCell->getAccessibleContext();
+ const sal_Int64 nCellChildCount( xColumnHeaderCellContext->getAccessibleChildCount() );
+ for ( sal_Int64 nChildIndex = 0; nChildIndex < nCellChildCount; ++nChildIndex )
+ {
+ uno::Reference< XAccessible > xChild = xColumnHeaderCellContext->getAccessibleChild( nChildIndex );
+ uno::Reference< XAccessibleText > xChildText( xChild, uno::UNO_QUERY );
+ if ( xChildText.is() )
+ {
+ sColumnDesc += xChildText->getText();
+ }
+ }
+ }
+
+ return sColumnDesc;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRowExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nExtend = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ UpdateTableData();
+ GetTableData().CheckRowAndCol( nRow, nColumn, this );
+
+ Int32Set_Impl::const_iterator aSttCol(
+ GetTableData().GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow(
+ GetTableData().GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ if( pCellFrame )
+ {
+ sal_Int32 nBottom = pCellFrame->getFrameArea().Bottom();
+ nBottom -= GetFrame()->getFrameArea().Top();
+ Int32Set_Impl::const_iterator aEndRow(
+ GetTableData().GetRows().upper_bound( nBottom ) );
+ nExtend =
+ static_cast< sal_Int32 >( std::distance( aSttRow, aEndRow ) );
+ }
+
+ return nExtend;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumnExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nExtend = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+ UpdateTableData();
+
+ GetTableData().CheckRowAndCol( nRow, nColumn, this );
+
+ Int32Set_Impl::const_iterator aSttCol(
+ GetTableData().GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow(
+ GetTableData().GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ if( pCellFrame )
+ {
+ sal_Int32 nRight = pCellFrame->getFrameArea().Right();
+ nRight -= GetFrame()->getFrameArea().Left();
+ Int32Set_Impl::const_iterator aEndCol(
+ GetTableData().GetColumns().upper_bound( nRight ) );
+ nExtend =
+ static_cast< sal_Int32 >( std::distance( aSttCol, aEndCol ) );
+ }
+
+ return nExtend;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL
+ SwAccessibleTable::getAccessibleRowHeaders( )
+{
+ // Row headers aren't supported
+ return uno::Reference< XAccessibleTable >();
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL
+ SwAccessibleTable::getAccessibleColumnHeaders( )
+{
+ SolarMutexGuard aGuard;
+
+ // #i87532# - assure that return accessible object is empty,
+ // if no column header exists.
+ rtl::Reference<SwAccessibleTableColHeaders> pTableColHeaders =
+ new SwAccessibleTableColHeaders(GetMap()->shared_from_this(),
+ static_cast<const SwTabFrame *>(GetFrame()));
+ if ( pTableColHeaders->getAccessibleChildCount() <= 0 )
+ {
+ return uno::Reference< XAccessibleTable >();
+ }
+
+ return pTableColHeaders;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL SwAccessibleTable::getSelectedAccessibleRows()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ sal_Int32 nRows = GetTableData().GetRowCount();
+ SwAccAllTableSelHandler_Impl aSelRows( nRows );
+
+ GetTableData().GetSelection( 0, nRows, *pSelBoxes, aSelRows,
+ false );
+
+ return aSelRows.GetSelSequence();
+ }
+ else
+ {
+ return uno::Sequence< sal_Int32 >( 0 );
+ }
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL SwAccessibleTable::getSelectedAccessibleColumns()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ sal_Int32 nCols = GetTableData().GetColumnCount();
+ SwAccAllTableSelHandler_Impl aSelCols( nCols );
+
+ GetTableData().GetSelection( 0, nCols, *pSelBoxes, aSelCols, true );
+
+ return aSelCols.GetSelSequence();
+ }
+ else
+ {
+ return uno::Sequence< sal_Int32 >( 0 );
+ }
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ GetTableData().CheckRowAndCol( nRow, 0, this );
+
+ bool bRet;
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ SwAccSingleTableSelHandler_Impl aSelRow;
+ GetTableData().GetSelection( nRow, nRow+1, *pSelBoxes, aSelRow,
+ false );
+ bRet = aSelRow.IsSelected();
+ }
+ else
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleColumnSelected(
+ sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ GetTableData().CheckRowAndCol( 0, nColumn, this );
+
+ bool bRet;
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ SwAccSingleTableSelHandler_Impl aSelCol;
+
+ GetTableData().GetSelection( nColumn, nColumn+1, *pSelBoxes, aSelCol,
+ true );
+ bRet = aSelCol.IsSelected();
+ }
+ else
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleCellAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ uno::Reference< XAccessible > xRet;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pCellFrame =
+ GetTableData().GetCell( nRow, nColumn, this );
+ if( pCellFrame )
+ xRet = GetMap()->GetContext( pCellFrame );
+
+ return xRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleCaption()
+{
+ // captions aren't supported
+ return uno::Reference< XAccessible >();
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleSummary()
+{
+ // summaries aren't supported
+ return uno::Reference< XAccessible >();
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleSelected(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ bool bRet = false;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pFrame =
+ GetTableData().GetCell( nRow, nColumn, this );
+ if( pFrame && pFrame->IsCellFrame() )
+ {
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ const SwCellFrame *pCFrame = static_cast < const SwCellFrame * >( pFrame );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ bRet = pSelBoxes->find( pBox ) != pSelBoxes->end();
+ }
+ }
+
+ return bRet;
+}
+
+sal_Int64 SAL_CALL SwAccessibleTable::getAccessibleIndex(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwAccessibleChild aCell( GetTableData().GetCell( nRow, nColumn, this ));
+ if ( aCell.IsValid() )
+ {
+ nRet = GetChildIndex( *(GetMap()), aCell );
+ }
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i77106#
+ if ( ( nChildIndex < 0 ) ||
+ ( nChildIndex >= getAccessibleChildCount() ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwAccessibleChild aCell( GetChild( *(GetMap()), nChildIndex ) );
+ if ( aCell.GetSwFrame() )
+ {
+ sal_Int32 nTop = aCell.GetSwFrame()->getFrameArea().Top();
+ nTop -= GetFrame()->getFrameArea().Top();
+ Int32Set_Impl::const_iterator aRow(
+ GetTableData().GetRows().lower_bound( nTop ) );
+ nRet = static_cast< sal_Int32 >( std::distance(
+ GetTableData().GetRows().begin(), aRow ) );
+ }
+ else
+ {
+ OSL_ENSURE( !aCell.IsValid(), "SwAccessibleTable::getAccessibleColumn:"
+ "aCell not expected to be valid.");
+
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumn(
+ sal_Int64 nChildIndex )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i77106#
+ if ( ( nChildIndex < 0 ) ||
+ ( nChildIndex >= getAccessibleChildCount() ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwAccessibleChild aCell( GetChild( *(GetMap()), nChildIndex ) );
+ if ( aCell.GetSwFrame() )
+ {
+ sal_Int32 nLeft = aCell.GetSwFrame()->getFrameArea().Left();
+ nLeft -= GetFrame()->getFrameArea().Left();
+ Int32Set_Impl::const_iterator aCol(
+ GetTableData().GetColumns().lower_bound( nLeft ) );
+ nRet = static_cast< sal_Int32 >( std::distance(
+ GetTableData().GetColumns().begin(), aCol ) );
+ }
+ else
+ {
+ OSL_ENSURE( !aCell.IsValid(), "SwAccessibleTable::getAccessibleColumn:"
+ "aCell not expected to be valid.");
+
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return nRet;
+}
+
+OUString SAL_CALL SwAccessibleTable::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleTableView";
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::supportsService(
+ const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleTable::getSupportedServiceNames()
+{
+ return { "com.sun.star.table.AccessibleTableView", sAccessibleServiceName };
+}
+
+void SwAccessibleTable::InvalidatePosOrSize( const SwRect& rOldBox )
+{
+ SolarMutexGuard aGuard;
+
+ //need to update children
+ std::unique_ptr<SwAccessibleTableData_Impl> pNewTableData = CreateNewTableData();
+ if( !pNewTableData->CompareExtents( GetTableData() ) )
+ {
+ mpTableData = std::move(pNewTableData);
+ FireTableChangeEvent(*mpTableData);
+ }
+ if( HasTableData() )
+ GetTableData().SetTablePos( GetFrame()->getFrameArea().Pos() );
+
+ SwAccessibleContext::InvalidatePosOrSize( rOldBox );
+}
+
+void SwAccessibleTable::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+void SwAccessibleTable::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible )
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrame *pFrame = rChildFrameOrObj.GetSwFrame();
+ OSL_ENSURE( pFrame, "frame expected" );
+ if( HasTableData() )
+ {
+ FireTableChangeEvent( GetTableData() );
+ ClearTableData();
+ }
+
+ // There are two reason why this method has been called. The first one
+ // is there is no context for pFrame. The method is then called by
+ // the map, and we have to call our superclass.
+ // The other situation is that we have been call by a call to get notified
+ // about its change. We then must not call the superclass
+ uno::Reference< XAccessible > xAcc( GetMap()->GetContext( pFrame, false ) );
+ if( !xAcc.is() )
+ SwAccessibleContext::DisposeChild( rChildFrameOrObj, bRecursive, bCanSkipInvisible );
+}
+
+void SwAccessibleTable::InvalidateChildPosOrSize( const SwAccessibleChild& rChildFrameOrObj,
+ const SwRect& rOldBox )
+{
+ SolarMutexGuard aGuard;
+
+ if( HasTableData() )
+ {
+ SAL_WARN_IF( HasTableData() &&
+ GetFrame()->getFrameArea().Pos() != GetTableData().GetTablePos(),
+ "sw.a11y", "table has invalid position" );
+ if( HasTableData() )
+ {
+ std::unique_ptr<SwAccessibleTableData_Impl> pNewTableData = CreateNewTableData(); // #i77106#
+ if( !pNewTableData->CompareExtents( GetTableData() ) )
+ {
+ if (pNewTableData->GetRowCount() != mpTableData->GetRowCount()
+ && 1 < GetTableData().GetRowCount())
+ {
+ Int32Set_Impl::const_iterator aSttCol( GetTableData().GetColumnIter( 0 ) );
+ Int32Set_Impl::const_iterator aSttRow( GetTableData().GetRowIter( 1 ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ Int32Set_Impl::const_iterator aSttCol2( pNewTableData->GetColumnIter( 0 ) );
+ Int32Set_Impl::const_iterator aSttRow2( pNewTableData->GetRowIter( 0 ) );
+ const SwFrame *pCellFrame2 = pNewTableData->GetCellAtPos( *aSttCol2, *aSttRow2 );
+
+ if(pCellFrame == pCellFrame2)
+ {
+ AccessibleTableModelChange aModelChange;
+ aModelChange.Type = AccessibleTableModelChangeType::UPDATE;
+ aModelChange.FirstRow = 0;
+ aModelChange.LastRow = mpTableData->GetRowCount() - 1;
+ aModelChange.FirstColumn = 0;
+ aModelChange.LastColumn = mpTableData->GetColumnCount() - 1;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED;
+ aEvent.NewValue <<= aModelChange;
+
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ else
+ FireTableChangeEvent( GetTableData() );
+ ClearTableData();
+ mpTableData = std::move(pNewTableData);
+ }
+ }
+ }
+
+ // #i013961# - always call super class method
+ SwAccessibleContext::InvalidateChildPosOrSize( rChildFrameOrObj, rOldBox );
+}
+
+// XAccessibleSelection
+
+void SAL_CALL SwAccessibleTable::selectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( (nChildIndex < 0) || (nChildIndex >= getAccessibleChildCount()) ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ // preliminaries: get 'our' table box, and get the cursor shell
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell == nullptr )
+ return;
+
+ // assure, that child, identified by the given index, isn't already selected.
+ if ( IsChildSelected( nChildIndex ) )
+ {
+ return;
+ }
+
+ // now we can start to do the work: check whether we already have
+ // a table selection (in 'our' table). If so, extend the
+ // selection, else select the current cell.
+
+ // if we have a selection in a table, check if it's in the
+ // same table that we're trying to select in
+ const SwTableNode* pSelectedTable = pCursorShell->IsCursorInTable();
+ if( pSelectedTable != nullptr )
+ {
+ // get top-most table line
+ const SwTableLine* pUpper = pBox->GetUpper();
+ while( pUpper->GetUpper() != nullptr )
+ pUpper = pUpper->GetUpper()->GetUpper();
+ sal_uInt16 nPos =
+ pSelectedTable->GetTable().GetTabLines().GetPos( pUpper );
+ if( nPos == USHRT_MAX )
+ pSelectedTable = nullptr;
+ }
+
+ // create the new selection
+ const SwStartNode* pStartNode = pBox->GetSttNd();
+ if( pSelectedTable == nullptr || !pCursorShell->GetTableCrs() )
+ {
+ pCursorShell->StartAction();
+ // Set cursor into current cell. This deletes any table cursor.
+ SwPaM aPaM( *pStartNode );
+ aPaM.Move( fnMoveForward, GoInNode );
+ Select( aPaM );
+ // Move cursor to the end of the table creating a selection and a table
+ // cursor.
+ pCursorShell->SetMark();
+ pCursorShell->MoveTable( GotoCurrTable, fnTableEnd );
+ // now set the cursor into the cell again.
+ SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *pPaM->GetPoint() = *pPaM->GetMark();
+ pCursorShell->EndAction();
+ // we now have one cell selected!
+ }
+ else
+ {
+ // if the cursor is already in this table,
+ // expand the current selection (i.e., set
+ // point to new position; keep mark)
+ SwPaM aPaM( *pStartNode );
+ aPaM.Move( fnMoveForward, GoInNode );
+ aPaM.SetMark();
+ const SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *(aPaM.GetMark()) = *pPaM->GetMark();
+ Select( aPaM );
+
+ }
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleChildSelected(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( (nChildIndex < 0) || (nChildIndex >= getAccessibleChildCount()) ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ return IsChildSelected( nChildIndex );
+}
+
+void SAL_CALL SwAccessibleTable::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ }
+}
+
+void SAL_CALL SwAccessibleTable::selectAllAccessibleChildren( )
+{
+ // first clear selection, then select first and last child
+ clearAccessibleSelection();
+ selectAccessibleChild( 0 );
+ selectAccessibleChild( getAccessibleChildCount()-1 ); // #i77106#
+}
+
+sal_Int64 SAL_CALL SwAccessibleTable::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // iterate over all children and count isAccessibleChildSelected()
+ sal_Int64 nCount = 0;
+
+ sal_Int64 nChildren = getAccessibleChildCount(); // #i71106#
+ for( sal_Int64 n = 0; n < nChildren; n++ )
+ if( IsChildSelected( n ) )
+ nCount++;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible> SAL_CALL SwAccessibleTable::getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking (part 1): index lower 0
+ if( nSelectedChildIndex < 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int64 nChildIndex = GetIndexOfSelectedChild( nSelectedChildIndex );
+
+ // parameter checking (part 2): index higher than selected children?
+ if( nChildIndex < 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ // #i77106#
+ if ( nChildIndex >= getAccessibleChildCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return getAccessibleChild( nChildIndex );
+}
+
+// index has to be treated as global child index.
+void SAL_CALL SwAccessibleTable::deselectAccessibleChild(
+ sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+
+ // index has to be treated as global child index
+ if ( !pCursorShell )
+ throw lang::IndexOutOfBoundsException();
+
+ // assure, that given child index is in bounds.
+ if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ // assure, that child, identified by the given index, is selected.
+ if ( !IsChildSelected( nChildIndex ) )
+ return;
+
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+
+ // If we unselect point, then set cursor to mark. If we clear another
+ // selected box, then set cursor to point.
+ // reduce selection to mark.
+ SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ bool bDeselectPoint =
+ pBox->GetSttNd() ==
+ pPaM->GetPoint()->GetNode().FindTableBoxStartNode();
+
+ SwPaM aPaM( bDeselectPoint ? *pPaM->GetMark() : *pPaM->GetPoint() );
+
+ pCursorShell->StartAction();
+
+ // Set cursor into either point or mark
+ Select( aPaM );
+ // Move cursor to the end of the table creating a selection and a table
+ // cursor.
+ pCursorShell->SetMark();
+ pCursorShell->MoveTable( GotoCurrTable, fnTableEnd );
+ // now set the cursor into the cell again.
+ pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *pPaM->GetPoint() = *pPaM->GetMark();
+ pCursorShell->EndAction();
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getBackground()
+{
+ const SvxBrushItem &rBack = GetFrame()->GetAttrSet()->GetBackground();
+ Color crBack = rBack.GetColor();
+
+ if (COL_AUTO == crBack)
+ {
+ uno::Reference<XAccessible> xAccDoc = getAccessibleParent();
+ if (xAccDoc.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponentDoc(xAccDoc,uno::UNO_QUERY);
+ if (xComponentDoc.is())
+ {
+ crBack = Color(ColorTransparency, xComponentDoc->getBackground());
+ }
+ }
+ }
+ return sal_Int32(crBack);
+}
+
+void SwAccessibleTable::FireSelectionEvent( )
+{
+ AccessibleEventObject aEvent;
+
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+
+ for (const unotools::WeakReference<SwAccessibleContext>& rxCell : m_vecCellRemove)
+ {
+ // fdo#57197: check if the object is still alive
+ rtl::Reference<SwAccessibleContext> const pAccCell(rxCell);
+ if (pAccCell)
+ pAccCell->FireAccessibleEvent(aEvent);
+ }
+
+ if (m_vecCellAdd.size() <= SELECTION_WITH_NUM)
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ for (const unotools::WeakReference<SwAccessibleContext>& rxCell : m_vecCellAdd)
+ {
+ // fdo#57197: check if the object is still alive
+ rtl::Reference<SwAccessibleContext> const pAccCell(rxCell);
+ if (pAccCell)
+ pAccCell->FireAccessibleEvent(aEvent);
+ }
+ return ;
+ }
+ else
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ FireAccessibleEvent(aEvent);
+ }
+}
+
+void SwAccessibleTable::AddSelectionCell(
+ SwAccessibleContext *const pAccCell, bool const bAddOrRemove)
+{
+ if (bAddOrRemove)
+ {
+ m_vecCellAdd.emplace_back(pAccCell);
+ }
+ else
+ {
+ m_vecCellRemove.emplace_back(pAccCell);
+ }
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL SwAccessibleTable::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleRowSelected( row ) )
+ return true;
+
+ tools::Long lColumnCount = getAccessibleColumnCount();
+ for(tools::Long lCol = 0; lCol < lColumnCount; lCol ++)
+ {
+ sal_Int64 nChildIndex = getAccessibleIndex(row, lCol);
+ selectAccessibleChild(nChildIndex);
+ }
+
+ return true;
+}
+sal_Bool SAL_CALL SwAccessibleTable::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleColumnSelected( column ) )
+ return true;
+
+ sal_Int32 lRowCount = getAccessibleRowCount();
+
+ for(sal_Int32 lRow = 0; lRow < lRowCount; lRow ++)
+ {
+ sal_Int64 nChildIndex = getAccessibleIndex(lRow, column);
+ selectAccessibleChild(nChildIndex);
+ }
+ return true;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleSelected( row , 0 ) && isAccessibleSelected( row , getAccessibleColumnCount()-1 ) )
+ {
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ return true;
+ }
+ }
+ return true;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleSelected( 0 , column ) && isAccessibleSelected( getAccessibleRowCount()-1,column))
+ {
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ return true;
+ }
+ }
+ return true;
+}
+
+// #i77106# - implementation of class <SwAccessibleTableColHeaders>
+SwAccessibleTableColHeaders::SwAccessibleTableColHeaders(
+ std::shared_ptr<SwAccessibleMap> const& pMap,
+ const SwTabFrame *const pTabFrame)
+ : SwAccessibleTable(pMap, pTabFrame)
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat();
+ if(pFrameFormat)
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+ const OUString aName = pFrameFormat->GetName() + "-ColumnHeaders";
+
+ SetName( aName + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ SetDesc( GetResource( STR_ACCESS_TABLE_DESC, &aName, &sArg2 ) );
+
+ NotRegisteredAtAccessibleMap(); // #i85634#
+}
+
+std::unique_ptr<SwAccessibleTableData_Impl> SwAccessibleTableColHeaders::CreateNewTableData()
+{
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ return std::unique_ptr<SwAccessibleTableData_Impl>(new SwAccessibleTableData_Impl( *(GetMap()), pTabFrame, IsInPagePreview(), true ));
+}
+
+void SwAccessibleTableColHeaders::Notify(const SfxHint& )
+{
+}
+
+// XInterface
+uno::Any SAL_CALL SwAccessibleTableColHeaders::queryInterface( const uno::Type& aType )
+{
+ return SwAccessibleTable::queryInterface( aType );
+}
+
+// XAccessibleContext
+sal_Int64 SAL_CALL SwAccessibleTableColHeaders::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pTabFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( IsInPagePreview() ) )
+ {
+ nCount++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ if ( !rLower.GetSwFrame()->IsRowFrame() ||
+ pTabFrame->IsInHeadline( *(rLower.GetSwFrame()) ) )
+ {
+ nCount += SwAccessibleFrame::GetChildCount( *(GetMap()),
+ GetVisArea(),
+ rLower.GetSwFrame(),
+ IsInPagePreview() );
+ }
+ }
+ ++aIter;
+ }
+
+ return nCount;
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleTableColHeaders::getAccessibleChild (sal_Int64 nIndex)
+{
+ if ( nIndex < 0 || nIndex >= getAccessibleChildCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return SwAccessibleTable::getAccessibleChild( nIndex );
+}
+
+// XAccessibleTable
+uno::Reference< XAccessibleTable >
+ SAL_CALL SwAccessibleTableColHeaders::getAccessibleRowHeaders()
+{
+ return uno::Reference< XAccessibleTable >();
+}
+
+uno::Reference< XAccessibleTable >
+ SAL_CALL SwAccessibleTableColHeaders::getAccessibleColumnHeaders()
+{
+ return uno::Reference< XAccessibleTable >();
+}
+
+// XServiceInfo
+
+OUString SAL_CALL SwAccessibleTableColHeaders::getImplementationName()
+{
+ static constexpr OUStringLiteral sImplName
+ = u"com.sun.star.comp.Writer.SwAccessibleTableColumnHeadersView";
+ return sImplName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctable.hxx b/sw/source/core/access/acctable.hxx
new file mode 100644
index 0000000000..9996ffa33b
--- /dev/null
+++ b/sw/source/core/access/acctable.hxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
+#include <vector>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+
+#include <svl/listener.hxx>
+#include <unotools/weakref.hxx>
+
+#include "acccontext.hxx"
+
+class SwTabFrame;
+class SwAccessibleTableData_Impl;
+class SwTableBox;
+class SwSelBoxes;
+
+namespace sw::access {
+ class SwAccessibleChild;
+}
+
+class SwAccessibleTable :
+ public SwAccessibleContext,
+ public css::accessibility::XAccessibleTable,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleTableSelection,
+ public SvtListener
+{
+ std::unique_ptr<SwAccessibleTableData_Impl> mpTableData; // the table's data, protected by SolarMutex
+ OUString m_sDesc;
+ typedef std::vector< unotools::WeakReference<SwAccessibleContext> > Cells_t;
+ Cells_t m_vecCellAdd;
+ Cells_t m_vecCellRemove;
+
+ const SwSelBoxes *GetSelBoxes() const;
+
+ void FireTableChangeEvent( const SwAccessibleTableData_Impl& rTableData );
+
+ /** get the SwTableBox* for the given child */
+ const SwTableBox* GetTableBox( sal_Int64 ) const;
+
+ bool IsChildSelected( sal_Int64 nChildIndex ) const;
+
+ sal_Int64 GetIndexOfSelectedChild( sal_Int64 nSelectedChildIndex ) const;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTISELECTABLE(+)
+ virtual void GetStates( sal_Int64& rStateSet ) override;
+
+ virtual ~SwAccessibleTable() override;
+
+ // #i77106#
+ void SetDesc( const OUString& sNewDesc )
+ {
+ m_sDesc = sNewDesc;
+ }
+
+ virtual std::unique_ptr<SwAccessibleTableData_Impl> CreateNewTableData(); // #i77106#
+
+ // force update of table data
+ void UpdateTableData();
+
+ // remove the current table data
+ void ClearTableData();
+
+ // get table data, update if necessary
+ inline SwAccessibleTableData_Impl& GetTableData();
+
+ // Is table data available?
+ bool HasTableData() const { return (mpTableData != nullptr); }
+
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTable(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTabFrame* pTableFrame);
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleTable).
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XAccessibleTable
+
+ virtual sal_Int32 SAL_CALL getAccessibleRowCount() override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumnCount( ) override;
+ virtual OUString SAL_CALL getAccessibleRowDescription(
+ sal_Int32 nRow ) override;
+ virtual OUString SAL_CALL getAccessibleColumnDescription(
+ sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleRowExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumnExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleRowHeaders( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleColumnHeaders( ) override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleRows( ) override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleColumns( ) override;
+ virtual sal_Bool SAL_CALL isAccessibleRowSelected( sal_Int32 nRow ) override;
+ virtual sal_Bool SAL_CALL isAccessibleColumnSelected( sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCaption( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleSummary( ) override;
+ virtual sal_Bool SAL_CALL isAccessibleSelected(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int64 SAL_CALL getAccessibleIndex(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleRow( sal_Int64 nChildIndex ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumn( sal_Int64 nChildIndex ) override;
+ // XAccessibleTableSelection
+ virtual sal_Bool SAL_CALL selectRow( sal_Int32 row ) override ;
+ virtual sal_Bool SAL_CALL selectColumn( sal_Int32 column ) override ;
+ virtual sal_Bool SAL_CALL unselectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL unselectColumn( sal_Int32 column ) override;
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // C++ interface
+
+ // The object has been moved by the layout
+ virtual void InvalidatePosOrSize( const SwRect& rOldBox ) override;
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual void DisposeChild( const sw::access::SwAccessibleChild& rFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible ) override;
+ virtual void InvalidateChildPosOrSize( const sw::access::SwAccessibleChild& rFrameOrObj,
+ const SwRect& rFrame ) override;
+
+ // XAccessibleSelection
+
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int64 nChildIndex ) override;
+
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+
+ void FireSelectionEvent( );
+ void AddSelectionCell(SwAccessibleContext*, bool bAddOrRemove);
+};
+
+inline SwAccessibleTableData_Impl& SwAccessibleTable::GetTableData()
+{
+ if( !mpTableData )
+ UpdateTableData();
+ return *mpTableData;
+}
+
+// #i77106# - subclass to represent table column headers
+class SwAccessibleTableColHeaders : public SwAccessibleTable
+{
+protected:
+ virtual ~SwAccessibleTableColHeaders() override
+ {}
+
+ virtual std::unique_ptr<SwAccessibleTableData_Impl> CreateNewTableData() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTableColHeaders(std::shared_ptr<SwAccessibleMap> const& pMap,
+ const SwTabFrame *pTabFrame);
+
+ // XInterface
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ // XAccessibleContext
+
+ /// Return the number of currently visible children.
+ virtual sal_Int64 SAL_CALL getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int64 nIndex) override;
+
+ // XAccessibleTable
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleRowHeaders( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleColumnHeaders( ) override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctextframe.cxx b/sw/source/core/access/acctextframe.cxx
new file mode 100644
index 0000000000..e1b5cd029c
--- /dev/null
+++ b/sw/source/core/access/acctextframe.cxx
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleRelation.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <accmap.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <hints.hxx>
+#include "acctextframe.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using utl::AccessibleRelationSetHelper;
+using ::com::sun::star::accessibility::XAccessibleContext;
+
+SwAccessibleTextFrame::SwAccessibleTextFrame(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame& rFlyFrame ) :
+ SwAccessibleFrameBase( pInitMap, AccessibleRole::TEXT_FRAME, &rFlyFrame )
+{
+ const SwFlyFrameFormat* pFlyFrameFormat = rFlyFrame.GetFormat();
+ msTitle = pFlyFrameFormat->GetObjTitle();
+
+ msDesc = pFlyFrameFormat->GetObjDescription();
+ if ( msDesc.isEmpty() &&
+ msTitle != GetName() )
+ {
+ msDesc = msTitle;
+ }
+}
+
+SwAccessibleTextFrame::~SwAccessibleTextFrame()
+{
+}
+
+void SwAccessibleTextFrame::Notify(const SfxHint& rHint)
+{
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(GetFrame());
+ const SwFlyFrameFormat* pFlyFrameFormat = pFlyFrame ? pFlyFrame->GetFormat() : nullptr;
+ switch(rHint.GetId())
+ {
+ case SfxHintId::Dying:
+ EndListeningAll();
+ return;
+ default:
+ return;
+ // #i73249#
+ case SfxHintId::SwTitleChanged:
+ {
+ auto rTitleChanged = static_cast<const sw::TitleChanged&>(rHint);
+ msTitle = rTitleChanged.m_sNew;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= rTitleChanged.m_sOld;
+ aEvent.NewValue <<= msTitle;
+ FireAccessibleEvent( aEvent );
+ if(!pFlyFrameFormat || !pFlyFrameFormat->GetObjDescription().isEmpty())
+ break;
+ [[fallthrough]];
+ }
+ case SfxHintId::SwDescriptionChanged:
+ {
+ if(!pFlyFrame)
+ return;
+ const OUString sOldDesc(msDesc);
+ msDesc = pFlyFrameFormat->GetObjDescription();
+ if(msDesc.isEmpty() && msTitle != GetName())
+ msDesc = msTitle;
+ if(msDesc == sOldDesc)
+ return;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= msDesc;
+ FireAccessibleEvent(aEvent);
+ return;
+ }
+ }
+}
+
+// XInterface
+
+css::uno::Any SAL_CALL
+ SwAccessibleTextFrame::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = SwAccessibleContext::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleSelection* >(this)
+ );
+ return aReturn;
+}
+
+void SAL_CALL
+ SwAccessibleTextFrame::acquire()
+ noexcept
+{
+ SwAccessibleContext::acquire ();
+}
+
+void SAL_CALL
+ SwAccessibleTextFrame::release()
+ noexcept
+{
+ SwAccessibleContext::release ();
+}
+
+// XAccessibleSelection
+
+void SAL_CALL SwAccessibleTextFrame::selectAccessibleChild( sal_Int64 )
+{
+ SAL_WARN("sw.a11y", "SwAccessibleTextFrame::selectAccessibleChild - missing implementation");
+}
+
+sal_Bool SAL_CALL SwAccessibleTextFrame::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard g;
+
+ uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
+ uno::Reference<XAccessibleContext> xContext;
+ if( xAcc.is() )
+ xContext = xAcc->getAccessibleContext();
+
+ if( xContext.is() )
+ {
+ const sal_Int16 nRole = xContext->getAccessibleRole();
+ if (nRole == AccessibleRole::PARAGRAPH || nRole == AccessibleRole::BLOCK_QUOTE)
+ {
+ uno::Reference< css::accessibility::XAccessibleText >
+ xText(xAcc, uno::UNO_QUERY);
+ if( xText.is() )
+ {
+ if( xText->getSelectionStart() >= 0 ) return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void SAL_CALL SwAccessibleTextFrame::clearAccessibleSelection( )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::clearAccessibleSelection()> - missing implementation");
+}
+
+void SAL_CALL SwAccessibleTextFrame::selectAllAccessibleChildren( )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::selectAllAccessibleChildren()> - missing implementation");
+}
+
+sal_Int64 SAL_CALL SwAccessibleTextFrame::getSelectedAccessibleChildCount()
+{
+ sal_Int64 nCount = 0;
+ sal_Int64 TotalCount = getAccessibleChildCount();
+ for( sal_Int64 i = 0; i < TotalCount; i++ )
+ if( isAccessibleChildSelected(i) ) nCount++;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible> SAL_CALL SwAccessibleTextFrame::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard g;
+
+ if ( nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+
+ for(sal_Int64 i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
+ if( isAccessibleChildSelected(i1) )
+ {
+ if( i2 == nSelectedChildIndex )
+ return getAccessibleChild( i1 );
+ i2++;
+ }
+ return uno::Reference<XAccessible>();
+}
+
+void SAL_CALL SwAccessibleTextFrame::deselectAccessibleChild( sal_Int64 )
+{
+ SAL_WARN("sw.a11y", "SwAccessibleTextFrame::selectAllAccessibleChildren - missing implementation");
+}
+
+// #i73249#
+OUString SAL_CALL SwAccessibleTextFrame::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if ( !msTitle.isEmpty() )
+ {
+ return msTitle;
+ }
+
+ return SwAccessibleFrameBase::getAccessibleName();
+}
+
+OUString SAL_CALL SwAccessibleTextFrame::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return msDesc;
+
+}
+
+OUString SAL_CALL SwAccessibleTextFrame::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleTextFrameView";
+}
+
+sal_Bool SAL_CALL SwAccessibleTextFrame::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleTextFrame::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextFrameView", sAccessibleServiceName };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleTextFrame::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleRelationSet
+
+SwFlyFrame* SwAccessibleTextFrame::getFlyFrame() const
+{
+ SwFlyFrame* pFlyFrame = nullptr;
+
+ const SwFrame* pFrame = GetFrame();
+ assert(pFrame);
+ if( pFrame->IsFlyFrame() )
+ {
+ pFlyFrame = static_cast<SwFlyFrame*>( const_cast<SwFrame*>( pFrame ) );
+ }
+
+ return pFlyFrame;
+}
+
+AccessibleRelation SwAccessibleTextFrame::makeRelation( sal_Int16 nType, const SwFlyFrame* pFrame )
+{
+ uno::Sequence<uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pFrame ) };
+ return AccessibleRelation( nType, aSequence );
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleTextFrame::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // get the frame, and insert prev/next relations into helper
+
+ rtl::Reference<AccessibleRelationSetHelper> pHelper = new AccessibleRelationSetHelper();
+
+ SwFlyFrame* pFlyFrame = getFlyFrame();
+ assert(pFlyFrame);
+
+ const SwFlyFrame* pPrevFrame = pFlyFrame->GetPrevLink();
+ if( pPrevFrame != nullptr )
+ pHelper->AddRelation( makeRelation(
+ AccessibleRelationType::CONTENT_FLOWS_FROM, pPrevFrame ) );
+
+ const SwFlyFrame* pNextFrame = pFlyFrame->GetNextLink();
+ if( pNextFrame != nullptr )
+ pHelper->AddRelation( makeRelation(
+ AccessibleRelationType::CONTENT_FLOWS_TO, pNextFrame ) );
+
+ return pHelper;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctextframe.hxx b/sw/source/core/access/acctextframe.hxx
new file mode 100644
index 0000000000..036e1e85d3
--- /dev/null
+++ b/sw/source/core/access/acctextframe.hxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "accframebase.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+
+class SwFlyFrame;
+
+class SwAccessibleTextFrame : public SwAccessibleFrameBase,
+ public css::accessibility::XAccessibleSelection
+{
+private:
+ // #i73249#
+ OUString msTitle;
+ OUString msDesc;
+
+protected:
+ virtual ~SwAccessibleTextFrame() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTextFrame(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame& rFlyFrame);
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int64 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int64 nChildIndex ) override;
+
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int64 nSelectedChildIndex ) override;
+
+ // XAccessibleContext
+
+ // #i73249# - Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleContext::getAccessibleRelationSet
+
+ // text frame may have accessible relations to their
+ // predecessor/successor frames
+
+private:
+ // helper methods for getAccessibleRelationSet:
+ SwFlyFrame* getFlyFrame() const;
+
+ css::accessibility::AccessibleRelation makeRelation(
+ sal_Int16 nType, const SwFlyFrame* pFrame );
+
+public:
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL getAccessibleRelationSet() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/parachangetrackinginfo.cxx b/sw/source/core/access/parachangetrackinginfo.cxx
new file mode 100644
index 0000000000..b20d9702e7
--- /dev/null
+++ b/sw/source/core/access/parachangetrackinginfo.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "parachangetrackinginfo.hxx"
+
+#include <wrong.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <osl/diagnose.h>
+
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+
+#include <algorithm>
+
+namespace {
+ void initChangeTrackTextMarkupLists( const SwTextFrame& rTextFrame,
+ std::unique_ptr<SwWrongList>& opChangeTrackInsertionTextMarkupList,
+ std::unique_ptr<SwWrongList>& opChangeTrackDeletionTextMarkupList,
+ std::unique_ptr<SwWrongList>& opChangeTrackFormatChangeTextMarkupList )
+ {
+ opChangeTrackInsertionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+ opChangeTrackDeletionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+ opChangeTrackFormatChangeTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+
+ if (!rTextFrame.GetTextNodeFirst())
+ {
+ OSL_FAIL( "<initChangeTrackTextMarkupLists(..) - missing <SwTextNode> instance!" );
+ return;
+ }
+ // sw_redlinehide: the first node is sufficient - there are only
+ // multiple ones in Hide case and the code below returns early then
+ const SwTextNode& rTextNode(*(rTextFrame.GetTextNodeFirst()));
+
+ const IDocumentRedlineAccess& rIDocChangeTrack( rTextNode.getIDocumentRedlineAccess() );
+
+ if (!IDocumentRedlineAccess::IsShowChanges(rIDocChangeTrack.GetRedlineFlags())
+ || rTextFrame.getRootFrame()->IsHideRedlines()
+ || rIDocChangeTrack.GetRedlineTable().empty())
+ {
+ // nothing to do --> empty change track text markup lists.
+ return;
+ }
+
+ const SwRedlineTable::size_type nIdxOfFirstRedlineForTextNode =
+ rIDocChangeTrack.GetRedlinePos( rTextNode, RedlineType::Any );
+ if ( nIdxOfFirstRedlineForTextNode == SwRedlineTable::npos )
+ {
+ // nothing to do --> empty change track text markup lists.
+ return;
+ }
+
+ // sw_redlinehide: rely on the Hide early return above & cast
+ // TextFrameIndex to SwContentIndex directly
+ const sal_Int32 nTextFrameTextStartPos = rTextFrame.IsFollow()
+ ? sal_Int32(rTextFrame.GetOffset())
+ : 0;
+ const sal_Int32 nTextFrameTextEndPos = rTextFrame.HasFollow()
+ ? sal_Int32(rTextFrame.GetFollow()->GetOffset())
+ : rTextFrame.GetText().getLength();
+
+ // iteration over the redlines which overlap with the text node.
+ const SwRedlineTable& rRedlineTable = rIDocChangeTrack.GetRedlineTable();
+ const SwRedlineTable::size_type nRedlineCount( rRedlineTable.size() );
+ for ( SwRedlineTable::size_type nActRedline = nIdxOfFirstRedlineForTextNode;
+ nActRedline < nRedlineCount;
+ ++nActRedline)
+ {
+ const SwRangeRedline* pActRedline = rRedlineTable[ nActRedline ];
+ if ( pActRedline->Start()->GetNode() > rTextNode )
+ {
+ break;
+ }
+
+ sal_Int32 nTextNodeChangeTrackStart(COMPLETE_STRING);
+ sal_Int32 nTextNodeChangeTrackEnd(COMPLETE_STRING);
+ pActRedline->CalcStartEnd( rTextNode.GetIndex(),
+ nTextNodeChangeTrackStart,
+ nTextNodeChangeTrackEnd );
+ if ( nTextNodeChangeTrackStart > nTextFrameTextEndPos ||
+ nTextNodeChangeTrackEnd < nTextFrameTextStartPos )
+ {
+ // Consider only redlines which overlap with the text frame's text.
+ continue;
+ }
+
+ SwWrongList* pMarkupList( nullptr );
+ switch ( pActRedline->GetType() )
+ {
+ case RedlineType::Insert:
+ {
+ pMarkupList = opChangeTrackInsertionTextMarkupList.get();
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ pMarkupList = opChangeTrackDeletionTextMarkupList.get();
+ }
+ break;
+ case RedlineType::Format:
+ {
+ pMarkupList = opChangeTrackFormatChangeTextMarkupList.get();
+ }
+ break;
+ default:
+ {
+ // other types are not considered
+ }
+ }
+ if ( pMarkupList )
+ {
+ const sal_Int32 nTextFrameChangeTrackStart =
+ std::max(nTextNodeChangeTrackStart, nTextFrameTextStartPos);
+
+ const sal_Int32 nTextFrameChangeTrackEnd =
+ std::min(nTextNodeChangeTrackEnd, nTextFrameTextEndPos);
+
+ pMarkupList->Insert( OUString(), nullptr,
+ nTextFrameChangeTrackStart,
+ nTextFrameChangeTrackEnd - nTextFrameChangeTrackStart,
+ pMarkupList->Count() );
+ }
+ } // eof iteration over the redlines which overlap with the text node
+ }
+} // eof anonymous namespace
+
+SwParaChangeTrackingInfo::SwParaChangeTrackingInfo( const SwTextFrame& rTextFrame )
+ : mrTextFrame( rTextFrame )
+{
+}
+
+SwParaChangeTrackingInfo::~SwParaChangeTrackingInfo()
+{
+ reset();
+}
+
+void SwParaChangeTrackingInfo::reset()
+{
+ mpChangeTrackInsertionTextMarkupList.reset();
+ mpChangeTrackDeletionTextMarkupList.reset();
+ mpChangeTrackFormatChangeTextMarkupList.reset();
+}
+
+const SwWrongList* SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList( const sal_Int32 nTextMarkupType )
+{
+ SwWrongList* pChangeTrackingTextMarkupList = nullptr;
+
+ if ( mpChangeTrackInsertionTextMarkupList == nullptr )
+ {
+ OSL_ENSURE( mpChangeTrackDeletionTextMarkupList == nullptr,
+ "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..) - <mpChangeTrackDeletionTextMarkupList> expected to be NULL." );
+ OSL_ENSURE( mpChangeTrackFormatChangeTextMarkupList == nullptr,
+ "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..) - <mpChangeTrackFormatChangeTextMarkupList> expected to be NULL." );
+ initChangeTrackTextMarkupLists( mrTextFrame,
+ mpChangeTrackInsertionTextMarkupList,
+ mpChangeTrackDeletionTextMarkupList,
+ mpChangeTrackFormatChangeTextMarkupList );
+ }
+
+ switch ( nTextMarkupType )
+ {
+ case css::text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackInsertionTextMarkupList.get();
+ }
+ break;
+ case css::text::TextMarkupType::TRACK_CHANGE_DELETION:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackDeletionTextMarkupList.get();
+ }
+ break;
+ case css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackFormatChangeTextMarkupList.get();
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..)> - misusage - unexpected text markup type for change tracking." );
+ }
+ }
+
+ return pChangeTrackingTextMarkupList;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/parachangetrackinginfo.hxx b/sw/source/core/access/parachangetrackinginfo.hxx
new file mode 100644
index 0000000000..30f020125b
--- /dev/null
+++ b/sw/source/core/access/parachangetrackinginfo.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_PARACHANGETRACKINGINFO_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_PARACHANGETRACKINGINFO_HXX
+
+#include <sal/types.h>
+#include <memory>
+
+class SwTextFrame;
+class SwWrongList;
+
+class SwParaChangeTrackingInfo
+{
+ public:
+ explicit SwParaChangeTrackingInfo( const SwTextFrame& rTextFrame );
+ ~SwParaChangeTrackingInfo();
+
+ void reset();
+
+ const SwWrongList* getChangeTrackingTextMarkupList( const sal_Int32 nTextMarkupType );
+
+ private:
+ SwParaChangeTrackingInfo( const SwParaChangeTrackingInfo& ) = delete;
+ SwParaChangeTrackingInfo& operator=( const SwParaChangeTrackingInfo& ) = delete;
+
+ const SwTextFrame& mrTextFrame;
+
+ std::unique_ptr<SwWrongList> mpChangeTrackInsertionTextMarkupList;
+ std::unique_ptr<SwWrongList> mpChangeTrackDeletionTextMarkupList;
+ std::unique_ptr<SwWrongList> mpChangeTrackFormatChangeTextMarkupList;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/textmarkuphelper.cxx b/sw/source/core/access/textmarkuphelper.cxx
new file mode 100644
index 0000000000..ae221f4998
--- /dev/null
+++ b/sw/source/core/access/textmarkuphelper.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "textmarkuphelper.hxx"
+#include "accportions.hxx"
+
+#include <vector>
+
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <com/sun/star/accessibility/TextSegment.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <ndtxt.hxx>
+#include <wrong.hxx>
+
+using namespace com::sun::star;
+
+// helper functions
+namespace {
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ SwWrongList const* (SwTextNode::*
+ getTextMarkupFunc(const sal_Int32 nTextMarkupType))() const
+ {
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::SPELLCHECK:
+ {
+ return &SwTextNode::GetWrong;
+ }
+ break;
+ case text::TextMarkupType::PROOFREADING:
+ {
+ // support not implemented yet
+ return nullptr;
+ }
+ break;
+ case text::TextMarkupType::SMARTTAG:
+ {
+ // support not implemented yet
+ return nullptr;
+ }
+ break;
+ default:
+ {
+ throw lang::IllegalArgumentException();
+ }
+ }
+ }
+}
+
+// implementation of class <SwTextMarkupoHelper>
+SwTextMarkupHelper::SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwTextFrame& rTextFrame)
+ : mrPortionData( rPortionData )
+ , m_pTextFrame(&rTextFrame)
+ , mpTextMarkupList( nullptr )
+{
+}
+
+// #i108125#
+SwTextMarkupHelper::SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwWrongList& rTextMarkupList )
+ : mrPortionData( rPortionData )
+ , m_pTextFrame( nullptr )
+ , mpTextMarkupList( &rTextMarkupList )
+{
+}
+
+
+std::unique_ptr<sw::WrongListIteratorCounter> SwTextMarkupHelper::getIterator(sal_Int32 nTextMarkupType)
+{
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter;
+ if (mpTextMarkupList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*mpTextMarkupList));
+ }
+ else
+ {
+ assert(m_pTextFrame);
+ SwWrongList const* (SwTextNode::*const pGetWrongList)() const = getTextMarkupFunc(nTextMarkupType);
+ if (pGetWrongList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*m_pTextFrame, pGetWrongList));
+ }
+ }
+
+ return pIter;
+}
+
+
+sal_Int32 SwTextMarkupHelper::getTextMarkupCount( const sal_Int32 nTextMarkupType )
+{
+ sal_Int32 nTextMarkupCount( 0 );
+
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter = getIterator(nTextMarkupType);
+ // iterator may handle all items in the underlying text node in the model, which may be more
+ // than what is in the portion data (e.g. if a paragraph is split across multiple pages),
+ // only take into account those that are in the portion data
+ for (sal_uInt16 i = 0; i < pIter->GetElementCount(); i++)
+ {
+ std::optional<std::pair<TextFrameIndex, TextFrameIndex>> oIndices = pIter->GetElementAt(i);
+ if (oIndices && mrPortionData.IsValidCorePosition(oIndices->first) && mrPortionData.IsValidCorePosition(oIndices->second))
+ nTextMarkupCount++;
+ }
+
+ return nTextMarkupCount;
+}
+
+css::accessibility::TextSegment
+ SwTextMarkupHelper::getTextMarkup( const sal_Int32 nTextMarkupIndex,
+ const sal_Int32 nTextMarkupType )
+{
+ if ( nTextMarkupIndex >= getTextMarkupCount( nTextMarkupType ) ||
+ nTextMarkupIndex < 0 )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ css::accessibility::TextSegment aTextMarkupSegment;
+ aTextMarkupSegment.SegmentStart = -1;
+ aTextMarkupSegment.SegmentEnd = -1;
+
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter = getIterator(nTextMarkupType);
+ if (pIter)
+ {
+ std::optional<std::pair<TextFrameIndex, TextFrameIndex>> oElement;
+ const sal_uInt16 nIterElementCount = pIter->GetElementCount();
+ sal_Int32 nIndexInPortion = 0;
+ sal_uInt16 nIterIndex = 0;
+ while (!oElement && nIterIndex < nIterElementCount)
+ {
+ // iterator may handle all items in the underlying text node in the model, which may be more
+ // than what is in the portion data (e.g. if a paragraph is split across multiple pages),
+ // only take into account those that are in the portion data
+ std::optional<std::pair<TextFrameIndex, TextFrameIndex>> oIndices = pIter->GetElementAt(nIterIndex);
+ if (oIndices && mrPortionData.IsValidCorePosition(oIndices->first) && mrPortionData.IsValidCorePosition(oIndices->second))
+ {
+ if (nIndexInPortion == nTextMarkupIndex)
+ oElement = oIndices;
+
+ nIndexInPortion++;
+ }
+
+ nIterIndex++;
+ }
+
+ if (oElement)
+ {
+ const OUString& rText = mrPortionData.GetAccessibleString();
+ const sal_Int32 nStartPos =
+ mrPortionData.GetAccessiblePosition(oElement->first);
+ const sal_Int32 nEndPos =
+ mrPortionData.GetAccessiblePosition(oElement->second);
+ aTextMarkupSegment.SegmentText = rText.copy( nStartPos, nEndPos - nStartPos );
+ aTextMarkupSegment.SegmentStart = nStartPos;
+ aTextMarkupSegment.SegmentEnd = nEndPos;
+ }
+ else
+ {
+ OSL_FAIL( "<SwTextMarkupHelper::getTextMarkup(..)> - missing <SwWrongArea> instance" );
+ }
+ }
+
+ return aTextMarkupSegment;
+}
+
+css::uno::Sequence< css::accessibility::TextSegment >
+ SwTextMarkupHelper::getTextMarkupAtIndex( const sal_Int32 nCharIndex,
+ const sal_Int32 nTextMarkupType )
+{
+ // assumption:
+ // value of <nCharIndex> is in range [0..length of accessible text)
+
+ const TextFrameIndex nCoreCharIndex = mrPortionData.GetCoreViewPosition(nCharIndex);
+ // Handling of portions with core length == 0 at the beginning of the
+ // paragraph - e.g. numbering portion.
+ if ( mrPortionData.GetAccessiblePosition( nCoreCharIndex ) > nCharIndex )
+ {
+ return uno::Sequence< css::accessibility::TextSegment >();
+ }
+
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter = getIterator(nTextMarkupType);
+ std::vector< css::accessibility::TextSegment > aTmpTextMarkups;
+ if (pIter)
+ {
+ const OUString& rText = mrPortionData.GetAccessibleString();
+ sal_uInt16 count(pIter->GetElementCount());
+ for (sal_uInt16 i = 0; i < count; ++i)
+ {
+ auto const oElement(pIter->GetElementAt(i));
+ if (oElement &&
+ oElement->first <= nCoreCharIndex &&
+ nCoreCharIndex < oElement->second)
+ {
+ const sal_Int32 nStartPos =
+ mrPortionData.GetAccessiblePosition(oElement->first);
+ const sal_Int32 nEndPos =
+ mrPortionData.GetAccessiblePosition(oElement->second);
+ css::accessibility::TextSegment aTextMarkupSegment;
+ aTextMarkupSegment.SegmentText = rText.copy( nStartPos, nEndPos - nStartPos );
+ aTextMarkupSegment.SegmentStart = nStartPos;
+ aTextMarkupSegment.SegmentEnd = nEndPos;
+ aTmpTextMarkups.push_back( aTextMarkupSegment );
+ }
+ }
+ }
+
+ return comphelper::containerToSequence(aTmpTextMarkups );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/textmarkuphelper.hxx b/sw/source/core/access/textmarkuphelper.hxx
new file mode 100644
index 0000000000..daccc0eef4
--- /dev/null
+++ b/sw/source/core/access/textmarkuphelper.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_TEXTMARKUPHELPER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_TEXTMARKUPHELPER_HXX
+
+#include <sal/types.h>
+#include <com/sun/star/uno/Sequence.h>
+
+namespace com::sun::star::accessibility {
+ struct TextSegment;
+}
+
+class SwAccessiblePortionData;
+class SwTextFrame;
+class SwWrongList; // #i108125#
+
+namespace sw {
+ class WrongListIteratorCounter;
+}
+
+class SwTextMarkupHelper
+{
+ public:
+ SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwTextFrame& rTextFrame);
+ SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwWrongList& rTextMarkupList ); // #i108125#
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getTextMarkupCount( const sal_Int32 nTextMarkupType );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::accessibility::TextSegment getTextMarkup(
+ const sal_Int32 nTextMarkupIndex,
+ const sal_Int32 nTextMarkupType );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::accessibility::TextSegment >
+ getTextMarkupAtIndex( const sal_Int32 nCharIndex,
+ const sal_Int32 nTextMarkupType );
+
+ private:
+ SwTextMarkupHelper( const SwTextMarkupHelper& ) = delete;
+ SwTextMarkupHelper& operator=( const SwTextMarkupHelper& ) = delete;
+
+ std::unique_ptr<sw::WrongListIteratorCounter> getIterator(sal_Int32 nTextMarkupType);
+
+ const SwAccessiblePortionData& mrPortionData;
+
+ SwTextFrame const* m_pTextFrame;
+ const SwWrongList* mpTextMarkupList;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */