/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using 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 > PageObjectList; PageObjectList maPageObjects; sal_Int32 mnFirstVisibleChild; sal_Int32 mnLastVisibleChild; bool mbListeningToDocument; VclPtr 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(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 && nIndexGetVisibleChildCount()) 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 SAL_CALL AccessibleSlideSorterView::getAccessibleContext() { ThrowIfDisposed (); return this; } //===== XAccessibleContext ================================================== sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleChildCount() { ThrowIfDisposed(); ::osl::MutexGuard aGuard (m_aMutex); return mpImpl->GetVisibleChildCount(); } Reference 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 SAL_CALL AccessibleSlideSorterView::getAccessibleParent() { ThrowIfDisposed(); const SolarMutexGuard aSolarGuard; Reference 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 xParentContext (getAccessibleParent()->getAccessibleContext()); if (xParentContext.is()) { sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); for (sal_Int32 i=0; igetAccessibleChild(i).get() == static_cast(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 SAL_CALL AccessibleSlideSorterView::getAccessibleRelationSet() { return Reference(); } Reference 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 xParentContext; Reference 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& rxListener) { if (!rxListener.is()) return; const osl::MutexGuard aGuard(m_aMutex); if (rBHelper.bDisposed || rBHelper.bInDispose) { uno::Reference x (static_cast(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& 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 SAL_CALL AccessibleSlideSorterView::getAccessibleAtPoint (const awt::Point& aPoint) { ThrowIfDisposed(); Reference 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 xParent (getAccessibleParent()); if (xParent.is()) { Reference 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(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 SAL_CALL AccessibleSlideSorterView::getSelectedAccessibleChild (sal_Int32 nSelectedChildIndex ) { ThrowIfDisposed (); const SolarMutexGuard aSolarGuard; Reference xChild; ::sd::slidesorter::controller::PageSelector& rSelector ( mrSlideSorter.GetController().GetPageSelector()); sal_Int32 nPageCount(rSelector.GetPageCount()); sal_Int32 nSelectedCount = 0; for (sal_Int32 i=0; iGetAccessibleChild(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("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(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(rxPageObject)), Any()); Reference xComponent (Reference(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=0 && o3tl::make_unsigned(nIndex)GetPage()->GetPageNum()-1)/2); mrAccessibleSlideSorter.FireAccessibleEvent( AccessibleEventId::CHILD, Any(), Any(Reference(maPageObjects[nIndex]))); } } pChild = maPageObjects[nIndex].get(); } else { OSL_ASSERT(nIndex>=0 && o3tl::make_unsigned(nIndex)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(&rHint); switch (pSdrHint->GetKind()) { case SdrHintKind::PageOrderChange: RequestUpdateChildren(); break; default: break; } } else if (auto pViewShellHint = dynamic_cast(&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: */