1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "vcl/svapp.hxx"
32 #include "vcl/window.hxx"
33 #include "vcl/toolbox.hxx"
34 #include "vcl/menu.hxx"
35 
36 #include "aqua/aqua11yfocustracker.hxx"
37 
38 #include "documentfocuslistener.hxx"
39 
40 #include <com/sun/star/accessibility/XAccessibleContext.hpp>
41 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
42 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
43 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
44 #include <com/sun/star/accessibility/AccessibleRole.hpp>
45 
46 using namespace ::com::sun::star::accessibility;
47 using namespace ::com::sun::star::uno;
48 
49 //------------------------------------------------------------------------------
50 
51 static inline Window *
52 getWindow(const ::VclSimpleEvent *pEvent)
53 {
54 	return static_cast< const ::VclWindowEvent *> (pEvent)->GetWindow();
55 }
56 
57 
58 //------------------------------------------------------------------------------
59 
60 // callback function for Application::addEventListener
61 
62 long AquaA11yFocusTracker::WindowEventHandler(AquaA11yFocusTracker *pFocusTracker, ::VclSimpleEvent const *pEvent)
63 {
64     switch (pEvent->GetId())
65     {
66     case VCLEVENT_WINDOW_PAINT:
67         pFocusTracker-> toolbox_open_floater( getWindow(pEvent) );
68         break;
69     case VCLEVENT_WINDOW_GETFOCUS:
70         pFocusTracker->window_got_focus( getWindow(pEvent) );
71         break;
72     case VCLEVENT_OBJECT_DYING:
73         pFocusTracker->m_aDocumentWindowList.erase( getWindow(pEvent) );
74         // intentional pass through ..
75     case VCLEVENT_TOOLBOX_HIGHLIGHTOFF:
76         pFocusTracker->toolbox_highlight_off( getWindow(pEvent) );
77         break;
78 	case VCLEVENT_TOOLBOX_HIGHLIGHT:
79         pFocusTracker->toolbox_highlight_on( getWindow(pEvent) );
80         break;
81     case VCLEVENT_TABPAGE_ACTIVATE:
82         pFocusTracker->tabpage_activated( getWindow(pEvent) );
83         break;
84     case VCLEVENT_MENU_HIGHLIGHT:
85         pFocusTracker->menu_highlighted( static_cast < const VclMenuEvent * > (pEvent) );
86         break;
87     default:
88 	    break;
89 	};
90 
91 	return 0;
92 }
93 
94 //------------------------------------------------------------------------------
95 
96 AquaA11yFocusTracker::AquaA11yFocusTracker() :
97     m_aWindowEventLink(this, (PSTUB) WindowEventHandler),
98     m_xDocumentFocusListener(new DocumentFocusListener(*this))
99 {
100     Application::AddEventListener(m_aWindowEventLink);
101     window_got_focus(Application::GetFocusWindow());
102 }
103 
104 //------------------------------------------------------------------------------
105 
106 void AquaA11yFocusTracker::setFocusedObject(const Reference< XAccessible >& xAccessible)
107 {
108     if( xAccessible != m_xFocusedObject )
109     {
110         m_xFocusedObject = xAccessible;
111 
112         if( m_aFocusListener.is() )
113             m_aFocusListener->focusedObjectChanged(xAccessible);
114     }
115 }
116 
117 //------------------------------------------------------------------------------
118 
119 void AquaA11yFocusTracker::notify_toolbox_item_focus(ToolBox *pToolBox)
120 {
121     Reference< XAccessible > xAccessible( pToolBox->GetAccessible() );
122 
123     if( xAccessible.is() )
124     {
125         Reference< XAccessibleContext > xContext(xAccessible->getAccessibleContext());
126 
127         if( xContext.is() )
128         {
129             sal_Int32 nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() );
130             if( nPos != TOOLBOX_ITEM_NOTFOUND )
131                 setFocusedObject( xContext->getAccessibleChild( nPos ) );
132         }
133     }
134 }
135 
136 //------------------------------------------------------------------------------
137 
138 void AquaA11yFocusTracker::toolbox_open_floater(Window *pWindow)
139 {
140     bool bToolboxFound = false;
141     bool bFloatingWindowFound = false;
142     Window * pFloatingWindow = NULL;
143     while ( pWindow != NULL ) {
144         if ( pWindow->GetType() == WINDOW_TOOLBOX ) {
145             bToolboxFound = true;
146         } else if ( pWindow->GetType() == WINDOW_FLOATINGWINDOW ) {
147             bFloatingWindowFound = true;
148             pFloatingWindow = pWindow;
149         }
150         pWindow = pWindow->GetParent();
151     }
152     if ( bToolboxFound && bFloatingWindowFound ) {
153         Reference < XAccessible > rxAccessible = pFloatingWindow -> GetAccessible();
154         if ( ! rxAccessible.is() ) {
155             return;
156         }
157         Reference < XAccessibleContext > rxContext = rxAccessible -> getAccessibleContext();
158         if ( ! rxContext.is() ) {
159             return;
160         }
161         if ( rxContext -> getAccessibleChildCount() > 0 ) {
162             Reference < XAccessible > rxAccessibleChild = rxContext -> getAccessibleChild( 0 );
163             if ( ! rxAccessibleChild.is() ) {
164                 return;
165             }
166             setFocusedObject ( rxAccessibleChild );
167         }
168     }
169 }
170 
171 //------------------------------------------------------------------------------
172 
173 void AquaA11yFocusTracker::toolbox_highlight_on(Window *pWindow)
174 {
175     // Make sure either the toolbox or its parent toolbox has the focus
176     if ( ! pWindow->HasFocus() )
177     {
178         ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
179         if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() )
180             return;
181     }
182 
183     notify_toolbox_item_focus(static_cast <ToolBox *> (pWindow));
184 }
185 
186 //------------------------------------------------------------------------------
187 
188 void AquaA11yFocusTracker::toolbox_highlight_off(Window *pWindow)
189 {
190     ToolBox* pToolBoxParent = dynamic_cast< ToolBox * >( pWindow->GetParent() );
191 
192     // Notify when leaving sub toolboxes
193     if( pToolBoxParent && pToolBoxParent->HasFocus() )
194         notify_toolbox_item_focus( pToolBoxParent );
195 }
196 
197 //------------------------------------------------------------------------------
198 
199 void AquaA11yFocusTracker::tabpage_activated(Window *pWindow)
200 {
201     Reference< XAccessible > xAccessible( pWindow->GetAccessible() );
202 
203     if( xAccessible.is() )
204     {
205         Reference< XAccessibleSelection > xSelection(xAccessible->getAccessibleContext(), UNO_QUERY);
206 
207         if( xSelection.is() )
208             setFocusedObject( xSelection->getSelectedAccessibleChild(0) );
209     }
210 }
211 
212 //------------------------------------------------------------------------------
213 
214 void AquaA11yFocusTracker::menu_highlighted(const VclMenuEvent *pEvent)
215 {
216     Menu * pMenu = pEvent->GetMenu();
217 
218     if( pMenu )
219     {
220         Reference< XAccessible > xAccessible( pMenu->GetAccessible() );
221 
222         if( xAccessible.is() )
223             setFocusedObject( xAccessible );
224     }
225 }
226 
227 //------------------------------------------------------------------------------
228 
229 void AquaA11yFocusTracker::window_got_focus(Window *pWindow)
230 {
231     // The menu bar is handled through VCLEVENT_MENU_HIGHLIGHTED
232     if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WINDOW_MENUBARWINDOW )
233         return;
234 
235     // ToolBoxes are handled through VCLEVENT_TOOLBOX_HIGHLIGHT
236     if( pWindow->GetType() == WINDOW_TOOLBOX )
237         return;
238 
239     if( pWindow->GetType() == WINDOW_TABCONTROL )
240     {
241         tabpage_activated( pWindow );
242         return;
243     }
244 
245     Reference< XAccessible > xAccessible(pWindow->GetAccessible());
246 
247     if( ! xAccessible.is() )
248         return;
249 
250     Reference< XAccessibleContext > xContext = xAccessible->getAccessibleContext();
251 
252     if( ! xContext.is() )
253         return;
254 
255     Reference< XAccessibleStateSet > xStateSet = xContext->getAccessibleStateSet();
256 
257     if( ! xStateSet.is() )
258         return;
259 
260 /* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we
261  * need to add listeners to the children instead of re-using the tabpage stuff
262  */
263     if( xStateSet->contains(AccessibleStateType::FOCUSED) && (pWindow->GetType() != WINDOW_TREELISTBOX) )
264     {
265         setFocusedObject( xAccessible );
266     }
267     else
268     {
269         if( m_aDocumentWindowList.find(pWindow) == m_aDocumentWindowList.end() )
270         {
271             m_aDocumentWindowList.insert(pWindow);
272             m_xDocumentFocusListener->attachRecursive(xAccessible, xContext, xStateSet);
273         }
274 #ifdef ENABLE_TRACING
275         else
276             fprintf(stderr, "Window %p already in the list\n", pWindow );
277 #endif
278     }
279 }
280