bump product version to 7.2.5.1
[LibreOffice.git] / svx / source / accessibility / AccessibleShape.cxx
blob28cb6b7b768fe2611a871f5b05c5ab8661b1893f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/AccessibleShape.hxx>
21 #include <svx/AccessibleShapeInfo.hxx>
22 #include <com/sun/star/accessibility/AccessibleRole.hpp>
23 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
24 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
25 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <com/sun/star/drawing/XShapes.hpp>
28 #include <com/sun/star/document/XShapeEventBroadcaster.hpp>
29 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
30 #include <com/sun/star/drawing/FillStyle.hpp>
31 #include <com/sun/star/text/XText.hpp>
32 #include <sal/log.hxx>
33 #include <editeng/unoedsrc.hxx>
34 #include <svx/AccessibleTextHelper.hxx>
35 #include <svx/ChildrenManager.hxx>
36 #include <svx/IAccessibleParent.hxx>
37 #include <svx/IAccessibleViewForwarder.hxx>
38 #include <svx/unoshtxt.hxx>
39 #include <svx/svdobj.hxx>
40 #include <svx/unoapi.hxx>
41 #include <svx/svdpage.hxx>
42 #include <svx/ShapeTypeHandler.hxx>
43 #include <svx/SvxShapeTypes.hxx>
45 #include <vcl/svapp.hxx>
46 #include <vcl/window.hxx>
47 #include <unotools/accessiblestatesethelper.hxx>
48 #include <unotools/accessiblerelationsethelper.hxx>
49 #include <svx/svdview.hxx>
50 #include <tools/diagnose_ex.h>
51 #include <cppuhelper/queryinterface.hxx>
52 #include <comphelper/sequence.hxx>
53 #include "AccessibleEmptyEditSource.hxx"
55 #include <algorithm>
56 #include <memory>
58 using namespace ::com::sun::star;
59 using namespace ::com::sun::star::accessibility;
60 using ::com::sun::star::uno::Reference;
61 using ::com::sun::star::lang::IndexOutOfBoundsException;
62 using ::com::sun::star::uno::RuntimeException;
64 namespace accessibility {
66 namespace {
68 OUString GetOptionalProperty (
69 const Reference<beans::XPropertySet>& rxSet,
70 const OUString& rsPropertyName)
72 OUString sValue;
74 if (rxSet.is())
76 const Reference<beans::XPropertySetInfo> xInfo (rxSet->getPropertySetInfo());
77 if ( ! xInfo.is() || xInfo->hasPropertyByName(rsPropertyName))
79 try
81 rxSet->getPropertyValue(rsPropertyName) >>= sValue;
83 catch (beans::UnknownPropertyException&)
85 // This exception should only be thrown when the property
86 // does not exits (of course) and the XPropertySetInfo is
87 // not available.
91 return sValue;
94 } // end of anonymous namespace
96 // internal
97 AccessibleShape::AccessibleShape (
98 const AccessibleShapeInfo& rShapeInfo,
99 const AccessibleShapeTreeInfo& rShapeTreeInfo)
100 : AccessibleContextBase (rShapeInfo.mxParent,AccessibleRole::SHAPE),
101 mxShape (rShapeInfo.mxShape),
102 maShapeTreeInfo (rShapeTreeInfo),
103 m_nIndexInParent(-1),
104 mpParent (rShapeInfo.mpChildrenManager)
106 m_pShape = GetSdrObjectFromXShape(mxShape);
107 UpdateNameAndDescription();
110 AccessibleShape::~AccessibleShape()
112 mpChildrenManager.reset();
113 mpText.reset();
114 SAL_INFO("svx", "~AccessibleShape");
116 // Unregistering from the various broadcasters should be unnecessary
117 // since this destructor would not have been called if one of the
118 // broadcasters would still hold a strong reference to this object.
121 void AccessibleShape::Init()
123 // Update the OPAQUE and SELECTED shape.
124 UpdateStates ();
126 // Create a children manager when this shape has children of its own.
127 Reference<drawing::XShapes> xShapes (mxShape, uno::UNO_QUERY);
128 if (xShapes.is() && xShapes->getCount() > 0)
129 mpChildrenManager.reset( new ChildrenManager (
130 this, xShapes, maShapeTreeInfo, *this) );
131 if (mpChildrenManager != nullptr)
132 mpChildrenManager->Update();
134 // Register at model as document::XEventListener.
135 if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
136 maShapeTreeInfo.GetModelBroadcaster()->addShapeEventListener(mxShape,
137 static_cast<document::XShapeEventListener*>(this));
139 // Beware! Here we leave the paths of the UNO API and descend into the
140 // depths of the core. Necessary for making the edit engine
141 // accessible.
142 Reference<text::XText> xText (mxShape, uno::UNO_QUERY);
143 if (!xText.is())
144 return;
146 SdrView* pView = maShapeTreeInfo.GetSdrView ();
147 const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
148 if (!(pView != nullptr && pWindow != nullptr && mxShape.is()))
149 return;
151 // #107948# Determine whether shape text is empty
152 SdrObject* pSdrObject = GetSdrObjectFromXShape(mxShape);
153 if( !pSdrObject )
154 return;
156 SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pSdrObject );
157 const bool hasOutlinerParaObject = (pTextObj && pTextObj->CanCreateEditOutlinerParaObject()) || (pSdrObject->GetOutlinerParaObject() != nullptr);
159 // create AccessibleTextHelper to handle this shape's text
160 if( !hasOutlinerParaObject )
162 // empty text -> use proxy edit source to delay creation of EditEngine
163 mpText.reset( new AccessibleTextHelper( std::make_unique<AccessibleEmptyEditSource >(*pSdrObject, *pView, *pWindow->GetOutDev()) ) );
165 else
167 // non-empty text -> use full-fledged edit source right away
168 mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource >(*pSdrObject, nullptr, *pView, *pWindow->GetOutDev()) ) );
170 if( pWindow->HasFocus() )
171 mpText->SetFocus();
173 mpText->SetEventSource(this);
177 void AccessibleShape::UpdateStates()
179 ::utl::AccessibleStateSetHelper* pStateSet =
180 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
181 if (pStateSet == nullptr)
182 return;
184 // Set the opaque state for certain shape types when their fill style is
185 // solid.
186 bool bShapeIsOpaque = false;
187 switch (ShapeTypeHandler::Instance().GetTypeId (mxShape))
189 case DRAWING_PAGE:
190 case DRAWING_RECTANGLE:
191 case DRAWING_TEXT:
193 uno::Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY);
194 if (xSet.is())
198 drawing::FillStyle aFillStyle;
199 bShapeIsOpaque = ( xSet->getPropertyValue ("FillStyle") >>= aFillStyle)
200 && aFillStyle == drawing::FillStyle_SOLID;
202 catch (css::beans::UnknownPropertyException&)
204 // Ignore.
209 if (bShapeIsOpaque)
210 pStateSet->AddState (AccessibleStateType::OPAQUE);
211 else
212 pStateSet->RemoveState (AccessibleStateType::OPAQUE);
214 // Set the selected state.
215 bool bShapeIsSelected = false;
216 // XXX fix_me this has to be done with an extra interface later on
217 if ( m_pShape && maShapeTreeInfo.GetSdrView() )
219 bShapeIsSelected = maShapeTreeInfo.GetSdrView()->IsObjMarked(m_pShape);
222 if (bShapeIsSelected)
223 pStateSet->AddState (AccessibleStateType::SELECTED);
224 else
225 pStateSet->RemoveState (AccessibleStateType::SELECTED);
228 OUString AccessibleShape::GetStyle()
230 return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
233 bool AccessibleShape::SetState (sal_Int16 aState)
235 bool bStateHasChanged = false;
237 if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
239 // Offer FOCUSED state to edit engine and detect whether the state
240 // changes.
241 bool bIsFocused = mpText->HaveFocus ();
242 mpText->SetFocus();
243 bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
245 else
246 bStateHasChanged = AccessibleContextBase::SetState (aState);
248 return bStateHasChanged;
252 bool AccessibleShape::ResetState (sal_Int16 aState)
254 bool bStateHasChanged = false;
256 if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
258 // Try to remove FOCUSED state from the edit engine and detect
259 // whether the state changes.
260 bool bIsFocused = mpText->HaveFocus ();
261 mpText->SetFocus (false);
262 bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
264 else
265 bStateHasChanged = AccessibleContextBase::ResetState (aState);
267 return bStateHasChanged;
271 bool AccessibleShape::GetState (sal_Int16 aState)
273 if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
275 // Just delegate the call to the edit engine. The state is not
276 // merged into the state set.
277 return mpText->HaveFocus();
279 else
280 return AccessibleContextBase::GetState (aState);
283 // OverWrite the parent's getAccessibleName method
284 OUString SAL_CALL AccessibleShape::getAccessibleName()
286 ThrowIfDisposed ();
287 if (m_pShape && !m_pShape->GetTitle().isEmpty())
288 return CreateAccessibleName() + " " + m_pShape->GetTitle();
289 else
290 return CreateAccessibleName();
293 OUString SAL_CALL AccessibleShape::getAccessibleDescription()
295 ThrowIfDisposed ();
296 if( m_pShape && !m_pShape->GetDescription().isEmpty())
297 return m_pShape->GetDescription() ;
298 else
299 return " ";
302 // XAccessibleContext
303 /** The children of this shape come from two sources: The children from
304 group or scene shapes and the paragraphs of text.
306 sal_Int32 SAL_CALL
307 AccessibleShape::getAccessibleChildCount ()
309 if (IsDisposed())
311 return 0;
314 sal_Int32 nChildCount = 0;
316 // Add the number of shapes that are children of this shape.
317 if (mpChildrenManager != nullptr)
318 nChildCount += mpChildrenManager->GetChildCount ();
319 // Add the number text paragraphs.
320 if (mpText != nullptr)
321 nChildCount += mpText->GetChildCount ();
323 return nChildCount;
327 /** Forward the request to the shape. Return the requested shape or throw
328 an exception for a wrong index.
330 uno::Reference<XAccessible> SAL_CALL
331 AccessibleShape::getAccessibleChild (sal_Int32 nIndex)
333 ThrowIfDisposed ();
335 uno::Reference<XAccessible> xChild;
337 // Depending on the index decide whether to delegate this call to the
338 // children manager or the edit engine.
339 if ((mpChildrenManager != nullptr)
340 && (nIndex < mpChildrenManager->GetChildCount()))
342 xChild = mpChildrenManager->GetChild (nIndex);
344 else if (mpText != nullptr)
346 sal_Int32 nI = nIndex;
347 if (mpChildrenManager != nullptr)
348 nI -= mpChildrenManager->GetChildCount();
349 xChild = mpText->GetChild (nI);
351 else
352 throw lang::IndexOutOfBoundsException (
353 "shape has no child with index " + OUString::number(nIndex),
354 static_cast<uno::XWeak*>(this));
356 return xChild;
359 uno::Reference<XAccessibleRelationSet> SAL_CALL
360 AccessibleShape::getAccessibleRelationSet()
362 ::osl::MutexGuard aGuard (maMutex);
363 if (mpParent == nullptr)
364 return uno::Reference<XAccessibleRelationSet>();
366 rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper;
368 //this mxshape is the captioned shape
369 uno::Sequence< uno::Reference< uno::XInterface > > aSequence { mpParent->GetAccessibleCaption(mxShape) };
370 if(aSequence[0])
372 pRelationSet->AddRelation(
373 AccessibleRelation( AccessibleRelationType::DESCRIBED_BY, aSequence ) );
375 return pRelationSet;
378 /** Return a copy of the state set.
379 Possible states are:
380 ENABLED
381 SHOWING
382 VISIBLE
384 uno::Reference<XAccessibleStateSet> SAL_CALL
385 AccessibleShape::getAccessibleStateSet()
387 ::osl::MutexGuard aGuard (maMutex);
389 if (IsDisposed())
391 // Return a minimal state set that only contains the DEFUNC state.
392 return AccessibleContextBase::getAccessibleStateSet ();
395 ::utl::AccessibleStateSetHelper* pStateSet =
396 static_cast<::utl::AccessibleStateSetHelper*>(mxStateSet.get());
398 if (!pStateSet)
399 return Reference<XAccessibleStateSet>();
401 // Merge current FOCUSED state from edit engine.
402 if (mpText)
404 if (mpText->HaveFocus())
405 pStateSet->AddState (AccessibleStateType::FOCUSED);
406 else
407 pStateSet->RemoveState (AccessibleStateType::FOCUSED);
409 //Just when the document is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
410 css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
411 if( xTempAcc.is() )
413 css::uno::Reference<XAccessibleContext>
414 xTempAccContext = xTempAcc->getAccessibleContext();
415 if( xTempAccContext.is() )
417 css::uno::Reference<XAccessibleStateSet> rState =
418 xTempAccContext->getAccessibleStateSet();
419 if (rState.is())
421 css::uno::Sequence<short> aStates = rState->getStates();
422 if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::EDITABLE) != aStates.end())
424 pStateSet->AddState (AccessibleStateType::EDITABLE);
425 pStateSet->AddState (AccessibleStateType::RESIZABLE);
426 pStateSet->AddState (AccessibleStateType::MOVEABLE);
432 // Create a copy of the state set that may be modified by the
433 // caller without affecting the current state set.
434 Reference<XAccessibleStateSet> xStateSet(new ::utl::AccessibleStateSetHelper(*pStateSet));
436 if (mpParent && mpParent->IsDocumentSelAll())
438 ::utl::AccessibleStateSetHelper* pCopyStateSet =
439 static_cast<::utl::AccessibleStateSetHelper*>(xStateSet.get());
440 pCopyStateSet->AddState (AccessibleStateType::SELECTED);
443 return xStateSet;
446 // XAccessibleComponent
447 /** The implementation below is at the moment straightforward. It iterates
448 over all children (and thereby instances all children which have not
449 been already instantiated) until a child covering the specified point is
450 found.
451 This leaves room for improvement. For instance, first iterate only over
452 the already instantiated children and only if no match is found
453 instantiate the remaining ones.
455 uno::Reference<XAccessible > SAL_CALL
456 AccessibleShape::getAccessibleAtPoint (
457 const awt::Point& aPoint)
459 ::osl::MutexGuard aGuard (maMutex);
461 sal_Int32 nChildCount = getAccessibleChildCount ();
462 for (sal_Int32 i=0; i<nChildCount; ++i)
464 Reference<XAccessible> xChild (getAccessibleChild (i));
465 if (xChild.is())
467 Reference<XAccessibleComponent> xChildComponent (
468 xChild->getAccessibleContext(), uno::UNO_QUERY);
469 if (xChildComponent.is())
471 awt::Rectangle aBBox (xChildComponent->getBounds());
472 if ( (aPoint.X >= aBBox.X)
473 && (aPoint.Y >= aBBox.Y)
474 && (aPoint.X < aBBox.X+aBBox.Width)
475 && (aPoint.Y < aBBox.Y+aBBox.Height) )
476 return xChild;
481 // Have not found a child under the given point. Returning empty
482 // reference to indicate this.
483 return uno::Reference<XAccessible>();
487 awt::Rectangle SAL_CALL AccessibleShape::getBounds()
489 SolarMutexGuard aSolarGuard;
490 ::osl::MutexGuard aGuard (maMutex);
492 ThrowIfDisposed ();
493 awt::Rectangle aBoundingBox;
494 if ( mxShape.is() )
497 static constexpr OUStringLiteral sBoundRectName = u"BoundRect";
498 static constexpr OUStringLiteral sAnchorPositionName = u"AnchorPosition";
500 // Get the shape's bounding box in internal coordinates (in 100th of
501 // mm). Use the property BoundRect. Only if that is not supported ask
502 // the shape for its position and size directly.
503 Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY);
504 Reference<beans::XPropertySetInfo> xSetInfo;
505 bool bFoundBoundRect = false;
506 if (xSet.is())
508 xSetInfo = xSet->getPropertySetInfo ();
509 if (xSetInfo.is())
511 if (xSetInfo->hasPropertyByName (sBoundRectName))
515 uno::Any aValue = xSet->getPropertyValue (sBoundRectName);
516 aValue >>= aBoundingBox;
517 bFoundBoundRect = true;
519 catch (beans::UnknownPropertyException const&)
521 // Handled below (bFoundBoundRect stays false).
524 else
525 SAL_WARN("svx", "no property BoundRect");
529 // Fallback when there is no BoundRect Property.
530 if ( ! bFoundBoundRect )
532 awt::Point aPosition (mxShape->getPosition());
533 awt::Size aSize (mxShape->getSize());
534 aBoundingBox = awt::Rectangle (
535 aPosition.X, aPosition.Y,
536 aSize.Width, aSize.Height);
538 // While BoundRects have absolute positions, the position returned
539 // by XPosition::getPosition is relative. Get the anchor position
540 // (usually not (0,0) for Writer shapes).
541 if (xSetInfo.is())
543 if (xSetInfo->hasPropertyByName (sAnchorPositionName))
545 uno::Any aPos = xSet->getPropertyValue (sAnchorPositionName);
546 awt::Point aAnchorPosition;
547 aPos >>= aAnchorPosition;
548 aBoundingBox.X += aAnchorPosition.X;
549 aBoundingBox.Y += aAnchorPosition.Y;
554 // Transform coordinates from internal to pixel.
555 if (maShapeTreeInfo.GetViewForwarder() == nullptr)
556 throw uno::RuntimeException (
557 "AccessibleShape has no valid view forwarder",
558 static_cast<uno::XWeak*>(this));
559 ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel (
560 ::Size (aBoundingBox.Width, aBoundingBox.Height));
561 ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel (
562 ::Point (aBoundingBox.X, aBoundingBox.Y));
564 // Clip the shape's bounding box with the bounding box of its parent.
565 Reference<XAccessibleComponent> xParentComponent (
566 getAccessibleParent(), uno::UNO_QUERY);
567 if (xParentComponent.is())
569 // Make the coordinates relative to the parent.
570 awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
571 int x = aPixelPosition.getX() - aParentLocation.X;
572 int y = aPixelPosition.getY() - aParentLocation.Y;
574 // Clip with parent (with coordinates relative to itself).
575 ::tools::Rectangle aBBox (
576 x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
577 awt::Size aParentSize (xParentComponent->getSize());
578 ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
579 aBBox = aBBox.GetIntersection (aParentBBox);
580 aBoundingBox = awt::Rectangle (
581 aBBox.getX(),
582 aBBox.getY(),
583 aBBox.getWidth(),
584 aBBox.getHeight());
586 else
588 SAL_INFO("svx", "parent does not support component");
589 aBoundingBox = awt::Rectangle (
590 aPixelPosition.getX(), aPixelPosition.getY(),
591 aPixelSize.getWidth(), aPixelSize.getHeight());
595 return aBoundingBox;
599 awt::Point SAL_CALL AccessibleShape::getLocation()
601 ThrowIfDisposed ();
602 awt::Rectangle aBoundingBox (getBounds());
603 return awt::Point (aBoundingBox.X, aBoundingBox.Y);
607 awt::Point SAL_CALL AccessibleShape::getLocationOnScreen()
609 ThrowIfDisposed ();
611 // Get relative position...
612 awt::Point aLocation (getLocation ());
614 // ... and add absolute position of the parent.
615 uno::Reference<XAccessibleComponent> xParentComponent (
616 getAccessibleParent(), uno::UNO_QUERY);
617 if (xParentComponent.is())
619 awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
620 aLocation.X += aParentLocation.X;
621 aLocation.Y += aParentLocation.Y;
623 else
624 SAL_WARN("svx", "parent does not support XAccessibleComponent");
625 return aLocation;
629 awt::Size SAL_CALL AccessibleShape::getSize()
631 ThrowIfDisposed ();
632 awt::Rectangle aBoundingBox (getBounds());
633 return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
637 sal_Int32 SAL_CALL AccessibleShape::getForeground()
639 ThrowIfDisposed ();
640 sal_Int32 nColor (0x0ffffffL);
644 uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
645 if (aSet.is())
647 uno::Any aColor;
648 aColor = aSet->getPropertyValue ("LineColor");
649 aColor >>= nColor;
652 catch (const css::beans::UnknownPropertyException &)
654 // Ignore exception and return default color.
656 return nColor;
660 sal_Int32 SAL_CALL AccessibleShape::getBackground()
662 ThrowIfDisposed ();
663 Color nColor;
667 uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
668 if (aSet.is())
670 uno::Any aColor;
671 aColor = aSet->getPropertyValue ("FillColor");
672 aColor >>= nColor;
673 aColor = aSet->getPropertyValue ("FillTransparence");
674 short nTrans=0;
675 aColor >>= nTrans;
676 Color crBk(nColor);
677 if (nTrans == 0 )
679 crBk.SetAlpha(0);
681 else
683 nTrans = short(256 - nTrans / 100. * 256);
684 crBk.SetAlpha(255 - sal_uInt8(nTrans));
686 nColor = crBk;
689 catch (const css::beans::UnknownPropertyException &)
691 // Ignore exception and return default color.
693 return sal_Int32(nColor);
696 // XAccessibleEventBroadcaster
697 void SAL_CALL AccessibleShape::addAccessibleEventListener (
698 const Reference<XAccessibleEventListener >& rxListener)
700 if (rBHelper.bDisposed || rBHelper.bInDispose)
702 uno::Reference<uno::XInterface> xThis (
703 static_cast<lang::XComponent *>(this), uno::UNO_QUERY);
704 rxListener->disposing (lang::EventObject (xThis));
706 else
708 AccessibleContextBase::addAccessibleEventListener (rxListener);
709 if (mpText != nullptr)
710 mpText->AddEventListener (rxListener);
715 void SAL_CALL AccessibleShape::removeAccessibleEventListener (
716 const Reference<XAccessibleEventListener >& rxListener)
718 AccessibleContextBase::removeAccessibleEventListener (rxListener);
719 if (mpText != nullptr)
720 mpText->RemoveEventListener (rxListener);
723 // XInterface
724 css::uno::Any SAL_CALL
725 AccessibleShape::queryInterface (const css::uno::Type & rType)
727 css::uno::Any aReturn = AccessibleContextBase::queryInterface (rType);
728 if ( ! aReturn.hasValue())
729 aReturn = ::cppu::queryInterface (rType,
730 static_cast<XAccessibleComponent*>(this),
731 static_cast<XAccessibleExtendedComponent*>(this),
732 static_cast< css::accessibility::XAccessibleSelection* >(this),
733 static_cast< css::accessibility::XAccessibleExtendedAttributes* >(this),
734 static_cast<document::XShapeEventListener*>(this),
735 static_cast<lang::XUnoTunnel*>(this),
736 static_cast<XAccessibleGroupPosition*>(this),
737 static_cast<XAccessibleHypertext*>(this)
739 return aReturn;
743 void SAL_CALL
744 AccessibleShape::acquire()
745 noexcept
747 AccessibleContextBase::acquire ();
751 void SAL_CALL
752 AccessibleShape::release()
753 noexcept
755 AccessibleContextBase::release ();
758 // XAccessibleSelection
759 void SAL_CALL AccessibleShape::selectAccessibleChild( sal_Int32 )
764 sal_Bool SAL_CALL AccessibleShape::isAccessibleChildSelected( sal_Int32 nChildIndex )
766 uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
767 uno::Reference<XAccessibleContext> xContext;
768 if( xAcc.is() )
770 xContext = xAcc->getAccessibleContext();
773 if( xContext.is() )
775 if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
777 uno::Reference< css::accessibility::XAccessibleText >
778 xText(xAcc, uno::UNO_QUERY);
779 if( xText.is() )
781 if( xText->getSelectionStart() >= 0 ) return true;
784 else if( xContext->getAccessibleRole() == AccessibleRole::SHAPE )
786 Reference< XAccessibleStateSet > pRState = xContext->getAccessibleStateSet();
787 if( !pRState.is() )
788 return false;
790 uno::Sequence<short> aStates = pRState->getStates();
791 return std::find(aStates.begin(), aStates.end(), AccessibleStateType::SELECTED) != aStates.end();
795 return false;
799 void SAL_CALL AccessibleShape::clearAccessibleSelection( )
804 void SAL_CALL AccessibleShape::selectAllAccessibleChildren( )
809 sal_Int32 SAL_CALL AccessibleShape::getSelectedAccessibleChildCount()
811 sal_Int32 nCount = 0;
812 sal_Int32 TotalCount = getAccessibleChildCount();
813 for( sal_Int32 i = 0; i < TotalCount; i++ )
814 if( isAccessibleChildSelected(i) ) nCount++;
816 return nCount;
820 Reference<XAccessible> SAL_CALL AccessibleShape::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
822 if ( nSelectedChildIndex > getSelectedAccessibleChildCount() )
823 throw IndexOutOfBoundsException();
824 sal_Int32 i1, i2;
825 for( i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
826 if( isAccessibleChildSelected(i1) )
828 if( i2 == nSelectedChildIndex )
829 return getAccessibleChild( i1 );
830 i2++;
832 return Reference<XAccessible>();
836 void SAL_CALL AccessibleShape::deselectAccessibleChild( sal_Int32 )
841 // XAccessibleExtendedAttributes
842 uno::Any SAL_CALL AccessibleShape::getExtendedAttributes()
844 uno::Any strRet;
845 OUString style;
846 if( getAccessibleRole() != AccessibleRole::SHAPE ) return strRet;
847 if( m_pShape )
849 style = "style:" + GetStyle();
851 style += ";";
852 strRet <<= style;
853 return strRet;
856 // XServiceInfo
857 OUString SAL_CALL
858 AccessibleShape::getImplementationName()
860 return "AccessibleShape";
864 uno::Sequence<OUString> SAL_CALL
865 AccessibleShape::getSupportedServiceNames()
867 ThrowIfDisposed ();
868 const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleShape" };
869 return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
872 // XTypeProvider
873 uno::Sequence<uno::Type> SAL_CALL
874 AccessibleShape::getTypes()
876 ThrowIfDisposed ();
877 // Get list of types from the context base implementation, ...
878 uno::Sequence<uno::Type> aTypeList (AccessibleContextBase::getTypes());
879 // ... get list of types from component base implementation, ...
880 uno::Sequence<uno::Type> aComponentTypeList (AccessibleComponentBase::getTypes());
881 // ... define local types
882 uno::Sequence<uno::Type> localTypesList = {
883 cppu::UnoType<lang::XEventListener>::get(),
884 cppu::UnoType<document::XEventListener>::get(),
885 cppu::UnoType<lang::XUnoTunnel>::get()
888 return comphelper::concatSequences(aTypeList, aComponentTypeList, localTypesList);
891 // lang::XEventListener
892 /** Disposing calls are accepted only from the model: Just reset the
893 reference to the model in the shape tree info. Otherwise this object
894 remains functional.
896 void AccessibleShape::disposing (const lang::EventObject& aEvent)
898 SolarMutexGuard aSolarGuard;
899 ::osl::MutexGuard aGuard (maMutex);
903 if (aEvent.Source == maShapeTreeInfo.GetModelBroadcaster())
905 // Remove reference to model broadcaster to allow it to pass
906 // away.
907 maShapeTreeInfo.SetModelBroadcaster(nullptr);
911 catch (uno::RuntimeException const&)
913 TOOLS_WARN_EXCEPTION("svx", "caught exception while disposing");
917 // document::XShapeEventListener
918 void SAL_CALL
919 AccessibleShape::notifyShapeEvent (const document::EventObject& rEventObject)
921 if (rEventObject.EventName != "ShapeModified")
922 return;
924 //Need to update text children when receiving ShapeModified hint when exiting edit mode for text box
925 if (mpText)
926 mpText->UpdateChildren();
929 // Some property of a shape has been modified. Send an event
930 // that indicates a change of the visible data to all listeners.
931 CommitChange (
932 AccessibleEventId::VISIBLE_DATA_CHANGED,
933 uno::Any(),
934 uno::Any());
936 // Name and Description may have changed. Update the local
937 // values accordingly.
938 UpdateNameAndDescription();
941 // lang::XUnoTunnel
942 UNO3_GETIMPLEMENTATION_IMPL(AccessibleShape)
944 // IAccessibleViewForwarderListener
945 void AccessibleShape::ViewForwarderChanged()
947 // Inform all listeners that the graphical representation (i.e. size
948 // and/or position) of the shape has changed.
949 CommitChange (AccessibleEventId::VISIBLE_DATA_CHANGED,
950 uno::Any(),
951 uno::Any());
953 // Tell children manager of the modified view forwarder.
954 if (mpChildrenManager != nullptr)
955 mpChildrenManager->ViewForwarderChanged();
957 // update our children that our screen position might have changed
958 if( mpText )
959 mpText->UpdateChildren();
962 // protected internal
963 // Set this object's name if is different to the current name.
964 OUString AccessibleShape::CreateAccessibleBaseName()
966 return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
970 OUString AccessibleShape::CreateAccessibleName()
972 return GetFullAccessibleName(this);
975 OUString AccessibleShape::GetFullAccessibleName (AccessibleShape *shape)
977 OUString sName (shape->CreateAccessibleBaseName());
978 // Append the shape's index to the name to disambiguate between shapes
979 // of the same type. If such an index where not given to the
980 // constructor then use the z-order instead. If even that does not exist
981 // we throw an exception.
982 OUString nameStr;
983 if (shape->m_pShape)
984 nameStr = shape->m_pShape->GetName();
985 if (nameStr.isEmpty())
987 sName += " ";
989 else
991 sName = nameStr;
994 //If the new produced name if not the same with last,notify name changed
995 //Event
996 if (aAccName != sName && !aAccName.isEmpty())
998 uno::Any aOldValue, aNewValue;
999 aOldValue <<= aAccName;
1000 aNewValue <<= sName;
1001 CommitChange(
1002 AccessibleEventId::NAME_CHANGED,
1003 aNewValue,
1004 aOldValue);
1006 aAccName = sName;
1007 return sName;
1010 // protected
1011 void AccessibleShape::disposing()
1013 SolarMutexGuard aSolarGuard;
1014 ::osl::MutexGuard aGuard (maMutex);
1016 // Make sure to send an event that this object loses the focus in the
1017 // case that it has the focus.
1018 ::utl::AccessibleStateSetHelper* pStateSet =
1019 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
1020 if (pStateSet != nullptr)
1021 pStateSet->RemoveState (AccessibleStateType::FOCUSED);
1023 // Unregister from model.
1024 if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
1025 maShapeTreeInfo.GetModelBroadcaster()->removeShapeEventListener(mxShape,
1026 static_cast<document::XShapeEventListener*>(this));
1028 // Release the child containers.
1029 if (mpChildrenManager != nullptr)
1031 mpChildrenManager.reset();
1033 if (mpText != nullptr)
1035 mpText->Dispose();
1036 mpText.reset();
1039 // Cleanup. Remove references to objects to allow them to be
1040 // destroyed.
1041 mxShape = nullptr;
1042 maShapeTreeInfo.dispose();
1044 // Call base classes.
1045 AccessibleContextBase::dispose ();
1048 sal_Int32 SAL_CALL
1049 AccessibleShape::getAccessibleIndexInParent()
1051 ThrowIfDisposed ();
1052 // Use a simple but slow solution for now. Optimize later.
1054 sal_Int32 nIndex = m_nIndexInParent;
1055 if ( -1 == nIndex )
1056 nIndex = AccessibleContextBase::getAccessibleIndexInParent();
1057 return nIndex;
1061 void AccessibleShape::UpdateNameAndDescription()
1063 // Ignore missing title, name, or description. There are fallbacks for
1064 // them.
1067 Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY_THROW);
1069 // Get the accessible name.
1070 OUString sString = GetOptionalProperty(xSet, "Title");
1071 if (!sString.isEmpty())
1073 SetAccessibleName(sString, AccessibleContextBase::FromShape);
1075 else
1077 sString = GetOptionalProperty(xSet, "Name");
1078 if (!sString.isEmpty())
1079 SetAccessibleName(sString, AccessibleContextBase::FromShape);
1082 // Get the accessible description.
1083 sString = GetOptionalProperty(xSet, "Description");
1084 if (!sString.isEmpty())
1085 SetAccessibleDescription(sString, AccessibleContextBase::FromShape);
1087 catch (uno::RuntimeException&)
1092 // Return this object's role.
1093 sal_Int16 SAL_CALL AccessibleShape::getAccessibleRole()
1095 sal_Int16 nAccessibleRole = AccessibleRole::SHAPE ;
1096 switch (ShapeTypeHandler::Instance().GetTypeId (mxShape))
1098 case DRAWING_GRAPHIC_OBJECT:
1099 nAccessibleRole = AccessibleRole::GRAPHIC ; break;
1100 case DRAWING_OLE:
1101 nAccessibleRole = AccessibleRole::EMBEDDED_OBJECT ; break;
1103 default:
1104 nAccessibleRole = AccessibleContextBase::getAccessibleRole();
1105 break;
1108 return nAccessibleRole;
1111 namespace {
1113 //sort the drawing objects from up to down, from left to right
1114 struct XShapePosCompareHelper
1116 bool operator() ( const uno::Reference<drawing::XShape>& xshape1,
1117 const uno::Reference<drawing::XShape>& xshape2 ) const
1119 SdrObject* pObj1 = GetSdrObjectFromXShape(xshape1);
1120 SdrObject* pObj2 = GetSdrObjectFromXShape(xshape2);
1121 if(pObj1 && pObj2)
1122 return pObj1->GetOrdNum() < pObj2->GetOrdNum();
1123 else
1124 return false;
1129 //end of group position
1131 // XAccessibleGroupPosition
1132 uno::Sequence< sal_Int32 > SAL_CALL
1133 AccessibleShape::getGroupPosition( const uno::Any& )
1135 // we will return the:
1136 // [0] group level
1137 // [1] similar items counts in the group
1138 // [2] the position of the object in the group
1139 uno::Sequence< sal_Int32 > aRet( 3 );
1140 aRet[0] = 0;
1141 aRet[1] = 0;
1142 aRet[2] = 0;
1144 css::uno::Reference<XAccessible> xParent = getAccessibleParent();
1145 if (!xParent.is())
1147 return aRet;
1149 SdrObject *pObj = GetSdrObjectFromXShape(mxShape);
1152 if(pObj == nullptr )
1154 return aRet;
1157 // Compute object's group level.
1158 sal_Int32 nGroupLevel = 0;
1159 SdrObject * pUper = pObj->getParentSdrObjectFromSdrObject();
1160 while( pUper )
1162 ++nGroupLevel;
1163 pUper = pUper->getParentSdrObjectFromSdrObject();
1166 css::uno::Reference<XAccessibleContext> xParentContext = xParent->getAccessibleContext();
1167 if( xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT ||
1168 xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_PRESENTATION ||
1169 xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_SPREADSHEET ||
1170 xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_TEXT )//Document
1172 Reference< XAccessibleGroupPosition > xGroupPosition( xParent,uno::UNO_QUERY );
1173 if ( xGroupPosition.is() )
1175 aRet = xGroupPosition->getGroupPosition( uno::makeAny( getAccessibleContext() ) );
1177 return aRet;
1179 if (xParentContext->getAccessibleRole() != AccessibleRole::SHAPE)
1181 return aRet;
1184 SdrObjList *pGrpList = nullptr;
1185 if( pObj->getParentSdrObjectFromSdrObject() )
1186 pGrpList = pObj->getParentSdrObjectFromSdrObject()->GetSubList();
1187 else
1188 return aRet;
1190 std::vector< uno::Reference<drawing::XShape> > vXShapes;
1191 if (pGrpList)
1193 const size_t nObj = pGrpList->GetObjCount();
1194 for(size_t i = 0 ; i < nObj ; ++i)
1196 SdrObject *pSubObj = pGrpList->GetObj(i);
1197 if (pSubObj &&
1198 xParentContext->getAccessibleChild(i)->getAccessibleContext()->getAccessibleRole() != AccessibleRole::GROUP_BOX)
1200 vXShapes.push_back( GetXShapeForSdrObject(pSubObj) );
1205 std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() );
1207 //get the index of the selected object in the group
1208 //we start counting position from 1
1209 sal_Int32 nPos = 1;
1210 for ( const auto& rpShape : vXShapes )
1212 if ( rpShape.get() == mxShape.get() )
1214 sal_Int32* pArray = aRet.getArray();
1215 pArray[0] = nGroupLevel;
1216 pArray[1] = vXShapes.size();
1217 pArray[2] = nPos;
1218 break;
1220 nPos++;
1223 return aRet;
1226 OUString AccessibleShape::getObjectLink( const uno::Any& )
1228 OUString aRet;
1230 SdrObject *pObj = GetSdrObjectFromXShape(mxShape);
1231 if(pObj == nullptr )
1233 return aRet;
1235 if (maShapeTreeInfo.GetDocumentWindow().is())
1237 Reference< XAccessibleGroupPosition > xGroupPosition( maShapeTreeInfo.GetDocumentWindow(), uno::UNO_QUERY );
1238 if (xGroupPosition.is())
1240 aRet = xGroupPosition->getObjectLink( uno::makeAny( getAccessibleContext() ) );
1243 return aRet;
1246 // XAccessibleHypertext
1247 sal_Int32 SAL_CALL AccessibleShape::getHyperLinkCount()
1249 // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
1250 // Code need to be adapted...
1251 return 0;
1254 SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
1255 if (pLink->IsValidHyperlink())
1256 return 1;
1257 else
1258 return 0;
1261 uno::Reference< XAccessibleHyperlink > SAL_CALL
1262 AccessibleShape::getHyperLink( sal_Int32 )
1264 uno::Reference< XAccessibleHyperlink > xRet;
1265 // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
1266 // Code need to be adapted...
1268 SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
1269 if (pLink->IsValidHyperlink())
1270 xRet = pLink;
1271 if( !xRet.is() )
1272 throw css::lang::IndexOutOfBoundsException();
1274 return xRet;
1276 sal_Int32 SAL_CALL AccessibleShape::getHyperLinkIndex( sal_Int32 )
1278 return 0;
1280 // XAccessibleText
1281 sal_Int32 SAL_CALL AccessibleShape::getCaretPosition( ){return 0;}
1282 sal_Bool SAL_CALL AccessibleShape::setCaretPosition( sal_Int32 ){return false;}
1283 sal_Unicode SAL_CALL AccessibleShape::getCharacter( sal_Int32 ){return 0;}
1284 css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleShape::getCharacterAttributes( sal_Int32, const css::uno::Sequence< OUString >& )
1286 uno::Sequence< css::beans::PropertyValue > aValues(0);
1287 return aValues;
1289 css::awt::Rectangle SAL_CALL AccessibleShape::getCharacterBounds( sal_Int32 )
1291 return css::awt::Rectangle(0, 0, 0, 0 );
1293 sal_Int32 SAL_CALL AccessibleShape::getCharacterCount( ){return 0;}
1294 sal_Int32 SAL_CALL AccessibleShape::getIndexAtPoint( const css::awt::Point& ){return 0;}
1295 OUString SAL_CALL AccessibleShape::getSelectedText( ){return OUString();}
1296 sal_Int32 SAL_CALL AccessibleShape::getSelectionStart( ){return 0;}
1297 sal_Int32 SAL_CALL AccessibleShape::getSelectionEnd( ){return 0;}
1298 sal_Bool SAL_CALL AccessibleShape::setSelection( sal_Int32, sal_Int32 ){return true;}
1299 OUString SAL_CALL AccessibleShape::getText( ){return OUString();}
1300 OUString SAL_CALL AccessibleShape::getTextRange( sal_Int32, sal_Int32 ){return OUString();}
1301 css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextAtIndex( sal_Int32, sal_Int16 )
1303 css::accessibility::TextSegment aResult;
1304 return aResult;
1306 css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBeforeIndex( sal_Int32, sal_Int16 )
1308 css::accessibility::TextSegment aResult;
1309 return aResult;
1311 css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBehindIndex( sal_Int32, sal_Int16 )
1313 css::accessibility::TextSegment aResult;
1314 return aResult;
1316 sal_Bool SAL_CALL AccessibleShape::copyText( sal_Int32, sal_Int32 ){return true;}
1317 sal_Bool SAL_CALL AccessibleShape::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ){return false;}
1319 } // end of namespace accessibility
1321 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */