diff options
153 files changed, 39654 insertions, 7 deletions
@@ -1,3 +1,16 @@ +2007-12-18 Christian Persch <chpe@gnome.org> + + * configure.in: + * docs/reference/Makefile.am: + * docs/reference/libgail-util/*: + * gail-uninstalled.pc.in: + * gail.pc.in: + * modules/Makefile.am: + * modules/other/Makefile.am: + * modules/other/gail/*: + * modules/other/gail/libgail-util/*: + * po/POTFILES.skip: Integrate gail into gtk+. Bug #169488. + 2007-12-17 Matthias Clasen <mclasen@redhat.com> * gtk/gtksettings.c: Add a gtk-im-module GTK setting diff --git a/configure.in b/configure.in index fbab5bb3c..58ea15787 100644 --- a/configure.in +++ b/configure.in @@ -33,7 +33,7 @@ m4_define([gtk_binary_version], [2.10.0]) # required versions of other packages m4_define([glib_required_version], [2.15.0]) m4_define([pango_required_version], [1.17.3]) -m4_define([atk_required_version], [1.9.0]) +m4_define([atk_required_version], [1.13.0]) m4_define([cairo_required_version], [1.2.0]) @@ -1559,6 +1559,36 @@ AC_SUBST(CAIRO_PREFIX) AC_SUBST(GTK_DEBUG_FLAGS) AC_SUBST(GTK_XIM_FLAGS) +######################## +# Checks needed for gail +######################## + +old_LIBS="$LIBS" +dnl Checks for inet libraries: +AC_SEARCH_LIBS(gethostent, nsl) +AC_SEARCH_LIBS(setsockopt, socket) +AC_SEARCH_LIBS(connect, inet) + +dnl check for the sockaddr_un.sun_len member +AC_CHECK_MEMBER([struct sockaddr_un.sun_len], + [struct_sockaddr_un_sun_len=true], + [struct_sockaddr_un_suin_len=false], + [#include <sys/types.h> + #include <sys/un.h>] + ) +case $struct_sockaddr_un_sun_len in + true) + AC_DEFINE_UNQUOTED(HAVE_SOCKADDR_UN_SUN_LEN, 1, Have the sockaddr_un.sun_len member.) + ;; + *) + ;; +esac + +GAIL_INET_LIBS="$LIBS" +AC_SUBST([GAIL_INET_LIBS]) + +LIBS="$old_LIBS" + ################################################################ # Printing system checks ################################################################ @@ -1760,9 +1790,11 @@ gdk-pixbuf-2.0.pc gdk-2.0.pc gtk+-2.0.pc gtk+-unix-print-2.0.pc +gail.pc gdk-pixbuf-2.0-uninstalled.pc gdk-2.0-uninstalled.pc gtk+-2.0-uninstalled.pc +gail-uninstalled.pc m4macros/Makefile po/Makefile.in po-properties/Makefile.in @@ -1778,6 +1810,7 @@ docs/reference/gdk/Makefile docs/reference/gdk/version.xml docs/reference/gtk/Makefile docs/reference/gtk/version.xml +docs/reference/libgail-util/Makefile docs/faq/Makefile docs/tools/Makefile docs/tutorial/Makefile @@ -1800,12 +1833,15 @@ gtk/theme-bits/Makefile gtk/tests/Makefile gtk/xdgmime/Makefile modules/Makefile -modules/input/Makefile +modules/other/Makefile +modules/other/gail/Makefile +modules/other/gail/libgail-util/Makefile modules/engines/Makefile modules/engines/pixbuf/Makefile modules/engines/ms-windows/Makefile modules/engines/ms-windows/Theme/Makefile modules/engines/ms-windows/Theme/gtk-2.0/Makefile +modules/input/Makefile modules/printbackends/Makefile modules/printbackends/cups/Makefile modules/printbackends/lpr/Makefile diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am index 1748f8372..f4d7451db 100644 --- a/docs/reference/Makefile.am +++ b/docs/reference/Makefile.am @@ -1,5 +1,5 @@ ## Process this file with automake to produce Makefile.in include $(top_srcdir)/Makefile.decl -SUBDIRS = gdk-pixbuf gdk gtk +SUBDIRS = gdk-pixbuf gdk gtk libgail-util diff --git a/docs/reference/libgail-util/Makefile.am b/docs/reference/libgail-util/Makefile.am new file mode 100644 index 000000000..a2f2e4807 --- /dev/null +++ b/docs/reference/libgail-util/Makefile.am @@ -0,0 +1,33 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = 1.7 + +# The name of the module. +DOC_MODULE=gail-libgail-util + +# The top-level SGML file. +DOC_MAIN_SGML_FILE=gail-libgail-util-docs.sgml + +# The directory containing the source code (if it contains documentation). +DOC_SOURCE_DIR=../../../modules/other/gail/libgail-util + +# Used for dependencies +HFILE_GLOB = $(top_srcdir)/modules/other/gail/libgail-util/*.h +CFILE_GLOB = $(top_srcdir)/modules/other/gail/libgail-util/*.c + +# CFLAGS and LDFLAGS for compiling scan program. Only needed +# if $(DOC_MODULE).types is non-empty. +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + $(DEP_CFLAGS) + +GTKDOC_LIBS = $(top_builddir)/modules/other/gail/libgail-util/libgailutil.la + +# gtkdoc-mkdb related varaibles +MKDB_OPTIONS = +content_files = + +HTML_IMAGES = + +include $(top_srcdir)/gtk-doc.make diff --git a/docs/reference/libgail-util/gail-libgail-util-docs.sgml b/docs/reference/libgail-util/gail-libgail-util-docs.sgml new file mode 100644 index 000000000..97bb08be2 --- /dev/null +++ b/docs/reference/libgail-util/gail-libgail-util-docs.sgml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +<!ENTITY gail-libgail-util-gailtextutil SYSTEM "xml/gailtextutil.xml"> +<!ENTITY gail-libgail-util-gailmisc SYSTEM "xml/gailmisc.xml"> +]> + +<book> + <bookinfo> + <title>GAIL Reference Manual</title> + </bookinfo> + <chapter id="libgail-util-main"> + <title>GAIL libgail-util Library</title> + &gail-libgail-util-gailtextutil; + &gail-libgail-util-gailmisc; + </chapter> +</book> diff --git a/docs/reference/libgail-util/gail-libgail-util-overrides.txt b/docs/reference/libgail-util/gail-libgail-util-overrides.txt new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/docs/reference/libgail-util/gail-libgail-util-overrides.txt diff --git a/docs/reference/libgail-util/gail-libgail-util-sections.txt b/docs/reference/libgail-util/gail-libgail-util-sections.txt new file mode 100644 index 000000000..1ef8ad444 --- /dev/null +++ b/docs/reference/libgail-util/gail-libgail-util-sections.txt @@ -0,0 +1,34 @@ +<SECTION> +<FILE>gailtextutil</FILE> +<TITLE>GailTextUtil</TITLE> +GailTextUtil +GailOffsetType +gail_text_util_new +gail_text_util_text_setup +gail_text_util_buffer_setup +gail_text_util_get_text +gail_text_util_get_substring +<SUBSECTION Standard> +GailTextUtilClass +GAIL_TEXT_UTIL +GAIL_IS_TEXT_UTIL +GAIL_TYPE_TEXT_UTIL +GAIL_TEXT_UTIL_CLASS +GAIL_IS_TEXT_UTIL_CLASS +GAIL_TEXT_UTIL_GET_CLASS +<SUBSECTION Private> +gail_text_util_get_type +</SECTION> + +<SECTION> +<FILE>gailmisc</FILE> +<TITLE>GailMisc</TITLE> +gail_misc_add_attribute +gail_misc_layout_get_run_attributes +gail_misc_get_default_attributes +gail_misc_get_extents_from_pango_rectangle +gail_misc_get_index_at_point_in_layout +gail_misc_get_origins +gail_misc_add_to_attr_set +gail_misc_buffer_get_run_attributes +</SECTION> diff --git a/docs/reference/libgail-util/gail-libgail-util.types b/docs/reference/libgail-util/gail-libgail-util.types new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/docs/reference/libgail-util/gail-libgail-util.types diff --git a/docs/reference/libgail-util/tmpl/gail-libgail-util-unused.sgml b/docs/reference/libgail-util/tmpl/gail-libgail-util-unused.sgml new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/docs/reference/libgail-util/tmpl/gail-libgail-util-unused.sgml diff --git a/docs/reference/libgail-util/tmpl/gailmisc.sgml b/docs/reference/libgail-util/tmpl/gailmisc.sgml new file mode 100644 index 000000000..6e9642df9 --- /dev/null +++ b/docs/reference/libgail-util/tmpl/gailmisc.sgml @@ -0,0 +1,122 @@ +<!-- ##### SECTION Title ##### --> +GailMisc + +<!-- ##### SECTION Short_Description ##### --> +GailMisc is a set of utility functions which may be useful to implementors of +Atk interfaces for custom widgets. + +<!-- ##### SECTION Long_Description ##### --> +<para> +GailMisc is a set of utility function which are used in the implemementation +of Atk interfaces for Gtk widgets. They may be useful to implementors of +Atk interfaces for custom widgets. + +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### FUNCTION gail_misc_add_attribute ##### --> +<para> + +</para> + +@attrib_set: +@attr: +@value: +@Returns: + + +<!-- ##### FUNCTION gail_misc_layout_get_run_attributes ##### --> +<para> + +</para> + +@attrib_set: +@layout: +@text: +@offset: +@start_offset: +@end_offset: +@Returns: + + +<!-- ##### FUNCTION gail_misc_get_default_attributes ##### --> +<para> + +</para> + +@attrib_set: +@layout: +@widget: +@Returns: + + +<!-- ##### FUNCTION gail_misc_get_extents_from_pango_rectangle ##### --> +<para> + +</para> + +@widget: +@char_rect: +@x_layout: +@y_layout: +@x: +@y: +@width: +@height: +@coords: + + +<!-- ##### FUNCTION gail_misc_get_index_at_point_in_layout ##### --> +<para> + +</para> + +@widget: +@layout: +@x_layout: +@y_layout: +@x: +@y: +@coords: +@Returns: + + +<!-- ##### FUNCTION gail_misc_get_origins ##### --> +<para> + +</para> + +@widget: +@x_window: +@y_window: +@x_toplevel: +@y_toplevel: + + +<!-- ##### FUNCTION gail_misc_add_to_attr_set ##### --> +<para> + +</para> + +@attrib_set: +@attrs: +@attr: +@Returns: + + +<!-- ##### FUNCTION gail_misc_buffer_get_run_attributes ##### --> +<para> + +</para> + +@buffer: +@offset: +@start_offset: +@end_offset: +@Returns: + + diff --git a/docs/reference/libgail-util/tmpl/gailtextutil.sgml b/docs/reference/libgail-util/tmpl/gailtextutil.sgml new file mode 100644 index 000000000..6b35c2d37 --- /dev/null +++ b/docs/reference/libgail-util/tmpl/gailtextutil.sgml @@ -0,0 +1,92 @@ +<!-- ##### SECTION Title ##### --> +GailTextUtil + +<!-- ##### SECTION Short_Description ##### --> +GailTextUtil is a utility class which can be used to implement some of +the AtkText functions for accessible objects which implement AtkText. + +<!-- ##### SECTION Long_Description ##### --> +<para> +GailTextUtil is a utility class which can be used to implement the +AtkText functions which get text for accessible objects which implement +AtkText. + +In GAIL it is used by the accsesible objects for GnomeCanvasText, GtkEntry, +GtkLabel, GtkCellRendererText and GtkTextview. +</para> + +<!-- ##### SECTION See_Also ##### --> +<para> + +</para> + +<!-- ##### STRUCT GailTextUtil ##### --> +<para> +The GailTextCell structure should not be accessed directly. + +</para> + +@parent: +@buffer: + +<!-- ##### ENUM GailOffsetType ##### --> +<para> + +</para> + +@GAIL_BEFORE_OFFSET: +@GAIL_AT_OFFSET: +@GAIL_AFTER_OFFSET: + +<!-- ##### FUNCTION gail_text_util_new ##### --> +<para> + +</para> + +@Returns: + + +<!-- ##### FUNCTION gail_text_util_text_setup ##### --> +<para> + +</para> + +@textutil: +@text: + + +<!-- ##### FUNCTION gail_text_util_buffer_setup ##### --> +<para> + +</para> + +@textutil: +@buffer: + + +<!-- ##### FUNCTION gail_text_util_get_text ##### --> +<para> + +</para> + +@textutil: +@layout: +@function: +@boundary_type: +@offset: +@start_offset: +@end_offset: +@Returns: + + +<!-- ##### FUNCTION gail_text_util_get_substring ##### --> +<para> + +</para> + +@textutil: +@start_pos: +@end_pos: +@Returns: + + diff --git a/gail-uninstalled.pc.in b/gail-uninstalled.pc.in new file mode 100644 index 000000000..63a49adbe --- /dev/null +++ b/gail-uninstalled.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Gail +Description: GNOME Accessibility Implementation Library +Version: @VERSION@ +Requires: atk gtk+-2.0 +Libs: ${pc_top_builddir}/${pcfiledir}/gail/libgail.la +Cflags: -I${pc_top_builddir}/${pcfiledir} diff --git a/gail.pc.in b/gail.pc.in new file mode 100644 index 000000000..bab729a89 --- /dev/null +++ b/gail.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Gail +Description: GNOME Accessibility Implementation Library +Version: @VERSION@ +Requires: atk gtk+-2.0 +Libs: -L${libdir} -lgailutil +Cflags: -I${includedir}/gail-1.0 diff --git a/modules/Makefile.am b/modules/Makefile.am index d8e0c5829..86a89af3c 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -1,8 +1,7 @@ include $(top_srcdir)/Makefile.decl +SUBDIRS = input engines other + if OS_UNIX -PRINTBACKENDS_SUBDIR=printbackends +SUBDIRS += printbackends endif - -SUBDIRS=input engines $(PRINTBACKENDS_SUBDIR) -DIST_SUBDIRS=input engines printbackends diff --git a/modules/other/Makefile.am b/modules/other/Makefile.am new file mode 100644 index 000000000..37488eb5e --- /dev/null +++ b/modules/other/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = gail diff --git a/modules/other/gail/Makefile.am b/modules/other/gail/Makefile.am new file mode 100644 index 000000000..0825a41da --- /dev/null +++ b/modules/other/gail/Makefile.am @@ -0,0 +1,173 @@ +include $(top_srcdir)/Makefile.decl + +SUBDIRS = libgail-util + +if OS_WIN32 +no_undefined = -no-undefined +endif + +moduledir = $(libdir)/gtk-2.0/$(GTK_BINARY_VERSION)/other +module_LTLIBRARIES = libgail.la + +gail_c_sources = \ + gail-private-macros.h \ + gail.c \ + gailadjustment.c \ + gailarrow.c \ + gailbooleancell.c \ + gailbooleancellfactory.c \ + gailbox.c \ + gailbutton.c \ + gailcalendar.c \ + gailcell.c \ + gailcellparent.c \ + gailcheckmenuitem.c \ + gailchecksubmenuitem.c \ + gailclist.c \ + gailclistcell.c \ + gailcombo.c \ + gailcombobox.c \ + gailcontainer.c \ + gailcontainercell.c \ + gailentry.c \ + gailexpander.c \ + gailframe.c \ + gailimage.c \ + gailimagecell.c \ + gailimagecellfactory.c \ + gailitem.c \ + gaillabel.c \ + gaillist.c \ + gailmenu.c \ + gailmenushell.c \ + gailmenuitem.c \ + gailnotebook.c \ + gailnotebookpage.c \ + gailobject.c \ + gailobjectfactory.c \ + gailoptionmenu.c \ + gailpaned.c \ + gailpixmap.c \ + gailprogressbar.c \ + gailradiobutton.c \ + gailradiomenuitem.c \ + gailradiosubmenuitem.c \ + gailrange.c \ + gailrenderercell.c \ + gailrenderercellfactory.c \ + gailscale.c \ + gailscrollbar.c \ + gailscrolledwindow.c \ + gailseparator.c \ + gailspinbutton.c \ + gailsubmenuitem.c \ + gailstatusbar.c \ + gailtextcell.c \ + gailtextcellfactory.c \ + gailtextview.c \ + gailtogglebutton.c \ + gailtoplevel.c \ + gailtreeview.c \ + gailutil.c \ + gailwidget.c \ + gailwindow.c + +libgailincludedir=$(includedir)/gail-1.0/gail + +gail_private_h_sources = \ + gail.h \ + gailadjustment.h \ + gailarrow.h \ + gailbooleancell.h \ + gailbooleancellfactory.h \ + gailbox.h \ + gailbutton.h \ + gailcalendar.h \ + gailcell.h \ + gailcellparent.h \ + gailcheckmenuitem.h \ + gailchecksubmenuitem.h \ + gailclist.h \ + gailclistcell.h \ + gailcombo.h \ + gailcombobox.h \ + gailcontainercell.h \ + gailcontainer.h \ + gailentry.h \ + gailexpander.h \ + gailfactory.h \ + gailframe.h \ + gailimage.h \ + gailimagecell.h \ + gailimagecellfactory.h \ + gailintl.h \ + gailitem.h \ + gaillabel.h \ + gaillist.h \ + gailmenu.h \ + gailmenushell.h \ + gailmenuitem.h \ + gailnotebook.h \ + gailnotebookpage.h \ + gailobject.h \ + gailobjectfactory.h \ + gailoptionmenu.h \ + gailpaned.h \ + gailpixmap.h \ + gailprogressbar.h \ + gailradiobutton.h \ + gailradiomenuitem.h \ + gailradiosubmenuitem.h \ + gailrange.h \ + gailrenderercell.h \ + gailrenderercellfactory.h \ + gailscale.h \ + gailscrollbar.h \ + gailscrolledwindow.h \ + gailseparator.h \ + gailspinbutton.h \ + gailsubmenuitem.h \ + gailstatusbar.h \ + gailtextcell.h \ + gailtextcellfactory.h \ + gailtextview.h \ + gailtogglebutton.h \ + gailtoplevel.h \ + gailtreeview.h \ + gailutil.h \ + gailwindow.h + +gail_public_h_sources = \ + gailwidget.h + +libgail_la_SOURCES = \ + $(gail_c_sources) \ + $(gail_public_h_sources) \ + $(gail_private_h_sources) + +libgailinclude_HEADERS = \ + $(gail_public_h_sources) + +libgail_la_CPPFLAGS = \ + -I$(top_srcdir)/modules/other \ + -I$(top_srcdir)/gdk \ + -I$(top_builddir)/gdk \ + -I$(top_srcdir)/gtk \ + -I$(top_builddir)/gtk \ + -DGTK_VERSION=\"$(GTK_VERSION)\" \ + $(AM_CPPFLAGS) + +libgail_la_CFLAGS = \ + $(GTK_DEP_CFLAGS) \ + $(GTK_DEBUG_FLAGS) \ + $(AM_CFLAGS) + +libgail_la_LIBADD = \ + $(GTK_DEP_LIBS) \ + $(INTLLIBS) + +libgail_la_LDFLAGS = \ + $(top_builddir)/modules/other/gail/libgail-util/libgailutil.la \ + -rpath $(moduledir) -module -avoid-version \ + $(no_undefined) \ + $(LDFLAGS) diff --git a/modules/other/gail/gail-private-macros.h b/modules/other/gail/gail-private-macros.h new file mode 100644 index 000000000..bb2022ba7 --- /dev/null +++ b/modules/other/gail/gail-private-macros.h @@ -0,0 +1,38 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GAIL_PRIVATE_MACROS_H__ +#define __GAIL_PRIVATE_MACROS_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Note: these macros are logic macros, not intended to warn on failure. */ + +#define gail_return_val_if_fail(a, b) if (!(a)) return (b) +#define gail_return_if_fail(a) if (!(a)) return + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_PRIVATE_MACROS_H__ */ diff --git a/modules/other/gail/gail.c b/modules/other/gail/gail.c new file mode 100644 index 000000000..217eb8caa --- /dev/null +++ b/modules/other/gail/gail.c @@ -0,0 +1,982 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <atk/atk.h> +#include <gtk/gtk.h> +#include "gail.h" +#include "gailfactory.h" + +#define GNOME_ACCESSIBILITY_ENV "GNOME_ACCESSIBILITY" + +static gboolean gail_focus_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static gboolean gail_select_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static gboolean gail_deselect_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static gboolean gail_switch_page_watcher(GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static AtkObject* gail_get_accessible_for_widget (GtkWidget *widget, + gboolean *transient); +static void gail_finish_select (GtkWidget *widget); +static void gail_map_cb (GtkWidget *widget); +static void gail_map_submenu_cb (GtkWidget *widget); +static gint gail_focus_idle_handler (gpointer data); +static void gail_focus_notify (GtkWidget *widget); +static void gail_focus_notify_when_idle (GtkWidget *widget); + +static void gail_focus_tracker_init (void); +static void gail_focus_object_destroyed (gpointer data); +static void gail_focus_tracker (AtkObject *object); +static void gail_set_focus_widget (GtkWidget *focus_widget, + GtkWidget *widget); +static void gail_set_focus_object (AtkObject *focus_obj, + AtkObject *obj); + +GtkWidget* focus_widget = NULL; +static GtkWidget* next_focus_widget = NULL; +static gboolean was_deselect = FALSE; +static GtkWidget* subsequent_focus_widget = NULL; +static GtkWidget* focus_before_menu = NULL; +static guint focus_notify_handler = 0; +static guint focus_tracker_id = 0; +static GQuark quark_focus_object = 0; + +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_WIDGET, gail_widget, gail_widget_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_CONTAINER, gail_container, gail_container_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_BUTTON, gail_button, gail_button_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_ITEM, gail_item, gail_item_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_MENU_ITEM, gail_menu_item, gail_menu_item_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_TOGGLE_BUTTON, gail_toggle_button, gail_toggle_button_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_IMAGE, gail_image, gail_image_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_TEXT_VIEW, gail_text_view, gail_text_view_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_COMBO, gail_combo, gail_combo_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_COMBO_BOX, gail_combo_box, gail_combo_box_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_ENTRY, gail_entry, gail_entry_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_MENU_SHELL, gail_menu_shell, gail_menu_shell_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_MENU, gail_menu, gail_menu_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_WINDOW, gail_window, gail_window_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_RANGE, gail_range, gail_range_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_SCALE, gail_scale, gail_scale_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_CLIST, gail_clist, gail_clist_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_LABEL, gail_label, gail_label_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_STATUSBAR, gail_statusbar, gail_statusbar_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_NOTEBOOK, gail_notebook, gail_notebook_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_CALENDAR, gail_calendar, gail_calendar_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_PROGRESS_BAR, gail_progress_bar, gail_progress_bar_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_SPIN_BUTTON, gail_spin_button, gail_spin_button_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_TREE_VIEW, gail_tree_view, gail_tree_view_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_FRAME, gail_frame, gail_frame_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_RADIO_BUTTON, gail_radio_button, gail_radio_button_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_ARROW, gail_arrow, gail_arrow_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_PIXMAP, gail_pixmap, gail_pixmap_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_SEPARATOR, gail_separator, gail_separator_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_BOX, gail_box, gail_box_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_SCROLLED_WINDOW, gail_scrolled_window, gail_scrolled_window_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_LIST, gail_list, gail_list_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_PANED, gail_paned, gail_paned_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_SCROLLBAR, gail_scrollbar, gail_scrollbar_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_OPTION_MENU, gail_option_menu, gail_option_menu_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_CHECK_MENU_ITEM, gail_check_menu_item, gail_check_menu_item_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_RADIO_MENU_ITEM, gail_radio_menu_item, gail_radio_menu_item_new) +GAIL_ACCESSIBLE_FACTORY (GAIL_TYPE_EXPANDER, gail_expander, gail_expander_new) + +static AtkObject* +gail_get_accessible_for_widget (GtkWidget *widget, + gboolean *transient) +{ + AtkObject *obj = NULL; + GType gnome_canvas; + + gnome_canvas = g_type_from_name ("GnomeCanvas"); + + *transient = FALSE; + if (!widget) + return NULL; + + if (GTK_IS_ENTRY (widget)) + { + GtkWidget *other_widget = widget->parent; + if (GTK_IS_COMBO (other_widget)) + { + gail_set_focus_widget (other_widget, widget); + widget = other_widget; + } + } + else if (GTK_IS_NOTEBOOK (widget)) + { + GtkNotebook *notebook; + gint page_num = -1; + + notebook = GTK_NOTEBOOK (widget); + /* + * Report the currently focused tab rather than the currently selected tab + */ + if (notebook->focus_tab) + { + page_num = g_list_index (notebook->children, notebook->focus_tab->data); + } + if (page_num != -1) + { + obj = gtk_widget_get_accessible (widget); + obj = atk_object_ref_accessible_child (obj, page_num); + g_object_unref (obj); + } + } + else if (GTK_CHECK_TYPE ((widget), gnome_canvas)) + { + GObject *focused_item; + GValue value = {0, }; + + g_value_init (&value, G_TYPE_OBJECT); + g_object_get_property (G_OBJECT (widget), "focused_item", &value); + focused_item = g_value_get_object (&value); + + if (focused_item) + { + AtkObject *tmp; + + obj = atk_gobject_accessible_for_object (G_OBJECT (focused_item)); + tmp = g_object_get_qdata (G_OBJECT (obj), quark_focus_object); + if (tmp != NULL) + obj = tmp; + } + } + else if (GTK_IS_TOGGLE_BUTTON (widget)) + { + GtkWidget *other_widget = widget->parent; + if (GTK_IS_COMBO_BOX (other_widget)) + { + gail_set_focus_widget (other_widget, widget); + widget = other_widget; + } + } + if (obj == NULL) + { + AtkObject *focus_object; + + obj = gtk_widget_get_accessible (widget); + focus_object = g_object_get_qdata (G_OBJECT (obj), quark_focus_object); + /* + * We check whether the object for this focus_object has been deleted. + * This can happen when navigating to an empty directory in nautilus. + * See bug #141907. + */ + if (ATK_IS_GOBJECT_ACCESSIBLE (focus_object)) + { + if (!atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (focus_object))) + focus_object = NULL; + } + if (focus_object) + obj = focus_object; + } + + return obj; +} + +static gboolean +gail_focus_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + GdkEvent *event; + + object = g_value_get_object (param_values + 0); + g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE); + + event = g_value_get_boxed (param_values + 1); + widget = GTK_WIDGET (object); + + if (event->type == GDK_FOCUS_CHANGE) + { + if (event->focus_change.in) + { + if (GTK_IS_WINDOW (widget)) + { + GtkWindow *window; + + window = GTK_WINDOW (widget); + if (window->focus_widget) + { + /* + * If we already have a potential focus widget set this + * windows's focus widget to focus_before_menu so that + * it will be reported when menu item is unset. + */ + if (next_focus_widget) + { + if (GTK_IS_MENU_ITEM (next_focus_widget) && + !focus_before_menu) + { + void *vp_focus_before_menu = &focus_before_menu; + focus_before_menu = window->focus_widget; + g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu); + } + + return TRUE; + } + widget = window->focus_widget; + } + else if (window->type == GTK_WINDOW_POPUP) + { + if (GTK_IS_BIN (widget)) + { + GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); + + if (GTK_IS_WIDGET (child) && GTK_WIDGET_HAS_GRAB (child)) + { + if (GTK_IS_MENU_SHELL (child)) + { + if (GTK_MENU_SHELL (child)->active_menu_item) + { + /* + * We have a menu which has a menu item selected + * so we do not report focus on the menu. + */ + return TRUE; + } + } + widget = child; + } + } + else /* popup window has no children; this edge case occurs in some custom code (OOo for instance) */ + { + return TRUE; + } + } + else /* Widget is a non-popup toplevel with no focus children; + don't emit for this case either, as it's useless */ + { + return TRUE; + } + } + } + else + { + if (next_focus_widget) + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (next_focus_widget); + if (toplevel == widget) + next_focus_widget = NULL; + } + /* focus out */ + widget = NULL; + } + } + else + { + if (event->type == GDK_MOTION_NOTIFY && GTK_WIDGET_HAS_FOCUS (widget)) + { + if (widget == focus_widget) + { + return TRUE; + } + } + else + { + return TRUE; + } + } + /* + * If the focus widget is a GtkSocket without a plug + * then ignore the focus notification as the embedded + * plug will report a focus notification. + */ + if (GTK_IS_SOCKET (widget) && + GTK_SOCKET (widget)->plug_widget == NULL) + return TRUE; + /* + * The widget may not yet be visible on the screen so we wait until it is. + */ + gail_focus_notify_when_idle (widget); + return TRUE; +} + +static gboolean +gail_select_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + + object = g_value_get_object (param_values + 0); + g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE); + + widget = GTK_WIDGET (object); + + if (!GTK_WIDGET_MAPPED (widget)) + { + g_signal_connect (widget, "map", + G_CALLBACK (gail_map_cb), + NULL); + } + else + gail_finish_select (widget); + + return TRUE; +} + +static void +gail_finish_select (GtkWidget *widget) +{ + if (GTK_IS_MENU_ITEM (widget)) + { + GtkMenuItem* menu_item; + + menu_item = GTK_MENU_ITEM (widget); + if (menu_item->submenu && + !GTK_WIDGET_MAPPED (menu_item->submenu)) + { + /* + * If the submenu is not visble, wait until it is before + * reporting focus on the menu item. + */ + gulong handler_id; + + handler_id = g_signal_handler_find (menu_item->submenu, + G_SIGNAL_MATCH_FUNC, + g_signal_lookup ("map", + GTK_TYPE_WINDOW), + 0, + NULL, + (gpointer) gail_map_submenu_cb, + NULL); + if (!handler_id) + g_signal_connect (menu_item->submenu, "map", + G_CALLBACK (gail_map_submenu_cb), + NULL); + return; + + } + /* + * If we are waiting to report focus on a menubar or a menu item + * because of a previous deselect, cancel it. + */ + if (was_deselect && + focus_notify_handler && + next_focus_widget && + (GTK_IS_MENU_BAR (next_focus_widget) || + GTK_IS_MENU_ITEM (next_focus_widget))) + { + void *vp_next_focus_widget = &next_focus_widget; + g_source_remove (focus_notify_handler); + g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + next_focus_widget = NULL; + focus_notify_handler = 0; + was_deselect = FALSE; + } + } + /* + * If previously focused widget is not a GtkMenuItem or a GtkMenu, + * keep track of it so we can return to it after menubar is deactivated + */ + if (focus_widget && + !GTK_IS_MENU_ITEM (focus_widget) && + !GTK_IS_MENU (focus_widget)) + { + void *vp_focus_before_menu = &focus_before_menu; + focus_before_menu = focus_widget; + g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu); + + } + gail_focus_notify_when_idle (widget); + + return; +} + +static void +gail_map_cb (GtkWidget *widget) +{ + gail_finish_select (widget); +} + +static void +gail_map_submenu_cb (GtkWidget *widget) +{ + if (GTK_IS_MENU (widget)) + { + if (GTK_MENU (widget)->parent_menu_item) + gail_finish_select (GTK_MENU (widget)->parent_menu_item); + } +} + + +static gboolean +gail_deselect_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + GtkWidget *menu_shell; + + object = g_value_get_object (param_values + 0); + g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE); + + widget = GTK_WIDGET (object); + + if (!GTK_IS_MENU_ITEM (widget)) + return TRUE; + + if (subsequent_focus_widget == widget) + subsequent_focus_widget = NULL; + + menu_shell = gtk_widget_get_parent (widget); + if (GTK_IS_MENU_SHELL (menu_shell)) + { + GtkWidget *parent_menu_shell; + + parent_menu_shell = GTK_MENU_SHELL (menu_shell)->parent_menu_shell; + if (parent_menu_shell) + { + GtkWidget *active_menu_item; + + active_menu_item = GTK_MENU_SHELL (parent_menu_shell)->active_menu_item; + if (active_menu_item) + { + gail_focus_notify_when_idle (active_menu_item); + } + } + else + { + if (!GTK_IS_MENU_BAR (menu_shell)) + { + gail_focus_notify_when_idle (menu_shell); + } + } + } + was_deselect = TRUE; + return TRUE; +} + +static gboolean +gail_switch_page_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + GtkNotebook *notebook; + + object = g_value_get_object (param_values + 0); + g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE); + + widget = GTK_WIDGET (object); + + if (!GTK_IS_NOTEBOOK (widget)) + return TRUE; + + notebook = GTK_NOTEBOOK (widget); + if (!notebook->focus_tab) + return TRUE; + + gail_focus_notify_when_idle (widget); + return TRUE; +} + + +static gint +gail_focus_idle_handler (gpointer data) +{ + GDK_THREADS_ENTER(); + + focus_notify_handler = 0; + /* + * The widget which was to receive focus may have been removed + */ + if (!next_focus_widget) + { + if (next_focus_widget != data) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + } + else + { + void *vp_next_focus_widget = &next_focus_widget; + g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + next_focus_widget = NULL; + } + + gail_focus_notify (data); + + GDK_THREADS_LEAVE (); + return FALSE; +} + +static void +gail_focus_notify (GtkWidget *widget) +{ + AtkObject *atk_obj; + gboolean transient; + + if (widget != focus_widget) + { + if (focus_widget) + { + void *vp_focus_widget = &focus_widget; + g_object_remove_weak_pointer (G_OBJECT (focus_widget), vp_focus_widget); + } + focus_widget = widget; + if (focus_widget) + { + void *vp_focus_widget = &focus_widget; + g_object_add_weak_pointer (G_OBJECT (focus_widget), vp_focus_widget); + /* + * The UI may not have been updated yet; e.g. in gtkhtml2 + * html_view_layout() is called in a idle handler + */ + if (focus_widget == focus_before_menu) + { + void *vp_focus_before_menu = &focus_before_menu; + g_object_remove_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu); + focus_before_menu = NULL; + } + } + gail_focus_notify_when_idle (focus_widget); + } + else + { + if (focus_widget) + atk_obj = gail_get_accessible_for_widget (focus_widget, &transient); + else + atk_obj = NULL; + /* + * Do not report focus on redundant object + */ + if (atk_obj && + (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT)) + atk_focus_tracker_notify (atk_obj); + if (atk_obj && transient) + g_object_unref (atk_obj); + if (subsequent_focus_widget) + { + GtkWidget *tmp_widget = subsequent_focus_widget; + subsequent_focus_widget = NULL; + gail_focus_notify_when_idle (tmp_widget); + } + } +} + +static void +gail_focus_notify_when_idle (GtkWidget *widget) +{ + if (focus_notify_handler) + { + if (widget) + { + /* + * Ignore focus request when menu item is going to be focused. + * See bug #124232. + */ + if (GTK_IS_MENU_ITEM (next_focus_widget) && !GTK_IS_MENU_ITEM (widget)) + return; + + if (next_focus_widget) + { + if (GTK_IS_MENU_ITEM (next_focus_widget) && GTK_IS_MENU_ITEM (widget)) + { + if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (next_focus_widget)) == gtk_widget_get_parent (widget)) + { + if (subsequent_focus_widget) + g_assert_not_reached (); + subsequent_focus_widget = widget; + return; + } + } + } + g_source_remove (focus_notify_handler); + if (next_focus_widget) + { + void *vp_next_focus_widget = &next_focus_widget; + g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + next_focus_widget = NULL; + } + } + else + /* + * Ignore if focus is being set to NULL and we are waiting to set focus + */ + return; + } + + if (widget) + { + void *vp_next_focus_widget = &next_focus_widget; + next_focus_widget = widget; + g_object_add_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + } + else + { + /* + * We are about to report focus as NULL so remove the weak pointer + * for the widget we were waiting to report focus on. + */ + if (next_focus_widget) + { + void *vp_next_focus_widget = &next_focus_widget; + g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + next_focus_widget = NULL; + } + } + + focus_notify_handler = g_idle_add (gail_focus_idle_handler, widget); +} + +static gboolean +gail_deactivate_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + GtkMenuShell *shell; + GtkWidget *focus = NULL; + + object = g_value_get_object (param_values + 0); + g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE); + widget = GTK_WIDGET (object); + + g_return_val_if_fail (GTK_IS_MENU_SHELL(widget), TRUE); + shell = GTK_MENU_SHELL(widget); + if (!shell->parent_menu_shell) + focus = focus_before_menu; + + /* + * If we are waiting to report focus on a menubar or a menu item + * because of a previous deselect, cancel it. + */ + if (was_deselect && + focus_notify_handler && + next_focus_widget && + (GTK_IS_MENU_BAR (next_focus_widget) || + GTK_IS_MENU_ITEM (next_focus_widget))) + { + void *vp_next_focus_widget = &next_focus_widget; + g_source_remove (focus_notify_handler); + g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget); + next_focus_widget = NULL; + focus_notify_handler = 0; + was_deselect = FALSE; + } + gail_focus_notify_when_idle (focus); + + return TRUE; +} + +static void +gail_focus_tracker_init (void) +{ + static gboolean emission_hooks_added = FALSE; + + if (!emission_hooks_added) + { + /* + * We cannot be sure that the classes exist so we make sure that they do. + */ + gtk_type_class (GTK_TYPE_WIDGET); + gtk_type_class (GTK_TYPE_ITEM); + gtk_type_class (GTK_TYPE_MENU_SHELL); + gtk_type_class (GTK_TYPE_NOTEBOOK); + + /* + * We listen for event_after signal and then check that the + * event was a focus in event so we get called after the event. + */ + g_signal_add_emission_hook ( + g_signal_lookup ("event-after", GTK_TYPE_WIDGET), 0, + gail_focus_watcher, NULL, (GDestroyNotify) NULL); + /* + * A "select" signal is emitted when arrow key is used to + * move to a list item in the popup window of a GtkCombo or + * a menu item in a menu. + */ + g_signal_add_emission_hook ( + g_signal_lookup ("select", GTK_TYPE_ITEM), 0, + gail_select_watcher, NULL, (GDestroyNotify) NULL); + + /* + * A "deselect" signal is emitted when arrow key is used to + * move from a menu item in a menu to the parent menu. + */ + g_signal_add_emission_hook ( + g_signal_lookup ("deselect", GTK_TYPE_ITEM), 0, + gail_deselect_watcher, NULL, (GDestroyNotify) NULL); + + /* + * We listen for deactivate signals on menushells to determine + * when the "focus" has left the menus. + */ + g_signal_add_emission_hook ( + g_signal_lookup ("deactivate", GTK_TYPE_MENU_SHELL), 0, + gail_deactivate_watcher, NULL, (GDestroyNotify) NULL); + + /* + * We listen for "switch-page" signal on a GtkNotebook to notify + * when page has changed because of clicking on a notebook tab. + */ + g_signal_add_emission_hook ( + g_signal_lookup ("switch-page", GTK_TYPE_NOTEBOOK), 0, + gail_switch_page_watcher, NULL, (GDestroyNotify) NULL); + emission_hooks_added = TRUE; + } +} + +static void +gail_focus_object_destroyed (gpointer data) +{ + GObject *obj; + + obj = G_OBJECT (data); + g_object_set_qdata (obj, quark_focus_object, NULL); + g_object_unref (obj); +} + +static void +gail_focus_tracker (AtkObject *focus_object) +{ + /* + * Do not report focus on redundant object + */ + if (focus_object && + (atk_object_get_role(focus_object) != ATK_ROLE_REDUNDANT_OBJECT)) + { + AtkObject *old_focus_object; + + if (!GTK_IS_ACCESSIBLE (focus_object)) + { + AtkObject *parent; + + parent = focus_object; + while (1) + { + parent = atk_object_get_parent (parent); + if (parent == NULL) + break; + if (GTK_IS_ACCESSIBLE (parent)) + break; + } + + if (parent) + { + gail_set_focus_object (focus_object, parent); + } + } + else + { + old_focus_object = g_object_get_qdata (G_OBJECT (focus_object), quark_focus_object); + if (old_focus_object) + { + g_object_weak_unref (G_OBJECT (old_focus_object), + (GWeakNotify) gail_focus_object_destroyed, + focus_object); + g_object_set_qdata (G_OBJECT (focus_object), quark_focus_object, NULL); + g_object_unref (G_OBJECT (focus_object)); + } + } + } +} + +static void +gail_set_focus_widget (GtkWidget *focus_widget, + GtkWidget *widget) +{ + AtkObject *focus_obj; + AtkObject *obj; + + focus_obj = gtk_widget_get_accessible (focus_widget); + obj = gtk_widget_get_accessible (widget); + gail_set_focus_object (focus_obj, obj); +} + +static void +gail_set_focus_object (AtkObject *focus_obj, + AtkObject *obj) +{ + AtkObject *old_focus_obj; + + old_focus_obj = g_object_get_qdata (G_OBJECT (obj), quark_focus_object); + if (old_focus_obj != obj) + { + if (old_focus_obj) + g_object_weak_unref (G_OBJECT (old_focus_obj), + (GWeakNotify) gail_focus_object_destroyed, + obj); + else + /* + * We call g_object_ref as if obj is destroyed + * while the weak reference exists then destroying the + * focus_obj would cause gail_focus_object_destroyed to be + * called when obj is not a valid GObject. + */ + g_object_ref (obj); + + g_object_weak_ref (G_OBJECT (focus_obj), + (GWeakNotify) gail_focus_object_destroyed, + obj); + g_object_set_qdata (G_OBJECT (obj), quark_focus_object, focus_obj); + } +} + +/* + * These exported symbols are hooked by gnome-program + * to provide automatic module initialization and shutdown. + */ +extern void gnome_accessibility_module_init (void); +extern void gnome_accessibility_module_shutdown (void); + +static int gail_initialized = FALSE; + +static void +gail_accessibility_module_init (void) +{ + const char *env_a_t_support; + gboolean a_t_support = FALSE; + + if (gail_initialized) + { + return; + } + gail_initialized = TRUE; + quark_focus_object = g_quark_from_static_string ("gail-focus-object"); + + env_a_t_support = g_getenv (GNOME_ACCESSIBILITY_ENV); + + if (env_a_t_support) + a_t_support = atoi (env_a_t_support); + if (a_t_support) + fprintf (stderr, "GTK Accessibility Module initialized\n"); + + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_WIDGET, gail_widget); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CONTAINER, gail_container); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_BUTTON, gail_button); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ITEM, gail_item); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU_ITEM, gail_menu_item); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TOGGLE_BUTTON, gail_toggle_button); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_IMAGE, gail_image); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TEXT_VIEW, gail_text_view); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_COMBO, gail_combo); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_COMBO_BOX, gail_combo_box); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ENTRY, gail_entry); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU_BAR, gail_menu_shell); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU, gail_menu); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_WINDOW, gail_window); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RANGE, gail_range); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCALE, gail_scale); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CLIST, gail_clist); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LABEL, gail_label); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_STATUSBAR, gail_statusbar); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_NOTEBOOK, gail_notebook); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CALENDAR, gail_calendar); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PROGRESS_BAR, gail_progress_bar); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SPIN_BUTTON, gail_spin_button); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TREE_VIEW, gail_tree_view); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_FRAME, gail_frame); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_TEXT, gail_text_cell); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_TOGGLE, gail_boolean_cell); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_PIXBUF, gail_image_cell); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER, gail_renderer_cell); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RADIO_BUTTON, gail_radio_button); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ARROW, gail_arrow); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PIXMAP, gail_pixmap); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SEPARATOR, gail_separator); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_BOX, gail_box); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCROLLED_WINDOW, gail_scrolled_window); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LIST, gail_list); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PANED, gail_paned); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCROLLBAR, gail_scrollbar); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_OPTION_MENU, gail_option_menu); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CHECK_MENU_ITEM, gail_check_menu_item); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RADIO_MENU_ITEM, gail_radio_menu_item); + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_EXPANDER, gail_expander); + + /* LIBGNOMECANVAS SUPPORT */ + GAIL_WIDGET_SET_FACTORY (GTK_TYPE_OBJECT, gail_object); + + atk_focus_tracker_init (gail_focus_tracker_init); + focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker); + + /* Initialize the GailUtility class */ + g_type_class_unref (g_type_class_ref (GAIL_TYPE_UTIL)); + g_type_class_unref (g_type_class_ref (GAIL_TYPE_MISC)); +} + +/** + * gnome_accessibility_module_init: + * @void: + * + * This method is invoked by name from libgnome's + * gnome-program.c to activate accessibility support. + **/ +void +gnome_accessibility_module_init (void) +{ + gail_accessibility_module_init (); +} + +/** + * gnome_accessibility_module_shutdown: + * @void: + * + * This method is invoked by name from libgnome's + * gnome-program.c to de-activate accessibility support. + **/ +void +gnome_accessibility_module_shutdown (void) +{ + if (!gail_initialized) + { + return; + } + gail_initialized = FALSE; + atk_remove_focus_tracker (focus_tracker_id); + + fprintf (stderr, "GTK Accessibility Module shutdown\n"); + + /* FIXME: de-register the factory types so we can unload ? */ +} + +int +gtk_module_init (gint *argc, char** argv[]) +{ + gail_accessibility_module_init (); + + return 0; +} diff --git a/modules/other/gail/gail.h b/modules/other/gail/gail.h new file mode 100644 index 000000000..5ee6277c0 --- /dev/null +++ b/modules/other/gail/gail.h @@ -0,0 +1,54 @@ +#include <gail/gailadjustment.h> +#include <gail/gailarrow.h> +#include <gail/gailbooleancell.h> +#include <gail/gailbooleancellfactory.h> +#include <gail/gailbox.h> +#include <gail/gailbutton.h> +#include <gail/gailcalendar.h> +#include <gail/gailcell.h> +#include <gail/gailcheckmenuitem.h> +#include <gail/gailclist.h> +#include <gail/gailclistcell.h> +#include <gail/gailcombo.h> +#include <gail/gailcombobox.h> +#include <gail/gailcontainer.h> +#include <gail/gailcontainercell.h> +#include <gail/gailentry.h> +#include <gail/gailexpander.h> +#include <gail/gailframe.h> +#include <gail/gailimage.h> +#include <gail/gailimagecell.h> +#include <gail/gailimagecellfactory.h> +#include <gail/gailitem.h> +#include <gail/gaillabel.h> +#include <gail/gaillist.h> +#include <gail/gailmenu.h> +#include <gail/gailmenushell.h> +#include <gail/gailmenuitem.h> +#include <gail/gailnotebook.h> +#include <gail/gailobject.h> +#include <gail/gailobjectfactory.h> +#include <gail/gailoptionmenu.h> +#include <gail/gailpaned.h> +#include <gail/gailpixmap.h> +#include <gail/gailprogressbar.h> +#include <gail/gailradiobutton.h> +#include <gail/gailradiomenuitem.h> +#include <gail/gailrenderercell.h> +#include <gail/gailrenderercellfactory.h> +#include <gail/gailrange.h> +#include <gail/gailscale.h> +#include <gail/gailscrollbar.h> +#include <gail/gailscrolledwindow.h> +#include <gail/gailseparator.h> +#include <gail/gailspinbutton.h> +#include <gail/gailstatusbar.h> +#include <gail/gailtextcell.h> +#include <gail/gailtextcellfactory.h> +#include <gail/gailtextview.h> +#include <gail/gailtogglebutton.h> +#include <gail/gailtoplevel.h> +#include <gail/gailtreeview.h> +#include <gail/gailutil.h> +#include <gail/gailwidget.h> +#include <gail/gailwindow.h> diff --git a/modules/other/gail/gailadjustment.c b/modules/other/gail/gailadjustment.c new file mode 100644 index 000000000..3669bd57e --- /dev/null +++ b/modules/other/gail/gailadjustment.c @@ -0,0 +1,234 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailadjustment.h" + +static void gail_adjustment_class_init (GailAdjustmentClass *klass); + +static void gail_adjustment_real_initialize (AtkObject *obj, + gpointer data); + +static void atk_value_interface_init (AtkValueIface *iface); + +static void gail_adjustment_get_current_value (AtkValue *obj, + GValue *value); +static void gail_adjustment_get_maximum_value (AtkValue *obj, + GValue *value); +static void gail_adjustment_get_minimum_value (AtkValue *obj, + GValue *value); +static gboolean gail_adjustment_set_current_value (AtkValue *obj, + const GValue *value); + +static void gail_adjustment_destroyed (GtkAdjustment *adjustment, + GailAdjustment *gail_adjustment); + +static gpointer parent_class = NULL; + +GType +gail_adjustment_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailAdjustmentClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_adjustment_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailAdjustment), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_value_info = + { + (GInterfaceInitFunc) atk_value_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (ATK_TYPE_OBJECT, + "GailAdjustment", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_VALUE, + &atk_value_info); + } + return type; +} + +static void +gail_adjustment_class_init (GailAdjustmentClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->initialize = gail_adjustment_real_initialize; +} + +AtkObject* +gail_adjustment_new (GtkAdjustment *adjustment) +{ + GObject *object; + AtkObject *atk_object; + + g_return_val_if_fail (GTK_IS_ADJUSTMENT (adjustment), NULL); + + object = g_object_new (GAIL_TYPE_ADJUSTMENT, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, adjustment); + + return atk_object; +} + +static void +gail_adjustment_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkAdjustment *adjustment; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + adjustment = GTK_ADJUSTMENT (data); + + obj->role = ATK_ROLE_UNKNOWN; + GAIL_ADJUSTMENT (obj)->adjustment = adjustment; + + g_signal_connect_object (G_OBJECT (adjustment), + "destroy", + G_CALLBACK (gail_adjustment_destroyed), + obj, 0); +} + +static void +atk_value_interface_init (AtkValueIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_current_value = gail_adjustment_get_current_value; + iface->get_maximum_value = gail_adjustment_get_maximum_value; + iface->get_minimum_value = gail_adjustment_get_minimum_value; + iface->set_current_value = gail_adjustment_set_current_value; + +} + +static void +gail_adjustment_get_current_value (AtkValue *obj, + GValue *value) +{ + GtkAdjustment* adjustment; + gdouble current_value; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment == NULL) + { + /* State is defunct */ + return; + } + + current_value = adjustment->value; + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value,current_value); +} + +static void +gail_adjustment_get_maximum_value (AtkValue *obj, + GValue *value) +{ + GtkAdjustment* adjustment; + gdouble maximum_value; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment == NULL) + { + /* State is defunct */ + return; + } + + maximum_value = adjustment->upper; + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, maximum_value); +} + +static void +gail_adjustment_get_minimum_value (AtkValue *obj, + GValue *value) +{ + GtkAdjustment* adjustment; + gdouble minimum_value; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment == NULL) + { + /* State is defunct */ + return; + } + + minimum_value = adjustment->lower; + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, minimum_value); +} + +static gboolean +gail_adjustment_set_current_value (AtkValue *obj, + const GValue *value) +{ + if (G_VALUE_HOLDS_DOUBLE (value)) + { + GtkAdjustment* adjustment; + gdouble new_value; + + adjustment = GAIL_ADJUSTMENT (obj)->adjustment; + if (adjustment == NULL) + { + /* State is defunct */ + return FALSE; + } + new_value = g_value_get_double (value); + gtk_adjustment_set_value (adjustment, new_value); + + return TRUE; + } + else + return FALSE; +} + +static void +gail_adjustment_destroyed (GtkAdjustment *adjustment, + GailAdjustment *gail_adjustment) +{ + /* + * This is the signal handler for the "destroy" signal for the + * GtkAdjustment. We set the pointer location to NULL; + */ + gail_adjustment->adjustment = NULL; +} diff --git a/modules/other/gail/gailadjustment.h b/modules/other/gail/gailadjustment.h new file mode 100644 index 000000000..4a6b38b87 --- /dev/null +++ b/modules/other/gail/gailadjustment.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_ADJUSTMENT_H__ +#define __GAIL_ADJUSTMENT_H__ + +#include <atk/atk.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_ADJUSTMENT (gail_adjustment_get_type ()) +#define GAIL_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_ADJUSTMENT, GailAdjustment)) +#define GAIL_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_ADJUSTMENT, GailAdjustmentClass)) +#define GAIL_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_ADJUSTMENT)) +#define GAIL_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_ADJUSTMENT)) +#define GAIL_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_ADJUSTMENT, GailAdjustmentClass)) + +typedef struct _GailAdjustment GailAdjustment; +typedef struct _GailAdjustmentClass GailAdjustmentClass; + +struct _GailAdjustment +{ + AtkObject parent; + + GtkAdjustment *adjustment; +}; + +GType gail_adjustment_get_type (void); + +struct _GailAdjustmentClass +{ + AtkObjectClass parent_class; +}; + +AtkObject *gail_adjustment_new (GtkAdjustment *adjustment); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_ADJUSTMENT_H__ */ diff --git a/modules/other/gail/gailarrow.c b/modules/other/gail/gailarrow.c new file mode 100644 index 000000000..c20cbc411 --- /dev/null +++ b/modules/other/gail/gailarrow.c @@ -0,0 +1,164 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailarrow.h" + +static void gail_arrow_class_init (GailArrowClass *klass); +static void gail_arrow_object_init (GailArrow *arrow); + +/* AtkImage */ +static void atk_image_interface_init (AtkImageIface *iface); +static G_CONST_RETURN gchar* gail_arrow_get_image_description + (AtkImage *obj); +static gboolean gail_arrow_set_image_description + (AtkImage *obj, + const gchar *description); +static void gail_arrow_finalize (GObject *object); + +static GailWidgetClass* parent_class = NULL; + +GType +gail_arrow_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailArrowClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_arrow_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailArrow), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_arrow_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) atk_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailArrow", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + } + return type; +} + +static void +gail_arrow_class_init (GailArrowClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_arrow_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gail_arrow_object_init (GailArrow *arrow) +{ + arrow->image_description = NULL; +} + +AtkObject* +gail_arrow_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_ARROW (widget), NULL); + + object = g_object_new (GAIL_TYPE_ARROW, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + accessible->role = ATK_ROLE_ICON; + + return accessible; +} + +static void +atk_image_interface_init (AtkImageIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_image_description = gail_arrow_get_image_description; + iface->set_image_description = gail_arrow_set_image_description; +} + +static G_CONST_RETURN gchar* +gail_arrow_get_image_description (AtkImage *obj) +{ + GailArrow* arrow; + + g_return_val_if_fail(GAIL_IS_ARROW(obj), NULL); + + arrow = GAIL_ARROW (obj); + + return arrow->image_description; + +} + + +static gboolean +gail_arrow_set_image_description (AtkImage *obj, + const gchar *description) +{ + GailArrow* arrow; + + g_return_val_if_fail(GAIL_IS_ARROW(obj), FALSE); + + arrow = GAIL_ARROW (obj); + g_free (arrow->image_description); + + arrow->image_description = g_strdup (description); + + return TRUE; + +} + +/* + * static void + * gail_arrow_get_image_size (AtkImage *obj, + * gint *height, + * gint *width) + * + * We dont implement this function for GailArrow as gtk hardcodes the size + * of the arrow to be 7x5 and it is not possible to query this. + */ + +static void +gail_arrow_finalize (GObject *object) +{ + GailArrow *arrow = GAIL_ARROW (object); + + g_free (arrow->image_description); + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailarrow.h b/modules/other/gail/gailarrow.h new file mode 100644 index 000000000..b1e7fb272 --- /dev/null +++ b/modules/other/gail/gailarrow.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_ARROW_H__ +#define __GAIL_ARROW_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_ARROW (gail_arrow_get_type ()) +#define GAIL_ARROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_ARROW, GailArrow)) +#define GAIL_ARROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_ARROW, GailArrowClass)) +#define GAIL_IS_ARROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_ARROW)) +#define GAIL_IS_ARROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_ARROW)) +#define GAIL_ARROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_ARROW, GailArrowClass)) + +typedef struct _GailArrow GailArrow; +typedef struct _GailArrowClass GailArrowClass; + +struct _GailArrow +{ + GailWidget parent; + + gchar* image_description; +}; + +GType gail_arrow_get_type (void); + +struct _GailArrowClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_arrow_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_ARROW_H__ */ diff --git a/modules/other/gail/gailbooleancell.c b/modules/other/gail/gailbooleancell.c new file mode 100644 index 000000000..40590bf82 --- /dev/null +++ b/modules/other/gail/gailbooleancell.c @@ -0,0 +1,122 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailbooleancell.h" + +static void gail_boolean_cell_class_init (GailBooleanCellClass *klass); + +/* Misc */ + +static gboolean gail_boolean_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal); + +gchar *gail_boolean_cell_property_list[] = { + "active", + "radio", + NULL +}; + +GType +gail_boolean_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailBooleanCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_boolean_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailBooleanCell), /* instance size */ + 0, /* nb preallocs */ + NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_RENDERER_CELL, + "GailBooleanCell", &tinfo, 0); + gail_cell_type_add_action_interface (type); + } + return type; +} + +static void +gail_boolean_cell_class_init (GailBooleanCellClass *klass) +{ + GailRendererCellClass *renderer_cell_class = GAIL_RENDERER_CELL_CLASS (klass); + + renderer_cell_class->update_cache = gail_boolean_cell_update_cache; + renderer_cell_class->property_list = gail_boolean_cell_property_list; +} + +AtkObject* +gail_boolean_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + GailRendererCell *cell; + GailBooleanCell *boolean_cell; + + object = g_object_new (GAIL_TYPE_BOOLEAN_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + cell = GAIL_RENDERER_CELL(object); + boolean_cell = GAIL_BOOLEAN_CELL(object); + + cell->renderer = gtk_cell_renderer_toggle_new (); + g_object_ref (cell->renderer); + gtk_object_sink (GTK_OBJECT (cell->renderer)); + boolean_cell->cell_value = FALSE; + return atk_object; +} + +static gboolean +gail_boolean_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal) +{ + GailBooleanCell *boolean_cell = GAIL_BOOLEAN_CELL (cell); + gboolean rv = FALSE; + gboolean new_boolean; + + g_object_get (G_OBJECT(cell->renderer), "active", &new_boolean, NULL); + + if (boolean_cell->cell_value != new_boolean) + { + rv = TRUE; + boolean_cell->cell_value = !(boolean_cell->cell_value); + + /* Update cell's state */ + + if (new_boolean) + gail_cell_add_state (GAIL_CELL (cell), ATK_STATE_CHECKED, emit_change_signal); + else + gail_cell_remove_state (GAIL_CELL (cell), ATK_STATE_CHECKED, emit_change_signal); + } + + return rv; +} diff --git a/modules/other/gail/gailbooleancell.h b/modules/other/gail/gailbooleancell.h new file mode 100644 index 000000000..cc5dc2d6d --- /dev/null +++ b/modules/other/gail/gailbooleancell.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_BOOLEAN_CELL_H__ +#define __GAIL_BOOLEAN_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailrenderercell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_BOOLEAN_CELL (gail_boolean_cell_get_type ()) +#define GAIL_BOOLEAN_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_BOOLEAN_CELL, GailBooleanCell)) +#define GAIL_BOOLEAN_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_BOOLEAN_CELL, GailBooleanCellClass)) +#define GAIL_IS_BOOLEAN_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_BOOLEAN_CELL)) +#define GAIL_IS_BOOLEAN_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_BOOLEAN_CELL)) +#define GAIL_BOOLEAN_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_BOOLEAN_CELL, GailBooleanCellClass)) + +typedef struct _GailBooleanCell GailBooleanCell; +typedef struct _GailBooleanCellClass GailBooleanCellClass; + +struct _GailBooleanCell +{ + GailRendererCell parent; + gboolean cell_value; +}; + + GType gail_boolean_cell_get_type (void); + +struct _GailBooleanCellClass +{ + GailRendererCellClass parent_class; +}; + +AtkObject *gail_boolean_cell_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_BOOLEAN_CELL_H__ */ diff --git a/modules/other/gail/gailbooleancellfactory.c b/modules/other/gail/gailbooleancellfactory.c new file mode 100644 index 000000000..30f11d254 --- /dev/null +++ b/modules/other/gail/gailbooleancellfactory.c @@ -0,0 +1,78 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkcellrenderertoggle.h> +#include "gailbooleancellfactory.h" +#include "gailbooleancell.h" + +static void gail_boolean_cell_factory_class_init (GailBooleanCellFactoryClass *klass); + +static AtkObject* gail_boolean_cell_factory_create_accessible ( + GObject *obj); +static GType gail_boolean_cell_factory_get_accessible_type (void); + +GType +gail_boolean_cell_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailBooleanCellFactoryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_boolean_cell_factory_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailBooleanCellFactory), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + "GailBooleanCellFactory" , &tinfo, 0); + } + + return type; +} + +static void +gail_boolean_cell_factory_class_init (GailBooleanCellFactoryClass *klass) +{ + AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass); + + class->create_accessible = gail_boolean_cell_factory_create_accessible; + class->get_accessible_type = gail_boolean_cell_factory_get_accessible_type; +} + +static AtkObject* +gail_boolean_cell_factory_create_accessible (GObject *obj) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (obj), NULL); + + return gail_boolean_cell_new (); +} + +static GType +gail_boolean_cell_factory_get_accessible_type (void) +{ + return GAIL_TYPE_BOOLEAN_CELL; +} diff --git a/modules/other/gail/gailbooleancellfactory.h b/modules/other/gail/gailbooleancellfactory.h new file mode 100644 index 000000000..fca9c058d --- /dev/null +++ b/modules/other/gail/gailbooleancellfactory.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_BOOLEAN_CELL_FACTORY_H__ +#define __GAIL_BOOLEAN_CELL_FACTORY_H__ + +#include <atk/atkobjectfactory.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_BOOLEAN_CELL_FACTORY (gail_boolean_cell_factory_get_type ()) +#define GAIL_BOOLEAN_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_BOOLEAN_CELL_FACTORY, GailBooleanCellFactory)) +#define GAIL_BOOLEAN_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AGTK_TYPE_BOOLEAN_CELL_FACTORY, GailBooleanCellFactoryClass)) +#define GAIL_IS_BOOLEAN_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_BOOLEAN_CELL_FACTORY)) +#define GAIL_IS_BOOLEAN_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_BOOLEAN_CELL_FACTORY)) +#define GAIL_BOOLEAN_CELL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_BOOLEAN_CELL_FACTORY, GailBooleanCellFactoryClass)) + +typedef struct _GailBooleanCellFactory GailBooleanCellFactory; +typedef struct _GailBooleanCellFactoryClass GailBooleanCellFactoryClass; + +struct _GailBooleanCellFactory +{ + AtkObjectFactory parent; +}; + +struct _GailBooleanCellFactoryClass +{ + AtkObjectFactoryClass parent_class; +}; + +GType gail_boolean_cell_factory_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_BOOLEAN_CELL_FACTORY_H__ */ diff --git a/modules/other/gail/gailbox.c b/modules/other/gail/gailbox.c new file mode 100644 index 000000000..08b3ef865 --- /dev/null +++ b/modules/other/gail/gailbox.c @@ -0,0 +1,101 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailbox.h" + +static void gail_box_class_init (GailBoxClass *klass); +static AtkStateSet* gail_box_ref_state_set (AtkObject *accessible); + +static GailContainerClass *parent_class = NULL; + +GType +gail_box_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailBoxClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_box_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailBox), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailBox", &tinfo, 0); + } + return type; +} + +static void +gail_box_class_init (GailBoxClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_box_ref_state_set; +} + +AtkObject* +gail_box_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_BOX (widget), NULL); + + object = g_object_new (GAIL_TYPE_BOX, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_FILLER; + + return accessible; +} + +static AtkStateSet* +gail_box_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + if (GTK_IS_VBOX (widget) || GTK_IS_VBUTTON_BOX (widget)) + atk_state_set_add_state (state_set, ATK_STATE_VERTICAL); + else if (GTK_IS_HBOX (widget) || GTK_IS_HBUTTON_BOX (widget)) + atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL); + + return state_set; +} diff --git a/modules/other/gail/gailbox.h b/modules/other/gail/gailbox.h new file mode 100644 index 000000000..17c00ece0 --- /dev/null +++ b/modules/other/gail/gailbox.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_BOX_H__ +#define __GAIL_BOX_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_BOX (gail_box_get_type ()) +#define GAIL_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_BOX, GailBox)) +#define GAIL_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_BOX, GailBoxClass)) +#define GAIL_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_BOX)) +#define GAIL_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_BOX)) +#define GAIL_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_BOX, GailBoxClass)) + +typedef struct _GailBox GailBox; +typedef struct _GailBoxClass GailBoxClass; + +struct _GailBox +{ + GailContainer parent; +}; + +GType gail_box_get_type (void); + +struct _GailBoxClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_box_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_BOX_H__ */ diff --git a/modules/other/gail/gailbutton.c b/modules/other/gail/gailbutton.c new file mode 100644 index 000000000..46b7db33a --- /dev/null +++ b/modules/other/gail/gailbutton.c @@ -0,0 +1,1720 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailbutton.h" +#include <libgail-util/gailmisc.h> + +#define GAIL_BUTTON_ATTACHED_MENUS "gtk-attached-menus" + +static void gail_button_class_init (GailButtonClass *klass); +static void gail_button_object_init (GailButton *button); + +static G_CONST_RETURN gchar* gail_button_get_name (AtkObject *obj); +static gint gail_button_get_n_children (AtkObject *obj); +static AtkObject* gail_button_ref_child (AtkObject *obj, + gint i); +static AtkStateSet* gail_button_ref_state_set (AtkObject *obj); +static void gail_button_notify_label_gtk (GObject *obj, + GParamSpec *pspec, + gpointer data); +static void gail_button_label_map_gtk (GtkWidget *widget, + gpointer data); + +static void gail_button_real_initialize (AtkObject *obj, + gpointer data); +static void gail_button_finalize (GObject *object); +static void gail_button_init_textutil (GailButton *button, + GtkWidget *label); + +static void gail_button_pressed_enter_handler (GtkWidget *widget); +static void gail_button_released_leave_handler (GtkWidget *widget); +static gint gail_button_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); + + +static void atk_action_interface_init (AtkActionIface *iface); +static gboolean gail_button_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_button_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* gail_button_get_description(AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_button_get_keybinding (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_button_action_get_name(AtkAction *action, + gint i); +static gboolean gail_button_set_description(AtkAction *action, + gint i, + const gchar *desc); +static void gail_button_notify_label_weak_ref (gpointer data, + GObject *obj); +static void gail_button_notify_weak_ref (gpointer data, + GObject *obj); + + +/* AtkImage.h */ +static void atk_image_interface_init (AtkImageIface *iface); +static G_CONST_RETURN gchar* gail_button_get_image_description + (AtkImage *image); +static void gail_button_get_image_position + (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type); +static void gail_button_get_image_size (AtkImage *image, + gint *width, + gint *height); +static gboolean gail_button_set_image_description + (AtkImage *image, + const gchar *description); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_button_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_button_get_character_at_offset(AtkText *text, + gint offset); +static gchar* gail_button_get_text_before_offset(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_button_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_button_get_text_after_offset(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_button_get_character_count (AtkText *text); +static void gail_button_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_button_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_button_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_button_get_default_attributes + (AtkText *text); +static GtkImage* get_image_from_button (GtkWidget *button); +static GtkWidget* get_label_from_button (GtkWidget *button, + gint index, + gboolean allow_many); +static gint get_n_labels_from_button (GtkWidget *button); +static void set_role_for_button (AtkObject *accessible, + GtkWidget *button); + +static gint get_n_attached_menus (GtkWidget *widget); +static GtkWidget* get_nth_attached_menu (GtkWidget *widget, + gint index); + +static GailContainer* parent_class = NULL; + +GType +gail_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailButtonClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_button_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailButton), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_button_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) atk_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailButton", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + + } + + return type; +} + +static void +gail_button_class_init (GailButtonClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + GailContainerClass *container_class; + + widget_class = (GailWidgetClass*)klass; + container_class = (GailContainerClass*)klass; + + gobject_class->finalize = gail_button_finalize; + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_button_get_name; + class->get_n_children = gail_button_get_n_children; + class->ref_child = gail_button_ref_child; + class->ref_state_set = gail_button_ref_state_set; + class->initialize = gail_button_real_initialize; + + container_class->add_gtk = gail_button_real_add_gtk; + container_class->remove_gtk = NULL; +} + +static void +gail_button_object_init (GailButton *button) +{ + button->click_description = NULL; + button->press_description = NULL; + button->release_description = NULL; + button->click_keybinding = NULL; + button->action_queue = NULL; + button->action_idle_handler = 0; + button->textutil = NULL; +} + +AtkObject* +gail_button_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_BUTTON (widget), NULL); + + object = g_object_new (GAIL_TYPE_BUTTON, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static G_CONST_RETURN gchar* +gail_button_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar* name = NULL; + + g_return_val_if_fail (GAIL_IS_BUTTON (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name == NULL) + { + /* + * Get the text on the label + */ + GtkWidget *widget; + GtkWidget *child; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_BUTTON (widget), NULL); + + child = get_label_from_button (widget, 0, FALSE); + if (GTK_IS_LABEL (child)) + name = gtk_label_get_text (GTK_LABEL (child)); + else + { + GtkImage *image; + + image = get_image_from_button (widget); + if (GTK_IS_IMAGE (image)) + { + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (image)); + name = atk_object_get_name (atk_obj); + } + } + } + return name; +} + +/* + * A DownArrow in a GtkToggltButton whose parent is not a ColorCombo + * has press as default action. + */ +static gboolean +gail_button_is_default_press (GtkWidget *widget) +{ + GtkWidget *child; + GtkWidget *parent; + gboolean ret = FALSE; + const gchar *parent_type_name; + + child = GTK_BIN (widget)->child; + if (GTK_IS_ARROW (child) && + GTK_ARROW (child)->arrow_type == GTK_ARROW_DOWN) + { + parent = gtk_widget_get_parent (widget); + if (parent) + { + parent_type_name = g_type_name (G_OBJECT_TYPE (parent)); + if (strcmp (parent_type_name, "ColorCombo")) + return TRUE; + } + } + + return ret; +} + +static void +gail_button_real_initialize (AtkObject *obj, + gpointer data) +{ + GailButton *button = GAIL_BUTTON (obj); + GtkWidget *label; + GtkWidget *widget; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + button->state = GTK_STATE_NORMAL; + + g_signal_connect (data, + "pressed", + G_CALLBACK (gail_button_pressed_enter_handler), + NULL); + g_signal_connect (data, + "enter", + G_CALLBACK (gail_button_pressed_enter_handler), + NULL); + g_signal_connect (data, + "released", + G_CALLBACK (gail_button_released_leave_handler), + NULL); + g_signal_connect (data, + "leave", + G_CALLBACK (gail_button_released_leave_handler), + NULL); + + + widget = GTK_WIDGET (data); + label = get_label_from_button (widget, 0, FALSE); + if (GTK_IS_LABEL (label)) + { + if (GTK_WIDGET_MAPPED (label)) + gail_button_init_textutil (button, label); + else + g_signal_connect (label, + "map", + G_CALLBACK (gail_button_label_map_gtk), + button); + } + button->default_is_press = gail_button_is_default_press (widget); + + set_role_for_button (obj, data); +} + +static void +gail_button_label_map_gtk (GtkWidget *widget, + gpointer data) +{ + GailButton *button; + + button = GAIL_BUTTON (data); + gail_button_init_textutil (button, widget); +} + +static void +gail_button_notify_label_gtk (GObject *obj, + GParamSpec *pspec, + gpointer data) +{ + AtkObject* atk_obj = ATK_OBJECT (data); + GtkLabel *label; + GailButton *gail_button; + + if (strcmp (pspec->name, "label") == 0) + { + const gchar* label_text; + + label = GTK_LABEL (obj); + + label_text = gtk_label_get_text (label); + + gail_button = GAIL_BUTTON (atk_obj); + gail_text_util_text_setup (gail_button->textutil, label_text); + + if (atk_obj->name == NULL) + { + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (atk_obj), "accessible-name"); + } + /* + * The label is the only property which can be changed + */ + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } +} + +static void +gail_button_notify_weak_ref (gpointer data, GObject* obj) +{ + GtkLabel *label = NULL; + + AtkObject* atk_obj = ATK_OBJECT (obj); + if (data && GTK_IS_WIDGET (data)) + { + label = GTK_LABEL (data); + if (label) + { + g_signal_handlers_disconnect_by_func (label, + (GCallback) gail_button_notify_label_gtk, + GAIL_BUTTON (atk_obj)); + g_object_weak_unref (G_OBJECT (label), + gail_button_notify_label_weak_ref, + GAIL_BUTTON (atk_obj)); + } + } +} + +static void +gail_button_notify_label_weak_ref (gpointer data, GObject* obj) +{ + GtkLabel *label = NULL; + GailButton *button = NULL; + + label = GTK_LABEL (obj); + if (data && GAIL_IS_BUTTON (data)) + { + button = GAIL_BUTTON (ATK_OBJECT (data)); + if (button) + g_object_weak_unref (G_OBJECT (button), gail_button_notify_weak_ref, + label); + } +} + + +static void +gail_button_init_textutil (GailButton *button, + GtkWidget *label) +{ + const gchar *label_text; + + button->textutil = gail_text_util_new (); + label_text = gtk_label_get_text (GTK_LABEL (label)); + gail_text_util_text_setup (button->textutil, label_text); + g_object_weak_ref (G_OBJECT (button), + gail_button_notify_weak_ref, label); + g_object_weak_ref (G_OBJECT (label), + gail_button_notify_label_weak_ref, button); + g_signal_connect (label, + "notify", + (GCallback) gail_button_notify_label_gtk, + button); +} + +static gint +gail_button_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + GtkLabel *label; + GailButton *button; + + if (GTK_IS_LABEL (widget)) + { + const gchar* label_text; + + label = GTK_LABEL (widget); + + + button = GAIL_BUTTON (data); + if (!button->textutil) + gail_button_init_textutil (button, widget); + else + { + label_text = gtk_label_get_text (label); + gail_text_util_text_setup (button->textutil, label_text); + } + } + + return 1; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_button_do_action; + iface->get_n_actions = gail_button_get_n_actions; + iface->get_description = gail_button_get_description; + iface->get_keybinding = gail_button_get_keybinding; + iface->get_name = gail_button_action_get_name; + iface->set_description = gail_button_set_description; +} + +static gboolean +gail_button_do_action (AtkAction *action, + gint i) +{ + GtkWidget *widget; + GailButton *button; + gboolean return_value = TRUE; + + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + button = GAIL_BUTTON (action); + + switch (i) + { + case 0: + case 1: + case 2: + if (!button->action_queue) + { + button->action_queue = g_queue_new (); + } + g_queue_push_head (button->action_queue, (gpointer) i); + if (!button->action_idle_handler) + button->action_idle_handler = g_idle_add (idle_do_action, button); + break; + default: + return_value = FALSE; + break; + } + return return_value; +} + +static gboolean +idle_do_action (gpointer data) +{ + GtkButton *button; + GtkWidget *widget; + GailButton *gail_button; + GdkEvent tmp_event; + + GDK_THREADS_ENTER (); + + gail_button = GAIL_BUTTON (data); + gail_button->action_idle_handler = 0; + widget = GTK_ACCESSIBLE (gail_button)->widget; + tmp_event.button.type = GDK_BUTTON_RELEASE; + tmp_event.button.window = widget->window; + tmp_event.button.button = 1; + tmp_event.button.send_event = TRUE; + tmp_event.button.time = GDK_CURRENT_TIME; + tmp_event.button.axes = NULL; + + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + else + gtk_widget_event (widget, &tmp_event); + + button = GTK_BUTTON (widget); + while (!g_queue_is_empty (gail_button->action_queue)) + { + gint action_number = (gint) g_queue_pop_head (gail_button->action_queue); + if (gail_button->default_is_press) + { + if (action_number == 0) + action_number = 1; + else if (action_number == 1) + action_number = 0; + } + switch (action_number) + { + case 0: + gtk_widget_activate (widget); + break; + case 1: + button->in_button = TRUE; + gtk_button_enter (button); + /* + * Simulate a button press event. calling gtk_button_pressed() does + * not get the job done for a GtkOptionMenu. + */ + tmp_event.button.type = GDK_BUTTON_PRESS; + tmp_event.button.window = widget->window; + tmp_event.button.button = 1; + tmp_event.button.send_event = TRUE; + tmp_event.button.time = GDK_CURRENT_TIME; + tmp_event.button.axes = NULL; + + gtk_widget_event (widget, &tmp_event); + break; + case 2: + button->in_button = FALSE; + gtk_button_leave (button); + break; + default: + g_assert_not_reached (); + break; + } + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_button_get_n_actions (AtkAction *action) +{ + return 3; +} + +static G_CONST_RETURN gchar* +gail_button_get_description (AtkAction *action, + gint i) +{ + GailButton *button; + G_CONST_RETURN gchar *return_value; + + button = GAIL_BUTTON (action); + + if (button->default_is_press) + { + if (i == 0) + i = 1; + else if (i == 1) + i = 0; + } + switch (i) + { + case 0: + return_value = button->click_description; + break; + case 1: + return_value = button->press_description; + break; + case 2: + return_value = button->release_description; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_button_get_keybinding (AtkAction *action, + gint i) +{ + GailButton *button; + gchar *return_value = NULL; + + button = GAIL_BUTTON (action); + if (button->default_is_press) + { + if (i == 0) + i = 1; + else if (i == 1) + i = 0; + } + switch (i) + { + case 0: + { + /* + * We look for a mnemonic on the label + */ + GtkWidget *widget; + GtkWidget *label; + guint key_val; + + widget = GTK_ACCESSIBLE (button)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_BUTTON (widget), NULL); + + label = get_label_from_button (widget, 0, FALSE); + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + if (return_value == NULL) + { + /* Find labelled-by relation */ + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + if (set) + { + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + + target_object = g_ptr_array_index (target, 0); + if (GTK_IS_ACCESSIBLE (target_object)) + { + label = GTK_ACCESSIBLE (target_object)->widget; + } + } + g_object_unref (set); + } + + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + } + g_free (button->click_keybinding); + button->click_keybinding = return_value; + break; + } + default: + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_button_action_get_name (AtkAction *action, + gint i) +{ + G_CONST_RETURN gchar *return_value; + GailButton *button; + + button = GAIL_BUTTON (action); + + if (button->default_is_press) + { + if (i == 0) + i = 1; + else if (i == 1) + i = 0; + } + switch (i) + { + case 0: + /* + * This action is a "click" to activate a button or "toggle" to change + * the state of a toggle button check box or radio button. + */ + return_value = "click"; + break; + case 1: + /* + * This action simulates a button press by simulating moving the + * mouse into the button followed by pressing the left mouse button. + */ + return_value = "press"; + break; + case 2: + /* + * This action simulates releasing the left mouse button outside the + * button. + * + * To simulate releasing the left mouse button inside the button use + * the click action. + */ + return_value = "release"; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static gboolean +gail_button_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + GailButton *button; + gchar **value; + + button = GAIL_BUTTON (action); + + if (button->default_is_press) + { + if (i == 0) + i = 1; + else if (i == 1) + i = 0; + } + switch (i) + { + case 0: + value = &button->click_description; + break; + case 1: + value = &button->press_description; + break; + case 2: + value = &button->release_description; + break; + default: + value = NULL; + break; + } + if (value) + { + g_free (*value); + *value = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} + +static gint +gail_button_get_n_children (AtkObject* obj) +{ + GtkWidget *widget; + gint n_children; + + g_return_val_if_fail (GAIL_IS_BUTTON (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + /* + * Check whether we have an attached menus for PanelMenuButton + */ + n_children = get_n_attached_menus (widget); + if (n_children > 0) + return n_children; + + n_children = get_n_labels_from_button (widget); + if (n_children <= 1) + n_children = 0; + + return n_children; +} + +static AtkObject* +gail_button_ref_child (AtkObject *obj, + gint i) +{ + GtkWidget *widget; + GtkWidget *child_widget; + AtkObject *child; + + g_return_val_if_fail (GAIL_IS_BUTTON (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + if (i >= gail_button_get_n_children (obj)) + return NULL; + + if (get_n_attached_menus (widget) > 0) + { + child_widget = get_nth_attached_menu (widget, i); + } + else + child_widget = NULL; + + if (!child_widget) + { + if (get_n_labels_from_button (widget) > 1) + { + child_widget = get_label_from_button (widget, i, TRUE); + } + } + + if (child_widget) + { + child = gtk_widget_get_accessible (child_widget); + g_object_ref (child); + } + else + child = NULL; + + return child; +} + +static AtkStateSet* +gail_button_ref_state_set (AtkObject *obj) +{ + AtkStateSet *state_set; + GtkWidget *widget; + GtkButton *button; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget == NULL) + return state_set; + + button = GTK_BUTTON (widget); + + if (GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE) + atk_state_set_add_state (state_set, ATK_STATE_ARMED); + + return state_set; +} + +/* + * This is the signal handler for the "pressed" or "enter" signal handler + * on the GtkButton. + * + * If the state is now GTK_STATE_ACTIVE we notify a property change + */ +static void +gail_button_pressed_enter_handler (GtkWidget *widget) +{ + AtkObject *accessible; + + if (GTK_WIDGET_STATE (widget) == GTK_STATE_ACTIVE) + { + accessible = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (accessible, ATK_STATE_ARMED, TRUE); + GAIL_BUTTON (accessible)->state = GTK_STATE_ACTIVE; + } +} + +/* + * This is the signal handler for the "released" or "leave" signal handler + * on the GtkButton. + * + * If the state was GTK_STATE_ACTIVE we notify a property change + */ +static void +gail_button_released_leave_handler (GtkWidget *widget) +{ + AtkObject *accessible; + + accessible = gtk_widget_get_accessible (widget); + if (GAIL_BUTTON (accessible)->state == GTK_STATE_ACTIVE) + { + atk_object_notify_state_change (accessible, ATK_STATE_ARMED, FALSE); + GAIL_BUTTON (accessible)->state = GTK_STATE_NORMAL; + } +} + +static void +atk_image_interface_init (AtkImageIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_image_description = gail_button_get_image_description; + iface->get_image_position = gail_button_get_image_position; + iface->get_image_size = gail_button_get_image_size; + iface->set_image_description = gail_button_set_image_description; +} + +static GtkImage* +get_image_from_button (GtkWidget *button) +{ + GtkWidget *child; + GList *list; + GtkImage *image = NULL; + + child = gtk_bin_get_child (GTK_BIN (button)); + if (GTK_IS_IMAGE (child)) + image = GTK_IMAGE (child); + else + { + if (GTK_IS_ALIGNMENT (child)) + child = gtk_bin_get_child (GTK_BIN (child)); + if (GTK_IS_CONTAINER (child)) + { + list = gtk_container_get_children (GTK_CONTAINER (child)); + if (!list) + return NULL; + if (GTK_IS_IMAGE (list->data)) + image = GTK_IMAGE (list->data); + g_list_free (list); + } + } + + return image; +} + +static G_CONST_RETURN gchar* +gail_button_get_image_description (AtkImage *image) { + + GtkWidget *widget; + GtkImage *button_image; + AtkObject *obj; + + widget = GTK_ACCESSIBLE (image)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + button_image = get_image_from_button (widget); + + if (button_image != NULL) + { + obj = gtk_widget_get_accessible (GTK_WIDGET (button_image)); + return atk_image_get_image_description (ATK_IMAGE (obj)); + } + else + return NULL; +} + +static void +gail_button_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + GtkWidget *widget; + GtkImage *button_image; + AtkObject *obj; + + widget = GTK_ACCESSIBLE (image)->widget; + + if (widget == NULL) + { + /* + * State is defunct + */ + *x = G_MININT; + *y = G_MININT; + return; + } + + button_image = get_image_from_button (widget); + + if (button_image != NULL) + { + obj = gtk_widget_get_accessible (GTK_WIDGET (button_image)); + atk_component_get_position (ATK_COMPONENT (obj), x, y, coord_type); + } + else + { + *x = G_MININT; + *y = G_MININT; + } +} + +static void +gail_button_get_image_size (AtkImage *image, + gint *width, + gint *height) +{ + GtkWidget *widget; + GtkImage *button_image; + AtkObject *obj; + + widget = GTK_ACCESSIBLE (image)->widget; + + if (widget == NULL) + { + /* + * State is defunct + */ + *width = -1; + *height = -1; + return; + } + + button_image = get_image_from_button (widget); + + if (button_image != NULL) + { + obj = gtk_widget_get_accessible (GTK_WIDGET (button_image)); + atk_image_get_image_size (ATK_IMAGE (obj), width, height); + } + else + { + *width = -1; + *height = -1; + } +} + +static gboolean +gail_button_set_image_description (AtkImage *image, + const gchar *description) +{ + GtkWidget *widget; + GtkImage *button_image; + AtkObject *obj; + + widget = GTK_ACCESSIBLE (image)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + button_image = get_image_from_button (widget); + + if (button_image != NULL) + { + obj = gtk_widget_get_accessible (GTK_WIDGET (button_image)); + return atk_image_set_image_description (ATK_IMAGE (obj), description); + } + else + return FALSE; +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_button_get_text; + iface->get_character_at_offset = gail_button_get_character_at_offset; + iface->get_text_before_offset = gail_button_get_text_before_offset; + iface->get_text_at_offset = gail_button_get_text_at_offset; + iface->get_text_after_offset = gail_button_get_text_after_offset; + iface->get_character_count = gail_button_get_character_count; + iface->get_character_extents = gail_button_get_character_extents; + iface->get_offset_at_point = gail_button_get_offset_at_point; + iface->get_run_attributes = gail_button_get_run_attributes; + iface->get_default_attributes = gail_button_get_default_attributes; +} + +static gchar* +gail_button_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkWidget *label; + GailButton *button; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL (label)) + return NULL; + + button = GAIL_BUTTON (text); + if (!button->textutil) + gail_button_init_textutil (button, label); + + label_text = gtk_label_get_text (GTK_LABEL (label)); + + if (label_text == NULL) + return NULL; + else + { + return gail_text_util_get_substring (button->textutil, + start_pos, end_pos); + } +} + +static gchar* +gail_button_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailButton *button; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return NULL; + + button = GAIL_BUTTON (text); + if (!button->textutil) + gail_button_init_textutil (button, label); + + return gail_text_util_get_text (button->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_button_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailButton *button; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return NULL; + + button = GAIL_BUTTON (text); + if (!button->textutil) + gail_button_init_textutil (button, label); + + return gail_text_util_get_text (button->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_button_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailButton *button; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + { + /* State is defunct */ + return NULL; + } + + /* Get label */ + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return NULL; + + button = GAIL_BUTTON (text); + if (!button->textutil) + gail_button_init_textutil (button, label); + + return gail_text_util_get_text (button->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_button_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return 0; + + return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1); +} + +static void +gail_button_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + label_text = gtk_label_get_text (GTK_LABEL (label)); + index = g_utf8_offset_to_pointer (label_text, offset) - label_text; + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (label, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_button_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return -1; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (label, + gtk_label_get_layout (GTK_LABEL (label)), + x_layout, y_layout, x, y, coords); + label_text = gtk_label_get_text (GTK_LABEL (label)); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label_text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label_text, label_text + index); +} + +static AtkAttributeSet* +gail_button_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return NULL; + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (GTK_LABEL (label)); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (label); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + (gchar *) gtk_label_get_text (GTK_LABEL (label)), + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_button_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return NULL; + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + widget); + return at_set; +} + +static gunichar +gail_button_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkWidget *label; + const gchar *string; + gchar *index; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + label = get_label_from_button (widget, 0, FALSE); + + if (!GTK_IS_LABEL(label)) + return '\0'; + string = gtk_label_get_text (GTK_LABEL (label)); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + +static void +gail_button_finalize (GObject *object) +{ + GailButton *button = GAIL_BUTTON (object); + + g_free (button->click_description); + g_free (button->press_description); + g_free (button->release_description); + g_free (button->click_keybinding); + if (button->action_idle_handler) + { + g_source_remove (button->action_idle_handler); + button->action_idle_handler = 0; + } + if (button->action_queue) + { + g_queue_free (button->action_queue); + } + if (button->textutil) + { + g_object_unref (button->textutil); + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GtkWidget* +find_label_child (GtkContainer *container, + gint *index, + gboolean allow_many) +{ + GList *children, *tmp_list; + GtkWidget *child; + + children = gtk_container_get_children (container); + + child = NULL; + for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) + { + if (GTK_IS_LABEL (tmp_list->data)) + { + if (!allow_many) + { + if (child) + { + child = NULL; + break; + } + child = GTK_WIDGET (tmp_list->data); + } + else + { + if (*index == 0) + { + child = GTK_WIDGET (tmp_list->data); + break; + } + (*index)--; + } + } + /* + * Label for button which are GtkTreeView column headers are in a + * GtkHBox in a GtkAlignment. + */ + else if (GTK_IS_ALIGNMENT (tmp_list->data)) + { + GtkWidget *widget; + + widget = gtk_bin_get_child (GTK_BIN (tmp_list->data)); + if (GTK_IS_LABEL (widget)) + { + if (!allow_many) + { + if (child) + { + child = NULL; + break; + } + child = widget; + } + else + { + if (*index == 0) + { + child = widget; + break; + } + (*index)--; + } + } + } + else if (GTK_IS_CONTAINER (tmp_list->data)) + { + child = find_label_child (GTK_CONTAINER (tmp_list->data), index, allow_many); + if (child) + break; + } + } + g_list_free (children); + return child; +} + +static GtkWidget* +get_label_from_button (GtkWidget *button, + gint index, + gboolean allow_many) +{ + GtkWidget *child; + + if (index > 0 && !allow_many) + g_warning ("Inconsistent values passed to get_label_from_button"); + + child = gtk_bin_get_child (GTK_BIN (button)); + if (GTK_IS_ALIGNMENT (child)) + child = gtk_bin_get_child (GTK_BIN (child)); + + if (GTK_IS_CONTAINER (child)) + child = find_label_child (GTK_CONTAINER (child), &index, allow_many); + else if (!GTK_IS_LABEL (child)) + child = NULL; + + return child; +} + +static void +count_labels (GtkContainer *container, + gint *n_labels) +{ + GList *children, *tmp_list; + + children = gtk_container_get_children (container); + + for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) + { + if (GTK_IS_LABEL (tmp_list->data)) + { + (*n_labels)++; + } + /* + * Label for button which are GtkTreeView column headers are in a + * GtkHBox in a GtkAlignment. + */ + else if (GTK_IS_ALIGNMENT (tmp_list->data)) + { + GtkWidget *widget; + + widget = gtk_bin_get_child (GTK_BIN (tmp_list->data)); + if (GTK_IS_LABEL (widget)) + (*n_labels)++; + } + else if (GTK_IS_CONTAINER (tmp_list->data)) + { + count_labels (GTK_CONTAINER (tmp_list->data), n_labels); + } + } + g_list_free (children); +} + +static gint +get_n_labels_from_button (GtkWidget *button) +{ + GtkWidget *child; + gint n_labels; + + n_labels = 0; + + child = gtk_bin_get_child (GTK_BIN (button)); + if (GTK_IS_ALIGNMENT (child)) + child = gtk_bin_get_child (GTK_BIN (child)); + + if (GTK_IS_CONTAINER (child)) + count_labels (GTK_CONTAINER (child), &n_labels); + + return n_labels; +} + +static void +set_role_for_button (AtkObject *accessible, + GtkWidget *button) +{ + GtkWidget *parent; + AtkRole role; + + parent = gtk_widget_get_parent (button); + if (GTK_IS_TREE_VIEW (parent)) + { + role = ATK_ROLE_TABLE_COLUMN_HEADER; + /* + * Even though the accessible parent of the column header will + * be reported as the table because the parent widget of the + * GtkTreeViewColumn's button is the GtkTreeView we set + * the accessible parent for column header to be the table + * to ensure that atk_object_get_index_in_parent() returns + * the correct value; see gail_widget_get_index_in_parent(). + */ + atk_object_set_parent (accessible, gtk_widget_get_accessible (parent)); + } + else + role = ATK_ROLE_PUSH_BUTTON; + + accessible->role = role; +} + +static gint +get_n_attached_menus (GtkWidget *widget) +{ + GList *list_menus; + + if (widget == NULL) + return 0; + + list_menus = g_object_get_data (G_OBJECT (widget), GAIL_BUTTON_ATTACHED_MENUS); + if (list_menus == NULL) + return 0; + + return g_list_length (list_menus); +} + +static GtkWidget* +get_nth_attached_menu (GtkWidget *widget, + gint index) +{ + GtkWidget *attached_menu; + GList *list_menus; + + if (widget == NULL) + return NULL; + + list_menus = g_object_get_data (G_OBJECT (widget), GAIL_BUTTON_ATTACHED_MENUS); + if (list_menus == NULL || + index >= g_list_length (list_menus)) + return NULL; + + attached_menu = (GtkWidget *) g_list_nth_data (list_menus, index); + + return attached_menu; +} diff --git a/modules/other/gail/gailbutton.h b/modules/other/gail/gailbutton.h new file mode 100644 index 000000000..2550af921 --- /dev/null +++ b/modules/other/gail/gailbutton.h @@ -0,0 +1,77 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_BUTTON_H__ +#define __GAIL_BUTTON_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> +#include <libgail-util/gailtextutil.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_BUTTON (gail_button_get_type ()) +#define GAIL_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_BUTTON, GailButton)) +#define GAIL_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_BUTTON, GailButtonClass)) +#define GAIL_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_BUTTON)) +#define GAIL_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_BUTTON)) +#define GAIL_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_BUTTON, GailButtonClass)) + +typedef struct _GailButton GailButton; +typedef struct _GailButtonClass GailButtonClass; + +struct _GailButton +{ + GailContainer parent; + + /* + * Cache the widget state so we know the previous state when it changed + */ + gint8 state; + + gchar *click_description; + gchar *press_description; + gchar *release_description; + gchar *click_keybinding; + guint action_idle_handler; + GQueue *action_queue; + + GailTextUtil *textutil; + + gboolean default_is_press; +}; + +GType gail_button_get_type (void); + +struct _GailButtonClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_button_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_BUTTON_H__ */ diff --git a/modules/other/gail/gailcalendar.c b/modules/other/gail/gailcalendar.c new file mode 100644 index 000000000..7d74f54e2 --- /dev/null +++ b/modules/other/gail/gailcalendar.c @@ -0,0 +1,73 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailcalendar.h" + +static void gail_calendar_class_init (GailCalendarClass *klass); + +GType +gail_calendar_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCalendarClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_calendar_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCalendar), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailCalendar", &tinfo, 0); + } + return type; +} + +static void +gail_calendar_class_init (GailCalendarClass *klass) +{ +} + +AtkObject* +gail_calendar_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_CALENDAR (widget), NULL); + + object = g_object_new (GAIL_TYPE_CALENDAR, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_CALENDAR; + + return accessible; +} diff --git a/modules/other/gail/gailcalendar.h b/modules/other/gail/gailcalendar.h new file mode 100644 index 000000000..568f4c478 --- /dev/null +++ b/modules/other/gail/gailcalendar.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CALENDAR_H__ +#define __GAIL_CALENDAR_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CALENDAR (gail_calendar_get_type ()) +#define GAIL_CALENDAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CALENDAR, GailCalendar)) +#define GAIL_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CALENDAR, GailCalendarClass)) +#define GAIL_IS_CALENDAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CALENDAR)) +#define GAIL_IS_CALENDAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CALENDAR)) +#define GAIL_CALENDAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CALENDAR, GailCalendarClass)) + +typedef struct _GailCalendar GailCalendar; +typedef struct _GailCalendarClass GailCalendarClass; + +struct _GailCalendar +{ + GailWidget parent; +}; + +GType gail_calendar_get_type (void); + +struct _GailCalendarClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_calendar_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CALENDAR_H__ */ diff --git a/modules/other/gail/gailcell.c b/modules/other/gail/gailcell.c new file mode 100644 index 000000000..684863186 --- /dev/null +++ b/modules/other/gail/gailcell.c @@ -0,0 +1,577 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <gtk/gtk.h> +#include "gailcontainercell.h" +#include "gailcell.h" +#include "gailcellparent.h" + +static void gail_cell_class_init (GailCellClass *klass); +static void gail_cell_destroyed (GtkWidget *widget, + GailCell *cell); + +static void gail_cell_object_init (GailCell *cell); +static void gail_cell_object_finalize (GObject *cell); +static AtkStateSet* gail_cell_ref_state_set (AtkObject *obj); +static gint gail_cell_get_index_in_parent (AtkObject *obj); + +/* AtkAction */ + +static void gail_cell_atk_action_interface_init + (AtkActionIface *iface); +static ActionInfo * _gail_cell_get_action_info (GailCell *cell, + gint index); +static void _gail_cell_destroy_action_info + (gpointer data, + gpointer user_data); + +static gint gail_cell_action_get_n_actions + (AtkAction *action); +static G_CONST_RETURN gchar* + gail_cell_action_get_name (AtkAction *action, + gint index); +static G_CONST_RETURN gchar * + gail_cell_action_get_description + (AtkAction *action, + gint index); +static gboolean gail_cell_action_set_description + (AtkAction *action, + gint index, + const gchar *desc); +static G_CONST_RETURN gchar * + gail_cell_action_get_keybinding + (AtkAction *action, + gint index); +static gboolean gail_cell_action_do_action (AtkAction *action, + gint index); +static gboolean idle_do_action (gpointer data); + +static void atk_component_interface_init (AtkComponentIface *iface); +static void gail_cell_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); +static gboolean gail_cell_grab_focus (AtkComponent *component); + +static gpointer parent_class = NULL; + +GType +gail_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_cell_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) atk_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + + type = g_type_register_static (ATK_TYPE_OBJECT, + "GailCell", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + + } + return type; +} + +static void +gail_cell_class_init (GailCellClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + g_object_class->finalize = gail_cell_object_finalize; + + class->get_index_in_parent = gail_cell_get_index_in_parent; + class->ref_state_set = gail_cell_ref_state_set; +} + +void +gail_cell_init (GailCell *cell, + GtkWidget *widget, + AtkObject *parent, + gint index) +{ + g_return_if_fail (GAIL_IS_CELL (cell)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + cell->widget = widget; + atk_object_set_parent (ATK_OBJECT (cell), parent); + cell->index = index; + + g_signal_connect_object (G_OBJECT (widget), + "destroy", + G_CALLBACK (gail_cell_destroyed ), + cell, 0); +} + +static void +gail_cell_destroyed (GtkWidget *widget, + GailCell *cell) +{ + /* + * This is the signal handler for the "destroy" signal for the + * GtkWidget. We set the pointer location to NULL; + */ + cell->widget = NULL; +} + +static void +gail_cell_object_init (GailCell *cell) +{ + cell->state_set = atk_state_set_new (); + cell->widget = NULL; + cell->action_list = NULL; + cell->index = 0; + atk_state_set_add_state (cell->state_set, ATK_STATE_TRANSIENT); + atk_state_set_add_state (cell->state_set, ATK_STATE_ENABLED); + atk_state_set_add_state (cell->state_set, ATK_STATE_SENSITIVE); + atk_state_set_add_state (cell->state_set, ATK_STATE_SELECTABLE); + cell->refresh_index = NULL; +} + +static void +gail_cell_object_finalize (GObject *obj) +{ + GailCell *cell = GAIL_CELL (obj); + AtkRelationSet *relation_set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + gint i; + + if (cell->state_set) + g_object_unref (cell->state_set); + g_list_free (cell->action_list); + if (cell->action_idle_handler) + { + g_source_remove (cell->action_idle_handler); + cell->action_idle_handler = 0; + } + relation_set = atk_object_ref_relation_set (ATK_OBJECT (obj)); + if (ATK_IS_RELATION_SET (relation_set)) + { + relation = atk_relation_set_get_relation_by_type (relation_set, + ATK_RELATION_NODE_CHILD_OF); + if (relation) + { + target = atk_relation_get_target (relation); + for (i = 0; i < target->len; i++) + { + target_object = g_ptr_array_index (target, i); + if (GAIL_IS_CELL (target_object)) + { + g_object_unref (target_object); + } + } + } + g_object_unref (relation_set); + } + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static AtkStateSet * +gail_cell_ref_state_set (AtkObject *obj) +{ + GailCell *cell = GAIL_CELL (obj); + g_assert (cell->state_set); + + g_object_ref(cell->state_set); + return cell->state_set; +} + +gboolean +gail_cell_add_state (GailCell *cell, + AtkStateType state_type, + gboolean emit_signal) +{ + if (!atk_state_set_contains_state (cell->state_set, state_type)) + { + gboolean rc; + AtkObject *parent; + + rc = atk_state_set_add_state (cell->state_set, state_type); + /* + * The signal should only be generated if the value changed, + * not when the cell is set up. So states that are set + * initially should pass FALSE as the emit_signal argument. + */ + + if (emit_signal) + { + atk_object_notify_state_change (ATK_OBJECT (cell), state_type, TRUE); + /* If state_type is ATK_STATE_VISIBLE, additional notification */ + if (state_type == ATK_STATE_VISIBLE) + g_signal_emit_by_name (cell, "visible_data_changed"); + } + + /* + * If the parent is a flyweight container cell, propagate the state + * change to it also + */ + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (GAIL_IS_CONTAINER_CELL (parent)) + gail_cell_add_state (GAIL_CELL (parent), state_type, emit_signal); + return rc; + } + else + return FALSE; +} + +gboolean +gail_cell_remove_state (GailCell *cell, + AtkStateType state_type, + gboolean emit_signal) +{ + if (atk_state_set_contains_state (cell->state_set, state_type)) + { + gboolean rc; + AtkObject *parent; + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + + rc = atk_state_set_remove_state (cell->state_set, state_type); + /* + * The signal should only be generated if the value changed, + * not when the cell is set up. So states that are set + * initially should pass FALSE as the emit_signal argument. + */ + + if (emit_signal) + { + atk_object_notify_state_change (ATK_OBJECT (cell), state_type, FALSE); + /* If state_type is ATK_STATE_VISIBLE, additional notification */ + if (state_type == ATK_STATE_VISIBLE) + g_signal_emit_by_name (cell, "visible_data_changed"); + } + + /* + * If the parent is a flyweight container cell, propagate the state + * change to it also + */ + + if (GAIL_IS_CONTAINER_CELL (parent)) + gail_cell_remove_state (GAIL_CELL (parent), state_type, emit_signal); + return rc; + } + else + return FALSE; +} + +static gint +gail_cell_get_index_in_parent (AtkObject *obj) +{ + GailCell *cell; + + g_assert (GAIL_IS_CELL (obj)); + + cell = GAIL_CELL (obj); + if (atk_state_set_contains_state (cell->state_set, ATK_STATE_STALE)) + if (cell->refresh_index) + { + cell->refresh_index (cell); + atk_state_set_remove_state (cell->state_set, ATK_STATE_STALE); + } + return cell->index; +} + +static void +gail_cell_atk_action_interface_init (AtkActionIface *iface) +{ + g_assert (iface != NULL); + + iface->get_n_actions = gail_cell_action_get_n_actions; + iface->do_action = gail_cell_action_do_action; + iface->get_name = gail_cell_action_get_name; + iface->get_description = gail_cell_action_get_description; + iface->set_description = gail_cell_action_set_description; + iface->get_keybinding = gail_cell_action_get_keybinding; +} + +void +gail_cell_type_add_action_interface (GType type) +{ + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) gail_cell_atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); +} + +gboolean +gail_cell_add_action (GailCell *cell, + const gchar *action_name, + const gchar *action_description, + const gchar *action_keybinding, + ACTION_FUNC action_func) +{ + ActionInfo *info; + g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE); + info = g_new (ActionInfo, 1); + + if (action_name != NULL) + info->name = g_strdup (action_name); + else + info->name = NULL; + if (action_description != NULL) + info->description = g_strdup (action_description); + else + info->description = NULL; + if (action_keybinding != NULL) + info->keybinding = g_strdup (action_keybinding); + else + info->keybinding = NULL; + info->do_action_func = action_func; + + cell->action_list = g_list_append (cell->action_list, (gpointer) info); + return TRUE; +} + +gboolean +gail_cell_remove_action (GailCell *cell, + gint action_index) +{ + GList *list_node; + + g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE); + list_node = g_list_nth (cell->action_list, action_index); + if (!list_node) + return FALSE; + _gail_cell_destroy_action_info (list_node->data, NULL); + cell->action_list = g_list_remove_link (cell->action_list, list_node); + return TRUE; +} + + +gboolean +gail_cell_remove_action_by_name (GailCell *cell, + const gchar *action_name) +{ + GList *list_node; + gboolean action_found= FALSE; + + g_return_val_if_fail (GAIL_IS_CELL (cell), FALSE); + for (list_node = cell->action_list; list_node && !action_found; + list_node = list_node->next) + { + if (!g_strcasecmp (((ActionInfo *)(list_node->data))->name, action_name)) + { + action_found = TRUE; + break; + } + } + if (!action_found) + return FALSE; + _gail_cell_destroy_action_info (list_node->data, NULL); + cell->action_list = g_list_remove_link (cell->action_list, list_node); + return TRUE; +} + +static ActionInfo * +_gail_cell_get_action_info (GailCell *cell, + gint index) +{ + GList *list_node; + + g_return_val_if_fail (GAIL_IS_CELL (cell), NULL); + if (cell->action_list == NULL) + return NULL; + list_node = g_list_nth (cell->action_list, index); + if (!list_node) + return NULL; + return (ActionInfo *) (list_node->data); +} + + +static void +_gail_cell_destroy_action_info (gpointer action_info, + gpointer user_data) +{ + ActionInfo *info = (ActionInfo *)action_info; + g_assert (info != NULL); + g_free (info->name); + g_free (info->description); + g_free (info->keybinding); + g_free (info); +} +static gint +gail_cell_action_get_n_actions (AtkAction *action) +{ + GailCell *cell = GAIL_CELL(action); + if (cell->action_list != NULL) + return g_list_length (cell->action_list); + else + return 0; +} + +static G_CONST_RETURN gchar * +gail_cell_action_get_name (AtkAction *action, + gint index) +{ + GailCell *cell = GAIL_CELL(action); + ActionInfo *info = _gail_cell_get_action_info (cell, index); + + if (info == NULL) + return NULL; + return info->name; +} + +static G_CONST_RETURN gchar * +gail_cell_action_get_description (AtkAction *action, + gint index) +{ + GailCell *cell = GAIL_CELL(action); + ActionInfo *info = _gail_cell_get_action_info (cell, index); + + if (info == NULL) + return NULL; + return info->description; +} + +static gboolean +gail_cell_action_set_description (AtkAction *action, + gint index, + const gchar *desc) +{ + GailCell *cell = GAIL_CELL(action); + ActionInfo *info = _gail_cell_get_action_info (cell, index); + + if (info == NULL) + return FALSE; + g_free (info->description); + info->description = g_strdup (desc); + return TRUE; +} + +static G_CONST_RETURN gchar * +gail_cell_action_get_keybinding (AtkAction *action, + gint index) +{ + GailCell *cell = GAIL_CELL(action); + ActionInfo *info = _gail_cell_get_action_info (cell, index); + if (info == NULL) + return NULL; + return info->keybinding; +} + +static gboolean +gail_cell_action_do_action (AtkAction *action, + gint index) +{ + GailCell *cell = GAIL_CELL(action); + ActionInfo *info = _gail_cell_get_action_info (cell, index); + if (info == NULL) + return FALSE; + if (info->do_action_func == NULL) + return FALSE; + if (cell->action_idle_handler) + return FALSE; + cell->action_func = info->do_action_func; + cell->action_idle_handler = g_idle_add (idle_do_action, cell); + return TRUE; +} + +static gboolean +idle_do_action (gpointer data) +{ + GailCell *cell; + + GDK_THREADS_ENTER (); + + cell = GAIL_CELL (data); + cell->action_idle_handler = 0; + cell->action_func (cell); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +atk_component_interface_init (AtkComponentIface *iface) +{ + g_assert (iface != NULL); + + iface->get_extents = gail_cell_get_extents; + iface->grab_focus = gail_cell_grab_focus; +} + +static void +gail_cell_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GailCell *gailcell; + AtkObject *cell_parent; + + g_assert (GAIL_IS_CELL (component)); + + gailcell = GAIL_CELL (component); + + cell_parent = gtk_widget_get_accessible (gailcell->widget); + + gail_cell_parent_get_cell_extents (GAIL_CELL_PARENT (cell_parent), + gailcell, x, y, width, height, coord_type); +} + +static gboolean +gail_cell_grab_focus (AtkComponent *component) +{ + GailCell *gailcell; + AtkObject *cell_parent; + + g_assert (GAIL_IS_CELL (component)); + + gailcell = GAIL_CELL (component); + + cell_parent = gtk_widget_get_accessible (gailcell->widget); + + return gail_cell_parent_grab_focus (GAIL_CELL_PARENT (cell_parent), + gailcell); +} diff --git a/modules/other/gail/gailcell.h b/modules/other/gail/gailcell.h new file mode 100644 index 000000000..7329c8986 --- /dev/null +++ b/modules/other/gail/gailcell.h @@ -0,0 +1,110 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CELL_H__ +#define __GAIL_CELL_H__ + +#include <atk/atk.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CELL (gail_cell_get_type ()) +#define GAIL_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CELL, GailCell)) +#define GAIL_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CELL, GailCellClass)) +#define GAIL_IS_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CELL)) +#define GAIL_IS_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CELL)) +#define GAIL_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CELL, GailCellClass)) + +typedef struct _GailCell GailCell; +typedef struct _GailCellClass GailCellClass; +typedef struct _ActionInfo ActionInfo; +typedef void (*ACTION_FUNC) (GailCell *cell); + +struct _GailCell +{ + AtkObject parent; + + GtkWidget *widget; + /* + * This cached value is used only by atk_object_get_index_in_parent() + * which updates the value when it is stale. + */ + gint index; + AtkStateSet *state_set; + GList *action_list; + void (*refresh_index) (GailCell *cell); + gint action_idle_handler; + ACTION_FUNC action_func; +}; + +GType gail_cell_get_type (void); + +struct _GailCellClass +{ + AtkObjectClass parent_class; +}; + +struct _ActionInfo { + gchar *name; + gchar *description; + gchar *keybinding; + ACTION_FUNC do_action_func; +}; + + +void gail_cell_init (GailCell *cell, + GtkWidget *widget, + AtkObject *parent, + gint index); + +gboolean gail_cell_add_state (GailCell *cell, + AtkStateType state_type, + gboolean emit_signal); + +gboolean gail_cell_remove_state (GailCell *cell, + AtkStateType state_type, + gboolean emit_signal); + +void gail_cell_type_add_action_interface + (GType type); + +gboolean gail_cell_add_action (GailCell *cell, + const gchar *action_name, + const gchar *action_description, + const gchar *action_keybinding, + ACTION_FUNC action_func); + +gboolean gail_cell_remove_action (GailCell *cell, + gint action_id); + +gboolean gail_cell_remove_action_by_name (GailCell *cell, + const gchar *action_name); + + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CELL_H__ */ diff --git a/modules/other/gail/gailcellparent.c b/modules/other/gail/gailcellparent.c new file mode 100644 index 000000000..06481c65b --- /dev/null +++ b/modules/other/gail/gailcellparent.c @@ -0,0 +1,124 @@ +/* GAIL - The GNOME Accessibility Implementation Library + + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkwidget.h> +#include "gailcellparent.h" + +GType +gail_cell_parent_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCellParentIface), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + + }; + + type = g_type_register_static (G_TYPE_INTERFACE, "GailCellParent", &tinfo, 0); + } + + return type; +} + +/** + * gail_cell_parent_get_cell_extents: + * @parent: a #GObject instance that implements GailCellParentIface + * @cell: a #GailCell whose extents is required + * @x: address of #gint to put x coordinate + * @y: address of #gint to put y coordinate + * @width: address of #gint to put width + * @height: address of #gint to put height + * @coord_type: specifies whether the coordinates are relative to the screen + * or to the components top level window + * + * Gets the rectangle which gives the extent of the @cell. + * + **/ +void +gail_cell_parent_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GailCellParentIface *iface; + + g_return_if_fail (GAIL_IS_CELL_PARENT (parent)); + + iface = GAIL_CELL_PARENT_GET_IFACE (parent); + + if (iface->get_cell_extents) + (iface->get_cell_extents) (parent, cell, x, y, width, height, coord_type); +} + +/** + * gail_cell_parent_get_cell_area: + * @parent: a #GObject instance that implements GailCellParentIface + * @cell: a #GailCell whose area is required + * @cell_rect: address of #GdkRectangle to put the cell area + * + * Gets the cell area of the @cell. + * + **/ +void +gail_cell_parent_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect) +{ + GailCellParentIface *iface; + + g_return_if_fail (GAIL_IS_CELL_PARENT (parent)); + g_return_if_fail (cell_rect); + + iface = GAIL_CELL_PARENT_GET_IFACE (parent); + + if (iface->get_cell_area) + (iface->get_cell_area) (parent, cell, cell_rect); +} +/** + * gail_cell_parent_grab_focus: + * @parent: a #GObject instance that implements GailCellParentIface + * @cell: a #GailCell whose area is required + * + * Puts focus in the specified cell. + * + **/ +gboolean +gail_cell_parent_grab_focus (GailCellParent *parent, + GailCell *cell) +{ + GailCellParentIface *iface; + + g_return_val_if_fail (GAIL_IS_CELL_PARENT (parent), FALSE); + + iface = GAIL_CELL_PARENT_GET_IFACE (parent); + + if (iface->grab_focus) + return (iface->grab_focus) (parent, cell); + else + return FALSE; +} diff --git a/modules/other/gail/gailcellparent.h b/modules/other/gail/gailcellparent.h new file mode 100644 index 000000000..3932093cc --- /dev/null +++ b/modules/other/gail/gailcellparent.h @@ -0,0 +1,88 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CELL_PARENT_H__ +#define __GAIL_CELL_PARENT_H__ + +#include <atk/atkobject.h> +#include <atk/atkutil.h> +#include <gail/gailcell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * The GailCellParent interface should be supported by any object which + * contains children which are flyweights, i.e. do not have corresponding + * widgets and the children need help from their parent to provide + * functionality. One example is GailTreeView where the children GailCell + * need help from the GailTreeView in order to implement + * atk_component_get_extents + */ + +#define GAIL_TYPE_CELL_PARENT (gail_cell_parent_get_type ()) +#define GAIL_IS_CELL_PARENT(obj) G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CELL_PARENT) +#define GAIL_CELL_PARENT(obj) G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CELL_PARENT, GailCellParent) +#define GAIL_CELL_PARENT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GAIL_TYPE_CELL_PARENT, GailCellParentIface)) + +#ifndef _TYPEDEF_GAIL_CELL_PARENT_ +#define _TYPEDEF_GAIL_CELL_PARENT_ +typedef struct _GailCellParent GailCellParent; +#endif +typedef struct _GailCellParentIface GailCellParentIface; + +struct _GailCellParentIface +{ + GTypeInterface parent; + void ( *get_cell_extents) (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); + void ( *get_cell_area) (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect); + gboolean ( *grab_focus) (GailCellParent *parent, + GailCell *cell); +}; + +GType gail_cell_parent_get_type (void); + +void gail_cell_parent_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type +); +void gail_cell_parent_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect); +gboolean gail_cell_parent_grab_focus (GailCellParent *parent, + GailCell *cell); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __GAIL_CELL_PARENT_H__ */ diff --git a/modules/other/gail/gailcheckmenuitem.c b/modules/other/gail/gailcheckmenuitem.c new file mode 100644 index 000000000..55164f6f8 --- /dev/null +++ b/modules/other/gail/gailcheckmenuitem.c @@ -0,0 +1,166 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailcheckmenuitem.h" +#include "gailchecksubmenuitem.h" + +static void gail_check_menu_item_class_init (GailCheckMenuItemClass *klass); + +static void gail_check_menu_item_toggled_gtk (GtkWidget *widget); + +static void gail_check_menu_item_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_check_menu_item_real_initialize (AtkObject *obj, + gpointer data); + +static AtkStateSet* gail_check_menu_item_ref_state_set (AtkObject *accessible); + +static GailMenuItemClass *parent_class = NULL; + +GType +gail_check_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCheckMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_check_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCheckMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_MENU_ITEM, + "GailCheckMenuItem", &tinfo, 0); + } + + return type; +} + +static void +gail_check_menu_item_class_init (GailCheckMenuItemClass *klass) +{ + GailWidgetClass *widget_class; + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + widget_class = (GailWidgetClass*)klass; + widget_class->notify_gtk = gail_check_menu_item_real_notify_gtk; + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_check_menu_item_ref_state_set; + class->initialize = gail_check_menu_item_real_initialize; +} + +AtkObject* +gail_check_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (widget), NULL); + + if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget))) + return gail_check_sub_menu_item_new (widget); + + object = g_object_new (GAIL_TYPE_CHECK_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_check_menu_item_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + g_signal_connect (data, + "toggled", + G_CALLBACK (gail_check_menu_item_toggled_gtk), + NULL); + + obj->role = ATK_ROLE_CHECK_MENU_ITEM; +} + +static void +gail_check_menu_item_toggled_gtk (GtkWidget *widget) +{ + AtkObject *accessible; + GtkCheckMenuItem *check_menu_item; + + check_menu_item = GTK_CHECK_MENU_ITEM (widget); + + accessible = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (accessible, ATK_STATE_CHECKED, + check_menu_item->active); +} + +static AtkStateSet* +gail_check_menu_item_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkCheckMenuItem *check_menu_item; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + check_menu_item = GTK_CHECK_MENU_ITEM (widget); + + if (gtk_check_menu_item_get_active (check_menu_item)) + atk_state_set_add_state (state_set, ATK_STATE_CHECKED); + + if (gtk_check_menu_item_get_inconsistent (check_menu_item)) + atk_state_set_remove_state (state_set, ATK_STATE_ENABLED); + + return state_set; +} + +static void +gail_check_menu_item_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (obj); + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (check_menu_item)); + + if (strcmp (pspec->name, "inconsistent") == 0) + atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, + !gtk_check_menu_item_get_inconsistent (check_menu_item)); + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} diff --git a/modules/other/gail/gailcheckmenuitem.h b/modules/other/gail/gailcheckmenuitem.h new file mode 100644 index 000000000..292cdd9aa --- /dev/null +++ b/modules/other/gail/gailcheckmenuitem.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CHECK_MENU_ITEM_H__ +#define __GAIL_CHECK_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailmenuitem.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CHECK_MENU_ITEM (gail_check_menu_item_get_type ()) +#define GAIL_CHECK_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CHECK_MENU_ITEM, GailCheckMenuItem)) +#define GAIL_CHECK_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CHECK_MENU_ITEM, GailCheckMenuItemClass)) +#define GAIL_IS_CHECK_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CHECK_MENU_ITEM)) +#define GAIL_IS_CHECK_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CHECK_MENU_ITEM)) +#define GAIL_CHECK_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CHECK_MENU_ITEM, GailCheckMenuItemClass)) + +typedef struct _GailCheckMenuItem GailCheckMenuItem; +typedef struct _GailCheckMenuItemClass GailCheckMenuItemClass; + +struct _GailCheckMenuItem +{ + GailMenuItem parent; +}; + +GType gail_check_menu_item_get_type (void); + +struct _GailCheckMenuItemClass +{ + GailMenuItemClass parent_class; +}; + +AtkObject* gail_check_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CHECK_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailchecksubmenuitem.c b/modules/other/gail/gailchecksubmenuitem.c new file mode 100644 index 000000000..d011f77a9 --- /dev/null +++ b/modules/other/gail/gailchecksubmenuitem.c @@ -0,0 +1,162 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailchecksubmenuitem.h" + +static void gail_check_sub_menu_item_class_init (GailCheckSubMenuItemClass *klass); + +static void gail_check_sub_menu_item_toggled_gtk (GtkWidget *widget); + +static void gail_check_sub_menu_item_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_check_sub_menu_item_real_initialize (AtkObject *obj, + gpointer data); + +static AtkStateSet* gail_check_sub_menu_item_ref_state_set (AtkObject *accessible); + +static GailSubMenuItemClass *parent_class = NULL; + +GType +gail_check_sub_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCheckSubMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_check_sub_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCheckSubMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_SUB_MENU_ITEM, + "GailCheckSubMenuItem", &tinfo, 0); + } + + return type; +} + +static void +gail_check_sub_menu_item_class_init (GailCheckSubMenuItemClass *klass) +{ + GailWidgetClass *widget_class; + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + widget_class = (GailWidgetClass*)klass; + widget_class->notify_gtk = gail_check_sub_menu_item_real_notify_gtk; + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_check_sub_menu_item_ref_state_set; + class->initialize = gail_check_sub_menu_item_real_initialize; +} + +AtkObject* +gail_check_sub_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_CHECK_MENU_ITEM (widget), NULL); + + object = g_object_new (GAIL_TYPE_CHECK_SUB_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_check_sub_menu_item_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + g_signal_connect (data, + "toggled", + G_CALLBACK (gail_check_sub_menu_item_toggled_gtk), + NULL); + + obj->role = ATK_ROLE_CHECK_MENU_ITEM; +} + +static void +gail_check_sub_menu_item_toggled_gtk (GtkWidget *widget) +{ + AtkObject *accessible; + GtkCheckMenuItem *check_menu_item; + + check_menu_item = GTK_CHECK_MENU_ITEM (widget); + + accessible = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (accessible, ATK_STATE_CHECKED, + check_menu_item->active); +} + +static AtkStateSet* +gail_check_sub_menu_item_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkCheckMenuItem *check_menu_item; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + check_menu_item = GTK_CHECK_MENU_ITEM (widget); + + if (gtk_check_menu_item_get_active (check_menu_item)) + atk_state_set_add_state (state_set, ATK_STATE_CHECKED); + + if (gtk_check_menu_item_get_inconsistent (check_menu_item)) + atk_state_set_remove_state (state_set, ATK_STATE_ENABLED); + + return state_set; +} + +static void +gail_check_sub_menu_item_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkCheckMenuItem *check_menu_item = GTK_CHECK_MENU_ITEM (obj); + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (check_menu_item)); + + if (strcmp (pspec->name, "inconsistent") == 0) + atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, + !gtk_check_menu_item_get_inconsistent (check_menu_item)); + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} diff --git a/modules/other/gail/gailchecksubmenuitem.h b/modules/other/gail/gailchecksubmenuitem.h new file mode 100644 index 000000000..174cdbafe --- /dev/null +++ b/modules/other/gail/gailchecksubmenuitem.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CHECK_SUB_MENU_ITEM_H__ +#define __GAIL_CHECK_SUB_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailsubmenuitem.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CHECK_SUB_MENU_ITEM (gail_check_sub_menu_item_get_type ()) +#define GAIL_CHECK_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CHECK_SUB_MENU_ITEM, GailCheckSubMenuItem)) +#define GAIL_CHECK_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CHECK_SUB_MENU_ITEM, GailCheckSubMenuItemClass)) +#define GAIL_IS_CHECK_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CHECK_SUB_MENU_ITEM)) +#define GAIL_IS_CHECK_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CHECK_SUB_MENU_ITEM)) +#define GAIL_CHECK_SUB_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CHECK_SUB_MENU_ITEM, GailCheckSubMenuItemClass)) + +typedef struct _GailCheckSubMenuItem GailCheckSubMenuItem; +typedef struct _GailCheckSubMenuItemClass GailCheckSubMenuItemClass; + +struct _GailCheckSubMenuItem +{ + GailSubMenuItem parent; +}; + +GType gail_check_sub_menu_item_get_type (void); + +struct _GailCheckSubMenuItemClass +{ + GailSubMenuItemClass parent_class; +}; + +AtkObject* gail_check_sub_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CHECK_SUB_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailclist.c b/modules/other/gail/gailclist.c new file mode 100644 index 000000000..d56839e3e --- /dev/null +++ b/modules/other/gail/gailclist.c @@ -0,0 +1,1670 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <stdio.h> +#include "gailclist.h" +#include "gailclistcell.h" +#include "gailcellparent.h" + +/* Copied from gtkclist.c */ +/* this defigns the base grid spacing */ +#define CELL_SPACING 1 + +/* added the horizontal space at the beginning and end of a row*/ +#define COLUMN_INSET 3 + + +/* gives the top pixel of the given row in context of + * the clist's voffset */ +#define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \ + (((row) + 1) * CELL_SPACING) + \ + (clist)->voffset) + +/* returns the row index from a y pixel location in the + * context of the clist's voffset */ +#define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \ + ((clist)->row_height + CELL_SPACING)) +/* gives the left pixel of the given column in context of + * the clist's hoffset */ +#define COLUMN_LEFT_XPIXEL(clist, colnum) ((clist)->column[(colnum)].area.x + \ + (clist)->hoffset) + +/* returns the column index from a x pixel location in the + * context of the clist's hoffset */ +static inline gint +COLUMN_FROM_XPIXEL (GtkCList * clist, + gint x) +{ + gint i, cx; + + for (i = 0; i < clist->columns; i++) + if (clist->column[i].visible) + { + cx = clist->column[i].area.x + clist->hoffset; + + if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) && + x <= (cx + clist->column[i].area.width + COLUMN_INSET)) + return i; + } + + /* no match */ + return -1; +} + +/* returns the top pixel of the given row in the context of + * the list height */ +#define ROW_TOP(clist, row) (((clist)->row_height + CELL_SPACING) * (row)) + +/* returns the left pixel of the given column in the context of + * the list width */ +#define COLUMN_LEFT(clist, colnum) ((clist)->column[(colnum)].area.x) + +/* returns the total height of the list */ +#define LIST_HEIGHT(clist) (((clist)->row_height * ((clist)->rows)) + \ + (CELL_SPACING * ((clist)->rows + 1))) + +static inline gint +LIST_WIDTH (GtkCList * clist) +{ + gint last_column; + + for (last_column = clist->columns - 1; + last_column >= 0 && !clist->column[last_column].visible; last_column--); + + if (last_column >= 0) + return (clist->column[last_column].area.x + + clist->column[last_column].area.width + + COLUMN_INSET + CELL_SPACING); + return 0; +} + +/* returns the GList item for the nth row */ +#define ROW_ELEMENT(clist, row) (((row) == (clist)->rows - 1) ? \ + (clist)->row_list_end : \ + g_list_nth ((clist)->row_list, (row))) + +typedef struct _GailCListRow GailCListRow; +typedef struct _GailCListCellData GailCListCellData; + + +static void gail_clist_class_init (GailCListClass *klass); +static void gail_clist_real_initialize (AtkObject *obj, + gpointer data); +static void gail_clist_finalize (GObject *object); + +static gint gail_clist_get_n_children (AtkObject *obj); +static AtkObject* gail_clist_ref_child (AtkObject *obj, + gint i); +static AtkStateSet* gail_clist_ref_state_set (AtkObject *obj); + + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_clist_clear_selection (AtkSelection *selection); + +static AtkObject* gail_clist_ref_selection (AtkSelection *selection, + gint i); +static gint gail_clist_get_selection_count (AtkSelection *selection); +static gboolean gail_clist_is_child_selected (AtkSelection *selection, + gint i); +static gboolean gail_clist_select_all_selection (AtkSelection *selection); + +static void atk_table_interface_init (AtkTableIface *iface); +static gint gail_clist_get_index_at (AtkTable *table, + gint row, + gint column); +static gint gail_clist_get_column_at_index (AtkTable *table, + gint index); +static gint gail_clist_get_row_at_index (AtkTable *table, + gint index); +static AtkObject* gail_clist_ref_at (AtkTable *table, + gint row, + gint column); +static AtkObject* gail_clist_ref_at_actual (AtkTable *table, + gint row, + gint column); +static AtkObject* + gail_clist_get_caption (AtkTable *table); + +static gint gail_clist_get_n_columns (AtkTable *table); +static gint gail_clist_get_n_actual_columns (GtkCList *clist); + +static G_CONST_RETURN gchar* + gail_clist_get_column_description(AtkTable *table, + gint column); +static AtkObject* gail_clist_get_column_header (AtkTable *table, + gint column); +static gint gail_clist_get_n_rows (AtkTable *table); +static G_CONST_RETURN gchar* + gail_clist_get_row_description (AtkTable *table, + gint row); +static AtkObject* gail_clist_get_row_header (AtkTable *table, + gint row); +static AtkObject* gail_clist_get_summary (AtkTable *table); +static gboolean gail_clist_add_row_selection (AtkTable *table, + gint row); +static gboolean gail_clist_remove_row_selection (AtkTable *table, + gint row); +static gint gail_clist_get_selected_rows (AtkTable *table, + gint **rows_selected); +static gboolean gail_clist_is_row_selected (AtkTable *table, + gint row); +static gboolean gail_clist_is_selected (AtkTable *table, + gint row, + gint column); +static void gail_clist_set_caption (AtkTable *table, + AtkObject *caption); +static void gail_clist_set_column_description(AtkTable *table, + gint column, + const gchar *description); +static void gail_clist_set_column_header (AtkTable *table, + gint column, + AtkObject *header); +static void gail_clist_set_row_description (AtkTable *table, + gint row, + const gchar *description); +static void gail_clist_set_row_header (AtkTable *table, + gint row, + AtkObject *header); +static void gail_clist_set_summary (AtkTable *table, + AtkObject *accessible); + +/* gailcellparent.h */ + +static void gail_cell_parent_interface_init (GailCellParentIface *iface); +static void gail_clist_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); + +static void gail_clist_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect); + +static void gail_clist_select_row_gtk (GtkCList *clist, + int row, + int column, + GdkEvent *event, + gpointer data); +static void gail_clist_unselect_row_gtk (GtkCList *clist, + int row, + int column, + GdkEvent *event, + gpointer data); +static gint gail_clist_get_visible_column (AtkTable *table, + int column); +static gint gail_clist_get_actual_column (AtkTable *table, + int visible_column); +static void gail_clist_set_row_data (AtkTable *table, + gint row, + const gchar *description, + AtkObject *header, + gboolean is_header); +static GailCListRow* + gail_clist_get_row_data (AtkTable *table, + gint row); +static void gail_clist_get_visible_rect (GtkCList *clist, + GdkRectangle *clist_rect); +static gboolean gail_clist_is_cell_visible (GdkRectangle *cell_rect, + GdkRectangle *visible_rect); +static void gail_clist_cell_data_new (GailCList *clist, + GailCell *cell, + gint column, + gint row); +static void gail_clist_cell_destroyed (gpointer data); +static void gail_clist_cell_data_remove (GailCList *clist, + GailCell *cell); +static GailCell* gail_clist_find_cell (GailCList *clist, + gint index); +static void gail_clist_adjustment_changed (GtkAdjustment *adjustment, + GtkCList *clist); + +struct _GailCListColumn +{ + gchar *description; + AtkObject *header; +}; + +struct _GailCListRow +{ + GtkCListRow *row_data; + int row_number; + gchar *description; + AtkObject *header; +}; + +struct _GailCListCellData +{ + GtkCell *gtk_cell; + GailCell *gail_cell; + int row_number; + int column_number; +}; + +static gpointer parent_class = NULL; + +GType +gail_clist_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCListClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_clist_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCList), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_table_info = + { + (GInterfaceInitFunc) atk_table_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo gail_cell_parent_info = + { + (GInterfaceInitFunc) gail_cell_parent_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailCList", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_TABLE, + &atk_table_info); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + g_type_add_interface_static (type, GAIL_TYPE_CELL_PARENT, + &gail_cell_parent_info); + } + return type; +} + +static void +gail_clist_class_init (GailCListClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->get_n_children = gail_clist_get_n_children; + class->ref_child = gail_clist_ref_child; + class->ref_state_set = gail_clist_ref_state_set; + class->initialize = gail_clist_real_initialize; + + gobject_class->finalize = gail_clist_finalize; +} + +AtkObject* +gail_clist_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_CLIST (widget), NULL); + + object = g_object_new (GAIL_TYPE_CLIST, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_TABLE; + + return accessible; +} + +static void +gail_clist_real_initialize (AtkObject *obj, + gpointer data) +{ + GailCList *clist; + GtkCList *gtk_clist; + gint i; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + clist = GAIL_CLIST (obj); + + clist->caption = NULL; + clist->summary = NULL; + clist->row_data = NULL; + clist->cell_data = NULL; + clist->previous_selected_cell = NULL; + + gtk_clist = GTK_CLIST (data); + + clist->n_cols = gtk_clist->columns; + clist->columns = g_new (GailCListColumn, gtk_clist->columns); + for (i = 0; i < gtk_clist->columns; i++) + { + clist->columns[i].description = NULL; + clist->columns[i].header = NULL; + } + /* + * Set up signal handlers for select-row and unselect-row + */ + g_signal_connect (gtk_clist, + "select-row", + G_CALLBACK (gail_clist_select_row_gtk), + obj); + g_signal_connect (gtk_clist, + "unselect-row", + G_CALLBACK (gail_clist_unselect_row_gtk), + obj); + /* + * Adjustment callbacks + */ + if (gtk_clist->hadjustment) + { + g_signal_connect (gtk_clist->hadjustment, + "value_changed", + G_CALLBACK (gail_clist_adjustment_changed), + gtk_clist); + } + if (gtk_clist->vadjustment) + { + g_signal_connect (gtk_clist->vadjustment, + "value_changed", + G_CALLBACK (gail_clist_adjustment_changed), + gtk_clist); + } +} + +static void +gail_clist_finalize (GObject *object) +{ + GailCList *clist = GAIL_CLIST (object); + gint i; + GArray *array; + + if (clist->caption) + g_object_unref (clist->caption); + if (clist->summary) + g_object_unref (clist->summary); + + for (i = 0; i < clist->n_cols; i++) + { + g_free (clist->columns[i].description); + if (clist->columns[i].header) + g_object_unref (clist->columns[i].header); + } + g_free (clist->columns); + + array = clist->row_data; + + if (clist->previous_selected_cell) + g_object_unref (clist->previous_selected_cell); + + if (array) + { + for (i = 0; i < array->len; i++) + { + GailCListRow *row_data; + + row_data = g_array_index (array, GailCListRow*, i); + + if (row_data->header) + g_object_unref (row_data->header); + g_free (row_data->description); + } + } + + if (clist->cell_data) + { + GList *temp_list; + + for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) + { + g_list_free (temp_list->data); + } + g_list_free (clist->cell_data); + } +} + +static gint +gail_clist_get_n_children (AtkObject *obj) +{ + GtkWidget *widget; + gint row, col; + + g_return_val_if_fail (GAIL_IS_CLIST (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + row = gail_clist_get_n_rows (ATK_TABLE (obj)); + col = gail_clist_get_n_actual_columns (GTK_CLIST (widget)); + return (row * col); +} + +static AtkObject* +gail_clist_ref_child (AtkObject *obj, + gint i) +{ + GtkWidget *widget; + gint row, col; + gint n_columns; + + g_return_val_if_fail (GAIL_IS_CLIST (obj), NULL); + g_return_val_if_fail (i >= 0, NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + n_columns = gail_clist_get_n_actual_columns (GTK_CLIST (widget)); + if (!n_columns) + return NULL; + + row = i / n_columns; + col = i % n_columns; + return gail_clist_ref_at_actual (ATK_TABLE (obj), row, col); +} + +static AtkStateSet* +gail_clist_ref_state_set (AtkObject *obj) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget != NULL) + atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS); + + return state_set; +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->clear_selection = gail_clist_clear_selection; + iface->ref_selection = gail_clist_ref_selection; + iface->get_selection_count = gail_clist_get_selection_count; + iface->is_child_selected = gail_clist_is_child_selected; + iface->select_all_selection = gail_clist_select_all_selection; +} + +static gboolean +gail_clist_clear_selection (AtkSelection *selection) +{ + GtkCList *clist; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + clist = GTK_CLIST (widget); + gtk_clist_unselect_all(clist); + return TRUE; +} + +static AtkObject* +gail_clist_ref_selection (AtkSelection *selection, + gint i) +{ + gint visible_columns; + gint selected_row; + gint selected_column; + gint *selected_rows; + + if ( i < 0 && i >= gail_clist_get_selection_count (selection)) + return NULL; + + visible_columns = gail_clist_get_n_columns (ATK_TABLE (selection)); + gail_clist_get_selected_rows (ATK_TABLE (selection), &selected_rows); + selected_row = selected_rows[i / visible_columns]; + g_free (selected_rows); + selected_column = gail_clist_get_actual_column (ATK_TABLE (selection), + i % visible_columns); + + return gail_clist_ref_at (ATK_TABLE (selection), selected_row, + selected_column); +} + +static gint +gail_clist_get_selection_count (AtkSelection *selection) +{ + gint n_rows_selected; + + n_rows_selected = gail_clist_get_selected_rows (ATK_TABLE (selection), NULL); + + if (n_rows_selected > 0) + /* + * The number of cells selected is the number of columns + * times the number of selected rows + */ + return gail_clist_get_n_columns (ATK_TABLE (selection)) * n_rows_selected; + return 0; +} + +static gboolean +gail_clist_is_child_selected (AtkSelection *selection, + gint i) +{ + gint row; + + row = atk_table_get_row_at_index (ATK_TABLE (selection), i); + + if (row == 0 && i >= gail_clist_get_n_columns (ATK_TABLE (selection))) + return FALSE; + return gail_clist_is_row_selected (ATK_TABLE (selection), row); +} + +static gboolean +gail_clist_select_all_selection (AtkSelection *selection) +{ + GtkCList *clist; + GtkWidget *widget; + /* GtkArg arg; */ + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + clist = GTK_CLIST (widget); + gtk_clist_select_all(clist); + + return TRUE; +} + +static void +atk_table_interface_init (AtkTableIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->ref_at = gail_clist_ref_at; + iface->get_index_at = gail_clist_get_index_at; + iface->get_column_at_index = gail_clist_get_column_at_index; + iface->get_row_at_index = gail_clist_get_row_at_index; + iface->get_caption = gail_clist_get_caption; + iface->get_n_columns = gail_clist_get_n_columns; + iface->get_column_description = gail_clist_get_column_description; + iface->get_column_header = gail_clist_get_column_header; + iface->get_n_rows = gail_clist_get_n_rows; + iface->get_row_description = gail_clist_get_row_description; + iface->get_row_header = gail_clist_get_row_header; + iface->get_summary = gail_clist_get_summary; + iface->add_row_selection = gail_clist_add_row_selection; + iface->remove_row_selection = gail_clist_remove_row_selection; + iface->get_selected_rows = gail_clist_get_selected_rows; + iface->is_row_selected = gail_clist_is_row_selected; + iface->is_selected = gail_clist_is_selected; + iface->set_caption = gail_clist_set_caption; + iface->set_column_description = gail_clist_set_column_description; + iface->set_column_header = gail_clist_set_column_header; + iface->set_row_description = gail_clist_set_row_description; + iface->set_row_header = gail_clist_set_row_header; + iface->set_summary = gail_clist_set_summary; +} + +static AtkObject* +gail_clist_ref_at (AtkTable *table, + gint row, + gint column) +{ + GtkWidget *widget; + gint actual_column; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + actual_column = gail_clist_get_actual_column (table, column); + return gail_clist_ref_at_actual (table, row, actual_column); +} + + +static AtkObject* +gail_clist_ref_at_actual (AtkTable *table, + gint row, + gint column) +{ + /* + * The column number pased to this function is the actual column number + * whereas the column number passed to gail_clist_ref_at is the + * visible column number + */ + GtkCList *clist; + GtkWidget *widget; + GtkCellType cellType; + AtkObject *return_object; + gint n_rows, n_columns; + gint index; + GailCell *cell; + + g_return_val_if_fail (GTK_IS_ACCESSIBLE (table), NULL); + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + clist = GTK_CLIST (widget); + n_rows = gail_clist_get_n_rows (table); + n_columns = gail_clist_get_n_actual_columns (clist); + + if (row < 0 || row >= n_rows) + return NULL; + if (column < 0 || column >= n_columns) + return NULL; + + /* + * Check whether the child is cached + */ + index = column + row * n_columns; + cell = gail_clist_find_cell (GAIL_CLIST (table), index); + if (cell) + { + g_object_ref (cell); + return ATK_OBJECT (cell); + } + cellType = gtk_clist_get_cell_type(clist, row, column); + switch (cellType) + { + case GTK_CELL_TEXT: + case GTK_CELL_PIXTEXT: + return_object = gail_clist_cell_new (); + break; + case GTK_CELL_PIXMAP: + return_object = NULL; + break; + default: + /* Don't handle GTK_CELL_EMPTY or GTK_CELL_WIDGET, return NULL */ + return_object = NULL; + break; + } + if (return_object) + { + cell = GAIL_CELL (return_object); + + g_return_val_if_fail (ATK_IS_OBJECT (table), NULL); + + gail_cell_init (cell, widget, ATK_OBJECT (table), + index); + /* + * Store the cell in a cache + */ + gail_clist_cell_data_new (GAIL_CLIST (table), cell, column, row); + /* + * If the column is visible, sets the cell's state + */ + if (clist->column[column].visible) + { + GdkRectangle cell_rect, visible_rect; + + gail_clist_get_cell_area (GAIL_CELL_PARENT (table), cell, &cell_rect); + gail_clist_get_visible_rect (clist, &visible_rect); + gail_cell_add_state (cell, ATK_STATE_VISIBLE, FALSE); + if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) + gail_cell_add_state (cell, ATK_STATE_SHOWING, FALSE); + } + /* + * If a row is selected, all cells in the row are selected + */ + if (gail_clist_is_row_selected (table, row)) + { + gail_cell_add_state (cell, ATK_STATE_SELECTED, FALSE); + if (clist->columns == 1) + gail_cell_add_state (cell, ATK_STATE_FOCUSED, FALSE); + } + } + + return return_object; +} + +static gint +gail_clist_get_index_at (AtkTable *table, + gint row, + gint column) +{ + gint n_cols, n_rows; + + n_cols = atk_table_get_n_columns (table); + n_rows = atk_table_get_n_rows (table); + + g_return_val_if_fail (row < n_rows, 0); + g_return_val_if_fail (column < n_cols, 0); + + return row * n_cols + column; +} + +static gint +gail_clist_get_column_at_index (AtkTable *table, + gint index) +{ + gint n_cols; + + n_cols = atk_table_get_n_columns (table); + + if (n_cols == 0) + return 0; + else + return (gint) (index % n_cols); +} + +static gint +gail_clist_get_row_at_index (AtkTable *table, + gint index) +{ + gint n_cols; + + n_cols = atk_table_get_n_columns (table); + + if (n_cols == 0) + return 0; + else + return (gint) (index / n_cols); +} + +static AtkObject* +gail_clist_get_caption (AtkTable *table) +{ + GailCList* obj = GAIL_CLIST (table); + + return obj->caption; +} + +static gint +gail_clist_get_n_columns (AtkTable *table) +{ + GtkWidget *widget; + GtkCList *clist; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + + return gail_clist_get_visible_column (table, + gail_clist_get_n_actual_columns (clist)); +} + +static gint +gail_clist_get_n_actual_columns (GtkCList *clist) +{ + return clist->columns; +} + +static G_CONST_RETURN gchar* +gail_clist_get_column_description (AtkTable *table, + gint column) +{ + GailCList *clist = GAIL_CLIST (table); + GtkWidget *widget; + gint actual_column; + + if (column < 0 || column >= gail_clist_get_n_columns (table)) + return NULL; + + actual_column = gail_clist_get_actual_column (table, column); + if (clist->columns[actual_column].description) + return (clist->columns[actual_column].description); + + widget = GTK_ACCESSIBLE (clist)->widget; + if (widget == NULL) + return NULL; + + return gtk_clist_get_column_title (GTK_CLIST (widget), actual_column); +} + +static AtkObject* +gail_clist_get_column_header (AtkTable *table, + gint column) +{ + GailCList *clist = GAIL_CLIST (table); + GtkWidget *widget; + GtkWidget *return_widget; + gint actual_column; + + if (column < 0 || column >= gail_clist_get_n_columns (table)) + return NULL; + + actual_column = gail_clist_get_actual_column (table, column); + + if (clist->columns[actual_column].header) + return (clist->columns[actual_column].header); + + widget = GTK_ACCESSIBLE (clist)->widget; + if (widget == NULL) + return NULL; + + return_widget = gtk_clist_get_column_widget (GTK_CLIST (widget), + actual_column); + if (return_widget == NULL) + return NULL; + + g_return_val_if_fail (GTK_IS_BIN (return_widget), NULL); + return_widget = gtk_bin_get_child (GTK_BIN(return_widget)); + + return gtk_widget_get_accessible (return_widget); +} + +static gint +gail_clist_get_n_rows (AtkTable *table) +{ + GtkWidget *widget; + GtkCList *clist; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + return clist->rows; +} + +static G_CONST_RETURN gchar* +gail_clist_get_row_description (AtkTable *table, + gint row) +{ + GailCListRow* row_data; + + row_data = gail_clist_get_row_data (table, row); + if (row_data == NULL) + return NULL; + return row_data->description; +} + +static AtkObject* +gail_clist_get_row_header (AtkTable *table, + gint row) +{ + GailCListRow* row_data; + + row_data = gail_clist_get_row_data (table, row); + if (row_data == NULL) + return NULL; + return row_data->header; +} + +static AtkObject* +gail_clist_get_summary (AtkTable *table) +{ + GailCList* obj = GAIL_CLIST (table); + + return obj->summary; +} + +static gboolean +gail_clist_add_row_selection (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkCList *clist; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + gtk_clist_select_row (clist, row, -1); + if (gail_clist_is_row_selected (table, row)) + return TRUE; + + return FALSE; +} + +static gboolean +gail_clist_remove_row_selection (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkCList *clist; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + if (gail_clist_is_row_selected (table, row)) + { + gtk_clist_select_row (clist, row, -1); + return TRUE; + } + return FALSE; +} + +static gint +gail_clist_get_selected_rows (AtkTable *table, + gint **rows_selected) +{ + GtkWidget *widget; + GtkCList *clist; + GList *list; + gint n_selected; + gint i; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + + n_selected = g_list_length (clist->selection); + + if (n_selected == 0) + return 0; + + if (rows_selected) + { + gint *selected_rows; + + selected_rows = (gint*) g_malloc (sizeof (gint) * n_selected); + list = clist->selection; + + i = 0; + while (list) + { + selected_rows[i++] = GPOINTER_TO_INT (list->data); + list = list->next; + } + *rows_selected = selected_rows; + } + return n_selected; +} + +static gboolean +gail_clist_is_row_selected (AtkTable *table, + gint row) +{ + GList *elem; + GtkWidget *widget; + GtkCList *clist; + GtkCListRow *clist_row; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + clist = GTK_CLIST (widget); + + if (row < 0 || row >= clist->rows) + return FALSE; + + elem = ROW_ELEMENT (clist, row); + if (!elem) + return FALSE; + clist_row = elem->data; + + return (clist_row->state == GTK_STATE_SELECTED); +} + +static gboolean +gail_clist_is_selected (AtkTable *table, + gint row, + gint column) +{ + return gail_clist_is_row_selected (table, row); +} + +static void +gail_clist_set_caption (AtkTable *table, + AtkObject *caption) +{ + GailCList* obj = GAIL_CLIST (table); + AtkPropertyValues values = { NULL }; + AtkObject *old_caption; + + old_caption = obj->caption; + obj->caption = caption; + if (obj->caption) + g_object_ref (obj->caption); + + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, old_caption); + g_value_init (&values.new_value, G_TYPE_POINTER); + g_value_set_pointer (&values.new_value, obj->caption); + + values.property_name = "accessible-table-caption"; + g_signal_emit_by_name (table, + "property_change::accessible-table-caption", + &values, NULL); + if (old_caption) + g_object_unref (old_caption); +} + +static void +gail_clist_set_column_description (AtkTable *table, + gint column, + const gchar *description) +{ + GailCList *clist = GAIL_CLIST (table); + AtkPropertyValues values = { NULL }; + gint actual_column; + + if (column < 0 || column >= gail_clist_get_n_columns (table)) + return; + + if (description == NULL) + return; + + actual_column = gail_clist_get_actual_column (table, column); + g_free (clist->columns[actual_column].description); + clist->columns[actual_column].description = g_strdup (description); + + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, column); + + values.property_name = "accessible-table-column-description"; + g_signal_emit_by_name (table, + "property_change::accessible-table-column-description", + &values, NULL); + +} + +static void +gail_clist_set_column_header (AtkTable *table, + gint column, + AtkObject *header) +{ + GailCList *clist = GAIL_CLIST (table); + AtkPropertyValues values = { NULL }; + gint actual_column; + + if (column < 0 || column >= gail_clist_get_n_columns (table)) + return; + + actual_column = gail_clist_get_actual_column (table, column); + if (clist->columns[actual_column].header) + g_object_unref (clist->columns[actual_column].header); + if (header) + g_object_ref (header); + clist->columns[actual_column].header = header; + + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, column); + + values.property_name = "accessible-table-column-header"; + g_signal_emit_by_name (table, + "property_change::accessible-table-column-header", + &values, NULL); +} + +static void +gail_clist_set_row_description (AtkTable *table, + gint row, + const gchar *description) +{ + gail_clist_set_row_data (table, row, description, NULL, FALSE); +} + +static void +gail_clist_set_row_header (AtkTable *table, + gint row, + AtkObject *header) +{ + gail_clist_set_row_data (table, row, NULL, header, TRUE); +} + +static void +gail_clist_set_summary (AtkTable *table, + AtkObject *accessible) +{ + GailCList* obj = GAIL_CLIST (table); + AtkPropertyValues values = { 0, }; + AtkObject *old_summary; + + old_summary = obj->summary; + obj->summary = accessible; + if (obj->summary) + g_object_ref (obj->summary); + + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, old_summary); + g_value_init (&values.new_value, G_TYPE_POINTER); + g_value_set_pointer (&values.new_value, obj->summary); + + values.property_name = "accessible-table-summary"; + g_signal_emit_by_name (table, + "property_change::accessible-table-summary", + &values, NULL); + if (old_summary) + g_object_unref (old_summary); +} + + +static void gail_cell_parent_interface_init (GailCellParentIface *iface) +{ + g_return_if_fail (iface); + + iface->get_cell_extents = gail_clist_get_cell_extents; + iface->get_cell_area = gail_clist_get_cell_area; +} + +static void +gail_clist_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GtkWidget* widget; + GtkCList *clist; + gint widget_x, widget_y, widget_width, widget_height; + GdkRectangle cell_rect; + GdkRectangle visible_rect; + + widget = GTK_ACCESSIBLE (parent)->widget; + if (widget == NULL) + return; + clist = GTK_CLIST (widget); + + atk_component_get_extents (ATK_COMPONENT (parent), &widget_x, &widget_y, + &widget_width, &widget_height, + coord_type); + + gail_clist_get_cell_area (parent, cell, &cell_rect); + *width = cell_rect.width; + *height = cell_rect.height; + gail_clist_get_visible_rect (clist, &visible_rect); + if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) + { + *x = cell_rect.x + widget_x; + *y = cell_rect.y + widget_y; + } + else + { + *x = G_MININT; + *y = G_MININT; + } +} + +static void +gail_clist_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect) +{ + GtkWidget* widget; + GtkCList *clist; + gint column, row, n_columns; + + widget = GTK_ACCESSIBLE (parent)->widget; + if (widget == NULL) + return; + clist = GTK_CLIST (widget); + + n_columns = gail_clist_get_n_actual_columns (clist); + g_return_if_fail (n_columns > 0); + column = cell->index % n_columns; + row = cell->index / n_columns; + cell_rect->x = COLUMN_LEFT (clist, column); + cell_rect->y = ROW_TOP (clist, row); + cell_rect->width = clist->column[column].area.width; + cell_rect->height = clist->row_height; +} + +static void +gail_clist_select_row_gtk (GtkCList *clist, + gint row, + gint column, + GdkEvent *event, + gpointer data) +{ + GailCList *gail_clist; + GList *temp_list; + AtkObject *selected_cell; + + gail_clist = GAIL_CLIST (data); + + for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next) + { + GailCListCellData *cell_data; + + cell_data = (GailCListCellData *) (temp_list->data); + + if (row == cell_data->row_number) + { + /* + * Row is selected + */ + gail_cell_add_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE); + } + } + if (clist->columns == 1) + { + selected_cell = gail_clist_ref_at (ATK_TABLE (data), row, 1); + if (selected_cell) + { + if (gail_clist->previous_selected_cell) + g_object_unref (gail_clist->previous_selected_cell); + gail_clist->previous_selected_cell = selected_cell; + gail_cell_add_state (GAIL_CELL (selected_cell), ATK_STATE_FOCUSED, FALSE); + g_signal_emit_by_name (gail_clist, + "active-descendant-changed", + selected_cell); + } + } + + g_signal_emit_by_name (gail_clist, "selection_changed"); +} + +static void +gail_clist_unselect_row_gtk (GtkCList *clist, + gint row, + gint column, + GdkEvent *event, + gpointer data) +{ + GailCList *gail_clist; + GList *temp_list; + + gail_clist = GAIL_CLIST (data); + + for (temp_list = gail_clist->cell_data; temp_list; temp_list = temp_list->next) + { + GailCListCellData *cell_data; + + cell_data = (GailCListCellData *) (temp_list->data); + + if (row == cell_data->row_number) + { + /* + * Row is unselected + */ + gail_cell_add_state (cell_data->gail_cell, ATK_STATE_FOCUSED, FALSE); + gail_cell_remove_state (cell_data->gail_cell, ATK_STATE_SELECTED, TRUE); + } + } + + g_signal_emit_by_name (gail_clist, "selection_changed"); +} + +/* + * This function determines the number of visible columns + * up to and including the specified column + */ +static gint +gail_clist_get_visible_column (AtkTable *table, + int column) +{ + GtkWidget *widget; + GtkCList *clist; + gint i; + gint vis_columns; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + for (i = 0, vis_columns = 0; i < column; i++) + if (clist->column[i].visible) + vis_columns++; + + return vis_columns; +} + +static gint +gail_clist_get_actual_column (AtkTable *table, + int visible_column) +{ + GtkWidget *widget; + GtkCList *clist; + gint i; + gint vis_columns; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + clist = GTK_CLIST (widget); + for (i = 0, vis_columns = 0; i < clist->columns; i++) + { + if (clist->column[i].visible) + { + if (visible_column == vis_columns) + return i; + vis_columns++; + } + } + return 0; +} + +static void +gail_clist_set_row_data (AtkTable *table, + gint row, + const gchar *description, + AtkObject *header, + gboolean is_header) +{ + GtkWidget *widget; + GtkCList *gtk_clist; + GailCList *gail_clist; + GArray *array; + GailCListRow* row_data; + gint i; + gboolean found = FALSE; + AtkPropertyValues values = { NULL }; + gchar *signal_name; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + gtk_clist = GTK_CLIST (widget); + if (row < 0 || row >= gtk_clist->rows) + return; + + gail_clist = GAIL_CLIST (table); + + if (gail_clist->row_data == NULL) + gail_clist->row_data = g_array_sized_new (FALSE, TRUE, + sizeof (GailCListRow *), 0); + + array = gail_clist->row_data; + + for (i = 0; i < array->len; i++) + { + row_data = g_array_index (array, GailCListRow*, i); + + if (row == row_data->row_number) + { + found = TRUE; + if (is_header) + { + if (row_data->header) + g_object_unref (row_data->header); + row_data->header = header; + if (row_data->header) + g_object_ref (row_data->header); + } + else + { + g_free (row_data->description); + row_data->description = g_strdup (row_data->description); + } + break; + } + } + if (!found) + { + GList *elem; + + elem = ROW_ELEMENT (gtk_clist, row); + g_return_if_fail (elem != NULL); + + row_data = g_new (GailCListRow, 1); + row_data->row_number = row; + row_data->row_data = elem->data; + if (is_header) + { + row_data->header = header; + if (row_data->header) + g_object_ref (row_data->header); + row_data->description = NULL; + } + else + { + row_data->description = g_strdup (row_data->description); + row_data->header = NULL; + } + g_array_append_val (array, row_data); + } + + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, row); + + if (is_header) + { + values.property_name = "accessible-table-row-header"; + signal_name = "property_change::accessible-table-row-header"; + } + else + { + values.property_name = "accessible-table-row-description"; + signal_name = "property_change::accessible-table-row-description"; + } + g_signal_emit_by_name (table, + signal_name, + &values, NULL); + +} + +static GailCListRow* +gail_clist_get_row_data (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkCList *clist; + GailCList *obj; + GArray *array; + GailCListRow* row_data; + gint i; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + clist = GTK_CLIST (widget); + if (row < 0 || row >= clist->rows) + return NULL; + + obj = GAIL_CLIST (table); + + if (obj->row_data == NULL) + return NULL; + + array = obj->row_data; + + for (i = 0; i < array->len; i++) + { + row_data = g_array_index (array, GailCListRow*, i); + + if (row == row_data->row_number) + return row_data; + } + + return NULL; +} + +static void +gail_clist_get_visible_rect (GtkCList *clist, + GdkRectangle *clist_rect) +{ + clist_rect->x = - clist->hoffset; + clist_rect->y = - clist->voffset; + clist_rect->width = clist->clist_window_width; + clist_rect->height = clist->clist_window_height; +} + +static gboolean +gail_clist_is_cell_visible (GdkRectangle *cell_rect, + GdkRectangle *visible_rect) +{ + /* + * A cell is reported as visible if any part of the cell is visible + */ + if (((cell_rect->x + cell_rect->width) < visible_rect->x) || + ((cell_rect->y + cell_rect->height) < visible_rect->y) || + (cell_rect->x > (visible_rect->x + visible_rect->width)) || + (cell_rect->y > (visible_rect->y + visible_rect->height))) + return FALSE; + else + return TRUE; +} + +static void +gail_clist_cell_data_new (GailCList *clist, + GailCell *cell, + gint column, + gint row) +{ + GList *elem; + GailCListCellData *cell_data; + GtkCList *gtk_clist; + GtkCListRow *clist_row; + + gtk_clist = GTK_CLIST (GTK_ACCESSIBLE (clist)->widget); + elem = g_list_nth (gtk_clist->row_list, row); + g_return_if_fail (elem != NULL); + clist_row = (GtkCListRow *) elem->data; + cell_data = g_new (GailCListCellData, 1); + cell_data->gail_cell = cell; + cell_data->gtk_cell = &(clist_row->cell[column]); + cell_data->column_number = column; + cell_data->row_number = row; + clist->cell_data = g_list_append (clist->cell_data, cell_data); + + g_object_weak_ref (G_OBJECT (cell), + (GWeakNotify) gail_clist_cell_destroyed, + cell); +} + +static void +gail_clist_cell_destroyed (gpointer data) +{ + GailCell *cell = GAIL_CELL (data); + AtkObject* parent; + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + + gail_clist_cell_data_remove (GAIL_CLIST (parent), cell); +} + +static void +gail_clist_cell_data_remove (GailCList *clist, + GailCell *cell) +{ + GList *temp_list; + + for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) + { + GailCListCellData *cell_data; + + cell_data = (GailCListCellData *) temp_list->data; + if (cell_data->gail_cell == cell) + { + clist->cell_data = g_list_remove_link (clist->cell_data, temp_list); + g_free (cell_data); + return; + } + } + g_warning ("No cell removed in gail_clist_cell_data_remove\n"); +} + +static GailCell* +gail_clist_find_cell (GailCList *clist, + gint index) +{ + GList *temp_list; + gint n_cols; + + n_cols = clist->n_cols; + + for (temp_list = clist->cell_data; temp_list; temp_list = temp_list->next) + { + GailCListCellData *cell_data; + gint real_index; + + cell_data = (GailCListCellData *) (temp_list->data); + + real_index = cell_data->column_number + n_cols * cell_data->row_number; + if (real_index == index) + return cell_data->gail_cell; + } + return NULL; +} + +static void +gail_clist_adjustment_changed (GtkAdjustment *adjustment, + GtkCList *clist) +{ + AtkObject *atk_obj; + GdkRectangle visible_rect; + GdkRectangle cell_rect; + GailCList* obj; + GList *temp_list; + + /* + * The scrollbars have changed + */ + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (clist)); + obj = GAIL_CLIST (atk_obj); + + /* Get the currently visible area */ + gail_clist_get_visible_rect (clist, &visible_rect); + + /* loop over the cells and report if they are visible or not. */ + /* Must loop through them all */ + for (temp_list = obj->cell_data; temp_list; temp_list = temp_list->next) + { + GailCell *cell; + GailCListCellData *cell_data; + + cell_data = (GailCListCellData *) (temp_list->data); + cell = cell_data->gail_cell; + + gail_clist_get_cell_area (GAIL_CELL_PARENT (atk_obj), + cell, &cell_rect); + if (gail_clist_is_cell_visible (&cell_rect, &visible_rect)) + gail_cell_add_state (cell, ATK_STATE_SHOWING, TRUE); + else + gail_cell_remove_state (cell, ATK_STATE_SHOWING, TRUE); + } + g_signal_emit_by_name (atk_obj, "visible_data_changed"); +} + diff --git a/modules/other/gail/gailclist.h b/modules/other/gail/gailclist.h new file mode 100644 index 000000000..92d1df692 --- /dev/null +++ b/modules/other/gail/gailclist.h @@ -0,0 +1,72 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CLIST_H__ +#define __GAIL_CLIST_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CLIST (gail_clist_get_type ()) +#define GAIL_CLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CLIST, GailCList)) +#define GAIL_CLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CLIST, GailCListClass)) +#define GAIL_IS_CLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CLIST)) +#define GAIL_IS_CLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CLIST)) +#define GAIL_CLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CLIST, GailCListClass)) + +typedef struct _GailCList GailCList; +typedef struct _GailCListClass GailCListClass; + +typedef struct _GailCListColumn GailCListColumn; + +struct _GailCList +{ + GailContainer parent; + + AtkObject* caption; + AtkObject* summary; + + /* dynamically allocated array of column structures */ + GailCListColumn *columns; + /* private */ + gint n_cols; + GArray *row_data; + GList *cell_data; + AtkObject *previous_selected_cell; +}; + +GType gail_clist_get_type (void); + +struct _GailCListClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_clist_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CLIST_H__ */ diff --git a/modules/other/gail/gailclistcell.c b/modules/other/gail/gailclistcell.c new file mode 100644 index 000000000..09a6b4a84 --- /dev/null +++ b/modules/other/gail/gailclistcell.c @@ -0,0 +1,121 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailclistcell.h" + +static void gail_clist_cell_class_init (GailCListCellClass *klass); + +static G_CONST_RETURN gchar* gail_clist_cell_get_name (AtkObject *accessible); + +GType +gail_clist_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailCListCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_clist_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCListCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CELL, + "GailCListCell", &tinfo, 0); + } + return type; +} + +static void +gail_clist_cell_class_init (GailCListCellClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_name = gail_clist_cell_get_name; +} + +AtkObject* +gail_clist_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + + object = g_object_new (GAIL_TYPE_CLIST_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + g_return_val_if_fail (!ATK_IS_TEXT (atk_object), NULL); + + return atk_object; +} + +static G_CONST_RETURN gchar* +gail_clist_cell_get_name (AtkObject *accessible) +{ + if (accessible->name) + return accessible->name; + else + { + /* + * Get the cell's text if it exists + */ + GailCell *cell = GAIL_CELL (accessible); + GtkWidget* widget = cell->widget; + GtkCellType cell_type; + GtkCList *clist; + gchar *text = NULL; + gint row, column; + + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + clist = GTK_CLIST (widget); + g_return_val_if_fail (clist->columns, NULL); + row = cell->index / clist->columns; + column = cell->index % clist->columns; + cell_type = gtk_clist_get_cell_type (clist, row, column); + switch (cell_type) + { + case GTK_CELL_TEXT: + gtk_clist_get_text (clist, row, column, &text); + break; + case GTK_CELL_PIXTEXT: + gtk_clist_get_pixtext (clist, row, column, &text, NULL, NULL, NULL); + break; + default: + break; + } + return text; + } +} diff --git a/modules/other/gail/gailclistcell.h b/modules/other/gail/gailclistcell.h new file mode 100644 index 000000000..0cd208ce2 --- /dev/null +++ b/modules/other/gail/gailclistcell.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CLIST_CELL_H__ +#define __GAIL_CLIST_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailcell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CLIST_CELL (gail_clist_cell_get_type ()) +#define GAIL_CLIST_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CLIST_CELL, GailCListCell)) +#define GAIL_CLIST_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CLIST_CELL, GailCListCellClass)) +#define GAIL_IS_CLIST_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CLIST_CELL)) +#define GAIL_IS_CLIST_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CLIST_CELL)) +#define GAIL_CLIST_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CLIST_CELL, GailCListCellClass)) + +typedef struct _GailCListCell GailCListCell; +typedef struct _GailCListCellClass GailCListCellClass; + +struct _GailCListCell +{ + GailCell parent; +}; + +GType gail_clist_cell_get_type (void); + +struct _GailCListCellClass +{ + GailCellClass parent_class; +}; + +AtkObject *gail_clist_cell_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CLIST_CELL_H__ */ diff --git a/modules/other/gail/gailcombo.c b/modules/other/gail/gailcombo.c new file mode 100644 index 000000000..45d6ade37 --- /dev/null +++ b/modules/other/gail/gailcombo.c @@ -0,0 +1,714 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailcombo.h" + +static void gail_combo_class_init (GailComboClass *klass); +static void gail_combo_object_init (GailCombo *combo); +static void gail_combo_real_initialize (AtkObject *obj, + gpointer data); + +static void gail_combo_selection_changed_gtk (GtkWidget *widget, + gpointer data); + +static gint gail_combo_get_n_children (AtkObject *obj); +static AtkObject* gail_combo_ref_child (AtkObject *obj, + gint i); +static void gail_combo_finalize (GObject *object); +static void atk_action_interface_init (AtkActionIface *iface); + +static gboolean gail_combo_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_combo_get_n_actions (AtkAction *action) +; +static G_CONST_RETURN gchar* gail_combo_get_description(AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_combo_get_name (AtkAction *action, + gint i); +static gboolean gail_combo_set_description(AtkAction *action, + gint i, + const gchar *desc); + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_combo_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_combo_clear_selection (AtkSelection *selection); +static AtkObject* gail_combo_ref_selection (AtkSelection *selection, + gint i); +static gint gail_combo_get_selection_count (AtkSelection *selection); +static gboolean gail_combo_is_child_selected (AtkSelection *selection, + gint i); +static gboolean gail_combo_remove_selection (AtkSelection *selection, + gint i); + +static gint _gail_combo_button_release (gpointer data); +static gint _gail_combo_popup_release (gpointer data); + + +static gpointer parent_class = NULL; + +GType +gail_combo_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailComboClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_combo_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailCombo), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_combo_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailCombo", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + } + + return type; +} + +static void +gail_combo_class_init (GailComboClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_combo_finalize; + + parent_class = g_type_class_peek_parent (klass); + + class->get_n_children = gail_combo_get_n_children; + class->ref_child = gail_combo_ref_child; + class->initialize = gail_combo_real_initialize; +} + +static void +gail_combo_object_init (GailCombo *combo) +{ + combo->press_description = NULL; + combo->old_selection = NULL; + combo->deselect_idle_handler = 0; + combo->select_idle_handler = 0; +} + +AtkObject* +gail_combo_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_COMBO (widget), NULL); + + object = g_object_new (GAIL_TYPE_COMBO, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_combo_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkCombo *combo; + GtkList *list; + GList *slist; + GailCombo *gail_combo; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + combo = GTK_COMBO (data); + + list = GTK_LIST (combo->list); + slist = list->selection; + + gail_combo = GAIL_COMBO (obj); + if (slist && slist->data) + { + gail_combo->old_selection = slist->data; + } + g_signal_connect (combo->list, + "selection_changed", + G_CALLBACK (gail_combo_selection_changed_gtk), + data); + atk_object_set_parent (gtk_widget_get_accessible (combo->entry), obj); + atk_object_set_parent (gtk_widget_get_accessible (combo->popup), obj); + + obj->role = ATK_ROLE_COMBO_BOX; +} + +static gboolean +notify_deselect (gpointer data) +{ + GailCombo *combo; + + GDK_THREADS_ENTER (); + + combo = GAIL_COMBO (data); + + combo->old_selection = NULL; + combo->deselect_idle_handler = 0; + g_signal_emit_by_name (data, "selection_changed"); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gboolean +notify_select (gpointer data) +{ + GailCombo *combo; + + GDK_THREADS_ENTER (); + + combo = GAIL_COMBO (data); + + combo->select_idle_handler = 0; + g_signal_emit_by_name (data, "selection_changed"); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +gail_combo_selection_changed_gtk (GtkWidget *widget, + gpointer data) +{ + GtkCombo *combo; + GtkList *list; + GList *slist; + AtkObject *obj; + GailCombo *gail_combo; + + combo = GTK_COMBO (data); + list = GTK_LIST (combo->list); + + slist = list->selection; + + obj = gtk_widget_get_accessible (GTK_WIDGET (data)); + gail_combo = GAIL_COMBO (obj); + if (slist && slist->data) + { + if (slist->data != gail_combo->old_selection) + { + gail_combo->old_selection = slist->data; + if (gail_combo->select_idle_handler == 0) + gail_combo->select_idle_handler = g_idle_add (notify_select, gail_combo); + } + if (gail_combo->deselect_idle_handler) + { + g_source_remove (gail_combo->deselect_idle_handler); + gail_combo->deselect_idle_handler = 0; + } + } + else + { + if (gail_combo->deselect_idle_handler == 0) + gail_combo->deselect_idle_handler = g_idle_add (notify_deselect, gail_combo); + if (gail_combo->select_idle_handler) + { + g_source_remove (gail_combo->select_idle_handler); + gail_combo->select_idle_handler = 0; + } + } +} + +/* + * The children of a GailCombo are the list of items and the entry field + */ +static gint +gail_combo_get_n_children (AtkObject* obj) +{ + gint n_children = 2; + GtkWidget *widget; + + g_return_val_if_fail (GAIL_IS_COMBO (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + return n_children; +} + +static AtkObject* +gail_combo_ref_child (AtkObject *obj, + gint i) +{ + AtkObject *accessible; + GtkWidget *widget; + + g_return_val_if_fail (GAIL_IS_COMBO (obj), NULL); + + if (i < 0 || i > 1) + return NULL; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + if (i == 0) + accessible = gtk_widget_get_accessible (GTK_COMBO (widget)->popup); + else + accessible = gtk_widget_get_accessible (GTK_COMBO (widget)->entry); + + g_object_ref (accessible); + return accessible; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_combo_do_action; + iface->get_n_actions = gail_combo_get_n_actions; + iface->get_description = gail_combo_get_description; + iface->get_name = gail_combo_get_name; + iface->set_description = gail_combo_set_description; +} + +static gboolean +gail_combo_do_action (AtkAction *action, + gint i) +{ + GailCombo *combo; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + combo = GAIL_COMBO (action); + if (i == 0) + { + if (combo->action_idle_handler) + return FALSE; + + combo->action_idle_handler = g_idle_add (idle_do_action, combo); + return TRUE; + } + else + return FALSE; +} + +/* + * This action is the pressing of the button on the combo box. + * The behavior is different depending on whether the list is being + * displayed or removed. + * + * A button press event is simulated on the appropriate widget and + * a button release event is simulated in an idle function. + */ +static gboolean +idle_do_action (gpointer data) +{ + GtkCombo *combo; + GtkWidget *action_widget; + GtkWidget *widget; + GailCombo *gail_combo; + gboolean do_popup; + GdkEvent tmp_event; + + GDK_THREADS_ENTER (); + + gail_combo = GAIL_COMBO (data); + gail_combo->action_idle_handler = 0; + widget = GTK_ACCESSIBLE (gail_combo)->widget; + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + combo = GTK_COMBO (widget); + + do_popup = !GTK_WIDGET_MAPPED (combo->popwin); + + tmp_event.button.type = GDK_BUTTON_PRESS; + tmp_event.button.window = widget->window; + tmp_event.button.button = 1; + tmp_event.button.send_event = TRUE; + tmp_event.button.time = GDK_CURRENT_TIME; + tmp_event.button.axes = NULL; + + if (do_popup) + { + /* Pop up list */ + action_widget = combo->button; + + gtk_widget_event (action_widget, &tmp_event); + + g_idle_add (_gail_combo_button_release, combo); + } + else + { + /* Pop down list */ + tmp_event.button.window = combo->list->window; + gdk_window_set_user_data (combo->list->window, combo->button); + action_widget = combo->popwin; + + gtk_widget_event (action_widget, &tmp_event); + g_idle_add (_gail_combo_popup_release, combo); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_combo_get_n_actions (AtkAction *action) +{ + /* + * The default behavior of a combo box is to have one action - + */ + return 1; +} + +static G_CONST_RETURN gchar* +gail_combo_get_description (AtkAction *action, + gint i) +{ + if (i == 0) + { + GailCombo *combo; + + combo = GAIL_COMBO (action); + return combo->press_description; + } + else + return NULL; +} + +static G_CONST_RETURN gchar* +gail_combo_get_name (AtkAction *action, + gint i) +{ + if (i == 0) + return "press"; + else + return NULL; +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_combo_add_selection; + iface->clear_selection = gail_combo_clear_selection; + iface->ref_selection = gail_combo_ref_selection; + iface->get_selection_count = gail_combo_get_selection_count; + iface->is_child_selected = gail_combo_is_child_selected; + iface->remove_selection = gail_combo_remove_selection; + /* + * select_all_selection does not make sense for a combo box + * so no implementation is provided. + */ +} + +static gboolean +gail_combo_add_selection (AtkSelection *selection, + gint i) +{ + GtkCombo *combo; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo = GTK_COMBO (widget); + + gtk_list_select_item (GTK_LIST (combo->list), i); + return TRUE; +} + +static gboolean +gail_combo_clear_selection (AtkSelection *selection) +{ + GtkCombo *combo; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo = GTK_COMBO (widget); + + gtk_list_unselect_all (GTK_LIST (combo->list)); + return TRUE; +} + +static AtkObject* +gail_combo_ref_selection (AtkSelection *selection, + gint i) +{ + GtkCombo *combo; + GList * list; + GtkWidget *item; + AtkObject *obj; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + combo = GTK_COMBO (widget); + + /* + * A combo box can have only one selection. + */ + if (i != 0) + return NULL; + + list = GTK_LIST (combo->list)->selection; + + if (list == NULL) + return NULL; + + item = GTK_WIDGET (list->data); + + obj = gtk_widget_get_accessible (item); + g_object_ref (obj); + return obj; +} + +static gint +gail_combo_get_selection_count (AtkSelection *selection) +{ + GtkCombo *combo; + GList * list; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + combo = GTK_COMBO (widget); + + /* + * The number of children currently selected is either 1 or 0 so we + * do not bother to count the elements of the selected list. + */ + list = GTK_LIST (combo->list)->selection; + + if (list == NULL) + return 0; + else + return 1; +} + +static gboolean +gail_combo_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkCombo *combo; + GList * list; + GtkWidget *item; + gint j; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo = GTK_COMBO (widget); + + list = GTK_LIST (combo->list)->selection; + + if (list == NULL) + return FALSE; + + item = GTK_WIDGET (list->data); + + j = g_list_index (GTK_LIST (combo->list)->children, item); + + return (j == i); +} + +static gboolean +gail_combo_remove_selection (AtkSelection *selection, + gint i) +{ + if (atk_selection_is_child_selected (selection, i)) + atk_selection_clear_selection (selection); + + return TRUE; +} + +static gint +_gail_combo_popup_release (gpointer data) +{ + GtkCombo *combo; + GtkWidget *action_widget; + GdkEvent tmp_event; + + GDK_THREADS_ENTER (); + + combo = GTK_COMBO (data); + if (combo->current_button == 0) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + tmp_event.button.type = GDK_BUTTON_RELEASE; + tmp_event.button.button = 1; + tmp_event.button.time = GDK_CURRENT_TIME; + action_widget = combo->button; + + gtk_widget_event (action_widget, &tmp_event); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +_gail_combo_button_release (gpointer data) +{ + GtkCombo *combo; + GtkWidget *action_widget; + GdkEvent tmp_event; + + GDK_THREADS_ENTER (); + + combo = GTK_COMBO (data); + if (combo->current_button == 0) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + tmp_event.button.type = GDK_BUTTON_RELEASE; + tmp_event.button.button = 1; + tmp_event.button.window = combo->list->window; + tmp_event.button.time = GDK_CURRENT_TIME; + gdk_window_set_user_data (combo->list->window, combo->button); + action_widget = combo->list; + + gtk_widget_event (action_widget, &tmp_event); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gboolean +gail_combo_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + if (i == 0) + { + GailCombo *combo; + + combo = GAIL_COMBO (action); + g_free (combo->press_description); + combo->press_description = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} + +static void +gail_combo_finalize (GObject *object) +{ + GailCombo *combo = GAIL_COMBO (object); + + g_free (combo->press_description); + if (combo->action_idle_handler) + { + g_source_remove (combo->action_idle_handler); + combo->action_idle_handler = 0; + } + if (combo->deselect_idle_handler) + { + g_source_remove (combo->deselect_idle_handler); + combo->deselect_idle_handler = 0; + } + if (combo->select_idle_handler) + { + g_source_remove (combo->select_idle_handler); + combo->select_idle_handler = 0; + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailcombo.h b/modules/other/gail/gailcombo.h new file mode 100644 index 000000000..79157af44 --- /dev/null +++ b/modules/other/gail/gailcombo.h @@ -0,0 +1,67 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_COMBO_H__ +#define __GAIL_COMBO_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_COMBO (gail_combo_get_type ()) +#define GAIL_COMBO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_COMBO, GailCombo)) +#define GAIL_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_COMBO, GailComboClass)) +#define GAIL_IS_COMBO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_COMBO)) +#define GAIL_IS_COMBO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_COMBO)) +#define GAIL_COMBO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_COMBO, GailComboClass)) + +typedef struct _GailCombo GailCombo; +typedef struct _GailComboClass GailComboClass; + +struct _GailCombo +{ + GailContainer parent; + + gchar *press_description; + guint action_idle_handler; + + gpointer old_selection; + guint select_idle_handler; + guint deselect_idle_handler; +}; + +GType gail_combo_get_type (void); + +struct _GailComboClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_combo_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_COMBO_H__ */ diff --git a/modules/other/gail/gailcombobox.c b/modules/other/gail/gailcombobox.c new file mode 100644 index 000000000..316e719f1 --- /dev/null +++ b/modules/other/gail/gailcombobox.c @@ -0,0 +1,686 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailcombobox.h" + +#if GTK_MINOR_VERSION > 4 +#define GAIL_COMBOX_BOX_A11y_COMPLETE +#endif + +static void gail_combo_box_class_init (GailComboBoxClass *klass); +static void gail_combo_box_object_init (GailComboBox *combo_box); +static void gail_combo_box_real_initialize (AtkObject *obj, + gpointer data); + +static void gail_combo_box_changed_gtk (GtkWidget *widget); + +static G_CONST_RETURN gchar* gail_combo_box_get_name (AtkObject *obj); +static gint gail_combo_box_get_n_children (AtkObject *obj); +static AtkObject* gail_combo_box_ref_child (AtkObject *obj, + gint i); +static void gail_combo_box_finalize (GObject *object); +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE +static void atk_action_interface_init (AtkActionIface *iface); + +static gboolean gail_combo_box_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_combo_box_get_n_actions (AtkAction *action) +; +static G_CONST_RETURN gchar* gail_combo_box_get_description(AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_combo_box_get_keybinding (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_combo_box_action_get_name(AtkAction *action, + gint i); +static gboolean gail_combo_box_set_description(AtkAction *action, + gint i, + const gchar *desc); +#endif + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_combo_box_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_combo_box_clear_selection (AtkSelection *selection); +static AtkObject* gail_combo_box_ref_selection (AtkSelection *selection, + gint i); +static gint gail_combo_box_get_selection_count (AtkSelection *selection); +static gboolean gail_combo_box_is_child_selected (AtkSelection *selection, + gint i); +static gboolean gail_combo_box_remove_selection (AtkSelection *selection, + gint i); + +static gpointer parent_class = NULL; + +GType +gail_combo_box_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailComboBoxClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_combo_box_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailComboBox), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_combo_box_object_init, /* instance init */ + NULL /* value table */ + }; + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; +#endif + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailComboBox", &tinfo, 0); + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); +#endif + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + } + + return type; +} + +static void +gail_combo_box_class_init (GailComboBoxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_combo_box_finalize; + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_combo_box_get_name; + class->get_n_children = gail_combo_box_get_n_children; + class->ref_child = gail_combo_box_ref_child; + class->initialize = gail_combo_box_real_initialize; +} + +static void +gail_combo_box_object_init (GailComboBox *combo_box) +{ + combo_box->press_description = NULL; + combo_box->press_keybinding = NULL; + combo_box->old_selection = -1; + combo_box->name = NULL; + combo_box->popup_set = FALSE; +} + +AtkObject* +gail_combo_box_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_COMBO_BOX (widget), NULL); + + object = g_object_new (GAIL_TYPE_COMBO_BOX, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_combo_box_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkComboBox *combo_box; + GailComboBox *gail_combo_box; +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + AtkObject *popup; +#endif + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + combo_box = GTK_COMBO_BOX (data); + + gail_combo_box = GAIL_COMBO_BOX (obj); + + g_signal_connect (combo_box, + "changed", + G_CALLBACK (gail_combo_box_changed_gtk), + NULL); + gail_combo_box->old_selection = gtk_combo_box_get_active (combo_box); + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + popup = gtk_combo_box_get_popup_accessible (combo_box); + if (popup) + { + atk_object_set_parent (popup, obj); + gail_combo_box->popup_set = TRUE; + } + if (GTK_IS_COMBO_BOX_ENTRY (combo_box)) + atk_object_set_parent (gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (combo_box))), obj); +#endif + + obj->role = ATK_ROLE_COMBO_BOX; +} + +static void +gail_combo_box_changed_gtk (GtkWidget *widget) +{ + GtkComboBox *combo_box; + AtkObject *obj; + GailComboBox *gail_combo_box; + gint index; + + combo_box = GTK_COMBO_BOX (widget); + + index = gtk_combo_box_get_active (combo_box); + obj = gtk_widget_get_accessible (widget); + gail_combo_box = GAIL_COMBO_BOX (obj); + if (gail_combo_box->old_selection != index) + { + gail_combo_box->old_selection = index; + g_signal_emit_by_name (obj, "selection_changed"); + } +} + +static G_CONST_RETURN gchar* +gail_combo_box_get_name (AtkObject *obj) +{ + GtkWidget *widget; + GtkComboBox *combo_box; + GailComboBox *gail_combo_box; + GtkTreeIter iter; + G_CONST_RETURN gchar *name; + GtkTreeModel *model; + gint n_columns; + gint i; + + g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name) + return name; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + combo_box = GTK_COMBO_BOX (widget); + gail_combo_box = GAIL_COMBO_BOX (obj); + if (gtk_combo_box_get_active_iter (combo_box, &iter)) + { + model = gtk_combo_box_get_model (combo_box); + n_columns = gtk_tree_model_get_n_columns (model); + for (i = 0; i < n_columns; i++) + { + GValue value = { 0, }; + + gtk_tree_model_get_value (model, &iter, i, &value); + if (G_VALUE_HOLDS_STRING (&value)) + { + if (gail_combo_box->name) g_free (gail_combo_box->name); + gail_combo_box->name = g_strdup ((gchar *) + g_value_get_string (&value)); + g_value_unset (&value); + break; + } + else + g_value_unset (&value); + } + } + return gail_combo_box->name; +} + +/* + * The children of a GailComboBox are the list of items and the entry field + * if it is editable. + */ +static gint +gail_combo_box_get_n_children (AtkObject* obj) +{ + gint n_children = 0; + GtkWidget *widget; + + g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + n_children++; + if (GTK_IS_COMBO_BOX_ENTRY (widget)) + n_children ++; +#endif + + return n_children; +} + +static AtkObject* +gail_combo_box_ref_child (AtkObject *obj, + gint i) +{ + GtkWidget *widget; +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + AtkObject *child; + GailComboBox *box; +#endif + + g_return_val_if_fail (GAIL_IS_COMBO_BOX (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + if (i == 0) + { + child = gtk_combo_box_get_popup_accessible (GTK_COMBO_BOX (widget)); + box = GAIL_COMBO_BOX (obj); + if (box->popup_set == FALSE) + { + atk_object_set_parent (child, obj); + box->popup_set = TRUE; + } + } + else if (i == 1 && GTK_IS_COMBO_BOX_ENTRY (widget)) + { + child = gtk_widget_get_accessible (gtk_bin_get_child (GTK_BIN (widget))); + } + else + { + return NULL; + } + return g_object_ref (child); +#else + return NULL; +#endif +} + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_combo_box_do_action; + iface->get_n_actions = gail_combo_box_get_n_actions; + iface->get_description = gail_combo_box_get_description; + iface->get_keybinding = gail_combo_box_get_keybinding; + iface->get_name = gail_combo_box_action_get_name; + iface->set_description = gail_combo_box_set_description; +} + +static gboolean +gail_combo_box_do_action (AtkAction *action, + gint i) +{ + GailComboBox *combo_box; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + combo_box = GAIL_COMBO_BOX (action); + if (i == 0) + { + if (combo_box->action_idle_handler) + return FALSE; + + combo_box->action_idle_handler = g_idle_add (idle_do_action, combo_box); + return TRUE; + } + else + return FALSE; +} + +static gboolean +idle_do_action (gpointer data) +{ + GtkComboBox *combo_box; + GtkWidget *widget; + GailComboBox *gail_combo_box; + AtkObject *popup; + gboolean do_popup; + + GDK_THREADS_ENTER (); + + gail_combo_box = GAIL_COMBO_BOX (data); + gail_combo_box->action_idle_handler = 0; + widget = GTK_ACCESSIBLE (gail_combo_box)->widget; + if (widget == NULL || /* State is defunct */ + !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + combo_box = GTK_COMBO_BOX (widget); + + popup = gtk_combo_box_get_popup_accessible (combo_box); + do_popup = !GTK_WIDGET_MAPPED (GTK_ACCESSIBLE (popup)->widget); + if (do_popup) + gtk_combo_box_popup (combo_box); + else + gtk_combo_box_popdown (combo_box); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_combo_box_get_n_actions (AtkAction *action) +{ + /* + * The default behavior of a combo_box box is to have one action - + */ + return 1; +} + +static G_CONST_RETURN gchar* +gail_combo_box_get_description (AtkAction *action, + gint i) +{ + if (i == 0) + { + GailComboBox *combo_box; + + combo_box = GAIL_COMBO_BOX (action); + return combo_box->press_description; + } + else + return NULL; +} + +static G_CONST_RETURN gchar* +gail_combo_box_get_keybinding (AtkAction *action, + gint i) +{ + GailComboBox *combo_box; + gchar *return_value = NULL; + combo_box = GAIL_COMBO_BOX (action); + switch (i) + { + case 0: + { + GtkWidget *widget; + GtkWidget *label; + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + guint key_val; + + combo_box = GAIL_COMBO_BOX (action); + widget = GTK_ACCESSIBLE (combo_box)->widget; + if (widget == NULL) + return NULL; + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + if (!set) + return NULL; + label = NULL; + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + target_object = g_ptr_array_index (target, 0); + if (GTK_IS_ACCESSIBLE (target_object)) + { + label = GTK_ACCESSIBLE (target_object)->widget; + } + } + g_object_unref (set); + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + g_free (combo_box->press_keybinding); + combo_box->press_keybinding = return_value; + break; } + default: + break; + } + return return_value; +} + + +static G_CONST_RETURN gchar* +gail_combo_box_action_get_name (AtkAction *action, + gint i) +{ + if (i == 0) + return "press"; + else + return NULL; +} + +static gboolean +gail_combo_box_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + if (i == 0) + { + GailComboBox *combo_box; + + combo_box = GAIL_COMBO_BOX (action); + g_free (combo_box->press_description); + combo_box->press_description = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} +#endif + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_combo_box_add_selection; + iface->clear_selection = gail_combo_box_clear_selection; + iface->ref_selection = gail_combo_box_ref_selection; + iface->get_selection_count = gail_combo_box_get_selection_count; + iface->is_child_selected = gail_combo_box_is_child_selected; + iface->remove_selection = gail_combo_box_remove_selection; + /* + * select_all_selection does not make sense for a combo_box + * so no implementation is provided. + */ +} + +static gboolean +gail_combo_box_add_selection (AtkSelection *selection, + gint i) +{ + GtkComboBox *combo_box; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo_box = GTK_COMBO_BOX (widget); + + gtk_combo_box_set_active (combo_box, i); + return TRUE; +} + +static gboolean +gail_combo_box_clear_selection (AtkSelection *selection) +{ + GtkComboBox *combo_box; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo_box = GTK_COMBO_BOX (widget); + + gtk_combo_box_set_active (combo_box, -1); + return TRUE; +} + +static AtkObject* +gail_combo_box_ref_selection (AtkSelection *selection, + gint i) +{ + GtkComboBox *combo_box; + GtkWidget *widget; +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + AtkObject *obj; + gint index; +#endif + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + combo_box = GTK_COMBO_BOX (widget); + + /* + * A combo_box box can have only one selection. + */ + if (i != 0) + return NULL; + +#ifdef GAIL_COMBOX_BOX_A11y_COMPLETE + obj = gtk_combo_box_get_popup_accessible (combo_box); + index = gtk_combo_box_get_active (combo_box); + return atk_object_ref_accessible_child (obj, index); +#else + return NULL; +#endif +} + +static gint +gail_combo_box_get_selection_count (AtkSelection *selection) +{ + GtkComboBox *combo_box; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + combo_box = GTK_COMBO_BOX (widget); + + return (gtk_combo_box_get_active (combo_box) == -1) ? 0 : 1; +} + +static gboolean +gail_combo_box_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkComboBox *combo_box; + gint j; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + combo_box = GTK_COMBO_BOX (widget); + + j = gtk_combo_box_get_active (combo_box); + + return (j == i); +} + +static gboolean +gail_combo_box_remove_selection (AtkSelection *selection, + gint i) +{ + if (atk_selection_is_child_selected (selection, i)) + atk_selection_clear_selection (selection); + + return TRUE; +} + +static void +gail_combo_box_finalize (GObject *object) +{ + GailComboBox *combo_box = GAIL_COMBO_BOX (object); + + g_free (combo_box->press_description); + g_free (combo_box->press_keybinding); + g_free (combo_box->name); + if (combo_box->action_idle_handler) + { + g_source_remove (combo_box->action_idle_handler); + combo_box->action_idle_handler = 0; + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailcombobox.h b/modules/other/gail/gailcombobox.h new file mode 100644 index 000000000..f9307296d --- /dev/null +++ b/modules/other/gail/gailcombobox.h @@ -0,0 +1,68 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_COMBO_BOX_H__ +#define __GAIL_COMBO_BOX_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_COMBO_BOX (gail_combo_box_get_type ()) +#define GAIL_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_COMBO_BOX, GailComboBox)) +#define GAIL_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_COMBO_BOX, GailComboBoxClass)) +#define GAIL_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_COMBO_BOX)) +#define GAIL_IS_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_COMBO_BOX)) +#define GAIL_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_COMBO_BOX, GailComboBoxClass)) + +typedef struct _GailComboBox GailComboBox; +typedef struct _GailComboBoxClass GailComboBoxClass; + +struct _GailComboBox +{ + GailContainer parent; + + gchar *press_keybinding; + gchar *press_description; + guint action_idle_handler; + + gchar *name; + gint old_selection; + gboolean popup_set; +}; + +GType gail_combo_box_get_type (void); + +struct _GailComboBoxClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_combo_box_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_COMBO_BOX_H__ */ diff --git a/modules/other/gail/gailcontainer.c b/modules/other/gail/gailcontainer.c new file mode 100644 index 000000000..23af9124c --- /dev/null +++ b/modules/other/gail/gailcontainer.c @@ -0,0 +1,298 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailcontainer.h" + +static void gail_container_class_init (GailContainerClass *klass); +static void gail_container_object_init (GailContainer *container); + +static gint gail_container_get_n_children (AtkObject *obj); +static AtkObject* gail_container_ref_child (AtkObject *obj, + gint i); +static gint gail_container_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); +static gint gail_container_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); +static gint gail_container_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); +static gint gail_container_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); + +static void gail_container_real_initialize (AtkObject *obj, + gpointer data); + +static void gail_container_finalize (GObject *object); + +static GailWidgetClass *parent_class = NULL; + +GType +gail_container_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailContainerClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_container_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailContainer), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_container_object_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailContainer", &tinfo, 0); + } + + return type; +} + +static void +gail_container_class_init (GailContainerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_container_finalize; + + class->get_n_children = gail_container_get_n_children; + class->ref_child = gail_container_ref_child; + class->initialize = gail_container_real_initialize; + + klass->add_gtk = gail_container_real_add_gtk; + klass->remove_gtk = gail_container_real_remove_gtk; +} + +static void +gail_container_object_init (GailContainer *container) +{ + container->children = NULL; +} + +AtkObject* +gail_container_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_CONTAINER (widget), NULL); + + object = g_object_new (GAIL_TYPE_CONTAINER, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static gint +gail_container_get_n_children (AtkObject* obj) +{ + GtkWidget *widget; + GList *children; + gint count = 0; + + g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return 0; + + children = gtk_container_get_children (GTK_CONTAINER(widget)); + count = g_list_length (children); + g_list_free (children); + + return count; +} + +static AtkObject* +gail_container_ref_child (AtkObject *obj, + gint i) +{ + GList *children, *tmp_list; + AtkObject *accessible; + GtkWidget *widget; + + g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL); + g_return_val_if_fail ((i >= 0), NULL); + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + tmp_list = g_list_nth (children, i); + if (!tmp_list) + { + g_list_free (children); + return NULL; + } + accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data)); + + g_list_free (children); + g_object_ref (accessible); + return accessible; +} + +static gint +gail_container_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + GailContainer *gail_container = GAIL_CONTAINER (data); + GailContainerClass *klass; + + klass = GAIL_CONTAINER_GET_CLASS (gail_container); + + if (klass->add_gtk) + return klass->add_gtk (container, widget, data); + else + return 1; +} + +static gint +gail_container_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + GailContainer *gail_container = GAIL_CONTAINER (data); + GailContainerClass *klass; + + klass = GAIL_CONTAINER_GET_CLASS (gail_container); + + if (klass->remove_gtk) + return klass->remove_gtk (container, widget, data); + else + return 1; +} + +static gint +gail_container_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + AtkObject* atk_parent = ATK_OBJECT (data); + AtkObject* atk_child = gtk_widget_get_accessible (widget); + GailContainer *gail_container = GAIL_CONTAINER (atk_parent); + gint index; + + g_object_notify (G_OBJECT (atk_child), "accessible_parent"); + + g_list_free (gail_container->children); + gail_container->children = gtk_container_get_children (container); + index = g_list_index (gail_container->children, widget); + g_signal_emit_by_name (atk_parent, "children_changed::add", + index, atk_child, NULL); + + return 1; +} + +static gint +gail_container_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + AtkPropertyValues values = { NULL }; + AtkObject* atk_parent; + AtkObject *atk_child; + GailContainer *gail_container; + gint index; + + atk_parent = ATK_OBJECT (data); + atk_child = gtk_widget_get_accessible (widget); + + if (atk_child) + { + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, atk_parent); + + values.property_name = "accessible-parent"; + + g_object_ref (atk_child); + g_signal_emit_by_name (atk_child, + "property_change::accessible-parent", &values, NULL); + g_object_unref (atk_child); + } + gail_container = GAIL_CONTAINER (atk_parent); + index = g_list_index (gail_container->children, widget); + g_list_free (gail_container->children); + gail_container->children = gtk_container_get_children (container); + if (index >= 0 && index <= g_list_length (gail_container->children)) + g_signal_emit_by_name (atk_parent, "children_changed::remove", + index, atk_child, NULL); + + return 1; +} + +static void +gail_container_real_initialize (AtkObject *obj, + gpointer data) +{ + GailContainer *container = GAIL_CONTAINER (obj); + guint handler_id; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + container->children = gtk_container_get_children (GTK_CONTAINER (data)); + + /* + * We store the handler ids for these signals in case some objects + * need to remove these handlers. + */ + handler_id = g_signal_connect (data, + "add", + G_CALLBACK (gail_container_add_gtk), + obj); + g_object_set_data (G_OBJECT (obj), "gail-add-handler-id", + GUINT_TO_POINTER (handler_id)); + handler_id = g_signal_connect (data, + "remove", + G_CALLBACK (gail_container_remove_gtk), + obj); + g_object_set_data (G_OBJECT (obj), "gail-remove-handler-id", + GUINT_TO_POINTER (handler_id)); + + if (GTK_IS_TOOLBAR (data)) + obj->role = ATK_ROLE_TOOL_BAR; + else if (GTK_IS_VIEWPORT (data)) + obj->role = ATK_ROLE_VIEWPORT; + else + obj->role = ATK_ROLE_PANEL; +} + +static void +gail_container_finalize (GObject *object) +{ + GailContainer *container = GAIL_CONTAINER (object); + + g_list_free (container->children); + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailcontainer.h b/modules/other/gail/gailcontainer.h new file mode 100644 index 000000000..6f0f61954 --- /dev/null +++ b/modules/other/gail/gailcontainer.h @@ -0,0 +1,72 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CONTAINER_H__ +#define __GAIL_CONTAINER_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CONTAINER (gail_container_get_type ()) +#define GAIL_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CONTAINER, GailContainer)) +#define GAIL_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CONTAINER, GailContainerClass)) +#define GAIL_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CONTAINER)) +#define GAIL_IS_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CONTAINER)) +#define GAIL_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CONTAINER, GailContainerClass)) + +typedef struct _GailContainer GailContainer; +typedef struct _GailContainerClass GailContainerClass; + +struct _GailContainer +{ + GailWidget parent; + + /* + * Cached list of children + */ + GList *children; +}; + +GType gail_container_get_type (void); + +struct _GailContainerClass +{ + GailWidgetClass parent_class; + + gint (*add_gtk) (GtkContainer *container, + GtkWidget *widget, + gpointer data); + gint (*remove_gtk) (GtkContainer *container, + GtkWidget *widget, + gpointer data); +}; + +AtkObject* gail_container_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_CONTAINER_H__ */ diff --git a/modules/other/gail/gailcontainercell.c b/modules/other/gail/gailcontainercell.c new file mode 100644 index 000000000..430ca75bb --- /dev/null +++ b/modules/other/gail/gailcontainercell.c @@ -0,0 +1,202 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailcontainercell.h" + +static void gail_container_cell_class_init (GailContainerCellClass *klass); +static void gail_container_cell_finalize (GObject *obj); + + +static void _gail_container_cell_recompute_child_indices + (GailContainerCell *container); + +static void gail_container_cell_refresh_child_index (GailCell *cell); + +static gint gail_container_cell_get_n_children (AtkObject *obj); + +static AtkObject* gail_container_cell_ref_child (AtkObject *obj, + gint child); + +static gpointer parent_class = NULL; + +GType +gail_container_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailContainerCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_container_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailContainerCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CELL, + "GailContainerCell", &tinfo, 0); + } + return type; +} + +static void +gail_container_cell_class_init (GailContainerCellClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS(klass); + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + g_object_class->finalize = gail_container_cell_finalize; + + class->get_n_children = gail_container_cell_get_n_children; + class->ref_child = gail_container_cell_ref_child; +} + + +GailContainerCell * +gail_container_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + GailContainerCell *container; + + object = g_object_new (GAIL_TYPE_CONTAINER_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + container = GAIL_CONTAINER_CELL(object); + container->children = NULL; + container->NChildren = 0; + return container; +} + +static void +gail_container_cell_finalize (GObject *obj) +{ + GailContainerCell *container = GAIL_CONTAINER_CELL (obj); + GList *list; + + list = container->children; + while (list) + { + g_object_unref (list->data); + list = list->next; + } + g_list_free (container->children); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + + +void +gail_container_cell_add_child (GailContainerCell *container, + GailCell *child) +{ + gint child_index; + + g_return_if_fail (GAIL_IS_CONTAINER_CELL(container)); + g_return_if_fail (GAIL_IS_CELL(child)); + + child_index = container->NChildren++; + container->children = g_list_append (container->children, (gpointer) child); + child->index = child_index; + atk_object_set_parent (ATK_OBJECT (child), ATK_OBJECT (container)); + child->refresh_index = gail_container_cell_refresh_child_index; +} + + +void +gail_container_cell_remove_child (GailContainerCell *container, + GailCell *child) +{ + g_return_if_fail (GAIL_IS_CONTAINER_CELL(container)); + g_return_if_fail (GAIL_IS_CELL(child)); + g_return_if_fail (container->NChildren > 0); + + g_list_remove (container->children, (gpointer) child); + _gail_container_cell_recompute_child_indices (container); + container->NChildren--; +} + + +static void +_gail_container_cell_recompute_child_indices (GailContainerCell *container) +{ + gint cur_index = 0; + GList *temp_list; + + g_return_if_fail (GAIL_IS_CONTAINER_CELL(container)); + + for (temp_list = container->children; temp_list; temp_list = temp_list->next) + { + GAIL_CELL(temp_list->data)->index = cur_index; + cur_index++; + } +} + + +static void +gail_container_cell_refresh_child_index (GailCell *cell) +{ + GailContainerCell *container; + g_return_if_fail (GAIL_IS_CELL(cell)); + container = GAIL_CONTAINER_CELL (atk_object_get_parent (ATK_OBJECT(cell))); + g_return_if_fail (GAIL_IS_CONTAINER_CELL (container)); + _gail_container_cell_recompute_child_indices (container); +} + + + +static gint +gail_container_cell_get_n_children (AtkObject *obj) +{ + GailContainerCell *cell; + g_return_val_if_fail (GAIL_IS_CONTAINER_CELL(obj), 0); + cell = GAIL_CONTAINER_CELL(obj); + return cell->NChildren; +} + + +static AtkObject * +gail_container_cell_ref_child (AtkObject *obj, + gint child) +{ + GailContainerCell *cell; + GList *list_node; + + g_return_val_if_fail (GAIL_IS_CONTAINER_CELL(obj), NULL); + cell = GAIL_CONTAINER_CELL(obj); + + list_node = g_list_nth (cell->children, child); + if (!list_node) + return NULL; + + return g_object_ref (ATK_OBJECT (list_node->data)); +} diff --git a/modules/other/gail/gailcontainercell.h b/modules/other/gail/gailcontainercell.h new file mode 100644 index 000000000..022b78bb6 --- /dev/null +++ b/modules/other/gail/gailcontainercell.h @@ -0,0 +1,71 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_CONTAINER_CELL_H__ +#define __GAIL_CONTAINER_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailcell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_CONTAINER_CELL (gail_container_cell_get_type ()) +#define GAIL_CONTAINER_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_CONTAINER_CELL, GailContainerCell)) +#define GAIL_CONTAINER_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_CONTAINER_CELL, GailContainerCellClass)) +#define GAIL_IS_CONTAINER_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_CONTAINER_CELL)) +#define GAIL_IS_CONTAINER_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_CONTAINER_CELL)) +#define GAIL_CONTAINER_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_CONTAINER_CELL, GailContainerCellClass)) + +typedef struct _GailContainerCell GailContainerCell; +typedef struct _GailContainerCellClass GailContainerCellClass; + +struct _GailContainerCell +{ + GailCell parent; + GList *children; + gint NChildren; +}; + +GType gail_container_cell_get_type (void); + +struct _GailContainerCellClass +{ + GailCellClass parent_class; +}; + +GailContainerCell * +gail_container_cell_new (void); + +void +gail_container_cell_add_child (GailContainerCell *container, + GailCell *child); + +void +gail_container_cell_remove_child (GailContainerCell *container, + GailCell *child); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_TEXT_CELL_H__ */ diff --git a/modules/other/gail/gailentry.c b/modules/other/gail/gailentry.c new file mode 100644 index 000000000..4910bc756 --- /dev/null +++ b/modules/other/gail/gailentry.c @@ -0,0 +1,1449 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailentry.h" +#include "gailcombo.h" +#include "gailcombobox.h" +#include <libgail-util/gailmisc.h> + +static void gail_entry_class_init (GailEntryClass *klass); +static void gail_entry_object_init (GailEntry *entry); +static void gail_entry_real_initialize (AtkObject *obj, + gpointer data); +static void text_setup (GailEntry *entry, + GtkEntry *gtk_entry); +static void gail_entry_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static void gail_entry_finalize (GObject *object); + +static gint gail_entry_get_index_in_parent (AtkObject *accessible); + +/* atkobject.h */ + +static AtkStateSet* gail_entry_ref_state_set (AtkObject *accessible); + +/* atktext.h */ + +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_entry_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_entry_get_character_at_offset + (AtkText *text, + gint offset); +static gchar* gail_entry_get_text_before_offset(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_entry_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_entry_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_entry_get_caret_offset (AtkText *text); +static gboolean gail_entry_set_caret_offset (AtkText *text, + gint offset); +static gint gail_entry_get_n_selections (AtkText *text); +static gchar* gail_entry_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset); +static gboolean gail_entry_add_selection (AtkText *text, + gint start_offset, + gint end_offset); +static gboolean gail_entry_remove_selection (AtkText *text, + gint selection_num); +static gboolean gail_entry_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset); +static gint gail_entry_get_character_count (AtkText *text); +static AtkAttributeSet * gail_entry_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet * gail_entry_get_default_attributes + (AtkText *text); +static void gail_entry_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_entry_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +/* atkeditabletext.h */ + +static void atk_editable_text_interface_init (AtkEditableTextIface *iface); +static void gail_entry_set_text_contents (AtkEditableText *text, + const gchar *string); +static void gail_entry_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position); +static void gail_entry_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_entry_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_entry_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_entry_paste_text (AtkEditableText *text, + gint position); +static void gail_entry_paste_received (GtkClipboard *clipboard, + const gchar *text, + gpointer data); + + +/* Callbacks */ + +static void gail_entry_notify_insert (GailEntry *entry); +static void gail_entry_notify_delete (GailEntry *entry); +static void _gail_entry_insert_text_cb (GtkEntry *entry, + gchar *arg1, + gint arg2, + gpointer arg3); +static void _gail_entry_delete_text_cb (GtkEntry *entry, + gint arg1, + gint arg2); +static void _gail_entry_changed_cb (GtkEntry *entry); +static gboolean check_for_selection_change (GailEntry *entry, + GtkEntry *gtk_entry); + +static void atk_action_interface_init (AtkActionIface *iface); + +static gboolean gail_entry_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_entry_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* gail_entry_get_description (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_entry_get_keybinding (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_entry_action_get_name (AtkAction *action, + gint i); +static gboolean gail_entry_set_description (AtkAction *action, + gint i, + const gchar *desc); + +static GailWidgetClass *parent_class = NULL; + +typedef struct _GailEntryPaste GailEntryPaste; + +struct _GailEntryPaste +{ + GtkEntry* entry; + gint position; +}; + +GType +gail_entry_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailEntryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_entry_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailEntry), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_entry_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_editable_text_info = + { + (GInterfaceInitFunc) atk_editable_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailEntry", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_EDITABLE_TEXT, + &atk_editable_text_info); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + } + return type; +} + +static void +gail_entry_class_init (GailEntryClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + widget_class = (GailWidgetClass*)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_entry_finalize; + + class->ref_state_set = gail_entry_ref_state_set; + class->get_index_in_parent = gail_entry_get_index_in_parent; + class->initialize = gail_entry_real_initialize; + + widget_class->notify_gtk = gail_entry_real_notify_gtk; +} + +static void +gail_entry_object_init (GailEntry *entry) +{ + entry->textutil = NULL; + entry->signal_name_insert = NULL; + entry->signal_name_delete = NULL; + entry->cursor_position = 0; + entry->selection_bound = 0; + entry->activate_description = NULL; + entry->activate_keybinding = NULL; +} + +static void +gail_entry_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkEntry *entry; + GailEntry *gail_entry; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gail_entry = GAIL_ENTRY (obj); + gail_entry->textutil = gail_text_util_new (); + + g_assert (GTK_IS_ENTRY (data)); + + entry = GTK_ENTRY (data); + text_setup (gail_entry, entry); + gail_entry->cursor_position = entry->current_pos; + gail_entry->selection_bound = entry->selection_bound; + + /* Set up signal callbacks */ + g_signal_connect (data, "insert-text", + G_CALLBACK (_gail_entry_insert_text_cb), NULL); + g_signal_connect (data, "delete-text", + G_CALLBACK (_gail_entry_delete_text_cb), NULL); + g_signal_connect (data, "changed", + G_CALLBACK (_gail_entry_changed_cb), NULL); + + if (entry->visible) + obj->role = ATK_ROLE_TEXT; + else + obj->role = ATK_ROLE_PASSWORD_TEXT; +} + +static void +gail_entry_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget; + AtkObject* atk_obj; + GtkEntry* gtk_entry; + GailEntry* entry; + + widget = GTK_WIDGET (obj); + atk_obj = gtk_widget_get_accessible (widget); + gtk_entry = GTK_ENTRY (widget); + entry = GAIL_ENTRY (atk_obj); + + if (strcmp (pspec->name, "cursor-position") == 0) + { + gail_entry_notify_insert (entry); + + if (check_for_selection_change (entry, gtk_entry)) + g_signal_emit_by_name (atk_obj, "text_selection_changed"); + /* + * The entry cursor position has moved so generate the signal. + */ + g_signal_emit_by_name (atk_obj, "text_caret_moved", + entry->cursor_position); + } + else if (strcmp (pspec->name, "selection-bound") == 0) + { + gail_entry_notify_insert (entry); + + if (check_for_selection_change (entry, gtk_entry)) + g_signal_emit_by_name (atk_obj, "text_selection_changed"); + } + else if (strcmp (pspec->name, "editable") == 0) + { + gboolean value; + + g_object_get (obj, "editable", &value, NULL); + atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, + value); + } + else if (strcmp (pspec->name, "visibility") == 0) + { + gboolean visibility; + AtkRole new_role; + + text_setup (entry, gtk_entry); + visibility = gtk_entry_get_visibility (gtk_entry); + new_role = visibility ? ATK_ROLE_TEXT : ATK_ROLE_PASSWORD_TEXT; + atk_object_set_role (atk_obj, new_role); + } + else if (strcmp (pspec->name, "invisible-char") == 0) + { + text_setup (entry, gtk_entry); + } + else + parent_class->notify_gtk (obj, pspec); +} + +static void +text_setup (GailEntry *entry, + GtkEntry *gtk_entry) +{ + if (gtk_entry_get_visibility (gtk_entry)) + { + gail_text_util_text_setup (entry->textutil, gtk_entry_get_text (gtk_entry)); + } + else + { + gunichar invisible_char; + GString *tmp_string = g_string_new (NULL); + gint ch_len; + gchar buf[7]; + gint i; + + invisible_char = gtk_entry_get_invisible_char (gtk_entry); + if (invisible_char == 0) + invisible_char = ' '; + + ch_len = g_unichar_to_utf8 (invisible_char, buf); + for (i = 0; i < gtk_entry->text_length; i++) + { + g_string_append_len (tmp_string, buf, ch_len); + } + + gail_text_util_text_setup (entry->textutil, tmp_string->str); + g_string_free (tmp_string, TRUE); + + } +} + +static void +gail_entry_finalize (GObject *object) +{ + GailEntry *entry = GAIL_ENTRY (object); + + g_object_unref (entry->textutil); + g_free (entry->activate_description); + g_free (entry->activate_keybinding); + if (entry->action_idle_handler) + { + g_source_remove (entry->action_idle_handler); + entry->action_idle_handler = 0; + } + if (entry->insert_idle_handler) + { + g_source_remove (entry->insert_idle_handler); + entry->insert_idle_handler = 0; + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +AtkObject* +gail_entry_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_ENTRY (widget), NULL); + + object = g_object_new (GAIL_TYPE_ENTRY, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static gint +gail_entry_get_index_in_parent (AtkObject *accessible) +{ + /* + * If the parent widget is a combo box then the index is 1 + * otherwise do the normal thing. + */ + if (accessible->accessible_parent) + if (GAIL_IS_COMBO (accessible->accessible_parent) || + GAIL_IS_COMBO_BOX (accessible->accessible_parent)) + return 1; + + return ATK_OBJECT_CLASS (parent_class)->get_index_in_parent (accessible); +} + +/* atkobject.h */ + +static AtkStateSet* +gail_entry_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkEntry *entry; + gboolean value; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + entry = GTK_ENTRY (widget); + + g_object_get (G_OBJECT (entry), "editable", &value, NULL); + if (value) + atk_state_set_add_state (state_set, ATK_STATE_EDITABLE); + atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE); + + return state_set; +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_text = gail_entry_get_text; + iface->get_character_at_offset = gail_entry_get_character_at_offset; + iface->get_text_before_offset = gail_entry_get_text_before_offset; + iface->get_text_at_offset = gail_entry_get_text_at_offset; + iface->get_text_after_offset = gail_entry_get_text_after_offset; + iface->get_caret_offset = gail_entry_get_caret_offset; + iface->set_caret_offset = gail_entry_set_caret_offset; + iface->get_character_count = gail_entry_get_character_count; + iface->get_n_selections = gail_entry_get_n_selections; + iface->get_selection = gail_entry_get_selection; + iface->add_selection = gail_entry_add_selection; + iface->remove_selection = gail_entry_remove_selection; + iface->set_selection = gail_entry_set_selection; + iface->get_run_attributes = gail_entry_get_run_attributes; + iface->get_default_attributes = gail_entry_get_default_attributes; + iface->get_character_extents = gail_entry_get_character_extents; + iface->get_offset_at_point = gail_entry_get_offset_at_point; +} + +static gchar* +gail_entry_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + return gail_text_util_get_substring (GAIL_ENTRY (text)->textutil, start_pos, end_pos); +} + +static gchar* +gail_entry_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkEntry *entry; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get Entry */ + entry = GTK_ENTRY (widget); + + return gail_text_util_get_text (GAIL_ENTRY (text)->textutil, + gtk_entry_get_layout (entry), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_entry_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkEntry *entry; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get Entry */ + entry = GTK_ENTRY (widget); + + return gail_text_util_get_text (GAIL_ENTRY (text)->textutil, + gtk_entry_get_layout (entry), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_entry_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkEntry *entry; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get Entry */ + entry = GTK_ENTRY (widget); + + return gail_text_util_get_text (GAIL_ENTRY (text)->textutil, + gtk_entry_get_layout (entry), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_entry_get_character_count (AtkText *text) +{ + GtkEntry *entry; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + entry = GTK_ENTRY (widget); + return g_utf8_strlen (gtk_entry_get_text (entry), -1); +} + +static gint +gail_entry_get_caret_offset (AtkText *text) +{ + GtkEntry *entry; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + entry = GTK_ENTRY (widget); + + return gtk_editable_get_position (GTK_EDITABLE (entry)); +} + +static gboolean +gail_entry_set_caret_offset (AtkText *text, gint offset) +{ + GtkEntry *entry; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + entry = GTK_ENTRY (widget); + + gtk_editable_set_position (GTK_EDITABLE (entry), offset); + return TRUE; +} + +static AtkAttributeSet* +gail_entry_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkEntry *entry; + AtkAttributeSet *at_set = NULL; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + entry = GTK_ENTRY (widget); + + dir = gtk_widget_get_direction (widget); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_entry_get_layout (entry), + entry->text, + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_entry_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkEntry *entry; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + entry = GTK_ENTRY (widget); + + at_set = gail_misc_get_default_attributes (at_set, + gtk_entry_get_layout (entry), + widget); + return at_set; +} + +static void +gail_entry_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkEntry *entry; + PangoRectangle char_rect; + gint index, cursor_index, x_layout, y_layout; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + + gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout); + index = g_utf8_offset_to_pointer (entry->text, offset) - entry->text; + cursor_index = g_utf8_offset_to_pointer (entry->text, entry->current_pos) - + entry->text; + if (index > cursor_index) + index += entry->preedit_length; + pango_layout_index_to_pos (gtk_entry_get_layout(entry), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_entry_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkEntry *entry; + gint index, cursor_index, x_layout, y_layout; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + entry = GTK_ENTRY (widget); + + gtk_entry_get_layout_offsets (entry, &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (widget, + gtk_entry_get_layout(entry), x_layout, y_layout, x, y, coords); + if (index == -1) + { + if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW) + return g_utf8_strlen (entry->text, -1); + + return index; + } + else + { + cursor_index = g_utf8_offset_to_pointer (entry->text, + entry->current_pos) - + entry->text; + if (index >= cursor_index && entry->preedit_length) + { + if (index >= cursor_index + entry->preedit_length) + index -= entry->preedit_length; + else + index = cursor_index; + } + return g_utf8_pointer_to_offset (entry->text, entry->text + index); + } +} + +static gint +gail_entry_get_n_selections (AtkText *text) +{ + GtkEntry *entry; + GtkWidget *widget; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + entry = GTK_ENTRY (widget); + + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, + &select_end); + + if (select_start != select_end) + return 1; + else + return 0; +} + +static gchar* +gail_entry_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Only let the user get the selection if one is set, and if the + * selection_num is 0. + */ + if (selection_num != 0) + return NULL; + + entry = GTK_ENTRY (widget); + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), start_pos, end_pos); + + if (*start_pos != *end_pos) + return gtk_editable_get_chars (GTK_EDITABLE (entry), *start_pos, *end_pos); + else + return NULL; +} + +static gboolean +gail_entry_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + entry = GTK_ENTRY (widget); + + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, + &select_end); + + /* If there is already a selection, then don't allow another to be added, + * since GtkEntry only supports one selected region. + */ + if (select_start == select_end) + { + gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_entry_remove_selection (AtkText *text, + gint selection_num) +{ + GtkEntry *entry; + GtkWidget *widget; + gint select_start, select_end, caret_pos; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (selection_num != 0) + return FALSE; + + entry = GTK_ENTRY (widget); + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, + &select_end); + + if (select_start != select_end) + { + /* Setting the start & end of the selected region to the caret position + * turns off the selection. + */ + caret_pos = gtk_editable_get_position (GTK_EDITABLE (entry)); + gtk_editable_select_region (GTK_EDITABLE (entry), caret_pos, caret_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_entry_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + /* Only let the user move the selection if one is set, and if the + * selection_num is 0 + */ + if (selection_num != 0) + return FALSE; + + entry = GTK_ENTRY (widget); + + gtk_editable_get_selection_bounds (GTK_EDITABLE (entry), &select_start, + &select_end); + + if (select_start != select_end) + { + gtk_editable_select_region (GTK_EDITABLE (entry), start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static void +atk_editable_text_interface_init (AtkEditableTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->set_text_contents = gail_entry_set_text_contents; + iface->insert_text = gail_entry_insert_text; + iface->copy_text = gail_entry_copy_text; + iface->cut_text = gail_entry_cut_text; + iface->delete_text = gail_entry_delete_text; + iface->paste_text = gail_entry_paste_text; + iface->set_run_attributes = NULL; +} + +static void +gail_entry_set_text_contents (AtkEditableText *text, + const gchar *string) +{ + GtkEntry *entry; + GtkWidget *widget; + GtkEditable *editable; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (entry); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_entry_set_text (entry, string); +} + +static void +gail_entry_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position) +{ + GtkEntry *entry; + GtkWidget *widget; + GtkEditable *editable; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (entry); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_editable_insert_text (editable, string, length, position); +} + +static void +gail_entry_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + GtkEditable *editable; + gchar *str; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (entry); + str = gtk_editable_get_chars (editable, start_pos, end_pos); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1); +} + +static void +gail_entry_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + GtkEditable *editable; + gchar *str; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (entry); + if (!gtk_editable_get_editable (editable)) + return; + str = gtk_editable_get_chars (editable, start_pos, end_pos); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1); + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +static void +gail_entry_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkEntry *entry; + GtkWidget *widget; + GtkEditable *editable; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + entry = GTK_ENTRY (widget); + editable = GTK_EDITABLE (entry); + if (!gtk_editable_get_editable (editable)) + return; + + gtk_editable_delete_text (editable, start_pos, end_pos); +} + +static void +gail_entry_paste_text (AtkEditableText *text, + gint position) +{ + GtkWidget *widget; + GtkEditable *editable; + GailEntryPaste paste_struct; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + editable = GTK_EDITABLE (widget); + if (!gtk_editable_get_editable (editable)) + return; + paste_struct.entry = GTK_ENTRY (widget); + paste_struct.position = position; + + g_object_ref (paste_struct.entry); + gtk_clipboard_request_text (gtk_clipboard_get(GDK_NONE), + gail_entry_paste_received, &paste_struct); +} + +static void +gail_entry_paste_received (GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + GailEntryPaste* paste_struct = (GailEntryPaste *)data; + + if (text) + gtk_editable_insert_text (GTK_EDITABLE (paste_struct->entry), text, -1, + &(paste_struct->position)); + + g_object_unref (paste_struct->entry); +} + +/* Callbacks */ + +static gboolean +idle_notify_insert (gpointer data) +{ + GailEntry *entry; + + GDK_THREADS_ENTER (); + + entry = GAIL_ENTRY (data); + entry->insert_idle_handler = 0; + gail_entry_notify_insert (entry); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +gail_entry_notify_insert (GailEntry *entry) +{ + if (entry->signal_name_insert) + { + g_signal_emit_by_name (entry, + entry->signal_name_insert, + entry->position_insert, + entry->length_insert); + entry->signal_name_insert = NULL; + } +} + +/* Note arg1 returns the character at the start of the insert. + * arg2 returns the number of characters inserted. + */ +static void +_gail_entry_insert_text_cb (GtkEntry *entry, + gchar *arg1, + gint arg2, + gpointer arg3) +{ + AtkObject *accessible; + GailEntry *gail_entry; + gint *position = (gint *) arg3; + + accessible = gtk_widget_get_accessible (GTK_WIDGET (entry)); + gail_entry = GAIL_ENTRY (accessible); + if (!gail_entry->signal_name_insert) + { + gail_entry->signal_name_insert = "text_changed::insert"; + gail_entry->position_insert = *position; + gail_entry->length_insert = g_utf8_strlen(arg1, arg2); + } + /* + * The signal will be emitted when the cursor position is updated. + * or in an idle handler if it not updated. + */ + if (gail_entry->insert_idle_handler == 0) + gail_entry->insert_idle_handler = g_idle_add (idle_notify_insert, gail_entry); +} + +static gunichar +gail_entry_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GailEntry *entry; + gchar *string; + gchar *index; + gunichar unichar; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + entry = GAIL_ENTRY (text); + string = gail_text_util_get_substring (entry->textutil, 0, -1); + if (offset >= g_utf8_strlen (string, -1)) + { + unichar = '\0'; + } + else + { + index = g_utf8_offset_to_pointer (string, offset); + + unichar = g_utf8_get_char(index); + } + + g_free(string); + return unichar; +} + +static void +gail_entry_notify_delete (GailEntry *entry) +{ + if (entry->signal_name_delete) + { + g_signal_emit_by_name (entry, + entry->signal_name_delete, + entry->position_delete, + entry->length_delete); + entry->signal_name_delete = NULL; + } +} + +/* Note arg1 returns the start of the delete range, arg2 returns the + * end of the delete range if multiple characters are deleted. + */ +static void +_gail_entry_delete_text_cb (GtkEntry *entry, + gint arg1, + gint arg2) +{ + AtkObject *accessible; + GailEntry *gail_entry; + + /* + * Zero length text deleted so ignore + */ + if (arg2 - arg1 == 0) + return; + + accessible = gtk_widget_get_accessible (GTK_WIDGET (entry)); + gail_entry = GAIL_ENTRY (accessible); + if (!gail_entry->signal_name_delete) + { + gail_entry->signal_name_delete = "text_changed::delete"; + gail_entry->position_delete = arg1; + gail_entry->length_delete = arg2 - arg1; + } + gail_entry_notify_delete (gail_entry); +} + +static void +_gail_entry_changed_cb (GtkEntry *entry) +{ + AtkObject *accessible; + GailEntry *gail_entry; + + accessible = gtk_widget_get_accessible (GTK_WIDGET (entry)); + + gail_entry = GAIL_ENTRY (accessible); + + text_setup (gail_entry, entry); +} + +static gboolean +check_for_selection_change (GailEntry *entry, + GtkEntry *gtk_entry) +{ + gboolean ret_val = FALSE; + + if (gtk_entry->current_pos != gtk_entry->selection_bound) + { + if (gtk_entry->current_pos != entry->cursor_position || + gtk_entry->selection_bound != entry->selection_bound) + /* + * This check is here as this function can be called + * for notification of selection_bound and current_pos. + * The values of current_pos and selection_bound may be the same + * for both notifications and we only want to generate one + * text_selection_changed signal. + */ + ret_val = TRUE; + } + else + { + /* We had a selection */ + ret_val = (entry->cursor_position != entry->selection_bound); + } + entry->cursor_position = gtk_entry->current_pos; + entry->selection_bound = gtk_entry->selection_bound; + + return ret_val; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_entry_do_action; + iface->get_n_actions = gail_entry_get_n_actions; + iface->get_description = gail_entry_get_description; + iface->get_keybinding = gail_entry_get_keybinding; + iface->get_name = gail_entry_action_get_name; + iface->set_description = gail_entry_set_description; +} + +static gboolean +gail_entry_do_action (AtkAction *action, + gint i) +{ + GailEntry *entry; + GtkWidget *widget; + gboolean return_value = TRUE; + + entry = GAIL_ENTRY (action); + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + switch (i) + { + case 0: + if (entry->action_idle_handler) + return_value = FALSE; + else + entry->action_idle_handler = g_idle_add (idle_do_action, entry); + break; + default: + return_value = FALSE; + break; + } + return return_value; +} + +static gboolean +idle_do_action (gpointer data) +{ + GailEntry *entry; + GtkWidget *widget; + + GDK_THREADS_ENTER (); + + entry = GAIL_ENTRY (data); + entry->action_idle_handler = 0; + widget = GTK_ACCESSIBLE (entry)->widget; + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + gtk_widget_activate (widget); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_entry_get_n_actions (AtkAction *action) +{ + return 1; +} + +static G_CONST_RETURN gchar* +gail_entry_get_description (AtkAction *action, + gint i) +{ + GailEntry *entry; + G_CONST_RETURN gchar *return_value; + + entry = GAIL_ENTRY (action); + switch (i) + { + case 0: + return_value = entry->activate_description; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_entry_get_keybinding (AtkAction *action, + gint i) +{ + GailEntry *entry; + gchar *return_value = NULL; + + entry = GAIL_ENTRY (action); + switch (i) + { + case 0: + { + /* + * We look for a mnemonic on the label + */ + GtkWidget *widget; + GtkWidget *label; + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + guint key_val; + + entry = GAIL_ENTRY (action); + widget = GTK_ACCESSIBLE (entry)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + /* Find labelled-by relation */ + + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + if (!set) + return NULL; + label = NULL; + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + + target_object = g_ptr_array_index (target, 0); + if (GTK_IS_ACCESSIBLE (target_object)) + { + label = GTK_ACCESSIBLE (target_object)->widget; + } + } + + g_object_unref (set); + + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + g_free (entry->activate_keybinding); + entry->activate_keybinding = return_value; + break; + } + default: + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_entry_action_get_name (AtkAction *action, + gint i) +{ + G_CONST_RETURN gchar *return_value; + + switch (i) + { + case 0: + return_value = "activate"; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static gboolean +gail_entry_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + GailEntry *entry; + gchar **value; + + entry = GAIL_ENTRY (action); + switch (i) + { + case 0: + value = &entry->activate_description; + break; + default: + value = NULL; + break; + } + + if (value) + { + g_free (*value); + *value = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} diff --git a/modules/other/gail/gailentry.h b/modules/other/gail/gailentry.h new file mode 100644 index 000000000..1fa332f1c --- /dev/null +++ b/modules/other/gail/gailentry.h @@ -0,0 +1,78 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_ENTRY_H__ +#define __GAIL_ENTRY_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_ENTRY (gail_entry_get_type ()) +#define GAIL_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_ENTRY, GailEntry)) +#define GAIL_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_ENTRY, GailEntryClass)) +#define GAIL_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_ENTRY)) +#define GAIL_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_ENTRY)) +#define GAIL_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_ENTRY, GailEntryClass)) + +typedef struct _GailEntry GailEntry; +typedef struct _GailEntryClass GailEntryClass; + +struct _GailEntry +{ + GailWidget parent; + + GailTextUtil *textutil; + /* + * These fields store information about text changed + */ + gchar *signal_name_insert; + gchar *signal_name_delete; + gint position_insert; + gint position_delete; + gint length_insert; + gint length_delete; + gint cursor_position; + gint selection_bound; + + gchar *activate_description; + gchar *activate_keybinding; + guint action_idle_handler; + guint insert_idle_handler; +}; + +GType gail_entry_get_type (void); + +struct _GailEntryClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_entry_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_ENTRY_H__ */ diff --git a/modules/other/gail/gailexpander.c b/modules/other/gail/gailexpander.c new file mode 100644 index 000000000..8d43e2fdc --- /dev/null +++ b/modules/other/gail/gailexpander.c @@ -0,0 +1,950 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailexpander.h" +#include <libgail-util/gailmisc.h> + +static void gail_expander_class_init (GailExpanderClass *klass); +static void gail_expander_object_init (GailExpander *expander); + +static G_CONST_RETURN gchar* gail_expander_get_name (AtkObject *obj); +static gint gail_expander_get_n_children (AtkObject *obj) +; +static AtkObject* gail_expander_ref_child (AtkObject *obj, + gint i); + +static AtkStateSet* gail_expander_ref_state_set (AtkObject *obj); +static void gail_expander_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static void gail_expander_map_gtk (GtkWidget *widget, + gpointer data); + +static void gail_expander_real_initialize (AtkObject *obj, + gpointer data); +static void gail_expander_finalize (GObject *object); +static void gail_expander_init_textutil (GailExpander *expander, + GtkExpander *widget); + +static void atk_action_interface_init (AtkActionIface *iface); +static gboolean gail_expander_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_expander_get_n_actions(AtkAction *action); +static G_CONST_RETURN gchar* gail_expander_get_description + (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_expander_get_keybinding + (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_expander_action_get_name + (AtkAction *action, + gint i); +static gboolean gail_expander_set_description + (AtkAction *action, + gint i, + const gchar *desc); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_expander_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_expander_get_character_at_offset + (AtkText *text, + gint offset); +static gchar* gail_expander_get_text_before_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_expander_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_expander_get_text_after_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_expander_get_character_count(AtkText *text); +static void gail_expander_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_expander_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_expander_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_expander_get_default_attributes + (AtkText *text); + +static GailContainer* parent_class = NULL; + +GType +gail_expander_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailExpanderClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_expander_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailExpander), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_expander_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailExpander", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + + } + + return type; +} + +static void +gail_expander_class_init (GailExpanderClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + widget_class = (GailWidgetClass*)klass; + widget_class->notify_gtk = gail_expander_real_notify_gtk; + + gobject_class->finalize = gail_expander_finalize; + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_expander_get_name; + class->get_n_children = gail_expander_get_n_children; + class->ref_child = gail_expander_ref_child; + class->ref_state_set = gail_expander_ref_state_set; + + class->initialize = gail_expander_real_initialize; +} + +static void +gail_expander_object_init (GailExpander *expander) +{ + expander->activate_description = NULL; + expander->activate_keybinding = NULL; + expander->action_idle_handler = 0; + expander->textutil = NULL; +} + +AtkObject* +gail_expander_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL); + + object = g_object_new (GAIL_TYPE_EXPANDER, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static G_CONST_RETURN gchar* +gail_expander_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar *name; + g_return_val_if_fail (GAIL_IS_EXPANDER (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name != NULL) + return name; + else + { + /* + * Get the text on the label + */ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL); + + return gtk_expander_get_label (GTK_EXPANDER (widget)); + } +} + +static gint +gail_expander_get_n_children (AtkObject* obj) +{ + GtkWidget *widget; + GList *children; + gint count = 0; + + g_return_val_if_fail (GAIL_IS_CONTAINER (obj), count); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return 0; + + children = gtk_container_get_children (GTK_CONTAINER(widget)); + count = g_list_length (children); + g_list_free (children); + + /* See if there is a label - if there is, reduce our count by 1 + * since we don't want the label included with the children. + */ + if (gtk_expander_get_label_widget (widget)) + count -= 1; + + return count; +} + +static AtkObject* +gail_expander_ref_child (AtkObject *obj, + gint i) +{ + GList *children, *tmp_list; + AtkObject *accessible; + GtkWidget *widget; + GtkWidget *label; + gint index; + gint count; + + g_return_val_if_fail (GAIL_IS_CONTAINER (obj), NULL); + g_return_val_if_fail ((i >= 0), NULL); + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + + /* See if there is a label - if there is, we need to skip it + * since we don't want the label included with the children. + */ + label = gtk_expander_get_label_widget (widget); + if (label) { + count = g_list_length (children); + for (index = 0; index <= i; index++) { + tmp_list = g_list_nth (children, index); + if (label == GTK_WIDGET (tmp_list->data)) { + i += 1; + break; + } + } + } + + tmp_list = g_list_nth (children, i); + if (!tmp_list) + { + g_list_free (children); + return NULL; + } + accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data)); + + g_list_free (children); + g_object_ref (accessible); + return accessible; +} + +static void +gail_expander_real_initialize (AtkObject *obj, + gpointer data) +{ + GailExpander *gail_expander = GAIL_EXPANDER (obj); + GtkWidget *expander; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + expander = GTK_WIDGET (data); + if (GTK_WIDGET_MAPPED (expander)) + gail_expander_init_textutil (gail_expander, GTK_EXPANDER (expander)); + else + g_signal_connect (expander, + "map", + G_CALLBACK (gail_expander_map_gtk), + gail_expander); + + obj->role = ATK_ROLE_TOGGLE_BUTTON; +} + +static void +gail_expander_map_gtk (GtkWidget *widget, + gpointer data) +{ + GailExpander *expander; + + expander = GAIL_EXPANDER (data); + gail_expander_init_textutil (expander, GTK_EXPANDER (widget)); +} + +static void +gail_expander_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + AtkObject* atk_obj; + GtkExpander *expander; + GailExpander *gail_expander; + + expander = GTK_EXPANDER (obj); + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (expander)); +; + if (strcmp (pspec->name, "label") == 0) + { + const gchar* label_text; + + + label_text = gtk_expander_get_label (expander); + + gail_expander = GAIL_EXPANDER (atk_obj); + if (gail_expander->textutil) + gail_text_util_text_setup (gail_expander->textutil, label_text); + + if (atk_obj->name == NULL) + { + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (atk_obj), "accessible-name"); + } + /* + * The label is the only property which can be changed + */ + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } + else if (strcmp (pspec->name, "expanded") == 0) + { + atk_object_notify_state_change (atk_obj, ATK_STATE_CHECKED, + gtk_expander_get_expanded (expander)); + atk_object_notify_state_change (atk_obj, ATK_STATE_EXPANDED, + gtk_expander_get_expanded (expander)); + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} + +static void +gail_expander_init_textutil (GailExpander *expander, + GtkExpander *widget) +{ + const gchar *label_text; + + expander->textutil = gail_text_util_new (); + label_text = gtk_expander_get_label (widget); + gail_text_util_text_setup (expander->textutil, label_text); +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_expander_do_action; + iface->get_n_actions = gail_expander_get_n_actions; + iface->get_description = gail_expander_get_description; + iface->get_keybinding = gail_expander_get_keybinding; + iface->get_name = gail_expander_action_get_name; + iface->set_description = gail_expander_set_description; +} + +static gboolean +gail_expander_do_action (AtkAction *action, + gint i) +{ + GtkWidget *widget; + GailExpander *expander; + gboolean return_value = TRUE; + + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + expander = GAIL_EXPANDER (action); + switch (i) + { + case 0: + if (expander->action_idle_handler) + return_value = FALSE; + else + expander->action_idle_handler = g_idle_add (idle_do_action, expander); + break; + default: + return_value = FALSE; + break; + } + return return_value; +} + +static gboolean +idle_do_action (gpointer data) +{ + GtkWidget *widget; + GailExpander *gail_expander; + + GDK_THREADS_ENTER (); + + gail_expander = GAIL_EXPANDER (data); + gail_expander->action_idle_handler = 0; + + widget = GTK_ACCESSIBLE (gail_expander)->widget; + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + gtk_widget_activate (widget); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_expander_get_n_actions (AtkAction *action) +{ + return 1; +} + +static G_CONST_RETURN gchar* +gail_expander_get_description (AtkAction *action, + gint i) +{ + GailExpander *expander; + G_CONST_RETURN gchar *return_value; + + expander = GAIL_EXPANDER (action); + + switch (i) + { + case 0: + return_value = expander->activate_description; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_expander_get_keybinding (AtkAction *action, + gint i) +{ + GailExpander *expander; + gchar *return_value = NULL; + + switch (i) + { + case 0: + { + /* + * We look for a mnemonic on the label + */ + GtkWidget *widget; + GtkWidget *label; + + expander = GAIL_EXPANDER (action); + widget = GTK_ACCESSIBLE (expander)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_EXPANDER (widget), NULL); + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (GTK_IS_LABEL (label)) + { + guint key_val; + + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + g_free (expander->activate_keybinding); + expander->activate_keybinding = return_value; + } + break; + } + default: + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_expander_action_get_name (AtkAction *action, + gint i) +{ + G_CONST_RETURN gchar *return_value; + + switch (i) + { + case 0: + return_value = "activate"; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static gboolean +gail_expander_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + GailExpander *expander; + gchar **value; + + expander = GAIL_EXPANDER (action); + + switch (i) + { + case 0: + value = &expander->activate_description; + break; + default: + value = NULL; + break; + } + if (value) + { + g_free (*value); + *value = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} + +static AtkStateSet* +gail_expander_ref_state_set (AtkObject *obj) +{ + AtkStateSet *state_set; + GtkWidget *widget; + GtkExpander *expander; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget == NULL) + return state_set; + + expander = GTK_EXPANDER (widget); + + atk_state_set_add_state (state_set, ATK_STATE_EXPANDABLE); + + if (gtk_expander_get_expanded (expander)) { + atk_state_set_add_state (state_set, ATK_STATE_CHECKED); + atk_state_set_add_state (state_set, ATK_STATE_EXPANDED); + } + + return state_set; +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_expander_get_text; + iface->get_character_at_offset = gail_expander_get_character_at_offset; + iface->get_text_before_offset = gail_expander_get_text_before_offset; + iface->get_text_at_offset = gail_expander_get_text_at_offset; + iface->get_text_after_offset = gail_expander_get_text_after_offset; + iface->get_character_count = gail_expander_get_character_count; + iface->get_character_extents = gail_expander_get_character_extents; + iface->get_offset_at_point = gail_expander_get_offset_at_point; + iface->get_run_attributes = gail_expander_get_run_attributes; + iface->get_default_attributes = gail_expander_get_default_attributes; +} + +static gchar* +gail_expander_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GailExpander *expander; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + expander = GAIL_EXPANDER (text); + if (!expander->textutil) + gail_expander_init_textutil (expander, GTK_EXPANDER (widget)); + + label_text = gtk_expander_get_label (GTK_EXPANDER (widget)); + + if (label_text == NULL) + return NULL; + else + return gail_text_util_get_substring (expander->textutil, + start_pos, end_pos); +} + +static gchar* +gail_expander_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailExpander *expander; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + expander = GAIL_EXPANDER (text); + if (!expander->textutil) + gail_expander_init_textutil (expander, GTK_EXPANDER (widget)); + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (!GTK_IS_LABEL(label)) + return NULL; + return gail_text_util_get_text (expander->textutil, + gtk_label_get_layout (GTK_LABEL (label)), + GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_expander_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailExpander *expander; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + expander = GAIL_EXPANDER (text); + if (!expander->textutil) + gail_expander_init_textutil (expander, GTK_EXPANDER (widget)); + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (!GTK_IS_LABEL(label)) + return NULL; + return gail_text_util_get_text (expander->textutil, + gtk_label_get_layout (GTK_LABEL (label)), + GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_expander_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailExpander *expander; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + expander = GAIL_EXPANDER (text); + if (!expander->textutil) + gail_expander_init_textutil (expander, GTK_EXPANDER (widget)); + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (!GTK_IS_LABEL(label)) + return NULL; + return gail_text_util_get_text (expander->textutil, + gtk_label_get_layout (GTK_LABEL (label)), + GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_expander_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (!GTK_IS_LABEL(label)) + return 0; + + return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1); +} + +static void +gail_expander_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + if (!GTK_IS_LABEL(label)) + return; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + label_text = gtk_label_get_text (GTK_LABEL (label)); + index = g_utf8_offset_to_pointer (label_text, offset) - label_text; + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (label, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_expander_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + + if (!GTK_IS_LABEL(label)) + return -1; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (label, + gtk_label_get_layout (GTK_LABEL (label)), + x_layout, y_layout, x, y, coords); + label_text = gtk_label_get_text (GTK_LABEL (label)); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label_text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label_text, label_text + index); +} + +static AtkAttributeSet* +gail_expander_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + + if (!GTK_IS_LABEL(label)) + return NULL; + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (GTK_LABEL (label)); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (label); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + (gchar *) gtk_label_get_text (GTK_LABEL (label)), + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_expander_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + + if (!GTK_IS_LABEL(label)) + return NULL; + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + widget); + return at_set; +} + +static gunichar +gail_expander_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkWidget *label; + const gchar *string; + gchar *index; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + label = gtk_expander_get_label_widget (GTK_EXPANDER (widget)); + + if (!GTK_IS_LABEL(label)) + return '\0'; + string = gtk_label_get_text (GTK_LABEL (label)); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + +static void +gail_expander_finalize (GObject *object) +{ + GailExpander *expander = GAIL_EXPANDER (object); + + g_free (expander->activate_description); + g_free (expander->activate_keybinding); + if (expander->action_idle_handler) + { + g_source_remove (expander->action_idle_handler); + expander->action_idle_handler = 0; + } + if (expander->textutil) + g_object_unref (expander->textutil); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailexpander.h b/modules/other/gail/gailexpander.h new file mode 100644 index 000000000..a1e8c29d0 --- /dev/null +++ b/modules/other/gail/gailexpander.h @@ -0,0 +1,66 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_EXPANDER_H__ +#define __GAIL_EXPANDER_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_EXPANDER (gail_expander_get_type ()) +#define GAIL_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_EXPANDER, GailExpander)) +#define GAIL_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_EXPANDER, GailExpanderClass)) +#define GAIL_IS_EXPANDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_EXPANDER)) +#define GAIL_IS_EXPANDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_EXPANDER)) +#define GAIL_EXPANDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_EXPANDER, GailExpanderClass)) + +typedef struct _GailExpander GailExpander; +typedef struct _GailExpanderClass GailExpanderClass; + +struct _GailExpander +{ + GailContainer parent; + + gchar *activate_description; + gchar *activate_keybinding; + guint action_idle_handler; + + GailTextUtil *textutil; +}; + +GType gail_expander_get_type (void); + +struct _GailExpanderClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_expander_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_EXPANDER_H__ */ diff --git a/modules/other/gail/gailfactory.h b/modules/other/gail/gailfactory.h new file mode 100644 index 000000000..0ea4dbac4 --- /dev/null +++ b/modules/other/gail/gailfactory.h @@ -0,0 +1,85 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GAIL_FACTORY_H__ +#define _GAIL_FACTORY_H__ + +#include <glib-object.h> +#include <atk/atkobject.h> + +#define GAIL_ACCESSIBLE_FACTORY(type, type_as_function, opt_create_accessible) \ + \ +static GType \ +type_as_function ## _factory_get_accessible_type (void) \ +{ \ + return type; \ +} \ + \ +static AtkObject* \ +type_as_function ## _factory_create_accessible (GObject *obj) \ +{ \ + GtkWidget *widget; \ + AtkObject *accessible; \ + \ + g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL); \ + \ + widget = GTK_WIDGET (obj); \ + \ + accessible = opt_create_accessible (widget); \ + \ + return accessible; \ +} \ + \ +static void \ +type_as_function ## _factory_class_init (AtkObjectFactoryClass *klass) \ +{ \ + klass->create_accessible = type_as_function ## _factory_create_accessible; \ + klass->get_accessible_type = type_as_function ## _factory_get_accessible_type;\ +} \ + \ +static GType \ +type_as_function ## _factory_get_type (void) \ +{ \ + static GType t = 0; \ + \ + if (!t) \ + { \ + char *name; \ + static const GTypeInfo tinfo = \ + { \ + sizeof (AtkObjectFactoryClass), \ + NULL, NULL, (GClassInitFunc) type_as_function ## _factory_class_init, \ + NULL, NULL, sizeof (AtkObjectFactory), 0, NULL, NULL \ + }; \ + \ + name = g_strconcat (g_type_name (type), "Factory", NULL); \ + t = g_type_register_static ( \ + ATK_TYPE_OBJECT_FACTORY, name, &tinfo, 0); \ + g_free (name); \ + } \ + \ + return t; \ +} + +#define GAIL_WIDGET_SET_FACTORY(widget_type, type_as_function) \ + atk_registry_set_factory_type (atk_get_default_registry (), \ + widget_type, \ + type_as_function ## _factory_get_type ()) + +#endif /* _GAIL_FACTORY_H__ */ diff --git a/modules/other/gail/gailframe.c b/modules/other/gail/gailframe.c new file mode 100644 index 000000000..4d85f0c7d --- /dev/null +++ b/modules/other/gail/gailframe.c @@ -0,0 +1,113 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailframe.h" + +static void gail_frame_class_init (GailFrameClass *klass); +static G_CONST_RETURN gchar* gail_frame_get_name (AtkObject *obj); + +static gpointer parent_class = NULL; + +GType +gail_frame_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailFrameClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_frame_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailFrame), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailFrame", &tinfo, 0); + } + + return type; +} + +static void +gail_frame_class_init (GailFrameClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_name = gail_frame_get_name; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_frame_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_FRAME (widget), NULL); + + object = g_object_new (GAIL_TYPE_FRAME, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_PANEL; + + return accessible; +} + +static G_CONST_RETURN gchar* +gail_frame_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar *name; + g_return_val_if_fail (GAIL_IS_FRAME (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name != NULL) + { + return name; + } + else + { + /* + * Get the text on the label + */ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + { + /* + * State is defunct + */ + return NULL; + } + return gtk_frame_get_label (GTK_FRAME (widget)); + } +} diff --git a/modules/other/gail/gailframe.h b/modules/other/gail/gailframe.h new file mode 100644 index 000000000..142c8deb7 --- /dev/null +++ b/modules/other/gail/gailframe.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_FRAME_H__ +#define __GAIL_FRAME_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_FRAME (gail_frame_get_type ()) +#define GAIL_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_FRAME, GailFrame)) +#define GAIL_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_FRAME, GailFrameClass)) +#define GAIL_IS_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_FRAME)) +#define GAIL_IS_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_FRAME)) +#define GAIL_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_FRAME, GailFrameClass)) + +typedef struct _GailFrame GailFrame; +typedef struct _GailFrameClass GailFrameClass; + +struct _GailFrame +{ + GailContainer parent; +}; + +GType gail_frame_get_type (void); + +struct _GailFrameClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_frame_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_FRAME_H__ */ diff --git a/modules/other/gail/gailhtmlbox.c b/modules/other/gail/gailhtmlbox.c new file mode 100644 index 000000000..1d8def6ad --- /dev/null +++ b/modules/other/gail/gailhtmlbox.c @@ -0,0 +1,289 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailhtmlbox.h" +#include "gailhtmlview.h" +#include <libgtkhtml/layout/htmlbox.h> + +static void gail_html_box_class_init (GailHtmlBoxClass *klass); +static void gail_html_box_initialize (AtkObject *obj, + gpointer data); +static gint gail_html_box_get_index_in_parent (AtkObject *obj); +static AtkStateSet* gail_html_box_ref_state_set (AtkObject *obj); + +static void gail_html_box_component_interface_init (AtkComponentIface *iface); +static guint gail_html_box_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler); +static void gail_html_box_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); +static gboolean gail_html_box_grab_focus (AtkComponent *component); +static void gail_html_box_remove_focus_handler (AtkComponent *component, + guint handler_id); + +static AtkGObjectClass *parent_class = NULL; + +GType +gail_html_box_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailHtmlBoxClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_html_box_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailHtmlBox), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) gail_html_box_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (ATK_TYPE_GOBJECT, + "GailHtmlBox", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + } + + return type; +} + +static void +gail_html_box_class_init (GailHtmlBoxClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (ATK_TYPE_GOBJECT); + + class->get_index_in_parent = gail_html_box_get_index_in_parent; + class->ref_state_set = gail_html_box_ref_state_set; + class->initialize = gail_html_box_initialize; +} + +AtkObject* +gail_html_box_new (GObject *obj) +{ + GObject *object; + AtkObject *atk_object; + + g_return_val_if_fail (HTML_IS_BOX (obj), NULL); + object = g_object_new (GAIL_TYPE_HTML_BOX, NULL); + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, obj); + atk_object->role = ATK_ROLE_UNKNOWN; + return atk_object; +} + +static void +gail_html_box_initialize (AtkObject *obj, + gpointer data) +{ + HtmlBox *box; + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + box = HTML_BOX (data); + + /* + * We do not set the parent here for the root node of a HtmlView + */ + if (box->parent) + { + atk_object_set_parent (obj, + atk_gobject_get_accessible (G_OBJECT (box->parent))); + } +} + +static gint +gail_html_box_get_index_in_parent (AtkObject *obj) +{ + AtkObject *parent; + AtkGObject *atk_gobj; + HtmlBox *box; + HtmlBox *parent_box; + gint n_children = 0; + GObject *g_obj; + + g_return_val_if_fail (GAIL_IS_HTML_BOX (obj), -1); + + atk_gobj = ATK_GOBJECT (obj); + g_obj = atk_gobject_get_object (atk_gobj); + if (g_obj == NULL) + return -1; + + g_return_val_if_fail (HTML_IS_BOX (g_obj), -1); + box = HTML_BOX (g_obj); + parent = atk_object_get_parent (obj); + if (GAIL_IS_HTML_VIEW (parent)) + { + return 0; + } + else if (ATK_IS_GOBJECT (parent)) + { + parent_box = HTML_BOX (atk_gobject_get_object (ATK_GOBJECT (parent))); + } + else + { + g_assert_not_reached (); + return -1; + } + + if (parent_box) + { + HtmlBox *child; + + child = parent_box->children; + + while (child) + { + if (child == box) + return n_children; + + n_children++; + child = child->next; + } + } + return -1; +} + +static AtkStateSet* +gail_html_box_ref_state_set (AtkObject *obj) +{ + AtkGObject *atk_gobj; + GObject *g_obj; + AtkStateSet *state_set; + + g_return_val_if_fail (GAIL_IS_HTML_BOX (obj), NULL); + atk_gobj = ATK_GOBJECT (obj); + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + + g_obj = atk_gobject_get_object (atk_gobj); + if (g_obj == NULL) + { + /* Object is defunct */ + atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); + } + else + { + HtmlBox *box; + + box = HTML_BOX (g_obj); + + if (HTML_BOX_GET_STYLE (box)->display != HTML_DISPLAY_NONE) + { + atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); + atk_state_set_add_state (state_set, ATK_STATE_SHOWING); + } + } + return state_set; +} + +static void +gail_html_box_component_interface_init (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + /* + * Use default implementation for contains and get_position + */ + iface->contains = NULL; + iface->get_position = NULL; + iface->add_focus_handler = gail_html_box_add_focus_handler; + iface->get_extents = gail_html_box_get_extents; + iface->get_size = NULL; + iface->grab_focus = gail_html_box_grab_focus; + iface->remove_focus_handler = gail_html_box_remove_focus_handler; + iface->set_extents = NULL; + iface->set_position = NULL; + iface->set_size = NULL; +} + +static guint +gail_html_box_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler) +{ + return g_signal_connect_closure (component, + "focus-event", + g_cclosure_new ( + G_CALLBACK (handler), NULL, + (GClosureNotify) NULL), + FALSE); +} + +static void +gail_html_box_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + AtkGObject *atk_gobj; + HtmlBox *box; + GObject *g_obj; + + g_return_if_fail (GAIL_IS_HTML_BOX (component)); + + atk_gobj = ATK_GOBJECT (component); + g_obj = atk_gobject_get_object (atk_gobj); + if (g_obj == NULL) + return; + + g_return_if_fail (HTML_IS_BOX (g_obj)); + box = HTML_BOX (g_obj); + + *x = html_box_get_absolute_x (box); + *y = html_box_get_absolute_y (box); + *width = box->width; + *height = box->height; + + g_print ("%d %d %d %d\n", + html_box_get_absolute_x (box), + html_box_get_absolute_y (box), + html_box_get_containing_block_width (box), + html_box_get_containing_block_height (box)); +} + +static gboolean +gail_html_box_grab_focus (AtkComponent *component) +{ + return TRUE; +} + +static void +gail_html_box_remove_focus_handler (AtkComponent *component, + guint handler_id) +{ + g_signal_handler_disconnect (ATK_OBJECT (component), handler_id); +} diff --git a/modules/other/gail/gailhtmlbox.h b/modules/other/gail/gailhtmlbox.h new file mode 100644 index 000000000..bf2200bbb --- /dev/null +++ b/modules/other/gail/gailhtmlbox.h @@ -0,0 +1,58 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_HTML_BOX_H__ +#define __GAIL_HTML_BOX_H__ + +#include <atk/atk.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_HTML_BOX (gail_html_box_get_type ()) +#define GAIL_HTML_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_HTML_BOX, GailHtmlBox)) +#define GAIL_HTML_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_HTML_BOX, GailHtmlBoxClass)) +#define GAIL_IS_HTML_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_HTML_BOX)) +#define GAIL_IS_HTML_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_HTML_BOX)) +#define GAIL_HTML_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_HTML_BOX, GailHtmlBoxClass)) + +typedef struct _GailHtmlBox GailHtmlBox; +typedef struct _GailHtmlBoxClass GailHtmlBoxClass; + +struct _GailHtmlBox +{ + AtkGObject parent; +}; + +struct _GailHtmlBoxClass +{ + AtkGObjectClass parent_class; +}; + +GType gail_html_box_get_type (void); + +AtkObject* gail_html_box_new (GObject *obj); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_HTML_BOX_H__ */ diff --git a/modules/other/gail/gailhtmlboxblock.c b/modules/other/gail/gailhtmlboxblock.c new file mode 100644 index 000000000..9240029ee --- /dev/null +++ b/modules/other/gail/gailhtmlboxblock.c @@ -0,0 +1,153 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <libgtkhtml/gtkhtml.h> +#include "gailhtmlboxblock.h" + +static void gail_html_box_block_class_init (GailHtmlBoxBlockClass *klass); +static gint gail_html_box_block_get_n_children (AtkObject *obj); +static AtkObject* gail_html_box_block_ref_child (AtkObject *obj, + gint i); + +static GailHtmlBoxClass *parent_class = NULL; + +GType +gail_html_box_block_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailHtmlBoxBlockClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_html_box_block_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailHtmlBoxBlock), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_HTML_BOX, + "GailHtmlBoxBlock", &tinfo, 0); + } + + return type; +} + +AtkObject* +gail_html_box_block_new (GObject *obj) +{ + GObject *object; + AtkObject *atk_object; + + g_return_val_if_fail (HTML_IS_BOX_BLOCK (obj), NULL); + object = g_object_new (GAIL_TYPE_HTML_BOX_BLOCK, NULL); + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, obj); + atk_object->role = ATK_ROLE_PANEL; + return atk_object; +} + +static void +gail_html_box_block_class_init (GailHtmlBoxBlockClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (GAIL_TYPE_HTML_BOX); + + class->get_n_children = gail_html_box_block_get_n_children; + class->ref_child = gail_html_box_block_ref_child; +} + +static gint +gail_html_box_block_get_n_children (AtkObject *obj) +{ + AtkGObject *atk_gobject; + HtmlBox *box; + gint n_children = 0; + GObject *g_obj; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_BLOCK (obj), 0); + atk_gobject = ATK_GOBJECT (obj); + g_obj = atk_gobject_get_object (atk_gobject); + if (g_obj == NULL) + return 0; + + g_return_val_if_fail (HTML_IS_BOX (g_obj), 0); + box = HTML_BOX (g_obj); + + if (box) + { + HtmlBox *child; + + child = box->children; + + while (child) + { + n_children++; + child = child->next; + } + } + return n_children; +} + +static AtkObject * +gail_html_box_block_ref_child (AtkObject *obj, + gint i) +{ + AtkGObject *atk_gobject; + GObject *g_obj; + HtmlBox *box; + AtkObject *atk_child = NULL; + gint n_children = 0; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_BLOCK (obj), NULL); + atk_gobject = ATK_GOBJECT (obj); + g_obj = atk_gobject_get_object (atk_gobject); + if (g_obj == NULL) + return NULL; + + g_return_val_if_fail (HTML_IS_BOX (g_obj), NULL); + box = HTML_BOX (g_obj); + + if (box) + { + HtmlBox *child; + + child = box->children; + + while (child) + { + if (n_children == i) + { + atk_child = atk_gobject_get_accessible (G_OBJECT (child)); + g_object_ref (atk_child); + break; + } + n_children++; + child = child->next; + } + } + return atk_child; +} diff --git a/modules/other/gail/gailhtmlboxembedded.c b/modules/other/gail/gailhtmlboxembedded.c new file mode 100644 index 000000000..30d20f36f --- /dev/null +++ b/modules/other/gail/gailhtmlboxembedded.c @@ -0,0 +1,133 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <libgtkhtml/gtkhtml.h> +#include "gailhtmlboxembedded.h" + +static void gail_html_box_embedded_class_init (GailHtmlBoxEmbeddedClass *klass); +static gint gail_html_box_embedded_get_n_children (AtkObject *obj); +static AtkObject* gail_html_box_embedded_ref_child (AtkObject *obj, + gint i); + +static GailHtmlBoxClass *parent_class = NULL; + +GType +gail_html_box_embedded_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailHtmlBoxEmbeddedClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_html_box_embedded_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailHtmlBoxEmbedded), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_HTML_BOX, + "GailHtmlBoxEmbedded", &tinfo, 0); + } + + return type; +} + +AtkObject* +gail_html_box_embedded_new (GObject *obj) +{ + gpointer object; + AtkObject *atk_object; + + g_return_val_if_fail (HTML_IS_BOX_EMBEDDED (obj), NULL); + object = g_object_new (GAIL_TYPE_HTML_BOX_EMBEDDED, NULL); + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, obj); + atk_object->role = ATK_ROLE_PANEL; + return atk_object; +} + +static void +gail_html_box_embedded_class_init (GailHtmlBoxEmbeddedClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_ref (GAIL_TYPE_HTML_BOX); + + class->get_n_children = gail_html_box_embedded_get_n_children; + class->ref_child = gail_html_box_embedded_ref_child; +} + +static gint +gail_html_box_embedded_get_n_children (AtkObject *obj) +{ + AtkGObject *atk_gobject; + HtmlBoxEmbedded *box_embedded; + GObject *g_obj; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_EMBEDDED (obj), 0); + atk_gobject = ATK_GOBJECT (obj); + g_obj = atk_gobject_get_object (atk_gobject); + if (g_obj == NULL) + /* State is defunct */ + return 0; + + g_return_val_if_fail (HTML_IS_BOX_EMBEDDED (g_obj), 0); + + box_embedded = HTML_BOX_EMBEDDED (g_obj); + g_return_val_if_fail (box_embedded->widget, 0); + return 1; +} + +static AtkObject* +gail_html_box_embedded_ref_child (AtkObject *obj, + gint i) +{ + AtkGObject *atk_gobject; + HtmlBoxEmbedded *box_embedded; + GObject *g_obj; + AtkObject *atk_child; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_EMBEDDED (obj), NULL); + + if (i != 0) + return NULL; + + atk_gobject = ATK_GOBJECT (obj); + g_obj = atk_gobject_get_object (atk_gobject); + if (g_obj == NULL) + /* State is defunct */ + return NULL; + + g_return_val_if_fail (HTML_IS_BOX_EMBEDDED (g_obj), NULL); + + box_embedded = HTML_BOX_EMBEDDED (g_obj); + g_return_val_if_fail (box_embedded->widget, NULL); + + atk_child = gtk_widget_get_accessible (box_embedded->widget); + g_object_ref (atk_child); + atk_object_set_parent (atk_child, obj); + return atk_child; +} diff --git a/modules/other/gail/gailhtmlboxtext.c b/modules/other/gail/gailhtmlboxtext.c new file mode 100644 index 000000000..9d2874cbb --- /dev/null +++ b/modules/other/gail/gailhtmlboxtext.c @@ -0,0 +1,506 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gailhtmlboxtext.h" + +static void gail_html_box_text_class_init (GailHtmlBoxTextClass *klass); +static void gail_html_box_text_text_interface_init (AtkTextIface *iface); +static gchar* gail_html_box_text_get_text (AtkText *text, + gint start_offset, + gint end_offset); +static gchar* gail_html_box_text_get_text_after_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_html_box_text_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_html_box_text_get_text_before_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gunichar gail_html_box_text_get_character_at_offset + (AtkText *text, + gint offset); +static gint gail_html_box_text_get_character_count (AtkText *text); +static gint gail_html_box_text_get_caret_offset (AtkText *text); +static gboolean gail_html_box_text_set_caret_offset (AtkText *text, + gint offset); +static gint gail_html_box_text_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static void gail_html_box_text_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static AtkAttributeSet* + gail_html_box_text_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* + gail_html_box_text_get_default_attributes (AtkText *text); +static gint gail_html_box_text_get_n_selections (AtkText *text); +static gchar* gail_html_box_text_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos); +static gboolean gail_html_box_text_add_selection (AtkText *text, + gint start_pos, + gint end_pos); +static gboolean gail_html_box_text_remove_selection (AtkText *text, + gint selection_num); +static gboolean gail_html_box_text_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos); +static AtkAttributeSet* + add_to_attr_set (AtkAttributeSet *attrib_set, + GtkTextAttributes *attrs, + AtkTextAttribute attr); +static gchar* get_text_near_offset (AtkText *text, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset); + +static AtkObjectClass *parent_class = NULL; + +GType +gail_html_box_text_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailHtmlBoxTextClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_html_box_text_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailHtmlBoxText), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) gail_html_box_text_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + type = g_type_register_static (GAIL_TYPE_HTML_BOX, + "GailHtmlBoxText", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + + return type; +} + +AtkObject* +gail_html_box_text_new (GObject *obj) +{ + gpointer object; + AtkObject *atk_object; + GailHtmlBoxText *gail_text; + + g_return_val_if_fail (HTML_IS_BOX_TEXT (obj), NULL); + object = g_object_new (GAIL_TYPE_HTML_BOX_TEXT, NULL); + atk_object = ATK_OBJECT (object); + gail_text = GAIL_HTML_BOX_TEXT (object); + + atk_object_initialize (atk_object, obj); + gail_text->texthelper = gail_text_helper_new (); +#if 0 + gail_text_helper_text_setup (gail_text->texthelper, + HTML_BOX_TEXT (obj)->master->text); +#endif + + atk_object->role = ATK_ROLE_TEXT; + return atk_object; +} + +static void +gail_html_box_text_class_init (GailHtmlBoxTextClass *klass) +{ + parent_class = g_type_class_ref (ATK_TYPE_OBJECT); +} + +static void +gail_html_box_text_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_text = gail_html_box_text_get_text; + iface->get_text_after_offset = gail_html_box_text_get_text_after_offset; + iface->get_text_at_offset = gail_html_box_text_get_text_at_offset; + iface->get_text_before_offset = gail_html_box_text_get_text_before_offset; + iface->get_character_at_offset = gail_html_box_text_get_character_at_offset; + iface->get_character_count = gail_html_box_text_get_character_count; + iface->get_caret_offset = gail_html_box_text_get_caret_offset; + iface->set_caret_offset = gail_html_box_text_set_caret_offset; + iface->get_offset_at_point = gail_html_box_text_get_offset_at_point; + iface->get_character_extents = gail_html_box_text_get_character_extents; + iface->get_n_selections = gail_html_box_text_get_n_selections; + iface->get_selection = gail_html_box_text_get_selection; + iface->add_selection = gail_html_box_text_add_selection; + iface->remove_selection = gail_html_box_text_remove_selection; + iface->set_selection = gail_html_box_text_set_selection; + iface->get_run_attributes = gail_html_box_text_get_run_attributes; + iface->get_default_attributes = gail_html_box_text_get_default_attributes; +} + +static gchar* +gail_html_box_text_get_text (AtkText *text, + gint start_offset, + gint end_offset) +{ + GailHtmlBoxText *gail_text; + GtkTextBuffer *buffer; + GtkTextIter start, end; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_TEXT (text), NULL); + gail_text = GAIL_HTML_BOX_TEXT (text); + g_return_val_if_fail (gail_text->texthelper, NULL); + + buffer = gail_text->texthelper->buffer; + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset); + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset); + + return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); +} + +static gchar* +gail_html_box_text_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return get_text_near_offset (text, GAIL_AFTER_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gchar* +gail_html_box_text_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return get_text_near_offset (text, GAIL_AT_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gchar* +gail_html_box_text_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return get_text_near_offset (text, GAIL_BEFORE_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gunichar +gail_html_box_text_get_character_at_offset (AtkText *text, + gint offset) +{ + GailHtmlBoxText *gail_item; + GtkTextIter start, end; + GtkTextBuffer *buffer; + gchar *string; + gchar *index; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_TEXT (text), 0); + gail_item = GAIL_HTML_BOX_TEXT (text); + buffer = gail_item->texthelper->buffer; + gtk_text_buffer_get_start_iter (buffer, &start); + gtk_text_buffer_get_end_iter (buffer, &end); + string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + index = g_utf8_offset_to_pointer (string, offset); + g_free (string); + + return g_utf8_get_char (index); +} + +static gint +gail_html_box_text_get_character_count (AtkText *text) +{ + GtkTextBuffer *buffer; + GailHtmlBoxText *gail_text; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_TEXT (text), 0); + gail_text = GAIL_HTML_BOX_TEXT (text); + g_return_val_if_fail (gail_text->texthelper, 0); + buffer = gail_text->texthelper->buffer; + return gtk_text_buffer_get_char_count (buffer); +} + +static gint +gail_html_box_text_get_caret_offset (AtkText *text) +{ + GailHtmlBoxText *gail_text; + GtkTextBuffer *buffer; + GtkTextMark *cursor_mark; + GtkTextIter cursor_itr; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_TEXT (text), 0); + gail_text = GAIL_HTML_BOX_TEXT (text); + g_return_val_if_fail (gail_text->texthelper, 0); + buffer = gail_text->texthelper->buffer; + cursor_mark = gtk_text_buffer_get_insert (buffer); + gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark); + return gtk_text_iter_get_offset (&cursor_itr); +} + +static gboolean +gail_html_box_text_set_caret_offset (AtkText *text, + gint offset) +{ + GailHtmlBoxText *gail_text; + GtkTextBuffer *buffer; + GtkTextIter pos_itr; + + g_return_val_if_fail (GAIL_IS_HTML_BOX_TEXT (text), FALSE); + gail_text = GAIL_HTML_BOX_TEXT (text); + g_return_val_if_fail (gail_text->texthelper, FALSE); + buffer = gail_text->texthelper->buffer; + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, offset); + gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr); + return TRUE; +} + +static gint +gail_html_box_text_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + return -1; +} + +static void +gail_html_box_text_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + return; +} + +static AtkAttributeSet* +gail_html_box_text_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + return NULL; +} + +static AtkAttributeSet* +gail_html_box_text_get_default_attributes (AtkText *text) +{ + return NULL; +} + +static gint +gail_html_box_text_get_n_selections (AtkText *text) +{ + return 0; +} + +static gchar* +gail_html_box_text_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + return NULL; +} + +static gboolean +gail_html_box_text_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + return FALSE; +} + +static gboolean +gail_html_box_text_remove_selection (AtkText *text, + gint selection_num) +{ + return FALSE; +} + +static gboolean +gail_html_box_text_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + return FALSE; +} + +static AtkAttributeSet* +add_to_attr_set (AtkAttributeSet *attrib_set, + GtkTextAttributes *attrs, + AtkTextAttribute attr) +{ + gchar *value; + + switch (attr) + { + case ATK_TEXT_ATTR_LEFT_MARGIN: + value = g_strdup_printf ("%i", attrs->left_margin); + break; + case ATK_TEXT_ATTR_RIGHT_MARGIN: + value = g_strdup_printf ("%i", attrs->right_margin); + break; + case ATK_TEXT_ATTR_INDENT: + value = g_strdup_printf ("%i", attrs->indent); + break; + case ATK_TEXT_ATTR_INVISIBLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->invisible)); + break; + case ATK_TEXT_ATTR_EDITABLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->editable)); + break; + case ATK_TEXT_ATTR_PIXELS_ABOVE_LINES: + value = g_strdup_printf ("%i", attrs->pixels_above_lines); + break; + case ATK_TEXT_ATTR_PIXELS_BELOW_LINES: + value = g_strdup_printf ("%i", attrs->pixels_below_lines); + break; + case ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP: + value = g_strdup_printf ("%i", attrs->pixels_inside_wrap); + break; + case ATK_TEXT_ATTR_BG_FULL_HEIGHT: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->bg_full_height)); + break; + case ATK_TEXT_ATTR_RISE: + value = g_strdup_printf ("%i", attrs->appearance.rise); + break; + case ATK_TEXT_ATTR_UNDERLINE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.underline)); + break; + case ATK_TEXT_ATTR_STRIKETHROUGH: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.strikethrough)); + break; + case ATK_TEXT_ATTR_SIZE: + value = g_strdup_printf ("%i", + pango_font_description_get_size (attrs->font)); + break; + case ATK_TEXT_ATTR_SCALE: + value = g_strdup_printf ("%g", attrs->font_scale); + break; + case ATK_TEXT_ATTR_WEIGHT: + value = g_strdup_printf ("%d", + pango_font_description_get_weight (attrs->font)); + break; + case ATK_TEXT_ATTR_LANGUAGE: + value = g_strdup ((gchar *)(attrs->language)); + break; + case ATK_TEXT_ATTR_FAMILY_NAME: + value = g_strdup (pango_font_description_get_family (attrs->font)); + break; + case ATK_TEXT_ATTR_BG_COLOR: + value = g_strdup_printf ("%u,%u,%u", + attrs->appearance.bg_color.red, + attrs->appearance.bg_color.green, + attrs->appearance.bg_color.blue); + break; + case ATK_TEXT_ATTR_FG_COLOR: + value = g_strdup_printf ("%u,%u,%u", + attrs->appearance.fg_color.red, + attrs->appearance.fg_color.green, + attrs->appearance.fg_color.blue); + break; + case ATK_TEXT_ATTR_BG_STIPPLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.bg_stipple ? 1 : 0)); + break; + case ATK_TEXT_ATTR_FG_STIPPLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.fg_stipple ? 1 : 0)); + break; + case ATK_TEXT_ATTR_WRAP_MODE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->wrap_mode)); + break; + case ATK_TEXT_ATTR_DIRECTION: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->direction)); + break; + case ATK_TEXT_ATTR_JUSTIFICATION: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->justification)); + break; + case ATK_TEXT_ATTR_STRETCH: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_stretch (attrs->font))); + break; + case ATK_TEXT_ATTR_VARIANT: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_variant (attrs->font))); + break; + case ATK_TEXT_ATTR_STYLE: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_style (attrs->font))); + break; + default: + value = NULL; + break; + } + return gail_text_helper_add_attribute (attrib_set, + attr, + value); +} + +static gchar* +get_text_near_offset (AtkText *text, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset) +{ + return gail_text_helper_get_text (GAIL_HTML_BOX_TEXT (text)->texthelper, NULL, + function, boundary_type, offset, + start_offset, end_offset); +} diff --git a/modules/other/gail/gailimage.c b/modules/other/gail/gailimage.c new file mode 100644 index 000000000..e0bcc08df --- /dev/null +++ b/modules/other/gail/gailimage.c @@ -0,0 +1,430 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailimage.h" +#include "gailintl.h" + +static void gail_image_class_init (GailImageClass *klass); +static void gail_image_object_init (GailImage *image); +static G_CONST_RETURN gchar* gail_image_get_name (AtkObject *accessible); + + +static void atk_image_interface_init (AtkImageIface *iface); + +static G_CONST_RETURN gchar * + gail_image_get_image_description (AtkImage *image); +static void gail_image_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type); +static void gail_image_get_image_size (AtkImage *image, + gint *width, + gint *height); +static gboolean gail_image_set_image_description (AtkImage *image, + const gchar *description); +static void gail_image_finalize (GObject *object); + +typedef struct _GailImageItem GailImageItem; + +struct _GailImageItem +{ + GQuark id; + gchar *name; + gchar *stock_id; +}; + +static GailImageItem stock_items [] = +{ + { 0, N_("dialog authentication"), "gtk-dialog-authentication"}, + { 0, N_("dialog information"), "gtk-dialog-info"}, + { 0, N_("dialog warning"), "gtk-dialog-warning"}, + { 0, N_("dialog error"), "gtk-dialog-error"}, + { 0, N_("dialog question"), "gtk-dialog-question"}, + { 0, N_("drag and drop"), "gtk-dnd"}, + { 0, N_("multiple drag and drop"), "gtk-dnd-multiple"}, + { 0, N_("add"), "gtk-add"}, + { 0, N_("apply"), "gtk-apply"}, + { 0, N_("bold"), "gtk-bold"}, + { 0, N_("cancel"), "gtk_cancel"}, + { 0, N_("cdrom"), "gtk-cdrom"}, + { 0, N_("clear"), "gtk-clear"}, + { 0, N_("close"), "gtk-close"}, + { 0, N_("color picker"), "gtk-color-picker"}, + { 0, N_("convert"), "gtk-convert"}, + { 0, N_("copy"), "gtk-copy"}, + { 0, N_("cut"), "gtk-cut"}, + { 0, N_("delete"), "gtk-delete"}, + { 0, N_("execute"), "gtk-execute"}, + { 0, N_("find"), "gtk-find"}, + { 0, N_("find and replace"), "gtk-find-and-replace"}, + { 0, N_("floppy"), "gtk-floppy"}, + { 0, N_("go to bottom"), "gtk-goto-bottom"}, + { 0, N_("go to first"), "gtk-goto-first"}, + { 0, N_("go to last"), "gtk-goto-last"}, + { 0, N_("go to top"), "gtk-goto-top"}, + { 0, N_("go back"), "gtk-go-back"}, + { 0, N_("go down"), "gtk-go-down"}, + { 0, N_("go forward"), "gtk-go-forward"}, + { 0, N_("go up"), "gtk-go-up"}, + { 0, N_("help"), "gtk-help"}, + { 0, N_("home"), "gtk-home"}, + { 0, N_("index"), "gtk-index"}, + { 0, N_("italic"), "gtk-italic"}, + { 0, N_("jump to"), "gtk-jump-to"}, + { 0, N_("justify center"), "gtk-justify-center"}, + { 0, N_("justify fill"), "gtk-justify-fill"}, + { 0, N_("justify left"), "gtk-justify-left"}, + { 0, N_("justify right"), "gtk-justify-right"}, + { 0, N_("missing image"), "gtk-missing-image"}, + { 0, N_("new"), "gtk-new"}, + { 0, N_("no"), "gtk-no"}, + { 0, N_("ok"), "gtk-ok"}, + { 0, N_("open"), "gtk-open"}, + { 0, N_("paste"), "gtk-paste"}, + { 0, N_("preferences"), "gtk-preferences"}, + { 0, N_("print"), "gtk-print"}, + { 0, N_("print preview"), "gtk-print-preview"}, + { 0, N_("properties"), "gtk-properties"}, + { 0, N_("quit"), "gtk-quit"}, + { 0, N_("redo"), "gtk-redo"}, + { 0, N_("refresh"), "gtk-refresh"}, + { 0, N_("remove"), "gtk-remove"}, + { 0, N_("revert to saved"), "gtk-revert-to-saved"}, + { 0, N_("save"), "gtk-save"}, + { 0, N_("save as"), "gtk-save-as"}, + { 0, N_("select color"), "gtk-select-color"}, + { 0, N_("select font"), "gtk-select-font"}, + { 0, N_("sort ascending"), "gtk-sort-ascending"}, + { 0, N_("sort descending"), "gtk-sort-descending"}, + { 0, N_("spell check"), "gtk-spell-check"}, + { 0, N_("stop"), "gtk-stop"}, + { 0, N_("strikethrough"), "gtk-strikethrough"}, + { 0, N_("undelete"), "gtk-undelete"}, + { 0, N_("underline"), "gtk-underline"}, + { 0, N_("yes"), "gtk-yes"}, + { 0, N_("zoom 100 percent"), "gtk-zoom-100"}, + { 0, N_("zoom fit"), "gtk-zoom-fit"}, + { 0, N_("zoom in"), "gtk-zoom-in"}, + { 0, N_("zoom out"), "gtk-zoom-out"}, + + { 0, N_("timer"), "gnome-stock-timer"}, + { 0, N_("timer stop"), "gnome-stock-timer-stop"}, + { 0, N_("trash"), "gnome-stock-trash"}, + { 0, N_("trash full"), "gnome-stock-trash-full"}, + { 0, N_("scores"), "gnome-stock-scores"}, + { 0, N_("about"), "gnome-stock-about"}, + { 0, N_("blank"), "gnome-stock-blank"}, + { 0, N_("volume"), "gnome-stock-volume"}, + { 0, N_("midi"), "gnome-stock-midi"}, + { 0, N_("microphone"), "gnome-stock-mic"}, + { 0, N_("line in"), "gnome-stock-line-in"}, + { 0, N_("mail"), "gnome-stock-mail"}, + { 0, N_("mail receive"), "gnome-stock-mail-rcv"}, + { 0, N_("mail send"), "gnome-stock-mail-snd"}, + { 0, N_("mail reply"), "gnome-stock-mail-rpl"}, + { 0, N_("mail forward"), "gnome-stock-mail-fwd"}, + { 0, N_("mail new"), "gnome-stock-mail-new"}, + { 0, N_("attach"), "gnome-stock-attach"}, + { 0, N_("book red"), "gnome-stock-book-red"}, + { 0, N_("book green"), "gnome-stock-book-green"}, + { 0, N_("book blue"), "gnome-stock-book-blue"}, + { 0, N_("book yellow"), "gnome-stock-book-yellow"}, + { 0, N_("book open"), "gnome-stock-book-open"}, + { 0, N_("multiple file"), "gnome-stock-multiple-file"}, + { 0, N_("not"), "gnome-stock-not"}, + { 0, N_("table borders"), "gnome-stock-table-borders"}, + { 0, N_("table fill"), "gnome-stock-table-fill"}, + { 0, N_("text indent"), "gnome-stock-text-indent"}, + { 0, N_("text unindent"), "gnome-stock-text-unindent"}, + { 0, N_("text bulleted list"), "gnome-stock-text-bulleted-list"}, + { 0, N_("text numbered list"), "gnome-stock-text-numbered-list"}, + { 0, N_("authentication"), "gnome-stock-authentication"} +}; + +static gpointer parent_class = NULL; + +GType +gail_image_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailImageClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_image_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailImage), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_image_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) atk_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailImage", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + } + + return type; +} + +static void +gail_image_class_init (GailImageClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_image_finalize; + class->get_name = gail_image_get_name; +} + +static void +gail_image_object_init (GailImage *image) +{ + image->image_description = NULL; +} + +AtkObject* +gail_image_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_IMAGE (widget), NULL); + + object = g_object_new (GAIL_TYPE_IMAGE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_ICON; + + return accessible; +} + +static void +init_strings (void) +{ + gint i; + + for (i = 0; i < G_N_ELEMENTS (stock_items); i++) + stock_items[i].id = g_quark_from_static_string (stock_items[i].stock_id); +} + +static G_CONST_RETURN gchar* +get_localized_name (const gchar *str) +{ + GQuark str_q; + gint i; + +#if 0 + static gboolean gettext_initialized = FALSE; + + if (!gettext_initialized) + { + init_strings (); + gettext_initialized = TRUE; +#ifdef ENABLE_NLS + bindtextdomain (GETTEXT_PACKAGE, GAIL_LOCALEDIR); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif +#endif + } +#endif + + str_q = g_quark_try_string (str); + for (i = 0; i < G_N_ELEMENTS (stock_items); i++) + { + if (str_q == stock_items[i].id) + return dgettext (GETTEXT_PACKAGE, stock_items[i].name); + } + + return str; +} + +static G_CONST_RETURN gchar* +gail_image_get_name (AtkObject *accessible) +{ + G_CONST_RETURN gchar *name; + + name = ATK_OBJECT_CLASS (parent_class)->get_name (accessible); + if (name) + return name; + else + { + GtkWidget* widget = GTK_ACCESSIBLE (accessible)->widget; + GtkImage *image; + + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_IMAGE (widget), NULL); + image = GTK_IMAGE (widget); + + if (image->storage_type == GTK_IMAGE_STOCK && + image->data.stock.stock_id) + return get_localized_name (image->data.stock.stock_id); + else return NULL; + } +} + +static void +atk_image_interface_init (AtkImageIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_image_description = gail_image_get_image_description; + iface->get_image_position = gail_image_get_image_position; + iface->get_image_size = gail_image_get_image_size; + iface->set_image_description = gail_image_set_image_description; +} + +static G_CONST_RETURN gchar * +gail_image_get_image_description (AtkImage *image) +{ + GailImage* aimage = GAIL_IMAGE (image); + + return aimage->image_description; +} + +static void +gail_image_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); +} + +static void +gail_image_get_image_size (AtkImage *image, + gint *width, + gint *height) +{ + GtkWidget* widget; + GtkImage *gtk_image; + GtkImageType image_type; + + widget = GTK_ACCESSIBLE (image)->widget; + if (widget == 0) + { + /* State is defunct */ + *height = -1; + *width = -1; + return; + } + + gtk_image = GTK_IMAGE(widget); + + image_type = gtk_image_get_storage_type(gtk_image); + + switch(image_type) { + case GTK_IMAGE_PIXMAP: + { + GdkPixmap *pixmap; + gtk_image_get_pixmap(gtk_image, &pixmap, NULL); + gdk_window_get_size (pixmap, width, height); + break; + } + case GTK_IMAGE_PIXBUF: + { + GdkPixbuf *pixbuf; + pixbuf = gtk_image_get_pixbuf(gtk_image); + *height = gdk_pixbuf_get_height(pixbuf); + *width = gdk_pixbuf_get_width(pixbuf); + break; + } + case GTK_IMAGE_IMAGE: + { + GdkImage *gdk_image; + gtk_image_get_image(gtk_image, &gdk_image, NULL); + *height = gdk_image->height; + *width = gdk_image->width; + break; + } + case GTK_IMAGE_STOCK: + { + GtkIconSize size; + gtk_image_get_stock(gtk_image, NULL, &size); + gtk_icon_size_lookup(size, width, height); + break; + } + case GTK_IMAGE_ICON_SET: + { + GtkIconSize size; + gtk_image_get_icon_set(gtk_image, NULL, &size); + gtk_icon_size_lookup(size, width, height); + break; + } + case GTK_IMAGE_ANIMATION: + { + GdkPixbufAnimation *animation; + animation = gtk_image_get_animation(gtk_image); + *height = gdk_pixbuf_animation_get_height(animation); + *width = gdk_pixbuf_animation_get_width(animation); + break; + } + default: + { + *height = -1; + *width = -1; + break; + } + } +} + +static gboolean +gail_image_set_image_description (AtkImage *image, + const gchar *description) +{ + GailImage* aimage = GAIL_IMAGE (image); + + g_free (aimage->image_description); + aimage->image_description = g_strdup (description); + return TRUE; +} + +static void +gail_image_finalize (GObject *object) +{ + GailImage *aimage = GAIL_IMAGE (object); + + g_free (aimage->image_description); + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailimage.h b/modules/other/gail/gailimage.h new file mode 100644 index 000000000..a30ff204d --- /dev/null +++ b/modules/other/gail/gailimage.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_IMAGE_H__ +#define __GAIL_IMAGE_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_IMAGE (gail_image_get_type ()) +#define GAIL_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_IMAGE, GailImage)) +#define GAIL_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_IMAGE, GailImageClass)) +#define GAIL_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_IMAGE)) +#define GAIL_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_IMAGE)) +#define GAIL_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_IMAGE, GailImageClass)) + +typedef struct _GailImage GailImage; +typedef struct _GailImageClass GailImageClass; + +struct _GailImage +{ + GailWidget parent; + + gchar* image_description; +}; + +GType gail_image_get_type (void); + +struct _GailImageClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_image_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_IMAGE_H__ */ diff --git a/modules/other/gail/gailimagecell.c b/modules/other/gail/gailimagecell.c new file mode 100644 index 000000000..28f3da7dc --- /dev/null +++ b/modules/other/gail/gailimagecell.c @@ -0,0 +1,207 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailimagecell.h" + +static void gail_image_cell_class_init (GailImageCellClass *klass); +static void gail_image_cell_object_init (GailImageCell *image_cell); + +static void gail_image_cell_finalize (GObject *object); + +/* AtkImage */ +static void atk_image_interface_init (AtkImageIface *iface); +static G_CONST_RETURN gchar * + gail_image_cell_get_image_description (AtkImage *image); +static gboolean gail_image_cell_set_image_description (AtkImage *image, + const gchar *description); +static void gail_image_cell_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type); +static void gail_image_cell_get_image_size (AtkImage *image, + gint *width, + gint *height); + +/* Misc */ + +static gboolean gail_image_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal); + +gchar *gail_image_cell_property_list[] = { + "pixbuf", + NULL +}; + +static gpointer parent_class = NULL; + +GType +gail_image_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailImageCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_image_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailImageCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_image_cell_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) atk_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_RENDERER_CELL, + "GailImageCell", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + + } + return type; +} + +static void +gail_image_cell_class_init (GailImageCellClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GailRendererCellClass *renderer_cell_class = GAIL_RENDERER_CELL_CLASS(klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_image_cell_finalize; + + renderer_cell_class->update_cache = gail_image_cell_update_cache; + renderer_cell_class->property_list = gail_image_cell_property_list; +} + +AtkObject* +gail_image_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + GailRendererCell *cell; + + object = g_object_new (GAIL_TYPE_IMAGE_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + cell = GAIL_RENDERER_CELL(object); + + cell->renderer = gtk_cell_renderer_pixbuf_new (); + g_object_ref (cell->renderer); + gtk_object_sink (GTK_OBJECT (cell->renderer)); + return atk_object; +} + +static void +gail_image_cell_object_init (GailImageCell *image_cell) +{ + image_cell->image_description = NULL; +} + + +static void +gail_image_cell_finalize (GObject *object) +{ + GailImageCell *image_cell = GAIL_IMAGE_CELL (object); + + g_free (image_cell->image_description); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gail_image_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal) +{ + return FALSE; +} + +static void +atk_image_interface_init (AtkImageIface *iface) +{ + iface->get_image_description = gail_image_cell_get_image_description; + iface->set_image_description = gail_image_cell_set_image_description; + iface->get_image_position = gail_image_cell_get_image_position; + iface->get_image_size = gail_image_cell_get_image_size; +} + +static G_CONST_RETURN gchar * +gail_image_cell_get_image_description (AtkImage *image) +{ + GailImageCell *image_cell; + + image_cell = GAIL_IMAGE_CELL (image); + return image_cell->image_description; +} + +static gboolean +gail_image_cell_set_image_description (AtkImage *image, + const gchar *description) +{ + GailImageCell *image_cell; + + image_cell = GAIL_IMAGE_CELL (image); + g_free (image_cell->image_description); + image_cell->image_description = g_strdup (description); + if (image_cell->image_description) + return TRUE; + else + return FALSE; +} + +static void +gail_image_cell_get_image_position (AtkImage *image, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + atk_component_get_position (ATK_COMPONENT (image), x, y, coord_type); +} + +static void +gail_image_cell_get_image_size (AtkImage *image, + gint *width, + gint *height) +{ + GailImageCell *cell = GAIL_IMAGE_CELL (image); + GtkCellRenderer *cell_renderer; + GdkPixbuf *pixbuf; + + cell_renderer = GAIL_RENDERER_CELL (cell)->renderer; + pixbuf = GTK_CELL_RENDERER_PIXBUF (cell_renderer)->pixbuf; + + *width = gdk_pixbuf_get_width (pixbuf); + *height = gdk_pixbuf_get_height (pixbuf); +} diff --git a/modules/other/gail/gailimagecell.h b/modules/other/gail/gailimagecell.h new file mode 100644 index 000000000..7dfd65c54 --- /dev/null +++ b/modules/other/gail/gailimagecell.h @@ -0,0 +1,62 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_IMAGE_CELL_H__ +#define __GAIL_IMAGE_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailrenderercell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_IMAGE_CELL (gail_image_cell_get_type ()) +#define GAIL_IMAGE_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_IMAGE_CELL, GailImageCell)) +#define GAIL_IMAGE_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_IMAGE_CELL, GailImageCellClass)) +#define GAIL_IS_IMAGE_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_IMAGE_CELL)) +#define GAIL_IS_IMAGE_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_IMAGE_CELL))78 +#define GAIL_IMAGE_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_IMAGE_CELL, GailImageCellClass)) + +typedef struct _GailImageCell GailImageCell; +typedef struct _GailImageCellClass GailImageCellClass; + +struct _GailImageCell +{ + GailRendererCell parent; + + gchar *image_description; + gint x, y; +}; + +GType gail_image_cell_get_type (void); + +struct _GailImageCellClass +{ + GailRendererCellClass parent_class; +}; + +AtkObject *gail_image_cell_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_IMAGE_CELL_H__ */ diff --git a/modules/other/gail/gailimagecellfactory.c b/modules/other/gail/gailimagecellfactory.c new file mode 100644 index 000000000..77c4b92e6 --- /dev/null +++ b/modules/other/gail/gailimagecellfactory.c @@ -0,0 +1,79 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkcellrendererpixbuf.h> +#include "gailimagecellfactory.h" +#include "gailimagecell.h" + +static void gail_image_cell_factory_class_init (GailImageCellFactoryClass *klass); + +static AtkObject* gail_image_cell_factory_create_accessible ( + GObject *obj); + +static GType gail_image_cell_factory_get_accessible_type (void); + +GType +gail_image_cell_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailImageCellFactoryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_image_cell_factory_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailImageCellFactory), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + "GailImageCellFactory" , &tinfo, 0); + } + + return type; +} + +static void +gail_image_cell_factory_class_init (GailImageCellFactoryClass *klass) +{ + AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass); + + class->create_accessible = gail_image_cell_factory_create_accessible; + class->get_accessible_type = gail_image_cell_factory_get_accessible_type; +} + +static AtkObject* +gail_image_cell_factory_create_accessible (GObject *obj) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER_PIXBUF (obj), NULL); + + return gail_image_cell_new (); +} + +static GType +gail_image_cell_factory_get_accessible_type (void) +{ + return GAIL_TYPE_IMAGE_CELL; +} diff --git a/modules/other/gail/gailimagecellfactory.h b/modules/other/gail/gailimagecellfactory.h new file mode 100644 index 000000000..adf3fd29d --- /dev/null +++ b/modules/other/gail/gailimagecellfactory.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_IMAGE_CELL_FACTORY_H__ +#define __GAIL_IMAGE_CELL_FACTORY_H__ + +#include <atk/atkobjectfactory.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_IMAGE_CELL_FACTORY (gail_image_cell_factory_get_type ()) +#define GAIL_IMAGE_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_IMAGE_CELL_FACTORY, GailImageCellFactory)) +#define GAIL_IMAGE_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AGTK_TYPE_IMAGE_CELL_FACTORY, GailImageCellFactoryClass)) +#define GAIL_IS_IMAGE_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_IMAGE_CELL_FACTORY)) +#define GAIL_IS_IMAGE_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_IMAGE_CELL_FACTORY)) +#define GAIL_IMAGE_CELL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_IMAGE_CELL_FACTORY, GailImageCellFactoryClass)) + +typedef struct _GailImageCellFactory GailImageCellFactory; +typedef struct _GailImageCellFactoryClass GailImageCellFactoryClass; + +struct _GailImageCellFactory +{ + AtkObjectFactory parent; +}; + +struct _GailImageCellFactoryClass +{ + AtkObjectFactoryClass parent_class; +}; + +GType gail_image_cell_factory_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_IMAGE_CELL_FACTORY_H__ */ diff --git a/modules/other/gail/gailintl.h b/modules/other/gail/gailintl.h new file mode 100644 index 000000000..555de7978 --- /dev/null +++ b/modules/other/gail/gailintl.h @@ -0,0 +1,24 @@ +#ifndef __GAILINTL_H__ +#define __GAILINTL_H__ + +#include "config.h" + +#ifdef ENABLE_NLS +#include<libintl.h> +#define _(String) dgettext(GETTEXT_PACKAGE,String) +#ifdef gettext_noop +#define N_(String) gettext_noop(String) +#else +#define N_(String) (String) +#endif +#else /* NLS is disabled */ +#define _(String) (String) +#define N_(String) (String) +#define textdomain(String) (String) +#define gettext(String) (String) +#define dgettext(Domain,String) (String) +#define dcgettext(Domain,String,Type) (String) +#define bindtextdomain(Domain,Directory) (Domain) +#endif + +#endif diff --git a/modules/other/gail/gailitem.c b/modules/other/gail/gailitem.c new file mode 100644 index 000000000..dffc784c0 --- /dev/null +++ b/modules/other/gail/gailitem.c @@ -0,0 +1,760 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailitem.h" +#include <libgail-util/gailmisc.h> + +static void gail_item_class_init (GailItemClass *klass); +static G_CONST_RETURN gchar* gail_item_get_name (AtkObject *obj); +static gint gail_item_get_n_children (AtkObject *obj); +static AtkObject* gail_item_ref_child (AtkObject *obj, + gint i); +static void gail_item_real_initialize (AtkObject *obj, + gpointer data); +static void gail_item_label_map_gtk (GtkWidget *widget, + gpointer data); +static void gail_item_finalize (GObject *object); +static void gail_item_init_textutil (GailItem *item, + GtkWidget *label); +static void gail_item_notify_label_gtk(GObject *obj, + GParamSpec *pspec, + gpointer data); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_item_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_item_get_character_at_offset(AtkText *text, + gint offset); +static gchar* gail_item_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_item_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_item_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_item_get_character_count (AtkText *text); +static void gail_item_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_item_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_item_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_item_get_default_attributes + (AtkText *text); +static GtkWidget* get_label_from_container (GtkWidget *container); + + + +static GailContainerClass* parent_class = NULL; + +GType +gail_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailItem", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + + return type; +} + +static void +gail_item_class_init (GailItemClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailContainerClass *container_class; + + parent_class = g_type_class_peek_parent (klass); + + container_class = (GailContainerClass *)klass; + + gobject_class->finalize = gail_item_finalize; + + class->get_name = gail_item_get_name; + class->get_n_children = gail_item_get_n_children; + class->ref_child = gail_item_ref_child; + class->initialize = gail_item_real_initialize; + /* + * As we report the item as having no children we are not interested + * in add and remove signals + */ + container_class->add_gtk = NULL; + container_class->remove_gtk = NULL; +} + +AtkObject* +gail_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_ITEM (widget), NULL); + + object = g_object_new (GAIL_TYPE_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_item_real_initialize (AtkObject *obj, + gpointer data) +{ + GailItem *item = GAIL_ITEM (obj); + GtkWidget *label; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + item->textutil = NULL; + item->text = NULL; + + label = get_label_from_container (GTK_WIDGET (data)); + if (GTK_IS_LABEL (label)) + { + if (GTK_WIDGET_MAPPED (label)) + gail_item_init_textutil (item, label); + else + g_signal_connect (label, + "map", + G_CALLBACK (gail_item_label_map_gtk), + item); + } + + obj->role = ATK_ROLE_LIST_ITEM; +} + +static void +gail_item_label_map_gtk (GtkWidget *widget, + gpointer data) +{ + GailItem *item; + + item = GAIL_ITEM (data); + gail_item_init_textutil (item, widget); +} + +static void +gail_item_init_textutil (GailItem *item, + GtkWidget *label) +{ + const gchar *label_text; + + if (item->textutil == NULL) + { + item->textutil = gail_text_util_new (); + g_signal_connect (label, + "notify", + (GCallback) gail_item_notify_label_gtk, + item); + } + label_text = gtk_label_get_text (GTK_LABEL (label)); + gail_text_util_text_setup (item->textutil, label_text); +} + +static void +gail_item_finalize (GObject *object) +{ + GailItem *item = GAIL_ITEM (object); + + if (item->textutil) + { + g_object_unref (item->textutil); + } + if (item->text) + { + g_free (item->text); + item->text = NULL; + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static G_CONST_RETURN gchar* +gail_item_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar* name; + + g_return_val_if_fail (GAIL_IS_ITEM (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name == NULL) + { + /* + * Get the label child + */ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + label = get_label_from_container (widget); + if (GTK_IS_LABEL (label)) + return gtk_label_get_text (GTK_LABEL(label)); + /* + * If we have a menu item in a menu attached to a GtkOptionMenu + * the label of the selected item is detached from the menu item + */ + else if (GTK_IS_MENU_ITEM (widget)) + { + GtkWidget *parent; + GtkWidget *attach; + GList *list; + AtkObject *parent_obj; + gint index; + + parent = gtk_widget_get_parent (widget); + if (GTK_IS_MENU (parent)) + { + attach = gtk_menu_get_attach_widget (GTK_MENU (parent)); + + if (GTK_IS_OPTION_MENU (attach)) + { + label = get_label_from_container (attach); + if (GTK_IS_LABEL (label)) + return gtk_label_get_text (GTK_LABEL(label)); + } + list = gtk_container_get_children (GTK_CONTAINER (parent)); + index = g_list_index (list, widget); + + if (index < 0 || index > g_list_length (list)) + { + g_list_free (list); + return NULL; + } + g_list_free (list); + + parent_obj = atk_object_get_parent (gtk_widget_get_accessible (parent)); + if (GTK_IS_ACCESSIBLE (parent_obj)) + { + parent = GTK_ACCESSIBLE (parent_obj)->widget; + if (GTK_IS_COMBO_BOX (parent)) + { + GtkTreeModel *model; + GtkTreeIter iter; + GailItem *item; + gint n_columns, i; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (parent)); + item = GAIL_ITEM (obj); + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, index)) + { + n_columns = gtk_tree_model_get_n_columns (model); + for (i = 0; i < n_columns; i++) + { + GValue value = { 0, }; + + gtk_tree_model_get_value (model, &iter, i, &value); + if (G_VALUE_HOLDS_STRING (&value)) + { + g_free (item->text); + item->text = (gchar *) g_value_dup_string (&value); + g_value_unset (&value); + break; + } + } + } + name = item->text; + } + } + } + } + } + return name; +} + +/* + * We report that this object has no children + */ + +static gint +gail_item_get_n_children (AtkObject* obj) +{ + return 0; +} + +static AtkObject* +gail_item_ref_child (AtkObject *obj, + gint i) +{ + return NULL; +} + +static void +gail_item_notify_label_gtk (GObject *obj, + GParamSpec *pspec, + gpointer data) +{ + AtkObject* atk_obj = ATK_OBJECT (data); + GtkLabel *label; + GailItem *gail_item; + + if (strcmp (pspec->name, "label") == 0) + { + const gchar* label_text; + + label = GTK_LABEL (obj); + + label_text = gtk_label_get_text (label); + + gail_item = GAIL_ITEM (atk_obj); + gail_text_util_text_setup (gail_item->textutil, label_text); + + if (atk_obj->name == NULL) + { + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (atk_obj), "accessible-name"); + } + /* + * The label is the only property which can be changed + */ + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_item_get_text; + iface->get_character_at_offset = gail_item_get_character_at_offset; + iface->get_text_before_offset = gail_item_get_text_before_offset; + iface->get_text_at_offset = gail_item_get_text_at_offset; + iface->get_text_after_offset = gail_item_get_text_after_offset; + iface->get_character_count = gail_item_get_character_count; + iface->get_character_extents = gail_item_get_character_extents; + iface->get_offset_at_point = gail_item_get_offset_at_point; + iface->get_run_attributes = gail_item_get_run_attributes; + iface->get_default_attributes = gail_item_get_default_attributes; +} + +static gchar* +gail_item_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkWidget *label; + GailItem *item; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL (label)) + return NULL; + + item = GAIL_ITEM (text); + if (!item->textutil) + gail_item_init_textutil (item, label); + + label_text = gtk_label_get_text (GTK_LABEL (label)); + + if (label_text == NULL) + return NULL; + else + { + return gail_text_util_get_substring (item->textutil, + start_pos, end_pos); + } +} + +static gchar* +gail_item_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailItem *item; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + item = GAIL_ITEM (text); + if (!item->textutil) + gail_item_init_textutil (item, label); + + return gail_text_util_get_text (item->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_item_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailItem *item; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + item = GAIL_ITEM (text); + if (!item->textutil) + gail_item_init_textutil (item, label); + + return gail_text_util_get_text (item->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_item_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailItem *item; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + { + /* State is defunct */ + return NULL; + } + + /* Get label */ + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + item = GAIL_ITEM (text); + if (!item->textutil) + gail_item_init_textutil (item, label); + + return gail_text_util_get_text (item->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_item_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return 0; + + return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1); +} + +static void +gail_item_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + label_text = gtk_label_get_text (GTK_LABEL (label)); + index = g_utf8_offset_to_pointer (label_text, offset) - label_text; + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (label, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_item_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return -1; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (label, + gtk_label_get_layout (GTK_LABEL (label)), + x_layout, y_layout, x, y, coords); + label_text = gtk_label_get_text (GTK_LABEL (label)); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label_text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label_text, label_text + index); +} + +static AtkAttributeSet* +gail_item_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (GTK_LABEL (label)); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (label); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + (gchar *) gtk_label_get_text (GTK_LABEL (label)), + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_item_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + widget); + return at_set; +} + +static gunichar +gail_item_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkWidget *label; + const gchar *string; + gchar *index; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + label = get_label_from_container (widget); + + if (!GTK_IS_LABEL(label)) + return '\0'; + string = gtk_label_get_text (GTK_LABEL (label)); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + +static GtkWidget* +get_label_from_container (GtkWidget *container) +{ + GtkWidget *label; + GList *children, *tmp_list; + + if (!GTK_IS_CONTAINER (container)) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (container)); + label = NULL; + + for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) + { + if (GTK_IS_LABEL (tmp_list->data)) + { + label = tmp_list->data; + break; + } + /* + * Get label from menu item in desktop background preferences + * option menu. See bug #144084. + */ + else if (GTK_IS_BOX (tmp_list->data)) + { + label = get_label_from_container (GTK_WIDGET (tmp_list->data)); + if (label) + break; + } + } + g_list_free (children); + + return label; +} diff --git a/modules/other/gail/gailitem.h b/modules/other/gail/gailitem.h new file mode 100644 index 000000000..18e49461f --- /dev/null +++ b/modules/other/gail/gailitem.h @@ -0,0 +1,64 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_ITEM_H__ +#define __GAIL_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_ITEM (gail_item_get_type ()) +#define GAIL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_ITEM, GailItem)) +#define GAIL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_ITEM, GailItemClass)) +#define GAIL_IS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_ITEM)) +#define GAIL_IS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_ITEM)) +#define GAIL_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_ITEM, GailItemClass)) + +typedef struct _GailItem GailItem; +typedef struct _GailItemClass GailItemClass; + +struct _GailItem +{ + GailContainer parent; + + GailTextUtil *textutil; + + gchar *text; +}; + +GType gail_item_get_type (void); + +struct _GailItemClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_ITEM_H__ */ diff --git a/modules/other/gail/gaillabel.c b/modules/other/gail/gaillabel.c new file mode 100644 index 000000000..f9c12ee26 --- /dev/null +++ b/modules/other/gail/gaillabel.c @@ -0,0 +1,1065 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gaillabel.h" +#include "gailwindow.h" +#include <libgail-util/gailmisc.h> + +static void gail_label_class_init (GailLabelClass *klass); +static void gail_label_real_initialize (AtkObject *obj, + gpointer data); +static void gail_label_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static void gail_label_map_gtk (GtkWidget *widget, + gpointer data); +static void gail_label_init_text_util (GailLabel *gail_label, + GtkWidget *widget); +static void gail_label_finalize (GObject *object); + +static void atk_text_interface_init (AtkTextIface *iface); + +/* atkobject.h */ + +static G_CONST_RETURN gchar* gail_label_get_name (AtkObject *accessible); +static AtkStateSet* gail_label_ref_state_set (AtkObject *accessible); +static AtkRelationSet* gail_label_ref_relation_set (AtkObject *accessible); + +/* atktext.h */ + +static gchar* gail_label_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_label_get_character_at_offset(AtkText *text, + gint offset); +static gchar* gail_label_get_text_before_offset(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_label_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_label_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_label_get_character_count (AtkText *text); +static gint gail_label_get_caret_offset (AtkText *text); +static gboolean gail_label_set_caret_offset (AtkText *text, + gint offset); +static gint gail_label_get_n_selections (AtkText *text); +static gchar* gail_label_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset); +static gboolean gail_label_add_selection (AtkText *text, + gint start_offset, + gint end_offset); +static gboolean gail_label_remove_selection (AtkText *text, + gint selection_num); +static gboolean gail_label_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset); +static void gail_label_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_label_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_label_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_label_get_default_attributes + (AtkText *text); + +static GailWidgetClass *parent_class = NULL; + +GType +gail_label_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailLabelClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_label_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailLabel), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailLabel", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + return type; +} + +static void +gail_label_class_init (GailLabelClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + gobject_class->finalize = gail_label_finalize; + + widget_class = (GailWidgetClass*)klass; + widget_class->notify_gtk = gail_label_real_notify_gtk; + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_label_get_name; + class->ref_state_set = gail_label_ref_state_set; + class->ref_relation_set = gail_label_ref_relation_set; + class->initialize = gail_label_real_initialize; +} + +static void +gail_label_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkWidget *widget; + GailLabel *gail_label; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gail_label = GAIL_LABEL (obj); + + gail_label->window_create_handler = 0; + gail_label->has_top_level = FALSE; + gail_label->cursor_position = 0; + gail_label->selection_bound = 0; + gail_label->textutil = NULL; + gail_label->label_length = 0; + + widget = GTK_WIDGET (data); + + if (GTK_WIDGET_MAPPED (widget)) + gail_label_init_text_util (gail_label, widget); + else + g_signal_connect (widget, + "map", + G_CALLBACK (gail_label_map_gtk), + gail_label); + + /* + * Check whether ancestor of GtkLabel is a GtkButton and if so + * set accessible parent for GailLabel + */ + while (widget != NULL) + { + widget = gtk_widget_get_parent (widget); + if (GTK_IS_BUTTON (widget)) + { + atk_object_set_parent (obj, gtk_widget_get_accessible (widget)); + break; + } + } + + if (GTK_IS_ACCEL_LABEL (widget)) + obj->role = ATK_ROLE_ACCEL_LABEL; + else + obj->role = ATK_ROLE_LABEL; +} + +static void +gail_label_map_gtk (GtkWidget *widget, + gpointer data) +{ + GailLabel *gail_label; + + gail_label = GAIL_LABEL (data); + gail_label_init_text_util (gail_label, widget); +} + +static void +gail_label_init_text_util (GailLabel *gail_label, + GtkWidget *widget) +{ + GtkLabel *label; + const gchar *label_text; + + if (gail_label->textutil == NULL) + gail_label->textutil = gail_text_util_new (); + + label = GTK_LABEL (widget); + label_text = gtk_label_get_text (label); + gail_text_util_text_setup (gail_label->textutil, label_text); + + if (label_text == NULL) + gail_label->label_length = 0; + else + gail_label->label_length = g_utf8_strlen (label_text, -1); +} + +AtkObject* +gail_label_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_LABEL (widget), NULL); + + object = g_object_new (GAIL_TYPE_LABEL, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +notify_name_change (AtkObject *atk_obj) +{ + GtkLabel *label; + GailLabel *gail_label; + GtkWidget *widget; + GObject *gail_obj; + + widget = GTK_ACCESSIBLE (atk_obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return; + + gail_obj = G_OBJECT (atk_obj); + label = GTK_LABEL (widget); + gail_label = GAIL_LABEL (atk_obj); + + if (gail_label->textutil == NULL) + return; + + /* + * Check whether the label has actually changed before emitting + * notification. + */ + if (gail_label->textutil->buffer) + { + GtkTextIter start, end; + const char *new_label; + char *old_label; + int same; + + gtk_text_buffer_get_start_iter (gail_label->textutil->buffer, &start); + gtk_text_buffer_get_end_iter (gail_label->textutil->buffer, &end); + old_label = gtk_text_buffer_get_text (gail_label->textutil->buffer, &start, &end, FALSE); + new_label = gtk_label_get_text (label); + same = strcmp (new_label, old_label); + g_free (old_label); + if (same == 0) + return; + } + + /* Create a delete text and an insert text signal */ + + g_signal_emit_by_name (gail_obj, "text_changed::delete", 0, + gail_label->label_length); + + gail_label_init_text_util (gail_label, widget); + + g_signal_emit_by_name (gail_obj, "text_changed::insert", 0, + gail_label->label_length); + + if (atk_obj->name == NULL) + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (gail_obj, "accessible-name"); + + g_signal_emit_by_name (gail_obj, "visible_data_changed"); +} + +static void +window_created (GObject *obj, + gpointer data) +{ + g_return_if_fail (GAIL_LABEL (data)); + + notify_name_change (ATK_OBJECT (data)); +} + +static void +gail_label_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget = GTK_WIDGET (obj); + AtkObject* atk_obj = gtk_widget_get_accessible (widget); + GtkLabel *label; + GailLabel *gail_label; + GObject *gail_obj; + AtkObject *top_level; + AtkObject *temp_obj; + + gail_label = GAIL_LABEL (atk_obj); + + if (strcmp (pspec->name, "label") == 0) + { + /* + * We may get a label change for a label which is not attached to an + * application. We wait until the toplevel window is created before + * emitting the notification. + * + * This happens when [Ctrl+]Alt+Tab is pressed in metacity + */ + if (!gail_label->has_top_level) + { + temp_obj = atk_obj; + top_level = NULL; + while (temp_obj) + { + top_level = temp_obj; + temp_obj = atk_object_get_parent (top_level); + } + if (atk_object_get_role (top_level) != ATK_ROLE_APPLICATION) + { + if (gail_label->window_create_handler == 0 && + GAIL_IS_WINDOW (top_level)) + gail_label->window_create_handler = g_signal_connect_after (top_level, "create", G_CALLBACK (window_created), atk_obj); + } + else + gail_label->has_top_level = TRUE; + } + if (gail_label->has_top_level) + notify_name_change (atk_obj); + } + else if (strcmp (pspec->name, "cursor-position") == 0) + { + gint start, end; + gboolean text_caret_moved = FALSE; + gboolean selection_changed = FALSE; + gboolean is_start = TRUE; + + gail_obj = G_OBJECT (atk_obj); + label = GTK_LABEL (widget); + + if (gtk_label_get_selection_bounds (label, &start, &end)) + { + if (start != gail_label->cursor_position || + end != gail_label->selection_bound) + { + if (end != gail_label->selection_bound) + is_start = FALSE; + gail_label->selection_bound = end; + gail_label->cursor_position = start; + text_caret_moved = TRUE; + if (start != end) + selection_changed = TRUE; + } + } + else + { + if (gail_label->cursor_position != gail_label->selection_bound) + selection_changed = TRUE; + if (gtk_label_get_selectable (label)) + { + if (gail_label->cursor_position != -1 && start != gail_label->cursor_position) + text_caret_moved = TRUE; + if (gail_label->selection_bound != -1 && end != gail_label->selection_bound) + { + text_caret_moved = TRUE; + is_start = FALSE; + } + gail_label->cursor_position = start; + gail_label->selection_bound = end; + } + else + { + /* GtkLabel has become non selectable */ + + gail_label->cursor_position = 0; + gail_label->selection_bound = 0; + text_caret_moved = TRUE; + } + + } + if (text_caret_moved) + g_signal_emit_by_name (gail_obj, "text_caret_moved", + is_start ? gail_label->cursor_position : gail_label->selection_bound); + if (selection_changed) + g_signal_emit_by_name (gail_obj, "text_selection_changed"); + + } + else + parent_class->notify_gtk (obj, pspec); +} + +static void +gail_label_finalize (GObject *object) +{ + GailLabel *label = GAIL_LABEL (object); + + if (label->textutil) + g_object_unref (label->textutil); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +/* atkobject.h */ + +static AtkStateSet* +gail_label_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE); + + return state_set; +} + +AtkRelationSet* +gail_label_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + + g_return_val_if_fail (GAIL_IS_LABEL (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR)) + { + /* + * Get the mnemonic widget + * + * The relation set is not updated if the mnemonic widget is changed + */ + GtkWidget *mnemonic_widget = GTK_LABEL (widget)->mnemonic_widget; + + if (mnemonic_widget) + { + AtkObject *accessible_array[1]; + AtkRelation* relation; + + if (!GTK_WIDGET_CAN_FOCUS (mnemonic_widget)) + { + /* + * Handle the case where a GtkFileChooserButton is specified as the + * mnemonic widget. use the combobox which is a child of the + * GtkFileChooserButton as the mnemonic widget. See bug #359843. + */ + if (GTK_IS_BOX (mnemonic_widget)) + { + GList *list, *tmpl; + + list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget)); + if (g_list_length (list) == 2) + { + tmpl = g_list_last (list); + if (GTK_IS_COMBO_BOX(tmpl->data)) + { + mnemonic_widget = GTK_WIDGET(tmpl->data); + } + } + g_list_free (list); + } + /* + * Handle the case where a GnomeIconEntry is specified as the + * mnemonic widget. use the button which is a grandchild of the + * GnomeIconEntry as the mnemonic widget. See bug #133967. + */ + else if (GTK_IS_BOX (mnemonic_widget)) + { + GList *list; + + list = gtk_container_get_children (GTK_CONTAINER (mnemonic_widget)); + if (g_list_length (list) == 1) + { + if (GTK_IS_ALIGNMENT (list->data)) + { + GtkWidget *temp_widget; + + temp_widget = GTK_BIN (list->data)->child; + if (GTK_IS_BUTTON (temp_widget)) + mnemonic_widget = temp_widget; + } + else if (GTK_IS_HBOX (list->data)) + { + GtkWidget *temp_widget; + + temp_widget = GTK_WIDGET (list->data); + g_list_free (list); + list = gtk_container_get_children (GTK_CONTAINER (temp_widget)); + if (GTK_IS_COMBO (list->data)) + { + mnemonic_widget = GTK_WIDGET (list->data); + } + } + } + g_list_free (list); + } + } + accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget); + relation = atk_relation_new (accessible_array, 1, + ATK_RELATION_LABEL_FOR); + atk_relation_set_add (relation_set, relation); + /* + * Unref the relation so that it is not leaked. + */ + g_object_unref (relation); + } + } + return relation_set; +} + +static G_CONST_RETURN gchar* +gail_label_get_name (AtkObject *accessible) +{ + G_CONST_RETURN gchar *name; + + g_return_val_if_fail (GAIL_IS_LABEL (accessible), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (accessible); + if (name != NULL) + return name; + else + { + /* + * Get the text on the label + */ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (accessible)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_LABEL (widget), NULL); + + return gtk_label_get_text (GTK_LABEL (widget)); + } +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_label_get_text; + iface->get_character_at_offset = gail_label_get_character_at_offset; + iface->get_text_before_offset = gail_label_get_text_before_offset; + iface->get_text_at_offset = gail_label_get_text_at_offset; + iface->get_text_after_offset = gail_label_get_text_after_offset; + iface->get_character_count = gail_label_get_character_count; + iface->get_caret_offset = gail_label_get_caret_offset; + iface->set_caret_offset = gail_label_set_caret_offset; + iface->get_n_selections = gail_label_get_n_selections; + iface->get_selection = gail_label_get_selection; + iface->add_selection = gail_label_add_selection; + iface->remove_selection = gail_label_remove_selection; + iface->set_selection = gail_label_set_selection; + iface->get_character_extents = gail_label_get_character_extents; + iface->get_offset_at_point = gail_label_get_offset_at_point; + iface->get_run_attributes = gail_label_get_run_attributes; + iface->get_default_attributes = gail_label_get_default_attributes; +} + +static gchar* +gail_label_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkLabel *label; + + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = GTK_LABEL (widget); + + label_text = gtk_label_get_text (label); + + if (label_text == NULL) + return NULL; + else + { + if (GAIL_LABEL (text)->textutil == NULL) + gail_label_init_text_util (GAIL_LABEL (text), widget); + return gail_text_util_get_substring (GAIL_LABEL(text)->textutil, + start_pos, end_pos); + } +} + +static gchar* +gail_label_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = GTK_LABEL (widget); + + return gail_text_util_get_text (GAIL_LABEL (text)->textutil, + gtk_label_get_layout (label), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_label_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = GTK_LABEL (widget); + + return gail_text_util_get_text (GAIL_LABEL (text)->textutil, + gtk_label_get_layout (label), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_label_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + { + /* State is defunct */ + return NULL; + } + + /* Get label */ + label = GTK_LABEL (widget); + + return gail_text_util_get_text (GAIL_LABEL (text)->textutil, + gtk_label_get_layout (label), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_label_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = GTK_LABEL (widget); + return g_utf8_strlen (gtk_label_get_text (label), -1); +} + +static gint +gail_label_get_caret_offset (AtkText *text) +{ + return GAIL_LABEL (text)->cursor_position; +} + +static gboolean +gail_label_set_caret_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = GTK_LABEL (widget); + + if (gtk_label_get_selectable (label) && + offset >= 0 && + offset <= g_utf8_strlen (label->text, -1)) + { + gtk_label_select_region (label, offset, offset); + return TRUE; + } + else + return FALSE; +} + +static gint +gail_label_get_n_selections (AtkText *text) +{ + GtkWidget *widget; + GtkLabel *label; + gint start, end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = GTK_LABEL (widget); + + if (!gtk_label_get_selectable (label)) + return 0; + + if (gtk_label_get_selection_bounds (label, &start, &end)) + return 1; + else + return 0; +} + +static gchar* +gail_label_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + GtkWidget *widget; + GtkLabel *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = GTK_LABEL (widget); + + /* Only let the user get the selection if one is set, and if the + * selection_num is 0. + */ + if (!gtk_label_get_selectable( label) || selection_num != 0) + return NULL; + + if (gtk_label_get_selection_bounds (label, start_pos, end_pos)) + { + const gchar* label_text = gtk_label_get_text (label); + + if (label_text == NULL) + return 0; + else + return gail_text_util_get_substring (GAIL_LABEL (text)->textutil, + *start_pos, *end_pos); + } + else + return NULL; +} + +static gboolean +gail_label_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkLabel *label; + gint start, end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + label = GTK_LABEL (widget); + + if (!gtk_label_get_selectable (label)) + return FALSE; + + if (! gtk_label_get_selection_bounds (label, &start, &end)) + { + gtk_label_select_region (label, start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_label_remove_selection (AtkText *text, + gint selection_num) +{ + GtkWidget *widget; + GtkLabel *label; + gint start, end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (selection_num != 0) + return FALSE; + + label = GTK_LABEL (widget); + + if (!gtk_label_get_selectable (label)) + return FALSE; + + if (gtk_label_get_selection_bounds (label, &start, &end)) + { + gtk_label_select_region (label, 0, 0); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_label_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkLabel *label; + gint start, end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (selection_num != 0) + return FALSE; + + label = GTK_LABEL (widget); + + if (!gtk_label_get_selectable (label)) + return FALSE; + + if (gtk_label_get_selection_bounds (label, &start, &end)) + { + gtk_label_select_region (label, start_pos, end_pos); + return TRUE; + } + else + return FALSE; +} + +static void +gail_label_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkLabel *label; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + label = GTK_LABEL (widget); + + gtk_label_get_layout_offsets (label, &x_layout, &y_layout); + index = g_utf8_offset_to_pointer (label->text, offset) - label->text; + pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_label_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkLabel *label; + gint index, x_layout, y_layout; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + label = GTK_LABEL (widget); + + gtk_label_get_layout_offsets (label, &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (widget, + gtk_label_get_layout (label), + x_layout, y_layout, x, y, coords); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label->text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label->text, label->text + index); +} + +static AtkAttributeSet* +gail_label_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkLabel *label; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = GTK_LABEL (widget); + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (label); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (widget); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (label), + label->text, + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_label_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkLabel *label; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = GTK_LABEL (widget); + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (label), + widget); + return at_set; +} + +static gunichar +gail_label_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkLabel *label; + const gchar *string; + gchar *index; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + label = GTK_LABEL (widget); + string = gtk_label_get_text (label); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} diff --git a/modules/other/gail/gaillabel.h b/modules/other/gail/gaillabel.h new file mode 100644 index 000000000..3c927c44a --- /dev/null +++ b/modules/other/gail/gaillabel.h @@ -0,0 +1,67 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_LABEL_H__ +#define __GAIL_LABEL_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_LABEL (gail_label_get_type ()) +#define GAIL_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_LABEL, GailLabel)) +#define GAIL_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_LABEL, GailLabelClass)) +#define GAIL_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_LABEL)) +#define GAIL_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_LABEL)) +#define GAIL_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_LABEL, GailLabelClass)) + +typedef struct _GailLabel GailLabel; +typedef struct _GailLabelClass GailLabelClass; + +struct _GailLabel +{ + GailWidget parent; + + GailTextUtil *textutil; + gint cursor_position; + gint selection_bound; + gint label_length; + guint window_create_handler; + gboolean has_top_level; +}; + +GType gail_label_get_type (void); + +struct _GailLabelClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_label_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_LABEL_H__ */ diff --git a/modules/other/gail/gaillist.c b/modules/other/gail/gaillist.c new file mode 100644 index 000000000..c5d40485e --- /dev/null +++ b/modules/other/gail/gaillist.c @@ -0,0 +1,277 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gaillist.h" +#include "gailcombo.h" + +static void gail_list_class_init (GailListClass *klass); + +static gint gail_list_get_index_in_parent (AtkObject *accessible); + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_list_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_list_clear_selection (AtkSelection *selection); +static AtkObject* gail_list_ref_selection (AtkSelection *selection, + gint i); +static gint gail_list_get_selection_count (AtkSelection *selection); +static gboolean gail_list_is_child_selected (AtkSelection *selection, + gint i); +static gboolean gail_list_remove_selection (AtkSelection *selection, + gint i); + + +static GailContainerClass *parent_class = NULL; + +GType +gail_list_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailListClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_list_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailList), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailList", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + + + } + return type; +} + +static void +gail_list_class_init (GailListClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->get_index_in_parent = gail_list_get_index_in_parent; +} + +AtkObject* +gail_list_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_LIST (widget), NULL); + + object = g_object_new (GAIL_TYPE_LIST, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_LIST; + + return accessible; +} + +static gint +gail_list_get_index_in_parent (AtkObject *accessible) +{ + /* + * If the parent widget is a combo box then the index is 0 + * otherwise do the normal thing. + */ + if (accessible->accessible_parent) + { + if (GAIL_IS_COMBO (accessible->accessible_parent)) + return 0; + } + return ATK_OBJECT_CLASS (parent_class)->get_index_in_parent (accessible); +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_list_add_selection; + iface->clear_selection = gail_list_clear_selection; + iface->ref_selection = gail_list_ref_selection; + iface->get_selection_count = gail_list_get_selection_count; + iface->is_child_selected = gail_list_is_child_selected; + iface->remove_selection = gail_list_remove_selection; + /* + * select_all_selection does not make sense for a combo box + * so no implementation is provided. + */ +} + +static gboolean +gail_list_add_selection (AtkSelection *selection, + gint i) +{ + GtkList *list; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + list = GTK_LIST (widget); + + gtk_list_select_item (list, i); + return TRUE; +} + +static gboolean +gail_list_clear_selection (AtkSelection *selection) +{ + GtkList *list; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + list = GTK_LIST (widget); + + gtk_list_unselect_all (list); + return TRUE; +} + +static AtkObject* +gail_list_ref_selection (AtkSelection *selection, + gint i) +{ + GtkList *list; + GList *g_list; + GtkWidget *item; + AtkObject *obj; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + list = GTK_LIST (widget); + + /* + * A combo box can have only one selection. + */ + if (i != 0) + return NULL; + + g_list = list->selection; + + if (g_list == NULL) + return NULL; + + item = GTK_WIDGET (g_list->data); + + obj = gtk_widget_get_accessible (item); + g_object_ref (obj); + return obj; +} + +static gint +gail_list_get_selection_count (AtkSelection *selection) +{ + GtkList *list; + GList *g_list; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + list = GTK_LIST (widget); + + g_list = list->selection; + + return g_list_length (g_list);; +} + +static gboolean +gail_list_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkList *list; + GList *g_list; + GtkWidget *item; + gint j; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + list = GTK_LIST (widget); + + g_list = list->selection; + + if (g_list == NULL) + return FALSE; + + item = GTK_WIDGET (g_list->data); + + j = g_list_index (list->children, item); + + return (j == i); +} + +static gboolean +gail_list_remove_selection (AtkSelection *selection, + gint i) +{ + if (atk_selection_is_child_selected (selection, i)) + atk_selection_clear_selection (selection); + + return TRUE; +} diff --git a/modules/other/gail/gaillist.h b/modules/other/gail/gaillist.h new file mode 100644 index 000000000..cd57874bb --- /dev/null +++ b/modules/other/gail/gaillist.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_LIST_H__ +#define __GAIL_LIST_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_LIST (gail_list_get_type ()) +#define GAIL_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_LIST, GailList)) +#define GAIL_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_LIST, GailListClass)) +#define GAIL_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_LIST)) +#define GAIL_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_LIST)) +#define GAIL_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_LIST, GailListClass)) + +typedef struct _GailList GailList; +typedef struct _GailListClass GailListClass; + +struct _GailList +{ + GailContainer parent; +}; + +GType gail_list_get_type (void); + +struct _GailListClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_list_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_LIST_H__ */ diff --git a/modules/other/gail/gailmenu.c b/modules/other/gail/gailmenu.c new file mode 100644 index 000000000..f86c859cd --- /dev/null +++ b/modules/other/gail/gailmenu.c @@ -0,0 +1,167 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenuitem.h> +#include <gtk/gtkoptionmenu.h> +#include <gtk/gtkcombobox.h> +#include "gailmenu.h" + +static void gail_menu_class_init (GailMenuClass *klass); + +static void gail_menu_real_initialize (AtkObject *obj, + gpointer data); + +static AtkObject* gail_menu_get_parent (AtkObject *accessible); +static gint gail_menu_get_index_in_parent (AtkObject *accessible); + +static GailMenuShell *parent_class = NULL; + +GType +gail_menu_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailMenuClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_menu_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailMenu), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_MENU_SHELL, + "GailMenu", &tinfo, 0); + } + + return type; +} + +static void +gail_menu_class_init (GailMenuClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_parent = gail_menu_get_parent; + class->get_index_in_parent = gail_menu_get_index_in_parent; + class->initialize = gail_menu_real_initialize; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_menu_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_MENU (widget), NULL); + + object = g_object_new (GAIL_TYPE_MENU, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + g_object_set_data (G_OBJECT (accessible), "atk-component-layer", + GINT_TO_POINTER (ATK_LAYER_POPUP)); + return accessible; +} + +static void +gail_menu_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_MENU; +} + +static AtkObject* +gail_menu_get_parent (AtkObject *accessible) +{ + AtkObject *parent; + + parent = accessible->accessible_parent; + + if (parent != NULL) + { + g_return_val_if_fail (ATK_IS_OBJECT (parent), NULL); + } + else + { + GtkWidget *widget, *parent_widget; + + widget = GTK_ACCESSIBLE (accessible)->widget; + if (widget == NULL) + { + /* + * State is defunct + */ + return NULL; + } + g_return_val_if_fail (GTK_IS_MENU (widget), NULL); + + /* + * If the menu is attached to a menu item or a button (Gnome Menu) + * report the menu item as parent. + */ + parent_widget = gtk_menu_get_attach_widget (GTK_MENU (widget)); + + if (!GTK_IS_MENU_ITEM (parent_widget) && !GTK_IS_BUTTON (parent_widget) && !GTK_IS_COMBO_BOX (parent_widget) && !GTK_IS_OPTION_MENU (parent_widget)) + parent_widget = widget->parent; + + if (parent_widget == NULL) + return NULL; + + parent = gtk_widget_get_accessible (parent_widget); + atk_object_set_parent (accessible, parent); + } + return parent; +} + +static gint +gail_menu_get_index_in_parent (AtkObject *accessible) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + { + /* + * State is defunct + */ + return -1; + } + g_return_val_if_fail (GTK_IS_MENU (widget), -1); + + if (gtk_menu_get_attach_widget (GTK_MENU (widget))) + { + return 0; + } + return ATK_OBJECT_CLASS (parent_class)->get_index_in_parent (accessible); +} diff --git a/modules/other/gail/gailmenu.h b/modules/other/gail/gailmenu.h new file mode 100644 index 000000000..5c1c5f9e7 --- /dev/null +++ b/modules/other/gail/gailmenu.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_MENU_H__ +#define __GAIL_MENU_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailmenushell.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_MENU (gail_menu_get_type ()) +#define GAIL_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_MENU_SHELL, GailMenu)) +#define GAIL_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_MENU, GailMenuClass)) +#define GAIL_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_MENU)) +#define GAIL_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_MENU)) +#define GAIL_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_MENU, GailMenuClass)) + +typedef struct _GailMenu GailMenu; +typedef struct _GailMenuClass GailMenuClass; + +struct _GailMenu +{ + GailMenuShell parent; +}; + +GType gail_menu_get_type (void); + +struct _GailMenuClass +{ + GailMenuShellClass parent_class; +}; + +AtkObject* gail_menu_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_MENU_H__ */ diff --git a/modules/other/gail/gailmenuitem.c b/modules/other/gail/gailmenuitem.c new file mode 100644 index 000000000..0425cae42 --- /dev/null +++ b/modules/other/gail/gailmenuitem.c @@ -0,0 +1,682 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailmenuitem.h" +#include "gailsubmenuitem.h" + +#define KEYBINDING_SEPARATOR ";" + +static void gail_menu_item_class_init (GailMenuItemClass *klass); +static void gail_menu_item_object_init (GailMenuItem *menu_item); + +static void gail_menu_item_real_initialize + (AtkObject *obj, + gpointer data); +static gint gail_menu_item_get_n_children (AtkObject *obj); +static AtkObject* gail_menu_item_ref_child (AtkObject *obj, + gint i); +static void gail_menu_item_finalize (GObject *object); + +static void atk_action_interface_init (AtkActionIface *iface); +static gboolean gail_menu_item_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_menu_item_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* gail_menu_item_get_description(AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_menu_item_get_name (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_menu_item_get_keybinding (AtkAction *action, + gint i); +static gboolean gail_menu_item_set_description(AtkAction *action, + gint i, + const gchar *desc); +static void menu_item_select (GtkItem *item); +static void menu_item_deselect (GtkItem *item); +static void menu_item_selection (GtkItem *item, + gboolean selected); +static gboolean find_accel (GtkAccelKey *key, + GClosure *closure, + gpointer data); +static gboolean find_accel_new (GtkAccelKey *key, + GClosure *closure, + gpointer data); + +static gpointer parent_class = NULL; + +GType +gail_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_menu_item_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_ITEM, + "GailMenuItem", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + } + return type; +} + +static void +gail_menu_item_class_init (GailMenuItemClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_menu_item_finalize; + + class->get_n_children = gail_menu_item_get_n_children; + class->ref_child = gail_menu_item_ref_child; + class->initialize = gail_menu_item_real_initialize; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gail_menu_item_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkWidget *widget; + GtkWidget *parent; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + g_signal_connect (data, + "select", + G_CALLBACK (menu_item_select), + NULL); + g_signal_connect (data, + "deselect", + G_CALLBACK (menu_item_deselect), + NULL); + widget = GTK_WIDGET (data); + parent = gtk_widget_get_parent (widget); + if (GTK_IS_MENU (parent)) + { + GtkWidget *parent_widget; + + parent_widget = gtk_menu_get_attach_widget (GTK_MENU (parent)); + + if (!GTK_IS_MENU_ITEM (parent_widget)) + parent_widget = gtk_widget_get_parent (widget); + if (parent_widget) + { + atk_object_set_parent (obj, gtk_widget_get_accessible (parent_widget)); + } + } + g_object_set_data (G_OBJECT (obj), "atk-component-layer", + GINT_TO_POINTER (ATK_LAYER_POPUP)); + + if (GTK_IS_TEAROFF_MENU_ITEM (data)) + obj->role = ATK_ROLE_TEAR_OFF_MENU_ITEM; + else if (GTK_IS_SEPARATOR_MENU_ITEM (data)) + obj->role = ATK_ROLE_SEPARATOR; + else + obj->role = ATK_ROLE_MENU_ITEM; +} + +static void +gail_menu_item_object_init (GailMenuItem *menu_item) +{ + menu_item->click_keybinding = NULL; + menu_item->click_description = NULL; +} + +AtkObject* +gail_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), NULL); + + if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget))) + return gail_sub_menu_item_new (widget); + + object = g_object_new (GAIL_TYPE_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +GList * +get_children (GtkWidget *submenu) +{ + GList *children; + + children = gtk_container_get_children (GTK_CONTAINER (submenu)); + if (g_list_length (children) == 0) + { + /* + * If menu is empty it may be because the menu items are created only + * on demand. For example, in gnome-panel the menu items are created + * only when "show" signal is emitted on the menu. + * + * The following hack forces the menu items to be created. + */ + if (!GTK_WIDGET_VISIBLE (submenu)) + { + GTK_WIDGET_SET_FLAGS (submenu, GTK_VISIBLE); + g_signal_emit_by_name (submenu, "show"); + GTK_WIDGET_UNSET_FLAGS (submenu, GTK_VISIBLE); + } + g_list_free (children); + children = gtk_container_get_children (GTK_CONTAINER (submenu)); + } + return children; +} + +/* + * If a menu item has a submenu return the items of the submenu as the + * accessible children; otherwise expose no accessible children. + */ +static gint +gail_menu_item_get_n_children (AtkObject* obj) +{ + GtkWidget *widget; + GtkWidget *submenu; + gint count = 0; + + g_return_val_if_fail (GAIL_IS_MENU_ITEM (obj), count); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return count; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + if (submenu) + { + GList *children; + + children = get_children (submenu); + count = g_list_length (children); + g_list_free (children); + } + return count; +} + +static AtkObject* +gail_menu_item_ref_child (AtkObject *obj, + gint i) +{ + AtkObject *accessible; + GtkWidget *widget; + GtkWidget *submenu; + + g_return_val_if_fail (GAIL_IS_MENU_ITEM (obj), NULL); + g_return_val_if_fail ((i >= 0), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return NULL; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + if (submenu) + { + GList *children; + GList *tmp_list; + + children = get_children (submenu); + tmp_list = g_list_nth (children, i); + if (!tmp_list) + { + g_list_free (children); + return NULL; + } + accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data)); + g_list_free (children); + g_object_ref (accessible); + } + else + accessible = NULL; + + return accessible; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_menu_item_do_action; + iface->get_n_actions = gail_menu_item_get_n_actions; + iface->get_description = gail_menu_item_get_description; + iface->get_name = gail_menu_item_get_name; + iface->get_keybinding = gail_menu_item_get_keybinding; + iface->set_description = gail_menu_item_set_description; +} + +static gboolean +gail_menu_item_do_action (AtkAction *action, + gint i) +{ + if (i == 0) + { + GtkWidget *item; + GailMenuItem *gail_menu_item; + + item = GTK_ACCESSIBLE (action)->widget; + if (item == NULL) + /* State is defunct */ + return FALSE; + + if (!GTK_WIDGET_SENSITIVE (item) || !GTK_WIDGET_VISIBLE (item)) + return FALSE; + + gail_menu_item = GAIL_MENU_ITEM (action); + if (gail_menu_item->action_idle_handler) + return FALSE; + else + { + g_object_ref (gail_menu_item); + gail_menu_item->action_idle_handler = g_idle_add (idle_do_action, gail_menu_item); + } + return TRUE; + } + else + return FALSE; +} + +static void +ensure_menus_unposted (GailMenuItem *menu_item) +{ + AtkObject *parent; + GtkWidget *widget; + + parent = atk_object_get_parent (ATK_OBJECT (menu_item)); + while (parent) + { + if (GTK_IS_ACCESSIBLE (parent)) + { + widget = GTK_ACCESSIBLE (parent)->widget; + if (GTK_IS_MENU (widget)) + { + if (GTK_WIDGET_MAPPED (widget)) + gtk_menu_shell_cancel (GTK_MENU_SHELL (widget)); + + return; + } + } + parent = atk_object_get_parent (parent); + } +} + +static gboolean +idle_do_action (gpointer data) +{ + GtkWidget *item; + GtkWidget *item_parent; + GailMenuItem *menu_item; + gboolean item_mapped; + + GDK_THREADS_ENTER (); + + menu_item = GAIL_MENU_ITEM (data); + menu_item->action_idle_handler = 0; + item = GTK_ACCESSIBLE (menu_item)->widget; + if (item == NULL /* State is defunct */ || + !GTK_WIDGET_SENSITIVE (item) || !GTK_WIDGET_VISIBLE (item)) + { + g_object_unref (menu_item); + GDK_THREADS_LEAVE (); + return FALSE; + } + + item_parent = gtk_widget_get_parent (item); + gtk_menu_shell_select_item (GTK_MENU_SHELL (item_parent), item); + item_mapped = GTK_WIDGET_MAPPED (item); + /* + * This is what is called when <Return> is pressed for a menu item + */ + g_signal_emit_by_name (item_parent, "activate_current", + /*force_hide*/ 1); + if (!item_mapped) + ensure_menus_unposted (menu_item); + + g_object_unref (menu_item); + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_menu_item_get_n_actions (AtkAction *action) +{ + /* + * Menu item has 1 action + */ + return 1; +} + +static G_CONST_RETURN gchar* +gail_menu_item_get_description (AtkAction *action, + gint i) +{ + if (i == 0) + { + GailMenuItem *item; + + item = GAIL_MENU_ITEM (action); + return item->click_description; + } + else + return NULL; +} + +static G_CONST_RETURN gchar* +gail_menu_item_get_name (AtkAction *action, + gint i) +{ + if (i == 0) + return "click"; + else + return NULL; +} + +static G_CONST_RETURN gchar* +gail_menu_item_get_keybinding (AtkAction *action, + gint i) +{ + /* + * This function returns a string of the form A;B;C where + * A is the keybinding for the widget; B is the keybinding to traverse + * from the menubar and C is the accelerator. + * The items in the keybinding to traverse from the menubar are separated + * by ":". + */ + GailMenuItem *gail_menu_item; + gchar *keybinding = NULL; + gchar *item_keybinding = NULL; + gchar *full_keybinding = NULL; + gchar *accelerator = NULL; + + gail_menu_item = GAIL_MENU_ITEM (action); + if (i == 0) + { + GtkWidget *item; + GtkWidget *temp_item; + GtkWidget *child; + GtkWidget *parent; + + item = GTK_ACCESSIBLE (action)->widget; + if (item == NULL) + /* State is defunct */ + return NULL; + + temp_item = item; + while (TRUE) + { + GdkModifierType mnemonic_modifier = 0; + guint key_val; + gchar *key, *temp_keybinding; + + child = gtk_bin_get_child (GTK_BIN (temp_item)); + if (child == NULL) + { + /* Possibly a tear off menu item; it could also be a menu + * separator generated by gtk_item_factory_create_items() + */ + return NULL; + } + parent = gtk_widget_get_parent (temp_item); + if (!parent) + { + /* + * parent can be NULL when activating a window from the panel + */ + return NULL; + } + g_return_val_if_fail (GTK_IS_MENU_SHELL (parent), NULL); + if (GTK_IS_MENU_BAR (parent)) + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (parent); + if (toplevel && GTK_IS_WINDOW (toplevel)) + mnemonic_modifier = gtk_window_get_mnemonic_modifier ( + GTK_WINDOW (toplevel)); + } + if (GTK_IS_LABEL (child)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (child)); + if (key_val != GDK_VoidSymbol) + { + key = gtk_accelerator_name (key_val, mnemonic_modifier); + if (full_keybinding) + temp_keybinding = g_strconcat (key, ":", full_keybinding, NULL); + else + temp_keybinding = g_strconcat (key, NULL); + if (temp_item == item) + { + item_keybinding = g_strdup (key); + } + g_free (key); + g_free (full_keybinding); + full_keybinding = temp_keybinding; + } + else + { + /* No keybinding */ + g_free (full_keybinding); + full_keybinding = NULL; + break; + } + } + if (GTK_IS_MENU_BAR (parent)) + /* We have reached the menu bar so we are finished */ + break; + g_return_val_if_fail (GTK_IS_MENU (parent), NULL); + temp_item = gtk_menu_get_attach_widget (GTK_MENU (parent)); + if (!GTK_IS_MENU_ITEM (temp_item)) + { + /* + * Menu is attached to something other than a menu item; + * probably an option menu + */ + g_free (full_keybinding); + full_keybinding = NULL; + break; + } + } + + parent = gtk_widget_get_parent (item); + if (GTK_IS_MENU (parent)) + { + GtkAccelGroup *group; + GtkAccelKey *key; + + group = gtk_menu_get_accel_group (GTK_MENU (parent)); + + if (group) + { + key = gtk_accel_group_find (group, find_accel, item); + } + else + { + /* + * If the menu item is created using GtkAction and GtkUIManager + * we get here. + */ + key = NULL; + child = GTK_BIN (item)->child; + if (GTK_IS_ACCEL_LABEL (child)) + { + GtkAccelLabel *accel_label; + + accel_label = GTK_ACCEL_LABEL (child); + if (accel_label->accel_closure) + { + key = gtk_accel_group_find (accel_label->accel_group, + find_accel_new, + accel_label->accel_closure); + } + + } + } + + if (key) + { + accelerator = gtk_accelerator_name (key->accel_key, + key->accel_mods); + } + } + } + /* + * Concatenate the bindings + */ + if (item_keybinding || full_keybinding || accelerator) + { + gchar *temp; + if (item_keybinding) + { + keybinding = g_strconcat (item_keybinding, KEYBINDING_SEPARATOR, NULL); + g_free (item_keybinding); + } + else + keybinding = g_strconcat (KEYBINDING_SEPARATOR, NULL); + + if (full_keybinding) + { + temp = g_strconcat (keybinding, full_keybinding, + KEYBINDING_SEPARATOR, NULL); + g_free (full_keybinding); + } + else + temp = g_strconcat (keybinding, KEYBINDING_SEPARATOR, NULL); + + g_free (keybinding); + keybinding = temp; + if (accelerator) + { + temp = g_strconcat (keybinding, accelerator, NULL); + g_free (accelerator); + g_free (keybinding); + keybinding = temp; + } + } + g_free (gail_menu_item->click_keybinding); + gail_menu_item->click_keybinding = keybinding; + return keybinding; +} + +static gboolean +gail_menu_item_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + if (i == 0) + { + GailMenuItem *item; + + item = GAIL_MENU_ITEM (action); + g_free (item->click_description); + item->click_description = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} + +static void +gail_menu_item_finalize (GObject *object) +{ + GailMenuItem *menu_item = GAIL_MENU_ITEM (object); + + g_free (menu_item->click_keybinding); + g_free (menu_item->click_description); + if (menu_item->action_idle_handler) + { + g_source_remove (menu_item->action_idle_handler); + menu_item->action_idle_handler = 0; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +menu_item_select (GtkItem *item) +{ + menu_item_selection (item, TRUE); +} + +static void +menu_item_deselect (GtkItem *item) +{ + menu_item_selection (item, FALSE); +} + +static void +menu_item_selection (GtkItem *item, + gboolean selected) +{ + AtkObject *obj, *parent; + + obj = gtk_widget_get_accessible (GTK_WIDGET (item)); + atk_object_notify_state_change (obj, ATK_STATE_SELECTED, selected); + + parent = atk_object_get_parent (obj); + g_signal_emit_by_name (parent, "selection_changed"); +} + +static gboolean +find_accel (GtkAccelKey *key, + GClosure *closure, + gpointer data) +{ + /* + * We assume that closure->data points to the widget + * pending gtk_widget_get_accel_closures being made public + */ + return data == (gpointer) closure->data; +} + +static gboolean +find_accel_new (GtkAccelKey *key, + GClosure *closure, + gpointer data) +{ + return data == (gpointer) closure; +} diff --git a/modules/other/gail/gailmenuitem.h b/modules/other/gail/gailmenuitem.h new file mode 100644 index 000000000..a7e0f6472 --- /dev/null +++ b/modules/other/gail/gailmenuitem.h @@ -0,0 +1,64 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_MENU_ITEM_H__ +#define __GAIL_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailitem.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_MENU_ITEM (gail_menu_item_get_type ()) +#define GAIL_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_MENU_ITEM, GailMenuItem)) +#define GAIL_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_MENU_ITEM, GailMenuItemClass)) +#define GAIL_IS_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_MENU_ITEM)) +#define GAIL_IS_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_MENU_ITEM)) +#define GAIL_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_MENU_ITEM, GailMenuItemClass)) + +typedef struct _GailMenuItem GailMenuItem; +typedef struct _GailMenuItemClass GailMenuItemClass; + +struct _GailMenuItem +{ + GailItem parent; + + gchar *click_keybinding; + gchar *click_description; + guint action_idle_handler; +}; + +GType gail_menu_item_get_type (void); + +struct _GailMenuItemClass +{ + GailItemClass parent_class; +}; + +AtkObject* gail_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailmenushell.c b/modules/other/gail/gailmenushell.c new file mode 100644 index 000000000..53ae7c502 --- /dev/null +++ b/modules/other/gail/gailmenushell.c @@ -0,0 +1,281 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailmenushell.h" + +static void gail_menu_shell_class_init (GailMenuShellClass *klass); + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_menu_shell_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_menu_shell_clear_selection (AtkSelection *selection); +static AtkObject* gail_menu_shell_ref_selection (AtkSelection *selection, + gint i); +static gint gail_menu_shell_get_selection_count (AtkSelection *selection); +static gboolean gail_menu_shell_is_child_selected (AtkSelection *selection, + gint i); +static gboolean gail_menu_shell_remove_selection (AtkSelection *selection, + gint i); + +GType +gail_menu_shell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailMenuShellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_menu_shell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailMenuShell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailMenuShell", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + } + + return type; +} + +static void +gail_menu_shell_class_init (GailMenuShellClass *klass) +{ +} + +AtkObject* +gail_menu_shell_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), NULL); + + object = g_object_new (GAIL_TYPE_MENU_SHELL, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + if (GTK_IS_MENU_BAR (widget)) + accessible->role = ATK_ROLE_MENU_BAR; + else + /* + * Accessible object for Menu is created in gailmenu.c + */ + accessible->role = ATK_ROLE_UNKNOWN; + + return accessible; +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_menu_shell_add_selection; + iface->clear_selection = gail_menu_shell_clear_selection; + iface->ref_selection = gail_menu_shell_ref_selection; + iface->get_selection_count = gail_menu_shell_get_selection_count; + iface->is_child_selected = gail_menu_shell_is_child_selected; + iface->remove_selection = gail_menu_shell_remove_selection; + /* + * select_all_selection does not make sense for a menu_shell + * so no implementation is provided. + */ +} + +static gboolean +gail_menu_shell_add_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + GList *item; + guint length; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return FALSE; + } + + shell = GTK_MENU_SHELL (widget); + length = g_list_length (shell->children); + if (i < 0 || i > length) + return FALSE; + + item = g_list_nth (shell->children, i); + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU_ITEM(item->data), FALSE); + + gtk_menu_shell_select_item (shell, GTK_WIDGET (item->data)); + return TRUE; +} + +static gboolean +gail_menu_shell_clear_selection (AtkSelection *selection) +{ + GtkMenuShell *shell; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return FALSE; + } + + shell = GTK_MENU_SHELL (widget); + + gtk_menu_shell_deselect (shell); + return TRUE; +} + +static AtkObject* +gail_menu_shell_ref_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + AtkObject *obj; + GtkWidget *widget; + + if (i != 0) + return NULL; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return NULL; + } + + shell = GTK_MENU_SHELL (widget); + + if (shell->active_menu_item != NULL) + { + obj = gtk_widget_get_accessible (shell->active_menu_item); + g_object_ref (obj); + return obj; + } + else + { + return NULL; + } +} + +static gint +gail_menu_shell_get_selection_count (AtkSelection *selection) +{ + GtkMenuShell *shell; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return 0; + } + + shell = GTK_MENU_SHELL (widget); + + /* + * Identifies the currently selected menu item + */ + if (shell->active_menu_item == NULL) + { + return 0; + } + else + { + return 1; + } +} + +static gboolean +gail_menu_shell_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + gint j; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return FALSE; + } + + shell = GTK_MENU_SHELL (widget); + if (shell->active_menu_item == NULL) + return FALSE; + + j = g_list_index (shell->children, shell->active_menu_item); + + return (j==i); +} + +static gboolean +gail_menu_shell_remove_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + GtkWidget *widget; + + if (i != 0) + return FALSE; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + { + /* State is defunct */ + return FALSE; + } + + shell = GTK_MENU_SHELL (widget); + + if (shell->active_menu_item && + GTK_MENU_ITEM (shell->active_menu_item)->submenu) + { + /* + * Menu item contains a menu and it is the selected menu item + * so deselect it. + */ + gtk_menu_shell_deselect (shell); + } + return TRUE; +} diff --git a/modules/other/gail/gailmenushell.h b/modules/other/gail/gailmenushell.h new file mode 100644 index 000000000..a44aa884e --- /dev/null +++ b/modules/other/gail/gailmenushell.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_MENU_SHELL_H__ +#define __GAIL_MENU_SHELL_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_MENU_SHELL (gail_menu_shell_get_type ()) +#define GAIL_MENU_SHELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_MENU_SHELL, GailMenuShell)) +#define GAIL_MENU_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_MENU_SHELL, GailMenuShellClass)) +#define GAIL_IS_MENU_SHELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_MENU_SHELL)) +#define GAIL_IS_MENU_SHELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_MENU_SHELL)) +#define GAIL_MENU_SHELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_MENU_SHELL, GailMenuShellClass)) + +typedef struct _GailMenuShell GailMenuShell; +typedef struct _GailMenuShellClass GailMenuShellClass; + +struct _GailMenuShell +{ + GailContainer parent; +}; + +GType gail_menu_shell_get_type (void); + +struct _GailMenuShellClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_menu_shell_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_MENU_SHELL_H__ */ diff --git a/modules/other/gail/gailnotebook.c b/modules/other/gail/gailnotebook.c new file mode 100644 index 000000000..1e60ac44a --- /dev/null +++ b/modules/other/gail/gailnotebook.c @@ -0,0 +1,655 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailnotebook.h" +#include "gailnotebookpage.h" +#include "gail-private-macros.h" + +static void gail_notebook_class_init (GailNotebookClass *klass); +static void gail_notebook_object_init (GailNotebook *notebook); +static void gail_notebook_finalize (GObject *object); +static void gail_notebook_real_initialize (AtkObject *obj, + gpointer data); + +static void gail_notebook_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static AtkObject* gail_notebook_ref_child (AtkObject *obj, + gint i); +static gint gail_notebook_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_notebook_add_selection (AtkSelection *selection, + gint i); +static AtkObject* gail_notebook_ref_selection (AtkSelection *selection, + gint i); +static gint gail_notebook_get_selection_count (AtkSelection *selection); +static gboolean gail_notebook_is_child_selected (AtkSelection *selection, + gint i); +static AtkObject* find_child_in_list (GList *list, + gint index); +static void check_cache (GailNotebook *gail_notebook, + GtkNotebook *notebook); +static void reset_cache (GailNotebook *gail_notebook, + gint index); +static void create_notebook_page_accessible (GailNotebook *gail_notebook, + GtkNotebook *notebook, + gint index, + gboolean insert_before, + GList *list); +static void gail_notebook_child_parent_set (GtkWidget *widget, + GtkWidget *old_parent, + gpointer data); +static gboolean gail_notebook_focus_cb (GtkWidget *widget, + GtkDirectionType type); +static gboolean gail_notebook_check_focus_tab (gpointer data); +static void gail_notebook_destroyed (gpointer data); + +static gpointer parent_class = NULL; + +GType +gail_notebook_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailNotebookClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_notebook_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailNotebook), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_notebook_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailNotebook", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + } + return type; +} + +static void +gail_notebook_class_init (GailNotebookClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + GailContainerClass *container_class; + + widget_class = (GailWidgetClass*)klass; + container_class = (GailContainerClass*)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_notebook_finalize; + + widget_class->notify_gtk = gail_notebook_real_notify_gtk; + + class->ref_child = gail_notebook_ref_child; + class->initialize = gail_notebook_real_initialize; + /* + * We do not provide an implementation of get_n_children + * as the implementation in GailContainer returns the correct + * number of children. + */ + container_class->remove_gtk = gail_notebook_real_remove_gtk; +} + +static void +gail_notebook_object_init (GailNotebook *notebook) +{ + notebook->page_cache = NULL; + notebook->selected_page = -1; + notebook->focus_tab_page = -1; + notebook->remove_index = -1; + notebook->idle_focus_id = 0; +} + +AtkObject* +gail_notebook_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), NULL); + + object = g_object_new (GAIL_TYPE_NOTEBOOK, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static AtkObject* +gail_notebook_ref_child (AtkObject *obj, + gint i) +{ + AtkObject *accessible = NULL; + GailNotebook *gail_notebook; + GtkNotebook *gtk_notebook; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + gail_notebook = GAIL_NOTEBOOK (obj); + + gtk_notebook = GTK_NOTEBOOK (widget); + + if (gail_notebook->page_count < g_list_length (gtk_notebook->children)) + check_cache (gail_notebook, gtk_notebook); + + accessible = find_child_in_list (gail_notebook->page_cache, i); + + if (accessible != NULL) + g_object_ref (accessible); + + return accessible; +} + +static void +gail_notebook_page_added (GtkNotebook *gtk_notebook, + GtkWidget *child, + guint page_num, + gpointer data) +{ + AtkObject *atk_obj; + GailNotebook *notebook; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (gtk_notebook)); + notebook = GAIL_NOTEBOOK (atk_obj); + create_notebook_page_accessible (notebook, gtk_notebook, page_num, FALSE, NULL); +} + +static void +gail_notebook_real_initialize (AtkObject *obj, + gpointer data) +{ + GailNotebook *notebook; + GtkNotebook *gtk_notebook; + gint i; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + notebook = GAIL_NOTEBOOK (obj); + gtk_notebook = GTK_NOTEBOOK (data); + for (i = 0; i < g_list_length (gtk_notebook->children); i++) + { + create_notebook_page_accessible (notebook, gtk_notebook, i, FALSE, NULL); + } + notebook->page_count = i; + notebook->selected_page = gtk_notebook_get_current_page (gtk_notebook); + if (gtk_notebook->focus_tab && gtk_notebook->focus_tab->data) + { + notebook->focus_tab_page = g_list_index (gtk_notebook->children, gtk_notebook->focus_tab->data); + } + g_signal_connect (gtk_notebook, + "focus", + G_CALLBACK (gail_notebook_focus_cb), + NULL); + g_signal_connect (gtk_notebook, + "page-added", + G_CALLBACK (gail_notebook_page_added), + NULL); + g_object_weak_ref (G_OBJECT(gtk_notebook), + (GWeakNotify) gail_notebook_destroyed, + obj); + + obj->role = ATK_ROLE_PAGE_TAB_LIST; +} + +static void +gail_notebook_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget; + AtkObject* atk_obj; + + widget = GTK_WIDGET (obj); + atk_obj = gtk_widget_get_accessible (widget); + + if (strcmp (pspec->name, "page") == 0) + { + gint page_num, old_page_num; + gint focus_page_num = 0; + gint old_focus_page_num; + GailNotebook *gail_notebook; + GtkNotebook *gtk_notebook; + + gail_notebook = GAIL_NOTEBOOK (atk_obj); + gtk_notebook = GTK_NOTEBOOK (widget); + + if (gail_notebook->page_count < g_list_length (gtk_notebook->children)) + check_cache (gail_notebook, gtk_notebook); + /* + * Notify SELECTED state change for old and new page + */ + old_page_num = gail_notebook->selected_page; + page_num = gtk_notebook_get_current_page (gtk_notebook); + gail_notebook->selected_page = page_num; + old_focus_page_num = gail_notebook->focus_tab_page; + if (gtk_notebook->focus_tab && gtk_notebook->focus_tab->data) + { + focus_page_num = g_list_index (gtk_notebook->children, gtk_notebook->focus_tab->data); + gail_notebook->focus_tab_page = focus_page_num; + } + + if (page_num != old_page_num) + { + AtkObject *obj; + + if (old_page_num != -1) + { + obj = gail_notebook_ref_child (atk_obj, old_page_num); + if (obj) + { + atk_object_notify_state_change (obj, + ATK_STATE_SELECTED, + FALSE); + g_object_unref (obj); + } + } + obj = gail_notebook_ref_child (atk_obj, page_num); + if (obj) + { + atk_object_notify_state_change (obj, + ATK_STATE_SELECTED, + TRUE); + g_object_unref (obj); + /* + * The page which is being displayed has changed but there is + * no need to tell the focus tracker as the focus page will also + * change or a widget in the page will receive focus if the + * Notebook does not have tabs. + */ + } + g_signal_emit_by_name (atk_obj, "selection_changed"); + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } + if (gtk_notebook_get_show_tabs (gtk_notebook) && + (focus_page_num != old_focus_page_num)) + { + if (gail_notebook->idle_focus_id) + g_source_remove (gail_notebook->idle_focus_id); + gail_notebook->idle_focus_id = g_idle_add (gail_notebook_check_focus_tab, atk_obj); + } + } + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} + +static void +gail_notebook_finalize (GObject *object) +{ + GailNotebook *notebook = GAIL_NOTEBOOK (object); + GList *list; + + /* + * Get rid of the GailNotebookPage objects which we have cached. + */ + list = notebook->page_cache; + if (list != NULL) + { + while (list) + { + g_object_unref (list->data); + list = list->next; + } + } + + g_list_free (notebook->page_cache); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_notebook_add_selection; + iface->ref_selection = gail_notebook_ref_selection; + iface->get_selection_count = gail_notebook_get_selection_count; + iface->is_child_selected = gail_notebook_is_child_selected; + /* + * The following don't make any sense for GtkNotebook widgets. + * Unsupported AtkSelection interfaces: + * clear_selection(); + * remove_selection(); + * select_all_selection(); + */ +} + +/* + * GtkNotebook only supports the selection of one page at a time. + * Selecting a page unselects any previous selection, so this + * changes the current selection instead of adding to it. + */ +static gboolean +gail_notebook_add_selection (AtkSelection *selection, + gint i) +{ + GtkNotebook *notebook; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + notebook = GTK_NOTEBOOK (widget); + gtk_notebook_set_current_page (notebook, i); + return TRUE; +} + +static AtkObject* +gail_notebook_ref_selection (AtkSelection *selection, + gint i) +{ + AtkObject *accessible; + GtkWidget *widget; + GtkNotebook *notebook; + gint pagenum; + + /* + * A note book can have only one selection. + */ + gail_return_val_if_fail (i == 0, NULL); + g_return_val_if_fail (GAIL_IS_NOTEBOOK (selection), NULL); + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + notebook = GTK_NOTEBOOK (widget); + pagenum = gtk_notebook_get_current_page (notebook); + gail_return_val_if_fail (pagenum != -1, NULL); + accessible = gail_notebook_ref_child (ATK_OBJECT (selection), pagenum); + + return accessible; +} + +/* + * Always return 1 because there can only be one page + * selected at any time + */ +static gint +gail_notebook_get_selection_count (AtkSelection *selection) +{ + GtkWidget *widget; + GtkNotebook *notebook; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + notebook = GTK_NOTEBOOK (widget); + if (notebook == NULL) + return 0; + else + return 1; +} + +static gboolean +gail_notebook_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkWidget *widget; + GtkNotebook *notebook; + gint pagenumber; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + + notebook = GTK_NOTEBOOK (widget); + pagenumber = gtk_notebook_get_current_page(notebook); + + if (pagenumber == i) + return TRUE; + else + return FALSE; +} + +static AtkObject* +find_child_in_list (GList *list, + gint index) +{ + AtkObject *obj = NULL; + + while (list) + { + if (GAIL_NOTEBOOK_PAGE (list->data)->index == index) + { + obj = ATK_OBJECT (list->data); + break; + } + list = list->next; + } + return obj; +} + +static void +check_cache (GailNotebook *gail_notebook, + GtkNotebook *notebook) +{ + GList *gtk_list; + GList *gail_list; + gint i; + + gtk_list = notebook->children; + gail_list = gail_notebook->page_cache; + + i = 0; + while (gtk_list) + { + if (!gail_list) + { + create_notebook_page_accessible (gail_notebook, notebook, i, FALSE, NULL); + } + else if (GAIL_NOTEBOOK_PAGE (gail_list->data)->page != gtk_list->data) + { + create_notebook_page_accessible (gail_notebook, notebook, i, TRUE, gail_list); + } + else + { + gail_list = gail_list->next; + } + i++; + gtk_list = gtk_list->next; + } + gail_notebook->page_count = i; +} + +static void +reset_cache (GailNotebook *gail_notebook, + gint index) +{ + GList *l; + + for (l = gail_notebook->page_cache; l; l = l->next) + { + if (GAIL_NOTEBOOK_PAGE (l->data)->index > index) + GAIL_NOTEBOOK_PAGE (l->data)->index -= 1; + } +} + +static void +create_notebook_page_accessible (GailNotebook *gail_notebook, + GtkNotebook *notebook, + gint index, + gboolean insert_before, + GList *list) +{ + AtkObject *obj; + + obj = gail_notebook_page_new (notebook, index); + g_object_ref (obj); + if (insert_before) + gail_notebook->page_cache = g_list_insert_before (gail_notebook->page_cache, list, obj); + else + gail_notebook->page_cache = g_list_append (gail_notebook->page_cache, obj); + g_signal_connect (gtk_notebook_get_nth_page (notebook, index), + "parent_set", + G_CALLBACK (gail_notebook_child_parent_set), + obj); +} + +static void +gail_notebook_child_parent_set (GtkWidget *widget, + GtkWidget *old_parent, + gpointer data) +{ + GailNotebook *gail_notebook; + + gail_return_if_fail (old_parent != NULL); + gail_notebook = GAIL_NOTEBOOK (gtk_widget_get_accessible (old_parent)); + gail_notebook->remove_index = GAIL_NOTEBOOK_PAGE (data)->index; +} + +static gint +gail_notebook_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + GailNotebook *gail_notebook; + AtkObject *obj; + gint index; + + g_return_val_if_fail (container != NULL, 1); + gail_notebook = GAIL_NOTEBOOK (gtk_widget_get_accessible (GTK_WIDGET (container))); + index = gail_notebook->remove_index; + gail_notebook->remove_index = -1; + + obj = find_child_in_list (gail_notebook->page_cache, index); + g_return_val_if_fail (obj, 1); + gail_notebook->page_cache = g_list_remove (gail_notebook->page_cache, obj); + gail_notebook->page_count -= 1; + reset_cache (gail_notebook, index); + g_signal_emit_by_name (gail_notebook, + "children_changed::remove", + GAIL_NOTEBOOK_PAGE (obj)->index, + obj, NULL); + g_object_unref (obj); + return 1; +} + +static gboolean +gail_notebook_focus_cb (GtkWidget *widget, + GtkDirectionType type) +{ + AtkObject *atk_obj = gtk_widget_get_accessible (widget); + GailNotebook *gail_notebook = GAIL_NOTEBOOK (atk_obj); + + switch (type) + { + case GTK_DIR_LEFT: + case GTK_DIR_RIGHT: + if (gail_notebook->idle_focus_id) + g_source_remove (gail_notebook->idle_focus_id); + gail_notebook->idle_focus_id = g_idle_add (gail_notebook_check_focus_tab, atk_obj); + break; + default: + break; + } + return FALSE; +} + +static gboolean +gail_notebook_check_focus_tab (gpointer data) +{ + GtkWidget *widget; + AtkObject *atk_obj; + gint focus_page_num, old_focus_page_num; + GailNotebook *gail_notebook; + GtkNotebook *gtk_notebook; + + GDK_THREADS_ENTER (); + + atk_obj = ATK_OBJECT (data); + gail_notebook = GAIL_NOTEBOOK (atk_obj); + widget = GTK_ACCESSIBLE (atk_obj)->widget; + + gtk_notebook = GTK_NOTEBOOK (widget); + + gail_notebook->idle_focus_id = 0; + + if (!gtk_notebook->focus_tab) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + old_focus_page_num = gail_notebook->focus_tab_page; + focus_page_num = g_list_index (gtk_notebook->children, gtk_notebook->focus_tab->data); + gail_notebook->focus_tab_page = focus_page_num; + if (old_focus_page_num != focus_page_num) + { + AtkObject *obj; + + obj = atk_object_ref_accessible_child (atk_obj, focus_page_num); + atk_focus_tracker_notify (obj); + g_object_unref (obj); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +gail_notebook_destroyed (gpointer data) +{ + GailNotebook *gail_notebook = GAIL_NOTEBOOK (data); + + if (gail_notebook->idle_focus_id) + g_source_remove (gail_notebook->idle_focus_id); +} diff --git a/modules/other/gail/gailnotebook.h b/modules/other/gail/gailnotebook.h new file mode 100644 index 000000000..ab17ff1c3 --- /dev/null +++ b/modules/other/gail/gailnotebook.h @@ -0,0 +1,73 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_NOTEBOOK_H__ +#define __GAIL_NOTEBOOK_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_NOTEBOOK (gail_notebook_get_type ()) +#define GAIL_NOTEBOOK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_NOTEBOOK, GailNotebook)) +#define GAIL_NOTEBOOK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_NOTEBOOK, GailNotebookClass)) +#define GAIL_IS_NOTEBOOK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_NOTEBOOK)) +#define GAIL_IS_NOTEBOOK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_NOTEBOOK)) +#define GAIL_NOTEBOOK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_NOTEBOOK, GailNotebookClass)) + +typedef struct _GailNotebook GailNotebook; +typedef struct _GailNotebookClass GailNotebookClass; + +struct _GailNotebook +{ + GailContainer parent; + + /* + * page_cache maintains a list of pre-ref'd Notebook Pages. + * This cache is queried by gail_notebook_ref_child(). + * If the page is found in the list then a new page does not + * need to be created + */ + GList* page_cache; + gint selected_page; + gint focus_tab_page; + gint page_count; + guint idle_focus_id; + + gint remove_index; +}; + +GType gail_notebook_get_type (void); + +struct _GailNotebookClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_notebook_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_NOTEBOOK_H__ */ diff --git a/modules/other/gail/gailnotebookpage.c b/modules/other/gail/gailnotebookpage.c new file mode 100644 index 000000000..b830f912e --- /dev/null +++ b/modules/other/gail/gailnotebookpage.c @@ -0,0 +1,868 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailnotebookpage.h" +#include <libgail-util/gailmisc.h> +#include "gail-private-macros.h" + +static void gail_notebook_page_class_init (GailNotebookPageClass *klass); + +static void gail_notebook_page_finalize (GObject *object); +static void gail_notebook_page_label_map_gtk (GtkWidget *widget, + gpointer data); + +static G_CONST_RETURN gchar* gail_notebook_page_get_name (AtkObject *accessible); +static AtkObject* gail_notebook_page_get_parent (AtkObject *accessible); +static gint gail_notebook_page_get_n_children (AtkObject *accessible); +static AtkObject* gail_notebook_page_ref_child (AtkObject *accessible, + gint i); +static gint gail_notebook_page_get_index_in_parent + (AtkObject *accessible); +static AtkStateSet* gail_notebook_page_ref_state_set (AtkObject *accessible); + +static gint gail_notebook_page_notify (GObject *obj, + GParamSpec *pspec, + gpointer user_data); +static void gail_notebook_page_init_textutil (GailNotebookPage *notebook_page, + GtkWidget *label); + +static void atk_component_interface_init (AtkComponentIface *iface); + +static AtkObject* gail_notebook_page_ref_accessible_at_point + (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type); + +static void gail_notebook_page_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); + +static AtkObject* _gail_notebook_page_get_tab_label (GailNotebookPage *page); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_notebook_page_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_notebook_page_get_character_at_offset + (AtkText *text, + gint offset); +static gchar* gail_notebook_page_get_text_before_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_notebook_page_get_text_at_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_notebook_page_get_text_after_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_notebook_page_get_character_count (AtkText *text); +static void gail_notebook_page_get_character_extents + (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_notebook_page_get_offset_at_point + (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_notebook_page_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_notebook_page_get_default_attributes + (AtkText *text); +static GtkWidget* get_label_from_notebook_page (GailNotebookPage *page); +static GtkWidget* find_label_child (GtkContainer *container); + +static gpointer parent_class = NULL; + +GType +gail_notebook_page_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailNotebookPageClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_notebook_page_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailNotebookPage), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) atk_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (ATK_TYPE_OBJECT, + "GailNotebookPage", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + return type; +} + +static void +gail_notebook_page_class_init (GailNotebookPageClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_notebook_page_get_name; + class->get_parent = gail_notebook_page_get_parent; + class->get_n_children = gail_notebook_page_get_n_children; + class->ref_child = gail_notebook_page_ref_child; + class->ref_state_set = gail_notebook_page_ref_state_set; + class->get_index_in_parent = gail_notebook_page_get_index_in_parent; + + gobject_class->finalize = gail_notebook_page_finalize; +} + +static gint +notify_child_added (gpointer data) +{ + GailNotebookPage *page; + AtkObject *atk_object, *atk_parent; + + GDK_THREADS_ENTER (); + + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (data), FALSE); + page = GAIL_NOTEBOOK_PAGE (data); + atk_object = ATK_OBJECT (data); + + /* The widget page->notebook may be deleted before this handler is called */ + if (page->notebook != NULL) + { + atk_parent = gtk_widget_get_accessible (GTK_WIDGET (page->notebook)); + atk_object_set_parent (atk_object, atk_parent); + g_signal_emit_by_name (atk_parent, "children_changed::add", page->index, atk_object, NULL); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +AtkObject* +gail_notebook_page_new (GtkNotebook *notebook, + gint pagenum) +{ + GObject *object; + AtkObject *atk_object; + GailNotebookPage *page; + GtkWidget *child; + GtkWidget *label; + GList *list; + + g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL); + + child = gtk_notebook_get_nth_page (notebook, pagenum); + + if (!child) + return NULL; + + object = g_object_new (GAIL_TYPE_NOTEBOOK_PAGE, NULL); + g_return_val_if_fail (object != NULL, NULL); + + page = GAIL_NOTEBOOK_PAGE (object); + page->notebook = notebook; + g_object_add_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook); + page->index = pagenum; + list = g_list_nth (notebook->children, pagenum); + page->page = list->data; + page->textutil = NULL; + + atk_object = ATK_OBJECT (page); + atk_object->role = ATK_ROLE_PAGE_TAB; + atk_object->layer = ATK_LAYER_WIDGET; + + g_idle_add (notify_child_added, atk_object); + /* + * We get notified of changes to the label + */ + label = get_label_from_notebook_page (page); + if (GTK_IS_LABEL (label)) + { + if (GTK_WIDGET_MAPPED (label)) + gail_notebook_page_init_textutil (page, label); + else + g_signal_connect (label, + "map", + G_CALLBACK (gail_notebook_page_label_map_gtk), + page); + } + + return atk_object; +} + +static void +gail_notebook_page_label_map_gtk (GtkWidget *widget, + gpointer data) +{ + GailNotebookPage *page; + + page = GAIL_NOTEBOOK_PAGE (data); + gail_notebook_page_init_textutil (page, widget); +} + +static void +gail_notebook_page_init_textutil (GailNotebookPage *page, + GtkWidget *label) +{ + const gchar *label_text; + + if (page->textutil == NULL) + { + page->textutil = gail_text_util_new (); + g_signal_connect (label, + "notify", + (GCallback) gail_notebook_page_notify, + page); + } + label_text = gtk_label_get_text (GTK_LABEL (label)); + gail_text_util_text_setup (page->textutil, label_text); +} + +static gint +gail_notebook_page_notify (GObject *obj, + GParamSpec *pspec, + gpointer user_data) +{ + AtkObject *atk_obj = ATK_OBJECT (user_data); + GtkLabel *label; + GailNotebookPage *page; + + if (strcmp (pspec->name, "label") == 0) + { + const gchar* label_text; + + label = GTK_LABEL (obj); + + label_text = gtk_label_get_text (label); + + page = GAIL_NOTEBOOK_PAGE (atk_obj); + gail_text_util_text_setup (page->textutil, label_text); + + if (atk_obj->name == NULL) + { + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (atk_obj), "accessible-name"); + } + /* + * The label is the only property which can be changed + */ + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } + return 1; +} + +static void +gail_notebook_page_finalize (GObject *object) +{ + GailNotebookPage *page = GAIL_NOTEBOOK_PAGE (object); + + if (page->notebook) + g_object_remove_weak_pointer (G_OBJECT (page->notebook), (gpointer *)&page->notebook); + + if (page->textutil) + g_object_unref (page->textutil); + + G_OBJECT_CLASS (parent_class)->finalize (object); + +} + +static G_CONST_RETURN gchar* +gail_notebook_page_get_name (AtkObject *accessible) +{ + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL); + + if (accessible->name != NULL) + return accessible->name; + else + { + GtkWidget *label; + + label = get_label_from_notebook_page (GAIL_NOTEBOOK_PAGE (accessible)); + if (GTK_IS_LABEL (label)) + return gtk_label_get_text (GTK_LABEL (label)); + else + return NULL; + } +} + +static AtkObject* +gail_notebook_page_get_parent (AtkObject *accessible) +{ + GailNotebookPage *page; + + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL); + + page = GAIL_NOTEBOOK_PAGE (accessible); + + if (!page->notebook) + return NULL; + + return gtk_widget_get_accessible (GTK_WIDGET (page->notebook)); +} + +static gint +gail_notebook_page_get_n_children (AtkObject *accessible) +{ + /* Notebook page has only one child */ + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), 0); + + return 1; +} + +static AtkObject* +gail_notebook_page_ref_child (AtkObject *accessible, + gint i) +{ + GtkWidget *child; + AtkObject *child_obj; + GailNotebookPage *page = NULL; + + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), NULL); + if (i != 0) + return NULL; + + page = GAIL_NOTEBOOK_PAGE (accessible); + if (!page->notebook) + return NULL; + + child = gtk_notebook_get_nth_page (page->notebook, page->index); + gail_return_val_if_fail (GTK_IS_WIDGET (child), NULL); + + child_obj = gtk_widget_get_accessible (child); + g_object_ref (child_obj); + return child_obj; +} + +static gint +gail_notebook_page_get_index_in_parent (AtkObject *accessible) +{ + GailNotebookPage *page; + + g_return_val_if_fail (GAIL_IS_NOTEBOOK_PAGE (accessible), -1); + page = GAIL_NOTEBOOK_PAGE (accessible); + + return page->index; +} + +static AtkStateSet* +gail_notebook_page_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set, *label_state_set, *merged_state_set; + AtkObject *atk_label; + + g_return_val_if_fail (GAIL_NOTEBOOK_PAGE (accessible), NULL); + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + + atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (accessible)); + if (atk_label) + { + label_state_set = atk_object_ref_state_set (atk_label); + merged_state_set = atk_state_set_or_sets (state_set, label_state_set); + g_object_unref (label_state_set); + g_object_unref (state_set); + } + else + { + AtkObject *child; + + child = atk_object_ref_accessible_child (accessible, 0); + gail_return_val_if_fail (child, state_set); + + merged_state_set = state_set; + state_set = atk_object_ref_state_set (child); + if (atk_state_set_contains_state (state_set, ATK_STATE_VISIBLE)) + { + atk_state_set_add_state (merged_state_set, ATK_STATE_VISIBLE); + if (atk_state_set_contains_state (state_set, ATK_STATE_ENABLED)) + atk_state_set_add_state (merged_state_set, ATK_STATE_ENABLED); + if (atk_state_set_contains_state (state_set, ATK_STATE_SHOWING)) + atk_state_set_add_state (merged_state_set, ATK_STATE_SHOWING); + + } + g_object_unref (state_set); + g_object_unref (child); + } + return merged_state_set; +} + + +static void +atk_component_interface_init (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + /* + * We use the default implementations for contains, get_position, get_size + */ + iface->ref_accessible_at_point = gail_notebook_page_ref_accessible_at_point; + iface->get_extents = gail_notebook_page_get_extents; +} + +static AtkObject* +gail_notebook_page_ref_accessible_at_point (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + /* + * There is only one child so we return it. + */ + AtkObject* child; + + g_return_val_if_fail (ATK_IS_OBJECT (component), NULL); + + child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0); + return child; +} + +static void +gail_notebook_page_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + AtkObject *atk_label; + + g_return_if_fail (GAIL_IS_NOTEBOOK_PAGE (component)); + + atk_label = _gail_notebook_page_get_tab_label (GAIL_NOTEBOOK_PAGE (component)); + + if (!atk_label) + { + AtkObject *child; + + *width = 0; + *height = 0; + + child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0); + gail_return_if_fail (child); + + atk_component_get_position (ATK_COMPONENT (child), x, y, coord_type); + g_object_unref (child); + } + else + { + atk_component_get_extents (ATK_COMPONENT (atk_label), + x, y, width, height, coord_type); + } + return; +} + +static AtkObject* +_gail_notebook_page_get_tab_label (GailNotebookPage *page) +{ + GtkWidget *label; + + label = get_label_from_notebook_page (page); + if (label) + return gtk_widget_get_accessible (label); + else + return NULL; +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_notebook_page_get_text; + iface->get_character_at_offset = gail_notebook_page_get_character_at_offset; + iface->get_text_before_offset = gail_notebook_page_get_text_before_offset; + iface->get_text_at_offset = gail_notebook_page_get_text_at_offset; + iface->get_text_after_offset = gail_notebook_page_get_text_after_offset; + iface->get_character_count = gail_notebook_page_get_character_count; + iface->get_character_extents = gail_notebook_page_get_character_extents; + iface->get_offset_at_point = gail_notebook_page_get_offset_at_point; + iface->get_run_attributes = gail_notebook_page_get_run_attributes; + iface->get_default_attributes = gail_notebook_page_get_default_attributes; +} + +static gchar* +gail_notebook_page_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + const gchar *label_text; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL (label)) + return NULL; + + if (!notebook_page->textutil) + gail_notebook_page_init_textutil (notebook_page, label); + + label_text = gtk_label_get_text (GTK_LABEL (label)); + + if (label_text == NULL) + return NULL; + else + { + return gail_text_util_get_substring (notebook_page->textutil, + start_pos, end_pos); + } +} + +static gchar* +gail_notebook_page_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return NULL; + + if (!notebook_page->textutil) + gail_notebook_page_init_textutil (notebook_page, label); + + return gail_text_util_get_text (notebook_page->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_notebook_page_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return NULL; + + if (!notebook_page->textutil) + gail_notebook_page_init_textutil (notebook_page, label); + + return gail_text_util_get_text (notebook_page->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_notebook_page_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return NULL; + + if (!notebook_page->textutil) + gail_notebook_page_init_textutil (notebook_page, label); + + return gail_text_util_get_text (notebook_page->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_notebook_page_get_character_count (AtkText *text) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return 0; + + return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1); +} + +static void +gail_notebook_page_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + const gchar *label_text; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + label_text = gtk_label_get_text (GTK_LABEL (label)); + index = g_utf8_offset_to_pointer (label_text, offset) - label_text; + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (label, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_notebook_page_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + gint index, x_layout, y_layout; + const gchar *label_text; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return -1; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (label, + gtk_label_get_layout (GTK_LABEL (label)), + x_layout, y_layout, x, y, coords); + label_text = gtk_label_get_text (GTK_LABEL (label)); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label_text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label_text, label_text + index); +} + +static AtkAttributeSet* +gail_notebook_page_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return NULL; + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (GTK_LABEL (label)); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (label); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + (gchar *) gtk_label_get_text (GTK_LABEL (label)), + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_notebook_page_get_default_attributes (AtkText *text) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + AtkAttributeSet *at_set = NULL; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return NULL; + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + label); + return at_set; +} + +static gunichar +gail_notebook_page_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *label; + GailNotebookPage *notebook_page; + const gchar *string; + gchar *index; + + notebook_page = GAIL_NOTEBOOK_PAGE (text); + label = get_label_from_notebook_page (notebook_page); + + if (!GTK_IS_LABEL(label)) + return '\0'; + string = gtk_label_get_text (GTK_LABEL (label)); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + +static GtkWidget* +get_label_from_notebook_page (GailNotebookPage *page) +{ + GtkWidget *child; + GtkNotebook *notebook; + + notebook = page->notebook; + if (!notebook) + return NULL; + + if (!gtk_notebook_get_show_tabs (notebook)) + return NULL; + + child = gtk_notebook_get_nth_page (notebook, page->index); + if (child == NULL) return NULL; + g_return_val_if_fail (GTK_IS_WIDGET (child), NULL); + + child = gtk_notebook_get_tab_label (notebook, child); + + if (GTK_IS_LABEL (child)) + return child; + + if (GTK_IS_CONTAINER (child)) + child = find_label_child (GTK_CONTAINER (child)); + + return child; +} + +static GtkWidget* +find_label_child (GtkContainer *container) +{ + GList *children, *tmp_list; + GtkWidget *child; + + children = gtk_container_get_children (container); + + child = NULL; + for (tmp_list = children; tmp_list != NULL; tmp_list = tmp_list->next) + { + if (GTK_IS_LABEL (tmp_list->data)) + { + child = GTK_WIDGET (tmp_list->data); + break; + } + else if (GTK_IS_CONTAINER (tmp_list->data)) + { + child = find_label_child (GTK_CONTAINER (tmp_list->data)); + if (child) + break; + } + } + g_list_free (children); + return child; +} diff --git a/modules/other/gail/gailnotebookpage.h b/modules/other/gail/gailnotebookpage.h new file mode 100644 index 000000000..50af79cfc --- /dev/null +++ b/modules/other/gail/gailnotebookpage.h @@ -0,0 +1,69 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_NOTEBOOK_PAGE_H__ +#define __GAIL_NOTEBOOK_PAGE_H__ + +#include <atk/atk.h> +#include <gtk/gtknotebook.h> +#include <gail/gailnotebook.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_NOTEBOOK_PAGE (gail_notebook_page_get_type ()) +#define GAIL_NOTEBOOK_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),GAIL_TYPE_NOTEBOOK_PAGE, GailNotebookPage)) +#define GAIL_NOTEBOOK_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_NOTEBOOK_PAGE, GailNotebookPageClass)) +#define GAIL_IS_NOTEBOOK_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_NOTEBOOK_PAGE)) +#define GAIL_IS_NOTEBOOK_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_NOTEBOOK_PAGE)) +#define GAIL_NOTEBOOK_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_NOTEBOOK_PAGE, GailNotebookPageClass)) + +typedef struct _GailNotebookPage GailNotebookPage; +typedef struct _GailNotebookPageClass GailNotebookPageClass; + +struct _GailNotebookPage +{ + AtkObject parent; + + GtkNotebook *notebook; + GtkNotebookPage *page; + + gint index; + + GailTextUtil *textutil; +}; + +GType gail_notebook_page_get_type (void); + +struct _GailNotebookPageClass +{ + AtkObjectClass parent_class; +}; + +AtkObject *gail_notebook_page_new(GtkNotebook *notebook, gint pagenum); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_NOTEBOOK_PAGE_H__ */ + diff --git a/modules/other/gail/gailobject.c b/modules/other/gail/gailobject.c new file mode 100644 index 000000000..2c6ceb475 --- /dev/null +++ b/modules/other/gail/gailobject.c @@ -0,0 +1,88 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailobject.h" + +static void gail_object_class_init (GailObjectClass *klass); + +static void gail_object_real_initialize (AtkObject *obj, + gpointer data); + +static AtkGObjectAccessibleClass *parent_class = NULL; + +GType +gail_object_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailObjectClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_object_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailObject), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (ATK_TYPE_GOBJECT_ACCESSIBLE, + "GailObject", &tinfo, 0); + } + + return type; +} + +AtkObject* +gail_object_new (GObject *obj) +{ + gpointer object; + AtkObject *atk_object; + + g_return_val_if_fail (GTK_IS_OBJECT (obj), NULL); + object = g_object_new (GAIL_TYPE_OBJECT, NULL); + atk_object = ATK_OBJECT (object); + atk_object_initialize (atk_object, obj); + return atk_object; +} + +static void +gail_object_class_init (GailObjectClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->initialize = gail_object_real_initialize; +} + +static void +gail_object_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_UNKNOWN; +} diff --git a/modules/other/gail/gailobject.h b/modules/other/gail/gailobject.h new file mode 100644 index 000000000..ffa36b3d1 --- /dev/null +++ b/modules/other/gail/gailobject.h @@ -0,0 +1,58 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_OBJECT_H__ +#define __GAIL_OBJECT_H__ + +#include <atk/atk.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_OBJECT (gail_object_get_type ()) +#define GAIL_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_OBJECT, GailObject) +#define GAIL_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_OBJECT, GailObjectlass)) +#define GAIL_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_OBJECT)) +#define GAIL_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_OBJECT)) +#define GAIL_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_OBJECT, GailObjectlass)) + +typedef struct _GailObject GailObject; +typedef struct _GailObjectClass GailObjectClass; + +struct _GailObject +{ + AtkGObjectAccessible parent; +}; + +GType gail_object_get_type (void); + +struct _GailObjectClass +{ + AtkGObjectAccessibleClass parent_class; +}; + +AtkObject* gail_object_new (GObject *obj); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_OBJECT_H__ */ diff --git a/modules/other/gail/gailobjectfactory.c b/modules/other/gail/gailobjectfactory.c new file mode 100644 index 000000000..c169baaea --- /dev/null +++ b/modules/other/gail/gailobjectfactory.c @@ -0,0 +1,77 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailobjectfactory.h" +#include "gailobject.h" + +static void gail_object_factory_class_init (GailObjectFactoryClass *klass); + +static AtkObject* gail_object_factory_create_accessible (GObject *obj); + +static GType gail_object_factory_get_accessible_type (void); + +GType +gail_object_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailObjectFactoryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_object_factory_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailObjectFactory), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + type = g_type_register_static ( + ATK_TYPE_OBJECT_FACTORY, + "GailObjectFactory" , &tinfo, 0); + } + + return type; +} + +static void +gail_object_factory_class_init (GailObjectFactoryClass *klass) +{ + AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass); + + class->create_accessible = gail_object_factory_create_accessible; + class->get_accessible_type = gail_object_factory_get_accessible_type; +} + +static AtkObject* +gail_object_factory_create_accessible (GObject *obj) +{ + return gail_object_new (obj); +} + +static GType +gail_object_factory_get_accessible_type (void) +{ + return GAIL_TYPE_OBJECT; +} diff --git a/modules/other/gail/gailobjectfactory.h b/modules/other/gail/gailobjectfactory.h new file mode 100644 index 000000000..5b8fef83a --- /dev/null +++ b/modules/other/gail/gailobjectfactory.h @@ -0,0 +1,58 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_OBJECT_FACTORY_H__ +#define __GAIL_OBJECT_FACTORY_H__ + +#include <atk/atkobjectfactory.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_OBJECT_FACTORY (gail_object_factory_get_type ()) +#define GAIL_OBJECT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_OBJECT_FACTORY, GailObjectFactory)) +#define GAIL_OBJECT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_OBJECT_FACTORY, GailObjectFactoryClass)) +#define GAIL_IS_OBJECT_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_OBJECT_FACTORY)) +#define GAIL_IS_OBJECT_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_OBJECT_FACTORY)) +#define GAIL_OBJECT_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_OBJECT_FACTORY, GailObjectFactoryClass)) + + +typedef struct _GailObjectFactory GailObjectFactory; +typedef struct _GailObjectFactoryClass GailObjectFactoryClass; + +struct _GailObjectFactory +{ + AtkObjectFactory parent; +}; + +struct _GailObjectFactoryClass +{ + AtkObjectFactoryClass parent_class; +}; + +GType gail_object_factory_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_OBJECT_FACTORY_H__ */ + diff --git a/modules/other/gail/gailoptionmenu.c b/modules/other/gail/gailoptionmenu.c new file mode 100644 index 000000000..873e93977 --- /dev/null +++ b/modules/other/gail/gailoptionmenu.c @@ -0,0 +1,388 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailoptionmenu.h" + +static void gail_option_menu_class_init (GailOptionMenuClass *klass); +static void gail_option_menu_real_initialize (AtkObject *obj, + gpointer data); + +static gint gail_option_menu_get_n_children (AtkObject *obj); +static AtkObject* gail_option_menu_ref_child (AtkObject *obj, + gint i); +static gint gail_option_menu_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); +static gint gail_option_menu_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data); + + +static void atk_action_interface_init (AtkActionIface *iface); + +static gboolean gail_option_menu_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_option_menu_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* gail_option_menu_get_description (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_option_menu_action_get_name (AtkAction *action, + gint i); +static gboolean gail_option_menu_set_description (AtkAction *action, + gint i, + const gchar *desc); + +static GailButtonClass* parent_class = NULL; + +GType +gail_option_menu_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailOptionMenuClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_option_menu_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailOptionMenu), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_BUTTON, + "GailOptionMenu", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + } + + return type; +} + +static void +gail_option_menu_class_init (GailOptionMenuClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailContainerClass *container_class; + + container_class = (GailContainerClass *) klass; + + class->get_n_children = gail_option_menu_get_n_children; + class->ref_child = gail_option_menu_ref_child; + class->initialize = gail_option_menu_real_initialize; + + container_class->add_gtk = gail_option_menu_real_add_gtk; + container_class->remove_gtk = gail_option_menu_real_remove_gtk; + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_option_menu_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), NULL); + + object = g_object_new (GAIL_TYPE_OPTION_MENU, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_option_menu_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + obj->role = ATK_ROLE_COMBO_BOX; +} + +static gint +gail_option_menu_get_n_children (AtkObject *obj) +{ + GtkWidget *widget; + GtkOptionMenu *option_menu; + gint n_children = 0; + + g_return_val_if_fail (GAIL_IS_OPTION_MENU (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + option_menu = GTK_OPTION_MENU (widget); + if (gtk_option_menu_get_menu (option_menu)) + n_children++; + + return n_children;; +} + +static AtkObject* +gail_option_menu_ref_child (AtkObject *obj, + gint i) +{ + GtkWidget *widget; + AtkObject *accessible; + + g_return_val_if_fail (GAIL_IS_OPTION_MENU (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + + if (i == 0) + accessible = g_object_ref (gtk_widget_get_accessible (gtk_option_menu_get_menu (GTK_OPTION_MENU (widget)))); + else + accessible = NULL; + + return accessible; +} + +static gint +gail_option_menu_real_add_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + AtkObject* atk_parent = ATK_OBJECT (data); + AtkObject* atk_child = gtk_widget_get_accessible (widget); + + GAIL_CONTAINER_CLASS (parent_class)->add_gtk (container, widget, data); + + g_object_notify (G_OBJECT (atk_child), "accessible_parent"); + + g_signal_emit_by_name (atk_parent, "children_changed::add", + 1, atk_child, NULL); + + return 1; +} + +static gint +gail_option_menu_real_remove_gtk (GtkContainer *container, + GtkWidget *widget, + gpointer data) +{ + AtkPropertyValues values = { NULL }; + AtkObject* atk_parent = ATK_OBJECT (data); + AtkObject *atk_child = gtk_widget_get_accessible (widget); + + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, atk_parent); + + values.property_name = "accessible-parent"; + g_signal_emit_by_name (atk_child, + "property_change::accessible-parent", &values, NULL); + g_signal_emit_by_name (atk_parent, "children_changed::remove", + 1, atk_child, NULL); + + return 1; +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_option_menu_do_action; + iface->get_n_actions = gail_option_menu_get_n_actions; + iface->get_description = gail_option_menu_get_description; + iface->get_name = gail_option_menu_action_get_name; + iface->set_description = gail_option_menu_set_description; +} + +static gboolean +gail_option_menu_do_action (AtkAction *action, + gint i) +{ + GtkWidget *widget; + GailButton *button; + gboolean return_value = TRUE; + + button = GAIL_BUTTON (action); + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + + if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + + switch (i) + { + case 0: + if (button->action_idle_handler) + return_value = FALSE; + else + button->action_idle_handler = g_idle_add (idle_do_action, button); + break; + default: + return_value = FALSE; + break; + } + return return_value; +} + +static gboolean +idle_do_action (gpointer data) +{ + GtkButton *button; + GtkWidget *widget; + GdkEvent tmp_event; + GailButton *gail_button; + + GDK_THREADS_ENTER (); + + gail_button = GAIL_BUTTON (data); + gail_button->action_idle_handler = 0; + + widget = GTK_ACCESSIBLE (gail_button)->widget; + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + button = GTK_BUTTON (widget); + + button->in_button = TRUE; + gtk_button_enter (button); + /* + * Simulate a button press event. calling gtk_button_pressed() does + * not get the job done for a GtkOptionMenu. + */ + tmp_event.button.type = GDK_BUTTON_PRESS; + tmp_event.button.window = widget->window; + tmp_event.button.button = 1; + tmp_event.button.send_event = TRUE; + tmp_event.button.time = GDK_CURRENT_TIME; + tmp_event.button.axes = NULL; + + gtk_widget_event (widget, &tmp_event); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_option_menu_get_n_actions (AtkAction *action) +{ + return 1; +} + +static G_CONST_RETURN gchar* +gail_option_menu_get_description (AtkAction *action, + gint i) +{ + GailButton *button; + G_CONST_RETURN gchar *return_value; + + button = GAIL_BUTTON (action); + + switch (i) + { + case 0: + return_value = button->press_description; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_option_menu_action_get_name (AtkAction *action, + gint i) +{ + G_CONST_RETURN gchar *return_value; + + switch (i) + { + case 0: + /* + * This action simulates a button press by simulating moving the + * mouse into the button followed by pressing the left mouse button. + */ + return_value = "press"; + break; + default: + return_value = NULL; + break; + } + return return_value; +} + +static gboolean +gail_option_menu_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + GailButton *button; + gchar **value; + + button = GAIL_BUTTON (action); + + switch (i) + { + case 0: + value = &button->press_description; + break; + default: + value = NULL; + break; + } + + if (value) + { + g_free (*value); + *value = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} diff --git a/modules/other/gail/gailoptionmenu.h b/modules/other/gail/gailoptionmenu.h new file mode 100644 index 000000000..dfd42a5e0 --- /dev/null +++ b/modules/other/gail/gailoptionmenu.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_OPTION_MENU_H__ +#define __GAIL_OPTION_MENU_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailbutton.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_OPTION_MENU (gail_option_menu_get_type ()) +#define GAIL_OPTION_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_OPTION_MENU, GailOptionMenu)) +#define GAIL_OPTION_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_OPTION_MENU, GailOptionMenuClass)) +#define GAIL_IS_OPTION_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_OPTION_MENU)) +#define GAIL_IS_OPTION_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_OPTION_MENU)) +#define GAIL_OPTION_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_OPTION_MENU, GailOptionMenuClass)) + +typedef struct _GailOptionMenu GailOptionMenu; +typedef struct _GailOptionMenuClass GailOptionMenuClass; + +struct _GailOptionMenu +{ + GailButton parent; +}; + +GType gail_option_menu_get_type (void); + +struct _GailOptionMenuClass +{ + GailButtonClass parent_class; +}; + +AtkObject* gail_option_menu_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_OPTION_MENU_H__ */ diff --git a/modules/other/gail/gailpaned.c b/modules/other/gail/gailpaned.c new file mode 100644 index 000000000..a3ac815a4 --- /dev/null +++ b/modules/other/gail/gailpaned.c @@ -0,0 +1,247 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailpaned.h" + +static void gail_paned_class_init (GailPanedClass *klass); + +static void gail_paned_real_initialize (AtkObject *obj, + gpointer data); +static void gail_paned_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation); + +static AtkStateSet* gail_paned_ref_state_set (AtkObject *accessible); + +static void atk_value_interface_init (AtkValueIface *iface); +static void gail_paned_get_current_value (AtkValue *obj, + GValue *value); +static void gail_paned_get_maximum_value (AtkValue *obj, + GValue *value); +static void gail_paned_get_minimum_value (AtkValue *obj, + GValue *value); +static gboolean gail_paned_set_current_value (AtkValue *obj, + const GValue *value); + +static GailContainerClass *parent_class = NULL; + +GType +gail_paned_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailPanedClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_paned_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailPaned), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_value_info = + { + (GInterfaceInitFunc) atk_value_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailPaned", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_VALUE, + &atk_value_info); + } + return type; +} + +static void +gail_paned_class_init (GailPanedClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_paned_ref_state_set; + class->initialize = gail_paned_real_initialize; +} + +AtkObject* +gail_paned_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_PANED (widget), NULL); + + object = g_object_new (GAIL_TYPE_PANED, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static AtkStateSet* +gail_paned_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + if (GTK_IS_VPANED (widget)) + atk_state_set_add_state (state_set, ATK_STATE_VERTICAL); + else if (GTK_IS_HPANED (widget)) + atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL); + + return state_set; +} + +static void +gail_paned_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + g_signal_connect (data, + "size_allocate", + G_CALLBACK (gail_paned_size_allocate_gtk), + NULL); + + obj->role = ATK_ROLE_SPLIT_PANE; +} + +static void +gail_paned_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation) +{ + AtkObject *obj = gtk_widget_get_accessible (widget); + + g_object_notify (G_OBJECT (obj), "accessible-value"); +} + + +static void +atk_value_interface_init (AtkValueIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_current_value = gail_paned_get_current_value; + iface->get_maximum_value = gail_paned_get_maximum_value; + iface->get_minimum_value = gail_paned_get_minimum_value; + iface->set_current_value = gail_paned_set_current_value; + +} + +static void +gail_paned_get_current_value (AtkValue *obj, + GValue *value) +{ + GtkWidget* widget; + gint current_value; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + current_value = gtk_paned_get_position (GTK_PANED (widget)); + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value,current_value); +} + +static void +gail_paned_get_maximum_value (AtkValue *obj, + GValue *value) +{ + GtkWidget* widget; + gint maximum_value; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + maximum_value = GTK_PANED (widget)->max_position; + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, maximum_value); +} + +static void +gail_paned_get_minimum_value (AtkValue *obj, + GValue *value) +{ + GtkWidget* widget; + gint minimum_value; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + minimum_value = GTK_PANED (widget)->min_position; + memset (value, 0, sizeof (GValue)); + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, minimum_value); +} + +/* + * Calling atk_value_set_current_value() is no guarantee that the value is + * acceptable; it is necessary to listen for accessible-value signals + * and check whether the current value has been changed or check what the + * maximum and minimum values are. + */ + +static gboolean +gail_paned_set_current_value (AtkValue *obj, + const GValue *value) +{ + GtkWidget* widget; + gint new_value; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (G_VALUE_HOLDS_INT (value)) + { + new_value = g_value_get_int (value); + gtk_paned_set_position (GTK_PANED (widget), new_value); + + return TRUE; + } + else + return FALSE; +} diff --git a/modules/other/gail/gailpaned.h b/modules/other/gail/gailpaned.h new file mode 100644 index 000000000..0726025c4 --- /dev/null +++ b/modules/other/gail/gailpaned.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_PANED_H__ +#define __GAIL_PANED_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_PANED (gail_paned_get_type ()) +#define GAIL_PANED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_PANED, GailPaned)) +#define GAIL_PANED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_PANED, GailPanedClass)) +#define GAIL_IS_PANED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_PANED)) +#define GAIL_IS_PANED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_PANED)) +#define GAIL_PANED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_PANED, GailPanedClass)) + +typedef struct _GailPaned GailPaned; +typedef struct _GailPanedClass GailPanedClass; + +struct _GailPaned +{ + GailContainer parent; +}; + +GType gail_paned_get_type (void); + +struct _GailPanedClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_paned_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_PANED_H__ */ diff --git a/modules/other/gail/gailpixmap.c b/modules/other/gail/gailpixmap.c new file mode 100644 index 000000000..6540f9c2a --- /dev/null +++ b/modules/other/gail/gailpixmap.c @@ -0,0 +1,198 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailpixmap.h" + +static void gail_pixmap_class_init (GailPixmapClass *klass); +static void gail_pixmap_object_init (GailPixmap *pixmap); + +/* AtkImage */ +static void atk_image_interface_init (AtkImageIface *iface); +static G_CONST_RETURN gchar* gail_pixmap_get_image_description + (AtkImage *obj); +static void gail_pixmap_get_image_position + (AtkImage *obj, + gint *x, + gint *y, + AtkCoordType coord_type); +static void gail_pixmap_get_image_size (AtkImage *obj, + gint *width, + gint *height); +static gboolean gail_pixmap_set_image_description + (AtkImage *obj, + const gchar *description); +static void gail_pixmap_finalize (GObject *object); + +static GailWidgetClass* parent_class = NULL; + +GType +gail_pixmap_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailPixmapClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_pixmap_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailPixmap), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_pixmap_object_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_image_info = + { + (GInterfaceInitFunc) atk_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailPixmap", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + } + return type; +} + +static void +gail_pixmap_class_init (GailPixmapClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_pixmap_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gail_pixmap_object_init (GailPixmap *pixmap) +{ + pixmap->image_description = NULL; +} + +AtkObject* +gail_pixmap_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_assert (GTK_IS_PIXMAP (widget)); + g_return_val_if_fail (GTK_IS_PIXMAP (widget), NULL); + + object = g_object_new (GAIL_TYPE_PIXMAP, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_ICON; + + return accessible; +} + +static void +atk_image_interface_init (AtkImageIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_image_description = gail_pixmap_get_image_description; + iface->get_image_position = gail_pixmap_get_image_position; + iface->get_image_size = gail_pixmap_get_image_size; + iface->set_image_description = gail_pixmap_set_image_description; +} + +static G_CONST_RETURN gchar* +gail_pixmap_get_image_description (AtkImage *obj) +{ + GailPixmap* pixmap; + + g_return_val_if_fail (GAIL_IS_PIXMAP (obj), NULL); + + pixmap = GAIL_PIXMAP (obj); + + return pixmap->image_description; +} + +static void +gail_pixmap_get_image_position (AtkImage *obj, + gint *x, + gint *y, + AtkCoordType coord_type) +{ + atk_component_get_position (ATK_COMPONENT (obj), x, y, coord_type); +} + +static void +gail_pixmap_get_image_size (AtkImage *obj, + gint *width, + gint *height) +{ + GtkWidget *widget; + GtkPixmap *pixmap; + + *width = -1; + *height = -1; + + g_return_if_fail (GAIL_IS_PIXMAP (obj)); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == 0) + /* State is defunct */ + return; + + g_return_if_fail (GTK_IS_PIXMAP (widget)); + + pixmap = GTK_PIXMAP (widget); + + if (pixmap->pixmap) + gdk_window_get_size (pixmap->pixmap, width, height); +} + +static gboolean +gail_pixmap_set_image_description (AtkImage *obj, + const gchar *description) +{ + GailPixmap* pixmap; + + g_return_val_if_fail (GAIL_IS_PIXMAP (obj), FALSE); + + pixmap = GAIL_PIXMAP (obj); + g_free (pixmap->image_description); + + pixmap->image_description = g_strdup (description); + + return TRUE; +} + +static void +gail_pixmap_finalize (GObject *object) +{ + GailPixmap *pixmap = GAIL_PIXMAP (object); + + g_free (pixmap->image_description); + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/modules/other/gail/gailpixmap.h b/modules/other/gail/gailpixmap.h new file mode 100644 index 000000000..889562a66 --- /dev/null +++ b/modules/other/gail/gailpixmap.h @@ -0,0 +1,63 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_PIXMAP_H__ +#define __GAIL_PIXMAP_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_PIXMAP (gail_pixmap_get_type ()) +#define GAIL_PIXMAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_PIXMAP, GailPixmap)) +#define GAIL_PIXMAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_PIXMAP, GailPixmapClass)) +#define GAIL_IS_PIXMAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_PIXMAP)) +#define GAIL_IS_PIXMAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_PIXMAP)) +#define GAIL_PIXMAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_PIXMAP, GailPixmapClass)) + +typedef struct _GailPixmap GailPixmap; +typedef struct _GailPixmapClass GailPixmapClass; + +struct _GailPixmap +{ + GailWidget parent; + + gchar* image_description; + +}; + +GType gail_pixmap_get_type (void); + +struct _GailPixmapClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_pixmap_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_PIXMAP_H__ */ + diff --git a/modules/other/gail/gailprogressbar.c b/modules/other/gail/gailprogressbar.c new file mode 100644 index 000000000..1ed4a49ac --- /dev/null +++ b/modules/other/gail/gailprogressbar.c @@ -0,0 +1,271 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailprogressbar.h" +#include "gailadjustment.h" + +static void gail_progress_bar_class_init (GailProgressBarClass *klass); +static void gail_progress_bar_real_initialize (AtkObject *obj, + gpointer data); +static void gail_progress_bar_finalize (GObject *object); + + +static void atk_value_interface_init (AtkValueIface *iface); + + +static void gail_progress_bar_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_progress_bar_get_current_value (AtkValue *obj, + GValue *value); +static void gail_progress_bar_get_maximum_value (AtkValue *obj, + GValue *value); +static void gail_progress_bar_get_minimum_value (AtkValue *obj, + GValue *value); +static void gail_progress_bar_value_changed (GtkAdjustment *adjustment, + gpointer data); + +static GailWidgetClass *parent_class = NULL; + +GType +gail_progress_bar_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailProgressBarClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_progress_bar_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailProgressBar), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_value_info = + { + (GInterfaceInitFunc) atk_value_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailProgressBar", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_VALUE, + &atk_value_info); + } + return type; +} + +static void +gail_progress_bar_class_init (GailProgressBarClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + widget_class = (GailWidgetClass*)klass; + + widget_class->notify_gtk = gail_progress_bar_real_notify_gtk; + + class->initialize = gail_progress_bar_real_initialize; + + gobject_class->finalize = gail_progress_bar_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_progress_bar_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_PROGRESS_BAR (widget), NULL); + + object = g_object_new (GAIL_TYPE_PROGRESS_BAR, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_progress_bar_real_initialize (AtkObject *obj, + gpointer data) +{ + GailProgressBar *progress_bar = GAIL_PROGRESS_BAR (obj); + GtkProgress *gtk_progress; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gtk_progress = GTK_PROGRESS (data); + /* + * If a GtkAdjustment already exists for the spin_button, + * create the GailAdjustment + */ + if (gtk_progress->adjustment) + { + progress_bar->adjustment = gail_adjustment_new (gtk_progress->adjustment); + g_signal_connect (gtk_progress->adjustment, + "value-changed", + G_CALLBACK (gail_progress_bar_value_changed), + obj); + } + else + progress_bar->adjustment = NULL; + + obj->role = ATK_ROLE_PROGRESS_BAR; +} + +static void +atk_value_interface_init (AtkValueIface *iface) +{ + + g_return_if_fail (iface != NULL); + + iface->get_current_value = gail_progress_bar_get_current_value; + iface->get_maximum_value = gail_progress_bar_get_maximum_value; + iface->get_minimum_value = gail_progress_bar_get_minimum_value; +} + +static void +gail_progress_bar_get_current_value (AtkValue *obj, + GValue *value) +{ + GailProgressBar *progress_bar; + + g_return_if_fail (GAIL_IS_PROGRESS_BAR (obj)); + + progress_bar = GAIL_PROGRESS_BAR (obj); + if (progress_bar->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_current_value (ATK_VALUE (progress_bar->adjustment), value); +} + +static void +gail_progress_bar_get_maximum_value (AtkValue *obj, + GValue *value) +{ + GailProgressBar *progress_bar; + + g_return_if_fail (GAIL_IS_PROGRESS_BAR (obj)); + + progress_bar = GAIL_PROGRESS_BAR (obj); + if (progress_bar->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_maximum_value (ATK_VALUE (progress_bar->adjustment), value); +} + +static void +gail_progress_bar_get_minimum_value (AtkValue *obj, + GValue *value) +{ + GailProgressBar *progress_bar; + + g_return_if_fail (GAIL_IS_PROGRESS_BAR (obj)); + + progress_bar = GAIL_PROGRESS_BAR (obj); + if (progress_bar->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_minimum_value (ATK_VALUE (progress_bar->adjustment), value); +} + +static void +gail_progress_bar_finalize (GObject *object) +{ + GailProgressBar *progress_bar = GAIL_PROGRESS_BAR (object); + + if (progress_bar->adjustment) + { + g_object_unref (progress_bar->adjustment); + progress_bar->adjustment = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static void +gail_progress_bar_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget = GTK_WIDGET (obj); + GailProgressBar *progress_bar = GAIL_PROGRESS_BAR (gtk_widget_get_accessible (widget)); + + if (strcmp (pspec->name, "adjustment") == 0) + { + /* + * Get rid of the GailAdjustment for the GtkAdjustment + * which was associated with the progress_bar. + */ + if (progress_bar->adjustment) + { + g_object_unref (progress_bar->adjustment); + progress_bar->adjustment = NULL; + } + /* + * Create the GailAdjustment when notify for "adjustment" property + * is received + */ + progress_bar->adjustment = gail_adjustment_new (GTK_PROGRESS (widget)->adjustment); + g_signal_connect (GTK_PROGRESS (widget)->adjustment, + "value-changed", + G_CALLBACK (gail_progress_bar_value_changed), + progress_bar); + } + else + parent_class->notify_gtk (obj, pspec); +} + +static void +gail_progress_bar_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GailProgressBar *progress_bar; + + g_return_if_fail (adjustment != NULL); + g_return_if_fail (data != NULL); + + progress_bar = GAIL_PROGRESS_BAR (data); + + g_object_notify (G_OBJECT (progress_bar), "accessible-value"); +} diff --git a/modules/other/gail/gailprogressbar.h b/modules/other/gail/gailprogressbar.h new file mode 100644 index 000000000..441cb36d6 --- /dev/null +++ b/modules/other/gail/gailprogressbar.h @@ -0,0 +1,64 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_PROGRESS_BAR_H__ +#define __GAIL_PROGRESS_BAR_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_PROGRESS_BAR (gail_progress_bar_get_type ()) +#define GAIL_PROGRESS_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_PROGRESS_BAR, GailProgressBar)) +#define GAIL_PROGRESS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_PROGRESS_BAR, GailProgressBarClass)) +#define GAIL_IS_PROGRESS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_PROGRESS_BAR)) +#define GAIL_IS_PROGRESS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_PROGRESS_BAR)) +#define GAIL_PROGRESS_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_PROGRESS_BAR, GailProgressBarClass)) + +typedef struct _GailProgressBar GailProgressBar; +typedef struct _GailProgressBarClass GailProgressBarClass; + +struct _GailProgressBar +{ + GailWidget parent; + + AtkObject *adjustment; +}; + +GType gail_progress_bar_get_type (void); + +struct _GailProgressBarClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_progress_bar_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_PROGRESS_BAR_H__ */ + + diff --git a/modules/other/gail/gailradiobutton.c b/modules/other/gail/gailradiobutton.c new file mode 100644 index 000000000..fecdf85ea --- /dev/null +++ b/modules/other/gail/gailradiobutton.c @@ -0,0 +1,164 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailradiobutton.h" + +static void gail_radio_button_class_init (GailRadioButtonClass *klass); +static void gail_radio_button_instance_init (GailRadioButton *radio_button); + +static AtkRelationSet* gail_radio_button_ref_relation_set (AtkObject *obj) +; + +static GailToggleButtonClass *parent_class = NULL; + +GType +gail_radio_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRadioButtonClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_radio_button_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRadioButton), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_radio_button_instance_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_TOGGLE_BUTTON, + "GailRadioButton", &tinfo, 0); + } + + return type; +} + +static void +gail_radio_button_class_init (GailRadioButtonClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_relation_set = gail_radio_button_ref_relation_set; +} + +AtkObject* +gail_radio_button_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_RADIO_BUTTON (widget), NULL); + + object = g_object_new (GAIL_TYPE_RADIO_BUTTON, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_RADIO_BUTTON; + return accessible; +} + +static void +gail_radio_button_instance_init (GailRadioButton *radio_button) +{ + radio_button->old_group = NULL; +} + +AtkRelationSet* +gail_radio_button_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + GSList *list; + GailRadioButton *radio_button; + + g_return_val_if_fail (GAIL_IS_RADIO_BUTTON (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + { + /* + * State is defunct + */ + return NULL; + } + radio_button = GAIL_RADIO_BUTTON (obj); + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + /* + * If the radio button'group has changed remove the relation + */ + list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (widget)); + + if (radio_button->old_group != list) + { + AtkRelation *relation; + + relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_MEMBER_OF); + atk_relation_set_remove (relation_set, relation); + } + + if (!atk_relation_set_contains (relation_set, ATK_RELATION_MEMBER_OF)) + { + /* + * Get the members of the button group + */ + + radio_button->old_group = list; + if (list) + { + AtkObject **accessible_array; + guint list_length; + AtkRelation* relation; + gint i = 0; + + list_length = g_slist_length (list); + accessible_array = (AtkObject**) g_malloc (sizeof (AtkObject *) * + list_length); + while (list != NULL) + { + GtkWidget* list_item = list->data; + + accessible_array[i++] = gtk_widget_get_accessible (list_item); + + list = list->next; + } + relation = atk_relation_new (accessible_array, list_length, + ATK_RELATION_MEMBER_OF); + g_free (accessible_array); + + atk_relation_set_add (relation_set, relation); + /* + * Unref the relation so that it is not leaked. + */ + g_object_unref (relation); + } + } + return relation_set; +} diff --git a/modules/other/gail/gailradiobutton.h b/modules/other/gail/gailradiobutton.h new file mode 100644 index 000000000..d48106bf2 --- /dev/null +++ b/modules/other/gail/gailradiobutton.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RADIO_BUTTON_H__ +#define __GAIL_RADIO_BUTTON_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailtogglebutton.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RADIO_BUTTON (gail_radio_button_get_type ()) +#define GAIL_RADIO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RADIO_BUTTON, GailRadioButton)) +#define GAIL_RADIO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_RADIO_BUTTON, GailRadioButtonClass)) +#define GAIL_IS_RADIO_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RADIO_BUTTON)) +#define GAIL_IS_RADIO_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RADIO_BUTTON)) +#define GAIL_RADIO_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RADIO_BUTTON, GailRadioButtonClass)) + +typedef struct _GailRadioButton GailRadioButton; +typedef struct _GailRadioButtonClass GailRadioButtonClass; + +struct _GailRadioButton +{ + GailToggleButton parent; + + GSList *old_group; +}; + +GType gail_radio_button_get_type (void); + +struct _GailRadioButtonClass +{ + GailToggleButtonClass parent_class; +}; + +AtkObject* gail_radio_button_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_RADIO_BUTTON_H__ */ diff --git a/modules/other/gail/gailradiomenuitem.c b/modules/other/gail/gailradiomenuitem.c new file mode 100644 index 000000000..cb7e20803 --- /dev/null +++ b/modules/other/gail/gailradiomenuitem.c @@ -0,0 +1,168 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailradiomenuitem.h" +#include "gailradiosubmenuitem.h" + +static void gail_radio_menu_item_class_init (GailRadioMenuItemClass *klass); +static void gail_radio_menu_item_instance_init (GailRadioMenuItem *radio_menu_item); + +static AtkRelationSet* gail_radio_menu_item_ref_relation_set (AtkObject *obj) +; + +static GailCheckMenuItemClass *parent_class = NULL; + +GType +gail_radio_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRadioMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_radio_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRadioMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_radio_menu_item_instance_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CHECK_MENU_ITEM, + "GailRadioMenuItem", &tinfo, 0); + } + + return type; +} + +static void +gail_radio_menu_item_class_init (GailRadioMenuItemClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_relation_set = gail_radio_menu_item_ref_relation_set; +} + +AtkObject* +gail_radio_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (widget), NULL); + + if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget))) + return gail_radio_sub_menu_item_new (widget); + + object = g_object_new (GAIL_TYPE_RADIO_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_RADIO_MENU_ITEM; + return accessible; +} + +static void +gail_radio_menu_item_instance_init (GailRadioMenuItem *radio_menu_item) +{ + radio_menu_item->old_group = NULL; +} + +AtkRelationSet* +gail_radio_menu_item_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + GSList *list; + GailRadioMenuItem *radio_menu_item; + + g_return_val_if_fail (GAIL_IS_RADIO_MENU_ITEM (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + { + /* + * State is defunct + */ + return NULL; + } + radio_menu_item = GAIL_RADIO_MENU_ITEM (obj); + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + /* + * If the radio menu_item'group has changed remove the relation + */ + list = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (widget)); + + if (radio_menu_item->old_group != list) + { + AtkRelation *relation; + + relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_MEMBER_OF); + atk_relation_set_remove (relation_set, relation); + } + + if (!atk_relation_set_contains (relation_set, ATK_RELATION_MEMBER_OF)) + { + /* + * Get the members of the menu_item group + */ + + radio_menu_item->old_group = list; + if (list) + { + AtkObject **accessible_array; + guint list_length; + AtkRelation* relation; + gint i = 0; + + list_length = g_slist_length (list); + accessible_array = (AtkObject**) g_malloc (sizeof (AtkObject *) * + list_length); + while (list != NULL) + { + GtkWidget* list_item = list->data; + + accessible_array[i++] = gtk_widget_get_accessible (list_item); + + list = list->next; + } + relation = atk_relation_new (accessible_array, list_length, + ATK_RELATION_MEMBER_OF); + g_free (accessible_array); + + atk_relation_set_add (relation_set, relation); + /* + * Unref the relation so that it is not leaked. + */ + g_object_unref (relation); + } + } + return relation_set; +} diff --git a/modules/other/gail/gailradiomenuitem.h b/modules/other/gail/gailradiomenuitem.h new file mode 100644 index 000000000..b8f6847bd --- /dev/null +++ b/modules/other/gail/gailradiomenuitem.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RADIO_MENU_ITEM_H__ +#define __GAIL_RADIO_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcheckmenuitem.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RADIO_MENU_ITEM (gail_radio_menu_item_get_type ()) +#define GAIL_RADIO_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RADIO_MENU_ITEM, GailRadioMenuItem)) +#define GAIL_RADIO_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_RADIO_MENU_ITEM, GailRadioMenuItemClass)) +#define GAIL_IS_RADIO_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RADIO_MENU_ITEM)) +#define GAIL_IS_RADIO_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RADIO_MENU_ITEM)) +#define GAIL_RADIO_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RADIO_MENU_ITEM, GailRadioMenuItemClass)) + +typedef struct _GailRadioMenuItem GailRadioMenuItem; +typedef struct _GailRadioMenuItemClass GailRadioMenuItemClass; + +struct _GailRadioMenuItem +{ + GailCheckMenuItem parent; + + GSList *old_group; +}; + +GType gail_radio_menu_item_get_type (void); + +struct _GailRadioMenuItemClass +{ + GailCheckMenuItemClass parent_class; +}; + +AtkObject* gail_radio_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_RADIO_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailradiosubmenuitem.c b/modules/other/gail/gailradiosubmenuitem.c new file mode 100644 index 000000000..b702567b8 --- /dev/null +++ b/modules/other/gail/gailradiosubmenuitem.c @@ -0,0 +1,164 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailradiosubmenuitem.h" + +static void gail_radio_sub_menu_item_class_init (GailRadioSubMenuItemClass *klass); +static void gail_radio_sub_menu_item_instance_init (GailRadioSubMenuItem *radio_menu_item); + +static AtkRelationSet* gail_radio_sub_menu_item_ref_relation_set (AtkObject *obj) +; + +static GailCheckSubMenuItemClass *parent_class = NULL; + +GType +gail_radio_sub_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRadioSubMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_radio_sub_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRadioSubMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_radio_sub_menu_item_instance_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CHECK_SUB_MENU_ITEM, + "GailRadioSubMenuItem", &tinfo, 0); + } + + return type; +} + +static void +gail_radio_sub_menu_item_class_init (GailRadioSubMenuItemClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_relation_set = gail_radio_sub_menu_item_ref_relation_set; +} + +AtkObject* +gail_radio_sub_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_RADIO_MENU_ITEM (widget), NULL); + + object = g_object_new (GAIL_TYPE_RADIO_SUB_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_RADIO_MENU_ITEM; + return accessible; +} + +static void +gail_radio_sub_menu_item_instance_init (GailRadioSubMenuItem *radio_menu_item) +{ + radio_menu_item->old_group = NULL; +} + +AtkRelationSet* +gail_radio_sub_menu_item_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + GSList *list; + GailRadioSubMenuItem *radio_menu_item; + + g_return_val_if_fail (GAIL_IS_RADIO_SUB_MENU_ITEM (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + { + /* + * State is defunct + */ + return NULL; + } + radio_menu_item = GAIL_RADIO_SUB_MENU_ITEM (obj); + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + /* + * If the radio menu_item'group has changed remove the relation + */ + list = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (widget)); + + if (radio_menu_item->old_group != list) + { + AtkRelation *relation; + + relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_MEMBER_OF); + atk_relation_set_remove (relation_set, relation); + } + + if (!atk_relation_set_contains (relation_set, ATK_RELATION_MEMBER_OF)) + { + /* + * Get the members of the menu_item group + */ + + radio_menu_item->old_group = list; + if (list) + { + AtkObject **accessible_array; + guint list_length; + AtkRelation* relation; + gint i = 0; + + list_length = g_slist_length (list); + accessible_array = (AtkObject**) g_malloc (sizeof (AtkObject *) * + list_length); + while (list != NULL) + { + GtkWidget* list_item = list->data; + + accessible_array[i++] = gtk_widget_get_accessible (list_item); + + list = list->next; + } + relation = atk_relation_new (accessible_array, list_length, + ATK_RELATION_MEMBER_OF); + g_free (accessible_array); + + atk_relation_set_add (relation_set, relation); + /* + * Unref the relation so that it is not leaked. + */ + g_object_unref (relation); + } + } + return relation_set; +} diff --git a/modules/other/gail/gailradiosubmenuitem.h b/modules/other/gail/gailradiosubmenuitem.h new file mode 100644 index 000000000..10e2e7eb7 --- /dev/null +++ b/modules/other/gail/gailradiosubmenuitem.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RADIO_SUB_MENU_ITEM_H__ +#define __GAIL_RADIO_SUB_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailchecksubmenuitem.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RADIO_SUB_MENU_ITEM (gail_radio_sub_menu_item_get_type ()) +#define GAIL_RADIO_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RADIO_SUB_MENU_ITEM, GailRadioSubMenuItem)) +#define GAIL_RADIO_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_RADIO_SUB_MENU_ITEM, GailRadioSubMenuItemClass)) +#define GAIL_IS_RADIO_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RADIO_SUB_MENU_ITEM)) +#define GAIL_IS_RADIO_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RADIO_SUB_MENU_ITEM)) +#define GAIL_RADIO_SUB_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RADIO_SUB_MENU_ITEM, GailRadioSubMenuItemClass)) + +typedef struct _GailRadioSubMenuItem GailRadioSubMenuItem; +typedef struct _GailRadioSubMenuItemClass GailRadioSubMenuItemClass; + +struct _GailRadioSubMenuItem +{ + GailCheckSubMenuItem parent; + + GSList *old_group; +}; + +GType gail_radio_sub_menu_item_get_type (void); + +struct _GailRadioSubMenuItemClass +{ + GailCheckSubMenuItemClass parent_class; +}; + +AtkObject* gail_radio_sub_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_RADIO_SUB_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailrange.c b/modules/other/gail/gailrange.c new file mode 100644 index 000000000..2313aeeda --- /dev/null +++ b/modules/other/gail/gailrange.c @@ -0,0 +1,554 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> +#include "gailrange.h" +#include "gailadjustment.h" +#include "gail-private-macros.h" + +static void gail_range_class_init (GailRangeClass *klass); + +static void gail_range_real_initialize (AtkObject *obj, + gpointer data); + +static void gail_range_finalize (GObject *object); + +static AtkStateSet* gail_range_ref_state_set (AtkObject *obj); + + +static void gail_range_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void atk_value_interface_init (AtkValueIface *iface); +static void gail_range_get_current_value (AtkValue *obj, + GValue *value); +static void gail_range_get_maximum_value (AtkValue *obj, + GValue *value); +static void gail_range_get_minimum_value (AtkValue *obj, + GValue *value); +static gboolean gail_range_set_current_value (AtkValue *obj, + const GValue *value); +static void gail_range_value_changed (GtkAdjustment *adjustment, + gpointer data); + +static void atk_action_interface_init (AtkActionIface *iface); +static gboolean gail_range_do_action (AtkAction *action, + gint i); +static gboolean idle_do_action (gpointer data); +static gint gail_range_get_n_actions (AtkAction *action); +static G_CONST_RETURN gchar* gail_range_get_description (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_range_get_keybinding (AtkAction *action, + gint i); +static G_CONST_RETURN gchar* gail_range_action_get_name (AtkAction *action, + gint i); +static gboolean gail_range_set_description (AtkAction *action, + gint i, + const gchar *desc); +static GailWidgetClass *parent_class = NULL; + +GType +gail_range_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRangeClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_range_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRange), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_action_info = + { + (GInterfaceInitFunc) atk_action_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + + static const GInterfaceInfo atk_value_info = + { + (GInterfaceInitFunc) atk_value_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailRange", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_ACTION, + &atk_action_info); + + g_type_add_interface_static (type, ATK_TYPE_VALUE, + &atk_value_info); + } + return type; +} + +static void +gail_range_class_init (GailRangeClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + widget_class = (GailWidgetClass*)klass; + + widget_class->notify_gtk = gail_range_real_notify_gtk; + + class->ref_state_set = gail_range_ref_state_set; + class->initialize = gail_range_real_initialize; + + gobject_class->finalize = gail_range_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_range_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_RANGE (widget), NULL); + + object = g_object_new (GAIL_TYPE_RANGE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_range_real_initialize (AtkObject *obj, + gpointer data) +{ + GailRange *range = GAIL_RANGE (obj); + GtkRange *gtk_range; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gtk_range = GTK_RANGE (data); + /* + * If a GtkAdjustment already exists for the GtkRange, + * create the GailAdjustment + */ + if (gtk_range->adjustment) + { + range->adjustment = gail_adjustment_new (gtk_range->adjustment); + g_signal_connect (gtk_range->adjustment, + "value-changed", + G_CALLBACK (gail_range_value_changed), + range); + } + else + range->adjustment = NULL; + range->activate_keybinding=NULL; + range->activate_description=NULL; + /* + * Assumed to GtkScale (either GtkHScale or GtkVScale) + */ + obj->role = ATK_ROLE_SLIDER; +} + +static AtkStateSet* +gail_range_ref_state_set (AtkObject *obj) +{ + AtkStateSet *state_set; + GtkWidget *widget; + GtkRange *range; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget == NULL) + return state_set; + + range = GTK_RANGE (widget); + + /* + * We do not generate property change for orientation change as there + * is no interface to change the orientation which emits a notification + */ + if (range->orientation == GTK_ORIENTATION_HORIZONTAL) + atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL); + else + atk_state_set_add_state (state_set, ATK_STATE_VERTICAL); + + return state_set; +} + +static void +atk_value_interface_init (AtkValueIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_current_value = gail_range_get_current_value; + iface->get_maximum_value = gail_range_get_maximum_value; + iface->get_minimum_value = gail_range_get_minimum_value; + iface->set_current_value = gail_range_set_current_value; + +} + +static void +gail_range_get_current_value (AtkValue *obj, + GValue *value) +{ + GailRange *range; + + g_return_if_fail (GAIL_IS_RANGE (obj)); + + range = GAIL_RANGE (obj); + if (range->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_current_value (ATK_VALUE (range->adjustment), value); +} + +static void +gail_range_get_maximum_value (AtkValue *obj, + GValue *value) +{ + GailRange *range; + + g_return_if_fail (GAIL_IS_RANGE (obj)); + + range = GAIL_RANGE (obj); + if (range->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_maximum_value (ATK_VALUE (range->adjustment), value); +} + +static void +gail_range_get_minimum_value (AtkValue *obj, + GValue *value) +{ + GailRange *range; + + g_return_if_fail (GAIL_IS_RANGE (obj)); + + range = GAIL_RANGE (obj); + if (range->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_minimum_value (ATK_VALUE (range->adjustment), value); +} + +static gboolean gail_range_set_current_value (AtkValue *obj, + const GValue *value) +{ + GtkWidget *widget; + + g_return_val_if_fail (GAIL_IS_RANGE (obj), FALSE); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return FALSE; + + if (G_VALUE_HOLDS_DOUBLE (value)) + { + GtkRange *range = GTK_RANGE (widget); + gdouble new_value; + + new_value = g_value_get_double (value); + gtk_range_set_value (range, new_value); + return TRUE; + } + else + { + return FALSE; + } +} + +static void +gail_range_finalize (GObject *object) +{ + GailRange *range = GAIL_RANGE (object); + + if (range->adjustment) + { + /* + * The GtkAdjustment may live on so we need to dicsonnect the + * signal handler + */ + if (GAIL_ADJUSTMENT (range->adjustment)->adjustment) + { + g_signal_handlers_disconnect_by_func (GAIL_ADJUSTMENT (range->adjustment)->adjustment, + (void *)gail_range_value_changed, + range); + } + g_object_unref (range->adjustment); + range->adjustment = NULL; + } + range->activate_keybinding=NULL; + range->activate_description=NULL; + if (range->action_idle_handler) + { + g_source_remove (range->action_idle_handler); + range->action_idle_handler = 0; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static void +gail_range_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget = GTK_WIDGET (obj); + GailRange *range = GAIL_RANGE (gtk_widget_get_accessible (widget)); + + if (strcmp (pspec->name, "adjustment") == 0) + { + /* + * Get rid of the GailAdjustment for the GtkAdjustment + * which was associated with the range. + */ + if (range->adjustment) + { + g_object_unref (range->adjustment); + range->adjustment = NULL; + } + /* + * Create the GailAdjustment when notify for "adjustment" property + * is received + */ + range->adjustment = gail_adjustment_new (GTK_RANGE (widget)->adjustment); + g_signal_connect (GTK_RANGE (widget)->adjustment, + "value-changed", + G_CALLBACK (gail_range_value_changed), + range); + } + else + parent_class->notify_gtk (obj, pspec); +} + +static void +gail_range_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GailRange *range; + + g_return_if_fail (adjustment != NULL); + gail_return_if_fail (data != NULL); + + range = GAIL_RANGE (data); + + g_object_notify (G_OBJECT (range), "accessible-value"); +} + +static void +atk_action_interface_init (AtkActionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->do_action = gail_range_do_action; + iface->get_n_actions = gail_range_get_n_actions; + iface->get_description = gail_range_get_description; + iface->get_keybinding = gail_range_get_keybinding; + iface->get_name = gail_range_action_get_name; + iface->set_description = gail_range_set_description; +} + +static gboolean +gail_range_do_action (AtkAction *action, + gint i) +{ + GailRange *range; + GtkWidget *widget; + gboolean return_value = TRUE; + + range = GAIL_RANGE (action); + widget = GTK_ACCESSIBLE (action)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return FALSE; + if (!GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + return FALSE; + if(i==0) + { + if (range->action_idle_handler) + return_value = FALSE; + else + range->action_idle_handler = g_idle_add (idle_do_action, range); + } + else + return_value = FALSE; + return return_value; +} + +static gboolean +idle_do_action (gpointer data) +{ + GailRange *range; + GtkWidget *widget; + + GDK_THREADS_ENTER (); + + range = GAIL_RANGE (data); + range->action_idle_handler = 0; + widget = GTK_ACCESSIBLE (range)->widget; + if (widget == NULL /* State is defunct */ || + !GTK_WIDGET_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + gtk_widget_activate (widget); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gint +gail_range_get_n_actions (AtkAction *action) +{ + return 1; +} + +static G_CONST_RETURN gchar* +gail_range_get_description (AtkAction *action, + gint i) +{ + GailRange *range; + G_CONST_RETURN gchar *return_value; + + range = GAIL_RANGE (action); + if (i==0) + return_value = range->activate_description; + else + return_value = NULL; + return return_value; +} + +static G_CONST_RETURN gchar* +gail_range_get_keybinding (AtkAction *action, + gint i) +{ + GailRange *range; + gchar *return_value = NULL; + range = GAIL_RANGE (action); + if(i==0) + { + GtkWidget *widget; + GtkWidget *label; + AtkRelationSet *set; + AtkRelation *relation; + GPtrArray *target; + gpointer target_object; + guint key_val; + + range = GAIL_RANGE (action); + widget = GTK_ACCESSIBLE (range)->widget; + if (widget == NULL) + return NULL; + set = atk_object_ref_relation_set (ATK_OBJECT (action)); + + if (!set) + return NULL; + label = NULL; + relation = atk_relation_set_get_relation_by_type (set, ATK_RELATION_LABELLED_BY); + if (relation) + { + target = atk_relation_get_target (relation); + target_object = g_ptr_array_index (target, 0); + if (GTK_IS_ACCESSIBLE (target_object)) + label = GTK_ACCESSIBLE (target_object)->widget; + } + g_object_unref (set); + if (GTK_IS_LABEL (label)) + { + key_val = gtk_label_get_mnemonic_keyval (GTK_LABEL (label)); + if (key_val != GDK_VoidSymbol) + return_value = gtk_accelerator_name (key_val, GDK_MOD1_MASK); + } + g_free (range->activate_keybinding); + range->activate_keybinding = return_value; + } + return return_value; +} + +static G_CONST_RETURN gchar* +gail_range_action_get_name (AtkAction *action, + gint i) +{ + G_CONST_RETURN gchar *return_value; + + if (i==0) + return_value = "activate"; + else + return_value = NULL; + + return return_value; +} + +static gboolean +gail_range_set_description (AtkAction *action, + gint i, + const gchar *desc) +{ + GailRange *range; + gchar **value; + + range = GAIL_RANGE (action); + + if (i==0) + value = &range->activate_description; + else + value = NULL; + + if (value) + { + g_free (*value); + *value = g_strdup (desc); + return TRUE; + } + else + return FALSE; +} + + diff --git a/modules/other/gail/gailrange.h b/modules/other/gail/gailrange.h new file mode 100644 index 000000000..ad6bcd45b --- /dev/null +++ b/modules/other/gail/gailrange.h @@ -0,0 +1,66 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RANGE_H__ +#define __GAIL_RANGE_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RANGE (gail_range_get_type ()) +#define GAIL_RANGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RANGE, GailRange)) +#define GAIL_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_RANGE, GailRangeClass)) +#define GAIL_IS_RANGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RANGE)) +#define GAIL_IS_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RANGE)) +#define GAIL_RANGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RANGE, GailRangeClass)) + +typedef struct _GailRange GailRange; +typedef struct _GailRangeClass GailRangeClass; + +struct _GailRange +{ + GailWidget parent; + + AtkObject *adjustment; + gchar *activate_description; + gchar *activate_keybinding; + guint action_idle_handler; + +}; + +GType gail_range_get_type (void); + +struct _GailRangeClass +{ + GailWidgetClass parent_class; +}; + +AtkObject* gail_range_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_RANGE_H__ */ diff --git a/modules/other/gail/gailrenderercell.c b/modules/other/gail/gailrenderercell.c new file mode 100644 index 000000000..9db9855c0 --- /dev/null +++ b/modules/other/gail/gailrenderercell.c @@ -0,0 +1,112 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailrenderercell.h" + +static void gail_renderer_cell_class_init (GailRendererCellClass *klass); +static void gail_renderer_cell_object_init (GailRendererCell *renderer_cell); + +static void gail_renderer_cell_finalize (GObject *object) +; +static gpointer parent_class = NULL; + +GType +gail_renderer_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRendererCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_renderer_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRendererCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_renderer_cell_object_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CELL, + "GailRendererCell", &tinfo, 0); + } + return type; +} + +static void +gail_renderer_cell_class_init (GailRendererCellClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + klass->property_list = NULL; + + gobject_class->finalize = gail_renderer_cell_finalize; +} + +static void +gail_renderer_cell_object_init (GailRendererCell *renderer_cell) +{ + renderer_cell->renderer = NULL; +} + +static void +gail_renderer_cell_finalize (GObject *object) +{ + GailRendererCell *renderer_cell = GAIL_RENDERER_CELL (object); + + if (renderer_cell->renderer) + g_object_unref (renderer_cell->renderer); + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +gboolean +gail_renderer_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal) +{ + GailRendererCellClass *class = GAIL_RENDERER_CELL_GET_CLASS(cell); + if (class->update_cache) + return (class->update_cache)(cell, emit_change_signal); + return FALSE; +} + +AtkObject* +gail_renderer_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + GailRendererCell *cell; + + object = g_object_new (GAIL_TYPE_RENDERER_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + cell = GAIL_RENDERER_CELL(object); + + return atk_object; +} diff --git a/modules/other/gail/gailrenderercell.h b/modules/other/gail/gailrenderercell.h new file mode 100644 index 000000000..f380647f2 --- /dev/null +++ b/modules/other/gail/gailrenderercell.h @@ -0,0 +1,65 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RENDERER_CELL_H__ +#define __GAIL_RENDERER_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailcell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RENDERER_CELL (gail_renderer_cell_get_type ()) +#define GAIL_RENDERER_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RENDERER_CELL, GailRendererCell)) +#define GAIL_RENDERER_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_RENDERER_CELL, GailRendererCellClass)) +#define GAIL_IS_RENDERER_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RENDERER_CELL)) +#define GAIL_IS_RENDERER_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RENDERER_CELL)) +#define GAIL_RENDERER_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RENDERER_CELL, GailRendererCellClass)) + +typedef struct _GailRendererCell GailRendererCell; +typedef struct _GailRendererCellClass GailRendererCellClass; + +struct _GailRendererCell +{ + GailCell parent; + GtkCellRenderer *renderer; +}; + +GType gail_renderer_cell_get_type (void); + +struct _GailRendererCellClass +{ + GailCellClass parent_class; + gchar **property_list; + gboolean (*update_cache)(GailRendererCell *cell, gboolean emit_change_signal); +}; + +gboolean +gail_renderer_cell_update_cache (GailRendererCell *cell, gboolean emit_change_signal); + +AtkObject *gail_renderer_cell_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_TEXT_CELL_H__ */ diff --git a/modules/other/gail/gailrenderercellfactory.c b/modules/other/gail/gailrenderercellfactory.c new file mode 100644 index 000000000..f7a6aa57b --- /dev/null +++ b/modules/other/gail/gailrenderercellfactory.c @@ -0,0 +1,78 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkcellrenderer.h> +#include "gailrenderercellfactory.h" +#include "gailrenderercell.h" + +static void gail_renderer_cell_factory_class_init (GailRendererCellFactoryClass *klass); + +static AtkObject* gail_renderer_cell_factory_create_accessible ( + GObject *obj); +static GType gail_renderer_cell_factory_get_accessible_type (void); + +GType +gail_renderer_cell_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRendererCellFactoryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_renderer_cell_factory_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailRendererCellFactory), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + "GailRendererCellFactory" , &tinfo, 0); + } + + return type; +} + +static void +gail_renderer_cell_factory_class_init (GailRendererCellFactoryClass *klass) +{ + AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass); + + class->create_accessible = gail_renderer_cell_factory_create_accessible; + class->get_accessible_type = gail_renderer_cell_factory_get_accessible_type; +} + +static AtkObject* +gail_renderer_cell_factory_create_accessible (GObject *obj) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (obj), NULL); + + return gail_renderer_cell_new (); +} + +static GType +gail_renderer_cell_factory_get_accessible_type (void) +{ + return GAIL_TYPE_RENDERER_CELL; +} diff --git a/modules/other/gail/gailrenderercellfactory.h b/modules/other/gail/gailrenderercellfactory.h new file mode 100644 index 000000000..636898126 --- /dev/null +++ b/modules/other/gail/gailrenderercellfactory.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_RENDERER_CELL_FACTORY_H__ +#define __GAIL_RENDERER_CELL_FACTORY_H__ + +#include <atk/atkobjectfactory.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_RENDERER_CELL_FACTORY (gail_renderer_cell_factory_get_type ()) +#define GAIL_RENDERER_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_RENDERER_CELL_FACTORY, GailRendererCellFactory)) +#define GAIL_RENDERER_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AGTK_TYPE_RENDERER_CELL_FACTORY, GailRendererCellFactoryClass)) +#define GAIL_IS_RENDERER_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_RENDERER_CELL_FACTORY)) +#define GAIL_IS_RENDERER_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_RENDERER_CELL_FACTORY)) +#define GAIL_RENDERER_CELL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_RENDERER_CELL_FACTORY, GailRendererCellFactoryClass)) + +typedef struct _GailRendererCellFactory GailRendererCellFactory; +typedef struct _GailRendererCellFactoryClass GailRendererCellFactoryClass; + +struct _GailRendererCellFactory +{ + AtkObjectFactory parent; +}; + +struct _GailRendererCellFactoryClass +{ + AtkObjectFactoryClass parent_class; +}; + +GType gail_renderer_cell_factory_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_RENDERER_CELL_FACTORY_H__ */ diff --git a/modules/other/gail/gailscale.c b/modules/other/gail/gailscale.c new file mode 100644 index 000000000..1a6fefd99 --- /dev/null +++ b/modules/other/gail/gailscale.c @@ -0,0 +1,544 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailscale.h" +#include <libgail-util/gailmisc.h> + +static void gail_scale_class_init (GailScaleClass *klass); + +static void gail_scale_real_initialize (AtkObject *obj, + gpointer data); +static void gail_scale_notify (GObject *obj, + GParamSpec *pspec); +static void gail_scale_finalize (GObject *object); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_scale_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_scale_get_character_at_offset + (AtkText *text, + gint offset); +static gchar* gail_scale_get_text_before_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_scale_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_scale_get_text_after_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_scale_get_character_count (AtkText *text); +static void gail_scale_get_character_extents + (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_scale_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_scale_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_scale_get_default_attributes + (AtkText *text); + +static gpointer parent_class = NULL; + +GType +gail_scale_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailRangeClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_scale_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailScale), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_RANGE, + "GailScale", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + return type; +} + +static void +gail_scale_class_init (GailScaleClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->initialize = gail_scale_real_initialize; + + gobject_class->finalize = gail_scale_finalize; + gobject_class->notify = gail_scale_notify; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_scale_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_RANGE (widget), NULL); + + object = g_object_new (GAIL_TYPE_SCALE, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_scale_real_initialize (AtkObject *obj, + gpointer data) +{ + GailScale *gail_scale; + const gchar *txt; + PangoLayout *layout; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gail_scale = GAIL_SCALE (obj); + gail_scale->textutil = gail_text_util_new (); + + layout = gtk_scale_get_layout (GTK_SCALE (data)); + if (layout) + { + txt = pango_layout_get_text (layout); + if (txt) + { + gail_text_util_text_setup (gail_scale->textutil, txt); + } + } +} + +static void +gail_scale_finalize (GObject *object) +{ + GailScale *scale = GAIL_SCALE (object); + + g_object_unref (scale->textutil); + G_OBJECT_CLASS (parent_class)->finalize (object); + +} + +static void +gail_scale_notify (GObject *obj, + GParamSpec *pspec) +{ + GailScale *scale = GAIL_SCALE (obj); + + if (strcmp (pspec->name, "accessible-value") == 0) + { + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget) + { + GtkScale *gtk_scale; + PangoLayout *layout; + const gchar *txt; + + gtk_scale = GTK_SCALE (widget); + layout = gtk_scale_get_layout (gtk_scale); + if (layout) + { + txt = pango_layout_get_text (layout); + if (txt) + { + g_signal_emit_by_name (obj, "text_changed::delete", 0, + gtk_text_buffer_get_char_count (scale->textutil->buffer)); + gail_text_util_text_setup (scale->textutil, txt); + g_signal_emit_by_name (obj, "text_changed::insert", 0, + g_utf8_strlen (txt, -1)); + } + } + } + } + G_OBJECT_CLASS (parent_class)->notify (obj, pspec); +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_scale_get_text; + iface->get_character_at_offset = gail_scale_get_character_at_offset; + iface->get_text_before_offset = gail_scale_get_text_before_offset; + iface->get_text_at_offset = gail_scale_get_text_at_offset; + iface->get_text_after_offset = gail_scale_get_text_after_offset; + iface->get_character_count = gail_scale_get_character_count; + iface->get_character_extents = gail_scale_get_character_extents; + iface->get_offset_at_point = gail_scale_get_offset_at_point; + iface->get_run_attributes = gail_scale_get_run_attributes; + iface->get_default_attributes = gail_scale_get_default_attributes; +} + +static gchar* +gail_scale_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GailScale *scale; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + scale = GAIL_SCALE (text); + return gail_text_util_get_substring (scale->textutil, + start_pos, end_pos); +} + +static gchar* +gail_scale_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailScale *scale; + PangoLayout *layout; + gchar *txt; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + scale = GAIL_SCALE (text); + layout = gtk_scale_get_layout (GTK_SCALE (widget)); + if (layout) + { + txt = gail_text_util_get_text (scale->textutil, + layout, GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); + } + else + txt = NULL; + + return txt; +} + +static gchar* +gail_scale_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailScale *scale; + PangoLayout *layout; + gchar *txt; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + scale = GAIL_SCALE (text); + layout = gtk_scale_get_layout (GTK_SCALE (widget)); + if (layout) + { + txt = gail_text_util_get_text (scale->textutil, + layout, GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); + } + else + txt = NULL; + + return txt; +} + +static gchar* +gail_scale_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GailScale *scale; + PangoLayout *layout; + gchar *txt; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + scale = GAIL_SCALE (text); + layout = gtk_scale_get_layout (GTK_SCALE (widget)); + if (layout) + { + txt = gail_text_util_get_text (scale->textutil, + layout, GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); + } + else + txt = NULL; + + return txt; +} + +static gint +gail_scale_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GailScale *scale; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + scale = GAIL_SCALE (text); + if (scale->textutil->buffer) + return gtk_text_buffer_get_char_count (scale->textutil->buffer); + else + return 0; + +} + +static void +gail_scale_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkScale *scale; + PangoRectangle char_rect; + PangoLayout *layout; + gint index, x_layout, y_layout; + const gchar *scale_text; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + scale = GTK_SCALE (widget); + layout = gtk_scale_get_layout (scale); + if (!layout) + return; + scale_text = pango_layout_get_text (layout); + if (!scale_text) + return; + index = g_utf8_offset_to_pointer (scale_text, offset) - scale_text; + gtk_scale_get_layout_offsets (scale, &x_layout, &y_layout); + pango_layout_index_to_pos (layout, index, &char_rect); + gail_misc_get_extents_from_pango_rectangle (widget, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_scale_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkScale *scale; + PangoLayout *layout; + gint index, x_layout, y_layout; + const gchar *scale_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + scale = GTK_SCALE (widget); + layout = gtk_scale_get_layout (scale); + if (!layout) + return -1; + scale_text = pango_layout_get_text (layout); + if (!scale_text) + return -1; + + gtk_scale_get_layout_offsets (scale, &x_layout, &y_layout); + index = gail_misc_get_index_at_point_in_layout (widget, + layout, + x_layout, y_layout, x, y, coords); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + index = g_utf8_strlen (scale_text, -1); + } + else + index = g_utf8_pointer_to_offset (scale_text, scale_text + index); + + return index; +} + +static AtkAttributeSet* +gail_scale_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkScale *scale; + AtkAttributeSet *at_set = NULL; + GtkTextDirection dir; + PangoLayout *layout; + const gchar *scale_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + scale = GTK_SCALE (widget); + + layout = gtk_scale_get_layout (scale); + if (!layout) + return at_set; + scale_text = pango_layout_get_text (layout); + if (!scale_text) + return at_set; + + dir = gtk_widget_get_direction (widget); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + layout, + (gchar *)scale_text, + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_scale_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + AtkAttributeSet *at_set = NULL; + PangoLayout *layout; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + layout = gtk_scale_get_layout (GTK_SCALE (widget)); + if (layout) + { + at_set = gail_misc_get_default_attributes (at_set, + layout, + widget); + } + return at_set; +} + +static gunichar +gail_scale_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkScale *scale; + PangoLayout *layout; + const gchar *string; + gchar *index; + gunichar c; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + scale = GTK_SCALE (widget); + + layout = gtk_scale_get_layout (scale); + if (!layout) + return '\0'; + string = pango_layout_get_text (layout); + if (offset >= g_utf8_strlen (string, -1)) + c = '\0'; + else + { + index = g_utf8_offset_to_pointer (string, offset); + + c = g_utf8_get_char (index); + } + + return c; +} diff --git a/modules/other/gail/gailscale.h b/modules/other/gail/gailscale.h new file mode 100644 index 000000000..22638a3d1 --- /dev/null +++ b/modules/other/gail/gailscale.h @@ -0,0 +1,62 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2004 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SCALE_H__ +#define __GAIL_SCALE_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailrange.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SCALE (gail_scale_get_type ()) +#define GAIL_SCALE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SCALE, GailScale)) +#define GAIL_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SCALE, GailScaleClass)) +#define GAIL_IS_SCALE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SCALE)) +#define GAIL_IS_SCALE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SCALE)) +#define GAIL_SCALE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SCALE, GailScaleClass)) + +typedef struct _GailScale GailScale; +typedef struct _GailScaleClass GailScaleClass; + +struct _GailScale +{ + GailRange parent; + + GailTextUtil *textutil; +}; + +GType gail_scale_get_type (void); + +struct _GailScaleClass +{ + GailRangeClass parent_class; +}; + +AtkObject* gail_scale_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SCALE_H__ */ diff --git a/modules/other/gail/gailscrollbar.c b/modules/other/gail/gailscrollbar.c new file mode 100644 index 000000000..da17889c3 --- /dev/null +++ b/modules/other/gail/gailscrollbar.c @@ -0,0 +1,134 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailscrollbar.h" + +static void gail_scrollbar_class_init (GailScrollbarClass *klass); + +static gint gail_scrollbar_get_index_in_parent (AtkObject *accessible); + +static GailRangeClass *parent_class = NULL; + +GType +gail_scrollbar_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailScrollbarClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_scrollbar_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailScrollbar), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_RANGE, + "GailScrollbar", &tinfo, 0); + } + return type; +} + +static void +gail_scrollbar_class_init (GailScrollbarClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->get_index_in_parent = gail_scrollbar_get_index_in_parent; + + parent_class = g_type_class_peek_parent (klass); +} + +AtkObject* +gail_scrollbar_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_SCROLLBAR (widget), NULL); + + object = g_object_new (GAIL_TYPE_SCROLLBAR, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_SCROLL_BAR; + + return accessible; +} + +static gint +gail_scrollbar_get_index_in_parent (AtkObject *accessible) +{ + GtkWidget *widget; + GtkScrolledWindow *scrolled_window; + gint n_children; + GList *children; + + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + { + /* + * State is defunct + */ + return -1; + } + g_return_val_if_fail (GTK_IS_SCROLLBAR (widget), -1); + + if (!GTK_IS_SCROLLED_WINDOW(widget->parent)) + return ATK_OBJECT_CLASS (parent_class)->get_index_in_parent (accessible); + + scrolled_window = GTK_SCROLLED_WINDOW (widget->parent); + children = gtk_container_get_children (GTK_CONTAINER (scrolled_window)); + n_children = g_list_length (children); + g_list_free (children); + + if (GTK_IS_HSCROLLBAR (widget)) + { + if (!scrolled_window->hscrollbar_visible) + { + n_children = -1; + } + } + else if (GTK_IS_VSCROLLBAR (widget)) + { + if (!scrolled_window->vscrollbar_visible) + { + n_children = -1; + } + else if (scrolled_window->hscrollbar_visible) + { + n_children++; + } + } + else + { + n_children = -1; + } + return n_children; +} diff --git a/modules/other/gail/gailscrollbar.h b/modules/other/gail/gailscrollbar.h new file mode 100644 index 000000000..985a71641 --- /dev/null +++ b/modules/other/gail/gailscrollbar.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SCROLLBAR_H__ +#define __GAIL_SCROLLBAR_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailrange.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SCROLLBAR (gail_scrollbar_get_type ()) +#define GAIL_SCROLLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SCROLLBAR, GailScrollbar)) +#define GAIL_SCROLLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SCROLLBAR, GailScrollbarClass)) +#define GAIL_IS_SCROLLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SCROLLBAR)) +#define GAIL_IS_SCROLLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SCROLLBAR)) +#define GAIL_SCROLLBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SCROLLBAR, GailScrollbarClass)) + +typedef struct _GailScrollbar GailScrollbar; +typedef struct _GailScrollbarClass GailScrollbarClass; + +struct _GailScrollbar +{ + GailRange parent; +}; + +GType gail_scrollbar_get_type (void); + +struct _GailScrollbarClass +{ + GailRangeClass parent_class; +}; + +AtkObject* gail_scrollbar_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SCROLLBAR_H__ */ diff --git a/modules/other/gail/gailscrolledwindow.c b/modules/other/gail/gailscrolledwindow.c new file mode 100644 index 000000000..024bc2812 --- /dev/null +++ b/modules/other/gail/gailscrolledwindow.c @@ -0,0 +1,243 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailscrolledwindow.h" + + +static void gail_scrolled_window_class_init (GailScrolledWindowClass *klass); +static void gail_scrolled_window_real_initialize + (AtkObject *obj, + gpointer data); + +static gint gail_scrolled_window_get_n_children (AtkObject *object); +static AtkObject* gail_scrolled_window_ref_child (AtkObject *obj, + gint child); +static void gail_scrolled_window_scrollbar_visibility_changed + (GObject *object, + GParamSpec *pspec, + gpointer user_data); + +static GailContainerClass *parent_class = NULL; + +GType +gail_scrolled_window_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailScrolledWindowClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_scrolled_window_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailScrolledWindow), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailScrolledWindow", &tinfo, 0); + } + return type; +} + +static void +gail_scrolled_window_class_init (GailScrolledWindowClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->get_n_children = gail_scrolled_window_get_n_children; + class->ref_child = gail_scrolled_window_ref_child; + class->initialize = gail_scrolled_window_real_initialize; +} + +AtkObject* +gail_scrolled_window_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_SCROLLED_WINDOW (widget), NULL); + + object = g_object_new (GAIL_TYPE_SCROLLED_WINDOW, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_scrolled_window_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkScrolledWindow *window; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + window = GTK_SCROLLED_WINDOW (data); + g_signal_connect_data (window->hscrollbar, "notify::visible", + (GCallback)gail_scrolled_window_scrollbar_visibility_changed, + obj, NULL, FALSE); + g_signal_connect_data (window->vscrollbar, "notify::visible", + (GCallback)gail_scrolled_window_scrollbar_visibility_changed, + obj, NULL, FALSE); + + obj->role = ATK_ROLE_SCROLL_PANE; +} + +static gint +gail_scrolled_window_get_n_children (AtkObject *object) +{ + GtkWidget *widget; + GtkScrolledWindow *gtk_window; + GList *children; + gint n_children; + + widget = GTK_ACCESSIBLE (object)->widget; + if (widget == NULL) + /* Object is defunct */ + return 0; + + gtk_window = GTK_SCROLLED_WINDOW (widget); + + /* Get the number of children returned by the backing GtkScrolledWindow */ + + children = gtk_container_get_children (GTK_CONTAINER(gtk_window)); + n_children = g_list_length (children); + g_list_free (children); + + /* Add one to the count for each visible scrollbar */ + + if (gtk_window->hscrollbar_visible) + n_children++; + if (gtk_window->vscrollbar_visible) + n_children++; + return n_children; +} + +static AtkObject * +gail_scrolled_window_ref_child (AtkObject *obj, + gint child) +{ + GtkWidget *widget; + GtkScrolledWindow *gtk_window; + GList *children, *tmp_list; + gint n_children; + AtkObject *accessible = NULL; + + g_return_val_if_fail (child >= 0, NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* Object is defunct */ + return NULL; + + gtk_window = GTK_SCROLLED_WINDOW (widget); + + children = gtk_container_get_children (GTK_CONTAINER (gtk_window)); + n_children = g_list_length (children); + + if (child == n_children) + { + if (gtk_window->hscrollbar_visible) + accessible = gtk_widget_get_accessible (gtk_window->hscrollbar); + else if (gtk_window->vscrollbar_visible) + accessible = gtk_widget_get_accessible (gtk_window->vscrollbar); + } + else if (child == n_children+1 && + gtk_window->hscrollbar_visible && + gtk_window->vscrollbar_visible) + accessible = gtk_widget_get_accessible (gtk_window->vscrollbar); + else if (child < n_children) + { + tmp_list = g_list_nth (children, child); + if (tmp_list) + accessible = gtk_widget_get_accessible ( + GTK_WIDGET (tmp_list->data)); + } + + g_list_free (children); + if (accessible) + g_object_ref (accessible); + return accessible; +} + +static void +gail_scrolled_window_scrollbar_visibility_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + if (!strcmp (pspec->name, "visible")) + { + gint index; + gint n_children; + gboolean child_added = FALSE; + GList *children; + AtkObject *child; + GtkScrolledWindow *gtk_window; + GailScrolledWindow *gail_window = GAIL_SCROLLED_WINDOW (user_data); + gchar *signal_name; + + gtk_window = GTK_SCROLLED_WINDOW (GTK_ACCESSIBLE (user_data)->widget); + if (gtk_window == NULL) + return; + children = gtk_container_get_children (GTK_CONTAINER (gtk_window)); + index = n_children = g_list_length (children); + g_list_free (children); + + if ((gpointer) object == (gpointer) (gtk_window->hscrollbar)) + { + if (gtk_window->hscrollbar_visible) + child_added = TRUE; + + child = gtk_widget_get_accessible (gtk_window->hscrollbar); + } + else if ((gpointer) object == (gpointer) (gtk_window->vscrollbar)) + { + if (gtk_window->vscrollbar_visible) + child_added = TRUE; + + child = gtk_widget_get_accessible (gtk_window->vscrollbar); + if (gtk_window->hscrollbar_visible) + index = n_children+1; + } + else + { + g_assert_not_reached (); + return; + } + + if (child_added) + signal_name = "children_changed::add"; + else + signal_name = "children_changed::delete"; + + g_signal_emit_by_name (gail_window, signal_name, index, child, NULL); + } +} diff --git a/modules/other/gail/gailscrolledwindow.h b/modules/other/gail/gailscrolledwindow.h new file mode 100644 index 000000000..0812823a6 --- /dev/null +++ b/modules/other/gail/gailscrolledwindow.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SCROLLED_WINDOW_H__ +#define __GAIL_SCROLLED_WINDOW_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SCROLLED_WINDOW (gail_scrolled_window_get_type ()) +#define GAIL_SCROLLED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SCROLLED_WINDOW, GailScrolledWindow)) +#define GAIL_SCROLLED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SCROLLED_WINDOW, GailScrolledWindowClass)) +#define GAIL_IS_SCROLLED_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SCROLLED_WINDOW)) +#define GAIL_IS_SCROLLED_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SCROLLED_WINDOW)) +#define GAIL_SCROLLED_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SCROLLED_WINDOW, GailScrolledWindowClass)) + +typedef struct _GailScrolledWindow GailScrolledWindow; +typedef struct _GailScrolledWindowClass GailScrolledWindowClass; + +struct _GailScrolledWindow +{ + GailContainer parent; +}; + +GType gail_scrolled_window_get_type (void); + +struct _GailScrolledWindowClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_scrolled_window_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SCROLLED_WINDOW_H__ */ diff --git a/modules/other/gail/gailseparator.c b/modules/other/gail/gailseparator.c new file mode 100644 index 000000000..87eaa5fc1 --- /dev/null +++ b/modules/other/gail/gailseparator.c @@ -0,0 +1,103 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailseparator.h" + +static void gail_separator_class_init (GailSeparatorClass *klass); +static AtkStateSet* gail_separator_ref_state_set (AtkObject *accessible); + +static GailWidgetClass *parent_class = NULL; + +GType +gail_separator_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailSeparatorClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_separator_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailSeparator), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_WIDGET, + "GailSeparator", &tinfo, 0); + } + return type; +} + +static void +gail_separator_class_init (GailSeparatorClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_separator_ref_state_set; +} + +AtkObject* +gail_separator_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_SEPARATOR (widget), NULL); + + object = g_object_new (GAIL_TYPE_SEPARATOR, NULL); + + g_return_val_if_fail (GTK_IS_ACCESSIBLE (object), NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + accessible->role = ATK_ROLE_SEPARATOR; + + return accessible; +} + +static AtkStateSet* +gail_separator_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + if (GTK_IS_VSEPARATOR (widget)) + atk_state_set_add_state (state_set, ATK_STATE_VERTICAL); + else if (GTK_IS_HSEPARATOR (widget)) + atk_state_set_add_state (state_set, ATK_STATE_HORIZONTAL); + + return state_set; +} diff --git a/modules/other/gail/gailseparator.h b/modules/other/gail/gailseparator.h new file mode 100644 index 000000000..765b73cf3 --- /dev/null +++ b/modules/other/gail/gailseparator.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SEPARATOR_H__ +#define __GAIL_SEPARATOR_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailwidget.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SEPARATOR (gail_separator_get_type ()) +#define GAIL_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SEPARATOR, GailSeparator)) +#define GAIL_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SEPARATOR, GailSeparatorClass)) +#define GAIL_IS_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SEPARATOR)) +#define GAIL_IS_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SEPARATOR)) +#define GAIL_SEPARATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SEPARATOR, GailSeparatorClass)) + +typedef struct _GailSeparator GailSeparator; +typedef struct _GailSeparatorClass GailSeparatorClass; + +struct _GailSeparator +{ + GailWidget parent; +}; + +GType gail_separator_get_type (void); + +struct _GailSeparatorClass +{ + GailWidgetClass parent_class; +}; + +AtkObject *gail_separator_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SEPARATOR_H__ */ diff --git a/modules/other/gail/gailspinbutton.c b/modules/other/gail/gailspinbutton.c new file mode 100644 index 000000000..d38d1c209 --- /dev/null +++ b/modules/other/gail/gailspinbutton.c @@ -0,0 +1,296 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailspinbutton.h" +#include "gailadjustment.h" +#include "gail-private-macros.h" + +static void gail_spin_button_class_init (GailSpinButtonClass *klass); +static void gail_spin_button_real_initialize (AtkObject *obj, + gpointer data); +static void gail_spin_button_finalize (GObject *object); + +static void atk_value_interface_init (AtkValueIface *iface); + +static void gail_spin_button_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_spin_button_get_current_value (AtkValue *obj, + GValue *value); +static void gail_spin_button_get_maximum_value (AtkValue *obj, + GValue *value); +static void gail_spin_button_get_minimum_value (AtkValue *obj, + GValue *value); +static gboolean gail_spin_button_set_current_value (AtkValue *obj, + const GValue *value); +static void gail_spin_button_value_changed (GtkAdjustment *adjustment, + gpointer data); + +static GailWidgetClass *parent_class = NULL; + +GType +gail_spin_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailSpinButtonClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_spin_button_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailSpinButton), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_value_info = + { + (GInterfaceInitFunc) atk_value_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_ENTRY, + "GailSpinButton", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_VALUE, + &atk_value_info); + } + + return type; +} + +static void +gail_spin_button_class_init (GailSpinButtonClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (klass); + + widget_class = (GailWidgetClass*)klass; + + widget_class->notify_gtk = gail_spin_button_real_notify_gtk; + + class->initialize = gail_spin_button_real_initialize; + + gobject_class->finalize = gail_spin_button_finalize; +} + +AtkObject* +gail_spin_button_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_SPIN_BUTTON (widget), NULL); + + object = g_object_new (GAIL_TYPE_SPIN_BUTTON, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_spin_button_real_initialize (AtkObject *obj, + gpointer data) +{ + GailSpinButton *spin_button = GAIL_SPIN_BUTTON (obj); + GtkSpinButton *gtk_spin_button; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + gtk_spin_button = GTK_SPIN_BUTTON (data); + /* + * If a GtkAdjustment already exists for the spin_button, + * create the GailAdjustment + */ + if (gtk_spin_button->adjustment) + { + spin_button->adjustment = gail_adjustment_new (gtk_spin_button->adjustment); + g_signal_connect (gtk_spin_button->adjustment, + "value-changed", + G_CALLBACK (gail_spin_button_value_changed), + obj); + } + else + spin_button->adjustment = NULL; + + obj->role = ATK_ROLE_SPIN_BUTTON; + +} + +static void +atk_value_interface_init (AtkValueIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_current_value = gail_spin_button_get_current_value; + iface->get_maximum_value = gail_spin_button_get_maximum_value; + iface->get_minimum_value = gail_spin_button_get_minimum_value; + iface->set_current_value = gail_spin_button_set_current_value; +} + +static void +gail_spin_button_get_current_value (AtkValue *obj, + GValue *value) +{ + GailSpinButton *spin_button; + + g_return_if_fail (GAIL_IS_SPIN_BUTTON (obj)); + + spin_button = GAIL_SPIN_BUTTON (obj); + if (spin_button->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_current_value (ATK_VALUE (spin_button->adjustment), value); +} + +static void +gail_spin_button_get_maximum_value (AtkValue *obj, + GValue *value) +{ + GailSpinButton *spin_button; + + g_return_if_fail (GAIL_IS_SPIN_BUTTON (obj)); + + spin_button = GAIL_SPIN_BUTTON (obj); + if (spin_button->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_maximum_value (ATK_VALUE (spin_button->adjustment), value); +} + +static void +gail_spin_button_get_minimum_value (AtkValue *obj, + GValue *value) +{ + GailSpinButton *spin_button; + + g_return_if_fail (GAIL_IS_SPIN_BUTTON (obj)); + + spin_button = GAIL_SPIN_BUTTON (obj); + if (spin_button->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return; + + atk_value_get_minimum_value (ATK_VALUE (spin_button->adjustment), value); +} + +static gboolean +gail_spin_button_set_current_value (AtkValue *obj, + const GValue *value) +{ + GailSpinButton *spin_button; + + g_return_val_if_fail (GAIL_IS_SPIN_BUTTON (obj), FALSE); + + spin_button = GAIL_SPIN_BUTTON (obj); + if (spin_button->adjustment == NULL) + /* + * Adjustment has not been specified + */ + return FALSE; + + return atk_value_set_current_value (ATK_VALUE (spin_button->adjustment), value); +} + +static void +gail_spin_button_finalize (GObject *object) +{ + GailSpinButton *spin_button = GAIL_SPIN_BUTTON (object); + + if (spin_button->adjustment) + { + g_object_unref (spin_button->adjustment); + spin_button->adjustment = NULL; + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static void +gail_spin_button_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget = GTK_WIDGET (obj); + GailSpinButton *spin_button = GAIL_SPIN_BUTTON (gtk_widget_get_accessible (widget)); + + if (strcmp (pspec->name, "adjustment") == 0) + { + /* + * Get rid of the GailAdjustment for the GtkAdjustment + * which was associated with the spin_button. + */ + GtkSpinButton* gtk_spin_button; + + if (spin_button->adjustment) + { + g_object_unref (spin_button->adjustment); + spin_button->adjustment = NULL; + } + /* + * Create the GailAdjustment when notify for "adjustment" property + * is received + */ + gtk_spin_button = GTK_SPIN_BUTTON (widget); + spin_button->adjustment = gail_adjustment_new (gtk_spin_button->adjustment); + g_signal_connect (gtk_spin_button->adjustment, + "value-changed", + G_CALLBACK (gail_spin_button_value_changed), + spin_button); + } + else + parent_class->notify_gtk (obj, pspec); +} + + +static void +gail_spin_button_value_changed (GtkAdjustment *adjustment, + gpointer data) +{ + GailSpinButton *spin_button; + + gail_return_if_fail (adjustment != NULL); + gail_return_if_fail (data != NULL); + + spin_button = GAIL_SPIN_BUTTON (data); + + g_object_notify (G_OBJECT (spin_button), "accessible-value"); +} + diff --git a/modules/other/gail/gailspinbutton.h b/modules/other/gail/gailspinbutton.h new file mode 100644 index 000000000..90e8f4ab7 --- /dev/null +++ b/modules/other/gail/gailspinbutton.h @@ -0,0 +1,61 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SPIN_BUTTON_H__ +#define __GAIL_SPIN_BUTTON_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailentry.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SPIN_BUTTON (gail_spin_button_get_type ()) +#define GAIL_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SPIN_BUTTON, GailSpinButton)) +#define GAIL_SPIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SPIN_BUTTON, GailSpinButtonClass)) +#define GAIL_IS_SPIN_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SPIN_BUTTON)) +#define GAIL_IS_SPIN_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SPIN_BUTTON)) +#define GAIL_SPIN_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SPIN_BUTTON, GailSpinButtonClass)) + +typedef struct _GailSpinButton GailSpinButton; +typedef struct _GailSpinButtonClass GailSpinButtonClass; + +struct _GailSpinButton +{ + GailEntry parent; + + AtkObject *adjustment; +}; + +GType gail_spin_button_get_type (void); + +struct _GailSpinButtonClass +{ + GailEntryClass parent_class; +}; + +AtkObject* gail_spin_button_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SPIN_BUTTON_H__ */ diff --git a/modules/other/gail/gailstatusbar.c b/modules/other/gail/gailstatusbar.c new file mode 100644 index 000000000..aca7774a5 --- /dev/null +++ b/modules/other/gail/gailstatusbar.c @@ -0,0 +1,680 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailstatusbar.h" +#include <libgail-util/gailmisc.h> + +static void gail_statusbar_class_init (GailStatusbarClass *klass); +static G_CONST_RETURN gchar* gail_statusbar_get_name (AtkObject *obj); +static gint gail_statusbar_get_n_children (AtkObject *obj); +static AtkObject* gail_statusbar_ref_child (AtkObject *obj, + gint i); +static void gail_statusbar_real_initialize (AtkObject *obj, + gpointer data); + +static gint gail_statusbar_notify (GObject *obj, + GParamSpec *pspec, + gpointer user_data); +static void gail_statusbar_finalize (GObject *object); +static void gail_statusbar_init_textutil (GailStatusbar *statusbar, + GtkWidget *label); + +/* atktext.h */ +static void atk_text_interface_init (AtkTextIface *iface); + +static gchar* gail_statusbar_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_statusbar_get_character_at_offset + (AtkText *text, + gint offset); +static gchar* gail_statusbar_get_text_before_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_statusbar_get_text_at_offset(AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_statusbar_get_text_after_offset + (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_statusbar_get_character_count (AtkText *text); +static void gail_statusbar_get_character_extents + (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_statusbar_get_offset_at_point(AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_statusbar_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_statusbar_get_default_attributes + (AtkText *text); +static GtkWidget* get_label_from_statusbar (GtkWidget *statusbar); + +static GailContainerClass* parent_class = NULL; + +GType +gail_statusbar_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailStatusbarClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_statusbar_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailStatusbar), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailStatusbar", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + } + return type; +} + +static void +gail_statusbar_class_init (GailStatusbarClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailContainerClass *container_class; + + container_class = (GailContainerClass*)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_statusbar_finalize; + + class->get_name = gail_statusbar_get_name; + class->get_n_children = gail_statusbar_get_n_children; + class->ref_child = gail_statusbar_ref_child; + class->initialize = gail_statusbar_real_initialize; + /* + * As we report the statusbar as having no children we are not interested + * in add and remove signals + */ + container_class->add_gtk = NULL; + container_class->remove_gtk = NULL; +} + +AtkObject* +gail_statusbar_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_STATUSBAR (widget), NULL); + + object = g_object_new (GAIL_TYPE_STATUSBAR, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static G_CONST_RETURN gchar* +gail_statusbar_get_name (AtkObject *obj) +{ + G_CONST_RETURN gchar* name; + + g_return_val_if_fail (GAIL_IS_STATUSBAR (obj), NULL); + + name = ATK_OBJECT_CLASS (parent_class)->get_name (obj); + if (name != NULL) + return name; + else + { + /* + * Get the text on the label + */ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + g_return_val_if_fail (GTK_IS_STATUSBAR (widget), NULL); + label = get_label_from_statusbar (widget); + if (GTK_IS_LABEL (label)) + return gtk_label_get_label (GTK_LABEL (label)); + else + return NULL; + } +} + +static gint +gail_statusbar_get_n_children (AtkObject *obj) +{ + GtkWidget *widget; + GList *children; + gint count = 0; + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return 0; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + if (children != NULL) + { + count = g_list_length (children); + g_list_free (children); + } + + return count; +} + +static AtkObject* +gail_statusbar_ref_child (AtkObject *obj, + gint i) +{ + GList *children, *tmp_list; + AtkObject *accessible; + GtkWidget *widget; + + g_return_val_if_fail ((i >= 0), NULL); + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + return NULL; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + if (children == NULL) + return NULL; + + tmp_list = g_list_nth (children, i); + if (!tmp_list) + { + g_list_free (children); + return NULL; + } + accessible = gtk_widget_get_accessible (GTK_WIDGET (tmp_list->data)); + + g_list_free (children); + g_object_ref (accessible); + return accessible; +} + +static void +gail_statusbar_real_initialize (AtkObject *obj, + gpointer data) +{ + GailStatusbar *statusbar = GAIL_STATUSBAR (obj); + GtkWidget *label; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + /* + * We get notified of changes to the label + */ + label = get_label_from_statusbar (GTK_WIDGET (data)); + if (GTK_IS_LABEL (label)) + { + gail_statusbar_init_textutil (statusbar, label); + } + + obj->role = ATK_ROLE_STATUSBAR; + +} + +static gint +gail_statusbar_notify (GObject *obj, + GParamSpec *pspec, + gpointer user_data) +{ + AtkObject *atk_obj = ATK_OBJECT (user_data); + GtkLabel *label; + GailStatusbar *statusbar; + + if (strcmp (pspec->name, "label") == 0) + { + const gchar* label_text; + + label = GTK_LABEL (obj); + + label_text = gtk_label_get_text (label); + + statusbar = GAIL_STATUSBAR (atk_obj); + gail_text_util_text_setup (statusbar->textutil, label_text); + + if (atk_obj->name == NULL) + { + /* + * The label has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (atk_obj), "accessible-name"); + } + /* + * The label is the only property which can be changed + */ + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + } + return 1; +} + +static void +gail_statusbar_init_textutil (GailStatusbar *statusbar, + GtkWidget *label) +{ + const gchar *label_text; + + statusbar->textutil = gail_text_util_new (); + label_text = gtk_label_get_text (GTK_LABEL (label)); + gail_text_util_text_setup (statusbar->textutil, label_text); + g_signal_connect (label, + "notify", + (GCallback) gail_statusbar_notify, + statusbar); +} + +static void +gail_statusbar_finalize (GObject *object) +{ + GailStatusbar *statusbar = GAIL_STATUSBAR (object); + + if (statusbar->textutil) + { + g_object_unref (statusbar->textutil); + } + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_statusbar_get_text; + iface->get_character_at_offset = gail_statusbar_get_character_at_offset; + iface->get_text_before_offset = gail_statusbar_get_text_before_offset; + iface->get_text_at_offset = gail_statusbar_get_text_at_offset; + iface->get_text_after_offset = gail_statusbar_get_text_after_offset; + iface->get_character_count = gail_statusbar_get_character_count; + iface->get_character_extents = gail_statusbar_get_character_extents; + iface->get_offset_at_point = gail_statusbar_get_offset_at_point; + iface->get_run_attributes = gail_statusbar_get_run_attributes; + iface->get_default_attributes = gail_statusbar_get_default_attributes; +} + +static gchar* +gail_statusbar_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkWidget *widget; + GtkWidget *label; + GailStatusbar *statusbar; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL (label)) + return NULL; + + statusbar = GAIL_STATUSBAR (text); + if (!statusbar->textutil) + gail_statusbar_init_textutil (statusbar, label); + + label_text = gtk_label_get_text (GTK_LABEL (label)); + + if (label_text == NULL) + return NULL; + else + { + return gail_text_util_get_substring (statusbar->textutil, + start_pos, end_pos); + } +} + +static gchar* +gail_statusbar_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailStatusbar *statusbar; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + statusbar = GAIL_STATUSBAR (text); + if (!statusbar->textutil) + gail_statusbar_init_textutil (statusbar, label); + + return gail_text_util_get_text (statusbar->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_BEFORE_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_statusbar_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailStatusbar *statusbar; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Get label */ + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + statusbar = GAIL_STATUSBAR (text); + if (!statusbar->textutil) + gail_statusbar_init_textutil (statusbar, label); + + return gail_text_util_get_text (statusbar->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AT_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_statusbar_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + GailStatusbar *statusbar; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + { + /* State is defunct */ + return NULL; + } + + /* Get label */ + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + statusbar = GAIL_STATUSBAR (text); + if (!statusbar->textutil) + gail_statusbar_init_textutil (statusbar, label); + + return gail_text_util_get_text (statusbar->textutil, + gtk_label_get_layout (GTK_LABEL (label)), GAIL_AFTER_OFFSET, + boundary_type, offset, start_offset, end_offset); +} + +static gint +gail_statusbar_get_character_count (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return 0; + + return g_utf8_strlen (gtk_label_get_text (GTK_LABEL (label)), -1); +} + +static void +gail_statusbar_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + PangoRectangle char_rect; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + + if (widget == NULL) + /* State is defunct */ + return; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + label_text = gtk_label_get_text (GTK_LABEL (label)); + index = g_utf8_offset_to_pointer (label_text, offset) - label_text; + pango_layout_index_to_pos (gtk_label_get_layout (GTK_LABEL (label)), index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (label, &char_rect, + x_layout, y_layout, x, y, width, height, coords); +} + +static gint +gail_statusbar_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkWidget *widget; + GtkWidget *label; + gint index, x_layout, y_layout; + const gchar *label_text; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return -1; + + gtk_label_get_layout_offsets (GTK_LABEL (label), &x_layout, &y_layout); + + index = gail_misc_get_index_at_point_in_layout (label, + gtk_label_get_layout (GTK_LABEL (label)), + x_layout, y_layout, x, y, coords); + label_text = gtk_label_get_text (GTK_LABEL (label)); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (label_text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (label_text, label_text + index); +} + +static AtkAttributeSet* +gail_statusbar_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + GtkJustification justify; + GtkTextDirection dir; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + /* Get values set for entire label, if any */ + justify = gtk_label_get_justify (GTK_LABEL (label)); + if (justify != GTK_JUSTIFY_CENTER) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, justify))); + } + dir = gtk_widget_get_direction (label); + if (dir == GTK_TEXT_DIR_RTL) + { + at_set = gail_misc_add_attribute (at_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, dir))); + } + + at_set = gail_misc_layout_get_run_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + (gchar *) gtk_label_get_text (GTK_LABEL (label)), + offset, + start_offset, + end_offset); + return at_set; +} + +static AtkAttributeSet* +gail_statusbar_get_default_attributes (AtkText *text) +{ + GtkWidget *widget; + GtkWidget *label; + AtkAttributeSet *at_set = NULL; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return NULL; + + at_set = gail_misc_get_default_attributes (at_set, + gtk_label_get_layout (GTK_LABEL (label)), + widget); + return at_set; +} + +static gunichar +gail_statusbar_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkWidget *label; + const gchar *string; + gchar *index; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return '\0'; + + label = get_label_from_statusbar (widget); + + if (!GTK_IS_LABEL(label)) + return '\0'; + string = gtk_label_get_text (GTK_LABEL (label)); + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + +static GtkWidget* +get_label_from_statusbar (GtkWidget *statusbar) +{ + return GTK_STATUSBAR (statusbar)->label; +} diff --git a/modules/other/gail/gailstatusbar.h b/modules/other/gail/gailstatusbar.h new file mode 100644 index 000000000..43d25f33f --- /dev/null +++ b/modules/other/gail/gailstatusbar.h @@ -0,0 +1,62 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_STATUSBAR_H__ +#define __GAIL_STATUSBAR_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_STATUSBAR (gail_statusbar_get_type ()) +#define GAIL_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_STATUSBAR, GailStatusbar)) +#define GAIL_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_STATUSBAR, GailStatusbarClass)) +#define GAIL_IS_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_STATUSBAR)) +#define GAIL_IS_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_STATUSBAR)) +#define GAIL_STATUSBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_STATUSBAR, GailStatusbarClass)) + +typedef struct _GailStatusbar GailStatusbar; +typedef struct _GailStatusbarClass GailStatusbarClass; + +struct _GailStatusbar +{ + GailContainer parent; + + GailTextUtil *textutil; +}; + +GType gail_statusbar_get_type (void); + +struct _GailStatusbarClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_statusbar_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_STATUSBAR_H__ */ diff --git a/modules/other/gail/gailsubmenuitem.c b/modules/other/gail/gailsubmenuitem.c new file mode 100644 index 000000000..b26df6b9a --- /dev/null +++ b/modules/other/gail/gailsubmenuitem.c @@ -0,0 +1,382 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailsubmenuitem.h" + +static void gail_sub_menu_item_class_init (GailSubMenuItemClass *klass); +static void gail_sub_menu_item_real_initialize (AtkObject *obj, + gpointer data); + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_sub_menu_item_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_sub_menu_item_clear_selection (AtkSelection *selection); +static AtkObject* gail_sub_menu_item_ref_selection (AtkSelection *selection, + gint i); +static gint gail_sub_menu_item_get_selection_count + (AtkSelection *selection); +static gboolean gail_sub_menu_item_is_child_selected + (AtkSelection *selection, + gint i); +static gboolean gail_sub_menu_item_remove_selection (AtkSelection *selection, + gint i); +static gint menu_item_add_gtk (GtkContainer *container, + GtkWidget *widget); +static gint menu_item_remove_gtk (GtkContainer *container, + GtkWidget *widget); + +static gpointer parent_class = NULL; + +GType +gail_sub_menu_item_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailSubMenuItemClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_sub_menu_item_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailSubMenuItem), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_MENU_ITEM, + "GailSubMenuItem", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + } + + return type; +} + +static void +gail_sub_menu_item_class_init (GailSubMenuItemClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + class->initialize = gail_sub_menu_item_real_initialize; + + parent_class = g_type_class_peek_parent (klass); +} + +static void +gail_sub_menu_item_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkWidget *submenu; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (data)); + g_return_if_fail (submenu); + + g_signal_connect (submenu, + "add", + G_CALLBACK (menu_item_add_gtk), + NULL); + g_signal_connect (submenu, + "remove", + G_CALLBACK (menu_item_remove_gtk), + NULL); + + obj->role = ATK_ROLE_MENU; +} + +AtkObject* +gail_sub_menu_item_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), NULL); + + object = g_object_new (GAIL_TYPE_SUB_MENU_ITEM, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->add_selection = gail_sub_menu_item_add_selection; + iface->clear_selection = gail_sub_menu_item_clear_selection; + iface->ref_selection = gail_sub_menu_item_ref_selection; + iface->get_selection_count = gail_sub_menu_item_get_selection_count; + iface->is_child_selected = gail_sub_menu_item_is_child_selected; + iface->remove_selection = gail_sub_menu_item_remove_selection; + /* + * select_all_selection does not make sense for a submenu of a menu item + * so no implementation is provided. + */ +} + +static gboolean +gail_sub_menu_item_add_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + GList *item; + guint length; + GtkWidget *widget; + GtkWidget *submenu; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), FALSE); + shell = GTK_MENU_SHELL (submenu); + length = g_list_length (shell->children); + if (i < 0 || i > length) + return FALSE; + + item = g_list_nth (shell->children, i); + g_return_val_if_fail (item != NULL, FALSE); + g_return_val_if_fail (GTK_IS_MENU_ITEM(item->data), FALSE); + + gtk_menu_shell_select_item (shell, GTK_WIDGET (item->data)); + return TRUE; +} + +static gboolean +gail_sub_menu_item_clear_selection (AtkSelection *selection) +{ + GtkMenuShell *shell; + GtkWidget *widget; + GtkWidget *submenu; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), FALSE); + shell = GTK_MENU_SHELL (submenu); + + gtk_menu_shell_deselect (shell); + return TRUE; +} + +static AtkObject* +gail_sub_menu_item_ref_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + AtkObject *obj; + GtkWidget *widget; + GtkWidget *submenu; + + if (i != 0) + return NULL; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), NULL); + shell = GTK_MENU_SHELL (submenu); + + if (shell->active_menu_item != NULL) + { + obj = gtk_widget_get_accessible (shell->active_menu_item); + g_object_ref (obj); + return obj; + } + else + { + return NULL; + } +} + +static gint +gail_sub_menu_item_get_selection_count (AtkSelection *selection) +{ + GtkMenuShell *shell; + GtkWidget *widget; + GtkWidget *submenu; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), FALSE); + shell = GTK_MENU_SHELL (submenu); + + /* + * Identifies the currently selected menu item + */ + if (shell->active_menu_item == NULL) + return 0; + else + return 1; +} + +static gboolean +gail_sub_menu_item_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + gint j; + GtkWidget *widget; + GtkWidget *submenu; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), FALSE); + shell = GTK_MENU_SHELL (submenu); + + if (shell->active_menu_item == NULL) + return FALSE; + + j = g_list_index (shell->children, shell->active_menu_item); + + return (j==i); +} + +static gboolean +gail_sub_menu_item_remove_selection (AtkSelection *selection, + gint i) +{ + GtkMenuShell *shell; + GtkWidget *widget; + GtkWidget *submenu; + + if (i != 0) + return FALSE; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + g_return_val_if_fail (GTK_IS_MENU_SHELL (submenu), FALSE); + shell = GTK_MENU_SHELL (submenu); + + if (shell->active_menu_item && + GTK_MENU_ITEM (shell->active_menu_item)->submenu) + { + /* + * Menu item contains a menu and it is the selected menu item + * so deselect it. + */ + gtk_menu_shell_deselect (shell); + } + return TRUE; +} + +static gint +menu_item_add_gtk (GtkContainer *container, + GtkWidget *widget) +{ + GtkWidget *parent_widget; + AtkObject *atk_parent; + AtkObject *atk_child; + GailContainer *gail_container; + gint index; + + g_return_val_if_fail (GTK_IS_MENU (container), 1); + + parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container)); + if (GTK_IS_MENU_ITEM (parent_widget)) + { + atk_parent = gtk_widget_get_accessible (parent_widget); + atk_child = gtk_widget_get_accessible (widget); + + gail_container = GAIL_CONTAINER (atk_parent); + g_object_notify (G_OBJECT (atk_child), "accessible_parent"); + + g_list_free (gail_container->children); + gail_container->children = gtk_container_get_children (container); + index = g_list_index (gail_container->children, widget); + g_signal_emit_by_name (atk_parent, "children_changed::add", + index, atk_child, NULL); + } + return 1; +} + +static gint +menu_item_remove_gtk (GtkContainer *container, + GtkWidget *widget) +{ + GtkWidget *parent_widget; + AtkObject *atk_parent; + AtkObject *atk_child; + GailContainer *gail_container; + AtkPropertyValues values = { NULL }; + gint index; + gint list_length; + + g_return_val_if_fail (GTK_IS_MENU (container), 1); + + parent_widget = gtk_menu_get_attach_widget (GTK_MENU (container)); + if (GTK_IS_MENU_ITEM (parent_widget)) + { + atk_parent = gtk_widget_get_accessible (parent_widget); + atk_child = gtk_widget_get_accessible (widget); + + gail_container = GAIL_CONTAINER (atk_parent); + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, atk_parent); + values.property_name = "accessible-parent"; + g_signal_emit_by_name (atk_child, + "property_change::accessible-parent", &values, NULL); + + index = g_list_index (gail_container->children, widget); + list_length = g_list_length (gail_container->children); + g_list_free (gail_container->children); + gail_container->children = gtk_container_get_children (container); + if (index >= 0 && index <= list_length) + g_signal_emit_by_name (atk_parent, "children_changed::remove", + index, atk_child, NULL); + } + return 1; +} diff --git a/modules/other/gail/gailsubmenuitem.h b/modules/other/gail/gailsubmenuitem.h new file mode 100644 index 000000000..91334c587 --- /dev/null +++ b/modules/other/gail/gailsubmenuitem.h @@ -0,0 +1,60 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2002 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_SUB_MENU_ITEM_H__ +#define __GAIL_SUB_MENU_ITEM_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailmenuitem.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_SUB_MENU_ITEM (gail_sub_menu_item_get_type ()) +#define GAIL_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_SUB_MENU_ITEM, GailSubMenuItem)) +#define GAIL_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_SUB_MENU_ITEM, GailSubMenuItemClass)) +#define GAIL_IS_SUB_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_SUB_MENU_ITEM)) +#define GAIL_IS_SUB_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_SUB_MENU_ITEM)) +#define GAIL_SUB_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_SUB_MENU_ITEM, GailSubMenuItemClass)) + +typedef struct _GailSubMenuItem GailSubMenuItem; +typedef struct _GailSubMenuItemClass GailSubMenuItemClass; + +struct _GailSubMenuItem +{ + GailMenuItem parent; + +}; + +GType gail_sub_menu_item_get_type (void); + +struct _GailSubMenuItemClass +{ + GailMenuItemClass parent_class; +}; + +AtkObject* gail_sub_menu_item_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_SUB_MENU_ITEM_H__ */ diff --git a/modules/other/gail/gailtextcell.c b/modules/other/gail/gailtextcell.c new file mode 100644 index 000000000..25c814cb2 --- /dev/null +++ b/modules/other/gail/gailtextcell.c @@ -0,0 +1,696 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "gailtextcell.h" +#include "gailcontainercell.h" +#include "gailcellparent.h" +#include <libgail-util/gailmisc.h> +#include "gail-private-macros.h" + +static void gail_text_cell_class_init (GailTextCellClass *klass); +static void gail_text_cell_init (GailTextCell *text_cell); +static void gail_text_cell_finalize (GObject *object); + +static G_CONST_RETURN gchar* gail_text_cell_get_name (AtkObject *atk_obj); + +static void atk_text_interface_init (AtkTextIface *iface); + +/* atktext.h */ + +static gchar* gail_text_cell_get_text (AtkText *text, + gint start_pos, + gint end_pos); +static gunichar gail_text_cell_get_character_at_offset (AtkText *text, + gint offset); +static gchar* gail_text_cell_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_text_cell_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_text_cell_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gint gail_text_cell_get_character_count (AtkText *text); +static gint gail_text_cell_get_caret_offset (AtkText *text); +static gboolean gail_text_cell_set_caret_offset (AtkText *text, + gint offset); +static void gail_text_cell_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static gint gail_text_cell_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static AtkAttributeSet* gail_text_cell_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet* gail_text_cell_get_default_attributes + (AtkText *text); + +static PangoLayout* create_pango_layout (GtkCellRendererText *gtk_renderer, + GtkWidget *widget); +static void add_attr (PangoAttrList *attr_list, + PangoAttribute *attr); + +/* Misc */ + +static gboolean gail_text_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal); + +gchar *gail_text_cell_property_list[] = { + /* Set font_desc first since it resets other values if it is NULL */ + "font_desc", + + "attributes", + "background_gdk", + "editable", + "family", + "foreground_gdk", + "rise", + "scale", + "size", + "size_points", + "stretch", + "strikethrough", + "style", + "text", + "underline", + "variant", + "weight", + + /* Also need the sets */ + "background_set", + "editable_set", + "family_set", + "foreground_set", + "rise_set", + "scale_set", + "size_set", + "stretch_set", + "strikethrough_set", + "style_set", + "underline_set", + "variant_set", + "weight_set", + NULL +}; + +static gpointer parent_class = NULL; + +GType +gail_text_cell_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailTextCellClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_text_cell_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailTextCell), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_text_cell_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_RENDERER_CELL, + "GailTextCell", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + gail_cell_type_add_action_interface (type); + } + return type; +} + +static void +gail_text_cell_class_init (GailTextCellClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *atk_object_class = ATK_OBJECT_CLASS (klass); + GailRendererCellClass *renderer_cell_class = GAIL_RENDERER_CELL_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + renderer_cell_class->update_cache = gail_text_cell_update_cache; + renderer_cell_class->property_list = gail_text_cell_property_list; + + atk_object_class->get_name = gail_text_cell_get_name; + + gobject_class->finalize = gail_text_cell_finalize; +} + +/* atktext.h */ + +static void +gail_text_cell_init (GailTextCell *text_cell) +{ + text_cell->cell_text = NULL; + text_cell->caret_pos = 0; + text_cell->cell_length = 0; + text_cell->textutil = gail_text_util_new (); + atk_state_set_add_state (GAIL_CELL (text_cell)->state_set, + ATK_STATE_SINGLE_LINE); +} + +AtkObject* +gail_text_cell_new (void) +{ + GObject *object; + AtkObject *atk_object; + GailRendererCell *cell; + + object = g_object_new (GAIL_TYPE_TEXT_CELL, NULL); + + g_return_val_if_fail (object != NULL, NULL); + + atk_object = ATK_OBJECT (object); + atk_object->role = ATK_ROLE_TABLE_CELL; + + cell = GAIL_RENDERER_CELL(object); + + cell->renderer = gtk_cell_renderer_text_new (); + g_object_ref (cell->renderer); + gtk_object_sink (GTK_OBJECT (cell->renderer)); + return atk_object; +} + +static void +gail_text_cell_finalize (GObject *object) +{ + GailTextCell *text_cell = GAIL_TEXT_CELL (object); + + g_object_unref (text_cell->textutil); + g_free (text_cell->cell_text); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static G_CONST_RETURN gchar* +gail_text_cell_get_name (AtkObject *atk_obj) +{ + if (atk_obj->name) + return atk_obj->name; + else + { + GailTextCell *text_cell = GAIL_TEXT_CELL (atk_obj); + + return text_cell->cell_text; + } +} + +static gboolean +gail_text_cell_update_cache (GailRendererCell *cell, + gboolean emit_change_signal) +{ + GailTextCell *text_cell = GAIL_TEXT_CELL (cell); + AtkObject *obj = ATK_OBJECT (cell); + gboolean rv = FALSE; + gint temp_length; + gchar *new_cache; + + g_object_get (G_OBJECT (cell->renderer), "text", &new_cache, NULL); + + if (text_cell->cell_text) + { + /* + * If the new value is NULL and the old value isn't NULL, then the + * value has changed. + */ + if (new_cache == NULL || + g_strcasecmp (text_cell->cell_text, new_cache)) + { + g_free (text_cell->cell_text); + temp_length = text_cell->cell_length; + text_cell->cell_text = NULL; + text_cell->cell_length = 0; + if (emit_change_signal) + { + g_signal_emit_by_name (cell, "text_changed::delete", 0, temp_length); + if (obj->name == NULL) + g_object_notify (G_OBJECT (obj), "accessible-name"); + } + if (new_cache) + rv = TRUE; + } + } + else + rv = TRUE; + + if (rv) + { + if (new_cache == NULL) + { + text_cell->cell_text = g_strdup (""); + text_cell->cell_length = 0; + } + else + { + text_cell->cell_text = g_strdup (new_cache); + text_cell->cell_length = g_utf8_strlen (new_cache, -1); + } + } + + g_free (new_cache); + gail_text_util_text_setup (text_cell->textutil, text_cell->cell_text); + + if (rv) + { + if (emit_change_signal) + { + g_signal_emit_by_name (cell, "text_changed::insert", + 0, text_cell->cell_length); + + if (obj->name == NULL) + g_object_notify (G_OBJECT (obj), "accessible-name"); + } + } + return rv; +} + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->get_text = gail_text_cell_get_text; + iface->get_character_at_offset = gail_text_cell_get_character_at_offset; + iface->get_text_before_offset = gail_text_cell_get_text_before_offset; + iface->get_text_at_offset = gail_text_cell_get_text_at_offset; + iface->get_text_after_offset = gail_text_cell_get_text_after_offset; + iface->get_character_count = gail_text_cell_get_character_count; + iface->get_caret_offset = gail_text_cell_get_caret_offset; + iface->set_caret_offset = gail_text_cell_set_caret_offset; + iface->get_run_attributes = gail_text_cell_get_run_attributes; + iface->get_default_attributes = gail_text_cell_get_default_attributes; + iface->get_character_extents = gail_text_cell_get_character_extents; + iface->get_offset_at_point = gail_text_cell_get_offset_at_point; +} + +static gchar* +gail_text_cell_get_text (AtkText *text, + gint start_pos, + gint end_pos) +{ + if (GAIL_TEXT_CELL (text)->cell_text) + return gail_text_util_get_substring (GAIL_TEXT_CELL (text)->textutil, + start_pos, end_pos); + else + return g_strdup (""); +} + +static gchar* +gail_text_cell_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return gail_text_util_get_text (GAIL_TEXT_CELL (text)->textutil, + NULL, GAIL_BEFORE_OFFSET, boundary_type, offset, start_offset, + end_offset); +} + +static gchar* +gail_text_cell_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return gail_text_util_get_text (GAIL_TEXT_CELL (text)->textutil, + NULL, GAIL_AT_OFFSET, boundary_type, offset, start_offset, end_offset); +} + +static gchar* +gail_text_cell_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + return gail_text_util_get_text (GAIL_TEXT_CELL (text)->textutil, + NULL, GAIL_AFTER_OFFSET, boundary_type, offset, start_offset, + end_offset); +} + +static gint +gail_text_cell_get_character_count (AtkText *text) +{ + if (GAIL_TEXT_CELL (text)->cell_text != NULL) + return GAIL_TEXT_CELL (text)->cell_length; + else + return 0; +} + +static gint +gail_text_cell_get_caret_offset (AtkText *text) +{ + return GAIL_TEXT_CELL (text)->caret_pos; +} + +static gboolean +gail_text_cell_set_caret_offset (AtkText *text, + gint offset) +{ + GailTextCell *text_cell = GAIL_TEXT_CELL (text); + + if (text_cell->cell_text == NULL) + return FALSE; + else + { + + /* Only set the caret within the bounds and if it is to a new position. */ + if (offset >= 0 && + offset <= text_cell->cell_length && + offset != text_cell->caret_pos) + { + text_cell->caret_pos = offset; + + /* emit the signal */ + g_signal_emit_by_name (text, "text_caret_moved", offset); + return TRUE; + } + else + return FALSE; + } +} + +static AtkAttributeSet* +gail_text_cell_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GailRendererCell *gail_renderer; + GtkCellRendererText *gtk_renderer; + AtkAttributeSet *attrib_set = NULL; + PangoLayout *layout; + AtkObject *parent; + GtkWidget *widget; + + gail_renderer = GAIL_RENDERER_CELL (text); + gtk_renderer = GTK_CELL_RENDERER_TEXT (gail_renderer->renderer); + + parent = atk_object_get_parent (ATK_OBJECT (text)); + if (GAIL_IS_CONTAINER_CELL (parent)) + parent = atk_object_get_parent (parent); + g_return_val_if_fail (GAIL_IS_CELL_PARENT (parent), NULL); + widget = GTK_ACCESSIBLE (parent)->widget; + layout = create_pango_layout (gtk_renderer, widget), + attrib_set = gail_misc_layout_get_run_attributes (attrib_set, + layout, + gtk_renderer->text, + offset, + start_offset, + end_offset); + g_object_unref (G_OBJECT (layout)); + + return attrib_set; +} + +static AtkAttributeSet* +gail_text_cell_get_default_attributes (AtkText *text) +{ + GailRendererCell *gail_renderer; + GtkCellRendererText *gtk_renderer; + AtkAttributeSet *attrib_set = NULL; + PangoLayout *layout; + AtkObject *parent; + GtkWidget *widget; + + gail_renderer = GAIL_RENDERER_CELL (text); + gtk_renderer = GTK_CELL_RENDERER_TEXT (gail_renderer->renderer); + + parent = atk_object_get_parent (ATK_OBJECT (text)); + if (GAIL_IS_CONTAINER_CELL (parent)) + parent = atk_object_get_parent (parent); + g_return_val_if_fail (GAIL_IS_CELL_PARENT (parent), NULL); + widget = GTK_ACCESSIBLE (parent)->widget; + layout = create_pango_layout (gtk_renderer, widget), + + attrib_set = gail_misc_get_default_attributes (attrib_set, + layout, + widget); + g_object_unref (G_OBJECT (layout)); + return attrib_set; +} + +/* + * This function is used by gail_text_cell_get_offset_at_point() + * and gail_text_cell_get_character_extents(). There is no + * cached PangoLayout for gailtextcell so we must create a temporary + * one using this function. + */ +static PangoLayout* +create_pango_layout(GtkCellRendererText *gtk_renderer, + GtkWidget *widget) +{ + PangoAttrList *attr_list; + PangoLayout *layout; + PangoUnderline uline; + PangoFontMask mask; + + layout = gtk_widget_create_pango_layout (widget, gtk_renderer->text); + + if (gtk_renderer->extra_attrs) + attr_list = pango_attr_list_copy (gtk_renderer->extra_attrs); + else + attr_list = pango_attr_list_new (); + + if (gtk_renderer->foreground_set) + { + PangoColor color; + color = gtk_renderer->foreground; + add_attr (attr_list, pango_attr_foreground_new (color.red, + color.green, color.blue)); + } + + if (gtk_renderer->strikethrough_set) + add_attr (attr_list, + pango_attr_strikethrough_new (gtk_renderer->strikethrough)); + + mask = pango_font_description_get_set_fields (gtk_renderer->font); + + if (mask & PANGO_FONT_MASK_FAMILY) + add_attr (attr_list, + pango_attr_family_new (pango_font_description_get_family (gtk_renderer->font))); + + if (mask & PANGO_FONT_MASK_STYLE) + add_attr (attr_list, pango_attr_style_new (pango_font_description_get_style (gtk_renderer->font))); + + if (mask & PANGO_FONT_MASK_VARIANT) + add_attr (attr_list, pango_attr_variant_new (pango_font_description_get_variant (gtk_renderer->font))); + + if (mask & PANGO_FONT_MASK_WEIGHT) + add_attr (attr_list, pango_attr_weight_new (pango_font_description_get_weight (gtk_renderer->font))); + + if (mask & PANGO_FONT_MASK_STRETCH) + add_attr (attr_list, pango_attr_stretch_new (pango_font_description_get_stretch (gtk_renderer->font))); + + if (mask & PANGO_FONT_MASK_SIZE) + add_attr (attr_list, pango_attr_size_new (pango_font_description_get_size (gtk_renderer->font))); + + if (gtk_renderer->scale_set && + gtk_renderer->font_scale != 1.0) + add_attr (attr_list, pango_attr_scale_new (gtk_renderer->font_scale)); + + if (gtk_renderer->underline_set) + uline = gtk_renderer->underline_style; + else + uline = PANGO_UNDERLINE_NONE; + + if (uline != PANGO_UNDERLINE_NONE) + add_attr (attr_list, + pango_attr_underline_new (gtk_renderer->underline_style)); + + if (gtk_renderer->rise_set) + add_attr (attr_list, pango_attr_rise_new (gtk_renderer->rise)); + + pango_layout_set_attributes (layout, attr_list); + pango_layout_set_width (layout, -1); + pango_attr_list_unref (attr_list); + + return layout; +} + +static void +add_attr (PangoAttrList *attr_list, + PangoAttribute *attr) +{ + attr->start_index = 0; + attr->end_index = G_MAXINT; + pango_attr_list_insert (attr_list, attr); +} + +static void +gail_text_cell_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GailRendererCell *gail_renderer; + GtkCellRendererText *gtk_renderer; + GdkRectangle rendered_rect; + GtkWidget *widget; + AtkObject *parent; + PangoRectangle char_rect; + PangoLayout *layout; + gint x_offset, y_offset, index, cell_height, cell_width; + + if (!GAIL_TEXT_CELL (text)->cell_text) + { + *x = *y = *height = *width = 0; + return; + } + if (offset < 0 || offset >= GAIL_TEXT_CELL (text)->cell_length) + { + *x = *y = *height = *width = 0; + return; + } + gail_renderer = GAIL_RENDERER_CELL (text); + gtk_renderer = GTK_CELL_RENDERER_TEXT (gail_renderer->renderer); + /* + * Thus would be inconsistent with the cache + */ + gail_return_if_fail (gtk_renderer->text); + + parent = atk_object_get_parent (ATK_OBJECT (text)); + if (GAIL_IS_CONTAINER_CELL (parent)) + parent = atk_object_get_parent (parent); + widget = GTK_ACCESSIBLE (parent)->widget; + g_return_if_fail (GAIL_IS_CELL_PARENT (parent)); + gail_cell_parent_get_cell_area (GAIL_CELL_PARENT (parent), GAIL_CELL (text), + &rendered_rect); + + gtk_cell_renderer_get_size (GTK_CELL_RENDERER (gtk_renderer), widget, + &rendered_rect, &x_offset, &y_offset, &cell_width, &cell_height); + layout = create_pango_layout (gtk_renderer, widget); + + index = g_utf8_offset_to_pointer (gtk_renderer->text, + offset) - gtk_renderer->text; + pango_layout_index_to_pos (layout, index, &char_rect); + + gail_misc_get_extents_from_pango_rectangle (widget, + &char_rect, + x_offset + rendered_rect.x + gail_renderer->renderer->xpad, + y_offset + rendered_rect.y + gail_renderer->renderer->ypad, + x, y, width, height, coords); + g_object_unref (layout); + return; +} + +static gint +gail_text_cell_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + AtkObject *parent; + GailRendererCell *gail_renderer; + GtkCellRendererText *gtk_renderer; + GtkWidget *widget; + GdkRectangle rendered_rect; + PangoLayout *layout; + gint x_offset, y_offset, index; + + if (!GAIL_TEXT_CELL (text)->cell_text) + return -1; + + gail_renderer = GAIL_RENDERER_CELL (text); + gtk_renderer = GTK_CELL_RENDERER_TEXT (gail_renderer->renderer); + parent = atk_object_get_parent (ATK_OBJECT (text)); + + g_return_val_if_fail (gtk_renderer->text, -1); + if (GAIL_IS_CONTAINER_CELL (parent)) + parent = atk_object_get_parent (parent); + + widget = GTK_ACCESSIBLE (parent)->widget; + + g_return_val_if_fail (GAIL_IS_CELL_PARENT (parent), -1); + gail_cell_parent_get_cell_area (GAIL_CELL_PARENT (parent), GAIL_CELL (text), + &rendered_rect); + gtk_cell_renderer_get_size (GTK_CELL_RENDERER (gtk_renderer), widget, + &rendered_rect, &x_offset, &y_offset, NULL, NULL); + + layout = create_pango_layout (gtk_renderer, widget); + + index = gail_misc_get_index_at_point_in_layout (widget, layout, + x_offset + rendered_rect.x + gail_renderer->renderer->xpad, + y_offset + rendered_rect.y + gail_renderer->renderer->ypad, + x, y, coords); + g_object_unref (layout); + if (index == -1) + { + if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN) + return g_utf8_strlen (gtk_renderer->text, -1); + + return index; + } + else + return g_utf8_pointer_to_offset (gtk_renderer->text, + gtk_renderer->text + index); +} + +static gunichar +gail_text_cell_get_character_at_offset (AtkText *text, + gint offset) +{ + gchar *index; + gchar *string; + + string = GAIL_TEXT_CELL(text)->cell_text; + + if (!string) + return '\0'; + + if (offset >= g_utf8_strlen (string, -1)) + return '\0'; + + index = g_utf8_offset_to_pointer (string, offset); + + return g_utf8_get_char (index); +} + diff --git a/modules/other/gail/gailtextcell.h b/modules/other/gail/gailtextcell.h new file mode 100644 index 000000000..76c1ade87 --- /dev/null +++ b/modules/other/gail/gailtextcell.h @@ -0,0 +1,64 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TEXT_CELL_H__ +#define __GAIL_TEXT_CELL_H__ + +#include <atk/atk.h> +#include <gail/gailrenderercell.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TEXT_CELL (gail_text_cell_get_type ()) +#define GAIL_TEXT_CELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TEXT_CELL, GailTextCell)) +#define GAIL_TEXT_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TEXT_CELL, GailTextCellClass)) +#define GAIL_IS_TEXT_CELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TEXT_CELL)) +#define GAIL_IS_TEXT_CELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TEXT_CELL)) +#define GAIL_TEXT_CELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TEXT_CELL, GailTextCellClass)) + +typedef struct _GailTextCell GailTextCell; +typedef struct _GailTextCellClass GailTextCellClass; + +struct _GailTextCell +{ + GailRendererCell parent; + GailTextUtil *textutil; + gchar *cell_text; + gint caret_pos; + gint cell_length; +}; + +GType gail_text_cell_get_type (void); + +struct _GailTextCellClass +{ + GailRendererCellClass parent_class; +}; + +AtkObject *gail_text_cell_new (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_TEXT_CELL_H__ */ diff --git a/modules/other/gail/gailtextcellfactory.c b/modules/other/gail/gailtextcellfactory.c new file mode 100644 index 000000000..b981a7c91 --- /dev/null +++ b/modules/other/gail/gailtextcellfactory.c @@ -0,0 +1,79 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtkcellrenderertext.h> +#include "gailtextcellfactory.h" +#include "gailtextcell.h" + +static void gail_text_cell_factory_class_init (GailTextCellFactoryClass *klass); + +static AtkObject* gail_text_cell_factory_create_accessible ( + GObject *obj); + +static GType gail_text_cell_factory_get_accessible_type (void); + +GType +gail_text_cell_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailTextCellFactoryClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_text_cell_factory_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailTextCellFactory), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + "GailTextCellFactory" , &tinfo, 0); + } + + return type; +} + +static void +gail_text_cell_factory_class_init (GailTextCellFactoryClass *klass) +{ + AtkObjectFactoryClass *class = ATK_OBJECT_FACTORY_CLASS (klass); + + class->create_accessible = gail_text_cell_factory_create_accessible; + class->get_accessible_type = gail_text_cell_factory_get_accessible_type; +} + +static AtkObject* +gail_text_cell_factory_create_accessible (GObject *obj) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TEXT (obj), NULL); + + return gail_text_cell_new (); +} + +static GType +gail_text_cell_factory_get_accessible_type (void) +{ + return GAIL_TYPE_TEXT_CELL; +} diff --git a/modules/other/gail/gailtextcellfactory.h b/modules/other/gail/gailtextcellfactory.h new file mode 100644 index 000000000..1aae137ff --- /dev/null +++ b/modules/other/gail/gailtextcellfactory.h @@ -0,0 +1,56 @@ +/* GAIL - The GNOME Accessibility Enabling Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TEXT_CELL_FACTORY_H__ +#define __GAIL_TEXT_CELL_FACTORY_H__ + +#include <atk/atkobjectfactory.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TEXT_CELL_FACTORY (gail_text_cell_factory_get_type ()) +#define GAIL_TEXT_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TEXT_CELL_FACTORY, GailTextCellFactory)) +#define GAIL_TEXT_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AGTK_TYPE_TEXT_CELL_FACTORY, GailTextCellFactoryClass)) +#define GAIL_IS_TEXT_CELL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TEXT_CELL_FACTORY)) +#define GAIL_IS_TEXT_CELL_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TEXT_CELL_FACTORY)) +#define GAIL_TEXT_CELL_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TEXT_CELL_FACTORY, GailTextCellFactoryClass)) + +typedef struct _GailTextCellFactory GailTextCellFactory; +typedef struct _GailTextCellFactoryClass GailTextCellFactoryClass; + +struct _GailTextCellFactory +{ + AtkObjectFactory parent; +}; + +struct _GailTextCellFactoryClass +{ + AtkObjectFactoryClass parent_class; +}; + +GType gail_text_cell_factory_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TEXT_CELL_FACTORY_H__ */ diff --git a/modules/other/gail/gailtextview.c b/modules/other/gail/gailtextview.c new file mode 100644 index 000000000..e99e02a1f --- /dev/null +++ b/modules/other/gail/gailtextview.c @@ -0,0 +1,1819 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <glib-object.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> +#include "gailtextview.h" +#include <libgail-util/gailmisc.h> + +static void gail_text_view_class_init (GailTextViewClass *klass); +static void gail_text_view_init (GailTextView *text_view); + +static void gail_text_view_real_initialize (AtkObject *obj, + gpointer data); +static void gail_text_view_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_text_view_finalize (GObject *object); + +static void atk_text_interface_init (AtkTextIface *iface); + +/* atkobject.h */ + +static AtkStateSet* gail_text_view_ref_state_set (AtkObject *accessible); + +/* atktext.h */ + +static gchar* gail_text_view_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_text_view_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_text_view_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset); +static gchar* gail_text_view_get_text (AtkText*text, + gint start_offset, + gint end_offset); +static gunichar gail_text_view_get_character_at_offset (AtkText *text, + gint offset); +static gint gail_text_view_get_character_count (AtkText *text); +static gint gail_text_view_get_caret_offset (AtkText *text); +static gboolean gail_text_view_set_caret_offset (AtkText *text, + gint offset); +static gint gail_text_view_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords); +static gint gail_text_view_get_n_selections (AtkText *text); +static gchar* gail_text_view_get_selection (AtkText *text, + gint selection_num, + gint *start_offset, + gint *end_offset); +static gboolean gail_text_view_add_selection (AtkText *text, + gint start_offset, + gint end_offset); +static gboolean gail_text_view_remove_selection (AtkText *text, + gint selection_num); +static gboolean gail_text_view_set_selection (AtkText *text, + gint selection_num, + gint start_offset, + gint end_offset); +static void gail_text_view_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); +static AtkAttributeSet * gail_text_view_get_run_attributes + (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset); +static AtkAttributeSet * gail_text_view_get_default_attributes + (AtkText *text); +/* atkeditabletext.h */ + +static void atk_editable_text_interface_init (AtkEditableTextIface *iface); +static gboolean gail_text_view_set_run_attributes (AtkEditableText *text, + AtkAttributeSet *attrib_set, + gint start_offset, + gint end_offset); +static void gail_text_view_set_text_contents (AtkEditableText *text, + const gchar *string); +static void gail_text_view_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position); +static void gail_text_view_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_text_view_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_text_view_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos); +static void gail_text_view_paste_text (AtkEditableText *text, + gint position); +static void gail_text_view_paste_received (GtkClipboard *clipboard, + const gchar *text, + gpointer data); +/* AtkStreamableContent */ +static void atk_streamable_content_interface_init (AtkStreamableContentIface *iface); +static gint gail_streamable_content_get_n_mime_types (AtkStreamableContent *streamable); +static G_CONST_RETURN gchar* gail_streamable_content_get_mime_type (AtkStreamableContent *streamable, + gint i); +static GIOChannel* gail_streamable_content_get_stream (AtkStreamableContent *streamable, + const gchar *mime_type); +/* getURI requires atk-1.12.0 or later +static void gail_streamable_content_get_uri (AtkStreamableContent *streamable); +*/ + +/* Callbacks */ + +static void _gail_text_view_insert_text_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + gchar *arg2, + gint arg3, + gpointer user_data); +static void _gail_text_view_delete_range_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + GtkTextIter *arg2, + gpointer user_data); +static void _gail_text_view_changed_cb (GtkTextBuffer *buffer, + gpointer user_data); +static void _gail_text_view_mark_set_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + GtkTextMark *arg2, + gpointer user_data); +static gchar* get_text_near_offset (AtkText *text, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset); +static gint get_insert_offset (GtkTextBuffer *buffer); +static gint get_selection_bound (GtkTextBuffer *buffer); +static void emit_text_caret_moved (GailTextView *gail_text_view, + gint insert_offset); +static gint insert_idle_handler (gpointer data); + +static GailWidgetClass *parent_class = NULL; + +typedef struct _GailTextViewPaste GailTextViewPaste; + +struct _GailTextViewPaste +{ + GtkTextBuffer* buffer; + gint position; +}; + +GType +gail_text_view_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailTextViewClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_text_view_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailTextView), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_text_view_init, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_editable_text_info = + { + (GInterfaceInitFunc) atk_editable_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_text_info = + { + (GInterfaceInitFunc) atk_text_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_streamable_content_info = + { + (GInterfaceInitFunc) atk_streamable_content_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailTextView", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_EDITABLE_TEXT, + &atk_editable_text_info); + g_type_add_interface_static (type, ATK_TYPE_TEXT, + &atk_text_info); + g_type_add_interface_static (type, ATK_TYPE_STREAMABLE_CONTENT, + &atk_streamable_content_info); + } + + return type; +} + +static void +gail_text_view_class_init (GailTextViewClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GailWidgetClass *widget_class; + + widget_class = (GailWidgetClass*)klass; + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_text_view_finalize; + + class->ref_state_set = gail_text_view_ref_state_set; + class->initialize = gail_text_view_real_initialize; + + widget_class->notify_gtk = gail_text_view_real_notify_gtk; +} + +static void +gail_text_view_init (GailTextView *text_view) +{ + text_view->textutil = NULL; + text_view->signal_name = NULL; + text_view->previous_insert_offset = -1; + text_view->previous_selection_bound = -1; + text_view->insert_notify_handler = 0; +} + +static void +setup_buffer (GtkTextView *view, + GailTextView *gail_view) +{ + GtkTextBuffer *buffer; + + buffer = view->buffer; + if (buffer == NULL) + return; + + gail_view->textutil = gail_text_util_new (); + gail_text_util_buffer_setup (gail_view->textutil, buffer); + + /* Set up signal callbacks */ + g_signal_connect_data (buffer, "insert-text", + (GCallback) _gail_text_view_insert_text_cb, view, NULL, 0); + g_signal_connect_data (buffer, "delete-range", + (GCallback) _gail_text_view_delete_range_cb, view, NULL, 0); + g_signal_connect_data (buffer, "mark-set", + (GCallback) _gail_text_view_mark_set_cb, view, NULL, 0); + g_signal_connect_data (buffer, "changed", + (GCallback) _gail_text_view_changed_cb, view, NULL, 0); + +} + +static void +gail_text_view_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkTextView *view; + GailTextView *gail_view; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + view = GTK_TEXT_VIEW (data); + + gail_view = GAIL_TEXT_VIEW (obj); + setup_buffer (view, gail_view); + + obj->role = ATK_ROLE_TEXT; + +} + +static void +gail_text_view_finalize (GObject *object) +{ + GailTextView *text_view = GAIL_TEXT_VIEW (object); + + g_object_unref (text_view->textutil); + if (text_view->insert_notify_handler) + g_source_remove (text_view->insert_notify_handler); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +AtkObject* +gail_text_view_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_TEXT_VIEW (widget), NULL); + + object = g_object_new (GAIL_TYPE_TEXT_VIEW, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_text_view_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + if (!strcmp (pspec->name, "editable")) + { + AtkObject *atk_obj; + gboolean editable; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj)); + editable = gtk_text_view_get_editable (GTK_TEXT_VIEW (obj)); + atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, + editable); + } + else if (!strcmp (pspec->name, "buffer")) + { + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (obj)); + setup_buffer (GTK_TEXT_VIEW (obj), GAIL_TEXT_VIEW (atk_obj)); + } + else + parent_class->notify_gtk (obj, pspec); +} + +/* atkobject.h */ + +static AtkStateSet* +gail_text_view_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkTextView *text_view; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + text_view = GTK_TEXT_VIEW (widget); + + if (gtk_text_view_get_editable (text_view)) + atk_state_set_add_state (state_set, ATK_STATE_EDITABLE); + atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE); + + return state_set; +} + +/* atktext.h */ + +static void +atk_text_interface_init (AtkTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_text = gail_text_view_get_text; + iface->get_text_after_offset = gail_text_view_get_text_after_offset; + iface->get_text_at_offset = gail_text_view_get_text_at_offset; + iface->get_text_before_offset = gail_text_view_get_text_before_offset; + iface->get_character_at_offset = gail_text_view_get_character_at_offset; + iface->get_character_count = gail_text_view_get_character_count; + iface->get_caret_offset = gail_text_view_get_caret_offset; + iface->set_caret_offset = gail_text_view_set_caret_offset; + iface->get_offset_at_point = gail_text_view_get_offset_at_point; + iface->get_character_extents = gail_text_view_get_character_extents; + iface->get_n_selections = gail_text_view_get_n_selections; + iface->get_selection = gail_text_view_get_selection; + iface->add_selection = gail_text_view_add_selection; + iface->remove_selection = gail_text_view_remove_selection; + iface->set_selection = gail_text_view_set_selection; + iface->get_run_attributes = gail_text_view_get_run_attributes; + iface->get_default_attributes = gail_text_view_get_default_attributes; +} + +static gchar* +gail_text_view_get_text (AtkText *text, + gint start_offset, + gint end_offset) +{ + GtkTextView *view; + GtkTextBuffer *buffer; + GtkTextIter start, end; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset); + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset); + + return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); +} + +static gchar* +gail_text_view_get_text_after_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + return get_text_near_offset (text, GAIL_AFTER_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gchar* +gail_text_view_get_text_at_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + return get_text_near_offset (text, GAIL_AT_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gchar* +gail_text_view_get_text_before_offset (AtkText *text, + gint offset, + AtkTextBoundary boundary_type, + gint *start_offset, + gint *end_offset) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + return get_text_near_offset (text, GAIL_BEFORE_OFFSET, + boundary_type, offset, + start_offset, end_offset); +} + +static gunichar +gail_text_view_get_character_at_offset (AtkText *text, + gint offset) +{ + GtkWidget *widget; + GtkTextIter start, end; + GtkTextBuffer *buffer; + gchar *string; + gunichar unichar; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + return '\0'; + + buffer = GAIL_TEXT_VIEW (text)->textutil->buffer; + if (offset >= gtk_text_buffer_get_char_count (buffer)) + return '\0'; + + gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); + end = start; + gtk_text_iter_forward_char (&end); + string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); + unichar = g_utf8_get_char (string); + g_free(string); + return unichar; +} + +static gint +gail_text_view_get_character_count (AtkText *text) +{ + GtkTextView *view; + GtkTextBuffer *buffer; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + return gtk_text_buffer_get_char_count (buffer); +} + +static gint +gail_text_view_get_caret_offset (AtkText *text) +{ + GtkTextView *view; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + view = GTK_TEXT_VIEW (widget); + return get_insert_offset (view->buffer); +} + +static gboolean +gail_text_view_set_caret_offset (AtkText *text, + gint offset) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter pos_itr; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, offset); + gtk_text_buffer_place_cursor (buffer, &pos_itr); + return TRUE; +} + +static gint +gail_text_view_get_offset_at_point (AtkText *text, + gint x, + gint y, + AtkCoordType coords) +{ + GtkTextView *view; + GtkTextBuffer *buffer; + GtkTextIter loc_itr; + gint x_widget, y_widget, x_window, y_window, buff_x, buff_y; + GtkWidget *widget; + GdkWindow *window; + GdkRectangle rect; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET); + gdk_window_get_origin (window, &x_widget, &y_widget); + + if (coords == ATK_XY_SCREEN) + { + x = x - x_widget; + y = y - y_widget; + } + else if (coords == ATK_XY_WINDOW) + { + window = gdk_window_get_toplevel (window); + gdk_window_get_origin (window, &x_window, &y_window); + + x = x - x_widget + x_window; + y = y - y_widget + y_window; + } + else + return -1; + + gtk_text_view_window_to_buffer_coords (view, GTK_TEXT_WINDOW_WIDGET, + x, y, &buff_x, &buff_y); + gtk_text_view_get_visible_rect (view, &rect); + /* + * Clamp point to visible rectangle + */ + buff_x = CLAMP (buff_x, rect.x, rect.x + rect.width - 1); + buff_y = CLAMP (buff_y, rect.y, rect.y + rect.height - 1); + + gtk_text_view_get_iter_at_location (view, &loc_itr, buff_x, buff_y); + /* + * The iter at a location sometimes points to the next character. + * See bug 111031. We work around that + */ + gtk_text_view_get_iter_location (view, &loc_itr, &rect); + if (buff_x < rect.x) + gtk_text_iter_backward_char (&loc_itr); + return gtk_text_iter_get_offset (&loc_itr); +} + +static void +gail_text_view_get_character_extents (AtkText *text, + gint offset, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + GtkTextView *view; + GtkTextBuffer *buffer; + GtkTextIter iter; + GtkWidget *widget; + GdkRectangle rectangle; + GdkWindow *window; + gint x_widget, y_widget, x_window, y_window; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + gtk_text_view_get_iter_location (view, &iter, &rectangle); + + window = gtk_text_view_get_window (view, GTK_TEXT_WINDOW_WIDGET); + gdk_window_get_origin (window, &x_widget, &y_widget); + + *height = rectangle.height; + *width = rectangle.width; + + gtk_text_view_buffer_to_window_coords (view, GTK_TEXT_WINDOW_WIDGET, + rectangle.x, rectangle.y, x, y); + if (coords == ATK_XY_WINDOW) + { + window = gdk_window_get_toplevel (window); + gdk_window_get_origin (window, &x_window, &y_window); + *x += x_widget - x_window; + *y += y_widget - y_window; + } + else if (coords == ATK_XY_SCREEN) + { + *x += x_widget; + *y += y_widget; + } + else + { + *x = 0; + *y = 0; + *height = 0; + *width = 0; + } +} + +static AtkAttributeSet* +gail_text_view_get_run_attributes (AtkText *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkTextView *view; + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + view = GTK_TEXT_VIEW (widget); + + return gail_misc_buffer_get_run_attributes (view->buffer, offset, + start_offset, end_offset); +} + +static AtkAttributeSet* +gail_text_view_get_default_attributes (AtkText *text) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextAttributes *text_attrs; + AtkAttributeSet *attrib_set = NULL; + PangoFontDescription *font; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + view = GTK_TEXT_VIEW (widget); + text_attrs = gtk_text_view_get_default_attributes (view); + + font = text_attrs->font; + + if (font) + { + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_STYLE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_VARIANT); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_STRETCH); + } + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_JUSTIFICATION); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_DIRECTION); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_WRAP_MODE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_FG_STIPPLE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_BG_STIPPLE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_FG_COLOR); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_BG_COLOR); + + if (font) + { + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_FAMILY_NAME); + } + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_LANGUAGE); + + if (font) + { + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_WEIGHT); + } + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_SCALE); + + if (font) + { + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_SIZE); + } + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_STRIKETHROUGH); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_UNDERLINE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_RISE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_BG_FULL_HEIGHT); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_PIXELS_BELOW_LINES); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_PIXELS_ABOVE_LINES); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_EDITABLE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_INVISIBLE); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_INDENT); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_RIGHT_MARGIN); + + attrib_set = gail_misc_add_to_attr_set (attrib_set, text_attrs, + ATK_TEXT_ATTR_LEFT_MARGIN); + + gtk_text_attributes_unref (text_attrs); + return attrib_set; +} + +static gint +gail_text_view_get_n_selections (AtkText *text) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter start, end; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + select_start = gtk_text_iter_get_offset (&start); + select_end = gtk_text_iter_get_offset (&end); + + if (select_start != select_end) + return 1; + else + return 0; +} + +static gchar* +gail_text_view_get_selection (AtkText *text, + gint selection_num, + gint *start_pos, + gint *end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter start, end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + /* Only let the user get the selection if one is set, and if the + * selection_num is 0. + */ + if (selection_num != 0) + return NULL; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + *start_pos = gtk_text_iter_get_offset (&start); + *end_pos = gtk_text_iter_get_offset (&end); + + if (*start_pos != *end_pos) + return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + else + return NULL; +} + +static gboolean +gail_text_view_add_selection (AtkText *text, + gint start_pos, + gint end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter pos_itr; + GtkTextIter start, end; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_selection_bounds (buffer, &start, &end); + select_start = gtk_text_iter_get_offset (&start); + select_end = gtk_text_iter_get_offset (&end); + + /* If there is already a selection, then don't allow another to be added, + * since GtkTextView only supports one selected region. + */ + if (select_start == select_end) + { + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos); + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr); + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos); + gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_text_view_remove_selection (AtkText *text, + gint selection_num) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextMark *cursor_mark; + GtkTextIter cursor_itr; + GtkTextIter start, end; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (selection_num != 0) + return FALSE; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_selection_bounds(buffer, &start, &end); + select_start = gtk_text_iter_get_offset(&start); + select_end = gtk_text_iter_get_offset(&end); + + if (select_start != select_end) + { + /* Setting the start & end of the selected region to the caret position + * turns off the selection. + */ + cursor_mark = gtk_text_buffer_get_insert (buffer); + gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark); + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor_itr); + return TRUE; + } + else + return FALSE; +} + +static gboolean +gail_text_view_set_selection (AtkText *text, + gint selection_num, + gint start_pos, + gint end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter pos_itr; + GtkTextIter start, end; + gint select_start, select_end; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + { + /* State is defunct */ + return FALSE; + } + + /* Only let the user move the selection if one is set, and if the + * selection_num is 0 + */ + if (selection_num != 0) + return FALSE; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_selection_bounds(buffer, &start, &end); + select_start = gtk_text_iter_get_offset(&start); + select_end = gtk_text_iter_get_offset(&end); + + if (select_start != select_end) + { + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, start_pos); + gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr); + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, end_pos); + gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr); + return TRUE; + } + else + return FALSE; +} + +/* atkeditabletext.h */ + +static void +atk_editable_text_interface_init (AtkEditableTextIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->set_text_contents = gail_text_view_set_text_contents; + iface->insert_text = gail_text_view_insert_text; + iface->copy_text = gail_text_view_copy_text; + iface->cut_text = gail_text_view_cut_text; + iface->delete_text = gail_text_view_delete_text; + iface->paste_text = gail_text_view_paste_text; + iface->set_run_attributes = gail_text_view_set_run_attributes; +} + +static gboolean +gail_text_view_set_run_attributes (AtkEditableText *text, + AtkAttributeSet *attrib_set, + gint start_offset, + gint end_offset) +{ + GtkTextView *view; + GtkTextBuffer *buffer; + GtkWidget *widget; + GtkTextTag *tag; + GtkTextIter start; + GtkTextIter end; + gint j; + GdkColor *color; + gchar** RGB_vals; + GSList *l; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return FALSE; + + buffer = view->buffer; + + if (attrib_set == NULL) + return FALSE; + + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset); + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset); + + tag = gtk_text_buffer_create_tag (buffer, NULL, NULL); + + for (l = attrib_set; l; l = l->next) + { + gchar *name; + gchar *value; + AtkAttribute *at; + + at = l->data; + + name = at->name; + value = at->value; + + if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LEFT_MARGIN))) + g_object_set (G_OBJECT (tag), "left_margin", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RIGHT_MARGIN))) + g_object_set (G_OBJECT (tag), "right_margin", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INDENT))) + g_object_set (G_OBJECT (tag), "indent", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_ABOVE_LINES))) + g_object_set (G_OBJECT (tag), "pixels_above_lines", atoi (value), NULL); + + else if (!g_strcasecmp(name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_BELOW_LINES))) + g_object_set (G_OBJECT (tag), "pixels_below_lines", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP))) + g_object_set (G_OBJECT (tag), "pixels_inside_wrap", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_SIZE))) + g_object_set (G_OBJECT (tag), "size", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_RISE))) + g_object_set (G_OBJECT (tag), "rise", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WEIGHT))) + g_object_set (G_OBJECT (tag), "weight", atoi (value), NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_FULL_HEIGHT))) + { + g_object_set (G_OBJECT (tag), "bg_full_height", + (g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_FULL_HEIGHT, 0))), + NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_LANGUAGE))) + g_object_set (G_OBJECT (tag), "language", value, NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FAMILY_NAME))) + g_object_set (G_OBJECT (tag), "family", value, NULL); + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_EDITABLE))) + { + g_object_set (G_OBJECT (tag), "editable", + (g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))), + NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_INVISIBLE))) + { + g_object_set (G_OBJECT (tag), "invisible", + (g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0))), + NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_UNDERLINE))) + { + for (j = 0; j < 3; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, j))) + { + g_object_set (G_OBJECT (tag), "underline", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRIKETHROUGH))) + { + g_object_set (G_OBJECT (tag), "strikethrough", + (g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0))), + NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_BG_COLOR))) + { + RGB_vals = g_strsplit (value, ",", 3); + color = g_malloc (sizeof (GdkColor)); + color->red = atoi (RGB_vals[0]); + color->green = atoi (RGB_vals[1]); + color->blue = atoi (RGB_vals[2]); + g_object_set (G_OBJECT (tag), "background_gdk", color, NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_FG_COLOR))) + { + RGB_vals = g_strsplit (value, ",", 3); + color = g_malloc (sizeof (GdkColor)); + color->red = atoi (RGB_vals[0]); + color->green = atoi (RGB_vals[1]); + color->blue = atoi (RGB_vals[2]); + g_object_set (G_OBJECT (tag), "foreground_gdk", color, NULL); + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STRETCH))) + { + for (j = 0; j < 9; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, j))) + { + g_object_set (G_OBJECT (tag), "stretch", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_JUSTIFICATION))) + { + for (j = 0; j < 4; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, j))) + { + g_object_set (G_OBJECT (tag), "justification", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_DIRECTION))) + { + for (j = 0; j < 3; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, j))) + { + g_object_set (G_OBJECT (tag), "direction", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_VARIANT))) + { + for (j = 0; j < 2; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, j))) + { + g_object_set (G_OBJECT (tag), "variant", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_WRAP_MODE))) + { + for (j = 0; j < 3; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, j))) + { + g_object_set (G_OBJECT (tag), "wrap_mode", j, NULL); + break; + } + } + } + + else if (!g_strcasecmp (name, atk_text_attribute_get_name (ATK_TEXT_ATTR_STYLE))) + { + for (j = 0; j < 3; j++) + { + if (!g_strcasecmp (value, atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, j))) + { + g_object_set (G_OBJECT (tag), "style", j, NULL); + break; + } + } + } + + else + return FALSE; + } + + gtk_text_buffer_apply_tag (buffer, tag, &start, &end); + + return TRUE; +} + +static void +gail_text_view_set_text_contents (AtkEditableText *text, + const gchar *string) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return; + buffer = view->buffer; + + /* The -1 indicates that the input string must be null-terminated */ + gtk_text_buffer_set_text (buffer, string, -1); +} + +static void +gail_text_view_insert_text (AtkEditableText *text, + const gchar *string, + gint length, + gint *position) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter pos_itr; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return; + buffer = view->buffer; + + gtk_text_buffer_get_iter_at_offset (buffer, &pos_itr, *position); + gtk_text_buffer_insert (buffer, &pos_itr, string, length); +} + +static void +gail_text_view_copy_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *str; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + buffer = view->buffer; + + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos); + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos); + str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1); +} + +static void +gail_text_view_cut_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *str; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return; + buffer = view->buffer; + + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos); + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos); + str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + gtk_clipboard_set_text (gtk_clipboard_get (GDK_NONE), str, -1); + gtk_text_buffer_delete (buffer, &start, &end); +} + +static void +gail_text_view_delete_text (AtkEditableText *text, + gint start_pos, + gint end_pos) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GtkTextIter start_itr; + GtkTextIter end_itr; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return; + buffer = view->buffer; + + gtk_text_buffer_get_iter_at_offset (buffer, &start_itr, start_pos); + gtk_text_buffer_get_iter_at_offset (buffer, &end_itr, end_pos); + gtk_text_buffer_delete (buffer, &start_itr, &end_itr); +} + +static void +gail_text_view_paste_text (AtkEditableText *text, + gint position) +{ + GtkTextView *view; + GtkWidget *widget; + GtkTextBuffer *buffer; + GailTextViewPaste paste_struct; + + widget = GTK_ACCESSIBLE (text)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + view = GTK_TEXT_VIEW (widget); + if (!gtk_text_view_get_editable (view)) + return; + buffer = view->buffer; + + paste_struct.buffer = buffer; + paste_struct.position = position; + + g_object_ref (paste_struct.buffer); + gtk_clipboard_request_text (gtk_clipboard_get (GDK_NONE), + gail_text_view_paste_received, &paste_struct); +} + +static void +gail_text_view_paste_received (GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + GailTextViewPaste* paste_struct = (GailTextViewPaste *)data; + GtkTextIter pos_itr; + + if (text) + { + gtk_text_buffer_get_iter_at_offset (paste_struct->buffer, &pos_itr, + paste_struct->position); + gtk_text_buffer_insert (paste_struct->buffer, &pos_itr, text, -1); + } + + g_object_unref (paste_struct->buffer); +} + +/* Callbacks */ + +/* Note arg1 returns the start of the insert range, arg3 returns the + * end of the insert range if multiple characters are inserted. If one + * character is inserted they have the same value, which is the caret + * location. arg2 returns the begin location of the insert. + */ +static void +_gail_text_view_insert_text_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + gchar *arg2, + gint arg3, + gpointer user_data) +{ + GtkTextView *text = (GtkTextView *) user_data; + AtkObject *accessible; + GailTextView *gail_text_view; + gint position; + gint length; + + g_return_if_fail (arg3 > 0); + + accessible = gtk_widget_get_accessible(GTK_WIDGET(text)); + gail_text_view = GAIL_TEXT_VIEW (accessible); + + gail_text_view->signal_name = "text_changed::insert"; + position = gtk_text_iter_get_offset (arg1); + length = g_utf8_strlen(arg2, arg3); + + if (gail_text_view->length == 0) + { + gail_text_view->position = position; + gail_text_view->length = length; + } + else if (gail_text_view->position + gail_text_view->length == position) + { + gail_text_view->length += length; + } + else + { + /* + * We have a non-contiguous insert so report what we have + */ + if (gail_text_view->insert_notify_handler) + { + g_source_remove (gail_text_view->insert_notify_handler); + } + gail_text_view->insert_notify_handler = 0; + insert_idle_handler (gail_text_view); + gail_text_view->position = position; + gail_text_view->length = length; + } + + /* + * The signal will be emitted when the changed signal is received + */ +} + +/* Note arg1 returns the start of the delete range, arg2 returns the + * end of the delete range if multiple characters are deleted. If one + * character is deleted they have the same value, which is the caret + * location. + */ +static void +_gail_text_view_delete_range_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + GtkTextIter *arg2, + gpointer user_data) +{ + GtkTextView *text = (GtkTextView *) user_data; + AtkObject *accessible; + GailTextView *gail_text_view; + gint offset = gtk_text_iter_get_offset (arg1); + gint length = gtk_text_iter_get_offset (arg2) - offset; + + accessible = gtk_widget_get_accessible(GTK_WIDGET(text)); + gail_text_view = GAIL_TEXT_VIEW (accessible); + if (gail_text_view->insert_notify_handler) + { + g_source_remove (gail_text_view->insert_notify_handler); + gail_text_view->insert_notify_handler = 0; + if (gail_text_view->position == offset && + gail_text_view->length == length) + { + /* + * Do not bother with insert and delete notifications + */ + gail_text_view->signal_name = NULL; + gail_text_view->position = 0; + gail_text_view->length = 0; + return; + } + + insert_idle_handler (gail_text_view); + } + g_signal_emit_by_name (accessible, "text_changed::delete", + offset, length); +} + +/* Note arg1 and arg2 point to the same offset, which is the caret + * position after the move + */ +static void +_gail_text_view_mark_set_cb (GtkTextBuffer *buffer, + GtkTextIter *arg1, + GtkTextMark *arg2, + gpointer user_data) +{ + GtkTextView *text = (GtkTextView *) user_data; + AtkObject *accessible; + GailTextView *gail_text_view; + const char *mark_name = gtk_text_mark_get_name(arg2); + + accessible = gtk_widget_get_accessible(GTK_WIDGET(text)); + gail_text_view = GAIL_TEXT_VIEW (accessible); + + /* + * Only generate the signal for the "insert" mark, which + * represents the cursor. + */ + if (mark_name && !strcmp(mark_name, "insert")) + { + int insert_offset, selection_bound; + gboolean selection_changed; + + insert_offset = gtk_text_iter_get_offset (arg1); + + selection_bound = get_selection_bound (buffer); + if (selection_bound != insert_offset) + { + if (selection_bound != gail_text_view->previous_selection_bound || + insert_offset != gail_text_view->previous_insert_offset) + { + selection_changed = TRUE; + } + else + { + selection_changed = FALSE; + } + } + else if (gail_text_view->previous_selection_bound != gail_text_view->previous_insert_offset) + { + selection_changed = TRUE; + } + else + { + selection_changed = FALSE; + } + + emit_text_caret_moved (gail_text_view, insert_offset); + /* + * insert and selection_bound marks are different to a selection + * has changed + */ + if (selection_changed) + g_signal_emit_by_name (accessible, "text_selection_changed"); + gail_text_view->previous_selection_bound = selection_bound; + } +} + +static void +_gail_text_view_changed_cb (GtkTextBuffer *buffer, + gpointer user_data) +{ + GtkTextView *text = (GtkTextView *) user_data; + AtkObject *accessible; + GailTextView *gail_text_view; + + accessible = gtk_widget_get_accessible (GTK_WIDGET (text)); + gail_text_view = GAIL_TEXT_VIEW (accessible); + if (gail_text_view->signal_name) + { + if (!gail_text_view->insert_notify_handler) + { + gail_text_view->insert_notify_handler = g_idle_add (insert_idle_handler, accessible); + } + return; + } + emit_text_caret_moved (gail_text_view, get_insert_offset (buffer)); + gail_text_view->previous_selection_bound = get_selection_bound (buffer); +} + +static gchar* +get_text_near_offset (AtkText *text, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkTextView *view; + gpointer layout = NULL; + + view = GTK_TEXT_VIEW (GTK_ACCESSIBLE (text)->widget); + + /* + * Pass the GtkTextView to the function gail_text_util_get_text() + * so it can find the start and end of the current line on the display. + */ + if (boundary_type == ATK_TEXT_BOUNDARY_LINE_START || + boundary_type == ATK_TEXT_BOUNDARY_LINE_END) + layout = view; + + return gail_text_util_get_text (GAIL_TEXT_VIEW (text)->textutil, layout, + function, boundary_type, offset, + start_offset, end_offset); +} + +static gint +get_insert_offset (GtkTextBuffer *buffer) +{ + GtkTextMark *cursor_mark; + GtkTextIter cursor_itr; + + cursor_mark = gtk_text_buffer_get_insert (buffer); + gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark); + return gtk_text_iter_get_offset (&cursor_itr); +} + +static gint +get_selection_bound (GtkTextBuffer *buffer) +{ + GtkTextMark *selection_mark; + GtkTextIter selection_itr; + + selection_mark = gtk_text_buffer_get_selection_bound (buffer); + gtk_text_buffer_get_iter_at_mark (buffer, &selection_itr, selection_mark); + return gtk_text_iter_get_offset (&selection_itr); +} + +static void +emit_text_caret_moved (GailTextView *gail_text_view, + gint insert_offset) +{ + /* + * If we have text which has been inserted notify the user + */ + if (gail_text_view->insert_notify_handler) + { + g_source_remove (gail_text_view->insert_notify_handler); + gail_text_view->insert_notify_handler = 0; + insert_idle_handler (gail_text_view); + } + + if (insert_offset != gail_text_view->previous_insert_offset) + { + /* + * If the caret position has not changed then don't bother notifying + * + * When mouse click is used to change caret position, notification + * is received on button down and button up. + */ + g_signal_emit_by_name (gail_text_view, "text_caret_moved", insert_offset); + gail_text_view->previous_insert_offset = insert_offset; + } +} + +static gint +insert_idle_handler (gpointer data) +{ + GailTextView *gail_text_view; + GtkTextBuffer *buffer; + + GDK_THREADS_ENTER (); + + gail_text_view = GAIL_TEXT_VIEW (data); + + g_signal_emit_by_name (data, + gail_text_view->signal_name, + gail_text_view->position, + gail_text_view->length); + gail_text_view->signal_name = NULL; + gail_text_view->position = 0; + gail_text_view->length = 0; + + buffer = gail_text_view->textutil->buffer; + if (gail_text_view->insert_notify_handler) + { + /* + * If called from idle handler notify caret moved + */ + gail_text_view->insert_notify_handler = 0; + emit_text_caret_moved (gail_text_view, get_insert_offset (buffer)); + gail_text_view->previous_selection_bound = get_selection_bound (buffer); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +atk_streamable_content_interface_init (AtkStreamableContentIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->get_n_mime_types = gail_streamable_content_get_n_mime_types; + iface->get_mime_type = gail_streamable_content_get_mime_type; + iface->get_stream = gail_streamable_content_get_stream; +} + +static gint gail_streamable_content_get_n_mime_types (AtkStreamableContent *streamable) +{ + gint n_mime_types = 0; + + if (GAIL_IS_TEXT_VIEW (streamable) && GAIL_TEXT_VIEW (streamable)->textutil) + { + int i; + gboolean advertises_plaintext = FALSE; + GdkAtom *atoms = + gtk_text_buffer_get_serialize_formats ( + GAIL_TEXT_VIEW (streamable)->textutil->buffer, + &n_mime_types); + for (i = 0; i < n_mime_types-1; ++i) + if (!strcmp ("text/plain", gdk_atom_name (atoms[i]))) + advertises_plaintext = TRUE; + if (!advertises_plaintext) ++n_mime_types; + /* we support text/plain even if the GtkTextBuffer doesn't */ + } + return n_mime_types; +} + +static G_CONST_RETURN gchar* +gail_streamable_content_get_mime_type (AtkStreamableContent *streamable, gint i) +{ + if (GAIL_IS_TEXT_VIEW (streamable) && GAIL_TEXT_VIEW (streamable)->textutil) + { + gint n_mime_types = 0; + GdkAtom *atoms; + atoms = gtk_text_buffer_get_serialize_formats ( + GAIL_TEXT_VIEW (streamable)->textutil->buffer, + &n_mime_types); + if (i < n_mime_types) + { + return gdk_atom_name (atoms [i]); + } + else if (i == n_mime_types) + return "text/plain"; + } + return NULL; +} + +static GIOChannel* gail_streamable_content_get_stream (AtkStreamableContent *streamable, + const gchar *mime_type) +{ + gint i, n_mime_types = 0; + GdkAtom *atoms; + if (!GAIL_IS_TEXT_VIEW (streamable) || !GAIL_TEXT_VIEW (streamable)->textutil) + return NULL; + atoms = gtk_text_buffer_get_serialize_formats ( + GAIL_TEXT_VIEW (streamable)->textutil->buffer, + &n_mime_types); + for (i = 0; i < n_mime_types; ++i) + { + if (!strcmp ("text/plain", mime_type) || + !strcmp (gdk_atom_name (atoms[i]), mime_type)) { + GtkTextBuffer *buffer; + guint8 *cbuf; + GError *err = NULL; + gsize len, written; + gchar tname[80]; + GtkTextIter start, end; + GIOChannel *gio = NULL; + int fd; + buffer = GAIL_TEXT_VIEW (streamable)->textutil->buffer; + gtk_text_buffer_get_iter_at_offset (buffer, &start, 0); + gtk_text_buffer_get_iter_at_offset (buffer, &end, -1); + if (!strcmp ("text/plain", mime_type)) + { + cbuf = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + len = strlen (cbuf); + } + else + { + cbuf = gtk_text_buffer_serialize (buffer, buffer, atoms[i], &start, &end, &len); + } + g_snprintf (tname, 20, "streamXXXXXX"); + fd = g_mkstemp (tname); + gio = g_io_channel_unix_new (fd); + g_io_channel_set_encoding (gio, NULL, &err); + if (!err) g_io_channel_write_chars (gio, cbuf, (gssize) len, &written, &err); + else g_message (err->message); + if (!err) g_io_channel_seek_position (gio, 0, G_SEEK_SET, &err); + else g_message (err->message); + if (!err) g_io_channel_flush (gio, &err); + else g_message (err->message); + if (err) { + g_message ("<error writing to stream [%s]>", tname); + g_free (err); + } + /* make sure the file is removed on unref of the giochannel */ + else { + g_unlink (tname); + return gio; + } + } + } + return NULL; +} + diff --git a/modules/other/gail/gailtextview.h b/modules/other/gail/gailtextview.h new file mode 100644 index 000000000..4218b7b84 --- /dev/null +++ b/modules/other/gail/gailtextview.h @@ -0,0 +1,72 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TEXT_VIEW_H__ +#define __GAIL_TEXT_VIEW_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> +#include <libgail-util/gailtextutil.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TEXT_VIEW (gail_text_view_get_type ()) +#define GAIL_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TEXT_VIEW, GailTextView)) +#define GAIL_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_TEXT_VIEW, GailTextViewClass)) +#define GAIL_IS_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TEXT_VIEW)) +#define GAIL_IS_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TEXT_VIEW)) +#define GAIL_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TEXT_VIEW, GailTextViewClass)) + +typedef struct _GailTextView GailTextView; +typedef struct _GailTextViewClass GailTextViewClass; + +struct _GailTextView +{ + GailContainer parent; + + GailTextUtil *textutil; + gint previous_insert_offset; + gint previous_selection_bound; + /* + * These fields store information about text changed + */ + gchar *signal_name; + gint position; + gint length; + + guint insert_notify_handler; +}; + +GType gail_text_view_get_type (void); + +struct _GailTextViewClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_text_view_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TEXT_VIEW_H__ */ diff --git a/modules/other/gail/gailtogglebutton.c b/modules/other/gail/gailtogglebutton.c new file mode 100644 index 000000000..13d509c74 --- /dev/null +++ b/modules/other/gail/gailtogglebutton.c @@ -0,0 +1,165 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailtogglebutton.h" + +static void gail_toggle_button_class_init (GailToggleButtonClass *klass); + +static void gail_toggle_button_toggled_gtk (GtkWidget *widget); + +static void gail_toggle_button_real_notify_gtk (GObject *obj, + GParamSpec *pspec); + +static void gail_toggle_button_real_initialize (AtkObject *obj, + gpointer data); + +static AtkStateSet* gail_toggle_button_ref_state_set (AtkObject *accessible); + +static GailButtonClass *parent_class = NULL; + +GType +gail_toggle_button_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailToggleButtonClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_toggle_button_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailToggleButton), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (GAIL_TYPE_BUTTON, + "GailToggleButton", &tinfo, 0); + } + + return type; +} + +static void +gail_toggle_button_class_init (GailToggleButtonClass *klass) +{ + GailWidgetClass *widget_class; + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + widget_class = (GailWidgetClass*)klass; + widget_class->notify_gtk = gail_toggle_button_real_notify_gtk; + + parent_class = g_type_class_peek_parent (klass); + + class->ref_state_set = gail_toggle_button_ref_state_set; + class->initialize = gail_toggle_button_real_initialize; +} + +AtkObject* +gail_toggle_button_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (widget), NULL); + + object = g_object_new (GAIL_TYPE_TOGGLE_BUTTON, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_toggle_button_real_initialize (AtkObject *obj, + gpointer data) +{ + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + g_signal_connect (data, + "toggled", + G_CALLBACK (gail_toggle_button_toggled_gtk), + NULL); + + if (GTK_IS_CHECK_BUTTON (data)) + obj->role = ATK_ROLE_CHECK_BOX; + else + obj->role = ATK_ROLE_TOGGLE_BUTTON; +} + +static void +gail_toggle_button_toggled_gtk (GtkWidget *widget) +{ + AtkObject *accessible; + GtkToggleButton *toggle_button; + + toggle_button = GTK_TOGGLE_BUTTON (widget); + + accessible = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (accessible, ATK_STATE_CHECKED, + toggle_button->active); +} + +static AtkStateSet* +gail_toggle_button_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkToggleButton *toggle_button; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + toggle_button = GTK_TOGGLE_BUTTON (widget); + + if (gtk_toggle_button_get_active (toggle_button)) + atk_state_set_add_state (state_set, ATK_STATE_CHECKED); + + if (gtk_toggle_button_get_inconsistent (toggle_button)) + atk_state_set_remove_state (state_set, ATK_STATE_ENABLED); + + return state_set; +} + +static void +gail_toggle_button_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (obj); + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (toggle_button)); + + if (strcmp (pspec->name, "inconsistent") == 0) + atk_object_notify_state_change (atk_obj, ATK_STATE_ENABLED, + !gtk_toggle_button_get_inconsistent (toggle_button)); + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} diff --git a/modules/other/gail/gailtogglebutton.h b/modules/other/gail/gailtogglebutton.h new file mode 100644 index 000000000..77580f26b --- /dev/null +++ b/modules/other/gail/gailtogglebutton.h @@ -0,0 +1,59 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TOGGLE_BUTTON_H__ +#define __GAIL_TOGGLE_BUTTON_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailbutton.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TOGGLE_BUTTON (gail_toggle_button_get_type ()) +#define GAIL_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TOGGLE_BUTTON, GailToggleButton)) +#define GAIL_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_TOGGLE_BUTTON, GailToggleButtonClass)) +#define GAIL_IS_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TOGGLE_BUTTON)) +#define GAIL_IS_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TOGGLE_BUTTON)) +#define GAIL_TOGGLE_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TOGGLE_BUTTON, GailToggleButtonClass)) + +typedef struct _GailToggleButton GailToggleButton; +typedef struct _GailToggleButtonClass GailToggleButtonClass; + +struct _GailToggleButton +{ + GailButton parent; +}; + +GType gail_toggle_button_get_type (void); + +struct _GailToggleButtonClass +{ + GailButtonClass parent_class; +}; + +AtkObject* gail_toggle_button_new( GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TOGGLE_BUTTON_H__ */ diff --git a/modules/other/gail/gailtoplevel.c b/modules/other/gail/gailtoplevel.c new file mode 100644 index 000000000..237204b02 --- /dev/null +++ b/modules/other/gail/gailtoplevel.c @@ -0,0 +1,399 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <gtk/gtk.h> +#include "gailtoplevel.h" + +static void gail_toplevel_class_init (GailToplevelClass *klass); +static void gail_toplevel_object_init (GailToplevel *toplevel); +static void gail_toplevel_object_finalize (GObject *obj); + +/* atkobject.h */ + +static gint gail_toplevel_get_n_children (AtkObject *obj); +static AtkObject* gail_toplevel_ref_child (AtkObject *obj, + gint i); +static AtkObject* gail_toplevel_get_parent (AtkObject *obj); + +/* Callbacks */ + + +static void gail_toplevel_window_destroyed (GtkWindow *window, + GailToplevel *text); +static gboolean gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static gboolean gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data); + +/* Misc */ + +static void _gail_toplevel_remove_child (GailToplevel *toplevel, + GtkWindow *window); +static gboolean is_attached_menu_window (GtkWidget *widget); +static gboolean is_combo_window (GtkWidget *widget); + + +static gpointer parent_class = NULL; + +GType +gail_toplevel_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailToplevelClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_toplevel_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailToplevel), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_toplevel_object_init, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (ATK_TYPE_OBJECT, + "GailToplevel", &tinfo, 0); + } + + return type; +} + +AtkObject* +gail_toplevel_new (void) +{ + GObject *object; + AtkObject *accessible; + + object = g_object_new (GAIL_TYPE_TOPLEVEL, NULL); + g_return_val_if_fail ((object != NULL), NULL); + + accessible = ATK_OBJECT (object); + accessible->role = ATK_ROLE_APPLICATION; + accessible->name = g_get_prgname(); + accessible->accessible_parent = NULL; + + return accessible; +} + +static void +gail_toplevel_class_init (GailToplevelClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS(klass); + GObjectClass *g_object_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent (klass); + + class->get_n_children = gail_toplevel_get_n_children; + class->ref_child = gail_toplevel_ref_child; + class->get_parent = gail_toplevel_get_parent; + + g_object_class->finalize = gail_toplevel_object_finalize; +} + +static void +gail_toplevel_object_init (GailToplevel *toplevel) +{ + GtkWindow *window; + GtkWidget *widget; + GList *l; + guint signal_id; + + l = toplevel->window_list = gtk_window_list_toplevels (); + + while (l) + { + window = GTK_WINDOW (l->data); + widget = GTK_WIDGET (window); + if (!window || + !GTK_WIDGET_VISIBLE (widget) || + is_attached_menu_window (widget) || + GTK_WIDGET (window)->parent || + GTK_IS_PLUG (window)) + { + GList *temp_l = l->next; + + toplevel->window_list = g_list_delete_link (toplevel->window_list, l); + l = temp_l; + } + else + { + g_signal_connect (G_OBJECT (window), + "destroy", + G_CALLBACK (gail_toplevel_window_destroyed), + toplevel); + l = l->next; + } + } + + gtk_type_class (GTK_TYPE_WINDOW); + + signal_id = g_signal_lookup ("show", GTK_TYPE_WINDOW); + g_signal_add_emission_hook (signal_id, 0, + gail_toplevel_show_event_watcher, toplevel, (GDestroyNotify) NULL); + + signal_id = g_signal_lookup ("hide", GTK_TYPE_WINDOW); + g_signal_add_emission_hook (signal_id, 0, + gail_toplevel_hide_event_watcher, toplevel, (GDestroyNotify) NULL); +} + +static void +gail_toplevel_object_finalize (GObject *obj) +{ + GailToplevel *toplevel = GAIL_TOPLEVEL (obj); + + if (toplevel->window_list) + g_list_free (toplevel->window_list); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static AtkObject* +gail_toplevel_get_parent (AtkObject *obj) +{ + return NULL; +} + +static gint +gail_toplevel_get_n_children (AtkObject *obj) +{ + GailToplevel *toplevel = GAIL_TOPLEVEL (obj); + + gint rc = g_list_length (toplevel->window_list); + return rc; +} + +static AtkObject* +gail_toplevel_ref_child (AtkObject *obj, + gint i) +{ + GailToplevel *toplevel; + gpointer ptr; + GtkWidget *widget; + AtkObject *atk_obj; + + toplevel = GAIL_TOPLEVEL (obj); + ptr = g_list_nth_data (toplevel->window_list, i); + if (!ptr) + return NULL; + widget = GTK_WIDGET (ptr); + atk_obj = gtk_widget_get_accessible (widget); + + g_object_ref (atk_obj); + return atk_obj; +} + +/* + * Window destroy events on GtkWindow cause a child to be removed + * from the toplevel + */ +static void +gail_toplevel_window_destroyed (GtkWindow *window, + GailToplevel *toplevel) +{ + _gail_toplevel_remove_child (toplevel, window); +} + +/* + * Show events cause a child to be added to the toplevel + */ +static gboolean +gail_toplevel_show_event_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GailToplevel *toplevel = GAIL_TOPLEVEL (data); + AtkObject *atk_obj = ATK_OBJECT (toplevel); + GObject *object; + GtkWidget *widget; + gint n_children; + AtkObject *child; + + object = g_value_get_object (param_values + 0); + + if (!GTK_IS_WINDOW (object)) + return TRUE; + + widget = GTK_WIDGET (object); + if (widget->parent || + is_attached_menu_window (widget) || + is_combo_window (widget) || + GTK_IS_PLUG (widget)) + return TRUE; + + child = gtk_widget_get_accessible (widget); + if (!strcmp (atk_role_get_name (atk_object_get_role (child)), "redundant object")) + { + return TRUE; + } + + child = gtk_widget_get_accessible (widget); + if (!strcmp (atk_role_get_name (atk_object_get_role (child)), "redundant object")) + { + return TRUE; + } + + /* + * Add the window to the list & emit the signal. + * Don't do this for tooltips (Bug #150649). + */ + if (atk_object_get_role (child) != ATK_ROLE_TOOL_TIP) + { + toplevel->window_list = g_list_append (toplevel->window_list, widget); + + n_children = g_list_length (toplevel->window_list); + + /* + * Must subtract 1 from the n_children since the index is 0-based + * but g_list_length is 1-based. + */ + atk_object_set_parent (child, atk_obj); + g_signal_emit_by_name (atk_obj, "children-changed::add", + n_children - 1, + child, NULL); + } + + /* Connect destroy signal callback */ + g_signal_connect (G_OBJECT(object), + "destroy", + G_CALLBACK (gail_toplevel_window_destroyed), + toplevel); + + return TRUE; +} + +/* + * Hide events on GtkWindow cause a child to be removed from the toplevel + */ +static gboolean +gail_toplevel_hide_event_watcher (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GailToplevel *toplevel = GAIL_TOPLEVEL (data); + GObject *object; + + object = g_value_get_object (param_values + 0); + + if (!GTK_IS_WINDOW (object)) + return TRUE; + + _gail_toplevel_remove_child (toplevel, GTK_WINDOW (object)); + return TRUE; +} + +/* + * Common code used by destroy and hide events on GtkWindow + */ +static void +_gail_toplevel_remove_child (GailToplevel *toplevel, + GtkWindow *window) +{ + AtkObject *atk_obj = ATK_OBJECT (toplevel); + GList *l; + guint window_count = 0; + AtkObject *child; + + if (toplevel->window_list) + { + GtkWindow *tmp_window; + + /* Must loop through them all */ + for (l = toplevel->window_list; l; l = l->next) + { + tmp_window = GTK_WINDOW (l->data); + + if (window == tmp_window) + { + /* Remove the window from the window_list & emit the signal */ + toplevel->window_list = g_list_remove (toplevel->window_list, + l->data); + child = gtk_widget_get_accessible (GTK_WIDGET (window)); + g_signal_emit_by_name (atk_obj, "children-changed::remove", + window_count, + child, NULL); + atk_object_set_parent (child, NULL); + break; + } + + window_count++; + } + } +} + +static gboolean +is_attached_menu_window (GtkWidget *widget) +{ + GtkWidget *child = GTK_BIN (widget)->child; + gboolean ret = FALSE; + + if (GTK_IS_MENU (child)) + { + GtkWidget *attach; + + attach = gtk_menu_get_attach_widget (GTK_MENU (child)); + /* Allow for menu belonging to the Panel Menu, which is a GtkButton */ + if (GTK_IS_MENU_ITEM (attach) || + GTK_IS_OPTION_MENU (attach) || + GTK_IS_BUTTON (attach)) + ret = TRUE; + } + return ret; +} + +static gboolean +is_combo_window (GtkWidget *widget) +{ + GtkWidget *child = GTK_BIN (widget)->child; + AtkObject *obj; + GtkAccessible *accessible; + + if (!GTK_IS_EVENT_BOX (child)) + return FALSE; + + child = GTK_BIN (child)->child; + + if (!GTK_IS_FRAME (child)) + return FALSE; + + child = GTK_BIN (child)->child; + + if (!GTK_IS_SCROLLED_WINDOW (child)) + return FALSE; + + obj = gtk_widget_get_accessible (child); + obj = atk_object_get_parent (obj); + accessible = GTK_ACCESSIBLE (obj); + if (GTK_IS_COMBO (accessible->widget)) + return TRUE; + + return FALSE; +} diff --git a/modules/other/gail/gailtoplevel.h b/modules/other/gail/gailtoplevel.h new file mode 100644 index 000000000..5044a4152 --- /dev/null +++ b/modules/other/gail/gailtoplevel.h @@ -0,0 +1,58 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TOPLEVEL_H__ +#define __GAIL_TOPLEVEL_H__ + +#include <atk/atk.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TOPLEVEL (gail_toplevel_get_type ()) +#define GAIL_TOPLEVEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TOPLEVEL, GailToplevel)) +#define GAIL_TOPLEVEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_TOPLEVEL, GailToplevelClass)) +#define GAIL_IS_TOPLEVEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TOPLEVEL)) +#define GAIL_IS_TOPLEVEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TOPLEVEL)) +#define GAIL_TOPLEVEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TOPLEVEL, GailToplevelClass)) + +typedef struct _GailToplevel GailToplevel; +typedef struct _GailToplevelClass GailToplevelClass; + +struct _GailToplevel +{ + AtkObject parent; + GList *window_list; +}; + +GType gail_toplevel_get_type (void); + +struct _GailToplevelClass +{ + AtkObjectClass parent_class; +}; + +AtkObject *gail_toplevel_new(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GAIL_TOPLEVEL_H__ */ diff --git a/modules/other/gail/gailtreeview.c b/modules/other/gail/gailtreeview.c new file mode 100644 index 000000000..ef3e42e07 --- /dev/null +++ b/modules/other/gail/gailtreeview.c @@ -0,0 +1,4710 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/x11/gdkx.h> +#endif +#include <gtk/gtktreeviewcolumn.h> +#include "gailtreeview.h" +#include "gailrenderercell.h" +#include "gailbooleancell.h" +#include "gailcontainercell.h" +#include "gailtextcell.h" +#include "gailcellparent.h" +#include "gail-private-macros.h" + +typedef struct _GailTreeViewRowInfo GailTreeViewRowInfo; +typedef struct _GailTreeViewCellInfo GailTreeViewCellInfo; + +static void gail_tree_view_class_init (GailTreeViewClass *klass); +static void gail_tree_view_real_initialize (AtkObject *obj, + gpointer data); +static void gail_tree_view_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static void gail_tree_view_finalize (GObject *object); + +static void gail_tree_view_connect_widget_destroyed + (GtkAccessible *accessible); +static void gail_tree_view_destroyed (GtkWidget *widget, + GtkAccessible *accessible); +/* atkobject.h */ + +static gint gail_tree_view_get_n_children (AtkObject *obj); +static AtkObject* gail_tree_view_ref_child (AtkObject *obj, + gint i); +static AtkStateSet* gail_tree_view_ref_state_set (AtkObject *obj); + +/* atkcomponent.h */ + +static void atk_component_interface_init (AtkComponentIface *iface); + +static AtkObject* gail_tree_view_ref_accessible_at_point + (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type); + +/* atktable.h */ + +static void atk_table_interface_init (AtkTableIface *iface); + +static gint gail_tree_view_get_index_at (AtkTable *table, + gint row, + gint column); +static gint gail_tree_view_get_column_at_index + (AtkTable *table, + gint index); +static gint gail_tree_view_get_row_at_index (AtkTable *table, + gint index); + +static AtkObject* gail_tree_view_table_ref_at (AtkTable *table, + gint row, + gint column); +static gint gail_tree_view_get_n_rows (AtkTable *table); +static gint gail_tree_view_get_n_columns (AtkTable *table); +static gint get_n_actual_columns (GtkTreeView *tree_view); +static gboolean gail_tree_view_is_row_selected (AtkTable *table, + gint row); +static gboolean gail_tree_view_is_selected (AtkTable *table, + gint row, + gint column); +static gint gail_tree_view_get_selected_rows + (AtkTable *table, + gint **selected); +static gboolean gail_tree_view_add_row_selection + (AtkTable *table, + gint row); +static gboolean gail_tree_view_remove_row_selection + (AtkTable *table, + gint row); +static AtkObject* gail_tree_view_get_row_header (AtkTable *table, + gint row); +static AtkObject* gail_tree_view_get_column_header + (AtkTable *table, + gint column); +static void gail_tree_view_set_row_header (AtkTable *table, + gint row, + AtkObject *header); +static void gail_tree_view_set_column_header + (AtkTable *table, + gint column, + AtkObject *header); +static AtkObject* + gail_tree_view_get_caption (AtkTable *table); +static void gail_tree_view_set_caption (AtkTable *table, + AtkObject *caption); +static AtkObject* gail_tree_view_get_summary (AtkTable *table); +static void gail_tree_view_set_summary (AtkTable *table, + AtkObject *accessible); +static G_CONST_RETURN gchar* + gail_tree_view_get_row_description + (AtkTable *table, + gint row); +static void gail_tree_view_set_row_description + (AtkTable *table, + gint row, + const gchar *description); +static G_CONST_RETURN gchar* + gail_tree_view_get_column_description + (AtkTable *table, + gint column); +static void gail_tree_view_set_column_description + (AtkTable *table, + gint column, + const gchar *description); + +static void set_row_data (AtkTable *table, + gint row, + AtkObject *header, + const gchar *description, + gboolean is_header); +static GailTreeViewRowInfo* + get_row_info (AtkTable *table, + gint row); + +/* atkselection.h */ + +static void atk_selection_interface_init (AtkSelectionIface *iface); +static gboolean gail_tree_view_add_selection (AtkSelection *selection, + gint i); +static gboolean gail_tree_view_clear_selection (AtkSelection *selection); +static AtkObject* gail_tree_view_ref_selection (AtkSelection *selection, + gint i); +static gint gail_tree_view_get_selection_count + (AtkSelection *selection); +static gboolean gail_tree_view_is_child_selected + (AtkSelection *selection, + gint i); + +/* gailcellparent.h */ + +static void gail_cell_parent_interface_init (GailCellParentIface *iface); +static void gail_tree_view_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); +static void gail_tree_view_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect); +static gboolean gail_tree_view_grab_cell_focus (GailCellParent *parent, + GailCell *cell); + +/* signal handling */ + +static gboolean gail_tree_view_expand_row_gtk (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); +static gint idle_expand_row (gpointer data); +static gboolean gail_tree_view_collapse_row_gtk (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path); +static void gail_tree_view_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation); +static void gail_tree_view_set_scroll_adjustments + (GtkWidget *widget, + GtkAdjustment *hadj, + GtkAdjustment *vadj); +static void gail_tree_view_changed_gtk (GtkTreeSelection *selection, + gpointer data); + +static void columns_changed (GtkTreeView *tree_view); +static void cursor_changed (GtkTreeView *tree_view); +static gint idle_cursor_changed (gpointer data); + +static void model_row_changed (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static void column_visibility_changed (GObject *object, + GParamSpec *param, + gpointer user_data); +static void column_destroy (GtkObject *obj); +static void model_row_inserted (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static void model_row_deleted (GtkTreeModel *tree_model, + GtkTreePath *path, + gpointer user_data); +static void destroy_count_func (GtkTreeView *tree_view, + GtkTreePath *path, + gint count, + gpointer user_data); +static void model_rows_reordered (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gint *new_order, + gpointer user_data); +static void adjustment_changed (GtkAdjustment *adjustment, + GtkTreeView *tree_view); + +/* Misc */ + +static void set_iter_nth_row (GtkTreeView *tree_view, + GtkTreeIter *iter, + gint row); +static gint get_row_from_tree_path (GtkTreeView *tree_view, + GtkTreePath *path); +static GtkTreeViewColumn* get_column (GtkTreeView *tree_view, + gint in_col); +static gint get_actual_column_number (GtkTreeView *tree_view, + gint visible_column); +static gint get_visible_column_number (GtkTreeView *tree_view, + gint actual_column); +static void iterate_thru_children (GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreePath *tree_path, + GtkTreePath *orig, + gint *count, + gint depth); +static GtkTreeIter* return_iter_nth_row (GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint increment, + gint row); +static void free_row_info (GArray *array, + gint array_idx, + gboolean shift); +static void clean_cell_info (GailTreeView *tree_view, + GList *list); +static void clean_rows (GailTreeView *tree_view); +static void clean_cols (GailTreeView *tree_view, + GtkTreeViewColumn *tv_col); +static void traverse_cells (GailTreeView *tree_view, + GtkTreePath *tree_path, + gboolean set_stale, + gboolean inc_row); +static gboolean update_cell_value (GailRendererCell *renderer_cell, + GailTreeView *gailview, + gboolean emit_change_signal); +static void set_cell_visibility (GtkTreeView *tree_view, + GailCell *cell, + GtkTreeViewColumn *tv_col, + GtkTreePath *tree_path, + gboolean emit_signal); +static gboolean is_cell_showing (GtkTreeView *tree_view, + GdkRectangle *cell_rect); +static void set_expand_state (GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GailTreeView *gailview, + GtkTreePath *tree_path, + gboolean set_on_ancestor); +static void add_cell_actions (GailCell *cell, + gboolean editable); + +static void toggle_cell_expanded (GailCell *cell); +static void toggle_cell_toggled (GailCell *cell); +static void edit_cell (GailCell *cell); +static void activate_cell (GailCell *cell); +static void cell_destroyed (gpointer data); +#if 0 +static void cell_info_remove (GailTreeView *tree_view, + GailCell *cell); +#endif +static void cell_info_get_index (GtkTreeView *tree_view, + GailTreeViewCellInfo *info, + gint *index); +static void cell_info_new (GailTreeView *gailview, + GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeViewColumn *tv_col, + GailCell *cell); +static GailCell* find_cell (GailTreeView *gailview, + gint index); +static void refresh_cell_index (GailCell *cell); +static void get_selected_rows (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); +static void connect_model_signals (GtkTreeView *view, + GailTreeView *gailview); +static void disconnect_model_signals (GailTreeView *gailview); +static void clear_cached_data (GailTreeView *view); +static gint get_column_number (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gboolean visible); +static gint get_focus_index (GtkTreeView *tree_view); +static gint get_index (GtkTreeView *tree_view, + GtkTreePath *path, + gint actual_column); +static void count_rows (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *end_path, + gint *count, + gint level, + gint depth); + +static gboolean get_next_node_with_child_at_depth + (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath **path, + gint level, + gint depth); +static gboolean get_next_node_with_child (GtkTreeModel *model, + GtkTreePath *path, + GtkTreePath **return_path); +static gboolean get_tree_path_from_row_index (GtkTreeModel *model, + gint row_index, + GtkTreePath **tree_path); +static gint get_row_count (GtkTreeModel *model); +static gboolean get_path_column_from_index (GtkTreeView *tree_view, + gint index, + GtkTreePath **path, + GtkTreeViewColumn **column); +static void set_cell_expandable (GailCell *cell); + +static GailTreeViewCellInfo* find_cell_info (GailTreeView *view, + GailCell *cell, + GList** list, + gboolean live_only); +static AtkObject * get_header_from_column (GtkTreeViewColumn *tv_col); +static gboolean idle_garbage_collect_cell_data (gpointer data); +static gboolean garbage_collect_cell_data (gpointer data); + +static GailWidgetClass *parent_class = NULL; +static GQuark quark_column_desc_object = 0; +static GQuark quark_column_header_object = 0; +static gboolean editing = FALSE; +static const gchar* hadjustment = "hadjustment"; +static const gchar* vadjustment = "vadjustment"; + +struct _GailTreeViewRowInfo +{ + GtkTreeRowReference *row_ref; + gchar *description; + AtkObject *header; +}; + +struct _GailTreeViewCellInfo +{ + GailCell *cell; + GtkTreeRowReference *cell_row_ref; + GtkTreeViewColumn *cell_col_ref; + GailTreeView *view; + gboolean in_use; +}; + +GType +gail_tree_view_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailTreeViewClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_tree_view_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailTreeView), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_table_info = + { + (GInterfaceInitFunc) atk_table_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_selection_info = + { + (GInterfaceInitFunc) atk_selection_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) atk_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + static const GInterfaceInfo gail_cell_parent_info = + { + (GInterfaceInitFunc) gail_cell_parent_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailTreeView", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_TABLE, + &atk_table_info); + g_type_add_interface_static (type, ATK_TYPE_SELECTION, + &atk_selection_info); + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + g_type_add_interface_static (type, GAIL_TYPE_CELL_PARENT, + &gail_cell_parent_info); + } + + return type; +} + +static void +gail_tree_view_class_init (GailTreeViewClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkAccessibleClass *accessible_class; + GailWidgetClass *widget_class; + GailContainerClass *container_class; + + accessible_class = (GtkAccessibleClass*)klass; + widget_class = (GailWidgetClass*)klass; + container_class = (GailContainerClass*)klass; + + parent_class = g_type_class_peek_parent (klass); + + class->get_n_children = gail_tree_view_get_n_children; + class->ref_child = gail_tree_view_ref_child; + class->ref_state_set = gail_tree_view_ref_state_set; + class->initialize = gail_tree_view_real_initialize; + + widget_class->notify_gtk = gail_tree_view_real_notify_gtk; + + accessible_class->connect_widget_destroyed = gail_tree_view_connect_widget_destroyed; + + /* + * The children of a GtkTreeView are the buttons at the top of the columns + * we do not represent these as children so we do not want to report + * children added or deleted when these changed. + */ + container_class->add_gtk = NULL; + container_class->remove_gtk = NULL; + + gobject_class->finalize = gail_tree_view_finalize; + + quark_column_desc_object = g_quark_from_static_string ("gtk-column-object"); + quark_column_header_object = g_quark_from_static_string ("gtk-header-object"); +} + +static void +gail_tree_view_real_initialize (AtkObject *obj, + gpointer data) +{ + GailTreeView *view; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GtkAdjustment *adj; + GList *tv_cols, *tmp_list; + GtkWidget *widget; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + view = GAIL_TREE_VIEW (obj); + view->caption = NULL; + view->summary = NULL; + view->row_data = NULL; + view->col_data = NULL; + view->cell_data = NULL; + view->focus_cell = NULL; + view->old_hadj = NULL; + view->old_vadj = NULL; + view->idle_expand_id = 0; + view->idle_expand_path = NULL; + + view->n_children_deleted = 0; + + widget = GTK_WIDGET (data); + g_signal_connect_after (widget, + "row-collapsed", + G_CALLBACK (gail_tree_view_collapse_row_gtk), + NULL); + g_signal_connect (widget, + "row-expanded", + G_CALLBACK (gail_tree_view_expand_row_gtk), + NULL); + g_signal_connect (widget, + "size-allocate", + G_CALLBACK (gail_tree_view_size_allocate_gtk), + NULL); + + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + + /* Set up signal handling */ + + g_signal_connect_data (gtk_tree_view_get_selection (tree_view), + "changed", + (GCallback) gail_tree_view_changed_gtk, + obj, NULL, 0); + + g_signal_connect_data (tree_view, "columns-changed", + (GCallback) columns_changed, NULL, NULL, 0); + g_signal_connect_data (tree_view, "cursor-changed", + (GCallback) cursor_changed, NULL, NULL, 0); + + view->tree_model = tree_model; + if (tree_model) + { + g_object_add_weak_pointer (G_OBJECT (view->tree_model), (gpointer *)&view->tree_model); + connect_model_signals (tree_view, view); + + if (GTK_IS_TREE_STORE (tree_model)) + obj->role = ATK_ROLE_TREE_TABLE; + else + obj->role = ATK_ROLE_TABLE; + } + else + { + obj->role = ATK_ROLE_UNKNOWN; + } + + /* adjustment callbacks */ + + g_object_get (tree_view, hadjustment, &adj, NULL); + view->old_hadj = adj; + g_object_add_weak_pointer (G_OBJECT (view->old_hadj), (gpointer *)&view->old_hadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + tree_view); + + g_object_get (tree_view, vadjustment, &adj, NULL); + view->old_vadj = adj; + g_object_add_weak_pointer (G_OBJECT (view->old_vadj), (gpointer *)&view->old_vadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + tree_view); + g_signal_connect_after (widget, + "set_scroll_adjustments", + G_CALLBACK (gail_tree_view_set_scroll_adjustments), + NULL); + + view->col_data = g_array_sized_new (FALSE, TRUE, + sizeof(GtkTreeViewColumn *), 0); + + tv_cols = gtk_tree_view_get_columns (tree_view); + + for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next) + { + g_signal_connect_data (tmp_list->data, "notify::visible", + (GCallback)column_visibility_changed, + tree_view, NULL, FALSE); + g_signal_connect_data (tmp_list->data, "destroy", + (GCallback)column_destroy, + NULL, NULL, FALSE); + g_array_append_val (view->col_data, tmp_list->data); + } + + gtk_tree_view_set_destroy_count_func (tree_view, + destroy_count_func, + NULL, NULL); + g_list_free (tv_cols); +} + +static void +gail_tree_view_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget; + AtkObject* atk_obj; + GtkTreeView *tree_view; + GailTreeView *gailview; + GtkAdjustment *adj; + + widget = GTK_WIDGET (obj); + atk_obj = gtk_widget_get_accessible (widget); + tree_view = GTK_TREE_VIEW (widget); + gailview = GAIL_TREE_VIEW (atk_obj); + + if (strcmp (pspec->name, "model") == 0) + { + GtkTreeModel *tree_model; + AtkRole role; + + tree_model = gtk_tree_view_get_model (tree_view); + if (gailview->tree_model) + disconnect_model_signals (gailview); + clear_cached_data (gailview); + gailview->tree_model = tree_model; + /* + * if there is no model the GtkTreeView is probably being destroyed + */ + if (tree_model) + { + g_object_add_weak_pointer (G_OBJECT (gailview->tree_model), (gpointer *)&gailview->tree_model); + connect_model_signals (tree_view, gailview); + + if (GTK_IS_TREE_STORE (tree_model)) + role = ATK_ROLE_TREE_TABLE; + else + role = ATK_ROLE_TABLE; + } + else + { + role = ATK_ROLE_UNKNOWN; + } + atk_object_set_role (atk_obj, role); + g_object_freeze_notify (G_OBJECT (atk_obj)); + g_signal_emit_by_name (atk_obj, "model_changed"); + g_signal_emit_by_name (atk_obj, "visible_data_changed"); + g_object_thaw_notify (G_OBJECT (atk_obj)); + } + else if (strcmp (pspec->name, hadjustment) == 0) + { + g_object_get (tree_view, hadjustment, &adj, NULL); + g_signal_handlers_disconnect_by_func (gailview->old_hadj, + (gpointer) adjustment_changed, + widget); + gailview->old_hadj = adj; + g_object_add_weak_pointer (G_OBJECT (gailview->old_hadj), (gpointer *)&gailview->old_hadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + tree_view); + } + else if (strcmp (pspec->name, vadjustment) == 0) + { + g_object_get (tree_view, vadjustment, &adj, NULL); + g_signal_handlers_disconnect_by_func (gailview->old_vadj, + (gpointer) adjustment_changed, + widget); + gailview->old_vadj = adj; + g_object_add_weak_pointer (G_OBJECT (gailview->old_hadj), (gpointer *)&gailview->old_vadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + tree_view); + } + else + parent_class->notify_gtk (obj, pspec); +} + +AtkObject* +gail_tree_view_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), NULL); + + object = g_object_new (GAIL_TYPE_TREE_VIEW, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +static void +gail_tree_view_finalize (GObject *object) +{ + GailTreeView *view = GAIL_TREE_VIEW (object); + + clear_cached_data (view); + + /* remove any idle handlers still pending */ + if (view->idle_garbage_collect_id) + g_source_remove (view->idle_garbage_collect_id); + + if (view->caption) + g_object_unref (view->caption); + if (view->summary) + g_object_unref (view->summary); + + if (view->tree_model) + disconnect_model_signals (view); + + if (view->col_data) + { + GArray *array = view->col_data; + + /* + * No need to free the contents of the array since it + * just contains pointers to the GtkTreeViewColumn + * objects that are in the GtkTreeView. + */ + g_array_free (array, TRUE); + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gail_tree_view_connect_widget_destroyed (GtkAccessible *accessible) +{ + if (accessible->widget) + { + g_signal_connect_after (accessible->widget, + "destroy", + G_CALLBACK (gail_tree_view_destroyed), + accessible); + } + GTK_ACCESSIBLE_CLASS (parent_class)->connect_widget_destroyed (accessible); +} + +static void +gail_tree_view_destroyed (GtkWidget *widget, + GtkAccessible *accessible) +{ + GtkAdjustment *adj; + GailTreeView *gailview; + + gail_return_if_fail (GTK_IS_TREE_VIEW (widget)); + + gailview = GAIL_TREE_VIEW (accessible); + adj = gailview->old_hadj; + if (adj) + g_signal_handlers_disconnect_by_func (adj, + (gpointer) adjustment_changed, + widget); + adj = gailview->old_vadj; + if (adj) + g_signal_handlers_disconnect_by_func (adj, + (gpointer) adjustment_changed, + widget); + if (gailview->tree_model) + { + disconnect_model_signals (gailview); + gailview->tree_model = NULL; + } + if (gailview->focus_cell) + { + g_object_unref (gailview->focus_cell); + gailview->focus_cell = NULL; + } + if (gailview->idle_expand_id) + { + g_source_remove (gailview->idle_expand_id); + gailview->idle_expand_id = 0; + } +} + +gint +get_focus_index (GtkTreeView *tree_view) +{ + GtkTreePath *focus_path; + GtkTreeViewColumn *focus_column; + gint index; + + gtk_tree_view_get_cursor (tree_view, &focus_path, &focus_column); + if (focus_path && focus_column) + { + + index = get_index (tree_view, focus_path, + get_column_number (tree_view, focus_column, FALSE)); + } + else + index = -1; + + if (focus_path) + gtk_tree_path_free (focus_path); + + return index; +} + +AtkObject * +gail_tree_view_ref_focus_cell (GtkTreeView *tree_view) +{ + /* + * This function returns a reference to the accessible object for the cell + * in the treeview which has focus, if a cell has focus. + */ + AtkObject *focus_cell = NULL; + AtkObject *atk_obj; + gint focus_index; + + focus_index = get_focus_index (tree_view); + if (focus_index >= 0) + { + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + focus_cell = atk_object_ref_accessible_child (atk_obj, focus_index); + } + + return focus_cell; +} + +/* atkobject.h */ + +static gint +gail_tree_view_get_n_children (AtkObject *obj) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + gint n_rows, n_cols; + + gail_return_val_if_fail (GAIL_IS_TREE_VIEW (obj), 0); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return 0; + + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + + /* + * We get the total number of rows including those which are collapsed + */ + n_rows = get_row_count (tree_model); + /* + * We get the total number of columns including those which are not visible + */ + n_cols = get_n_actual_columns (tree_view); + return (n_rows * n_cols); +} + +static AtkObject* +gail_tree_view_ref_child (AtkObject *obj, + gint i) +{ + GtkWidget *widget; + GailTreeView *gailview; + GailCell *cell; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GtkCellRenderer *renderer; + GtkTreeIter iter; + GtkTreeViewColumn *tv_col; + GtkTreeSelection *selection; + GtkTreePath *path; + AtkRegistry *default_registry; + AtkObjectFactory *factory; + AtkObject *child; + AtkObject *parent; + GtkTreeViewColumn *expander_tv; + GList *renderer_list; + GList *l; + GailContainerCell *container = NULL; + GailRendererCell *renderer_cell; + gboolean is_expander, is_expanded, retval; + gboolean editable = FALSE; + gint focus_index; + + g_return_val_if_fail (GAIL_IS_TREE_VIEW (obj), NULL); + g_return_val_if_fail (i >= 0, NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + if (i >= gail_tree_view_get_n_children (obj)) + return NULL; + + tree_view = GTK_TREE_VIEW (widget); + if (i < get_n_actual_columns (tree_view)) + { + tv_col = gtk_tree_view_get_column (tree_view, i); + child = get_header_from_column (tv_col); + if (child) + g_object_ref (child); + return child; + } + + gailview = GAIL_TREE_VIEW (obj); + /* + * Check whether the child is cached + */ + cell = find_cell (gailview, i); + if (cell) + { + g_object_ref (cell); + return ATK_OBJECT (cell); + } + + if (gailview->focus_cell == NULL) + focus_index = get_focus_index (tree_view); + else + focus_index = -1; + /* + * Find the TreePath and GtkTreeViewColumn for the index + */ + if (!get_path_column_from_index (tree_view, i, &path, &tv_col)) + return NULL; + + tree_model = gtk_tree_view_get_model (tree_view); + retval = gtk_tree_model_get_iter (tree_model, &iter, path); + gail_return_val_if_fail (retval, NULL); + + expander_tv = gtk_tree_view_get_expander_column (tree_view); + is_expander = FALSE; + is_expanded = FALSE; + if (gtk_tree_model_iter_has_child (tree_model, &iter)) + { + if (expander_tv == tv_col) + { + is_expander = TRUE; + is_expanded = gtk_tree_view_row_expanded (tree_view, path); + } + } + gtk_tree_view_column_cell_set_cell_data (tv_col, tree_model, &iter, + is_expander, is_expanded); + + renderer_list = gtk_tree_view_column_get_cell_renderers (tv_col); + + /* If there are more than one renderer in the list, make a container */ + + if (renderer_list && renderer_list->next) + { + GailCell *container_cell; + + container = gail_container_cell_new (); + gail_return_val_if_fail (container, NULL); + + container_cell = GAIL_CELL (container); + gail_cell_init (container_cell, + widget, ATK_OBJECT (gailview), + i); + /* + * The GailTreeViewCellInfo structure for the container will be before + * the ones for the cells so that the first one we find for a position + * will be for the container + */ + cell_info_new (gailview, tree_model, path, tv_col, container_cell); + container_cell->refresh_index = refresh_cell_index; + parent = ATK_OBJECT (container); + } + else + parent = ATK_OBJECT (gailview); + + child = NULL; + + /* + * Now we make a fake cell_renderer if there is no cell in renderer_list + */ + + if (renderer_list == NULL) + { + GtkCellRenderer *fake_renderer; + fake_renderer = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, NULL); + default_registry = atk_get_default_registry (); + factory = atk_registry_get_factory (default_registry, + GTK_OBJECT_TYPE (fake_renderer)); + child = atk_object_factory_create_accessible (factory, + G_OBJECT (fake_renderer)); + gail_return_val_if_fail (GAIL_IS_RENDERER_CELL (child), NULL); + cell = GAIL_CELL (child); + renderer_cell = GAIL_RENDERER_CELL (child); + renderer_cell->renderer = fake_renderer; + + /* Create the GailTreeViewCellInfo structure for this cell */ + cell_info_new (gailview, tree_model, path, tv_col, cell); + + gail_cell_init (cell, + widget, parent, + i); + + cell->refresh_index = refresh_cell_index; + + /* set state if it is expandable */ + if (is_expander) + { + set_cell_expandable (cell); + if (is_expanded) + gail_cell_add_state (cell, + ATK_STATE_EXPANDED, + FALSE); + } + } else { + for (l = renderer_list; l; l = l->next) + { + renderer = GTK_CELL_RENDERER (l->data); + + if (GTK_IS_CELL_RENDERER_TEXT (renderer)) + g_object_get (G_OBJECT (renderer), "editable", &editable, NULL); + + default_registry = atk_get_default_registry (); + factory = atk_registry_get_factory (default_registry, + GTK_OBJECT_TYPE (renderer)); + child = atk_object_factory_create_accessible (factory, + G_OBJECT (renderer)); + gail_return_val_if_fail (GAIL_IS_RENDERER_CELL (child), NULL); + cell = GAIL_CELL (child); + renderer_cell = GAIL_RENDERER_CELL (child); + + /* Create the GailTreeViewCellInfo structure for this cell */ + cell_info_new (gailview, tree_model, path, tv_col, cell); + + gail_cell_init (cell, + widget, parent, + i); + + if (container) + gail_container_cell_add_child (container, cell); + else + cell->refresh_index = refresh_cell_index; + + update_cell_value (renderer_cell, gailview, FALSE); + /* Add the actions appropriate for this cell */ + add_cell_actions (cell, editable); + + /* set state if it is expandable */ + if (is_expander) + { + set_cell_expandable (cell); + if (is_expanded) + gail_cell_add_state (cell, + ATK_STATE_EXPANDED, + FALSE); + } + /* + * If the column is visible, sets the cell's state + */ + if (gtk_tree_view_column_get_visible (tv_col)) + set_cell_visibility (tree_view, cell, tv_col, path, FALSE); + /* + * If the row is selected, all cells on the row are selected + */ + selection = gtk_tree_view_get_selection (tree_view); + + if (gtk_tree_selection_path_is_selected (selection, path)) + gail_cell_add_state (cell, ATK_STATE_SELECTED, FALSE); + + gail_cell_add_state (cell, ATK_STATE_FOCUSABLE, FALSE); + if (focus_index == i) + { + gailview->focus_cell = g_object_ref (cell); + gail_cell_add_state (cell, ATK_STATE_FOCUSED, FALSE); + } + } + g_list_free (renderer_list); + if (container) + child = ATK_OBJECT (container); + } + + if (expander_tv == tv_col) + { + AtkRelationSet *relation_set; + AtkObject *accessible_array[1]; + AtkRelation* relation; + AtkObject *parent_node; + + relation_set = atk_object_ref_relation_set (ATK_OBJECT (child)); + + gtk_tree_path_up (path); + if (gtk_tree_path_get_depth (path) == 0) + parent_node = obj; + else + { + gint parent_index; + gint n_columns; + + n_columns = get_n_actual_columns (tree_view); + parent_index = get_index (tree_view, path, i % n_columns); + parent_node = atk_object_ref_accessible_child (obj, parent_index); + } + accessible_array[0] = parent_node; + relation = atk_relation_new (accessible_array, 1, + ATK_RELATION_NODE_CHILD_OF); + atk_relation_set_add (relation_set, relation); + g_object_unref (relation); + g_object_unref (relation_set); + } + gtk_tree_path_free (path); + + /* + * We do not increase the reference count here; when g_object_unref() is + * called for the cell then cell_destroyed() is called and + * this removes the cell from the cache. + */ + return child; +} + +static AtkStateSet* +gail_tree_view_ref_state_set (AtkObject *obj) +{ + AtkStateSet *state_set; + GtkWidget *widget; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (obj); + widget = GTK_ACCESSIBLE (obj)->widget; + + if (widget != NULL) + atk_state_set_add_state (state_set, ATK_STATE_MANAGES_DESCENDANTS); + + return state_set; +} + +/* atkcomponent.h */ + +static void +atk_component_interface_init (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + iface->ref_accessible_at_point = gail_tree_view_ref_accessible_at_point; +} + +static AtkObject* +gail_tree_view_ref_accessible_at_point (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreePath *path; + GtkTreeViewColumn *tv_column; + gint x_pos, y_pos; + gboolean ret_val; + + widget = GTK_ACCESSIBLE (component)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + tree_view = GTK_TREE_VIEW (widget); + + atk_component_get_extents (component, &x_pos, &y_pos, NULL, NULL, coord_type); + ret_val = gtk_tree_view_get_path_at_pos (tree_view, + x - x_pos, y - y_pos, + &path, &tv_column, NULL, NULL); + if (ret_val) + { + gint index, column; + + column = get_column_number (tree_view, tv_column, FALSE); + index = get_index (tree_view, path, column); + gtk_tree_path_free (path); + + return gail_tree_view_ref_child (ATK_OBJECT (component), index); + } + else + { + g_warning ("gail_tree_view_ref_accessible_at_point: gtk_tree_view_get_path_at_pos () failed\n"); + } + return NULL; +} + +/* atktable.h */ + +static void +atk_table_interface_init (AtkTableIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->ref_at = gail_tree_view_table_ref_at; + iface->get_n_rows = gail_tree_view_get_n_rows; + iface->get_n_columns = gail_tree_view_get_n_columns; + iface->get_index_at = gail_tree_view_get_index_at; + iface->get_column_at_index = gail_tree_view_get_column_at_index; + iface->get_row_at_index = gail_tree_view_get_row_at_index; + iface->is_row_selected = gail_tree_view_is_row_selected; + iface->is_selected = gail_tree_view_is_selected; + iface->get_selected_rows = gail_tree_view_get_selected_rows; + iface->add_row_selection = gail_tree_view_add_row_selection; + iface->remove_row_selection = gail_tree_view_remove_row_selection; + iface->get_column_extent_at = NULL; + iface->get_row_extent_at = NULL; + iface->get_row_header = gail_tree_view_get_row_header; + iface->set_row_header = gail_tree_view_set_row_header; + iface->get_column_header = gail_tree_view_get_column_header; + iface->set_column_header = gail_tree_view_set_column_header; + iface->get_caption = gail_tree_view_get_caption; + iface->set_caption = gail_tree_view_set_caption; + iface->get_summary = gail_tree_view_get_summary; + iface->set_summary = gail_tree_view_set_summary; + iface->get_row_description = gail_tree_view_get_row_description; + iface->set_row_description = gail_tree_view_set_row_description; + iface->get_column_description = gail_tree_view_get_column_description; + iface->set_column_description = gail_tree_view_set_column_description; +} + +static gint +gail_tree_view_get_index_at (AtkTable *table, + gint row, + gint column) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + gint actual_column; + gint n_cols, n_rows; + GtkTreeIter iter; + GtkTreePath *path; + gint index; + + n_cols = atk_table_get_n_columns (table); + n_rows = atk_table_get_n_rows (table); + + if (row >= n_rows || + column >= n_cols) + return -1; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + tree_view = GTK_TREE_VIEW (widget); + actual_column = get_actual_column_number (tree_view, column); + + set_iter_nth_row (tree_view, &iter, row); + path = gtk_tree_model_get_path (gtk_tree_view_get_model (tree_view), &iter); + + index = get_index (tree_view, path, actual_column); + gtk_tree_path_free (path); + + return index; +} + +static gint +gail_tree_view_get_column_at_index (AtkTable *table, + gint index) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + gint n_columns; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + tree_view = GTK_TREE_VIEW (widget); + n_columns = get_n_actual_columns (tree_view); + + if (n_columns == 0) + return 0; + index = index % n_columns; + + return get_visible_column_number (tree_view, index); +} + +static gint +gail_tree_view_get_row_at_index (AtkTable *table, + gint index) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreePath *path; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return -1; + + tree_view = GTK_TREE_VIEW (widget); + if (get_path_column_from_index (tree_view, index, &path, NULL)) + { + gint row = get_row_from_tree_path (tree_view, path); + gtk_tree_path_free (path); + return row; + } + else + return -1; +} + +static AtkObject* +gail_tree_view_table_ref_at (AtkTable *table, + gint row, + gint column) +{ + gint index; + + index = gail_tree_view_get_index_at (table, row, column); + if (index == -1) + return NULL; + + return gail_tree_view_ref_child (ATK_OBJECT (table), index); +} + +static gint +gail_tree_view_get_n_rows (AtkTable *table) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + gint n_rows; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + + if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY) + /* + * If working with a LIST store, then this is a faster way + * to get the number of rows. + */ + n_rows = gtk_tree_model_iter_n_children (tree_model, NULL); + else + { + GtkTreePath *root_tree; + + n_rows = 0; + root_tree = gtk_tree_path_new_root (); + iterate_thru_children (tree_view, tree_model, + root_tree, NULL, &n_rows, 0); + g_free (root_tree); + } + + return n_rows; +} + +/* + * The function get_n_actual_columns returns the number of columns in the + * GtkTreeView. i.e. it include both visible and non-visible columns. + */ +static gint +get_n_actual_columns (GtkTreeView *tree_view) +{ + GList *columns; + gint n_cols; + + columns = gtk_tree_view_get_columns (tree_view); + n_cols = g_list_length (columns); + g_list_free (columns); + return n_cols; +} + +static gint +gail_tree_view_get_n_columns (AtkTable *table) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + gint n_cols = 0; + gint i = 0; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + tree_view = GTK_TREE_VIEW (widget); + tv_col = gtk_tree_view_get_column (tree_view, i); + + while (tv_col != NULL) + { + if (gtk_tree_view_column_get_visible (tv_col)) + n_cols++; + + i++; + tv_col = gtk_tree_view_get_column (tree_view, i); + } + + return n_cols; +} + +static gboolean +gail_tree_view_is_row_selected (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeSelection *selection; + GtkTreeIter iter; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (row < 0) + return FALSE; + + tree_view = GTK_TREE_VIEW (widget); + + selection = gtk_tree_view_get_selection (tree_view); + + set_iter_nth_row (tree_view, &iter, row); + + return gtk_tree_selection_iter_is_selected (selection, &iter); +} + +static gboolean +gail_tree_view_is_selected (AtkTable *table, + gint row, + gint column) +{ + return gail_tree_view_is_row_selected (table, row); +} + +static gint +gail_tree_view_get_selected_rows (AtkTable *table, + gint **rows_selected) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GtkTreeIter iter; + GtkTreeSelection *selection; + GtkTreePath *tree_path; + gint ret_val = 0; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return 0; + + tree_view = GTK_TREE_VIEW (widget); + + selection = gtk_tree_view_get_selection (tree_view); + + switch (selection->type) + { + case GTK_SELECTION_SINGLE: + case GTK_SELECTION_BROWSE: + if (gtk_tree_selection_get_selected (selection, &tree_model, &iter)) + { + gint row; + + if (rows_selected) + { + *rows_selected = (gint *)g_malloc (sizeof(gint)); + tree_path = gtk_tree_model_get_path (tree_model, &iter); + row = get_row_from_tree_path (tree_view, tree_path); + gtk_tree_path_free (tree_path); + + /* shouldn't ever happen */ + g_return_val_if_fail (row != -1, 0); + + *rows_selected[0] = row; + } + ret_val = 1; + } + break; + case GTK_SELECTION_MULTIPLE: + { + GPtrArray *array = g_ptr_array_new(); + + gtk_tree_selection_selected_foreach (selection, + get_selected_rows, + array); + ret_val = array->len; + + if (rows_selected && ret_val) + { + gint i; + *rows_selected = (gint *) g_malloc (ret_val * sizeof (gint)); + + for (i = 0; i < ret_val; i++) + { + gint row; + + tree_path = (GtkTreePath *) g_ptr_array_index (array, i); + row = get_row_from_tree_path (tree_view, tree_path); + gtk_tree_path_free (tree_path); + (*rows_selected)[i] = row; + } + } + g_ptr_array_free (array, FALSE); + } + break; + case GTK_SELECTION_NONE: + break; + } + return ret_val; +} + +static gboolean +gail_tree_view_add_row_selection (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GtkTreeSelection *selection; + GtkTreePath *tree_path; + GtkTreeIter iter_to_row; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + if (!gail_tree_view_is_row_selected (table, row)) + { + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + selection = gtk_tree_view_get_selection (tree_view); + + if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY) + { + tree_path = gtk_tree_path_new (); + gtk_tree_path_append_index (tree_path, row); + gtk_tree_selection_select_path (selection,tree_path); + gtk_tree_path_free (tree_path); + } + else + { + set_iter_nth_row (tree_view, &iter_to_row, row); + if (&iter_to_row != NULL) + gtk_tree_selection_select_iter (selection, &iter_to_row); + else + return FALSE; + } + } + + return gail_tree_view_is_row_selected (table, row); +} + +static gboolean +gail_tree_view_remove_row_selection (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeSelection *selection; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + tree_view = GTK_TREE_VIEW (widget); + + selection = gtk_tree_view_get_selection (tree_view); + + if (gail_tree_view_is_row_selected (table, row)) + { + gtk_tree_selection_unselect_all (selection); + return TRUE; + } + else return FALSE; +} + +static AtkObject* +gail_tree_view_get_row_header (AtkTable *table, + gint row) +{ + GailTreeViewRowInfo *row_info; + + row_info = get_row_info (table, row); + if (row_info) + return row_info->header; + else + return NULL; +} + +static void +gail_tree_view_set_row_header (AtkTable *table, + gint row, + AtkObject *header) +{ + set_row_data (table, row, header, NULL, TRUE); +} + +static AtkObject* +gail_tree_view_get_column_header (AtkTable *table, + gint in_col) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + tree_view = GTK_TREE_VIEW (widget); + tv_col = get_column (tree_view, in_col); + return get_header_from_column (tv_col); +} + +static void +gail_tree_view_set_column_header (AtkTable *table, + gint in_col, + AtkObject *header) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + AtkObject *rc; + AtkPropertyValues values = { NULL }; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + tree_view = GTK_TREE_VIEW (widget); + tv_col = get_column (tree_view, in_col); + if (tv_col == NULL) + return; + + rc = g_object_get_qdata (G_OBJECT (tv_col), + quark_column_header_object); + if (rc) + g_object_unref (rc); + + g_object_set_qdata (G_OBJECT (tv_col), + quark_column_header_object, + header); + if (header) + g_object_ref (header); + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, in_col); + + values.property_name = "accessible-table-column-header"; + g_signal_emit_by_name (table, + "property_change::accessible-table-column-header", + &values, NULL); +} + +static AtkObject* +gail_tree_view_get_caption (AtkTable *table) +{ + GailTreeView* obj = GAIL_TREE_VIEW (table); + + return obj->caption; +} + +static void +gail_tree_view_set_caption (AtkTable *table, + AtkObject *caption) +{ + GailTreeView* obj = GAIL_TREE_VIEW (table); + AtkPropertyValues values = { NULL }; + AtkObject *old_caption; + + old_caption = obj->caption; + obj->caption = caption; + if (obj->caption) + g_object_ref (obj->caption); + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, old_caption); + g_value_init (&values.new_value, G_TYPE_POINTER); + g_value_set_pointer (&values.new_value, obj->caption); + + values.property_name = "accessible-table-caption-object"; + g_signal_emit_by_name (table, + "property_change::accessible-table-caption-object", + &values, NULL); + if (old_caption) + g_object_unref (old_caption); +} + +static G_CONST_RETURN gchar* +gail_tree_view_get_column_description (AtkTable *table, + gint in_col) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + gchar *rc; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + tree_view = GTK_TREE_VIEW (widget); + tv_col = get_column (tree_view, in_col); + if (tv_col == NULL) + return NULL; + + rc = g_object_get_qdata (G_OBJECT (tv_col), + quark_column_desc_object); + + if (rc != NULL) + return rc; + else + { + gchar *title_text; + + g_object_get (tv_col, "title", &title_text, NULL); + return title_text; + } +} + +static void +gail_tree_view_set_column_description (AtkTable *table, + gint in_col, + const gchar *description) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + AtkPropertyValues values = { NULL }; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + tree_view = GTK_TREE_VIEW (widget); + tv_col = get_column (tree_view, in_col); + if (tv_col == NULL) + return; + + g_object_set_qdata (G_OBJECT (tv_col), + quark_column_desc_object, + g_strdup (description)); + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, in_col); + + values.property_name = "accessible-table-column-description"; + g_signal_emit_by_name (table, + "property_change::accessible-table-column-description", + &values, NULL); +} + +static G_CONST_RETURN gchar* +gail_tree_view_get_row_description (AtkTable *table, + gint row) +{ + GailTreeViewRowInfo *row_info; + + row_info = get_row_info (table, row); + if (row_info) + return row_info->description; + else + return NULL; +} + +static void +gail_tree_view_set_row_description (AtkTable *table, + gint row, + const gchar *description) +{ + set_row_data (table, row, NULL, description, FALSE); +} + +static AtkObject* +gail_tree_view_get_summary (AtkTable *table) +{ + GailTreeView* obj = GAIL_TREE_VIEW (table); + + return obj->summary; +} + +static void +gail_tree_view_set_summary (AtkTable *table, + AtkObject *accessible) +{ + GailTreeView* obj = GAIL_TREE_VIEW (table); + AtkPropertyValues values = { NULL }; + AtkObject *old_summary; + + old_summary = obj->summary; + obj->summary = accessible; + if (obj->summary) + g_object_ref (obj->summary); + g_value_init (&values.old_value, G_TYPE_POINTER); + g_value_set_pointer (&values.old_value, old_summary); + g_value_init (&values.new_value, G_TYPE_POINTER); + g_value_set_pointer (&values.new_value, obj->summary); + + values.property_name = "accessible-table-summary"; + g_signal_emit_by_name (table, + "property_change::accessible-table-ummary", + &values, NULL); + if (old_summary) + g_object_unref (old_summary); +} + +static void +set_row_data (AtkTable *table, + gint row, + AtkObject *header, + const gchar *description, + gboolean is_header) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GailTreeView* obj = GAIL_TREE_VIEW (table); + GailTreeViewRowInfo* row_info; + GtkTreePath *path; + GtkTreeIter iter; + GArray *array; + gboolean found = FALSE; + gint i; + AtkPropertyValues values = { NULL }; + gchar *signal_name; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + + set_iter_nth_row (tree_view, &iter, row); + path = gtk_tree_model_get_path (tree_model, &iter); + + if (obj->row_data == NULL) + obj->row_data = g_array_sized_new (FALSE, TRUE, + sizeof(GailTreeViewRowInfo *), 0); + + array = obj->row_data; + + for (i = 0; i < array->len; i++) + { + GtkTreePath *row_path; + + row_info = g_array_index (array, GailTreeViewRowInfo*, i); + row_path = gtk_tree_row_reference_get_path (row_info->row_ref); + + if (row_path != NULL) + { + if (path && gtk_tree_path_compare (row_path, path) == 0) + found = TRUE; + + gtk_tree_path_free (row_path); + + if (found) + { + if (is_header) + { + if (row_info->header) + g_object_unref (row_info->header); + row_info->header = header; + if (row_info->header) + g_object_ref (row_info->header); + } + else + { + g_free (row_info->description); + row_info->description = g_strdup (description); + } + break; + } + } + } + + if (!found) + { + /* if not found */ + row_info = g_malloc (sizeof(GailTreeViewRowInfo)); + row_info->row_ref = gtk_tree_row_reference_new (tree_model, path); + if (is_header) + { + row_info->header = header; + if (row_info->header) + g_object_ref (row_info->header); + row_info->description = NULL; + } + else + { + row_info->header = NULL; + row_info->description = g_strdup (description); + } + g_array_append_val (array, row_info); + } + g_value_init (&values.new_value, G_TYPE_INT); + g_value_set_int (&values.new_value, row); + + if (is_header) + { + values.property_name = "accessible-table-row-header"; + signal_name = "property_change::accessible-table-row-header"; + } + else + { + values.property_name = "accessible-table-row-description"; + signal_name = "property-change::accessible-table-row-description"; + } + g_signal_emit_by_name (table, + signal_name, + &values, NULL); + + gtk_tree_path_free (path); +} + + +static GailTreeViewRowInfo* +get_row_info (AtkTable *table, + gint row) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GailTreeView* obj = GAIL_TREE_VIEW (table); + GtkTreePath *path; + GtkTreeIter iter; + GArray *array; + GailTreeViewRowInfo *rc = NULL; + + widget = GTK_ACCESSIBLE (table)->widget; + if (widget == NULL) + /* State is defunct */ + return NULL; + + tree_view = GTK_TREE_VIEW (widget); + tree_model = gtk_tree_view_get_model (tree_view); + + set_iter_nth_row (tree_view, &iter, row); + path = gtk_tree_model_get_path (tree_model, &iter); + array = obj->row_data; + + if (array != NULL) + { + GailTreeViewRowInfo *row_info; + GtkTreePath *row_path; + gint i; + + for (i = 0; i < array->len; i++) + { + row_info = g_array_index (array, GailTreeViewRowInfo*, i); + row_path = gtk_tree_row_reference_get_path (row_info->row_ref); + if (row_path != NULL) + { + if (path && gtk_tree_path_compare (row_path, path) == 0) + rc = row_info; + + gtk_tree_path_free (row_path); + + if (rc != NULL) + break; + } + } + } + + gtk_tree_path_free (path); + return rc; +} +/* atkselection.h */ + +static void atk_selection_interface_init (AtkSelectionIface *iface) +{ + g_return_if_fail (iface != NULL); + iface->add_selection = gail_tree_view_add_selection; + iface->clear_selection = gail_tree_view_clear_selection; + iface->ref_selection = gail_tree_view_ref_selection; + iface->get_selection_count = gail_tree_view_get_selection_count; + iface->is_child_selected = gail_tree_view_is_child_selected; +} + +static gboolean +gail_tree_view_add_selection (AtkSelection *selection, + gint i) +{ + AtkTable *table; + gint n_columns; + gint row; + + table = ATK_TABLE (selection); + n_columns = gail_tree_view_get_n_columns (table); + if (n_columns != 1) + return FALSE; + + row = gail_tree_view_get_row_at_index (table, i); + return gail_tree_view_add_row_selection (table, row); +} + +static gboolean +gail_tree_view_clear_selection (AtkSelection *selection) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeSelection *tree_selection; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + tree_view = GTK_TREE_VIEW (widget); + + tree_selection = gtk_tree_view_get_selection (tree_view); + gtk_tree_selection_unselect_all (tree_selection); + + return TRUE; +} + +static AtkObject* +gail_tree_view_ref_selection (AtkSelection *selection, + gint i) +{ + AtkTable *table; + gint row; + gint n_selected; + gint n_columns; + gint *selected; + + table = ATK_TABLE (selection); + n_columns = gail_tree_view_get_n_columns (table); + n_selected = gail_tree_view_get_selected_rows (table, &selected); + if (i >= n_columns * n_selected) + return NULL; + + row = selected[i / n_columns]; + g_free (selected); + + return gail_tree_view_table_ref_at (table, row, i % n_columns); +} + +static gint +gail_tree_view_get_selection_count (AtkSelection *selection) +{ + AtkTable *table; + gint n_selected; + + table = ATK_TABLE (selection); + n_selected = gail_tree_view_get_selected_rows (table, NULL); + if (n_selected > 0) + n_selected *= gail_tree_view_get_n_columns (table); + return n_selected; +} + +static gboolean +gail_tree_view_is_child_selected (AtkSelection *selection, + gint i) +{ + GtkWidget *widget; + gint row; + + widget = GTK_ACCESSIBLE (selection)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + row = atk_table_get_row_at_index (ATK_TABLE (selection), i); + + return gail_tree_view_is_row_selected (ATK_TABLE (selection), row); +} + + +static void gail_cell_parent_interface_init (GailCellParentIface *iface) +{ + g_return_if_fail (iface); + + iface->get_cell_extents = gail_tree_view_get_cell_extents; + iface->get_cell_area = gail_tree_view_get_cell_area; + iface->grab_focus = gail_tree_view_grab_cell_focus; +} + +static void +gail_tree_view_get_cell_extents (GailCellParent *parent, + GailCell *cell, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GdkWindow *bin_window; + GdkRectangle cell_rect; + gint w_x, w_y; + + widget = GTK_ACCESSIBLE (parent)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + tree_view = GTK_TREE_VIEW (widget); + gail_tree_view_get_cell_area (parent, cell, &cell_rect); + bin_window = gtk_tree_view_get_bin_window (tree_view); + gdk_window_get_origin (bin_window, &w_x, &w_y); + + if (coord_type == ATK_XY_WINDOW) + { + GdkWindow *window; + gint x_toplevel, y_toplevel; + + window = gdk_window_get_toplevel (bin_window); + gdk_window_get_origin (window, &x_toplevel, &y_toplevel); + + w_x -= x_toplevel; + w_y -= y_toplevel; + } + + *width = cell_rect.width; + *height = cell_rect.height; + if (is_cell_showing (tree_view, &cell_rect)) + { + *x = cell_rect.x + w_x; + *y = cell_rect.y + w_y; + } + else + { + *x = G_MININT; + *y = G_MININT; + } +} + +#define EXTRA_EXPANDER_PADDING 4 + +static void +gail_tree_view_get_cell_area (GailCellParent *parent, + GailCell *cell, + GdkRectangle *cell_rect) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + GtkTreePath *path; + AtkObject *parent_cell; + GailTreeViewCellInfo *cell_info; + GailCell *top_cell; + + widget = GTK_ACCESSIBLE (parent)->widget; + if (widget == NULL) + /* State is defunct */ + return; + + tree_view = GTK_TREE_VIEW (widget); + parent_cell = atk_object_get_parent (ATK_OBJECT (cell)); + if (parent_cell != ATK_OBJECT (parent)) + { + /* + * GailCell is in a GailContainerCell + */ + top_cell = GAIL_CELL (parent_cell); + } + else + { + top_cell = cell; + } + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), top_cell, NULL, TRUE); + gail_return_if_fail (cell_info); + gail_return_if_fail (cell_info->cell_col_ref); + gail_return_if_fail (cell_info->cell_row_ref); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + tv_col = cell_info->cell_col_ref; + if (path && cell_info->in_use) + { + GtkTreeViewColumn *expander_column; + gint focus_line_width; + + gtk_tree_view_get_cell_area (tree_view, path, tv_col, cell_rect); + expander_column = gtk_tree_view_get_expander_column (tree_view); + if (expander_column == tv_col) + { + gint expander_size; + + gtk_widget_style_get (widget, + "expander_size", &expander_size, + NULL); + + cell_rect->x += expander_size + EXTRA_EXPANDER_PADDING; + cell_rect->width -= expander_size + EXTRA_EXPANDER_PADDING; + } + gtk_widget_style_get (widget, + "focus-line-width", &focus_line_width, + NULL); + + cell_rect->x += focus_line_width; + cell_rect->width -= 2 * focus_line_width; + + gtk_tree_path_free (path); + + /* + * A column has more than one renderer so we find the position and width + * of each. + */ + if (top_cell != cell) + { + gint cell_index; + gboolean found; + gint cell_start; + gint cell_width; + GList *renderers; + GtkCellRenderer *renderer; + + cell_index = atk_object_get_index_in_parent (ATK_OBJECT (cell)); + renderers = gtk_tree_view_column_get_cell_renderers (tv_col); + renderer = g_list_nth_data (renderers, cell_index); + + found = gtk_tree_view_column_cell_get_position (tv_col, renderer, &cell_start, &cell_width); + if (found) + { + cell_rect->x += cell_start; + cell_rect->width = cell_width; + } + g_list_free (renderers); + } + + } +} + +static gboolean +gail_tree_view_grab_cell_focus (GailCellParent *parent, + GailCell *cell) +{ + GtkWidget *widget; + GtkTreeView *tree_view; + GtkTreeViewColumn *tv_col; + GtkTreePath *path; + AtkObject *parent_cell; + AtkObject *cell_object; + GailTreeViewCellInfo *cell_info; + GtkCellRenderer *renderer = NULL; + GtkWidget *toplevel; + gint index; + + widget = GTK_ACCESSIBLE (parent)->widget; + if (widget == NULL) + /* State is defunct */ + return FALSE; + + tree_view = GTK_TREE_VIEW (widget); + + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_val_if_fail (cell_info, FALSE); + gail_return_val_if_fail (cell_info->cell_col_ref, FALSE); + gail_return_val_if_fail (cell_info->cell_row_ref, FALSE); + cell_object = ATK_OBJECT (cell); + parent_cell = atk_object_get_parent (cell_object); + tv_col = cell_info->cell_col_ref; + if (parent_cell != ATK_OBJECT (parent)) + { + /* + * GailCell is in a GailContainerCell. + * The GtkTreeViewColumn has multiple renderers; + * find the corresponding one. + */ + GList *renderers; + + renderers = gtk_tree_view_column_get_cell_renderers (tv_col); + if (cell_info->in_use) { + index = atk_object_get_index_in_parent (cell_object); + renderer = g_list_nth_data (renderers, index); + } + g_list_free (renderers); + } + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + if (path && cell_info->in_use) + { + if (renderer) + gtk_tree_view_set_cursor_on_cell (tree_view, path, tv_col, renderer, FALSE); + else + gtk_tree_view_set_cursor (tree_view, path, tv_col, FALSE); + + gtk_tree_path_free (path); + gtk_widget_grab_focus (widget); + toplevel = gtk_widget_get_toplevel (widget); + if (GTK_WIDGET_TOPLEVEL (toplevel)) + { +#ifdef GDK_WINDOWING_X11 + gtk_window_present_with_time (GTK_WINDOW (toplevel), gdk_x11_get_server_time (widget->window)); +#else + gtk_window_present (GTK_WINDOW (toplevel)); +#endif + } + + return TRUE; + } + else + return FALSE; +} + +/* signal handling */ + +static gboolean +gail_tree_view_expand_row_gtk (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path) +{ + AtkObject *atk_obj; + GailTreeView *gailview; + + g_assert (GTK_IS_TREE_VIEW (tree_view)); + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + + g_assert (GAIL_IS_TREE_VIEW (atk_obj)); + + gailview = GAIL_TREE_VIEW (atk_obj); + + /* + * The visible rectangle has not been updated when this signal is emitted + * so we process the signal when the GTK processing is completed + */ + /* this seems wrong since it overwrites any other pending expand handlers... */ + gailview->idle_expand_path = gtk_tree_path_copy (path); + if (gailview->idle_expand_id) g_source_remove (gailview->idle_expand_id); + gailview->idle_expand_id = g_idle_add (idle_expand_row, gailview); + return FALSE; +} + +static gint +idle_expand_row (gpointer data) +{ + GailTreeView *gailview = data; + GtkTreePath *path; + GtkTreeView *tree_view; + GtkTreeIter iter; + GtkTreeModel *tree_model; + gint n_inserted, row; + + GDK_THREADS_ENTER (); + + path = gailview->idle_expand_path; + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (gailview)->widget); + + g_assert (GTK_IS_TREE_VIEW (tree_view)); + + tree_model = gtk_tree_view_get_model(tree_view); + + g_assert (GTK_IS_TREE_MODEL (tree_model)); + + if (!path || !gtk_tree_model_get_iter (tree_model, &iter, path)) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + /* + * Update visibility of cells below expansion row + */ + traverse_cells (gailview, path, FALSE, FALSE); + /* + * Figure out number of visible children, the following test + * should not fail + */ + if (gtk_tree_model_iter_has_child (tree_model, &iter)) + { + GtkTreePath *path_copy; + + /* + * By passing path into this function, we find the number of + * visible children of path. + */ + path_copy = gtk_tree_path_copy (path); + gtk_tree_path_append_index(path_copy, 0); + + n_inserted = 0; + iterate_thru_children (tree_view, tree_model, + path_copy, NULL, &n_inserted, 0); + gtk_tree_path_free (path_copy); + } + else + { + /* We can get here if the row expanded callback deleted the row */ + GDK_THREADS_LEAVE (); + return FALSE; + } + + /* Set expand state */ + set_expand_state (tree_view, tree_model, gailview, path, TRUE); + + row = get_row_from_tree_path (tree_view, path); + + /* shouldn't ever happen */ + if (row == -1) + g_assert_not_reached (); + + /* Must add 1 because the "added rows" are below the row being expanded */ + row += 1; + + g_signal_emit_by_name (gailview, "row_inserted", row, n_inserted); + + gailview->idle_expand_path = NULL; + + gtk_tree_path_free (path); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gboolean +gail_tree_view_collapse_row_gtk (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModel *tree_model; + AtkObject *atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + gint row; + + tree_model = gtk_tree_view_get_model (tree_view); + + clean_rows (gailview); + + /* + * Update visibility of cells below collapsed row + */ + traverse_cells (gailview, path, FALSE, FALSE); + /* Set collapse state */ + set_expand_state (tree_view, tree_model, gailview, path, FALSE); + + gail_return_val_if_fail (gailview->n_children_deleted, FALSE); + row = get_row_from_tree_path (tree_view, path); + gail_return_val_if_fail (row != -1, FALSE); + g_signal_emit_by_name (atk_obj, "row_deleted", row, + gailview->n_children_deleted); + gailview->n_children_deleted = 0; + return FALSE; +} + +static void +gail_tree_view_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation) +{ + AtkObject *atk_obj = gtk_widget_get_accessible (widget); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + + /* + * If the size allocation changes, the visibility of cells may change so + * update the cells visibility. + */ + traverse_cells (gailview, NULL, FALSE, FALSE); +} + +static void +gail_tree_view_set_scroll_adjustments (GtkWidget *widget, + GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + AtkObject *atk_obj = gtk_widget_get_accessible (widget); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + GtkAdjustment *adj; + + g_object_get (widget, hadjustment, &adj, NULL); + if (gailview->old_hadj != adj) + { + g_signal_handlers_disconnect_by_func (gailview->old_hadj, + (gpointer) adjustment_changed, + widget); + gailview->old_hadj = adj; + g_object_add_weak_pointer (G_OBJECT (gailview->old_hadj), (gpointer *)&gailview->old_hadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + widget); + } + g_object_get (widget, vadjustment, &adj, NULL); + if (gailview->old_vadj != adj) + { + g_signal_handlers_disconnect_by_func (gailview->old_vadj, + (gpointer) adjustment_changed, + widget); + gailview->old_vadj = adj; + g_object_add_weak_pointer (G_OBJECT (gailview->old_vadj), (gpointer *)&gailview->old_vadj); + g_signal_connect (adj, + "value_changed", + G_CALLBACK (adjustment_changed), + widget); + } +} + +static void +gail_tree_view_changed_gtk (GtkTreeSelection *selection, + gpointer data) +{ + GailTreeView *gailview; + GtkTreeView *tree_view; + GtkWidget *widget; + GList *cell_list; + GList *l; + GailTreeViewCellInfo *info; + GtkTreeSelection *tree_selection; + GtkTreePath *path; + + gailview = GAIL_TREE_VIEW (data); + cell_list = gailview->cell_data; + widget = GTK_ACCESSIBLE (gailview)->widget; + if (widget == NULL) + /* + * destroy signal emitted for widget + */ + return; + tree_view = GTK_TREE_VIEW (widget); + + tree_selection = gtk_tree_view_get_selection (tree_view); + + for (l = cell_list; l; l = l->next) + { + info = (GailTreeViewCellInfo *) (l->data); + + if (info->in_use) + { + gail_cell_remove_state (info->cell, ATK_STATE_SELECTED, TRUE); + + path = gtk_tree_row_reference_get_path (info->cell_row_ref); + if (path && gtk_tree_selection_path_is_selected (tree_selection, path)) + gail_cell_add_state (info->cell, ATK_STATE_SELECTED, TRUE); + gtk_tree_path_free (path); + } + } + if (GTK_WIDGET_REALIZED (widget)) + g_signal_emit_by_name (gailview, "selection_changed"); +} + +static void +columns_changed (GtkTreeView *tree_view) +{ + AtkObject *atk_obj = gtk_widget_get_accessible (GTK_WIDGET(tree_view)); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + GList *tv_cols, *tmp_list; + gboolean column_found; + gboolean move_found = FALSE; + gboolean stale_set = FALSE; + gint column_count = 0; + gint i; + + /* + * This function must determine if the change is an add, delete or + * a move based upon its cache of TreeViewColumns in + * gailview->col_data + */ + tv_cols = gtk_tree_view_get_columns (tree_view); + + /* check for adds or moves */ + for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next) + { + column_found = FALSE; + + for (i = 0; i < gailview->col_data->len; i++) + { + + if ((GtkTreeViewColumn *)tmp_list->data == + (GtkTreeViewColumn *)g_array_index (gailview->col_data, + GtkTreeViewColumn *, i)) + { + column_found = TRUE; + + /* If the column isn't in the same position, a move happened */ + if (!move_found && i != column_count) + { + if (!stale_set) + { + /* Set all rows to ATK_STATE_STALE */ + traverse_cells (gailview, NULL, TRUE, FALSE); + stale_set = TRUE; + } + + /* Just emit one column reordered signal when a move happens */ + g_signal_emit_by_name (atk_obj, "column_reordered"); + move_found = TRUE; + } + + break; + } + } + + /* + * If column_found is FALSE, then an insert happened for column + * number column_count + */ + if (!column_found) + { + gint n_cols, n_rows, row; + + if (!stale_set) + { + /* Set all rows to ATK_STATE_STALE */ + traverse_cells (gailview, NULL, TRUE, FALSE); + stale_set = TRUE; + } + + /* Generate column-inserted signal */ + g_signal_emit_by_name (atk_obj, "column_inserted", column_count, 1); + + /* Generate children-changed signals */ + n_rows = get_row_count (gtk_tree_view_get_model (tree_view)); + n_cols = get_n_actual_columns (tree_view); + for (row = 0; row < n_rows; row++) + { + /* + * Pass NULL as the child object, i.e. 4th argument. + */ + g_signal_emit_by_name (atk_obj, "children_changed::add", + ((row * n_cols) + column_count), NULL, NULL); + } + } + + column_count++; + } + + /* check for deletes */ + for (i = 0; i < gailview->col_data->len; i++) + { + column_found = FALSE; + + for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next) + { + if ((GtkTreeViewColumn *)tmp_list->data == + (GtkTreeViewColumn *)g_array_index (gailview->col_data, + GtkTreeViewColumn *, i)) + { + column_found = TRUE; + break; + } + } + + /* + * If column_found is FALSE, then a delete happened for column + * number i + */ + if (!column_found) + { + gint n_rows, n_cols, row; + + clean_cols (gailview, + (GtkTreeViewColumn *)g_array_index (gailview->col_data, + GtkTreeViewColumn *, i)); + + if (!stale_set) + { + /* Set all rows to ATK_STATE_STALE */ + traverse_cells (gailview, NULL, TRUE, FALSE); + stale_set = TRUE; + } + + /* Generate column-deleted signal */ + g_signal_emit_by_name (atk_obj, "column_deleted", i, 1); + + /* Generate children-changed signals */ + n_rows = get_row_count (gtk_tree_view_get_model (tree_view)); + n_cols = get_n_actual_columns (tree_view); + for (row = 0; row < n_rows; row++) + { + /* + * Pass NULL as the child object, 4th argument. + */ + g_signal_emit_by_name (atk_obj, "children_changed::remove", + ((row * n_cols) + column_count), NULL, NULL); + } + } + } + + /* rebuild the array */ + + g_array_free (gailview->col_data, TRUE); + gailview->col_data = g_array_sized_new (FALSE, TRUE, + sizeof(GtkTreeViewColumn *), 0); + + for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next) + g_array_append_val (gailview->col_data, tmp_list->data); + g_list_free (tv_cols); +} + +static void +cursor_changed (GtkTreeView *tree_view) +{ + /* + * We notify the focus change in a idle handler so that the processing + * of the cursor change is completed when the focus handler is called. + * This will allow actions to be called in the focus handler + */ + g_idle_add (idle_cursor_changed, gtk_widget_get_accessible (GTK_WIDGET (tree_view))); +} + +static gint +idle_cursor_changed (gpointer data) +{ + GtkTreeView *tree_view; + GtkWidget *widget; + AtkObject *parent; + AtkObject *cell; + + GDK_THREADS_ENTER (); + + parent = ATK_OBJECT (data); + + widget = GTK_ACCESSIBLE (parent)->widget; + /* + * Widget has been deleted + */ + if (widget == NULL) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + tree_view = GTK_TREE_VIEW (widget); + + cell = gail_tree_view_ref_focus_cell (tree_view); + if (cell) + { + GailTreeView *gail_tree_view; + + gail_tree_view = GAIL_TREE_VIEW (parent); + + if (cell != gail_tree_view->focus_cell) + { + if (gail_tree_view->focus_cell) + { + gail_cell_remove_state (GAIL_CELL (gail_tree_view->focus_cell), ATK_STATE_ACTIVE, FALSE); + g_object_unref (gail_tree_view->focus_cell); + } + gail_tree_view->focus_cell = cell; + + if (GTK_WIDGET_HAS_FOCUS (widget)) + gail_cell_add_state (GAIL_CELL (cell), ATK_STATE_ACTIVE, FALSE); + g_signal_emit_by_name (parent, + "active-descendant-changed", + cell); + } + else + g_object_unref (cell); + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +model_row_changed (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW(user_data); + GailTreeView *gailview; + GtkTreePath *cell_path; + GList *l; + GailTreeViewCellInfo *cell_info; + + gailview = GAIL_TREE_VIEW (gtk_widget_get_accessible (GTK_WIDGET (tree_view))); + + /* Loop through our cached cells */ + /* Must loop through them all */ + for (l = gailview->cell_data; l; l = l->next) + { + cell_info = (GailTreeViewCellInfo *) l->data; + if (cell_info->in_use) + { + cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + + if (cell_path != NULL) + { + if (path && gtk_tree_path_compare (cell_path, path) == 0) + { + if (GAIL_IS_RENDERER_CELL (cell_info->cell)) + { + update_cell_value (GAIL_RENDERER_CELL (cell_info->cell), + gailview, TRUE); + } + } + gtk_tree_path_free (cell_path); + } + } + } + g_signal_emit_by_name (gailview, "visible-data-changed"); +} + +static void +column_visibility_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + if (strcmp (pspec->name, "visible") == 0) + { + /* + * A column has been made visible or invisible + * + * We update our cache of cells and emit model_changed signal + */ + GtkTreeView *tree_view = (GtkTreeView *)user_data; + GailTreeView *gailview; + GList *l; + GailTreeViewCellInfo *cell_info; + GtkTreeViewColumn *this_col = GTK_TREE_VIEW_COLUMN (object); + GtkTreeViewColumn *tv_col; + + gailview = GAIL_TREE_VIEW (gtk_widget_get_accessible (GTK_WIDGET (tree_view)) +); + g_signal_emit_by_name (gailview, "model_changed"); + + for (l = gailview->cell_data; l; l = l->next) + { + cell_info = (GailTreeViewCellInfo *) l->data; + if (cell_info->in_use) + { + tv_col = cell_info->cell_col_ref; + if (tv_col == this_col) + { + GtkTreePath *row_path; + row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + if (GAIL_IS_RENDERER_CELL (cell_info->cell)) + { + if (gtk_tree_view_column_get_visible (tv_col)) + set_cell_visibility (tree_view, + cell_info->cell, + tv_col, row_path, FALSE); + else + { + gail_cell_remove_state (cell_info->cell, + ATK_STATE_VISIBLE, TRUE); + gail_cell_remove_state (cell_info->cell, + ATK_STATE_SHOWING, TRUE); + } + } + gtk_tree_path_free (row_path); + } + } + } + } +} + +/* + * This is the signal handler for the "destroy" signal for a GtkTreeViewColumn + * + * We check whether we have stored column description or column header + * and if so we get rid of it. + */ +static void +column_destroy (GtkObject *obj) +{ + GtkTreeViewColumn *tv_col = GTK_TREE_VIEW_COLUMN (obj); + AtkObject *header; + gchar *desc; + + header = g_object_get_qdata (G_OBJECT (tv_col), + quark_column_header_object); + if (header) + g_object_unref (header); + desc = g_object_get_qdata (G_OBJECT (tv_col), + quark_column_desc_object); + g_free (desc); +} + +static void +model_row_inserted (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkTreeView *tree_view = (GtkTreeView *)user_data; + GtkTreePath *path_copy; + AtkObject *atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + gint row, n_inserted, child_row; + + if (gailview->idle_expand_id) + { + g_source_remove (gailview->idle_expand_id); + /* don't do this if the insertion precedes the idle path, since it will now be invalid */ + if (path && gailview->idle_expand_path && + (gtk_tree_path_compare (path, gailview->idle_expand_path) > 0)) + set_expand_state (tree_view, tree_model, gailview, gailview->idle_expand_path, FALSE); + if (gailview->idle_expand_path) + gtk_tree_path_free (gailview->idle_expand_path); + gailview->idle_expand_id = 0; + } + /* Check to see if row is visible */ + row = get_row_from_tree_path (tree_view, path); + + /* + * A row insert is not necessarily visible. For example, + * a row can be draged & dropped into another row, which + * causes an insert on the model that isn't visible in the + * view. Only generate a signal if the inserted row is + * visible. + */ + if (row != -1) + { + GtkTreeIter iter; + gint n_cols, col; + + gtk_tree_model_get_iter (tree_model, &iter, path); + + /* Figure out number of visible children. */ + if (gtk_tree_model_iter_has_child (tree_model, &iter)) + { + /* + * By passing path into this function, we find the number of + * visible children of path. + */ + n_inserted = 0; + iterate_thru_children (tree_view, tree_model, + path, NULL, &n_inserted, 0); + + /* Must add one to include the row that is being added */ + n_inserted++; + } + else + n_inserted = 1; + + /* Set rows below the inserted row to ATK_STATE_STALE */ + traverse_cells (gailview, path, TRUE, TRUE); + + /* Generate row-inserted signal */ + g_signal_emit_by_name (atk_obj, "row_inserted", row, n_inserted); + + /* Generate children-changed signals */ + n_cols = gail_tree_view_get_n_columns (ATK_TABLE (atk_obj)); + for (child_row = row; child_row < (row + n_inserted); child_row++) + { + for (col = 0; col < n_cols; col++) + { + /* + * Pass NULL as the child object, i.e. 4th argument + */ + g_signal_emit_by_name (atk_obj, "children_changed::add", + ((row * n_cols) + col), NULL, NULL); + } + } + } + else + { + /* + * The row has been inserted inside another row. This can + * cause a row that previously couldn't be expanded to now + * be expandable. + */ + path_copy = gtk_tree_path_copy (path); + gtk_tree_path_up (path_copy); + set_expand_state (tree_view, tree_model, gailview, path_copy, TRUE); + gtk_tree_path_free (path_copy); + } +} + +static void +model_row_deleted (GtkTreeModel *tree_model, + GtkTreePath *path, + gpointer user_data) +{ + GtkTreeView *tree_view; + GtkTreePath *path_copy; + AtkObject *atk_obj; + GailTreeView *gailview; + gint row; + + tree_view = (GtkTreeView *)user_data; + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + gailview = GAIL_TREE_VIEW (atk_obj); + + if (gailview->idle_expand_id) + { + g_source_remove (gailview->idle_expand_id); + gtk_tree_path_free (gailview->idle_expand_path); + gailview->idle_expand_id = 0; + } + /* Check to see if row is visible */ + clean_rows (gailview); + + /* Set rows at or below the specified row to ATK_STATE_STALE */ + traverse_cells (gailview, path, TRUE, TRUE); + + /* + * If deleting a row with a depth > 1, then this may affect the + * expansion/contraction of its parent(s). Make sure this is + * handled. + */ + if (gtk_tree_path_get_depth (path) > 1) + { + path_copy = gtk_tree_path_copy (path); + gtk_tree_path_up (path_copy); + set_expand_state (tree_view, tree_model, gailview, path_copy, TRUE); + gtk_tree_path_free (path_copy); + } + row = get_row_from_tree_path (tree_view, path); + /* + * If the row which is deleted is not visible because it is a child of + * a collapsed row then row will be -1 + */ + if (row > 0) + g_signal_emit_by_name (atk_obj, "row_deleted", row, + gailview->n_children_deleted + 1); + gailview->n_children_deleted = 0; +} + +/* + * This function gets called when a row is deleted or when rows are + * removed from the view due to a collapse event. Note that the + * count is the number of visible *children* of the deleted row, + * so it does not include the row being deleted. + * + * As this function is called before the rows are removed we just note the + * number of rows and then deal with it when we get a notification that + * rows were deleted or collapsed. + */ +static void +destroy_count_func (GtkTreeView *tree_view, + GtkTreePath *path, + gint count, + gpointer user_data) +{ + AtkObject *atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + + gail_return_if_fail (gailview->n_children_deleted == 0); + gailview->n_children_deleted = count; +} + +static void +model_rows_reordered (GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeIter *iter, + gint *new_order, + gpointer user_data) +{ + GtkTreeView *tree_view = (GtkTreeView *)user_data; + AtkObject *atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj); + + if (gailview->idle_expand_id) + { + g_source_remove (gailview->idle_expand_id); + gtk_tree_path_free (gailview->idle_expand_path); + gailview->idle_expand_id = 0; + } + traverse_cells (gailview, NULL, TRUE, FALSE); + + g_signal_emit_by_name (atk_obj, "row_reordered"); +} + +static void +adjustment_changed (GtkAdjustment *adjustment, + GtkTreeView *tree_view) +{ + AtkObject *atk_obj; + GailTreeView* obj; + + /* + * The scrollbars have changed + */ + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view)); + obj = GAIL_TREE_VIEW (atk_obj); + + traverse_cells (obj, NULL, FALSE, FALSE); +} + +static void +set_cell_visibility (GtkTreeView *tree_view, + GailCell *cell, + GtkTreeViewColumn *tv_col, + GtkTreePath *tree_path, + gboolean emit_signal) +{ + GdkRectangle cell_rect; + + /* Get these three values in tree coords */ + if (GTK_WIDGET_REALIZED (GTK_WIDGET (tree_view))) + gtk_tree_view_get_cell_area (tree_view, tree_path, tv_col, &cell_rect); + else + cell_rect.height = 0; + + if (cell_rect.height > 0) + { + /* + * The height will be zero for a cell for which an antecedent is not + * expanded + */ + gail_cell_add_state (cell, ATK_STATE_VISIBLE, emit_signal); + if (is_cell_showing (tree_view, &cell_rect)) + gail_cell_add_state (cell, ATK_STATE_SHOWING, emit_signal); + else + gail_cell_remove_state (cell, ATK_STATE_SHOWING, emit_signal); + } + else + { + gail_cell_remove_state (cell, ATK_STATE_VISIBLE, emit_signal); + gail_cell_remove_state (cell, ATK_STATE_SHOWING, emit_signal); + } +} + +static gboolean +is_cell_showing (GtkTreeView *tree_view, + GdkRectangle *cell_rect) +{ + GdkRectangle rect, *visible_rect; + GdkRectangle rect1, *tree_cell_rect; + gboolean is_showing; + /* + * A cell is considered "SHOWING" if any part of the cell is in the visible + * area. Other ways we could do this is by a cell's midpoint or if the cell + * is fully in the visible range. Since we have the cell_rect x,y,width,height + * of the cell, any of these is easy to compute. + * + * It is assumed that cell's rectangle is in widget coordinates so we + * must transform to tree cordinates. + */ + visible_rect = ▭ + tree_cell_rect = &rect1; + tree_cell_rect->x = cell_rect->x; + tree_cell_rect->width = cell_rect->width; + tree_cell_rect->height = cell_rect->height; + + gtk_tree_view_get_visible_rect (tree_view, visible_rect); + gtk_tree_view_widget_to_tree_coords (tree_view, cell_rect->x, cell_rect->y, + NULL, &(rect1.y)); + + if (((tree_cell_rect->x + tree_cell_rect->width) < visible_rect->x) || + ((tree_cell_rect->y + tree_cell_rect->height) < (visible_rect->y)) || + (tree_cell_rect->x > (visible_rect->x + visible_rect->width)) || + (tree_cell_rect->y > (visible_rect->y + visible_rect->height))) + is_showing = FALSE; + else + is_showing = TRUE; + + return is_showing; +} + +/* Misc Public */ + +/* + * This function is called when a cell's flyweight is created in + * gail_tree_view_table_ref_at with emit_change_signal set to FALSE + * and in model_row_changed() on receipt of "row-changed" signal when + * emit_change_signal is set to TRUE + */ +static gboolean +update_cell_value (GailRendererCell *renderer_cell, + GailTreeView *gailview, + gboolean emit_change_signal) +{ + GailTreeViewCellInfo *cell_info; + GtkTreeView *tree_view; + GtkTreeModel *tree_model; + GtkTreePath *path; + GtkTreeIter iter; + GList *renderers, *cur_renderer; + GParamSpec *spec; + GailRendererCellClass *gail_renderer_cell_class; + GtkCellRendererClass *gtk_cell_renderer_class; + GailCell *cell; + gchar **prop_list; + AtkObject *parent; + gboolean is_expander, is_expanded; + + gail_renderer_cell_class = GAIL_RENDERER_CELL_GET_CLASS (renderer_cell); + if (renderer_cell->renderer) + gtk_cell_renderer_class = GTK_CELL_RENDERER_GET_CLASS (renderer_cell->renderer); + else + gtk_cell_renderer_class = NULL; + + prop_list = gail_renderer_cell_class->property_list; + + cell = GAIL_CELL (renderer_cell); + cell_info = find_cell_info (gailview, cell, NULL, TRUE); + gail_return_val_if_fail (cell_info, FALSE); + gail_return_val_if_fail (cell_info->cell_col_ref, FALSE); + gail_return_val_if_fail (cell_info->cell_row_ref, FALSE); + + if (emit_change_signal && cell_info->in_use) + { + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (gailview)->widget); + tree_model = gtk_tree_view_get_model (tree_view); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + if (path == NULL) + return FALSE; + + gtk_tree_model_get_iter (tree_model, &iter, path); + is_expander = FALSE; + is_expanded = FALSE; + if (gtk_tree_model_iter_has_child (tree_model, &iter)) + { + GtkTreeViewColumn *expander_tv; + + expander_tv = gtk_tree_view_get_expander_column (tree_view); + if (expander_tv == cell_info->cell_col_ref) + { + is_expander = TRUE; + is_expanded = gtk_tree_view_row_expanded (tree_view, path); + } + } + gtk_tree_path_free (path); + gtk_tree_view_column_cell_set_cell_data (cell_info->cell_col_ref, + tree_model, &iter, is_expander, is_expanded); + } + renderers = gtk_tree_view_column_get_cell_renderers (cell_info->cell_col_ref); + gail_return_val_if_fail (renderers, FALSE); + + /* + * If the cell is in a container, it's index is used to find the renderer + * in the list + */ + + /* + * Otherwise, we assume that the cell is represented by the first renderer + * in the list + */ + + if (cell_info->in_use) { + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (!ATK_IS_OBJECT (cell)) g_on_error_query (NULL); + if (GAIL_IS_CONTAINER_CELL (parent)) + cur_renderer = g_list_nth (renderers, cell->index); + else + cur_renderer = renderers; + } + else { + return FALSE; + } + + gail_return_val_if_fail (cur_renderer != NULL, FALSE); + + if (gtk_cell_renderer_class) + { + while (*prop_list) + { + spec = g_object_class_find_property + (G_OBJECT_CLASS (gtk_cell_renderer_class), *prop_list); + + if (spec != NULL) + { + GValue value = { 0, }; + + g_value_init (&value, spec->value_type); + g_object_get_property (cur_renderer->data, *prop_list, &value); + g_object_set_property (G_OBJECT (renderer_cell->renderer), + *prop_list, &value); + g_value_unset(&value); + } + else + g_warning ("Invalid property: %s\n", *prop_list); + prop_list++; + } + } + g_list_free (renderers); + return gail_renderer_cell_update_cache (renderer_cell, emit_change_signal); +} + +static void +set_iter_nth_row (GtkTreeView *tree_view, + GtkTreeIter *iter, + gint row) +{ + GtkTreeModel *tree_model; + + tree_model = gtk_tree_view_get_model (tree_view); + gtk_tree_model_get_iter_root (tree_model, iter); + iter = return_iter_nth_row (tree_view, tree_model, iter, 0 , row); +} + +static gint +get_row_from_tree_path (GtkTreeView *tree_view, + GtkTreePath *path) +{ + GtkTreeModel *tree_model; + GtkTreePath *root_tree; + gint row; + + tree_model = gtk_tree_view_get_model (tree_view); + + if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY) + row = gtk_tree_path_get_indices (path)[0]; + else + { + root_tree = gtk_tree_path_new_root (); + row = 0; + iterate_thru_children (tree_view, tree_model, root_tree, path, &row, 0); + gtk_tree_path_free (root_tree); + } + + return row; +} + +/* Misc Private */ + +/* + * Get the specified GtkTreeViewColumn in the GtkTreeView. + * Only visible columns are considered. + */ +static GtkTreeViewColumn* +get_column (GtkTreeView *tree_view, + gint in_col) +{ + GtkTreeViewColumn *tv_col; + gint n_cols = -1; + gint i = 0; + + if (in_col < 0) + { + g_warning ("Request for invalid column %d\n", in_col); + return NULL; + } + + tv_col = gtk_tree_view_get_column (tree_view, i); + + while (tv_col != NULL) + { + if (gtk_tree_view_column_get_visible (tv_col)) + n_cols++; + if (in_col == n_cols) + break; + tv_col = gtk_tree_view_get_column (tree_view, ++i); + } + + if (in_col != n_cols) + { + g_warning ("Request for invalid column %d\n", in_col); + return NULL; + } + return tv_col; +} + +static gint +get_actual_column_number (GtkTreeView *tree_view, + gint visible_column) +{ + GtkTreeViewColumn *tv_col; + gint actual_column = 0; + gint visible_columns = -1; + /* + * This function calculates the column number which corresponds to the + * specified visible column number + */ + tv_col = gtk_tree_view_get_column (tree_view, actual_column); + + while (tv_col != NULL) + { + if (gtk_tree_view_column_get_visible (tv_col)) + visible_columns++; + if (visible_columns == visible_column) + return actual_column; + tv_col = gtk_tree_view_get_column (tree_view, ++actual_column); + } + g_warning ("get_actual_column_number failed for %d\n", visible_column); + return -1; +} + +static gint +get_visible_column_number (GtkTreeView *tree_view, + gint actual_column) +{ + GtkTreeViewColumn *tv_col; + gint column = 0; + gint visible_columns = -1; + /* + * This function calculates the visible column number which corresponds to the + * specified actual column number + */ + tv_col = gtk_tree_view_get_column (tree_view, column); + + while (tv_col != NULL) + { + if (gtk_tree_view_column_get_visible (tv_col)) + { + visible_columns++; + if (actual_column == column) + return visible_columns; + } + else + if (actual_column == column) + return -1; + tv_col = gtk_tree_view_get_column (tree_view, ++column); + } + g_warning ("get_visible_column_number failed for %d\n", actual_column); + return -1; +} + +/** + * Helper recursive function that returns GtkTreeIter pointer to nth row. + **/ +static GtkTreeIter* +return_iter_nth_row(GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gint increment, + gint row) +{ + GtkTreePath *current_path = gtk_tree_model_get_path (tree_model, iter); + GtkTreeIter new_iter; + gboolean row_expanded; + + if (increment == row) { + gtk_tree_path_free (current_path); + return iter; + } + + row_expanded = gtk_tree_view_row_expanded (tree_view, current_path); + gtk_tree_path_free (current_path); + + new_iter = *iter; + if ((row_expanded && gtk_tree_model_iter_children (tree_model, iter, &new_iter)) || + (gtk_tree_model_iter_next (tree_model, iter)) || + (gtk_tree_model_iter_parent (tree_model, iter, &new_iter) && + (gtk_tree_model_iter_next (tree_model, iter)))) + return return_iter_nth_row (tree_view, tree_model, iter, + ++increment, row); + + return NULL; +} + +/** + * Recursively called until the row specified by orig is found. + * + * *count will be set to the visible row number of the child + * relative to the row that was initially passed in as tree_path. + * + * *count will be -1 if orig is not found as a child (a row that is + * not visible will not be found, e.g. if the row is inside a + * collapsed row). If NULL is passed in as orig, *count will + * be a count of the visible children. + * + * NOTE: the value for depth must be 0 when this recursive function + * is initially called, or it may not function as expected. + **/ +static void +iterate_thru_children(GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GtkTreePath *tree_path, + GtkTreePath *orig, + gint *count, + gint depth) +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter (tree_model, &iter, tree_path)) + return; + + if (tree_path && orig && !gtk_tree_path_compare (tree_path, orig)) + /* Found it! */ + return; + + if (tree_path && orig && gtk_tree_path_compare (tree_path, orig) > 0) + { + /* Past it, so return -1 */ + *count = -1; + return; + } + else if (gtk_tree_view_row_expanded (tree_view, tree_path) && + gtk_tree_model_iter_has_child (tree_model, &iter)) + { + (*count)++; + gtk_tree_path_append_index (tree_path, 0); + iterate_thru_children (tree_view, tree_model, tree_path, + orig, count, (depth + 1)); + return; + } + else if (gtk_tree_model_iter_next (tree_model, &iter)) + { + (*count)++; + tree_path = gtk_tree_model_get_path (tree_model, &iter); + if (tree_path) + { + iterate_thru_children (tree_view, tree_model, tree_path, + orig, count, depth); + gtk_tree_path_free (tree_path); + } + return; + } + else if (gtk_tree_path_up (tree_path)) + { + GtkTreeIter temp_iter; + gboolean exit_loop = FALSE; + gint new_depth = depth - 1; + + (*count)++; + + /* + * Make sure that we back up until we find a row + * where gtk_tree_path_next does not return NULL. + */ + while (!exit_loop) + { + if (gtk_tree_path_get_depth (tree_path) == 0) + /* depth is now zero so */ + return; + gtk_tree_path_next (tree_path); + + /* Verify that the next row is a valid row! */ + exit_loop = gtk_tree_model_get_iter (tree_model, &temp_iter, tree_path); + + if (!exit_loop) + { + /* Keep going up until we find a row that has a valid next */ + if (gtk_tree_path_get_depth(tree_path) > 1) + { + new_depth--; + gtk_tree_path_up (tree_path); + } + else + { + /* + * If depth is 1 and gtk_tree_model_get_iter returns FALSE, + * then we are at the last row, so just return. + */ + if (orig != NULL) + *count = -1; + + return; + } + } + } + + /* + * This guarantees that we will stop when we hit the end of the + * children. + */ + if (new_depth < 0) + return; + + iterate_thru_children (tree_view, tree_model, tree_path, + orig, count, new_depth); + return; + } + + /* + * If it gets here, then the path wasn't found. Situations + * that would cause this would be if the path passed in is + * invalid or contained within the last row, but not visible + * because the last row is not expanded. If NULL was passed + * in then a row count is desired, so only set count to -1 + * if orig is not NULL. + */ + if (orig != NULL) + *count = -1; + + return; +} + +static void +clean_cell_info (GailTreeView *gailview, + GList *list) +{ + GailTreeViewCellInfo *cell_info; + GObject *obj; + + g_assert (GAIL_IS_TREE_VIEW (gailview)); + + cell_info = list->data; + + if (cell_info->in_use) { + obj = G_OBJECT (cell_info->cell); + + gail_cell_add_state (cell_info->cell, ATK_STATE_DEFUNCT, TRUE); + g_object_weak_unref (obj, (GWeakNotify) cell_destroyed, cell_info); + cell_info->in_use = FALSE; + if (!gailview->garbage_collection_pending) { + gailview->garbage_collection_pending = TRUE; + gailview->idle_garbage_collect_id = + g_idle_add (idle_garbage_collect_cell_data, gailview); + } + } +} + +static void +clean_rows (GailTreeView *gailview) +{ + GArray *array; + + /* Clean GailTreeViewRowInfo data */ + + array = gailview->row_data; + if (array != NULL) + { + GailTreeViewRowInfo *row_info; + GtkTreePath *row_path; + gint i; + + /* + * Loop backwards so that calls to free_row_info + * do not affect the index numbers + */ + for (i = (array->len - 1); i >= 0; i --) + { + row_info = g_array_index (array, GailTreeViewRowInfo*, i); + row_path = gtk_tree_row_reference_get_path (row_info->row_ref); + + /* Remove any rows that have become invalid */ + if (row_path == NULL) + free_row_info (array, i, TRUE); + else + gtk_tree_path_free (row_path); + } + } + + /* Clean GailTreeViewCellInfo data */ + + if (gailview->cell_data != NULL) + { + GailTreeViewCellInfo *cell_info; + GtkTreePath *row_path; + GList *cur_list; + GList *temp_list; + + temp_list = gailview->cell_data; + + /* Must loop through them all */ + while (temp_list != NULL) + { + cur_list = temp_list; + cell_info = temp_list->data; + temp_list = temp_list->next; + row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + + /* + * If the cell has become invalid because the row has been removed, + * then set the cell's state to ATK_STATE_DEFUNCT and remove the cell + * from gailview->cell_data. If row_path is NULL then the row has + * been removed. + */ + if (row_path == NULL) + { + clean_cell_info (gailview, cur_list); + } + else + { + gtk_tree_path_free (row_path); + } + } + } +} + +static void +clean_cols (GailTreeView *gailview, + GtkTreeViewColumn *tv_col) +{ + /* Clean GailTreeViewCellInfo data */ + + if (gailview->cell_data != NULL) + { + GailTreeViewCellInfo *cell_info; + GList *cur_list, *temp_list; + + temp_list = gailview->cell_data; + + while (temp_list != NULL) + { + cur_list = temp_list; + cell_info = temp_list->data; + temp_list = temp_list->next; + + /* + * If the cell has become invalid because the column tv_col + * has been removed, then set the cell's state to ATK_STATE_DEFUNCT + * and remove the cell from gailview->cell_data. + */ + if (cell_info->cell_col_ref == tv_col) + { + clean_cell_info (gailview, cur_list); + } + } + } +} + + +static gboolean +idle_garbage_collect_cell_data (gpointer data) +{ + GailTreeView *tree_view; + + GDK_THREADS_ENTER (); + g_assert (GAIL_IS_TREE_VIEW (data)); + tree_view = (GailTreeView *)data; + + /* this is the idle handler (only one instance allowed), so + * we can safely delete it. + */ + tree_view->garbage_collection_pending = FALSE; + tree_view->idle_garbage_collect_id = 0; + + tree_view->garbage_collection_pending = garbage_collect_cell_data (data); + + GDK_THREADS_LEAVE (); + + /* N.B.: if for some reason another handler has re-enterantly been queued + * while this handler was being serviced, it has its own gsource, therefore this handler + * should always return FALSE. + */ + return FALSE; +} + +static gboolean +garbage_collect_cell_data (gpointer data) +{ + GailTreeView *tree_view; + GList *temp_list; + GailTreeViewCellInfo *cell_info; + + g_assert (GAIL_IS_TREE_VIEW (data)); + tree_view = (GailTreeView *)data; + temp_list = g_list_copy (tree_view->cell_data); + + tree_view->garbage_collection_pending = FALSE; + if (tree_view->idle_garbage_collect_id != 0) + { + g_source_remove (tree_view->idle_garbage_collect_id); + tree_view->idle_garbage_collect_id = 0; + } + + /* Must loop through them all */ + while (temp_list != NULL) + { + cell_info = temp_list->data; + if (!cell_info->in_use) + { + /* g_object_unref (cell_info->cell); */ + tree_view->cell_data = g_list_remove (tree_view->cell_data, + cell_info); + if (cell_info->cell_row_ref) + gtk_tree_row_reference_free (cell_info->cell_row_ref); + g_free (cell_info); + } + temp_list = temp_list->next; + } + g_list_free (temp_list); + + return tree_view->garbage_collection_pending; +} + +/** + * If tree_path is passed in as NULL, then all cells are acted on. + * Otherwise, just act on those cells that are on a row greater than + * the specified tree_path. If inc_row is passed in as TRUE, then rows + * greater and equal to the specified tree_path are acted on. + * + * if set_stale is set the ATK_STATE_STALE is set on cells which are to be + * acted on. + * + * The function set_cell_visibility() is called on all cells to be + * acted on to update the visibility of the cell. + **/ +static void +traverse_cells (GailTreeView *tree_view, + GtkTreePath *tree_path, + gboolean set_stale, + gboolean inc_row) +{ + if (tree_view->cell_data != NULL) + { + GailTreeViewCellInfo *cell_info; + GtkTreeView *gtk_tree_view; + GList *temp_list; + GtkWidget *widget; + + g_assert (GTK_IS_ACCESSIBLE (tree_view)); + + widget = GTK_ACCESSIBLE (tree_view)->widget; + if (!widget) + /* Widget is being deleted */ + return; + + gtk_tree_view = GTK_TREE_VIEW (widget); + temp_list = tree_view->cell_data; + + /* Must loop through them all */ + while (temp_list != NULL) + { + GtkTreePath *row_path; + gboolean act_on_cell; + + cell_info = temp_list->data; + temp_list = temp_list->next; + + if (cell_info->in_use) + { + row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + g_assert (row_path != NULL); + if (tree_path == NULL) + act_on_cell = TRUE; + else + { + gint comparison; + + comparison = gtk_tree_path_compare (row_path, tree_path); + if ((comparison > 0) || + (comparison == 0 && inc_row)) + act_on_cell = TRUE; + else + act_on_cell = FALSE; + } + if (!cell_info->in_use) g_warning ("warning: cell info destroyed during traversal"); + if (act_on_cell && cell_info->in_use) + { + if (set_stale) + gail_cell_add_state (cell_info->cell, ATK_STATE_STALE, TRUE); + set_cell_visibility (gtk_tree_view, + cell_info->cell, + cell_info->cell_col_ref, + row_path, TRUE); + } + gtk_tree_path_free (row_path); + } + } + } + g_signal_emit_by_name (tree_view, "visible-data-changed"); +} + +static void +free_row_info (GArray *array, + gint array_idx, + gboolean shift) +{ + GailTreeViewRowInfo* obj; + + obj = g_array_index (array, GailTreeViewRowInfo*, array_idx); + + g_free (obj->description); + if (obj->row_ref != NULL) + gtk_tree_row_reference_free (obj->row_ref); + if (obj->header) + g_object_unref (obj->header); + g_free (obj); + + if (shift) + g_array_remove_index (array, array_idx); +} + +/* + * If the tree_path passed in has children, then + * ATK_STATE_EXPANDABLE is set. If the row is expanded + * ATK_STATE_EXPANDED is turned on. If the row is + * collapsed, then ATK_STATE_EXPANDED is removed. + * + * If the tree_path passed in has no children, then + * ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED are removed. + * + * If set_on_ancestor is TRUE, then this function will also + * update all cells that are ancestors of the tree_path. + */ +static void +set_expand_state (GtkTreeView *tree_view, + GtkTreeModel *tree_model, + GailTreeView *gailview, + GtkTreePath *tree_path, + gboolean set_on_ancestor) +{ + if (gailview->cell_data != NULL) + { + GtkTreeViewColumn *expander_tv; + GailTreeViewCellInfo *cell_info; + GList *temp_list; + GtkTreePath *cell_path; + GtkTreeIter iter; + gboolean found; + + temp_list = gailview->cell_data; + + while (temp_list != NULL) + { + cell_info = temp_list->data; + temp_list = temp_list->next; + if (cell_info->in_use) + { + cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + found = FALSE; + + if (cell_path != NULL) + { + GailCell *cell = GAIL_CELL (cell_info->cell); + + expander_tv = gtk_tree_view_get_expander_column (tree_view); + + /* + * Only set state for the cell that is in the column with the + * expander toggle + */ + if (expander_tv == cell_info->cell_col_ref) + { + if (tree_path && gtk_tree_path_compare (cell_path, tree_path) == 0) + found = TRUE; + else if (set_on_ancestor && + gtk_tree_path_get_depth (cell_path) < + gtk_tree_path_get_depth (tree_path) && + gtk_tree_path_is_ancestor (cell_path, tree_path) == 1) + /* Only set if set_on_ancestor was passed in as TRUE */ + found = TRUE; + } + + /* + * Set ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED + * for ancestors and found cells. + */ + if (found) + { + /* + * Must check against cell_path since cell_path + * can be equal to or an ancestor of tree_path. + */ + gtk_tree_model_get_iter (tree_model, &iter, cell_path); + + /* Set or unset ATK_STATE_EXPANDABLE as appropriate */ + if (gtk_tree_model_iter_has_child (tree_model, &iter)) + { + set_cell_expandable (cell); + + if (gtk_tree_view_row_expanded (tree_view, cell_path)) + gail_cell_add_state (cell, ATK_STATE_EXPANDED, TRUE); + else + gail_cell_remove_state (cell, + ATK_STATE_EXPANDED, TRUE); + } + else + { + gail_cell_remove_state (cell, + ATK_STATE_EXPANDED, TRUE); + if (gail_cell_remove_state (cell, + ATK_STATE_EXPANDABLE, TRUE)) + /* The state may have been propagated to the container cell */ + if (!GAIL_IS_CONTAINER_CELL (cell)) + gail_cell_remove_action_by_name (cell, + "expand or contract"); + } + + /* + * We assume that each cell in the cache once and + * a container cell is before its child cells so we are + * finished if set_on_ancestor is not set to TRUE. + */ + if (!set_on_ancestor) + break; + } + } + gtk_tree_path_free (cell_path); + } + } + } +} + + +static void +add_cell_actions (GailCell *cell, + gboolean editable) +{ + if (GAIL_IS_BOOLEAN_CELL (cell)) + gail_cell_add_action (cell, + "toggle", + "toggles the cell", /* action description */ + NULL, + toggle_cell_toggled); + if (editable) + gail_cell_add_action (cell, + "edit", + "creates a widget in which the contents of the cell can be edited", + NULL, + edit_cell); + gail_cell_add_action (cell, + "activate", + "activate the cell", + NULL, + activate_cell); +} + +static void +toggle_cell_expanded (GailCell *cell) +{ + GailTreeViewCellInfo *cell_info; + GtkTreeView *tree_view; + GtkTreePath *path; + AtkObject *parent; + AtkStateSet *stateset; + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (GAIL_IS_CONTAINER_CELL (parent)) + parent = atk_object_get_parent (parent); + + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_if_fail (cell_info); + gail_return_if_fail (cell_info->cell_col_ref); + gail_return_if_fail (cell_info->cell_row_ref); + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (parent)->widget); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + gail_return_if_fail (path); + + stateset = atk_object_ref_state_set (ATK_OBJECT (cell)); + if (atk_state_set_contains_state (stateset, ATK_STATE_EXPANDED)) + gtk_tree_view_collapse_row (tree_view, path); + else + gtk_tree_view_expand_row (tree_view, path, TRUE); + g_object_unref (stateset); + gtk_tree_path_free (path); + return; +} + +static void +toggle_cell_toggled (GailCell *cell) +{ + GailTreeViewCellInfo *cell_info; + GtkTreeView *tree_view; + GtkTreePath *path; + gchar *pathstring; + GList *renderers, *cur_renderer; + AtkObject *parent; + gboolean is_container_cell = FALSE; + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (GAIL_IS_CONTAINER_CELL (parent)) + { + is_container_cell = TRUE; + parent = atk_object_get_parent (parent); + } + + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_if_fail (cell_info); + gail_return_if_fail (cell_info->cell_col_ref); + gail_return_if_fail (cell_info->cell_row_ref); + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (parent)->widget); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + gail_return_if_fail (path); + pathstring = gtk_tree_path_to_string (path); + + renderers = gtk_tree_view_column_get_cell_renderers (cell_info->cell_col_ref); + gail_return_if_fail (renderers); + + /* + * if the cell is in a container, it's index is used to find the + * renderer in the list + */ + + if (is_container_cell) + cur_renderer = g_list_nth (renderers, cell->index); + else + /* + * Otherwise, we assume that the cell is represented by the first + * renderer in the list + */ + cur_renderer = renderers; + + gail_return_if_fail (cur_renderer); + + g_signal_emit_by_name (cur_renderer->data, "toggled", pathstring); + g_list_free (renderers); + g_free (pathstring); + gtk_tree_path_free (path); + return; +} + +static void +edit_cell (GailCell *cell) +{ + GailTreeViewCellInfo *cell_info; + GtkTreeView *tree_view; + GtkTreePath *path; + AtkObject *parent; + gboolean is_container_cell = FALSE; + + editing = TRUE; + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (GAIL_IS_CONTAINER_CELL (parent)) + { + is_container_cell = TRUE; + parent = atk_object_get_parent (parent); + } + + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_if_fail (cell_info); + gail_return_if_fail (cell_info->cell_col_ref); + gail_return_if_fail (cell_info->cell_row_ref); + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (parent)->widget); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + gail_return_if_fail (path); + gtk_tree_view_set_cursor (tree_view, path, cell_info->cell_col_ref, TRUE); + gtk_tree_path_free (path); + return; +} + +static void +activate_cell (GailCell *cell) +{ + GailTreeViewCellInfo *cell_info; + GtkTreeView *tree_view; + GtkTreePath *path; + AtkObject *parent; + gboolean is_container_cell = FALSE; + + editing = TRUE; + parent = atk_object_get_parent (ATK_OBJECT (cell)); + if (GAIL_IS_CONTAINER_CELL (parent)) + { + is_container_cell = TRUE; + parent = atk_object_get_parent (parent); + } + + cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_if_fail (cell_info); + gail_return_if_fail (cell_info->cell_col_ref); + gail_return_if_fail (cell_info->cell_row_ref); + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (parent)->widget); + path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref); + gail_return_if_fail (path); + gtk_tree_view_row_activated (tree_view, path, cell_info->cell_col_ref); + gtk_tree_path_free (path); + return; +} + +static void +cell_destroyed (gpointer data) +{ + GailTreeViewCellInfo *cell_info = data; + + gail_return_if_fail (cell_info); + if (cell_info->in_use) { + cell_info->in_use = FALSE; + + g_assert (GAIL_IS_TREE_VIEW (cell_info->view)); + if (!cell_info->view->garbage_collection_pending) { + cell_info->view->garbage_collection_pending = TRUE; + cell_info->view->idle_garbage_collect_id = + g_idle_add (idle_garbage_collect_cell_data, cell_info->view); + } + } +} + +#if 0 +static void +cell_info_remove (GailTreeView *tree_view, + GailCell *cell) +{ + GailTreeViewCellInfo *info; + GList *temp_list; + + info = find_cell_info (tree_view, cell, &temp_list, FALSE); + if (info) + { + info->in_use = FALSE; + return; + } + g_warning ("No cell removed in cell_info_remove\n"); +} +#endif + +static void +cell_info_get_index (GtkTreeView *tree_view, + GailTreeViewCellInfo *info, + gint *index) +{ + GtkTreePath *path; + gint column_number; + + path = gtk_tree_row_reference_get_path (info->cell_row_ref); + gail_return_if_fail (path); + + column_number = get_column_number (tree_view, info->cell_col_ref, FALSE); + *index = get_index (tree_view, path, column_number); + gtk_tree_path_free (path); +} + +static void +cell_info_new (GailTreeView *gailview, + GtkTreeModel *tree_model, + GtkTreePath *path, + GtkTreeViewColumn *tv_col, + GailCell *cell ) +{ + GailTreeViewCellInfo *cell_info; + + g_assert (GAIL_IS_TREE_VIEW (gailview)); + + cell_info = g_new (GailTreeViewCellInfo, 1); + cell_info->cell_row_ref = gtk_tree_row_reference_new (tree_model, path); + + cell_info->cell_col_ref = tv_col; + cell_info->cell = cell; + cell_info->in_use = TRUE; /* if we've created it, assume it's in use */ + cell_info->view = gailview; + gailview->cell_data = g_list_append (gailview->cell_data, cell_info); + + /* Setup weak reference notification */ + + g_object_weak_ref (G_OBJECT (cell), + (GWeakNotify) cell_destroyed, + cell_info); +} + +static GailCell* +find_cell (GailTreeView *gailview, + gint index) +{ + GailTreeViewCellInfo *info; + GtkTreeView *tree_view; + GList *cell_list; + GList *l; + gint real_index; + gboolean needs_cleaning = FALSE; + GailCell *retval = NULL; + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (gailview)->widget); + cell_list = gailview->cell_data; + + for (l = cell_list; l; l = l->next) + { + info = (GailTreeViewCellInfo *) (l->data); + if (info->in_use) + { + cell_info_get_index (tree_view, info, &real_index); + if (index == real_index) + { + retval = info->cell; + break; + } + } + else + { + needs_cleaning = TRUE; + } + } + if (needs_cleaning) + garbage_collect_cell_data (gailview); + + return retval; +} + +static void +refresh_cell_index (GailCell *cell) +{ + GailTreeViewCellInfo *info; + AtkObject *parent; + GtkTreeView *tree_view; + gint index; + + parent = atk_object_get_parent (ATK_OBJECT (cell)); + gail_return_if_fail (GAIL_IS_TREE_VIEW (parent)); + + tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (parent)->widget); + + /* Find this cell in the GailTreeView's cache */ + + info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE); + gail_return_if_fail (info); + + cell_info_get_index (tree_view, info, &index); + cell->index = index; +} + +static void +get_selected_rows (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GPtrArray *array = (GPtrArray *)data; + + g_ptr_array_add (array, gtk_tree_path_copy (path)); +} + +static void +connect_model_signals (GtkTreeView *view, + GailTreeView *gailview) +{ + GObject *obj; + + obj = G_OBJECT (gailview->tree_model); + g_signal_connect_data (obj, "row-changed", + (GCallback) model_row_changed, view, NULL, 0); + g_signal_connect_data (obj, "row-inserted", + (GCallback) model_row_inserted, view, NULL, + G_CONNECT_AFTER); + g_signal_connect_data (obj, "row-deleted", + (GCallback) model_row_deleted, view, NULL, + G_CONNECT_AFTER); + g_signal_connect_data (obj, "rows-reordered", + (GCallback) model_rows_reordered, view, NULL, + G_CONNECT_AFTER); +} + +static void +disconnect_model_signals (GailTreeView *view) +{ + GObject *obj; + GtkWidget *widget; + + obj = G_OBJECT (view->tree_model); + widget = GTK_ACCESSIBLE (view)->widget; + g_signal_handlers_disconnect_by_func (obj, (gpointer) model_row_changed, widget); + g_signal_handlers_disconnect_by_func (obj, (gpointer) model_row_inserted, widget); + g_signal_handlers_disconnect_by_func (obj, (gpointer) model_row_deleted, widget); + g_signal_handlers_disconnect_by_func (obj, (gpointer) model_rows_reordered, widget); +} + +static void +clear_cached_data (GailTreeView *view) +{ + GList *temp_list; + + if (view->row_data) + { + GArray *array = view->row_data; + gint i; + + /* + * Since the third argument to free_row_info is FALSE, we don't remove + * the element. Therefore it is safe to loop forward. + */ + for (i = 0; i < array->len; i++) + free_row_info (array, i, FALSE); + + g_array_free (array, TRUE); + + view->row_data = NULL; + } + + if (view->cell_data) + { + /* Must loop through them all */ + for (temp_list = view->cell_data; temp_list; temp_list = temp_list->next) + { + clean_cell_info (view, temp_list); + } + } + garbage_collect_cell_data (view); + if (view->cell_data) + g_list_free (view->cell_data); + + view->cell_data = NULL; +} + +/* + * Returns the column number of the specified GtkTreeViewColumn + * + * If visible is set, the value returned will be the visible column number, + * i.e. suitable for use in AtkTable function. If visible is not set, the + * value returned is the actual column number, which is suitable for use in + * getting an index value. + */ +static gint +get_column_number (GtkTreeView *tree_view, + GtkTreeViewColumn *column, + gboolean visible) +{ + GList *temp_list, *column_list; + GtkTreeViewColumn *tv_column; + gint ret_val; + + column_list = gtk_tree_view_get_columns (tree_view); + ret_val = 0; + for (temp_list = column_list; temp_list; temp_list = temp_list->next) + { + tv_column = GTK_TREE_VIEW_COLUMN (temp_list->data); + if (tv_column == column) + break; + if (!visible || gtk_tree_view_column_get_visible (tv_column)) + ret_val++; + } + if (temp_list == NULL) + { + ret_val = -1; + } + g_list_free (column_list); + return ret_val; +} + +static gint +get_index (GtkTreeView *tree_view, + GtkTreePath *path, + gint actual_column) +{ + gint depth = 0; + gint index = 1; + gint *indices = NULL; + + + if (path) + { + depth = gtk_tree_path_get_depth (path); + indices = gtk_tree_path_get_indices (path); + } + + if (depth > 1) + { + GtkTreePath *copy_path; + GtkTreeModel *model; + + model = gtk_tree_view_get_model (tree_view); + copy_path = gtk_tree_path_copy (path); + gtk_tree_path_up (copy_path); + count_rows (model, NULL, copy_path, &index, 0, depth); + gtk_tree_path_free (copy_path); + } + + if (path) + index += indices[depth-1]; + index *= get_n_actual_columns (tree_view); + index += actual_column; + return index; +} + +/* + * The function count_rows counts the number of rows starting at iter and ending + * at end_path. The value of level is the depth of iter and the value of depth + * is the depth of end_path. Rows at depth before end_path are counted. + * This functions counts rows which are not visible because an ancestor is + * collapsed. + */ +static void +count_rows (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *end_path, + gint *count, + gint level, + gint depth) +{ + GtkTreeIter child_iter; + + if (!model) return; + + level++; + + *count += gtk_tree_model_iter_n_children (model, iter); + +#if 0 + g_print ("count_rows : %d level: %d depth: %d\n", *count, level, depth); + if (iter != NULL) + g_print ("path: %s\n", + gtk_tree_path_to_string (gtk_tree_model_get_path (model, iter))); +#endif + + if (level >= depth) + return; + + if (gtk_tree_model_iter_children (model, &child_iter, iter)) + { + gboolean ret_val = TRUE; + + while (ret_val) + { + if (level == depth - 1) + { + GtkTreePath *iter_path; + gboolean finished = FALSE; + + iter_path = gtk_tree_model_get_path (model, &child_iter); + if (end_path && gtk_tree_path_compare (iter_path, end_path) >= 0) + finished = TRUE; + gtk_tree_path_free (iter_path); + if (finished) + break; + } + if (gtk_tree_model_iter_has_child (model, &child_iter)) + count_rows (model, &child_iter, end_path, count, level, depth); + ret_val = gtk_tree_model_iter_next (model, &child_iter); + } + } +} + +/* + * Find the next node, which has children, at the specified depth below + * the specified iter. The level is the depth of the current iter. + * The position of the node is returned in path and the return value of TRUE + * means that a node was found. + */ + +gboolean get_next_node_with_child_at_depth (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath **path, + gint level, + gint depth) +{ + GtkTreeIter child_iter; + + *path = NULL; + + if (gtk_tree_model_iter_children (model, &child_iter, iter)) + { + level++; + + while (TRUE) + { + while (!gtk_tree_model_iter_has_child (model, &child_iter)) + { + if (!gtk_tree_model_iter_next (model, &child_iter)) + return FALSE; + } + + if (level == depth) + /* We have found what we were looking for */ + { + *path = gtk_tree_model_get_path (model, &child_iter); + return TRUE; + } + + if (get_next_node_with_child_at_depth (model, &child_iter, path, + level, depth)) + return TRUE; + + if (!gtk_tree_model_iter_next (model, &child_iter)) + return FALSE; + } + } + return FALSE; +} + +/* + * Find the next node, which has children, at the same depth as + * the specified GtkTreePath. + */ +static gboolean +get_next_node_with_child (GtkTreeModel *model, + GtkTreePath *path, + GtkTreePath **return_path) +{ + GtkTreeIter iter; + gint depth; + + gtk_tree_model_get_iter (model, &iter, path); + + while (gtk_tree_model_iter_next (model, &iter)) + { + if (gtk_tree_model_iter_has_child (model, &iter)) + { + *return_path = gtk_tree_model_get_path (model, &iter); + return TRUE; + } + } + depth = gtk_tree_path_get_depth (path); + while (gtk_tree_path_up (path)) + { + if (gtk_tree_path_get_depth (path) == 0) + break; + + gtk_tree_model_get_iter (model, &iter, path); + while (gtk_tree_model_iter_next (model, &iter)) + if (get_next_node_with_child_at_depth (model, &iter, return_path, + gtk_tree_path_get_depth (path), depth)) + return TRUE; + } + *return_path = NULL; + return FALSE; +} + +static gboolean +get_tree_path_from_row_index (GtkTreeModel *model, + gint row_index, + GtkTreePath **tree_path) +{ + GtkTreeIter iter; + gint count; + gint depth; + + count = gtk_tree_model_iter_n_children (model, NULL); + if (count > row_index) + { + if (gtk_tree_model_iter_nth_child (model, &iter, NULL, row_index)) + { + *tree_path = gtk_tree_model_get_path (model, &iter); + return TRUE; + } + else + return FALSE; + } + else + row_index -= count; + + depth = 0; + while (TRUE) + { + depth++; + + if (get_next_node_with_child_at_depth (model, NULL, tree_path, 0, depth)) + { + GtkTreePath *next_path; + + while (TRUE) + { + gtk_tree_model_get_iter (model, &iter, *tree_path); + count = gtk_tree_model_iter_n_children (model, &iter); + if (count > row_index) + { + gtk_tree_path_append_index (*tree_path, row_index); + return TRUE; + } + else + row_index -= count; + + if (!get_next_node_with_child (model, *tree_path, &next_path)) + break; + + gtk_tree_path_free (*tree_path); + *tree_path = next_path; + } + } + else + { + g_warning ("Index value is too large\n"); + gtk_tree_path_free (*tree_path); + *tree_path = NULL; + return FALSE; + } + } +} + +/* + * This function returns the number of rows, including those which are collapsed + */ +static gint +get_row_count (GtkTreeModel *model) +{ + gint n_rows = 1; + + count_rows (model, NULL, NULL, &n_rows, 0, G_MAXINT); + + return n_rows; +} + +static gboolean +get_path_column_from_index (GtkTreeView *tree_view, + gint index, + GtkTreePath **path, + GtkTreeViewColumn **column) +{ + GtkTreeModel *tree_model; + gint n_columns; + + tree_model = gtk_tree_view_get_model (tree_view); + n_columns = get_n_actual_columns (tree_view); + if (n_columns == 0) + return FALSE; + /* First row is the column headers */ + index -= n_columns; + if (index < 0) + return FALSE; + + if (path) + { + gint row_index; + gboolean retval; + + row_index = index / n_columns; + retval = get_tree_path_from_row_index (tree_model, row_index, path); + gail_return_val_if_fail (retval, FALSE); + if (*path == NULL) + return FALSE; + } + + if (column) + { + *column = gtk_tree_view_get_column (tree_view, index % n_columns); + if (*column == NULL) + { + if (path) + gtk_tree_path_free (*path); + return FALSE; + } + } + return TRUE; +} + +static void +set_cell_expandable (GailCell *cell) +{ + if (gail_cell_add_state (cell, + ATK_STATE_EXPANDABLE, + FALSE)) + gail_cell_add_action (cell, + "expand or contract", /* action name */ + "expands or contracts the row in the tree view " + "containing this cell", /* description */ + NULL, /* Keybinding */ + toggle_cell_expanded); +} + +static GailTreeViewCellInfo* +find_cell_info (GailTreeView *view, + GailCell *cell, + GList** list, + gboolean live_only) +{ + GList *temp_list; + GailTreeViewCellInfo *cell_info; + + for (temp_list = view->cell_data; temp_list; temp_list = temp_list->next) + { + cell_info = (GailTreeViewCellInfo *) temp_list->data; + if (cell_info->cell == cell && (!live_only || cell_info->in_use)) + { + if (list) + *list = temp_list; + return cell_info; + } + } + return NULL; +} + +static AtkObject * +get_header_from_column (GtkTreeViewColumn *tv_col) +{ + AtkObject *rc; + GtkWidget *header_widget; + + if (tv_col == NULL) + return NULL; + + /* If the user has set a header object, use that */ + + rc = g_object_get_qdata (G_OBJECT (tv_col), quark_column_header_object); + + if (rc == NULL) + { + /* If the user has not set a header object, grab the column */ + /* header object defined by the GtkTreeView */ + + header_widget = tv_col->button; + + if (header_widget) + { + rc = gtk_widget_get_accessible (header_widget); + } + else + rc = NULL; + } + return rc; +} diff --git a/modules/other/gail/gailtreeview.h b/modules/other/gail/gailtreeview.h new file mode 100644 index 000000000..6f153318e --- /dev/null +++ b/modules/other/gail/gailtreeview.h @@ -0,0 +1,77 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TREE_VIEW_H__ +#define __GAIL_TREE_VIEW_H__ + +#include <gtk/gtk.h> +#include <gail/gailcontainer.h> +#include <gail/gailcell.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_TREE_VIEW (gail_tree_view_get_type ()) +#define GAIL_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TREE_VIEW, GailTreeView)) +#define GAIL_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_TREE_VIEW, GailTreeViewClass)) +#define GAIL_IS_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TREE_VIEW)) +#define GAIL_IS_TREE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TREE_VIEW)) +#define GAIL_TREE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TREE_VIEW, GailTreeViewClass)) + +typedef struct _GailTreeView GailTreeView; +typedef struct _GailTreeViewClass GailTreeViewClass; + +struct _GailTreeView +{ + GailContainer parent; + + AtkObject* caption; + AtkObject* summary; + gint n_children_deleted; + GArray* col_data; + GArray* row_data; + GList* cell_data; + GtkTreeModel *tree_model; + AtkObject *focus_cell; + GtkAdjustment *old_hadj; + GtkAdjustment *old_vadj; + guint idle_expand_id; + guint idle_garbage_collect_id; + GtkTreePath *idle_expand_path; + gboolean garbage_collection_pending; +}; + +GType gail_tree_view_get_type (void); + +struct _GailTreeViewClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_tree_view_new (GtkWidget *widget); + +AtkObject* gail_tree_view_ref_focus_cell (GtkTreeView *treeview); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_TREE_VIEW_H__ */ diff --git a/modules/other/gail/gailutil.c b/modules/other/gail/gailutil.c new file mode 100644 index 000000000..bdfa62516 --- /dev/null +++ b/modules/other/gail/gailutil.c @@ -0,0 +1,658 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <gtk/gtk.h> +#include "gailutil.h" +#include "gailtoplevel.h" +#include "gailwindow.h" +#include "gail-private-macros.h" + +static void gail_util_class_init (GailUtilClass *klass); + +/* atkutil.h */ + +static guint gail_util_add_global_event_listener (GSignalEmissionHook listener, + const gchar* event_type); +static void gail_util_remove_global_event_listener (guint remove_listener); +static guint gail_util_add_key_event_listener (AtkKeySnoopFunc listener, + gpointer data); +static void gail_util_remove_key_event_listener (guint remove_listener); +static AtkObject* gail_util_get_root (void); +static G_CONST_RETURN gchar *gail_util_get_toolkit_name (void); +static G_CONST_RETURN gchar *gail_util_get_toolkit_version (void); + +/* gailmisc/AtkMisc */ +static void gail_misc_class_init (GailMiscClass *klass); + +static void gail_misc_threads_enter (AtkMisc *misc); +static void gail_misc_threads_leave (AtkMisc *misc); + +/* Misc */ + +static void _listener_info_destroy (gpointer data); +static guint add_listener (GSignalEmissionHook listener, + const gchar *object_type, + const gchar *signal, + const gchar *hook_data); +static void do_window_event_initialization (void); +static gboolean state_event_watcher (GSignalInvocationHint *hint, + guint n_param_values, + const GValue *param_values, + gpointer data); +static void window_added (AtkObject *atk_obj, + guint index, + AtkObject *child); +static void window_removed (AtkObject *atk_obj, + guint index, + AtkObject *child); +static gboolean window_focus (GtkWidget *widget, + GdkEventFocus *event); +static gboolean configure_event_watcher (GSignalInvocationHint *hint, + guint n_param_values, + const GValue *param_values, + gpointer data); + + +static AtkObject* root = NULL; +static GHashTable *listener_list = NULL; +static gint listener_idx = 1; +static GHashTable *key_listener_list = NULL; +static guint key_snooper_id = 0; + +typedef struct _GailUtilListenerInfo GailUtilListenerInfo; +typedef struct _GailKeyEventInfo GailKeyEventInfo; + +struct _GailUtilListenerInfo +{ + gint key; + guint signal_id; + gulong hook_id; +}; + +struct _GailKeyEventInfo +{ + AtkKeyEventStruct *key_event; + gpointer func_data; +}; + +GType +gail_util_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailUtilClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_util_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailUtil), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (ATK_TYPE_UTIL, + "GailUtil", &tinfo, 0); + } + return type; +} + +static void +gail_util_class_init (GailUtilClass *klass) +{ + AtkUtilClass *atk_class; + gpointer data; + + data = g_type_class_peek (ATK_TYPE_UTIL); + atk_class = ATK_UTIL_CLASS (data); + + atk_class->add_global_event_listener = + gail_util_add_global_event_listener; + atk_class->remove_global_event_listener = + gail_util_remove_global_event_listener; + atk_class->add_key_event_listener = + gail_util_add_key_event_listener; + atk_class->remove_key_event_listener = + gail_util_remove_key_event_listener; + atk_class->get_root = gail_util_get_root; + atk_class->get_toolkit_name = gail_util_get_toolkit_name; + atk_class->get_toolkit_version = gail_util_get_toolkit_version; + + listener_list = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, + _listener_info_destroy); +} + +static guint +gail_util_add_global_event_listener (GSignalEmissionHook listener, + const gchar *event_type) +{ + guint rc = 0; + gchar **split_string; + + split_string = g_strsplit (event_type, ":", 3); + + if (split_string) + { + if (!strcmp ("window", split_string[0])) + { + static gboolean initialized = FALSE; + + if (!initialized) + { + do_window_event_initialization (); + initialized = TRUE; + } + rc = add_listener (listener, "GailWindow", split_string[1], event_type); + } + else + { + rc = add_listener (listener, split_string[1], split_string[2], event_type); + } + + g_strfreev (split_string); + } + + return rc; +} + +static void +gail_util_remove_global_event_listener (guint remove_listener) +{ + if (remove_listener > 0) + { + GailUtilListenerInfo *listener_info; + gint tmp_idx = remove_listener; + + listener_info = (GailUtilListenerInfo *) + g_hash_table_lookup(listener_list, &tmp_idx); + + if (listener_info != NULL) + { + /* Hook id of 0 and signal id of 0 are invalid */ + if (listener_info->hook_id != 0 && listener_info->signal_id != 0) + { + /* Remove the emission hook */ + g_signal_remove_emission_hook(listener_info->signal_id, + listener_info->hook_id); + + /* Remove the element from the hash */ + g_hash_table_remove(listener_list, &tmp_idx); + } + else + { + g_warning("Invalid listener hook_id %ld or signal_id %d\n", + listener_info->hook_id, listener_info->signal_id); + } + } + else + { + g_warning("No listener with the specified listener id %d", + remove_listener); + } + } + else + { + g_warning("Invalid listener_id %d", remove_listener); + } +} + + +static +AtkKeyEventStruct * +atk_key_event_from_gdk_event_key (GdkEventKey *key) +{ + AtkKeyEventStruct *event = g_new0 (AtkKeyEventStruct, 1); + switch (key->type) + { + case GDK_KEY_PRESS: + event->type = ATK_KEY_EVENT_PRESS; + break; + case GDK_KEY_RELEASE: + event->type = ATK_KEY_EVENT_RELEASE; + break; + default: + g_assert_not_reached (); + return NULL; + } + event->state = key->state; + event->keyval = key->keyval; + event->length = key->length; + if (key->string && key->string [0] && + (key->state & GDK_CONTROL_MASK || + g_unichar_isgraph (g_utf8_get_char (key->string)))) + { + event->string = key->string; + } + else if (key->type == GDK_KEY_PRESS || + key->type == GDK_KEY_RELEASE) + { + event->string = gdk_keyval_name (key->keyval); + } + event->keycode = key->hardware_keycode; + event->timestamp = key->time; +#ifdef GAIL_DEBUG + g_print ("GailKey:\tsym %u\n\tmods %x\n\tcode %u\n\ttime %lx\n", + (unsigned int) event->keyval, + (unsigned int) event->state, + (unsigned int) event->keycode, + (unsigned long int) event->timestamp); +#endif + return event; +} + +static gboolean +notify_hf (gpointer key, gpointer value, gpointer data) +{ + GailKeyEventInfo *info = (GailKeyEventInfo *) data; + return (*(AtkKeySnoopFunc) value) (info->key_event, info->func_data) ? TRUE : FALSE; +} + +static void +insert_hf (gpointer key, gpointer value, gpointer data) +{ + GHashTable *new_table = (GHashTable *) data; + g_hash_table_insert (new_table, key, value); +} + +static gint +gail_key_snooper (GtkWidget *the_widget, GdkEventKey *event, gpointer func_data) +{ + /* notify each AtkKeySnoopFunc in turn... */ + GailKeyEventInfo *info = g_new0 (GailKeyEventInfo, 1); + gint consumed = 0; + if (key_listener_list) + { + GHashTable *new_hash = g_hash_table_new (NULL, NULL); + g_hash_table_foreach (key_listener_list, insert_hf, new_hash); + info->key_event = atk_key_event_from_gdk_event_key (event); + info->func_data = func_data; + consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info); + g_hash_table_destroy (new_hash); + } + g_free (info->key_event); + g_free (info); + return (consumed ? 1 : 0); +} + +static guint +gail_util_add_key_event_listener (AtkKeySnoopFunc listener, + gpointer data) +{ + static guint key=0; + if (!key_listener_list) + { + key_listener_list = g_hash_table_new (NULL, NULL); + key_snooper_id = gtk_key_snooper_install (gail_key_snooper, data); + } + g_hash_table_insert (key_listener_list, GUINT_TO_POINTER (key++), (gpointer) listener); + /* XXX: we don't check to see if n_listeners > MAXUINT */ + return key; +} + +static void +gail_util_remove_key_event_listener (guint remove_listener) +{ + g_hash_table_remove (key_listener_list, GUINT_TO_POINTER (remove_listener)); + if (g_hash_table_size (key_listener_list) == 0) + { + gtk_key_snooper_remove (key_snooper_id); + } +} + +static AtkObject* +gail_util_get_root (void) +{ + if (!root) + root = gail_toplevel_new(); + + return root; +} + +static G_CONST_RETURN gchar * +gail_util_get_toolkit_name (void) +{ + return "GAIL"; +} + +static G_CONST_RETURN gchar * +gail_util_get_toolkit_version (void) +{ + /* + * Version is passed in as a -D flag when this file is + * compiled. + */ + return GTK_VERSION; +} + +static void +_listener_info_destroy (gpointer data) +{ + g_free(data); +} + +static guint +add_listener (GSignalEmissionHook listener, + const gchar *object_type, + const gchar *signal, + const gchar *hook_data) +{ + GType type; + guint signal_id; + gint rc = 0; + + type = g_type_from_name (object_type); + if (type) + { + signal_id = g_signal_lookup (signal, type); + if (signal_id > 0) + { + GailUtilListenerInfo *listener_info; + + rc = listener_idx; + + listener_info = g_malloc(sizeof(GailUtilListenerInfo)); + listener_info->key = listener_idx; + listener_info->hook_id = + g_signal_add_emission_hook (signal_id, 0, listener, + g_strdup (hook_data), + (GDestroyNotify) g_free); + listener_info->signal_id = signal_id; + + g_hash_table_insert(listener_list, &(listener_info->key), listener_info); + listener_idx++; + } + else + { + g_warning("Invalid signal type %s\n", signal); + } + } + else + { + g_warning("Invalid object type %s\n", object_type); + } + return rc; +} + +static void +do_window_event_initialization (void) +{ + AtkObject *root; + + /* + * Ensure that GailWindowClass exists. + */ + g_type_class_ref (GAIL_TYPE_WINDOW); + g_signal_add_emission_hook (g_signal_lookup ("window-state-event", GTK_TYPE_WIDGET), + 0, state_event_watcher, NULL, (GDestroyNotify) NULL); + g_signal_add_emission_hook (g_signal_lookup ("configure-event", GTK_TYPE_WIDGET), + 0, configure_event_watcher, NULL, (GDestroyNotify) NULL); + + root = atk_get_root (); + g_signal_connect (root, "children-changed::add", + (GCallback) window_added, NULL); + g_signal_connect (root, "children-changed::remove", + (GCallback) window_removed, NULL); +} + +static gboolean +state_event_watcher (GSignalInvocationHint *hint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + AtkObject *atk_obj; + AtkObject *parent; + GdkEventWindowState *event; + gchar *signal_name; + guint signal_id; + + object = g_value_get_object (param_values + 0); + /* + * The object can be a GtkMenu when it is popped up; we ignore this + */ + if (!GTK_IS_WINDOW (object)) + return FALSE; + + event = g_value_get_boxed (param_values + 1); + gail_return_val_if_fail (event->type == GDK_WINDOW_STATE, FALSE); + widget = GTK_WIDGET (object); + + if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) + { + signal_name = "maximize"; + } + else if (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) + { + signal_name = "minimize"; + } + else if (event->new_window_state == 0) + { + signal_name = "restore"; + } + else + return TRUE; + + atk_obj = gtk_widget_get_accessible (widget); + + if (GAIL_IS_WINDOW (atk_obj)) + { + parent = atk_object_get_parent (atk_obj); + if (parent == atk_get_root ()) + { + signal_id = g_signal_lookup (signal_name, GAIL_TYPE_WINDOW); + g_signal_emit (atk_obj, signal_id, 0); + } + + return TRUE; + } + else + { + return FALSE; + } +} + +static void +window_added (AtkObject *atk_obj, + guint index, + AtkObject *child) +{ + GtkWidget *widget; + + if (!GAIL_IS_WINDOW (child)) return; + + widget = GTK_ACCESSIBLE (child)->widget; + gail_return_if_fail (widget); + + g_signal_connect (widget, "focus-in-event", + (GCallback) window_focus, NULL); + g_signal_connect (widget, "focus-out-event", + (GCallback) window_focus, NULL); + g_signal_emit (child, g_signal_lookup ("create", GAIL_TYPE_WINDOW), 0); +} + + +static void +window_removed (AtkObject *atk_obj, + guint index, + AtkObject *child) +{ + GtkWidget *widget; + GtkWindow *window; + + if (!GAIL_IS_WINDOW (child)) return; + + widget = GTK_ACCESSIBLE (child)->widget; + gail_return_if_fail (widget); + + window = GTK_WINDOW (widget); + /* + * Deactivate window if it is still focused and we are removing it. This + * can happen when a dialog displayed by gok is removed. + */ + if (window->is_active && + window->has_toplevel_focus) + { + gchar *signal_name; + AtkObject *atk_obj; + + atk_obj = gtk_widget_get_accessible (widget); + signal_name = "deactivate"; + g_signal_emit (atk_obj, g_signal_lookup (signal_name, GAIL_TYPE_WINDOW), 0); + } + + g_signal_handlers_disconnect_by_func (widget, (gpointer) window_focus, NULL); + g_signal_emit (child, g_signal_lookup ("destroy", GAIL_TYPE_WINDOW), 0); +} + +static gboolean +window_focus (GtkWidget *widget, + GdkEventFocus *event) +{ + gchar *signal_name; + AtkObject *atk_obj; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + atk_obj = gtk_widget_get_accessible (widget); + signal_name = (event->in) ? "activate" : "deactivate"; + g_signal_emit (atk_obj, g_signal_lookup (signal_name, GAIL_TYPE_WINDOW), 0); + + return FALSE; +} + +static gboolean +configure_event_watcher (GSignalInvocationHint *hint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GObject *object; + GtkWidget *widget; + AtkObject *atk_obj; + AtkObject *parent; + GdkEvent *event; + gchar *signal_name; + guint signal_id; + + object = g_value_get_object (param_values + 0); + if (!GTK_IS_WINDOW (object)) + /* + * GtkDrawingArea can send a GDK_CONFIGURE event but we ignore here + */ + return FALSE; + + event = g_value_get_boxed (param_values + 1); + if (event->type != GDK_CONFIGURE) + return FALSE; + if (GTK_WINDOW (object)->configure_request_count) + /* + * There is another ConfigureRequest pending so we ignore this one. + */ + return TRUE; + widget = GTK_WIDGET (object); + if (widget->allocation.x == ((GdkEventConfigure *)event)->x && + widget->allocation.y == ((GdkEventConfigure *)event)->y && + widget->allocation.width == ((GdkEventConfigure *)event)->width && + widget->allocation.height == ((GdkEventConfigure *)event)->height) + return TRUE; + + if (widget->allocation.width != ((GdkEventConfigure *)event)->width || + widget->allocation.height != ((GdkEventConfigure *)event)->height) + { + signal_name = "resize"; + } + else + { + signal_name = "move"; + } + + atk_obj = gtk_widget_get_accessible (widget); + if (GAIL_IS_WINDOW (atk_obj)) + { + parent = atk_object_get_parent (atk_obj); + if (parent == atk_get_root ()) + { + signal_id = g_signal_lookup (signal_name, GAIL_TYPE_WINDOW); + g_signal_emit (atk_obj, signal_id, 0); + } + + return TRUE; + } + else + { + return FALSE; + } +} + +GType +gail_misc_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailMiscClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_misc_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailMisc), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + type = g_type_register_static (ATK_TYPE_MISC, + "GailMisc", &tinfo, 0); + } + return type; +} + +static void +gail_misc_class_init (GailMiscClass *klass) +{ + AtkMiscClass *miscclass = ATK_MISC_CLASS (klass); + miscclass->threads_enter = + gail_misc_threads_enter; + miscclass->threads_leave = + gail_misc_threads_leave; + atk_misc_instance = g_object_new (GAIL_TYPE_MISC, NULL); +} + +static void gail_misc_threads_enter (AtkMisc *misc) +{ + GDK_THREADS_ENTER (); +} + +static void gail_misc_threads_leave (AtkMisc *misc) +{ + GDK_THREADS_LEAVE (); +} diff --git a/modules/other/gail/gailutil.h b/modules/other/gail/gailutil.h new file mode 100644 index 000000000..4b968a62c --- /dev/null +++ b/modules/other/gail/gailutil.h @@ -0,0 +1,80 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_UTIL_H__ +#define __GAIL_UTIL_H__ + +#include <atk/atk.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_UTIL (gail_util_get_type ()) +#define GAIL_UTIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_UTIL, GailUtil)) +#define GAIL_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_UTIL, GailUtilClass)) +#define GAIL_IS_UTIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_UTIL)) +#define GAIL_IS_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_UTIL)) +#define GAIL_UTIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_UTIL, GailUtilClass)) + +typedef struct _GailUtil GailUtil; +typedef struct _GailUtilClass GailUtilClass; + +struct _GailUtil +{ + AtkUtil parent; + GList *listener_list; +}; + +GType gail_util_get_type (void); + +struct _GailUtilClass +{ + AtkUtilClass parent_class; +}; + +#define GAIL_TYPE_MISC (gail_misc_get_type ()) +#define GAIL_MISC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_MISC, GailMisc)) +#define GAIL_MISC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_MISC, GailMiscClass)) +#define GAIL_IS_MISC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_MISC)) +#define GAIL_IS_MISC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_MISC)) +#define GAIL_MISC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_MISC, GailMiscClass)) + +typedef struct _GailMisc GailMisc; +typedef struct _GailMiscClass GailMiscClass; + +struct _GailMisc +{ + AtkMisc parent; +}; + +GType gail_misc_get_type (void); + +struct _GailMiscClass +{ + AtkMiscClass parent_class; +}; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_UTIL_H__ */ diff --git a/modules/other/gail/gailwidget.c b/modules/other/gail/gailwidget.c new file mode 100644 index 000000000..728347913 --- /dev/null +++ b/modules/other/gail/gailwidget.c @@ -0,0 +1,1111 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#ifdef GDK_WINDOWING_X11 +#include <gdk/x11/gdkx.h> +#endif +#include "gailwidget.h" +#include "gailnotebookpage.h" +#include "gail-private-macros.h" + +extern GtkWidget *focus_widget; + +static void gail_widget_class_init (GailWidgetClass *klass); + +static void gail_widget_connect_widget_destroyed (GtkAccessible *accessible); +static void gail_widget_destroyed (GtkWidget *widget, + GtkAccessible *accessible); + +static G_CONST_RETURN gchar* gail_widget_get_description (AtkObject *accessible); +static AtkObject* gail_widget_get_parent (AtkObject *accessible); +static AtkStateSet* gail_widget_ref_state_set (AtkObject *accessible); +static AtkRelationSet* gail_widget_ref_relation_set (AtkObject *accessible); +static gint gail_widget_get_index_in_parent (AtkObject *accessible); + +static void atk_component_interface_init (AtkComponentIface *iface); + +static guint gail_widget_add_focus_handler + (AtkComponent *component, + AtkFocusHandler handler); + +static void gail_widget_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); + +static void gail_widget_get_size (AtkComponent *component, + gint *width, + gint *height); + +static AtkLayer gail_widget_get_layer (AtkComponent *component); + +static gboolean gail_widget_grab_focus (AtkComponent *component); + + +static void gail_widget_remove_focus_handler + (AtkComponent *component, + guint handler_id); + +static gboolean gail_widget_set_extents (AtkComponent *component, + gint x, + gint y, + gint width, + gint height, + AtkCoordType coord_type); + +static gboolean gail_widget_set_position (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type); + +static gboolean gail_widget_set_size (AtkComponent *component, + gint width, + gint height); + +static gint gail_widget_map_gtk (GtkWidget *widget); +static void gail_widget_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static void gail_widget_notify_gtk (GObject *obj, + GParamSpec *pspec); +static gboolean gail_widget_focus_gtk (GtkWidget *widget, + GdkEventFocus *event); +static gboolean gail_widget_real_focus_gtk (GtkWidget *widget, + GdkEventFocus *event); +static void gail_widget_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation); + +static void gail_widget_focus_event (AtkObject *obj, + gboolean focus_in); + +static void gail_widget_real_initialize (AtkObject *obj, + gpointer data); +static GtkWidget* gail_widget_find_viewport (GtkWidget *widget); +static gboolean gail_widget_on_screen (GtkWidget *widget); + +static gpointer parent_class = NULL; + +GType +gail_widget_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailWidgetClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_widget_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailWidget), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) atk_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + + type = g_type_register_static (GTK_TYPE_ACCESSIBLE, + "GailWidget", &tinfo, 0); + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + } + + return type; +} + +static void +gail_widget_class_init (GailWidgetClass *klass) +{ + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + GtkAccessibleClass *accessible_class = GTK_ACCESSIBLE_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + klass->notify_gtk = gail_widget_real_notify_gtk; + klass->focus_gtk = gail_widget_real_focus_gtk; + + accessible_class->connect_widget_destroyed = gail_widget_connect_widget_destroyed; + + class->get_description = gail_widget_get_description; + class->get_parent = gail_widget_get_parent; + class->ref_relation_set = gail_widget_ref_relation_set; + class->ref_state_set = gail_widget_ref_state_set; + class->get_index_in_parent = gail_widget_get_index_in_parent; + class->initialize = gail_widget_real_initialize; +} + +/** + * This function specifies the GtkWidget for which the GailWidget was created + * and specifies a handler to be called when the GtkWidget is destroyed. + **/ +static void +gail_widget_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkAccessible *accessible; + GtkWidget *widget; + + g_return_if_fail (GTK_IS_WIDGET (data)); + + widget = GTK_WIDGET (data); + + accessible = GTK_ACCESSIBLE (obj); + accessible->widget = widget; + gtk_accessible_connect_widget_destroyed (accessible); + g_signal_connect_after (widget, + "focus-in-event", + G_CALLBACK (gail_widget_focus_gtk), + NULL); + g_signal_connect_after (widget, + "focus-out-event", + G_CALLBACK (gail_widget_focus_gtk), + NULL); + g_signal_connect (widget, + "notify", + G_CALLBACK (gail_widget_notify_gtk), + NULL); + g_signal_connect (widget, + "size_allocate", + G_CALLBACK (gail_widget_size_allocate_gtk), + NULL); + atk_component_add_focus_handler (ATK_COMPONENT (accessible), + gail_widget_focus_event); + /* + * Add signal handlers for GTK signals required to support property changes + */ + g_signal_connect (widget, + "map", + G_CALLBACK (gail_widget_map_gtk), + NULL); + g_signal_connect (widget, + "unmap", + G_CALLBACK (gail_widget_map_gtk), + NULL); + g_object_set_data (G_OBJECT (obj), "atk-component-layer", + GINT_TO_POINTER (ATK_LAYER_WIDGET)); + + obj->role = ATK_ROLE_UNKNOWN; +} + +AtkObject* +gail_widget_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + object = g_object_new (GAIL_TYPE_WIDGET, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + return accessible; +} + +/* + * This function specifies the function to be called when the widget + * is destroyed + */ +static void +gail_widget_connect_widget_destroyed (GtkAccessible *accessible) +{ + if (accessible->widget) + { + g_signal_connect_after (accessible->widget, + "destroy", + G_CALLBACK (gail_widget_destroyed), + accessible); + } +} + +/* + * This function is called when the widget is destroyed. + * It sets the widget field in the GtkAccessible structure to NULL + * and emits a state-change signal for the state ATK_STATE_DEFUNCT + */ +static void +gail_widget_destroyed (GtkWidget *widget, + GtkAccessible *accessible) +{ + accessible->widget = NULL; + atk_object_notify_state_change (ATK_OBJECT (accessible), ATK_STATE_DEFUNCT, + TRUE); +} + +static G_CONST_RETURN gchar* +gail_widget_get_description (AtkObject *accessible) +{ + if (accessible->description) + return accessible->description; + else + { + /* Get the tooltip from the widget */ + GtkAccessible *obj = GTK_ACCESSIBLE (accessible); + GtkTooltipsData *data; + + gail_return_val_if_fail (obj, NULL); + + if (obj->widget == NULL) + /* + * Object is defunct + */ + return NULL; + + gail_return_val_if_fail (GTK_WIDGET (obj->widget), NULL); + + data = gtk_tooltips_data_get (obj->widget); + if (data == NULL) + return NULL; + + return data->tip_text; + } +} + +static AtkObject* +gail_widget_get_parent (AtkObject *accessible) +{ + AtkObject *parent; + + parent = accessible->accessible_parent; + + if (parent != NULL) + g_return_val_if_fail (ATK_IS_OBJECT (parent), NULL); + else + { + GtkWidget *widget, *parent_widget; + + widget = GTK_ACCESSIBLE (accessible)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + gail_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + parent_widget = widget->parent; + if (parent_widget == NULL) + return NULL; + + /* + * For a widget whose parent is a GtkNoteBook, we return the + * accessible object corresponding the GtkNotebookPage containing + * the widget as the accessible parent. + */ + if (GTK_IS_NOTEBOOK (parent_widget)) + { + gint page_num; + GtkWidget *child; + GtkNotebook *notebook; + + page_num = 0; + notebook = GTK_NOTEBOOK (parent_widget); + while (TRUE) + { + child = gtk_notebook_get_nth_page (notebook, page_num); + if (!child) + break; + if (child == widget) + { + parent = gtk_widget_get_accessible (parent_widget); + parent = atk_object_ref_accessible_child (parent, page_num); + g_object_unref (parent); + return parent; + } + page_num++; + } + } + + parent = gtk_widget_get_accessible (parent_widget); + } + return parent; +} + +static GtkWidget* +find_label (GtkWidget *widget) +{ + GList *labels; + GtkWidget *label; + GtkWidget *temp_widget; + + labels = gtk_widget_list_mnemonic_labels (widget); + label = NULL; + if (labels) + { + if (labels->data) + { + if (labels->next) + { + g_warning ("Widget (%s) has more than one label", G_OBJECT_TYPE_NAME (widget)); + + } + else + { + label = labels->data; + } + } + g_list_free (labels); + } + + /* + * Ignore a label within a button; bug #136602 + */ + if (label && GTK_IS_BUTTON (widget)) + { + temp_widget = label; + while (temp_widget) + { + if (temp_widget == widget) + { + label = NULL; + break; + } + temp_widget = gtk_widget_get_parent (temp_widget); + } + } + return label; +} + +static AtkRelationSet* +gail_widget_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + GtkWidget *label; + AtkObject *array[1]; + AtkRelation* relation; + + gail_return_val_if_fail (GAIL_IS_WIDGET (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + if (GTK_IS_BOX (widget) && !GTK_IS_COMBO (widget)) + /* + * Do not report labelled-by for a GtkBox which could be a + * GnomeFileEntry. + */ + return relation_set; + + if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABELLED_BY)) + { + label = find_label (widget); + if (label == NULL) + { + if (GTK_IS_BUTTON (widget)) + /* + * Handle the case where GnomeIconEntry is the mnemonic widget. + * The GtkButton which is a grandchild of the GnomeIconEntry + * should really be the mnemonic widget. See bug #133967. + */ + { + GtkWidget *temp_widget; + + temp_widget = gtk_widget_get_parent (widget); + + if (GTK_IS_ALIGNMENT (temp_widget)) + { + temp_widget = gtk_widget_get_parent (temp_widget); + if (GTK_IS_BOX (temp_widget)) + { + label = find_label (temp_widget); + + if (!label) + label = find_label (gtk_widget_get_parent (temp_widget)); + } + } + } + else if (GTK_IS_COMBO (widget)) + /* + * Handle the case when GnomeFileEntry is the mnemonic widget. + * The GnomeEntry which is a grandchild of the GnomeFileEntry + * should be the mnemonic widget. See bug #137584. + */ + { + GtkWidget *temp_widget; + + temp_widget = gtk_widget_get_parent (widget); + + if (GTK_IS_HBOX (temp_widget)) + { + temp_widget = gtk_widget_get_parent (temp_widget); + if (GTK_IS_BOX (temp_widget)) + { + label = find_label (temp_widget); + } + } + } + else if (GTK_IS_COMBO_BOX (widget)) + /* + * Handle the case when GtkFileChooserButton is the mnemonic + * widget. The GtkComboBox which is a child of the + * GtkFileChooserButton should be the mnemonic widget. + * See bug #359843. + */ + { + GtkWidget *temp_widget; + + temp_widget = gtk_widget_get_parent (widget); + if (GTK_IS_HBOX (temp_widget)) + { + label = find_label (temp_widget); + } + } + } + + if (label) + { + array [0] = gtk_widget_get_accessible (label); + + relation = atk_relation_new (array, 1, ATK_RELATION_LABELLED_BY); + atk_relation_set_add (relation_set, relation); + g_object_unref (relation); + } + } + + return relation_set; +} + +static AtkStateSet* +gail_widget_ref_state_set (AtkObject *accessible) +{ + GtkWidget *widget = GTK_ACCESSIBLE (accessible)->widget; + AtkStateSet *state_set; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + + if (widget == NULL) + { + atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); + } + else + { + if (GTK_WIDGET_IS_SENSITIVE (widget)) + { + atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); + atk_state_set_add_state (state_set, ATK_STATE_ENABLED); + } + + if (GTK_WIDGET_CAN_FOCUS (widget)) + { + atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); + } + /* + * We do not currently generate notifications when an ATK object + * corresponding to a GtkWidget changes visibility by being scrolled + * on or off the screen. The testcase for this is the main window + * of the testgtk application in which a set of buttons in a GtkVBox + * is in a scrooled window with a viewport. + * + * To generate the notifications we would need to do the following: + * 1) Find the GtkViewPort among the antecendents of the objects + * 2) Create an accesible for the GtkViewPort + * 3) Connect to the value-changed signal on the viewport + * 4) When the signal is received we need to traverse the children + * of the viewport and check whether the children are visible or not + * visible; we may want to restrict this to the widgets for which + * accessible objects have been created. + * 5) We probably need to store a variable on_screen in the + * GailWidget data structure so we can determine whether the value has + * changed. + */ + if (GTK_WIDGET_VISIBLE (widget)) + { + atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); + if (gail_widget_on_screen (widget) && + GTK_WIDGET_MAPPED (widget)) + { + atk_state_set_add_state (state_set, ATK_STATE_SHOWING); + } + } + + if (GTK_WIDGET_HAS_FOCUS (widget) && (widget == focus_widget)) + { + AtkObject *focus_obj; + + focus_obj = g_object_get_data (G_OBJECT (accessible), "gail-focus-object"); + if (focus_obj == NULL) + atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); + } + if (GTK_WIDGET_HAS_DEFAULT(widget)) + { + atk_state_set_add_state (state_set, ATK_STATE_DEFAULT); + } + } + return state_set; +} + +static gint +gail_widget_get_index_in_parent (AtkObject *accessible) +{ + GtkWidget *widget; + GtkWidget *parent_widget; + gint index; + GList *children; + GType type; + + type = g_type_from_name ("GailCanvasWidget"); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return -1; + + if (accessible->accessible_parent) + { + AtkObject *parent; + + parent = accessible->accessible_parent; + + if (GAIL_IS_NOTEBOOK_PAGE (parent) || + G_TYPE_CHECK_INSTANCE_TYPE ((parent), type)) + return 0; + else + { + gint n_children, i; + gboolean found = FALSE; + + n_children = atk_object_get_n_accessible_children (parent); + for (i = 0; i < n_children; i++) + { + AtkObject *child; + + child = atk_object_ref_accessible_child (parent, i); + if (child == accessible) + found = TRUE; + + g_object_unref (child); + if (found) + return i; + } + } + } + + gail_return_val_if_fail (GTK_IS_WIDGET (widget), -1); + parent_widget = widget->parent; + if (parent_widget == NULL) + return -1; + gail_return_val_if_fail (GTK_IS_CONTAINER (parent_widget), -1); + + children = gtk_container_get_children (GTK_CONTAINER (parent_widget)); + + index = g_list_index (children, widget); + g_list_free (children); + return index; +} + +static void +atk_component_interface_init (AtkComponentIface *iface) +{ + g_return_if_fail (iface != NULL); + + /* + * Use default implementation for contains and get_position + */ + iface->add_focus_handler = gail_widget_add_focus_handler; + iface->get_extents = gail_widget_get_extents; + iface->get_size = gail_widget_get_size; + iface->get_layer = gail_widget_get_layer; + iface->grab_focus = gail_widget_grab_focus; + iface->remove_focus_handler = gail_widget_remove_focus_handler; + iface->set_extents = gail_widget_set_extents; + iface->set_position = gail_widget_set_position; + iface->set_size = gail_widget_set_size; +} + +static guint +gail_widget_add_focus_handler (AtkComponent *component, + AtkFocusHandler handler) +{ + GSignalMatchType match_type; + gulong ret; + guint signal_id; + + match_type = 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 +gail_widget_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GdkWindow *window; + gint x_window, y_window; + gint x_toplevel, y_toplevel; + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * Object is defunct + */ + return; + + gail_return_if_fail (GTK_IS_WIDGET (widget)); + + *width = widget->allocation.width; + *height = widget->allocation.height; + if (!gail_widget_on_screen (widget) || (!GTK_WIDGET_DRAWABLE (widget))) + { + *x = G_MININT; + *y = G_MININT; + return; + } + + if (widget->parent) + { + *x = widget->allocation.x; + *y = widget->allocation.y; + window = gtk_widget_get_parent_window (widget); + } + else + { + *x = 0; + *y = 0; + window = widget->window; + } + gdk_window_get_origin (window, &x_window, &y_window); + *x += x_window; + *y += y_window; + + + if (coord_type == ATK_XY_WINDOW) + { + window = gdk_window_get_toplevel (widget->window); + gdk_window_get_origin (window, &x_toplevel, &y_toplevel); + + *x -= x_toplevel; + *y -= y_toplevel; + } +} + +static void +gail_widget_get_size (AtkComponent *component, + gint *width, + gint *height) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * Object is defunct + */ + return; + + gail_return_if_fail (GTK_IS_WIDGET (widget)); + + *width = widget->allocation.width; + *height = widget->allocation.height; +} + +static AtkLayer +gail_widget_get_layer (AtkComponent *component) +{ + gint layer; + layer = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (component), "atk-component-layer")); + + return (AtkLayer) layer; +} + +static gboolean +gail_widget_grab_focus (AtkComponent *component) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + GtkWidget *toplevel; + + gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + if (GTK_WIDGET_CAN_FOCUS (widget)) + { + gtk_widget_grab_focus (widget); + toplevel = gtk_widget_get_toplevel (widget); + if (GTK_WIDGET_TOPLEVEL (toplevel)) + { +#ifdef GDK_WINDOWING_X11 + gtk_window_present_with_time (GTK_WINDOW (toplevel), gdk_x11_get_server_time (widget->window)); +#else + gtk_window_present (GTK_WINDOW (toplevel)); +#endif + } + return TRUE; + } + else + return FALSE; +} + +static void +gail_widget_remove_focus_handler (AtkComponent *component, + guint handler_id) +{ + g_signal_handler_disconnect (component, handler_id); +} + +static gboolean +gail_widget_set_extents (AtkComponent *component, + gint x, + gint y, + gint width, + gint height, + AtkCoordType coord_type) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * Object is defunct + */ + return FALSE; + gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + if (GTK_WIDGET_TOPLEVEL (widget)) + { + if (coord_type == ATK_XY_WINDOW) + { + gint x_current, y_current; + GdkWindow *window = widget->window; + + gdk_window_get_origin (window, &x_current, &y_current); + x_current += x; + y_current += y; + if (x_current < 0 || y_current < 0) + return FALSE; + else + { + gtk_widget_set_uposition (widget, x_current, y_current); + gtk_widget_set_usize (widget, width, height); + return TRUE; + } + } + else if (coord_type == ATK_XY_SCREEN) + { + gtk_widget_set_uposition (widget, x, y); + gtk_widget_set_usize (widget, width, height); + return TRUE; + } + } + return FALSE; +} + +static gboolean +gail_widget_set_position (AtkComponent *component, + gint x, + gint y, + AtkCoordType coord_type) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * Object is defunct + */ + return FALSE; + gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + if (GTK_WIDGET_TOPLEVEL (widget)) + { + if (coord_type == ATK_XY_WINDOW) + { + gint x_current, y_current; + GdkWindow *window = widget->window; + + gdk_window_get_origin (window, &x_current, &y_current); + x_current += x; + y_current += y; + if (x_current < 0 || y_current < 0) + return FALSE; + else + { + gtk_widget_set_uposition (widget, x_current, y_current); + return TRUE; + } + } + else if (coord_type == ATK_XY_SCREEN) + { + gtk_widget_set_uposition (widget, x, y); + return TRUE; + } + } + return FALSE; +} + +static gboolean +gail_widget_set_size (AtkComponent *component, + gint width, + gint height) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * Object is defunct + */ + return FALSE; + gail_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + if (GTK_WIDGET_TOPLEVEL (widget)) + { + gtk_widget_set_usize (widget, width, height); + return TRUE; + } + else + return FALSE; +} + +/* + * This function is a signal handler for notify_in_event and focus_out_event + * signal which gets emitted on a GtkWidget. + */ +static gboolean +gail_widget_focus_gtk (GtkWidget *widget, + GdkEventFocus *event) +{ + GailWidget *gail_widget; + GailWidgetClass *klass; + + gail_widget = GAIL_WIDGET (gtk_widget_get_accessible (widget)); + klass = GAIL_WIDGET_GET_CLASS (gail_widget); + if (klass->focus_gtk) + return klass->focus_gtk (widget, event); + else + return FALSE; +} + +/* + * This function is the signal handler defined for focus_in_event and + * focus_out_event got GailWidget. + * + * It emits a focus-event signal on the GailWidget. + */ +static gboolean +gail_widget_real_focus_gtk (GtkWidget *widget, + GdkEventFocus *event) +{ + AtkObject* accessible; + gboolean return_val; + return_val = FALSE; + + accessible = gtk_widget_get_accessible (widget); + g_signal_emit_by_name (accessible, "focus_event", event->in, &return_val); + return FALSE; +} + +static void +gail_widget_size_allocate_gtk (GtkWidget *widget, + GtkAllocation *allocation) +{ + AtkObject* accessible; + AtkRectangle rect; + + accessible = gtk_widget_get_accessible (widget); + if (ATK_IS_COMPONENT (accessible)) + { + rect.x = allocation->x; + rect.y = allocation->y; + rect.width = allocation->width; + rect.height = allocation->height; + g_signal_emit_by_name (accessible, "bounds_changed", &rect); + } +} + +/* + * This function is the signal handler defined for map and unmap signals. + */ +static gint +gail_widget_map_gtk (GtkWidget *widget) +{ + AtkObject* accessible; + + accessible = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (accessible, ATK_STATE_SHOWING, + GTK_WIDGET_MAPPED (widget)); + return 1; +} + +/* + * This function is a signal handler for notify signal which gets emitted + * when a property changes value on the GtkWidget associated with the object. + * + * It calls a function for the GailWidget type + */ +static void +gail_widget_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GailWidget *widget; + GailWidgetClass *klass; + + widget = GAIL_WIDGET (gtk_widget_get_accessible (GTK_WIDGET (obj))); + klass = GAIL_WIDGET_GET_CLASS (widget); + if (klass->notify_gtk) + klass->notify_gtk (obj, pspec); +} + +/* + * This function is a signal handler for notify signal which gets emitted + * when a property changes value on the GtkWidget associated with a GailWidget. + * + * It constructs an AtkPropertyValues structure and emits a "property_changed" + * signal which causes the user specified AtkPropertyChangeHandler + * to be called. + */ +static void +gail_widget_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget* widget = GTK_WIDGET (obj); + AtkObject* atk_obj = gtk_widget_get_accessible (widget); + AtkState state; + gboolean value; + + if (strcmp (pspec->name, "has-focus") == 0) + /* + * We use focus-in-event and focus-out-event signals to catch + * focus changes so we ignore this. + */ + return; + else if (strcmp (pspec->name, "visible") == 0) + { + state = ATK_STATE_VISIBLE; + value = GTK_WIDGET_VISIBLE (widget); + } + else if (strcmp (pspec->name, "sensitive") == 0) + { + state = ATK_STATE_SENSITIVE; + value = GTK_WIDGET_SENSITIVE (widget); + } + else + return; + + atk_object_notify_state_change (atk_obj, state, value); +} + +static void +gail_widget_focus_event (AtkObject *obj, + gboolean focus_in) +{ + AtkObject *focus_obj; + + focus_obj = g_object_get_data (G_OBJECT (obj), "gail-focus-object"); + if (focus_obj == NULL) + focus_obj = obj; + atk_object_notify_state_change (focus_obj, ATK_STATE_FOCUSED, focus_in); +} + +static GtkWidget* +gail_widget_find_viewport (GtkWidget *widget) +{ + /* + * Find an antecedent which is a GtkViewPort + */ + GtkWidget *parent; + + parent = widget->parent; + while (parent != NULL) + { + if (GTK_IS_VIEWPORT (parent)) + break; + parent = parent->parent; + } + return parent; +} + +/* + * This function checks whether the widget has an antecedent which is + * a GtkViewport and, if so, whether any part of the widget intersects + * the visible rectangle of the GtkViewport. + */ +static gboolean gail_widget_on_screen (GtkWidget *widget) +{ + GtkWidget *viewport; + gboolean return_value; + + viewport = gail_widget_find_viewport (widget); + if (viewport) + { + GtkAdjustment *adjustment; + GdkRectangle visible_rect; + + adjustment = gtk_viewport_get_vadjustment (GTK_VIEWPORT (viewport)); + visible_rect.y = adjustment->value; + adjustment = gtk_viewport_get_hadjustment (GTK_VIEWPORT (viewport)); + visible_rect.x = adjustment->value; + visible_rect.width = viewport->allocation.width; + visible_rect.height = viewport->allocation.height; + + if (((widget->allocation.x + widget->allocation.width) < visible_rect.x) || + ((widget->allocation.y + widget->allocation.height) < visible_rect.y) || + (widget->allocation.x > (visible_rect.x + visible_rect.width)) || + (widget->allocation.y > (visible_rect.y + visible_rect.height))) + return_value = FALSE; + else + return_value = TRUE; + } + else + { + /* + * Check whether the widget has been placed of the screen. The + * widget may be MAPPED as when toolbar items do not fit on the toolbar. + */ + if (widget->allocation.x + widget->allocation.width <= 0 && + widget->allocation.y + widget->allocation.height <= 0) + return_value = FALSE; + else + return_value = TRUE; + } + + return return_value; +} diff --git a/modules/other/gail/gailwidget.h b/modules/other/gail/gailwidget.h new file mode 100644 index 000000000..04fdae050 --- /dev/null +++ b/modules/other/gail/gailwidget.h @@ -0,0 +1,71 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_WIDGET_H__ +#define __GAIL_WIDGET_H__ + +#include <gtk/gtkaccessible.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_WIDGET (gail_widget_get_type ()) +#define GAIL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_WIDGET, GailWidget)) +#define GAIL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_WIDGET, GailWidgetClass)) +#define GAIL_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_WIDGET)) +#define GAIL_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_WIDGET)) +#define GAIL_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_WIDGET, GailWidgetClass)) + +typedef struct _GailWidget GailWidget; +typedef struct _GailWidgetClass GailWidgetClass; + +struct _GailWidget +{ + GtkAccessible parent; +}; + +GType gail_widget_get_type (void); + +struct _GailWidgetClass +{ + GtkAccessibleClass parent_class; + + /* + * Signal handler for notify signal on GTK widget + */ + void (*notify_gtk) (GObject *object, + GParamSpec *pspec); + /* + * Signal handler for focus_in_event and focus_out_event signal on GTK widget + */ + gboolean (*focus_gtk) (GtkWidget *widget, + GdkEventFocus *event); + +}; + +AtkObject* gail_widget_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_WIDGET_H__ */ diff --git a/modules/other/gail/gailwindow.c b/modules/other/gail/gailwindow.c new file mode 100644 index 000000000..c6c9fd050 --- /dev/null +++ b/modules/other/gail/gailwindow.c @@ -0,0 +1,1094 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001, 2002, 2003 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <gtk/gtk.h> +#include "gailwindow.h" +#include "gailtoplevel.h" +#include "gail-private-macros.h" + +enum { + ACTIVATE, + CREATE, + DEACTIVATE, + DESTROY, + MAXIMIZE, + MINIMIZE, + MOVE, + RESIZE, + RESTORE, + LAST_SIGNAL +}; + +static void gail_window_class_init (GailWindowClass *klass); + +static void gail_window_real_initialize (AtkObject *obj, + gpointer data); +static void gail_window_finalize (GObject *object); + +static G_CONST_RETURN gchar* gail_window_get_name (AtkObject *accessible); + +static AtkObject* gail_window_get_parent (AtkObject *accessible); +static gint gail_window_get_index_in_parent (AtkObject *accessible); +static gboolean gail_window_real_focus_gtk (GtkWidget *widget, + GdkEventFocus *event); + +static AtkStateSet* gail_window_ref_state_set (AtkObject *accessible); +static AtkRelationSet* gail_window_ref_relation_set (AtkObject *accessible); +static void gail_window_real_notify_gtk (GObject *obj, + GParamSpec *pspec); +static gint gail_window_get_mdi_zorder (AtkComponent *component); + +static gboolean gail_window_state_event_gtk (GtkWidget *widget, + GdkEventWindowState *event); + +/* atkcomponent.h */ +static void atk_component_interface_init (AtkComponentIface *iface); + +static void gail_window_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type); +static void gail_window_get_size (AtkComponent *component, + gint *width, + gint *height); + +static guint gail_window_signals [LAST_SIGNAL] = { 0, }; + +static gpointer parent_class = NULL; + +GType +gail_window_get_type (void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailWindowClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_window_class_init, /* class init */ + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof (GailWindow), /* instance size */ + 0, /* nb preallocs */ + (GInstanceInitFunc) NULL, /* instance init */ + NULL /* value table */ + }; + + static const GInterfaceInfo atk_component_info = + { + (GInterfaceInitFunc) atk_component_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + type = g_type_register_static (GAIL_TYPE_CONTAINER, + "GailWindow", &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_COMPONENT, + &atk_component_info); + } + + return type; +} + +static void +gail_window_class_init (GailWindowClass *klass) +{ + GailWidgetClass *widget_class; + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + AtkObjectClass *class = ATK_OBJECT_CLASS (klass); + + gobject_class->finalize = gail_window_finalize; + + widget_class = (GailWidgetClass*)klass; + widget_class->focus_gtk = gail_window_real_focus_gtk; + widget_class->notify_gtk = gail_window_real_notify_gtk; + + parent_class = g_type_class_peek_parent (klass); + + class->get_name = gail_window_get_name; + class->get_parent = gail_window_get_parent; + class->get_index_in_parent = gail_window_get_index_in_parent; + class->ref_relation_set = gail_window_ref_relation_set; + class->ref_state_set = gail_window_ref_state_set; + class->initialize = gail_window_real_initialize; + + gail_window_signals [ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [CREATE] = + g_signal_new ("create", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [DEACTIVATE] = + g_signal_new ("deactivate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [DESTROY] = + g_signal_new ("destroy", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [MAXIMIZE] = + g_signal_new ("maximize", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [MINIMIZE] = + g_signal_new ("minimize", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [MOVE] = + g_signal_new ("move", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [RESIZE] = + g_signal_new ("resize", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + gail_window_signals [RESTORE] = + g_signal_new ("restore", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, /* default signal handler */ + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +AtkObject* +gail_window_new (GtkWidget *widget) +{ + GObject *object; + AtkObject *accessible; + + gail_return_val_if_fail (widget != NULL, NULL); + /* + * A GailWindow can be created for a GtkHandleBox or a GtkWindow + */ + if (!GTK_IS_WINDOW (widget) && + !GTK_IS_HANDLE_BOX (widget)) + gail_return_val_if_fail (FALSE, NULL); + + object = g_object_new (GAIL_TYPE_WINDOW, NULL); + + accessible = ATK_OBJECT (object); + atk_object_initialize (accessible, widget); + + /* + * Notify that tooltip is showing + */ + if (accessible->role == ATK_ROLE_TOOL_TIP && + GTK_WIDGET_MAPPED (widget)) + atk_object_notify_state_change (accessible, ATK_STATE_SHOWING, 1); + + return accessible; +} + +static void +gail_window_real_initialize (AtkObject *obj, + gpointer data) +{ + GtkWidget *widget; + GailWindow *window; + + ATK_OBJECT_CLASS (parent_class)->initialize (obj, data); + + window = GAIL_WINDOW (obj); + window->name_change_handler = 0; + window->previous_name = g_strdup (gtk_window_get_title (GTK_WINDOW (data))); + widget = GTK_WIDGET (data); + + g_signal_connect (data, + "window_state_event", + G_CALLBACK (gail_window_state_event_gtk), + NULL); + g_object_set_data (G_OBJECT (obj), "atk-component-layer", + GINT_TO_POINTER (ATK_LAYER_WINDOW)); + + if (GTK_IS_FILE_SELECTION (widget)) + obj->role = ATK_ROLE_FILE_CHOOSER; + else if (GTK_IS_COLOR_SELECTION_DIALOG (widget)) + obj->role = ATK_ROLE_COLOR_CHOOSER; + else if (GTK_IS_FONT_SELECTION_DIALOG (widget)) + obj->role = ATK_ROLE_FONT_CHOOSER; + else if (GTK_IS_MESSAGE_DIALOG (widget)) + obj->role = ATK_ROLE_ALERT; + else if (GTK_IS_DIALOG (widget)) + obj->role = ATK_ROLE_DIALOG; + else + { + const gchar *name; + + name = gtk_widget_get_name (widget); + if (name && (!strcmp (name, "gtk-tooltip") || + !strcmp (name, "gtk-tooltips"))) + obj->role = ATK_ROLE_TOOL_TIP; + else if (GTK_IS_PLUG (widget)) + obj->role = ATK_ROLE_PANEL; + else if (GTK_WINDOW (widget)->type == GTK_WINDOW_POPUP) + obj->role = ATK_ROLE_WINDOW; + else + obj->role = ATK_ROLE_FRAME; + } +} + +static void +gail_window_finalize (GObject *object) +{ + GailWindow* window = GAIL_WINDOW (object); + + if (window->name_change_handler) + { + g_source_remove (window->name_change_handler); + window->name_change_handler = 0; + } + if (window->previous_name) + { + g_free (window->previous_name); + window->previous_name = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static G_CONST_RETURN gchar* +gail_window_get_name (AtkObject *accessible) +{ + G_CONST_RETURN gchar* name; + + name = ATK_OBJECT_CLASS (parent_class)->get_name (accessible); + if (name == NULL) + { + /* + * Get the window title if it exists + */ + GtkWidget* widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + gail_return_val_if_fail (GTK_IS_WIDGET (widget), NULL); + + if (GTK_IS_WINDOW (widget)) + { + GtkWindow *window = GTK_WINDOW (widget); + + name = gtk_window_get_title (window); + if (name == NULL && + accessible->role == ATK_ROLE_TOOL_TIP) + { + GtkWidget *child; + + child = gtk_bin_get_child (GTK_BIN (window)); + /* could be some kind of egg notification bubble thingy? */ + + /* Handle new GTK+ GNOME 2.20 tooltips */ + if (GTK_IS_ALIGNMENT(child)) + { + child = gtk_bin_get_child (GTK_BIN (child)); + if (GTK_IS_BOX(child)) + { + GList *children; + guint count; + children = gtk_container_get_children (child); + count = g_list_length (children); + if (count == 2) + { + child = (GtkWidget *) g_list_nth_data (children, 1); + } + g_list_free (children); + } + } + + if (!GTK_IS_LABEL (child)) + { + g_message ("ATK_ROLE_TOOLTIP object found, but doesn't look like a tooltip."); + return NULL; + } + name = gtk_label_get_text (GTK_LABEL (child)); + } + } + } + return name; +} + +static AtkObject* +gail_window_get_parent (AtkObject *accessible) +{ + AtkObject* parent; + + parent = ATK_OBJECT_CLASS (parent_class)->get_parent (accessible); + + return parent; +} + +static gint +gail_window_get_index_in_parent (AtkObject *accessible) +{ + GtkWidget* widget = GTK_ACCESSIBLE (accessible)->widget; + AtkObject* atk_obj = atk_get_root (); + gint index = -1; + + if (widget == NULL) + /* + * State is defunct + */ + return -1; + + gail_return_val_if_fail (GTK_IS_WIDGET (widget), -1); + + index = ATK_OBJECT_CLASS (parent_class)->get_index_in_parent (accessible); + if (index != -1) + return index; + + if (GTK_IS_WINDOW (widget)) + { + GtkWindow *window = GTK_WINDOW (widget); + if (GAIL_IS_TOPLEVEL (atk_obj)) + { + GailToplevel* toplevel = GAIL_TOPLEVEL (atk_obj); + index = g_list_index (toplevel->window_list, window); + } + else + { + int i, sibling_count = atk_object_get_n_accessible_children (atk_obj); + for (i = 0; i < sibling_count && index == -1; ++i) + { + AtkObject *child = atk_object_ref_accessible_child (atk_obj, i); + if (accessible == child) index = i; + g_object_unref (G_OBJECT (child)); + } + } + } + return index; +} + +static gboolean +gail_window_real_focus_gtk (GtkWidget *widget, + GdkEventFocus *event) +{ + AtkObject* obj; + + obj = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (obj, ATK_STATE_ACTIVE, event->in); + + return FALSE; +} + +static AtkRelationSet* +gail_window_ref_relation_set (AtkObject *obj) +{ + GtkWidget *widget; + AtkRelationSet *relation_set; + AtkObject *array[1]; + AtkRelation* relation; + GtkWidget *current_widget; + + gail_return_val_if_fail (GAIL_IS_WIDGET (obj), NULL); + + widget = GTK_ACCESSIBLE (obj)->widget; + if (widget == NULL) + /* + * State is defunct + */ + return NULL; + + relation_set = ATK_OBJECT_CLASS (parent_class)->ref_relation_set (obj); + + if (atk_object_get_role (obj) == ATK_ROLE_TOOL_TIP) + { + relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_POPUP_FOR); + + if (relation) + { + atk_relation_set_remove (relation_set, relation); + } + if (GTK_WIDGET_VISIBLE(widget) && gtk_tooltips_get_info_from_tip_window (GTK_WINDOW (widget), NULL, ¤t_widget)) + { + array [0] = gtk_widget_get_accessible (current_widget); + + relation = atk_relation_new (array, 1, ATK_RELATION_POPUP_FOR); + atk_relation_set_add (relation_set, relation); + g_object_unref (relation); + } + } + return relation_set; +} + +static AtkStateSet* +gail_window_ref_state_set (AtkObject *accessible) +{ + AtkStateSet *state_set; + GtkWidget *widget; + GtkWindow *window; + GdkWindowState state; + + state_set = ATK_OBJECT_CLASS (parent_class)->ref_state_set (accessible); + widget = GTK_ACCESSIBLE (accessible)->widget; + + if (widget == NULL) + return state_set; + + window = GTK_WINDOW (widget); + + if (window->has_focus) + atk_state_set_add_state (state_set, ATK_STATE_ACTIVE); + + if (widget->window) + { + state = gdk_window_get_state (widget->window); + if (state & GDK_WINDOW_STATE_ICONIFIED) + atk_state_set_add_state (state_set, ATK_STATE_ICONIFIED); + } + if (gtk_window_get_modal (window)) + atk_state_set_add_state (state_set, ATK_STATE_MODAL); + + if (gtk_window_get_resizable (window)) + atk_state_set_add_state (state_set, ATK_STATE_RESIZABLE); + + return state_set; +} + +static gboolean +idle_notify_name_change (gpointer data) +{ + GailWindow *window; + AtkObject *obj; + + GDK_THREADS_ENTER (); + + window = GAIL_WINDOW (data); + window->name_change_handler = 0; + if (GTK_ACCESSIBLE (window)->widget == NULL) + { + GDK_THREADS_LEAVE (); + return FALSE; + } + + obj = ATK_OBJECT (window); + if (obj->name == NULL) + { + /* + * The title has changed so notify a change in accessible-name + */ + g_object_notify (G_OBJECT (obj), "accessible-name"); + } + g_signal_emit_by_name (obj, "visible_data_changed"); + GDK_THREADS_LEAVE (); + return FALSE; +} + +static void +gail_window_real_notify_gtk (GObject *obj, + GParamSpec *pspec) +{ + GtkWidget *widget = GTK_WIDGET (obj); + AtkObject* atk_obj = gtk_widget_get_accessible (widget); + GailWindow *window = GAIL_WINDOW (atk_obj); + const gchar *name; + gboolean name_changed = FALSE; + + if (strcmp (pspec->name, "title") == 0) + { + name = gtk_window_get_title (GTK_WINDOW (widget)); + if (name) + { + if (window->previous_name == NULL || + strcmp (name, window->previous_name) != 0) + name_changed = TRUE; + } + else if (window->previous_name != NULL) + name_changed = TRUE; + + if (name_changed) + { + g_free (window->previous_name); + window->previous_name = g_strdup (name); + + if (window->name_change_handler == 0) + window->name_change_handler = g_idle_add (idle_notify_name_change, atk_obj); + } + } + else + GAIL_WIDGET_CLASS (parent_class)->notify_gtk (obj, pspec); +} + +static gboolean +gail_window_state_event_gtk (GtkWidget *widget, + GdkEventWindowState *event) +{ + AtkObject* obj; + + obj = gtk_widget_get_accessible (widget); + atk_object_notify_state_change (obj, ATK_STATE_ICONIFIED, + (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0); + return FALSE; +} + +static void +atk_component_interface_init (AtkComponentIface *iface) +{ + gail_return_if_fail (iface != NULL); + + iface->get_extents = gail_window_get_extents; + iface->get_size = gail_window_get_size; + iface->get_mdi_zorder = gail_window_get_mdi_zorder; +} + +static void +gail_window_get_extents (AtkComponent *component, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coord_type) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + GdkRectangle rect; + gint x_toplevel, y_toplevel; + + if (widget == NULL) + /* + * State is defunct + */ + return; + + gail_return_if_fail (GTK_IS_WINDOW (widget)); + + if (!GTK_WIDGET_TOPLEVEL (widget)) + { + AtkComponentIface *parent_iface; + + parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component)); + parent_iface->get_extents (component, x, y, width, height, coord_type); + return; + } + + gdk_window_get_frame_extents (widget->window, &rect); + + *width = rect.width; + *height = rect.height; + if (!GTK_WIDGET_DRAWABLE (widget)) + { + *x = G_MININT; + *y = G_MININT; + return; + } + *x = rect.x; + *y = rect.y; + if (coord_type == ATK_XY_WINDOW) + { + gdk_window_get_origin (widget->window, &x_toplevel, &y_toplevel); + *x -= x_toplevel; + *y -= y_toplevel; + } +} + +static void +gail_window_get_size (AtkComponent *component, + gint *width, + gint *height) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + GdkRectangle rect; + + if (widget == NULL) + /* + * State is defunct + */ + return; + + gail_return_if_fail (GTK_IS_WINDOW (widget)); + + if (!GTK_WIDGET_TOPLEVEL (widget)) + { + AtkComponentIface *parent_iface; + + parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component)); + parent_iface->get_size (component, width, height); + return; + } + gdk_window_get_frame_extents (widget->window, &rect); + + *width = rect.width; + *height = rect.height; +} + +#if defined (GDK_WINDOWING_X11) + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <gdk/x11/gdkx.h> + +/* _NET_CLIENT_LIST_STACKING monitoring */ + +typedef struct { + Window *stacked_windows; + int stacked_windows_len; + GdkWindow *root_window; + guint update_handler; + int *desktop; + guint update_desktop_handler; + gboolean *desktop_changed; + + guint screen_initialized : 1; + guint update_stacked_windows : 1; +} GailScreenInfo; + +static GailScreenInfo *gail_screens = NULL; +static int num_screens = 0; +static Atom _net_client_list_stacking = None; +static Atom _net_wm_desktop = None; + +static gint +get_window_desktop (Window window) +{ + Atom ret_type; + int format; + gulong nitems; + gulong bytes_after; + guchar *cardinals; + int error; + int result; + int desktop; + + if (_net_wm_desktop == None) + _net_wm_desktop = + XInternAtom (gdk_display, "_NET_WM_DESKTOP", False); + + gdk_error_trap_push (); + result = XGetWindowProperty (gdk_display, window, _net_wm_desktop, + 0, G_MAXLONG, + False, XA_CARDINAL, + &ret_type, &format, &nitems, + &bytes_after, &cardinals); + error = gdk_error_trap_pop(); + /* nitems < 1 will occur if the property is not set */ + if (error != Success || result != Success || nitems < 1) + return -1; + + desktop = *cardinals; + + XFree (cardinals); + if (nitems != 1) + return -1; + return desktop; +} + +static void +free_screen_info (GailScreenInfo *info) +{ + if (info->stacked_windows) + XFree (info->stacked_windows); + if (info->desktop) + g_free (info->desktop); + if (info->desktop_changed) + g_free (info->desktop_changed); + + info->stacked_windows = NULL; + info->stacked_windows_len = 0; + info->desktop = NULL; + info->desktop_changed = NULL; +} + +static gboolean +get_stacked_windows (GailScreenInfo *info) +{ + Atom ret_type; + int format; + gulong nitems; + gulong bytes_after; + guchar *data; + int error; + int result; + int i; + int j; + int *desktops; + gboolean *desktops_changed; + + if (_net_client_list_stacking == None) + _net_client_list_stacking = + XInternAtom (gdk_display, "_NET_CLIENT_LIST_STACKING", False); + + gdk_error_trap_push (); + ret_type = None; + result = XGetWindowProperty (gdk_display, + GDK_WINDOW_XWINDOW (info->root_window), + _net_client_list_stacking, + 0, G_MAXLONG, + False, XA_WINDOW, &ret_type, &format, &nitems, + &bytes_after, &data); + error = gdk_error_trap_pop (); + /* nitems < 1 will occur if the property is not set */ + if (error != Success || result != Success || nitems < 1) + { + free_screen_info (info); + return FALSE; + } + + if (ret_type != XA_WINDOW) + { + XFree (data); + free_screen_info (info); + return FALSE; + } + + desktops = g_malloc0 (nitems * sizeof (int)); + desktops_changed = g_malloc0 (nitems * sizeof (gboolean)); + for (i = 0; i < nitems; i++) + { + gboolean window_found = FALSE; + + for (j = 0; j < info->stacked_windows_len; j++) + { + if (info->stacked_windows [j] == data [i]) + { + desktops [i] = info->desktop [j]; + desktops_changed [i] = info->desktop_changed [j]; + window_found = TRUE; + break; + } + } + if (!window_found) + { + desktops [i] = get_window_desktop (data [i]); + desktops_changed [i] = FALSE; + } + } + free_screen_info (info); + info->stacked_windows = (Window*) data; + info->stacked_windows_len = nitems; + info->desktop = desktops; + info->desktop_changed = desktops_changed; + + return TRUE; +} + +static gboolean +update_screen_info (gpointer data) +{ + int screen_n = GPOINTER_TO_INT (data); + + GDK_THREADS_ENTER (); + + gail_screens [screen_n].update_handler = 0; + gail_screens [screen_n].update_stacked_windows = FALSE; + + get_stacked_windows (&gail_screens [screen_n]); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static gboolean +update_desktop_info (gpointer data) +{ + int screen_n = GPOINTER_TO_INT (data); + GailScreenInfo *info; + int i; + + GDK_THREADS_ENTER (); + + info = &gail_screens [screen_n]; + info->update_desktop_handler = 0; + + for (i = 0; i < info->stacked_windows_len; i++) + { + if (info->desktop_changed [i]) + { + info->desktop [i] = get_window_desktop (info->stacked_windows [i]); + info->desktop_changed [i] = FALSE; + } + } + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static GdkFilterReturn +filter_func (GdkXEvent *gdkxevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xevent = gdkxevent; + + if (xevent->type == PropertyNotify) + { + if (xevent->xproperty.atom == _net_client_list_stacking) + { + int screen_n; + GdkWindow *window; + + window = event->any.window; + + if (window) + { + screen_n = gdk_screen_get_number ( + gdk_drawable_get_screen (GDK_DRAWABLE (window))); + + gail_screens [screen_n].update_stacked_windows = TRUE; + if (!gail_screens [screen_n].update_handler) + { + gail_screens [screen_n].update_handler = g_idle_add (update_screen_info, + GINT_TO_POINTER (screen_n)); + } + } + } + else if (xevent->xproperty.atom == _net_wm_desktop) + { + int i; + int j; + GailScreenInfo *info; + + for (i = 0; i < num_screens; i++) + { + info = &gail_screens [i]; + for (j = 0; j < info->stacked_windows_len; j++) + { + if (xevent->xany.window == info->stacked_windows [j]) + { + info->desktop_changed [j] = TRUE; + if (!info->update_desktop_handler) + { + info->update_desktop_handler = g_idle_add (update_desktop_info, + GINT_TO_POINTER (i)); + } + break; + } + } + } + } + } + return GDK_FILTER_CONTINUE; +} + +static void +display_closed (GdkDisplay *display, + gboolean is_error) +{ + int i; + + for (i = 0; i < num_screens; i++) + { + if (gail_screens [i].update_handler) + { + g_source_remove (gail_screens [i].update_handler); + gail_screens [i].update_handler = 0; + } + + if (gail_screens [i].update_desktop_handler) + { + g_source_remove (gail_screens [i].update_desktop_handler); + gail_screens [i].update_desktop_handler = 0; + } + + free_screen_info (&gail_screens [i]); + } + + g_free (gail_screens); + gail_screens = NULL; + num_screens = 0; +} + +static void +init_gail_screens (void) +{ + GdkDisplay *display; + + display = gdk_display_get_default (); + + num_screens = gdk_display_get_n_screens (display); + + gail_screens = g_new0 (GailScreenInfo, num_screens); + gdk_window_add_filter (NULL, filter_func, NULL); + + g_signal_connect (display, "closed", G_CALLBACK (display_closed), NULL); +} + +static void +init_gail_screen (GdkScreen *screen, + int screen_n) +{ + XWindowAttributes attrs; + + gail_screens [screen_n].root_window = gdk_screen_get_root_window (screen); + + get_stacked_windows (&gail_screens [screen_n]); + + XGetWindowAttributes (gdk_display, + GDK_WINDOW_XWINDOW (gail_screens [screen_n].root_window), + &attrs); + + XSelectInput (gdk_display, + GDK_WINDOW_XWINDOW (gail_screens [screen_n].root_window), + attrs.your_event_mask | PropertyChangeMask); + + gail_screens [screen_n].screen_initialized = TRUE; +} + +static GailScreenInfo * +get_screen_info (GdkScreen *screen) +{ + int screen_n; + + gail_return_val_if_fail (GDK_IS_SCREEN (screen), NULL); + + screen_n = gdk_screen_get_number (screen); + + if (gail_screens && gail_screens [screen_n].screen_initialized) + return &gail_screens [screen_n]; + + if (!gail_screens) + init_gail_screens (); + + g_assert (gail_screens != NULL); + + init_gail_screen (screen, screen_n); + + g_assert (gail_screens [screen_n].screen_initialized); + + return &gail_screens [screen_n]; +} + +static gint +get_window_zorder (GdkWindow *window) +{ + GailScreenInfo *info; + Window xid; + int i; + int zorder; + int w_desktop; + + gail_return_val_if_fail (GDK_IS_WINDOW (window), -1); + + info = get_screen_info ( + gdk_drawable_get_screen (GDK_DRAWABLE (window))); + + gail_return_val_if_fail (info->stacked_windows != NULL, -1); + + xid = GDK_WINDOW_XID (window); + + w_desktop = -1; + for (i = 0; i < info->stacked_windows_len; i++) + { + if (info->stacked_windows [i] == xid) + { + w_desktop = info->desktop[i]; + break; + } + } + if (w_desktop < 0) + return w_desktop; + + zorder = 0; + for (i = 0; i < info->stacked_windows_len; i++) + { + if (info->stacked_windows [i] == xid) + { + return zorder; + } + else + { + if (info->desktop[i] == w_desktop) + zorder++; + } + } + + return -1; +} + +static gint +gail_window_get_mdi_zorder (AtkComponent *component) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return -1; + + gail_return_val_if_fail (GTK_IS_WINDOW (widget), -1); + + return get_window_zorder (widget->window); +} + +#elif defined (GDK_WINDOWING_WIN32) + +static gint +gail_window_get_mdi_zorder (AtkComponent *component) +{ + GtkWidget *widget = GTK_ACCESSIBLE (component)->widget; + + if (widget == NULL) + /* + * State is defunct + */ + return -1; + + gail_return_val_if_fail (GTK_IS_WINDOW (widget), -1); + + return 0; /* Punt, FIXME */ +} + +#else +#error Port to this GDK backend +#endif diff --git a/modules/other/gail/gailwindow.h b/modules/other/gail/gailwindow.h new file mode 100644 index 000000000..289f83d1b --- /dev/null +++ b/modules/other/gail/gailwindow.h @@ -0,0 +1,63 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_WINDOW_H__ +#define __GAIL_WINDOW_H__ + +#include <gtk/gtkaccessible.h> +#include <gail/gailcontainer.h> + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GAIL_TYPE_WINDOW (gail_window_get_type ()) +#define GAIL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_WINDOW, GailWindow)) +#define GAIL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_WINDOW, GailWindowClass)) +#define GAIL_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_WINDOW)) +#define GAIL_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_WINDOW)) +#define GAIL_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_WINDOW, GailWindowClass)) + +typedef struct _GailWindow GailWindow; +typedef struct _GailWindowClass GailWindowClass; + +struct _GailWindow +{ + GailContainer parent; + + guint name_change_handler; + gchar *previous_name; +}; + +GType gail_window_get_type (void); + +struct _GailWindowClass +{ + GailContainerClass parent_class; +}; + +AtkObject* gail_window_new (GtkWidget *widget); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GAIL_WINDOW_H__ */ diff --git a/modules/other/gail/libgail-util/Makefile.am b/modules/other/gail/libgail-util/Makefile.am new file mode 100644 index 000000000..428ac9e3a --- /dev/null +++ b/modules/other/gail/libgail-util/Makefile.am @@ -0,0 +1,76 @@ +include $(top_srcdir)/Makefile.decl + +EXTRA_DIST += gailutil.def +if OS_WIN32 +export_symbols = -export-symbols gailutil.def +no_undefined = -no-undefined +install-libtool-import-lib: + $(INSTALL) .libs/libgailutil.dll.a $(DESTDIR)$(libdir) +uninstall-libtool-import-lib: + -rm $(DESTDIR)$(libdir)/libgailutil.dll.a +else +install-libtool-import-lib: +uninstall-libtool-import-lib: +endif + +if MS_LIB_AVAILABLE +noinst_DATA = gailutil.lib + +install-ms-lib: + $(INSTALL) gailutil.lib $(DESTDIR)$(libdir) + +uninstall-ms-lib: + -rm $(DESTDIR)$(libdir)/gailutil.lib +else +install-ms-lib: +uninstall-ms-lib: +endif + + +lib_LTLIBRARIES = libgailutil.la + +util_c_sources = \ + gailmisc.c \ + gailtextutil.c + +libgailutilincludedir=$(includedir)/gail-1.0/libgail-util + +util_public_h_sources = \ + gailmisc.h \ + gailtextutil.h \ + gail-util.h + +libgailutil_la_SOURCES = \ + $(util_c_sources) + +libgailutilinclude_HEADERS = \ + $(util_public_h_sources) + +libgailutil_la_CPPFLAGS = \ + -I$(top_srcdir)/gdk \ + -I$(top_builddir)/gdk \ + -I$(top_srcdir)/gtk \ + -I$(top_builddir)/gtk \ + $(AM_CPPFLAGS) + +libgailutil_la_CFLAGS = \ + $(GTK_DEP_CFLAGS) \ + $(GTK_DEBUG_FLAGS) \ + $(AM_CFLAGS) + +libgailutil_la_LIBADD = \ + $(GTK_DEP_LIBS) + +libgailutil_la_LDFLAGS = \ + -version-info $(LT_VERSION_INFO) \ + $(no_undefined) \ + $(export_symbols) \ + $(LDFLAGS) + +gailutil.lib: libgailutil.la gailutil.def + lib -name:libgailutil-@LT_CURRENT_MINUS_AGE@.dll -def:gailutil.def -out:$@ + +install-data-local: install-ms-lib install-libtool-import-lib + +uninstall-local: uninstall-ms-lib uninstall-libtool-import-lib + diff --git a/modules/other/gail/libgail-util/gail-util.h b/modules/other/gail/libgail-util/gail-util.h new file mode 100644 index 000000000..87824f652 --- /dev/null +++ b/modules/other/gail/libgail-util/gail-util.h @@ -0,0 +1,2 @@ +#include <libgail-util/gailmisc.h> +#include <libgail-util/gailtextutil.h> diff --git a/modules/other/gail/libgail-util/gailmisc.c b/modules/other/gail/libgail-util/gailmisc.c new file mode 100644 index 000000000..cd02743a0 --- /dev/null +++ b/modules/other/gail/libgail-util/gailmisc.c @@ -0,0 +1,1112 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <gtk/gtk.h> +#include "gailmisc.h" + +/* IMPORTANT!!! This source file does NOT contain the implementation + * code for AtkUtil - for that code, please see gail/gail.c. + */ + +/** + * gail_misc_get_extents_from_pango_rectangle: + * @widget: The widget that contains the PangoLayout, that contains + * the PangoRectangle + * @char_rect: The #PangoRectangle from which to calculate extents + * @x_layout: The x-offset at which the widget displays the + * PangoLayout that contains the PangoRectangle, relative to @widget + * @y_layout: The y-offset at which the widget displays the + * PangoLayout that contains the PangoRectangle, relative to @widget + * @x: The x-position of the #PangoRectangle relative to @coords + * @y: The y-position of the #PangoRectangle relative to @coords + * @width: The width of the #PangoRectangle + * @height: The height of the #PangoRectangle + * @coords: An #AtkCoordType enumeration + * + * Gets the extents of @char_rect in device coordinates, + * relative to either top-level window or screen coordinates as + * specified by @coords. + **/ +void +gail_misc_get_extents_from_pango_rectangle (GtkWidget *widget, + PangoRectangle *char_rect, + gint x_layout, + gint y_layout, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords) +{ + gint x_window, y_window, x_toplevel, y_toplevel; + + gail_misc_get_origins (widget, &x_window, &y_window, + &x_toplevel, &y_toplevel); + + *x = (char_rect->x / PANGO_SCALE) + x_layout + x_window; + *y = (char_rect->y / PANGO_SCALE) + y_layout + y_window; + if (coords == ATK_XY_WINDOW) + { + *x -= x_toplevel; + *y -= y_toplevel; + } + else if (coords != ATK_XY_SCREEN) + { + *x = 0; + *y = 0; + *height = 0; + *width = 0; + return; + } + *height = char_rect->height / PANGO_SCALE; + *width = char_rect->width / PANGO_SCALE; + + return; +} + +/** + * gail_misc_get_index_at_point_in_layout: + * @widget: A #GtkWidget + * @layout: The #PangoLayout from which to get the index at the + * specified point. + * @x_layout: The x-offset at which the widget displays the + * #PangoLayout, relative to @widget + * @y_layout: The y-offset at which the widget displays the + * #PangoLayout, relative to @widget + * @x: The x-coordinate relative to @coords at which to + * calculate the index + * @y: The y-coordinate relative to @coords at which to + * calculate the index + * @coords: An #AtkCoordType enumeration + * + * Gets the byte offset at the specified @x and @y in a #PangoLayout. + * + * Returns: the byte offset at the specified @x and @y in a + * #PangoLayout + **/ +gint +gail_misc_get_index_at_point_in_layout (GtkWidget *widget, + PangoLayout *layout, + gint x_layout, + gint y_layout, + gint x, + gint y, + AtkCoordType coords) +{ + gint index, x_window, y_window, x_toplevel, y_toplevel; + gint x_temp, y_temp; + gboolean ret; + + gail_misc_get_origins (widget, &x_window, &y_window, + &x_toplevel, &y_toplevel); + x_temp = x - x_layout - x_window; + y_temp = y - y_layout - y_window; + if (coords == ATK_XY_WINDOW) + { + x_temp += x_toplevel; + y_temp += y_toplevel; + } + else if (coords != ATK_XY_SCREEN) + return -1; + + ret = pango_layout_xy_to_index (layout, + x_temp * PANGO_SCALE, + y_temp * PANGO_SCALE, + &index, NULL); + if (!ret) + { + if (x_temp < 0 || y_temp < 0) + index = 0; + else + index = -1; + } + return index; +} + +/** + * gail_misc_add_attribute: + * @attrib_set: The #AtkAttributeSet to add the attribute to + * @attr: The AtkTextAttrribute which identifies the attribute to be added + * @value: The attribute value + * + * Creates an #AtkAttribute from @attr and @value, and adds it + * to @attrib_set. + * + * Returns: A pointer to the new #AtkAttributeSet. + **/ +AtkAttributeSet* +gail_misc_add_attribute (AtkAttributeSet *attrib_set, + AtkTextAttribute attr, + gchar *value) +{ + AtkAttributeSet *return_set; + AtkAttribute *at = g_malloc (sizeof (AtkAttribute)); + at->name = g_strdup (atk_text_attribute_get_name (attr)); + at->value = value; + return_set = g_slist_prepend(attrib_set, at); + return return_set; +} + +/** + * gail_misc_layout_get_run_attributes: + * @attrib_set: The #AtkAttributeSet to add the attribute to + * @layout: The PangoLayout from which the attributes will be obtained + * @text: The text + * @offset: The offset at which the attributes are required + * @start_offset: The start offset of the current run + * @end_offset: The end offset of the current run + * + * Adds the attributes for the run starting at offset to the specified + * attribute set. + * + * Returns: A pointer to the #AtkAttributeSet. + **/ +AtkAttributeSet* +gail_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, + PangoLayout *layout, + gchar *text, + gint offset, + gint *start_offset, + gint *end_offset) +{ + PangoAttrIterator *iter; + PangoAttrList *attr; + PangoAttrString *pango_string; + PangoAttrInt *pango_int; + PangoAttrColor *pango_color; + PangoAttrLanguage *pango_lang; + PangoAttrFloat *pango_float; + gint index, start_index, end_index; + gboolean is_next = TRUE; + gchar *value = NULL; + glong len; + + len = g_utf8_strlen (text, -1); + /* Grab the attributes of the PangoLayout, if any */ + if ((attr = pango_layout_get_attributes (layout)) == NULL) + { + *start_offset = 0; + *end_offset = len; + return attrib_set; + } + iter = pango_attr_list_get_iterator (attr); + /* Get invariant range offsets */ + /* If offset out of range, set offset in range */ + if (offset > len) + offset = len; + else if (offset < 0) + offset = 0; + + index = g_utf8_offset_to_pointer (text, offset) - text; + pango_attr_iterator_range (iter, &start_index, &end_index); + while (is_next) + { + if (index >= start_index && index < end_index) + { + *start_offset = g_utf8_pointer_to_offset (text, + text + start_index); + if (end_index == G_MAXINT) + /* Last iterator */ + end_index = len; + + *end_offset = g_utf8_pointer_to_offset (text, + text + end_index); + break; + } + is_next = pango_attr_iterator_next (iter); + pango_attr_iterator_range (iter, &start_index, &end_index); + } + /* Get attributes */ + if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, + PANGO_ATTR_FAMILY)) != NULL) + { + value = g_strdup_printf("%s", pango_string->value); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FAMILY_NAME, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STYLE)) != NULL) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STYLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_WEIGHT)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_WEIGHT, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_VARIANT)) != NULL) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_VARIANT, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STRETCH)) != NULL) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRETCH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_SIZE)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SIZE, + value); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_UNDERLINE)) != NULL) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_UNDERLINE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_STRIKETHROUGH)) != NULL) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRIKETHROUGH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value))); + } + if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, + PANGO_ATTR_RISE)) != NULL) + { + value = g_strdup_printf("%i", pango_int->value); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_RISE, + value); + } + if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, + PANGO_ATTR_LANGUAGE)) != NULL) + { + value = g_strdup( pango_language_to_string( pango_lang->value)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_LANGUAGE, + value); + } + if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, + PANGO_ATTR_SCALE)) != NULL) + { + value = g_strdup_printf("%g", pango_float->value); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SCALE, + value); + } + if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, + PANGO_ATTR_FOREGROUND)) != NULL) + { + value = g_strdup_printf ("%u,%u,%u", + pango_color->color.red, + pango_color->color.green, + pango_color->color.blue); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FG_COLOR, + value); + } + if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, + PANGO_ATTR_BACKGROUND)) != NULL) + { + value = g_strdup_printf ("%u,%u,%u", + pango_color->color.red, + pango_color->color.green, + pango_color->color.blue); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_BG_COLOR, + value); + } + pango_attr_iterator_destroy (iter); + return attrib_set; +} + +/** + * gail_misc_get_default_attributes: + * @attrib_set: The #AtkAttributeSet to add the attribute to + * @layout: The PangoLayout from which the attributes will be obtained + * @widget: The GtkWidget for which the default attributes are required. + * + * Adds the default attributes to the specified attribute set. + * + * Returns: A pointer to the #AtkAttributeSet. + **/ +AtkAttributeSet* +gail_misc_get_default_attributes (AtkAttributeSet *attrib_set, + PangoLayout *layout, + GtkWidget *widget) +{ + PangoContext *context; + GtkStyle *style_value; + gint int_value; + PangoWrapMode mode; + + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_DIRECTION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, + gtk_widget_get_direction (widget)))); + + context = pango_layout_get_context (layout); + if (context) + { + PangoLanguage* language; + PangoFontDescription* font; + + language = pango_context_get_language (context); + if (language) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_LANGUAGE, + g_strdup (pango_language_to_string (language))); + } + font = pango_context_get_font_description (context); + if (font) + { + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STYLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, + pango_font_description_get_style (font)))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_VARIANT, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, + pango_font_description_get_variant (font)))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRETCH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, + pango_font_description_get_stretch (font)))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FAMILY_NAME, + g_strdup (pango_font_description_get_family (font))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_WEIGHT, + g_strdup_printf ("%d", + pango_font_description_get_weight (font))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SIZE, + g_strdup_printf ("%i", + pango_font_description_get_size (font) / PANGO_SCALE)); + } + } + if (pango_layout_get_justify (layout)) + { + int_value = 3; + } + else + { + PangoAlignment align; + + align = pango_layout_get_alignment (layout); + if (align == PANGO_ALIGN_LEFT) + int_value = 0; + else if (align == PANGO_ALIGN_CENTER) + int_value = 2; + else /* if (align == PANGO_ALIGN_RIGHT) */ + int_value = 1; + } + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_JUSTIFICATION, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, + int_value))); + mode = pango_layout_get_wrap (layout); + if (mode == PANGO_WRAP_WORD) + int_value = 2; + else /* if (mode == PANGO_WRAP_CHAR) */ + int_value = 1; + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_WRAP_MODE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, + int_value))); + + style_value = gtk_widget_get_style (widget); + if (style_value) + { + GdkColor color; + gchar *value; + + color = style_value->base[GTK_STATE_NORMAL]; + value = g_strdup_printf ("%u,%u,%u", + color.red, color.green, color.blue); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_BG_COLOR, + value); + color = style_value->text[GTK_STATE_NORMAL]; + value = g_strdup_printf ("%u,%u,%u", + color.red, color.green, color.blue); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FG_COLOR, + value); + } + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_FG_STIPPLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_FG_STIPPLE, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_BG_STIPPLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_STIPPLE, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_STRIKETHROUGH, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_UNDERLINE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_RISE, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_SCALE, + g_strdup_printf ("%g", 1.0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_BG_FULL_HEIGHT, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_PIXELS_BELOW_LINES, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_EDITABLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_INVISIBLE, + g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, + 0))); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_INDENT, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_RIGHT_MARGIN, + g_strdup_printf ("%i", 0)); + attrib_set = gail_misc_add_attribute (attrib_set, + ATK_TEXT_ATTR_LEFT_MARGIN, + g_strdup_printf ("%i", 0)); + return attrib_set; +} + +/** + * gail_misc_get_origins: + * @widget: a #GtkWidget + * @x_window: the x-origin of the widget->window + * @y_window: the y-origin of the widget->window + * @x_toplevel: the x-origin of the toplevel window for widget->window + * @y_toplevel: the y-origin of the toplevel window for widget->window + * + * Gets the origin of the widget window, and the origin of the + * widgets top-level window. + **/ +void +gail_misc_get_origins (GtkWidget *widget, + gint *x_window, + gint *y_window, + gint *x_toplevel, + gint *y_toplevel) +{ + GdkWindow *window; + + if (GTK_IS_TREE_VIEW (widget)) + window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)); + else + window = widget->window; + gdk_window_get_origin (window, x_window, y_window); + window = gdk_window_get_toplevel (widget->window); + gdk_window_get_origin (window, x_toplevel, y_toplevel); +} + +/** + * gail_misc_add_to_attr_set: + * @attrib_set: An #AtkAttributeSet + * @attrs: The #GtkTextAttributes containing the attribute value + * @attr: The #AtkTextAttribute to be added + * + * Gets the value for the AtkTextAttribute from the GtkTextAttributes + * and adds it to the AttributeSet. + * + * Returns: A pointer to the updated #AtkAttributeSet. + **/ +AtkAttributeSet* +gail_misc_add_to_attr_set (AtkAttributeSet *attrib_set, + GtkTextAttributes *attrs, + AtkTextAttribute attr) +{ + gchar *value; + + switch (attr) + { + case ATK_TEXT_ATTR_LEFT_MARGIN: + value = g_strdup_printf ("%i", attrs->left_margin); + break; + case ATK_TEXT_ATTR_RIGHT_MARGIN: + value = g_strdup_printf ("%i", attrs->right_margin); + break; + case ATK_TEXT_ATTR_INDENT: + value = g_strdup_printf ("%i", attrs->indent); + break; + case ATK_TEXT_ATTR_INVISIBLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->invisible)); + break; + case ATK_TEXT_ATTR_EDITABLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->editable)); + break; + case ATK_TEXT_ATTR_PIXELS_ABOVE_LINES: + value = g_strdup_printf ("%i", attrs->pixels_above_lines); + break; + case ATK_TEXT_ATTR_PIXELS_BELOW_LINES: + value = g_strdup_printf ("%i", attrs->pixels_below_lines); + break; + case ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP: + value = g_strdup_printf ("%i", attrs->pixels_inside_wrap); + break; + case ATK_TEXT_ATTR_BG_FULL_HEIGHT: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->bg_full_height)); + break; + case ATK_TEXT_ATTR_RISE: + value = g_strdup_printf ("%i", attrs->appearance.rise); + break; + case ATK_TEXT_ATTR_UNDERLINE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.underline)); + break; + case ATK_TEXT_ATTR_STRIKETHROUGH: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.strikethrough)); + break; + case ATK_TEXT_ATTR_SIZE: + value = g_strdup_printf ("%i", + pango_font_description_get_size (attrs->font) / PANGO_SCALE); + break; + case ATK_TEXT_ATTR_SCALE: + value = g_strdup_printf ("%g", attrs->font_scale); + break; + case ATK_TEXT_ATTR_WEIGHT: + value = g_strdup_printf ("%d", + pango_font_description_get_weight (attrs->font)); + break; + case ATK_TEXT_ATTR_LANGUAGE: + value = g_strdup ((gchar *)(attrs->language)); + break; + case ATK_TEXT_ATTR_FAMILY_NAME: + value = g_strdup (pango_font_description_get_family (attrs->font)); + break; + case ATK_TEXT_ATTR_BG_COLOR: + value = g_strdup_printf ("%u,%u,%u", + attrs->appearance.bg_color.red, + attrs->appearance.bg_color.green, + attrs->appearance.bg_color.blue); + break; + case ATK_TEXT_ATTR_FG_COLOR: + value = g_strdup_printf ("%u,%u,%u", + attrs->appearance.fg_color.red, + attrs->appearance.fg_color.green, + attrs->appearance.fg_color.blue); + break; + case ATK_TEXT_ATTR_BG_STIPPLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.bg_stipple ? 1 : 0)); + break; + case ATK_TEXT_ATTR_FG_STIPPLE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.fg_stipple ? 1 : 0)); + break; + case ATK_TEXT_ATTR_WRAP_MODE: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->wrap_mode)); + break; + case ATK_TEXT_ATTR_DIRECTION: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->direction)); + break; + case ATK_TEXT_ATTR_JUSTIFICATION: + value = g_strdup (atk_text_attribute_get_value (attr, attrs->justification)); + break; + case ATK_TEXT_ATTR_STRETCH: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_stretch (attrs->font))); + break; + case ATK_TEXT_ATTR_VARIANT: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_variant (attrs->font))); + break; + case ATK_TEXT_ATTR_STYLE: + value = g_strdup (atk_text_attribute_get_value (attr, + pango_font_description_get_style (attrs->font))); + break; + default: + value = NULL; + break; + } + return gail_misc_add_attribute (attrib_set, attr, value); +} + +/** + * gail_misc_buffer_get_run_attributes: + * @buffer: The #GtkTextBuffer for which the attributes will be obtained + * @offset: The offset at which the attributes are required + * @start_offset: The start offset of the current run + * @end_offset: The end offset of the current run + * + * Creates an AtkAttributeSet which contains the attributes for the + * run starting at offset. + * + * Returns: A pointer to the #AtkAttributeSet. + **/ +AtkAttributeSet* +gail_misc_buffer_get_run_attributes (GtkTextBuffer *buffer, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkTextIter iter; + AtkAttributeSet *attrib_set = NULL; + AtkAttribute *at; + GSList *tags, *temp_tags; + gdouble scale = 1; + gboolean val_set = FALSE; + PangoFontMask mask; + + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + + gtk_text_iter_forward_to_tag_toggle (&iter, NULL); + *end_offset = gtk_text_iter_get_offset (&iter); + + gtk_text_iter_backward_to_tag_toggle (&iter, NULL); + *start_offset = gtk_text_iter_get_offset (&iter); + + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + + tags = gtk_text_iter_get_tags (&iter); + tags = g_slist_reverse (tags); + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_STYLE; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_STYLE); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_VARIANT; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_VARIANT); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_STRETCH; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_STRETCH); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->justification_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_JUSTIFICATION); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + if (tag->values->direction != GTK_TEXT_DIR_NONE) + { + val_set = TRUE; + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_DIRECTION); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->wrap_mode_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_WRAP_MODE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->fg_stipple_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_FG_STIPPLE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->bg_stipple_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_BG_STIPPLE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->fg_color_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_FG_COLOR); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->bg_color_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_BG_COLOR); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_FAMILY; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_FAMILY_NAME); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->language_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_LANGUAGE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_WEIGHT; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_WEIGHT); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + + /* + * scale is special as the scale is the product of all scale values + * specified. + */ + temp_tags = tags; + while (temp_tags) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + if (tag->scale_set) + { + val_set = TRUE; + scale *= tag->values->font_scale; + } + temp_tags = temp_tags->next; + } + if (val_set) + { + at = g_malloc(sizeof(AtkAttribute)); + at->name = g_strdup(atk_text_attribute_get_name (ATK_TEXT_ATTR_SCALE)); + at->value = g_strdup_printf("%g", scale); + attrib_set = g_slist_prepend(attrib_set, at); + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoFontDescription *font; + + font = tag->values->font; + + if (font) + { + mask = pango_font_description_get_set_fields (font); + val_set = mask & PANGO_FONT_MASK_SIZE; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_SIZE); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->strikethrough_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_STRIKETHROUGH); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->underline_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_UNDERLINE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->rise_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_RISE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->bg_full_height_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_BG_FULL_HEIGHT); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->pixels_inside_wrap_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->pixels_below_lines_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_PIXELS_BELOW_LINES); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->pixels_above_lines_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_PIXELS_ABOVE_LINES); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->editable_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_EDITABLE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->invisible_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_INVISIBLE); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->indent_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_INDENT); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->right_margin_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_RIGHT_MARGIN); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + val_set = tag->left_margin_set; + if (val_set) + attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, + ATK_TEXT_ATTR_LEFT_MARGIN); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + g_slist_free (tags); + return attrib_set; +} diff --git a/modules/other/gail/libgail-util/gailmisc.h b/modules/other/gail/libgail-util/gailmisc.h new file mode 100644 index 000000000..ced98e60d --- /dev/null +++ b/modules/other/gail/libgail-util/gailmisc.h @@ -0,0 +1,86 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_MISC_H__ +#define __GAIL_MISC_H__ + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus */ + +#include <glib-object.h> +#include <gtk/gtk.h> +#include <pango/pango.h> + +AtkAttributeSet* gail_misc_add_attribute (AtkAttributeSet *attrib_set, + AtkTextAttribute attr, + gchar *value); +AtkAttributeSet* gail_misc_layout_get_run_attributes + (AtkAttributeSet *attrib_set, + PangoLayout *layout, + gchar *text, + gint offset, + gint *start_offset, + gint *end_offset); + +AtkAttributeSet* gail_misc_get_default_attributes (AtkAttributeSet *attrib_set, + PangoLayout *layout, + GtkWidget *widget); + +void gail_misc_get_extents_from_pango_rectangle + (GtkWidget *widget, + PangoRectangle *char_rect, + gint x_layout, + gint y_layout, + gint *x, + gint *y, + gint *width, + gint *height, + AtkCoordType coords); + +gint gail_misc_get_index_at_point_in_layout + (GtkWidget *widget, + PangoLayout *layout, + gint x_layout, + gint y_layout, + gint x, + gint y, + AtkCoordType coords); + +void gail_misc_get_origins (GtkWidget *widget, + gint *x_window, + gint *y_window, + gint *x_toplevel, + gint *y_toplevel); + +AtkAttributeSet* gail_misc_add_to_attr_set (AtkAttributeSet *attrib_set, + GtkTextAttributes *attrs, + AtkTextAttribute attr); + +AtkAttributeSet* gail_misc_buffer_get_run_attributes + (GtkTextBuffer *buffer, + gint offset, + gint *start_offset, + gint *end_offset); + +#ifdef __cplusplus +} +#endif /*cplusplus */ + +#endif /*__GAIL_MISC_H__ */ diff --git a/modules/other/gail/libgail-util/gailtextutil.c b/modules/other/gail/libgail-util/gailtextutil.c new file mode 100644 index 000000000..66fd9f115 --- /dev/null +++ b/modules/other/gail/libgail-util/gailtextutil.c @@ -0,0 +1,769 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include "gailtextutil.h" + +static void gail_text_util_class_init (GailTextUtilClass *klass); + +static void gail_text_util_init (GailTextUtil *textutil); +static void gail_text_util_finalize (GObject *object); + + +static void get_pango_text_offsets (PangoLayout *layout, + GtkTextBuffer *buffer, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset, + GtkTextIter *start_iter, + GtkTextIter *end_iter); +static GObjectClass *parent_class = NULL; + +GType +gail_text_util_get_type(void) +{ + static GType type = 0; + + if (!type) + { + static const GTypeInfo tinfo = + { + sizeof (GailTextUtilClass), + (GBaseInitFunc) NULL, /* base init */ + (GBaseFinalizeFunc) NULL, /* base finalize */ + (GClassInitFunc) gail_text_util_class_init, + (GClassFinalizeFunc) NULL, /* class finalize */ + NULL, /* class data */ + sizeof(GailTextUtil), + 0, /* nb preallocs */ + (GInstanceInitFunc) gail_text_util_init, + NULL, /* value table */ + }; + + type = g_type_register_static (G_TYPE_OBJECT, "GailTextUtil", &tinfo, 0); + } + return type; +} + +/** + * gail_text_util_new: + * + * This function creates a new GailTextUtil object. + * + * Returns: the GailTextUtil object + **/ +GailTextUtil* +gail_text_util_new (void) +{ + return GAIL_TEXT_UTIL (g_object_new (GAIL_TYPE_TEXT_UTIL, NULL)); +} + +static void +gail_text_util_init (GailTextUtil *textutil) +{ + textutil->buffer = NULL; +} + +static void +gail_text_util_class_init (GailTextUtilClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->finalize = gail_text_util_finalize; +} + +static void +gail_text_util_finalize (GObject *object) +{ + GailTextUtil *textutil = GAIL_TEXT_UTIL (object); + + if (textutil->buffer) + g_object_unref (textutil->buffer); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/** + * gail_text_util_text_setup: + * @textutil: The #GailTextUtil to be initialized. + * @text: A gchar* which points to the text to be stored in the GailTextUtil + * + * This function initializes the GailTextUtil with the specified character string, + **/ +void +gail_text_util_text_setup (GailTextUtil *textutil, + const gchar *text) +{ + g_return_if_fail (GAIL_IS_TEXT_UTIL (textutil)); + + if (textutil->buffer) + { + if (!text) + { + g_object_unref (textutil->buffer); + textutil->buffer = NULL; + return; + } + } + else + { + textutil->buffer = gtk_text_buffer_new (NULL); + } + + gtk_text_buffer_set_text (textutil->buffer, text, -1); +} + +/** + * gail_text_util_buffer_setup: + * @textutil: A #GailTextUtil to be initialized + * @buffer: The #GtkTextBuffer which identifies the text to be stored in the GailUtil. + * + * This function initializes the GailTextUtil with the specified GtkTextBuffer + **/ +void +gail_text_util_buffer_setup (GailTextUtil *textutil, + GtkTextBuffer *buffer) +{ + g_return_if_fail (GAIL_IS_TEXT_UTIL (textutil)); + + textutil->buffer = g_object_ref (buffer); +} + +/** + * gail_text_util_get_text: + * @textutil: A #GailTextUtil + * @layout: A gpointer which is a PangoLayout, a GtkTreeView of NULL + * @function: An enumeration specifying whether to return the text before, at, or + * after the offset. + * @boundary_type: The boundary type. + * @offset: The offset of the text in the GailTextUtil + * @start_offset: Address of location in which the start offset is returned + * @end_offset: Address of location in which the end offset is returned + * + * This function gets the requested substring from the text in the GtkTextUtil. + * The layout is used only for getting the text on a line. The value is NULL + * for a GtkTextView which is not wrapped, is a GtkTextView for a GtkTextView + * which is wrapped and is a PangoLayout otherwise. + * + * Returns: the substring requested + **/ +gchar* +gail_text_util_get_text (GailTextUtil *textutil, + gpointer layout, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset) +{ + GtkTextIter start, end; + gint line_number; + GtkTextBuffer *buffer; + + g_return_val_if_fail (GAIL_IS_TEXT_UTIL (textutil), NULL); + + buffer = textutil->buffer; + if (buffer == NULL) + { + *start_offset = 0; + *end_offset = 0; + return NULL; + } + + if (!gtk_text_buffer_get_char_count (buffer)) + { + *start_offset = 0; + *end_offset = 0; + return g_strdup (""); + } + gtk_text_buffer_get_iter_at_offset (buffer, &start, offset); + + + end = start; + + switch (function) + { + case GAIL_BEFORE_OFFSET: + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_backward_char(&start); + break; + case ATK_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + end = start; + gtk_text_iter_backward_word_start(&start); + break; + case ATK_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (&start) && + !gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + end = start; + gtk_text_iter_backward_word_start(&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + break; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + end = start; + gtk_text_iter_backward_sentence_start (&start); + break; + case ATK_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (&start) && + !gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + end = start; + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + break; + case ATK_TEXT_BOUNDARY_LINE_START: + if (layout == NULL) + { + line_number = gtk_text_iter_get_line (&start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, + &start, 0); + } + else + { + gtk_text_iter_backward_line (&start); + gtk_text_iter_forward_line (&start); + } + end = start; + gtk_text_iter_backward_line (&start); + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_backward_display_line_start (view, &start); + end = start; + gtk_text_view_backward_display_line (view, &start); + } + else if (PANGO_IS_LAYOUT (layout)) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + break; + case ATK_TEXT_BOUNDARY_LINE_END: + if (layout == NULL) + { + line_number = gtk_text_iter_get_line (&start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, + &start, 0); + end = start; + } + else + { + gtk_text_iter_backward_line (&start); + end = start; + while (!gtk_text_iter_ends_line (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_to_line_end (&end); + } + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_backward_display_line_start (view, &start); + if (!gtk_text_iter_is_start (&start)) + { + gtk_text_view_backward_display_line (view, &start); + end = start; + if (!gtk_text_iter_is_start (&start)) + { + gtk_text_view_backward_display_line (view, &start); + gtk_text_view_forward_display_line_end (view, &start); + } + gtk_text_view_forward_display_line_end (view, &end); + } + else + { + end = start; + } + } + else if (PANGO_IS_LAYOUT (layout)) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + break; + } + break; + + case GAIL_AT_OFFSET: + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char (&end); + break; + case ATK_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + if (gtk_text_iter_inside_word (&end)) + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + break; + case ATK_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (&start) && + !gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_word_end (&end); + break; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + if (gtk_text_iter_inside_sentence (&end)) + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + break; + case ATK_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (&start) && + !gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_sentence_end (&end); + break; + case ATK_TEXT_BOUNDARY_LINE_START: + if (layout == NULL) + { + line_number = gtk_text_iter_get_line (&start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, + &start, 0); + } + else + { + gtk_text_iter_backward_line (&start); + gtk_text_iter_forward_line (&start); + } + gtk_text_iter_forward_line (&end); + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_backward_display_line_start (view, &start); + /* + * The call to gtk_text_iter_forward_to_end() is needed + * because of bug 81960 + */ + if (!gtk_text_view_forward_display_line (view, &end)) + gtk_text_iter_forward_to_end (&end); + } + else if PANGO_IS_LAYOUT (layout) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + + break; + case ATK_TEXT_BOUNDARY_LINE_END: + if (layout == NULL) + { + line_number = gtk_text_iter_get_line (&start); + if (line_number == 0) + { + gtk_text_buffer_get_iter_at_offset (buffer, + &start, 0); + } + else + { + gtk_text_iter_backward_line (&start); + gtk_text_iter_forward_line (&start); + } + while (!gtk_text_iter_ends_line (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_to_line_end (&end); + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_backward_display_line_start (view, &start); + if (!gtk_text_iter_is_start (&start)) + { + gtk_text_view_backward_display_line (view, &start); + gtk_text_view_forward_display_line_end (view, &start); + } + gtk_text_view_forward_display_line_end (view, &end); + } + else if PANGO_IS_LAYOUT (layout) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + break; + } + break; + + case GAIL_AFTER_OFFSET: + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char(&start); + gtk_text_iter_forward_chars(&end, 2); + break; + case ATK_TEXT_BOUNDARY_WORD_START: + if (gtk_text_iter_inside_word (&end)) + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + start = end; + if (!gtk_text_iter_is_end (&end)) + { + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + } + break; + case ATK_TEXT_BOUNDARY_WORD_END: + gtk_text_iter_forward_word_end (&end); + start = end; + if (!gtk_text_iter_is_end (&end)) + gtk_text_iter_forward_word_end (&end); + break; + case ATK_TEXT_BOUNDARY_SENTENCE_START: + if (gtk_text_iter_inside_sentence (&end)) + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + start = end; + if (!gtk_text_iter_is_end (&end)) + { + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + } + break; + case ATK_TEXT_BOUNDARY_SENTENCE_END: + gtk_text_iter_forward_sentence_end (&end); + start = end; + if (!gtk_text_iter_is_end (&end)) + gtk_text_iter_forward_sentence_end (&end); + break; + case ATK_TEXT_BOUNDARY_LINE_START: + if (layout == NULL) + { + gtk_text_iter_forward_line (&end); + start = end; + gtk_text_iter_forward_line (&end); + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_forward_display_line (view, &end); + start = end; + gtk_text_view_forward_display_line (view, &end); + } + else if (PANGO_IS_LAYOUT (layout)) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + break; + case ATK_TEXT_BOUNDARY_LINE_END: + if (layout == NULL) + { + gtk_text_iter_forward_line (&start); + end = start; + if (!gtk_text_iter_is_end (&start)) + { + while (!gtk_text_iter_ends_line (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_to_line_end (&end); + } + } + else if GTK_IS_TEXT_VIEW (layout) + { + GtkTextView *view = GTK_TEXT_VIEW (layout); + + gtk_text_view_forward_display_line_end (view, &end); + start = end; + gtk_text_view_forward_display_line (view, &end); + gtk_text_view_forward_display_line_end (view, &end); + } + else if (PANGO_IS_LAYOUT (layout)) + get_pango_text_offsets (PANGO_LAYOUT (layout), + buffer, + function, + boundary_type, + offset, + start_offset, + end_offset, + &start, + &end); + break; + } + break; + } + *start_offset = gtk_text_iter_get_offset (&start); + *end_offset = gtk_text_iter_get_offset (&end); + + return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); +} + +/** + * gail_text_util_get_substring: + * @textutil: A #GailTextUtil + * @start_pos: The start position of the substring + * @end_pos: The end position of the substring. + * + * Gets the substring indicated by @start_pos and @end_pos + * + * Returns: the substring indicated by @start_pos and @end_pos + **/ +gchar* +gail_text_util_get_substring (GailTextUtil *textutil, + gint start_pos, + gint end_pos) +{ + GtkTextIter start, end; + GtkTextBuffer *buffer; + + g_return_val_if_fail(GAIL_IS_TEXT_UTIL (textutil), NULL); + + buffer = textutil->buffer; + if (buffer == NULL) + return NULL; + + gtk_text_buffer_get_iter_at_offset (buffer, &start, start_pos); + if (end_pos < 0) + gtk_text_buffer_get_end_iter (buffer, &end); + else + gtk_text_buffer_get_iter_at_offset (buffer, &end, end_pos); + + return gtk_text_buffer_get_text (buffer, &start, &end, FALSE); +} + +static void +get_pango_text_offsets (PangoLayout *layout, + GtkTextBuffer *buffer, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset, + GtkTextIter *start_iter, + GtkTextIter *end_iter) +{ + PangoLayoutIter *iter; + PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL; + gint index, start_index, end_index; + const gchar *text; + gboolean found = FALSE; + + text = pango_layout_get_text (layout); + index = g_utf8_offset_to_pointer (text, offset) - text; + iter = pango_layout_get_iter (layout); + do + { + line = pango_layout_iter_get_line (iter); + start_index = line->start_index; + end_index = start_index + line->length; + + if (index >= start_index && index <= end_index) + { + /* + * Found line for offset + */ + switch (function) + { + case GAIL_BEFORE_OFFSET: + /* + * We want the previous line + */ + if (prev_line) + { + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_LINE_START: + end_index = start_index; + start_index = prev_line->start_index; + break; + case ATK_TEXT_BOUNDARY_LINE_END: + if (prev_prev_line) + start_index = prev_prev_line->start_index + + prev_prev_line->length; + end_index = prev_line->start_index + prev_line->length; + break; + default: + g_assert_not_reached(); + } + } + else + start_index = end_index = 0; + break; + case GAIL_AT_OFFSET: + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_LINE_START: + if (pango_layout_iter_next_line (iter)) + end_index = pango_layout_iter_get_line (iter)->start_index; + break; + case ATK_TEXT_BOUNDARY_LINE_END: + if (prev_line) + start_index = prev_line->start_index + + prev_line->length; + break; + default: + g_assert_not_reached(); + } + break; + case GAIL_AFTER_OFFSET: + /* + * We want the next line + */ + if (pango_layout_iter_next_line (iter)) + { + line = pango_layout_iter_get_line (iter); + switch (boundary_type) + { + case ATK_TEXT_BOUNDARY_LINE_START: + start_index = line->start_index; + if (pango_layout_iter_next_line (iter)) + end_index = pango_layout_iter_get_line (iter)->start_index; + else + end_index = start_index + line->length; + break; + case ATK_TEXT_BOUNDARY_LINE_END: + start_index = end_index; + end_index = line->start_index + line->length; + break; + default: + g_assert_not_reached(); + } + } + else + start_index = end_index; + break; + } + found = TRUE; + break; + } + prev_prev_line = prev_line; + prev_line = line; + } + while (pango_layout_iter_next_line (iter)); + + if (!found) + { + start_index = prev_line->start_index + prev_line->length; + end_index = start_index; + } + pango_layout_iter_free (iter); + *start_offset = g_utf8_pointer_to_offset (text, text + start_index); + *end_offset = g_utf8_pointer_to_offset (text, text + end_index); + + gtk_text_buffer_get_iter_at_offset (buffer, start_iter, *start_offset); + gtk_text_buffer_get_iter_at_offset (buffer, end_iter, *end_offset); +} diff --git a/modules/other/gail/libgail-util/gailtextutil.h b/modules/other/gail/libgail-util/gailtextutil.h new file mode 100644 index 000000000..530e699b0 --- /dev/null +++ b/modules/other/gail/libgail-util/gailtextutil.h @@ -0,0 +1,91 @@ +/* GAIL - The GNOME Accessibility Implementation Library + * Copyright 2001 Sun Microsystems Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GAIL_TEXT_UTIL_H__ +#define __GAIL_TEXT_UTIL_H__ + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus */ + +#include <glib-object.h> +#include <gtk/gtk.h> + +#define GAIL_TYPE_TEXT_UTIL (gail_text_util_get_type ()) +#define GAIL_TEXT_UTIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GAIL_TYPE_TEXT_UTIL, GailTextUtil)) +#define GAIL_TEXT_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GAIL_TYPE_TEXT_UTIL, GailTextUtilClass)) +#define GAIL_IS_TEXT_UTIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GAIL_TYPE_TEXT_UTIL)) +#define GAIL_IS_TEXT_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GAIL_TYPE_TEXT_UTIL)) +#define GAIL_TEXT_UTIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GAIL_TYPE_TEXT_UTIL, GailTextUtilClass)) + +/** + *GailOffsetType: + *@GAIL_BEFORE_OFFSET: Text before offset is required. + *@GAIL_AT_OFFSET: Text at offset is required, + *@GAIL_AFTER_OFFSET: Text after offset is required. + * + * Specifies which of the functions atk_text_get_text_before_offset(), + * atk_text_get_text_at_offset(), atk_text_get_text_after_offset() the + * function gail_text_util_get_text() is being called for. + **/ +typedef enum +{ + GAIL_BEFORE_OFFSET, + GAIL_AT_OFFSET, + GAIL_AFTER_OFFSET +}GailOffsetType; + +typedef struct _GailTextUtil GailTextUtil; +typedef struct _GailTextUtilClass GailTextUtilClass; + +struct _GailTextUtil +{ + GObject parent; + + GtkTextBuffer *buffer; +}; + +struct _GailTextUtilClass +{ + GObjectClass parent_class; +}; + +GType gail_text_util_get_type (void); +GailTextUtil* gail_text_util_new (void); + +void gail_text_util_text_setup (GailTextUtil *textutil, + const gchar *text); +void gail_text_util_buffer_setup (GailTextUtil *textutil, + GtkTextBuffer *buffer); +gchar* gail_text_util_get_text (GailTextUtil *textutil, + gpointer layout, + GailOffsetType function, + AtkTextBoundary boundary_type, + gint offset, + gint *start_offset, + gint *end_offset); +gchar* gail_text_util_get_substring (GailTextUtil *textutil, + gint start_pos, + gint end_pos); + +#ifdef __cplusplus +} +#endif /*cplusplus */ + +#endif /*__GAIL_TEXT_UTIL_H__ */ diff --git a/modules/other/gail/libgail-util/gailutil.def b/modules/other/gail/libgail-util/gailutil.def new file mode 100644 index 000000000..2077be15f --- /dev/null +++ b/modules/other/gail/libgail-util/gailutil.def @@ -0,0 +1,15 @@ +EXPORTS + gail_misc_add_attribute + gail_misc_add_to_attr_set + gail_misc_buffer_get_run_attributes + gail_misc_get_default_attributes + gail_misc_get_extents_from_pango_rectangle + gail_misc_get_index_at_point_in_layout + gail_misc_get_origins + gail_misc_layout_get_run_attributes + gail_text_util_buffer_setup + gail_text_util_get_substring + gail_text_util_get_text + gail_text_util_get_type + gail_text_util_new + gail_text_util_text_setup diff --git a/po/POTFILES.in b/po/POTFILES.in index 5070ca94b..a92e21455 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -17,6 +17,7 @@ gdk-pixbuf/io-bmp.c gdk-pixbuf/io-gif.c gdk-pixbuf/io-ico.c gdk-pixbuf/io-icns.c +gdk-pixbuf/io-jasper.c gdk-pixbuf/io-jpeg.c gdk-pixbuf/io-pcx.c gdk-pixbuf/io-png.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 0f3232e19..736e9548a 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -1,2 +1,4 @@ demos/gtk-demo/demo.ui gtk/paper_names.c +modules/other/gail/gailimage.c + |