1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/svapp.hxx>
21 #include <vcl/window.hxx>
22 #include <vcl/toolbox.hxx>
23 #include <vcl/menu.hxx>
25 #include <osx/a11yfocustracker.hxx>
27 #include "documentfocuslistener.hxx"
29 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
30 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
31 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
35 using namespace ::com::sun::star::accessibility
;
36 using namespace ::com::sun::star::lang
;
37 using namespace ::com::sun::star::uno
;
39 AquaA11yFocusTracker
& TheAquaA11yFocusTracker()
41 static AquaA11yFocusTracker SINGLETON
;
46 getWindow(const ::VclSimpleEvent
*pEvent
)
48 return static_cast< const ::VclWindowEvent
*> (pEvent
)->GetWindow();
51 // callback function for Application::addEventListener
53 void AquaA11yFocusTracker::WindowEventHandler(void * pThis
, VclSimpleEvent
& rEvent
)
55 AquaA11yFocusTracker
*pFocusTracker
= static_cast<AquaA11yFocusTracker
*>(
57 switch (rEvent
.GetId())
59 case VclEventId::WindowPaint
:
60 pFocusTracker
-> toolbox_open_floater( getWindow(&rEvent
) );
62 case VclEventId::WindowGetFocus
:
63 pFocusTracker
->window_got_focus( getWindow(&rEvent
) );
65 case VclEventId::ObjectDying
:
66 pFocusTracker
->m_aDocumentWindowList
.erase( getWindow(&rEvent
) );
68 case VclEventId::ToolboxHighlightOff
:
69 pFocusTracker
->toolbox_highlight_off( getWindow(&rEvent
) );
71 case VclEventId::ToolboxHighlight
:
72 pFocusTracker
->toolbox_highlight_on( getWindow(&rEvent
) );
74 case VclEventId::TabpageActivate
:
75 pFocusTracker
->tabpage_activated( getWindow(&rEvent
) );
77 case VclEventId::MenuHighlight
:
78 // Inspired by code in WindowEventHandler in
79 // vcl/unx/gtk/a11y/atkutil.cxx, find out what kind of event
80 // it is to avoid blindly using a static_cast and crash,
82 if( const VclMenuEvent
* pMenuEvent
= dynamic_cast < const VclMenuEvent
* > (&rEvent
) )
84 pFocusTracker
->menu_highlighted( pMenuEvent
);
92 AquaA11yFocusTracker::AquaA11yFocusTracker() :
93 m_aWindowEventLink(this, WindowEventHandler
),
94 m_xDocumentFocusListener(new DocumentFocusListener(*this))
96 Application::AddEventListener(m_aWindowEventLink
);
97 window_got_focus(Application::GetFocusWindow());
100 AquaA11yFocusTracker::~AquaA11yFocusTracker() {}
102 void AquaA11yFocusTracker::setFocusedObject(const Reference
< XAccessible
>& xAccessible
)
104 if( xAccessible
!= m_xFocusedObject
)
106 m_xFocusedObject
= xAccessible
;
108 if( m_aFocusListener
.is() )
109 m_aFocusListener
->focusedObjectChanged(xAccessible
);
113 void AquaA11yFocusTracker::notify_toolbox_item_focus(ToolBox
*pToolBox
)
115 Reference
< XAccessible
> xAccessible( pToolBox
->GetAccessible() );
117 if( xAccessible
.is() )
119 Reference
< XAccessibleContext
> xContext(xAccessible
->getAccessibleContext());
124 ToolBox::ImplToolItems::size_type nPos
= pToolBox
->GetItemPos( pToolBox
->GetHighlightItemId() );
125 if( nPos
!= ToolBox::ITEM_NOTFOUND
)
126 setFocusedObject( xContext
->getAccessibleChild( nPos
) );
127 //TODO: ToolBox::ImplToolItems::size_type -> sal_Int32!
129 catch (const IndexOutOfBoundsException
&)
131 SAL_WARN("vcl", "Accessible object has invalid index in parent");
137 void AquaA11yFocusTracker::toolbox_open_floater(vcl::Window
*pWindow
)
139 bool bToolboxFound
= false;
140 bool bFloatingWindowFound
= false;
141 vcl::Window
* pFloatingWindow
= nullptr;
142 while ( pWindow
!= nullptr ) {
143 if ( pWindow
->GetType() == WindowType::TOOLBOX
) {
144 bToolboxFound
= true;
145 } else if ( pWindow
->GetType() == WindowType::FLOATINGWINDOW
) {
146 bFloatingWindowFound
= true;
147 pFloatingWindow
= pWindow
;
149 pWindow
= pWindow
->GetParent();
151 if ( bToolboxFound
&& bFloatingWindowFound
) {
152 Reference
< XAccessible
> rxAccessible
= pFloatingWindow
-> GetAccessible();
153 if ( ! rxAccessible
.is() ) {
156 Reference
< XAccessibleContext
> rxContext
= rxAccessible
-> getAccessibleContext();
157 if ( ! rxContext
.is() ) {
160 if ( rxContext
-> getAccessibleChildCount() > 0 ) {
162 Reference
< XAccessible
> rxAccessibleChild
= rxContext
-> getAccessibleChild( 0 );
163 if ( ! rxAccessibleChild
.is() ) {
166 setFocusedObject ( rxAccessibleChild
);
168 catch (const IndexOutOfBoundsException
&)
170 SAL_WARN("vcl", "No valid accessible objects in parent");
176 void AquaA11yFocusTracker::toolbox_highlight_on(vcl::Window
*pWindow
)
178 // Make sure either the toolbox or its parent toolbox has the focus
179 if ( ! pWindow
->HasFocus() )
181 ToolBox
* pToolBoxParent
= dynamic_cast< ToolBox
* >( pWindow
->GetParent() );
182 if ( ! pToolBoxParent
|| ! pToolBoxParent
->HasFocus() )
186 notify_toolbox_item_focus(static_cast <ToolBox
*> (pWindow
));
189 void AquaA11yFocusTracker::toolbox_highlight_off(vcl::Window
const *pWindow
)
191 ToolBox
* pToolBoxParent
= dynamic_cast< ToolBox
* >( pWindow
->GetParent() );
193 // Notify when leaving sub toolboxes
194 if( pToolBoxParent
&& pToolBoxParent
->HasFocus() )
195 notify_toolbox_item_focus( pToolBoxParent
);
198 void AquaA11yFocusTracker::tabpage_activated(vcl::Window
*pWindow
)
200 Reference
< XAccessible
> xAccessible( pWindow
->GetAccessible() );
202 if( xAccessible
.is() )
204 Reference
< XAccessibleSelection
> xSelection(xAccessible
->getAccessibleContext(), UNO_QUERY
);
206 if( xSelection
.is() )
207 setFocusedObject( xSelection
->getSelectedAccessibleChild(0) );
211 void AquaA11yFocusTracker::menu_highlighted(const VclMenuEvent
*pEvent
)
213 Menu
* pMenu
= pEvent
->GetMenu();
217 Reference
< XAccessible
> xAccessible( pMenu
->GetAccessible() );
219 if( xAccessible
.is() )
220 setFocusedObject( xAccessible
);
224 void AquaA11yFocusTracker::window_got_focus(vcl::Window
*pWindow
)
226 // The menu bar is handled through VclEventId::MenuHighlightED
227 if( ! pWindow
|| !pWindow
->IsReallyVisible() || pWindow
->GetType() == WindowType::MENUBARWINDOW
)
230 // ToolBoxes are handled through VclEventId::ToolboxHighlight
231 if( pWindow
->GetType() == WindowType::TOOLBOX
)
234 if( pWindow
->GetType() == WindowType::TABCONTROL
)
236 tabpage_activated( pWindow
);
240 Reference
< XAccessible
> xAccessible(pWindow
->GetAccessible());
242 if( ! xAccessible
.is() )
245 Reference
< XAccessibleContext
> xContext
= xAccessible
->getAccessibleContext();
247 if( ! xContext
.is() )
250 sal_Int64 nStateSet
= xContext
->getAccessibleStateSet();
255 /* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we
256 * need to add listeners to the children instead of re-using the tabpage stuff
258 if( (nStateSet
& AccessibleStateType::FOCUSED
) && (pWindow
->GetType() != WindowType::TREELISTBOX
) )
260 setFocusedObject( xAccessible
);
264 if( m_aDocumentWindowList
.insert(pWindow
).second
)
265 m_xDocumentFocusListener
->attachRecursive(xAccessible
, xContext
, nStateSet
);
269 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */