xref: /trunk/main/editeng/source/accessibility/AccessibleContextBase.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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