diff options
Diffstat (limited to 'vcl/unx/gtk')
35 files changed, 17780 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..47efde7d3dfd --- /dev/null +++ b/vcl/unx/gtk/a11y/atkbridge.cxx @@ -0,0 +1,81 @@ +/************************************************************************* + * + * 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 ) + { + // g_warning( "unable to get gail version number" ); + 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..f346a6a5a02c --- /dev/null +++ b/vcl/unx/gtk/a11y/atktext.cxx @@ -0,0 +1,798 @@ +/************************************************************************* + * + * 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; +} + +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 missspelled + accessibility::XAccessibleTextMarkup* pTextMarkup = getTextMarkup( text ); + if( pTextMarkup ) + { + uno::Sequence< accessibility::TextSegment > aTextSegmentSeq = + pTextMarkup->getTextMarkupAtIndex( offset, com::sun::star::text::TextMarkupType::SPELLCHECK ); + if( aTextSegmentSeq.getLength() > 0 ) + { + accessibility::TextSegment aTextSegment = aTextSegmentSeq[0]; + gint nStartOffsetMisspelled = aTextSegment.SegmentStart; + gint nEndOffsetMisspelled = aTextSegment.SegmentEnd; + + // 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; + } + + if( nEndOffsetMisspelled <= offset ) + *start_offset = ::std::max( *start_offset, nEndOffsetMisspelled ); + else if( nStartOffsetMisspelled <= offset ) + *start_offset = ::std::max( *start_offset, nStartOffsetMisspelled ); + + if( nStartOffsetMisspelled > offset ) + *end_offset = ::std::min( *end_offset, nStartOffsetMisspelled ); + else if( nEndOffsetMisspelled > offset ) + *end_offset = ::std::min( *end_offset, nEndOffsetMisspelled ); + + if( nStartOffsetMisspelled <= offset && nEndOffsetMisspelled > offset ) + pSet = attribute_set_prepend_misspelled( pSet ); + } + } + } + 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..02624a9628cf --- /dev/null +++ b/vcl/unx/gtk/a11y/atktextattributes.cxx @@ -0,0 +1,1393 @@ +/************************************************************************* + * + * 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; + +/*****************************************************************************/ + +/** + * !! 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, + 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 + "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)); + + 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; +} + + +/*****************************************************************************/ + +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..e363460bb578 --- /dev/null +++ b/vcl/unx/gtk/a11y/atktextattributes.hxx @@ -0,0 +1,49 @@ +/************************************************************************* + * + * 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 ); + +#endif diff --git a/vcl/unx/gtk/a11y/atkutil.cxx b/vcl/unx/gtk/a11y/atkutil.cxx new file mode 100644 index 000000000000..13492f3d4a5c --- /dev/null +++ b/vcl/unx/gtk/a11y/atkutil.cxx @@ -0,0 +1,802 @@ +/************************************************************************* + * + * 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) ) + { + // Gail does not notify focus changes to NULL, so do we .. + if( xAccessible.is() ) + { + AtkObject *atk_obj = atk_object_wrapper_ref( xAccessible ); + +#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->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..f588c1e345e4 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwindow.cxx @@ -0,0 +1,305 @@ +/************************************************************************* + * + * 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 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); +} + +/*****************************************************************************/ + +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..5beb838c0e82 --- /dev/null +++ b/vcl/unx/gtk/a11y/atkwrapper.cxx @@ -0,0 +1,951 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Type.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleRelation.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/XAccessibleText.hpp> +#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp> +#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp> +#include <com/sun/star/accessibility/XAccessibleValue.hpp> +#include <com/sun/star/accessibility/XAccessibleAction.hpp> +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/XAccessibleComponent.hpp> +#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp> +#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp> +#include <com/sun/star/accessibility/XAccessibleTable.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> +#include <com/sun/star/accessibility/XAccessibleImage.hpp> +#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp> +#include <com/sun/star/accessibility/XAccessibleHypertext.hpp> +#include <com/sun/star/accessibility/XAccessibleSelection.hpp> +#include <com/sun/star/awt/XExtendedToolkit.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/XTopWindowListener.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/beans/Property.hpp> + +#include <rtl/ref.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/queryinterface.hxx> + +#include "atkwrapper.hxx" +#include "atkregistry.hxx" +#include "atklistener.hxx" + +#ifdef ENABLE_TRACING +#include <stdio.h> +#endif + +#include <string.h> + +using namespace ::com::sun::star; + +static GObjectClass *parent_class = NULL; + +static AtkRelationType mapRelationType( sal_Int16 nRelation ) +{ + AtkRelationType type = ATK_RELATION_NULL; + + switch( nRelation ) + { + case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM: + type = ATK_RELATION_FLOWS_FROM; + break; + + case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO: + type = ATK_RELATION_FLOWS_TO; + break; + + case accessibility::AccessibleRelationType::CONTROLLED_BY: + type = ATK_RELATION_CONTROLLED_BY; + break; + + case accessibility::AccessibleRelationType::CONTROLLER_FOR: + type = ATK_RELATION_CONTROLLER_FOR; + break; + + case accessibility::AccessibleRelationType::LABEL_FOR: + type = ATK_RELATION_LABEL_FOR; + break; + + case accessibility::AccessibleRelationType::LABELED_BY: + type = ATK_RELATION_LABELLED_BY; + break; + + case accessibility::AccessibleRelationType::MEMBER_OF: + type = ATK_RELATION_MEMBER_OF; + break; + + case accessibility::AccessibleRelationType::SUB_WINDOW_OF: + type = ATK_RELATION_SUBWINDOW_OF; + break; + + case accessibility::AccessibleRelationType::NODE_CHILD_OF: + type = ATK_RELATION_NODE_CHILD_OF; + break; + + default: + break; + } +#if 0 + ATK_RELATION_NODE_CHILD_OF, + ATK_RELATION_EMBEDS, + ATK_RELATION_EMBEDDED_BY, + ATK_RELATION_POPUP_FOR, +#endif + return type; +} + + +AtkStateType mapAtkState( sal_Int16 nState ) +{ + AtkStateType type = ATK_STATE_INVALID; + + // A perfect / complete mapping ... + switch( nState ) + { +#define MAP_DIRECT( a ) \ + case accessibility::AccessibleStateType::a: \ + type = ATK_STATE_##a; break + + MAP_DIRECT( INVALID ); + MAP_DIRECT( ACTIVE ); + MAP_DIRECT( ARMED ); + MAP_DIRECT( BUSY ); + MAP_DIRECT( CHECKED ); + MAP_DIRECT( EDITABLE ); + MAP_DIRECT( ENABLED ); + MAP_DIRECT( EXPANDABLE ); + MAP_DIRECT( EXPANDED ); + MAP_DIRECT( FOCUSABLE ); + MAP_DIRECT( FOCUSED ); + MAP_DIRECT( HORIZONTAL ); + MAP_DIRECT( ICONIFIED ); + MAP_DIRECT( INDETERMINATE ); + MAP_DIRECT( MANAGES_DESCENDANTS ); + MAP_DIRECT( MODAL ); + MAP_DIRECT( MULTI_LINE ); + MAP_DIRECT( OPAQUE ); + MAP_DIRECT( PRESSED ); + MAP_DIRECT( RESIZABLE ); + MAP_DIRECT( SELECTABLE ); + MAP_DIRECT( SELECTED ); + MAP_DIRECT( SENSITIVE ); + MAP_DIRECT( SHOWING ); + MAP_DIRECT( SINGLE_LINE ); + MAP_DIRECT( STALE ); + MAP_DIRECT( TRANSIENT ); + MAP_DIRECT( VERTICAL ); + MAP_DIRECT( VISIBLE ); + // a spelling error ... + case accessibility::AccessibleStateType::DEFUNC: + type = ATK_STATE_DEFUNCT; break; + case accessibility::AccessibleStateType::MULTI_SELECTABLE: + type = ATK_STATE_MULTISELECTABLE; break; + default: + break; + } + + return type; +} + +static inline AtkRole registerRole( const gchar * name ) +{ + AtkRole ret = atk_role_for_name( name ); + if( ATK_ROLE_INVALID == ret ) + ret = atk_role_register( name ); + + return ret; +} + +static AtkRole mapToAtkRole( sal_Int16 nRole ) +{ + AtkRole role = ATK_ROLE_UNKNOWN; + + static AtkRole roleMap[] = { + ATK_ROLE_UNKNOWN, + ATK_ROLE_ALERT, + ATK_ROLE_COLUMN_HEADER, + ATK_ROLE_CANVAS, + ATK_ROLE_CHECK_BOX, + ATK_ROLE_CHECK_MENU_ITEM, + ATK_ROLE_COLOR_CHOOSER, + ATK_ROLE_COMBO_BOX, + ATK_ROLE_DATE_EDITOR, + ATK_ROLE_DESKTOP_ICON, + ATK_ROLE_DESKTOP_FRAME, // ? pane + ATK_ROLE_DIRECTORY_PANE, + ATK_ROLE_DIALOG, + ATK_ROLE_UNKNOWN, // DOCUMENT - registered below + ATK_ROLE_UNKNOWN, // EMBEDDED_OBJECT - registered below + ATK_ROLE_UNKNOWN, // END_NOTE - registered below + ATK_ROLE_FILE_CHOOSER, + ATK_ROLE_FILLER, + ATK_ROLE_FONT_CHOOSER, + ATK_ROLE_FOOTER, + ATK_ROLE_TEXT, // FOOTNOTE - registered below + ATK_ROLE_FRAME, + ATK_ROLE_GLASS_PANE, + ATK_ROLE_IMAGE, // GRAPHIC + ATK_ROLE_UNKNOWN, // GROUP_BOX - registered below + ATK_ROLE_HEADER, + ATK_ROLE_PARAGRAPH, // HEADING - registered below + ATK_ROLE_TEXT, // HYPER_LINK - registered below + ATK_ROLE_ICON, + ATK_ROLE_INTERNAL_FRAME, + ATK_ROLE_LABEL, + ATK_ROLE_LAYERED_PANE, + ATK_ROLE_LIST, + ATK_ROLE_LIST_ITEM, + ATK_ROLE_MENU, + ATK_ROLE_MENU_BAR, + ATK_ROLE_MENU_ITEM, + ATK_ROLE_OPTION_PANE, + ATK_ROLE_PAGE_TAB, + ATK_ROLE_PAGE_TAB_LIST, + ATK_ROLE_PANEL, + ATK_ROLE_PARAGRAPH, + ATK_ROLE_PASSWORD_TEXT, + ATK_ROLE_POPUP_MENU, + ATK_ROLE_PUSH_BUTTON, + ATK_ROLE_PROGRESS_BAR, + ATK_ROLE_RADIO_BUTTON, + ATK_ROLE_RADIO_MENU_ITEM, + ATK_ROLE_ROW_HEADER, + ATK_ROLE_ROOT_PANE, + ATK_ROLE_SCROLL_BAR, + ATK_ROLE_SCROLL_PANE, + ATK_ROLE_UNKNOWN, // SHAPE - registered below + ATK_ROLE_SEPARATOR, + ATK_ROLE_SLIDER, + ATK_ROLE_SPIN_BUTTON, // SPIN_BOX ? + ATK_ROLE_SPLIT_PANE, + ATK_ROLE_STATUSBAR, + ATK_ROLE_TABLE, + ATK_ROLE_TABLE_CELL, + ATK_ROLE_TEXT, + ATK_ROLE_INTERNAL_FRAME, // TEXT_FRAME - registered below + ATK_ROLE_TOGGLE_BUTTON, + ATK_ROLE_TOOL_BAR, + ATK_ROLE_TOOL_TIP, + ATK_ROLE_TREE, + ATK_ROLE_VIEWPORT, + ATK_ROLE_WINDOW, + ATK_ROLE_PUSH_BUTTON, // BUTTON_DROPDOWN + ATK_ROLE_PUSH_BUTTON, // BUTTON_MENU + ATK_ROLE_UNKNOWN, // CAPTION - registered below + ATK_ROLE_UNKNOWN, // CHART - registered below + ATK_ROLE_UNKNOWN, // EDIT_BAR - registered below + ATK_ROLE_UNKNOWN, // FORM - registered below + ATK_ROLE_UNKNOWN, // IMAGE_MAP - registered below + ATK_ROLE_UNKNOWN, // NOTE - registered below + ATK_ROLE_UNKNOWN, // PAGE - registered below + ATK_ROLE_RULER, + ATK_ROLE_UNKNOWN, // SECTION - registered below + ATK_ROLE_UNKNOWN, // TREE_ITEM - registered below + ATK_ROLE_TREE_TABLE + }; + + static bool initialized = false; + + if( ! initialized ) + { + // re-use strings from ATK library + roleMap[accessibility::AccessibleRole::EDIT_BAR] = registerRole("edit bar"); + roleMap[accessibility::AccessibleRole::EMBEDDED_OBJECT] = registerRole("embedded component"); + roleMap[accessibility::AccessibleRole::CHART] = registerRole("chart"); + roleMap[accessibility::AccessibleRole::CAPTION] = registerRole("caption"); + roleMap[accessibility::AccessibleRole::DOCUMENT] = registerRole("document frame"); + roleMap[accessibility::AccessibleRole::HEADING] = registerRole("heading"); + roleMap[accessibility::AccessibleRole::PAGE] = registerRole("page"); + roleMap[accessibility::AccessibleRole::SECTION] = registerRole("section"); + roleMap[accessibility::AccessibleRole::FORM] = registerRole("form"); + + // these don't exist in ATK yet + roleMap[accessibility::AccessibleRole::END_NOTE] = registerRole("end note"); + roleMap[accessibility::AccessibleRole::FOOTNOTE] = registerRole("foot note"); + roleMap[accessibility::AccessibleRole::GROUP_BOX] = registerRole("group box"); + roleMap[accessibility::AccessibleRole::HYPER_LINK] = registerRole("hyper link"); + roleMap[accessibility::AccessibleRole::SHAPE] = registerRole("shape"); + roleMap[accessibility::AccessibleRole::TEXT_FRAME] = registerRole("text frame"); + roleMap[accessibility::AccessibleRole::IMAGE_MAP] = registerRole("image map"); + roleMap[accessibility::AccessibleRole::NOTE] = registerRole("note"); + roleMap[accessibility::AccessibleRole::TREE_ITEM] = registerRole("tree item"); + + initialized = true; + } + + static const sal_Int32 nMapSize = sizeof(roleMap)/sizeof(sal_Int16); + if( 0 <= nRole && nMapSize > nRole ) + role = roleMap[nRole]; + + return role; +} + + +/*****************************************************************************/ + +extern "C" { + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_name( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext ) + { + try { + rtl::OString aName = + rtl::OUStringToOString( + obj->mpContext->getAccessibleName(), + RTL_TEXTENCODING_UTF8); + + int nCmp = atk_obj->name ? rtl_str_compare( atk_obj->name, aName.getStr() ) : -1; + if( nCmp != 0 ) + { + if( atk_obj->name ) + g_free(atk_obj->name); + atk_obj->name = g_strdup(aName.getStr()); + } + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleName()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_name(atk_obj); +} + +/*****************************************************************************/ + +static G_CONST_RETURN gchar* +wrapper_get_description( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + + if( obj->mpContext ) + { + try { + rtl::OString aDescription = + rtl::OUStringToOString( + obj->mpContext->getAccessibleDescription(), + RTL_TEXTENCODING_UTF8); + + g_free(atk_obj->description); + atk_obj->description = g_strdup(aDescription.getStr()); + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleDescription()" ); + } + } + + return ATK_OBJECT_CLASS (parent_class)->get_description(atk_obj); + +} + +/*****************************************************************************/ + +static gint +wrapper_get_n_children( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + gint n = 0; + + if( obj->mpContext ) + { + try { + n = obj->mpContext->getAccessibleChildCount(); + } + catch(const uno::Exception& e) { + OSL_ENSURE(0, "Exception in getAccessibleChildCount()" ); + } + } + + return n; +} + +/*****************************************************************************/ + +static AtkObject * +wrapper_ref_child( AtkObject *atk_obj, + gint i ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkObject* child = NULL; + + // see comments above atk_object_wrapper_remove_child + if( -1 < i && obj->index_of_child_about_to_be_removed == i ) + { + g_object_ref( obj->child_about_to_be_removed ); + return obj->child_about_to_be_removed; + } + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessible > xAccessible = + obj->mpContext->getAccessibleChild( i ); + + child = atk_object_wrapper_ref( xAccessible ); + } + catch(const uno::Exception& e) { + OSL_ENSURE(0, "Exception in getAccessibleChild"); + } + } + + return child; +} + +/*****************************************************************************/ + +static gint +wrapper_get_index_in_parent( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + gint i = -1; + + if( obj->mpContext ) + { + try { + i = obj->mpContext->getAccessibleIndexInParent(); + +#ifdef ENABLE_TRACING + fprintf(stderr, "%p->getAccessibleIndexInParent() returned: %u\n", + obj->mpAccessible, i); +#endif + } + catch(const uno::Exception& e) { + g_warning( "Exception in getAccessibleIndexInParent()" ); + } + } + return i; +} + +/*****************************************************************************/ + +static AtkRelationSet * +wrapper_ref_relation_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkRelationSet *pSet = atk_relation_set_new(); + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessibleRelationSet > xRelationSet( + obj->mpContext->getAccessibleRelationSet() + ); + + sal_Int32 nRelations = xRelationSet.is() ? xRelationSet->getRelationCount() : 0; + for( sal_Int32 n = 0; n < nRelations; n++ ) + { + accessibility::AccessibleRelation aRelation = xRelationSet->getRelation( n ); + sal_uInt32 nTargetCount = aRelation.TargetSet.getLength(); + AtkObject **pTargets = (AtkObject **) alloca( nTargetCount * sizeof(AtkObject *) ); + + for( sal_uInt32 i = 0; i < nTargetCount; i++ ) + { + uno::Reference< accessibility::XAccessible > xAccessible( + aRelation.TargetSet[i], uno::UNO_QUERY ); + pTargets[i] = atk_object_wrapper_ref( xAccessible ); + } + + AtkRelation *pRel = + atk_relation_new( + pTargets, nTargetCount, + mapRelationType( aRelation.RelationType ) + ); + atk_relation_set_add( pSet, pRel ); + g_object_unref( G_OBJECT( pRel ) ); + } + } + catch(const uno::Exception &e) { + g_object_unref( G_OBJECT( pSet ) ); + pSet = NULL; + } + } + + return pSet; +} + +/*****************************************************************************/ + +#if 0 +struct { + sal_Int16 value; + const sal_Char* name; +} aStateTypeTable[] = { + { accessibility::AccessibleStateType::INVALID, "INVALID" }, + { accessibility::AccessibleStateType::ACTIVE, "ACTIVE" }, + { accessibility::AccessibleStateType::ARMED, "ARMED" }, + { accessibility::AccessibleStateType::BUSY, "BUSY" }, + { accessibility::AccessibleStateType::CHECKED, "CHECKED" }, + { accessibility::AccessibleStateType::DEFUNC, "DEFUNC" }, + { accessibility::AccessibleStateType::EDITABLE, "EDITABLE" }, + { accessibility::AccessibleStateType::ENABLED, "ENABLED" }, + { accessibility::AccessibleStateType::EXPANDABLE, "EXPANDABLE" }, + { accessibility::AccessibleStateType::EXPANDED, "EXPANDED" }, + { accessibility::AccessibleStateType::FOCUSABLE, "FOCUSABLE" }, + { accessibility::AccessibleStateType::FOCUSED, "FOCUSED" }, + { accessibility::AccessibleStateType::HORIZONTAL, "HORIZONTAL" }, + { accessibility::AccessibleStateType::ICONIFIED, "ICONIFIED" }, + { accessibility::AccessibleStateType::INDETERMINATE, "INDETERMINATE" }, + { accessibility::AccessibleStateType::MANAGES_DESCENDANTS, "MANAGES_DESCENDANTS" }, + { accessibility::AccessibleStateType::MODAL, "MODAL" }, + { accessibility::AccessibleStateType::MULTI_LINE, "MULTI_LINE" }, + { accessibility::AccessibleStateType::MULTI_SELECTABLE, "MULTI_SELECTABLE" }, + { accessibility::AccessibleStateType::OPAQUE, "OPAQUE" }, + { accessibility::AccessibleStateType::PRESSED, "PRESSED" }, + { accessibility::AccessibleStateType::RESIZABLE, "RESIZABLE" }, + { accessibility::AccessibleStateType::SELECTABLE, "SELECTABLE" }, + { accessibility::AccessibleStateType::SELECTED, "SELECTED" }, + { accessibility::AccessibleStateType::SENSITIVE, "SENSITIVE" }, + { accessibility::AccessibleStateType::SHOWING, "SHOWING" }, + { accessibility::AccessibleStateType::SINGLE_LINE, "SINGLE_LINE" }, + { accessibility::AccessibleStateType::STALE, "STALE" }, + { accessibility::AccessibleStateType::TRANSIENT, "TRANSIENT" }, + { accessibility::AccessibleStateType::VERTICAL, "VERTICAL" }, + { accessibility::AccessibleStateType::VISIBLE, "VISIBLE" } +}; + +static void printStates(const uno::Sequence<sal_Int16>& rStates) +{ + sal_Int32 n = rStates.getLength(); + size_t nTypes = sizeof(aStateTypeTable)/sizeof(aStateTypeTable[0]); + for (sal_Int32 i = 0; i < n; ++i) + { + for (size_t j = 0; j < nTypes; ++j) + { + if (aStateTypeTable[j].value == rStates[i]) + printf("%s ", aStateTypeTable[j].name); + } + } + printf("\n"); +} +#endif + +static AtkStateSet * +wrapper_ref_state_set( AtkObject *atk_obj ) +{ + AtkObjectWrapper *obj = ATK_OBJECT_WRAPPER (atk_obj); + AtkStateSet *pSet = atk_state_set_new(); + + if( obj->mpContext ) + { + try { + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( + obj->mpContext->getAccessibleStateSet()); + + if( xStateSet.is() ) + { + uno::Sequence< sal_Int16 > aStates = xStateSet->getStates(); + + for( sal_Int32 n = 0; n < aStates.getLength(); n++ ) + atk_state_set_add_state( pSet, mapAtkState( aStates[n] ) ); + + // We need to emulate FOCUS state for menus, menu-items etc. + if( atk_obj == atk_get_focus_object() ) + atk_state_set_add_state( pSet, ATK_STATE_FOCUSED ); +/* FIXME - should we do this ? + else + atk_state_set_remove_state( pSet, ATK_STATE_FOCUSED ); +*/ + } + } + + catch(const uno::Exception &e) { + g_warning( "Exception in wrapper_ref_state_set" ); + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + } + } + else + atk_state_set_add_state( pSet, ATK_STATE_DEFUNCT ); + + return pSet; +} + +/*****************************************************************************/ + + +static void +atk_object_wrapper_finalize (GObject *obj) +{ + AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER (obj); + + if( pWrap->mpAccessible ) + { + ooo_wrapper_registry_remove( pWrap->mpAccessible ); + pWrap->mpAccessible->release(); + pWrap->mpAccessible = NULL; + } + + atk_object_wrapper_dispose( pWrap ); + + parent_class->finalize( obj ); +} + +static void +atk_object_wrapper_class_init (AtkObjectWrapperClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( klass ); + AtkObjectClass *atk_class = ATK_OBJECT_CLASS( klass ); + + parent_class = (GObjectClass *) g_type_class_peek_parent (klass); + + // GObject methods + gobject_class->finalize = atk_object_wrapper_finalize; + + // AtkObject methods + atk_class->get_name = wrapper_get_name; + atk_class->get_description = wrapper_get_description; + atk_class->get_n_children = wrapper_get_n_children; + atk_class->ref_child = wrapper_ref_child; + atk_class->get_index_in_parent = wrapper_get_index_in_parent; + atk_class->ref_relation_set = wrapper_ref_relation_set; + atk_class->ref_state_set = wrapper_ref_state_set; +} + +static void +atk_object_wrapper_init (AtkObjectWrapper *wrapper, + AtkObjectWrapperClass) +{ + wrapper->mpAction = NULL; + wrapper->mpComponent = NULL; + wrapper->mpEditableText = NULL; + wrapper->mpHypertext = NULL; + wrapper->mpImage = NULL; + wrapper->mpSelection = NULL; + wrapper->mpTable = NULL; + wrapper->mpText = NULL; + wrapper->mpValue = NULL; +} + +} // extern "C" + +GType +atk_object_wrapper_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo typeInfo = + { + sizeof (AtkObjectWrapperClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) atk_object_wrapper_class_init, + (GClassFinalizeFunc) NULL, + NULL, + sizeof (AtkObjectWrapper), + 0, + (GInstanceInitFunc) atk_object_wrapper_init, + NULL + } ; + type = g_type_register_static (ATK_TYPE_OBJECT, + "OOoAtkObj", + &typeInfo, (GTypeFlags)0) ; + } + return type; +} + +static bool +isOfType( uno::XInterface *pInterface, const uno::Type & rType ) +{ + g_return_val_if_fail( pInterface != NULL, false ); + + bool bIs = false; + try { + uno::Any aRet = pInterface->queryInterface( rType ); + + bIs = ( ( typelib_TypeClass_INTERFACE == aRet.pType->eTypeClass ) && + ( aRet.pReserved != NULL ) ); + } catch( const uno::Exception &e) { } + + return bIs; +} + +extern "C" { +typedef GType (* GetGIfaceType ) (void); +} +const struct { + const char *name; + GInterfaceInitFunc aInit; + GetGIfaceType aGetGIfaceType; + const uno::Type & (*aGetUnoType) (void *); +} aTypeTable[] = { +// re-location heaven: + { + "Comp", (GInterfaceInitFunc) componentIfaceInit, + atk_component_get_type, + accessibility::XAccessibleComponent::static_type + }, + { + "Act", (GInterfaceInitFunc) actionIfaceInit, + atk_action_get_type, + accessibility::XAccessibleAction::static_type + }, + { + "Txt", (GInterfaceInitFunc) textIfaceInit, + atk_text_get_type, + accessibility::XAccessibleText::static_type + }, + { + "Val", (GInterfaceInitFunc) valueIfaceInit, + atk_value_get_type, + accessibility::XAccessibleValue::static_type + }, + { + "Tab", (GInterfaceInitFunc) tableIfaceInit, + atk_table_get_type, + accessibility::XAccessibleTable::static_type + }, + { + "Edt", (GInterfaceInitFunc) editableTextIfaceInit, + atk_editable_text_get_type, + accessibility::XAccessibleEditableText::static_type + }, + { + "Img", (GInterfaceInitFunc) imageIfaceInit, + atk_image_get_type, + accessibility::XAccessibleImage::static_type + }, + { + "Hyp", (GInterfaceInitFunc) hypertextIfaceInit, + atk_hypertext_get_type, + accessibility::XAccessibleHypertext::static_type + }, + { + "Sel", (GInterfaceInitFunc) selectionIfaceInit, + atk_selection_get_type, + accessibility::XAccessibleSelection::static_type + } + // AtkDocument is a nastily broken interface (so far) + // we could impl. get_document_type perhaps though. +}; + +const int aTypeTableSize = G_N_ELEMENTS( aTypeTable ); + +static GType +ensureTypeFor( uno::XInterface *pAccessible ) +{ + int i; + int bTypes[ aTypeTableSize ] = { 0, }; + rtl::OString aTypeName( "OOoAtkObj" ); + + for( i = 0; i < aTypeTableSize; i++ ) + { + if( isOfType( pAccessible, aTypeTable[i].aGetUnoType(0) ) ) + { + aTypeName += aTypeTable[i].name; + bTypes[i] = TRUE; + } +// g_message( "Accessible %p has type '%s' (%d)", +// pAccessible, aTypeTable[i].name, bTypes[i] ); + } + + GType nType = g_type_from_name( aTypeName ); + if( nType == G_TYPE_INVALID ) + { + GTypeInfo aTypeInfo = { + sizeof( AtkObjectWrapperClass ), + NULL, NULL, NULL, NULL, NULL, + sizeof( AtkObjectWrapper ), + 0, NULL, NULL + } ; + nType = g_type_register_static( ATK_TYPE_OBJECT_WRAPPER, + aTypeName, &aTypeInfo, (GTypeFlags)0 ) ; + + for( int j = 0; j < aTypeTableSize; j++ ) + if( bTypes[j] ) + { + GInterfaceInfo aIfaceInfo = { NULL, NULL, NULL }; + aIfaceInfo.interface_init = aTypeTable[j].aInit; + g_type_add_interface_static (nType, aTypeTable[j].aGetGIfaceType(), + &aIfaceInfo); + } + } + return nType; +} + +AtkObject * +atk_object_wrapper_ref( const uno::Reference< accessibility::XAccessible > &rxAccessible, bool create ) +{ + g_return_val_if_fail( rxAccessible.get() != NULL, NULL ); + + AtkObject *obj = ooo_wrapper_registry_get(rxAccessible); + if( obj ) + { + g_object_ref( obj ); + return obj; + } + + if( create ) + return atk_object_wrapper_new( rxAccessible ); + + return NULL; +} + + +AtkObject * +atk_object_wrapper_new( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible, + AtkObject* parent ) +{ + g_return_val_if_fail( rxAccessible.get() != NULL, NULL ); + + AtkObjectWrapper *pWrap = NULL; + + try { + uno::Reference< accessibility::XAccessibleContext > xContext(rxAccessible->getAccessibleContext()); + + g_return_val_if_fail( xContext.get() != NULL, NULL ); + + GType nType = ensureTypeFor( xContext.get() ); + gpointer obj = g_object_new( nType, NULL); + + pWrap = ATK_OBJECT_WRAPPER( obj ); + pWrap->mpAccessible = rxAccessible.get(); + rxAccessible->acquire(); + + pWrap->index_of_child_about_to_be_removed = -1; + pWrap->child_about_to_be_removed = NULL; + + xContext->acquire(); + pWrap->mpContext = xContext.get(); + + AtkObject* atk_obj = ATK_OBJECT(pWrap); + atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() ); + atk_obj->accessible_parent = parent; + + ooo_wrapper_registry_add( rxAccessible, atk_obj ); + + if( parent ) + g_object_ref( atk_obj->accessible_parent ); + else + { + /* gail_focus_tracker remembers the focused object at the first + * parent in the hierachy that is a Gtk+ widget, but at the time the + * event gets processed (at idle), it may be too late to create the + * hierachy, so doing it now .. + */ + uno::Reference< accessibility::XAccessible > xParent( xContext->getAccessibleParent() ); + + /* The top-level objects should never be of this class */ + OSL_ASSERT( xParent.is() ); + + if( xParent.is() ) + atk_obj->accessible_parent = atk_object_wrapper_ref( xParent ); + } + + // Attach a listener to the UNO object if it's not TRANSIENT + uno::Reference< accessibility::XAccessibleStateSet > xStateSet( xContext->getAccessibleStateSet() ); + if( xStateSet.is() && ! xStateSet->contains( accessibility::AccessibleStateType::TRANSIENT ) ) + { + uno::Reference< accessibility::XAccessibleEventBroadcaster > xBroadcaster(xContext, uno::UNO_QUERY); + if( xBroadcaster.is() ) + xBroadcaster->addEventListener( static_cast< accessibility::XAccessibleEventListener * > ( new AtkListener(pWrap) ) ); + else + OSL_ASSERT( false ); + } + + return ATK_OBJECT( pWrap ); + } + catch (const uno::Exception &e) + { + if( pWrap ) + g_object_unref( pWrap ); + + return NULL; + } +} + + +/*****************************************************************************/ + +void atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + AtkObject *atk_obj = ATK_OBJECT( wrapper ); + + atk_object_set_parent( child, atk_obj ); + g_signal_emit_by_name( atk_obj, "children_changed::add", index, child, NULL ); +} + +/*****************************************************************************/ + +void atk_object_wrapper_remove_child(AtkObjectWrapper* wrapper, AtkObject *child, gint index) +{ + /* + * the atk-bridge GTK+ module get's back to the event source to ref the child just + * vanishing, so we keep this reference because the semantic on OOo side is different. + */ + wrapper->child_about_to_be_removed = child; + wrapper->index_of_child_about_to_be_removed = index; + + g_signal_emit_by_name( ATK_OBJECT( wrapper ), "children_changed::remove", index, child, NULL ); + + wrapper->index_of_child_about_to_be_removed = -1; + wrapper->child_about_to_be_removed = NULL; +} + +/*****************************************************************************/ + +#define RELEASE(i) if( i ) { i->release(); i = NULL; } + +void atk_object_wrapper_dispose(AtkObjectWrapper* wrapper) +{ + RELEASE( wrapper->mpContext ) + RELEASE( wrapper->mpAction ) + RELEASE( wrapper->mpComponent ) + RELEASE( wrapper->mpEditableText ) + RELEASE( wrapper->mpHypertext ) + RELEASE( wrapper->mpImage ) + RELEASE( wrapper->mpSelection ) + RELEASE( wrapper->mpMultiLineText ) + RELEASE( wrapper->mpTable ) + RELEASE( wrapper->mpText ) + RELEASE( wrapper->mpTextMarkup ) + RELEASE( wrapper->mpTextAttributes ) + RELEASE( wrapper->mpValue ) +} 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 + diff --git a/vcl/unx/gtk/app/gtkdata.cxx b/vcl/unx/gtk/app/gtkdata.cxx new file mode 100644 index 000000000000..d1e5c5954352 --- /dev/null +++ b/vcl/unx/gtk/app/gtkdata.cxx @@ -0,0 +1,994 @@ +/************************************************************************* + * + * 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" + +#define _SV_SALDATA_CXX + +// -=-= #includes =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include <unistd.h> +#include <fcntl.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <errno.h> +#include <poll.h> +#ifdef FREEBSD +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#endif +#include <plugins/gtk/gtkdata.hxx> +#include <plugins/gtk/gtkinst.hxx> +#include <plugins/gtk/gtkframe.hxx> +#include <salobj.h> +#include <osl/thread.h> +#include <osl/process.h> + +#include <tools/debug.hxx> +#include "i18n_im.hxx" +#include "i18n_xkb.hxx" +#include <wmadaptor.hxx> + +#include "../../unx/source/inc/salcursors.h" + +#include <vcl/svapp.hxx> + +using namespace rtl; +using namespace vcl_sal; + +/*************************************************************************** + * class GtkDisplay * + ***************************************************************************/ + +GtkSalDisplay::GtkSalDisplay( GdkDisplay* pDisplay ) + : SalDisplay( gdk_x11_display_get_xdisplay( pDisplay ) ), + m_pGdkDisplay( pDisplay ), + m_bStartupCompleted( false ) +{ + m_bUseRandRWrapper = false; // use gdk signal instead + for(int i = 0; i < POINTER_COUNT; i++) + m_aCursors[ i ] = NULL; + Init (); +} + +GtkSalDisplay::~GtkSalDisplay() +{ + if( !m_bStartupCompleted ) + gdk_notify_startup_complete(); + doDestruct(); + + for(int i = 0; i < POINTER_COUNT; i++) + if( m_aCursors[ i ] ) + gdk_cursor_unref( m_aCursors[ i ] ); + + pDisp_ = NULL; +} + +void GtkSalDisplay::deregisterFrame( SalFrame* pFrame ) +{ + if( m_pCapture == pFrame ) + { + static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); + m_pCapture = NULL; + } + SalDisplay::deregisterFrame( pFrame ); +} + +extern "C" { +GdkFilterReturn call_filterGdkEvent( GdkXEvent* sys_event, + GdkEvent* event, + gpointer data ) +{ + return GtkSalDisplay::filterGdkEvent( sys_event, event, data ); +} + +void signalKeysChanged( GdkKeymap*, gpointer data ) +{ + GtkSalDisplay* pDisp = (GtkSalDisplay*)data; + pDisp->GetKeyboardName(TRUE); +} + +void signalScreenSizeChanged( GdkScreen* pScreen, gpointer data ) +{ + GtkSalDisplay* pDisp = (GtkSalDisplay*)data; + pDisp->screenSizeChanged( pScreen ); +} + +void signalMonitorsChanged( GdkScreen* pScreen, gpointer data ) +{ + GtkSalDisplay* pDisp = (GtkSalDisplay*)data; + pDisp->monitorsChanged( pScreen ); +} + +} + +GdkFilterReturn GtkSalDisplay::filterGdkEvent( GdkXEvent* sys_event, + GdkEvent*, + gpointer data ) +{ + GdkFilterReturn aFilterReturn = GDK_FILTER_CONTINUE; + + XEvent *pEvent = (XEvent *)sys_event; + GtkSalDisplay *pDisplay = (GtkSalDisplay *)data; + + // dispatch all XEvents to event callback + if( GetSalData()->m_pInstance-> + CallEventCallback( pEvent, sizeof( XEvent ) ) ) + aFilterReturn = GDK_FILTER_REMOVE; + + GTK_YIELD_GRAB(); + + if (pDisplay->GetDisplay() == pEvent->xany.display ) + { + // #i53471# gtk has no callback mechanism that lets us be notified + // when settings (as in XSETTING and opposed to styles) are changed. + // so we need to listen for corresponding property notifications here + // these should be rare enough so that we can assume that the settings + // actually change when a corresponding PropertyNotify occurs + if( pEvent->type == PropertyNotify && + pEvent->xproperty.atom == pDisplay->getWMAdaptor()->getAtom( WMAdaptor::XSETTINGS ) && + ! pDisplay->m_aFrames.empty() + ) + { + pDisplay->SendInternalEvent( pDisplay->m_aFrames.front(), NULL, SALEVENT_SETTINGSCHANGED ); + } + // let's see if one of our frames wants to swallow these events + // get the frame + for( std::list< SalFrame* >::const_iterator it = pDisplay->m_aFrames.begin(); + it != pDisplay->m_aFrames.end(); ++it ) + { + GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(*it); + if( (GdkNativeWindow)pFrame->GetSystemData()->aWindow == pEvent->xany.window || + ( pFrame->getForeignParent() && pFrame->getForeignParentWindow() == pEvent->xany.window ) || + ( pFrame->getForeignTopLevel() && pFrame->getForeignTopLevelWindow() == pEvent->xany.window ) + ) + { + if( ! pFrame->Dispatch( pEvent ) ) + aFilterReturn = GDK_FILTER_REMOVE; + break; + } + } + X11SalObject::Dispatch( pEvent ); + } + + return aFilterReturn; +} + +void GtkSalDisplay::screenSizeChanged( GdkScreen* pScreen ) +{ + if( pScreen ) + { + int nScreen = gdk_screen_get_number( pScreen ); + if( nScreen < static_cast<int>(m_aScreens.size()) ) + { + ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]); + if( rSD.m_bInit ) + { + rSD.m_aSize = Size( gdk_screen_get_width( pScreen ), + gdk_screen_get_height( pScreen ) ); + if( ! m_aFrames.empty() ) + m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 ); + } + } + else + { + DBG_ERROR( "unknown screen changed size" ); + } + } +} + +void GtkSalDisplay::monitorsChanged( GdkScreen* pScreen ) +{ + if( pScreen ) + { + if( gdk_display_get_n_screens(m_pGdkDisplay) == 1 ) + { + int nScreen = gdk_screen_get_number( pScreen ); + if( nScreen == m_nDefaultScreen ) //To-Do, make m_aXineramaScreens a per-screen thing ? + { + gint nMonitors = gdk_screen_get_n_monitors(pScreen); + m_aXineramaScreens = std::vector<Rectangle>(); + for (gint i = 0; i < nMonitors; ++i) + { + GdkRectangle dest; + gdk_screen_get_monitor_geometry(pScreen, i, &dest); + addXineramaScreenUnique( dest.x, dest.y, dest.width, dest.height ); + } + m_bXinerama = m_aXineramaScreens.size() > 1; + if( ! m_aFrames.empty() ) + m_aFrames.front()->CallCallback( SALEVENT_DISPLAYCHANGED, 0 ); + } + else + { + DBG_ERROR( "monitors for non-default screen changed, extend-me" ); + } + } + } +} + +void GtkSalDisplay::initScreen( int nScreen ) const +{ + if( nScreen < 0 || nScreen >= static_cast<int>(m_aScreens.size()) ) + nScreen = m_nDefaultScreen; + ScreenData& rSD = const_cast<ScreenData&>(m_aScreens[nScreen]); + if( rSD.m_bInit ) + return; + + // choose visual for screen + SalDisplay::initScreen( nScreen ); + // now set a gdk default colormap matching the chosen visual to the screen + GdkVisual* pVis = gdkx_visual_get( rSD.m_aVisual.visualid ); + GdkScreen* pScreen = gdk_display_get_screen( m_pGdkDisplay, nScreen ); + if( pVis ) + { + GdkColormap* pDefCol = gdk_screen_get_default_colormap( pScreen ); + GdkVisual* pDefVis = gdk_colormap_get_visual( pDefCol ); + if( pDefVis != pVis ) + { + pDefCol = gdk_x11_colormap_foreign_new( pVis, rSD.m_aColormap.GetXColormap() ); + gdk_screen_set_default_colormap( pScreen, pDefCol ); + #if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "set new gdk color map for screen %d\n", nScreen ); + #endif + } + } + #if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "not GdkVisual for visual id %d\n", (int)rSD.m_aVisual.visualid ); + #endif +} + +long GtkSalDisplay::Dispatch( XEvent* pEvent ) +{ + if( GetDisplay() == pEvent->xany.display ) + { + // let's see if one of our frames wants to swallow these events + // get the child frame + for( std::list< SalFrame* >::const_iterator it = m_aFrames.begin(); + it != m_aFrames.end(); ++it ) + { + if( (GdkNativeWindow)(*it)->GetSystemData()->aWindow == pEvent->xany.window ) + return static_cast<GtkSalFrame*>(*it)->Dispatch( pEvent ); + } + } + + return GDK_FILTER_CONTINUE; +} + +GdkCursor* GtkSalDisplay::getFromXPM( const char *pBitmap, + const char *pMask, + int nWidth, int nHeight, + int nXHot, int nYHot ) +{ + GdkScreen *pScreen = gdk_display_get_default_screen( m_pGdkDisplay ); + GdkDrawable *pDrawable = GDK_DRAWABLE( gdk_screen_get_root_window (pScreen) ); + GdkBitmap *pBitmapPix = gdk_bitmap_create_from_data + ( pDrawable, pBitmap, nWidth, nHeight ); + GdkBitmap *pMaskPix = gdk_bitmap_create_from_data + ( pDrawable, pMask, nWidth, nHeight ); + GdkColormap *pColormap = gdk_drawable_get_colormap( pDrawable ); + + GdkColor aWhite = { 0, 0xffff, 0xffff, 0xffff }; + GdkColor aBlack = { 0, 0, 0, 0 }; + + gdk_colormap_alloc_color( pColormap, &aBlack, FALSE, TRUE); + gdk_colormap_alloc_color( pColormap, &aWhite, FALSE, TRUE); + + return gdk_cursor_new_from_pixmap + ( pBitmapPix, pMaskPix, + &aBlack, &aWhite, nXHot, nYHot); +} + +#define MAKE_CURSOR( vcl_name, name ) \ + case vcl_name: \ + pCursor = getFromXPM( name##curs##_bits, name##mask##_bits, \ + name##curs_width, name##curs_height, \ + name##curs_x_hot, name##curs_y_hot ); \ + break +#define MAP_BUILTIN( vcl_name, gdk_name ) \ + case vcl_name: \ + pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, gdk_name ); \ + break + +GdkCursor *GtkSalDisplay::getCursor( PointerStyle ePointerStyle ) +{ + if( ePointerStyle > POINTER_COUNT ) + return NULL; + + if ( !m_aCursors[ ePointerStyle ] ) + { + GdkCursor *pCursor = NULL; + + switch( ePointerStyle ) + { + MAP_BUILTIN( POINTER_ARROW, GDK_LEFT_PTR ); + MAP_BUILTIN( POINTER_TEXT, GDK_XTERM ); + MAP_BUILTIN( POINTER_HELP, GDK_QUESTION_ARROW ); + MAP_BUILTIN( POINTER_CROSS, GDK_CROSSHAIR ); + MAP_BUILTIN( POINTER_WAIT, GDK_WATCH ); + + MAP_BUILTIN( POINTER_NSIZE, GDK_SB_V_DOUBLE_ARROW ); + MAP_BUILTIN( POINTER_SSIZE, GDK_SB_V_DOUBLE_ARROW ); + MAP_BUILTIN( POINTER_WSIZE, GDK_SB_H_DOUBLE_ARROW ); + MAP_BUILTIN( POINTER_ESIZE, GDK_SB_H_DOUBLE_ARROW ); + + MAP_BUILTIN( POINTER_NWSIZE, GDK_TOP_LEFT_CORNER ); + MAP_BUILTIN( POINTER_NESIZE, GDK_TOP_RIGHT_CORNER ); + MAP_BUILTIN( POINTER_SWSIZE, GDK_BOTTOM_LEFT_CORNER ); + MAP_BUILTIN( POINTER_SESIZE, GDK_BOTTOM_RIGHT_CORNER ); + + MAP_BUILTIN( POINTER_WINDOW_NSIZE, GDK_TOP_SIDE ); + MAP_BUILTIN( POINTER_WINDOW_SSIZE, GDK_BOTTOM_SIDE ); + MAP_BUILTIN( POINTER_WINDOW_WSIZE, GDK_LEFT_SIDE ); + MAP_BUILTIN( POINTER_WINDOW_ESIZE, GDK_RIGHT_SIDE ); + + MAP_BUILTIN( POINTER_WINDOW_NWSIZE, GDK_TOP_LEFT_CORNER ); + MAP_BUILTIN( POINTER_WINDOW_NESIZE, GDK_TOP_RIGHT_CORNER ); + MAP_BUILTIN( POINTER_WINDOW_SWSIZE, GDK_BOTTOM_LEFT_CORNER ); + MAP_BUILTIN( POINTER_WINDOW_SESIZE, GDK_BOTTOM_RIGHT_CORNER ); + + MAP_BUILTIN( POINTER_HSIZEBAR, GDK_SB_H_DOUBLE_ARROW ); + MAP_BUILTIN( POINTER_VSIZEBAR, GDK_SB_V_DOUBLE_ARROW ); + + MAP_BUILTIN( POINTER_REFHAND, GDK_HAND1 ); + MAP_BUILTIN( POINTER_HAND, GDK_HAND2 ); + MAP_BUILTIN( POINTER_PEN, GDK_PENCIL ); + + MAP_BUILTIN( POINTER_HSPLIT, GDK_SB_H_DOUBLE_ARROW ); + MAP_BUILTIN( POINTER_VSPLIT, GDK_SB_V_DOUBLE_ARROW ); + + MAP_BUILTIN( POINTER_MOVE, GDK_FLEUR ); + + MAKE_CURSOR( POINTER_NULL, null ); + MAKE_CURSOR( POINTER_MAGNIFY, magnify_ ); + MAKE_CURSOR( POINTER_FILL, fill_ ); + MAKE_CURSOR( POINTER_MOVEDATA, movedata_ ); + MAKE_CURSOR( POINTER_COPYDATA, copydata_ ); + MAKE_CURSOR( POINTER_MOVEFILE, movefile_ ); + MAKE_CURSOR( POINTER_COPYFILE, copyfile_ ); + MAKE_CURSOR( POINTER_MOVEFILES, movefiles_ ); + MAKE_CURSOR( POINTER_COPYFILES, copyfiles_ ); + MAKE_CURSOR( POINTER_NOTALLOWED, nodrop_ ); + MAKE_CURSOR( POINTER_ROTATE, rotate_ ); + MAKE_CURSOR( POINTER_HSHEAR, hshear_ ); + MAKE_CURSOR( POINTER_VSHEAR, vshear_ ); + MAKE_CURSOR( POINTER_DRAW_LINE, drawline_ ); + MAKE_CURSOR( POINTER_DRAW_RECT, drawrect_ ); + MAKE_CURSOR( POINTER_DRAW_POLYGON, drawpolygon_ ); + MAKE_CURSOR( POINTER_DRAW_BEZIER, drawbezier_ ); + MAKE_CURSOR( POINTER_DRAW_ARC, drawarc_ ); + MAKE_CURSOR( POINTER_DRAW_PIE, drawpie_ ); + MAKE_CURSOR( POINTER_DRAW_CIRCLECUT, drawcirclecut_ ); + MAKE_CURSOR( POINTER_DRAW_ELLIPSE, drawellipse_ ); + MAKE_CURSOR( POINTER_DRAW_CONNECT, drawconnect_ ); + MAKE_CURSOR( POINTER_DRAW_TEXT, drawtext_ ); + MAKE_CURSOR( POINTER_MIRROR, mirror_ ); + MAKE_CURSOR( POINTER_CROOK, crook_ ); + MAKE_CURSOR( POINTER_CROP, crop_ ); + MAKE_CURSOR( POINTER_MOVEPOINT, movepoint_ ); + MAKE_CURSOR( POINTER_MOVEBEZIERWEIGHT, movebezierweight_ ); + MAKE_CURSOR( POINTER_DRAW_FREEHAND, drawfreehand_ ); + MAKE_CURSOR( POINTER_DRAW_CAPTION, drawcaption_ ); + MAKE_CURSOR( POINTER_LINKDATA, linkdata_ ); + MAKE_CURSOR( POINTER_MOVEDATALINK, movedlnk_ ); + MAKE_CURSOR( POINTER_COPYDATALINK, copydlnk_ ); + MAKE_CURSOR( POINTER_LINKFILE, linkfile_ ); + MAKE_CURSOR( POINTER_MOVEFILELINK, moveflnk_ ); + MAKE_CURSOR( POINTER_COPYFILELINK, copyflnk_ ); + MAKE_CURSOR( POINTER_CHART, chart_ ); + MAKE_CURSOR( POINTER_DETECTIVE, detective_ ); + MAKE_CURSOR( POINTER_PIVOT_COL, pivotcol_ ); + MAKE_CURSOR( POINTER_PIVOT_ROW, pivotrow_ ); + MAKE_CURSOR( POINTER_PIVOT_FIELD, pivotfld_ ); + MAKE_CURSOR( POINTER_PIVOT_DELETE, pivotdel_ ); + MAKE_CURSOR( POINTER_CHAIN, chain_ ); + MAKE_CURSOR( POINTER_CHAIN_NOTALLOWED, chainnot_ ); + MAKE_CURSOR( POINTER_TIMEEVENT_MOVE, timemove_ ); + MAKE_CURSOR( POINTER_TIMEEVENT_SIZE, timesize_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_N, asn_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_S, ass_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_W, asw_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_E, ase_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_NW, asnw_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_NE, asne_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_SW, assw_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_SE, asse_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_NS, asns_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_WE, aswe_ ); + MAKE_CURSOR( POINTER_AUTOSCROLL_NSWE, asnswe_ ); + MAKE_CURSOR( POINTER_AIRBRUSH, airbrush_ ); + MAKE_CURSOR( POINTER_TEXT_VERTICAL, vertcurs_ ); + + // --> FME 2004-07-30 #i32329# Enhanced table selection + MAKE_CURSOR( POINTER_TAB_SELECT_S, tblsels_ ); + MAKE_CURSOR( POINTER_TAB_SELECT_E, tblsele_ ); + MAKE_CURSOR( POINTER_TAB_SELECT_SE, tblselse_ ); + MAKE_CURSOR( POINTER_TAB_SELECT_W, tblselw_ ); + MAKE_CURSOR( POINTER_TAB_SELECT_SW, tblselsw_ ); + // <-- + + // --> FME 2004-08-16 #i20119# Paintbrush tool + MAKE_CURSOR( POINTER_PAINTBRUSH, paintbrush_ ); + // <-- + + default: + fprintf( stderr, "pointer %d not implemented", ePointerStyle ); + break; + } + if( !pCursor ) + pCursor = gdk_cursor_new_for_display( m_pGdkDisplay, GDK_LEFT_PTR ); + + m_aCursors[ ePointerStyle ] = pCursor; + } + + return m_aCursors[ ePointerStyle ]; +} + +int GtkSalDisplay::CaptureMouse( SalFrame* pSFrame ) +{ + GtkSalFrame* pFrame = static_cast<GtkSalFrame*>(pSFrame); + + if( !pFrame ) + { + if( m_pCapture ) + static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); + m_pCapture = NULL; + return 0; + } + + if( m_pCapture ) + { + if( pFrame == m_pCapture ) + return 1; + static_cast<GtkSalFrame*>(m_pCapture)->grabPointer( FALSE ); + } + + m_pCapture = pFrame; + static_cast<GtkSalFrame*>(pFrame)->grabPointer( TRUE ); + return 1; +} + +/*************************************************************************** + * class GtkXLib * + ***************************************************************************/ + +class GtkXLib : public SalXLib +{ + GtkSalDisplay *m_pGtkSalDisplay; + std::list<GSource *> m_aSources; + GSource *m_pTimeout; + GSource *m_pUserEvent; + oslMutex m_aDispatchMutex; + oslCondition m_aDispatchCondition; + +public: + static gboolean timeoutFn(gpointer data); + static gboolean userEventFn(gpointer data); + + GtkXLib(); + virtual ~GtkXLib(); + + virtual void Init(); + virtual void Yield( bool bWait, bool bHandleAllCurrentEvents ); + virtual void Insert( int fd, void* data, + YieldFunc pending, + YieldFunc queued, + YieldFunc handle ); + virtual void Remove( int fd ); + + virtual void StartTimer( ULONG nMS ); + virtual void StopTimer(); + virtual void Wakeup(); + virtual void PostUserEvent(); +}; + +GtkXLib::GtkXLib() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "GtkXLib::GtkXLib()\n" ); +#endif + m_pGtkSalDisplay = NULL; + m_pTimeout = NULL; + m_nTimeoutMS = 0; + m_pUserEvent = NULL; + m_aDispatchCondition = osl_createCondition(); + m_aDispatchMutex = osl_createMutex(); +} + +GtkXLib::~GtkXLib() +{ +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "GtkXLib::~GtkXLib()\n" ); +#endif + StopTimer(); + // sanity check: at this point nobody should be yielding, but wake them + // up anyway before the condition they're waiting on gets destroyed. + osl_setCondition( m_aDispatchCondition ); + osl_destroyCondition( m_aDispatchCondition ); + osl_destroyMutex( m_aDispatchMutex ); +} + +void GtkXLib::Init() +{ + int i; +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "GtkXLib::Init()\n" ); +#endif + XrmInitialize(); + + gtk_set_locale(); + + /* + * open connection to X11 Display + * try in this order: + * o -display command line parameter, + * o $DISPLAY environment variable + * o default display + */ + + GdkDisplay *pGdkDisp = NULL; + + // is there a -display command line parameter? + rtl_TextEncoding aEnc = osl_getThreadTextEncoding(); + int nParams = osl_getCommandArgCount(); + rtl::OString aDisplay; + rtl::OUString aParam, aBin; + char** pCmdLineAry = new char*[ nParams+1 ]; + osl_getExecutableFile( &aParam.pData ); + osl_getSystemPathFromFileURL( aParam.pData, &aBin.pData ); + pCmdLineAry[0] = g_strdup( OUStringToOString( aBin, aEnc ).getStr() ); + for (i=0; i<nParams; i++) + { + osl_getCommandArg(i, &aParam.pData ); + OString aBParam( OUStringToOString( aParam, aEnc ) ); + + if( aParam.equalsAscii( "-display" ) || aParam.equalsAscii( "--display" ) ) + { + pCmdLineAry[i+1] = g_strdup( "--display" ); + osl_getCommandArg(i+1, &aParam.pData ); + aDisplay = rtl::OUStringToOString( aParam, aEnc ); + } + else + pCmdLineAry[i+1] = g_strdup( aBParam.getStr() ); + } + // add executable + nParams++; + + g_set_application_name(X11SalData::getFrameClassName()); + + // Set consistant name of the root accessible + rtl::OUString aAppName = Application::GetAppName(); + if( aAppName.getLength() > 0 ) + { + rtl::OString aPrgName = rtl::OUStringToOString(aAppName, aEnc); + g_set_prgname(aPrgName); + } + + // init gtk/gdk + gtk_init_check( &nParams, &pCmdLineAry ); + + for (i = 0; i < nParams; i++ ) + g_free( pCmdLineAry[i] ); + delete [] pCmdLineAry; + +#if OSL_DEBUG_LEVEL > 1 + if (g_getenv ("SAL_DEBUG_UPDATES")) + gdk_window_set_debug_updates (TRUE); +#endif + + pGdkDisp = gdk_display_get_default(); + if ( !pGdkDisp ) + { + rtl::OUString aProgramFileURL; + osl_getExecutableFile( &aProgramFileURL.pData ); + rtl::OUString aProgramSystemPath; + osl_getSystemPathFromFileURL (aProgramFileURL.pData, &aProgramSystemPath.pData); + rtl::OString aProgramName = rtl::OUStringToOString( + aProgramSystemPath, + osl_getThreadTextEncoding() ); + fprintf( stderr, "%s X11 error: Can't open display: %s\n", + aProgramName.getStr(), aDisplay.getStr()); + fprintf( stderr, " Set DISPLAY environment variable, use -display option\n"); + fprintf( stderr, " or check permissions of your X-Server\n"); + fprintf( stderr, " (See \"man X\" resp. \"man xhost\" for details)\n"); + fflush( stderr ); + exit(0); + } + + /* + * if a -display switch was used, we need + * to set the environment accoringly since + * the clipboard build another connection + * to the xserver using $DISPLAY + */ + char *pPutEnvIsBroken = g_strdup_printf( "DISPLAY=%s", + gdk_display_get_name( pGdkDisp ) ); + putenv( pPutEnvIsBroken ); + + Display *pDisp = gdk_x11_display_get_xdisplay( pGdkDisp ); + + XSetIOErrorHandler ( (XIOErrorHandler)X11SalData::XIOErrorHdl ); + XSetErrorHandler ( (XErrorHandler)X11SalData::XErrorHdl ); + + m_pGtkSalDisplay = new GtkSalDisplay( pGdkDisp ); + + gdk_window_add_filter( NULL, call_filterGdkEvent, m_pGtkSalDisplay ); + + PushXErrorLevel( true ); + SalI18N_KeyboardExtension *pKbdExtension = new SalI18N_KeyboardExtension( pDisp ); + XSync( pDisp, False ); + + pKbdExtension->UseExtension( ! HasXErrorOccured() ); + PopXErrorLevel(); + + m_pGtkSalDisplay->SetKbdExtension( pKbdExtension ); + + g_signal_connect( G_OBJECT(gdk_keymap_get_default()), "keys_changed", G_CALLBACK(signalKeysChanged), m_pGtkSalDisplay ); + + // add signal handler to notify screen size changes + int nScreens = gdk_display_get_n_screens( pGdkDisp ); + for( int n = 0; n < nScreens; n++ ) + { + GdkScreen *pScreen = gdk_display_get_screen( pGdkDisp, n ); + if( pScreen ) + { + g_signal_connect( G_OBJECT(pScreen), "size-changed", G_CALLBACK(signalScreenSizeChanged), m_pGtkSalDisplay ); + if( ! gtk_check_version( 2, 14, 0 ) ) // monitors-changed came in with 2.14, avoid an assertion + g_signal_connect( G_OBJECT(pScreen), "monitors-changed", G_CALLBACK(signalMonitorsChanged), m_pGtkSalDisplay ); + } + } +} + +extern "C" +{ + gboolean call_timeoutFn(gpointer data) + { + return GtkXLib::timeoutFn(data); + } +} + +gboolean GtkXLib::timeoutFn(gpointer data) +{ + SalData *pSalData = GetSalData(); + GtkXLib *pThis = (GtkXLib *) data; + + pSalData->m_pInstance->GetYieldMutex()->acquire(); + + if( pThis->m_pTimeout ) + { + g_source_unref (pThis->m_pTimeout); + pThis->m_pTimeout = NULL; + } + + // Auto-restart immediately + pThis->StartTimer( pThis->m_nTimeoutMS ); + + GetX11SalData()->Timeout(); + + pSalData->m_pInstance->GetYieldMutex()->release(); + + return FALSE; +} + +void GtkXLib::StartTimer( ULONG nMS ) +{ + m_nTimeoutMS = nMS; // for restarting + + if (m_pTimeout) + { + g_source_destroy (m_pTimeout); + g_source_unref (m_pTimeout); + } + + m_pTimeout = g_timeout_source_new (m_nTimeoutMS); + // #i36226# timers should be executed with lower priority + // than XEvents like in generic plugin + g_source_set_priority( m_pTimeout, G_PRIORITY_LOW ); + g_source_set_can_recurse (m_pTimeout, TRUE); + g_source_set_callback (m_pTimeout, call_timeoutFn, + (gpointer) this, NULL); + g_source_attach (m_pTimeout, g_main_context_default ()); + + SalXLib::StartTimer( nMS ); +} + +void GtkXLib::StopTimer() +{ + SalXLib::StopTimer(); + + if (m_pTimeout) + { + g_source_destroy (m_pTimeout); + g_source_unref (m_pTimeout); + m_pTimeout = NULL; + } +} + +extern "C" +{ + gboolean call_userEventFn( gpointer data ) + { + return GtkXLib::userEventFn( data ); + } +} + +gboolean GtkXLib::userEventFn(gpointer data) +{ + gboolean bContinue; + GtkXLib *pThis = (GtkXLib *) data; + SalData *pSalData = GetSalData(); + + pSalData->m_pInstance->GetYieldMutex()->acquire(); + pThis->m_pGtkSalDisplay->EventGuardAcquire(); + + if( !pThis->m_pGtkSalDisplay->HasMoreEvents() ) + { + if( pThis->m_pUserEvent ) + { + g_source_unref (pThis->m_pUserEvent); + pThis->m_pUserEvent = NULL; + } + bContinue = FALSE; + } + else + bContinue = TRUE; + + pThis->m_pGtkSalDisplay->EventGuardRelease(); + + pThis->m_pGtkSalDisplay->DispatchInternalEvent(); + + pSalData->m_pInstance->GetYieldMutex()->release(); + + return bContinue; +} + +// hEventGuard_ held during this invocation +void GtkXLib::PostUserEvent() +{ + if( !m_pUserEvent ) // not pending anyway + { + m_pUserEvent = g_idle_source_new(); + g_source_set_priority( m_pUserEvent, G_PRIORITY_HIGH ); + g_source_set_can_recurse (m_pUserEvent, TRUE); + g_source_set_callback (m_pUserEvent, call_userEventFn, + (gpointer) this, NULL); + g_source_attach (m_pUserEvent, g_main_context_default ()); + } + Wakeup(); +} + +void GtkXLib::Wakeup() +{ + g_main_context_wakeup( g_main_context_default () ); +} + +void GtkXLib::Yield( bool bWait, bool bHandleAllCurrentEvents ) +{ + /* #i33212# only enter g_main_context_iteration in one thread at any one + * time, else one of them potentially will never end as long as there is + * another thread in in there. Having only one yieldin thread actually dispatch + * fits the vcl event model (see e.g. the generic plugin). + */ + + bool bDispatchThread = false; + { + // release YieldMutex (and re-acquire at block end) + YieldMutexReleaser aReleaser; + if( osl_tryToAcquireMutex( m_aDispatchMutex ) ) + { + // we are the dispatch thread + osl_resetCondition( m_aDispatchCondition ); + bDispatchThread = true; + } + else if( ! bWait ) + return; // someone else is waiting already, return + + + if( bDispatchThread ) + { + int nMaxEvents = bHandleAllCurrentEvents ? 100 : 1; + gboolean wasEvent = FALSE, wasOneEvent = TRUE; + while( nMaxEvents-- && wasOneEvent ) + { + wasOneEvent = g_main_context_iteration( NULL, FALSE ); + if( wasOneEvent ) + wasEvent = TRUE; + } + if( bWait && ! wasEvent ) + g_main_context_iteration( NULL, TRUE ); + } + else if( userEventFn( this ) ) + { + /* #i41693# in case the dispatch thread hangs in join + * for this thread the condition will never be set + * workaround: timeout of 1 second a emergency exit + */ + TimeValue aValue; + aValue.Seconds = 1; + aValue.Nanosec = 0; + osl_waitCondition( m_aDispatchCondition, &aValue ); + } + } + + if( bDispatchThread ) + { + osl_releaseMutex( m_aDispatchMutex ); + osl_setCondition( m_aDispatchCondition ); // trigger non dispatch thread yields + osl_resetCondition( m_aDispatchCondition ); + } +} + +extern "C" { + +typedef struct { + GSource source; + + GPollFD pollfd; + GIOCondition condition; + + YieldFunc pending; + YieldFunc handle; + gpointer user_data; +} SalWatch; + +static gboolean +sal_source_prepare (GSource *source, + gint *timeout) +{ + SalWatch *watch = (SalWatch *)source; + + *timeout = -1; + + if (watch->pending && + watch->pending (watch->pollfd.fd, watch->user_data)) { + watch->pollfd.revents |= watch->condition; + return TRUE; + } + + return FALSE; +} + +static gboolean +sal_source_check (GSource *source) +{ + SalWatch *watch = (SalWatch *)source; + + return watch->pollfd.revents & watch->condition; +} + +static gboolean +sal_source_dispatch (GSource *source, + GSourceFunc, + gpointer) +{ + SalData *pSalData = GetSalData(); + SalWatch *watch = (SalWatch *) source; + + pSalData->m_pInstance->GetYieldMutex()->acquire(); + + watch->handle (watch->pollfd.fd, watch->user_data); + + pSalData->m_pInstance->GetYieldMutex()->release(); + + return TRUE; +} + +static void +sal_source_finalize (GSource*) +{ +} + +static GSourceFuncs sal_source_watch_funcs = { + sal_source_prepare, + sal_source_check, + sal_source_dispatch, + sal_source_finalize, + NULL, + NULL +}; + +static GSource * +sal_source_create_watch (int fd, + GIOCondition condition, + YieldFunc pending, + YieldFunc handle, + gpointer user_data) +{ + GSource *source; + SalWatch *watch; + GMainContext *context = g_main_context_default (); + + source = g_source_new (&sal_source_watch_funcs, + sizeof (SalWatch)); + watch = (SalWatch *) source; + + watch->pollfd.fd = fd; + watch->pollfd.events = condition; + watch->condition = condition; + watch->pending = pending; + watch->handle = handle; + watch->user_data = user_data; + + g_source_set_can_recurse (source, TRUE); + g_source_add_poll (source, &watch->pollfd); + g_source_attach (source, context); + + return source; +} + +} // extern "C" + +void GtkXLib::Insert( int nFD, + void *data, + YieldFunc pending, + YieldFunc, + YieldFunc handle ) +{ + GSource *source = sal_source_create_watch + ( nFD, (GIOCondition) ((G_IO_IN|G_IO_PRI) | + (G_IO_ERR|G_IO_HUP|G_IO_NVAL)), + pending, handle, data ); + m_aSources.push_back( source ); +} + +void GtkXLib::Remove( int nFD ) +{ + ::std::list< GSource * >::iterator it; + + for (it = m_aSources.begin(); it != m_aSources.end(); ++it) + { + SalWatch *watch = (SalWatch *) *it; + + if (watch->pollfd.fd == nFD) + { + m_aSources.erase( it ); + + g_source_destroy ((GSource *)watch); + g_source_unref ((GSource *)watch); + return; + } + } +} + +/********************************************************************** + * class GtkData * + **********************************************************************/ + +GtkData::~GtkData() +{ +} + +void GtkData::Init() +{ + pXLib_ = new GtkXLib(); + pXLib_->Init(); +} diff --git a/vcl/unx/gtk/app/gtkinst.cxx b/vcl/unx/gtk/app/gtkinst.cxx new file mode 100644 index 000000000000..68617c8c16be --- /dev/null +++ b/vcl/unx/gtk/app/gtkinst.cxx @@ -0,0 +1,352 @@ +/************************************************************************* + * + * 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 <osl/module.h> +#include <plugins/gtk/gtkdata.hxx> +#include <plugins/gtk/gtkinst.hxx> +#include <salframe.h> +#include <salobj.h> +#include <plugins/gtk/gtkframe.hxx> +#include <plugins/gtk/gtkobject.hxx> +#include <plugins/gtk/atkbridge.hxx> + +#include <rtl/strbuf.hxx> + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif + +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> + +GtkHookedYieldMutex::GtkHookedYieldMutex() +{ +} + +/* + * These methods always occur in pairs + * A ThreadsEnter is followed by a ThreadsLeave + * We need to queue up the recursive lock count + * for each pair, so we can accurately restore + * it later. + */ +void GtkHookedYieldMutex::ThreadsEnter() +{ + acquire(); + if( !aYieldStack.empty() ) + { /* Previously called ThreadsLeave() */ + ULONG nCount = aYieldStack.front(); + aYieldStack.pop_front(); + while( nCount-- > 1 ) + acquire(); + } +} + +void GtkHookedYieldMutex::ThreadsLeave() +{ + aYieldStack.push_front( mnCount ); + +#if OSL_DEBUG_LEVEL > 1 + if( mnThreadId && + mnThreadId != NAMESPACE_VOS(OThread)::getCurrentIdentifier()) + fprintf( stderr, "\n\n--- A different thread owns the mutex ...---\n\n\n"); +#endif + + while( mnCount > 1 ) + release(); + release(); +} + +void GtkHookedYieldMutex::acquire() +{ + SalYieldMutex::acquire(); +} + +void GtkHookedYieldMutex::release() +{ + SalYieldMutex::release(); +} + +extern "C" +{ + #define GET_YIELD_MUTEX() static_cast<GtkHookedYieldMutex*>(GetSalData()->m_pInstance->GetYieldMutex()) + static void GdkThreadsEnter( void ) + { + GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); + pYieldMutex->ThreadsEnter(); + } + static void GdkThreadsLeave( void ) + { + GtkHookedYieldMutex *pYieldMutex = GET_YIELD_MUTEX(); + pYieldMutex->ThreadsLeave(); + } + static bool hookLocks( oslModule pModule ) + { + typedef void (*GdkLockFn) (GCallback enter_fn, GCallback leave_fn); + + GdkLockFn gdk_threads_set_lock_functions = + (GdkLockFn) osl_getAsciiFunctionSymbol( pModule, "gdk_threads_set_lock_functions" ); + if ( !gdk_threads_set_lock_functions ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Failed to hook gdk threads locks\n" ); +#endif + return false; + } + + gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "Hooked gdk threads locks\n" ); +#endif + return true; + } + + VCL_DLLPUBLIC SalInstance* create_SalInstance( oslModule pModule ) + { + /* #i92121# workaround deadlocks in the X11 implementation + */ + static const char* pNoXInitThreads = getenv( "SAL_NO_XINITTHREADS" ); + /* #i90094# + from now on we know that an X connection will be + established, so protect X against itself + */ + if( ! ( pNoXInitThreads && *pNoXInitThreads ) ) + XInitThreads(); + + const gchar* pVersion = gtk_check_version( 2, 2, 0 ); + if( pVersion ) + { +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "gtk version conflict: %s\n", pVersion ); +#endif + return NULL; + } + + GtkYieldMutex *pYieldMutex; + + // init gdk thread protection + if ( !g_thread_supported() ) + g_thread_init( NULL ); + + if ( hookLocks( pModule ) ) + pYieldMutex = new GtkHookedYieldMutex(); + else + pYieldMutex = new GtkYieldMutex(); + + gdk_threads_init(); + + GtkInstance* pInstance = new GtkInstance( pYieldMutex ); +#if OSL_DEBUG_LEVEL > 1 + fprintf( stderr, "creating GtkSalInstance 0x%p\n", pInstance ); +#endif + + // initialize SalData + GtkData *pSalData = new GtkData(); + SetSalData( pSalData ); + pSalData->m_pInstance = pInstance; + pSalData->Init(); + pSalData->initNWF(); + + InitAtkBridge(); + + return pInstance; + } +} + +GtkInstance::~GtkInstance() +{ + DeInitAtkBridge(); +} + +SalFrame* GtkInstance::CreateFrame( SalFrame* pParent, ULONG nStyle ) +{ + return new GtkSalFrame( pParent, nStyle ); +} + +SalFrame* GtkInstance::CreateChildFrame( SystemParentData* pParentData, ULONG ) +{ + SalFrame* pFrame = new GtkSalFrame( pParentData ); + + return pFrame; +} + +SalObject* GtkInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, BOOL bShow ) +{ + // there is no method to set a visual for a GtkWidget + // so we need the X11SalObject in that case + if( pWindowData ) + return X11SalObject::CreateObject( pParent, pWindowData, bShow ); + + return new GtkSalObject( static_cast<GtkSalFrame*>(pParent), bShow ); +} + +extern "C" +{ + typedef void*(* getDefaultFnc)(); + typedef void(* addItemFnc)(void *, const char *); +} + +void GtkInstance::AddToRecentDocumentList(const rtl::OUString& rFileUrl, const rtl::OUString& rMimeType) +{ +#if GTK_CHECK_VERSION(2,10,0) + GtkRecentManager *manager = gtk_recent_manager_get_default (); + gtk_recent_manager_add_item (manager, rtl::OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8).getStr()); + (void)rMimeType; +#else + static getDefaultFnc sym_gtk_recent_manager_get_default = + (getDefaultFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_get_default" ); + + static addItemFnc sym_gtk_recent_manager_add_item = + (addItemFnc)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_recent_manager_add_item"); + if (sym_gtk_recent_manager_get_default && sym_gtk_recent_manager_add_item) + { + sym_gtk_recent_manager_add_item(sym_gtk_recent_manager_get_default(), + rtl::OUStringToOString(rFileUrl, RTL_TEXTENCODING_UTF8).getStr()); + } + else + X11SalInstance::AddToRecentDocumentList(rFileUrl, rMimeType); +#endif +} + +GtkYieldMutex::GtkYieldMutex() +{ +} + +void GtkYieldMutex::acquire() +{ + vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier(); + // protect member manipulation + OMutex::acquire(); + if( mnCount > 0 && mnThreadId == aCurrentThread ) + { + mnCount++; + OMutex::release(); + return; + } + OMutex::release(); + + // obtain gdk mutex + gdk_threads_enter(); + + // obtained gdk mutex, now lock count is one by definition + OMutex::acquire(); + mnCount = 1; + mnThreadId = aCurrentThread; + OMutex::release(); +} + +void GtkYieldMutex::release() +{ + vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier(); + // protect member manipulation + OMutex::acquire(); + // strange things happen, do nothing if we don't own the mutex + if( mnThreadId == aCurrentThread ) + { + mnCount--; + if( mnCount == 0 ) + { + gdk_threads_leave(); + mnThreadId = 0; + } + } + OMutex::release(); +} + +sal_Bool GtkYieldMutex::tryToAcquire() +{ + vos::OThread::TThreadIdentifier aCurrentThread = vos::OThread::getCurrentIdentifier(); + // protect member manipulation + OMutex::acquire(); + if( mnCount > 0 ) + { + if( mnThreadId == aCurrentThread ) + { + mnCount++; + OMutex::release(); + return sal_True; + } + else + { + OMutex::release(); + return sal_False; + } + } + OMutex::release(); + + // HACK: gdk_threads_mutex is private, we shouldn't use it. + // how to we do a try_lock without having a gdk_threads_try_enter ? + if( ! g_mutex_trylock( gdk_threads_mutex ) ) + return sal_False; + + // obtained gdk mutex, now lock count is one by definition + OMutex::acquire(); + mnCount = 1; + mnThreadId = aCurrentThread; + OMutex::release(); + + return sal_True; +} + +int GtkYieldMutex::Grab() +{ + // this MUST only be called by gdk/gtk callbacks: + // they are entered with gdk mutex locked; the mutex + // was unlocked by GtkYieldMutex befor yielding which + // is now locked again by gtk implicitly + + // obtained gdk mutex, now lock count is one by definition + OMutex::acquire(); + int nRet = mnCount; + if( mnCount == 0 ) // recursive else + mnThreadId = vos::OThread::getCurrentIdentifier(); +#if OSL_DEBUG_LEVEL > 1 + else if( mnThreadId != vos::OThread::getCurrentIdentifier() ) + { + fprintf( stderr, "Yield mutex grabbed in different thread !\n" ); + abort(); + } +#endif + mnCount = 1; + OMutex::release(); + return nRet; +} + +void GtkYieldMutex::Ungrab( int nGrabs ) +{ + // this MUST only be called when leaving the callback + // that locked the mutex with Grab() + OMutex::acquire(); + mnCount = nGrabs; + if( mnCount == 0 ) + mnThreadId = 0; + OMutex::release(); +} diff --git a/vcl/unx/gtk/app/gtksys.cxx b/vcl/unx/gtk/app/gtksys.cxx new file mode 100644 index 000000000000..272af20d0886 --- /dev/null +++ b/vcl/unx/gtk/app/gtksys.cxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * 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 <svunx.h> +#include <vcl/svdata.hxx> +#include <vcl/window.hxx> +#include <plugins/gtk/gtkinst.hxx> +#include <cstdio> +#include <gdk/gdk.h> +#include <gtk/gtk.h> +#include <X11/Xlib.h> + +SalSystem *GtkInstance::CreateSalSystem() +{ + return new GtkSalSystem(); +} + +GtkSalSystem::~GtkSalSystem() +{ +} + +int GtkSalSystem::ShowNativeDialog( const String& rTitle, + const String& rMessage, + const std::list< String >& rButtons, + int nDefButton ) +{ + + ImplSVData* pSVData = ImplGetSVData(); + if( pSVData->mpIntroWindow ) + pSVData->mpIntroWindow->Hide(); + +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "GtkSalSystem::ShowNativeDialog\n"); +#endif + + ByteString aTitle( rTitle, RTL_TEXTENCODING_UTF8 ); + ByteString aMessage( rMessage, RTL_TEXTENCODING_UTF8 ); + + /* Create the dialogue */ + GtkWidget* mainwin = gtk_message_dialog_new + ( NULL, (GtkDialogFlags)0, GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, aMessage.GetBuffer(), NULL ); + gtk_window_set_title( GTK_WINDOW( mainwin ), aTitle.GetBuffer() ); + + gint nButtons = 0, nResponse; + + int nButton = 0; + for( std::list< String >::const_iterator it = rButtons.begin(); it != rButtons.end(); ++it ) + { + ByteString aLabel( *it, RTL_TEXTENCODING_UTF8 ); + + if( nButton == nDefButton ) + { + gtk_dialog_add_button( GTK_DIALOG( mainwin ), aLabel.GetBuffer(), nButtons ); + gtk_dialog_set_default_response( GTK_DIALOG( mainwin ), nButtons ); + } + else + gtk_dialog_add_button( GTK_DIALOG( mainwin ), aLabel.GetBuffer(), nButtons ); + nButtons++; + } + + nResponse = gtk_dialog_run( GTK_DIALOG(mainwin) ); + if( nResponse == GTK_RESPONSE_NONE || nResponse == GTK_RESPONSE_DELETE_EVENT ) + nResponse = -1; + + gtk_widget_destroy( GTK_WIDGET(mainwin) ); + + return nResponse; +} diff --git a/vcl/unx/gtk/app/makefile.mk b/vcl/unx/gtk/app/makefile.mk new file mode 100644 index 000000000000..3e8cd750bf7a --- /dev/null +++ b/vcl/unx/gtk/app/makefile.mk @@ -0,0 +1,76 @@ +#************************************************************************* +# +# 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=gtkapp +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# workaround for makedepend hang +MKDEPENDSOLVER= + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- 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 + +SLOFILES=\ + $(SLO)$/gtkdata.obj \ + $(SLO)$/gtkinst.obj \ + $(SLO)$/gtksys.obj + +EXCEPTIONSFILES=\ + $(SLO)$/gtkdata.obj\ + $(SLO)$/gtkinst.obj + +.ELSE # "$(ENABLE_GTK)" != "" + +dummy: + @echo GTK disabled - nothing to build +.ENDIF +.ENDIF # "$(GUIBASE)"!="unx" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk diff --git a/vcl/unx/gtk/gdi/makefile.mk b/vcl/unx/gtk/gdi/makefile.mk new file mode 100644 index 000000000000..ec6b4e1d205e --- /dev/null +++ b/vcl/unx/gtk/gdi/makefile.mk @@ -0,0 +1,67 @@ +#************************************************************************* +# +# 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=gtkgdi +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- 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 + +SLOFILES=$(SLO)$/salnativewidgets-gtk.obj +EXCEPTIONSFILES=$(SLO)$/salnativewidgets-gtk.obj + +.ELSE # "$(ENABLE_GTK)" != "" +dummy: + @echo GTK disabled - nothing to build +.ENDIF + +.ENDIF # "$(GUIBASE)"!="unx" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk diff --git a/vcl/unx/gtk/gdi/salnativewidgets-gtk.cxx b/vcl/unx/gtk/gdi/salnativewidgets-gtk.cxx new file mode 100644 index 000000000000..de4d55b0230a --- /dev/null +++ b/vcl/unx/gtk/gdi/salnativewidgets-gtk.cxx @@ -0,0 +1,4146 @@ +/************************************************************************* + * + * 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 "plugins/gtk/gtkdata.hxx" +#include "plugins/gtk/gtkinst.hxx" +#include "plugins/gtk/gtkgdi.hxx" + +#include "pspgraphics.h" + +#include <cstdio> +#include <cmath> +#include <vector> +#include <algorithm> +#include <hash_map> + +#include "saldata.hxx" +#include "saldisp.hxx" +#include "vcl/svapp.hxx" + +typedef struct _cairo_font_options cairo_font_options_t; + +// initialize statics +BOOL GtkSalGraphics::bThemeChanged = TRUE; +BOOL GtkSalGraphics::bNeedPixmapPaint = FALSE; +BOOL GtkSalGraphics::bGlobalNeedPixmapPaint = FALSE; +BOOL GtkSalGraphics::bToolbarGripWorkaround = FALSE; +BOOL GtkSalGraphics::bNeedButtonStyleAsEditBackgroundWorkaround = FALSE; + +GtkSalGraphics::~GtkSalGraphics() +{ +} + + +using namespace rtl; + +/************************************* + * Cached native widget objects + *************************************/ +class NWPixmapCacheList; +class NWPixmapCache; +struct NWFWidgetData +{ + GtkWidget * gCacheWindow; + GtkWidget * gDumbContainer; + + GtkWidget * gBtnWidget; + GtkWidget * gRadioWidget; + GtkWidget * gRadioWidgetSibling; + GtkWidget * gCheckWidget; + GtkWidget * gScrollHorizWidget; + GtkWidget * gScrollVertWidget; + GtkWidget * gArrowWidget; + GtkWidget * gDropdownWidget; + GtkWidget * gEditBoxWidget; + GtkWidget * gSpinButtonWidget; + GtkWidget * gNotebookWidget; + GtkWidget * gOptionMenuWidget; + GtkWidget * gComboWidget; + GtkWidget * gScrolledWindowWidget; + GtkWidget * gToolbarWidget; + GtkWidget * gToolbarButtonWidget; + GtkWidget * gToolbarToggleWidget; + GtkWidget * gHandleBoxWidget; + GtkWidget * gMenubarWidget; + GtkWidget * gMenuItemMenubarWidget; + GtkWidget * gMenuWidget; + GtkWidget * gMenuItemMenuWidget; + GtkWidget * gMenuItemCheckMenuWidget; + GtkWidget * gMenuItemRadioMenuWidget; + GtkWidget * gImageMenuItem; + GtkWidget * gTooltipPopup; + GtkWidget * gProgressBar; + GtkWidget * gTreeView; + GtkWidget * gHScale; + GtkWidget * gVScale; + + NWPixmapCacheList* gNWPixmapCacheList; + NWPixmapCache* gCacheTabItems; + NWPixmapCache* gCacheTabPages; + + NWFWidgetData() : + gCacheWindow( NULL ), + gDumbContainer( NULL ), + gBtnWidget( NULL ), + gRadioWidget( NULL ), + gRadioWidgetSibling( NULL ), + gCheckWidget( NULL ), + gScrollHorizWidget( NULL ), + gScrollVertWidget( NULL ), + gArrowWidget( NULL ), + gDropdownWidget( NULL ), + gEditBoxWidget( NULL ), + gSpinButtonWidget( NULL ), + gNotebookWidget( NULL ), + gOptionMenuWidget( NULL ), + gComboWidget( NULL ), + gScrolledWindowWidget( NULL ), + gToolbarWidget( NULL ), + gToolbarButtonWidget( NULL ), + gToolbarToggleWidget( NULL ), + gHandleBoxWidget( NULL ), + gMenubarWidget( NULL ), + gMenuItemMenubarWidget( NULL ), + gMenuWidget( NULL ), + gMenuItemMenuWidget( NULL ), + gMenuItemCheckMenuWidget( NULL ), + gMenuItemRadioMenuWidget( NULL ), + gImageMenuItem( NULL ), + gTooltipPopup( NULL ), + gProgressBar( NULL ), + gTreeView( NULL ), + gHScale( NULL ), + gVScale( NULL ), + gNWPixmapCacheList( NULL ), + gCacheTabItems( NULL ), + gCacheTabPages( NULL ) + {} +}; + +// Keep a hash table of Widgets->default flags so that we can +// easily and quickly reset each to a default state before using +// them +static std::hash_map<long, guint> gWidgetDefaultFlags; +static std::vector<NWFWidgetData> gWidgetData; + +static const GtkBorder aDefDefBorder = { 1, 1, 1, 1 }; + +// Some GTK defaults +#define MIN_ARROW_SIZE 11 +#define BTN_CHILD_SPACING 1 +#define MIN_SPIN_ARROW_WIDTH 6 + + +static void NWEnsureGTKRadio ( int nScreen ); +static void NWEnsureGTKButton ( int nScreen ); +static void NWEnsureGTKCheck ( int nScreen ); +static void NWEnsureGTKScrollbars ( int nScreen ); +static void NWEnsureGTKArrow ( int nScreen ); +static void NWEnsureGTKEditBox ( int nScreen ); +static void NWEnsureGTKSpinButton ( int nScreen ); +static void NWEnsureGTKNotebook ( int nScreen ); +static void NWEnsureGTKOptionMenu ( int nScreen ); +static void NWEnsureGTKCombo ( int nScreen ); +static void NWEnsureGTKScrolledWindow ( int nScreen ); +static void NWEnsureGTKToolbar ( int nScreen ); +static void NWEnsureGTKMenubar ( int nScreen ); +static void NWEnsureGTKMenu ( int nScreen ); +static void NWEnsureGTKTooltip ( int nScreen ); +static void NWEnsureGTKProgressBar ( int nScreen ); +static void NWEnsureGTKTreeView ( int nScreen ); +static void NWEnsureGTKSlider ( int nScreen ); + +static void NWConvertVCLStateToGTKState( ControlState nVCLState, GtkStateType* nGTKState, GtkShadowType* nGTKShadow ); +static void NWAddWidgetToCacheWindow( GtkWidget* widget, int nScreen ); +static void NWSetWidgetState( GtkWidget* widget, ControlState nState, GtkStateType nGtkState ); + +static void NWCalcArrowRect( const Rectangle& rButton, Rectangle& rArrow ); + +/* + * Individual helper functions + * + */ + +//--- +static Rectangle NWGetButtonArea( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +//--- +static Rectangle NWGetEditBoxPixmapRect( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +static void NWPaintOneEditBox( int nScreen, GdkDrawable * gdkDrawable, GdkRectangle *gdkRect, + ControlType nType, ControlPart nPart, Rectangle aEditBoxRect, + ControlState nState, const ImplControlValue& aValue, + SalControlHandle& rControlHandle, const OUString& rCaption ); + +//--- +static Rectangle NWGetSpinButtonRect( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +static void NWPaintOneSpinButton( int nScreen, GdkPixmap * pixmap, ControlType nType, ControlPart nPart, Rectangle aAreaRect, + ControlState nState, const ImplControlValue& aValue, SalControlHandle& rControlHandle, + const OUString& rCaption ); +//--- +static Rectangle NWGetComboBoxButtonRect( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +//--- +static Rectangle NWGetListBoxButtonRect( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +static Rectangle NWGetListBoxIndicatorRect( int nScreen, ControlType nType, ControlPart nPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, const OUString& rCaption ); + +static Rectangle NWGetToolbarRect( int nScreen, + ControlType nType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, + const OUString& rCaption ); +//--- + +static Rectangle NWGetScrollButtonRect( int nScreen, ControlPart nPart, Rectangle aAreaRect ); +//--- + +/********************************************************* + * PixmapCache + *********************************************************/ + +// as some native widget drawing operations are pretty slow +// with certain themes (eg tabpages) +// this cache can be used to cache the corresponding pixmap +// see NWPaintGTKTabItem + +class NWPixmapCacheData +{ +public: + ControlType m_nType; + ControlState m_nState; + Rectangle m_pixmapRect; + GdkPixmap* m_pixmap; + + NWPixmapCacheData() : m_nType(0), m_nState(0), m_pixmap(0) {} + ~NWPixmapCacheData() + { SetPixmap( NULL ); }; + void SetPixmap( GdkPixmap* pPixmap ); +}; + +class NWPixmapCache +{ + int m_size; + int m_idx; + int m_screen; + NWPixmapCacheData* pData; +public: + NWPixmapCache( int nScreen ); + ~NWPixmapCache(); + + void SetSize( int n) + { delete [] pData; m_idx = 0; m_size = n; pData = new NWPixmapCacheData[m_size]; } + int GetSize() { return m_size; } + + BOOL Find( ControlType aType, ControlState aState, const Rectangle& r_pixmapRect, GdkPixmap** pPixmap ); + void Fill( ControlType aType, ControlState aState, const Rectangle& r_pixmapRect, GdkPixmap* pPixmap ); + + void ThemeChanged(); +}; + +class NWPixmapCacheList +{ +public: + ::std::vector< NWPixmapCache* > mCaches; + + void AddCache( NWPixmapCache *pCache ); + void RemoveCache( NWPixmapCache *pCache ); + void ThemeChanged(); +}; + +// --- implementation --- + +void NWPixmapCacheData::SetPixmap( GdkPixmap* pPixmap ) +{ + if( m_pixmap ) + g_object_unref( m_pixmap ); + + m_pixmap = pPixmap; + + if( m_pixmap ) + g_object_ref( m_pixmap ); +} + + +NWPixmapCache::NWPixmapCache( int nScreen ) +{ + m_idx = 0; + m_size = 0; + m_screen = nScreen; + pData = NULL; + if( gWidgetData[m_screen].gNWPixmapCacheList ) + gWidgetData[m_screen].gNWPixmapCacheList->AddCache(this); +} +NWPixmapCache::~NWPixmapCache() +{ + if( gWidgetData[m_screen].gNWPixmapCacheList ) + gWidgetData[m_screen].gNWPixmapCacheList->RemoveCache(this); + delete[] pData; +} +void NWPixmapCache::ThemeChanged() +{ + // throw away cached pixmaps + int i; + for(i=0; i<m_size; i++) + pData[i].SetPixmap( NULL ); +} + +BOOL NWPixmapCache::Find( ControlType aType, ControlState aState, const Rectangle& r_pixmapRect, GdkPixmap** pPixmap ) +{ + aState &= ~CTRL_CACHING_ALLOWED; // mask clipping flag + int i; + for(i=0; i<m_size; i++) + { + if( pData[i].m_nType == aType && + pData[i].m_nState == aState && + pData[i].m_pixmapRect.GetWidth() == r_pixmapRect.GetWidth() && + pData[i].m_pixmapRect.GetHeight() == r_pixmapRect.GetHeight() && + pData[i].m_pixmap != NULL ) + { + *pPixmap = pData[i].m_pixmap; + return TRUE; + } + } + return FALSE; +} + +void NWPixmapCache::Fill( ControlType aType, ControlState aState, const Rectangle& r_pixmapRect, GdkPixmap* pPixmap ) +{ + if( !(aState & CTRL_CACHING_ALLOWED) ) + return; + + aState &= ~CTRL_CACHING_ALLOWED; // mask clipping flag + m_idx = (m_idx+1) % m_size; // just wrap + pData[m_idx].m_nType = aType; + pData[m_idx].m_nState = aState; + pData[m_idx].m_pixmapRect = r_pixmapRect; + pData[m_idx].SetPixmap( pPixmap ); +} + + +void NWPixmapCacheList::AddCache( NWPixmapCache* pCache ) +{ + mCaches.push_back( pCache ); +} +void NWPixmapCacheList::RemoveCache( NWPixmapCache* pCache ) +{ + ::std::vector< NWPixmapCache* >::iterator p; + p = ::std::find( mCaches.begin(), mCaches.end(), pCache ); + if( p != mCaches.end() ) + mCaches.erase( p ); +} +void NWPixmapCacheList::ThemeChanged( ) +{ + ::std::vector< NWPixmapCache* >::iterator p = mCaches.begin(); + while( p != mCaches.end() ) + { + (*p)->ThemeChanged(); + p++; + } +} + + +/********************************************************* + * Make border manipulation easier + *********************************************************/ +inline void NW_gtk_border_set_from_border( GtkBorder& aDst, const GtkBorder * pSrc ) +{ + aDst.left = pSrc->left; + aDst.top = pSrc->top; + aDst.right = pSrc->right; + aDst.bottom = pSrc->bottom; +} + + +/********************************************************* + * Initialize GTK and local stuff + *********************************************************/ +void GtkData::initNWF( void ) +{ + ImplSVData* pSVData = ImplGetSVData(); + + // draw no border for popup menus (NWF draws its own) + pSVData->maNWFData.mbFlatMenu = true; + + // draw separate buttons for toolbox dropdown items + pSVData->maNWFData.mbToolboxDropDownSeparate = true; + + // small extra border around menu items + pSVData->maNWFData.mnMenuFormatExtraBorder = 1; + + // draw toolbars in separate lines + pSVData->maNWFData.mbDockingAreaSeparateTB = true; + + // open first menu on F10 + pSVData->maNWFData.mbOpenMenuOnF10 = true; + + int nScreens = GetX11SalData()->GetDisplay()->GetScreenCount(); + gWidgetData = std::vector<NWFWidgetData>( nScreens ); + for( int i = 0; i < nScreens; i++ ) + gWidgetData[i].gNWPixmapCacheList = new NWPixmapCacheList; + + + if( SalGetDesktopEnvironment().equalsAscii( "KDE" ) ) + { + // #i97196# ensure a widget exists and the style engine was loaded + NWEnsureGTKButton( 0 ); + if( g_type_from_name( "QtEngineStyle" ) ) + { + // KDE 3.3 invented a bug in the qt<->gtk theme engine + // that makes direct rendering impossible: they totally + // ignore the clip rectangle passed to the paint methods + GtkSalGraphics::bNeedPixmapPaint = GtkSalGraphics::bGlobalNeedPixmapPaint = true; + } + } + static const char* pEnv = getenv( "SAL_GTK_USE_PIXMAPPAINT" ); + if( pEnv && *pEnv ) + GtkSalGraphics::bNeedPixmapPaint = GtkSalGraphics::bGlobalNeedPixmapPaint = true; + + #if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "GtkPlugin: using %s NWF\n", + GtkSalGraphics::bNeedPixmapPaint ? "offscreen" : "direct" ); + #endif +} + + +/********************************************************* + * Release GTK and local stuff + *********************************************************/ +void GtkData::deInitNWF( void ) +{ + + for( unsigned int i = 0; i < gWidgetData.size(); i++ ) + { + // free up global widgets + // gtk_widget_destroy will in turn destroy the child hierarchy + // so only destroy disjunct hierachies + if( gWidgetData[i].gCacheWindow ) + gtk_widget_destroy( gWidgetData[i].gCacheWindow ); + if( gWidgetData[i].gMenuWidget ) + gtk_widget_destroy( gWidgetData[i].gMenuWidget ); + if( gWidgetData[i].gTooltipPopup ) + gtk_widget_destroy( gWidgetData[i].gTooltipPopup ); + delete gWidgetData[i].gCacheTabPages; + gWidgetData[i].gCacheTabPages = NULL; + delete gWidgetData[i].gCacheTabItems; + gWidgetData[i].gCacheTabItems = NULL; + delete gWidgetData[i].gNWPixmapCacheList; + gWidgetData[i].gNWPixmapCacheList = NULL; + } +} + + +/********************************************************** + * track clip region + **********************************************************/ +void GtkSalGraphics::ResetClipRegion() +{ + m_aClipRegion.SetNull(); + X11SalGraphics::ResetClipRegion(); +} + +void GtkSalGraphics::BeginSetClipRegion( ULONG nCount ) +{ + m_aClipRegion.SetNull(); + X11SalGraphics::BeginSetClipRegion( nCount ); +} + +BOOL GtkSalGraphics::unionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) ); + m_aClipRegion.Union( aRect ); + return X11SalGraphics::unionClipRegion( nX, nY, nWidth, nHeight ); +} + +bool GtkSalGraphics::unionClipRegion( const ::basegfx::B2DPolyPolygon& ) +{ + // TODO: implement and advertise OutDevSupport_B2DClip support + return false; +} + +void GtkSalGraphics::EndSetClipRegion() +{ + if( m_aClipRegion.IsEmpty() ) + m_aClipRegion.SetNull(); + X11SalGraphics::EndSetClipRegion(); +} + +void GtkSalGraphics::copyBits( const SalTwoRect* pPosAry, + SalGraphics* pSrcGraphics ) +{ + GtkSalFrame* pFrame = GetGtkFrame(); + XLIB_Window aWin = None; + if( pFrame && m_pWindow ) + { + /* #i64117# some themes set the background pixmap VERY frequently */ + GdkWindow* pWin = GTK_WIDGET(m_pWindow)->window; + if( pWin ) + { + aWin = GDK_WINDOW_XWINDOW(pWin); + if( aWin != None ) + XSetWindowBackgroundPixmap( pFrame->getDisplay()->GetDisplay(), + aWin, + None ); + } + } + X11SalGraphics::copyBits( pPosAry, pSrcGraphics ); + if( pFrame && pFrame->getBackgroundPixmap() != None ) + XSetWindowBackgroundPixmap( pFrame->getDisplay()->GetDisplay(), + aWin, + pFrame->getBackgroundPixmap() ); +} + +/* + * IsNativeControlSupported() + * + * Returns TRUE if the platform supports native + * drawing of the control defined by nPart + */ +BOOL GtkSalGraphics::IsNativeControlSupported( ControlType nType, ControlPart nPart ) +{ + if ( + ((nType==CTRL_PUSHBUTTON) && (nPart==PART_ENTIRE_CONTROL)) || + ((nType==CTRL_RADIOBUTTON) && (nPart==PART_ENTIRE_CONTROL)) || + ((nType==CTRL_CHECKBOX) && (nPart==PART_ENTIRE_CONTROL)) || + ((nType==CTRL_SCROLLBAR) && + ( (nPart==PART_DRAW_BACKGROUND_HORZ) + || (nPart==PART_DRAW_BACKGROUND_VERT) + || (nPart==PART_ENTIRE_CONTROL) + || (nPart==HAS_THREE_BUTTONS) ) ) || + ((nType==CTRL_EDITBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==HAS_BACKGROUND_TEXTURE) ) ) || + ((nType==CTRL_MULTILINE_EDITBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==HAS_BACKGROUND_TEXTURE) ) ) || + ((nType==CTRL_SPINBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_ALL_BUTTONS) + || (nPart==HAS_BACKGROUND_TEXTURE) ) ) || + ((nType==CTRL_SPINBUTTONS) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_ALL_BUTTONS) ) ) || + ((nType==CTRL_COMBOBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==HAS_BACKGROUND_TEXTURE) ) ) || + (((nType==CTRL_TAB_ITEM) || (nType==CTRL_TAB_PANE) || + (nType==CTRL_TAB_BODY) || (nType==CTRL_FIXEDBORDER)) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_TABS_DRAW_RTL) ) ) || + ((nType==CTRL_LISTBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_WINDOW) + || (nPart==HAS_BACKGROUND_TEXTURE) ) ) || + ((nType == CTRL_TOOLBAR) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_DRAW_BACKGROUND_HORZ) + || (nPart==PART_DRAW_BACKGROUND_VERT) + || (nPart==PART_THUMB_HORZ) + || (nPart==PART_THUMB_VERT) + || (nPart==PART_BUTTON) + ) + ) || + ((nType == CTRL_MENUBAR) && + ( (nPart==PART_ENTIRE_CONTROL) ) ) || + ((nType == CTRL_TOOLTIP) && + ( (nPart==PART_ENTIRE_CONTROL) ) ) || + ((nType == CTRL_MENU_POPUP) && + ( (nPart==PART_ENTIRE_CONTROL) + || (nPart==PART_MENU_ITEM) + || (nPart==PART_MENU_ITEM_CHECK_MARK) + || (nPart==PART_MENU_ITEM_RADIO_MARK) + ) + ) || + ((nType == CTRL_PROGRESS) && + ( (nPart == PART_ENTIRE_CONTROL) ) + ) || + ((nType == CTRL_LISTNODE || nType == CTRL_LISTNET) && + ( (nPart == PART_ENTIRE_CONTROL) ) + ) || + ((nType == CTRL_SLIDER) && + ( (nPart == PART_TRACK_HORZ_AREA) + || (nPart == PART_TRACK_VERT_AREA) + ) + ) + ) + return( TRUE ); + + return( FALSE ); +} + + +/* + * HitTestNativeControl() + * + * bIsInside is set to TRUE if aPos is contained within the + * given part of the control, whose bounding region is + * given by rControlRegion (in VCL frame coordinates). + * + * returns whether bIsInside was really set. + */ +BOOL GtkSalGraphics::hitTestNativeControl( ControlType nType, + ControlPart nPart, + const Region& rControlRegion, + const Point& aPos, + SalControlHandle&, + BOOL& rIsInside ) +{ + if ( ( nType == CTRL_SCROLLBAR ) && + ( ( nPart == PART_BUTTON_UP ) || + ( nPart == PART_BUTTON_DOWN ) || + ( nPart == PART_BUTTON_LEFT ) || + ( nPart == PART_BUTTON_RIGHT ) ) ) + { + NWEnsureGTKScrollbars( m_nScreen ); + + // Grab some button style attributes + gboolean has_forward; + gboolean has_forward2; + gboolean has_backward; + gboolean has_backward2; + + gtk_widget_style_get( gWidgetData[m_nScreen].gScrollHorizWidget, "has-forward-stepper", &has_forward, + "has-secondary-forward-stepper", &has_forward2, + "has-backward-stepper", &has_backward, + "has-secondary-backward-stepper", &has_backward2, (char *)NULL ); + Rectangle aForward; + Rectangle aBackward; + + rIsInside = FALSE; + + ControlPart nCounterPart = 0; + if ( nPart == PART_BUTTON_UP ) + nCounterPart = PART_BUTTON_DOWN; + else if ( nPart == PART_BUTTON_DOWN ) + nCounterPart = PART_BUTTON_UP; + else if ( nPart == PART_BUTTON_LEFT ) + nCounterPart = PART_BUTTON_RIGHT; + else if ( nPart == PART_BUTTON_RIGHT ) + nCounterPart = PART_BUTTON_LEFT; + + aBackward = NWGetScrollButtonRect( m_nScreen, nPart, rControlRegion.GetBoundRect() ); + aForward = NWGetScrollButtonRect( m_nScreen, nCounterPart, rControlRegion.GetBoundRect() ); + + if ( has_backward && has_forward2 ) + { + Size aSize( aBackward.GetSize() ); + if ( ( nPart == PART_BUTTON_UP ) || ( nPart == PART_BUTTON_DOWN ) ) + aSize.setHeight( aBackward.GetHeight() / 2 ); + else + aSize.setWidth( aBackward.GetWidth() / 2 ); + aBackward.SetSize( aSize ); + + if ( nPart == PART_BUTTON_DOWN ) + aBackward.Move( 0, aBackward.GetHeight() / 2 ); + else if ( nPart == PART_BUTTON_RIGHT ) + aBackward.Move( aBackward.GetWidth() / 2, 0 ); + } + + if ( has_backward2 && has_forward ) + { + Size aSize( aForward.GetSize() ); + if ( ( nPart == PART_BUTTON_UP ) || ( nPart == PART_BUTTON_DOWN ) ) + aSize.setHeight( aForward.GetHeight() / 2 ); + else + aSize.setWidth( aForward.GetWidth() / 2 ); + aForward.SetSize( aSize ); + + if ( nPart == PART_BUTTON_DOWN ) + aForward.Move( 0, aForward.GetHeight() / 2 ); + else if ( nPart == PART_BUTTON_RIGHT ) + aForward.Move( aForward.GetWidth() / 2, 0 ); + } + + if ( ( nPart == PART_BUTTON_UP ) || ( nPart == PART_BUTTON_LEFT ) ) + { + if ( has_backward ) + rIsInside |= aBackward.IsInside( aPos ); + if ( has_backward2 ) + rIsInside |= aForward.IsInside( aPos ); + } + else + { + if ( has_forward ) + rIsInside |= aBackward.IsInside( aPos ); + if ( has_forward2 ) + rIsInside |= aForward.IsInside( aPos ); + } + return ( TRUE ); + } + + if( IsNativeControlSupported(nType, nPart) ) + { + rIsInside = rControlRegion.IsInside( aPos ); + return( TRUE ); + } + else + { + return( FALSE ); + } +} + + +/* + * DrawNativeControl() + * + * Draws the requested control described by nPart/nState. + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * rControlHandle: Carries platform dependent data and is maintained by the SalFrame implementation. + * rCaption: A caption or title string (like button text etc) + */ +BOOL GtkSalGraphics::drawNativeControl( ControlType nType, + ControlPart nPart, + const Region& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, + const OUString& rCaption ) +{ + if( (nType==CTRL_CHECKBOX) && (nPart==PART_ENTIRE_CONTROL) && + aValue.getTristateVal() == BUTTONVALUE_MIXED ) + { + return drawNativeMixedStateCheck( nType, nPart, rControlRegion, nState, aValue, rControlHandle, rCaption ); + } + + BOOL returnVal = FALSE; + // get a GC with current clipping region set + SelectFont(); + + + // theme changed ? + if( GtkSalGraphics::bThemeChanged ) + { + // invalidate caches + for( unsigned int i = 0; i < gWidgetData.size(); i++ ) + if( gWidgetData[i].gNWPixmapCacheList ) + gWidgetData[i].gNWPixmapCacheList->ThemeChanged(); + GtkSalGraphics::bThemeChanged = FALSE; + } + + Rectangle aCtrlRect = rControlRegion.GetBoundRect(); + Region aClipRegion( m_aClipRegion ); + if( aClipRegion.IsNull() ) + aClipRegion = aCtrlRect; + + clipList aClip; + GdkDrawable* gdkDrawable = GDK_DRAWABLE( GetGdkWindow() ); + GdkPixmap* pixmap = NULL; + Rectangle aPixmapRect; + if( ( bNeedPixmapPaint ) + && nType != CTRL_SCROLLBAR + && nType != CTRL_SPINBOX + && nType != CTRL_TAB_ITEM + && nType != CTRL_TAB_PANE + && nType != CTRL_PROGRESS + && ! (bToolbarGripWorkaround && nType == CTRL_TOOLBAR && (nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT) ) + ) + { + // make pixmap a little larger since some themes draw decoration + // outside the rectangle, see e.g. checkbox + aPixmapRect = Rectangle( Point( aCtrlRect.Left()-1, aCtrlRect.Top()-1 ), + Size( aCtrlRect.GetWidth()+2, aCtrlRect.GetHeight()+2) ); + pixmap = NWGetPixmapFromScreen( aPixmapRect ); + if( ! pixmap ) + return FALSE; + gdkDrawable = GDK_DRAWABLE( pixmap ); + aCtrlRect = Rectangle( Point(1,1), aCtrlRect.GetSize() ); + aClip.push_back( aCtrlRect ); + } + else + { + RegionHandle aHdl = aClipRegion.BeginEnumRects(); + Rectangle aPaintRect; + while( aClipRegion.GetNextEnumRect( aHdl, aPaintRect ) ) + { + aPaintRect = aCtrlRect.GetIntersection( aPaintRect ); + if( aPaintRect.IsEmpty() ) + continue; + aClip.push_back( aPaintRect ); + } + aClipRegion.EndEnumRects( aHdl ); + } + + if ( (nType==CTRL_PUSHBUTTON) && (nPart==PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKButton( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType==CTRL_RADIOBUTTON) && (nPart==PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKRadio( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType==CTRL_CHECKBOX) && (nPart==PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKCheck( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType==CTRL_SCROLLBAR) && ((nPart==PART_DRAW_BACKGROUND_HORZ) || (nPart==PART_DRAW_BACKGROUND_VERT)) ) + { + returnVal = NWPaintGTKScrollbar( nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( ((nType==CTRL_EDITBOX) && ((nPart==PART_ENTIRE_CONTROL) || (nPart==HAS_BACKGROUND_TEXTURE)) ) + || ((nType==CTRL_SPINBOX) && (nPart==HAS_BACKGROUND_TEXTURE)) + || ((nType==CTRL_COMBOBOX) && (nPart==HAS_BACKGROUND_TEXTURE)) + || ((nType==CTRL_LISTBOX) && (nPart==HAS_BACKGROUND_TEXTURE)) ) + { + returnVal = NWPaintGTKEditBox( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( ((nType==CTRL_MULTILINE_EDITBOX) && ((nPart==PART_ENTIRE_CONTROL) || (nPart==HAS_BACKGROUND_TEXTURE)) ) ) + { + returnVal = NWPaintGTKEditBox( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( ((nType==CTRL_SPINBOX) || (nType==CTRL_SPINBUTTONS)) + && ((nPart==PART_ENTIRE_CONTROL) || (nPart==PART_ALL_BUTTONS)) ) + { + returnVal = NWPaintGTKSpinBox( nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType == CTRL_COMBOBOX) && + ( (nPart==PART_ENTIRE_CONTROL) + ||(nPart==PART_BUTTON_DOWN) + ) ) + { + returnVal = NWPaintGTKComboBox( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType==CTRL_TAB_ITEM) || (nType==CTRL_TAB_PANE) || (nType==CTRL_TAB_BODY) || (nType==CTRL_FIXEDBORDER) ) + { + if ( nType == CTRL_TAB_BODY ) + returnVal = TRUE; + else + returnVal = NWPaintGTKTabItem( nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption); + } + else if ( (nType==CTRL_LISTBOX) && ((nPart==PART_ENTIRE_CONTROL) || (nPart==PART_WINDOW)) ) + { + returnVal = NWPaintGTKListBox( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType== CTRL_TOOLBAR) ) + { + returnVal = NWPaintGTKToolbar( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if ( (nType== CTRL_MENUBAR) ) + { + returnVal = NWPaintGTKMenubar( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if( (nType == CTRL_MENU_POPUP) + && ( (nPart == PART_ENTIRE_CONTROL) + || (nPart == PART_MENU_ITEM) + || (nPart == PART_MENU_ITEM_CHECK_MARK) + || (nPart == PART_MENU_ITEM_RADIO_MARK) + ) + ) + { + returnVal = NWPaintGTKPopupMenu( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if( (nType == CTRL_TOOLTIP) && (nPart == PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKTooltip( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if( (nType == CTRL_PROGRESS) && (nPart == PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKProgress( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if( (nType == CTRL_LISTNODE) && (nPart == PART_ENTIRE_CONTROL) ) + { + returnVal = NWPaintGTKListNode( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + else if( (nType == CTRL_LISTNET) && (nPart == PART_ENTIRE_CONTROL) ) + { + // don't actually draw anything; gtk treeviews do not draw lines + returnVal = true; + } + else if( (nType == CTRL_SLIDER) ) + { + returnVal = NWPaintGTKSlider( gdkDrawable, nType, nPart, aCtrlRect, aClip, nState, aValue, rControlHandle, rCaption ); + } + + if( pixmap ) + { + returnVal = NWRenderPixmapToScreen( pixmap, aPixmapRect ) && returnVal; + g_object_unref( pixmap ); + } + + return( returnVal ); +} + +BOOL GtkSalGraphics::drawNativeMixedStateCheck( ControlType nType, + ControlPart nPart, + const Region& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, + const OUString& rCaption ) +{ + // need to emulate something for mixed state + + // do this via pixmap since some themes don't care for regions + bool bOldNeedPixmapPaint = bNeedPixmapPaint; + bNeedPixmapPaint = true; + + Rectangle aCtrlRect = rControlRegion.GetBoundRect(); + BOOL returnVal = FALSE; + SelectFont(); + + // draw upper half in off state + const_cast<ImplControlValue&>(aValue).setTristateVal( BUTTONVALUE_OFF ); + XLIB_Region aRegion = XCreateRegion(); + XRectangle aXRect = { aCtrlRect.Left(), aCtrlRect.Top(), aCtrlRect.GetWidth(), aCtrlRect.GetHeight() }; + const unsigned short nH = aXRect.height/2; + aXRect.height -= nH; + XUnionRectWithRegion( &aXRect, aRegion, aRegion ); + SetClipRegion( pFontGC_, aRegion ); + XDestroyRegion( aRegion ); + + returnVal = drawNativeControl( nType, nPart, rControlRegion, nState, aValue, rControlHandle, rCaption ); + + if( returnVal ) + { + // draw lower half in on state + const_cast<ImplControlValue&>(aValue).setTristateVal( BUTTONVALUE_ON ); + aXRect.y += nH; + aRegion = XCreateRegion(); + XUnionRectWithRegion( &aXRect, aRegion, aRegion ); + SetClipRegion( pFontGC_, aRegion ); + XDestroyRegion( aRegion ); + returnVal = drawNativeControl( nType, nPart, rControlRegion, nState, aValue, rControlHandle, rCaption ); + } + + // clean up + bNeedPixmapPaint = bOldNeedPixmapPaint; + const_cast<ImplControlValue&>(aValue).setTristateVal( BUTTONVALUE_MIXED ); + SetClipRegion( pFontGC_ ); + return returnVal; +} + + +/* + * DrawNativeControlText() + * + * OPTIONAL. Draws the requested text for the control described by nPart/nState. + * Used if text not drawn by DrawNativeControl(). + * + * rControlRegion: The bounding region of the complete control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * rControlHandle: Carries platform dependent data and is maintained by the SalFrame implementation. + * rCaption: A caption or title string (like button text etc) + */ +BOOL GtkSalGraphics::drawNativeControlText( ControlType, + ControlPart, + const Region&, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + return( FALSE ); +} + + +/* + * GetNativeControlRegion() + * + * If the return value is TRUE, rNativeBoundingRegion + * contains the TRUE bounding region covered by the control + * including any adornment, while rNativeContentRegion contains the area + * within the control that can be safely drawn into without drawing over + * the borders of the control. + * + * rControlRegion: The bounding region of the control in VCL frame coordinates. + * aValue: An optional value (tristate/numerical/string) + * rControlHandle: Carries platform dependent data and is maintained by the SalFrame implementation. + * rCaption: A caption or title string (like button text etc) + */ +BOOL GtkSalGraphics::getNativeControlRegion( ControlType nType, + ControlPart nPart, + const Region& rControlRegion, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, + const OUString& rCaption, + Region &rNativeBoundingRegion, + Region &rNativeContentRegion ) +{ + BOOL returnVal = FALSE; + + if ( (nType==CTRL_PUSHBUTTON) && (nPart==PART_ENTIRE_CONTROL) + && (rControlRegion.GetBoundRect().GetWidth() > 16) + && (rControlRegion.GetBoundRect().GetHeight() > 16) ) + { + rNativeBoundingRegion = NWGetButtonArea( m_nScreen, nType, nPart, rControlRegion.GetBoundRect(), + nState, aValue, rControlHandle, rCaption ); + rNativeContentRegion = rControlRegion; + + returnVal = TRUE; + } + if ( (nType==CTRL_COMBOBOX) && ((nPart==PART_BUTTON_DOWN) || (nPart==PART_SUB_EDIT)) ) + { + rNativeBoundingRegion = NWGetComboBoxButtonRect( m_nScreen, nType, nPart, rControlRegion.GetBoundRect(), nState, + aValue, rControlHandle, rCaption ); + rNativeContentRegion = rNativeBoundingRegion; + + returnVal = TRUE; + } + if ( (nType==CTRL_SPINBOX) && ((nPart==PART_BUTTON_UP) || (nPart==PART_BUTTON_DOWN) || (nPart==PART_SUB_EDIT)) ) + { + + rNativeBoundingRegion = NWGetSpinButtonRect( m_nScreen, nType, nPart, rControlRegion.GetBoundRect(), nState, + aValue, rControlHandle, rCaption ); + rNativeContentRegion = rNativeBoundingRegion; + + returnVal = TRUE; + } + if ( (nType==CTRL_LISTBOX) && ((nPart==PART_BUTTON_DOWN) || (nPart==PART_SUB_EDIT)) ) + { + rNativeBoundingRegion = NWGetListBoxButtonRect( m_nScreen, nType, nPart, rControlRegion.GetBoundRect(), nState, + aValue, rControlHandle, rCaption ); + rNativeContentRegion = rNativeBoundingRegion; + + returnVal = TRUE; + } + if ( (nType==CTRL_TOOLBAR) && + ((nPart==PART_DRAW_BACKGROUND_HORZ) || + (nPart==PART_DRAW_BACKGROUND_VERT) || + (nPart==PART_THUMB_HORZ) || + (nPart==PART_THUMB_VERT) || + (nPart==PART_BUTTON) + )) + { + rNativeBoundingRegion = NWGetToolbarRect( m_nScreen, nType, nPart, rControlRegion.GetBoundRect(), nState, aValue, rControlHandle, rCaption ); + rNativeContentRegion = rNativeBoundingRegion; + returnVal = TRUE; + } + if ( (nType==CTRL_SCROLLBAR) && ((nPart==PART_BUTTON_LEFT) || (nPart==PART_BUTTON_RIGHT) || + (nPart==PART_BUTTON_UP) || (nPart==PART_BUTTON_DOWN) ) ) + { + rNativeBoundingRegion = NWGetScrollButtonRect( m_nScreen, nPart, rControlRegion.GetBoundRect() ); + rNativeContentRegion = rNativeBoundingRegion; + + returnVal = TRUE; + } + if( (nType == CTRL_MENUBAR) && (nPart == PART_ENTIRE_CONTROL) ) + { + NWEnsureGTKMenubar( m_nScreen ); + GtkRequisition aReq; + gtk_widget_size_request( gWidgetData[m_nScreen].gMenubarWidget, &aReq ); + Rectangle aMenuBarRect = rControlRegion.GetBoundRect(); + aMenuBarRect = Rectangle( aMenuBarRect.TopLeft(), + Size( aMenuBarRect.GetWidth(), aReq.height+1 ) ); + rNativeBoundingRegion = Region( aMenuBarRect ); + rNativeContentRegion = rNativeBoundingRegion; + returnVal = TRUE; + } + if( (nType == CTRL_MENU_POPUP) ) + { + if( (nPart == PART_MENU_ITEM_CHECK_MARK) || + (nPart == PART_MENU_ITEM_RADIO_MARK) ) + { + NWEnsureGTKMenu( m_nScreen ); + + gint indicator_size = 0; + GtkWidget* pWidget = (nPart == PART_MENU_ITEM_CHECK_MARK) ? + gWidgetData[m_nScreen].gMenuItemCheckMenuWidget : gWidgetData[m_nScreen].gMenuItemRadioMenuWidget; + gtk_widget_style_get( pWidget, + "indicator_size", &indicator_size, + (char *)NULL ); + rNativeBoundingRegion = rControlRegion; + Rectangle aIndicatorRect( Point( 0, + (rControlRegion.GetBoundRect().GetHeight()-indicator_size)/2), + Size( indicator_size, indicator_size ) ); + rNativeContentRegion = Region( aIndicatorRect ); + returnVal = TRUE; + } + } + if( (nType == CTRL_RADIOBUTTON || nType == CTRL_CHECKBOX) ) + { + NWEnsureGTKRadio( m_nScreen ); + NWEnsureGTKCheck( m_nScreen ); + GtkWidget* widget = (nType == CTRL_RADIOBUTTON) ? gWidgetData[m_nScreen].gRadioWidget : gWidgetData[m_nScreen].gCheckWidget; + gint indicator_size, indicator_spacing; + gtk_widget_style_get( widget, + "indicator_size", &indicator_size, + "indicator_spacing", &indicator_spacing, + (char *)NULL); + indicator_size += 2*indicator_spacing; // guess overpaint of theme + rNativeBoundingRegion = rControlRegion; + Rectangle aIndicatorRect( Point( 0, + (rControlRegion.GetBoundRect().GetHeight()-indicator_size)/2), + Size( indicator_size, indicator_size ) ); + rNativeContentRegion = Region( aIndicatorRect ); + returnVal = TRUE; + } + if( (nType == CTRL_EDITBOX || nType == CTRL_SPINBOX) && nPart == PART_ENTIRE_CONTROL ) + { + NWEnsureGTKEditBox( m_nScreen ); + GtkWidget* widget = gWidgetData[m_nScreen].gEditBoxWidget; + GtkRequisition aReq; + gtk_widget_size_request( widget, &aReq ); + Rectangle aEditRect = rControlRegion.GetBoundRect(); + aEditRect = Rectangle( aEditRect.TopLeft(), + Size( aEditRect.GetWidth(), aReq.height+1 ) ); + rNativeBoundingRegion = Region( aEditRect ); + rNativeContentRegion = rNativeBoundingRegion; + returnVal = TRUE; + } + if( (nType == CTRL_SLIDER) && (nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT) ) + { + NWEnsureGTKSlider( m_nScreen ); + GtkWidget* widget = (nPart == PART_THUMB_HORZ) ? gWidgetData[m_nScreen].gHScale : gWidgetData[m_nScreen].gVScale; + gint slider_length = 10; + gint slider_width = 10; + gtk_widget_style_get( widget, + "slider-width", &slider_width, + "slider-length", &slider_length, + (char *)NULL); + Rectangle aRect( rControlRegion.GetBoundRect() ); + if( nPart == PART_THUMB_HORZ ) + { + aRect.Right() = aRect.Left() + slider_length - 1; + aRect.Bottom() = aRect.Top() + slider_width - 1; + } + else + { + aRect.Bottom() = aRect.Top() + slider_length - 1; + aRect.Right() = aRect.Left() + slider_width - 1; + } + rNativeBoundingRegion = rNativeContentRegion = Region( aRect ); + returnVal = TRUE; + } + + return( returnVal ); +} + + +/************************************************************************ + * Individual control drawing functions + ************************************************************************/ +BOOL GtkSalGraphics::NWPaintGTKButton( + GdkDrawable* gdkDrawable, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, const ImplControlValue&, + SalControlHandle&, const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + gboolean interiorFocus; + gint focusWidth; + gint focusPad; + BOOL bDrawFocus = TRUE; + gint x, y, w, h; + GtkBorder aDefBorder; + GtkBorder* pBorder; + GdkRectangle clipRect; + + NWEnsureGTKButton( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + // Grab some button style attributes + gtk_widget_style_get( gWidgetData[m_nScreen].gBtnWidget, "focus-line-width", &focusWidth, + "focus-padding", &focusPad, + "interior_focus", &interiorFocus, + "default_border", &pBorder, + (char *)NULL ); + + // Make sure the border values exist, otherwise use some defaults + if ( pBorder ) + { + NW_gtk_border_set_from_border( aDefBorder, pBorder ); + gtk_border_free( pBorder ); + } + else NW_gtk_border_set_from_border( aDefBorder, &aDefDefBorder ); + + // If the button is too small, don't ever draw focus or grab more space + if ( (w < 16) || (h < 16) ) + bDrawFocus = FALSE; + + NWSetWidgetState( gWidgetData[m_nScreen].gBtnWidget, nState, stateType ); + + gint xi = x, yi = y, wi = w, hi = h; + if ( (nState & CTRL_STATE_DEFAULT) && bDrawFocus ) + { + xi += aDefBorder.left; + yi += aDefBorder.top; + wi -= aDefBorder.left + aDefBorder.right; + hi -= aDefBorder.top + aDefBorder.bottom; + } + + if ( !interiorFocus && bDrawFocus ) + { + xi += focusWidth + focusPad; + yi += focusWidth + focusPad; + wi -= 2 * (focusWidth + focusPad); + hi -= 2 * (focusWidth + focusPad); + } + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + // Buttons must paint opaque since some themes have alpha-channel enabled buttons + gtk_paint_flat_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, + &clipRect, m_pWindow, "base", x, y, w, h ); + + if ( (nState & CTRL_STATE_DEFAULT) && (GTK_BUTTON(gWidgetData[m_nScreen].gBtnWidget)->relief == GTK_RELIEF_NORMAL) ) + { + gtk_paint_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, + &clipRect, gWidgetData[m_nScreen].gBtnWidget, "buttondefault", x, y, w, h ); + } + + if ( (GTK_BUTTON(gWidgetData[m_nScreen].gBtnWidget)->relief != GTK_RELIEF_NONE) + || (nState & CTRL_STATE_PRESSED) + || (nState & CTRL_STATE_ROLLOVER) ) + { + gtk_paint_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, stateType, shadowType, + &clipRect, gWidgetData[m_nScreen].gBtnWidget, "button", xi, yi, wi, hi ); + } + } +#if 0 // VCL draws focus rects + // Draw focus rect + if ( (nState & CTRL_STATE_FOCUSED) && (nState & CTRL_STATE_ENABLED) && bDrawFocus ) + { + if (interiorFocus) + { + x += gWidgetData[m_nScreen].gBtnWidget->style->xthickness + focusPad; + y += gWidgetData[m_nScreen].gBtnWidget->style->ythickness + focusPad; + w -= 2 * (gWidgetData[m_nScreen].gBtnWidget->style->xthickness + focusPad); + h -= 2 * (gWidgetData[m_nScreen].gBtnWidget->style->xthickness + focusPad); + } + else + { + x -= focusWidth + focusPad; + y -= focusWidth + focusPad; + w += 2 * (focusWidth + focusPad); + h += 2 * (focusWidth + focusPad); + } + if ( !interiorFocus ) + gtk_paint_focus( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, stateType, &clipRect, + gWidgetData[m_nScreen].gBtnWidget, "button", x, y, w, h ); + } +#endif + + return( TRUE ); +} + +static Rectangle NWGetButtonArea( int nScreen, + ControlType, ControlPart, Rectangle aAreaRect, ControlState nState, + const ImplControlValue&, SalControlHandle&, const OUString& ) +{ + gboolean interiorFocus; + gint focusWidth; + gint focusPad; + GtkBorder aDefBorder; + GtkBorder * pBorder; + BOOL bDrawFocus = TRUE; + Rectangle aRect; + gint x, y, w, h; + + NWEnsureGTKButton( nScreen ); + gtk_widget_style_get( gWidgetData[nScreen].gBtnWidget, + "focus-line-width", &focusWidth, + "focus-padding", &focusPad, + "interior_focus", &interiorFocus, + "default_border", &pBorder, + (char *)NULL ); + + // Make sure the border values exist, otherwise use some defaults + if ( pBorder ) + { + NW_gtk_border_set_from_border( aDefBorder, pBorder ); + gtk_border_free( pBorder ); + } + else NW_gtk_border_set_from_border( aDefBorder, &aDefDefBorder ); + + x = aAreaRect.Left(); + y = aAreaRect.Top(); + w = aAreaRect.GetWidth(); + h = aAreaRect.GetHeight(); + + // If the button is too small, don't ever draw focus or grab more space + if ( (w < 16) || (h < 16) ) + bDrawFocus = FALSE; + + if ( (nState & CTRL_STATE_DEFAULT) && bDrawFocus ) + { + x -= aDefBorder.left; + y -= aDefBorder.top; + w += aDefBorder.left + aDefBorder.right; + h += aDefBorder.top + aDefBorder.bottom; + } + + aRect = Rectangle( Point( x, y ), Size( w, h ) ); + + return( aRect ); +} + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKRadio( GdkDrawable* gdkDrawable, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, + const ImplControlValue& aValue, SalControlHandle&, + const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + BOOL isChecked = (aValue.getTristateVal()==BUTTONVALUE_ON); + gint x, y; + GdkRectangle clipRect; + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKRadio( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + gint indicator_size; + gtk_widget_style_get( gWidgetData[m_nScreen].gRadioWidget, "indicator_size", &indicator_size, (char *)NULL); + + x = rControlRectangle.Left() + (rControlRectangle.GetWidth()-indicator_size)/2; + y = rControlRectangle.Top() + (rControlRectangle.GetHeight()-indicator_size)/2; + + // Set the shadow based on if checked or not so we get a freakin checkmark. + shadowType = isChecked ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + NWSetWidgetState( gWidgetData[m_nScreen].gRadioWidget, nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gRadioWidgetSibling, nState, stateType ); + + // GTK enforces radio groups, so that if we don't have 2 buttons in the group, + // the single button will always be active. So we have to have 2 buttons. + + // #i59666# set the members directly where we should use + // gtk_toggle_button_set_active. reason: there are animated themes + // which are in active state only after a while leading to painting + // intermediate states between active/inactive. Let's hope that + // GtkToggleButtone stays binary compatible. + if (!isChecked) + GTK_TOGGLE_BUTTON(gWidgetData[m_nScreen].gRadioWidgetSibling)->active = TRUE; + GTK_TOGGLE_BUTTON(gWidgetData[m_nScreen].gRadioWidget)->active = isChecked; + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + gtk_paint_option( gWidgetData[m_nScreen].gRadioWidget->style, gdkDrawable, stateType, shadowType, + &clipRect, gWidgetData[m_nScreen].gRadioWidget, "radiobutton", + x, y, indicator_size, indicator_size ); + } + + return( TRUE ); +} + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKCheck( GdkDrawable* gdkDrawable, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle&, const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + BOOL isChecked = (aValue.getTristateVal()==BUTTONVALUE_ON) ? TRUE : FALSE; + GdkRectangle clipRect; + gint x,y; + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKCheck( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + gint indicator_size; + gtk_widget_style_get( gWidgetData[m_nScreen].gCheckWidget, "indicator_size", &indicator_size, (char *)NULL); + + x = rControlRectangle.Left() + (rControlRectangle.GetWidth()-indicator_size)/2; + y = rControlRectangle.Top() + (rControlRectangle.GetHeight()-indicator_size)/2; + + // Set the shadow based on if checked or not so we get a checkmark. + shadowType = isChecked ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + NWSetWidgetState( gWidgetData[m_nScreen].gCheckWidget, nState, stateType ); + GTK_TOGGLE_BUTTON(gWidgetData[m_nScreen].gCheckWidget)->active = isChecked; + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + gtk_paint_check( gWidgetData[m_nScreen].gCheckWidget->style, gdkDrawable, stateType, shadowType, + &clipRect, gWidgetData[m_nScreen].gCheckWidget, "checkbutton", + x, y, indicator_size, indicator_size ); + } + + return( TRUE ); +} + +//------------------------------------- +static void NWCalcArrowRect( const Rectangle& rButton, Rectangle& rArrow ) +{ + // Size the arrow appropriately + Size aSize( rButton.GetWidth()/2, rButton.GetHeight()/2 ); + rArrow.SetSize( aSize ); + + rArrow.SetPos( Point( + rButton.Left() + ( rButton.GetWidth() - rArrow.GetWidth() ) / 2, + rButton.Top() + ( rButton.GetHeight() - rArrow.GetHeight() ) / 2 + ) ); +} + +BOOL GtkSalGraphics::NWPaintGTKScrollbar( ControlType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState nState, + const ImplControlValue& aValue, SalControlHandle&, + const OUString& ) +{ + ScrollbarValue* pScrollbarVal = (ScrollbarValue *)(aValue.getOptionalVal()); + GdkPixmap* pixmap = NULL; + Rectangle pixmapRect, scrollbarRect; + GtkStateType stateType; + GtkShadowType shadowType; + GtkScrollbar * scrollbarWidget; + GtkStyle * style; + GtkAdjustment* scrollbarValues = NULL; + GtkOrientation scrollbarOrientation; + Rectangle thumbRect = pScrollbarVal->maThumbRect; + Rectangle button11BoundRect = pScrollbarVal->maButton1Rect; // backward + Rectangle button22BoundRect = pScrollbarVal->maButton2Rect; // forward + Rectangle button12BoundRect = pScrollbarVal->maButton1Rect; // secondary forward + Rectangle button21BoundRect = pScrollbarVal->maButton2Rect; // secondary backward + GtkArrowType button1Type; // backward + GtkArrowType button2Type; // forward + gchar * scrollbarTagH = (gchar *) "hscrollbar"; + gchar * scrollbarTagV = (gchar *) "vscrollbar"; + gchar * scrollbarTag = NULL; + Rectangle arrowRect; + gint slider_width = 0; + gint stepper_size = 0; + gint stepper_spacing = 0; + gint trough_border = 0; + gint min_slider_length = 0; + gint vShim = 0; + gint hShim = 0; + gint x,y,w,h; + + // make controlvalue rectangles relative to area + thumbRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); + button11BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); + button22BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); + button12BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); + button21BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() ); + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKScrollbars( m_nScreen ); + NWEnsureGTKArrow( m_nScreen ); + + // Find the overall bounding rect of the control + pixmapRect = rControlRectangle; + pixmapRect.SetSize( Size( pixmapRect.GetWidth() + 1, + pixmapRect.GetHeight() + 1 ) ); + scrollbarRect = pixmapRect; + + if ( (scrollbarRect.GetWidth() <= 1) || (scrollbarRect.GetHeight() <= 1) ) + return( TRUE ); + + // Grab some button style attributes + gtk_widget_style_get( gWidgetData[m_nScreen].gScrollHorizWidget, + "slider_width", &slider_width, + "stepper_size", &stepper_size, + "trough_border", &trough_border, + "stepper_spacing", &stepper_spacing, + "min_slider_length", &min_slider_length, (char *)NULL ); + gboolean has_forward; + gboolean has_forward2; + gboolean has_backward; + gboolean has_backward2; + + gtk_widget_style_get( gWidgetData[m_nScreen].gScrollHorizWidget, "has-forward-stepper", &has_forward, + "has-secondary-forward-stepper", &has_forward2, + "has-backward-stepper", &has_backward, + "has-secondary-backward-stepper", &has_backward2, (char *)NULL ); + gint magic = trough_border ? 1 : 0; + gint nFirst = 0; + + if ( has_backward ) nFirst += 1; + if ( has_forward2 ) nFirst += 1; + + if ( nPart == PART_DRAW_BACKGROUND_HORZ ) + { + unsigned int sliderHeight = slider_width + (trough_border * 2); + vShim = (pixmapRect.GetHeight() - sliderHeight) / 2; + + scrollbarRect.Move( 0, vShim ); + scrollbarRect.SetSize( Size( scrollbarRect.GetWidth(), sliderHeight ) ); + + scrollbarWidget = GTK_SCROLLBAR( gWidgetData[m_nScreen].gScrollHorizWidget ); + scrollbarOrientation = GTK_ORIENTATION_HORIZONTAL; + scrollbarTag = scrollbarTagH; + button1Type = GTK_ARROW_LEFT; + button2Type = GTK_ARROW_RIGHT; + + if ( has_backward ) + { + button12BoundRect.Move( stepper_size - trough_border, + (scrollbarRect.GetHeight() - slider_width) / 2 ); + } + + button11BoundRect.Move( trough_border, (scrollbarRect.GetHeight() - slider_width) / 2 ); + button11BoundRect.SetSize( Size( stepper_size, slider_width ) ); + button12BoundRect.SetSize( Size( stepper_size, slider_width ) ); + + if ( has_backward2 ) + { + button22BoundRect.Move( stepper_size+(trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); + button21BoundRect.Move( (trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); + } + else + { + button22BoundRect.Move( (trough_border+1)/2, (scrollbarRect.GetHeight() - slider_width) / 2 ); + } + + button21BoundRect.SetSize( Size( stepper_size, slider_width ) ); + button22BoundRect.SetSize( Size( stepper_size, slider_width ) ); + + thumbRect.Bottom() = thumbRect.Top() + slider_width - 1; + // Make sure the thumb is at least the default width (so we don't get tiny thumbs), + // but if the VCL gives us a size smaller than the theme's default thumb size, + // honor the VCL size +#if 0 + if ( (thumbRect.GetWidth() < min_slider_length) + && ((scrollbarRect.GetWidth()-button1BoundRect.GetWidth()-button2BoundRect.GetWidth()) > min_slider_length) ) + thumbRect.SetSize( Size( min_slider_length, thumbRect.GetHeight() ) ); +#endif + + thumbRect.Right() += magic; + // Center vertically in the track + thumbRect.Move( 0, (scrollbarRect.GetHeight() - slider_width) / 2 ); + } + else + { + unsigned int sliderWidth = slider_width + (trough_border * 2); + hShim = (pixmapRect.GetWidth() - sliderWidth) / 2; + + scrollbarRect.Move( hShim, 0 ); + scrollbarRect.SetSize( Size( sliderWidth, scrollbarRect.GetHeight() ) ); + + scrollbarWidget = GTK_SCROLLBAR( gWidgetData[m_nScreen].gScrollVertWidget ); + scrollbarOrientation = GTK_ORIENTATION_VERTICAL; + scrollbarTag = scrollbarTagV; + button1Type = GTK_ARROW_UP; + button2Type = GTK_ARROW_DOWN; + + if ( has_backward ) + { + button12BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, + stepper_size + trough_border ); + } + button11BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, trough_border ); + button11BoundRect.SetSize( Size( slider_width, stepper_size ) ); + button12BoundRect.SetSize( Size( slider_width, stepper_size ) ); + + if ( has_backward2 ) + { + button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, stepper_size+(trough_border+1)/2 ); + button21BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, (trough_border+1)/2 ); + } + else + { + button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, (trough_border+1)/2 ); + } + + button21BoundRect.SetSize( Size( slider_width, stepper_size ) ); + button22BoundRect.SetSize( Size( slider_width, stepper_size ) ); + + thumbRect.Right() = thumbRect.Left() + slider_width - 1; +#if 0 + // Make sure the thumb is at least the default width (so we don't get tiny thumbs), + // but if the VCL gives us a size smaller than the theme's default thumb size, + // honor the VCL size + if ( (thumbRect.GetHeight() < min_slider_length) + && ((scrollbarRect.GetHeight()-button1BoundRect.GetHeight()-button2BoundRect.GetHeight()) > min_slider_length) ) + thumbRect.SetSize( Size( thumbRect.GetWidth(), min_slider_length ) ); +#endif + + thumbRect.Bottom() += magic; + // Center horizontally in the track + thumbRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, 0 ); + } + + BOOL has_slider = ( thumbRect.GetWidth() > 0 && thumbRect.GetHeight() > 0 ); + + scrollbarValues = gtk_range_get_adjustment( GTK_RANGE(scrollbarWidget) ); + if ( scrollbarValues == NULL ) + scrollbarValues = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); + if ( nPart == PART_DRAW_BACKGROUND_HORZ ) + { + scrollbarValues->lower = pScrollbarVal->mnMin; + scrollbarValues->upper = pScrollbarVal->mnMax; + scrollbarValues->value = pScrollbarVal->mnCur; + scrollbarValues->page_size = scrollbarRect.GetWidth() / 2; + } + else + { + scrollbarValues->lower = pScrollbarVal->mnMin; + scrollbarValues->upper = pScrollbarVal->mnMax; + scrollbarValues->value = pScrollbarVal->mnCur; + scrollbarValues->page_size = scrollbarRect.GetHeight() / 2; + } + gtk_adjustment_changed( scrollbarValues ); + + // as multiple paints are required for the scrollbar + // painting them directly to the window flickers + pixmap = NWGetPixmapFromScreen( pixmapRect ); + if( ! pixmap ) + return FALSE; + x = y = 0; + + w = pixmapRect.GetWidth(); + h = pixmapRect.GetHeight(); + + GdkDrawable* const &gdkDrawable = GDK_DRAWABLE( pixmap ); + GdkRectangle* gdkRect = NULL; + + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + NWSetWidgetState( GTK_WIDGET(scrollbarWidget), nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gBtnWidget, nState, stateType ); + style = GTK_WIDGET( scrollbarWidget )->style; + + // ----------------- TROUGH + gtk_paint_flat_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, + GTK_STATE_NORMAL, GTK_SHADOW_NONE, gdkRect, + m_pWindow, "base", x, y, + w, h ); + gtk_paint_box( style, gdkDrawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, + gdkRect, GTK_WIDGET(scrollbarWidget), "trough", + x, y, + scrollbarRect.GetWidth(), scrollbarRect.GetHeight() ); + + if ( nState & CTRL_STATE_FOCUSED ) + { + gtk_paint_focus( style, gdkDrawable, GTK_STATE_ACTIVE, + gdkRect, GTK_WIDGET(scrollbarWidget), "trough", + x, y, + scrollbarRect.GetWidth(), scrollbarRect.GetHeight() ); + } + + // ----------------- THUMB + if ( has_slider ) + { + NWConvertVCLStateToGTKState( pScrollbarVal->mnThumbState, &stateType, &shadowType ); + if ( pScrollbarVal->mnThumbState & CTRL_STATE_PRESSED ) stateType = GTK_STATE_PRELIGHT; + gtk_paint_slider( style, gdkDrawable, stateType, GTK_SHADOW_OUT, + gdkRect, GTK_WIDGET(scrollbarWidget), "slider", + x+hShim+thumbRect.Left(), y+vShim+thumbRect.Top(), + thumbRect.GetWidth(), thumbRect.GetHeight(), scrollbarOrientation ); + } + // ----------------- BUTTON 1 // + if ( has_backward ) + { + NWConvertVCLStateToGTKState( pScrollbarVal->mnButton1State, &stateType, &shadowType ); + if ( stateType == GTK_STATE_INSENSITIVE ) stateType = GTK_STATE_NORMAL; + gtk_paint_box( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), "stepper", + x+hShim+button11BoundRect.Left(), y+vShim+button11BoundRect.Top(), + button11BoundRect.GetWidth(), button11BoundRect.GetHeight() ); + // ----------------- ARROW 1 + NWCalcArrowRect( button11BoundRect, arrowRect ); + gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button1Type, TRUE, + x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), + arrowRect.GetWidth(), arrowRect.GetHeight() ); + } + if ( has_forward2 ) + { + NWConvertVCLStateToGTKState( pScrollbarVal->mnButton2State, &stateType, &shadowType ); + if ( stateType == GTK_STATE_INSENSITIVE ) stateType = GTK_STATE_NORMAL; + gtk_paint_box( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), "stepper", + x+hShim+button12BoundRect.Left(), y+vShim+button12BoundRect.Top(), + button12BoundRect.GetWidth(), button12BoundRect.GetHeight() ); + // ----------------- ARROW 1 + NWCalcArrowRect( button12BoundRect, arrowRect ); + gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button2Type, TRUE, + x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), + arrowRect.GetWidth(), arrowRect.GetHeight() ); + } + // ----------------- BUTTON 2 + if ( has_backward2 ) + { + NWConvertVCLStateToGTKState( pScrollbarVal->mnButton1State, &stateType, &shadowType ); + if ( stateType == GTK_STATE_INSENSITIVE ) stateType = GTK_STATE_NORMAL; + gtk_paint_box( style, gdkDrawable, stateType, shadowType, gdkRect, + GTK_WIDGET(scrollbarWidget), "stepper", + x+hShim+button21BoundRect.Left(), y+vShim+button21BoundRect.Top(), + button21BoundRect.GetWidth(), button21BoundRect.GetHeight() ); + // ----------------- ARROW 2 + NWCalcArrowRect( button21BoundRect, arrowRect ); + gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button1Type, TRUE, + x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), + arrowRect.GetWidth(), arrowRect.GetHeight() ); + } + if ( has_forward ) + { + NWConvertVCLStateToGTKState( pScrollbarVal->mnButton2State, &stateType, &shadowType ); + if ( stateType == GTK_STATE_INSENSITIVE ) stateType = GTK_STATE_NORMAL; + gtk_paint_box( style, gdkDrawable, stateType, shadowType, gdkRect, + GTK_WIDGET(scrollbarWidget), "stepper", + x+hShim+button22BoundRect.Left(), y+vShim+button22BoundRect.Top(), + button22BoundRect.GetWidth(), button22BoundRect.GetHeight() ); + // ----------------- ARROW 2 + NWCalcArrowRect( button22BoundRect, arrowRect ); + gtk_paint_arrow( style, gdkDrawable, stateType, shadowType, + gdkRect, GTK_WIDGET(scrollbarWidget), scrollbarTag, button2Type, TRUE, + x+hShim+arrowRect.Left(), y+vShim+arrowRect.Top(), + arrowRect.GetWidth(), arrowRect.GetHeight() ); + } + + if( !NWRenderPixmapToScreen(pixmap, pixmapRect) ) + { + g_object_unref( pixmap ); + return( FALSE ); + } + g_object_unref( pixmap ); + + return( TRUE ); +} + +//--- + +static Rectangle NWGetScrollButtonRect( int nScreen, ControlPart nPart, Rectangle aAreaRect ) +{ + gint slider_width; + gint stepper_size; + gint stepper_spacing; + gint trough_border; + + NWEnsureGTKScrollbars( nScreen ); + + // Grab some button style attributes + gtk_widget_style_get( gWidgetData[nScreen].gScrollHorizWidget, + "slider-width", &slider_width, + "stepper-size", &stepper_size, + "trough-border", &trough_border, + "stepper-spacing", &stepper_spacing, (char *)NULL ); + + gboolean has_forward; + gboolean has_forward2; + gboolean has_backward; + gboolean has_backward2; + + gtk_widget_style_get( gWidgetData[nScreen].gScrollHorizWidget, + "has-forward-stepper", &has_forward, + "has-secondary-forward-stepper", &has_forward2, + "has-backward-stepper", &has_backward, + "has-secondary-backward-stepper", &has_backward2, (char *)NULL ); + gint buttonWidth; + gint buttonHeight; + Rectangle buttonRect; + + gint nFirst = 0; + gint nSecond = 0; + + if ( has_forward ) nSecond += 1; + if ( has_forward2 ) nFirst += 1; + if ( has_backward ) nFirst += 1; + if ( has_backward2 ) nSecond += 1; + + if ( ( nPart == PART_BUTTON_UP ) || ( nPart == PART_BUTTON_DOWN ) ) + { + buttonWidth = slider_width + 2 * trough_border; + buttonHeight = stepper_size + trough_border + stepper_spacing; + } + else + { + buttonWidth = stepper_size + trough_border + stepper_spacing; + buttonHeight = slider_width + 2 * trough_border; + } + + if ( nPart == PART_BUTTON_UP ) + { + buttonHeight *= nFirst; + buttonHeight -= 1; + buttonRect.setX( aAreaRect.Left() ); + buttonRect.setY( aAreaRect.Top() ); + } + else if ( nPart == PART_BUTTON_LEFT ) + { + buttonWidth *= nFirst; + buttonWidth -= 1; + buttonRect.setX( aAreaRect.Left() ); + buttonRect.setY( aAreaRect.Top() ); + } + else if ( nPart == PART_BUTTON_DOWN ) + { + buttonHeight *= nSecond; + buttonRect.setX( aAreaRect.Left() ); + buttonRect.setY( aAreaRect.Top() + aAreaRect.GetHeight() - buttonHeight ); + } + else if ( nPart == PART_BUTTON_RIGHT ) + { + buttonWidth *= nSecond; + buttonRect.setX( aAreaRect.Left() + aAreaRect.GetWidth() - buttonWidth ); + buttonRect.setY( aAreaRect.Top() ); + } + + buttonRect.SetSize( Size( buttonWidth, buttonHeight ) ); + + return( buttonRect ); +} + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKEditBox( GdkDrawable* gdkDrawable, + ControlType nType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, + const ImplControlValue& aValue, SalControlHandle& rControlHandle, + const OUString& rCaption ) +{ + Rectangle pixmapRect; + GdkRectangle clipRect; + + // Find the overall bounding rect of the buttons's drawing area, + // plus its actual draw rect excluding adornment + pixmapRect = NWGetEditBoxPixmapRect( m_nScreen, nType, nPart, rControlRectangle, + nState, aValue, rControlHandle, rCaption ); + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + NWPaintOneEditBox( m_nScreen, gdkDrawable, &clipRect, nType, nPart, pixmapRect, nState, aValue, rControlHandle, rCaption ); + } + + return( TRUE ); +} + + +/* Take interior/exterior focus into account and return + * the bounding rectangle of the edit box including + * any focus requirements. + */ +static Rectangle NWGetEditBoxPixmapRect(int nScreen, + ControlType, + ControlPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + Rectangle pixmapRect = aAreaRect; + gboolean interiorFocus; + gint focusWidth; + + NWEnsureGTKEditBox( nScreen ); + + // Grab some entry style attributes + gtk_widget_style_get( gWidgetData[nScreen].gEditBoxWidget, + "focus-line-width", &focusWidth, + "interior-focus", &interiorFocus, (char *)NULL ); + + if ( !interiorFocus ) + { + pixmapRect.Move( -(focusWidth), -(focusWidth) ); + pixmapRect.SetSize( Size( pixmapRect.GetWidth() + (2*(focusWidth)), + pixmapRect.GetHeight() + (2*(focusWidth)) ) ); + } + + return( pixmapRect ); +} + + +/* Paint a GTK Entry widget into the specified GdkPixmap. + * All coordinates should be local to the Pixmap, NOT + * screen/window coordinates. + */ +static void NWPaintOneEditBox( int nScreen, + GdkDrawable * gdkDrawable, + GdkRectangle * gdkRect, + ControlType nType, + ControlPart, + Rectangle aEditBoxRect, + ControlState nState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + GtkWidget *widget; + + NWEnsureGTKButton( nScreen ); + NWEnsureGTKEditBox( nScreen ); + NWEnsureGTKSpinButton( nScreen ); + NWEnsureGTKCombo( nScreen ); + NWEnsureGTKScrolledWindow( nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + /* border's shadowType for gtk entries is always GTK_SHADOW_IN (see gtkentry.c) + shadowType = GTK_SHADOW_IN; + */ + + switch ( nType ) + { + case CTRL_SPINBOX: + widget = gWidgetData[nScreen].gSpinButtonWidget; + break; + + case CTRL_MULTILINE_EDITBOX: + widget = gWidgetData[nScreen].gScrolledWindowWidget; + break; + case CTRL_COMBOBOX: + widget = GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry; + break; + + default: + widget = gWidgetData[nScreen].gEditBoxWidget; + break; + } + + if ( stateType == GTK_STATE_PRELIGHT ) + stateType = GTK_STATE_NORMAL; + + // Blueprint needs to paint entry_bg with a Button widget, not an Entry widget to get + // a nice white (or whatever default color) background + GtkWidget* pBGWidget = widget; + if( GtkSalGraphics::bNeedButtonStyleAsEditBackgroundWorkaround ) + { + NWSetWidgetState( gWidgetData[nScreen].gBtnWidget, nState, stateType ); + pBGWidget = gWidgetData[nScreen].gBtnWidget; + } + NWSetWidgetState( widget, nState, stateType ); + + gtk_paint_flat_box( pBGWidget->style, gdkDrawable, stateType, GTK_SHADOW_NONE, + gdkRect, pBGWidget, "entry_bg", + aEditBoxRect.Left(), aEditBoxRect.Top(), + aEditBoxRect.GetWidth(), aEditBoxRect.GetHeight() ); + gtk_paint_shadow( widget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, + gdkRect, widget, "entry", + aEditBoxRect.Left(), aEditBoxRect.Top(), + aEditBoxRect.GetWidth(), aEditBoxRect.GetHeight() ); + +} + + + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKSpinBox( ControlType nType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, const OUString& rCaption ) +{ + GdkPixmap * pixmap; + Rectangle pixmapRect; + GtkStateType stateType; + GtkShadowType shadowType; + SpinbuttonValue * pSpinVal = (SpinbuttonValue *)(aValue.getOptionalVal()); + Rectangle upBtnRect; + ControlPart upBtnPart = PART_BUTTON_UP; + ControlState upBtnState = CTRL_STATE_ENABLED; + Rectangle downBtnRect; + ControlPart downBtnPart = PART_BUTTON_DOWN; + ControlState downBtnState = CTRL_STATE_ENABLED; + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKSpinButton( m_nScreen ); + NWEnsureGTKArrow( m_nScreen ); + + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + if ( pSpinVal ) + { + upBtnPart = pSpinVal->mnUpperPart; + upBtnState = pSpinVal->mnUpperState; + + downBtnPart = pSpinVal->mnLowerPart; + downBtnState = pSpinVal->mnLowerState; + } + + // CTRL_SPINBUTTONS pass their area in pSpinVal, not in rControlRectangle + if ( nType == CTRL_SPINBUTTONS ) + { + if ( !pSpinVal ) + { + std::fprintf( stderr, "Tried to draw CTRL_SPINBUTTONS, but the SpinButtons data structure didn't exist!\n" ); + return( false ); + } + pixmapRect = pSpinVal->maUpperRect; + pixmapRect.Union( pSpinVal->maLowerRect ); + } + else + pixmapRect = rControlRectangle; + + + pixmap = NWGetPixmapFromScreen( pixmapRect ); + if ( !pixmap ) + return( FALSE ); + + upBtnRect = NWGetSpinButtonRect( m_nScreen, nType, upBtnPart, pixmapRect, upBtnState, aValue, rControlHandle, rCaption ); + downBtnRect = NWGetSpinButtonRect( m_nScreen, nType, downBtnPart, pixmapRect, downBtnState, aValue, rControlHandle, rCaption ); + + if ( (nType==CTRL_SPINBOX) && (nPart!=PART_ALL_BUTTONS) ) + { + // Draw an edit field for SpinBoxes and ComboBoxes + Rectangle aEditBoxRect( pixmapRect ); + aEditBoxRect.SetSize( Size( upBtnRect.Left() - pixmapRect.Left(), aEditBoxRect.GetHeight() ) ); + aEditBoxRect.setX( 0 ); + aEditBoxRect.setY( 0 ); + + NWPaintOneEditBox( m_nScreen, pixmap, NULL, nType, nPart, aEditBoxRect, nState, aValue, rControlHandle, rCaption ); + } + + NWSetWidgetState( gWidgetData[m_nScreen].gSpinButtonWidget, nState, stateType ); + gtk_widget_style_get( gWidgetData[m_nScreen].gSpinButtonWidget, "shadow_type", &shadowType, (char *)NULL ); + + if ( shadowType != GTK_SHADOW_NONE ) + { + Rectangle shadowRect( upBtnRect ); + + shadowRect.Union( downBtnRect ); + gtk_paint_box( gWidgetData[m_nScreen].gSpinButtonWidget->style, pixmap, GTK_STATE_NORMAL, shadowType, NULL, + gWidgetData[m_nScreen].gSpinButtonWidget, "spinbutton", + (shadowRect.Left() - pixmapRect.Left()), (shadowRect.Top() - pixmapRect.Top()), + shadowRect.GetWidth(), shadowRect.GetHeight() ); + } + + NWPaintOneSpinButton( m_nScreen, pixmap, nType, upBtnPart, pixmapRect, upBtnState, aValue, rControlHandle, rCaption ); + NWPaintOneSpinButton( m_nScreen, pixmap, nType, downBtnPart, pixmapRect, downBtnState, aValue, rControlHandle, rCaption ); + + if( !NWRenderPixmapToScreen(pixmap, pixmapRect) ) + { + g_object_unref( pixmap ); + return( FALSE ); + } + + g_object_unref( pixmap ); + return( TRUE ); +} + +//--- + +static Rectangle NWGetSpinButtonRect( int nScreen, + ControlType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + gint buttonSize; + Rectangle buttonRect; + + NWEnsureGTKSpinButton( nScreen ); + + buttonSize = MAX( PANGO_PIXELS( pango_font_description_get_size(GTK_WIDGET(gWidgetData[nScreen].gSpinButtonWidget)->style->font_desc) ), + MIN_SPIN_ARROW_WIDTH ); + buttonSize -= buttonSize % 2 - 1; /* force odd */ + buttonRect.SetSize( Size( buttonSize + 2 * gWidgetData[nScreen].gSpinButtonWidget->style->xthickness, + buttonRect.GetHeight() ) ); + buttonRect.setX( aAreaRect.Left() + (aAreaRect.GetWidth() - buttonRect.GetWidth()) ); + if ( nPart == PART_BUTTON_UP ) + { + buttonRect.setY( aAreaRect.Top() ); + buttonRect.Bottom() = buttonRect.Top() + (aAreaRect.GetHeight() / 2); + } + else if( nPart == PART_BUTTON_DOWN ) + { + buttonRect.setY( aAreaRect.Top() + (aAreaRect.GetHeight() / 2) ); + buttonRect.Bottom() = aAreaRect.Bottom(); // cover area completely + } + else + { + buttonRect.Right() = buttonRect.Left()-1; + buttonRect.Left() = aAreaRect.Left(); + buttonRect.Top() = aAreaRect.Top(); + buttonRect.Bottom() = aAreaRect.Bottom(); + } + + return( buttonRect ); +} + +//--- + +static void NWPaintOneSpinButton( int nScreen, + GdkPixmap* pixmap, + ControlType nType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, + const OUString& rCaption ) +{ + Rectangle buttonRect; + GtkStateType stateType; + GtkShadowType shadowType; + Rectangle arrowRect; + gint arrowSize; + + NWEnsureGTKSpinButton( nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + buttonRect = NWGetSpinButtonRect( nScreen, nType, nPart, aAreaRect, nState, aValue, rControlHandle, rCaption ); + + NWSetWidgetState( gWidgetData[nScreen].gSpinButtonWidget, nState, stateType ); + gtk_paint_box( gWidgetData[nScreen].gSpinButtonWidget->style, pixmap, stateType, shadowType, NULL, gWidgetData[nScreen].gSpinButtonWidget, + (nPart == PART_BUTTON_UP) ? "spinbutton_up" : "spinbutton_down", + (buttonRect.Left() - aAreaRect.Left()), (buttonRect.Top() - aAreaRect.Top()), + buttonRect.GetWidth(), buttonRect.GetHeight() ); + + arrowSize = (buttonRect.GetWidth() - (2 * gWidgetData[nScreen].gSpinButtonWidget->style->xthickness)) - 4; + arrowSize -= arrowSize % 2 - 1; /* force odd */ + arrowRect.SetSize( Size( arrowSize, arrowSize ) ); + arrowRect.setX( buttonRect.Left() + (buttonRect.GetWidth() - arrowRect.GetWidth()) / 2 ); + if ( nPart == PART_BUTTON_UP ) + arrowRect.setY( buttonRect.Top() + (buttonRect.GetHeight() - arrowRect.GetHeight()) / 2 + 1); + else + arrowRect.setY( buttonRect.Top() + (buttonRect.GetHeight() - arrowRect.GetHeight()) / 2 - 1); + + gtk_paint_arrow( gWidgetData[nScreen].gSpinButtonWidget->style, pixmap, stateType, GTK_SHADOW_OUT, NULL, gWidgetData[nScreen].gSpinButtonWidget, + "spinbutton", (nPart == PART_BUTTON_UP) ? GTK_ARROW_UP : GTK_ARROW_DOWN, TRUE, + (arrowRect.Left() - aAreaRect.Left()), (arrowRect.Top() - aAreaRect.Top()), + arrowRect.GetWidth(), arrowRect.GetHeight() ); +} + + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKComboBox( GdkDrawable* gdkDrawable, + ControlType nType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, const OUString& rCaption ) +{ + Rectangle pixmapRect; + Rectangle buttonRect; + GtkStateType stateType; + GtkShadowType shadowType; + Rectangle arrowRect; + gint x,y; + GdkRectangle clipRect; + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKArrow( m_nScreen ); + NWEnsureGTKCombo( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + // Find the overall bounding rect of the buttons's drawing area, + // plus its actual draw rect excluding adornment + pixmapRect = rControlRectangle; + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + + NWSetWidgetState( gWidgetData[m_nScreen].gBtnWidget, nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gComboWidget, nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gArrowWidget, nState, stateType ); + + buttonRect = NWGetComboBoxButtonRect( m_nScreen, nType, PART_BUTTON_DOWN, pixmapRect, nState, aValue, rControlHandle, rCaption ); + if( nPart == PART_BUTTON_DOWN ) + buttonRect.Left() += 1; + + Rectangle aEditBoxRect( pixmapRect ); + aEditBoxRect.SetSize( Size( pixmapRect.GetWidth() - buttonRect.GetWidth(), aEditBoxRect.GetHeight() ) ); + + #define ARROW_EXTENT 0.7 + arrowRect.SetSize( Size( (gint)(MIN_ARROW_SIZE * ARROW_EXTENT), + (gint)(MIN_ARROW_SIZE * ARROW_EXTENT) ) ); + arrowRect.SetPos( Point( buttonRect.Left() + (gint)((buttonRect.GetWidth() - arrowRect.GetWidth()) / 2), + buttonRect.Top() + (gint)((buttonRect.GetHeight() - arrowRect.GetHeight()) / 2) ) ); + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + if( nPart == PART_ENTIRE_CONTROL ) + NWPaintOneEditBox( m_nScreen, gdkDrawable, &clipRect, nType, nPart, aEditBoxRect, + nState, aValue, rControlHandle, rCaption ); + + // Buttons must paint opaque since some themes have alpha-channel enabled buttons + gtk_paint_flat_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, + &clipRect, m_pWindow, "base", + x+(buttonRect.Left() - pixmapRect.Left()), + y+(buttonRect.Top() - pixmapRect.Top()), + buttonRect.GetWidth(), buttonRect.GetHeight() ); + gtk_paint_box( GTK_COMBO(gWidgetData[m_nScreen].gComboWidget)->button->style, gdkDrawable, stateType, shadowType, + &clipRect, GTK_COMBO(gWidgetData[m_nScreen].gComboWidget)->button, "button", + x+(buttonRect.Left() - pixmapRect.Left()), + y+(buttonRect.Top() - pixmapRect.Top()), + buttonRect.GetWidth(), buttonRect.GetHeight() ); + + gtk_paint_arrow( gWidgetData[m_nScreen].gArrowWidget->style, gdkDrawable, stateType, shadowType, + &clipRect, gWidgetData[m_nScreen].gArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, + x+(arrowRect.Left() - pixmapRect.Left()), y+(arrowRect.Top() - pixmapRect.Top()), + arrowRect.GetWidth(), arrowRect.GetHeight() ); + } + + return( TRUE ); +} + +//---- + +static Rectangle NWGetComboBoxButtonRect( int nScreen, + ControlType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + Rectangle aButtonRect; + gint nArrowWidth; + gint nButtonWidth; + gint nFocusWidth; + gint nFocusPad; + + NWEnsureGTKArrow( nScreen ); + + // Grab some button style attributes + gtk_widget_style_get( gWidgetData[nScreen].gDropdownWidget, + "focus-line-width", &nFocusWidth, + "focus-padding", &nFocusPad, (char *)NULL ); + + nArrowWidth = MIN_ARROW_SIZE + (GTK_MISC(gWidgetData[nScreen].gArrowWidget)->xpad * 2); + nButtonWidth = nArrowWidth + + ((BTN_CHILD_SPACING + gWidgetData[nScreen].gDropdownWidget->style->xthickness) * 2) + + (2 * (nFocusWidth+nFocusPad)); + if( nPart == PART_BUTTON_DOWN ) + { + aButtonRect.SetSize( Size( nButtonWidth, aAreaRect.GetHeight() ) ); + aButtonRect.SetPos( Point( aAreaRect.Left() + aAreaRect.GetWidth() - nButtonWidth, + aAreaRect.Top() ) ); + } + else if( nPart == PART_SUB_EDIT ) + { + NWEnsureGTKCombo( nScreen ); + + gint adjust_x = GTK_CONTAINER(gWidgetData[nScreen].gComboWidget)->border_width + + nFocusWidth + + nFocusPad; + gint adjust_y = adjust_x + gWidgetData[nScreen].gComboWidget->style->ythickness; + adjust_x += gWidgetData[nScreen].gComboWidget->style->xthickness; + aButtonRect.SetSize( Size( aAreaRect.GetWidth() - nButtonWidth - 2 * adjust_x, + aAreaRect.GetHeight() - 2 * adjust_y ) ); + Point aEditPos = aAreaRect.TopLeft(); + aEditPos.X() += adjust_x; + aEditPos.Y() += adjust_y; + aButtonRect.SetPos( aEditPos ); + } + + return( aButtonRect ); +} + +//------------------------------------- + + + +BOOL GtkSalGraphics::NWPaintGTKTabItem( ControlType nType, ControlPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle&, const OUString& ) +{ + GdkPixmap * pixmap; + Rectangle pixmapRect; + Rectangle tabRect; + TabitemValue * pTabitemValue = (TabitemValue *)(aValue.getOptionalVal()); + GtkStateType stateType; + GtkShadowType shadowType; + if( ! gWidgetData[ m_nScreen ].gCacheTabItems ) + { + gWidgetData[ m_nScreen ].gCacheTabItems = new NWPixmapCache( m_nScreen ); + gWidgetData[ m_nScreen ].gCacheTabPages = new NWPixmapCache( m_nScreen ); + } + NWPixmapCache& aCacheItems = *gWidgetData[ m_nScreen ].gCacheTabItems; + NWPixmapCache& aCachePage = *gWidgetData[ m_nScreen ].gCacheTabPages; + + if( !aCacheItems.GetSize() ) + aCacheItems.SetSize( 20 ); + if( !aCachePage.GetSize() ) + aCachePage.SetSize( 1 ); + + if ( !pTabitemValue && (nType==CTRL_TAB_ITEM) ) + { + std::fprintf( stderr, "NWPaintGTKTabItem() received a NULL TabitemValue. Cannot draw native tab\n" ); + return( false ); + } + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKNotebook( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + // Find the overall bounding rect of the buttons's drawing area, + // plus its actual draw rect excluding adornment + pixmapRect = rControlRectangle; + if ( nType == CTRL_TAB_ITEM ) + { + if ( !pTabitemValue->isFirst() ) + { + // GTK+ tabs overlap on the right edge (the top tab obscures the + // left edge of the tab right "below" it, so adjust the rectangle + // to draw tabs slightly large so the overlap happens + pixmapRect.Move( -2, 0 ); + pixmapRect.SetSize( Size( pixmapRect.GetWidth() + 2, pixmapRect.GetHeight() ) ); + } + if ( nState & CTRL_STATE_SELECTED ) + { + // In GTK+, the selected tab is 2px taller than all other tabs + pixmapRect.Move( 0, -2 ); + pixmapRect.Bottom() += 2; + tabRect = pixmapRect; + // Only draw over 1 pixel of the tab pane that this tab is drawn on top of. + tabRect.Bottom() -= 1; + } + else + tabRect = pixmapRect; + + // Allow the tab to draw a right border if needed + tabRect.Right() -= 1; + + // #129732# avoid degenerate cases which might lead to crashes + if( tabRect.GetWidth() <= 1 || tabRect.GetHeight() <= 1 ) + return false; + } + + if( nType == CTRL_TAB_ITEM ) + { + if( aCacheItems.Find( nType, nState, pixmapRect, &pixmap ) ) + return NWRenderPixmapToScreen( pixmap, pixmapRect ); + } + else + { + if( aCachePage.Find( nType, nState, pixmapRect, &pixmap ) ) + return NWRenderPixmapToScreen( pixmap, pixmapRect ); + } + + +// gtk_widget_set_state( gWidgetData[m_nScreen].gNotebookWidget, stateType ); + + pixmap = gdk_pixmap_new( NULL, pixmapRect.GetWidth(), pixmapRect.GetHeight(), + GetX11SalData()->GetDisplay()->GetVisual( m_nScreen ).GetDepth() ); + GdkRectangle paintRect; + paintRect.x = paintRect.y = 0; + paintRect.width = pixmapRect.GetWidth(); + paintRect.height = pixmapRect.GetHeight(); + + gtk_paint_flat_box( m_pWindow->style, pixmap, GTK_STATE_NORMAL, + GTK_SHADOW_NONE, &paintRect, m_pWindow, "base", 0, 0, -1, -1); + + NWSetWidgetState( gWidgetData[m_nScreen].gNotebookWidget, nState, stateType ); + + switch( nType ) + { + case CTRL_TAB_BODY: + break; + + case CTRL_FIXEDBORDER: + case CTRL_TAB_PANE: + gtk_paint_box_gap( gWidgetData[m_nScreen].gNotebookWidget->style, pixmap, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, gWidgetData[m_nScreen].gNotebookWidget, + (char *)"notebook", 0, 0, pixmapRect.GetWidth(), pixmapRect.GetHeight(), GTK_POS_TOP, 0, 0 ); + break; + + case CTRL_TAB_ITEM: + stateType = ( nState & CTRL_STATE_SELECTED ) ? GTK_STATE_NORMAL : GTK_STATE_ACTIVE; + + gtk_paint_extension( gWidgetData[m_nScreen].gNotebookWidget->style, pixmap, stateType, GTK_SHADOW_OUT, NULL, gWidgetData[m_nScreen].gNotebookWidget, + (char *)"tab", (tabRect.Left() - pixmapRect.Left()), (tabRect.Top() - pixmapRect.Top()), + tabRect.GetWidth(), tabRect.GetHeight(), GTK_POS_BOTTOM ); + + if ( nState & CTRL_STATE_SELECTED ) + { + gtk_paint_flat_box( gWidgetData[m_nScreen].gNotebookWidget->style, pixmap, stateType, GTK_SHADOW_NONE, NULL, m_pWindow, + (char *)"base", 0, (pixmapRect.GetHeight() - 1), pixmapRect.GetWidth(), 1 ); + } + break; + + default: + break; + } + + // Crux seems to think it can make the pane without a left edge + if ( nType == CTRL_FIXEDBORDER ) + pixmapRect.Move( 1, 0 ); + + // cache data + if( nType == CTRL_TAB_ITEM ) + aCacheItems.Fill( nType, nState, pixmapRect, pixmap ); + else + aCachePage.Fill( nType, nState, pixmapRect, pixmap ); + + BOOL bSuccess = NWRenderPixmapToScreen(pixmap, pixmapRect); + g_object_unref( pixmap ); + return bSuccess; +} + +//------------------------------------- + +BOOL GtkSalGraphics::NWPaintGTKListBox( GdkDrawable* gdkDrawable, + ControlType nType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, + const ImplControlValue& aValue, + SalControlHandle& rControlHandle, const OUString& rCaption ) +{ + Rectangle pixmapRect; + Rectangle widgetRect; + Rectangle aIndicatorRect; + GtkStateType stateType; + GtkShadowType shadowType; + gint bInteriorFocus; + gint nFocusLineWidth; + gint nFocusPadding; + gint x,y; + GdkRectangle clipRect; + + NWEnsureGTKButton( m_nScreen ); + NWEnsureGTKOptionMenu( m_nScreen ); + NWEnsureGTKScrolledWindow( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + // Find the overall bounding rect of the buttons's drawing area, + // plus its actual draw rect excluding adornment + pixmapRect = rControlRectangle; + if ( nPart == PART_WINDOW ) + { + // Make the widget a _bit_ bigger + pixmapRect.SetPos( Point( pixmapRect.Left() - 1, + pixmapRect.Top() - 1 ) ); + pixmapRect.SetSize( Size( pixmapRect.GetWidth() + 2, + pixmapRect.GetHeight() + 2 ) ); + } + + widgetRect = pixmapRect; + x = pixmapRect.Left(); + y = pixmapRect.Top(); + + // set up references to correct drawable and cliprect + NWSetWidgetState( gWidgetData[m_nScreen].gBtnWidget, nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gOptionMenuWidget, nState, stateType ); + NWSetWidgetState( gWidgetData[m_nScreen].gScrolledWindowWidget, nState, stateType ); + + if ( nPart != PART_WINDOW ) + { + gtk_widget_style_get( gWidgetData[m_nScreen].gOptionMenuWidget, + "interior_focus", &bInteriorFocus, + "focus_line_width", &nFocusLineWidth, + "focus_padding", &nFocusPadding, + (char *)NULL); + } + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + if ( nPart != PART_WINDOW ) + { + // Listboxes must paint opaque since some themes have alpha-channel enabled bodies + gtk_paint_flat_box( gWidgetData[m_nScreen].gBtnWidget->style, gdkDrawable, GTK_STATE_NORMAL, GTK_SHADOW_NONE, + &clipRect, m_pWindow, "base", x, y, + pixmapRect.GetWidth(), pixmapRect.GetHeight() ); + gtk_paint_box( gWidgetData[m_nScreen].gOptionMenuWidget->style, gdkDrawable, stateType, shadowType, &clipRect, + gWidgetData[m_nScreen].gOptionMenuWidget, "optionmenu", + x+(widgetRect.Left() - pixmapRect.Left()), + y+(widgetRect.Top() - pixmapRect.Top()), + widgetRect.GetWidth(), widgetRect.GetHeight() ); + aIndicatorRect = NWGetListBoxIndicatorRect( m_nScreen, nType, nPart, widgetRect, nState, + aValue, rControlHandle, rCaption ); + gtk_paint_tab( gWidgetData[m_nScreen].gOptionMenuWidget->style, gdkDrawable, stateType, shadowType, &clipRect, + gWidgetData[m_nScreen].gOptionMenuWidget, "optionmenutab", + x+(aIndicatorRect.Left() - pixmapRect.Left()), + y+(aIndicatorRect.Top() - pixmapRect.Top()), + aIndicatorRect.GetWidth(), aIndicatorRect.GetHeight() ); + } + else + { + shadowType = GTK_SHADOW_IN; + + gtk_paint_shadow( gWidgetData[m_nScreen].gScrolledWindowWidget->style, gdkDrawable, GTK_STATE_NORMAL, shadowType, + &clipRect, gWidgetData[m_nScreen].gScrolledWindowWidget, "scrolled_window", + x+(widgetRect.Left() - pixmapRect.Left()), y+(widgetRect.Top() - pixmapRect.Top()), + widgetRect.GetWidth(), widgetRect.GetHeight() ); + } + } + + return( TRUE ); +} + +BOOL GtkSalGraphics::NWPaintGTKToolbar( + GdkDrawable* gdkDrawable, + ControlType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, const ImplControlValue& aValue, + SalControlHandle&, const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + gint x, y, w, h; + gint g_x=0, g_y=0, g_w=10, g_h=10; + bool bPaintButton = true; + GtkWidget* pButtonWidget = gWidgetData[m_nScreen].gToolbarButtonWidget; + const gchar* pButtonDetail = "button"; + GdkRectangle clipRect; + + NWEnsureGTKToolbar( m_nScreen ); + if( nPart == PART_BUTTON ) // toolbar buttons cannot focus in gtk + nState &= ~CTRL_STATE_FOCUSED; + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + // handle toolbar + if( nPart == PART_DRAW_BACKGROUND_HORZ || nPart == PART_DRAW_BACKGROUND_VERT ) + { + NWSetWidgetState( gWidgetData[m_nScreen].gToolbarWidget, nState, stateType ); + + GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nScreen].gToolbarWidget, GTK_SENSITIVE ); + if ( nState & CTRL_STATE_ENABLED ) + GTK_WIDGET_SET_FLAGS( gWidgetData[m_nScreen].gToolbarWidget, GTK_SENSITIVE ); + + if( nPart == PART_DRAW_BACKGROUND_HORZ ) + gtk_toolbar_set_orientation( GTK_TOOLBAR(gWidgetData[m_nScreen].gToolbarWidget), GTK_ORIENTATION_HORIZONTAL ); + else + gtk_toolbar_set_orientation( GTK_TOOLBAR(gWidgetData[m_nScreen].gToolbarWidget), GTK_ORIENTATION_VERTICAL ); + } + // handle grip + else if( nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT ) + { + NWSetWidgetState( gWidgetData[m_nScreen].gHandleBoxWidget, nState, stateType ); + + GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nScreen].gHandleBoxWidget, GTK_SENSITIVE ); + if ( nState & CTRL_STATE_ENABLED ) + GTK_WIDGET_SET_FLAGS( gWidgetData[m_nScreen].gHandleBoxWidget, GTK_SENSITIVE ); + + gtk_handle_box_set_shadow_type( GTK_HANDLE_BOX(gWidgetData[m_nScreen].gHandleBoxWidget), shadowType ); + + // evaluate grip rect + ToolbarValue* pVal = (ToolbarValue*)aValue.getOptionalVal(); + if( pVal ) + { + g_x = pVal->maGripRect.Left(); + g_y = pVal->maGripRect.Top(); + g_w = pVal->maGripRect.GetWidth(); + g_h = pVal->maGripRect.GetHeight(); + } + } + // handle button + else if( nPart == PART_BUTTON ) + { + bPaintButton = + (GTK_BUTTON(pButtonWidget)->relief != GTK_RELIEF_NONE) + || (nState & CTRL_STATE_PRESSED) + || (nState & CTRL_STATE_ROLLOVER); + if( aValue.getTristateVal() == BUTTONVALUE_ON ) + { + pButtonWidget = gWidgetData[m_nScreen].gToolbarToggleWidget; + shadowType = GTK_SHADOW_IN; + // special case stateType value for depressed toggle buttons + // cf. gtk+/gtk/gtktogglebutton.c (gtk_toggle_button_update_state) + if( ! (nState & (CTRL_STATE_PRESSED|CTRL_STATE_ROLLOVER)) ) + stateType = GTK_STATE_ACTIVE; + pButtonDetail = "togglebutton"; + bPaintButton = true; + } + + NWSetWidgetState( pButtonWidget, nState, stateType ); + gtk_widget_ensure_style( pButtonWidget ); + } + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + // draw toolbar + if( nPart == PART_DRAW_BACKGROUND_HORZ || nPart == PART_DRAW_BACKGROUND_VERT ) + { + gtk_paint_flat_box( gWidgetData[m_nScreen].gToolbarWidget->style, + gdkDrawable, + (GtkStateType)GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &clipRect, + gWidgetData[m_nScreen].gToolbarWidget, + "base", + x, y, w, h ); + gtk_paint_box( gWidgetData[m_nScreen].gToolbarWidget->style, + gdkDrawable, + stateType, + shadowType, + &clipRect, + gWidgetData[m_nScreen].gToolbarWidget, + "toolbar", + x, y, w, h ); + } + // draw grip + else if( nPart == PART_THUMB_HORZ || nPart == PART_THUMB_VERT ) + { + gtk_paint_handle( gWidgetData[m_nScreen].gHandleBoxWidget->style, + gdkDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + &clipRect, + gWidgetData[m_nScreen].gHandleBoxWidget, + "handlebox", + g_x, g_y, g_w, g_h, + nPart == PART_THUMB_HORZ ? + GTK_ORIENTATION_HORIZONTAL : + GTK_ORIENTATION_VERTICAL + ); + } + // draw button + else if( nPart == PART_BUTTON ) + { + if( bPaintButton ) + { + gtk_paint_box( pButtonWidget->style, gdkDrawable, + stateType, + shadowType, + &clipRect, + pButtonWidget, pButtonDetail, x, y, w, h ); + } + } + } + + return( TRUE ); +} + +//---- + +BOOL GtkSalGraphics::NWPaintGTKMenubar( + GdkDrawable* gdkDrawable, + ControlType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, const ImplControlValue&, + SalControlHandle&, const OUString& ) +{ + GtkStateType stateType; + GtkShadowType shadowType; + GtkShadowType selected_shadow_type = GTK_SHADOW_OUT; + gint x, y, w, h; + GdkRectangle clipRect; + + NWEnsureGTKMenubar( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + if( nPart == PART_MENU_ITEM ) + { + if( nState & (CTRL_STATE_SELECTED|CTRL_STATE_ROLLOVER) ) + { + gtk_widget_style_get( gWidgetData[m_nScreen].gMenuItemMenubarWidget, + "selected_shadow_type", &selected_shadow_type, + (char *)NULL); + } + } + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + // handle Menubar + if( nPart == PART_ENTIRE_CONTROL ) + { + NWSetWidgetState( gWidgetData[m_nScreen].gMenubarWidget, nState, stateType ); + + GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nScreen].gMenubarWidget, GTK_SENSITIVE ); + if ( nState & CTRL_STATE_ENABLED ) + GTK_WIDGET_SET_FLAGS( gWidgetData[m_nScreen].gMenubarWidget, GTK_SENSITIVE ); + + // #118704# for translucent menubar styles paint background first + gtk_paint_flat_box( gWidgetData[m_nScreen].gMenubarWidget->style, + gdkDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &clipRect, + GTK_WIDGET(m_pWindow), + "base", + x, y, w, h ); + gtk_paint_box( gWidgetData[m_nScreen].gMenubarWidget->style, + gdkDrawable, + stateType, + shadowType, + &clipRect, + gWidgetData[m_nScreen].gMenubarWidget, + "menubar", + x, y, w, h ); + } + else if( nPart == PART_MENU_ITEM ) + { + if( nState & (CTRL_STATE_SELECTED|CTRL_STATE_ROLLOVER) ) + { + gtk_paint_box( gWidgetData[m_nScreen].gMenuItemMenubarWidget->style, + gdkDrawable, + GTK_STATE_PRELIGHT, + selected_shadow_type, + &clipRect, + gWidgetData[m_nScreen].gMenuItemMenubarWidget, + "menuitem", + x, y, w, h); + } + } + } + + return( TRUE ); +} + +BOOL GtkSalGraphics::NWPaintGTKPopupMenu( + GdkDrawable* gdkDrawable, + ControlType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState nState, const ImplControlValue&, + SalControlHandle&, const OUString& ) +{ + // #i50745# gtk does not draw disabled menu entries (and crux theme + // even crashes), draw them using vcl functionality. + if( nPart == PART_MENU_ITEM && ! (nState & CTRL_STATE_ENABLED) ) + return FALSE; + + GtkStateType stateType; + GtkShadowType shadowType; + GtkShadowType selected_shadow_type = GTK_SHADOW_OUT; + gint x, y, w, h; + GdkRectangle clipRect; + + NWEnsureGTKMenu( m_nScreen ); + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + if( nPart == PART_MENU_ITEM && + ( nState & (CTRL_STATE_SELECTED|CTRL_STATE_ROLLOVER) ) ) + { + gtk_widget_style_get( gWidgetData[m_nScreen].gMenuItemMenuWidget, + "selected_shadow_type", &selected_shadow_type, + (char *)NULL); + } + + NWSetWidgetState( gWidgetData[m_nScreen].gMenuWidget, nState, stateType ); + + GTK_WIDGET_UNSET_FLAGS( gWidgetData[m_nScreen].gMenuWidget, GTK_SENSITIVE ); + if ( nState & CTRL_STATE_ENABLED ) + GTK_WIDGET_SET_FLAGS( gWidgetData[m_nScreen].gMenuWidget, GTK_SENSITIVE ); + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + if( nPart == PART_ENTIRE_CONTROL ) + { + // #118704# for translucent menubar styles paint background first + gtk_paint_flat_box( gWidgetData[m_nScreen].gMenuWidget->style, + gdkDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + &clipRect, + GTK_WIDGET(m_pWindow), + "base", + x, y, w, h ); + gtk_paint_box( gWidgetData[m_nScreen].gMenuWidget->style, + gdkDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + &clipRect, + gWidgetData[m_nScreen].gMenuWidget, + "menu", + x, y, w, h ); + } + else if( nPart == PART_MENU_ITEM ) + { + if( nState & (CTRL_STATE_SELECTED|CTRL_STATE_ROLLOVER) ) + { + if( nState & CTRL_STATE_ENABLED ) + gtk_paint_box( gWidgetData[m_nScreen].gMenuItemMenuWidget->style, + gdkDrawable, + GTK_STATE_PRELIGHT, + selected_shadow_type, + &clipRect, + gWidgetData[m_nScreen].gMenuItemMenuWidget, + "menuitem", + x, y, w, h); + } + } + else if( nPart == PART_MENU_ITEM_CHECK_MARK || nPart == PART_MENU_ITEM_RADIO_MARK ) + { + GtkWidget* pWidget = (nPart == PART_MENU_ITEM_CHECK_MARK) ? + gWidgetData[m_nScreen].gMenuItemCheckMenuWidget : + gWidgetData[m_nScreen].gMenuItemRadioMenuWidget; + + GtkStateType nStateType = GTK_STATE_NORMAL; + GtkShadowType nShadowType; + + if ( nState & CTRL_STATE_SELECTED ) + nStateType = GTK_STATE_PRELIGHT; + + NWSetWidgetState( pWidget, nState, nStateType ); + + if ( nState & CTRL_STATE_PRESSED ) + nShadowType = GTK_SHADOW_IN; + else + nShadowType = GTK_SHADOW_OUT; + + if ( nPart == PART_MENU_ITEM_CHECK_MARK ) + { + gtk_paint_check( pWidget->style, + gdkDrawable, + nStateType, + nShadowType, + &clipRect, + gWidgetData[m_nScreen].gMenuItemMenuWidget, + "check", + x, y, w, h ); + } + else + { + gtk_paint_option( pWidget->style, + gdkDrawable, + nStateType, + nShadowType, + &clipRect, + gWidgetData[m_nScreen].gMenuItemMenuWidget, + "option", + x, y, w, h ); + } + } + } + + return( TRUE ); +} + +BOOL GtkSalGraphics::NWPaintGTKTooltip( + GdkDrawable* gdkDrawable, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList& rClipList, + ControlState, const ImplControlValue&, + SalControlHandle&, const OUString& ) +{ + NWEnsureGTKTooltip( m_nScreen ); + + gint x, y, w, h; + GdkRectangle clipRect; + + x = rControlRectangle.Left(); + y = rControlRectangle.Top(); + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + for( clipList::const_iterator it = rClipList.begin(); it != rClipList.end(); ++it ) + { + clipRect.x = it->Left(); + clipRect.y = it->Top(); + clipRect.width = it->GetWidth(); + clipRect.height = it->GetHeight(); + + gtk_paint_flat_box( gWidgetData[m_nScreen].gTooltipPopup->style, + gdkDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + &clipRect, + gWidgetData[m_nScreen].gTooltipPopup, + "tooltip", + x, y, w, h ); + } + + return( TRUE ); +} + +BOOL GtkSalGraphics::NWPaintGTKListNode( + GdkDrawable*, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState nState, const ImplControlValue& rValue, + SalControlHandle&, const OUString& ) +{ + NWEnsureGTKTreeView( m_nScreen ); + + Rectangle aRect( rControlRectangle ); + aRect.Left() -= 2; + aRect.Right() += 2; + aRect.Top() -= 2; + aRect.Bottom() += 2; + gint w, h; + w = aRect.GetWidth(); + h = aRect.GetHeight(); + + GtkStateType stateType; + GtkShadowType shadowType; + NWConvertVCLStateToGTKState( nState, &stateType, &shadowType ); + + ButtonValue aButtonValue = rValue.getTristateVal(); + GtkExpanderStyle eStyle = GTK_EXPANDER_EXPANDED; + + switch( aButtonValue ) + { + case BUTTONVALUE_ON: eStyle = GTK_EXPANDER_EXPANDED;break; + case BUTTONVALUE_OFF: eStyle = GTK_EXPANDER_COLLAPSED; break; + default: + break; + } + + GdkPixmap* pixmap = NWGetPixmapFromScreen( aRect ); + if( ! pixmap ) + return FALSE; + + GdkDrawable* const &pixDrawable = GDK_DRAWABLE( pixmap ); + gtk_paint_expander( gWidgetData[m_nScreen].gTreeView->style, + pixDrawable, + stateType, + NULL, + gWidgetData[m_nScreen].gTreeView, + "treeview", + w/2, h/2, + eStyle ); + + BOOL bRet = NWRenderPixmapToScreen( pixmap, aRect ); + g_object_unref( pixmap ); + + return bRet; +} + +BOOL GtkSalGraphics::NWPaintGTKProgress( + GdkDrawable*, + ControlType, ControlPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState, const ImplControlValue& rValue, + SalControlHandle&, const OUString& ) +{ + NWEnsureGTKProgressBar( m_nScreen ); + + gint w, h; + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + long nProgressWidth = rValue.getNumericVal(); + + GdkPixmap* pixmap = NWGetPixmapFromScreen( Rectangle( Point( 0, 0 ), Size( w, h ) ) ); + if( ! pixmap ) + return FALSE; + + GdkDrawable* const &pixDrawable = GDK_DRAWABLE( pixmap ); + + // paint background + gtk_paint_flat_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "trough", + 0, 0, w, h ); + if( nProgressWidth > 0 ) + { + // paint progress + if( Application::GetSettings().GetLayoutRTL() ) + { + gtk_paint_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "bar", + w-nProgressWidth, 0, nProgressWidth, h + ); + } + else + { + gtk_paint_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "bar", + 0, 0, nProgressWidth, h + ); + } + } + + BOOL bRet = NWRenderPixmapToScreen( pixmap, rControlRectangle ); + g_object_unref( pixmap ); + + return bRet; +} + +BOOL GtkSalGraphics::NWPaintGTKSlider( + GdkDrawable*, + ControlType, ControlPart nPart, + const Rectangle& rControlRectangle, + const clipList&, + ControlState nState, const ImplControlValue& rValue, + SalControlHandle&, const OUString& ) +{ + NWEnsureGTKSlider( m_nScreen ); + + gint w, h; + w = rControlRectangle.GetWidth(); + h = rControlRectangle.GetHeight(); + + SliderValue* pVal = (SliderValue*)rValue.getOptionalVal(); + + GdkPixmap* pixmap = NWGetPixmapFromScreen( rControlRectangle ); + if( ! pixmap ) + return FALSE; + + (void)pVal; + + GdkDrawable* const &pixDrawable = GDK_DRAWABLE( pixmap ); + GtkWidget* pWidget = (nPart == PART_TRACK_HORZ_AREA) + ? GTK_WIDGET(gWidgetData[m_nScreen].gHScale) + : GTK_WIDGET(gWidgetData[m_nScreen].gVScale); + const gchar* pDetail = (nPart == PART_TRACK_HORZ_AREA) ? "hscale" : "vscale"; + GtkOrientation eOri = (nPart == PART_TRACK_HORZ_AREA) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL; + GtkStateType eState = (nState & CTRL_STATE_ENABLED) ? GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE; + gint slider_width = 10; + gint slider_length = 10; + gint trough_border = 0; + gtk_widget_style_get( pWidget, + "slider-width", &slider_width, + "slider-length", &slider_length, + "trough-border", &trough_border, + NULL); + + eState = (nState & CTRL_STATE_ENABLED) ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE; + if( nPart == PART_TRACK_HORZ_AREA ) + { + gtk_paint_box( pWidget->style, + pixDrawable, + eState, + GTK_SHADOW_IN, + NULL, + pWidget, + "trough", + 0, (h-slider_width-2*trough_border)/2, w, slider_width + 2*trough_border); + gint x = (w - slider_length + 1) * (pVal->mnCur - pVal->mnMin) / (pVal->mnMax - pVal->mnMin); + gtk_paint_slider( pWidget->style, + pixDrawable, + eState, + GTK_SHADOW_OUT, + NULL, + pWidget, + pDetail, + x, (h-slider_width)/2, + slider_length, slider_width, + eOri ); + } + else + { + gtk_paint_box( pWidget->style, + pixDrawable, + eState, + GTK_SHADOW_IN, + NULL, + pWidget, + "trough", + (w-slider_width-2*trough_border)/2, 0, slider_width + 2*trough_border, h); + gint y = (h - slider_length + 1) * (pVal->mnCur - pVal->mnMin) / (pVal->mnMax - pVal->mnMin); + gtk_paint_slider( pWidget->style, + pixDrawable, + eState, + GTK_SHADOW_OUT, + NULL, + pWidget, + pDetail, + (w-slider_width)/2, y, + slider_width, slider_length, + eOri ); + } + #if 0 + // paint background + gtk_paint_flat_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_NORMAL, + GTK_SHADOW_NONE, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "trough", + 0, 0, w, h ); + if( nProgressWidth > 0 ) + { + // paint progress + if( Application::GetSettings().GetLayoutRTL() ) + { + gtk_paint_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "bar", + w-nProgressWidth, 0, nProgressWidth, h + ); + } + else + { + gtk_paint_box( gWidgetData[m_nScreen].gProgressBar->style, + pixDrawable, + GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + NULL, + gWidgetData[m_nScreen].gProgressBar, + "bar", + 0, 0, nProgressWidth, h + ); + } + } + #endif + + BOOL bRet = NWRenderPixmapToScreen( pixmap, rControlRectangle ); + g_object_unref( pixmap ); + + return bRet; +} + +//---- + +static Rectangle NWGetListBoxButtonRect( int nScreen, + ControlType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + Rectangle aPartRect; + GtkRequisition *pIndicatorSize = NULL; + GtkBorder *pIndicatorSpacing = NULL; + gint width = 13; // GTK+ default + gint right = 5; // GTK+ default + gint nButtonAreaWidth = 0; + gint xthickness = 0; + + NWEnsureGTKOptionMenu( nScreen ); + + gtk_widget_style_get( gWidgetData[nScreen].gOptionMenuWidget, + "indicator_size", &pIndicatorSize, + "indicator_spacing",&pIndicatorSpacing, (char *)NULL); + + if ( pIndicatorSize ) + width = pIndicatorSize->width; + + if ( pIndicatorSpacing ) + right = pIndicatorSpacing->right; + + Size aPartSize( 0, aAreaRect.GetHeight() ); + Point aPartPos ( 0, aAreaRect.Top() ); + + xthickness = gWidgetData[nScreen].gOptionMenuWidget->style->xthickness; + nButtonAreaWidth = width + right + (xthickness * 2); + switch( nPart ) + { + case PART_BUTTON_DOWN: + aPartSize.Width() = nButtonAreaWidth; + aPartPos.X() = aAreaRect.Left() + aAreaRect.GetWidth() - aPartSize.Width(); + break; + + case PART_SUB_EDIT: + aPartSize.Width() = aAreaRect.GetWidth() - nButtonAreaWidth - xthickness; + aPartPos.X() = aAreaRect.Left() + xthickness; + break; + + default: + aPartSize.Width() = aAreaRect.GetWidth(); + aPartPos.X() = aAreaRect.Left(); + break; + } + aPartRect = Rectangle( aPartPos, aPartSize ); + + if ( pIndicatorSize ) + gtk_requisition_free( pIndicatorSize ); + if ( pIndicatorSpacing ) + gtk_border_free( pIndicatorSpacing ); + + return( aPartRect ); +} + +//---- + +static Rectangle NWGetListBoxIndicatorRect( int nScreen, + ControlType, + ControlPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + Rectangle aIndicatorRect; + GtkRequisition *pIndicatorSize = NULL; + GtkBorder *pIndicatorSpacing = NULL; + gint width = 13; // GTK+ default + gint height = 13; // GTK+ default + gint right = 5; // GTK+ default + + NWEnsureGTKOptionMenu( nScreen ); + + gtk_widget_style_get( gWidgetData[nScreen].gOptionMenuWidget, + "indicator_size", &pIndicatorSize, + "indicator_spacing",&pIndicatorSpacing, (char *)NULL); + + if ( pIndicatorSize ) + { + width = pIndicatorSize->width; + height = pIndicatorSize->height; + } + + if ( pIndicatorSpacing ) + right = pIndicatorSpacing->right; + + aIndicatorRect.SetSize( Size( width, height ) ); + aIndicatorRect.SetPos( Point( aAreaRect.Left() + aAreaRect.GetWidth() - width - right - gWidgetData[nScreen].gOptionMenuWidget->style->xthickness, + aAreaRect.Top() + ((aAreaRect.GetHeight() - height) / 2) ) ); + + // If height is odd, move the indicator down 1 pixel + if ( aIndicatorRect.GetHeight() % 2 ) + aIndicatorRect.Move( 0, 1 ); + + if ( pIndicatorSize ) + gtk_requisition_free( pIndicatorSize ); + if ( pIndicatorSpacing ) + gtk_border_free( pIndicatorSpacing ); + + return( aIndicatorRect ); +} + +static Rectangle NWGetToolbarRect( int nScreen, + ControlType, + ControlPart nPart, + Rectangle aAreaRect, + ControlState, + const ImplControlValue&, + SalControlHandle&, + const OUString& ) +{ + Rectangle aRet; + + if( nPart == PART_DRAW_BACKGROUND_HORZ || + nPart == PART_DRAW_BACKGROUND_VERT ) + aRet = aAreaRect; + else if( nPart == PART_THUMB_HORZ ) + aRet = Rectangle( Point( 0, 0 ), Size( aAreaRect.GetWidth(), 10 ) ); + else if( nPart == PART_THUMB_VERT ) + aRet = Rectangle( Point( 0, 0 ), Size( 10, aAreaRect.GetHeight() ) ); + else if( nPart == PART_BUTTON ) + { + aRet = aAreaRect; + + NWEnsureGTKToolbar( nScreen ); + + gint nMinWidth = + 2*gWidgetData[nScreen].gToolbarButtonWidget->style->xthickness + + 1 // CHILD_SPACING constant, found in gtk_button.c + + 3*gWidgetData[nScreen].gToolbarButtonWidget->style->xthickness; // Murphy factor + gint nMinHeight = + 2*gWidgetData[nScreen].gToolbarButtonWidget->style->ythickness + + 1 // CHILD_SPACING constant, found in gtk_button.c + + 3*gWidgetData[nScreen].gToolbarButtonWidget->style->ythickness; // Murphy factor + + gtk_widget_ensure_style( gWidgetData[nScreen].gToolbarButtonWidget ); + if( aAreaRect.GetWidth() < nMinWidth ) + aRet.Right() = aRet.Left() + nMinWidth; + if( aAreaRect.GetHeight() < nMinHeight ) + aRet.Bottom() = aRet.Top() + nMinHeight; + } + + return aRet; +} + +/************************************************************************ + * helper for GtkSalFrame + ************************************************************************/ +static inline Color getColor( const GdkColor& rCol ) +{ + return Color( rCol.red >> 8, rCol.green >> 8, rCol.blue >> 8 ); +} + +#if OSL_DEBUG_LEVEL > 1 + +void printColor( const char* name, const GdkColor& rCol ) +{ + std::fprintf( stderr, " %s = 0x%2x 0x%2x 0x%2x\n", + name, + rCol.red >> 8, rCol.green >> 8, rCol.blue >> 8 ); +} + +void printStyleColors( GtkStyle* pStyle ) +{ + static const char* pStates[] = { "NORMAL", "ACTIVE", "PRELIGHT", "SELECTED", "INSENSITIVE" }; + + for( int i = 0; i < 5; i++ ) + { + std::fprintf( stderr, "state %s colors:\n", pStates[i] ); + printColor( "bg ", pStyle->bg[i] ); + printColor( "fg ", pStyle->fg[i] ); + printColor( "light ", pStyle->light[i] ); + printColor( "dark ", pStyle->dark[i] ); + printColor( "mid ", pStyle->mid[i] ); + printColor( "text ", pStyle->text[i] ); + printColor( "base ", pStyle->base[i] ); + printColor( "text_aa", pStyle->text_aa[i] ); + } +} +#endif + +void GtkSalGraphics::updateSettings( AllSettings& rSettings ) +{ + // get the widgets in place + NWEnsureGTKMenu( m_nScreen ); + NWEnsureGTKMenubar( m_nScreen ); + NWEnsureGTKScrollbars( m_nScreen ); + NWEnsureGTKEditBox( m_nScreen ); + NWEnsureGTKTooltip( m_nScreen ); + + gtk_widget_ensure_style( m_pWindow ); + GtkStyle* pStyle = gtk_widget_get_style( m_pWindow ); + + StyleSettings aStyleSet = rSettings.GetStyleSettings(); + +#if OSL_DEBUG_LEVEL > 2 + printStyleColors( pStyle ); +#endif + + // text colors + Color aTextColor = getColor( pStyle->text[GTK_STATE_NORMAL] ); + aStyleSet.SetDialogTextColor( aTextColor ); + aStyleSet.SetButtonTextColor( aTextColor ); + aStyleSet.SetRadioCheckTextColor( aTextColor ); + aStyleSet.SetGroupTextColor( aTextColor ); + aStyleSet.SetLabelTextColor( aTextColor ); + aStyleSet.SetInfoTextColor( aTextColor ); + aStyleSet.SetWindowTextColor( aTextColor ); + aStyleSet.SetFieldTextColor( aTextColor ); + + // Tooltip colors + GtkStyle* pTooltipStyle = gtk_widget_get_style( gWidgetData[m_nScreen].gTooltipPopup ); + aTextColor = getColor( pTooltipStyle->fg[ GTK_STATE_NORMAL ] ); + aStyleSet.SetHelpTextColor( aTextColor ); + + // mouse over text colors + aTextColor = getColor( pStyle->fg[ GTK_STATE_PRELIGHT ] ); + aStyleSet.SetButtonRolloverTextColor( aTextColor ); + aStyleSet.SetFieldRolloverTextColor( aTextColor ); + + // background colors + Color aBackColor = getColor( pStyle->bg[GTK_STATE_NORMAL] ); + Color aBackFieldColor = getColor( pStyle->base[ GTK_STATE_NORMAL ] ); + aStyleSet.Set3DColors( aBackColor ); + aStyleSet.SetFaceColor( aBackColor ); + aStyleSet.SetDialogColor( aBackColor ); + aStyleSet.SetWorkspaceColor( aBackColor ); + aStyleSet.SetFieldColor( aBackFieldColor ); + aStyleSet.SetWindowColor( aBackFieldColor ); +// aStyleSet.SetHelpColor( aBackColor ); + // ancient wisdom tells us a mystic algorithm how to set checked color + if( aBackColor == COL_LIGHTGRAY ) + aStyleSet.SetCheckedColor( Color( 0xCC, 0xCC, 0xCC ) ); + else + { + Color aColor2 = aStyleSet.GetLightColor(); + Color aCheck( (BYTE)(((USHORT)aBackColor.GetRed()+(USHORT)aColor2.GetRed())/2), + (BYTE)(((USHORT)aBackColor.GetGreen()+(USHORT)aColor2.GetGreen())/2), + (BYTE)(((USHORT)aBackColor.GetBlue()+(USHORT)aColor2.GetBlue())/2) + ); + aStyleSet.SetCheckedColor( aCheck ); + } + + // highlighting colors + Color aHighlightColor = getColor( pStyle->base[GTK_STATE_SELECTED] ); + Color aHighlightTextColor = getColor( pStyle->text[GTK_STATE_SELECTED] ); + aStyleSet.SetHighlightColor( aHighlightColor ); + aStyleSet.SetHighlightTextColor( aHighlightTextColor ); + + if( ! gtk_check_version( 2, 10, 0 ) ) // link colors came in with 2.10, avoid an assertion + { + // hyperlink colors + GdkColor *link_color = NULL; + gtk_widget_style_get (m_pWindow, "link-color", &link_color, NULL); + if (link_color) + { + aStyleSet.SetLinkColor(getColor(*link_color)); + gdk_color_free (link_color); + link_color = NULL; + } + gtk_widget_style_get (m_pWindow, "visited-link-color", &link_color, NULL); + if (link_color) + { + aStyleSet.SetVisitedLinkColor(getColor(*link_color)); + gdk_color_free (link_color); + } + } + + // Tab colors + aStyleSet.SetActiveTabColor( aBackFieldColor ); // same as the window color. + Color aSelectedBackColor = getColor( pStyle->bg[GTK_STATE_ACTIVE] ); + aStyleSet.SetInactiveTabColor( aSelectedBackColor ); + + // menu disabled entries handling + aStyleSet.SetSkipDisabledInMenus( TRUE ); + // menu colors + GtkStyle* pMenuStyle = gtk_widget_get_style( gWidgetData[m_nScreen].gMenuWidget ); + GtkStyle* pMenuItemStyle = gtk_rc_get_style( gWidgetData[m_nScreen].gMenuItemMenuWidget ); + GtkStyle* pMenubarStyle = gtk_rc_get_style( gWidgetData[m_nScreen].gMenubarWidget ); + GtkStyle* pMenuTextStyle = gtk_rc_get_style( gtk_bin_get_child( GTK_BIN(gWidgetData[m_nScreen].gMenuItemMenuWidget) ) ); + + aBackColor = getColor( pMenubarStyle->bg[GTK_STATE_NORMAL] ); + aStyleSet.SetMenuBarColor( aBackColor ); + aBackColor = getColor( pMenuStyle->bg[GTK_STATE_NORMAL] ); + aTextColor = getColor( pMenuTextStyle->text[GTK_STATE_NORMAL] ); + aStyleSet.SetMenuColor( aBackColor ); + aStyleSet.SetMenuTextColor( aTextColor ); + + aTextColor = getColor( pMenubarStyle->text[GTK_STATE_NORMAL] ); + aStyleSet.SetMenuBarTextColor( aTextColor ); + +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "==\n" ); + std::fprintf( stderr, "MenuColor = %x (%d)\n", (int)aStyleSet.GetMenuColor().GetColor(), aStyleSet.GetMenuColor().GetLuminance() ); + std::fprintf( stderr, "MenuTextColor = %x (%d)\n", (int)aStyleSet.GetMenuTextColor().GetColor(), aStyleSet.GetMenuTextColor().GetLuminance() ); + std::fprintf( stderr, "MenuBarColor = %x (%d)\n", (int)aStyleSet.GetMenuBarColor().GetColor(), aStyleSet.GetMenuBarColor().GetLuminance() ); + std::fprintf( stderr, "MenuBarTextColor = %x (%d)\n", (int)aStyleSet.GetMenuBarTextColor().GetColor(), aStyleSet.GetMenuBarTextColor().GetLuminance() ); + std::fprintf( stderr, "LightColor = %x (%d)\n", (int)aStyleSet.GetLightColor().GetColor(), aStyleSet.GetLightColor().GetLuminance() ); + std::fprintf( stderr, "ShadowColor = %x (%d)\n", (int)aStyleSet.GetShadowColor().GetColor(), aStyleSet.GetShadowColor().GetLuminance() ); +#endif + + // Awful hack for menu separators in the Sonar and similar themes. + // If the menu color is not too dark, and the menu text color is lighter, + // make the "light" color lighter than the menu color and the "shadow" + // color darker than it. + if ( aStyleSet.GetMenuColor().GetLuminance() >= 32 && + aStyleSet.GetMenuColor().GetLuminance() <= aStyleSet.GetMenuTextColor().GetLuminance() ) + { + Color temp = aStyleSet.GetMenuColor(); + temp.IncreaseLuminance( 8 ); + aStyleSet.SetLightColor( temp ); + temp = aStyleSet.GetMenuColor(); + temp.DecreaseLuminance( 16 ); + aStyleSet.SetShadowColor( temp ); + } + + aHighlightColor = getColor( pMenuItemStyle->bg[ GTK_STATE_SELECTED ] ); + aHighlightTextColor = getColor( pMenuTextStyle->fg[ GTK_STATE_PRELIGHT ] ); + if( aHighlightColor == aHighlightTextColor ) + aHighlightTextColor = (aHighlightColor.GetLuminance() < 128) ? Color( COL_WHITE ) : Color( COL_BLACK ); + aStyleSet.SetMenuHighlightColor( aHighlightColor ); + aStyleSet.SetMenuHighlightTextColor( aHighlightTextColor ); + + // UI font + OString aFamily = pango_font_description_get_family( pStyle->font_desc ); + int nPangoHeight = pango_font_description_get_size( pStyle->font_desc ); + PangoStyle eStyle = pango_font_description_get_style( pStyle->font_desc ); + PangoWeight eWeight = pango_font_description_get_weight( pStyle->font_desc ); + PangoStretch eStretch = pango_font_description_get_stretch( pStyle->font_desc ); + + psp::FastPrintFontInfo aInfo; + // set family name + aInfo.m_aFamilyName = OStringToOUString( aFamily, RTL_TEXTENCODING_UTF8 ); + // set italic + switch( eStyle ) + { + case PANGO_STYLE_NORMAL: aInfo.m_eItalic = psp::italic::Upright;break; + case PANGO_STYLE_ITALIC: aInfo.m_eItalic = psp::italic::Italic;break; + case PANGO_STYLE_OBLIQUE: aInfo.m_eItalic = psp::italic::Oblique;break; + } + // set weight + if( eWeight <= PANGO_WEIGHT_ULTRALIGHT ) + aInfo.m_eWeight = psp::weight::UltraLight; + else if( eWeight <= PANGO_WEIGHT_LIGHT ) + aInfo.m_eWeight = psp::weight::Light; + else if( eWeight <= PANGO_WEIGHT_NORMAL ) + aInfo.m_eWeight = psp::weight::Normal; + else if( eWeight <= PANGO_WEIGHT_BOLD ) + aInfo.m_eWeight = psp::weight::Bold; + else + aInfo.m_eWeight = psp::weight::UltraBold; + // set width + switch( eStretch ) + { + case PANGO_STRETCH_ULTRA_CONDENSED: aInfo.m_eWidth = psp::width::UltraCondensed;break; + case PANGO_STRETCH_EXTRA_CONDENSED: aInfo.m_eWidth = psp::width::ExtraCondensed;break; + case PANGO_STRETCH_CONDENSED: aInfo.m_eWidth = psp::width::Condensed;break; + case PANGO_STRETCH_SEMI_CONDENSED: aInfo.m_eWidth = psp::width::SemiCondensed;break; + case PANGO_STRETCH_NORMAL: aInfo.m_eWidth = psp::width::Normal;break; + case PANGO_STRETCH_SEMI_EXPANDED: aInfo.m_eWidth = psp::width::SemiExpanded;break; + case PANGO_STRETCH_EXPANDED: aInfo.m_eWidth = psp::width::Expanded;break; + case PANGO_STRETCH_EXTRA_EXPANDED: aInfo.m_eWidth = psp::width::ExtraExpanded;break; + case PANGO_STRETCH_ULTRA_EXPANDED: aInfo.m_eWidth = psp::width::UltraExpanded;break; + } + +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "font name BEFORE system match: \"%s\"\n", aFamily.getStr() ); +#endif + + // match font to e.g. resolve "Sans" + psp::PrintFontManager::get().matchFont( aInfo, rSettings.GetUILocale() ); + +#if OSL_DEBUG_LEVEL > 1 + std::fprintf( stderr, "font match %s, name AFTER: \"%s\"\n", + aInfo.m_nID != 0 ? "succeeded" : "failed", + OUStringToOString( aInfo.m_aFamilyName, RTL_TEXTENCODING_ISO_8859_1 ).getStr() ); +#endif + + sal_Int32 nDispDPIY = GetDisplay()->GetResolution().B(); + int nPointHeight = 0; + static gboolean(*pAbso)(const PangoFontDescription*) = + (gboolean(*)(const PangoFontDescription*))osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "pango_font_description_get_size_is_absolute" ); + + if( pAbso && pAbso( pStyle->font_desc ) ) + nPointHeight = (nPangoHeight * 72 + nDispDPIY*PANGO_SCALE/2) / (nDispDPIY * PANGO_SCALE); + else + nPointHeight = nPangoHeight/PANGO_SCALE; + + Font aFont( aInfo.m_aFamilyName, Size( 0, nPointHeight ) ); + if( aInfo.m_eWeight != psp::weight::Unknown ) + aFont.SetWeight( PspGraphics::ToFontWeight( aInfo.m_eWeight ) ); + if( aInfo.m_eWidth != psp::width::Unknown ) + aFont.SetWidthType( PspGraphics::ToFontWidth( aInfo.m_eWidth ) ); + if( aInfo.m_eItalic != psp::italic::Unknown ) + aFont.SetItalic( PspGraphics::ToFontItalic( aInfo.m_eItalic ) ); + if( aInfo.m_ePitch != psp::pitch::Unknown ) + aFont.SetPitch( PspGraphics::ToFontPitch( aInfo.m_ePitch ) ); + + aStyleSet.SetAppFont( aFont ); + aStyleSet.SetHelpFont( aFont ); + aStyleSet.SetTitleFont( aFont ); + aStyleSet.SetFloatTitleFont( aFont ); + aStyleSet.SetMenuFont( aFont ); + aStyleSet.SetToolFont( aFont ); + aStyleSet.SetLabelFont( aFont ); + aStyleSet.SetInfoFont( aFont ); + aStyleSet.SetRadioCheckFont( aFont ); + aStyleSet.SetPushButtonFont( aFont ); + aStyleSet.SetFieldFont( aFont ); + aStyleSet.SetIconFont( aFont ); + aStyleSet.SetGroupFont( aFont ); + + // get cursor blink time + GtkSettings *pSettings = gtk_widget_get_settings( gWidgetData[m_nScreen].gEditBoxWidget ); + gboolean blink = false; + + g_object_get( pSettings, "gtk-cursor-blink", &blink, (char *)NULL ); + if( blink ) + { + gint blink_time = STYLE_CURSOR_NOBLINKTIME; + g_object_get( pSettings, "gtk-cursor-blink-time", &blink_time, (char *)NULL ); + // set the blink_time if there is a setting and it is reasonable + // else leave the default value + if( blink_time > 100 && blink_time != gint(STYLE_CURSOR_NOBLINKTIME) ) + aStyleSet.SetCursorBlinkTime( blink_time/2 ); + } + else + aStyleSet.SetCursorBlinkTime( STYLE_CURSOR_NOBLINKTIME ); + + gboolean showmenuicons = true; + pSettings = gtk_widget_get_settings( gWidgetData[m_nScreen].gImageMenuItem ); + g_object_get( pSettings, "gtk-menu-images", &showmenuicons, (char *)NULL ); + aStyleSet.SetUseImagesInMenus( showmenuicons ); + + // set scrollbar settings + gint slider_width = 14; + gint trough_border = 1; + gint min_slider_length = 21; + + // Grab some button style attributes + gtk_widget_style_get( gWidgetData[m_nScreen].gScrollHorizWidget, + "slider-width", &slider_width, + "trough-border", &trough_border, + "min-slider-length", &min_slider_length, + (char *)NULL ); + gint magic = trough_border ? 1 : 0; + aStyleSet.SetScrollBarSize( slider_width + 2*trough_border ); + aStyleSet.SetMinThumbSize( min_slider_length - magic ); + + // preferred icon style + gchar* pIconThemeName = NULL; + g_object_get( gtk_settings_get_default(), "gtk-icon-theme-name", &pIconThemeName, (char *)NULL ); + aStyleSet.SetPreferredSymbolsStyleName( OUString::createFromAscii( pIconThemeName ) ); + g_free( pIconThemeName ); + + // FIXME: need some way of fetching toolbar icon size. +// aStyleSet.SetToolbarIconSize( STYLE_TOOLBAR_ICONSIZE_SMALL ); + + const cairo_font_options_t* pNewOptions = NULL; + if( GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), m_nScreen ) ) + { +//#if !GTK_CHECK_VERSION(2,8,1) +#if !GTK_CHECK_VERSION(2,9,0) + static cairo_font_options_t* (*gdk_screen_get_font_options)(GdkScreen*) = + (cairo_font_options_t*(*)(GdkScreen*))osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_screen_get_font_options" ); + if( gdk_screen_get_font_options != NULL ) +#endif + pNewOptions = gdk_screen_get_font_options( pScreen ); + } + aStyleSet.SetCairoFontOptions( pNewOptions ); + + // finally update the collected settings + rSettings.SetStyleSettings( aStyleSet ); + + #if OSL_DEBUG_LEVEL > 1 + { + GtkSettings* pGtkSettings = gtk_settings_get_default(); + GValue aValue; + memset( &aValue, 0, sizeof(GValue) ); + g_value_init( &aValue, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(pGtkSettings), "gtk-theme-name", &aValue ); + const gchar* pThemeName = g_value_get_string( &aValue ); + std::fprintf( stderr, "Theme name is \"%s\"\n", pThemeName ); + g_value_unset( &aValue ); + } + #endif + GtkSettings* pGtkSettings = gtk_settings_get_default(); + GValue aValue; + memset( &aValue, 0, sizeof(GValue) ); + g_value_init( &aValue, G_TYPE_STRING ); + g_object_get_property( G_OBJECT(pGtkSettings), "gtk-theme-name", &aValue ); + const gchar* pThemeName = g_value_get_string( &aValue ); + + // default behaviour + bNeedPixmapPaint = bGlobalNeedPixmapPaint; + bToolbarGripWorkaround = false; + bNeedButtonStyleAsEditBackgroundWorkaround = false; + + // setup some workarounds for "blueprint" theme + if( pThemeName && strncasecmp( pThemeName, "blueprint", 9 ) == 0 ) + { + bNeedButtonStyleAsEditBackgroundWorkaround = true; + if( GetX11SalData()->GetDisplay()->GetServerVendor() == vendor_sun ) + { + // #i52570#, #i61532# workaround a weird paint issue; + // on a Sunray Xserver sometimes painting buttons and edits + // won't work when using the blueprint theme + // not reproducible with simpler programs or other themes + if( pThemeName && strncasecmp( pThemeName, "blueprint", 9 ) == 0 ) + { + bNeedPixmapPaint = true; + bToolbarGripWorkaround = true; + } + } + } + // clean up + g_value_unset( &aValue ); +} + + +/************************************************************************ + * Create a GdkPixmap filled with the contents of an area of an Xlib window + ************************************************************************/ + +GdkPixmap* GtkSalGraphics::NWGetPixmapFromScreen( Rectangle srcRect ) +{ + // Create a new pixmap to hold the composite of the window background and the control + GdkPixmap * pPixmap = gdk_pixmap_new( GDK_DRAWABLE(GetGdkWindow()), srcRect.GetWidth(), srcRect.GetHeight(), -1 ); + GdkGC * pPixmapGC = gdk_gc_new( pPixmap ); + + if( !pPixmap || !pPixmapGC ) + { + if ( pPixmap ) + g_object_unref( pPixmap ); + if ( pPixmapGC ) + g_object_unref( pPixmapGC ); + std::fprintf( stderr, "salnativewidgets-gtk.cxx: could not get valid pixmap from screen\n" ); + return( NULL ); + } + + // Copy the background of the screen into a composite pixmap + CopyScreenArea( GetXDisplay(), + GetDrawable(), GetScreenNumber(), GetVisual().GetDepth(), + gdk_x11_drawable_get_xid(pPixmap), + gdk_screen_get_number( gdk_drawable_get_screen( GDK_DRAWABLE(pPixmap) ) ), + gdk_drawable_get_depth( GDK_DRAWABLE( pPixmap ) ), + gdk_x11_gc_get_xgc(pPixmapGC), + srcRect.Left(), srcRect.Top(), srcRect.GetWidth(), srcRect.GetHeight(), 0, 0 ); + + g_object_unref( pPixmapGC ); + return( pPixmap ); +} + + + + +/************************************************************************ + * Copy an alpha pixmap to screen using a gc with clipping + ************************************************************************/ + +BOOL GtkSalGraphics::NWRenderPixmapToScreen( GdkPixmap* pPixmap, Rectangle dstRect ) +{ + // The GC can't be null, otherwise we'd have no clip region + if( SelectFont() == NULL ) + { + std::fprintf(stderr, "salnativewidgets.cxx: no valid GC\n" ); + return( FALSE ); + } + + if ( !pPixmap ) + return( FALSE ); + + // Copy the background of the screen into a composite pixmap + CopyScreenArea( GetXDisplay(), + GDK_DRAWABLE_XID(pPixmap), + gdk_screen_get_number( gdk_drawable_get_screen( GDK_DRAWABLE(pPixmap) ) ), + gdk_drawable_get_depth( GDK_DRAWABLE(pPixmap) ), + GetDrawable(), m_nScreen, GetVisual().GetDepth(), + SelectFont(), + 0, 0, dstRect.GetWidth(), dstRect.GetHeight(), dstRect.Left(), dstRect.Top() ); + + return( TRUE ); +} + + +/************************************************************************ + * State conversion + ************************************************************************/ +static void NWConvertVCLStateToGTKState( ControlState nVCLState, + GtkStateType* nGTKState, GtkShadowType* nGTKShadow ) +{ + *nGTKShadow = GTK_SHADOW_OUT; + *nGTKState = GTK_STATE_INSENSITIVE; + + if ( nVCLState & CTRL_STATE_ENABLED ) + { + if ( nVCLState & CTRL_STATE_PRESSED ) + { + *nGTKState = GTK_STATE_ACTIVE; + *nGTKShadow = GTK_SHADOW_IN; + } + else if ( nVCLState & CTRL_STATE_ROLLOVER ) + { + *nGTKState = GTK_STATE_PRELIGHT; + *nGTKShadow = GTK_SHADOW_OUT; + } + else + { + *nGTKState = GTK_STATE_NORMAL; + *nGTKShadow = GTK_SHADOW_OUT; + } + } +} + +/************************************************************************ + * Set widget flags + ************************************************************************/ +static void NWSetWidgetState( GtkWidget* widget, ControlState nState, GtkStateType nGtkState ) +{ + // Set to default state, then build up from there + GTK_WIDGET_UNSET_FLAGS( widget, GTK_HAS_DEFAULT ); + GTK_WIDGET_UNSET_FLAGS( widget, GTK_HAS_FOCUS ); + GTK_WIDGET_UNSET_FLAGS( widget, GTK_SENSITIVE ); + GTK_WIDGET_SET_FLAGS( widget, gWidgetDefaultFlags[(long)widget] ); + + if ( nState & CTRL_STATE_DEFAULT ) + GTK_WIDGET_SET_FLAGS( widget, GTK_HAS_DEFAULT ); + if ( !GTK_IS_TOGGLE_BUTTON(widget) && (nState & CTRL_STATE_FOCUSED) ) + GTK_WIDGET_SET_FLAGS( widget, GTK_HAS_FOCUS ); + if ( nState & CTRL_STATE_ENABLED ) + GTK_WIDGET_SET_FLAGS( widget, GTK_SENSITIVE ); + gtk_widget_set_state( widget, nGtkState ); +} + +/************************************************************************ + * Widget ensure functions - make sure cached objects are valid + ************************************************************************/ + +//------------------------------------- + +static void NWAddWidgetToCacheWindow( GtkWidget* widget, int nScreen ) +{ + NWFWidgetData& rData = gWidgetData[nScreen]; + if ( !rData.gCacheWindow || !rData.gDumbContainer ) + { + if ( !rData.gCacheWindow ) + { + rData.gCacheWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL ); + GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), nScreen ); + if( pScreen ) + gtk_window_set_screen( GTK_WINDOW(rData.gCacheWindow), pScreen ); + } + if ( !rData.gDumbContainer ) + rData.gDumbContainer = gtk_fixed_new(); + gtk_container_add( GTK_CONTAINER(rData.gCacheWindow), rData.gDumbContainer ); + gtk_widget_realize( rData.gDumbContainer ); + gtk_widget_realize( rData.gCacheWindow ); + } + + gtk_container_add( GTK_CONTAINER(rData.gDumbContainer), widget ); + gtk_widget_realize( widget ); + gtk_widget_ensure_style( widget ); + + // Store widget's default flags + gWidgetDefaultFlags[ (long)widget ] = GTK_WIDGET_FLAGS( widget ); +} + +//------------------------------------- + +static void NWEnsureGTKButton( int nScreen ) +{ + if ( !gWidgetData[nScreen].gBtnWidget ) + { + gWidgetData[nScreen].gBtnWidget = gtk_button_new_with_label( "" ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gBtnWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKRadio( int nScreen ) +{ + if ( !gWidgetData[nScreen].gRadioWidget || !gWidgetData[nScreen].gRadioWidgetSibling ) + { + gWidgetData[nScreen].gRadioWidget = gtk_radio_button_new( NULL ); + gWidgetData[nScreen].gRadioWidgetSibling = gtk_radio_button_new_from_widget( GTK_RADIO_BUTTON(gWidgetData[nScreen].gRadioWidget) ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gRadioWidget, nScreen ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gRadioWidgetSibling, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKCheck( int nScreen ) +{ + if ( !gWidgetData[nScreen].gCheckWidget ) + { + gWidgetData[nScreen].gCheckWidget = gtk_check_button_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gCheckWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKScrollbars( int nScreen ) +{ + if ( !gWidgetData[nScreen].gScrollHorizWidget ) + { + gWidgetData[nScreen].gScrollHorizWidget = gtk_hscrollbar_new( NULL ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrollHorizWidget, nScreen ); + } + + if ( !gWidgetData[nScreen].gScrollVertWidget ) + { + gWidgetData[nScreen].gScrollVertWidget = gtk_vscrollbar_new( NULL ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrollVertWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKArrow( int nScreen ) +{ + if ( !gWidgetData[nScreen].gArrowWidget || !gWidgetData[nScreen].gDropdownWidget ) + { + gWidgetData[nScreen].gDropdownWidget = gtk_toggle_button_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gDropdownWidget, nScreen ); + gWidgetData[nScreen].gArrowWidget = gtk_arrow_new( GTK_ARROW_DOWN, GTK_SHADOW_OUT ); + gtk_container_add( GTK_CONTAINER(gWidgetData[nScreen].gDropdownWidget), gWidgetData[nScreen].gArrowWidget ); + gtk_widget_set_rc_style( gWidgetData[nScreen].gArrowWidget ); + gtk_widget_realize( gWidgetData[nScreen].gArrowWidget ); + } +} + +//------------------------------------- + +static void NWEnsureGTKEditBox( int nScreen ) +{ + if ( !gWidgetData[nScreen].gEditBoxWidget ) + { + gWidgetData[nScreen].gEditBoxWidget = gtk_entry_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gEditBoxWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKSpinButton( int nScreen ) +{ + if ( !gWidgetData[nScreen].gSpinButtonWidget ) + { + GtkAdjustment *adj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 1, 1, 1, 0) ); + gWidgetData[nScreen].gSpinButtonWidget = gtk_spin_button_new( adj, 1, 2 ); + + //Setting non-editable means it doesn't blink, so there's no timeouts + //running around to nobble us + gtk_editable_set_editable(GTK_EDITABLE(gWidgetData[nScreen].gSpinButtonWidget), false); + + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gSpinButtonWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKNotebook( int nScreen ) +{ + if ( !gWidgetData[nScreen].gNotebookWidget ) + { + gWidgetData[nScreen].gNotebookWidget = gtk_notebook_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gNotebookWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKOptionMenu( int nScreen ) +{ + if ( !gWidgetData[nScreen].gOptionMenuWidget ) + { + gWidgetData[nScreen].gOptionMenuWidget = gtk_option_menu_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gOptionMenuWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKCombo( int nScreen ) +{ + if ( !gWidgetData[nScreen].gComboWidget ) + { + gWidgetData[nScreen].gComboWidget = gtk_combo_new(); + + // #i59129# Setting non-editable means it doesn't blink, so + // there are no timeouts running around to nobble us + gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry), false); + + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gComboWidget, nScreen ); + // Must realize the ComboBox's children, since GTK + // does not do this for us in GtkCombo::gtk_widget_realize() + gtk_widget_realize( GTK_COMBO(gWidgetData[nScreen].gComboWidget)->button ); + gtk_widget_realize( GTK_COMBO(gWidgetData[nScreen].gComboWidget)->entry ); + } +} + +//------------------------------------- + +static void NWEnsureGTKScrolledWindow( int nScreen ) +{ + if ( !gWidgetData[nScreen].gScrolledWindowWidget ) + { + GtkAdjustment *hadj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); + GtkAdjustment *vadj = GTK_ADJUSTMENT( gtk_adjustment_new(0, 0, 0, 0, 0, 0) ); + + gWidgetData[nScreen].gScrolledWindowWidget = gtk_scrolled_window_new( hadj, vadj ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gScrolledWindowWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKToolbar( int nScreen ) +{ + if( !gWidgetData[nScreen].gToolbarWidget ) + { + gWidgetData[nScreen].gToolbarWidget = gtk_toolbar_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gToolbarWidget, nScreen ); + gWidgetData[nScreen].gToolbarButtonWidget = gtk_button_new(); + gWidgetData[nScreen].gToolbarToggleWidget = gtk_toggle_button_new(); + + GtkReliefStyle aRelief = GTK_RELIEF_NORMAL; + gtk_widget_ensure_style( gWidgetData[nScreen].gToolbarWidget ); + gtk_widget_style_get( gWidgetData[nScreen].gToolbarWidget, + "button_relief", &aRelief, + (char *)NULL); + + gtk_button_set_relief( GTK_BUTTON(gWidgetData[nScreen].gToolbarButtonWidget), aRelief ); + GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarButtonWidget, GTK_CAN_FOCUS ); + GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarButtonWidget, GTK_CAN_DEFAULT ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gToolbarButtonWidget, nScreen ); + + gtk_button_set_relief( GTK_BUTTON(gWidgetData[nScreen].gToolbarToggleWidget), aRelief ); + GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarToggleWidget, GTK_CAN_FOCUS ); + GTK_WIDGET_UNSET_FLAGS( gWidgetData[nScreen].gToolbarToggleWidget, GTK_CAN_DEFAULT ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gToolbarToggleWidget, nScreen ); + } + if( ! gWidgetData[nScreen].gHandleBoxWidget ) + { + gWidgetData[nScreen].gHandleBoxWidget = gtk_handle_box_new(); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gHandleBoxWidget, nScreen ); + } +} + +//------------------------------------- + +static void NWEnsureGTKMenubar( int nScreen ) +{ + if( !gWidgetData[nScreen].gMenubarWidget ) + { + gWidgetData[nScreen].gMenubarWidget = gtk_menu_bar_new(); + gWidgetData[nScreen].gMenuItemMenubarWidget = gtk_menu_item_new_with_label( "b" ); + gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenubarWidget ), gWidgetData[nScreen].gMenuItemMenubarWidget ); + gtk_widget_show( gWidgetData[nScreen].gMenuItemMenubarWidget ); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gMenubarWidget, nScreen ); + gtk_widget_show( gWidgetData[nScreen].gMenubarWidget ); + + // do what NWAddWidgetToCacheWindow does except adding to def container + gtk_widget_realize( gWidgetData[nScreen].gMenuItemMenubarWidget ); + gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemMenubarWidget ); + + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gMenuItemMenubarWidget ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemMenubarWidget ); + } +} + +static void NWEnsureGTKMenu( int nScreen ) +{ + if( !gWidgetData[nScreen].gMenuWidget ) + { + gWidgetData[nScreen].gMenuWidget = gtk_menu_new(); + gWidgetData[nScreen].gMenuItemMenuWidget = gtk_menu_item_new_with_label( "b" ); + gWidgetData[nScreen].gMenuItemCheckMenuWidget = gtk_check_menu_item_new_with_label( "b" ); + gWidgetData[nScreen].gMenuItemRadioMenuWidget = gtk_radio_menu_item_new_with_label( NULL, "b" ); + gWidgetData[nScreen].gImageMenuItem = gtk_image_menu_item_new(); + + gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemMenuWidget ); + gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemCheckMenuWidget ); + gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gMenuItemRadioMenuWidget ); + gtk_menu_shell_append( GTK_MENU_SHELL( gWidgetData[nScreen].gMenuWidget ), gWidgetData[nScreen].gImageMenuItem ); + + // do what NWAddWidgetToCacheWindow does except adding to def container + gtk_widget_realize( gWidgetData[nScreen].gMenuWidget ); + gtk_widget_ensure_style( gWidgetData[nScreen].gMenuWidget ); + + gtk_widget_realize( gWidgetData[nScreen].gMenuItemMenuWidget ); + gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemMenuWidget ); + + gtk_widget_realize( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); + gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); + + gtk_widget_realize( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); + gtk_widget_ensure_style( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); + + gtk_widget_realize( gWidgetData[nScreen].gImageMenuItem ); + gtk_widget_ensure_style( gWidgetData[nScreen].gImageMenuItem ); + + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gMenuWidget ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuWidget ); + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gMenuItemMenuWidget ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemMenuWidget ); + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gMenuItemCheckMenuWidget ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemCheckMenuWidget ); + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gMenuItemRadioMenuWidget ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gMenuItemRadioMenuWidget ); + gWidgetDefaultFlags[ (long)gWidgetData[nScreen].gImageMenuItem ] = GTK_WIDGET_FLAGS( gWidgetData[nScreen].gImageMenuItem ); + } +} + +static void NWEnsureGTKTooltip( int nScreen ) +{ + if( !gWidgetData[nScreen].gTooltipPopup ) + { + gWidgetData[nScreen].gTooltipPopup = gtk_window_new (GTK_WINDOW_POPUP); + GdkScreen* pScreen = gdk_display_get_screen( gdk_display_get_default(), nScreen ); + if( pScreen ) + gtk_window_set_screen( GTK_WINDOW(gWidgetData[nScreen].gTooltipPopup), pScreen ); + gtk_widget_set_name( gWidgetData[nScreen].gTooltipPopup, "gtk-tooltips"); + gtk_widget_realize( gWidgetData[nScreen].gTooltipPopup ); + gtk_widget_ensure_style( gWidgetData[nScreen].gTooltipPopup ); + } +} + +static void NWEnsureGTKProgressBar( int nScreen ) +{ + if( !gWidgetData[nScreen].gProgressBar ) + { + gWidgetData[nScreen].gProgressBar = gtk_progress_bar_new (); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gProgressBar, nScreen ); + } +} + +static void NWEnsureGTKTreeView( int nScreen ) +{ + if( !gWidgetData[nScreen].gTreeView ) + { + gWidgetData[nScreen].gTreeView = gtk_tree_view_new (); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gTreeView, nScreen ); + } +} + +static void NWEnsureGTKSlider( int nScreen ) +{ + if( !gWidgetData[nScreen].gHScale ) + { + gWidgetData[nScreen].gHScale = gtk_hscale_new_with_range(0, 10, 1); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gHScale, nScreen ); + } + if( !gWidgetData[nScreen].gVScale ) + { + gWidgetData[nScreen].gVScale = gtk_vscale_new_with_range(0, 10, 1); + NWAddWidgetToCacheWindow( gWidgetData[nScreen].gVScale, nScreen ); + } +} diff --git a/vcl/unx/gtk/window/gtkframe.cxx b/vcl/unx/gtk/window/gtkframe.cxx new file mode 100644 index 000000000000..ef356eb57aa9 --- /dev/null +++ b/vcl/unx/gtk/window/gtkframe.cxx @@ -0,0 +1,3733 @@ +/************************************************************************* + * + * 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 <plugins/gtk/gtkdata.hxx> +#include <plugins/gtk/gtkinst.hxx> +#include <plugins/gtk/gtkgdi.hxx> +#include <vcl/keycodes.hxx> +#include <wmadaptor.hxx> +#include <sm.hxx> +#include <salbmp.h> +#include <salprn.h> +#include <vcl/floatwin.hxx> +#include <salprn.h> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#include <tools/prex.h> +#include <X11/Xatom.h> +#include <tools/postx.h> + +#include <dlfcn.h> +#include <vcl/salbtype.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/impbmp.hxx> +#include <vcl/svids.hrc> + +#include <algorithm> + +#if OSL_DEBUG_LEVEL > 1 +#include <cstdio> +#endif + +#include <com/sun/star/accessibility/XAccessibleContext.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/XAccessibleStateSet.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> +#include <com/sun/star/accessibility/XAccessibleEditableText.hpp> + +#ifdef ENABLE_DBUS +#include <dbus/dbus-glib.h> + +#define GSM_DBUS_SERVICE "org.gnome.SessionManager" +#define GSM_DBUS_PATH "/org/gnome/SessionManager" +#define GSM_DBUS_INTERFACE "org.gnome.SessionManager" +#endif + +// make compile on gtk older than 2.10 +#if GTK_MINOR_VERSION < 10 +#define GDK_SUPER_MASK (1 << 26) +#define GDK_HYPER_MASK (1 << 27) +#define GDK_META_MASK (1 << 28) +#endif + +using namespace com::sun::star; + +int GtkSalFrame::m_nFloats = 0; + +static USHORT GetKeyModCode( guint state ) +{ + USHORT nCode = 0; + if( (state & GDK_SHIFT_MASK) ) + nCode |= KEY_SHIFT; + if( (state & GDK_CONTROL_MASK) ) + nCode |= KEY_MOD1; + if( (state & GDK_MOD1_MASK) ) + nCode |= KEY_MOD2; + + // Map Meta/Super keys to MOD3 modifier on all Unix systems + // except Mac OS X + if ( (state & GDK_META_MASK ) || ( state & GDK_SUPER_MASK ) ) + nCode |= KEY_MOD3; + return nCode; +} + +static USHORT GetMouseModCode( guint state ) +{ + USHORT nCode = GetKeyModCode( state ); + if( (state & GDK_BUTTON1_MASK) ) + nCode |= MOUSE_LEFT; + if( (state & GDK_BUTTON2_MASK) ) + nCode |= MOUSE_MIDDLE; + if( (state & GDK_BUTTON3_MASK) ) + nCode |= MOUSE_RIGHT; + + return nCode; +} + +static USHORT GetKeyCode( guint keyval ) +{ + USHORT nCode = 0; + if( keyval >= GDK_0 && keyval <= GDK_9 ) + nCode = KEY_0 + (keyval-GDK_0); + else if( keyval >= GDK_KP_0 && keyval <= GDK_KP_9 ) + nCode = KEY_0 + (keyval-GDK_KP_0); + else if( keyval >= GDK_A && keyval <= GDK_Z ) + nCode = KEY_A + (keyval-GDK_A ); + else if( keyval >= GDK_a && keyval <= GDK_z ) + nCode = KEY_A + (keyval-GDK_a ); + else if( keyval >= GDK_F1 && keyval <= GDK_F26 ) + { + if( GetX11SalData()->GetDisplay()->IsNumLockFromXS() ) + { + nCode = KEY_F1 + (keyval-GDK_F1); + } + else + { + switch( keyval ) + { + // - - - - - Sun keyboard, see vcl/unx/source/app/saldisp.cxx + case GDK_L2: + if( GetX11SalData()->GetDisplay()->GetServerVendor() == vendor_sun ) + nCode = KEY_REPEAT; + else + nCode = KEY_F12; + break; + case GDK_L3: nCode = KEY_PROPERTIES; break; + case GDK_L4: nCode = KEY_UNDO; break; + case GDK_L6: nCode = KEY_COPY; break; // KEY_F16 + case GDK_L8: nCode = KEY_PASTE; break; // KEY_F18 + case GDK_L10: nCode = KEY_CUT; break; // KEY_F20 + default: + nCode = KEY_F1 + (keyval-GDK_F1); break; + } + } + } + else + { + switch( keyval ) + { + case GDK_KP_Down: + case GDK_Down: nCode = KEY_DOWN; break; + case GDK_KP_Up: + case GDK_Up: nCode = KEY_UP; break; + case GDK_KP_Left: + case GDK_Left: nCode = KEY_LEFT; break; + case GDK_KP_Right: + case GDK_Right: nCode = KEY_RIGHT; break; + case GDK_KP_Begin: + case GDK_KP_Home: + case GDK_Begin: + case GDK_Home: nCode = KEY_HOME; break; + case GDK_KP_End: + case GDK_End: nCode = KEY_END; break; + case GDK_KP_Page_Up: + case GDK_Page_Up: nCode = KEY_PAGEUP; break; + case GDK_KP_Page_Down: + case GDK_Page_Down: nCode = KEY_PAGEDOWN; break; + case GDK_KP_Enter: + case GDK_Return: nCode = KEY_RETURN; break; + case GDK_Escape: nCode = KEY_ESCAPE; break; + case GDK_ISO_Left_Tab: + case GDK_KP_Tab: + case GDK_Tab: nCode = KEY_TAB; break; + case GDK_BackSpace: nCode = KEY_BACKSPACE; break; + case GDK_KP_Space: + case GDK_space: nCode = KEY_SPACE; break; + case GDK_KP_Insert: + case GDK_Insert: nCode = KEY_INSERT; break; + case GDK_KP_Delete: + case GDK_Delete: nCode = KEY_DELETE; break; + case GDK_plus: + case GDK_KP_Add: nCode = KEY_ADD; break; + case GDK_minus: + case GDK_KP_Subtract: nCode = KEY_SUBTRACT; break; + case GDK_asterisk: + case GDK_KP_Multiply: nCode = KEY_MULTIPLY; break; + case GDK_slash: + case GDK_KP_Divide: nCode = KEY_DIVIDE; break; + case GDK_period: + case GDK_decimalpoint: nCode = KEY_POINT; break; + case GDK_comma: nCode = KEY_COMMA; break; + case GDK_less: nCode = KEY_LESS; break; + case GDK_greater: nCode = KEY_GREATER; break; + case GDK_KP_Equal: + case GDK_equal: nCode = KEY_EQUAL; break; + case GDK_Find: nCode = KEY_FIND; break; + case GDK_Menu: nCode = KEY_CONTEXTMENU;break; + case GDK_Help: nCode = KEY_HELP; break; + case GDK_Undo: nCode = KEY_UNDO; break; + case GDK_Redo: nCode = KEY_REPEAT; break; + case GDK_KP_Decimal: + case GDK_KP_Separator: nCode = KEY_DECIMAL; break; + case GDK_asciitilde: nCode = KEY_TILDE; break; + case GDK_leftsinglequotemark: + case GDK_quoteleft: nCode = KEY_QUOTELEFT; break; + // some special cases, also see saldisp.cxx + // - - - - - - - - - - - - - Apollo - - - - - - - - - - - - - 0x1000 + case 0x1000FF02: // apXK_Copy + nCode = KEY_COPY; + break; + case 0x1000FF03: // apXK_Cut + nCode = KEY_CUT; + break; + case 0x1000FF04: // apXK_Paste + nCode = KEY_PASTE; + break; + case 0x1000FF14: // apXK_Repeat + nCode = KEY_REPEAT; + break; + // Exit, Save + // - - - - - - - - - - - - - - D E C - - - - - - - - - - - - - 0x1000 + case 0x1000FF00: + nCode = KEY_DELETE; + break; + // - - - - - - - - - - - - - - H P - - - - - - - - - - - - - 0x1000 + case 0x1000FF73: // hpXK_DeleteChar + nCode = KEY_DELETE; + break; + case 0x1000FF74: // hpXK_BackTab + case 0x1000FF75: // hpXK_KP_BackTab + nCode = KEY_TAB; + break; + // - - - - - - - - - - - - - - I B M - - - - - - - - - - - - - + // - - - - - - - - - - - - - - O S F - - - - - - - - - - - - - 0x1004 + case 0x1004FF02: // osfXK_Copy + nCode = KEY_COPY; + break; + case 0x1004FF03: // osfXK_Cut + nCode = KEY_CUT; + break; + case 0x1004FF04: // osfXK_Paste + nCode = KEY_PASTE; + break; + case 0x1004FF07: // osfXK_BackTab + nCode = KEY_TAB; + break; + case 0x1004FF08: // osfXK_BackSpace + nCode = KEY_BACKSPACE; + break; + case 0x1004FF1B: // osfXK_Escape + nCode = KEY_ESCAPE; + break; + // Up, Down, Left, Right, PageUp, PageDown + // - - - - - - - - - - - - - - S C O - - - - - - - - - - - - - + // - - - - - - - - - - - - - - S G I - - - - - - - - - - - - - 0x1007 + // - - - - - - - - - - - - - - S N I - - - - - - - - - - - - - + // - - - - - - - - - - - - - - S U N - - - - - - - - - - - - - 0x1005 + case 0x1005FF10: // SunXK_F36 + nCode = KEY_F11; + break; + case 0x1005FF11: // SunXK_F37 + nCode = KEY_F12; + break; + case 0x1005FF70: // SunXK_Props + nCode = KEY_PROPERTIES; + break; + case 0x1005FF71: // SunXK_Front + nCode = KEY_FRONT; + break; + case 0x1005FF72: // SunXK_Copy + nCode = KEY_COPY; + break; + case 0x1005FF73: // SunXK_Open + nCode = KEY_OPEN; + break; + case 0x1005FF74: // SunXK_Paste + nCode = KEY_PASTE; + break; + case 0x1005FF75: // SunXK_Cut + nCode = KEY_CUT; + break; + } + } + + return nCode; +} + +// F10 means either KEY_F10 or KEY_MENU, which has to be decided +// in the independent part. +struct KeyAlternate +{ + USHORT nKeyCode; + sal_Unicode nCharCode; + KeyAlternate() : nKeyCode( 0 ), nCharCode( 0 ) {} + KeyAlternate( USHORT nKey, sal_Unicode nChar = 0 ) : nKeyCode( nKey ), nCharCode( nChar ) {} +}; + +inline KeyAlternate +GetAlternateKeyCode( const USHORT nKeyCode ) +{ + KeyAlternate aAlternate; + + switch( nKeyCode ) + { + case KEY_F10: aAlternate = KeyAlternate( KEY_MENU );break; + case KEY_F24: aAlternate = KeyAlternate( KEY_SUBTRACT, '-' );break; + } + + return aAlternate; +} + +void GtkSalFrame::doKeyCallback( guint state, + guint keyval, + guint16 hardware_keycode, + guint8 /*group*/, + guint32 time, + sal_Unicode aOrigCode, + bool bDown, + bool bSendRelease + ) +{ + SalKeyEvent aEvent; + + aEvent.mnTime = time; + aEvent.mnCharCode = aOrigCode; + aEvent.mnRepeat = 0; + + vcl::DeletionListener aDel( this ); + /* #i42122# translate all keys with Ctrl and/or Alt to group 0 + * else shortcuts (e.g. Ctrl-o) will not work but be inserted by + * the application + */ + /* #i52338# do this for all keys that the independent part has no key code for + */ + aEvent.mnCode = GetKeyCode( keyval ); + if( aEvent.mnCode == 0 ) + { + // check other mapping + gint eff_group, level; + GdkModifierType consumed; + guint updated_keyval = 0; + // use gdk_keymap_get_default instead of NULL; + // workaround a crahs fixed in gtk 2.4 + if( gdk_keymap_translate_keyboard_state( gdk_keymap_get_default(), + hardware_keycode, + (GdkModifierType)0, + 0, + &updated_keyval, + &eff_group, + &level, + &consumed ) ) + { + aEvent.mnCode = GetKeyCode( updated_keyval ); + } + } + aEvent.mnCode |= GetKeyModCode( state ); + + if( bDown ) + { + bool bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + // #i46889# copy AlternatKeyCode handling from generic plugin + if( ! bHandled ) + { + KeyAlternate aAlternate = GetAlternateKeyCode( aEvent.mnCode ); + if( aAlternate.nKeyCode ) + { + aEvent.mnCode = aAlternate.nKeyCode; + if( aAlternate.nCharCode ) + aEvent.mnCharCode = aAlternate.nCharCode; + bHandled = CallCallback( SALEVENT_KEYINPUT, &aEvent ); + } + } + if( bSendRelease && ! aDel.isDeleted() ) + { + CallCallback( SALEVENT_KEYUP, &aEvent ); + } + } + else + CallCallback( SALEVENT_KEYUP, &aEvent ); +} + +GtkSalFrame::GraphicsHolder::~GraphicsHolder() +{ + delete pGraphics; +} + +GtkSalFrame::GtkSalFrame( SalFrame* pParent, ULONG nStyle ) +{ + m_nScreen = getDisplay()->GetDefaultScreenNumber(); + getDisplay()->registerFrame( this ); + m_bDefaultPos = true; + m_bDefaultSize = ( (nStyle & SAL_FRAME_STYLE_SIZEABLE) && ! pParent ); + m_bWindowIsGtkPlug = false; + Init( pParent, nStyle ); +} + +GtkSalFrame::GtkSalFrame( SystemParentData* pSysData ) +{ + m_nScreen = getDisplay()->GetDefaultScreenNumber(); + getDisplay()->registerFrame( this ); + getDisplay()->setHaveSystemChildFrame(); + m_bDefaultPos = true; + m_bDefaultSize = true; + Init( pSysData ); +} + +GtkSalFrame::~GtkSalFrame() +{ + for( unsigned int i = 0; i < sizeof(m_aGraphics)/sizeof(m_aGraphics[0]); ++i ) + { + if( !m_aGraphics[i].pGraphics ) + continue; + m_aGraphics[i].pGraphics->SetDrawable( None, m_nScreen ); + m_aGraphics[i].bInUse = false; + } + + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + + getDisplay()->deregisterFrame( this ); + + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + + if( m_hBackgroundPixmap ) + { + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + None ); + XFreePixmap( getDisplay()->GetDisplay(), m_hBackgroundPixmap ); + } + + if( m_pIMHandler ) + delete m_pIMHandler; + + if( m_pFixedContainer ) + gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) ); + if( m_pWindow ) + { + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", NULL ); + gtk_widget_destroy( m_pWindow ); + } + if( m_pForeignParent ) + g_object_unref( G_OBJECT(m_pForeignParent) ); + if( m_pForeignTopLevel ) + g_object_unref(G_OBJECT( m_pForeignTopLevel) ); +} + +void GtkSalFrame::moveWindow( long nX, long nY ) +{ + if( isChild( false, true ) ) + { + if( m_pParent ) + gtk_fixed_move( m_pParent->getFixedContainer(), + m_pWindow, + nX - m_pParent->maGeometry.nX, nY - m_pParent->maGeometry.nY ); + } + else + gtk_window_move( GTK_WINDOW(m_pWindow), nX, nY ); +} + +void GtkSalFrame::resizeWindow( long nWidth, long nHeight ) +{ + if( isChild( false, true ) ) + gtk_widget_set_size_request( m_pWindow, nWidth, nHeight ); + else if( ! isChild( true, false ) ) + gtk_window_resize( GTK_WINDOW(m_pWindow), nWidth, nHeight ); +} + +/* + * Always use a sub-class of GtkFixed we can tag for a11y. This allows us to + * utilize GAIL for the toplevel window and toolkit implementation incl. + * key event listener support .. + */ + +GType +ooo_fixed_get_type() +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo tinfo = + { + sizeof (GtkFixedClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) NULL, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GtkFixed), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static( GTK_TYPE_FIXED, "OOoFixed", + &tinfo, (GTypeFlags) 0); + } + + return type; +} + +void GtkSalFrame::updateScreenNumber() +{ + if( getDisplay()->IsXinerama() && getDisplay()->GetXineramaScreens().size() > 1 ) + { + Point aPoint( maGeometry.nX, maGeometry.nY ); + const std::vector<Rectangle>& rScreenRects( getDisplay()->GetXineramaScreens() ); + size_t nScreens = rScreenRects.size(); + for( size_t i = 0; i < nScreens; i++ ) + { + if( rScreenRects[i].IsInside( aPoint ) ) + { + maGeometry.nScreenNumber = static_cast<unsigned int>(i); + break; + } + } + } + else + maGeometry.nScreenNumber = static_cast<unsigned int>(m_nScreen); +} + +void GtkSalFrame::InitCommon() +{ + // connect signals + g_signal_connect( G_OBJECT(m_pWindow), "style-set", G_CALLBACK(signalStyleSet), this ); + g_signal_connect( G_OBJECT(m_pWindow), "button-press-event", G_CALLBACK(signalButton), this ); + g_signal_connect( G_OBJECT(m_pWindow), "button-release-event", G_CALLBACK(signalButton), this ); + g_signal_connect( G_OBJECT(m_pWindow), "expose-event", G_CALLBACK(signalExpose), this ); + g_signal_connect( G_OBJECT(m_pWindow), "focus-in-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pWindow), "focus-out-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pWindow), "map-event", G_CALLBACK(signalMap), this ); + g_signal_connect( G_OBJECT(m_pWindow), "unmap-event", G_CALLBACK(signalUnmap), this ); + g_signal_connect( G_OBJECT(m_pWindow), "configure-event", G_CALLBACK(signalConfigure), this ); + g_signal_connect( G_OBJECT(m_pWindow), "motion-notify-event", G_CALLBACK(signalMotion), this ); + g_signal_connect( G_OBJECT(m_pWindow), "key-press-event", G_CALLBACK(signalKey), this ); + g_signal_connect( G_OBJECT(m_pWindow), "key-release-event", G_CALLBACK(signalKey), this ); + g_signal_connect( G_OBJECT(m_pWindow), "delete-event", G_CALLBACK(signalDelete), this ); + g_signal_connect( G_OBJECT(m_pWindow), "window-state-event", G_CALLBACK(signalState), this ); + g_signal_connect( G_OBJECT(m_pWindow), "scroll-event", G_CALLBACK(signalScroll), this ); + g_signal_connect( G_OBJECT(m_pWindow), "leave-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "enter-notify-event", G_CALLBACK(signalCrossing), this ); + g_signal_connect( G_OBJECT(m_pWindow), "visibility-notify-event", G_CALLBACK(signalVisibility), this ); + g_signal_connect( G_OBJECT(m_pWindow), "destroy", G_CALLBACK(signalDestroy), this ); + + // init members + m_pCurrentCursor = NULL; + m_nKeyModifiers = 0; + m_bSingleAltPress = false; + m_bFullscreen = false; + m_nState = GDK_WINDOW_STATE_WITHDRAWN; + m_nVisibility = GDK_VISIBILITY_FULLY_OBSCURED; + m_bSendModChangeOnRelease = false; + m_pIMHandler = NULL; + m_hBackgroundPixmap = None; + m_nSavedScreenSaverTimeout = 0; + m_nGSMCookie = 0; + m_nExtStyle = 0; + m_pRegion = NULL; + m_ePointerStyle = 0xffff; + + gtk_widget_set_app_paintable( m_pWindow, TRUE ); + gtk_widget_set_double_buffered( m_pWindow, FALSE ); + gtk_widget_set_redraw_on_allocate( m_pWindow, FALSE ); + gtk_widget_add_events( m_pWindow, + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | + GDK_VISIBILITY_NOTIFY_MASK + ); + + // add the fixed container child, + // fixed is needed since we have to position plugin windows + m_pFixedContainer = GTK_FIXED(g_object_new( ooo_fixed_get_type(), NULL )); + gtk_container_add( GTK_CONTAINER(m_pWindow), GTK_WIDGET(m_pFixedContainer) ); + + // show the widgets + gtk_widget_show( GTK_WIDGET(m_pFixedContainer) ); + + // realize the window, we need an XWindow id + gtk_widget_realize( m_pWindow ); + + //system data + SalDisplay* pDisp = GetX11SalData()->GetDisplay(); + m_aSystemData.nSize = sizeof( SystemChildData ); + m_aSystemData.pDisplay = pDisp->GetDisplay(); + m_aSystemData.aWindow = GDK_WINDOW_XWINDOW(m_pWindow->window); + m_aSystemData.pSalFrame = this; + m_aSystemData.pWidget = m_pWindow; + m_aSystemData.pVisual = pDisp->GetVisual( m_nScreen ).GetVisual(); + m_aSystemData.nScreen = m_nScreen; + m_aSystemData.nDepth = pDisp->GetVisual( m_nScreen ).GetDepth(); + m_aSystemData.aColormap = pDisp->GetColormap( m_nScreen ).GetXColormap(); + m_aSystemData.pAppContext = NULL; + m_aSystemData.aShellWindow = m_aSystemData.aWindow; + m_aSystemData.pShellWidget = m_aSystemData.pWidget; + + + // fake an initial geometry, gets updated via configure event or SetPosSize + if( m_bDefaultPos || m_bDefaultSize ) + { + Size aDefSize = calcDefaultSize(); + maGeometry.nX = -1; + maGeometry.nY = -1; + maGeometry.nWidth = aDefSize.Width(); + maGeometry.nHeight = aDefSize.Height(); + if( m_pParent ) + { + // approximation + maGeometry.nTopDecoration = m_pParent->maGeometry.nTopDecoration; + maGeometry.nBottomDecoration = m_pParent->maGeometry.nBottomDecoration; + maGeometry.nLeftDecoration = m_pParent->maGeometry.nLeftDecoration; + maGeometry.nRightDecoration = m_pParent->maGeometry.nRightDecoration; + } + else + { + maGeometry.nTopDecoration = 0; + maGeometry.nBottomDecoration = 0; + maGeometry.nLeftDecoration = 0; + maGeometry.nRightDecoration = 0; + } + } + else + { + resizeWindow( maGeometry.nWidth, maGeometry.nHeight ); + moveWindow( maGeometry.nX, maGeometry.nY ); + } + updateScreenNumber(); + + SetIcon(1); + m_nWorkArea = pDisp->getWMAdaptor()->getCurrentWorkArea(); + + /* #i64117# gtk sets a nice background pixmap + * but we actually don't really want that, so save + * some time on the Xserver as well as prevent + * some paint issues + */ + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + m_hBackgroundPixmap ); +} + +/* Sadly gtk_window_set_accept_focus exists only since gtk 2.4 + * for achieving the same effect we will remove the WM_TAKE_FOCUS + * protocol from the window and set the input hint to false. + * But gtk_window_set_accept_focus needs to be called before + * window realization whereas the removal obviously can only happen + * after realization. + */ + +extern "C" { + typedef void(*setAcceptFn)( GtkWindow*, gboolean ); + static setAcceptFn p_gtk_window_set_accept_focus = NULL; + static bool bGetAcceptFocusFn = true; + + typedef void(*setUserTimeFn)( GdkWindow*, guint32 ); + static setUserTimeFn p_gdk_x11_window_set_user_time = NULL; + static bool bGetSetUserTimeFn = true; +} + +static void lcl_set_accept_focus( GtkWindow* pWindow, gboolean bAccept, bool bBeforeRealize ) +{ + if( bGetAcceptFocusFn ) + { + bGetAcceptFocusFn = false; + p_gtk_window_set_accept_focus = (setAcceptFn)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gtk_window_set_accept_focus" ); + } + if( p_gtk_window_set_accept_focus && bBeforeRealize ) + p_gtk_window_set_accept_focus( pWindow, bAccept ); + else if( ! bBeforeRealize ) + { + Display* pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + XLIB_Window aWindow = GDK_WINDOW_XWINDOW( GTK_WIDGET(pWindow)->window ); + XWMHints* pHints = XGetWMHints( pDisplay, aWindow ); + if( ! pHints ) + { + pHints = XAllocWMHints(); + pHints->flags = 0; + } + pHints->flags |= InputHint; + pHints->input = bAccept ? True : False; + XSetWMHints( pDisplay, aWindow, pHints ); + XFree( pHints ); + + if (GetX11SalData()->GetDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii("compiz")) + return; + + /* remove WM_TAKE_FOCUS protocol; this would usually be the + * right thing, but gtk handles it internally whereas we + * want to handle it ourselves (as to sometimes not get + * the focus) + */ + Atom* pProtocols = NULL; + int nProtocols = 0; + XGetWMProtocols( pDisplay, + aWindow, + &pProtocols, &nProtocols ); + if( pProtocols ) + { + bool bSet = false; + Atom nTakeFocus = XInternAtom( pDisplay, "WM_TAKE_FOCUS", True ); + if( nTakeFocus ) + { + for( int i = 0; i < nProtocols; i++ ) + { + if( pProtocols[i] == nTakeFocus ) + { + for( int n = i; n < nProtocols-1; n++ ) + pProtocols[n] = pProtocols[n+1]; + nProtocols--; + i--; + bSet = true; + } + } + } + if( bSet ) + XSetWMProtocols( pDisplay, aWindow, pProtocols, nProtocols ); + XFree( pProtocols ); + } + } +} +static void lcl_set_user_time( GdkWindow* i_pWindow, guint32 i_nTime ) +{ + if( bGetSetUserTimeFn ) + { + bGetSetUserTimeFn = false; + p_gdk_x11_window_set_user_time = (setUserTimeFn)osl_getAsciiFunctionSymbol( GetSalData()->m_pPlugin, "gdk_x11_window_set_user_time" ); + } + if( p_gdk_x11_window_set_user_time ) + p_gdk_x11_window_set_user_time( i_pWindow, i_nTime ); + else + { + Display* pDisplay = GetX11SalData()->GetDisplay()->GetDisplay(); + XLIB_Window aWindow = GDK_WINDOW_XWINDOW( i_pWindow ); + Atom nUserTime = XInternAtom( pDisplay, "_NET_WM_USER_TIME", True ); + if( nUserTime ) + { + XChangeProperty( pDisplay, aWindow, + nUserTime, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&i_nTime, 1 ); + } + } +}; + +GtkSalFrame *GtkSalFrame::getFromWindow( GtkWindow *pWindow ) +{ + return (GtkSalFrame *) g_object_get_data( G_OBJECT( pWindow ), "SalFrame" ); +} + +void GtkSalFrame::Init( SalFrame* pParent, ULONG nStyle ) +{ + if( nStyle & SAL_FRAME_STYLE_DEFAULT ) // ensure default style + { + nStyle |= SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE; + nStyle &= ~SAL_FRAME_STYLE_FLOAT; + } + + m_pParent = static_cast<GtkSalFrame*>(pParent); + m_pForeignParent = NULL; + m_aForeignParentWindow = None; + m_pForeignTopLevel = NULL; + m_aForeignTopLevelWindow = None; + m_nStyle = nStyle; + + GtkWindowType eWinType = ( (nStyle & SAL_FRAME_STYLE_FLOAT) && + ! (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION| + SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) + ) + ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL; + + if( nStyle & SAL_FRAME_STYLE_SYSTEMCHILD ) + { + m_pWindow = gtk_event_box_new(); + if( m_pParent ) + { + // insert into container + gtk_fixed_put( m_pParent->getFixedContainer(), + m_pWindow, 0, 0 ); + + } + } + else + m_pWindow = gtk_widget_new( GTK_TYPE_WINDOW, "type", eWinType, "visible", FALSE, NULL ); + g_object_set_data( G_OBJECT( m_pWindow ), "SalFrame", this ); + + // force wm class hint + m_nExtStyle = ~0; + SetExtendedFrameStyle( 0 ); + + if( m_pParent && m_pParent->m_pWindow && ! isChild() ) + gtk_window_set_screen( GTK_WINDOW(m_pWindow), gtk_window_get_screen( GTK_WINDOW(m_pParent->m_pWindow) ) ); + + // set window type + bool bDecoHandling = + ! isChild() && + ( ! (nStyle & SAL_FRAME_STYLE_FLOAT) || + (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) ); + + /* #i100116# metacity has a peculiar behavior regarding WM_HINT accept focus and _NET_WM_USER_TIME + at some point that may be fixed in metacity and we will have to revisit this + */ + + // MT/PL 2010/02: #i102694# and #i102803# have been introduced by this hack + // Nowadays the original issue referenced above doesn't seem to exist anymore, tested different szenarious described in the issues + // If some older versions of MetaCity are still in use somewhere, they need to be updated, instead of using strange hacks in OOo. + // As a work around for such old systems, people might consider to not use the GTK plugin. + + bool bMetaCityToolWindowHack = false; + // bMetaCityToolWindowHack = getDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii("Metacity") && (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ); + + if( bDecoHandling ) + { + bool bNoDecor = ! (nStyle & (SAL_FRAME_STYLE_MOVEABLE | SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_CLOSEABLE ) ); + GdkWindowTypeHint eType = GDK_WINDOW_TYPE_HINT_NORMAL; + if( (nStyle & SAL_FRAME_STYLE_DIALOG) && m_pParent != 0 ) + eType = GDK_WINDOW_TYPE_HINT_DIALOG; + if( (nStyle & SAL_FRAME_STYLE_INTRO) ) + { + gtk_window_set_role( GTK_WINDOW(m_pWindow), "splashscreen" ); + eType = GDK_WINDOW_TYPE_HINT_SPLASHSCREEN; + } + else if( (nStyle & SAL_FRAME_STYLE_TOOLWINDOW ) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + gtk_window_set_skip_taskbar_hint( GTK_WINDOW(m_pWindow), true ); + if( bMetaCityToolWindowHack ) + lcl_set_accept_focus( GTK_WINDOW(m_pWindow), FALSE, true ); + } + else if( (nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + { + eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; + lcl_set_accept_focus( GTK_WINDOW(m_pWindow), FALSE, true ); + bNoDecor = true; + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT_FOCUSABLE) ) + { + eType = GDK_WINDOW_TYPE_HINT_UTILITY; + } + + if( (nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN ) ) + { + eType = GDK_WINDOW_TYPE_HINT_TOOLBAR; + gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), true ); + } + + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), eType ); + if( bNoDecor ) + gtk_window_set_decorated( GTK_WINDOW(m_pWindow), FALSE ); + gtk_window_set_gravity( GTK_WINDOW(m_pWindow), GDK_GRAVITY_STATIC ); + if( m_pParent && ! (m_pParent->m_nStyle & SAL_FRAME_STYLE_PLUG) ) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), GTK_WINDOW(m_pParent->m_pWindow) ); + } + else if( (nStyle & SAL_FRAME_STYLE_FLOAT) ) + { + gtk_window_set_type_hint( GTK_WINDOW(m_pWindow), GDK_WINDOW_TYPE_HINT_UTILITY ); + } + if( m_pParent ) + m_pParent->m_aChildren.push_back( this ); + + InitCommon(); + + if( eWinType == GTK_WINDOW_TOPLEVEL ) + { + guint32 nUserTime = 0; + if( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) + { + /* #i99360# ugly workaround an X11 library bug */ + nUserTime= getDisplay()->GetLastUserEventTime( true ); + // nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + } + lcl_set_user_time(GTK_WIDGET(m_pWindow)->window, nUserTime); + } + + if( bDecoHandling ) + { + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), (nStyle & SAL_FRAME_STYLE_SIZEABLE) ? TRUE : FALSE ); + if( ( (nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) ) || bMetaCityToolWindowHack ) + lcl_set_accept_focus( GTK_WINDOW(m_pWindow), FALSE, false ); + } + +} + +GdkNativeWindow GtkSalFrame::findTopLevelSystemWindow( GdkNativeWindow aWindow ) +{ + XLIB_Window aRoot, aParent; + XLIB_Window* pChildren; + unsigned int nChildren; + bool bBreak = false; + do + { + pChildren = NULL; + nChildren = 0; + aParent = aRoot = None; + XQueryTree( getDisplay()->GetDisplay(), aWindow, + &aRoot, &aParent, &pChildren, &nChildren ); + XFree( pChildren ); + if( aParent != aRoot ) + aWindow = aParent; + int nCount = 0; + Atom* pProps = XListProperties( getDisplay()->GetDisplay(), + aWindow, + &nCount ); + for( int i = 0; i < nCount && ! bBreak; ++i ) + bBreak = (pProps[i] == XA_WM_HINTS); + if( pProps ) + XFree( pProps ); + } while( aParent != aRoot && ! bBreak ); + + return aWindow; +} + +void GtkSalFrame::Init( SystemParentData* pSysData ) +{ + m_pParent = NULL; + m_aForeignParentWindow = (GdkNativeWindow)pSysData->aWindow; + m_pForeignParent = NULL; + m_aForeignTopLevelWindow = findTopLevelSystemWindow( (GdkNativeWindow)pSysData->aWindow ); + m_pForeignTopLevel = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignTopLevelWindow ); + gdk_window_set_events( m_pForeignTopLevel, GDK_STRUCTURE_MASK ); + + if( pSysData->nSize > sizeof(pSysData->nSize)+sizeof(pSysData->aWindow) && pSysData->bXEmbedSupport ) + { + m_pWindow = gtk_plug_new( pSysData->aWindow ); + m_bWindowIsGtkPlug = true; + GTK_WIDGET_SET_FLAGS( m_pWindow, GTK_CAN_FOCUS | GTK_SENSITIVE | GTK_CAN_DEFAULT ); + gtk_widget_set_sensitive( m_pWindow, true ); + } + else + { + m_pWindow = gtk_window_new( GTK_WINDOW_POPUP ); + m_bWindowIsGtkPlug = false; + } + m_nStyle = SAL_FRAME_STYLE_PLUG; + InitCommon(); + + m_pForeignParent = gdk_window_foreign_new_for_display( getGdkDisplay(), m_aForeignParentWindow ); + gdk_window_set_events( m_pForeignParent, GDK_STRUCTURE_MASK ); + int x_ret, y_ret; + unsigned int w, h, bw, d; + XLIB_Window aRoot; + XGetGeometry( getDisplay()->GetDisplay(), pSysData->aWindow, + &aRoot, &x_ret, &y_ret, &w, &h, &bw, &d ); + maGeometry.nWidth = w; + maGeometry.nHeight = h; + gtk_window_resize( GTK_WINDOW(m_pWindow), w, h ); + gtk_window_move( GTK_WINDOW(m_pWindow), 0, 0 ); + if( ! m_bWindowIsGtkPlug ) + { + XReparentWindow( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + (XLIB_Window)pSysData->aWindow, + 0, 0 ); + } +} + +void GtkSalFrame::askForXEmbedFocus( sal_Int32 i_nTimeCode ) +{ + XEvent aEvent; + + rtl_zeroMemory( &aEvent, sizeof(aEvent) ); + aEvent.xclient.window = m_aForeignParentWindow; + aEvent.xclient.type = ClientMessage; + aEvent.xclient.message_type = getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ); + aEvent.xclient.format = 32; + aEvent.xclient.data.l[0] = i_nTimeCode ? i_nTimeCode : CurrentTime; + aEvent.xclient.data.l[1] = 3; // XEMBED_REQUEST_FOCUS + aEvent.xclient.data.l[2] = 0; + aEvent.xclient.data.l[3] = 0; + aEvent.xclient.data.l[4] = 0; + + getDisplay()->GetXLib()->PushXErrorLevel( true ); + XSendEvent( getDisplay()->GetDisplay(), + m_aForeignParentWindow, + False, NoEventMask, &aEvent ); + XSync( getDisplay()->GetDisplay(), False ); + getDisplay()->GetXLib()->PopXErrorLevel(); +} + +void GtkSalFrame::SetExtendedFrameStyle( SalExtStyle nStyle ) +{ + if( nStyle != m_nExtStyle && ! isChild() ) + { + m_nExtStyle = nStyle; + if( GTK_WIDGET_REALIZED( m_pWindow ) ) + { + XClassHint* pClass = XAllocClassHint(); + rtl::OString aResHint = X11SalData::getFrameResName( m_nExtStyle ); + pClass->res_name = const_cast<char*>(aResHint.getStr()); + pClass->res_class = const_cast<char*>(X11SalData::getFrameClassName()); + XSetClassHint( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + pClass ); + XFree( pClass ); + } + else + gtk_window_set_wmclass( GTK_WINDOW(m_pWindow), + X11SalData::getFrameResName( m_nExtStyle ), + X11SalData::getFrameClassName() ); + } +} + + +SalGraphics* GtkSalFrame::GetGraphics() +{ + if( m_pWindow ) + { + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( ! m_aGraphics[i].bInUse ) + { + m_aGraphics[i].bInUse = true; + if( ! m_aGraphics[i].pGraphics ) + { + m_aGraphics[i].pGraphics = new GtkSalGraphics( m_pWindow ); + m_aGraphics[i].pGraphics->Init( this, GDK_WINDOW_XWINDOW(m_pWindow->window), m_nScreen ); + } + return m_aGraphics[i].pGraphics; + } + } + } + + return NULL; +} + +void GtkSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) +{ + for( int i = 0; i < nMaxGraphics; i++ ) + { + if( m_aGraphics[i].pGraphics == pGraphics ) + { + m_aGraphics[i].bInUse = false; + break; + } + } +} + +BOOL GtkSalFrame::PostEvent( void* pData ) +{ + getDisplay()->SendInternalEvent( this, pData ); + return TRUE; +} + +void GtkSalFrame::SetTitle( const String& rTitle ) +{ + m_aTitle = rTitle; + if( m_pWindow && ! isChild() ) + gtk_window_set_title( GTK_WINDOW(m_pWindow), rtl::OUStringToOString( rTitle, RTL_TEXTENCODING_UTF8 ).getStr() ); +} + +static inline BYTE * +getRow( BitmapBuffer *pBuffer, ULONG nRow ) +{ + if( BMP_SCANLINE_ADJUSTMENT( pBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN ) + return pBuffer->mpBits + nRow * pBuffer->mnScanlineSize; + else + return pBuffer->mpBits + ( pBuffer->mnHeight - nRow - 1 ) * pBuffer->mnScanlineSize; +} + +static GdkPixbuf * +bitmapToPixbuf( SalBitmap *pSalBitmap, SalBitmap *pSalAlpha ) +{ + g_return_val_if_fail( pSalBitmap != NULL, NULL ); + g_return_val_if_fail( pSalAlpha != NULL, NULL ); + + BitmapBuffer *pBitmap = pSalBitmap->AcquireBuffer( TRUE ); + g_return_val_if_fail( pBitmap != NULL, NULL ); + g_return_val_if_fail( pBitmap->mnBitCount == 24, NULL ); + + BitmapBuffer *pAlpha = pSalAlpha->AcquireBuffer( TRUE ); + g_return_val_if_fail( pAlpha != NULL, NULL ); + g_return_val_if_fail( pAlpha->mnBitCount == 8, NULL ); + + Size aSize = pSalBitmap->GetSize(); + g_return_val_if_fail( pSalAlpha->GetSize() == aSize, NULL ); + + int nX, nY; + guchar *pPixbufData = (guchar *)g_malloc (4 * aSize.Width() * aSize.Height() ); + guchar *pDestData = pPixbufData; + + for( nY = 0; nY < pBitmap->mnHeight; nY++ ) + { + BYTE *pData = getRow( pBitmap, nY ); + BYTE *pAlphaData = getRow( pAlpha, nY ); + + for( nX = 0; nX < pBitmap->mnWidth; nX++ ) + { + if( pBitmap->mnFormat == BMP_FORMAT_24BIT_TC_BGR ) + { + pDestData[2] = *pData++; + pDestData[1] = *pData++; + pDestData[0] = *pData++; + } + else // BMP_FORMAT_24BIT_TC_RGB + { + pDestData[0] = *pData++; + pDestData[1] = *pData++; + pDestData[2] = *pData++; + } + pDestData += 3; + *pDestData++ = 255 - *pAlphaData++; + } + } + + pSalBitmap->ReleaseBuffer( pBitmap, TRUE ); + pSalAlpha->ReleaseBuffer( pAlpha, TRUE ); + + return gdk_pixbuf_new_from_data( pPixbufData, + GDK_COLORSPACE_RGB, TRUE, 8, + aSize.Width(), aSize.Height(), + aSize.Width() * 4, + (GdkPixbufDestroyNotify) g_free, + NULL ); +} + +void GtkSalFrame::SetIcon( USHORT nIcon ) +{ + if( (m_nStyle & (SAL_FRAME_STYLE_PLUG|SAL_FRAME_STYLE_SYSTEMCHILD|SAL_FRAME_STYLE_FLOAT|SAL_FRAME_STYLE_INTRO|SAL_FRAME_STYLE_OWNERDRAWDECORATION)) + || ! m_pWindow ) + return; + + if( !ImplGetResMgr() ) + return; + + GdkPixbuf *pBuf; + GList *pIcons = NULL; + + USHORT nOffsets[2] = { SV_ICON_SMALL_START, SV_ICON_LARGE_START }; + USHORT nIndex; + + // Use high contrast icons where appropriate + if( Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) + { + nOffsets[0] = SV_ICON_LARGE_HC_START; + nOffsets[1] = SV_ICON_SMALL_HC_START; + } + + for( nIndex = 0; nIndex < sizeof(nOffsets)/ sizeof(USHORT); nIndex++ ) + { + // #i44723# workaround gcc temporary problem + ResId aResId( nOffsets[nIndex] + nIcon, *ImplGetResMgr() ); + BitmapEx aIcon( aResId ); + + // #i81083# convert to 24bit/8bit alpha bitmap + Bitmap aBmp = aIcon.GetBitmap(); + if( aBmp.GetBitCount() != 24 || ! aIcon.IsAlpha() ) + { + if( aBmp.GetBitCount() != 24 ) + aBmp.Convert( BMP_CONVERSION_24BIT ); + AlphaMask aMask; + if( ! aIcon.IsAlpha() ) + { + switch( aIcon.GetTransparentType() ) + { + case TRANSPARENT_NONE: + { + BYTE nTrans = 0; + aMask = AlphaMask( aBmp.GetSizePixel(), &nTrans ); + } + break; + case TRANSPARENT_COLOR: + aMask = AlphaMask( aBmp.CreateMask( aIcon.GetTransparentColor() ) ); + break; + case TRANSPARENT_BITMAP: + aMask = AlphaMask( aIcon.GetMask() ); + break; + default: + DBG_ERROR( "unhandled transparent type" ); + break; + } + } + else + aMask = aIcon.GetAlpha(); + aIcon = BitmapEx( aBmp, aMask ); + } + + ImpBitmap *pIconImpBitmap = aIcon.ImplGetBitmapImpBitmap(); + ImpBitmap *pIconImpMask = aIcon.ImplGetMaskImpBitmap(); + + + if( pIconImpBitmap && pIconImpMask ) + { + SalBitmap *pIconBitmap = + pIconImpBitmap->ImplGetSalBitmap(); + SalBitmap *pIconMask = + pIconImpMask->ImplGetSalBitmap(); + + if( ( pBuf = bitmapToPixbuf( pIconBitmap, pIconMask ) ) ) + pIcons = g_list_prepend( pIcons, pBuf ); + } + } + + gtk_window_set_icon_list( GTK_WINDOW(m_pWindow), pIcons ); + + g_list_foreach( pIcons, (GFunc) g_object_unref, NULL ); + g_list_free( pIcons ); +} + +void GtkSalFrame::SetMenu( SalMenu* ) +{ +} + +void GtkSalFrame::DrawMenuBar() +{ +} + +void GtkSalFrame::Center() +{ + long nX, nY; + + if( m_pParent ) + { + nX = ((long)m_pParent->maGeometry.nWidth - (long)maGeometry.nWidth)/2; + nY = ((long)m_pParent->maGeometry.nHeight - (long)maGeometry.nHeight)/2; + + } + else + { + long nScreenWidth, nScreenHeight; + long nScreenX = 0, nScreenY = 0; + + Size aScreenSize = GetX11SalData()->GetDisplay()->GetScreenSize( m_nScreen ); + nScreenWidth = aScreenSize.Width(); + nScreenHeight = aScreenSize.Height(); + if( GetX11SalData()->GetDisplay()->IsXinerama() ) + { + // get xinerama screen we are on + // if there is a parent, use its center for screen determination + // else use the pointer + GdkScreen* pScreen; + gint x, y; + GdkModifierType aMask; + gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); + + const std::vector< Rectangle >& rScreens = GetX11SalData()->GetDisplay()->GetXineramaScreens(); + for( unsigned int i = 0; i < rScreens.size(); i++ ) + if( rScreens[i].IsInside( Point( x, y ) ) ) + { + nScreenX = rScreens[i].Left(); + nScreenY = rScreens[i].Top(); + nScreenWidth = rScreens[i].GetWidth(); + nScreenHeight = rScreens[i].GetHeight(); + break; + } + } + nX = nScreenX + (nScreenWidth - (long)maGeometry.nWidth)/2; + nY = nScreenY + (nScreenHeight - (long)maGeometry.nHeight)/2; + } + SetPosSize( nX, nY, 0, 0, SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); +} + +Size GtkSalFrame::calcDefaultSize() +{ + Size aScreenSize = GetX11SalData()->GetDisplay()->GetScreenSize( m_nScreen ); + long w = aScreenSize.Width(); + long h = aScreenSize.Height(); + + // fill in holy default values brought to us by product management + if( aScreenSize.Width() >= 800 ) + w = 785; + if( aScreenSize.Width() >= 1024 ) + w = 920; + + if( aScreenSize.Height() >= 600 ) + h = 550; + if( aScreenSize.Height() >= 768 ) + h = 630; + if( aScreenSize.Height() >= 1024 ) + h = 875; + + return Size( w, h ); +} + +void GtkSalFrame::SetDefaultSize() +{ + Size aDefSize = calcDefaultSize(); + + SetPosSize( 0, 0, aDefSize.Width(), aDefSize.Height(), + SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + + if( (m_nStyle & SAL_FRAME_STYLE_DEFAULT) && m_pWindow ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); +} + +static void initClientId() +{ + static bool bOnce = false; + if( ! bOnce ) + { + bOnce = true; + const ByteString& rID = SessionManagerClient::getSessionID(); + if( rID.Len() > 0 ) + gdk_set_sm_client_id(rID.GetBuffer()); + } +} + +void GtkSalFrame::Show( BOOL bVisible, BOOL bNoActivate ) +{ + if( m_pWindow ) + { + if( m_pParent && (m_pParent->m_nStyle & SAL_FRAME_STYLE_PARTIAL_FULLSCREEN) ) + gtk_window_set_keep_above( GTK_WINDOW(m_pWindow), bVisible ); + if( bVisible ) + { + SessionManagerClient::open(); // will simply return after the first time + initClientId(); + getDisplay()->startupNotificationCompleted(); + + if( m_bDefaultPos ) + Center(); + if( m_bDefaultSize ) + SetDefaultSize(); + setMinMaxSize(); + + // #i45160# switch to desktop where a dialog with parent will appear + if( m_pParent && m_pParent->m_nWorkArea != m_nWorkArea && GTK_WIDGET_MAPPED(m_pParent->m_pWindow) ) + getDisplay()->getWMAdaptor()->switchToWorkArea( m_pParent->m_nWorkArea ); + + if( isFloatGrabWindow() && + m_pParent && + m_nFloats == 0 && + ! getDisplay()->GetCaptureFrame() ) + { + /* #i63086# + * outsmart Metacity's "focus:mouse" mode + * which insists on taking the focus from the document + * to the new float. Grab focus to parent frame BEFORE + * showing the float (cannot grab it to the float + * before show). + */ + m_pParent->grabPointer( TRUE, TRUE ); + } + + guint32 nUserTime = 0; + if( ! bNoActivate && (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_TOOLWINDOW)) == 0 ) + /* #i99360# ugly workaround an X11 library bug */ + nUserTime= getDisplay()->GetLastUserEventTime( true ); + //nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + + //For these floating windows we don't want the main window to lose focus, and metacity has... + // metacity-2.24.0/src/core/window.c + // + // if ((focus_window != NULL) && XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time)) + // "compare" window focus prevented by other activity + // + // where "compare" is this window + + // which leads to... + + // /* This happens for error dialogs or alerts; these need to remain on + // * top, but it would be confusing to have its ancestor remain + // * focused. + // */ + // if (meta_window_is_ancestor_of_transient (focus_window, window)) + // "The focus window %s is an ancestor of the newly mapped " + // "window %s which isn't being focused. Unfocusing the " + // "ancestor.\n", + // + // i.e. having a time < that of the toplevel frame means that the toplevel frame gets unfocused. + // awesome. + if( nUserTime == 0 && + ( + getDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii("Metacity") || + ( + getDisplay()->getWMAdaptor()->getWindowManagerName().EqualsAscii("compiz") && + (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION)) + ) + ) + ) + { + /* #i99360# ugly workaround an X11 library bug */ + nUserTime= getDisplay()->GetLastUserEventTime( true ); + //nUserTime = gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window); + } + + lcl_set_user_time( GTK_WIDGET(m_pWindow)->window, nUserTime ); + + gtk_widget_show( m_pWindow ); + + if( isFloatGrabWindow() ) + { + m_nFloats++; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 1 ) + grabPointer( TRUE, TRUE ); + // #i44068# reset parent's IM context + if( m_pParent ) + m_pParent->EndExtTextInput(0); + } + if( m_bWindowIsGtkPlug ) + askForXEmbedFocus( 0 ); + } + else + { + if( isFloatGrabWindow() ) + { + m_nFloats--; + if( ! getDisplay()->GetCaptureFrame() && m_nFloats == 0) + grabPointer( FALSE ); + } + gtk_widget_hide( m_pWindow ); + if( m_pIMHandler ) + m_pIMHandler->focusChanged( false ); + // flush here; there may be a very seldom race between + // the display connection used for clipboard and our connection + Flush(); + } + CallCallback( SALEVENT_RESIZE, NULL ); + } +} + +void GtkSalFrame::Enable( BOOL /*bEnable*/ ) +{ + // Not implemented by X11SalFrame either +} + +void GtkSalFrame::setMinMaxSize() +{ +/* FIXME: for yet unknown reasons the reported size is a little smaller + * than the max size hint; one would guess that this was due to the border + * sizes of the widgets involved (GtkWindow and GtkFixed), but setting the + * their border to 0 (which is the default anyway) does not change the + * behaviour. Until the reason is known we'll add some pixels here. + */ +#define CONTAINER_ADJUSTMENT 6 + + /* #i34504# metacity (and possibly others) do not treat + * _NET_WM_STATE_FULLSCREEN and max_width/heigth independently; + * whether they should is undefined. So don't set the max size hint + * for a full screen window. + */ + if( m_pWindow && ! isChild() ) + { + GdkGeometry aGeo; + int aHints = 0; + if( m_nStyle & SAL_FRAME_STYLE_SIZEABLE ) + { + if( m_aMinSize.Width() && m_aMinSize.Height() ) + { + aGeo.min_width = m_aMinSize.Width()+CONTAINER_ADJUSTMENT; + aGeo.min_height = m_aMinSize.Height()+CONTAINER_ADJUSTMENT; + aHints |= GDK_HINT_MIN_SIZE; + } + if( m_aMaxSize.Width() && m_aMaxSize.Height() && ! m_bFullscreen ) + { + aGeo.max_width = m_aMaxSize.Width()+CONTAINER_ADJUSTMENT; + aGeo.max_height = m_aMaxSize.Height()+CONTAINER_ADJUSTMENT; + aHints |= GDK_HINT_MAX_SIZE; + } + } + else + { + aGeo.min_width = maGeometry.nWidth; + aGeo.min_height = maGeometry.nHeight; + aHints |= GDK_HINT_MIN_SIZE; + if( ! m_bFullscreen ) + { + aGeo.max_width = maGeometry.nWidth; + aGeo.max_height = maGeometry.nHeight; + aHints |= GDK_HINT_MAX_SIZE; + } + } + if( aHints ) + gtk_window_set_geometry_hints( GTK_WINDOW(m_pWindow), + NULL, + &aGeo, + GdkWindowHints( aHints ) ); + } +} + +void GtkSalFrame::SetMaxClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMaxSize = Size( nWidth, nHeight ); + // Show does a setMinMaxSize + if( GTK_WIDGET_MAPPED( m_pWindow ) ) + setMinMaxSize(); + } +} +void GtkSalFrame::SetMinClientSize( long nWidth, long nHeight ) +{ + if( ! isChild() ) + { + m_aMinSize = Size( nWidth, nHeight ); + if( m_pWindow ) + { + gtk_widget_set_size_request( m_pWindow, nWidth, nHeight ); + // Show does a setMinMaxSize + if( GTK_WIDGET_MAPPED( m_pWindow ) ) + setMinMaxSize(); + } + } +} + +void GtkSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight, USHORT nFlags ) +{ + if( !m_pWindow || isChild( true, false ) ) + return; + + bool bSized = false, bMoved = false; + + if( (nFlags & ( SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT )) && + (nWidth > 0 && nHeight > 0 ) // sometimes stupid things happen + ) + { + m_bDefaultSize = false; + + if( (unsigned long)nWidth != maGeometry.nWidth || (unsigned long)nHeight != maGeometry.nHeight ) + bSized = true; + maGeometry.nWidth = nWidth; + maGeometry.nHeight = nHeight; + + if( isChild( false, true ) ) + gtk_widget_set_size_request( m_pWindow, nWidth, nHeight ); + else if( ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) ) + gtk_window_resize( GTK_WINDOW(m_pWindow), nWidth, nHeight ); + setMinMaxSize(); + } + else if( m_bDefaultSize ) + SetDefaultSize(); + + m_bDefaultSize = false; + + if( nFlags & ( SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ) ) + { + if( m_pParent ) + { + if( Application::GetSettings().GetLayoutRTL() ) + nX = m_pParent->maGeometry.nWidth-maGeometry.nWidth-1-nX; + nX += m_pParent->maGeometry.nX; + nY += m_pParent->maGeometry.nY; + } + + // adjust position to avoid off screen windows + // but allow toolbars to be positioned partly off screen by the user + Size aScreenSize = GetX11SalData()->GetDisplay()->GetScreenSize( m_nScreen ); + if( ! (m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) ) + { + if( nX < (long)maGeometry.nLeftDecoration ) + nX = maGeometry.nLeftDecoration; + if( nY < (long)maGeometry.nTopDecoration ) + nY = maGeometry.nTopDecoration; + if( (nX + (long)maGeometry.nWidth + (long)maGeometry.nRightDecoration) > (long)aScreenSize.Width() ) + nX = aScreenSize.Width() - maGeometry.nWidth - maGeometry.nRightDecoration; + if( (nY + (long)maGeometry.nHeight + (long)maGeometry.nBottomDecoration) > (long)aScreenSize.Height() ) + nY = aScreenSize.Height() - maGeometry.nHeight - maGeometry.nBottomDecoration; + } + else + { + if( nX + (long)maGeometry.nWidth < 10 ) + nX = 10 - (long)maGeometry.nWidth; + if( nY + (long)maGeometry.nHeight < 10 ) + nY = 10 - (long)maGeometry.nHeight; + if( nX > (long)aScreenSize.Width() - 10 ) + nX = (long)aScreenSize.Width() - 10; + if( nY > (long)aScreenSize.Height() - 10 ) + nY = (long)aScreenSize.Height() - 10; + } + + if( nX != maGeometry.nX || nY != maGeometry.nY ) + bMoved = true; + maGeometry.nX = nX; + maGeometry.nY = nY; + + m_bDefaultPos = false; + + moveWindow( maGeometry.nX, maGeometry.nY ); + + updateScreenNumber(); + } + else if( m_bDefaultPos ) + Center(); + + m_bDefaultPos = false; + + if( bSized && ! bMoved ) + CallCallback( SALEVENT_RESIZE, NULL ); + else if( bMoved && ! bSized ) + CallCallback( SALEVENT_MOVE, NULL ); + else if( bMoved && bSized ) + CallCallback( SALEVENT_MOVERESIZE, NULL ); +} + +void GtkSalFrame::GetClientSize( long& rWidth, long& rHeight ) +{ + if( m_pWindow && !(m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + { + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; + } + else + rWidth = rHeight = 0; +} + +void GtkSalFrame::GetWorkArea( Rectangle& rRect ) +{ + rRect = GetX11SalData()->GetDisplay()->getWMAdaptor()->getWorkArea( 0 ); +} + +SalFrame* GtkSalFrame::GetParent() const +{ + return m_pParent; +} + +void GtkSalFrame::SetWindowState( const SalFrameState* pState ) +{ + if( ! m_pWindow || ! pState || isChild( true, false ) ) + return; + + const ULONG nMaxGeometryMask = + SAL_FRAMESTATE_MASK_X | SAL_FRAMESTATE_MASK_Y | + SAL_FRAMESTATE_MASK_WIDTH | SAL_FRAMESTATE_MASK_HEIGHT | + SAL_FRAMESTATE_MASK_MAXIMIZED_X | SAL_FRAMESTATE_MASK_MAXIMIZED_Y | + SAL_FRAMESTATE_MASK_MAXIMIZED_WIDTH | SAL_FRAMESTATE_MASK_MAXIMIZED_HEIGHT; + + if( (pState->mnMask & SAL_FRAMESTATE_MASK_STATE) && + ! ( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) && + (pState->mnState & SAL_FRAMESTATE_MAXIMIZED) && + (pState->mnMask & nMaxGeometryMask) == nMaxGeometryMask ) + { + resizeWindow( pState->mnWidth, pState->mnHeight ); + moveWindow( pState->mnX, pState->mnY ); + m_bDefaultPos = m_bDefaultSize = false; + + maGeometry.nX = pState->mnMaximizedX; + maGeometry.nY = pState->mnMaximizedY; + maGeometry.nWidth = pState->mnMaximizedWidth; + maGeometry.nHeight = pState->mnMaximizedHeight; + updateScreenNumber(); + + m_nState = GdkWindowState( m_nState | GDK_WINDOW_STATE_MAXIMIZED ); + m_aRestorePosSize = Rectangle( Point( pState->mnX, pState->mnY ), + Size( pState->mnWidth, pState->mnHeight ) ); + } + else if( pState->mnMask & (SAL_FRAMESTATE_MASK_X | SAL_FRAMESTATE_MASK_Y | + SAL_FRAMESTATE_MASK_WIDTH | SAL_FRAMESTATE_MASK_HEIGHT ) ) + { + USHORT nPosSizeFlags = 0; + long nX = pState->mnX - (m_pParent ? m_pParent->maGeometry.nX : 0); + long nY = pState->mnY - (m_pParent ? m_pParent->maGeometry.nY : 0); + long nWidth = pState->mnWidth; + long nHeight = pState->mnHeight; + if( pState->mnMask & SAL_FRAMESTATE_MASK_X ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_X; + else + nX = maGeometry.nX - (m_pParent ? m_pParent->maGeometry.nX : 0); + if( pState->mnMask & SAL_FRAMESTATE_MASK_Y ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_Y; + else + nY = maGeometry.nY - (m_pParent ? m_pParent->maGeometry.nY : 0); + if( pState->mnMask & SAL_FRAMESTATE_MASK_WIDTH ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_WIDTH; + else + nWidth = maGeometry.nWidth; + if( pState->mnMask & SAL_FRAMESTATE_MASK_HEIGHT ) + nPosSizeFlags |= SAL_FRAME_POSSIZE_HEIGHT; + else + nHeight = maGeometry.nHeight; + SetPosSize( nX, nY, pState->mnWidth, pState->mnHeight, nPosSizeFlags ); + } + if( pState->mnMask & SAL_FRAMESTATE_MASK_STATE && ! isChild() ) + { + if( pState->mnState & SAL_FRAMESTATE_MAXIMIZED ) + gtk_window_maximize( GTK_WINDOW(m_pWindow) ); + else + gtk_window_unmaximize( GTK_WINDOW(m_pWindow) ); + /* #i42379# there is no rollup state in GDK; and rolled up windows are + * (probably depending on the WM) reported as iconified. If we iconify a + * window here that was e.g. a dialog, then it will be unmapped but still + * not be displayed in the task list, so it's an iconified window that + * the user cannot get out of this state. So do not set the iconified state + * on windows with a parent (that is transient frames) since these tend + * to not be represented in an icon task list. + */ + if( (pState->mnState & SAL_FRAMESTATE_MINIMIZED) + && ! m_pParent ) + gtk_window_iconify( GTK_WINDOW(m_pWindow) ); + else + gtk_window_deiconify( GTK_WINDOW(m_pWindow) ); + } +} + +BOOL GtkSalFrame::GetWindowState( SalFrameState* pState ) +{ + pState->mnState = SAL_FRAMESTATE_NORMAL; + pState->mnMask = SAL_FRAMESTATE_MASK_STATE; + // rollup ? gtk 2.2 does not seem to support the shaded state + if( (m_nState & GDK_WINDOW_STATE_ICONIFIED) ) + pState->mnState |= SAL_FRAMESTATE_MINIMIZED; + if( m_nState & GDK_WINDOW_STATE_MAXIMIZED ) + { + pState->mnState |= SAL_FRAMESTATE_MAXIMIZED; + pState->mnX = m_aRestorePosSize.Left(); + pState->mnY = m_aRestorePosSize.Top(); + pState->mnWidth = m_aRestorePosSize.GetWidth(); + pState->mnHeight = m_aRestorePosSize.GetHeight(); + pState->mnMaximizedX = maGeometry.nX; + pState->mnMaximizedY = maGeometry.nY; + pState->mnMaximizedWidth = maGeometry.nWidth; + pState->mnMaximizedHeight = maGeometry.nHeight; + pState->mnMask |= SAL_FRAMESTATE_MASK_MAXIMIZED_X | + SAL_FRAMESTATE_MASK_MAXIMIZED_Y | + SAL_FRAMESTATE_MASK_MAXIMIZED_WIDTH | + SAL_FRAMESTATE_MASK_MAXIMIZED_HEIGHT; + } + else + { + + pState->mnX = maGeometry.nX; + pState->mnY = maGeometry.nY; + pState->mnWidth = maGeometry.nWidth; + pState->mnHeight = maGeometry.nHeight; + } + pState->mnMask |= SAL_FRAMESTATE_MASK_X | + SAL_FRAMESTATE_MASK_Y | + SAL_FRAMESTATE_MASK_WIDTH | + SAL_FRAMESTATE_MASK_HEIGHT; + + return TRUE; +} + +void GtkSalFrame::moveToScreen( int nScreen ) +{ + if( isChild() ) + return; + + if( nScreen < 0 || nScreen >= gdk_display_get_n_screens( getGdkDisplay() ) ) + nScreen = m_nScreen; + if( nScreen == m_nScreen ) + return; + + GdkScreen* pScreen = gdk_display_get_screen( getGdkDisplay(), nScreen ); + if( pScreen ) + { + m_nScreen = nScreen; + gtk_window_set_screen( GTK_WINDOW(m_pWindow), pScreen ); + // realize the window, we need an XWindow id + gtk_widget_realize( m_pWindow ); + // update system data + GtkSalDisplay* pDisp = getDisplay(); + m_aSystemData.aWindow = GDK_WINDOW_XWINDOW(m_pWindow->window); + m_aSystemData.pVisual = pDisp->GetVisual( m_nScreen ).GetVisual(); + m_aSystemData.nScreen = nScreen; + m_aSystemData.nDepth = pDisp->GetVisual( m_nScreen ).GetDepth(); + m_aSystemData.aColormap = pDisp->GetColormap( m_nScreen ).GetXColormap(); + m_aSystemData.pAppContext = NULL; + m_aSystemData.aShellWindow = m_aSystemData.aWindow; + // update graphics if necessary + for( unsigned int i = 0; i < sizeof(m_aGraphics)/sizeof(m_aGraphics[0]); i++ ) + { + if( m_aGraphics[i].bInUse ) + m_aGraphics[i].pGraphics->SetDrawable( GDK_WINDOW_XWINDOW(m_pWindow->window), m_nScreen ); + } + updateScreenNumber(); + } + + if( m_pParent && m_pParent->m_nScreen != m_nScreen ) + SetParent( NULL ); + std::list< GtkSalFrame* > aChildren = m_aChildren; + for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->moveToScreen( m_nScreen ); + + // FIXME: SalObjects +} + +void GtkSalFrame::SetScreenNumber( unsigned int nNewScreen ) +{ + if( nNewScreen == maGeometry.nScreenNumber ) + return; + + if( m_pWindow && ! isChild() ) + { + GtkSalDisplay* pDisp = getDisplay(); + if( pDisp->IsXinerama() && pDisp->GetXineramaScreens().size() > 1 ) + { + if( nNewScreen >= pDisp->GetXineramaScreens().size() ) + return; + + Rectangle aOldScreenRect( pDisp->GetXineramaScreens()[maGeometry.nScreenNumber] ); + Rectangle aNewScreenRect( pDisp->GetXineramaScreens()[nNewScreen] ); + bool bVisible = GTK_WIDGET_MAPPED(m_pWindow); + if( bVisible ) + Show( FALSE ); + maGeometry.nX = aNewScreenRect.Left() + (maGeometry.nX - aOldScreenRect.Left()); + maGeometry.nY = aNewScreenRect.Top() + (maGeometry.nY - aOldScreenRect.Top()); + createNewWindow( None, false, m_nScreen ); + gtk_window_move( GTK_WINDOW(m_pWindow), maGeometry.nX, maGeometry.nY ); + if( bVisible ) + Show( TRUE ); + maGeometry.nScreenNumber = nNewScreen; + } + else if( sal_Int32(nNewScreen) < pDisp->GetScreenCount() ) + { + moveToScreen( (int)nNewScreen ); + maGeometry.nScreenNumber = nNewScreen; + gtk_window_move( GTK_WINDOW(m_pWindow), maGeometry.nX, maGeometry.nY ); + } + } +} + +void GtkSalFrame::ShowFullScreen( BOOL bFullScreen, sal_Int32 nScreen ) +{ + if( m_pWindow && ! isChild() ) + { + GtkSalDisplay* pDisp = getDisplay(); + // xinerama ? + if( pDisp->IsXinerama() && pDisp->GetXineramaScreens().size() > 1 ) + { + if( bFullScreen ) + { + m_aRestorePosSize = Rectangle( Point( maGeometry.nX, maGeometry.nY ), + Size( maGeometry.nWidth, maGeometry.nHeight ) ); + // workaround different window managers have different opinions about + // _NET_WM_STATE_FULLSCREEN (Metacity <-> KWin) + bool bVisible = GTK_WIDGET_MAPPED(m_pWindow); + if( bVisible ) + Show( FALSE ); + m_nStyle |= SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + createNewWindow( None, false, m_nScreen ); + Rectangle aNewPosSize; + if( nScreen < 0 || nScreen >= static_cast<int>(pDisp->GetXineramaScreens().size()) ) + aNewPosSize = Rectangle( Point( 0, 0 ), pDisp->GetScreenSize(m_nScreen) ); + else + aNewPosSize = pDisp->GetXineramaScreens()[ nScreen ]; + gtk_window_resize( GTK_WINDOW(m_pWindow), + maGeometry.nWidth = aNewPosSize.GetWidth(), + maGeometry.nHeight = aNewPosSize.GetHeight() ); + gtk_window_move( GTK_WINDOW(m_pWindow), + maGeometry.nX = aNewPosSize.Left(), + maGeometry.nY = aNewPosSize.Top() ); + if( bVisible ) + Show( TRUE ); + } + else + { + bool bVisible = GTK_WIDGET_MAPPED(m_pWindow); + if( bVisible ) + Show( FALSE ); + m_nStyle &= ~SAL_FRAME_STYLE_PARTIAL_FULLSCREEN; + createNewWindow( None, false, m_nScreen ); + if( ! m_aRestorePosSize.IsEmpty() ) + { + gtk_window_resize( GTK_WINDOW(m_pWindow), + maGeometry.nWidth = m_aRestorePosSize.GetWidth(), + maGeometry.nHeight = m_aRestorePosSize.GetHeight() ); + gtk_window_move( GTK_WINDOW(m_pWindow), + maGeometry.nX = m_aRestorePosSize.Left(), + maGeometry.nY = m_aRestorePosSize.Top() ); + m_aRestorePosSize = Rectangle(); + } + if( bVisible ) + Show( TRUE ); + } + } + else + { + if( bFullScreen ) + { + if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), TRUE ); + gtk_window_fullscreen( GTK_WINDOW(m_pWindow) ); + moveToScreen( nScreen ); + Size aScreenSize = pDisp->GetScreenSize( m_nScreen ); + maGeometry.nX = 0; + maGeometry.nY = 0; + maGeometry.nWidth = aScreenSize.Width(); + maGeometry.nHeight = aScreenSize.Height(); + } + else + { + gtk_window_unfullscreen( GTK_WINDOW(m_pWindow) ); + if( !(m_nStyle & SAL_FRAME_STYLE_SIZEABLE) ) + gtk_window_set_resizable( GTK_WINDOW(m_pWindow), FALSE ); + moveToScreen( nScreen ); + } + } + m_bDefaultPos = m_bDefaultSize = false; + updateScreenNumber(); + CallCallback( SALEVENT_MOVERESIZE, NULL ); + } + m_bFullscreen = bFullScreen; +} + +/* definitions from xautolock.c (pl15) */ +#define XAUTOLOCK_DISABLE 1 +#define XAUTOLOCK_ENABLE 2 + +void GtkSalFrame::setAutoLock( bool bLock ) +{ + if( isChild() ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + GdkWindow *pRootWin = gdk_screen_get_root_window( pScreen ); + + Atom nAtom = XInternAtom( GDK_DISPLAY_XDISPLAY( pDisplay ), + "XAUTOLOCK_MESSAGE", False ); + + int nMessage = bLock ? XAUTOLOCK_ENABLE : XAUTOLOCK_DISABLE; + + XChangeProperty( GDK_DISPLAY_XDISPLAY( pDisplay ), + GDK_WINDOW_XID( pRootWin ), + nAtom, XA_INTEGER, + 8, PropModeReplace, + (unsigned char*)&nMessage, + sizeof( nMessage ) ); +} + +#ifdef ENABLE_DBUS +/** cookie is returned as an unsigned integer */ +static guint +dbus_inhibit_gsm (const gchar *appname, + const gchar *reason, + guint xid) +{ + gboolean res; + guint cookie; + GError *error = NULL; + DBusGProxy *proxy = NULL; + DBusGConnection *session_connection = NULL; + + /* get the DBUS session connection */ + session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error != NULL) { + g_warning ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return -1; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_warning ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return -1; + } + + res = dbus_g_proxy_call (proxy, + "Inhibit", &error, + G_TYPE_STRING, appname, + G_TYPE_UINT, xid, + G_TYPE_STRING, reason, + G_TYPE_UINT, 8, //Inhibit the session being marked as idle + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + cookie = -1; + g_warning ("Inhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_warning ("Inhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + + g_object_unref (G_OBJECT (proxy)); + return cookie; +} + +static void +dbus_uninhibit_gsm (guint cookie) +{ + gboolean res; + GError *error = NULL; + DBusGProxy *proxy = NULL; + DBusGConnection *session_connection = NULL; + + if (cookie == guint(-1)) { + g_warning ("Invalid cookie"); + return; + } + + /* get the DBUS session connection */ + session_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error) { + g_warning ("DBUS cannot connect : %s", error->message); + g_error_free (error); + return; + } + + /* get the proxy with gnome-session-manager */ + proxy = dbus_g_proxy_new_for_name (session_connection, + GSM_DBUS_SERVICE, + GSM_DBUS_PATH, + GSM_DBUS_INTERFACE); + if (proxy == NULL) { + g_warning ("Could not get DBUS proxy: %s", GSM_DBUS_SERVICE); + return; + } + + res = dbus_g_proxy_call (proxy, + "Uninhibit", + &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + + /* check the return value */ + if (! res) { + g_warning ("Uninhibit method failed"); + } + + /* check the error value */ + if (error != NULL) { + g_warning ("Uninhibit problem : %s", error->message); + g_error_free (error); + cookie = -1; + } + g_object_unref (G_OBJECT (proxy)); +} +#endif + +void GtkSalFrame::StartPresentation( BOOL bStart ) +{ + Display *pDisplay = GDK_DISPLAY_XDISPLAY( getGdkDisplay() ); + + setAutoLock( !bStart ); + + int nTimeout, nInterval, bPreferBlanking, bAllowExposures; + + XGetScreenSaver( pDisplay, &nTimeout, &nInterval, + &bPreferBlanking, &bAllowExposures ); + if( bStart ) + { + if ( nTimeout ) + { + m_nSavedScreenSaverTimeout = nTimeout; + XResetScreenSaver( pDisplay ); + XSetScreenSaver( pDisplay, 0, nInterval, + bPreferBlanking, bAllowExposures ); + } +#ifdef ENABLE_DBUS + m_nGSMCookie = dbus_inhibit_gsm(g_get_application_name(), "presentation", + GDK_WINDOW_XID(m_pWindow->window)); +#endif + } + else + { + if( m_nSavedScreenSaverTimeout ) + XSetScreenSaver( pDisplay, m_nSavedScreenSaverTimeout, + nInterval, bPreferBlanking, + bAllowExposures ); + m_nSavedScreenSaverTimeout = 0; +#ifdef ENABLE_DBUS + dbus_uninhibit_gsm(m_nGSMCookie); +#endif + } +} + +void GtkSalFrame::SetAlwaysOnTop( BOOL /*bOnTop*/ ) +{ +} + +void GtkSalFrame::ToTop( USHORT nFlags ) +{ + if( m_pWindow ) + { + if( isChild( false, true ) ) + gtk_widget_grab_focus( m_pWindow ); + else if( GTK_WIDGET_MAPPED( m_pWindow ) ) + { + if( ! (nFlags & SAL_FRAME_TOTOP_GRABFOCUS_ONLY) ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + else + { + // gdk_window_focus( m_pWindow->window, gdk_x11_get_server_time(GTK_WIDGET (m_pWindow)->window) ); + /* #i99360# ugly workaround an X11 library bug */ + guint32 nUserTime= getDisplay()->GetLastUserEventTime( true ); + gdk_window_focus( m_pWindow->window, nUserTime ); + } + /* need to do an XSetInputFocus here because + * gdk_window_focus will ask a EWMH compliant WM to put the focus + * to our window - which it of course won't since our input hint + * is set to false. + */ + if( (m_nStyle & (SAL_FRAME_STYLE_OWNERDRAWDECORATION|SAL_FRAME_STYLE_FLOAT_FOCUSABLE)) ) + { + // sad but true: this can cause an XError, we need to catch that + // to do this we need to synchronize with the XServer + getDisplay()->GetXLib()->PushXErrorLevel( true ); + XSetInputFocus( getDisplay()->GetDisplay(), GDK_WINDOW_XWINDOW( m_pWindow->window ), RevertToParent, CurrentTime ); + XSync( getDisplay()->GetDisplay(), False ); + getDisplay()->GetXLib()->PopXErrorLevel(); + } + } + else + { + if( nFlags & SAL_FRAME_TOTOP_RESTOREWHENMIN ) + gtk_window_present( GTK_WINDOW(m_pWindow) ); + } + } +} + +void GtkSalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + if( m_pWindow && ePointerStyle != m_ePointerStyle ) + { + m_ePointerStyle = ePointerStyle; + GdkCursor *pCursor = getDisplay()->getCursor( ePointerStyle ); + gdk_window_set_cursor( m_pWindow->window, pCursor ); + m_pCurrentCursor = pCursor; + + // #i80791# use grabPointer the same way as CaptureMouse, respective float grab + if( getDisplay()->MouseCaptured( this ) ) + grabPointer( TRUE, FALSE ); + else if( m_nFloats > 0 ) + grabPointer( TRUE, TRUE ); + } +} + +void GtkSalFrame::grabPointer( BOOL bGrab, BOOL bOwnerEvents ) +{ + if( m_pWindow ) + { + if( bGrab ) + { + bool bUseGdkGrab = true; + if( getDisplay()->getHaveSystemChildFrame() ) + { + const std::list< SalFrame* >& rFrames = getDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + const GtkSalFrame* pFrame = static_cast< const GtkSalFrame* >(*it); + if( pFrame->m_bWindowIsGtkPlug ) + { + bUseGdkGrab = false; + break; + } + } + } + if( bUseGdkGrab ) + { + const int nMask = ( GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK ); + + gdk_pointer_grab( m_pWindow->window, bOwnerEvents, + (GdkEventMask) nMask, NULL, m_pCurrentCursor, + GDK_CURRENT_TIME ); + } + else + { + // FIXME: for some unknown reason gdk_pointer_grab does not + // really produce owner events for GtkPlug windows + // the cause is yet unknown + // + // this is of course a bad hack, especially as we cannot + // set the right cursor this way + XGrabPointer( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW( m_pWindow->window), + bOwnerEvents, + PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, + GrabModeAsync, + None, + None, + CurrentTime + ); + + } + } + else + { + // Two GdkDisplays may be open + gdk_display_pointer_ungrab( getGdkDisplay(), GDK_CURRENT_TIME); + } + } +} + +void GtkSalFrame::CaptureMouse( BOOL bCapture ) +{ + getDisplay()->CaptureMouse( bCapture ? this : NULL ); +} + +void GtkSalFrame::SetPointerPos( long nX, long nY ) +{ + GtkSalFrame* pFrame = this; + while( pFrame && pFrame->isChild( false, true ) ) + pFrame = pFrame->m_pParent; + if( ! pFrame ) + return; + + GdkScreen *pScreen = gtk_window_get_screen( GTK_WINDOW(pFrame->m_pWindow) ); + GdkDisplay *pDisplay = gdk_screen_get_display( pScreen ); + + /* #87921# when the application tries to center the mouse in the dialog the + * window isn't mapped already. So use coordinates relative to the root window. + */ + unsigned int nWindowLeft = maGeometry.nX + nX; + unsigned int nWindowTop = maGeometry.nY + nY; + + XWarpPointer( GDK_DISPLAY_XDISPLAY (pDisplay), None, + GDK_WINDOW_XID (gdk_screen_get_root_window( pScreen ) ), + 0, 0, 0, 0, nWindowLeft, nWindowTop); + // #i38648# ask for the next motion hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( pFrame->m_pWindow->window, &x, &y, &mask ); +} + +void GtkSalFrame::Flush() +{ +#ifdef HAVE_A_RECENT_GTK + gdk_display_flush( getGdkDisplay() ); +#else + XFlush (GDK_DISPLAY_XDISPLAY (getGdkDisplay())); +#endif +} + +void GtkSalFrame::Sync() +{ + gdk_display_sync( getGdkDisplay() ); +} + +String GtkSalFrame::GetSymbolKeyName( const String&, USHORT nKeyCode ) +{ + return getDisplay()->GetKeyName( nKeyCode ); +} + +String GtkSalFrame::GetKeyName( USHORT nKeyCode ) +{ + return getDisplay()->GetKeyName( nKeyCode ); +} + +GdkDisplay *GtkSalFrame::getGdkDisplay() +{ + return static_cast<GtkSalDisplay*>(GetX11SalData()->GetDisplay())->GetGdkDisplay(); +} + +GtkSalDisplay *GtkSalFrame::getDisplay() +{ + return static_cast<GtkSalDisplay*>(GetX11SalData()->GetDisplay()); +} + +SalFrame::SalPointerState GtkSalFrame::GetPointerState() +{ + SalPointerState aState; + GdkScreen* pScreen; + gint x, y; + GdkModifierType aMask; + gdk_display_get_pointer( getGdkDisplay(), &pScreen, &x, &y, &aMask ); + aState.maPos = Point( x - maGeometry.nX, y - maGeometry.nY ); + aState.mnState = GetMouseModCode( aMask ); + return aState; +} + +void GtkSalFrame::SetInputContext( SalInputContext* pContext ) +{ + if( ! pContext ) + return; + + if( ! (pContext->mnOptions & SAL_INPUTCONTEXT_TEXT) ) + return; + + // create a new im context + if( ! m_pIMHandler ) + m_pIMHandler = new IMHandler( this ); + m_pIMHandler->setInputContext( pContext ); +} + +void GtkSalFrame::EndExtTextInput( USHORT nFlags ) +{ + if( m_pIMHandler ) + m_pIMHandler->endExtTextInput( nFlags ); +} + +BOOL GtkSalFrame::MapUnicodeToKeyCode( sal_Unicode , LanguageType , KeyCode& ) +{ + // not supported yet + return FALSE; +} + +LanguageType GtkSalFrame::GetInputLanguage() +{ + return LANGUAGE_DONTKNOW; +} + +SalBitmap* GtkSalFrame::SnapShot() +{ + if( !m_pWindow ) + return NULL; + + X11SalBitmap *pBmp = new X11SalBitmap; + GdkWindow *pWin = m_pWindow->window; + if( pBmp->SnapShot( GDK_DISPLAY_XDISPLAY( getGdkDisplay() ), + GDK_WINDOW_XID( pWin ) ) ) + return pBmp; + else + delete pBmp; + + return NULL; +} + +void GtkSalFrame::UpdateSettings( AllSettings& rSettings ) +{ + if( ! m_pWindow ) + return; + + GtkSalGraphics* pGraphics = static_cast<GtkSalGraphics*>(m_aGraphics[0].pGraphics); + bool bFreeGraphics = false; + if( ! pGraphics ) + { + pGraphics = static_cast<GtkSalGraphics*>(GetGraphics()); + bFreeGraphics = true; + } + + pGraphics->updateSettings( rSettings ); + + if( bFreeGraphics ) + ReleaseGraphics( pGraphics ); +} + +void GtkSalFrame::Beep( SoundType eType ) +{ + switch( eType ) + { + case SOUND_DEFAULT: + case SOUND_ERROR: + gdk_display_beep( getGdkDisplay() ); + break; + default: + break; + } +} + +const SystemEnvData* GtkSalFrame::GetSystemData() const +{ + return &m_aSystemData; +} + +void GtkSalFrame::SetParent( SalFrame* pNewParent ) +{ + if( m_pParent ) + m_pParent->m_aChildren.remove( this ); + m_pParent = static_cast<GtkSalFrame*>(pNewParent); + if( m_pParent ) + m_pParent->m_aChildren.push_back( this ); + if( ! isChild() ) + gtk_window_set_transient_for( GTK_WINDOW(m_pWindow), + (m_pParent && ! m_pParent->isChild(true,false)) ? GTK_WINDOW(m_pParent->m_pWindow) : NULL + ); +} + +void GtkSalFrame::createNewWindow( XLIB_Window aNewParent, bool bXEmbed, int nScreen ) +{ + bool bWasVisible = GTK_WIDGET_MAPPED(m_pWindow); + if( bWasVisible ) + Show( FALSE ); + + if( nScreen < 0 || nScreen >= getDisplay()->GetScreenCount() ) + nScreen = m_nScreen; + + SystemParentData aParentData; + aParentData.aWindow = aNewParent; + aParentData.bXEmbedSupport = bXEmbed; + if( aNewParent == None ) + { + aNewParent = getDisplay()->GetRootWindow(nScreen); + aParentData.aWindow = None; + aParentData.bXEmbedSupport = false; + } + else + { + // is new parent a root window ? + Display* pDisp = getDisplay()->GetDisplay(); + int nScreens = getDisplay()->GetScreenCount(); + for( int i = 0; i < nScreens; i++ ) + { + if( aNewParent == RootWindow( pDisp, i ) ) + { + nScreen = i; + aParentData.aWindow = None; + aParentData.bXEmbedSupport = false; + break; + } + } + } + + // free xrender resources + for( unsigned int i = 0; i < sizeof(m_aGraphics)/sizeof(m_aGraphics[0]); i++ ) + if( m_aGraphics[i].bInUse ) + m_aGraphics[i].pGraphics->SetDrawable( None, m_nScreen ); + + // first deinit frame + if( m_pIMHandler ) + { + delete m_pIMHandler; + m_pIMHandler = NULL; + } + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + if( m_pFixedContainer ) + gtk_widget_destroy( GTK_WIDGET(m_pFixedContainer) ); + if( m_pWindow ) + gtk_widget_destroy( m_pWindow ); + if( m_pForeignParent ) + g_object_unref( G_OBJECT(m_pForeignParent) ); + if( m_pForeignTopLevel ) + g_object_unref( G_OBJECT(m_pForeignTopLevel) ); + + // init new window + m_bDefaultPos = m_bDefaultSize = false; + if( aParentData.aWindow != None ) + { + m_nStyle |= SAL_FRAME_STYLE_PLUG; + Init( &aParentData ); + } + else + { + m_nStyle &= ~SAL_FRAME_STYLE_PLUG; + Init( (m_pParent && m_pParent->m_nScreen == m_nScreen) ? m_pParent : NULL, m_nStyle ); + } + + // update graphics + for( unsigned int i = 0; i < sizeof(m_aGraphics)/sizeof(m_aGraphics[0]); i++ ) + { + if( m_aGraphics[i].bInUse ) + { + m_aGraphics[i].pGraphics->SetDrawable( GDK_WINDOW_XWINDOW(m_pWindow->window), m_nScreen ); + m_aGraphics[i].pGraphics->SetWindow( m_pWindow ); + } + } + + if( m_aTitle.Len() ) + SetTitle( m_aTitle ); + + if( bWasVisible ) + Show( TRUE ); + + std::list< GtkSalFrame* > aChildren = m_aChildren; + m_aChildren.clear(); + for( std::list< GtkSalFrame* >::iterator it = aChildren.begin(); it != aChildren.end(); ++it ) + (*it)->createNewWindow( None, false, m_nScreen ); + + // FIXME: SalObjects +} + +bool GtkSalFrame::SetPluginParent( SystemParentData* pSysParent ) +{ + if( pSysParent ) // this may be the first system child frame now + getDisplay()->setHaveSystemChildFrame(); + createNewWindow( pSysParent->aWindow, (pSysParent->nSize > sizeof(long)) ? pSysParent->bXEmbedSupport : false, m_nScreen ); + return true; +} + +void GtkSalFrame::ResetClipRegion() +{ + if( m_pWindow ) + gdk_window_shape_combine_region( m_pWindow->window, NULL, 0, 0 ); +} + +void GtkSalFrame::BeginSetClipRegion( ULONG ) +{ + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + m_pRegion = gdk_region_new(); +} + +void GtkSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + if( m_pRegion ) + { + GdkRectangle aRect; + aRect.x = nX; + aRect.y = nY; + aRect.width = nWidth; + aRect.height = nHeight; + + gdk_region_union_with_rect( m_pRegion, &aRect ); + } +} + +void GtkSalFrame::EndSetClipRegion() +{ + if( m_pWindow && m_pRegion ) + gdk_window_shape_combine_region( m_pWindow->window, m_pRegion, 0, 0 ); +} + +bool GtkSalFrame::Dispatch( const XEvent* pEvent ) +{ + bool bContinueDispatch = true; + + if( pEvent->type == PropertyNotify ) + { + vcl_sal::WMAdaptor* pAdaptor = getDisplay()->getWMAdaptor(); + Atom nDesktopAtom = pAdaptor->getAtom( vcl_sal::WMAdaptor::NET_WM_DESKTOP ); + if( pEvent->xproperty.atom == nDesktopAtom && + pEvent->xproperty.state == PropertyNewValue ) + { + m_nWorkArea = pAdaptor->getWindowWorkArea( GDK_WINDOW_XWINDOW( m_pWindow->window) ); + } + } + else if( pEvent->type == ConfigureNotify ) + { + if( m_pForeignParent && pEvent->xconfigure.window == m_aForeignParentWindow ) + { + bContinueDispatch = false; + gtk_window_resize( GTK_WINDOW(m_pWindow), pEvent->xconfigure.width, pEvent->xconfigure.height ); + if( ( sal::static_int_cast< int >(maGeometry.nWidth) != + pEvent->xconfigure.width ) || + ( sal::static_int_cast< int >(maGeometry.nHeight) != + pEvent->xconfigure.height ) ) + { + maGeometry.nWidth = pEvent->xconfigure.width; + maGeometry.nHeight = pEvent->xconfigure.height; + setMinMaxSize(); + getDisplay()->SendInternalEvent( this, NULL, SALEVENT_RESIZE ); + } + } + else if( m_pForeignTopLevel && pEvent->xconfigure.window == m_aForeignTopLevelWindow ) + { + bContinueDispatch = false; + // update position + int x = 0, y = 0; + XLIB_Window aChild; + XTranslateCoordinates( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW( m_pWindow->window), + getDisplay()->GetRootWindow( getDisplay()->GetDefaultScreenNumber() ), + 0, 0, + &x, &y, + &aChild ); + if( x != maGeometry.nX || y != maGeometry.nY ) + { + maGeometry.nX = x; + maGeometry.nY = y; + getDisplay()->SendInternalEvent( this, NULL, SALEVENT_MOVE ); + } + } + } + else if( pEvent->type == ClientMessage && + pEvent->xclient.message_type == getDisplay()->getWMAdaptor()->getAtom( vcl_sal::WMAdaptor::XEMBED ) && + pEvent->xclient.window == GDK_WINDOW_XWINDOW(m_pWindow->window) && + m_bWindowIsGtkPlug + ) + { + // FIXME: this should not be necessary, GtkPlug should do this + // transparently for us + if( pEvent->xclient.data.l[1] == 1 || // XEMBED_WINDOW_ACTIVATE + pEvent->xclient.data.l[1] == 2 // XEMBED_WINDOW_DEACTIVATE + ) + { + GdkEventFocus aEvent; + aEvent.type = GDK_FOCUS_CHANGE; + aEvent.window = m_pWindow->window; + aEvent.send_event = TRUE; + aEvent.in = (pEvent->xclient.data.l[1] == 1); + signalFocus( m_pWindow, &aEvent, this ); + } + } + + return bContinueDispatch; +} + +void GtkSalFrame::SetBackgroundBitmap( SalBitmap* pBitmap ) +{ + if( m_hBackgroundPixmap ) + { + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + None ); + XFreePixmap( getDisplay()->GetDisplay(), m_hBackgroundPixmap ); + m_hBackgroundPixmap = None; + } + if( pBitmap ) + { + X11SalBitmap* pBM = static_cast<X11SalBitmap*>(pBitmap); + Size aSize = pBM->GetSize(); + if( aSize.Width() && aSize.Height() ) + { + m_hBackgroundPixmap = + XCreatePixmap( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + aSize.Width(), + aSize.Height(), + getDisplay()->GetVisual(m_nScreen).GetDepth() ); + if( m_hBackgroundPixmap ) + { + SalTwoRect aTwoRect; + aTwoRect.mnSrcX = aTwoRect.mnSrcY = aTwoRect.mnDestX = aTwoRect.mnDestY = 0; + aTwoRect.mnSrcWidth = aTwoRect.mnDestWidth = aSize.Width(); + aTwoRect.mnSrcHeight = aTwoRect.mnDestHeight = aSize.Height(); + pBM->ImplDraw( m_hBackgroundPixmap, + m_nScreen, + getDisplay()->GetVisual(m_nScreen).GetDepth(), + aTwoRect, + getDisplay()->GetCopyGC(m_nScreen) ); + XSetWindowBackgroundPixmap( getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(m_pWindow->window), + m_hBackgroundPixmap ); + } + } + } +} + +gboolean GtkSalFrame::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + SalMouseEvent aEvent; + USHORT nEventType = 0; + switch( pEvent->type ) + { + case GDK_BUTTON_PRESS: + nEventType = SALEVENT_MOUSEBUTTONDOWN; + break; + case GDK_BUTTON_RELEASE: + nEventType = SALEVENT_MOUSEBUTTONUP; + break; + default: + return FALSE; + } + switch( pEvent->button ) + { + case 1: aEvent.mnButton = MOUSE_LEFT; break; + case 2: aEvent.mnButton = MOUSE_MIDDLE; break; + case 3: aEvent.mnButton = MOUSE_RIGHT; break; + default: return FALSE; + } + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + + bool bClosePopups = false; + if( pEvent->type == GDK_BUTTON_PRESS && + (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) == 0 + ) + { + if( m_nFloats > 0 ) + { + // close popups if user clicks outside our application + gint x, y; + bClosePopups = (gdk_display_get_window_at_pointer( pThis->getGdkDisplay(), &x, &y ) == NULL); + } + /* #i30306# release implicit pointer grab if no popups are open; else + * Drag cannot grab the pointer and will fail. + */ + if( m_nFloats < 1 || bClosePopups ) + gdk_display_pointer_ungrab( pThis->getGdkDisplay(), GDK_CURRENT_TIME ); + } + + GTK_YIELD_GRAB(); + + if( pThis->m_bWindowIsGtkPlug && + pEvent->type == GDK_BUTTON_PRESS && + pEvent->button == 1 ) + { + pThis->askForXEmbedFocus( pEvent->time ); + } + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( nEventType, &aEvent ); + + if( ! aDel.isDeleted() ) + { + if( bClosePopups ) + { + ImplSVData* pSVData = ImplGetSVData(); + if ( pSVData->maWinData.mpFirstFloat ) + { + static const char* pEnv = getenv( "SAL_FLOATWIN_NOAPPFOCUSCLOSE" ); + if ( !(pSVData->maWinData.mpFirstFloat->GetPopupModeFlags() & FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE) && !(pEnv && *pEnv) ) + pSVData->maWinData.mpFirstFloat->EndPopupMode( FLOATWIN_POPUPMODEEND_CANCEL | FLOATWIN_POPUPMODEEND_CLOSEALL ); + } + } + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + } + } + + return FALSE; +} + +gboolean GtkSalFrame::signalScroll( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + GdkEventScroll* pSEvent = (GdkEventScroll*)pEvent; + + static ULONG nLines = 0; + if( ! nLines ) + { + char* pEnv = getenv( "SAL_WHEELLINES" ); + nLines = pEnv ? atoi( pEnv ) : 3; + if( nLines > 10 ) + nLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; + } + + bool bNeg = (pSEvent->direction == GDK_SCROLL_DOWN || pSEvent->direction == GDK_SCROLL_RIGHT ); + SalWheelMouseEvent aEvent; + aEvent.mnTime = pSEvent->time; + aEvent.mnX = (ULONG)pSEvent->x; + aEvent.mnY = (ULONG)pSEvent->y; + aEvent.mnDelta = bNeg ? -120 : 120; + aEvent.mnNotchDelta = bNeg ? -1 : 1; + aEvent.mnScrollLines = nLines; + aEvent.mnCode = GetMouseModCode( pSEvent->state ); + aEvent.mbHorz = (pSEvent->direction == GDK_SCROLL_LEFT || pSEvent->direction == GDK_SCROLL_RIGHT); + + GTK_YIELD_GRAB(); + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + pThis->CallCallback( SALEVENT_WHEELMOUSE, &aEvent ); + + return FALSE; +} + +gboolean GtkSalFrame::signalMotion( GtkWidget*, GdkEventMotion* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + + GTK_YIELD_GRAB(); + + // --- RTL --- (mirror mouse pos) + if( Application::GetSettings().GetLayoutRTL() ) + aEvent.mnX = pThis->maGeometry.nWidth-1-aEvent.mnX; + + vcl::DeletionListener aDel( pThis ); + + pThis->CallCallback( SALEVENT_MOUSEMOVE, &aEvent ); + + if( ! aDel.isDeleted() ) + { + int frame_x = (int)(pEvent->x_root - pEvent->x); + int frame_y = (int)(pEvent->y_root - pEvent->y); + if( frame_x != pThis->maGeometry.nX || frame_y != pThis->maGeometry.nY ) + { + pThis->maGeometry.nX = frame_x; + pThis->maGeometry.nY = frame_y; + pThis->CallCallback( SALEVENT_MOVE, NULL ); + } + + if( ! aDel.isDeleted() ) + { + // ask for the next hint + gint x, y; + GdkModifierType mask; + gdk_window_get_pointer( GTK_WIDGET(pThis->m_pWindow)->window, &x, &y, &mask ); + } + } + + return TRUE; +} + +gboolean GtkSalFrame::signalCrossing( GtkWidget*, GdkEventCrossing* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + SalMouseEvent aEvent; + aEvent.mnTime = pEvent->time; + aEvent.mnX = (long)pEvent->x_root - pThis->maGeometry.nX; + aEvent.mnY = (long)pEvent->y_root - pThis->maGeometry.nY; + aEvent.mnCode = GetMouseModCode( pEvent->state ); + aEvent.mnButton = 0; + + GTK_YIELD_GRAB(); + pThis->CallCallback( (pEvent->type == GDK_ENTER_NOTIFY) ? SALEVENT_MOUSEMOVE : SALEVENT_MOUSELEAVE, &aEvent ); + + return TRUE; +} + + +gboolean GtkSalFrame::signalExpose( GtkWidget*, GdkEventExpose* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + struct SalPaintEvent aEvent( pEvent->area.x, pEvent->area.y, pEvent->area.width, pEvent->area.height ); + + GTK_YIELD_GRAB(); + pThis->CallCallback( SALEVENT_PAINT, &aEvent ); + + return FALSE; +} + +gboolean GtkSalFrame::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + GTK_YIELD_GRAB(); + + // check if printers have changed (analogous to salframe focus handler) + vcl_sal::PrinterUpdate::update(); + + if( !pEvent->in ) + { + pThis->m_nKeyModifiers = 0; + pThis->m_bSingleAltPress = false; + pThis->m_bSendModChangeOnRelease = false; + } + + if( pThis->m_pIMHandler ) + pThis->m_pIMHandler->focusChanged( pEvent->in ); + + // ask for changed printers like generic implementation + if( pEvent->in ) + if( static_cast< X11SalInstance* >(GetSalData()->m_pInstance)->isPrinterInit() ) + vcl_sal::PrinterUpdate::update(); + + // FIXME: find out who the hell steals the focus from our frame + // while we have the pointer grabbed, this should not come from + // the window manager. Is this an event that was still queued ? + // The focus does not seem to get set inside our process + // + // in the meantime do not propagate focus get/lose if floats are open + if( m_nFloats == 0 ) + pThis->CallCallback( pEvent->in ? SALEVENT_GETFOCUS : SALEVENT_LOSEFOCUS, NULL ); + + return FALSE; +} + +gboolean GtkSalFrame::signalMap( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + GTK_YIELD_GRAB(); + + if( ImplGetSVData()->mbIsTestTool ) + { + /* #i76541# testtool needs the focus to be in a new document + * however e.g. metacity does not necessarily put the focus into + * a newly shown window. An extra little hint seems to help here. + * however we don't want to interfere with the normal user experience + * so this is done when running in testtool only + */ + if( ! pThis->m_pParent && (pThis->m_nStyle & SAL_FRAME_STYLE_MOVEABLE) != 0 ) + XSetInputFocus( pThis->getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW( GTK_WIDGET(pThis->m_pWindow)->window), + RevertToParent, CurrentTime ); + } + + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + + return FALSE; +} + +gboolean GtkSalFrame::signalUnmap( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + GTK_YIELD_GRAB(); + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + + return FALSE; +} + +gboolean GtkSalFrame::signalConfigure( GtkWidget*, GdkEventConfigure* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + bool bMoved = false, bSized = false; + int x = pEvent->x, y = pEvent->y; + + /* HACK: during sizing/moving a toolbar pThis->maGeometry is actually + * already exact; even worse: due to the asynchronicity of configure + * events the borderwindow which would evaluate this event + * would size/move based on wrong data if we would actually evaluate + * this event. So let's swallow it; this is also a performance + * improvement as one can omit the synchronous XTranslateCoordinates + * call below. + */ + if( (pThis->m_nStyle & SAL_FRAME_STYLE_OWNERDRAWDECORATION) && + pThis->getDisplay()->GetCaptureFrame() == pThis ) + return FALSE; + + + // in child case the coordinates are not root coordinates, + // need to transform + + /* #i31785# sadly one cannot really trust the x,y members of the event; + * they are e.g. not set correctly on maximize/demaximize; this rather + * sounds like a bug in gtk we have to workaround. + */ + XLIB_Window aChild; + XTranslateCoordinates( pThis->getDisplay()->GetDisplay(), + GDK_WINDOW_XWINDOW(GTK_WIDGET(pThis->m_pWindow)->window), + pThis->getDisplay()->GetRootWindow( pThis->getDisplay()->GetDefaultScreenNumber() ), + 0, 0, + &x, &y, + &aChild ); + + if( x != pThis->maGeometry.nX || y != pThis->maGeometry.nY ) + { + bMoved = true; + pThis->maGeometry.nX = x; + pThis->maGeometry.nY = y; + } + /* #i86302# + * for non sizeable windows we set the min and max hint for the window manager to + * achieve correct sizing. However this is asynchronous and e.g. on Compiz + * it sometimes happens that the window gets resized to another size (some default) + * if we update the size here, subsequent setMinMaxSize will use this wrong size + * - which is not good since the window manager will now size the window back to this + * wrong size at some point. + */ + if( (pThis->m_nStyle & (SAL_FRAME_STYLE_SIZEABLE | SAL_FRAME_STYLE_PLUG)) == SAL_FRAME_STYLE_SIZEABLE ) + { + if( pEvent->width != (int)pThis->maGeometry.nWidth || pEvent->height != (int)pThis->maGeometry.nHeight ) + { + bSized = true; + pThis->maGeometry.nWidth = pEvent->width; + pThis->maGeometry.nHeight = pEvent->height; + } + } + + // update decoration hints + if( ! (pThis->m_nStyle & SAL_FRAME_STYLE_PLUG) ) + { + GdkRectangle aRect; + gdk_window_get_frame_extents( GTK_WIDGET(pThis->m_pWindow)->window, &aRect ); + pThis->maGeometry.nTopDecoration = y - aRect.y; + pThis->maGeometry.nBottomDecoration = aRect.y + aRect.height - y - pEvent->height; + pThis->maGeometry.nLeftDecoration = x - aRect.x; + pThis->maGeometry.nRightDecoration = aRect.x + aRect.width - x - pEvent->width; + } + else + { + pThis->maGeometry.nTopDecoration = + pThis->maGeometry.nBottomDecoration = + pThis->maGeometry.nLeftDecoration = + pThis->maGeometry.nRightDecoration = 0; + } + + GTK_YIELD_GRAB(); + pThis->updateScreenNumber(); + if( bMoved && bSized ) + pThis->CallCallback( SALEVENT_MOVERESIZE, NULL ); + else if( bMoved ) + pThis->CallCallback( SALEVENT_MOVE, NULL ); + else if( bSized ) + pThis->CallCallback( SALEVENT_RESIZE, NULL ); + + return FALSE; +} + +gboolean GtkSalFrame::signalKey( GtkWidget*, GdkEventKey* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + vcl::DeletionListener aDel( pThis ); + + if( pThis->m_pIMHandler ) + { + if( pThis->m_pIMHandler->handleKeyEvent( pEvent ) ) + { + pThis->m_bSingleAltPress = false; + return TRUE; + } + } + GTK_YIELD_GRAB(); + + // handle modifiers + if( pEvent->keyval == GDK_Shift_L || pEvent->keyval == GDK_Shift_R || + pEvent->keyval == GDK_Control_L || pEvent->keyval == GDK_Control_R || + pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R || + pEvent->keyval == GDK_Meta_L || pEvent->keyval == GDK_Meta_R || + pEvent->keyval == GDK_Super_L || pEvent->keyval == GDK_Super_R ) + { + SalKeyModEvent aModEvt; + + USHORT nModCode = GetKeyModCode( pEvent->state ); + + aModEvt.mnModKeyCode = 0; // emit no MODKEYCHANGE events + if( pEvent->type == GDK_KEY_PRESS && !pThis->m_nKeyModifiers ) + pThis->m_bSendModChangeOnRelease = true; + + else if( pEvent->type == GDK_KEY_RELEASE && + pThis->m_bSendModChangeOnRelease ) + { + aModEvt.mnModKeyCode = pThis->m_nKeyModifiers; + pThis->m_nKeyModifiers = 0; + } + + USHORT nExtModMask = 0; + USHORT nModMask = 0; + // pressing just the ctrl key leads to a keysym of XK_Control but + // the event state does not contain ControlMask. In the release + // event its the other way round: it does contain the Control mask. + // The modifier mode therefore has to be adapted manually. + switch( pEvent->keyval ) + { + case GDK_Control_L: + nExtModMask = MODKEY_LMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Control_R: + nExtModMask = MODKEY_RMOD1; + nModMask = KEY_MOD1; + break; + case GDK_Alt_L: + nExtModMask = MODKEY_LMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Alt_R: + nExtModMask = MODKEY_RMOD2; + nModMask = KEY_MOD2; + break; + case GDK_Shift_L: + nExtModMask = MODKEY_LSHIFT; + nModMask = KEY_SHIFT; + break; + case GDK_Shift_R: + nExtModMask = MODKEY_RSHIFT; + nModMask = KEY_SHIFT; + break; + // Map Meta/Super to MOD3 modifier on all Unix systems + // except Mac OS X + case GDK_Meta_L: + case GDK_Super_L: + nExtModMask = MODKEY_LMOD3; + nModMask = KEY_MOD3; + break; + case GDK_Meta_R: + case GDK_Super_R: + nExtModMask = MODKEY_RMOD3; + nModMask = KEY_MOD3; + break; + } + if( pEvent->type == GDK_KEY_RELEASE ) + { + nModCode &= ~nModMask; + pThis->m_nKeyModifiers &= ~nExtModMask; + } + else + { + nModCode |= nModMask; + pThis->m_nKeyModifiers |= nExtModMask; + } + + aModEvt.mnCode = nModCode; + aModEvt.mnTime = pEvent->time; + + pThis->CallCallback( SALEVENT_KEYMODCHANGE, &aModEvt ); + + if( ! aDel.isDeleted() ) + { + // emulate KEY_MENU + if( ( pEvent->keyval == GDK_Alt_L || pEvent->keyval == GDK_Alt_R ) && + ( nModCode & ~(KEY_MOD3|KEY_MOD2)) == 0 ) + { + if( pEvent->type == GDK_KEY_PRESS ) + pThis->m_bSingleAltPress = true; + + else if( pThis->m_bSingleAltPress ) + { + SalKeyEvent aKeyEvt; + + aKeyEvt.mnCode = KEY_MENU | nModCode; + aKeyEvt.mnRepeat = 0; + aKeyEvt.mnTime = pEvent->time; + aKeyEvt.mnCharCode = 0; + + // simulate KEY_MENU + pThis->CallCallback( SALEVENT_KEYINPUT, &aKeyEvt ); + if( ! aDel.isDeleted() ) + { + pThis->CallCallback( SALEVENT_KEYUP, &aKeyEvt ); + pThis->m_bSingleAltPress = false; + } + } + } + else + pThis->m_bSingleAltPress = false; + } + } + else + { + pThis->doKeyCallback( pEvent->state, + pEvent->keyval, + pEvent->hardware_keycode, + pEvent->group, + pEvent->time, + sal_Unicode(gdk_keyval_to_unicode( pEvent->keyval )), + (pEvent->type == GDK_KEY_PRESS), + false ); + if( ! aDel.isDeleted() ) + { + pThis->m_bSendModChangeOnRelease = false; + pThis->m_bSingleAltPress = false; + } + } + + if( !aDel.isDeleted() && pThis->m_pIMHandler ) + pThis->m_pIMHandler->updateIMSpotLocation(); + + return TRUE; +} + +gboolean GtkSalFrame::signalDelete( GtkWidget*, GdkEvent*, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + GTK_YIELD_GRAB(); + pThis->CallCallback( SALEVENT_CLOSE, NULL ); + + return TRUE; +} + +void GtkSalFrame::signalStyleSet( GtkWidget*, GtkStyle* pPrevious, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + + // every frame gets an initial style set on creation + // do not post these as the whole application tends to + // redraw itself to adjust to the new style + // where there IS no new style resulting in tremendous unnecessary flickering + if( pPrevious != NULL ) + { + // signalStyleSet does NOT usually have the gdk lock + // so post user event to safely dispatch the SALEVENT_SETTINGSCHANGED + // note: settings changed for multiple frames is avoided in winproc.cxx ImplHandleSettings + pThis->getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_SETTINGSCHANGED ); + pThis->getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_FONTCHANGED ); + } + + /* #i64117# gtk sets a nice background pixmap + * but we actually don't really want that, so save + * some time on the Xserver as well as prevent + * some paint issues + */ + GdkWindow* pWin = GTK_WIDGET(pThis->getWindow())->window; + if( pWin ) + { + XLIB_Window aWin = GDK_WINDOW_XWINDOW(pWin); + if( aWin != None ) + XSetWindowBackgroundPixmap( pThis->getDisplay()->GetDisplay(), + aWin, + pThis->m_hBackgroundPixmap ); + } + + if( ! pThis->m_pParent ) + { + // signalize theme changed for NWF caches + // FIXME: should be called only once for a style change + GtkSalGraphics::bThemeChanged = TRUE; + } +} + +gboolean GtkSalFrame::signalState( GtkWidget*, GdkEvent* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + if( (pThis->m_nState & GDK_WINDOW_STATE_ICONIFIED) != (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_ICONIFIED ) ) + pThis->getDisplay()->SendInternalEvent( pThis, NULL, SALEVENT_RESIZE ); + + if( (pEvent->window_state.new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && + ! (pThis->m_nState & GDK_WINDOW_STATE_MAXIMIZED) ) + { + pThis->m_aRestorePosSize = + Rectangle( Point( pThis->maGeometry.nX, pThis->maGeometry.nY ), + Size( pThis->maGeometry.nWidth, pThis->maGeometry.nHeight ) ); + } + pThis->m_nState = pEvent->window_state.new_window_state; + + return FALSE; +} + +gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + pThis->m_nVisibility = pEvent->state; + + return FALSE; +} + +void GtkSalFrame::signalDestroy( GtkObject* pObj, gpointer frame ) +{ + GtkSalFrame* pThis = (GtkSalFrame*)frame; + if( GTK_WIDGET( pObj ) == pThis->m_pWindow ) + { + pThis->m_pFixedContainer = NULL; + pThis->m_pWindow = NULL; + } +} + +// ---------------------------------------------------------------------- +// GtkSalFrame::IMHandler +// ---------------------------------------------------------------------- + +GtkSalFrame::IMHandler::IMHandler( GtkSalFrame* pFrame ) +: m_pFrame(pFrame), + m_nPrevKeyPresses( 0 ), + m_pIMContext( NULL ), + m_bFocused( true ) +{ + m_aInputEvent.mpTextAttr = NULL; + createIMContext(); +} + +GtkSalFrame::IMHandler::~IMHandler() +{ + // cancel an eventual event posted to begin preedit again + m_pFrame->getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + deleteIMContext(); +} + +void GtkSalFrame::IMHandler::createIMContext() +{ + if( ! m_pIMContext ) + { + m_pIMContext = gtk_im_multicontext_new (); + g_signal_connect( m_pIMContext, "commit", + G_CALLBACK (signalIMCommit), this ); + g_signal_connect( m_pIMContext, "preedit_changed", + G_CALLBACK (signalIMPreeditChanged), this ); + g_signal_connect( m_pIMContext, "retrieve_surrounding", + G_CALLBACK (signalIMRetrieveSurrounding), this ); + g_signal_connect( m_pIMContext, "delete_surrounding", + G_CALLBACK (signalIMDeleteSurrounding), this ); + g_signal_connect( m_pIMContext, "preedit_start", + G_CALLBACK (signalIMPreeditStart), this ); + g_signal_connect( m_pIMContext, "preedit_end", + G_CALLBACK (signalIMPreeditEnd), this ); + + m_pFrame->getDisplay()->GetXLib()->PushXErrorLevel( true ); + gtk_im_context_set_client_window( m_pIMContext, GTK_WIDGET(m_pFrame->m_pWindow)->window ); + gtk_im_context_focus_in( m_pIMContext ); + m_pFrame->getDisplay()->GetXLib()->PopXErrorLevel(); + m_bFocused = true; + } +} + +void GtkSalFrame::IMHandler::deleteIMContext() +{ + if( m_pIMContext ) + { + // first give IC a chance to deinitialize + m_pFrame->getDisplay()->GetXLib()->PushXErrorLevel( true ); + gtk_im_context_set_client_window( m_pIMContext, NULL ); + m_pFrame->getDisplay()->GetXLib()->PopXErrorLevel(); + // destroy old IC + g_object_unref( m_pIMContext ); + m_pIMContext = NULL; + } +} + +void GtkSalFrame::IMHandler::doCallEndExtTextInput() +{ + m_aInputEvent.mpTextAttr = NULL; + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::updateIMSpotLocation() +{ + SalExtTextInputPosEvent aPosEvent; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + GdkRectangle aArea; + aArea.x = aPosEvent.mnX; + aArea.y = aPosEvent.mnY; + aArea.width = aPosEvent.mnWidth; + aArea.height = aPosEvent.mnHeight; + gtk_im_context_set_cursor_location( m_pIMContext, &aArea ); +} + +void GtkSalFrame::IMHandler::setInputContext( SalInputContext* ) +{ +} + +void GtkSalFrame::IMHandler::sendEmptyCommit() +{ + vcl::DeletionListener aDel( m_pFrame ); + + SalExtTextInputEvent aEmptyEv; + aEmptyEv.mnTime = 0; + aEmptyEv.mpTextAttr = 0; + aEmptyEv.maText = String(); + aEmptyEv.mnCursorPos = 0; + aEmptyEv.mnCursorFlags = 0; + aEmptyEv.mnDeltaStart = 0; + aEmptyEv.mbOnlyCursor = False; + m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&aEmptyEv ); + if( ! aDel.isDeleted() ) + m_pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, NULL ); +} + +void GtkSalFrame::IMHandler::endExtTextInput( USHORT /*nFlags*/ ) +{ + gtk_im_context_reset ( m_pIMContext ); + + if( m_aInputEvent.mpTextAttr ) + { + vcl::DeletionListener aDel( m_pFrame ); + // delete preedit in sal (commit an empty string) + sendEmptyCommit(); + if( ! aDel.isDeleted() ) + { + // mark previous preedit state again (will e.g. be sent at focus gain) + m_aInputEvent.mpTextAttr = &m_aInputFlags[0]; + if( m_bFocused ) + { + // begin preedit again + m_pFrame->getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + } +} + +void GtkSalFrame::IMHandler::focusChanged( bool bFocusIn ) +{ + m_bFocused = bFocusIn; + if( bFocusIn ) + { + m_pFrame->getDisplay()->GetXLib()->PushXErrorLevel( true ); + gtk_im_context_focus_in( m_pIMContext ); + m_pFrame->getDisplay()->GetXLib()->PopXErrorLevel(); + if( m_aInputEvent.mpTextAttr ) + { + sendEmptyCommit(); + // begin preedit again + m_pFrame->getDisplay()->SendInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } + } + else + { + m_pFrame->getDisplay()->GetXLib()->PushXErrorLevel( true ); + gtk_im_context_focus_out( m_pIMContext ); + m_pFrame->getDisplay()->GetXLib()->PopXErrorLevel(); + // cancel an eventual event posted to begin preedit again + m_pFrame->getDisplay()->CancelInternalEvent( m_pFrame, &m_aInputEvent, SALEVENT_EXTTEXTINPUT ); + } +} + +bool GtkSalFrame::IMHandler::handleKeyEvent( GdkEventKey* pEvent ) +{ + vcl::DeletionListener aDel( m_pFrame ); + + if( pEvent->type == GDK_KEY_PRESS ) + { + // Add this key press event to the list of previous key presses + // to which we compare key release events. If a later key release + // event has a matching key press event in this list, we swallow + // the key release because some GTK Input Methods don't swallow it + // for us. + m_aPrevKeyPresses.push_back( PreviousKeyPress(pEvent) ); + m_nPrevKeyPresses++; + + // Also pop off the earliest key press event if there are more than 10 + // already. + while (m_nPrevKeyPresses > 10) + { + m_aPrevKeyPresses.pop_front(); + m_nPrevKeyPresses--; + } + + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + + // #i51353# update spot location on every key input since we cannot + // know which key may activate a preedit choice window + updateIMSpotLocation(); + if( aDel.isDeleted() ) + return true; + + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + if( bResult ) + return true; + else + { + DBG_ASSERT( m_nPrevKeyPresses > 0, "key press has vanished !" ); + if( ! m_aPrevKeyPresses.empty() ) // sanity check + { + // event was not swallowed, do not filter a following + // key release event + // note: this relies on gtk_im_context_filter_keypress + // returning without calling a handler (in the "not swallowed" + // case ) which might change the previous key press list so + // we would pop the wrong event here + m_aPrevKeyPresses.pop_back(); + m_nPrevKeyPresses--; + } + } + } + + // Determine if we got an earlier key press event corresponding to this key release + if (pEvent->type == GDK_KEY_RELEASE) + { + GObject* pRef = G_OBJECT( g_object_ref( G_OBJECT( m_pIMContext ) ) ); + gboolean bResult = gtk_im_context_filter_keypress( m_pIMContext, pEvent ); + g_object_unref( pRef ); + + if( aDel.isDeleted() ) + return true; + + std::list<PreviousKeyPress>::iterator iter = m_aPrevKeyPresses.begin(); + std::list<PreviousKeyPress>::iterator iter_end = m_aPrevKeyPresses.end(); + while (iter != iter_end) + { + // If we found a corresponding previous key press event, swallow the release + // and remove the earlier key press from our list + if (*iter == pEvent) + { + m_aPrevKeyPresses.erase(iter); + m_nPrevKeyPresses--; + return true; + } + ++iter; + } + + if( bResult ) + return true; + } + + return false; +} + +/* FIXME: +* #122282# still more hacking: some IMEs never start a preedit but simply commit +* in this case we cannot commit a single character. Workaround: do not do the +* single key hack for enter or space if the unicode commited does not match +*/ + +static bool checkSingleKeyCommitHack( guint keyval, sal_Unicode cCode ) +{ + bool bRet = true; + switch( keyval ) + { + case GDK_KP_Enter: + case GDK_Return: + if( cCode != '\n' && cCode != '\r' ) + bRet = false; + break; + case GDK_space: + case GDK_KP_Space: + if( cCode != ' ' ) + bRet = false; + break; + default: + break; + } + return bRet; +} + +#ifdef SOLARIS +#define CONTEXT_ARG pContext +#else +#define CONTEXT_ARG EMPTYARG +#endif +void GtkSalFrame::IMHandler::signalIMCommit( GtkIMContext* CONTEXT_ARG, gchar* pText, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = (GtkSalFrame::IMHandler*)im_handler; + + vcl::DeletionListener aDel( pThis->m_pFrame ); + // open a block that will end the GTK_YIELD_GRAB before calling preedit changed again + { + GTK_YIELD_GRAB(); + + bool bWasPreedit = (pThis->m_aInputEvent.mpTextAttr != 0); + + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.mpTextAttr = 0; + pThis->m_aInputEvent.maText = String( pText, RTL_TEXTENCODING_UTF8 ); + pThis->m_aInputEvent.mnCursorPos = pThis->m_aInputEvent.maText.Len(); + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mnDeltaStart = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags.clear(); + + /* necessary HACK: all keyboard input comes in here as soon as a IMContext is set + * which is logical and consequent. But since even simple input like + * <space> comes through the commit signal instead of signalKey + * and all kinds of windows only implement KeyInput (e.g. PushButtons, + * RadioButtons and a lot of other Controls), will send a single + * KeyInput/KeyUp sequence instead of an ExtText event if there + * never was a preedit and the text is only one character. + * + * In this case there the last ExtText event must have been + * SALEVENT_ENDEXTTEXTINPUT, either because of a regular commit + * or because there never was a preedit. + */ + bool bSingleCommit = false; + if( ! bWasPreedit + && pThis->m_aInputEvent.maText.Len() == 1 + && ! pThis->m_aPrevKeyPresses.empty() + ) + { + const PreviousKeyPress& rKP = pThis->m_aPrevKeyPresses.back(); + sal_Unicode aOrigCode = pThis->m_aInputEvent.maText.GetChar(0); + + if( checkSingleKeyCommitHack( rKP.keyval, aOrigCode ) ) + { + pThis->m_pFrame->doKeyCallback( rKP.state, rKP.keyval, rKP.hardware_keycode, rKP.group, rKP.time, aOrigCode, true, true ); + bSingleCommit = true; + } + } + + if( ! bSingleCommit ) + { + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + } + if( ! aDel.isDeleted() ) + { + // reset input event + pThis->m_aInputEvent.maText = String(); + pThis->m_aInputEvent.mnCursorPos = 0; + pThis->updateIMSpotLocation(); + } + } + #ifdef SOLARIS + // #i51356# workaround a solaris IIIMP bug + // in case of partial commits the preedit changed signal + // and commit signal come in wrong order + if( ! aDel.isDeleted() ) + signalIMPreeditChanged( pContext, im_handler ); + #endif +} + +void GtkSalFrame::IMHandler::signalIMPreeditChanged( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = (GtkSalFrame::IMHandler*)im_handler; + + char* pText = NULL; + PangoAttrList* pAttrs = NULL; + gint nCursorPos = 0; + + gtk_im_context_get_preedit_string( pThis->m_pIMContext, + &pText, + &pAttrs, + &nCursorPos ); + if( pText && ! *pText ) // empty string + { + // change from nothing to nothing -> do not start preedit + // e.g. this will activate input into a calc cell without + // user input + if( pThis->m_aInputEvent.maText.Len() == 0 ) + { + g_free( pText ); + return; + } + } + + bool bEndPreedit = (!pText || !*pText) && pThis->m_aInputEvent.mpTextAttr != NULL; + pThis->m_aInputEvent.mnTime = 0; + pThis->m_aInputEvent.maText = String( pText, RTL_TEXTENCODING_UTF8 ); + pThis->m_aInputEvent.mnCursorPos = nCursorPos; + pThis->m_aInputEvent.mnCursorFlags = 0; + pThis->m_aInputEvent.mnDeltaStart = 0; + pThis->m_aInputEvent.mbOnlyCursor = False; + + pThis->m_aInputFlags = std::vector<USHORT>( std::max( 1, (int)pThis->m_aInputEvent.maText.Len() ), 0 ); + + PangoAttrIterator *iter = pango_attr_list_get_iterator (pAttrs); + do + { + GSList *attr_list = NULL; + GSList *tmp_list = NULL; + gint start, end; + guint sal_attr = 0; + + pango_attr_iterator_range (iter, &start, &end); + if (end == G_MAXINT) + end = pText ? strlen (pText) : 0; + if (end == start) + continue; + + start = g_utf8_pointer_to_offset (pText, pText + start); + end = g_utf8_pointer_to_offset (pText, pText + end); + + tmp_list = attr_list = pango_attr_iterator_get_attrs (iter); + while (tmp_list) + { + PangoAttribute *pango_attr = (PangoAttribute *)(tmp_list->data); + + switch (pango_attr->klass->type) + { + case PANGO_ATTR_BACKGROUND: + sal_attr |= (SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT | SAL_EXTTEXTINPUT_CURSOR_INVISIBLE); + break; + case PANGO_ATTR_UNDERLINE: + sal_attr |= SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + break; + case PANGO_ATTR_STRIKETHROUGH: + sal_attr |= SAL_EXTTEXTINPUT_ATTR_REDTEXT; + break; + default: + break; + } + pango_attribute_destroy (pango_attr); + tmp_list = tmp_list->next; + } + if (sal_attr == 0) + sal_attr |= SAL_EXTTEXTINPUT_ATTR_UNDERLINE; + g_slist_free (attr_list); + + // Set the sal attributes on our text + for (int i = start; i < end; i++) + pThis->m_aInputFlags[i] |= sal_attr; + } while (pango_attr_iterator_next (iter)); + + pThis->m_aInputEvent.mpTextAttr = &pThis->m_aInputFlags[0]; + + g_free( pText ); + pango_attr_list_unref( pAttrs ); + + GTK_YIELD_GRAB(); + + vcl::DeletionListener aDel( pThis->m_pFrame ); + + pThis->m_pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pThis->m_aInputEvent); + if( bEndPreedit && ! aDel.isDeleted() ) + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +void GtkSalFrame::IMHandler::signalIMPreeditStart( GtkIMContext*, gpointer /*im_handler*/ ) +{ +} + +void GtkSalFrame::IMHandler::signalIMPreeditEnd( GtkIMContext*, gpointer im_handler ) +{ + GtkSalFrame::IMHandler* pThis = (GtkSalFrame::IMHandler*)im_handler; + GTK_YIELD_GRAB(); + + vcl::DeletionListener aDel( pThis->m_pFrame ); + pThis->doCallEndExtTextInput(); + if( ! aDel.isDeleted() ) + pThis->updateIMSpotLocation(); +} + +uno::Reference<accessibility::XAccessibleEditableText> + FindFocus(uno::Reference< accessibility::XAccessibleContext > xContext) +{ + if (!xContext.is()) + uno::Reference< accessibility::XAccessibleEditableText >(); + + uno::Reference<accessibility::XAccessibleStateSet> xState = xContext->getAccessibleStateSet(); + if (xState.is()) + { + if (xState->contains(accessibility::AccessibleStateType::FOCUSED)) + return uno::Reference<accessibility::XAccessibleEditableText>(xContext, uno::UNO_QUERY); + } + + for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i) + { + uno::Reference< accessibility::XAccessible > xChild = xContext->getAccessibleChild(i); + if (!xChild.is()) + continue; + uno::Reference< accessibility::XAccessibleContext > xChildContext = xChild->getAccessibleContext(); + if (!xChildContext.is()) + continue; + uno::Reference< accessibility::XAccessibleEditableText > xText = FindFocus(xChildContext); + if (xText.is()) + return xText; + } + return uno::Reference< accessibility::XAccessibleEditableText >(); +} + +uno::Reference<accessibility::XAccessibleEditableText> lcl_GetxText() +{ + uno::Reference<accessibility::XAccessibleEditableText> xText; + Window* pFocusWin = ImplGetSVData()->maWinData.mpFocusWin; + if (!pFocusWin) + return xText; + + uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible( true ) ); + if (xAccessible.is()) + xText = FindFocus(xAccessible->getAccessibleContext()); + return xText; +} + +gboolean GtkSalFrame::IMHandler::signalIMRetrieveSurrounding( GtkIMContext* pContext, gpointer /*im_handler*/ ) +{ + uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(); + + if (xText.is()) + { + sal_uInt32 nPosition = xText->getCaretPosition(); + rtl::OUString sAllText = xText->getText(); + if (!sAllText.getLength()) + return FALSE; + rtl::OString sUTF = rtl::OUStringToOString(sAllText, RTL_TEXTENCODING_UTF8); + rtl::OUString sCursorText(sAllText, nPosition); + gtk_im_context_set_surrounding(pContext, sUTF.getStr(), sUTF.getLength(), + rtl::OUStringToOString(sCursorText, RTL_TEXTENCODING_UTF8).getLength()); + return TRUE; + } + + return FALSE; +} + +gboolean GtkSalFrame::IMHandler::signalIMDeleteSurrounding( GtkIMContext*, gint offset, gint nchars, + gpointer /*im_handler*/ ) +{ + uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(); + + if (xText.is()) + { + sal_uInt32 nPosition = xText->getCaretPosition(); + xText->deleteText(nPosition + offset, nPosition + offset + nchars); + return TRUE; + } + + return FALSE; +} diff --git a/vcl/unx/gtk/window/gtkobject.cxx b/vcl/unx/gtk/window/gtkobject.cxx new file mode 100644 index 000000000000..2a2bbe78078a --- /dev/null +++ b/vcl/unx/gtk/window/gtkobject.cxx @@ -0,0 +1,211 @@ +/************************************************************************* + * + * 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/gtkobject.hxx> +#include <plugins/gtk/gtkframe.hxx> +#include <plugins/gtk/gtkdata.hxx> +#include <plugins/gtk/gtkinst.hxx> + +GtkSalObject::GtkSalObject( GtkSalFrame* pParent, BOOL bShow ) + : m_pSocket( NULL ), + m_pRegion( NULL ) +{ + if( pParent ) + { + // our plug window + m_pSocket = gtk_drawing_area_new(); + Show( bShow ); + // insert into container + gtk_fixed_put( pParent->getFixedContainer(), + m_pSocket, + 0, 0 ); + // realize so we can get a window id + gtk_widget_realize( m_pSocket ); + + + // make it transparent; some plugins may not insert + // their own window here but use the socket window itself + gtk_widget_set_app_paintable( m_pSocket, TRUE ); + + //system data + SalDisplay* pDisp = GetX11SalData()->GetDisplay(); + m_aSystemData.nSize = sizeof( SystemChildData ); + m_aSystemData.pDisplay = pDisp->GetDisplay(); + m_aSystemData.aWindow = GDK_WINDOW_XWINDOW(m_pSocket->window); + m_aSystemData.pSalFrame = NULL; + m_aSystemData.pWidget = m_pSocket; + m_aSystemData.pVisual = pDisp->GetVisual(pParent->getScreenNumber()).GetVisual(); + m_aSystemData.nScreen = pParent->getScreenNumber(); + m_aSystemData.nDepth = pDisp->GetVisual(pParent->getScreenNumber()).GetDepth(); + m_aSystemData.aColormap = pDisp->GetColormap(pParent->getScreenNumber()).GetXColormap(); + m_aSystemData.pAppContext = NULL; + m_aSystemData.aShellWindow = GDK_WINDOW_XWINDOW(GTK_WIDGET(pParent->getWindow())->window); + m_aSystemData.pShellWidget = GTK_WIDGET(pParent->getWindow()); + + g_signal_connect( G_OBJECT(m_pSocket), "button-press-event", G_CALLBACK(signalButton), this ); + g_signal_connect( G_OBJECT(m_pSocket), "button-release-event", G_CALLBACK(signalButton), this ); + g_signal_connect( G_OBJECT(m_pSocket), "focus-in-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pSocket), "focus-out-event", G_CALLBACK(signalFocus), this ); + g_signal_connect( G_OBJECT(m_pSocket), "destroy", G_CALLBACK(signalDestroy), this ); + + // #i59255# necessary due to sync effects with java child windows + pParent->Sync(); + } +} + +GtkSalObject::~GtkSalObject() +{ + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + if( m_pSocket ) + { + // remove socket from parent frame's fixed container + gtk_container_remove( GTK_CONTAINER(gtk_widget_get_parent(m_pSocket)), + m_pSocket ); + // get rid of the socket + // actually the gtk_container_remove should let the ref count + // of the socket sink to 0 and destroy it (see signalDestroy) + // this is just a sanity check + if( m_pSocket ) + gtk_widget_destroy( m_pSocket ); + } +} + +void GtkSalObject::ResetClipRegion() +{ + if( m_pSocket ) + gdk_window_shape_combine_region( m_pSocket->window, NULL, 0, 0 ); +} + +USHORT GtkSalObject::GetClipRegionType() +{ + return SAL_OBJECT_CLIP_INCLUDERECTS; +} + +void GtkSalObject::BeginSetClipRegion( ULONG ) +{ + if( m_pRegion ) + gdk_region_destroy( m_pRegion ); + m_pRegion = gdk_region_new(); +} + +void GtkSalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight ) +{ + GdkRectangle aRect; + aRect.x = nX; + aRect.y = nY; + aRect.width = nWidth; + aRect.height = nHeight; + + gdk_region_union_with_rect( m_pRegion, &aRect ); +} + +void GtkSalObject::EndSetClipRegion() +{ + if( m_pSocket ) + gdk_window_shape_combine_region( m_pSocket->window, m_pRegion, 0, 0 ); +} + +void GtkSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight ) +{ + if( m_pSocket ) + { + GtkFixed* pContainer = GTK_FIXED(gtk_widget_get_parent(m_pSocket)); + gtk_fixed_move( pContainer, m_pSocket, nX, nY ); + gtk_widget_set_size_request( m_pSocket, nWidth, nHeight ); + gtk_container_resize_children( GTK_CONTAINER(pContainer) ); + } +} + +void GtkSalObject::Show( BOOL bVisible ) +{ + if( m_pSocket ) + { + if( bVisible ) + gtk_widget_show( m_pSocket ); + else + gtk_widget_hide( m_pSocket ); + } +} + +void GtkSalObject::Enable( BOOL ) +{ +} + +void GtkSalObject::GrabFocus() +{ +} + +void GtkSalObject::SetBackground() +{ +} + +void GtkSalObject::SetBackground( SalColor ) +{ +} + +const SystemEnvData* GtkSalObject::GetSystemData() const +{ + return &m_aSystemData; +} + + +gboolean GtkSalObject::signalButton( GtkWidget*, GdkEventButton* pEvent, gpointer object ) +{ + GtkSalObject* pThis = (GtkSalObject*)object; + + if( pEvent->type == GDK_BUTTON_PRESS ) + { + GTK_YIELD_GRAB(); + pThis->CallCallback( SALOBJ_EVENT_TOTOP, NULL ); + } + + return FALSE; +} + +gboolean GtkSalObject::signalFocus( GtkWidget*, GdkEventFocus* pEvent, gpointer object ) +{ + GtkSalObject* pThis = (GtkSalObject*)object; + + GTK_YIELD_GRAB(); + + pThis->CallCallback( pEvent->in ? SALOBJ_EVENT_GETFOCUS : SALOBJ_EVENT_LOSEFOCUS, NULL ); + + return FALSE; +} + +void GtkSalObject::signalDestroy( GtkObject* pObj, gpointer object ) +{ + GtkSalObject* pThis = (GtkSalObject*)object; + if( GTK_WIDGET(pObj) == pThis->m_pSocket ) + { + pThis->m_pSocket = NULL; + } +} diff --git a/vcl/unx/gtk/window/makefile.mk b/vcl/unx/gtk/window/makefile.mk new file mode 100644 index 000000000000..ac23e9363eef --- /dev/null +++ b/vcl/unx/gtk/window/makefile.mk @@ -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. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=vcl +TARGET=gtkwin +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# workaround for makedepend hang +MKDEPENDSOLVER= + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk +.INCLUDE : $(PRJ)$/util$/makefile2.pmk + +# --- Files -------------------------------------------------------- + +.IF "$(GUIBASE)"!="unx" + +dummy: + @echo "Nothing to build for GUIBASE $(GUIBASE)" + +.ELSE # "$(GUIBASE)"!="unx" + +.IF "$(ENABLE_GTK)" != "" + +PKGCONFIG_MODULES=gtk+-2.0 +.IF "$(ENABLE_DBUS)" != "" +CDEFS+=-DENABLE_DBUS +PKGCONFIG_MODULES+= dbus-glib-1 +.ENDIF +.INCLUDE : pkg_config.mk + +.IF "$(COM)" == "C52" +NOOPTFILES=$(SLO)$/gtkframe.obj +.ENDIF + +SLOFILES=\ + $(SLO)$/gtkframe.obj \ + $(SLO)$/gtkobject.obj +EXCEPTIONSFILES=$(SLO)$/gtkframe.obj +.ELSE # "$(ENABLE_GTK)" != "" + +dummy: + @echo GTK disabled - nothing to build +.ENDIF +.ENDIF # "$(GUIBASE)"!="unx" + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + +.INCLUDE : $(PRJ)$/util$/target.pmk |