diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /accessibility/source/extended | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessibility/source/extended')
29 files changed, 12175 insertions, 0 deletions
diff --git a/accessibility/source/extended/AccessibleBrowseBox.cxx b/accessibility/source/extended/AccessibleBrowseBox.cxx new file mode 100644 index 000000000..6bc4865c6 --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBox.cxx @@ -0,0 +1,314 @@ +/* -*- 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/AccessibleBrowseBox.hxx> +#include <extended/AccessibleBrowseBoxTable.hxx> +#include <extended/AccessibleBrowseBoxHeaderBar.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <utility> +#include <vcl/accessibletableprovider.hxx> +#include <toolkit/helper/convert.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <sal/types.h> + + +namespace accessibility +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; + +// Ctor/Dtor/disposing + +AccessibleBrowseBox::AccessibleBrowseBox( + const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, const css::uno::Reference< css::accessibility::XAccessible >& _rxCreator, + ::vcl::IAccessibleTableProvider& _rBrowseBox ) + : AccessibleBrowseBoxBase( _rxParent, _rBrowseBox,nullptr, AccessibleBrowseBoxObjType::BrowseBox ), + m_aCreator(_rxCreator) +{ + m_xFocusWindow = VCLUnoHelper::GetInterface(mpBrowseBox->GetWindowInstance()); +} + +void AccessibleBrowseBox::setCreator( const css::uno::Reference< css::accessibility::XAccessible >& _rxCreator ) +{ +#if OSL_DEBUG_LEVEL > 0 + css::uno::Reference< css::accessibility::XAccessible > xCreator(m_aCreator); + OSL_ENSURE( !xCreator.is(), "extended/AccessibleBrowseBox::setCreator: creator already set!" ); +#endif + m_aCreator = _rxCreator; +} + + +AccessibleBrowseBox::~AccessibleBrowseBox() +{ +} + + +void SAL_CALL AccessibleBrowseBox::disposing() +{ + ::osl::MutexGuard aGuard( getMutex() ); + + m_aCreator.clear(); + + if ( mxTable.is() ) + { + mxTable->dispose(); + mxTable.clear(); + } + if ( mxRowHeaderBar.is() ) + { + mxRowHeaderBar->dispose(); + mxRowHeaderBar.clear(); + } + if ( mxColumnHeaderBar.is() ) + { + mxColumnHeaderBar->dispose(); + mxColumnHeaderBar.clear(); + } + + AccessibleBrowseBoxBase::disposing(); +} + + +// css::accessibility::XAccessibleContext + +sal_Int32 SAL_CALL AccessibleBrowseBox::getAccessibleChildCount() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return vcl::BBINDEX_FIRSTCONTROL + mpBrowseBox->GetAccessibleControlCount(); +} + + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +AccessibleBrowseBox::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + css::uno::Reference< css::accessibility::XAccessible > xRet; + if( nChildIndex >= 0 ) + { + if( nChildIndex < vcl::BBINDEX_FIRSTCONTROL ) + xRet = implGetFixedChild( nChildIndex ); + else + { + // additional controls + nChildIndex -= vcl::BBINDEX_FIRSTCONTROL; + if( nChildIndex < mpBrowseBox->GetAccessibleControlCount() ) + xRet = mpBrowseBox->CreateAccessibleControl( nChildIndex ); + } + } + + if( !xRet.is() ) + throw lang::IndexOutOfBoundsException(); + return xRet; +} + +// css::accessibility::XAccessibleComponent + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +AccessibleBrowseBox::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + css::uno::Reference< css::accessibility::XAccessible > xChild; + sal_Int32 nIndex = 0; + if( mpBrowseBox->ConvertPointToControlIndex( nIndex, VCLPoint( rPoint ) ) ) + xChild = mpBrowseBox->CreateAccessibleControl( nIndex ); + else + { + // try whether point is in one of the fixed children + // (table, header bars, corner control) + Point aPoint( VCLPoint( rPoint ) ); + for( nIndex = 0; (nIndex < vcl::BBINDEX_FIRSTCONTROL) && !xChild.is(); ++nIndex ) + { + css::uno::Reference< css::accessibility::XAccessible > xCurrChild( implGetFixedChild( nIndex ) ); + css::uno::Reference< css::accessibility::XAccessibleComponent > + xCurrChildComp( xCurrChild, uno::UNO_QUERY ); + + if( xCurrChildComp.is() && + VCLRectangle( xCurrChildComp->getBounds() ).Contains( aPoint ) ) + xChild = xCurrChild; + } + } + return xChild; +} + + +void SAL_CALL AccessibleBrowseBox::grabFocus() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + mpBrowseBox->GrabFocus(); +} + +// XServiceInfo + +OUString SAL_CALL AccessibleBrowseBox::getImplementationName() +{ + return "com.sun.star.comp.svtools.AccessibleBrowseBox"; +} + + +// internal virtual methods + +tools::Rectangle AccessibleBrowseBox::implGetBoundingBox() +{ + vcl::Window* pParent = mpBrowseBox->GetAccessibleParentWindow(); + OSL_ENSURE( pParent, "implGetBoundingBox - missing parent window" ); + return mpBrowseBox->GetWindowExtentsRelative( pParent ); +} + + +tools::Rectangle AccessibleBrowseBox::implGetBoundingBoxOnScreen() +{ + return mpBrowseBox->GetWindowExtentsRelative( nullptr ); +} + +// internal helper methods +css::uno::Reference< css::accessibility::XAccessible > AccessibleBrowseBox::implGetTable() +{ + if( !mxTable.is() ) + { + mxTable = createAccessibleTable(); + + } + return mxTable; +} + +css::uno::Reference< css::accessibility::XAccessible > +AccessibleBrowseBox::implGetHeaderBar(AccessibleBrowseBoxObjType eObjType) +{ + css::uno::Reference< css::accessibility::XAccessible > xRet; + rtl::Reference< AccessibleBrowseBoxHeaderBar >* pxMember = nullptr; + + if( eObjType == AccessibleBrowseBoxObjType::RowHeaderBar ) + pxMember = &mxRowHeaderBar; + else if( eObjType == AccessibleBrowseBoxObjType::ColumnHeaderBar ) + pxMember = &mxColumnHeaderBar; + + if( pxMember ) + { + if( !pxMember->is() ) + { + rtl::Reference<AccessibleBrowseBoxHeaderBar> pHeaderBar = new AccessibleBrowseBoxHeaderBar( + m_aCreator, *mpBrowseBox, eObjType ); + *pxMember = pHeaderBar; + } + xRet = pxMember->get(); + } + return xRet; +} + +css::uno::Reference< css::accessibility::XAccessible > +AccessibleBrowseBox::implGetFixedChild( sal_Int32 nChildIndex ) +{ + css::uno::Reference< css::accessibility::XAccessible > xRet; + switch( nChildIndex ) + { + case vcl::BBINDEX_COLUMNHEADERBAR: + xRet = implGetHeaderBar( AccessibleBrowseBoxObjType::ColumnHeaderBar ); + break; + case vcl::BBINDEX_ROWHEADERBAR: + xRet = implGetHeaderBar( AccessibleBrowseBoxObjType::RowHeaderBar ); + break; + case vcl::BBINDEX_TABLE: + xRet = implGetTable(); + break; + } + return xRet; +} + +rtl::Reference<AccessibleBrowseBoxTable> AccessibleBrowseBox::createAccessibleTable() +{ + css::uno::Reference< css::accessibility::XAccessible > xCreator(m_aCreator); + OSL_ENSURE( xCreator.is(), "extended/AccessibleBrowseBox::createAccessibleTable: my creator died - how this?" ); + return new AccessibleBrowseBoxTable( xCreator, *mpBrowseBox ); +} + +void AccessibleBrowseBox::commitTableEvent(sal_Int16 _nEventId,const Any& _rNewValue,const Any& _rOldValue) +{ + if ( mxTable.is() ) + { + mxTable->commitEvent(_nEventId,_rNewValue,_rOldValue); + } +} + +void AccessibleBrowseBox::commitHeaderBarEvent( sal_Int16 _nEventId, + const Any& _rNewValue, + const Any& _rOldValue,bool _bColumnHeaderBar) +{ + rtl::Reference< AccessibleBrowseBoxHeaderBar >& xHeaderBar = _bColumnHeaderBar ? mxColumnHeaderBar : mxRowHeaderBar; + if ( xHeaderBar.is() ) + xHeaderBar->commitEvent(_nEventId,_rNewValue,_rOldValue); +} + + +// = AccessibleBrowseBoxAccess + +AccessibleBrowseBoxAccess::AccessibleBrowseBoxAccess( css::uno::Reference< css::accessibility::XAccessible > _xParent, ::vcl::IAccessibleTableProvider& _rBrowseBox ) + :m_xParent(std::move( _xParent )) + ,m_rBrowseBox( _rBrowseBox ) +{ +} + + +AccessibleBrowseBoxAccess::~AccessibleBrowseBoxAccess() +{ +} + + +void AccessibleBrowseBoxAccess::dispose() +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_xContext.is()) + { + m_xContext->dispose(); + m_xContext.clear(); + } +} + + +css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL AccessibleBrowseBoxAccess::getAccessibleContext() +{ + std::unique_lock aGuard( m_aMutex ); + + // if the context died meanwhile (there is no listener, so it won't tell us explicitly when this happens), + // then reset and re-create. + if ( m_xContext.is() && !m_xContext->isAlive() ) + m_xContext = nullptr; + + if ( !m_xContext.is() ) + m_xContext = new AccessibleBrowseBox( m_xParent, this, m_rBrowseBox ); + + return m_xContext; +} + + + +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxBase.cxx b/accessibility/source/extended/AccessibleBrowseBoxBase.cxx new file mode 100644 index 000000000..4b16293e5 --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxBase.cxx @@ -0,0 +1,561 @@ +/* -*- 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/AccessibleBrowseBoxBase.hxx> +#include <toolkit/helper/convert.hxx> +#include <utility> +#include <vcl/accessibletableprovider.hxx> +#include <cppuhelper/supportsservice.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/accessibility/IllegalAccessibleComponentStateException.hpp> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vcl/window.hxx> +#include <vcl/svapp.hxx> +#include <sal/log.hxx> + + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; + + +namespace accessibility { + +using namespace com::sun::star::accessibility::AccessibleStateType; + + +// Ctor/Dtor/disposing + +AccessibleBrowseBoxBase::AccessibleBrowseBoxBase( + css::uno::Reference< css::accessibility::XAccessible > xParent, + ::vcl::IAccessibleTableProvider& rBrowseBox, + css::uno::Reference< css::awt::XWindow > _xFocusWindow, + AccessibleBrowseBoxObjType eObjType ) : + AccessibleBrowseBoxImplHelper( m_aMutex ), + mxParent(std::move( xParent )), + mpBrowseBox( &rBrowseBox ), + m_xFocusWindow(std::move(_xFocusWindow)), + maName( rBrowseBox.GetAccessibleObjectName( eObjType ) ), + maDescription( rBrowseBox.GetAccessibleObjectDescription( eObjType ) ), + meObjType( eObjType ), + m_aClientId(0) +{ + if ( m_xFocusWindow.is() ) + m_xFocusWindow->addFocusListener( this ); +} + +AccessibleBrowseBoxBase::AccessibleBrowseBoxBase( + css::uno::Reference< css::accessibility::XAccessible > rxParent, + ::vcl::IAccessibleTableProvider& rBrowseBox, + css::uno::Reference< css::awt::XWindow > _xFocusWindow, + AccessibleBrowseBoxObjType eObjType, + OUString rName, + OUString rDescription ) : + AccessibleBrowseBoxImplHelper( m_aMutex ), + mxParent(std::move( rxParent )), + mpBrowseBox( &rBrowseBox ), + m_xFocusWindow(std::move(_xFocusWindow)), + maName(std::move( rName )), + maDescription(std::move( rDescription )), + meObjType( eObjType ), + m_aClientId(0) +{ + if ( m_xFocusWindow.is() ) + m_xFocusWindow->addFocusListener( this ); +} + +AccessibleBrowseBoxBase::~AccessibleBrowseBoxBase() +{ + if( isAlive() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +void SAL_CALL AccessibleBrowseBoxBase::disposing() +{ + ::osl::MutexGuard aGuard( getMutex() ); + if ( m_xFocusWindow.is() ) + { + SolarMutexGuard aSolarGuard; + m_xFocusWindow->removeFocusListener( this ); + } + + if ( getClientId( ) ) + { + AccessibleEventNotifier::TClientId nId( getClientId( ) ); + setClientId( 0 ); + AccessibleEventNotifier::revokeClientNotifyDisposing( nId, *this ); + } + + mxParent = nullptr; + mpBrowseBox = nullptr; +} + +// css::accessibility::XAccessibleContext + +Reference< css::accessibility::XAccessible > SAL_CALL AccessibleBrowseBoxBase::getAccessibleParent() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return mxParent; +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxBase::getAccessibleIndexInParent() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + + // -1 for child not found/no parent (according to specification) + sal_Int32 nRet = -1; + + css::uno::Reference< uno::XInterface > xMeMyselfAndI( static_cast< css::accessibility::XAccessibleContext* >( this ), uno::UNO_QUERY ); + + // iterate over parent's children and search for this object + if( mxParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > + xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + css::uno::Reference< uno::XInterface > xChild; + + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; ++nChild ) + { + xChild.set(xParentContext->getAccessibleChild( nChild ), css::uno::UNO_QUERY); + + if ( xMeMyselfAndI.get() == xChild.get() ) + { + nRet = nChild; + break; + } + } + } + } + return nRet; +} + +OUString SAL_CALL AccessibleBrowseBoxBase::getAccessibleDescription() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return maDescription; +} + +OUString SAL_CALL AccessibleBrowseBoxBase::getAccessibleName() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return maName; +} + +Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL +AccessibleBrowseBoxBase::getAccessibleRelationSet() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + // BrowseBox does not have relations. + return new utl::AccessibleRelationSetHelper; +} + +Reference< css::accessibility::XAccessibleStateSet > SAL_CALL +AccessibleBrowseBoxBase::getAccessibleStateSet() +{ + SolarMethodGuard aGuard( getMutex() ); + // don't check whether alive -> StateSet may contain DEFUNC + return implCreateStateSetHelper(); +} + +lang::Locale SAL_CALL AccessibleBrowseBoxBase::getLocale() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + if( mxParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > + xParentContext( mxParent->getAccessibleContext() ); + if( xParentContext.is() ) + return xParentContext->getLocale(); + } + throw IllegalAccessibleComponentStateException(); +} + +// css::accessibility::XAccessibleComponent + +sal_Bool SAL_CALL AccessibleBrowseBoxBase::containsPoint( const css::awt::Point& rPoint ) +{ + return tools::Rectangle( Point(), getBoundingBox().GetSize() ).Contains( VCLPoint( rPoint ) ); +} + +awt::Rectangle SAL_CALL AccessibleBrowseBoxBase::getBounds() +{ + return AWTRectangle( getBoundingBox() ); +} + +awt::Point SAL_CALL AccessibleBrowseBoxBase::getLocation() +{ + return AWTPoint( getBoundingBox().TopLeft() ); +} + +awt::Point SAL_CALL AccessibleBrowseBoxBase::getLocationOnScreen() +{ + return AWTPoint( getBoundingBoxOnScreen().TopLeft() ); +} + +awt::Size SAL_CALL AccessibleBrowseBoxBase::getSize() +{ + return AWTSize( getBoundingBox().GetSize() ); +} + +void SAL_CALL AccessibleBrowseBoxBase::focusGained( const css::awt::FocusEvent& ) +{ + com::sun::star::uno::Any aFocused; + com::sun::star::uno::Any aEmpty; + aFocused <<= FOCUSED; + + commitEvent(AccessibleEventId::STATE_CHANGED,aFocused,aEmpty); +} + + +void SAL_CALL AccessibleBrowseBoxBase::focusLost( const css::awt::FocusEvent& ) +{ + com::sun::star::uno::Any aFocused; + com::sun::star::uno::Any aEmpty; + aFocused <<= FOCUSED; + + commitEvent(AccessibleEventId::STATE_CHANGED,aEmpty,aFocused); +} +// css::accessibility::XAccessibleEventBroadcaster + +void SAL_CALL AccessibleBrowseBoxBase::addAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener>& _rxListener ) +{ + if ( _rxListener.is() ) + { + ::osl::MutexGuard aGuard( getMutex() ); + if ( !getClientId( ) ) + setClientId( AccessibleEventNotifier::registerClient( ) ); + + AccessibleEventNotifier::addEventListener( getClientId( ), _rxListener ); + } +} + +void SAL_CALL AccessibleBrowseBoxBase::removeAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener>& _rxListener ) +{ + if( !(_rxListener.is() && getClientId( )) ) + return; + + ::osl::MutexGuard aGuard( getMutex() ); + sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( getClientId( ), _rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + + AccessibleEventNotifier::TClientId nId( getClientId( ) ); + setClientId( 0 ); + AccessibleEventNotifier::revokeClient( nId ); + } +} + +// XTypeProvider + +Sequence< sal_Int8 > SAL_CALL AccessibleBrowseBoxBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo + +sal_Bool SAL_CALL AccessibleBrowseBoxBase::supportsService( + const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL AccessibleBrowseBoxBase::getSupportedServiceNames() +{ + return { "com.sun.star.accessibility.AccessibleContext" }; +} + +// other public methods + +void AccessibleBrowseBoxBase::setAccessibleName( const OUString& rName ) +{ + ::osl::ClearableMutexGuard aGuard( getMutex() ); + Any aOld; + aOld <<= maName; + maName = rName; + + aGuard.clear(); + + commitEvent( + AccessibleEventId::NAME_CHANGED, + uno::Any( maName ), + aOld ); +} + +void AccessibleBrowseBoxBase::setAccessibleDescription( const OUString& rDescription ) +{ + ::osl::ClearableMutexGuard aGuard( getMutex() ); + Any aOld; + aOld <<= maDescription; + maDescription = rDescription; + + aGuard.clear(); + + commitEvent( + AccessibleEventId::DESCRIPTION_CHANGED, + uno::Any( maDescription ), + aOld ); +} + +// internal virtual methods + +bool AccessibleBrowseBoxBase::implIsShowing() +{ + bool bShowing = false; + if( mxParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleComponent > + xParentComp( mxParent->getAccessibleContext(), uno::UNO_QUERY ); + if( xParentComp.is() ) + bShowing = implGetBoundingBox().Overlaps( + VCLRectangle( xParentComp->getBounds() ) ); + } + return bShowing; +} + +rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleBrowseBoxBase::implCreateStateSetHelper() +{ + rtl::Reference<::utl::AccessibleStateSetHelper> + pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with mxParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + // BrowseBox fills StateSet with states depending on object type + mpBrowseBox->FillAccessibleStateSet( *pStateSetHelper, getType() ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; +} + +// internal helper methods + +bool AccessibleBrowseBoxBase::isAlive() const +{ + return !rBHelper.bDisposed && !rBHelper.bInDispose && mpBrowseBox; +} + +void AccessibleBrowseBoxBase::ensureIsAlive() const +{ + if( !isAlive() ) + throw lang::DisposedException(); +} + +tools::Rectangle AccessibleBrowseBoxBase::getBoundingBox() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + tools::Rectangle aRect = implGetBoundingBox(); + if ( aRect.Left() == 0 && aRect.Top() == 0 && aRect.Right() == 0 && aRect.Bottom() == 0 ) + { + SAL_WARN( "accessibility", "rectangle doesn't exist" ); + } + return aRect; +} + +tools::Rectangle AccessibleBrowseBoxBase::getBoundingBoxOnScreen() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + tools::Rectangle aRect = implGetBoundingBoxOnScreen(); + if ( aRect.Left() == 0 && aRect.Top() == 0 && aRect.Right() == 0 && aRect.Bottom() == 0 ) + { + SAL_WARN( "accessibility", "rectangle doesn't exist" ); + } + return aRect; +} + +void AccessibleBrowseBoxBase::commitEvent( + sal_Int16 _nEventId, const Any& _rNewValue, const Any& _rOldValue ) +{ + osl::MutexGuard aGuard( getMutex() ); + if ( !getClientId( ) ) + // if we don't have a client id for the notifier, then we don't have listeners, then + // we don't need to notify anything + return; + + // build an event object + AccessibleEventObject aEvent; + aEvent.Source = *this; + aEvent.EventId = _nEventId; + aEvent.OldValue = _rOldValue; + aEvent.NewValue = _rNewValue; + + // let the notifier handle this event + + AccessibleEventNotifier::addEvent( getClientId( ), aEvent ); +} + +sal_Int16 SAL_CALL AccessibleBrowseBoxBase::getAccessibleRole() +{ + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + sal_Int16 nRole = AccessibleRole::UNKNOWN; + switch ( meObjType ) + { + case AccessibleBrowseBoxObjType::RowHeaderCell: + nRole = AccessibleRole::ROW_HEADER; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderCell: + nRole = AccessibleRole::COLUMN_HEADER; + break; + case AccessibleBrowseBoxObjType::ColumnHeaderBar: + case AccessibleBrowseBoxObjType::RowHeaderBar: + case AccessibleBrowseBoxObjType::Table: + nRole = AccessibleRole::TABLE; + break; + case AccessibleBrowseBoxObjType::TableCell: + nRole = AccessibleRole::TABLE_CELL; + break; + case AccessibleBrowseBoxObjType::BrowseBox: + nRole = AccessibleRole::PANEL; + break; + case AccessibleBrowseBoxObjType::CheckBoxCell: + nRole = AccessibleRole::CHECK_BOX; + break; + } + return nRole; +} + +Reference<XAccessible > SAL_CALL AccessibleBrowseBoxBase::getAccessibleAtPoint( const css::awt::Point& ) +{ + return nullptr; +} + +void SAL_CALL AccessibleBrowseBoxBase::disposing( const css::lang::EventObject& ) +{ + m_xFocusWindow = nullptr; +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxBase::getForeground( ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Color nColor; + vcl::Window* pInst = mpBrowseBox->GetWindowInstance(); + if ( pInst ) + { + if ( pInst->IsControlForeground() ) + nColor = pInst->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pInst->IsControlFont() ) + aFont = pInst->GetControlFont(); + else + aFont = pInst->GetFont(); + nColor = aFont.GetColor(); + } + } + + return sal_Int32(nColor); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxBase::getBackground( ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Color nColor; + vcl::Window* pInst = mpBrowseBox->GetWindowInstance(); + if ( pInst ) + { + if ( pInst->IsControlBackground() ) + nColor = pInst->GetControlBackground(); + else + nColor = pInst->GetBackground().GetColor(); + } + + return sal_Int32(nColor); +} + + +// XInterface +IMPLEMENT_FORWARD_XINTERFACE2( BrowseBoxAccessibleElement, AccessibleBrowseBoxBase, BrowseBoxAccessibleElement_Base ) + +// XTypeProvider +IMPLEMENT_FORWARD_XTYPEPROVIDER2( BrowseBoxAccessibleElement, AccessibleBrowseBoxBase, BrowseBoxAccessibleElement_Base ) + +// css::accessibility::XAccessible + +Reference< css::accessibility::XAccessibleContext > SAL_CALL BrowseBoxAccessibleElement::getAccessibleContext() +{ + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return this; +} + + +BrowseBoxAccessibleElement::BrowseBoxAccessibleElement( const css::uno::Reference< css::accessibility::XAccessible >& rxParent, ::vcl::IAccessibleTableProvider& rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, AccessibleBrowseBoxObjType eObjType ) + :AccessibleBrowseBoxBase( rxParent, rBrowseBox, _xFocusWindow, eObjType ) +{ +} + + +BrowseBoxAccessibleElement::BrowseBoxAccessibleElement( const css::uno::Reference< css::accessibility::XAccessible >& rxParent, ::vcl::IAccessibleTableProvider& rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, AccessibleBrowseBoxObjType eObjType, + const OUString& rName, const OUString& rDescription ) + :AccessibleBrowseBoxBase( rxParent, rBrowseBox, _xFocusWindow, eObjType, rName, rDescription ) +{ +} + + +BrowseBoxAccessibleElement::~BrowseBoxAccessibleElement( ) +{ +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxCheckBoxCell.cxx b/accessibility/source/extended/AccessibleBrowseBoxCheckBoxCell.cxx new file mode 100644 index 000000000..2b29547ba --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxCheckBoxCell.cxx @@ -0,0 +1,162 @@ +/* -*- 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/AccessibleBrowseBoxCheckBoxCell.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <vcl/accessibletableprovider.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +namespace accessibility +{ + using namespace com::sun::star::accessibility; + using namespace com::sun::star::uno; + using namespace com::sun::star::accessibility::AccessibleEventId; + + AccessibleCheckBoxCell::AccessibleCheckBoxCell(const Reference<XAccessible >& _rxParent, + vcl::IAccessibleTableProvider& _rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, + sal_Int32 _nRowPos, + sal_uInt16 _nColPos + ,const TriState& _eState, + bool _bIsTriState) + :AccessibleBrowseBoxCell(_rxParent, _rBrowseBox, _xFocusWindow, _nRowPos, _nColPos, AccessibleBrowseBoxObjType::CheckBoxCell) + ,m_eState(_eState) + ,m_bIsTriState(_bIsTriState) + { + } + IMPLEMENT_FORWARD_XINTERFACE2( AccessibleCheckBoxCell, AccessibleBrowseBoxCell, AccessibleCheckBoxCell_BASE ) + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccessibleCheckBoxCell, AccessibleBrowseBoxCell, AccessibleCheckBoxCell_BASE ) + + Reference< XAccessibleContext > SAL_CALL AccessibleCheckBoxCell::getAccessibleContext( ) + { + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return this; + } + + rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleCheckBoxCell::implCreateStateSetHelper() + { + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSetHelper = + AccessibleBrowseBoxCell::implCreateStateSetHelper(); + if( isAlive() ) + { + mpBrowseBox->FillAccessibleStateSetForCell( + *pStateSetHelper, getRowPos(), static_cast< sal_uInt16 >( getColumnPos() ) ); + if ( m_eState == TRISTATE_TRUE ) + pStateSetHelper->AddState( AccessibleStateType::CHECKED ); + } + return pStateSetHelper; + } + + // XAccessibleValue + + Any SAL_CALL AccessibleCheckBoxCell::getCurrentValue( ) + { + ::osl::MutexGuard aGuard( getMutex() ); + + sal_Int32 nValue = 0; + switch( m_eState ) + { + case TRISTATE_FALSE: + nValue = 0; + break; + case TRISTATE_TRUE: + nValue = 1; + break; + case TRISTATE_INDET: + nValue = 2; + break; + } + return Any(nValue); + } + + sal_Bool SAL_CALL AccessibleCheckBoxCell::setCurrentValue( const Any& ) + { + return false; + } + + Any SAL_CALL AccessibleCheckBoxCell::getMaximumValue( ) + { + ::osl::MutexGuard aGuard( getMutex() ); + + Any aValue; + + if ( m_bIsTriState ) + aValue <<= sal_Int32(2); + else + aValue <<= sal_Int32(1); + + return aValue; + } + + Any SAL_CALL AccessibleCheckBoxCell::getMinimumValue( ) + { + Any aValue; + aValue <<= sal_Int32(0); + + return aValue; + } + + Any SAL_CALL AccessibleCheckBoxCell::getMinimumIncrement( ) + { + Any aValue; + aValue <<= sal_Int32(1); + + return aValue; + } + + // XAccessibleContext + sal_Int32 SAL_CALL AccessibleCheckBoxCell::getAccessibleChildCount( ) + { + return 0; + } + + css::uno::Reference< css::accessibility::XAccessible > SAL_CALL AccessibleCheckBoxCell::getAccessibleChild( sal_Int32 ) + { + throw css::lang::IndexOutOfBoundsException(); + } + + OUString SAL_CALL AccessibleCheckBoxCell::getImplementationName() + { + return "com.sun.star.comp.svtools.TableCheckBoxCell"; + } + + sal_Int32 SAL_CALL AccessibleCheckBoxCell::getAccessibleIndexInParent() + { + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + + return ( getRowPos() * mpBrowseBox->GetColumnCount() ) + getColumnPos(); + } + + void AccessibleCheckBoxCell::SetChecked( bool _bChecked ) + { + m_eState = _bChecked ? TRISTATE_TRUE : TRISTATE_FALSE; + Any aOldValue, aNewValue; + if ( _bChecked ) + aNewValue <<= AccessibleStateType::CHECKED; + else + aOldValue <<= AccessibleStateType::CHECKED; + commitEvent( AccessibleEventId::STATE_CHANGED, aNewValue, aOldValue ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxHeaderBar.cxx b/accessibility/source/extended/AccessibleBrowseBoxHeaderBar.cxx new file mode 100644 index 000000000..3d3541e4a --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxHeaderBar.cxx @@ -0,0 +1,362 @@ +/* -*- 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/AccessibleBrowseBoxHeaderBar.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletableprovider.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + + +namespace accessibility { + + +// Ctor/Dtor/disposing -------------------------------------------------------- + +AccessibleBrowseBoxHeaderBar::AccessibleBrowseBoxHeaderBar( + const Reference< XAccessible >& rxParent, + vcl::IAccessibleTableProvider& rBrowseBox, + AccessibleBrowseBoxObjType eObjType ) : + AccessibleBrowseBoxTableBase( rxParent, rBrowseBox,eObjType ) +{ + OSL_ENSURE( isRowBar() || isColumnBar(), + "extended/AccessibleBrowseBoxHeaderBar - invalid object type" ); +} + +AccessibleBrowseBoxHeaderBar::~AccessibleBrowseBoxHeaderBar() +{ +} + +// XAccessibleContext --------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleBrowseBoxHeaderBar::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidHeaderIndex( nChildIndex ); + return implGetChild( nChildIndex, implToVCLColumnPos( nChildIndex ) ); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleIndexInParent() +{ + return isRowBar() ? vcl::BBINDEX_ROWHEADERBAR : vcl::BBINDEX_COLUMNHEADERBAR; +} + +// XAccessibleComponent ------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleBrowseBoxHeaderBar::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + sal_Int32 nRow = 0; + sal_uInt16 nColumnPos = 0; + bool bConverted = isRowBar() ? + mpBrowseBox->ConvertPointToRowHeader( nRow, VCLPoint( rPoint ) ) : + mpBrowseBox->ConvertPointToColumnHeader( nColumnPos, VCLPoint( rPoint ) ); + + return bConverted ? implGetChild( nRow, nColumnPos ) : Reference< XAccessible >(); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::grabFocus() +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + // focus on header not supported +} + +// XAccessibleTable ----------------------------------------------------------- + +OUString SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleRowDescription( sal_Int32 nRow ) +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + ensureIsValidRow( nRow ); + return OUString(); // no headers in headers +} + +OUString SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleColumnDescription( sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + ensureIsValidColumn( nColumn ); + return OUString(); // no headers in headers +} + +Reference< XAccessibleTable > SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleRowHeaders() +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + return nullptr; // no headers in headers +} + +Reference< XAccessibleTable > SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleColumnHeaders() +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + return nullptr; // no headers in headers +} + +Sequence< sal_Int32 > SAL_CALL AccessibleBrowseBoxHeaderBar::getSelectedAccessibleRows() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Sequence< sal_Int32 > aSelSeq; + // row of column header bar not selectable + if( isRowBar() ) + implGetSelectedRows( aSelSeq ); + return aSelSeq; +} + +Sequence< sal_Int32 > SAL_CALL AccessibleBrowseBoxHeaderBar::getSelectedAccessibleColumns() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Sequence< sal_Int32 > aSelSeq; + // column of row header bar ("handle column") not selectable + if( isColumnBar() ) + implGetSelectedColumns( aSelSeq ); + return aSelSeq; +} + +sal_Bool SAL_CALL AccessibleBrowseBoxHeaderBar::isAccessibleRowSelected( sal_Int32 nRow ) +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + ensureIsValidRow( nRow ); + return isRowBar() && implIsRowSelected( nRow ); +} + +sal_Bool SAL_CALL AccessibleBrowseBoxHeaderBar::isAccessibleColumnSelected( sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + ensureIsValidColumn( nColumn ); + return isColumnBar() && implIsColumnSelected( nColumn ); +} + +Reference< XAccessible > SAL_CALL AccessibleBrowseBoxHeaderBar::getAccessibleCellAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + + return implGetChild( nRow, implToVCLColumnPos( nColumn ) ); +} + +sal_Bool SAL_CALL AccessibleBrowseBoxHeaderBar::isAccessibleSelected( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidAddress( nRow, nColumn ); + return isRowBar() ? implIsRowSelected( nRow ) : implIsColumnSelected( nColumn ); +} + +// XAccessibleSelection ------------------------------------------------------- + +void SAL_CALL AccessibleBrowseBoxHeaderBar::selectAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidHeaderIndex( nChildIndex ); + if( isRowBar() ) + implSelectRow( nChildIndex, true ); + else + implSelectColumn( implToVCLColumnPos( nChildIndex ), true ); +} + +sal_Bool SAL_CALL AccessibleBrowseBoxHeaderBar::isAccessibleChildSelected( sal_Int32 nChildIndex ) +{ + // using interface methods - no mutex + return isRowBar() ? + isAccessibleRowSelected( nChildIndex ) : + isAccessibleColumnSelected( nChildIndex ); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::clearAccessibleSelection() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + mpBrowseBox->SetNoSelection(); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::selectAllAccessibleChildren() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + // no multiselection of columns possible + if( isRowBar() ) + mpBrowseBox->SelectAll(); + else + implSelectColumn( implToVCLColumnPos( 0 ), true ); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxHeaderBar::getSelectedAccessibleChildCount() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return isRowBar() ? implGetSelectedRowCount() : implGetSelectedColumnCount(); +} + +Reference< XAccessible > SAL_CALL +AccessibleBrowseBoxHeaderBar::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + // method may throw lang::IndexOutOfBoundsException + sal_Int32 nIndex = implGetChildIndexFromSelectedIndex( nSelectedChildIndex ); + return implGetChild( nIndex, implToVCLColumnPos( nIndex ) ); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::deselectAccessibleChild( + sal_Int32 nSelectedChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + // method may throw lang::IndexOutOfBoundsException + if ( isAccessibleChildSelected(nSelectedChildIndex) ) + { + if( isRowBar() ) + implSelectRow( nSelectedChildIndex, false ); + else + implSelectColumn( implToVCLColumnPos( nSelectedChildIndex ), false ); + } +} + +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL AccessibleBrowseBoxHeaderBar::queryInterface( const uno::Type& rType ) +{ + Any aAny( AccessibleBrowseBoxTableBase::queryInterface( rType ) ); + return aAny.hasValue() ? + aAny : AccessibleBrowseBoxHeaderBarImplHelper::queryInterface( rType ); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::acquire() noexcept +{ + AccessibleBrowseBoxTableBase::acquire(); +} + +void SAL_CALL AccessibleBrowseBoxHeaderBar::release() noexcept +{ + AccessibleBrowseBoxTableBase::release(); +} + +// XServiceInfo --------------------------------------------------------------- + +OUString SAL_CALL AccessibleBrowseBoxHeaderBar::getImplementationName() +{ + return "com.sun.star.comp.svtools.AccessibleBrowseBoxHeaderBar"; +} + +Sequence< sal_Int8 > SAL_CALL AccessibleBrowseBoxHeaderBar::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// internal virtual methods --------------------------------------------------- + +tools::Rectangle AccessibleBrowseBoxHeaderBar::implGetBoundingBox() +{ + return mpBrowseBox->calcHeaderRect(isColumnBar(), false); +} + +tools::Rectangle AccessibleBrowseBoxHeaderBar::implGetBoundingBoxOnScreen() +{ + return mpBrowseBox->calcHeaderRect(isColumnBar()); +} + +sal_Int32 AccessibleBrowseBoxHeaderBar::implGetRowCount() const +{ + // column header bar: only 1 row + return isRowBar() ? AccessibleBrowseBoxTableBase::implGetRowCount() : 1; +} + +sal_Int32 AccessibleBrowseBoxHeaderBar::implGetColumnCount() const +{ + // row header bar ("handle column"): only 1 column + return isColumnBar() ? AccessibleBrowseBoxTableBase::implGetColumnCount() : 1; +} + +// internal helper methods ---------------------------------------------------- + +Reference< XAccessible > AccessibleBrowseBoxHeaderBar::implGetChild( + sal_Int32 nRow, sal_uInt16 nColumnPos ) +{ + return isRowBar() ? + mpBrowseBox->CreateAccessibleRowHeader( nRow ) : + mpBrowseBox->CreateAccessibleColumnHeader( nColumnPos ); +} + +sal_Int32 AccessibleBrowseBoxHeaderBar::implGetChildIndexFromSelectedIndex( + sal_Int32 nSelectedChildIndex ) +{ + Sequence< sal_Int32 > aSelSeq; + if( isRowBar() ) + implGetSelectedRows( aSelSeq ); + else + implGetSelectedColumns( aSelSeq ); + + if( (nSelectedChildIndex < 0) || (nSelectedChildIndex >= aSelSeq.getLength()) ) + throw lang::IndexOutOfBoundsException(); + + return aSelSeq.getConstArray()[ nSelectedChildIndex ]; +} + +void AccessibleBrowseBoxHeaderBar::ensureIsValidHeaderIndex( sal_Int32 nIndex ) +{ + if( isRowBar() ) + ensureIsValidRow( nIndex ); + else + ensureIsValidColumn( nIndex ); +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxHeaderCell.cxx b/accessibility/source/extended/AccessibleBrowseBoxHeaderCell.cxx new file mode 100644 index 000000000..81b3bfa4e --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxHeaderCell.cxx @@ -0,0 +1,159 @@ +/* -*- 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/AccessibleBrowseBoxHeaderCell.hxx> +#include <vcl/accessibletableprovider.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +namespace accessibility +{ + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::uno; + +AccessibleBrowseBoxHeaderCell::AccessibleBrowseBoxHeaderCell(sal_Int32 _nColumnRowId, + const Reference< XAccessible >& rxParent, + vcl::IAccessibleTableProvider& rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, + AccessibleBrowseBoxObjType eObjType) +: BrowseBoxAccessibleElement(rxParent, + rBrowseBox, + _xFocusWindow, + eObjType, + rBrowseBox.GetAccessibleObjectName( eObjType ,_nColumnRowId), + rBrowseBox.GetAccessibleObjectDescription( eObjType ,_nColumnRowId)) +, m_nColumnRowId(_nColumnRowId) +{ +} +/** Creates a new AccessibleStateSetHelper and fills it with states of the + current object. + @return + A filled AccessibleStateSetHelper. +*/ +rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleBrowseBoxHeaderCell::implCreateStateSetHelper() +{ + SolarMethodGuard aGuard( getMutex() ); + + rtl::Reference<::utl::AccessibleStateSetHelper> + pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with mxParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + + mpBrowseBox->FillAccessibleStateSet( *pStateSetHelper, getType() ); + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE ); + pStateSetHelper->AddState( AccessibleStateType::TRANSIENT ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + + bool bSelected = isRowBarCell() ? mpBrowseBox->IsRowSelected(m_nColumnRowId) : mpBrowseBox->IsColumnSelected(m_nColumnRowId); + if ( bSelected ) + pStateSetHelper->AddState( AccessibleStateType::SELECTED ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; +} + +/** @return + The count of visible children. +*/ +sal_Int32 SAL_CALL AccessibleBrowseBoxHeaderCell::getAccessibleChildCount() +{ + return 0; +} + + +/** @return + The XAccessible interface of the specified child. +*/ +Reference<XAccessible > SAL_CALL AccessibleBrowseBoxHeaderCell::getAccessibleChild( sal_Int32 ) +{ + throw IndexOutOfBoundsException(); +} + + +/** Grabs the focus to the column header. */ +void SAL_CALL AccessibleBrowseBoxHeaderCell::grabFocus() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + if ( isRowBarCell() ) + mpBrowseBox->SelectRow(m_nColumnRowId); + else + mpBrowseBox->SelectColumn(static_cast<sal_uInt16>(m_nColumnRowId)); //!!! +} + +/** @return + The name of this class. +*/ +OUString SAL_CALL AccessibleBrowseBoxHeaderCell::getImplementationName() +{ + return "com.sun.star.comp.svtools.AccessibleBrowseBoxHeaderCell"; +} + +namespace +{ + tools::Rectangle getRectangle(vcl::IAccessibleTableProvider* _pBrowseBox,sal_Int32 _nRowColIndex, bool _bOnScreen,bool _bRowBar) + { + sal_Int32 nRow = 0; + sal_uInt16 nCol = static_cast<sal_uInt16>(_nRowColIndex); + if ( _bRowBar ) + { + nRow = _nRowColIndex + 1; + nCol = 0; + } + + tools::Rectangle aRet(_pBrowseBox->GetFieldRectPixelAbs( nRow , nCol, true, _bOnScreen)); + return tools::Rectangle(aRet.TopLeft() - Point(0,aRet.GetHeight()),aRet.GetSize()); + } +} + +tools::Rectangle AccessibleBrowseBoxHeaderCell::implGetBoundingBox() +{ + return getRectangle(mpBrowseBox,m_nColumnRowId,false,isRowBarCell()); +} + + +tools::Rectangle AccessibleBrowseBoxHeaderCell::implGetBoundingBoxOnScreen() +{ + return getRectangle(mpBrowseBox,m_nColumnRowId,true,isRowBarCell()); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxHeaderCell::getAccessibleIndexInParent() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + sal_Int32 nIndex = m_nColumnRowId; + if ( mpBrowseBox->HasRowHeader() ) + --nIndex; + return nIndex; +} + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxTable.cxx b/accessibility/source/extended/AccessibleBrowseBoxTable.cxx new file mode 100644 index 000000000..d245c5155 --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxTable.cxx @@ -0,0 +1,229 @@ +/* -*- 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/AccessibleBrowseBoxTable.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletableprovider.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + + +namespace accessibility { + + +// Ctor/Dtor/disposing -------------------------------------------------------- + +AccessibleBrowseBoxTable::AccessibleBrowseBoxTable( + const Reference< XAccessible >& rxParent, + vcl::IAccessibleTableProvider& rBrowseBox ) : + AccessibleBrowseBoxTableBase( rxParent, rBrowseBox, AccessibleBrowseBoxObjType::Table ) +{ +} + +AccessibleBrowseBoxTable::~AccessibleBrowseBoxTable() +{ +} + +// XAccessibleContext --------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleBrowseBoxTable::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidIndex( nChildIndex ); + return mpBrowseBox->CreateAccessibleCell( + implGetRow( nChildIndex ), static_cast<sal_Int16>(implGetColumn( nChildIndex )) ); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTable::getAccessibleIndexInParent() +{ + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return vcl::BBINDEX_TABLE; +} + +// XAccessibleComponent ------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleBrowseBoxTable::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Reference< XAccessible > xChild; + sal_Int32 nRow = 0; + sal_uInt16 nColumnPos = 0; + if( mpBrowseBox->ConvertPointToCellAddress( nRow, nColumnPos, VCLPoint( rPoint ) ) ) + xChild = mpBrowseBox->CreateAccessibleCell( nRow, nColumnPos ); + + return xChild; +} + +void SAL_CALL AccessibleBrowseBoxTable::grabFocus() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + mpBrowseBox->GrabTableFocus(); +} + +// XAccessibleTable ----------------------------------------------------------- + +OUString SAL_CALL AccessibleBrowseBoxTable::getAccessibleRowDescription( sal_Int32 nRow ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidRow( nRow ); + return mpBrowseBox->GetRowDescription( nRow ); +} + +OUString SAL_CALL AccessibleBrowseBoxTable::getAccessibleColumnDescription( sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidColumn( nColumn ); + return mpBrowseBox->GetColumnDescription( static_cast<sal_uInt16>(nColumn) ); +} + +Reference< XAccessibleTable > SAL_CALL AccessibleBrowseBoxTable::getAccessibleRowHeaders() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return implGetHeaderBar( vcl::BBINDEX_ROWHEADERBAR ); +} + +Reference< XAccessibleTable > SAL_CALL AccessibleBrowseBoxTable::getAccessibleColumnHeaders() +{ + ::osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return implGetHeaderBar( vcl::BBINDEX_COLUMNHEADERBAR ); +} + +Sequence< sal_Int32 > SAL_CALL AccessibleBrowseBoxTable::getSelectedAccessibleRows() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Sequence< sal_Int32 > aSelSeq; + implGetSelectedRows( aSelSeq ); + return aSelSeq; +} + +Sequence< sal_Int32 > SAL_CALL AccessibleBrowseBoxTable::getSelectedAccessibleColumns() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + Sequence< sal_Int32 > aSelSeq; + implGetSelectedColumns( aSelSeq ); + return aSelSeq; +} + +sal_Bool SAL_CALL AccessibleBrowseBoxTable::isAccessibleRowSelected( sal_Int32 nRow ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidRow( nRow ); + return implIsRowSelected( nRow ); +} + +sal_Bool SAL_CALL AccessibleBrowseBoxTable::isAccessibleColumnSelected( sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidColumn( nColumn ); + return implIsColumnSelected( nColumn ); +} + +Reference< XAccessible > SAL_CALL AccessibleBrowseBoxTable::getAccessibleCellAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidAddress( nRow, nColumn ); + return mpBrowseBox->CreateAccessibleCell( nRow, static_cast<sal_Int16>(nColumn) ); +} + +sal_Bool SAL_CALL AccessibleBrowseBoxTable::isAccessibleSelected( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + ensureIsValidAddress( nRow, nColumn ); + return implIsRowSelected( nRow ) || implIsColumnSelected( nColumn ); +} + +// XServiceInfo --------------------------------------------------------------- + +OUString SAL_CALL AccessibleBrowseBoxTable::getImplementationName() +{ + return "com.sun.star.comp.svtools.AccessibleBrowseBoxTable"; +} + +// internal virtual methods --------------------------------------------------- + +tools::Rectangle AccessibleBrowseBoxTable::implGetBoundingBox() +{ + return mpBrowseBox->calcTableRect(false); +} + +tools::Rectangle AccessibleBrowseBoxTable::implGetBoundingBoxOnScreen() +{ + return mpBrowseBox->calcTableRect(); +} + +// internal helper methods ---------------------------------------------------- + +Reference< XAccessibleTable > AccessibleBrowseBoxTable::implGetHeaderBar( + sal_Int32 nChildIndex ) +{ + Reference< XAccessible > xRet; + Reference< XAccessibleContext > xContext( mxParent, uno::UNO_QUERY ); + if( xContext.is() ) + { + try + { + xRet = xContext->getAccessibleChild( nChildIndex ); + } + catch (const lang::IndexOutOfBoundsException&) + { + OSL_FAIL( "implGetHeaderBar - wrong child index" ); + } + // RuntimeException goes to caller + } + return Reference< XAccessibleTable >( xRet, uno::UNO_QUERY ); +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxTableBase.cxx b/accessibility/source/extended/AccessibleBrowseBoxTableBase.cxx new file mode 100644 index 000000000..ebb6e06e0 --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxTableBase.cxx @@ -0,0 +1,292 @@ +/* -*- 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/AccessibleBrowseBoxTableBase.hxx> +#include <vcl/accessibletableprovider.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + + +using css::uno::Reference; +using css::uno::Sequence; +using css::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + + +namespace accessibility { + + +// Ctor/Dtor/disposing -------------------------------------------------------- + +AccessibleBrowseBoxTableBase::AccessibleBrowseBoxTableBase( + const Reference< XAccessible >& rxParent, + vcl::IAccessibleTableProvider& rBrowseBox, + AccessibleBrowseBoxObjType eObjType ) : + BrowseBoxAccessibleElement( rxParent, rBrowseBox,nullptr, eObjType ) +{ +} + +// XAccessibleContext --------------------------------------------------------- + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleChildCount() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + return implGetChildCount(); +} + +sal_Int16 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleRole() +{ + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return AccessibleRole::TABLE; +} + +// XAccessibleTable ----------------------------------------------------------- + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleRowCount() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + return implGetRowCount(); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleColumnCount() +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + return implGetColumnCount(); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleRowExtentAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return 1; // merged cells not supported +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleColumnExtentAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return 1; // merged cells not supported +} + +Reference< XAccessible > SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleCaption() +{ + ensureIsAlive(); + return nullptr; // not supported +} + +Reference< XAccessible > SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleSummary() +{ + ensureIsAlive(); + return nullptr; // not supported +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleIndex( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return nRow * implGetColumnCount() + nColumn; +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleRow( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + return implGetRow( nChildIndex ); +} + +sal_Int32 SAL_CALL AccessibleBrowseBoxTableBase::getAccessibleColumn( sal_Int32 nChildIndex ) +{ + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + return implGetColumn( nChildIndex ); +} + +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL AccessibleBrowseBoxTableBase::queryInterface( const uno::Type& rType ) +{ + Any aAny( BrowseBoxAccessibleElement::queryInterface( rType ) ); + return aAny.hasValue() ? + aAny : AccessibleBrowseBoxTableImplHelper::queryInterface( rType ); +} + +void SAL_CALL AccessibleBrowseBoxTableBase::acquire() noexcept +{ + BrowseBoxAccessibleElement::acquire(); +} + +void SAL_CALL AccessibleBrowseBoxTableBase::release() noexcept +{ + BrowseBoxAccessibleElement::release(); +} + +// XTypeProvider -------------------------------------------------------------- + +Sequence< uno::Type > SAL_CALL AccessibleBrowseBoxTableBase::getTypes() +{ + return ::comphelper::concatSequences( + BrowseBoxAccessibleElement::getTypes(), + AccessibleBrowseBoxTableImplHelper::getTypes() ); +} + +Sequence< sal_Int8 > SAL_CALL AccessibleBrowseBoxTableBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// internal virtual methods --------------------------------------------------- + +sal_Int32 AccessibleBrowseBoxTableBase::implGetRowCount() const +{ + return mpBrowseBox->GetRowCount(); +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetColumnCount() const +{ + sal_uInt16 nColumns = mpBrowseBox->GetColumnCount(); + // do not count the "handle column" + if( nColumns && implHasHandleColumn() ) + --nColumns; + return nColumns; +} + +// internal helper methods ---------------------------------------------------- + +bool AccessibleBrowseBoxTableBase::implHasHandleColumn() const +{ + return mpBrowseBox->HasRowHeader(); +} + +sal_uInt16 AccessibleBrowseBoxTableBase::implToVCLColumnPos( sal_Int32 nColumn ) const +{ + sal_uInt16 nVCLPos = 0; + if( (0 <= nColumn) && (nColumn < implGetColumnCount()) ) + { + // regard "handle column" + if( implHasHandleColumn() ) + ++nColumn; + nVCLPos = static_cast< sal_uInt16 >( nColumn ); + } + return nVCLPos; +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetChildCount() const +{ + return implGetRowCount() * implGetColumnCount(); +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetRow( sal_Int32 nChildIndex ) const +{ + sal_Int32 nColumns = implGetColumnCount(); + return nColumns ? (nChildIndex / nColumns) : 0; +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetColumn( sal_Int32 nChildIndex ) const +{ + sal_Int32 nColumns = implGetColumnCount(); + return nColumns ? (nChildIndex % nColumns) : 0; +} + +bool AccessibleBrowseBoxTableBase::implIsRowSelected( sal_Int32 nRow ) const +{ + return mpBrowseBox->IsRowSelected( nRow ); +} + +bool AccessibleBrowseBoxTableBase::implIsColumnSelected( sal_Int32 nColumn ) const +{ + if( implHasHandleColumn() ) + --nColumn; + return mpBrowseBox->IsColumnSelected( nColumn ); +} + +void AccessibleBrowseBoxTableBase::implSelectRow( sal_Int32 nRow, bool bSelect ) +{ + mpBrowseBox->SelectRow( nRow, bSelect ); +} + +void AccessibleBrowseBoxTableBase::implSelectColumn( sal_Int32 nColumnPos, bool bSelect ) +{ + mpBrowseBox->SelectColumn( static_cast<sal_uInt16>(nColumnPos), bSelect ); +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetSelectedRowCount() const +{ + return mpBrowseBox->GetSelectedRowCount(); +} + +sal_Int32 AccessibleBrowseBoxTableBase::implGetSelectedColumnCount() const +{ + return mpBrowseBox->GetSelectedColumnCount(); +} + +void AccessibleBrowseBoxTableBase::implGetSelectedRows( Sequence< sal_Int32 >& rSeq ) +{ + mpBrowseBox->GetAllSelectedRows( rSeq ); +} + +void AccessibleBrowseBoxTableBase::implGetSelectedColumns( Sequence< sal_Int32 >& rSeq ) +{ + mpBrowseBox->GetAllSelectedColumns( rSeq ); +} + +void AccessibleBrowseBoxTableBase::ensureIsValidRow( sal_Int32 nRow ) +{ + if( nRow >= implGetRowCount() ) + throw lang::IndexOutOfBoundsException( "row index is invalid", *this ); +} + +void AccessibleBrowseBoxTableBase::ensureIsValidColumn( sal_Int32 nColumn ) +{ + if( nColumn >= implGetColumnCount() ) + throw lang::IndexOutOfBoundsException( "column index is invalid", *this ); +} + +void AccessibleBrowseBoxTableBase::ensureIsValidAddress( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + ensureIsValidRow( nRow ); + ensureIsValidColumn( nColumn ); +} + +void AccessibleBrowseBoxTableBase::ensureIsValidIndex( sal_Int32 nChildIndex ) +{ + if( nChildIndex >= implGetChildCount() ) + throw lang::IndexOutOfBoundsException( "child index is invalid", *this ); +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleBrowseBoxTableCell.cxx b/accessibility/source/extended/AccessibleBrowseBoxTableCell.cxx new file mode 100644 index 000000000..13cdb63e2 --- /dev/null +++ b/accessibility/source/extended/AccessibleBrowseBoxTableCell.cxx @@ -0,0 +1,338 @@ +/* -*- 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/AccessibleBrowseBoxTableCell.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletableprovider.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +namespace accessibility +{ + namespace + { + /// @throws css::lang::IndexOutOfBoundsException + void checkIndex_Impl( sal_Int32 _nIndex, const OUString& _sText ) + { + if ( _nIndex >= _sText.getLength() ) + throw css::lang::IndexOutOfBoundsException(); + } + + sal_Int32 getIndex_Impl( sal_Int32 _nRow, sal_uInt16 _nColumn, sal_uInt16 _nColumnCount ) + { + return _nRow * _nColumnCount + _nColumn; + } + } + using namespace ::com::sun::star::lang; + using namespace utl; + using namespace comphelper; + using namespace ::com::sun::star::uno; + using ::com::sun::star::accessibility::XAccessible; + using namespace ::com::sun::star::accessibility; + + + // implementation of a table cell + OUString AccessibleBrowseBoxTableCell::implGetText() + { + return mpBrowseBox->GetAccessibleCellText( getRowPos(), static_cast< sal_uInt16 >( getColumnPos() ) ); + } + + css::lang::Locale AccessibleBrowseBoxTableCell::implGetLocale() + { + return mpBrowseBox->GetAccessible()->getAccessibleContext()->getLocale(); + } + + void AccessibleBrowseBoxTableCell::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex ) + { + nStartIndex = 0; + nEndIndex = 0; + } + + AccessibleBrowseBoxTableCell::AccessibleBrowseBoxTableCell(const Reference<XAccessible >& _rxParent, + vcl::IAccessibleTableProvider& _rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, + sal_Int32 _nRowPos, + sal_uInt16 _nColPos, + sal_Int32 _nOffset ) + :AccessibleBrowseBoxCell( _rxParent, _rBrowseBox, _xFocusWindow, _nRowPos, _nColPos ) + { + m_nOffset = ( _nOffset == OFFSET_DEFAULT ) ? sal_Int32(vcl::BBINDEX_FIRSTCONTROL) : _nOffset; + sal_Int32 nIndex = getIndex_Impl( _nRowPos, _nColPos, _rBrowseBox.GetColumnCount() ); + setAccessibleName( _rBrowseBox.GetAccessibleObjectName( AccessibleBrowseBoxObjType::TableCell, nIndex ) ); + setAccessibleDescription( _rBrowseBox.GetAccessibleObjectDescription( AccessibleBrowseBoxObjType::TableCell, nIndex ) ); + // Need to register as event listener + Reference< XComponent > xComponent(_rxParent, UNO_QUERY); + if( xComponent.is() ) + xComponent->addEventListener(static_cast< XEventListener *> (this)); + } + + // XInterface ------------------------------------------------------------- + + /** Queries for a new interface. */ + css::uno::Any SAL_CALL AccessibleBrowseBoxTableCell::queryInterface( const css::uno::Type& rType ) + { + Any aRet = AccessibleBrowseBoxCell::queryInterface(rType); + if ( !aRet.hasValue() ) + aRet = AccessibleTextHelper_BASE::queryInterface(rType); + return aRet; + } + + /** Acquires the object (calls acquire() on base class). */ + void SAL_CALL AccessibleBrowseBoxTableCell::acquire() noexcept + { + AccessibleBrowseBoxCell::acquire(); + } + + /** Releases the object (calls release() on base class). */ + void SAL_CALL AccessibleBrowseBoxTableCell::release() noexcept + { + AccessibleBrowseBoxCell::release(); + } + + css::awt::Rectangle SAL_CALL AccessibleBrowseBoxTableCell::getCharacterBounds( sal_Int32 nIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + css::awt::Rectangle aRect; + + if ( mpBrowseBox ) + { + if ( !implIsValidIndex( nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + aRect = AWTRectangle( mpBrowseBox->GetFieldCharacterBounds( getRowPos(), getColumnPos(), nIndex ) ); + } + + return aRect; + } + + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getIndexAtPoint( const css::awt::Point& _aPoint ) + { + //! TODO CTL bidi + // OSL_FAIL("Need to be done by base class!"); + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return mpBrowseBox->GetFieldIndexAtPoint( getRowPos(), getColumnPos(), VCLPoint( _aPoint ) ); + } + + /** @return + The name of this class. + */ + OUString SAL_CALL AccessibleBrowseBoxTableCell::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleBrowseBoxTableCell"; + } + + /** @return The count of visible children. */ + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getAccessibleChildCount() + { + return 0; + } + + /** @return The XAccessible interface of the specified child. */ + css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + AccessibleBrowseBoxTableCell::getAccessibleChild( sal_Int32 ) + { + throw css::lang::IndexOutOfBoundsException(); + } + + /** Creates a new AccessibleStateSetHelper and fills it with states of the + current object. + @return + A filled AccessibleStateSetHelper. + */ + rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleBrowseBoxTableCell::implCreateStateSetHelper() + { + SolarMethodGuard aGuard(getMutex()); + + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with mxParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + + mpBrowseBox->FillAccessibleStateSetForCell( *pStateSetHelper, getRowPos(), static_cast< sal_uInt16 >( getColumnPos() ) ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; + } + + + // XAccessible ------------------------------------------------------------ + + /** @return The XAccessibleContext interface of this object. */ + Reference< XAccessibleContext > SAL_CALL AccessibleBrowseBoxTableCell::getAccessibleContext() + { + osl::MutexGuard aGuard( getMutex() ); + ensureIsAlive(); + return this; + } + + // XAccessibleContext ----------------------------------------------------- + + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getAccessibleIndexInParent() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return /*vcl::BBINDEX_FIRSTCONTROL*/ m_nOffset + ( getRowPos() * mpBrowseBox->GetColumnCount() ) + getColumnPos(); + } + + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getCaretPosition( ) + { + return -1; + } + + sal_Bool SAL_CALL AccessibleBrowseBoxTableCell::setCaretPosition ( sal_Int32 nIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + if ( !implIsValidRange( nIndex, nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + sal_Unicode SAL_CALL AccessibleBrowseBoxTableCell::getCharacter( sal_Int32 nIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex ); + } + css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleBrowseBoxTableCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + OUString sText( implGetText() ); + + if ( !implIsValidIndex( nIndex, sText.getLength() ) ) + throw IndexOutOfBoundsException(); + + return css::uno::Sequence< css::beans::PropertyValue >(); + } + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getCharacterCount( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return implGetText().getLength(); + } + + OUString SAL_CALL AccessibleBrowseBoxTableCell::getSelectedText( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getSelectedText( ); + } + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getSelectionStart( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getSelectionStart( ); + } + sal_Int32 SAL_CALL AccessibleBrowseBoxTableCell::getSelectionEnd( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getSelectionEnd( ); + } + sal_Bool SAL_CALL AccessibleBrowseBoxTableCell::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + if ( !implIsValidRange( nStartIndex, nEndIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + OUString SAL_CALL AccessibleBrowseBoxTableCell::getText( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return implGetText( ); + } + OUString SAL_CALL AccessibleBrowseBoxTableCell::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::implGetTextRange( implGetText(), nStartIndex, nEndIndex ); + } + css::accessibility::TextSegment SAL_CALL AccessibleBrowseBoxTableCell::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getTextAtIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleBrowseBoxTableCell::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getTextBeforeIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleBrowseBoxTableCell::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return OCommonAccessibleText::getTextBehindIndex( nIndex ,aTextType); + } + sal_Bool SAL_CALL AccessibleBrowseBoxTableCell::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + OUString sText = implGetText(); + checkIndex_Impl( nStartIndex, sText ); + checkIndex_Impl( nEndIndex, sText ); + + //!!! don't know how to put a string into the clipboard + return false; + } + sal_Bool SAL_CALL AccessibleBrowseBoxTableCell::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + void AccessibleBrowseBoxTableCell::disposing( const EventObject& _rSource ) + { + if ( _rSource.Source == mxParent ) + { + dispose(); + } + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControl.cxx b/accessibility/source/extended/AccessibleGridControl.cxx new file mode 100644 index 000000000..a3d016194 --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControl.cxx @@ -0,0 +1,354 @@ +/* -*- 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/AccessibleGridControl.hxx> +#include <extended/AccessibleGridControlTable.hxx> +#include <extended/AccessibleGridControlHeader.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <toolkit/helper/convert.hxx> +#include <utility> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> + +namespace accessibility +{ + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; +using namespace ::vcl; +using namespace ::vcl::table; + +AccessibleGridControl::AccessibleGridControl( + const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, const css::uno::Reference< css::accessibility::XAccessible >& _rxCreator, + ::vcl::table::IAccessibleTable& _rTable ) + : AccessibleGridControlBase( _rxParent, _rTable, TCTYPE_GRIDCONTROL ), + m_aCreator(_rxCreator) +{ +} + + +void SAL_CALL AccessibleGridControl::disposing() +{ + SolarMutexGuard g; + + m_aCreator.clear(); + + if ( m_xTable.is() ) + { + m_xTable->dispose(); + m_xTable.clear(); + } + if ( m_xRowHeaderBar.is() ) + { + m_xRowHeaderBar->dispose(); + m_xRowHeaderBar.clear(); + } + if ( m_xColumnHeaderBar.is() ) + { + m_xColumnHeaderBar->dispose(); + m_xColumnHeaderBar.clear(); + } + AccessibleGridControlBase::disposing(); +} + +sal_Int32 AccessibleGridControl::implGetAccessibleChildCount() +{ + return m_aTable.GetAccessibleControlCount(); +} + +// css::accessibility::XAccessibleContext --------------------------------------------------------- + + +sal_Int32 SAL_CALL AccessibleGridControl::getAccessibleChildCount() +{ + SolarMutexGuard aSolarGuard; + ensureIsAlive(); + return implGetAccessibleChildCount(); +} + + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +AccessibleGridControl::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + if (nChildIndex<0 || nChildIndex>=implGetAccessibleChildCount()) + throw IndexOutOfBoundsException(); + + css::uno::Reference< css::accessibility::XAccessible > xChild; + if (isAlive()) + { + if(nChildIndex == 0 && m_aTable.HasColHeader()) + { + if(!m_xColumnHeaderBar.is()) + { + m_xColumnHeaderBar = new AccessibleGridControlHeader(m_aCreator, m_aTable, vcl::table::TCTYPE_COLUMNHEADERBAR); + } + xChild = m_xColumnHeaderBar.get(); + } + else if(m_aTable.HasRowHeader() && (nChildIndex == 1 || nChildIndex == 0)) + { + if(!m_xRowHeaderBar.is()) + { + m_xRowHeaderBar = new AccessibleGridControlHeader(m_aCreator, m_aTable, vcl::table::TCTYPE_ROWHEADERBAR); + } + xChild = m_xRowHeaderBar.get(); + } + else + { + if(!m_xTable.is()) + { + m_xTable = new AccessibleGridControlTable(m_aCreator, m_aTable); + } + xChild = m_xTable.get(); + } + } + return xChild; +} + + +sal_Int16 SAL_CALL AccessibleGridControl::getAccessibleRole() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return AccessibleRole::PANEL; +} + + +// css::accessibility::XAccessibleComponent ------------------------------------------------------- + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +AccessibleGridControl::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMutexGuard aSolarGuard; + ensureIsAlive(); + + css::uno::Reference< css::accessibility::XAccessible > xChild; + sal_Int32 nIndex = 0; + if( m_aTable.ConvertPointToControlIndex( nIndex, VCLPoint( rPoint ) ) ) + xChild = m_aTable.CreateAccessibleControl( nIndex ); + else + { + // try whether point is in one of the fixed children + // (table, header bars, corner control) + Point aPoint( VCLPoint( rPoint ) ); + for( nIndex = 0; (nIndex < 3) && !xChild.is(); ++nIndex ) + { + css::uno::Reference< css::accessibility::XAccessible > xCurrChild( implGetFixedChild( nIndex ) ); + css::uno::Reference< css::accessibility::XAccessibleComponent > + xCurrChildComp( xCurrChild, uno::UNO_QUERY ); + + if( xCurrChildComp.is() && + VCLRectangle( xCurrChildComp->getBounds() ).Contains( aPoint ) ) + xChild = xCurrChild; + } + } + return xChild; +} + + +void SAL_CALL AccessibleGridControl::grabFocus() +{ + SolarMutexGuard aSolarGuard; + ensureIsAlive(); + m_aTable.GrabFocus(); +} + +// XServiceInfo --------------------------------------------------------------- + +OUString SAL_CALL AccessibleGridControl::getImplementationName() +{ + return "com.sun.star.accessibility.AccessibleGridControl"; +} + + +// internal virtual methods --------------------------------------------------- + +tools::Rectangle AccessibleGridControl::implGetBoundingBox() +{ + vcl::Window* pParent = m_aTable.GetAccessibleParentWindow(); + OSL_ENSURE( pParent, "implGetBoundingBox - missing parent window" ); + return m_aTable.GetWindowExtentsRelative( pParent ); +} + + +tools::Rectangle AccessibleGridControl::implGetBoundingBoxOnScreen() +{ + return m_aTable.GetWindowExtentsRelative( nullptr ); +} +// internal helper methods ---------------------------------------------------- + +css::uno::Reference< css::accessibility::XAccessible > AccessibleGridControl::implGetTable() +{ + if( !m_xTable.is() ) + { + m_xTable = createAccessibleTable(); + } + return m_xTable; +} + + +css::uno::Reference< css::accessibility::XAccessible > +AccessibleGridControl::implGetHeaderBar( AccessibleTableControlObjType eObjType ) +{ + css::uno::Reference< css::accessibility::XAccessible > xRet; + rtl::Reference< AccessibleGridControlHeader >* pxMember = nullptr; + + if( eObjType == TCTYPE_ROWHEADERBAR ) + pxMember = &m_xRowHeaderBar; + else if( eObjType == TCTYPE_COLUMNHEADERBAR ) + pxMember = &m_xColumnHeaderBar; + + if( pxMember ) + { + if( !pxMember->is() ) + { + *pxMember = new AccessibleGridControlHeader( + m_aCreator, m_aTable, eObjType ); + } + xRet = pxMember->get(); + } + return xRet; +} + +css::uno::Reference< css::accessibility::XAccessible > +AccessibleGridControl::implGetFixedChild( sal_Int32 nChildIndex ) +{ + css::uno::Reference< css::accessibility::XAccessible > xRet; + switch( nChildIndex ) + { + /** Child index of the column header bar (first row). */ + case 0: + xRet = implGetHeaderBar( TCTYPE_COLUMNHEADERBAR ); + break; + /** Child index of the row header bar ("handle column"). */ + case 1: + xRet = implGetHeaderBar( TCTYPE_ROWHEADERBAR ); + break; + /** Child index of the data table. */ + case 2: + xRet = implGetTable(); + break; + } + return xRet; +} + +rtl::Reference<AccessibleGridControlTable> AccessibleGridControl::createAccessibleTable() +{ + css::uno::Reference< css::accessibility::XAccessible > xCreator(m_aCreator); + OSL_ENSURE( xCreator.is(), "extended/AccessibleGridControl::createAccessibleTable: my creator died - how this?" ); + return new AccessibleGridControlTable( xCreator, m_aTable ); +} + +void AccessibleGridControl::commitCellEvent(sal_Int16 _nEventId,const Any& _rNewValue,const Any& _rOldValue) +{ + sal_Int32 nChildCount = implGetAccessibleChildCount(); + if(nChildCount != 0) + { + for(sal_Int32 i=0;i<nChildCount;i++) + { + css::uno::Reference< css::accessibility::XAccessible > xAccessible = getAccessibleChild(i); + if(css::uno::Reference< css::accessibility::XAccessible >(m_xTable) == xAccessible) + { + Reference<XAccessible> xCell = m_xTable->getAccessibleCellAt( + m_aTable.GetCurrentRow(), m_aTable.GetCurrentColumn()); + AccessibleGridControlTableCell* pCell = static_cast<AccessibleGridControlTableCell*>(xCell.get()); + pCell->commitEvent(_nEventId, _rNewValue, _rOldValue); + } + } + } + else + { + if ( m_xTable.is() ) + m_xTable->commitEvent(_nEventId,_rNewValue,_rOldValue); + } +} + +void AccessibleGridControl::commitTableEvent(sal_Int16 _nEventId,const Any& _rNewValue,const Any& _rOldValue) +{ + if ( !m_xTable.is() ) + return; + + if(_nEventId == AccessibleEventId::ACTIVE_DESCENDANT_CHANGED) + { + const sal_Int32 nCurrentRow = m_aTable.GetCurrentRow(); + const sal_Int32 nCurrentCol = m_aTable.GetCurrentColumn(); + css::uno::Reference< css::accessibility::XAccessible > xChild; + if (nCurrentRow > -1 && nCurrentCol > -1) + xChild = m_xTable->getAccessibleCellAt(nCurrentRow, nCurrentCol); + + m_xTable->commitEvent(_nEventId, Any(xChild),_rOldValue); + } + else + m_xTable->commitEvent(_nEventId,_rNewValue,_rOldValue); +} + +// = AccessibleGridControlAccess + + +AccessibleGridControlAccess::AccessibleGridControlAccess( + css::uno::Reference< css::accessibility::XAccessible > xParent, ::vcl::table::IAccessibleTable& rTable ) + : m_xParent(std::move( xParent )) + , m_pTable( & rTable ) +{ +} + + +AccessibleGridControlAccess::~AccessibleGridControlAccess() +{ +} + + +void AccessibleGridControlAccess::DisposeAccessImpl() +{ + SolarMutexGuard g; + + m_pTable = nullptr; + if (m_xContext.is()) + { + m_xContext->dispose(); + m_xContext.clear(); + } +} + + +css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL AccessibleGridControlAccess::getAccessibleContext() +{ + SolarMutexGuard g; + + // if the context died meanwhile (we're no listener, so it won't tell us explicitly when this happens), + // then reset and re-create. + if ( m_xContext.is() && !m_xContext->isAlive() ) + m_xContext = nullptr; + + if (!m_xContext.is() && m_pTable) + m_xContext = new AccessibleGridControl(m_xParent, this, *m_pTable); + + return m_xContext; +} + + +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlBase.cxx b/accessibility/source/extended/AccessibleGridControlBase.cxx new file mode 100644 index 000000000..6b7ce3fad --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlBase.cxx @@ -0,0 +1,465 @@ +/* -*- 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/AccessibleGridControlBase.hxx> +#include <toolkit/helper/convert.hxx> +#include <utility> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <sal/types.h> + +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp> +#include <unotools/accessiblerelationsethelper.hxx> +#include <sal/log.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::comphelper; +using namespace ::vcl; +using namespace ::vcl::table; + + +namespace accessibility { + +using namespace com::sun::star::accessibility::AccessibleStateType; + + +AccessibleGridControlBase::AccessibleGridControlBase( + css::uno::Reference< css::accessibility::XAccessible > xParent, + ::vcl::table::IAccessibleTable& rTable, + ::vcl::table::AccessibleTableControlObjType eObjType ) : + AccessibleGridControlImplHelper( m_aMutex ), + m_xParent(std::move( xParent )), + m_aTable( rTable), + m_eObjType( eObjType ), + m_aClientId(0) +{ +} + +AccessibleGridControlBase::~AccessibleGridControlBase() +{ + if( isAlive() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +void SAL_CALL AccessibleGridControlBase::disposing() +{ + SolarMutexGuard g; + + if ( getClientId( ) ) + { + AccessibleEventNotifier::TClientId nId( getClientId( ) ); + setClientId( 0 ); + AccessibleEventNotifier::revokeClientNotifyDisposing( nId, *this ); + } + + m_xParent = nullptr; + //m_aTable = NULL; +} + +// css::accessibility::XAccessibleContext + +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL AccessibleGridControlBase::getAccessibleParent() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return m_xParent; +} + +sal_Int32 SAL_CALL AccessibleGridControlBase::getAccessibleIndexInParent() +{ + SolarMutexGuard g; + + ensureIsAlive(); + + // -1 for child not found/no parent (according to specification) + sal_Int32 nRet = -1; + + css::uno::Reference< uno::XInterface > xMeMyselfAndI( static_cast< css::accessibility::XAccessibleContext* >( this ), uno::UNO_QUERY ); + + // iterate over parent's children and search for this object + if( m_xParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > + xParentContext( m_xParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + css::uno::Reference< uno::XInterface > xChild; + + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for( sal_Int32 nChild = 0; nChild < nChildCount; ++nChild ) + { + xChild.set(xParentContext->getAccessibleChild( nChild ), css::uno::UNO_QUERY); + if ( xMeMyselfAndI.get() == xChild.get() ) + { + nRet = nChild; + break; + } + } + } + } + return nRet; +} + +OUString SAL_CALL AccessibleGridControlBase::getAccessibleDescription() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return m_aTable.GetAccessibleObjectDescription(m_eObjType); +} + +OUString SAL_CALL AccessibleGridControlBase::getAccessibleName() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return m_aTable.GetAccessibleObjectName(m_eObjType, 0, 0); +} + +css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL +AccessibleGridControlBase::getAccessibleRelationSet() +{ + SolarMutexGuard g; + + ensureIsAlive(); + // GridControl does not have relations. + return new utl::AccessibleRelationSetHelper; +} + +css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL +AccessibleGridControlBase::getAccessibleStateSet() +{ + SolarMutexGuard aSolarGuard; + + // don't check whether alive -> StateSet may contain DEFUNC + return implCreateStateSetHelper(); +} + +lang::Locale SAL_CALL AccessibleGridControlBase::getLocale() +{ + SolarMutexGuard g; + + ensureIsAlive(); + if( m_xParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > + xParentContext( m_xParent->getAccessibleContext() ); + if( xParentContext.is() ) + return xParentContext->getLocale(); + } + throw IllegalAccessibleComponentStateException(); +} + +// css::accessibility::XAccessibleComponent + +sal_Bool SAL_CALL AccessibleGridControlBase::containsPoint( const awt::Point& rPoint ) +{ + return tools::Rectangle( Point(), getBoundingBox().GetSize() ).Contains( VCLPoint( rPoint ) ); +} + +awt::Rectangle SAL_CALL AccessibleGridControlBase::getBounds() +{ + return AWTRectangle( getBoundingBox() ); +} + +awt::Point SAL_CALL AccessibleGridControlBase::getLocation() +{ + return AWTPoint( getBoundingBox().TopLeft() ); +} + +awt::Point SAL_CALL AccessibleGridControlBase::getLocationOnScreen() +{ + return AWTPoint( getBoundingBoxOnScreen().TopLeft() ); +} + +awt::Size SAL_CALL AccessibleGridControlBase::getSize() +{ + return AWTSize( getBoundingBox().GetSize() ); +} + +// css::accessibility::XAccessibleEventBroadcaster + +void SAL_CALL AccessibleGridControlBase::addAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener>& _rxListener ) +{ + if ( _rxListener.is() ) + { + SolarMutexGuard g; + + if ( !getClientId( ) ) + setClientId( AccessibleEventNotifier::registerClient( ) ); + + AccessibleEventNotifier::addEventListener( getClientId( ), _rxListener ); + } +} + +void SAL_CALL AccessibleGridControlBase::removeAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener>& _rxListener ) +{ + if( !(_rxListener.is() && getClientId( )) ) + return; + + SolarMutexGuard g; + + sal_Int32 nListenerCount = AccessibleEventNotifier::removeEventListener( getClientId( ), _rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + AccessibleEventNotifier::TClientId nId( getClientId( ) ); + setClientId( 0 ); + AccessibleEventNotifier::revokeClient( nId ); + } +} + +// XTypeProvider + +Sequence< sal_Int8 > SAL_CALL AccessibleGridControlBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XServiceInfo + +sal_Bool SAL_CALL AccessibleGridControlBase::supportsService( + const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL AccessibleGridControlBase::getSupportedServiceNames() +{ + return { "com.sun.star.accessibility.AccessibleContext" }; +} +// internal virtual methods + +bool AccessibleGridControlBase::implIsShowing() +{ + bool bShowing = false; + if( m_xParent.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleComponent > + xParentComp( m_xParent->getAccessibleContext(), uno::UNO_QUERY ); + if( xParentComp.is() ) + bShowing = implGetBoundingBox().Overlaps( + VCLRectangle( xParentComp->getBounds() ) ); + } + return bShowing; +} + +rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleGridControlBase::implCreateStateSetHelper() +{ + rtl::Reference<::utl::AccessibleStateSetHelper> + pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with m_xParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + // GridControl fills StateSet with states depending on object type + m_aTable.FillAccessibleStateSet( *pStateSetHelper, getType() ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + return pStateSetHelper; +} + +// internal helper methods + +bool AccessibleGridControlBase::isAlive() const +{ + ::osl::MutexGuard g(m_aMutex); // guards rBHelper members + return !rBHelper.bDisposed && !rBHelper.bInDispose; +} + +void AccessibleGridControlBase::ensureIsAlive() const +{ + if( !isAlive() ) + throw lang::DisposedException(); +} + +tools::Rectangle AccessibleGridControlBase::getBoundingBox() +{ + SolarMutexGuard aSolarGuard; + ensureIsAlive(); + tools::Rectangle aRect = implGetBoundingBox(); + if ( aRect.Left() == 0 && aRect.Top() == 0 && aRect.Right() == 0 && aRect.Bottom() == 0 ) + { + SAL_WARN( "accessibility", "rectangle doesn't exist" ); + } + return aRect; +} + +tools::Rectangle AccessibleGridControlBase::getBoundingBoxOnScreen() +{ + SolarMutexGuard aSolarGuard; + ensureIsAlive(); + tools::Rectangle aRect = implGetBoundingBoxOnScreen(); + if ( aRect.Left() == 0 && aRect.Top() == 0 && aRect.Right() == 0 && aRect.Bottom() == 0 ) + { + SAL_WARN( "accessibility", "rectangle doesn't exist" ); + } + return aRect; +} + +void AccessibleGridControlBase::commitEvent( + sal_Int16 _nEventId, const Any& _rNewValue, const Any& _rOldValue ) +{ + SolarMutexGuard g; + + if ( !getClientId( ) ) + // if we don't have a client id for the notifier, then we don't have listeners, then + // we don't need to notify anything + return; + + // build an event object + AccessibleEventObject aEvent; + aEvent.Source = *this; + aEvent.EventId = _nEventId; + aEvent.OldValue = _rOldValue; + aEvent.NewValue = _rNewValue; + + // let the notifier handle this event + + AccessibleEventNotifier::addEvent( getClientId( ), aEvent ); +} + +sal_Int16 SAL_CALL AccessibleGridControlBase::getAccessibleRole() +{ + ensureIsAlive(); + sal_Int16 nRole = AccessibleRole::UNKNOWN; + switch ( m_eObjType ) + { + case TCTYPE_ROWHEADERCELL: + nRole = AccessibleRole::ROW_HEADER; + break; + case TCTYPE_COLUMNHEADERCELL: + nRole = AccessibleRole::COLUMN_HEADER; + break; + case TCTYPE_COLUMNHEADERBAR: + case TCTYPE_ROWHEADERBAR: + case TCTYPE_TABLE: + nRole = AccessibleRole::TABLE; + break; + case TCTYPE_TABLECELL: + nRole = AccessibleRole::TABLE_CELL; + break; + case TCTYPE_GRIDCONTROL: + nRole = AccessibleRole::PANEL; + break; + } + return nRole; +} + +css::uno::Reference<css::accessibility::XAccessible > SAL_CALL AccessibleGridControlBase::getAccessibleAtPoint( const css::awt::Point& ) +{ + return nullptr; +} + +sal_Int32 SAL_CALL AccessibleGridControlBase::getForeground( ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + + Color nColor; + vcl::Window* pInst = m_aTable.GetWindowInstance(); + if ( pInst ) + { + if ( pInst->IsControlForeground() ) + nColor = pInst->GetControlForeground(); + else + { + vcl::Font aFont; + if ( pInst->IsControlFont() ) + aFont = pInst->GetControlFont(); + else + aFont = pInst->GetFont(); + nColor = aFont.GetColor(); + } + } + return sal_Int32(nColor); +} + +sal_Int32 SAL_CALL AccessibleGridControlBase::getBackground( ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + Color nColor; + vcl::Window* pInst = m_aTable.GetWindowInstance(); + if ( pInst ) + { + if ( pInst->IsControlBackground() ) + nColor = pInst->GetControlBackground(); + else + nColor = pInst->GetBackground().GetColor(); + } + return sal_Int32(nColor); +} + + +GridControlAccessibleElement::GridControlAccessibleElement( const css::uno::Reference< css::accessibility::XAccessible >& rxParent, + ::vcl::table::IAccessibleTable& rTable, + ::vcl::table::AccessibleTableControlObjType eObjType ) + :AccessibleGridControlBase( rxParent, rTable, eObjType ) +{ +} + +// XInterface +IMPLEMENT_FORWARD_XINTERFACE2( GridControlAccessibleElement, AccessibleGridControlBase, GridControlAccessibleElement_Base) + +// XTypeProvider +IMPLEMENT_FORWARD_XTYPEPROVIDER2( GridControlAccessibleElement, AccessibleGridControlBase, GridControlAccessibleElement_Base ) + +// css::accessibility::XAccessible + +css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL GridControlAccessibleElement::getAccessibleContext() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return this; +} + +GridControlAccessibleElement::~GridControlAccessibleElement( ) +{ +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlHeader.cxx b/accessibility/source/extended/AccessibleGridControlHeader.cxx new file mode 100644 index 000000000..d88b34218 --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlHeader.cxx @@ -0,0 +1,231 @@ +/* -*- 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 <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <extended/AccessibleGridControlHeader.hxx> +#include <extended/AccessibleGridControlHeaderCell.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::accessibility; +using namespace ::vcl; +using namespace ::vcl::table; + + +namespace accessibility { + + +AccessibleGridControlHeader::AccessibleGridControlHeader( + const Reference< XAccessible >& rxParent, + ::vcl::table::IAccessibleTable& rTable, + ::vcl::table::AccessibleTableControlObjType eObjType): + AccessibleGridControlTableBase( rxParent, rTable, eObjType ) +{ + OSL_ENSURE( isRowBar() || isColumnBar(), + "extended/AccessibleGridControlHeaderBar - invalid object type" ); +} + +// XAccessibleContext --------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleGridControlHeader::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + if (nChildIndex<0 || nChildIndex>=getAccessibleChildCount()) + throw IndexOutOfBoundsException(); + ensureIsAlive(); + Reference< XAccessible > xChild; + if(m_eObjType == vcl::table::TCTYPE_COLUMNHEADERBAR) + { + rtl::Reference<AccessibleGridControlHeaderCell> pColHeaderCell = new AccessibleGridControlHeaderCell(nChildIndex, this, m_aTable, vcl::table::TCTYPE_COLUMNHEADERCELL); + xChild = pColHeaderCell; + } + else if(m_eObjType == vcl::table::TCTYPE_ROWHEADERBAR) + { + rtl::Reference<AccessibleGridControlHeaderCell> pRowHeaderCell = new AccessibleGridControlHeaderCell(nChildIndex, this, m_aTable, vcl::table::TCTYPE_ROWHEADERCELL); + xChild = pRowHeaderCell; + } + return xChild; +} + +sal_Int32 SAL_CALL AccessibleGridControlHeader::getAccessibleIndexInParent() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + if(m_eObjType == vcl::table::TCTYPE_ROWHEADERBAR && m_aTable.HasColHeader()) + return 1; + else + return 0; +} + +// XAccessibleComponent ------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleGridControlHeader::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + + sal_Int32 nRow = 0; + sal_Int32 nColumnPos = 0; + bool bConverted = m_aTable.ConvertPointToCellAddress(nRow, nColumnPos, VCLPoint(rPoint)); + return bConverted ? implGetChild( nRow, nColumnPos ) : Reference< XAccessible >(); +} + +void SAL_CALL AccessibleGridControlHeader::grabFocus() +{ + ensureIsAlive(); + // focus on header not supported +} + +// XAccessibleTable ----------------------------------------------------------- + +OUString SAL_CALL AccessibleGridControlHeader::getAccessibleRowDescription( sal_Int32 nRow ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidRow( nRow ); + return OUString(); // no headers in headers +} + +OUString SAL_CALL AccessibleGridControlHeader::getAccessibleColumnDescription( sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidColumn( nColumn ); + return OUString(); // no headers in headers +} + +Reference< XAccessibleTable > SAL_CALL AccessibleGridControlHeader::getAccessibleRowHeaders() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return nullptr; // no headers in headers +} + +Reference< XAccessibleTable > SAL_CALL AccessibleGridControlHeader::getAccessibleColumnHeaders() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return nullptr; // no headers in headers +} +//not selectable +Sequence< sal_Int32 > SAL_CALL AccessibleGridControlHeader::getSelectedAccessibleRows() +{ + return {}; +} +//columns aren't selectable +Sequence< sal_Int32 > SAL_CALL AccessibleGridControlHeader::getSelectedAccessibleColumns() +{ + return {}; +} +//row headers not selectable +sal_Bool SAL_CALL AccessibleGridControlHeader::isAccessibleRowSelected( sal_Int32 /*nRow*/ ) +{ + return false; +} +//columns aren't selectable +sal_Bool SAL_CALL AccessibleGridControlHeader::isAccessibleColumnSelected( sal_Int32 ) +{ + return false; +} +//not implemented +Reference< XAccessible > SAL_CALL AccessibleGridControlHeader::getAccessibleCellAt( + sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ ) +{ + return nullptr; +} +// not selectable +sal_Bool SAL_CALL AccessibleGridControlHeader::isAccessibleSelected( + sal_Int32 /*nRow*/, sal_Int32 /*nColumn */) +{ + return false; +} + +// XServiceInfo --------------------------------------------------------------- + +OUString SAL_CALL AccessibleGridControlHeader::getImplementationName() +{ + return "com.sun.star.accessibility.AccessibleGridControlHeader"; +} + +Sequence< sal_Int8 > SAL_CALL AccessibleGridControlHeader::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// internal virtual methods --------------------------------------------------- + +tools::Rectangle AccessibleGridControlHeader::implGetBoundingBox() +{ + vcl::Window* pParent = m_aTable.GetAccessibleParentWindow(); + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( pParent ) ); + tools::Rectangle aHeaderRect (m_aTable.calcHeaderRect(isColumnBar())); + if(isColumnBar()) + return tools::Rectangle(aGridRect.TopLeft(), Size(aGridRect.getWidth(),aHeaderRect.getHeight())); + else + return tools::Rectangle(aGridRect.TopLeft(), Size(aHeaderRect.getWidth(),aGridRect.getHeight())); + +} + +tools::Rectangle AccessibleGridControlHeader::implGetBoundingBoxOnScreen() +{ + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( nullptr ) ); + tools::Rectangle aHeaderRect (m_aTable.calcHeaderRect(isColumnBar())); + if(isColumnBar()) + return tools::Rectangle(aGridRect.TopLeft(), Size(aGridRect.getWidth(),aHeaderRect.getHeight())); + else + return tools::Rectangle(aGridRect.TopLeft(), Size(aHeaderRect.getWidth(),aGridRect.getHeight())); +} + +// internal helper methods ---------------------------------------------------- +Reference< XAccessible > AccessibleGridControlHeader::implGetChild( + sal_Int32 nRow, sal_uInt32 nColumnPos ) +{ + Reference< XAccessible > xChild; + if(m_eObjType == vcl::table::TCTYPE_COLUMNHEADERBAR) + { + rtl::Reference<AccessibleGridControlHeaderCell> pColHeaderCell = new AccessibleGridControlHeaderCell(nColumnPos, this, m_aTable, vcl::table::TCTYPE_COLUMNHEADERCELL); + xChild = pColHeaderCell; + } + else if(m_eObjType == vcl::table::TCTYPE_ROWHEADERBAR) + { + rtl::Reference<AccessibleGridControlHeaderCell> pRowHeaderCell = new AccessibleGridControlHeaderCell(nRow, this, m_aTable, vcl::table::TCTYPE_ROWHEADERCELL); + xChild = pRowHeaderCell; + } + return xChild; +} + +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlHeaderCell.cxx b/accessibility/source/extended/AccessibleGridControlHeaderCell.cxx new file mode 100644 index 000000000..72333d340 --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlHeaderCell.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <extended/AccessibleGridControlHeaderCell.hxx> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> + +namespace accessibility +{ + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::uno; + using namespace ::vcl; + using namespace ::vcl::table; + +AccessibleGridControlHeaderCell::AccessibleGridControlHeaderCell(sal_Int32 _nColumnRowId, + const Reference< XAccessible >& rxParent, + IAccessibleTable& rTable, + AccessibleTableControlObjType eObjType) +: AccessibleGridControlCell( rxParent, rTable, _nColumnRowId, 0, eObjType) +, m_nColumnRowId(_nColumnRowId) +{ +} +/** Creates a new AccessibleStateSetHelper and fills it with states of the + current object. + @return + A filled AccessibleStateSetHelper. +*/ +rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleGridControlHeaderCell::implCreateStateSetHelper() +{ + rtl::Reference<::utl::AccessibleStateSetHelper> + pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with mxParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE ); + pStateSetHelper->AddState( AccessibleStateType::TRANSIENT ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + + if ( m_aTable.IsRowSelected(m_nColumnRowId) ) + pStateSetHelper->AddState( AccessibleStateType::SELECTED ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; +} + +/** @return + The count of visible children. +*/ +sal_Int32 SAL_CALL AccessibleGridControlHeaderCell::getAccessibleChildCount() +{ + return 0; +} + + +/** @return + The XAccessible interface of the specified child. +*/ +Reference<XAccessible > SAL_CALL AccessibleGridControlHeaderCell::getAccessibleChild( sal_Int32 ) +{ + throw IndexOutOfBoundsException(); +} +// XInterface ------------------------------------------------------------- + + /** Queries for a new interface. */ + css::uno::Any SAL_CALL AccessibleGridControlHeaderCell::queryInterface( const css::uno::Type& rType ) + { + Any aRet = AccessibleGridControlCell::queryInterface(rType); + return aRet; + } + + /** Acquires the object (calls acquire() on base class). */ + void SAL_CALL AccessibleGridControlHeaderCell::acquire() noexcept + { + AccessibleGridControlCell::acquire(); + } + + /** Releases the object (calls release() on base class). */ + void SAL_CALL AccessibleGridControlHeaderCell::release() noexcept + { + AccessibleGridControlCell::release(); + } + /** @return The XAccessibleContext interface of this object. */ + Reference< css::accessibility::XAccessibleContext > SAL_CALL AccessibleGridControlHeaderCell::getAccessibleContext() + { + ensureIsAlive(); + return this; + } + + +/** Grabs the focus to the column header. */ +void SAL_CALL AccessibleGridControlHeaderCell::grabFocus() +{ +} + +/** @return + The name of this class. +*/ +OUString SAL_CALL AccessibleGridControlHeaderCell::getImplementationName() +{ + return "com.sun.star.accessibility.AccessibleGridControlHeaderCell"; +} + +tools::Rectangle AccessibleGridControlHeaderCell::implGetBoundingBox() +{ + vcl::Window* pParent = m_aTable.GetAccessibleParentWindow(); + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( pParent ) ); + sal_Int32 nIndex = getAccessibleIndexInParent(); + tools::Rectangle aCellRect; + if(m_eObjType == TCTYPE_COLUMNHEADERCELL) + aCellRect = m_aTable.calcHeaderCellRect(true, nIndex); + else + aCellRect = m_aTable.calcHeaderCellRect(false, nIndex); + return tools::Rectangle(Point(aGridRect.Left()+aCellRect.Left(),aGridRect.Top()+aCellRect.Top()), aCellRect.GetSize()); +} + + +tools::Rectangle AccessibleGridControlHeaderCell::implGetBoundingBoxOnScreen() +{ + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( nullptr ) ); + sal_Int32 nIndex = getAccessibleIndexInParent(); + tools::Rectangle aCellRect; + if(m_eObjType == TCTYPE_COLUMNHEADERCELL) + aCellRect = m_aTable.calcHeaderCellRect(true, nIndex); + else + aCellRect = m_aTable.calcHeaderCellRect(false, nIndex); + return tools::Rectangle(Point(aGridRect.Left()+aCellRect.Left(),aGridRect.Top()+aCellRect.Top()), aCellRect.GetSize()); +} + +sal_Int32 SAL_CALL AccessibleGridControlHeaderCell::getAccessibleIndexInParent() +{ + SolarMutexGuard g; + + ensureIsAlive(); + sal_Int32 nIndex = m_nColumnRowId; + return nIndex; +} + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlTable.cxx b/accessibility/source/extended/AccessibleGridControlTable.cxx new file mode 100644 index 000000000..00788cdf2 --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlTable.cxx @@ -0,0 +1,388 @@ +/* -*- 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 <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <extended/AccessibleGridControlTable.hxx> +#include <extended/AccessibleGridControlTableCell.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::vcl; +using namespace ::vcl::table; + + +namespace accessibility { + + +AccessibleGridControlTable::AccessibleGridControlTable( + const Reference< XAccessible >& rxParent, + IAccessibleTable& rTable) : + AccessibleGridControlTableBase( rxParent, rTable, TCTYPE_TABLE ) +{ +} + +// XAccessibleContext --------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleGridControlTable::getAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + sal_Int32 nCount = getAccessibleChildCount(); + if(m_aCellVector.empty() || m_aCellVector.size() != static_cast<unsigned>(nCount)) + { + m_aCellVector.resize(nCount); + } + if(!m_aCellVector[nChildIndex].is()) + { + rtl::Reference<AccessibleGridControlTableCell> pCell = new AccessibleGridControlTableCell(this, m_aTable, nChildIndex/m_aTable.GetColumnCount(), nChildIndex%m_aTable.GetColumnCount()); + m_aCellVector[nChildIndex] = pCell; + } + return m_aCellVector[nChildIndex]; +} + +sal_Int32 SAL_CALL AccessibleGridControlTable::getAccessibleIndexInParent() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + if(m_aTable.HasRowHeader() && m_aTable.HasColHeader()) + return 0; + else if((!m_aTable.HasRowHeader() && m_aTable.HasColHeader()) || (m_aTable.HasRowHeader() && !m_aTable.HasColHeader()) ) + return 1; + else + return 2; +} + +// XAccessibleComponent ------------------------------------------------------- + +Reference< XAccessible > SAL_CALL +AccessibleGridControlTable::getAccessibleAtPoint( const awt::Point& rPoint ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + + Reference< XAccessible > xChild; + sal_Int32 nRow = 0; + sal_Int32 nColumnPos = 0; + if( m_aTable.ConvertPointToCellAddress( nRow, nColumnPos, VCLPoint( rPoint ) ) ) + xChild = new AccessibleGridControlTableCell(this, m_aTable, nRow, nColumnPos); + return xChild; +} + +void SAL_CALL AccessibleGridControlTable::grabFocus() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + m_aTable.GrabFocus(); +} + +// XAccessibleTable ----------------------------------------------------------- + +OUString SAL_CALL AccessibleGridControlTable::getAccessibleRowDescription( sal_Int32 nRow ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidRow( nRow ); + return "row description"; +} + +OUString SAL_CALL AccessibleGridControlTable::getAccessibleColumnDescription( sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidColumn( nColumn ); + return "col description"; +} + +Reference< XAccessibleTable > SAL_CALL AccessibleGridControlTable::getAccessibleRowHeaders() +{ + SolarMutexGuard g; + + ensureIsAlive(); + if(m_aTable.HasColHeader()) + return implGetHeaderBar( 1 ); + else + return implGetHeaderBar( 0 ); +} + +Reference< XAccessibleTable > SAL_CALL AccessibleGridControlTable::getAccessibleColumnHeaders() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return implGetHeaderBar( 0 ); +} + +Sequence< sal_Int32 > SAL_CALL AccessibleGridControlTable::getSelectedAccessibleRows() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + Sequence< sal_Int32 > aSelSeq; + implGetSelectedRows( aSelSeq ); + return aSelSeq; +} + +//columns aren't selectable +Sequence< sal_Int32 > SAL_CALL AccessibleGridControlTable::getSelectedAccessibleColumns() +{ + return {}; +} + +sal_Bool SAL_CALL AccessibleGridControlTable::isAccessibleRowSelected( sal_Int32 nRow ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidRow( nRow ); + Sequence< sal_Int32 > selectedRows = getSelectedAccessibleRows(); + return comphelper::findValue(selectedRows, nRow) != -1; +} + +//columns aren't selectable +sal_Bool SAL_CALL AccessibleGridControlTable::isAccessibleColumnSelected( sal_Int32 ) +{ + return false; +} + +Reference< XAccessible > SAL_CALL AccessibleGridControlTable::getAccessibleCellAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + sal_Int32 nChildIndex = nRow*m_aTable.GetColumnCount() + nColumn; + return getAccessibleChild(nChildIndex); +} + +sal_Bool SAL_CALL AccessibleGridControlTable::isAccessibleSelected( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + //selection of single cells not possible, so if row is selected, the cell will be selected too + return isAccessibleRowSelected(nRow); +} +void SAL_CALL AccessibleGridControlTable::selectAccessibleChild( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + sal_Int32 nColumns = m_aTable.GetColumnCount(); + sal_Int32 nRow = nChildIndex / nColumns; + m_aTable.SelectRow( nRow, true ); +} +sal_Bool SAL_CALL AccessibleGridControlTable::isAccessibleChildSelected( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + sal_Int32 nColumns = m_aTable.GetColumnCount(); + sal_Int32 nRow = nChildIndex / nColumns; + return isAccessibleRowSelected(nRow); +} +void SAL_CALL AccessibleGridControlTable::clearAccessibleSelection() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + m_aTable.SelectAllRows( false ); +} +void SAL_CALL AccessibleGridControlTable::selectAllAccessibleChildren() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + Sequence< sal_Int32 > selectedRows = getSelectedAccessibleRows(); + auto selectedRowsRange = asNonConstRange(selectedRows); + for(tools::Long i=0; i<m_aTable.GetRowCount(); i++) + selectedRowsRange[i]=i; +} +sal_Int32 SAL_CALL AccessibleGridControlTable::getSelectedAccessibleChildCount() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + Sequence< sal_Int32 > selectedRows = getSelectedAccessibleRows(); + sal_Int32 nColumns = m_aTable.GetColumnCount(); + return selectedRows.getLength()*nColumns; +} +Reference< XAccessible > SAL_CALL +AccessibleGridControlTable::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + if(isAccessibleChildSelected(nSelectedChildIndex)) + return getAccessibleChild(nSelectedChildIndex); + else + return nullptr; +} +//not implemented yet, because only row selection possible +void SAL_CALL AccessibleGridControlTable::deselectAccessibleChild( + sal_Int32 ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); +} +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL AccessibleGridControlTable::queryInterface( const uno::Type& rType ) +{ + Any aAny( AccessibleGridControlTableBase::queryInterface( rType ) ); + return aAny.hasValue() ? + aAny : AccessibleGridControlTableSelectionImplHelper::queryInterface( rType ); +} + +void SAL_CALL AccessibleGridControlTable::acquire() noexcept +{ + AccessibleGridControlTableBase::acquire(); +} + +void SAL_CALL AccessibleGridControlTable::release() noexcept +{ + AccessibleGridControlTableBase::release(); +} +// XServiceInfo --------------------------------------------------------------- + +OUString SAL_CALL AccessibleGridControlTable::getImplementationName() +{ + return "com.sun.star.accessibility.AccessibleGridControlTable"; +} + +void AccessibleGridControlTable::dispose() +{ + for (rtl::Reference<AccessibleGridControlTableCell>& rxCell : m_aCellVector) + { + if (rxCell.is()) + { + rxCell->dispose(); + rxCell.clear(); + } + } + + AccessibleGridControlTableBase::dispose(); +} + +void AccessibleGridControlTable::commitEvent(sal_Int16 nEventId, const css::uno::Any& rNewValue, + const css::uno::Any& rOldValue) +{ + if (nEventId == AccessibleEventId::TABLE_MODEL_CHANGED) + { + AccessibleTableModelChange aChange; + if (rNewValue >>= aChange) + { + assert(aChange.Type != AccessibleTableModelChangeType::COLUMNS_REMOVED); + + if (aChange.Type == AccessibleTableModelChangeType::ROWS_REMOVED) + { + int nColCount = m_aTable.GetColumnCount(); + // check valid index - entries are inserted lazily + size_t const nStart = nColCount * aChange.FirstRow; + size_t const nEnd = nColCount * aChange.LastRow; + if (nStart < m_aCellVector.size()) + { + m_aCellVector.erase( + m_aCellVector.begin() + nStart, + m_aCellVector.begin() + std::min(m_aCellVector.size(), nEnd)); + } + } + } + } + + AccessibleGridControlBase::commitEvent(nEventId, rNewValue, rOldValue); +} + +// internal virtual methods --------------------------------------------------- + +tools::Rectangle AccessibleGridControlTable::implGetBoundingBox() +{ + vcl::Window* pParent = m_aTable.GetAccessibleParentWindow(); + DBG_ASSERT( pParent, "implGetBoundingBox - missing parent window" ); + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( pParent )); + tools::Rectangle aTableRect( m_aTable.calcTableRect() ); + tools::Long nX = aGridRect.Left() + aTableRect.Left(); + tools::Long nY = aGridRect.Top() + aTableRect.Top(); + tools::Long nWidth = aGridRect.GetSize().Width()-aTableRect.Left(); + tools::Long nHeight = aGridRect.GetSize().Height()-aTableRect.Top(); + tools::Rectangle aTable( Point( nX, nY ), Size( nWidth, nHeight )); + return aTable; +} + +tools::Rectangle AccessibleGridControlTable::implGetBoundingBoxOnScreen() +{ + tools::Rectangle aGridRect( m_aTable.GetWindowExtentsRelative( nullptr )); + tools::Rectangle aTableRect( m_aTable.calcTableRect() ); + tools::Long nX = aGridRect.Left() + aTableRect.Left(); + tools::Long nY = aGridRect.Top() + aTableRect.Top(); + tools::Long nWidth = aGridRect.GetSize().Width()-aTableRect.Left(); + tools::Long nHeight = aGridRect.GetSize().Height()-aTableRect.Top(); + tools::Rectangle aTable( Point( nX, nY ), Size( nWidth, nHeight )); + return aTable; +} +// internal helper methods ---------------------------------------------------- +Reference< XAccessibleTable > AccessibleGridControlTable::implGetHeaderBar( + sal_Int32 nChildIndex ) +{ + Reference< XAccessible > xRet; + Reference< XAccessibleContext > xContext( m_xParent, uno::UNO_QUERY ); + if( xContext.is() ) + { + try + { + xRet = xContext->getAccessibleChild( nChildIndex ); + } + catch (const lang::IndexOutOfBoundsException&) + { + OSL_FAIL( "implGetHeaderBar - wrong child index" ); + } + // RuntimeException goes to caller + } + return Reference< XAccessibleTable >( xRet, uno::UNO_QUERY ); +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlTableBase.cxx b/accessibility/source/extended/AccessibleGridControlTableBase.cxx new file mode 100644 index 000000000..7ed42bc6b --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlTableBase.cxx @@ -0,0 +1,240 @@ +/* -*- 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 <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <extended/AccessibleGridControlTableBase.hxx> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/sequence.hxx> + +using css::uno::Reference; +using css::uno::Sequence; +using css::uno::Any; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using namespace ::vcl; +using namespace ::vcl::table; + + +namespace accessibility { + + +AccessibleGridControlTableBase::AccessibleGridControlTableBase( + const Reference< XAccessible >& rxParent, + IAccessibleTable& rTable, + AccessibleTableControlObjType eObjType ) : + GridControlAccessibleElement( rxParent, rTable, eObjType ) +{ +} + +// XAccessibleContext --------------------------------------------------------- + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleChildCount() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + sal_Int32 nChildren = 0; + if(m_eObjType == TCTYPE_ROWHEADERBAR) + nChildren = m_aTable.GetRowCount(); + else if(m_eObjType == TCTYPE_TABLE) + nChildren = m_aTable.GetRowCount()*m_aTable.GetColumnCount(); + else if(m_eObjType == TCTYPE_COLUMNHEADERBAR) + nChildren = m_aTable.GetColumnCount(); + return nChildren; +} + +sal_Int16 SAL_CALL AccessibleGridControlTableBase::getAccessibleRole() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return AccessibleRole::TABLE; +} + +// XAccessibleTable ----------------------------------------------------------- + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleRowCount() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + return m_aTable.GetRowCount(); +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleColumnCount() +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + return m_aTable.GetColumnCount(); +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleRowExtentAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return 1; // merged cells not supported +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleColumnExtentAt( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return 1; // merged cells not supported +} + +Reference< XAccessible > SAL_CALL AccessibleGridControlTableBase::getAccessibleCaption() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return nullptr; // not supported +} + +Reference< XAccessible > SAL_CALL AccessibleGridControlTableBase::getAccessibleSummary() +{ + SolarMutexGuard g; + + ensureIsAlive(); + return nullptr; // not supported +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleIndex( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidAddress( nRow, nColumn ); + return nRow * m_aTable.GetColumnCount() + nColumn; +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleRow( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + return implGetRow( nChildIndex ); +} + +sal_Int32 SAL_CALL AccessibleGridControlTableBase::getAccessibleColumn( sal_Int32 nChildIndex ) +{ + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + ensureIsValidIndex( nChildIndex ); + return implGetColumn( nChildIndex ); +} + +// XInterface ----------------------------------------------------------------- + +Any SAL_CALL AccessibleGridControlTableBase::queryInterface( const uno::Type& rType ) +{ + Any aAny( GridControlAccessibleElement::queryInterface( rType ) ); + return aAny.hasValue() ? + aAny : AccessibleGridControlTableImplHelper::queryInterface( rType ); +} + +void SAL_CALL AccessibleGridControlTableBase::acquire() noexcept +{ + GridControlAccessibleElement::acquire(); +} + +void SAL_CALL AccessibleGridControlTableBase::release() noexcept +{ + GridControlAccessibleElement::release(); +} + +// XTypeProvider -------------------------------------------------------------- + +Sequence< uno::Type > SAL_CALL AccessibleGridControlTableBase::getTypes() +{ + return ::comphelper::concatSequences( + GridControlAccessibleElement::getTypes(), + AccessibleGridControlTableImplHelper::getTypes() ); +} + +Sequence< sal_Int8 > SAL_CALL AccessibleGridControlTableBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// internal helper methods ---------------------------------------------------- + +sal_Int32 AccessibleGridControlTableBase::implGetRow( sal_Int32 nChildIndex ) const +{ + sal_Int32 nColumns = m_aTable.GetColumnCount(); + return nColumns ? (nChildIndex / nColumns) : 0; +} + +sal_Int32 AccessibleGridControlTableBase::implGetColumn( sal_Int32 nChildIndex ) const +{ + sal_Int32 nColumns = m_aTable.GetColumnCount(); + return nColumns ? (nChildIndex % nColumns) : 0; +} + +void AccessibleGridControlTableBase::implGetSelectedRows( Sequence< sal_Int32 >& rSeq ) +{ + sal_Int32 const selectionCount( m_aTable.GetSelectedRowCount() ); + rSeq.realloc( selectionCount ); + auto pSeq = rSeq.getArray(); + for ( sal_Int32 i=0; i<selectionCount; ++i ) + pSeq[i] = m_aTable.GetSelectedRowIndex(i); +} + +void AccessibleGridControlTableBase::ensureIsValidRow( sal_Int32 nRow ) +{ + if( nRow >= m_aTable.GetRowCount() ) + throw lang::IndexOutOfBoundsException( "row index is invalid", *this ); +} + +void AccessibleGridControlTableBase::ensureIsValidColumn( sal_Int32 nColumn ) +{ + if( nColumn >= m_aTable.GetColumnCount() ) + throw lang::IndexOutOfBoundsException( "column index is invalid", *this ); +} + +void AccessibleGridControlTableBase::ensureIsValidAddress( + sal_Int32 nRow, sal_Int32 nColumn ) +{ + ensureIsValidRow( nRow ); + ensureIsValidColumn( nColumn ); +} + +void AccessibleGridControlTableBase::ensureIsValidIndex( sal_Int32 nChildIndex ) +{ + if( nChildIndex >= m_aTable.GetRowCount()*m_aTable.GetColumnCount() ) + throw lang::IndexOutOfBoundsException( "child index is invalid", *this ); +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleGridControlTableCell.cxx b/accessibility/source/extended/AccessibleGridControlTableCell.cxx new file mode 100644 index 000000000..20bf37698 --- /dev/null +++ b/accessibility/source/extended/AccessibleGridControlTableCell.cxx @@ -0,0 +1,355 @@ +/* -*- 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/AccessibleGridControlTableCell.hxx> +#include <toolkit/helper/convert.hxx> +#include <vcl/accessibletable.hxx> +#include <vcl/svapp.hxx> +#include <tools/gen.hxx> +#include <tools/debug.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +namespace accessibility +{ + namespace + { + // FIXME this is a copy'n'paste from + // source/extended/AccessibleBrowseBoxTableCell.cxx, get rid of that... + /// @throws css::lang::IndexOutOfBoundsException + void checkIndex_Impl( sal_Int32 _nIndex, const OUString& _sText ) + { + if ( _nIndex >= _sText.getLength() ) + throw css::lang::IndexOutOfBoundsException(); + } + } + using namespace ::com::sun::star::lang; + using namespace utl; + using namespace comphelper; + using namespace ::com::sun::star::uno; + using ::com::sun::star::accessibility::XAccessible; + using namespace ::com::sun::star::accessibility; + using namespace ::vcl; + using namespace ::vcl::table; + + + // = AccessibleGridControlCell + + + AccessibleGridControlCell::AccessibleGridControlCell( + const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, ::vcl::table::IAccessibleTable& _rTable, + sal_Int32 _nRowPos, sal_uInt16 _nColPos, ::vcl::table::AccessibleTableControlObjType _eType ) + :AccessibleGridControlBase( _rxParent, _rTable, _eType ) + ,m_nRowPos( _nRowPos ) + ,m_nColPos( _nColPos ) + { + } + + void SAL_CALL AccessibleGridControlCell::grabFocus() + { + SolarMutexGuard aSolarGuard; + + m_aTable.GoToCell( m_nColPos, m_nRowPos ); + } + + OUString SAL_CALL AccessibleGridControlCell::getAccessibleName() + { + SolarMutexGuard g; + + ensureIsAlive(); + + OUString sAccName; + if (m_eObjType == TCTYPE_TABLECELL) + sAccName = m_aTable.GetAccessibleObjectName(TCTYPE_TABLECELL, m_nRowPos, m_nColPos); + else if (m_eObjType == TCTYPE_ROWHEADERCELL) + sAccName = m_aTable.GetAccessibleObjectName(TCTYPE_ROWHEADERCELL, m_nRowPos, 0); + else if (m_eObjType == TCTYPE_COLUMNHEADERCELL) + sAccName = m_aTable.GetAccessibleObjectName(TCTYPE_COLUMNHEADERCELL, 0, m_nRowPos); + else + assert(false && "Unhandled table cell type"); + return sAccName; + } + + // implementation of a table cell + OUString AccessibleGridControlTableCell::implGetText() + { + ensureIsAlive(); + return m_aTable.GetAccessibleCellText( getRowPos(), getColumnPos() ); + } + + css::lang::Locale AccessibleGridControlTableCell::implGetLocale() + { + ensureIsAlive(); + return m_aTable.GetAccessible()->getAccessibleContext()->getLocale(); + } + + void AccessibleGridControlTableCell::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex ) + { + nStartIndex = 0; + nEndIndex = 0; + } + + AccessibleGridControlTableCell::AccessibleGridControlTableCell(const css::uno::Reference<XAccessible >& _rxParent, + ::vcl::table::IAccessibleTable& _rTable, + sal_Int32 _nRowPos, + sal_uInt16 _nColPos) + :AccessibleGridControlCell( _rxParent, _rTable, _nRowPos, _nColPos, TCTYPE_TABLECELL ) + { + } + + // XInterface + + /** Queries for a new interface. */ + css::uno::Any SAL_CALL AccessibleGridControlTableCell::queryInterface( + const css::uno::Type& rType ) + { + Any aRet = AccessibleGridControlCell::queryInterface(rType); + if ( !aRet.hasValue() ) + aRet = AccessibleTextHelper_BASE::queryInterface(rType); + return aRet; + } + + /** Acquires the object (calls acquire() on base class). */ + void SAL_CALL AccessibleGridControlTableCell::acquire() noexcept + { + AccessibleGridControlCell::acquire(); + } + + /** Releases the object (calls release() on base class). */ + void SAL_CALL AccessibleGridControlTableCell::release() noexcept + { + AccessibleGridControlCell::release(); + } + + css::awt::Rectangle SAL_CALL AccessibleGridControlTableCell::getCharacterBounds( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + if ( !implIsValidIndex( nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return AWTRectangle( m_aTable.GetFieldCharacterBounds( getRowPos(), getColumnPos(), nIndex ) ); + } + + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getIndexAtPoint( const css::awt::Point& _aPoint ) + { + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + + return m_aTable.GetFieldIndexAtPoint( getRowPos(), getColumnPos(), VCLPoint( _aPoint ) ); + } + + /** @return + The name of this class. + */ + OUString SAL_CALL AccessibleGridControlTableCell::getImplementationName() + { + return "com.sun.star.accessibility.AccessibleGridControlTableCell"; + } + + /** @return The count of visible children. */ + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getAccessibleChildCount() + { + return 0; + } + + /** @return The css::accessibility::XAccessible interface of the specified child. */ + css::uno::Reference< css::accessibility::XAccessible > SAL_CALL AccessibleGridControlTableCell::getAccessibleChild( sal_Int32 ) + { + throw css::lang::IndexOutOfBoundsException(); + } + + /** Creates a new AccessibleStateSetHelper and fills it with states of the + current object. + @return + A filled AccessibleStateSetHelper. + */ + rtl::Reference<::utl::AccessibleStateSetHelper> AccessibleGridControlTableCell::implCreateStateSetHelper() + { + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSetHelper = new ::utl::AccessibleStateSetHelper; + + if( isAlive() ) + { + // SHOWING done with mxParent + if( implIsShowing() ) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + + m_aTable.FillAccessibleStateSetForCell( *pStateSetHelper, getRowPos(), static_cast< sal_uInt16 >( getColumnPos() ) ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; + } + + + // css::accessibility::XAccessible + + /** @return The css::accessibility::XAccessibleContext interface of this object. */ + css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL AccessibleGridControlTableCell::getAccessibleContext() + { + SolarMutexGuard g; + + ensureIsAlive(); + return this; + } + + // css::accessibility::XAccessibleContext + + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getAccessibleIndexInParent() + { + SolarMutexGuard aSolarGuard; + + ensureIsAlive(); + + return ( getRowPos() * m_aTable.GetColumnCount() ) + getColumnPos(); + } + + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getCaretPosition( ) + { + return -1; + } + sal_Bool SAL_CALL AccessibleGridControlTableCell::setCaretPosition ( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + + if ( !implIsValidRange( nIndex, nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + sal_Unicode SAL_CALL AccessibleGridControlTableCell::getCharacter( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + + return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex ); + } + css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleGridControlTableCell::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& ) + { + SolarMutexGuard aSolarGuard; + + OUString sText( implGetText() ); + + if ( !implIsValidIndex( nIndex, sText.getLength() ) ) + throw IndexOutOfBoundsException(); + + return css::uno::Sequence< css::beans::PropertyValue >(); + } + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getCharacterCount( ) + { + SolarMutexGuard aSolarGuard; + + return implGetText().getLength(); + } + + OUString SAL_CALL AccessibleGridControlTableCell::getSelectedText( ) + { + return OUString(); + } + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getSelectionStart( ) + { + return 0; + } + sal_Int32 SAL_CALL AccessibleGridControlTableCell::getSelectionEnd( ) + { + return 0; + } + sal_Bool SAL_CALL AccessibleGridControlTableCell::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + + if ( !implIsValidRange( nStartIndex, nEndIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + OUString SAL_CALL AccessibleGridControlTableCell::getText( ) + { + SolarMutexGuard aSolarGuard; + + return implGetText( ); + } + OUString SAL_CALL AccessibleGridControlTableCell::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + + return OCommonAccessibleText::implGetTextRange( implGetText(), nStartIndex, nEndIndex ); + } + css::accessibility::TextSegment SAL_CALL AccessibleGridControlTableCell::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + + return OCommonAccessibleText::getTextAtIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleGridControlTableCell::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + + return OCommonAccessibleText::getTextBeforeIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleGridControlTableCell::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + + return OCommonAccessibleText::getTextBehindIndex( nIndex ,aTextType); + } + sal_Bool SAL_CALL AccessibleGridControlTableCell::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + + OUString sText = implGetText(); + checkIndex_Impl( nStartIndex, sText ); + checkIndex_Impl( nEndIndex, sText ); + + //!!! don't know how to put a string into the clipboard + return false; + } + sal_Bool SAL_CALL AccessibleGridControlTableCell::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + + tools::Rectangle AccessibleGridControlTableCell::implGetBoundingBox() + { + vcl::Window* pParent = m_aTable.GetAccessibleParentWindow(); + DBG_ASSERT( pParent, "implGetBoundingBox - missing parent window" ); + tools::Rectangle aGridRect = m_aTable.GetWindowExtentsRelative( pParent ); + sal_Int32 nIndex = getAccessibleIndexInParent(); + tools::Rectangle aCellRect = m_aTable.calcCellRect(nIndex%m_aTable.GetColumnCount(), nIndex/m_aTable.GetColumnCount()); + tools::Long nX = aGridRect.Left() + aCellRect.Left(); + tools::Long nY = aGridRect.Top() + aCellRect.Top(); + tools::Rectangle aCell( Point( nX, nY ), aCellRect.GetSize()); + return aCell; + } + + tools::Rectangle AccessibleGridControlTableCell::implGetBoundingBoxOnScreen() + { + tools::Rectangle aGridRect = m_aTable.GetWindowExtentsRelative( nullptr ); + sal_Int32 nIndex = getAccessibleIndexInParent(); + tools::Rectangle aCellRect = m_aTable.calcCellRect(nIndex%m_aTable.GetColumnCount(), nIndex/m_aTable.GetColumnCount()); + tools::Long nX = aGridRect.Left() + aCellRect.Left(); + tools::Long nY = aGridRect.Top() + aCellRect.Top(); + tools::Rectangle aCell( Point( nX, nY ), aCellRect.GetSize()); + return aCell; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/AccessibleIconView.cxx b/accessibility/source/extended/AccessibleIconView.cxx new file mode 100644 index 000000000..6bc5c99e9 --- /dev/null +++ b/accessibility/source/extended/AccessibleIconView.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * 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/. + */ + +#include <sal/config.h> + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <toolkit/helper/convert.hxx> +#include <vcl/event.hxx> + +#include <extended/AccessibleIconView.hxx> + +namespace accessibility +{ +AccessibleIconView::AccessibleIconView( + SvTreeListBox const& _rListBox, + const css::uno::Reference<css::accessibility::XAccessible>& _xParent) + : AccessibleListBox(_rListBox, _xParent) +{ +} + +void AccessibleIconView::ProcessWindowEvent(const VclWindowEvent& rVclWindowEvent) +{ + if (!isAlive()) + return; + + switch (rVclWindowEvent.GetId()) + { + case VclEventId::WindowMouseMove: + if (MouseEvent* pMouseEvt = static_cast<MouseEvent*>(rVclWindowEvent.GetData())) + { + if (auto xChild = getAccessibleAtPoint(AWTPoint(pMouseEvt->GetPosPixel()))) + { + // Allow announcing the element on mouse hover + css::uno::Any aNew(xChild); + NotifyAccessibleEvent( + css::accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, {}, aNew); + } + } + break; + default: + AccessibleListBox::ProcessWindowEvent(rVclWindowEvent); + } +} +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/accessibility/source/extended/accessiblebrowseboxcell.cxx b/accessibility/source/extended/accessiblebrowseboxcell.cxx new file mode 100644 index 000000000..4996b7eed --- /dev/null +++ b/accessibility/source/extended/accessiblebrowseboxcell.cxx @@ -0,0 +1,69 @@ +/* -*- 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 <sal/types.h> +#include <vcl/accessibletableprovider.hxx> +#include <extended/accessiblebrowseboxcell.hxx> + +namespace accessibility +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::awt; + using namespace ::com::sun::star::accessibility; + + // AccessibleBrowseBoxCell + AccessibleBrowseBoxCell::AccessibleBrowseBoxCell( + const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, ::vcl::IAccessibleTableProvider& _rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, + sal_Int32 _nRowPos, sal_uInt16 _nColPos, AccessibleBrowseBoxObjType _eType ) + :AccessibleBrowseBoxBase( _rxParent, _rBrowseBox, _xFocusWindow, _eType ) + ,m_nRowPos( _nRowPos ) + ,m_nColPos( _nColPos ) + { + // set accessible name here, because for that we need the position of the cell + // and so the base class isn't capable of doing this + sal_Int32 nPos = _nRowPos * _rBrowseBox.GetColumnCount() + _nColPos; + OUString aAccName = _rBrowseBox.GetAccessibleObjectName( AccessibleBrowseBoxObjType::TableCell, nPos ); + implSetName( aAccName ); + } + + AccessibleBrowseBoxCell::~AccessibleBrowseBoxCell() + { + } + + void SAL_CALL AccessibleBrowseBoxCell::grabFocus() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + mpBrowseBox->GoToCell( m_nRowPos, m_nColPos ); + } + + ::tools::Rectangle AccessibleBrowseBoxCell::implGetBoundingBox() + { + return mpBrowseBox->GetFieldRectPixelAbs( m_nRowPos, m_nColPos, false, false ); + } + + ::tools::Rectangle AccessibleBrowseBoxCell::implGetBoundingBoxOnScreen() + { + return mpBrowseBox->GetFieldRectPixelAbs( m_nRowPos, m_nColPos, false ); + } +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibleeditbrowseboxcell.cxx b/accessibility/source/extended/accessibleeditbrowseboxcell.cxx new file mode 100644 index 000000000..54f069673 --- /dev/null +++ b/accessibility/source/extended/accessibleeditbrowseboxcell.cxx @@ -0,0 +1,240 @@ +/* -*- 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 <helper/accresmgr.hxx> +#include <strings.hrc> + +#include <extended/accessibleeditbrowseboxcell.hxx> +#include <comphelper/processfactory.hxx> +#include <utility> +#include <tools/diagnose_ex.h> + +namespace accessibility +{ + using namespace com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::awt; + using namespace ::comphelper; + + EditBrowseBoxTableCell::EditBrowseBoxTableCell( + const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, + const css::uno::Reference< css::accessibility::XAccessible >& _rxOwningAccessible, + const css::uno::Reference< css::accessibility::XAccessibleContext >& _xControlChild, + ::vcl::IAccessibleTableProvider& _rBrowseBox, + const css::uno::Reference< css::awt::XWindow >& _xFocusWindow, + sal_Int32 _nRowPos, + sal_uInt16 _nColPos) + :AccessibleBrowseBoxCell( _rxParent, _rBrowseBox, _xFocusWindow, _nRowPos, _nColPos ) + ,OAccessibleContextWrapperHelper( ::comphelper::getProcessComponentContext(), rBHelper, _xControlChild, _rxOwningAccessible, _rxParent ) + { + aggregateProxy( m_refCount, *this ); + } + + EditBrowseBoxTableCell::~EditBrowseBoxTableCell() + { + if ( !rBHelper.bDisposed ) + { + acquire(); // to prevent duplicate dtor calls + dispose(); + } + } + + OUString SAL_CALL EditBrowseBoxTableCell::getImplementationName() + { + return "com.sun.star.comp.svtools.TableCellProxy"; + } + + IMPLEMENT_FORWARD_XINTERFACE2( EditBrowseBoxTableCell, AccessibleBrowseBoxCell, OAccessibleContextWrapperHelper ) + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( EditBrowseBoxTableCell, AccessibleBrowseBoxCell, OAccessibleContextWrapperHelper ) + + void EditBrowseBoxTableCell::notifyTranslatedEvent( const AccessibleEventObject& _rEvent ) + { + commitEvent( _rEvent.EventId, _rEvent.NewValue, _rEvent.OldValue ); + } + + // css::accessibility::XAccessibleComponent + sal_Int32 SAL_CALL EditBrowseBoxTableCell::getForeground( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + css::uno::Reference< css::accessibility::XAccessibleComponent > xAccComp( m_xInnerContext, UNO_QUERY ); + if ( xAccComp.is() ) + return xAccComp->getForeground(); + return 0; + } + + sal_Int32 SAL_CALL EditBrowseBoxTableCell::getBackground( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + css::uno::Reference< css::accessibility::XAccessibleComponent > xAccComp( m_xInnerContext, UNO_QUERY ); + if ( xAccComp.is() ) + return xAccComp->getBackground(); + return 0; + } + + css::uno::Reference< css::accessibility::XAccessible > SAL_CALL EditBrowseBoxTableCell::getAccessibleParent( ) + { + return m_xParentAccessible; + } + + OUString SAL_CALL EditBrowseBoxTableCell::getAccessibleDescription() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return m_xInnerContext->getAccessibleDescription(); + } + + OUString SAL_CALL EditBrowseBoxTableCell::getAccessibleName() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return AccResId(RID_STR_ACC_COLUMN_NUM).replaceAll("%COLUMNNUMBER", OUString::number(getColumnPos()-1)) + ", " + + AccResId(RID_STR_ACC_ROW_NUM).replaceAll("%ROWNUMBER", OUString::number(getRowPos())); + } + + css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL EditBrowseBoxTableCell::getAccessibleRelationSet() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return baseGetAccessibleRelationSet( ); + } + + css::uno::Reference<css::accessibility::XAccessibleStateSet > SAL_CALL EditBrowseBoxTableCell::getAccessibleStateSet() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return m_xInnerContext->getAccessibleStateSet(); + // TODO: shouldn't we add an ACTIVE here? Isn't the EditBrowseBoxTableCell always ACTIVE? + } + + sal_Int32 SAL_CALL EditBrowseBoxTableCell::getAccessibleChildCount( ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return baseGetAccessibleChildCount(); + } + + css::uno::Reference< css::accessibility::XAccessible > SAL_CALL EditBrowseBoxTableCell::getAccessibleChild( sal_Int32 i ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return baseGetAccessibleChild( i ); + } + + sal_Int16 SAL_CALL EditBrowseBoxTableCell::getAccessibleRole() + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + return m_xInnerContext->getAccessibleRole( ); + } + + void SAL_CALL EditBrowseBoxTableCell::dispose() + { + // simply disambiguate. Note that the OComponentHelper base in AccessibleBrowseBoxCell + // will call our "disposing()", which will call "dispose()" on the OAccessibleContextWrapperHelper + // so there is no need to do this here. + AccessibleBrowseBoxCell::dispose(); + } + + void SAL_CALL EditBrowseBoxTableCell::disposing( const css::lang::EventObject& _rSource ) + { + AccessibleBrowseBoxCell::disposing( _rSource ); + OAccessibleContextWrapperHelper::disposing( _rSource ); + } + + void SAL_CALL EditBrowseBoxTableCell::disposing() + { + SolarMethodGuard aGuard(getMutex()); + + OAccessibleContextWrapperHelper::dispose(); + // TODO: do we need to dispose our inner object? The base class does this, but is it a good idea? + AccessibleBrowseBoxCell::disposing(); + } + + // EditBrowseBoxTableCell + EditBrowseBoxTableCellAccess::EditBrowseBoxTableCellAccess( + css::uno::Reference< css::accessibility::XAccessible > _xParent, css::uno::Reference< css::accessibility::XAccessible > _xControlAccessible, + css::uno::Reference< css::awt::XWindow > _xFocusWindow, + ::vcl::IAccessibleTableProvider& _rBrowseBox, sal_Int32 _nRowPos, sal_uInt16 _nColPos ) + :m_xParent(std::move( _xParent )) + ,m_xControlAccessible(std::move( _xControlAccessible )) + ,m_xFocusWindow(std::move( _xFocusWindow )) + ,m_pBrowseBox( &_rBrowseBox ) + ,m_nRowPos( _nRowPos ) + ,m_nColPos( _nColPos ) + { + } + + EditBrowseBoxTableCellAccess::~EditBrowseBoxTableCellAccess( ) + { + } + + css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL EditBrowseBoxTableCellAccess::getAccessibleContext( ) + { + if ( !m_pBrowseBox || !m_xControlAccessible.is() ) + throw DisposedException(); + css::uno::Reference< css::accessibility::XAccessibleContext > xMyContext( m_aContext ); + if ( !xMyContext.is() ) + { + css::uno::Reference< css::accessibility::XAccessibleContext > xInnerContext = m_xControlAccessible->getAccessibleContext(); + css::uno::Reference< css::accessibility::XAccessible > xMe( this ); + + xMyContext = new EditBrowseBoxTableCell( xMe, m_xParent, xInnerContext, *m_pBrowseBox, m_xFocusWindow, m_nRowPos, m_nColPos ); + m_aContext = xMyContext; + } + return xMyContext; + } + + void EditBrowseBoxTableCellAccess::disposing(std::unique_lock<std::mutex>&) + { + // dispose our context, if it still alive + css::uno::Reference< XComponent > xMyContext( m_aContext.get(), UNO_QUERY ); + if ( xMyContext.is() ) + { + try + { + xMyContext->dispose(); + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "accessibility", "EditBrowseBoxTableCellAccess::disposing: caught an exception while disposing the context!" ); + } + } + + m_pBrowseBox = nullptr; + m_xControlAccessible.clear(); + m_aContext.clear(); + // NO dispose of the inner object there: it is the css::accessibility::XAccessible of a window, and disposing + // it would delete the respective VCL window + } +} // namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibleiconchoicectrl.cxx b/accessibility/source/extended/accessibleiconchoicectrl.cxx new file mode 100644 index 000000000..9ad90c387 --- /dev/null +++ b/accessibility/source/extended/accessibleiconchoicectrl.cxx @@ -0,0 +1,340 @@ +/* -*- 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/accessibleiconchoicectrl.hxx> +#include <extended/accessibleiconchoicectrlentry.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 <unotools/accessiblestatesethelper.hxx> +#include <vcl/toolkit/ivctrl.hxx> +#include <cppuhelper/supportsservice.hxx> + + +namespace accessibility +{ + + + // class AccessibleIconChoiceCtrl ---------------------------------------------- + + 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() + + AccessibleIconChoiceCtrl::AccessibleIconChoiceCtrl( SvtIconChoiceCtrl const & _rIconCtrl, const Reference< XAccessible >& _xParent ) : + VCLXAccessibleComponent( _rIconCtrl.GetWindowPeer() ), + m_xParent ( _xParent ) + { + } + + IMPLEMENT_FORWARD_XINTERFACE2(AccessibleIconChoiceCtrl, VCLXAccessibleComponent, AccessibleIconChoiceCtrl_BASE) + IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleIconChoiceCtrl, VCLXAccessibleComponent, AccessibleIconChoiceCtrl_BASE) + + void AccessibleIconChoiceCtrl::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + if ( !isAlive() ) + return; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::ListboxSelect : + { + // First send an event that tells the listeners of a + // modified selection. The active descendant event is + // send after that so that the receiving AT has time to + // read the text or name of the active child. +// NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + + if ( getCtrl() && getCtrl()->HasFocus() ) + { + SvxIconChoiceCtrlEntry* pEntry = static_cast< SvxIconChoiceCtrlEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + sal_Int32 nPos = getCtrl()->GetEntryListPos( pEntry ); + Reference< XAccessible > xChild = new AccessibleIconChoiceCtrlEntry( *getCtrl(), nPos, this ); + uno::Any aOldValue, aNewValue; + aNewValue <<= xChild; + NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue ); + + NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, aOldValue, aNewValue ); + + } + } + break; + } + case VclEventId::WindowGetFocus : + { + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + if ( pCtrl && pCtrl->HasFocus() ) + { + SvxIconChoiceCtrlEntry* pEntry = static_cast< SvxIconChoiceCtrlEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry == nullptr ) + { + pEntry = getCtrl()->GetSelectedEntry(); + } + if ( pEntry ) + { + sal_Int32 nPos = pCtrl->GetEntryListPos( pEntry ); + Reference< XAccessible > xChild = new AccessibleIconChoiceCtrlEntry( *pCtrl, nPos, this ); + uno::Any aOldValue, aNewValue; + aNewValue <<= xChild; + NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue ); + NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, aOldValue, aNewValue ); + } + } + break; + } + default: + VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent); + } + } + + // XComponent + + void SAL_CALL AccessibleIconChoiceCtrl::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + m_xParent = nullptr; + } + + // XServiceInfo + + OUString SAL_CALL AccessibleIconChoiceCtrl::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleIconChoiceControl"; + } + + Sequence< OUString > SAL_CALL AccessibleIconChoiceCtrl::getSupportedServiceNames() + { + return {"com.sun.star.accessibility.AccessibleContext", + "com.sun.star.accessibility.AccessibleComponent", + "com.sun.star.awt.AccessibleIconChoiceControl"}; + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrl::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + // XAccessible + + Reference< XAccessibleContext > SAL_CALL AccessibleIconChoiceCtrl::getAccessibleContext( ) + { + ensureAlive(); + return this; + } + + // XAccessibleContext + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrl::getAccessibleChildCount( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + return getCtrl()->GetEntryCount(); + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrl::getAccessibleChild( sal_Int32 i ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry(i); + if ( !pEntry ) + throw RuntimeException("getAccessibleChild: Entry " + + OUString::number(i) + " not found", + static_cast<css::lang::XTypeProvider*>( + static_cast<VCLXAccessibleComponent_BASE*>(this))); + + return new AccessibleIconChoiceCtrlEntry( *pCtrl, i, this ); + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrl::getAccessibleParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + ensureAlive(); + return m_xParent; + } + + sal_Int16 SAL_CALL AccessibleIconChoiceCtrl::getAccessibleRole( ) + { + //return AccessibleRole::TREE; + return AccessibleRole::LIST; + } + + OUString SAL_CALL AccessibleIconChoiceCtrl::getAccessibleDescription( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + return getCtrl()->GetAccessibleDescription(); + } + + OUString SAL_CALL AccessibleIconChoiceCtrl::getAccessibleName( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + OUString sName = getCtrl()->GetAccessibleName(); + if ( sName.isEmpty() ) + sName = "IconChoiceControl"; + return sName; + } + + // XAccessibleSelection + + void SAL_CALL AccessibleIconChoiceCtrl::selectAccessibleChild( sal_Int32 nChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( nChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + pCtrl->SetCursor( pEntry ); + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrl::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( nChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + return ( pCtrl->GetCursor() == pEntry ); + } + + void SAL_CALL AccessibleIconChoiceCtrl::clearAccessibleSelection( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + getCtrl()->SetNoSelection(); + } + + void SAL_CALL AccessibleIconChoiceCtrl::selectAllAccessibleChildren( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + sal_Int32 nCount = pCtrl->GetEntryCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( i ); + if ( pCtrl->GetCursor() != pEntry ) + pCtrl->SetCursor( pEntry ); + } + } + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrl::getSelectedAccessibleChildCount( ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + sal_Int32 nSelCount = 0; + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + sal_Int32 nCount = pCtrl->GetEntryCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( i ); + if ( pCtrl->GetCursor() == pEntry ) + ++nSelCount; + } + + return nSelCount; + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrl::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild; + sal_Int32 nSelCount = 0; + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + sal_Int32 nCount = pCtrl->GetEntryCount(); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( i ); + if ( pCtrl->GetCursor() == pEntry ) + ++nSelCount; + + if ( nSelCount == ( nSelectedChildIndex + 1 ) ) + { + xChild = new AccessibleIconChoiceCtrlEntry( *pCtrl, i, this ); + break; + } + } + + return xChild; + } + + void SAL_CALL AccessibleIconChoiceCtrl::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::comphelper::OExternalLockGuard aGuard( this ); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + sal_Int32 nSelCount = 0; + VclPtr<SvtIconChoiceCtrl> pCtrl = getCtrl(); + sal_Int32 nCount = pCtrl->GetEntryCount(); + bool bFound = false; + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvxIconChoiceCtrlEntry* pEntry = pCtrl->GetEntry( i ); + if ( pEntry->IsSelected() ) + { + ++nSelCount; + if ( i == nSelectedChildIndex ) + bFound = true; + } + } + + // if only one entry is selected and its index is chosen to deselect -> no selection anymore + if ( nSelCount == 1 && bFound ) + pCtrl->SetNoSelection(); + } + + void AccessibleIconChoiceCtrl::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) + { + VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet ); + if ( isAlive() ) + { + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS ); + rStateSet.AddState( AccessibleStateType::SELECTABLE ); + } + } + + VclPtr< SvtIconChoiceCtrl > AccessibleIconChoiceCtrl::getCtrl() const + { + return GetAs<SvtIconChoiceCtrl >(); + } + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibleiconchoicectrlentry.cxx b/accessibility/source/extended/accessibleiconchoicectrlentry.cxx new file mode 100644 index 000000000..5a995a94f --- /dev/null +++ b/accessibility/source/extended/accessibleiconchoicectrlentry.cxx @@ -0,0 +1,680 @@ +/* -*- 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/accessibleiconchoicectrlentry.hxx> +#include <vcl/toolkit/ivctrl.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/helper/convert.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <svtools/stringtransfer.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <i18nlangtag/languagetag.hxx> + +#define ACCESSIBLE_ACTION_COUNT 1 + +namespace +{ + /// @throws css::lang::IndexOutOfBoundsException + void checkActionIndex_Impl( sal_Int32 _nIndex ) + { + if ( _nIndex < 0 || _nIndex >= ACCESSIBLE_ACTION_COUNT ) + // only three actions + throw css::lang::IndexOutOfBoundsException(); + } +} + + +namespace accessibility +{ + + + 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() + + AccessibleIconChoiceCtrlEntry::AccessibleIconChoiceCtrlEntry( SvtIconChoiceCtrl& _rIconCtrl, + sal_Int32 _nPos, + const Reference< XAccessible >& _xParent ) : + + AccessibleIconChoiceCtrlEntry_BASE ( m_aMutex ), + + m_pIconCtrl ( &_rIconCtrl ), + m_nIndex ( _nPos ), + m_nClientId ( 0 ), + m_xParent ( _xParent ) + + { + osl_atomic_increment( &m_refCount ); + { + Reference< XComponent > xComp( m_xParent, UNO_QUERY ); + if ( xComp.is() ) + xComp->addEventListener( this ); + } + osl_atomic_decrement( &m_refCount ); + } + + void AccessibleIconChoiceCtrlEntry::disposing( const css::lang::EventObject& _rSource ) + { + if ( _rSource.Source == m_xParent ) + { + dispose(); + OSL_ENSURE( !m_xParent.is() && ( m_pIconCtrl == nullptr ), "" ); + } + } + + AccessibleIconChoiceCtrlEntry::~AccessibleIconChoiceCtrlEntry() + { + if ( IsAlive_Impl() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + + tools::Rectangle AccessibleIconChoiceCtrlEntry::GetBoundingBox_Impl() const + { + tools::Rectangle aRect; + SvxIconChoiceCtrlEntry* pEntry = m_pIconCtrl->GetEntry( m_nIndex ); + if ( pEntry ) + aRect = m_pIconCtrl->GetBoundingBox( pEntry ); + + return aRect; + } + + tools::Rectangle AccessibleIconChoiceCtrlEntry::GetBoundingBoxOnScreen_Impl() const + { + tools::Rectangle aRect; + SvxIconChoiceCtrlEntry* pEntry = m_pIconCtrl->GetEntry( m_nIndex ); + if ( pEntry ) + { + aRect = m_pIconCtrl->GetBoundingBox( pEntry ); + Point aTopLeft = aRect.TopLeft(); + aTopLeft += m_pIconCtrl->GetWindowExtentsRelative( nullptr ).TopLeft(); + aRect = tools::Rectangle( aTopLeft, aRect.GetSize() ); + } + + return aRect; + } + + bool AccessibleIconChoiceCtrlEntry::IsAlive_Impl() const + { + return ( !rBHelper.bDisposed && !rBHelper.bInDispose && m_pIconCtrl ); + } + + bool AccessibleIconChoiceCtrlEntry::IsShowing_Impl() const + { + bool bShowing = false; + Reference< XAccessibleContext > xParentContext = + m_xParent.is() ? m_xParent->getAccessibleContext() : Reference< XAccessibleContext >(); + if( xParentContext.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParentContext, uno::UNO_QUERY ); + if( xParentComp.is() ) + bShowing = GetBoundingBox_Impl().Overlaps( VCLRectangle( xParentComp->getBounds() ) ); + } + + return bShowing; + } + + tools::Rectangle AccessibleIconChoiceCtrlEntry::GetBoundingBox() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return GetBoundingBox_Impl(); + } + + tools::Rectangle AccessibleIconChoiceCtrlEntry::GetBoundingBoxOnScreen() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return GetBoundingBoxOnScreen_Impl(); + } + + void AccessibleIconChoiceCtrlEntry::EnsureIsAlive() const + { + if ( !IsAlive_Impl() ) + throw lang::DisposedException(); + } + + OUString AccessibleIconChoiceCtrlEntry::implGetText() + { + OUString sRet; + SvxIconChoiceCtrlEntry* pEntry = m_pIconCtrl->GetEntry( m_nIndex ); + if ( pEntry ) + sRet = pEntry->GetDisplayText(); + return sRet; + } + + Locale AccessibleIconChoiceCtrlEntry::implGetLocale() + { + return Application::GetSettings().GetUILanguageTag().getLocale(); + } + void AccessibleIconChoiceCtrlEntry::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex ) + { + nStartIndex = 0; + nEndIndex = 0; + } + + // XTypeProvider + + + Sequence< sal_Int8 > AccessibleIconChoiceCtrlEntry::getImplementationId() + { + return css::uno::Sequence<sal_Int8>(); + } + + // XComponent + + void SAL_CALL AccessibleIconChoiceCtrlEntry::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // Send a disposing to all listeners. + if ( m_nClientId ) + { + sal_uInt32 nId = m_nClientId; + m_nClientId = 0; + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nId, *this ); + } + + Reference< XComponent > xComp( m_xParent, UNO_QUERY ); + if ( xComp.is() ) + xComp->removeEventListener( this ); + + m_pIconCtrl = nullptr; + m_xParent = nullptr; + } + + // XServiceInfo + + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleIconChoiceControlEntry"; + } + + Sequence< OUString > SAL_CALL AccessibleIconChoiceCtrlEntry::getSupportedServiceNames() + { + return {"com.sun.star.accessibility.AccessibleContext", + "com.sun.star.accessibility.AccessibleComponent", + "com.sun.star.awt.AccessibleIconChoiceControlEntry"}; + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + // XAccessible + + Reference< XAccessibleContext > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleContext( ) + { + EnsureIsAlive(); + return this; + } + + // XAccessibleContext + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleChildCount( ) + { + return 0; // no children + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleChild( sal_Int32 ) + { + throw IndexOutOfBoundsException(); + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return m_xParent; + } + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleIndexInParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return m_nIndex; + } + + sal_Int16 SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleRole( ) + { + //return AccessibleRole::LABEL; + return AccessibleRole::LIST_ITEM; + } + + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleDescription( ) + { + // no description for every item + return OUString(); + } + + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleName( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return implGetText(); + } + + Reference< XAccessibleRelationSet > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleRelationSet( ) + { + return new utl::AccessibleRelationSetHelper; + } + + Reference< XAccessibleStateSet > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleStateSet( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( IsAlive_Impl() ) + { + pStateSetHelper->AddState( AccessibleStateType::TRANSIENT ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + pStateSetHelper->AddState( AccessibleStateType::SENSITIVE ); + if ( IsShowing_Impl() ) + { + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + pStateSetHelper->AddState( AccessibleStateType::VISIBLE ); + } + + if ( m_pIconCtrl && m_pIconCtrl->GetCursor() == m_pIconCtrl->GetEntry( m_nIndex ) ) + pStateSetHelper->AddState( AccessibleStateType::SELECTED ); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; + } + + Locale SAL_CALL AccessibleIconChoiceCtrlEntry::getLocale( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + return implGetLocale(); + } + + // XAccessibleComponent + + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::containsPoint( const awt::Point& rPoint ) + { + return tools::Rectangle( Point(), GetBoundingBox().GetSize() ).Contains( VCLPoint( rPoint ) ); + } + + Reference< XAccessible > SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleAtPoint( const awt::Point& ) + { + return Reference< XAccessible >(); + } + + awt::Rectangle SAL_CALL AccessibleIconChoiceCtrlEntry::getBounds( ) + { + return AWTRectangle( GetBoundingBox() ); + } + + awt::Point SAL_CALL AccessibleIconChoiceCtrlEntry::getLocation( ) + { + return AWTPoint( GetBoundingBox().TopLeft() ); + } + + awt::Point SAL_CALL AccessibleIconChoiceCtrlEntry::getLocationOnScreen( ) + { + return AWTPoint( GetBoundingBoxOnScreen().TopLeft() ); + } + + awt::Size SAL_CALL AccessibleIconChoiceCtrlEntry::getSize( ) + { + return AWTSize( GetBoundingBox().GetSize() ); + } + + void SAL_CALL AccessibleIconChoiceCtrlEntry::grabFocus( ) + { + // do nothing, because no focus for each item + } + + sal_Int32 AccessibleIconChoiceCtrlEntry::getForeground( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getForeground(); + } + + return nColor; + } + + sal_Int32 AccessibleIconChoiceCtrlEntry::getBackground( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getBackground(); + } + + return nColor; + } + + // XAccessibleText + + + awt::Rectangle SAL_CALL AccessibleIconChoiceCtrlEntry::getCharacterBounds( sal_Int32 _nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( ( 0 > _nIndex ) || ( implGetText().getLength() <= _nIndex ) ) + throw IndexOutOfBoundsException(); + + awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( m_pIconCtrl ) + { + tools::Rectangle aItemRect = GetBoundingBox_Impl(); + tools::Rectangle aCharRect = m_pIconCtrl->GetEntryCharacterBounds( m_nIndex, _nIndex ); + aCharRect.Move( -aItemRect.Left(), -aItemRect.Top() ); + aBounds = AWTRectangle( aCharRect ); + } + + return aBounds; + } + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getIndexAtPoint( const awt::Point& aPoint ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nIndex = -1; + if ( m_pIconCtrl ) + { + vcl::ControlLayoutData aLayoutData; + tools::Rectangle aItemRect = GetBoundingBox_Impl(); + m_pIconCtrl->RecordLayoutData( &aLayoutData, aItemRect ); + Point aPnt( VCLPoint( aPoint ) ); + aPnt += aItemRect.TopLeft(); + nIndex = aLayoutData.GetIndexForPoint( aPnt ); + + tools::Long nLen = aLayoutData.m_aUnicodeBoundRects.size(); + for ( tools::Long i = 0; i < nLen; ++i ) + { + tools::Rectangle aRect = aLayoutData.GetCharacterBounds(i); + bool bInside = aRect.Contains( aPnt ); + + if ( bInside ) + break; + } + } + + return nIndex; + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + OUString sText = implGetText(); + if ( ( 0 > nStartIndex ) || ( sText.getLength() <= nStartIndex ) + || ( 0 > nEndIndex ) || ( sText.getLength() <= nEndIndex ) ) + throw IndexOutOfBoundsException(); + + sal_Int32 nLen = nEndIndex - nStartIndex + 1; + ::svt::OStringTransfer::CopyString( sText.copy( nStartIndex, nLen ), m_pIconCtrl ); + + return true; + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + + // XAccessibleEventBroadcaster + + void SAL_CALL AccessibleIconChoiceCtrlEntry::addAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener ) + { + if (xListener.is()) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_nClientId) + m_nClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener ); + } + } + + void SAL_CALL AccessibleIconChoiceCtrlEntry::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener ) + { + if (!xListener.is()) + return; + + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + sal_Int32 nId = m_nClientId; + m_nClientId = 0; + comphelper::AccessibleEventNotifier::revokeClient( nId ); + } + } + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getCaretPosition( ) + { + return -1; + } + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::setCaretPosition ( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + if ( !implIsValidRange( nIndex, nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + sal_Unicode SAL_CALL AccessibleIconChoiceCtrlEntry::getCharacter( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex ); + } + css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleIconChoiceCtrlEntry::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + OUString sText( implGetText() ); + + if ( !implIsValidIndex( nIndex, sText.getLength() ) ) + throw IndexOutOfBoundsException(); + + return css::uno::Sequence< css::beans::PropertyValue >(); + } + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getCharacterCount( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return implGetText().getLength(); + } + + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getSelectedText( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OUString(); + } + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getSelectionStart( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return 0; + } + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getSelectionEnd( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return 0; + } + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + if ( !implIsValidRange( nStartIndex, nEndIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getText( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return implGetText( ); + } + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::implGetTextRange( implGetText(), nStartIndex, nEndIndex ); + } + css::accessibility::TextSegment SAL_CALL AccessibleIconChoiceCtrlEntry::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::getTextAtIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleIconChoiceCtrlEntry::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::getTextBeforeIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleIconChoiceCtrlEntry::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + return OCommonAccessibleText::getTextBehindIndex( nIndex ,aTextType); + } + + + // XAccessibleAction + + sal_Int32 SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleActionCount( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // three actions supported + return ACCESSIBLE_ACTION_COUNT; + } + + sal_Bool SAL_CALL AccessibleIconChoiceCtrlEntry::doAccessibleAction( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bRet = false; + checkActionIndex_Impl( nIndex ); + EnsureIsAlive(); + + SvxIconChoiceCtrlEntry* pEntry = m_pIconCtrl->GetEntry( m_nIndex ); + if ( pEntry && !pEntry->IsSelected() ) + { + m_pIconCtrl->SetNoSelection(); + m_pIconCtrl->SetCursor( pEntry ); + bRet = true; + } + + return bRet; + } + + OUString SAL_CALL AccessibleIconChoiceCtrlEntry::getAccessibleActionDescription( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + checkActionIndex_Impl( nIndex ); + EnsureIsAlive(); + + return "Select"; + } + + Reference< XAccessibleKeyBinding > AccessibleIconChoiceCtrlEntry::getAccessibleActionKeyBinding( sal_Int32 nIndex ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XAccessibleKeyBinding > xRet; + checkActionIndex_Impl( nIndex ); + // ... which key? + return xRet; + } + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/accessibility/source/extended/accessiblelistboxentry.cxx b/accessibility/source/extended/accessiblelistboxentry.cxx new file mode 100644 index 000000000..2fff77b03 --- /dev/null +++ b/accessibility/source/extended/accessiblelistboxentry.cxx @@ -0,0 +1,1194 @@ +/* -*- 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/accessiblelistboxentry.hxx> +#include <extended/accessiblelistbox.hxx> +#include <vcl/toolkit/treelistbox.hxx> +#include <svtools/stringtransfer.hxx> +#include <vcl/toolkit/svlbitm.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <i18nlangtag/languagetag.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/helper/convert.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <helper/accresmgr.hxx> +#include <strings.hrc> + +#define ACCESSIBLE_ACTION_COUNT 1 + +namespace +{ + /// @throws css::lang::IndexOutOfBoundsException + void checkActionIndex_Impl( sal_Int32 _nIndex ) + { + if ( _nIndex < 0 || _nIndex >= ACCESSIBLE_ACTION_COUNT ) + // only three actions + throw css::lang::IndexOutOfBoundsException(); + } +} + + +namespace accessibility +{ + // class AccessibleListBoxEntry ----------------------------------------------------- + + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + using namespace ::comphelper; + + + // Ctor() and Dtor() + + AccessibleListBoxEntry::AccessibleListBoxEntry( SvTreeListBox& _rListBox, + SvTreeListEntry& rEntry, + AccessibleListBox & rListBox) + : AccessibleListBoxEntry_BASE( m_aMutex ) + + , m_pTreeListBox( &_rListBox ) + , m_pSvLBoxEntry(&rEntry) + , m_nClientId( 0 ) + , m_wListBox(&rListBox) + , m_rListBox(rListBox) + { + m_pTreeListBox->AddEventListener( LINK( this, AccessibleListBoxEntry, WindowEventListener ) ); + _rListBox.FillEntryPath( m_pSvLBoxEntry, m_aEntryPath ); + } + + AccessibleListBoxEntry::~AccessibleListBoxEntry() + { + if ( IsAlive_Impl() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + + IMPL_LINK( AccessibleListBoxEntry, WindowEventListener, VclWindowEvent&, rEvent, void ) + { + OSL_ENSURE( rEvent.GetWindow() , "AccessibleListBoxEntry::WindowEventListener: no event window!" ); + OSL_ENSURE( rEvent.GetWindow() == m_pTreeListBox, "AccessibleListBoxEntry::WindowEventListener: where did this come from?" ); + + if ( m_pTreeListBox == nullptr ) + return; + + switch ( rEvent.GetId() ) + { + case VclEventId::ObjectDying : + { + if ( m_pTreeListBox ) + m_pTreeListBox->RemoveEventListener( LINK( this, AccessibleListBoxEntry, WindowEventListener ) ); + m_pTreeListBox = nullptr; + dispose(); + break; + } + default: break; + } + } + + void AccessibleListBoxEntry::NotifyAccessibleEvent( sal_Int16 _nEventId, + const css::uno::Any& _aOldValue, + const css::uno::Any& _aNewValue ) + { + Reference< uno::XInterface > xSource( *this ); + AccessibleEventObject aEventObj( xSource, _nEventId, _aNewValue, _aOldValue ); + + if (m_nClientId) + comphelper::AccessibleEventNotifier::addEvent( m_nClientId, aEventObj ); + } + + + tools::Rectangle AccessibleListBoxEntry::GetBoundingBox_Impl() const + { + tools::Rectangle aRect; + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + aRect = m_pTreeListBox->GetBoundingRect( pEntry ); + SvTreeListEntry* pParent = m_pTreeListBox->GetParent( pEntry ); + if ( pParent ) + { + // position relative to parent entry + Point aTopLeft = aRect.TopLeft(); + aTopLeft -= m_pTreeListBox->GetBoundingRect( pParent ).TopLeft(); + aRect = tools::Rectangle( aTopLeft, aRect.GetSize() ); + } + } + + return aRect; + } + + tools::Rectangle AccessibleListBoxEntry::GetBoundingBoxOnScreen_Impl() const + { + tools::Rectangle aRect; + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + aRect = m_pTreeListBox->GetBoundingRect( pEntry ); + Point aTopLeft = aRect.TopLeft(); + aTopLeft += m_pTreeListBox->GetWindowExtentsRelative( nullptr ).TopLeft(); + aRect = tools::Rectangle( aTopLeft, aRect.GetSize() ); + } + + return aRect; + } + + bool AccessibleListBoxEntry::IsAlive_Impl() const + { + return !rBHelper.bDisposed && !rBHelper.bInDispose && (m_pTreeListBox != nullptr); + } + + bool AccessibleListBoxEntry::IsShowing_Impl() const + { + Reference< XAccessible > xParent = implGetParentAccessible( ); + + bool bShowing = false; + Reference< XAccessibleContext > xParentContext = + xParent.is() ? xParent->getAccessibleContext() : Reference< XAccessibleContext >(); + if( xParentContext.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParentContext, uno::UNO_QUERY ); + if( xParentComp.is() ) + bShowing = GetBoundingBox_Impl().Overlaps( VCLRectangle( xParentComp->getBounds() ) ); + } + + return bShowing; + } + + tools::Rectangle AccessibleListBoxEntry::GetBoundingBox() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return GetBoundingBox_Impl(); + } + + tools::Rectangle AccessibleListBoxEntry::GetBoundingBoxOnScreen() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + return GetBoundingBoxOnScreen_Impl(); + } + + void AccessibleListBoxEntry::EnsureIsAlive() const + { + if ( !IsAlive_Impl() ) + throw lang::DisposedException(); + } + + OUString AccessibleListBoxEntry::implGetText() + { + OUString sRet; + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + sRet = SvTreeListBox::SearchEntryTextWithHeadTitle( pEntry ); + return sRet; + } + + Locale AccessibleListBoxEntry::implGetLocale() + { + return Application::GetSettings().GetUILanguageTag().getLocale(); + } + void AccessibleListBoxEntry::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex ) + { + nStartIndex = 0; + nEndIndex = 0; + } + + // XTypeProvider + + + Sequence< sal_Int8 > AccessibleListBoxEntry::getImplementationId() + { + return css::uno::Sequence<sal_Int8>(); + } + + + // XComponent + + void SAL_CALL AccessibleListBoxEntry::disposing() + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XAccessible > xKeepAlive( this ); + + // Send a disposing to all listeners. + if ( m_nClientId ) + { + ::comphelper::AccessibleEventNotifier::TClientId nId = m_nClientId; + m_nClientId = 0; + ::comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nId, *this ); + } + + // clean up + m_wListBox.clear(); + + if ( m_pTreeListBox ) + m_pTreeListBox->RemoveEventListener( LINK( this, AccessibleListBoxEntry, WindowEventListener ) ); + m_pTreeListBox = nullptr; + } + + // XServiceInfo + + OUString SAL_CALL AccessibleListBoxEntry::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTreeListBoxEntry"; + } + + Sequence< OUString > SAL_CALL AccessibleListBoxEntry::getSupportedServiceNames() + { + return {"com.sun.star.accessibility.AccessibleContext", + "com.sun.star.accessibility.AccessibleComponent", + "com.sun.star.awt.AccessibleTreeListBoxEntry"}; + } + + sal_Bool SAL_CALL AccessibleListBoxEntry::supportsService( const OUString& _rServiceName ) + { + return cppu::supportsService(this, _rServiceName); + } + + // XAccessible + + Reference< XAccessibleContext > SAL_CALL AccessibleListBoxEntry::getAccessibleContext( ) + { + EnsureIsAlive(); + return this; + } + + // XAccessibleContext + + sal_Int32 SAL_CALL AccessibleListBoxEntry::getAccessibleChildCount( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + sal_Int32 nCount = 0; + if ( pEntry ) + nCount = m_pTreeListBox->GetLevelChildCount( pEntry ); + + return nCount; + } + + Reference< XAccessible > SAL_CALL AccessibleListBoxEntry::getAccessibleChild( sal_Int32 i ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + SvTreeListEntry* pEntry = GetRealChild(i); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + uno::Reference<XAccessible> xListBox(m_wListBox); + assert(xListBox.is()); + + return m_rListBox.implGetAccessible(*pEntry); + } + + Reference< XAccessible > AccessibleListBoxEntry::implGetParentAccessible( ) const + { + Reference< XAccessible > xParent; + if ( !xParent.is() ) + { + OSL_ENSURE( m_aEntryPath.size(), "AccessibleListBoxEntry::getAccessibleParent: invalid path!" ); + if ( m_aEntryPath.size() == 1 ) + { // we're a top level entry + // -> our parent is the tree listbox itself + if ( m_pTreeListBox ) + xParent = m_pTreeListBox->GetAccessible( ); + } + else + { // we have an entry as parent -> get its accessible + + // shorten our access path by one + std::deque< sal_Int32 > aParentPath( m_aEntryPath ); + aParentPath.pop_back(); + + // get the entry for this shortened access path + SvTreeListEntry* pParentEntry = m_pTreeListBox->GetEntryFromPath( aParentPath ); + OSL_ENSURE( pParentEntry, "AccessibleListBoxEntry::implGetParentAccessible: could not obtain a parent entry!" ); + + if ( pParentEntry ) + pParentEntry = m_pTreeListBox->GetParent(pParentEntry); + if ( pParentEntry ) + { + uno::Reference<XAccessible> xListBox(m_wListBox); + assert(xListBox.is()); + return m_rListBox.implGetAccessible(*pParentEntry); + // the AccessibleListBoxEntry class will create its parent + // when needed + } + } + } + + return xParent; + } + + + Reference< XAccessible > SAL_CALL AccessibleListBoxEntry::getAccessibleParent( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + return implGetParentAccessible( ); + } + + sal_Int32 SAL_CALL AccessibleListBoxEntry::getAccessibleIndexInParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + OSL_ENSURE( !m_aEntryPath.empty(), "empty path" ); + return m_aEntryPath.empty() ? -1 : m_aEntryPath.back(); + } + + sal_Int32 AccessibleListBoxEntry::GetRoleType() const + { + sal_Int32 nCase = 0; + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry(0); + if ( pEntry ) + { + if( pEntry->HasChildrenOnDemand() || m_pTreeListBox->GetChildCount(pEntry) > 0 ) + { + nCase = 1; + return nCase; + } + } + + bool bHasButtons = (m_pTreeListBox->GetStyle() & WB_HASBUTTONS)!=0; + if( !(m_pTreeListBox->GetTreeFlags() & SvTreeFlags::CHKBTN) ) + { + if( bHasButtons ) + nCase = 1; + } + else + { + if( bHasButtons ) + nCase = 2; + else + nCase = 3; + } + return nCase; + } + + sal_Int16 SAL_CALL AccessibleListBoxEntry::getAccessibleRole( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + SvTreeListBox* pBox = m_pTreeListBox; + if(!pBox) + return AccessibleRole::UNKNOWN; + + SvTreeFlags treeFlag = pBox->GetTreeFlags(); + if(treeFlag & SvTreeFlags::CHKBTN ) + { + SvTreeListEntry* pEntry = pBox->GetEntryFromPath( m_aEntryPath ); + SvButtonState eState = pBox->GetCheckButtonState( pEntry ); + switch( eState ) + { + case SvButtonState::Checked: + case SvButtonState::Unchecked: + return AccessibleRole::CHECK_BOX; + case SvButtonState::Tristate: + default: + return AccessibleRole::LABEL; + } + } + if (GetRoleType() == 0) + return AccessibleRole::LIST_ITEM; + else + //o is: return AccessibleRole::LABEL; + return AccessibleRole::TREE_ITEM; + } + + OUString SAL_CALL AccessibleListBoxEntry::getAccessibleDescription( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if( getAccessibleRole() == AccessibleRole::TREE_ITEM ) + { + return OUString(); + } + return m_pTreeListBox->GetEntryAccessibleDescription( + m_pTreeListBox->GetEntryFromPath(m_aEntryPath)); + } + + OUString SAL_CALL AccessibleListBoxEntry::getAccessibleName( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + return implGetText(); + } + + Reference< XAccessibleRelationSet > SAL_CALL AccessibleListBoxEntry::getAccessibleRelationSet( ) + { + Reference< XAccessibleRelationSet > xRelSet; + Reference< XAccessible > xParent; + if ( m_aEntryPath.size() > 1 ) // not a root entry + xParent = implGetParentAccessible(); + if ( xParent.is() ) + { + rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper; + Sequence< Reference< XInterface > > aSequence { xParent }; + pRelationSetHelper->AddRelation( + AccessibleRelation( AccessibleRelationType::NODE_CHILD_OF, aSequence ) ); + xRelSet = pRelationSetHelper; + } + return xRelSet; + } + + Reference< XAccessibleStateSet > SAL_CALL AccessibleListBoxEntry::getAccessibleStateSet( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( IsAlive_Impl() ) + { + switch(getAccessibleRole()) + { + case AccessibleRole::LABEL: + pStateSetHelper->AddState( AccessibleStateType::TRANSIENT ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + if (m_pTreeListBox->IsInplaceEditingEnabled()) + pStateSetHelper->AddState( AccessibleStateType::EDITABLE ); + if (IsShowing_Impl()) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + break; + case AccessibleRole::CHECK_BOX: + pStateSetHelper->AddState( AccessibleStateType::TRANSIENT ); + pStateSetHelper->AddState( AccessibleStateType::SELECTABLE ); + pStateSetHelper->AddState( AccessibleStateType::ENABLED ); + if (IsShowing_Impl()) + pStateSetHelper->AddState( AccessibleStateType::SHOWING ); + break; + } + SvTreeListEntry *pEntry = m_pTreeListBox->GetEntryFromPath(m_aEntryPath); + if (pEntry) + m_pTreeListBox->FillAccessibleEntryStateSet(pEntry, *pStateSetHelper); + } + else + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + + return pStateSetHelper; + } + + Locale SAL_CALL AccessibleListBoxEntry::getLocale( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + return implGetLocale(); + } + + // XAccessibleComponent + + sal_Bool SAL_CALL AccessibleListBoxEntry::containsPoint( const awt::Point& rPoint ) + { + return tools::Rectangle( Point(), GetBoundingBox().GetSize() ).Contains( VCLPoint( rPoint ) ); + } + + Reference< XAccessible > SAL_CALL AccessibleListBoxEntry::getAccessibleAtPoint( const awt::Point& _aPoint ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( VCLPoint( _aPoint ) ); + if ( !pEntry ) + throw RuntimeException("AccessibleListBoxEntry::getAccessibleAtPoint - pEntry cannot be empty!"); + + Reference< XAccessible > xAcc; + uno::Reference<XAccessible> xListBox(m_wListBox); + assert(xListBox.is()); + auto pAccEntry = m_rListBox.implGetAccessible(*pEntry); + tools::Rectangle aRect = pAccEntry->GetBoundingBox_Impl(); + if ( aRect.Contains( VCLPoint( _aPoint ) ) ) + xAcc = pAccEntry.get(); + return xAcc; + } + + awt::Rectangle SAL_CALL AccessibleListBoxEntry::getBounds( ) + { + return AWTRectangle( GetBoundingBox() ); + } + + awt::Point SAL_CALL AccessibleListBoxEntry::getLocation( ) + { + return AWTPoint( GetBoundingBox().TopLeft() ); + } + + awt::Point SAL_CALL AccessibleListBoxEntry::getLocationOnScreen( ) + { + return AWTPoint( GetBoundingBoxOnScreen().TopLeft() ); + } + + awt::Size SAL_CALL AccessibleListBoxEntry::getSize( ) + { + return AWTSize( GetBoundingBox().GetSize() ); + } + + void SAL_CALL AccessibleListBoxEntry::grabFocus( ) + { + // do nothing, because no focus for each item + } + + sal_Int32 AccessibleListBoxEntry::getForeground( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getForeground(); + } + + return nColor; + } + + sal_Int32 AccessibleListBoxEntry::getBackground( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getBackground(); + } + + return nColor; + } + + // XAccessibleText + + + awt::Rectangle SAL_CALL AccessibleListBoxEntry::getCharacterBounds( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + if ( !implIsValidIndex( nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + awt::Rectangle aBounds( 0, 0, 0, 0 ); + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + vcl::ControlLayoutData aLayoutData; + tools::Rectangle aItemRect = GetBoundingBox(); + m_pTreeListBox->RecordLayoutData( &aLayoutData, aItemRect ); + tools::Rectangle aCharRect = aLayoutData.GetCharacterBounds( nIndex ); + aCharRect.Move( -aItemRect.Left(), -aItemRect.Top() ); + aBounds = AWTRectangle( aCharRect ); + } + + return aBounds; + } + + sal_Int32 SAL_CALL AccessibleListBoxEntry::getIndexAtPoint( const awt::Point& aPoint ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + if(aPoint.X==0 && aPoint.Y==0) return 0; + + sal_Int32 nIndex = -1; + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + vcl::ControlLayoutData aLayoutData; + tools::Rectangle aItemRect = GetBoundingBox(); + m_pTreeListBox->RecordLayoutData( &aLayoutData, aItemRect ); + Point aPnt( VCLPoint( aPoint ) ); + aPnt += aItemRect.TopLeft(); + nIndex = aLayoutData.GetIndexForPoint( aPnt ); + } + + return nIndex; + } + + sal_Bool SAL_CALL AccessibleListBoxEntry::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + OUString sText = implGetText(); + if ( ( 0 > nStartIndex ) || ( sText.getLength() <= nStartIndex ) + || ( 0 > nEndIndex ) || ( sText.getLength() <= nEndIndex ) ) + throw IndexOutOfBoundsException(); + + sal_Int32 nLen = nEndIndex - nStartIndex + 1; + ::svt::OStringTransfer::CopyString( sText.copy( nStartIndex, nLen ), m_pTreeListBox ); + + return true; + } + + sal_Bool SAL_CALL AccessibleListBoxEntry::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + + // XAccessibleEventBroadcaster + + void SAL_CALL AccessibleListBoxEntry::addAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener ) + { + if (xListener.is()) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if (!m_nClientId) + m_nClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener ); + } + } + + void SAL_CALL AccessibleListBoxEntry::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener ) + { + if (!xListener.is()) + return; + + ::osl::MutexGuard aGuard( m_aMutex ); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + sal_Int32 nId = m_nClientId; + m_nClientId = 0; + comphelper::AccessibleEventNotifier::revokeClient( nId ); + + } + } + + // XAccessibleAction + + sal_Int32 SAL_CALL AccessibleListBoxEntry::getAccessibleActionCount( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // three actions supported + SvTreeFlags treeFlag = m_pTreeListBox->GetTreeFlags(); + bool bHasButtons = (m_pTreeListBox->GetStyle() & WB_HASBUTTONS)!=0; + if( (treeFlag & SvTreeFlags::CHKBTN) && !bHasButtons) + { + sal_Int16 role = getAccessibleRole(); + if ( role == AccessibleRole::CHECK_BOX ) + return 2; + else if ( role == AccessibleRole::LABEL ) + return 0; + } + else + return ACCESSIBLE_ACTION_COUNT; + return 0; + } + + sal_Bool SAL_CALL AccessibleListBoxEntry::doAccessibleAction( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + bool bRet = false; + checkActionIndex_Impl( nIndex ); + EnsureIsAlive(); + + SvTreeFlags treeFlag = m_pTreeListBox->GetTreeFlags(); + if( nIndex == 0 && (treeFlag & SvTreeFlags::CHKBTN) ) + { + if(getAccessibleRole() == AccessibleRole::CHECK_BOX) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + SvButtonState state = m_pTreeListBox->GetCheckButtonState( pEntry ); + if ( state == SvButtonState::Checked ) + m_pTreeListBox->SetCheckButtonState(pEntry, SvButtonState::Unchecked); + else if (state == SvButtonState::Unchecked) + m_pTreeListBox->SetCheckButtonState(pEntry, SvButtonState::Checked); + } + } + else if( (nIndex == 1 && (treeFlag & SvTreeFlags::CHKBTN) ) || (nIndex == 0) ) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + if ( m_pTreeListBox->IsExpanded( pEntry ) ) + m_pTreeListBox->Collapse( pEntry ); + else + m_pTreeListBox->Expand( pEntry ); + bRet = true; + } + } + + return bRet; + } + + OUString SAL_CALL AccessibleListBoxEntry::getAccessibleActionDescription( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + checkActionIndex_Impl( nIndex ); + EnsureIsAlive(); + + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + SvButtonState state = m_pTreeListBox->GetCheckButtonState( pEntry ); + SvTreeFlags treeFlag = m_pTreeListBox->GetTreeFlags(); + if(nIndex == 0 && (treeFlag & SvTreeFlags::CHKBTN)) + { + if(getAccessibleRole() == AccessibleRole::CHECK_BOX) + { + if ( state == SvButtonState::Checked ) + return "UnCheck"; + else if (state == SvButtonState::Unchecked) + return "Check"; + } + else + { + //Sometimes, a List or Tree may have both checkbox and label at the same time + return OUString(); + } + }else if( (nIndex == 1 && (treeFlag & SvTreeFlags::CHKBTN)) || nIndex == 0 ) + { + if( pEntry->HasChildren() || pEntry->HasChildrenOnDemand() ) + return m_pTreeListBox->IsExpanded( pEntry ) ? + AccResId(STR_SVT_ACC_ACTION_COLLAPSE) : + AccResId(STR_SVT_ACC_ACTION_EXPAND); + return OUString(); + + } + throw IndexOutOfBoundsException(); + } + + Reference< XAccessibleKeyBinding > AccessibleListBoxEntry::getAccessibleActionKeyBinding( sal_Int32 nIndex ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XAccessibleKeyBinding > xRet; + checkActionIndex_Impl( nIndex ); + // ... which key? + return xRet; + } + + // XAccessibleSelection + + void SAL_CALL AccessibleListBoxEntry::selectAccessibleChild( sal_Int32 nChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + SvTreeListEntry* pEntry = GetRealChild(nChildIndex); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + m_pTreeListBox->Select( pEntry ); + } + + sal_Bool SAL_CALL AccessibleListBoxEntry::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, nChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + return m_pTreeListBox->IsSelected( pEntry ); + } + + void SAL_CALL AccessibleListBoxEntry::clearAccessibleSelection( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( !pParent ) + throw RuntimeException("AccessibleListBoxEntry::clearAccessibleSelection - pParent cannot be empty!"); + sal_Int32 nCount = m_pTreeListBox->GetLevelChildCount( pParent ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, i ); + if ( m_pTreeListBox->IsSelected( pEntry ) ) + m_pTreeListBox->Select( pEntry, false ); + } + } + + void SAL_CALL AccessibleListBoxEntry::selectAllAccessibleChildren( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( !pParent ) + throw RuntimeException("AccessibleListBoxEntry::selectAllAccessibleChildren - pParent cannot be empty!"); + sal_Int32 nCount = m_pTreeListBox->GetLevelChildCount( pParent ); + for ( sal_Int32 i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, i ); + if ( !m_pTreeListBox->IsSelected( pEntry ) ) + m_pTreeListBox->Select( pEntry ); + } + } + + sal_Int32 SAL_CALL AccessibleListBoxEntry::getSelectedAccessibleChildCount( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + sal_Int32 i, nSelCount = 0, nCount = 0; + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( !pParent ) + throw RuntimeException("AccessibleListBoxEntry::getSelectedAccessibleChildCount - pParent cannot be empty!"); + nCount = m_pTreeListBox->GetLevelChildCount( pParent ); + for ( i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, i ); + if ( m_pTreeListBox->IsSelected( pEntry ) ) + ++nSelCount; + } + + return nSelCount; + } + + Reference< XAccessible > SAL_CALL AccessibleListBoxEntry::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild; + sal_Int32 i, nSelCount = 0, nCount = 0; + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if ( !pParent ) + throw RuntimeException("AccessibleListBoxEntry::getSelectedAccessibleChild - pParent cannot be empty!"); + nCount = m_pTreeListBox->GetLevelChildCount( pParent ); + for ( i = 0; i < nCount; ++i ) + { + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, i ); + if ( m_pTreeListBox->IsSelected( pEntry ) ) + ++nSelCount; + + if ( nSelCount == ( nSelectedChildIndex + 1 ) ) + { + uno::Reference<XAccessible> xListBox(m_wListBox); + assert(xListBox.is()); + xChild = m_rListBox.implGetAccessible(*pEntry).get(); + break; + } + } + + return xChild; + } + + void SAL_CALL AccessibleListBoxEntry::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + EnsureIsAlive(); + + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + SvTreeListEntry* pEntry = m_pTreeListBox->GetEntry( pParent, nSelectedChildIndex ); + if ( !pEntry ) + throw IndexOutOfBoundsException(); + + m_pTreeListBox->Select( pEntry, false ); + } + sal_Int32 SAL_CALL AccessibleListBoxEntry::getCaretPosition( ) + { + return -1; + } + sal_Bool SAL_CALL AccessibleListBoxEntry::setCaretPosition ( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + if ( !implIsValidRange( nIndex, nIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + sal_Unicode SAL_CALL AccessibleListBoxEntry::getCharacter( sal_Int32 nIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex ); + } + css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleListBoxEntry::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + OUString sText( implGetText() ); + + if ( !implIsValidIndex( nIndex, sText.getLength() ) ) + throw IndexOutOfBoundsException(); + + return css::uno::Sequence< css::beans::PropertyValue >(); + } + sal_Int32 SAL_CALL AccessibleListBoxEntry::getCharacterCount( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return implGetText().getLength(); + } + + OUString SAL_CALL AccessibleListBoxEntry::getSelectedText( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OUString(); + } + sal_Int32 SAL_CALL AccessibleListBoxEntry::getSelectionStart( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return 0; + } + sal_Int32 SAL_CALL AccessibleListBoxEntry::getSelectionEnd( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return 0; + } + sal_Bool SAL_CALL AccessibleListBoxEntry::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + if ( !implIsValidRange( nStartIndex, nEndIndex, implGetText().getLength() ) ) + throw IndexOutOfBoundsException(); + + return false; + } + OUString SAL_CALL AccessibleListBoxEntry::getText( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return implGetText( ); + } + OUString SAL_CALL AccessibleListBoxEntry::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::implGetTextRange( implGetText(), nStartIndex, nEndIndex ); + } + css::accessibility::TextSegment SAL_CALL AccessibleListBoxEntry::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::getTextAtIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleListBoxEntry::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + return OCommonAccessibleText::getTextBeforeIndex( nIndex ,aTextType); + } + css::accessibility::TextSegment SAL_CALL AccessibleListBoxEntry::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + EnsureIsAlive(); + + return OCommonAccessibleText::getTextBehindIndex( nIndex ,aTextType); + } + + // XAccessibleValue + + + Any AccessibleListBoxEntry::getCurrentValue( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Any aValue; + sal_Int32 level = static_cast<sal_Int32>(m_aEntryPath.size()) - 1; + level = level < 0 ? 0: level; + aValue <<= level; + return aValue; + } + + + sal_Bool AccessibleListBoxEntry::setCurrentValue( const Any& aNumber ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + + bool bReturn = false; + SvTreeListBox* pBox = m_pTreeListBox; + if(getAccessibleRole() == AccessibleRole::CHECK_BOX) + { + SvTreeListEntry* pEntry = pBox->GetEntryFromPath( m_aEntryPath ); + if ( pEntry ) + { + sal_Int32 nValue(0), nValueMin(0), nValueMax(0); + aNumber >>= nValue; + getMinimumValue() >>= nValueMin; + getMaximumValue() >>= nValueMax; + + if ( nValue < nValueMin ) + nValue = nValueMin; + else if ( nValue > nValueMax ) + nValue = nValueMax; + + pBox->SetCheckButtonState(pEntry, static_cast<SvButtonState>(nValue) ); + bReturn = true; + } + } + + return bReturn; + } + + + Any AccessibleListBoxEntry::getMaximumValue( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Any aValue; + // SvTreeListBox* pBox = m_pTreeListBox; + switch(getAccessibleRole()) + { + case AccessibleRole::CHECK_BOX: + aValue <<= sal_Int32(1); + break; + case AccessibleRole::LABEL: + default: + break; + } + + return aValue; + } + + + Any AccessibleListBoxEntry::getMinimumValue( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Any aValue; + // SvTreeListBox* pBox = m_pTreeListBox; + switch(getAccessibleRole()) + { + case AccessibleRole::CHECK_BOX: + aValue <<= sal_Int32(0); + break; + case AccessibleRole::LABEL: + default: + break; + } + + return aValue; + } + + Any AccessibleListBoxEntry::getMinimumIncrement( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + Any aValue; + switch(getAccessibleRole()) + { + case AccessibleRole::CHECK_BOX: + aValue <<= sal_Int32(1); + break; + case AccessibleRole::LABEL: + default: + break; + } + + return aValue; + } + + SvTreeListEntry* AccessibleListBoxEntry::GetRealChild(sal_Int32 nIndex) + { + SvTreeListEntry* pEntry = nullptr; + SvTreeListEntry* pParent = m_pTreeListBox->GetEntryFromPath( m_aEntryPath ); + if (pParent) + { + pEntry = m_pTreeListBox->GetEntry( pParent, nIndex ); + if ( !pEntry && getAccessibleChildCount() > 0 ) + { + m_pTreeListBox->RequestingChildren(pParent); + pEntry = m_pTreeListBox->GetEntry( pParent, nIndex ); + } + } + return pEntry; + } + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletabbar.cxx b/accessibility/source/extended/accessibletabbar.cxx new file mode 100644 index 000000000..8bf2d7add --- /dev/null +++ b/accessibility/source/extended/accessibletabbar.cxx @@ -0,0 +1,507 @@ +/* -*- 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/accessibletabbar.hxx> +#include <svtools/tabbar.hxx> +#include <extended/accessibletabbarpagelist.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/awt/XDevice.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <o3tl/safeint.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/awt/vclxfont.hxx> +#include <toolkit/helper/convert.hxx> + + +namespace accessibility +{ + + + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::accessibility; + using namespace ::comphelper; + + + + + AccessibleTabBar::AccessibleTabBar( TabBar* pTabBar ) + :AccessibleTabBarBase( pTabBar ) + { + if ( m_pTabBar ) + m_aAccessibleChildren.assign( m_pTabBar->GetAccessibleChildWindowCount() + 1, Reference< XAccessible >() ); + } + + + void AccessibleTabBar::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + Any aOldValue, aNewValue; + + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowEnabled: + { + aNewValue <<= AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + aNewValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowDisabled: + { + aOldValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + aOldValue <<= AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowGetFocus: + { + aNewValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowLoseFocus: + { + aOldValue <<= AccessibleStateType::FOCUSED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowShow: + { + aNewValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + case VclEventId::WindowHide: + { + aOldValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + break; + default: + { + AccessibleTabBarBase::ProcessWindowEvent( rVclWindowEvent ); + } + break; + } + } + + + void AccessibleTabBar::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) + { + if ( !m_pTabBar ) + return; + + if ( m_pTabBar->IsEnabled() ) + { + rStateSet.AddState( AccessibleStateType::ENABLED ); + rStateSet.AddState( AccessibleStateType::SENSITIVE ); + } + + rStateSet.AddState( AccessibleStateType::FOCUSABLE ); + + if ( m_pTabBar->HasFocus() ) + rStateSet.AddState( AccessibleStateType::FOCUSED ); + + rStateSet.AddState( AccessibleStateType::VISIBLE ); + + if ( m_pTabBar->IsVisible() ) + rStateSet.AddState( AccessibleStateType::SHOWING ); + + if ( m_pTabBar->GetStyle() & WB_SIZEABLE ) + rStateSet.AddState( AccessibleStateType::RESIZABLE ); + } + + + // OCommonAccessibleComponent + + + awt::Rectangle AccessibleTabBar::implGetBounds() + { + awt::Rectangle aBounds; + if ( m_pTabBar ) + aBounds = AWTRectangle( tools::Rectangle( m_pTabBar->GetPosPixel(), m_pTabBar->GetSizePixel() ) ); + + return aBounds; + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( AccessibleTabBar, OAccessibleExtendedComponentHelper, AccessibleTabBar_BASE ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccessibleTabBar, OAccessibleExtendedComponentHelper, AccessibleTabBar_BASE ) + + + // XComponent + + + void AccessibleTabBar::disposing() + { + AccessibleTabBarBase::disposing(); + + // dispose all children + for (const Reference<XAccessible>& i : m_aAccessibleChildren) + { + Reference< XComponent > xComponent( i, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + m_aAccessibleChildren.clear(); + } + + + // XServiceInfo + + + OUString AccessibleTabBar::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTabBar"; + } + + + sal_Bool AccessibleTabBar::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + + Sequence< OUString > AccessibleTabBar::getSupportedServiceNames() + { + return { "com.sun.star.awt.AccessibleTabBar" }; + } + + + // XAccessible + + + Reference< XAccessibleContext > AccessibleTabBar::getAccessibleContext( ) + { + OExternalLockGuard aGuard( this ); + + return this; + } + + + // XAccessibleContext + + + sal_Int32 AccessibleTabBar::getAccessibleChildCount() + { + OExternalLockGuard aGuard( this ); + + return m_aAccessibleChildren.size(); + } + + + Reference< XAccessible > AccessibleTabBar::getAccessibleChild( sal_Int32 i ) + { + OExternalLockGuard aGuard( this ); + + if ( i < 0 || o3tl::make_unsigned(i) >= m_aAccessibleChildren.size() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild = m_aAccessibleChildren[i]; + if ( !xChild.is() ) + { + if ( m_pTabBar ) + { + sal_Int32 nCount = m_pTabBar->GetAccessibleChildWindowCount(); + + if ( i < nCount ) + { + vcl::Window* pChild = m_pTabBar->GetAccessibleChildWindow( static_cast<sal_uInt16>(i) ); + if ( pChild ) + xChild = pChild->GetAccessible(); + } + else if ( i == nCount ) + { + xChild = new AccessibleTabBarPageList( m_pTabBar, i ); + } + + // insert into child list + m_aAccessibleChildren[i] = xChild; + } + } + + return xChild; + } + + + Reference< XAccessible > AccessibleTabBar::getAccessibleParent( ) + { + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xParent; + if ( m_pTabBar ) + { + vcl::Window* pParent = m_pTabBar->GetAccessibleParentWindow(); + if ( pParent ) + xParent = pParent->GetAccessible(); + } + + return xParent; + } + + + sal_Int32 AccessibleTabBar::getAccessibleIndexInParent( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nIndexInParent = -1; + if ( m_pTabBar ) + { + vcl::Window* pParent = m_pTabBar->GetAccessibleParentWindow(); + if ( pParent ) + { + for ( sal_uInt16 i = 0, nCount = pParent->GetAccessibleChildWindowCount(); i < nCount; ++i ) + { + vcl::Window* pChild = pParent->GetAccessibleChildWindow( i ); + if ( pChild == static_cast< vcl::Window* >( m_pTabBar ) ) + { + nIndexInParent = i; + break; + } + } + } + } + + return nIndexInParent; + } + + + sal_Int16 AccessibleTabBar::getAccessibleRole( ) + { + OExternalLockGuard aGuard( this ); + + return AccessibleRole::PANEL; + } + + + OUString AccessibleTabBar::getAccessibleDescription( ) + { + OExternalLockGuard aGuard( this ); + + OUString sDescription; + if ( m_pTabBar ) + sDescription = m_pTabBar->GetAccessibleDescription(); + + return sDescription; + } + + + OUString AccessibleTabBar::getAccessibleName( ) + { + OExternalLockGuard aGuard( this ); + + OUString sName; + if ( m_pTabBar ) + sName = m_pTabBar->GetAccessibleName(); + + return sName; + } + + + Reference< XAccessibleRelationSet > AccessibleTabBar::getAccessibleRelationSet( ) + { + OExternalLockGuard aGuard( this ); + + return new utl::AccessibleRelationSetHelper; + } + + + Reference< XAccessibleStateSet > AccessibleTabBar::getAccessibleStateSet( ) + { + OExternalLockGuard aGuard( this ); + + rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + FillAccessibleStateSet( *pStateSetHelper ); + } + else + { + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + } + + return pStateSetHelper; + } + + + Locale AccessibleTabBar::getLocale( ) + { + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); + } + + + // XAccessibleComponent + + + Reference< XAccessible > AccessibleTabBar::getAccessibleAtPoint( const awt::Point& rPoint ) + { + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xChild; + for ( size_t i = 0; i < m_aAccessibleChildren.size(); ++i ) + { + Reference< XAccessible > xAcc = getAccessibleChild( i ); + if ( xAcc.is() ) + { + Reference< XAccessibleComponent > xComp( xAcc->getAccessibleContext(), UNO_QUERY ); + if ( xComp.is() ) + { + tools::Rectangle aRect = VCLRectangle( xComp->getBounds() ); + Point aPos = VCLPoint( rPoint ); + if ( aRect.Contains( aPos ) ) + { + xChild = xAcc; + break; + } + } + } + } + + return xChild; + } + + + void AccessibleTabBar::grabFocus( ) + { + OExternalLockGuard aGuard( this ); + + if ( m_pTabBar ) + m_pTabBar->GrabFocus(); + } + + + sal_Int32 AccessibleTabBar::getForeground( ) + { + OExternalLockGuard aGuard( this ); + + Color nColor; + if ( m_pTabBar ) + { + if ( m_pTabBar->IsControlForeground() ) + nColor = m_pTabBar->GetControlForeground(); + else + { + vcl::Font aFont; + if ( m_pTabBar->IsControlFont() ) + aFont = m_pTabBar->GetControlFont(); + else + aFont = m_pTabBar->GetFont(); + nColor = aFont.GetColor(); + } + } + + return sal_Int32(nColor); + } + + + sal_Int32 AccessibleTabBar::getBackground( ) + { + OExternalLockGuard aGuard( this ); + + Color nColor; + if ( m_pTabBar ) + { + if ( m_pTabBar->IsControlBackground() ) + nColor = m_pTabBar->GetControlBackground(); + else + nColor = m_pTabBar->GetBackground().GetColor(); + } + + return sal_Int32(nColor); + } + + + // XAccessibleExtendedComponent + + + Reference< awt::XFont > AccessibleTabBar::getFont( ) + { + OExternalLockGuard aGuard( this ); + + Reference< awt::XFont > xFont; + if ( m_pTabBar ) + { + Reference< awt::XDevice > xDev( m_pTabBar->GetComponentInterface(), UNO_QUERY ); + if ( xDev.is() ) + { + vcl::Font aFont; + if ( m_pTabBar->IsControlFont() ) + aFont = m_pTabBar->GetControlFont(); + else + aFont = m_pTabBar->GetFont(); + rtl::Reference<VCLXFont> pVCLXFont = new VCLXFont; + pVCLXFont->Init( *xDev, aFont ); + xFont = pVCLXFont; + } + } + + return xFont; + } + + + OUString AccessibleTabBar::getTitledBorderText( ) + { + OExternalLockGuard aGuard( this ); + + OUString sText; + if ( m_pTabBar ) + sText = m_pTabBar->GetText(); + + return sText; + } + + + OUString AccessibleTabBar::getToolTipText( ) + { + OExternalLockGuard aGuard( this ); + + OUString sText; + if ( m_pTabBar ) + sText = m_pTabBar->GetQuickHelpText(); + + return sText; + } + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletabbarbase.cxx b/accessibility/source/extended/accessibletabbarbase.cxx new file mode 100644 index 000000000..1e213f071 --- /dev/null +++ b/accessibility/source/extended/accessibletabbarbase.cxx @@ -0,0 +1,96 @@ +/* -*- 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/accessibletabbarbase.hxx> +#ifndef ACCESSIBILITY_EXT_ACCESSIBLETABBARPAGELIST +#include <extended/accessibletabbarpagelist.hxx> +#endif +#include <svtools/tabbar.hxx> +#include <vcl/vclevent.hxx> + + +namespace accessibility +{ + + +AccessibleTabBarBase::AccessibleTabBarBase( TabBar* pTabBar ) : + m_pTabBar( nullptr ) +{ + SetTabBarPointer( pTabBar ); +} + +AccessibleTabBarBase::~AccessibleTabBarBase() +{ + ClearTabBarPointer(); +} + +IMPL_LINK( AccessibleTabBarBase, WindowEventListener, VclWindowEvent&, rEvent, void ) +{ + vcl::Window* pEventWindow = rEvent.GetWindow(); + OSL_ENSURE( pEventWindow, "AccessibleTabBarBase::WindowEventListener: no window!" ); + + if( ( rEvent.GetId() == VclEventId::TabbarPageRemoved ) && + ( static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rEvent.GetData())) == TabBar::PAGE_NOT_FOUND ) && + (dynamic_cast<AccessibleTabBarPageList *>(this) == nullptr)) + { + return; + } + + if ( !pEventWindow->IsAccessibilityEventsSuppressed() || (rEvent.GetId() == VclEventId::ObjectDying) ) + ProcessWindowEvent( rEvent ); +} + +void AccessibleTabBarBase::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) +{ + if( rVclWindowEvent.GetId() == VclEventId::ObjectDying ) + ClearTabBarPointer(); +} + +// XComponent + +void AccessibleTabBarBase::disposing() +{ + OAccessibleExtendedComponentHelper::disposing(); + ClearTabBarPointer(); +} + +// private + +void AccessibleTabBarBase::SetTabBarPointer( TabBar* pTabBar ) +{ + OSL_ENSURE( !m_pTabBar, "AccessibleTabBarBase::SetTabBarPointer - multiple call" ); + m_pTabBar = pTabBar; + if( m_pTabBar ) + m_pTabBar->AddEventListener( LINK( this, AccessibleTabBarBase, WindowEventListener ) ); +} + +void AccessibleTabBarBase::ClearTabBarPointer() +{ + if( m_pTabBar ) + { + m_pTabBar->RemoveEventListener( LINK( this, AccessibleTabBarBase, WindowEventListener ) ); + m_pTabBar = nullptr; + } +} + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletabbarpage.cxx b/accessibility/source/extended/accessibletabbarpage.cxx new file mode 100644 index 000000000..197ad78d9 --- /dev/null +++ b/accessibility/source/extended/accessibletabbarpage.cxx @@ -0,0 +1,427 @@ +/* -*- 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/accessibletabbarpage.hxx> +#include <svtools/tabbar.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> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/helper/convert.hxx> +#include <i18nlangtag/languagetag.hxx> + + +namespace accessibility +{ + + + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + using namespace ::comphelper; + + + + + AccessibleTabBarPage::AccessibleTabBarPage( TabBar* pTabBar, sal_uInt16 nPageId, const Reference< XAccessible >& rxParent ) + :AccessibleTabBarBase( pTabBar ) + ,m_nPageId( nPageId ) + ,m_xParent( rxParent ) + { + m_bShowing = IsShowing(); + m_bSelected = IsSelected(); + + if ( m_pTabBar ) + m_sPageText = m_pTabBar->GetPageText( m_nPageId ); + } + + + bool AccessibleTabBarPage::IsEnabled() + { + OExternalLockGuard aGuard( this ); + + bool bEnabled = false; + if ( m_pTabBar ) + bEnabled = m_pTabBar->IsPageEnabled( m_nPageId ); + + return bEnabled; + } + + + bool AccessibleTabBarPage::IsShowing() const + { + bool bShowing = false; + + if ( m_pTabBar && m_pTabBar->IsVisible() ) + bShowing = true; + + return bShowing; + } + + + bool AccessibleTabBarPage::IsSelected() const + { + bool bSelected = false; + + if ( m_pTabBar && m_pTabBar->GetCurPageId() == m_nPageId ) + bSelected = true; + + return bSelected; + } + + + void AccessibleTabBarPage::SetShowing( bool bShowing ) + { + if ( m_bShowing != bShowing ) + { + Any aOldValue, aNewValue; + if ( m_bShowing ) + aOldValue <<= AccessibleStateType::SHOWING; + else + aNewValue <<= AccessibleStateType::SHOWING; + m_bShowing = bShowing; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + + + void AccessibleTabBarPage::SetSelected( bool bSelected ) + { + if ( m_bSelected != bSelected ) + { + Any aOldValue, aNewValue; + if ( m_bSelected ) + aOldValue <<= AccessibleStateType::SELECTED; + else + aNewValue <<= AccessibleStateType::SELECTED; + m_bSelected = bSelected; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + } + } + + + void AccessibleTabBarPage::SetPageText( const OUString& sPageText ) + { + if ( m_sPageText != sPageText ) + { + Any aOldValue, aNewValue; + aOldValue <<= m_sPageText; + aNewValue <<= sPageText; + m_sPageText = sPageText; + NotifyAccessibleEvent( AccessibleEventId::NAME_CHANGED, aOldValue, aNewValue ); + } + } + + + void AccessibleTabBarPage::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) + { + if ( IsEnabled() ) + { + rStateSet.AddState( AccessibleStateType::ENABLED ); + rStateSet.AddState( AccessibleStateType::SENSITIVE ); + } + + rStateSet.AddState( AccessibleStateType::VISIBLE ); + + if ( IsShowing() ) + rStateSet.AddState( AccessibleStateType::SHOWING ); + + rStateSet.AddState( AccessibleStateType::SELECTABLE ); + + if ( IsSelected() ) + rStateSet.AddState( AccessibleStateType::SELECTED ); + } + + + // OCommonAccessibleComponent + + + awt::Rectangle AccessibleTabBarPage::implGetBounds() + { + awt::Rectangle aBounds; + if ( m_pTabBar ) + { + // get bounding rectangle relative to the AccessibleTabBar + aBounds = AWTRectangle( m_pTabBar->GetPageRect( m_nPageId ) ); + + // get position of the AccessibleTabBarPageList relative to the AccessibleTabBar + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComponent.is() ) + { + awt::Point aParentLoc = xParentComponent->getLocation(); + + // calculate bounding rectangle relative to the AccessibleTabBarPageList + aBounds.X -= aParentLoc.X; + aBounds.Y -= aParentLoc.Y; + } + } + } + + return aBounds; + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( AccessibleTabBarPage, OAccessibleExtendedComponentHelper, AccessibleTabBarPage_BASE ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccessibleTabBarPage, OAccessibleExtendedComponentHelper, AccessibleTabBarPage_BASE ) + + + // XComponent + + + void AccessibleTabBarPage::disposing() + { + AccessibleTabBarBase::disposing(); + m_sPageText.clear(); + } + + + // XServiceInfo + + + OUString AccessibleTabBarPage::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTabBarPage"; + } + + + sal_Bool AccessibleTabBarPage::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + + Sequence< OUString > AccessibleTabBarPage::getSupportedServiceNames() + { + return { "com.sun.star.awt.AccessibleTabBarPage" }; + } + + + // XAccessible + + + Reference< XAccessibleContext > AccessibleTabBarPage::getAccessibleContext( ) + { + OExternalLockGuard aGuard( this ); + + return this; + } + + + // XAccessibleContext + + + sal_Int32 AccessibleTabBarPage::getAccessibleChildCount() + { + return 0; + } + + + Reference< XAccessible > AccessibleTabBarPage::getAccessibleChild( sal_Int32 ) + { + OExternalLockGuard aGuard( this ); + + throw IndexOutOfBoundsException(); + } + + + Reference< XAccessible > AccessibleTabBarPage::getAccessibleParent( ) + { + OExternalLockGuard aGuard( this ); + + return m_xParent; + } + + + sal_Int32 AccessibleTabBarPage::getAccessibleIndexInParent( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nIndexInParent = -1; + if ( m_pTabBar ) + nIndexInParent = m_pTabBar->GetPagePos( m_nPageId ); + + return nIndexInParent; + } + + + sal_Int16 AccessibleTabBarPage::getAccessibleRole( ) + { + return AccessibleRole::PAGE_TAB; + } + + + OUString AccessibleTabBarPage::getAccessibleDescription( ) + { + OExternalLockGuard aGuard( this ); + + OUString sDescription; + if ( m_pTabBar ) + sDescription = m_pTabBar->GetHelpText( m_nPageId ); + + return sDescription; + } + + + OUString AccessibleTabBarPage::getAccessibleName( ) + { + OExternalLockGuard aGuard( this ); + + return m_sPageText; + } + + + Reference< XAccessibleRelationSet > AccessibleTabBarPage::getAccessibleRelationSet( ) + { + OExternalLockGuard aGuard( this ); + + return new utl::AccessibleRelationSetHelper; + } + + + Reference< XAccessibleStateSet > AccessibleTabBarPage::getAccessibleStateSet( ) + { + OExternalLockGuard aGuard( this ); + + rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + FillAccessibleStateSet( *pStateSetHelper ); + } + else + { + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + } + + return pStateSetHelper; + } + + + Locale AccessibleTabBarPage::getLocale( ) + { + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); + } + + + // XAccessibleComponent + + + Reference< XAccessible > AccessibleTabBarPage::getAccessibleAtPoint( const awt::Point& ) + { + return Reference< XAccessible >(); + } + + + void AccessibleTabBarPage::grabFocus( ) + { + // no focus + } + + + sal_Int32 AccessibleTabBarPage::getForeground( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getForeground(); + } + + return nColor; + } + + + sal_Int32 AccessibleTabBarPage::getBackground( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getBackground(); + } + + return nColor; + } + + + // XAccessibleExtendedComponent + + + Reference< awt::XFont > AccessibleTabBarPage::getFont( ) + { + OExternalLockGuard aGuard( this ); + + Reference< awt::XFont > xFont; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleExtendedComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + xFont = xParentComp->getFont(); + } + + return xFont; + } + + + OUString AccessibleTabBarPage::getTitledBorderText( ) + { + OExternalLockGuard aGuard( this ); + + return m_sPageText; + } + + + OUString AccessibleTabBarPage::getToolTipText( ) + { + return OUString(); + } + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletabbarpagelist.cxx b/accessibility/source/extended/accessibletabbarpagelist.cxx new file mode 100644 index 000000000..a65750fbd --- /dev/null +++ b/accessibility/source/extended/accessibletabbarpagelist.cxx @@ -0,0 +1,692 @@ +/* -*- 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/accessibletabbarpagelist.hxx> +#include <svtools/tabbar.hxx> +#include <extended/accessibletabbarpage.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 <o3tl/safeint.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <toolkit/helper/convert.hxx> +#include <i18nlangtag/languagetag.hxx> + + +namespace accessibility +{ + + + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + using namespace ::comphelper; + + + + + AccessibleTabBarPageList::AccessibleTabBarPageList( TabBar* pTabBar, sal_Int32 nIndexInParent ) + :AccessibleTabBarBase( pTabBar ) + ,m_nIndexInParent( nIndexInParent ) + { + if ( m_pTabBar ) + m_aAccessibleChildren.assign( m_pTabBar->GetPageCount(), Reference< XAccessible >() ); + } + + + void AccessibleTabBarPageList::UpdateShowing( bool bShowing ) + { + for (const Reference<XAccessible>& xChild : m_aAccessibleChildren) + { + if ( xChild.is() ) + { + AccessibleTabBarPage* pAccessibleTabBarPage = static_cast< AccessibleTabBarPage* >( xChild.get() ); + if ( pAccessibleTabBarPage ) + pAccessibleTabBarPage->SetShowing( bShowing ); + } + } + } + + + void AccessibleTabBarPageList::UpdateSelected( sal_Int32 i, bool bSelected ) + { + NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + + if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() ) + { + Reference< XAccessible > xChild( m_aAccessibleChildren[i] ); + if ( xChild.is() ) + { + AccessibleTabBarPage* pAccessibleTabBarPage = static_cast< AccessibleTabBarPage* >( xChild.get() ); + if ( pAccessibleTabBarPage ) + pAccessibleTabBarPage->SetSelected( bSelected ); + } + } + } + + + void AccessibleTabBarPageList::UpdatePageText( sal_Int32 i ) + { + if ( i < 0 || o3tl::make_unsigned(i) >= m_aAccessibleChildren.size() ) + return; + + Reference< XAccessible > xChild( m_aAccessibleChildren[i] ); + if ( xChild.is() ) + { + AccessibleTabBarPage* pAccessibleTabBarPage = static_cast< AccessibleTabBarPage* >( xChild.get() ); + if ( pAccessibleTabBarPage && m_pTabBar ) + { + OUString sPageText = m_pTabBar->GetPageText( m_pTabBar->GetPageId( static_cast<sal_uInt16>(i) ) ); + pAccessibleTabBarPage->SetPageText( sPageText ); + } + } + } + + + void AccessibleTabBarPageList::InsertChild( sal_Int32 i ) + { + if ( i < 0 || o3tl::make_unsigned(i) > m_aAccessibleChildren.size() ) + return; + + // insert entry in child list + m_aAccessibleChildren.insert( m_aAccessibleChildren.begin() + i, Reference< XAccessible >() ); + + // send accessible child event + Reference< XAccessible > xChild( getAccessibleChild( i ) ); + if ( xChild.is() ) + { + Any aOldValue, aNewValue; + aNewValue <<= xChild; + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + } + } + + + void AccessibleTabBarPageList::RemoveChild( sal_Int32 i ) + { + if ( i < 0 || o3tl::make_unsigned(i) >= m_aAccessibleChildren.size() ) + return; + + // get the accessible of the removed page + Reference< XAccessible > xChild( m_aAccessibleChildren[i] ); + + // remove entry in child list + m_aAccessibleChildren.erase( m_aAccessibleChildren.begin() + i ); + + // send accessible child event + if ( xChild.is() ) + { + Any aOldValue, aNewValue; + aOldValue <<= xChild; + NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue ); + + Reference< XComponent > xComponent( xChild, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + } + + + void AccessibleTabBarPageList::MoveChild( sal_Int32 i, sal_Int32 j ) + { + if ( !(i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() && + j >= 0 && o3tl::make_unsigned(j) <= m_aAccessibleChildren.size()) ) + return; + + if ( i < j ) + --j; + + // get the accessible of the moved page + Reference< XAccessible > xChild( m_aAccessibleChildren[i] ); + + // remove entry in child list at old position + m_aAccessibleChildren.erase( m_aAccessibleChildren.begin() + i ); + + // insert entry in child list at new position + m_aAccessibleChildren.insert( m_aAccessibleChildren.begin() + j, xChild ); + } + + + void AccessibleTabBarPageList::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + switch ( rVclWindowEvent.GetId() ) + { + case VclEventId::WindowEnabled: + { + Any aNewValue; + aNewValue <<= AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, Any(), aNewValue ); + aNewValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, Any(), aNewValue ); + } + break; + case VclEventId::WindowDisabled: + { + Any aOldValue; + aOldValue <<= AccessibleStateType::ENABLED; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, Any() ); + aOldValue <<= AccessibleStateType::SENSITIVE; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, Any() ); + } + break; + case VclEventId::WindowShow: + { + Any aOldValue, aNewValue; + aNewValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + UpdateShowing( true ); + } + break; + case VclEventId::WindowHide: + { + Any aOldValue, aNewValue; + aOldValue <<= AccessibleStateType::SHOWING; + NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); + UpdateShowing( false ); + } + break; + case VclEventId::TabbarPageSelected: + { + // do nothing + } + break; + case VclEventId::TabbarPageActivated: + { + if ( m_pTabBar ) + { + sal_uInt16 nPageId = static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rVclWindowEvent.GetData())); + sal_uInt16 nPagePos = m_pTabBar->GetPagePos( nPageId ); + UpdateSelected( nPagePos, true ); + } + } + break; + case VclEventId::TabbarPageDeactivated: + { + if ( m_pTabBar ) + { + sal_uInt16 nPageId = static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rVclWindowEvent.GetData())); + sal_uInt16 nPagePos = m_pTabBar->GetPagePos( nPageId ); + UpdateSelected( nPagePos, false ); + } + } + break; + case VclEventId::TabbarPageInserted: + { + if ( m_pTabBar ) + { + sal_uInt16 nPageId = static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rVclWindowEvent.GetData())); + sal_uInt16 nPagePos = m_pTabBar->GetPagePos( nPageId ); + InsertChild( nPagePos ); + } + } + break; + case VclEventId::TabbarPageRemoved: + { + if ( m_pTabBar ) + { + sal_uInt16 nPageId = static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rVclWindowEvent.GetData())); + + if ( nPageId == TabBar::PAGE_NOT_FOUND ) + { + for ( sal_Int32 i = m_aAccessibleChildren.size() - 1; i >= 0; --i ) + RemoveChild( i ); + } + else + { + for ( sal_Int32 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + Reference< XAccessible > xChild( getAccessibleChild( i ) ); + if ( xChild.is() ) + { + AccessibleTabBarPage* pAccessibleTabBarPage = static_cast< AccessibleTabBarPage* >( xChild.get() ); + if ( pAccessibleTabBarPage && pAccessibleTabBarPage->GetPageId() == nPageId ) + { + RemoveChild( i ); + break; + } + } + } + } + } + } + break; + case VclEventId::TabbarPageMoved: + { + Pair* pPair = static_cast<Pair*>(rVclWindowEvent.GetData()); + if ( pPair ) + MoveChild( pPair->A(), pPair->B() ); + } + break; + case VclEventId::TabbarPageTextChanged: + { + sal_uInt16 nPageId = static_cast<sal_uInt16>(reinterpret_cast<sal_IntPtr>(rVclWindowEvent.GetData())); + sal_uInt16 nPagePos = m_pTabBar->GetPagePos( nPageId ); + UpdatePageText( nPagePos ); + } + break; + default: + { + AccessibleTabBarBase::ProcessWindowEvent( rVclWindowEvent ); + } + break; + } + } + + + void AccessibleTabBarPageList::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) + { + if ( !m_pTabBar ) + return; + + if ( m_pTabBar->IsEnabled() ) + { + rStateSet.AddState( AccessibleStateType::ENABLED ); + rStateSet.AddState( AccessibleStateType::SENSITIVE ); + } + + rStateSet.AddState( AccessibleStateType::VISIBLE ); + + if ( m_pTabBar->IsVisible() ) + rStateSet.AddState( AccessibleStateType::SHOWING ); + } + + + // OCommonAccessibleComponent + + + awt::Rectangle AccessibleTabBarPageList::implGetBounds() + { + awt::Rectangle aBounds; + if ( m_pTabBar ) + aBounds = AWTRectangle( m_pTabBar->GetPageArea() ); + + return aBounds; + } + + + // XInterface + + + IMPLEMENT_FORWARD_XINTERFACE2( AccessibleTabBarPageList, OAccessibleExtendedComponentHelper, AccessibleTabBarPageList_BASE ) + + + // XTypeProvider + + + IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccessibleTabBarPageList, OAccessibleExtendedComponentHelper, AccessibleTabBarPageList_BASE ) + + + // XComponent + + + void AccessibleTabBarPageList::disposing() + { + AccessibleTabBarBase::disposing(); + + // dispose all children + for (const Reference<XAccessible>& i : m_aAccessibleChildren) + { + Reference< XComponent > xComponent( i, UNO_QUERY ); + if ( xComponent.is() ) + xComponent->dispose(); + } + m_aAccessibleChildren.clear(); + } + + + // XServiceInfo + + + OUString AccessibleTabBarPageList::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTabBarPageList"; + } + + + sal_Bool AccessibleTabBarPageList::supportsService( const OUString& rServiceName ) + { + return cppu::supportsService(this, rServiceName); + } + + + Sequence< OUString > AccessibleTabBarPageList::getSupportedServiceNames() + { + return { "com.sun.star.awt.AccessibleTabBarPageList" }; + } + + + // XAccessible + + + Reference< XAccessibleContext > AccessibleTabBarPageList::getAccessibleContext( ) + { + OExternalLockGuard aGuard( this ); + + return this; + } + + + // XAccessibleContext + + + sal_Int32 AccessibleTabBarPageList::getAccessibleChildCount() + { + OExternalLockGuard aGuard( this ); + + return m_aAccessibleChildren.size(); + } + + + Reference< XAccessible > AccessibleTabBarPageList::getAccessibleChild( sal_Int32 i ) + { + OExternalLockGuard aGuard( this ); + + if ( i < 0 || i >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild = m_aAccessibleChildren[i]; + if ( !xChild.is() ) + { + if ( m_pTabBar ) + { + sal_uInt16 nPageId = m_pTabBar->GetPageId( static_cast<sal_uInt16>(i) ); + + xChild = new AccessibleTabBarPage( m_pTabBar, nPageId, this ); + + // insert into child list + m_aAccessibleChildren[i] = xChild; + } + } + + return xChild; + } + + + Reference< XAccessible > AccessibleTabBarPageList::getAccessibleParent( ) + { + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xParent; + if ( m_pTabBar ) + xParent = m_pTabBar->GetAccessible(); + + return xParent; + } + + + sal_Int32 AccessibleTabBarPageList::getAccessibleIndexInParent( ) + { + OExternalLockGuard aGuard( this ); + + return m_nIndexInParent; + } + + + sal_Int16 AccessibleTabBarPageList::getAccessibleRole( ) + { + return AccessibleRole::PAGE_TAB_LIST; + } + + + OUString AccessibleTabBarPageList::getAccessibleDescription( ) + { + return OUString(); + } + + + OUString AccessibleTabBarPageList::getAccessibleName( ) + { + return OUString(); + } + + + Reference< XAccessibleRelationSet > AccessibleTabBarPageList::getAccessibleRelationSet( ) + { + OExternalLockGuard aGuard( this ); + + return new utl::AccessibleRelationSetHelper; + } + + + Reference< XAccessibleStateSet > AccessibleTabBarPageList::getAccessibleStateSet( ) + { + OExternalLockGuard aGuard( this ); + + rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper; + + if ( !rBHelper.bDisposed && !rBHelper.bInDispose ) + { + FillAccessibleStateSet( *pStateSetHelper ); + } + else + { + pStateSetHelper->AddState( AccessibleStateType::DEFUNC ); + } + + return pStateSetHelper; + } + + + Locale AccessibleTabBarPageList::getLocale( ) + { + OExternalLockGuard aGuard( this ); + + return Application::GetSettings().GetLanguageTag().getLocale(); + } + + + // XAccessibleComponent + + + Reference< XAccessible > AccessibleTabBarPageList::getAccessibleAtPoint( const awt::Point& rPoint ) + { + OExternalLockGuard aGuard( this ); + + Reference< XAccessible > xChild; + for ( size_t i = 0; i < m_aAccessibleChildren.size(); ++i ) + { + Reference< XAccessible > xAcc = getAccessibleChild( i ); + if ( xAcc.is() ) + { + Reference< XAccessibleComponent > xComp( xAcc->getAccessibleContext(), UNO_QUERY ); + if ( xComp.is() ) + { + tools::Rectangle aRect = VCLRectangle( xComp->getBounds() ); + Point aPos = VCLPoint( rPoint ); + if ( aRect.Contains( aPos ) ) + { + xChild = xAcc; + break; + } + } + } + } + + return xChild; + } + + + void AccessibleTabBarPageList::grabFocus( ) + { + // no focus + } + + + sal_Int32 AccessibleTabBarPageList::getForeground( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getForeground(); + } + + return nColor; + } + + + sal_Int32 AccessibleTabBarPageList::getBackground( ) + { + OExternalLockGuard aGuard( this ); + + sal_Int32 nColor = 0; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + nColor = xParentComp->getBackground(); + } + + return nColor; + } + + + // XAccessibleExtendedComponent + + + Reference< awt::XFont > AccessibleTabBarPageList::getFont( ) + { + OExternalLockGuard aGuard( this ); + + Reference< awt::XFont > xFont; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleExtendedComponent > xParentComp( xParent->getAccessibleContext(), UNO_QUERY ); + if ( xParentComp.is() ) + xFont = xParentComp->getFont(); + } + + return xFont; + } + + + OUString AccessibleTabBarPageList::getTitledBorderText( ) + { + return OUString(); + } + + + OUString AccessibleTabBarPageList::getToolTipText( ) + { + return OUString(); + } + + + // XAccessibleSelection + + + void AccessibleTabBarPageList::selectAccessibleChild( sal_Int32 nChildIndex ) + { + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + if ( m_pTabBar ) + { + m_pTabBar->SetCurPageId( m_pTabBar->GetPageId( static_cast<sal_uInt16>(nChildIndex) ) ); + m_pTabBar->PaintImmediately(); + m_pTabBar->ActivatePage(); + m_pTabBar->Select(); + } + } + + + sal_Bool AccessibleTabBarPageList::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + bool bSelected = false; + if ( m_pTabBar && m_pTabBar->GetCurPageId() == m_pTabBar->GetPageId( static_cast<sal_uInt16>(nChildIndex) ) ) + bSelected = true; + + return bSelected; + } + + + void AccessibleTabBarPageList::clearAccessibleSelection( ) + { + // This method makes no sense in a TabBar, and so does nothing. + } + + + void AccessibleTabBarPageList::selectAllAccessibleChildren( ) + { + selectAccessibleChild( 0 ); + } + + + sal_Int32 AccessibleTabBarPageList::getSelectedAccessibleChildCount( ) + { + return 1; + } + + + Reference< XAccessible > AccessibleTabBarPageList::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + OExternalLockGuard aGuard( this ); + + if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xChild; + + for ( sal_Int32 i = 0, j = 0, nCount = getAccessibleChildCount(); i < nCount; i++ ) + { + if ( isAccessibleChildSelected( i ) && ( j++ == nSelectedChildIndex ) ) + { + xChild = getAccessibleChild( i ); + break; + } + } + + return xChild; + } + + + void AccessibleTabBarPageList::deselectAccessibleChild( sal_Int32 nChildIndex ) + { + OExternalLockGuard aGuard( this ); + + if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + + // This method makes no sense in a TabBar, and so does nothing. + } + + +} // namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletablistbox.cxx b/accessibility/source/extended/accessibletablistbox.cxx new file mode 100644 index 000000000..e34f1896f --- /dev/null +++ b/accessibility/source/extended/accessibletablistbox.cxx @@ -0,0 +1,112 @@ +/* -*- 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/accessibletablistbox.hxx> +#include <extended/accessibletablistboxtable.hxx> +#include <vcl/toolkit/svtabbx.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +namespace accessibility +{ + + + // class AccessibleTabListBox ----------------------------------------------------- + + 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() + + AccessibleTabListBox::AccessibleTabListBox( const Reference< XAccessible >& rxParent, SvHeaderTabListBox& rBox ) + :AccessibleBrowseBox( rxParent, nullptr, rBox ) + ,m_pTabListBox( &rBox ) + { + osl_atomic_increment( &m_refCount ); + { + setCreator( this ); + } + osl_atomic_decrement( &m_refCount ); + } + + + AccessibleTabListBox::~AccessibleTabListBox() + { + if ( isAlive() ) + { + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + + rtl::Reference<AccessibleBrowseBoxTable> AccessibleTabListBox::createAccessibleTable() + { + return new AccessibleTabListBoxTable( this, *m_pTabListBox ); + } + + // XInterface ----------------------------------------------------------------- + IMPLEMENT_FORWARD_XINTERFACE2( AccessibleTabListBox, AccessibleBrowseBox, AccessibleTabListBox_Base ) + + // XTypeProvider -------------------------------------------------------------- + IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccessibleTabListBox, AccessibleBrowseBox, AccessibleTabListBox_Base ) + + // XAccessibleContext --------------------------------------------------------- + + sal_Int32 SAL_CALL AccessibleTabListBox::getAccessibleChildCount() + { + return 2; // header and table + } + + Reference< XAccessibleContext > SAL_CALL AccessibleTabListBox::getAccessibleContext() + { + return this; + } + + Reference< XAccessible > SAL_CALL + AccessibleTabListBox::getAccessibleChild( sal_Int32 nChildIndex ) + { + SolarMethodGuard aGuard(getMutex()); + ensureIsAlive(); + + if ( nChildIndex < 0 || nChildIndex > 1 ) + throw IndexOutOfBoundsException(); + + Reference< XAccessible > xRet; + if (nChildIndex == 0) + { + //! so far the actual implementation object only supports column headers + xRet = implGetHeaderBar( AccessibleBrowseBoxObjType::ColumnHeaderBar ); + } + else if (nChildIndex == 1) + xRet = implGetTable(); + + if ( !xRet.is() ) + throw RuntimeException(); + + return xRet; + } + + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/accessibletablistboxtable.cxx b/accessibility/source/extended/accessibletablistboxtable.cxx new file mode 100644 index 000000000..14d48bcad --- /dev/null +++ b/accessibility/source/extended/accessibletablistboxtable.cxx @@ -0,0 +1,347 @@ +/* -*- 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/accessibletablistboxtable.hxx> +#include <extended/AccessibleBrowseBoxTableCell.hxx> +#include <extended/AccessibleBrowseBoxCheckBoxCell.hxx> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <vcl/toolkit/svtabbx.hxx> + +namespace accessibility +{ + + + // class AccessibleTabListBoxTable --------------------------------------------- + + 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() + + AccessibleTabListBoxTable::AccessibleTabListBoxTable( const Reference< XAccessible >& rxParent, SvHeaderTabListBox& rBox ) : + + AccessibleBrowseBoxTable( rxParent, rBox ), + + m_pTabListBox ( &rBox ) + + { + m_pTabListBox->AddEventListener( LINK( this, AccessibleTabListBoxTable, WindowEventListener ) ); + } + + AccessibleTabListBoxTable::~AccessibleTabListBoxTable() + { + if ( isAlive() ) + { + m_pTabListBox = nullptr; + + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } + } + + void AccessibleTabListBoxTable::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + if ( !isAlive() ) + return; + + switch ( VclEventId nEventId = rVclWindowEvent.GetId(); nEventId ) + { + case VclEventId::ObjectDying : + { + m_pTabListBox->RemoveEventListener( LINK( this, AccessibleTabListBoxTable, WindowEventListener ) ); + m_pTabListBox = nullptr; + break; + } + + case VclEventId::ControlGetFocus : + case VclEventId::ControlLoseFocus : + { + uno::Any aOldValue, aNewValue; + if ( nEventId == VclEventId::ControlGetFocus ) + aNewValue <<= AccessibleStateType::FOCUSED; + else + aOldValue <<= AccessibleStateType::FOCUSED; + commitEvent( AccessibleEventId::STATE_CHANGED, aNewValue, aOldValue ); + break; + } + + case VclEventId::ListboxSelect : + { + // First send an event that tells the listeners of a + // modified selection. The active descendant event is + // send after that so that the receiving AT has time to + // read the text or name of the active child. + commitEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + if ( m_pTabListBox && m_pTabListBox->HasFocus() ) + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + sal_Int32 nRow = m_pTabListBox->GetEntryPos( pEntry ); + sal_uInt16 nCol = m_pTabListBox->GetCurrColumn(); + Reference< XAccessible > xChild = + m_pTabListBox->CreateAccessibleCell( nRow, nCol ); + uno::Any aOldValue, aNewValue; + aNewValue <<= xChild; + commitEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aNewValue, aOldValue ); + } + } + break; + } + case VclEventId::WindowGetFocus : + { + uno::Any aOldValue, aNewValue; + aNewValue <<= AccessibleStateType::FOCUSED; + commitEvent( AccessibleEventId::STATE_CHANGED, aNewValue, aOldValue ); + break; + + } + case VclEventId::WindowLoseFocus : + { + uno::Any aOldValue, aNewValue; + aOldValue <<= AccessibleStateType::FOCUSED; + commitEvent( AccessibleEventId::STATE_CHANGED, aNewValue, aOldValue ); + break; + } + case VclEventId::ListboxTreeSelect: + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if (pEntry) + { + sal_Int32 nRow = m_pTabListBox->GetEntryPos( pEntry ); + Reference< XAccessible > xChild = m_pTabListBox->CreateAccessibleCell( nRow, m_pTabListBox->GetCurrColumn() ); + TriState eState = TRISTATE_INDET; + if ( m_pTabListBox->IsCellCheckBox( nRow, m_pTabListBox->GetCurrColumn(), eState ) ) + { + AccessibleCheckBoxCell* pCell = static_cast< AccessibleCheckBoxCell* >( xChild.get() ); + pCell->commitEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + } + else + { + AccessibleBrowseBoxTableCell* pCell = static_cast< AccessibleBrowseBoxTableCell* >( xChild.get() ); + pCell->commitEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); + } + } + } + break; + case VclEventId::ListboxTreeFocus: + { + if ( m_pTabListBox && m_pTabListBox->HasFocus() ) + { + uno::Any aOldValue, aNewValue; + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + sal_Int32 nRow = m_pTabListBox->GetEntryPos( pEntry ); + m_xCurChild = m_pTabListBox->CreateAccessibleCell( nRow, m_pTabListBox->GetCurrColumn() ); + aNewValue <<= m_xCurChild; + commitEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aNewValue ,aOldValue); + } + else + { + aNewValue <<= AccessibleStateType::FOCUSED; + commitEvent( AccessibleEventId::STATE_CHANGED, aNewValue ,aOldValue); + } + } + } + break; + + case VclEventId::CheckboxToggle : + { + if ( m_pTabListBox && m_pTabListBox->HasFocus() ) + { + SvTreeListEntry* pEntry = static_cast< SvTreeListEntry* >( rVclWindowEvent.GetData() ); + if ( pEntry ) + { + sal_Int32 nRow = m_pTabListBox->GetEntryPos( pEntry ); + sal_uInt16 nCol = m_pTabListBox->GetCurrColumn(); + TriState eState = TRISTATE_INDET; + if ( m_pTabListBox->IsCellCheckBox( nRow, nCol, eState ) ) + { + Reference< XAccessible > xChild = + m_pTabListBox->CreateAccessibleCell( nRow, nCol ); + AccessibleCheckBoxCell* pCell = + static_cast< AccessibleCheckBoxCell* >( xChild.get() ); + pCell->SetChecked( SvHeaderTabListBox::IsItemChecked( pEntry, nCol ) ); + } + } + } + break; + } + + default: break; + } + } + + IMPL_LINK( AccessibleTabListBoxTable, WindowEventListener, VclWindowEvent&, rEvent, void ) + { + OSL_ENSURE( rEvent.GetWindow() && m_pTabListBox, "no event window" ); + ProcessWindowEvent( rEvent ); + } + // helpers -------------------------------------------------------------------- + + void AccessibleTabListBoxTable::ensureValidIndex( sal_Int32 _nIndex ) const + { + if ( ( _nIndex < 0 ) || ( _nIndex >= (implGetRowCount() * implGetColumnCount()) ) ) + throw IndexOutOfBoundsException(); + } + + void AccessibleTabListBoxTable::implSelectRow( sal_Int32 _nRow, bool _bSelect ) + { + if ( m_pTabListBox ) + m_pTabListBox->Select( m_pTabListBox->GetEntry( _nRow ), _bSelect ); + } + + sal_Int32 AccessibleTabListBoxTable::implGetRowCount() const + { + return m_pTabListBox ? m_pTabListBox->GetEntryCount() : 0; + } + + sal_Int32 AccessibleTabListBoxTable::implGetColumnCount() const + { + return m_pTabListBox ? m_pTabListBox->GetColumnCount() : 0; + } + + sal_Int32 AccessibleTabListBoxTable::implGetSelRowCount() const + { + return m_pTabListBox ? m_pTabListBox->GetSelectionCount() : 0; + } + + sal_Int32 AccessibleTabListBoxTable::implGetSelRow( sal_Int32 nSelRow ) const + { + if ( m_pTabListBox ) + { + sal_Int32 nRow = 0; + SvTreeListEntry* pEntry = m_pTabListBox->FirstSelected(); + while ( pEntry ) + { + ++nRow; + if ( nRow == nSelRow ) + return m_pTabListBox->GetEntryPos( pEntry ); + pEntry = m_pTabListBox->NextSelected( pEntry ); + } + } + + return 0; + } + + // XInterface & XTypeProvider + + IMPLEMENT_FORWARD_XINTERFACE2(AccessibleTabListBoxTable, AccessibleBrowseBoxTable, AccessibleTabListBoxTableImplHelper) + IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleTabListBoxTable, AccessibleBrowseBoxTable, AccessibleTabListBoxTableImplHelper) + + // XServiceInfo + + OUString AccessibleTabListBoxTable::getImplementationName() + { + return "com.sun.star.comp.svtools.AccessibleTabListBoxTable"; + } + + // XAccessibleSelection + + void SAL_CALL AccessibleTabListBoxTable::selectAccessibleChild( sal_Int32 nChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + ensureValidIndex( nChildIndex ); + + implSelectRow( implGetRow( nChildIndex ), true ); + } + + sal_Bool SAL_CALL AccessibleTabListBoxTable::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + ensureValidIndex( nChildIndex ); + + return m_pTabListBox && m_pTabListBox->IsSelected( m_pTabListBox->GetEntry( implGetRow( nChildIndex ) ) ); + } + + void SAL_CALL AccessibleTabListBoxTable::clearAccessibleSelection( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + + m_pTabListBox->SetNoSelection(); + } + + void SAL_CALL AccessibleTabListBoxTable::selectAllAccessibleChildren( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + + m_pTabListBox->SelectAll(); + } + + sal_Int32 SAL_CALL AccessibleTabListBoxTable::getSelectedAccessibleChildCount( ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + + return implGetColumnCount() * implGetSelRowCount(); + } + + Reference< XAccessible > SAL_CALL AccessibleTabListBoxTable::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + + sal_Int32 nRows = implGetSelRowCount(); + if ( nRows == 0 ) + throw IndexOutOfBoundsException(); + + sal_Int32 nRow = implGetSelRow( nSelectedChildIndex % nRows ); + sal_Int32 nColumn = nSelectedChildIndex / nRows; + return getAccessibleCellAt( nRow, nColumn ); + } + + void SAL_CALL AccessibleTabListBoxTable::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + + ensureIsAlive(); + ensureValidIndex( nSelectedChildIndex ); + + implSelectRow( implGetRow( nSelectedChildIndex ), false ); + } + + +}// namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/accessibility/source/extended/textwindowaccessibility.cxx b/accessibility/source/extended/textwindowaccessibility.cxx new file mode 100644 index 000000000..218ba33e6 --- /dev/null +++ b/accessibility/source/extended/textwindowaccessibility.cxx @@ -0,0 +1,2271 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/awt/FontWeight.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/i18n/Boundary.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <extended/textwindowaccessibility.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/txtattr.hxx> +#include <vcl/window.hxx> +#include <tools/diagnose_ex.h> +#include <toolkit/helper/convert.hxx> +#include <comphelper/sequence.hxx> + +#include <algorithm> +#include <memory> +#include <numeric> +#include <vector> + +namespace accessibility +{ +void SfxListenerGuard::startListening(::SfxBroadcaster & rNotifier) +{ + assert(m_pNotifier == nullptr && "called more than once"); + m_pNotifier = &rNotifier; + m_rListener.StartListening(*m_pNotifier, DuplicateHandling::Prevent); +} + +void SfxListenerGuard::endListening() +{ + if (m_pNotifier != nullptr) + { + m_rListener.EndListening(*m_pNotifier); + m_pNotifier = nullptr; + } +} + +void WindowListenerGuard::startListening(vcl::Window & rNotifier) +{ + assert(m_pNotifier == nullptr && "called more than once"); + m_pNotifier = &rNotifier; + m_pNotifier->AddEventListener(m_aListener); +} + +void WindowListenerGuard::endListening() +{ + if (m_pNotifier) + { + m_pNotifier->RemoveEventListener(m_aListener); + m_pNotifier = nullptr; + } +} + +Paragraph::Paragraph(::rtl::Reference< Document > xDocument, + Paragraphs::size_type nNumber): + ParagraphBase(m_aMutex), + m_xDocument(std::move(xDocument)), + m_nNumber(nNumber), + m_nClientId(0) +{ + m_aParagraphText = m_xDocument->retrieveParagraphText(this); +} + +void +Paragraph::numberChanged(bool bIncremented) +{ + if (bIncremented) + ++m_nNumber; + else + --m_nNumber; +} + +void Paragraph::textChanged() +{ + OUString aParagraphText = implGetText(); + css::uno::Any aOldValue, aNewValue; + if ( implInitTextChangedEvent( m_aParagraphText, aParagraphText, aOldValue, aNewValue ) ) + { + m_aParagraphText = aParagraphText; + notifyEvent(css::accessibility::AccessibleEventId:: + TEXT_CHANGED, + aOldValue, aNewValue); + } +} + +void Paragraph::notifyEvent(::sal_Int16 nEventId, + css::uno::Any const & rOldValue, + css::uno::Any const & rNewValue) +{ + if (m_nClientId) + comphelper::AccessibleEventNotifier::addEvent( m_nClientId, css::accessibility::AccessibleEventObject( + static_cast< ::cppu::OWeakObject * >(this), + nEventId, rNewValue, rOldValue) ); +} + +// virtual +css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL +Paragraph::getAccessibleContext() +{ + checkDisposed(); + return this; +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getAccessibleChildCount() +{ + checkDisposed(); + return 0; +} + +// virtual +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +Paragraph::getAccessibleChild(::sal_Int32) +{ + checkDisposed(); + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Paragraph::getAccessibleChild", + static_cast< css::uno::XWeak * >(this)); +} + +// virtual +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +Paragraph::getAccessibleParent() +{ + checkDisposed(); + return m_xDocument->getAccessible(); +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getAccessibleIndexInParent() +{ + checkDisposed(); + return m_xDocument->retrieveParagraphIndex(this); +} + +// virtual +::sal_Int16 SAL_CALL Paragraph::getAccessibleRole() +{ + checkDisposed(); + return css::accessibility::AccessibleRole::PARAGRAPH; +} + +// virtual +OUString SAL_CALL Paragraph::getAccessibleDescription() +{ + checkDisposed(); + return OUString(); +} + +// virtual +OUString SAL_CALL Paragraph::getAccessibleName() +{ + checkDisposed(); + return OUString(); +} + +// virtual +css::uno::Reference< css::accessibility::XAccessibleRelationSet > +SAL_CALL Paragraph::getAccessibleRelationSet() +{ + checkDisposed(); + return m_xDocument->retrieveParagraphRelationSet( this ); +} + +// virtual +css::uno::Reference< css::accessibility::XAccessibleStateSet > +SAL_CALL Paragraph::getAccessibleStateSet() +{ + checkDisposed(); + + // FIXME Notification of changes (STATE_CHANGED) missing when + // m_rView.IsReadOnly() changes: + return new ::utl::AccessibleStateSetHelper( + m_xDocument->retrieveParagraphState(this)); +} + +// virtual +css::lang::Locale SAL_CALL Paragraph::getLocale() +{ + checkDisposed(); + return m_xDocument->retrieveLocale(); +} + +// virtual +sal_Bool SAL_CALL Paragraph::containsPoint(css::awt::Point const & rPoint) +{ + checkDisposed(); + css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return rPoint.X >= 0 && rPoint.X < aRect.Width + && rPoint.Y >= 0 && rPoint.Y < aRect.Height; +} + +// virtual +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +Paragraph::getAccessibleAtPoint(css::awt::Point const &) +{ + checkDisposed(); + return nullptr; +} + +// virtual +css::awt::Rectangle SAL_CALL Paragraph::getBounds() +{ + checkDisposed(); + return m_xDocument->retrieveParagraphBounds(this, false); +} + +// virtual +css::awt::Point SAL_CALL Paragraph::getLocation() +{ + checkDisposed(); + css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return css::awt::Point(aRect.X, aRect.Y); +} + +// virtual +css::awt::Point SAL_CALL Paragraph::getLocationOnScreen() +{ + checkDisposed(); + css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + true)); + return css::awt::Point(aRect.X, aRect.Y); +} + +// virtual +css::awt::Size SAL_CALL Paragraph::getSize() +{ + checkDisposed(); + css::awt::Rectangle aRect(m_xDocument->retrieveParagraphBounds(this, + false)); + return css::awt::Size(aRect.Width, aRect.Height); +} + +// virtual +void SAL_CALL Paragraph::grabFocus() +{ + checkDisposed(); + VclPtr<vcl::Window> pWindow = m_xDocument->GetWindow(); + if ( pWindow ) + { + pWindow->GrabFocus(); + } + try + { + m_xDocument->changeParagraphSelection(this, 0, 0); + } + catch (const css::lang::IndexOutOfBoundsException &) + { + TOOLS_INFO_EXCEPTION("accessibility", "Paragraph::grabFocus: caught unexpected"); + } +} + +// virtual +sal_Int32 SAL_CALL Paragraph::getForeground() +{ + return 0; // TODO +} + +// virtual +sal_Int32 SAL_CALL Paragraph::getBackground() +{ + return 0; // TODO +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getCaretPosition() +{ + checkDisposed(); + return m_xDocument->retrieveParagraphCaretPosition(this); +} + +// virtual +sal_Bool SAL_CALL Paragraph::setCaretPosition(::sal_Int32 nIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphSelection(this, nIndex, nIndex); + return true; +} + +// virtual +::sal_Unicode SAL_CALL Paragraph::getCharacter(::sal_Int32 nIndex) +{ + checkDisposed(); + return OCommonAccessibleText::implGetCharacter(implGetText(), nIndex); +} + +// virtual +css::uno::Sequence< css::beans::PropertyValue > SAL_CALL +Paragraph::getCharacterAttributes(::sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes) +{ + checkDisposed(); + return m_xDocument->retrieveCharacterAttributes( this, nIndex, aRequestedAttributes ); +} + +// virtual +css::awt::Rectangle SAL_CALL +Paragraph::getCharacterBounds(::sal_Int32 nIndex) +{ + checkDisposed(); + css::awt::Rectangle aBounds(m_xDocument->retrieveCharacterBounds(this, nIndex)); + css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false)); + aBounds.X -= aParaBounds.X; + aBounds.Y -= aParaBounds.Y; + return aBounds; +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getCharacterCount() +{ + checkDisposed(); + return implGetText().getLength(); +} + +// virtual +::sal_Int32 SAL_CALL +Paragraph::getIndexAtPoint(css::awt::Point const & rPoint) +{ + checkDisposed(); + css::awt::Point aPoint(rPoint); + css::awt::Rectangle aParaBounds(m_xDocument->retrieveParagraphBounds(this, false)); + aPoint.X += aParaBounds.X; + aPoint.Y += aParaBounds.Y; + return m_xDocument->retrieveCharacterIndex(this, aPoint); +} + +// virtual +OUString SAL_CALL Paragraph::getSelectedText() +{ + checkDisposed(); + + return OCommonAccessibleText::getSelectedText(); +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getSelectionStart() +{ + checkDisposed(); + return OCommonAccessibleText::getSelectionStart(); +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getSelectionEnd() +{ + checkDisposed(); + return OCommonAccessibleText::getSelectionEnd(); +} + +// virtual +sal_Bool SAL_CALL Paragraph::setSelection(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphSelection(this, nStartIndex, nEndIndex); + return true; +} + +// virtual +OUString SAL_CALL Paragraph::getText() +{ + checkDisposed(); + return implGetText(); +} + +// virtual +OUString SAL_CALL Paragraph::getTextRange(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) +{ + checkDisposed(); + return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex); +} + +// virtual +css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + checkDisposed(); + return OCommonAccessibleText::getTextAtIndex(nIndex, aTextType); +} + +// virtual +css::accessibility::TextSegment SAL_CALL Paragraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + checkDisposed(); + return OCommonAccessibleText::getTextBeforeIndex(nIndex, aTextType); +} + +// virtual +css::accessibility::TextSegment SAL_CALL Paragraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + checkDisposed(); + return OCommonAccessibleText::getTextBehindIndex(nIndex, aTextType); +} + +// virtual +sal_Bool SAL_CALL Paragraph::copyText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) +{ + checkDisposed(); + m_xDocument->copyParagraphText(this, nStartIndex, nEndIndex); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::scrollSubstringTo( sal_Int32, sal_Int32, css::accessibility::AccessibleScrollType ) +{ + return false; +} + +// virtual +sal_Bool SAL_CALL Paragraph::cutText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, true, false, + OUString()); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::pasteText(::sal_Int32 nIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nIndex, nIndex, false, true, + OUString()); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::deleteText(::sal_Int32 nStartIndex, + ::sal_Int32 nEndIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false, + OUString()); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::insertText(OUString const & rText, + ::sal_Int32 nIndex) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nIndex, nIndex, false, false, rText); + return true; +} + +// virtual +sal_Bool SAL_CALL +Paragraph::replaceText(::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex, + OUString const & rReplacement) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, nStartIndex, nEndIndex, false, false, + rReplacement); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::setAttributes( + ::sal_Int32 nStartIndex, ::sal_Int32 nEndIndex, + css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet) +{ + checkDisposed(); + m_xDocument->changeParagraphAttributes(this, nStartIndex, nEndIndex, + rAttributeSet); + return true; +} + +// virtual +sal_Bool SAL_CALL Paragraph::setText(OUString const & rText) +{ + checkDisposed(); + m_xDocument->changeParagraphText(this, rText); + return true; +} + +// virtual +css::uno::Sequence< css::beans::PropertyValue > SAL_CALL +Paragraph::getDefaultAttributes(const css::uno::Sequence< OUString >&) +{ + checkDisposed(); + return {}; // default attributes are not supported by text engine +} + +// virtual +css::uno::Sequence< css::beans::PropertyValue > SAL_CALL +Paragraph::getRunAttributes(::sal_Int32 Index, const css::uno::Sequence< OUString >& RequestedAttributes) +{ + checkDisposed(); + return m_xDocument->retrieveRunAttributes( this, Index, RequestedAttributes ); +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getLineNumberAtIndex( ::sal_Int32 nIndex ) +{ + checkDisposed(); + + ::sal_Int32 nLineNo = -1; + m_xDocument->retrieveParagraphLineBoundary( this, nIndex, &nLineNo ); + + return nLineNo; +} + +// virtual +css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineNumber( ::sal_Int32 nLineNo ) +{ + checkDisposed(); + + css::i18n::Boundary aBoundary = + m_xDocument->retrieveParagraphBoundaryOfLine( this, nLineNo ); + + return css::accessibility::TextSegment( getTextRange(aBoundary.startPos, aBoundary.endPos), + aBoundary.startPos, aBoundary.endPos); +} + +// virtual +css::accessibility::TextSegment SAL_CALL Paragraph::getTextAtLineWithCaret( ) +{ + checkDisposed(); + + sal_Int32 nLineNo = getNumberOfLineWithCaret(); + + try { + return ( nLineNo >= 0 ) ? + getTextAtLineNumber( nLineNo ) : + css::accessibility::TextSegment(); + } catch (const css::lang::IndexOutOfBoundsException&) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "textwindowaccessibility.cxx:" + " Paragraph::getTextAtLineWithCaret", + static_cast< css::uno::XWeak * >( this ), anyEx ); + } +} + +// virtual +::sal_Int32 SAL_CALL Paragraph::getNumberOfLineWithCaret( ) +{ + checkDisposed(); + return m_xDocument->retrieveParagraphLineWithCursor(this); +} + + +// virtual +void SAL_CALL Paragraph::addAccessibleEventListener( + css::uno::Reference< + css::accessibility::XAccessibleEventListener > const & rListener) +{ + if (!rListener.is()) + return; + + ::osl::ClearableMutexGuard aGuard(rBHelper.rMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + aGuard.clear(); + rListener->disposing(css::lang::EventObject( + static_cast< ::cppu::OWeakObject * >(this))); + } + else + { + if (!m_nClientId) + m_nClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, rListener ); + } +} + +// virtual +void SAL_CALL Paragraph::removeAccessibleEventListener( + css::uno::Reference< + css::accessibility::XAccessibleEventListener > const & rListener) +{ + comphelper::AccessibleEventNotifier::TClientId nId = 0; + { + osl::MutexGuard aGuard(rBHelper.rMutex); + if (rListener.is() && m_nClientId != 0 + && comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, rListener ) == 0) + { + nId = m_nClientId; + m_nClientId = 0; + } + } + if (nId != 0) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient(nId); + } +} + +// virtual +void SAL_CALL Paragraph::disposing() +{ + comphelper::AccessibleEventNotifier::TClientId nId = 0; + { + osl::MutexGuard aGuard(rBHelper.rMutex); + nId = m_nClientId; + m_nClientId = 0; + } + if (nId != 0) + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(nId, *this); +} + +// virtual +OUString Paragraph::implGetText() +{ + return m_xDocument->retrieveParagraphText(this); +} + +// virtual +css::lang::Locale Paragraph::implGetLocale() +{ + return m_xDocument->retrieveLocale(); +} + +// virtual +void Paragraph::implGetSelection(::sal_Int32 & rStartIndex, + ::sal_Int32 & rEndIndex) +{ + m_xDocument->retrieveParagraphSelection(this, &rStartIndex, &rEndIndex); +} + +// virtual +void Paragraph::implGetParagraphBoundary( const OUString& rText, + css::i18n::Boundary& rBoundary, + ::sal_Int32 nIndex ) +{ + ::sal_Int32 nLength = rText.getLength(); + + if ( implIsValidIndex( nIndex, nLength ) ) + { + rBoundary.startPos = 0; + rBoundary.endPos = nLength; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } +} + +// virtual +void Paragraph::implGetLineBoundary( const OUString& rText, + css::i18n::Boundary& rBoundary, + ::sal_Int32 nIndex ) +{ + ::sal_Int32 nLength = rText.getLength(); + + if ( implIsValidIndex( nIndex, nLength ) || nIndex == nLength ) + { + css::i18n::Boundary aBoundary = + m_xDocument->retrieveParagraphLineBoundary( this, nIndex, nullptr ); + rBoundary.startPos = aBoundary.startPos; + rBoundary.endPos = aBoundary.endPos; + } + else + { + rBoundary.startPos = nIndex; + rBoundary.endPos = nIndex; + } +} + + +void Paragraph::checkDisposed() +{ + ::osl::MutexGuard aGuard(rBHelper.rMutex); + if (!(rBHelper.bDisposed || rBHelper.bInDispose)) + return; + throw css::lang::DisposedException( + OUString(), static_cast< css::uno::XWeak * >(this)); +} + +Document::Document(::VCLXWindow * pVclXWindow, ::TextEngine & rEngine, + ::TextView & rView): + VCLXAccessibleComponent(pVclXWindow), + m_xAccessible(pVclXWindow), + m_rEngine(rEngine), + m_rView(rView), + m_aEngineListener(*this), + m_aViewListener(LINK(this, Document, WindowEventHandler)), + m_nViewOffset(0), + m_nViewHeight(0), + m_nVisibleBeginOffset(0), + m_nSelectionFirstPara(-1), + m_nSelectionFirstPos(-1), + m_nSelectionLastPara(-1), + m_nSelectionLastPos(-1), + m_bSelectionChangedNotification(false) +{} + +css::lang::Locale Document::retrieveLocale() +{ + SolarMutexGuard aGuard; + return m_rEngine.GetLocale(); +} + +::sal_Int32 Document::retrieveParagraphIndex(Paragraph const * pParagraph) +{ + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible, it can + // happen that this Paragraph lies outside the range from m_aVisibleBegin + // to m_aVisibleEnd. In that case, return -1 instead of a valid index: + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + return aPara < m_aVisibleBegin || aPara >= m_aVisibleEnd + ? -1 : static_cast< ::sal_Int32 >(aPara - m_aVisibleBegin); + // XXX numeric overflow +} + +::sal_Int64 Document::retrieveParagraphState(Paragraph const * pParagraph) +{ + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible, it can + // happen that this Paragraph lies outside the range from m_aVisibleBegin + // to m_aVisibleEnd. In that case, it is neither VISIBLE nor SHOWING: + ::sal_Int64 nState + = (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::ENABLED) + | (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::SENSITIVE) + | (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::FOCUSABLE) + | (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::MULTI_LINE); + if (!m_rView.IsReadOnly()) + nState |= (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::EDITABLE); + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + if (aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd) + { + nState + |= (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::VISIBLE) + | (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::SHOWING); + if (aPara == m_aFocused) + nState |= (static_cast< ::sal_Int64 >(1) + << css::accessibility::AccessibleStateType::FOCUSED); + } + return nState; +}; + +css::awt::Rectangle +Document::retrieveParagraphBounds(Paragraph const * pParagraph, + bool bAbsolute) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + + // If a client holds on to a Paragraph that is no longer visible (as it + // scrolled out the top of the view), it can happen that this Paragraph + // lies before m_aVisibleBegin. In that case, calculate the vertical + // position of the Paragraph starting at paragraph 0, otherwise optimize + // and start at m_aVisibleBegin: + Paragraphs::iterator aPara(m_xParagraphs->begin() + + pParagraph->getNumber()); + auto lAddHeight = [](const sal_Int32& rSum, const ParagraphInfo& rParagraph) { + return rSum + rParagraph.getHeight(); }; + ::sal_Int32 nPos; + if (aPara < m_aVisibleBegin) + nPos = std::accumulate(m_xParagraphs->begin(), aPara, sal_Int32(0), lAddHeight); + else + nPos = std::accumulate(m_aVisibleBegin, aPara, m_nViewOffset - m_nVisibleBeginOffset, lAddHeight); + + Point aOrig(0, 0); + if (bAbsolute) + aOrig = m_rView.GetWindow()->OutputToAbsoluteScreenPixel(aOrig); + + return css::awt::Rectangle( + static_cast< ::sal_Int32 >(aOrig.X()), + static_cast< ::sal_Int32 >(aOrig.Y()) + nPos - m_nViewOffset, + m_rView.GetWindow()->GetOutputSizePixel().Width(), aPara->getHeight()); + // XXX numeric overflow (3x) +} + +OUString +Document::retrieveParagraphText(Paragraph const * pParagraph) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + return m_rEngine.GetText(static_cast< ::sal_uInt32 >(pParagraph->getNumber())); + // numeric overflow cannot happen here +} + +void Document::retrieveParagraphSelection(Paragraph const * pParagraph, + ::sal_Int32 * pBegin, + ::sal_Int32 * pEnd) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::TextSelection const & rSelection = m_rView.GetSelection(); + Paragraphs::size_type nNumber = pParagraph->getNumber(); + TextPaM aStartPaM( rSelection.GetStart() ); + TextPaM aEndPaM( rSelection.GetEnd() ); + TextPaM aMinPaM( std::min( aStartPaM, aEndPaM ) ); + TextPaM aMaxPaM( std::max( aStartPaM, aEndPaM ) ); + + if ( nNumber >= aMinPaM.GetPara() && nNumber <= aMaxPaM.GetPara() ) + { + *pBegin = nNumber > aMinPaM.GetPara() ? 0 : aMinPaM.GetIndex(); + // XXX numeric overflow + *pEnd = nNumber < aMaxPaM.GetPara() + ? m_rEngine.GetText(static_cast< ::sal_uInt32 >(nNumber)).getLength() + : aMaxPaM.GetIndex(); + // XXX numeric overflow (3x) + + if ( aStartPaM > aEndPaM ) + std::swap( *pBegin, *pEnd ); + } + else + { + *pBegin = 0; + *pEnd = 0; + } +} + +::sal_Int32 Document::retrieveParagraphCaretPosition(Paragraph const * pParagraph) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::TextSelection const & rSelection = m_rView.GetSelection(); + Paragraphs::size_type nNumber = pParagraph->getNumber(); + TextPaM aEndPaM( rSelection.GetEnd() ); + + return aEndPaM.GetPara() == nNumber ? aEndPaM.GetIndex() : -1; +} + +css::awt::Rectangle +Document::retrieveCharacterBounds(Paragraph const * pParagraph, + ::sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + sal_Int32 nLength = m_rEngine.GetText(nNumber).getLength(); + // XXX numeric overflow + if (nIndex < 0 || nIndex > nLength) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::retrieveCharacterAttributes", + static_cast< css::uno::XWeak * >(this)); + css::awt::Rectangle aBounds( 0, 0, 0, 0 ); + if ( nIndex == nLength ) + { + aBounds = AWTRectangle( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex))); + } + else + { + ::tools::Rectangle aLeft( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex))); + // XXX numeric overflow + ::tools::Rectangle aRight( + m_rEngine.PaMtoEditCursor(::TextPaM(nNumber, nIndex + 1))); + // XXX numeric overflow (2x) + // FIXME If the vertical extends of the two cursors do not match, assume + // nIndex is the last character on the line; the bounding box will then + // extend to m_rEngine.GetMaxTextWidth(): + ::sal_Int32 nWidth = (aLeft.Top() == aRight.Top() + && aLeft.Bottom() == aRight.Bottom()) + ? static_cast< ::sal_Int32 >(aRight.Left() - aLeft.Left()) + : static_cast< ::sal_Int32 >(m_rEngine.GetMaxTextWidth() + - aLeft.Left()); + // XXX numeric overflow (4x) + aBounds = css::awt::Rectangle(static_cast< ::sal_Int32 >(aLeft.Left()), + static_cast< ::sal_Int32 >(aLeft.Top() - m_nViewOffset), + nWidth, + static_cast< ::sal_Int32 >(aLeft.Bottom() + - aLeft.Top())); + // XXX numeric overflow (4x) + } + return aBounds; +} + +::sal_Int32 Document::retrieveCharacterIndex(Paragraph const * pParagraph, + css::awt::Point const & rPoint) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + ::TextPaM aPaM(m_rEngine.GetPaM(::Point(static_cast< tools::Long >(rPoint.X), + static_cast< tools::Long >(rPoint.Y)))); + // XXX numeric overflow (2x) + return aPaM.GetPara() == nNumber ? aPaM.GetIndex() : -1; + // XXX numeric overflow +} + +css::uno::Sequence< css::beans::PropertyValue > +Document::retrieveCharacterAttributes( + Paragraph const * pParagraph, ::sal_Int32 nIndex, + const css::uno::Sequence< OUString >& aRequestedAttributes) +{ + SolarMutexGuard aGuard; + + vcl::Font aFont = m_rEngine.GetFont(); + const sal_Int32 AttributeCount = 9; + std::vector< css::beans::PropertyValue > aAttribs; + aAttribs.reserve(AttributeCount); + + css::beans::PropertyValue aAttrib; + aAttrib.Handle = -1; + aAttrib.State = css::beans::PropertyState_DIRECT_VALUE; + + //character background color + aAttrib.Name = "CharBackColor"; + aAttrib.Value = mapFontColor( aFont.GetFillColor() ); + aAttribs.push_back(aAttrib); + + //character color + aAttrib.Name = "CharColor"; + //aAttrib.Value = mapFontColor( aFont.GetColor() ); + aAttrib.Value = mapFontColor( m_rEngine.GetTextColor() ); + aAttribs.push_back(aAttrib); + + //character font name + aAttrib.Name = "CharFontName"; + aAttrib.Value <<= aFont.GetFamilyName(); + aAttribs.push_back(aAttrib); + + //character height + aAttrib.Name = "CharHeight"; + aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetFontHeight()); + aAttribs.push_back(aAttrib); + + //character posture + aAttrib.Name = "CharPosture"; + aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetItalic()); + aAttribs.push_back(aAttrib); + + //character relief + /* + aAttrib.Name = "CharRelief"; + aAttrib.Value = css::uno::Any( (sal_Int16)aFont.GetRelief() ); + aAttribs.push_back(aAttrib); + */ + + //character strikeout + aAttrib.Name = "CharStrikeout"; + aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetStrikeout()); + aAttribs.push_back(aAttrib); + + //character underline + aAttrib.Name = "CharUnderline"; + aAttrib.Value <<= static_cast<sal_Int16>(aFont.GetUnderline()); + aAttribs.push_back(aAttrib); + + //character weight + aAttrib.Name = "CharWeight"; + aAttrib.Value <<= static_cast<float>(aFont.GetWeight()); + aAttribs.push_back(aAttrib); + + //character alignment + aAttrib.Name = "ParaAdjust"; + aAttrib.Value <<= static_cast<sal_Int16>(m_rEngine.GetTextAlign()); + aAttribs.push_back(aAttrib); + + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + // nIndex can be equal to getLength(); + if (nIndex < 0 || nIndex > m_rEngine.GetText(nNumber).getLength()) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::retrieveCharacterAttributes", + static_cast< css::uno::XWeak * >(this)); + + + // retrieve run attributes + tPropValMap aCharAttrSeq; + retrieveRunAttributesImpl( pParagraph, nIndex, aRequestedAttributes, aCharAttrSeq ); + + for (const css::beans::PropertyValue& rAttrib : aAttribs) + { + aCharAttrSeq[ rAttrib.Name ] = rAttrib; + } + + const css::uno::Sequence< css::beans::PropertyValue > aRes = comphelper::mapValuesToSequence( aCharAttrSeq ); + + // sort the attributes + auto nLength = static_cast<size_t>(aRes.getLength()); + std::unique_ptr<sal_Int32[]> pIndices( new sal_Int32[nLength] ); + std::iota(&pIndices[0], &pIndices[nLength], 0); + std::sort(&pIndices[0], &pIndices[nLength], + [&aRes](sal_Int32 a, sal_Int32 b) { return aRes[a].Name < aRes[b].Name; }); + + // create sorted sequences according to index array + std::vector<css::beans::PropertyValue> aNewValues; + aNewValues.reserve(nLength); + std::transform(&pIndices[0], &pIndices[nLength], std::back_inserter(aNewValues), + [&aRes](const sal_Int32 nIdx) { return aRes[nIdx]; }); + + return comphelper::containerToSequence(aNewValues); +} + +void Document::retrieveRunAttributesImpl( + Paragraph const * pParagraph, ::sal_Int32 Index, + const css::uno::Sequence< OUString >& RequestedAttributes, + tPropValMap& rRunAttrSeq) +{ + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() ); + ::TextPaM aPaM( nNumber, Index ); + // XXX numeric overflow + ::TextAttribFontColor const * pColor + = static_cast< ::TextAttribFontColor const * >( + m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTCOLOR ) ); + ::TextAttribFontWeight const * pWeight + = static_cast< ::TextAttribFontWeight const * >( + m_rEngine.FindAttrib( aPaM, TEXTATTR_FONTWEIGHT ) ); + tPropValMap aRunAttrSeq; + if ( pColor ) + { + css::beans::PropertyValue aPropVal; + aPropVal.Name = "CharColor"; + aPropVal.Handle = -1; + aPropVal.Value = mapFontColor( pColor->GetColor() ); + aPropVal.State = css::beans::PropertyState_DIRECT_VALUE; + aRunAttrSeq[ aPropVal.Name ] = aPropVal; + } + if ( pWeight ) + { + css::beans::PropertyValue aPropVal; + aPropVal.Name = "CharWeight"; + aPropVal.Handle = -1; + aPropVal.Value = mapFontWeight( pWeight->getFontWeight() ); + aPropVal.State = css::beans::PropertyState_DIRECT_VALUE; + aRunAttrSeq[ aPropVal.Name ] = aPropVal; + } + if ( !RequestedAttributes.hasElements() ) + { + rRunAttrSeq = aRunAttrSeq; + } + else + { + for ( const OUString& rReqAttr : RequestedAttributes ) + { + tPropValMap::iterator aIter = aRunAttrSeq.find( rReqAttr ); + if ( aIter != aRunAttrSeq.end() ) + { + rRunAttrSeq[ (*aIter).first ] = (*aIter).second; + } + } + } +} + +css::uno::Sequence< css::beans::PropertyValue > +Document::retrieveRunAttributes( + Paragraph const * pParagraph, ::sal_Int32 Index, + const css::uno::Sequence< OUString >& RequestedAttributes) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard( GetMutex() ); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() ); + // XXX numeric overflow + if ( Index < 0 || Index >= m_rEngine.GetText(nNumber).getLength() ) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::retrieveRunAttributes", + static_cast< css::uno::XWeak * >( this ) ); + + tPropValMap aRunAttrSeq; + retrieveRunAttributesImpl( pParagraph, Index, RequestedAttributes, aRunAttrSeq ); + return comphelper::mapValuesToSequence( aRunAttrSeq ); +} + +void Document::changeParagraphText(Paragraph const * pParagraph, + OUString const & rText) +{ + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + changeParagraphText(nNumber, 0, m_rEngine.GetTextLen(nNumber), false, + false, rText); + } +} + +void Document::changeParagraphText(Paragraph const * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd, + bool bCut, bool bPaste, + OUString const & rText) +{ + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).getLength()) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::changeParagraphText", + static_cast< css::uno::XWeak * >(this)); + changeParagraphText(nNumber, static_cast< ::sal_uInt16 >(nBegin), + static_cast< ::sal_uInt16 >(nEnd), bCut, bPaste, rText); + // XXX numeric overflow (2x) + } +} + +void Document::copyParagraphText(Paragraph const * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd) +{ + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).getLength()) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::copyParagraphText", + static_cast< css::uno::XWeak * >(this)); + m_rView.SetSelection( + ::TextSelection(::TextPaM(nNumber, nBegin), + ::TextPaM(nNumber, nEnd))); + // XXX numeric overflow (2x) + m_rView.Copy(); + } +} + +void Document::changeParagraphAttributes( + Paragraph const * pParagraph, ::sal_Int32 nBegin, ::sal_Int32 nEnd, + css::uno::Sequence< css::beans::PropertyValue > const & rAttributeSet) +{ + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).getLength()) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::changeParagraphAttributes", + static_cast< css::uno::XWeak * >(this)); + + // FIXME The new attributes are added to any attributes already set, + // they do not replace the old attributes as required by + // XAccessibleEditableText.setAttributes: + for (const auto& rAttr : rAttributeSet) + if ( rAttr.Name == "CharColor" ) + m_rEngine.SetAttrib(::TextAttribFontColor( + mapFontColor(rAttr.Value)), + nNumber, nBegin, nEnd); + // XXX numeric overflow (2x) + else if ( rAttr.Name == "CharWeight" ) + m_rEngine.SetAttrib(::TextAttribFontWeight( + mapFontWeight(rAttr.Value)), + nNumber, nBegin, nEnd); + // XXX numeric overflow (2x) + } +} + +void Document::changeParagraphSelection(Paragraph const * pParagraph, + ::sal_Int32 nBegin, ::sal_Int32 nEnd) +{ + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >(pParagraph->getNumber()); + // XXX numeric overflow + if (nBegin < 0 || nBegin > nEnd + || nEnd > m_rEngine.GetText(nNumber).getLength()) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::changeParagraphSelection", + static_cast< css::uno::XWeak * >(this)); + m_rView.SetSelection( + ::TextSelection(::TextPaM(nNumber, nBegin), + ::TextPaM(nNumber, nEnd))); + // XXX numeric overflow (2x) + } +} + +css::i18n::Boundary +Document::retrieveParagraphLineBoundary( Paragraph const * pParagraph, + ::sal_Int32 nIndex, ::sal_Int32 *pLineNo ) +{ + css::i18n::Boundary aBoundary; + aBoundary.startPos = nIndex; + aBoundary.endPos = nIndex; + + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard( GetMutex() ); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() ); + if ( nIndex < 0 || nIndex > m_rEngine.GetText( nNumber ).getLength() ) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::retrieveParagraphLineBoundary", + static_cast< css::uno::XWeak * >( this ) ); + ::sal_Int32 nLineStart = 0; + ::sal_Int32 nLineEnd = 0; + ::sal_uInt16 nLineCount = m_rEngine.GetLineCount( nNumber ); + for ( ::sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine ) + { + nLineStart = nLineEnd; + nLineEnd += m_rEngine.GetLineLen( nNumber, nLine ); + if ( nIndex >= nLineStart && ( ( nLine == nLineCount - 1 ) ? nIndex <= nLineEnd : nIndex < nLineEnd ) ) + { + aBoundary.startPos = nLineStart; + aBoundary.endPos = nLineEnd; + if( pLineNo ) + pLineNo[0] = nLine; + break; + } + } + } + + return aBoundary; +} + +css::i18n::Boundary +Document::retrieveParagraphBoundaryOfLine( Paragraph const * pParagraph, + ::sal_Int32 nLineNo ) +{ + css::i18n::Boundary aBoundary; + aBoundary.startPos = 0; + aBoundary.endPos = 0; + + SolarMutexGuard aGuard; + { + ::osl::MutexGuard aInternalGuard( GetMutex() ); + ::sal_uInt32 nNumber = static_cast< ::sal_uInt32 >( pParagraph->getNumber() ); + if ( nLineNo >= m_rEngine.GetLineCount( nNumber ) ) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::retrieveParagraphBoundaryOfLine", + static_cast< css::uno::XWeak * >( this ) ); + ::sal_Int32 nLineStart = 0; + ::sal_Int32 nLineEnd = 0; + for ( ::sal_Int32 nLine = 0; nLine <= nLineNo; ++nLine ) + { + nLineStart = nLineEnd; + nLineEnd += m_rEngine.GetLineLen( nNumber, nLine ); + } + + aBoundary.startPos = nLineStart; + aBoundary.endPos = nLineEnd; + } + + return aBoundary; +} + +sal_Int32 Document::retrieveParagraphLineWithCursor( Paragraph const * pParagraph ) +{ + SolarMutexGuard aGuard; + ::osl::MutexGuard aInternalGuard(GetMutex()); + ::TextSelection const & rSelection = m_rView.GetSelection(); + Paragraphs::size_type nNumber = pParagraph->getNumber(); + TextPaM aEndPaM( rSelection.GetEnd() ); + + return aEndPaM.GetPara() == nNumber + ? m_rView.GetLineNumberOfCursorInSelection() : -1; +} + + +css::uno::Reference< css::accessibility::XAccessibleRelationSet > +Document::retrieveParagraphRelationSet( Paragraph const * pParagraph ) +{ + ::osl::MutexGuard aInternalGuard( GetMutex() ); + + rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSetHelper = new ::utl::AccessibleRelationSetHelper(); + + Paragraphs::iterator aPara( m_xParagraphs->begin() + pParagraph->getNumber() ); + + if ( aPara > m_aVisibleBegin && aPara < m_aVisibleEnd ) + { + css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara - 1 ) }; + css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM, aSequence ); + pRelationSetHelper->AddRelation( aRelation ); + } + + if ( aPara >= m_aVisibleBegin && aPara < m_aVisibleEnd -1 ) + { + css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleChild( aPara + 1 ) }; + css::accessibility::AccessibleRelation aRelation( css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO, aSequence ); + pRelationSetHelper->AddRelation( aRelation ); + } + + return pRelationSetHelper; +} + +// virtual +::sal_Int32 SAL_CALL Document::getAccessibleChildCount() +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + return m_aVisibleEnd - m_aVisibleBegin; +} + +// virtual +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +Document::getAccessibleChild(::sal_Int32 i) +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + if (i < 0 || i >= m_aVisibleEnd - m_aVisibleBegin) + throw css::lang::IndexOutOfBoundsException( + "textwindowaccessibility.cxx:" + " Document::getAccessibleChild", + static_cast< css::uno::XWeak * >(this)); + return getAccessibleChild(m_aVisibleBegin + + static_cast< Paragraphs::size_type >(i)); +} + +// virtual +::sal_Int16 SAL_CALL Document::getAccessibleRole() +{ + return css::accessibility::AccessibleRole::TEXT_FRAME; +} + +// virtual +css::uno::Reference< css::accessibility::XAccessible > SAL_CALL +Document::getAccessibleAtPoint(css::awt::Point const & rPoint) +{ + ::comphelper::OExternalLockGuard aGuard(this); + init(); + if (rPoint.X >= 0 + && rPoint.X < m_rView.GetWindow()->GetOutputSizePixel().Width() + && rPoint.Y >= 0 && rPoint.Y < m_nViewHeight) + { + ::sal_Int32 nOffset = m_nViewOffset + rPoint.Y; // XXX numeric overflow + ::sal_Int32 nPos = m_nViewOffset - m_nVisibleBeginOffset; + for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd; + ++aIt) + { + nPos += aIt->getHeight(); // XXX numeric overflow + if (nOffset < nPos) + return getAccessibleChild(aIt); + } + } + return nullptr; +} +void Document::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet ) +{ + VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet ); + if (!m_rView.IsReadOnly()) + rStateSet.AddState( css::accessibility::AccessibleStateType::EDITABLE ); +} + +void Document::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet ) +{ + if( getAccessibleParent()->getAccessibleContext()->getAccessibleRole() == css::accessibility::AccessibleRole::SCROLL_PANE ) + { + css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { getAccessibleParent() }; + rRelationSet.AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) ); + } + else + { + VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet); + } +} +// virtual +void SAL_CALL Document::disposing() +{ + m_aEngineListener.endListening(); + m_aViewListener.endListening(); + if (m_xParagraphs != nullptr) + disposeParagraphs(); + VCLXAccessibleComponent::disposing(); +} + +// virtual +void Document::Notify(::SfxBroadcaster &, ::SfxHint const & rHint) +{ + const TextHint* pTextHint = dynamic_cast<const TextHint*>(&rHint); + if (!pTextHint) + return; + + ::TextHint const & rTextHint = *pTextHint; + switch (rTextHint.GetId()) + { + case SfxHintId::TextParaInserted: + case SfxHintId::TextParaRemoved: + // SfxHintId::TextParaInserted and SfxHintId::TextParaRemoved are sent at + // "unsafe" times (when the text engine has not yet re-formatted its + // content), so that for example calling ::TextEngine::GetTextHeight + // from within the code that handles SfxHintId::TextParaInserted causes + // trouble within the text engine. Therefore, these hints are just + // buffered until a following ::TextEngine::FormatDoc causes a + // SfxHintId::TextFormatted to come in: + case SfxHintId::TextFormatPara: + // ::TextEngine::FormatDoc sends a sequence of + // SfxHintId::TextFormatParas, followed by an optional + // SfxHintId::TextHeightChanged, followed in all cases by one + // SfxHintId::TextFormatted. Only the SfxHintId::TextFormatParas contain + // the numbers of the affected paragraphs, but they are sent + // before the changes are applied. Therefore, SfxHintId::TextFormatParas + // are just buffered until another hint comes in: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + m_aParagraphNotifications.push(rTextHint); + break; + } + case SfxHintId::TextFormatted: + case SfxHintId::TextHeightChanged: + case SfxHintId::TextModified: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + handleParagraphNotifications(); + break; + } + case SfxHintId::TextViewScrolled: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + handleParagraphNotifications(); + + ::sal_Int32 nOffset = static_cast< ::sal_Int32 >( + m_rView.GetStartDocPos().Y()); + // XXX numeric overflow + if (nOffset != m_nViewOffset) + { + m_nViewOffset = nOffset; + + Paragraphs::iterator aOldVisibleBegin( + m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + + determineVisibleRange(); + + notifyVisibleRangeChanges(aOldVisibleBegin, + aOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case SfxHintId::TextViewSelectionChanged: + case SfxHintId::TextViewCaretChanged: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + if (m_aParagraphNotifications.empty()) + { + handleSelectionChangeNotification(); + } + else + { + // SfxHintId::TextViewSelectionChanged is sometimes sent at + // "unsafe" times (when the text engine has not yet re- + // formatted its content), so that for example calling + // ::TextEngine::GetTextHeight from within the code that + // handles a previous SfxHintId::TextParaInserted causes + // trouble within the text engine. Therefore, these + // hints are just buffered (along with + // SfxHintId::TextParaInserted/REMOVED/FORMATPARA) until a + // following ::TextEngine::FormatDoc causes a + // SfxHintId::TextFormatted to come in: + m_bSelectionChangedNotification = true; + } + break; + } + default: break; + } +} + +IMPL_LINK(Document, WindowEventHandler, ::VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowResize: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + + ::sal_Int32 nHeight = static_cast< ::sal_Int32 >( + m_rView.GetWindow()->GetOutputSizePixel().Height()); + // XXX numeric overflow + if (nHeight != m_nViewHeight) + { + m_nViewHeight = nHeight; + + Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + + determineVisibleRange(); + + notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case VclEventId::WindowGetFocus: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + //to enable the PARAGRAPH to get focus for multiline edit + ::sal_Int32 count = getAccessibleChildCount(); + bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1; + if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty) + { + Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused; + ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp)); + if (xParagraph.is()) + { + xParagraph->notifyEvent( + css::accessibility::AccessibleEventId:: + STATE_CHANGED, + css::uno::Any(), + css::uno::Any( + css::accessibility::AccessibleStateType:: + FOCUSED)); + } + } + break; + } + case VclEventId::WindowLoseFocus: + { + ::osl::MutexGuard aInternalGuard(GetMutex()); + if (!isAlive()) + break; + //to enable the PARAGRAPH to get focus for multiline edit + ::sal_Int32 count = getAccessibleChildCount(); + bool bEmpty = m_aFocused == m_aVisibleEnd && count == 1; + if ((m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) || bEmpty) + { + Paragraphs::iterator aTemp = bEmpty ? m_aVisibleBegin : m_aFocused; + ::rtl::Reference< Paragraph > xParagraph(getParagraph(aTemp)); + if (xParagraph.is()) + xParagraph->notifyEvent( + css::accessibility::AccessibleEventId:: + STATE_CHANGED, + css::uno::Any( + css::accessibility::AccessibleStateType:: + FOCUSED), + css::uno::Any()); + } + break; + } + default: break; + } +} + +void Document::init() +{ + if (m_xParagraphs != nullptr) + return; + + const ::sal_uInt32 nCount = m_rEngine.GetParagraphCount(); + m_xParagraphs.reset(new Paragraphs); + m_xParagraphs->reserve(static_cast< Paragraphs::size_type >(nCount)); + // numeric overflow is harmless here + for (::sal_uInt32 i = 0; i < nCount; ++i) + m_xParagraphs->push_back(ParagraphInfo(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(i)))); + // XXX numeric overflow + m_nViewOffset = static_cast< ::sal_Int32 >( + m_rView.GetStartDocPos().Y()); // XXX numeric overflow + m_nViewHeight = static_cast< ::sal_Int32 >( + m_rView.GetWindow()->GetOutputSizePixel().Height()); + // XXX numeric overflow + determineVisibleRange(); + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + m_aFocused = m_xParagraphs->end(); + m_bSelectionChangedNotification = false; + m_aEngineListener.startListening(m_rEngine); + m_aViewListener.startListening(*m_rView.GetWindow()); +} + +::rtl::Reference< Paragraph > +Document::getParagraph(Paragraphs::iterator const & rIt) +{ + return static_cast< Paragraph * >( + css::uno::Reference< css::accessibility::XAccessible >( + rIt->getParagraph()).get()); +} + +css::uno::Reference< css::accessibility::XAccessible > +Document::getAccessibleChild(Paragraphs::iterator const & rIt) +{ + css::uno::Reference< css::accessibility::XAccessible > xParagraph( + rIt->getParagraph()); + if (!xParagraph.is()) + { + xParagraph = new Paragraph(this, rIt - m_xParagraphs->begin()); + rIt->setParagraph(xParagraph); + } + return xParagraph; +} + +void Document::determineVisibleRange() +{ + Paragraphs::iterator const aEnd = m_xParagraphs->end(); + + m_aVisibleBegin = aEnd; + m_aVisibleEnd = aEnd; + m_nVisibleBeginOffset = 0; + + ::sal_Int32 nPos = 0; + for (Paragraphs::iterator aIt = m_xParagraphs->begin(); m_aVisibleEnd == aEnd && aIt != aEnd; ++aIt) + { + ::sal_Int32 const nOldPos = nPos; + nPos += aIt->getHeight(); // XXX numeric overflow + if (m_aVisibleBegin == aEnd) + { + if (nPos >= m_nViewOffset) + { + m_aVisibleBegin = aIt; + m_nVisibleBeginOffset = m_nViewOffset - nOldPos; + } + } + else + { + if (nPos >= m_nViewOffset + m_nViewHeight) // XXX numeric overflow + { + m_aVisibleEnd = aIt; + } + } + } + + SAL_WARN_IF( + !((m_aVisibleBegin == m_xParagraphs->end() && m_aVisibleEnd == m_xParagraphs->end() && m_nVisibleBeginOffset == 0) + || (m_aVisibleBegin < m_aVisibleEnd && m_nVisibleBeginOffset >= 0)), + "accessibility", + "invalid visible range"); +} + +void Document::notifyVisibleRangeChanges( + Paragraphs::iterator const & rOldVisibleBegin, + Paragraphs::iterator const & rOldVisibleEnd, + Paragraphs::iterator const & rInserted) +{ + // XXX Replace this code that determines which paragraphs have changed from + // invisible to visible or vice versa with a better algorithm. + for (Paragraphs::iterator aIt(rOldVisibleBegin); aIt != rOldVisibleEnd; + ++aIt) + { + if (aIt != rInserted + && (aIt < m_aVisibleBegin || aIt >= m_aVisibleEnd)) + NotifyAccessibleEvent( + css::accessibility::AccessibleEventId:: + CHILD, + css::uno::Any(getAccessibleChild(aIt)), + css::uno::Any()); + } + for (Paragraphs::iterator aIt(m_aVisibleBegin); aIt != m_aVisibleEnd; + ++aIt) + { + if (aIt == rInserted + || aIt < rOldVisibleBegin || aIt >= rOldVisibleEnd) + NotifyAccessibleEvent( + css::accessibility::AccessibleEventId:: + CHILD, + css::uno::Any(), + css::uno::Any(getAccessibleChild(aIt))); + } +} + +void +Document::changeParagraphText(::sal_uInt32 nNumber, ::sal_uInt16 nBegin, ::sal_uInt16 nEnd, + bool bCut, bool bPaste, + OUString const & rText) +{ + m_rView.SetSelection(::TextSelection(::TextPaM(nNumber, nBegin), + ::TextPaM(nNumber, nEnd))); + if (bCut) + m_rView.Cut(); + else if (nBegin != nEnd) + m_rView.DeleteSelected(); + if (bPaste) + m_rView.Paste(); + else if (!rText.isEmpty()) + m_rView.InsertText(rText); +} + +void Document::handleParagraphNotifications() +{ + while (!m_aParagraphNotifications.empty()) + { + ::TextHint aHint(m_aParagraphNotifications.front()); + m_aParagraphNotifications.pop(); + switch (aHint.GetId()) + { + case SfxHintId::TextParaInserted: + { + ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() ); + assert(n <= m_xParagraphs->size() && "bad SfxHintId::TextParaInserted event"); + + // Save the values of old iterators (the iterators themselves + // will get invalidated), and adjust the old values so that they + // reflect the insertion of the new paragraph: + Paragraphs::size_type nOldVisibleBegin + = m_aVisibleBegin - m_xParagraphs->begin(); + Paragraphs::size_type nOldVisibleEnd + = m_aVisibleEnd - m_xParagraphs->begin(); + Paragraphs::size_type nOldFocused + = m_aFocused - m_xParagraphs->begin(); + if (n <= nOldVisibleBegin) + ++nOldVisibleBegin; // XXX numeric overflow + if (n <= nOldVisibleEnd) + ++nOldVisibleEnd; // XXX numeric overflow + if (n <= nOldFocused) + ++nOldFocused; // XXX numeric overflow + if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionFirstPara) + ++m_nSelectionFirstPara; // XXX numeric overflow + if (sal::static_int_cast<sal_Int32>(n) <= m_nSelectionLastPara) + ++m_nSelectionLastPara; // XXX numeric overflow + + Paragraphs::iterator aIns( + m_xParagraphs->insert( + m_xParagraphs->begin() + n, + ParagraphInfo(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(n))))); + // XXX numeric overflow (2x) + + determineVisibleRange(); + m_aFocused = m_xParagraphs->begin() + nOldFocused; + + for (Paragraphs::iterator aIt(aIns);;) + { + ++aIt; + if (aIt == m_xParagraphs->end()) + break; + ::rtl::Reference< Paragraph > xParagraph( + getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->numberChanged(true); + } + + notifyVisibleRangeChanges( + m_xParagraphs->begin() + nOldVisibleBegin, + m_xParagraphs->begin() + nOldVisibleEnd, aIns); + break; + } + case SfxHintId::TextParaRemoved: + { + ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() ); + if (n == TEXT_PARA_ALL) + { + for (Paragraphs::iterator aIt(m_aVisibleBegin); + aIt != m_aVisibleEnd; ++aIt) + { + NotifyAccessibleEvent( + css::accessibility::AccessibleEventId:: + CHILD, + css::uno::Any(getAccessibleChild(aIt)), + css::uno::Any()); + } + disposeParagraphs(); + m_xParagraphs->clear(); + determineVisibleRange(); + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + m_aFocused = m_xParagraphs->end(); + } + else + { + assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextParaRemoved event"); + + Paragraphs::iterator aIt(m_xParagraphs->begin() + n); + // numeric overflow cannot occur + + // Save the values of old iterators (the iterators + // themselves will get invalidated), and adjust the old + // values so that they reflect the removal of the paragraph: + Paragraphs::size_type nOldVisibleBegin + = m_aVisibleBegin - m_xParagraphs->begin(); + Paragraphs::size_type nOldVisibleEnd + = m_aVisibleEnd - m_xParagraphs->begin(); + bool bWasVisible + = nOldVisibleBegin <= n && n < nOldVisibleEnd; + Paragraphs::size_type nOldFocused + = m_aFocused - m_xParagraphs->begin(); + bool bWasFocused = aIt == m_aFocused; + if (n < nOldVisibleBegin) + --nOldVisibleBegin; + if (n < nOldVisibleEnd) + --nOldVisibleEnd; + if (n < nOldFocused) + --nOldFocused; + if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionFirstPara) + --m_nSelectionFirstPara; + else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionFirstPara) + { + if (m_nSelectionFirstPara == m_nSelectionLastPara) + { + m_nSelectionFirstPara = -1; + m_nSelectionFirstPos = -1; + m_nSelectionLastPara = -1; + m_nSelectionLastPos = -1; + } + else + { + ++m_nSelectionFirstPara; + m_nSelectionFirstPos = 0; + } + } + if (sal::static_int_cast<sal_Int32>(n) < m_nSelectionLastPara) + --m_nSelectionLastPara; + else if (sal::static_int_cast<sal_Int32>(n) == m_nSelectionLastPara) + { + assert(m_nSelectionFirstPara < m_nSelectionLastPara && "logic error"); + --m_nSelectionLastPara; + m_nSelectionLastPos = 0x7FFFFFFF; + } + + css::uno::Reference< css::accessibility::XAccessible > + xStrong; + if (bWasVisible) + xStrong = getAccessibleChild(aIt); + css::uno::WeakReference< + css::accessibility::XAccessible > xWeak( + aIt->getParagraph()); + aIt = m_xParagraphs->erase(aIt); + + determineVisibleRange(); + m_aFocused = bWasFocused ? m_xParagraphs->end() + : m_xParagraphs->begin() + nOldFocused; + + for (; aIt != m_xParagraphs->end(); ++aIt) + { + ::rtl::Reference< Paragraph > xParagraph( + getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->numberChanged(false); + } + + if (bWasVisible) + NotifyAccessibleEvent( + css::accessibility::AccessibleEventId:: + CHILD, + css::uno::Any(xStrong), + css::uno::Any()); + + css::uno::Reference< css::lang::XComponent > xComponent( + xWeak.get(), css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + notifyVisibleRangeChanges( + m_xParagraphs->begin() + nOldVisibleBegin, + m_xParagraphs->begin() + nOldVisibleEnd, + m_xParagraphs->end()); + } + break; + } + case SfxHintId::TextFormatPara: + { + ::sal_uInt32 n = static_cast< ::sal_uInt32 >( aHint.GetValue() ); + assert(n < m_xParagraphs->size() && "Bad SfxHintId::TextFormatPara event"); + + (*m_xParagraphs)[static_cast< Paragraphs::size_type >(n)]. + changeHeight(static_cast< ::sal_Int32 >( + m_rEngine.GetTextHeight(n))); + // XXX numeric overflow + Paragraphs::iterator aOldVisibleBegin(m_aVisibleBegin); + Paragraphs::iterator aOldVisibleEnd(m_aVisibleEnd); + determineVisibleRange(); + notifyVisibleRangeChanges(aOldVisibleBegin, aOldVisibleEnd, + m_xParagraphs->end()); + + if (n < m_xParagraphs->size()) + { + Paragraphs::iterator aIt(m_xParagraphs->begin() + n); + ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->textChanged(); + } + break; + } + default: + SAL_WARN("accessibility", "bad buffered hint"); + break; + } + } + if (m_bSelectionChangedNotification) + { + m_bSelectionChangedNotification = false; + handleSelectionChangeNotification(); + } +} + +namespace +{ + +enum class SelChangeType +{ + None, // no change, or invalid + CaretMove, // neither old nor new have selection, and they are different + NoSelToSel, // old has no selection but new has selection + SelToNoSel, // old has selection but new has no selection + // both old and new have selections + NoParaChange, // only end index changed inside end para + EndParaNoMoreBehind, // end para was behind start, but now is same or ahead + AddedFollowingPara, // selection extended to following paragraph(s) + ExcludedPreviousPara, // selection shrunk excluding previous paragraph(s) + ExcludedFollowingPara, // selection shrunk excluding following paragraph(s) + AddedPreviousPara, // selection extended to previous paragraph(s) + EndParaBecameBehind // end para was ahead of start, but now is behind +}; + +SelChangeType getSelChangeType(const TextPaM& Os, const TextPaM& Oe, + const TextPaM& Ns, const TextPaM& Ne) +{ + if (Os == Oe) // no old selection + { + if (Ns == Ne) // no new selection: only caret moves + return Os != Ns ? SelChangeType::CaretMove : SelChangeType::None; + else // old has no selection but new has selection + return SelChangeType::NoSelToSel; + } + else if (Ns == Ne) // old has selection; no new selection + { + return SelChangeType::SelToNoSel; + } + else if (Os == Ns) // both old and new have selections, and their starts are same + { + const sal_Int32 Osp = Os.GetPara(), Oep = Oe.GetPara(); + const sal_Int32 Nsp = Ns.GetPara(), Nep = Ne.GetPara(); + if (Oep == Nep) // end of selection stays in the same paragraph + { + //Send text_selection_change event on Nep + return Oe.GetIndex() != Ne.GetIndex() ? SelChangeType::NoParaChange + : SelChangeType::None; + } + else if (Oep < Nep) // end of selection moved to a following paragraph + { + //all the following examples like 1,2->1,3 means that old start select para is 1, old end select para is 2, + // then press shift up, the new start select para is 1, new end select para is 3; + //for example, 1, 2 -> 1, 3; 4,1 -> 4, 7; 4,1 -> 4, 2; 4,4->4,5 + if (Nep >= Nsp) // new end para not behind start + { + // 1, 2 -> 1, 3; 4, 1 -> 4, 7; 4,4->4,5; + if (Oep < Osp) // old end was behind start + { + // 4,1 -> 4,7; 4,1 -> 4,4 + return SelChangeType::EndParaNoMoreBehind; + } + else // old end para wasn't behind start + { + // 1, 2 -> 1, 3; 4,4->4,5; + return SelChangeType::AddedFollowingPara; + } + } + else // new end para is still behind start + { + // 4,1 -> 4,2, + return SelChangeType::ExcludedPreviousPara; + } + } + else // Oep > Nep => end of selection moved to a previous paragraph + { + // 3,2 -> 3,1; 4,7 -> 4,1; 4, 7 -> 4,6; 4,4 -> 4,3 + if (Nep >= Nsp) // new end para is still not behind of start + { + // 4,7 ->4,6 + return SelChangeType::ExcludedFollowingPara; + } + else // new end para is behind start + { + // 3,2 -> 3,1, 4,7 -> 4,1; 4,4->4,3 + if (Oep <= Osp) // it was not ahead already + { + // 3,2 -> 3,1; 4,4->4,3 + return SelChangeType::AddedPreviousPara; + } + else // it was ahead previously + { + // 4,7 -> 4,1 + return SelChangeType::EndParaBecameBehind; + } + } + } + } + return SelChangeType::None; +} + +} // namespace + +void Document::sendEvent(::sal_Int32 start, ::sal_Int32 end, ::sal_Int16 nEventId) +{ + size_t nAvailDistance = std::distance(m_xParagraphs->begin(), m_aVisibleEnd); + + Paragraphs::iterator aEnd(m_xParagraphs->begin()); + size_t nEndDistance = std::min<size_t>(end + 1, nAvailDistance); + std::advance(aEnd, nEndDistance); + + Paragraphs::iterator aIt(m_xParagraphs->begin()); + size_t nStartDistance = std::min<size_t>(start, nAvailDistance); + std::advance(aIt, nStartDistance); + + while (aIt < aEnd) + { + ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt)); + if (xParagraph.is()) + xParagraph->notifyEvent( + nEventId, + css::uno::Any(), css::uno::Any()); + ++aIt; + } +} + +void Document::handleSelectionChangeNotification() +{ + ::TextSelection const & rSelection = m_rView.GetSelection(); + assert(rSelection.GetStart().GetPara() < m_xParagraphs->size() && + rSelection.GetEnd().GetPara() < m_xParagraphs->size() && + "bad SfxHintId::TextViewSelectionChanged event"); + ::sal_Int32 nNewFirstPara + = static_cast< ::sal_Int32 >(rSelection.GetStart().GetPara()); + ::sal_Int32 nNewFirstPos = rSelection.GetStart().GetIndex(); + // XXX numeric overflow + ::sal_Int32 nNewLastPara + = static_cast< ::sal_Int32 >(rSelection.GetEnd().GetPara()); + ::sal_Int32 nNewLastPos = rSelection.GetEnd().GetIndex(); + // XXX numeric overflow + + // Lose focus: + Paragraphs::iterator aIt(m_xParagraphs->begin() + nNewLastPara); + if (m_aFocused != m_xParagraphs->end() && m_aFocused != aIt + && m_aFocused >= m_aVisibleBegin && m_aFocused < m_aVisibleEnd) + { + ::rtl::Reference< Paragraph > xParagraph(getParagraph(m_aFocused)); + if (xParagraph.is()) + xParagraph->notifyEvent( + css::accessibility::AccessibleEventId:: + STATE_CHANGED, + css::uno::Any( + css::accessibility::AccessibleStateType::FOCUSED), + css::uno::Any()); + } + + // Gain focus and update cursor position: + if (aIt >= m_aVisibleBegin && aIt < m_aVisibleEnd + && (aIt != m_aFocused + || nNewLastPara != m_nSelectionLastPara + || nNewLastPos != m_nSelectionLastPos)) + { + ::rtl::Reference< Paragraph > xParagraph(getParagraph(aIt)); + if (xParagraph.is()) + { + //disable the first event when user types in empty field. + ::sal_Int32 count = getAccessibleChildCount(); + bool bEmpty = count > 1; + //if (aIt != m_aFocused) + if (aIt != m_aFocused && bEmpty) + xParagraph->notifyEvent( + css::accessibility::AccessibleEventId:: + STATE_CHANGED, + css::uno::Any(), + css::uno::Any( + css::accessibility::AccessibleStateType::FOCUSED)); + if (nNewLastPara != m_nSelectionLastPara + || nNewLastPos != m_nSelectionLastPos) + xParagraph->notifyEvent( + css::accessibility::AccessibleEventId:: + CARET_CHANGED, + css::uno::Any( ::sal_Int32 ( + nNewLastPara == m_nSelectionLastPara + ? m_nSelectionLastPos : 0)), + css::uno::Any(nNewLastPos)); + } + } + m_aFocused = aIt; + + if (m_nSelectionFirstPara != -1) + { + sal_Int32 nMin; + sal_Int32 nMax; + SelChangeType ret = getSelChangeType(TextPaM(m_nSelectionFirstPara, m_nSelectionFirstPos), + TextPaM(m_nSelectionLastPara, m_nSelectionLastPos), + rSelection.GetStart(), rSelection.GetEnd()); + switch (ret) + { + case SelChangeType::None: + //no event + break; + case SelChangeType::CaretMove: + //only caret moved, already handled in above + break; + case SelChangeType::NoSelToSel: + //old has no selection but new has selection + nMin = std::min(nNewFirstPara, nNewLastPara); + nMax = std::max(nNewFirstPara, nNewLastPara); + sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED); + sendEvent(nMin, nMax, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::SelToNoSel: + //old has selection but new has no selection. + nMin = std::min(m_nSelectionFirstPara, m_nSelectionLastPara); + nMax = std::max(m_nSelectionFirstPara, m_nSelectionLastPara); + sendEvent(nMin, nMax, css::accessibility::AccessibleEventId::SELECTION_CHANGED); + sendEvent(nMin, nMax, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::NoParaChange: + //Send text_selection_change event on Nep + sendEvent(nNewLastPara, nNewLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::EndParaNoMoreBehind: + // 4, 1 -> 4, 7; 4,1 -> 4,4 + sendEvent(m_nSelectionLastPara, m_nSelectionFirstPara - 1, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + sendEvent(nNewFirstPara + 1, nNewLastPara, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(m_nSelectionLastPara, nNewLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::AddedFollowingPara: + // 1, 2 -> 1, 4; 4,4->4,5; + sendEvent(m_nSelectionLastPara + 1, nNewLastPara, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(m_nSelectionLastPara, nNewLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::ExcludedPreviousPara: + // 4,1 -> 4,3, + sendEvent(m_nSelectionLastPara + 1, nNewLastPara, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(m_nSelectionLastPara, nNewLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::ExcludedFollowingPara: + // 4,7 ->4,5; + sendEvent(nNewLastPara + 1, m_nSelectionLastPara, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(nNewLastPara, m_nSelectionLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::AddedPreviousPara: + // 3,2 -> 3,1; 4,4->4,3 + sendEvent(nNewLastPara, m_nSelectionLastPara - 1, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(nNewLastPara, m_nSelectionLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + case SelChangeType::EndParaBecameBehind: + // 4,7 -> 4,1 + sendEvent(m_nSelectionFirstPara + 1, m_nSelectionLastPara, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + sendEvent(nNewLastPara, nNewFirstPara - 1, + css::accessibility::AccessibleEventId::SELECTION_CHANGED); + + sendEvent(nNewLastPara, m_nSelectionLastPara, + css::accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED); + break; + } + } + + m_nSelectionFirstPara = nNewFirstPara; + m_nSelectionFirstPos = nNewFirstPos; + m_nSelectionLastPara = nNewLastPara; + m_nSelectionLastPos = nNewLastPos; +} + +void Document::disposeParagraphs() +{ + for (auto const& paragraph : *m_xParagraphs) + { + css::uno::Reference< css::lang::XComponent > xComponent( + paragraph.getParagraph().get(), css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } +} + +// static +css::uno::Any Document::mapFontColor(::Color const & rColor) +{ + return css::uno::Any(rColor.GetRGBColor()); + // FIXME keep transparency? +} + +// static +::Color Document::mapFontColor(css::uno::Any const & rColor) +{ + ::Color nColor; + rColor >>= nColor; + return nColor; +} + +// static +css::uno::Any Document::mapFontWeight(::FontWeight nWeight) +{ + // Map from ::FontWeight to css::awt::FontWeight, depends on order of + // elements in ::FontWeight (vcl/vclenum.hxx): + static float const aWeight[] + = { css::awt::FontWeight::DONTKNOW, // WEIGHT_DONTKNOW + css::awt::FontWeight::THIN, // WEIGHT_THIN + css::awt::FontWeight::ULTRALIGHT, // WEIGHT_ULTRALIGHT + css::awt::FontWeight::LIGHT, // WEIGHT_LIGHT + css::awt::FontWeight::SEMILIGHT, // WEIGHT_SEMILIGHT + css::awt::FontWeight::NORMAL, // WEIGHT_NORMAL + css::awt::FontWeight::NORMAL, // WEIGHT_MEDIUM + css::awt::FontWeight::SEMIBOLD, // WEIGHT_SEMIBOLD + css::awt::FontWeight::BOLD, // WEIGHT_BOLD + css::awt::FontWeight::ULTRABOLD, // WEIGHT_ULTRABOLD + css::awt::FontWeight::BLACK }; // WEIGHT_BLACK + return css::uno::Any(aWeight[nWeight]); +} + +// static +::FontWeight Document::mapFontWeight(css::uno::Any const & rWeight) +{ + float nWeight = css::awt::FontWeight::NORMAL; + rWeight >>= nWeight; + return nWeight <= css::awt::FontWeight::DONTKNOW ? WEIGHT_DONTKNOW + : nWeight <= css::awt::FontWeight::THIN ? WEIGHT_THIN + : nWeight <= css::awt::FontWeight::ULTRALIGHT ? WEIGHT_ULTRALIGHT + : nWeight <= css::awt::FontWeight::LIGHT ? WEIGHT_LIGHT + : nWeight <= css::awt::FontWeight::SEMILIGHT ? WEIGHT_SEMILIGHT + : nWeight <= css::awt::FontWeight::NORMAL ? WEIGHT_NORMAL + : nWeight <= css::awt::FontWeight::SEMIBOLD ? WEIGHT_SEMIBOLD + : nWeight <= css::awt::FontWeight::BOLD ? WEIGHT_BOLD + : nWeight <= css::awt::FontWeight::ULTRABOLD ? WEIGHT_ULTRABOLD + : WEIGHT_BLACK; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |