diff options
Diffstat (limited to 'accessibility/source/extended/accessiblelistbox.cxx')
-rw-r--r-- | accessibility/source/extended/accessiblelistbox.cxx | 530 |
1 files changed, 530 insertions, 0 deletions
diff --git a/accessibility/source/extended/accessiblelistbox.cxx b/accessibility/source/extended/accessiblelistbox.cxx new file mode 100644 index 000000000..462f28664 --- /dev/null +++ b/accessibility/source/extended/accessiblelistbox.cxx @@ -0,0 +1,530 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <extended/accessiblelistbox.hxx> +#include <extended/accessiblelistboxentry.hxx> +#include <vcl/toolkit/treelistbox.hxx> +#include <vcl/toolkit/treelistentry.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> + + +namespace accessibility +{ + + + // class AccessibleListBox ----------------------------------------------------- + + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + + + // Ctor() and Dtor() + + AccessibleListBox::AccessibleListBox( SvTreeListBox const & _rListBox, const Reference< XAccessible >& _xParent ) : + + VCLXAccessibleComponent( _rListBox.GetWindowPeer() ), + m_xParent( _xParent ) + { + } + + AccessibleListBox::~AccessibleListBox() + { + if ( isAlive() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + IMPLEMENT_FORWARD_XINTERFACE2(AccessibleListBox, VCLXAccessibleComponent, ImplHelper2) + IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleListBox, VCLXAccessibleComponent, ImplHelper2) + + void AccessibleListBox::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + if ( !isAlive() ) + return; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::CheckboxToggle : + { + if ( !getListBox() || !getListBox()->HasFocus() ) + { + return; + } + AccessibleListBoxEntry* pCurOpEntry = GetCurEventEntry(rVclWindowEvent); + if(!pCurOpEntry) + { + return ; + } + uno::Any aValue; + aValue <<= AccessibleStateType::CHECKED; + + if ( getListBox()->GetCheckButtonState( pCurOpEntry->GetSvLBoxEntry() ) == SvButtonState::Checked ) + { + pCurOpEntry->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), aValue ); + } + else + { + pCurOpEntry->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aValue,uno::Any() ); + } + break; + } + + case VclEventId::ListboxSelect : + { + OSL_FAIL("Debug: Treelist shouldn't use VclEventId::ListboxSelect"); + break; + } + + case VclEventId::ListboxTreeSelect: + { + if ( getListBox() && getListBox()->HasFocus() ) + { + AccessibleListBoxEntry* pEntry =static_cast< AccessibleListBoxEntry* >(m_xFocusedChild.get()); + if (pEntry) + { + pEntry->NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + } + } + } + break; + case VclEventId::ListboxTreeFocus: + { + VclPtr<SvTreeListBox> pBox = getListBox(); + if( pBox && pBox->HasFocus() ) + { + uno::Any aNewValue; + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + AccessibleListBoxEntry* pEntryFocus =static_cast< AccessibleListBoxEntry* >(m_xFocusedChild.get()); + if (pEntryFocus && pEntryFocus->GetSvLBoxEntry() == pEntry) + { + aNewValue <<= m_xFocusedChild; + NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, uno::Any(), aNewValue ); + return ; + } + + uno::Any aOldValue; + aOldValue <<= m_xFocusedChild; + + m_xFocusedChild.set(implGetAccessible(*pEntry)); + + aNewValue <<= m_xFocusedChild; + NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue ); + } + else + { + aNewValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), aNewValue ); + } + } + } + break; + case VclEventId::ListboxItemRemoved: + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + RemoveChildEntries(pEntry); + } + else + { + // NULL means Clear() + for (auto const& entry : m_mapEntry) + { + uno::Any aNewValue; + uno::Any aOldValue; + aOldValue <<= uno::Reference<XAccessible>(entry.second); + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + for (auto const& entry : m_mapEntry) + { // release references ... + entry.second->dispose(); + } + m_mapEntry.clear(); + } + } + break; + + // #i92103# + case VclEventId::ItemExpanded : + case VclEventId::ItemCollapsed : + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + Reference<XAccessible> const xChild(implGetAccessible(*pEntry)); + const short nAccEvent = + ( rVclWindowEvent.GetId() == VclEventId::ItemExpanded ) + ? AccessibleEventId::LISTBOX_ENTRY_EXPANDED + : AccessibleEventId::LISTBOX_ENTRY_COLLAPSED; + uno::Any aListBoxEntry; + aListBoxEntry <<= xChild; + NotifyAccessibleEvent( nAccEvent, Any(), aListBoxEntry ); + if ( getListBox() && getListBox()->HasFocus() ) + { + NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), aListBoxEntry ); + } + } + } + break; + default: + VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent); + } + } + + AccessibleListBoxEntry* AccessibleListBox::GetCurEventEntry( const VclWindowEvent& rVclWindowEvent ) + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( !pEntry ) + pEntry = getListBox()->GetCurEntry(); + + AccessibleListBoxEntry* pEntryFocus =static_cast< AccessibleListBoxEntry* >(m_xFocusedChild.get()); + if (pEntryFocus && pEntry && pEntry != pEntryFocus->GetSvLBoxEntry()) + { + AccessibleListBoxEntry *const pAccCurOptionEntry = implGetAccessible(*pEntry).get(); + uno::Any aNewValue; + aNewValue <<= uno::Reference<XAccessible>(pAccCurOptionEntry); + NotifyAccessibleEvent( AccessibleEventId::CHILD, uno::Any(), aNewValue );//Add + + return pAccCurOptionEntry; + } + else + { + return pEntryFocus; + } + } + + void AccessibleListBox::RemoveChildEntries(SvTreeListEntry* pEntry) + { + MAP_ENTRY::iterator mi = m_mapEntry.find(pEntry); + if ( mi != m_mapEntry.end() ) + { + uno::Any aNewValue; + uno::Any aOldValue; + aOldValue <<= uno::Reference<XAccessible>(mi->second); + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + + m_mapEntry.erase(mi); + } + + VclPtr<SvTreeListBox> pBox = getListBox(); + SvTreeListEntry* pEntryChild = pBox->FirstChild(pEntry); + while (pEntryChild) + { + RemoveChildEntries(pEntryChild); + pEntryChild = pEntryChild->NextSibling(); + } + } + + + void AccessibleListBox::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent ) + { + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowShow: + case VclEventId::WindowHide: + { + } + break; + default: + { + VCLXAccessibleComponent::ProcessWindowChildEvent( rVclWindowEvent ); + } + break; + } + } + + + // XComponent + + void SAL_CALL AccessibleListBox::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + m_mapEntry.clear(); + VCLXAccessibleComponent::disposing(); + m_xParent = nullptr; + } + + // XServiceInfo + + OUString SAL_CALL AccessibleListBox::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTreeListBox"; + } + + Sequence< OUString > SAL_CALL AccessibleListBox::getSupportedServiceNames() + { + return {"com.sun.star.accessibility.AccessibleContext", + "com.sun.star.accessibility.AccessibleComponent", + "com.sun.star.awt.AccessibleTreeListBox"}; + } + + sal_Bool SAL_CALL AccessibleListBox::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + // XAccessible + + Reference< XAccessibleContext > SAL_CALL AccessibleListBox::getAccessibleContext( ) + { + ensureAlive(); + return this; + } + + // XAccessibleContext + + sal_Int32 SAL_CALL AccessibleListBox::getAccessibleChildCount( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + sal_Int32 nCount = 0; + VclPtr<SvTreeListBox> pSvTreeListBox = getListBox(); + if ( pSvTreeListBox ) + nCount = pSvTreeListBox->GetLevelChildCount( nullptr ); + + return nCount; + } + + Reference< XAccessible > SAL_CALL AccessibleListBox::getAccessibleChild( sal_Int32 i ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + SvTreeListEntry* pEntry = getListBox()->GetEntry(i); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + // Solution: Set the parameter of the parent to null to let entry determine the parent by itself + //return new AccessibleListBoxEntry( *getListBox(), pEntry, this ); + //return new AccessibleListBoxEntry( *getListBox(), pEntry, nullptr ); + return implGetAccessible(*pEntry); + } + + Reference< XAccessible > SAL_CALL AccessibleListBox::getAccessibleParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ensureAlive(); + return m_xParent; + } + + sal_Int32 AccessibleListBox::GetRoleType() const + { + sal_Int32 nCase = 0; + SvTreeListEntry* pEntry = getListBox()->GetEntry(0); + if ( pEntry ) + { + if( pEntry->HasChildrenOnDemand() || getListBox()->GetChildCount(pEntry) > 0 ) + { + nCase = 1; + return nCase; + } + } + + bool bHasButtons = (getListBox()->GetStyle() & WB_HASBUTTONS)!=0; + if( !(getListBox()->GetTreeFlags() & SvTreeFlags::CHKBTN) ) + { + if( bHasButtons ) + nCase = 1; + } + else + { + if( bHasButtons ) + nCase = 2; + else + nCase = 3; + } + return nCase; + } + + sal_Int16 SAL_CALL AccessibleListBox::getAccessibleRole() + { + ::comphelper::OExternalLockGuard aGuard( this ); + + //o is: return AccessibleRole::TREE; + bool bHasButtons = (getListBox()->GetStyle() & WB_HASBUTTONS)!=0; + if(!bHasButtons && (getListBox()->GetTreeFlags() & SvTreeFlags::CHKBTN)) + return AccessibleRole::LIST; + else + if (GetRoleType() == 0) + return AccessibleRole::LIST; + else + return AccessibleRole::TREE; + } + + OUString SAL_CALL AccessibleListBox::getAccessibleDescription( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + return getListBox()->GetAccessibleDescription(); + } + + OUString SAL_CALL AccessibleListBox::getAccessibleName( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + return getListBox()->GetAccessibleName(); + } + + // XAccessibleSelection + + void SAL_CALL AccessibleListBox::selectAccessibleChild( sal_Int32 nChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + SvTreeListEntry* pEntry = getListBox()->GetEntry( nChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + getListBox()->Select( pEntry ); + } + + sal_Bool SAL_CALL AccessibleListBox::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + SvTreeListEntry* pEntry = getListBox()->GetEntry( nChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + return getListBox()->IsSelected( pEntry ); + } + + void SAL_CALL AccessibleListBox::clearAccessibleSelection( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = getListBox()->GetEntry( i ); + if ( getListBox()->IsSelected( pEntry ) ) + getListBox()->Select( pEntry, false ); + } + } + + void SAL_CALL AccessibleListBox::selectAllAccessibleChildren( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = getListBox()->GetEntry( i ); + if ( !getListBox()->IsSelected( pEntry ) ) + getListBox()->Select( pEntry ); + } + } + + sal_Int32 SAL_CALL AccessibleListBox::getSelectedAccessibleChildCount( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + return getListBox()->GetSelectionCount(); + } + + Reference< XAccessible > SAL_CALL AccessibleListBox::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild; + sal_Int32 nSelCount= 0; + sal_Int32 nCount = getListBox()->GetLevelChildCount( nullptr ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = getListBox()->GetEntry( i ); + if ( getListBox()->IsSelected( pEntry ) ) + ++nSelCount; + + if ( nSelCount == ( nSelectedChildIndex + 1 ) ) + { + // Solution: Set the parameter of the parent to null to let entry determine the parent by itself + //xChild = new AccessibleListBoxEntry( *getListBox(), pEntry, this ); + //xChild = new AccessibleListBoxEntry( *getListBox(), pEntry, nullptr ); + xChild = implGetAccessible(*pEntry).get(); + break; + } + } + + return xChild; + } + + void SAL_CALL AccessibleListBox::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + SvTreeListEntry* pEntry = getListBox()->GetEntry( nSelectedChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + getListBox()->Select( pEntry, false ); + } + + void AccessibleListBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) + { + VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet ); + if ( getListBox() && isAlive() ) + { + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + if ( getListBox()->GetSelectionMode() == SelectionMode::Multiple ) + rStateSet.AddState( AccessibleStateType::MULTI_SELECTABLE ); + } + } + + rtl::Reference<AccessibleListBoxEntry> AccessibleListBox::implGetAccessible(SvTreeListEntry & rEntry) + { + rtl::Reference<AccessibleListBoxEntry> pAccessible; + auto const it = m_mapEntry.find(&rEntry); + if (it != m_mapEntry.end()) + { + pAccessible = it->second; + } + else + { + pAccessible = new AccessibleListBoxEntry(*getListBox(), rEntry, *this); + m_mapEntry.emplace(&rEntry, pAccessible); + } + assert(pAccessible.is()); + return pAccessible; + } + + VclPtr< SvTreeListBox > AccessibleListBox::getListBox() const + { + return GetAs< SvTreeListBox >(); + } + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |