xref: /AOO41X/main/vcl/unx/gtk/a11y/atkwindow.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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 <unx/gtk/gtkframe.hxx>
32 #include <vcl/window.hxx>
33 #include "vcl/popupmenuwindow.hxx"
34 
35 #include "atkwindow.hxx"
36 #include "atkwrapper.hxx"
37 #include "atkregistry.hxx"
38 
39 #include <com/sun/star/accessibility/AccessibleRole.hpp>
40 
41 using namespace ::com::sun::star::accessibility;
42 using namespace ::com::sun::star::uno;
43 
44 extern "C" {
45 
46 static void (* window_real_initialize) (AtkObject *obj, gpointer data) = NULL;
47 static void (* window_real_finalize) (GObject *obj) = NULL;
48 
49 static void
50 init_from_window( AtkObject *accessible, Window *pWindow )
51 {
52     static AtkRole aDefaultRole = ATK_ROLE_INVALID;
53 
54     // Special role for sub-menu and combo-box popups that are exposed directly
55     // by their parents already.
56     if( aDefaultRole == ATK_ROLE_INVALID )
57         aDefaultRole = atk_role_register( "redundant object" );
58 
59     AtkRole role = aDefaultRole;
60 
61     // Determine the appropriate role for the GtkWindow
62     switch( pWindow->GetAccessibleRole() )
63     {
64         case AccessibleRole::ALERT:
65             role = ATK_ROLE_ALERT;
66             break;
67 
68         case AccessibleRole::DIALOG:
69             role = ATK_ROLE_DIALOG;
70             break;
71 
72         case AccessibleRole::FRAME:
73             role = ATK_ROLE_FRAME;
74             break;
75 
76         /* Ignore window objects for sub-menus, combo- and list boxes,
77          *  which are exposed as children of their parents.
78          */
79         case AccessibleRole::WINDOW:
80         {
81             sal_uInt16 type = WINDOW_WINDOW;
82             bool parentIsMenuFloatingWindow = false;
83 
84             Window *pParent = pWindow->GetParent();
85             if( pParent ) {
86                 type = pParent->GetType();
87                 parentIsMenuFloatingWindow = ( TRUE == pParent->IsMenuFloatingWindow() );
88             }
89 
90             if( (WINDOW_LISTBOX != type) && (WINDOW_COMBOBOX != type) &&
91                 (WINDOW_MENUBARWINDOW != type) && ! parentIsMenuFloatingWindow )
92             {
93                 role = ATK_ROLE_WINDOW;
94             }
95         }
96         break;
97 
98         default:
99         {
100             Window *pChild = pWindow->GetChild( 0 );
101             if( pChild )
102             {
103                 if( WINDOW_HELPTEXTWINDOW == pChild->GetType() )
104                 {
105                     role = ATK_ROLE_TOOL_TIP;
106                     pChild->SetAccessibleRole( AccessibleRole::LABEL );
107                     accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() );
108                 }
109                 else if ( pWindow->GetType() == WINDOW_BORDERWINDOW && pChild->GetType() == WINDOW_FLOATINGWINDOW )
110                 {
111                     PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild);
112                     if (p && p->IsPopupMenu() && p->GetMenuStackLevel() == 0)
113                     {
114                         // This is a top-level menu popup.  Register it.
115                         role = ATK_ROLE_POPUP_MENU;
116                         pChild->SetAccessibleRole( AccessibleRole::POPUP_MENU );
117                         accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() );
118                     }
119                 }
120             }
121             break;
122         }
123     }
124 
125     accessible->role = role;
126 }
127 
128 /*****************************************************************************/
129 
130 static gint
131 ooo_window_wrapper_clear_focus(gpointer)
132 {
133     atk_focus_tracker_notify( NULL );
134     return FALSE;
135 }
136 
137 /*****************************************************************************/
138 
139 static gboolean
140 ooo_window_wrapper_real_focus_gtk (GtkWidget *, GdkEventFocus *)
141 {
142     g_idle_add( ooo_window_wrapper_clear_focus, NULL );
143     return FALSE;
144 }
145 
146 static gboolean ooo_tooltip_map( GtkWidget* pToolTip, gpointer )
147 {
148     AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip );
149     if( pAccessible )
150         atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, TRUE );
151     return FALSE;
152 }
153 
154 static gboolean ooo_tooltip_unmap( GtkWidget* pToolTip, gpointer )
155 {
156     AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip );
157     if( pAccessible )
158         atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, FALSE );
159     return FALSE;
160 }
161 
162 /*****************************************************************************/
163 
164 static bool
165 isChildPopupMenu(Window* pWindow)
166 {
167     Window* pChild = pWindow->GetAccessibleChildWindow(0);
168     if (!pChild)
169         return false;
170 
171     if (WINDOW_FLOATINGWINDOW != pChild->GetType())
172         return false;
173 
174     PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild);
175     if (!p)
176         return false;
177 
178     return p->IsPopupMenu();
179 }
180 
181 static void
182 ooo_window_wrapper_real_initialize(AtkObject *obj, gpointer data)
183 {
184     window_real_initialize(obj, data);
185 
186     GtkSalFrame *pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( data ) );
187     if( pFrame )
188     {
189         Window *pWindow = pFrame->GetWindow();
190         if( pWindow )
191         {
192             init_from_window( obj, pWindow );
193 
194             Reference< XAccessible > xAccessible( pWindow->GetAccessible(true) );
195 
196             /* We need the wrapper object for the top-level XAccessible to be
197              * in the wrapper registry when atk traverses the hierachy up on
198              * focus events
199              */
200             if( WINDOW_BORDERWINDOW == pWindow->GetType() )
201             {
202                 if ( isChildPopupMenu(pWindow) )
203                 {
204                     AtkObject *child = atk_object_wrapper_new( xAccessible, obj );
205                     ooo_wrapper_registry_add( xAccessible, child );
206                 }
207                 else
208                 {
209                     ooo_wrapper_registry_add( xAccessible, obj );
210                     g_object_set_data( G_OBJECT(obj), "ooo:atk-wrapper-key", xAccessible.get() );
211                 }
212             }
213             else
214             {
215                 AtkObject *child = atk_object_wrapper_new( xAccessible, obj );
216                 child->role = ATK_ROLE_FILLER;
217                 if( (ATK_ROLE_DIALOG == obj->role) || (ATK_ROLE_ALERT == obj->role) )
218                     child->role = ATK_ROLE_OPTION_PANE;
219                 ooo_wrapper_registry_add( xAccessible, child );
220             }
221         }
222     }
223 
224     g_signal_connect_after( GTK_WIDGET( data ), "focus-out-event",
225                             G_CALLBACK (ooo_window_wrapper_real_focus_gtk),
226                             NULL);
227 
228     if( obj->role == ATK_ROLE_TOOL_TIP )
229     {
230         g_signal_connect_after( GTK_WIDGET( data ), "map-event",
231                                 G_CALLBACK (ooo_tooltip_map),
232                                 NULL);
233         g_signal_connect_after( GTK_WIDGET( data ), "unmap-event",
234                                 G_CALLBACK (ooo_tooltip_unmap),
235                                 NULL);
236     }
237 }
238 
239 /*****************************************************************************/
240 
241 static void
242 ooo_window_wrapper_real_finalize (GObject *obj)
243 {
244     ooo_wrapper_registry_remove( (XAccessible *) g_object_get_data( obj, "ooo:atk-wrapper-key" ));
245     window_real_finalize( obj );
246 }
247 
248 /*****************************************************************************/
249 
250 static void
251 ooo_window_wrapper_class_init (AtkObjectClass *klass, gpointer)
252 {
253     AtkObjectClass *atk_class;
254     GObjectClass *gobject_class;
255     gpointer data;
256 
257     /*
258      * Patch the gobject vtable of GailWindow to refer to our instance of
259      * "initialize".
260      */
261 
262     data = g_type_class_peek_parent( klass );
263     atk_class = ATK_OBJECT_CLASS (data);
264 
265     window_real_initialize = atk_class->initialize;
266     atk_class->initialize = ooo_window_wrapper_real_initialize;
267 
268     gobject_class = G_OBJECT_CLASS (data);
269 
270     window_real_finalize = gobject_class->finalize;
271     gobject_class->finalize = ooo_window_wrapper_real_finalize;
272 }
273 
274 } // extern "C"
275 
276 /*****************************************************************************/
277 
278 GType
279 ooo_window_wrapper_get_type (void)
280 {
281     static GType type = 0;
282 
283     if (!type)
284     {
285         GType parent_type = g_type_from_name( "GailWindow" );
286 
287         if( ! parent_type )
288         {
289             g_warning( "Unknown type: GailWindow" );
290             parent_type = ATK_TYPE_OBJECT;
291         }
292 
293         GTypeQuery type_query;
294         g_type_query( parent_type, &type_query );
295 
296         static const GTypeInfo typeInfo =
297         {
298             type_query.class_size,
299             (GBaseInitFunc) NULL,
300             (GBaseFinalizeFunc) NULL,
301             (GClassInitFunc) ooo_window_wrapper_class_init,
302             (GClassFinalizeFunc) NULL,
303             NULL,
304             type_query.instance_size,
305             0,
306             (GInstanceInitFunc) NULL,
307             NULL
308         } ;
309 
310         type = g_type_register_static (parent_type, "OOoWindowAtkObject", &typeInfo, (GTypeFlags)0) ;
311     }
312 
313     return type;
314 }
315 
316 void restore_gail_window_vtable (void)
317 {
318     AtkObjectClass *atk_class;
319     gpointer data;
320 
321     GType type = g_type_from_name( "GailWindow" );
322 
323     if( type == G_TYPE_INVALID )
324         return;
325 
326     data = g_type_class_peek( type );
327     atk_class = ATK_OBJECT_CLASS (data);
328 
329     atk_class->initialize = window_real_initialize;
330 }
331 
332