Kaydet (Commit) a7e52282 authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski

tdf#65587 SM implement ElementControl accessibility

This took me ages to implement. I'm still not 100% sure, it works
correct. One main problem was the loop where the ATK listener will
rebuild the list of children based on the current child count.

Then there is the "broken" SPI bridge behaviour. I could actually
test this with the gtk3 backend just fine, if I started LO *after*
the accerciser. Otherwise the displayed tree will be really broken
and the add and remove child events won't be correctly processed,
because some Windows in the hierarchy will return a negative
parent index. And generally the accerciser has various problems,
with most result in Python backtraces an inconsistent app state,
but even SIGSEGV happened a few time. Already have some patches
for the easy reproducible ones.

No idea what will happen on any other setup then Linux with ATK.

Change-Id: I3280fd8622966be74e3833621952d95a2671d214
Reviewed-on: https://gerrit.libreoffice.org/73077
Tested-by: Jenkins
Reviewed-by: 's avatarJan-Marek Glogowski <glogow@fbihome.de>
üst 3eba9602
......@@ -62,6 +62,8 @@ $(eval $(call gb_Library_use_libraries,sm,\
))
$(eval $(call gb_Library_add_exception_objects,sm,\
starmath/source/AccessibleSmElement \
starmath/source/AccessibleSmElementsControl \
starmath/source/ElementsDockingWindow \
starmath/source/accessibility \
starmath/source/action \
......
/* -*- 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 .
*/
#ifndef INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX
#define INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/XAccessibleAction.hpp>
#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
#include <com/sun/star/accessibility/XAccessibleContext.hpp>
#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <cppuhelper/implbase3.hxx>
#include <comphelper/accessiblecomponenthelper.hxx>
#include <sal/types.h>
#include <vcl/vclptr.hxx>
class SmElementsControl;
typedef ::cppu::ImplHelper3<css::lang::XServiceInfo, css::accessibility::XAccessible,
css::accessibility::XAccessibleAction>
AccessibleSmElement_BASE;
class AccessibleSmElement final : public comphelper::OAccessibleComponentHelper,
public AccessibleSmElement_BASE
{
VclPtr<SmElementsControl> m_pSmElementsControl;
const sal_Int32 m_nIndexInParent; ///< index in the parent XAccessible
const sal_uInt16 m_nItemId; ///< index in the SmElementsControl
bool m_bHasFocus;
sal_Int16 m_nRole;
~AccessibleSmElement() override;
void SAL_CALL disposing() override;
css::awt::Rectangle implGetBounds() override;
void testAction(sal_Int32) const;
public:
explicit AccessibleSmElement(SmElementsControl* pSmElementsControl, sal_uInt16 nItemId,
sal_Int32 nIndexInParent);
void SetFocus(bool _bFocus);
bool HasFocus() const { return m_bHasFocus; }
void ReleaseSmElementsControl() { m_pSmElementsControl = nullptr; }
sal_uInt16 itemId() const { return m_nItemId; }
DECLARE_XINTERFACE()
DECLARE_XTYPEPROVIDER()
// XAccessible
css::uno::Reference<css::accessibility::XAccessibleContext>
SAL_CALL getAccessibleContext() override;
// XServiceInfo
OUString SAL_CALL getImplementationName() override;
sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
// XAccessibleContext
sal_Int32 SAL_CALL getAccessibleChildCount() override;
css::uno::Reference<css::accessibility::XAccessible>
SAL_CALL getAccessibleChild(sal_Int32 i) override;
css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override;
sal_Int32 SAL_CALL getAccessibleIndexInParent() override;
sal_Int16 SAL_CALL getAccessibleRole() override;
OUString SAL_CALL getAccessibleDescription() override;
OUString SAL_CALL getAccessibleName() override;
css::uno::Reference<css::accessibility::XAccessibleRelationSet>
SAL_CALL getAccessibleRelationSet() override;
css::uno::Reference<css::accessibility::XAccessibleStateSet>
SAL_CALL getAccessibleStateSet() override;
// XAccessibleComponent
css::uno::Reference<css::accessibility::XAccessible>
SAL_CALL getAccessibleAtPoint(const css::awt::Point& aPoint) override;
void SAL_CALL grabFocus() override;
sal_Int32 SAL_CALL getForeground() override;
sal_Int32 SAL_CALL getBackground() override;
// XAccessibleAction
sal_Int32 SAL_CALL getAccessibleActionCount() override;
sal_Bool SAL_CALL doAccessibleAction(sal_Int32 nIndex) override;
OUString SAL_CALL getAccessibleActionDescription(sal_Int32 nIndex) override;
css::uno::Reference<css::accessibility::XAccessibleKeyBinding>
SAL_CALL getAccessibleActionKeyBinding(sal_Int32 nIndex) override;
};
#endif // INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENT_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- 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 .
*/
#ifndef INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX
#define INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX
#include <comphelper/accessiblecomponenthelper.hxx>
#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <cppuhelper/implbase3.hxx>
#include <vcl/vclptr.hxx>
#include <vector>
class AccessibleSmElement;
class SmElementsControl;
typedef ::cppu::ImplHelper3<css::lang::XServiceInfo, css::accessibility::XAccessible,
css::accessibility::XAccessibleSelection>
AccessibleSmElementsControl_BASE;
class AccessibleSmElementsControl final : public comphelper::OAccessibleComponentHelper,
public AccessibleSmElementsControl_BASE
{
std::vector<rtl::Reference<AccessibleSmElement>> m_aAccessibleChildren;
VclPtr<SmElementsControl> m_pControl;
AccessibleSmElement* GetItem_Impl(sal_uInt16 _nPos);
void ReleaseFocus_Impl(sal_uInt16 _nPos);
sal_Int32 implGetAccessibleChildCount();
void ReleaseItems(bool bNotifyRemoval);
void UpdateFocus(sal_uInt16);
inline void TestControl();
~AccessibleSmElementsControl() override;
void SAL_CALL disposing() override;
css::awt::Rectangle implGetBounds() override;
public:
AccessibleSmElementsControl(SmElementsControl& rControl);
void ReleaseAllItems(bool bNotify);
void AddAllItems();
inline void AcquireFocus();
inline void ReleaseFocus(sal_uInt16);
DECLARE_XINTERFACE()
DECLARE_XTYPEPROVIDER()
// XAccessible
css::uno::Reference<css::accessibility::XAccessibleContext>
SAL_CALL getAccessibleContext() override;
// XServiceInfo
OUString SAL_CALL getImplementationName() override;
sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
// XAccessibleComponent
sal_Bool SAL_CALL containsPoint(const css::awt::Point& aPoint) override;
css::uno::Reference<css::accessibility::XAccessible>
SAL_CALL getAccessibleAtPoint(const css::awt::Point& aPoint) override;
css::awt::Rectangle SAL_CALL getBounds() override;
css::awt::Point SAL_CALL getLocation() override;
css::awt::Point SAL_CALL getLocationOnScreen() override;
css::awt::Size SAL_CALL getSize() override;
void SAL_CALL grabFocus() override;
sal_Int32 SAL_CALL getForeground() override;
sal_Int32 SAL_CALL getBackground() override;
// XAccessibleContext
sal_Int32 SAL_CALL getAccessibleChildCount() override;
css::uno::Reference<css::accessibility::XAccessible>
SAL_CALL getAccessibleChild(sal_Int32 i) override;
css::uno::Reference<css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override;
sal_Int16 SAL_CALL getAccessibleRole() override;
OUString SAL_CALL getAccessibleDescription() override;
OUString SAL_CALL getAccessibleName() override;
css::uno::Reference<css::accessibility::XAccessibleRelationSet>
SAL_CALL getAccessibleRelationSet() override;
css::uno::Reference<css::accessibility::XAccessibleStateSet>
SAL_CALL getAccessibleStateSet() override;
// XAccessibleSelection
void SAL_CALL selectAccessibleChild(sal_Int32 nChildIndex) override;
sal_Bool SAL_CALL isAccessibleChildSelected(sal_Int32 nChildIndex) override;
void SAL_CALL clearAccessibleSelection() override;
void SAL_CALL selectAllAccessibleChildren() override;
sal_Int32 SAL_CALL getSelectedAccessibleChildCount() override;
css::uno::Reference<css::accessibility::XAccessible>
SAL_CALL getSelectedAccessibleChild(sal_Int32 nSelectedChildIndex) override;
void SAL_CALL deselectAccessibleChild(sal_Int32 nChildIndex) override;
};
void AccessibleSmElementsControl::AcquireFocus() { UpdateFocus(SAL_MAX_UINT16); }
void AccessibleSmElementsControl::ReleaseFocus(sal_uInt16 nPos) { UpdateFocus(nPos); }
#endif // INCLUDED_STARMATH_INC_ACCESSIBLESMELEMENTSCONTROL_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -27,6 +27,8 @@
#include <memory>
#include <tuple>
#include "AccessibleSmElementsControl.hxx"
class SmDocShell;
class SmNode;
......@@ -88,12 +90,14 @@ class SmElementsControl : public Control
virtual void GetFocus() override;
virtual void LoseFocus() override;
virtual void KeyInput(const KeyEvent& rKEvt) override;
css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
SmDocShell* mpDocShell;
SmFormat maFormat;
OString msCurrentSetId;
sal_uInt16 m_nCurrentElement;
sal_uInt16 m_nCurrentRolloverElement;
sal_uInt16 m_nCurrentOffset;
Link<SmElement&,void> maSelectHdlLink;
std::vector< std::unique_ptr<SmElement> > maElementList;
......@@ -101,10 +105,12 @@ class SmElementsControl : public Control
bool mbVerticalMode;
VclPtr< ScrollBar > mxScroll;
bool m_bFirstPaintAfterLayout;
rtl::Reference<AccessibleSmElementsControl> m_xAccessible;
void addElement(const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText);
void addElements(const SmElementDescr aElementsArray[], sal_uInt16 size);
SmElement* current() const;
void setCurrentElement(sal_uInt16);
bool hasRollover() const { return m_nCurrentRolloverElement != SAL_MAX_UINT16; }
void stepFocus(const bool bBackward);
......@@ -126,10 +132,24 @@ public:
static const auto& categories() { return m_aCategories; }
static size_t categoriesSize() { return m_aCategoriesSize; }
OString elementSetId() const { return msCurrentSetId; }
void setElementSetId(const char* pSetId);
void setVerticalMode(bool bVertical);
sal_uInt16 itemCount() const;
sal_uInt16 itemHighlighted() const;
sal_uInt16 itemFocused() const;
sal_uInt16 itemAtPos(const Point& rPos) const;
tools::Rectangle itemPosRect(sal_uInt16) const;
bool itemIsSeparator(sal_uInt16) const;
bool itemIsVisible(sal_uInt16) const;
OUString itemName(sal_uInt16) const;
bool itemTrigger(sal_uInt16);
void setItemHighlighted(sal_uInt16);
sal_uInt16 itemOffset() const { return m_nCurrentOffset; }
css::uno::Reference<css::accessibility::XAccessible> scrollbarAccessible() const;
Size GetOptimalSize() const override;
DECL_LINK( ScrollHdl, ScrollBar*, void );
......
/* -*- 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 <AccessibleSmElement.hxx>
#include <ElementsDockingWindow.hxx>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <cppuhelper/supportsservice.hxx>
#include <cppuhelper/typeprovider.hxx>
#include <toolkit/helper/convert.hxx>
#include <unotools/accessiblerelationsethelper.hxx>
#include <unotools/accessiblestatesethelper.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
using namespace ::com::sun::star::accessibility;
using namespace ::com::sun::star;
using OContextEntryGuard = ::comphelper::OContextEntryGuard;
using OExternalLockGuard = ::comphelper::OExternalLockGuard;
AccessibleSmElement::AccessibleSmElement(SmElementsControl* pSmElementsControl, sal_uInt16 nItemId,
sal_Int32 nIndexInParent)
: m_pSmElementsControl(pSmElementsControl)
, m_nIndexInParent(nIndexInParent)
, m_nItemId(nItemId)
, m_bHasFocus(false)
{
assert(m_pSmElementsControl);
m_nRole = m_pSmElementsControl->itemIsSeparator(m_nItemId) ? AccessibleRole::SEPARATOR
: AccessibleRole::PUSH_BUTTON;
}
AccessibleSmElement::~AccessibleSmElement() {}
void AccessibleSmElement::SetFocus(bool bFocus)
{
if (m_bHasFocus == bFocus)
return;
uno::Any aOldValue;
uno::Any aNewValue;
if (m_bHasFocus)
aOldValue <<= AccessibleStateType::FOCUSED;
else
aNewValue <<= AccessibleStateType::FOCUSED;
m_bHasFocus = bFocus;
NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue);
}
awt::Rectangle AccessibleSmElement::implGetBounds()
{
awt::Rectangle aRect;
if (m_pSmElementsControl)
aRect = AWTRectangle(m_pSmElementsControl->itemPosRect(m_nItemId));
return aRect;
}
// XInterface
IMPLEMENT_FORWARD_REFCOUNT(AccessibleSmElement, comphelper::OAccessibleComponentHelper)
uno::Any AccessibleSmElement::queryInterface(const uno::Type& _rType)
{
if (_rType == cppu::UnoType<XAccessibleAction>::get()
&& (!m_pSmElementsControl || m_pSmElementsControl->itemIsSeparator(m_nItemId)))
return uno::Any();
uno::Any aReturn = comphelper::OAccessibleComponentHelper::queryInterface(_rType);
if (!aReturn.hasValue())
aReturn = AccessibleSmElement_BASE::queryInterface(_rType);
return aReturn;
}
// XTypeProvider
IMPLEMENT_FORWARD_XTYPEPROVIDER2(AccessibleSmElement, comphelper::OAccessibleComponentHelper,
AccessibleSmElement_BASE)
// XComponent
void AccessibleSmElement::disposing()
{
comphelper::OAccessibleComponentHelper::disposing();
m_pSmElementsControl = nullptr;
}
// XServiceInfo
OUString AccessibleSmElement::getImplementationName()
{
return OUString("com.sun.star.comp.toolkit.AccessibleSmElement");
}
sal_Bool AccessibleSmElement::supportsService(const OUString& rServiceName)
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence<OUString> AccessibleSmElement::getSupportedServiceNames()
{
return { "com.sun.star.accessibility.AccessibleContext",
"com.sun.star.accessibility.AccessibleComponent",
"com.sun.star.accessibility.AccessibleSmElement" };
}
// XAccessible
uno::Reference<XAccessibleContext> AccessibleSmElement::getAccessibleContext() { return this; }
// XAccessibleContext
sal_Int32 AccessibleSmElement::getAccessibleChildCount() { return 0; }
uno::Reference<accessibility::XAccessible> AccessibleSmElement::getAccessibleChild(sal_Int32)
{
return uno::Reference<XAccessible>();
}
uno::Reference<XAccessible> AccessibleSmElement::getAccessibleParent()
{
OContextEntryGuard aGuard(this);
uno::Reference<XAccessible> xParent;
if (m_pSmElementsControl)
xParent = m_pSmElementsControl->GetAccessible();
return xParent;
}
sal_Int32 AccessibleSmElement::getAccessibleIndexInParent()
{
OContextEntryGuard aGuard(this);
return m_nIndexInParent;
}
sal_Int16 AccessibleSmElement::getAccessibleRole()
{
OContextEntryGuard aGuard(this);
return m_nRole;
}
OUString AccessibleSmElement::getAccessibleDescription() { return getAccessibleName(); }
OUString AccessibleSmElement::getAccessibleName()
{
OExternalLockGuard aGuard(this);
OUString aName;
if (m_pSmElementsControl)
aName = m_pSmElementsControl->itemName(m_nItemId);
return aName;
}
uno::Reference<XAccessibleRelationSet> AccessibleSmElement::getAccessibleRelationSet()
{
OContextEntryGuard aGuard(this);
utl::AccessibleRelationSetHelper* pRelationSetHelper = new utl::AccessibleRelationSetHelper;
uno::Reference<XAccessibleRelationSet> xSet = pRelationSetHelper;
return xSet;
}
uno::Reference<XAccessibleStateSet> AccessibleSmElement::getAccessibleStateSet()
{
OExternalLockGuard aGuard(this);
utl::AccessibleStateSetHelper* pStateSetHelper = new utl::AccessibleStateSetHelper;
uno::Reference<XAccessibleStateSet> xStateSet = pStateSetHelper;
if (m_pSmElementsControl && !rBHelper.bDisposed && !rBHelper.bInDispose)
{
if (m_pSmElementsControl->itemIsVisible(m_nItemId))
pStateSetHelper->AddState(AccessibleStateType::VISIBLE);
if (!m_pSmElementsControl->itemIsSeparator(m_nItemId))
{
if (m_pSmElementsControl->IsEnabled())
{
pStateSetHelper->AddState(AccessibleStateType::ENABLED);
pStateSetHelper->AddState(AccessibleStateType::SENSITIVE);
}
pStateSetHelper->AddState(AccessibleStateType::FOCUSABLE);
if (m_bHasFocus)
pStateSetHelper->AddState(AccessibleStateType::FOCUSED);
}
}
else
pStateSetHelper->AddState(AccessibleStateType::DEFUNC);
return xStateSet;
}
// XAccessibleComponent
uno::Reference<XAccessible> AccessibleSmElement::getAccessibleAtPoint(const awt::Point&)
{
return uno::Reference<XAccessible>();
}
void AccessibleSmElement::grabFocus()
{
uno::Reference<XAccessible> xParent(getAccessibleParent());
if (xParent.is())
{
uno::Reference<XAccessibleSelection> rxAccessibleSelection(xParent->getAccessibleContext(),
uno::UNO_QUERY);
if (rxAccessibleSelection.is())
rxAccessibleSelection->selectAccessibleChild(getAccessibleIndexInParent());
}
}
sal_Int32 AccessibleSmElement::getForeground()
{
OExternalLockGuard aGuard(this);
Color nColor;
if (m_pSmElementsControl)
nColor = m_pSmElementsControl->GetControlForeground();
return sal_Int32(nColor);
}
sal_Int32 AccessibleSmElement::getBackground()
{
OExternalLockGuard aGuard(this);
Color nColor;
if (m_pSmElementsControl)
nColor = m_pSmElementsControl->GetControlBackground();
return sal_Int32(nColor);
}
// XAccessibleAction
sal_Int32 AccessibleSmElement::getAccessibleActionCount()
{
// only one action -> "Press"
return m_pSmElementsControl->itemIsSeparator(m_nItemId) ? 0 : 1;
}
void AccessibleSmElement::testAction(sal_Int32 nIndex) const
{
if (!m_pSmElementsControl || m_pSmElementsControl->itemIsSeparator(m_nItemId) || (nIndex != 0))
throw lang::IndexOutOfBoundsException();
}
sal_Bool AccessibleSmElement::doAccessibleAction(sal_Int32 nIndex)
{
OExternalLockGuard aGuard(this);
testAction(nIndex);
return m_pSmElementsControl->itemTrigger(m_nItemId);
}
OUString AccessibleSmElement::getAccessibleActionDescription(sal_Int32 nIndex)
{
OExternalLockGuard aGuard(this);
testAction(nIndex);
return OUString("press");
}
uno::Reference<XAccessibleKeyBinding>
AccessibleSmElement::getAccessibleActionKeyBinding(sal_Int32 nIndex)
{
OContextEntryGuard aGuard(this);
testAction(nIndex);
return uno::Reference<XAccessibleKeyBinding>();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
This diff is collapsed.
......@@ -261,8 +261,9 @@ const size_t SmElementsControl::m_aCategoriesSize = SAL_N_ELEMENTS(m_aCategories
SmElementsControl::SmElementsControl(vcl::Window *pParent)
: Control(pParent, WB_TABSTOP)
, mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
, m_nCurrentElement(0)
, m_nCurrentElement(SAL_MAX_UINT16)
, m_nCurrentRolloverElement(SAL_MAX_UINT16)
, m_nCurrentOffset(1) // Default offset of 1 due to the ScrollBar child
, mbVerticalMode(true)
, mxScroll(VclPtr<ScrollBar>::Create(this, WB_VERT))
, m_bFirstPaintAfterLayout(false)
......@@ -311,6 +312,19 @@ SmElement* SmElementsControl::current() const
return (nCur < maElementList.size()) ? maElementList[nCur].get() : nullptr;
}
void SmElementsControl::setCurrentElement(sal_uInt16 nPos)
{
if (m_nCurrentElement == nPos)
return;
if (nPos != SAL_MAX_UINT16 && nPos >= maElementList.size())
return;
if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
m_xAccessible->ReleaseFocus(m_nCurrentElement);
m_nCurrentElement = nPos;
if (m_xAccessible.is() && m_nCurrentElement != SAL_MAX_UINT16)
m_xAccessible->AcquireFocus();
}
/**
* !pContext => layout only
*
......@@ -357,6 +371,9 @@ void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext *pContext)
x += boxX;
y = 0;
element->mBoxLocation = Point(x, y);
element->mBoxSize = Size(10, nControlHeight);
tools::Rectangle aSelectionRectangle(x + 5 - 1, y + 5,
x + 5 + 1, nControlHeight - 5);
......@@ -369,6 +386,9 @@ void SmElementsControl::LayoutOrPaintContents(vcl::RenderContext *pContext)
x = 0;
y += boxY;
element->mBoxLocation = Point(x, y);
element->mBoxSize = Size(nControlWidth, 10);
tools::Rectangle aSelectionRectangle(x + 5, y + 5 - 1,
nControlWidth - 5, y + 5 + 1);
......@@ -603,7 +623,7 @@ void SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
tools::Rectangle rect(pPrevElement->mBoxLocation, pPrevElement->mBoxSize);
if (rect.IsInside(rMouseEvent.GetPosPixel()))
{
m_nCurrentElement = m_nCurrentRolloverElement;
setCurrentElement(m_nCurrentRolloverElement);
maSelectHdlLink.Call(*const_cast<SmElement*>(pPrevElement));
collectUIInformation(OUString::number(m_nCurrentRolloverElement));
return;
......@@ -617,7 +637,7 @@ void SmElementsControl::MouseButtonDown(const MouseEvent& rMouseEvent)
tools::Rectangle rect(element->mBoxLocation, element->mBoxSize);
if (rect.IsInside(rMouseEvent.GetPosPixel()))
{
m_nCurrentElement = n;
setCurrentElement(n);
maSelectHdlLink.Call(*element);
collectUIInformation(OUString::number(n));
return;
......@@ -697,8 +717,8 @@ void SmElementsControl::stepFocus(const bool bBackward)
sal_uInt16 nPos = nextElement(bBackward, nStartPos, nLastElement);
if (nStartPos != nPos)
{
m_nCurrentElement = nPos;
m_nCurrentRolloverElement = SAL_MAX_UINT16;
setCurrentElement(nPos);
const tools::Rectangle outputRect(Point(0,0), GetOutputSizePixel());
const SmElement *pCur = maElementList[nPos].get();
......@@ -752,7 +772,7 @@ void SmElementsControl::pageFocus(const bool bBackward)
if (nStartPos != nPos)
{
m_nCurrentElement = nPos;
setCurrentElement(nPos);
if (bMoved)
scrollToElement(bBackward, maElementList[nPos].get());
Invalidate();
......@@ -793,12 +813,18 @@ void SmElementsControl::KeyInput(const KeyEvent& rKEvt)
break;
case KEY_HOME:
m_nCurrentElement = 0;
mxScroll->DoScroll(0);
if (!maElementList.empty())
{
setCurrentElement(0);
mxScroll->DoScroll(0);
}
break;
case KEY_END:
m_nCurrentElement = (maElementList.size() ? maElementList.size() - 1 : 0);
mxScroll->DoScroll(mxScroll->GetRangeMax());
if (!maElementList.empty())
{
setCurrentElement(maElementList.size() - 1);
mxScroll->DoScroll(mxScroll->GetRangeMax());
}
break;
case KEY_PAGEUP:
......@@ -964,8 +990,21 @@ void SmElementsControl::addElements(const SmElementDescr aElementsArray[], sal_u
void SmElementsControl::build()
{
// The order is important!
// 1. Ensure there are no items left, including the default scrollbar!
// 2. Release all the current accessible items.
// This will check for new items after releasing them!
// 3. Set the cursor element
maElementList.clear();
mxScroll->SetThumbPos(0);
mxScroll->Hide();
if (m_xAccessible.is())
m_xAccessible->ReleaseAllItems(true);
setCurrentElement(SAL_MAX_UINT16);
// The first element is the scrollbar. We can't change its indexInParent