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>
25 #include "accessiblecell.hxx"
28 #include <com/sun/star/accessibility/AccessibleRole.hpp>
29 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
30 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 #include <editeng/unoedsrc.hxx>
33 #include <vcl/svapp.hxx>
35 #include <unotools/accessiblestatesethelper.hxx>
36 #include <comphelper/string.hxx>
37 #include <comphelper/sequence.hxx>
38 #include <svx/IAccessibleViewForwarder.hxx>
39 #include <svx/unoshtxt.hxx>
40 #include <svx/svdotext.hxx>
41 #include <tools/debug.hxx>
43 using namespace sdr::table
;
44 using namespace ::com::sun::star
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::accessibility
;
47 using namespace ::com::sun::star::lang
;
48 using namespace ::com::sun::star::container
;
50 namespace accessibility
{
52 AccessibleCell::AccessibleCell( const css::uno::Reference
< css::accessibility::XAccessible
>& rxParent
, const sdr::table::CellRef
& rCell
, sal_Int32 nIndex
, const AccessibleShapeTreeInfo
& rShapeTreeInfo
)
53 : AccessibleCellBase( rxParent
, AccessibleRole::TABLE_CELL
)
54 , maShapeTreeInfo( rShapeTreeInfo
)
55 , mnIndexInParent( nIndex
)
58 //Init the pAccTable var
59 pAccTable
= dynamic_cast <AccessibleTableShape
*> (rxParent
.get());
63 AccessibleCell::~AccessibleCell()
65 DBG_ASSERT( mpText
== nullptr, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
69 void AccessibleCell::Init()
71 SdrView
* pView
= maShapeTreeInfo
.GetSdrView();
72 const vcl::Window
* pWindow
= maShapeTreeInfo
.GetWindow ();
73 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);
89 bool AccessibleCell::SetState (sal_Int16 aState
)
91 bool bStateHasChanged
= false;
93 if (aState
== AccessibleStateType::FOCUSED
&& mpText
!= nullptr)
95 // Offer FOCUSED state to edit engine and detect whether the state
97 bool bIsFocused
= mpText
->HaveFocus ();
99 bStateHasChanged
= (bIsFocused
!= mpText
->HaveFocus ());
102 bStateHasChanged
= AccessibleContextBase::SetState (aState
);
104 return bStateHasChanged
;
108 bool AccessibleCell::ResetState (sal_Int16 aState
)
110 bool bStateHasChanged
= false;
112 if (aState
== AccessibleStateType::FOCUSED
&& mpText
!= nullptr)
114 // Try to remove FOCUSED state from the edit engine and detect
115 // whether the state changes.
116 bool bIsFocused
= mpText
->HaveFocus ();
117 mpText
->SetFocus (false);
118 bStateHasChanged
= (bIsFocused
!= mpText
->HaveFocus ());
121 bStateHasChanged
= AccessibleContextBase::ResetState (aState
);
123 return bStateHasChanged
;
130 Any SAL_CALL
AccessibleCell::queryInterface( const Type
& aType
)
132 return AccessibleCellBase::queryInterface( aType
);
136 void SAL_CALL
AccessibleCell::acquire( ) throw ()
138 AccessibleCellBase::acquire();
142 void SAL_CALL
AccessibleCell::release( ) throw ()
144 AccessibleCellBase::release();
148 // XAccessibleContext
151 /** The children of this cell come from the paragraphs of text.
153 sal_Int32 SAL_CALL
AccessibleCell::getAccessibleChildCount()
155 SolarMutexGuard aSolarGuard
;
157 return mpText
!= nullptr ? mpText
->GetChildCount () : 0;
161 /** Forward the request to the shape. Return the requested shape or throw
162 an exception for a wrong index.
164 Reference
<XAccessible
> SAL_CALL
AccessibleCell::getAccessibleChild (sal_Int32 nIndex
)
166 SolarMutexGuard aSolarGuard
;
169 // todo: does GetChild throw IndexOutOfBoundsException?
170 return mpText
->GetChild (nIndex
);
174 /** Return a copy of the state set.
180 Reference
<XAccessibleStateSet
> SAL_CALL
AccessibleCell::getAccessibleStateSet()
182 SolarMutexGuard aSolarGuard
;
183 ::osl::MutexGuard
aGuard (maMutex
);
184 Reference
<XAccessibleStateSet
> xStateSet
;
186 if (rBHelper
.bDisposed
|| mpText
== nullptr)
188 // Return a minimal state set that only contains the DEFUNC state.
189 xStateSet
= AccessibleContextBase::getAccessibleStateSet ();
193 ::utl::AccessibleStateSetHelper
* pStateSet
= static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
197 // Merge current FOCUSED state from edit engine.
198 if (mpText
!= nullptr)
200 if (mpText
->HaveFocus())
201 pStateSet
->AddState (AccessibleStateType::FOCUSED
);
203 pStateSet
->RemoveState (AccessibleStateType::FOCUSED
);
205 // Set the invisible state for merged cell
206 if (mxCell
.is() && mxCell
->isMerged())
207 pStateSet
->RemoveState(AccessibleStateType::VISIBLE
);
209 pStateSet
->AddState(AccessibleStateType::VISIBLE
);
212 //Just when the parent table is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
213 css::uno::Reference
<XAccessible
> xTempAcc
= getAccessibleParent();
216 css::uno::Reference
<XAccessibleContext
>
217 xTempAccContext
= xTempAcc
->getAccessibleContext();
218 if( xTempAccContext
.is() )
220 css::uno::Reference
<XAccessibleStateSet
> rState
=
221 xTempAccContext
->getAccessibleStateSet();
224 css::uno::Sequence
<short> aStates
= rState
->getStates();
225 if (std::find(aStates
.begin(), aStates
.end(), AccessibleStateType::EDITABLE
) != aStates
.end())
227 pStateSet
->AddState (AccessibleStateType::EDITABLE
);
228 pStateSet
->AddState (AccessibleStateType::RESIZABLE
);
229 pStateSet
->AddState (AccessibleStateType::MOVEABLE
);
234 // Create a copy of the state set that may be modified by the
235 // caller without affecting the current state set.
236 xStateSet
.set(new ::utl::AccessibleStateSetHelper (*pStateSet
));
244 // XAccessibleComponent
247 sal_Bool SAL_CALL
AccessibleCell::containsPoint( const css::awt::Point
& aPoint
)
249 return AccessibleComponentBase::containsPoint( aPoint
);
252 /** The implementation below is at the moment straightforward. It iterates
253 over all children (and thereby instances all children which have not
254 been already instantiated) until a child covering the specified point is
256 This leaves room for improvement. For instance, first iterate only over
257 the already instantiated children and only if no match is found
258 instantiate the remaining ones.
260 Reference
<XAccessible
> SAL_CALL
AccessibleCell::getAccessibleAtPoint ( const css::awt::Point
& aPoint
)
262 SolarMutexGuard aSolarGuard
;
263 ::osl::MutexGuard
aGuard (maMutex
);
265 sal_Int32 nChildCount
= getAccessibleChildCount ();
266 for (sal_Int32 i
=0; i
<nChildCount
; ++i
)
268 Reference
<XAccessible
> xChild (getAccessibleChild (i
));
271 Reference
<XAccessibleComponent
> xChildComponent (xChild
->getAccessibleContext(), uno::UNO_QUERY
);
272 if (xChildComponent
.is())
274 awt::Rectangle
aBBox (xChildComponent
->getBounds());
275 if ( (aPoint
.X
>= aBBox
.X
)
276 && (aPoint
.Y
>= aBBox
.Y
)
277 && (aPoint
.X
< aBBox
.X
+aBBox
.Width
)
278 && (aPoint
.Y
< aBBox
.Y
+aBBox
.Height
) )
284 // Have not found a child under the given point. Returning empty
285 // reference to indicate this.
286 return uno::Reference
<XAccessible
>();
290 css::awt::Rectangle SAL_CALL
AccessibleCell::getBounds()
292 SolarMutexGuard aSolarGuard
;
293 ::osl::MutexGuard
aGuard (maMutex
);
296 css::awt::Rectangle aBoundingBox
;
299 // Get the cell's bounding box in internal coordinates (in 100th of mm)
300 const ::tools::Rectangle
aCellRect( mxCell
->getCellRect() );
302 // Transform coordinates from internal to pixel.
303 if (maShapeTreeInfo
.GetViewForwarder() == nullptr)
304 throw uno::RuntimeException ("AccessibleCell has no valid view forwarder",static_cast<uno::XWeak
*>(this));
306 ::Size
aPixelSize( maShapeTreeInfo
.GetViewForwarder()->LogicToPixel(::Size(aCellRect
.GetWidth(), aCellRect
.GetHeight())) );
307 ::Point
aPixelPosition( maShapeTreeInfo
.GetViewForwarder()->LogicToPixel( aCellRect
.TopLeft() ));
309 // Clip the shape's bounding box with the bounding box of its parent.
310 Reference
<XAccessibleComponent
> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY
);
311 if (xParentComponent
.is())
313 // Make the coordinates relative to the parent.
314 awt::Point
aParentLocation (xParentComponent
->getLocationOnScreen());
315 int x
= aPixelPosition
.getX() - aParentLocation
.X
;
316 int y
= aPixelPosition
.getY() - aParentLocation
.Y
;
318 // Clip with parent (with coordinates relative to itself).
319 ::tools::Rectangle
aBBox ( x
, y
, x
+ aPixelSize
.getWidth(), y
+ aPixelSize
.getHeight());
320 awt::Size
aParentSize (xParentComponent
->getSize());
321 ::tools::Rectangle
aParentBBox (0,0, aParentSize
.Width
, aParentSize
.Height
);
322 aBBox
= aBBox
.GetIntersection (aParentBBox
);
323 aBoundingBox
= awt::Rectangle ( aBBox
.getX(), aBBox
.getY(), aBBox
.getWidth(), aBBox
.getHeight());
327 SAL_INFO("svx", "parent does not support component");
328 aBoundingBox
= awt::Rectangle (aPixelPosition
.getX(), aPixelPosition
.getY(),aPixelSize
.getWidth(), aPixelSize
.getHeight());
336 css::awt::Point SAL_CALL
AccessibleCell::getLocation()
339 css::awt::Rectangle
aBoundingBox(getBounds());
340 return css::awt::Point(aBoundingBox
.X
, aBoundingBox
.Y
);
344 css::awt::Point SAL_CALL
AccessibleCell::getLocationOnScreen()
348 // Get relative position...
349 css::awt::Point
aLocation(getLocation ());
351 // ... and add absolute position of the parent.
352 Reference
<XAccessibleComponent
> xParentComponent( getAccessibleParent(), uno::UNO_QUERY
);
353 if(xParentComponent
.is())
355 css::awt::Point
aParentLocation(xParentComponent
->getLocationOnScreen());
356 aLocation
.X
+= aParentLocation
.X
;
357 aLocation
.Y
+= aParentLocation
.Y
;
361 SAL_WARN("svx", "parent does not support XAccessibleComponent");
368 awt::Size SAL_CALL
AccessibleCell::getSize()
371 awt::Rectangle
aBoundingBox (getBounds());
372 return awt::Size (aBoundingBox
.Width
, aBoundingBox
.Height
);
376 void SAL_CALL
AccessibleCell::grabFocus()
378 AccessibleComponentBase::grabFocus();
382 sal_Int32 SAL_CALL
AccessibleCell::getForeground()
387 return sal_Int32(0x0ffffffL
);
391 sal_Int32 SAL_CALL
AccessibleCell::getBackground()
400 // XAccessibleExtendedComponent
403 css::uno::Reference
< css::awt::XFont
> SAL_CALL
AccessibleCell::getFont()
406 return AccessibleComponentBase::getFont();
410 OUString SAL_CALL
AccessibleCell::getTitledBorderText()
412 return AccessibleComponentBase::getTitledBorderText();
416 OUString SAL_CALL
AccessibleCell::getToolTipText()
418 return AccessibleComponentBase::getToolTipText();
422 // XAccessibleEventBroadcaster
425 void SAL_CALL
AccessibleCell::addAccessibleEventListener( const Reference
<XAccessibleEventListener
>& rxListener
)
427 SolarMutexGuard aSolarGuard
;
428 ::osl::MutexGuard
aGuard (maMutex
);
429 if (rBHelper
.bDisposed
|| rBHelper
.bInDispose
)
431 Reference
<XInterface
> xSource( static_cast<XComponent
*>(this) );
432 lang::EventObject
aEventObj(xSource
);
433 rxListener
->disposing(aEventObj
);
437 AccessibleContextBase::addAccessibleEventListener (rxListener
);
438 if (mpText
!= nullptr)
439 mpText
->AddEventListener (rxListener
);
444 void SAL_CALL
AccessibleCell::removeAccessibleEventListener( const Reference
<XAccessibleEventListener
>& rxListener
)
446 SolarMutexGuard aSolarGuard
;
447 AccessibleContextBase::removeAccessibleEventListener(rxListener
);
448 if (mpText
!= nullptr)
449 mpText
->RemoveEventListener (rxListener
);
456 OUString SAL_CALL
AccessibleCell::getImplementationName()
458 return "AccessibleCell";
462 Sequence
<OUString
> SAL_CALL
AccessibleCell::getSupportedServiceNames()
465 const css::uno::Sequence
<OUString
> vals
{ "com.sun.star.drawing.AccessibleCell" };
466 return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals
);
470 // IAccessibleViewForwarderListener
473 void AccessibleCell::ViewForwarderChanged()
475 // Inform all listeners that the graphical representation (i.e. size
476 // and/or position) of the shape has changed.
477 CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED
, Any(), Any());
479 // update our children that our screen position might have changed
481 mpText
->UpdateChildren();
488 void AccessibleCell::disposing()
490 SolarMutexGuard aSolarGuard
;
491 ::osl::MutexGuard
aGuard (maMutex
);
493 // Make sure to send an event that this object loses the focus in the
494 // case that it has the focus.
495 ::utl::AccessibleStateSetHelper
* pStateSet
= static_cast< ::utl::AccessibleStateSetHelper
*>(mxStateSet
.get());
496 if (pStateSet
!= nullptr)
497 pStateSet
->RemoveState(AccessibleStateType::FOCUSED
);
499 if (mpText
!= nullptr)
505 // Cleanup. Remove references to objects to allow them to be
508 maShapeTreeInfo
.dispose();
510 // Call base classes.
511 AccessibleContextBase::dispose ();
514 sal_Int32 SAL_CALL
AccessibleCell::getAccessibleIndexInParent()
517 return mnIndexInParent
;
521 OUString
AccessibleCell::getCellName( sal_Int32 nCol
, sal_Int32 nRow
)
528 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
529 static_cast<sal_uInt16
>(nCol
)));
532 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
533 (static_cast<sal_uInt16
>(nCol
) / 26) - 1));
534 aBuf
.append( static_cast<sal_Unicode
>( 'A' +
535 (static_cast<sal_uInt16
>(nCol
) % 26)));
543 sal_Int32 nC
= nCol
% 26;
544 aStr
.append(static_cast<sal_Unicode
>( 'A' +
545 static_cast<sal_uInt16
>(nC
)));
547 nCol
= nCol
/ 26 - 1;
549 aStr
.append(static_cast<sal_Unicode
>( 'A' +
550 static_cast<sal_uInt16
>(nCol
)));
551 aBuf
.append(comphelper::string::reverseString(aStr
.makeStringAndClear()));
553 aBuf
.append( OUString::number(nRow
+1) );
554 return aBuf
.makeStringAndClear();
557 OUString SAL_CALL
AccessibleCell::getAccessibleName()
560 SolarMutexGuard aSolarGuard
;
566 sal_Int32 nRow
= 0, nCol
= 0;
567 pAccTable
->getColumnAndRow(mnIndexInParent
, nCol
, nRow
);
568 return getCellName( nCol
, nRow
);
570 catch(const Exception
&)
575 return AccessibleCellBase::getAccessibleName();
578 void AccessibleCell::UpdateChildren()
581 mpText
->UpdateChildren();
584 /* MT: Above getAccessibleName was introduced with IA2 CWS, while below was introduce in 3.3 meanwhile. Check which one is correct
585 +If this is correct, we also don't need sdr::table::CellRef getCellRef(), UpdateChildren(), getCellName( sal_Int32 nCol, sal_Int32 nRow ) above
588 OUString SAL_CALL AccessibleCell::getAccessibleName() throw (css::uno::RuntimeException)
591 SolarMutexGuard aSolarGuard;
594 return mxCell->getName();
596 return AccessibleCellBase::getAccessibleName();
600 } // end of namespace accessibility
602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */