diff options
Diffstat (limited to 'vcl/unx/gtk/a11y')
26 files changed, 8196 insertions, 0 deletions
diff --git a/vcl/unx/gtk/a11y/TODO b/vcl/unx/gtk/a11y/TODO new file mode 100644 index 000000000000..1048bd96ef75 --- /dev/null +++ b/vcl/unx/gtk/a11y/TODO @@ -0,0 +1,49 @@ +cws 'atkbridge' +#Issue number: i#47890# +Submitted by: mmeeks + +Hacked up prototype of atk bridge + + +Serious problems + + Threading/locking: + + incoming CORBA calls & the GDK lock + + how are these being processed & on what thread ? + + are we holding the GDK_THREADS lock ? + + can we even do that ? + + is it really necessary to be thread safe ? + + how does this work in combination with the (unsafe) GAIL code ? + + what should incoming CORBA calls be doing ? + + esp. since we can't tell if they're coming from + in-proc or not either [ though this is unlikely ] + + +Test: + + in-line text editing, does the TEXT_CHANGED signal get it right, + + why not copy/paste/delete etc. ? + + check vs. writer & other bits ... + + AtkSelection + + AtkHyper* + +* At-poke + + implement non-gui mode - for to-console event logging + + logging + + more detail from remaining events + + add a Tree navigation thing instead (?) + + poke a sub-child (?) + + embed a tree widget inside the tree view ? + + AtkHyperText testing (?) + + +Known bugs: + + AtkText + + selection interface - multiple selections ? + + word boundary issues + + copy AccessibleTextImpl.java's getAfterIndex eg. + + the 'getFoo' methods need to use UNO_QUERY_THROW & + throw an exception to avoid null pointer dereferences. + + AtkAttributeSet (etc.) + + AtkEditableText + + finish/test AtkTable + + HyperLink 'link_activated', HyperText 'link_selected' (?) + + tooltips create new toplevels with broken roles. diff --git a/vcl/unx/gtk/a11y/atkaction.cxx b/vcl/unx/gtk/a11y/atkaction.cxx new file mode 100644 index 000000000000..4329dd345d14 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkaction.cxx @@ -0,0 +1,278 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp> + +#include <com/sun/star/awt/Key.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> + +#include <rtl/strbuf.hxx> +#include <algorithm> +#include <map> + +#include <stdio.h> + +using namespace ::com::sun::star; + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( const rtl::OString& rString ) +{ + static const int nMax = 10; + static rtl::OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = rString; + return aUgly[ nIdx ]; +} + +static accessibility::XAccessibleAction* + getAction( AtkAction *action ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( action ); + + if( pWrap ) + { + if( !pWrap->mpAction && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleAction::static_type(NULL) ); + pWrap->mpAction = reinterpret_cast< accessibility::XAccessibleAction * > (any.pReserved); + pWrap->mpAction->acquire(); + } + + return pWrap->mpAction; + } + + return NULL; +} + +extern "C" { + +static gboolean +action_wrapper_do_action (AtkAction *action, + gint i) +{ + try { + accessibility::XAccessibleAction* pAction = getAction( action ); + if( pAction ) + return pAction->doAccessibleAction( i ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in doAccessibleAction()" ); + } + + return FALSE; +} + +static gint +action_wrapper_get_n_actions (AtkAction *action) +{ + try { + accessibility::XAccessibleAction* pAction = getAction( action ); + if( pAction ) + return pAction->getAccessibleActionCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleActionCount()" ); + } + + return 0; +} + +static G_CONST_RETURN gchar * +action_wrapper_get_description (AtkAction *, gint) +{ + // GAIL implement this only for cells + g_warning( "Not implemented: get_description()" ); + return ""; +} + +static G_CONST_RETURN gchar * +action_wrapper_get_localized_name (AtkAction *, gint) +{ + // GAIL doesn't implement this as well + g_warning( "Not implemented: get_localized_name()" ); + return ""; +} + +#define ACTION_NAME_PAIR( OOoName, AtkName ) \ + std::pair< const rtl::OUString, const gchar * > ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OOoName ) ), AtkName ) + +static G_CONST_RETURN gchar * +action_wrapper_get_name (AtkAction *action, + gint i) +{ + static std::map< rtl::OUString, const gchar * > aNameMap; + + if( aNameMap.empty() ) + { + aNameMap.insert( ACTION_NAME_PAIR( "click", "click" ) ); + aNameMap.insert( ACTION_NAME_PAIR( "select", "click" ) ); + aNameMap.insert( ACTION_NAME_PAIR( "togglePopup", "push" ) ); + } + + try { + accessibility::XAccessibleAction* pAction = getAction( action ); + if( pAction ) + { + std::map< rtl::OUString, const gchar * >::iterator iter; + + rtl::OUString aDesc( pAction->getAccessibleActionDescription( i ) ); + + iter = aNameMap.find( aDesc ); + if( iter != aNameMap.end() ) + return iter->second; + + std::pair< const rtl::OUString, const gchar * > aNewVal( aDesc, + g_strdup( OUStringToConstGChar(aDesc) ) ); + + if( aNameMap.insert( aNewVal ).second ) + return aNewVal.second; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleActionDescription()" ); + } + + return ""; +} + +/* +* GNOME Expects a string in the format: +* +* <nmemonic>;<full-path>;<accelerator> +* +* The keybindings in <full-path> should be separated by ":" +*/ + +static inline void +appendKeyStrokes(rtl::OStringBuffer& rBuffer, const uno::Sequence< awt::KeyStroke >& rKeyStrokes) +{ + for( sal_Int32 i = 0; i < rKeyStrokes.getLength(); i++ ) + { + if( rKeyStrokes[i].Modifiers & awt::KeyModifier::SHIFT ) + rBuffer.append("<Shift>"); + if( rKeyStrokes[i].Modifiers & awt::KeyModifier::MOD1 ) + rBuffer.append("<Control>"); + if( rKeyStrokes[i].Modifiers & awt::KeyModifier::MOD2 ) + rBuffer.append("<Alt>"); + + if( ( rKeyStrokes[i].KeyCode >= awt::Key::A ) && ( rKeyStrokes[i].KeyCode <= awt::Key::Z ) ) + rBuffer.append( (sal_Char) ( 'a' + ( rKeyStrokes[i].KeyCode - awt::Key::A ) ) ); + else + { + sal_Char c = '\0'; + + switch( rKeyStrokes[i].KeyCode ) + { + case awt::Key::TAB: c = '\t'; break; + case awt::Key::SPACE: c = ' '; break; + case awt::Key::ADD: c = '+'; break; + case awt::Key::SUBTRACT: c = '-'; break; + case awt::Key::MULTIPLY: c = '*'; break; + case awt::Key::DIVIDE: c = '/'; break; + case awt::Key::POINT: c = '.'; break; + case awt::Key::COMMA: c = ','; break; + case awt::Key::LESS: c = '<'; break; + case awt::Key::GREATER: c = '>'; break; + case awt::Key::EQUAL: c = '='; break; + case 0: + break; + default: + g_warning( "Unmapped KeyCode: %d", rKeyStrokes[i].KeyCode ); + break; + } + + if( c != '\0' ) + rBuffer.append( c ); + } + } +} + + +static G_CONST_RETURN gchar * +action_wrapper_get_keybinding (AtkAction *action, + gint i) +{ + try { + accessibility::XAccessibleAction* pAction = getAction( action ); + if( pAction ) + { + uno::Reference< accessibility::XAccessibleKeyBinding > xBinding( pAction->getAccessibleActionKeyBinding( i )); + + if( xBinding.is() ) + { + rtl::OStringBuffer aRet; + + sal_Int32 nmax = std::min( xBinding->getAccessibleKeyBindingCount(), (sal_Int32) 3 ); + for( sal_Int32 n = 0; n < nmax; n++ ) + { + appendKeyStrokes( aRet, xBinding->getAccessibleKeyBinding( n ) ); + + if( n < 2 ) + aRet.append( (sal_Char) ';' ); + } + + // !! FIXME !! remember keystroke in wrapper object ? + return getAsConst( aRet.makeStringAndClear() ); + } + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in get_keybinding()" ); + } + + return ""; +} + +static gboolean +action_wrapper_set_description (AtkAction *, gint, const gchar *) +{ + return FALSE; +} + +} // extern "C" + +void +actionIfaceInit (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = action_wrapper_do_action; + iface->get_n_actions = action_wrapper_get_n_actions; + iface->get_description = action_wrapper_get_description; + iface->get_keybinding = action_wrapper_get_keybinding; + iface->get_name = action_wrapper_get_name; + iface->get_localized_name = action_wrapper_get_localized_name; + iface->set_description = action_wrapper_set_description; +} diff --git a/vcl/unx/gtk/a11y/atkbridge.cxx b/vcl/unx/gtk/a11y/atkbridge.cxx new file mode 100644 index 000000000000..25add8e0dd18 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkbridge.cxx @@ -0,0 +1,78 @@ +/************************************************************************* + * + * 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 <plugins/gtk/atkbridge.hxx> +#include <plugins/gtk/gtkframe.hxx> + +#include "atkfactory.hxx" +#include "atkutil.hxx" +#include "atkwindow.hxx" +#include <stdio.h> + +bool InitAtkBridge(void) +{ + const char* pVersion = atk_get_toolkit_version(); + if( ! pVersion ) + return false; + + unsigned int major, minor, micro; + + /* check gail minimum version requirements */ + if( sscanf( pVersion, "%u.%u.%u", &major, &minor, µ) < 3 ) + { + // g_warning( "unable to parse gail version number" ); + return false; + } + + if( ( (major << 16) | (minor << 8) | micro ) < ( (1 << 16) | 8 << 8 | 6 ) ) + { + g_warning( "libgail >= 1.8.6 required for accessibility support" ); + return false; + } + + /* Initialize the AtkUtilityWrapper class */ + g_type_class_unref( g_type_class_ref( OOO_TYPE_ATK_UTIL ) ); + + /* Initialize the GailWindow wrapper class */ + g_type_class_unref( g_type_class_ref( OOO_TYPE_WINDOW_WRAPPER ) ); + + /* Register AtkObject wrapper factory */ + AtkRegistry * registry = atk_get_default_registry(); + if( registry ) + atk_registry_set_factory_type( registry, OOO_TYPE_FIXED, OOO_TYPE_WRAPPER_FACTORY ); + + return true; +} + +void DeInitAtkBridge() +{ + restore_gail_window_vtable(); +} + diff --git a/vcl/unx/gtk/a11y/atkcomponent.cxx b/vcl/unx/gtk/a11y/atkcomponent.cxx new file mode 100644 index 000000000000..24cf335ebeb0 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkcomponent.cxx @@ -0,0 +1,382 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +using namespace ::com::sun::star; + +static accessibility::XAccessibleComponent* + getComponent( AtkComponent *pComponent ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pComponent ); + if( pWrap ) + { + if( !pWrap->mpComponent && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleComponent::static_type(NULL) ); + pWrap->mpComponent = reinterpret_cast< accessibility::XAccessibleComponent * > (any.pReserved); + pWrap->mpComponent->acquire(); + } + + return pWrap->mpComponent; + } + + return NULL; +} + +/*****************************************************************************/ + +static awt::Point +translatePoint( accessibility::XAccessibleComponent *pComponent, + gint x, gint y, AtkCoordType t) +{ + awt::Point aOrigin( 0, 0 ); + if( t == ATK_XY_SCREEN ) + aOrigin = pComponent->getLocationOnScreen(); + +#ifdef ENABLE_TRACING + fprintf(stderr, "coordinates ( %u, %u ) translated to: ( %u, %u )\n", + x, y, x - aOrigin.X, y - aOrigin.Y); +#endif + + return awt::Point( x - aOrigin.X, y - aOrigin.Y ); +} + +/*****************************************************************************/ + +extern "C" { + +static gboolean +component_wrapper_grab_focus (AtkComponent *component) +{ + try + { + accessibility::XAccessibleComponent* pComponent = getComponent( component ); + if( pComponent ) + { + pComponent->grabFocus(); + return TRUE; + } + } + catch( const uno::Exception &e ) + { + g_warning( "Exception in grabFocus()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_contains (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + try + { + accessibility::XAccessibleComponent* pComponent = getComponent( component ); + if( pComponent ) + return pComponent->containsPoint( translatePoint( pComponent, x, y, coord_type ) ); + } + catch( const uno::Exception &e ) + { + g_warning( "Exception in containsPoint()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static AtkObject * +component_wrapper_ref_accessible_at_point (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + try + { + accessibility::XAccessibleComponent* pComponent = getComponent( component ); + + if( pComponent ) + { + uno::Reference< accessibility::XAccessible > xAccessible; + xAccessible = pComponent->getAccessibleAtPoint( + translatePoint( pComponent, x, y, coord_type ) ); + +#ifdef ENABLE_TRACING + fprintf(stderr, "getAccessibleAtPoint( %u, %u ) returned %p\n", + x, y, xAccessible.get()); + + uno::Reference< accessibility::XAccessibleComponent > xComponent( + xAccessible->getAccessibleContext(), uno::UNO_QUERY ); + + if( xComponent.is() ) + { + awt::Rectangle rect = xComponent->getBounds(); + fprintf(stderr, "%p->getBounds() returned: ( %u, %u, %u, %u )\n", + xAccessible.get(), rect.X, rect.Y, rect.Width, rect.Height ); + } +#endif + + return atk_object_wrapper_ref( xAccessible ); + } + } + catch( const uno::Exception &e ) + { + g_warning( "Exception in getAccessibleAtPoint()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static void +component_wrapper_get_position (AtkComponent *component, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + try + { + accessibility::XAccessibleComponent* pComponent = getComponent( component ); + if( pComponent ) + { + awt::Point aPos; + + if( coord_type == ATK_XY_SCREEN ) + aPos = pComponent->getLocationOnScreen(); + else + aPos = pComponent->getLocation(); + + *x = aPos.X; + *y = aPos.Y; + +#ifdef ENABLE_TRACING + fprintf(stderr, "getLocation[OnScreen]() returned: ( %u, %u )\n", *x, *y ); +#endif + } + } + catch( const uno::Exception &e ) + { + g_warning( "Exception in getLocation[OnScreen]()" ); + } +} + +/*****************************************************************************/ + +static void +component_wrapper_get_size (AtkComponent *component, + gint *width, + gint *height) +{ + try + { + accessibility::XAccessibleComponent* pComponent = getComponent( component ); + if( pComponent ) + { + awt::Size aSize = pComponent->getSize(); + *width = aSize.Width; + *height = aSize.Height; + +#ifdef ENABLE_TRACING + fprintf(stderr, "getSize() returned: ( %u, %u )\n", *width, *height ); +#endif + } + } + catch( const uno::Exception &e ) + { + g_warning( "Exception in getSize()" ); + } +} + +/*****************************************************************************/ + +static void +component_wrapper_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + component_wrapper_get_position( component, x, y, coord_type ); + component_wrapper_get_size( component, width, height ); +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_extents (AtkComponent *, gint, gint, gint, gint, AtkCoordType) +{ + g_warning( "AtkComponent::set_extents unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_position (AtkComponent *, gint, gint, AtkCoordType) +{ + g_warning( "AtkComponent::set_position unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +component_wrapper_set_size (AtkComponent *, gint, gint) +{ + g_warning( "AtkComponent::set_size unimplementable" ); + return FALSE; +} + +/*****************************************************************************/ + +static AtkLayer +component_wrapper_get_layer (AtkComponent *component) +{ + AtkRole role = atk_object_get_role( ATK_OBJECT( component ) ); + AtkLayer layer = ATK_LAYER_WIDGET; + + switch (role) + { + case ATK_ROLE_POPUP_MENU: + case ATK_ROLE_MENU_ITEM: + case ATK_ROLE_CHECK_MENU_ITEM: + case ATK_ROLE_SEPARATOR: + case ATK_ROLE_LIST_ITEM: + layer = ATK_LAYER_POPUP; + break; + case ATK_ROLE_MENU: + { + AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); + if( atk_object_get_role( parent ) != ATK_ROLE_MENU_BAR ) + layer = ATK_LAYER_POPUP; + } + break; + + case ATK_ROLE_LIST: + { + AtkObject * parent = atk_object_get_parent( ATK_OBJECT( component ) ); + if( atk_object_get_role( parent ) == ATK_ROLE_COMBO_BOX ) + layer = ATK_LAYER_POPUP; + } + break; + + default: + ; + } + + return layer; +} + +/*****************************************************************************/ + +static gint +component_wrapper_get_mdi_zorder (AtkComponent *) +{ + // only needed for ATK_LAYER_MDI (not used) or ATK_LAYER_WINDOW (inherited from GAIL) + return G_MININT; +} + +/*****************************************************************************/ + +// This code is mostly stolen from libgail .. + +static guint +component_wrapper_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler) +{ + GSignalMatchType match_type; + gulong ret; + guint signal_id; + + match_type = (GSignalMatchType) (G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC); + signal_id = g_signal_lookup( "focus-event", ATK_TYPE_OBJECT ); + + ret = g_signal_handler_find( component, match_type, signal_id, 0, NULL, + (gpointer) &handler, NULL); + if (!ret) + { + return g_signal_connect_closure_by_id (component, + signal_id, 0, + g_cclosure_new ( + G_CALLBACK (handler), NULL, + (GClosureNotify) NULL), + FALSE); + } + else + { + return 0; + } +} + +/*****************************************************************************/ + +static void +component_wrapper_remove_focus_handler (AtkComponent *component, + guint handler_id) +{ + g_signal_handler_disconnect (component, handler_id); +} + +/*****************************************************************************/ + +} // extern "C" + +void +componentIfaceInit (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_focus_handler = component_wrapper_add_focus_handler; + iface->contains = component_wrapper_contains; + iface->get_extents = component_wrapper_get_extents; + iface->get_layer = component_wrapper_get_layer; + iface->get_mdi_zorder = component_wrapper_get_mdi_zorder; + iface->get_position = component_wrapper_get_position; + iface->get_size = component_wrapper_get_size; + iface->grab_focus = component_wrapper_grab_focus; + iface->ref_accessible_at_point = component_wrapper_ref_accessible_at_point; + iface->remove_focus_handler = component_wrapper_remove_focus_handler; + iface->set_extents = component_wrapper_set_extents; + iface->set_position = component_wrapper_set_position; + iface->set_size = component_wrapper_set_size; +} diff --git a/vcl/unx/gtk/a11y/atkeditabletext.cxx b/vcl/unx/gtk/a11y/atkeditabletext.cxx new file mode 100644 index 000000000000..c0399145b07c --- /dev/null +++ b/vcl/unx/gtk/a11y/atkeditabletext.cxx @@ -0,0 +1,202 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" +#include "atktextattributes.hxx" + +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/TextSegment.hpp> + +// #include <functional> +// #include <hash_map> + +#include <stdio.h> +#include <string.h> + +using namespace ::com::sun::star; + +static accessibility::XAccessibleEditableText* + getEditableText( AtkEditableText *pEditableText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pEditableText ); + if( pWrap ) + { + if( !pWrap->mpEditableText && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleEditableText::static_type(NULL) ); + pWrap->mpEditableText = reinterpret_cast< accessibility::XAccessibleEditableText * > (any.pReserved); + pWrap->mpEditableText->acquire(); + } + + return pWrap->mpEditableText; + } + + return NULL; +} + + +/*****************************************************************************/ + +extern "C" { + +static gboolean +editable_text_wrapper_set_run_attributes( AtkEditableText *text, + AtkAttributeSet *attribute_set, + gint nStartOffset, + gint nEndOffset) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + { + uno::Sequence< beans::PropertyValue > aAttributeList; + + if( attribute_set_map_to_property_values( attribute_set, aAttributeList ) ) + return pEditableText->setAttributes(nStartOffset, nEndOffset, aAttributeList); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in setAttributes()" ); + } + + return FALSE; +} + +static void +editable_text_wrapper_set_text_contents( AtkEditableText *text, + const gchar *string ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + { + rtl::OUString aString ( string, strlen(string), RTL_TEXTENCODING_UTF8 ); + pEditableText->setText( aString ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in setText()" ); + } +} + +static void +editable_text_wrapper_insert_text( AtkEditableText *text, + const gchar *string, + gint length, + gint *pos ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + { + rtl::OUString aString ( string, length, RTL_TEXTENCODING_UTF8 ); + if( pEditableText->insertText( aString, *pos ) ) + *pos += length; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in insertText()" ); + } +} + +static void +editable_text_wrapper_cut_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + pEditableText->cutText( start, end ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in cutText()" ); + } +} + +static void +editable_text_wrapper_delete_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + pEditableText->deleteText( start, end ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in deleteText()" ); + } +} + +static void +editable_text_wrapper_paste_text( AtkEditableText *text, + gint pos ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + pEditableText->pasteText( pos ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in pasteText()" ); + } +} + +static void +editable_text_wrapper_copy_text( AtkEditableText *text, + gint start, + gint end ) +{ + try { + accessibility::XAccessibleEditableText* pEditableText = getEditableText( text ); + if( pEditableText ) + pEditableText->copyText( start, end ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in copyText()" ); + } +} + +} // extern "C" + +void +editableTextIfaceInit (AtkEditableTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->set_text_contents = editable_text_wrapper_set_text_contents; + iface->insert_text = editable_text_wrapper_insert_text; + iface->copy_text = editable_text_wrapper_copy_text; + iface->cut_text = editable_text_wrapper_cut_text; + iface->delete_text = editable_text_wrapper_delete_text; + iface->paste_text = editable_text_wrapper_paste_text; + iface->set_run_attributes = editable_text_wrapper_set_run_attributes; +} diff --git a/vcl/unx/gtk/a11y/atkfactory.cxx b/vcl/unx/gtk/a11y/atkfactory.cxx new file mode 100644 index 000000000000..d2574f616539 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkfactory.cxx @@ -0,0 +1,183 @@ +/************************************************************************* + * + * 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 <plugins/gtk/gtkframe.hxx> +#include <vcl/window.hxx> +#include "atkwrapper.hxx" +#include "atkfactory.hxx" +#include "atkregistry.hxx" + +using namespace ::com::sun::star; + +extern "C" { + +/* + * Instances of this dummy object class are returned whenever we have to + * create an AtkObject, but can't touch the OOo object anymore since it + * is already disposed. + */ + +static AtkStateSet * +noop_wrapper_ref_state_set( AtkObject * ) +{ + AtkStateSet *state_set = atk_state_set_new(); + atk_state_set_add_state( state_set, ATK_STATE_DEFUNCT ); + return state_set; +} + +static void +atk_noop_object_wrapper_class_init(AtkNoOpObjectClass *klass) +{ + AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); + atk_class->ref_state_set = noop_wrapper_ref_state_set; +} + +static GType +atk_noop_object_wrapper_get_type(void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo typeInfo = + { + sizeof (AtkNoOpObjectClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) atk_noop_object_wrapper_class_init, + (GClassFinalizeFunc) NULL, + NULL, + sizeof (AtkObjectWrapper), + 0, + (GInstanceInitFunc) NULL, + NULL + } ; + + type = g_type_register_static (ATK_TYPE_OBJECT, "OOoAtkNoOpObj", &typeInfo, (GTypeFlags)0) ; + } + return type; +} + +AtkObject* +atk_noop_object_wrapper_new() +{ + AtkObject *accessible; + + accessible = (AtkObject *) g_object_new (atk_noop_object_wrapper_get_type(), NULL); + g_return_val_if_fail (accessible != NULL, NULL); + + accessible->role = ATK_ROLE_INVALID; + accessible->layer = ATK_LAYER_INVALID; + + return accessible; +} + +/* + * The wrapper factory + */ + +static GType +wrapper_factory_get_accessible_type(void) +{ + return atk_object_wrapper_get_type(); +} + +static AtkObject* +wrapper_factory_create_accessible( GObject *obj ) +{ + GtkWidget* parent_widget = gtk_widget_get_parent( GTK_WIDGET( obj ) ); + + // gail_container_real_remove_gtk tries to re-instanciate an accessible + // for a widget that is about to vanish .. + if( ! parent_widget ) + return atk_noop_object_wrapper_new(); + + GtkSalFrame* pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( parent_widget ) ); + g_return_val_if_fail( pFrame != NULL, NULL ); + + Window* pFrameWindow = pFrame->GetWindow(); + if( pFrameWindow ) + { + Window* pWindow = pFrameWindow; + + // skip accessible objects already exposed by the frame objects + if( WINDOW_BORDERWINDOW == pWindow->GetType() ) + pWindow = pFrameWindow->GetAccessibleChildWindow(0); + + if( pWindow ) + { + uno::Reference< accessibility::XAccessible > xAccessible = pWindow->GetAccessible(true); + if( xAccessible.is() ) + { + AtkObject *accessible = ooo_wrapper_registry_get( xAccessible ); + + if( accessible ) + g_object_ref( G_OBJECT(accessible) ); + else + accessible = atk_object_wrapper_new( xAccessible, gtk_widget_get_accessible(parent_widget) ); + + return accessible; + } + } + } + + return NULL; +} + +static void +wrapper_factory_class_init( AtkObjectFactoryClass *klass ) +{ + klass->create_accessible = wrapper_factory_create_accessible; + klass->get_accessible_type = wrapper_factory_get_accessible_type; +} + +GType +wrapper_factory_get_type (void) +{ + static GType t = 0; + + if (!t) { + static const GTypeInfo tinfo = + { + sizeof (AtkObjectFactoryClass), + NULL, NULL, (GClassInitFunc) wrapper_factory_class_init, + NULL, NULL, sizeof (AtkObjectFactory), 0, NULL, NULL + }; + + t = g_type_register_static ( + ATK_TYPE_OBJECT_FACTORY, "OOoAtkObjectWrapperFactory", + &tinfo, (GTypeFlags) 0); + } + + return t; +} + +} // extern C + diff --git a/vcl/unx/gtk/a11y/atkfactory.hxx b/vcl/unx/gtk/a11y/atkfactory.hxx new file mode 100644 index 000000000000..82be08cfad1b --- /dev/null +++ b/vcl/unx/gtk/a11y/atkfactory.hxx @@ -0,0 +1,41 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_FACTORY_HXX__ +#define __ATK_FACTORY_HXX__ + +#include <atk/atk.h> + +#define OOO_TYPE_WRAPPER_FACTORY wrapper_factory_get_type() + +extern "C" { + +GType wrapper_factory_get_type (void); + +} // extern "C" + +#endif diff --git a/vcl/unx/gtk/a11y/atkhypertext.cxx b/vcl/unx/gtk/a11y/atkhypertext.cxx new file mode 100644 index 000000000000..90d735890655 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkhypertext.cxx @@ -0,0 +1,291 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> + +#include <stdio.h> + +using namespace ::com::sun::star; + + +// ---------------------- AtkHyperlink ---------------------- + +typedef struct { + AtkHyperlink atk_hyper_link; + + uno::Reference< accessibility::XAccessibleHyperlink > xLink; +} HyperLink; + +static uno::Reference< accessibility::XAccessibleHyperlink > + getHyperlink( AtkHyperlink *pHyperlink ) +{ + HyperLink *pLink = (HyperLink *) pHyperlink; + return pLink->xLink; +} + +static GObjectClass *hyper_parent_class = NULL; + +extern "C" { + +static void +hyper_link_finalize (GObject *obj) +{ + HyperLink *hl = (HyperLink *) obj; + hl->xLink.clear(); + hyper_parent_class->finalize (obj); +} + +static gchar * +hyper_link_get_uri( AtkHyperlink *pLink, + gint i ) +{ + try { + uno::Any aAny = getHyperlink( pLink )->getAccessibleActionObject( i ); + rtl::OUString aUri = aAny.get< rtl::OUString > (); + return OUStringToGChar(aUri); + } + catch(const uno::Exception& e) { + g_warning( "Exception in hyper_link_get_uri" ); + } + return NULL; +} + +static AtkObject * +hyper_link_get_object( AtkHyperlink *pLink, + gint i) +{ + try { + uno::Any aAny = getHyperlink( pLink )->getAccessibleActionObject( i ); + uno::Reference< accessibility::XAccessible > xObj( aAny, uno::UNO_QUERY_THROW ); + return atk_object_wrapper_ref( xObj ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in hyper_link_get_object" ); + } + return NULL; +} +static gint +hyper_link_get_end_index( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getEndIndex(); + } + catch(const uno::Exception& e) { + } + return -1; +} +static gint +hyper_link_get_start_index( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getStartIndex(); + } + catch(const uno::Exception& e) { + } + return -1; +} +static gboolean +hyper_link_is_valid( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->isValid(); + } + catch(const uno::Exception& e) { + } + return FALSE; +} +static gint +hyper_link_get_n_anchors( AtkHyperlink *pLink ) +{ + try { + return getHyperlink( pLink )->getAccessibleActionCount(); + } + catch(const uno::Exception& e) { + } + return 0; +} + +static guint +hyper_link_link_state( AtkHyperlink * ) +{ + g_warning( "FIXME: hyper_link_link_state unimplemented" ); + return 0; +} +static gboolean +hyper_link_is_selected_link( AtkHyperlink * ) +{ + g_warning( "FIXME: hyper_link_is_selected_link unimplemented" ); + return FALSE; +} + +static void +hyper_link_class_init (AtkHyperlinkClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = hyper_link_finalize; + + hyper_parent_class = (GObjectClass *)g_type_class_peek_parent (klass); + + klass->get_uri = hyper_link_get_uri; + klass->get_object = hyper_link_get_object; + klass->get_end_index = hyper_link_get_end_index; + klass->get_start_index = hyper_link_get_start_index; + klass->is_valid = hyper_link_is_valid; + klass->get_n_anchors = hyper_link_get_n_anchors; + klass->link_state = hyper_link_link_state; + klass->is_selected_link = hyper_link_is_selected_link; +} + +static GType +hyper_link_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = { + sizeof (AtkHyperlinkClass), + NULL, /* base init */ + NULL, /* base finalize */ + (GClassInitFunc) hyper_link_class_init, + NULL, /* class finalize */ + NULL, /* class data */ + sizeof (HyperLink), /* instance size */ + 0, /* nb preallocs */ + NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = { + (GInterfaceInitFunc) actionIfaceInit, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (ATK_TYPE_HYPERLINK, + "OOoAtkObjHyperLink", &tinfo, + (GTypeFlags)0); + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + } + + return type; +} + +// ---------------------- AtkHyperText ---------------------- + +static accessibility::XAccessibleHypertext* + getHypertext( AtkHypertext *pHypertext ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pHypertext ); + if( pWrap ) + { + if( !pWrap->mpHypertext && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleHypertext::static_type(NULL) ); + pWrap->mpHypertext = reinterpret_cast< accessibility::XAccessibleHypertext * > (any.pReserved); + pWrap->mpHypertext->acquire(); + } + + return pWrap->mpHypertext; + } + + return NULL; +} + + +static AtkHyperlink * +hypertext_get_link( AtkHypertext *hypertext, + gint link_index) +{ + try { + accessibility::XAccessibleHypertext* pHypertext = getHypertext( hypertext ); + if( pHypertext ) + { + HyperLink *pLink = (HyperLink *)g_object_new( hyper_link_get_type(), NULL ); + pLink->xLink = pHypertext->getHyperLink( link_index ); + if( !pLink->xLink.is() ) { + g_object_unref( G_OBJECT( pLink ) ); + pLink = NULL; + } + return ATK_HYPERLINK( pLink ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getHyperLink()" ); + } + + return NULL; +} + +static gint +hypertext_get_n_links( AtkHypertext *hypertext ) +{ + try { + accessibility::XAccessibleHypertext* pHypertext = getHypertext( hypertext ); + if( pHypertext ) + return pHypertext->getHyperLinkCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getHyperLinkCount()" ); + } + + return 0; +} + +static gint +hypertext_get_link_index( AtkHypertext *hypertext, + gint index) +{ + try { + accessibility::XAccessibleHypertext* pHypertext = getHypertext( hypertext ); + if( pHypertext ) + return pHypertext->getHyperLinkIndex( index ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getHyperLinkIndex()" ); + } + + return 0; +} + +} // extern "C" + +void +hypertextIfaceInit (AtkHypertextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_link = hypertext_get_link; + iface->get_n_links = hypertext_get_n_links; + iface->get_link_index = hypertext_get_link_index; +} diff --git a/vcl/unx/gtk/a11y/atkimage.cxx b/vcl/unx/gtk/a11y/atkimage.cxx new file mode 100644 index 000000000000..b48c59555a29 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkimage.cxx @@ -0,0 +1,138 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleImage.hpp> + +#include <stdio.h> + +using namespace ::com::sun::star; + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( rtl::OUString rString ) +{ + static const int nMax = 10; + static rtl::OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = rtl::OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); + return aUgly[ nIdx ]; +} + +static accessibility::XAccessibleImage* + getImage( AtkImage *pImage ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pImage ); + if( pWrap ) + { + if( !pWrap->mpImage && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleImage::static_type(NULL) ); + pWrap->mpImage = reinterpret_cast< accessibility::XAccessibleImage * > (any.pReserved); + pWrap->mpImage->acquire(); + } + + return pWrap->mpImage; + } + + return NULL; +} + +extern "C" { + +static G_CONST_RETURN gchar * +image_get_image_description( AtkImage *image ) +{ + try { + accessibility::XAccessibleImage* pImage = getImage( image ); + if( pImage ) + return getAsConst( pImage->getAccessibleImageDescription() ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleImageDescription()" ); + } + + return NULL; +} + +static void +image_get_image_position( AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type ) +{ + *x = *y = 0; + if( ATK_IS_COMPONENT( image ) ) + atk_component_get_position( ATK_COMPONENT( image ), x, y, coord_type ); + else + g_warning( "FIXME: no image position information" ); +} + +static void +image_get_image_size( AtkImage *image, + gint *width, + gint *height ) +{ + *width = 0; + *height = 0; + try { + accessibility::XAccessibleImage* pImage = getImage( image ); + if( pImage ) + { + *width = pImage->getAccessibleImageWidth(); + *height = pImage->getAccessibleImageHeight(); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleImageHeight() or Width" ); + } +} + +static gboolean +image_set_image_description( AtkImage *, const gchar * ) +{ + g_warning ("FIXME: no set image description"); + return FALSE; +} + +} // extern "C" + +void +imageIfaceInit (AtkImageIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->set_image_description = image_set_image_description; + iface->get_image_description = image_get_image_description; + iface->get_image_position = image_get_image_position; + iface->get_image_size = image_get_image_size; +} diff --git a/vcl/unx/gtk/a11y/atklistener.cxx b/vcl/unx/gtk/a11y/atklistener.cxx new file mode 100644 index 000000000000..e02478ac8ad8 --- /dev/null +++ b/vcl/unx/gtk/a11y/atklistener.cxx @@ -0,0 +1,537 @@ +/************************************************************************* + * + * 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/accessibility/TextSegment.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> +#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> + +#include "atklistener.hxx" +#include "atkwrapper.hxx" + +#include <rtl/ref.hxx> +#include <stdio.h> + +using namespace com::sun::star; + + +#define CSTRING_FROM_ANY(i) rtl::OUStringToOString( i.get< rtl::OUString >(), RTL_TEXTENCODING_UTF8 ).getStr() + +AtkListener::AtkListener( AtkObjectWrapper* pWrapper ) : mpWrapper( pWrapper ) +{ + if( mpWrapper ) + { + g_object_ref( mpWrapper ); + updateChildList( mpWrapper->mpContext ); + } +} + +AtkListener::~AtkListener() +{ + if( mpWrapper ) + g_object_unref( mpWrapper ); +} + +/*****************************************************************************/ + +AtkStateType mapState( const uno::Any &rAny ) +{ + sal_Int16 nState = accessibility::AccessibleStateType::INVALID; + rAny >>= nState; + return mapAtkState( nState ); +} + +/*****************************************************************************/ + +// XEventListener implementation +void AtkListener::disposing( const lang::EventObject& ) throw (uno::RuntimeException) +{ + if( mpWrapper ) + { + AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); + + // Release all interface references to avoid shutdown problems with + // global mutex + atk_object_wrapper_dispose( mpWrapper ); + + // This is an equivalent to a state change to DEFUNC(T). + atk_object_notify_state_change( atk_obj, ATK_STATE_DEFUNCT, TRUE ); + + if( atk_get_focus_object() == atk_obj ) + atk_focus_tracker_notify( NULL ); + + // Release the wrapper object so that it can vanish .. + g_object_unref( mpWrapper ); + mpWrapper = NULL; + } +} + +/*****************************************************************************/ + +static AtkObject *getObjFromAny( const uno::Any &rAny ) +{ + uno::Reference< accessibility::XAccessible > xAccessible; + rAny >>= xAccessible; + return xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : NULL; +} + +/*****************************************************************************/ + +// Updates the child list held to provide the old IndexInParent on children_changed::remove +void AtkListener::updateChildList(accessibility::XAccessibleContext* pContext) +{ + m_aChildList.clear(); + + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = pContext->getAccessibleStateSet(); + if( xStateSet.is() + && !xStateSet->contains(accessibility::AccessibleStateType::DEFUNC) + && !xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS) ) + { + sal_Int32 nChildren = pContext->getAccessibleChildCount(); + m_aChildList.resize(nChildren); + for(sal_Int32 n = 0; n < nChildren; n++) + { + m_aChildList[n] = pContext->getAccessibleChild(n); + OSL_ASSERT(m_aChildList[n].is()); + } + } +} + +/*****************************************************************************/ + +void AtkListener::handleChildAdded( + const uno::Reference< accessibility::XAccessibleContext >& rxParent, + const uno::Reference< accessibility::XAccessible>& rxAccessible) +{ + AtkObject * pChild = atk_object_wrapper_ref( rxAccessible ); + + if( pChild ) + { + updateChildList(rxParent.get()); + + atk_object_wrapper_add_child( mpWrapper, pChild, + atk_object_get_index_in_parent( pChild )); + + g_object_unref( pChild ); + } +} + +/*****************************************************************************/ + +void AtkListener::handleChildRemoved( + const uno::Reference< accessibility::XAccessibleContext >& rxParent, + const uno::Reference< accessibility::XAccessible>& rxChild) +{ + sal_Int32 nIndex = -1; + + // Locate the child in the children list + size_t n, nmax = m_aChildList.size(); + for( n = 0; n < nmax; ++n ) + { + if( rxChild == m_aChildList[n] ) + { + nIndex = n; + break; + } + } + + // FIXME: two problems here: + // a) we get child-removed events for objects that are no real childs + // in the accessibility hierarchy or have been removed before due to + // some child removing batch. + // b) spi_atk_bridge_signal_listener ignores the given parameters + // for children_changed events and always asks the parent for the + // 0. child, which breaks somehow on vanishing list boxes. + // Ignoring "remove" events for objects not in the m_aChildList + // for now. + if( nIndex >= 0 ) + { + updateChildList(rxParent.get()); + + AtkObject * pChild = atk_object_wrapper_ref( rxChild, false ); + if( pChild ) + { + atk_object_wrapper_remove_child( mpWrapper, pChild, nIndex ); + g_object_unref( pChild ); + } + } +} + +/*****************************************************************************/ + +void AtkListener::handleInvalidateChildren( + const uno::Reference< accessibility::XAccessibleContext >& rxParent) +{ + // Send notifications for all previous children + size_t n = m_aChildList.size(); + while( n-- > 0 ) + { + if( m_aChildList[n].is() ) + { + AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n], false ); + if( pChild ) + { + atk_object_wrapper_remove_child( mpWrapper, pChild, n ); + g_object_unref( pChild ); + } + } + } + + updateChildList(rxParent.get()); + + // Send notifications for all new children + size_t nmax = m_aChildList.size(); + for( n = 0; n < nmax; ++n ) + { + if( m_aChildList[n].is() ) + { + AtkObject * pChild = atk_object_wrapper_ref( m_aChildList[n] ); + + if( pChild ) + { + atk_object_wrapper_add_child( mpWrapper, pChild, n ); + g_object_unref( pChild ); + } + } + } +} + +/*****************************************************************************/ + +static uno::Reference< accessibility::XAccessibleContext > +getAccessibleContextFromSource( const uno::Reference< uno::XInterface >& rxSource ) +{ + uno::Reference< accessibility::XAccessibleContext > xContext(rxSource, uno::UNO_QUERY); + if( ! xContext.is() ) + { + g_warning( "ERROR: Event source does not implement XAccessibleContext" ); + + // Second try - query for XAccessible, which should give us access to + // XAccessibleContext. + uno::Reference< accessibility::XAccessible > xAccessible(rxSource, uno::UNO_QUERY); + if( xAccessible.is() ) + xContext = xAccessible->getAccessibleContext(); + } + + return xContext; +} + +/*****************************************************************************/ + +// XAccessibleEventListener +void AtkListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) throw( uno::RuntimeException ) +{ + if( !mpWrapper ) + return; + + AtkObject *atk_obj = ATK_OBJECT( mpWrapper ); + + switch( aEvent.EventId ) + { + // AtkObject signals: + // Hierarchy signals + case accessibility::AccessibleEventId::CHILD: + { + uno::Reference< accessibility::XAccessibleContext > xParent; + uno::Reference< accessibility::XAccessible > xChild; + + xParent = getAccessibleContextFromSource(aEvent.Source); + g_return_if_fail( xParent.is() ); + + if( aEvent.OldValue >>= xChild ) + handleChildRemoved(xParent, xChild); + + if( aEvent.NewValue >>= xChild ) + handleChildAdded(xParent, xChild); + } + break; + + case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: + { + uno::Reference< accessibility::XAccessibleContext > xParent; + + xParent = getAccessibleContextFromSource(aEvent.Source); + g_return_if_fail( xParent.is() ); + + handleInvalidateChildren(xParent); + } + break; + + case accessibility::AccessibleEventId::NAME_CHANGED: + { + rtl::OUString aName; + if( aEvent.NewValue >>= aName ) + { + atk_object_set_name(atk_obj, + rtl::OUStringToOString(aName, RTL_TEXTENCODING_UTF8).getStr()); + } + } + break; + + case accessibility::AccessibleEventId::DESCRIPTION_CHANGED: + { + rtl::OUString aDescription; + if( aEvent.NewValue >>= aDescription ) + { + atk_object_set_description(atk_obj, + rtl::OUStringToOString(aDescription, RTL_TEXTENCODING_UTF8).getStr()); + } + } + break; + + case accessibility::AccessibleEventId::STATE_CHANGED: + { + AtkStateType eOldState = mapState( aEvent.OldValue ); + AtkStateType eNewState = mapState( aEvent.NewValue ); + + gboolean bState = eNewState != ATK_STATE_INVALID; + AtkStateType eRealState = bState ? eNewState : eOldState; + + atk_object_notify_state_change( atk_obj, eRealState, bState ); + break; + } + + case accessibility::AccessibleEventId::BOUNDRECT_CHANGED: + +#ifdef HAS_ATKRECTANGLE + if( ATK_IS_COMPONENT( atk_obj ) ) + { + AtkRectangle rect; + + atk_component_get_extents( ATK_COMPONENT( atk_obj ), + &rect.x, + &rect.y, + &rect.width, + &rect.height, + ATK_XY_SCREEN ); + + g_signal_emit_by_name( atk_obj, "bounds_changed", &rect ); + } + else + g_warning( "bounds_changed event for object not implementing AtkComponent\n"); +#endif + + break; + + case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED: + g_signal_emit_by_name( atk_obj, "visible-data-changed" ); + break; + + case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + g_signal_emit_by_name( atk_obj, "active-descendant-changed", pChild ); + g_object_unref( pChild ); + } + break; + } + + // --> OD 2009-05-26 #i92103# + case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + AtkStateType eExpandedState = ATK_STATE_EXPANDED; + atk_object_notify_state_change( pChild, eExpandedState, true ); + g_object_unref( pChild ); + } + break; + } + + case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED: + { + AtkObject *pChild = getObjFromAny( aEvent.NewValue ); + if( pChild ) + { + AtkStateType eExpandedState = ATK_STATE_EXPANDED; + atk_object_notify_state_change( pChild, eExpandedState, false ); + g_object_unref( pChild ); + } + break; + } + // <-- + + // AtkAction signals ... + case accessibility::AccessibleEventId::ACTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-actions"); + break; + + // AtkText + case accessibility::AccessibleEventId::CARET_CHANGED: + { + sal_Int32 nPos=0; + aEvent.NewValue >>= nPos; + g_signal_emit_by_name( atk_obj, "text_caret_moved", nPos ); + break; + } + case accessibility::AccessibleEventId::TEXT_CHANGED: + { + // TESTME: and remove this comment: + // cf. comphelper/source/misc/accessibletexthelper.cxx (implInitTextChangedEvent) + accessibility::TextSegment aDeletedText; + accessibility::TextSegment aInsertedText; + + // TODO: when GNOME starts to send "update" kind of events, change + // we need to re-think this implementation as well + if( aEvent.OldValue >>= aDeletedText ) + { + /* Remember the text segment here to be able to return removed text in get_text(). + * This is clearly a hack to be used until appropriate API exists in atk to pass + * the string value directly or we find a compelling reason to start caching the + * UTF-8 converted strings in the atk wrapper object. + */ + + g_object_set_data( G_OBJECT(atk_obj), "ooo::text_changed::delete", &aDeletedText); + + g_signal_emit_by_name( atk_obj, "text_changed::delete", + (gint) aDeletedText.SegmentStart, + (gint)( aDeletedText.SegmentEnd - aDeletedText.SegmentStart ) ); + + g_object_steal_data( G_OBJECT(atk_obj), "ooo::text_changed::delete" ); + } + + if( aEvent.NewValue >>= aInsertedText ) + g_signal_emit_by_name( atk_obj, "text_changed::insert", + (gint) aInsertedText.SegmentStart, + (gint)( aInsertedText.SegmentEnd - aInsertedText.SegmentStart ) ); + break; + } + + case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED: + { + g_signal_emit_by_name( atk_obj, "text-selection-changed" ); + break; + } + + case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED: + g_signal_emit_by_name( atk_obj, "text-attributes-changed" ); + break; + + // AtkValue + case accessibility::AccessibleEventId::VALUE_CHANGED: + g_object_notify( G_OBJECT( atk_obj ), "accessible-value" ); + break; + + case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED: + case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED: + case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED: + case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED: + case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED: + case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED: + // FIXME: ask Bill how Atk copes with this little lot ... + break; + + // AtkTable + case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED: + { + accessibility::AccessibleTableModelChange aChange; + aEvent.NewValue >>= aChange; + + sal_Int32 nRowsChanged = aChange.LastRow - aChange.FirstRow + 1; + sal_Int32 nColumnsChanged = aChange.LastColumn - aChange.FirstColumn + 1; + + static const struct { + const char *row; + const char *col; + } aSignalNames[] = + { + { NULL, NULL }, // dummy + { "row_inserted", "column_inserted" }, // INSERT = 1 + { "row_deleted", "column_deleted" } // DELETE = 2 + }; + switch( aChange.Type ) + { + case accessibility::AccessibleTableModelChangeType::INSERT: + case accessibility::AccessibleTableModelChangeType::DELETE: + if( nRowsChanged > 0 ) + g_signal_emit_by_name( G_OBJECT( atk_obj ), + aSignalNames[aChange.Type].row, + aChange.FirstRow, nRowsChanged ); + if( nColumnsChanged > 0 ) + g_signal_emit_by_name( G_OBJECT( atk_obj ), + aSignalNames[aChange.Type].col, + aChange.FirstColumn, nColumnsChanged ); + break; + + case accessibility::AccessibleTableModelChangeType::UPDATE: + // This is not really a model change, is it ? + break; + default: + g_warning( "TESTME: unusual table model change %d\n", aChange.Type ); + break; + } + g_signal_emit_by_name( G_OBJECT( atk_obj ), "model-changed" ); + break; + } + + case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-header"); + break; + + case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-caption"); + break; + + case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-column-description"); + break; + + case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-description"); + break; + + case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-row-header"); + break; + + case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-table-summary"); + break; + + case accessibility::AccessibleEventId::SELECTION_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "selection_changed"); + break; + + case accessibility::AccessibleEventId::HYPERTEXT_CHANGED: + g_signal_emit_by_name( G_OBJECT( atk_obj ), "property_change::accessible-hypertext-offset"); + break; + + default: + g_warning( "Unknown event notification %d", aEvent.EventId ); + break; + } +} diff --git a/vcl/unx/gtk/a11y/atklistener.hxx b/vcl/unx/gtk/a11y/atklistener.hxx new file mode 100644 index 000000000000..d2889caa3e24 --- /dev/null +++ b/vcl/unx/gtk/a11y/atklistener.hxx @@ -0,0 +1,79 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef _ATK_LISTENER_HXX_ +#define _ATK_LISTENER_HXX_ + +#include <com/sun/star/accessibility/XAccessibleEventListener.hpp> +#include <cppuhelper/implbase1.hxx> + +#include <vector> + +#include "atkwrapper.hxx" + +typedef std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > > AccessibleVector; + +class AtkListener : public ::cppu::WeakImplHelper1< ::com::sun::star::accessibility::XAccessibleEventListener > +{ +public: + AtkListener(AtkObjectWrapper * pWrapper); + + // XEventListener + virtual void disposing( const ::com::sun::star::lang::EventObject& Source ) + throw (::com::sun::star::uno::RuntimeException); + + // XAccessibleEventListener + virtual void notifyEvent( const ::com::sun::star::accessibility::AccessibleEventObject& aEvent ) + throw( ::com::sun::star::uno::RuntimeException ); + + AtkObjectWrapper *mpWrapper; + AccessibleVector m_aChildList; + +private: + + virtual ~AtkListener(); + + // Updates the child list held to provide the old IndexInParent on children_changed::remove + void updateChildList(::com::sun::star::accessibility::XAccessibleContext* pContext); + + // Process CHILD_EVENT notifications with a new child added + void handleChildAdded( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& rxParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible>& rxChild); + + // Process CHILD_EVENT notifications with a child removed + void handleChildRemoved( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& rxParent, + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible>& rxChild); + + // Process INVALIDATE_ALL_CHILDREN notification + void handleInvalidateChildren( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext >& rxParent); +}; + +#endif /* _ATK_LISTENER_HXX_ */ + diff --git a/vcl/unx/gtk/a11y/atkregistry.cxx b/vcl/unx/gtk/a11y/atkregistry.cxx new file mode 100644 index 000000000000..81ec22dc4ce1 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkregistry.cxx @@ -0,0 +1,71 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#include "atkregistry.hxx" + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +static GHashTable *uno_to_gobject = NULL; + +/*****************************************************************************/ + +AtkObject * +ooo_wrapper_registry_get(const Reference< XAccessible >& rxAccessible) +{ + if( uno_to_gobject ) + { + gpointer cached = + g_hash_table_lookup(uno_to_gobject, (gpointer) rxAccessible.get()); + + if( cached ) + return ATK_OBJECT( cached ); + } + + return NULL; +} + +/*****************************************************************************/ + +void +ooo_wrapper_registry_add(const Reference< XAccessible >& rxAccessible, AtkObject *obj) +{ + if( !uno_to_gobject ) + uno_to_gobject = g_hash_table_new (NULL, NULL); + + g_hash_table_insert( uno_to_gobject, (gpointer) rxAccessible.get(), obj ); +} + +/*****************************************************************************/ + +void +ooo_wrapper_registry_remove(XAccessible *pAccessible) +{ + if( uno_to_gobject ) + g_hash_table_remove( uno_to_gobject, (gpointer) pAccessible ); +} + diff --git a/vcl/unx/gtk/a11y/atkregistry.hxx b/vcl/unx/gtk/a11y/atkregistry.hxx new file mode 100644 index 000000000000..f4de3b2e4c1e --- /dev/null +++ b/vcl/unx/gtk/a11y/atkregistry.hxx @@ -0,0 +1,40 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_REGISTRY_HXX__ +#define __ATK_REGISTRY_HXX__ + +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <atk/atk.h> + +AtkObject * ooo_wrapper_registry_get(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible); + +void ooo_wrapper_registry_add(const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible, AtkObject *obj); + +void ooo_wrapper_registry_remove(::com::sun::star::accessibility::XAccessible *pAccessible); + +#endif // __ATK_REGISTRY_HXX_ diff --git a/vcl/unx/gtk/a11y/atkselection.cxx b/vcl/unx/gtk/a11y/atkselection.cxx new file mode 100644 index 000000000000..172faac4c704 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkselection.cxx @@ -0,0 +1,195 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> + +#include <stdio.h> + +using namespace ::com::sun::star; + +static accessibility::XAccessibleSelection* + getSelection( AtkSelection *pSelection ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pSelection ); + if( pWrap ) + { + if( !pWrap->mpSelection && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleSelection::static_type(NULL) ); + pWrap->mpSelection = reinterpret_cast< accessibility::XAccessibleSelection * > (any.pReserved); + pWrap->mpSelection->acquire(); + } + + return pWrap->mpSelection; + } + + return NULL; +} + +extern "C" { + +static gboolean +selection_add_selection( AtkSelection *selection, + gint i ) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + { + pSelection->selectAccessibleChild( i ); + return TRUE; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in selectAccessibleChild()" ); + } + + return FALSE; +} + +static gboolean +selection_clear_selection( AtkSelection *selection ) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + { + pSelection->clearAccessibleSelection(); + return TRUE; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in selectAccessibleChild()" ); + } + + return FALSE; +} + +static AtkObject* +selection_ref_selection( AtkSelection *selection, + gint i ) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + return atk_object_wrapper_ref( pSelection->getSelectedAccessibleChild( i ) ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleChild()" ); + } + + return NULL; +} + +static gint +selection_get_selection_count( AtkSelection *selection) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + return pSelection->getSelectedAccessibleChildCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return -1; +} + +static gboolean +selection_is_child_selected( AtkSelection *selection, + gint i) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + return pSelection->isAccessibleChildSelected( i ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +static gboolean +selection_remove_selection( AtkSelection *selection, + gint i ) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + { + pSelection->deselectAccessibleChild( i ); + return TRUE; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +static gboolean +selection_select_all_selection( AtkSelection *selection) +{ + try { + accessibility::XAccessibleSelection* pSelection = getSelection( selection ); + if( pSelection ) + { + pSelection->selectAllAccessibleChildren(); + return TRUE; + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleChildCount()" ); + } + + return FALSE; +} + +} // extern "C" + +void +selectionIfaceInit( AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = selection_add_selection; + iface->clear_selection = selection_clear_selection; + iface->ref_selection = selection_ref_selection; + iface->get_selection_count = selection_get_selection_count; + iface->is_child_selected = selection_is_child_selected; + iface->remove_selection = selection_remove_selection; + iface->select_all_selection = selection_select_all_selection; +} diff --git a/vcl/unx/gtk/a11y/atktable.cxx b/vcl/unx/gtk/a11y/atktable.cxx new file mode 100644 index 000000000000..78571ff11c34 --- /dev/null +++ b/vcl/unx/gtk/a11y/atktable.cxx @@ -0,0 +1,721 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleTable.hpp> + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +using namespace ::com::sun::star; + +static inline AtkObject * +atk_object_wrapper_conditional_ref( const uno::Reference< accessibility::XAccessible >& rxAccessible ) +{ +#ifdef ENABLE_TRACING + fprintf( stderr, ": %p\n", rxAccessible.get() ); +#endif + + if( rxAccessible.is() ) + return atk_object_wrapper_ref( rxAccessible ); + + return NULL; +} + +/*****************************************************************************/ + +// FIXME +static G_CONST_RETURN gchar * +getAsConst( rtl::OUString rString ) +{ + static const int nMax = 10; + static rtl::OString aUgly[nMax]; + static int nIdx = 0; + nIdx = (nIdx + 1) % nMax; + aUgly[nIdx] = rtl::OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); + return aUgly[ nIdx ]; +} + +/*****************************************************************************/ + +static accessibility::XAccessibleTable* + getTable( AtkTable *pTable ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pTable ); + if( pWrap ) + { + if( !pWrap->mpTable && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTable::static_type(NULL) ); + pWrap->mpTable = reinterpret_cast< accessibility::XAccessibleTable * > (any.pReserved); + pWrap->mpTable->acquire(); + } + + return pWrap->mpTable; + } + + return NULL; +} + +/*****************************************************************************/ + +extern "C" { + +static AtkObject* +table_wrapper_ref_at (AtkTable *table, + gint row, + gint column) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleCellAt( %u, %u ) returns", row, column ); + + if( column >= 255 ) + fprintf(stderr, "getAccessibleCellAt( %u, %u ) returns", row, column ); + +#endif + + if( pTable ) + return atk_object_wrapper_conditional_ref( pTable->getAccessibleCellAt( row, column ) ); + } + + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleCellAt()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_index_at (AtkTable *table, + gint row, + gint column) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleIndex( %u, %u ) returns %u\n", + row, column, pTable->getAccessibleIndex( row, column ) ); +#endif + + if( pTable ) + return pTable->getAccessibleIndex( row, column ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleIndex()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_column_at_index (AtkTable *table, + gint nIndex) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleColumn( %u ) returns %u\n", + nIndex, pTable->getAccessibleColumn( nIndex ) ); +#endif + + if( pTable ) + return pTable->getAccessibleColumn( nIndex ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleColumn()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_row_at_index( AtkTable *table, + gint nIndex ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleRow( %u ) returns %u\n", + nIndex, pTable->getAccessibleRow( nIndex ) ); +#endif + + if( pTable ) + return pTable->getAccessibleRow( nIndex ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleRow()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_n_columns( AtkTable *table ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "XAccessibleTable::getAccessibleColumnCount returns %u\n", + pTable->getAccessibleColumnCount() ); +#endif + + if( pTable ) + return pTable->getAccessibleColumnCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleColumnCount()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_n_rows( AtkTable *table ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleRowCount() returns %u\n", + pTable->getAccessibleRowCount() ); +#endif + + if( pTable ) + return pTable->getAccessibleRowCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleRowCount()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_column_extent_at( AtkTable *table, + gint row, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleColumnExtentAt( %u, %u ) returns %u\n", + row, column, pTable->getAccessibleColumnExtentAt( row, column ) ); +#endif + + if( pTable ) + return pTable->getAccessibleColumnExtentAt( row, column ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleColumnExtentAt()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_row_extent_at( AtkTable *table, + gint row, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleRowExtentAt( %u, %u ) returns %u\n", + row, column, pTable->getAccessibleRowExtentAt( row, column ) ); +#endif + + if( pTable ) + return pTable->getAccessibleRowExtentAt( row, column ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleRowExtentAt()" ); + } + + return -1; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_caption( AtkTable *table ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleCaption() returns" ); +#endif + + if( pTable ) + return atk_object_wrapper_conditional_ref( pTable->getAccessibleCaption() ); + } + + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleCaption()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +table_wrapper_get_row_description( AtkTable *table, + gint row ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleRowDescription( %u ) returns %s\n", + row, getAsConst( pTable->getAccessibleRowDescription( row ) ) ); +#endif + + if( pTable ) + return getAsConst( pTable->getAccessibleRowDescription( row ) ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleRowDescription()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +table_wrapper_get_column_description( AtkTable *table, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleColumnDescription( %u ) returns %s\n", + column, getAsConst( pTable->getAccessibleColumnDescription( column ) ) ); +#endif + + if( pTable ) + return getAsConst( pTable->getAccessibleColumnDescription( column ) ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleColumnDescription()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_row_header( AtkTable *table, + gint row ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + if( pTable ) + { + uno::Reference< accessibility::XAccessibleTable > xRowHeaders( pTable->getAccessibleRowHeaders() ); + +#ifdef ENABLE_TRACING + if( xRowHeaders.is() ) + fprintf(stderr, "getAccessibleRowHeader( %u )->getAccessibleCellAt( 0, %u ) returns", + row, row ); + else + fprintf(stderr, "getAccessibleRowHeader( %u ) returns %p\n", row, xRowHeaders.get() ); +#endif + + if( xRowHeaders.is() ) + return atk_object_wrapper_conditional_ref( xRowHeaders->getAccessibleCellAt( row, 0 ) ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleRowHeaders()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_column_header( AtkTable *table, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + + if( pTable ) + { + uno::Reference< accessibility::XAccessibleTable > xColumnHeaders( pTable->getAccessibleColumnHeaders() ); + +#ifdef ENABLE_TRACING + if( xColumnHeaders.is() ) + fprintf(stderr, "getAccessibleColumnHeader( %u )->getAccessibleCellAt( 0, %u ) returns", + column, column ); + else + fprintf(stderr, "getAccessibleColumnHeader( %u ) returns %p\n", column, xColumnHeaders.get() ); +#endif + + if( xColumnHeaders.is() ) + return atk_object_wrapper_conditional_ref( xColumnHeaders->getAccessibleCellAt( 0, column ) ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleColumnHeaders()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static AtkObject * +table_wrapper_get_summary( AtkTable *table ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getAccessibleSummary() returns" ); +#endif + + if( pTable ) + { + // FIXME: Summary ?? +// AtkObject* summary; + return atk_object_wrapper_conditional_ref( pTable->getAccessibleSummary() ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleSummary()" ); + } + + return NULL; +} + +/*****************************************************************************/ + +static gint +convertToGIntArray( const uno::Sequence< ::sal_Int32 >& aSequence, gint **pSelected ) +{ + if( aSequence.getLength() ) + { + *pSelected = g_new( gint, aSequence.getLength() ); + + for( sal_Int32 i = 0; i < aSequence.getLength(); i++ ) + (*pSelected) [i] = aSequence[i]; + } + + return aSequence.getLength(); +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_selected_columns( AtkTable *table, + gint **pSelected ) +{ + *pSelected = NULL; + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getSelectedAccessibleColumns() \n" ); +#endif + + if( pTable ) + return convertToGIntArray( pTable->getSelectedAccessibleColumns(), pSelected ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleColumns()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gint +table_wrapper_get_selected_rows( AtkTable *table, + gint **pSelected ) +{ + *pSelected = NULL; + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "getSelectedAccessibleRows() \n" ); +#endif + + if( pTable ) + return convertToGIntArray( pTable->getSelectedAccessibleRows(), pSelected ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectedAccessibleRows()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_column_selected( AtkTable *table, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "isAccessibleColumnSelected( %u ) returns %s\n", + column, pTable->isAccessibleColumnSelected( column ) ? "true" : "false" ); +#endif + + if( pTable ) + return pTable->isAccessibleColumnSelected( column ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in isAccessibleColumnSelected()" ); + } + + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_row_selected( AtkTable *table, + gint row ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "isAccessibleRowSelected( %u ) returns %s\n", + row, pTable->isAccessibleRowSelected( row ) ? "true" : "false" ); +#endif + + if( pTable ) + return pTable->isAccessibleRowSelected( row ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in isAccessibleRowSelected()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_is_selected( AtkTable *table, + gint row, + gint column ) +{ + try { + accessibility::XAccessibleTable* pTable = getTable( table ); + +#ifdef ENABLE_TRACING + if( pTable ) + fprintf(stderr, "isAccessibleSelected( %u, %u ) returns %s\n", + row, column, pTable->isAccessibleSelected( row , column ) ? "true" : "false" ); +#endif + + if( pTable ) + return pTable->isAccessibleSelected( row, column ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in isAccessibleSelected()" ); + } + + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_add_row_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for add_row_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_remove_row_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for remove_row_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_add_column_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for add_column_selection" ); + return 0; +} + +/*****************************************************************************/ + +static gboolean +table_wrapper_remove_column_selection( AtkTable *, gint ) +{ + g_warning( "FIXME: no simple analogue for remove_column_selection" ); + return 0; +} + +/*****************************************************************************/ + +static void +table_wrapper_set_caption( AtkTable *, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_column_description( AtkTable *, gint, const gchar * ) +{ // meaningless helper +} + + +/*****************************************************************************/ + +static void +table_wrapper_set_column_header( AtkTable *, gint, AtkObject * ) +{ // meaningless helper +} + + +/*****************************************************************************/ + +static void +table_wrapper_set_row_description( AtkTable *, gint, const gchar * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_row_header( AtkTable *, gint, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +static void +table_wrapper_set_summary( AtkTable *, AtkObject * ) +{ // meaningless helper +} + +/*****************************************************************************/ + +} // extern "C" + +void +tableIfaceInit (AtkTableIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->ref_at = table_wrapper_ref_at; + iface->get_n_rows = table_wrapper_get_n_rows; + iface->get_n_columns = table_wrapper_get_n_columns; + iface->get_index_at = table_wrapper_get_index_at; + iface->get_column_at_index = table_wrapper_get_column_at_index; + iface->get_row_at_index = table_wrapper_get_row_at_index; + iface->is_row_selected = table_wrapper_is_row_selected; + iface->is_selected = table_wrapper_is_selected; + iface->get_selected_rows = table_wrapper_get_selected_rows; + iface->add_row_selection = table_wrapper_add_row_selection; + iface->remove_row_selection = table_wrapper_remove_row_selection; + iface->add_column_selection = table_wrapper_add_column_selection; + iface->remove_column_selection = table_wrapper_remove_column_selection; + iface->get_selected_columns = table_wrapper_get_selected_columns; + iface->is_column_selected = table_wrapper_is_column_selected; + iface->get_column_extent_at = table_wrapper_get_column_extent_at; + iface->get_row_extent_at = table_wrapper_get_row_extent_at; + iface->get_row_header = table_wrapper_get_row_header; + iface->set_row_header = table_wrapper_set_row_header; + iface->get_column_header = table_wrapper_get_column_header; + iface->set_column_header = table_wrapper_set_column_header; + iface->get_caption = table_wrapper_get_caption; + iface->set_caption = table_wrapper_set_caption; + iface->get_summary = table_wrapper_get_summary; + iface->set_summary = table_wrapper_set_summary; + iface->get_row_description = table_wrapper_get_row_description; + iface->set_row_description = table_wrapper_set_row_description; + iface->get_column_description = table_wrapper_get_column_description; + iface->set_column_description = table_wrapper_set_column_description; +} diff --git a/vcl/unx/gtk/a11y/atktext.cxx b/vcl/unx/gtk/a11y/atktext.cxx new file mode 100644 index 000000000000..e6d3276891de --- /dev/null +++ b/vcl/unx/gtk/a11y/atktext.cxx @@ -0,0 +1,876 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" +#include "atktextattributes.hxx" +#include <algorithm> + +#include <com/sun/star/accessibility/AccessibleTextType.hpp> +#include <com/sun/star/accessibility/TextSegment.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> +#include <com/sun/star/text/TextMarkupType.hpp> + +// #define ENABLE_TRACING + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +using namespace ::com::sun::star; + +static sal_Int16 +text_type_from_boundary(AtkTextBoundary boundary_type) +{ + switch(boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + return accessibility::AccessibleTextType::CHARACTER; + case ATK_TEXT_BOUNDARY_WORD_START: + case ATK_TEXT_BOUNDARY_WORD_END: + return accessibility::AccessibleTextType::WORD; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + case ATK_TEXT_BOUNDARY_SENTENCE_END: + return accessibility::AccessibleTextType::SENTENCE; + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + return accessibility::AccessibleTextType::LINE; + default: + return -1; + } +} + +/*****************************************************************************/ + +static gchar * +adjust_boundaries( accessibility::XAccessibleText* pText, + accessibility::TextSegment& rTextSegment, + AtkTextBoundary boundary_type, + gint * start_offset, gint * end_offset ) +{ + accessibility::TextSegment aTextSegment; + rtl::OUString aString; + gint start = 0, end = 0; + + if( rTextSegment.SegmentText.getLength() > 0 ) + { + switch(boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + case ATK_TEXT_BOUNDARY_LINE_START: + case ATK_TEXT_BOUNDARY_LINE_END: + case ATK_TEXT_BOUNDARY_SENTENCE_START: + start = rTextSegment.SegmentStart; + end = rTextSegment.SegmentEnd; + aString = rTextSegment.SegmentText; + break; + + // the OOo break iterator behaves as SENTENCE_START + case ATK_TEXT_BOUNDARY_SENTENCE_END: + start = rTextSegment.SegmentStart; + end = rTextSegment.SegmentEnd; + + if( start > 0 ) + --start; + if( end > 0 && end < pText->getCharacterCount() - 1 ) + --end; + + aString = pText->getTextRange(start, end); + break; + + case ATK_TEXT_BOUNDARY_WORD_START: + start = rTextSegment.SegmentStart; + + // Determine the start index of the next segment + aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd, + text_type_from_boundary(boundary_type)); + if( aTextSegment.SegmentText.getLength() > 0 ) + end = aTextSegment.SegmentStart; + else + end = pText->getCharacterCount(); + + aString = pText->getTextRange(start, end); + break; + + case ATK_TEXT_BOUNDARY_WORD_END: + end = rTextSegment.SegmentEnd; + + // Determine the end index of the previous segment + aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart, + text_type_from_boundary(boundary_type)); + if( aTextSegment.SegmentText.getLength() > 0 ) + start = aTextSegment.SegmentEnd; + else + start = 0; + + aString = pText->getTextRange(start, end); + break; + + default: + return NULL; + } + } + + *start_offset = start; + *end_offset = end; + +#ifdef ENABLE_TRACING + fprintf(stderr, "adjust_boundaries( %d, %d, %d ) returns %d, %d\n", + rTextSegment.SegmentStart, rTextSegment.SegmentEnd, boundary_type, + start, end); +#endif + + return OUStringToGChar(aString); +} + +/*****************************************************************************/ + +static accessibility::XAccessibleText* + getText( AtkText *pText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpText && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleText::static_type(NULL) ); + pWrap->mpText = reinterpret_cast< accessibility::XAccessibleText * > (any.pReserved); + pWrap->mpText->acquire(); + } + + return pWrap->mpText; + } + + return NULL; +} + +/*****************************************************************************/ + +static accessibility::XAccessibleTextMarkup* + getTextMarkup( AtkText *pText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpTextMarkup && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextMarkup::static_type(NULL) ); + /* Since this not a dedicated interface in Atk and thus has not + * been queried during wrapper initialization, we need to check + * the return value here. + */ + if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass ) + { + pWrap->mpTextMarkup = reinterpret_cast< accessibility::XAccessibleTextMarkup * > (any.pReserved); + if( pWrap->mpTextMarkup ) + pWrap->mpTextMarkup->acquire(); + } + } + + return pWrap->mpTextMarkup; + } + + return NULL; +} + +/*****************************************************************************/ + +static accessibility::XAccessibleTextAttributes* + getTextAttributes( AtkText *pText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpTextAttributes && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextAttributes::static_type(NULL) ); + /* Since this not a dedicated interface in Atk and thus has not + * been queried during wrapper initialization, we need to check + * the return value here. + */ + if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass ) + { + pWrap->mpTextAttributes = reinterpret_cast< accessibility::XAccessibleTextAttributes * > (any.pReserved); + pWrap->mpTextAttributes->acquire(); + } + } + + return pWrap->mpTextAttributes; + } + + return NULL; +} + +/*****************************************************************************/ + +static accessibility::XAccessibleMultiLineText* + getMultiLineText( AtkText *pText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpMultiLineText && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleMultiLineText::static_type(NULL) ); + /* Since this not a dedicated interface in Atk and thus has not + * been queried during wrapper initialization, we need to check + * the return value here. + */ + if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass ) + { + pWrap->mpMultiLineText = reinterpret_cast< accessibility::XAccessibleMultiLineText * > (any.pReserved); + pWrap->mpMultiLineText->acquire(); + } + } + + return pWrap->mpMultiLineText; + } + + return NULL; +} + +/*****************************************************************************/ + +extern "C" { + +static gchar * +text_wrapper_get_text (AtkText *text, + gint start_offset, + gint end_offset) +{ + gchar * ret = NULL; + + g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), NULL ); + + /* at-spi expects the delete event to be send before the deletion happened + * so we save the deleted string object in the UNO event notification and + * fool libatk-bridge.so here .. + */ + void * pData = g_object_get_data( G_OBJECT(text), "ooo::text_changed::delete" ); + if( pData != NULL ) + { + accessibility::TextSegment * pTextSegment = + reinterpret_cast <accessibility::TextSegment *> (pData); + + if( pTextSegment->SegmentStart == start_offset && + pTextSegment->SegmentEnd == end_offset ) + { + rtl::OString aUtf8 = rtl::OUStringToOString( pTextSegment->SegmentText, RTL_TEXTENCODING_UTF8 ); + return g_strdup( aUtf8.getStr() ); + } + } + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + rtl::OUString aText; + sal_Int32 n = pText->getCharacterCount(); + + if( -1 == end_offset ) + aText = pText->getText(); + else if( start_offset < n ) + aText = pText->getTextRange(start_offset, end_offset); + + ret = g_strdup( rtl::OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getText()" ); + } + +#ifdef ENABLE_TRACING + fprintf(stderr, "text_wrapper_get_text( %d,%d ) returns %s\n", start_offset, end_offset, ret ? ret : "null" ); +#endif + return ret; +} + +static gchar * +text_wrapper_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in get_text_after_offset()" ); + } + + return NULL; +} + +static gchar * +text_wrapper_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + /* If the user presses the 'End' key, the caret will be placed behind the last character, + * which is the same index as the first character of the next line. In atk the magic offset + * '-2' is used to cover this special case. + */ + if ( + -2 == offset && + (ATK_TEXT_BOUNDARY_LINE_START == boundary_type || + ATK_TEXT_BOUNDARY_LINE_END == boundary_type) + ) + { + accessibility::XAccessibleMultiLineText* pMultiLineText = getMultiLineText( text ); + if( pMultiLineText ) + { + accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret(); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + + accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in get_text_at_offset()" ); + } + + return NULL; +} + +static gunichar +text_wrapper_get_character_at_offset (AtkText *text, + gint offset) +{ + gint start, end; + gunichar uc = 0; + + gchar * char_as_string = + text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR, + &start, &end); + if( char_as_string ) + { + uc = g_utf8_get_char( char_as_string ); + g_free( char_as_string ); + } + + return uc; +} + +static gchar * +text_wrapper_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type)); + return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in text_before_offset()" ); + } + + return NULL; +} + +static gint +text_wrapper_get_caret_offset (AtkText *text) +{ + gint offset = -1; + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + offset = pText->getCaretPosition(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCaretPosition()" ); + } + +#ifdef ENABLE_TRACING + fprintf(stderr, "get_caret_offset(%p) returns %d\n", text, offset); +#endif + + return offset; +} + +static gboolean +text_wrapper_set_caret_offset (AtkText *text, + gint offset) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + return pText->setCaretPosition( offset ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in setCaretPosition()" ); + } + + return FALSE; +} + +// --> OD 2010-03-04 #i92232# +AtkAttributeSet* +handle_text_markup_as_run_attribute( accessibility::XAccessibleTextMarkup* pTextMarkup, + const gint nTextMarkupType, + const gint offset, + AtkAttributeSet* pSet, + gint *start_offset, + gint *end_offset ) +{ + const gint nTextMarkupCount( pTextMarkup->getTextMarkupCount( nTextMarkupType ) ); + if ( nTextMarkupCount > 0 ) + { + for ( gint nTextMarkupIndex = 0; + nTextMarkupIndex < nTextMarkupCount; + ++nTextMarkupIndex ) + { + accessibility::TextSegment aTextSegment = + pTextMarkup->getTextMarkup( nTextMarkupIndex, nTextMarkupType ); + const gint nStartOffsetTextMarkup = aTextSegment.SegmentStart; + const gint nEndOffsetTextMarkup = aTextSegment.SegmentEnd; + if ( nStartOffsetTextMarkup <= offset ) + { + if ( offset < nEndOffsetTextMarkup ) + { + // text markup at <offset> + *start_offset = ::std::max( *start_offset, + nStartOffsetTextMarkup ); + *end_offset = ::std::min( *end_offset, + nEndOffsetTextMarkup ); + switch ( nTextMarkupType ) + { + case com::sun::star::text::TextMarkupType::SPELLCHECK: + { + pSet = attribute_set_prepend_misspelled( pSet ); + } + break; + case com::sun::star::text::TextMarkupType::TRACK_CHANGE_INSERTION: + { + pSet = attribute_set_prepend_tracked_change_insertion( pSet ); + } + break; + case com::sun::star::text::TextMarkupType::TRACK_CHANGE_DELETION: + { + pSet = attribute_set_prepend_tracked_change_deletion( pSet ); + } + break; + case com::sun::star::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE: + { + pSet = attribute_set_prepend_tracked_change_formatchange( pSet ); + } + break; + default: + { + OSL_ASSERT( false ); + } + } + break; // no further iteration needed. + } + else + { + *start_offset = ::std::max( *start_offset, + nEndOffsetTextMarkup ); + // continue iteration. + } + } + else + { + *end_offset = ::std::min( *end_offset, + nStartOffsetTextMarkup ); + break; // no further iteration. + } + } // eof iteration over text markups + } + + return pSet; +} +// <-- + +static AtkAttributeSet * +text_wrapper_get_run_attributes( AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + AtkAttributeSet *pSet = NULL; + + try { + bool bOffsetsAreValid = false; + + accessibility::XAccessibleText* pText = getText( text ); + accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text ); + if( pText && pTextAttributes ) + { + uno::Sequence< beans::PropertyValue > aAttributeList = + pTextAttributes->getRunAttributes( offset, uno::Sequence< rtl::OUString > () ); + + pSet = attribute_set_new_from_property_values( aAttributeList, true, text ); + // --> OD 2009-06-22 #i100938# + // - always provide start_offset and end_offset +// if( pSet ) + // <-- + { + accessibility::TextSegment aTextSegment = + pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); + + *start_offset = aTextSegment.SegmentStart; + // --> OD 2009-06-22 #i100938# + // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance +// *end_offset = aTextSegment.SegmentEnd + 1; // FIXME: TESTME + *end_offset = aTextSegment.SegmentEnd; + // <-- + bOffsetsAreValid = true; + } + } + + // Special handling for misspelled text + // --> OD 2010-03-01 #i92232# + // - add special handling for tracked changes and refactor the + // corresponding code for handling misspelled text. + accessibility::XAccessibleTextMarkup* pTextMarkup = getTextMarkup( text ); + if( pTextMarkup ) + { + // Get attribute run here if it hasn't been done before + if( !bOffsetsAreValid ) + { + accessibility::TextSegment aAttributeTextSegment = + pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN); + *start_offset = aAttributeTextSegment.SegmentStart; + *end_offset = aAttributeTextSegment.SegmentEnd; + } + // handle misspelled text + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + com::sun::star::text::TextMarkupType::SPELLCHECK, + offset, pSet, start_offset, end_offset ); + // handle tracked changes + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + com::sun::star::text::TextMarkupType::TRACK_CHANGE_INSERTION, + offset, pSet, start_offset, end_offset ); + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + com::sun::star::text::TextMarkupType::TRACK_CHANGE_DELETION, + offset, pSet, start_offset, end_offset ); + pSet = handle_text_markup_as_run_attribute( + pTextMarkup, + com::sun::star::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE, + offset, pSet, start_offset, end_offset ); + } + // <-- + } + catch(const uno::Exception& e){ + + g_warning( "Exception in get_run_attributes()" ); + + if( pSet ) + { + atk_attribute_set_free( pSet ); + pSet = NULL; + } + } + + return pSet; +} + +/*****************************************************************************/ + +static AtkAttributeSet * +text_wrapper_get_default_attributes( AtkText *text ) +{ + AtkAttributeSet *pSet = NULL; + + try { + accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text ); + if( pTextAttributes ) + { + uno::Sequence< beans::PropertyValue > aAttributeList = + pTextAttributes->getDefaultAttributes( uno::Sequence< rtl::OUString > () ); + + pSet = attribute_set_new_from_property_values( aAttributeList, false, text ); + } + } + catch(const uno::Exception& e) { + + g_warning( "Exception in get_default_attributes()" ); + + if( pSet ) + { + atk_attribute_set_free( pSet ); + pSet = NULL; + } + } + + return pSet; +} + +/*****************************************************************************/ + +static void +text_wrapper_get_character_extents( AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords ) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + *x = *y = *width = *height = 0; + awt::Rectangle aRect = pText->getCharacterBounds( offset ); + + gint origin_x = 0; + gint origin_y = 0; + + if( coords == ATK_XY_SCREEN ) + { + g_return_if_fail( ATK_IS_COMPONENT( text ) ); + atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); + } + + *x = aRect.X + origin_x; + *y = aRect.Y + origin_y; + *width = aRect.Width; + *height = aRect.Height; + +#ifdef ENABLE_TRACING + fprintf(stderr, "get_character_extents(%d, %d) returns: %d,%d,%d,%d ", + offset, coords, *x, *y, *width, *height); +#endif + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCharacterBounds" ); + } +} + +static gint +text_wrapper_get_character_count (AtkText *text) +{ + gint rv = 0; + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + rv = pText->getCharacterCount(); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCharacterCount" ); + } + +#ifdef ENABLE_TRACING + fprintf(stderr, "get_character_count(%p) returns: %d\n", text, rv); +#endif + + return rv; +} + +static gint +text_wrapper_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + gint origin_x = 0; + gint origin_y = 0; + + if( coords == ATK_XY_SCREEN ) + { + g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 ); + atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords); + } + + return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getIndexAtPoint" ); + } + + return -1; +} + +// FIXME: the whole series of selections API is problematic ... + +static gint +text_wrapper_get_n_selections (AtkText *text) +{ + gint rv = 0; + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0; + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectionEnd() or getSelectionStart()" ); + } + +#ifdef ENABLE_TRACING + fprintf(stderr, "get_n_selections(%p) returns %d\n", text, rv); +#endif + + return rv; +} + +static gchar * +text_wrapper_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + { + *start_offset = pText->getSelectionStart(); + *end_offset = pText->getSelectionEnd(); + + return OUStringToGChar( pText->getSelectedText() ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" ); + } + + return NULL; +} + +static gboolean +text_wrapper_add_selection (AtkText *text, + gint start_offset, + gint end_offset) +{ + // FIXME: can we try to be more compatible by expanding an + // existing adjacent selection ? + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + return pText->setSelection( start_offset, end_offset ); // ? + } + catch(const uno::Exception& e) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +static gboolean +text_wrapper_remove_selection (AtkText *text, + gint selection_num) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + return pText->setSelection( 0, 0 ); // ? + } + catch(const uno::Exception& e) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +static gboolean +text_wrapper_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset) +{ + g_return_val_if_fail( selection_num == 0, FALSE ); + + try { + accessibility::XAccessibleText* pText = getText( text ); + if( pText ) + return pText->setSelection( start_offset, end_offset ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in setSelection()" ); + } + + return FALSE; +} + +} // extern "C" + +void +textIfaceInit (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_text = text_wrapper_get_text; + iface->get_character_at_offset = text_wrapper_get_character_at_offset; + iface->get_text_before_offset = text_wrapper_get_text_before_offset; + iface->get_text_at_offset = text_wrapper_get_text_at_offset; + iface->get_text_after_offset = text_wrapper_get_text_after_offset; + iface->get_caret_offset = text_wrapper_get_caret_offset; + iface->set_caret_offset = text_wrapper_set_caret_offset; + iface->get_character_count = text_wrapper_get_character_count; + iface->get_n_selections = text_wrapper_get_n_selections; + iface->get_selection = text_wrapper_get_selection; + iface->add_selection = text_wrapper_add_selection; + iface->remove_selection = text_wrapper_remove_selection; + iface->set_selection = text_wrapper_set_selection; + iface->get_run_attributes = text_wrapper_get_run_attributes; + iface->get_default_attributes = text_wrapper_get_default_attributes; + iface->get_character_extents = text_wrapper_get_character_extents; + iface->get_offset_at_point = text_wrapper_get_offset_at_point; +} diff --git a/vcl/unx/gtk/a11y/atktextattributes.cxx b/vcl/unx/gtk/a11y/atktextattributes.cxx new file mode 100644 index 000000000000..04498810597f --- /dev/null +++ b/vcl/unx/gtk/a11y/atktextattributes.cxx @@ -0,0 +1,1456 @@ +/************************************************************************* + * + * 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 "atktextattributes.hxx" + +#include <com/sun/star/awt/FontSlant.hpp> +#include <com/sun/star/awt/FontStrikeout.hpp> +#include <com/sun/star/awt/FontUnderline.hpp> + +#include <com/sun/star/style/CaseMap.hpp> +#include <com/sun/star/style/LineSpacing.hpp> +#include <com/sun/star/style/LineSpacingMode.hpp> +#include <com/sun/star/style/ParagraphAdjust.hpp> +#include <com/sun/star/style/TabAlign.hpp> +#include <com/sun/star/style/TabStop.hpp> + +#include <com/sun/star/text/WritingMode2.hpp> + +#include "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> + +#include <vcl/svapp.hxx> +#include <vcl/outdev.hxx> + +#include <stdio.h> +#include <string.h> + +using namespace ::com::sun::star; + +typedef gchar* (* AtkTextAttrFunc) ( const uno::Any& rAny ); +typedef bool (* TextPropertyValueFunc) ( uno::Any& rAny, const gchar * value ); + +#define STRNCMP_PARAM( s ) s,sizeof( s )-1 + + +/*****************************************************************************/ + +static AtkTextAttribute atk_text_attribute_paragraph_style = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_font_effect = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_decoration = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_line_height = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_rotation = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_shadow = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_tab_interval = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_tab_stops = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_writing_mode = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_vertical_align = ATK_TEXT_ATTR_INVALID; +static AtkTextAttribute atk_text_attribute_misspelled = ATK_TEXT_ATTR_INVALID; +// --> OD 2010-03-01 #i92232# +static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID; +// <-- +// --> OD 2010-03-05 #i92233# +static AtkTextAttribute atk_text_attribute_mm_to_pixel_ratio = ATK_TEXT_ATTR_INVALID; +// <-- + +/*****************************************************************************/ + +/** + * !! IMPORTANT NOTE !! : when adding items to this list, KEEP THE LIST SORTED + * and re-arrange the enum values accordingly. + */ + +enum ExportedAttribute +{ + TEXT_ATTRIBUTE_BACKGROUND_COLOR = 0, + TEXT_ATTRIBUTE_CASEMAP, + TEXT_ATTRIBUTE_FOREGROUND_COLOR, + TEXT_ATTRIBUTE_CONTOURED, + TEXT_ATTRIBUTE_CHAR_ESCAPEMENT, + TEXT_ATTRIBUTE_BLINKING, + TEXT_ATTRIBUTE_FONT_NAME, + TEXT_ATTRIBUTE_HEIGHT, + TEXT_ATTRIBUTE_HIDDEN, + TEXT_ATTRIBUTE_KERNING, + TEXT_ATTRIBUTE_LOCALE, + TEXT_ATTRIBUTE_POSTURE, + TEXT_ATTRIBUTE_RELIEF, + TEXT_ATTRIBUTE_ROTATION, + TEXT_ATTRIBUTE_SCALE, + TEXT_ATTRIBUTE_SHADOWED, + TEXT_ATTRIBUTE_STRIKETHROUGH, + TEXT_ATTRIBUTE_UNDERLINE, + TEXT_ATTRIBUTE_WEIGHT, + // --> OD 2010-03-05 #i92233# + TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO, + // <-- + TEXT_ATTRIBUTE_JUSTIFICATION, + TEXT_ATTRIBUTE_BOTTOM_MARGIN, + TEXT_ATTRIBUTE_FIRST_LINE_INDENT, + TEXT_ATTRIBUTE_LEFT_MARGIN, + TEXT_ATTRIBUTE_LINE_SPACING, + TEXT_ATTRIBUTE_RIGHT_MARGIN, + TEXT_ATTRIBUTE_STYLE_NAME, + TEXT_ATTRIBUTE_TAB_STOPS, + TEXT_ATTRIBUTE_TOP_MARGIN, + TEXT_ATTRIBUTE_WRITING_MODE, + TEXT_ATTRIBUTE_LAST +}; + +static const char * ExportedTextAttributes[TEXT_ATTRIBUTE_LAST] = +{ + "CharBackColor", // TEXT_ATTRIBUTE_BACKGROUND_COLOR + "CharCaseMap", // TEXT_ATTRIBUTE_CASEMAP + "CharColor", // TEXT_ATTRIBUTE_FOREGROUND_COLOR + "CharContoured", // TEXT_ATTRIBUTE_CONTOURED + "CharEscapement", // TEXT_ATTRIBUTE_CHAR_ESCAPEMENT + "CharFlash", // TEXT_ATTRIBUTE_BLINKING + "CharFontName", // TEXT_ATTRIBUTE_FONT_NAME + "CharHeight", // TEXT_ATTRIBUTE_HEIGHT + "CharHidden", // TEXT_ATTRIBUTE_HIDDEN + "CharKerning", // TEXT_ATTRIBUTE_KERNING + "CharLocale", // TEXT_ATTRIBUTE_LOCALE + "CharPosture", // TEXT_ATTRIBUTE_POSTURE + "CharRelief", // TEXT_ATTRIBUTE_RELIEF + "CharRotation", // TEXT_ATTRIBUTE_ROTATION + "CharScaleWidth", // TEXT_ATTRIBUTE_SCALE + "CharShadowed", // TEXT_ATTRIBUTE_SHADOWED + "CharStrikeout", // TEXT_ATTRIBUTE_STRIKETHROUGH + "CharUnderline", // TEXT_ATTRIBUTE_UNDERLINE + "CharWeight", // TEXT_ATTRIBUTE_WEIGHT + // --> OD 2010-03-05 #i92233# + "MMToPixelRatio", // TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO + // <-- + "ParaAdjust", // TEXT_ATTRIBUTE_JUSTIFICATION + "ParaBottomMargin", // TEXT_ATTRIBUTE_BOTTOM_MARGIN + "ParaFirstLineIndent", // TEXT_ATTRIBUTE_FIRST_LINE_INDENT + "ParaLeftMargin", // TEXT_ATTRIBUTE_LEFT_MARGIN + "ParaLineSpacing", // TEXT_ATTRIBUTE_LINE_SPACING + "ParaRightMargin", // TEXT_ATTRIBUTE_RIGHT_MARGIN + "ParaStyleName", // TEXT_ATTRIBUTE_STYLE_NAME + "ParaTabStops", // TEXT_ATTRIBUTE_TAB_STOPS + "ParaTopMargin", // TEXT_ATTRIBUTE_TOP_MARGIN + "WritingMode" // TEXT_ATTRIBUTE_WRITING_MODE +}; + + +/*****************************************************************************/ + +static gchar* +get_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nIndex, AtkTextAttrFunc func ) +{ + if( nIndex != -1 ) + return func(rAttributeList[nIndex].Value); + + return NULL; +} + +#define get_bool_value( list, index ) get_value( list, index, Bool2String ) +#define get_short_value( list, index ) get_value( list, index, Short2String ) +//#define get_long_value( list, index ) get_value( list, index, Long2String ) pb: not used (warning on linux) +#define get_height_value( list, index ) get_value( list, index, Float2String ) +#define get_justification_value( list, index ) get_value( list, index, Adjust2Justification ) +#define get_cmm_value( list, index ) get_value( list, index, CMM2UnitString ) +#define get_scale_width( list, index ) get_value( list, index, Scale2String ) +#define get_strikethrough_value( list, index ) get_value( list, index, Strikeout2String ) +#define get_string_value( list, index ) get_value( list, index, GetString ) +#define get_style_value( list, index ) get_value( list, index, FontSlant2Style ) +#define get_underline_value( list, index ) get_value( list, index, Underline2String ) +#define get_variant_value( list, index ) get_value( list, index, CaseMap2String ) +#define get_weight_value( list, index ) get_value( list, index, Weight2String ) +#define get_language_string( list, index ) get_value( list, index, Locale2String ) + +/* +static gchar* +dump_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, sal_Int32 nIndex ) +{ + if( nIndex != -1 ) + { + rtl::OString aName = rtl::OUStringToOString(rAttributeList[nIndex].Name, RTL_TEXTENCODING_UTF8); + + if( rAttributeList[nIndex].Value.has<sal_Int16> () ) + OSL_TRACE( "%s = %d (short value)", aName.getStr(), + rAttributeList[nIndex].Value.get<sal_Int16> () ); + + else if( rAttributeList[nIndex].Value.has<sal_Int8> () ) + OSL_TRACE( "%s = %d (byte value)", aName.getStr(), + rAttributeList[nIndex].Value.get<sal_Int8> () ); + + else if( rAttributeList[nIndex].Value.has<sal_Bool> () ) + OSL_TRACE( "%s = %s (bool value)", aName.getStr(), + rAttributeList[nIndex].Value.get<sal_Bool> () ? "true" : "false" ); + + else if( rAttributeList[nIndex].Value.has<rtl::OUString> () ) + OSL_TRACE( "%s = %s", aName.getStr(), + rtl::OUStringToOString(rAttributeList[nIndex].Value.get<rtl::OUString> (), + RTL_TEXTENCODING_UTF8).getStr() ); + } + + return NULL; +} +*/ + +static inline +double toPoint(sal_Int16 n) +{ + // 100th mm -> pt + return (double) (n * 72) / 2540; +} + + +/*****************************************************************************/ + +/* +static gchar* +NullString(const uno::Any&) +{ + return NULL; +} +*/ + +static bool +InvalidValue( uno::Any&, const gchar * ) +{ + return false; +} + +/*****************************************************************************/ + +static gchar* +Float2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%g", rAny.get<float>() ); +} + +static bool +String2Float( uno::Any& rAny, const gchar * value ) +{ + float fval; + + if( 1 != sscanf( value, "%g", &fval ) ) + return false; + + rAny = uno::makeAny( fval ); + return true; +} + +/*****************************************************************************/ + +/* +static gchar* +Short2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%d", rAny.get<sal_Int16>() ); +} + +static bool +String2Short( uno::Any& rAny, const gchar * value ) +{ + sal_Int32 lval; + + if( 1 != sscanf( value, "%d", &lval ) ) + return false; + + rAny = uno::makeAny( (sal_Int16) lval ); + return true; +} +*/ + +/*****************************************************************************/ +/* pb: not used (warning on linux) +static gchar* +Long2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%ld", rAny.get<sal_Int32>() ); +} + +static bool +String2Long( uno::Any& rAny, const gchar * value ) +{ + sal_Int32 lval; + + if( 1 != sscanf( value, "%ld", &lval ) ) + return false; + + rAny = uno::makeAny( lval ); + return true; +} +*/ +/*****************************************************************************/ + +static accessibility::XAccessibleComponent* + getComponent( AtkText *pText ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText ); + if( pWrap ) + { + if( !pWrap->mpComponent && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleComponent::static_type(NULL) ); + pWrap->mpComponent = reinterpret_cast< accessibility::XAccessibleComponent * > (any.pReserved); + pWrap->mpComponent->acquire(); + } + + return pWrap->mpComponent; + } + + return NULL; +} + +static gchar* +get_color_value(const uno::Sequence< beans::PropertyValue >& rAttributeList, + const sal_Int32 * pIndexArray, + ExportedAttribute attr, + AtkText * text) +{ + sal_Int32 nColor = -1; // AUTOMATIC + sal_Int32 nIndex = pIndexArray[attr]; + + if( nIndex != -1 ) + nColor = rAttributeList[nIndex].Value.get<sal_Int32>(); + + /* + * Check for color value for 100% alpha white, which means + * "automatic". Grab the RGB value from XAccessibleComponent + * in this case. + */ + + if( (nColor == -1) && text ) + { + try + { + accessibility::XAccessibleComponent *pComponent = getComponent( text ); + if( pComponent ) + { + switch( attr ) + { + case TEXT_ATTRIBUTE_BACKGROUND_COLOR: + nColor = pComponent->getBackground(); + break; + case TEXT_ATTRIBUTE_FOREGROUND_COLOR: + nColor = pComponent->getForeground(); + break; + default: + break; + } + } + } + + catch(const uno::Exception& e) { + g_warning( "Exception in get[Fore|Back]groundColor()" ); + } + } + + if( nColor != -1 ) + { + sal_uInt8 blue = nColor & 0xFF; + sal_uInt8 green = (nColor >> 8) & 0xFF; + sal_uInt8 red = (nColor >> 16) & 0xFF; + + return g_strdup_printf( "%u,%u,%u", red, green, blue ); + } + + return NULL; +} + +static bool +String2Color( uno::Any& rAny, const gchar * value ) +{ + int red, green, blue; + + if( 3 != sscanf( value, "%d,%d,%d", &red, &green, &blue ) ) + return false; + + sal_Int32 nColor = (sal_Int32) blue | ( (sal_Int32) green << 8 ) | ( ( sal_Int32 ) red << 16 ); + rAny = uno::makeAny( nColor ); + return true; +} + +/*****************************************************************************/ + +static gchar* +FontSlant2Style(const uno::Any& rAny) +{ + const gchar * value = NULL; + + switch( rAny.get<awt::FontSlant>() ) + { + case awt::FontSlant_NONE: + value = "normal"; + break; + + case awt::FontSlant_OBLIQUE: + value = "oblique"; + break; + + case awt::FontSlant_ITALIC: + value = "italic"; + break; + + case awt::FontSlant_REVERSE_OBLIQUE: + value = "reverse oblique"; + break; + + case awt::FontSlant_REVERSE_ITALIC: + value = "reverse italic"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return NULL; +} + +static bool +Style2FontSlant( uno::Any& rAny, const gchar * value ) +{ + awt::FontSlant aFontSlant; + + if( strncmp( value, STRNCMP_PARAM( "normal" ) ) ) + aFontSlant = awt::FontSlant_NONE; + else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) ) + aFontSlant = awt::FontSlant_OBLIQUE; + else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) ) + aFontSlant = awt::FontSlant_ITALIC; + else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) ) + aFontSlant = awt::FontSlant_REVERSE_OBLIQUE; + else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) ) + aFontSlant = awt::FontSlant_REVERSE_ITALIC; + else + return false; + + rAny = uno::makeAny( aFontSlant ); + return true; +} + +/*****************************************************************************/ + +static gchar* +Weight2String(const uno::Any& rAny) +{ + return g_strdup_printf( "%g", rAny.get<float>() * 4 ); +} + +static bool +String2Weight( uno::Any& rAny, const gchar * value ) +{ + float weight; + + if( 1 != sscanf( value, "%g", &weight ) ) + return false; + + rAny = uno::makeAny( weight / 4 ); + return true; +} + + +/*****************************************************************************/ + +static gchar* +Adjust2Justification(const uno::Any& rAny) +{ + const gchar * value = NULL; + + switch( rAny.get<short>() ) + { + case style::ParagraphAdjust_LEFT: + value = "left"; + break; + + case style::ParagraphAdjust_RIGHT: + value = "right"; + break; + + case style::ParagraphAdjust_BLOCK: + case style::ParagraphAdjust_STRETCH: + value = "fill"; + break; + + case style::ParagraphAdjust_CENTER: + value = "center"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return NULL; +} + +static bool +Justification2Adjust( uno::Any& rAny, const gchar * value ) +{ + short nParagraphAdjust; + + if( strncmp( value, STRNCMP_PARAM( "left" ) ) ) + nParagraphAdjust = style::ParagraphAdjust_LEFT; + else if( strncmp( value, STRNCMP_PARAM( "right" ) ) ) + nParagraphAdjust = style::ParagraphAdjust_RIGHT; + else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) ) + nParagraphAdjust = style::ParagraphAdjust_BLOCK; + else if( strncmp( value, STRNCMP_PARAM( "center" ) ) ) + nParagraphAdjust = style::ParagraphAdjust_CENTER; + else + return false; + + rAny = uno::makeAny( nParagraphAdjust ); + return true; +} + +/*****************************************************************************/ + +const gchar * font_strikethrough[] = { + "none", // FontStrikeout::NONE + "single", // FontStrikeout::SINGLE + "double", // FontStrikeout::DOUBLE + NULL, // FontStrikeout::DONTKNOW + "bold", // FontStrikeout::BOLD + "with /", // FontStrikeout::SLASH + "with X" // FontStrikeout::X +}; + +const sal_Int16 n_strikeout_constants = sizeof(font_strikethrough) / sizeof(gchar*); + +static gchar* +Strikeout2String(const uno::Any& rAny) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( n >= 0 && n < n_strikeout_constants ) + return g_strdup( font_strikethrough[n] ); + + return NULL; +} + +static bool +String2Strikeout( uno::Any& rAny, const gchar * value ) +{ + for( sal_Int16 n=0; n < n_strikeout_constants; ++n ) + { + if( ( NULL != font_strikethrough[n] ) && + 0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) ) + { + rAny = uno::makeAny( n ); + return true; + } + } + + return false; +} + +/*****************************************************************************/ + +static gchar* +Underline2String(const uno::Any& rAny) +{ + const gchar * value = NULL; + + switch( rAny.get<sal_Int16>() ) + { + case awt::FontUnderline::NONE: + value = "none"; + break; + + case awt::FontUnderline::SINGLE: + value = "single"; + break; + + case awt::FontUnderline::DOUBLE: + value = "double"; + break; + + default: + break; + } + + if( value ) + return g_strdup( value ); + + return NULL; +} + +static bool +String2Underline( uno::Any& rAny, const gchar * value ) +{ + short nUnderline; + + if( strncmp( value, STRNCMP_PARAM( "none" ) ) ) + nUnderline = awt::FontUnderline::NONE; + else if( strncmp( value, STRNCMP_PARAM( "single" ) ) ) + nUnderline = awt::FontUnderline::SINGLE; + else if( strncmp( value, STRNCMP_PARAM( "double" ) ) ) + nUnderline = awt::FontUnderline::DOUBLE; + else + return false; + + rAny = uno::makeAny( nUnderline ); + return true; +} + +/*****************************************************************************/ + +static gchar* +GetString(const uno::Any& rAny) +{ + rtl::OString aFontName = rtl::OUStringToOString( rAny.get< rtl::OUString > (), RTL_TEXTENCODING_UTF8 ); + + if( aFontName.getLength() ) + return g_strdup( aFontName.getStr() ); + + return NULL; +} + +static bool +SetString( uno::Any& rAny, const gchar * value ) +{ + rtl::OString aFontName( value ); + + if( aFontName.getLength() ) + { + rAny = uno::makeAny( rtl::OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ) ); + return true; + } + + return false; +} + +/*****************************************************************************/ + +// @see http://developer.gnome.org/doc/API/2.0/atk/AtkText.html#AtkTextAttribute + +// CMM = 100th of mm +static gchar* +CMM2UnitString(const uno::Any& rAny) +{ + double fValue = rAny.get<sal_Int32>(); + fValue = fValue * 0.01; + + return g_strdup_printf( "%gmm", fValue ); +} + +static bool +UnitString2CMM( uno::Any& rAny, const gchar * value ) +{ + float fValue = 0.0; // pb: dont use double here because of warning on linux + + if( 1 != sscanf( value, "%gmm", &fValue ) ) + return false; + + fValue = fValue * 100; + + rAny = uno::makeAny( (sal_Int32) fValue); + return true; +} + +/*****************************************************************************/ + +static const gchar * bool_values[] = { "true", "false" }; + +static gchar * +Bool2String( const uno::Any& rAny ) +{ + int n = 1; + + if( rAny.get<sal_Bool>() ) + n = 0; + + return g_strdup( bool_values[n] ); +} + +static bool +String2Bool( uno::Any& rAny, const gchar * value ) +{ + sal_Bool bValue; + + if( strncmp( value, STRNCMP_PARAM( "true" ) ) ) + bValue = sal_True; + else if( strncmp( value, STRNCMP_PARAM( "false" ) ) ) + bValue = sal_False; + else + return false; + + rAny = uno::makeAny(bValue); + return true; +} + +/*****************************************************************************/ + +static gchar* +Scale2String( const uno::Any& rAny ) +{ + return g_strdup_printf( "%g", (double) (rAny.get< sal_Int16 > ()) / 100 ); +} + +static bool +String2Scale( uno::Any& rAny, const gchar * value ) +{ + double dval; + + if( 1 != sscanf( value, "%lg", &dval ) ) + return false; + + rAny = uno::makeAny((sal_Int16) (dval * 100)); + return true; +} + +/*****************************************************************************/ + +static gchar * +CaseMap2String( const uno::Any& rAny ) +{ + const gchar * value = NULL; + + switch( rAny.get<short>() ) + { + case style::CaseMap::SMALLCAPS: + value = "small_caps"; + break; + + default: + value = "normal"; + break; + } + + if( value ) + return g_strdup( value ); + + return NULL; +} + +static bool +String2CaseMap( uno::Any& rAny, const gchar * value ) +{ + short nCaseMap; + + if( strncmp( value, STRNCMP_PARAM( "normal" ) ) ) + nCaseMap = style::CaseMap::NONE; + else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) ) + nCaseMap = style::CaseMap::SMALLCAPS; + else + return false; + + rAny = uno::makeAny( nCaseMap ); + return true; +} + +/*****************************************************************************/ + +const gchar * font_stretch[] = { + "ultra_condensed", + "extra_condensed", + "condensed", + "semi_condensed", + "normal", + "semi_expanded", + "expanded", + "extra_expanded", + "ultra_expanded" +}; + +static gchar* +Kerning2Stretch(const uno::Any& rAny) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + int i = 4; + + // No good idea for a mapping - just return the basic info + if( n < 0 ) + i=2; + else if( n > 0 ) + i=6; + + return g_strdup(font_stretch[i]); +} + +/*****************************************************************************/ + +static gchar* +Locale2String(const uno::Any& rAny) +{ + lang::Locale aLocale = rAny.get<lang::Locale> (); + return g_strdup_printf( "%s-%s", + rtl::OUStringToOString( aLocale.Language, RTL_TEXTENCODING_ASCII_US).getStr(), + rtl::OUStringToOString( aLocale.Country, RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() ); +} + +static bool +String2Locale( uno::Any& rAny, const gchar * value ) +{ + bool ret = false; + + gchar ** str_array = g_strsplit_set( value, "-.@", -1 ); + if( str_array[0] != NULL ) + { + ret = true; + + lang::Locale aLocale; + + aLocale.Language = rtl::OUString::createFromAscii(str_array[0]); + if( str_array[1] != NULL ) + { + gchar * country = g_ascii_strup(str_array[1], -1); + aLocale.Country = rtl::OUString::createFromAscii(country); + g_free(country); + } + + rAny = uno::makeAny(aLocale); + } + + g_strfreev(str_array); + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-effect-prop +static const gchar * relief[] = { "none", "emboss", "engrave" }; +static const gchar * outline = "outline"; + +static gchar * +get_font_effect(const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nContourIndex, sal_Int32 nReliefIndex) +{ + if( nContourIndex != -1 ) + { + if( rAttributeList[nContourIndex].Value.get<sal_Bool>() ) + return g_strdup(outline); + } + + if( nReliefIndex != -1 ) + { + sal_Int16 n = rAttributeList[nReliefIndex].Value.get<sal_Int16>(); + if( n < 3) + return g_strdup(relief[n]); + } + + return NULL; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/text.html#lining-striking-props + + +enum +{ + DECORATION_NONE = 0, + DECORATION_BLINK, + DECORATION_UNDERLINE, + DECORATION_LINE_THROUGH +}; + + +static const gchar * decorations[] = { "none", "blink", "underline", "line-through" }; + +static gchar * +get_text_decoration(const uno::Sequence< beans::PropertyValue >& rAttributeList, + sal_Int32 nBlinkIndex, sal_Int32 nUnderlineIndex, + sal_Int16 nStrikeoutIndex) +{ + gchar * value_list[4] = { NULL, NULL, NULL, NULL }; + gint count = 0; + + // no property value found + if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1)) + return NULL; + + if( nBlinkIndex != -1 ) + { + if( rAttributeList[nBlinkIndex].Value.get<sal_Bool>() ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_BLINK]); + } + if( nUnderlineIndex != -1 ) + { + sal_Int16 n = rAttributeList[nUnderlineIndex].Value.get<sal_Int16> (); + if( n != awt::FontUnderline::NONE ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_UNDERLINE]); + } + if( nStrikeoutIndex != -1 ) + { + sal_Int16 n = rAttributeList[nStrikeoutIndex].Value.get<sal_Int16> (); + if( n != awt::FontStrikeout::NONE && n != awt::FontStrikeout::DONTKNOW ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_LINE_THROUGH]); + } + + if( count == 0 ) + value_list[count++] = const_cast <gchar *> (decorations[DECORATION_NONE]); + + return g_strjoinv(" ", value_list); +} + + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/text.html#propdef-text-shadow + +static const gchar * shadow_values[] = { "none", "black" }; + +static gchar * +Bool2Shadow( const uno::Any& rAny ) +{ + int n = 0; + + if( rAny.get<sal_Bool>() ) + n = 1; + + return g_strdup( shadow_values[n] ); +} + +/*****************************************************************************/ + +static gchar * +Short2Degree( const uno::Any& rAny ) +{ + float f = rAny.get<sal_Int16>() / 10; + return g_strdup_printf( "%g", f ); +} + +/*****************************************************************************/ + +const gchar * directions[] = { "ltr", "rtl", "rtl", "ltr", "none" }; + +static gchar * +WritingMode2Direction( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( 0 <= n && n <= text::WritingMode2::PAGE ) + return g_strdup(directions[n]); + + return NULL; +} + +// @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection + +const gchar * writing_modes[] = { "lr-tb", "rl-tb", "tb-rl", "tb-lr", "none" }; +static gchar * +WritingMode2String( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + + if( 0 <= n && n <= text::WritingMode2::PAGE ) + return g_strdup(writing_modes[n]); + + return NULL; +} + +/*****************************************************************************/ + +const char * baseline_values[] = { "baseline", "sub", "super" }; + +// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-vertical-align +static gchar * +Escapement2VerticalAlign( const uno::Any& rAny ) +{ + sal_Int16 n = rAny.get<sal_Int16>(); + gchar * ret = NULL; + + // Values are in %, 101% means "automatic" + if( n == 0 ) + ret = g_strdup(baseline_values[0]); + else if( n == 101 ) + ret = g_strdup(baseline_values[2]); + else if( n == -101 ) + ret = g_strdup(baseline_values[1]); + else + ret = g_strdup_printf( "%d%%", n ); + + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-line-height +static gchar * +LineSpacing2LineHeight( const uno::Any& rAny ) +{ + style::LineSpacing ls; + gchar * ret = NULL; + + if( rAny >>= ls ) + { + if( ls.Mode == style::LineSpacingMode::PROP ) + ret = g_strdup_printf( "%d%%", ls.Height ); + else if( ls.Mode == style::LineSpacingMode::FIX ) + ret = g_strdup_printf( "%.3gpt", toPoint(ls.Height) ); + } + + return ret; +} + +/*****************************************************************************/ + +// @see http://www.w3.org/People/howcome/t/970224HTMLERB-CSS/WD-tabs-970117.html +static gchar * +TabStopList2String( const uno::Any& rAny, bool default_tabs ) +{ + uno::Sequence< style::TabStop > theTabStops; + gchar * ret = NULL; + + if( rAny >>= theTabStops) + { + sal_Int32 indexOfTab = 0; + sal_Int32 numberOfTabs = theTabStops.getLength(); + sal_Unicode lastFillChar = (sal_Unicode) ' '; + + for( ; indexOfTab < numberOfTabs; ++indexOfTab ) + { + bool is_default_tab = (style::TabAlign_DEFAULT == theTabStops[indexOfTab].Alignment); + + if( is_default_tab != default_tabs ) + continue; + + double fValue = theTabStops[indexOfTab].Position; + fValue = fValue * 0.01; + + const gchar * tab_align = ""; + switch( theTabStops[indexOfTab].Alignment ) + { + case style::TabAlign_LEFT : + tab_align = "left "; + break; + case style::TabAlign_CENTER : + tab_align = "center "; + break; + case style::TabAlign_RIGHT : + tab_align = "right "; + break; + case style::TabAlign_DECIMAL : + tab_align = "decimal "; + break; + default: + break; + } + + const gchar * lead_char = ""; + + if( theTabStops[indexOfTab].FillChar != lastFillChar ) + { + lastFillChar = theTabStops[indexOfTab].FillChar; + switch (lastFillChar) + { + case (sal_Unicode) ' ': + lead_char = "blank "; + break; + + case (sal_Unicode) '.': + lead_char = "dotted "; + break; + + case (sal_Unicode) '-': + lead_char = "dashed "; + break; + + case (sal_Unicode) '_': + lead_char = "lined "; + break; + + default: + lead_char = "custom "; + break; + } + } + + gchar * tab_str = g_strdup_printf( "%s%s%gmm", lead_char, tab_align, fValue ); + + if( ret ) + { + gchar * old_tab_str = ret; + ret = g_strconcat(old_tab_str, " ", tab_str, NULL /* terminated */); + g_free( old_tab_str ); + } + else + ret = tab_str; + } + } + + return ret; +} + +static gchar * +TabStops2String( const uno::Any& rAny ) +{ + return TabStopList2String(rAny, false); +} + +static gchar * +DefaultTabStops2String( const uno::Any& rAny ) +{ + return TabStopList2String(rAny, true); +} + +/*****************************************************************************/ + +extern "C" int +attr_compare(const void *p1,const void *p2) +{ + const rtl_uString * pustr = (const rtl_uString *) p1; + const char * pc = *((const char **) p2); + + return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc); +} + +static void +find_exported_attributes( sal_Int32 *pArray, + const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& rAttributeList ) +{ + for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ ) + { + const char ** pAttr = (const char **) bsearch(rAttributeList[i].Name.pData, + ExportedTextAttributes, TEXT_ATTRIBUTE_LAST, sizeof(const char *), + attr_compare); + + if( pAttr ) + { + sal_Int32 nIndex = pAttr - ExportedTextAttributes; + pArray[nIndex] = i; + } + } +} + +/*****************************************************************************/ + +static AtkAttributeSet* +attribute_set_prepend( AtkAttributeSet* attribute_set, + AtkTextAttribute attribute, + gchar * value ) +{ + if( value ) + { + AtkAttribute *at = (AtkAttribute *) g_malloc( sizeof (AtkAttribute) ); + at->name = g_strdup( atk_text_attribute_get_name( attribute ) ); + at->value = value; + + return g_slist_prepend(attribute_set, at); + } + + return attribute_set; +} + +/*****************************************************************************/ + +AtkAttributeSet* +attribute_set_new_from_property_values( + const uno::Sequence< beans::PropertyValue >& rAttributeList, + bool run_attributes_only, + AtkText *text) +{ + AtkAttributeSet* attribute_set = NULL; + + sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 }; + + // Initialize index array with -1 + for( sal_Int32 attr = 0; attr < TEXT_ATTRIBUTE_LAST; ++attr ) + aIndexList[attr] = -1; + + find_exported_attributes(aIndexList, rAttributeList); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_BG_COLOR, + get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_BACKGROUND_COLOR, run_attributes_only ? NULL : text ) ); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FG_COLOR, + get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_FOREGROUND_COLOR, run_attributes_only ? NULL : text) ); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INVISIBLE, + get_bool_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HIDDEN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_UNDERLINE, + get_underline_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_UNDERLINE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRIKETHROUGH, + get_strikethrough_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SIZE, + get_height_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HEIGHT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_WEIGHT, + get_weight_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WEIGHT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FAMILY_NAME, + get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FONT_NAME])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_VARIANT, + get_variant_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CASEMAP])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STYLE, + get_style_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_POSTURE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SCALE, + get_scale_width(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SCALE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LANGUAGE, + get_language_string(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LOCALE])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_DIRECTION, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2Direction)); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRETCH, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_KERNING], Kerning2Stretch)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_font_effect ) + atk_text_attribute_font_effect = atk_text_attribute_register("font-effect"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_font_effect, + get_font_effect(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CONTOURED], aIndexList[TEXT_ATTRIBUTE_RELIEF])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_decoration ) + atk_text_attribute_decoration = atk_text_attribute_register("text-decoration"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_decoration, + get_text_decoration(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BLINKING], + aIndexList[TEXT_ATTRIBUTE_UNDERLINE], aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_rotation ) + atk_text_attribute_rotation = atk_text_attribute_register("text-rotation"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_rotation, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_ROTATION], Short2Degree)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_shadow ) + atk_text_attribute_shadow = atk_text_attribute_register("text-shadow"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_shadow, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SHADOWED], Bool2Shadow)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_writing_mode ) + atk_text_attribute_writing_mode = atk_text_attribute_register("writing-mode"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_writing_mode, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2String)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_vertical_align ) + atk_text_attribute_vertical_align = atk_text_attribute_register("vertical-align"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_vertical_align, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CHAR_ESCAPEMENT], Escapement2VerticalAlign)); + + if( run_attributes_only ) + return attribute_set; + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LEFT_MARGIN, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LEFT_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_RIGHT_MARGIN, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_RIGHT_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INDENT, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FIRST_LINE_INDENT])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TOP_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, + get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BOTTOM_MARGIN])); + + attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_JUSTIFICATION, + get_justification_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_JUSTIFICATION])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_paragraph_style ) + atk_text_attribute_paragraph_style = atk_text_attribute_register("paragraph-style"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_paragraph_style, + get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STYLE_NAME])); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_line_height ) + atk_text_attribute_line_height = atk_text_attribute_register("line-height"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_line_height, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LINE_SPACING], LineSpacing2LineHeight)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_interval ) + atk_text_attribute_tab_interval = atk_text_attribute_register("tab-interval"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_interval, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], DefaultTabStops2String)); + + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_stops ) + atk_text_attribute_tab_stops = atk_text_attribute_register("tab-stops"); + + attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_stops, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], TabStops2String)); + + // --> OD 2010-03-05 #i92233# + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_mm_to_pixel_ratio ) + atk_text_attribute_mm_to_pixel_ratio = atk_text_attribute_register("mm-to-pixel-ratio"); + + attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_mm_to_pixel_ratio, + get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO], Float2String)); + // <-- + + return attribute_set; +} + + +AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set ) +{ + if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_misspelled ) + atk_text_attribute_misspelled = atk_text_attribute_register( "text-spelling" ); + + attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_misspelled, + g_strdup_printf( "misspelled" ) ); + + return attribute_set; +} + +// --> OD 2010-03-01 #i92232# +AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "insertion" ) ); + + return attribute_set; +} + +AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "deletion" ) ); + + return attribute_set; +} + +AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set ) +{ + if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change ) + { + atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" ); + } + + attribute_set = attribute_set_prepend( attribute_set, + atk_text_attribute_tracked_change, + g_strdup_printf( "attribute-change" ) ); + + return attribute_set; +} +// <-- + +/*****************************************************************************/ + +struct AtkTextAttrMapping +{ + const char * name; + TextPropertyValueFunc toPropertyValue; +}; + +const AtkTextAttrMapping g_TextAttrMap[] = +{ + { "", InvalidValue }, // ATK_TEXT_ATTR_INVALID = 0 + { "ParaLeftMargin", UnitString2CMM }, // ATK_TEXT_ATTR_LEFT_MARGIN + { "ParaRightMargin", UnitString2CMM }, // ATK_TEXT_ATTR_RIGHT_MARGIN + { "ParaFirstLineIndent", UnitString2CMM }, // ATK_TEXT_ATTR_INDENT + { "CharHidden", String2Bool }, // ATK_TEXT_ATTR_INVISIBLE + { "", InvalidValue }, // ATK_TEXT_ATTR_EDITABLE + { "ParaTopMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES + { "ParaBottomMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_BELOW_LINES + { "", InvalidValue }, // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP + { "", InvalidValue }, // ATK_TEXT_ATTR_BG_FULL_HEIGHT + { "", InvalidValue }, // ATK_TEXT_ATTR_RISE + { "CharUnderline", String2Underline }, // ATK_TEXT_ATTR_UNDERLINE + { "CharStrikeout", String2Strikeout }, // ATK_TEXT_ATTR_STRIKETHROUGH + { "CharHeight", String2Float }, // ATK_TEXT_ATTR_SIZE + { "CharScaleWidth", String2Scale }, // ATK_TEXT_ATTR_SCALE + { "CharWeight", String2Weight }, // ATK_TEXT_ATTR_WEIGHT + { "CharLocale", String2Locale }, // ATK_TEXT_ATTR_LANGUAGE + { "CharFontName", SetString }, // ATK_TEXT_ATTR_FAMILY_NAME + { "CharBackColor", String2Color }, // ATK_TEXT_ATTR_BG_COLOR + { "CharColor", String2Color }, // ATK_TEXT_ATTR_FG_COLOR + { "", InvalidValue }, // ATK_TEXT_ATTR_BG_STIPPLE + { "", InvalidValue }, // ATK_TEXT_ATTR_FG_STIPPLE + { "", InvalidValue }, // ATK_TEXT_ATTR_WRAP_MODE + { "", InvalidValue }, // ATK_TEXT_ATTR_DIRECTION + { "ParaAdjust", Justification2Adjust }, // ATK_TEXT_ATTR_JUSTIFICATION + { "", InvalidValue }, // ATK_TEXT_ATTR_STRETCH + { "CharCaseMap", String2CaseMap }, // ATK_TEXT_ATTR_VARIANT + { "CharPosture", Style2FontSlant } // ATK_TEXT_ATTR_STYLE +}; + +static const sal_Int32 g_TextAttrMapSize = sizeof( g_TextAttrMap ) / sizeof( AtkTextAttrMapping ); + +/*****************************************************************************/ + +bool +attribute_set_map_to_property_values( + AtkAttributeSet* attribute_set, + uno::Sequence< beans::PropertyValue >& rValueList ) +{ + // Ensure enough space .. + uno::Sequence< beans::PropertyValue > aAttributeList (g_TextAttrMapSize); + + sal_Int32 nIndex = 0; + for( GSList * item = attribute_set; item != NULL; item = g_slist_next( item ) ) + { + AtkAttribute* attribute = (AtkAttribute *) item; + + AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name ); + if( text_attr < g_TextAttrMapSize ) + { + if( g_TextAttrMap[text_attr].name[0] != '\0' ) + { + if( ! g_TextAttrMap[text_attr].toPropertyValue( aAttributeList[nIndex].Value, attribute->value) ) + return false; + + aAttributeList[nIndex].Name = rtl::OUString::createFromAscii( g_TextAttrMap[text_attr].name ); + aAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE; + ++nIndex; + } + } + else + { + // Unsupported text attribute + return false; + } + } + + aAttributeList.realloc( nIndex ); + rValueList = aAttributeList; + return true; +} + diff --git a/vcl/unx/gtk/a11y/atktextattributes.hxx b/vcl/unx/gtk/a11y/atktextattributes.hxx new file mode 100644 index 000000000000..9c7628bf927e --- /dev/null +++ b/vcl/unx/gtk/a11y/atktextattributes.hxx @@ -0,0 +1,54 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_ATKTEXTATTRIBUTES_HXX__ +#define __ATK_ATKTEXTATTRIBUTES_HXX__ + +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <atk/atk.h> + +AtkAttributeSet* +attribute_set_new_from_property_values( + const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& rAttributeList, + bool run_attributes_only, + AtkText *text); + +bool +attribute_set_map_to_property_values( + AtkAttributeSet* attribute_set, + com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& rValueList ); + +AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set ); +// --> OD 2010-03-01 #i92232# +AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set ); +AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set ); +AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set ); +// <-- + +#endif diff --git a/vcl/unx/gtk/a11y/atkutil.cxx b/vcl/unx/gtk/a11y/atkutil.cxx new file mode 100644 index 000000000000..076e36291ae6 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkutil.cxx @@ -0,0 +1,801 @@ +/************************************************************************* + * + * 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/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +// --> OD 2009-04-14 #i93269# +#include <com/sun/star/accessibility/XAccessibleText.hpp> +// <-- +#include <cppuhelper/implbase1.hxx> +#include <vos/mutex.hxx> +#include <rtl/ref.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <vcl/menu.hxx> +#include <vcl/toolbox.hxx> + +#include "atkwrapper.hxx" +#include "atkutil.hxx" + +#include <gtk/gtk.h> + +#include <set> + +// #define ENABLE_TRACING + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +using namespace ::com::sun::star; + +static uno::WeakReference< accessibility::XAccessible > xNextFocusObject; +static guint focus_notify_handler = 0; + +/*****************************************************************************/ + +extern "C" { + +static gint +atk_wrapper_focus_idle_handler (gpointer data) +{ + vos::OGuard aGuard( Application::GetSolarMutex() ); + + focus_notify_handler = 0; + + uno::Reference< accessibility::XAccessible > xAccessible = xNextFocusObject; + if( xAccessible.get() == reinterpret_cast < accessibility::XAccessible * > (data) ) + { + AtkObject *atk_obj = xAccessible.is() ? atk_object_wrapper_ref( xAccessible ) : NULL; + // Gail does not notify focus changes to NULL, so do we .. + if( atk_obj ) + { +#ifdef ENABLE_TRACING + fprintf(stderr, "notifying focus event for %p\n", atk_obj); +#endif + atk_focus_tracker_notify(atk_obj); + // --> OD 2009-04-14 #i93269# + // emit text_caret_moved event for <XAccessibleText> object, + // if cursor is inside the <XAccessibleText> object. + // also emit state-changed:focused event under the same condition. + { + AtkObjectWrapper* wrapper_obj = ATK_OBJECT_WRAPPER (atk_obj); + if( wrapper_obj && !wrapper_obj->mpText && wrapper_obj->mpContext ) + { + uno::Any any = wrapper_obj->mpContext->queryInterface( accessibility::XAccessibleText::static_type(NULL) ); + if ( typelib_TypeClass_INTERFACE == any.pType->eTypeClass && + any.pReserved != 0 ) + { + wrapper_obj->mpText = reinterpret_cast< accessibility::XAccessibleText * > (any.pReserved); + if ( wrapper_obj->mpText != 0 ) + { + wrapper_obj->mpText->acquire(); + gint caretPos = wrapper_obj->mpText->getCaretPosition(); + + if ( caretPos != -1 ) + { + atk_object_notify_state_change( atk_obj, ATK_STATE_FOCUSED, TRUE ); + g_signal_emit_by_name( atk_obj, "text_caret_moved", caretPos ); + } + } + } + } + } + // <-- + g_object_unref(atk_obj); + } + } + + return FALSE; +} + +} // extern "C" + +/*****************************************************************************/ + +static void +atk_wrapper_focus_tracker_notify_when_idle( const uno::Reference< accessibility::XAccessible > &xAccessible ) +{ + if( focus_notify_handler ) + g_source_remove(focus_notify_handler); + + xNextFocusObject = xAccessible; + + focus_notify_handler = g_idle_add (atk_wrapper_focus_idle_handler, xAccessible.get()); +} + +/*****************************************************************************/ + +class DocumentFocusListener : + public ::cppu::WeakImplHelper1< accessibility::XAccessibleEventListener > +{ + + std::set< uno::Reference< uno::XInterface > > m_aRefList; + +public: + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + void attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + void detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + void detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + void detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet + ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + static uno::Reference< accessibility::XAccessible > getAccessible(const lang::EventObject& aEvent ) + throw (lang::IndexOutOfBoundsException, uno::RuntimeException); + + // XEventListener + virtual void disposing( const lang::EventObject& Source ) throw (uno::RuntimeException); + + // XAccessibleEventListener + virtual void notifyEvent( const accessibility::AccessibleEventObject& aEvent ) throw( uno::RuntimeException ); +}; + +/*****************************************************************************/ + +void DocumentFocusListener::disposing( const lang::EventObject& aEvent ) + throw (uno::RuntimeException) +{ +// fprintf(stderr, "In DocumentFocusListener::disposing (%p)\n", this); +// fprintf(stderr, "m_aRefList has %d entries\n", m_aRefList.size()); + + // Unref the object here, but do not remove as listener since the object + // might no longer be in a state that safely allows this. + if( aEvent.Source.is() ) + m_aRefList.erase(aEvent.Source); + +// fprintf(stderr, "m_aRefList has %d entries\n", m_aRefList.size()); + +} + +/*****************************************************************************/ + +void DocumentFocusListener::notifyEvent( const accessibility::AccessibleEventObject& aEvent ) + throw( uno::RuntimeException ) +{ + switch( aEvent.EventId ) + { + case accessibility::AccessibleEventId::STATE_CHANGED: + try + { + sal_Int16 nState = accessibility::AccessibleStateType::INVALID; + aEvent.NewValue >>= nState; + + if( accessibility::AccessibleStateType::FOCUSED == nState ) + atk_wrapper_focus_tracker_notify_when_idle( getAccessible(aEvent) ); + } + catch(const lang::IndexOutOfBoundsException &e) + { + g_warning("Focused object has invalid index in parent"); + } + break; + + case accessibility::AccessibleEventId::CHILD: + { + uno::Reference< accessibility::XAccessible > xChild; + if( (aEvent.OldValue >>= xChild) && xChild.is() ) + detachRecursive(xChild); + + if( (aEvent.NewValue >>= xChild) && xChild.is() ) + attachRecursive(xChild); + } + break; + + case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN: +/* { + uno::Reference< accessibility::XAccessible > xAccessible( getAccessible(aEvent) ); + detachRecursive(xAccessible); + attachRecursive(xAccessible); + } +*/ + g_warning( "Invalidate all children called\n" ); + break; + default: + break; + } +} + +/*****************************************************************************/ + +uno::Reference< accessibility::XAccessible > DocumentFocusListener::getAccessible(const lang::EventObject& aEvent ) + throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessible > xAccessible(aEvent.Source, uno::UNO_QUERY); + + if( xAccessible.is() ) + return xAccessible; + + uno::Reference< accessibility::XAccessibleContext > xContext(aEvent.Source, uno::UNO_QUERY); + + if( xContext.is() ) + { + uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); + if( xParent.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() ); + if( xParentContext.is() ) + { + return xParentContext->getAccessibleChild( xContext->getAccessibleIndexInParent() ); + } + } + } + + return uno::Reference< accessibility::XAccessible >(); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( xContext.is() ) + attachRecursive(xAccessible, xContext); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + attachRecursive(xAccessible, xContext, xStateSet); +} + +/*****************************************************************************/ + +void DocumentFocusListener::attachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED ) ) + atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); + + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster = + uno::Reference< accessibility::XAccessibleEventBroadcaster >(xContext, uno::UNO_QUERY); + + // If not already done, add the broadcaster to the list and attach as listener. + if( xBroadcaster.is() && m_aRefList.insert(xBroadcaster).second ) + { + xBroadcaster->addEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + attachRecursive(xChild); + } + } + } +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( xContext.is() ) + detachRecursive(xAccessible, xContext); +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessible >& xAccessible, + const uno::Reference< accessibility::XAccessibleContext >& xContext +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( xStateSet.is() ) + detachRecursive(xAccessible, xContext, xStateSet); +} + +/*****************************************************************************/ + +void DocumentFocusListener::detachRecursive( + const uno::Reference< accessibility::XAccessible >&, + const uno::Reference< accessibility::XAccessibleContext >& xContext, + const uno::Reference< accessibility::XAccessibleStateSet >& xStateSet +) throw (lang::IndexOutOfBoundsException, uno::RuntimeException) +{ + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster = + uno::Reference< accessibility::XAccessibleEventBroadcaster >(xContext, uno::UNO_QUERY); + + if( xBroadcaster.is() && 0 < m_aRefList.erase(xBroadcaster) ) + { + xBroadcaster->removeEventListener(static_cast< accessibility::XAccessibleEventListener *>(this)); + + if( ! xStateSet->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS ) ) + { + sal_Int32 n, nmax = xContext->getAccessibleChildCount(); + for( n = 0; n < nmax; n++ ) + { + uno::Reference< accessibility::XAccessible > xChild( xContext->getAccessibleChild( n ) ); + + if( xChild.is() ) + detachRecursive(xChild); + } + } + } +} + +/*****************************************************************************/ + +/* + * page tabs in gtk are widgets, so we need to simulate focus events for those + */ + +static void handle_tabpage_activated(Window *pWindow) +{ + uno::Reference< accessibility::XAccessible > xAccessible = + pWindow->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleSelection > xSelection( + xAccessible->getAccessibleContext(), uno::UNO_QUERY); + + if( xSelection.is() ) + atk_wrapper_focus_tracker_notify_when_idle( xSelection->getSelectedAccessibleChild(0) ); +} + +/*****************************************************************************/ + +/* + * toolbar items in gtk are widgets, so we need to simulate focus events for those + */ + +static void notify_toolbox_item_focus(ToolBox *pToolBox) +{ + uno::Reference< accessibility::XAccessible > xAccessible = + pToolBox->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( ! xContext.is() ) + return; + + sal_Int32 nPos = pToolBox->GetItemPos( pToolBox->GetHighlightItemId() ); + if( nPos != TOOLBOX_ITEM_NOTFOUND ) + atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); +} + +static void handle_toolbox_highlight(Window *pWindow) +{ + ToolBox *pToolBox = static_cast <ToolBox *> (pWindow); + + // Make sure either the toolbox or its parent toolbox has the focus + if ( ! pToolBox->HasFocus() ) + { + ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pToolBox->GetParent() ); + if ( ! pToolBoxParent || ! pToolBoxParent->HasFocus() ) + return; + } + + notify_toolbox_item_focus(pToolBox); +} + +static void handle_toolbox_highlightoff(Window *pWindow) +{ + ToolBox *pToolBox = static_cast <ToolBox *> (pWindow); + ToolBox* pToolBoxParent = dynamic_cast< ToolBox* >( pToolBox->GetParent() ); + + // Notify when leaving sub toolboxes + if( pToolBoxParent && pToolBoxParent->HasFocus() ) + notify_toolbox_item_focus( pToolBoxParent ); +} + +/*****************************************************************************/ + +static void create_wrapper_for_child( + const uno::Reference< accessibility::XAccessibleContext >& xContext, + sal_Int32 index) +{ + if( xContext.is() ) + { + uno::Reference< accessibility::XAccessible > xChild(xContext->getAccessibleChild(index)); + if( xChild.is() ) + { + // create the wrapper object - it will survive the unref unless it is a transient object + g_object_unref( atk_object_wrapper_ref( xChild ) ); + } + } +} + +/*****************************************************************************/ + +static void handle_toolbox_buttonchange(VclWindowEvent const *pEvent) +{ + Window* pWindow = pEvent->GetWindow(); + sal_Int32 index = (sal_Int32)(sal_IntPtr) pEvent->GetData(); + + if( pWindow && pWindow->IsReallyVisible() ) + { + uno::Reference< accessibility::XAccessible > xAccessible(pWindow->GetAccessible()); + if( xAccessible.is() ) + { + create_wrapper_for_child(xAccessible->getAccessibleContext(), index); + } + } +} + +/*****************************************************************************/ + +/* currently not needed anymore... +static void create_wrapper_for_children(Window *pWindow) +{ + if( pWindow && pWindow->IsReallyVisible() ) + { + uno::Reference< accessibility::XAccessible > xAccessible(pWindow->GetAccessible()); + if( xAccessible.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xContext(xAccessible->getAccessibleContext()); + if( xContext.is() ) + { + sal_Int32 nChildren = xContext->getAccessibleChildCount(); + for( sal_Int32 i = 0; i < nChildren; ++i ) + create_wrapper_for_child(xContext, i); + } + } + } +} +*/ + +/*****************************************************************************/ + +static std::set< Window * > g_aWindowList; + +static void handle_get_focus(::VclWindowEvent const * pEvent) +{ + static rtl::Reference< DocumentFocusListener > aDocumentFocusListener = + new DocumentFocusListener(); + + Window *pWindow = pEvent->GetWindow(); + + // The menu bar is handled through VCLEVENT_MENU_HIGHLIGHTED + if( ! pWindow || !pWindow->IsReallyVisible() || pWindow->GetType() == WINDOW_MENUBARWINDOW ) + return; + + // ToolBoxes are handled through VCLEVENT_TOOLBOX_HIGHLIGHT + if( pWindow->GetType() == WINDOW_TOOLBOX ) + return; + + if( pWindow->GetType() == WINDOW_TABCONTROL ) + { + handle_tabpage_activated( pWindow ); + return; + } + + uno::Reference< accessibility::XAccessible > xAccessible = + pWindow->GetAccessible(); + + if( ! xAccessible.is() ) + return; + + uno::Reference< accessibility::XAccessibleContext > xContext = + xAccessible->getAccessibleContext(); + + if( ! xContext.is() ) + return; + + uno::Reference< accessibility::XAccessibleStateSet > xStateSet = + xContext->getAccessibleStateSet(); + + if( ! xStateSet.is() ) + return; + +/* the UNO ToolBox wrapper does not (yet?) support XAccessibleSelection, so we + * need to add listeners to the children instead of re-using the tabpage stuff + */ + if( xStateSet->contains(accessibility::AccessibleStateType::FOCUSED) && + ( pWindow->GetType() != WINDOW_TREELISTBOX ) ) + { + atk_wrapper_focus_tracker_notify_when_idle( xAccessible ); + } + else + { + if( g_aWindowList.find(pWindow) == g_aWindowList.end() ) + { + g_aWindowList.insert(pWindow); + try + { + aDocumentFocusListener->attachRecursive(xAccessible, xContext, xStateSet); + } + catch( const uno::Exception &e ) + { + g_warning( "Exception caught processing focus events" ); + } + } +#ifdef ENABLE_TRACING + else + fprintf(stderr, "Window %p already in the list\n", pWindow ); +#endif + } +} + +/*****************************************************************************/ + +static void handle_menu_highlighted(::VclMenuEvent const * pEvent) +{ + try + { + Menu* pMenu = pEvent->GetMenu(); + USHORT nPos = pEvent->GetItemPos(); + + if( pMenu && nPos != 0xFFFF) + { + uno::Reference< accessibility::XAccessible > xAccessible ( pMenu->GetAccessible() ); + + if( xAccessible.is() ) + { + uno::Reference< accessibility::XAccessibleContext > xContext ( xAccessible->getAccessibleContext() ); + + if( xContext.is() ) + atk_wrapper_focus_tracker_notify_when_idle( xContext->getAccessibleChild( nPos ) ); + } + } + } + catch( const uno::Exception& e ) + { + g_warning( "Exception caught processing menu highlight events" ); + } +} + +/*****************************************************************************/ + +long WindowEventHandler(void *, ::VclSimpleEvent const * pEvent) +{ + switch (pEvent->GetId()) + { + case VCLEVENT_WINDOW_SHOW: +// fprintf(stderr, "got VCLEVENT_WINDOW_SHOW for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_HIDE: +// fprintf(stderr, "got VCLEVENT_WINDOW_HIDE for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_CLOSE: +// fprintf(stderr, "got VCLEVENT_WINDOW_CLOSE for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_GETFOCUS: + handle_get_focus(static_cast< ::VclWindowEvent const * >(pEvent)); + break; + case VCLEVENT_WINDOW_LOSEFOCUS: +// fprintf(stderr, "got VCLEVENT_WINDOW_LOSEFOCUS for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_MINIMIZE: +// fprintf(stderr, "got VCLEVENT_WINDOW_MINIMIZE for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_NORMALIZE: +// fprintf(stderr, "got VCLEVENT_WINDOW_NORMALIZE for %p\n", +// static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + case VCLEVENT_WINDOW_KEYINPUT: + case VCLEVENT_WINDOW_KEYUP: + case VCLEVENT_WINDOW_COMMAND: + case VCLEVENT_WINDOW_MOUSEMOVE: + break; + /* + fprintf(stderr, "got VCLEVENT_WINDOW_COMMAND (%d) for %p\n", + static_cast< ::CommandEvent const * > ( + static_cast< ::VclWindowEvent const * >(pEvent)->GetData())->GetCommand(), + static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + */ + case VCLEVENT_MENU_HIGHLIGHT: + if (const VclMenuEvent* pMenuEvent = dynamic_cast<const VclMenuEvent*>(pEvent)) + { + handle_menu_highlighted(pMenuEvent); + } + else if (const VclAccessibleEvent* pAccEvent = dynamic_cast<const VclAccessibleEvent*>(pEvent)) + { + uno::Reference< accessibility::XAccessible > xAccessible = pAccEvent->GetAccessible(); + if (xAccessible.is()) + atk_wrapper_focus_tracker_notify_when_idle(xAccessible); + } + break; + + case VCLEVENT_TOOLBOX_HIGHLIGHT: + handle_toolbox_highlight(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + + case VCLEVENT_TOOLBOX_BUTTONSTATECHANGED: + handle_toolbox_buttonchange(static_cast< ::VclWindowEvent const * >(pEvent)); + break; + + case VCLEVENT_OBJECT_DYING: + g_aWindowList.erase( static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow() ); + // fallthrough intentional ! + case VCLEVENT_TOOLBOX_HIGHLIGHTOFF: + handle_toolbox_highlightoff(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + + case VCLEVENT_TABPAGE_ACTIVATE: + handle_tabpage_activated(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + + case VCLEVENT_COMBOBOX_SETTEXT: + // MT 2010/02: This looks quite strange to me. Stumbled over this when fixing #i104290#. + // This kicked in when leaving the combobox in the toolbar, after that the events worked. + // I guess this was a try to work around missing combobox events, which didn't do the full job, and shouldn't be necessary anymore. + // Fix for #i104290# was done in toolkit/source/awt/vclxaccessiblecomponent, FOCUSED state for compound controls in general. + // create_wrapper_for_children(static_cast< ::VclWindowEvent const * >(pEvent)->GetWindow()); + break; + + default: +// OSL_TRACE("got event %d \n", pEvent->GetId()); + break; + } + return 0; +} + +static Link g_aEventListenerLink( NULL, (PSTUB) WindowEventHandler ); + +/*****************************************************************************/ + +extern "C" { + +static G_CONST_RETURN gchar * +ooo_atk_util_get_toolkit_name (void) +{ + return "VCL"; +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar * +ooo_atk_util_get_toolkit_version (void) +{ + /* + * Version is passed in as a -D flag when this file is + * compiled. + */ + + return VERSION; +} + +/*****************************************************************************/ + +/* + * GObject inheritance + */ + +static void +ooo_atk_util_class_init (AtkUtilClass *) +{ + AtkUtilClass *atk_class; + gpointer data; + + data = g_type_class_peek (ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS (data); + + atk_class->get_toolkit_name = ooo_atk_util_get_toolkit_name; + atk_class->get_toolkit_version = ooo_atk_util_get_toolkit_version; + + Application::AddEventListener( g_aEventListenerLink ); +} + +} // extern "C" + +/*****************************************************************************/ + +GType +ooo_atk_util_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GType parent_type = g_type_from_name( "GailUtil" ); + + if( ! parent_type ) + { + g_warning( "Unknown type: GailUtil" ); + parent_type = ATK_TYPE_UTIL; + } + + GTypeQuery type_query; + g_type_query( parent_type, &type_query ); + + static const GTypeInfo typeInfo = + { + type_query.class_size, + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) ooo_atk_util_class_init, + (GClassFinalizeFunc) NULL, + NULL, + type_query.instance_size, + 0, + (GInstanceInitFunc) NULL, + NULL + } ; + + type = g_type_register_static (parent_type, "OOoUtil", &typeInfo, (GTypeFlags)0) ; + } + + return type; +} + + diff --git a/vcl/unx/gtk/a11y/atkutil.hxx b/vcl/unx/gtk/a11y/atkutil.hxx new file mode 100644 index 000000000000..8c8ddf59c65f --- /dev/null +++ b/vcl/unx/gtk/a11y/atkutil.hxx @@ -0,0 +1,37 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_UTIL_HXX__ +#define __ATK_UTIL_HXX__ + +#include <atk/atk.h> + +#define OOO_TYPE_ATK_UTIL ooo_atk_util_get_type() + +GType ooo_atk_util_get_type (void); + +#endif diff --git a/vcl/unx/gtk/a11y/atkvalue.cxx b/vcl/unx/gtk/a11y/atkvalue.cxx new file mode 100644 index 000000000000..9b8e9743eb18 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkvalue.cxx @@ -0,0 +1,147 @@ +/************************************************************************* + * + * 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 "atkwrapper.hxx" + +#include <com/sun/star/accessibility/XAccessibleValue.hpp> + +#include <stdio.h> +#include <string.h> + +using namespace ::com::sun::star; + +static accessibility::XAccessibleValue* + getValue( AtkValue *pValue ) throw (uno::RuntimeException) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pValue ); + if( pWrap ) + { + if( !pWrap->mpValue && pWrap->mpContext ) + { + uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleValue::static_type(NULL) ); + pWrap->mpValue = reinterpret_cast< accessibility::XAccessibleValue * > (any.pReserved); + pWrap->mpValue->acquire(); + } + + return pWrap->mpValue; + } + + return NULL; +} + +static void anyToGValue( uno::Any aAny, GValue *pValue ) +{ + // FIXME: expand to lots of types etc. + double aDouble=0; + aAny >>= aDouble; + + memset( pValue, 0, sizeof( GValue ) ); + g_value_init( pValue, G_TYPE_DOUBLE ); + g_value_set_double( pValue, aDouble ); +} + +extern "C" { + +static void +value_wrapper_get_current_value( AtkValue *value, + GValue *gval ) +{ + try { + accessibility::XAccessibleValue* pValue = getValue( value ); + if( pValue ) + anyToGValue( pValue->getCurrentValue(), gval ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static void +value_wrapper_get_maximum_value( AtkValue *value, + GValue *gval ) +{ + try { + accessibility::XAccessibleValue* pValue = getValue( value ); + if( pValue ) + anyToGValue( pValue->getMaximumValue(), gval ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static void +value_wrapper_get_minimum_value( AtkValue *value, + GValue *gval ) +{ + try { + accessibility::XAccessibleValue* pValue = getValue( value ); + if( pValue ) + anyToGValue( pValue->getMinimumValue(), gval ); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCurrentValue()" ); + } +} + +static gboolean +value_wrapper_set_current_value( AtkValue *value, + const GValue *gval ) +{ + try { + accessibility::XAccessibleValue* pValue = getValue( value ); + if( pValue ) + { + // FIXME - this needs expanding + double aDouble = g_value_get_double( gval ); + uno::Any aAny; + aAny <<= aDouble; + return pValue->setCurrentValue( aAny ); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getCurrentValue()" ); + } + + return FALSE; +} + +} // extern "C" + +void +valueIfaceInit (AtkValueIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_current_value = value_wrapper_get_current_value; + iface->get_maximum_value = value_wrapper_get_maximum_value; + iface->get_minimum_value = value_wrapper_get_minimum_value; + iface->set_current_value = value_wrapper_set_current_value; +} diff --git a/vcl/unx/gtk/a11y/atkwindow.cxx b/vcl/unx/gtk/a11y/atkwindow.cxx new file mode 100644 index 000000000000..5448235998e8 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwindow.cxx @@ -0,0 +1,331 @@ +/************************************************************************* + * + * 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 <plugins/gtk/gtkframe.hxx> +#include <vcl/window.hxx> +#include "vcl/popupmenuwindow.hxx" + +#include "atkwindow.hxx" +#include "atkwrapper.hxx" +#include "atkregistry.hxx" + +#include <com/sun/star/accessibility/AccessibleRole.hpp> + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::uno; + +extern "C" { + +static void (* window_real_initialize) (AtkObject *obj, gpointer data) = NULL; +static void (* window_real_finalize) (GObject *obj) = NULL; + +static void +init_from_window( AtkObject *accessible, Window *pWindow ) +{ + static AtkRole aDefaultRole = ATK_ROLE_INVALID; + + // Special role for sub-menu and combo-box popups that are exposed directly + // by their parents already. + if( aDefaultRole == ATK_ROLE_INVALID ) + aDefaultRole = atk_role_register( "redundant object" ); + + AtkRole role = aDefaultRole; + + // Determine the appropriate role for the GtkWindow + switch( pWindow->GetAccessibleRole() ) + { + case AccessibleRole::ALERT: + role = ATK_ROLE_ALERT; + break; + + case AccessibleRole::DIALOG: + role = ATK_ROLE_DIALOG; + break; + + case AccessibleRole::FRAME: + role = ATK_ROLE_FRAME; + break; + + /* Ignore window objects for sub-menus, combo- and list boxes, + * which are exposed as children of their parents. + */ + case AccessibleRole::WINDOW: + { + USHORT type = WINDOW_WINDOW; + bool parentIsMenuFloatingWindow = false; + + Window *pParent = pWindow->GetParent(); + if( pParent ) { + type = pParent->GetType(); + parentIsMenuFloatingWindow = ( TRUE == pParent->IsMenuFloatingWindow() ); + } + + if( (WINDOW_LISTBOX != type) && (WINDOW_COMBOBOX != type) && + (WINDOW_MENUBARWINDOW != type) && ! parentIsMenuFloatingWindow ) + { + role = ATK_ROLE_WINDOW; + } + } + break; + + default: + { + Window *pChild = pWindow->GetChild( 0 ); + if( pChild ) + { + if( WINDOW_HELPTEXTWINDOW == pChild->GetType() ) + { + role = ATK_ROLE_TOOL_TIP; + pChild->SetAccessibleRole( AccessibleRole::LABEL ); + accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + else if ( pWindow->GetType() == WINDOW_BORDERWINDOW && pChild->GetType() == WINDOW_FLOATINGWINDOW ) + { + PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); + if (p && p->IsPopupMenu() && p->GetMenuStackLevel() == 0) + { + // This is a top-level menu popup. Register it. + role = ATK_ROLE_POPUP_MENU; + pChild->SetAccessibleRole( AccessibleRole::POPUP_MENU ); + accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + } + } + break; + } + } + + accessible->role = role; +} + +/*****************************************************************************/ + +static gint +ooo_window_wrapper_clear_focus(gpointer) +{ + atk_focus_tracker_notify( NULL ); + return FALSE; +} + +/*****************************************************************************/ + +static gboolean +ooo_window_wrapper_real_focus_gtk (GtkWidget *, GdkEventFocus *) +{ + g_idle_add( ooo_window_wrapper_clear_focus, NULL ); + return FALSE; +} + +static gboolean ooo_tooltip_map( GtkWidget* pToolTip, gpointer ) +{ + AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); + if( pAccessible ) + atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, TRUE ); + return FALSE; +} + +static gboolean ooo_tooltip_unmap( GtkWidget* pToolTip, gpointer ) +{ + AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip ); + if( pAccessible ) + atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, FALSE ); + return FALSE; +} + +/*****************************************************************************/ + +static bool +isChildPopupMenu(Window* pWindow) +{ + Window* pChild = pWindow->GetAccessibleChildWindow(0); + if (!pChild) + return false; + + if (WINDOW_FLOATINGWINDOW != pChild->GetType()) + return false; + + PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild); + if (!p) + return false; + + return p->IsPopupMenu(); +} + +static void +ooo_window_wrapper_real_initialize(AtkObject *obj, gpointer data) +{ + window_real_initialize(obj, data); + + GtkSalFrame *pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( data ) ); + if( pFrame ) + { + Window *pWindow = pFrame->GetWindow(); + if( pWindow ) + { + init_from_window( obj, pWindow ); + + Reference< XAccessible > xAccessible( pWindow->GetAccessible(true) ); + + /* We need the wrapper object for the top-level XAccessible to be + * in the wrapper registry when atk traverses the hierachy up on + * focus events + */ + if( WINDOW_BORDERWINDOW == pWindow->GetType() ) + { + if ( isChildPopupMenu(pWindow) ) + { + AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); + ooo_wrapper_registry_add( xAccessible, child ); + } + else + { + ooo_wrapper_registry_add( xAccessible, obj ); + g_object_set_data( G_OBJECT(obj), "ooo:atk-wrapper-key", xAccessible.get() ); + } + } + else + { + AtkObject *child = atk_object_wrapper_new( xAccessible, obj ); + child->role = ATK_ROLE_FILLER; + if( (ATK_ROLE_DIALOG == obj->role) || (ATK_ROLE_ALERT == obj->role) ) + child->role = ATK_ROLE_OPTION_PANE; + ooo_wrapper_registry_add( xAccessible, child ); + } + } + } + + g_signal_connect_after( GTK_WIDGET( data ), "focus-out-event", + G_CALLBACK (ooo_window_wrapper_real_focus_gtk), + NULL); + + if( obj->role == ATK_ROLE_TOOL_TIP ) + { + g_signal_connect_after( GTK_WIDGET( data ), "map-event", + G_CALLBACK (ooo_tooltip_map), + NULL); + g_signal_connect_after( GTK_WIDGET( data ), "unmap-event", + G_CALLBACK (ooo_tooltip_unmap), + NULL); + } +} + +/*****************************************************************************/ + +static void +ooo_window_wrapper_real_finalize (GObject *obj) +{ + ooo_wrapper_registry_remove( (XAccessible *) g_object_get_data( obj, "ooo:atk-wrapper-key" )); + window_real_finalize( obj ); +} + +/*****************************************************************************/ + +static void +ooo_window_wrapper_class_init (AtkObjectClass *klass, gpointer) +{ + AtkObjectClass *atk_class; + GObjectClass *gobject_class; + gpointer data; + + /* + * Patch the gobject vtable of GailWindow to refer to our instance of + * "initialize". + */ + + data = g_type_class_peek_parent( klass ); + atk_class = ATK_OBJECT_CLASS (data); + + window_real_initialize = atk_class->initialize; + atk_class->initialize = ooo_window_wrapper_real_initialize; + + gobject_class = G_OBJECT_CLASS (data); + + window_real_finalize = gobject_class->finalize; + gobject_class->finalize = ooo_window_wrapper_real_finalize; +} + +} // extern "C" + +/*****************************************************************************/ + +GType +ooo_window_wrapper_get_type (void) +{ + static GType type = 0; + + if (!type) + { + GType parent_type = g_type_from_name( "GailWindow" ); + + if( ! parent_type ) + { + g_warning( "Unknown type: GailWindow" ); + parent_type = ATK_TYPE_OBJECT; + } + + GTypeQuery type_query; + g_type_query( parent_type, &type_query ); + + static const GTypeInfo typeInfo = + { + type_query.class_size, + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) ooo_window_wrapper_class_init, + (GClassFinalizeFunc) NULL, + NULL, + type_query.instance_size, + 0, + (GInstanceInitFunc) NULL, + NULL + } ; + + type = g_type_register_static (parent_type, "OOoWindowAtkObject", &typeInfo, (GTypeFlags)0) ; + } + + return type; +} + +void restore_gail_window_vtable (void) +{ + AtkObjectClass *atk_class; + gpointer data; + + GType type = g_type_from_name( "GailWindow" ); + + if( type == G_TYPE_INVALID ) + return; + + data = g_type_class_peek( type ); + atk_class = ATK_OBJECT_CLASS (data); + + atk_class->initialize = window_real_initialize; +} + diff --git a/vcl/unx/gtk/a11y/atkwindow.hxx b/vcl/unx/gtk/a11y/atkwindow.hxx new file mode 100644 index 000000000000..6a9862256999 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwindow.hxx @@ -0,0 +1,38 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_WINDOW_HXX__ +#define __ATK_WINDOW_HXX__ + +#include <atk/atk.h> + +#define OOO_TYPE_WINDOW_WRAPPER ooo_window_wrapper_get_type() + +GType ooo_window_wrapper_get_type (void); +void restore_gail_window_vtable (void); + +#endif diff --git a/vcl/unx/gtk/a11y/atkwrapper.cxx b/vcl/unx/gtk/a11y/atkwrapper.cxx new file mode 100644 index 000000000000..10f75309708d --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwrapper.cxx @@ -0,0 +1,953 @@ +/************************************************************************* + * + * 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, + ATK_ROLE_SCROLL_PANE, // COMMENT - mapped to atk_role_scroll_pane + ATK_ROLE_UNKNOWN // COMMENT_END - mapped to atk_role_unknown + }; + + 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 ) +} diff --git a/vcl/unx/gtk/a11y/atkwrapper.hxx b/vcl/unx/gtk/a11y/atkwrapper.hxx new file mode 100644 index 000000000000..4252c0404833 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwrapper.hxx @@ -0,0 +1,125 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#ifndef __ATK_WRAPPER_HXX__ +#define __ATK_WRAPPER_HXX__ + +#include <atk/atk.h> +#include <com/sun/star/accessibility/XAccessible.hpp> + +extern "C" { + +typedef struct _AtkObjectWrapper AtkObjectWrapper; +typedef struct _AtkObjectWrapperClass AtkObjectWrapperClass; + +namespace com { namespace sun { namespace star { namespace accessibility { + class XAccessibleAction; + class XAccessibleComponent; + class XAccessibleEditableText; + class XAccessibleHypertext; + class XAccessibleImage; + class XAccessibleMultiLineText; + class XAccessibleSelection; + class XAccessibleTable; + class XAccessibleText; + class XAccessibleTextMarkup; + class XAccessibleTextAttributes; + class XAccessibleValue; +} } } } + + +struct _AtkObjectWrapper +{ + AtkObject aParent; + + ::com::sun::star::accessibility::XAccessible *mpAccessible; + ::com::sun::star::accessibility::XAccessibleContext *mpContext; + ::com::sun::star::accessibility::XAccessibleAction *mpAction; + ::com::sun::star::accessibility::XAccessibleComponent *mpComponent; + ::com::sun::star::accessibility::XAccessibleEditableText *mpEditableText; + ::com::sun::star::accessibility::XAccessibleHypertext *mpHypertext; + ::com::sun::star::accessibility::XAccessibleImage *mpImage; + ::com::sun::star::accessibility::XAccessibleMultiLineText *mpMultiLineText; + ::com::sun::star::accessibility::XAccessibleSelection *mpSelection; + ::com::sun::star::accessibility::XAccessibleTable *mpTable; + ::com::sun::star::accessibility::XAccessibleText *mpText; + ::com::sun::star::accessibility::XAccessibleTextMarkup *mpTextMarkup; + ::com::sun::star::accessibility::XAccessibleTextAttributes *mpTextAttributes; + ::com::sun::star::accessibility::XAccessibleValue *mpValue; + + AtkObject *child_about_to_be_removed; + gint index_of_child_about_to_be_removed; +// ::rtl::OString * m_pKeyBindings +}; + +struct _AtkObjectWrapperClass +{ + AtkObjectClass aParentClass; +}; + +GType atk_object_wrapper_get_type (void) G_GNUC_CONST; +AtkObject * atk_object_wrapper_ref( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible, + bool create = true ); + +AtkObject * atk_object_wrapper_new( + const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible, + AtkObject* parent = NULL ); + +void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index); +void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index); + +void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper); + +AtkStateType mapAtkState( sal_Int16 nState ); + +void actionIfaceInit(AtkActionIface *iface); +void componentIfaceInit(AtkComponentIface *iface); +void editableTextIfaceInit(AtkEditableTextIface *iface); +void hypertextIfaceInit(AtkHypertextIface *iface); +void imageIfaceInit(AtkImageIface *iface); +void selectionIfaceInit(AtkSelectionIface *iface); +void tableIfaceInit(AtkTableIface *iface); +void textIfaceInit(AtkTextIface *iface); +void valueIfaceInit(AtkValueIface *iface); + +} // extern "C" + +#define ATK_TYPE_OBJECT_WRAPPER atk_object_wrapper_get_type() +#define ATK_OBJECT_WRAPPER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), ATK_TYPE_OBJECT_WRAPPER, AtkObjectWrapper)) + +static inline gchar * +OUStringToGChar(const rtl::OUString& rString ) +{ + rtl::OString aUtf8 = rtl::OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ); + return g_strdup( aUtf8 ); +} + +#define OUStringToConstGChar( string ) rtl::OUStringToOString( string, RTL_TEXTENCODING_UTF8 ).getStr() + +#endif /* __ATK_WRAPPER_HXX__ */ diff --git a/vcl/unx/gtk/a11y/makefile.mk b/vcl/unx/gtk/a11y/makefile.mk new file mode 100644 index 000000000000..14d3014ddf11 --- /dev/null +++ b/vcl/unx/gtk/a11y/makefile.mk @@ -0,0 +1,93 @@ +#************************************************************************* +# +# 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. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=gtka11y +ENABLE_EXCEPTIONS=TRUE + +# workaround for makedepend hang +MKDEPENDSOLVER= +NO_DEFAULT_STL=YES + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="unx" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="unx" + +.IF "$(ENABLE_GTK)" != "" + +PKGCONFIG_MODULES=gtk+-2.0 +.INCLUDE : pkg_config.mk + +CFLAGS+=-DVERSION=$(EMQ)"$(UPD)$(LAST_MINOR)$(EMQ)" + +ATKVERSION:=$(shell @$(PKG_CONFIG) --modversion atk | $(AWK) -v num=true -f $(SOLARENV)$/bin$/getcompver.awk) + +.IF "$(ATKVERSION)" >= "000100070000" +CFLAGS+=-DHAS_ATKRECTANGLE +.ENDIF + +SLOFILES=\ + $(SLO)$/atkaction.obj \ + $(SLO)$/atkbridge.obj \ + $(SLO)$/atkcomponent.obj \ + $(SLO)$/atkeditabletext.obj \ + $(SLO)$/atkfactory.obj \ + $(SLO)$/atkhypertext.obj \ + $(SLO)$/atkimage.obj \ + $(SLO)$/atklistener.obj \ + $(SLO)$/atkregistry.obj \ + $(SLO)$/atkselection.obj \ + $(SLO)$/atktable.obj \ + $(SLO)$/atktext.obj \ + $(SLO)$/atktextattributes.obj \ + $(SLO)$/atkutil.obj \ + $(SLO)$/atkvalue.obj \ + $(SLO)$/atkwindow.obj \ + $(SLO)$/atkwrapper.obj + +.ELSE # "$(ENABLE_GTK)" != "" + +dummy: + @echo GTK disabled - nothing to build +.ENDIF +.ENDIF # "$(GUIBASE)"!="unx" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + |