diff options
Diffstat (limited to 'starmath/source/accessibility.cxx')
-rw-r--r-- | starmath/source/accessibility.cxx | 1791 |
1 files changed, 1791 insertions, 0 deletions
diff --git a/starmath/source/accessibility.cxx b/starmath/source/accessibility.cxx new file mode 100644 index 000000000..854f09443 --- /dev/null +++ b/starmath/source/accessibility.cxx @@ -0,0 +1,1791 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> +#include <sal/log.hxx> + +#include <memory> + +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/AccessibleEventObject.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <unotools/accessiblerelationsethelper.hxx> + +#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp> +#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp> +#include <com/sun/star/i18n/WordType.hpp> +#include <unotools/accessiblestatesethelper.hxx> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <osl/diagnose.h> +#include <svx/AccessibleTextHelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/unohelp2.hxx> +#include <vcl/settings.hxx> + +#include <tools/gen.hxx> +#include <svl/itemset.hxx> + +#include <editeng/editobj.hxx> +#include <editeng/editdata.hxx> +#include <editeng/editview.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/outliner.hxx> +#include <editeng/unoedhlp.hxx> + + +#include "accessibility.hxx" +#include <document.hxx> +#include <view.hxx> +#include <strings.hrc> +#include <smmod.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; +using namespace com::sun::star::accessibility; + + +static awt::Rectangle lcl_GetBounds( vcl::Window const *pWin ) +{ + // !! see VCLXAccessibleComponent::implGetBounds() + + //! the coordinates returned are relative to the parent window ! + //! Thus the top-left point may be different from (0, 0) ! + + awt::Rectangle aBounds; + if (pWin) + { + tools::Rectangle aRect = pWin->GetWindowExtentsRelative( nullptr ); + aBounds.X = aRect.Left(); + aBounds.Y = aRect.Top(); + aBounds.Width = aRect.GetWidth(); + aBounds.Height = aRect.GetHeight(); + vcl::Window* pParent = pWin->GetAccessibleParentWindow(); + if (pParent) + { + tools::Rectangle aParentRect = pParent->GetWindowExtentsRelative( nullptr ); + awt::Point aParentScreenLoc( aParentRect.Left(), aParentRect.Top() ); + aBounds.X -= aParentScreenLoc.X; + aBounds.Y -= aParentScreenLoc.Y; + } + } + return aBounds; +} + +static awt::Point lcl_GetLocationOnScreen( vcl::Window const *pWin ) +{ + // !! see VCLXAccessibleComponent::getLocationOnScreen() + + awt::Point aPos; + if (pWin) + { + tools::Rectangle aRect = pWin->GetWindowExtentsRelative( nullptr ); + aPos.X = aRect.Left(); + aPos.Y = aRect.Top(); + } + return aPos; +} + + +SmGraphicAccessible::SmGraphicAccessible( SmGraphicWindow *pGraphicWin ) : + aAccName (SmResId(RID_DOCUMENTSTR)), + nClientId (0), + pWin (pGraphicWin) +{ + OSL_ENSURE( pWin, "SmGraphicAccessible: window missing" ); +} + +SmGraphicAccessible::~SmGraphicAccessible() +{ +} + + +SmDocShell * SmGraphicAccessible::GetDoc_Impl() +{ + SmViewShell *pView = pWin ? pWin->GetView() : nullptr; + return pView ? pView->GetDoc() : nullptr; +} + +OUString SmGraphicAccessible::GetAccessibleText_Impl() +{ + OUString aTxt; + SmDocShell *pDoc = GetDoc_Impl(); + if (pDoc) + aTxt = pDoc->GetAccessibleText(); + return aTxt; +} + +void SmGraphicAccessible::ClearWin() +{ + pWin = nullptr; // implicitly results in AccessibleStateType::DEFUNC set + + if ( nClientId ) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( nClientId, *this ); + nClientId = 0; + } +} + +void SmGraphicAccessible::LaunchEvent( + const sal_Int16 nAccessibleEventId, + const uno::Any &rOldVal, + const uno::Any &rNewVal) +{ + AccessibleEventObject aEvt; + aEvt.Source = static_cast<XAccessible *>(this); + aEvt.EventId = nAccessibleEventId; + aEvt.OldValue = rOldVal; + aEvt.NewValue = rNewVal ; + + // pass event on to event-listener's + if (nClientId) + comphelper::AccessibleEventNotifier::addEvent( nClientId, aEvt ); +} + +uno::Reference< XAccessibleContext > SAL_CALL SmGraphicAccessible::getAccessibleContext() +{ + return this; +} + +sal_Bool SAL_CALL SmGraphicAccessible::containsPoint( const awt::Point& aPoint ) +{ + //! the arguments coordinates are relative to the current window ! + //! Thus the top-left point is (0, 0) + + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + Size aSz( pWin->GetSizePixel() ); + return aPoint.X >= 0 && aPoint.Y >= 0 && + aPoint.X < aSz.Width() && aPoint.Y < aSz.Height(); +} + +uno::Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleAtPoint( + const awt::Point& aPoint ) +{ + SolarMutexGuard aGuard; + XAccessible *pRes = nullptr; + if (containsPoint( aPoint )) + pRes = this; + return pRes; +} + +awt::Rectangle SAL_CALL SmGraphicAccessible::getBounds() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + return lcl_GetBounds( pWin ); +} + +awt::Point SAL_CALL SmGraphicAccessible::getLocation() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + awt::Rectangle aRect( lcl_GetBounds( pWin ) ); + return awt::Point( aRect.X, aRect.Y ); +} + +awt::Point SAL_CALL SmGraphicAccessible::getLocationOnScreen() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + return lcl_GetLocationOnScreen( pWin ); +} + +awt::Size SAL_CALL SmGraphicAccessible::getSize() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + + Size aSz( pWin->GetSizePixel() ); +#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG + awt::Rectangle aRect( lcl_GetBounds( pWin ) ); + Size aSz2( aRect.Width, aRect.Height ); + assert(aSz == aSz2 && "mismatch in width" ); +#endif + return awt::Size( aSz.Width(), aSz.Height() ); +} + +void SAL_CALL SmGraphicAccessible::grabFocus() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + pWin->GrabFocus(); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getForeground() +{ + SolarMutexGuard aGuard; + + if (!pWin) + throw RuntimeException(); + return static_cast<sal_Int32>(pWin->GetTextColor()); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getBackground() +{ + SolarMutexGuard aGuard; + + if (!pWin) + throw RuntimeException(); + Wallpaper aWall( pWin->GetDisplayBackground() ); + Color nCol; + if (aWall.IsBitmap() || aWall.IsGradient()) + nCol = pWin->GetSettings().GetStyleSettings().GetWindowColor(); + else + nCol = aWall.GetColor(); + return static_cast<sal_Int32>(nCol); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleChildCount() +{ + return 0; +} + +Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleChild( + sal_Int32 /*i*/ ) +{ + throw IndexOutOfBoundsException(); // there is no child... +} + +Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleParent() +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + vcl::Window *pAccParent = pWin->GetAccessibleParentWindow(); + OSL_ENSURE( pAccParent, "accessible parent missing" ); + return pAccParent ? pAccParent->GetAccessible() : Reference< XAccessible >(); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleIndexInParent() +{ + SolarMutexGuard aGuard; + sal_Int32 nIdx = -1; + vcl::Window *pAccParent = pWin ? pWin->GetAccessibleParentWindow() : nullptr; + if (pAccParent) + { + sal_uInt16 nCnt = pAccParent->GetAccessibleChildWindowCount(); + for (sal_uInt16 i = 0; i < nCnt && nIdx == -1; ++i) + if (pAccParent->GetAccessibleChildWindow( i ) == pWin) + nIdx = i; + } + return nIdx; +} + +sal_Int16 SAL_CALL SmGraphicAccessible::getAccessibleRole() +{ + return AccessibleRole::DOCUMENT; +} + +OUString SAL_CALL SmGraphicAccessible::getAccessibleDescription() +{ + SolarMutexGuard aGuard; + SmDocShell *pDoc = GetDoc_Impl(); + return pDoc ? pDoc->GetText() : OUString(); +} + +OUString SAL_CALL SmGraphicAccessible::getAccessibleName() +{ + SolarMutexGuard aGuard; + return aAccName; +} + +Reference< XAccessibleRelationSet > SAL_CALL SmGraphicAccessible::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + Reference< XAccessibleRelationSet > xRelSet = new utl::AccessibleRelationSetHelper(); + return xRelSet; // empty relation set +} + +Reference< XAccessibleStateSet > SAL_CALL SmGraphicAccessible::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + ::utl::AccessibleStateSetHelper *pStateSet = + new ::utl::AccessibleStateSetHelper; + + Reference<XAccessibleStateSet> xStateSet( pStateSet ); + + if (!pWin) + pStateSet->AddState( AccessibleStateType::DEFUNC ); + else + { + pStateSet->AddState( AccessibleStateType::ENABLED ); + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + if (pWin->HasFocus()) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + if (pWin->IsActive()) + pStateSet->AddState( AccessibleStateType::ACTIVE ); + if (pWin->IsVisible()) + pStateSet->AddState( AccessibleStateType::SHOWING ); + if (pWin->IsReallyVisible()) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + if (COL_TRANSPARENT != pWin->GetBackground().GetColor()) + pStateSet->AddState( AccessibleStateType::OPAQUE ); + } + + return xStateSet; +} + +Locale SAL_CALL SmGraphicAccessible::getLocale() +{ + SolarMutexGuard aGuard; + // should be the document language... + // We use the language of the localized symbol names here. + return Application::GetSettings().GetUILanguageTag().getLocale(); +} + + +void SAL_CALL SmGraphicAccessible::addAccessibleEventListener( + const Reference< XAccessibleEventListener >& xListener ) +{ + if (xListener.is()) + { + SolarMutexGuard aGuard; + if (pWin) + { + if (!nClientId) + nClientId = comphelper::AccessibleEventNotifier::registerClient( ); + comphelper::AccessibleEventNotifier::addEventListener( nClientId, xListener ); + } + } +} + +void SAL_CALL SmGraphicAccessible::removeAccessibleEventListener( + const Reference< XAccessibleEventListener >& xListener ) +{ + if (!(xListener.is() && nClientId)) + return; + + SolarMutexGuard aGuard; + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( nClientId, xListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( nClientId ); + nClientId = 0; + } +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getCaretPosition() +{ + return 0; +} + +sal_Bool SAL_CALL SmGraphicAccessible::setCaretPosition( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + if (nIndex >= aTxt.getLength()) + throw IndexOutOfBoundsException(); + return false; +} + +sal_Unicode SAL_CALL SmGraphicAccessible::getCharacter( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + if (nIndex >= aTxt.getLength()) + throw IndexOutOfBoundsException(); + return aTxt[nIndex]; +} + +Sequence< beans::PropertyValue > SAL_CALL SmGraphicAccessible::getCharacterAttributes( + sal_Int32 nIndex, + const uno::Sequence< OUString > & /*rRequestedAttributes*/ ) +{ + SolarMutexGuard aGuard; + sal_Int32 nLen = GetAccessibleText_Impl().getLength(); + if (!(0 <= nIndex && nIndex < nLen)) + throw IndexOutOfBoundsException(); + return Sequence< beans::PropertyValue >(); +} + +awt::Rectangle SAL_CALL SmGraphicAccessible::getCharacterBounds( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + awt::Rectangle aRes; + + if (!pWin) + throw RuntimeException(); + + // get accessible text + SmViewShell *pView = pWin->GetView(); + SmDocShell *pDoc = pView ? pView->GetDoc() : nullptr; + if (!pDoc) + throw RuntimeException(); + OUString aTxt( GetAccessibleText_Impl() ); + if (!(0 <= nIndex && nIndex <= aTxt.getLength())) // aTxt.getLength() is valid + throw IndexOutOfBoundsException(); + + // find a reasonable rectangle for position aTxt.getLength(). + bool bWasBehindText = (nIndex == aTxt.getLength()); + if (bWasBehindText && nIndex) + --nIndex; + + const SmNode *pTree = pDoc->GetFormulaTree(); + const SmNode *pNode = pTree->FindNodeWithAccessibleIndex( nIndex ); + //! pNode may be 0 if the index belongs to a char that was inserted + //! only for the accessible text! + if (pNode) + { + sal_Int32 nAccIndex = pNode->GetAccessibleIndex(); + OSL_ENSURE( nAccIndex >= 0, "invalid accessible index" ); + OSL_ENSURE( nIndex >= nAccIndex, "index out of range" ); + + OUStringBuffer aBuf; + pNode->GetAccessibleText(aBuf); + OUString aNodeText = aBuf.makeStringAndClear(); + sal_Int32 nNodeIndex = nIndex - nAccIndex; + if (0 <= nNodeIndex && nNodeIndex < aNodeText.getLength()) + { + // get appropriate rectangle + Point aOffset(pNode->GetTopLeft() - pTree->GetTopLeft()); + Point aTLPos (pWin->GetFormulaDrawPos() + aOffset); + Size aSize (pNode->GetSize()); + + std::unique_ptr<long[]> pXAry(new long[ aNodeText.getLength() ]); + pWin->SetFont( pNode->GetFont() ); + pWin->GetTextArray( aNodeText, pXAry.get(), 0, aNodeText.getLength() ); + aTLPos.AdjustX(nNodeIndex > 0 ? pXAry[nNodeIndex - 1] : 0 ); + aSize.setWidth( nNodeIndex > 0 ? pXAry[nNodeIndex] - pXAry[nNodeIndex - 1] : pXAry[nNodeIndex] ); + pXAry.reset(); + + aTLPos = pWin->LogicToPixel( aTLPos ); + aSize = pWin->LogicToPixel( aSize ); + aRes.X = aTLPos.X(); + aRes.Y = aTLPos.Y(); + aRes.Width = aSize.Width(); + aRes.Height = aSize.Height(); + } + } + + // take rectangle from last character and move it to the right + if (bWasBehindText) + aRes.X += aRes.Width; + + return aRes; +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getCharacterCount() +{ + SolarMutexGuard aGuard; + return GetAccessibleText_Impl().getLength(); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getIndexAtPoint( const awt::Point& aPoint ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nRes = -1; + if (pWin) + { + const SmNode *pTree = pWin->GetView()->GetDoc()->GetFormulaTree(); + // can be NULL! e.g. if one clicks within the window already during loading of the + // document (before the parser even started) + if (!pTree) + return nRes; + + // get position relative to formula draw position + Point aPos( aPoint.X, aPoint.Y ); + aPos = pWin->PixelToLogic( aPos ); + aPos -= pWin->GetFormulaDrawPos(); + + // if it was inside the formula then get the appropriate node + const SmNode *pNode = nullptr; + if (pTree->OrientedDist(aPos) <= 0) + pNode = pTree->FindRectClosestTo(aPos); + + if (pNode) + { + // get appropriate rectangle + Point aOffset( pNode->GetTopLeft() - pTree->GetTopLeft() ); + Point aTLPos ( aOffset ); + Size aSize( pNode->GetSize() ); + + tools::Rectangle aRect( aTLPos, aSize ); + if (aRect.IsInside( aPos )) + { + OSL_ENSURE( pNode->IsVisible(), "node is not a leaf" ); + OUStringBuffer aBuf; + pNode->GetAccessibleText(aBuf); + OUString aTxt = aBuf.makeStringAndClear(); + OSL_ENSURE( !aTxt.isEmpty(), "no accessible text available" ); + + long nNodeX = pNode->GetLeft(); + + std::unique_ptr<long[]> pXAry(new long[ aTxt.getLength() ]); + pWin->SetFont( pNode->GetFont() ); + pWin->GetTextArray( aTxt, pXAry.get(), 0, aTxt.getLength() ); + for (sal_Int32 i = 0; i < aTxt.getLength() && nRes == -1; ++i) + { + if (pXAry[i] + nNodeX > aPos.X()) + nRes = i; + } + pXAry.reset(); + OSL_ENSURE( nRes >= 0 && nRes < aTxt.getLength(), "index out of range" ); + OSL_ENSURE( pNode->GetAccessibleIndex() >= 0, + "invalid accessible index" ); + + nRes = pNode->GetAccessibleIndex() + nRes; + } + } + } + return nRes; +} + +OUString SAL_CALL SmGraphicAccessible::getSelectedText() +{ + return OUString(); +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionStart() +{ + return -1; +} + +sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionEnd() +{ + return -1; +} + +sal_Bool SAL_CALL SmGraphicAccessible::setSelection( + sal_Int32 nStartIndex, + sal_Int32 nEndIndex ) +{ + SolarMutexGuard aGuard; + sal_Int32 nLen = GetAccessibleText_Impl().getLength(); + if (!(0 <= nStartIndex && nStartIndex < nLen) || + !(0 <= nEndIndex && nEndIndex < nLen)) + throw IndexOutOfBoundsException(); + return false; +} + +OUString SAL_CALL SmGraphicAccessible::getText() +{ + SolarMutexGuard aGuard; + return GetAccessibleText_Impl(); +} + +OUString SAL_CALL SmGraphicAccessible::getTextRange( + sal_Int32 nStartIndex, + sal_Int32 nEndIndex ) +{ + //!! nEndIndex may be the string length per definition of the interface !! + //!! text should be copied exclusive that end index though. And arguments + //!! may be switched. + + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + sal_Int32 nStart = std::min(nStartIndex, nEndIndex); + sal_Int32 nEnd = std::max(nStartIndex, nEndIndex); + if ((nStart > aTxt.getLength()) || + (nEnd > aTxt.getLength())) + throw IndexOutOfBoundsException(); + return aTxt.copy( nStart, nEnd - nStart ); +} + +css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + //!! nIndex is allowed to be the string length + if (nIndex > aTxt.getLength()) + throw IndexOutOfBoundsException(); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) ) + { + aResult.SegmentText = aTxt.copy(nIndex, 1); + aResult.SegmentStart = nIndex; + aResult.SegmentEnd = nIndex+1; + } + return aResult; +} + +css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + //!! nIndex is allowed to be the string length + if (nIndex > aTxt.getLength()) + throw IndexOutOfBoundsException(); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + if ( (AccessibleTextType::CHARACTER == aTextType) && nIndex ) + { + aResult.SegmentText = aTxt.copy(nIndex-1, 1); + aResult.SegmentStart = nIndex-1; + aResult.SegmentEnd = nIndex; + } + return aResult; +} + +css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) +{ + SolarMutexGuard aGuard; + OUString aTxt( GetAccessibleText_Impl() ); + //!! nIndex is allowed to be the string length + if (nIndex > aTxt.getLength()) + throw IndexOutOfBoundsException(); + + css::accessibility::TextSegment aResult; + aResult.SegmentStart = -1; + aResult.SegmentEnd = -1; + + nIndex++; // text *behind* + if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) ) + { + aResult.SegmentText = aTxt.copy(nIndex, 1); + aResult.SegmentStart = nIndex; + aResult.SegmentEnd = nIndex+1; + } + return aResult; +} + +sal_Bool SAL_CALL SmGraphicAccessible::copyText( + sal_Int32 nStartIndex, + sal_Int32 nEndIndex ) +{ + SolarMutexGuard aGuard; + bool bReturn = false; + + if (!pWin) + throw RuntimeException(); + + Reference< datatransfer::clipboard::XClipboard > xClipboard = pWin->GetClipboard(); + if ( xClipboard.is() ) + { + OUString sText( getTextRange(nStartIndex, nEndIndex) ); + + vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( sText ); + SolarMutexReleaser aReleaser; + xClipboard->setContents( pDataObj, nullptr ); + + Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( xClipboard, uno::UNO_QUERY ); + if( xFlushableClipboard.is() ) + xFlushableClipboard->flushClipboard(); + + bReturn = true; + } + + + return bReturn; +} + +sal_Bool SAL_CALL SmGraphicAccessible::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ) +{ + return false; +} + +OUString SAL_CALL SmGraphicAccessible::getImplementationName() +{ + return "SmGraphicAccessible"; +} + +sal_Bool SAL_CALL SmGraphicAccessible::supportsService( + const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL SmGraphicAccessible::getSupportedServiceNames() +{ + return { + "css::accessibility::Accessible", + "css::accessibility::AccessibleComponent", + "css::accessibility::AccessibleContext", + "css::accessibility::AccessibleText" + }; +} + + +SmEditSource::SmEditSource( SmEditAccessible &rAcc ) : + aViewFwd (rAcc), + aTextFwd (rAcc, *this), + aEditViewFwd(rAcc), + rEditAcc (rAcc) +{ +} + +SmEditSource::SmEditSource( const SmEditSource &rSrc ) : + SvxEditSource(), + aViewFwd (rSrc.rEditAcc), + aTextFwd (rSrc.rEditAcc, *this), + aEditViewFwd(rSrc.rEditAcc), + rEditAcc (rSrc.rEditAcc) +{ +} + +SmEditSource::~SmEditSource() +{ +} + +std::unique_ptr<SvxEditSource> SmEditSource::Clone() const +{ + return std::unique_ptr<SvxEditSource>(new SmEditSource( *this )); +} + +SvxTextForwarder* SmEditSource::GetTextForwarder() +{ + return &aTextFwd; +} + +SvxViewForwarder* SmEditSource::GetViewForwarder() +{ + return &aViewFwd; +} + +SvxEditViewForwarder* SmEditSource::GetEditViewForwarder( bool /*bCreate*/ ) +{ + return &aEditViewFwd; +} + +void SmEditSource::UpdateData() +{ + // would possibly only by needed if the XText interface is implemented + // and its text needs to be updated. +} + +SfxBroadcaster & SmEditSource::GetBroadcaster() const +{ + return const_cast<SmEditSource*>(this)->aBroadCaster; +} + +SmViewForwarder::SmViewForwarder( SmEditAccessible &rAcc ) : + rEditAcc(rAcc) +{ +} + +SmViewForwarder::~SmViewForwarder() +{ +} + +bool SmViewForwarder::IsValid() const +{ + return rEditAcc.GetEditView() != nullptr; +} + +Point SmViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const +{ + EditView *pEditView = rEditAcc.GetEditView(); + OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr; + + if( pOutDev ) + { + MapMode aMapMode(pOutDev->GetMapMode()); + Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode, + MapMode(aMapMode.GetMapUnit())) ); + aMapMode.SetOrigin(Point()); + return pOutDev->LogicToPixel( aPoint, aMapMode ); + } + + return Point(); +} + +Point SmViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const +{ + EditView *pEditView = rEditAcc.GetEditView(); + OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr; + + if( pOutDev ) + { + MapMode aMapMode(pOutDev->GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint( pOutDev->PixelToLogic( rPoint, aMapMode ) ); + return OutputDevice::LogicToLogic( aPoint, + MapMode(aMapMode.GetMapUnit()), + rMapMode ); + } + + return Point(); +} + + +SmTextForwarder::SmTextForwarder( SmEditAccessible& rAcc, SmEditSource & rSource) : + rEditAcc ( rAcc ), + rEditSource (rSource) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetNotifyHdl( LINK(this, SmTextForwarder, NotifyHdl) ); +} + +SmTextForwarder::~SmTextForwarder() +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetNotifyHdl( Link<EENotify&,void>() ); +} + +IMPL_LINK(SmTextForwarder, NotifyHdl, EENotify&, rNotify, void) +{ + ::std::unique_ptr< SfxHint > aHint = SvxEditSourceHelper::EENotification2Hint( &rNotify ); + if (aHint) + rEditSource.GetBroadcaster().Broadcast(*aHint); +} + +sal_Int32 SmTextForwarder::GetParagraphCount() const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetParagraphCount() : 0; +} + +sal_Int32 SmTextForwarder::GetTextLen( sal_Int32 nParagraph ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetTextLen( nParagraph ) : 0; +} + +OUString SmTextForwarder::GetText( const ESelection& rSel ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + OUString aRet; + if (pEditEngine) + aRet = pEditEngine->GetText( rSel ); + return convertLineEnd(aRet, GetSystemLineEnd()); +} + +SfxItemSet SmTextForwarder::GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + assert(pEditEngine && "EditEngine missing"); + if( rSel.nStartPara == rSel.nEndPara ) + { + GetAttribsFlags nFlags = GetAttribsFlags::NONE; + switch( nOnlyHardAttrib ) + { + case EditEngineAttribs::All: + nFlags = GetAttribsFlags::ALL; + break; + case EditEngineAttribs::OnlyHard: + nFlags = GetAttribsFlags::CHARATTRIBS; + break; + default: + SAL_WARN("starmath", "unknown flags for SmTextForwarder::GetAttribs"); + } + + return pEditEngine->GetAttribs( rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags ); + } + else + { + return pEditEngine->GetAttribs( rSel, nOnlyHardAttrib ); + } +} + +SfxItemSet SmTextForwarder::GetParaAttribs( sal_Int32 nPara ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + assert(pEditEngine && "EditEngine missing"); + + SfxItemSet aSet( pEditEngine->GetParaAttribs( nPara ) ); + + sal_uInt16 nWhich = EE_PARA_START; + while( nWhich <= EE_PARA_END ) + { + if( aSet.GetItemState( nWhich ) != SfxItemState::SET ) + { + if( pEditEngine->HasParaAttrib( nPara, nWhich ) ) + aSet.Put( pEditEngine->GetParaAttrib( nPara, nWhich ) ); + } + nWhich++; + } + + return aSet; +} + +void SmTextForwarder::SetParaAttribs( sal_Int32 nPara, const SfxItemSet& rSet ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->SetParaAttribs( nPara, rSet ); +} + +SfxItemPool* SmTextForwarder::GetPool() const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetEmptyItemSet().GetPool() : nullptr; +} + +void SmTextForwarder::RemoveAttribs( const ESelection& rSelection ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->RemoveAttribs( rSelection, false/*bRemoveParaAttribs*/, 0 ); +} + +void SmTextForwarder::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->GetPortions( nPara, rList ); +} + +void SmTextForwarder::QuickInsertText( const OUString& rText, const ESelection& rSel ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertText( rText, rSel ); +} + +void SmTextForwarder::QuickInsertLineBreak( const ESelection& rSel ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertLineBreak( rSel ); +} + +void SmTextForwarder::QuickInsertField( const SvxFieldItem& rFld, const ESelection& rSel ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickInsertField( rFld, rSel ); +} + +void SmTextForwarder::QuickSetAttribs( const SfxItemSet& rSet, const ESelection& rSel ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->QuickSetAttribs( rSet, rSel ); +} + +bool SmTextForwarder::IsValid() const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + // cannot reliably query EditEngine state + // while in the middle of an update + return pEditEngine && pEditEngine->GetUpdateMode(); +} + +OUString SmTextForwarder::CalcFieldValue( const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor ) +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor) : OUString(); +} + +void SmTextForwarder::FieldClicked(const SvxFieldItem&) +{ +} + +static SfxItemState GetSvxEditEngineItemState( EditEngine const & rEditEngine, const ESelection& rSel, sal_uInt16 nWhich ) +{ + std::vector<EECharAttrib> aAttribs; + + const SfxPoolItem* pLastItem = nullptr; + + SfxItemState eState = SfxItemState::DEFAULT; + + // check all paragraphs inside the selection + for( sal_Int32 nPara = rSel.nStartPara; nPara <= rSel.nEndPara; nPara++ ) + { + SfxItemState eParaState = SfxItemState::DEFAULT; + + // calculate start and endpos for this paragraph + sal_Int32 nPos = 0; + if( rSel.nStartPara == nPara ) + nPos = rSel.nStartPos; + + sal_Int32 nEndPos = rSel.nEndPos; + if( rSel.nEndPara != nPara ) + nEndPos = rEditEngine.GetTextLen( nPara ); + + + // get list of char attribs + rEditEngine.GetCharAttribs( nPara, aAttribs ); + + bool bEmpty = true; // we found no item inside the selection of this paragraph + bool bGaps = false; // we found items but there are gaps between them + sal_Int32 nLastEnd = nPos; + + const SfxPoolItem* pParaItem = nullptr; + + for(const auto& rAttrib : aAttribs) + { + OSL_ENSURE( rAttrib.pAttr, "GetCharAttribs gives corrupt data" ); + + const bool bEmptyPortion = (rAttrib.nStart == rAttrib.nEnd); + if( (!bEmptyPortion && (rAttrib.nStart >= nEndPos)) || (bEmptyPortion && (rAttrib.nStart > nEndPos)) ) + break; // break if we are already behind our selection + + if( (!bEmptyPortion && (rAttrib.nEnd <= nPos)) || (bEmptyPortion && (rAttrib.nEnd < nPos)) ) + continue; // or if the attribute ends before our selection + + if( rAttrib.pAttr->Which() != nWhich ) + continue; // skip if is not the searched item + + // if we already found an item + if( pParaItem ) + { + // ... and its different to this one than the state is don't care + if( *pParaItem != *(rAttrib.pAttr) ) + return SfxItemState::DONTCARE; + } + else + { + pParaItem = rAttrib.pAttr; + } + + if( bEmpty ) + bEmpty = false; + + if( !bGaps && rAttrib.nStart > nLastEnd ) + bGaps = true; + + nLastEnd = rAttrib.nEnd; + } + + if( !bEmpty && !bGaps && nLastEnd < ( nEndPos - 1 ) ) + bGaps = true; + if( bEmpty ) + eParaState = SfxItemState::DEFAULT; + else if( bGaps ) + eParaState = SfxItemState::DONTCARE; + else + eParaState = SfxItemState::SET; + + // if we already found an item check if we found the same + if( pLastItem ) + { + if( (pParaItem == nullptr) || (*pLastItem != *pParaItem) ) + return SfxItemState::DONTCARE; + } + else + { + pLastItem = pParaItem; + eState = eParaState; + } + } + + return eState; +} + +SfxItemState SmTextForwarder::GetItemState( const ESelection& rSel, sal_uInt16 nWhich ) const +{ + SfxItemState nState = SfxItemState::DISABLED; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + nState = GetSvxEditEngineItemState( *pEditEngine, rSel, nWhich ); + return nState; +} + +SfxItemState SmTextForwarder::GetItemState( sal_Int32 nPara, sal_uInt16 nWhich ) const +{ + SfxItemState nState = SfxItemState::DISABLED; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + const SfxItemSet& rSet = pEditEngine->GetParaAttribs( nPara ); + nState = rSet.GetItemState( nWhich ); + } + return nState; +} + +LanguageType SmTextForwarder::GetLanguage( sal_Int32 nPara, sal_Int32 nIndex ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLanguage(nPara, nIndex) : LANGUAGE_NONE; +} + +sal_Int32 SmTextForwarder::GetFieldCount( sal_Int32 nPara ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetFieldCount(nPara) : 0; +} + +EFieldInfo SmTextForwarder::GetFieldInfo( sal_Int32 nPara, sal_uInt16 nField ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetFieldInfo( nPara, nField ) : EFieldInfo(); +} + +EBulletInfo SmTextForwarder::GetBulletInfo( sal_Int32 /*nPara*/ ) const +{ + return EBulletInfo(); +} + +tools::Rectangle SmTextForwarder::GetCharBounds( sal_Int32 nPara, sal_Int32 nIndex ) const +{ + tools::Rectangle aRect(0,0,0,0); + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + + if (pEditEngine) + { + // Handle virtual position one-past-the end of the string + if( nIndex >= pEditEngine->GetTextLen(nPara) ) + { + if( nIndex ) + aRect = pEditEngine->GetCharacterBounds( EPosition(nPara, nIndex-1) ); + + aRect.Move( aRect.Right() - aRect.Left(), 0 ); + aRect.SetSize( Size(1, pEditEngine->GetTextHeight()) ); + } + else + { + aRect = pEditEngine->GetCharacterBounds( EPosition(nPara, nIndex) ); + } + } + return aRect; +} + +tools::Rectangle SmTextForwarder::GetParaBounds( sal_Int32 nPara ) const +{ + tools::Rectangle aRect(0,0,0,0); + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + + if (pEditEngine) + { + const Point aPnt = pEditEngine->GetDocPosTopLeft( nPara ); + const sal_uLong nWidth = pEditEngine->CalcTextWidth(); + const sal_uLong nHeight = pEditEngine->GetTextHeight( nPara ); + aRect = tools::Rectangle( aPnt.X(), aPnt.Y(), aPnt.X() + nWidth, aPnt.Y() + nHeight ); + } + + return aRect; +} + +MapMode SmTextForwarder::GetMapMode() const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetRefMapMode() : MapMode( MapUnit::Map100thMM ); +} + +OutputDevice* SmTextForwarder::GetRefDevice() const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetRefDevice() : nullptr; +} + +bool SmTextForwarder::GetIndexAtPoint( const Point& rPos, sal_Int32& nPara, sal_Int32& nIndex ) const +{ + bool bRes = false; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + EPosition aDocPos = pEditEngine->FindDocPosition( rPos ); + nPara = aDocPos.nPara; + nIndex = aDocPos.nIndex; + bRes = true; + } + return bRes; +} + +bool SmTextForwarder::GetWordIndices( sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart, sal_Int32& nEnd ) const +{ + bool bRes = false; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + ESelection aRes = pEditEngine->GetWord( ESelection(nPara, nIndex, nPara, nIndex), css::i18n::WordType::DICTIONARY_WORD ); + + if( aRes.nStartPara == nPara && + aRes.nStartPara == aRes.nEndPara ) + { + nStart = aRes.nStartPos; + nEnd = aRes.nEndPos; + + bRes = true; + } + } + + return bRes; +} + +bool SmTextForwarder::GetAttributeRun( sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara, sal_Int32 nIndex, bool bInCell ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (!pEditEngine) + return false; + SvxEditSourceHelper::GetAttributeRun( nStartIndex, nEndIndex, *pEditEngine, nPara, nIndex, bInCell ); + return true; +} + +sal_Int32 SmTextForwarder::GetLineCount( sal_Int32 nPara ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineCount(nPara) : 0; +} + +sal_Int32 SmTextForwarder::GetLineLen( sal_Int32 nPara, sal_Int32 nLine ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineLen(nPara, nLine) : 0; +} + +void SmTextForwarder::GetLineBoundaries( /*out*/sal_Int32 &rStart, /*out*/sal_Int32 &rEnd, sal_Int32 nPara, sal_Int32 nLine ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + pEditEngine->GetLineBoundaries(rStart, rEnd, nPara, nLine); + else + rStart = rEnd = 0; +} + +sal_Int32 SmTextForwarder::GetLineNumberAtIndex( sal_Int32 nPara, sal_Int32 nIndex ) const +{ + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + return pEditEngine ? pEditEngine->GetLineNumberAtIndex(nPara, nIndex) : 0; +} + +bool SmTextForwarder::QuickFormatDoc( bool /*bFull*/ ) +{ + bool bRes = false; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +sal_Int16 SmTextForwarder::GetDepth( sal_Int32 /*nPara*/ ) const +{ + // math has no outliner... + return -1; +} + +bool SmTextForwarder::SetDepth( sal_Int32 /*nPara*/, sal_Int16 nNewDepth ) +{ + // math has no outliner... + return -1 == nNewDepth; // is it the value from 'GetDepth' ? +} + +bool SmTextForwarder::Delete( const ESelection& rSelection ) +{ + bool bRes = false; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickDelete( rSelection ); + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +bool SmTextForwarder::InsertText( const OUString& rStr, const ESelection& rSelection ) +{ + bool bRes = false; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pEditEngine->QuickInsertText( rStr, rSelection ); + pEditEngine->QuickFormatDoc(); + bRes = true; + } + return bRes; +} + +const SfxItemSet* SmTextForwarder::GetEmptyItemSetPtr() +{ + const SfxItemSet *pItemSet = nullptr; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + pItemSet = &pEditEngine->GetEmptyItemSet(); + } + return pItemSet; +} + +void SmTextForwarder::AppendParagraph() +{ + // append an empty paragraph + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine) + { + sal_Int32 nParaCount = pEditEngine->GetParagraphCount(); + pEditEngine->InsertParagraph( nParaCount, OUString() ); + } +} + +sal_Int32 SmTextForwarder::AppendTextPortion( sal_Int32 nPara, const OUString &rText, const SfxItemSet &rSet ) +{ + sal_uInt16 nRes = 0; + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine && nPara < pEditEngine->GetParagraphCount()) + { + // append text + ESelection aSel( nPara, pEditEngine->GetTextLen( nPara ) ); + pEditEngine->QuickInsertText( rText, aSel ); + + // set attributes for new appended text + nRes = aSel.nEndPos = pEditEngine->GetTextLen( nPara ); + pEditEngine->QuickSetAttribs( rSet, aSel ); + } + return nRes; +} + +void SmTextForwarder::CopyText(const SvxTextForwarder& rSource) +{ + + const SmTextForwarder* pSourceForwarder = dynamic_cast< const SmTextForwarder* >( &rSource ); + if( !pSourceForwarder ) + return; + EditEngine* pSourceEditEngine = pSourceForwarder->rEditAcc.GetEditEngine(); + EditEngine *pEditEngine = rEditAcc.GetEditEngine(); + if (pEditEngine && pSourceEditEngine ) + { + std::unique_ptr<EditTextObject> pNewTextObject = pSourceEditEngine->CreateTextObject(); + pEditEngine->SetText( *pNewTextObject ); + } +} + + +SmEditViewForwarder::SmEditViewForwarder( SmEditAccessible& rAcc ) : + rEditAcc( rAcc ) +{ +} + +SmEditViewForwarder::~SmEditViewForwarder() +{ +} + +bool SmEditViewForwarder::IsValid() const +{ + return rEditAcc.GetEditView() != nullptr; +} + + +Point SmEditViewForwarder::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const +{ + EditView *pEditView = rEditAcc.GetEditView(); + OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr; + + if( pOutDev ) + { + MapMode aMapMode(pOutDev->GetMapMode()); + Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode, + MapMode(aMapMode.GetMapUnit()))); + aMapMode.SetOrigin(Point()); + return pOutDev->LogicToPixel( aPoint, aMapMode ); + } + + return Point(); +} + +Point SmEditViewForwarder::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const +{ + EditView *pEditView = rEditAcc.GetEditView(); + OutputDevice* pOutDev = pEditView ? pEditView->GetWindow() : nullptr; + + if( pOutDev ) + { + MapMode aMapMode(pOutDev->GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint( pOutDev->PixelToLogic( rPoint, aMapMode ) ); + return OutputDevice::LogicToLogic( aPoint, + MapMode(aMapMode.GetMapUnit()), + rMapMode ); + } + + return Point(); +} + +bool SmEditViewForwarder::GetSelection( ESelection& rSelection ) const +{ + bool bRes = false; + EditView *pEditView = rEditAcc.GetEditView(); + if (pEditView) + { + rSelection = pEditView->GetSelection(); + bRes = true; + } + return bRes; +} + +bool SmEditViewForwarder::SetSelection( const ESelection& rSelection ) +{ + bool bRes = false; + EditView *pEditView = rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->SetSelection( rSelection ); + bRes = true; + } + return bRes; +} + +bool SmEditViewForwarder::Copy() +{ + bool bRes = false; + EditView *pEditView = rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Copy(); + bRes = true; + } + return bRes; +} + +bool SmEditViewForwarder::Cut() +{ + bool bRes = false; + EditView *pEditView = rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Cut(); + bRes = true; + } + return bRes; +} + +bool SmEditViewForwarder::Paste() +{ + bool bRes = false; + EditView *pEditView = rEditAcc.GetEditView(); + if (pEditView) + { + pEditView->Paste(); + bRes = true; + } + return bRes; +} + + +SmEditAccessible::SmEditAccessible( SmEditWindow *pEditWin ) : + aAccName (SmResId(STR_CMDBOXWINDOW)), + pTextHelper (), + pWin (pEditWin) +{ + OSL_ENSURE( pWin, "SmEditAccessible: window missing" ); +} + +SmEditAccessible::~SmEditAccessible() +{ +} + +::accessibility::AccessibleTextHelper *SmEditAccessible::GetTextHelper() +{ + return pTextHelper.get(); +} + +void SmEditAccessible::Init() +{ + OSL_ENSURE( pWin, "SmEditAccessible: window missing" ); + if (pWin) + { + EditEngine *pEditEngine = pWin->GetEditEngine(); + EditView *pEditView = pWin->GetEditView(); + if (pEditEngine && pEditView) + { + assert(!pTextHelper); + pTextHelper.reset(new ::accessibility::AccessibleTextHelper( std::make_unique<SmEditSource>( *this ) )); + pTextHelper->SetEventSource( this ); + } + } +} + +void SmEditAccessible::ClearWin() +{ + // remove handler before current object gets destroyed + // (avoid handler being called for already dead object) + EditEngine *pEditEngine = GetEditEngine(); + if (pEditEngine) + pEditEngine->SetNotifyHdl( Link<EENotify&,void>() ); + + pWin = nullptr; // implicitly results in AccessibleStateType::DEFUNC set + + //! make TextHelper implicitly release C++ references to some core objects + pTextHelper->SetEditSource( ::std::unique_ptr<SvxEditSource>() ); + //! make TextHelper release references + //! (e.g. the one set by the 'SetEventSource' call) + pTextHelper->Dispose(); + pTextHelper.reset(); +} + +// XAccessible +uno::Reference< XAccessibleContext > SAL_CALL SmEditAccessible::getAccessibleContext( ) +{ + return this; +} + +// XAccessibleComponent +sal_Bool SAL_CALL SmEditAccessible::containsPoint( const awt::Point& aPoint ) +{ + //! the arguments coordinates are relative to the current window ! + //! Thus the top left-point is (0, 0) + + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + Size aSz( pWin->GetSizePixel() ); + return aPoint.X >= 0 && aPoint.Y >= 0 && + aPoint.X < aSz.Width() && aPoint.Y < aSz.Height(); +} + +uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleAtPoint( const awt::Point& aPoint ) +{ + SolarMutexGuard aGuard; + if (!pTextHelper) + throw RuntimeException(); + return pTextHelper->GetAt( aPoint ); +} + +awt::Rectangle SAL_CALL SmEditAccessible::getBounds( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + return lcl_GetBounds( pWin ); +} + +awt::Point SAL_CALL SmEditAccessible::getLocation( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + awt::Rectangle aRect( lcl_GetBounds( pWin ) ); + return awt::Point( aRect.X, aRect.Y ); +} + +awt::Point SAL_CALL SmEditAccessible::getLocationOnScreen( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + return lcl_GetLocationOnScreen( pWin ); +} + +awt::Size SAL_CALL SmEditAccessible::getSize( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + OSL_ENSURE(pWin->GetParent()->GetAccessible() == getAccessibleParent(), + "mismatch of window parent and accessible parent" ); + + Size aSz( pWin->GetSizePixel() ); +#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG + awt::Rectangle aRect( lcl_GetBounds( pWin ) ); + Size aSz2( aRect.Width, aRect.Height ); + assert(aSz == aSz2 && "mismatch in width"); +#endif + return awt::Size( aSz.Width(), aSz.Height() ); +} + +void SAL_CALL SmEditAccessible::grabFocus( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + pWin->GrabFocus(); +} + +sal_Int32 SAL_CALL SmEditAccessible::getForeground() +{ + SolarMutexGuard aGuard; + + if (!pWin) + throw RuntimeException(); + return static_cast<sal_Int32>(pWin->GetTextColor()); +} + +sal_Int32 SAL_CALL SmEditAccessible::getBackground() +{ + SolarMutexGuard aGuard; + + if (!pWin) + throw RuntimeException(); + Wallpaper aWall( pWin->GetDisplayBackground() ); + Color nCol; + if (aWall.IsBitmap() || aWall.IsGradient()) + nCol = pWin->GetSettings().GetStyleSettings().GetWindowColor(); + else + nCol = aWall.GetColor(); + return static_cast<sal_Int32>(nCol); +} + +// XAccessibleContext +sal_Int32 SAL_CALL SmEditAccessible::getAccessibleChildCount( ) +{ + SolarMutexGuard aGuard; + if (!pTextHelper) + return 0; + return pTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleChild( sal_Int32 i ) +{ + SolarMutexGuard aGuard; + if (!pTextHelper) + throw RuntimeException(); + return pTextHelper->GetChild( i ); +} + +uno::Reference< XAccessible > SAL_CALL SmEditAccessible::getAccessibleParent( ) +{ + SolarMutexGuard aGuard; + if (!pWin) + throw RuntimeException(); + + vcl::Window *pAccParent = pWin->GetAccessibleParentWindow(); + OSL_ENSURE( pAccParent, "accessible parent missing" ); + return pAccParent ? pAccParent->GetAccessible() : Reference< XAccessible >(); +} + +sal_Int32 SAL_CALL SmEditAccessible::getAccessibleIndexInParent( ) +{ + SolarMutexGuard aGuard; + sal_Int32 nIdx = -1; + vcl::Window *pAccParent = pWin ? pWin->GetAccessibleParentWindow() : nullptr; + if (pAccParent) + { + sal_uInt16 nCnt = pAccParent->GetAccessibleChildWindowCount(); + for (sal_uInt16 i = 0; i < nCnt && nIdx == -1; ++i) + if (pAccParent->GetAccessibleChildWindow( i ) == pWin) + nIdx = i; + } + return nIdx; +} + +sal_Int16 SAL_CALL SmEditAccessible::getAccessibleRole( ) +{ + return AccessibleRole::TEXT_FRAME; +} + +OUString SAL_CALL SmEditAccessible::getAccessibleDescription( ) +{ + return OUString(); // empty as agreed with product-management +} + +OUString SAL_CALL SmEditAccessible::getAccessibleName( ) +{ + SolarMutexGuard aGuard; + // same name as displayed by the window when not docked + return aAccName; +} + +uno::Reference< XAccessibleRelationSet > SAL_CALL SmEditAccessible::getAccessibleRelationSet( ) +{ + Reference< XAccessibleRelationSet > xRelSet = new utl::AccessibleRelationSetHelper(); + return xRelSet; // empty relation set +} + +uno::Reference< XAccessibleStateSet > SAL_CALL SmEditAccessible::getAccessibleStateSet( ) +{ + SolarMutexGuard aGuard; + ::utl::AccessibleStateSetHelper *pStateSet = + new ::utl::AccessibleStateSetHelper; + + Reference<XAccessibleStateSet> xStateSet( pStateSet ); + + if (!pWin || !pTextHelper) + pStateSet->AddState( AccessibleStateType::DEFUNC ); + else + { + pStateSet->AddState( AccessibleStateType::MULTI_LINE ); + pStateSet->AddState( AccessibleStateType::ENABLED ); + pStateSet->AddState( AccessibleStateType::EDITABLE ); + pStateSet->AddState( AccessibleStateType::FOCUSABLE ); + if (pWin->HasFocus()) + pStateSet->AddState( AccessibleStateType::FOCUSED ); + if (pWin->IsActive()) + pStateSet->AddState( AccessibleStateType::ACTIVE ); + if (pWin->IsVisible()) + pStateSet->AddState( AccessibleStateType::SHOWING ); + if (pWin->IsReallyVisible()) + pStateSet->AddState( AccessibleStateType::VISIBLE ); + if (COL_TRANSPARENT != pWin->GetBackground().GetColor()) + pStateSet->AddState( AccessibleStateType::OPAQUE ); + } + + return xStateSet; +} + +Locale SAL_CALL SmEditAccessible::getLocale( ) +{ + SolarMutexGuard aGuard; + // should be the document language... + // We use the language of the localized symbol names here. + return Application::GetSettings().GetUILanguageTag().getLocale(); +} + + +// XAccessibleEventBroadcaster +void SAL_CALL SmEditAccessible::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + if (pTextHelper) // not disposing (about to destroy view shell) + pTextHelper->AddEventListener( xListener ); +} + +void SAL_CALL SmEditAccessible::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + if (pTextHelper) // not disposing (about to destroy view shell) + pTextHelper->RemoveEventListener( xListener ); +} + +OUString SAL_CALL SmEditAccessible::getImplementationName() +{ + return "SmEditAccessible"; +} + +sal_Bool SAL_CALL SmEditAccessible::supportsService( + const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL SmEditAccessible::getSupportedServiceNames() +{ + return { + "css::accessibility::Accessible", + "css::accessibility::AccessibleComponent", + "css::accessibility::AccessibleContext" + }; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |