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_editeng.hxx"
26
27
28 #include <editeng/AccessibleContextBase.hxx>
29
30 #include <com/sun/star/accessibility/AccessibleRole.hpp>
31 #include <com/sun/star/beans/PropertyChangeEvent.hpp>
32 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
33 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
34 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
35
36 #include <unotools/accessiblestatesethelper.hxx>
37 #include <unotools/accessiblerelationsethelper.hxx>
38 #include <comphelper/accessibleeventnotifier.hxx>
39 #include <rtl/uuid.h>
40 #include <vos/mutex.hxx>
41 //#include <vcl/svapp.hxx>
42
43 #include <utility>
44
45 using namespace ::rtl;
46 using namespace ::com::sun::star;
47 using namespace ::com::sun::star::accessibility;
48 using ::com::sun::star::uno::Reference;
49
50 namespace accessibility {
51
52 //===== internal ============================================================
53
54 // Define a shortcut for the somewhot longish base class name.
55 typedef ::cppu::WeakComponentImplHelper4<
56 ::com::sun::star::accessibility::XAccessible,
57 ::com::sun::star::accessibility::XAccessibleContext,
58 ::com::sun::star::accessibility::XAccessibleEventBroadcaster,
59 ::com::sun::star::lang::XServiceInfo> BaseClass;
60
AccessibleContextBase(const uno::Reference<XAccessible> & rxParent,const sal_Int16 aRole)61 AccessibleContextBase::AccessibleContextBase (
62 const uno::Reference<XAccessible>& rxParent,
63 const sal_Int16 aRole)
64 : BaseClass (MutexOwner::maMutex),
65 mxStateSet (NULL),
66 mxRelationSet (NULL),
67 mxParent(rxParent),
68 msDescription(),
69 meDescriptionOrigin(NotSet),
70 msName(),
71 meNameOrigin(NotSet),
72 mnClientId(0),
73 maRole(aRole)
74 {
75 // Create the state set.
76 ::utl::AccessibleStateSetHelper* pStateSet = new ::utl::AccessibleStateSetHelper ();
77 mxStateSet = pStateSet;
78
79 // Set some states. Don't use the SetState method because no events
80 // shall be broadcastet (that is not yet initialized anyway).
81 if (pStateSet != NULL)
82 {
83 pStateSet->AddState (AccessibleStateType::ENABLED);
84 pStateSet->AddState (AccessibleStateType::SENSITIVE);
85 pStateSet->AddState (AccessibleStateType::SHOWING);
86 pStateSet->AddState (AccessibleStateType::VISIBLE);
87 pStateSet->AddState (AccessibleStateType::FOCUSABLE);
88 pStateSet->AddState (AccessibleStateType::SELECTABLE);
89 }
90
91 // Create the relation set.
92 ::utl::AccessibleRelationSetHelper* pRelationSet = new ::utl::AccessibleRelationSetHelper ();
93 mxRelationSet = pRelationSet;
94 }
95
96
97
98
~AccessibleContextBase(void)99 AccessibleContextBase::~AccessibleContextBase(void)
100 {
101 }
102
103
104
105
SetState(sal_Int16 aState)106 sal_Bool AccessibleContextBase::SetState (sal_Int16 aState)
107 {
108 ::osl::ClearableMutexGuard aGuard (maMutex);
109 ::utl::AccessibleStateSetHelper* pStateSet =
110 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
111 if ((pStateSet != NULL) && !pStateSet->contains(aState))
112 {
113 pStateSet->AddState (aState);
114 // Clear the mutex guard so that it is not locked during calls to
115 // listeners.
116 aGuard.clear();
117
118 // Send event for all states except the DEFUNC state.
119 if (aState != AccessibleStateType::DEFUNC)
120 {
121 uno::Any aNewValue;
122 aNewValue <<= aState;
123 CommitChange(
124 AccessibleEventId::STATE_CHANGED,
125 aNewValue,
126 uno::Any());
127 }
128 return sal_True;
129 }
130 else
131 return sal_False;
132 }
133
134
135
136
ResetState(sal_Int16 aState)137 sal_Bool AccessibleContextBase::ResetState (sal_Int16 aState)
138 {
139 ::osl::ClearableMutexGuard aGuard (maMutex);
140 ::utl::AccessibleStateSetHelper* pStateSet =
141 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
142 if ((pStateSet != NULL) && pStateSet->contains(aState))
143 {
144 pStateSet->RemoveState (aState);
145 // Clear the mutex guard so that it is not locked during calls to listeners.
146 aGuard.clear();
147
148 uno::Any aOldValue;
149 aOldValue <<= aState;
150 CommitChange(
151 AccessibleEventId::STATE_CHANGED,
152 uno::Any(),
153 aOldValue);
154 return sal_True;
155 }
156 else
157 return sal_False;
158 }
159
160
161
162
GetState(sal_Int16 aState)163 sal_Bool AccessibleContextBase::GetState (sal_Int16 aState)
164 {
165 ::osl::MutexGuard aGuard (maMutex);
166 ::utl::AccessibleStateSetHelper* pStateSet =
167 static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
168 if (pStateSet != NULL)
169 return pStateSet->contains(aState);
170 else
171 // If there is no state set then return false as a default value.
172 return sal_False;
173 }
174
175
176
177
SetRelationSet(const uno::Reference<XAccessibleRelationSet> & rxNewRelationSet)178 void AccessibleContextBase::SetRelationSet (
179 const uno::Reference<XAccessibleRelationSet>& rxNewRelationSet)
180 throw (::com::sun::star::uno::RuntimeException)
181 {
182 OSL_TRACE ("setting relation set");
183
184 // Try to emit some meaningful events indicating differing relations in
185 // both sets.
186 typedef std::pair<short int,short int> RD;
187 const RD aRelationDescriptors[] = {
188 RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED),
189 RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED),
190 RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED),
191 RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED),
192 RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED),
193 RD(AccessibleRelationType::INVALID, -1),
194 };
195 for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++)
196 if (mxRelationSet->containsRelation(aRelationDescriptors[i].first)
197 != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first))
198 CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any());
199
200 mxRelationSet = rxNewRelationSet;
201 }
202
203
204
205
206 //===== XAccessible =========================================================
207
208 uno::Reference< XAccessibleContext> SAL_CALL
getAccessibleContext(void)209 AccessibleContextBase::getAccessibleContext (void)
210 throw (uno::RuntimeException)
211 {
212 ThrowIfDisposed ();
213 return this;
214 }
215
216
217
218
219 //===== XAccessibleContext ==================================================
220
221 /** No children.
222 */
223 sal_Int32 SAL_CALL
getAccessibleChildCount(void)224 AccessibleContextBase::getAccessibleChildCount (void)
225 throw (uno::RuntimeException)
226 {
227 ThrowIfDisposed ();
228 return 0;
229 }
230
231
232
233
234 /** Forward the request to the shape. Return the requested shape or throw
235 an exception for a wrong index.
236 */
237 uno::Reference<XAccessible> SAL_CALL
getAccessibleChild(sal_Int32 nIndex)238 AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex)
239 throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::uno::RuntimeException)
240 {
241 ThrowIfDisposed ();
242 throw lang::IndexOutOfBoundsException (
243 ::rtl::OUString::createFromAscii ("no child with index " + nIndex),
244 NULL);
245 }
246
247
248
249
250 uno::Reference<XAccessible> SAL_CALL
getAccessibleParent(void)251 AccessibleContextBase::getAccessibleParent (void)
252 throw (::com::sun::star::uno::RuntimeException)
253 {
254 ThrowIfDisposed ();
255 return mxParent;
256 }
257
258
259
260
261 sal_Int32 SAL_CALL
getAccessibleIndexInParent(void)262 AccessibleContextBase::getAccessibleIndexInParent (void)
263 throw (::com::sun::star::uno::RuntimeException)
264 {
265 ThrowIfDisposed ();
266 // Use a simple but slow solution for now. Optimize later.
267
268 // Iterate over all the parent's children and search for this object.
269 if (mxParent.is())
270 {
271 uno::Reference<XAccessibleContext> xParentContext (
272 mxParent->getAccessibleContext());
273 if (xParentContext.is())
274 {
275 sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
276 for (sal_Int32 i=0; i<nChildCount; i++)
277 {
278 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
279 if (xChild.is())
280 {
281 uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext();
282 if (xChildContext == (XAccessibleContext*)this)
283 return i;
284 }
285 }
286 }
287 }
288
289 // Return -1 to indicate that this object's parent does not know about the
290 // object.
291 return -1;
292 }
293
294
295
296
297 sal_Int16 SAL_CALL
getAccessibleRole(void)298 AccessibleContextBase::getAccessibleRole (void)
299 throw (::com::sun::star::uno::RuntimeException)
300 {
301 ThrowIfDisposed ();
302 return maRole;
303 }
304
305
306
307
308 ::rtl::OUString SAL_CALL
getAccessibleDescription(void)309 AccessibleContextBase::getAccessibleDescription (void)
310 throw (::com::sun::star::uno::RuntimeException)
311 {
312 ThrowIfDisposed ();
313
314 return msDescription;
315 }
316
317
318
319
320 OUString SAL_CALL
getAccessibleName(void)321 AccessibleContextBase::getAccessibleName (void)
322 throw (::com::sun::star::uno::RuntimeException)
323 {
324 ThrowIfDisposed ();
325
326 if (meNameOrigin == NotSet)
327 {
328 // Do not send an event because this is the first time it has been
329 // requested.
330 msName = CreateAccessibleName();
331 meNameOrigin = AutomaticallyCreated;
332 }
333
334 return msName;
335 }
336
337
338
339
340 /** Return a copy of the relation set.
341 */
342 uno::Reference<XAccessibleRelationSet> SAL_CALL
getAccessibleRelationSet(void)343 AccessibleContextBase::getAccessibleRelationSet (void)
344 throw (::com::sun::star::uno::RuntimeException)
345 {
346 ThrowIfDisposed ();
347
348 // Create a copy of the relation set and return it.
349 ::utl::AccessibleRelationSetHelper* pRelationSet =
350 static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get());
351 if (pRelationSet != NULL)
352 {
353 return uno::Reference<XAccessibleRelationSet> (
354 new ::utl::AccessibleRelationSetHelper (*pRelationSet));
355 }
356 else
357 return uno::Reference<XAccessibleRelationSet>(NULL);
358 }
359
360
361
362
363 /** Return a copy of the state set.
364 Possible states are:
365 ENABLED
366 SHOWING
367 VISIBLE
368 */
369 uno::Reference<XAccessibleStateSet> SAL_CALL
getAccessibleStateSet(void)370 AccessibleContextBase::getAccessibleStateSet (void)
371 throw (::com::sun::star::uno::RuntimeException)
372 {
373 ::utl::AccessibleStateSetHelper* pStateSet = NULL;
374
375 if (rBHelper.bDisposed)
376 {
377 // We are already disposed!
378 // Create a new state set that has only set the DEFUNC state.
379 pStateSet = new ::utl::AccessibleStateSetHelper ();
380 if (pStateSet != NULL)
381 pStateSet->AddState (AccessibleStateType::DEFUNC);
382 }
383 else
384 {
385 // Create a copy of the state set and return it.
386 pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
387
388 // Merge current focused state from edit engine.
389 #if 0
390 if (aState == AccessibleStateType::FOCUSED
391 && pStateSet != NULL
392 && mpText != NULL)
393 {
394 if (mpText->GetFocusedState ())
395 pStateSet->AddState (aState);
396 else
397 pStateSet->RemoveState (aState);
398 }
399 #endif
400 if (pStateSet != NULL)
401 pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet);
402 }
403
404 return uno::Reference<XAccessibleStateSet>(pStateSet);
405 }
406
407
408
409
410 lang::Locale SAL_CALL
getLocale(void)411 AccessibleContextBase::getLocale (void)
412 throw (IllegalAccessibleComponentStateException,
413 ::com::sun::star::uno::RuntimeException)
414 {
415 ThrowIfDisposed ();
416 // Delegate request to parent.
417 if (mxParent.is())
418 {
419 uno::Reference<XAccessibleContext> xParentContext (
420 mxParent->getAccessibleContext());
421 if (xParentContext.is())
422 return xParentContext->getLocale ();
423 }
424
425 // No locale and no parent. Therefore throw exception to indicate this
426 // cluelessness.
427 throw IllegalAccessibleComponentStateException ();
428 }
429
430
431
432
433 //===== XAccessibleEventListener ============================================
434
435 void SAL_CALL
addEventListener(const uno::Reference<XAccessibleEventListener> & rxListener)436 AccessibleContextBase::addEventListener (
437 const uno::Reference<XAccessibleEventListener >& rxListener)
438 throw (uno::RuntimeException)
439 {
440 if (rxListener.is())
441 {
442 if (rBHelper.bDisposed || rBHelper.bInDispose)
443 {
444 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY);
445 rxListener->disposing (lang::EventObject (x));
446 }
447 else
448 {
449 if (!mnClientId)
450 mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
451 comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener );
452 }
453 }
454 }
455
456
457
458
459 void SAL_CALL
removeEventListener(const uno::Reference<XAccessibleEventListener> & rxListener)460 AccessibleContextBase::removeEventListener (
461 const uno::Reference<XAccessibleEventListener >& rxListener )
462 throw (uno::RuntimeException)
463 {
464 ThrowIfDisposed ();
465 if (rxListener.is())
466 {
467 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener );
468 if ( !nListenerCount )
469 {
470 // no listeners anymore
471 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
472 // and at least to us not firing any events anymore, in case somebody calls
473 // NotifyAccessibleEvent, again
474 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
475 mnClientId = 0;
476 }
477 }
478 }
479
480
481
482
483 //===== XServiceInfo ========================================================
484
485 ::rtl::OUString SAL_CALL
getImplementationName(void)486 AccessibleContextBase::getImplementationName (void)
487 throw (::com::sun::star::uno::RuntimeException)
488 {
489 ThrowIfDisposed ();
490 return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleContextBase"));
491 }
492
493
494
495
496 sal_Bool SAL_CALL
supportsService(const OUString & sServiceName)497 AccessibleContextBase::supportsService (const OUString& sServiceName)
498 throw (::com::sun::star::uno::RuntimeException)
499 {
500 ThrowIfDisposed ();
501 // Iterate over all supported service names and return true if on of them
502 // matches the given name.
503 uno::Sequence< ::rtl::OUString> aSupportedServices (
504 getSupportedServiceNames ());
505 for (int i=0; i<aSupportedServices.getLength(); i++)
506 if (sServiceName == aSupportedServices[i])
507 return sal_True;
508 return sal_False;
509 }
510
511
512
513
514 uno::Sequence< ::rtl::OUString> SAL_CALL
getSupportedServiceNames(void)515 AccessibleContextBase::getSupportedServiceNames (void)
516 throw (::com::sun::star::uno::RuntimeException)
517 {
518 ThrowIfDisposed ();
519 static const OUString sServiceNames[2] = {
520 OUString(RTL_CONSTASCII_USTRINGPARAM(
521 "com.sun.star.accessibility.Accessible")),
522 OUString(RTL_CONSTASCII_USTRINGPARAM(
523 "com.sun.star.accessibility.AccessibleContext"))
524 };
525 return uno::Sequence<OUString> (sServiceNames, 2);
526 }
527
528
529
530
531 //===== XTypeProvider =======================================================
532
533 uno::Sequence< ::com::sun::star::uno::Type>
getTypes(void)534 AccessibleContextBase::getTypes (void)
535 throw (::com::sun::star::uno::RuntimeException)
536 {
537 ThrowIfDisposed ();
538
539 // This class supports no interfaces on its own. Just return those
540 // supported by the base class.
541 return BaseClass::getTypes();
542 }
543
544
545
546
547 uno::Sequence<sal_Int8> SAL_CALL
getImplementationId(void)548 AccessibleContextBase::getImplementationId (void)
549 throw (::com::sun::star::uno::RuntimeException)
550 {
551 ThrowIfDisposed ();
552 static uno::Sequence<sal_Int8> aId;
553 if (aId.getLength() == 0)
554 {
555 ::osl::MutexGuard aGuard (maMutex);
556 aId.realloc (16);
557 rtl_createUuid ((sal_uInt8 *)aId.getArray(), 0, sal_True);
558 }
559 return aId;
560 }
561
562
563
564
565 //===== internal ============================================================
566
disposing(void)567 void SAL_CALL AccessibleContextBase::disposing (void)
568 {
569 SetState (AccessibleStateType::DEFUNC);
570
571 ::osl::MutexGuard aGuard (maMutex);
572
573 // Send a disposing to all listeners.
574 if ( mnClientId )
575 {
576 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
577 mnClientId = 0;
578 }
579 }
580
581
582
583
SetAccessibleDescription(const::rtl::OUString & rDescription,StringOrigin eDescriptionOrigin)584 void AccessibleContextBase::SetAccessibleDescription (
585 const ::rtl::OUString& rDescription,
586 StringOrigin eDescriptionOrigin)
587 throw (uno::RuntimeException)
588 {
589 if (eDescriptionOrigin < meDescriptionOrigin
590 || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription))
591 {
592 uno::Any aOldValue, aNewValue;
593 aOldValue <<= msDescription;
594 aNewValue <<= rDescription;
595
596 msDescription = rDescription;
597 meDescriptionOrigin = eDescriptionOrigin;
598
599 CommitChange(
600 AccessibleEventId::DESCRIPTION_CHANGED,
601 aNewValue,
602 aOldValue);
603 }
604 }
605
606
607
608
SetAccessibleName(const::rtl::OUString & rName,StringOrigin eNameOrigin)609 void AccessibleContextBase::SetAccessibleName (
610 const ::rtl::OUString& rName,
611 StringOrigin eNameOrigin)
612 throw (uno::RuntimeException)
613 {
614 if (eNameOrigin < meNameOrigin
615 || (eNameOrigin == meNameOrigin && msName != rName))
616 {
617 uno::Any aOldValue, aNewValue;
618 aOldValue <<= msName;
619 aNewValue <<= rName;
620
621 msName = rName;
622 meNameOrigin = eNameOrigin;
623
624 CommitChange(
625 AccessibleEventId::NAME_CHANGED,
626 aNewValue,
627 aOldValue);
628 }
629 }
630
631
632
633
CreateAccessibleDescription(void)634 ::rtl::OUString AccessibleContextBase::CreateAccessibleDescription (void)
635 throw (::com::sun::star::uno::RuntimeException)
636 {
637 return ::rtl::OUString::createFromAscii ("Empty Description");
638 }
639
640
641
642
CreateAccessibleName(void)643 ::rtl::OUString AccessibleContextBase::CreateAccessibleName (void)
644 throw (::com::sun::star::uno::RuntimeException)
645 {
646 return ::rtl::OUString::createFromAscii ("Empty Name");
647 }
648
649
650
651
CommitChange(sal_Int16 nEventId,const uno::Any & rNewValue,const uno::Any & rOldValue)652 void AccessibleContextBase::CommitChange (
653 sal_Int16 nEventId,
654 const uno::Any& rNewValue,
655 const uno::Any& rOldValue)
656 {
657 // Do not call FireEvent and do not even create the event object when no
658 // listener has been registered yet. Creating the event object can
659 // otherwise lead to a crash. See issue 93419 for details.
660 if (mnClientId != 0)
661 {
662 AccessibleEventObject aEvent (
663 static_cast<XAccessibleContext*>(this),
664 nEventId,
665 rNewValue,
666 rOldValue);
667
668 FireEvent (aEvent);
669 }
670 }
671
672
673
674
FireEvent(const AccessibleEventObject & aEvent)675 void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent)
676 {
677 if (mnClientId)
678 comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
679 }
680
681
682
683
ThrowIfDisposed(void)684 void AccessibleContextBase::ThrowIfDisposed (void)
685 throw (::com::sun::star::lang::DisposedException)
686 {
687 if (rBHelper.bDisposed || rBHelper.bInDispose)
688 {
689 OSL_TRACE ("Calling disposed object. Throwing exception:");
690 throw lang::DisposedException (
691 OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")),
692 static_cast<uno::XWeak*>(this));
693 }
694 }
695
696
697
IsDisposed(void)698 sal_Bool AccessibleContextBase::IsDisposed (void)
699 {
700 return (rBHelper.bDisposed || rBHelper.bInDispose);
701 }
702
703
704
SetAccessibleRole(sal_Int16 _nRole)705 void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole )
706 {
707 maRole = _nRole;
708 }
709
710
711 } // end of namespace accessibility
712