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