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

/*
 *  Copyright (C) 2006 Hiroyuki Ikezoe
 *
 *  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-notebook.h"

#include <string.h>
#include <glib/gi18n.h>
#include <gdk/gdkkeysyms.h>

#include "gtk-utils.h"
#include "kz-tab-label.h"
#include "kz-actions.h"
#include "kz-web.h"

static const gpointer KZ_NOTEBOOK_GROUP = "KzNotebookGroup";

enum {
	PROP_0,
	PROP_KZ_WINDOW
};

typedef struct _KzNotebookPrivate	KzNotebookPrivate;
struct _KzNotebookPrivate
{
	/* Kazehakase */
	KzWindow *kz;
	/* tabs */
	GList *open_hist;
	GList *view_hist;
	GNode *tab_tree;

	GtkWidget *dragged_page;
};

#define KZ_NOTEBOOK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_NOTEBOOK, KzNotebookPrivate))

static void     dispose      (GObject         *object);
static void     finalize     (GObject         *object);
static void     set_property (GObject         *object,
			      guint            prop_id,
			      const GValue    *value,
			      GParamSpec      *pspec);
static void     get_property (GObject         *object,
			      guint            prop_id,
			      GValue          *value,
			      GParamSpec      *pspec);

static gboolean scroll_event (GtkWidget       *widget,
			      GdkEventScroll  *event);

static void	drag_begin    (GtkWidget *widget,
			       GdkDragContext *context);
static void	drag_end      (GtkWidget *widget,
			       GdkDragContext *context);
static void     drag_data_received
			     (GtkWidget *widget,
			      GdkDragContext *context,
			      gint x, gint y,
			      GtkSelectionData *data,
			      guint info,
			      guint time);

static void     cb_page_added(GtkNotebook *notebook,
			      GtkWidget   *child,
			      guint 	   page_num,
			      gpointer     data);
static void     switch_page  (GtkNotebook     *notebook,
			      GtkNotebookPage *page,
			      guint 	   page_num);

static void	cb_page_reordered (GtkNotebook *notebook,
				   GtkWidget   *child,
				   guint        page_num,
				   gpointer     data);

G_DEFINE_TYPE (KzNotebook, kz_notebook, GTK_TYPE_NOTEBOOK)

static void
kz_notebook_class_init (KzNotebookClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;
	GtkNotebookClass *notebook_class;

	gobject_class  = G_OBJECT_CLASS(klass);
	widget_class   = GTK_WIDGET_CLASS(klass);
	notebook_class = GTK_NOTEBOOK_CLASS(klass);

	/* GtkObject signals */
	gobject_class->dispose      = dispose;
	gobject_class->finalize     = finalize;
	gobject_class->set_property = set_property;
	gobject_class->get_property = get_property;

	widget_class->scroll_event       = scroll_event;
	widget_class->drag_begin         = drag_begin;
	widget_class->drag_end           = drag_end;
	widget_class->drag_data_received = drag_data_received;

	notebook_class->switch_page = switch_page;

	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_type_class_add_private(gobject_class, sizeof(KzNotebookPrivate));
}

static void
kz_notebook_init (KzNotebook *notebook)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);

	priv->kz        = NULL;
	priv->open_hist = NULL;
	priv->view_hist = NULL;

	priv->dragged_page = NULL;

	priv->tab_tree = g_node_new(NULL);

	g_signal_connect(notebook, "page-reordered", 
			 G_CALLBACK(cb_page_reordered), NULL);
	g_signal_connect(notebook, "page-added", 
			 G_CALLBACK(cb_page_added), NULL);

}


static void
set_property (GObject *object, guint prop_id,
              const GValue *value, GParamSpec *pspec)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		priv->kz = g_value_dup_object(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}

static void
get_property (GObject *object, guint prop_id,
              GValue *value, GParamSpec *pspec)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);

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


GtkWidget *
kz_notebook_new (KzWindow *kz)
{
	KzNotebook *notebook;

	notebook = g_object_new(KZ_TYPE_NOTEBOOK,
				"kz-window", kz,
				"scrollable", TRUE,
				"show-tabs", TRUE,
				"group", KZ_NOTEBOOK_GROUP,
				NULL);
	
	return GTK_WIDGET(notebook);
}

static void
dispose (GObject *object)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);

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

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

static void
finalize (GObject *object)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(object);

	g_list_free(priv->open_hist);
	g_list_free(priv->view_hist);

	if (priv->tab_tree)
		g_node_destroy(priv->tab_tree);

	if (G_OBJECT_CLASS(kz_notebook_parent_class)->finalize)
		G_OBJECT_CLASS(kz_notebook_parent_class)->finalize(object);
}

static gboolean
scroll_event (GtkWidget *widget, GdkEventScroll *event)
{
	GtkWidget* originator;
	GtkWidget *current;
	
	current = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget),
					    gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)));
	originator = gtk_get_event_widget((GdkEvent *)event);

	/* ignore scroll events from the content of the page */
	if (!originator || gtk_widget_is_ancestor(originator, current) || originator == current)
		return FALSE;

	return kz_notebook_scroll_tab(KZ_NOTEBOOK(widget), event->direction);
}

static void
drag_begin (GtkWidget *widget, GdkDragContext *context)
{
	gint current_page_num;
	GtkWidget *current_page;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(widget);

	current_page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));

	if (current_page_num < 0)
		return;

	current_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), current_page_num);
	if (current_page)
		priv->dragged_page = g_object_ref(current_page);

	if (GTK_WIDGET_CLASS(kz_notebook_parent_class)->drag_begin)
		GTK_WIDGET_CLASS(kz_notebook_parent_class)->drag_begin(widget, context);
}

static void
drag_end (GtkWidget *widget, GdkDragContext *context)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(widget);

	if (priv->dragged_page)
		g_object_unref(priv->dragged_page);
	priv->dragged_page = NULL;

	if (GTK_WIDGET_CLASS(kz_notebook_parent_class)->drag_end)
		GTK_WIDGET_CLASS(kz_notebook_parent_class)->drag_end(widget, context);
}

static void
drag_data_received (GtkWidget *widget,
                    GdkDragContext *context,
                    gint x, gint y,
                    GtkSelectionData *seldata,
                    guint info,
                    guint time)
{
	if (seldata->target == gdk_atom_intern_static_string("GTK_NOTEBOOK_TAB"))
	{
		GtkWidget *src_notebook;
		GtkWidget **child;
		KzWindow *src_window, *dest_window;

		child = (void*) seldata->data;
		src_notebook = gtk_drag_get_source_widget(context);
		src_window = KZ_NOTEBOOK_GET_PRIVATE(src_notebook)->kz;
		dest_window = KZ_NOTEBOOK_GET_PRIVATE(widget)->kz;
		kz_window_move_tab(src_window, dest_window, *child);
		gtk_drag_finish(context, TRUE, TRUE, time);
	}

	gtk_drag_finish(context, FALSE, FALSE, time);
}


static gboolean
idle_focus_location_entry(gpointer data)
{
	g_return_val_if_fail(KZ_IS_WINDOW(data), FALSE);

	kz_window_activate_action(KZ_WINDOW(data), "FocusLocationEntry");

	return FALSE;
}

static void
switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num)
{
	KzTabLabel *tab;
	gchar *title;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	KzWeb *kzweb = kz_notebook_get_nth_web(KZ_NOTEBOOK(notebook), page_num);

	title = kz_web_ensure_title(kzweb);
	if (title)
	{
		gtk_window_set_title(GTK_WINDOW(priv->kz), title);
		g_free(title);
	}
	else
	{
		gtk_window_set_title(GTK_WINDOW(priv->kz), _("Kazehakase"));
	}

	priv->view_hist = g_list_remove(priv->view_hist, kzweb);
	priv->view_hist = g_list_prepend(priv->view_hist, kzweb);

	tab = kz_notebook_get_tab_label(KZ_NOTEBOOK(notebook), kzweb);
	g_return_if_fail(tab);

	if (kz_tab_label_get_state(tab) == KZ_TAB_LABEL_STATE_LOADED)
	{
		const gchar *location;
		gboolean focus;
		kz_tab_label_set_state(tab, KZ_TAB_LABEL_STATE_NORMAL);
		location = kz_web_get_location(kzweb);
		if((!location || *location == 0) &&
		   KZ_CONF_GET("Tab", "focus_loc_ent_new", focus, BOOL) &&
		   focus)
		{
			g_idle_add(idle_focus_location_entry, priv->kz);
		}
	}	

	if (GTK_NOTEBOOK_CLASS(kz_notebook_parent_class)->switch_page)
		GTK_NOTEBOOK_CLASS(kz_notebook_parent_class)->switch_page(notebook, page, page_num);
}

static void
cb_page_added (GtkNotebook *notebook, GtkWidget *child, guint page_num, gpointer data)
{
	gtk_notebook_set_tab_reorderable(notebook, child, TRUE);
	gtk_notebook_set_tab_detachable(notebook, child, TRUE);
}

static void
cb_page_reordered (GtkNotebook *notebook,
		   GtkWidget   *child,
		   guint        page_num,
		   gpointer     data)
{
	KzTabLabel *kztab, *sibtab = NULL;
	KzBookmark *sib_bookmark = NULL;
	GtkWidget *sibchild = NULL;
	KzWindow *kz = KZ_NOTEBOOK_GET_PRIVATE(notebook)->kz;

	sibchild = gtk_notebook_get_nth_page(notebook, page_num + 1);
	if (sibchild)
		sibtab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(notebook, sibchild));
	if (sibtab)
		sib_bookmark = KZ_BOOKMARK(sibtab->history);

	kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(notebook, child));
	g_object_ref(kztab->history);
	kz_bookmark_folder_remove(kz->tabs, KZ_BOOKMARK(kztab->history));
	kz_bookmark_folder_insert_before(kz->tabs, 
				  	 KZ_BOOKMARK(kztab->history),
					 sib_bookmark);
	g_object_unref(kztab->history);
}

static gint 
get_insert_tab_position (KzNotebook *notebook)
{
	gchar pos_str[256];
	gint pos = -1;

	KZ_CONF_GET("Tab", "new_tab_position", pos_str, STRING);
	
	if (pos_str[0] == '\0' || !strcasecmp(pos_str, "last"))
	{
		pos = -1;
	}
	else if (!strcasecmp(pos_str, "first"))
	{
		pos = 0;
	}
	else if (!strcasecmp(pos_str, "left"))
	{
		pos = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
	}
	else if (!strcasecmp(pos_str, "right"))
	{
		pos = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
	}
	else if (!strcasecmp(pos_str, "unread_right"))
	{
		gint i = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
		gint num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
		
		if (i > num)
		{
			pos = -1;
		}
		else
		{
			while (i < num)
			{
				KzTabLabel *kztab;
				KzTabLabelState state;

				kztab = kz_notebook_get_nth_tab_label(notebook, i);
				state = kz_tab_label_get_state(kztab);
				if (state == KZ_TAB_LABEL_STATE_NORMAL)
					break;
				i++;
			}
			pos = i;
		}
	}

	return pos;
}

gint
kz_notebook_open_new_tab_at_pos (KzNotebook *notebook, KzWeb *web, KzTabLabel *label, gint pos)
{
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	gint inserted_pos;

	gtk_widget_show_all(GTK_WIDGET(web));
	gtk_widget_show(GTK_WIDGET(label));
	inserted_pos = gtk_notebook_insert_page(GTK_NOTEBOOK(notebook),
						GTK_WIDGET(web),
						GTK_WIDGET(label),
						pos);

	/* add to this notebook's history */
	priv->open_hist = g_list_prepend(priv->open_hist, web);

	return inserted_pos;
}

gint
kz_notebook_open_new_tab (KzNotebook *notebook, KzWeb *web, KzTabLabel *label)
{
	gint pos;
	GNode *node;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);

	pos = get_insert_tab_position(notebook);

	pos = kz_notebook_open_new_tab_at_pos(notebook, web, label, pos);

	/* insret node */
	node = g_node_new(web);
	g_node_append(priv->tab_tree, node);

	return pos;
}

gint
kz_notebook_open_new_tab_with_parent (KzNotebook *notebook, KzWeb *web, KzTabLabel *label, KzWeb *parent)
{
	gint pos;
	GNode *node, *parent_node;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);

	pos = get_insert_tab_position(notebook);

	pos = kz_notebook_open_new_tab_at_pos(notebook, web, label, pos);
	
	/* insret node */
	node = g_node_new(web);
	parent_node = g_node_find(priv->tab_tree,
				  G_IN_ORDER, G_TRAVERSE_ALL, parent);
	if (parent_node)
		g_node_append(parent_node, node);
	else
		g_node_append(priv->tab_tree, node);

	return pos;
}

gint
kz_notebook_prepend_new_tab (KzNotebook *notebook, KzWeb *web, KzTabLabel *label)
{
	gint pos;
	GNode *node;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);

	pos = kz_notebook_open_new_tab_at_pos(notebook, web, label, -1);

	/* insret node */
	node = g_node_new(web);
	g_node_append(priv->tab_tree, node);

	return pos;
}

gboolean
kz_notebook_close_tab (KzNotebook *notebook, KzWeb *web)
{
	gchar *ret_page = NULL;
	KzTabLabel *kztab = NULL;
	GtkWidget *next = NULL;
	KzNotebookPrivate *priv = KZ_NOTEBOOK_GET_PRIVATE(notebook);
	KzWindow *kz = priv->kz;
	GNode *node, *child;
	KzWeb *current = kz_notebook_get_nth_web(notebook,
						 gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
	kztab = kz_notebook_get_tab_label(notebook, web);

	if (kztab && kz_tab_label_get_lock(kztab))
	{
		/* kztab is locked, so return without closing tab */
		return FALSE;
	}
	else if (current != web)
	{
		/* close tab which is not current , so there is nothing to consider */
		goto CLOSE_TAB;
	}

	ret_page = KZ_CONF_GET_STR("Tab", "page_to_return_when_close");
	if (ret_page)
	{
		if (!strcmp(ret_page, "last_shown"))
		{
			GList *node;

			if (priv->view_hist &&
					(node = g_list_next(priv->view_hist)))
			{
				next = node->data;
			}
		}
		else if (!strcmp(ret_page, "last_created"))
		{
			GList *node = priv->open_hist;

			for (; node && !next; node = g_list_next(node))
			{
				if (current != node->data)
					next = node->data;
			}
		}
		else if (!strcmp(ret_page, "prev_tab"))
		{
			gtk_notebook_prev_page(GTK_NOTEBOOK(notebook));
		}
		else if (!strcmp(ret_page, "next_tab"))
		{
			gtk_notebook_next_page(GTK_NOTEBOOK(notebook));
		}
	}
	if (next)
	{
		gint num;

		num = gtk_notebook_page_num
			(GTK_NOTEBOOK(notebook),
			 GTK_WIDGET(next));
		gtk_notebook_set_current_page
			(GTK_NOTEBOOK(notebook), num);
	}
	g_free(ret_page);

CLOSE_TAB:
	priv->view_hist = g_list_remove(priv->view_hist, web);
	node = g_node_find(priv->tab_tree,
			   G_IN_ORDER, G_TRAVERSE_ALL, web);
	if (node)
	{
		/* move children */
		child = g_node_first_child(node);
		while (child)
		{
			GNode *next = g_node_next_sibling(child);
			g_node_unlink(child);
			g_node_append(priv->tab_tree, child);
			child = next;
		}

		g_node_destroy(node);
	}
	gtk_widget_destroy(GTK_WIDGET(web));

	if (kz && gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) == 0)
	{
		gtk_window_set_title(GTK_WINDOW(kz), _("Kazehakase"));
		kz_window_set_location_entry_text(kz, "");
	}
	return TRUE;
}

gboolean
kz_notebook_close_tabs (KzNotebook *notebook, KzNotebookCloseCondition condition, gint pos)
{
	gint num, i;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), FALSE);

	num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));

	if (num < 2 || pos < 0) return FALSE;

	for (i = num - 1; i >= 0; i--)
	{
		KzWeb *web = kz_notebook_get_nth_web(notebook, i);

		if (i == pos) continue;
		if (i > pos && condition == KZ_NOTEBOOK_CLOSE_BACKWARD) continue;
		if (i < pos && condition == KZ_NOTEBOOK_CLOSE_FORWARD) continue;

		kz_notebook_close_tab(notebook, web);
	}
	return TRUE;
}

gboolean
kz_notebook_close_all_tab (KzNotebook *notebook)
{
	gint n_pages, i;

	n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
	for (i = n_pages - 1; i >= 0; i--)
	{
		GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i);
		gtk_widget_destroy(widget);
	}
	return TRUE;
}

gboolean
kz_notebook_move_tab (KzNotebook *src_notebook, KzNotebook *dest_notebook, GtkWidget *widget)
{
	KzTabLabel *new_kztab;
	KzNotebookPrivate *dest_priv, *src_priv;
	GNode *node, *child;

	/* create new tab label */
	dest_priv = KZ_NOTEBOOK_GET_PRIVATE(dest_notebook);
	new_kztab = KZ_TAB_LABEL(kz_tab_label_new(dest_priv->kz, KZ_WEB(widget)));

	/* move the page to this window */
	g_object_ref(widget);
	gtk_container_remove(GTK_CONTAINER(src_notebook), widget);
	gtk_notebook_prepend_page(GTK_NOTEBOOK(dest_notebook),
				  widget,
				  GTK_WIDGET(new_kztab));
	g_object_unref(widget);

	src_priv = KZ_NOTEBOOK_GET_PRIVATE(src_notebook);
	/* remove view_hist */
	src_priv->view_hist = g_list_remove(src_priv->view_hist, widget);
	/* move open_hist */
	src_priv->open_hist = g_list_remove(src_priv->open_hist, widget);
	dest_priv->open_hist = g_list_prepend(dest_priv->open_hist, widget);
	/* move tab tree */
	node = g_node_find(src_priv->tab_tree,
			   G_IN_ORDER, G_TRAVERSE_ALL, widget);
	if (node)
	{
		/* move children */
		child = g_node_first_child(node);
		while (child)
		{
			GNode *next = g_node_next_sibling(child);
			g_node_unlink(child);
			g_node_append(src_priv->tab_tree,
				      child);
			child = next;
		}

		/* move node */
		g_node_unlink(node);
		if (!dest_priv->tab_tree)
			dest_priv->tab_tree = g_node_new(NULL);
		g_node_append(dest_priv->tab_tree, node);
	}
	else
	{
		g_warning("KzWindow: cannot find tab node!");
	}
	return TRUE;
}

void
kz_notebook_next_tab (KzNotebook *notebook)
{
	gboolean circulation = FALSE;
	GtkNotebook *gtk_notebook;

	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook = GTK_NOTEBOOK(notebook);
	KZ_CONF_GET("Tab", "wheel_circulation", circulation, BOOL);
	
	if (circulation && 
	    gtk_notebook_get_current_page(gtk_notebook) == 
	    gtk_notebook_get_n_pages(gtk_notebook) - 1)
	{
		gtk_notebook_set_current_page(gtk_notebook, 0);
	}
	else
	{
		gtk_notebook_next_page(gtk_notebook);
	}
}

void
kz_notebook_prev_tab (KzNotebook *notebook)
{
	gboolean circulation = FALSE;
	GtkNotebook *gtk_notebook;

	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook = GTK_NOTEBOOK(notebook);
	KZ_CONF_GET("Tab", "wheel_circulation", circulation, BOOL);

	if (circulation &&
	    gtk_notebook_get_current_page(gtk_notebook) == 0)
	{
		gtk_notebook_set_current_page
			(gtk_notebook, gtk_notebook_get_n_pages(gtk_notebook) - 1);
	}
	else
	{
		gtk_notebook_prev_page(gtk_notebook);
	}
}

gboolean
kz_notebook_scroll_tab (KzNotebook *notebook, GdkScrollDirection direction)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), FALSE);

	switch (direction) {
	case GDK_SCROLL_UP:
	case GDK_SCROLL_LEFT:
		kz_notebook_prev_tab(notebook);
		return TRUE;
		break;
	case GDK_SCROLL_DOWN:
	case GDK_SCROLL_RIGHT:
		kz_notebook_next_tab(notebook);
		return TRUE;
		break;
	default:
		g_warning ("Invalid scroll direction!");
		break;
	}

	return FALSE;
}

KzWeb *
kz_notebook_get_nth_web (KzNotebook *notebook, gint page_num)
{
	GtkWidget *widget;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);

	widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);

	return widget ? KZ_WEB(widget) : NULL;
}

KzTabLabel *
kz_notebook_get_tab_label (KzNotebook *notebook, KzWeb *web)
{
	return KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), GTK_WIDGET(web)));
}

KzTabLabel *
kz_notebook_get_nth_tab_label (KzNotebook *notebook, gint page_num)
{
	GtkWidget *widget;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);

	widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
	if (!widget)
		return NULL;

	return KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), widget));
}

gint
kz_notebook_page_num (KzNotebook *notebook, GtkWidget *child)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), -1);

	return gtk_notebook_page_num(GTK_NOTEBOOK(notebook), child);
}

gint
kz_notebook_get_current_page (KzNotebook *notebook)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), -1);

	return gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
}

void
kz_notebook_set_current_page (KzNotebook *notebook, gint page_num)
{
	g_return_if_fail(KZ_IS_NOTEBOOK(notebook));

	gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
}

gint
kz_notebook_get_n_pages (KzNotebook *notebook)
{
	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), 0);

	return gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook));
}

KzWeb *
kz_notebook_get_sibling_web (KzNotebook *notebook, KzWeb *web)
{
	gint pos;
	GtkWidget *sibweb;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);
	g_return_val_if_fail(KZ_IS_WEB(web), NULL);

	pos = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), GTK_WIDGET(web));
	if (pos < 0)
		return NULL;
	pos++;
	if (pos >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)))
		return NULL;

	sibweb = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), pos);

	return sibweb ? KZ_WEB(sibweb) : NULL;
}

KzTabLabel *
kz_notebook_get_sibling_tab_label (KzNotebook *notebook, KzTabLabel *label)
{
	KzWeb *sibweb;
	KzTabLabel *kztab;

	g_return_val_if_fail(KZ_IS_NOTEBOOK(notebook), NULL);
	g_return_val_if_fail(KZ_IS_TAB_LABEL(label), NULL);
	g_return_val_if_fail(KZ_IS_WEB(label->kzweb), NULL);

	sibweb = kz_notebook_get_sibling_web(notebook, label->kzweb);
	if (!sibweb)
		return NULL;

	kztab = kz_notebook_get_tab_label(notebook, sibweb);
	return kztab;
}

GtkWidget *
kz_notebook_get_dragged_page (KzNotebook *notebook)
{
	return KZ_NOTEBOOK_GET_PRIVATE(notebook)->dragged_page;
}

KzWeb *
kz_notebook_get_dragged_web (KzNotebook *notebook)
{
	return KZ_WEB(KZ_NOTEBOOK_GET_PRIVATE(notebook)->dragged_page);
}

GNode *
kz_notebook_get_tree (KzNotebook *notebook)
{
	return KZ_NOTEBOOK_GET_PRIVATE(notebook)->tab_tree;
}

void
kz_notebook_foreach_web (KzNotebook *notebook,
			   GFunc       func,
                           gpointer    user_data)
{
	GList *pages, *node;

	pages = gtk_container_get_children(GTK_CONTAINER(notebook));
	if (!pages)
		return;

	for (node = pages; node; node = g_list_next(node))
	{
		GtkWidget *web = node->data;
		if (!web)
			continue;
		func(web, user_data);
	}
	g_list_free(pages);
}

