summaryrefslogtreecommitdiffstats
path: root/sw/source/core/access/accmap.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /sw/source/core/access/accmap.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/access/accmap.cxx')
-rw-r--r--sw/source/core/access/accmap.cxx3527
1 files changed, 3527 insertions, 0 deletions
diff --git a/sw/source/core/access/accmap.cxx b/sw/source/core/access/accmap.cxx
new file mode 100644
index 000000000..dd63845d4
--- /dev/null
+++ b/sw/source/core/access/accmap.cxx
@@ -0,0 +1,3527 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <rtl/ref.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <vcl/window.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomod.hxx>
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+#include <list>
+#include <vector>
+#include <accmap.hxx>
+#include "acccontext.hxx"
+#include "accdoc.hxx"
+#include <strings.hrc>
+#include "accpreview.hxx"
+#include "accpage.hxx"
+#include "accpara.hxx"
+#include "accheaderfooter.hxx"
+#include "accfootnote.hxx"
+#include "acctextframe.hxx"
+#include "accgraphic.hxx"
+#include "accembedded.hxx"
+#include "acccell.hxx"
+#include "acctable.hxx"
+#include <fesh.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <hffrm.hxx>
+#include <ftnfrm.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <ndtyp.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdpage.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <pagepreviewlayout.hxx>
+#include <dcontact.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdmark.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <dflyobj.hxx>
+#include <prevwpage.hxx>
+#include <calbck.hxx>
+#include <undobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::sw::access;
+
+namespace {
+
+struct SwFrameFunc
+{
+ bool operator()( const SwFrame * p1, const SwFrame * p2) const
+ {
+ return p1 < p2;
+ }
+};
+
+}
+
+class SwAccessibleContextMap_Impl
+{
+public:
+ typedef const SwFrame * key_type;
+ typedef uno::WeakReference < XAccessible > mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwFrameFunc key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map <key_type,mapped_type,key_compare> maMap;
+public:
+
+#if OSL_DEBUG_LEVEL > 0
+ bool mbLocked;
+#endif
+
+ SwAccessibleContextMap_Impl()
+#if OSL_DEBUG_LEVEL > 0
+ : mbLocked( false )
+#endif
+ {}
+
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ bool empty() const { return maMap.empty(); }
+ void clear() { maMap.clear(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+class SwDrawModellListener_Impl : public SfxListener,
+ public ::cppu::WeakImplHelper< document::XShapeEventBroadcaster >
+{
+ mutable ::osl::Mutex maListenerMutex;
+ ::comphelper::OInterfaceContainerHelper2 maEventListeners;
+ std::unordered_multimap<css::uno::Reference< css::drawing::XShape >, css::uno::Reference< css::document::XShapeEventListener >> maShapeListeners;
+ SdrModel *mpDrawModel;
+protected:
+ virtual ~SwDrawModellListener_Impl() override;
+
+public:
+ explicit SwDrawModellListener_Impl( SdrModel *pDrawModel );
+
+ // css::document::XEventBroadcaster
+ virtual void SAL_CALL addEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ // css::document::XShapeEventBroadcaster
+ virtual void SAL_CALL addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+ virtual void SAL_CALL removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ void Dispose();
+};
+
+}
+
+SwDrawModellListener_Impl::SwDrawModellListener_Impl( SdrModel *pDrawModel ) :
+ maEventListeners( maListenerMutex ),
+ mpDrawModel( pDrawModel )
+{
+ StartListening( *mpDrawModel );
+}
+
+SwDrawModellListener_Impl::~SwDrawModellListener_Impl()
+{
+ Dispose();
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ maEventListeners.addInterface( xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ maEventListeners.removeInterface( xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ osl::MutexGuard aGuard(maListenerMutex);
+ maShapeListeners.emplace(xShape, xListener);
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ osl::MutexGuard aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ if (it->second == xListener)
+ {
+ maShapeListeners.erase(it);
+ return;
+ }
+}
+
+void SwDrawModellListener_Impl::Notify( SfxBroadcaster& /*rBC*/,
+ const SfxHint& rHint )
+{
+ // do not broadcast notifications for writer fly frames, because there
+ // are no shapes that need to know about them.
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint *pSdrHint = static_cast<const SdrHint*>( &rHint );
+ if (pSdrHint->GetObject() &&
+ ( dynamic_cast< const SwFlyDrawObj* >(pSdrHint->GetObject()) != nullptr ||
+ dynamic_cast< const SwVirtFlyDrawObj* >(pSdrHint->GetObject()) != nullptr ||
+ typeid(SdrObject) == typeid(pSdrHint->GetObject()) ) )
+ {
+ return;
+ }
+
+ OSL_ENSURE( mpDrawModel, "draw model listener is disposed" );
+ if( !mpDrawModel )
+ return;
+
+ document::EventObject aEvent;
+ if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
+ return;
+
+ ::comphelper::OInterfaceIteratorHelper2 aIter( maEventListeners );
+ while( aIter.hasMoreElements() )
+ {
+ uno::Reference < document::XEventListener > xListener( aIter.next(),
+ uno::UNO_QUERY );
+ try
+ {
+ xListener->notifyEvent( aEvent );
+ }
+ catch( uno::RuntimeException const & )
+ {
+ TOOLS_WARN_EXCEPTION("sw.a11y", "Runtime exception caught while notifying shape");
+ }
+ }
+
+ // right now, we're only handling the specific event necessary to fix this performance problem
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
+ {
+ auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
+ uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
+ osl::MutexGuard aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ it->second->notifyShapeEvent(aEvent);
+ }
+}
+
+void SwDrawModellListener_Impl::Dispose()
+{
+ if (mpDrawModel != nullptr) {
+ EndListening( *mpDrawModel );
+ }
+ mpDrawModel = nullptr;
+}
+
+namespace {
+
+struct SwShapeFunc
+{
+ bool operator()( const SdrObject * p1, const SdrObject * p2) const
+ {
+ return p1 < p2;
+ }
+};
+
+}
+
+typedef std::pair < const SdrObject *, ::rtl::Reference < ::accessibility::AccessibleShape > > SwAccessibleObjShape_Impl;
+
+class SwAccessibleShapeMap_Impl
+{
+public:
+
+ typedef const SdrObject * key_type;
+ typedef uno::WeakReference<XAccessible> mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwShapeFunc key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+
+private:
+
+ ::accessibility::AccessibleShapeTreeInfo maInfo;
+ std::map<key_type,mapped_type,SwShapeFunc> maMap;
+
+public:
+
+ explicit SwAccessibleShapeMap_Impl( SwAccessibleMap const *pMap )
+ : maMap()
+ {
+ maInfo.SetSdrView( pMap->GetShell()->GetDrawView() );
+ maInfo.SetDevice( pMap->GetShell()->GetWin() );
+ maInfo.SetViewForwarder( pMap );
+ uno::Reference < document::XShapeEventBroadcaster > xModelBroadcaster =
+ new SwDrawModellListener_Impl(
+ pMap->GetShell()->getIDocumentDrawModelAccess().GetOrCreateDrawModel() );
+ maInfo.SetModelBroadcaster( xModelBroadcaster );
+ }
+
+ ~SwAccessibleShapeMap_Impl();
+
+ const ::accessibility::AccessibleShapeTreeInfo& GetInfo() const { return maInfo; }
+
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> Copy( size_t& rSize,
+ const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelShape ) const;
+
+ iterator end() { return maMap.end(); }
+ const_iterator cbegin() const { return maMap.cbegin(); }
+ const_iterator cend() const { return maMap.cend(); }
+ bool empty() const { return maMap.empty(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+SwAccessibleShapeMap_Impl::~SwAccessibleShapeMap_Impl()
+{
+ uno::Reference < document::XEventBroadcaster > xBrd( maInfo.GetModelBroadcaster() );
+ if( xBrd.is() )
+ static_cast < SwDrawModellListener_Impl * >( xBrd.get() )->Dispose();
+}
+
+std::unique_ptr<SwAccessibleObjShape_Impl[]>
+ SwAccessibleShapeMap_Impl::Copy(
+ size_t& rSize, const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelStart ) const
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+
+ size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+ rSize = maMap.size();
+
+ if( rSize > 0 )
+ {
+ pShapes.reset(new SwAccessibleObjShape_Impl[rSize]);
+
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ pSelShape = &(pShapes[rSize]);
+ for( const auto& rEntry : maMap )
+ {
+ const SdrObject *pObj = rEntry.first;
+ uno::Reference < XAccessible > xAcc( rEntry.second );
+ if( nSelShapes && pFESh && pFESh->IsObjSelected( *pObj ) )
+ {
+ // selected objects are inserted from the back
+ --pSelShape;
+ pSelShape->first = pObj;
+ pSelShape->second =
+ static_cast < ::accessibility::AccessibleShape* >(
+ xAcc.get() );
+ --nSelShapes;
+ }
+ else
+ {
+ pShape->first = pObj;
+ pShape->second =
+ static_cast < ::accessibility::AccessibleShape* >(
+ xAcc.get() );
+ ++pShape;
+ }
+ }
+ assert(pSelShape == pShape);
+ }
+
+ if( pSelStart )
+ *pSelStart = pSelShape;
+
+ return pShapes;
+}
+
+struct SwAccessibleEvent_Impl
+{
+public:
+ enum EventType { CARET_OR_STATES,
+ INVALID_CONTENT,
+ POS_CHANGED,
+ CHILD_POS_CHANGED,
+ SHAPE_SELECTION,
+ DISPOSE,
+ INVALID_ATTR };
+
+private:
+ SwRect maOldBox; // the old bounds for CHILD_POS_CHANGED
+ // and POS_CHANGED
+ uno::WeakReference < XAccessible > mxAcc; // The object that fires the event
+ SwAccessibleChild maFrameOrObj; // the child for CHILD_POS_CHANGED and
+ // the same as xAcc for any other
+ // event type
+ EventType meType; // The event type
+ AccessibleStates mnStates; // check states or update caret pos
+
+public:
+ const SwFrame* mpParentFrame; // The object that fires the event
+ bool IsNoXaccParentFrame() const
+ {
+ return CHILD_POS_CHANGED == meType && mpParentFrame != nullptr;
+ }
+
+public:
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj )
+ : mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {}
+
+ SwAccessibleEvent_Impl( EventType eT,
+ const SwAccessibleChild& rFrameOrObj )
+ : maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::DISPOSE == meType &&
+ "wrong event constructor, DISPOSE only");
+ }
+
+ explicit SwAccessibleEvent_Impl( EventType eT )
+ : meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::SHAPE_SELECTION == meType &&
+ "wrong event constructor, SHAPE_SELECTION only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj,
+ const SwRect& rR )
+ : maOldBox( rR ),
+ mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert((SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType ||
+ SwAccessibleEvent_Impl::POS_CHANGED == meType) &&
+ "wrong event constructor, (CHILD_)POS_CHANGED only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj,
+ const AccessibleStates _nStates )
+ : mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( _nStates ),
+ mpParentFrame( nullptr )
+ {
+ assert( SwAccessibleEvent_Impl::CARET_OR_STATES == meType &&
+ "wrong event constructor, CARET_OR_STATES only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT, const SwFrame *pParentFrame,
+ const SwAccessibleChild& rFrameOrObj, const SwRect& rR ) :
+ maOldBox( rR ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( pParentFrame )
+ {
+ assert( SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType &&
+ "wrong event constructor, CHILD_POS_CHANGED only" );
+ }
+
+ // <SetType(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetType( EventType eT )
+ {
+ meType = eT;
+ }
+ EventType GetType() const
+ {
+ return meType;
+ }
+
+ ::rtl::Reference < SwAccessibleContext > GetContext() const
+ {
+ uno::Reference < XAccessible > xTmp( mxAcc );
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xTmp.get() ) );
+
+ return xAccImpl;
+ }
+
+ const SwRect& GetOldBox() const
+ {
+ return maOldBox;
+ }
+ // <SetOldBox(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetOldBox( const SwRect& rOldBox )
+ {
+ maOldBox = rOldBox;
+ }
+
+ const SwAccessibleChild& GetFrameOrObj() const
+ {
+ return maFrameOrObj;
+ }
+
+ // <SetStates(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetStates( AccessibleStates _nStates )
+ {
+ mnStates |= _nStates;
+ }
+
+ bool IsUpdateCursorPos() const
+ {
+ return bool(mnStates & AccessibleStates::CARET);
+ }
+ bool IsInvalidateStates() const
+ {
+ return bool(mnStates & (AccessibleStates::EDITABLE | AccessibleStates::OPAQUE));
+ }
+ bool IsInvalidateRelation() const
+ {
+ return bool(mnStates & (AccessibleStates::RELATION_FROM | AccessibleStates::RELATION_TO));
+ }
+ bool IsInvalidateTextSelection() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_SELECTION_CHANGED );
+ }
+
+ bool IsInvalidateTextAttrs() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ }
+
+ AccessibleStates GetStates() const
+ {
+ return mnStates;
+ }
+
+ AccessibleStates GetAllStates() const
+ {
+ return mnStates;
+ }
+};
+
+class SwAccessibleEventList_Impl
+{
+ std::list<SwAccessibleEvent_Impl> maEvents;
+ bool mbFiring;
+
+public:
+ SwAccessibleEventList_Impl()
+ : mbFiring( false )
+ {}
+
+ void SetFiring()
+ {
+ mbFiring = true;
+ }
+ bool IsFiring() const
+ {
+ return mbFiring;
+ }
+
+ void MoveMissingXAccToEnd();
+
+ size_t size() const { return maEvents.size(); }
+ std::list<SwAccessibleEvent_Impl>::iterator begin() { return maEvents.begin(); }
+ std::list<SwAccessibleEvent_Impl>::iterator end() { return maEvents.end(); }
+ std::list<SwAccessibleEvent_Impl>::iterator insert( const std::list<SwAccessibleEvent_Impl>::iterator& aIter,
+ const SwAccessibleEvent_Impl& rEvent )
+ {
+ return maEvents.insert( aIter, rEvent );
+ }
+ std::list<SwAccessibleEvent_Impl>::iterator erase( const std::list<SwAccessibleEvent_Impl>::iterator& aPos )
+ {
+ return maEvents.erase( aPos );
+ }
+};
+
+// see comment in SwAccessibleMap::InvalidatePosOrSize()
+// last case "else if(pParent)" for why this surprising hack exists
+void SwAccessibleEventList_Impl::MoveMissingXAccToEnd()
+{
+ size_t nSize = size();
+ if (nSize < 2 )
+ {
+ return;
+ }
+ SwAccessibleEventList_Impl lstEvent;
+ for (auto li = begin(); li != end(); )
+ {
+ if (li->IsNoXaccParentFrame())
+ {
+ lstEvent.insert(lstEvent.end(), *li);
+ li = erase(li);
+ }
+ else
+ ++li;
+ }
+ assert(size() + lstEvent.size() == nSize);
+ maEvents.insert(end(),lstEvent.begin(),lstEvent.end());
+ assert(size() == nSize);
+}
+
+namespace {
+
+struct SwAccessibleChildFunc
+{
+ bool operator()( const SwAccessibleChild& r1,
+ const SwAccessibleChild& r2 ) const
+ {
+ const void *p1 = r1.GetSwFrame()
+ ? static_cast < const void * >( r1.GetSwFrame())
+ : ( r1.GetDrawObject()
+ ? static_cast < const void * >( r1.GetDrawObject() )
+ : static_cast < const void * >( r1.GetWindow() ) );
+ const void *p2 = r2.GetSwFrame()
+ ? static_cast < const void * >( r2.GetSwFrame())
+ : ( r2.GetDrawObject()
+ ? static_cast < const void * >( r2.GetDrawObject() )
+ : static_cast < const void * >( r2.GetWindow() ) );
+ return p1 < p2;
+ }
+};
+
+}
+
+class SwAccessibleEventMap_Impl
+{
+public:
+ typedef SwAccessibleChild key_type;
+ typedef std::list<SwAccessibleEvent_Impl>::iterator mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwAccessibleChildFunc key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map <key_type,mapped_type,key_compare> maMap;
+public:
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+struct SwAccessibleParaSelection
+{
+ TextFrameIndex nStartOfSelection;
+ TextFrameIndex nEndOfSelection;
+
+ SwAccessibleParaSelection(const TextFrameIndex nStartOfSelection_,
+ const TextFrameIndex nEndOfSelection_)
+ : nStartOfSelection(nStartOfSelection_)
+ , nEndOfSelection(nEndOfSelection_)
+ {}
+};
+
+struct SwXAccWeakRefComp
+{
+ bool operator()( const uno::WeakReference<XAccessible>& _rXAccWeakRef1,
+ const uno::WeakReference<XAccessible>& _rXAccWeakRef2 ) const
+ {
+ return _rXAccWeakRef1.get() < _rXAccWeakRef2.get();
+ }
+};
+
+}
+
+class SwAccessibleSelectedParas_Impl
+{
+public:
+ typedef uno::WeakReference < XAccessible > key_type;
+ typedef SwAccessibleParaSelection mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwXAccWeakRefComp key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map<key_type,mapped_type,key_compare> maMap;
+public:
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+// helper class that stores preview data
+class SwAccPreviewData
+{
+ typedef std::vector<tools::Rectangle> Rectangles;
+ Rectangles maPreviewRects;
+ Rectangles maLogicRects;
+
+ SwRect maVisArea;
+ Fraction maScale;
+
+ const SwPageFrame *mpSelPage;
+
+ /** adjust logic page rectangle to its visible part
+
+ @param _iorLogicPgSwRect
+ input/output parameter - reference to the logic page rectangle, which
+ has to be adjusted.
+
+ @param _rPreviewPgSwRect
+ input parameter - constant reference to the corresponding preview page
+ rectangle; needed to determine the visible part of the logic page rectangle.
+
+ @param _rPreviewWinSize
+ input parameter - constant reference to the preview window size in TWIP;
+ needed to determine the visible part of the logic page rectangle
+ */
+ static void AdjustLogicPgRectToVisibleArea( SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize );
+
+public:
+ SwAccPreviewData();
+
+ void Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize );
+
+ void InvalidateSelection( const SwPageFrame* _pSelectedPageFrame );
+
+ const SwRect& GetVisArea() const { return maVisArea;}
+
+ /** Adjust the MapMode so that the preview page appears at the
+ * proper position. rPoint identifies the page for which the
+ * MapMode should be adjusted. If bFromPreview is true, rPoint is
+ * a preview coordinate; else it's a document coordinate. */
+ void AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const;
+
+ const SwPageFrame *GetSelPage() const { return mpSelPage; }
+
+ void DisposePage(const SwPageFrame *pPageFrame );
+};
+
+SwAccPreviewData::SwAccPreviewData() :
+ mpSelPage( nullptr )
+{
+}
+
+void SwAccPreviewData::Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // store preview scaling, maximal preview page size and selected page
+ maScale = _rScale;
+ mpSelPage = _pSelectedPageFrame;
+
+ // prepare loop on preview pages
+ maPreviewRects.clear();
+ maLogicRects.clear();
+ SwAccessibleChild aPage;
+ maVisArea.Clear();
+
+ // loop on preview pages to calculate <maPreviewRects>, <maLogicRects> and
+ // <maVisArea>
+ for ( auto & rpPreviewPage : _rPreviewPages )
+ {
+ aPage = rpPreviewPage->pPage;
+
+ // add preview page rectangle to <maPreviewRects>
+ tools::Rectangle aPreviewPgRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize );
+ maPreviewRects.push_back( aPreviewPgRect );
+
+ // add logic page rectangle to <maLogicRects>
+ SwRect aLogicPgSwRect( aPage.GetBox( rAccMap ) );
+ tools::Rectangle aLogicPgRect( aLogicPgSwRect.SVRect() );
+ maLogicRects.push_back( aLogicPgRect );
+ // union visible area with visible part of logic page rectangle
+ if ( rpPreviewPage->bVisible )
+ {
+ if ( !rpPreviewPage->pPage->IsEmptyPage() )
+ {
+ AdjustLogicPgRectToVisibleArea( aLogicPgSwRect,
+ SwRect( aPreviewPgRect ),
+ _rPreviewWinSize );
+ }
+ if ( maVisArea.IsEmpty() )
+ maVisArea = aLogicPgSwRect;
+ else
+ maVisArea.Union( aLogicPgSwRect );
+ }
+ }
+}
+
+void SwAccPreviewData::InvalidateSelection( const SwPageFrame* _pSelectedPageFrame )
+{
+ mpSelPage = _pSelectedPageFrame;
+ assert(mpSelPage);
+}
+
+namespace {
+
+struct ContainsPredicate
+{
+ const Point& mrPoint;
+ explicit ContainsPredicate( const Point& rPoint ) : mrPoint(rPoint) {}
+ bool operator() ( const tools::Rectangle& rRect ) const
+ {
+ return rRect.IsInside( mrPoint );
+ }
+};
+
+}
+
+void SwAccPreviewData::AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const
+{
+ // adjust scale
+ rMapMode.SetScaleX( maScale );
+ rMapMode.SetScaleY( maScale );
+
+ // find proper rectangle
+ Rectangles::const_iterator aBegin = maLogicRects.begin();
+ Rectangles::const_iterator aEnd = maLogicRects.end();
+ Rectangles::const_iterator aFound = std::find_if( aBegin, aEnd,
+ ContainsPredicate( rPoint ) );
+
+ if( aFound != aEnd )
+ {
+ // found! set new origin
+ Point aPoint = (maPreviewRects.begin() + (aFound - aBegin))->TopLeft();
+ aPoint -= (maLogicRects.begin() + (aFound-aBegin))->TopLeft();
+ rMapMode.SetOrigin( aPoint );
+ }
+ // else: don't adjust MapMode
+}
+
+void SwAccPreviewData::DisposePage(const SwPageFrame *pPageFrame )
+{
+ if( mpSelPage == pPageFrame )
+ mpSelPage = nullptr;
+}
+
+// adjust logic page rectangle to its visible part
+void SwAccPreviewData::AdjustLogicPgRectToVisibleArea(
+ SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize )
+{
+ // determine preview window rectangle
+ const SwRect aPreviewWinSwRect( Point( 0, 0 ), _rPreviewWinSize );
+ // calculate visible preview page rectangle
+ SwRect aVisPreviewPgSwRect( _rPreviewPgSwRect );
+ aVisPreviewPgSwRect.Intersection( aPreviewWinSwRect );
+ // adjust logic page rectangle
+ SwTwips nTmpDiff;
+ // left
+ nTmpDiff = aVisPreviewPgSwRect.Left() - _rPreviewPgSwRect.Left();
+ _iorLogicPgSwRect.AddLeft( nTmpDiff );
+ // top
+ nTmpDiff = aVisPreviewPgSwRect.Top() - _rPreviewPgSwRect.Top();
+ _iorLogicPgSwRect.AddTop( nTmpDiff );
+ // right
+ nTmpDiff = _rPreviewPgSwRect.Right() - aVisPreviewPgSwRect.Right();
+ _iorLogicPgSwRect.AddRight( - nTmpDiff );
+ // bottom
+ nTmpDiff = _rPreviewPgSwRect.Bottom() - aVisPreviewPgSwRect.Bottom();
+ _iorLogicPgSwRect.AddBottom( - nTmpDiff );
+}
+
+static bool AreInSameTable( const uno::Reference< XAccessible >& rAcc,
+ const SwFrame *pFrame )
+{
+ bool bRet = false;
+
+ if( pFrame && pFrame->IsCellFrame() && rAcc.is() )
+ {
+ // Is it in the same table? We check that
+ // by comparing the last table frame in the
+ // follow chain, because that's cheaper than
+ // searching the first one.
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( rAcc.get() );
+ if( pAccImpl->GetFrame()->IsCellFrame() )
+ {
+ const SwTabFrame *pTabFrame1 = pAccImpl->GetFrame()->FindTabFrame();
+ if (pTabFrame1)
+ {
+ while (pTabFrame1->GetFollow())
+ pTabFrame1 = pTabFrame1->GetFollow();
+ }
+
+ const SwTabFrame *pTabFrame2 = pFrame->FindTabFrame();
+ if (pTabFrame2)
+ {
+ while (pTabFrame2->GetFollow())
+ pTabFrame2 = pTabFrame2->GetFollow();
+ }
+
+ bRet = (pTabFrame1 == pTabFrame2);
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleMap::FireEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ ::rtl::Reference < SwAccessibleContext > xAccImpl( rEvent.GetContext() );
+ if (!xAccImpl.is() && rEvent.mpParentFrame != nullptr)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( rEvent.mpParentFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if (xAcc.is())
+ {
+ uno::Reference < XAccessibleContext > xContext(xAcc,uno::UNO_QUERY);
+ if (xContext.is() && xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ }
+ if( SwAccessibleEvent_Impl::SHAPE_SELECTION == rEvent.GetType() )
+ {
+ DoInvalidateShapeSelection();
+ }
+ else if( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ if ( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ rEvent.IsInvalidateTextAttrs() )
+ {
+ xAccImpl->InvalidateAttr();
+ }
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ xAccImpl->InvalidateContent();
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ xAccImpl->InvalidatePosOrSize( rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ xAccImpl->InvalidateChildPosOrSize( rEvent.GetFrameOrObj(),
+ rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ assert(!"dispose event has been stored");
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ // nothing to do here - handled above
+ break;
+ default:
+ break;
+ }
+ if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ if( rEvent.IsUpdateCursorPos() )
+ xAccImpl->InvalidateCursorPos();
+ if( rEvent.IsInvalidateStates() )
+ xAccImpl->InvalidateStates( rEvent.GetStates() );
+ if( rEvent.IsInvalidateRelation() )
+ {
+ // both events CONTENT_FLOWS_FROM_RELATION_CHANGED and
+ // CONTENT_FLOWS_TO_RELATION_CHANGED are possible
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_FROM )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED );
+ }
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_TO )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+ }
+
+ if ( rEvent.IsInvalidateTextSelection() )
+ {
+ xAccImpl->InvalidateTextSelection();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::AppendEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ osl::MutexGuard aGuard( maEventMutex );
+
+ if( !mpEvents )
+ mpEvents.reset(new SwAccessibleEventList_Impl);
+ if( !mpEventMap )
+ mpEventMap.reset(new SwAccessibleEventMap_Impl);
+
+ if( mpEvents->IsFiring() )
+ {
+ // While events are fired new ones are generated. They have to be fired
+ // now. This does not work for DISPOSE events!
+ OSL_ENSURE( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE,
+ "dispose event while firing events" );
+ FireEvent( rEvent );
+ }
+ else
+ {
+
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( rEvent.GetFrameOrObj() );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent( *(*aIter).second );
+ assert( aEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ "dispose events should not be stored" );
+ bool bAppendEvent = true;
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::CARET_OR_STATES:
+ // A CARET_OR_STATES event is added to any other
+ // event only. It is broadcasted after any other event, so the
+ // event should be put to the back.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ break;
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ // An INVALID_CONTENT event overwrites a CARET_OR_STATES
+ // event (but keeps its flags) and it is contained in a
+ // POS_CHANGED event.
+ // Therefore, the event's type has to be adapted and the event
+ // has to be put at the end.
+ //
+ // fdo#56031 An INVALID_CONTENT event overwrites a INVALID_ATTR
+ // event and overwrites its flags
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES )
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ else if ( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR )
+ {
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ }
+
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ // A pos changed event overwrites CARET_STATES (keeping its
+ // flags) as well as INVALID_CONTENT. The old box position
+ // has to be stored however if the old event is not a
+ // POS_CHANGED itself.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() != SwAccessibleEvent_Impl::POS_CHANGED )
+ aEvent.SetOldBox( rEvent.GetOldBox() );
+ aEvent.SetType( SwAccessibleEvent_Impl::POS_CHANGED );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ // CHILD_POS_CHANGED events can only follow CHILD_POS_CHANGED
+ // events. The only action that needs to be done again is
+ // to put the old event to the back. The new one cannot be used,
+ // because we are interested in the old frame bounds.
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::SHAPE_SELECTION:
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::SHAPE_SELECTION,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ // DISPOSE events overwrite all others. They are not stored
+ // but executed immediately to avoid broadcasting of
+ // nonfunctional objects. So what needs to be done here is to
+ // remove all events for the frame in question.
+ bAppendEvent = false;
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR,
+ "invalid event combination" );
+ break;
+ }
+ if( bAppendEvent )
+ {
+ mpEvents->erase( (*aIter).second );
+ (*aIter).second = mpEvents->insert( mpEvents->end(), aEvent );
+ }
+ else
+ {
+ mpEvents->erase( (*aIter).second );
+ mpEventMap->erase( aIter );
+ }
+ }
+ else if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ mpEventMap->emplace( rEvent.GetFrameOrObj(),
+ mpEvents->insert( mpEvents->end(), rEvent ) );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition(
+ const uno::Reference< XAccessible >& rAcc )
+{
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( rAcc.get() );
+ assert(pAccImpl);
+ assert(pAccImpl->GetFrame());
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::CARET );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ // While firing events the current frame might have
+ // been disposed because it moved out of the visible area.
+ // Setting the cursor for such frames is useless and even
+ // causes asserts.
+ if( pAccImpl->GetFrame() )
+ pAccImpl->InvalidateCursorPos();
+ }
+}
+
+void SwAccessibleMap::InvalidateShapeSelection()
+{
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::SHAPE_SELECTION );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ DoInvalidateShapeSelection();
+ }
+}
+
+//This method should implement the following functions:
+//1.find the shape objects and set the selected state.
+//2.find the Swframe objects and set the selected state.
+//3.find the paragraph objects and set the selected state.
+void SwAccessibleMap::InvalidateShapeInParaSelection()
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) != nullptr ?
+ static_cast< const SwFEShell * >( pVSh ) : nullptr;
+ SwPaM* pCursor = pFESh ? pFESh->GetCursor( false /* ??? */ ) : nullptr;
+
+ //const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+ }
+
+ bool bIsSelAll =IsDocumentSelAll();
+
+ if( mpShapeMap )
+ {
+ //Checked for shapes.
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+
+ if( bIsSelAll)
+ {
+ while( aIter != aEndIter )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ }
+ }
+ else
+ {
+ while( aIter != aEndIter )
+ {
+ const SwFrameFormat *pFrameFormat = (*aIter).first ? ::FindFrameFormat( (*aIter).first ) : nullptr;
+ if( !pFrameFormat )
+ {
+ ++aIter;
+ continue;
+ }
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+
+ if(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if(xAcc.is())
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ continue;
+ }
+
+ if( !pPos )
+ {
+ ++aIter;
+ continue;
+ }
+ if( pPos->nNode.GetNode().GetTextNode() )
+ {
+ int nIndex = pPos->nContent.GetIndex();
+ bool bMarked = false;
+ if( pCursor != nullptr )
+ {
+ const SwTextNode* pNode = pPos->nNode.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pVSh->GetLayout())));
+ sal_uLong nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ sal_uLong nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ sal_uLong nHere = pNode->GetIndex();
+
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ bMarked = true;
+ // check whether nHere is 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ sal_uLong nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ sal_uLong nEndIndex = pEnd->nNode.GetIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ if( ( ((nHere == nStartIndex) && (nIndex >= pStart->nContent.GetIndex())) || (nHere > nStartIndex) )
+ &&( ((nHere == nEndIndex) && (nIndex < pEnd->nContent.GetIndex())) || (nHere < nEndIndex) ) )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ uno::Reference<XAccessible> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+ {
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ uno::Reference<XAccessible> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsDestroyFrameAnchoredAtChar(*pPos, *pStart, *pEnd))
+ {
+ static_cast<::accessibility::AccessibleShape*>(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ static_cast<::accessibility::AccessibleShape*>(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !bMarked )
+ {
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ size_t nNumShapes = nShapes;
+ while( nNumShapes )
+ {
+ if( pShape < pSelShape && (pShape->first==(*aIter).first) )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if(xAcc.is())
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ --nNumShapes;
+ ++pShape;
+ }
+ }
+ }
+
+ ++aIter;
+ }//while( aIter != aEndIter )
+ }//else
+ }
+
+ pShapes.reset();
+
+ //Checked for FlyFrame
+ if (mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ const SwFrame *pFrame = (*aIter).first;
+ if(pFrame->IsFlyFrame())
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+
+ if(xAcc.is())
+ {
+ SwAccessibleFrameBase *pAccFrame = static_cast< SwAccessibleFrameBase * >(xAcc.get());
+ bool bFrameChanged = pAccFrame->SetSelectedState( true );
+ if (bFrameChanged)
+ {
+ const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame * >( pFrame );
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xAccParent = pAccFrame->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference< XAccessibleContext > xAccContext = xAccParent->getAccessibleContext();
+ if(xAccContext.is() && xAccContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xAccContext.get());
+ if(pAccFrame->IsSeletedInDoc())
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ else if(m_setParaAdd.count(pAccPara) == 0)
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ++aIter;
+ }
+ }
+
+ typedef std::vector< SwAccessibleContext* > VEC_PARA;
+ VEC_PARA vecAdd;
+ VEC_PARA vecRemove;
+ //Checked for Paras.
+ bool bMarkChanged = false;
+ SwAccessibleContextMap_Impl mapTemp;
+ if( pCursor != nullptr )
+ {
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ if( rTmpCursor.HasMark() )
+ {
+ SwNodeIndex nStartIndex( rTmpCursor.Start()->nNode );
+ SwNodeIndex nEndIndex( rTmpCursor.End()->nNode );
+ for (; nStartIndex <= nEndIndex; ++nStartIndex)
+ {
+ SwFrame *pFrame = nullptr;
+ if(nStartIndex.GetNode().IsContentNode())
+ {
+ SwContentNode* pCNd = static_cast<SwContentNode*>(&(nStartIndex.GetNode()));
+ pFrame = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First();
+ if (mapTemp.find(pFrame) != mapTemp.end())
+ {
+ continue; // sw_redlinehide: once is enough
+ }
+ }
+ else if( nStartIndex.GetNode().IsTableNode() )
+ {
+ SwTableNode * pTable = static_cast<SwTableNode *>(&(nStartIndex.GetNode()));
+ SwTableFormat* pFormat = pTable->GetTable().GetFrameFormat();
+ pFrame = SwIterator<SwFrame, SwTableFormat>(*pFormat).First();
+ }
+
+ if( pFrame && mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+ bool isChanged = false;
+ if( xAcc.is() )
+ {
+ isChanged = static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( true );
+ }
+ if(!isChanged)
+ {
+ SwAccessibleContextMap_Impl::iterator aEraseIter = mpSeletedFrameMap->find( pFrame );
+ if(aEraseIter != mpSeletedFrameMap->end())
+ mpSeletedFrameMap->erase(aEraseIter);
+ }
+ else
+ {
+ bMarkChanged = true;
+ vecAdd.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
+ }
+
+ mapTemp.emplace( pFrame, xAcc );
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !mpSeletedFrameMap )
+ mpSeletedFrameMap.reset( new SwAccessibleContextMap_Impl );
+ if( !mpSeletedFrameMap->empty() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpSeletedFrameMap->begin();
+ while( aIter != mpSeletedFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+ if(xAcc.is())
+ static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( false );
+ ++aIter;
+ vecRemove.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
+ }
+ bMarkChanged = true;
+ mpSeletedFrameMap->clear();
+ }
+
+ SwAccessibleContextMap_Impl::iterator aIter = mapTemp.begin();
+ while( aIter != mapTemp.end() )
+ {
+ mpSeletedFrameMap->emplace( (*aIter).first, (*aIter).second );
+ ++aIter;
+ }
+ mapTemp.clear();
+
+ if( bMarkChanged && mpFrameMap)
+ {
+ for (SwAccessibleContext* pAccPara : vecAdd)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ for (SwAccessibleContext* pAccPara : vecRemove)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+}
+
+//Merge with DoInvalidateShapeFocus
+void SwAccessibleMap::DoInvalidateShapeSelection(bool bInvalidateFocusMode /*=false*/)
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) != nullptr ?
+ static_cast< const SwFEShell * >( pVSh ) : nullptr;
+ const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ //when InvalidateFocus Call this function ,and the current selected shape count is not 1 ,
+ //return
+ if (bInvalidateFocusMode && nSelShapes != 1)
+ {
+ return;
+ }
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+ }
+
+ if( !pShapes )
+ return;
+
+ typedef std::vector< ::rtl::Reference < ::accessibility::AccessibleShape > > VEC_SHAPE;
+ VEC_SHAPE vecxShapeAdd;
+ VEC_SHAPE vecxShapeRemove;
+ int nCountSelectedShape=0;
+
+ vcl::Window *pWin = GetShell()->GetWin();
+ bool bFocused = pWin && pWin->HasFocus();
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ int nShapeCount = nShapes;
+ while( nShapeCount )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape < pSelShape )
+ {
+ if(pShape->second->ResetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeRemove.push_back(pShape->second);
+ }
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+ }
+ }
+ --nShapeCount;
+ ++pShape;
+ }
+
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, uno::Any(), uno::Any());
+ }
+ }
+
+ pShape = pShapes.get();
+
+ while( nShapes )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape >= pSelShape )
+ {
+ //first fire focus event
+ if( bFocused && 1 == nSelShapes )
+ pShape->second->SetState( AccessibleStateType::FOCUSED );
+ else
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+
+ if(pShape->second->SetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeAdd.push_back(pShape->second);
+ }
+ ++nCountSelectedShape;
+ }
+ }
+
+ --nShapes;
+ ++pShape;
+ }
+
+ const unsigned int SELECTION_WITH_NUM = 10;
+ if (vecxShapeAdd.size() > SELECTION_WITH_NUM )
+ {
+ uno::Reference< XAccessible > xDoc = GetDocumentView( );
+ SwAccessibleContext * pCont = static_cast<SwAccessibleContext *>(xDoc.get());
+ if (pCont)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ pCont->FireAccessibleEvent(aEvent);
+ }
+ }
+ else
+ {
+ short nEventID = AccessibleEventId::SELECTION_CHANGED_ADD;
+ if (nCountSelectedShape <= 1 && vecxShapeAdd.size() == 1 )
+ {
+ nEventID = AccessibleEventId::SELECTION_CHANGED;
+ }
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(nEventID, uno::Any(), uno::Any());
+ }
+ }
+ }
+
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ SdrObject *pObj = GetSdrObjectFromXShape(pAccShape->GetXShape());
+ SwFrameFormat *pFrameFormat = pObj ? FindFrameFormat( pObj ) : nullptr;
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ if (xPara.is())
+ {
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (pAccPara)
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape && !pAccShape->IsDisposed())
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (m_setParaAdd.count(pAccPara) == 0 )
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+}
+
+//Merge with DoInvalidateShapeSelection
+/*
+void SwAccessibleMap::DoInvalidateShapeFocus()
+{
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>( pVSh) != nullptr ?
+ static_cast< const SwFEShell * >( pVSh ) : nullptr;
+ const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ if( nSelShapes != 1 )
+ return;
+
+ SwAccessibleObjShape_Impl *pShapes = nullptr;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+ }
+
+ if( pShapes )
+ {
+ vcl::Window *pWin = GetShell()->GetWin();
+ bool bFocused = pWin && pWin->HasFocus();
+ SwAccessibleObjShape_Impl *pShape = pShapes;
+ while( nShapes )
+ {
+ if( pShape->second.is() )
+ {
+ if( bFocused && pShape >= pSelShape )
+ pShape->second->SetState( AccessibleStateType::FOCUSED );
+ else
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+ }
+
+ --nShapes;
+ ++pShape;
+ }
+
+ delete[] pShapes;
+ }
+}
+
+*/
+
+SwAccessibleMap::SwAccessibleMap( SwViewShell *pSh ) :
+ mpVSh( pSh ),
+ mbShapeSelected( false ),
+ maDocName(SwAccessibleContext::GetResource(STR_ACCESS_DOC_NAME))
+{
+ pSh->GetLayout()->AddAccessibleShell();
+}
+
+SwAccessibleMap::~SwAccessibleMap()
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpFrameMap )
+ {
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( !xAcc.is() )
+ assert(false); // let's hope this can't happen? the vcl::Window apparently owns the top-level
+ //xAcc = new SwAccessibleDocument(shared_from_this());
+ }
+ }
+
+ if(xAcc.is())
+ {
+ SwAccessibleDocumentBase *const pAcc =
+ static_cast<SwAccessibleDocumentBase *>(xAcc.get());
+ pAcc->Dispose( true );
+ }
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xTmp = (*aIter).second;
+ if( xTmp.is() )
+ {
+ SwAccessibleContext *pTmp = static_cast< SwAccessibleContext * >( xTmp.get() );
+ assert(pTmp->GetMap() == nullptr); // must be disposed
+ }
+ ++aIter;
+ }
+ }
+#endif
+ {
+ osl::MutexGuard aGuard( maMutex );
+ assert((!mpFrameMap || mpFrameMap->empty()) &&
+ "Frame map should be empty after disposing the root frame");
+ assert((!mpShapeMap || mpShapeMap->empty()) &&
+ "Object map should be empty after disposing the root frame");
+ mpFrameMap.reset();
+ mpShapeMap.reset();
+ mvShapes.clear();
+ mpSelectedParas.reset();
+ }
+
+ mpPreview.reset();
+
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ assert(!mpEvents);
+ assert(!mpEventMap);
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ mpVSh->GetLayout()->RemoveAccessibleShell();
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView_(
+ bool bPagePreview )
+{
+ uno::Reference < XAccessible > xAcc;
+ bool bSetVisArea = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpFrameMap )
+ {
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ assert(!mpFrameMap->mbLocked);
+ mpFrameMap->mbLocked = true;
+#endif
+
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( xAcc.is() )
+ {
+ bSetVisArea = true; // Set VisArea when map mutex is not locked
+ }
+ else
+ {
+ if( bPagePreview )
+ xAcc = new SwAccessiblePreview(shared_from_this());
+ else
+ xAcc = new SwAccessibleDocument(shared_from_this());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpFrameMap->emplace( pRootFrame, xAcc );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+ }
+
+ if( bSetVisArea )
+ {
+ SwAccessibleDocumentBase *pAcc =
+ static_cast< SwAccessibleDocumentBase * >( xAcc.get() );
+ pAcc->SetVisArea();
+ }
+
+ return xAcc;
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView( )
+{
+ return GetDocumentView_( false );
+}
+
+uno::Reference<XAccessible> SwAccessibleMap::GetDocumentPreview(
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // create & update preview data object
+ if( mpPreview == nullptr )
+ mpPreview.reset( new SwAccPreviewData() );
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ return xAcc;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext( const SwFrame *pFrame,
+ bool bCreate )
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Reference < XAccessible > xAcc;
+ uno::Reference < XAccessible > xOldCursorAcc;
+ bool bOldShapeSelected = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpFrameMap && bCreate )
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ SwAccessibleContext *pAcc = nullptr;
+ switch( pFrame->GetType() )
+ {
+ case SwFrameType::Txt:
+ pAcc = new SwAccessibleParagraph(shared_from_this(),
+ static_cast< const SwTextFrame& >( *pFrame ) );
+ break;
+ case SwFrameType::Header:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwHeaderFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Footer:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwFooterFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Ftn:
+ {
+ const SwFootnoteFrame *pFootnoteFrame =
+ static_cast < const SwFootnoteFrame * >( pFrame );
+ bool bIsEndnote =
+ SwAccessibleFootnote::IsEndnote( pFootnoteFrame );
+ pAcc = new SwAccessibleFootnote(shared_from_this(), bIsEndnote,
+ /*(bIsEndnote ? mnEndnote++ : mnFootnote++),*/
+ pFootnoteFrame );
+ }
+ break;
+ case SwFrameType::Fly:
+ {
+ const SwFlyFrame *pFlyFrame =
+ static_cast < const SwFlyFrame * >( pFrame );
+ switch( SwAccessibleFrameBase::GetNodeType( pFlyFrame ) )
+ {
+ case SwNodeType::Grf:
+ pAcc = new SwAccessibleGraphic(shared_from_this(), pFlyFrame );
+ break;
+ case SwNodeType::Ole:
+ pAcc = new SwAccessibleEmbeddedObject(shared_from_this(), pFlyFrame );
+ break;
+ default:
+ pAcc = new SwAccessibleTextFrame(shared_from_this(), *pFlyFrame );
+ break;
+ }
+ }
+ break;
+ case SwFrameType::Cell:
+ pAcc = new SwAccessibleCell(shared_from_this(),
+ static_cast< const SwCellFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Tab:
+ pAcc = new SwAccessibleTable(shared_from_this(),
+ static_cast< const SwTabFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Page:
+ OSL_ENSURE( GetShell()->IsPreview(),
+ "accessible page frames only in PagePreview" );
+ pAcc = new SwAccessiblePage(shared_from_this(), pFrame);
+ break;
+ default: break;
+ }
+ xAcc = pAcc;
+ assert(xAcc.is());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpFrameMap->emplace( pFrame, xAcc );
+ }
+
+ if( pAcc->HasCursor() &&
+ !AreInSameTable( mxCursorContext, pFrame ) )
+ {
+ // If the new context has the focus, and if we know
+ // another context that had the focus, then the focus
+ // just moves from the old context to the new one. We
+ // then have to send a focus event and a caret event for
+ // the old context. We have to do that now,
+ // because after we have left this method, anyone might
+ // call getStates for the new context and will get a
+ // focused state then. Sending the focus changes event
+ // after that seems to be strange. However, we cannot
+ // send a focus event for the new context now, because
+ // no one except us knows it. In any case, we remember
+ // the new context as the one that has the focus
+ // currently.
+
+ xOldCursorAcc = mxCursorContext;
+ mxCursorContext = xAcc;
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = false;
+ }
+ }
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+ if( bOldShapeSelected )
+ InvalidateShapeSelection();
+
+ return xAcc;
+}
+
+::rtl::Reference < SwAccessibleContext > SwAccessibleMap::GetContextImpl(
+ const SwFrame *pFrame,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pFrame, bCreate ) );
+
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast< SwAccessibleContext * >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc;
+ uno::Reference < XAccessible > xOldCursorAcc;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpShapeMap && bCreate )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ rtl::Reference< ::accessibility::AccessibleShape> pAcc;
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ uno::Reference < XAccessible > xParent( pParentImpl );
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+
+ pAcc = rShapeTypeHandler.CreateAccessibleObject(
+ aShapeInfo, mpShapeMap->GetInfo() );
+ }
+ xAcc = pAcc.get();
+ assert(xAcc.is());
+ pAcc->Init();
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ // TODO: focus!!!
+ AddGroupContext(pObj, xAcc);
+ }
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+
+ return xAcc;
+}
+
+bool SwAccessibleMap::IsInSameLevel(const SdrObject* pObj, const SwFEShell* pFESh)
+{
+ if (pFESh)
+ return pFESh->IsObjSameLevelWithMarked(pObj);
+ return false;
+}
+
+void SwAccessibleMap::AddShapeContext(const SdrObject *pObj, uno::Reference < XAccessible > const & xAccShape)
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpShapeMap )
+ {
+ mpShapeMap->emplace( pObj, xAccShape );
+ }
+
+}
+
+//Added by yanjun for sym2_6407
+void SwAccessibleMap::RemoveGroupContext(const SdrObject *pParentObj)
+{
+ osl::MutexGuard aGuard( maMutex );
+ // TODO: Why are sub-shapes of group shapes even added to our map?
+ // Doesn't the AccessibleShape of the top-level shape create them
+ // on demand anyway? Why does SwAccessibleMap need to know them?
+ // We cannot rely on getAccessibleChild here to remove the sub-shapes
+ // from mpShapes because the top-level shape may not only be disposed here
+ // but also by visibility checks in svx, then it doesn't return children.
+ if (mpShapeMap && pParentObj && pParentObj->IsGroupObject())
+ {
+ SdrObjList *const pChildren(pParentObj->GetSubList());
+ for (size_t i = 0; pChildren && i < pChildren->GetObjCount(); ++i)
+ {
+ SdrObject *const pChild(pChildren->GetObj(i));
+ assert(pChild);
+ RemoveContext(pChild);
+ }
+ }
+}
+//End
+
+void SwAccessibleMap::AddGroupContext(const SdrObject *pParentObj, uno::Reference < XAccessible > const & xAccParent)
+{
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ {
+ //here get all the sub list.
+ if (pParentObj->IsGroupObject())
+ {
+ if (xAccParent.is())
+ {
+ uno::Reference < XAccessibleContext > xContext = xAccParent->getAccessibleContext();
+ if (xContext.is())
+ {
+ sal_Int32 nChildren = xContext->getAccessibleChildCount();
+ for(sal_Int32 i = 0; i<nChildren; i++)
+ {
+ uno::Reference < XAccessible > xChild = xContext->getAccessibleChild(i);
+ if (xChild.is())
+ {
+ uno::Reference < XAccessibleContext > xChildContext = xChild->getAccessibleContext();
+ if (xChildContext.is())
+ {
+ short nRole = xChildContext->getAccessibleRole();
+ if (nRole == AccessibleRole::SHAPE)
+ {
+ ::accessibility::AccessibleShape* pAccShape = static_cast < ::accessibility::AccessibleShape* >( xChild.get());
+ uno::Reference < drawing::XShape > xShape = pAccShape->GetXShape();
+ if (xShape.is())
+ {
+ SdrObject* pObj = GetSdrObjectFromXShape(xShape);
+ AddShapeContext(pObj, xChild);
+ AddGroupContext(pObj,xChild);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+::rtl::Reference < ::accessibility::AccessibleShape > SwAccessibleMap::GetContextImpl(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pObj, pParentImpl, bCreate ) );
+
+ ::rtl::Reference < ::accessibility::AccessibleShape > xAccImpl(
+ static_cast< ::accessibility::AccessibleShape* >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+void SwAccessibleMap::RemoveContext( const SwFrame *pFrame )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ mpFrameMap->erase( aIter );
+
+ // Remove reference to old caret object. Though mxCursorContext
+ // is a weak reference and cleared automatically, clearing it
+ // directly makes sure to not keep a non-functional object.
+ uno::Reference < XAccessible > xOldAcc( mxCursorContext );
+ if( xOldAcc.is() )
+ {
+ SwAccessibleContext *pOldAccImpl =
+ static_cast< SwAccessibleContext *>( xOldAcc.get() );
+ OSL_ENSURE( pOldAccImpl->GetFrame(), "old caret context is disposed" );
+ if( pOldAccImpl->GetFrame() == pFrame )
+ {
+ xOldAcc.clear(); // get an empty ref
+ mxCursorContext = xOldAcc;
+ }
+ }
+
+ if( mpFrameMap->empty() )
+ {
+ mpFrameMap.reset();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::RemoveContext( const SdrObject *pObj )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ {
+ uno::Reference < XAccessible > xTempHold( (*aIter).second );
+ mpShapeMap->erase( aIter );
+ RemoveGroupContext(pObj);
+ // The shape selection flag is not cleared, but one might do
+ // so but has to make sure that the removed context is the one
+ // that is selected.
+
+ if( mpShapeMap && mpShapeMap->empty() )
+ {
+ mpShapeMap.reset();
+ }
+ }
+ }
+}
+
+bool SwAccessibleMap::Contains(const SwFrame *pFrame) const
+{
+ return (pFrame && mpFrameMap && mpFrameMap->find(pFrame) != mpFrameMap->end());
+}
+
+void SwAccessibleMap::A11yDispose( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ bool bRecursive,
+ bool bCanSkipInvisible )
+{
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+
+ // Indeed, the following assert checks the frame's accessible flag,
+ // because that's the one that is evaluated in the layout. The frame
+ // might not be accessible anyway. That's the case for cell frames that
+ // contain further cells.
+ OSL_ENSURE( !aFrameOrObj.GetSwFrame() || aFrameOrObj.GetSwFrame()->IsAccessibleFrame(),
+ "non accessible frame should be disposed" );
+
+ if (aFrameOrObj.IsAccessible(GetShell()->IsPreview())
+ // fdo#87199 dispose the darn thing if it ever was accessible
+ || Contains(pFrame))
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ ::rtl::Reference< ::accessibility::AccessibleShape > xShapeAccImpl;
+ // get accessible context for frame
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ // First of all look for an accessible context for a frame
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ if( !xAccImpl.is() && mpFrameMap )
+ {
+ // If there is none, look if the parent is accessible.
+ const SwFrame *pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xParentAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ if( !xParentAccImpl.is() && !aFrameOrObj.GetSwFrame() && mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter =
+ mpShapeMap->find( aFrameOrObj.GetDrawObject() );
+ if( aIter != mpShapeMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xShapeAccImpl =
+ static_cast< ::accessibility::AccessibleShape *>( xAcc.get() );
+ }
+ }
+ if( pObj && GetShell()->ActionPend() &&
+ (xParentAccImpl.is() || xShapeAccImpl.is()) )
+ {
+ // Keep a reference to the XShape to avoid that it
+ // is deleted with a SwFrameFormat::Modify.
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ mvShapes.push_back( xShape );
+ }
+ }
+ }
+
+ // remove events stored for the frame
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( aFrameOrObj );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::DISPOSE, aFrameOrObj );
+ AppendEvent( aEvent );
+ }
+ }
+ }
+
+ // If the frame is accessible and there is a context for it, dispose
+ // the frame. If the frame is no context for it but disposing should
+ // take place recursive, the frame's children have to be disposed
+ // anyway, so we have to create the context then.
+ if( xAccImpl.is() )
+ {
+ xAccImpl->Dispose( bRecursive );
+ }
+ else if( xParentAccImpl.is() )
+ {
+ // If the frame is a cell frame, the table must be notified.
+ // If we are in an action, a table model change event will
+ // be broadcasted at the end of the action to give the table
+ // a chance to generate a single table change event.
+
+ xParentAccImpl->DisposeChild( aFrameOrObj, bRecursive, bCanSkipInvisible );
+ }
+ else if( xShapeAccImpl.is() )
+ {
+ RemoveContext( aFrameOrObj.GetDrawObject() );
+ xShapeAccImpl->dispose();
+ }
+
+ if( mpPreview && pFrame && pFrame->IsPageFrame() )
+ mpPreview->DisposePage( static_cast< const SwPageFrame *>( pFrame ) );
+ }
+}
+
+void SwAccessibleMap::InvalidatePosOrSize( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ const SwRect& rOldBox )
+{
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ const SwFrame *pParent =nullptr;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ if( aFrameOrObj.GetSwFrame() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ // If there is an accessible object already it is
+ // notified directly.
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ if( !xAccImpl.is() )
+ {
+ // Otherwise we look if the parent is accessible.
+ // If not, there is nothing to do.
+ pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xParentAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ }
+ }
+
+ if( xAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::POS_CHANGED, xAccImpl.get(),
+ aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ if (xAccImpl->GetFrame()) // not if disposed by FireEvents()
+ {
+ xAccImpl->InvalidatePosOrSize(rOldBox);
+ }
+ }
+ }
+ else if( xParentAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ assert(pParent);
+ // tdf#99722 faster not to buffer events that won't be sent
+ if (!SwAccessibleChild(pParent).IsVisibleChildrenOnly()
+ || xParentAccImpl->IsShowing(rOldBox)
+ || xParentAccImpl->IsShowing(*this, aFrameOrObj))
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ xParentAccImpl.get(), aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ }
+ else
+ {
+ FireEvents();
+ xParentAccImpl->InvalidateChildPosOrSize( aFrameOrObj,
+ rOldBox );
+ }
+ }
+ else if(pParent)
+ {
+/*
+For child graphic and its parent paragraph,if split 2 graphic to 2 paragraph,
+will delete one graphic swfrm and new create 1 graphic swfrm ,
+then the new paragraph and the new graphic SwFrame will add .
+but when add graphic SwFrame ,the accessible of the new Paragraph is not created yet.
+so the new graphic accessible 'parent is NULL,
+so run here: save the parent's SwFrame not the accessible object parent,
+*/
+ bool bIsValidFrame = false;
+ bool bIsTextParent = false;
+ if (aFrameOrObj.GetSwFrame())
+ {
+ if (SwFrameType::Fly == pFrame->GetType())
+ {
+ bIsValidFrame =true;
+ }
+ }
+ else if(pObj)
+ {
+ if (SwFrameType::Txt == pParent->GetType())
+ {
+ bIsTextParent =true;
+ }
+ }
+ if( bIsValidFrame || bIsTextParent )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ pParent, aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ OSL_ENSURE(false,"");
+ }
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateContent( const SwFrame *pFrame )
+{
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::INVALID_CONTENT, pAccImpl,
+ aFrameOrObj );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateContent();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateAttr( const SwTextFrame& rTextFrame )
+{
+ SwAccessibleChild aFrameOrObj( &rTextFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::INVALID_ATTR,
+ pAccImpl, aFrameOrObj );
+ aEvent.SetStates( AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateAttr();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition( const SwFrame *pFrame )
+{
+ SwAccessibleChild aFrameOrObj( pFrame );
+ bool bShapeSelected = false;
+ const SwViewShell *pVSh = GetShell();
+ if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
+ {
+ if( pCSh->IsTableMode() )
+ {
+ while( aFrameOrObj.GetSwFrame() && !aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ }
+ else if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
+ {
+ const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ OSL_ENSURE( !pFrame || pFrame->FindFlyFrame() == pFlyFrame,
+ "cursor is not contained in fly frame" );
+ aFrameOrObj = pFlyFrame;
+ }
+ else if( pFESh->IsObjSelected() > 0 )
+ {
+ bShapeSelected = true;
+ aFrameOrObj = static_cast<const SwFrame *>( nullptr );
+ }
+ }
+ }
+
+ OSL_ENSURE( bShapeSelected || aFrameOrObj.IsAccessible(GetShell()->IsPreview()),
+ "frame is not accessible" );
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ bool bOldShapeSelected = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+ mxCursorContext = xAcc; // clear reference
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = bShapeSelected;
+
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ else
+ {
+ SwRect rcEmpty;
+ const SwTabFrame* pTabFrame = aFrameOrObj.GetSwFrame()->FindTabFrame();
+ if (pTabFrame)
+ {
+ InvalidatePosOrSize(pTabFrame, nullptr, nullptr, rcEmpty);
+ }
+ else
+ {
+ InvalidatePosOrSize(aFrameOrObj.GetSwFrame(), nullptr, nullptr, rcEmpty);
+ }
+
+ aIter = mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+
+ // For cells, some extra thoughts are necessary,
+ // because invalidating the cursor for one cell
+ // invalidates the cursor for all cells of the same
+ // table. For this reason, we don't want to
+ // invalidate the cursor for the old cursor object
+ // and the new one if they are within the same table,
+ // because this would result in doing the work twice.
+ // Moreover, we have to make sure to invalidate the
+ // cursor even if the current cell has no accessible object.
+ // If the old cursor objects exists and is in the same
+ // table, it's the best choice, because using it avoids
+ // an unnecessary cursor invalidation cycle when creating
+ // a new object for the current cell.
+ if( aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ {
+ if( xOldAcc.is() &&
+ AreInSameTable( xOldAcc, aFrameOrObj.GetSwFrame() ) )
+ {
+ if( xAcc.is() )
+ xOldAcc = xAcc; // avoid extra invalidation
+ else
+ xAcc = xOldAcc; // make sure at least one
+ }
+ if( !xAcc.is() )
+ xAcc = GetContext( aFrameOrObj.GetSwFrame() );
+ }
+ }
+ else if (bShapeSelected)
+ {
+ const SwFEShell *pFESh = static_cast< const SwFEShell * >( pVSh );
+ const SdrMarkList *pMarkList = pFESh->GetMarkList();
+ if (pMarkList != nullptr && pMarkList->GetMarkCount() == 1)
+ {
+ SdrObject *pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj();
+ ::rtl::Reference < ::accessibility::AccessibleShape > pAccShapeImpl = GetContextImpl(pObj,nullptr,false);
+ if (!pAccShapeImpl.is())
+ {
+ while (pObj && pObj->getParentSdrObjectFromSdrObject())
+ {
+ pObj = pObj->getParentSdrObjectFromSdrObject();
+ }
+ if (pObj != nullptr)
+ {
+ const SwFrame *pParent = SwAccessibleFrame::GetParent( SwAccessibleChild(pObj), GetShell()->IsPreview() );
+ if( pParent )
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl = GetContextImpl(pParent,false);
+ if (!xParentAccImpl.is())
+ {
+ const SwTabFrame* pTabFrame = pParent->FindTabFrame();
+ if (pTabFrame)
+ {
+ //The Table should not add in acc.because the "pParent" is not add to acc .
+ uno::Reference< XAccessible> xAccParentTab = GetContext(pTabFrame);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pTabFrame), GetShell()->IsPreview() );
+ if (pParentRoot)
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccParentTab;
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+
+ //Get "pParent" acc again.
+ xParentAccImpl = GetContextImpl(pParent,false);
+ }
+ else
+ {
+ //directly create this acc para .
+ xParentAccImpl = GetContextImpl(pParent);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pParent), GetShell()->IsPreview() );
+
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= uno::Reference< XAccessible>(xParentAccImpl.get());
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ if (xParentAccImpl.is())
+ {
+ uno::Reference< XAccessible> xAccShape =
+ GetContext(pObj,xParentAccImpl.get());
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccShape;
+ xParentAccImpl->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_setParaAdd.clear();
+ m_setParaRemove.clear();
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( bOldShapeSelected || bShapeSelected )
+ InvalidateShapeSelection();
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+
+ InvalidateShapeInParaSelection();
+
+ for (SwAccessibleParagraph* pAccPara : m_setParaRemove)
+ {
+ if(pAccPara && pAccPara->getSelectedAccessibleChildCount() == 0 && pAccPara->getSelectedText().getLength() == 0)
+ {
+ if(pAccPara->SetSelectedState(false))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ for (SwAccessibleParagraph* pAccPara : m_setParaAdd)
+ {
+ if(pAccPara && pAccPara->SetSelectedState(true))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateFocus()
+{
+ if(GetShell()->IsPreview())
+ {
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ if (xAcc)
+ {
+ SwAccessiblePreview *pAccPreview = static_cast<SwAccessiblePreview *>(xAcc.get());
+ if (pAccPreview)
+ {
+ pAccPreview->InvalidateFocus();
+ return ;
+ }
+ }
+ }
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xAcc = mxCursorContext;
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ pAccImpl->InvalidateFocus();
+ }
+ else
+ {
+ DoInvalidateShapeSelection(true);
+ }
+}
+
+void SwAccessibleMap::SetCursorContext(
+ const ::rtl::Reference < SwAccessibleContext >& rCursorContext )
+{
+ osl::MutexGuard aGuard( maMutex );
+ uno::Reference < XAccessible > xAcc( rCursorContext.get() );
+ mxCursorContext = xAcc;
+}
+
+void SwAccessibleMap::InvalidateEditableStates( const SwFrame* _pFrame )
+{
+ // Start with the frame or the first upper that is accessible
+ SwAccessibleChild aFrameOrObj( _pFrame );
+ while( aFrameOrObj.GetSwFrame() &&
+ !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ if( !aFrameOrObj.GetSwFrame() )
+ aFrameOrObj = GetShell()->GetLayout();
+
+ uno::Reference< XAccessible > xAcc( GetContext( aFrameOrObj.GetSwFrame() ) );
+ SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::EDITABLE );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateStates( AccessibleStates::EDITABLE );
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet_( const SwFrame* pFrame,
+ bool bFrom )
+{
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ // deliver event directly, or queue event
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl, SwAccessibleChild(pFrame),
+ ( bFrom
+ ? AccessibleStates::RELATION_FROM
+ : AccessibleStates::RELATION_TO ) );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateRelation( bFrom
+ ? AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
+ : AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet( const SwFrame* pMaster,
+ const SwFrame* pFollow )
+{
+ InvalidateRelationSet_( pMaster, false );
+ InvalidateRelationSet_( pFollow, true );
+}
+
+// invalidation of CONTENT_FLOW_FROM/_TO relation of a paragraph
+void SwAccessibleMap::InvalidateParaFlowRelation( const SwTextFrame& _rTextFrame,
+ const bool _bFrom )
+{
+ InvalidateRelationSet_( &_rTextFrame, _bFrom );
+}
+
+// invalidation of text selection of a paragraph
+void SwAccessibleMap::InvalidateParaTextSelection( const SwTextFrame& _rTextFrame )
+{
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( &_rTextFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ // deliver event directly, or queue event
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild( &_rTextFrame ),
+ AccessibleStates::TEXT_SELECTION_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateTextSelection();
+ }
+ }
+ }
+}
+
+sal_Int32 SwAccessibleMap::GetChildIndex( const SwFrame& rParentFrame,
+ vcl::Window& rChild ) const
+{
+ sal_Int32 nIndex( -1 );
+
+ SwAccessibleChild aFrameOrObj( &rParentFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+
+ nIndex = pAccImpl->GetChildIndex( const_cast<SwAccessibleMap&>(*this),
+ SwAccessibleChild( &rChild ) );
+ }
+ }
+
+ return nIndex;
+}
+
+void SwAccessibleMap::UpdatePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ assert(GetShell()->IsPreview() && "no preview?");
+ assert(mpPreview != nullptr && "no preview data?");
+
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ // propagate change of VisArea through the document's
+ // accessibility tree; this will also send appropriate scroll
+ // events
+ SwAccessibleContext* pDoc =
+ GetContextImpl( GetShell()->GetLayout() ).get();
+ static_cast<SwAccessibleDocumentBase*>( pDoc )->SetVisArea();
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+void SwAccessibleMap::InvalidatePreviewSelection( sal_uInt16 nSelPage )
+{
+ assert(GetShell()->IsPreview());
+ assert(mpPreview != nullptr);
+
+ mpPreview->InvalidateSelection( GetShell()->GetLayout()->GetPageByPageNum( nSelPage ) );
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+bool SwAccessibleMap::IsPageSelected( const SwPageFrame *pPageFrame ) const
+{
+ return mpPreview && mpPreview->GetSelPage() == pPageFrame;
+}
+
+void SwAccessibleMap::FireEvents()
+{
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ if (mpEvents->IsFiring())
+ {
+ return; // prevent recursive FireEvents()
+ }
+
+ mpEvents->SetFiring();
+ mpEvents->MoveMissingXAccToEnd();
+ for( auto const& aEvent : *mpEvents )
+ FireEvent(aEvent);
+
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ }
+ {
+ osl::MutexGuard aGuard( maMutex );
+ mvShapes.clear();
+ }
+
+}
+
+tools::Rectangle SwAccessibleMap::GetVisibleArea() const
+{
+ MapMode aSrc( MapUnit::MapTwip );
+ MapMode aDest( MapUnit::Map100thMM );
+ return OutputDevice::LogicToLogic( GetVisArea().SVRect(), aSrc, aDest );
+}
+
+// Convert a MM100 value relative to the document root into a pixel value
+// relative to the screen!
+Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const
+{
+ MapMode aSrc( MapUnit::Map100thMM );
+ MapMode aDest( MapUnit::MapTwip );
+
+ Point aPoint = OutputDevice::LogicToLogic( rPoint, aSrc, aDest );
+ if (const vcl::Window* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( aPoint, aMapMode );
+ aPoint = pWin->LogicToPixel( aPoint, aMapMode );
+ aPoint = pWin->OutputToAbsoluteScreenPixel( aPoint );
+ }
+
+ return aPoint;
+}
+
+Size SwAccessibleMap::LogicToPixel( const Size& rSize ) const
+{
+ MapMode aSrc( MapUnit::Map100thMM );
+ MapMode aDest( MapUnit::MapTwip );
+ Size aSize( OutputDevice::LogicToLogic( rSize, aSrc, aDest ) );
+ if (const OutputDevice* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( Point(0,0), aMapMode );
+ aSize = pWin->LogicToPixel( aSize, aMapMode );
+ }
+
+ return aSize;
+}
+
+bool SwAccessibleMap::ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const uno::Reference< drawing::XShape >& _rxShape,
+ const long /*_nIndex*/,
+ const ::accessibility::AccessibleShapeTreeInfo& /*_rShapeTreeInfo*/
+ )
+{
+ const SdrObject *pObj = nullptr;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter && !pObj )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ ::accessibility::AccessibleShape *pAccShape =
+ static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
+ if( pAccShape == pCurrentChild )
+ {
+ pObj = (*aIter).first;
+ }
+ ++aIter;
+ }
+ }
+ }
+ if( !pObj )
+ return false;
+
+ uno::Reference < drawing::XShape > xShape( _rxShape ); // keep reference to shape, because
+ // we might be the only one that
+ // holds it.
+ // Also get keep parent.
+ uno::Reference < XAccessible > xParent( pCurrentChild->getAccessibleParent() );
+ pCurrentChild = nullptr; // will be released by dispose
+ A11yDispose( nullptr, pObj, nullptr );
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpShapeMap )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+
+ // create the new child
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+ rtl::Reference< ::accessibility::AccessibleShape> pReplacement(
+ rShapeTypeHandler.CreateAccessibleObject (
+ aShapeInfo, mpShapeMap->GetInfo() ));
+
+ uno::Reference < XAccessible > xAcc( pReplacement.get() );
+ if( xAcc.is() )
+ {
+ pReplacement->Init();
+
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ }
+ }
+
+ SwRect aEmptyRect;
+ InvalidatePosOrSize( nullptr, pObj, nullptr, aEmptyRect );
+
+ return true;
+}
+
+//Get the accessible control shape from the model object, here model object is with XPropertySet type
+::accessibility::AccessibleControlShape * SwAccessibleMap::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter)
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ ::accessibility::AccessibleShape *pAccShape =
+ static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
+ if(pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
+ {
+ ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape);
+ if (pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ ++aIter;
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Reference< XAccessible >
+ SwAccessibleMap::GetAccessibleCaption (const css::uno::Reference< css::drawing::XShape >&)
+{
+ return nullptr;
+}
+
+Point SwAccessibleMap::PixelToCore( const Point& rPoint ) const
+{
+ Point aPoint;
+ if (const OutputDevice* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( rPoint, aMapMode );
+ aPoint = pWin->PixelToLogic( rPoint, aMapMode );
+ }
+ return aPoint;
+}
+
+static long lcl_CorrectCoarseValue(long aCoarseValue, long aFineValue,
+ long aRefValue, bool bToLower)
+{
+ long aResult = aCoarseValue;
+
+ if (bToLower)
+ {
+ if (aFineValue < aRefValue)
+ aResult -= 1;
+ }
+ else
+ {
+ if (aFineValue > aRefValue)
+ aResult += 1;
+ }
+
+ return aResult;
+}
+
+static void lcl_CorrectRectangle(tools::Rectangle & rRect,
+ const tools::Rectangle & rSource,
+ const tools::Rectangle & rInGrid)
+{
+ rRect.SetLeft( lcl_CorrectCoarseValue(rRect.Left(), rSource.Left(),
+ rInGrid.Left(), false) );
+ rRect.SetTop( lcl_CorrectCoarseValue(rRect.Top(), rSource.Top(),
+ rInGrid.Top(), false) );
+ rRect.SetRight( lcl_CorrectCoarseValue(rRect.Right(), rSource.Right(),
+ rInGrid.Right(), true) );
+ rRect.SetBottom( lcl_CorrectCoarseValue(rRect.Bottom(), rSource.Bottom(),
+ rInGrid.Bottom(), true) );
+}
+
+tools::Rectangle SwAccessibleMap::CoreToPixel( const tools::Rectangle& rRect ) const
+{
+ tools::Rectangle aRect;
+ if (const OutputDevice* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( rRect.TopLeft(), aMapMode );
+ aRect = pWin->LogicToPixel( rRect, aMapMode );
+
+ tools::Rectangle aTmpRect = pWin->PixelToLogic( aRect, aMapMode );
+ lcl_CorrectRectangle(aRect, rRect, aTmpRect);
+ }
+
+ return aRect;
+}
+
+/** get mapping mode for LogicToPixel and PixelToLogic conversions
+
+ Method returns mapping mode of current output device and adjusts it,
+ if the shell is in page/print preview.
+ Necessary, because <PreviewAdjust(..)> changes mapping mode at current
+ output device for mapping logic document positions to page preview window
+ positions and vice versa and doesn't take care to recover its changes.
+*/
+void SwAccessibleMap::GetMapMode( const Point& _rPoint,
+ MapMode& _orMapMode ) const
+{
+ MapMode aMapMode = GetShell()->GetWin()->GetMapMode();
+ if( GetShell()->IsPreview() )
+ {
+ assert(mpPreview != nullptr);
+ mpPreview->AdjustMapMode( aMapMode, _rPoint );
+ }
+ _orMapMode = aMapMode;
+}
+
+Size SwAccessibleMap::GetPreviewPageSize(sal_uInt16 const nPreviewPageNum) const
+{
+ assert(mpVSh->IsPreview());
+ assert(mpPreview != nullptr);
+ return mpVSh->PagePreviewLayout()->GetPreviewPageSizeByPageNum(nPreviewPageNum);
+}
+
+/** method to build up a new data structure of the accessible paragraphs,
+ which have a selection
+ Important note: method has to be used inside a mutual exclusive section
+*/
+std::unique_ptr<SwAccessibleSelectedParas_Impl> SwAccessibleMap::BuildSelectedParas()
+{
+ // no accessible contexts, no selection
+ if ( !mpFrameMap )
+ {
+ return nullptr;
+ }
+
+ // get cursor as an instance of its base class <SwPaM>
+ SwPaM* pCursor( nullptr );
+ {
+ SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>(GetShell());
+ if ( pCursorShell )
+ {
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ if ( !pFEShell ||
+ ( !pFEShell->IsFrameSelected() &&
+ pFEShell->IsObjSelected() == 0 ) )
+ {
+ // get cursor without updating an existing table cursor.
+ pCursor = pCursorShell->GetCursor( false );
+ }
+ }
+ }
+ // no cursor, no selection
+ if ( !pCursor )
+ {
+ return nullptr;
+ }
+
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pRetSelectedParas;
+
+ // loop on all cursors
+ SwPaM* pRingStart = pCursor;
+ do {
+
+ // for a selection the cursor has to have a mark.
+ // for safety reasons assure that point and mark are in text nodes
+ if ( pCursor->HasMark() &&
+ pCursor->GetPoint()->nNode.GetNode().IsTextNode() &&
+ pCursor->GetMark()->nNode.GetNode().IsTextNode() )
+ {
+ SwPosition* pStartPos = pCursor->Start();
+ SwPosition* pEndPos = pCursor->End();
+ // loop on all text nodes inside the selection
+ SwNodeIndex aIdx( pStartPos->nNode );
+ for ( ; aIdx.GetIndex() <= pEndPos->nNode.GetIndex(); ++aIdx )
+ {
+ SwTextNode* pTextNode( aIdx.GetNode().GetTextNode() );
+ if ( pTextNode )
+ {
+ // loop on all text frames registered at the text node.
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
+ for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame; pTextFrame = aIter.Next() )
+ {
+ uno::WeakReference < XAccessible > xWeakAcc;
+ SwAccessibleContextMap_Impl::iterator aMapIter =
+ mpFrameMap->find( pTextFrame );
+ if( aMapIter != mpFrameMap->end() )
+ {
+ xWeakAcc = (*aMapIter).second;
+ SwAccessibleParaSelection aDataEntry(
+ sw::FrameContainsNode(*pTextFrame, pStartPos->nNode.GetIndex())
+ ? pTextFrame->MapModelToViewPos(*pStartPos)
+ : TextFrameIndex(0),
+
+ sw::FrameContainsNode(*pTextFrame, pEndPos->nNode.GetIndex())
+ ? pTextFrame->MapModelToViewPos(*pEndPos)
+ : TextFrameIndex(COMPLETE_STRING));
+ if ( !pRetSelectedParas )
+ {
+ pRetSelectedParas.reset(
+ new SwAccessibleSelectedParas_Impl);
+ }
+ // sw_redlinehide: should be idempotent for multiple nodes in a merged para
+ pRetSelectedParas->emplace( xWeakAcc, aDataEntry );
+ }
+ }
+ }
+ }
+ }
+
+ // prepare next turn: get next cursor in ring
+ pCursor = pCursor->GetNext();
+ } while ( pCursor != pRingStart );
+
+ return pRetSelectedParas;
+}
+
+void SwAccessibleMap::InvalidateTextSelectionOfAllParas()
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ // keep previously known selected paragraphs
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pPrevSelectedParas( std::move(mpSelectedParas) );
+
+ // determine currently selected paragraphs
+ mpSelectedParas = BuildSelectedParas();
+
+ // compare currently selected paragraphs with the previously selected
+ // paragraphs and submit corresponding TEXT_SELECTION_CHANGED events.
+ // first, search for new and changed selections.
+ // on the run remove selections from previously known ones, if they are
+ // also in the current ones.
+ if ( mpSelectedParas )
+ {
+ SwAccessibleSelectedParas_Impl::iterator aIter = mpSelectedParas->begin();
+ for ( ; aIter != mpSelectedParas->end(); ++aIter )
+ {
+ bool bSubmitEvent( false );
+ if ( !pPrevSelectedParas )
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ else
+ {
+ SwAccessibleSelectedParas_Impl::iterator aPrevSelected =
+ pPrevSelectedParas->find( (*aIter).first );
+ if ( aPrevSelected != pPrevSelectedParas->end() )
+ {
+ // check, if selection has changed
+ if ( (*aIter).second.nStartOfSelection !=
+ (*aPrevSelected).second.nStartOfSelection ||
+ (*aIter).second.nEndOfSelection !=
+ (*aPrevSelected).second.nEndOfSelection )
+ {
+ // changed selection
+ bSubmitEvent = true;
+ }
+ pPrevSelectedParas->erase( aPrevSelected );
+ }
+ else
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ }
+
+ if ( bSubmitEvent )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).first );
+ if ( xAcc.is() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xAcc.get() ) );
+ if ( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame(
+ dynamic_cast<const SwTextFrame*>(xAccImpl->GetFrame()) );
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // second, handle previous selections - after the first step the data
+ // structure of the previously known only contains the 'old' selections
+ if ( pPrevSelectedParas )
+ {
+ SwAccessibleSelectedParas_Impl::iterator aIter = pPrevSelectedParas->begin();
+ for ( ; aIter != pPrevSelectedParas->end(); ++aIter )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).first );
+ if ( xAcc.is() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xAcc.get() ) );
+ if ( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame(
+ dynamic_cast<const SwTextFrame*>(xAccImpl->GetFrame()) );
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+ }
+ }
+}
+
+const SwRect& SwAccessibleMap::GetVisArea() const
+{
+ assert(!GetShell()->IsPreview() || (mpPreview != nullptr));
+
+ return GetShell()->IsPreview()
+ ? mpPreview->GetVisArea()
+ : GetShell()->VisArea();
+}
+
+bool SwAccessibleMap::IsDocumentSelAll()
+{
+ return GetShell()->GetDoc()->IsPrepareSelAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */