1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "atkwrapper.hxx"
21 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
22 #include <gtk/gtk.h>
23 
24 using namespace ::com::sun::star;
25 
getObjectWrapper(AtkComponent * pComponent)26 static AtkObjectWrapper* getObjectWrapper(AtkComponent *pComponent)
27 {
28     AtkObjectWrapper *pWrap = nullptr;
29     if (ATK_IS_OBJECT_WRAPPER(pComponent))
30         pWrap = ATK_OBJECT_WRAPPER(pComponent);
31     else if (GTK_IS_DRAWING_AREA(pComponent)) //when using a GtkDrawingArea as a custom widget in welded gtk3
32     {
33         GtkWidget* pDrawingArea = GTK_WIDGET(pComponent);
34         AtkObject* pAtkObject = gtk_widget_get_accessible(pDrawingArea);
35         pWrap = ATK_IS_OBJECT_WRAPPER(pAtkObject) ? ATK_OBJECT_WRAPPER(pAtkObject) : nullptr;
36     }
37     return pWrap;
38 }
39 
40 /// @throws uno::RuntimeException
41 static css::uno::Reference<css::accessibility::XAccessibleComponent>
getComponent(AtkObjectWrapper * pWrap)42     getComponent(AtkObjectWrapper *pWrap)
43 {
44     if (pWrap)
45     {
46         if (!pWrap->mpComponent.is())
47             pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY);
48         return pWrap->mpComponent;
49     }
50 
51     return css::uno::Reference<css::accessibility::XAccessibleComponent>();
52 }
53 
54 /*****************************************************************************/
55 
56 static awt::Point
translatePoint(css::uno::Reference<accessibility::XAccessibleComponent> const & pComponent,gint x,gint y,AtkCoordType t)57 translatePoint( css::uno::Reference<accessibility::XAccessibleComponent> const & pComponent,
58                 gint x, gint y, AtkCoordType t)
59 {
60     awt::Point aOrigin( 0, 0 );
61     if( t == ATK_XY_SCREEN )
62         aOrigin = pComponent->getLocationOnScreen();
63     return awt::Point( x - aOrigin.X, y - aOrigin.Y );
64 }
65 
66 /*****************************************************************************/
67 
68 extern "C" {
69 
70 static gboolean
component_wrapper_grab_focus(AtkComponent * component)71 component_wrapper_grab_focus (AtkComponent *component)
72 {
73     AtkObjectWrapper* obj = getObjectWrapper(component);
74     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
75     if (obj && obj->mpOrig)
76         return atk_component_grab_focus(ATK_COMPONENT(obj->mpOrig));
77 
78     try
79     {
80         css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent
81             = getComponent(obj);
82         if( pComponent.is() )
83         {
84             pComponent->grabFocus();
85             return TRUE;
86         }
87     }
88     catch( const uno::Exception & )
89     {
90         g_warning( "Exception in grabFocus()" );
91     }
92 
93     return FALSE;
94 }
95 
96 /*****************************************************************************/
97 
98 static gboolean
component_wrapper_contains(AtkComponent * component,gint x,gint y,AtkCoordType coord_type)99 component_wrapper_contains (AtkComponent *component,
100                             gint          x,
101                             gint          y,
102                             AtkCoordType  coord_type)
103 {
104     AtkObjectWrapper* obj = getObjectWrapper(component);
105     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
106     if (obj && obj->mpOrig)
107         return atk_component_contains(ATK_COMPONENT(obj->mpOrig), x, y, coord_type);
108 
109     try
110     {
111         css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent
112             = getComponent(obj);
113         if( pComponent.is() )
114             return pComponent->containsPoint( translatePoint( pComponent, x, y, coord_type ) );
115     }
116     catch( const uno::Exception & )
117     {
118         g_warning( "Exception in containsPoint()" );
119     }
120 
121     return FALSE;
122 }
123 
124 /*****************************************************************************/
125 
126 static AtkObject *
component_wrapper_ref_accessible_at_point(AtkComponent * component,gint x,gint y,AtkCoordType coord_type)127 component_wrapper_ref_accessible_at_point (AtkComponent *component,
128                                            gint          x,
129                                            gint          y,
130                                            AtkCoordType  coord_type)
131 {
132     AtkObjectWrapper* obj = getObjectWrapper(component);
133     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
134     if (obj && obj->mpOrig)
135         return atk_component_ref_accessible_at_point(ATK_COMPONENT(obj->mpOrig), x, y, coord_type);
136 
137     try
138     {
139         css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent
140             = getComponent(obj);
141 
142         if( pComponent.is() )
143         {
144             uno::Reference< accessibility::XAccessible > xAccessible = pComponent->getAccessibleAtPoint(
145                 translatePoint( pComponent, x, y, coord_type ) );
146             return atk_object_wrapper_ref( xAccessible );
147         }
148     }
149     catch( const uno::Exception & )
150     {
151         g_warning( "Exception in getAccessibleAtPoint()" );
152     }
153 
154     return nullptr;
155 }
156 
157 /*****************************************************************************/
158 
159 static void
component_wrapper_get_position(AtkComponent * component,gint * x,gint * y,AtkCoordType coord_type)160 component_wrapper_get_position (AtkComponent   *component,
161                                 gint           *x,
162                                 gint           *y,
163                                 AtkCoordType   coord_type)
164 {
165     AtkObjectWrapper* obj = getObjectWrapper(component);
166     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
167     if (obj && obj->mpOrig)
168     {
169         atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), x, y, nullptr, nullptr, coord_type);
170         return;
171     }
172 
173     *x = *y = -1;
174 
175     try
176     {
177         css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent
178             = getComponent(obj);
179         if( pComponent.is() )
180         {
181             awt::Point aPos;
182 
183             if( coord_type == ATK_XY_SCREEN )
184                 aPos = pComponent->getLocationOnScreen();
185             else
186                 aPos = pComponent->getLocation();
187 
188             *x = aPos.X;
189             *y = aPos.Y;
190         }
191     }
192     catch( const uno::Exception & )
193     {
194         g_warning( "Exception in getLocation[OnScreen]()" );
195     }
196 }
197 
198 /*****************************************************************************/
199 
200 static void
component_wrapper_get_size(AtkComponent * component,gint * width,gint * height)201 component_wrapper_get_size (AtkComponent   *component,
202                             gint           *width,
203                             gint           *height)
204 {
205     AtkObjectWrapper* obj = getObjectWrapper(component);
206     //if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
207     if (obj && obj->mpOrig)
208     {
209         atk_component_get_extents(ATK_COMPONENT(obj->mpOrig), nullptr, nullptr, width, height, ATK_XY_WINDOW);
210         return;
211     }
212 
213     *width = *height = -1;
214 
215     try
216     {
217         css::uno::Reference<css::accessibility::XAccessibleComponent> pComponent
218             = getComponent(obj);
219         if( pComponent.is() )
220         {
221             awt::Size aSize = pComponent->getSize();
222             *width = aSize.Width;
223             *height = aSize.Height;
224         }
225     }
226     catch( const uno::Exception & )
227     {
228         g_warning( "Exception in getSize()" );
229     }
230 }
231 
232 /*****************************************************************************/
233 
234 static void
component_wrapper_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)235 component_wrapper_get_extents (AtkComponent *component,
236                                gint         *x,
237                                gint         *y,
238                                gint         *width,
239                                gint         *height,
240                                AtkCoordType  coord_type)
241 {
242     component_wrapper_get_position( component, x, y, coord_type );
243     component_wrapper_get_size( component, width, height );
244 }
245 
246 /*****************************************************************************/
247 
248 static gboolean
component_wrapper_set_extents(AtkComponent *,gint,gint,gint,gint,AtkCoordType)249 component_wrapper_set_extents (AtkComponent *, gint, gint, gint, gint, AtkCoordType)
250 {
251     g_warning( "AtkComponent::set_extents unimplementable" );
252     return FALSE;
253 }
254 
255 /*****************************************************************************/
256 
257 static gboolean
component_wrapper_set_position(AtkComponent *,gint,gint,AtkCoordType)258 component_wrapper_set_position (AtkComponent *, gint, gint, AtkCoordType)
259 {
260     g_warning( "AtkComponent::set_position unimplementable" );
261     return FALSE;
262 }
263 
264 /*****************************************************************************/
265 
266 static gboolean
component_wrapper_set_size(AtkComponent *,gint,gint)267 component_wrapper_set_size (AtkComponent *, gint, gint)
268 {
269     g_warning( "AtkComponent::set_size unimplementable" );
270     return FALSE;
271 }
272 
273 /*****************************************************************************/
274 
275 static AtkLayer
component_wrapper_get_layer(AtkComponent * component)276 component_wrapper_get_layer (AtkComponent   *component)
277 {
278     AtkRole role = atk_object_get_role( ATK_OBJECT( component ) );
279     AtkLayer layer = ATK_LAYER_WIDGET;
280 
281     switch (role)
282     {
283         case ATK_ROLE_POPUP_MENU:
284         case ATK_ROLE_MENU_ITEM:
285         case ATK_ROLE_CHECK_MENU_ITEM:
286         case ATK_ROLE_SEPARATOR:
287         case ATK_ROLE_LIST_ITEM:
288             layer = ATK_LAYER_POPUP;
289             break;
290         case ATK_ROLE_MENU:
291             {
292                 AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) );
293                 if( atk_object_get_role( parent ) != ATK_ROLE_MENU_BAR )
294                     layer = ATK_LAYER_POPUP;
295             }
296             break;
297 
298         case ATK_ROLE_LIST:
299             {
300                 AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) );
301                 if( atk_object_get_role( parent ) == ATK_ROLE_COMBO_BOX )
302                     layer = ATK_LAYER_POPUP;
303             }
304             break;
305 
306         default:
307             ;
308     }
309 
310     return layer;
311 }
312 
313 /*****************************************************************************/
314 
315 static gint
component_wrapper_get_mdi_zorder(AtkComponent *)316 component_wrapper_get_mdi_zorder (AtkComponent   *)
317 {
318     // only needed for ATK_LAYER_MDI (not used) or ATK_LAYER_WINDOW (inherited from GAIL)
319     return G_MININT;
320 }
321 
322 /*****************************************************************************/
323 
324 // This code is mostly stolen from libgail ..
325 
326 static guint
component_wrapper_add_focus_handler(AtkComponent * component,AtkFocusHandler handler)327 component_wrapper_add_focus_handler (AtkComponent    *component,
328                                      AtkFocusHandler  handler)
329 {
330     GSignalMatchType match_type;
331     gulong ret;
332     guint signal_id;
333 
334     match_type = GSignalMatchType(G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC);
335     signal_id = g_signal_lookup( "focus-event", ATK_TYPE_OBJECT );
336 
337     ret = g_signal_handler_find( component, match_type, signal_id, 0, nullptr,
338                                  static_cast<gpointer>(&handler), nullptr);
339     if (!ret)
340     {
341         return g_signal_connect_closure_by_id (component,
342                                                signal_id, 0,
343                                                g_cclosure_new (
344                                                G_CALLBACK (handler), nullptr,
345                                                nullptr),
346                                                FALSE);
347     }
348     else
349     {
350         return 0;
351     }
352 }
353 
354 /*****************************************************************************/
355 
356 static void
component_wrapper_remove_focus_handler(AtkComponent * component,guint handler_id)357 component_wrapper_remove_focus_handler (AtkComponent  *component,
358                                         guint         handler_id)
359 {
360     g_signal_handler_disconnect (component, handler_id);
361 }
362 
363 /*****************************************************************************/
364 
365 } // extern "C"
366 
367 void
componentIfaceInit(AtkComponentIface * iface)368 componentIfaceInit (AtkComponentIface *iface)
369 {
370   g_return_if_fail (iface != nullptr);
371 
372   iface->add_focus_handler = component_wrapper_add_focus_handler;
373   iface->contains = component_wrapper_contains;
374   iface->get_extents = component_wrapper_get_extents;
375   iface->get_layer = component_wrapper_get_layer;
376   iface->get_mdi_zorder = component_wrapper_get_mdi_zorder;
377   iface->get_position = component_wrapper_get_position;
378   iface->get_size = component_wrapper_get_size;
379   iface->grab_focus = component_wrapper_grab_focus;
380   iface->ref_accessible_at_point = component_wrapper_ref_accessible_at_point;
381   iface->remove_focus_handler = component_wrapper_remove_focus_handler;
382   iface->set_extents = component_wrapper_set_extents;
383   iface->set_position = component_wrapper_set_position;
384   iface->set_size = component_wrapper_set_size;
385 }
386 
387 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
388