diff options
Diffstat (limited to 'sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx')
-rw-r--r-- | sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx | 1567 |
1 files changed, 1567 insertions, 0 deletions
diff --git a/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx new file mode 100644 index 000000000..9656b82af --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleDocumentPagePreview.cxx @@ -0,0 +1,1567 @@ +/* -*- 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 <unotools/accessiblestatesethelper.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; + + 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; + + 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; + mpAccDoc->CommitChange(aEvent); + ++aNewItr; + } + else + { + xAcc = GetAccShape(*aOldItr); + AccessibleEventObject aEvent; + aEvent.Source = uno::Reference<XAccessibleContext> (mpAccDoc); + aEvent.EventId = AccessibleEventId::CHILD; + aEvent.OldValue <<= xAcc; + 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; + 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; + 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; + const size_t nCount(pPage->GetObjCount()); + for (size_t i = 0; i < nCount; ++i) + { + SdrObject* pObj = pPage->GetObj(i); + if (pObj) + { + uno::Reference< drawing::XShape > xShape(pObj->getUnoShape(), uno::UNO_QUERY); + if (xShape.is()) + { + tools::Rectangle aRect(pWin->LogicToPixel(VCLPoint(xShape->getPosition()), aMapMode), pWin->LogicToPixel(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; + 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; + 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_Int32 SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + + tools::Long 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_Int32 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_Int32 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. +uno::Reference<XAccessibleStateSet> SAL_CALL ScAccessibleDocumentPagePreview::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference<XAccessibleStateSet> xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference<utl::AccessibleStateSetHelper> pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + // never editable + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (isShowing()) + pStateSet->AddState(AccessibleStateType::SHOWING); + if (isVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + + //===== 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; +} + +tools::Rectangle ScAccessibleDocumentPagePreview::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aRect; + if (mpViewShell) + { + vcl::Window* pWindow = mpViewShell->GetWindow(); + if (pWindow) + aRect = pWindow->GetWindowExtentsRelative(nullptr); + } + 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( + const uno::Reference<XAccessibleStateSet>& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(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(); + + SfxObjectShell* 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: */ |