diff options
Diffstat (limited to 'editeng/source/accessibility')
-rw-r--r-- | editeng/source/accessibility/AccessibleComponentBase.cxx | 149 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleContextBase.cxx | 527 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleEditableTextPara.cxx | 2710 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleHyperlink.cxx | 128 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleHyperlink.hxx | 64 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleImageBullet.cxx | 530 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleImageBullet.hxx | 202 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleParaManager.cxx | 403 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleSelectionBase.cxx | 91 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleStaticTextBase.cxx | 965 | ||||
-rw-r--r-- | editeng/source/accessibility/AccessibleStringWrap.cxx | 91 |
11 files changed, 5860 insertions, 0 deletions
diff --git a/editeng/source/accessibility/AccessibleComponentBase.cxx b/editeng/source/accessibility/AccessibleComponentBase.cxx new file mode 100644 index 000000000..5e95afbd2 --- /dev/null +++ b/editeng/source/accessibility/AccessibleComponentBase.cxx @@ -0,0 +1,149 @@ +/* -*- 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 <editeng/AccessibleComponentBase.hxx> + +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> + +#include <tools/color.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +// internal + +AccessibleComponentBase::AccessibleComponentBase() +{ +} + + +AccessibleComponentBase::~AccessibleComponentBase() +{ +} + +// XAccessibleComponent + +sal_Bool SAL_CALL AccessibleComponentBase::containsPoint ( + const css::awt::Point& aPoint) +{ + awt::Size aSize (getSize()); + return (aPoint.X >= 0) + && (aPoint.X < aSize.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aSize.Height); +} + + +uno::Reference<XAccessible > SAL_CALL + AccessibleComponentBase::getAccessibleAtPoint ( + const awt::Point& /*aPoint*/) +{ + return uno::Reference<XAccessible>(); +} + + +awt::Rectangle SAL_CALL AccessibleComponentBase::getBounds() +{ + return awt::Rectangle(); +} + + +awt::Point SAL_CALL AccessibleComponentBase::getLocation() +{ + awt::Rectangle aBBox (getBounds()); + return awt::Point (aBBox.X, aBBox.Y); +} + + +awt::Point SAL_CALL AccessibleComponentBase::getLocationOnScreen() +{ + return awt::Point(); +} + + +css::awt::Size SAL_CALL AccessibleComponentBase::getSize() +{ + awt::Rectangle aBBox (getBounds()); + return awt::Size (aBBox.Width, aBBox.Height); +} + + +void SAL_CALL AccessibleComponentBase::grabFocus() +{ + uno::Reference<XAccessibleContext> xContext (this, uno::UNO_QUERY); + uno::Reference<XAccessibleSelection> xSelection ( + xContext->getAccessibleParent(), uno::UNO_QUERY); + if (xSelection.is()) + { + // Do a single selection on this object. + xSelection->clearAccessibleSelection(); + xSelection->selectAccessibleChild (xContext->getAccessibleIndexInParent()); + } +} + + +sal_Int32 SAL_CALL AccessibleComponentBase::getForeground() +{ + return sal_Int32(COL_BLACK); +} + + +sal_Int32 SAL_CALL AccessibleComponentBase::getBackground() +{ + return sal_Int32(COL_WHITE); +} + + +// XAccessibleExtendedComponent + +css::uno::Reference< css::awt::XFont > SAL_CALL + AccessibleComponentBase::getFont() +{ + return uno::Reference<awt::XFont>(); +} + + +OUString SAL_CALL AccessibleComponentBase::getTitledBorderText() +{ + return OUString(); +} + + +OUString SAL_CALL AccessibleComponentBase::getToolTipText() +{ + return OUString(); +} + +// XTypeProvider + +uno::Sequence<uno::Type> + AccessibleComponentBase::getTypes() +{ + static const uno::Sequence aTypeList { + cppu::UnoType<XAccessibleComponent>::get(), + cppu::UnoType<XAccessibleExtendedComponent>::get() }; + return aTypeList; +} + + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleContextBase.cxx b/editeng/source/accessibility/AccessibleContextBase.cxx new file mode 100644 index 000000000..4cd16d735 --- /dev/null +++ b/editeng/source/accessibility/AccessibleContextBase.cxx @@ -0,0 +1,527 @@ +/* -*- 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 <editeng/AccessibleContextBase.hxx> + +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp> + +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/mutex.hxx> +#include <rtl/ref.hxx> + +#include <utility> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +// internal + +AccessibleContextBase::AccessibleContextBase ( + uno::Reference<XAccessible> xParent, + const sal_Int16 aRole) + : WeakComponentImplHelper(m_aMutex), + mxParent(std::move(xParent)), + meDescriptionOrigin(NotSet), + meNameOrigin(NotSet), + mnClientId(0), + maRole(aRole) +{ + // Create the state set. + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper (); + mxStateSet = pStateSet; + + // Set some states. Don't use the SetState method because no events + // shall be broadcasted (that is not yet initialized anyway). + pStateSet->AddState (AccessibleStateType::ENABLED); + pStateSet->AddState (AccessibleStateType::SENSITIVE); + pStateSet->AddState (AccessibleStateType::SHOWING); + pStateSet->AddState (AccessibleStateType::VISIBLE); + pStateSet->AddState (AccessibleStateType::FOCUSABLE); + pStateSet->AddState (AccessibleStateType::SELECTABLE); + + // Create the relation set. + mxRelationSet = new ::utl::AccessibleRelationSetHelper (); +} + +AccessibleContextBase::~AccessibleContextBase() +{ +} + +bool AccessibleContextBase::SetState (sal_Int16 aState) +{ + ::osl::ClearableMutexGuard aGuard (m_aMutex); + if ((mxStateSet != nullptr) && !mxStateSet->contains(aState)) + { + mxStateSet->AddState (aState); + // Clear the mutex guard so that it is not locked during calls to + // listeners. + aGuard.clear(); + + // Send event for all states except the DEFUNC state. + if (aState != AccessibleStateType::DEFUNC) + { + uno::Any aNewValue; + aNewValue <<= aState; + CommitChange( + AccessibleEventId::STATE_CHANGED, + aNewValue, + uno::Any()); + } + return true; + } + else + return false; +} + + +bool AccessibleContextBase::ResetState (sal_Int16 aState) +{ + ::osl::ClearableMutexGuard aGuard (m_aMutex); + if ((mxStateSet != nullptr) && mxStateSet->contains(aState)) + { + mxStateSet->RemoveState (aState); + // Clear the mutex guard so that it is not locked during calls to listeners. + aGuard.clear(); + + uno::Any aOldValue; + aOldValue <<= aState; + CommitChange( + AccessibleEventId::STATE_CHANGED, + uno::Any(), + aOldValue); + return true; + } + else + return false; +} + + +bool AccessibleContextBase::GetState (sal_Int16 aState) +{ + ::osl::MutexGuard aGuard (m_aMutex); + if (mxStateSet != nullptr) + return mxStateSet->contains(aState); + else + // If there is no state set then return false as a default value. + return false; +} + + +void AccessibleContextBase::SetRelationSet ( + const uno::Reference<XAccessibleRelationSet>& rxNewRelationSet) +{ + // Try to emit some meaningful events indicating differing relations in + // both sets. + typedef std::pair<short int,short int> RD; + const RD aRelationDescriptors[] = { + RD(AccessibleRelationType::CONTROLLED_BY, AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED), + RD(AccessibleRelationType::CONTROLLER_FOR, AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED), + RD(AccessibleRelationType::LABELED_BY, AccessibleEventId::LABELED_BY_RELATION_CHANGED), + RD(AccessibleRelationType::LABEL_FOR, AccessibleEventId::LABEL_FOR_RELATION_CHANGED), + RD(AccessibleRelationType::MEMBER_OF, AccessibleEventId::MEMBER_OF_RELATION_CHANGED), + RD(AccessibleRelationType::INVALID, -1), + }; + for (int i=0; aRelationDescriptors[i].first!=AccessibleRelationType::INVALID; i++) + if (mxRelationSet->containsRelation(aRelationDescriptors[i].first) + != rxNewRelationSet->containsRelation(aRelationDescriptors[i].first)) + CommitChange (aRelationDescriptors[i].second, uno::Any(), uno::Any()); + + mxRelationSet = rxNewRelationSet; +} + + +// XAccessible + +uno::Reference< XAccessibleContext> SAL_CALL + AccessibleContextBase::getAccessibleContext() +{ + return this; +} + + +// XAccessibleContext + +/** No children. +*/ +sal_Int32 SAL_CALL + AccessibleContextBase::getAccessibleChildCount() +{ + return 0; +} + + +/** Forward the request to the shape. Return the requested shape or throw + an exception for a wrong index. +*/ +uno::Reference<XAccessible> SAL_CALL + AccessibleContextBase::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + throw lang::IndexOutOfBoundsException ( + "no child with index " + OUString::number(nIndex), + nullptr); +} + + +uno::Reference<XAccessible> SAL_CALL + AccessibleContextBase::getAccessibleParent() +{ + ThrowIfDisposed (); + return mxParent; +} + + +sal_Int32 SAL_CALL + AccessibleContextBase::getAccessibleIndexInParent() +{ + ThrowIfDisposed (); + // Use a simple but slow solution for now. Optimize later. + + // Iterate over all the parent's children and search for this object. + if (!mxParent.is()) + // Return -1 to indicate that this object's parent does not know about the + // object. + return -1; + + uno::Reference<XAccessibleContext> xParentContext ( + mxParent->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount = xParentContext->getAccessibleChildCount(); + for (sal_Int32 i=0; i<nChildCount; i++) + { + uno::Reference<XAccessible> xChild (xParentContext->getAccessibleChild (i)); + if (xChild.is()) + { + uno::Reference<XAccessibleContext> xChildContext = xChild->getAccessibleContext(); + if (xChildContext == static_cast<XAccessibleContext*>(this)) + return i; + } + } + } + + // Return -1 to indicate that this object's parent does not know about the + // object. + return -1; +} + + +sal_Int16 SAL_CALL + AccessibleContextBase::getAccessibleRole() +{ + ThrowIfDisposed (); + return maRole; +} + + +OUString SAL_CALL + AccessibleContextBase::getAccessibleDescription() +{ + ThrowIfDisposed (); + + return msDescription; +} + + +OUString SAL_CALL + AccessibleContextBase::getAccessibleName() +{ + ThrowIfDisposed (); + + if (meNameOrigin == NotSet) + { + // Do not send an event because this is the first time it has been + // requested. + msName = CreateAccessibleName(); + meNameOrigin = AutomaticallyCreated; + } + + return msName; +} + + +/** Return a copy of the relation set. +*/ +uno::Reference<XAccessibleRelationSet> SAL_CALL + AccessibleContextBase::getAccessibleRelationSet() +{ + ThrowIfDisposed (); + + // Create a copy of the relation set and return it. + ::utl::AccessibleRelationSetHelper* pRelationSet = + static_cast< ::utl::AccessibleRelationSetHelper*>(mxRelationSet.get()); + if (pRelationSet != nullptr) + { + return uno::Reference<XAccessibleRelationSet> ( + new ::utl::AccessibleRelationSetHelper (*pRelationSet)); + } + else + return uno::Reference<XAccessibleRelationSet>(nullptr); +} + + +/** Return a copy of the state set. + Possible states are: + ENABLED + SHOWING + VISIBLE +*/ +uno::Reference<XAccessibleStateSet> SAL_CALL + AccessibleContextBase::getAccessibleStateSet() +{ + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet; + + if (rBHelper.bDisposed) + { + // We are already disposed! + // Create a new state set that has only set the DEFUNC state. + pStateSet = new ::utl::AccessibleStateSetHelper (); + pStateSet->AddState (AccessibleStateType::DEFUNC); + } + else + { + // Create a copy of the state set and return it. + pStateSet = mxStateSet; + + if (pStateSet != nullptr) + pStateSet = new ::utl::AccessibleStateSetHelper (*pStateSet); + } + + return pStateSet; +} + + +lang::Locale SAL_CALL + AccessibleContextBase::getLocale() +{ + ThrowIfDisposed (); + // Delegate request to parent. + if (mxParent.is()) + { + uno::Reference<XAccessibleContext> xParentContext ( + mxParent->getAccessibleContext()); + if (xParentContext.is()) + return xParentContext->getLocale (); + } + + // No locale and no parent. Therefore throw exception to indicate this + // cluelessness. + throw IllegalAccessibleComponentStateException (); +} + + +// XAccessibleEventListener + +void SAL_CALL AccessibleContextBase::addAccessibleEventListener ( + const uno::Reference<XAccessibleEventListener >& rxListener) +{ + if (!rxListener.is()) + return; + + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if (!mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( mnClientId, rxListener ); + } +} + + +void SAL_CALL AccessibleContextBase::removeAccessibleEventListener ( + const uno::Reference<XAccessibleEventListener >& rxListener ) +{ + ThrowIfDisposed (); + if (!(rxListener.is() && mnClientId)) + return; + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, 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 + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } +} + +// XServiceInfo +OUString SAL_CALL AccessibleContextBase::getImplementationName() +{ + return "AccessibleContextBase"; +} + +sal_Bool SAL_CALL AccessibleContextBase::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString > SAL_CALL + AccessibleContextBase::getSupportedServiceNames() +{ + return { + "com.sun.star.accessibility.Accessible", + "com.sun.star.accessibility.AccessibleContext"}; +} + + +// XTypeProvider + +uno::Sequence<sal_Int8> SAL_CALL + AccessibleContextBase::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// internal + +void SAL_CALL AccessibleContextBase::disposing() +{ + SetState (AccessibleStateType::DEFUNC); + + ::osl::MutexGuard aGuard (m_aMutex); + + // Send a disposing to all listeners. + if ( mnClientId ) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } +} + + +void AccessibleContextBase::SetAccessibleDescription ( + const OUString& rDescription, + StringOrigin eDescriptionOrigin) +{ + if (!(eDescriptionOrigin < meDescriptionOrigin + || (eDescriptionOrigin == meDescriptionOrigin && msDescription != rDescription))) + return; + + uno::Any aOldValue, aNewValue; + aOldValue <<= msDescription; + aNewValue <<= rDescription; + + msDescription = rDescription; + meDescriptionOrigin = eDescriptionOrigin; + + CommitChange( + AccessibleEventId::DESCRIPTION_CHANGED, + aNewValue, + aOldValue); +} + + +void AccessibleContextBase::SetAccessibleName ( + const OUString& rName, + StringOrigin eNameOrigin) +{ + if (!(eNameOrigin < meNameOrigin + || (eNameOrigin == meNameOrigin && msName != rName))) + return; + + uno::Any aOldValue, aNewValue; + aOldValue <<= msName; + aNewValue <<= rName; + + msName = rName; + meNameOrigin = eNameOrigin; + + CommitChange( + AccessibleEventId::NAME_CHANGED, + aNewValue, + aOldValue); +} + + +OUString AccessibleContextBase::CreateAccessibleName() +{ + return "Empty Name"; +} + + +void AccessibleContextBase::CommitChange ( + sal_Int16 nEventId, + const uno::Any& rNewValue, + const uno::Any& rOldValue) +{ + // Do not call FireEvent and do not even create the event object when no + // listener has been registered yet. Creating the event object can + // otherwise lead to a crash. See issue 93419 for details. + if (mnClientId != 0) + { + AccessibleEventObject aEvent ( + static_cast<XAccessibleContext*>(this), + nEventId, + rNewValue, + rOldValue); + + FireEvent (aEvent); + } +} + + +void AccessibleContextBase::FireEvent (const AccessibleEventObject& aEvent) +{ + if (mnClientId) + comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent ); +} + + +void AccessibleContextBase::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ("object has been already disposed", + static_cast<uno::XWeak*>(this)); + } +} + + +bool AccessibleContextBase::IsDisposed() const +{ + return (rBHelper.bDisposed || rBHelper.bInDispose); +} + + +void AccessibleContextBase::SetAccessibleRole( sal_Int16 _nRole ) +{ + maRole = _nRole; +} + + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleEditableTextPara.cxx b/editeng/source/accessibility/AccessibleEditableTextPara.cxx new file mode 100644 index 000000000..c6c6520cc --- /dev/null +++ b/editeng/source/accessibility/AccessibleEditableTextPara.cxx @@ -0,0 +1,2710 @@ +/* -*- 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 . + */ + + +// Global header + + +#include <algorithm> +#include <utility> +#include <vcl/svapp.hxx> +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <editeng/flditem.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/i18n/Boundary.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <unotools/accessiblerelationsethelper.hxx> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <vcl/unohelp.hxx> +#include <vcl/settings.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <editeng/editeng.hxx> +#include <editeng/unoprnms.hxx> +#include <editeng/unoipset.hxx> +#include <editeng/outliner.hxx> +#include <editeng/unoedprx.hxx> +#include <editeng/unoedsrc.hxx> +#include <svl/eitem.hxx> + + +// Project-local header + + +#include <com/sun/star/beans/PropertyState.hpp> + +#include <unopracc.hxx> +#include <editeng/AccessibleEditableTextPara.hxx> +#include "AccessibleHyperlink.hxx" +#include "AccessibleImageBullet.hxx" + +#include <svtools/colorcfg.hxx> +#include <editeng/editrids.hrc> +#include <editeng/eerdll.hxx> +#include <editeng/numitem.hxx> +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::accessibility; + + +// AccessibleEditableTextPara implementation + + +namespace accessibility +{ + static const SvxItemPropertySet* ImplGetSvxCharAndParaPropertiesSet() + { + // PropertyMap for character and paragraph properties + static const SfxItemPropertyMapEntry aPropMap[] = + { + SVX_UNOEDIT_OUTLINER_PROPERTIES, + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + SVX_UNOEDIT_NUMBERING_PROPERTY, + { u"TextUserDefinedAttributes", EE_CHAR_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aPropSet( aPropMap, EditEngine::GetGlobalItemPool() ); + return &aPropSet; + } + + // #i27138# - add parameter <_pParaManager> + AccessibleEditableTextPara::AccessibleEditableTextPara( + uno::Reference< XAccessible > xParent, + const AccessibleParaManager* _pParaManager ) + : mnParagraphIndex( 0 ), + mnIndexInParent( 0 ), + mpEditSource( nullptr ), + maEEOffset( 0, 0 ), + mxParent(std::move( xParent )), + // well, that's strictly (UNO) exception safe, though not + // really robust. We rely on the fact that this member is + // constructed last, and that the constructor body catches + // exceptions, thus no chance for exceptions once the Id is + // fetched. Nevertheless, normally should employ RAII here... + mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient()), + // #i27138# + mpParaManager( _pParaManager ) + { + + try + { + // Create the state set. + mxStateSet = new ::utl::AccessibleStateSetHelper (); + + // these are always on + mxStateSet->AddState( AccessibleStateType::MULTI_LINE ); + mxStateSet->AddState( AccessibleStateType::FOCUSABLE ); + mxStateSet->AddState( AccessibleStateType::VISIBLE ); + mxStateSet->AddState( AccessibleStateType::SHOWING ); + mxStateSet->AddState( AccessibleStateType::ENABLED ); + mxStateSet->AddState( AccessibleStateType::SENSITIVE ); + } + catch (const uno::Exception&) + { + } + } + + AccessibleEditableTextPara::~AccessibleEditableTextPara() + { + // sign off from event notifier + if( getNotifierClientId() != -1 ) + { + try + { + ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() ); + } + catch (const uno::Exception&) + { + } + } + } + + OUString AccessibleEditableTextPara::implGetText() + { + return GetTextRange( 0, GetTextLen() ); + } + + css::lang::Locale AccessibleEditableTextPara::implGetLocale() + { + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getLocale: paragraph index value overflow"); + + // return locale of first character in the paragraph + return LanguageTag(GetTextForwarder().GetLanguage( GetParagraphIndex(), 0 )).getLocale(); + } + + void AccessibleEditableTextPara::implGetSelection( sal_Int32& nStartIndex, sal_Int32& nEndIndex ) + { + sal_Int32 nStart, nEnd; + + if( GetSelection( nStart, nEnd ) ) + { + nStartIndex = nStart; + nEndIndex = nEnd; + } + else + { + // #102234# No exception, just set to 'invalid' + nStartIndex = -1; + nEndIndex = -1; + } + } + + void AccessibleEditableTextPara::implGetParagraphBoundary( const OUString& rText, css::i18n::Boundary& rBoundary, sal_Int32 /*nIndex*/ ) + { + SAL_INFO( "editeng", "AccessibleEditableTextPara::implGetParagraphBoundary: only a base implementation, ignoring the index" ); + + rBoundary.startPos = 0; + rBoundary.endPos = rText.getLength(); + } + + void AccessibleEditableTextPara::implGetLineBoundary( const OUString&, css::i18n::Boundary& rBoundary, sal_Int32 nIndex ) + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + const sal_Int32 nParaIndex = GetParagraphIndex(); + + DBG_ASSERT(nParaIndex >= 0, + "AccessibleEditableTextPara::implGetLineBoundary: paragraph index value overflow"); + + const sal_Int32 nTextLen = rCacheTF.GetTextLen( nParaIndex ); + + CheckPosition(nIndex); + + rBoundary.startPos = rBoundary.endPos = -1; + + const sal_Int32 nLineCount=rCacheTF.GetLineCount( nParaIndex ); + + if( nIndex == nTextLen ) + { + // #i17014# Special-casing one-behind-the-end character + if( nLineCount <= 1 ) + rBoundary.startPos = 0; + else + rBoundary.startPos = nTextLen - rCacheTF.GetLineLen( nParaIndex, + nLineCount-1 ); + + rBoundary.endPos = nTextLen; + } + else + { + // normal line search + sal_Int32 nLine; + sal_Int32 nCurIndex; + for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine ) + { + nCurIndex += rCacheTF.GetLineLen( nParaIndex, nLine); + + if( nCurIndex > nIndex ) + { + rBoundary.startPos = nCurIndex - rCacheTF.GetLineLen( nParaIndex, nLine); + rBoundary.endPos = nCurIndex; + break; + } + } + } + } + + + void AccessibleEditableTextPara::SetIndexInParent( sal_Int32 nIndex ) + { + mnIndexInParent = nIndex; + } + + + void AccessibleEditableTextPara::SetParagraphIndex( sal_Int32 nIndex ) + { + sal_Int32 nOldIndex = mnParagraphIndex; + + mnParagraphIndex = nIndex; + + auto aChild( maImageBullet.get() ); + if( aChild.is() ) + aChild->SetParagraphIndex(mnParagraphIndex); + + try + { + if( nOldIndex != nIndex ) + { + uno::Any aOldDesc; + uno::Any aOldName; + + try + { + aOldDesc <<= getAccessibleDescription(); + aOldName <<= getAccessibleName(); + } + catch (const uno::Exception&) // optional behaviour + { + } + // index and therefore description changed + FireEvent( AccessibleEventId::DESCRIPTION_CHANGED, uno::Any( getAccessibleDescription() ), aOldDesc ); + FireEvent( AccessibleEventId::NAME_CHANGED, uno::Any( getAccessibleName() ), aOldName ); + } + } + catch (const uno::Exception&) // optional behaviour + { + } + } + + + void AccessibleEditableTextPara::Dispose() + { + int nClientId( getNotifierClientId() ); + + // #108212# drop all references before notifying dispose + mxParent = nullptr; + mnNotifierClientId = -1; + mpEditSource = nullptr; + + // notify listeners + if( nClientId == -1 ) + return; + + try + { + uno::Reference < XAccessibleContext > xThis = getAccessibleContext(); + + // #106234# Delegate to EventNotifier + ::comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, xThis ); + } + catch (const uno::Exception&) + { + } + } + + void AccessibleEditableTextPara::SetEditSource( SvxEditSourceAdapter* pEditSource ) + { + auto aChild( maImageBullet.get() ); + if( aChild.is() ) + aChild->SetEditSource(pEditSource); + + if( !pEditSource ) + { + // going defunc + UnSetState( AccessibleStateType::SHOWING ); + UnSetState( AccessibleStateType::VISIBLE ); + SetState( AccessibleStateType::INVALID ); + SetState( AccessibleStateType::DEFUNC ); + + Dispose(); + } + mpEditSource = pEditSource; + // #108900# Init last text content + try + { + TextChanged(); + } + catch (const uno::RuntimeException&) + { + } + } + + ESelection AccessibleEditableTextPara::MakeSelection( sal_Int32 nStartEEIndex, sal_Int32 nEndEEIndex ) + { + // check overflow + DBG_ASSERT(nStartEEIndex >= 0 && + nEndEEIndex >= 0 && + GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::MakeSelection: index value overflow"); + + sal_Int32 nParaIndex = GetParagraphIndex(); + return ESelection(nParaIndex, nStartEEIndex, nParaIndex, nEndEEIndex); + } + + ESelection AccessibleEditableTextPara::MakeSelection( sal_Int32 nEEIndex ) + { + return MakeSelection( nEEIndex, nEEIndex+1 ); + } + + ESelection AccessibleEditableTextPara::MakeCursor( sal_Int32 nEEIndex ) + { + return MakeSelection( nEEIndex, nEEIndex ); + } + + void AccessibleEditableTextPara::CheckIndex( sal_Int32 nIndex ) + { + if( nIndex < 0 || nIndex >= getCharacterCount() ) + throw lang::IndexOutOfBoundsException("AccessibleEditableTextPara: character index out of bounds", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > (this) ) ); // disambiguate hierarchy + } + + void AccessibleEditableTextPara::CheckPosition( sal_Int32 nIndex ) + { + if( nIndex < 0 || nIndex > getCharacterCount() ) + throw lang::IndexOutOfBoundsException("AccessibleEditableTextPara: character position out of bounds", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > (this) ) ); // disambiguate hierarchy + } + + void AccessibleEditableTextPara::CheckRange( sal_Int32 nStart, sal_Int32 nEnd ) + { + CheckPosition( nStart ); + CheckPosition( nEnd ); + } + + bool AccessibleEditableTextPara::GetSelection(sal_Int32 &nStartPos, sal_Int32 &nEndPos) + { + ESelection aSelection; + sal_Int32 nPara = GetParagraphIndex(); + if( !GetEditViewForwarder().GetSelection( aSelection ) ) + return false; + + if( aSelection.nStartPara < aSelection.nEndPara ) + { + if( aSelection.nStartPara > nPara || + aSelection.nEndPara < nPara ) + return false; + + if( nPara == aSelection.nStartPara ) + nStartPos = aSelection.nStartPos; + else + nStartPos = 0; + + if( nPara == aSelection.nEndPara ) + nEndPos = aSelection.nEndPos; + else + nEndPos = GetTextLen(); + } + else + { + if( aSelection.nStartPara < nPara || + aSelection.nEndPara > nPara ) + return false; + + if( nPara == aSelection.nStartPara ) + nStartPos = aSelection.nStartPos; + else + nStartPos = GetTextLen(); + + if( nPara == aSelection.nEndPara ) + nEndPos = aSelection.nEndPos; + else + nEndPos = 0; + } + + return true; + } + + OUString AccessibleEditableTextPara::GetTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + return GetTextForwarder().GetText( MakeSelection(nStartIndex, nEndIndex) ); + } + + sal_Int32 AccessibleEditableTextPara::GetTextLen() const + { + return GetTextForwarder().GetTextLen(GetParagraphIndex()); + } + + SvxEditSourceAdapter& AccessibleEditableTextPara::GetEditSource() const + { + if( !mpEditSource ) + throw uno::RuntimeException("No edit source, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + return *mpEditSource; + } + + SvxAccessibleTextAdapter& AccessibleEditableTextPara::GetTextForwarder() const + { + SvxEditSourceAdapter& rEditSource = GetEditSource(); + SvxAccessibleTextAdapter* pTextForwarder = rEditSource.GetTextForwarderAdapter(); + + if( !pTextForwarder ) + throw uno::RuntimeException("Unable to fetch text forwarder, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + + if( !pTextForwarder->IsValid() ) + throw uno::RuntimeException("Text forwarder is invalid, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + return *pTextForwarder; + } + + SvxViewForwarder& AccessibleEditableTextPara::GetViewForwarder() const + { + SvxEditSource& rEditSource = GetEditSource(); + SvxViewForwarder* pViewForwarder = rEditSource.GetViewForwarder(); + + if( !pViewForwarder ) + { + throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + } + + if( !pViewForwarder->IsValid() ) + throw uno::RuntimeException("View forwarder is invalid, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + return *pViewForwarder; + } + + SvxAccessibleTextEditViewAdapter& AccessibleEditableTextPara::GetEditViewForwarder( bool bCreate ) const + { + SvxEditSourceAdapter& rEditSource = GetEditSource(); + SvxAccessibleTextEditViewAdapter* pTextEditViewForwarder = rEditSource.GetEditViewForwarderAdapter( bCreate ); + + if( !pTextEditViewForwarder ) + { + if( bCreate ) + throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + else + throw uno::RuntimeException("No view forwarder, object not in edit mode", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + } + + if( pTextEditViewForwarder->IsValid() ) + return *pTextEditViewForwarder; + else + { + if( bCreate ) + throw uno::RuntimeException("View forwarder is invalid, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + else + throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleEditableTextPara* > (this) ) ) ); // disambiguate hierarchy + } + } + + bool AccessibleEditableTextPara::HaveEditView() const + { + SvxEditSource& rEditSource = GetEditSource(); + SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder(); + + if( !pViewForwarder ) + return false; + + if( !pViewForwarder->IsValid() ) + return false; + + return true; + } + + bool AccessibleEditableTextPara::HaveChildren() + { + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::HaveChildren: paragraph index value overflow"); + + return GetTextForwarder().HaveImageBullet( GetParagraphIndex() ); + } + + tools::Rectangle AccessibleEditableTextPara::LogicToPixel( const tools::Rectangle& rRect, const MapMode& rMapMode, SvxViewForwarder const & rForwarder ) + { + // convert to screen coordinates + return tools::Rectangle( rForwarder.LogicToPixel( rRect.TopLeft(), rMapMode ), + rForwarder.LogicToPixel( rRect.BottomRight(), rMapMode ) ); + } + + + void AccessibleEditableTextPara::SetEEOffset( const Point& rOffset ) + { + auto aChild( maImageBullet.get() ); + if( aChild.is() ) + aChild->SetEEOffset(rOffset); + + maEEOffset = rOffset; + } + + void AccessibleEditableTextPara::FireEvent(const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue) const + { + uno::Reference < XAccessibleContext > xThis( const_cast< AccessibleEditableTextPara* > (this)->getAccessibleContext() ); + + AccessibleEventObject aEvent(xThis, nEventId, rNewValue, rOldValue); + + // #106234# Delegate to EventNotifier + if( getNotifierClientId() != -1 ) + ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), + aEvent ); + } + + void AccessibleEditableTextPara::SetState( const sal_Int16 nStateId ) + { + if( mxStateSet != nullptr && + !mxStateSet->contains(nStateId) ) + { + mxStateSet->AddState( nStateId ); + FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any( nStateId ) ); + } + } + + void AccessibleEditableTextPara::UnSetState( const sal_Int16 nStateId ) + { + if( mxStateSet != nullptr && + mxStateSet->contains(nStateId) ) + { + mxStateSet->RemoveState( nStateId ); + FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any( nStateId ) ); + } + } + + void AccessibleEditableTextPara::TextChanged() + { + OUString aCurrentString( implGetText() ); + uno::Any aDeleted; + uno::Any aInserted; + if( OCommonAccessibleText::implInitTextChangedEvent( maLastTextString, aCurrentString, + aDeleted, aInserted) ) + { + FireEvent( AccessibleEventId::TEXT_CHANGED, aInserted, aDeleted ); + maLastTextString = aCurrentString; + } + } + + bool AccessibleEditableTextPara::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nIndex ) + { + DBG_ASSERT(nIndex >= 0, + "AccessibleEditableTextPara::GetAttributeRun: index value overflow"); + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getLocale: paragraph index value overflow"); + + return GetTextForwarder().GetAttributeRun( nStartIndex, + nEndIndex, + GetParagraphIndex(), + nIndex ); + } + + uno::Any SAL_CALL AccessibleEditableTextPara::queryInterface (const uno::Type & rType) + { + uno::Any aRet; + + // must provide XAccessibleText by hand, since it comes publicly inherited by XAccessibleEditableText + if ( rType == cppu::UnoType<XAccessibleText>::get()) + { + uno::Reference< XAccessibleText > aAccText = static_cast< XAccessibleEditableText * >(this); + aRet <<= aAccText; + } + else if ( rType == cppu::UnoType<XAccessibleEditableText>::get()) + { + uno::Reference< XAccessibleEditableText > aAccEditText = this; + aRet <<= aAccEditText; + } + else if ( rType == cppu::UnoType<XAccessibleHypertext>::get()) + { + uno::Reference< XAccessibleHypertext > aAccHyperText = this; + aRet <<= aAccHyperText; + } + else + { + aRet = AccessibleTextParaInterfaceBase::queryInterface(rType); + } + + return aRet; + } + + // XAccessible + uno::Reference< XAccessibleContext > SAL_CALL AccessibleEditableTextPara::getAccessibleContext() + { + // We implement the XAccessibleContext interface in the same object + return uno::Reference< XAccessibleContext > ( this ); + } + + // XAccessibleContext + sal_Int32 SAL_CALL AccessibleEditableTextPara::getAccessibleChildCount() + { + SolarMutexGuard aGuard; + + return HaveChildren() ? 1 : 0; + } + + uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleChild( sal_Int32 i ) + { + SolarMutexGuard aGuard; + + if( !HaveChildren() ) + throw lang::IndexOutOfBoundsException("No children available", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > (this) ) ); // static_cast: disambiguate hierarchy + + if( i != 0 ) + throw lang::IndexOutOfBoundsException("Invalid child index", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > (this) ) ); // static_cast: disambiguate hierarchy + + auto aChild( maImageBullet.get() ); + + if( !aChild.is() ) + { + // there is no hard reference available, create object then + aChild = new AccessibleImageBullet(this); + + aChild->SetEditSource( &GetEditSource() ); + aChild->SetParagraphIndex( GetParagraphIndex() ); + aChild->SetIndexInParent( i ); + + maImageBullet = aChild; + } + + return aChild; + } + + uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleParent() + { + SAL_WARN_IF(!mxParent.is(), "editeng", "AccessibleEditableTextPara::getAccessibleParent: no frontend set, did somebody forgot to call AccessibleTextHelper::SetEventSource()?"); + + return mxParent; + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getAccessibleIndexInParent() + { + return mnIndexInParent; + } + + sal_Int16 SAL_CALL AccessibleEditableTextPara::getAccessibleRole() + { + return AccessibleRole::PARAGRAPH; + } + + OUString SAL_CALL AccessibleEditableTextPara::getAccessibleDescription() + { + SolarMutexGuard aGuard; + + // append first 40 characters from text, or first line, if shorter + // (writer takes first sentence here, but that's not supported + // from EditEngine) + // throws if defunc + OUString aLine; + + if( getCharacterCount() ) + aLine = getTextAtIndex(0, AccessibleTextType::LINE).SegmentText; + + // Get the string from the resource for the specified id. + OUString sStr(EditResId(RID_SVXSTR_A11Y_PARAGRAPH_DESCRIPTION)); + OUString sParaIndex = OUString::number(GetParagraphIndex()); + sStr = sStr.replaceFirst("$(ARG)", sParaIndex); + + if( aLine.getLength() > MaxDescriptionLen ) + { + OUString aCurrWord; + sal_Int32 i; + + // search backward from MaxDescriptionLen for previous word start + for( aCurrWord=getTextAtIndex(MaxDescriptionLen, AccessibleTextType::WORD).SegmentText, + i=MaxDescriptionLen, + aLine=OUString(); + i>=0; + --i ) + { + if( getTextAtIndex(i, AccessibleTextType::WORD).SegmentText != aCurrWord ) + { + if( i == 0 ) + // prevent completely empty string + aLine = getTextAtIndex(0, AccessibleTextType::WORD).SegmentText; + else + aLine = getTextRange(0, i); + } + } + } + + return sStr + aLine; + } + + OUString SAL_CALL AccessibleEditableTextPara::getAccessibleName() + { + //See tdf#101003 before implementing a body + return OUString(); + } + + uno::Reference< XAccessibleRelationSet > SAL_CALL AccessibleEditableTextPara::getAccessibleRelationSet() + { + // #i27138# - provide relations CONTENT_FLOWS_FROM + // and CONTENT_FLOWS_TO + if ( mpParaManager ) + { + rtl::Reference<utl::AccessibleRelationSetHelper> pAccRelSetHelper = + new utl::AccessibleRelationSetHelper(); + sal_Int32 nMyParaIndex( GetParagraphIndex() ); + // relation CONTENT_FLOWS_FROM + if ( nMyParaIndex > 0 && + mpParaManager->IsReferencable( nMyParaIndex - 1 ) ) + { + uno::Sequence<uno::Reference<XInterface> > aSequence + { static_cast<cppu::OWeakObject *>(mpParaManager->GetChild( nMyParaIndex - 1 ).first.get().get()) }; + AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_FROM, + aSequence ); + pAccRelSetHelper->AddRelation( aAccRel ); + } + + // relation CONTENT_FLOWS_TO + if ( (nMyParaIndex + 1) < mpParaManager->GetNum() && + mpParaManager->IsReferencable( nMyParaIndex + 1 ) ) + { + uno::Sequence<uno::Reference<XInterface> > aSequence + { static_cast<cppu::OWeakObject *>(mpParaManager->GetChild( nMyParaIndex + 1 ).first.get().get()) }; + AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_TO, + aSequence ); + pAccRelSetHelper->AddRelation( aAccRel ); + } + + return pAccRelSetHelper; + } + else + { + // no relations, therefore empty + return uno::Reference< XAccessibleRelationSet >(); + } + } + + static uno::Sequence< OUString > const & getAttributeNames() + { + static const uno::Sequence<OUString> aNames{ + "CharColor", + "CharContoured", + "CharEmphasis", + "CharEscapement", + "CharFontName", + "CharHeight", + "CharPosture", + "CharShadowed", + "CharStrikeout", + "CharCaseMap", + "CharUnderline", + "CharUnderlineColor", + "CharWeight", + "NumberingLevel", + "NumberingRules", + "ParaAdjust", + "ParaBottomMargin", + "ParaFirstLineIndent", + "ParaLeftMargin", + "ParaLineSpacing", + "ParaRightMargin", + "ParaTabStops"}; + + return aNames; + } + + namespace { + + struct IndexCompare + { + const PropertyValue* pValues; + explicit IndexCompare( const PropertyValue* pVals ) : pValues(pVals) {} + bool operator() ( sal_Int32 a, sal_Int32 b ) const + { + return pValues[a].Name < pValues[b].Name; + } + }; + + } +} + +namespace +{ + OUString GetFieldTypeNameFromField(EFieldInfo const &ree) + { + OUString strFldType; + sal_Int32 nFieldType = -1; + if (ree.pFieldItem) + { + // So we get a field, check its type now. + nFieldType = ree.pFieldItem->GetField()->GetClassId() ; + } + switch (nFieldType) + { + case text::textfield::Type::DATE: + { + const SvxDateField* pDateField = static_cast< const SvxDateField* >(ree.pFieldItem->GetField()); + if (pDateField) + { + if (pDateField->GetType() == SvxDateType::Fix) + strFldType = "date (fixed)"; + else if (pDateField->GetType() == SvxDateType::Var) + strFldType = "date (variable)"; + } + break; + } + case text::textfield::Type::PAGE: + strFldType = "page-number"; + break; + //support the sheet name & pages fields + case text::textfield::Type::PAGES: + strFldType = "page-count"; + break; + case text::textfield::Type::TABLE: + strFldType = "sheet-name"; + break; + //End + case text::textfield::Type::TIME: + strFldType = "time"; + break; + case text::textfield::Type::EXTENDED_TIME: + { + const SvxExtTimeField* pTimeField = static_cast< const SvxExtTimeField* >(ree.pFieldItem->GetField()); + if (pTimeField) + { + if (pTimeField->GetType() == SvxTimeType::Fix) + strFldType = "time (fixed)"; + else if (pTimeField->GetType() == SvxTimeType::Var) + strFldType = "time (variable)"; + } + break; + } + case text::textfield::Type::AUTHOR: + strFldType = "author"; + break; + case text::textfield::Type::EXTENDED_FILE: + case text::textfield::Type::DOCINFO_TITLE: + strFldType = "file name"; + break; + case text::textfield::Type::DOCINFO_CUSTOM: + strFldType = "custom document property"; + break; + default: + break; + } + return strFldType; + } +} + +namespace accessibility +{ + OUString AccessibleEditableTextPara::GetFieldTypeNameAtIndex(sal_Int32 nIndex) + { + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); + //For field object info + sal_Int32 nParaIndex = GetParagraphIndex(); + sal_Int32 nAllFieldLen = 0; + sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex); + for (sal_Int32 j = 0; j < nField; ++j) + { + EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j); + sal_Int32 reeBegin = ree.aPosition.nIndex + nAllFieldLen; + sal_Int32 reeEnd = reeBegin + ree.aCurrentText.getLength(); + nAllFieldLen += (ree.aCurrentText.getLength() - 1); + if (nIndex < reeBegin) + break; + if (nIndex < reeEnd) + return GetFieldTypeNameFromField(ree); + } + return OUString(); + } + + uno::Reference< XAccessibleStateSet > SAL_CALL AccessibleEditableTextPara::getAccessibleStateSet() + { + SolarMutexGuard aGuard; + + // Create a copy of the state set and return it. + + if( !mxStateSet ) + return uno::Reference<XAccessibleStateSet>(); + uno::Reference<XAccessibleStateSet> xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + if (xParentStates.is() && xParentStates->contains(AccessibleStateType::EDITABLE) ) + { + mxStateSet->AddState(AccessibleStateType::EDITABLE); + } + return uno::Reference<XAccessibleStateSet>( new ::utl::AccessibleStateSetHelper (*mxStateSet) ); + } + + lang::Locale SAL_CALL AccessibleEditableTextPara::getLocale() + { + SolarMutexGuard aGuard; + + return implGetLocale(); + } + + void SAL_CALL AccessibleEditableTextPara::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) + { + if( getNotifierClientId() != -1 ) + ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener ); + } + + void SAL_CALL AccessibleEditableTextPara::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) + { + if( getNotifierClientId() == -1 ) + return; + + const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), 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 + ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() ); + mnNotifierClientId = -1; + ::comphelper::AccessibleEventNotifier::revokeClient( nId ); + } + } + + // XAccessibleComponent + sal_Bool SAL_CALL AccessibleEditableTextPara::containsPoint( const awt::Point& aTmpPoint ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::contains: index value overflow"); + + awt::Rectangle aTmpRect = getBounds(); + tools::Rectangle aRect( Point(aTmpRect.X, aTmpRect.Y), Size(aTmpRect.Width, aTmpRect.Height) ); + Point aPoint( aTmpPoint.X, aTmpPoint.Y ); + + return aRect.Contains( aPoint ); + } + + uno::Reference< XAccessible > SAL_CALL AccessibleEditableTextPara::getAccessibleAtPoint( const awt::Point& _aPoint ) + { + SolarMutexGuard aGuard; + + if( HaveChildren() ) + { + // #103862# No longer need to make given position relative + Point aPoint( _aPoint.X, _aPoint.Y ); + + // respect EditEngine offset to surrounding shape/cell + aPoint -= GetEEOffset(); + + // convert to EditEngine coordinate system + SvxTextForwarder& rCacheTF = GetTextForwarder(); + Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) ); + + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex()); + + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && + aBulletInfo.bVisible && + aBulletInfo.nType == SVX_NUM_BITMAP ) + { + tools::Rectangle aRect = aBulletInfo.aBounds; + + if( aRect.Contains( aLogPoint ) ) + return getAccessibleChild(0); + } + } + + // no children at all, or none at given position + return uno::Reference< XAccessible >(); + } + + awt::Rectangle SAL_CALL AccessibleEditableTextPara::getBounds() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getBounds: index value overflow"); + + SvxTextForwarder& rCacheTF = GetTextForwarder(); + tools::Rectangle aRect = rCacheTF.GetParaBounds( GetParagraphIndex() ); + + // convert to screen coordinates + tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect, + rCacheTF.GetMapMode(), + GetViewForwarder() ); + + // offset from shape/cell + Point aOffset = GetEEOffset(); + + return awt::Rectangle( aScreenRect.Left() + aOffset.X(), + aScreenRect.Top() + aOffset.Y(), + aScreenRect.GetSize().Width(), + aScreenRect.GetSize().Height() ); + } + + awt::Point SAL_CALL AccessibleEditableTextPara::getLocation( ) + { + SolarMutexGuard aGuard; + + awt::Rectangle aRect = getBounds(); + + return awt::Point( aRect.X, aRect.Y ); + } + + awt::Point SAL_CALL AccessibleEditableTextPara::getLocationOnScreen( ) + { + SolarMutexGuard aGuard; + + // relate us to parent + uno::Reference< XAccessible > xParent = getAccessibleParent(); + if( xParent.is() ) + { + uno::Reference< XAccessibleComponent > xParentComponent( xParent, uno::UNO_QUERY ); + if( xParentComponent.is() ) + { + awt::Point aRefPoint = xParentComponent->getLocationOnScreen(); + awt::Point aPoint = getLocation(); + aPoint.X += aRefPoint.X; + aPoint.Y += aRefPoint.Y; + + return aPoint; + } + // #i88070# + // fallback to parent's <XAccessibleContext> instance + else + { + uno::Reference< XAccessibleContext > xParentContext = xParent->getAccessibleContext(); + if ( xParentContext.is() ) + { + uno::Reference< XAccessibleComponent > xParentContextComponent( xParentContext, uno::UNO_QUERY ); + if( xParentContextComponent.is() ) + { + awt::Point aRefPoint = xParentContextComponent->getLocationOnScreen(); + awt::Point aPoint = getLocation(); + aPoint.X += aRefPoint.X; + aPoint.Y += aRefPoint.Y; + + return aPoint; + } + } + } + } + + throw uno::RuntimeException("Cannot access parent", + uno::Reference< uno::XInterface > + ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy + } + + awt::Size SAL_CALL AccessibleEditableTextPara::getSize( ) + { + SolarMutexGuard aGuard; + + awt::Rectangle aRect = getBounds(); + + return awt::Size( aRect.Width, aRect.Height ); + } + + void SAL_CALL AccessibleEditableTextPara::grabFocus( ) + { + // set cursor to this paragraph + setSelection(0,0); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getForeground( ) + { + // #104444# Added to XAccessibleComponent interface + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getBackground( ) + { + // #104444# Added to XAccessibleComponent interface + Color aColor( Application::GetSettings().GetStyleSettings().GetWindowColor() ); + + // the background is transparent + aColor.SetAlpha(0); + + return static_cast<sal_Int32>( aColor ); + } + + // XAccessibleText + sal_Int32 SAL_CALL AccessibleEditableTextPara::getCaretPosition() + { + SolarMutexGuard aGuard; + + if( !HaveEditView() ) + return -1; + + ESelection aSelection; + if( GetEditViewForwarder().GetSelection( aSelection ) && + GetParagraphIndex() == aSelection.nEndPara ) + { + // caret is always nEndPara,nEndPos + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && + aBulletInfo.bVisible && + aBulletInfo.nType != SVX_NUM_BITMAP ) + { + sal_Int32 nBulletLen = aBulletInfo.aText.getLength(); + if( aSelection.nEndPos - nBulletLen >= 0 ) + return aSelection.nEndPos - nBulletLen; + } + return aSelection.nEndPos; + } + + // not within this paragraph + return -1; + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::setCaretPosition( sal_Int32 nIndex ) + { + return setSelection(nIndex, nIndex); + } + + sal_Unicode SAL_CALL AccessibleEditableTextPara::getCharacter( sal_Int32 nIndex ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getCharacter: index value overflow"); + + return OCommonAccessibleText::implGetCharacter( implGetText(), nIndex ); + } + + uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& rRequestedAttributes ) + { + SolarMutexGuard aGuard; + + //Skip the bullet range to ignore the bullet text + SvxTextForwarder& rCacheTF = GetTextForwarder(); + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex()); + if (aBulletInfo.bVisible) + nIndex += aBulletInfo.aText.getLength(); + CheckIndex(nIndex); // may throw IndexOutOfBoundsException + + bool bSupplementalMode = false; + uno::Sequence< OUString > aPropertyNames = rRequestedAttributes; + if (!aPropertyNames.hasElements()) + { + bSupplementalMode = true; + aPropertyNames = getAttributeNames(); + } + + // get default attributes... + ::comphelper::SequenceAsHashMap aPropHashMap( getDefaultAttributes( aPropertyNames ) ); + + // ... and override them with the direct attributes from the specific position + const uno::Sequence< beans::PropertyValue > aRunAttribs( getRunAttributes( nIndex, aPropertyNames ) ); + for (auto const& rRunAttrib : aRunAttribs) + { + aPropHashMap[ rRunAttrib.Name ] = rRunAttrib.Value; //!! should not only be the value !! + } + + // get resulting sequence + uno::Sequence< beans::PropertyValue > aRes; + aPropHashMap >> aRes; + + // since SequenceAsHashMap ignores property handles and property state + // we have to restore the property state here (property handles are + // of no use to the accessibility API). + for (beans::PropertyValue & rRes : asNonConstRange(aRes)) + { + bool bIsDirectVal = false; + for (auto const& rRunAttrib : aRunAttribs) + { + bIsDirectVal = rRes.Name == rRunAttrib.Name; + if (bIsDirectVal) + break; + } + rRes.Handle = -1; + rRes.State = bIsDirectVal ? PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE; + } + if( bSupplementalMode ) + { + _correctValues( aRes ); + // NumberingPrefix + sal_Int32 nRes = aRes.getLength(); + aRes.realloc( nRes + 1 ); + beans::PropertyValue &rRes = aRes.getArray()[nRes]; + rRes.Name = "NumberingPrefix"; + OUString numStr; + if (aBulletInfo.nType != SVX_NUM_CHAR_SPECIAL && aBulletInfo.nType != SVX_NUM_BITMAP) + numStr = aBulletInfo.aText; + rRes.Value <<= numStr; + rRes.Handle = -1; + rRes.State = PropertyState_DIRECT_VALUE; + //For field object. + OUString strFieldType = GetFieldTypeNameAtIndex(nIndex); + if (!strFieldType.isEmpty()) + { + nRes = aRes.getLength(); + aRes.realloc( nRes + 1 ); + beans::PropertyValue &rResField = aRes.getArray()[nRes]; + rResField.Name = "FieldType"; + rResField.Value <<= strFieldType.toAsciiLowerCase(); + rResField.Handle = -1; + rResField.State = PropertyState_DIRECT_VALUE; + } + //sort property values + // build sorted index array + sal_Int32 nLength = aRes.getLength(); + const beans::PropertyValue* pPairs = aRes.getConstArray(); + std::unique_ptr<sal_Int32[]> pIndices(new sal_Int32[nLength]); + sal_Int32 i = 0; + for( i = 0; i < nLength; i++ ) + pIndices[i] = i; + std::sort( &pIndices[0], &pIndices[nLength], IndexCompare(pPairs) ); + // create sorted sequences according to index array + uno::Sequence<beans::PropertyValue> aNewValues( nLength ); + beans::PropertyValue* pNewValues = aNewValues.getArray(); + for( i = 0; i < nLength; i++ ) + { + pNewValues[i] = pPairs[pIndices[i]]; + } + + return aNewValues; + } + return aRes; + } + + awt::Rectangle SAL_CALL AccessibleEditableTextPara::getCharacterBounds( sal_Int32 nIndex ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getCharacterBounds: index value overflow"); + + // #108900# Have position semantics now for nIndex, as + // one-past-the-end values are legal, too. + CheckPosition( nIndex ); + + SvxTextForwarder& rCacheTF = GetTextForwarder(); + tools::Rectangle aRect = rCacheTF.GetCharBounds(GetParagraphIndex(), nIndex); + + // convert to screen + tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect, + rCacheTF.GetMapMode(), + GetViewForwarder() ); + // #109864# offset from parent (paragraph), but in screen + // coordinates. This makes sure the internal text offset in + // the outline view forwarder gets cancelled out here + awt::Rectangle aParaRect( getBounds() ); + aScreenRect.Move( -aParaRect.X, -aParaRect.Y ); + + // offset from shape/cell + Point aOffset = GetEEOffset(); + + return awt::Rectangle( aScreenRect.Left() + aOffset.X(), + aScreenRect.Top() + aOffset.Y(), + aScreenRect.GetSize().Width(), + aScreenRect.GetSize().Height() ); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getCharacterCount() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getCharacterCount: index value overflow"); + + return implGetText().getLength(); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getIndexAtPoint( const awt::Point& rPoint ) + { + SolarMutexGuard aGuard; + + sal_Int32 nPara; + sal_Int32 nIndex; + + // offset from surrounding cell/shape + Point aOffset( GetEEOffset() ); + Point aPoint( rPoint.X - aOffset.X(), rPoint.Y - aOffset.Y() ); + + // convert to logical coordinates + SvxTextForwarder& rCacheTF = GetTextForwarder(); + Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) ); + + // re-offset to parent (paragraph) + tools::Rectangle aParaRect = rCacheTF.GetParaBounds( GetParagraphIndex() ); + aLogPoint.Move( aParaRect.Left(), aParaRect.Top() ); + + if( rCacheTF.GetIndexAtPoint( aLogPoint, nPara, nIndex ) && + GetParagraphIndex() == nPara ) + { + // #102259# Double-check if we're _really_ on the given character + try + { + awt::Rectangle aRect1( getCharacterBounds(nIndex) ); + tools::Rectangle aRect2( aRect1.X, aRect1.Y, + aRect1.Width + aRect1.X, aRect1.Height + aRect1.Y ); + if( aRect2.Contains( Point( rPoint.X, rPoint.Y ) ) ) + return nIndex; + else + return -1; + } + catch (const lang::IndexOutOfBoundsException&) + { + // #103927# Don't throw for invalid nIndex values + return -1; + } + } + else + { + // not within our paragraph + return -1; + } + } + + OUString SAL_CALL AccessibleEditableTextPara::getSelectedText() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getSelectedText: index value overflow"); + + if( !HaveEditView() ) + return OUString(); + + return OCommonAccessibleText::getSelectedText(); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getSelectionStart() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getSelectionStart: index value overflow"); + + if( !HaveEditView() ) + return -1; + + return OCommonAccessibleText::getSelectionStart(); + } + + sal_Int32 SAL_CALL AccessibleEditableTextPara::getSelectionEnd() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getSelectionEnd: index value overflow"); + + if( !HaveEditView() ) + return -1; + + return OCommonAccessibleText::getSelectionEnd(); + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::setSelection: paragraph index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + try + { + SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true ); + return rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) ); + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + OUString SAL_CALL AccessibleEditableTextPara::getText() + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getText: paragraph index value overflow"); + + return implGetText(); + } + + OUString SAL_CALL AccessibleEditableTextPara::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getTextRange: paragraph index value overflow"); + + return OCommonAccessibleText::implGetTextRange(implGetText(), nStartIndex, nEndIndex); + } + + void AccessibleEditableTextPara::_correctValues( uno::Sequence< PropertyValue >& rValues) + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + sal_Int32 nRes = rValues.getLength(); + beans::PropertyValue *pRes = rValues.getArray(); + for (sal_Int32 i = 0; i < nRes; ++i) + { + beans::PropertyValue &rRes = pRes[i]; + // Char color + if (rRes.Name == "CharColor") + { + uno::Any &anyChar = rRes.Value; + Color crChar(ColorTransparency, static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved))); + if (COL_AUTO == crChar ) + { + uno::Reference< css::accessibility::XAccessibleComponent > xComponent(mxParent,uno::UNO_QUERY); + if (xComponent.is()) + { + uno::Reference< css::accessibility::XAccessibleContext > xContext(xComponent,uno::UNO_QUERY); + if (xContext->getAccessibleRole() == AccessibleRole::SHAPE + || xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL) + { + anyChar <<= COL_BLACK; + } + else + { + Color cr(ColorTransparency, xComponent->getBackground()); + crChar = cr.IsDark() ? COL_WHITE : COL_BLACK; + anyChar <<= crChar; + } + } + } + continue; + } + // Underline + if (rRes.Name == "CharUnderline") + { + continue; + } + // Underline color && Mis-spell + if (rRes.Name == "CharUnderlineColor") + { + uno::Any &anyCharUnderLine = rRes.Value; + Color crCharUnderLine(ColorTransparency, static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>( anyCharUnderLine.pReserved))); + if (COL_AUTO == crCharUnderLine ) + { + uno::Reference< css::accessibility::XAccessibleComponent > xComponent(mxParent,uno::UNO_QUERY); + if (xComponent.is()) + { + uno::Reference< css::accessibility::XAccessibleContext > xContext(xComponent,uno::UNO_QUERY); + if (xContext->getAccessibleRole() == AccessibleRole::SHAPE + || xContext->getAccessibleRole() == AccessibleRole::TABLE_CELL) + { + anyCharUnderLine <<= COL_BLACK; + } + else + { + Color cr(ColorTransparency, xComponent->getBackground()); + crCharUnderLine = cr.IsDark() ? COL_WHITE : COL_BLACK; + anyCharUnderLine <<= crCharUnderLine; + } + } + } + continue; + } + // NumberingLevel + if (rRes.Name == "NumberingLevel") + { + const SvxNumBulletItem& rNumBullet = rCacheTF.GetParaAttribs(GetParagraphIndex()).Get(EE_PARA_NUMBULLET); + if(rNumBullet.GetNumRule().GetLevelCount()==0) + { + rRes.Value <<= sal_Int16(-1); + rRes.Handle = -1; + rRes.State = PropertyState_DIRECT_VALUE; + } + else + { +// SvxAccessibleTextPropertySet aPropSet( &GetEditSource(), +// ImplGetSvxCharAndParaPropertiesMap() ); + // MT IA2 TODO: Check if this is the correct replacement for ImplGetSvxCharAndParaPropertiesMap + rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), ImplGetSvxTextPortionSvxPropertySet() ) ); + + xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) ); + rRes.Value = xPropSet->_getPropertyValue( rRes.Name, mnParagraphIndex ); + rRes.State = xPropSet->_getPropertyState( rRes.Name, mnParagraphIndex ); + rRes.Handle = -1; + } + continue; + } + // NumberingRules + if (rRes.Name == "NumberingRules") + { + SfxItemSet aAttribs = rCacheTF.GetParaAttribs(GetParagraphIndex()); + bool bVis = aAttribs.Get( EE_PARA_BULLETSTATE ).GetValue(); + if(bVis) + { + rRes.Value <<= sal_Int16(-1); + rRes.Handle = -1; + rRes.State = PropertyState_DIRECT_VALUE; + } + else + { + // MT IA2 TODO: Check if this is the correct replacement for ImplGetSvxCharAndParaPropertiesMap + rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), ImplGetSvxTextPortionSvxPropertySet() ) ); + xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) ); + rRes.Value = xPropSet->_getPropertyValue( rRes.Name, mnParagraphIndex ); + rRes.State = xPropSet->_getPropertyState( rRes.Name, mnParagraphIndex ); + rRes.Handle = -1; + } + continue; + } + } + } + sal_Int32 AccessibleEditableTextPara::SkipField(sal_Int32 nIndex, bool bForward) + { + sal_Int32 nParaIndex = GetParagraphIndex(); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); + sal_Int32 nAllFieldLen = 0; + sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex), nFoundFieldIndex = -1; + sal_Int32 reeBegin=0, reeEnd=0; + for (sal_Int32 j = 0; j < nField; ++j) + { + EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j); + reeBegin = ree.aPosition.nIndex + nAllFieldLen; + reeEnd = reeBegin + ree.aCurrentText.getLength(); + nAllFieldLen += (ree.aCurrentText.getLength() - 1); + if (nIndex < reeBegin) + break; + if (!ree.pFieldItem) + continue; + if (nIndex < reeEnd) + { + if (ree.pFieldItem->GetField()->GetClassId() != text::textfield::Type::URL) + { + nFoundFieldIndex = j; + break; + } + } + } + if( nFoundFieldIndex >= 0 ) + { + if( bForward ) + return reeEnd - 1; + else + return reeBegin; + } + return nIndex; + } + void AccessibleEditableTextPara::ExtendByField( css::accessibility::TextSegment& Segment ) + { + sal_Int32 nParaIndex = GetParagraphIndex(); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); + sal_Int32 nAllFieldLen = 0; + sal_Int32 nField = rCacheTF.GetFieldCount(nParaIndex), nFoundFieldIndex = -1; + sal_Int32 reeBegin=0, reeEnd=0; + for (sal_Int32 j = 0; j < nField; ++j) + { + EFieldInfo ree = rCacheTF.GetFieldInfo(nParaIndex, j); + reeBegin = ree.aPosition.nIndex + nAllFieldLen; + reeEnd = reeBegin + ree.aCurrentText.getLength(); + nAllFieldLen += (ree.aCurrentText.getLength() - 1); + if( reeBegin > Segment.SegmentEnd ) + { + break; + } + if (!ree.pFieldItem) + continue; + if( (Segment.SegmentEnd > reeBegin && Segment.SegmentEnd <= reeEnd) || + (Segment.SegmentStart >= reeBegin && Segment.SegmentStart < reeEnd) ) + { + if(ree.pFieldItem->GetField()->GetClassId() != text::textfield::Type::URL) + { + nFoundFieldIndex = j; + break; + } + } + } + if( nFoundFieldIndex < 0 ) + return; + + bool bExtend = false; + if( Segment.SegmentEnd < reeEnd ) + { + Segment.SegmentEnd = reeEnd; + bExtend = true; + } + if( Segment.SegmentStart > reeBegin ) + { + Segment.SegmentStart = reeBegin; + bExtend = true; + } + if( !bExtend ) + return; + + //If there is a bullet before the field, should add the bullet length into the segment. + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex); + sal_Int32 nBulletLen = aBulletInfo.aText.getLength(); + if (nBulletLen > 0) + { + Segment.SegmentEnd += nBulletLen; + if (nFoundFieldIndex > 0) + Segment.SegmentStart += nBulletLen; + Segment.SegmentText = GetTextRange(Segment.SegmentStart, Segment.SegmentEnd); + //After get the correct field name, should restore the offset value which don't contain the bullet. + Segment.SegmentEnd -= nBulletLen; + if (nFoundFieldIndex > 0) + Segment.SegmentStart -= nBulletLen; + } + else + Segment.SegmentText = GetTextRange(Segment.SegmentStart, Segment.SegmentEnd); + } + + css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getTextAtIndex: paragraph index value overflow"); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + switch( aTextType ) + { + case AccessibleTextType::CHARACTER: + case AccessibleTextType::WORD: + { + aResult = OCommonAccessibleText::getTextAtIndex( nIndex, aTextType ); + ExtendByField( aResult ); + break; + } + // Not yet handled by OCommonAccessibleText. Missing + // implGetAttributeRunBoundary() method there + case AccessibleTextType::ATTRIBUTE_RUN: + { + const sal_Int32 nTextLen = GetTextForwarder().GetTextLen( GetParagraphIndex() ); + + if( nIndex == nTextLen ) + { + // #i17014# Special-casing one-behind-the-end character + aResult.SegmentStart = aResult.SegmentEnd = nTextLen; + } + else + { + sal_Int32 nStartIndex, nEndIndex; + //For the bullet paragraph, the bullet string is ignored for IAText::attributes() function. + SvxTextForwarder& rCacheTF = GetTextForwarder(); + // MT IA2: Not used? sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(GetParagraphIndex()); + if (aBulletInfo.bVisible) + nIndex += aBulletInfo.aText.getLength(); + if (nIndex != 0 && nIndex >= getCharacterCount()) + nIndex = getCharacterCount()-1; + CheckPosition(nIndex); + if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) ) + { + aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex); + if (aBulletInfo.bVisible) + { + nStartIndex -= aBulletInfo.aText.getLength(); + nEndIndex -= aBulletInfo.aText.getLength(); + } + aResult.SegmentStart = nStartIndex; + aResult.SegmentEnd = nEndIndex; + } + } + break; + } + case AccessibleTextType::LINE: + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + sal_Int32 nParaIndex = GetParagraphIndex(); + CheckPosition(nIndex); + if (nIndex != 0 && nIndex == getCharacterCount()) + --nIndex; + sal_Int32 nLine, nLineCount=rCacheTF.GetLineCount( nParaIndex ); + sal_Int32 nCurIndex; + //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line, + //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed + //by the IAText::attributes(). So here must do special support for bullet line. + sal_Int32 nBulletLen = 0; + for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine ) + { + if (nLine == 0) + { + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo( nParaIndex ); + if (aBulletInfo.bVisible) + { + //in bullet or numbering; + nBulletLen = aBulletInfo.aText.getLength(); + } + } + sal_Int32 nLineLen = rCacheTF.GetLineLen(nParaIndex, nLine); + if (nLine == 0) + nCurIndex += nLineLen - nBulletLen; + else + nCurIndex += nLineLen; + if( nCurIndex > nIndex ) + { + if (nLine ==0) + { + aResult.SegmentStart = 0; + aResult.SegmentEnd = nCurIndex; + aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd + nBulletLen); + break; + } + else + { + aResult.SegmentStart = nCurIndex - nLineLen; + aResult.SegmentEnd = nCurIndex; + //aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd ); + aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen); + break; + } + } + } + break; + } + default: + aResult = OCommonAccessibleText::getTextAtIndex( nIndex, aTextType ); + break; + } /* end of switch( aTextType ) */ + + return aResult; + } + + css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getTextBeforeIndex: paragraph index value overflow"); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + i18n::Boundary aBoundary; + switch( aTextType ) + { + // Not yet handled by OCommonAccessibleText. Missing + // implGetAttributeRunBoundary() method there + case AccessibleTextType::ATTRIBUTE_RUN: + { + const sal_Int32 nTextLen = GetTextForwarder().GetTextLen( GetParagraphIndex() ); + sal_Int32 nStartIndex, nEndIndex; + + if( nIndex == nTextLen ) + { + // #i17014# Special-casing one-behind-the-end character + if( nIndex > 0 && + GetAttributeRun(nStartIndex, nEndIndex, nIndex-1) ) + { + aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex); + aResult.SegmentStart = nStartIndex; + aResult.SegmentEnd = nEndIndex; + } + } + else + { + if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) ) + { + // already at the left border? If not, query + // one index further left + if( nStartIndex > 0 && + GetAttributeRun(nStartIndex, nEndIndex, nStartIndex-1) ) + { + aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex); + aResult.SegmentStart = nStartIndex; + aResult.SegmentEnd = nEndIndex; + } + } + } + break; + } + case AccessibleTextType::LINE: + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + sal_Int32 nParaIndex = GetParagraphIndex(); + + CheckPosition(nIndex); + + sal_Int32 nLine, nLineCount=rCacheTF.GetLineCount( nParaIndex ); + //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line, + //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed + //by the IAText::attributes(). So here must do special support for bullet line. + sal_Int32 nCurIndex=0, nLastIndex=0, nCurLineLen=0; + sal_Int32 nLastLineLen = 0, nBulletLen = 0; + // get the line before the line the index points into + for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine ) + { + nLastIndex = nCurIndex; + if (nLine == 0) + { + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex); + if (aBulletInfo.bVisible) + { + //in bullet or numbering; + nBulletLen = aBulletInfo.aText.getLength(); + } + } + if (nLine == 1) + nLastLineLen = nCurLineLen - nBulletLen; + else + nLastLineLen = nCurLineLen; + nCurLineLen = rCacheTF.GetLineLen( nParaIndex, nLine); + //nCurIndex += nCurLineLen; + if (nLine == 0) + nCurIndex += nCurLineLen - nBulletLen; + else + nCurIndex += nCurLineLen; + + //if( nCurIndex > nIndex && + //nLastIndex > nCurLineLen ) + if (nCurIndex > nIndex) + { + if (nLine == 0) + { + break; + } + else if (nLine == 1) + { + aResult.SegmentStart = 0; + aResult.SegmentEnd = nLastIndex; + aResult.SegmentText = GetTextRange( aResult.SegmentStart, aResult.SegmentEnd + nBulletLen); + break; + } + else + { + //aResult.SegmentStart = nLastIndex - nCurLineLen; + aResult.SegmentStart = nLastIndex - nLastLineLen; + aResult.SegmentEnd = nLastIndex; + aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen); + break; + } + } + } + + break; + } + case AccessibleTextType::WORD: + { + nIndex = SkipField( nIndex, false); + OUString sText( implGetText() ); + sal_Int32 nLength = sText.getLength(); + + // get word at index + implGetWordBoundary( sText, aBoundary, nIndex ); + + + //sal_Int32 curWordStart = aBoundary.startPos; + //sal_Int32 preWordStart = curWordStart; + sal_Int32 curWordStart , preWordStart; + if( aBoundary.startPos == -1 || aBoundary.startPos > nIndex) + curWordStart = preWordStart = nIndex; + else + curWordStart = preWordStart = aBoundary.startPos; + + // get previous word + + bool bWord = false; + + //while ( preWordStart > 0 && aBoundary.startPos == curWordStart) + while ( (preWordStart >= 0 && !bWord ) || ( aBoundary.endPos > curWordStart ) ) + { + preWordStart--; + bWord = implGetWordBoundary( sText, aBoundary, preWordStart ); + } + if ( bWord && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + ExtendByField( aResult ); + } + } + break; + case AccessibleTextType::CHARACTER: + { + nIndex = SkipField( nIndex, false); + aResult = OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType ); + ExtendByField( aResult ); + break; + } + default: + aResult = OCommonAccessibleText::getTextBeforeIndex( nIndex, aTextType ); + break; + } /* end of switch( aTextType ) */ + + return aResult; + } + + css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getTextBehindIndex: paragraph index value overflow"); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + i18n::Boundary aBoundary; + switch( aTextType ) + { + case AccessibleTextType::ATTRIBUTE_RUN: + { + sal_Int32 nStartIndex, nEndIndex; + + if( GetAttributeRun(nStartIndex, nEndIndex, nIndex) ) + { + // already at the right border? + if( nEndIndex < GetTextLen() ) + { + if( GetAttributeRun(nStartIndex, nEndIndex, nEndIndex) ) + { + aResult.SegmentText = GetTextRange(nStartIndex, nEndIndex); + aResult.SegmentStart = nStartIndex; + aResult.SegmentEnd = nEndIndex; + } + } + } + break; + } + + case AccessibleTextType::LINE: + { + SvxTextForwarder& rCacheTF = GetTextForwarder(); + sal_Int32 nParaIndex = GetParagraphIndex(); + + CheckPosition(nIndex); + + sal_Int32 nLine, nLineCount = rCacheTF.GetLineCount( nParaIndex ); + sal_Int32 nCurIndex; + //the problem is that rCacheTF.GetLineLen() will include the bullet length. But for the bullet line, + //the text value doesn't contain the bullet characters. all of the bullet and numbering info are exposed + //by the IAText::attributes(). So here must do special support for bullet line. + sal_Int32 nBulletLen = 0; + // get the line after the line the index points into + for( nLine=0, nCurIndex=0; nLine<nLineCount; ++nLine ) + { + if (nLine == 0) + { + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo(nParaIndex); + if (aBulletInfo.bVisible) + { + //in bullet or numbering; + nBulletLen = aBulletInfo.aText.getLength(); + } + } + sal_Int32 nLineLen = rCacheTF.GetLineLen( nParaIndex, nLine); + + if (nLine == 0) + nCurIndex += nLineLen - nBulletLen; + else + nCurIndex += nLineLen; + + if( nCurIndex > nIndex && + nLine < nLineCount-1 ) + { + aResult.SegmentStart = nCurIndex; + aResult.SegmentEnd = nCurIndex + rCacheTF.GetLineLen( nParaIndex, nLine+1); + aResult.SegmentText = GetTextRange( aResult.SegmentStart + nBulletLen, aResult.SegmentEnd + nBulletLen); + break; + } + } + + break; + } + case AccessibleTextType::WORD: + { + nIndex = SkipField( nIndex, true); + OUString sText( implGetText() ); + sal_Int32 nLength = sText.getLength(); + + // get word at index + bool bWord = implGetWordBoundary( sText, aBoundary, nIndex ); + + // real current world + sal_Int32 nextWord = nIndex; + //if( nIndex >= aBoundary.startPos && nIndex <= aBoundary.endPos ) + if( nIndex <= aBoundary.endPos ) + { + nextWord = aBoundary.endPos; + if (nextWord < sText.getLength() && sText[nextWord] == u' ') nextWord++; + bWord = implGetWordBoundary( sText, aBoundary, nextWord ); + } + + if ( bWord && implIsValidBoundary( aBoundary, nLength ) ) + { + aResult.SegmentText = sText.copy( aBoundary.startPos, aBoundary.endPos - aBoundary.startPos ); + aResult.SegmentStart = aBoundary.startPos; + aResult.SegmentEnd = aBoundary.endPos; + + // If the end position of aBoundary is inside a field, extend the result to the end of the field + + ExtendByField( aResult ); + } + } + break; + + case AccessibleTextType::CHARACTER: + { + nIndex = SkipField( nIndex, true); + aResult = OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType ); + ExtendByField( aResult ); + break; + } + default: + aResult = OCommonAccessibleText::getTextBehindIndex( nIndex, aTextType ); + break; + } /* end of switch( aTextType ) */ + + return aResult; + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + try + { + SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true ); + GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + bool aRetVal; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::copyText: index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + //Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + // save current selection + ESelection aOldSelection; + + rCacheVF.GetSelection( aOldSelection ); + //rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) ); + rCacheVF.SetSelection( MakeSelection(nStartIndex + nBulletLen, nEndIndex + nBulletLen) ); + aRetVal = rCacheVF.Copy(); + rCacheVF.SetSelection( aOldSelection ); // restore + + return aRetVal; + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + + // XAccessibleEditableText + sal_Bool SAL_CALL AccessibleEditableTextPara::cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + + SolarMutexGuard aGuard; + + try + { + SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::cutText: index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen); + //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) ) + if( !rCacheTF.IsEditable( aSelection ) ) + return false; // non-editable area selected + + // don't save selection, might become invalid after cut! + //rCacheVF.SetSelection( MakeSelection(nStartIndex, nEndIndex) ); + rCacheVF.SetSelection( aSelection ); + + return rCacheVF.Cut(); + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::pasteText( sal_Int32 nIndex ) + { + + SolarMutexGuard aGuard; + + try + { + SvxEditViewForwarder& rCacheVF = GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::pasteText: index value overflow"); + + CheckPosition(nIndex); + + // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + if( !rCacheTF.IsEditable( MakeSelection(nIndex + nBulletLen) ) ) + return false; // non-editable area selected + + // #104400# set empty selection (=> cursor) to given index + //rCacheVF.SetSelection( MakeCursor(nIndex) ); + rCacheVF.SetSelection( MakeCursor(nIndex + nBulletLen) ); + + return rCacheVF.Paste(); + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + + SolarMutexGuard aGuard; + + try + { + // #102710# Request edit view when doing changes + // AccessibleEmptyEditSource relies on this behaviour + GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::deleteText: index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen); + + //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) ) + if( !rCacheTF.IsEditable( aSelection ) ) + return false; // non-editable area selected + + //sal_Bool bRet = rCacheTF.Delete( MakeSelection(nStartIndex, nEndIndex) ); + bool bRet = rCacheTF.Delete( aSelection ); + + GetEditSource().UpdateData(); + + return bRet; + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::insertText( const OUString& sText, sal_Int32 nIndex ) + { + + SolarMutexGuard aGuard; + + try + { + // #102710# Request edit view when doing changes + // AccessibleEmptyEditSource relies on this behaviour + GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::insertText: index value overflow"); + + CheckPosition(nIndex); + + // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + + if( !rCacheTF.IsEditable( MakeSelection(nIndex + nBulletLen) ) ) + return false; // non-editable area selected + + // #104400# insert given text at empty selection (=> cursor) + bool bRet = rCacheTF.InsertText( sText, MakeCursor(nIndex + nBulletLen) ); + + rCacheTF.QuickFormatDoc(); + GetEditSource().UpdateData(); + + return bRet; + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::replaceText( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const OUString& sReplacement ) + { + + SolarMutexGuard aGuard; + + try + { + // #102710# Request edit view when doing changes + // AccessibleEmptyEditSource relies on this behaviour + GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::replaceText: index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + // Because bullet may occupy one or more characters, the TextAdapter will include bullet to calculate the selection. Add offset to handle bullet + sal_Int32 nBulletLen = 0; + EBulletInfo aBulletInfo = GetTextForwarder().GetBulletInfo(GetParagraphIndex()); + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && aBulletInfo.bVisible ) + nBulletLen = aBulletInfo.aText.getLength(); + ESelection aSelection = MakeSelection (nStartIndex + nBulletLen, nEndIndex + nBulletLen); + + //if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) ) + if( !rCacheTF.IsEditable( aSelection ) ) + return false; // non-editable area selected + + // insert given text into given range => replace + //sal_Bool bRet = rCacheTF.InsertText( sReplacement, MakeSelection(nStartIndex, nEndIndex) ); + bool bRet = rCacheTF.InsertText( sReplacement, aSelection ); + + rCacheTF.QuickFormatDoc(); + GetEditSource().UpdateData(); + + return bRet; + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::setAttributes( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const uno::Sequence< beans::PropertyValue >& aAttributeSet ) + { + + SolarMutexGuard aGuard; + + try + { + // #102710# Request edit view when doing changes + // AccessibleEmptyEditSource relies on this behaviour + GetEditViewForwarder( true ); + SvxAccessibleTextAdapter& rCacheTF = GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + sal_Int32 nPara = GetParagraphIndex(); + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::setAttributes: index value overflow"); + + CheckRange(nStartIndex, nEndIndex); + + if( !rCacheTF.IsEditable( MakeSelection(nStartIndex, nEndIndex) ) ) + return false; // non-editable area selected + + // do the indices span the whole paragraph? Then use the outliner map + // TODO: hold it as a member? + rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), + 0 == nStartIndex && + rCacheTF.GetTextLen(nPara) == nEndIndex ? + ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() : + ImplGetSvxTextPortionSvxPropertySet() ) ); + + xPropSet->SetSelection( MakeSelection(nStartIndex, nEndIndex) ); + + // convert from PropertyValue to Any + for(const beans::PropertyValue& rProp : aAttributeSet) + { + try + { + xPropSet->setPropertyValue(rProp.Name, rProp.Value); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "dbaccess", "AccessibleEditableTextPara::setAttributes exception in setPropertyValue"); + } + } + + rCacheTF.QuickFormatDoc(); + GetEditSource().UpdateData(); + + return true; + } + catch (const uno::RuntimeException&) + { + return false; + } + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::setText( const OUString& sText ) + { + + SolarMutexGuard aGuard; + + return replaceText(0, getCharacterCount(), sText); + } + + // XAccessibleTextAttributes + uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getDefaultAttributes( + const uno::Sequence< OUString >& rRequestedAttributes ) + { + SolarMutexGuard aGuard; + + GetTextForwarder(); + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getCharacterAttributes: index value overflow"); + + // get XPropertySetInfo for paragraph attributes and + // character attributes that span all the paragraphs text. + rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), + ImplGetSvxCharAndParaPropertiesSet() ) ); + xPropSet->SetSelection( MakeSelection( 0, GetTextLen() ) ); + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + if (!xPropSetInfo.is()) + throw uno::RuntimeException("Cannot query XPropertySetInfo", + uno::Reference< uno::XInterface > + ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy + + // build sequence of available properties to check + uno::Sequence< beans::Property > aProperties; + if (const sal_Int32 nLenReqAttr = rRequestedAttributes.getLength()) + { + aProperties.realloc( nLenReqAttr ); + beans::Property *pProperties = aProperties.getArray(); + sal_Int32 nCurLen = 0; + for (const OUString& rRequestedAttribute : rRequestedAttributes) + { + beans::Property aProp; + try + { + aProp = xPropSetInfo->getPropertyByName( rRequestedAttribute ); + } + catch (const beans::UnknownPropertyException&) + { + continue; + } + pProperties[ nCurLen++ ] = aProp; + } + aProperties.realloc( nCurLen ); + } + else + aProperties = xPropSetInfo->getProperties(); + + // build resulting sequence + uno::Sequence< beans::PropertyValue > aOutSequence( aProperties.getLength() ); + beans::PropertyValue* pOutSequence = aOutSequence.getArray(); + sal_Int32 nOutLen = 0; + for (const beans::Property& rProperty : std::as_const(aProperties)) + { + // calling implementation functions: + // _getPropertyState and _getPropertyValue (see below) to provide + // the proper paragraph number when retrieving paragraph attributes + PropertyState eState = xPropSet->_getPropertyState( rProperty.Name, mnParagraphIndex ); + if ( eState == PropertyState_AMBIGUOUS_VALUE ) + { + OSL_FAIL( "ambiguous property value encountered" ); + } + + //if (eState == PropertyState_DIRECT_VALUE) + // per definition all paragraph properties and all character + // properties spanning the whole paragraph should be returned + // and declared as default value + { + pOutSequence->Name = rProperty.Name; + pOutSequence->Handle = rProperty.Handle; + pOutSequence->Value = xPropSet->_getPropertyValue( rProperty.Name, mnParagraphIndex ); + pOutSequence->State = PropertyState_DEFAULT_VALUE; + + ++pOutSequence; + ++nOutLen; + } + } + aOutSequence.realloc( nOutLen ); + + return aOutSequence; + } + + + uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleEditableTextPara::getRunAttributes( + sal_Int32 nIndex, + const uno::Sequence< OUString >& rRequestedAttributes ) + { + + SolarMutexGuard aGuard; + + GetTextForwarder(); + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::getCharacterAttributes: index value overflow"); + + if( getCharacterCount() > 0 ) + CheckIndex(nIndex); + else + CheckPosition(nIndex); + + rtl::Reference< SvxAccessibleTextPropertySet > xPropSet( new SvxAccessibleTextPropertySet( &GetEditSource(), + ImplGetSvxCharAndParaPropertiesSet() ) ); + xPropSet->SetSelection( MakeSelection( nIndex ) ); + uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xPropSet->getPropertySetInfo(); + if (!xPropSetInfo.is()) + throw uno::RuntimeException("Cannot query XPropertySetInfo", + uno::Reference< uno::XInterface > + ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy + + // build sequence of available properties to check + uno::Sequence< beans::Property > aProperties; + if (const sal_Int32 nLenReqAttr = rRequestedAttributes.getLength()) + { + aProperties.realloc( nLenReqAttr ); + beans::Property *pProperties = aProperties.getArray(); + sal_Int32 nCurLen = 0; + for (const OUString& rRequestedAttribute : rRequestedAttributes) + { + beans::Property aProp; + try + { + aProp = xPropSetInfo->getPropertyByName( rRequestedAttribute ); + } + catch (const beans::UnknownPropertyException&) + { + continue; + } + pProperties[ nCurLen++ ] = aProp; + } + aProperties.realloc( nCurLen ); + } + else + aProperties = xPropSetInfo->getProperties(); + + // build resulting sequence + uno::Sequence< beans::PropertyValue > aOutSequence( aProperties.getLength() ); + beans::PropertyValue* pOutSequence = aOutSequence.getArray(); + sal_Int32 nOutLen = 0; + for (const beans::Property& rProperty : std::as_const(aProperties)) + { + // calling 'regular' functions that will operate on the selection + PropertyState eState = xPropSet->getPropertyState( rProperty.Name ); + if (eState == PropertyState_DIRECT_VALUE) + { + pOutSequence->Name = rProperty.Name; + pOutSequence->Handle = rProperty.Handle; + pOutSequence->Value = xPropSet->getPropertyValue( rProperty.Name ); + pOutSequence->State = eState; + + ++pOutSequence; + ++nOutLen; + } + } + aOutSequence.realloc( nOutLen ); + + return aOutSequence; + } + + // XAccessibleHypertext + ::sal_Int32 SAL_CALL AccessibleEditableTextPara::getHyperLinkCount( ) + { + SvxAccessibleTextAdapter& rT = GetTextForwarder(); + const sal_Int32 nPara = GetParagraphIndex(); + + sal_Int32 nHyperLinks = 0; + sal_Int32 nFields = rT.GetFieldCount( nPara ); + for (sal_Int32 n = 0; n < nFields; ++n) + { + EFieldInfo aField = rT.GetFieldInfo( nPara, n ); + if ( dynamic_cast<const SvxURLField* >(aField.pFieldItem->GetField() ) != nullptr) + nHyperLinks++; + } + return nHyperLinks; + } + + css::uno::Reference< css::accessibility::XAccessibleHyperlink > SAL_CALL AccessibleEditableTextPara::getHyperLink( ::sal_Int32 nLinkIndex ) + { + css::uno::Reference< css::accessibility::XAccessibleHyperlink > xRef; + + SvxAccessibleTextAdapter& rT = GetTextForwarder(); + const sal_Int32 nPara = GetParagraphIndex(); + + sal_Int32 nHyperLink = 0; + sal_Int32 nFields = rT.GetFieldCount( nPara ); + for (sal_Int32 n = 0; n < nFields; ++n) + { + EFieldInfo aField = rT.GetFieldInfo( nPara, n ); + if ( dynamic_cast<const SvxURLField* >(aField.pFieldItem->GetField()) != nullptr ) + { + if ( nHyperLink == nLinkIndex ) + { + sal_Int32 nEEStart = aField.aPosition.nIndex; + + // Translate EE Index to accessible index + sal_Int32 nStart = rT.CalcEditEngineIndex( nPara, nEEStart ); + sal_Int32 nEnd = nStart + aField.aCurrentText.getLength(); + xRef = new AccessibleHyperlink( rT, new SvxFieldItem( *aField.pFieldItem ), nStart, nEnd, aField.aCurrentText ); + break; + } + nHyperLink++; + } + } + + return xRef; + } + + ::sal_Int32 SAL_CALL AccessibleEditableTextPara::getHyperLinkIndex( ::sal_Int32 nCharIndex ) + { + const sal_Int32 nPara = GetParagraphIndex(); + SvxAccessibleTextAdapter& rT = GetTextForwarder(); + + const sal_Int32 nEEIndex = rT.CalcEditEngineIndex( nPara, nCharIndex ); + sal_Int32 nHLIndex = -1; //i123620 + sal_Int32 nHyperLink = 0; + sal_Int32 nFields = rT.GetFieldCount( nPara ); + for (sal_Int32 n = 0; n < nFields; ++n) + { + EFieldInfo aField = rT.GetFieldInfo( nPara, n ); + if ( dynamic_cast<const SvxURLField* >( aField.pFieldItem->GetField() ) != nullptr) + { + if ( aField.aPosition.nIndex == nEEIndex ) + { + nHLIndex = nHyperLink; + break; + } + nHyperLink++; + } + } + + return nHLIndex; + } + + // XAccessibleMultiLineText + sal_Int32 SAL_CALL AccessibleEditableTextPara::getLineNumberAtIndex( sal_Int32 nIndex ) + { + + sal_Int32 nRes = -1; + sal_Int32 nPara = GetParagraphIndex(); + + SvxTextForwarder &rCacheTF = GetTextForwarder(); + const bool bValidPara = 0 <= nPara && nPara < rCacheTF.GetParagraphCount(); + DBG_ASSERT( bValidPara, "getLineNumberAtIndex: current paragraph index out of range" ); + if (bValidPara) + { + // we explicitly allow for the index to point at the character right behind the text + if (0 > nIndex || nIndex > rCacheTF.GetTextLen( nPara )) + throw lang::IndexOutOfBoundsException(); + nRes = rCacheTF.GetLineNumberAtIndex( nPara, nIndex ); + } + return nRes; + } + + // XAccessibleMultiLineText + css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtLineNumber( sal_Int32 nLineNo ) + { + + css::accessibility::TextSegment aResult; + sal_Int32 nPara = GetParagraphIndex(); + SvxTextForwarder &rCacheTF = GetTextForwarder(); + const bool bValidPara = 0 <= nPara && nPara < rCacheTF.GetParagraphCount(); + DBG_ASSERT( bValidPara, "getTextAtLineNumber: current paragraph index out of range" ); + if (bValidPara) + { + if (0 > nLineNo || nLineNo >= rCacheTF.GetLineCount( nPara )) + throw lang::IndexOutOfBoundsException(); + sal_Int32 nStart = 0, nEnd = 0; + rCacheTF.GetLineBoundaries( nStart, nEnd, nPara, nLineNo ); + if (nStart >= 0 && nEnd >= 0) + { + try + { + aResult.SegmentText = getTextRange( nStart, nEnd ); + aResult.SegmentStart = nStart; + aResult.SegmentEnd = nEnd; + } + catch (const lang::IndexOutOfBoundsException&) + { + // this is not the exception that should be raised in this function ... + DBG_UNHANDLED_EXCEPTION("editeng"); + } + } + } + return aResult; + } + + // XAccessibleMultiLineText + css::accessibility::TextSegment SAL_CALL AccessibleEditableTextPara::getTextAtLineWithCaret( ) + { + + css::accessibility::TextSegment aResult; + try + { + aResult = getTextAtLineNumber( getNumberOfLineWithCaret() ); + } + catch (const lang::IndexOutOfBoundsException&) + { + // this one needs to be caught since this interface does not allow for it. + } + return aResult; + } + + // XAccessibleMultiLineText + sal_Int32 SAL_CALL AccessibleEditableTextPara::getNumberOfLineWithCaret( ) + { + + sal_Int32 nRes = -1; + try + { + nRes = getLineNumberAtIndex( getCaretPosition() ); + } + catch (const lang::IndexOutOfBoundsException&) + { + // this one needs to be caught since this interface does not allow for it. + } + return nRes; + } + + + // XServiceInfo + OUString SAL_CALL AccessibleEditableTextPara::getImplementationName() + { + + return "AccessibleEditableTextPara"; + } + + sal_Bool SAL_CALL AccessibleEditableTextPara::supportsService (const OUString& sServiceName) + { + + return cppu::supportsService(this, sServiceName); + } + + uno::Sequence< OUString> SAL_CALL AccessibleEditableTextPara::getSupportedServiceNames() + { + // #105185# Using correct service now + return { OUString("com.sun.star.text.AccessibleParagraphView") }; + } + +} // end of namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleHyperlink.cxx b/editeng/source/accessibility/AccessibleHyperlink.cxx new file mode 100644 index 000000000..25d9683fc --- /dev/null +++ b/editeng/source/accessibility/AccessibleHyperlink.cxx @@ -0,0 +1,128 @@ +/* -*- 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/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <comphelper/accessiblekeybindinghelper.hxx> + +#include "AccessibleHyperlink.hxx" +#include <editeng/unoedprx.hxx> +#include <editeng/flditem.hxx> +#include <vcl/keycodes.hxx> + +using namespace ::com::sun::star; + + +// AccessibleHyperlink implementation + + +namespace accessibility +{ + + AccessibleHyperlink::AccessibleHyperlink( SvxAccessibleTextAdapter& r, SvxFieldItem* p, sal_Int32 nStt, sal_Int32 nEnd, const OUString& rD ) + : rTA( r ) + { + pFld.reset( p ); + nStartIdx = nStt; + nEndIdx = nEnd; + aDescription = rD; + } + + AccessibleHyperlink::~AccessibleHyperlink() + { + } + + // XAccessibleAction + sal_Int32 SAL_CALL AccessibleHyperlink::getAccessibleActionCount() + { + return isValid() ? 1 : 0; + } + + sal_Bool SAL_CALL AccessibleHyperlink::doAccessibleAction( sal_Int32 nIndex ) + { + bool bRet = false; + if ( isValid() && ( nIndex == 0 ) ) + { + rTA.FieldClicked( *pFld ); + bRet = true; + } + return bRet; + } + + OUString SAL_CALL AccessibleHyperlink::getAccessibleActionDescription( sal_Int32 nIndex ) + { + OUString aDesc; + + if ( isValid() && ( nIndex == 0 ) ) + aDesc = aDescription; + + return aDesc; + } + + uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL AccessibleHyperlink::getAccessibleActionKeyBinding( sal_Int32 nIndex ) + { + uno::Reference< css::accessibility::XAccessibleKeyBinding > xKeyBinding; + + if( isValid() && ( nIndex == 0 ) ) + { + rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper = new ::comphelper::OAccessibleKeyBindingHelper(); + xKeyBinding = pKeyBindingHelper; + + awt::KeyStroke aKeyStroke; + aKeyStroke.Modifiers = 0; + aKeyStroke.KeyCode = KEY_RETURN; + aKeyStroke.KeyChar = 0; + aKeyStroke.KeyFunc = 0; + pKeyBindingHelper->AddKeyBinding( aKeyStroke ); + } + + return xKeyBinding; + } + + // XAccessibleHyperlink + uno::Any SAL_CALL AccessibleHyperlink::getAccessibleActionAnchor( sal_Int32 /*nIndex*/ ) + { + return uno::Any(); + } + + uno::Any SAL_CALL AccessibleHyperlink::getAccessibleActionObject( sal_Int32 /*nIndex*/ ) + { + return uno::Any(); + } + + sal_Int32 SAL_CALL AccessibleHyperlink::getStartIndex() + { + return nStartIdx; + } + + sal_Int32 SAL_CALL AccessibleHyperlink::getEndIndex() + { + return nEndIdx; + } + + sal_Bool SAL_CALL AccessibleHyperlink::isValid( ) + { + return rTA.IsValid(); + } + +} // end of namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleHyperlink.hxx b/editeng/source/accessibility/AccessibleHyperlink.hxx new file mode 100644 index 000000000..7e4f36a6b --- /dev/null +++ b/editeng/source/accessibility/AccessibleHyperlink.hxx @@ -0,0 +1,64 @@ +/* -*- 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 . + */ + +#pragma once + +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> + +#include <memory> + +class SvxFieldItem; +class SvxAccessibleTextAdapter; + +namespace accessibility +{ + + class AccessibleHyperlink : public ::cppu::WeakImplHelper< css::accessibility::XAccessibleHyperlink > + { + private: + + SvxAccessibleTextAdapter& rTA; + std::unique_ptr<SvxFieldItem> pFld; + sal_Int32 nStartIdx, nEndIdx; // translated values + OUString aDescription; + + public: + AccessibleHyperlink( SvxAccessibleTextAdapter& r, SvxFieldItem* p, sal_Int32 nStt, sal_Int32 nEnd, const OUString& rD ); + virtual ~AccessibleHyperlink() override; + + // XAccessibleAction + virtual sal_Int32 SAL_CALL getAccessibleActionCount() override; + virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override; + virtual OUString SAL_CALL getAccessibleActionDescription( sal_Int32 nIndex ) override; + virtual css::uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL getAccessibleActionKeyBinding( sal_Int32 nIndex ) override; + + // XAccessibleHyperlink + virtual css::uno::Any SAL_CALL getAccessibleActionAnchor( sal_Int32 nIndex ) override; + virtual css::uno::Any SAL_CALL getAccessibleActionObject( sal_Int32 nIndex ) override; + virtual sal_Int32 SAL_CALL getStartIndex() override; + virtual sal_Int32 SAL_CALL getEndIndex() override; + virtual sal_Bool SAL_CALL isValid() override; + }; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleImageBullet.cxx b/editeng/source/accessibility/AccessibleImageBullet.cxx new file mode 100644 index 000000000..da157b919 --- /dev/null +++ b/editeng/source/accessibility/AccessibleImageBullet.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 <tools/gen.hxx> +#include <tools/debug.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <editeng/AccessibleEditableTextPara.hxx> +#include <editeng/eerdll.hxx> + +#include <editeng/editdata.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editrids.hrc> +#include <editeng/unoedsrc.hxx> +#include <svtools/colorcfg.hxx> + +#include "AccessibleImageBullet.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ + + AccessibleImageBullet::AccessibleImageBullet ( uno::Reference< XAccessible > xParent ) : + mnParagraphIndex( 0 ), + mnIndexInParent( 0 ), + mpEditSource( nullptr ), + maEEOffset( 0, 0 ), + mxParent(std::move( xParent )), + // well, that's strictly (UNO) exception safe, though not + // really robust. We rely on the fact that this member is + // constructed last, and that the constructor body catches + // exceptions, thus no chance for exceptions once the Id is + // fetched. Nevertheless, normally should employ RAII here... + mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient()) + { + try + { + // Create the state set. + mxStateSet = new ::utl::AccessibleStateSetHelper (); + + // these are always on + mxStateSet->AddState( AccessibleStateType::VISIBLE ); + mxStateSet->AddState( AccessibleStateType::SHOWING ); + mxStateSet->AddState( AccessibleStateType::ENABLED ); + mxStateSet->AddState( AccessibleStateType::SENSITIVE ); + } + catch( const uno::Exception& ) {} + } + + AccessibleImageBullet::~AccessibleImageBullet() + { + + // sign off from event notifier + if( getNotifierClientId() != -1 ) + { + try + { + ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() ); + } + catch( const uno::Exception& ) {} + } + } + + uno::Reference< XAccessibleContext > SAL_CALL AccessibleImageBullet::getAccessibleContext( ) + { + + // We implement the XAccessibleContext interface in the same object + return uno::Reference< XAccessibleContext > ( this ); + } + + sal_Int32 SAL_CALL AccessibleImageBullet::getAccessibleChildCount() + { + + return 0; + } + + uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleChild( sal_Int32 ) + { + throw lang::IndexOutOfBoundsException("No children available", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > (this) ) ); // static_cast: disambiguate hierarchy + } + + uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleParent() + { + + return mxParent; + } + + sal_Int32 SAL_CALL AccessibleImageBullet::getAccessibleIndexInParent() + { + + return mnIndexInParent; + } + + sal_Int16 SAL_CALL AccessibleImageBullet::getAccessibleRole() + { + + return AccessibleRole::GRAPHIC; + } + + OUString SAL_CALL AccessibleImageBullet::getAccessibleDescription() + { + // Get the string from the resource for the specified id. + return EditResId(RID_SVXSTR_A11Y_IMAGEBULLET_DESCRIPTION); + } + + OUString SAL_CALL AccessibleImageBullet::getAccessibleName() + { + // Get the string from the resource for the specified id. + return EditResId(RID_SVXSTR_A11Y_IMAGEBULLET_NAME); + } + + uno::Reference< XAccessibleRelationSet > SAL_CALL AccessibleImageBullet::getAccessibleRelationSet() + { + + // no relations, therefore empty + return uno::Reference< XAccessibleRelationSet >(); + } + + uno::Reference< XAccessibleStateSet > SAL_CALL AccessibleImageBullet::getAccessibleStateSet() + { + + SolarMutexGuard aGuard; + + // Create a copy of the state set and return it. + + if( !mxStateSet ) + return uno::Reference<XAccessibleStateSet>(); + + return uno::Reference<XAccessibleStateSet>( new ::utl::AccessibleStateSetHelper (*mxStateSet) ); + } + + lang::Locale SAL_CALL AccessibleImageBullet::getLocale() + { + + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleImageBullet::getLocale: paragraph index value overflow"); + + // return locale of first character in the paragraph + return LanguageTag(GetTextForwarder().GetLanguage( GetParagraphIndex(), 0 )).getLocale(); + } + + void SAL_CALL AccessibleImageBullet::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) + { + + if( getNotifierClientId() != -1 ) + ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener ); + } + + void SAL_CALL AccessibleImageBullet::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) + { + + if( getNotifierClientId() == -1 ) + return; + + const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), 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 + ::comphelper::AccessibleEventNotifier::TClientId nId( getNotifierClientId() ); + mnNotifierClientId = -1; + ::comphelper::AccessibleEventNotifier::revokeClient( nId ); + } + } + + sal_Bool SAL_CALL AccessibleImageBullet::containsPoint( const awt::Point& rPoint ) + { + + SolarMutexGuard aGuard; + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::contains: index value overflow"); + + awt::Rectangle aTmpRect = implGetBounds(); + tools::Rectangle aRect( Point(aTmpRect.X, aTmpRect.Y), Size(aTmpRect.Width, aTmpRect.Height) ); + Point aPoint( rPoint.X, rPoint.Y ); + + return aRect.Contains( aPoint ); + } + + uno::Reference< XAccessible > SAL_CALL AccessibleImageBullet::getAccessibleAtPoint( const awt::Point& /*aPoint*/ ) + { + + // as we have no children, empty reference + return uno::Reference< XAccessible >(); + } + + awt::Rectangle SAL_CALL AccessibleImageBullet::getBounds( ) + { + SolarMutexGuard aGuard; + + return implGetBounds(); + } + awt::Rectangle AccessibleImageBullet::implGetBounds( ) + { + + DBG_ASSERT(GetParagraphIndex() >= 0, + "AccessibleEditableTextPara::implGetBounds: index value overflow"); + + SvxTextForwarder& rCacheTF = GetTextForwarder(); + EBulletInfo aBulletInfo = rCacheTF.GetBulletInfo( GetParagraphIndex() ); + tools::Rectangle aParentRect = rCacheTF.GetParaBounds( GetParagraphIndex() ); + + if( aBulletInfo.nParagraph != EE_PARA_NOT_FOUND && + aBulletInfo.bVisible && + aBulletInfo.nType == SVX_NUM_BITMAP ) + { + tools::Rectangle aRect = aBulletInfo.aBounds; + + // subtract paragraph position (bullet pos is absolute in EditEngine/Outliner) + aRect.Move( -aParentRect.Left(), -aParentRect.Top() ); + + // convert to screen coordinates + tools::Rectangle aScreenRect = AccessibleEditableTextPara::LogicToPixel( aRect, + rCacheTF.GetMapMode(), + GetViewForwarder() ); + + // offset from shape/cell + Point aOffset = maEEOffset; + + return awt::Rectangle( aScreenRect.Left() + aOffset.X(), + aScreenRect.Top() + aOffset.Y(), + aScreenRect.GetSize().Width(), + aScreenRect.GetSize().Height() ); + } + + return awt::Rectangle(); + } + + awt::Point SAL_CALL AccessibleImageBullet::getLocation( ) + { + + SolarMutexGuard aGuard; + + awt::Rectangle aRect = implGetBounds(); + + return awt::Point( aRect.X, aRect.Y ); + } + + awt::Point SAL_CALL AccessibleImageBullet::getLocationOnScreen( ) + { + + SolarMutexGuard aGuard; + + // relate us to parent + uno::Reference< XAccessible > xParent = getAccessibleParent(); + if( xParent.is() ) + { + uno::Reference< XAccessibleComponent > xParentComponent( xParent, uno::UNO_QUERY ); + if( xParentComponent.is() ) + { + awt::Point aRefPoint = xParentComponent->getLocationOnScreen(); + awt::Point aPoint = getLocation(); + aPoint.X += aRefPoint.X; + aPoint.Y += aRefPoint.Y; + + return aPoint; + } + } + + throw uno::RuntimeException("Cannot access parent", + uno::Reference< uno::XInterface > + ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy + } + + awt::Size SAL_CALL AccessibleImageBullet::getSize( ) + { + + SolarMutexGuard aGuard; + + awt::Rectangle aRect = implGetBounds(); + + return awt::Size( aRect.Width, aRect.Height ); + } + + void SAL_CALL AccessibleImageBullet::grabFocus( ) + { + + throw uno::RuntimeException("Not focusable", + uno::Reference< uno::XInterface > + ( static_cast< XAccessible* > (this) ) ); // disambiguate hierarchy + } + + sal_Int32 SAL_CALL AccessibleImageBullet::getForeground( ) + { + + // #104444# Added to XAccessibleComponent interface + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); + } + + sal_Int32 SAL_CALL AccessibleImageBullet::getBackground( ) + { + + // #104444# Added to XAccessibleComponent interface + Color aColor( Application::GetSettings().GetStyleSettings().GetWindowColor() ); + + // the background is transparent + aColor.SetAlpha(0); + + return static_cast<sal_Int32>( aColor ); + } + + OUString SAL_CALL AccessibleImageBullet::getImplementationName() + { + + return "AccessibleImageBullet"; + } + + sal_Bool SAL_CALL AccessibleImageBullet::supportsService (const OUString& sServiceName) + { + + return cppu::supportsService(this, sServiceName); + } + + uno::Sequence< OUString > SAL_CALL AccessibleImageBullet::getSupportedServiceNames() + { + return { "com.sun.star.accessibility.AccessibleContext" }; + } + + void AccessibleImageBullet::SetIndexInParent( sal_Int32 nIndex ) + { + + mnIndexInParent = nIndex; + } + + void AccessibleImageBullet::SetEEOffset( const Point& rOffset ) + { + + maEEOffset = rOffset; + } + + void AccessibleImageBullet::Dispose() + { + + int nClientId( getNotifierClientId() ); + + // #108212# drop all references before notifying dispose + mxParent = nullptr; + mnNotifierClientId = -1; + mpEditSource = nullptr; + + // notify listeners + if( nClientId != -1 ) + { + try + { + uno::Reference < XAccessibleContext > xThis = getAccessibleContext(); + + // #106234# Delegate to EventNotifier + ::comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, xThis ); + } + catch( const uno::Exception& ) {} + } + } + + void AccessibleImageBullet::SetEditSource( SvxEditSource* pEditSource ) + { + + mpEditSource = pEditSource; + + if( !mpEditSource ) + { + // going defunc + UnSetState( AccessibleStateType::SHOWING ); + UnSetState( AccessibleStateType::VISIBLE ); + SetState( AccessibleStateType::INVALID ); + SetState( AccessibleStateType::DEFUNC ); + + Dispose(); + } + } + + void AccessibleImageBullet::FireEvent(const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const + { + + uno::Reference < XAccessibleContext > xThis( const_cast< AccessibleImageBullet* > (this)->getAccessibleContext() ); + + AccessibleEventObject aEvent(xThis, nEventId, rNewValue, rOldValue); + + // #106234# Delegate to EventNotifier + ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), + aEvent ); + } + + void AccessibleImageBullet::SetState( const sal_Int16 nStateId ) + { + if( mxStateSet != nullptr && + !mxStateSet->contains(nStateId) ) + { + mxStateSet->AddState( nStateId ); + FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any( nStateId ) ); + } + } + + void AccessibleImageBullet::UnSetState( const sal_Int16 nStateId ) + { + if( mxStateSet != nullptr && + mxStateSet->contains(nStateId) ) + { + mxStateSet->RemoveState( nStateId ); + FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any( nStateId ) ); + } + } + + + void AccessibleImageBullet::SetParagraphIndex( sal_Int32 nIndex ) + { + + uno::Any aOldDesc; + uno::Any aOldName; + + try + { + aOldDesc <<= getAccessibleDescription(); + aOldName <<= getAccessibleName(); + } + catch( const uno::Exception& ) {} // optional behaviour + + sal_Int32 nOldIndex = mnParagraphIndex; + + mnParagraphIndex = nIndex; + + try + { + if( nOldIndex != nIndex ) + { + // index and therefore description changed + FireEvent( AccessibleEventId::DESCRIPTION_CHANGED, uno::Any( getAccessibleDescription() ), aOldDesc ); + FireEvent( AccessibleEventId::NAME_CHANGED, uno::Any( getAccessibleName() ), aOldName ); + } + } + catch( const uno::Exception& ) {} // optional behaviour + } + + + SvxEditSource& AccessibleImageBullet::GetEditSource() const + { + + if( !mpEditSource ) + throw uno::RuntimeException("No edit source, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleImageBullet* > (this) ) ) ); // disambiguate hierarchy + return *mpEditSource; + } + + SvxTextForwarder& AccessibleImageBullet::GetTextForwarder() const + { + + SvxEditSource& rEditSource = GetEditSource(); + SvxTextForwarder* pTextForwarder = rEditSource.GetTextForwarder(); + + if( !pTextForwarder ) + throw uno::RuntimeException("Unable to fetch text forwarder, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleImageBullet* > (this) ) ) ); // disambiguate hierarchy + + if( !pTextForwarder->IsValid() ) + throw uno::RuntimeException("Text forwarder is invalid, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleImageBullet* > (this) ) ) ); // disambiguate hierarchy + return *pTextForwarder; + } + + SvxViewForwarder& AccessibleImageBullet::GetViewForwarder() const + { + + SvxEditSource& rEditSource = GetEditSource(); + SvxViewForwarder* pViewForwarder = rEditSource.GetViewForwarder(); + + if( !pViewForwarder ) + { + throw uno::RuntimeException("Unable to fetch view forwarder, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleImageBullet* > (this) ) ) ); // disambiguate hierarchy + } + + if( !pViewForwarder->IsValid() ) + throw uno::RuntimeException("View forwarder is invalid, object is defunct", + uno::Reference< uno::XInterface > + ( static_cast< ::cppu::OWeakObject* > + ( const_cast< AccessibleImageBullet* > (this) ) ) ); // disambiguate hierarchy + return *pViewForwarder; + } + + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleImageBullet.hxx b/editeng/source/accessibility/AccessibleImageBullet.hxx new file mode 100644 index 000000000..85f1011fb --- /dev/null +++ b/editeng/source/accessibility/AccessibleImageBullet.hxx @@ -0,0 +1,202 @@ +/* -*- 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 . + */ + +#pragma once + +#include <rtl/ref.hxx> +#include <tools/gen.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> + +class SvxEditSource; +class SvxTextForwarder; +class SvxViewForwarder; +namespace utl { class AccessibleStateSetHelper; } + +namespace accessibility +{ + typedef ::cppu::WeakImplHelper< css::accessibility::XAccessible, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleComponent, + css::accessibility::XAccessibleEventBroadcaster, + css::lang::XServiceInfo > AccessibleImageBulletInterfaceBase; + + /** This class implements the image bullets for the EditEngine/Outliner UAA + */ + class AccessibleImageBullet final : public AccessibleImageBulletInterfaceBase + { + + public: + /// Create accessible object for given parent + AccessibleImageBullet ( css::uno::Reference< css::accessibility::XAccessible > xParent ); + + virtual ~AccessibleImageBullet () override; + + // XAccessible + virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override; + + // XAccessibleContext + virtual sal_Int32 SAL_CALL getAccessibleChildCount() override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override; + virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override; + virtual sal_Int16 SAL_CALL getAccessibleRole() override; + virtual OUString SAL_CALL getAccessibleDescription() override; + virtual OUString SAL_CALL getAccessibleName() override; + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override; + virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet() override; + virtual css::lang::Locale SAL_CALL getLocale() override; + + // XAccessibleEventBroadcaster + virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override; + virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override; + + // XAccessibleComponent + virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint ) override; + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override; + virtual css::awt::Rectangle SAL_CALL getBounds( ) override; + virtual css::awt::Point SAL_CALL getLocation( ) override; + virtual css::awt::Point SAL_CALL getLocationOnScreen( ) override; + virtual css::awt::Size SAL_CALL getSize( ) override; + virtual void SAL_CALL grabFocus( ) override; + virtual sal_Int32 SAL_CALL getForeground( ) override; + virtual sal_Int32 SAL_CALL getBackground( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService (const OUString& sServiceName) override; + virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override; + + /** Set the current index in the accessibility parent + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + void SetIndexInParent( sal_Int32 nIndex ); + + /** Set the edit engine offset + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + void SetEEOffset( const Point& rOffset ); + + /** Set the EditEngine offset + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + void SetEditSource( SvxEditSource* pEditSource ); + + /** Dispose this object + + Notifies and deregisters the listeners, drops all references. + */ + void Dispose(); + + /** Set the current paragraph number + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + void SetParagraphIndex( sal_Int32 nIndex ); + + /** Query the current paragraph number (0 - nParas-1) + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + sal_Int32 GetParagraphIndex() const { return mnParagraphIndex; } + + /// Calls all Listener objects to tell them the change. Don't hold locks when calling this! + void FireEvent(const sal_Int16 nEventId, const css::uno::Any& rNewValue, const css::uno::Any& rOldValue = css::uno::Any() ) const; + + private: + AccessibleImageBullet( const AccessibleImageBullet& ) = delete; + AccessibleImageBullet& operator= ( const AccessibleImageBullet& ) = delete; + + // maintain state set and send STATE_CHANGE events + void SetState( const sal_Int16 nStateId ); + void UnSetState( const sal_Int16 nStateId ); + + SvxEditSource& GetEditSource() const; + + int getNotifierClientId() const { return mnNotifierClientId; } + + /** Query the SvxTextForwarder for EditEngine access. + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + SvxTextForwarder& GetTextForwarder() const; + + /** Query the SvxViewForwarder for EditEngine access. + + @attention This method does not lock the SolarMutex, + leaving that to the calling code. This is because only + there potential deadlock situations can be resolved. Thus, + make sure SolarMutex is locked when calling this. + */ + SvxViewForwarder& GetViewForwarder() const; + + css::awt::Rectangle implGetBounds(); + + // the paragraph index in the edit engine (guarded by solar mutex) + sal_Int32 mnParagraphIndex; + + // our current index in the parent (guarded by solar mutex) + sal_Int32 mnIndexInParent; + + // the current edit source (guarded by solar mutex) + SvxEditSource* mpEditSource; + + // the offset of the underlying EditEngine from the shape/cell (guarded by solar mutex) + Point maEEOffset; + + // the current state set (updated from SetState/UnSetState and guarded by solar mutex) + rtl::Reference< utl::AccessibleStateSetHelper > mxStateSet; + + /// The shape we're the accessible for (unguarded) + css::uno::Reference< css::accessibility::XAccessible > mxParent; + + /// Our listeners (guarded by maMutex) + int mnNotifierClientId; + }; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleParaManager.cxx b/editeng/source/accessibility/AccessibleParaManager.cxx new file mode 100644 index 000000000..f597bba6a --- /dev/null +++ b/editeng/source/accessibility/AccessibleParaManager.cxx @@ -0,0 +1,403 @@ +/* -*- 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 . + */ + + +// Global header + + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Reference.hxx> +#include <o3tl/safeint.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> + + +// Project-local header + + +#include <editeng/AccessibleParaManager.hxx> +#include <editeng/AccessibleEditableTextPara.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + + +namespace accessibility +{ + AccessibleParaManager::AccessibleParaManager() : + maChildren(1), + maEEOffset( 0, 0 ), + mnFocusedChild( -1 ), + mbActive( false ) + { + } + + AccessibleParaManager::~AccessibleParaManager() + { + // owner is responsible for possible child death + } + + void AccessibleParaManager::SetAdditionalChildStates( VectorOfStates&& rChildStates ) + { + maChildStates = std::move(rChildStates); + } + + void AccessibleParaManager::SetNum( sal_Int32 nNumParas ) + { + if( o3tl::make_unsigned(nNumParas) < maChildren.size() ) + Release( nNumParas, maChildren.size() ); + + maChildren.resize( nNumParas ); + + if( mnFocusedChild >= nNumParas ) + mnFocusedChild = -1; + } + + sal_Int32 AccessibleParaManager::GetNum() const + { + size_t nSize = maChildren.size(); + if (nSize > SAL_MAX_INT32) + { + SAL_WARN( "editeng", "AccessibleParaManager::GetNum - overflow " << nSize); + return SAL_MAX_INT32; + } + return static_cast<sal_Int32>(nSize); + } + + AccessibleParaManager::VectorOfChildren::iterator AccessibleParaManager::begin() + { + return maChildren.begin(); + } + + AccessibleParaManager::VectorOfChildren::iterator AccessibleParaManager::end() + { + return maChildren.end(); + } + + void AccessibleParaManager::FireEvent( sal_Int32 nPara, + const sal_Int16 nEventId ) const + { + DBG_ASSERT( 0 <= nPara && maChildren.size() > o3tl::make_unsigned(nPara), + "AccessibleParaManager::FireEvent: invalid index" ); + + if( 0 <= nPara && maChildren.size() > o3tl::make_unsigned(nPara) ) + { + auto aChild( GetChild( nPara ).first.get() ); + if( aChild.is() ) + aChild->FireEvent( nEventId ); + } + } + + bool AccessibleParaManager::IsReferencable( + rtl::Reference<AccessibleEditableTextPara> const & aChild) + { + return aChild.is(); + } + + bool AccessibleParaManager::IsReferencable( sal_Int32 nChild ) const + { + DBG_ASSERT( 0 <= nChild && maChildren.size() > o3tl::make_unsigned(nChild), + "AccessibleParaManager::IsReferencable: invalid index" ); + + if( 0 <= nChild && maChildren.size() > o3tl::make_unsigned(nChild) ) + { + // retrieve hard reference from weak one + return IsReferencable( GetChild( nChild ).first.get() ); + } + else + { + return false; + } + } + + AccessibleParaManager::WeakChild AccessibleParaManager::GetChild( sal_Int32 nParagraphIndex ) const + { + DBG_ASSERT( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex), + "AccessibleParaManager::GetChild: invalid index" ); + + if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) ) + { + return maChildren[ nParagraphIndex ]; + } + else + { + return WeakChild(); + } + } + + bool AccessibleParaManager::HasCreatedChild( sal_Int32 nParagraphIndex ) const + { + if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) ) + { + auto const & rChild = maChildren[ nParagraphIndex ]; + return rChild.second.Width != 0 || rChild.second.Height != 0; + } + else + return false; + } + + AccessibleParaManager::Child AccessibleParaManager::CreateChild( sal_Int32 nChild, + const uno::Reference< XAccessible >& xFrontEnd, + SvxEditSourceAdapter& rEditSource, + sal_Int32 nParagraphIndex ) + { + DBG_ASSERT( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex), + "AccessibleParaManager::CreateChild: invalid index" ); + + if( 0 <= nParagraphIndex && maChildren.size() > o3tl::make_unsigned(nParagraphIndex) ) + { + // retrieve hard reference from weak one + auto aChild( GetChild( nParagraphIndex ).first.get() ); + + if( !IsReferencable( nParagraphIndex ) ) + { + // there is no hard reference available, create object then + // #i27138# + aChild = new AccessibleEditableTextPara(xFrontEnd, this); + + InitChild( *aChild, rEditSource, nChild, nParagraphIndex ); + + maChildren[ nParagraphIndex ] = WeakChild( aChild, aChild->getBounds() ); + } + + return Child( aChild.get(), GetChild( nParagraphIndex ).second ); + } + else + { + return Child(); + } + } + + void AccessibleParaManager::SetEEOffset( const Point& rOffset ) + { + maEEOffset = rOffset; + + MemFunAdapter< const Point& > aAdapter( &::accessibility::AccessibleEditableTextPara::SetEEOffset, rOffset ); + std::for_each( begin(), end(), aAdapter ); + } + + void AccessibleParaManager::SetActive( bool bActive ) + { + mbActive = bActive; + + if( bActive ) + { + SetState( AccessibleStateType::ACTIVE ); + SetState( AccessibleStateType::EDITABLE ); + } + else + { + UnSetState( AccessibleStateType::ACTIVE ); + UnSetState( AccessibleStateType::EDITABLE ); + } + } + + void AccessibleParaManager::SetFocus( sal_Int32 nChild ) + { + if( mnFocusedChild != -1 ) + UnSetState( mnFocusedChild, AccessibleStateType::FOCUSED ); + + mnFocusedChild = nChild; + + if( mnFocusedChild != -1 ) + SetState( mnFocusedChild, AccessibleStateType::FOCUSED ); + } + + void AccessibleParaManager::InitChild( AccessibleEditableTextPara& rChild, + SvxEditSourceAdapter& rEditSource, + sal_Int32 nChild, + sal_Int32 nParagraphIndex ) const + { + rChild.SetEditSource( &rEditSource ); + rChild.SetIndexInParent( nChild ); + rChild.SetParagraphIndex( nParagraphIndex ); + + rChild.SetEEOffset( maEEOffset ); + + if( mbActive ) + { + rChild.SetState( AccessibleStateType::ACTIVE ); + rChild.SetState( AccessibleStateType::EDITABLE ); + } + + if( mnFocusedChild == nParagraphIndex ) + rChild.SetState( AccessibleStateType::FOCUSED ); + + // add states passed from outside + for( const auto& rState : maChildStates ) + rChild.SetState( rState ); + } + + void AccessibleParaManager::SetState( sal_Int32 nChild, const sal_Int16 nStateId ) + { + MemFunAdapter< const sal_Int16 > aFunc( &AccessibleEditableTextPara::SetState, + nStateId ); + aFunc( GetChild(nChild) ); + } + + void AccessibleParaManager::SetState( const sal_Int16 nStateId ) + { + std::for_each( begin(), end(), + MemFunAdapter< const sal_Int16 >( &AccessibleEditableTextPara::SetState, + nStateId ) ); + } + + void AccessibleParaManager::UnSetState( sal_Int32 nChild, const sal_Int16 nStateId ) + { + MemFunAdapter< const sal_Int16 > aFunc( &AccessibleEditableTextPara::UnSetState, + nStateId ); + aFunc( GetChild(nChild) ); + } + + void AccessibleParaManager::UnSetState( const sal_Int16 nStateId ) + { + std::for_each( begin(), end(), + MemFunAdapter< const sal_Int16 >( &AccessibleEditableTextPara::UnSetState, + nStateId ) ); + } + + namespace { + + // not generic yet, no arguments... + class AccessibleParaManager_DisposeChildren + { + public: + AccessibleParaManager_DisposeChildren() {} + void operator()( ::accessibility::AccessibleEditableTextPara& rPara ) + { + rPara.Dispose(); + } + }; + + } + + void AccessibleParaManager::Dispose() + { + AccessibleParaManager_DisposeChildren aFunctor; + + std::for_each( begin(), end(), + WeakChildAdapter< AccessibleParaManager_DisposeChildren > (aFunctor) ); + } + + namespace { + + // not generic yet, too many method arguments... + class StateChangeEvent + { + public: + StateChangeEvent( const sal_Int16 nEventId, + const uno::Any& rNewValue, + const uno::Any& rOldValue ) : + mnEventId( nEventId ), + mrNewValue( rNewValue ), + mrOldValue( rOldValue ) {} + void operator()( ::accessibility::AccessibleEditableTextPara const & rPara ) + { + rPara.FireEvent( mnEventId, mrNewValue, mrOldValue ); + } + + private: + const sal_Int16 mnEventId; + const uno::Any& mrNewValue; + const uno::Any& mrOldValue; + }; + + } + + void AccessibleParaManager::FireEvent( sal_Int32 nStartPara, + sal_Int32 nEndPara, + const sal_Int16 nEventId, + const uno::Any& rNewValue, + const uno::Any& rOldValue ) const + { + DBG_ASSERT( 0 <= nStartPara && 0 <= nEndPara && + maChildren.size() > o3tl::make_unsigned(nStartPara) && + maChildren.size() >= o3tl::make_unsigned(nEndPara) && + nEndPara >= nStartPara, "AccessibleParaManager::FireEvent: invalid index" ); + + + if( 0 <= nStartPara && 0 <= nEndPara && + maChildren.size() > o3tl::make_unsigned(nStartPara) && + maChildren.size() >= o3tl::make_unsigned(nEndPara) && + nEndPara >= nStartPara ) + { + VectorOfChildren::const_iterator front = maChildren.begin(); + VectorOfChildren::const_iterator back = front; + + std::advance( front, nStartPara ); + std::advance( back, nEndPara ); + + StateChangeEvent aFunctor( nEventId, rNewValue, rOldValue ); + + std::for_each( front, back, AccessibleParaManager::WeakChildAdapter< StateChangeEvent >( aFunctor ) ); + } + } + + namespace { + + class ReleaseChild + { + public: + AccessibleParaManager::WeakChild operator()( const AccessibleParaManager::WeakChild& rPara ) + { + AccessibleParaManager::ShutdownPara( rPara ); + + // clear reference + return AccessibleParaManager::WeakChild(); + } + }; + + } + + void AccessibleParaManager::Release( sal_Int32 nStartPara, sal_Int32 nEndPara ) + { + DBG_ASSERT( 0 <= nStartPara && 0 <= nEndPara && + maChildren.size() > o3tl::make_unsigned(nStartPara) && + maChildren.size() >= o3tl::make_unsigned(nEndPara), + "AccessibleParaManager::Release: invalid index" ); + + if( 0 <= nStartPara && 0 <= nEndPara && + maChildren.size() > o3tl::make_unsigned(nStartPara) && + maChildren.size() >= o3tl::make_unsigned(nEndPara) ) + { + VectorOfChildren::iterator front = maChildren.begin(); + VectorOfChildren::iterator back = front; + + std::advance( front, nStartPara ); + std::advance( back, nEndPara ); + + std::transform( front, back, front, ReleaseChild() ); + } + } + + void AccessibleParaManager::ShutdownPara( const WeakChild& rChild ) + { + auto aChild( rChild.first.get() ); + + if( IsReferencable( aChild ) ) + aChild->SetEditSource( nullptr ); + } + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleSelectionBase.cxx b/editeng/source/accessibility/AccessibleSelectionBase.cxx new file mode 100644 index 000000000..09975c9ec --- /dev/null +++ b/editeng/source/accessibility/AccessibleSelectionBase.cxx @@ -0,0 +1,91 @@ +/* -*- 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 <editeng/AccessibleSelectionBase.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ + + // - AccessibleSelectionBase - + + + AccessibleSelectionBase::AccessibleSelectionBase() + { + } + + + AccessibleSelectionBase::~AccessibleSelectionBase() + { + } + + + void SAL_CALL AccessibleSelectionBase::selectAccessibleChild( sal_Int32 nChildIndex ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + OCommonAccessibleSelection::selectAccessibleChild( nChildIndex ); + } + + + sal_Bool SAL_CALL AccessibleSelectionBase::isAccessibleChildSelected( sal_Int32 nChildIndex ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + return OCommonAccessibleSelection::isAccessibleChildSelected( nChildIndex ); + } + + + void SAL_CALL AccessibleSelectionBase::clearAccessibleSelection( ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + OCommonAccessibleSelection::clearAccessibleSelection(); + } + + + void SAL_CALL AccessibleSelectionBase::selectAllAccessibleChildren( ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + OCommonAccessibleSelection::selectAllAccessibleChildren(); + } + + + sal_Int32 SAL_CALL AccessibleSelectionBase::getSelectedAccessibleChildCount( ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + return OCommonAccessibleSelection::getSelectedAccessibleChildCount(); + } + + + uno::Reference< XAccessible > SAL_CALL AccessibleSelectionBase::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + return OCommonAccessibleSelection::getSelectedAccessibleChild( nSelectedChildIndex ); + } + + + void SAL_CALL AccessibleSelectionBase::deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) + { + ::osl::MutexGuard aGuard( implGetMutex() ); + OCommonAccessibleSelection::deselectAccessibleChild( nSelectedChildIndex ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleStaticTextBase.cxx b/editeng/source/accessibility/AccessibleStaticTextBase.cxx new file mode 100644 index 000000000..9e73d192b --- /dev/null +++ b/editeng/source/accessibility/AccessibleStaticTextBase.cxx @@ -0,0 +1,965 @@ +/* -*- 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 . + */ + + +// Global header + + +#include <utility> +#include <memory> +#include <vector> +#include <algorithm> +#include <rtl/ustrbuf.hxx> +#include <tools/debug.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/sequence.hxx> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/accessibility/AccessibleTextType.hpp> + + +// Project-local header + + +#include <editeng/editdata.hxx> +#include <editeng/unoedprx.hxx> +#include <editeng/AccessibleStaticTextBase.hxx> +#include <editeng/AccessibleEditableTextPara.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +/* TODO: + ===== + + - separate adapter functionality from AccessibleStaticText class + + - refactor common loops into templates, using mem_fun + + */ + +namespace accessibility +{ + typedef std::vector< beans::PropertyValue > PropertyValueVector; + + namespace { + + class PropertyValueEqualFunctor + { + const beans::PropertyValue& m_rPValue; + + public: + explicit PropertyValueEqualFunctor(const beans::PropertyValue& rPValue) + : m_rPValue(rPValue) + {} + bool operator() ( const beans::PropertyValue& rhs ) const + { + return ( m_rPValue.Name == rhs.Name && m_rPValue.Value == rhs.Value ); + } + }; + + } + + sal_Unicode const cNewLine(0x0a); + + + // Static Helper + + + static ESelection MakeSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, + sal_Int32 nEndPara, sal_Int32 nEndIndex ) + { + DBG_ASSERT(nStartPara >= 0 && + nStartIndex >= 0 && + nEndPara >= 0 && + nEndIndex >= 0, + "AccessibleStaticTextBase_Impl::MakeSelection: index value overflow"); + + return ESelection(nStartPara, nStartIndex, nEndPara, nEndIndex); + } + + + // AccessibleStaticTextBase_Impl declaration + + + /** AccessibleStaticTextBase_Impl + + This class implements the AccessibleStaticTextBase + functionality, mainly by forwarding the calls to an aggregated + AccessibleEditableTextPara. As this is a therefore non-trivial + adapter, factoring out the common functionality from + AccessibleEditableTextPara might be a profitable future task. + */ + class AccessibleStaticTextBase_Impl + { + friend class AccessibleStaticTextBase; + public: + + // receive pointer to our frontend class and view window + AccessibleStaticTextBase_Impl(); + + void SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource ); + + void SetEventSource( const uno::Reference< XAccessible >& rInterface ) + { + + mxThis = rInterface; + } + + void SetOffset( const Point& ); + + void Dispose(); + + AccessibleEditableTextPara& GetParagraph( sal_Int32 nPara ) const; + sal_Int32 GetParagraphCount() const; + + EPosition Index2Internal( sal_Int32 nFlatIndex ) const + { + + return ImpCalcInternal( nFlatIndex, false ); + } + + EPosition Range2Internal( sal_Int32 nFlatIndex ) const + { + + return ImpCalcInternal( nFlatIndex, true ); + } + + sal_Int32 Internal2Index( EPosition nEEIndex ) const; + + void CorrectTextSegment( TextSegment& aTextSegment, + int nPara ) const; + + bool SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, + sal_Int32 nEndPara, sal_Int32 nEndIndex ); + bool CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex, + sal_Int32 nEndPara, sal_Int32 nEndIndex ); + + tools::Rectangle GetParagraphBoundingBox() const; + bool RemoveLineBreakCount( sal_Int32& rIndex ); + + private: + + EPosition ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const; + + // our frontend class (the one implementing the actual + // interface). That's not necessarily the one containing the impl + // pointer + uno::Reference< XAccessible > mxThis; + + // implements our functionality, we're just an adapter (guarded by solar mutex) + mutable rtl::Reference<AccessibleEditableTextPara> mxTextParagraph; + + // a wrapper for the text forwarders (guarded by solar mutex) + mutable SvxEditSourceAdapter maEditSource; + }; + + + // AccessibleStaticTextBase_Impl implementation + + + AccessibleStaticTextBase_Impl::AccessibleStaticTextBase_Impl() : + mxTextParagraph( new AccessibleEditableTextPara(nullptr) ) + { + + // TODO: this is still somewhat of a hack, all the more since + // now the maTextParagraph has an empty parent reference set + } + + void AccessibleStaticTextBase_Impl::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource ) + { + + maEditSource.SetEditSource( std::move(pEditSource) ); + if( mxTextParagraph.is() ) + mxTextParagraph->SetEditSource( &maEditSource ); + } + + void AccessibleStaticTextBase_Impl::SetOffset( const Point& rPoint ) + { + if( mxTextParagraph.is() ) + mxTextParagraph->SetEEOffset( rPoint ); + } + + void AccessibleStaticTextBase_Impl::Dispose() + { + + // we're the owner of the paragraph, so destroy it, too + if( mxTextParagraph.is() ) + mxTextParagraph->Dispose(); + + // drop references + mxThis = nullptr; + mxTextParagraph.clear(); + } + + AccessibleEditableTextPara& AccessibleStaticTextBase_Impl::GetParagraph( sal_Int32 nPara ) const + { + + if( !mxTextParagraph.is() ) + throw lang::DisposedException ("object has been already disposed", mxThis ); + + // TODO: Have a different method on AccessibleEditableTextPara + // that does not care about state changes + mxTextParagraph->SetParagraphIndex( nPara ); + + return *mxTextParagraph; + } + + sal_Int32 AccessibleStaticTextBase_Impl::GetParagraphCount() const + { + + if( !mxTextParagraph.is() ) + return 0; + else + return mxTextParagraph->GetTextForwarder().GetParagraphCount(); + } + + sal_Int32 AccessibleStaticTextBase_Impl::Internal2Index( EPosition nEEIndex ) const + { + // XXX checks for overflow and returns maximum if so + sal_Int32 aRes(0); + for(sal_Int32 i=0; i<nEEIndex.nPara; ++i) + { + sal_Int32 nCount = GetParagraph(i).getCharacterCount(); + if (SAL_MAX_INT32 - aRes > nCount) + return SAL_MAX_INT32; + aRes += nCount; + } + + if (SAL_MAX_INT32 - aRes > nEEIndex.nIndex) + return SAL_MAX_INT32; + return aRes + nEEIndex.nIndex; + } + + void AccessibleStaticTextBase_Impl::CorrectTextSegment( TextSegment& aTextSegment, + int nPara ) const + { + // Keep 'invalid' values at the TextSegment + if( aTextSegment.SegmentStart != -1 && + aTextSegment.SegmentEnd != -1 ) + { + // #112814# Correct TextSegment by paragraph offset + sal_Int32 nOffset(0); + int i; + for(i=0; i<nPara; ++i) + nOffset += GetParagraph(i).getCharacterCount(); + + aTextSegment.SegmentStart += nOffset; + aTextSegment.SegmentEnd += nOffset; + } + } + + EPosition AccessibleStaticTextBase_Impl::ImpCalcInternal( sal_Int32 nFlatIndex, bool bExclusive ) const + { + + if( nFlatIndex < 0 ) + throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds", + mxThis); + // gratuitously accepting larger indices here, AccessibleEditableTextPara will throw eventually + + sal_Int32 nCurrPara, nCurrIndex, nParas, nCurrCount; + for( nCurrPara=0, nParas=GetParagraphCount(), nCurrCount=0, nCurrIndex=0; nCurrPara<nParas; ++nCurrPara ) + { + nCurrCount = GetParagraph( nCurrPara ).getCharacterCount(); + nCurrIndex += nCurrCount; + if( nCurrIndex >= nFlatIndex ) + { + // check overflow + DBG_ASSERT(nCurrPara >= 0 && + nFlatIndex - nCurrIndex + nCurrCount >= 0, + "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow"); + + return EPosition(nCurrPara, nFlatIndex - nCurrIndex + nCurrCount); + } + } + + // #102170# Allow one-past the end for ranges + if( bExclusive && nCurrIndex == nFlatIndex ) + { + // check overflow + DBG_ASSERT(nCurrPara > 0 && + nFlatIndex - nCurrIndex + nCurrCount >= 0, + "AccessibleStaticTextBase_Impl::Index2Internal: index value overflow"); + + return EPosition(nCurrPara-1, nFlatIndex - nCurrIndex + nCurrCount); + } + + // not found? Out of bounds + throw lang::IndexOutOfBoundsException("AccessibleStaticTextBase_Impl::Index2Internal: character index out of bounds", + mxThis); + } + + bool AccessibleStaticTextBase_Impl::SetSelection( sal_Int32 nStartPara, sal_Int32 nStartIndex, + sal_Int32 nEndPara, sal_Int32 nEndIndex ) + { + + if( !mxTextParagraph.is() ) + return false; + + try + { + SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true ); + return rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) ); + } + catch( const uno::RuntimeException& ) + { + return false; + } + } + + bool AccessibleStaticTextBase_Impl::CopyText( sal_Int32 nStartPara, sal_Int32 nStartIndex, + sal_Int32 nEndPara, sal_Int32 nEndIndex ) + { + + if( !mxTextParagraph.is() ) + return false; + + try + { + SvxEditViewForwarder& rCacheVF = mxTextParagraph->GetEditViewForwarder( true ); + mxTextParagraph->GetTextForwarder(); // MUST be after GetEditViewForwarder(), see method docs + bool aRetVal; + + // save current selection + ESelection aOldSelection; + + rCacheVF.GetSelection( aOldSelection ); + rCacheVF.SetSelection( MakeSelection(nStartPara, nStartIndex, nEndPara, nEndIndex) ); + aRetVal = rCacheVF.Copy(); + rCacheVF.SetSelection( aOldSelection ); // restore + + return aRetVal; + } + catch( const uno::RuntimeException& ) + { + return false; + } + } + + tools::Rectangle AccessibleStaticTextBase_Impl::GetParagraphBoundingBox() const + { + tools::Rectangle aRect; + if( mxTextParagraph.is() ) + { + awt::Rectangle aAwtRect = mxTextParagraph->getBounds(); + aRect = tools::Rectangle( Point( aAwtRect.X, aAwtRect.Y ), Size( aAwtRect.Width, aAwtRect.Height ) ); + } + else + { + aRect.SetEmpty(); + } + return aRect; + } + //the input argument is the index(including "\n" ) in the string. + //the function will calculate the actual index(not including "\n") in the string. + //and return true if the index is just at a "\n" + bool AccessibleStaticTextBase_Impl::RemoveLineBreakCount( sal_Int32& rIndex ) + { + // get the total char number inside the cell. + sal_Int32 i, nCount, nParas; + for( i=0, nCount=0, nParas=GetParagraphCount(); i<nParas; ++i ) + nCount += GetParagraph(i).getCharacterCount(); + nCount = nCount + (nParas-1); + if( nCount == 0 && rIndex == 0) return false; + + + sal_Int32 nCurrPara, nCurrCount; + sal_Int32 nLineBreakPos = 0, nLineBreakCount = 0; + sal_Int32 nParaCount = GetParagraphCount(); + for ( nCurrCount = 0, nCurrPara = 0; nCurrPara < nParaCount; nCurrPara++ ) + { + nCurrCount += GetParagraph( nCurrPara ).getCharacterCount(); + nLineBreakPos = nCurrCount++; + if ( rIndex == nLineBreakPos ) + { + rIndex -= (++nLineBreakCount);//(++nLineBreakCount); + if ( rIndex < 0) + { + rIndex = 0; + } + //if the index is at the last position of the last paragraph + //there is no "\n" , so we should increase rIndex by 1 and return false. + if ( (nCurrPara+1) == nParaCount ) + { + rIndex++; + return false; + } + else + { + return true; + } + } + else if ( rIndex < nLineBreakPos ) + { + rIndex -= nLineBreakCount; + return false; + } + else + { + nLineBreakCount++; + } + } + return false; + } + + + // AccessibleStaticTextBase implementation + + AccessibleStaticTextBase::AccessibleStaticTextBase( std::unique_ptr< SvxEditSource > && pEditSource ) : + mpImpl( new AccessibleStaticTextBase_Impl() ) + { + SolarMutexGuard aGuard; + + SetEditSource( std::move(pEditSource) ); + } + + AccessibleStaticTextBase::~AccessibleStaticTextBase() + { + } + + void AccessibleStaticTextBase::SetEditSource( std::unique_ptr< SvxEditSource > && pEditSource ) + { + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->SetEditSource( std::move(pEditSource) ); + } + + void AccessibleStaticTextBase::SetEventSource( const uno::Reference< XAccessible >& rInterface ) + { + mpImpl->SetEventSource( rInterface ); + + } + + void AccessibleStaticTextBase::SetOffset( const Point& rPoint ) + { + // precondition: solar mutex locked + DBG_TESTSOLARMUTEX(); + + mpImpl->SetOffset( rPoint ); + } + + void AccessibleStaticTextBase::Dispose() + { + mpImpl->Dispose(); + + } + + // XAccessibleContext + sal_Int32 AccessibleStaticTextBase::getAccessibleChildCount() + { + // no children at all + return 0; + } + + uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleChild( sal_Int32 /*i*/ ) + { + // no children at all + return uno::Reference< XAccessible >(); + } + + uno::Reference< XAccessible > AccessibleStaticTextBase::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ ) + { + // no children at all + return uno::Reference< XAccessible >(); + } + + // XAccessibleText + sal_Int32 SAL_CALL AccessibleStaticTextBase::getCaretPosition() + { + SolarMutexGuard aGuard; + + sal_Int32 i, nPos, nParas; + for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) + { + if( (nPos=mpImpl->GetParagraph(i).getCaretPosition()) != -1 ) + return nPos; + } + + return nPos; + } + + sal_Bool SAL_CALL AccessibleStaticTextBase::setCaretPosition( sal_Int32 nIndex ) + { + return setSelection(nIndex, nIndex); + } + + sal_Unicode SAL_CALL AccessibleStaticTextBase::getCharacter( sal_Int32 nIndex ) + { + SolarMutexGuard aGuard; + + EPosition aPos( mpImpl->Index2Internal(nIndex) ); + + return mpImpl->GetParagraph( aPos.nPara ).getCharacter( aPos.nIndex ); + } + + uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) + { + SolarMutexGuard aGuard; + + //get the actual index without "\n" + mpImpl->RemoveLineBreakCount( nIndex ); + + EPosition aPos( mpImpl->Index2Internal(nIndex) ); + + return mpImpl->GetParagraph( aPos.nPara ).getCharacterAttributes( aPos.nIndex, aRequestedAttributes ); + } + + awt::Rectangle SAL_CALL AccessibleStaticTextBase::getCharacterBounds( sal_Int32 nIndex ) + { + SolarMutexGuard aGuard; + + // #108900# Allow ranges for nIndex, as one-past-the-end + // values are now legal, too. + EPosition aPos( mpImpl->Range2Internal(nIndex) ); + + // #i70916# Text in spread sheet cells return the wrong extents + AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); + awt::Rectangle aParaBounds( rPara.getBounds() ); + awt::Rectangle aBounds( rPara.getCharacterBounds( aPos.nIndex ) ); + aBounds.X += aParaBounds.X; + aBounds.Y += aParaBounds.Y; + + return aBounds; + } + + sal_Int32 SAL_CALL AccessibleStaticTextBase::getCharacterCount() + { + SolarMutexGuard aGuard; + + sal_Int32 i, nCount, nParas; + for( i=0, nCount=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) + nCount += mpImpl->GetParagraph(i).getCharacterCount(); + //count on the number of "\n" which equals number of paragraphs decrease 1. + nCount = nCount + (nParas-1); + return nCount; + } + + sal_Int32 SAL_CALL AccessibleStaticTextBase::getIndexAtPoint( const awt::Point& rPoint ) + { + SolarMutexGuard aGuard; + + const sal_Int32 nParas( mpImpl->GetParagraphCount() ); + sal_Int32 nIndex; + int i; + for( i=0; i<nParas; ++i ) + { + // TODO: maybe exploit the fact that paragraphs are + // ordered vertically for early exit + + // #i70916# Text in spread sheet cells return the wrong extents + AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( i ); + awt::Rectangle aParaBounds( rPara.getBounds() ); + awt::Point aPoint( rPoint ); + aPoint.X -= aParaBounds.X; + aPoint.Y -= aParaBounds.Y; + + // #112814# Use correct index offset + if ( ( nIndex = rPara.getIndexAtPoint( aPoint ) ) != -1 ) + return mpImpl->Internal2Index(EPosition(i, nIndex)); + } + + return -1; + } + + OUString SAL_CALL AccessibleStaticTextBase::getSelectedText() + { + SolarMutexGuard aGuard; + + sal_Int32 nStart( getSelectionStart() ); + sal_Int32 nEnd( getSelectionEnd() ); + + // #104481# Return the empty string for 'no selection' + if( nStart < 0 || nEnd < 0 ) + return OUString(); + + return getTextRange( nStart, nEnd ); + } + + sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionStart() + { + SolarMutexGuard aGuard; + + sal_Int32 i, nPos, nParas; + for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) + { + if( (nPos=mpImpl->GetParagraph(i).getSelectionStart()) != -1 ) + return nPos; + } + + return nPos; + } + + sal_Int32 SAL_CALL AccessibleStaticTextBase::getSelectionEnd() + { + SolarMutexGuard aGuard; + + sal_Int32 i, nPos, nParas; + for( i=0, nPos=-1, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) + { + if( (nPos=mpImpl->GetParagraph(i).getSelectionEnd()) != -1 ) + return nPos; + } + + return nPos; + } + + sal_Bool SAL_CALL AccessibleStaticTextBase::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); + EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); + + return mpImpl->SetSelection( aStartIndex.nPara, aStartIndex.nIndex, + aEndIndex.nPara, aEndIndex.nIndex ); + } + + OUString SAL_CALL AccessibleStaticTextBase::getText() + { + SolarMutexGuard aGuard; + + sal_Int32 i, nParas; + OUStringBuffer aRes; + for( i=0, nParas=mpImpl->GetParagraphCount(); i<nParas; ++i ) + aRes.append(mpImpl->GetParagraph(i).getText()); + + return aRes.makeStringAndClear(); + } + + OUString SAL_CALL AccessibleStaticTextBase::getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + if( nStartIndex > nEndIndex ) + std::swap(nStartIndex, nEndIndex); + //if startindex equals endindex we will get nothing. So return an empty string directly. + if ( nStartIndex == nEndIndex ) + { + return OUString(); + } + bool bStart = mpImpl->RemoveLineBreakCount( nStartIndex ); + //if the start index is just at a "\n", we need to begin from the next char + if ( bStart ) + { + nStartIndex++; + } + //we need to find out whether the previous position of the current endindex is at "\n" or not + //if yes we need to mark it and add "\n" at the end of the result + sal_Int32 nTemp = nEndIndex - 1; + bool bEnd = mpImpl->RemoveLineBreakCount( nTemp ); + bool bTemp = mpImpl->RemoveLineBreakCount( nEndIndex ); + //if the below condition is true it indicates an empty paragraph with just a "\n" + //so we need to set one "\n" flag to avoid duplication. + if ( bStart && bEnd && ( nStartIndex == nEndIndex) ) + { + bEnd = false; + } + //if the current endindex is at a "\n", we need to increase endindex by 1 to make sure + //the char before "\n" is included. Because string returned by this function will not include + //the char at the endindex. + if ( bTemp ) + { + nEndIndex++; + } + OUStringBuffer aRes; + EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); + EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); + + // #102170# Special case: start and end paragraph are identical + if( aStartIndex.nPara == aEndIndex.nPara ) + { + //we don't return the string directly now for that we have to do some further process for "\n" + aRes = mpImpl->GetParagraph( aStartIndex.nPara ).getTextRange( aStartIndex.nIndex, aEndIndex.nIndex ); + } + else + { + sal_Int32 i( aStartIndex.nPara ); + aRes = mpImpl->GetParagraph(i).getTextRange( aStartIndex.nIndex, + mpImpl->GetParagraph(i).getCharacterCount()/*-1*/); + ++i; + + // paragraphs inbetween are fully included + for( ; i<aEndIndex.nPara; ++i ) + { + aRes.append(cNewLine); + aRes.append(mpImpl->GetParagraph(i).getText()); + } + + if( i<=aEndIndex.nPara ) + { + //if the below condition is matched it means that endindex is at mid of the last paragraph + //we need to add a "\n" before we add the last part of the string. + if ( !bEnd && aEndIndex.nIndex ) + { + aRes.append(cNewLine); + } + aRes.append(mpImpl->GetParagraph(i).getTextRange( 0, aEndIndex.nIndex )); + } + } + //According to the flag we marked before, we have to add "\n" at the beginning + //or at the end of the result string. + if ( bStart ) + { + aRes.insert(0, OUStringChar(cNewLine)); + } + if ( bEnd ) + { + aRes.append(OUStringChar(cNewLine)); + } + return aRes.makeStringAndClear(); + } + + css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex ); + EPosition aPos( mpImpl->Range2Internal(nIndex) ); + + css::accessibility::TextSegment aResult; + + if( AccessibleTextType::PARAGRAPH == aTextType ) + { + // #106393# Special casing one behind last paragraph is + // not necessary, since then, we return the content and + // boundary of that last paragraph. Range2Internal is + // tolerant against that, and returns the last paragraph + // in aPos.nPara. + + // retrieve full text of the paragraph + aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); + + // #112814# Adapt the start index with the paragraph offset + aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); + aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); + } + else if ( AccessibleTextType::ATTRIBUTE_RUN == aTextType ) + { + SvxAccessibleTextAdapter& rTextForwarder = mpImpl->GetParagraph( aPos.nIndex ).GetTextForwarder(); + sal_Int32 nStartIndex, nEndIndex; + if ( rTextForwarder.GetAttributeRun( nStartIndex, nEndIndex, aPos.nPara, aPos.nIndex, true ) ) + { + aResult.SegmentText = getTextRange( nStartIndex, nEndIndex ); + aResult.SegmentStart = nStartIndex; + aResult.SegmentEnd = nEndIndex; + } + } + else + { + // No special handling required, forward to wrapped class + aResult = mpImpl->GetParagraph( aPos.nPara ).getTextAtIndex( aPos.nIndex, aTextType ); + + // #112814# Adapt the start index with the paragraph offset + mpImpl->CorrectTextSegment( aResult, aPos.nPara ); + if ( bLineBreak ) + { + aResult.SegmentText = OUString(cNewLine); + } + } + + return aResult; + } + + css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + sal_Int32 nOldIdx = nIndex; + bool bLineBreak = mpImpl->RemoveLineBreakCount( nIndex ); + EPosition aPos( mpImpl->Range2Internal(nIndex) ); + + css::accessibility::TextSegment aResult; + + if( AccessibleTextType::PARAGRAPH == aTextType ) + { + if( aPos.nIndex == mpImpl->GetParagraph( aPos.nPara ).getCharacterCount() ) + { + // #103589# Special casing one behind the last paragraph + aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara ).getText(); + + // #112814# Adapt the start index with the paragraph offset + aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara, 0 ) ); + } + else if( aPos.nPara > 0 ) + { + aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara - 1 ).getText(); + + // #112814# Adapt the start index with the paragraph offset + aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara - 1, 0 ) ); + } + + aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); + } + else + { + // No special handling required, forward to wrapped class + aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBeforeIndex( aPos.nIndex, aTextType ); + + // #112814# Adapt the start index with the paragraph offset + mpImpl->CorrectTextSegment( aResult, aPos.nPara ); + if ( bLineBreak && (nOldIdx-1) >= 0) + { + aResult = getTextAtIndex( nOldIdx-1, aTextType ); + } + } + + return aResult; + } + + css::accessibility::TextSegment SAL_CALL AccessibleStaticTextBase::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) + { + SolarMutexGuard aGuard; + + sal_Int32 nTemp = nIndex+1; + bool bLineBreak = mpImpl->RemoveLineBreakCount( nTemp ); + mpImpl->RemoveLineBreakCount( nIndex ); + EPosition aPos( mpImpl->Range2Internal(nIndex) ); + + css::accessibility::TextSegment aResult; + + if( AccessibleTextType::PARAGRAPH == aTextType ) + { + // Special casing one behind the last paragraph is not + // necessary, this case is invalid here for + // getTextBehindIndex + if( aPos.nPara + 1 < mpImpl->GetParagraphCount() ) + { + aResult.SegmentText = mpImpl->GetParagraph( aPos.nPara + 1 ).getText(); + + // #112814# Adapt the start index with the paragraph offset + aResult.SegmentStart = mpImpl->Internal2Index( EPosition( aPos.nPara + 1, 0 ) ); + aResult.SegmentEnd = aResult.SegmentStart + aResult.SegmentText.getLength(); + } + } + else + { + // No special handling required, forward to wrapped class + aResult = mpImpl->GetParagraph( aPos.nPara ).getTextBehindIndex( aPos.nIndex, aTextType ); + + // #112814# Adapt the start index with the paragraph offset + mpImpl->CorrectTextSegment( aResult, aPos.nPara ); + if ( bLineBreak ) + { + aResult.SegmentText = OUStringChar(cNewLine) + aResult.SegmentText; + } + } + + return aResult; + } + + sal_Bool SAL_CALL AccessibleStaticTextBase::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) + { + SolarMutexGuard aGuard; + + if( nStartIndex > nEndIndex ) + std::swap(nStartIndex, nEndIndex); + + EPosition aStartIndex( mpImpl->Range2Internal(nStartIndex) ); + EPosition aEndIndex( mpImpl->Range2Internal(nEndIndex) ); + + return mpImpl->CopyText( aStartIndex.nPara, aStartIndex.nIndex, + aEndIndex.nPara, aEndIndex.nIndex ); + } + + sal_Bool SAL_CALL AccessibleStaticTextBase::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) + { + return false; + } + + // XAccessibleTextAttributes + uno::Sequence< beans::PropertyValue > AccessibleStaticTextBase::getDefaultAttributes( const uno::Sequence< OUString >& RequestedAttributes ) + { + // get the intersection of the default attributes of all paragraphs + + SolarMutexGuard aGuard; + + PropertyValueVector aDefAttrVec( + comphelper::sequenceToContainer<PropertyValueVector>(mpImpl->GetParagraph( 0 ).getDefaultAttributes( RequestedAttributes )) ); + + const sal_Int32 nParaCount = mpImpl->GetParagraphCount(); + for ( sal_Int32 nPara = 1; nPara < nParaCount; ++nPara ) + { + uno::Sequence< beans::PropertyValue > aSeq = mpImpl->GetParagraph( nPara ).getDefaultAttributes( RequestedAttributes ); + PropertyValueVector aIntersectionVec; + + for ( const auto& rDefAttr : aDefAttrVec ) + { + const beans::PropertyValue* pItr = aSeq.getConstArray(); + const beans::PropertyValue* pEnd = pItr + aSeq.getLength(); + const beans::PropertyValue* pFind = std::find_if( pItr, pEnd, PropertyValueEqualFunctor(rDefAttr) ); + if ( pFind != pEnd ) + { + aIntersectionVec.push_back( *pFind ); + } + } + + aDefAttrVec.swap( aIntersectionVec ); + + if ( aDefAttrVec.empty() ) + { + break; + } + } + + return comphelper::containerToSequence(aDefAttrVec); + } + + uno::Sequence< beans::PropertyValue > SAL_CALL AccessibleStaticTextBase::getRunAttributes( sal_Int32 nIndex, const uno::Sequence< OUString >& RequestedAttributes ) + { + // get those default attributes of the paragraph, which are not part + // of the intersection of all paragraphs and add them to the run attributes + + SolarMutexGuard aGuard; + + EPosition aPos( mpImpl->Index2Internal( nIndex ) ); + AccessibleEditableTextPara& rPara = mpImpl->GetParagraph( aPos.nPara ); + uno::Sequence< beans::PropertyValue > aDefAttrSeq = rPara.getDefaultAttributes( RequestedAttributes ); + uno::Sequence< beans::PropertyValue > aRunAttrSeq = rPara.getRunAttributes( aPos.nIndex, RequestedAttributes ); + uno::Sequence< beans::PropertyValue > aIntersectionSeq = getDefaultAttributes( RequestedAttributes ); + PropertyValueVector aDiffVec; + + const beans::PropertyValue* pDefAttr = aDefAttrSeq.getConstArray(); + const sal_Int32 nLength = aDefAttrSeq.getLength(); + for ( sal_Int32 i = 0; i < nLength; ++i ) + { + const beans::PropertyValue* pItr = aIntersectionSeq.getConstArray(); + const beans::PropertyValue* pEnd = pItr + aIntersectionSeq.getLength(); + bool bNone = std::none_of( pItr, pEnd, PropertyValueEqualFunctor( pDefAttr[i] ) ); + if ( bNone && pDefAttr[i].Handle != 0) + { + aDiffVec.push_back( pDefAttr[i] ); + } + } + + return ::comphelper::concatSequences( aRunAttrSeq, comphelper::containerToSequence(aDiffVec) ); + } + + tools::Rectangle AccessibleStaticTextBase::GetParagraphBoundingBox() const + { + return mpImpl->GetParagraphBoundingBox(); + } + +} // end of namespace accessibility + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/accessibility/AccessibleStringWrap.cxx b/editeng/source/accessibility/AccessibleStringWrap.cxx new file mode 100644 index 000000000..519a1feb7 --- /dev/null +++ b/editeng/source/accessibility/AccessibleStringWrap.cxx @@ -0,0 +1,91 @@ +/* -*- 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 <algorithm> +#include <cstdlib> + +#include <tools/debug.hxx> +#include <utility> +#include <vcl/outdev.hxx> + +#include <editeng/svxfont.hxx> +#include <AccessibleStringWrap.hxx> + + +// AccessibleStringWrap implementation + + +AccessibleStringWrap::AccessibleStringWrap( OutputDevice& rDev, SvxFont& rFont, OUString aText ) : + mrDev( rDev ), + mrFont( rFont ), + maText(std::move( aText )) +{ +} + +void AccessibleStringWrap::GetCharacterBounds( sal_Int32 nIndex, tools::Rectangle& rRect ) +{ + DBG_ASSERT(nIndex >= 0, + "SvxAccessibleStringWrap::GetCharacterBounds: index value overflow"); + + mrFont.SetPhysFont(mrDev); + + // #108900# Handle virtual position one-past-the end of the string + if( nIndex >= maText.getLength() ) + { + // create a caret bounding rect that has the height of the + // current font and is one pixel wide. + rRect.SetLeft( mrDev.GetTextWidth(maText) ); + rRect.SetTop( 0 ); + rRect.SetSize( Size(mrDev.GetTextHeight(), 1) ); + } + else + { + sal_Int32 aXArray[2]; + mrDev.GetCaretPositions( maText, aXArray, nIndex, 1 ); + rRect.SetLeft( 0 ); + rRect.SetTop( 0 ); + rRect.SetSize( Size(mrDev.GetTextHeight(), std::abs(aXArray[0] - aXArray[1])) ); + rRect.Move( std::min(aXArray[0], aXArray[1]), 0 ); + } + + if( mrFont.IsVertical() ) + { + // #101701# Rotate to vertical + rRect = tools::Rectangle( Point(-rRect.Top(), rRect.Left()), + Point(-rRect.Bottom(), rRect.Right())); + } +} + +sal_Int32 AccessibleStringWrap::GetIndexAtPoint( const Point& rPoint ) +{ + // search for character bounding box containing given point + tools::Rectangle aRect; + sal_Int32 i, nLen = maText.getLength(); + for( i=0; i<nLen; ++i ) + { + GetCharacterBounds(i, aRect); + if( aRect.Contains(rPoint) ) + return i; + } + + return -1; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |