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 {
181 OSL_TRACE ("setting relation set");
182
183 // Try to emit some meaningful events indicating differing relations in
184 // both sets.
185 typedef std::pair<short int,short int> RD;
186 const RD aRelationDescriptors[] = {
187 RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED),
188 RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED),
189 RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED),
190 RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED),
191 RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED),
192 RD(AccessibleRelationType::INVALID, -1),
193 };
194 for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++)
195 if (mxRelationSet->containsRelation(aRelationDescriptors[i].first)
196 != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first))
197 CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any());
198
199 mxRelationSet = rxNewRelationSet;
200 }
201
202
203
204
205 //===== XAccessible =========================================================
206
207 uno::Reference< XAccessibleContext> SAL_CALL
getAccessibleContext(void)208 AccessibleContextBase::getAccessibleContext (void)
209 {
210 ThrowIfDisposed ();
211 return this;
212 }
213
214
215
216
217 //===== XAccessibleContext ==================================================
218
219 /** No children.
220 */
221 sal_Int32 SAL_CALL
getAccessibleChildCount(void)222 AccessibleContextBase::getAccessibleChildCount (void)
223 {
224 ThrowIfDisposed ();
225 return 0;
226 }
227
228
229
230
231 /** Forward the request to the shape. Return the requested shape or throw
232 an exception for a wrong index.
233 */
234 uno::Reference<XAccessible> SAL_CALL
getAccessibleChild(sal_Int32 nIndex)235 AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex)
236 {
237 ThrowIfDisposed ();
238 throw lang::IndexOutOfBoundsException (
239 ::rtl::OUString::createFromAscii ("no child with index " + nIndex),
240 NULL);
241 }
242
243
244
245
246 uno::Reference<XAccessible> SAL_CALL
getAccessibleParent(void)247 AccessibleContextBase::getAccessibleParent (void)
248 {
249 ThrowIfDisposed ();
250 return mxParent;
251 }
252
253
254
255
256 sal_Int32 SAL_CALL
getAccessibleIndexInParent(void)257 AccessibleContextBase::getAccessibleIndexInParent (void)
258 {
259 ThrowIfDisposed ();
260 // Use a simple but slow solution for now. Optimize later.
261
262 // Iterate over all the parent's children and search for this object.
263 if (mxParent.is())
264 {
265 uno::Reference<XAccessibleContext> xParentContext (
266 mxParent->getAccessibleContext());
267 if (xParentContext.is())
268 {
269 sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
270 for (sal_Int32 i=0; i<nChildCount; i++)
271 {
272 uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i));
273 if (xChild.is())
274 {
275 uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext();
276 if (xChildContext == (XAccessibleContext*)this)
277 return i;
278 }
279 }
280 }
281 }
282
283 // Return -1 to indicate that this object's parent does not know about the
284 // object.
285 return -1;
286 }
287
288
289
290
291 sal_Int16 SAL_CALL
getAccessibleRole(void)292 AccessibleContextBase::getAccessibleRole (void)
293 {
294 ThrowIfDisposed ();
295 return maRole;
296 }
297
298
299
300
301 ::rtl::OUString SAL_CALL
getAccessibleDescription(void)302 AccessibleContextBase::getAccessibleDescription (void)
303 {
304 ThrowIfDisposed ();
305
306 return msDescription;
307 }
308
309
310
311
312 OUString SAL_CALL
getAccessibleName(void)313 AccessibleContextBase::getAccessibleName (void)
314 {
315 ThrowIfDisposed ();
316
317 if (meNameOrigin == NotSet)
318 {
319 // Do not send an event because this is the first time it has been
320 // requested.
321 msName = CreateAccessibleName();
322 meNameOrigin = AutomaticallyCreated;
323 }
324
325 return msName;
326 }
327
328
329
330
331 /** Return a copy of the relation set.
332 */
333 uno::Reference<XAccessibleRelationSet> SAL_CALL
getAccessibleRelationSet(void)334 AccessibleContextBase::getAccessibleRelationSet (void)
335 {
336 ThrowIfDisposed ();
337
338 // Create a copy of the relation set and return it.
339 ::utl::AccessibleRelationSetHelper* pRelationSet =
340 static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get());
341 if (pRelationSet != NULL)
342 {
343 return uno::Reference<XAccessibleRelationSet> (
344 new ::utl::AccessibleRelationSetHelper (*pRelationSet));
345 }
346 else
347 return uno::Reference<XAccessibleRelationSet>(NULL);
348 }
349
350
351
352
353 /** Return a copy of the state set.
354 Possible states are:
355 ENABLED
356 SHOWING
357 VISIBLE
358 */
359 uno::Reference<XAccessibleStateSet> SAL_CALL
getAccessibleStateSet(void)360 AccessibleContextBase::getAccessibleStateSet (void)
361 {
362 ::utl::AccessibleStateSetHelper* pStateSet = NULL;
363
364 if (rBHelper.bDisposed)
365 {
366 // We are already disposed!
367 // Create a new state set that has only set the DEFUNC state.
368 pStateSet = new ::utl::AccessibleStateSetHelper ();
369 if (pStateSet != NULL)
370 pStateSet->AddState (AccessibleStateType::DEFUNC);
371 }
372 else
373 {
374 // Create a copy of the state set and return it.
375 pStateSet = static_cast< ::utl::AccessibleStateSetHelper*>(mxStateSet.get());
376
377 // Merge current focused state from edit engine.
378 #if 0
379 if (aState == AccessibleStateType::FOCUSED
380 && pStateSet != NULL
381 && mpText != NULL)
382 {
383 if (mpText->GetFocusedState ())
384 pStateSet->AddState (aState);
385 else
386 pStateSet->RemoveState (aState);
387 }
388 #endif
389 if (pStateSet != NULL)
390 pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet);
391 }
392
393 return uno::Reference<XAccessibleStateSet>(pStateSet);
394 }
395
396
397
398
399 lang::Locale SAL_CALL
getLocale(void)400 AccessibleContextBase::getLocale (void)
401 {
402 ThrowIfDisposed ();
403 // Delegate request to parent.
404 if (mxParent.is())
405 {
406 uno::Reference<XAccessibleContext> xParentContext (
407 mxParent->getAccessibleContext());
408 if (xParentContext.is())
409 return xParentContext->getLocale ();
410 }
411
412 // No locale and no parent. Therefore throw exception to indicate this
413 // cluelessness.
414 throw IllegalAccessibleComponentStateException ();
415 }
416
417
418
419
420 //===== XAccessibleEventListener ============================================
421
422 void SAL_CALL
addEventListener(const uno::Reference<XAccessibleEventListener> & rxListener)423 AccessibleContextBase::addEventListener (
424 const uno::Reference<XAccessibleEventListener >& rxListener)
425 {
426 if (rxListener.is())
427 {
428 if (rBHelper.bDisposed || rBHelper.bInDispose)
429 {
430 uno::Reference<uno::XInterface> x ((lang::XComponent *)this, uno::UNO_QUERY);
431 rxListener->disposing (lang::EventObject (x));
432 }
433 else
434 {
435 if (!mnClientId)
436 mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
437 comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener );
438 }
439 }
440 }
441
442
443
444
445 void SAL_CALL
removeEventListener(const uno::Reference<XAccessibleEventListener> & rxListener)446 AccessibleContextBase::removeEventListener (
447 const uno::Reference<XAccessibleEventListener >& rxListener )
448 {
449 ThrowIfDisposed ();
450 if (rxListener.is())
451 {
452 sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener );
453 if ( !nListenerCount )
454 {
455 // no listeners anymore
456 // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
457 // and at least to us not firing any events anymore, in case somebody calls
458 // NotifyAccessibleEvent, again
459 comphelper::AccessibleEventNotifier::revokeClient( mnClientId );
460 mnClientId = 0;
461 }
462 }
463 }
464
465
466
467
468 //===== XServiceInfo ========================================================
469
470 ::rtl::OUString SAL_CALL
getImplementationName(void)471 AccessibleContextBase::getImplementationName (void)
472 {
473 ThrowIfDisposed ();
474 return OUString(RTL_CONSTASCII_USTRINGPARAM("AccessibleContextBase"));
475 }
476
477
478
479
480 sal_Bool SAL_CALL
supportsService(const OUString & sServiceName)481 AccessibleContextBase::supportsService (const OUString& sServiceName)
482 {
483 ThrowIfDisposed ();
484 // Iterate over all supported service names and return true if on of them
485 // matches the given name.
486 uno::Sequence< ::rtl::OUString> aSupportedServices (
487 getSupportedServiceNames ());
488 for (int i=0; i<aSupportedServices.getLength(); i++)
489 if (sServiceName == aSupportedServices[i])
490 return sal_True;
491 return sal_False;
492 }
493
494
495
496
497 uno::Sequence< ::rtl::OUString> SAL_CALL
getSupportedServiceNames(void)498 AccessibleContextBase::getSupportedServiceNames (void)
499 {
500 ThrowIfDisposed ();
501 static const OUString sServiceNames[2] = {
502 OUString(RTL_CONSTASCII_USTRINGPARAM(
503 "com.sun.star.accessibility.Accessible")),
504 OUString(RTL_CONSTASCII_USTRINGPARAM(
505 "com.sun.star.accessibility.AccessibleContext"))
506 };
507 return uno::Sequence<OUString> (sServiceNames, 2);
508 }
509
510
511
512
513 //===== XTypeProvider =======================================================
514
515 uno::Sequence< ::com::sun::star::uno::Type>
getTypes(void)516 AccessibleContextBase::getTypes (void)
517 {
518 ThrowIfDisposed ();
519
520 // This class supports no interfaces on its own. Just return those
521 // supported by the base class.
522 return BaseClass::getTypes();
523 }
524
525
526
527
528 uno::Sequence<sal_Int8> SAL_CALL
getImplementationId(void)529 AccessibleContextBase::getImplementationId (void)
530 {
531 ThrowIfDisposed ();
532 static uno::Sequence<sal_Int8> aId;
533 if (aId.getLength() == 0)
534 {
535 ::osl::MutexGuard aGuard (maMutex);
536 aId.realloc (16);
537 rtl_createUuid ((sal_uInt8 *)aId.getArray(), 0, sal_True);
538 }
539 return aId;
540 }
541
542
543
544
545 //===== internal ============================================================
546
disposing(void)547 void SAL_CALL AccessibleContextBase::disposing (void)
548 {
549 SetState (AccessibleStateType::DEFUNC);
550
551 ::osl::MutexGuard aGuard (maMutex);
552
553 // Send a disposing to all listeners.
554 if ( mnClientId )
555 {
556 comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
557 mnClientId = 0;
558 }
559 }
560
561
562
563
SetAccessibleDescription(const::rtl::OUString & rDescription,StringOrigin eDescriptionOrigin)564 void AccessibleContextBase::SetAccessibleDescription (
565 const ::rtl::OUString& rDescription,
566 StringOrigin eDescriptionOrigin)
567 {
568 if (eDescriptionOrigin < meDescriptionOrigin
569 || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription))
570 {
571 uno::Any aOldValue, aNewValue;
572 aOldValue <<= msDescription;
573 aNewValue <<= rDescription;
574
575 msDescription = rDescription;
576 meDescriptionOrigin = eDescriptionOrigin;
577
578 CommitChange(
579 AccessibleEventId::DESCRIPTION_CHANGED,
580 aNewValue,
581 aOldValue);
582 }
583 }
584
585
586
587
SetAccessibleName(const::rtl::OUString & rName,StringOrigin eNameOrigin)588 void AccessibleContextBase::SetAccessibleName (
589 const ::rtl::OUString& rName,
590 StringOrigin eNameOrigin)
591 {
592 if (eNameOrigin < meNameOrigin
593 || (eNameOrigin == meNameOrigin && msName != rName))
594 {
595 uno::Any aOldValue, aNewValue;
596 aOldValue <<= msName;
597 aNewValue <<= rName;
598
599 msName = rName;
600 meNameOrigin = eNameOrigin;
601
602 CommitChange(
603 AccessibleEventId::NAME_CHANGED,
604 aNewValue,
605 aOldValue);
606 }
607 }
608
609
610
611
CreateAccessibleDescription(void)612 ::rtl::OUString AccessibleContextBase::CreateAccessibleDescription (void)
613 {
614 return ::rtl::OUString::createFromAscii ("Empty Description");
615 }
616
617
618
619
CreateAccessibleName(void)620 ::rtl::OUString AccessibleContextBase::CreateAccessibleName (void)
621 {
622 return ::rtl::OUString::createFromAscii ("Empty Name");
623 }
624
625
626
627
CommitChange(sal_Int16 nEventId,const uno::Any & rNewValue,const uno::Any & rOldValue)628 void AccessibleContextBase::CommitChange (
629 sal_Int16 nEventId,
630 const uno::Any& rNewValue,
631 const uno::Any& rOldValue)
632 {
633 // Do not call FireEvent and do not even create the event object when no
634 // listener has been registered yet. Creating the event object can
635 // otherwise lead to a crash. See issue 93419 for details.
636 if (mnClientId != 0)
637 {
638 AccessibleEventObject aEvent (
639 static_cast<XAccessibleContext*>(this),
640 nEventId,
641 rNewValue,
642 rOldValue);
643
644 FireEvent (aEvent);
645 }
646 }
647
648
649
650
FireEvent(const AccessibleEventObject & aEvent)651 void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent)
652 {
653 if (mnClientId)
654 comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
655 }
656
657
658
659
ThrowIfDisposed(void)660 void AccessibleContextBase::ThrowIfDisposed (void)
661 {
662 if (rBHelper.bDisposed || rBHelper.bInDispose)
663 {
664 OSL_TRACE ("Calling disposed object. Throwing exception:");
665 throw lang::DisposedException (
666 OUString(RTL_CONSTASCII_USTRINGPARAM("object has been already disposed")),
667 static_cast<uno::XWeak*>(this));
668 }
669 }
670
671
672
IsDisposed(void)673 sal_Bool AccessibleContextBase::IsDisposed (void)
674 {
675 return (rBHelper.bDisposed || rBHelper.bInDispose);
676 }
677
678
679
SetAccessibleRole(sal_Int16 _nRole)680 void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole )
681 {
682 maRole = _nRole;
683 }
684
685
686 } // end of namespace accessibility
687