/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
/* eggassistant.c
 * Copyright (C) 2005 Carlos Garnacho Parro
 *
 * 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 "eggassistant.h"

#define EGG_ASSISTANT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_TYPE_ASSISTANT, EggAssistantPrivate))

typedef struct _EggAssistantPrivate EggAssistantPrivate;

struct _EggAssistantPrivate
{
  GtkWidget *event_box;

  GtkWidget *table;
  GtkWidget *title;
  GtkWidget *image;

  GtkWidget *watermark;
  GtkWidget *notebook_event_box;
  GtkWidget *notebook;

  GSList    *visited_pages;

  gboolean   back_sensitive;

  EggAssistantPageFunc forward_function;
  gpointer forward_function_data;

  EggAssistantPageFunc back_function;
  gpointer back_function_data;
};

static void egg_assistant_class_init (EggAssistantClass *class);
static void egg_assistant_init       (EggAssistant      *assistant);
static void egg_assistant_finalize   (GObject           *object);

static void egg_assistant_style_set    (GtkWidget    *widget,
					GtkStyle     *old_style);

static void egg_assistant_map          (GtkWidget    *widget);


G_DEFINE_TYPE (EggAssistant, egg_assistant, GTK_TYPE_DIALOG);


static void
egg_assistant_class_init (EggAssistantClass *class)
{
  GObjectClass   *object_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);

  object_class->finalize = egg_assistant_finalize;

  widget_class->style_set = egg_assistant_style_set;
  widget_class->map       = egg_assistant_map;

  g_type_class_add_private (class, sizeof (EggAssistantPrivate));
}

static void
on_dialog_response (GtkDialog *dialog,
		    gint       response_id)
{
  /* should just exit on delete_event, close or apply */
  if (response_id == GTK_RESPONSE_DELETE_EVENT ||
      response_id == GTK_RESPONSE_CLOSE ||
      response_id == GTK_RESPONSE_APPLY)
    return;

  /* otherwise we retain the dialog */
  g_signal_stop_emission_by_name (G_OBJECT (dialog), "response");
}

static void
init_buttons (EggAssistant *assistant)
{
  GtkSizeGroup *size_group;

  size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
  assistant->close   = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
  assistant->back    = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
  assistant->forward = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
  assistant->apply   = gtk_button_new_from_stock (GTK_STOCK_APPLY);

  gtk_size_group_add_widget (size_group, assistant->close);
  gtk_size_group_add_widget (size_group, assistant->back);
  gtk_size_group_add_widget (size_group, assistant->forward);
  gtk_size_group_add_widget (size_group, assistant->apply);

  gtk_dialog_add_action_widget (GTK_DIALOG (assistant), assistant->close,   GTK_RESPONSE_CLOSE);
  gtk_dialog_add_action_widget (GTK_DIALOG (assistant), assistant->back,    GTK_RESPONSE_NONE);
  gtk_dialog_add_action_widget (GTK_DIALOG (assistant), assistant->forward, GTK_RESPONSE_NONE);
  gtk_dialog_add_action_widget (GTK_DIALOG (assistant), assistant->apply,   GTK_RESPONSE_APPLY);
}

static void
configure_page_appearance (EggAssistant *assistant, EggAssistantPage *page)
{
  EggAssistantPrivate *priv;
  gchar *title;
  GdkPixbuf *header_image, *watermark;

  if (GTK_WIDGET_MAPPED (page))
    {
      priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

      g_object_get (G_OBJECT (page),
		    "title", &title,
		    "header-image", &header_image,
		    "watermark", &watermark,
		    NULL);

      gtk_label_set_text (GTK_LABEL (priv->title), title);
      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), header_image);
      gtk_image_set_from_pixbuf (GTK_IMAGE (priv->watermark), watermark);
    }
}

static void
configure_assistant_buttons (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;
  gint n_pages, current_page;

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
  current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));

  gtk_widget_show (assistant->close);
  gtk_widget_show (assistant->back);

  gtk_widget_set_sensitive (assistant->back,
			    (current_page != 0 && priv->back_sensitive));

  if (current_page != -1 &&
      current_page < n_pages - 1)
    {
      gtk_widget_show (assistant->forward);
      gtk_widget_hide (assistant->apply);
    }
  else
    {
      gtk_widget_hide (assistant->forward);
      gtk_widget_show (assistant->apply);
    }
}

