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