1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svx.hxx"
26 
27 #include <accessiblecell.hxx>
28 
29 #include "svx/DescriptionGenerator.hxx"
30 
31 #include <com/sun/star/accessibility/AccessibleRole.hpp>
32 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
33 
34 #include <vcl/svapp.hxx>
35 
36 #include <unotools/accessiblestatesethelper.hxx>
37 
38 #include <editeng/outlobj.hxx>
39 #include <svx/unoshtxt.hxx>
40 #include <svx/svdotext.hxx>
41 
42 using ::rtl::OUString;
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;
49 
50 namespace accessibility {
51 
52 // --------------------------------------------------------------------
53 // AccessibleCell
54 // --------------------------------------------------------------------
55 
56 AccessibleCell::AccessibleCell( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible>& rxParent, const sdr::table::CellRef& rCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo )
57 : AccessibleCellBase( rxParent, AccessibleRole::TABLE_CELL )
58 , maShapeTreeInfo( rShapeTreeInfo )
59 , mnIndexInParent( nIndex )
60 , mpText( NULL )
61 , mxCell( rCell )
62 {
63 }
64 
65 // --------------------------------------------------------------------
66 
67 AccessibleCell::~AccessibleCell (void)
68 {
69 	DBG_ASSERT( mpText == 0, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
70 }
71 
72 // --------------------------------------------------------------------
73 
74 void AccessibleCell::Init (void)
75 {
76 	SdrView* pView = maShapeTreeInfo.GetSdrView();
77 	const Window* pWindow = maShapeTreeInfo.GetWindow ();
78 	if( (pView != NULL) && (pWindow != NULL) && mxCell.is())
79 	{
80         OutlinerParaObject* pOutlinerParaObject = mxCell->GetEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active
81 
82         bool bOwnParaObject = pOutlinerParaObject != 0;
83 
84 		if( !pOutlinerParaObject )
85 			pOutlinerParaObject = mxCell->GetOutlinerParaObject();
86 
87 		// create AccessibleTextHelper to handle this shape's text
88         if( pOutlinerParaObject )
89         {
90             // non-empty text -> use full-fledged edit source right away
91             ::std::auto_ptr<SvxEditSource> pEditSource( new SvxTextEditSource( mxCell->GetObject(), mxCell.get(), *pView, *pWindow) );
92             mpText = new AccessibleTextHelper( pEditSource );
93 			mpText->SetEventSource(this);
94         }
95 
96         if( bOwnParaObject)
97             delete pOutlinerParaObject;
98     }
99 }
100 
101 // --------------------------------------------------------------------
102 
103 sal_Bool AccessibleCell::SetState (sal_Int16 aState)
104 {
105     sal_Bool bStateHasChanged = sal_False;
106 
107     if (aState == AccessibleStateType::FOCUSED && mpText != NULL)
108     {
109         // Offer FOCUSED state to edit engine and detect whether the state
110         // changes.
111         sal_Bool bIsFocused = mpText->HaveFocus ();
112         mpText->SetFocus (sal_True);
113         bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
114     }
115     else
116         bStateHasChanged = AccessibleContextBase::SetState (aState);
117 
118     return bStateHasChanged;
119 }
120 
121 // --------------------------------------------------------------------
122 
123 sal_Bool AccessibleCell::ResetState (sal_Int16 aState)
124 {
125     sal_Bool bStateHasChanged = sal_False;
126 
127     if (aState == AccessibleStateType::FOCUSED && mpText != NULL)
128     {
129         // Try to remove FOCUSED state from the edit engine and detect
130         // whether the state changes.
131         sal_Bool bIsFocused = mpText->HaveFocus ();
132         mpText->SetFocus (sal_False);
133         bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
134     }
135     else
136         bStateHasChanged = AccessibleContextBase::ResetState (aState);
137 
138     return bStateHasChanged;
139 }
140 
141 // --------------------------------------------------------------------
142 
143 sal_Bool AccessibleCell::GetState (sal_Int16 aState)
144 {
145     if (aState == AccessibleStateType::FOCUSED && mpText != NULL)
146     {
147         // Just delegate the call to the edit engine.  The state is not
148         // merged into the state set.
149         return mpText->HaveFocus();
150     }
151     else
152         return AccessibleContextBase::GetState (aState);
153 }
154 
155 //-----------------------------------------------------------------------------
156 
157 bool AccessibleCell::operator== (const AccessibleCell& rAccessibleCell)
158 {
159 	return this == &rAccessibleCell;
160 }
161 
162 //-----------------------------------------------------------------------------
163 // XInterface
164 //-----------------------------------------------------------------------------
165 
166 Any SAL_CALL AccessibleCell::queryInterface( const Type& aType ) throw (RuntimeException)
167 {
168 	return AccessibleCellBase::queryInterface( aType );
169 }
170 
171 //-----------------------------------------------------------------------------
172 
173 void SAL_CALL AccessibleCell::acquire(  ) throw ()
174 {
175 	AccessibleCellBase::acquire();
176 }
177 
178 //-----------------------------------------------------------------------------
179 
180 void SAL_CALL AccessibleCell::release(  ) throw ()
181 {
182 	AccessibleCellBase::release();
183 }
184 
185 // --------------------------------------------------------------------
186 // XAccessibleContext
187 // --------------------------------------------------------------------
188 
189 /** The children of this cell come from the paragraphs of text.
190 */
191 sal_Int32 SAL_CALL AccessibleCell::getAccessibleChildCount() throw (::com::sun::star::uno::RuntimeException)
192 {
193     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
194     ThrowIfDisposed ();
195 	return mpText != NULL ? mpText->GetChildCount () : 0;
196 }
197 
198 // --------------------------------------------------------------------
199 
200 /** Forward the request to the shape.  Return the requested shape or throw
201     an exception for a wrong index.
202 */
203 Reference<XAccessible> SAL_CALL AccessibleCell::getAccessibleChild (sal_Int32 nIndex) throw (IndexOutOfBoundsException, RuntimeException)
204 {
205     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
206     ThrowIfDisposed ();
207 
208 	// todo: does GetChild throw IndexOutOfBoundsException?
209 	return mpText->GetChild (nIndex);
210 }
211 
212 // --------------------------------------------------------------------
213 
214 /**	Return a copy of the state set.
215     Possible states are:
216 		ENABLED
217 		SHOWING
218 		VISIBLE
219 */
220 Reference<XAccessibleStateSet> SAL_CALL AccessibleCell::getAccessibleStateSet (void) throw (RuntimeException)
221 {
222     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
223     ::osl::MutexGuard aGuard (maMutex);
224     Reference<XAccessibleStateSet> xStateSet;
225 
226 	if (rBHelper.bDisposed || mpText == NULL)
227 	{
228         // Return a minimal state set that only contains the DEFUNC state.
229         xStateSet = AccessibleContextBase::getAccessibleStateSet ();
230 	}
231     else
232     {
233         ::utl::AccessibleStateSetHelper* pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
234 
235         if(pStateSet)
236         {
237             // Merge current FOCUSED state from edit engine.
238             if (mpText != NULL)
239 			{
240 				if (mpText->HaveFocus())
241                     pStateSet->AddState (AccessibleStateType::FOCUSED);
242                 else
243                     pStateSet->RemoveState (AccessibleStateType::FOCUSED);
244 			}
245 
246             // Create a copy of the state set that may be modified by the
247             // caller without affecting the current state set.
248             xStateSet = Reference<XAccessibleStateSet>(new ::utl::AccessibleStateSetHelper (*pStateSet));
249         }
250     }
251 
252     return xStateSet;
253 }
254 
255 // --------------------------------------------------------------------
256 // XAccessibleComponent
257 // --------------------------------------------------------------------
258 
259 sal_Bool SAL_CALL AccessibleCell::containsPoint( const ::com::sun::star::awt::Point& aPoint) throw (::com::sun::star::uno::RuntimeException)
260 {
261 	return AccessibleComponentBase::containsPoint( aPoint );
262 }
263 
264 /** The implementation below is at the moment straightforward.  It iterates
265     over all children (and thereby instances all children which have not
266     been already instatiated) until a child covering the specifed point is
267     found.
268     This leaves room for improvement.  For instance, first iterate only over
269     the already instantiated children and only if no match is found
270     instantiate the remaining ones.
271 */
272 Reference<XAccessible > SAL_CALL  AccessibleCell::getAccessibleAtPoint ( const ::com::sun::star::awt::Point& aPoint) throw(RuntimeException)
273 {
274     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
275     ::osl::MutexGuard aGuard (maMutex);
276 
277     sal_Int32 nChildCount = getAccessibleChildCount ();
278     for (sal_Int32 i=0; i<nChildCount; ++i)
279     {
280         Reference<XAccessible> xChild (getAccessibleChild (i));
281         if (xChild.is())
282         {
283             Reference<XAccessibleComponent> xChildComponent (xChild->getAccessibleContext(), uno::UNO_QUERY);
284             if (xChildComponent.is())
285             {
286                 awt::Rectangle aBBox (xChildComponent->getBounds());
287                 if ( (aPoint.X >= aBBox.X)
288                     && (aPoint.Y >= aBBox.Y)
289                     && (aPoint.X < aBBox.X+aBBox.Width)
290                     && (aPoint.Y < aBBox.Y+aBBox.Height) )
291                     return xChild;
292             }
293         }
294     }
295 
296     // Have not found a child under the given point.  Returning empty
297     // reference to indicate this.
298     return uno::Reference<XAccessible>();
299 }
300 
301 // --------------------------------------------------------------------
302 
303 ::com::sun::star::awt::Rectangle SAL_CALL AccessibleCell::getBounds(void) throw(RuntimeException)
304 {
305     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
306     ::osl::MutexGuard aGuard (maMutex);
307 
308     ThrowIfDisposed ();
309 	::com::sun::star::awt::Rectangle aBoundingBox;
310 	if( mxCell.is() )
311 	{
312 		// Get the cell's bounding box in internal coordinates (in 100th of mm)
313 		const ::Rectangle aCellRect( mxCell->getCellRect() );
314 
315 		// Transform coordinates from internal to pixel.
316 		if (maShapeTreeInfo.GetViewForwarder() == NULL)
317 			throw uno::RuntimeException (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleCell has no valid view forwarder")),static_cast<uno::XWeak*>(this));
318 
319 		::Size aPixelSize( maShapeTreeInfo.GetViewForwarder()->LogicToPixel(::Size(aCellRect.GetWidth(), aCellRect.GetHeight())) );
320 		::Point aPixelPosition( maShapeTreeInfo.GetViewForwarder()->LogicToPixel( aCellRect.TopLeft() ));
321 
322 		// Clip the shape's bounding box with the bounding box of its parent.
323 		Reference<XAccessibleComponent> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY);
324 		if (xParentComponent.is())
325 		{
326 			// Make the coordinates relative to the parent.
327 			awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
328 			int x = aPixelPosition.getX() - aParentLocation.X;
329 			int y = aPixelPosition.getY() - aParentLocation.Y;
330 
331 			// Clip with parent (with coordinates relative to itself).
332 			::Rectangle aBBox ( x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
333 			awt::Size aParentSize (xParentComponent->getSize());
334 			::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
335 			aBBox = aBBox.GetIntersection (aParentBBox);
336 			aBoundingBox = awt::Rectangle (	aBBox.getX(), aBBox.getY(), aBBox.getWidth(), aBBox.getHeight());
337 		}
338 		else
339 		{
340 			OSL_TRACE ("parent does not support component");
341 			aBoundingBox = awt::Rectangle (aPixelPosition.getX(), aPixelPosition.getY(),aPixelSize.getWidth(), aPixelSize.getHeight());
342 		}
343 	}
344 
345     return aBoundingBox;
346 }
347 
348 // --------------------------------------------------------------------
349 
350 ::com::sun::star::awt::Point SAL_CALL AccessibleCell::getLocation(void) throw (RuntimeException)
351 {
352     ThrowIfDisposed ();
353 	::com::sun::star::awt::Rectangle aBoundingBox(getBounds());
354     return ::com::sun::star::awt::Point(aBoundingBox.X, aBoundingBox.Y);
355 }
356 
357 // --------------------------------------------------------------------
358 
359 ::com::sun::star::awt::Point SAL_CALL AccessibleCell::getLocationOnScreen(void) throw(RuntimeException)
360 {
361     ThrowIfDisposed ();
362 
363     // Get relative position...
364     ::com::sun::star::awt::Point aLocation(getLocation ());
365 
366     // ... and add absolute position of the parent.
367     Reference<XAccessibleComponent> xParentComponent( getAccessibleParent(), uno::UNO_QUERY);
368     if(xParentComponent.is())
369     {
370         ::com::sun::star::awt::Point aParentLocation(xParentComponent->getLocationOnScreen());
371         aLocation.X += aParentLocation.X;
372         aLocation.Y += aParentLocation.Y;
373     }
374     else
375 	{
376         OSL_TRACE ("getLocation: parent does not support XAccessibleComponent");
377 	}
378 
379 	return aLocation;
380 }
381 
382 // --------------------------------------------------------------------
383 
384 awt::Size SAL_CALL AccessibleCell::getSize (void) throw (RuntimeException)
385 {
386     ThrowIfDisposed ();
387     awt::Rectangle aBoundingBox (getBounds());
388     return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
389 }
390 
391 // --------------------------------------------------------------------
392 
393 void SAL_CALL AccessibleCell::addFocusListener ( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XFocusListener >& xListener) throw (::com::sun::star::uno::RuntimeException)
394 {
395 	AccessibleComponentBase::addFocusListener( xListener );
396 }
397 
398 // --------------------------------------------------------------------
399 
400 void SAL_CALL AccessibleCell::removeFocusListener (const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XFocusListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
401 {
402 	AccessibleComponentBase::removeFocusListener( xListener );
403 }
404 
405 // --------------------------------------------------------------------
406 
407 void SAL_CALL AccessibleCell::grabFocus (void) throw (::com::sun::star::uno::RuntimeException)
408 {
409 	AccessibleComponentBase::grabFocus();
410 }
411 
412 // --------------------------------------------------------------------
413 
414 sal_Int32 SAL_CALL AccessibleCell::getForeground(void) throw (RuntimeException)
415 {
416     ThrowIfDisposed ();
417     sal_Int32 nColor (0x0ffffffL);
418 
419 	// todo
420     return nColor;
421 }
422 
423 // --------------------------------------------------------------------
424 
425 sal_Int32 SAL_CALL AccessibleCell::getBackground (void) throw (RuntimeException)
426 {
427     ThrowIfDisposed ();
428     sal_Int32 nColor (0L);
429 
430 	// todo
431     return nColor;
432 }
433 
434 // --------------------------------------------------------------------
435 // XAccessibleExtendedComponent
436 // --------------------------------------------------------------------
437 
438 ::com::sun::star::uno::Reference< ::com::sun::star::awt::XFont > SAL_CALL AccessibleCell::getFont (void) throw (::com::sun::star::uno::RuntimeException)
439 {
440 //todo
441 	return AccessibleComponentBase::getFont();
442 }
443 
444 // --------------------------------------------------------------------
445 
446 ::rtl::OUString SAL_CALL AccessibleCell::getTitledBorderText (void) throw (::com::sun::star::uno::RuntimeException)
447 {
448 	return AccessibleComponentBase::getTitledBorderText();
449 }
450 
451 // --------------------------------------------------------------------
452 
453 ::rtl::OUString SAL_CALL AccessibleCell::getToolTipText (void) throw (::com::sun::star::uno::RuntimeException)
454 {
455 	return AccessibleComponentBase::getToolTipText();
456 }
457 
458 // --------------------------------------------------------------------
459 // XAccessibleEventBroadcaster
460 // --------------------------------------------------------------------
461 
462 void SAL_CALL AccessibleCell::addEventListener( const Reference<XAccessibleEventListener >& rxListener)  throw (RuntimeException)
463 {
464     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
465     ::osl::MutexGuard aGuard (maMutex);
466 	if (rBHelper.bDisposed || rBHelper.bInDispose)
467 	{
468 		Reference<XInterface> xSource( static_cast<XComponent *>(this) );
469 		lang::EventObject aEventObj(xSource);
470 		rxListener->disposing(aEventObj);
471 	}
472     else
473     {
474         AccessibleContextBase::addEventListener (rxListener);
475         if (mpText != NULL)
476             mpText->AddEventListener (rxListener);
477     }
478 }
479 
480 // --------------------------------------------------------------------
481 
482 void SAL_CALL AccessibleCell::removeEventListener( const Reference<XAccessibleEventListener >& rxListener) throw (RuntimeException)
483 {
484     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
485     AccessibleContextBase::removeEventListener(rxListener);
486     if (mpText != NULL)
487         mpText->RemoveEventListener (rxListener);
488 }
489 
490 // --------------------------------------------------------------------
491 // XServiceInfo
492 // --------------------------------------------------------------------
493 
494 OUString SAL_CALL AccessibleCell::getImplementationName(void) throw (RuntimeException)
495 {
496 	return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleCell"));
497 }
498 
499 // --------------------------------------------------------------------
500 
501 Sequence<OUString> SAL_CALL AccessibleCell::getSupportedServiceNames(void) throw (RuntimeException)
502 {
503     ThrowIfDisposed ();
504 
505 	// Get list of supported service names from base class...
506     uno::Sequence<OUString> aServiceNames = AccessibleContextBase::getSupportedServiceNames();
507     sal_Int32 nCount (aServiceNames.getLength());
508 
509     // ...and add additional names.
510     aServiceNames.realloc (nCount + 1);
511     static const OUString sAdditionalServiceName (RTL_CONSTASCII_USTRINGPARAM("com.sun.star.drawing.AccessibleCell"));
512     aServiceNames[nCount] = sAdditionalServiceName;
513 
514     return aServiceNames;
515 }
516 
517 // --------------------------------------------------------------------
518 // IAccessibleViewForwarderListener
519 // --------------------------------------------------------------------
520 
521 void AccessibleCell::ViewForwarderChanged (ChangeType /*aChangeType*/, const IAccessibleViewForwarder* /*pViewForwarder*/)
522 {
523     // Inform all listeners that the graphical representation (i.e. size
524     // and/or position) of the shape has changed.
525     CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any());
526 
527     // update our children that our screen position might have changed
528     if( mpText )
529         mpText->UpdateChildren();
530 }
531 
532 // --------------------------------------------------------------------
533 // protected
534 // --------------------------------------------------------------------
535 
536 void AccessibleCell::disposing (void)
537 {
538     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
539     ::osl::MutexGuard aGuard (maMutex);
540 
541     // Make sure to send an event that this object looses the focus in the
542     // case that it has the focus.
543     ::utl::AccessibleStateSetHelper* pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
544     if (pStateSet != NULL)
545         pStateSet->RemoveState(AccessibleStateType::FOCUSED);
546 
547     if (mpText != NULL)
548     {
549         mpText->Dispose();
550         delete mpText;
551         mpText = NULL;
552     }
553 
554     // Cleanup.  Remove references to objects to allow them to be
555     // destroyed.
556     mxCell.clear();
557     maShapeTreeInfo = AccessibleShapeTreeInfo();
558 
559     // Call base classes.
560     AccessibleContextBase::dispose ();
561 }
562 
563 sal_Int32 SAL_CALL AccessibleCell::getAccessibleIndexInParent (void) throw (RuntimeException)
564 {
565     ThrowIfDisposed ();
566     return mnIndexInParent;
567 }
568 
569 ::rtl::OUString SAL_CALL AccessibleCell::getAccessibleName (void) throw (::com::sun::star::uno::RuntimeException)
570 {
571     ThrowIfDisposed ();
572     ::vos::OGuard aSolarGuard (::Application::GetSolarMutex());
573 
574     if( mxCell.is() )
575         return mxCell->getName();
576 
577     return AccessibleCellBase::getAccessibleName();
578 }
579 
580 } // end of namespace accessibility
581