static void
_egg_assistant_set_current_page (EggAssistant *assistant, gint n_page)
{
  EggAssistantPrivate *priv;
  EggAssistantPage *page;

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);
  page = egg_assistant_get_nth_page (assistant, n_page);

  egg_assistant_page_prepare (page);

  gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), n_page);
  configure_page_appearance (assistant, page);
  configure_assistant_buttons (assistant);
}

static void
init_event_box (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  priv->event_box = gtk_event_box_new ();
  gtk_container_set_border_width (GTK_CONTAINER (priv->event_box), 5);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (assistant)->vbox), priv->event_box, TRUE, TRUE, 0);
}

static void
init_body (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;
  GtkWidget *vbox, *header_hbox, *hbox;

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  vbox = gtk_vbox_new (FALSE, 0);

  header_hbox = gtk_hbox_new (FALSE, 12);
  gtk_container_set_border_width (GTK_CONTAINER (header_hbox), 6);

  hbox = gtk_hbox_new (FALSE, 0);
  gtk_container_set_border_width (GTK_CONTAINER (hbox), 1);

  /* Header */
  priv->title = gtk_label_new (NULL);
  gtk_misc_set_alignment (GTK_MISC (priv->title), 0.0, 0.0);
  gtk_box_pack_start (GTK_BOX (header_hbox), priv->title, TRUE, TRUE, 0);

  priv->image = gtk_image_new ();
  gtk_box_pack_start (GTK_BOX (header_hbox), priv->image, FALSE, FALSE, 0);

  /* watermark */
  priv->watermark = gtk_image_new ();
  gtk_box_pack_start (GTK_BOX (hbox), priv->watermark, FALSE, FALSE, 0);

  /* pages placeholder */
  priv->notebook = gtk_notebook_new ();
  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
  gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);

  priv->notebook_event_box = gtk_event_box_new ();
  gtk_container_add (GTK_CONTAINER (priv->notebook_event_box), priv->notebook);
  
  gtk_box_pack_start (GTK_BOX (hbox), priv->notebook_event_box, TRUE, TRUE, 0);

  /* put it all in the event box */
  gtk_box_pack_start (GTK_BOX (vbox), header_hbox, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  gtk_container_add (GTK_CONTAINER (priv->event_box), vbox);

  g_signal_connect (G_OBJECT (assistant), "response",
		    G_CALLBACK (on_dialog_response), NULL);
}

static gint
default_forward_func (gint current, gpointer data)
{
  return ++current;
}

static gint
default_back_func (gint current, gpointer data)
{
  EggAssistantPrivate *priv;
  EggAssistant *assistant;
  GSList *element;
  gint next_page;

  g_return_val_if_fail (EGG_IS_ASSISTANT (data), -1);

  assistant = EGG_ASSISTANT (data);
  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  element = priv->visited_pages;
  priv->visited_pages = priv->visited_pages->next;
  next_page = (gint) element->data;
  g_slist_free_1 (element);

  return next_page;
}

static void
on_forward_clicked (GtkWidget *widget, gpointer data)
{
  EggAssistantPrivate *priv;
  EggAssistant *assistant;
  gint current_page, next_page, n_pages;

  assistant = EGG_ASSISTANT (data);
  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  current_page = egg_assistant_get_current_page (assistant);
  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));

  next_page = (priv->forward_function) (current_page,
					priv->forward_function_data);

  g_return_if_fail (next_page >= 0 && next_page < n_pages);

  _egg_assistant_set_current_page (assistant, next_page);
  priv->visited_pages = g_slist_prepend (priv->visited_pages, GINT_TO_POINTER (current_page));
}

static void
on_back_clicked (GtkWidget *widget, gpointer data)
{
  EggAssistantPrivate *priv;
  EggAssistant *assistant;
  gint next_page, n_pages;

  assistant = EGG_ASSISTANT (data);
  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  n_pages   = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));

  next_page = (priv->back_function) (egg_assistant_get_current_page (assistant),
				     priv->back_function_data);

  g_return_if_fail (next_page >= 0 && next_page < n_pages);

  _egg_assistant_set_current_page (assistant, next_page);
}

static void
egg_assistant_init (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  init_buttons (assistant);
  init_event_box (assistant);
  init_body (assistant);

  priv->back_sensitive = TRUE;

  priv->forward_function = default_forward_func;
  priv->forward_function_data = assistant;

  priv->back_function = default_back_func;
  priv->back_function_data = assistant;

  g_signal_connect (G_OBJECT (assistant->forward), "clicked",
		    G_CALLBACK (on_forward_clicked), assistant);
  g_signal_connect (G_OBJECT (assistant->back), "clicked",
		    G_CALLBACK (on_back_clicked), assistant);

  gtk_widget_show_all (priv->event_box);
  configure_assistant_buttons (assistant);
}

static void
egg_assistant_finalize (GObject *object)
{
  EggAssistantPrivate *priv;
  EggAssistant *assistant;

  assistant = EGG_ASSISTANT (object);
  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  if (priv)
    g_slist_free (priv->visited_pages);

  if (G_OBJECT_CLASS (egg_assistant_parent_class)->finalize)
    (* G_OBJECT_CLASS (egg_assistant_parent_class)->finalize) (object);
}

static void
notify_handle (EggAssistantPage *page, GParamSpec *pspec, gpointer data)
{
  EggAssistant *assistant = EGG_ASSISTANT (data);
  
  configure_page_appearance (assistant, page);
}


static void
_listen_page_notifications (EggAssistant *assistant, EggAssistantPage *page)
{
  g_signal_connect (G_OBJECT (page), "notify::title",
		    G_CALLBACK (notify_handle), assistant);
  g_signal_connect (G_OBJECT (page), "notify::header-image",
		    G_CALLBACK (notify_handle), assistant);
  g_signal_connect (G_OBJECT (page), "notify::watermark",
		    G_CALLBACK (notify_handle), assistant);
}

static void
set_assistant_colors (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;
  GtkStyle *style;
  GdkColor bg, text;

  g_return_if_fail (assistant != NULL);
  g_return_if_fail (EGG_IS_ASSISTANT (assistant));

  gtk_widget_ensure_style (GTK_WIDGET (assistant));
  style = gtk_widget_get_style (GTK_WIDGET (assistant));

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  /* change colors schema, for making the header visible */
  gtk_widget_modify_bg (priv->event_box, GTK_STATE_NORMAL, &style->bg[GTK_STATE_SELECTED]);

  gtk_widget_modify_bg (priv->title, GTK_STATE_NORMAL, &style->bg[GTK_STATE_SELECTED]);
  gtk_widget_modify_fg (priv->title, GTK_STATE_NORMAL, &style->fg[GTK_STATE_SELECTED]);
}

static void
set_title_font (EggAssistant *assistant)
{
  EggAssistantPrivate  *priv;
  PangoFontDescription *desc;
  gint size;

  g_return_if_fail (assistant != NULL);
  g_return_if_fail (EGG_IS_ASSISTANT (assistant));

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);
  desc = pango_font_description_new ();
  size = pango_font_description_get_size (GTK_WIDGET (assistant)->style->font_desc);

  pango_font_description_set_weight (desc, PANGO_WEIGHT_ULTRABOLD);
  pango_font_description_set_size   (desc, size * PANGO_SCALE_XX_LARGE);

  gtk_widget_modify_font (priv->title, desc);
  pango_font_description_free (desc);
}

static void
egg_assistant_style_set (GtkWidget *widget,
			 GtkStyle  *old_style)
{
  set_assistant_colors (EGG_ASSISTANT (widget));
  set_title_font (EGG_ASSISTANT (widget));

  GTK_WIDGET_CLASS (egg_assistant_parent_class)->style_set (widget, old_style);
}

static void
egg_assistant_map (GtkWidget *widget)
{
  EggAssistantPrivate *priv;
  EggAssistant *assistant;

  if (GTK_WIDGET_CLASS (egg_assistant_parent_class)->map)
    GTK_WIDGET_CLASS (egg_assistant_parent_class)->map (widget);

  _egg_assistant_set_current_page (EGG_ASSISTANT (widget), 0);
}

GtkWidget*
egg_assistant_new (GtkWindow *parent)
{
  GtkWidget *assistant;

  assistant = g_object_new (EGG_TYPE_ASSISTANT,
			    "has-separator", FALSE,
			    NULL);

  if (parent && GTK_IS_WINDOW (parent))
    gtk_window_set_transient_for (GTK_WINDOW (assistant), GTK_WINDOW (parent));

  return assistant;
}

gint
egg_assistant_get_current_page (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;

  g_return_val_if_fail (assistant != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), -1);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  return gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
}

gint
egg_assistant_get_n_pages (EggAssistant *assistant)
{
  EggAssistantPrivate *priv;

  g_return_val_if_fail (assistant != NULL, 0);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), 0);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  return gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
}

EggAssistantPage*
egg_assistant_get_nth_page (EggAssistant *assistant, gint n_page)
{
  EggAssistantPrivate *priv;
  GtkWidget *page;

  g_return_val_if_fail (assistant != NULL, NULL);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), NULL);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (priv->notebook), n_page);

  if (page)
    g_return_val_if_fail (EGG_IS_ASSISTANT_PAGE (page), NULL);

  return EGG_ASSISTANT_PAGE (page);
}

gint
egg_assistant_prepend_page (EggAssistant *assistant, EggAssistantPage *page)
{
  EggAssistantPrivate *priv;
  gint n_page;

  g_return_val_if_fail (assistant != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), -1);
  g_return_val_if_fail (page != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT_PAGE (page), -1);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  n_page = gtk_notebook_prepend_page (GTK_NOTEBOOK (priv->notebook), GTK_WIDGET (page), NULL);
  _listen_page_notifications (assistant, page);

  return n_page;
}

gint
egg_assistant_append_page (EggAssistant *assistant, EggAssistantPage *page)
{
  EggAssistantPrivate *priv;
  gint n_page;

  g_return_val_if_fail (assistant != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), -1);
  g_return_val_if_fail (page != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT_PAGE (page), -1);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  n_page = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), GTK_WIDGET (page), NULL);
  _listen_page_notifications (assistant, page);

  return n_page;
}

gint
egg_assistant_insert_page (EggAssistant *assistant, EggAssistantPage *page, gint position)
{
  EggAssistantPrivate *priv;
  gint n_page;

  g_return_val_if_fail (assistant != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT (assistant), -1);
  g_return_val_if_fail (page != NULL, -1);
  g_return_val_if_fail (EGG_IS_ASSISTANT_PAGE (page), -1);

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  n_page = gtk_notebook_insert_page (GTK_NOTEBOOK (priv->notebook), GTK_WIDGET (page), NULL, position);
  _listen_page_notifications (assistant, page);

  return n_page;
}

void
egg_assistant_set_forward_page_func (EggAssistant         *assistant,
				     EggAssistantPageFunc  page_func,
				     gpointer              data)
{
  EggAssistantPrivate *priv;

  g_return_if_fail (assistant != NULL);
  g_return_if_fail (EGG_IS_ASSISTANT (assistant));

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);
  
  if (!page_func)
    {
      priv->forward_function = default_forward_func;
      priv->forward_function_data = assistant;
    }
  else
    {
      priv->forward_function = page_func;
      priv->forward_function_data = data;
    }
}

void
egg_assistant_set_back_page_func (EggAssistant         *assistant,
				  EggAssistantPageFunc  page_func,
				  gpointer              data)
{
  EggAssistantPrivate *priv;

  g_return_if_fail (assistant != NULL);
  g_return_if_fail (EGG_IS_ASSISTANT (assistant));

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);
  
  if (!page_func)
    {
      priv->back_function = default_back_func;
      priv->back_function_data = assistant;
    }
  else
    {
      priv->forward_function = page_func;
      priv->forward_function_data = data;
    }
}

void
egg_assistant_set_buttons_sensitive (EggAssistant *assistant,
				     gboolean      close_sensitive,
				     gboolean      back_sensitive,
				     gboolean      forward_sensitive)
{
  EggAssistantPrivate *priv;

  g_return_if_fail (assistant != NULL);
  g_return_if_fail (EGG_IS_ASSISTANT (assistant));

  priv = EGG_ASSISTANT_GET_PRIVATE (assistant);

  gtk_widget_set_sensitive (assistant->close,   close_sensitive);
  gtk_widget_set_sensitive (assistant->back,    back_sensitive);
  gtk_widget_set_sensitive (assistant->forward, forward_sensitive);
  gtk_widget_set_sensitive (assistant->apply,   forward_sensitive);

  priv->back_sensitive = back_sensitive;
}
