diff options
Diffstat (limited to 'vcl/unx/gtk/a11y/atkwrapper.cxx')
-rw-r--r-- | vcl/unx/gtk/a11y/atkwrapper.cxx | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/vcl/unx/gtk/a11y/atkwrapper.cxx b/vcl/unx/gtk/a11y/atkwrapper.cxx new file mode 100644 index 000000000000..5beb838c0e82 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwrapper.cxx @@ -0,0 +1,951 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Type.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleRelation.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleImage.hpp> +#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/awt/XExtendedToolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/XTopWindowListener.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/Property.hpp> + +#include <rtl/ref.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include "atkwrapper.hxx" +#include "atkregistry.hxx" +#include "atklistener.hxx" + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +#include <string.h> + +using namespace ::com::sun::star; + +static GObjectClass *parent_class = NULL; + +static AtkRelationType mapRelationType( sal_Int16 nRelation ) +{ + AtkRelationType type = ATK_RELATION_NULL; + + switch( nRelation ) + { + case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM: + type = ATK_RELATION_FLOWS_FROM; + break; + + case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO: + type = ATK_RELATION_FLOWS_TO; + break; + + case accessibility::AccessibleRelationType::CONTROLLED_BY: + type = ATK_RELATION_CONTROLLED_BY; + break; + + case accessibility::AccessibleRelationType::CONTROLLER_FOR: + type = ATK_RELATION_CONTROLLER_FOR; + break; + + case accessibility::AccessibleRelationType::LABEL_FOR: + type = ATK_RELATION_LABEL_FOR; + break; + + case accessibility::AccessibleRelationType::LABELED_BY: + type = ATK_RELATION_LABELLED_BY; + break; + + case accessibility::AccessibleRelationType::MEMBER_OF: + type = ATK_RELATION_MEMBER_OF; + break; + + case accessibility::AccessibleRelationType::SUB_WINDOW_OF: + type = ATK_RELATION_SUBWINDOW_OF; + break; + + case accessibility::AccessibleRelationType::NODE_CHILD_OF: + type = ATK_RELATION_NODE_CHILD_OF; + break; + + default: + break; + } +#if 0 + ATK_RELATION_NODE_CHILD_OF, + ATK_RELATION_EMBEDS, + ATK_RELATION_EMBEDDED_BY, + ATK_RELATION_POPUP_FOR, +#endif + return type; +} + + +AtkStateType mapAtkState( sal_Int16 nState ) +{ + AtkStateType type = ATK_STATE_INVALID; + + // A perfect / complete mapping ... + switch( nState ) + { +#define MAP_DIRECT( a ) \ + case accessibility::AccessibleStateType::a: \ + type = ATK_STATE_##a; break + + MAP_DIRECT( INVALID ); + MAP_DIRECT( ACTIVE ); + MAP_DIRECT( ARMED ); + MAP_DIRECT( BUSY ); + MAP_DIRECT( CHECKED ); + MAP_DIRECT( EDITABLE ); + MAP_DIRECT( ENABLED ); + MAP_DIRECT( EXPANDABLE ); + MAP_DIRECT( EXPANDED ); + MAP_DIRECT( FOCUSABLE ); + MAP_DIRECT( FOCUSED ); + MAP_DIRECT( HORIZONTAL ); + MAP_DIRECT( ICONIFIED ); + MAP_DIRECT( INDETERMINATE ); + MAP_DIRECT( MANAGES_DESCENDANTS ); + MAP_DIRECT( MODAL ); + MAP_DIRECT( MULTI_LINE ); + MAP_DIRECT( OPAQUE ); + MAP_DIRECT( PRESSED ); + MAP_DIRECT( RESIZABLE ); + MAP_DIRECT( SELECTABLE ); + MAP_DIRECT( SELECTED ); + MAP_DIRECT( SENSITIVE ); + MAP_DIRECT( SHOWING ); + MAP_DIRECT( SINGLE_LINE ); + MAP_DIRECT( STALE ); + MAP_DIRECT( TRANSIENT ); + MAP_DIRECT( VERTICAL ); + MAP_DIRECT( VISIBLE ); + // a spelling error ... + case accessibility::AccessibleStateType::DEFUNC: + type = ATK_STATE_DEFUNCT; break; + case accessibility::AccessibleStateType::MULTI_SELECTABLE: + type = ATK_STATE_MULTISELECTABLE; break; + default: + break; + } + + return type; +} + +static inline AtkRole registerRole( const gchar * name ) +{ + AtkRole ret = atk_role_for_name( name ); + if( ATK_ROLE_INVALID == ret ) + ret = atk_role_register( name ); + + return ret; +} + +static AtkRole mapToAtkRole( sal_Int16 nRole ) +{ + AtkRole role = ATK_ROLE_UNKNOWN; + + static AtkRole roleMap[] = { + ATK_ROLE_UNKNOWN, + ATK_ROLE_ALERT, + ATK_ROLE_COLUMN_HEADER, + ATK_ROLE_CANVAS, + ATK_ROLE_CHECK_BOX, + ATK_ROLE_CHECK_MENU_ITEM, + ATK_ROLE_COLOR_CHOOSER, + ATK_ROLE_COMBO_BOX, + ATK_ROLE_DATE_EDITOR, + ATK_ROLE_DESKTOP_ICON, + ATK_ROLE_DESKTOP_FRAME, // ? pane + ATK_ROLE_DIRECTORY_PANE, + ATK_ROLE_DIALOG, + ATK_ROLE_UNKNOWN, // DOCUMENT - registered below + ATK_ROLE_UNKNOWN, // EMBEDDED_OBJECT - registered below + ATK_ROLE_UNKNOWN, // END_NOTE - registered below + ATK_ROLE_FILE_CHOOSER, + ATK_ROLE_FILLER, + ATK_ROLE_FONT_CHOOSER, + ATK_ROLE_FOOTER, + ATK_ROLE_TEXT, // FOOTNOTE - registered below + ATK_ROLE_FRAME, + ATK_ROLE_GLASS_PANE, + ATK_ROLE_IMAGE, // GRAPHIC + ATK_ROLE_UNKNOWN, // GROUP_BOX - registered below + ATK_ROLE_HEADER, + ATK_ROLE_PARAGRAPH, // HEADING - registered below + ATK_ROLE_TEXT, // HYPER_LINK - registered below + ATK_ROLE_ICON, + ATK_ROLE_INTERNAL_FRAME, + ATK_ROLE_LABEL, + ATK_ROLE_LAYERED_PANE, + ATK_ROLE_LIST, + ATK_ROLE_LIST_ITEM, + ATK_ROLE_MENU, + ATK_ROLE_MENU_BAR, + ATK_ROLE_MENU_ITEM, + ATK_ROLE_OPTION_PANE, + ATK_ROLE_PAGE_TAB, + ATK_ROLE_PAGE_TAB_LIST, + ATK_ROLE_PANEL, + ATK_ROLE_PARAGRAPH, + ATK_ROLE_PASSWORD_TEXT, + ATK_ROLE_POPUP_MENU, + ATK_ROLE_PUSH_BUTTON, + ATK_ROLE_PROGRESS_BAR, + ATK_ROLE_RADIO_BUTTON, + ATK_ROLE_RADIO_MENU_ITEM, + ATK_ROLE_ROW_HEADER, + ATK_ROLE_ROOT_PANE, + ATK_ROLE_SCROLL_BAR, + ATK_ROLE_SCROLL_PANE, + ATK_ROLE_UNKNOWN, // SHAPE - registered below + ATK_ROLE_SEPARATOR, + ATK_ROLE_SLIDER, + ATK_ROLE_SPIN_BUTTON, // SPIN_BOX ? + ATK_ROLE_SPLIT_PANE, + ATK_ROLE_STATUSBAR, + ATK_ROLE_TABLE, + ATK_ROLE_TABLE_CELL, + ATK_ROLE_TEXT, + ATK_ROLE_INTERNAL_FRAME, // TEXT_FRAME - registered below + ATK_ROLE_TOGGLE_BUTTON, + ATK_ROLE_TOOL_BAR, + ATK_ROLE_TOOL_TIP, + ATK_ROLE_TREE, + ATK_ROLE_VIEWPORT, + ATK_ROLE_WINDOW, + ATK_ROLE_PUSH_BUTTON, // BUTTON_DROPDOWN + ATK_ROLE_PUSH_BUTTON, // BUTTON_MENU + ATK_ROLE_UNKNOWN, // CAPTION - registered below + ATK_ROLE_UNKNOWN, // CHART - registered below + ATK_ROLE_UNKNOWN, // EDIT_BAR - registered below + ATK_ROLE_UNKNOWN, // FORM - registered below + ATK_ROLE_UNKNOWN, // IMAGE_MAP - registered below + ATK_ROLE_UNKNOWN, // NOTE - registered below + ATK_ROLE_UNKNOWN, // PAGE - registered below + ATK_ROLE_RULER, + ATK_ROLE_UNKNOWN, // SECTION - registered below + ATK_ROLE_UNKNOWN, // TREE_ITEM - registered below + ATK_ROLE_TREE_TABLE + }; + + static bool initialized = false; + + if( ! initialized ) + { + // re-use strings from ATK library + roleMap[accessibility::AccessibleRole::EDIT_BAR] = registerRole("edit bar"); + roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = registerRole("embedded component"); + roleMap[accessibility::AccessibleRole::CHART] = registerRole("chart"); + roleMap[accessibility::AccessibleRole::CAPTION] = registerRole("caption"); + roleMap[accessibility::AccessibleRole::DOCUMENT] = registerRole("document frame"); + roleMap[accessibility::AccessibleRole::HEADING] = registerRole("heading"); + roleMap[accessibility::AccessibleRole::PAGE] = registerRole("page"); + roleMap[accessibility::AccessibleRole::SECTION] = registerRole("section"); + roleMap[accessibility::AccessibleRole::FORM] = registerRole("form"); + + // these don't exist in ATK yet + roleMap[accessibility::AccessibleRole::END_NOTE] = registerRole("end note"); + roleMap[accessibility::AccessibleRole::FOOTNOTE] = registerRole("foot note"); + roleMap[accessibility::AccessibleRole::GROUP_BOX] = registerRole("group box"); + roleMap[accessibility::AccessibleRole::HYPER_LINK] = registerRole("hyper link"); + roleMap[accessibility::AccessibleRole::SHAPE] = registerRole("shape"); + roleMap[accessibility::AccessibleRole::TEXT_FRAME] = registerRole("text frame"); + roleMap[accessibility::AccessibleRole::IMAGE_MAP] = registerRole("image map"); + roleMap[accessibility::AccessibleRole::NOTE] = registerRole("note"); + roleMap[accessibility::AccessibleRole::TREE_ITEM] = registerRole("tree item"); + + initialized = true; + } + + static const sal_Int32 nMapSize = sizeof(roleMap)/sizeof(sal_Int16); + if( 0 <= nRole && nMapSize > nRole ) + role = roleMap[nRole]; + + return role; +} + + +/*****************************************************************************/ + +extern "C" { + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_name( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext ) + { + try { + rtl::OString aName = + rtl::OUStringToOString( + obj->mpContext->getAccessibleName(), + RTL_TEXTENCODING_UTF8); + + int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1; + if( nCmp != 0 ) + { + if( atk_obj->name ) + g_free(atk_obj->name); + atk_obj->name = g_strdup(aName.getStr()); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleName()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj); +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_description( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext ) + { + try { + rtl::OString aDescription = + rtl::OUStringToOString( + obj->mpContext->getAccessibleDescription(), + RTL_TEXTENCODING_UTF8); + + g_free(atk_obj->description); + atk_obj->description = g_strdup(aDescription.getStr()); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleDescription()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj); + +} + +/*****************************************************************************/ + +static gint +wrapper_get_n_children( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + gint n = 0; + + if( obj->mpContext ) + { + try { + n = obj->mpContext->getAccessibleChildCount(); + } + catch(const uno::Exception& e) { + OSL_ENSURE(0, "Exception in getAccessibleChildCount()" ); + } + } + + return n; +} + +/*****************************************************************************/ + +static AtkObject * +wrapper_ref_child( AtkObject *atk_obj, + gint i ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkObject* child = NULL; + + // see comments above atk_object_wrapper_remove_child + if( -1 < i && obj->index_of_child_about_to_be_removed == i ) + { + g_object_ref( obj->child_about_to_be_removed ); + return obj->child_about_to_be_removed; + } + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessible > xAccessible = + obj->mpContext->getAccessibleChild( i ); + + child = atk_object_wrapper_ref( xAccessible ); + } + catch(const uno::Exception& e) { + OSL_ENSURE(0, "Exception in getAccessibleChild"); + } + } + + return child; +} + +/*****************************************************************************/ + +static gint +wrapper_get_index_in_parent( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + gint i = -1; + + if( obj->mpContext ) + { + try { + i = obj->mpContext->getAccessibleIndexInParent(); + +#ifdef ENABLE_TRACING + fprintf(stderr, "%p->getAccessibleIndexInParent() returned: %u\n", + obj->mpAccessible, i); +#endif + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleIndexInParent()" ); + } + } + return i; +} + +/*****************************************************************************/ + +static AtkRelationSet * +wrapper_ref_relation_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkRelationSet *pSet = atk_relation_set_new(); + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet( + obj->mpContext->getAccessibleRelationSet() + ); + + sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0; + for( sal_Int32 n = 0; n < nRelations; n++ ) + { + accessibility::AccessibleRelation aRelation = xRelationSet->getRelation( n ); + sal_uInt32 nTargetCount = aRelation.TargetSet.getLength(); + AtkObject **pTargets = (AtkObject **) alloca( nTargetCount * sizeof(AtkObject *) ); + + for( sal_uInt32 i = 0; i < nTargetCount; i++ ) + { + uno::Reference< accessibility::XAccessible > xAccessible( + aRelation.TargetSet[i], uno::UNO_QUERY ); + pTargets[i] = atk_object_wrapper_ref( xAccessible ); + } + + AtkRelation *pRel = + atk_relation_new( + pTargets, nTargetCount, + mapRelationType( aRelation.RelationType ) + ); + atk_relation_set_add( pSet, pRel ); + g_object_unref( G_OBJECT( pRel ) ); + } + } + catch(const uno::Exception &e) { + g_object_unref( G_OBJECT( pSet ) ); + pSet = NULL; + } + } + + return pSet; +} + +/*****************************************************************************/ + +#if 0 +struct { + sal_Int16 value; + const sal_Char* name; +} aStateTypeTable[] = { + { accessibility::AccessibleStateType::INVALID, "INVALID" }, + { accessibility::AccessibleStateType::ACTIVE, "ACTIVE" }, + { accessibility::AccessibleStateType::ARMED, "ARMED" }, + { accessibility::AccessibleStateType::BUSY, "BUSY" }, + { accessibility::AccessibleStateType::CHECKED, "CHECKED" }, + { accessibility::AccessibleStateType::DEFUNC, "DEFUNC" }, + { accessibility::AccessibleStateType::EDITABLE, "EDITABLE" }, + { accessibility::AccessibleStateType::ENABLED, "ENABLED" }, + { accessibility::AccessibleStateType::EXPANDABLE, "EXPANDABLE" }, + { accessibility::AccessibleStateType::EXPANDED, "EXPANDED" }, + { accessibility::AccessibleStateType::FOCUSABLE, "FOCUSABLE" }, + { accessibility::AccessibleStateType::FOCUSED, "FOCUSED" }, + { accessibility::AccessibleStateType::HORIZONTAL, "HORIZONTAL" }, + { accessibility::AccessibleStateType::ICONIFIED, "ICONIFIED" }, + { accessibility::AccessibleStateType::INDETERMINATE, "INDETERMINATE" }, + { accessibility::AccessibleStateType::MANAGES_DESCENDANTS, "MANAGES_DESCENDANTS" }, + { accessibility::AccessibleStateType::MODAL, "MODAL" }, + { accessibility::AccessibleStateType::MULTI_LINE, "MULTI_LINE" }, + { accessibility::AccessibleStateType::MULTI_SELECTABLE, "MULTI_SELECTABLE" }, + { accessibility::AccessibleStateType::OPAQUE, "OPAQUE" }, + { accessibility::AccessibleStateType::PRESSED, "PRESSED" }, + { accessibility::AccessibleStateType::RESIZABLE, "RESIZABLE" }, + { accessibility::AccessibleStateType::SELECTABLE, "SELECTABLE" }, + { accessibility::AccessibleStateType::SELECTED, "SELECTED" }, + { accessibility::AccessibleStateType::SENSITIVE, "SENSITIVE" }, + { accessibility::AccessibleStateType::SHOWING, "SHOWING" }, + { accessibility::AccessibleStateType::SINGLE_LINE, "SINGLE_LINE" }, + { accessibility::AccessibleStateType::STALE, "STALE" }, + { accessibility::AccessibleStateType::TRANSIENT, "TRANSIENT" }, + { accessibility::AccessibleStateType::VERTICAL, "VERTICAL" }, + { accessibility::AccessibleStateType::VISIBLE, "VISIBLE" } +}; + +static void printStates(const uno::Sequence<sal_Int16>& rStates) +{ + sal_Int32 n = rStates.getLength(); + size_t nTypes = sizeof(aStateTypeTable)/sizeof(aStateTypeTable[0]); + for (sal_Int32 i = 0; i < n; ++i) + { + for (size_t j = 0; j < nTypes; ++j) + { + if (aStateTypeTable[j].value == rStates[i]) + printf("%s ", aStateTypeTable[j].name); + } + } + printf("\n"); +} +#endif + +static AtkStateSet * +wrapper_ref_state_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkStateSet *pSet = atk_state_set_new(); + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( + obj->mpContext->getAccessibleStateSet()); + + if( xStateSet.is() ) + { + uno::Sequence< sal_Int16 > aStates = xStateSet->getStates(); + + for( sal_Int32 n = 0; n < aStates.getLength(); n++ ) + atk_state_set_add_state( pSet, mapAtkState( aStates[n] ) ); + + // We need to emulate FOCUS state for menus, menu-items etc. + if( atk_obj == atk_get_focus_object() ) + atk_state_set_add_state( pSet, ATK_STATE_FOCUSED ); +/* FIXME - should we do this ? + else + atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED ); +*/ + } + } + + catch(const uno::Exception &e) { + g_warning( "Exception in wrapper_ref_state_set" ); + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + } + } + else + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + + return pSet; +} + +/*****************************************************************************/ + + +static void +atk_object_wrapper_finalize (GObject *obj) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj); + + if( pWrap->mpAccessible ) + { + ooo_wrapper_registry_remove( pWrap->mpAccessible ); + pWrap->mpAccessible->release(); + pWrap->mpAccessible = NULL; + } + + atk_object_wrapper_dispose( pWrap ); + + parent_class->finalize( obj ); +} + +static void +atk_object_wrapper_class_init (AtkObjectWrapperClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); + + parent_class = (GObjectClass *) g_type_class_peek_parent (klass); + + // GObject methods + gobject_class->finalize = atk_object_wrapper_finalize; + + // AtkObject methods + atk_class->get_name = wrapper_get_name; + atk_class->get_description = wrapper_get_description; + atk_class->get_n_children = wrapper_get_n_children; + atk_class->ref_child = wrapper_ref_child; + atk_class->get_index_in_parent = wrapper_get_index_in_parent; + atk_class->ref_relation_set = wrapper_ref_relation_set; + atk_class->ref_state_set = wrapper_ref_state_set; +} + +static void +atk_object_wrapper_init (AtkObjectWrapper *wrapper, + AtkObjectWrapperClass) +{ + wrapper->mpAction = NULL; + wrapper->mpComponent = NULL; + wrapper->mpEditableText = NULL; + wrapper->mpHypertext = NULL; + wrapper->mpImage = NULL; + wrapper->mpSelection = NULL; + wrapper->mpTable = NULL; + wrapper->mpText = NULL; + wrapper->mpValue = NULL; +} + +} // extern "C" + +GType +atk_object_wrapper_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo typeInfo = + { + sizeof (AtkObjectWrapperClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) atk_object_wrapper_class_init, + (GClassFinalizeFunc) NULL, + NULL, + sizeof (AtkObjectWrapper), + 0, + (GInstanceInitFunc) atk_object_wrapper_init, + NULL + } ; + type = g_type_register_static (ATK_TYPE_OBJECT, + "OOoAtkObj", + &typeInfo, (GTypeFlags)0) ; + } + return type; +} + +static bool +isOfType( uno::XInterface *pInterface, const uno::Type & rType ) +{ + g_return_val_if_fail( pInterface != NULL, false ); + + bool bIs = false; + try { + uno::Any aRet = pInterface->queryInterface( rType ); + + bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) && + ( aRet.pReserved != NULL ) ); + } catch( const uno::Exception &e) { } + + return bIs; +} + +extern "C" { +typedef GType (* GetGIfaceType ) (void); +} +const struct { + const char *name; + GInterfaceInitFunc aInit; + GetGIfaceType aGetGIfaceType; + const uno::Type & (*aGetUnoType) (void *); +} aTypeTable[] = { +// re-location heaven: + { + "Comp", (GInterfaceInitFunc) componentIfaceInit, + atk_component_get_type, + accessibility::XAccessibleComponent::static_type + }, + { + "Act", (GInterfaceInitFunc) actionIfaceInit, + atk_action_get_type, + accessibility::XAccessibleAction::static_type + }, + { + "Txt", (GInterfaceInitFunc) textIfaceInit, + atk_text_get_type, + accessibility::XAccessibleText::static_type + }, + { + "Val", (GInterfaceInitFunc) valueIfaceInit, + atk_value_get_type, + accessibility::XAccessibleValue::static_type + }, + { + "Tab", (GInterfaceInitFunc) tableIfaceInit, + atk_table_get_type, + accessibility::XAccessibleTable::static_type + }, + { + "Edt", (GInterfaceInitFunc) editableTextIfaceInit, + atk_editable_text_get_type, + accessibility::XAccessibleEditableText::static_type + }, + { + "Img", (GInterfaceInitFunc) imageIfaceInit, + atk_image_get_type, + accessibility::XAccessibleImage::static_type + }, + { + "Hyp", (GInterfaceInitFunc) hypertextIfaceInit, + atk_hypertext_get_type, + accessibility::XAccessibleHypertext::static_type + }, + { + "Sel", (GInterfaceInitFunc) selectionIfaceInit, + atk_selection_get_type, + accessibility::XAccessibleSelection::static_type + } + // AtkDocument is a nastily broken interface (so far) + // we could impl. get_document_type perhaps though. +}; + +const int aTypeTableSize = G_N_ELEMENTS( aTypeTable ); + +static GType +ensureTypeFor( uno::XInterface *pAccessible ) +{ + int i; + int bTypes[ aTypeTableSize ] = { 0, }; + rtl::OString aTypeName( "OOoAtkObj" ); + + for( i = 0; i < aTypeTableSize; i++ ) + { + if( isOfType( pAccessible, aTypeTable[i].aGetUnoType(0) ) ) + { + aTypeName += aTypeTable[i].name; + bTypes[i] = TRUE; + } +// g_message( "Accessible %p has type '%s' (%d)", +// pAccessible, aTypeTable[i].name, bTypes[i] ); + } + + GType nType = g_type_from_name( aTypeName ); + if( nType == G_TYPE_INVALID ) + { + GTypeInfo aTypeInfo = { + sizeof( AtkObjectWrapperClass ), + NULL, NULL, NULL, NULL, NULL, + sizeof( AtkObjectWrapper ), + 0, NULL, NULL + } ; + nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER, + aTypeName, &aTypeInfo, (GTypeFlags)0 ) ; + + for( int j = 0; j < aTypeTableSize; j++ ) + if( bTypes[j] ) + { + GInterfaceInfo aIfaceInfo = { NULL, NULL, NULL }; + aIfaceInfo.interface_init = aTypeTable[j].aInit; + g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(), + &aIfaceInfo); + } + } + return nType; +} + +AtkObject * +atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create ) +{ + g_return_val_if_fail( rxAccessible.get() != NULL, NULL ); + + AtkObject *obj = ooo_wrapper_registry_get(rxAccessible); + if( obj ) + { + g_object_ref( obj ); + return obj; + } + + if( create ) + return atk_object_wrapper_new( rxAccessible ); + + return NULL; +} + + +AtkObject * +atk_object_wrapper_new( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible, + AtkObject* parent ) +{ + g_return_val_if_fail( rxAccessible.get() != NULL, NULL ); + + AtkObjectWrapper *pWrap = NULL; + + try { + uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext()); + + g_return_val_if_fail( xContext.get() != NULL, NULL ); + + GType nType = ensureTypeFor( xContext.get() ); + gpointer obj = g_object_new( nType, NULL); + + pWrap = ATK_OBJECT_WRAPPER( obj ); + pWrap->mpAccessible = rxAccessible.get(); + rxAccessible->acquire(); + + pWrap->index_of_child_about_to_be_removed = -1; + pWrap->child_about_to_be_removed = NULL; + + xContext->acquire(); + pWrap->mpContext = xContext.get(); + + AtkObject* atk_obj = ATK_OBJECT(pWrap); + atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() ); + atk_obj->accessible_parent = parent; + + ooo_wrapper_registry_add( rxAccessible, atk_obj ); + + if( parent ) + g_object_ref( atk_obj->accessible_parent ); + else + { + /* gail_focus_tracker remembers the focused object at the first + * parent in the hierachy that is a Gtk+ widget, but at the time the + * event gets processed (at idle), it may be too late to create the + * hierachy, so doing it now .. + */ + uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); + + /* The top-level objects should never be of this class */ + OSL_ASSERT( xParent.is() ); + + if( xParent.is() ) + atk_obj->accessible_parent = atk_object_wrapper_ref( xParent ); + } + + // Attach a listener to the UNO object if it's not TRANSIENT + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() ); + if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) ) + { + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); + if( xBroadcaster.is() ) + xBroadcaster->addEventListener( static_cast< accessibility::XAccessibleEventListener * > ( new AtkListener(pWrap) ) ); + else + OSL_ASSERT( false ); + } + + return ATK_OBJECT( pWrap ); + } + catch (const uno::Exception &e) + { + if( pWrap ) + g_object_unref( pWrap ); + + return NULL; + } +} + + +/*****************************************************************************/ + +void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + AtkObject *atk_obj = ATK_OBJECT( wrapper ); + + atk_object_set_parent( child, atk_obj ); + g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, NULL ); +} + +/*****************************************************************************/ + +void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + /* + * the atk-bridge GTK+ module get's back to the event source to ref the child just + * vanishing, so we keep this reference because the semantic on OOo side is different. + */ + wrapper->child_about_to_be_removed = child; + wrapper->index_of_child_about_to_be_removed = index; + + g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, NULL ); + + wrapper->index_of_child_about_to_be_removed = -1; + wrapper->child_about_to_be_removed = NULL; +} + +/*****************************************************************************/ + +#define RELEASE(i) if( i ) { i->release(); i = NULL; } + +void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper) +{ + RELEASE( wrapper->mpContext ) + RELEASE( wrapper->mpAction ) + RELEASE( wrapper->mpComponent ) + RELEASE( wrapper->mpEditableText ) + RELEASE( wrapper->mpHypertext ) + RELEASE( wrapper->mpImage ) + RELEASE( wrapper->mpSelection ) + RELEASE( wrapper->mpMultiLineText ) + RELEASE( wrapper->mpTable ) + RELEASE( wrapper->mpText ) + RELEASE( wrapper->mpTextMarkup ) + RELEASE( wrapper->mpTextAttributes ) + RELEASE( wrapper->mpValue ) +} |