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

#include <glib/gi18n.h>
#include "gtk-utils.h"
#include "kz-tab-label.h"
#include "kz-actions-tab.h"

static gchar *label_color[KZ_TAB_LABEL_N_STATE];

enum {
	PROP_0,
	PROP_SIDEBAR,
	PROP_TREE
};


enum
{
	COLUMN_TERMINATOR = -1,
	COLUMN_ICON,
	COLUMN_TITLE,
	COLUMN_TITLE_COLOR,
	COLUMN_EMBEDER,
	N_COLUMNS
};


static GtkWidget *kz_tab_list_new (KzSidebarEntry *entry, KzSidebar *sidebar);
static GtkWidget *kz_tab_tree_new (KzSidebarEntry *entry, KzSidebar *sidebar);


/* Object class methods */
static GObject *constructor  (GType           type,
                              guint           n_props,
                              GObjectConstructParam *props);
static void     dispose      (GObject        *obj);
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);

/* KzTabTree private methods */
static void         build_tab_tree         (KzTabTree    *tabtree,
					    GNode        *parent);
static void         build_tab_list         (KzTabTree    *tabtree);
static GtkTreeIter *find_node              (GtkTreeStore *store,
					    KzEmbed      *kzembed);
static void         remove_all_embed_signals
					   (KzTabTree    *tabtree);

/* signal handlers for TreeView */
static void     cb_cursor_changed           (GtkTreeView     *tree_view,
					     KzTabTree       *tabtree);
static gboolean cb_tree_view_button_release (GtkWidget       *widget,
					     GdkEventButton  *event,
					     KzTabTree       *tabtree);
static gboolean cb_scroll_event             (GtkWidget       *widget,
					     GdkEventScroll  *event,
					     KzTabTree       *tabtree);

/* signal handlers for Notebook on KzWindow */
static void     cb_switch_page    (GtkNotebook     *notebook,
				   GtkNotebookPage *page,
				   guint            page_num,
				   KzTabTree       *tabtree);

/* signal handlers for KzWindow */
static void     cb_append_tab     (KzWindow        *kz,
				   GtkWidget       *widget,
				   GtkWidget       *parent,
				   KzTabTree       *tabtree);
static void     parent_append     (KzTabTree * tabtree, 
				   KzEmbed * kzembed, 
				   GtkTreeIter * parent, 
				   GtkTreeIter * iter);
static void     cb_remove_tab     (KzWindow        *kz,
				   GtkWidget       *widget,
				   KzTabTree       *tabtree);
static void     cb_reorder_tab    (KzWindow        *kz,
				   GtkWidget       *widget,
				   gint             pos,
				   KzTabTree       *tabtree);

/* signal handlers for embed */
static void     cb_net_start      (KzEmbed         *embed,
				   KzTabTree       *tabtree);
static void     cb_net_stop       (KzEmbed         *embed,
				   KzTabTree       *tabtree);
static void     cb_title_changed  (KzEmbed         *embed,
				   KzTabTree       *tabtree);


static KzSidebarEntry kz_sidebar_tab_tree[] = 
{
	{
		/* .priority_hint = */ 0,
		/* .label         = */ N_("Tab List"),
		/* .icon          = */ NULL,
		/* .create        = */ kz_tab_list_new,
	},
	{
		/* .priority_hint = */ 0,
		/* .label         = */ N_("Tab Tree"),
		/* .icon          = */ NULL,
		/* .create        = */ kz_tab_tree_new,
	},
};


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


#if 0
static GtkTargetEntry dnd_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_dnd_types = G_N_ELEMENTS (dnd_types);
#endif


KzSidebarEntry *
kz_tab_tree_get_entry (gint idx)
{
	switch (idx)
	{
	case 0:
		return &kz_sidebar_tab_tree[0];
	case 1:
		return &kz_sidebar_tab_tree[1];
	default:
		return NULL;
	}

	return NULL;
}

G_DEFINE_TYPE(KzTabTree, kz_tab_tree, GTK_TYPE_VBOX)

static void
kz_tab_tree_class_init (KzTabTreeClass *klass)
{
	GObjectClass *gobject_class;

	gobject_class = (GObjectClass *) klass;

	gobject_class->constructor  = constructor;
	gobject_class->dispose      = dispose;
	gobject_class->set_property = set_property;
	gobject_class->get_property = get_property;

	g_object_class_install_property
		(gobject_class,
		 PROP_SIDEBAR,
		 g_param_spec_object ("sidebar",
				      _("Sidebar"),
				      _("The parent sidebar"),
				      KZ_TYPE_SIDEBAR,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property
		(gobject_class,
		 PROP_TREE,
		 g_param_spec_boolean ("tree",
				       _("Tree"),
				       _("Whether use Tree view or List view"),
				       FALSE,
				       G_PARAM_READWRITE |
				       G_PARAM_CONSTRUCT_ONLY));
}


static void
kz_tab_tree_init (KzTabTree *tabtree)
{
	GtkTreeStore *store;
	GtkWidget *widget, *scrwin;
	GtkTreeView *tree_view;
	GtkCellRenderer *cell;
	GtkTreeViewColumn *column;

	gchar *color;

        /* label color */
        color = KZ_CONF_GET_STR("Tab" , "normal_color");
        if (!color)
                color = g_strdup("#000000");

        if (label_color[KZ_TAB_LABEL_STATE_NORMAL])
                g_free(label_color[KZ_TAB_LABEL_STATE_NORMAL]);
        label_color[KZ_TAB_LABEL_STATE_NORMAL] = g_strdup(color);
        g_free(color);

        color = KZ_CONF_GET_STR("Tab" , "loading_color");
        if (!color)
                color = g_strdup("#ff0000");
        if (label_color[KZ_TAB_LABEL_STATE_LOADING])
                g_free(label_color[KZ_TAB_LABEL_STATE_LOADING]);
        label_color[KZ_TAB_LABEL_STATE_LOADING] = g_strdup(color);
        g_free(color);

        color = KZ_CONF_GET_STR("Tab" , "loaded_color");
        if (!color)
                color = g_strdup("#22aa44");
        if (label_color[KZ_TAB_LABEL_STATE_LOADED])
                g_free(label_color[KZ_TAB_LABEL_STATE_LOADED]);
        label_color[KZ_TAB_LABEL_STATE_LOADED] = g_strdup(color);
        g_free(color);

	scrwin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
					    GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(tabtree), scrwin, TRUE, TRUE, 0);
	gtk_widget_show(scrwin);

	store = gtk_tree_store_new(N_COLUMNS,
				   GDK_TYPE_PIXBUF,
				   G_TYPE_STRING,
				   G_TYPE_STRING,
				   G_TYPE_POINTER);

	widget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
	tree_view = GTK_TREE_VIEW(widget);
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE);
	gtk_container_add(GTK_CONTAINER(scrwin), GTK_WIDGET(tree_view));
#if 0
	gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tree_view),
					       GDK_BUTTON1_MASK
					       | GDK_BUTTON2_MASK
					       | GDK_BUTTON3_MASK,
					       dnd_types,
					       n_dnd_types,
					       GDK_ACTION_ASK  | GDK_ACTION_COPY
					       | GDK_ACTION_MOVE | GDK_ACTION_LINK);
	gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW (tree_view),
					     dnd_types,
					     n_dnd_types,
					     GDK_ACTION_ASK  | GDK_ACTION_COPY
					     | GDK_ACTION_MOVE | GDK_ACTION_LINK);
#endif
	gtk_widget_show(GTK_WIDGET(tree_view));

	column = gtk_tree_view_column_new();

	cell = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(column, cell, FALSE);
	gtk_tree_view_column_add_attribute(column, cell,
					   "pixbuf", COLUMN_ICON);

	cell = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(column, cell, TRUE);
	gtk_tree_view_column_set_title(column, _("Title"));
	gtk_tree_view_column_add_attribute(column, cell,
					   "text", COLUMN_TITLE);
	gtk_tree_view_column_add_attribute(column, cell,
					   "foreground", COLUMN_TITLE_COLOR);
	gtk_tree_view_append_column(tree_view, column);

	g_signal_connect(tree_view, "cursor-changed",
			 G_CALLBACK(cb_cursor_changed), tabtree);
	g_signal_connect(tree_view, "button-release-event",
			 G_CALLBACK(cb_tree_view_button_release), tabtree);
	g_signal_connect(tree_view, "scroll-event",
			 G_CALLBACK(cb_scroll_event), tabtree);

	tabtree->sidebar   = NULL;
	tabtree->tree_view = GTK_TREE_VIEW(tree_view);
	tabtree->store     = store;
	tabtree->tree      = FALSE;
	tabtree->switching = FALSE;
}


static GObject*
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	KzTabTree *tabtree;

	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(kz_tab_tree_parent_class);

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

	tabtree = KZ_TAB_TREE(object);

	if (tabtree->tree)
	{
		GNode *tree = kz_window_get_tree(tabtree->sidebar->kz);
		if (tree && g_node_first_child(tree))
			build_tab_tree(tabtree, tree);
	}
	else
	{
		build_tab_list(tabtree);
	}

	gtk_tree_view_expand_all(tabtree->tree_view);

	g_signal_connect(tabtree->sidebar->kz->notebook, "switch-page",
			 G_CALLBACK(cb_switch_page), tabtree);
	g_signal_connect(tabtree->sidebar->kz, "append-tab",
			 G_CALLBACK(cb_append_tab), tabtree);
	g_signal_connect(tabtree->sidebar->kz, "remove-tab",
			 G_CALLBACK(cb_remove_tab), tabtree);
	g_signal_connect(tabtree->sidebar->kz, "reorder-tab",
			 G_CALLBACK(cb_reorder_tab), tabtree);

	return object;
}


static void
dispose (GObject *obj)
{
	KzTabTree *tabtree = KZ_TAB_TREE(obj);

	if (tabtree->sidebar)
	{
		KzWindow *kz = tabtree->sidebar->kz;

		if (kz)
		{
			g_signal_handlers_disconnect_by_func
				(kz->notebook,
				 G_CALLBACK(cb_switch_page),
				 tabtree);
			g_signal_handlers_disconnect_by_func
				(kz,
				 G_CALLBACK(cb_append_tab),
				 tabtree);
			g_signal_handlers_disconnect_by_func
				(kz,
				 G_CALLBACK(cb_remove_tab),
				 tabtree);
			g_signal_handlers_disconnect_by_func
				(kz,
				 G_CALLBACK(cb_reorder_tab),
				 tabtree);
		}

		g_object_unref(tabtree->sidebar);
		tabtree->sidebar = NULL;
	}

	if (tabtree->store)
	{
		remove_all_embed_signals(tabtree);
		g_object_unref(tabtree->store);
		tabtree->store = NULL;
	}
	if (G_OBJECT_CLASS (kz_tab_tree_parent_class)->dispose)
		G_OBJECT_CLASS (kz_tab_tree_parent_class)->dispose(obj);
}


static void
set_property (GObject         *object,
              guint            prop_id,
              const GValue    *value,
              GParamSpec      *pspec)
{
	KzTabTree *tabtree = KZ_TAB_TREE(object);
  
	switch (prop_id)
	{
	case PROP_SIDEBAR:
		tabtree->sidebar = g_object_ref(g_value_get_object(value));
		break;
	case PROP_TREE:
		tabtree->tree = g_value_get_boolean(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)
{
	KzTabTree *tabtree = KZ_TAB_TREE(object);

	switch (prop_id)
	{
	case PROP_SIDEBAR:
		g_value_set_object(value, tabtree->sidebar);
		break;
	case PROP_TREE:
		g_value_set_boolean(value, tabtree->tree);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static GtkWidget *
kz_tab_list_new (KzSidebarEntry *entry, KzSidebar *sidebar)
{
	KzTabTree *tabtree;

	tabtree = g_object_new(KZ_TYPE_TAB_TREE,
			       "sidebar", sidebar,
			       "tree",    FALSE,
			       NULL);

	return GTK_WIDGET(tabtree);
}


static GtkWidget *
kz_tab_tree_new (KzSidebarEntry *entry, KzSidebar *sidebar)
{
	KzTabTree *tabtree;

	tabtree = g_object_new(KZ_TYPE_TAB_TREE,
			       "sidebar", sidebar,
			       "tree",    TRUE,
			       NULL);

	return GTK_WIDGET(tabtree);
}



/*****************************************************************************
 *                                                                           *
 *                       KzTabTree private methods                           *
 *                                                                           *
 *****************************************************************************/
static void
build_tab_tree (KzTabTree *tabtree, GNode *parent)
{
	GtkTreeIter iter, *parent_iter = NULL;
	GNode *node;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));
	g_return_if_fail(parent);

	if (parent->data)
		parent_iter = find_node(tabtree->store, parent->data);

	node = g_node_first_child(parent);
	for (; node; node = g_node_next_sibling(node))
	{
		KzEmbed *kzembed = node->data;
		GNode *child = g_node_first_child(node);

		if (KZ_IS_EMBED(kzembed))
		{
			parent_append(tabtree, kzembed, parent_iter, &iter);
		}
		else
		{
			g_warning("KzTabTree: Invalid tree item!");
		}

		if (child)
			build_tab_tree(tabtree, node);
	}

	if (parent_iter)
		gtk_tree_iter_free(parent_iter);
}


static void
build_tab_list (KzTabTree *tabtree)
{
	KzWindow *kz;
	GtkTreeIter iter;
	gint i, num;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	if (!tabtree->sidebar) return;

	kz = tabtree->sidebar->kz;
	if (!kz) return;

	num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));

	for (i = 0; i < num; i++)
	{
		KzEmbed *kzembed = KZ_EMBED(KZ_WINDOW_NTH_PAGE(kz, i));

		if (KZ_IS_EMBED(kzembed))
		{
			parent_append(tabtree, kzembed, NULL, &iter);
		}
		else
		{
			g_warning("KzTabTree: Invalid tree item!");
		}
	}
}


typedef struct _FindNode
{
	KzEmbed  *kzembed;
	GtkTreeIter *iter;
} FindNode;


static gboolean
find_node_func (GtkTreeModel *model,
		GtkTreePath *path, GtkTreeIter *iter,
		FindNode *data)
{
	KzEmbed *kzembed = NULL;

	if (!iter) return FALSE;

	gtk_tree_model_get(model, iter,
			   COLUMN_EMBEDER, &kzembed,
			   COLUMN_TERMINATOR);
	if (kzembed && data->kzembed && kzembed == data->kzembed)
	{
		data->iter = gtk_tree_iter_copy(iter);
		return TRUE;
	}

	return FALSE;
}


static GtkTreeIter *
find_node (GtkTreeStore *store, KzEmbed *kzembed)
{
	FindNode data;
	data.iter = NULL;
	data.kzembed = kzembed;
	gtk_tree_model_foreach(GTK_TREE_MODEL(store),
			       (GtkTreeModelForeachFunc) find_node_func,
			       &data);
	return data.iter;
}


static gboolean
remove_embed_signal_func (GtkTreeModel *model,
			  GtkTreePath *path, GtkTreeIter *iter,
			  gpointer data)
{
	KzEmbed *kzembed = NULL;
	KzTabTree *tabtree;

	g_return_val_if_fail(KZ_IS_TAB_TREE(data), FALSE);
	tabtree = KZ_TAB_TREE(data);

	g_return_val_if_fail(iter, FALSE);

	gtk_tree_model_get(model, iter,
			   COLUMN_EMBEDER, &kzembed,
			   COLUMN_TERMINATOR);
	g_return_val_if_fail(kzembed, FALSE);

	g_signal_handlers_disconnect_by_func(kzembed,
					     G_CALLBACK(cb_title_changed),
					     tabtree);
	g_signal_handlers_disconnect_by_func(kzembed,
					     G_CALLBACK(cb_net_start),
					     tabtree);
	g_signal_handlers_disconnect_by_func(kzembed,
					     G_CALLBACK(cb_net_stop),
					     tabtree);

	return FALSE;
}


static void
remove_all_embed_signals (KzTabTree *tabtree)
{
	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));
	g_return_if_fail(GTK_IS_TREE_MODEL(tabtree->store));

	gtk_tree_model_foreach(GTK_TREE_MODEL(tabtree->store),
			       remove_embed_signal_func,
			       tabtree);
}



/*****************************************************************************
 *                                                                           *
 *                               Callbacks                                   *
 *                                                                           *
 *****************************************************************************/
static void
cb_cursor_changed(GtkTreeView *tree_view, KzTabTree *tabtree)
{
	GtkTreePath *treepath;
	GtkTreeIter iter;
	KzWindow *kz;
	KzEmbed *kzembed = NULL;
	gint num;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	if (tabtree->switching) return;

	kz = tabtree->sidebar->kz;

	gtk_tree_view_get_cursor(tabtree->tree_view, &treepath, NULL);
	if (!treepath) return;

	gtk_tree_model_get_iter(GTK_TREE_MODEL(tabtree->store),
				&iter, treepath);
	gtk_tree_path_free(treepath);
	gtk_tree_model_get(GTK_TREE_MODEL(tabtree->store), &iter,
			   COLUMN_EMBEDER, &kzembed,
			   COLUMN_TERMINATOR);
	if (!kzembed) return;

	num = gtk_notebook_page_num(GTK_NOTEBOOK(kz->notebook),
				    GTK_WIDGET(kzembed));
	g_return_if_fail(num >= 0);

	tabtree->switching = TRUE;
	gtk_notebook_set_current_page(GTK_NOTEBOOK(kz->notebook), num);
	tabtree->switching = FALSE;
}


static gboolean
cb_tree_view_button_release (GtkWidget *widget, GdkEventButton *event,
			     KzTabTree *tabtree)
{
	GtkTreePath *treepath;
	GtkTreeIter iter;
	KzWindow *kz;
	KzEmbed *kzembed = NULL;
	GtkWidget *label;

	g_return_val_if_fail(KZ_IS_TAB_TREE(tabtree), FALSE);

	kz = tabtree->sidebar->kz;

	gtk_tree_view_get_cursor(tabtree->tree_view, &treepath, NULL);
	if (!treepath) return FALSE;

	gtk_tree_model_get_iter(GTK_TREE_MODEL(tabtree->store),
				&iter, treepath);
	gtk_tree_path_free(treepath);
	gtk_tree_model_get(GTK_TREE_MODEL(tabtree->store), &iter,
			   COLUMN_EMBEDER, &kzembed,
			   COLUMN_TERMINATOR);
	if (!kzembed) return FALSE;

	switch (event->button) {
	case 3:
		label = kz_window_get_tab_label(kz, GTK_WIDGET(kzembed));
		g_return_val_if_fail(KZ_IS_TAB_LABEL(label), FALSE);

		kz_actions_tab_popup_menu_modal(KZ_TAB_LABEL(label),
						event->button, event->time);
		return TRUE;
	default:
		break;
	}

	return FALSE;
}


static gboolean
cb_scroll_event (GtkWidget *widget, GdkEventScroll *event, KzTabTree *tabtree)
{
	gboolean retval = FALSE;

	g_return_val_if_fail(KZ_IS_TAB_TREE(tabtree), FALSE);

	switch (event->direction) {
	case GDK_SCROLL_UP:
		g_signal_emit_by_name(widget, "move-cursor",
				      GTK_MOVEMENT_DISPLAY_LINES, -1, &retval);
		break;
	case GDK_SCROLL_DOWN:
		g_signal_emit_by_name(widget, "move-cursor",
				      GTK_MOVEMENT_DISPLAY_LINES, 1, &retval);
		break;
	case GDK_SCROLL_LEFT:
	case GDK_SCROLL_RIGHT:
		break;
	default:
		g_warning ("Invalid scroll direction!");
		break;
	}

	return retval;
}


static void
cb_switch_page (GtkNotebook *notebook, GtkNotebookPage *page,
		guint page_num, KzTabTree *tabtree)
{
	GtkWidget *widget;
	GtkTreeIter *iter;
	GtkTreePath *treepath;
	KzTabLabel *label;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	if (tabtree->switching) return;

	widget = gtk_notebook_get_nth_page(notebook, page_num);
	iter = find_node(tabtree->store, KZ_EMBED(widget));
	/* g_return_if_fail(iter); */
	if (!iter) return;

	treepath = gtk_tree_model_get_path(GTK_TREE_MODEL(tabtree->store),
					   iter);
	g_return_if_fail(treepath);

	tabtree->switching = TRUE;
	gtk_tree_view_set_cursor(tabtree->tree_view, treepath, NULL, FALSE);

	label = KZ_TAB_LABEL(gtk_notebook_get_tab_label(notebook, widget));
	
	gtk_tree_store_set(tabtree->store, iter,
			   COLUMN_TITLE,   kz_embed_ensure_title(KZ_EMBED(widget)),
			   COLUMN_TITLE_COLOR, label_color[label->state],
			   COLUMN_TERMINATOR);

	if (!gtk_tree_view_row_expanded(tabtree->tree_view, treepath))
		gtk_tree_path_up(treepath);
		
        gtk_tree_view_expand_to_path(tabtree->tree_view, treepath);
	
	tabtree->switching = FALSE;

	gtk_tree_path_free(treepath);
	gtk_tree_iter_free(iter);
}


static void
cb_append_tab (KzWindow *kz, GtkWidget *widget, GtkWidget *parent,
	       KzTabTree *tabtree)
{
	GtkTreeIter iter;
	GtkTreePath *path;
	KzEmbed * kzembed = KZ_EMBED(widget); 

	g_return_if_fail(GTK_IS_WIDGET(widget));
	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	parent_append(tabtree, kzembed, find_node(tabtree->store, KZ_EMBED(parent)), &iter);
	
	path = gtk_tree_model_get_path(GTK_TREE_MODEL(tabtree->store), &iter);
	gtk_tree_view_expand_to_path(tabtree->tree_view, path);
	gtk_tree_path_free(path);
}


static void 
parent_append(KzTabTree * tabtree, KzEmbed * kzembed, GtkTreeIter * parent, GtkTreeIter * iter) {

	const gchar *color;
	gchar *title = kz_embed_ensure_title(kzembed);
	const gchar *uri   = kz_embed_get_location(kzembed);
	GdkPixbuf *favicon = kz_favicon_get_pixbuf(KZ_GET_FAVICON,
						   uri,
						   GTK_ICON_SIZE_MENU);

	KzTabLabel * label = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(tabtree->sidebar->kz->notebook), GTK_WIDGET(kzembed)));
	color = label_color[label->state];

	gtk_tree_store_append(tabtree->store, iter, parent);
	gtk_tree_store_set(tabtree->store, iter,
			   COLUMN_ICON,        favicon,
			   COLUMN_TITLE,       title,
			   COLUMN_TITLE_COLOR, color,
			   COLUMN_EMBEDER,     kzembed,
			   COLUMN_TERMINATOR);

	g_signal_connect(kzembed, "kz-title",
			 G_CALLBACK(cb_title_changed),
			 tabtree);
	g_signal_connect(kzembed, "kz-net-start",
			 G_CALLBACK(cb_net_start),
			 tabtree);
	g_signal_connect(kzembed, "kz-net-stop",
			 G_CALLBACK(cb_net_stop),
			 tabtree);

	g_free(title);
	if (favicon)
		g_object_unref(favicon);
}


static void
move_to(KzTabTree * tabtree, GtkTreeIter * child, GtkTreeIter * parent)
{
	KzEmbed * kzembed;
	GtkTreeIter newparent, newchild;
	GtkTreePath *path;
	
        g_return_if_fail(KZ_IS_TAB_TREE(tabtree));
	
	gtk_tree_model_get(GTK_TREE_MODEL(tabtree->store), child, COLUMN_EMBEDER, &kzembed, COLUMN_TERMINATOR);
	parent_append(tabtree, kzembed, parent, &newparent);

	path = gtk_tree_model_get_path(GTK_TREE_MODEL(tabtree->store),
                                       child);
	if (gtk_tree_path_up(path) && gtk_tree_view_row_expanded(tabtree->tree_view, path))
	{
                GtkTreePath * path2 = gtk_tree_model_get_path(GTK_TREE_MODEL(tabtree->store),
                                                              &newparent);
                gtk_tree_view_expand_to_path(tabtree->tree_view, path2);
                gtk_tree_path_free(path2);
	}
        gtk_tree_path_free(path);

        while (gtk_tree_model_iter_children(GTK_TREE_MODEL(tabtree->store), &newchild, child))
	{
                move_to(tabtree, &newchild, &newparent);
        }

        gtk_tree_store_remove(tabtree->store, child);

}

static void
cb_remove_tab (KzWindow *kz, GtkWidget *widget,
	       KzTabTree *tabtree)
{
	GtkTreeIter child;
	GtkTreeIter *parent;

	g_return_if_fail(GTK_IS_WIDGET(widget));
	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	parent = find_node(tabtree->store, KZ_EMBED(widget));

	while (gtk_tree_model_iter_children(GTK_TREE_MODEL(tabtree->store), &child, parent))
	{
		GtkTreeIter grand;
		if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(tabtree->store), &grand, parent))
		{
			move_to(tabtree, &child, &grand);
		}
		else
		{
			move_to(tabtree, &child, NULL);
		}
	}
	
	gtk_tree_store_remove(tabtree->store, parent);
}


static void
cb_reorder_tab (KzWindow *kz,
		GtkWidget *widget, gint pos,
		KzTabTree *tabtree)
{
	g_return_if_fail(GTK_IS_WIDGET(widget));
	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	if (!tabtree->tree)
	{
		gtk_tree_store_clear(tabtree->store);
		build_tab_list(tabtree);
	}
}



/*****************************************************************************
 *                                                                           *
 *                         Callbacks for Embed                               *
 *                                                                           *
 *****************************************************************************/
static void
cb_net_start (KzEmbed *embed, KzTabTree *tabtree)
{
	GtkTreeIter *iter;
	gchar *title;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	title = kz_embed_ensure_title(KZ_EMBED(embed));

	iter = find_node (tabtree->store, KZ_EMBED(embed));
	if (iter)
	{
		gtk_tree_store_set(tabtree->store, iter,
				   COLUMN_TITLE,       title,
				   COLUMN_TITLE_COLOR, label_color[KZ_TAB_LABEL_STATE_LOADING],
				   COLUMN_TERMINATOR);
		gtk_tree_iter_free(iter);
	}

	g_free(title);
}


static void
cb_net_stop (KzEmbed *embed, KzTabTree *tabtree)
{
	GtkTreeIter *iter;
	gchar *title;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	title = kz_embed_ensure_title(KZ_EMBED(embed));

	iter = find_node (tabtree->store, KZ_EMBED(embed));
	if (iter)
	{
		KzTabLabel * label = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(tabtree->sidebar->kz->notebook), GTK_WIDGET(embed)));

		gtk_tree_store_set(tabtree->store, iter,
				   COLUMN_TITLE,       title,
				   COLUMN_TITLE_COLOR,  
				   label_color[label->state],
				   COLUMN_TERMINATOR);

		gtk_tree_iter_free(iter);
	}

	g_free(title);
}


static void
cb_title_changed (KzEmbed *embed, KzTabTree *tabtree)
{
	gchar *title;
	GtkTreeIter *iter;

	g_return_if_fail(KZ_IS_TAB_TREE(tabtree));

	title = kz_embed_ensure_title(KZ_EMBED(embed));

	iter = find_node (tabtree->store, KZ_EMBED(embed));
	if (iter)
	{
                KzTabLabel * label = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(tabtree->sidebar->kz->notebook), GTK_WIDGET(embed)));

		gtk_tree_store_set(tabtree->store, iter,
				   COLUMN_TITLE, title,
				   COLUMN_TITLE_COLOR, label_color[label->state],
				   COLUMN_TERMINATOR);

		gtk_tree_iter_free(iter);
	}

	g_free(title);
}
