2225 lines
74 KiB
C++
2225 lines
74 KiB
C++
/* -*- 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 <toolkit/helper/vclunohelper.hxx>
|
|
#include <unotools/accessiblerelationsethelper.hxx>
|
|
#include <utility>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/unohelp.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;
|
|
std::optional<ScAddress> xRelationCell; // if it is NULL this shape is anchored on the table
|
|
css::uno::Reference< css::drawing::XShape > xShape;
|
|
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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::ScChildrenShapes.");
|
|
|
|
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(vcl::unohelper::ConvertToVCLPoint(rPoint));
|
|
aPoint
|
|
-= vcl::unohelper::ConvertToVCLRect(pShape->pAccShape->getBounds()).TopLeft();
|
|
if (pShape->pAccShape->containsPoint(vcl::unohelper::ConvertToAWTPoint(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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::IsSelected.");
|
|
|
|
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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectionChanged.");
|
|
|
|
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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::Select.");
|
|
|
|
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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::DeselectAll.");
|
|
|
|
bool bSomethingSelected(true);
|
|
try
|
|
{
|
|
xSelectionSupplier->select(uno::Any()); //deselects all
|
|
}
|
|
catch (lang::IllegalArgumentException&)
|
|
{
|
|
OSL_FAIL("nothing selected before");
|
|
bSomethingSelected = false;
|
|
}
|
|
|
|
if (bSomethingSelected)
|
|
for (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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::SelectAll.");
|
|
|
|
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 (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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::GetSelectedCount.");
|
|
|
|
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 )
|
|
{
|
|
pMarkList = &(pScDrawView->GetMarkedObjectList());
|
|
if( pMarkList->GetMarkCount() == 1 )
|
|
{
|
|
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 = std::move(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(u"LayerID"_ustr);
|
|
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("Could not get selected shapes. Null reference to xSelectionSupplier in ScChildrenShapes::AddShape.");
|
|
|
|
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 (rHint.GetId() == SfxHintId::ScAccGridWinFocusLost )
|
|
{
|
|
auto pFocusLostHint = static_cast<const ScAccGridWinFocusLostHint*>(&rHint);
|
|
if (pFocusLostHint->GetOldGridWin() == meSplitPos)
|
|
{
|
|
if (mxTempAcc.is() && mpTempAccEdit)
|
|
mpTempAccEdit->LostFocus();
|
|
else if (mpAccessibleSpreadsheet.is())
|
|
mpAccessibleSpreadsheet->LostFocus();
|
|
else
|
|
CommitFocusLost();
|
|
}
|
|
}
|
|
else if (rHint.GetId() == SfxHintId::ScAccGridWinFocusGot)
|
|
{
|
|
auto pFocusGotHint = static_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 if (rHint.GetId() == SfxHintId::ScAccTableChanged)
|
|
{
|
|
// only notify if child exist, otherwise it is not necessary
|
|
if (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->GotFocus();
|
|
}
|
|
}
|
|
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();
|
|
EditEngine const& rEditEng = rViewData.GetEditView(meSplitPos)->getEditEngine();
|
|
if (rEditEng.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(vcl::unohelper::ConvertToVCLRect(xComp->getBounds()));
|
|
if (aBound.Contains(vcl::unohelper::ConvertToVCLPoint(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 u"ScAccessibleDocument"_ustr;
|
|
}
|
|
|
|
uno::Sequence< OUString> SAL_CALL
|
|
ScAccessibleDocument::getSupportedServiceNames()
|
|
{
|
|
const css::uno::Sequence<OUString> vals { u"com.sun.star.AccessibleSpreadsheetDocumentView"_ustr };
|
|
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;
|
|
}
|
|
|
|
const OUString & ScAccessibleDocument::GetCurrentCellDescription()
|
|
{
|
|
return EMPTY_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(ScModule::get()->GetColorConfig().GetColorValue(::svtools::DOCCOLOR).nColor);
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|