/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using ::com::sun::star::lang::IndexOutOfBoundsException; using namespace ::com::sun::star; using namespace ::com::sun::star::accessibility; //===== internal ============================================================ ScAccessibleEditObject::ScAccessibleEditObject( const uno::Reference& 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& 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(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 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 SAL_CALL ScAccessibleEditObject::getAccessibleStateSet() { SolarMutexGuard aGuard; uno::Reference xParentStates; if (getAccessibleParent().is()) { uno::Reference xParentContext = getAccessibleParent()->getAccessibleContext(); xParentStates = xParentContext->getAccessibleStateSet(); } rtl::Reference 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& xListener) { CreateTextHelper(); mpTextHelper->AddEventListener(xListener); ScAccessibleContextBase::addAccessibleEventListener(xListener); } void SAL_CALL ScAccessibleEditObject::removeAccessibleEventListener(const uno::Reference& xListener) { CreateTextHelper(); mpTextHelper->RemoveEventListener(xListener); ScAccessibleContextBase::removeAccessibleEventListener(xListener); } //===== XServiceInfo ==================================================== OUString SAL_CALL ScAccessibleEditObject::getImplementationName() { return "ScAccessibleEditObject"; } //===== XTypeProvider ======================================================= uno::Sequence SAL_CALL ScAccessibleEditObject::getImplementationId() { return css::uno::Sequence(); } //==== internal ========================================================= bool ScAccessibleEditObject::IsDefunc( const uno::Reference& 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 pEditSrc = std::make_unique(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 xSpreadDoc( pObjSh->GetModel(), uno::UNO_QUERY ); if ( xSpreadDoc.is() ) { uno::Reference xSheets = xSpreadDoc->getSheets(); uno::Reference xIndex( xSheets, uno::UNO_QUERY ); if ( xIndex.is() ) { uno::Any aTable = xIndex->getByIndex(m_curCellAddress.Tab()); uno::Reference xTable; if (aTable>>=xTable) { uno::Reference xCell = xTable->getCellByPosition(m_curCellAddress.Col(), m_curCellAddress.Row()); if (xCell.is()) { uno::Reference 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 xAcc = getAccessibleChild( nChildIndex ); uno::Reference 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 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(); } void SAL_CALL ScAccessibleEditObject::deselectAccessibleChild( sal_Int32 ) { } uno::Reference< XAccessibleRelationSet > ScAccessibleEditObject::getAccessibleRelationSet( ) { SolarMutexGuard aGuard; vcl::Window* pWindow = mpWindow; rtl::Reference 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(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: */