diff options
Diffstat (limited to 'sd/source/ui/accessibility')
12 files changed, 4169 insertions, 0 deletions
diff --git a/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx new file mode 100644 index 000000000..c297184fa --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx @@ -0,0 +1,773 @@ +/* -*- 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 <AccessibleDocumentViewBase.hxx> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/document/XShapeEventBroadcaster.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <rtl/ustrbuf.hxx> +#include <sfx2/viewfrm.hxx> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <sfx2/objsh.hxx> +#include <tools/debug.hxx> + +#include <cppuhelper/queryinterface.hxx> +#include <svx/svdobj.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <Window.hxx> +#include <OutlineViewShell.hxx> + +#include <svx/svdlayer.hxx> +#include <editeng/editobj.hxx> +#include <LayerTabBar.hxx> +#include <svtools/colorcfg.hxx> +#include <ViewShell.hxx> +#include <View.hxx> +#include <drawdoc.hxx> +#include <editeng/outlobj.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <PresentationViewShell.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ +AccessibleDocumentViewBase::AccessibleDocumentViewBase ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleContextBase (rxParent, + pViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress ? + AccessibleRole::DOCUMENT_PRESENTATION : + AccessibleRole::DOCUMENT), + mxController (rxController), + maViewForwarder ( + static_cast<SdrPaintView*>(pViewShell->GetView()), + *pSdWindow->GetOutDev()) +{ + if (mxController.is()) + mxModel = mxController->getModel(); + + // Fill the shape tree info. + maShapeTreeInfo.SetModelBroadcaster ( + uno::Reference<document::XShapeEventBroadcaster>( + mxModel, uno::UNO_QUERY_THROW)); + maShapeTreeInfo.SetController (mxController); + maShapeTreeInfo.SetSdrView (pViewShell->GetView()); + maShapeTreeInfo.SetWindow (pSdWindow); + maShapeTreeInfo.SetViewForwarder (&maViewForwarder); + + mxWindow = ::VCLUnoHelper::GetInterface (pSdWindow); + mpViewShell = pViewShell; +} + +AccessibleDocumentViewBase::~AccessibleDocumentViewBase() +{ + // At this place we should be disposed. You may want to add a + // corresponding assertion into the destructor of a derived class. +} + +void AccessibleDocumentViewBase::Init() +{ + // Finish the initialization of the shape tree info container. + maShapeTreeInfo.SetDocumentWindow (this); + + // Register as window listener to stay up to date with its size and + // position. + mxWindow->addWindowListener (this); + // Register as focus listener to + mxWindow->addFocusListener (this); + + // Determine the list of shapes on the current page. + uno::Reference<drawing::XShapes> xShapeList; + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Register this object as dispose event listener at the model. + if (mxModel.is()) + mxModel->addEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Register as property change listener at the controller. + uno::Reference<beans::XPropertySet> xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->addPropertyChangeListener ( + "", + static_cast<beans::XPropertyChangeListener*>(this)); + + // Register this object as dispose event listener at the controller. + if (mxController.is()) + mxController->addEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Register at VCL Window to be informed of activated and deactivated + // OLE objects. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (pWindow != nullptr) + { + maWindowLink = LINK( + this, AccessibleDocumentViewBase, WindowChildEventListener); + + pWindow->AddChildEventListener (maWindowLink); + + sal_uInt16 nCount = pWindow->GetChildCount(); + for (sal_uInt16 i=0; i<nCount; i++) + { + vcl::Window* pChildWindow = pWindow->GetChild (i); + if (pChildWindow && + (AccessibleRole::EMBEDDED_OBJECT + ==pChildWindow->GetAccessibleRole())) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + } + SfxObjectShell* pObjShell = mpViewShell->GetViewFrame()->GetObjectShell(); + if(!pObjShell->IsReadOnly()) + SetState(AccessibleStateType::EDITABLE); +} + +IMPL_LINK(AccessibleDocumentViewBase, WindowChildEventListener, + VclWindowEvent&, rEvent, void) +{ + // DBG_ASSERT( pVclEvent->GetWindow(), "Window???" ); + switch (rEvent.GetId()) + { + case VclEventId::ObjectDying: + { + // Window is dying. Unregister from VCL Window. + // This is also attempted in the disposing() method. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + vcl::Window* pDyingWindow = rEvent.GetWindow(); + if (pWindow==pDyingWindow && pWindow!=nullptr && maWindowLink.IsSet()) + { + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link<VclWindowEvent&,void>(); + } + } + break; + + case VclEventId::WindowShow: + { + // A new window has been created. Is it an OLE object? + vcl::Window* pChildWindow = static_cast<vcl::Window*>( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + break; + + case VclEventId::WindowHide: + { + // A window has been destroyed. Has that been an OLE + // object? + vcl::Window* pChildWindow = static_cast<vcl::Window*>( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (nullptr); + } + } + break; + + default: break; + } +} + +//===== IAccessibleViewForwarderListener ==================================== + +void AccessibleDocumentViewBase::ViewForwarderChanged() +{ + // Empty +} + +//===== XAccessibleContext ================================================== + +Reference<XAccessible> SAL_CALL + AccessibleDocumentViewBase::getAccessibleParent() +{ + ThrowIfDisposed (); + + return AccessibleContextBase::getAccessibleParent(); +} + +sal_Int32 SAL_CALL + AccessibleDocumentViewBase::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + if (mxAccessibleOLEObject.is()) + return 1; + else + return 0; +} + +Reference<XAccessible> SAL_CALL + AccessibleDocumentViewBase::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + if (mxAccessibleOLEObject.is()) + if (nIndex == 0) + return mxAccessibleOLEObject; + + throw lang::IndexOutOfBoundsException ( "no child with index " + OUString::number(nIndex) ); +} + +//===== XAccessibleComponent ================================================ + +/** Iterate over all children and test whether the specified point lies + within one of their bounding boxes. Return the first child for which + this is true. +*/ +uno::Reference<XAccessible > SAL_CALL + AccessibleDocumentViewBase::getAccessibleAtPoint ( + const awt::Point& aPoint) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + uno::Reference<XAccessible> xChildAtPosition; + + sal_Int32 nChildCount = getAccessibleChildCount (); + for (sal_Int32 i=nChildCount-1; i>=0; --i) + { + Reference<XAccessible> xChild (getAccessibleChild (i)); + if (xChild.is()) + { + Reference<XAccessibleComponent> xChildComponent ( + xChild->getAccessibleContext(), uno::UNO_QUERY); + if (xChildComponent.is()) + { + awt::Rectangle aBBox (xChildComponent->getBounds()); + if ( (aPoint.X >= aBBox.X) + && (aPoint.Y >= aBBox.Y) + && (aPoint.X < aBBox.X+aBBox.Width) + && (aPoint.Y < aBBox.Y+aBBox.Height) ) + { + xChildAtPosition = xChild; + break; + } + } + } + } + + // Have not found a child under the given point. Returning empty + // reference to indicate this. + return xChildAtPosition; +} + +awt::Rectangle SAL_CALL + AccessibleDocumentViewBase::getBounds() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + // Prepare to subtract the parent position to transform into relative + // coordinates. + awt::Point aParentPosition; + Reference<XAccessible> xParent = getAccessibleParent (); + if (xParent.is()) + { + Reference<XAccessibleComponent> xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentPosition = xParentComponent->getLocationOnScreen(); + } + + return awt::Rectangle ( + aPixelTopLeft.X() - aParentPosition.X, + aPixelTopLeft.Y() - aParentPosition.Y, + aPixelSize.X(), + aPixelSize.Y()); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocation() +{ + ThrowIfDisposed (); + awt::Rectangle aBoundingBox (getBounds()); + return awt::Point (aBoundingBox.X, aBoundingBox.Y); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocationOnScreen() +{ + ThrowIfDisposed (); + ::Point aLogicalPoint (maShapeTreeInfo.GetViewForwarder()->GetVisibleArea().TopLeft()); + ::Point aPixelPoint (maShapeTreeInfo.GetViewForwarder()->LogicToPixel (aLogicalPoint)); + return awt::Point (aPixelPoint.X(), aPixelPoint.Y()); +} + +awt::Size SAL_CALL + AccessibleDocumentViewBase::getSize() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + return awt::Size (aPixelSize.X(), aPixelSize.Y()); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDocumentViewBase::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleContextBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleComponent*>(this), + static_cast<XAccessibleSelection*>(this), + static_cast<lang::XEventListener*>( + static_cast<awt::XWindowListener*>(this)), + static_cast<beans::XPropertyChangeListener*>(this), + static_cast<awt::XWindowListener*>(this), + static_cast<awt::XFocusListener*>(this) + ,static_cast<XAccessibleExtendedAttributes*>(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDocumentViewBase::acquire() + noexcept +{ + AccessibleContextBase::acquire (); +} + +void SAL_CALL + AccessibleDocumentViewBase::release() + noexcept +{ + AccessibleContextBase::release (); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDocumentViewBase::getImplementationName() +{ + return "AccessibleDocumentViewBase"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDocumentViewBase::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleContextBase::getSupportedServiceNames (); +} + +//===== XTypeProvider ======================================================= + +css::uno::Sequence< css::uno::Type> SAL_CALL + AccessibleDocumentViewBase::getTypes() +{ + ThrowIfDisposed (); + + return comphelper::concatSequences( + // Get list of types from the context base implementation, ... + AccessibleContextBase::getTypes(), + // ... get list of types from component base implementation, ... + AccessibleComponentBase::getTypes(), + // ...and add the additional type for the component, ... + css::uno::Sequence { + cppu::UnoType<lang::XEventListener>::get(), + cppu::UnoType<beans::XPropertyChangeListener>::get(), + cppu::UnoType<awt::XWindowListener>::get(), + cppu::UnoType<awt::XFocusListener>::get(), + cppu::UnoType<XAccessibleEventBroadcaster>::get() }); +} + +void AccessibleDocumentViewBase::impl_dispose() +{ + // Unregister from VCL Window. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (maWindowLink.IsSet()) + { + if (pWindow) + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link<VclWindowEvent&,void>(); + } + else + { + DBG_ASSERT (pWindow, "AccessibleDocumentViewBase::disposing"); + } + + // Unregister from window. + if (mxWindow.is()) + { + mxWindow->removeWindowListener (this); + mxWindow->removeFocusListener (this); + mxWindow = nullptr; + } + + // Unregister from the model. + if (mxModel.is()) + mxModel->removeEventListener ( + static_cast<awt::XWindowListener*>(this)); + + // Unregister from the controller. + if (mxController.is()) + { + uno::Reference<beans::XPropertySet> xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->removePropertyChangeListener ("", static_cast<beans::XPropertyChangeListener*>(this)); + + mxController->removeEventListener ( + static_cast<awt::XWindowListener*>(this)); + } + + // Propagate change of controller down the shape tree. + maShapeTreeInfo.SetModelBroadcaster (nullptr); + + // Reset the model reference. + mxModel = nullptr; + // Reset the model reference. + mxController = nullptr; + + maShapeTreeInfo.SetDocumentWindow (nullptr); +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDocumentViewBase::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + // Register this object as dispose event and document::XEventListener + // listener at the model. + + if ( ! rEventObject.Source.is()) + { + // Paranoia. Can this really happen? + } + else if (rEventObject.Source == mxModel || rEventObject.Source == mxController) + { + impl_dispose(); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL AccessibleDocumentViewBase::propertyChange (const beans::PropertyChangeEvent& ) +{ + // Empty +} + +//===== XWindowListener ===================================================== + +void SAL_CALL + AccessibleDocumentViewBase::windowResized (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowMoved (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowShown (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowHidden (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +//===== XFocusListener ================================================== + +void AccessibleDocumentViewBase::focusGained (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Activated (); +} + +void AccessibleDocumentViewBase::focusLost (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Deactivated (); +} + +//===== protected internal ================================================== + +// This method is called from the component helper base class while disposing. +void SAL_CALL AccessibleDocumentViewBase::disposing() +{ + impl_dispose(); + + AccessibleContextBase::disposing (); +} + +/// Create a name for this view. +OUString + AccessibleDocumentViewBase::CreateAccessibleName() +{ + return "AccessibleDocumentViewBase"; +} + +void AccessibleDocumentViewBase::Activated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::Deactivated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::SetAccessibleOLEObject ( + const Reference <XAccessible>& xOLEObject) +{ + // Send child event about removed accessible OLE object if necessary. + if (mxAccessibleOLEObject != xOLEObject) + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + uno::Any (mxAccessibleOLEObject)); + + // Assume that the accessible OLE Object disposes itself correctly. + + { + ::osl::MutexGuard aGuard (m_aMutex); + mxAccessibleOLEObject = xOLEObject; + } + + // Send child event about new accessible OLE object if necessary. + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any (mxAccessibleOLEObject), + uno::Any()); +} + +//===== methods from AccessibleSelectionBase ================================================== + +// return the member maMutex; +::osl::Mutex& + AccessibleDocumentViewBase::implGetMutex() +{ + return m_aMutex; +} + +// return ourself as context in default case +uno::Reference< XAccessibleContext > + AccessibleDocumentViewBase::implGetAccessibleContext() +{ + return this; +} + +// return sal_False in default case +bool + AccessibleDocumentViewBase::implIsSelected( sal_Int32 ) +{ + return false; +} + +// return nothing in default case +void + AccessibleDocumentViewBase::implSelect( sal_Int32, bool ) +{ +} + +uno::Any SAL_CALL AccessibleDocumentViewBase::getExtendedAttributes() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + uno::Any anyAttribute; + OUStringBuffer sValue; + if (auto pDrViewSh = dynamic_cast<::sd::DrawViewShell* > (mpViewShell)) + { + OUString sDisplay; + OUString sName = "page-name:"; + // MT IA2: Not used... + // SdPage* pCurrPge = pDrViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + sDisplay = pDrViewSh->getCurrentPage()->GetName(); + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = sName + sDisplay ; + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(static_cast<sal_uInt16>((pDrViewSh->getCurrentPage()->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetPageTabControl().GetPageCount())); + sValue.append(";"); + if(pDrViewSh->IsLayerModeActive() && pDrViewSh->GetLayerTabControl()) // #i87182# + { + sName = "page-name:"; + sValue = sName; + OUString sLayerName(pDrViewSh->GetLayerTabControl()->GetLayerName(pDrViewSh->GetLayerTabControl()->GetCurPageId()) ); + sDisplay = pDrViewSh->GetLayerTabControl()->GetPageText(pDrViewSh->GetLayerTabControl()->GetCurPageId()); + if( pDoc ) + { + SdrLayerAdmin& rLayerAdmin = pDoc->GetLayerAdmin(); + SdrLayer* aSdrLayer = rLayerAdmin.GetLayer(sLayerName); + if( aSdrLayer ) + { + const OUString& layerAltText = aSdrLayer->GetTitle(); + if (!layerAltText.isEmpty()) + { + sName = " "; + sDisplay += sName + layerAltText; + } + } + } + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue.append(sDisplay); + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetActiveTabLayerIndex()+1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDrViewSh->GetLayerTabControl()->GetPageCount())); + sValue.append(";"); + } + } + if (auto pPresViewSh = dynamic_cast<::sd::PresentationViewShell* >(mpViewShell)) + { + SdPage* pCurrPge = pPresViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pPresViewSh->GetDoc(); + SdPage* pNotesPge = pDoc->GetSdPage((pCurrPge->GetPageNum()-1)>>1, PageKind::Notes); + if (pNotesPge) + { + SdrObject* pNotesObj = pNotesPge->GetPresObj(PresObjKind::Notes); + if (pNotesObj) + { + OutlinerParaObject* pPara = pNotesObj->GetOutlinerParaObject(); + if (pPara) + { + sValue.append("note:"); + const EditTextObject& rEdit = pPara->GetTextObject(); + for (sal_Int32 i=0;i<rEdit.GetParagraphCount();i++) + { + OUString strNote = rEdit.GetText(i); + strNote = strNote.replaceFirst( "\\", "\\\\" ); + strNote = strNote.replaceFirst( "=", "\\=" ); + strNote = strNote.replaceFirst( ";", "\\;" ); + strNote = strNote.replaceFirst( ",", "\\," ); + strNote = strNote.replaceFirst( ":", "\\:" ); + sValue.append(strNote); + sValue.append(";");//to divide each paragraph + } + } + } + } + } + if (dynamic_cast<const ::sd::OutlineViewShell* >(mpViewShell ) != nullptr ) + { + SdPage* pCurrPge = mpViewShell->GetActualPage(); + SdDrawDocument* pDoc = mpViewShell->GetDoc(); + if(pCurrPge && pDoc) + { + OUString sDisplay; + sDisplay = pCurrPge->GetName(); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = "page-name:" + sDisplay; + sValue.append(";page-number:"); + sValue.append(static_cast<sal_Int32>(static_cast<sal_uInt16>((pCurrPge->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast<sal_Int32>(pDoc->GetSdPageCount(PageKind::Standard))); + sValue.append(";"); + } + } + if (sValue.getLength()) + anyAttribute <<= sValue.makeStringAndClear(); + return anyAttribute; +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getForeground( ) +{ + return sal_Int32(COL_BLACK); +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getBackground( ) +{ + ThrowIfDisposed (); + ::osl::MutexGuard aGuard (m_aMutex); + return sal_Int32(mpViewShell->GetView()->getColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor); +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx new file mode 100644 index 000000000..f6111962a --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx @@ -0,0 +1,777 @@ +/* -*- 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 <AccessibleDrawDocumentView.hxx> +#include <com/sun/star/drawing/ShapeCollection.hpp> +#include <com/sun/star/drawing/XDrawView.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> + +#include <svx/AccessibleShape.hxx> +#include <svx/ChildrenManager.hxx> +#include <svx/svdobj.hxx> +#include <vcl/svapp.hxx> + +#include <ViewShell.hxx> +#include <View.hxx> +#include <DrawDocShell.hxx> +#include <drawdoc.hxx> +#include <algorithm> +#include <slideshow.hxx> +#include <anminfo.hxx> +#include <AccessiblePageShape.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <osl/mutex.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +namespace { + +struct XShapePosCompareHelper +{ + bool operator() ( const uno::Reference<drawing::XShape>& xshape1, + const uno::Reference<drawing::XShape>& xshape2 ) const + { + // modify the compare method to return the Z-Order, not layout order + SdrObject* pObj1 = SdrObject::getSdrObjectFromXShape(xshape1); + SdrObject* pObj2 = SdrObject::getSdrObjectFromXShape(xshape2); + if(pObj1 && pObj2) + return pObj1->GetOrdNum() < pObj2->GetOrdNum(); + else + return false; + } +}; + +} + +//===== internal ============================================================ + +AccessibleDrawDocumentView::AccessibleDrawDocumentView ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + mpSdViewSh( pViewShell ) +{ + UpdateAccessibleName(); +} + +AccessibleDrawDocumentView::~AccessibleDrawDocumentView() +{ + DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose, + "~AccessibleDrawDocumentView: object has not been disposed"); +} + +void AccessibleDrawDocumentView::Init() +{ + AccessibleDocumentViewBase::Init (); + + // Determine the list of shapes on the current page. + uno::Reference<drawing::XShapes> xShapeList; + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Create the children manager. + mpChildrenManager.reset(new ChildrenManager(this, xShapeList, maShapeTreeInfo, *this)); + + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (); + } + + mpChildrenManager->UpdateSelection (); +} + +void AccessibleDrawDocumentView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); +} + +/** The page shape is created on every call at the moment (provided that + everything goes well). +*/ +rtl::Reference<AccessiblePageShape> AccessibleDrawDocumentView::CreateDrawPageShape() +{ + rtl::Reference<AccessiblePageShape> xShape; + + // Create a shape that represents the actual draw page. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference<beans::XPropertySet> xSet ( + uno::Reference<beans::XPropertySet> (xView->getCurrentPage(), uno::UNO_QUERY)); + if (xSet.is()) + { + // Create a rectangle shape that will represent the draw page. + uno::Reference<lang::XMultiServiceFactory> xFactory (mxModel, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xRectangle; + if (xFactory.is()) + xRectangle.set(xFactory->createInstance ("com.sun.star.drawing.RectangleShape"), + uno::UNO_QUERY); + + // Set the shape's size and position. + if (xRectangle.is()) + { + uno::Any aValue; + awt::Point aPosition; + awt::Size aSize; + + // Set size and position of the shape to those of the draw + // page. + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aPosition.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aPosition.Y; + xRectangle->setPosition (aPosition); + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aSize.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aSize.Height; + xRectangle->setSize (aSize); + + // Create the accessible object for the shape and + // initialize it. + xShape = new AccessiblePageShape ( + xView->getCurrentPage(), this, maShapeTreeInfo); + } + } + } + return xShape; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleDrawDocumentView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + tools::Long nChildCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + + // Forward request to children manager. + if (mpChildrenManager != nullptr) + nChildCount += mpChildrenManager->GetChildCount(); + + return nChildCount; +} + +uno::Reference<XAccessible> SAL_CALL + AccessibleDrawDocumentView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::ClearableMutexGuard aGuard (m_aMutex); + + // Take care of children of the base class. + sal_Int32 nCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + if (nCount > 0) + { + if (nIndex < nCount) + return AccessibleDocumentViewBase::getAccessibleChild(nIndex); + else + nIndex -= nCount; + } + + // Create a copy of the pointer to the children manager and release the + // mutex before calling any of its methods. + ChildrenManager* pChildrenManager = mpChildrenManager.get(); + aGuard.clear(); + + // Forward request to children manager. + if (pChildrenManager == nullptr) + throw lang::IndexOutOfBoundsException ( + "no accessible child with index " + OUString::number(nIndex), + static_cast<uno::XWeak*>(this)); + + return pChildrenManager->GetChild (nIndex); +} + +OUString SAL_CALL + AccessibleDrawDocumentView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if ( !sFileName.getLength() ) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + + OUString sReadOnly; + if(rDoc.getDocReadOnly()) + { + sReadOnly = SdResId(SID_SD_A11Y_D_PRESENTATION_READONLY); + } + + if ( sFileName.getLength() ) + { + sName = sFileName + sReadOnly + " - " + sName; + } + } + + return sName; +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDrawDocumentView::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::disposing (rEventObject); + if (rEventObject.Source == mxModel) + { + ::osl::Guard< ::osl::Mutex> aGuard (::osl::Mutex::getGlobalMutex()); + // maShapeTreeInfo has been modified in base class. + if (mpChildrenManager != nullptr) + mpChildrenManager->SetInfo (maShapeTreeInfo); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleDrawDocumentView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + // add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // Update the accessible name to reflect the current slide. + UpdateAccessibleName(); + + // The current page changed. Update the children manager accordingly. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + mpChildrenManager->SetShapeList (xView->getCurrentPage()); + + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + else + SAL_WARN("sd", "View invalid"); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); + } + else if (rEventObject.PropertyName == "ActiveLayer") + { + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if (rEventObject.PropertyName == "UpdateAcc") + { + // The current page changed. Update the children manager accordingly. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + // update the slide show page's accessible info + //mpChildrenManager->SetShapeList (uno::Reference<drawing::XShapes> ( + // xView->getCurrentPage(), uno::UNO_QUERY)); + rtl::Reference< sd::SlideShow > xSlideshow( sd::SlideShow::GetSlideShow( mpSdViewSh->GetViewShellBase() ) ); + if( xSlideshow.is() && xSlideshow->isRunning() && xSlideshow->isFullScreen() ) + { + css::uno::Reference< drawing::XDrawPage > xSlide; + // MT IA2: Not used... + // sal_Int32 currentPageIndex = xSlideshow->getCurrentPageIndex(); + css::uno::Reference< css::presentation::XSlideShowController > xSlideController = xSlideshow->getController(); + if( xSlideController.is() ) + { + xSlide = xSlideController->getCurrentSlide(); + if (xSlide.is()) + { + mpChildrenManager->SetShapeList (xSlide); + } + } + } + rtl::Reference<AccessiblePageShape> xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDrawDocumentView::getImplementationName() +{ + return "AccessibleDrawDocumentView"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDrawDocumentView::getSupportedServiceNames() +{ + ThrowIfDisposed(); + const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleDrawDocumentView" }; + uno::Sequence<OUString> aServiceNames = + AccessibleDocumentViewBase::getSupportedServiceNames(); + + return comphelper::concatSequences(aServiceNames, vals); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDrawDocumentView::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleDocumentViewBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast<XAccessibleGroupPosition*>(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDrawDocumentView::acquire() + noexcept +{ + AccessibleDocumentViewBase::acquire (); +} +void SAL_CALL + AccessibleDrawDocumentView::release() + noexcept +{ + AccessibleDocumentViewBase::release (); +} +//===== XAccessibleGroupPosition ========================================= +uno::Sequence< sal_Int32 > SAL_CALL + AccessibleDrawDocumentView::getGroupPosition( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + // we will return the: + // [0] group level(always be 0 now) + // [1] similar items counts in the group + // [2] the position of the object in the group + uno::Sequence< sal_Int32 > aRet( 3 ); + //get the xShape of the current selected drawing object + uno::Reference<XAccessibleContext> xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + //find all the child in the page, insert them into a vector and sort + if ( mpChildrenManager == nullptr ) + { + return aRet; + } + std::vector< uno::Reference<drawing::XShape> > vXShapes; + sal_Int32 nCount = mpChildrenManager->GetChildCount(); + //get pointer of SdView & SdrPageView for further use. + SdrPageView* pPV = nullptr; + ::sd::View* pSdView = nullptr; + if ( mpSdViewSh ) + { + pSdView = mpSdViewSh->GetView(); + pPV = pSdView->GetSdrPageView(); + } + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xShape = mpChildrenManager->GetChildShape(i); + if ( xShape.is() ) + { + //if the object is visible in the page, we add it into the group list. + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if ( pObj && pPV && pSdView && pSdView->IsObjMarkable( pObj, pPV ) ) + { + vXShapes.push_back( xShape ); + } + } + } + std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() ); + //get the index of the selected object in the group + auto aIter = std::find_if(vXShapes.begin(), vXShapes.end(), + [&xCurShape](const uno::Reference<drawing::XShape>& rxShape) { return rxShape.get() == xCurShape.get(); }); + if (aIter != vXShapes.end()) + { + sal_Int32* pArray = aRet.getArray(); + pArray[0] = 1; //it should be 1 based, not 0 based. + pArray[1] = vXShapes.size(); + pArray[2] = static_cast<sal_Int32>(std::distance(vXShapes.begin(), aIter)) + 1; //we start counting position from 1 + } + return aRet; +} + +OUString AccessibleDrawDocumentView::getObjectLink( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + OUString aRet; + //get the xShape of the current selected drawing object + uno::Reference<XAccessibleContext> xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xCurShape); + if (pObj) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo && (pInfo->meClickAction == presentation::ClickAction_DOCUMENT) ) + aRet = pInfo->GetBookmark(); + } + return aRet; +} + +/// Create a name for this view. +OUString AccessibleDrawDocumentView::CreateAccessibleName() +{ + OUString sName; + + uno::Reference<lang::XServiceInfo> xInfo (mxController, uno::UNO_QUERY); + if (xInfo.is()) + { + uno::Sequence< OUString > aServices( xInfo->getSupportedServiceNames() ); + OUString sFirstService = aServices[0]; + if ( sFirstService == "com.sun.star.drawing.DrawingDocumentDrawView" ) + { + if( aServices.getLength() >= 2 && aServices[1] == "com.sun.star.presentation.PresentationView") + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_DRAWVIEW_N); + } + else + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_D_DRAWVIEW_N); + } + } + else if ( sFirstService == "com.sun.star.presentation.NotesView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_NOTESVIEW_N); + } + else if ( sFirstService == "com.sun.star.presentation.HandoutView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_HANDOUTVIEW_N); + } + else + { + sName = sFirstService; + } + } + else + { + sName = "AccessibleDrawDocumentView"; + } + return sName; +} + +/** Return selection state of specified child +*/ +bool + AccessibleDrawDocumentView::implIsSelected( sal_Int32 nAccessibleChildIndex ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + bool bRet = false; + + OSL_ENSURE( 0 <= nAccessibleChildIndex, "AccessibleDrawDocumentView::implIsSelected: invalid index!" ); + + if( xSel.is() && ( 0 <= nAccessibleChildIndex ) ) + { + uno::Any aAny( xSel->getSelection() ); + uno::Reference< drawing::XShapes > xShapes; + + aAny >>= xShapes; + + if( xShapes.is() ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( getAccessibleChild( nAccessibleChildIndex ) ); + + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + for( sal_Int32 i = 0, nCount = xShapes->getCount(); ( i < nCount ) && !bRet; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bRet = true; + } + } + } + } + + return bRet; +} + +/** Select or deselect the specified shapes. The corresponding accessible + shapes are notified over the selection change listeners registered with + the XSelectionSupplier of the controller. +*/ +void + AccessibleDrawDocumentView::implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + + if( !xSel.is() ) + return; + + uno::Any aAny; + + if( ACCESSIBLE_SELECTION_CHILD_ALL == nAccessibleChildIndex ) + { + // Select or deselect all children. + + if( !bSelect ) + xSel->select( aAny ); + else + { + uno::Reference< drawing::XShapes > xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + for(sal_Int32 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( getAccessibleChild( i ) ); + + if( pAcc && pAcc->GetXShape().is() ) + xShapes->add( pAcc->GetXShape() ); + } + + if( xShapes->getCount() ) + { + xSel->select( Any(xShapes) ); + } + } + } + else if( nAccessibleChildIndex >= 0 ) + { + // Select or deselect only the child with index + // nAccessibleChildIndex. + + AccessibleShape* pAcc = comphelper::getFromUnoTunnel<AccessibleShape>( + getAccessibleChild( nAccessibleChildIndex )); + + // Add or remove the shape that is made accessible from the + // selection of the controller. + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + uno::Reference< drawing::XShapes > xShapes; + bool bFound = false; + + aAny = xSel->getSelection(); + aAny >>= xShapes; + + // Search shape to be selected in current selection. + if (xShapes.is()) + { + sal_Int32 nCount = xShapes->getCount(); + for (sal_Int32 i=0; ( i < nCount ) && !bFound; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bFound = true; + } + else + // Create an empty selection to add the shape to. + xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + // Update the selection. + if( !bFound && bSelect ) + xShapes->add( xShape ); + else if( bFound && !bSelect ) + xShapes->remove( xShape ); + + xSel->select( Any(xShapes) ); + } + } + } +} + +void AccessibleDrawDocumentView::Activated() +{ + if (mpChildrenManager == nullptr) + return; + + bool bChange = false; + // When none of the children has the focus then claim it for the + // view. + if ( ! mpChildrenManager->HasFocus()) + { + SetState (AccessibleStateType::FOCUSED); + bChange = true; + } + else + ResetState (AccessibleStateType::FOCUSED); + mpChildrenManager->UpdateSelection(); + // if the child gets focus in UpdateSelection(), needs to reset the focus on document. + if (mpChildrenManager->HasFocus() && bChange) + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::Deactivated() +{ + if (mpChildrenManager != nullptr) + mpChildrenManager->RemoveFocus(); + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::impl_dispose() +{ + mpChildrenManager.reset(); + AccessibleDocumentViewBase::impl_dispose(); +} + +/** This method is called from the component helper base class while + disposing. +*/ +void SAL_CALL AccessibleDrawDocumentView::disposing() +{ + // Release resources. + mpChildrenManager.reset(); + + // Forward call to base classes. + AccessibleDocumentViewBase::disposing (); +} + +void AccessibleDrawDocumentView::UpdateAccessibleName() +{ + OUString sNewName (CreateAccessibleName() + ": "); + + // Add the number of the current slide. + uno::Reference<drawing::XDrawView> xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference<beans::XPropertySet> xProperties (xView->getCurrentPage(), UNO_QUERY); + if (xProperties.is()) + try + { + sal_Int16 nPageNumber (0); + if (xProperties->getPropertyValue("Number") >>= nPageNumber) + { + sNewName += OUString::number(nPageNumber); + } + } + catch (const beans::UnknownPropertyException&) + { + } + } + + // Add the number of pages/slides. + Reference<drawing::XDrawPagesSupplier> xPagesSupplier (mxModel, UNO_QUERY); + if (xPagesSupplier.is()) + { + Reference<container::XIndexAccess> xPages = xPagesSupplier->getDrawPages(); + if (xPages.is()) + { + sNewName += " / " + OUString::number(xPages->getCount()); + } + } + + SetAccessibleName (sNewName, AutomaticallyCreated); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx new file mode 100644 index 000000000..a1a79a678 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <memory> +#include <editeng/unoedhlp.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svdview.hxx> +#include <vcl/window.hxx> +#include <AccessibleOutlineEditSource.hxx> + +namespace accessibility +{ + + AccessibleOutlineEditSource::AccessibleOutlineEditSource( + SdrOutliner& rOutliner, + SdrView& rView, + OutlinerView& rOutlView, + const vcl::Window& rViewWindow ) + : mrView( rView ), + mrWindow( rViewWindow ), + mpOutliner( &rOutliner ), + mpOutlinerView( &rOutlView ), + mTextForwarder( rOutliner, false ), + mViewForwarder( rOutlView ) + { + // register as listener - need to broadcast state change messages + // Moved to ::GetTextForwarder() + //rOutliner.SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + StartListening(rOutliner); + } + + AccessibleOutlineEditSource::~AccessibleOutlineEditSource() + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link<EENotify&,void>() ); + Broadcast( TextHint( SfxHintId::Dying ) ); + } + + std::unique_ptr<SvxEditSource> AccessibleOutlineEditSource::Clone() const + { + return std::unique_ptr<SvxEditSource>(new AccessibleOutlineEditSource(*mpOutliner, mrView, *mpOutlinerView, mrWindow)); + } + + SvxTextForwarder* AccessibleOutlineEditSource::GetTextForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // Moved here to make sure that + // the NotifyHandler was set on the current object. + mpOutliner->SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + return &mTextForwarder; + } + else + return nullptr; + } + + SvxViewForwarder* AccessibleOutlineEditSource::GetViewForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + return this; + else + return nullptr; + } + + SvxEditViewForwarder* AccessibleOutlineEditSource::GetEditViewForwarder( bool ) + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // ignore parameter, we're always in edit mode here + return &mViewForwarder; + } + else + return nullptr; + } + + void AccessibleOutlineEditSource::UpdateData() + { + // NOOP, since we're always working on the 'real' outliner, + // i.e. changes are immediately reflected on the screen + } + + SfxBroadcaster& AccessibleOutlineEditSource::GetBroadcaster() const + { + return * const_cast< AccessibleOutlineEditSource* > (this); + } + + bool AccessibleOutlineEditSource::IsValid() const + { + if( mpOutliner && mpOutlinerView ) + { + // Our view still on outliner? + sal_uLong nCurrView, nViews; + + for( nCurrView=0, nViews=mpOutliner->GetViewCount(); nCurrView<nViews; ++nCurrView ) + { + if( mpOutliner->GetView(nCurrView) == mpOutlinerView ) + return true; + } + } + + return false; + } + + Point AccessibleOutlineEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode, + MapMode(mrView.GetModel()->GetScaleUnit()) ) ); + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + return mrWindow.LogicToPixel( aPoint, aMapMode ); + } + + return Point(); + } + + Point AccessibleOutlineEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint( mrWindow.PixelToLogic( rPoint, aMapMode ) ); + return OutputDevice::LogicToLogic( aPoint, + MapMode(mrView.GetModel()->GetScaleUnit()), + rMapMode ); + } + + return Point(); + } + + void AccessibleOutlineEditSource::Notify( SfxBroadcaster& rBroadcaster, const SfxHint& rHint ) + { + bool bDispose = false; + + if( &rBroadcaster == mpOutliner ) + { + if( rHint.GetId() == SfxHintId::Dying ) + { + bDispose = true; + mpOutliner = nullptr; + } + } + else + { + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + // model is dying under us, going defunc + bDispose = true; + } + } + } + + if( bDispose ) + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link<EENotify&,void>() ); + mpOutliner = nullptr; + mpOutlinerView = nullptr; + Broadcast( TextHint( SfxHintId::Dying ) ); + } + } + + IMPL_LINK(AccessibleOutlineEditSource, NotifyHdl, EENotify&, rNotify, void) + { + ::std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify) ); + + if (aHint) + { + Broadcast(*aHint); + } + } + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineView.cxx b/sd/source/ui/accessibility/AccessibleOutlineView.cxx new file mode 100644 index 000000000..4e020efef --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineView.cxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/accessibility/AccessibleEventId.hpp> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <Window.hxx> +#include <OutlineViewShell.hxx> +#include <DrawDocShell.hxx> +#include <OutlineView.hxx> +#include <View.hxx> +#include <AccessibleOutlineView.hxx> +#include <AccessibleOutlineEditSource.hxx> +#include <drawdoc.hxx> +#include <strings.hrc> +#include <sdresid.hxx> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +//===== internal ============================================================ + +AccessibleOutlineView::AccessibleOutlineView ( + ::sd::Window* pSdWindow, + ::sd::OutlineViewShell* pViewShell, + const uno::Reference<frame::XController>& rxController, + const uno::Reference<XAccessible>& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + maTextHelper( ::std::unique_ptr< SvxEditSource >() ) +{ + SolarMutexGuard aGuard; + + // Beware! Here we leave the paths of the UNO API and descend into the + // depths of the core. Necessary for making the edit engine accessible. + if (!pSdWindow) + return; + + ::sd::View* pView = pViewShell->GetView(); + + auto pShellView = dynamic_cast<::sd::OutlineView* >( pView ); + if(!pShellView) + return; + + OutlinerView* pOutlineView = pShellView->GetViewByWindow( pSdWindow ); + SdrOutliner& rOutliner = pShellView->GetOutliner(); + + if( pOutlineView ) + { + maTextHelper.SetEditSource( ::std::unique_ptr< SvxEditSource >( new AccessibleOutlineEditSource( + rOutliner, *pView, *pOutlineView, *pSdWindow ) ) ); + } +} + +AccessibleOutlineView::~AccessibleOutlineView() +{ +} + +void AccessibleOutlineView::Init() +{ + // Set event source _before_ starting to listen + maTextHelper.SetEventSource(this); + + AccessibleDocumentViewBase::Init (); +} + +void AccessibleOutlineView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + + UpdateChildren(); +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleOutlineView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + // forward + return maTextHelper.GetChildCount(); +} + +uno::Reference<XAccessible> SAL_CALL + AccessibleOutlineView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + // Forward request to children manager. + return maTextHelper.GetChild(nIndex); +} + +OUString SAL_CALL + AccessibleOutlineView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if (sFileName.isEmpty()) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + if (!sFileName.isEmpty()) + { + sName = sFileName + " - " + sName; + } + } + return sName; +} + +//===== XAccessibleEventBroadcaster ======================================== + +void SAL_CALL AccessibleOutlineView::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // delegate listener handling to children manager. + if ( ! IsDisposed()) + maTextHelper.AddEventListener(xListener); + AccessibleContextBase::addEventListener(xListener); +} + +void SAL_CALL AccessibleOutlineView::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // forward + if ( ! IsDisposed()) + maTextHelper.RemoveEventListener(xListener); + AccessibleContextBase::removeEventListener(xListener); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleOutlineView::getImplementationName() +{ + return "AccessibleOutlineView"; +} + +//===== XEventListener ====================================================== + +//===== protected internal ================================================== + +void AccessibleOutlineView::Activated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(); +} + +void AccessibleOutlineView::Deactivated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(false); +} + +void SAL_CALL AccessibleOutlineView::disposing() +{ + // dispose children + maTextHelper.Dispose(); + + AccessibleDocumentViewBase::disposing (); +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleOutlineView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + //add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // The current page changed. Update the children accordingly. + UpdateChildren(); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue, rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + // The visible area changed. Update the children accordingly. + UpdateChildren(); + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +/// Create a name for this view. +OUString AccessibleOutlineView::CreateAccessibleName() +{ + return SdResId(SID_SD_A11Y_I_OUTLINEVIEW_N); +} + +void AccessibleOutlineView::UpdateChildren() +{ + SolarMutexGuard aGuard; + + // Update visible children + maTextHelper.UpdateChildren(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePageShape.cxx b/sd/source/ui/accessibility/AccessiblePageShape.cxx new file mode 100644 index 000000000..2900019ae --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePageShape.cxx @@ -0,0 +1,261 @@ +/* -*- 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 <AccessiblePageShape.hxx> +#include <svx/AccessibleShapeInfo.hxx> +#include <svx/IAccessibleViewForwarder.hxx> +#include <tools/diagnose_ex.h> +#include <tools/gen.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/drawing/XMasterPageTarget.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ + +AccessiblePageShape::AccessiblePageShape ( + const uno::Reference<drawing::XDrawPage>& rxPage, + const uno::Reference<XAccessible>& rxParent, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (AccessibleShapeInfo (nullptr, rxParent), rShapeTreeInfo), + mxPage (rxPage) +{ + // The main part of the initialization is done in the init method which + // has to be called from this constructor's caller. +} + +AccessiblePageShape::~AccessiblePageShape() +{ +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessiblePageShape::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 + AccessiblePageShape::getAccessibleChild( sal_Int32 ) +{ + throw lang::IndexOutOfBoundsException ("page shape has no children", + static_cast<uno::XWeak*>(this)); +} + +//===== XAccessibleComponent ================================================ + +awt::Rectangle SAL_CALL AccessiblePageShape::getBounds() +{ + ThrowIfDisposed (); + + awt::Rectangle aBoundingBox; + + if (maShapeTreeInfo.GetViewForwarder() != nullptr) + { + uno::Reference<beans::XPropertySet> xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aValue; + + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aBoundingBox.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aBoundingBox.Y; + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aBoundingBox.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aBoundingBox.Height; + } + + // Transform coordinates from internal to pixel. + ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Size (aBoundingBox.Width, aBoundingBox.Height)); + ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Point (aBoundingBox.X, aBoundingBox.Y)); + + // Clip the shape's bounding box with the bounding box of its parent. + Reference<XAccessibleComponent> xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + // Make the coordinates relative to the parent. + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + int x = aPixelPosition.getX() - aParentLocation.X; + int y = aPixelPosition.getY() - aParentLocation.Y; + + // Clip with parent (with coordinates relative to itself). + ::tools::Rectangle aBBox ( + x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight()); + awt::Size aParentSize (xParentComponent->getSize()); + ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height); + aBBox = aBBox.GetIntersection (aParentBBox); + aBoundingBox = awt::Rectangle ( + aBBox.Left(), + aBBox.Top(), + aBBox.getWidth(), + aBBox.getHeight()); + } + else + aBoundingBox = awt::Rectangle ( + aPixelPosition.getX(), aPixelPosition.getY(), + aPixelSize.getWidth(), aPixelSize.getHeight()); + } + + return aBoundingBox; +} + +sal_Int32 SAL_CALL AccessiblePageShape::getForeground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x0ffffffL); + + try + { + uno::Reference<beans::XPropertySet> aSet (mxPage, uno::UNO_QUERY); + if (aSet.is()) + { + uno::Any aColor = aSet->getPropertyValue ("LineColor"); + aColor >>= nColor; + } + } + catch (const css::beans::UnknownPropertyException&) + { + // Ignore exception and return default color. + } + return nColor; +} + +/** Extract the background color from the Background property of the + draw page or its master page. +*/ +sal_Int32 SAL_CALL AccessiblePageShape::getBackground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x01020ffL); + + try + { + uno::Reference<beans::XPropertySet> xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aBGSet = xSet->getPropertyValue ("Background"); + Reference<beans::XPropertySet> xBGSet (aBGSet, uno::UNO_QUERY); + if ( ! xBGSet.is()) + { + // Draw page has no Background property. Try the master + // page instead. + Reference<drawing::XMasterPageTarget> xTarget (mxPage, uno::UNO_QUERY); + if (xTarget.is()) + { + xSet.set(xTarget->getMasterPage(), uno::UNO_QUERY); + aBGSet = xSet->getPropertyValue ("Background"); + xBGSet.set(aBGSet, uno::UNO_QUERY); + } + } + // Fetch the fill color. Has to be extended to cope with + // gradients, hashes, and bitmaps. + if (xBGSet.is()) + { + uno::Any aColor = xBGSet->getPropertyValue ("FillColor"); + aColor >>= nColor; + } + else + SAL_WARN("sd", "no Background property in page"); + } + } + catch (const css::beans::UnknownPropertyException&) + { + TOOLS_WARN_EXCEPTION("sd", "caught exception due to unknown property"); + // Ignore exception and return default color. + } + return nColor; +} + +// XServiceInfo + +OUString SAL_CALL + AccessiblePageShape::getImplementationName() +{ + ThrowIfDisposed (); + return "AccessiblePageShape"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessiblePageShape::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleShape::getSupportedServiceNames(); +} + +//===== XComponent ========================================================== + +void AccessiblePageShape::dispose() +{ + // Cleanup. + mxShape = nullptr; + + // Call base classes. + AccessibleContextBase::dispose (); +} + +//===== protected internal ================================================== + +OUString + AccessiblePageShape::CreateAccessibleBaseName() +{ + return "PageShape"; +} + +OUString + AccessiblePageShape::CreateAccessibleName() +{ + Reference<beans::XPropertySet> xPageProperties (mxPage, UNO_QUERY); + + // Get name of the current slide. + OUString sCurrentSlideName; + try + { + if (xPageProperties.is()) + { + xPageProperties->getPropertyValue( "LinkDisplayName" ) >>= sCurrentSlideName; + } + } + catch (const beans::UnknownPropertyException&) + { + } + + return CreateAccessibleBaseName()+": "+sCurrentSlideName; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx new file mode 100644 index 000000000..a8c37fa89 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx @@ -0,0 +1,76 @@ +/* -*- 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 <AccessiblePresentationGraphicShape.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <SdShapeTypes.hxx> + +#include <svx/ShapeTypeHandler.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationGraphicShape::AccessiblePresentationGraphicShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleGraphicShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationGraphicShape::~AccessiblePresentationGraphicShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationGraphicShape::getImplementationName() +{ + return "AccessiblePresentationGraphicShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationGraphicShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_GRAPHIC_OBJECT: + sName = "ImpressGraphicObject"; + break; + default: + sName = "UnknownAccessibleImpressShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +sal_Int16 SAL_CALL AccessiblePresentationGraphicShape::getAccessibleRole() +{ + return AccessibleRole::GRAPHIC; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx new file mode 100644 index 000000000..411d04af1 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx @@ -0,0 +1,84 @@ +/* -*- 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 <AccessiblePresentationOLEShape.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/drawing/XShape.hpp> + +#include <SdShapeTypes.hxx> + +#include <svx/ShapeTypeHandler.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationOLEShape::AccessiblePresentationOLEShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleOLEShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationOLEShape::~AccessiblePresentationOLEShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationOLEShape::getImplementationName() +{ + return "AccessiblePresentationOLEShape"; +} + +/// Set this object's name if it is different to the current name. +OUString AccessiblePresentationOLEShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_OLE: + sName = "ImpressOLE"; + break; + case PRESENTATION_CHART: + sName = "ImpressChart"; + break; + case PRESENTATION_TABLE: + sName = "ImpressTable"; + break; + default: + sName = "UnknownAccessibleImpressOLEShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +// Return this object's role. +sal_Int16 SAL_CALL AccessiblePresentationOLEShape::getAccessibleRole() +{ + return AccessibleRole::EMBEDDED_OBJECT; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx new file mode 100644 index 000000000..e4afe7e9a --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx @@ -0,0 +1,146 @@ +/* -*- 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 <AccessiblePresentationShape.hxx> + +#include <SdShapeTypes.hxx> + +#include <strings.hrc> +#include <sdresid.hxx> +#include <svx/ShapeTypeHandler.hxx> + +#include <com/sun/star/drawing/XShape.hpp> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationShape::AccessiblePresentationShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationShape::~AccessiblePresentationShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationShape::getImplementationName() +{ + return "AccessiblePresentationShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +OUString AccessiblePresentationShape::GetStyle() const +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N_STYLE); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N_STYLE); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N_STYLE); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N_STYLE); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N_STYLE); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N_STYLE); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N_STYLE); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N_STYLE); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N_STYLE); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N_STYLE); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N_STYLE); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx new file mode 100644 index 000000000..13fc60db0 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx @@ -0,0 +1,429 @@ +/* -*- 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 <AccessibleSlideSorterObject.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> +#include <view/SlideSorterView.hxx> +#include <view/SlsLayouter.hxx> +#include <view/SlsPageObjectLayouter.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <sal/log.hxx> + +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <strings.hrc> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +AccessibleSlideSorterObject::AccessibleSlideSorterObject( + const Reference<XAccessible>& rxParent, + ::sd::slidesorter::SlideSorter& rSlideSorter, + sal_uInt16 nPageNumber) + : mxParent(rxParent), + mnPageNumber(nPageNumber), + mrSlideSorter(rSlideSorter), + mnClientId(0) +{ +} + +AccessibleSlideSorterObject::~AccessibleSlideSorterObject() +{ + if ( ! IsDisposed()) + dispose(); +} + +void AccessibleSlideSorterObject::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference<XWeak>(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent(mnClientId, aEventObject); + } +} + +void AccessibleSlideSorterObject::disposing(std::unique_lock<std::mutex>&) +{ + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(mnClientId, *this); + mnClientId = 0; + } +} + +//===== XAccessible =========================================================== + +Reference<XAccessibleContext> SAL_CALL + AccessibleSlideSorterObject::getAccessibleContext() +{ + ThrowIfDisposed(); + return this; +} + +//===== XAccessibleContext ==================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleChildCount() +{ + ThrowIfDisposed(); + return 0; +} + +Reference<XAccessible> SAL_CALL AccessibleSlideSorterObject::getAccessibleChild (sal_Int32 ) +{ + ThrowIfDisposed(); + throw lang::IndexOutOfBoundsException(); +} + +Reference<XAccessible> SAL_CALL AccessibleSlideSorterObject::getAccessibleParent() +{ + ThrowIfDisposed(); + return mxParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleIndexInParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + if (mxParent.is()) + { + Reference<XAccessibleContext> xParentContext (mxParent->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; i<nChildCount; ++i) + if (xParentContext->getAccessibleChild(i).get() + == static_cast<XAccessible*>(this)) + { + nIndexInParent = i; + break; + } + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterObject::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::SHAPE; +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleDescription() +{ + ThrowIfDisposed(); + return SdResId(STR_PAGE); +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleName() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + SdPage* pPage = GetPage(); + if (pPage != nullptr) + return pPage->GetName(); + else + return OUString(); +} + +Reference<XAccessibleRelationSet> SAL_CALL + AccessibleSlideSorterObject::getAccessibleRelationSet() +{ + ThrowIfDisposed(); + return Reference<XAccessibleRelationSet>(); +} + +Reference<XAccessibleStateSet> SAL_CALL + AccessibleSlideSorterObject::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + if (mxParent.is()) + { + // Unconditional states. + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::VISIBLE); + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::SENSITIVE); + + // Conditional states. + if (mrSlideSorter.GetController().GetPageSelector().IsPageSelected(mnPageNumber)) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex() == mnPageNumber) + if (mrSlideSorter.GetController().GetFocusManager().IsFocusShowing()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterObject::getLocale() +{ + ThrowIfDisposed(); + // Delegate request to parent. + if (mxParent.is()) + { + 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(); +} + +//===== XAccessibleEventBroadcaster =========================================== + +void SAL_CALL AccessibleSlideSorterObject::addAccessibleEventListener( + const Reference<XAccessibleEventListener>& rxListener) +{ + if (!rxListener.is()) + return; + + const std::unique_lock aGuard(m_aMutex); + + if (IsDisposed()) + { + uno::Reference<uno::XInterface> x (static_cast<lang::XComponent *>(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if (mnClientId == 0) + mnClientId = comphelper::AccessibleEventNotifier::registerClient(); + comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); + } +} + +void SAL_CALL AccessibleSlideSorterObject::removeAccessibleEventListener( + const Reference<XAccessibleEventListener>& rxListener) +{ + ThrowIfDisposed(); + if (!(rxListener.is() && mnClientId)) + return; + + const std::unique_lock aGuard(m_aMutex); + + 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; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterObject::containsPoint(const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Size aSize (getSize()); + return (aPoint.X >= 0) + && (aPoint.X < aSize.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aSize.Height); +} + +Reference<XAccessible> SAL_CALL + AccessibleSlideSorterObject::getAccessibleAtPoint(const awt::Point& ) +{ + return nullptr; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterObject::getBounds() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + ::tools::Rectangle aBBox ( + mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber), + ::sd::slidesorter::view::PageObjectLayouter::Part::PageObject, + ::sd::slidesorter::view::PageObjectLayouter::WindowCoordinateSystem)); + + if (mxParent.is()) + { + Reference<XAccessibleComponent> xParentComponent(mxParent->getAccessibleContext(), UNO_QUERY); + if (xParentComponent.is()) + { + awt::Rectangle aParentBBox (xParentComponent->getBounds()); + aBBox.Intersection(::tools::Rectangle( + aParentBBox.X, + aParentBBox.Y, + aParentBBox.Width, + aParentBBox.Height)); + } + } + + return awt::Rectangle( + aBBox.Left(), + aBBox.Top(), + aBBox.GetWidth(), + aBBox.GetHeight()); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocation () +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Point(aBBox.X, aBBox.Y); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocationOnScreen() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + awt::Point aLocation (getLocation()); + + if (mxParent.is()) + { + Reference<XAccessibleComponent> xParentComponent(mxParent->getAccessibleContext(),UNO_QUERY); + if (xParentComponent.is()) + { + const awt::Point aParentLocationOnScreen(xParentComponent->getLocationOnScreen()); + aLocation.X += aParentLocationOnScreen.X; + aLocation.Y += aParentLocationOnScreen.Y; + } + } + + return aLocation; +} + +awt::Size SAL_CALL AccessibleSlideSorterObject::getSize() +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Size(aBBox.Width,aBBox.Height); +} + +void SAL_CALL AccessibleSlideSorterObject::grabFocus() +{ + // nothing to do +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getForeground() +{ + ThrowIfDisposed (); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getBackground() +{ + ThrowIfDisposed (); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterObject::getImplementationName() +{ + return "AccessibleSlideSorterObject"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterObject::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterObject::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence<OUString> { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext") + }; +} + +void AccessibleSlideSorterObject::ThrowIfDisposed() +{ + if (m_bDisposed) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast<uno::XWeak*>(this)); + } +} + +bool AccessibleSlideSorterObject::IsDisposed() const +{ + return m_bDisposed; +} + +SdPage* AccessibleSlideSorterObject::GetPage() const +{ + ::sd::slidesorter::model::SharedPageDescriptor pDescriptor( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber)); + if (pDescriptor) + return pDescriptor->GetPage(); + else + return nullptr; +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx new file mode 100644 index 000000000..87eea89d2 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx @@ -0,0 +1,950 @@ +/* -*- 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 <AccessibleSlideSorterView.hxx> +#include <AccessibleSlideSorterObject.hxx> + +#include <SlideSorter.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsFocusManager.hxx> +#include <controller/SlsSelectionManager.hxx> +#include <view/SlideSorterView.hxx> +#include <model/SlideSorterModel.hxx> +#include <model/SlsPageDescriptor.hxx> + +#include <ViewShell.hxx> +#include <ViewShellHint.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> + +#include <sdresid.hxx> +#include <strings.hrc> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/accessibleeventnotifier.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <o3tl/safeint.hxx> +#include <unotools/accessiblestatesethelper.hxx> +#include <rtl/ref.hxx> +#include <sal/log.hxx> +#include <i18nlangtag/languagetag.hxx> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +/** Inner implementation class of the AccessibleSlideSorterView. + + Note that some event broadcasting is done asynchronously because + otherwise it could lead to deadlocks on (at least) some Solaris + machines. Probably (but unverified) this can happen on all GTK based + systems. The asynchronous broadcasting is just a workaround for a + poorly understood problem. +*/ +class AccessibleSlideSorterView::Implementation + : public SfxListener +{ +public: + Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow); + virtual ~Implementation() override; + + void RequestUpdateChildren(); + void Clear(); + sal_Int32 GetVisibleChildCount() const; + AccessibleSlideSorterObject* GetAccessibleChild (sal_Int32 nIndex); + AccessibleSlideSorterObject* GetVisibleChild (sal_Int32 nIndex); + + void ConnectListeners(); + void ReleaseListeners(); + void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + DECL_LINK(WindowEventListener, VclWindowEvent&, void); + DECL_LINK(SelectionChangeListener, LinkParamNone*, void); + DECL_LINK(BroadcastSelectionChange, void*, void); + DECL_LINK(FocusChangeListener, LinkParamNone*, void); + DECL_LINK(VisibilityChangeListener, LinkParamNone*, void); + DECL_LINK(UpdateChildrenCallback, void*, void); + + void Activated(); +private: + AccessibleSlideSorterView& mrAccessibleSlideSorter; + ::sd::slidesorter::SlideSorter& mrSlideSorter; + typedef ::std::vector<rtl::Reference<AccessibleSlideSorterObject> > PageObjectList; + PageObjectList maPageObjects; + sal_Int32 mnFirstVisibleChild; + sal_Int32 mnLastVisibleChild; + bool mbListeningToDocument; + VclPtr<vcl::Window> mpWindow; + sal_Int32 mnFocusedIndex; + bool mbModelChangeLocked; + ImplSVEvent * mnUpdateChildrenUserEventId; + ImplSVEvent * mnSelectionChangeUserEventId; + + void UpdateChildren(); +}; + +//===== AccessibleSlideSorterView ============================================= + +AccessibleSlideSorterView::AccessibleSlideSorterView( + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pContentWindow) + : AccessibleSlideSorterViewBase(m_aMutex), + mrSlideSorter(rSlideSorter), + mnClientId(0), + mpContentWindow(pContentWindow) +{ +} + +void AccessibleSlideSorterView::Init() +{ + mpImpl.reset(new Implementation(*this,mrSlideSorter,mpContentWindow)); +} + +AccessibleSlideSorterView::~AccessibleSlideSorterView() +{ + Destroyed (); +} + +void AccessibleSlideSorterView::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue ) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference<XWeak>(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent (mnClientId, aEventObject); + } +} + +void SAL_CALL AccessibleSlideSorterView::disposing() +{ + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } + mpImpl.reset(); +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::GetAccessibleChildImplementation ( + sal_Int32 nIndex) +{ + AccessibleSlideSorterObject* pResult = nullptr; + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex>=0 && nIndex<mpImpl->GetVisibleChildCount()) + pResult = mpImpl->GetVisibleChild(nIndex); + + return pResult; +} + +void AccessibleSlideSorterView::Destroyed() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } +} + +//===== XAccessible ========================================================= + +Reference<XAccessibleContext > SAL_CALL + AccessibleSlideSorterView::getAccessibleContext() +{ + ThrowIfDisposed (); + return this; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleChildCount() +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + return mpImpl->GetVisibleChildCount(); +} + +Reference<XAccessible > SAL_CALL + AccessibleSlideSorterView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex<0 || nIndex>=mpImpl->GetVisibleChildCount()) + throw lang::IndexOutOfBoundsException(); + + return mpImpl->GetVisibleChild(nIndex); +} + +Reference<XAccessible > SAL_CALL AccessibleSlideSorterView::getAccessibleParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + Reference<XAccessible> xParent; + + if (mpContentWindow != nullptr) + { + vcl::Window* pParent = mpContentWindow->GetAccessibleParentWindow(); + if (pParent != nullptr) + xParent = pParent->GetAccessible(); + } + + return xParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleIndexInParent() +{ + OSL_ASSERT(getAccessibleParent().is()); + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + Reference<XAccessibleContext> xParentContext (getAccessibleParent()->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; i<nChildCount; ++i) + if (xParentContext->getAccessibleChild(i).get() + == static_cast<XAccessible*>(this)) + { + nIndexInParent = i; + break; + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterView::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::DOCUMENT; +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleDescription() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_D); +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleName() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_N); +} + +Reference<XAccessibleRelationSet> SAL_CALL + AccessibleSlideSorterView::getAccessibleRelationSet() +{ + return Reference<XAccessibleRelationSet>(); +} + +Reference<XAccessibleStateSet > SAL_CALL + AccessibleSlideSorterView::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (mpContentWindow!=nullptr) + { + if (mpContentWindow->IsVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + if (mpContentWindow->IsReallyVisible()) + pStateSet->AddState(AccessibleStateType::SHOWING); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterView::getLocale() +{ + ThrowIfDisposed (); + Reference<XAccessibleContext> xParentContext; + Reference<XAccessible> xParent (getAccessibleParent()); + if (xParent.is()) + xParentContext = xParent->getAccessibleContext(); + + if (xParentContext.is()) + return xParentContext->getLocale(); + else + // Strange, no parent! Anyway, return the default locale. + return Application::GetSettings().GetLanguageTag().getLocale(); +} + +void SAL_CALL AccessibleSlideSorterView::addAccessibleEventListener( + const Reference<XAccessibleEventListener >& rxListener) +{ + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + 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 AccessibleSlideSorterView::removeAccessibleEventListener( + const Reference<XAccessibleEventListener >& rxListener) +{ + ThrowIfDisposed(); + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + if (mnClientId == 0) + 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; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterView::containsPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Rectangle aBBox (getBounds()); + return (aPoint.X >= 0) + && (aPoint.X < aBBox.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aBBox.Height); +} + +Reference<XAccessible> SAL_CALL + AccessibleSlideSorterView::getAccessibleAtPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + Reference<XAccessible> xAccessible; + const SolarMutexGuard aSolarGuard; + + const Point aTestPoint (aPoint.X, aPoint.Y); + ::sd::slidesorter::model::SharedPageDescriptor pHitDescriptor ( + mrSlideSorter.GetController().GetPageAt(aTestPoint)); + if (pHitDescriptor) + xAccessible = mpImpl->GetAccessibleChild( + (pHitDescriptor->GetPage()->GetPageNum()-1)/2); + + return xAccessible; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterView::getBounds() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Rectangle aBBox; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + const Size aSize (mpContentWindow->GetOutputSizePixel()); + + aBBox.X = aPosition.X(); + aBBox.Y = aPosition.Y(); + aBBox.Width = aSize.Width(); + aBBox.Height = aSize.Height(); + } + + return aBBox; +} + +awt::Point SAL_CALL AccessibleSlideSorterView::getLocation() +{ + ThrowIfDisposed(); + awt::Point aLocation; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + aLocation.X = aPosition.X(); + aLocation.Y = aPosition.Y(); + } + + return aLocation; +} + +/** Calculate the location on screen from the parent's location on screen + and our own relative location. +*/ +awt::Point SAL_CALL AccessibleSlideSorterView::getLocationOnScreen() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Point aParentLocationOnScreen; + + Reference<XAccessible> xParent (getAccessibleParent()); + if (xParent.is()) + { + Reference<XAccessibleComponent> xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentLocationOnScreen = xParentComponent->getLocationOnScreen(); + } + + awt::Point aLocationOnScreen (getLocation()); + aLocationOnScreen.X += aParentLocationOnScreen.X; + aLocationOnScreen.Y += aParentLocationOnScreen.Y; + + return aLocationOnScreen; +} + +awt::Size SAL_CALL AccessibleSlideSorterView::getSize() +{ + ThrowIfDisposed(); + awt::Size aSize; + + if (mpContentWindow != nullptr) + { + const Size aOutputSize (mpContentWindow->GetOutputSizePixel()); + aSize.Width = aOutputSize.Width(); + aSize.Height = aOutputSize.Height(); + } + + return aSize; +} + +void SAL_CALL AccessibleSlideSorterView::grabFocus() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + if (mpContentWindow) + mpContentWindow->GrabFocus(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getForeground() +{ + ThrowIfDisposed(); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast<sal_Int32>(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getBackground() +{ + ThrowIfDisposed(); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +//===== XAccessibleSelection ================================================== + +void SAL_CALL AccessibleSlideSorterView::selectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().SelectPage(pChild->GetPageNumber()); +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::isAccessibleChildSelected (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + bool bIsSelected = false; + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + bIsSelected = mrSlideSorter.GetController().GetPageSelector().IsPageSelected( + pChild->GetPageNumber()); + + return bIsSelected; +} + +void SAL_CALL AccessibleSlideSorterView::clearAccessibleSelection() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); +} + +void SAL_CALL AccessibleSlideSorterView::selectAllAccessibleChildren() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().SelectAllPages(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getSelectedAccessibleChildCount() +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + return mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount(); +} + +Reference<XAccessible > SAL_CALL + AccessibleSlideSorterView::getSelectedAccessibleChild (sal_Int32 nSelectedChildIndex ) +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + Reference<XAccessible> xChild; + + ::sd::slidesorter::controller::PageSelector& rSelector ( + mrSlideSorter.GetController().GetPageSelector()); + sal_Int32 nPageCount(rSelector.GetPageCount()); + sal_Int32 nSelectedCount = 0; + for (sal_Int32 i=0; i<nPageCount; i++) + if (rSelector.IsPageSelected(i)) + { + if (nSelectedCount == nSelectedChildIndex) + { + xChild = mpImpl->GetAccessibleChild(i); + break; + } + ++nSelectedCount; + } + + if ( ! xChild.is() ) + throw lang::IndexOutOfBoundsException(); + + return xChild; +} + +void SAL_CALL AccessibleSlideSorterView::deselectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().DeselectPage(pChild->GetPageNumber()); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterView::getImplementationName() +{ + return "AccessibleSlideSorterView"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterView::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence<OUString> { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext"), + OUString("com.sun.star.drawing.AccessibleSlideSorterView") + }; +} + +void AccessibleSlideSorterView::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast<uno::XWeak*>(this)); + } +} + +//===== AccessibleSlideSorterView::Implementation ============================= + +AccessibleSlideSorterView::Implementation::Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow) + : mrAccessibleSlideSorter(rAccessibleSlideSorter), + mrSlideSorter(rSlideSorter), + mnFirstVisibleChild(0), + mnLastVisibleChild(-1), + mbListeningToDocument(false), + mpWindow(pWindow), + mnFocusedIndex(-1), + mbModelChangeLocked(false), + mnUpdateChildrenUserEventId(nullptr), + mnSelectionChangeUserEventId(nullptr) +{ + ConnectListeners(); + UpdateChildren(); +} + +AccessibleSlideSorterView::Implementation::~Implementation() +{ + if (mnUpdateChildrenUserEventId != nullptr) + Application::RemoveUserEvent(mnUpdateChildrenUserEventId); + if (mnSelectionChangeUserEventId != nullptr) + Application::RemoveUserEvent(mnSelectionChangeUserEventId); + ReleaseListeners(); + Clear(); +} + +void AccessibleSlideSorterView::Implementation::RequestUpdateChildren() +{ + if (mnUpdateChildrenUserEventId == nullptr) + mnUpdateChildrenUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, + UpdateChildrenCallback)); +} + +void AccessibleSlideSorterView::Implementation::UpdateChildren() +{ + //By default, all children should be accessible. So here workaround is to make all children visible. + // MT: This was in UpdateVisibility, which has some similarity, and hg merge automatically has put it here. Correct?! + // In the IA2 CWS, also setting mnFirst/LastVisibleChild was commented out! + mnLastVisibleChild = maPageObjects.size(); + + if (mbModelChangeLocked) + { + // Do nothing right now. When the flag is reset, this method is + // called again. + return; + } + + const Range aRange (mrSlideSorter.GetView().GetVisiblePageRange()); + mnFirstVisibleChild = aRange.Min(); + mnLastVisibleChild = aRange.Max(); + + // Release all children. + Clear(); + + // Create new children for the modified visible range. + maPageObjects.resize(mrSlideSorter.GetModel().GetPageCount()); + + // No Visible children + if (mnFirstVisibleChild == -1 && mnLastVisibleChild == -1) + return; + + for (sal_Int32 nIndex(mnFirstVisibleChild); nIndex<=mnLastVisibleChild; ++nIndex) + GetAccessibleChild(nIndex); +} + +void AccessibleSlideSorterView::Implementation::Clear() +{ + for (auto& rxPageObject : maPageObjects) + if (rxPageObject != nullptr) + { + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(Reference<XAccessible>(rxPageObject)), + Any()); + + Reference<XComponent> xComponent (Reference<XWeak>(rxPageObject), UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + rxPageObject = nullptr; + } + maPageObjects.clear(); +} + +sal_Int32 AccessibleSlideSorterView::Implementation::GetVisibleChildCount() const +{ + if (mnFirstVisibleChild<=mnLastVisibleChild && mnFirstVisibleChild>=0) + return mnLastVisibleChild - mnFirstVisibleChild + 1; + else + return 0; +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::Implementation::GetVisibleChild ( + sal_Int32 nIndex) +{ + assert(nIndex>=0 && nIndex<GetVisibleChildCount()); + + return GetAccessibleChild(nIndex+mnFirstVisibleChild); +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::Implementation::GetAccessibleChild ( + sal_Int32 nIndex) +{ + AccessibleSlideSorterObject* pChild = nullptr; + + if (nIndex>=0 && o3tl::make_unsigned(nIndex)<maPageObjects.size()) + { + if (maPageObjects[nIndex] == nullptr) + { + ::sd::slidesorter::model::SharedPageDescriptor pDescriptor( + mrSlideSorter.GetModel().GetPageDescriptor(nIndex)); + if (pDescriptor) + { + maPageObjects[nIndex] = new AccessibleSlideSorterObject( + &mrAccessibleSlideSorter, + mrSlideSorter, + (pDescriptor->GetPage()->GetPageNum()-1)/2); + + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(), + Any(Reference<XAccessible>(maPageObjects[nIndex]))); + } + + } + + pChild = maPageObjects[nIndex].get(); + } + else + { + OSL_ASSERT(nIndex>=0 && o3tl::make_unsigned(nIndex)<maPageObjects.size()); + } + + return pChild; +} + +void AccessibleSlideSorterView::Implementation::ConnectListeners() +{ + StartListening (*mrSlideSorter.GetModel().GetDocument()); + if (mrSlideSorter.GetViewShell() != nullptr) + StartListening (*mrSlideSorter.GetViewShell()); + mbListeningToDocument = true; + + if (mpWindow != nullptr) + mpWindow->AddEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + mrSlideSorter.GetController().GetSelectionManager()->AddSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetController().GetFocusManager().AddFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetView().AddVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); +} + +void AccessibleSlideSorterView::Implementation::ReleaseListeners() +{ + mrSlideSorter.GetController().GetFocusManager().RemoveFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetController().GetSelectionManager()->RemoveSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetView().RemoveVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); + + if (mpWindow != nullptr) + mpWindow->RemoveEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + if (mbListeningToDocument) + { + if (mrSlideSorter.GetViewShell() != nullptr && !IsListening(*mrSlideSorter.GetViewShell())) + { // ??? is it even possible that ConnectListeners is called with no + // view shell and this one with a view shell? + StartListening(*mrSlideSorter.GetViewShell()); + } + EndListening (*mrSlideSorter.GetModel().GetDocument()); + mbListeningToDocument = false; + } +} + +void AccessibleSlideSorterView::Implementation::Notify ( + SfxBroadcaster&, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + RequestUpdateChildren(); + break; + default: + break; + } + } + else if (auto pViewShellHint = dynamic_cast<const sd::ViewShellHint*>(&rHint)) + { + switch (pViewShellHint->GetHintId()) + { + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START: + mbModelChangeLocked = true; + break; + + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END: + mbModelChangeLocked = false; + RequestUpdateChildren(); + break; + default: + break; + } + } +} + +void AccessibleSlideSorterView::SwitchViewActivated() +{ + // Firstly, set focus to view + FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + + mpImpl->Activated(); +} + +void AccessibleSlideSorterView::Implementation::Activated() +{ + mrSlideSorter.GetController().GetFocusManager().ShowFocus(); + +} + +IMPL_LINK(AccessibleSlideSorterView::Implementation, WindowEventListener, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowMove: + case VclEventId::WindowResize: + RequestUpdateChildren(); + break; + + case VclEventId::WindowGetFocus: + case VclEventId::WindowLoseFocus: + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); + break; + default: + break; + } +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, SelectionChangeListener, LinkParamNone*, void) +{ + if (mnSelectionChangeUserEventId == nullptr) + mnSelectionChangeUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, BroadcastSelectionChange)); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, BroadcastSelectionChange, void*, void) +{ + mnSelectionChangeUserEventId = nullptr; + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, FocusChangeListener, LinkParamNone*, void) +{ + sal_Int32 nNewFocusedIndex ( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex()); + + bool bHasFocus = mrSlideSorter.GetController().GetFocusManager().IsFocusShowing(); + if (!bHasFocus) + nNewFocusedIndex = -1; + + // add a checker whether the focus event is sent out. Only after sent, the mnFocusedIndex should be updated. + bool bSentFocus = false; + if (nNewFocusedIndex == mnFocusedIndex) + return; + + if (mnFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(mnFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(AccessibleStateType::FOCUSED), + Any()); + bSentFocus = true; + } + } + if (nNewFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(nNewFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + bSentFocus = true; + } + } + if (bSentFocus) + mnFocusedIndex = nNewFocusedIndex; +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, UpdateChildrenCallback, void*, void) +{ + mnUpdateChildrenUserEventId = nullptr; + UpdateChildren(); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, VisibilityChangeListener, LinkParamNone*, void) +{ + UpdateChildren(); +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleViewForwarder.cxx b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx new file mode 100644 index 000000000..09225e27f --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <AccessibleViewForwarder.hxx> +#include <svx/svdpntv.hxx> +#include <vcl/window.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <osl/diagnose.h> + +namespace accessibility +{ +/** For the time being, the implementation of this class will not use the + member mrDevice. Instead the device is retrieved from the view + every time it is used. This is necessary because the device has to stay + up-to-date with the current view and the class has to stay compatible. + May change in the future. +*/ + +AccessibleViewForwarder::AccessibleViewForwarder(SdrPaintView* pView, const OutputDevice& rDevice) + : mpView(pView) + , mnWindowId(0) +{ + // Search the output device to determine its id. + for (sal_uInt32 a(0); a < mpView->PaintWindowCount(); a++) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(a); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + + if (&rOutDev == &rDevice) + { + mnWindowId = static_cast<sal_uInt16>(a); + break; + } + } +} + +AccessibleViewForwarder::~AccessibleViewForwarder() +{ + // empty +} + +::tools::Rectangle AccessibleViewForwarder::GetVisibleArea() const +{ + ::tools::Rectangle aVisibleArea; + + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + aVisibleArea = pPaintWindow->GetVisibleArea(); + } + + return aVisibleArea; +} + +/** Transform the given point into pixel coordinates. After the pixel + coordinates of the window origin are added to make the point coordinates + absolute. +*/ +Point AccessibleViewForwarder::LogicToPixel(const Point& rPoint) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + ::tools::Rectangle aBBox(rOutDev.GetOwnerWindow()->GetWindowExtentsRelative(nullptr)); + return rOutDev.LogicToPixel(rPoint) + aBBox.TopLeft(); + } + else + return Point(); +} + +Size AccessibleViewForwarder::LogicToPixel(const Size& rSize) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast<sal_uInt32>(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast<sal_uInt32>(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + return rOutDev.LogicToPixel(rSize); + } + else + return Size(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/SdShapeTypes.cxx b/sd/source/ui/accessibility/SdShapeTypes.cxx new file mode 100644 index 000000000..7fab0961e --- /dev/null +++ b/sd/source/ui/accessibility/SdShapeTypes.cxx @@ -0,0 +1,132 @@ +/* -*- 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 <svx/ShapeTypeHandler.hxx> +#include <SdShapeTypes.hxx> +#include <AccessiblePresentationShape.hxx> +#include <AccessiblePresentationGraphicShape.hxx> +#include <AccessiblePresentationOLEShape.hxx> + +namespace accessibility { + +static rtl::Reference<AccessibleShape> + CreateSdAccessibleShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + ShapeTypeId nId) +{ + switch (nId) + { + case PRESENTATION_TITLE: + case PRESENTATION_OUTLINER: + case PRESENTATION_SUBTITLE: + case PRESENTATION_PAGE: + case PRESENTATION_NOTES: + case PRESENTATION_HANDOUT: + case PRESENTATION_HEADER: + case PRESENTATION_FOOTER: + case PRESENTATION_DATETIME: + case PRESENTATION_PAGENUMBER: + return new AccessiblePresentationShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_GRAPHIC_OBJECT: + return new AccessiblePresentationGraphicShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_OLE: + case PRESENTATION_CHART: + case PRESENTATION_TABLE: + return new AccessiblePresentationOLEShape (rShapeInfo, rShapeTreeInfo); + + default: + return new AccessibleShape (rShapeInfo, rShapeTreeInfo); + } +} + +void RegisterImpressShapeTypes() +{ + /** List of shape type descriptors corresponding to the + <type>SdShapeTypes</type> enum. + */ + ShapeTypeDescriptor aSdShapeTypeList[] = { + ShapeTypeDescriptor ( + PRESENTATION_OUTLINER, + "com.sun.star.presentation.OutlinerShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_SUBTITLE, + "com.sun.star.presentation.SubtitleShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_GRAPHIC_OBJECT, + "com.sun.star.presentation.GraphicObjectShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGE, + "com.sun.star.presentation.PageShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_OLE, + "com.sun.star.presentation.OLE2Shape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_CHART, + "com.sun.star.presentation.ChartShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TABLE, + "com.sun.star.presentation.TableShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_NOTES, + "com.sun.star.presentation.NotesShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TITLE, + "com.sun.star.presentation.TitleTextShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HANDOUT, + "com.sun.star.presentation.HandoutShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HEADER, + "com.sun.star.presentation.HeaderShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_FOOTER, + "com.sun.star.presentation.FooterShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_DATETIME, + "com.sun.star.presentation.DateTimeShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGENUMBER, + "com.sun.star.presentation.SlideNumberShape", + CreateSdAccessibleShape ) + }; + + ShapeTypeHandler::Instance().AddShapeTypeList ( + PRESENTATION_PAGENUMBER - PRESENTATION_OUTLINER + 1, + aSdShapeTypeList); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |