/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "kz-tab-label.h"

#include <stdlib.h>
#include <string.h>
#include "gobject-utils.h"
#include "gtk-utils.h"
#include "intl.h"
#include "kazehakase.h"
#include "kz-actions.h"
#include "kz-actions-tab.h"
#include "kz-icons.h"
#include "kz-favicon.h"
#include "kz-input-event-box.h"


enum {
	PROP_0,
	PROP_KZ_WINDOW,
	PROP_KZ_MOZ_EMBED
};


struct _KzTabLabelPriv
{
	gint width;
	gint start_x, start_y;
	gboolean moved;
	gboolean lock;
	gulong favicon_signal;
};

#warning FIXME!

const gchar *label_color[] =
{
	"#000000",
	"#ff0000",
	"#22aa44",
};

/* object class */
static void     kz_tab_label_class_init         (KzTabLabelClass *klass);
static void     kz_tab_label_init               (KzTabLabel *kztab);
static GObject *kz_tab_label_constructor        (GType type,
						 guint n_props,
						 GObjectConstructParam *props);
static void     kz_tab_label_dispose            (GObject *object);
static void     kz_tab_label_finalize           (GObject *object);
static void     kz_tab_label_set_property       (GObject *object,
						 guint prop_id,
						 const GValue *value,
						 GParamSpec *pspec);
static void     kz_tab_label_get_property       (GObject *object,
						 guint prop_id,
						 GValue *value,
						 GParamSpec *pspec);

/* widget class */
static void     kz_tab_label_realize            (GtkWidget *widget);
#if 0
static void     kz_tab_label_unrealize          (GtkWidget *widget);
#endif
static gboolean kz_tab_label_button_press       (GtkWidget *widget,
						 GdkEventButton *event);
static gboolean kz_tab_label_motion_notify      (GtkWidget *widget,
						 GdkEventMotion *event);
static gboolean kz_tab_label_button_release     (GtkWidget *widget,
						 GdkEventButton *event);
static gboolean kz_tab_label_scroll_event       (GtkWidget *widget,
						 GdkEventScroll *event);
static gboolean kz_tab_label_drag_motion        (GtkWidget *widget,
						 GdkDragContext *drag_context,
						 gint x, gint y,
						 guint time);
static void     kz_tab_label_drag_data_get      (GtkWidget        *widget,
						 GdkDragContext   *context,
						 GtkSelectionData *seldata,
						 guint             info,
						 guint             time);
static void     kz_tab_label_drag_data_received (GtkWidget *widget,
						 GdkDragContext *drag_context,
						 gint x, gint y,
						 GtkSelectionData *data,
						 guint info,
						 guint time);

static void     kz_tab_label_sync_to_profile    (KzTabLabel *kztab);
static void     kz_tab_label_move_page          (KzTabLabel *kztab,
						 KzTabLabel *dest_kztab);

static void     cb_close_button_clicked         (GtkWidget *button,
						 KzTabLabel *kztab);

static void     cb_profile_changed              (KzProfile *profile,
						 const gchar *section,
						 const gchar *key,
						 const gchar *old_value,
						 KzTabLabel *kztab);

static void     cb_favicon_changed              (KzFavicon *kzfav,
						 gchar *uri,
						 KzTabLabel *kztab);
static void     cb_close_button_state_changed   (GtkWidget *widget,
						 GtkStateType previous_state);

/* callbacks for mozilla */
static void cb_title_changed (GtkMozEmbed *embed, KzTabLabel *kztab);
static void cb_net_start     (GtkMozEmbed *embed, KzTabLabel *kztab);
static void cb_net_stop      (GtkMozEmbed *embed, KzTabLabel *kztab);
static void cb_destroy       (GtkObject *object,  KzTabLabel *kztab);
static void cb_net_state_all (GtkMozEmbed *embed, const char *aURI,
			      gint state, gint status,
			      KzTabLabel *kztab);

static void make_progress_circle (GtkWidget *widget);

static GtkHBoxClass *parent_class = NULL;

enum {
	TARGET_KAZEHAKASE_TAB,
	TARGET_NETSCAPE_URL,
	TARGET_TEXT_URI_LIST,
	TARGET_TEXT_PLAIN,
	TARGET_STRING
};

static GtkTargetEntry url_drag_types [] = 
{
        { "_KAZEHAKASE_TAB", 0, TARGET_KAZEHAKASE_TAB},
        { "_NETSCAPE_URL",   0, TARGET_NETSCAPE_URL},
	{ "text/uri-list",   0, TARGET_TEXT_URI_LIST},
	{ "text/plain",      0, TARGET_TEXT_PLAIN},
	{ "STRING",          0, TARGET_STRING}
};
static guint n_url_drag_types = G_N_ELEMENTS (url_drag_types);

static GdkColor red = { 0, 0xffff, 0x0000, 0x0000 };

KZ_OBJECT_GET_TYPE(kz_tab_label, "KzTabLabel", KzTabLabel,
		   kz_tab_label_class_init, kz_tab_label_init,
		   GTK_TYPE_HBOX)
KZ_OBJECT_FINALIZE (kz_tab_label, KzTabLabel)

static void
kz_tab_label_class_init (KzTabLabelClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;

	parent_class = g_type_class_peek_parent (klass);

	gobject_class = (GObjectClass *) klass;
	widget_class  = (GtkWidgetClass *) klass;

	/* GtkObject signals */
	gobject_class->constructor  = kz_tab_label_constructor;
	gobject_class->dispose      = kz_tab_label_dispose;
	gobject_class->finalize     = kz_tab_label_finalize;
	gobject_class->set_property = kz_tab_label_set_property;
	gobject_class->get_property = kz_tab_label_get_property;

	/* GtkWidget signales */
	widget_class->realize              = kz_tab_label_realize;
#if 0
	widget_class->unrealize            = kz_tab_label_unrealize;
#endif
	widget_class->button_press_event   = kz_tab_label_button_press;
	widget_class->scroll_event         = kz_tab_label_scroll_event;
	widget_class->motion_notify_event  = kz_tab_label_motion_notify;
	widget_class->button_release_event = kz_tab_label_button_release;
	widget_class->drag_motion          = kz_tab_label_drag_motion;
	widget_class->drag_data_get        = kz_tab_label_drag_data_get;
	widget_class->drag_data_received   = kz_tab_label_drag_data_received;

	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object ("kz-window",
				      _("KzWindow"),
				      _("The parent kazehakase window"),
				      KZ_TYPE_WINDOW,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_MOZ_EMBED,
		 g_param_spec_object ("kz-moz-embed",
				      _("KzMozEmbed"),
				      _("The KzMozEmbed object to observe"),
				      KZ_TYPE_MOZ_EMBED,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
}

static void
kz_tab_label_init (KzTabLabel *kztab)
{
	GtkWidget *close_image, *hbox;
	GtkRcStyle *style;
	GtkRequisition size;
	gint width, height;

	kztab->kz           = NULL;
	kztab->kzembed      = NULL;
	kztab->state        = KZ_TAB_LABEL_STATE_NORMAL;
	kztab->favicon      = gtk_image_new();
	kztab->eventbox     = kz_input_event_box_new();
	kztab->label        = gtk_label_new(NULL);
	kztab->close_button = gtk_button_new();
	kztab->lock_button  = gtk_image_new_from_stock(KZ_STOCK_ANCHOR,
						       GTK_ICON_SIZE_MENU);
	kztab->tooltips     = gtk_tooltips_new();

	kztab->priv = g_new0(KzTabLabelPriv, 1);
	kztab->priv->width             = 80;
	kztab->priv->start_x           = 0;
	kztab->priv->start_y           = 0;
	kztab->priv->moved             = FALSE;
	kztab->priv->lock              = FALSE;
	kztab->priv->favicon_signal    = 0;
	
	kztab->favicon_pixbuf   = NULL;
	kztab->circle           = NULL;
	kztab->mask             = NULL;

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(kztab->eventbox), hbox);
	gtk_widget_show(hbox);
	/* 
	 * input only event box 
	 * this widget have a effect widget background display correctly.
	 * See http://bugzilla.gnome.org/show_bug.cgi?id=103206
	 */
	gtk_container_add (GTK_CONTAINER(kztab), kztab->eventbox);
	gtk_widget_show(kztab->eventbox);

	/* label */
	gtk_widget_set_size_request(kztab->label, -1, -1);
	gtk_widget_show (kztab->label);

	/* favicon */
	gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
	gtk_widget_set_size_request(kztab->favicon, width, height);

	/* close button */
	gtk_button_set_relief(GTK_BUTTON(kztab->close_button), GTK_RELIEF_NONE);
	g_signal_connect(G_OBJECT(kztab->close_button), "clicked", 
			 G_CALLBACK(cb_close_button_clicked), kztab);
	/* Picked from galeon-1.3.11a */
	/* work around a cosmetic bug in gtk where buttons with GTK_RELIEF_NONE
	 * have annoyingly visible border only when in GTK_STATE_ACTIVE (ie. in
	 * inactive tabs) */
	g_signal_connect(G_OBJECT (kztab->close_button), "state_changed",
			 G_CALLBACK (cb_close_button_state_changed),
			 NULL);
		
	close_image = gtk_image_new_from_stock(GTK_STOCK_CLOSE,
					       GTK_ICON_SIZE_MENU);
	gtk_widget_size_request(close_image, &size);
	gtk_widget_set_size_request(kztab->close_button,
				    size.width, size.height);
	gtk_container_add(GTK_CONTAINER(kztab->close_button), close_image);
	gtk_widget_show(close_image);

	style = gtk_rc_style_new ();
	style->xthickness = style->ythickness = 0;
	gtk_widget_modify_style (kztab->close_button, style);
	gtk_widget_modify_style (kztab->lock_button, style);
	gtk_rc_style_unref (style),

	gtk_box_pack_start(GTK_BOX(hbox), kztab->favicon,
			   FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(hbox), kztab->label,
			   TRUE, TRUE, 0);
	/* 
	 * close button (and other widget want to accept event signals)
	 * must be outside the eel-input-event-box
	 */ 
	gtk_box_pack_start(GTK_BOX(kztab), kztab->close_button,
			   FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(kztab), kztab->lock_button,
			   FALSE, FALSE, 0);

	gtk_widget_show (kztab->close_button);

	gtk_drag_source_set(GTK_WIDGET(kztab),
			    GDK_BUTTON1_MASK |
			    GDK_BUTTON2_MASK |
			    GDK_BUTTON3_MASK,
			    url_drag_types, n_url_drag_types,
			    GDK_ACTION_ASK  | GDK_ACTION_COPY
			    | GDK_ACTION_MOVE | GDK_ACTION_LINK);
	gtk_drag_dest_set(GTK_WIDGET(kztab), 
			  GTK_DEST_DEFAULT_HIGHLIGHT |
			  GTK_DEST_DEFAULT_MOTION | 
			  GTK_DEST_DEFAULT_DROP,
                          url_drag_types,n_url_drag_types,
			  GDK_ACTION_MOVE);

	g_signal_connect(G_OBJECT(kz_global_profile), "changed::Tab",
			 G_CALLBACK(cb_profile_changed), kztab);
	kz_tab_label_set_text(kztab, NULL);
	kz_tab_label_sync_to_profile(kztab);
}


static GObject*
kz_tab_label_constructor (GType                  type,
			  guint                  n_props,
			  GObjectConstructParam *props)
{
	KzTabLabel *kztab;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(parent_class);
	gchar *title;

	object = klass->constructor(type, n_props, props);

	kztab = KZ_TAB_LABEL(object);

	/* set signals */
	g_signal_connect(G_OBJECT(kztab->kzembed), "title",
			 G_CALLBACK(cb_title_changed), kztab);
	g_signal_connect(G_OBJECT(kztab->kzembed), "net-start",
			 G_CALLBACK(cb_net_start), kztab);
	g_signal_connect(G_OBJECT(kztab->kzembed), "net-stop",
			 G_CALLBACK(cb_net_stop), kztab);
	g_signal_connect(G_OBJECT(kztab->kzembed), "destroy",
			 G_CALLBACK(cb_destroy), kztab);
	g_signal_connect(G_OBJECT(kztab->kzembed), "net-state-all",
			 G_CALLBACK(cb_net_state_all), kztab);

	/* connect favicon signal */
	kz_favicon_connect_signal(kztab->kz->kzfav, kztab->kzembed);

	/* set label text */
	title = kz_moz_embed_ensure_title(kztab->kzembed);
	kz_tab_label_set_text(kztab, title);
	g_free(title);

	return object;
}


static void
kz_tab_label_dispose (GObject *object)
{
	KzTabLabel *kztab;

	g_return_if_fail (KZ_IS_TAB_LABEL(object));

	kztab = KZ_TAB_LABEL(object);

	g_signal_handlers_disconnect_by_func(G_OBJECT(kz_global_profile),
					     G_CALLBACK(cb_profile_changed),
					     kztab);
	if (kztab->kzembed)
	{
		g_signal_handlers_disconnect_by_func(G_OBJECT(kztab->kzembed),
						     cb_net_start, kztab);
		g_signal_handlers_disconnect_by_func(G_OBJECT(kztab->kzembed),
						     cb_net_stop, kztab);
		g_signal_handlers_disconnect_by_func(G_OBJECT(kztab->kzembed),
						     cb_title_changed, kztab);
		g_signal_handlers_disconnect_by_func(G_OBJECT(kztab->kzembed),
						     cb_destroy, kztab);
		g_signal_handlers_disconnect_by_func(G_OBJECT(kztab->kzembed),
						     cb_net_state_all, kztab);
		/* disconnect favicon signal */
		kz_favicon_disconnect_signal(kztab->kz->kzfav, kztab->kzembed);

		g_object_unref(kztab->kzembed);
		kztab->kzembed = NULL;
	}


	/* if connected signal exists, disconnect it. */
	if (kztab->priv->favicon_signal)
	{
		g_signal_handler_disconnect(kztab->kz->kzfav,
					    kztab->priv->favicon_signal);
		kztab->priv->favicon_signal = 0;
	}

	if (kztab->kz)
	{
		g_object_unref(kztab->kz);
		kztab->kz = NULL;
	}

	if (kztab->tooltips)
	{
		gtk_object_sink(GTK_OBJECT(kztab->tooltips));
		kztab->tooltips = NULL;
	}
	
	if (kztab->favicon_pixbuf)
	{
		g_object_unref(kztab->favicon_pixbuf);
		kztab->favicon_pixbuf = NULL;
	}
	if (kztab->circle)
	{
		g_object_unref(kztab->circle);
		kztab->circle = NULL;
	}
	if (kztab->mask)
	{
		g_object_unref(kztab->mask);
		kztab->mask = NULL;
	}

	if (G_OBJECT_CLASS (parent_class)->dispose)
		G_OBJECT_CLASS (parent_class)->dispose(object);
}


static void
kz_tab_label_set_property (GObject         *object,
			   guint            prop_id,
			   const GValue    *value,
			   GParamSpec      *pspec)
{
	KzTabLabel *tablabel = KZ_TAB_LABEL(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		tablabel->kz = g_object_ref(g_value_get_object(value));
		break;
	case PROP_KZ_MOZ_EMBED:
		tablabel->kzembed = g_object_ref(g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
kz_tab_label_get_property (GObject         *object,
			   guint            prop_id,
			   GValue          *value,
			   GParamSpec      *pspec)
{
	KzTabLabel *tablabel = KZ_TAB_LABEL(object);

	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		g_value_set_object(value, tablabel->kz);
		break;
	case PROP_KZ_MOZ_EMBED:
		g_value_set_object(value, tablabel->kzembed);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


GtkWidget *
kz_tab_label_new (KzWindow *kz, KzMozEmbed *kzembed)
{
	KzTabLabel *kztab;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	g_return_val_if_fail(KZ_IS_MOZ_EMBED(kzembed), NULL);

	kztab = KZ_TAB_LABEL(g_object_new(KZ_TYPE_TAB_LABEL,
					  "kz-window",    kz,
					  "kz-moz-embed", kzembed,
					  NULL));

	return GTK_WIDGET(kztab);
}


void
kz_tab_label_set_text(KzTabLabel *kztab, const gchar *text)
{
	gchar *escaped, *markup;
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	if (!text || !*text) text = _("No title");
	
	escaped = g_markup_escape_text(text, strlen(text));
	markup = g_strdup_printf("<span foreground=\"%s\">%s</span>",
				 label_color[kztab->state],
				 escaped);
	gtk_label_set_markup(GTK_LABEL(kztab->label), markup);
	gtk_tooltips_set_tip(kztab->tooltips, GTK_WIDGET(kztab->eventbox),
			     text, NULL);
	g_free(markup);
	g_free(escaped);
}

void
kz_tab_label_set_state (KzTabLabel *kztab,
			KzTabLabelState state)
{
	gchar *title;
	
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	if (!GTK_WIDGET_REALIZED(kztab)) return;

	switch(state)
	{
	case KZ_TAB_LABEL_STATE_LOADING:
		kztab->state = state;
		break;
	case KZ_TAB_LABEL_STATE_LOADED:
		kztab->state = state;
		break;
	case KZ_TAB_LABEL_STATE_NORMAL:
	default:
		kztab->state = KZ_TAB_LABEL_STATE_NORMAL;
		break;
	}
	
	title = kz_moz_embed_ensure_title(kztab->kzembed);
	kz_tab_label_set_text(kztab, title);
	g_free(title);
}

KzTabLabelState
kz_tab_label_get_state (KzTabLabel *kztab)
{
	g_return_val_if_fail(KZ_IS_TAB_LABEL(kztab), KZ_TAB_LABEL_STATE_NORMAL);
	return kztab->state;
}

void
kz_tab_label_set_width (KzTabLabel *kztab, gint width)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	gtk_widget_set_size_request(GTK_WIDGET(kztab), width, -1);
	if (width < 0)
	{
		gtk_widget_set_size_request(kztab->label, -1, -1);
		gtk_widget_queue_resize(GTK_WIDGET(kztab));
		gtk_widget_queue_resize(kztab->label);
	}
	kztab->priv->width = width;
}

void
kz_tab_label_set_show_close_button (KzTabLabel *kztab, gboolean show)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	if (show)
		gtk_widget_show(kztab->close_button);
	else
		gtk_widget_hide(kztab->close_button);
}


void
kz_tab_label_set_show_lock_button (KzTabLabel *kztab, gboolean show)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	if (show)
		gtk_widget_show(kztab->lock_button);
	else
		gtk_widget_hide(kztab->lock_button);
}


void
kz_tab_label_set_show_favicon (KzTabLabel *kztab, gboolean show)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	if (show && kztab->favicon_pixbuf)
		gtk_widget_show(kztab->favicon);
	else
		gtk_widget_hide(kztab->favicon);
}


static void
kz_tab_label_realize (GtkWidget *widget)
{
	KzTabLabel *kztab = KZ_TAB_LABEL(widget);

	if (GTK_WIDGET_CLASS (parent_class)->realize)
		GTK_WIDGET_CLASS (parent_class)->realize(widget);

	widget->window = gtk_widget_get_parent_window (widget);
	g_object_ref (widget->window);

	make_progress_circle(widget);

	if (kz_moz_embed_is_loading(kztab->kzembed))
		kz_tab_label_set_state (kztab, KZ_TAB_LABEL_STATE_LOADING);
}
#if 0
static void
kz_tab_label_unrealize (GtkWidget *widget)
{
	if (GTK_WIDGET_CLASS (parent_class)->unrealize)
		return GTK_WIDGET_CLASS (parent_class)->unrealize(widget);
}
#endif
static gboolean
kz_tab_label_button_press (GtkWidget *widget, GdkEventButton *event)
{
	KzTabLabel *kztab = KZ_TAB_LABEL(widget);

	kztab->priv->start_x = event->x;
	kztab->priv->start_y = event->y;
	kztab->priv->moved = FALSE;

#if 0
	if (event->button == 3)
	{
		kz_actions_tab_popup_menu_modal(kztab,
						event->button,
						event->time);
	}
#endif

	if (GTK_WIDGET_CLASS(parent_class)->button_press_event)
		GTK_WIDGET_CLASS(parent_class)->button_press_event(widget, event);
	return FALSE;
}

static gboolean
kz_tab_label_motion_notify (GtkWidget *widget, GdkEventMotion *event)
{
	KzTabLabel *kztab = KZ_TAB_LABEL(widget);

	if (abs(event->x - kztab->priv->start_x) > 2 ||
	    abs(event->y - kztab->priv->start_y) > 2)
	{
		kztab->priv->moved = TRUE;
	}

	if (GTK_WIDGET_CLASS(parent_class)->motion_notify_event)
		return GTK_WIDGET_CLASS(parent_class)->motion_notify_event(widget, event);
	return FALSE;
}

static gboolean
kz_tab_label_button_release (GtkWidget *widget, GdkEventButton *event)
{
	KzTabLabel *kztab = KZ_TAB_LABEL(widget);

	if (event->button == 2 && !kztab->priv->moved)
	{
		/* Load URL in clipboard */
		/* return TRUE; */
	}
	else if (event->button == 3 && !kztab->priv->moved)
	{
		kz_actions_tab_popup_menu_modal(kztab,
						event->button,
						event->time);
		return TRUE;
	}

	kztab->priv->start_x = 0;
	kztab->priv->start_y = 0;
	kztab->priv->moved = FALSE;

	if (GTK_WIDGET_CLASS(parent_class)->button_release_event)
		return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget, event);
	return FALSE;
}


static gboolean
kz_tab_label_scroll_event (GtkWidget *widget, GdkEventScroll *event)
{
	KzTabLabel *kztab;
	GtkNotebook *notebook;
	gboolean retval = FALSE;

	kztab = KZ_TAB_LABEL(widget);
	notebook = GTK_NOTEBOOK(kztab->kz->notebook);

	switch (event->direction) {
	case GDK_SCROLL_UP:
	case GDK_SCROLL_LEFT:
		gtk_notebook_prev_page(notebook);
		retval = TRUE;
		break;
	case GDK_SCROLL_DOWN:
	case GDK_SCROLL_RIGHT:
		gtk_notebook_next_page(notebook);
		retval = TRUE;
		break;
	default:
		g_warning ("Invalid scroll direction!");
		break;
	}

	if (GTK_WIDGET_CLASS(parent_class)->scroll_event)
		return GTK_WIDGET_CLASS(parent_class)->scroll_event(widget, event)
			|| retval;
	return retval;
}


static gboolean
kz_tab_label_drag_motion (GtkWidget *widget,
			  GdkDragContext *drag_context,
			  gint x, gint y,
			  guint time)
{
	KzTabLabel *kztab;
	KzMozEmbed *current_kzembed;
	gint page_num;

	g_return_val_if_fail(KZ_TAB_LABEL(widget), FALSE);
	kztab = KZ_TAB_LABEL(widget);

	g_return_val_if_fail (KZ_IS_MOZ_EMBED(kztab->kzembed), FALSE);

	current_kzembed = KZ_MOZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kztab->kz));

	if (KZ_MOZ_EMBED(kztab->kzembed) == current_kzembed)
		return FALSE;

	page_num = gtk_notebook_page_num(GTK_NOTEBOOK(kztab->kz->notebook),
					 GTK_WIDGET(kztab->kzembed));
	gtk_notebook_set_current_page(GTK_NOTEBOOK(kztab->kz->notebook),
				      page_num);
	return FALSE;
}

static void
kz_tab_label_drag_data_get (GtkWidget        *widget,
			    GdkDragContext   *context,
			    GtkSelectionData *data,
			    guint             info,
			    guint             time)
{
	KzTabLabel *kztab;
	const gchar *uri, *title;

	g_return_if_fail(KZ_IS_TAB_LABEL(widget));
	kztab = KZ_TAB_LABEL(widget);

	g_return_if_fail (KZ_IS_MOZ_EMBED(kztab->kzembed));

	uri   = kz_moz_embed_get_location(kztab->kzembed);
	title = kz_moz_embed_get_title(kztab->kzembed);

	if (!uri || !*uri) return;

	switch (info)
	{
	case TARGET_KAZEHAKASE_TAB:
		gtk_selection_data_set(data, data->target,
				       8, "dummy", strlen("dummy"));
		break;
	case TARGET_NETSCAPE_URL:
	{
		gchar *str;
		if (title && *title)
		{
			gchar *title_locale;
			gsize bytes_read, bytes_written;
			GError *error = NULL;

			title_locale = g_locale_from_utf8(title, -1,
							  &bytes_read,
							  &bytes_written,
							  &error);
			if (error)
			{
				g_warning("kz_tab_label_drag_data_get(): %s",
					 error->message);
				g_error_free(error);
			}
			if (title_locale && *title_locale)
			{
				str = g_strconcat(uri, "\n", title_locale,
						  NULL);
				g_free(title_locale);
			}
			else
			{
				str = g_strdup (uri);
			}
		}
		else
		{
			str = g_strdup (uri);
		}
		gtk_selection_data_set(data, data->target,
				       8, str, strlen(str));
		g_free(str);
		break;
	}
	case TARGET_TEXT_URI_LIST:
	case TARGET_TEXT_PLAIN:
	case TARGET_STRING:
		gtk_selection_data_set(data, data->target,
				       8, uri, strlen(uri));
		break;
	default:
		break;
	}
}

static void
kz_tab_label_drag_data_received (GtkWidget *widget,
				 GdkDragContext *drag_context,
				 gint x, gint y,
				 GtkSelectionData *data,
				 guint info,
				 guint time)
{
	KzTabLabel *kztab;

	g_return_if_fail(KZ_IS_TAB_LABEL(widget));
	kztab = KZ_TAB_LABEL(widget);

	g_return_if_fail (KZ_IS_MOZ_EMBED(kztab->kzembed));

	switch (info)
	{
	case TARGET_KAZEHAKASE_TAB:
	{
		GtkWidget *src_widget = gtk_drag_get_source_widget(drag_context);
		KzTabLabel *src_kztab;

		if (!KZ_IS_TAB_LABEL(src_widget)) return;
		src_kztab = KZ_TAB_LABEL(src_widget);

		kz_tab_label_move_page(src_kztab, kztab);
		return;
	}

	case TARGET_NETSCAPE_URL:
	case TARGET_TEXT_URI_LIST:
	case TARGET_TEXT_PLAIN:
	case TARGET_STRING:
	{
		gchar **strings;

		if (data->length < 0) return;

		strings = g_strsplit(data->data, "\n", 2);
		kz_moz_embed_load_url(kztab->kzembed,
				      strings[0]);
		g_strfreev(strings);
		break;
	}

	default:
		break;
	}
}

static void
kz_tab_label_sync_to_profile (KzTabLabel *kztab)
{
	gint width = 80;
	gboolean fix = TRUE;
	gboolean show_close = TRUE;
	gboolean show_favicon = TRUE;

	/* width */
	KZ_CONF_GET("Tab", "fix_width", fix, BOOL);
	KZ_CONF_GET("Tab", "fixed_width", width, INT);

	if (fix)
		kz_tab_label_set_width(kztab, width);
	else
		kz_tab_label_set_width(kztab, -1);

	/* close button */
	KZ_CONF_GET("Tab", "show_close_button", show_close, BOOL);
	kz_tab_label_set_show_close_button(kztab, show_close && !kztab->priv->lock);
	
	/* lock button */
	kz_tab_label_set_show_lock_button(kztab, kztab->priv->lock);

	/* favicon */
	KZ_CONF_GET("Tab", "show_favicon", show_favicon, BOOL);
	kz_tab_label_set_show_favicon(kztab, show_favicon);
}

/* move kztab to dest_kztab's position */
static void
kz_tab_label_move_page(KzTabLabel *kztab, KzTabLabel *dest_kztab)
{
	GtkWidget *src_page;
	GtkNotebook *notebook;
	gint dest_pos;

	g_return_if_fail(dest_kztab->kz && dest_kztab->kzembed);
	g_return_if_fail(kztab->kz && kztab->kzembed);

	notebook = GTK_NOTEBOOK(dest_kztab->kz->notebook);
	dest_pos = gtk_notebook_page_num(notebook,
					 GTK_WIDGET(dest_kztab->kzembed));

	if (dest_kztab->kz != kztab->kz)
	{
		src_page = GTK_WIDGET(kztab->kzembed);
		kz_window_move_tab(dest_kztab->kz, GTK_WIDGET(src_page));
	}
	else
	{
		src_page = GTK_WIDGET(kztab->kzembed);
	}

	kz_window_reorder_tab(dest_kztab->kz, src_page, dest_pos);
	kz_actions_set_sensitive
		(kztab->kz, KZ_MOZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kztab->kz)));
}


static void
make_progress_circle(GtkWidget *widget)
{
	GdkGC *gc;
	gint width, height;
	GdkColormap *colormap;
	KzTabLabel *kztab = KZ_TAB_LABEL(widget);

	gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
	kztab->circle = gdk_pixmap_new(widget->window,
				       width, height, -1);
	kztab->mask = gdk_pixmap_new(widget->window,
				     width, height, 1);
	gc = gdk_gc_new(kztab->mask);
	gdk_draw_rectangle(kztab->mask,
			   gc,
			   TRUE,
			   0, 0,
			   width, height);
	gdk_gc_set_function(gc, GDK_INVERT);
	gdk_draw_arc(kztab->mask,
		     gc,
		     TRUE,
		     0, 0,
		     width, height,
		     90 * 64, 360 * 64);
	g_object_unref(gc);

	gc = gdk_gc_new(kztab->circle);
	colormap = gdk_gc_get_colormap(gc);
	gdk_rgb_find_color(colormap, &red);
	gdk_gc_set_foreground(gc, &red);	
	gdk_draw_rectangle(kztab->circle,
			   widget->style->white_gc, 
			   TRUE,
			   0, 0,
			   width, height);
	gdk_draw_arc(kztab->circle,
		     gc, 
		     TRUE,
		     0, 0,
		     width, height,
		     90 * 64, 360 * 64);
	g_object_unref(colormap);
	g_object_unref(gc);
}

		
static void
cb_close_button_clicked (GtkWidget *button, KzTabLabel *kztab)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));
	kz_window_close_tab(kztab->kz, GTK_WIDGET(kztab->kzembed));
}

static void
cb_profile_changed (KzProfile *profile,
		    const gchar *section,
		    const gchar *key,
		    const gchar *old_value,
		    KzTabLabel *kztab)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	kz_tab_label_sync_to_profile (kztab);
}

/* callbacks for mozilla */

static void
cb_title_changed (GtkMozEmbed *embed, KzTabLabel *kztab)
{
	gchar *title;

	g_return_if_fail(KZ_IS_MOZ_EMBED(embed));
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	title = kz_moz_embed_ensure_title(kztab->kzembed);
	kz_tab_label_set_text(kztab, title);
	g_free(title);
	
	if (!kztab->priv->favicon_signal)
	{
		gchar *detailed_signal;
		const gchar *location;

		location = kz_moz_embed_get_location(kztab->kzembed);
		if (!location)
			return;

		detailed_signal = g_strdup_printf("ready::%s", location);
		kztab->priv->favicon_signal = g_signal_connect(G_OBJECT(kztab->kz->kzfav),
							       detailed_signal,
							       G_CALLBACK(cb_favicon_changed),
							       kztab);
		g_free(detailed_signal);
	}
}

static void
cb_net_start (GtkMozEmbed *embed, KzTabLabel *kztab)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	kz_tab_label_set_state(kztab, KZ_TAB_LABEL_STATE_LOADING);

	/* if connected signal exists, disconnect it. */
	if (kztab->priv->favicon_signal)
	{
		g_signal_handler_disconnect(kztab->kz->kzfav,
					    kztab->priv->favicon_signal);
		kztab->priv->favicon_signal = 0;
	}

	if (kztab->favicon_pixbuf)
	{
		gtk_image_set_from_pixbuf(GTK_IMAGE(kztab->favicon), NULL);
		g_object_unref(kztab->favicon_pixbuf);
		kztab->favicon_pixbuf = NULL;
	}
	/* show favicon widget for progress maru. */
	gtk_widget_show(kztab->favicon);
}

static void
cb_net_stop (GtkMozEmbed *embed, KzTabLabel *kztab)
{
	GtkNotebook *note;
	gboolean show_favicon;

	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	note = GTK_NOTEBOOK(kztab->kz->notebook);
	if(gtk_notebook_page_num(note, GTK_WIDGET(embed)) ==
	   gtk_notebook_get_current_page(note))
	{
		kz_tab_label_set_state(kztab, KZ_TAB_LABEL_STATE_NORMAL);
	}
	else
	{
		kz_tab_label_set_state(kztab, KZ_TAB_LABEL_STATE_LOADED);
	}

	if (kztab->favicon_pixbuf)
	{
		gtk_image_set_from_pixbuf(GTK_IMAGE(kztab->favicon),
					  kztab->favicon_pixbuf);
		gtk_widget_show(kztab->favicon);
	}
	else
		gtk_image_set_from_pixbuf(GTK_IMAGE(kztab->favicon),
					  NULL);

	KZ_CONF_GET("Tab", "show_favicon", show_favicon, BOOL);
	kz_tab_label_set_show_favicon(kztab, show_favicon);
}



static void
cb_destroy (GtkObject *object, KzTabLabel *kztab)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));
	kztab->kzembed = NULL;
}


static void
cb_net_state_all (GtkMozEmbed *embed, const char *aURI,
		  gint state, gint status,
		  KzTabLabel *kztab)
{
	gdouble progress;
	GtkWidget *widget;	       
	GdkGC *gc;

	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));

	widget = GTK_WIDGET(kztab);
	progress = kz_moz_embed_get_progress(KZ_MOZ_EMBED(embed));

	if(GTK_WIDGET_REALIZED(widget))
	{
		GdkBitmap *mask;
		gint width, height;
		gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);

		mask = gdk_pixmap_new(widget->window,
				      width, height, 1);
		gc = gdk_gc_new(mask);
		gdk_draw_rectangle(mask,
				   gc,
				   TRUE,
				   0, 0,
				   width, height);
		gdk_gc_set_function(gc, GDK_INVERT);
		gdk_draw_rectangle(mask,
				   gc,
				   TRUE,
				   0, 0,
				   width, height);
		gdk_gc_set_function(gc, GDK_COPY);
		gdk_draw_arc(mask,
			     gc,
			     TRUE,
			     0, 0,
			     width, height,
			     90 * 64, 360 * 64 * progress);
		gdk_gc_set_function(gc, GDK_AND);
		gdk_draw_drawable(mask,
				  gc,
				  kztab->mask,
				  0, 0,
				  0, 0,
				  -1, -1);
		gtk_image_set_from_pixmap(GTK_IMAGE(kztab->favicon), 
					  kztab->circle,
					  mask);
		g_object_unref(mask);
		g_object_unref(gc);
	}
}


static void
cb_favicon_changed (KzFavicon *kzfav, gchar *uri, KzTabLabel *kztab)
{
	gboolean show_favicon = TRUE;
	
	/* if connected signal exists, disconnect it. */
	if (kztab->priv->favicon_signal)
	{
		g_signal_handler_disconnect(kztab->kz->kzfav,
					    kztab->priv->favicon_signal);
		kztab->priv->favicon_signal = 0;
	}

	if (kztab->favicon_pixbuf)
	{
		g_object_unref(kztab->favicon_pixbuf);
		kztab->favicon_pixbuf = NULL;
	}
	
	kztab->favicon_pixbuf = kz_favicon_get_pixbuf(kztab->kz->kzfav, uri,
						      GTK_ICON_SIZE_MENU);
	
	if (kztab->favicon_pixbuf && 
	    kz_tab_label_get_state(kztab) != KZ_TAB_LABEL_STATE_LOADING)
	{
		gtk_image_set_from_pixbuf(GTK_IMAGE(kztab->favicon),
					  kztab->favicon_pixbuf);
	}

	KZ_CONF_GET("Tab", "show_favicon", show_favicon, BOOL);
	kz_tab_label_set_show_favicon(kztab, show_favicon);
}


static void
cb_close_button_state_changed (GtkWidget *widget,
			       GtkStateType previous_state)
{
	if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE)
	{
		gtk_widget_set_state (widget, GTK_STATE_NORMAL);
	}
}


gboolean
kz_tab_label_get_lock (KzTabLabel *kztab)
{
	g_return_val_if_fail(KZ_IS_TAB_LABEL(kztab), FALSE);
	return kztab->priv->lock;
}


void
kz_tab_label_set_lock (KzTabLabel *kztab, gboolean lock)
{
	g_return_if_fail(KZ_IS_TAB_LABEL(kztab));
	kztab->priv->lock = lock;
	kz_moz_embed_set_lock(kztab->kzembed, lock);
	kz_tab_label_sync_to_profile(kztab);
}
