1
0
Fork 0
libreoffice/vcl/unx/gtk3/a11y/atkwrapper.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1103 lines
37 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#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/XAccessibleValue.hpp>
#include <com/sun/star/accessibility/XAccessibleAction.hpp>
#include <com/sun/star/accessibility/XAccessibleContext.hpp>
#include <com/sun/star/accessibility/XAccessibleContext2.hpp>
#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.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/XAccessibleExtendedAttributes.hpp>
#include <com/sun/star/accessibility/XAccessibleImage.hpp>
#include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
#include <com/sun/star/awt/XWindow.hpp>
#include <rtl/strbuf.hxx>
#include <osl/diagnose.h>
#include <comphelper/diagnose_ex.hxx>
#include <vcl/syschild.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/svapp.hxx>
#include <vcl/toolkit/unowrap.hxx>
#include "atkwrapper.hxx"
#include "atkregistry.hxx"
#include "atklistener.hxx"
#include "atktextattributes.hxx"
#include <vector>
#include <dlfcn.h>
using namespace ::com::sun::star;
static GObjectClass *parent_class = nullptr;
static AtkRelationType mapRelationType(accessibility::AccessibleRelationType eRelation)
{
AtkRelationType type = ATK_RELATION_NULL;
switch (eRelation)
{
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;
}
return type;
}
AtkStateType mapAtkState( sal_Int64 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( CHECKABLE );
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 );
MAP_DIRECT( DEFAULT );
// a spelling error ...
case accessibility::AccessibleStateType::DEFUNC:
type = ATK_STATE_DEFUNCT; break;
case accessibility::AccessibleStateType::MULTI_SELECTABLE:
type = ATK_STATE_MULTISELECTABLE; break;
default:
//Mis-use ATK_STATE_LAST_DEFINED to check if a state is unmapped
//NOTE! Do not report it
type = ATK_STATE_LAST_DEFINED;
break;
}
return type;
}
static AtkRole mapToAtkRole(sal_Int16 nRole, sal_Int64 nStates)
{
switch (nRole)
{
case accessibility::AccessibleRole::UNKNOWN:
return ATK_ROLE_UNKNOWN;
case accessibility::AccessibleRole::ALERT:
return ATK_ROLE_ALERT;
case accessibility::AccessibleRole::BLOCK_QUOTE:
return ATK_ROLE_BLOCK_QUOTE;
case accessibility::AccessibleRole::COLUMN_HEADER:
return ATK_ROLE_COLUMN_HEADER;
case accessibility::AccessibleRole::CANVAS:
return ATK_ROLE_CANVAS;
case accessibility::AccessibleRole::CHECK_BOX:
return ATK_ROLE_CHECK_BOX;
case accessibility::AccessibleRole::CHECK_MENU_ITEM:
return ATK_ROLE_CHECK_MENU_ITEM;
case accessibility::AccessibleRole::COLOR_CHOOSER:
return ATK_ROLE_COLOR_CHOOSER;
case accessibility::AccessibleRole::COMBO_BOX:
return ATK_ROLE_COMBO_BOX;
case accessibility::AccessibleRole::DATE_EDITOR:
return ATK_ROLE_DATE_EDITOR;
case accessibility::AccessibleRole::DESKTOP_ICON:
return ATK_ROLE_DESKTOP_ICON;
case accessibility::AccessibleRole::DESKTOP_PANE:
return ATK_ROLE_DESKTOP_FRAME;
case accessibility::AccessibleRole::DIRECTORY_PANE:
return ATK_ROLE_DIRECTORY_PANE;
case accessibility::AccessibleRole::DIALOG:
return ATK_ROLE_DIALOG;
case accessibility::AccessibleRole::DOCUMENT:
return ATK_ROLE_DOCUMENT_FRAME;
case accessibility::AccessibleRole::EMBEDDED_OBJECT:
return ATK_ROLE_EMBEDDED;
case accessibility::AccessibleRole::END_NOTE:
return ATK_ROLE_FOOTNOTE;
case accessibility::AccessibleRole::FILE_CHOOSER:
return ATK_ROLE_FILE_CHOOSER;
case accessibility::AccessibleRole::FILLER:
return ATK_ROLE_FILLER;
case accessibility::AccessibleRole::FONT_CHOOSER:
return ATK_ROLE_FONT_CHOOSER;
case accessibility::AccessibleRole::FOOTER:
return ATK_ROLE_FOOTER;
case accessibility::AccessibleRole::FOOTNOTE:
return ATK_ROLE_FOOTNOTE;
case accessibility::AccessibleRole::FRAME:
return ATK_ROLE_FRAME;
case accessibility::AccessibleRole::GLASS_PANE:
return ATK_ROLE_GLASS_PANE;
case accessibility::AccessibleRole::GRAPHIC:
return ATK_ROLE_IMAGE;
case accessibility::AccessibleRole::GROUP_BOX:
return ATK_ROLE_GROUPING;
case accessibility::AccessibleRole::HEADER:
return ATK_ROLE_HEADER;
case accessibility::AccessibleRole::HEADING:
return ATK_ROLE_HEADING;
case accessibility::AccessibleRole::HYPER_LINK:
return ATK_ROLE_LINK;
case accessibility::AccessibleRole::ICON:
return ATK_ROLE_ICON;
case accessibility::AccessibleRole::INTERNAL_FRAME:
return ATK_ROLE_INTERNAL_FRAME;
case accessibility::AccessibleRole::LABEL:
return ATK_ROLE_LABEL;
case accessibility::AccessibleRole::LAYERED_PANE:
return ATK_ROLE_LAYERED_PANE;
case accessibility::AccessibleRole::LIST:
return ATK_ROLE_LIST;
case accessibility::AccessibleRole::LIST_ITEM:
return ATK_ROLE_LIST_ITEM;
case accessibility::AccessibleRole::MENU:
return ATK_ROLE_MENU;
case accessibility::AccessibleRole::MENU_BAR:
return ATK_ROLE_MENU_BAR;
case accessibility::AccessibleRole::MENU_ITEM:
return ATK_ROLE_MENU_ITEM;
case accessibility::AccessibleRole::OPTION_PANE:
return ATK_ROLE_OPTION_PANE;
case accessibility::AccessibleRole::PAGE_TAB:
return ATK_ROLE_PAGE_TAB;
case accessibility::AccessibleRole::PAGE_TAB_LIST:
return ATK_ROLE_PAGE_TAB_LIST;
case accessibility::AccessibleRole::PANEL:
return ATK_ROLE_PANEL;
case accessibility::AccessibleRole::PARAGRAPH:
return ATK_ROLE_PARAGRAPH;
case accessibility::AccessibleRole::PASSWORD_TEXT:
return ATK_ROLE_PASSWORD_TEXT;
case accessibility::AccessibleRole::POPUP_MENU:
return ATK_ROLE_POPUP_MENU;
case accessibility::AccessibleRole::PUSH_BUTTON:
return ATK_ROLE_PUSH_BUTTON;
case accessibility::AccessibleRole::PROGRESS_BAR:
return ATK_ROLE_PROGRESS_BAR;
case accessibility::AccessibleRole::RADIO_BUTTON:
return ATK_ROLE_RADIO_BUTTON;
case accessibility::AccessibleRole::RADIO_MENU_ITEM:
return ATK_ROLE_RADIO_MENU_ITEM;
case accessibility::AccessibleRole::ROW_HEADER:
return ATK_ROLE_ROW_HEADER;
case accessibility::AccessibleRole::ROOT_PANE:
return ATK_ROLE_ROOT_PANE;
case accessibility::AccessibleRole::SCROLL_BAR:
return ATK_ROLE_SCROLL_BAR;
case accessibility::AccessibleRole::SCROLL_PANE:
return ATK_ROLE_SCROLL_PANE;
case accessibility::AccessibleRole::SHAPE:
return ATK_ROLE_PANEL;
case accessibility::AccessibleRole::SEPARATOR:
return ATK_ROLE_SEPARATOR;
case accessibility::AccessibleRole::SLIDER:
return ATK_ROLE_SLIDER;
case accessibility::AccessibleRole::SPIN_BOX:
return ATK_ROLE_SPIN_BUTTON;
case accessibility::AccessibleRole::SPLIT_PANE:
return ATK_ROLE_SPLIT_PANE;
case accessibility::AccessibleRole::STATUS_BAR:
return ATK_ROLE_STATUSBAR;
case accessibility::AccessibleRole::TABLE:
return ATK_ROLE_TABLE;
case accessibility::AccessibleRole::TABLE_CELL:
return ATK_ROLE_TABLE_CELL;
case accessibility::AccessibleRole::TEXT:
return ATK_ROLE_TEXT;
case accessibility::AccessibleRole::TEXT_FRAME:
return ATK_ROLE_PANEL;
case accessibility::AccessibleRole::TOGGLE_BUTTON:
return ATK_ROLE_TOGGLE_BUTTON;
case accessibility::AccessibleRole::TOOL_BAR:
return ATK_ROLE_TOOL_BAR;
case accessibility::AccessibleRole::TOOL_TIP:
return ATK_ROLE_TOOL_TIP;
case accessibility::AccessibleRole::TREE:
return ATK_ROLE_TREE;
case accessibility::AccessibleRole::VIEW_PORT:
return ATK_ROLE_VIEWPORT;
case accessibility::AccessibleRole::WINDOW:
return ATK_ROLE_WINDOW;
case accessibility::AccessibleRole::BUTTON_DROPDOWN:
{
if (nStates & css::accessibility::AccessibleStateType::CHECKABLE)
return ATK_ROLE_TOGGLE_BUTTON;
return ATK_ROLE_PUSH_BUTTON;
}
case accessibility::AccessibleRole::BUTTON_MENU:
#if ATK_CHECK_VERSION(2, 46, 0)
return ATK_ROLE_PUSH_BUTTON_MENU;
#else
return ATK_ROLE_PUSH_BUTTON;
#endif
case accessibility::AccessibleRole::CAPTION:
return ATK_ROLE_CAPTION;
case accessibility::AccessibleRole::CHART:
return ATK_ROLE_CHART;
case accessibility::AccessibleRole::EDIT_BAR:
return ATK_ROLE_EDITBAR;
case accessibility::AccessibleRole::FORM:
return ATK_ROLE_FORM;
case accessibility::AccessibleRole::IMAGE_MAP:
return ATK_ROLE_IMAGE_MAP;
case accessibility::AccessibleRole::NOTE:
return ATK_ROLE_COMMENT;
case accessibility::AccessibleRole::PAGE:
return ATK_ROLE_PAGE;
case accessibility::AccessibleRole::RULER:
return ATK_ROLE_RULER;
case accessibility::AccessibleRole::SECTION:
return ATK_ROLE_SECTION;
case accessibility::AccessibleRole::TREE_ITEM:
return ATK_ROLE_TREE_ITEM;
case accessibility::AccessibleRole::TREE_TABLE:
return ATK_ROLE_TREE_TABLE;
case accessibility::AccessibleRole::COMMENT:
return ATK_ROLE_COMMENT;
case accessibility::AccessibleRole::COMMENT_END:
return ATK_ROLE_UNKNOWN;
case accessibility::AccessibleRole::DOCUMENT_PRESENTATION:
return ATK_ROLE_DOCUMENT_PRESENTATION;
case accessibility::AccessibleRole::DOCUMENT_SPREADSHEET:
return ATK_ROLE_DOCUMENT_SPREADSHEET;
case accessibility::AccessibleRole::DOCUMENT_TEXT:
return ATK_ROLE_DOCUMENT_TEXT;
case accessibility::AccessibleRole::STATIC:
return ATK_ROLE_STATIC;
case accessibility::AccessibleRole::NOTIFICATION:
return ATK_ROLE_NOTIFICATION;
default:
SAL_WARN("vcl.gtk", "Unmapped accessible role: " << nRole);
return ATK_ROLE_UNKNOWN;
}
}
/*****************************************************************************/
extern "C" {
/*****************************************************************************/
static const gchar*
wrapper_get_name( AtkObject *atk_obj )
{
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
if( obj->mpContext.is() )
{
try {
OString aName =
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());
return atk_obj->name;
}
}
catch(const uno::Exception&) {
g_warning( "Exception in getAccessibleName()" );
}
}
return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj);
}
/*****************************************************************************/
static const gchar*
wrapper_get_description( AtkObject *atk_obj )
{
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
if( obj->mpContext.is() )
{
try {
OString aDescription =
OUStringToOString(
obj->mpContext->getAccessibleDescription(),
RTL_TEXTENCODING_UTF8);
g_free(atk_obj->description);
atk_obj->description = g_strdup(aDescription.getStr());
return atk_obj->description;
}
catch(const uno::Exception&) {
g_warning( "Exception in getAccessibleDescription()" );
}
}
return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj);
}
/*****************************************************************************/
static AtkAttributeSet *
wrapper_get_attributes( AtkObject *atk_obj )
{
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER( atk_obj );
AtkAttributeSet *pSet = nullptr;
try
{
uno::Reference< accessibility::XAccessibleExtendedAttributes >
xExtendedAttrs( obj->mpContext, uno::UNO_QUERY );
if( xExtendedAttrs.is() )
pSet = attribute_set_new_from_extended_attributes( xExtendedAttrs );
}
catch(const uno::Exception&)
{
g_warning( "Exception in getAccessibleAttributes()" );
}
return pSet;
}
/*****************************************************************************/
static gint
wrapper_get_n_children( AtkObject *atk_obj )
{
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
if (obj->mpSysObjChild)
return 1;
gint n = 0;
if( obj->mpContext.is() )
{
try {
sal_Int64 nChildCount = obj->mpContext->getAccessibleChildCount();
if (nChildCount > std::numeric_limits<gint>::max())
{
SAL_WARN("vcl.gtk", "wrapper_get_n_children: Child count exceeds maximum gint value, "
"returning max gint.");
nChildCount = std::numeric_limits<gint>::max();
}
else if (nChildCount < std::numeric_limits<gint>::min())
{
SAL_WARN("vcl.gtk", "wrapper_get_n_children: Child count exceeds minimum gint value, "
"returning min gint.");
nChildCount = std::numeric_limits<gint>::min();
}
n = nChildCount;
}
catch(const uno::Exception&) {
TOOLS_WARN_EXCEPTION( "vcl", "Exception" );
}
}
return n;
}
/*****************************************************************************/
static AtkObject *
wrapper_ref_child( AtkObject *atk_obj,
gint i )
{
SolarMutexGuard aGuard;
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
if (obj->mpSysObjChild)
{
g_object_ref(obj->mpSysObjChild);
return obj->mpSysObjChild;
}
AtkObject* child = nullptr;
// 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.is() )
{
try {
uno::Reference< accessibility::XAccessible > xAccessible =
obj->mpContext->getAccessibleChild( i );
child = atk_object_wrapper_ref( xAccessible );
}
catch(const uno::Exception&) {
TOOLS_WARN_EXCEPTION( "vcl", "getAccessibleChild");
}
}
return child;
}
/*****************************************************************************/
static gint
wrapper_get_index_in_parent( AtkObject *atk_obj )
{
SolarMutexGuard aGuard;
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
//if we're a native GtkDrawingArea with custom a11y, use the default toolkit a11y
if (obj->mpOrig)
return atk_object_get_index_in_parent(obj->mpOrig);
gint i = -1;
if( obj->mpContext.is() )
{
try {
sal_Int64 nIndex = obj->mpContext->getAccessibleIndexInParent();
if (nIndex > std::numeric_limits<gint>::max() ||
nIndex < std::numeric_limits<gint>::min())
{
// use -2 when the child index is too large to fit into 32 bit to neither use the
// valid index of another child nor -1, which would e.g. make Orca interpret the
// object as being a zombie
SAL_WARN("vcl.gtk", "wrapper_get_index_in_parent: Child index exceeds maximum gint value, "
"returning -2.");
nIndex = -2;
}
i = nIndex;
}
catch(const uno::Exception&) {
g_warning( "Exception in getAccessibleIndexInParent()" );
}
}
return i;
}
/*****************************************************************************/
AtkRelation*
atk_object_wrapper_relation_new(const accessibility::AccessibleRelation& rRelation)
{
sal_uInt32 nTargetCount = rRelation.TargetSet.getLength();
std::vector<AtkObject*> aTargets;
for (const uno::Reference<accessibility::XAccessible>& xTarget : rRelation.TargetSet)
{
aTargets.push_back(atk_object_wrapper_ref(xTarget));
}
AtkRelation *pRel =
atk_relation_new(
aTargets.data(), nTargetCount,
mapRelationType( rRelation.RelationType )
);
return pRel;
}
static AtkRelationSet *
wrapper_ref_relation_set( AtkObject *atk_obj )
{
AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj);
//if we're a native GtkDrawingArea with custom a11y, use the default toolkit relation set impl
if (obj->mpOrig)
return atk_object_ref_relation_set(obj->mpOrig);
AtkRelationSet *pSet = atk_relation_set_new();
if( obj->mpContext.is() )
{
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++ )
{
AtkRelation *pRel = atk_object_wrapper_relation_new(xRelationSet->getRelation(n));
atk_relation_set_add(pSet, pRel);
g_object_unref(pRel);
}
}
catch(const uno::Exception &) {
g_object_unref( G_OBJECT( pSet ) );
pSet = nullptr;
}
}
return pSet;
}
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.is() )
{
try {
sal_Int64 nStateSet = obj->mpContext->getAccessibleStateSet();
for (int i = 0; i < 63; ++i)
{
// ATK_STATE_LAST_DEFINED is used to check if the state
// is unmapped, do not report it to Atk
sal_Int64 nState = sal_Int64(1) << i;
if ((nStateSet & nState) && mapAtkState(nState) != ATK_STATE_LAST_DEFINED)
atk_state_set_add_state(pSet, mapAtkState(nState));
}
}
catch(const uno::Exception &) {
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.is() )
{
ooo_wrapper_registry_remove( pWrap->mpAccessible );
SolarMutexGuard aGuard;
pWrap->mpAccessible.clear();
}
atk_object_wrapper_dispose( pWrap );
if (pWrap->mpOrig)
g_object_unref(pWrap->mpOrig);
parent_class->finalize( obj );
}
static void
atk_object_wrapper_class_init (gpointer klass_, gpointer)
{
auto const klass = static_cast<AtkObjectWrapperClass *>(klass_);
GObjectClass *gobject_class = G_OBJECT_CLASS( klass );
AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass );
parent_class = static_cast<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_attributes = wrapper_get_attributes;
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;
AtkObjectClass* orig_atk_klass = static_cast<AtkObjectClass*>(g_type_class_ref(ATK_TYPE_OBJECT));
// tdf#150496 we want to inherit from GtkAccessible because gtk assumes it can cast to GtkAccessible
// but we want the original behaviour we got from atk_object_real_get_parent when we inherited
// from AtkObject
atk_class->get_parent = orig_atk_klass->get_parent;
// and likewise for focus_event
atk_class->focus_event = orig_atk_klass->focus_event;
g_type_class_unref(orig_atk_klass);
}
static void
atk_object_wrapper_init (AtkObjectWrapper *wrapper,
AtkObjectWrapperClass*)
{
wrapper->mpAction = nullptr;
wrapper->mpComponent = nullptr;
wrapper->mpEditableText = nullptr;
wrapper->mpHypertext = nullptr;
wrapper->mpImage = nullptr;
wrapper->mpSelection = nullptr;
wrapper->mpTable = nullptr;
wrapper->mpTableSelection = nullptr;
wrapper->mpText = nullptr;
wrapper->mpValue = nullptr;
}
} // extern "C"
GType
atk_object_wrapper_get_type()
{
static GType type = 0;
if (!type)
{
static const GTypeInfo typeInfo =
{
sizeof (AtkObjectWrapperClass),
nullptr,
nullptr,
atk_object_wrapper_class_init,
nullptr,
nullptr,
sizeof (AtkObjectWrapper),
0,
reinterpret_cast<GInstanceInitFunc>(atk_object_wrapper_init),
nullptr
} ;
type = g_type_register_static (GTK_TYPE_WIDGET_ACCESSIBLE,
"OOoAtkObj",
&typeInfo, GTypeFlags(0)) ;
}
return type;
}
static bool
isOfType( uno::XInterface *pInterface, const uno::Type & rType )
{
g_return_val_if_fail( pInterface != nullptr, false );
bool bIs = false;
try {
uno::Any aRet = pInterface->queryInterface( rType );
bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) &&
( aRet.pReserved != nullptr ) );
} catch( const uno::Exception &) { }
return bIs;
}
// Whether AtkTableCell can be supported for the interface.
// Returns true if the corresponding XAccessible has role TABLE_CELL
// and an XAccessibleTable as parent.
static bool isTableCell(uno::XInterface* pInterface)
{
g_return_val_if_fail(pInterface != nullptr, false);
try {
auto aType = cppu::UnoType<accessibility::XAccessible>::get().getTypeLibType();
uno::Any aAcc = pInterface->queryInterface(aType);
css::uno::Reference<css::accessibility::XAccessible> xAcc;
aAcc >>= xAcc;
if (!xAcc.is())
return false;
css::uno::Reference<css::accessibility::XAccessibleContext> xContext = xAcc->getAccessibleContext();
if (!xContext.is() || !(xContext->getAccessibleRole() == accessibility::AccessibleRole::TABLE_CELL))
return false;
css::uno::Reference<css::accessibility::XAccessible> xParent = xContext->getAccessibleParent();
if (!xParent.is())
return false;
css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext = xParent->getAccessibleContext();
if (!xParentContext.is())
return false;
css::uno::Reference<css::accessibility::XAccessibleTable> xTable(xParentContext, uno::UNO_QUERY);
return xTable.is();
}
catch(const uno::Exception &)
{
g_warning("Exception in isTableCell()");
}
return false;
}
extern "C" {
typedef GType (* GetGIfaceType ) ();
}
constexpr struct {
OString sName;
GInterfaceInitFunc const aInit;
GetGIfaceType const aGetGIfaceType;
const uno::Type & (*aGetUnoType) ();
} aTypeTable[] = {
// re-location heaven:
{
"Comp"_ostr, componentIfaceInit,
atk_component_get_type,
cppu::UnoType<accessibility::XAccessibleComponent>::get
},
{
"Act"_ostr, actionIfaceInit,
atk_action_get_type,
cppu::UnoType<accessibility::XAccessibleAction>::get
},
{
"Txt"_ostr, textIfaceInit,
atk_text_get_type,
cppu::UnoType<accessibility::XAccessibleText>::get
},
{
"Val"_ostr, valueIfaceInit,
atk_value_get_type,
cppu::UnoType<accessibility::XAccessibleValue>::get
},
{
"Tab"_ostr, tableIfaceInit,
atk_table_get_type,
cppu::UnoType<accessibility::XAccessibleTable>::get
},
{
"Cell"_ostr, tablecellIfaceInit,
atk_table_cell_get_type,
// there is no UNO a11y interface for table cells, so this case is handled separately below
nullptr
},
{
"Edt"_ostr, editableTextIfaceInit,
atk_editable_text_get_type,
cppu::UnoType<accessibility::XAccessibleEditableText>::get
},
{
"Img"_ostr, imageIfaceInit,
atk_image_get_type,
cppu::UnoType<accessibility::XAccessibleImage>::get
},
{
"Hyp"_ostr, hypertextIfaceInit,
atk_hypertext_get_type,
cppu::UnoType<accessibility::XAccessibleHypertext>::get
},
{
"Sel"_ostr, selectionIfaceInit,
atk_selection_get_type,
cppu::UnoType<accessibility::XAccessibleSelection>::get
}
// 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;
bool bTypes[ aTypeTableSize ] = { false, };
OStringBuffer aTypeNameBuf( "OOoAtkObj" );
for( i = 0; i < aTypeTableSize; i++ )
{
if (aTypeTable[i].sName == "Cell")
{
// there is no UNO interface for table cells, but AtkTableCell can be supported
// for table cells via the methods of the parent that is a table
if (isTableCell(pAccessible))
{
aTypeNameBuf.append(aTypeTable[i].sName);
bTypes[i] = true;
}
}
else if (isOfType( pAccessible, aTypeTable[i].aGetUnoType() ) )
{
aTypeNameBuf.append(aTypeTable[i].sName);
bTypes[i] = true;
}
}
OString aTypeName = aTypeNameBuf.makeStringAndClear();
GType nType = g_type_from_name( aTypeName.getStr() );
if( nType == G_TYPE_INVALID )
{
GTypeInfo aTypeInfo = {
sizeof( AtkObjectWrapperClass ),
nullptr, nullptr, nullptr, nullptr, nullptr,
sizeof( AtkObjectWrapper ),
0, nullptr, nullptr
} ;
nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER,
aTypeName.getStr(), &aTypeInfo,
GTypeFlags(0) ) ;
for( int j = 0; j < aTypeTableSize; j++ )
if( bTypes[j] )
{
GInterfaceInfo aIfaceInfo = { nullptr, nullptr, nullptr };
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( bool(rxAccessible), nullptr );
AtkObject *obj = ooo_wrapper_registry_get(rxAccessible);
if( obj )
{
g_object_ref( obj );
return obj;
}
if( create )
return atk_object_wrapper_new( rxAccessible );
return nullptr;
}
AtkObject *
atk_object_wrapper_new( const css::uno::Reference< css::accessibility::XAccessible >& rxAccessible,
AtkObject* parent, AtkObject* orig )
{
g_return_val_if_fail( bool(rxAccessible), nullptr );
AtkObjectWrapper *pWrap = nullptr;
try {
uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext());
g_return_val_if_fail( bool(xContext), nullptr );
GType nType = ensureTypeFor( xContext.get() );
gpointer obj = g_object_new( nType, nullptr);
pWrap = ATK_OBJECT_WRAPPER( obj );
pWrap->mpAccessible = rxAccessible;
pWrap->index_of_child_about_to_be_removed = -1;
pWrap->child_about_to_be_removed = nullptr;
pWrap->mpContext = xContext;
pWrap->mpOrig = orig;
if (pWrap->mpOrig)
g_object_ref(pWrap->mpOrig);
AtkObject* atk_obj = ATK_OBJECT(pWrap);
atk_obj->role = mapToAtkRole(xContext->getAccessibleRole(), xContext->getAccessibleStateSet());
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 hierarchy that is a Gtk+ widget, but at the time the
* event gets processed (at idle), it may be too late to create the
* hierarchy, so doing it now ..
*/
uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() );
if( xParent.is() )
atk_obj->accessible_parent = atk_object_wrapper_ref( xParent );
}
// Attach a listener to the UNO object if it's not TRANSIENT
sal_Int64 nStateSet( xContext->getAccessibleStateSet() );
if( ! (nStateSet & accessibility::AccessibleStateType::TRANSIENT ) )
{
uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY);
if( xBroadcaster.is() )
{
uno::Reference<accessibility::XAccessibleEventListener> xListener(new AtkListener(pWrap));
xBroadcaster->addAccessibleEventListener(xListener);
}
else
OSL_ASSERT( false );
}
static auto func = reinterpret_cast<void(*)(AtkObject*, const gchar*)>(dlsym(nullptr, "atk_object_set_accessible_id"));
if (func)
{
css::uno::Reference<css::accessibility::XAccessibleContext2> xContext2(xContext, css::uno::UNO_QUERY);
if( xContext2.is() )
{
OString aId = OUStringToOString( xContext2->getAccessibleId(), RTL_TEXTENCODING_UTF8);
(*func)(atk_obj, aId.getStr());
}
}
// tdf#141197 if we have a sysobj child then include that in the hierarchy
if (UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper())
{
css::uno::Reference<css::awt::XWindow> xAWTWindow(rxAccessible, css::uno::UNO_QUERY);
VclPtr<vcl::Window> xWindow = pWrapper->GetWindow(xAWTWindow);
if (xWindow && xWindow->GetType() == WindowType::SYSTEMCHILDWINDOW)
{
const SystemEnvData* pEnvData = static_cast<SystemChildWindow*>(xWindow.get())->GetSystemData();
if (GtkWidget *pSysObj = pEnvData ? static_cast<GtkWidget*>(pEnvData->pWidget) : nullptr)
pWrap->mpSysObjChild = gtk_widget_get_accessible(pSysObj);
}
}
return ATK_OBJECT( pWrap );
}
catch (const uno::Exception &)
{
if( pWrap )
g_object_unref( pWrap );
return nullptr;
}
}
/*****************************************************************************/
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, nullptr );
}
/*****************************************************************************/
void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index)
{
/*
* the atk-bridge GTK+ module gets 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, nullptr );
wrapper->index_of_child_about_to_be_removed = -1;
wrapper->child_about_to_be_removed = nullptr;
}
/*****************************************************************************/
void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role, sal_Int64 nStates)
{
AtkObject *atk_obj = ATK_OBJECT( wrapper );
atk_object_set_role(atk_obj, mapToAtkRole(role, nStates));
}
/*****************************************************************************/
void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper)
{
wrapper->mpContext.clear();
wrapper->mpAction.clear();
wrapper->mpComponent.clear();
wrapper->mpEditableText.clear();
wrapper->mpHypertext.clear();
wrapper->mpImage.clear();
wrapper->mpSelection.clear();
wrapper->mpMultiLineText.clear();
wrapper->mpTable.clear();
wrapper->mpTableSelection.clear();
wrapper->mpText.clear();
wrapper->mpTextMarkup.clear();
wrapper->mpTextAttributes.clear();
wrapper->mpValue.clear();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */