diff options
Diffstat (limited to 'sc/source/ui/Accessibility/AccessibleEditObject.cxx')
-rw-r--r-- | sc/source/ui/Accessibility/AccessibleEditObject.cxx | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/sc/source/ui/Accessibility/AccessibleEditObject.cxx b/sc/source/ui/Accessibility/AccessibleEditObject.cxx new file mode 100644 index 000000000..8412b313c --- /dev/null +++ b/sc/source/ui/Accessibility/AccessibleEditObject.cxx @@ -0,0 +1,604 @@ +/* -*- 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 <memory> +#include <utility> + +#include <AccessibleEditObject.hxx> +#include <AccessibleText.hxx> +#include <editsrc.hxx> +#include <scmod.hxx> +#include <inputhdl.hxx> +#include <inputwin.hxx> + +#include <unotools/accessiblestatesethelper.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/sheet/XSpreadsheetDocument.hpp> +#include <com/sun/star/sheet/XSpreadsheet.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <svx/AccessibleTextHelper.hxx> +#include <editeng/editview.hxx> +#include <editeng/editeng.hxx> +#include <svx/svdmodel.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <sfx2/objsh.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include <unonames.hxx> +#include <document.hxx> +#include <AccessibleDocument.hxx> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <unotools/accessiblerelationsethelper.hxx> +#include <com/sun/star/accessibility/XAccessibleText.hpp> + +using ::com::sun::star::lang::IndexOutOfBoundsException; +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +//===== internal ============================================================ + +ScAccessibleEditObject::ScAccessibleEditObject( + const uno::Reference<XAccessible>& rxParent, + EditView* pEditView, vcl::Window* pWin, const OUString& rName, + const OUString& rDescription, EditObjectType eObjectType) + : ScAccessibleContextBase(rxParent, AccessibleRole::TEXT_FRAME) + , mpEditView(pEditView) + , mpWindow(pWin) + , mpTextWnd(nullptr) + , meObjectType(eObjectType) + , mbHasFocus(false) + , m_pScDoc(nullptr) +{ + InitAcc(rxParent, pEditView, rName, rDescription); +} + +ScAccessibleEditObject::ScAccessibleEditObject(EditObjectType eObjectType) + : ScAccessibleContextBase(nullptr, AccessibleRole::TEXT_FRAME) + , mpEditView(nullptr) + , mpWindow(nullptr) + , mpTextWnd(nullptr) + , meObjectType(eObjectType) + , mbHasFocus(false) + , m_pScDoc(nullptr) +{ +} + +void ScAccessibleEditObject::InitAcc( + const uno::Reference<XAccessible>& rxParent, + EditView* pEditView, + const OUString& rName, + const OUString& rDescription) +{ + SetParent(rxParent); + mpEditView = pEditView; + + CreateTextHelper(); + SetName(rName); + SetDescription(rDescription); + if( meObjectType == CellInEditMode) + { + const ScAccessibleDocument *pAccDoc = static_cast<ScAccessibleDocument*>(rxParent.get()); + if (pAccDoc) + { + m_pScDoc = pAccDoc->GetDocument(); + m_curCellAddress =pAccDoc->GetCurCellAddress(); + } + } +} + +ScAccessibleEditObject::~ScAccessibleEditObject() +{ + if (!ScAccessibleContextBase::IsDefunc() && !rBHelper.bInDispose) + { + // increment refcount to prevent double call off dtor + osl_atomic_increment( &m_refCount ); + // call dispose to inform object which have a weak reference to this object + dispose(); + } +} + +void SAL_CALL ScAccessibleEditObject::disposing() +{ + SolarMutexGuard aGuard; + mpTextHelper.reset(); + + ScAccessibleContextBase::disposing(); +} + +void ScAccessibleEditObject::LostFocus() +{ + mbHasFocus = false; + if (mpTextHelper) + mpTextHelper->SetFocus(false); + CommitFocusLost(); +} + +void ScAccessibleEditObject::GotFocus() +{ + mbHasFocus = true; + CommitFocusGained(); + if (mpTextHelper) + mpTextHelper->SetFocus(); +} + +//===== XInterface ========================================================== + +css::uno::Any SAL_CALL + ScAccessibleEditObject::queryInterface (const css::uno::Type & rType) +{ + css::uno::Any aReturn = ScAccessibleContextBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast< css::accessibility::XAccessibleSelection* >(this) + ); + return aReturn; +} +void SAL_CALL + ScAccessibleEditObject::acquire() + noexcept +{ + ScAccessibleContextBase::acquire (); +} +void SAL_CALL + ScAccessibleEditObject::release() + noexcept +{ + ScAccessibleContextBase::release (); +} + //===== XAccessibleComponent ============================================ + +uno::Reference< XAccessible > SAL_CALL ScAccessibleEditObject::getAccessibleAtPoint( + const awt::Point& rPoint ) +{ + uno::Reference<XAccessible> xRet; + if (containsPoint(rPoint)) + { + SolarMutexGuard aGuard; + IsObjectValid(); + + CreateTextHelper(); + + xRet = mpTextHelper->GetAt(rPoint); + } + + return xRet; +} + +tools::Rectangle ScAccessibleEditObject::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aScreenBounds; + + if ( mpWindow ) + { + if ( meObjectType == CellInEditMode ) + { + if ( mpEditView && mpEditView->GetEditEngine() ) + { + MapMode aMapMode( mpEditView->GetEditEngine()->GetRefMapMode() ); + aScreenBounds = mpWindow->LogicToPixel( mpEditView->GetOutputArea(), aMapMode ); + Point aCellLoc = aScreenBounds.TopLeft(); + tools::Rectangle aWindowRect = mpWindow->GetWindowExtentsRelative( nullptr ); + Point aWindowLoc = aWindowRect.TopLeft(); + Point aPos( aCellLoc.getX() + aWindowLoc.getX(), aCellLoc.getY() + aWindowLoc.getY() ); + aScreenBounds.SetPos( aPos ); + } + } + else + { + aScreenBounds = mpWindow->GetWindowExtentsRelative( nullptr ); + } + } + + return aScreenBounds; +} + +tools::Rectangle ScAccessibleEditObject::GetBoundingBox() const +{ + tools::Rectangle aBounds( GetBoundingBoxOnScreen() ); + + if ( mpWindow ) + { + uno::Reference< XAccessible > xThis( mpWindow->GetAccessible() ); + if ( xThis.is() ) + { + uno::Reference< XAccessibleContext > xContext( xThis->getAccessibleContext() ); + if ( xContext.is() ) + { + uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() ); + if ( xParent.is() ) + { + uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY ); + if ( xParentComponent.is() ) + { + Point aScreenLoc = aBounds.TopLeft(); + awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen(); + Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y ); + aBounds.SetPos( aPos ); + } + } + } + } + } + + return aBounds; +} + + //===== XAccessibleContext ============================================== + +sal_Int32 SAL_CALL + ScAccessibleEditObject::getAccessibleChildCount() +{ + SolarMutexGuard aGuard; + IsObjectValid(); + CreateTextHelper(); + return mpTextHelper->GetChildCount(); +} + +uno::Reference< XAccessible > SAL_CALL + ScAccessibleEditObject::getAccessibleChild(sal_Int32 nIndex) +{ + SolarMutexGuard aGuard; + IsObjectValid(); + CreateTextHelper(); + return mpTextHelper->GetChild(nIndex); +} + +uno::Reference<XAccessibleStateSet> SAL_CALL + ScAccessibleEditObject::getAccessibleStateSet() +{ + SolarMutexGuard aGuard; + uno::Reference<XAccessibleStateSet> xParentStates; + if (getAccessibleParent().is()) + { + uno::Reference<XAccessibleContext> xParentContext = getAccessibleParent()->getAccessibleContext(); + xParentStates = xParentContext->getAccessibleStateSet(); + } + rtl::Reference<utl::AccessibleStateSetHelper> pStateSet = new utl::AccessibleStateSetHelper(); + if (IsDefunc(xParentStates)) + pStateSet->AddState(AccessibleStateType::DEFUNC); + else + { + // all states are const, because this object exists only in one state + pStateSet->AddState(AccessibleStateType::EDITABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::SENSITIVE); + pStateSet->AddState(AccessibleStateType::MULTI_LINE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::VISIBLE); + } + return pStateSet; +} + +OUString + ScAccessibleEditObject::createAccessibleDescription() +{ +// OSL_FAIL("Should never be called, because is set in the constructor.") + return OUString(); +} + +OUString + ScAccessibleEditObject::createAccessibleName() +{ + OSL_FAIL("Should never be called, because is set in the constructor."); + return OUString(); +} + + ///===== XAccessibleEventBroadcaster ===================================== + +void SAL_CALL + ScAccessibleEditObject::addAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener) +{ + CreateTextHelper(); + + mpTextHelper->AddEventListener(xListener); + + ScAccessibleContextBase::addAccessibleEventListener(xListener); +} + +void SAL_CALL + ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference<XAccessibleEventListener>& xListener) +{ + CreateTextHelper(); + + mpTextHelper->RemoveEventListener(xListener); + + ScAccessibleContextBase::removeAccessibleEventListener(xListener); +} + + //===== XServiceInfo ==================================================== + +OUString SAL_CALL ScAccessibleEditObject::getImplementationName() +{ + return "ScAccessibleEditObject"; +} + +//===== XTypeProvider ======================================================= + +uno::Sequence<sal_Int8> SAL_CALL + ScAccessibleEditObject::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + + //==== internal ========================================================= + +bool ScAccessibleEditObject::IsDefunc( + const uno::Reference<XAccessibleStateSet>& rxParentStates) +{ + return ScAccessibleContextBase::IsDefunc() || !getAccessibleParent().is() || + (rxParentStates.is() && rxParentStates->contains(AccessibleStateType::DEFUNC)); +} + +OutputDevice* ScAccessibleEditObject::GetOutputDeviceForView() +{ + return mpWindow->GetOutDev(); +} + +void ScAccessibleEditObject::CreateTextHelper() +{ + if (mpTextHelper) + return; + + ::std::unique_ptr < ScAccessibleTextData > pAccessibleTextData; + if (meObjectType == CellInEditMode || meObjectType == EditControl) + { + pAccessibleTextData.reset + (new ScAccessibleEditObjectTextData(mpEditView, GetOutputDeviceForView())); + } + else + { + pAccessibleTextData.reset + (new ScAccessibleEditLineTextData(nullptr, GetOutputDeviceForView(), mpTextWnd)); + } + + std::unique_ptr<ScAccessibilityEditSource> pEditSrc = + std::make_unique<ScAccessibilityEditSource>(std::move(pAccessibleTextData)); + + mpTextHelper = std::make_unique<::accessibility::AccessibleTextHelper>(std::move(pEditSrc)); + mpTextHelper->SetEventSource(this); + + const ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(); + if ( pInputHdl && pInputHdl->IsEditMode() ) + { + mpTextHelper->SetFocus(); + } + else + { + mpTextHelper->SetFocus(mbHasFocus); + } + + // #i54814# activate cell in edit mode + if( meObjectType == CellInEditMode ) + { + // do not activate cell object, if top edit line is active + if( pInputHdl && !pInputHdl->IsTopMode() ) + { + SdrHint aHint( SdrHintKind::BeginEdit ); + mpTextHelper->GetEditSource().GetBroadcaster().Broadcast( aHint ); + } + } +} + +sal_Int32 SAL_CALL ScAccessibleEditObject::getForeground( ) +{ + return GetFgBgColor(SC_UNONAME_CCOLOR); +} + +sal_Int32 SAL_CALL ScAccessibleEditObject::getBackground( ) +{ + return GetFgBgColor(SC_UNONAME_CELLBACK); +} + +sal_Int32 ScAccessibleEditObject::GetFgBgColor( const OUString &strPropColor) +{ + SolarMutexGuard aGuard; + sal_Int32 nColor(0); + if (m_pScDoc) + { + SfxObjectShell* pObjSh = m_pScDoc->GetDocumentShell(); + if ( pObjSh ) + { + uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); + if ( xSpreadDoc.is() ) + { + uno::Reference<sheet::XSpreadsheets> xSheets = xSpreadDoc->getSheets(); + uno::Reference<container::XIndexAccess> xIndex( xSheets, uno::UNO_QUERY ); + if ( xIndex.is() ) + { + uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab()); + uno::Reference<sheet::XSpreadsheet> xTable; + if (aTable>>=xTable) + { + uno::Reference<table::XCell> xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row()); + if (xCell.is()) + { + uno::Reference<beans::XPropertySet> xCellProps(xCell, uno::UNO_QUERY); + if (xCellProps.is()) + { + uno::Any aAny = xCellProps->getPropertyValue(strPropColor); + aAny >>= nColor; + } + } + } + } + } + } + } + return nColor; +} +//===== XAccessibleSelection ============================================ + +void SAL_CALL ScAccessibleEditObject::selectAccessibleChild( sal_Int32 ) +{ +} + +sal_Bool SAL_CALL ScAccessibleEditObject::isAccessibleChildSelected( sal_Int32 nChildIndex ) +{ + uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex ); + uno::Reference<XAccessibleContext> xContext; + if( xAcc.is() ) + xContext = xAcc->getAccessibleContext(); + if( xContext.is() ) + { + if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH ) + { + uno::Reference< css::accessibility::XAccessibleText > + xText(xAcc, uno::UNO_QUERY); + if( xText.is() ) + { + if( xText->getSelectionStart() >= 0 ) return true; + } + } + } + return false; +} + +void SAL_CALL ScAccessibleEditObject::clearAccessibleSelection( ) +{ +} + +void SAL_CALL ScAccessibleEditObject::selectAllAccessibleChildren( ) +{ +} + +sal_Int32 SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChildCount() +{ + sal_Int32 nCount = 0; + sal_Int32 TotalCount = getAccessibleChildCount(); + for( sal_Int32 i = 0; i < TotalCount; i++ ) + if( isAccessibleChildSelected(i) ) nCount++; + return nCount; +} + +uno::Reference<XAccessible> SAL_CALL ScAccessibleEditObject::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) +{ + if ( nSelectedChildIndex > getSelectedAccessibleChildCount() ) + throw IndexOutOfBoundsException(); + sal_Int32 i1, i2; + for( i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ ) + if( isAccessibleChildSelected(i1) ) + { + if( i2 == nSelectedChildIndex ) + return getAccessibleChild( i1 ); + i2++; + } + return uno::Reference<XAccessible>(); +} + +void SAL_CALL ScAccessibleEditObject::deselectAccessibleChild( + sal_Int32 ) +{ +} + +uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet( ) +{ + SolarMutexGuard aGuard; + vcl::Window* pWindow = mpWindow; + rtl::Reference<utl::AccessibleRelationSetHelper> rRelationSet = new utl::AccessibleRelationSetHelper; + if ( pWindow ) + { + vcl::Window *pLabeledBy = pWindow->GetAccessibleRelationLabeledBy(); + if ( pLabeledBy && pLabeledBy != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pLabeledBy->GetAccessible() }; + rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) ); + } + vcl::Window* pMemberOf = pWindow->GetAccessibleRelationMemberOf(); + if ( pMemberOf && pMemberOf != pWindow ) + { + uno::Sequence< uno::Reference< uno::XInterface > > aSequence { pMemberOf->GetAccessible() }; + rRelationSet->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) ); + } + return rRelationSet; + } + return uno::Reference< XAccessibleRelationSet >(); +} + +tools::Rectangle ScAccessibleEditControlObject::GetBoundingBoxOnScreen() const +{ + tools::Rectangle aScreenBounds; + + if (m_pController && m_pController->GetDrawingArea()) + { + aScreenBounds = tools::Rectangle(m_pController->GetDrawingArea()->get_accessible_location_on_screen(), + m_pController->GetOutputSizePixel()); + } + + return aScreenBounds; +} + +tools::Rectangle ScAccessibleEditControlObject::GetBoundingBox() const +{ + tools::Rectangle aBounds( GetBoundingBoxOnScreen() ); + + uno::Reference< XAccessibleContext > xContext(const_cast<ScAccessibleEditControlObject*>(this)->getAccessibleContext()); + if ( xContext.is() ) + { + uno::Reference< XAccessible > xParent( xContext->getAccessibleParent() ); + if ( xParent.is() ) + { + uno::Reference< XAccessibleComponent > xParentComponent( xParent->getAccessibleContext(), uno::UNO_QUERY ); + if ( xParentComponent.is() ) + { + Point aScreenLoc = aBounds.TopLeft(); + awt::Point aParentScreenLoc = xParentComponent->getLocationOnScreen(); + Point aPos( aScreenLoc.getX() - aParentScreenLoc.X, aScreenLoc.getY() - aParentScreenLoc.Y ); + aBounds.SetPos( aPos ); + } + } + } + + return aBounds; +} + +void SAL_CALL ScAccessibleEditControlObject::disposing() +{ + ScAccessibleEditObject::disposing(); + m_pController = nullptr; +} + +uno::Reference< XAccessibleRelationSet > ScAccessibleEditControlObject::getAccessibleRelationSet() +{ + SolarMutexGuard aGuard; + if (!m_pController || !m_pController->GetDrawingArea()) + return uno::Reference< XAccessibleRelationSet >(); + return m_pController->GetDrawingArea()->get_accessible_relation_set(); +} + +OutputDevice* ScAccessibleEditControlObject::GetOutputDeviceForView() +{ + if (!m_pController || !m_pController->GetDrawingArea()) + return nullptr; + return &m_pController->GetDrawingArea()->get_ref_device(); +} + +ScAccessibleEditLineObject::ScAccessibleEditLineObject(ScTextWnd* pTextWnd) + : ScAccessibleEditControlObject(pTextWnd, ScAccessibleEditObject::EditLine) +{ + // tdf#141769 set this early so its always available, even before the on-demand + // editview is created + mpTextWnd = pTextWnd; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |