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