summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/Accessibility
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/ui/Accessibility')
-rw-r--r--sc/source/ui/Accessibility/AccessibilityHints.cxx62
-rw-r--r--sc/source/ui/Accessibility/AccessibleCell.cxx615
-rw-r--r--sc/source/ui/Accessibility/AccessibleCellBase.cxx587
-rw-r--r--sc/source/ui/Accessibility/AccessibleContextBase.cxx493
-rw-r--r--sc/source/ui/Accessibility/AccessibleCsvControl.cxx1405
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocument.cxx2222
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocumentBase.cxx36
-rw-r--r--sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx1569
-rw-r--r--sc/source/ui/Accessibility/AccessibleEditObject.cxx600
-rw-r--r--sc/source/ui/Accessibility/AccessiblePageHeader.cxx372
-rw-r--r--sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx273
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewCell.cxx272
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx403
-rw-r--r--sc/source/ui/Accessibility/AccessiblePreviewTable.cxx640
-rw-r--r--sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx1669
-rw-r--r--sc/source/ui/Accessibility/AccessibleTableBase.cxx466
-rw-r--r--sc/source/ui/Accessibility/AccessibleText.cxx1386
-rw-r--r--sc/source/ui/Accessibility/DrawModelBroadcaster.cxx107
18 files changed, 13177 insertions, 0 deletions
diff --git a/sc/source/ui/Accessibility/AccessibilityHints.cxx b/sc/source/ui/Accessibility/AccessibilityHints.cxx
new file mode 100644
index 0000000000..733311f721
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibilityHints.cxx
@@ -0,0 +1,62 @@
+/* -*- 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 <AccessibilityHints.hxx>
+
+using namespace ::com::sun::star;
+
+// ScAccWinFocusLostHint - the current window lost its focus (to another application, view or document)
+
+ScAccWinFocusLostHint::~ScAccWinFocusLostHint()
+{
+}
+
+// ScAccWinFocusGotHint - the window got the focus (from another application, view or document)
+
+ScAccWinFocusGotHint::~ScAccWinFocusGotHint()
+{
+}
+
+// ScAccGridWinFocusLostHint - the current grid window lost its focus (to another application, view or document)
+
+ScAccGridWinFocusLostHint::ScAccGridWinFocusLostHint(ScSplitPos eOld )
+ :
+ ScAccWinFocusLostHint(),
+ eOldGridWin(eOld)
+{
+}
+
+ScAccGridWinFocusLostHint::~ScAccGridWinFocusLostHint()
+{
+}
+
+// ScAccGridWinFocusGotHint - the grid window got the focus (from another application, view or document)
+
+ScAccGridWinFocusGotHint::ScAccGridWinFocusGotHint(ScSplitPos eNew )
+ :
+ ScAccWinFocusGotHint(),
+ eNewGridWin(eNew)
+{
+}
+
+ScAccGridWinFocusGotHint::~ScAccGridWinFocusGotHint()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCell.cxx b/sc/source/ui/Accessibility/AccessibleCell.cxx
new file mode 100644
index 0000000000..3ac2fdba30
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCell.cxx
@@ -0,0 +1,615 @@
+/* -*- 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 <string_view>
+
+#include <sal/config.h>
+
+#include <AccessibleCell.hxx>
+#include <scitems.hxx>
+#include <AccessibleText.hxx>
+#include <AccessibleDocument.hxx>
+#include <tabvwsh.hxx>
+#include <comphelper/sequence.hxx>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <editsrc.hxx>
+#include <dociter.hxx>
+#include <markdata.hxx>
+#include <cellvalue.hxx>
+#include <formulaiter.hxx>
+#include <validat.hxx>
+
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <editeng/brushitem.hxx>
+#include <vcl/svapp.hxx>
+
+#include <AccessibleSpreadsheet.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+rtl::Reference<ScAccessibleCell> ScAccessibleCell::create(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc)
+{
+ rtl::Reference<ScAccessibleCell> x(new ScAccessibleCell(
+ rxParent, pViewShell, rCellAddress, nIndex, eSplitPos, pAccDoc));
+ x->Init();
+ return x;
+}
+
+ScAccessibleCell::ScAccessibleCell(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex,
+ ScSplitPos eSplitPos,
+ ScAccessibleDocument* pAccDoc)
+ :
+ ScAccessibleCellBase(rxParent, GetDocument(pViewShell), rCellAddress, nIndex),
+ ::accessibility::AccessibleStaticTextBase(CreateEditSource(pViewShell, rCellAddress, eSplitPos)),
+ mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ meSplitPos(eSplitPos)
+{
+ if (pViewShell)
+ pViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessibleCell::~ScAccessibleCell()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void ScAccessibleCell::Init()
+{
+ ScAccessibleCellBase::Init();
+
+ SetEventSource(this);
+}
+
+void SAL_CALL ScAccessibleCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ // dispose in AccessibleStaticTextBase
+ Dispose();
+
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpAccDoc = nullptr;
+
+ ScAccessibleCellBase::disposing();
+}
+
+ //===== XInterface =====================================================
+
+IMPLEMENT_FORWARD_XINTERFACE3( ScAccessibleCell, ScAccessibleCellBase, AccessibleStaticTextBase, ScAccessibleCellAttributeImpl )
+
+ //===== XTypeProvider ===================================================
+
+css::uno::Sequence< css::uno::Type > SAL_CALL ScAccessibleCell::getTypes()
+{
+ return ::comphelper::concatSequences(
+ ScAccessibleCellBase::getTypes(),
+ AccessibleStaticTextBase::getTypes(),
+ ScAccessibleCellAttributeImpl::getTypes()
+ );
+}
+IMPLEMENT_GET_IMPLEMENTATION_ID( ScAccessibleCell )
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleCell::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ return AccessibleStaticTextBase::getAccessibleAtPoint(rPoint);
+}
+
+void SAL_CALL ScAccessibleCell::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is() && mpViewShell)
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ xAccessibleComponent->grabFocus();
+ mpViewShell->SetCursor(maCellAddress.Col(), maCellAddress.Row());
+ }
+ }
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleCell::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return aCellRect;
+}
+
+tools::Rectangle ScAccessibleCell::GetBoundingBox() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ tools::Long nSizeX, nSizeY;
+ mpViewShell->GetViewData().GetMergeSizePixel(
+ maCellAddress.Col(), maCellAddress.Row(), nSizeX, nSizeY);
+ aCellRect.SetSize(Size(nSizeX, nSizeY));
+ aCellRect.SetPos(mpViewShell->GetViewData().GetScrPos(maCellAddress.Col(), maCellAddress.Row(), meSplitPos, true));
+
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ {
+ tools::Rectangle aRect(pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow()));
+ aRect.Move(-aRect.Left(), -aRect.Top());
+ aCellRect = aRect.Intersection(aCellRect);
+ }
+
+ /* #i19430# Gnopernicus reads text partly if it sticks out of the cell
+ boundaries. This leads to wrong results in cases where the cell
+ text is rotated, because rotation is not taken into account when
+ calculating the visible part of the text. In these cases we will
+ simply expand the cell size to the width of the unrotated text. */
+ if (mpDoc)
+ {
+ const ScRotateValueItem* pItem = mpDoc->GetAttr( maCellAddress, ATTR_ROTATE_VALUE );
+ if( pItem && (pItem->GetValue() != 0_deg100) )
+ {
+ tools::Rectangle aParaRect = GetParagraphBoundingBox();
+ if( !aParaRect.IsEmpty() && (aCellRect.GetWidth() < aParaRect.GetWidth()) )
+ aCellRect.SetSize( Size( aParaRect.GetWidth(), aCellRect.GetHeight() ) );
+ }
+ }
+ }
+ if (aCellRect.IsEmpty())
+ aCellRect.SetPos(Point(-1, -1));
+ return aCellRect;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessibleCell::getAccessibleChildCount()
+{
+ return AccessibleStaticTextBase::getAccessibleChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ return AccessibleStaticTextBase::getAccessibleChild(nIndex);
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ if (IsFocused())
+ nStateSet |= AccessibleStateType::FOCUSED;
+
+ if (IsFormulaMode())
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ return nStateSet;
+ }
+ if (IsEditable(nParentStates))
+ {
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::RESIZABLE;
+ }
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleCell::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if (mpAccDoc)
+ pRelationSet = mpAccDoc->GetRelationSet(&maCellAddress);
+ if (!pRelationSet)
+ pRelationSet = new utl::AccessibleRelationSetHelper();
+ FillDependents(pRelationSet.get());
+ FillPrecedents(pRelationSet.get());
+ return pRelationSet;
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleCell::getImplementationName()
+{
+ return "ScAccessibleCell";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.sheet.AccessibleCell" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+ //==== internal =========================================================
+
+bool ScAccessibleCell::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessibleCell::IsEditable(sal_Int64 nParentStates)
+{
+ bool bEditable(true);
+ if ( !(nParentStates & AccessibleStateType::EDITABLE) &&
+ mpDoc)
+ {
+ // here I have to test whether the protection of the table should influence this cell.
+ const ScProtectionAttr* pItem = mpDoc->GetAttr(maCellAddress, ATTR_PROTECTION);
+ if (pItem)
+ bEditable = !pItem->GetProtection();
+ }
+ return bEditable;
+}
+
+bool ScAccessibleCell::IsOpaque() const
+{
+ // test whether there is a background color
+ bool bOpaque(true);
+ if (mpDoc)
+ {
+ const SvxBrushItem* pItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND);
+ if (pItem)
+ bOpaque = pItem->GetColor() != COL_TRANSPARENT;
+ }
+ return bOpaque;
+}
+
+bool ScAccessibleCell::IsFocused() const
+{
+ if (mpViewShell && mpViewShell->GetViewData().GetCurPos() == maCellAddress)
+ return mpViewShell->GetActiveWin()->HasFocus();
+
+ return false;
+}
+
+bool ScAccessibleCell::IsSelected()
+{
+ if (IsFormulaMode())
+ {
+ const ScAccessibleSpreadsheet *pSheet =static_cast<const ScAccessibleSpreadsheet*>(mxParent.get());
+ if (pSheet)
+ {
+ return pSheet->IsScAddrFormulaSel(maCellAddress);
+ }
+ return false;
+ }
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsCellMarked(maCellAddress.Col(), maCellAddress.Row());
+ }
+ return bResult;
+}
+
+ScDocument* ScAccessibleCell::GetDocument(ScTabViewShell* pViewShell)
+{
+ ScDocument* pDoc = nullptr;
+ if (pViewShell)
+ pDoc = &pViewShell->GetViewData().GetDocument();
+ return pDoc;
+}
+
+::std::unique_ptr< SvxEditSource > ScAccessibleCell::CreateEditSource(ScTabViewShell* pViewShell, ScAddress aCell, ScSplitPos eSplitPos)
+{
+ if (IsFormulaMode())
+ {
+ return ::std::unique_ptr< SvxEditSource >();
+ }
+ ::std::unique_ptr< SvxEditSource > pEditSource (new ScAccessibilityEditSource(std::make_unique<ScAccessibleCellTextData>(pViewShell, aCell, eSplitPos, this)));
+
+ return pEditSource;
+}
+
+void ScAccessibleCell::FillDependents(utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ if (!mpDoc)
+ return;
+
+ ScRange aRange(0, 0, maCellAddress.Tab(), mpDoc->MaxCol(), mpDoc->MaxRow(), maCellAddress.Tab());
+ ScCellIterator aCellIter(*mpDoc, aRange);
+
+ for (bool bHasCell = aCellIter.first(); bHasCell; bHasCell = aCellIter.next())
+ {
+ if (aCellIter.getType() == CELLTYPE_FORMULA)
+ {
+ bool bFound = false;
+ ScDetectiveRefIter aIter(*mpDoc, aCellIter.getFormulaCell());
+ ScRange aRef;
+ while ( !bFound && aIter.GetNextRef( aRef ) )
+ {
+ if (aRef.Contains(maCellAddress))
+ bFound = true;
+ }
+ if (bFound)
+ AddRelation(aCellIter.GetPos(), AccessibleRelationType::CONTROLLER_FOR, pRelationSet);
+ }
+ }
+}
+
+void ScAccessibleCell::FillPrecedents(utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ if (!mpDoc)
+ return;
+
+ ScRefCellValue aCell(*mpDoc, maCellAddress);
+ if (aCell.getType() == CELLTYPE_FORMULA)
+ {
+ ScFormulaCell* pCell = aCell.getFormula();
+ ScDetectiveRefIter aIter(*mpDoc, pCell);
+ ScRange aRef;
+ while ( aIter.GetNextRef( aRef ) )
+ {
+ AddRelation( aRef, AccessibleRelationType::CONTROLLED_BY, pRelationSet);
+ }
+ }
+}
+
+void ScAccessibleCell::AddRelation(const ScAddress& rCell,
+ const sal_uInt16 aRelationType,
+ utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ AddRelation(ScRange(rCell, rCell), aRelationType, pRelationSet);
+}
+
+void ScAccessibleCell::AddRelation(const ScRange& rRange,
+ const sal_uInt16 aRelationType,
+ utl::AccessibleRelationSetHelper* pRelationSet)
+{
+ uno::Reference < XAccessibleTable > xTable ( getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY );
+ if (!xTable.is())
+ return;
+
+ const sal_uInt32 nCount(static_cast<sal_uInt32>(rRange.aEnd.Col() -
+ rRange.aStart.Col() + 1) * (rRange.aEnd.Row() -
+ rRange.aStart.Row() + 1));
+
+ // tdf#157299 avoid handling a large amount of cells for performance reasons
+ if (nCount > 1000)
+ {
+ SAL_WARN("sc", "ScAccessibleCell::AddRelation: Not setting relations "
+ "for cell range with more than 1000 cells for performance reasons.");
+ return;
+ }
+
+ uno::Sequence < uno::Reference < uno::XInterface > > aTargetSet( nCount );
+ uno::Reference < uno::XInterface >* pTargetSet = aTargetSet.getArray();
+ sal_uInt32 nPos(0);
+ for (sal_uInt32 nRow = rRange.aStart.Row(); nRow <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Row()); ++nRow)
+ {
+ for (sal_uInt32 nCol = rRange.aStart.Col(); nCol <= sal::static_int_cast<sal_uInt32>(rRange.aEnd.Col()); ++nCol)
+ {
+ pTargetSet[nPos] = xTable->getAccessibleCellAt(nRow, nCol);
+ ++nPos;
+ }
+ }
+ OSL_ENSURE(nCount == nPos, "something went wrong");
+ AccessibleRelation aRelation;
+ aRelation.RelationType = aRelationType;
+ aRelation.TargetSet = aTargetSet;
+ pRelationSet->AddRelation(aRelation);
+}
+
+static OUString ReplaceFourChar(const OUString& oldOUString)
+{
+ return oldOUString.replaceAll(u"\\", u"\\\\")
+ .replaceAll(u";", u"\\;")
+ .replaceAll(u"=", u"\\=")
+ .replaceAll(u",", u"\\,")
+ .replaceAll(u":", u"\\:");
+}
+
+uno::Any SAL_CALL ScAccessibleCell::getExtendedAttributes()
+{
+ SolarMutexGuard aGuard;
+
+ // report row and column index text via attributes as specified in ARIA which map
+ // to attributes of the same name for AT-SPI2, IAccessible2, UIA
+ // https://www.w3.org/TR/core-aam-1.2/#ariaRowIndexText
+ // https://www.w3.org/TR/core-aam-1.2/#ariaColIndexText
+ const OUString sRowIndexText = maCellAddress.Format(ScRefFlags::ROW_VALID);
+ const OUString sColIndexText = maCellAddress.Format(ScRefFlags::COL_VALID);
+ OUString sAttributes = "rowindextext:" + sRowIndexText + ";colindextext:" + sColIndexText + ";";
+
+ if (mpViewShell)
+ {
+ OUString strFor = mpViewShell->GetFormula(maCellAddress) ;
+ if (!strFor.isEmpty())
+ {
+ strFor = strFor.copy(1);
+ strFor = ReplaceFourChar(strFor);
+ }
+ strFor = "Formula:" + strFor +
+ ";Note:" +
+ ReplaceFourChar(GetAllDisplayNote()) + ";" +
+ getShadowAttrs() + //the string returned contains the spliter ";"
+ getBorderAttrs();//the string returned contains the spliter ";"
+ //end of cell attributes
+ if( mpDoc )
+ {
+ strFor += "isdropdown:";
+ if( IsDropdown() )
+ strFor += "true";
+ else
+ strFor += "false";
+ strFor += ";";
+ }
+ sAttributes += strFor ;
+ }
+
+ return uno::Any(sAttributes);
+}
+
+// cell has its own ParaIndent property, so when calling character attributes on cell, the ParaIndent should replace the ParaLeftMargin if its value is not zero.
+uno::Sequence< beans::PropertyValue > SAL_CALL ScAccessibleCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< beans::PropertyValue > aAttribs = AccessibleStaticTextBase::getCharacterAttributes( nIndex, aRequestedAttributes );
+
+ sal_uInt16 nParaIndent = mpDoc->GetAttr( maCellAddress, ATTR_INDENT )->GetValue();
+ if (nParaIndent > 0)
+ {
+ auto [begin, end] = asNonConstRange(aAttribs);
+ auto pAttrib = std::find_if(begin, end,
+ [](const beans::PropertyValue& rAttrib) { return "ParaLeftMargin" == rAttrib.Name; });
+ if (pAttrib != end)
+ pAttrib->Value <<= nParaIndent;
+ }
+ return aAttribs;
+}
+
+bool ScAccessibleCell::IsFormulaMode()
+{
+ ScAccessibleSpreadsheet* pSheet = static_cast<ScAccessibleSpreadsheet*>(mxParent.get());
+ if (pSheet)
+ {
+ return pSheet->IsFormulaMode();
+ }
+ return false;
+}
+
+bool ScAccessibleCell::IsDropdown() const
+{
+ sal_uInt16 nPosX = maCellAddress.Col();
+ sal_uInt16 nPosY = sal_uInt16(maCellAddress.Row());
+ sal_uInt16 nTab = maCellAddress.Tab();
+ sal_uInt32 nValidation = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = mpDoc->GetValidationEntry( nValidation );
+ if( pData && pData->HasSelectionList() )
+ return true;
+ }
+ const ScMergeFlagAttr* pAttr = mpDoc->GetAttr( nPosX, nPosY, nTab, ATTR_MERGE_FLAG );
+ if( pAttr->HasAutoFilter() )
+ {
+ return true;
+ }
+ else
+ {
+ sal_uInt16 nTabCount = mpDoc->GetTableCount();
+ if ( nTab+1<nTabCount && mpDoc->IsScenario(nTab+1) && !mpDoc->IsScenario(nTab) )
+ {
+ SCTAB i;
+ ScMarkData aMarks(mpDoc->GetSheetLimits());
+ for (i=nTab+1; i<nTabCount && mpDoc->IsScenario(i); i++)
+ mpDoc->MarkScenario( i, nTab, aMarks, false, ScScenarioFlags::ShowFrame );
+ ScRangeList aRanges;
+ aMarks.FillRangeListWithMarks( &aRanges, false );
+ bool bHasScenario;
+ SCTAB nRangeCount = aRanges.size();
+ for (i=0; i<nRangeCount; i++)
+ {
+ ScRange aRange = aRanges[i];
+ mpDoc->ExtendTotalMerge( aRange );
+ bool bTextBelow = ( aRange.aStart.Row() == 0 );
+ // MT IA2: Not used: sal_Bool bIsInScen = sal_False;
+ if ( bTextBelow )
+ {
+ bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aEnd.Row() == nPosY-1);
+ }
+ else
+ {
+ bHasScenario = (aRange.aStart.Col() == nPosX && aRange.aStart.Row() == nPosY+1);
+ }
+ if( bHasScenario ) return true;
+ }
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCellBase.cxx b/sc/source/ui/Accessibility/AccessibleCellBase.cxx
new file mode 100644
index 0000000000..d8b84fabaf
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCellBase.cxx
@@ -0,0 +1,587 @@
+/* -*- 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 <AccessibleCellBase.hxx>
+#include <document.hxx>
+#include <docfunc.hxx>
+#include <docsh.hxx>
+#include <strings.hxx>
+#include <unonames.hxx>
+#include <detfunc.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XSheetAnnotationAnchor.hpp>
+#include <com/sun/star/text/XSimpleText.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <comphelper/sequence.hxx>
+#include <sfx2/objsh.hxx>
+#include <vcl/svapp.hxx>
+
+#include <float.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+#define DEFAULT_LINE_WIDTH 2
+
+//===== internal ============================================================
+
+ScAccessibleCellBase::ScAccessibleCellBase(
+ const uno::Reference<XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScAddress& rCellAddress,
+ sal_Int64 nIndex)
+ :
+ ScAccessibleContextBase(rxParent, AccessibleRole::TABLE_CELL),
+ maCellAddress(rCellAddress),
+ mpDoc(pDoc),
+ mnIndex(nIndex)
+{
+}
+
+ScAccessibleCellBase::~ScAccessibleCellBase()
+{
+}
+
+ //===== XAccessibleComponent ============================================
+
+bool ScAccessibleCellBase::isVisible()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ // test whether the cell is hidden (column/row - hidden/filtered)
+ bool bVisible(true);
+ if (mpDoc)
+ {
+ bool bColHidden = mpDoc->ColHidden(maCellAddress.Col(), maCellAddress.Tab());
+ bool bRowHidden = mpDoc->RowHidden(maCellAddress.Row(), maCellAddress.Tab());
+ bool bColFiltered = mpDoc->ColFiltered(maCellAddress.Col(), maCellAddress.Tab());
+ bool bRowFiltered = mpDoc->RowFiltered(maCellAddress.Row(), maCellAddress.Tab());
+
+ if (bColHidden || bColFiltered || bRowHidden || bRowFiltered)
+ bVisible = false;
+ }
+ return bVisible;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCellBase::getForeground()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int32 nColor(0);
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_CCOLOR);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nColor;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCellBase::getBackground()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int32 nColor(0);
+
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_CELLBACK);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nColor;
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleCellBase::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessibleCellBaseImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessibleCellBase::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleCellBase::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64
+ ScAccessibleCellBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return mnIndex;
+}
+
+OUString
+ ScAccessibleCellBase::createAccessibleDescription()
+{
+ return STR_ACC_CELL_DESCR;
+}
+
+OUString
+ ScAccessibleCellBase::createAccessibleName()
+{
+ // Document not needed, because only the cell address, but not the tablename is needed
+ // always us OOO notation
+ return maCellAddress.Format(ScRefFlags::VALID);
+}
+
+ //===== XAccessibleValue ================================================
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getCurrentValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Any aAny;
+ if (mpDoc)
+ {
+ aAny <<= mpDoc->GetValue(maCellAddress);
+ }
+ return aAny;
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleCellBase::setCurrentValue( const uno::Any& aNumber )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ double fValue = 0;
+ bool bResult = false;
+ if((aNumber >>= fValue) && mpDoc && mpDoc->GetDocumentShell())
+ {
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ if (IsEditable(nParentStates))
+ {
+ ScDocShell* pDocShell = mpDoc->GetDocumentShell();
+ bResult = pDocShell->GetDocFunc().SetValueCell(maCellAddress, fValue, false);
+ }
+ }
+ return bResult;
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMaximumValue( )
+{
+ return uno::Any(DBL_MAX);
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMinimumValue( )
+{
+ return uno::Any(-DBL_MAX);
+}
+
+uno::Any SAL_CALL
+ ScAccessibleCellBase::getMinimumIncrement( )
+{
+ return uno::Any();
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleCellBase::getImplementationName()
+{
+ return "ScAccessibleCellBase";
+}
+
+ //===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleCellBase::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleCellBaseImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleCellBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+bool ScAccessibleCellBase::IsEditable(sal_Int64 nParentStates)
+{
+ bool bEditable = nParentStates & AccessibleStateType::EDITABLE;
+ return bEditable;
+}
+
+OUString ScAccessibleCellBase::GetNote() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ OUString sNote;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference <sheet::XSheetAnnotationAnchor> xAnnotationAnchor ( xCell, uno::UNO_QUERY);
+ if(xAnnotationAnchor.is())
+ {
+ uno::Reference <sheet::XSheetAnnotation> xSheetAnnotation = xAnnotationAnchor->getAnnotation();
+ if (xSheetAnnotation.is())
+ {
+ uno::Reference <text::XSimpleText> xText (xSheetAnnotation, uno::UNO_QUERY);
+ if (xText.is())
+ {
+ sNote = xText->getString();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return sNote;
+}
+
+OUString ScAccessibleCellBase::getShadowAttrs() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ table::ShadowFormat aShadowFmt;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_SHADOW);
+ aAny >>= aShadowFmt;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ //construct shadow attributes string
+ OUString sShadowAttrs("Shadow:");
+ OUString sInnerSplit(",");
+ OUString sOuterSplit(";");
+ sal_Int32 nLocationVal = 0;
+ switch( aShadowFmt.Location )
+ {
+ case table::ShadowLocation_TOP_LEFT:
+ nLocationVal = 1;
+ break;
+ case table::ShadowLocation_TOP_RIGHT:
+ nLocationVal = 2;
+ break;
+ case table::ShadowLocation_BOTTOM_LEFT:
+ nLocationVal = 3;
+ break;
+ case table::ShadowLocation_BOTTOM_RIGHT:
+ nLocationVal = 4;
+ break;
+ default:
+ break;
+ }
+ //if there is no shadow property for the cell
+ if ( nLocationVal == 0 )
+ {
+ sShadowAttrs += sOuterSplit;
+ return sShadowAttrs;
+ }
+ //else return all the shadow properties
+ sShadowAttrs += "Location=" +
+ OUString::number( nLocationVal ) +
+ sInnerSplit +
+ "ShadowWidth=" +
+ OUString::number( static_cast<sal_Int32>(aShadowFmt.ShadowWidth) ) +
+ sInnerSplit +
+ "IsTransparent=" +
+ OUString::number( static_cast<int>(aShadowFmt.IsTransparent) ) +
+ sInnerSplit +
+ "Color=" +
+ OUString::number( aShadowFmt.Color ) +
+ sOuterSplit;
+ return sShadowAttrs;
+}
+
+OUString ScAccessibleCellBase::getBorderAttrs()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ table::BorderLine aTopBorder;
+ table::BorderLine aBottomBorder;
+ table::BorderLine aLeftBorder;
+ table::BorderLine aRightBorder;
+ if (mpDoc)
+ {
+ ScDocShell* pObjSh = mpDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(maCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(maCellAddress.Col(), maCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(SC_UNONAME_TOPBORDER);
+ aAny >>= aTopBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_BOTTBORDER);
+ aAny >>= aBottomBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_LEFTBORDER);
+ aAny >>= aLeftBorder;
+ aAny = xCellProps->getPropertyValue(SC_UNONAME_RIGHTBORDER);
+ aAny >>= aRightBorder;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Color aColor;
+ bool bIn = mpDoc && mpDoc->IsCellInChangeTrack(maCellAddress,&aColor);
+ if (bIn)
+ {
+ aTopBorder.Color = sal_Int32(aColor);
+ aBottomBorder.Color = sal_Int32(aColor);
+ aLeftBorder.Color = sal_Int32(aColor);
+ aRightBorder.Color = sal_Int32(aColor);
+ aTopBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aBottomBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aLeftBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ aRightBorder.OuterLineWidth = DEFAULT_LINE_WIDTH;
+ }
+
+ //construct border attributes string
+ OUString sBorderAttrs;
+ OUString sInnerSplit(",");
+ OUString sOuterSplit(";");
+ //top border
+ //if top of the cell has no border
+ if ( aTopBorder.InnerLineWidth == 0 && aTopBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "TopBorder:;";
+ }
+ else//add all the border properties to the return string.
+ {
+ sBorderAttrs += "TopBorder:Color=" +
+ OUString::number( aTopBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aTopBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //bottom border
+ if ( aBottomBorder.InnerLineWidth == 0 && aBottomBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "BottomBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "BottomBorder:Color=" +
+ OUString::number( aBottomBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aBottomBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //left border
+ if ( aLeftBorder.InnerLineWidth == 0 && aLeftBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "LeftBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "LeftBorder:Color=" +
+ OUString::number( aLeftBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aLeftBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ //right border
+ if ( aRightBorder.InnerLineWidth == 0 && aRightBorder.OuterLineWidth == 0 )
+ {
+ sBorderAttrs += "RightBorder:;";
+ }
+ else
+ {
+ sBorderAttrs += "RightBorder:Color=" +
+ OUString::number( aRightBorder.Color ) +
+ sInnerSplit +
+ "InnerLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.InnerLineWidth) ) +
+ sInnerSplit +
+ "OuterLineWidth=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.OuterLineWidth) ) +
+ sInnerSplit +
+ "LineDistance=" +
+ OUString::number( static_cast<sal_Int32>(aRightBorder.LineDistance) ) +
+ sOuterSplit;
+ }
+ return sBorderAttrs;
+}
+//end of cell attributes
+
+OUString ScAccessibleCellBase::GetAllDisplayNote() const
+{
+ OUString strNote;
+ OUString strTrackText;
+ if (mpDoc)
+ {
+ bool bLeftedge = false;
+ mpDoc->GetCellChangeTrackNote(maCellAddress,strTrackText,bLeftedge);
+ }
+ if (!strTrackText.isEmpty())
+ {
+ ScDetectiveFunc::AppendChangTrackNoteSeparator(strTrackText);
+ strNote = strTrackText;
+ }
+ strNote += GetNote();
+ return strNote;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleContextBase.cxx b/sc/source/ui/Accessibility/AccessibleContextBase.cxx
new file mode 100644
index 0000000000..59f2f39903
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleContextBase.cxx
@@ -0,0 +1,493 @@
+/* -*- 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 <AccessibleContextBase.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <tools/gen.hxx>
+#include <tools/color.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <svl/hint.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <utility>
+#include <vcl/unohelp.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+/**
+ The listener is an internal class to prevent reference-counting cycles and therefore memory leaks.
+*/
+typedef cppu::WeakComponentImplHelper<
+ css::accessibility::XAccessibleEventListener
+ > ScAccessibleContextBaseEventListenerWeakImpl;
+class ScAccessibleContextBase::ScAccessibleContextBaseEventListener : public cppu::BaseMutex, public ScAccessibleContextBaseEventListenerWeakImpl
+{
+public:
+ ScAccessibleContextBaseEventListener(ScAccessibleContextBase& rBase)
+ : ScAccessibleContextBaseEventListenerWeakImpl(m_aMutex), mrBase(rBase) {}
+
+ using WeakComponentImplHelperBase::disposing;
+
+ ///===== XAccessibleEventListener ========================================
+
+ virtual void SAL_CALL disposing( const lang::EventObject& rSource ) override
+ {
+ SolarMutexGuard aGuard;
+ if (rSource.Source == mrBase.mxParent)
+ dispose();
+ }
+
+ virtual void SAL_CALL
+ notifyEvent(
+ const css::accessibility::AccessibleEventObject& /*aEvent*/ ) override {}
+private:
+ ScAccessibleContextBase& mrBase;
+};
+
+
+ScAccessibleContextBase::ScAccessibleContextBase(
+ uno::Reference<XAccessible> xParent,
+ const sal_Int16 aRole)
+ :
+ ScAccessibleContextBaseWeakImpl(m_aMutex),
+ mxParent(std::move(xParent)),
+ mnClientId(0),
+ maRole(aRole)
+{
+}
+
+ScAccessibleContextBase::~ScAccessibleContextBase()
+{
+ if (!IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void ScAccessibleContextBase::Init()
+{
+ // hold reference to make sure that the destructor is not called
+ uno::Reference< XAccessibleContext > xKeepAlive(this);
+
+ if (mxParent.is())
+ {
+ uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xBroadcaster.is())
+ {
+ if (!mxEventListener)
+ mxEventListener = new ScAccessibleContextBaseEventListener(*this);
+ xBroadcaster->addAccessibleEventListener(mxEventListener);
+ }
+ }
+ msName = createAccessibleName();
+ msDescription = createAccessibleDescription();
+}
+
+void SAL_CALL ScAccessibleContextBase::disposing()
+{
+ SolarMutexGuard aGuard;
+// CommitDefunc(); not necessary and should not be send, because it cost a lot of time
+
+ // hold reference to make sure that the destructor is not called
+ uno::Reference< XAccessibleContext > xKeepAlive(this);
+
+ if ( mnClientId )
+ {
+ sal_Int32 nTemClientId(mnClientId);
+ mnClientId = 0;
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nTemClientId, *this );
+ }
+
+ if (mxParent.is())
+ {
+ uno::Reference< XAccessibleEventBroadcaster > xBroadcaster (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xBroadcaster && mxEventListener)
+ xBroadcaster->removeAccessibleEventListener(mxEventListener);
+ mxParent = nullptr;
+ }
+}
+
+
+//===== SfxListener =====================================================
+
+void ScAccessibleContextBase::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ // it seems the Broadcaster is dying, since the view is dying
+ dispose();
+ }
+}
+
+//===== XAccessible =========================================================
+
+uno::Reference< XAccessibleContext> SAL_CALL
+ ScAccessibleContextBase::getAccessibleContext()
+{
+ return this;
+}
+
+//===== XAccessibleComponent ================================================
+
+sal_Bool SAL_CALL ScAccessibleContextBase::containsPoint(const awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return tools::Rectangle (Point(), GetBoundingBox().GetSize()).Contains(VCLPoint(rPoint));
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleContextBase::getAccessibleAtPoint(
+ const awt::Point& /* rPoint */ )
+{
+ OSL_FAIL("not implemented");
+ return uno::Reference<XAccessible>();
+}
+
+awt::Rectangle SAL_CALL ScAccessibleContextBase::getBounds( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTRectangle(GetBoundingBox());
+}
+
+awt::Point SAL_CALL ScAccessibleContextBase::getLocation( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTPoint(GetBoundingBox().TopLeft());
+}
+
+awt::Point SAL_CALL ScAccessibleContextBase::getLocationOnScreen( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTPoint(GetBoundingBoxOnScreen().TopLeft());
+}
+
+awt::Size SAL_CALL ScAccessibleContextBase::getSize( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return AWTSize(GetBoundingBox().GetSize());
+}
+
+bool ScAccessibleContextBase::isShowing( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ bool bShowing(false);
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleComponent> xParentComponent (mxParent->getAccessibleContext(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ tools::Rectangle aParentBounds(VCLRectangle(xParentComponent->getBounds()));
+ tools::Rectangle aBounds(VCLRectangle(getBounds()));
+ bShowing = aBounds.Overlaps(aParentBounds);
+ }
+ }
+ return bShowing;
+}
+
+bool ScAccessibleContextBase::isVisible()
+{
+ return true;
+}
+
+void SAL_CALL ScAccessibleContextBase::grabFocus( )
+{
+ OSL_FAIL("not implemented");
+}
+
+sal_Int32 SAL_CALL ScAccessibleContextBase::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleContextBase::getBackground( )
+{
+ return sal_Int32(COL_WHITE);
+}
+
+//===== XAccessibleContext ==================================================
+
+sal_Int64 SAL_CALL ScAccessibleContextBase::getAccessibleChildCount()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return 0;
+}
+
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleContextBase::getAccessibleChild(sal_Int64 /* nIndex */)
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return uno::Reference<XAccessible>();
+}
+
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleContextBase::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleContextBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ // Use a simple but slow solution for now. Optimize later.
+ // Return -1 to indicate that this object's parent does not know about the
+ // object.
+ sal_Int64 nIndex(-1);
+
+ // Iterate over all the parent's children and search for this object.
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ {
+ sal_Int64 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int64 i=0; i<nChildCount; ++i)
+ {
+ uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
+ if (xChild.is() && xChild.get() == this)
+ nIndex = i;
+ }
+ }
+ }
+
+ return nIndex;
+}
+
+sal_Int16 SAL_CALL
+ ScAccessibleContextBase::getAccessibleRole()
+{
+ return maRole;
+}
+
+OUString SAL_CALL
+ ScAccessibleContextBase::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (msDescription.isEmpty())
+ {
+ OUString sDescription(createAccessibleDescription());
+
+ if (msDescription != sDescription)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= msDescription;
+ aEvent.NewValue <<= sDescription;
+
+ msDescription = sDescription;
+
+ CommitChange(aEvent);
+ }
+ }
+ return msDescription;
+}
+
+OUString SAL_CALL
+ ScAccessibleContextBase::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (msName.isEmpty())
+ {
+ OUString sName(createAccessibleName());
+ OSL_ENSURE(!sName.isEmpty(), "We should give always a name.");
+
+ if (msName != sName)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= msName;
+ aEvent.NewValue <<= sName;
+
+ msName = sName;
+
+ CommitChange(aEvent);
+ }
+ }
+ return msName;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleContextBase::getAccessibleRelationSet()
+{
+ return new utl::AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleContextBase::getAccessibleStateSet()
+{
+ return 0;
+}
+
+lang::Locale SAL_CALL
+ ScAccessibleContextBase::getLocale()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext (
+ mxParent->getAccessibleContext());
+ if (xParentContext.is())
+ return xParentContext->getLocale ();
+ }
+
+ // No locale and no parent. Therefore throw exception to indicate this
+ // cluelessness.
+ throw IllegalAccessibleComponentStateException ();
+}
+
+ //===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL
+ ScAccessibleContextBase::addAccessibleEventListener(
+ const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!IsDefunc())
+ {
+ if (!mnClientId)
+ mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
+ }
+ }
+}
+
+void SAL_CALL
+ ScAccessibleContextBase::removeAccessibleEventListener(
+ const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ if (!xListener.is())
+ return;
+
+ SolarMutexGuard aGuard;
+ if (IsDefunc() || !mnClientId)
+ return;
+
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, 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( mnClientId );
+ mnClientId = 0;
+ }
+}
+
+// XServiceInfo
+OUString SAL_CALL ScAccessibleContextBase::getImplementationName()
+{
+ return "ScAccessibleContextBase";
+}
+
+sal_Bool SAL_CALL ScAccessibleContextBase::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleContextBase::getSupportedServiceNames()
+{
+ return {"com.sun.star.accessibility.Accessible",
+ "com.sun.star.accessibility.AccessibleContext"};
+}
+
+//===== internal ============================================================
+
+OUString
+ ScAccessibleContextBase::createAccessibleDescription()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return OUString();
+}
+
+OUString ScAccessibleContextBase::createAccessibleName()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return OUString();
+}
+
+void ScAccessibleContextBase::CommitChange(const AccessibleEventObject& rEvent) const
+{
+ if (mnClientId)
+ comphelper::AccessibleEventNotifier::addEvent( mnClientId, rEvent );
+}
+
+void ScAccessibleContextBase::CommitFocusGained() const
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
+ aEvent.NewValue <<= AccessibleStateType::FOCUSED;
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleContextBase::CommitFocusLost() const
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(const_cast<ScAccessibleContextBase*>(this));
+ aEvent.OldValue <<= AccessibleStateType::FOCUSED;
+
+ CommitChange(aEvent);
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleContextBase::GetBoundingBoxOnScreen() const
+{
+ OSL_FAIL("not implemented");
+ return AbsoluteScreenPixelRectangle();
+}
+
+tools::Rectangle ScAccessibleContextBase::GetBoundingBox() const
+{
+ OSL_FAIL("not implemented");
+ return tools::Rectangle();
+}
+
+void ScAccessibleContextBase::IsObjectValid() const
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ throw lang::DisposedException();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleCsvControl.cxx b/sc/source/ui/Accessibility/AccessibleCsvControl.cxx
new file mode 100644
index 0000000000..c7050d7777
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleCsvControl.cxx
@@ -0,0 +1,1405 @@
+/* -*- 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 <sal/config.h>
+
+#include <utility>
+
+#include <AccessibleCsvControl.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <scitems.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/langitem.hxx>
+#include <csvtablebox.hxx>
+#include <csvcontrol.hxx>
+#include <csvruler.hxx>
+#include <csvgrid.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <scmod.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/string_view.hxx>
+
+using ::utl::AccessibleRelationSetHelper;
+using ::accessibility::AccessibleStaticTextBase;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::RuntimeException;
+using ::com::sun::star::uno::XInterface;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::beans::PropertyValue;
+using namespace ::com::sun::star::accessibility;
+
+const sal_Unicode cRulerDot = '.';
+const sal_Unicode cRulerLine = '|';
+
+const sal_Int32 CSV_LINE_HEADER = CSV_POS_INVALID;
+const sal_uInt32 CSV_COLUMN_HEADER = CSV_COLUMN_INVALID;
+
+ScAccessibleCsvControl::ScAccessibleCsvControl(ScCsvControl& rControl)
+ : mpControl(&rControl)
+{
+}
+
+ScAccessibleCsvControl::~ScAccessibleCsvControl()
+{
+ ensureDisposed();
+}
+
+void SAL_CALL ScAccessibleCsvControl::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpControl = nullptr;
+ comphelper::OAccessibleComponentHelper::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvControl::getAccessibleAtPoint( const css::awt::Point& /* rPoint */ )
+{
+ ensureAlive();
+ return nullptr;
+}
+
+void SAL_CALL ScAccessibleCsvControl::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ implGetControl().GrabFocus();
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvControl::SendFocusEvent( bool bFocused )
+{
+ Any aOldAny, aNewAny;
+ if (bFocused)
+ aNewAny <<= AccessibleStateType::FOCUSED;
+ else
+ aOldAny <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny);
+}
+
+void ScAccessibleCsvControl::SendCaretEvent()
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendCaretEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendVisibleEvent()
+{
+ NotifyAccessibleEvent(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any());
+}
+
+void ScAccessibleCsvControl::SendSelectionEvent()
+{
+ NotifyAccessibleEvent(AccessibleEventId::SELECTION_CHANGED, Any(), Any());
+}
+
+void ScAccessibleCsvControl::SendTableUpdateEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */, bool /* bAllRows */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendTableUpdateEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendInsertColumnEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendInsertColumnEvent - Illegal call" );
+}
+
+void ScAccessibleCsvControl::SendRemoveColumnEvent( sal_uInt32 /* nFirstColumn */, sal_uInt32 /* nLastColumn */ )
+{
+ OSL_FAIL( "ScAccessibleCsvControl::SendRemoveColumnEvent - Illegal call" );
+}
+
+// helpers --------------------------------------------------------------------
+
+css::awt::Rectangle ScAccessibleCsvControl::implGetBounds()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ Size aOutSize(implGetControl().GetOutputSizePixel());
+ return css::awt::Rectangle(0, 0, aOutSize.Width(), aOutSize.Height());
+}
+
+ScCsvControl& ScAccessibleCsvControl::implGetControl() const
+{
+ assert(mpControl && "ScAccessibleCsvControl::implGetControl - missing control");
+ return *mpControl;
+}
+
+sal_Int64 ScAccessibleCsvControl::implCreateStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+ if (isAlive())
+ {
+ const ScCsvControl& rCtrl = implGetControl();
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if( rCtrl.IsEnabled() )
+ nStateSet |= AccessibleStateType::ENABLED;
+ if( rCtrl.IsReallyVisible() )
+ nStateSet |= AccessibleStateType::SHOWING;
+ if( rCtrl.IsVisible() )
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+ return nStateSet;
+}
+
+// Ruler ======================================================================
+
+/** Converts a ruler cursor position to API text index. */
+static sal_Int32 lcl_GetApiPos( sal_Int32 nRulerPos )
+{
+ sal_Int32 nApiPos = nRulerPos;
+ sal_Int32 nStart = (nRulerPos - 1) / 10;
+ sal_Int32 nExp = 1;
+ while( nStart >= nExp )
+ {
+ nApiPos += nStart - nExp + 1;
+ nExp *= 10;
+ }
+ return ::std::max( nApiPos, static_cast<sal_Int32>(0) );
+}
+
+/** Converts an API text index to a ruler cursor position. */
+static sal_Int32 lcl_GetRulerPos( sal_Int32 nApiPos )
+{
+ sal_Int32 nDiv = 10;
+ sal_Int32 nExp = 10;
+ sal_Int32 nRulerPos = 0;
+ sal_Int32 nApiBase = 0;
+ sal_Int32 nApiLimit = 10;
+ while( nApiPos >= nApiLimit )
+ {
+ ++nDiv;
+ nRulerPos = nExp;
+ nExp *= 10;
+ nApiBase = nApiLimit;
+ nApiLimit = lcl_GetApiPos( nExp );
+ }
+ sal_Int32 nRelPos = nApiPos - nApiBase;
+ return nRulerPos + nRelPos / nDiv * 10 + ::std::max<sal_Int32>( nRelPos % nDiv - nDiv + 10, 0 );
+}
+
+/** Expands the sequence's size and returns the base index of the new inserted elements. */
+static sal_Int32 lcl_ExpandSequence( Sequence< PropertyValue >& rSeq, sal_Int32 nExp )
+{
+ OSL_ENSURE( nExp > 0, "lcl_ExpandSequence - invalid value" );
+ rSeq.realloc( rSeq.getLength() + nExp );
+ return rSeq.getLength() - nExp;
+}
+
+/** Fills the property value rVal with the specified name and value from the item. */
+static void lcl_FillProperty( PropertyValue& rVal, const OUString& rPropName, const SfxPoolItem& rItem, sal_uInt8 nMID )
+{
+ rVal.Name = rPropName;
+ rItem.QueryValue( rVal.Value, nMID );
+}
+
+/** Fills the sequence with all font attributes of rFont. */
+static void lcl_FillFontAttributes( Sequence< PropertyValue >& rSeq, const vcl::Font& rFont )
+{
+ SvxFontItem aFontItem( rFont.GetFamilyType(), rFont.GetFamilyName(), rFont.GetStyleName(), rFont.GetPitch(), rFont.GetCharSet(), ATTR_FONT );
+ SvxFontHeightItem aHeightItem( rFont.GetFontSize().Height(), 100, ATTR_FONT_HEIGHT );
+ SvxLanguageItem aLangItem( rFont.GetLanguage(), ATTR_FONT_LANGUAGE );
+
+ sal_Int32 nIndex = lcl_ExpandSequence( rSeq, 7 );
+ auto pSeq = rSeq.getArray();
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontName", aFontItem, MID_FONT_FAMILY_NAME );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontFamily", aFontItem, MID_FONT_FAMILY );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontStyleName", aFontItem, MID_FONT_STYLE_NAME );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontCharSet", aFontItem, MID_FONT_PITCH );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharFontPitch", aFontItem, MID_FONT_CHAR_SET );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharHeight", aHeightItem, MID_FONTHEIGHT );
+ lcl_FillProperty( pSeq[ nIndex++ ], "CharLocale", aLangItem, MID_LANG_LOCALE );
+}
+
+ScAccessibleCsvRuler::ScAccessibleCsvRuler(ScCsvRuler& rRuler)
+ : ImplInheritanceHelper(rRuler)
+{
+ constructStringBuffer();
+}
+
+ScAccessibleCsvRuler::~ScAccessibleCsvRuler()
+{
+ ensureDisposed();
+}
+
+// XAccessibleComponent -----------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetLabelTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetFaceColor());
+}
+
+// XAccessibleContext ---------------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvRuler::getAccessibleChildCount()
+{
+ ensureAlive();
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvRuler::getAccessibleChild( sal_Int64 /* nIndex */ )
+{
+ ensureAlive();
+ throw IndexOutOfBoundsException();
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvRuler::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ rtl::Reference<AccessibleRelationSetHelper> pRelationSet = new AccessibleRelationSetHelper();
+
+ ScCsvRuler& rRuler = implGetRuler();
+ ScCsvTableBox* pTableBox = rRuler.GetTableBox();
+ ScCsvGrid& rGrid = pTableBox->GetGrid();
+
+ css::uno::Reference<css::accessibility::XAccessible> xAccObj(static_cast<ScAccessibleCsvGrid*>(rGrid.GetAccessible()));
+ if( xAccObj.is() )
+ {
+ Sequence< Reference< XInterface > > aSeq{ xAccObj };
+ pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLER_FOR, aSeq ) );
+ }
+
+ return pRelationSet;
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvRuler::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ nStateSet |= AccessibleStateType::SINGLE_LINE;
+ if( implGetRuler().HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ }
+ return nStateSet;
+}
+
+// XAccessibleText ------------------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getCaretPosition()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return lcl_GetApiPos( implGetRuler().GetRulerCursorPos() );
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+ ScCsvRuler& rRuler = implGetRuler();
+ sal_Int32 nOldCursor = rRuler.GetRulerCursorPos();
+ rRuler.Execute( CSVCMD_MOVERULERCURSOR, lcl_GetRulerPos( nIndex ) );
+ return rRuler.GetRulerCursorPos() != nOldCursor;
+}
+
+sal_Unicode SAL_CALL ScAccessibleCsvRuler::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+ return maBuffer[nIndex];
+}
+
+Sequence< PropertyValue > SAL_CALL ScAccessibleCsvRuler::getCharacterAttributes( sal_Int32 nIndex,
+ const css::uno::Sequence< OUString >& /* aRequestedAttributes */ )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+ Sequence< PropertyValue > aSeq;
+ lcl_FillFontAttributes( aSeq, implGetRuler().GetDrawingArea()->get_ref_device().GetFont() );
+ return aSeq;
+}
+
+css::awt::Rectangle SAL_CALL ScAccessibleCsvRuler::getCharacterBounds( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+ ScCsvRuler& rRuler = implGetRuler();
+ Point aPos( rRuler.GetX( lcl_GetRulerPos( nIndex ) ) - rRuler.GetCharWidth() / 2, 0 );
+ css::awt::Rectangle aRect( aPos.X(), aPos.Y(), rRuler.GetCharWidth(), rRuler.GetOutputSizePixel().Height() );
+ // do not return rectangle out of window
+ sal_Int32 nWidth = rRuler.GetOutputSizePixel().Width();
+ if( aRect.X >= nWidth )
+ throw IndexOutOfBoundsException();
+ if( aRect.X + aRect.Width > nWidth )
+ aRect.Width = nWidth - aRect.X;
+ return aRect;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetTextLength();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getIndexAtPoint( const css::awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ScCsvRuler& rRuler = implGetRuler();
+ // use object's coordinate system, convert to API position
+ return lcl_GetApiPos( ::std::clamp( rRuler.GetPosFromX( rPoint.X ), sal_Int32(0), rRuler.GetPosCount() ) );
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getSelectedText()
+{
+ ensureAlive();
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getSelectionStart()
+{
+ ensureAlive();
+ return -1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvRuler::getSelectionEnd()
+{
+ ensureAlive();
+ return -1;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::setSelection( sal_Int32 /* nStartIndex */, sal_Int32 /* nEndIndex */ )
+{
+ ensureAlive();
+ return false;
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getText()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return OUString(maBuffer.subView( 0, implGetTextLength() ));
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidRange( nStartIndex, nEndIndex );
+ return OUString( maBuffer.getStr() + nStartIndex, nEndIndex - nStartIndex );
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextAtIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ if( (nIndex == implGetTextLength()) && (nTextType != AccessibleTextType::LINE) )
+ return aResult;
+
+ ensureValidIndex( nIndex );
+
+ OUStringBuffer aResultText; // will be assigned to aResult.SegmentText below
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ {
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex;
+ o3tl::iterateCodePoints(maBuffer, &aResult.SegmentEnd);
+ for (; nIndex < aResult.SegmentEnd; nIndex++)
+ aResultText.append(maBuffer[nIndex]);
+ }
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ aResult.SegmentStart = nIndex;
+ if( nRulerPos % 10 )
+ aResultText.append(maBuffer[nIndex]);
+ else
+ aResultText.append( nRulerPos ); // string representation of sal_Int32!!!
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ aResult.SegmentStart = 0;
+ aResultText.append( maBuffer.getStr(), implGetTextLength() );
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nFirstIndex = implGetFirstEqualFormatted( nIndex );
+ sal_Int32 nLastIndex = implGetLastEqualFormatted( nIndex );
+ aResult.SegmentStart = nFirstIndex;
+ aResultText.append( maBuffer.getStr() + nFirstIndex, nLastIndex - nFirstIndex + 1 );
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+
+ aResult.SegmentText = aResultText.makeStringAndClear();
+ aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength();
+ return aResult;
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ if( nIndex > 0 )
+ {
+ o3tl::iterateCodePoints(maBuffer, &nIndex, -1);
+ aResult = getTextAtIndex(nIndex, nTextType);
+ }
+ // else empty
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ if( nRulerPos > 0 )
+ aResult = getTextAtIndex( lcl_GetApiPos( nRulerPos - 1 ), nTextType );
+ // else empty
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ // empty
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nFirstIndex = implGetFirstEqualFormatted( nIndex );
+ if( nFirstIndex > 0 )
+ aResult = getTextAtIndex( nFirstIndex - 1, nTextType );
+ // else empty
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+ return aResult;
+}
+
+TextSegment SAL_CALL ScAccessibleCsvRuler::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndexWithEnd( nIndex );
+
+ TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nIndex );
+ sal_Int32 nLastValid = implGetTextLength();
+
+ switch( nTextType )
+ {
+ // single character
+ case AccessibleTextType::CHARACTER:
+ if( nIndex < nLastValid )
+ {
+ o3tl::iterateCodePoints(maBuffer, &nIndex);
+ aResult = getTextAtIndex(nIndex, nTextType);
+ }
+ // else empty
+ break;
+
+ // entire number or single dot/line
+ case AccessibleTextType::WORD:
+ case AccessibleTextType::GLYPH:
+ if( nRulerPos < implGetRuler().GetPosCount() )
+ aResult = getTextAtIndex( lcl_GetApiPos( nRulerPos + 1 ), nTextType );
+ // else empty
+ break;
+
+ // entire text
+ case AccessibleTextType::SENTENCE:
+ case AccessibleTextType::PARAGRAPH:
+ case AccessibleTextType::LINE:
+ // empty
+ break;
+
+ // equal-formatted text
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ {
+ sal_Int32 nLastIndex = implGetLastEqualFormatted( nIndex );
+ if( nLastIndex < nLastValid )
+ aResult = getTextAtIndex( nLastIndex + 1, nTextType );
+ // else empty
+ }
+ break;
+
+ default:
+ throw RuntimeException();
+ }
+ return aResult;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::copyText( sal_Int32 /* nStartIndex */, sal_Int32 /* nEndIndex */ )
+{
+ ensureAlive();
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvRuler::scrollSubstringTo( sal_Int32 /* nStartIndex */, sal_Int32/* nEndIndex */, AccessibleScrollType /* aScrollType */ )
+{
+ return false;
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvRuler::SendCaretEvent()
+{
+ sal_Int32 nPos = implGetRuler().GetRulerCursorPos();
+ if (nPos != CSV_POS_INVALID)
+ {
+ Any aOldValue, aNewValue;
+ aNewValue <<= nPos;
+ NotifyAccessibleEvent( AccessibleEventId::CARET_CHANGED, aOldValue, aNewValue );
+ }
+}
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvRuler::getAccessibleName()
+{
+ return ScResId( STR_ACC_CSVRULER_NAME );
+}
+
+OUString SAL_CALL ScAccessibleCsvRuler::getAccessibleDescription()
+{
+ return ScResId( STR_ACC_CSVRULER_DESCR );
+}
+
+void ScAccessibleCsvRuler::ensureValidIndex( sal_Int32 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex >= implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvRuler::ensureValidIndexWithEnd( sal_Int32 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex > implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvRuler::ensureValidRange( sal_Int32& rnStartIndex, sal_Int32& rnEndIndex ) const
+{
+ if( rnStartIndex > rnEndIndex )
+ ::std::swap( rnStartIndex, rnEndIndex );
+ if( (rnStartIndex < 0) || (rnEndIndex > implGetTextLength()) )
+ throw IndexOutOfBoundsException();
+}
+
+ScCsvRuler& ScAccessibleCsvRuler::implGetRuler() const
+{
+ return static_cast< ScCsvRuler& >( implGetControl() );
+}
+
+void ScAccessibleCsvRuler::constructStringBuffer()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ // extend existing string buffer to new ruler size
+ sal_Int32 nRulerCount = implGetRuler().GetPosCount();
+ sal_Int32 nRulerPos = lcl_GetRulerPos( maBuffer.getLength() );
+ for( ; nRulerPos <= nRulerCount; ++nRulerPos ) // include last position
+ {
+ switch( nRulerPos % 10 )
+ {
+ case 0: maBuffer.append( nRulerPos ); break;
+ case 5: maBuffer.append( cRulerLine ); break;
+ default: maBuffer.append( cRulerDot );
+ }
+ }
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetTextLength() const
+{
+ return lcl_GetApiPos( implGetRuler().GetPosCount() + 1 );
+}
+
+bool ScAccessibleCsvRuler::implHasSplit( sal_Int32 nApiPos )
+{
+ sal_Int32 nRulerPos = lcl_GetRulerPos( nApiPos );
+ return implGetRuler().HasSplit( nRulerPos ) && (nApiPos == lcl_GetApiPos( nRulerPos ));
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetFirstEqualFormatted( sal_Int32 nApiPos )
+{
+ bool bSplit = implHasSplit( nApiPos );
+ while( (nApiPos > 0) && (implHasSplit( nApiPos - 1 ) == bSplit) )
+ --nApiPos;
+ return nApiPos;
+}
+
+sal_Int32 ScAccessibleCsvRuler::implGetLastEqualFormatted( sal_Int32 nApiPos )
+{
+ bool bSplit = implHasSplit( nApiPos );
+ sal_Int32 nLength = implGetTextLength();
+ while( (nApiPos < nLength - 1) && (implHasSplit( nApiPos + 1 ) == bSplit) )
+ ++nApiPos;
+ return nApiPos;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvRuler::getAccessibleParent()
+{
+ return implGetControl().GetDrawingArea()->get_accessible_parent();
+}
+
+// Grid =======================================================================
+
+/** Converts a grid columnm index to an API column index. */
+static sal_Int32 lcl_GetApiColumn( sal_uInt32 nGridColumn )
+{
+ return (nGridColumn != CSV_COLUMN_HEADER) ? static_cast< sal_Int32 >( nGridColumn + 1 ) : 0;
+}
+
+/** Converts an API columnm index to a ScCsvGrid column index. */
+static sal_uInt32 lcl_GetGridColumn( sal_Int32 nApiColumn )
+{
+ return (nApiColumn > 0) ? static_cast< sal_uInt32 >( nApiColumn - 1 ) : CSV_COLUMN_HEADER;
+}
+
+ScAccessibleCsvGrid::ScAccessibleCsvGrid(ScCsvGrid& rGrid)
+ : ImplInheritanceHelper(rGrid)
+{
+}
+
+ScAccessibleCsvGrid::~ScAccessibleCsvGrid()
+{
+ ensureDisposed();
+}
+
+void ScAccessibleCsvGrid::disposing()
+{
+ SolarMutexGuard aGuard;
+ for (auto& rEntry : maAccessibleChildren)
+ rEntry.second->dispose();
+ maAccessibleChildren.clear();
+ ScAccessibleCsvControl::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleAtPoint( const css::awt::Point& rPoint )
+{
+ Reference< XAccessible > xRet;
+ if( containsPoint( rPoint ) )
+ {
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ const ScCsvGrid& rGrid = implGetGrid();
+ // #102679#; use <= instead of <, because the offset is the size and not the point
+ sal_Int32 nColumn = ((rGrid.GetFirstX() <= rPoint.X) && (rPoint.X <= rGrid.GetLastX())) ?
+ lcl_GetApiColumn( rGrid.GetColumnFromX( rPoint.X ) ) : 0;
+ sal_Int32 nRow = (rPoint.Y >= rGrid.GetHdrHeight()) ?
+ (rGrid.GetLineFromY( rPoint.Y ) - rGrid.GetFirstVisLine() + 1) : 0;
+ xRet = getAccessibleCell(nRow, nColumn);
+ }
+ return xRet;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetButtonTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+// XAccessibleContext ---------------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetCellCount();
+}
+
+Reference<XAccessible> ScAccessibleCsvGrid::getAccessibleCell(sal_Int32 nRow, sal_Int32 nColumn)
+{
+ sal_Int64 nIndex = implGetIndex(nRow, nColumn);
+
+ XAccessibleSet::iterator aI = maAccessibleChildren.lower_bound(nIndex);
+ if (aI != maAccessibleChildren.end() && !(maAccessibleChildren.key_comp()(nIndex, aI->first)))
+ {
+ // key already exists
+ return aI->second;
+ }
+ // key does not exist
+ rtl::Reference<ScAccessibleCsvCell> xNew = implCreateCellObj(nRow, nColumn);
+ maAccessibleChildren.insert(aI, XAccessibleSet::value_type(nIndex, xNew));
+ return xNew;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nIndex );
+
+ return getAccessibleCell(implGetRow(nIndex), implGetColumn(nIndex));
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvGrid::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ rtl::Reference<AccessibleRelationSetHelper> pRelationSet = new AccessibleRelationSetHelper();
+
+ ScCsvGrid& rGrid = implGetGrid();
+ ScCsvTableBox* pTableBox = rGrid.GetTableBox();
+ ScCsvRuler& rRuler = pTableBox->GetRuler();
+
+ if (rRuler.IsVisible())
+ {
+ css::uno::Reference<css::accessibility::XAccessible> xAccObj(static_cast<ScAccessibleCsvGrid*>(rRuler.GetAccessible()));
+ if( xAccObj.is() )
+ {
+ Sequence< Reference< XInterface > > aSeq{ xAccObj };
+ pRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::CONTROLLED_BY, aSeq ) );
+ }
+ }
+
+ return pRelationSet;
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ if( implGetGrid().HasFocus() )
+ nStateSet |= AccessibleStateType::FOCUSED;
+ }
+ else
+ nStateSet |= AccessibleStateType::DEFUNC;
+ return nStateSet;
+}
+
+// XAccessibleTable -----------------------------------------------------------
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetRowCount();
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return implGetColumnCount();
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, 0 );
+ return implGetCellText( nRow, 0 );
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( 0, nColumn );
+ return implGetCellText( 0, nColumn );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return 1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return 1;
+}
+
+Reference< XAccessibleTable > SAL_CALL ScAccessibleCsvGrid::getAccessibleRowHeaders()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Reference< XAccessibleTable > SAL_CALL ScAccessibleCsvGrid::getAccessibleColumnHeaders()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Sequence< sal_Int32 > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleRows()
+{
+ ensureAlive();
+ return Sequence< sal_Int32 >();
+}
+
+Sequence< sal_Int32 > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleColumns()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+
+ ScCsvGrid& rGrid = implGetGrid();
+ Sequence< sal_Int32 > aSeq( implGetColumnCount() );
+ auto pSeq = aSeq.getArray();
+
+ sal_Int32 nSeqIx = 0;
+ sal_uInt32 nColIx = rGrid.GetFirstSelected();
+ for( ; nColIx != CSV_COLUMN_INVALID; ++nSeqIx, nColIx = rGrid.GetNextSelected( nColIx ) )
+ pSeq[ nSeqIx ] = lcl_GetApiColumn( nColIx );
+
+ aSeq.realloc( nSeqIx );
+ return aSeq;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleRowSelected( sal_Int32 /* nRow */ )
+{
+ ensureAlive();
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nColumn );
+ return implIsColumnSelected( nColumn );
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return getAccessibleCell(nRow, nColumn);
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleCaption()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getAccessibleSummary()
+{
+ ensureAlive();
+ return nullptr;
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleSelected( sal_Int32 /* nRow */, sal_Int32 nColumn )
+{
+ return isAccessibleColumnSelected( nColumn );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidPosition( nRow, nColumn );
+ return implGetIndex( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ return implGetRow( nChildIndex );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvGrid::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ return implGetColumn( nChildIndex );
+}
+
+// XAccessibleSelection -------------------------------------------------------
+
+void SAL_CALL ScAccessibleCsvGrid::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ sal_Int32 nColumn = implGetColumn( nChildIndex );
+ if( nChildIndex == 0 )
+ implGetGrid().SelectAll();
+ else
+ implSelectColumn( nColumn, true );
+}
+
+sal_Bool SAL_CALL ScAccessibleCsvGrid::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex( nChildIndex );
+ sal_Int32 nColumn = implGetColumn( nChildIndex );
+ return implIsColumnSelected( nColumn );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::clearAccessibleSelection()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ implGetGrid().SelectAll( false );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::selectAllAccessibleChildren()
+{
+ selectAccessibleChild( 0 );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return static_cast<sal_Int64>(implGetRowCount()) * static_cast<sal_Int64>(implGetSelColumnCount());
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvGrid::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ sal_Int32 nColumns = implGetSelColumnCount();
+ if( nColumns == 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nRow = nSelectedChildIndex / nColumns;
+ sal_Int32 nColumn = implGetSelColumn( nSelectedChildIndex % nColumns );
+ return getAccessibleCellAt( nRow, nColumn );
+}
+
+void SAL_CALL ScAccessibleCsvGrid::deselectAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ensureValidIndex(nSelectedChildIndex);
+ sal_Int32 nColumns = implGetSelColumnCount();
+ if( nColumns == 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nColumn = implGetSelColumn( nSelectedChildIndex % nColumns );
+ ensureValidPosition( nSelectedChildIndex / nColumns, nColumn );
+ if( nColumn > 0 )
+ implSelectColumn( nColumn, false );
+}
+
+// events ---------------------------------------------------------------------
+
+void ScAccessibleCsvGrid::SendFocusEvent( bool bFocused )
+{
+ ScAccessibleCsvControl::SendFocusEvent( bFocused );
+ Any aOldAny, aNewAny;
+ (bFocused ? aNewAny : aOldAny) <<=
+ getAccessibleCellAt( 0, lcl_GetApiColumn( implGetGrid().GetFocusColumn() ) );
+ NotifyAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldAny, aNewAny);
+}
+
+void ScAccessibleCsvGrid::SendTableUpdateEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn, bool bAllRows )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::UPDATE, 0, bAllRows ? implGetRowCount() - 1 : 0,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+void ScAccessibleCsvGrid::SendInsertColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::COLUMNS_INSERTED, -1, -1,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+void ScAccessibleCsvGrid::SendRemoveColumnEvent( sal_uInt32 nFirstColumn, sal_uInt32 nLastColumn )
+{
+ if( nFirstColumn <= nLastColumn )
+ {
+ AccessibleTableModelChange aModelChange(
+ AccessibleTableModelChangeType::COLUMNS_REMOVED, -1, -1,
+ lcl_GetApiColumn( nFirstColumn ), lcl_GetApiColumn( nLastColumn ) );
+ Any aOldAny, aNewAny;
+ aNewAny <<= aModelChange;
+ NotifyAccessibleEvent(AccessibleEventId::TABLE_MODEL_CHANGED, aOldAny, aNewAny);
+ }
+}
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleName()
+{
+ return ScResId( STR_ACC_CSVGRID_NAME );
+}
+
+OUString SAL_CALL ScAccessibleCsvGrid::getAccessibleDescription()
+{
+ return ScResId( STR_ACC_CSVGRID_DESCR );
+}
+
+void ScAccessibleCsvGrid::ensureValidIndex( sal_Int64 nIndex ) const
+{
+ if( (nIndex < 0) || (nIndex >= implGetCellCount()) )
+ throw IndexOutOfBoundsException();
+}
+
+void ScAccessibleCsvGrid::ensureValidPosition( sal_Int32 nRow, sal_Int32 nColumn ) const
+{
+ if( (nRow < 0) || (nRow >= implGetRowCount()) || (nColumn < 0) || (nColumn >= implGetColumnCount()) )
+ throw IndexOutOfBoundsException();
+}
+
+ScCsvGrid& ScAccessibleCsvGrid::implGetGrid() const
+{
+ return static_cast< ScCsvGrid& >( implGetControl() );
+}
+
+bool ScAccessibleCsvGrid::implIsColumnSelected( sal_Int32 nColumn ) const
+{
+ return (nColumn > 0) && implGetGrid().IsSelected( lcl_GetGridColumn( nColumn ) );
+}
+
+void ScAccessibleCsvGrid::implSelectColumn( sal_Int32 nColumn, bool bSelect )
+{
+ if( nColumn > 0 )
+ implGetGrid().Select( lcl_GetGridColumn( nColumn ), bSelect );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetRowCount() const
+{
+ return static_cast< sal_Int32 >( implGetGrid().GetLastVisLine() - implGetGrid().GetFirstVisLine() + 2 );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetColumnCount() const
+{
+ return static_cast< sal_Int32 >( implGetGrid().GetColumnCount() + 1 );
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetSelColumnCount() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nCount = 0;
+ for( sal_uInt32 nColIx = rGrid.GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = rGrid.GetNextSelected( nColIx ) )
+ ++nCount;
+ return nCount;
+}
+
+sal_Int32 ScAccessibleCsvGrid::implGetSelColumn( sal_Int32 nSelColumn ) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nColumn = 0;
+ for( sal_uInt32 nColIx = rGrid.GetFirstSelected(); nColIx != CSV_COLUMN_INVALID; nColIx = rGrid.GetNextSelected( nColIx ) )
+ {
+ if( nColumn == nSelColumn )
+ return static_cast< sal_Int32 >( nColIx + 1 );
+ ++nColumn;
+ }
+ return 0;
+}
+
+OUString ScAccessibleCsvGrid::implGetCellText( sal_Int32 nRow, sal_Int32 nColumn ) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ sal_Int32 nLine = nRow + rGrid.GetFirstVisLine() - 1;
+ OUString aCellStr;
+ if( (nColumn > 0) && (nRow > 0) )
+ aCellStr = rGrid.GetCellText( lcl_GetGridColumn( nColumn ), nLine );
+ else if( nRow > 0 )
+ aCellStr = OUString::number( nLine + 1 );
+ else if( nColumn > 0 )
+ aCellStr = rGrid.GetColumnTypeName( lcl_GetGridColumn( nColumn ) );
+ return aCellStr;
+}
+
+rtl::Reference<ScAccessibleCsvCell> ScAccessibleCsvGrid::implCreateCellObj( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return new ScAccessibleCsvCell(implGetGrid(), implGetCellText(nRow, nColumn), nRow, nColumn);
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvGrid::getAccessibleParent()
+{
+ return implGetControl().GetDrawingArea()->get_accessible_parent();
+}
+
+ScAccessibleCsvCell::ScAccessibleCsvCell(
+ ScCsvGrid& rGrid,
+ OUString aCellText,
+ sal_Int32 nRow, sal_Int32 nColumn ) :
+ ImplInheritanceHelper( rGrid ),
+ AccessibleStaticTextBase( SvxEditSourcePtr() ),
+ maCellText(std::move( aCellText )),
+ mnLine( nRow ? (nRow + rGrid.GetFirstVisLine() - 1) : CSV_LINE_HEADER ),
+ mnColumn( lcl_GetGridColumn( nColumn ) ),
+ mnIndex( nRow * (rGrid.GetColumnCount() + 1) + nColumn )
+{
+ SetEditSource( implCreateEditSource() );
+}
+
+ScAccessibleCsvCell::~ScAccessibleCsvCell()
+{
+}
+
+void SAL_CALL ScAccessibleCsvCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ SetEditSource( SvxEditSourcePtr() );
+ ScAccessibleCsvControl::disposing();
+}
+
+// XAccessibleComponent -------------------------------------------------------
+
+void SAL_CALL ScAccessibleCsvCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ ScCsvGrid& rGrid = implGetGrid();
+ rGrid.Execute( CSVCMD_MOVEGRIDCURSOR, rGrid.GetColumnPos( mnColumn ) );
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvCell::getForeground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(Application::GetSettings().GetStyleSettings().GetButtonTextColor());
+}
+
+sal_Int32 SAL_CALL ScAccessibleCsvCell::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+// XAccessibleContext -----------------------------------------------------
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleChildCount()
+{
+ return AccessibleStaticTextBase::getAccessibleChildCount();
+}
+
+Reference< XAccessible > SAL_CALL ScAccessibleCsvCell::getAccessibleChild( sal_Int64 nIndex )
+{
+ return AccessibleStaticTextBase::getAccessibleChild( nIndex );
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return mnIndex;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL ScAccessibleCsvCell::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ ensureAlive();
+ return new AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleCsvCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = implCreateStateSet();
+ if( isAlive() )
+ {
+ const ScCsvGrid& rGrid = implGetGrid();
+ nStateSet |= AccessibleStateType::SINGLE_LINE;
+ if( mnColumn != CSV_COLUMN_HEADER )
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if( rGrid.HasFocus() && (rGrid.GetFocusColumn() == mnColumn) && (mnLine == CSV_LINE_HEADER) )
+ nStateSet |= AccessibleStateType::ACTIVE;
+ if( rGrid.IsSelected( mnColumn ) )
+ nStateSet |= AccessibleStateType::SELECTED;
+ }
+ return nStateSet;
+}
+
+// XInterface -----------------------------------------------------------------
+
+IMPLEMENT_FORWARD_XINTERFACE2( ScAccessibleCsvCell, ImplInheritanceHelper, AccessibleStaticTextBase )
+
+// XTypeProvider --------------------------------------------------------------
+
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( ScAccessibleCsvCell, ImplInheritanceHelper, AccessibleStaticTextBase )
+
+// helpers --------------------------------------------------------------------
+
+OUString SAL_CALL ScAccessibleCsvCell::getAccessibleName()
+{
+ return maCellText;
+}
+
+OUString SAL_CALL ScAccessibleCsvCell::getAccessibleDescription()
+{
+ return OUString();
+}
+
+ScCsvGrid& ScAccessibleCsvCell::implGetGrid() const
+{
+ return static_cast< ScCsvGrid& >( implGetControl() );
+}
+
+Point ScAccessibleCsvCell::implGetRealPos() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return Point(
+ (mnColumn == CSV_COLUMN_HEADER) ? rGrid.GetHdrX() : rGrid.GetColumnX( mnColumn ),
+ (mnLine == CSV_LINE_HEADER) ? 0 : rGrid.GetY( mnLine ) );
+}
+
+sal_uInt32 ScAccessibleCsvCell::implCalcPixelWidth(sal_uInt32 nChars) const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return rGrid.GetCharWidth() * nChars;
+}
+
+Size ScAccessibleCsvCell::implGetRealSize() const
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ return Size(
+ (mnColumn == CSV_COLUMN_HEADER) ? rGrid.GetHdrWidth() : implCalcPixelWidth( rGrid.GetColumnWidth( mnColumn ) ),
+ (mnLine == CSV_LINE_HEADER) ? rGrid.GetHdrHeight() : rGrid.GetLineHeight() );
+}
+
+css::awt::Rectangle ScAccessibleCsvCell::implGetBounds()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+ tools::Rectangle aClipRect( Point( 0, 0 ), rGrid.GetOutputSizePixel() );
+ if( mnColumn != CSV_COLUMN_HEADER )
+ {
+ aClipRect.SetLeft( rGrid.GetFirstX() );
+ aClipRect.SetRight( rGrid.GetLastX() );
+ }
+ if( mnLine != CSV_LINE_HEADER )
+ aClipRect.SetTop( rGrid.GetHdrHeight() );
+
+ tools::Rectangle aRect( implGetRealPos(), implGetRealSize() );
+ aRect.Intersection( aClipRect );
+ if( aRect.IsEmpty() )
+ aRect.SetSize( Size( -1, -1 ) );
+
+ return css::awt::Rectangle(aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight());
+}
+
+::std::unique_ptr< SvxEditSource > ScAccessibleCsvCell::implCreateEditSource()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+
+ ::std::unique_ptr< SvxEditSource > pEditSource( new ScAccessibilityEditSource( std::make_unique<ScAccessibleCsvTextData>(&rGrid.GetDrawingArea()->get_ref_device(), rGrid.GetEditEngine(), maCellText, implGetRealSize()) ) );
+ return pEditSource;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> SAL_CALL ScAccessibleCsvCell::getAccessibleParent()
+{
+ ScCsvGrid& rGrid = implGetGrid();
+
+ ScAccessibleCsvGrid* pAcc = static_cast<ScAccessibleCsvGrid*>(rGrid.GetAccessible());
+
+ return pAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocument.cxx b/sc/source/ui/Accessibility/AccessibleDocument.cxx
new file mode 100644
index 0000000000..c309f1b1fe
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocument.cxx
@@ -0,0 +1,2222 @@
+/* -*- 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 <AccessibleDocument.hxx>
+#include <AccessibleSpreadsheet.hxx>
+#include <tabvwsh.hxx>
+#include <AccessibilityHints.hxx>
+#include <document.hxx>
+#include <drwlayer.hxx>
+#include <DrawModelBroadcaster.hxx>
+#include <drawview.hxx>
+#include <gridwin.hxx>
+#include <AccessibleEditObject.hxx>
+#include <userdat.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <markdata.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/drawing/ShapeCollection.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <tools/gen.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <comphelper/sequence.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/docfile.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <sfx2/objsh.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <algorithm>
+
+#include <scmod.hxx>
+
+#ifdef indices
+#undef indices
+#endif
+
+#ifdef extents
+#undef extents
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+ //===== internal ========================================================
+
+namespace {
+
+struct ScAccessibleShapeData
+{
+ ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_);
+ ~ScAccessibleShapeData();
+ mutable rtl::Reference< ::accessibility::AccessibleShape > pAccShape;
+ mutable std::optional<ScAddress> xRelationCell; // if it is NULL this shape is anchored on the table
+ css::uno::Reference< css::drawing::XShape > xShape;
+ mutable bool bSelected;
+ bool bSelectable;
+ // cache these to make the sorting cheaper
+ std::optional<sal_Int16> mxLayerID;
+ std::optional<sal_Int32> mxZOrder;
+};
+
+}
+
+ScAccessibleShapeData::ScAccessibleShapeData(css::uno::Reference< css::drawing::XShape > xShape_)
+ : xShape(std::move(xShape_)),
+ bSelected(false), bSelectable(true)
+{
+ static constexpr OUStringLiteral gsLayerId = u"LayerID";
+ static constexpr OUStringLiteral gsZOrder = u"ZOrder";
+ uno::Reference< beans::XPropertySet> xProps(xShape, uno::UNO_QUERY);
+ if (xProps.is())
+ {
+ uno::Any aAny = xProps->getPropertyValue(gsLayerId);
+ sal_Int16 nLayerID;
+ if (aAny >>= nLayerID)
+ mxLayerID = nLayerID;
+ sal_Int32 nZOrder;
+ aAny = xProps->getPropertyValue(gsZOrder);
+ if (aAny >>= nZOrder)
+ mxZOrder = nZOrder;
+ }
+}
+
+ScAccessibleShapeData::~ScAccessibleShapeData()
+{
+ if (pAccShape.is())
+ {
+ pAccShape->dispose();
+ }
+}
+
+namespace {
+
+struct ScShapeDataLess
+{
+ static void ConvertLayerId(sal_Int16& rLayerID) // changes the number of the LayerId so it the accessibility order
+ {
+ // note: MSVC 2017 ICE's if this is written as "switch" so use "if"
+ if (SC_LAYER_FRONT.get() == rLayerID)
+ {
+ rLayerID = 1;
+ }
+ else if (SC_LAYER_BACK.get() == rLayerID)
+ {
+ rLayerID = 0;
+ }
+ else if (SC_LAYER_INTERN.get() == rLayerID)
+ {
+ rLayerID = 2;
+ }
+ else if (SC_LAYER_CONTROLS.get() == rLayerID)
+ {
+ rLayerID = 3;
+ }
+ }
+ static bool LessThanSheet(const ScAccessibleShapeData* pData)
+ {
+ bool bResult(false);
+ if (pData->mxLayerID)
+ {
+ if (SdrLayerID(*pData->mxLayerID) == SC_LAYER_BACK)
+ bResult = true;
+ }
+ return bResult;
+ }
+ bool operator()(const ScAccessibleShapeData* pData1, const ScAccessibleShapeData* pData2) const
+ {
+ bool bResult(false);
+ if (pData1 && pData2)
+ {
+ if( pData1->mxLayerID && pData2->mxLayerID )
+ {
+ sal_Int16 nLayerID1 = *pData1->mxLayerID;
+ sal_Int16 nLayerID2 = *pData2->mxLayerID;
+ if (nLayerID1 == nLayerID2)
+ {
+ if ( pData1->mxZOrder && pData2->mxZOrder )
+ bResult = (*pData1->mxZOrder < *pData2->mxZOrder);
+ }
+ else
+ {
+ ConvertLayerId(nLayerID1);
+ ConvertLayerId(nLayerID2);
+ bResult = (nLayerID1 < nLayerID2);
+ }
+ }
+ }
+ else if (pData1 && !pData2)
+ bResult = LessThanSheet(pData1);
+ else if (!pData1 && pData2)
+ bResult = !LessThanSheet(pData2);
+ else
+ bResult = false;
+ return bResult;
+ }
+};
+
+}
+
+class ScChildrenShapes : public SfxListener,
+ public ::accessibility::IAccessibleParent
+{
+public:
+ ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
+ virtual ~ScChildrenShapes() override;
+
+ ///===== SfxListener =====================================================
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ ///===== IAccessibleParent ===============================================
+
+ virtual bool ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ virtual ::accessibility::AccessibleControlShape* GetAccControlShapeFromModel
+ (css::beans::XPropertySet* pSet) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible>
+ GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
+ ///===== Internal ========================================================
+ void SetDrawBroadcaster();
+
+ sal_Int32 GetCount() const;
+ uno::Reference< XAccessible > Get(const ScAccessibleShapeData* pData) const;
+ uno::Reference< XAccessible > Get(sal_Int32 nIndex) const;
+ uno::Reference< XAccessible > GetAt(const awt::Point& rPoint) const;
+
+ // gets the index of the shape starting on 0 (without the index of the table)
+ // returns the selected shape
+ bool IsSelected(sal_Int32 nIndex,
+ css::uno::Reference<css::drawing::XShape>& rShape) const;
+
+ bool SelectionChanged();
+
+ void Select(sal_Int32 nIndex);
+ void DeselectAll(); // deselect also the table
+ void SelectAll();
+ sal_Int32 GetSelectedCount() const;
+ uno::Reference< XAccessible > GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const;
+ void Deselect(sal_Int32 nChildIndex);
+
+ SdrPage* GetDrawPage() const;
+
+ rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAddress* pAddress) const;
+
+ void VisAreaChanged() const;
+private:
+ typedef std::vector<ScAccessibleShapeData*> SortedShapes;
+ typedef std::unordered_map<css::uno::Reference< css::drawing::XShape >, ScAccessibleShapeData*> ShapesMap;
+
+ mutable SortedShapes maZOrderedShapes; // a null pointer represents the sheet in the correct order
+ mutable ShapesMap maShapesMap;
+ mutable bool mbShapesNeedSorting; // set if maZOrderedShapes needs sorting
+
+ mutable ::accessibility::AccessibleShapeTreeInfo maShapeTreeInfo;
+ mutable css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier;
+ mutable sal_uInt32 mnShapesSelected;
+ ScTabViewShell* mpViewShell;
+ ScAccessibleDocument* mpAccessibleDocument;
+ ScSplitPos meSplitPos;
+
+ void FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const;
+ bool FindSelectedShapesChanges(const css::uno::Reference<css::drawing::XShapes>& xShapes) const;
+
+ std::optional<ScAddress> GetAnchor(const uno::Reference<drawing::XShape>& xShape) const;
+ rtl::Reference<utl::AccessibleRelationSetHelper> GetRelationSet(const ScAccessibleShapeData* pData) const;
+ void SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const;
+ void AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const;
+ void RemoveShape(const uno::Reference<drawing::XShape>& xShape) const;
+
+ bool FindShape(const uno::Reference<drawing::XShape>& xShape, SortedShapes::iterator& rItr) const;
+
+ static sal_Int8 Compare(const ScAccessibleShapeData* pData1,
+ const ScAccessibleShapeData* pData2);
+};
+
+ScChildrenShapes::ScChildrenShapes(ScAccessibleDocument* pAccessibleDocument, ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
+ :
+ mbShapesNeedSorting(false),
+ mnShapesSelected(0),
+ mpViewShell(pViewShell),
+ mpAccessibleDocument(pAccessibleDocument),
+ meSplitPos(eSplitPos)
+{
+ if (mpViewShell)
+ {
+ SfxViewFrame& rViewFrame = mpViewShell->GetViewFrame();
+ xSelectionSupplier = uno::Reference<view::XSelectionSupplier>(rViewFrame.GetFrame().GetController(), uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ xSelectionSupplier->addSelectionChangeListener(mpAccessibleDocument);
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ mnShapesSelected = xShapes->getCount();
+ }
+ }
+
+ maZOrderedShapes.push_back(nullptr); // add an element which represents the table
+
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (mnShapesSelected)
+ {
+ //set flag on every selected shape
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ FindSelectedShapesChanges(xShapes);
+ }
+ if (!pViewShell)
+ return;
+
+ ScViewData& rViewData = pViewShell->GetViewData();
+ SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ {
+ StartListening(*pDrawBC);
+
+ maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
+ maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
+ maShapeTreeInfo.SetController(nullptr);
+ maShapeTreeInfo.SetWindow(pViewShell->GetWindowByPos(meSplitPos));
+ maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
+ }
+}
+
+ScChildrenShapes::~ScChildrenShapes()
+{
+ for (ScAccessibleShapeData* pShapeData : maZOrderedShapes)
+ delete pShapeData;
+ if (mpViewShell)
+ {
+ SfxBroadcaster* pDrawBC = mpViewShell->GetViewData().GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ EndListening(*pDrawBC);
+ }
+ if (mpAccessibleDocument && xSelectionSupplier.is())
+ xSelectionSupplier->removeSelectionChangeListener(mpAccessibleDocument);
+}
+
+void ScChildrenShapes::SetDrawBroadcaster()
+{
+ if (!mpViewShell)
+ return;
+
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ SfxBroadcaster* pDrawBC = rViewData.GetDocument().GetDrawBroadcaster();
+ if (pDrawBC)
+ {
+ StartListening(*pDrawBC, DuplicateHandling::Prevent);
+
+ maShapeTreeInfo.SetModelBroadcaster( new ScDrawModelBroadcaster(rViewData.GetDocument().GetDrawLayer()) );
+ maShapeTreeInfo.SetSdrView(rViewData.GetScDrawView());
+ maShapeTreeInfo.SetController(nullptr);
+ maShapeTreeInfo.SetWindow(mpViewShell->GetWindowByPos(meSplitPos));
+ maShapeTreeInfo.SetViewForwarder(mpAccessibleDocument);
+ }
+}
+
+void ScChildrenShapes::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+
+ SdrObject* pObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ if (!(pObj && /*(pObj->GetLayer() != SC_LAYER_INTERN) && */(pObj->getSdrPageFromSdrObject() == GetDrawPage()) &&
+ (pObj->getSdrPageFromSdrObject() == pObj->getParentSdrObjListFromSdrObject())) ) //only do something if the object lies direct on the page
+ return;
+
+ switch (pSdrHint->GetKind())
+ {
+ case SdrHintKind::ObjectChange : // object changed
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ {
+ mbShapesNeedSorting = true; // sort, because the z index or layer could be changed
+ auto it = maShapesMap.find(xShape);
+ if (it != maShapesMap.end())
+ SetAnchor(xShape, it->second);
+ }
+ }
+ break;
+ case SdrHintKind::ObjectInserted : // new drawing object inserted
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ AddShape(xShape, true);
+ }
+ break;
+ case SdrHintKind::ObjectRemoved : // Removed drawing object from list
+ {
+ uno::Reference<drawing::XShape> xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ RemoveShape(xShape);
+ }
+ break;
+ default :
+ {
+ // other events are not interesting
+ }
+ break;
+ }
+}
+
+bool ScChildrenShapes::ReplaceChild (::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/, const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo)
+{
+ // create the new child
+ rtl::Reference< ::accessibility::AccessibleShape > pReplacement(::accessibility::ShapeTypeHandler::Instance().CreateAccessibleObject (
+ ::accessibility::AccessibleShapeInfo ( _rxShape, pCurrentChild->getAccessibleParent(), this ),
+ _rShapeTreeInfo
+ ));
+
+ bool bResult(false);
+ if (pReplacement.is())
+ {
+ OSL_ENSURE(pCurrentChild->GetXShape().get() == pReplacement->GetXShape().get(), "XShape changes and should be inserted sorted");
+ auto it = maShapesMap.find(pCurrentChild->GetXShape());
+ if (it != maShapesMap.end() && it->second->pAccShape.is())
+ {
+ OSL_ENSURE(it->second->pAccShape == pCurrentChild, "wrong child found");
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(pCurrentChild);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
+
+ pCurrentChild->dispose();
+ }
+
+ // Init after above possible pCurrentChild->dispose so we don't trigger the assert
+ // ScDrawModelBroadcaster::addShapeEventListener of duplicate listeners
+ pReplacement->Init();
+
+ if (it != maShapesMap.end())
+ {
+ it->second->pAccShape = pReplacement;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(pReplacement);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is new - event
+ bResult = true;
+ }
+ }
+ return bResult;
+}
+
+::accessibility::AccessibleControlShape * ScChildrenShapes::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ GetCount(); // populate
+ for (ScAccessibleShapeData* pShape : maZOrderedShapes)
+ {
+ if (pShape)
+ {
+ rtl::Reference< ::accessibility::AccessibleShape > pAccShape(pShape->pAccShape);
+ if (pAccShape.is() && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
+ {
+ ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape.get());
+ if (pCtlAccShape && pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Reference < css::accessibility::XAccessible >
+ScChildrenShapes::GetAccessibleCaption (const css::uno::Reference < css::drawing::XShape>& xShape)
+{
+ GetCount(); // populate
+ auto it = maShapesMap.find(xShape);
+ if (it == maShapesMap.end())
+ return nullptr;
+ ScAccessibleShapeData* pShape = it->second;
+ css::uno::Reference< css::accessibility::XAccessible > xNewChild( pShape->pAccShape );
+ if(xNewChild)
+ return xNewChild;
+ return nullptr;
+}
+
+sal_Int32 ScChildrenShapes::GetCount() const
+{
+ SdrPage* pDrawPage = GetDrawPage();
+ if (pDrawPage && (maZOrderedShapes.size() == 1)) // the table is always in
+ {
+ size_t nSdrObjCount = pDrawPage->GetObjCount();
+ maZOrderedShapes.reserve(nSdrObjCount + 1); // the table is always in
+ for (const rtl::Reference<SdrObject>& pObj : *pDrawPage)
+ {
+ uno::Reference< drawing::XShape > xShape (pObj->getUnoShape(), uno::UNO_QUERY);
+ AddShape(xShape, false); //inserts in the correct order
+ }
+ }
+ return maZOrderedShapes.size();
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::Get(const ScAccessibleShapeData* pData) const
+{
+ if (!pData)
+ return nullptr;
+
+ if (!pData->pAccShape.is())
+ {
+ ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(pData->xShape, mpAccessibleDocument, const_cast<ScChildrenShapes*>(this));
+ pData->pAccShape = rShapeHandler.CreateAccessibleObject(
+ aShapeInfo, maShapeTreeInfo);
+ if (pData->pAccShape.is())
+ {
+ pData->pAccShape->Init();
+ if (pData->bSelected)
+ pData->pAccShape->SetState(AccessibleStateType::SELECTED);
+ if (!pData->bSelectable)
+ pData->pAccShape->ResetState(AccessibleStateType::SELECTABLE);
+ pData->pAccShape->SetRelationSet(GetRelationSet(pData));
+ }
+ }
+ return pData->pAccShape;
+ }
+
+uno::Reference< XAccessible > ScChildrenShapes::Get(sal_Int32 nIndex) const
+{
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (o3tl::make_unsigned(nIndex) >= maZOrderedShapes.size())
+ return nullptr;
+
+ return Get(maZOrderedShapes[nIndex]);
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::GetAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ if(mpViewShell)
+ {
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ sal_Int32 i(maZOrderedShapes.size() - 1);
+ bool bFound(false);
+ while (!bFound && i >= 0)
+ {
+ ScAccessibleShapeData* pShape = maZOrderedShapes[i];
+ if (pShape)
+ {
+ if (!pShape->pAccShape.is())
+ Get(pShape);
+
+ if (pShape->pAccShape.is())
+ {
+ Point aPoint(VCLPoint(rPoint));
+ aPoint -= VCLRectangle(pShape->pAccShape->getBounds()).TopLeft();
+ if (pShape->pAccShape->containsPoint(AWTPoint(aPoint)))
+ {
+ xAccessible = pShape->pAccShape.get();
+ bFound = true;
+ }
+ }
+ else
+ {
+ OSL_FAIL("I should have an accessible shape now!");
+ }
+ }
+ else
+ bFound = true; // this is the sheet and it lies before the rest of the shapes which are background shapes
+
+ --i;
+ }
+ }
+ return xAccessible;
+}
+
+bool ScChildrenShapes::IsSelected(sal_Int32 nIndex,
+ uno::Reference<drawing::XShape>& rShape) const
+{
+ bool bResult (false);
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (!maZOrderedShapes[nIndex])
+ return false;
+
+ bResult = maZOrderedShapes[nIndex]->bSelected;
+ rShape = maZOrderedShapes[nIndex]->xShape;
+
+#if OSL_DEBUG_LEVEL > 0 // test whether it is truly selected by a slower method
+ uno::Reference< drawing::XShape > xReturnShape;
+ bool bDebugResult(false);
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ if (xShapes.is())
+ {
+ sal_Int32 nCount(xShapes->getCount());
+ if (nCount)
+ {
+ uno::Reference< drawing::XShape > xShape;
+ uno::Reference< drawing::XShape > xIndexShape = maZOrderedShapes[nIndex]->xShape;
+ sal_Int32 i(0);
+ while (!bDebugResult && (i < nCount))
+ {
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is() && (xIndexShape.get() == xShape.get()))
+ {
+ bDebugResult = true;
+ xReturnShape = xShape;
+ }
+ else
+ ++i;
+ }
+ }
+ }
+ OSL_ENSURE((bResult == bDebugResult) && ((bResult && (rShape.get() == xReturnShape.get())) || !bResult), "found the wrong shape or result");
+#endif
+
+ return bResult;
+}
+
+bool ScChildrenShapes::SelectionChanged()
+{
+ bool bResult(false);
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ bResult = FindSelectedShapesChanges(xShapes);
+
+ return bResult;
+}
+
+void ScChildrenShapes::Select(sal_Int32 nIndex)
+{
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+
+ if (!maZOrderedShapes[nIndex])
+ return;
+
+ uno::Reference<drawing::XShape> xShape;
+ if (IsSelected(nIndex, xShape) || !maZOrderedShapes[nIndex]->bSelectable)
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+
+ if (!xShapes.is())
+ xShapes = drawing::ShapeCollection::create(
+ comphelper::getProcessComponentContext());
+
+ xShapes->add(maZOrderedShapes[nIndex]->xShape);
+
+ try
+ {
+ xSelectionSupplier->select(uno::Any(xShapes));
+ maZOrderedShapes[nIndex]->bSelected = true;
+ if (maZOrderedShapes[nIndex]->pAccShape.is())
+ maZOrderedShapes[nIndex]->pAccShape->SetState(AccessibleStateType::SELECTED);
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ }
+}
+
+void ScChildrenShapes::DeselectAll()
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ bool bSomethingSelected(true);
+ try
+ {
+ xSelectionSupplier->select(uno::Any()); //deselects all
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ OSL_FAIL("nothing selected before");
+ bSomethingSelected = false;
+ }
+
+ if (bSomethingSelected)
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ if (pAccShapeData)
+ {
+ pAccShapeData->bSelected = false;
+ if (pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->ResetState(AccessibleStateType::SELECTED);
+ }
+};
+
+
+void ScChildrenShapes::SelectAll()
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with filtered shapes (no internal shapes)
+
+ if (maZOrderedShapes.size() <= 1)
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes = drawing::ShapeCollection::create(
+ comphelper::getProcessComponentContext());
+
+ try
+ {
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ {
+ if (pAccShapeData && pAccShapeData->bSelectable)
+ {
+ pAccShapeData->bSelected = true;
+ if (pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->SetState(AccessibleStateType::SELECTED);
+ if (xShapes.is())
+ xShapes->add(pAccShapeData->xShape);
+ }
+ }
+ xSelectionSupplier->select(uno::Any(xShapes));
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ SelectionChanged(); // find all selected shapes and set the flags
+ }
+}
+
+void ScChildrenShapes::FillShapes(std::vector < uno::Reference < drawing::XShape > >& rShapes) const
+{
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ {
+ sal_uInt32 nCount(xShapes->getCount());
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ uno::Reference<drawing::XShape> xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ rShapes.push_back(xShape);
+ }
+ }
+}
+
+sal_Int32 ScChildrenShapes::GetSelectedCount() const
+{
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ std::vector < uno::Reference < drawing::XShape > > aShapes;
+ FillShapes(aShapes);
+
+ return aShapes.size();
+}
+
+uno::Reference< XAccessible > ScChildrenShapes::GetSelected(sal_Int32 nSelectedChildIndex, bool bTabSelected) const
+{
+ uno::Reference< XAccessible > xAccessible;
+
+ if (maZOrderedShapes.size() <= 1)
+ GetCount(); // fill list with shapes
+
+ if (!bTabSelected)
+ {
+ std::vector < uno::Reference < drawing::XShape > > aShapes;
+ FillShapes(aShapes);
+
+ if (nSelectedChildIndex < 0 || o3tl::make_unsigned(nSelectedChildIndex) >= aShapes.size())
+ return xAccessible;
+
+ SortedShapes::iterator aItr;
+ if (FindShape(aShapes[nSelectedChildIndex], aItr))
+ xAccessible = Get(*aItr);
+ }
+ else
+ {
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ for(const auto& rpShape : maZOrderedShapes)
+ {
+ if (!rpShape || rpShape->bSelected)
+ {
+ if (nSelectedChildIndex == 0)
+ {
+ if (rpShape)
+ xAccessible = rpShape->pAccShape.get();
+ break;
+ }
+ else
+ --nSelectedChildIndex;
+ }
+ }
+ }
+
+ return xAccessible;
+}
+
+void ScChildrenShapes::Deselect(sal_Int32 nChildIndex)
+{
+ uno::Reference<drawing::XShape> xShape;
+ if (!IsSelected(nChildIndex, xShape)) // returns false if it is the sheet
+ return;
+
+ if (!xShape.is())
+ return;
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ if (xShapes.is())
+ xShapes->remove(xShape);
+
+ try
+ {
+ xSelectionSupplier->select(uno::Any(xShapes));
+ }
+ catch (lang::IllegalArgumentException&)
+ {
+ OSL_FAIL("something not selectable");
+ }
+
+ maZOrderedShapes[nChildIndex]->bSelected = false;
+ if (maZOrderedShapes[nChildIndex]->pAccShape.is())
+ maZOrderedShapes[nChildIndex]->pAccShape->ResetState(AccessibleStateType::SELECTED);
+}
+
+SdrPage* ScChildrenShapes::GetDrawPage() const
+{
+ SCTAB nTab(mpAccessibleDocument->getVisibleTable());
+ SdrPage* pDrawPage = nullptr;
+ if (mpViewShell)
+ {
+ ScDocument& rDoc = mpViewShell->GetViewData().GetDocument();
+ if (ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer())
+ {
+ if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
+ pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
+ }
+ }
+ return pDrawPage;
+}
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAddress* pAddress) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ for (const ScAccessibleShapeData* pAccShapeData : maZOrderedShapes)
+ {
+ if (pAccShapeData &&
+ ((!pAccShapeData->xRelationCell && !pAddress) ||
+ (pAccShapeData->xRelationCell && pAddress && (*(pAccShapeData->xRelationCell) == *pAddress))))
+ {
+ if (!pRelationSet)
+ pRelationSet = new utl::AccessibleRelationSetHelper();
+
+ AccessibleRelation aRelation;
+ aRelation.TargetSet = { Get(pAccShapeData) };
+ aRelation.RelationType = AccessibleRelationType::CONTROLLER_FOR;
+
+ pRelationSet->AddRelation(aRelation);
+ }
+ }
+ return pRelationSet;
+}
+
+bool ScChildrenShapes::FindSelectedShapesChanges(const uno::Reference<drawing::XShapes>& xShapes) const
+{
+ bool bResult(false);
+ SortedShapes aShapesList;
+ if (xShapes.is())
+ {
+ mnShapesSelected = xShapes->getCount();
+ for (sal_uInt32 i = 0; i < mnShapesSelected; ++i)
+ {
+ uno::Reference< drawing::XShape > xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ {
+ ScAccessibleShapeData* pShapeData = new ScAccessibleShapeData(xShape);
+ aShapesList.push_back(pShapeData);
+ }
+ }
+ }
+ else
+ mnShapesSelected = 0;
+ SdrObject *pFocusedObj = nullptr;
+ if( mnShapesSelected == 1 && aShapesList.size() == 1)
+ {
+ pFocusedObj = SdrObject::getSdrObjectFromXShape(aShapesList[0]->xShape);
+ }
+ std::sort(aShapesList.begin(), aShapesList.end(), ScShapeDataLess());
+ SortedShapes vecSelectedShapeAdd;
+ SortedShapes vecSelectedShapeRemove;
+ bool bHasSelect=false;
+ SortedShapes::iterator aXShapesItr(aShapesList.begin());
+ SortedShapes::const_iterator aXShapesEndItr(aShapesList.end());
+ SortedShapes::iterator aDataItr(maZOrderedShapes.begin());
+ SortedShapes::const_iterator aDataEndItr(maZOrderedShapes.end());
+ SortedShapes::const_iterator aFocusedItr = aDataEndItr;
+ while(aDataItr != aDataEndItr)
+ {
+ if (*aDataItr) // is it really a shape or only the sheet
+ {
+ sal_Int8 nComp(0);
+ if (aXShapesItr == aXShapesEndItr)
+ nComp = -1; // simulate that the Shape is lower, so the selection state will be removed
+ else
+ nComp = Compare(*aDataItr, *aXShapesItr);
+ if (nComp == 0)
+ {
+ if (!(*aDataItr)->bSelected)
+ {
+ (*aDataItr)->bSelected = true;
+ if ((*aDataItr)->pAccShape.is())
+ {
+ (*aDataItr)->pAccShape->SetState(AccessibleStateType::SELECTED);
+ (*aDataItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
+ bResult = true;
+ vecSelectedShapeAdd.push_back(*aDataItr);
+ }
+ aFocusedItr = aDataItr;
+ }
+ else
+ {
+ bHasSelect = true;
+ }
+ ++aDataItr;
+ ++aXShapesItr;
+ }
+ else if (nComp < 0)
+ {
+ if ((*aDataItr)->bSelected)
+ {
+ (*aDataItr)->bSelected = false;
+ if ((*aDataItr)->pAccShape.is())
+ {
+ (*aDataItr)->pAccShape->ResetState(AccessibleStateType::SELECTED);
+ (*aDataItr)->pAccShape->ResetState(AccessibleStateType::FOCUSED);
+ bResult = true;
+ vecSelectedShapeRemove.push_back(*aDataItr);
+ }
+ }
+ ++aDataItr;
+ }
+ else
+ {
+ OSL_FAIL("here is a selected shape which is not in the childlist");
+ ++aXShapesItr;
+ --mnShapesSelected;
+ }
+ }
+ else
+ ++aDataItr;
+ }
+ bool bWinFocus=false;
+ if (mpViewShell)
+ {
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ {
+ bWinFocus = pWin->HasFocus();
+ }
+ }
+ const SdrMarkList* pMarkList = nullptr;
+ SdrObject* pMarkedObj = nullptr;
+ bool bIsFocuseMarked = true;
+ if( mpViewShell && mnShapesSelected == 1 && bWinFocus)
+ {
+ ScDrawView* pScDrawView = mpViewShell->GetViewData().GetScDrawView();
+ if( pScDrawView )
+ {
+ if( pScDrawView->GetMarkedObjectList().GetMarkCount() == 1 )
+ {
+ pMarkList = &(pScDrawView->GetMarkedObjectList());
+ pMarkedObj = pMarkList->GetMark(0)->GetMarkedSdrObj();
+ uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
+ if( aFocusedItr != aDataEndItr &&
+ (*aFocusedItr)->xShape.is() &&
+ xMarkedXShape.is() &&
+ (*aFocusedItr)->xShape != xMarkedXShape )
+ bIsFocuseMarked = false;
+ }
+ }
+ }
+ //if ((aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1))
+ if ( bIsFocuseMarked && (aFocusedItr != aDataEndItr) && (*aFocusedItr)->pAccShape.is() && (mnShapesSelected == 1) && bWinFocus)
+ {
+ (*aFocusedItr)->pAccShape->SetState(AccessibleStateType::FOCUSED);
+ }
+ else if( pFocusedObj && bWinFocus && pMarkList && pMarkList->GetMarkCount() == 1 && mnShapesSelected == 1 )
+ {
+ if( pMarkedObj )
+ {
+ uno::Reference< drawing::XShape > xMarkedXShape (pMarkedObj->getUnoShape(), uno::UNO_QUERY);
+ SdrObject* pUpObj = pMarkedObj->getParentSdrObjectFromSdrObject();
+
+ if( pMarkedObj == pFocusedObj && pUpObj )
+ {
+ uno::Reference< drawing::XShape > xUpGroupXShape (pUpObj->getUnoShape(), uno::UNO_QUERY);
+ uno::Reference < XAccessible > xAccGroupShape =
+ const_cast<ScChildrenShapes*>(this)->GetAccessibleCaption( xUpGroupXShape );
+ if( xAccGroupShape.is() )
+ {
+ ::accessibility::AccessibleShape* pAccGroupShape =
+ static_cast< ::accessibility::AccessibleShape* >(xAccGroupShape.get());
+ if( pAccGroupShape )
+ {
+ sal_Int64 nCount = pAccGroupShape->getAccessibleChildCount();
+ for( sal_Int64 i = 0; i < nCount; i++ )
+ {
+ uno::Reference<XAccessible> xAccShape = pAccGroupShape->getAccessibleChild(i);
+ if (xAccShape.is())
+ {
+ ::accessibility::AccessibleShape* pChildAccShape = static_cast< ::accessibility::AccessibleShape* >(xAccShape.get());
+ uno::Reference< drawing::XShape > xChildShape = pChildAccShape->GetXShape();
+ if (xChildShape == xMarkedXShape)
+ {
+ pChildAccShape->SetState(AccessibleStateType::FOCUSED);
+ }
+ else
+ {
+ pChildAccShape->ResetState(AccessibleStateType::FOCUSED);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (vecSelectedShapeAdd.size() >= 10 )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ else
+ {
+ for (const auto& rpShape : vecSelectedShapeAdd)
+ {
+ AccessibleEventObject aEvent;
+ if (bHasSelect)
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ }
+ else
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ }
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ uno::Reference< XAccessible > xChild( rpShape->pAccShape );
+ aEvent.NewValue <<= xChild;
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ }
+ for (const auto& rpShape : vecSelectedShapeRemove)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.Source = uno::Reference< XAccessible >(mpAccessibleDocument);
+ uno::Reference< XAccessible > xChild( rpShape->pAccShape );
+ aEvent.NewValue <<= xChild;
+ mpAccessibleDocument->CommitChange(aEvent);
+ }
+ for(ScAccessibleShapeData*& pShapeData : aShapesList)
+ {
+ delete pShapeData;
+ pShapeData = nullptr;
+ }
+ return bResult;
+}
+
+std::optional<ScAddress> ScChildrenShapes::GetAnchor(const uno::Reference<drawing::XShape>& xShape) const
+{
+ if (mpViewShell)
+ {
+ SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
+ uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
+ if (pSdrObj && xShapeProp.is())
+ {
+ if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjData(pSdrObj))
+ return std::optional<ScAddress>(pAnchor->maStart);
+ }
+ }
+
+ return std::optional<ScAddress>();
+}
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScChildrenShapes::GetRelationSet(const ScAccessibleShapeData* pData) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper();
+
+ if (pData && mpAccessibleDocument)
+ {
+ uno::Reference<XAccessible> xAccessible = mpAccessibleDocument->GetAccessibleSpreadsheet(); // should be the current table
+ if (pData->xRelationCell && xAccessible.is())
+ {
+ sal_Int32 nRow = pData->xRelationCell->Row();
+ sal_Int32 nColumn = pData->xRelationCell->Col();
+ bool bPositionUnset = nRow == -1 && nColumn == -1;
+ if (!bPositionUnset)
+ {
+ uno::Reference<XAccessibleTable> xAccTable(xAccessible->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccTable.is())
+ xAccessible = xAccTable->getAccessibleCellAt(nRow, nColumn);
+ }
+ }
+ AccessibleRelation aRelation;
+ aRelation.TargetSet = { xAccessible };
+ aRelation.RelationType = AccessibleRelationType::CONTROLLED_BY;
+ pRelationSet->AddRelation(aRelation);
+ }
+
+ return pRelationSet;
+}
+
+void ScChildrenShapes::SetAnchor(const uno::Reference<drawing::XShape>& xShape, ScAccessibleShapeData* pData) const
+{
+ if (pData)
+ {
+ std::optional<ScAddress> xAddress = GetAnchor(xShape);
+ if ((xAddress && pData->xRelationCell && (*xAddress != *(pData->xRelationCell))) ||
+ (!xAddress && pData->xRelationCell) || (xAddress && !pData->xRelationCell))
+ {
+ pData->xRelationCell = xAddress;
+ if (pData->pAccShape.is())
+ pData->pAccShape->SetRelationSet(GetRelationSet(pData));
+ }
+ }
+}
+
+void ScChildrenShapes::AddShape(const uno::Reference<drawing::XShape>& xShape, bool bCommitChange) const
+{
+ assert( maShapesMap.find(xShape) == maShapesMap.end());
+
+ ScAccessibleShapeData* pShape = new ScAccessibleShapeData(xShape);
+ maZOrderedShapes.push_back(pShape);
+ mbShapesNeedSorting = true;
+ maShapesMap[xShape] = pShape;
+ SetAnchor(xShape, pShape);
+
+ uno::Reference< beans::XPropertySet > xShapeProp(xShape, uno::UNO_QUERY);
+ if (xShapeProp.is())
+ {
+ uno::Any aPropAny = xShapeProp->getPropertyValue("LayerID");
+ sal_Int16 nLayerID = 0;
+ if( aPropAny >>= nLayerID )
+ {
+ if( (SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN) )
+ pShape->bSelectable = false;
+ else
+ pShape->bSelectable = true;
+ }
+ }
+
+ if (!xSelectionSupplier.is())
+ throw uno::RuntimeException();
+
+ uno::Reference<drawing::XShapes> xShapes(mpViewShell->getSelectedXShapes());
+ uno::Reference<container::XEnumerationAccess> xEnumAcc(xShapes, uno::UNO_QUERY);
+ if (xEnumAcc.is())
+ {
+ uno::Reference<container::XEnumeration> xEnum = xEnumAcc->createEnumeration();
+ if (xEnum.is())
+ {
+ uno::Reference<drawing::XShape> xSelectedShape;
+ bool bFound(false);
+ while (!bFound && xEnum->hasMoreElements())
+ {
+ xEnum->nextElement() >>= xSelectedShape;
+ if (xShape.is() && (xShape.get() == xSelectedShape.get()))
+ {
+ pShape->bSelected = true;
+ bFound = true;
+ }
+ }
+ }
+ }
+ if (mpAccessibleDocument && bCommitChange)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.NewValue <<= Get(pShape);
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // new child - event
+ }
+}
+
+void ScChildrenShapes::RemoveShape(const uno::Reference<drawing::XShape>& xShape) const
+{
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ SortedShapes::iterator aItr;
+ if (FindShape(xShape, aItr))
+ {
+ if (mpAccessibleDocument)
+ {
+ uno::Reference<XAccessible> xOldAccessible (Get(*aItr));
+
+ delete *aItr;
+ maShapesMap.erase((*aItr)->xShape);
+ maZOrderedShapes.erase(aItr);
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccessibleDocument);
+ aEvent.OldValue <<= xOldAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccessibleDocument->CommitChange(aEvent); // child is gone - event
+ }
+ else
+ {
+ delete *aItr;
+ maShapesMap.erase((*aItr)->xShape);
+ maZOrderedShapes.erase(aItr);
+ }
+ }
+ else
+ {
+ OSL_FAIL("shape was not in internal list");
+ }
+}
+
+bool ScChildrenShapes::FindShape(const uno::Reference<drawing::XShape>& xShape, ScChildrenShapes::SortedShapes::iterator& rItr) const
+{
+ if (mbShapesNeedSorting)
+ {
+ std::sort(maZOrderedShapes.begin(), maZOrderedShapes.end(), ScShapeDataLess());
+ mbShapesNeedSorting = false;
+ }
+ bool bResult(false);
+ ScAccessibleShapeData aShape(xShape);
+ rItr = std::lower_bound(maZOrderedShapes.begin(), maZOrderedShapes.end(), &aShape, ScShapeDataLess());
+ if ((rItr != maZOrderedShapes.end()) && (*rItr != nullptr) && ((*rItr)->xShape.get() == xShape.get()))
+ bResult = true; // if the shape is found
+
+#if OSL_DEBUG_LEVEL > 0 // test whether it finds truly the correct shape (perhaps it is not really sorted)
+ SortedShapes::iterator aDebugItr = std::find_if(maZOrderedShapes.begin(), maZOrderedShapes.end(),
+ [&xShape](const ScAccessibleShapeData* pShape) { return pShape && (pShape->xShape.get() == xShape.get()); });
+ bool bResult2 = (aDebugItr != maZOrderedShapes.end());
+ OSL_ENSURE((bResult == bResult2) && ((bResult && (rItr == aDebugItr)) || !bResult), "wrong Shape found");
+#endif
+ return bResult;
+}
+
+sal_Int8 ScChildrenShapes::Compare(const ScAccessibleShapeData* pData1,
+ const ScAccessibleShapeData* pData2)
+{
+ ScShapeDataLess aLess;
+
+ bool bResult1(aLess(pData1, pData2));
+ bool bResult2(aLess(pData2, pData1));
+
+ sal_Int8 nResult(0);
+ if (!bResult1 && bResult2)
+ nResult = 1;
+ else if (bResult1 && !bResult2)
+ nResult = -1;
+
+ return nResult;
+}
+
+void ScChildrenShapes::VisAreaChanged() const
+{
+ for (const ScAccessibleShapeData* pAccShapeData: maZOrderedShapes)
+ if (pAccShapeData && pAccShapeData->pAccShape.is())
+ pAccShapeData->pAccShape->ViewForwarderChanged();
+}
+
+ScAccessibleDocument::ScAccessibleDocument(
+ const uno::Reference<XAccessible>& rxParent,
+ ScTabViewShell* pViewShell,
+ ScSplitPos eSplitPos)
+ : ScAccessibleDocumentBase(rxParent),
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos),
+ mbCompleteSheetSelected(false)
+{
+ maVisArea = GetVisibleArea_Impl();
+}
+
+void ScAccessibleDocument::PreInit()
+{
+ if (!mpViewShell)
+ return;
+
+ mpViewShell->AddAccessibilityObject(*this);
+ vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
+ if( pWin )
+ {
+ pWin->AddChildEventListener( LINK( this, ScAccessibleDocument, 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->GetAccessible(), false );
+ }
+ }
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ if (rViewData.HasEditView(meSplitPos))
+ {
+ uno::Reference<XAccessible> xAcc = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
+ mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(), GetCurrentCellDescription(),
+ ScAccessibleEditObject::CellInEditMode);
+ AddChild(xAcc, false);
+ }
+}
+
+void ScAccessibleDocument::Init()
+{
+ if(!mpChildrenShapes)
+ mpChildrenShapes.reset( new ScChildrenShapes(this, mpViewShell, meSplitPos) );
+}
+
+ScAccessibleDocument::~ScAccessibleDocument()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleDocument::disposing()
+{
+ SolarMutexGuard aGuard;
+ FreeAccessibleSpreadsheet();
+ if (mpViewShell)
+ {
+ vcl::Window *pWin = mpViewShell->GetWindowByPos(meSplitPos);
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, ScAccessibleDocument, WindowChildEventListener ));
+
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpChildrenShapes.reset();
+
+ ScAccessibleDocumentBase::disposing();
+}
+
+void SAL_CALL ScAccessibleDocument::disposing( const lang::EventObject& /* Source */ )
+{
+ disposing();
+}
+
+ //===== SfxListener =====================================================
+
+IMPL_LINK( ScAccessibleDocument, 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->GetAccessible(), true );
+ }
+ }
+ 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->GetAccessible(), true );
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+void ScAccessibleDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (auto pFocusLostHint = dynamic_cast<const ScAccGridWinFocusLostHint*>(&rHint) )
+ {
+ if (pFocusLostHint->GetOldGridWin() == meSplitPos)
+ {
+ if (mxTempAcc.is() && mpTempAccEdit)
+ mpTempAccEdit->LostFocus();
+ else if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->LostFocus();
+ else
+ CommitFocusLost();
+ }
+ }
+ else if (auto pFocusGotHint = dynamic_cast<const ScAccGridWinFocusGotHint*>(&rHint) )
+ {
+ if (pFocusGotHint->GetNewGridWin() == meSplitPos)
+ {
+ uno::Reference<XAccessible> xAccessible;
+ if (mpChildrenShapes)
+ {
+ bool bTabMarked(IsTableSelected());
+ xAccessible = mpChildrenShapes->GetSelected(0, bTabMarked);
+ }
+ if( xAccessible.is() )
+ {
+ uno::Any aNewValue;
+ aNewValue<<=AccessibleStateType::FOCUSED;
+ static_cast< ::accessibility::AccessibleShape* >(xAccessible.get())->
+ CommitChange(AccessibleEventId::STATE_CHANGED,
+ aNewValue,
+ uno::Any(), -1 );
+ }
+ else
+ {
+ if (mxTempAcc.is() && mpTempAccEdit)
+ mpTempAccEdit->GotFocus();
+ else if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->GotFocus();
+ else
+ CommitFocusGained();
+ }
+ }
+ }
+ else
+ {
+ // only notify if child exist, otherwise it is not necessary
+ if ((rHint.GetId() == SfxHintId::ScAccTableChanged) &&
+ mpAccessibleSpreadsheet.is())
+ {
+ FreeAccessibleSpreadsheet();
+
+ // Shapes / form controls after reload not accessible, rebuild the
+ // mpChildrenShapes variable.
+ mpChildrenShapes.reset( new ScChildrenShapes( this, mpViewShell, meSplitPos ) );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::INVALIDATE_ALL_CHILDREN;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent); // all children changed
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->FireFirstCellFocus();
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccMakeDrawLayer)
+ {
+ if (mpChildrenShapes)
+ mpChildrenShapes->SetDrawBroadcaster();
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccEnterEditMode) // this event comes only on creating edit field of a cell
+ {
+ if (mpViewShell->GetViewData().GetEditActivePart() == meSplitPos)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ const EditEngine* pEditEng = rViewData.GetEditView(meSplitPos)->GetEditEngine();
+ if (pEditEng && pEditEng->IsUpdateLayout())
+ {
+ mpTempAccEdit = new ScAccessibleEditObject(this, rViewData.GetEditView(meSplitPos),
+ mpViewShell->GetWindowByPos(meSplitPos), GetCurrentCellName(),
+ ScResId(STR_ACC_EDITLINE_DESCR), ScAccessibleEditObject::CellInEditMode);
+ uno::Reference<XAccessible> xAcc = mpTempAccEdit;
+
+ AddChild(xAcc, true);
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->LostFocus();
+ else
+ CommitFocusLost();
+
+ mpTempAccEdit->GotFocus();
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccLeaveEditMode)
+ {
+ if (mxTempAcc.is())
+ {
+ if (mpTempAccEdit)
+ {
+ mpTempAccEdit->LostFocus();
+ }
+ RemoveChild(mxTempAcc, true);
+ if (mpTempAccEdit)
+ {
+ // tdf#125982 a11y use-after-free of editengine by
+ // ScAccessibleEditObjectTextData living past the
+ // the editengine of the editview passed in above
+ // in ScAccEnterEditMode
+ mpTempAccEdit->dispose();
+ mpTempAccEdit = nullptr;
+ }
+ if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
+ mpAccessibleSpreadsheet->GotFocus();
+ else if( mpViewShell && mpViewShell->IsActive())
+ CommitFocusGained();
+ }
+ }
+ else if ((rHint.GetId() == SfxHintId::ScAccVisAreaChanged) || (rHint.GetId() == SfxHintId::ScAccWindowResized))
+ {
+ tools::Rectangle aOldVisArea(maVisArea);
+ maVisArea = GetVisibleArea_Impl();
+
+ if (maVisArea != aOldVisArea)
+ {
+ if (maVisArea.GetSize() != aOldVisArea.GetSize())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+
+ if (mpAccessibleSpreadsheet.is())
+ mpAccessibleSpreadsheet->BoundingBoxChanged();
+ if (mpAccessibleSpreadsheet.is() && mpViewShell && mpViewShell->IsActive())
+ mpAccessibleSpreadsheet->FireFirstCellFocus();
+ }
+ else if (mpAccessibleSpreadsheet.is())
+ {
+ mpAccessibleSpreadsheet->VisAreaChanged();
+ }
+ if (mpChildrenShapes)
+ mpChildrenShapes->VisAreaChanged();
+ }
+ }
+ }
+
+ ScAccessibleDocumentBase::Notify(rBC, rHint);
+}
+
+void SAL_CALL ScAccessibleDocument::selectionChanged( const lang::EventObject& /* aEvent */ )
+{
+ bool bSelectionChanged(false);
+ if (mpAccessibleSpreadsheet.is())
+ {
+ bool bOldSelected(mbCompleteSheetSelected);
+ mbCompleteSheetSelected = IsTableSelected();
+ if (bOldSelected != mbCompleteSheetSelected)
+ {
+ mpAccessibleSpreadsheet->CompleteSelectionChanged(mbCompleteSheetSelected);
+ bSelectionChanged = true;
+ }
+ }
+
+ if (mpChildrenShapes && mpChildrenShapes->SelectionChanged())
+ bSelectionChanged = true;
+
+ if (bSelectionChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+ }
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleDocument::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessibleDocumentImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessibleDocument::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleDocument::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleDocument::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpChildrenShapes)
+ xAccessible = mpChildrenShapes->GetAt(rPoint);
+ if(!xAccessible.is())
+ {
+ if (mxTempAcc.is())
+ {
+ uno::Reference< XAccessibleContext > xCont(mxTempAcc->getAccessibleContext());
+ uno::Reference< XAccessibleComponent > xComp(xCont, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ tools::Rectangle aBound(VCLRectangle(xComp->getBounds()));
+ if (aBound.Contains(VCLPoint(rPoint)))
+ xAccessible = mxTempAcc;
+ }
+ }
+ if (!xAccessible.is())
+ xAccessible = GetAccessibleSpreadsheet();
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleDocument::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!getAccessibleParent().is())
+ return;
+
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ xAccessibleComponent->grabFocus();
+ // grab only focus if it does not have the focus and it is not hidden
+ if (mpViewShell &&
+ (mpViewShell->GetViewData().GetActivePart() != meSplitPos) &&
+ mpViewShell->GetWindowByPos(meSplitPos)->IsVisible())
+ {
+ mpViewShell->ActivatePart(meSplitPos);
+ }
+ }
+}
+
+ //===== XAccessibleContext ==============================================
+
+ /// Return the number of currently visible children.
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nCount(1);
+ if (mpChildrenShapes)
+ nCount = mpChildrenShapes->GetCount(); // returns the count of the shapes inclusive the table
+
+ if (mxTempAcc.is())
+ ++nCount;
+
+ return nCount;
+}
+
+ /// Return the specified child or NULL if index is invalid.
+uno::Reference<XAccessible> SAL_CALL
+ ScAccessibleDocument::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+ if (nIndex >= 0)
+ {
+ sal_Int64 nCount(1);
+ if (mpChildrenShapes)
+ {
+ xAccessible = mpChildrenShapes->Get(nIndex); // returns NULL if it is the table or out of range
+ nCount = mpChildrenShapes->GetCount(); //there is always a table
+ }
+ if (!xAccessible.is())
+ {
+ if (nIndex < nCount)
+ xAccessible = GetAccessibleSpreadsheet();
+ else if (nIndex == nCount && mxTempAcc.is())
+ xAccessible = mxTempAcc;
+ }
+ }
+
+ if (!xAccessible.is())
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+ /// Return the set of current states.
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+OUString SAL_CALL
+ ScAccessibleDocument::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
+ ScDocument* pScDoc = GetDocument();
+ if (!pScDoc)
+ return aName;
+
+ ScDocShell* pObjSh = pScDoc->GetDocumentShell();
+ if (!pObjSh)
+ return aName;
+
+ OUString aFileName;
+ SfxMedium* pMed = pObjSh->GetMedium();
+ if (pMed)
+ aFileName = pMed->GetName();
+
+ if (aFileName.isEmpty())
+ aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
+
+ if (!aFileName.isEmpty())
+ {
+ OUString aReadOnly;
+ if (pObjSh->IsReadOnly())
+ aReadOnly = ScResId(STR_ACC_DOC_SPREADSHEET_READONLY);
+
+ aName = aFileName + aReadOnly + " - " + aName;
+ }
+ return aName;
+}
+
+///===== XAccessibleSelection ===========================================
+
+void SAL_CALL
+ ScAccessibleDocument::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (!(mpChildrenShapes && mpViewShell))
+ return;
+
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ bool bWasTableSelected(IsTableSelected());
+ mpChildrenShapes->Select(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (bWasTableSelected)
+ mpViewShell->SelectAll();
+ }
+ else
+ {
+ mpViewShell->SelectAll();
+ }
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleDocument::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ bool bResult(false);
+
+ if (mpChildrenShapes)
+ {
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ uno::Reference<drawing::XShape> xShape;
+ bResult = mpChildrenShapes->IsSelected(nChildIndex, xShape); // throws no lang::IndexOutOfBoundsException if Index is too high
+ }
+ else
+ {
+ if (mxTempAcc.is() && nChildIndex == nCount)
+ bResult = true;
+ else
+ bResult = IsTableSelected();
+ }
+ }
+ return bResult;
+}
+
+void SAL_CALL
+ ScAccessibleDocument::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (mpChildrenShapes)
+ mpChildrenShapes->DeselectAll(); //deselects all (also the table)
+}
+
+void SAL_CALL
+ ScAccessibleDocument::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (mpChildrenShapes)
+ mpChildrenShapes->SelectAll();
+
+ // select table after shapes, because while selecting shapes the table will be deselected
+ if (mpViewShell)
+ {
+ mpViewShell->SelectAll();
+ }
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleDocument::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nCount(0);
+
+ if (mpChildrenShapes)
+ nCount = mpChildrenShapes->GetSelectedCount();
+
+ if (IsTableSelected())
+ ++nCount;
+
+ if (mxTempAcc.is())
+ ++nCount;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleDocument::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+ if (mpChildrenShapes)
+ {
+ sal_Int64 nCount(getSelectedAccessibleChildCount()); //all shapes and the table
+ if (nSelectedChildIndex < 0 || nSelectedChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ bool bTabMarked(IsTableSelected());
+
+ if (mpChildrenShapes)
+ xAccessible = mpChildrenShapes->GetSelected(nSelectedChildIndex, bTabMarked); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (mxTempAcc.is() && nSelectedChildIndex == nCount - 1)
+ xAccessible = mxTempAcc;
+ else if (bTabMarked)
+ xAccessible = GetAccessibleSpreadsheet();
+ }
+
+ OSL_ENSURE(xAccessible.is(), "here should always be an accessible object or an exception thrown");
+
+ return xAccessible;
+}
+
+void SAL_CALL
+ ScAccessibleDocument::deselectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (!(mpChildrenShapes && mpViewShell))
+ return;
+
+ sal_Int32 nCount(mpChildrenShapes->GetCount()); // all shapes and the table
+ if (mxTempAcc.is())
+ ++nCount;
+ if (nChildIndex < 0 || nChildIndex >= nCount)
+ throw lang::IndexOutOfBoundsException();
+
+ bool bTabMarked(IsTableSelected());
+
+ uno::Reference < XAccessible > xAccessible = mpChildrenShapes->Get(nChildIndex);
+ if (xAccessible.is())
+ {
+ mpChildrenShapes->Deselect(nChildIndex); // throws no lang::IndexOutOfBoundsException if Index is too high
+ if (bTabMarked)
+ mpViewShell->SelectAll(); // select the table again
+ }
+ else if (bTabMarked)
+ mpViewShell->Unmark();
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL
+ ScAccessibleDocument::getImplementationName()
+{
+ return "ScAccessibleDocument";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleDocument::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheetDocumentView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleDocument::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleDocumentImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleDocument::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+///===== IAccessibleViewForwarder ========================================
+
+tools::Rectangle ScAccessibleDocument::GetVisibleArea_Impl() const
+{
+ tools::Rectangle aVisRect(GetBoundingBox());
+
+ if (mpViewShell)
+ {
+ Point aPoint(mpViewShell->GetViewData().GetPixPos(meSplitPos)); // returns a negative Point
+ aPoint.setX(-aPoint.getX());
+ aPoint.setY(-aPoint.getY());
+ aVisRect.SetPos(aPoint);
+
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ aVisRect = pWin->PixelToLogic(aVisRect, pWin->GetDrawMapMode());
+ }
+
+ return aVisRect;
+}
+
+tools::Rectangle ScAccessibleDocument::GetVisibleArea() const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maVisArea;
+}
+
+Point ScAccessibleDocument::LogicToPixel (const Point& rPoint) const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ Point aPoint;
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ {
+ aPoint = pWin->LogicToPixel(rPoint, pWin->GetDrawMapMode());
+ aPoint += Point(pWin->GetWindowExtentsAbsolute().TopLeft());
+ }
+ return aPoint;
+}
+
+Size ScAccessibleDocument::LogicToPixel (const Size& rSize) const
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ Size aSize;
+ ScGridWindow* pWin = static_cast<ScGridWindow*>(mpViewShell->GetWindowByPos(meSplitPos));
+ if (pWin)
+ aSize = pWin->LogicToPixel(rSize, pWin->GetDrawMapMode());
+ return aSize;
+}
+
+ //===== internal ========================================================
+
+rtl::Reference<utl::AccessibleRelationSetHelper> ScAccessibleDocument::GetRelationSet(const ScAddress* pAddress) const
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if (mpChildrenShapes)
+ pRelationSet = mpChildrenShapes->GetRelationSet(pAddress);
+ return pRelationSet;
+}
+
+OUString
+ ScAccessibleDocument::createAccessibleDescription()
+{
+ return STR_ACC_DOC_DESCR;
+}
+
+OUString
+ ScAccessibleDocument::createAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ OUString sName = ScResId(STR_ACC_DOC_NAME);
+ sal_Int32 nNumber(sal_Int32(meSplitPos) + 1);
+ sName += OUString::number(nNumber);
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleDocument::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleDocument::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ }
+ return aRect;
+}
+
+SCTAB ScAccessibleDocument::getVisibleTable() const
+{
+ SCTAB nVisibleTable(0);
+ if (mpViewShell)
+ nVisibleTable = mpViewShell->GetViewData().GetTabNo();
+ return nVisibleTable;
+}
+
+uno::Reference < XAccessible >
+ ScAccessibleDocument::GetAccessibleSpreadsheet()
+{
+ if (!mpAccessibleSpreadsheet.is() && mpViewShell)
+ {
+ mpAccessibleSpreadsheet = new ScAccessibleSpreadsheet(this, mpViewShell, getVisibleTable(), meSplitPos);
+ mpAccessibleSpreadsheet->Init();
+ mbCompleteSheetSelected = IsTableSelected();
+ }
+ return mpAccessibleSpreadsheet;
+}
+
+void ScAccessibleDocument::FreeAccessibleSpreadsheet()
+{
+ if (mpAccessibleSpreadsheet.is())
+ {
+ mpAccessibleSpreadsheet->dispose();
+ mpAccessibleSpreadsheet.clear();
+ }
+}
+
+bool ScAccessibleDocument::IsTableSelected() const
+{
+ bool bResult (false);
+ if(mpViewShell)
+ {
+ SCTAB nTab(getVisibleTable());
+ //#103800#; use a copy of MarkData
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ ScDocument* pDoc = GetDocument();
+ if (aMarkData.IsAllMarked( ScRange( 0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab)))
+ bResult = true;
+ }
+ return bResult;
+}
+
+bool ScAccessibleDocument::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessibleDocument::AddChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
+{
+ OSL_ENSURE(!mxTempAcc.is(), "this object should be removed before");
+ if (xAcc.is())
+ {
+ mxTempAcc = xAcc;
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext>(this);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= mxTempAcc;
+ aEvent.IndexHint = getAccessibleChildCount() - 1;
+ CommitChange( aEvent );
+ }
+ }
+}
+
+void ScAccessibleDocument::RemoveChild(const uno::Reference<XAccessible>& xAcc, bool bFireEvent)
+{
+ OSL_ENSURE(mxTempAcc.is(), "this object should be added before");
+ if (!xAcc.is())
+ return;
+
+ OSL_ENSURE(xAcc.get() == mxTempAcc.get(), "only the same object should be removed");
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext>(this);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= mxTempAcc;
+ aEvent.IndexHint = -1;
+ CommitChange( aEvent );
+ }
+ mxTempAcc = nullptr;
+}
+
+OUString ScAccessibleDocument::GetCurrentCellName() const
+{
+ OUString sName(ScResId(STR_ACC_CELL_NAME));
+ if (mpViewShell)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ OUString sAddress(mpViewShell->GetViewData().GetCurPos().Format(ScRefFlags::VALID));
+ sName = sName.replaceFirst("%1", sAddress);
+ }
+ return sName;
+}
+
+OUString ScAccessibleDocument::GetCurrentCellDescription()
+{
+ return OUString();
+}
+
+ScDocument *ScAccessibleDocument::GetDocument() const
+{
+ return mpViewShell ? &mpViewShell->GetViewData().GetDocument() : nullptr;
+}
+
+ScAddress ScAccessibleDocument::GetCurCellAddress() const
+{
+ return mpViewShell ? mpViewShell->GetViewData().GetCurPos() : ScAddress();
+}
+
+uno::Any SAL_CALL ScAccessibleDocument::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ uno::Any anyAttribute;
+
+ sal_uInt16 sheetIndex;
+ OUString sSheetName;
+ sheetIndex = getVisibleTable();
+ if(GetDocument()==nullptr)
+ return anyAttribute;
+ GetDocument()->GetName(sheetIndex,sSheetName);
+ OUString sValue = "page-name:" + sSheetName +
+ ";page-number:" + OUString::number(sheetIndex+1) +
+ ";total-pages:" + OUString::number(GetDocument()->GetTableCount()) + ";";
+ anyAttribute <<= sValue;
+ return anyAttribute;
+}
+
+sal_Int32 SAL_CALL ScAccessibleDocument::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleDocument::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocumentBase.cxx b/sc/source/ui/Accessibility/AccessibleDocumentBase.cxx
new file mode 100644
index 0000000000..78da26006f
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocumentBase.cxx
@@ -0,0 +1,36 @@
+/* -*- 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 <AccessibleDocumentBase.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ========================================================
+
+ScAccessibleDocumentBase::ScAccessibleDocumentBase(const uno::Reference<XAccessible>& rxParent)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::DOCUMENT_SPREADSHEET)
+{
+}
+
+ScAccessibleDocumentBase::~ScAccessibleDocumentBase() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx
new file mode 100644
index 0000000000..3161e2ba32
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx
@@ -0,0 +1,1569 @@
+/* -*- 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 <AccessibleDocumentPagePreview.hxx>
+#include <AccessiblePreviewTable.hxx>
+#include <AccessiblePageHeader.hxx>
+#include <AccessibilityHints.hxx>
+#include <AccessibleText.hxx>
+#include <document.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <drwlayer.hxx>
+#include <editsrc.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <preview.hxx>
+#include <postit.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <tools/gen.hxx>
+#include <svx/fmview.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/docfile.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+typedef std::vector< uno::Reference< XAccessible > > ScXAccVector;
+
+namespace {
+
+struct ScAccNote
+{
+ OUString maNoteText;
+ tools::Rectangle maRect;
+ ScAddress maNoteCell;
+ ::accessibility::AccessibleTextHelper* mpTextHelper;
+ sal_Int32 mnParaCount;
+ bool mbMarkNote;
+
+ ScAccNote()
+ : mpTextHelper(nullptr)
+ , mnParaCount(0)
+ , mbMarkNote(false)
+ {
+ }
+};
+
+}
+
+class ScNotesChildren
+{
+public:
+ ScNotesChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc);
+ ~ScNotesChildren();
+ void Init(const tools::Rectangle& rVisRect, sal_Int32 nOffset);
+
+ sal_Int32 GetChildrenCount() const { return mnParagraphs;}
+ uno::Reference<XAccessible> GetChild(sal_Int32 nIndex) const;
+ uno::Reference<XAccessible> GetAt(const awt::Point& rPoint) const;
+
+ void DataChanged(const tools::Rectangle& rVisRect);
+
+private:
+ ScPreviewShell* mpViewShell;
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ typedef std::vector<ScAccNote> ScAccNotes;
+ mutable ScAccNotes maNotes;
+ mutable ScAccNotes maMarks;
+ sal_Int32 mnParagraphs;
+ sal_Int32 mnOffset;
+
+ ::accessibility::AccessibleTextHelper* CreateTextHelper(const OUString& rString, const tools::Rectangle& rVisRect, const ScAddress& aCellPos, bool bMarkNote, sal_Int32 nChildOffset) const;
+ sal_Int32 AddNotes(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rNotes);
+
+ static sal_Int8 CompareCell(const ScAddress& aCell1, const ScAddress& aCell2);
+ static void CollectChildren(const ScAccNote& rNote, ScXAccVector& rVector);
+ sal_Int32 CheckChanges(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect,
+ bool bMark, ScAccNotes& rOldNotes, ScAccNotes& rNewNotes,
+ ScXAccVector& rOldParas, ScXAccVector& rNewParas);
+
+ inline ScDocument* GetDocument() const;
+};
+
+ScNotesChildren::ScNotesChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc)
+ : mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ mnParagraphs(0),
+ mnOffset(0)
+{
+}
+
+ScNotesChildren::~ScNotesChildren()
+{
+ for (auto & i : maNotes)
+ if (i.mpTextHelper)
+ {
+ delete i.mpTextHelper;
+ i.mpTextHelper = nullptr;
+ }
+ for (auto & i : maMarks)
+ if (i.mpTextHelper)
+ {
+ delete i.mpTextHelper;
+ i.mpTextHelper = nullptr;
+ }
+}
+
+::accessibility::AccessibleTextHelper* ScNotesChildren::CreateTextHelper(const OUString& rString, const tools::Rectangle& rVisRect, const ScAddress& aCellPos, bool bMarkNote, sal_Int32 nChildOffset) const
+{
+ ::accessibility::AccessibleTextHelper* pTextHelper = new ::accessibility::AccessibleTextHelper(std::make_unique<ScAccessibilityEditSource>(std::make_unique<ScAccessibleNoteTextData>(mpViewShell, rString, aCellPos, bMarkNote)));
+ pTextHelper->SetEventSource(mpAccDoc);
+ pTextHelper->SetStartIndex(nChildOffset);
+ pTextHelper->SetOffset(rVisRect.TopLeft());
+
+ return pTextHelper;
+}
+
+sal_Int32 ScNotesChildren::AddNotes(const ScPreviewLocationData& rData, const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rNotes)
+{
+ sal_Int32 nCount = rData.GetNoteCountInRange(rVisRect, bMark);
+
+ rNotes.reserve(nCount);
+
+ sal_Int32 nParagraphs(0);
+ ScDocument* pDoc = GetDocument();
+ if (pDoc)
+ {
+ ScAccNote aNote;
+ aNote.mbMarkNote = bMark;
+ if (bMark)
+ aNote.mnParaCount = 1;
+ for (sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ if (rData.GetNoteInRange(rVisRect, nIndex, bMark, aNote.maNoteCell, aNote.maRect))
+ {
+ if (bMark)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ aNote.maNoteText = aNote.maNoteCell.Format(ScRefFlags::VALID);
+ }
+ else
+ {
+ if( ScPostIt* pNote = pDoc->GetNote( aNote.maNoteCell ) )
+ aNote.maNoteText = pNote->GetText();
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ }
+ nParagraphs += aNote.mnParaCount;
+ rNotes.push_back(aNote);
+ }
+ }
+ }
+ return nParagraphs;
+}
+
+void ScNotesChildren::Init(const tools::Rectangle& rVisRect, sal_Int32 nOffset)
+{
+ if (mpViewShell && !mnParagraphs)
+ {
+ mnOffset = nOffset;
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+
+ mnParagraphs = AddNotes(rData, rVisRect, false, maMarks);
+ mnParagraphs += AddNotes(rData, rVisRect, true, maNotes);
+ }
+}
+
+namespace {
+
+struct ScParaFound
+{
+ sal_Int32 mnIndex;
+ explicit ScParaFound(sal_Int32 nIndex) : mnIndex(nIndex) {}
+ bool operator() (const ScAccNote& rNote)
+ {
+ bool bResult(false);
+ if (rNote.mnParaCount > mnIndex)
+ bResult = true;
+ else
+ mnIndex -= rNote.mnParaCount;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScNotesChildren::GetChild(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+
+ if (nIndex < mnParagraphs)
+ {
+ if (nIndex < static_cast<sal_Int32>(maMarks.size()))
+ {
+ ScAccNotes::iterator aEndItr = maMarks.end();
+ ScParaFound aParaFound(nIndex);
+ ScAccNotes::iterator aItr = std::find_if(maMarks.begin(), aEndItr, aParaFound);
+ if (aItr != aEndItr)
+ {
+ OSL_ENSURE((aItr->maNoteCell == maMarks[nIndex].maNoteCell) && (aItr->mbMarkNote == maMarks[nIndex].mbMarkNote), "wrong note found");
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(maMarks[nIndex].maNoteText, maMarks[nIndex].maRect, maMarks[nIndex].maNoteCell, maMarks[nIndex].mbMarkNote, nIndex + mnOffset); // the marks are the first and every mark has only one paragraph
+ xAccessible = aItr->mpTextHelper->GetChild(aParaFound.mnIndex + aItr->mpTextHelper->GetStartIndex());
+ }
+ else
+ {
+ OSL_FAIL("wrong note found");
+ }
+ }
+ else
+ {
+ nIndex -= maMarks.size();
+ ScAccNotes::iterator aEndItr = maNotes.end();
+ ScParaFound aParaFound(nIndex);
+ ScAccNotes::iterator aItr = std::find_if(maNotes.begin(), aEndItr, aParaFound);
+ if (aEndItr != aItr)
+ {
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(aItr->maNoteText, aItr->maRect, aItr->maNoteCell, aItr->mbMarkNote, (nIndex - aParaFound.mnIndex) + mnOffset + maMarks.size());
+ xAccessible = aItr->mpTextHelper->GetChild(aParaFound.mnIndex + aItr->mpTextHelper->GetStartIndex());
+ }
+ }
+ }
+
+ return xAccessible;
+}
+
+namespace {
+
+struct ScPointFound
+{
+ tools::Rectangle maPoint;
+ sal_Int32 mnParagraphs;
+ explicit ScPointFound(const Point& rPoint) : maPoint(rPoint, Size(0, 0)), mnParagraphs(0) {}
+ bool operator() (const ScAccNote& rNote)
+ {
+ bool bResult(false);
+ if (maPoint.Contains(rNote.maRect))
+ bResult = true;
+ else
+ mnParagraphs += rNote.mnParaCount;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScNotesChildren::GetAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAccessible;
+
+ ScPointFound aPointFound(Point(rPoint.X, rPoint.Y));
+
+ ScAccNotes::iterator aEndItr = maMarks.end();
+ ScAccNotes::iterator aItr = std::find_if(maMarks.begin(), aEndItr, aPointFound);
+ if (aEndItr == aItr)
+ {
+ aEndItr = maNotes.end();
+ aItr = std::find_if(maNotes.begin(), aEndItr, aPointFound);
+ }
+ if (aEndItr != aItr)
+ {
+ if (!aItr->mpTextHelper)
+ aItr->mpTextHelper = CreateTextHelper(aItr->maNoteText, aItr->maRect, aItr->maNoteCell, aItr->mbMarkNote, aPointFound.mnParagraphs + mnOffset);
+ xAccessible = aItr->mpTextHelper->GetAt(rPoint);
+ }
+
+ return xAccessible;
+}
+
+sal_Int8 ScNotesChildren::CompareCell(const ScAddress& aCell1, const ScAddress& aCell2)
+{
+ OSL_ENSURE(aCell1.Tab() == aCell2.Tab(), "the notes should be on the same table");
+ sal_Int8 nResult(0);
+ if (aCell1 != aCell2)
+ {
+ if (aCell1.Row() == aCell2.Row())
+ nResult = (aCell1.Col() < aCell2.Col()) ? -1 : 1;
+ else
+ nResult = (aCell1.Row() < aCell2.Row()) ? -1 : 1;
+ }
+ return nResult;
+}
+
+void ScNotesChildren::CollectChildren(const ScAccNote& rNote, ScXAccVector& rVector)
+{
+ if (rNote.mpTextHelper)
+ for (sal_Int32 i = 0; i < rNote.mnParaCount; ++i)
+ rVector.push_back(rNote.mpTextHelper->GetChild(i + rNote.mpTextHelper->GetStartIndex()));
+}
+
+sal_Int32 ScNotesChildren::CheckChanges(const ScPreviewLocationData& rData,
+ const tools::Rectangle& rVisRect, bool bMark, ScAccNotes& rOldNotes,
+ ScAccNotes& rNewNotes, ScXAccVector& rOldParas, ScXAccVector& rNewParas)
+{
+ sal_Int32 nCount = rData.GetNoteCountInRange(rVisRect, bMark);
+
+ rNewNotes.reserve(nCount);
+
+ sal_Int32 nParagraphs(0);
+ ScDocument* pDoc = GetDocument();
+ if (pDoc)
+ {
+ ScAccNote aNote;
+ aNote.mbMarkNote = bMark;
+ if (bMark)
+ aNote.mnParaCount = 1;
+ ScAccNotes::iterator aItr = rOldNotes.begin();
+ ScAccNotes::iterator aEndItr = rOldNotes.end();
+ bool bAddNote(false);
+ for (sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ if (rData.GetNoteInRange(rVisRect, nIndex, bMark, aNote.maNoteCell, aNote.maRect))
+ {
+ if (bMark)
+ {
+ // Document not needed, because only the cell address, but not the tablename is needed
+ aNote.maNoteText = aNote.maNoteCell.Format(ScRefFlags::VALID);
+ }
+ else
+ {
+ if( ScPostIt* pNote = pDoc->GetNote( aNote.maNoteCell ) )
+ aNote.maNoteText = pNote->GetText();
+ }
+
+ sal_Int8 nCompare(-1); // if there are no more old children it is always a new one
+ if (aItr != aEndItr)
+ nCompare = CompareCell(aNote.maNoteCell, aItr->maNoteCell);
+ if (nCompare == 0)
+ {
+ if (aNote.maNoteText == aItr->maNoteText)
+ {
+ aNote.mpTextHelper = aItr->mpTextHelper;
+ if (aNote.maRect != aItr->maRect) // set new VisArea
+ {
+ aNote.mpTextHelper->SetOffset(aNote.maRect.TopLeft());
+ aNote.mpTextHelper->UpdateChildren();
+ //OSL_ENSURE(aItr->maRect.GetSize() == aNote.maRect.GetSize(), "size should be the same, because the text is not changed");
+ // could be changed, because only a part of the note is visible
+ }
+ }
+ else
+ {
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ // collect removed children
+ CollectChildren(*aItr, rOldParas);
+ delete aItr->mpTextHelper;
+ aItr->mpTextHelper = nullptr;;
+ // collect new children
+ CollectChildren(aNote, rNewParas);
+ }
+ bAddNote = true;
+ // not necessary, because this branch should not be reached if it is the end
+ //if (aItr != aEndItr)
+ ++aItr;
+ }
+ else if (nCompare < 0)
+ {
+ aNote.mpTextHelper = CreateTextHelper(aNote.maNoteText, aNote.maRect, aNote.maNoteCell, aNote.mbMarkNote, nParagraphs + mnOffset);
+ if (aNote.mpTextHelper)
+ aNote.mnParaCount = aNote.mpTextHelper->GetChildCount();
+ // collect new children
+ CollectChildren(aNote, rNewParas);
+ bAddNote = true;
+ }
+ else
+ {
+ // collect removed children
+ CollectChildren(*aItr, rOldParas);
+ delete aItr->mpTextHelper;
+ aItr->mpTextHelper = nullptr;
+
+ // no note to add
+ // not necessary, because this branch should not be reached if it is the end
+ //if (aItr != aEndItr)
+ ++aItr;
+ }
+ if (bAddNote)
+ {
+ nParagraphs += aNote.mnParaCount;
+ rNewNotes.push_back(aNote);
+ bAddNote = false;
+ }
+ }
+ }
+ }
+ return nParagraphs;
+}
+
+namespace {
+
+struct ScChildGone
+{
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ explicit ScChildGone(ScAccessibleDocumentPagePreview* pAccDoc) : mpAccDoc(pAccDoc) {}
+ void operator() (const uno::Reference<XAccessible>& xAccessible) const
+ {
+ if (mpAccDoc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc);
+ aEvent.OldValue <<= xAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccDoc->CommitChange(aEvent); // gone child - event
+ }
+ }
+};
+
+struct ScChildNew
+{
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ explicit ScChildNew(ScAccessibleDocumentPagePreview* pAccDoc) : mpAccDoc(pAccDoc) {}
+ void operator() (const uno::Reference<XAccessible>& xAccessible) const
+ {
+ if (mpAccDoc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(mpAccDoc);
+ aEvent.NewValue <<= xAccessible;
+ aEvent.IndexHint = -1;
+
+ mpAccDoc->CommitChange(aEvent); // new child - event
+ }
+ }
+};
+
+}
+
+void ScNotesChildren::DataChanged(const tools::Rectangle& rVisRect)
+{
+ if (!(mpViewShell && mpAccDoc))
+ return;
+
+ ScXAccVector aNewParas;
+ ScXAccVector aOldParas;
+ ScAccNotes aNewMarks;
+ mnParagraphs = CheckChanges(mpViewShell->GetLocationData(), rVisRect, true, maMarks, aNewMarks, aOldParas, aNewParas);
+ maMarks = aNewMarks;
+ ScAccNotes aNewNotes;
+ mnParagraphs += CheckChanges(mpViewShell->GetLocationData(), rVisRect, false, maNotes, aNewNotes, aOldParas, aNewParas);
+ maNotes = aNewNotes;
+
+ std::for_each(aOldParas.begin(), aOldParas.end(), ScChildGone(mpAccDoc));
+ std::for_each(aNewParas.begin(), aNewParas.end(), ScChildNew(mpAccDoc));
+}
+
+inline ScDocument* ScNotesChildren::GetDocument() const
+{
+ ScDocument* pDoc = nullptr;
+ if (mpViewShell)
+ pDoc = &mpViewShell->GetDocument();
+ return pDoc;
+}
+
+namespace {
+
+class ScIAccessibleViewForwarder : public ::accessibility::IAccessibleViewForwarder
+{
+public:
+ ScIAccessibleViewForwarder();
+ ScIAccessibleViewForwarder(ScPreviewShell* pViewShell,
+ ScAccessibleDocumentPagePreview* pAccDoc,
+ const MapMode& aMapMode);
+
+ ///===== IAccessibleViewForwarder ========================================
+
+ virtual tools::Rectangle GetVisibleArea() const override;
+ virtual Point LogicToPixel (const Point& rPoint) const override;
+ virtual Size LogicToPixel (const Size& rSize) const override;
+
+private:
+ ScPreviewShell* mpViewShell;
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ MapMode maMapMode;
+};
+
+}
+
+ScIAccessibleViewForwarder::ScIAccessibleViewForwarder()
+ : mpViewShell(nullptr), mpAccDoc(nullptr)
+{
+}
+
+ScIAccessibleViewForwarder::ScIAccessibleViewForwarder(ScPreviewShell* pViewShell,
+ ScAccessibleDocumentPagePreview* pAccDoc,
+ const MapMode& aMapMode)
+ : mpViewShell(pViewShell),
+ mpAccDoc(pAccDoc),
+ maMapMode(aMapMode)
+{
+}
+
+///===== IAccessibleViewForwarder ========================================
+
+tools::Rectangle ScIAccessibleViewForwarder::GetVisibleArea() const
+{
+ SolarMutexGuard aGuard;
+ tools::Rectangle aVisRect;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ {
+ aVisRect.SetSize(pWin->GetOutputSizePixel());
+ aVisRect.SetPos(Point(0, 0));
+
+ aVisRect = pWin->PixelToLogic(aVisRect, maMapMode);
+ }
+
+ return aVisRect;
+}
+
+Point ScIAccessibleViewForwarder::LogicToPixel (const Point& rPoint) const
+{
+ SolarMutexGuard aGuard;
+ Point aPoint;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin && mpAccDoc)
+ {
+ tools::Rectangle aRect(mpAccDoc->GetBoundingBoxOnScreen());
+ aPoint = pWin->LogicToPixel(rPoint, maMapMode) + aRect.TopLeft();
+ }
+
+ return aPoint;
+}
+
+Size ScIAccessibleViewForwarder::LogicToPixel (const Size& rSize) const
+{
+ SolarMutexGuard aGuard;
+ Size aSize;
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->LogicToPixel(rSize, maMapMode);
+ return aSize;
+}
+
+namespace {
+
+struct ScShapeChild
+{
+ ScShapeChild()
+ : mnRangeId(0)
+ {
+ }
+ ScShapeChild(ScShapeChild const &) = delete;
+ ScShapeChild(ScShapeChild &&) = default;
+ ~ScShapeChild();
+ ScShapeChild & operator =(ScShapeChild const &) = delete;
+ ScShapeChild & operator =(ScShapeChild && other) {
+ std::swap(mpAccShape, other.mpAccShape);
+ mxShape = std::move(other.mxShape);
+ mnRangeId = other.mnRangeId;
+ return *this;
+ }
+
+ mutable rtl::Reference< ::accessibility::AccessibleShape > mpAccShape;
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ sal_Int32 mnRangeId;
+};
+
+}
+
+ScShapeChild::~ScShapeChild()
+{
+ if (mpAccShape.is())
+ {
+ mpAccShape->dispose();
+ }
+}
+
+namespace {
+
+struct ScShapeChildLess
+{
+ bool operator()(const ScShapeChild& rChild1, const ScShapeChild& rChild2) const
+ {
+ bool bResult(false);
+ if (rChild1.mxShape.is() && rChild2.mxShape.is())
+ bResult = (rChild1.mxShape.get() < rChild2.mxShape.get());
+ return bResult;
+ }
+};
+
+}
+
+typedef std::vector<ScShapeChild> ScShapeChildVec;
+
+namespace {
+
+struct ScShapeRange
+{
+ ScShapeRange() = default;
+ ScShapeRange(ScShapeRange const &) = delete;
+ ScShapeRange(ScShapeRange &&) = default;
+ ScShapeRange & operator =(ScShapeRange const &) = delete;
+ ScShapeRange & operator =(ScShapeRange &&) = default;
+
+ ScShapeChildVec maBackShapes;
+ ScShapeChildVec maForeShapes; // inclusive internal shapes
+ ScShapeChildVec maControls;
+ ScIAccessibleViewForwarder maViewForwarder;
+};
+
+}
+
+typedef std::vector<ScShapeRange> ScShapeRangeVec;
+
+class ScShapeChildren : public ::accessibility::IAccessibleParent
+{
+public:
+ ScShapeChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc);
+
+ ///===== IAccessibleParent ==============================================
+
+ virtual bool ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const ::accessibility::AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ ///===== Internal ========================================================
+
+ void Init();
+
+ sal_Int32 GetBackShapeCount() const;
+ uno::Reference<XAccessible> GetBackShape(sal_Int32 nIndex) const;
+ sal_Int32 GetForeShapeCount() const;
+ uno::Reference<XAccessible> GetForeShape(sal_Int32 nIndex) const;
+ sal_Int32 GetControlCount() const;
+ uno::Reference<XAccessible> GetControl(sal_Int32 nIndex) const;
+ uno::Reference<XAccessible> GetForegroundShapeAt(const awt::Point& rPoint) const; // inclusive controls
+ uno::Reference<XAccessible> GetBackgroundShapeAt(const awt::Point& rPoint) const;
+
+ void DataChanged();
+ void VisAreaChanged() const;
+
+private:
+ ScAccessibleDocumentPagePreview* mpAccDoc;
+ ScPreviewShell* mpViewShell;
+ ScShapeRangeVec maShapeRanges;
+
+ void FindChanged(ScShapeChildVec& aOld, ScShapeChildVec& aNew) const;
+ void FindChanged(ScShapeRange& aOld, ScShapeRange& aNew) const;
+ ::accessibility::AccessibleShape* GetAccShape(const ScShapeChild& rShape) const;
+ ::accessibility::AccessibleShape* GetAccShape(const ScShapeChildVec& rShapes, sal_Int32 nIndex) const;
+ void FillShapes(const tools::Rectangle& aPixelPaintRect, const MapMode& aMapMode, sal_uInt8 nRangeId);
+
+// void AddShape(const uno::Reference<drawing::XShape>& xShape, SdrLayerID aLayerID);
+// void RemoveShape(const uno::Reference<drawing::XShape>& xShape, SdrLayerID aLayerID);
+ SdrPage* GetDrawPage() const;
+};
+
+ScShapeChildren::ScShapeChildren(ScPreviewShell* pViewShell, ScAccessibleDocumentPagePreview* pAccDoc)
+ :
+ mpAccDoc(pAccDoc),
+ mpViewShell(pViewShell),
+ maShapeRanges(SC_PREVIEW_MAXRANGES)
+{
+}
+
+void ScShapeChildren::FindChanged(ScShapeChildVec& rOld, ScShapeChildVec& rNew) const
+{
+ ScShapeChildVec::iterator aOldItr = rOld.begin();
+ ScShapeChildVec::iterator aOldEnd = rOld.end();
+ ScShapeChildVec::const_iterator aNewItr = rNew.begin();
+ ScShapeChildVec::const_iterator aNewEnd = rNew.end();
+ uno::Reference<XAccessible> xAcc;
+ while ((aNewItr != aNewEnd) && (aOldItr != aOldEnd))
+ {
+ if (aNewItr->mxShape.get() == aOldItr->mxShape.get())
+ {
+ ++aOldItr;
+ ++aNewItr;
+ }
+ else if (aNewItr->mxShape.get() < aOldItr->mxShape.get())
+ {
+ xAcc = GetAccShape(*aNewItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aNewItr;
+ }
+ else
+ {
+ xAcc = GetAccShape(*aOldItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aOldItr;
+ }
+ }
+ while (aOldItr != aOldEnd)
+ {
+ xAcc = GetAccShape(*aOldItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aOldItr;
+ }
+ while (aNewItr != aNewEnd)
+ {
+ xAcc = GetAccShape(*aNewItr);
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc);
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ mpAccDoc->CommitChange(aEvent);
+ ++aNewItr;
+ }
+}
+
+void ScShapeChildren::FindChanged(ScShapeRange& rOld, ScShapeRange& rNew) const
+{
+ FindChanged(rOld.maBackShapes, rNew.maBackShapes);
+ FindChanged(rOld.maForeShapes, rNew.maForeShapes);
+ FindChanged(rOld.maControls, rNew.maControls);
+}
+
+void ScShapeChildren::DataChanged()
+{
+ ScShapeRangeVec aOldShapeRanges(std::move(maShapeRanges));
+ maShapeRanges.clear();
+ maShapeRanges.resize(SC_PREVIEW_MAXRANGES);
+ Init();
+ for (sal_Int32 i = 0; i < SC_PREVIEW_MAXRANGES; ++i)
+ {
+ FindChanged(aOldShapeRanges[i], maShapeRanges[i]);
+ }
+}
+
+namespace
+{
+ struct ScVisAreaChanged
+ {
+ void operator() (const ScShapeChild& rAccShapeData) const
+ {
+ if (rAccShapeData.mpAccShape.is())
+ {
+ rAccShapeData.mpAccShape->ViewForwarderChanged();
+ }
+ }
+ };
+}
+
+void ScShapeChildren::VisAreaChanged() const
+{
+ for (auto const& shape : maShapeRanges)
+ {
+ ScVisAreaChanged aVisAreaChanged;
+ std::for_each(shape.maBackShapes.begin(), shape.maBackShapes.end(), aVisAreaChanged);
+ std::for_each(shape.maControls.begin(), shape.maControls.end(), aVisAreaChanged);
+ std::for_each(shape.maForeShapes.begin(), shape.maForeShapes.end(), aVisAreaChanged);
+ }
+}
+
+ ///===== IAccessibleParent ==============================================
+
+bool ScShapeChildren::ReplaceChild (::accessibility::AccessibleShape* /* pCurrentChild */,
+ const css::uno::Reference< css::drawing::XShape >& /* _rxShape */,
+ const tools::Long /* _nIndex */, const ::accessibility::AccessibleShapeTreeInfo& /* _rShapeTreeInfo */)
+{
+ OSL_FAIL("should not be called in the page preview");
+ return false;
+}
+
+ ///===== Internal ========================================================
+
+void ScShapeChildren::Init()
+{
+ if(!mpViewShell)
+ return;
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ MapMode aMapMode;
+ tools::Rectangle aPixelPaintRect;
+ sal_uInt8 nRangeId;
+ sal_uInt16 nCount(rData.GetDrawRanges());
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ rData.GetDrawRange(i, aPixelPaintRect, aMapMode, nRangeId);
+ FillShapes(aPixelPaintRect, aMapMode, nRangeId);
+ }
+}
+
+sal_Int32 ScShapeChildren::GetBackShapeCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maBackShapes.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetBackShape(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maBackShapes.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maBackShapes, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+sal_Int32 ScShapeChildren::GetForeShapeCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maForeShapes.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetForeShape(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maForeShapes.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maForeShapes, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+sal_Int32 ScShapeChildren::GetControlCount() const
+{
+ sal_Int32 nCount(0);
+ for (auto const& shape : maShapeRanges)
+ nCount += shape.maControls.size();
+ return nCount;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetControl(sal_Int32 nIndex) const
+{
+ uno::Reference<XAccessible> xAccessible;
+ for (const auto& rShapeRange : maShapeRanges)
+ {
+ sal_Int32 nCount(rShapeRange.maControls.size());
+ if(nIndex < nCount)
+ xAccessible = GetAccShape(rShapeRange.maControls, nIndex);
+ nIndex -= nCount;
+ if (xAccessible.is())
+ break;
+ }
+
+ if (nIndex >= 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+namespace {
+
+struct ScShapePointFound
+{
+ Point maPoint;
+ explicit ScShapePointFound(const awt::Point& rPoint) : maPoint(VCLPoint(rPoint)) {}
+ bool operator() (const ScShapeChild& rShape)
+ {
+ bool bResult(false);
+ if (VCLRectangle(rShape.mpAccShape->getBounds()).Contains(maPoint))
+ bResult = true;
+ return bResult;
+ }
+};
+
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetForegroundShapeAt(const awt::Point& rPoint) const //inclusive Controls
+{
+ uno::Reference<XAccessible> xAcc;
+
+ for(const auto& rShapeRange : maShapeRanges)
+ {
+ ScShapeChildVec::const_iterator aFindItr = std::find_if(rShapeRange.maForeShapes.begin(), rShapeRange.maForeShapes.end(), ScShapePointFound(rPoint));
+ if (aFindItr != rShapeRange.maForeShapes.end())
+ xAcc = GetAccShape(*aFindItr);
+ else
+ {
+ ScShapeChildVec::const_iterator aCtrlItr = std::find_if(rShapeRange.maControls.begin(), rShapeRange.maControls.end(), ScShapePointFound(rPoint));
+ if (aCtrlItr != rShapeRange.maControls.end())
+ xAcc = GetAccShape(*aCtrlItr);
+ }
+
+ if (xAcc.is())
+ break;
+ }
+
+ return xAcc;
+}
+
+uno::Reference<XAccessible> ScShapeChildren::GetBackgroundShapeAt(const awt::Point& rPoint) const
+{
+ uno::Reference<XAccessible> xAcc;
+
+ for(const auto& rShapeRange : maShapeRanges)
+ {
+ ScShapeChildVec::const_iterator aFindItr = std::find_if(rShapeRange.maBackShapes.begin(), rShapeRange.maBackShapes.end(), ScShapePointFound(rPoint));
+ if (aFindItr != rShapeRange.maBackShapes.end())
+ xAcc = GetAccShape(*aFindItr);
+ if (xAcc.is())
+ break;
+ }
+
+ return xAcc;
+}
+
+::accessibility::AccessibleShape* ScShapeChildren::GetAccShape(const ScShapeChild& rShape) const
+{
+ if (!rShape.mpAccShape.is())
+ {
+ ::accessibility::ShapeTypeHandler& rShapeHandler = ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(rShape.mxShape, mpAccDoc);
+
+ if (mpViewShell)
+ {
+ ::accessibility::AccessibleShapeTreeInfo aShapeTreeInfo;
+ aShapeTreeInfo.SetSdrView(mpViewShell->GetPreview()->GetDrawView());
+ aShapeTreeInfo.SetController(nullptr);
+ aShapeTreeInfo.SetWindow(mpViewShell->GetWindow());
+ aShapeTreeInfo.SetViewForwarder(&(maShapeRanges[rShape.mnRangeId].maViewForwarder));
+ rShape.mpAccShape = rShapeHandler.CreateAccessibleObject(aShapeInfo, aShapeTreeInfo);
+ if (rShape.mpAccShape.is())
+ {
+ rShape.mpAccShape->Init();
+ }
+ }
+ }
+ return rShape.mpAccShape.get();
+}
+
+::accessibility::AccessibleShape* ScShapeChildren::GetAccShape(const ScShapeChildVec& rShapes, sal_Int32 nIndex) const
+{
+ return GetAccShape(rShapes[nIndex]);
+}
+
+void ScShapeChildren::FillShapes(const tools::Rectangle& aPixelPaintRect, const MapMode& aMapMode, sal_uInt8 nRangeId)
+{
+ OSL_ENSURE(nRangeId < maShapeRanges.size(), "this is not a valid range for draw objects");
+ SdrPage* pPage = GetDrawPage();
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (!(pPage && pWin))
+ return;
+
+ bool bForeAdded(false);
+ bool bBackAdded(false);
+ bool bControlAdded(false);
+ tools::Rectangle aClippedPixelPaintRect(aPixelPaintRect);
+ if (mpAccDoc)
+ {
+ tools::Rectangle aRect2(Point(0,0), mpAccDoc->GetBoundingBoxOnScreen().GetSize());
+ aClippedPixelPaintRect = aPixelPaintRect.GetIntersection(aRect2);
+ }
+ ScIAccessibleViewForwarder aViewForwarder(mpViewShell, mpAccDoc, aMapMode);
+ maShapeRanges[nRangeId].maViewForwarder = aViewForwarder;
+ for (const rtl::Reference<SdrObject>& pObj : *pPage)
+ {
+ uno::Reference< drawing::XShape > xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ if (xShape.is())
+ {
+ tools::Rectangle aRect(pWin->LogicToPixel(
+ tools::Rectangle(VCLPoint(xShape->getPosition()), VCLSize(xShape->getSize())), aMapMode));
+ if(!aClippedPixelPaintRect.GetIntersection(aRect).IsEmpty())
+ {
+ ScShapeChild aShape;
+ aShape.mxShape = xShape;
+ aShape.mnRangeId = nRangeId;
+ if (pObj->GetLayer().anyOf(SC_LAYER_INTERN, SC_LAYER_FRONT))
+ {
+ maShapeRanges[nRangeId].maForeShapes.push_back(std::move(aShape));
+ bForeAdded = true;
+ }
+ else if (pObj->GetLayer() == SC_LAYER_BACK)
+ {
+ maShapeRanges[nRangeId].maBackShapes.push_back(std::move(aShape));
+ bBackAdded = true;
+ }
+ else if (pObj->GetLayer() == SC_LAYER_CONTROLS)
+ {
+ maShapeRanges[nRangeId].maControls.push_back(std::move(aShape));
+ bControlAdded = true;
+ }
+ else
+ {
+ OSL_FAIL("I don't know this layer.");
+ }
+ }
+ }
+ }
+ if (bForeAdded)
+ std::sort(maShapeRanges[nRangeId].maForeShapes.begin(), maShapeRanges[nRangeId].maForeShapes.end(),ScShapeChildLess());
+ if (bBackAdded)
+ std::sort(maShapeRanges[nRangeId].maBackShapes.begin(), maShapeRanges[nRangeId].maBackShapes.end(),ScShapeChildLess());
+ if (bControlAdded)
+ std::sort(maShapeRanges[nRangeId].maControls.begin(), maShapeRanges[nRangeId].maControls.end(),ScShapeChildLess());
+}
+
+SdrPage* ScShapeChildren::GetDrawPage() const
+{
+ SCTAB nTab( mpViewShell->GetLocationData().GetPrintTab() );
+ SdrPage* pDrawPage = nullptr;
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ if (rDoc.GetDrawLayer())
+ {
+ ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
+ if (pDrawLayer->HasObjects() && (pDrawLayer->GetPageCount() > nTab))
+ pDrawPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(static_cast<sal_Int16>(nTab)));
+ }
+ return pDrawPage;
+}
+
+namespace {
+
+struct ScPagePreviewCountData
+{
+ // order is background shapes, header, table or notes, footer, foreground shapes, controls
+
+ tools::Rectangle aVisRect;
+ tools::Long nBackShapes;
+ tools::Long nHeaders;
+ tools::Long nTables;
+ tools::Long nNoteParagraphs;
+ tools::Long nFooters;
+ tools::Long nForeShapes;
+ tools::Long nControls;
+
+ ScPagePreviewCountData( const ScPreviewLocationData& rData, const vcl::Window* pSizeWindow,
+ const ScNotesChildren* pNotesChildren, const ScShapeChildren* pShapeChildren );
+
+ tools::Long GetTotal() const
+ {
+ return nBackShapes + nHeaders + nTables + nNoteParagraphs + nFooters + nForeShapes + nControls;
+ }
+};
+
+}
+
+ScPagePreviewCountData::ScPagePreviewCountData( const ScPreviewLocationData& rData,
+ const vcl::Window* pSizeWindow, const ScNotesChildren* pNotesChildren,
+ const ScShapeChildren* pShapeChildren) :
+ nBackShapes( 0 ),
+ nHeaders( 0 ),
+ nTables( 0 ),
+ nNoteParagraphs( 0 ),
+ nFooters( 0 ),
+ nForeShapes( 0 ),
+ nControls( 0 )
+{
+ Size aOutputSize;
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ aVisRect = tools::Rectangle( Point(), aOutputSize );
+
+ tools::Rectangle aObjRect;
+
+ if ( rData.GetHeaderPosition( aObjRect ) && aObjRect.Overlaps( aVisRect ) )
+ nHeaders = 1;
+
+ if ( rData.GetFooterPosition( aObjRect ) && aObjRect.Overlaps( aVisRect ) )
+ nFooters = 1;
+
+ if ( rData.HasCellsInRange( aVisRect ) )
+ nTables = 1;
+
+ //! shapes...
+ nBackShapes = pShapeChildren->GetBackShapeCount();
+ nForeShapes = pShapeChildren->GetForeShapeCount();
+ nControls = pShapeChildren->GetControlCount();
+
+ // there are only notes if there is no table
+ if (nTables == 0)
+ nNoteParagraphs = pNotesChildren->GetChildrenCount();
+}
+
+//===== internal ========================================================
+
+ScAccessibleDocumentPagePreview::ScAccessibleDocumentPagePreview(
+ const uno::Reference<XAccessible>& rxParent, ScPreviewShell* pViewShell ) :
+ ScAccessibleDocumentBase(rxParent),
+ mpViewShell(pViewShell)
+{
+ if (pViewShell)
+ pViewShell->AddAccessibilityObject(*this);
+
+}
+
+ScAccessibleDocumentPagePreview::~ScAccessibleDocumentPagePreview()
+{
+ if (!ScAccessibleDocumentBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleDocumentPagePreview::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpTable.clear();
+ mpHeader.clear();
+ mpFooter.clear();
+
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ // no need to Dispose the AccessibleTextHelper,
+ // as long as mpNotesChildren are destructed here
+ mpNotesChildren.reset();
+
+ mpShapeChildren.reset();
+
+ ScAccessibleDocumentBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessibleDocumentPagePreview::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( dynamic_cast<const ScAccWinFocusLostHint*>(&rHint) )
+ {
+ CommitFocusLost();
+ }
+ else if ( dynamic_cast<const ScAccWinFocusGotHint*>(&rHint) )
+ {
+ CommitFocusGained();
+ }
+ else
+ {
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ if (mpTable.is()) // if there is no table there is nothing to notify, because no one recognizes the change
+ {
+ {
+ uno::Reference<XAccessible> xAcc = mpTable;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ CommitChange(aEvent);
+ }
+
+ mpTable->dispose();
+ mpTable.clear();
+ }
+
+ Size aOutputSize;
+ vcl::Window* pSizeWindow = mpViewShell->GetWindow();
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ GetNotesChildren()->DataChanged(aVisRect);
+
+ GetShapeChildren()->DataChanged();
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if (aCount.nTables > 0)
+ {
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ sal_Int32 nIndex (aCount.nBackShapes + aCount.nHeaders);
+
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+
+ {
+ uno::Reference<XAccessible> xAcc = mpTable;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= xAcc;
+ aEvent.IndexHint = -1;
+ CommitChange(aEvent);
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ Size aOutputSize;
+ vcl::Window* pSizeWindow = mpViewShell->GetWindow();
+ if ( pSizeWindow )
+ aOutputSize = pSizeWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ GetNotesChildren()->DataChanged(aVisRect);
+
+ GetShapeChildren()->VisAreaChanged();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+ }
+ ScAccessibleDocumentBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ( mpViewShell )
+ {
+ xAccessible = GetShapeChildren()->GetForegroundShapeAt(rPoint);
+ if (!xAccessible.is())
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if ( !mpTable.is() && (aCount.nTables > 0) )
+ {
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ sal_Int32 nIndex (aCount.nBackShapes + aCount.nHeaders);
+
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+ }
+ if (mpTable.is() && VCLRectangle(mpTable->getBounds()).Contains(VCLPoint(rPoint)))
+ xAccessible = mpTable.get();
+ }
+ if (!xAccessible.is())
+ xAccessible = GetNotesChildren()->GetAt(rPoint);
+ if (!xAccessible.is())
+ {
+ if (!mpHeader.is() || !mpFooter.is())
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if (!mpHeader.is())
+ {
+ mpHeader = new ScAccessiblePageHeader( this, mpViewShell, true, aCount.nBackShapes + aCount.nHeaders - 1);
+ }
+ if (!mpFooter.is())
+ {
+ mpFooter = new ScAccessiblePageHeader( this, mpViewShell, false, aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters - 1 );
+ }
+ }
+
+ Point aPoint(VCLPoint(rPoint));
+
+ if (VCLRectangle(mpHeader->getBounds()).Contains(aPoint))
+ xAccessible = mpHeader.get();
+ else if (VCLRectangle(mpFooter->getBounds()).Contains(aPoint))
+ xAccessible = mpFooter.get();
+ }
+ if (!xAccessible.is())
+ xAccessible = GetShapeChildren()->GetBackgroundShapeAt(rPoint);
+ }
+ }
+
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleDocumentPagePreview::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ {
+ // just grab the focus for the window
+ xAccessibleComponent->grabFocus();
+ }
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ sal_Int64 nRet = 0;
+ if ( mpViewShell )
+ {
+ ScPagePreviewCountData aCount( mpViewShell->GetLocationData(), mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+ nRet = aCount.GetTotal();
+ }
+
+ return nRet;
+}
+
+uno::Reference<XAccessible> SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference<XAccessible> xAccessible;
+
+ if ( mpViewShell )
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ if ( nIndex < aCount.nBackShapes )
+ {
+ xAccessible = GetShapeChildren()->GetBackShape(nIndex);
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders )
+ {
+ if ( !mpHeader.is() )
+ {
+ mpHeader = new ScAccessiblePageHeader( this, mpViewShell, true, nIndex );
+ }
+
+ xAccessible = mpHeader.get();
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nTables )
+ {
+ if ( !mpTable.is() )
+ {
+ mpTable = new ScAccessiblePreviewTable( this, mpViewShell, nIndex );
+ mpTable->Init();
+ }
+ xAccessible = mpTable.get();
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nNoteParagraphs )
+ {
+ xAccessible = GetNotesChildren()->GetChild(nIndex - aCount.nBackShapes - aCount.nHeaders);
+ }
+ else if ( nIndex < aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters )
+ {
+ if ( !mpFooter.is() )
+ {
+ mpFooter = new ScAccessiblePageHeader( this, mpViewShell, false, nIndex );
+ }
+ xAccessible = mpFooter.get();
+ }
+ else
+ {
+ sal_Int64 nIdx(nIndex - (aCount.nBackShapes + aCount.nHeaders + aCount.nTables + aCount.nNoteParagraphs + aCount.nFooters));
+ if (nIdx < aCount.nForeShapes)
+ xAccessible = GetShapeChildren()->GetForeShape(nIdx);
+ else
+ xAccessible = GetShapeChildren()->GetControl(nIdx - aCount.nForeShapes);
+ }
+ }
+
+ if ( !xAccessible.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xAccessible;
+}
+
+ /// Return the set of current states.
+sal_Int64 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ // never editable
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleDocumentPagePreview::getImplementationName()
+{
+ return "ScAccessibleDocumentPagePreview";
+}
+
+uno::Sequence< OUString> SAL_CALL ScAccessibleDocumentPagePreview::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheetPageView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleDocumentPagePreview::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//===== internal ========================================================
+
+OUString ScAccessibleDocumentPagePreview::createAccessibleDescription()
+{
+ return STR_ACC_PREVIEWDOC_DESCR;
+}
+
+OUString ScAccessibleDocumentPagePreview::createAccessibleName()
+{
+ OUString sName = ScResId(STR_ACC_PREVIEWDOC_NAME);
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleDocumentPagePreview::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleDocumentPagePreview::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ }
+ return aRect;
+}
+
+bool ScAccessibleDocumentPagePreview::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+ScNotesChildren* ScAccessibleDocumentPagePreview::GetNotesChildren()
+{
+ if (!mpNotesChildren && mpViewShell)
+ {
+ mpNotesChildren.reset( new ScNotesChildren(mpViewShell, this) );
+
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ ScPagePreviewCountData aCount( rData, mpViewShell->GetWindow(), GetNotesChildren(), GetShapeChildren() );
+
+ //! order is background shapes, header, table or notes, footer, foreground shapes, controls
+ mpNotesChildren->Init(aCount.aVisRect, aCount.nBackShapes + aCount.nHeaders);
+ }
+ return mpNotesChildren.get();
+}
+
+ScShapeChildren* ScAccessibleDocumentPagePreview::GetShapeChildren()
+{
+ if (!mpShapeChildren && mpViewShell)
+ {
+ mpShapeChildren.reset( new ScShapeChildren(mpViewShell, this) );
+ mpShapeChildren->Init();
+ }
+
+ return mpShapeChildren.get();
+}
+
+OUString ScAccessibleDocumentPagePreview::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString aName = ScResId(STR_ACC_DOC_SPREADSHEET);
+ ScDocument& rScDoc = mpViewShell->GetDocument();
+
+ ScDocShell* pObjSh = rScDoc.GetDocumentShell();
+ if (!pObjSh)
+ return aName;
+
+ OUString aFileName;
+ SfxMedium* pMed = pObjSh->GetMedium();
+ if (pMed)
+ aFileName = pMed->GetName();
+
+ if (aFileName.isEmpty())
+ aFileName = pObjSh->GetTitle(SFX_TITLE_APINAME);
+
+ if (!aFileName.isEmpty())
+ {
+ aName = aFileName + " - " + aName + ScResId(STR_ACC_DOC_PREVIEW_SUFFIX);
+
+ }
+
+ return aName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleEditObject.cxx b/sc/source/ui/Accessibility/AccessibleEditObject.cxx
new file mode 100644
index 0000000000..7e58af04ef
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleEditObject.cxx
@@ -0,0 +1,600 @@
+/* -*- 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 <memory>
+#include <utility>
+
+#include <AccessibleEditObject.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <scmod.hxx>
+#include <inputhdl.hxx>
+#include <inputwin.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <svx/AccessibleTextHelper.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/svdmodel.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sfx2/objsh.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <unonames.hxx>
+#include <document.hxx>
+#include <AccessibleDocument.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessibleEditObject::ScAccessibleEditObject(
+ const uno::Reference<XAccessible>& rxParent,
+ EditView* pEditView, vcl::Window* pWin, const OUString& rName,
+ const OUString& rDescription, EditObjectType eObjectType)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::TEXT_FRAME)
+ , mpEditView(pEditView)
+ , mpWindow(pWin)
+ , mpTextWnd(nullptr)
+ , meObjectType(eObjectType)
+ , mbHasFocus(false)
+ , m_pScDoc(nullptr)
+{
+ InitAcc(rxParent, pEditView, rName, rDescription);
+}
+
+ScAccessibleEditObject::ScAccessibleEditObject(EditObjectType eObjectType)
+ : ScAccessibleContextBase(nullptr, AccessibleRole::TEXT_FRAME)
+ , mpEditView(nullptr)
+ , mpWindow(nullptr)
+ , mpTextWnd(nullptr)
+ , meObjectType(eObjectType)
+ , mbHasFocus(false)
+ , m_pScDoc(nullptr)
+{
+}
+
+void ScAccessibleEditObject::InitAcc(
+ const uno::Reference<XAccessible>& rxParent,
+ EditView* pEditView,
+ const OUString& rName,
+ const OUString& rDescription)
+{
+ SetParent(rxParent);
+ mpEditView = pEditView;
+
+ CreateTextHelper();
+ SetName(rName);
+ SetDescription(rDescription);
+ if( meObjectType == CellInEditMode)
+ {
+ const ScAccessibleDocument *pAccDoc = static_cast<ScAccessibleDocument*>(rxParent.get());
+ if (pAccDoc)
+ {
+ m_pScDoc = pAccDoc->GetDocument();
+ m_curCellAddress =pAccDoc->GetCurCellAddress();
+ }
+ }
+}
+
+ScAccessibleEditObject::~ScAccessibleEditObject()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessibleEditObject::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpTextHelper.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+void ScAccessibleEditObject::LostFocus()
+{
+ mbHasFocus = false;
+ if (mpTextHelper)
+ mpTextHelper->SetFocus(false);
+ CommitFocusLost();
+}
+
+void ScAccessibleEditObject::GotFocus()
+{
+ mbHasFocus = true;
+ CommitFocusGained();
+ if (mpTextHelper)
+ mpTextHelper->SetFocus();
+}
+
+//===== XInterface ==========================================================
+
+css::uno::Any SAL_CALL
+ ScAccessibleEditObject::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = ScAccessibleContextBase::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleSelection* >(this)
+ );
+ return aReturn;
+}
+void SAL_CALL
+ ScAccessibleEditObject::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire ();
+}
+void SAL_CALL
+ ScAccessibleEditObject::release()
+ noexcept
+{
+ ScAccessibleContextBase::release ();
+}
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleEditObject::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleEditObject::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aScreenBounds;
+
+ if ( mpWindow )
+ {
+ if ( meObjectType == CellInEditMode )
+ {
+ if ( mpEditView && mpEditView->GetEditEngine() )
+ {
+ MapMode aMapMode( mpEditView->GetEditEngine()->GetRefMapMode() );
+ tools::Rectangle aScreenBoundsLog = mpWindow->LogicToPixel( mpEditView->GetOutputArea(), aMapMode );
+ Point aCellLoc = aScreenBoundsLog.TopLeft();
+ AbsoluteScreenPixelRectangle aWindowRect = mpWindow->GetWindowExtentsAbsolute();
+ AbsoluteScreenPixelPoint aWindowLoc = aWindowRect.TopLeft();
+ AbsoluteScreenPixelPoint aPos( aCellLoc.getX() + aWindowLoc.getX(), aCellLoc.getY() + aWindowLoc.getY() );
+ aScreenBounds = AbsoluteScreenPixelRectangle( aPos, aScreenBoundsLog.GetSize() );
+ }
+ }
+ else
+ {
+ aScreenBounds = mpWindow->GetWindowExtentsAbsolute();
+ }
+ }
+
+ return aScreenBounds;
+}
+
+tools::Rectangle ScAccessibleEditObject::GetBoundingBox() const
+{
+ tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
+
+ if ( mpWindow )
+ {
+ uno::Reference< XAccessible > xThis( mpWindow->GetAccessible() );
+ if ( xThis.is() )
+ {
+ uno::Reference< XAccessibleContext > xContext( xThis->getAccessibleContext() );
+ if ( xContext.is() )
+ {
+ uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() );
+ if ( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
+ if ( xParentComponent.is() )
+ {
+ Point aScreenLoc = aBounds.TopLeft();
+ awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
+ Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
+ aBounds.SetPos( aPos );
+ }
+ }
+ }
+ }
+ }
+
+ return aBounds;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessibleEditObject::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleEditObject::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessibleEditObject::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ // all states are const, because this object exists only in one state
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::SENSITIVE;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+OUString
+ ScAccessibleEditObject::createAccessibleDescription()
+{
+// OSL_FAIL("Should never be called, because is set in the constructor.")
+ return OUString();
+}
+
+OUString
+ ScAccessibleEditObject::createAccessibleName()
+{
+ OSL_FAIL("Should never be called, because is set in the constructor.");
+ return OUString();
+}
+
+ ///===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL
+ ScAccessibleEditObject::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ CreateTextHelper();
+
+ mpTextHelper->AddEventListener(xListener);
+
+ ScAccessibleContextBase::addAccessibleEventListener(xListener);
+}
+
+void SAL_CALL
+ ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ CreateTextHelper();
+
+ mpTextHelper->RemoveEventListener(xListener);
+
+ ScAccessibleContextBase::removeAccessibleEventListener(xListener);
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleEditObject::getImplementationName()
+{
+ return "ScAccessibleEditObject";
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleEditObject::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+ //==== internal =========================================================
+
+bool ScAccessibleEditObject::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+OutputDevice* ScAccessibleEditObject::GetOutputDeviceForView()
+{
+ return mpWindow->GetOutDev();
+}
+
+void ScAccessibleEditObject::CreateTextHelper()
+{
+ if (mpTextHelper)
+ return;
+
+ ::std::unique_ptr < ScAccessibleTextData > pAccessibleTextData;
+ if (meObjectType == CellInEditMode || meObjectType == EditControl)
+ {
+ pAccessibleTextData.reset
+ (new ScAccessibleEditObjectTextData(mpEditView, GetOutputDeviceForView()));
+ }
+ else
+ {
+ pAccessibleTextData.reset
+ (new ScAccessibleEditLineTextData(nullptr, GetOutputDeviceForView(), mpTextWnd));
+ }
+
+ std::unique_ptr<ScAccessibilityEditSource> pEditSrc =
+ std::make_unique<ScAccessibilityEditSource>(std::move(pAccessibleTextData));
+
+ mpTextHelper = std::make_unique<::accessibility::AccessibleTextHelper>(std::move(pEditSrc));
+ mpTextHelper->SetEventSource(this);
+
+ const ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
+ if ( pInputHdl && pInputHdl->IsEditMode() )
+ {
+ mpTextHelper->SetFocus();
+ }
+ else
+ {
+ mpTextHelper->SetFocus(mbHasFocus);
+ }
+
+ // #i54814# activate cell in edit mode
+ if( meObjectType == CellInEditMode )
+ {
+ // do not activate cell object, if top edit line is active
+ if( pInputHdl && !pInputHdl->IsTopMode() )
+ {
+ SdrHint aHint( SdrHintKind::BeginEdit );
+ mpTextHelper->GetEditSource().GetBroadcaster().Broadcast( aHint );
+ }
+ }
+}
+
+sal_Int32 SAL_CALL ScAccessibleEditObject::getForeground( )
+{
+ return GetFgBgColor(SC_UNONAME_CCOLOR);
+}
+
+sal_Int32 SAL_CALL ScAccessibleEditObject::getBackground( )
+{
+ return GetFgBgColor(SC_UNONAME_CELLBACK);
+}
+
+sal_Int32 ScAccessibleEditObject::GetFgBgColor( const OUString &strPropColor)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nColor(0);
+ if (m_pScDoc)
+ {
+ ScDocShell* pObjSh = m_pScDoc->GetDocumentShell();
+ if ( pObjSh )
+ {
+ ScModelObj* pSpreadDoc = pObjSh->GetModel();
+ if ( pSpreadDoc )
+ {
+ uno::Reference<sheet::XSpreadsheets> xSheets = pSpreadDoc->getSheets();
+ uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY );
+ if ( xIndex.is() )
+ {
+ uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab());
+ uno::Reference<sheet::XSpreadsheet> xTable;
+ if (aTable>>=xTable)
+ {
+ uno::Reference<table::XCell> xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row());
+ if (xCell.is())
+ {
+ uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY);
+ if (xCellProps.is())
+ {
+ uno::Any aAny = xCellProps->getPropertyValue(strPropColor);
+ aAny >>= nColor;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return nColor;
+}
+//===== XAccessibleSelection ============================================
+
+void SAL_CALL ScAccessibleEditObject::selectAccessibleChild( sal_Int64 )
+{
+}
+
+sal_Bool SAL_CALL ScAccessibleEditObject::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
+ uno::Reference<XAccessibleContext> xContext;
+ if( xAcc.is() )
+ xContext = xAcc->getAccessibleContext();
+ if( xContext.is() )
+ {
+ if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
+ {
+ uno::Reference< css::accessibility::XAccessibleText >
+ xText(xAcc, uno::UNO_QUERY);
+ if( xText.is() )
+ {
+ if( xText->getSelectionStart() >= 0 ) return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SAL_CALL ScAccessibleEditObject::clearAccessibleSelection( )
+{
+}
+
+void SAL_CALL ScAccessibleEditObject::selectAllAccessibleChildren( )
+{
+}
+
+sal_Int64 SAL_CALL ScAccessibleEditObject::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 ScAccessibleEditObject::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ if ( nSelectedChildIndex < 0 || nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw 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 ScAccessibleEditObject::deselectAccessibleChild(sal_Int64)
+{
+}
+
+uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ vcl::Window* pWindow = mpWindow;
+ rtl::Reference<utl::AccessibleRelationSetHelper> rRelationSet = new utl::AccessibleRelationSetHelper;
+ if ( pWindow )
+ {
+ vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy();
+ if ( pLabeledBy && pLabeledBy != pWindow )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabeledBy->GetAccessible() };
+ rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
+ }
+ vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf();
+ if ( pMemberOf && pMemberOf != pWindow )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pMemberOf->GetAccessible() };
+ rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+ return rRelationSet;
+ }
+ return uno::Reference< XAccessibleRelationSet >();
+}
+
+AbsoluteScreenPixelRectangle ScAccessibleEditControlObject::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aScreenBounds;
+
+ if (m_pController && m_pController->GetDrawingArea())
+ {
+ aScreenBounds = AbsoluteScreenPixelRectangle(m_pController->GetDrawingArea()->get_accessible_location_on_screen(),
+ m_pController->GetOutputSizePixel());
+ }
+
+ return aScreenBounds;
+}
+
+tools::Rectangle ScAccessibleEditControlObject::GetBoundingBox() const
+{
+ tools::Rectangle aBounds( GetBoundingBoxOnScreen() );
+
+ uno::Reference< XAccessibleContext > xContext(const_cast<ScAccessibleEditControlObject*>(this)->getAccessibleContext());
+ if ( xContext.is() )
+ {
+ uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() );
+ if ( xParent.is() )
+ {
+ uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY );
+ if ( xParentComponent.is() )
+ {
+ Point aScreenLoc = aBounds.TopLeft();
+ awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen();
+ Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y );
+ aBounds.SetPos( aPos );
+ }
+ }
+ }
+
+ return aBounds;
+}
+
+void SAL_CALL ScAccessibleEditControlObject::disposing()
+{
+ ScAccessibleEditObject::disposing();
+ m_pController = nullptr;
+}
+
+uno::Reference< XAccessibleRelationSet > ScAccessibleEditControlObject::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pController || !m_pController->GetDrawingArea())
+ return uno::Reference< XAccessibleRelationSet >();
+ return m_pController->GetDrawingArea()->get_accessible_relation_set();
+}
+
+OutputDevice* ScAccessibleEditControlObject::GetOutputDeviceForView()
+{
+ if (!m_pController || !m_pController->GetDrawingArea())
+ return nullptr;
+ return &m_pController->GetDrawingArea()->get_ref_device();
+}
+
+ScAccessibleEditLineObject::ScAccessibleEditLineObject(ScTextWnd* pTextWnd)
+ : ScAccessibleEditControlObject(pTextWnd, ScAccessibleEditObject::EditLine)
+{
+ // tdf#141769 set this early so its always available, even before the on-demand
+ // editview is created
+ mpTextWnd = pTextWnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePageHeader.cxx b/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
new file mode 100644
index 0000000000..a79017276b
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
@@ -0,0 +1,372 @@
+/* -*- 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 <AccessiblePageHeader.hxx>
+#include <AccessiblePageHeaderArea.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <document.hxx>
+#include <stlpool.hxx>
+#include <scitems.hxx>
+#include <attrib.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.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 <comphelper/sequence.hxx>
+
+#include <vcl/window.hxx>
+#include <svl/hint.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/style.hxx>
+#include <editeng/editobj.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+const sal_uInt8 MAX_AREAS = 3;
+
+ScAccessiblePageHeader::ScAccessiblePageHeader( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, bool bHeader, sal_Int32 nIndex ) :
+ScAccessibleContextBase( rxParent, bHeader ? AccessibleRole::HEADER : AccessibleRole::FOOTER ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex ),
+ mbHeader( bHeader ),
+ maAreas(MAX_AREAS, rtl::Reference<ScAccessiblePageHeaderArea>()),
+ mnChildCount(-1)
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePageHeader::~ScAccessiblePageHeader()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePageHeader::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ for (auto & i : maAreas)
+ {
+ if (i.is())
+ {
+ i->dispose();
+ i.clear();
+ }
+ }
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePageHeader::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ std::vector<rtl::Reference<ScAccessiblePageHeaderArea>> aOldAreas(maAreas);
+ mnChildCount = -1;
+ getAccessibleChildCount();
+ for (sal_uInt8 i = 0; i < MAX_AREAS; ++i)
+ {
+ if ((aOldAreas[i].is() && maAreas[i].is() && !ScGlobal::EETextObjEqual(aOldAreas[i]->GetEditTextObject(), maAreas[i]->GetEditTextObject())) ||
+ (aOldAreas[i].is() && !maAreas[i].is()) || (!aOldAreas[i].is() && maAreas[i].is()))
+ {
+ if (aOldAreas[i].is() && aOldAreas[i]->GetEditTextObject())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(aOldAreas[i]);
+ aEvent.IndexHint = -1;
+
+ CommitChange(aEvent); // child gone - event
+ aOldAreas[i]->dispose();
+ }
+ if (maAreas[i].is() && maAreas[i]->GetEditTextObject())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(maAreas[i]);
+ aEvent.IndexHint = -1;
+
+ CommitChange(aEvent); // new child - event
+ }
+ }
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ uno::Reference<XAccessible> xRet;
+
+ if (containsPoint(aPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ sal_Int64 nCount(getAccessibleChildCount()); // fill the areas
+
+ if (nCount)
+ {
+ // return the first with content, because they have all the same Bounding Box
+ sal_uInt8 i(0);
+ while(!xRet.is() && i < MAX_AREAS)
+ {
+ if (maAreas[i].is())
+ xRet = maAreas[i].get();
+ else
+ ++i;
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePageHeader::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if((mnChildCount < 0) && mpViewShell)
+ {
+ mnChildCount = 0;
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ // find out how many regions (left,center, right) are with content
+
+ SfxStyleSheetBase* pStyle = rDoc.GetStyleSheetPool()->Find(rDoc.GetPageStyle(mpViewShell->GetLocationData().GetPrintTab()), SfxStyleFamily::Page);
+ if (pStyle)
+ {
+ sal_uInt16 nPageWhichId(0);
+ if (mbHeader)
+ nPageWhichId = mpViewShell->GetLocationData().IsHeaderLeft() ? ATTR_PAGE_HEADERLEFT : ATTR_PAGE_HEADERRIGHT;
+ else
+ nPageWhichId = mpViewShell->GetLocationData().IsFooterLeft() ? ATTR_PAGE_FOOTERLEFT : ATTR_PAGE_FOOTERRIGHT;
+
+ const ScPageHFItem& rPageItem = static_cast<const ScPageHFItem&>(pStyle->GetItemSet().Get(nPageWhichId));
+ AddChild(rPageItem.GetLeftArea(), 0, SvxAdjust::Left);
+ AddChild(rPageItem.GetCenterArea(), 1, SvxAdjust::Center);
+ AddChild(rPageItem.GetRightArea(), 2, SvxAdjust::Right);
+ }
+ }
+
+ return mnChildCount;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeader::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ uno::Reference<XAccessible> xRet;
+
+ if(mnChildCount < 0)
+ getAccessibleChildCount();
+
+ if (nIndex >= 0)
+ for (const auto& rxArea : maAreas)
+ {
+ if (rxArea.is())
+ {
+ if (nIndex == 0)
+ {
+ xRet = rxArea.get();
+ break;
+ }
+ else
+ --nIndex;
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeader::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePageHeader::getImplementationName()
+{
+ return "ScAccessiblePageHeader";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePageHeader::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.text.AccessibleHeaderFooterView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//==== internal =========================================================
+
+OUString ScAccessiblePageHeader::createAccessibleDescription()
+{
+ OUString sDesc(mbHeader ? STR_ACC_HEADER_DESCR : STR_ACC_FOOTER_DESCR);
+ return sDesc.replaceFirst("%1", ScResId(SCSTR_UNKNOWN));
+}
+
+OUString ScAccessiblePageHeader::createAccessibleName()
+{
+ OUString sName(ScResId(mbHeader ? STR_ACC_HEADER_NAME : STR_ACC_FOOTER_NAME));
+ return sName.replaceFirst("%1", ScResId(SCSTR_UNKNOWN));
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePageHeader::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePageHeader::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ const ScPreviewLocationData& rData = mpViewShell->GetLocationData();
+ if ( mbHeader )
+ rData.GetHeaderPosition( aRect );
+ else
+ rData.GetFooterPosition( aRect );
+
+ // the Rectangle could contain negative coordinates so it should be clipped
+ tools::Rectangle aClipRect(Point(0, 0), aRect.GetSize());
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ aClipRect = pWindow->GetWindowExtentsRelative(*pWindow->GetAccessibleParentWindow());
+ aRect = aClipRect.GetIntersection(aRect);
+ }
+ if (aRect.IsEmpty())
+ aRect.SetSize(Size(-1, -1));
+
+ return aRect;
+}
+
+bool ScAccessiblePageHeader::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePageHeader::AddChild(const EditTextObject* pArea, sal_uInt32 nIndex, SvxAdjust eAdjust)
+{
+ if (pArea && (!pArea->GetText(0).isEmpty() || (pArea->GetParagraphCount() > 1)))
+ {
+ if (maAreas[nIndex].is())
+ {
+ if (!ScGlobal::EETextObjEqual(maAreas[nIndex]->GetEditTextObject(), pArea))
+ {
+ maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, eAdjust);
+ }
+ }
+ else
+ {
+ maAreas[nIndex] = new ScAccessiblePageHeaderArea(this, mpViewShell, pArea, eAdjust);
+ }
+ ++mnChildCount;
+ }
+ else
+ {
+ maAreas[nIndex].clear();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx b/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx
new file mode 100644
index 0000000000..ec1fbf3add
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePageHeaderArea.cxx
@@ -0,0 +1,273 @@
+/* -*- 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 <tools/gen.hxx>
+#include <AccessiblePageHeaderArea.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <prevwsh.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <comphelper/sequence.hxx>
+#include <editeng/editobj.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+ //===== internal ========================================================
+
+ScAccessiblePageHeaderArea::ScAccessiblePageHeaderArea(
+ const uno::Reference<XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj,
+ SvxAdjust eAdjust)
+ : ScAccessibleContextBase(rxParent, AccessibleRole::TEXT),
+ mpEditObj(pEditObj->Clone()),
+ mpViewShell(pViewShell),
+ meAdjust(eAdjust)
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePageHeaderArea::~ScAccessiblePageHeaderArea()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePageHeaderArea::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpTextHelper.reset();
+ mpEditObj.reset();
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePageHeaderArea::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ // only notify if child exist, otherwise it is not necessary
+ if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mpTextHelper)
+ mpTextHelper->UpdateChildren();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePageHeaderArea::getAccessibleAtPoint(
+ const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mpTextHelper)
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+ //===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL
+ ScAccessiblePageHeaderArea::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessiblePageHeaderArea::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePageHeaderArea::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc())
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+// XServiceInfo
+
+OUString SAL_CALL
+ ScAccessiblePageHeaderArea::getImplementationName()
+{
+ return "ScAccessiblePageHeaderArea";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessiblePageHeaderArea::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.sheet.AccessiblePageHeaderFooterAreasView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePageHeaderArea::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//===== internal ==============================================================
+OUString ScAccessiblePageHeaderArea::createAccessibleDescription()
+{
+ OUString sDesc;
+ switch (meAdjust)
+ {
+ case SvxAdjust::Left :
+ sDesc = STR_ACC_LEFTAREA_DESCR;
+ break;
+ case SvxAdjust::Right:
+ sDesc = STR_ACC_RIGHTAREA_DESCR;
+ break;
+ case SvxAdjust::Center:
+ sDesc = STR_ACC_CENTERAREA_DESCR;
+ break;
+ default:
+ OSL_FAIL("wrong adjustment found");
+ }
+
+ return sDesc;
+}
+
+OUString ScAccessiblePageHeaderArea::createAccessibleName()
+{
+ OUString sName;
+ switch (meAdjust)
+ {
+ case SvxAdjust::Left :
+ sName = ScResId(STR_ACC_LEFTAREA_NAME);
+ break;
+ case SvxAdjust::Right:
+ sName = ScResId(STR_ACC_RIGHTAREA_NAME);
+ break;
+ case SvxAdjust::Center:
+ sName = ScResId(STR_ACC_CENTERAREA_NAME);
+ break;
+ default:
+ OSL_FAIL("wrong adjustment found");
+ }
+
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePageHeaderArea::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xContext = mxParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xComp(xContext, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // has the same size and position on screen like the parent
+ aRect = AbsoluteScreenPixelRectangle(
+ AbsoluteScreenPixelPoint(VCLPoint(xComp->getLocationOnScreen())),
+ AbsoluteScreenPixelSize(VCLRectangle(xComp->getBounds()).GetSize()));
+ }
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessiblePageHeaderArea::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mxParent.is())
+ {
+ uno::Reference<XAccessibleContext> xContext = mxParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xComp(xContext, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // has the same size and position on screen like the parent and so the pos is (0, 0)
+ tools::Rectangle aNewRect(Point(0, 0), VCLRectangle(xComp->getBounds()).GetSize());
+ aRect = aNewRect;
+ }
+ }
+
+ return aRect;
+}
+
+void ScAccessiblePageHeaderArea::CreateTextHelper()
+{
+ if (!mpTextHelper)
+ {
+ mpTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessibleHeaderTextData>(
+ mpViewShell, mpEditObj.get(), meAdjust))) );
+ mpTextHelper->SetEventSource(this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx b/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx
new file mode 100644
index 0000000000..3c4d334305
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewCell.cxx
@@ -0,0 +1,272 @@
+/* -*- 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 <scitems.hxx>
+#include <tools/gen.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <AccessiblePreviewCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <document.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <editeng/brushitem.hxx>
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewCell::ScAccessiblePreviewCell( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const ScAddress& rCellAddress,
+ sal_Int32 nIndex ) :
+ ScAccessibleCellBase( rxParent, ( pViewShell ? &pViewShell->GetDocument() : nullptr ), rCellAddress, nIndex ),
+ mpViewShell( pViewShell )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewCell::~ScAccessiblePreviewCell()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ // call dispose to inform object which have a weak reference to this object
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePreviewCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTextHelper.reset();
+
+ ScAccessibleCellBase::disposing();
+}
+
+void ScAccessiblePreviewCell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mpTextHelper)
+ mpTextHelper->UpdateChildren();
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewCell::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mpTextHelper)
+ CreateTextHelper();
+
+ xRet = mpTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpTextHelper)
+ CreateTextHelper();
+ return mpTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (IsOpaque())
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ // MANAGES_DESCENDANTS (for paragraphs)
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewCell::getImplementationName()
+{
+ return "ScAccessiblePreviewCell";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.table.AccessibleCellView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePreviewCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewCell::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect );
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewCell::GetBoundingBox() const
+{
+ tools::Rectangle aCellRect;
+ if (mpViewShell)
+ {
+ mpViewShell->GetLocationData().GetCellPosition( maCellAddress, aCellRect );
+ uno::Reference<XAccessible> xAccParent = const_cast<ScAccessiblePreviewCell*>(this)->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference<XAccessibleContext> xAccParentContext = xAccParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xAccParentComp (xAccParentContext, uno::UNO_QUERY);
+ if (xAccParentComp.is())
+ {
+ tools::Rectangle aParentRect (VCLRectangle(xAccParentComp->getBounds()));
+ aCellRect.Move(-aParentRect.Left(), -aParentRect.Top());
+ }
+ }
+ }
+ return aCellRect;
+}
+
+bool ScAccessiblePreviewCell::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpDoc == nullptr) || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessiblePreviewCell::IsEditable(sal_Int64 /* nParentStates */)
+{
+ return false;
+}
+
+bool ScAccessiblePreviewCell::IsOpaque() const
+{
+ // test whether there is a background color
+ //! could be moved to ScAccessibleCellBase
+
+ bool bOpaque(true);
+ if (mpDoc)
+ {
+ const SvxBrushItem* pItem = mpDoc->GetAttr(maCellAddress, ATTR_BACKGROUND);
+ if (pItem)
+ bOpaque = pItem->GetColor() != COL_TRANSPARENT;
+ }
+ return bOpaque;
+}
+
+void ScAccessiblePreviewCell::CreateTextHelper()
+{
+ if (mpTextHelper)
+ return;
+
+ mpTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessiblePreviewCellTextData>(
+ mpViewShell, maCellAddress))) );
+ mpTextHelper->SetEventSource( this );
+
+ // paragraphs in preview are transient
+ mpTextHelper->SetAdditionalChildStates( AccessibleStateType::TRANSIENT );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx b/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx
new file mode 100644
index 0000000000..d772db890a
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewHeaderCell.cxx
@@ -0,0 +1,403 @@
+/* -*- 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 <tools/gen.hxx>
+#include <AccessibleText.hxx>
+#include <editsrc.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <AccessiblePreviewHeaderCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <strings.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <comphelper/sequence.hxx>
+
+#include <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <toolkit/helper/convert.hxx>
+
+#ifdef indices
+#undef indices
+#endif
+
+#ifdef extents
+#undef extents
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewHeaderCell::ScAccessiblePreviewHeaderCell( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell,
+ const ScAddress& rCellPos, bool bIsColHdr, bool bIsRowHdr,
+ sal_Int32 nIndex ) :
+ ScAccessibleContextBase( rxParent, AccessibleRole::TABLE_CELL ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex ),
+ maCellPos( rCellPos ),
+ mbColumnHeader( bIsColHdr ),
+ mbRowHeader( bIsRowHdr )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewHeaderCell::~ScAccessiblePreviewHeaderCell()
+{
+ if (mpViewShell)
+ mpViewShell->RemoveAccessibilityObject(*this);
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTableInfo.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePreviewHeaderCell::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if (nId == SfxHintId::ScAccVisAreaChanged)
+ {
+ if (mxTextHelper)
+ mxTextHelper->UpdateChildren();
+ }
+ else if ( nId == SfxHintId::DataChanged )
+ {
+ // column / row layout may change with any document change,
+ // so it must be invalidated
+ mpTableInfo.reset();
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessiblePreviewHeaderCellImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+//===== XAccessibleValue ================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getCurrentValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ double fValue(0.0);
+ if (mbColumnHeader)
+ fValue = maCellPos.Col();
+ else
+ fValue = maCellPos.Row();
+
+ return uno::Any(fValue);
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewHeaderCell::setCurrentValue( const uno::Any& /* aNumber */ )
+{
+ // it is not possible to set a value
+ return false;
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMaximumValue()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ double fValue(0.0);
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ if (mbColumnHeader)
+ fValue = rDoc.MaxCol();
+ else
+ fValue = rDoc.MaxRow();
+ return uno::Any(fValue);
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMinimumValue()
+{
+ return uno::Any(0.0);
+}
+
+uno::Any SAL_CALL ScAccessiblePreviewHeaderCell::getMinimumIncrement()
+{
+ // value can't be changed, s. 'setCurrentValue'
+ return uno::Any();
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if(!mxTextHelper)
+ CreateTextHelper();
+
+ xRet = mxTextHelper->GetAt(rPoint);
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewHeaderCell::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mxTextHelper)
+ CreateTextHelper();
+ return mxTextHelper->GetChildCount();
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mxTextHelper)
+ CreateTextHelper();
+ return mxTextHelper->GetChild(nIndex);
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewHeaderCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::MULTI_LINE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ nStateSet |= AccessibleStateType::TRANSIENT;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewHeaderCell::getImplementationName()
+{
+ return "ScAccessiblePreviewHeaderCell";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewHeaderCell::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.table.AccessibleCellView" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessiblePreviewHeaderCell::getTypes()
+{
+ return comphelper::concatSequences(ScAccessiblePreviewHeaderCellImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessiblePreviewHeaderCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewHeaderCell::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect;
+
+ FillTableInfo();
+
+ if (mpTableInfo)
+ {
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[maCellPos.Col()];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[maCellPos.Row()];
+
+ aCellRect = tools::Rectangle( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ }
+
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewHeaderCell::GetBoundingBox() const
+{
+ FillTableInfo();
+
+ if (mpTableInfo)
+ {
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[maCellPos.Col()];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[maCellPos.Row()];
+
+ tools::Rectangle aCellRect( rColInfo.nPixelStart, rRowInfo.nPixelStart, rColInfo.nPixelEnd, rRowInfo.nPixelEnd );
+ uno::Reference<XAccessible> xAccParent = const_cast<ScAccessiblePreviewHeaderCell*>(this)->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference<XAccessibleContext> xAccParentContext = xAccParent->getAccessibleContext();
+ uno::Reference<XAccessibleComponent> xAccParentComp (xAccParentContext, uno::UNO_QUERY);
+ if (xAccParentComp.is())
+ {
+ tools::Rectangle aParentRect (VCLRectangle(xAccParentComp->getBounds()));
+ aCellRect.Move(-aParentRect.Left(), -aParentRect.Top());
+ }
+ }
+ return aCellRect;
+ }
+ return tools::Rectangle();
+}
+
+OUString ScAccessiblePreviewHeaderCell::createAccessibleDescription()
+{
+ return STR_ACC_HEADERCELL_DESCR;
+}
+
+OUString ScAccessiblePreviewHeaderCell::createAccessibleName()
+{
+ OUString sName = STR_ACC_HEADERCELL_NAME;
+
+ if ( mbColumnHeader )
+ {
+ if ( mbRowHeader )
+ {
+ //! name for corner cell?
+
+// sName = "Column/Row Header";
+ }
+ else
+ {
+ // name of column header
+ sName += ScColToAlpha( maCellPos.Col() );
+ }
+ }
+ else
+ {
+ // name of row header
+ sName += OUString::number( maCellPos.Row() + 1 );
+ }
+
+ return sName;
+}
+
+bool ScAccessiblePreviewHeaderCell::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePreviewHeaderCell::CreateTextHelper()
+{
+ if (!mxTextHelper)
+ {
+ mxTextHelper.reset( new ::accessibility::AccessibleTextHelper(
+ std::make_unique<ScAccessibilityEditSource>(
+ std::make_unique<ScAccessiblePreviewHeaderCellTextData>(
+ mpViewShell, getAccessibleName(), maCellPos,
+ mbColumnHeader, mbRowHeader))) );
+ mxTextHelper->SetEventSource(this);
+ }
+}
+
+void ScAccessiblePreviewHeaderCell::FillTableInfo() const
+{
+ if ( mpViewShell && !mpTableInfo )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+
+ mpTableInfo.reset( new ScPreviewTableInfo );
+ mpViewShell->GetLocationData().GetTableInfo( aVisRect, *mpTableInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx b/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx
new file mode 100644
index 0000000000..925ad1075c
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessiblePreviewTable.cxx
@@ -0,0 +1,640 @@
+/* -*- 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 <scitems.hxx>
+#include <AccessiblePreviewTable.hxx>
+#include <AccessiblePreviewCell.hxx>
+#include <AccessiblePreviewHeaderCell.hxx>
+#include <prevwsh.hxx>
+#include <prevloc.hxx>
+#include <attrib.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.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 <vcl/window.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessiblePreviewTable::ScAccessiblePreviewTable( const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ ScPreviewShell* pViewShell, sal_Int32 nIndex ) :
+ ScAccessibleContextBase( rxParent, AccessibleRole::TABLE ),
+ mpViewShell( pViewShell ),
+ mnIndex( nIndex )
+{
+ if (mpViewShell)
+ mpViewShell->AddAccessibilityObject(*this);
+}
+
+ScAccessiblePreviewTable::~ScAccessiblePreviewTable()
+{
+ if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose)
+ {
+ // increment refcount to prevent double call off dtor
+ osl_atomic_increment( &m_refCount );
+ dispose();
+ }
+}
+
+void SAL_CALL ScAccessiblePreviewTable::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+
+ mpTableInfo.reset();
+
+ ScAccessibleContextBase::disposing();
+}
+
+//===== SfxListener =====================================================
+
+void ScAccessiblePreviewTable::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ const SfxHintId nId = rHint.GetId();
+ if ( nId == SfxHintId::DataChanged )
+ {
+ // column / row layout may change with any document change,
+ // so it must be invalidated
+ mpTableInfo.reset();
+ }
+ else if (nId == SfxHintId::ScAccVisAreaChanged)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ CommitChange(aEvent);
+ }
+
+ ScAccessibleContextBase::Notify(rBC, rHint);
+}
+
+//===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessiblePreviewTable::queryInterface( uno::Type const & rType )
+{
+ uno::Any aAny (ScAccessiblePreviewTableImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+}
+
+void SAL_CALL ScAccessiblePreviewTable::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessiblePreviewTable::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+//===== XAccessibleTable ================================================
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRet = 0;
+ if ( mpTableInfo )
+ nRet = mpTableInfo->GetRows();
+ return nRet;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRet = 0;
+ if ( mpTableInfo )
+ nRet = mpTableInfo->GetCols();
+ return nRet;
+}
+
+OUString SAL_CALL ScAccessiblePreviewTable::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nRow < 0 || (mpTableInfo && nRow >= mpTableInfo->GetRows()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return OUString();
+}
+
+OUString SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nColumn < 0 || (mpTableInfo && nColumn >= mpTableInfo->GetCols()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nRows = 1;
+ if ( !mpViewShell || !mpTableInfo || nColumn < 0 || nRow < 0 ||
+ nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ // header cells only span a single cell
+ }
+ else
+ {
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ const ScMergeAttr* pItem = rDoc.GetAttr(
+ static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab(), ATTR_MERGE );
+ if ( pItem && pItem->GetRowMerge() > 0 )
+ nRows = pItem->GetRowMerge();
+ }
+
+ return nRows;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ sal_Int32 nColumns = 1;
+ if ( !mpViewShell || !mpTableInfo || nColumn < 0 || nRow < 0 ||
+ nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ // header cells only span a single cell
+ }
+ else
+ {
+ ScDocument& rDoc = mpViewShell->GetDocument();
+ const ScMergeAttr* pItem = rDoc.GetAttr(
+ static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab(), ATTR_MERGE );
+ if ( pItem && pItem->GetColMerge() > 0 )
+ nColumns = pItem->GetColMerge();
+ }
+
+ return nColumns;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessiblePreviewTable::getAccessibleRowHeaders()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessiblePreviewTable::getAccessibleColumnHeaders()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessiblePreviewTable::getSelectedAccessibleRows()
+{
+ // in the page preview, there is no selection
+ return {};
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessiblePreviewTable::getSelectedAccessibleColumns()
+{
+ // in the page preview, there is no selection
+ return {};
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ // in the page preview, there is no selection
+
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nRow < 0 || (mpTableInfo && nRow >= mpTableInfo->GetRows()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ // in the page preview, there is no selection
+
+ SolarMutexGuard aGuard;
+ FillTableInfo();
+ if ( nColumn < 0 || (mpTableInfo && nColumn >= mpTableInfo->GetCols()) )
+ throw lang::IndexOutOfBoundsException();
+
+ return false;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ uno::Reference<XAccessible> xRet;
+ if ( mpTableInfo && nColumn >= 0 && nRow >= 0 && nColumn < mpTableInfo->GetCols() && nRow < mpTableInfo->GetRows() )
+ {
+ // index iterates horizontally
+ tools::Long nNewIndex = nRow * mpTableInfo->GetCols() + nColumn;
+
+ const ScPreviewColRowInfo& rColInfo = mpTableInfo->GetColInfo()[nColumn];
+ const ScPreviewColRowInfo& rRowInfo = mpTableInfo->GetRowInfo()[nRow];
+
+ ScAddress aCellPos( static_cast<SCCOL>(rColInfo.nDocIndex), static_cast<SCROW>(rRowInfo.nDocIndex), mpTableInfo->GetTab() );
+ if ( rColInfo.bIsHeader || rRowInfo.bIsHeader )
+ {
+ const bool bRotatedColHeader = rRowInfo.bIsHeader;
+ const bool bRotatedRowHeader = rColInfo.bIsHeader;
+ rtl::Reference<ScAccessiblePreviewHeaderCell> pHeaderCell(new ScAccessiblePreviewHeaderCell(this, mpViewShell, aCellPos,
+ bRotatedColHeader, bRotatedRowHeader, nNewIndex));
+ xRet = pHeaderCell.get();
+ pHeaderCell->Init();
+ }
+ else
+ {
+ rtl::Reference<ScAccessiblePreviewCell> pCell(new ScAccessiblePreviewCell( this, mpViewShell, aCellPos, nNewIndex ));
+ xRet = pCell.get();
+ pCell->Init();
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleCaption()
+{
+ //! missing
+ return nullptr;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleSummary()
+{
+ //! missing
+ return nullptr;
+}
+
+sal_Bool SAL_CALL ScAccessiblePreviewTable::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ // in the page preview, there is no selection
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nColumn < 0 || nRow < 0 || nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ // index iterates horizontally
+ return false;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nColumn < 0 || nRow < 0 || nColumn >= mpTableInfo->GetCols() || nRow >= mpTableInfo->GetRows() )
+ throw lang::IndexOutOfBoundsException();
+
+ // index iterates horizontally
+ sal_Int64 nRet = static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(mpTableInfo->GetCols()) + nColumn;
+ return nRet;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast<sal_Int64>(mpTableInfo->GetRows()) * mpTableInfo->GetCols() )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nRow = nChildIndex / mpTableInfo->GetCols();
+ return nRow;
+}
+
+sal_Int32 SAL_CALL ScAccessiblePreviewTable::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( !mpTableInfo || nChildIndex < 0 || nChildIndex >= static_cast<sal_Int64>(mpTableInfo->GetRows()) * mpTableInfo->GetCols() )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCol = nChildIndex % static_cast<sal_Int32>(mpTableInfo->GetCols());
+ return nCol;
+}
+
+//===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ uno::Reference<XAccessible> xRet;
+ if (containsPoint(aPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ if ( mpTableInfo )
+ {
+ SCCOL nCols = mpTableInfo->GetCols();
+ SCROW nRows = mpTableInfo->GetRows();
+ const ScPreviewColRowInfo* pColInfo = mpTableInfo->GetColInfo();
+ const ScPreviewColRowInfo* pRowInfo = mpTableInfo->GetRowInfo();
+
+ tools::Rectangle aScreenRect(GetBoundingBox());
+
+ awt::Point aMovedPoint = aPoint;
+ aMovedPoint.X += aScreenRect.Left();
+ aMovedPoint.Y += aScreenRect.Top();
+
+ if ( nCols > 0 && nRows > 0 && aMovedPoint.X >= pColInfo[0].nPixelStart && aMovedPoint.Y >= pRowInfo[0].nPixelStart )
+ {
+ SCCOL nColIndex = 0;
+ while ( nColIndex < nCols && aMovedPoint.X > pColInfo[nColIndex].nPixelEnd )
+ ++nColIndex;
+ SCROW nRowIndex = 0;
+ while ( nRowIndex < nRows && aMovedPoint.Y > pRowInfo[nRowIndex].nPixelEnd )
+ ++nRowIndex;
+ if ( nColIndex < nCols && nRowIndex < nRows )
+ {
+ try
+ {
+ xRet = getAccessibleCellAt( nRowIndex, nColIndex );
+ }
+ catch (uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ }
+
+ return xRet;
+}
+
+void SAL_CALL ScAccessiblePreviewTable::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+//===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ tools::Long nRet = 0;
+ if ( mpTableInfo )
+ nRet = static_cast<sal_Int64>(mpTableInfo->GetCols()) * mpTableInfo->GetRows();
+ return nRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessiblePreviewTable::getAccessibleChild( sal_Int64 nIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ FillTableInfo();
+
+ uno::Reference<XAccessible> xRet;
+ if ( mpTableInfo )
+ {
+ sal_Int32 nColumns = mpTableInfo->GetCols();
+ if ( nColumns > 0 )
+ {
+ // nCol, nRow are within the visible table, not the document
+ sal_Int32 nCol = nIndex % nColumns;
+ sal_Int64 nRow = nIndex / nColumns;
+ assert(nRow <= std::numeric_limits<sal_Int32>::max());
+ xRet = getAccessibleCellAt( nRow, nCol );
+ }
+ }
+
+ if ( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleIndexInParent()
+{
+ return mnIndex;
+}
+
+sal_Int64 SAL_CALL ScAccessiblePreviewTable::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+//===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessiblePreviewTable::getImplementationName()
+{
+ return "ScAccessiblePreviewTable";
+}
+
+uno::Sequence<OUString> SAL_CALL ScAccessiblePreviewTable::getSupportedServiceNames()
+{
+ uno::Sequence< OUString > aSequence = ScAccessibleContextBase::getSupportedServiceNames();
+ sal_Int32 nOldSize(aSequence.getLength());
+ aSequence.realloc(nOldSize + 1);
+
+ aSequence.getArray()[nOldSize] = "com.sun.star.table.AccessibleTableView";
+
+ return aSequence;
+}
+
+//===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessiblePreviewTable::getTypes()
+{
+ return comphelper::concatSequences(ScAccessiblePreviewTableImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL ScAccessiblePreviewTable::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+//==== internal =========================================================
+
+OUString ScAccessiblePreviewTable::createAccessibleDescription()
+{
+ return STR_ACC_TABLE_DESCR;
+}
+
+OUString ScAccessiblePreviewTable::createAccessibleName()
+{
+ OUString sName(ScResId(STR_ACC_TABLE_NAME));
+
+ if (mpViewShell)
+ {
+ FillTableInfo();
+
+ if ( mpTableInfo )
+ {
+ OUString sCoreName;
+ if (mpViewShell->GetDocument().GetName( mpTableInfo->GetTab(), sCoreName ))
+ sName = sName.replaceFirst("%1", sCoreName);
+ }
+ }
+
+ return sName;
+}
+
+AbsoluteScreenPixelRectangle ScAccessiblePreviewTable::GetBoundingBoxOnScreen() const
+{
+ tools::Rectangle aCellRect(GetBoundingBox());
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ AbsoluteScreenPixelRectangle aRect = pWindow->GetWindowExtentsAbsolute();
+ aCellRect.Move(aRect.Left(), aRect.Top());
+ }
+ }
+ return AbsoluteScreenPixelRectangle(aCellRect);
+}
+
+tools::Rectangle ScAccessiblePreviewTable::GetBoundingBox() const
+{
+ FillTableInfo();
+
+ tools::Rectangle aRect;
+ if ( mpTableInfo )
+ {
+ SCCOL nColumns = mpTableInfo->GetCols();
+ SCROW nRows = mpTableInfo->GetRows();
+ if ( nColumns > 0 && nRows > 0 )
+ {
+ const ScPreviewColRowInfo* pColInfo = mpTableInfo->GetColInfo();
+ const ScPreviewColRowInfo* pRowInfo = mpTableInfo->GetRowInfo();
+
+ aRect = tools::Rectangle( pColInfo[0].nPixelStart,
+ pRowInfo[0].nPixelStart,
+ pColInfo[nColumns-1].nPixelEnd,
+ pRowInfo[nRows-1].nPixelEnd );
+ }
+ }
+ return aRect;
+}
+
+bool ScAccessiblePreviewTable::IsDefunc( sal_Int64 nParentStates )
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+void ScAccessiblePreviewTable::FillTableInfo() const
+{
+ if ( mpViewShell && !mpTableInfo )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+
+ mpTableInfo.reset( new ScPreviewTableInfo );
+ mpViewShell->GetLocationData().GetTableInfo( aVisRect, *mpTableInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx
new file mode 100644
index 0000000000..7aaa7237cc
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx
@@ -0,0 +1,1669 @@
+/* -*- 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 <AccessibleSpreadsheet.hxx>
+#include <AccessibleCell.hxx>
+#include <AccessibleDocument.hxx>
+#include <tabvwsh.hxx>
+#include <document.hxx>
+#include <hints.hxx>
+#include <scmod.hxx>
+#include <markdata.hxx>
+#include <gridwin.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <sal/log.hxx>
+#include <tools/gen.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/svapp.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+
+#include <algorithm>
+#include <cstdlib>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+static bool CompMinCol(const std::pair<sal_uInt16,sal_uInt16> & pc1,const std::pair<sal_uInt16,sal_uInt16> &pc2)
+{
+ return pc1.first < pc2.first;
+}
+
+ScMyAddress ScAccessibleSpreadsheet::CalcScAddressFromRangeList(ScRangeList *pMarkedRanges,sal_Int32 nSelectedChildIndex)
+{
+ if (pMarkedRanges->size() <= 1)
+ {
+ ScRange const & rRange = pMarkedRanges->front();
+ // MT IA2: Not used.
+ // const int nRowNum = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ const int nColNum = rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ const int nCurCol = nSelectedChildIndex % nColNum;
+ const int nCurRow = (nSelectedChildIndex - nCurCol)/nColNum;
+ return ScMyAddress(static_cast<SCCOL>(rRange.aStart.Col() + nCurCol), rRange.aStart.Row() + nCurRow, maActiveCell.Tab());
+ }
+ else
+ {
+ ScDocument* pDoc= GetDocument(mpViewShell);
+ sal_Int32 nMinRow = pDoc->MaxRow();
+ sal_Int32 nMaxRow = 0;
+ std::vector<ScRange> aRanges;
+ size_t nSize = pMarkedRanges->size();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ ScRange const & rRange = (*pMarkedRanges)[i];
+ if (rRange.aStart.Tab() != rRange.aEnd.Tab())
+ {
+ if ((maActiveCell.Tab() >= rRange.aStart.Tab()) ||
+ maActiveCell.Tab() <= rRange.aEnd.Tab())
+ {
+ aRanges.push_back(rRange);
+ nMinRow = std::min(rRange.aStart.Row(),nMinRow);
+ nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
+ }
+ else
+ SAL_WARN("sc", "Range of wrong table");
+ }
+ else if(rRange.aStart.Tab() == maActiveCell.Tab())
+ {
+ aRanges.push_back(rRange);
+ nMinRow = std::min(rRange.aStart.Row(),nMinRow);
+ nMaxRow = std::max(rRange.aEnd.Row(),nMaxRow);
+ }
+ else
+ SAL_WARN("sc", "Range of wrong table");
+ }
+ int nCurrentIndex = 0 ;
+ for(sal_Int32 row = nMinRow ; row <= nMaxRow ; ++row)
+ {
+ std::vector<std::pair<SCCOL, SCCOL>> aVecCol;
+ for (ScRange const & r : aRanges)
+ {
+ if ( row >= r.aStart.Row() && row <= r.aEnd.Row())
+ {
+ aVecCol.emplace_back(r.aStart.Col(), r.aEnd.Col());
+ }
+ }
+ std::sort(aVecCol.begin(), aVecCol.end(), CompMinCol);
+ for (const std::pair<SCCOL, SCCOL> &pairCol : aVecCol)
+ {
+ SCCOL nCol = pairCol.second - pairCol.first + 1;
+ if (nCol + nCurrentIndex > nSelectedChildIndex)
+ {
+ return ScMyAddress(static_cast<SCCOL>(pairCol.first + nSelectedChildIndex - nCurrentIndex), row, maActiveCell.Tab());
+ }
+ nCurrentIndex += nCol;
+ }
+ }
+ }
+ return ScMyAddress(0,0,maActiveCell.Tab());
+}
+
+bool ScAccessibleSpreadsheet::CalcScRangeDifferenceMax(const ScRange & rSrc, const ScRange & rDest, int nMax,
+ std::vector<ScMyAddress> &vecRet, int &nSize)
+{
+ //Src Must be :Src > Dest
+ if (rDest.Contains(rSrc))
+ {//Here is Src In Dest,Src <= Dest
+ return false;
+ }
+ if (!rDest.Intersects(rSrc))
+ {
+ int nCellCount = sal_uInt32(rDest.aEnd.Col() - rDest.aStart.Col() + 1)
+ * sal_uInt32(rDest.aEnd.Row() - rDest.aStart.Row() + 1)
+ * sal_uInt32(rDest.aEnd.Tab() - rDest.aStart.Tab() + 1);
+ if (nCellCount + nSize > nMax)
+ {
+ return true;
+ }
+ else if(nCellCount > 0)
+ {
+ for (sal_Int32 row = rDest.aStart.Row(); row <= rDest.aEnd.Row();++row)
+ {
+ for (sal_uInt16 col = rDest.aStart.Col(); col <= rDest.aEnd.Col();++col)
+ {
+ vecRet.emplace_back(col,row,rDest.aStart.Tab());
+ }
+ }
+ }
+ return false;
+ }
+ sal_Int32 nMinRow = rSrc.aStart.Row();
+ sal_Int32 nMaxRow = rSrc.aEnd.Row();
+ for (; nMinRow <= nMaxRow ; ++nMinRow,--nMaxRow)
+ {
+ for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
+ {
+ if (nSize > nMax)
+ {
+ return true;
+ }
+ ScMyAddress cell(col,nMinRow,rSrc.aStart.Tab());
+ if(!rDest.Contains(cell))
+ {//In Src ,Not In Dest
+ vecRet.push_back(cell);
+ ++nSize;
+ }
+ }
+ if (nMinRow != nMaxRow)
+ {
+ for (sal_uInt16 col = rSrc.aStart.Col(); col <= rSrc.aEnd.Col();++col)
+ {
+ if (nSize > nMax)
+ {
+ return true;
+ }
+ ScMyAddress cell(col,nMaxRow,rSrc.aStart.Tab());
+ if(!rDest.Contains(cell))
+ {//In Src ,Not In Dest
+ vecRet.push_back(cell);
+ ++nSize;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+//In Src , Not in Dest
+bool ScAccessibleSpreadsheet::CalcScRangeListDifferenceMax(ScRangeList *pSrc, ScRangeList *pDest,
+ int nMax, std::vector<ScMyAddress> &vecRet)
+{
+ if (pSrc == nullptr || pDest == nullptr)
+ {
+ return false;
+ }
+ int nSize =0;
+ if (pDest->GetCellCount() == 0)//if the Dest Rang List is empty
+ {
+ if (pSrc->GetCellCount() > o3tl::make_unsigned(nMax))//if the Src Cell count is greater than nMax
+ {
+ return true;
+ }
+ //now the cell count is less than nMax
+ vecRet.reserve(10);
+ size_t nSrcSize = pSrc->size();
+ for (size_t i = 0; i < nSrcSize; ++i)
+ {
+ ScRange const & rRange = (*pSrc)[i];
+ for (sal_Int32 row = rRange.aStart.Row(); row <= rRange.aEnd.Row();++row)
+ {
+ for (sal_uInt16 col = rRange.aStart.Col(); col <= rRange.aEnd.Col();++col)
+ {
+ vecRet.emplace_back(col,row, rRange.aStart.Tab());
+ }
+ }
+ }
+ return false;
+ }
+ //the Dest Rang List is not empty
+ vecRet.reserve(10);
+ size_t nSizeSrc = pSrc->size();
+ for (size_t i = 0; i < nSizeSrc; ++i)
+ {
+ ScRange const & rRange = (*pSrc)[i];
+ size_t nSizeDest = pDest->size();
+ for (size_t j = 0; j < nSizeDest; ++j)
+ {
+ ScRange const & rRangeDest = (*pDest)[j];
+ if (CalcScRangeDifferenceMax(rRange,rRangeDest,nMax,vecRet,nSize))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//===== internal ============================================================
+
+// FIXME: really unclear why we have an ScAccessibleTableBase with
+// only this single sub-class
+ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos)
+ :
+ ScAccessibleTableBase( pAccDoc, GetDocument(pViewShell), ScRange( 0, 0, nTab, GetDocument(pViewShell)->MaxCol(), GetDocument(pViewShell)->MaxRow(), nTab)),
+ mbIsSpreadsheet( true ),
+ m_bFormulaMode( false ),
+ m_bFormulaLastMode( false ),
+ m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
+{
+ ConstructScAccessibleSpreadsheet( pAccDoc, pViewShell, nTab, eSplitPos );
+}
+
+ScAccessibleSpreadsheet::ScAccessibleSpreadsheet(
+ ScAccessibleSpreadsheet& rParent, const ScRange& rRange ) :
+ ScAccessibleTableBase( rParent.mpAccDoc, rParent.mpDoc, rRange),
+ mbIsSpreadsheet( false ),
+ m_bFormulaMode( false ),
+ m_bFormulaLastMode( false ),
+ m_nMinX(0),m_nMaxX(0),m_nMinY(0),m_nMaxY(0)
+{
+ ConstructScAccessibleSpreadsheet( rParent.mpAccDoc, rParent.mpViewShell, rParent.mnTab, rParent.meSplitPos );
+}
+
+ScAccessibleSpreadsheet::~ScAccessibleSpreadsheet()
+{
+ mpMarkedRanges.reset();
+ if (mpViewShell)
+ mpViewShell->RemoveAccessibilityObject(*this);
+}
+
+void ScAccessibleSpreadsheet::ConstructScAccessibleSpreadsheet(
+ ScAccessibleDocument* pAccDoc,
+ ScTabViewShell* pViewShell,
+ SCTAB nTab,
+ ScSplitPos eSplitPos)
+{
+ mpViewShell = pViewShell;
+ mpMarkedRanges = nullptr;
+ mpAccDoc = pAccDoc;
+ mpAccCell.clear();
+ meSplitPos = eSplitPos;
+ mnTab = nTab;
+ mbDelIns = false;
+ mbIsFocusSend = false;
+ if (!mpViewShell)
+ return;
+
+ mpViewShell->AddAccessibilityObject(*this);
+
+ const ScViewData& rViewData = mpViewShell->GetViewData();
+ maActiveCell = rViewData.GetCurPos();
+ mpAccCell = GetAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ pScDoc->GetName( maActiveCell.Tab(), m_strOldTabName );
+ }
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::disposing()
+{
+ SolarMutexGuard aGuard;
+ if (mpViewShell)
+ {
+ mpViewShell->RemoveAccessibilityObject(*this);
+ mpViewShell = nullptr;
+ }
+ mpAccCell.clear();
+
+ ScAccessibleTableBase::disposing();
+}
+
+void ScAccessibleSpreadsheet::CompleteSelectionChanged(bool bNewState)
+{
+ if (IsFormulaMode())
+ {
+ return ;
+ }
+ mpMarkedRanges.reset();
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ if (bNewState)
+ aEvent.NewValue <<= AccessibleStateType::SELECTED;
+ else
+ aEvent.OldValue <<= AccessibleStateType::SELECTED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::LostFocus()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
+
+ CommitChange(aEvent);
+
+ CommitFocusLost();
+}
+
+void ScAccessibleSpreadsheet::GotFocus()
+{
+ CommitFocusGained();
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ uno::Reference< XAccessible > xNew;
+ if (IsFormulaMode())
+ {
+ if (!m_pAccFormulaCell.is() || !m_bFormulaLastMode)
+ {
+ ScAddress aFormulaAddr;
+ if(!GetFormulaCurrentFocusCell(aFormulaAddr))
+ {
+ return;
+ }
+ m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(),aFormulaAddr.Col());
+ }
+ xNew = m_pAccFormulaCell.get();
+ }
+ else
+ {
+ if(mpAccCell->GetCellAddress() == maActiveCell)
+ {
+ xNew = mpAccCell.get();
+ }
+ else
+ {
+ CommitFocusCell(maActiveCell);
+ return ;
+ }
+ }
+ aEvent.NewValue <<= xNew;
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::BoundingBoxChanged()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::BOUNDRECT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::VisAreaChanged()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+
+ CommitChange(aEvent);
+}
+
+ //===== SfxListener =====================================================
+
+void ScAccessibleSpreadsheet::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( auto pRefHint = dynamic_cast<const ScUpdateRefHint*>(&rHint) )
+ {
+ if (pRefHint->GetMode() == URM_INSDEL && pRefHint->GetDz() == 0) //test whether table is inserted or deleted
+ {
+ if (((pRefHint->GetRange().aStart.Col() == maRange.aStart.Col()) &&
+ (pRefHint->GetRange().aEnd.Col() == maRange.aEnd.Col())) ||
+ ((pRefHint->GetRange().aStart.Row() == maRange.aStart.Row()) &&
+ (pRefHint->GetRange().aEnd.Row() == maRange.aEnd.Row())))
+ {
+ // ignore next SfxHintId::ScDataChanged notification
+ mbDelIns = true;
+
+ SCROW nFirstRow = -1;
+ SCROW nLastRow = -1;
+ SCCOL nFirstCol = -1;
+ SCCOL nLastCol = -1;
+
+ sal_Int16 nId(0);
+ SCCOL nX(pRefHint->GetDx());
+ SCROW nY(pRefHint->GetDy());
+ ScRange aRange(pRefHint->GetRange());
+ if ((nX < 0) || (nY < 0))
+ {
+ assert(!((nX < 0) && (nY < 0)) && "should not be possible to remove row and column at the same time");
+
+ // Range in the update hint is the range after the removed rows/columns;
+ // calculate indices for the removed ones from that
+ if (nX < 0)
+ {
+ nId = AccessibleTableModelChangeType::COLUMNS_REMOVED;
+ nFirstCol = aRange.aStart.Col() + nX;
+ nLastCol = aRange.aStart.Col() - 1;
+ }
+ else
+ {
+ nId = AccessibleTableModelChangeType::ROWS_REMOVED;
+ nFirstRow = aRange.aStart.Row() + nY;
+ nLastRow = aRange.aStart.Row() - 1;
+ }
+ }
+ else if ((nX > 0) || (nY > 0))
+ {
+ assert(!((nX > 0) && (nY > 0)) && "should not be possible to add row and column at the same time");
+
+ // Range in the update hint is from first inserted row/column to last one in spreadsheet;
+ // calculate indices for the inserted ones from that
+ if (nX > 0)
+ {
+ nId = AccessibleTableModelChangeType::COLUMNS_INSERTED;
+ nFirstCol = aRange.aStart.Col();
+ nLastCol = aRange.aStart.Col() + nX - 1;
+ }
+ else
+ {
+ nId = AccessibleTableModelChangeType::ROWS_INSERTED;
+ nFirstRow = aRange.aStart.Row();
+ nLastRow = aRange.aStart.Row() + nY -1;
+ }
+ }
+ else
+ {
+ assert(false && "is it a deletion or an insertion?");
+ }
+
+ CommitTableModelChange(nFirstRow, nFirstCol, nLastRow, nLastCol, nId);
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
+
+ CommitChange(aEvent);
+ }
+ }
+ }
+ else
+ {
+ if (rHint.GetId() == SfxHintId::ScAccCursorChanged)
+ {
+ if (mpViewShell)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+
+ m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode();
+ if ( m_bFormulaMode )
+ {
+ NotifyRefMode();
+ m_bFormulaLastMode = true;
+ return;
+ }
+ if (m_bFormulaLastMode)
+ {//Last Notify Mode Is Formula Mode.
+ m_vecFormulaLastMyAddr.clear();
+ RemoveFormulaSelection(true);
+ m_pAccFormulaCell.clear();
+ //Remove All Selection
+ }
+ m_bFormulaLastMode = m_bFormulaMode;
+
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ ScAddress aNewCell = rViewData.GetCurPos();
+ if(aNewCell.Tab() != maActiveCell.Tab())
+ {
+ aEvent.EventId = AccessibleEventId::PAGE_CHANGED;
+ auto pAccParent = getAccessibleParent();
+ ScAccessibleDocument *pAccDoc =
+ static_cast<ScAccessibleDocument*>(pAccParent.get());
+ if(pAccDoc)
+ {
+ pAccDoc->CommitChange(aEvent);
+ }
+ }
+ bool bNewPosCell = (aNewCell != maActiveCell) || mpViewShell->GetForceFocusOnCurCell(); // #i123629#
+ bool bNewPosCellFocus=false;
+ if ( bNewPosCell && IsFocused() && aNewCell.Tab() == maActiveCell.Tab() )
+ {//single Focus
+ bNewPosCellFocus=true;
+ }
+ ScMarkData &refScMarkData = rViewData.GetMarkData();
+ // MT IA2: Not used
+ // int nSelCount = refScMarkData.GetSelectCount();
+ bool bIsMark =refScMarkData.IsMarked();
+ bool bIsMultMark = refScMarkData.IsMultiMarked();
+ bool bNewMarked = refScMarkData.GetTableSelect(aNewCell.Tab()) && ( bIsMark || bIsMultMark );
+// sal_Bool bNewCellSelected = isAccessibleSelected(aNewCell.Row(), aNewCell.Col());
+ sal_uInt16 nTab = rViewData.GetTabNo();
+ const ScRange& aMarkRange = refScMarkData.GetMarkArea();
+ aEvent.OldValue.clear();
+ ScDocument* pDoc= GetDocument(mpViewShell);
+ //Mark All
+ if ( !bNewPosCellFocus &&
+ (bNewMarked || bIsMark || bIsMultMark ) &&
+ aMarkRange == ScRange( 0,0,nTab, pDoc->MaxCol(),pDoc->MaxRow(),nTab ) )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ return ;
+ }
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ }
+ refScMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), true);
+
+ //For Whole Col Row
+ bool bWholeRow = std::abs(aMarkRange.aStart.Row() - aMarkRange.aEnd.Row()) == pDoc->MaxRow() ;
+ bool bWholeCol = ::abs(aMarkRange.aStart.Col() - aMarkRange.aEnd.Col()) == pDoc->MaxCol() ;
+ if ((bNewMarked || bIsMark || bIsMultMark ) && (bWholeCol || bWholeRow))
+ {
+ if ( aMarkRange != m_aLastWithInMarkRange )
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCell)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ bool bLastIsWholeColRow =
+ (std::abs(m_aLastWithInMarkRange.aStart.Row() - m_aLastWithInMarkRange.aEnd.Row()) == pDoc->MaxRow() && bWholeRow) ||
+ (::abs(m_aLastWithInMarkRange.aStart.Col() - m_aLastWithInMarkRange.aEnd.Col()) == pDoc->MaxCol() && bWholeCol);
+ bool bSelSmaller=
+ bLastIsWholeColRow &&
+ !aMarkRange.Contains(m_aLastWithInMarkRange) &&
+ aMarkRange.Intersects(m_aLastWithInMarkRange);
+ if( !bSelSmaller )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ m_aLastWithInMarkRange = aMarkRange;
+ }
+ return ;
+ }
+ m_aLastWithInMarkRange = aMarkRange;
+ int nNewMarkCount = mpMarkedRanges->GetCellCount();
+ bool bSendSingle= (0 == nNewMarkCount) && bNewPosCell;
+ if (bSendSingle)
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCellFocus)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ uno::Reference< XAccessible > xChild ;
+ if (bNewPosCellFocus)
+ {
+ xChild = mpAccCell.get();
+ }
+ else
+ {
+ mpAccCell = GetAccessibleCellAt(aNewCell.Row(),aNewCell.Col());
+ xChild = mpAccCell.get();
+
+ maActiveCell = aNewCell;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ aEvent.OldValue <<= uno::Reference< XAccessible >();
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ OSL_ASSERT(m_mapSelectionSend.count(aNewCell) == 0 );
+ m_mapSelectionSend.emplace(aNewCell,xChild);
+
+ }
+ else
+ {
+ ScRange aDelRange;
+ bool bIsDel = rViewData.GetDelMark( aDelRange );
+ if ( (!bIsDel || aMarkRange != aDelRange) &&
+ bNewMarked &&
+ nNewMarkCount > 0 &&
+ m_LastMarkedRanges != *mpMarkedRanges )
+ {
+ RemoveSelection(refScMarkData);
+ if(bNewPosCellFocus)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ std::vector<ScMyAddress> vecNew;
+ if(CalcScRangeListDifferenceMax(mpMarkedRanges.get(), &m_LastMarkedRanges,10,vecNew))
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ else
+ {
+ for(const auto& rAddr : vecNew)
+ {
+ uno::Reference< XAccessible > xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
+ if (!(bNewPosCellFocus && rAddr == aNewCell) )
+ {
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ m_mapSelectionSend.emplace(rAddr,xChild);
+ }
+ }
+ }
+ }
+ if (bNewPosCellFocus && maActiveCell != aNewCell)
+ {
+ CommitFocusCell(aNewCell);
+ }
+ m_LastMarkedRanges = *mpMarkedRanges;
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ScDataChanged)
+ {
+ if (!mbDelIns)
+ CommitTableModelChange(maRange.aStart.Row(), maRange.aStart.Col(), maRange.aEnd.Row(), maRange.aEnd.Col(), AccessibleTableModelChangeType::UPDATE);
+ else
+ mbDelIns = false;
+ if (mpViewShell)
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ ScAddress aNewCell = rViewData.GetCurPos();
+ if( maActiveCell == aNewCell)
+ {
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ OUString valStr(pScDoc->GetString(aNewCell.Col(),aNewCell.Row(),aNewCell.Tab()));
+ if(m_strCurCellValue != valStr)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VALUE_CHANGED;
+ mpAccCell->CommitChange(aEvent);
+ m_strCurCellValue=valStr;
+ }
+ OUString tabName;
+ pScDoc->GetName( maActiveCell.Tab(), tabName );
+ if( m_strOldTabName != tabName )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ OUString sOldName(ScResId(STR_ACC_TABLE_NAME));
+ sOldName = sOldName.replaceFirst("%1", m_strOldTabName);
+ aEvent.OldValue <<= sOldName;
+ OUString sNewName(ScResId(STR_ACC_TABLE_NAME));
+ sNewName = sNewName.replaceFirst("%1", tabName);
+ aEvent.NewValue <<= sNewName;
+ CommitChange( aEvent );
+ m_strOldTabName = tabName;
+ }
+ }
+ }
+ }
+ }
+ // commented out, because to use a ModelChangeEvent is not the right way
+ // at the moment there is no way, but the Java/Gnome Api should be extended sometime
+/* if (mpViewShell)
+ {
+ Rectangle aNewVisCells(GetVisCells(GetVisArea(mpViewShell, meSplitPos)));
+
+ Rectangle aNewPos(aNewVisCells);
+
+ if (aNewVisCells.Overlaps(maVisCells))
+ aNewPos.Union(maVisCells);
+ else
+ CommitTableModelChange(maVisCells.Top(), maVisCells.Left(), maVisCells.Bottom(), maVisCells.Right(), AccessibleTableModelChangeType::UPDATE);
+
+ maVisCells = aNewVisCells;
+
+ CommitTableModelChange(aNewPos.Top(), aNewPos.Left(), aNewPos.Bottom(), aNewPos.Right(), AccessibleTableModelChangeType::UPDATE);
+ }
+ }*/
+ }
+
+ ScAccessibleTableBase::Notify(rBC, rHint);
+}
+
+void ScAccessibleSpreadsheet::RemoveSelection(const ScMarkData &refScMarkData)
+{
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ MAP_ADDR_XACC::iterator miRemove = m_mapSelectionSend.begin();
+ while (miRemove != m_mapSelectionSend.end())
+ {
+ if (refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row(),true) ||
+ refScMarkData.IsCellMarked(miRemove->first.Col(),miRemove->first.Row()) )
+ {
+ ++miRemove;
+ continue;
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.NewValue <<= miRemove->second;
+ CommitChange(aEvent);
+ miRemove = m_mapSelectionSend.erase(miRemove);
+ }
+}
+void ScAccessibleSpreadsheet::CommitFocusCell(const ScAddress &aNewCell)
+{
+ OSL_ASSERT(!IsFormulaMode());
+ if(IsFormulaMode())
+ {
+ return ;
+ }
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.OldValue <<= uno::Reference<XAccessible>(mpAccCell);
+ mpAccCell.clear();
+ mpAccCell = GetAccessibleCellAt(aNewCell.Row(), aNewCell.Col());
+ aEvent.NewValue <<= uno::Reference<XAccessible>(mpAccCell);
+ maActiveCell = aNewCell;
+ ScDocument* pScDoc= GetDocument(mpViewShell);
+ if (pScDoc)
+ {
+ m_strCurCellValue = pScDoc->GetString(maActiveCell.Col(),maActiveCell.Row(),maActiveCell.Tab());
+ }
+ CommitChange(aEvent);
+}
+
+//===== XAccessibleTable ================================================
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleRowHeaders( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ if( mpDoc && mbIsSpreadsheet )
+ {
+ if( std::optional<ScRange> oRowRange = mpDoc->GetRepeatRowRange( mnTab ) )
+ {
+ SCROW nStart = oRowRange->aStart.Row();
+ SCROW nEnd = oRowRange->aEnd.Row();
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxRow()) )
+ xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( 0, nStart, mnTab, pDoc->MaxCol(), nEnd, mnTab ) ) );
+ }
+ }
+ return xAccessibleTable;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleSpreadsheet::getAccessibleColumnHeaders( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ if( mpDoc && mbIsSpreadsheet )
+ {
+ if( std::optional<ScRange> oColRange = mpDoc->GetRepeatColRange( mnTab ) )
+ {
+ SCCOL nStart = oColRange->aStart.Col();
+ SCCOL nEnd = oColRange->aEnd.Col();
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( (0 <= nStart) && (nStart <= nEnd) && (nEnd <= pDoc->MaxCol()) )
+ xAccessibleTable.set( new ScAccessibleSpreadsheet( *this, ScRange( nStart, 0, mnTab, nEnd, pDoc->MaxRow(), mnTab ) ) );
+ }
+ }
+ return xAccessibleTable;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleRows( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Sequence<sal_Int32> aSequence;
+ if (IsFormulaMode())
+ {
+ return aSequence;
+ }
+ if (mpViewShell)
+ {
+ aSequence.realloc(maRange.aEnd.Row() - maRange.aStart.Row() + 1);
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ sal_Int32* pSequence = aSequence.getArray();
+ sal_Int32 nCount(0);
+ for (SCROW i = maRange.aStart.Row(); i <= maRange.aEnd.Row(); ++i)
+ {
+ if (rMarkdata.IsRowMarked(i))
+ {
+ pSequence[nCount] = i;
+ ++nCount;
+ }
+ }
+ aSequence.realloc(nCount);
+ }
+ else
+ aSequence.realloc(0);
+ return aSequence;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleSpreadsheet::getSelectedAccessibleColumns( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Sequence<sal_Int32> aSequence;
+ if (IsFormulaMode() || !mpViewShell)
+ return aSequence;
+
+ aSequence.realloc(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ sal_Int32* pSequence = aSequence.getArray();
+ sal_Int32 nCount(0);
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ for (SCCOL i = maRange.aStart.Col(); i <= maRange.aEnd.Col(); ++i)
+ {
+ if (rMarkdata.IsColumnMarked(i))
+ {
+ pSequence[nCount] = i;
+ ++nCount;
+ }
+ }
+ aSequence.realloc(nCount);
+ return aSequence;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsRowMarked(static_cast<SCROW>(nRow));
+ }
+ return bResult;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsColumnMarked(static_cast<SCCOL>(nColumn));
+ }
+ return bResult;
+}
+
+rtl::Reference<ScAccessibleCell> ScAccessibleSpreadsheet::GetAccessibleCellAt(sal_Int32 nRow, sal_Int32 nColumn)
+{
+ if (IsFormulaMode())
+ {
+ ScAddress aCellAddress(static_cast<SCCOL>(nColumn), nRow, mpViewShell->GetViewData().GetTabNo());
+ if ((aCellAddress == m_aFormulaActiveCell) && m_pAccFormulaCell.is())
+ {
+ return m_pAccFormulaCell;
+ }
+ else
+ {
+ return ScAccessibleCell::create(this, mpViewShell, aCellAddress, GetAccessibleIndexFormula(nRow, nColumn), meSplitPos, mpAccDoc);
+ }
+ }
+ else
+ {
+ ScAddress aCellAddress(static_cast<SCCOL>(maRange.aStart.Col() + nColumn),
+ static_cast<SCROW>(maRange.aStart.Row() + nRow), maRange.aStart.Tab());
+ if ((aCellAddress == maActiveCell) && mpAccCell.is())
+ {
+ return mpAccCell;
+ }
+ else
+ {
+ return ScAccessibleCell::create(this, mpViewShell, aCellAddress, getAccessibleIndex(nRow, nColumn), meSplitPos, mpAccDoc);
+ }
+ }
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!IsFormulaMode())
+ {
+ if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) ||
+ nRow < 0 ||
+ nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) ||
+ nColumn < 0)
+ throw lang::IndexOutOfBoundsException();
+ }
+ rtl::Reference<ScAccessibleCell> pAccessibleCell = GetAccessibleCellAt(nRow, nColumn);
+ return pAccessibleCell;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (IsFormulaMode())
+ {
+ ScAddress addr(static_cast<SCCOL>(nColumn), nRow, 0);
+ return IsScAddrFormulaSel(addr);
+ }
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bResult(false);
+ if (mpViewShell)
+ {
+ const ScMarkData& rMarkdata = mpViewShell->GetViewData().GetMarkData();
+ bResult = rMarkdata.IsCellMarked(static_cast<SCCOL>(nColumn), static_cast<SCROW>(nRow));
+ }
+ return bResult;
+}
+
+ //===== XAccessibleComponent ============================================
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleSpreadsheet::getAccessibleAtPoint(const awt::Point& rPoint)
+{
+ uno::Reference< XAccessible > xAccessible;
+ if (containsPoint(rPoint))
+ {
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpViewShell)
+ {
+ SCCOL nX;
+ SCROW nY;
+ mpViewShell->GetViewData().GetPosFromPixel( rPoint.X, rPoint.Y, meSplitPos, nX, nY);
+ try {
+ xAccessible = getAccessibleCellAt(nY, nX);
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return nullptr;
+ }
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::grabFocus( )
+{
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleComponent> xAccessibleComponent(getAccessibleParent()->getAccessibleContext(), uno::UNO_QUERY);
+ if (xAccessibleComponent.is())
+ xAccessibleComponent->grabFocus();
+ }
+}
+
+sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getForeground( )
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL ScAccessibleSpreadsheet::getBackground( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return sal_Int32(SC_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+ //===== XAccessibleContext ==============================================
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL ScAccessibleSpreadsheet::getAccessibleRelationSet()
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSet;
+ if(mpAccDoc)
+ pRelationSet = mpAccDoc->GetRelationSet(nullptr);
+ if (pRelationSet)
+ return pRelationSet;
+ return new utl::AccessibleRelationSetHelper();
+}
+
+sal_Int64 SAL_CALL ScAccessibleSpreadsheet::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ sal_Int64 nParentStates = 0;
+ if (getAccessibleParent().is())
+ {
+ uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext();
+ nParentStates = xParentContext->getAccessibleStateSet();
+ }
+ sal_Int64 nStateSet = 0;
+ if (IsDefunc(nParentStates))
+ nStateSet |= AccessibleStateType::DEFUNC;
+ else
+ {
+ nStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
+ if (IsEditable())
+ nStateSet |= AccessibleStateType::EDITABLE;
+ nStateSet |= AccessibleStateType::ENABLED;
+ nStateSet |= AccessibleStateType::FOCUSABLE;
+ if (IsFocused())
+ nStateSet |= AccessibleStateType::FOCUSED;
+ nStateSet |= AccessibleStateType::MULTI_SELECTABLE;
+ nStateSet |= AccessibleStateType::OPAQUE;
+ nStateSet |= AccessibleStateType::SELECTABLE;
+ if (IsCompleteSheetSelected())
+ nStateSet |= AccessibleStateType::SELECTED;
+ if (isShowing())
+ nStateSet |= AccessibleStateType::SHOWING;
+ if (isVisible())
+ nStateSet |= AccessibleStateType::VISIBLE;
+ }
+ return nStateSet;
+}
+
+ ///===== XAccessibleSelection ===========================================
+
+void SAL_CALL ScAccessibleSpreadsheet::selectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (mpViewShell)
+ {
+ sal_Int32 nCol(getAccessibleColumn(nChildIndex));
+ sal_Int32 nRow(getAccessibleRow(nChildIndex));
+
+ SelectCell(nRow, nCol, false);
+ }
+}
+
+void SAL_CALL
+ ScAccessibleSpreadsheet::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (mpViewShell && !IsFormulaMode())
+ mpViewShell->Unmark();
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ if (!mpViewShell)
+ return;
+
+ if (IsFormulaMode())
+ {
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ mpViewShell->InitRefMode( 0, 0, rViewData.GetTabNo(), SC_REFTYPE_REF );
+ rViewData.SetRefStart(0, 0, rViewData.GetTabNo());
+ rViewData.SetRefEnd(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
+ mpViewShell->UpdateRef(pDoc->MaxCol(), pDoc->MaxRow(), rViewData.GetTabNo());
+ }
+ else
+ mpViewShell->SelectAll();
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleSpreadsheet::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ sal_Int64 nResult(0);
+ if (mpViewShell)
+ {
+ if (IsFormulaMode())
+ {
+ nResult = static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(GetColAll());
+ }
+ else
+ {
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ aMarkData.FillRangeListWithMarks(mpMarkedRanges.get(), false);
+ }
+ // is possible, because there shouldn't be overlapped ranges in it
+ if (mpMarkedRanges)
+ nResult = mpMarkedRanges->GetCellCount();
+ }
+ }
+ return nResult;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleSpreadsheet::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ uno::Reference < XAccessible > xAccessible;
+ if (IsFormulaMode())
+ {
+ if(CheckChildIndex(nSelectedChildIndex))
+ {
+ ScAddress addr = GetChildIndexAddress(nSelectedChildIndex);
+ xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
+ }
+ return xAccessible;
+ }
+ if (mpViewShell)
+ {
+ if (!mpMarkedRanges)
+ {
+ mpMarkedRanges.reset(new ScRangeList());
+ mpViewShell->GetViewData().GetMarkData().FillRangeListWithMarks(mpMarkedRanges.get(), false);
+ }
+ if (mpMarkedRanges)
+ {
+ if ((nSelectedChildIndex < 0) ||
+ (mpMarkedRanges->GetCellCount() <= o3tl::make_unsigned(nSelectedChildIndex)))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+ ScMyAddress addr = CalcScAddressFromRangeList(mpMarkedRanges.get(),nSelectedChildIndex);
+ auto it = m_mapSelectionSend.find(addr);
+ if( it != m_mapSelectionSend.end() )
+ xAccessible = it->second;
+ else
+ xAccessible = getAccessibleCellAt(addr.Row(), addr.Col());
+ }
+ }
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleSpreadsheet::deselectAccessibleChild( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (!mpViewShell)
+ return;
+
+ sal_Int32 nCol(getAccessibleColumn(nChildIndex));
+ sal_Int32 nRow(getAccessibleRow(nChildIndex));
+
+ if (IsFormulaMode())
+ {
+ if(IsScAddrFormulaSel(
+ ScAddress(static_cast<SCCOL>(nCol), nRow,mpViewShell->GetViewData().GetTabNo()))
+ )
+ {
+ SelectCell(nRow, nCol, true);
+ }
+ return ;
+ }
+ if (mpViewShell->GetViewData().GetMarkData().IsCellMarked(static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow)))
+ SelectCell(nRow, nCol, true);
+}
+
+void ScAccessibleSpreadsheet::SelectCell(sal_Int32 nRow, sal_Int32 nCol, bool bDeselect)
+{
+ if (IsFormulaMode())
+ {
+ if (bDeselect)
+ {//??
+ return;
+ }
+ else
+ {
+ ScViewData& rViewData = mpViewShell->GetViewData();
+
+ mpViewShell->InitRefMode( static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo(), SC_REFTYPE_REF );
+ mpViewShell->UpdateRef(static_cast<SCCOL>(nCol), nRow, rViewData.GetTabNo());
+ }
+ return ;
+ }
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), maRange.aStart.Tab(), bDeselect );
+
+ mpViewShell->SelectionChanged();
+}
+
+/*
+void ScAccessibleSpreadsheet::CreateSortedMarkedCells()
+{
+ mpSortedMarkedCells = new std::vector<ScMyAddress>();
+ mpSortedMarkedCells->reserve(mpMarkedRanges->GetCellCount());
+ for ( size_t i = 0, ListSize = mpMarkedRanges->size(); i < ListSize; ++i )
+ {
+ ScRange* pRange = (*mpMarkedRanges)[i];
+ if (pRange->aStart.Tab() != pRange->aEnd.Tab())
+ {
+ if ((maActiveCell.Tab() >= pRange->aStart.Tab()) ||
+ maActiveCell.Tab() <= pRange->aEnd.Tab())
+ {
+ ScRange aRange(*pRange);
+ aRange.aStart.SetTab(maActiveCell.Tab());
+ aRange.aEnd.SetTab(maActiveCell.Tab());
+ AddMarkedRange(aRange);
+ }
+ else
+ {
+ OSL_FAIL("Range of wrong table");
+ }
+ }
+ else if(pRange->aStart.Tab() == maActiveCell.Tab())
+ AddMarkedRange(*pRange);
+ else
+ {
+ OSL_FAIL("Range of wrong table");
+ }
+ }
+ std::sort(mpSortedMarkedCells->begin(), mpSortedMarkedCells->end());
+}
+
+void ScAccessibleSpreadsheet::AddMarkedRange(const ScRange& rRange)
+{
+ for (SCROW nRow = rRange.aStart.Row(); nRow <= rRange.aEnd.Row(); ++nRow)
+ {
+ for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
+ {
+ ScMyAddress aCell(nCol, nRow, maActiveCell.Tab());
+ mpSortedMarkedCells->push_back(aCell);
+ }
+ }
+}*/
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleSpreadsheet::getImplementationName()
+{
+ return "ScAccessibleSpreadsheet";
+}
+
+uno::Sequence< OUString> SAL_CALL
+ ScAccessibleSpreadsheet::getSupportedServiceNames()
+{
+ const css::uno::Sequence<OUString> vals { "com.sun.star.AccessibleSpreadsheet" };
+ return comphelper::concatSequences(ScAccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleSpreadsheet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+///===== XAccessibleEventBroadcaster =====================================
+
+void SAL_CALL ScAccessibleSpreadsheet::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ ScAccessibleTableBase::addAccessibleEventListener(xListener);
+
+}
+
+//==== internal =========================================================
+
+AbsoluteScreenPixelRectangle ScAccessibleSpreadsheet::GetBoundingBoxOnScreen() const
+{
+ AbsoluteScreenPixelRectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ aRect = pWindow->GetWindowExtentsAbsolute();
+ }
+ return aRect;
+}
+
+tools::Rectangle ScAccessibleSpreadsheet::GetBoundingBox() const
+{
+ tools::Rectangle aRect;
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ //#101986#; extends to the same window, because the parent is the document and it has the same window
+ aRect = pWindow->GetWindowExtentsRelative(*pWindow);
+ }
+ return aRect;
+}
+
+bool ScAccessibleSpreadsheet::IsDefunc(sal_Int64 nParentStates)
+{
+ return ScAccessibleContextBase::IsDefunc() || (mpViewShell == nullptr) || !getAccessibleParent().is() ||
+ (nParentStates & AccessibleStateType::DEFUNC);
+}
+
+bool ScAccessibleSpreadsheet::IsEditable()
+{
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+ bool bProtected(false);
+ if (mpDoc && mpDoc->IsTabProtected(maRange.aStart.Tab()))
+ bProtected = true;
+ return !bProtected;
+}
+
+bool ScAccessibleSpreadsheet::IsFocused()
+{
+ bool bFocused(false);
+ if (mpViewShell)
+ {
+ if (mpViewShell->GetViewData().GetActivePart() == meSplitPos)
+ bFocused = mpViewShell->GetActiveWin()->HasFocus();
+ }
+ return bFocused;
+}
+
+bool ScAccessibleSpreadsheet::IsCompleteSheetSelected()
+{
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ bool bResult(false);
+ if(mpViewShell)
+ {
+ //#103800#; use a copy of MarkData
+ ScMarkData aMarkData(mpViewShell->GetViewData().GetMarkData());
+ if (aMarkData.IsAllMarked(maRange))
+ bResult = true;
+ }
+ return bResult;
+}
+
+ScDocument* ScAccessibleSpreadsheet::GetDocument(ScTabViewShell* pViewShell)
+{
+ ScDocument* pDoc = nullptr;
+ if (pViewShell)
+ pDoc = &pViewShell->GetViewData().GetDocument();
+ return pDoc;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true );
+ mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
+ mpViewShell->SelectionChanged();
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true );
+ mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
+ mpViewShell->SelectionChanged();
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( 0, row, maRange.aStart.Tab(), false, false, true, true );
+ mpViewShell->MarkCursor( pDoc->MaxCol(), row, maRange.aStart.Tab(), false, true );
+ mpViewShell->SelectionChanged();
+ mpViewShell->DoneBlockMode( true );
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleSpreadsheet::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if (IsFormulaMode())
+ {
+ return false;
+ }
+
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ mpViewShell->SetTabNo( maRange.aStart.Tab() );
+ mpViewShell->DoneBlockMode( true ); // continue selecting
+ mpViewShell->InitBlockMode( static_cast<SCCOL>(column), 0, maRange.aStart.Tab(), false, true, false, true );
+ mpViewShell->MarkCursor( static_cast<SCCOL>(column), pDoc->MaxRow(), maRange.aStart.Tab(), true );
+ mpViewShell->SelectionChanged();
+ mpViewShell->DoneBlockMode( true );
+ return true;
+}
+
+void ScAccessibleSpreadsheet::FireFirstCellFocus()
+{
+ if (IsFormulaMode())
+ {
+ return ;
+ }
+ if (mbIsFocusSend)
+ {
+ return ;
+ }
+ mbIsFocusSend = true;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.NewValue <<= getAccessibleCellAt(maActiveCell.Row(), maActiveCell.Col());
+ CommitChange(aEvent);
+}
+
+void ScAccessibleSpreadsheet::NotifyRefMode()
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ if (!rViewData.IsRefMode())
+ // Not in reference mode. Bail out.
+ return;
+
+ sal_uInt16 nRefStartX = rViewData.GetRefStartX();
+ sal_Int32 nRefStartY = rViewData.GetRefStartY();
+ sal_uInt16 nRefEndX = rViewData.GetRefEndX();
+ sal_Int32 nRefEndY = rViewData.GetRefEndY();
+ ScAddress aFormulaAddr;
+ if(!GetFormulaCurrentFocusCell(aFormulaAddr))
+ {
+ return ;
+ }
+ if (m_aFormulaActiveCell != aFormulaAddr)
+ {//New Focus
+ m_nMinX =std::min(nRefStartX,nRefEndX);
+ m_nMaxX =std::max(nRefStartX,nRefEndX);
+ m_nMinY = std::min(nRefStartY,nRefEndY);
+ m_nMaxY = std::max(nRefStartY,nRefEndY);
+ RemoveFormulaSelection();
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED;
+ aEvent.OldValue <<= uno::Reference<XAccessible>(m_pAccFormulaCell);
+ m_pAccFormulaCell = GetAccessibleCellAt(aFormulaAddr.Row(), aFormulaAddr.Col());
+ uno::Reference< XAccessible > xNew = m_pAccFormulaCell;
+ aEvent.NewValue <<= xNew;
+ CommitChange(aEvent);
+ if (nRefStartX == nRefEndX && nRefStartY == nRefEndY)
+ {//Selection Single
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ aEvent.NewValue <<= xNew;
+ CommitChange(aEvent);
+ m_mapFormulaSelectionSend.emplace(aFormulaAddr,xNew);
+ m_vecFormulaLastMyAddr.clear();
+ m_vecFormulaLastMyAddr.emplace_back(aFormulaAddr);
+ }
+ else
+ {
+ std::vector<ScMyAddress> vecCurSel;
+ int nCurSize = (m_nMaxX - m_nMinX +1)*(m_nMaxY - m_nMinY +1) ;
+ vecCurSel.reserve(nCurSize);
+ for (sal_uInt16 x = m_nMinX ; x <= m_nMaxX ; ++x)
+ {
+ for (sal_Int32 y = m_nMinY ; y <= m_nMaxY ; ++y)
+ {
+ ScMyAddress aAddr(x,y,0);
+ vecCurSel.push_back(aAddr);
+ }
+ }
+ std::sort(vecCurSel.begin(), vecCurSel.end());
+ std::vector<ScMyAddress> vecNew;
+ std::set_difference(vecCurSel.begin(),vecCurSel.end(),
+ m_vecFormulaLastMyAddr.begin(),m_vecFormulaLastMyAddr.end(),
+ std::back_insert_iterator(vecNew));
+ int nNewSize = vecNew.size();
+ if ( nNewSize > 10 )
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ aEvent.NewValue.clear();
+ CommitChange(aEvent);
+ }
+ else
+ {
+ for(const auto& rAddr : vecNew)
+ {
+ uno::Reference< XAccessible > xChild;
+ if (rAddr == aFormulaAddr)
+ {
+ xChild = m_pAccFormulaCell.get();
+ }
+ else
+ {
+ xChild = getAccessibleCellAt(rAddr.Row(),rAddr.Col());
+ aEvent.EventId = AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ aEvent.NewValue <<= xChild;
+ CommitChange(aEvent);
+ m_mapFormulaSelectionSend.emplace(rAddr,xChild);
+ }
+ }
+ m_vecFormulaLastMyAddr.swap(vecCurSel);
+ }
+ }
+ m_aFormulaActiveCell = aFormulaAddr;
+}
+
+void ScAccessibleSpreadsheet::RemoveFormulaSelection(bool bRemoveAll )
+{
+ AccessibleEventObject aEvent;
+ aEvent.Source = uno::Reference< XAccessible >(this);
+ MAP_ADDR_XACC::iterator miRemove = m_mapFormulaSelectionSend.begin();
+ while (miRemove != m_mapFormulaSelectionSend.end())
+ {
+ if( !bRemoveAll && IsScAddrFormulaSel(miRemove->first) )
+ {
+ ++miRemove;
+ continue;
+ }
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ aEvent.NewValue <<= miRemove->second;
+ CommitChange(aEvent);
+ miRemove = m_mapFormulaSelectionSend.erase(miRemove);
+ }
+}
+
+bool ScAccessibleSpreadsheet::IsScAddrFormulaSel(const ScAddress &addr) const
+{
+ return addr.Col() >= m_nMinX && addr.Col() <= m_nMaxX &&
+ addr.Row() >= m_nMinY && addr.Row() <= m_nMaxY &&
+ addr.Tab() == mpViewShell->GetViewData().GetTabNo();
+}
+
+bool ScAccessibleSpreadsheet::CheckChildIndex(sal_Int64 nIndex) const
+{
+ sal_Int64 nMaxIndex = static_cast<sal_Int64>(m_nMaxX - m_nMinX +1) * static_cast<sal_Int64>(m_nMaxY - m_nMinY +1) -1 ;
+ return nIndex <= nMaxIndex && nIndex >= 0 ;
+}
+
+ScAddress ScAccessibleSpreadsheet::GetChildIndexAddress(sal_Int64 nIndex) const
+{
+ sal_Int64 nRowAll = GetRowAll();
+ sal_Int64 nColAll = GetColAll();
+ if (nIndex < 0 || nIndex >= nRowAll * nColAll)
+ {
+ return ScAddress();
+ }
+ return ScAddress(
+ static_cast<SCCOL>((nIndex - nIndex % nRowAll) / nRowAll + + m_nMinX),
+ nIndex % nRowAll + m_nMinY,
+ mpViewShell->GetViewData().GetTabNo()
+ );
+}
+
+sal_Int64 ScAccessibleSpreadsheet::GetAccessibleIndexFormula( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_uInt16 nColRelative = sal_uInt16(nColumn) - GetColAll();
+ sal_Int32 nRowRelative = nRow - GetRowAll();
+ if (nRow < 0 || nColumn < 0 || nRowRelative >= GetRowAll() || nColRelative >= GetColAll() )
+ {
+ return -1;
+ }
+ return static_cast<sal_Int64>(GetRowAll()) * static_cast<sal_Int64>(nRowRelative) + nColRelative;
+}
+
+bool ScAccessibleSpreadsheet::IsFormulaMode()
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ m_bFormulaMode = rViewData.IsRefMode() || SC_MOD()->IsFormulaMode();
+ return m_bFormulaMode ;
+}
+
+bool ScAccessibleSpreadsheet::GetFormulaCurrentFocusCell(ScAddress &addr)
+{
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ sal_uInt16 nRefX=0;
+ sal_Int32 nRefY=0;
+ if(m_bFormulaLastMode)
+ {
+ nRefX=rViewData.GetRefEndX();
+ nRefY=rViewData.GetRefEndY();
+ }
+ else
+ {
+ nRefX=rViewData.GetRefStartX();
+ nRefY=rViewData.GetRefStartY();
+ }
+ ScDocument* pDoc = GetDocument(mpViewShell);
+ if( /* Always true: nRefX >= 0 && */ nRefX <= pDoc->MaxCol() && nRefY >= 0 && nRefY <= pDoc->MaxRow())
+ {
+ addr = ScAddress(nRefX,nRefY,rViewData.GetTabNo());
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleTableBase.cxx b/sc/source/ui/Accessibility/AccessibleTableBase.cxx
new file mode 100644
index 0000000000..5ce54fad56
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleTableBase.cxx
@@ -0,0 +1,466 @@
+/* -*- 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 <AccessibleTableBase.hxx>
+#include <document.hxx>
+#include <scresid.hxx>
+#include <strings.hrc>
+#include <strings.hxx>
+#include <table.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <comphelper/sequence.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+//===== internal ============================================================
+
+ScAccessibleTableBase::ScAccessibleTableBase(
+ const uno::Reference<XAccessible>& rxParent,
+ ScDocument* pDoc,
+ const ScRange& rRange)
+ :
+ ScAccessibleContextBase (rxParent, AccessibleRole::TABLE),
+ maRange(rRange),
+ mpDoc(pDoc)
+{
+}
+
+ScAccessibleTableBase::~ScAccessibleTableBase()
+{
+}
+
+void SAL_CALL ScAccessibleTableBase::disposing()
+{
+ SolarMutexGuard aGuard;
+ mpDoc = nullptr;
+
+ ScAccessibleContextBase::disposing();
+}
+
+ //===== XInterface =====================================================
+
+uno::Any SAL_CALL ScAccessibleTableBase::queryInterface( uno::Type const & rType )
+{
+ if ( rType == cppu::UnoType<XAccessibleTableSelection>::get())
+ {
+ return uno::Any(uno::Reference<XAccessibleTableSelection>(this));
+ }
+ else
+ {
+ uno::Any aAny (ScAccessibleTableBaseImpl::queryInterface(rType));
+ return aAny.hasValue() ? aAny : ScAccessibleContextBase::queryInterface(rType);
+ }
+}
+
+void SAL_CALL ScAccessibleTableBase::acquire()
+ noexcept
+{
+ ScAccessibleContextBase::acquire();
+}
+
+void SAL_CALL ScAccessibleTableBase::release()
+ noexcept
+{
+ ScAccessibleContextBase::release();
+}
+
+ //===== XAccessibleTable ================================================
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRowCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maRange.aEnd.Row() - maRange.aStart.Row() + 1;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+ return maRange.aEnd.Col() - maRange.aStart.Col() + 1;
+}
+
+OUString SAL_CALL ScAccessibleTableBase::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ OSL_FAIL("Here should be an implementation to fill the description");
+
+ if ((nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ //setAccessibleRowDescription(nRow, xAccessible); // to remember the created Description
+ return OUString();
+}
+
+OUString SAL_CALL ScAccessibleTableBase::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ OSL_FAIL("Here should be an implementation to fill the description");
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ //setAccessibleColumnDescription(nColumn, xAccessible); // to remember the created Description
+ return OUString();
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCount(1); // the same cell
+ nRow += maRange.aStart.Row();
+ nColumn += maRange.aStart.Col();
+
+ if (mpDoc)
+ {
+ ScTable* pTab = mpDoc->FetchTable(maRange.aStart.Tab());
+ if (pTab)
+ {
+ SCROW nStartRow = static_cast<SCROW>(nRow);
+ SCROW nEndRow = nStartRow;
+ SCCOL nStartCol = static_cast<SCCOL>(nColumn);
+ SCCOL nEndCol = nStartCol;
+ if (pTab->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, false))
+ {
+ if (nEndRow > nStartRow)
+ nCount = nEndRow - nStartRow + 1;
+ }
+ }
+ }
+
+ return nCount;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if ((nColumn > (maRange.aEnd.Col() - maRange.aStart.Col())) || (nColumn < 0) ||
+ (nRow > (maRange.aEnd.Row() - maRange.aStart.Row())) || (nRow < 0))
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nCount(1); // the same cell
+ nRow += maRange.aStart.Row();
+ nColumn += maRange.aStart.Col();
+
+ if (mpDoc)
+ {
+ ScTable* pTab = mpDoc->FetchTable(maRange.aStart.Tab());
+ if (pTab)
+ {
+ SCROW nStartRow = static_cast<SCROW>(nRow);
+ SCROW nEndRow = nStartRow;
+ SCCOL nStartCol = static_cast<SCCOL>(nColumn);
+ SCCOL nEndCol = nStartCol;
+ if (pTab->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, false))
+ {
+ if (nEndCol > nStartCol)
+ nCount = nEndCol - nStartCol + 1;
+ }
+ }
+ }
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleTableBase::getAccessibleRowHeaders( )
+{
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ OSL_FAIL("Here should be an implementation to fill the row headers");
+
+ //CommitChange
+ return xAccessibleTable;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL ScAccessibleTableBase::getAccessibleColumnHeaders( )
+{
+ uno::Reference< XAccessibleTable > xAccessibleTable;
+ OSL_FAIL("Here should be an implementation to fill the column headers");
+
+ //CommitChange
+ return xAccessibleTable;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleTableBase::getSelectedAccessibleRows( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Sequence< sal_Int32 > aSequence;
+ return aSequence;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL ScAccessibleTableBase::getSelectedAccessibleColumns( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Sequence< sal_Int32 > aSequence;
+ return aSequence;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleRowSelected( sal_Int32 /* nRow */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleColumnSelected( sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleCellAt( sal_Int32 /* nRow */, sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleCaption( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+uno::Reference< XAccessible > SAL_CALL ScAccessibleTableBase::getAccessibleSummary( )
+{
+ OSL_FAIL("not implemented yet");
+ uno::Reference< XAccessible > xAccessible;
+ return xAccessible;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::isAccessibleSelected( sal_Int32 /* nRow */, sal_Int32 /* nColumn */ )
+{
+ OSL_FAIL("not implemented yet");
+ return false;
+}
+
+// ===== XAccessibleExtendedTable ========================================
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nRow > (maRange.aEnd.Row() - maRange.aStart.Row()) ||
+ nRow < 0 ||
+ nColumn > (maRange.aEnd.Col() - maRange.aStart.Col()) ||
+ nColumn < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ nRow -= maRange.aStart.Row();
+ nColumn -= maRange.aStart.Col();
+ return (static_cast<sal_Int64>(nRow) * static_cast<sal_Int64>(maRange.aEnd.Col() + 1)) + nColumn;
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleRow( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex >= getAccessibleChildCount() || nChildIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return nChildIndex / (maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+}
+
+sal_Int32 SAL_CALL ScAccessibleTableBase::getAccessibleColumn( sal_Int64 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nChildIndex >= getAccessibleChildCount() || nChildIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ return nChildIndex % static_cast<sal_Int32>(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+}
+
+// ===== XAccessibleContext ==============================================
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ // FIXME: representing rows & columns this way is a plain and simple madness.
+ // this needs a radical re-think.
+ sal_Int64 nMax = static_cast<sal_Int64>(maRange.aEnd.Row() - maRange.aStart.Row() + 1) *
+ static_cast<sal_Int64>(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ if (nMax < 0)
+ return 0;
+ return nMax;
+}
+
+uno::Reference< XAccessible > SAL_CALL
+ ScAccessibleTableBase::getAccessibleChild(sal_Int64 nIndex)
+{
+ SolarMutexGuard aGuard;
+ IsObjectValid();
+
+ if (nIndex >= getAccessibleChildCount() || nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ // FIXME: representing rows & columns this way is a plain and simple madness.
+ // this needs a radical re-think.
+
+ sal_Int32 nRow(0);
+ sal_Int32 nColumn(0);
+ sal_Int32 nTemp(maRange.aEnd.Col() - maRange.aStart.Col() + 1);
+ nRow = nIndex / nTemp;
+ nColumn = nIndex % nTemp;
+ return getAccessibleCellAt(nRow, nColumn);
+}
+
+OUString
+ ScAccessibleTableBase::createAccessibleDescription()
+{
+ return STR_ACC_TABLE_DESCR;
+}
+
+OUString ScAccessibleTableBase::createAccessibleName()
+{
+ OUString sName(ScResId(STR_ACC_TABLE_NAME));
+ OUString sCoreName;
+ if (mpDoc && mpDoc->GetName( maRange.aStart.Tab(), sCoreName ))
+ sName = sName.replaceFirst("%1", sCoreName);
+ return sName;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ ScAccessibleTableBase::getAccessibleRelationSet()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return uno::Reference<XAccessibleRelationSet>();
+}
+
+sal_Int64 SAL_CALL ScAccessibleTableBase::getAccessibleStateSet()
+{
+ OSL_FAIL("should be implemented in the abrevated class");
+ return 0;
+}
+
+ ///===== XAccessibleSelection ===========================================
+
+void SAL_CALL ScAccessibleTableBase::selectAccessibleChild( sal_Int64 /* nChildIndex */ )
+{
+}
+
+sal_Bool SAL_CALL
+ ScAccessibleTableBase::isAccessibleChildSelected( sal_Int64 nChildIndex )
+{
+ // I don't need to guard, because the called functions have a guard
+ if (nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+ return isAccessibleSelected(getAccessibleRow(nChildIndex), getAccessibleColumn(nChildIndex));
+}
+
+void SAL_CALL
+ ScAccessibleTableBase::clearAccessibleSelection( )
+{
+}
+
+void SAL_CALL ScAccessibleTableBase::selectAllAccessibleChildren()
+{
+}
+
+sal_Int64 SAL_CALL
+ ScAccessibleTableBase::getSelectedAccessibleChildCount( )
+{
+ return 0;
+}
+
+uno::Reference<XAccessible > SAL_CALL
+ ScAccessibleTableBase::getSelectedAccessibleChild( sal_Int64 /* nSelectedChildIndex */ )
+{
+ uno::Reference < XAccessible > xAccessible;
+ return xAccessible;
+}
+
+void SAL_CALL ScAccessibleTableBase::deselectAccessibleChild( sal_Int64 /* nSelectedChildIndex */ )
+{
+}
+
+ //===== XServiceInfo ====================================================
+
+OUString SAL_CALL ScAccessibleTableBase::getImplementationName()
+{
+ return "ScAccessibleTableBase";
+}
+
+ //===== XTypeProvider ===================================================
+
+uno::Sequence< uno::Type > SAL_CALL ScAccessibleTableBase::getTypes()
+{
+ return comphelper::concatSequences(ScAccessibleTableBaseImpl::getTypes(), ScAccessibleContextBase::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+ ScAccessibleTableBase::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void ScAccessibleTableBase::CommitTableModelChange(sal_Int32 nStartRow, sal_Int32 nStartCol, sal_Int32 nEndRow, sal_Int32 nEndCol, sal_uInt16 nId)
+{
+ AccessibleTableModelChange aModelChange;
+ aModelChange.FirstRow = nStartRow;
+ aModelChange.FirstColumn = nStartCol;
+ aModelChange.LastRow = nEndRow;
+ aModelChange.LastColumn = nEndCol;
+ aModelChange.Type = nId;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_MODEL_CHANGED;
+ aEvent.Source = uno::Reference< XAccessibleContext >(this);
+ aEvent.NewValue <<= aModelChange;
+
+ CommitChange(aEvent);
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::selectRow( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::selectColumn( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::unselectRow( sal_Int32 )
+{
+ return true;
+}
+
+sal_Bool SAL_CALL ScAccessibleTableBase::unselectColumn( sal_Int32 )
+{
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/AccessibleText.cxx b/sc/source/ui/Accessibility/AccessibleText.cxx
new file mode 100644
index 0000000000..3f7ff57f8f
--- /dev/null
+++ b/sc/source/ui/Accessibility/AccessibleText.cxx
@@ -0,0 +1,1386 @@
+/* -*- 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 <scitems.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <memory>
+#include <AccessibleText.hxx>
+#include <AccessibleCell.hxx>
+#include <attrib.hxx>
+#include <tabvwsh.hxx>
+#include <editutil.hxx>
+#include <document.hxx>
+#include <scmod.hxx>
+#include <prevwsh.hxx>
+#include <docsh.hxx>
+#include <prevloc.hxx>
+#include <patattr.hxx>
+#include <inputwin.hxx>
+#include <editeng/unofored.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/algitem.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+class ScViewForwarder : public SvxViewForwarder
+{
+ ScTabViewShell* mpViewShell;
+ ScSplitPos meSplitPos;
+public:
+ ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos);
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+
+ void SetInvalid();
+};
+
+ScViewForwarder::ScViewForwarder(ScTabViewShell* pViewShell, ScSplitPos eSplitPos)
+ :
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos)
+{
+}
+
+bool ScViewForwarder::IsValid() const
+{
+ return mpViewShell != nullptr;
+}
+
+Point ScViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ return pWindow->LogicToPixel( rPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindowByPos(meSplitPos);
+ if (pWindow)
+ return pWindow->PixelToLogic( rPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScViewForwarder::SetInvalid()
+{
+ mpViewShell = nullptr;
+}
+
+class ScEditObjectViewForwarder : public SvxViewForwarder
+{
+ VclPtr<OutputDevice> mpWindow;
+ // #i49561# EditView needed for access to its visible area.
+ const EditView* mpEditView;
+public:
+ ScEditObjectViewForwarder( OutputDevice* pWindow,
+ const EditView* _pEditView);
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+
+ void SetInvalid();
+};
+
+ScEditObjectViewForwarder::ScEditObjectViewForwarder( OutputDevice* pWindow,
+ const EditView* _pEditView )
+ : mpWindow(pWindow)
+ , mpEditView( _pEditView )
+{
+}
+
+bool ScEditObjectViewForwarder::IsValid() const
+{
+ return (mpWindow != nullptr);
+}
+
+Point ScEditObjectViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ {
+ // #i49561# - consider offset of the visible area
+ // of the EditView before converting point to pixel.
+ Point aPoint( rPoint );
+ if ( mpEditView )
+ {
+ tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
+ aPoint += aEditViewVisArea.TopLeft();
+ }
+ return mpWindow->LogicToPixel( aPoint, rMapMode );
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScEditObjectViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ {
+ // #i49561# - consider offset of the visible area
+ // of the EditView after converting point to logic.
+ Point aPoint( mpWindow->PixelToLogic( rPoint, rMapMode ) );
+ if ( mpEditView )
+ {
+ tools::Rectangle aEditViewVisArea( mpEditView->GetVisArea() );
+ aPoint -= aEditViewVisArea.TopLeft();
+ }
+ return aPoint;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScEditObjectViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+}
+
+class ScPreviewViewForwarder : public SvxViewForwarder
+{
+protected:
+ ScPreviewShell* mpViewShell;
+public:
+ explicit ScPreviewViewForwarder(ScPreviewShell* pViewShell);
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+
+ void SetInvalid();
+};
+
+ScPreviewViewForwarder::ScPreviewViewForwarder(ScPreviewShell* pViewShell)
+ : mpViewShell(pViewShell)
+{
+}
+
+bool ScPreviewViewForwarder::IsValid() const
+{
+ return mpViewShell != nullptr;
+}
+
+Point ScPreviewViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ MapMode aMapMode(pWindow->GetMapMode().GetMapUnit());
+ Point aPoint2( OutputDevice::LogicToLogic( rPoint, rMapMode, aMapMode) );
+ return pWindow->LogicToPixel(aPoint2);
+ }
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScPreviewViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpViewShell)
+ {
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if (pWindow)
+ {
+ MapMode aMapMode(pWindow->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint1( pWindow->PixelToLogic( rPoint ) );
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
+ MapMode(aMapMode.GetMapUnit()),
+ rMapMode ) );
+ return aPoint2;
+ }
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+void ScPreviewViewForwarder::SetInvalid()
+{
+ mpViewShell = nullptr;
+}
+
+namespace {
+
+class ScPreviewHeaderFooterViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewHeaderFooterViewForwarder::ScPreviewHeaderFooterViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewCellViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewCellViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewCellViewForwarder::ScPreviewCellViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewHeaderCellViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewHeaderCellViewForwarder::ScPreviewHeaderCellViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+namespace {
+
+class ScPreviewNoteViewForwarder : public ScPreviewViewForwarder
+{
+public:
+ ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell);
+};
+
+}
+
+ScPreviewNoteViewForwarder::ScPreviewNoteViewForwarder(ScPreviewShell* pViewShell)
+ :
+ ScPreviewViewForwarder(pViewShell)
+{
+}
+
+class ScEditViewForwarder : public SvxEditViewForwarder
+{
+ EditView* mpEditView;
+ VclPtr<OutputDevice> mpWindow;
+public:
+ ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin);
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual bool GetSelection( ESelection& rSelection ) const override;
+ virtual bool SetSelection( const ESelection& rSelection ) override;
+ virtual bool Copy() override;
+ virtual bool Cut() override;
+ virtual bool Paste() override;
+
+ void SetInvalid();
+};
+
+ScEditViewForwarder::ScEditViewForwarder(EditView* pEditView, OutputDevice* pWin)
+ : mpEditView(pEditView)
+ , mpWindow(pWin)
+{
+}
+
+bool ScEditViewForwarder::IsValid() const
+{
+ return mpWindow && mpEditView;
+}
+
+Point ScEditViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ return mpWindow->LogicToPixel( rPoint, rMapMode );
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+Point ScEditViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if (mpWindow)
+ return mpWindow->PixelToLogic( rPoint, rMapMode );
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return Point();
+}
+
+bool ScEditViewForwarder::GetSelection( ESelection& rSelection ) const
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ rSelection = mpEditView->GetSelection();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::SetSelection( const ESelection& rSelection )
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->SetSelection(rSelection);
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Copy()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Copy();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Cut()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Cut();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+bool ScEditViewForwarder::Paste()
+{
+ bool bResult(false);
+ if (IsValid())
+ {
+ mpEditView->Paste();
+ bResult = true;
+ }
+ else
+ {
+ OSL_FAIL("this ViewForwarder is not valid");
+ }
+ return bResult;
+}
+
+void ScEditViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+ mpEditView = nullptr;
+}
+
+// ScAccessibleCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessibleCellTextData::ScAccessibleCellTextData(ScTabViewShell* pViewShell,
+ const ScAddress& rP, ScSplitPos eSplitPos, ScAccessibleCell* pAccCell)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell),
+ meSplitPos(eSplitPos),
+ mpAccessibleCell( pAccCell )
+{
+}
+
+ScAccessibleCellTextData::~ScAccessibleCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessibleCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessibleCellTextData::Clone() const
+{
+ return new ScAccessibleCellTextData( mpViewShell, aCellPos, meSplitPos, mpAccessibleCell );
+}
+
+SvxTextForwarder* ScAccessibleCellTextData::GetTextForwarder()
+{
+ ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine
+
+ if ( pDocShell && pEditEngine && mpViewShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ tools::Long nSizeX, nSizeY;
+ mpViewShell->GetViewData().GetMergeSizePixel(
+ aCellPos.Col(), aCellPos.Row(), nSizeX, nSizeY);
+
+ Size aSize(nSizeX, nSizeY);
+
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ tools::Long nIndent = 0;
+ const SvxHorJustifyItem* pHorJustifyItem = rDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
+ SvxCellHorJustify eHorJust = pHorJustifyItem ? pHorJustifyItem->GetValue() : SvxCellHorJustify::Standard;
+ if ( eHorJust == SvxCellHorJustify::Left )
+ {
+ const ScIndentItem* pIndentItem = rDoc.GetAttr( aCellPos, ATTR_INDENT );
+ if ( pIndentItem )
+ {
+ nIndent = static_cast< tools::Long >( pIndentItem->GetValue() );
+ }
+ }
+
+ const SvxMarginItem* pMarginItem = rDoc.GetAttr( aCellPos, ATTR_MARGIN );
+ ScViewData& rViewData = mpViewShell->GetViewData();
+ double nPPTX = rViewData.GetPPTX();
+ double nPPTY = rViewData.GetPPTY();
+ tools::Long nLeftM = ( pMarginItem ? static_cast< tools::Long >( ( pMarginItem->GetLeftMargin() + nIndent ) * nPPTX ) : 0 );
+ tools::Long nTopM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetTopMargin() * nPPTY ) : 0 );
+ tools::Long nRightM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetRightMargin() * nPPTX ) : 0 );
+ tools::Long nBottomM = ( pMarginItem ? static_cast< tools::Long >( pMarginItem->GetBottomMargin() * nPPTY ) : 0 );
+ tools::Long nWidth = aSize.getWidth() - nLeftM - nRightM;
+ aSize.setWidth( nWidth );
+ aSize.setHeight( aSize.getHeight() - nTopM - nBottomM );
+
+ vcl::Window* pWin = mpViewShell->GetWindowByPos( meSplitPos );
+ if ( pWin )
+ {
+ aSize = pWin->PixelToLogic( aSize, pEditEngine->GetRefMapMode() );
+ }
+
+ /* #i19430# Gnopernicus reads text partly if it sticks out of the cell
+ boundaries. This leads to wrong results in cases where the cell text
+ is rotated, because rotation is not taken into account when calcu-
+ lating the visible part of the text. In these cases we will expand
+ the cell size passed as paper size to the edit engine. The function
+ accessibility::AccessibleStaticTextBase::GetParagraphBoundingBox()
+ (see svx/source/accessibility/AccessibleStaticTextBase.cxx) will
+ return the size of the complete text then, which is used to expand
+ the cell bounding box in ScAccessibleCell::GetBoundingBox()
+ (see sc/source/ui/Accessibility/AccessibleCell.cxx). */
+ const ScRotateValueItem* pItem = rDoc.GetAttr( aCellPos, ATTR_ROTATE_VALUE );
+ if( pItem && (pItem->GetValue() != 0_deg100) )
+ {
+ pEditEngine->SetPaperSize( Size( LONG_MAX, aSize.getHeight() ) );
+ tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
+ aSize.setWidth( std::max( aSize.getWidth(), nTxtWidth + 2 ) );
+ }
+ else
+ {
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ const ScLineBreakCell* pLineBreakItem = rDoc.GetAttr( aCellPos, ATTR_LINEBREAK );
+ bool bLineBreak = ( pLineBreakItem && pLineBreakItem->GetValue() );
+ if ( !bLineBreak )
+ {
+ tools::Long nTxtWidth = static_cast< tools::Long >( pEditEngine->CalcTextWidth() );
+ aSize.setWidth( ::std::max( aSize.getWidth(), nTxtWidth ) );
+ }
+ }
+
+ pEditEngine->SetPaperSize( aSize );
+
+ // #i92143# text getRangeExtents reports incorrect 'x' values for spreadsheet cells
+ if ( eHorJust == SvxCellHorJustify::Standard && rDoc.HasValueData( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab() ) )
+ {
+ pEditEngine->SetDefaultItem( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) );
+ }
+
+ Size aTextSize;
+ if ( pWin )
+ {
+ aTextSize = pWin->LogicToPixel( Size( pEditEngine->CalcTextWidth(), pEditEngine->GetTextHeight() ), pEditEngine->GetRefMapMode() );
+ }
+ tools::Long nTextWidth = aTextSize.Width();
+ tools::Long nTextHeight = aTextSize.Height();
+
+ tools::Long nOffsetX = nLeftM;
+ tools::Long nDiffX = nTextWidth - nWidth;
+ if ( nDiffX > 0 )
+ {
+ switch ( eHorJust )
+ {
+ case SvxCellHorJustify::Right:
+ {
+ nOffsetX -= nDiffX;
+ }
+ break;
+ case SvxCellHorJustify::Center:
+ {
+ nOffsetX -= nDiffX / 2;
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+ }
+
+ tools::Long nOffsetY = 0;
+ const SvxVerJustifyItem* pVerJustifyItem = rDoc.GetAttr( aCellPos, ATTR_VER_JUSTIFY );
+ SvxCellVerJustify eVerJust = ( pVerJustifyItem ? pVerJustifyItem->GetValue() : SvxCellVerJustify::Standard );
+ switch ( eVerJust )
+ {
+ case SvxCellVerJustify::Standard:
+ case SvxCellVerJustify::Bottom:
+ {
+ nOffsetY = nSizeY - nBottomM - nTextHeight;
+ }
+ break;
+ case SvxCellVerJustify::Center:
+ {
+ nOffsetY = ( nSizeY - nTopM - nBottomM - nTextHeight ) / 2 + nTopM;
+ }
+ break;
+ default:
+ {
+ nOffsetY = nTopM;
+ }
+ break;
+ }
+
+ if ( mpAccessibleCell )
+ {
+ mpAccessibleCell->SetOffset( Point( nOffsetX, nOffsetY ) );
+ }
+
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessibleCellTextData, NotifyHdl) );
+ }
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScViewForwarder(mpViewShell, meSplitPos));
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleCellTextData::GetEditViewForwarder( bool /* bCreate */ )
+{
+ //#102219#; there should no EditViewForwarder be, because the cell is now readonly in this interface
+ return nullptr;
+}
+
+IMPL_LINK(ScAccessibleTextData, NotifyHdl, EENotify&, aNotify, void)
+{
+ ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &aNotify );
+
+ if (aHint)
+ GetBroadcaster().Broadcast(*aHint);
+}
+
+ScDocShell* ScAccessibleCellTextData::GetDocShell(ScTabViewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetViewData().GetDocShell();
+ return pDocSh;
+}
+
+ScAccessibleEditObjectTextData::ScAccessibleEditObjectTextData(EditView* pEditView, OutputDevice* pWin, bool isClone)
+ :
+ mpEditView(pEditView),
+ mpEditEngine(pEditView ? pEditView->GetEditEngine() : nullptr),
+ mpWindow(pWin)
+{
+ // If the object is cloned, do NOT add notify hdl.
+ mbIsCloned = isClone;
+ if (mpEditEngine && !mbIsCloned)
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+}
+
+ScAccessibleEditObjectTextData::~ScAccessibleEditObjectTextData()
+{
+ // If the object is cloned, do NOT set notify hdl.
+ if (mpEditEngine && !mbIsCloned)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+ mpEditViewForwarder.reset();
+ mpForwarder.reset();
+}
+
+void ScAccessibleEditObjectTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpWindow = nullptr;
+ mpEditView = nullptr;
+ mpEditEngine = nullptr;
+ mpForwarder.reset();
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ if (mpEditViewForwarder)
+ mpEditViewForwarder->SetInvalid();
+ }
+ ScAccessibleTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessibleEditObjectTextData::Clone() const
+{
+ // Add para to indicate the object is cloned
+ return new ScAccessibleEditObjectTextData(mpEditView, mpWindow, true);
+}
+
+SvxTextForwarder* ScAccessibleEditObjectTextData::GetTextForwarder()
+{
+ if ((!mpForwarder && mpEditView) || (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()))
+ {
+ if (!mpEditEngine)
+ mpEditEngine = mpEditView->GetEditEngine();
+ // If the object is cloned, do NOT add notify hdl.
+ if (mpEditEngine && !mpEditEngine->GetNotifyHdl().IsSet()&&!mbIsCloned)
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+ if(!mpForwarder)
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+ }
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleEditObjectTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ {
+ // i#49561 Get right-aligned cell content to be read by screenreader.
+ mpViewForwarder.reset(new ScEditObjectViewForwarder( mpWindow, mpEditView ));
+ }
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleEditObjectTextData::GetEditViewForwarder( bool bCreate )
+{
+ if (!mpEditViewForwarder && mpEditView)
+ mpEditViewForwarder.reset(new ScEditViewForwarder(mpEditView, mpWindow));
+ if (bCreate)
+ {
+ if (!mpEditView && mpEditViewForwarder)
+ {
+ mpEditViewForwarder.reset();
+ }
+ }
+ return mpEditViewForwarder.get();
+}
+
+IMPL_LINK(ScAccessibleEditObjectTextData, NotifyHdl, EENotify&, rNotify, void)
+{
+ ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &rNotify );
+
+ if (aHint)
+ GetBroadcaster().Broadcast(*aHint);
+}
+
+ScAccessibleEditLineTextData::ScAccessibleEditLineTextData(EditView* pEditView,
+ OutputDevice* pWin,
+ ScTextWnd* pTxtWnd)
+ : ScAccessibleEditObjectTextData(pEditView, pWin)
+ , mpTxtWnd(pTxtWnd)
+ , mbEditEngineCreated(false)
+{
+ if (mpTxtWnd)
+ mpTxtWnd->InsertAccessibleTextData( *this );
+}
+
+ScAccessibleEditLineTextData::~ScAccessibleEditLineTextData()
+{
+ if (mpTxtWnd)
+ mpTxtWnd->RemoveAccessibleTextData( *this );
+
+ if (mbEditEngineCreated && mpEditEngine)
+ {
+ delete mpEditEngine;
+ mpEditEngine = nullptr; // don't access in ScAccessibleEditObjectTextData dtor!
+ }
+ else if (mpTxtWnd && mpTxtWnd->HasEditView() && mpTxtWnd->GetEditView()->GetEditEngine())
+ {
+ // the NotifyHdl also has to be removed from the ScTextWnd's EditEngine
+ // (it's set in ScAccessibleEditLineTextData::GetTextForwarder, and mpEditEngine
+ // is reset there)
+ mpTxtWnd->GetEditView()->GetEditEngine()->SetNotifyHdl(Link<EENotify&,void>());
+ }
+}
+
+void ScAccessibleEditLineTextData::Dispose()
+{
+ if (mpTxtWnd)
+ mpTxtWnd->RemoveAccessibleTextData( *this );
+
+ ResetEditMode();
+ mpWindow = nullptr;
+ mpTxtWnd = nullptr;
+}
+
+ScAccessibleTextData* ScAccessibleEditLineTextData::Clone() const
+{
+ return new ScAccessibleEditLineTextData(mpEditView, mpWindow, mpTxtWnd);
+}
+
+SvxTextForwarder* ScAccessibleEditLineTextData::GetTextForwarder()
+{
+ if (mpTxtWnd)
+ {
+ if (mpTxtWnd->HasEditView())
+ {
+ mpEditView = mpTxtWnd->GetEditView();
+
+ if (mbEditEngineCreated && mpEditEngine)
+ ResetEditMode();
+ mbEditEngineCreated = false;
+
+ mpEditView = mpTxtWnd->GetEditView();
+ ScAccessibleEditObjectTextData::GetTextForwarder(); // fill the mpForwarder
+ mpEditEngine = nullptr;
+ }
+ else
+ {
+ mpEditView = nullptr;
+
+ if (mpEditEngine && !mbEditEngineCreated)
+ ResetEditMode();
+ if (!mpEditEngine)
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ mpEditEngine = new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true);
+ mbEditEngineCreated = true;
+ mpEditEngine->EnableUndo( false );
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+
+ mpEditEngine->SetText(mpTxtWnd->GetTextString());
+
+#if 0
+ Size aSize(pTxtWnd->GetSizePixel());
+ aSize = pTxtWnd->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+#else
+ OutputDevice& rDevice = mpTxtWnd->GetDrawingArea()->get_ref_device();
+ Size aSize(rDevice.GetOutputSizePixel());
+ aSize = rDevice.PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+#endif
+
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleEditObjectTextData, NotifyHdl) );
+ }
+ }
+ }
+ return mpForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleEditLineTextData::GetEditViewForwarder( bool bCreate )
+{
+ if (mpTxtWnd)
+ {
+ if (!mpTxtWnd->HasEditView() && bCreate)
+ {
+ if ( !mpTxtWnd->IsInputActive() )
+ {
+ mpTxtWnd->StartEditEngine();
+ mpTxtWnd->GrabFocus();
+
+ mpEditView = mpTxtWnd->GetEditView();
+ }
+ }
+ }
+
+ return ScAccessibleEditObjectTextData::GetEditViewForwarder(bCreate);
+}
+
+void ScAccessibleEditLineTextData::ResetEditMode()
+{
+ if (mbEditEngineCreated && mpEditEngine)
+ delete mpEditEngine;
+ else if (mpTxtWnd && mpTxtWnd->HasEditView() && mpTxtWnd->GetEditView()->GetEditEngine())
+ mpTxtWnd->GetEditView()->GetEditEngine()->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine = nullptr;
+
+ mpForwarder.reset();
+ mpEditViewForwarder.reset();
+ mpViewForwarder.reset();
+ mbEditEngineCreated = false;
+}
+
+void ScAccessibleEditLineTextData::TextChanged()
+{
+ if (mbEditEngineCreated && mpEditEngine)
+ {
+ if (mpTxtWnd)
+ mpEditEngine->SetText(mpTxtWnd->GetTextString());
+ }
+}
+
+void ScAccessibleEditLineTextData::StartEdit()
+{
+ ResetEditMode();
+ mpEditView = nullptr;
+
+ // send SdrHintKind::BeginEdit
+ SdrHint aHint(SdrHintKind::BeginEdit);
+ GetBroadcaster().Broadcast( aHint );
+}
+
+void ScAccessibleEditLineTextData::EndEdit()
+{
+ // send SdrHintKind::EndEdit
+ SdrHint aHint(SdrHintKind::EndEdit);
+ GetBroadcaster().Broadcast( aHint );
+
+ ResetEditMode();
+ mpEditView = nullptr;
+}
+
+// ScAccessiblePreviewCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessiblePreviewCellTextData::ScAccessiblePreviewCellTextData(ScPreviewShell* pViewShell,
+ const ScAddress& rP)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell)
+{
+}
+
+ScAccessiblePreviewCellTextData::~ScAccessiblePreviewCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessiblePreviewCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessiblePreviewCellTextData::Clone() const
+{
+ return new ScAccessiblePreviewCellTextData(mpViewShell, aCellPos);
+}
+
+SvxTextForwarder* ScAccessiblePreviewCellTextData::GetTextForwarder()
+{
+ bool bEditEngineBefore(pEditEngine != nullptr);
+
+ ScCellTextData::GetTextForwarder(); // creates Forwarder and EditEngine
+
+ if (!bEditEngineBefore && pEditEngine)
+ {
+ Size aSize(mpViewShell->GetLocationData().GetCellOutputRect(aCellPos).GetSize());
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
+ pEditEngine->SetPaperSize(aSize);
+ }
+
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewCellTextData, NotifyHdl) );
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessiblePreviewCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScPreviewCellViewForwarder(mpViewShell));
+ return mpViewForwarder.get();
+}
+
+ScDocShell* ScAccessiblePreviewCellTextData::GetDocShell(ScPreviewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetDocument().GetDocumentShell();
+ return pDocSh;
+}
+
+// ScAccessiblePreviewHeaderCellTextData: shared data between sub objects of an accessible cell text object
+
+ScAccessiblePreviewHeaderCellTextData::ScAccessiblePreviewHeaderCellTextData(ScPreviewShell* pViewShell,
+ OUString aText, const ScAddress& rP, bool bColHeader, bool bRowHeader)
+ : ScAccessibleCellBaseTextData(GetDocShell(pViewShell), rP),
+ mpViewShell(pViewShell),
+ maText(std::move(aText)),
+ mbColHeader(bColHeader),
+ mbRowHeader(bRowHeader)
+{
+}
+
+ScAccessiblePreviewHeaderCellTextData::~ScAccessiblePreviewHeaderCellTextData()
+{
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpViewForwarder.reset();
+}
+
+void ScAccessiblePreviewHeaderCellTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr; // invalid now
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleCellBaseTextData::Notify(rBC, rHint);
+}
+
+ScAccessibleTextData* ScAccessiblePreviewHeaderCellTextData::Clone() const
+{
+ return new ScAccessiblePreviewHeaderCellTextData(mpViewShell, maText, aCellPos, mbColHeader, mbRowHeader);
+}
+
+SvxTextForwarder* ScAccessiblePreviewHeaderCellTextData::GetTextForwarder()
+{
+ if (!pEditEngine)
+ {
+ if ( pDocShell )
+ {
+ ScDocument& rDoc = pDocShell->GetDocument();
+ pEditEngine = rDoc.CreateFieldEditEngine();
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ pEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) );
+ }
+ pEditEngine->EnableUndo( false );
+ if (pDocShell)
+ pEditEngine->SetRefDevice(pDocShell->GetRefDevice());
+ else
+ pEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ pForwarder.reset( new SvxEditEngineForwarder(*pEditEngine) );
+ }
+
+ if (bDataValid)
+ return pForwarder.get();
+
+ if (!maText.isEmpty())
+ {
+ if ( mpViewShell )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ Size aSize(mpViewShell->GetLocationData().GetHeaderCellOutputRect(aVisRect, aCellPos, mbColHeader).GetSize());
+ if (pWindow)
+ aSize = pWindow->PixelToLogic(aSize, pEditEngine->GetRefMapMode());
+ pEditEngine->SetPaperSize(aSize);
+ }
+ pEditEngine->SetTextCurrentDefaults( maText );
+ }
+
+ bDataValid = true;
+
+ pEditEngine->SetNotifyHdl( LINK(this, ScAccessiblePreviewHeaderCellTextData, NotifyHdl) );
+
+ return pForwarder.get();
+}
+
+SvxViewForwarder* ScAccessiblePreviewHeaderCellTextData::GetViewForwarder()
+{
+ if (!mpViewForwarder)
+ mpViewForwarder.reset(new ScPreviewHeaderCellViewForwarder(mpViewShell));
+ return mpViewForwarder.get();
+}
+
+ScDocShell* ScAccessiblePreviewHeaderCellTextData::GetDocShell(ScPreviewShell* pViewShell)
+{
+ ScDocShell* pDocSh = nullptr;
+ if (pViewShell)
+ pDocSh = pViewShell->GetDocument().GetDocumentShell();
+ return pDocSh;
+}
+
+ScAccessibleHeaderTextData::ScAccessibleHeaderTextData(ScPreviewShell* pViewShell,
+ const EditTextObject* pEditObj, SvxAdjust eAdjust)
+ :
+ mpViewShell(pViewShell),
+ mpDocSh(nullptr),
+ mpEditObj(pEditObj),
+ mbDataValid(false),
+ meAdjust(eAdjust)
+{
+ if (pViewShell)
+ mpDocSh = pViewShell->GetDocument().GetDocumentShell();
+ if (mpDocSh)
+ mpDocSh->GetDocument().AddUnoObject(*this);
+}
+
+ScAccessibleHeaderTextData::~ScAccessibleHeaderTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (mpDocSh)
+ mpDocSh->GetDocument().RemoveUnoObject(*this);
+ if (mpEditEngine)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine.reset();
+ mpForwarder.reset();
+}
+
+ScAccessibleTextData* ScAccessibleHeaderTextData::Clone() const
+{
+ return new ScAccessibleHeaderTextData(mpViewShell, mpEditObj, meAdjust);
+}
+
+void ScAccessibleHeaderTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr;// invalid now
+ mpDocSh = nullptr;
+ if (mxViewForwarder)
+ mxViewForwarder->SetInvalid();
+ }
+}
+
+SvxTextForwarder* ScAccessibleHeaderTextData::GetTextForwarder()
+{
+ if (!mpEditEngine)
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ std::unique_ptr<ScHeaderEditEngine> pHdrEngine(new ScHeaderEditEngine( pEnginePool.get() ));
+
+ pHdrEngine->EnableUndo( false );
+ pHdrEngine->SetRefMapMode(MapMode(MapUnit::MapTwip));
+
+ // default font must be set, independently of document
+ // -> use global pool from module
+
+ SfxItemSet aDefaults( pHdrEngine->GetEmptyItemSet() );
+ const ScPatternAttr& rPattern = SC_MOD()->GetPool().GetDefaultItem(ATTR_PATTERN);
+ rPattern.FillEditItemSet( &aDefaults );
+ // FillEditItemSet adjusts font height to 1/100th mm,
+ // but for header/footer twips is needed, as in the PatternAttr:
+ aDefaults.Put( rPattern.GetItem(ATTR_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT) );
+ aDefaults.Put( rPattern.GetItem(ATTR_CJK_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CJK) );
+ aDefaults.Put( rPattern.GetItem(ATTR_CTL_FONT_HEIGHT).CloneSetWhich(EE_CHAR_FONTHEIGHT_CTL) );
+ aDefaults.Put( SvxAdjustItem( meAdjust, EE_PARA_JUST ) );
+ pHdrEngine->SetDefaults( aDefaults );
+
+ ScHeaderFieldData aData;
+ if (mpViewShell)
+ mpViewShell->FillFieldData(aData);
+ else
+ ScHeaderFooterTextObj::FillDummyFieldData( aData );
+ pHdrEngine->SetData( aData );
+
+ mpEditEngine = std::move(pHdrEngine);
+ mpForwarder.reset(new SvxEditEngineForwarder(*mpEditEngine));
+ }
+
+ if (mbDataValid)
+ return mpForwarder.get();
+
+ if ( mpViewShell )
+ {
+ tools::Rectangle aVisRect;
+ mpViewShell->GetLocationData().GetHeaderPosition(aVisRect);
+ Size aSize(aVisRect.GetSize());
+ vcl::Window* pWin = mpViewShell->GetWindow();
+ if (pWin)
+ aSize = pWin->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+ }
+ if (mpEditObj)
+ mpEditEngine->SetTextCurrentDefaults(*mpEditObj);
+
+ mbDataValid = true;
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleHeaderTextData::GetViewForwarder()
+{
+ if (!mxViewForwarder)
+ mxViewForwarder = std::make_unique<ScPreviewHeaderFooterViewForwarder>(mpViewShell);
+ return mxViewForwarder.get();
+}
+
+ScAccessibleNoteTextData::ScAccessibleNoteTextData(ScPreviewShell* pViewShell,
+ OUString sText, const ScAddress& aCellPos, bool bMarkNote)
+ :
+ mpViewShell(pViewShell),
+ mpDocSh(nullptr),
+ msText(std::move(sText)),
+ maCellPos(aCellPos),
+ mbMarkNote(bMarkNote),
+ mbDataValid(false)
+{
+ if (pViewShell)
+ mpDocSh = pViewShell->GetDocument().GetDocumentShell();
+ if (mpDocSh)
+ mpDocSh->GetDocument().AddUnoObject(*this);
+}
+
+ScAccessibleNoteTextData::~ScAccessibleNoteTextData()
+{
+ SolarMutexGuard aGuard; // needed for EditEngine dtor
+
+ if (mpDocSh)
+ mpDocSh->GetDocument().RemoveUnoObject(*this);
+ if (mpEditEngine)
+ mpEditEngine->SetNotifyHdl(Link<EENotify&,void>());
+ mpEditEngine.reset();
+ mpForwarder.reset();
+}
+
+ScAccessibleTextData* ScAccessibleNoteTextData::Clone() const
+{
+ return new ScAccessibleNoteTextData(mpViewShell, msText, maCellPos, mbMarkNote);
+}
+
+void ScAccessibleNoteTextData::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpViewShell = nullptr;// invalid now
+ mpDocSh = nullptr;
+ if (mxViewForwarder)
+ mxViewForwarder->SetInvalid();
+ }
+}
+
+SvxTextForwarder* ScAccessibleNoteTextData::GetTextForwarder()
+{
+ if (!mpEditEngine)
+ {
+ if ( mpDocSh )
+ {
+ ScDocument& rDoc = mpDocSh->GetDocument();
+ mpEditEngine = rDoc.CreateFieldEditEngine();
+ }
+ else
+ {
+ rtl::Reference<SfxItemPool> pEnginePool = EditEngine::CreatePool();
+ pEnginePool->FreezeIdRanges();
+ mpEditEngine.reset( new ScFieldEditEngine(nullptr, pEnginePool.get(), nullptr, true) );
+ }
+ mpEditEngine->EnableUndo( false );
+ if (mpDocSh)
+ mpEditEngine->SetRefDevice(mpDocSh->GetRefDevice());
+ else
+ mpEditEngine->SetRefMapMode(MapMode(MapUnit::Map100thMM));
+ mpForwarder.reset( new SvxEditEngineForwarder(*mpEditEngine) );
+ }
+
+ if (mbDataValid)
+ return mpForwarder.get();
+
+ if (!msText.isEmpty())
+ {
+
+ if ( mpViewShell )
+ {
+ Size aOutputSize;
+ vcl::Window* pWindow = mpViewShell->GetWindow();
+ if ( pWindow )
+ aOutputSize = pWindow->GetOutputSizePixel();
+ tools::Rectangle aVisRect( Point(), aOutputSize );
+ Size aSize(mpViewShell->GetLocationData().GetNoteInRangeOutputRect(aVisRect, mbMarkNote, maCellPos).GetSize());
+ if (pWindow)
+ aSize = pWindow->PixelToLogic(aSize, mpEditEngine->GetRefMapMode());
+ mpEditEngine->SetPaperSize(aSize);
+ }
+ mpEditEngine->SetTextCurrentDefaults( msText );
+ }
+
+ mbDataValid = true;
+
+ mpEditEngine->SetNotifyHdl( LINK(this, ScAccessibleNoteTextData, NotifyHdl) );
+
+ return mpForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleNoteTextData::GetViewForwarder()
+{
+ if (!mxViewForwarder)
+ mxViewForwarder = std::make_unique<ScPreviewNoteViewForwarder>(mpViewShell);
+ return mxViewForwarder.get();
+}
+
+// CSV import =================================================================
+
+class ScCsvViewForwarder : public SvxViewForwarder
+{
+ VclPtr<OutputDevice> mpWindow;
+
+public:
+ explicit ScCsvViewForwarder( OutputDevice* pWindow );
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override;
+ virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override;
+
+ void SetInvalid();
+};
+
+ScCsvViewForwarder::ScCsvViewForwarder( OutputDevice* pWindow ) :
+ mpWindow( pWindow )
+{
+}
+
+bool ScCsvViewForwarder::IsValid() const
+{
+ return mpWindow != nullptr;
+}
+
+Point ScCsvViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if( !mpWindow ) return Point();
+ return mpWindow->LogicToPixel( rPoint, rMapMode );
+}
+
+Point ScCsvViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ if( !mpWindow ) return Point();
+ return mpWindow->PixelToLogic( rPoint, rMapMode );
+}
+
+void ScCsvViewForwarder::SetInvalid()
+{
+ mpWindow = nullptr;
+}
+
+ScAccessibleCsvTextData::ScAccessibleCsvTextData(
+ OutputDevice* pWindow, EditEngine* pEditEngine,
+ OUString aCellText, const Size& rCellSize ) :
+ mpWindow( pWindow ),
+ mpEditEngine( pEditEngine ),
+ maCellText(std::move( aCellText )),
+ maCellSize( rCellSize )
+{
+}
+
+ScAccessibleCsvTextData::~ScAccessibleCsvTextData()
+{
+}
+
+void ScAccessibleCsvTextData::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if ( rHint.GetId() == SfxHintId::Dying )
+ {
+ mpWindow = nullptr;
+ mpEditEngine = nullptr;
+ if (mpViewForwarder)
+ mpViewForwarder->SetInvalid();
+ }
+ ScAccessibleTextData::Notify( rBC, rHint );
+}
+
+ScAccessibleTextData* ScAccessibleCsvTextData::Clone() const
+{
+ return new ScAccessibleCsvTextData( mpWindow, mpEditEngine, maCellText, maCellSize );
+}
+
+SvxTextForwarder* ScAccessibleCsvTextData::GetTextForwarder()
+{
+ if( mpEditEngine )
+ {
+ mpEditEngine->SetPaperSize( maCellSize );
+ mpEditEngine->SetText( maCellText );
+ if( !mpTextForwarder )
+ mpTextForwarder.reset( new SvxEditEngineForwarder( *mpEditEngine ) );
+ }
+ else
+ mpTextForwarder.reset();
+ return mpTextForwarder.get();
+}
+
+SvxViewForwarder* ScAccessibleCsvTextData::GetViewForwarder()
+{
+ if( !mpViewForwarder )
+ mpViewForwarder.reset( new ScCsvViewForwarder( mpWindow ) );
+ return mpViewForwarder.get();
+}
+
+SvxEditViewForwarder* ScAccessibleCsvTextData::GetEditViewForwarder( bool /* bCreate */ )
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/Accessibility/DrawModelBroadcaster.cxx b/sc/source/ui/Accessibility/DrawModelBroadcaster.cxx
new file mode 100644
index 0000000000..c181656fe1
--- /dev/null
+++ b/sc/source/ui/Accessibility/DrawModelBroadcaster.cxx
@@ -0,0 +1,107 @@
+/* -*- 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 <DrawModelBroadcaster.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomod.hxx>
+#include <svx/svdobj.hxx>
+
+using namespace ::com::sun::star;
+
+ScDrawModelBroadcaster::ScDrawModelBroadcaster( SdrModel *pDrawModel ) :
+ mpDrawModel( pDrawModel )
+{
+ if (mpDrawModel)
+ StartListening( *mpDrawModel );
+}
+
+ScDrawModelBroadcaster::~ScDrawModelBroadcaster()
+{
+ if (mpDrawModel)
+ EndListening( *mpDrawModel );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::addEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.removeInterface( aGuard, xListener );
+}
+
+void SAL_CALL ScDrawModelBroadcaster::addShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ std::scoped_lock aGuard(maListenerMutex);
+ auto rv = maShapeListeners.emplace(xShape, xListener);
+ assert(rv.second && "duplicate listener?");
+ (void)rv;
+}
+
+void SAL_CALL ScDrawModelBroadcaster::removeShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ std::scoped_lock aGuard(maListenerMutex);
+ auto it = maShapeListeners.find(xShape);
+ if (it != maShapeListeners.end())
+ {
+ assert(it->second == xListener && "removing wrong listener?");
+ (void)xListener;
+ maShapeListeners.erase(it);
+ }
+}
+
+void ScDrawModelBroadcaster::Notify( SfxBroadcaster&,
+ const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+
+ document::EventObject aEvent;
+ if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
+ return;
+
+ std::unique_lock aGuard(maListenerMutex);
+ maEventListeners.forEach(aGuard,
+ [&aEvent](const css::uno::Reference<document::XEventListener>& xListener)
+ {
+ xListener->notifyEvent(aEvent);
+ }
+ );
+
+ // 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);
+ auto it = maShapeListeners.find(xShape);
+ if (it != maShapeListeners.end())
+ it->second->notifyShapeEvent(aEvent);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */