/* GTK+ - accessibility implementations
 * 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, see <http://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <string.h>
#include <gtk/gtk.h>
#include "gtknotebookpageaccessible.h"


struct _GtkNotebookPageAccessiblePrivate
{
  GtkAccessible *notebook;
  GtkWidget *child;
};

static void atk_component_interface_init (AtkComponentIface *iface);

G_DEFINE_TYPE_WITH_CODE (GtkNotebookPageAccessible, gtk_notebook_page_accessible, ATK_TYPE_OBJECT,
                         G_ADD_PRIVATE (GtkNotebookPageAccessible)
                         G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))


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

static GtkWidget *
get_label_from_notebook_page (GtkNotebookPageAccessible *page)
{
  GtkWidget *child;
  GtkNotebook *notebook;

  notebook = GTK_NOTEBOOK (gtk_accessible_get_widget (page->priv->notebook));
  if (!notebook)
    return NULL;

  if (!gtk_notebook_get_show_tabs (notebook))
    return NULL;

  child = gtk_notebook_get_tab_label (notebook, page->priv->child);

  if (GTK_IS_LABEL (child))
    return child;

  if (GTK_IS_CONTAINER (child))
    child = find_label_child (GTK_CONTAINER (child));

  return child;
}

static const gchar *
gtk_notebook_page_accessible_get_name (AtkObject *accessible)
{
  GtkWidget *label;

  if (accessible->name != NULL)
    return accessible->name;

  label = get_label_from_notebook_page (GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible));
  if (GTK_IS_LABEL (label))
    return gtk_label_get_text (GTK_LABEL (label));

  return NULL;
}

static AtkObject *
gtk_notebook_page_accessible_get_parent (AtkObject *accessible)
{
  GtkNotebookPageAccessible *page;

  page = GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible);

  return ATK_OBJECT (page->priv->notebook);
}

static gint
gtk_notebook_page_accessible_get_n_children (AtkObject *accessible)
{
  return 1;
}

static AtkObject *
gtk_notebook_page_accessible_ref_child (AtkObject *accessible,
                                        gint       i)
{
  AtkObject *child_obj;
  GtkNotebookPageAccessible *page = NULL;

  if (i != 0)
    return NULL;

  page = GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible);
  if (!page->priv->child)
    return NULL;

  child_obj = gtk_widget_get_accessible (page->priv->child);
  g_object_ref (child_obj);

  return child_obj;
}

static AtkStateSet *
gtk_notebook_page_accessible_ref_state_set (AtkObject *accessible)
{
  GtkNotebookPageAccessible *page = GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible);
  AtkStateSet *state_set, *label_state_set, *merged_state_set;
  AtkObject *atk_label;
  GtkWidget *label;
  AtkObject *selected;

  state_set = ATK_OBJECT_CLASS (gtk_notebook_page_accessible_parent_class)->ref_state_set (accessible);

  atk_state_set_add_state (state_set, ATK_STATE_SELECTABLE);

  selected = atk_selection_ref_selection (ATK_SELECTION (page->priv->notebook), 0);
  if (selected)
    {
      if (selected == accessible)
        atk_state_set_add_state (state_set, ATK_STATE_SELECTED);

      g_object_unref (selected);
    }

  label = get_label_from_notebook_page (GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible));
  if (label)
    {
      atk_label = gtk_widget_get_accessible (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);
      if (!child)
        return 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 gint
gtk_notebook_page_accessible_get_index_in_parent (AtkObject *accessible)
{
  GtkNotebookPageAccessible *page;

  page = GTK_NOTEBOOK_PAGE_ACCESSIBLE (accessible);
  if (!page->priv->child)
    return -1;

  return gtk_notebook_page_num (GTK_NOTEBOOK (gtk_accessible_get_widget (page->priv->notebook)),
                                page->priv->child);
}

static void
gtk_notebook_page_accessible_class_init (GtkNotebookPageAccessibleClass *klass)
{
  AtkObjectClass *class = ATK_OBJECT_CLASS (klass);

  class->get_name = gtk_notebook_page_accessible_get_name;
  class->get_parent = gtk_notebook_page_accessible_get_parent;
  class->get_n_children = gtk_notebook_page_accessible_get_n_children;
  class->ref_child = gtk_notebook_page_accessible_ref_child;
  class->ref_state_set = gtk_notebook_page_accessible_ref_state_set;
  class->get_index_in_parent = gtk_notebook_page_accessible_get_index_in_parent;
}

static void
gtk_notebook_page_accessible_init (GtkNotebookPageAccessible *page)
{
  page->priv = gtk_notebook_page_accessible_get_instance_private (page);
}

static void
notify_tab_label (GObject    *object,
                  GParamSpec *pspec,
                  AtkObject  *atk_obj)
{
  if (atk_obj->name == NULL)
    g_object_notify (G_OBJECT (atk_obj), "accessible-name");
  g_signal_emit_by_name (atk_obj, "visible-data-changed");
}

AtkObject *
gtk_notebook_page_accessible_new (GtkNotebookAccessible *notebook,
                                  GtkWidget             *child)
{
  GObject *object;
  AtkObject *atk_object;
  GtkNotebookPageAccessible *page;

  g_return_val_if_fail (GTK_IS_NOTEBOOK_ACCESSIBLE (notebook), NULL);
  g_return_val_if_fail (GTK_WIDGET (child), NULL);

  object = g_object_new (GTK_TYPE_NOTEBOOK_PAGE_ACCESSIBLE, NULL);

  page = GTK_NOTEBOOK_PAGE_ACCESSIBLE (object);
  page->priv->notebook = GTK_ACCESSIBLE (notebook);
  page->priv->child = child;

  atk_object = ATK_OBJECT (page);
  atk_object->role = ATK_ROLE_PAGE_TAB;
  atk_object->layer = ATK_LAYER_WIDGET;

  atk_object_set_parent (gtk_widget_get_accessible (child), atk_object);

  g_signal_connect (gtk_accessible_get_widget (page->priv->notebook),
                    "child-notify::tab-label",
                    G_CALLBACK (notify_tab_label), page);

  return atk_object;
}

void
gtk_notebook_page_accessible_invalidate (GtkNotebookPageAccessible *page)
{
  AtkObject *obj = ATK_OBJECT (page);
  GtkWidget *notebook;

  notebook = gtk_accessible_get_widget (page->priv->notebook);
  if (notebook)
    g_signal_handlers_disconnect_by_func (notebook, notify_tab_label, page);

  atk_object_notify_state_change (obj, ATK_STATE_DEFUNCT, TRUE);
  atk_object_set_parent (obj, NULL);
  page->priv->notebook = NULL;
  atk_object_set_parent (gtk_widget_get_accessible (page->priv->child), NULL);
  page->priv->child = NULL;
}

static AtkObject*
gtk_notebook_page_accessible_ref_accessible_at_point (AtkComponent *component,
                                                      gint          x,
                                                      gint          y,
                                                      AtkCoordType  coord_type)
{
  /* There is only one child so we return it */
  AtkObject* child;

  child = atk_object_ref_accessible_child (ATK_OBJECT (component), 0);

  return child;
}

static void
gtk_notebook_page_accessible_get_extents (AtkComponent *component,
                                          gint         *x,
                                          gint         *y,
                                          gint         *width,
                                          gint         *height,
                                          AtkCoordType  coord_type)
{
  GtkWidget *label;
  AtkObject *atk_label;

  label = get_label_from_notebook_page (GTK_NOTEBOOK_PAGE_ACCESSIBLE (component));
  if (!label)
    {
      *x = -1;
      *y = -1;
      *width = -1;
      *height = -1;
    }
  else
    {
      atk_label = gtk_widget_get_accessible (label);
      atk_component_get_extents (ATK_COMPONENT (atk_label),
                                 x, y, width, height, coord_type);
    }
}

static void
atk_component_interface_init (AtkComponentIface *iface)
{
  /* We use the default implementations for contains, get_position, get_size */
  iface->ref_accessible_at_point = gtk_notebook_page_accessible_ref_accessible_at_point;
  iface->get_extents = gtk_notebook_page_accessible_get_extents;
}
