<commit_before>/* 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 "config.h"

#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_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);

G_DEFINE_TYPE_WITH_CODE (GailButton, gail_button, GAIL_TYPE_CONTAINER,
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE, atk_image_interface_init)
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))

static void
gail_button_class_init (GailButtonClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
  GailContainerClass *container_class;

  container_class = (GailContainerClass*)klass;

  gobject_class->finalize = gail_button_finalize;

  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_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;
}

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 (gail_button_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 (gail_button_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;

  if (button->textutil)
    g_object_unref (button->textutil);
  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)
{
  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 = gdk_threads_add_idle (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;

  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;

  g_object_ref (gail_button);

  if (widget == NULL /* State is defunct */ ||
      !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
    {
      g_object_unref (gail_button);
      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:
	  /* first a press */ 

	  button->in_button = TRUE;
	  g_signal_emit_by_name (button, "enter");
	  /*
	   * 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;<commit_msg>Fix compilation warning<commit_after>/* 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 "config.h"

#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_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);

G_DEFINE_TYPE_WITH_CODE (GailButton, gail_button, GAIL_TYPE_CONTAINER,
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, atk_action_interface_init)
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_IMAGE, atk_image_interface_init)
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))

static void
gail_button_class_init (GailButtonClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
  AtkObjectClass *class = ATK_OBJECT_CLASS (klass);
  GailContainerClass *container_class;

  container_class = (GailContainerClass*)klass;

  gobject_class->finalize = gail_button_finalize;

  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_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;
}

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 (gail_button_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 (gail_button_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;

  if (button->textutil)
    g_object_unref (button->textutil);
  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)
{
  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, GINT_TO_POINTER(i));
      if (!button->action_idle_handler)
	button->action_idle_handler = gdk_threads_add_idle (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;

  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;

  g_object_ref (gail_button);

  if (widget == NULL /* State is defunct */ ||
      !GTK_WIDGET_IS_SENSITIVE (widget) || !GTK_WIDGET_VISIBLE (widget))
    {
      g_object_unref (gail_button);
      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 = GPOINTER_TO_INT(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:
	  /* first a press */ 

	  button->in_button = TRUE;
	  g_signal_emit_by_name (button, "enter");
	  /*
	   * 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;
{'fraction_non_alphanumeric': 0.06930176774260834, 'fraction_numerical': 0.002242269385201022, 'mean_word_length': 1.6373513030323867, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 113, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 162, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1532705', 'n_tokens_mistral': 11487, 'n_tokens_neox': 10987, 'n_words': 2361}