summaryrefslogtreecommitdiffstats
path: root/vcl/osx/a11yfocustracker.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/osx/a11yfocustracker.cxx')
-rw-r--r--vcl/osx/a11yfocustracker.cxx269
1 files changed, 269 insertions, 0 deletions
diff --git a/vcl/osx/a11yfocustracker.cxx b/vcl/osx/a11yfocustracker.cxx
new file mode 100644
index 0000000000..2aaa8b0a8e
--- /dev/null
+++ b/vcl/osx/a11yfocustracker.cxx
@@ -0,0 +1,269 @@
+/* -*- 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 <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/menu.hxx>
+
+#include <osx/a11yfocustracker.hxx>
+
+#include "documentfocuslistener.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+AquaA11yFocusTracker& TheAquaA11yFocusTracker()
+{
+ static AquaA11yFocusTracker SINGLETON;
+ return SINGLETON;
+}
+
+static vcl::Window *
+getWindow(const ::VclSimpleEvent *pEvent)
+{
+ return static_cast< const ::VclWindowEvent *> (pEvent)->GetWindow();
+}
+
+// callback function for Application::addEventListener
+
+void AquaA11yFocusTracker::WindowEventHandler(void * pThis, VclSimpleEvent& rEvent)
+{
+ AquaA11yFocusTracker *pFocusTracker = static_cast<AquaA11yFocusTracker *>(
+ pThis);
+ switch (rEvent.GetId())
+ {
+ case VclEventId::WindowPaint:
+ pFocusTracker-> toolbox_open_floater( getWindow(&rEvent) );
+ break;
+ case VclEventId::WindowGetFocus:
+ pFocusTracker->window_got_focus( getWindow(&rEvent) );
+ break;
+ case VclEventId::ObjectDying:
+ pFocusTracker->m_aDocumentWindowList.erase( getWindow(&rEvent) );
+ [[fallthrough]];
+ case VclEventId::ToolboxHighlightOff:
+ pFocusTracker->toolbox_highlight_off( getWindow(&rEvent) );
+ break;
+ case VclEventId::ToolboxHighlight:
+ pFocusTracker->toolbox_highlight_on( getWindow(&rEvent) );
+ break;
+ case VclEventId::TabpageActivate:
+ pFocusTracker->tabpage_activated( getWindow(&rEvent) );
+ break;
+ case VclEventId::MenuHighlight:
+ // Inspired by code in WindowEventHandler in
+ // vcl/unx/gtk/a11y/atkutil.cxx, find out what kind of event
+ // it is to avoid blindly using a static_cast and crash,
+ // fdo#47275.
+ if( const VclMenuEvent* pMenuEvent = dynamic_cast < const VclMenuEvent* > (&rEvent) )
+ {
+ pFocusTracker->menu_highlighted( pMenuEvent );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+AquaA11yFocusTracker::AquaA11yFocusTracker() :
+ m_aWindowEventLink(this, WindowEventHandler),
+ m_xDocumentFocusListener(new DocumentFocusListener(*this))
+{
+ Application::AddEventListener(m_aWindowEventLink);
+ window_got_focus(Application::GetFocusWindow());
+}
+
+AquaA11yFocusTracker::~AquaA11yFocusTracker() {}
+
+void AquaA11yFocusTracker::setFocusedObject(const Reference< XAccessible >& xAccessible)
+{
+ if( xAccessible != m_xFocusedObject )
+ {
+ m_xFocusedObject = xAccessible;
+
+ if( m_aFocusListener.is() )
+ m_aFocusListener->focusedObjectChanged(xAccessible);
+ }
+}
+
+void AquaA11yFocusTracker::notify_toolbox_item_focus(ToolBox *pToolBox)
+{
+ Reference< XAccessible > xAccessible( pToolBox->GetAccessible() );
+
+ if( xAccessible.is() )
+ {
+ Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext());
+
+ if( xContext.is() )
+ {
+ try {
+ ToolBox::ImplToolItems::size_type nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() );
+ if( nPos != ToolBox::ITEM_NOTFOUND )
+ setFocusedObject( xContext->getAccessibleChild( nPos ) );
+ //TODO: ToolBox::ImplToolItems::size_type -> sal_Int32!
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ SAL_WARN("vcl", "Accessible object has invalid index in parent");
+ }
+ }
+ }
+}
+
+void AquaA11yFocusTracker::toolbox_open_floater(vcl::Window *pWindow)
+{
+ bool bToolboxFound = false;
+ bool bFloatingWindowFound = false;
+ vcl::Window * pFloatingWindow = nullptr;
+ while ( pWindow != nullptr ) {
+ if ( pWindow->GetType() == WindowType::TOOLBOX ) {
+ bToolboxFound = true;
+ } else if ( pWindow->GetType() == WindowType::FLOATINGWINDOW ) {
+ bFloatingWindowFound = true;
+ pFloatingWindow = pWindow;
+ }
+ pWindow = pWindow->GetParent();
+ }
+ if ( bToolboxFound && bFloatingWindowFound ) {
+ Reference < XAccessible > rxAccessible = pFloatingWindow -> GetAccessible();
+ if ( ! rxAccessible.is() ) {
+ return;
+ }
+ Reference < XAccessibleContext > rxContext = rxAccessible -> getAccessibleContext();
+ if ( ! rxContext.is() ) {
+ return;
+ }
+ if ( rxContext -> getAccessibleChildCount() > 0 ) {
+ try {
+ Reference < XAccessible > rxAccessibleChild = rxContext -> getAccessibleChild( 0 );
+ if ( ! rxAccessibleChild.is() ) {
+ return;
+ }
+ setFocusedObject ( rxAccessibleChild );
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ SAL_WARN("vcl", "No valid accessible objects in parent");
+ }
+ }
+ }
+}
+
+void AquaA11yFocusTracker::toolbox_highlight_on(vcl::Window *pWindow)
+{
+ // Make sure either the toolbox or its parent toolbox has the focus
+ if ( ! pWindow->HasFocus() )
+ {
+ ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
+ if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() )
+ return;
+ }
+
+ notify_toolbox_item_focus(static_cast <ToolBox *> (pWindow));
+}
+
+void AquaA11yFocusTracker::toolbox_highlight_off(vcl::Window const *pWindow)
+{
+ ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
+
+ // Notify when leaving sub toolboxes
+ if( pToolBoxParent && pToolBoxParent->HasFocus() )
+ notify_toolbox_item_focus( pToolBoxParent );
+}
+
+void AquaA11yFocusTracker::tabpage_activated(vcl::Window *pWindow)
+{
+ Reference< XAccessible > xAccessible( pWindow->GetAccessible() );
+
+ if( xAccessible.is() )
+ {
+ Reference< XAccessibleSelection > xSelection(xAccessible->getAccessibleContext(), UNO_QUERY);
+
+ if( xSelection.is() )
+ setFocusedObject( xSelection->getSelectedAccessibleChild(0) );
+ }
+}
+
+void AquaA11yFocusTracker::menu_highlighted(const VclMenuEvent *pEvent)
+{
+ Menu * pMenu = pEvent->GetMenu();
+
+ if( pMenu )
+ {
+ Reference< XAccessible > xAccessible( pMenu->GetAccessible() );
+
+ if( xAccessible.is() )
+ setFocusedObject( xAccessible );
+ }
+}
+
+void AquaA11yFocusTracker::window_got_focus(vcl::Window *pWindow)
+{
+ // The menu bar is handled through VclEventId::MenuHighlightED
+ if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WindowType::MENUBARWINDOW )
+ return;
+
+ // ToolBoxes are handled through VclEventId::ToolboxHighlight
+ if( pWindow->GetType() == WindowType::TOOLBOX )
+ return;
+
+ if( pWindow->GetType() == WindowType::TABCONTROL )
+ {
+ tabpage_activated( pWindow );
+ return;
+ }
+
+ Reference< XAccessible > xAccessible(pWindow->GetAccessible());
+
+ if( ! xAccessible.is() )
+ return;
+
+ Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext();
+
+ if( ! xContext.is() )
+ return;
+
+ sal_Int64 nStateSet = xContext->getAccessibleStateSet();
+
+ if( ! nStateSet )
+ return;
+
+/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we
+ * need to add listeners to the children instead of re-using the tabpage stuff
+ */
+ if( (nStateSet & AccessibleStateType::FOCUSED) && (pWindow->GetType() != WindowType::TREELISTBOX) )
+ {
+ setFocusedObject( xAccessible );
+ }
+ else
+ {
+ if( m_aDocumentWindowList.insert(pWindow).second )
+ m_xDocumentFocusListener->attachRecursive(xAccessible, xContext, nStateSet);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */