blob: a67f49df955c357e4e5a884c1f43125ae8dd8e07 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include <unx/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:
{
sal_uInt16 type = WINDOW_WINDOW;
bool parentIsMenuFloatingWindow = false;
Window *pParent = pWindow->GetParent();
if( pParent ) {
type = pParent->GetType();
parentIsMenuFloatingWindow = ( TRUE == pParent->IsMenuFloatingWindow() );
}
if( (WINDOW_LISTBOX != type) && (WINDOW_COMBOBOX != type) &&
(WINDOW_MENUBARWINDOW != type) && ! parentIsMenuFloatingWindow )
{
role = ATK_ROLE_WINDOW;
}
}
break;
default:
{
Window *pChild = pWindow->GetChild( 0 );
if( pChild )
{
if( WINDOW_HELPTEXTWINDOW == pChild->GetType() )
{
role = ATK_ROLE_TOOL_TIP;
pChild->SetAccessibleRole( AccessibleRole::LABEL );
accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() );
}
else if ( pWindow->GetType() == WINDOW_BORDERWINDOW && pChild->GetType() == WINDOW_FLOATINGWINDOW )
{
PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild);
if (p && p->IsPopupMenu() && p->GetMenuStackLevel() == 0)
{
// This is a top-level menu popup. Register it.
role = ATK_ROLE_POPUP_MENU;
pChild->SetAccessibleRole( AccessibleRole::POPUP_MENU );
accessible->name = g_strdup( rtl::OUStringToOString( pChild->GetText(), RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
}
break;
}
}
accessible->role = role;
}
/*****************************************************************************/
static gint
ooo_window_wrapper_clear_focus(gpointer)
{
atk_focus_tracker_notify( NULL );
return FALSE;
}
/*****************************************************************************/
static gboolean
ooo_window_wrapper_real_focus_gtk (GtkWidget *, GdkEventFocus *)
{
g_idle_add( ooo_window_wrapper_clear_focus, NULL );
return FALSE;
}
static gboolean ooo_tooltip_map( GtkWidget* pToolTip, gpointer )
{
AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip );
if( pAccessible )
atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, TRUE );
return FALSE;
}
static gboolean ooo_tooltip_unmap( GtkWidget* pToolTip, gpointer )
{
AtkObject* pAccessible = gtk_widget_get_accessible( pToolTip );
if( pAccessible )
atk_object_notify_state_change( pAccessible, ATK_STATE_SHOWING, FALSE );
return FALSE;
}
/*****************************************************************************/
static bool
isChildPopupMenu(Window* pWindow)
{
Window* pChild = pWindow->GetAccessibleChildWindow(0);
if (!pChild)
return false;
if (WINDOW_FLOATINGWINDOW != pChild->GetType())
return false;
PopupMenuFloatingWindow* p = dynamic_cast<PopupMenuFloatingWindow*>(pChild);
if (!p)
return false;
return p->IsPopupMenu();
}
static void
ooo_window_wrapper_real_initialize(AtkObject *obj, gpointer data)
{
window_real_initialize(obj, data);
GtkSalFrame *pFrame = GtkSalFrame::getFromWindow( GTK_WINDOW( data ) );
if( pFrame )
{
Window *pWindow = pFrame->GetWindow();
if( pWindow )
{
init_from_window( obj, pWindow );
Reference< XAccessible > xAccessible( pWindow->GetAccessible(true) );
/* We need the wrapper object for the top-level XAccessible to be
* in the wrapper registry when atk traverses the hierachy up on
* focus events
*/
if( WINDOW_BORDERWINDOW == pWindow->GetType() )
{
if ( isChildPopupMenu(pWindow) )
{
AtkObject *child = atk_object_wrapper_new( xAccessible, obj );
ooo_wrapper_registry_add( xAccessible, child );
}
else
{
ooo_wrapper_registry_add( xAccessible, obj );
g_object_set_data( G_OBJECT(obj), "ooo:atk-wrapper-key", xAccessible.get() );
}
}
else
{
AtkObject *child = atk_object_wrapper_new( xAccessible, obj );
child->role = ATK_ROLE_FILLER;
if( (ATK_ROLE_DIALOG == obj->role) || (ATK_ROLE_ALERT == obj->role) )
child->role = ATK_ROLE_OPTION_PANE;
ooo_wrapper_registry_add( xAccessible, child );
}
}
}
g_signal_connect_after( GTK_WIDGET( data ), "focus-out-event",
G_CALLBACK (ooo_window_wrapper_real_focus_gtk),
NULL);
if( obj->role == ATK_ROLE_TOOL_TIP )
{
g_signal_connect_after( GTK_WIDGET( data ), "map-event",
G_CALLBACK (ooo_tooltip_map),
NULL);
g_signal_connect_after( GTK_WIDGET( data ), "unmap-event",
G_CALLBACK (ooo_tooltip_unmap),
NULL);
}
}
/*****************************************************************************/
static void
ooo_window_wrapper_real_finalize (GObject *obj)
{
ooo_wrapper_registry_remove( (XAccessible *) g_object_get_data( obj, "ooo:atk-wrapper-key" ));
window_real_finalize( obj );
}
/*****************************************************************************/
static void
ooo_window_wrapper_class_init (AtkObjectClass *klass, gpointer)
{
AtkObjectClass *atk_class;
GObjectClass *gobject_class;
gpointer data;
/*
* Patch the gobject vtable of GailWindow to refer to our instance of
* "initialize".
*/
data = g_type_class_peek_parent( klass );
atk_class = ATK_OBJECT_CLASS (data);
window_real_initialize = atk_class->initialize;
atk_class->initialize = ooo_window_wrapper_real_initialize;
gobject_class = G_OBJECT_CLASS (data);
window_real_finalize = gobject_class->finalize;
gobject_class->finalize = ooo_window_wrapper_real_finalize;
}
} // extern "C"
/*****************************************************************************/
GType
ooo_window_wrapper_get_type (void)
{
static GType type = 0;
if (!type)
{
GType parent_type = g_type_from_name( "GailWindow" );
if( ! parent_type )
{
g_warning( "Unknown type: GailWindow" );
parent_type = ATK_TYPE_OBJECT;
}
GTypeQuery type_query;
g_type_query( parent_type, &type_query );
static const GTypeInfo typeInfo =
{
type_query.class_size,
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) ooo_window_wrapper_class_init,
(GClassFinalizeFunc) NULL,
NULL,
type_query.instance_size,
0,
(GInstanceInitFunc) NULL,
NULL
} ;
type = g_type_register_static (parent_type, "OOoWindowAtkObject", &typeInfo, (GTypeFlags)0) ;
}
return type;
}
void restore_gail_window_vtable (void)
{
AtkObjectClass *atk_class;
gpointer data;
GType type = g_type_from_name( "GailWindow" );
if( type == G_TYPE_INVALID )
return;
data = g_type_class_peek( type );
atk_class = ATK_OBJECT_CLASS (data);
atk_class->initialize = window_real_initialize;
}