/* -*- 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 .
*/
// right now, we're only handling the specific event necessary to fix this performance problem if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
{ auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
std::unique_lock aGuard(maListenerMutex); auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape); for (auto it = itBegin; it != itEnd; ++it)
it->second->notifyShapeEvent(aEvent);
}
}
private:
SwRect maOldBox; // the old bounds for CHILD_POS_CHANGED // and POS_CHANGED
unotools::WeakReference < SwAccessibleContext > mxAcc; // The object that fires the event
SwAccessibleChild maFrameOrObj; // the child for CHILD_POS_CHANGED and // the same as xAcc for any other // event type
EventType meType; // The event type
AccessibleStates mnStates; // check states or update caret pos
public: const SwFrame* mpParentFrame; // The object that fires the event bool IsNoXaccParentFrame() const
{ return CHILD_POS_CHANGED == meType && mpParentFrame != nullptr;
}
public:
SwAccessibleEvent_Impl( EventType eT,
SwAccessibleContext *pA,
SwAccessibleChild aFrameOrObj )
: mxAcc( pA ),
maFrameOrObj(std::move( aFrameOrObj )),
meType( eT ),
mnStates( AccessibleStates::NONE ),
mpParentFrame( nullptr )
{}
// 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
*/ staticvoid AdjustLogicPgRectToVisibleArea( SwRect& _iorLogicPgSwRect, const SwRect& _rPreviewPgSwRect, const Size& _rPreviewWinSize );
/** 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;
// loop on preview pages to calculate <maPreviewRects>, <maLogicRects> and // <maVisArea> for ( auto & rpPreviewPage : _rPreviewPages )
{
aPage = rpPreviewPage->pPage;
if( pFrame && pFrame->IsCellFrame() && rAcc.is() )
{ // Is it in the same table? We check that // by comparing the last table frame in the // follow chain, because that's cheaper than // searching the first one. if( rAcc->GetFrame()->IsCellFrame() )
{ const SwTabFrame *pTabFrame1 = rAcc->GetFrame()->FindTabFrame(); if (pTabFrame1)
{ while (pTabFrame1->GetFollow())
pTabFrame1 = pTabFrame1->GetFollow();
}
const SwTabFrame *pTabFrame2 = pFrame->FindTabFrame(); if (pTabFrame2)
{ while (pTabFrame2->GetFollow())
pTabFrame2 = pTabFrame2->GetFollow();
}
bRet = (pTabFrame1 == pTabFrame2);
}
}
return bRet;
}
void SwAccessibleMap::FireEvent( const SwAccessibleEvent_Impl& rEvent )
{
::rtl::Reference < SwAccessibleContext > xAccImpl( rEvent.GetContext() ); if (!xAccImpl.is() && rEvent.mpParentFrame != nullptr)
{
SwAccessibleContextMap::iterator aIter = maFrameMap.find(rEvent.mpParentFrame); if (aIter != maFrameMap.end())
{
rtl::Reference < SwAccessibleContext > xContext( (*aIter).second.get() ); if (xContext.is() && (xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH
|| xContext->getAccessibleRole() == AccessibleRole::BLOCK_QUOTE))
{
xAccImpl = xContext.get();
}
}
} if( SwAccessibleEvent_Impl::SHAPE_SELECTION == rEvent.GetType() )
{
DoInvalidateShapeSelection();
} elseif( 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();
}
}
}
}
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 ); elseif ( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR )
{
aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
aEvent.SetStates( rEvent.GetAllStates() );
}
break; case SwAccessibleEvent_Impl::POS_CHANGED: // A pos changed event overwrites CARET_STATES (keeping its // flags) as well as INVALID_CONTENT. The old box position // has to be stored however if the old event is not a // POS_CHANGED itself.
OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED, "invalid event combination" ); if( aEvent.GetType() != SwAccessibleEvent_Impl::POS_CHANGED )
aEvent.SetOldBox( rEvent.GetOldBox() );
aEvent.SetType( SwAccessibleEvent_Impl::POS_CHANGED ); break; case SwAccessibleEvent_Impl::CHILD_POS_CHANGED: // CHILD_POS_CHANGED events can only follow CHILD_POS_CHANGED // events. The only action that needs to be done again is // to put the old event to the back. The new one cannot be used, // because we are interested in the old frame bounds.
OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::CHILD_POS_CHANGED, "invalid event combination" ); break; case SwAccessibleEvent_Impl::SHAPE_SELECTION:
OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::SHAPE_SELECTION, "invalid event combination" ); break; case SwAccessibleEvent_Impl::DISPOSE: // DISPOSE events overwrite all others. They are not stored // but executed immediately to avoid broadcasting of // nonfunctional objects. So what needs to be done here is to // remove all events for the frame in question.
bAppendEvent = false; break; case SwAccessibleEvent_Impl::INVALID_ATTR: // tdf#150708 if the old is CARET_OR_STATES then try updating it // with the additional states if (aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES)
aEvent.SetStates(rEvent.GetAllStates()); else
{
OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR, "invalid event combination" );
} break;
} if( bAppendEvent )
{
mpEvents->erase( (*aIter).second );
(*aIter).second = mpEvents->insert( mpEvents->end(), aEvent );
} else
{
mpEvents->erase( (*aIter).second );
mpEventMap->erase( aIter );
}
} elseif( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
{
mpEventMap->emplace( rEvent.GetFrameOrObj(),
mpEvents->insert( mpEvents->end(), rEvent ) );
}
}
}
void SwAccessibleMap::InvalidateCursorPosition(const rtl::Reference<SwAccessibleContext>& rxAcc)
{
assert(rxAcc.is());
assert(rxAcc->GetFrame()); if (GetShell().ActionPend())
{
SwAccessibleEvent_Impl aEvent(SwAccessibleEvent_Impl::CARET_OR_STATES, rxAcc.get(),
SwAccessibleChild(rxAcc->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 (rxAcc->GetFrame())
rxAcc->InvalidateCursorPos();
}
}
//This method should implement the following functions: //1.find the shape objects and set the selected state. //2.find the Swframe objects and set the selected state. //3.find the paragraph objects and set the selected state. void SwAccessibleMap::InvalidateShapeInParaSelection()
{
DBG_TESTSOLARMUTEX();
//when InvalidateFocus Call this function ,and the current selected shape count is not 1 , //return if (bInvalidateFocusMode && nSelShapes != 1)
{ return;
} if( mpShapeMap )
pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
assert(maFrameMap.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");
mpShapeMap.reset();
mvShapes.clear();
mpSelectedParas.reset();
if (xAcc->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.
rtl::Reference<SwAccessibleContext> xOldCursorAcc = mxCursorContext;
mxCursorContext = xAcc.get();
// Invalidate focus for old object when map is not locked if (xOldCursorAcc.is())
InvalidateCursorPosition(xOldCursorAcc); if (bOldShapeSelected)
InvalidateShapeSelection();
}
//Added by yanjun for sym2_6407 void SwAccessibleMap::RemoveGroupContext(const SdrObject *pParentObj)
{
DBG_TESTSOLARMUTEX();
// TODO: Why are sub-shapes of group shapes even added to our map? // Doesn't the AccessibleShape of the top-level shape create them // on demand anyway? Why does SwAccessibleMap need to know them? // We cannot rely on getAccessibleChild here to remove the sub-shapes // from mpShapes because the top-level shape may not only be disposed here // but also by visibility checks in svx, then it doesn't return children. if (mpShapeMap && pParentObj && pParentObj->IsGroupObject())
{ if (SdrObjList *const pChildren = pParentObj->GetSubList()) for (const rtl::Reference<SdrObject>& pChild : *pChildren)
{
assert(pChild);
RemoveContext(pChild.get());
}
}
} //End
SwAccessibleContextMap::iterator aIter = maFrameMap.find(pFrame); if (aIter == maFrameMap.end()) return;
maFrameMap.erase(aIter);
SwAccessibleContextMap::iterator aSelectedIter = maSelectedFrameMap.find(pFrame); if (aSelectedIter != maSelectedFrameMap.end())
maSelectedFrameMap.erase(aSelectedIter);
// Remove reference to old caret object. Though mxCursorContext // is a weak reference and cleared automatically, clearing it // directly makes sure to not keep a non-functional object.
rtl::Reference < SwAccessibleContext > xOldAcc( mxCursorContext ); if( xOldAcc.is() )
{
SwAccessibleContext *pOldAccImpl = xOldAcc.get();
OSL_ENSURE( pOldAccImpl->GetFrame(), "old caret context is disposed" ); if( pOldAccImpl->GetFrame() == pFrame )
{
xOldAcc.clear(); // get an empty ref
mxCursorContext = xOldAcc.get();
}
}
}
rtl::Reference < ::accessibility::AccessibleShape > xTempHold( (*aIter).second );
mpShapeMap->erase( aIter );
RemoveGroupContext(pObj); // The shape selection flag is not cleared, but one might do // so but has to make sure that the removed context is the one // that is selected.
// Indeed, the following assert checks the frame's accessible flag, // because that's the one that is evaluated in the layout. The frame // might not be accessible anyway. That's the case for cell frames that // contain further cells.
OSL_ENSURE( !aFrameOrObj.GetSwFrame() || aFrameOrObj.GetSwFrame()->IsAccessibleFrame(), "non accessible frame should be disposed" );
if (!(aFrameOrObj.IsAccessible(GetShell().IsPreview()) // fdo#87199 dispose the darn thing if it ever was accessible
|| Contains(pFrame))) return;
// get accessible context for frame // First of all look for an accessible context for a frame if (aFrameOrObj.GetSwFrame())
{
SwAccessibleContextMap::iterator aIter = maFrameMap.find(aFrameOrObj.GetSwFrame()); if (aIter != maFrameMap.end())
xAccImpl = (*aIter).second;
} if (!xAccImpl.is())
{ // If there is none, look if the parent is accessible. const SwFrame *pParent =
SwAccessibleFrame::GetParent( aFrameOrObj,
GetShell().IsPreview());
if( pParent )
{
SwAccessibleContextMap::iterator aIter = maFrameMap.find(pParent); if (aIter != maFrameMap.end())
xParentAccImpl = (*aIter).second;
}
} if( !xParentAccImpl.is() && !aFrameOrObj.GetSwFrame() && mpShapeMap )
{
SwAccessibleShapeMap_Impl::iterator aIter =
mpShapeMap->find( aFrameOrObj.GetDrawObject() ); if( aIter != mpShapeMap->end() )
{
xShapeAccImpl = aIter->second;
}
} if (pObj && GetShell().ActionPend() &&
(xParentAccImpl.is() || xShapeAccImpl.is()) )
{ // Keep a reference to the XShape to avoid that it // is deleted with a SwFrameFormat::SwClientNotify.
uno::Reference < drawing::XShape > xShape( const_cast< SdrObject * >( pObj )->getUnoShape(),
uno::UNO_QUERY ); if( xShape.is() )
{
mvShapes.push_back( xShape );
}
}
// 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 );
} elseif( 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.
SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow ); if (!aFrameOrObj.IsAccessible(GetShell().IsPreview())) return;
::rtl::Reference< SwAccessibleContext > xAccImpl;
::rtl::Reference< SwAccessibleContext > xParentAccImpl; const SwFrame *pParent =nullptr; if (aFrameOrObj.GetSwFrame())
{
SwAccessibleContextMap::iterator aIter = maFrameMap.find(aFrameOrObj.GetSwFrame()); if (aIter != maFrameMap.end())
{ // If there is an accessible object already it is // notified directly.
xAccImpl = (*aIter).second;
}
} if (!xAccImpl.is())
{ // Otherwise we look if the parent is accessible. // If not, there is nothing to do.
pParent =
SwAccessibleFrame::GetParent( aFrameOrObj,
GetShell().IsPreview());
if (pParent)
{
SwAccessibleContextMap::iterator aIter = maFrameMap.find(pParent); if (aIter != maFrameMap.end())
xParentAccImpl = (*aIter).second;
}
}
if( xAccImpl.is() )
{ if (GetShell().ActionPend())
{
SwAccessibleEvent_Impl aEvent(
SwAccessibleEvent_Impl::POS_CHANGED, xAccImpl.get(),
std::move(aFrameOrObj), rOldBox );
AppendEvent( aEvent );
} else
{
FireEvents(); if (xAccImpl->GetFrame()) // not if disposed by FireEvents()
{
xAccImpl->InvalidatePosOrSize(rOldBox);
}
}
} elseif( 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(), std::move(aFrameOrObj), rOldBox );
AppendEvent( aEvent );
}
} else
{
FireEvents();
xParentAccImpl->InvalidateChildPosOrSize( aFrameOrObj,
rOldBox );
}
} elseif(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;
}
} elseif(pObj)
{ if (SwFrameType::Txt == pParent->GetType())
{
bIsTextParent =true;
}
} if( bIsValidFrame || bIsTextParent )
{ if (GetShell().ActionPend())
{
SwAccessibleEvent_Impl aEvent(
SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
pParent, std::move(aFrameOrObj), rOldBox );
AppendEvent( aEvent );
} else
{
OSL_ENSURE(false,"");
}
}
}
}
// 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 (pAccFrame->IsCellFrame())
{ if (xOldAcc.is() && AreInSameTable(xOldAcc, pAccFrame))
{ if( xAcc.is() )
xOldAcc = xAcc; // avoid extra invalidation else
xAcc = xOldAcc; // make sure at least one
} if( !xAcc.is() )
xAcc = GetContextImpl(pAccFrame);
}
} elseif (bShapeSelected)
{ const SwFEShell* pFESh = static_cast<const SwFEShell*>(&rVSh); 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.
void SwAccessibleMap::InvalidateEditableStates( const SwFrame* _pFrame )
{ // Start with the frame or the first upper that is accessible const SwFrame* pAccFrame = _pFrame; while (pAccFrame && !SwAccessibleChild::IsFrameAccessible(*pAccFrame, GetShell().IsPreview()))
pAccFrame = pAccFrame->GetUpper(); if (!pAccFrame)
pAccFrame = GetShell().GetLayout();
// first, see if this frame is accessible, and if so, get the respective if (!SwAccessibleChild::IsFrameAccessible(rFrame, GetShell().IsPreview())) return;
// invalidation of CONTENT_FLOW_FROM/_TO relation of a paragraph void SwAccessibleMap::InvalidateParaFlowRelation( const SwTextFrame& _rTextFrame, constbool _bFrom )
{
InvalidateRelationSet_(_rTextFrame, _bFrom );
}
// invalidation of text selection of a paragraph void SwAccessibleMap::InvalidateParaTextSelection( const SwTextFrame& _rTextFrame )
{
DBG_TESTSOLARMUTEX();
// first, see if this frame is accessible, and if so, get the respective if (!SwAccessibleChild::IsFrameAccessible(_rTextFrame, GetShell().IsPreview())) return;
// 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();
// Convert a MM100 value relative to the document root into a pixel value // relative to the screen!
Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const
{
Point aPoint = o3tl::toTwips( rPoint, o3tl::Length::mm100 ); if (const vcl::Window* pWin = GetShell().GetWin())
{ const MapMode aMapMode = GetMapMode(aPoint);
aPoint = pWin->LogicToPixel( aPoint, aMapMode );
aPoint = Point(pWin->OutputToAbsoluteScreenPixel( aPoint ));
}
uno::Reference < drawing::XShape > xShape( _rxShape ); // keep reference to shape, because // we might be the only one that // holds it. // Also get keep parent.
uno::Reference < XAccessible > xParent( pCurrentChild->getAccessibleParent() );
pCurrentChild = nullptr; // will be released by dispose
A11yDispose( nullptr, pObj, nullptr );
if( !mpShapeMap )
mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
// create the new child
::accessibility::ShapeTypeHandler& rShapeTypeHandler =
::accessibility::ShapeTypeHandler::Instance();
::accessibility::AccessibleShapeInfo aShapeInfo(
xShape, xParent, this );
rtl::Reference< ::accessibility::AccessibleShape> pReplacement(
rShapeTypeHandler.CreateAccessibleObject (
aShapeInfo, mpShapeMap->GetInfo() ));
//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 )
{ for (constauto& rIter : *mpShapeMap)
{
rtl::Reference<::accessibility::AccessibleShape> xAcc(rIter.second); if(xAcc && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (xAcc->GetXShape()) == ::accessibility::DRAWING_CONTROL)
{
::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(xAcc.get()); if (pCtlAccShape->GetControlModel() == pSet) return pCtlAccShape;
}
}
} return nullptr;
}
/** 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.
*/
MapMode SwAccessibleMap::GetMapMode(const Point& _rPoint) const
{
MapMode aMapMode = GetShell().GetWin()->GetMapMode(); if ( GetShell().IsPreview())
{
assert(mpPreview != nullptr);
mpPreview->AdjustMapMode( aMapMode, _rPoint );
} return aMapMode;
}
/** 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 (maFrameMap.empty()) 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->GetSelectedObjCount() == 0 ) )
{ // get cursor without updating an existing table cursor.
pCursor = pCursorShell->GetCursor( false );
}
}
} // no cursor, no selection if ( !pCursor )
{ return nullptr;
}
// loop on all cursors
SwPaM* pRingStart = pCursor; do {
// for a selection the cursor has to have a mark. // for safety reasons assure that point and mark are in text nodes if ( pCursor->HasMark() &&
pCursor->GetPoint()->GetNode().IsTextNode() &&
pCursor->GetMark()->GetNode().IsTextNode() )
{ auto [pStartPos, pEndPos] = pCursor->StartEnd(); // SwPosition* // loop on all text nodes inside the selection
SwNodeIndex aIdx( pStartPos->GetNode() ); for ( ; aIdx.GetIndex() <= pEndPos->GetNodeIndex(); ++aIdx )
{
SwTextNode* pTextNode( aIdx.GetNode().GetTextNode() ); if ( pTextNode )
{ // loop on all text frames registered at the text node.
SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode); for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame; pTextFrame = aIter.Next() )
{
unotools::WeakReference < SwAccessibleContext > xWeakAcc;
SwAccessibleContextMap::iterator aMapIter = maFrameMap.find(pTextFrame); if (aMapIter != maFrameMap.end())
{
xWeakAcc = (*aMapIter).second;
SwAccessibleParaSelection aDataEntry(
sw::FrameContainsNode(*pTextFrame, pStartPos->GetNodeIndex())
? pTextFrame->MapModelToViewPos(*pStartPos)
: TextFrameIndex(0),
sw::FrameContainsNode(*pTextFrame, pEndPos->GetNodeIndex())
? pTextFrame->MapModelToViewPos(*pEndPos)
: TextFrameIndex(COMPLETE_STRING)); if ( !pRetSelectedParas )
{
pRetSelectedParas.reset( new SwAccessibleSelectedParas_Impl);
} // sw_redlinehide: should be idempotent for multiple nodes in a merged para
pRetSelectedParas->emplace( xWeakAcc, aDataEntry );
}
}
}
}
}
// prepare next turn: get next cursor in ring
pCursor = pCursor->GetNext();
} while ( pCursor != pRingStart );
// 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 )
{ for (constauto& rIter : *mpSelectedParas)
{ bool bSubmitEvent( false ); if ( !pPrevSelectedParas )
{ // new selection
bSubmitEvent = true;
} else
{
SwAccessibleSelectedParas_Impl::iterator aPrevSelected
= pPrevSelectedParas->find(rIter.first); if ( aPrevSelected != pPrevSelectedParas->end() )
{ // check, if selection has changed if (rIter.second.nStartOfSelection != (*aPrevSelected).second.nStartOfSelection
|| rIter.second.nEndOfSelection != (*aPrevSelected).second.nEndOfSelection)
{ // changed selection
bSubmitEvent = true;
}
pPrevSelectedParas->erase( aPrevSelected );
} else
{ // new selection
bSubmitEvent = true;
}
}
if ( bSubmitEvent )
{
rtl::Reference<SwAccessibleContext> xAcc(rIter.first); if ( xAcc.is() && xAcc->GetFrame() )
{ const SwTextFrame* pTextFrame = xAcc->GetFrame()->DynCastTextFrame();
OSL_ENSURE( pTextFrame, "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" ); if ( pTextFrame )
{
InvalidateParaTextSelection( *pTextFrame );
}
}
}
}
}
// second, handle previous selections - after the first step the data // structure of the previously known only contains the 'old' selections if ( !pPrevSelectedParas ) return;
for (constauto& rIter : *pPrevSelectedParas)
{
rtl::Reference<SwAccessibleContext> xAcc(rIter.first); if ( xAcc.is() && xAcc->GetFrame() )
{ const SwTextFrame* pTextFrame = xAcc->GetFrame()->DynCastTextFrame();
OSL_ENSURE( pTextFrame, "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" ); if ( pTextFrame )
{
InvalidateParaTextSelection( *pTextFrame );
}
}
}
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.45Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-07)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.