1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <extended/accessiblelistbox.hxx>
21 #include <extended/accessiblelistboxentry.hxx>
22 #include <vcl/treelistbox.hxx>
23 #include <vcl/treelistentry.hxx>
24 #include <com/sun/star/awt/Point.hpp>
25 #include <com/sun/star/awt/Rectangle.hpp>
26 #include <com/sun/star/awt/Size.hpp>
27 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
28 #include <com/sun/star/accessibility/AccessibleRole.hpp>
29 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
30 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
31 #include <cppuhelper/supportsservice.hxx>
32 #include <vcl/svapp.hxx>
33 #include <toolkit/awt/vclxwindow.hxx>
34 #include <toolkit/helper/convert.hxx>
35 #include <unotools/accessiblestatesethelper.hxx>
38 namespace accessibility
42 // class AccessibleListBox -----------------------------------------------------
44 using namespace ::com::sun::star::accessibility
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star
;
52 AccessibleListBox::AccessibleListBox( SvTreeListBox
const & _rListBox
, const Reference
< XAccessible
>& _xParent
) :
54 VCLXAccessibleComponent( _rListBox
.GetWindowPeer() ),
59 AccessibleListBox::~AccessibleListBox()
63 // increment ref count to prevent double call of Dtor
64 osl_atomic_increment( &m_refCount
);
68 IMPLEMENT_FORWARD_XINTERFACE2(AccessibleListBox
, VCLXAccessibleComponent
, ImplHelper2
)
69 IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleListBox
, VCLXAccessibleComponent
, ImplHelper2
)
71 void AccessibleListBox::ProcessWindowEvent( const VclWindowEvent
& rVclWindowEvent
)
75 switch ( rVclWindowEvent
.GetId() )
77 case VclEventId::CheckboxToggle
:
79 if ( !getListBox() || !getListBox()->HasFocus() )
83 AccessibleListBoxEntry
* pCurOpEntry
= GetCurEventEntry(rVclWindowEvent
);
89 aValue
<<= AccessibleStateType::CHECKED
;
91 if ( getListBox()->GetCheckButtonState( pCurOpEntry
->GetSvLBoxEntry() ) == SvButtonState::Checked
)
93 pCurOpEntry
->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED
, uno::Any(), aValue
);
97 pCurOpEntry
->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED
, aValue
,uno::Any() );
102 case VclEventId::ListboxSelect
:
104 OSL_FAIL("Debug: Treelist shouldn't use VclEventId::ListboxSelect");
108 case VclEventId::ListboxTreeSelect
:
110 if ( getListBox() && getListBox()->HasFocus() )
112 AccessibleListBoxEntry
* pEntry
=static_cast< AccessibleListBoxEntry
* >(m_xFocusedChild
.get());
115 pEntry
->NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED
, Any(), Any() );
120 case VclEventId::ListboxTreeFocus
:
122 VclPtr
<SvTreeListBox
> pBox
= getListBox();
123 if( pBox
&& pBox
->HasFocus() )
126 SvTreeListEntry
* pEntry
= static_cast< SvTreeListEntry
* >( rVclWindowEvent
.GetData() );
129 AccessibleListBoxEntry
* pEntryFocus
=static_cast< AccessibleListBoxEntry
* >(m_xFocusedChild
.get());
130 if (pEntryFocus
&& pEntryFocus
->GetSvLBoxEntry() == pEntry
)
132 aNewValue
<<= m_xFocusedChild
;
133 NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, uno::Any(), aNewValue
);
138 aOldValue
<<= m_xFocusedChild
;
140 MAP_ENTRY::iterator mi
= m_mapEntry
.find(pEntry
);
141 if(mi
!= m_mapEntry
.end())
143 OSL_ASSERT(mi
->second
.get() != nullptr);
144 m_xFocusedChild
= mi
->second
;
148 AccessibleListBoxEntry
*pEntNew
= new AccessibleListBoxEntry( *getListBox(), pEntry
, nullptr );
149 m_xFocusedChild
= pEntNew
;
150 m_mapEntry
.emplace(pEntry
,pEntNew
);
153 aNewValue
<<= m_xFocusedChild
;
154 NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, aOldValue
, aNewValue
);
158 aNewValue
<<= AccessibleStateType::FOCUSED
;
159 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED
, uno::Any(), aNewValue
);
164 case VclEventId::ListboxItemRemoved
:
166 SvTreeListEntry
* pEntry
= static_cast< SvTreeListEntry
* >( rVclWindowEvent
.GetData() );
169 RemoveChildEntries(pEntry
);
173 // NULL means Clear()
174 for (auto const& entry
: m_mapEntry
)
178 aOldValue
<<= entry
.second
;
179 NotifyAccessibleEvent( AccessibleEventId::CHILD
, aOldValue
, aNewValue
);
187 case VclEventId::ItemExpanded
:
188 case VclEventId::ItemCollapsed
:
190 SvTreeListEntry
* pEntry
= static_cast< SvTreeListEntry
* >( rVclWindowEvent
.GetData() );
193 AccessibleListBoxEntry
* pAccListBoxEntry
=
194 new AccessibleListBoxEntry( *getListBox(), pEntry
, this );
195 Reference
< XAccessible
> xChild
= pAccListBoxEntry
;
196 const short nAccEvent
=
197 ( rVclWindowEvent
.GetId() == VclEventId::ItemExpanded
)
198 ? AccessibleEventId::LISTBOX_ENTRY_EXPANDED
199 : AccessibleEventId::LISTBOX_ENTRY_COLLAPSED
;
200 uno::Any aListBoxEntry
;
201 aListBoxEntry
<<= xChild
;
202 NotifyAccessibleEvent( nAccEvent
, Any(), aListBoxEntry
);
203 if ( getListBox() && getListBox()->HasFocus() )
205 NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, Any(), aListBoxEntry
);
211 VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent
);
216 AccessibleListBoxEntry
* AccessibleListBox::GetCurEventEntry( const VclWindowEvent
& rVclWindowEvent
)
218 SvTreeListEntry
* pEntry
= static_cast< SvTreeListEntry
* >( rVclWindowEvent
.GetData() );
220 pEntry
= getListBox()->GetCurEntry();
222 AccessibleListBoxEntry
* pEntryFocus
=static_cast< AccessibleListBoxEntry
* >(m_xFocusedChild
.get());
223 if (pEntryFocus
&& pEntry
&& pEntry
!= pEntryFocus
->GetSvLBoxEntry())
225 AccessibleListBoxEntry
*pAccCurOptionEntry
=nullptr;
226 MAP_ENTRY::iterator mi
= m_mapEntry
.find(pEntry
);
227 if (mi
!= m_mapEntry
.end())
229 pAccCurOptionEntry
= static_cast< AccessibleListBoxEntry
* >(mi
->second
.get());
233 pAccCurOptionEntry
=new AccessibleListBoxEntry( *getListBox(), pEntry
, nullptr );
234 std::pair
<MAP_ENTRY::iterator
, bool> pairMi
= m_mapEntry
.emplace(pAccCurOptionEntry
->GetSvLBoxEntry(), pAccCurOptionEntry
);
239 aNewValue
<<= mi
->second
;//xAcc
240 NotifyAccessibleEvent( AccessibleEventId::CHILD
, uno::Any(), aNewValue
);//Add
242 return pAccCurOptionEntry
;
250 void AccessibleListBox::RemoveChildEntries(SvTreeListEntry
* pEntry
)
252 MAP_ENTRY::iterator mi
= m_mapEntry
.find(pEntry
);
253 if ( mi
!= m_mapEntry
.end() )
257 aOldValue
<<= mi
->second
;
258 NotifyAccessibleEvent( AccessibleEventId::CHILD
, aOldValue
, aNewValue
);
260 m_mapEntry
.erase(mi
);
263 VclPtr
<SvTreeListBox
> pBox
= getListBox();
264 SvTreeListEntry
* pEntryChild
= pBox
->FirstChild(pEntry
);
267 RemoveChildEntries(pEntryChild
);
268 pEntryChild
= pEntryChild
->NextSibling();
273 void AccessibleListBox::ProcessWindowChildEvent( const VclWindowEvent
& rVclWindowEvent
)
275 switch ( rVclWindowEvent
.GetId() )
277 case VclEventId::WindowShow
:
278 case VclEventId::WindowHide
:
284 VCLXAccessibleComponent::ProcessWindowChildEvent( rVclWindowEvent
);
293 void SAL_CALL
AccessibleListBox::disposing()
295 ::osl::MutexGuard
aGuard( m_aMutex
);
298 VCLXAccessibleComponent::disposing();
304 OUString SAL_CALL
AccessibleListBox::getImplementationName()
306 return OUString( "com.sun.star.comp.svtools.AccessibleTreeListBox" );
309 Sequence
< OUString
> SAL_CALL
AccessibleListBox::getSupportedServiceNames()
311 return {"com.sun.star.accessibility.AccessibleContext",
312 "com.sun.star.accessibility.AccessibleComponent",
313 "com.sun.star.awt.AccessibleTreeListBox"};
316 sal_Bool SAL_CALL
AccessibleListBox::supportsService( const OUString
& _rServiceName
)
318 return cppu::supportsService(this, _rServiceName
);
323 Reference
< XAccessibleContext
> SAL_CALL
AccessibleListBox::getAccessibleContext( )
329 // XAccessibleContext
331 sal_Int32 SAL_CALL
AccessibleListBox::getAccessibleChildCount( )
333 ::comphelper::OExternalLockGuard
aGuard( this );
335 sal_Int32 nCount
= 0;
336 VclPtr
<SvTreeListBox
> pSvTreeListBox
= getListBox();
337 if ( pSvTreeListBox
)
338 nCount
= pSvTreeListBox
->GetLevelChildCount( nullptr );
343 Reference
< XAccessible
> SAL_CALL
AccessibleListBox::getAccessibleChild( sal_Int32 i
)
345 ::comphelper::OExternalLockGuard
aGuard( this );
347 SvTreeListEntry
* pEntry
= getListBox()->GetEntry(i
);
349 throw IndexOutOfBoundsException();
351 // Solution: Set the parameter of the parent to null to let entry determine the parent by itself
352 //return new AccessibleListBoxEntry( *getListBox(), pEntry, this );
353 return new AccessibleListBoxEntry( *getListBox(), pEntry
, nullptr );
356 Reference
< XAccessible
> SAL_CALL
AccessibleListBox::getAccessibleParent( )
358 ::osl::MutexGuard
aGuard( m_aMutex
);
364 sal_Int32
AccessibleListBox::GetRoleType()
367 SvTreeListEntry
* pEntry
= getListBox()->GetEntry(0);
370 if( pEntry
->HasChildrenOnDemand() || getListBox()->GetChildCount(pEntry
) > 0 )
377 bool bHasButtons
= (getListBox()->GetStyle() & WB_HASBUTTONS
)!=0;
378 if( !(getListBox()->GetTreeFlags() & SvTreeFlags::CHKBTN
) )
393 sal_Int16 SAL_CALL
AccessibleListBox::getAccessibleRole()
395 ::comphelper::OExternalLockGuard
aGuard( this );
399 SvTreeAccRoleType nType
= getListBox()->GetAllEntriesAccessibleRoleType();
400 if( nType
== SvTreeAccRoleType::TREE
)
401 return AccessibleRole::TREE
;
404 //o is: return AccessibleRole::TREE;
405 bool bHasButtons
= (getListBox()->GetStyle() & WB_HASBUTTONS
)!=0;
406 if(!bHasButtons
&& (getListBox()->GetTreeFlags() & SvTreeFlags::CHKBTN
))
407 return AccessibleRole::LIST
;
409 if (GetRoleType() == 0)
410 return AccessibleRole::LIST
;
412 return AccessibleRole::TREE
;
415 OUString SAL_CALL
AccessibleListBox::getAccessibleDescription( )
417 ::comphelper::OExternalLockGuard
aGuard( this );
419 return getListBox()->GetAccessibleDescription();
422 OUString SAL_CALL
AccessibleListBox::getAccessibleName( )
424 ::comphelper::OExternalLockGuard
aGuard( this );
426 return getListBox()->GetAccessibleName();
429 // XAccessibleSelection
431 void SAL_CALL
AccessibleListBox::selectAccessibleChild( sal_Int32 nChildIndex
)
433 ::comphelper::OExternalLockGuard
aGuard( this );
435 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( nChildIndex
);
437 throw IndexOutOfBoundsException();
439 getListBox()->Select( pEntry
);
442 sal_Bool SAL_CALL
AccessibleListBox::isAccessibleChildSelected( sal_Int32 nChildIndex
)
444 ::comphelper::OExternalLockGuard
aGuard( this );
446 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( nChildIndex
);
448 throw IndexOutOfBoundsException();
450 return getListBox()->IsSelected( pEntry
);
453 void SAL_CALL
AccessibleListBox::clearAccessibleSelection( )
455 ::comphelper::OExternalLockGuard
aGuard( this );
457 sal_Int32 nCount
= getListBox()->GetLevelChildCount( nullptr );
458 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
460 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( i
);
461 if ( getListBox()->IsSelected( pEntry
) )
462 getListBox()->Select( pEntry
, false );
466 void SAL_CALL
AccessibleListBox::selectAllAccessibleChildren( )
468 ::comphelper::OExternalLockGuard
aGuard( this );
470 sal_Int32 nCount
= getListBox()->GetLevelChildCount( nullptr );
471 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
473 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( i
);
474 if ( !getListBox()->IsSelected( pEntry
) )
475 getListBox()->Select( pEntry
);
479 sal_Int32 SAL_CALL
AccessibleListBox::getSelectedAccessibleChildCount( )
481 ::comphelper::OExternalLockGuard
aGuard( this );
483 return getListBox()->GetSelectionCount();
486 Reference
< XAccessible
> SAL_CALL
AccessibleListBox::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex
)
488 ::comphelper::OExternalLockGuard
aGuard( this );
490 if ( nSelectedChildIndex
< 0 || nSelectedChildIndex
>= getSelectedAccessibleChildCount() )
491 throw IndexOutOfBoundsException();
493 Reference
< XAccessible
> xChild
;
494 sal_Int32 nSelCount
= 0;
495 sal_Int32 nCount
= getListBox()->GetLevelChildCount( nullptr );
496 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
498 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( i
);
499 if ( getListBox()->IsSelected( pEntry
) )
502 if ( nSelCount
== ( nSelectedChildIndex
+ 1 ) )
504 // Solution: Set the parameter of the parent to null to let entry determine the parent by itself
505 //xChild = new AccessibleListBoxEntry( *getListBox(), pEntry, this );
506 xChild
= new AccessibleListBoxEntry( *getListBox(), pEntry
, nullptr );
514 void SAL_CALL
AccessibleListBox::deselectAccessibleChild( sal_Int32 nSelectedChildIndex
)
516 ::comphelper::OExternalLockGuard
aGuard( this );
518 SvTreeListEntry
* pEntry
= getListBox()->GetEntry( nSelectedChildIndex
);
520 throw IndexOutOfBoundsException();
522 getListBox()->Select( pEntry
, false );
525 void AccessibleListBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper
& rStateSet
)
527 VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet
);
528 if ( getListBox() && isAlive() )
530 rStateSet
.AddState( AccessibleStateType::FOCUSABLE
);
531 rStateSet
.AddState( AccessibleStateType::MANAGES_DESCENDANTS
);
532 if ( getListBox()->GetSelectionMode() == SelectionMode::Multiple
)
533 rStateSet
.AddState( AccessibleStateType::MULTI_SELECTABLE
);
537 VclPtr
< SvTreeListBox
> AccessibleListBox::getListBox() const
539 return GetAs
< SvTreeListBox
>();
542 }// namespace accessibility
545 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */