1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <sal/config.h>
21 #include <sal/log.hxx>
26 #include "accessiblecell.hxx"
28 #include <svx/DescriptionGenerator.hxx>
30 #include <com/sun/star/accessibility/AccessibleRole.hpp>
31 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
32 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
34 #include <vcl/svapp.hxx>
36 #include <unotools/accessiblestatesethelper.hxx>
37 #include <comphelper/string.hxx>
38 #include <editeng/outlobj.hxx>
39 #include <svx/IAccessibleViewForwarder.hxx>
40 #include <svx/unoshtxt.hxx>
41 #include <svx/svdotext.hxx>
42 #include <tools/debug.hxx>
44 using namespace sdr::table
;
45 using namespace ::com::sun::star
;
46 using namespace ::com::sun::star::uno
;
47 using namespace ::com::sun::star::accessibility
;
48 using namespace ::com::sun::star::lang
;
49 using namespace ::com::sun::star::container
;
51 namespace accessibility
{
53 AccessibleCell::AccessibleCell( const css::uno::Reference
< css::accessibility::XAccessible
>& rxParent
, const sdr::table::CellRef
& rCell
, sal_Int32 nIndex
, const AccessibleShapeTreeInfo
& rShapeTreeInfo
)
54 : AccessibleCellBase( rxParent
, AccessibleRole::TABLE_CELL
)
55 , maShapeTreeInfo( rShapeTreeInfo
)
56 , mnIndexInParent( nIndex
)
59 //Init the pAccTable var
60 pAccTable
= dynamic_cast <AccessibleTableShape
*> (rxParent
.get());
64 AccessibleCell::~AccessibleCell()
66 DBG_ASSERT( mpText
== nullptr, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
70 void AccessibleCell::Init()
72 SdrView
* pView
= maShapeTreeInfo
.GetSdrView();
73 const vcl::Window
* pWindow
= maShapeTreeInfo
.GetWindow ();
74 if( (pView
!= nullptr) && (pWindow
!= nullptr) && mxCell
.is())
76 // create AccessibleTextHelper to handle this shape's text
77 if( mxCell
->CanCreateEditOutlinerParaObject() || mxCell
->GetOutlinerParaObject() != nullptr )
79 // non-empty text -> use full-fledged edit source right away
81 mpText
.reset( new AccessibleTextHelper( std::make_unique
<SvxTextEditSource
>(mxCell
->GetObject(), mxCell
.get(), *pView
, *pWindow
) ) );
82 if( mxCell
.is() && mxCell
->IsActiveCell() )
84 mpText
->SetEventSource(this);
90 bool AccessibleCell::SetState (sal_Int16 aState
)
92 bool bStateHasChanged
= false;
94 if (aState
== AccessibleStateType::FOCUSED
&& mpText
!= nullptr)
96 // Offer FOCUSED state to edit engine and detect whether the state
98 bool bIsFocused
= mpText
->HaveFocus ();
100 bStateHasChanged
= (bIsFocused
!= mpText
->HaveFocus ());
103 bStateHasChanged
= AccessibleContextBase::SetState (aState
);
105 return bStateHasChanged
;
109 bool AccessibleCell::ResetState (sal_Int16 aState
)
111 bool bStateHasChanged
= false;
113 if (aState
== AccessibleStateType::FOCUSED
&& mpText
!= nullptr)
115 // Try to remove FOCUSED state from the edit engine and detect
116 // whether the state changes.
117 bool bIsFocused
= mpText
->HaveFocus ();
118 mpText
->SetFocus (false);
119 bStateHasChanged
= (bIsFocused
!= mpText
->HaveFocus ());
122 bStateHasChanged
= AccessibleContextBase::ResetState (aState
);
124 return bStateHasChanged
;
131 Any SAL_CALL
AccessibleCell::queryInterface( const Type
& aType
)
133 return AccessibleCellBase::queryInterface( aType
);
137 void SAL_CALL
AccessibleCell::acquire( ) throw ()
139 AccessibleCellBase::acquire();
143 void SAL_CALL
AccessibleCell::release( ) throw ()
145 AccessibleCellBase::release();
149 // XAccessibleContext
152 /** The children of this cell come from the paragraphs of text.
154 sal_Int32 SAL_CALL
AccessibleCell::getAccessibleChildCount()
156 SolarMutexGuard aSolarGuard
;
158 return mpText
!= nullptr ? mpText
->GetChildCount () : 0;
162 /** Forward the request to the shape. Return the requested shape or throw
163 an exception for a wrong index.
165 Reference
<XAccessible
> SAL_CALL
AccessibleCell::getAccessibleChild (sal_Int32 nIndex
)
167 SolarMutexGuard aSolarGuard
;
170 // todo: does GetChild throw IndexOutOfBoundsException?
171 return mpText
->GetChild (nIndex
);
175 /** Return a copy of the state set.
181 Reference
<XAccessibleStateSet
> SAL_CALL
AccessibleCell::getAccessibleStateSet()
183 SolarMutexGuard aSolarGuard
;
184 ::osl::MutexGuard
aGuard (maMutex
);
185 Reference
<XAccessibleStateSet
> xStateSet
;
187 if (rBHelper
.bDisposed
|| mpText
== nullptr)
189 // Return a minimal state set that only contains the DEFUNC state.
190 xStateSet
= AccessibleContextBase::getAccessibleStateSet ();
194 ::utl::AccessibleStateSetHelper
* pStateSet
= static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
198 // Merge current FOCUSED state from edit engine.
199 if (mpText
!= nullptr)
201 if (mpText
->HaveFocus())
202 pStateSet
->AddState (AccessibleStateType::FOCUSED
);
204 pStateSet
->RemoveState (AccessibleStateType::FOCUSED
);
206 // Set the invisible state for merged cell
207 if (mxCell
.is() && mxCell
->isMerged())
208 pStateSet
->RemoveState(AccessibleStateType::VISIBLE
);
210 pStateSet
->AddState(AccessibleStateType::VISIBLE
);
213 //Just when the parent table is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
214 css::uno::Reference
<XAccessible
> xTempAcc
= getAccessibleParent();
217 css::uno::Reference
<XAccessibleContext
>
218 xTempAccContext
= xTempAcc
->getAccessibleContext();
219 if( xTempAccContext
.is() )
221 css::uno::Reference
<XAccessibleStateSet
> rState
=
222 xTempAccContext
->getAccessibleStateSet();
225 css::uno::Sequence
<short> aStates
= rState
->getStates();
226 if (std::find(aStates
.begin(), aStates
.end(), AccessibleStateType::EDITABLE
) != aStates
.end())
228 pStateSet
->AddState (AccessibleStateType::EDITABLE
);
229 pStateSet
->AddState (AccessibleStateType::RESIZABLE
);
230 pStateSet
->AddState (AccessibleStateType::MOVEABLE
);
235 // Create a copy of the state set that may be modified by the
236 // caller without affecting the current state set.
237 xStateSet
.set(new ::utl::AccessibleStateSetHelper (*pStateSet
));
245 // XAccessibleComponent
248 sal_Bool SAL_CALL
AccessibleCell::containsPoint( const css::awt::Point
& aPoint
)
250 return AccessibleComponentBase::containsPoint( aPoint
);
253 /** The implementation below is at the moment straightforward. It iterates
254 over all children (and thereby instances all children which have not
255 been already instantiated) until a child covering the specified point is
257 This leaves room for improvement. For instance, first iterate only over
258 the already instantiated children and only if no match is found
259 instantiate the remaining ones.
261 Reference
<XAccessible
> SAL_CALL
AccessibleCell::getAccessibleAtPoint ( const css::awt::Point
& aPoint
)
263 SolarMutexGuard aSolarGuard
;
264 ::osl::MutexGuard
aGuard (maMutex
);
266 sal_Int32 nChildCount
= getAccessibleChildCount ();
267 for (sal_Int32 i
=0; i
<nChildCount
; ++i
)
269 Reference
<XAccessible
> xChild (getAccessibleChild (i
));
272 Reference
<XAccessibleComponent
> xChildComponent (xChild
->getAccessibleContext(), uno::UNO_QUERY
);
273 if (xChildComponent
.is())
275 awt::Rectangle
aBBox (xChildComponent
->getBounds());
276 if ( (aPoint
.X
>= aBBox
.X
)
277 && (aPoint
.Y
>= aBBox
.Y
)
278 && (aPoint
.X
< aBBox
.X
+aBBox
.Width
)
279 && (aPoint
.Y
< aBBox
.Y
+aBBox
.Height
) )
285 // Have not found a child under the given point. Returning empty
286 // reference to indicate this.
287 return uno::Reference
<XAccessible
>();
291 css::awt::Rectangle SAL_CALL
AccessibleCell::getBounds()
293 SolarMutexGuard aSolarGuard
;
294 ::osl::MutexGuard
aGuard (maMutex
);
297 css::awt::Rectangle aBoundingBox
;
300 // Get the cell's bounding box in internal coordinates (in 100th of mm)
301 const ::tools::Rectangle
aCellRect( mxCell
->getCellRect() );
303 // Transform coordinates from internal to pixel.
304 if (maShapeTreeInfo
.GetViewForwarder() == nullptr)
305 throw uno::RuntimeException ("AccessibleCell has no valid view forwarder",static_cast<uno::XWeak
*>(this));
307 ::Size
aPixelSize( maShapeTreeInfo
.GetViewForwarder()->LogicToPixel(::Size(aCellRect
.GetWidth(), aCellRect
.GetHeight())) );
308 ::Point
aPixelPosition( maShapeTreeInfo
.GetViewForwarder()->LogicToPixel( aCellRect
.TopLeft() ));
310 // Clip the shape's bounding box with the bounding box of its parent.
311 Reference
<XAccessibleComponent
> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY
);
312 if (xParentComponent
.is())
314 // Make the coordinates relative to the parent.
315 awt::Point
aParentLocation (xParentComponent
->getLocationOnScreen());
316 int x
= aPixelPosition
.getX() - aParentLocation
.X
;
317 int y
= aPixelPosition
.getY() - aParentLocation
.Y
;
319 // Clip with parent (with coordinates relative to itself).
320 ::tools::Rectangle
aBBox ( x
, y
, x
+ aPixelSize
.getWidth(), y
+ aPixelSize
.getHeight());
321 awt::Size
aParentSize (xParentComponent
->getSize());
322 ::tools::Rectangle
aParentBBox (0,0, aParentSize
.Width
, aParentSize
.Height
);
323 aBBox
= aBBox
.GetIntersection (aParentBBox
);
324 aBoundingBox
= awt::Rectangle ( aBBox
.getX(), aBBox
.getY(), aBBox
.getWidth(), aBBox
.getHeight());
328 SAL_INFO("svx", "parent does not support component");
329 aBoundingBox
= awt::Rectangle (aPixelPosition
.getX(), aPixelPosition
.getY(),aPixelSize
.getWidth(), aPixelSize
.getHeight());
337 css::awt::Point SAL_CALL
AccessibleCell::getLocation()
340 css::awt::Rectangle
aBoundingBox(getBounds());
341 return css::awt::Point(aBoundingBox
.X
, aBoundingBox
.Y
);
345 css::awt::Point SAL_CALL
AccessibleCell::getLocationOnScreen()
349 // Get relative position...
350 css::awt::Point
aLocation(getLocation ());
352 // ... and add absolute position of the parent.
353 Reference
<XAccessibleComponent
> xParentComponent( getAccessibleParent(), uno::UNO_QUERY
);
354 if(xParentComponent
.is())
356 css::awt::Point
aParentLocation(xParentComponent
->getLocationOnScreen());
357 aLocation
.X
+= aParentLocation
.X
;
358 aLocation
.Y
+= aParentLocation
.Y
;
362 SAL_WARN("svx", "parent does not support XAccessibleComponent");
369 awt::Size SAL_CALL
AccessibleCell::getSize()
372 awt::Rectangle
aBoundingBox (getBounds());
373 return awt::Size (aBoundingBox
.Width
, aBoundingBox
.Height
);
377 void SAL_CALL
AccessibleCell::grabFocus()
379 AccessibleComponentBase::grabFocus();
383 sal_Int32 SAL_CALL
AccessibleCell::getForeground()
388 return sal_Int32(0x0ffffffL
);
392 sal_Int32 SAL_CALL
AccessibleCell::getBackground()
401 // XAccessibleExtendedComponent
404 css::uno::Reference
< css::awt::XFont
> SAL_CALL
AccessibleCell::getFont()
407 return AccessibleComponentBase::getFont();
411 OUString SAL_CALL
AccessibleCell::getTitledBorderText()
413 return AccessibleComponentBase::getTitledBorderText();
417 OUString SAL_CALL
AccessibleCell::getToolTipText()
419 return AccessibleComponentBase::getToolTipText();
423 // XAccessibleEventBroadcaster
426 void SAL_CALL
AccessibleCell::addAccessibleEventListener( const Reference
<XAccessibleEventListener
>& rxListener
)
428 SolarMutexGuard aSolarGuard
;
429 ::osl::MutexGuard
aGuard (maMutex
);
430 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
432 Reference
<XInterface
> xSource( static_cast<XComponent
*>(this) );
433 lang::EventObject
aEventObj(xSource
);
434 rxListener
->disposing(aEventObj
);
438 AccessibleContextBase::addAccessibleEventListener (rxListener
);
439 if (mpText
!= nullptr)
440 mpText
->AddEventListener (rxListener
);
445 void SAL_CALL
AccessibleCell::removeAccessibleEventListener( const Reference
<XAccessibleEventListener
>& rxListener
)
447 SolarMutexGuard aSolarGuard
;
448 AccessibleContextBase::removeAccessibleEventListener(rxListener
);
449 if (mpText
!= nullptr)
450 mpText
->RemoveEventListener (rxListener
);
457 OUString SAL_CALL
AccessibleCell::getImplementationName()
459 return "AccessibleCell";
463 Sequence
<OUString
> SAL_CALL
AccessibleCell::getSupportedServiceNames()
467 // Get list of supported service names from base class...
468 uno::Sequence
<OUString
> aServiceNames
= AccessibleContextBase::getSupportedServiceNames();
469 sal_Int32
nCount (aServiceNames
.getLength());
471 // ...and add additional names.
472 aServiceNames
.realloc (nCount
+ 1);
473 aServiceNames
[nCount
] = "com.sun.star.drawing.AccessibleCell";
475 return aServiceNames
;
479 // IAccessibleViewForwarderListener
482 void AccessibleCell::ViewForwarderChanged()
484 // Inform all listeners that the graphical representation (i.e. size
485 // and/or position) of the shape has changed.
486 CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED
, Any(), Any());
488 // update our children that our screen position might have changed
490 mpText
->UpdateChildren();
497 void AccessibleCell::disposing()
499 SolarMutexGuard aSolarGuard
;
500 ::osl::MutexGuard
aGuard (maMutex
);
502 // Make sure to send an event that this object loses the focus in the
503 // case that it has the focus.
504 ::utl::AccessibleStateSetHelper
* pStateSet
= static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
505 if (pStateSet
!= nullptr)
506 pStateSet
->RemoveState(AccessibleStateType::FOCUSED
);
508 if (mpText
!= nullptr)
514 // Cleanup. Remove references to objects to allow them to be
517 maShapeTreeInfo
.dispose();
519 // Call base classes.
520 AccessibleContextBase::dispose ();
523 sal_Int32 SAL_CALL
AccessibleCell::getAccessibleIndexInParent()
526 return mnIndexInParent
;
530 OUString
AccessibleCell::getCellName( sal_Int32 nCol
, sal_Int32 nRow
)
537 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
538 static_cast<sal_uInt16
>(nCol
)));
541 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
542 (static_cast<sal_uInt16
>(nCol
) / 26) - 1));
543 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
544 (static_cast<sal_uInt16
>(nCol
) % 26)));
552 sal_Int32 nC
= nCol
% 26;
553 aStr
.append(static_cast<sal_Unicode
>( 'A' +
554 static_cast<sal_uInt16
>(nC
)));
556 nCol
= nCol
/ 26 - 1;
558 aStr
.append(static_cast<sal_Unicode
>( 'A' +
559 static_cast<sal_uInt16
>(nCol
)));
560 aBuf
.append(comphelper::string::reverseString(aStr
.makeStringAndClear()));
562 aBuf
.append( OUString::number(nRow
+1) );
563 return aBuf
.makeStringAndClear();
566 OUString SAL_CALL
AccessibleCell::getAccessibleName()
569 SolarMutexGuard aSolarGuard
;
575 sal_Int32 nRow
= 0, nCol
= 0;
576 pAccTable
->getColumnAndRow(mnIndexInParent
, nCol
, nRow
);
577 return getCellName( nCol
, nRow
);
579 catch(const Exception
&)
584 return AccessibleCellBase::getAccessibleName();
587 void AccessibleCell::UpdateChildren()
590 mpText
->UpdateChildren();
593 /* MT: Above getAccessibleName was introduced with IA2 CWS, while below was introduce in 3.3 meanwhile. Check which one is correct
594 +If this is correct, we also don't need sdr::table::CellRef getCellRef(), UpdateChildren(), getCellName( sal_Int32 nCol, sal_Int32 nRow ) above
597 OUString SAL_CALL AccessibleCell::getAccessibleName() throw (css::uno::RuntimeException)
600 SolarMutexGuard aSolarGuard;
603 return mxCell->getName();
605 return AccessibleCellBase::getAccessibleName();
609 } // end of namespace accessibility
611 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */