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

/*
 *  Copyright (C) 2002-2004 Hiroyuki Ikezoe
 *  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-window.h"

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

#include "kz-marshalers.h"
#include "kz-history-action.h"
#include "kz-zoom-action.h"
#include "gobject-utils.h"
#include "gtk-utils.h"
#include "kz-actions.h"
#include "kz-actions-popup.h"
#include "kz-actions-tab.h"
#include "kz-bookmark-bar.h"
#include "kz-embed.h"
#include "kz-tab-label.h"
#include "kz-icons.h"
#include "kazehakase.h"
#include "utils.h"
#include "mozilla.h"
#include "kz-sidebar.h"
#include "kz-feed-info.h"
#include "kz-download-box.h"
#include "kz-bookmark-menu.h"
#include "kz-bookmark-tab-menu.h"
#include "kz-bookmark-file.h"
#include "kz-paned.h"
#include "kz-entry.h"
#include "kz-proxy-menu.h"
#include "kz-popup-tablist.h"
#include "kz-ext.h"

extern GtkWidget *kz_moz_embed_new (const gchar *url);
#ifdef ENABLE_GTK_WEBCORE
extern GtkWidget *kz_khtml_new     (const gchar *url);
#endif /* ENABLE_GTK_WEBCORE */

#define MAX_CLOSED_TABS 10

enum {
	APPEND_TAB_SIGNAL,
	REMOVE_TAB_SIGNAL,
	REORDER_TAB_SIGNAL,
	LAST_SIGNAL
};

typedef struct _KzWindowPrivate	KzWindowPrivate;
struct _KzWindowPrivate
{
	guint       merge_id;

	const char *status_msg;
	char       *temp_msg;

	/* for find keyword */
	gboolean    did_find;
	GtkTooltips *find_tips[2];

	KzEmbedEventMouse *event;

	/* for popup & gesture */
	KzGesture  *gesture;
	gint start_x, start_y;
	gboolean is_gesture;

	/* for scroll */
	gint is_button3_pressed;
	gint is_button3_scrolled;
	
	/* sidebar */
	gboolean sidebar_was_shown;

	/* tabs */
	GList *open_hist;
	GList *view_hist;
	GNode *tab_tree;

	/* statusbar id */
	guint status_link_message;
	guint status_gesture;
	guint status_search;
};
#define KZ_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_WINDOW, KzWindowPrivate))

#define STATUS_LINK_MESSAGE "INTERNAL_LINK_MESSAGE"
#define STATUS_GESTURE "INTERNAL_GESTURE"
#define STATUS_SEARCH "INTERNAL_SEARCH"

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 struct {
	const gchar *pos;
	const gchar *tab_act;
	const gchar *sidebar_act;
} positems[] = {
	{"top",    "TabPosTop",    "SidebarPosTop"},
	{"bottom", "TabPosBottom", "SidebarPosBottom"},
	{"left",   "TabPosLeft",   "SidebarPosLeft"},
	{"right",  "TabPosRight",  "SidebarPosRight"},
};

static struct {
	const gint id;
	const gchar *name;
} modifier_map[] = {
	{KZ_ALT_KEY, "alt"},
	{KZ_CTRL_KEY, "ctrl"},
	{KZ_SHIFT_KEY,"shift"},
};

static gboolean key_theme_is_emacs = FALSE;

static void     kz_window_class_init     (KzWindowClass *klass);
static void     kz_window_init           (KzWindow      *kz);
static gboolean kz_window_delete_event   (GtkWidget     *widget,
					  GdkEventAny   *event);
static gboolean kz_window_key_press_event(GtkWidget     *widget,
					  GdkEventKey   *event);
static void     kz_window_destroy        (GtkObject     *object);

static void     kz_window_append_tab     (KzWindow      *kz,
					  GtkWidget     *widget,
					  GtkWidget     *parent);
static void     kz_window_remove_tab     (KzWindow      *kz,
					  GtkWidget     *widget);

static KzEmbed *kz_window_create_embed   (KzWindow      *kz,
					  const gchar   *url);

static void     kz_window_set_cur_embed_callbacks  (KzWindow   *kz,
						    KzEmbed    *kzembed);
static void     kz_window_unset_cur_embed_callbacks(KzWindow   *kz,
						    KzEmbed    *kzembed);
static void     kz_window_set_embed_callbacks      (KzWindow   *kz,
						    KzEmbed    *kzembed);
static void     kz_window_unset_embed_callbacks    (KzWindow   *kz,
						    KzEmbed    *kzembed);

static void     kz_window_restore_session     (KzWindow  *kz);
static void     kz_window_store_session       (KzWindow  *kz);

/* callbacks */
static void     cb_profile_global_changed     (KzProfile       *profile,
					       const gchar     *section,
					       const gchar     *key,
					       const gchar     *old_value,
					       KzWindow        *kz);
static void     cb_profile_proxy_changed      (KzProfile       *profile,
					       const gchar     *section,
					       const gchar     *key,
					       const gchar     *old_value,
					       KzWindow        *kz);

static void     cb_profile_gesture_changed    (KzProfile       *profile,
					       const gchar     *section,
					       const gchar     *key,
					       const gchar     *old_value,
					       KzWindow        *kz);
static void     cb_bookmark_bars_insert_child (KzBookmark      *bookmark,
					       KzBookmark      *child,
					       KzBookmark      *sibling,
					       KzWindow        *kz);
static void     cb_bookmark_bars_remove_child (KzBookmark      *bookmark,
					       KzBookmark      *child,
					       KzWindow        *kz);
static void     cb_bookmark_bars_reordered    (KzBookmark      *bookmark,
					       KzWindow        *kz);
static void     cb_smartbookmark_insert_child (KzBookmark      *bookmark,
					       KzBookmark      *child,
					       KzBookmark      *sibling,
					       KzWindow        *kz);
static void     cb_smartbookmark_remove_child (KzBookmark      *bookmark,
					       KzBookmark      *child,
					       KzWindow        *kz);
static void     cb_smartbookmark_reordered    (KzBookmark      *bookmark,
					       KzWindow        *kz);
static void     cb_menu_merge_add_widget      (GtkUIManager    *merge,
					       GtkWidget       *widget,
					       GtkBox          *box);
static void     cb_clipboard_get_text         (GtkClipboard *clipboard,
					       const gchar *text,
					       gpointer data);
static void     cb_notebook_switch_page       (GtkNotebook     *notebook,
					       GtkNotebookPage *page,
					       guint            page_num,
					       KzWindow        *kz);
static void     cb_notebook_switch_page_after (GtkNotebook     *notebook,
					       GtkNotebookPage *page,
					       guint            page_num,
					       KzWindow        *kz);
static void     cb_find_keyword               (GtkWidget       *widget,
					       KzWindow        *kz);
static gboolean cb_find_key_release           (GtkWidget       *widget,
					       GdkEventKey     *event,
					       KzWindow        *kz);
#if 0
static void     cb_find_grab_focus            (GtkWidget       *widget,
					       KzWindow        *kz);
#endif
static void     cb_find_direction_toggle      (GtkToggleButton *button,
					       KzWindow        *kz);
static void     cb_gesture_stack_motion       (KzGesture       *gesture,
					       KzGestureMotion  motion,
					       KzWindow        *kz);
static gboolean cb_notebook_scroll_event      (GtkWidget       *widget,
					       GdkEventScroll  *event,
					       KzWindow        *kz);
static void     cb_sidebar_map                (GtkWidget       *widget,
					       GtkToggleAction *action);
static void     cb_sidebar_unmap              (GtkWidget       *widget,
					       GtkToggleAction *action);
static gboolean cb_focus_out_event            (GtkWidget       *widget,
					       GdkEventFocus   *event,
					       KzWindow        *kz);
 
/* callbacks for embed */
static void     cb_embed_title_changed         (KzEmbed     *embed,
						KzWindow    *kz);
static void     cb_embed_location_changed      (KzEmbed     *embed,
						KzWindow    *kz);
static void     cb_embed_link_message          (KzEmbed     *embed,
						KzWindow    *kz);
static void     cb_embed_load_started          (KzEmbed     *embed,
						KzWindow    *kz);
static void     cb_embed_load_finished         (KzEmbed     *embed,
						KzWindow    *kz);
static void     cb_embed_new_window            (KzEmbed     *embed,
						KzEmbed    **new_embed,
						KzWindow    *kz);
static void     cb_embed_close_tab             (GtkObject   *obj,
						KzWindow    *kz);
static gint     cb_embed_dom_key_down          (KzEmbed     *embed,
						KzEmbedEventKey *event,
						KzWindow    *kz);
static gint     cb_embed_dom_key_up            (KzEmbed     *embed,
						KzEmbedEventKey *event,
						KzWindow    *kz);
static gint     cb_embed_dom_mouse_click       (KzEmbed     *embed,
						KzEmbedEventMouse *event,
						KzWindow    *kz);
static gint     cb_embed_dom_mouse_dbl_click   (KzEmbed     *embed,
						KzEmbedEventMouse *event,
						KzWindow    *kz);
static gint     cb_embed_dom_mouse_down        (KzEmbed     *embed,
						KzEmbedEventMouse *event,
						KzWindow    *kz);
static gint     cb_embed_dom_mouse_up          (KzEmbed *embed,
						KzEmbedEventMouse *event,
						KzWindow    *kz);

static gint     cb_embed_dom_mouse_over          (KzEmbed *embed,
						KzEmbedEventMouse *event,
						KzWindow    *kz);

/*
 * mozilla doesn't accept these signals, so we connect these funtions to
 * KzWindow instead of KzEmbed.
 */
static gboolean cb_window_motion_notify_event  (GtkWidget      *widget,
						GdkEventMotion *event,
						KzEmbed        *kzembed);
static gboolean cb_window_button_release_event (GtkWidget      *widget,
						GdkEventButton *event,
						KzEmbed        *kzembed);
static gboolean cb_window_scroll_event         (GtkWidget      *widget,
						GdkEventScroll *event,
						KzEmbed        *kzembed);

/* notebook received dropped url */
static void     cb_notebook_drag_data_received (GtkWidget        *widget,
						GdkDragContext   *drag_context,
						gint              x,
						gint              y,
						GtkSelectionData *data,
						guint             info,
						guint             time,
						KzWindow         *kz);

static void     gtk_key_theme_changed_cb       (GtkSettings  *settings,
                                                GParamSpec   *pspec,
                                                gpointer dummy);

void kz_window_sync_ui_level (KzWindow *kz);
void kz_window_sync_proxy (KzWindow *kz);


static GtkWindowClass *parent_class = NULL;
static gint kz_window_signals[LAST_SIGNAL] = {0};
static GList *window_list = NULL;


KZ_OBJECT_GET_TYPE(kz_window, "KzWindow", KzWindow,
		   kz_window_class_init, kz_window_init,
		   GTK_TYPE_WINDOW)

static void
kz_window_class_init (KzWindowClass *klass)
{
	GObjectClass *gobject_class;
	GtkObjectClass *object_class;
	GtkWidgetClass *widget_class;

	parent_class = g_type_class_peek_parent (klass);

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

	kz_window_signals[APPEND_TAB_SIGNAL]
		= g_signal_new ("append-tab",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzWindowClass, append_tab),
				NULL, NULL,
				_kz_marshal_VOID__OBJECT_OBJECT,
				G_TYPE_NONE, 2,
				GTK_TYPE_WIDGET, GTK_TYPE_WIDGET);

	kz_window_signals[REMOVE_TAB_SIGNAL]
		= g_signal_new ("remove-tab",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzWindowClass, remove_tab),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				GTK_TYPE_WIDGET);

	kz_window_signals[REORDER_TAB_SIGNAL]
		= g_signal_new ("reorder-tab",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzWindowClass, reorder_tab),
				NULL, NULL,
				_kz_marshal_VOID__OBJECT_INT,
				G_TYPE_NONE, 2,
				GTK_TYPE_WIDGET, G_TYPE_INT);

	/* GtkObject signals */
	object_class->destroy = kz_window_destroy;

	/* GtkWidget signals */
	widget_class->delete_event    = kz_window_delete_event;
	widget_class->key_press_event = kz_window_key_press_event;

	/* KzWindow signals */
	klass->append_tab = kz_window_append_tab;
	klass->remove_tab = kz_window_remove_tab;
	
	g_type_class_add_private (gobject_class, sizeof(KzWindowPrivate));
}

void
kz_window_connect_action (KzWindow *kz, GtkAction *action)
{
	GtkAccelGroup *accel_group;

	accel_group = gtk_ui_manager_get_accel_group(kz->menu_merge);

	gtk_action_set_accel_group(action, accel_group);
	gtk_action_connect_accelerator(action);
}

void
kz_window_disconnect_action (KzWindow *kz, GtkAction *action)
{
	gtk_action_disconnect_accelerator(action);
	gtk_action_set_accel_group(action, NULL);
}

static void
kz_window_setup_action_group (GtkActionGroup *action_group,
			      GtkAccelGroup *accel_group)
{
	GList *node, *action_list;

	action_list = gtk_action_group_list_actions(action_group);

	for (node = action_list; node; node = g_list_next(node))
	{
		GtkAction *action = GTK_ACTION(node->data);
		gtk_action_set_accel_group(action, accel_group);
		gtk_action_connect_accelerator(action);
	}
	g_list_free(action_list);
}

static void
kz_window_setup_action_groups (KzWindow *kz)
{
	GtkAccelGroup *accel_group;

	accel_group = gtk_ui_manager_get_accel_group(kz->menu_merge);

	kz_window_setup_action_group (kz->actions, accel_group);
	kz_window_setup_action_group (kz->popup_actions, accel_group);
	kz_window_setup_action_group (kz->tabpop_actions, accel_group);
}

static void
kz_window_init (KzWindow *kz)
{
	GtkWidget *menu_box;
	GtkSettings *setting;
	GtkWidget *vseparator;
	GList *children, *node;
	GtkSettings *settings;
	KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);

	g_object_set(G_OBJECT(kz), "allow-shrink", TRUE, NULL);

	gtk_window_set_title(GTK_WINDOW(kz), _("Kazehakase"));
	gtk_window_set_icon(GTK_WINDOW(kz), kz_icon);

	/* init member */
	kz->top_vbox           = gtk_vbox_new(FALSE, 0);

	kz->bookmark_bars_area = gtk_vbox_new(FALSE, 0);
	children = kz_bookmark_get_children(kz_bookmarks->bookmark_bars);
	for (node = children; node; node = g_list_next(node))
	{
		GtkWidget *bar = GTK_WIDGET(kz_bookmark_bar_new(kz, node->data));
		kz->bookmark_bars = g_list_append(kz->bookmark_bars, bar);
	}
	g_list_free(children);

	kz->statusbar          = gtk_statusbar_new();
	kz->notebook           = gtk_notebook_new();
	kz->statusbar_area     = gtk_hbox_new(FALSE, 0);
	kz->find_area          = kz_entry_new_with_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU);
	kz->find_direction     = gtk_toggle_button_new();
	kz->dlbox              = kz_download_box_new(kz);
	kz->grip_box           = gtk_statusbar_new();

	kz->feed_info          = g_object_ref(kz_feed_info_new(kz));

	kz->menu_merge         = gtk_ui_manager_new();

	kz->actions            = kz_actions_create_group(kz);
	kz->popup_actions      = kz_actions_popup_create_group(kz);
	kz->tabpop_actions     = kz_actions_tab_popup_create_group(kz);
	
	kz->kzfav              = kz_favicon_get_instance();
	kz->popup              = kz_popup_preview_get_instance();
	kz->popup_tablist      = NULL;

	priv->merge_id     = 0;
	priv->status_msg   = NULL;
	priv->temp_msg     = NULL;

	priv->did_find     = FALSE;

	priv->event        = NULL;

	priv->gesture      = kz_gesture_new();
	priv->start_x      = 0;
	priv->start_y      = 0;
	priv->is_gesture   = FALSE;

	priv->is_button3_pressed  = FALSE;
	priv->is_button3_scrolled = FALSE;
	
	priv->sidebar_was_shown = FALSE;

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

	priv->status_link_message =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(kz->statusbar),
					     STATUS_LINK_MESSAGE);
	priv->status_gesture =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(kz->statusbar),
					     STATUS_GESTURE);
	priv->status_search =
		gtk_statusbar_get_context_id(GTK_STATUSBAR(kz->statusbar),
					     STATUS_SEARCH);

	kz->tabs   = kz_bookmark_pure_folder_new();
	kz->closed_tabs     = kz_bookmark_pure_folder_new();
	kz->history_search  = kz_bookmark_pure_folder_new();
	
	kz->is_closing_all  = FALSE;

	kz_ext_setup(kz);

	kz_window_setup_action_groups(kz);

	/* gesture */
	{
		kz_window_update_gesture_items(kz);
		g_signal_connect(priv->gesture,
				 "stack_motion",
				 G_CALLBACK(cb_gesture_stack_motion), kz);
		g_signal_connect(kz_global_profile,
				 "changed::Gesture",
				 G_CALLBACK(cb_profile_gesture_changed), kz);
		g_signal_connect(kz,
				 "focus-out-event",
				 G_CALLBACK(cb_focus_out_event), kz);
	}

	/* top level vbox */
	{
		gtk_container_add(GTK_CONTAINER(kz),
				  kz->top_vbox);
		gtk_widget_show(kz->top_vbox);
	}

	/* menu & toolbar */
	{
		GtkAccelGroup *accel_group;

		kz->menu_box = menu_box = gtk_vbox_new (FALSE, 0);
		gtk_box_pack_start(GTK_BOX(kz->top_vbox), menu_box,
				   FALSE, FALSE, 0);   
		gtk_widget_show(menu_box);

		gtk_ui_manager_insert_action_group(kz->menu_merge,
						   kz->actions, 0);
		gtk_ui_manager_insert_action_group(kz->menu_merge,
						   kz->popup_actions, 0);
		gtk_ui_manager_insert_action_group(kz->menu_merge,
						   kz->tabpop_actions, 0);
		
		g_signal_connect(kz->menu_merge, "add_widget",
				 G_CALLBACK(cb_menu_merge_add_widget),
				 menu_box);
		accel_group = gtk_ui_manager_get_accel_group(kz->menu_merge);
		gtk_window_add_accel_group(GTK_WINDOW(kz), accel_group);

		kz_window_sync_ui_level(kz);
		gtk_ui_manager_ensure_update(kz->menu_merge);
		g_signal_connect(kz_global_profile,
				 "changed::Global",
				 G_CALLBACK(cb_profile_global_changed), kz);
		g_signal_connect(kz_proxy,
				 "changed",
				 G_CALLBACK(cb_profile_proxy_changed), kz);


		/* FIXME */
		setting = gtk_settings_get_default();

		if (setting)
		{
			gtk_settings_set_long_property (setting,
							"gtk-toolbar-style",
							GTK_TOOLBAR_ICONS,
							"");
		}

	}

	/* smart bookmark */
	g_signal_connect_after(kz_bookmarks->smarts,
			       "insert-child",
			       G_CALLBACK(cb_smartbookmark_insert_child), kz);
	g_signal_connect_after(kz_bookmarks->smarts,
			       "remove-child",
			       G_CALLBACK(cb_smartbookmark_remove_child), kz);
	g_signal_connect_after(kz_bookmarks->smarts,
			       "children-reordered",
			       G_CALLBACK(cb_smartbookmark_reordered), kz);
	kz_actions_update_smartbookmarks(kz, kz_bookmarks->smarts);

	/* bookmark bar */
	gtk_box_pack_start(GTK_BOX(kz->top_vbox), 
			   kz->bookmark_bars_area, FALSE, FALSE, 0);
	/* gtk_widget_show(kz->bookmark_bars_area); */

	for (node = kz->bookmark_bars;
	     node;
	     node = g_list_next(node))
	{
		gtk_box_pack_start(GTK_BOX(kz->bookmark_bars_area), 
				   node->data, FALSE, FALSE, 0);    
		gtk_widget_show(node->data);
	}

	g_signal_connect_after(kz_bookmarks->bookmark_bars,
			       "insert-child",
			       G_CALLBACK(cb_bookmark_bars_insert_child), kz);
	g_signal_connect_after(kz_bookmarks->bookmark_bars,
			       "remove-child",
			       G_CALLBACK(cb_bookmark_bars_remove_child), kz);
	g_signal_connect_after(kz_bookmarks->bookmark_bars,
			       "children-reordered",
			       G_CALLBACK(cb_bookmark_bars_reordered), kz);

	/* paned widget to separate sidebar and main contents */
	{
		kz->pane = kz_paned_new();
		gtk_container_add(GTK_CONTAINER(kz->top_vbox), kz->pane);
		gtk_widget_show(kz->pane);
	}

	/* main notebook widget */
	{
		gtk_notebook_set_show_tabs(GTK_NOTEBOOK(kz->notebook), TRUE);
		gtk_notebook_set_scrollable(GTK_NOTEBOOK(kz->notebook), TRUE);
		g_signal_connect(kz->notebook, "switch-page", 
				 G_CALLBACK(cb_notebook_switch_page), kz);
		g_signal_connect_after(kz->notebook, "switch-page",
				 G_CALLBACK(cb_notebook_switch_page_after), kz);
		g_signal_connect(kz->notebook, "scroll-event",
				 G_CALLBACK(cb_notebook_scroll_event), kz);

		gtk_drag_dest_set(GTK_WIDGET(kz->notebook),
				  GTK_DEST_DEFAULT_ALL,
				  url_drag_types, G_N_ELEMENTS (url_drag_types),
				  GDK_ACTION_MOVE);
		g_signal_connect(kz->notebook, "drag-data-received",
				 G_CALLBACK(cb_notebook_drag_data_received),
				 kz);
		gtk_paned_add2(GTK_PANED(kz->pane), kz->notebook);
		gtk_widget_show(kz->notebook);

		/* sidebar */
		kz->sidebar = kz_sidebar_new(kz);
		gtk_widget_set_size_request(kz->sidebar, 150, -1);
		gtk_paned_add1(GTK_PANED(kz->pane), kz->sidebar);
		/* gtk_widget_show(kz->sidebar); */
		{
			GtkAction *action;
			action = gtk_action_group_get_action(kz->actions,
							     "ShowHideSidebar");
			g_signal_connect(kz->sidebar, "map",
					 G_CALLBACK(cb_sidebar_map), action);
			g_signal_connect(kz->sidebar, "unmap",
					 G_CALLBACK(cb_sidebar_unmap), action);
		}

		/* embed tab */
		/* curpage = kz_window_open_new_tab(kz, NULL); */
	}

	/* search area */
	{
		gtk_box_pack_start(GTK_BOX(kz->top_vbox), kz->statusbar_area,
				   FALSE, FALSE, 0);   
		gtk_widget_show(kz->statusbar_area);
	}

	/* find direction toggle */
	{
		GtkWidget *toggle, *arrow;
		GtkTooltips *tips = gtk_tooltips_new();

		toggle = kz->find_direction;
		arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
		gtk_container_add(GTK_CONTAINER(toggle), arrow);
		gtk_box_pack_start(GTK_BOX(kz->statusbar_area), toggle,
				   FALSE, FALSE, 0); 
		gtk_widget_show(arrow);
		gtk_widget_show(toggle);

		kz_entry_set_backtext(KZ_ENTRY(kz->find_area),
				      _("Find in this page"));
		g_signal_connect(kz->find_area, "activate",
				 G_CALLBACK(cb_find_keyword), kz);
		g_signal_connect(toggle, "toggled",
				 G_CALLBACK(cb_find_direction_toggle), kz);

		g_object_ref(G_OBJECT(tips));
		gtk_object_sink(GTK_OBJECT(tips));
		gtk_tooltips_set_tip(tips, toggle,
				     _("Find direction"), NULL);
		priv->find_tips[0] = tips;
	}

	/* find entry */
	{
		GtkTooltips *tips = gtk_tooltips_new();

		gtk_box_pack_start(GTK_BOX(kz->statusbar_area), kz->find_area,
				   FALSE, FALSE, 0); 
		g_signal_connect(kz->find_area, 
				 "key-release-event",
				 G_CALLBACK(cb_find_key_release), kz);
		/*
		g_signal_connect(kz->find_area, 
				 "grab-focus",
				 G_CALLBACK(cb_find_grab_focus), kz);
		*/
		gtk_widget_show(kz->find_area);

		g_object_ref(G_OBJECT(tips));
		gtk_object_sink(GTK_OBJECT(tips));
		gtk_tooltips_set_tip(tips, kz->find_area,
				     _("Find a word or phrase in this page"), NULL);
		priv->find_tips[1] = tips;
	}

	/* grip box */
	{
		/* Why 18?
		   Because gtk_status_bar's max grip width is 18. */
		gtk_widget_set_size_request(kz->grip_box, 18, -1);
		gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(kz->grip_box),
						  TRUE);
		gtk_box_pack_end(GTK_BOX(kz->statusbar_area), kz->grip_box,
				 FALSE, FALSE, 0);
		gtk_widget_show(kz->grip_box);
		vseparator = gtk_vseparator_new();
		gtk_box_pack_end(GTK_BOX(kz->statusbar_area), vseparator,
				 FALSE, FALSE, 0);
		gtk_widget_show(vseparator);
	}
	
	/* download box */
	{
		gtk_widget_set_size_request(kz->dlbox, 200, -1);
		gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(kz->dlbox),
						  FALSE);
		gtk_statusbar_push(GTK_STATUSBAR(kz->dlbox), 0,
				   _("Drop link to download"));
		gtk_box_pack_end(GTK_BOX(kz->statusbar_area), kz->dlbox,
				 FALSE, FALSE, 0);
		gtk_widget_show(kz->dlbox);
	}
	
	/* status bar */
	{
		vseparator = gtk_vseparator_new();
		gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(kz->statusbar),
						  FALSE);
		gtk_box_pack_start(GTK_BOX(kz->statusbar_area), kz->statusbar,
				   TRUE, TRUE, 0);
		gtk_widget_show(kz->statusbar);
		gtk_box_pack_start(GTK_BOX(kz->statusbar_area),
				   vseparator,
				   FALSE, FALSE, 0);
		gtk_widget_show(vseparator);
	}
	
	/* from Galeon-1.3.18 */
	/* initialize the listener for the key theme */
	settings = gtk_settings_get_default();
	g_signal_connect (settings,
			  "notify::gtk-key-theme-name",
			  G_CALLBACK (gtk_key_theme_changed_cb),
			  NULL);
	gtk_key_theme_changed_cb (settings, 0, 0);

	window_list = g_list_append(window_list, kz);
	kz_bookmark_set_title(kz->tabs, "Window");
	kz_bookmark_append(kz_bookmarks->current_session, kz->tabs);

	/* kz_actions_set_sensitive(kz, KZ_EMBED(curpage)); */
	kz_window_restore_state(kz);

	kz_window_restore_session(kz); 
}


static void
kz_window_append_tab (KzWindow *kz, GtkWidget *widget, GtkWidget *parent)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));
	kz_actions_set_tab_sensitive(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
}


static void
kz_window_remove_tab (KzWindow *kz, GtkWidget *widget)
{

	g_return_if_fail(KZ_IS_WINDOW(kz));
	kz_actions_set_sensitive(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
	kz_actions_set_tab_sensitive(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
}


GList *
kz_window_get_window_list(void)
{
	return window_list;
}


GtkWidget *
kz_window_new(const gchar *url)
{
	KzWindow *kz = g_object_new(KZ_TYPE_WINDOW,
				   "type", GTK_WINDOW_TOPLEVEL,
				    NULL);
	
	if (url)
		kz_window_open_new_tab (kz, url);

	return GTK_WIDGET (kz);
}


void
kz_window_sync_ui_level (KzWindow *kz)
{
	GtkAction *action;
	const gchar *action_str;
	GError *err = NULL;
	gchar *ui_file;
	GtkWidget *widget;
	KzEmbed *kzembed;
	KzWindowPrivate *priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	priv = KZ_WINDOW_GET_PRIVATE (kz);

	switch (kz_ui_level()) 
	{
	case KZ_UI_LEVEL_CUSTOM:
		action_str = "UILevelCustom";
		ui_file = g_build_filename(g_get_home_dir(),
				           "."PACKAGE,
					   "kz-ui.xml",
				           NULL);
		break;
	case KZ_UI_LEVEL_EXPERT:
		action_str = "UILevelExpert";
		ui_file = g_strdup(KZ_SYSCONFDIR"/kz-ui-expert.xml");
		break;
	case KZ_UI_LEVEL_MEDIUM:
		action_str = "UILevelMedium";
		ui_file = g_strdup(KZ_SYSCONFDIR"/kz-ui-medium.xml");
		break;
	case KZ_UI_LEVEL_BEGINNER:
	default:
		action_str = "UILevelBeginner";
		ui_file = g_strdup(KZ_SYSCONFDIR"/kz-ui-beginner.xml");
		break;
	}

	action = gtk_action_group_get_action(kz->actions, action_str);
	if (!gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)))
		gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), TRUE);

	if (priv->merge_id != 0)
	{
		/* remove feed_info explicitly */
		GtkWidget *menu_bar;
		menu_bar = gtk_ui_manager_get_widget(kz->menu_merge,
						     "/menubar");
		gtk_container_remove(GTK_CONTAINER(menu_bar), kz->feed_info);

		gtk_ui_manager_remove_ui(kz->menu_merge, priv->merge_id);
		priv->merge_id = 0;
		kz_actions_remove_smartbookmarks(kz, kz_bookmarks->smarts);
        	gtk_ui_manager_ensure_update(kz->menu_merge);
	}

	priv->merge_id = gtk_ui_manager_add_ui_from_file(kz->menu_merge,
						         ui_file, &err);
	if (err)
	{
		g_warning("%s", err->message);
		g_error_free(err);
	}
        gtk_ui_manager_ensure_update(kz->menu_merge);

	/* update bookmarks */
	widget = gtk_ui_manager_get_widget(kz->menu_merge, 
					   "/menubar/BookmarksMenu");
	if (GTK_IS_MENU_ITEM(widget))
		widget = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
	if (GTK_IS_MENU_SHELL(widget))
	{
		kz_bookmark_menu_remove_menuitems(GTK_MENU_SHELL(widget), kz);
		kz_bookmark_menu_append_menuitems(GTK_MENU_SHELL(widget), kz,
						  kz_bookmarks->menu);
	}

	/* update clips */
	widget = gtk_ui_manager_get_widget(kz->menu_merge,
					   "/menubar/ToolsMenu/ClipMenu");
	if (GTK_IS_MENU_ITEM(widget))
		widget = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
	if (GTK_IS_MENU_SHELL(widget))
	{
		kz_bookmark_menu_remove_menuitems(GTK_MENU_SHELL(widget), kz);
		kz_bookmark_menu_append_menuitems(GTK_MENU_SHELL(widget), kz,
						  kz_bookmarks->clip);
	}

	/* append recent close tab menu */
	widget = gtk_ui_manager_get_widget(kz->menu_merge,
					   "/menubar/TabMenu/RecentCloseTabMenu");

	if (GTK_IS_MENU_ITEM(widget))
	{
		GtkWidget *submenu;
		submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget));
		if (!submenu)
		{
			submenu = gtk_menu_new();
			gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), submenu);
		}
		if (GTK_IS_MENU_SHELL(submenu))
		{
			kz_bookmark_menu_remove_tab_menuitems(GTK_MENU_SHELL(submenu), kz);
			kz_bookmark_menu_append_tab_menuitems(GTK_MENU_SHELL(submenu), kz,
							      kz->closed_tabs);
		}
	}

	/* feed info */
	{
		GtkWidget *menu_bar;
		menu_bar = gtk_ui_manager_get_widget(kz->menu_merge,
						     "/menubar");

		gtk_container_add(GTK_CONTAINER(menu_bar), kz->feed_info);
		gtk_menu_item_set_right_justified(GTK_MENU_ITEM(kz->feed_info),
						  TRUE);
	}

	/* update proxy */
	kz_window_sync_proxy(kz);

	/* for stop/reload button updating */
	kzembed = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz));
	kz_actions_set_sensitive(kz, kzembed);

	/* smart bookmark */
	kz_actions_update_smartbookmarks(kz, kz_bookmarks->smarts);

	g_free(ui_file);
}

void
kz_window_sync_proxy (KzWindow *kz)
{
	GtkWidget *menuitem, *submenu = NULL;
	GtkAction *action;
	gboolean use_proxy = FALSE;

	KZ_CONF_GET("Global", "use_proxy", use_proxy, BOOL);
	
	action = gtk_action_group_get_action(kz->actions,
					     "ToggleProxyUse");
	if (action)
		gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
					     use_proxy);
	KZ_WINDOW_SET_VISIBLE(kz, "StockProxyMenu", use_proxy);
	if (!use_proxy) return;

	menuitem = gtk_ui_manager_get_widget(kz->menu_merge,
					     "/menubar/EditMenu/ProxyMenu");
	if (!GTK_IS_MENU_ITEM(menuitem))
		return;
	
	submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menuitem));

	if (!submenu)
		submenu = gtk_menu_new();

	kz_proxy_menu_remove_menuitems(GTK_MENU_SHELL(submenu), kz);
	kz_proxy_menu_append_menuitems(GTK_MENU_SHELL(submenu), kz);

	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
}

GtkWidget *
kz_window_get_from_tab (GtkWidget *tab_widget)
{
	GList *node;

	for (node = window_list; node; node = g_list_next (node))
	{
		KzWindow *kz = node->data;
		GtkWidget *label;

		if (!KZ_IS_WINDOW(kz)) continue;

		label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
						   tab_widget);
		if (label)
			return GTK_WIDGET(kz);
	}

	return NULL;
}


GtkWidget *
kz_window_open_new_tab (KzWindow *kz, const gchar *url)
{
	return kz_window_open_new_tab_with_parent (kz, url, NULL);
}


/* It's temporary implementation */
static KzEmbed *
kz_window_create_embed (KzWindow *kz, const gchar *url)
{
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);

#ifdef ENABLE_GTK_WEBCORE
	{
		gchar *engine;
		engine = KZ_CONF_GET_STR("Global", "layout_engine");
		if (engine && !strcmp(engine, "gtk+-webcore"))
		{
			g_free(engine);
			return KZ_EMBED(kz_khtml_new(url));
		}
		else
		{
			g_free(engine);
			return KZ_EMBED(kz_moz_embed_new(url));
		}
	}
#else /* ENABLE_GTK_WEBCORE */
	return KZ_EMBED(kz_moz_embed_new(url));
#endif /* ENABLE_GTK_WEBCORE */
}


GtkWidget *
kz_window_open_new_tab_at_tail (KzWindow *kz, const gchar *url)
{
	KzEmbed *kzembed;
	KzTabLabel *kztab;
	GNode *node;
	KzWindowPrivate *priv;
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	kzembed = kz_window_create_embed(kz, url);
	kztab = KZ_TAB_LABEL(kz_tab_label_new(kz, kzembed));

	gtk_widget_show(GTK_WIDGET(kzembed));
	gtk_widget_show(GTK_WIDGET(kztab));
	gtk_notebook_append_page(GTK_NOTEBOOK(kz->notebook),
				 GTK_WIDGET(kzembed),
				 GTK_WIDGET(kztab));
	kz_window_set_embed_callbacks(kz, kzembed);

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

	/* root node */
	if (!priv->tab_tree)
		priv->tab_tree = g_node_new(NULL);

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

	kz_bookmark_append(kz->tabs, kztab->history);

	g_signal_emit(kz, kz_window_signals[APPEND_TAB_SIGNAL],
		      0, kzembed, NULL);

	return GTK_WIDGET(kzembed);
}

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

	KZ_CONF_GET("Tab", "new_tab_position", pos_str, STRING);
	
	if (!pos_str || !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(kz->notebook));
	}
	else if (!strcasecmp(pos_str, "right"))
	{
		pos = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(kz->notebook));
	}
	else if (!strcasecmp(pos_str, "unread_right"))
	{
		gint i = 1 + gtk_notebook_get_current_page(GTK_NOTEBOOK(kz->notebook));
		gint num = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));
		
		if (i > num)
		{
			pos = -1;
		}
		else
		{
			while (i < num)
			{
				GtkWidget *kzembed;
				GtkWidget *label;
				KzTabLabelState state;

				kzembed = GTK_WIDGET(KZ_WINDOW_NTH_PAGE(kz, i));
				label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
								   kzembed);
				state = kz_tab_label_get_state(KZ_TAB_LABEL(label));
				if (state == KZ_TAB_LABEL_STATE_NORMAL)
					break;
				i++;
			}
			pos = i;
		}
	}

	return pos;
}


GtkWidget *
kz_window_open_new_tab_with_parent (KzWindow *kz, const gchar *url,
				    GtkWidget *parent)
{
	KzEmbed *kzembed;
	GtkWidget *sibembed;
	KzTabLabel *kztab, *sibtab;
	GNode *node, *parent_node;
	gint pos;
	KzWindowPrivate *priv;
	
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	if (url) g_return_val_if_fail(g_utf8_validate(url, strlen(url), NULL), NULL);

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	kzembed = kz_window_create_embed(kz, url);
	kztab = KZ_TAB_LABEL(kz_tab_label_new(kz, kzembed));

	gtk_widget_show(GTK_WIDGET(kzembed));
	gtk_widget_show(GTK_WIDGET(kztab));

	pos = get_insert_tab_position (kz);

	gtk_notebook_insert_page(GTK_NOTEBOOK(kz->notebook),
				 GTK_WIDGET(kzembed),
				 GTK_WIDGET (kztab),
				 pos);			

	/* insert tab bookmark */
	sibembed = gtk_notebook_get_nth_page(GTK_NOTEBOOK(kz->notebook), pos);
	sibtab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
			      sibembed));

	kz_bookmark_insert_before(kz->tabs, 
				  kztab->history,
				  sibtab->history);

	kz_window_set_embed_callbacks(kz, kzembed);

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

	/* root node */
	if (!priv->tab_tree)
		priv->tab_tree = g_node_new(NULL);

	/* insret node */
	node = g_node_new(kzembed);
	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);

	g_signal_emit(kz, kz_window_signals[APPEND_TAB_SIGNAL],
		      0, kzembed, parent);

	return GTK_WIDGET(kzembed);
}


void
kz_window_close_tab (KzWindow *kz, GtkWidget *widget)
{
	gchar *ret_page = NULL;
	KzEmbed *next = NULL;
	KzTabLabel *kztab = NULL;
	KzWindowPrivate *priv;
	
	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(GTK_IS_WIDGET(widget));

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
							widget));
	if (kztab && kz_tab_label_get_lock(kztab)) {
/* kztab is locked, so return without closing tab */
		return;

	} else if (KZ_WINDOW_CURRENT_PAGE(kz) != widget) {
/* 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 && !strcmp(ret_page, "last_shown"))
	{
		GList *node;

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

		node = priv->open_hist;
		for (; node && !next; node = g_list_next(node))
		{
			if (KZ_WINDOW_CURRENT_PAGE(kz) != node->data)
				next = node->data;
		}
	}
	else if (ret_page && !strcmp(ret_page, "prev_tab"))
	{
		gtk_notebook_prev_page(GTK_NOTEBOOK(kz->notebook));
	}
	else if (ret_page && !strcmp(ret_page, "next_tab"))
	{
		gtk_notebook_next_page(GTK_NOTEBOOK(kz->notebook));
	}

	if (next)
	{
		gint num;

		num = gtk_notebook_page_num
			(GTK_NOTEBOOK(kz->notebook),
			 GTK_WIDGET(next));
		gtk_notebook_set_current_page
			(GTK_NOTEBOOK(kz->notebook), num);
	}

CLOSE_TAB:
	g_free(ret_page);
	gtk_widget_destroy(widget);

	if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook)) == 0)
		gtk_window_set_title(GTK_WINDOW(kz), _("Kazehakase"));
}


void
kz_window_close_all_tab(KzWindow *kz)
{
	gint n_pages, i;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz->is_closing_all = TRUE;
	n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));
	for (i = n_pages - 1; i >= 0; i--)
	{
		KzTabLabel *kztab;
		GtkWidget *widget = GTK_WIDGET(KZ_WINDOW_NTH_PAGE(kz, i));
		/* FIXME! silly! */
		kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(
					GTK_NOTEBOOK(kz->notebook),
					widget));

		gtk_widget_destroy(widget);
	}
	kz->is_closing_all = FALSE;
}


void
kz_window_reorder_tab (KzWindow *kz, GtkWidget *widget, gint pos)
{
	gint n_pages, cur_pos, sib_pos = pos;
	KzTabLabel *kztab, *sibtab;
	KzBookmark *sib_bookmark = NULL;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(GTK_IS_WIDGET(widget));

	n_pages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));
	g_return_if_fail(pos >= 0 && pos < n_pages);

	cur_pos = gtk_notebook_page_num(GTK_NOTEBOOK(kz->notebook), widget);
	if (cur_pos == pos)
		return;

	if (cur_pos < pos)
		sib_pos = sib_pos + 1;

	kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
							widget));
	if (sib_pos < n_pages)
	{
		sibtab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(
					GTK_NOTEBOOK(kz->notebook),
					KZ_WINDOW_NTH_PAGE(kz, sib_pos)));
		sib_bookmark = sibtab->history;
	}

	gtk_notebook_reorder_child (GTK_NOTEBOOK(kz->notebook),
				    widget, pos);
	
	g_object_ref(kztab->history);
	kz_bookmark_remove(kz->tabs, kztab->history);
	kz_bookmark_insert_before(kz->tabs, 
				  kztab->history,
				  sib_bookmark);
	g_object_unref(kztab->history);

	kz_window_store_session(kz);

	g_signal_emit(kz, kz_window_signals[REORDER_TAB_SIGNAL],
		      0, widget, pos);
}


/* FIXME!! It's a hacky way. */
void
kz_window_move_tab (KzWindow *kz, GtkWidget *widget)
{
	GtkNotebook *src_notebook;
	GtkWidget *label;
	KzWindow *src_kz;
	KzEmbed *kzembed;
	KzTabLabel *new_kztab;
	GNode *node, *child;
	KzWindowPrivate *priv, *src_priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(KZ_IS_EMBED(widget));

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	kzembed = KZ_EMBED(widget);

	src_kz = KZ_WINDOW(kz_window_get_from_tab(widget));
	if (!src_kz) return;

	src_priv = KZ_WINDOW_GET_PRIVATE (src_kz);
	label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
					   widget);

	/* the kzembed is already the kz's child */
	if (label) return;

	src_notebook = GTK_NOTEBOOK(src_kz->notebook);
	label = gtk_notebook_get_tab_label(src_notebook,
					   widget);
	g_return_if_fail(label);

	/* create new tab label */
	new_kztab = KZ_TAB_LABEL(kz_tab_label_new(kz, kzembed));

	/* move the page to this window */
	kz_window_unset_cur_embed_callbacks(src_kz, kzembed);
	kz_window_unset_embed_callbacks(src_kz, kzembed);
	g_object_ref(widget);
	gtk_container_remove(GTK_CONTAINER(src_kz->notebook), widget);
	gtk_notebook_prepend_page(GTK_NOTEBOOK(kz->notebook),
				  widget,
				  GTK_WIDGET(new_kztab));
	kz_bookmark_prepend(kz->tabs, new_kztab->history);
	kz_window_set_embed_callbacks(kz, kzembed);

	/* remove view_hist */
	src_priv->view_hist = g_list_remove(src_priv->view_hist,
						kzembed);
	/* move open_hist */
	src_priv->open_hist = g_list_remove(src_priv->open_hist,
						kzembed);
	priv->open_hist = g_list_prepend(priv->open_hist,
					     kzembed);

	/* 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 (!priv->tab_tree)
			priv->tab_tree = g_node_new(NULL);
		g_node_append(priv->tab_tree, node);
		
		g_signal_emit(src_kz,
			      kz_window_signals[REMOVE_TAB_SIGNAL],
			      0, kzembed);
		g_signal_emit(kz,
			      kz_window_signals[APPEND_TAB_SIGNAL],
			      0, kzembed, NULL);
	}
	else
	{
		g_warning("KzWindow: cannot find tab node!");
	}
}


static gboolean
kz_window_delete_event (GtkWidget *widget, GdkEventAny *event)
{
	KzWindow *kz;

	kz = KZ_WINDOW(widget);

	kz_window_store_state(kz);
	kz_window_close_all_tab(kz);

	return FALSE;
}


/* from Galeon-1.3.18 */
static void
gtk_key_theme_changed_cb (GtkSettings  *settings,
                          GParamSpec   *pspec,
                          gpointer dummy)
{
	gchar *key_theme_name;

	g_object_get (settings,
		      "gtk-key-theme-name", &key_theme_name,
		      NULL);
	if (key_theme_name && g_ascii_strcasecmp (key_theme_name, "Emacs") == 0)
	{
		key_theme_is_emacs = TRUE;
	}
	else
	{
		key_theme_is_emacs = FALSE;
	}
	g_free (key_theme_name);
}

static gboolean
kz_window_key_press_event(GtkWidget *widget, GdkEventKey *event)
{
	KzWindow *kz;

	gboolean shortcircuit;
	gboolean force_chain;
	gboolean handled;
	guint modifier;

	kz = KZ_WINDOW(widget);

	/* In an attempt to get the mozembed playing nice with things like emacs keybindings
	 * we are passing important events to the focused child widget before letting the window's
	 * base handler see them. This is *completely against* stated gtk2 policy but the 
	 * 'correct' behaviour is exceptionally useless. We need to keep an eye out for 
	 * unexpected consequences of this decision. IME's should be a high concern, but 
	 * considering that the IME folks complained about the upside-down event propagation
	 * rules, we might be doing them a favour.
	 */

	shortcircuit = FALSE;
	force_chain  = FALSE;
	handled = FALSE;
	modifier = event->state & gtk_accelerator_get_default_mod_mask ();
	/* guint i; */

	if (event->keyval == GDK_Escape)
	{
		/* Always pass Escape to both the widget, and the parent */
		shortcircuit = TRUE;
		force_chain = TRUE;
	}
	else if (key_theme_is_emacs && 
		 (modifier == GDK_CONTROL_MASK) && event->length > 0 &&
		 /* But don't pass Ctrl+Enter twice */
		 event->keyval != GDK_Return)
	{
		/* Pass CTRL+letter characters to the widget */
		shortcircuit = TRUE;
	}

	if (shortcircuit)
	{
		GtkWidget *widget = gtk_window_get_focus (GTK_WINDOW (kz));

		if (GTK_IS_WIDGET (widget))
		{
			handled = gtk_widget_event (widget, (GdkEvent*)event);
		}

		if (handled && !force_chain)
		{
			return handled;
		}
	}
#if 0
	/* Handle accelerators that we want bound, but aren't associated with
	 * an action */
	for (i = 0; i < G_N_ELEMENTS (extra_keybindings); i++)
	{
		if (modifier == extra_keybindings[i].modifier &&
		    event->keyval == extra_keybindings[i].keyval)
		{
			GtkAction *action;
			action = gtk_action_group_get_action 
				(kz->actions, 
				 extra_keybindings[i].action);
			gtk_action_activate(action);
			return TRUE;
		}
	}
#endif
#if 0
	if (event->keyval == GDK_F10 && modifier == 0 &&
	    priv->menubar && priv->menubar->parent &&
	    !GTK_WIDGET_VISIBLE(priv->menubar->parent))
	{
		/* When F10 is pressed gtk+ automatically opens the menu if the
		 * menubar is visible, but when the menubar is not visible
		 * (think fullscreen or popup windows) we show it manually and
		 * hide again when the user activates or otherwise dismisses
		 * the menu.
		 */

		gulong *handler_id = g_new(gulong, 1);

		*handler_id = g_signal_connect_data (priv->menubar, "deactivate",
						     G_CALLBACK(menubar_deactivate_cb),
						     handler_id, (GClosureNotify)g_free, 0);
		gtk_widget_show (GTK_WIDGET(priv->menubar->parent));
		gtk_menu_shell_select_first (GTK_MENU_SHELL(priv->menubar),
				             FALSE);
		return TRUE;
	}
#endif
	return GTK_WIDGET_CLASS(parent_class)->key_press_event(widget, event);
}


static void
kz_window_destroy (GtkObject *object)
{
	KzWindow *kz = KZ_WINDOW(object);
	KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);

	if (GTK_OBJECT_CLASS (parent_class)->destroy)
		GTK_OBJECT_CLASS (parent_class)->destroy(object);

	window_list = g_list_remove(window_list, kz);

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

	g_free(priv->temp_msg);
        priv->temp_msg = NULL;

	if (priv->event)
		kz_embed_event_free((KzEmbedEvent *) priv->event);
	priv->event = NULL;

	if (kz->feed_info)
		g_object_unref(kz->feed_info);
	kz->feed_info = NULL;

	if (priv->find_tips[0])
		g_object_unref(priv->find_tips[0]);
	priv->find_tips[0] = NULL;
	if (priv->find_tips[1])
		g_object_unref(priv->find_tips[1]);
	priv->find_tips[1] = NULL;

	if (kz->menu_merge)
		g_object_unref(kz->menu_merge);
	kz->menu_merge = NULL;

	if (kz->actions)
		g_object_unref(kz->actions);
	kz->actions = NULL;

	if (kz->popup_actions)
		g_object_unref(kz->popup_actions);
	kz->popup_actions = NULL;

	if (kz->tabpop_actions)
		g_object_unref(kz->tabpop_actions);
	kz->tabpop_actions = NULL;

	if (kz->kzfav)
		g_object_unref(kz->kzfav);
	kz->kzfav = NULL;

	if (kz->popup)
		g_object_unref(kz->popup);
	kz->popup = NULL;

	if (kz->popup_tablist)
		gtk_widget_destroy(kz->popup_tablist);
	kz->popup_tablist = NULL;

	if (kz->tabs)
	{
		kz_bookmark_remove(kz_bookmarks->current_session, kz->tabs);
		g_object_unref(kz->tabs);
		kz->tabs = NULL;
		
		if (g_list_length(window_list) >0)
			kz_window_store_session(kz);
	}
	if (kz->closed_tabs)
		g_object_unref(kz->closed_tabs);
	kz->closed_tabs = NULL;
	if (kz->history_search)
		g_object_unref(kz->history_search);
	kz->history_search = NULL;

	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->bookmark_bars,
		 G_CALLBACK(cb_bookmark_bars_insert_child), kz);
	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->bookmark_bars,
		 G_CALLBACK(cb_bookmark_bars_remove_child), kz);
	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->bookmark_bars,
		 G_CALLBACK(cb_bookmark_bars_reordered), kz);

	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->smarts,
		 G_CALLBACK(cb_smartbookmark_insert_child), kz);
	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->smarts,
		 G_CALLBACK(cb_smartbookmark_remove_child), kz);
	g_signal_handlers_disconnect_by_func
		(kz_bookmarks->smarts,
		 G_CALLBACK(cb_smartbookmark_reordered), kz);

	g_signal_handlers_disconnect_by_func
		(kz_global_profile,
		 G_CALLBACK(cb_profile_global_changed), kz);
	g_signal_handlers_disconnect_by_func
		(kz_global_profile,
		 G_CALLBACK(cb_profile_gesture_changed), kz);
	g_signal_handlers_disconnect_by_func
		(kz_proxy,
		 G_CALLBACK(cb_profile_proxy_changed), kz);

	if (!kz_window_get_window_list())
	{
		static gboolean main_quited = FALSE;
		if (!main_quited)
		{
			gtk_main_quit();
			main_quited = TRUE;
		}
	}
}


void
kz_window_update_gesture_items (KzWindow *kz)
{
	GtkAction *action;
	KzGestureItems *items;
	GList *list, *node;
	KzWindowPrivate *priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	priv = KZ_WINDOW_GET_PRIVATE (kz);

	items = kz_gesture_items_new();

	list = kz_profile_enum_key(kz_global_profile, "Gesture", FALSE);

	for (node = list; node; node = g_list_next(node))
	{
		const gchar *action_name = node->data;
		gchar *gesture;

		action = gtk_action_group_get_action(kz->actions,
						     action_name);
		if (!action) continue;

		gesture = KZ_CONF_GET_STR("Gesture", action_name);
		if (!gesture) continue;
		if (!*gesture)
		{
			g_free(gesture);
			continue;
		}

		kz_gesture_items_set_action(items, action,
					    0, gesture);

		g_free(gesture);
	}

	kz_gesture_set_items(priv->gesture, items);
	kz_gesture_items_unref(items);

	g_list_free(list);
}


static void
cb_window_map (KzWindow *kz, GtkAction *action)
{
	gtk_action_activate(action);
	g_signal_handlers_disconnect_by_func
		(kz, G_CALLBACK(cb_window_map), action);
}


static void
kz_window_store_session (KzWindow *kz)
{
	gboolean save_session = FALSE;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	KZ_CONF_GET("Session", "save", save_session, BOOL);
	if (save_session)
		kz_bookmark_file_save(KZ_BOOKMARK_FILE(kz_bookmarks->current_session));
}


static void
kz_window_restore_session (KzWindow *kz)
{
	GtkAction *action;
	gboolean restore_session = FALSE;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	KZ_CONF_GET("Session", "restore",
		    restore_session, BOOL);
	if (restore_session && g_list_length(window_list) == 1)
	{
		action = gtk_action_group_get_action(kz->actions,
						     "RestoreSession");
		if (GTK_WIDGET_MAPPED(kz))
			gtk_action_activate(action);
		else
			g_signal_connect(kz, "map",
					 G_CALLBACK(cb_window_map), action);
	}
}


void
kz_window_store_state (KzWindow *kz)
{
	GtkAction *action;
	gint i, client_x, client_y, width, height;
	const gchar *label;
	gboolean active;
	KzWindowPrivate *priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	priv = KZ_WINDOW_GET_PRIVATE (kz);

	kz_profile_set_save_each_time(kz_global_profile, FALSE);
	/*
	 * window size
	 */

	gdk_window_get_geometry (GTK_WIDGET(kz)->window,
				 &client_x, &client_y,
				 &width, &height, NULL);
	KZ_CONF_SET("MainWindow", "width",  width,  INT);
	KZ_CONF_SET("MainWindow", "height", height, INT);

	/*
	 * sidebar
	 */
	for (i = 0; i < G_N_ELEMENTS(positems); i++)
	{
		action = gtk_action_group_get_action(kz->actions,
						     positems[i].sidebar_act);
		active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
		if (active)
		{
			KZ_CONF_SET_STR("MainWindow", "sidebar_pos",
					positems[i].pos);
			/* width */
			/*
			 * On startup time, allocation.width and allocation.height are
			 * not set correctly(maybe both 1), so we should use 
			 * sidebar or notebook width according to sidebar position.
			 */
			if (priv->sidebar_was_shown)
			{
				if (!strcmp(positems[i].pos, "top"))
					width = kz->sidebar->allocation.height;
				else if (!strcmp(positems[i].pos, "bottom"))
					width = kz->notebook->allocation.height;
				else if (!strcmp(positems[i].pos, "left"))
					width = kz->sidebar->allocation.width;
				else if (!strcmp(positems[i].pos, "right"))
					width = kz->notebook->allocation.width;
				KZ_CONF_SET("MainWindow", "sidebar_width", width, INT);
			}
			break;
		}
	}

	/* content */
	label = kz_sidebar_get_current(KZ_SIDEBAR(kz->sidebar));
	if (label && *label)
		KZ_CONF_SET_STR("MainWindow", "sidebar", label);

	/* visible */
	action = gtk_action_group_get_action(kz->actions,
					     "ShowHideSidebar");
	active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
	KZ_CONF_SET("MainWindow", "show_sidebar", active, BOOL);

	/* bookmarkbar */
	action = gtk_action_group_get_action(kz->actions,
					     "ShowHideBookmarkbars");
	active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
	KZ_CONF_SET("MainWindow", "show_bookmarkbars", active, BOOL);


	/*
	 * tab position
	 */

	for (i = 0; i < G_N_ELEMENTS(positems); i++)
	{
		action = gtk_action_group_get_action(kz->actions,
						     positems[i].tab_act);
		active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action));
		if (active)
		{
			KZ_CONF_SET_STR("MainWindow", "tab_pos",
					positems[i].pos);
			break;
		}
	}

	kz_profile_set_save_each_time(kz_global_profile, TRUE);
	kz_profile_save(kz_global_profile);

	/*
	 *  Session
	 */

	/* kz_window_store_session(kz); */
}


void
kz_window_restore_state (KzWindow *kz)
{
	GtkAction *action = NULL;
	gint width = 640, height = 450, sidebar_width = 150;
	gboolean active = FALSE, bookmarkbar = TRUE;
	gchar *tab_pos, *label, *sidebar_pos;
	gint i;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	/*
	 * window size
	 */

	KZ_CONF_GET("MainWindow", "width",  width,  INT);
	KZ_CONF_GET("MainWindow", "height", height, INT);

	gtk_window_set_default_size(GTK_WINDOW(kz), width, height);

	/*
	 * sidebar
	 */
	/* content */
	label = KZ_CONF_GET_STR("MainWindow", "sidebar");
	if (label && *label)
		kz_sidebar_set_current(KZ_SIDEBAR(kz->sidebar), label);
	g_free(label);

	/* visible */
	KZ_CONF_GET("MainWindow", "show_sidebar", active, BOOL);
	action = gtk_action_group_get_action(kz->actions,
					     "ShowHideSidebar");
	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), active);
	if (active)
	{
		sidebar_pos = KZ_CONF_GET_STR("MainWindow", "sidebar_pos");
		for (i = 0; sidebar_pos && i < G_N_ELEMENTS(positems); i++)
		{
			if (strcasecmp(sidebar_pos, positems[i].pos)) continue;

			/* width */
			KZ_CONF_GET("MainWindow", "sidebar_width", sidebar_width, INT);
			action = gtk_action_group_get_action(kz->actions,
							     positems[i].sidebar_act);
			gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
						     TRUE);
			/*
			 * On startup time, allocation.width and allocation.height are not set
			 * correctly(maybe both 1), so we should use sidebar or notebook width
			 * according to sidebar position.
			 */
			gtk_paned_set_position(GTK_PANED(kz->pane), sidebar_width);
		}
		g_free(sidebar_pos);
	}
	KZ_CONF_GET("MainWindow", "show_bookmarkbars", bookmarkbar, BOOL);
	action = gtk_action_group_get_action(kz->actions,
					     "ShowHideBookmarkbars");
	gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), bookmarkbar);
	
	/*
	 * tab position
	 */

	tab_pos = KZ_CONF_GET_STR("MainWindow", "tab_pos");
	for (i = 0; tab_pos && i < G_N_ELEMENTS(positems); i++)
	{
		if (strcasecmp(tab_pos, positems[i].pos)) continue;

		action = gtk_action_group_get_action(kz->actions,
						     positems[i].tab_act);
		gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action),
					     TRUE);
	}
	g_free(tab_pos);
}


static void
kz_window_set_cur_embed_callbacks(KzWindow *kz, KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(KZ_IS_EMBED(kzembed));

	g_signal_connect(kz,
			 "motion_notify_event",
			 G_CALLBACK(cb_window_motion_notify_event), kzembed);
	g_signal_connect(kz,
			 "button_release_event",
			 G_CALLBACK(cb_window_button_release_event), kzembed);
	g_signal_connect(kz,
			 "scroll_event",
			 G_CALLBACK(cb_window_scroll_event), kzembed);

	/*
	 * key event signal
	 */
	g_signal_connect(kzembed,
			 "kz-dom-key-down",
			 G_CALLBACK(cb_embed_dom_key_down), kz);
	g_signal_connect(kzembed,
			 "kz-dom-key-up",
			 G_CALLBACK(cb_embed_dom_key_up), kz);

	/*
	 * mouse event signal
	 */
	g_signal_connect(kzembed,
			 "kz-dom-mouse-down",
			 G_CALLBACK(cb_embed_dom_mouse_down), kz);
	g_signal_connect(kzembed,
			 "kz-dom-mouse-up",
			 G_CALLBACK(cb_embed_dom_mouse_up), kz);
	g_signal_connect(kzembed,
			 "kz-dom-mouse-over",
			 G_CALLBACK(cb_embed_dom_mouse_over), kz);
	g_signal_connect(kzembed, "kz-dom-mouse-click",
			 G_CALLBACK(cb_embed_dom_mouse_click), kz);
	g_signal_connect(kzembed, "kz-dom-mouse-dbl-click",
			 G_CALLBACK(cb_embed_dom_mouse_dbl_click), kz);

	/*
	 * other callbacks
	 */
	g_signal_connect(kzembed, "kz-title",
			 G_CALLBACK(cb_embed_title_changed), kz);
	g_signal_connect(kzembed, "kz-location",
			 G_CALLBACK(cb_embed_location_changed), kz);
	g_signal_connect(kzembed, "kz-link-message",
			 G_CALLBACK(cb_embed_link_message), kz);
}


static void
kz_window_unset_cur_embed_callbacks(KzWindow *kz, KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(KZ_IS_EMBED(kzembed));

	g_signal_handlers_disconnect_by_func
		(kz,
		 G_CALLBACK(cb_window_motion_notify_event), kzembed);
	g_signal_handlers_disconnect_by_func
		(kz,
		 G_CALLBACK(cb_window_button_release_event), kzembed);
	g_signal_handlers_disconnect_by_func
		(kz,
		 G_CALLBACK(cb_window_scroll_event), kzembed);

	/*
	 * key event signal
	 */
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_key_down), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_key_up), kz);

	/*
	 * mouse event signal
	 */
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_mouse_down), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_mouse_up), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_mouse_over), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_mouse_click), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_dom_mouse_dbl_click), kz);

	/*
	 * other callbacks
	 */
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_title_changed), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_location_changed), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_link_message), kz);
}


static void
kz_window_set_embed_callbacks(KzWindow *kz, KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(KZ_IS_EMBED(kzembed));

	/*
	 * other callbacks
	 */
	g_signal_connect(kzembed, "kz-net-start",
			 G_CALLBACK(cb_embed_load_started), kz);
	g_signal_connect(kzembed, "kz-net-stop",
			 G_CALLBACK(cb_embed_load_finished), kz);
	g_signal_connect(kzembed, "kz-new-window",
			 G_CALLBACK(cb_embed_new_window), kz);
	g_signal_connect(kzembed, "destroy",
			 G_CALLBACK(cb_embed_close_tab), kz);
}


static void
kz_window_unset_embed_callbacks(KzWindow *kz, KzEmbed *kzembed)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_return_if_fail(KZ_IS_EMBED(kzembed));

	/*
	 * other callbacks
	 */
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_load_started), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_load_finished), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_new_window), kz);
	g_signal_handlers_disconnect_by_func
		(kzembed,
		 G_CALLBACK(cb_embed_close_tab), kz);
}


void
kz_window_load_url (KzWindow *kz, const gchar *url)
{
	GtkWidget *widget = KZ_WINDOW_CURRENT_PAGE(kz);
	KzTabLabel *kztab = NULL;

	g_return_if_fail(KZ_IS_WINDOW(kz));

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

	if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook)) < 1 ||
	    !KZ_IS_EMBED(widget))
	{
		kz_window_open_new_tab(kz, url);
	}
	else
	{
		kz_embed_load_url(KZ_EMBED(widget), url);
	}
}


const gchar *
kz_window_get_title (KzWindow *kz)
{
	GtkWidget *widget = KZ_WINDOW_CURRENT_PAGE(kz);

	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);

	if (KZ_IS_EMBED(widget))
		return kz_embed_get_title(KZ_EMBED(widget));
	else
		return NULL;
}


const gchar *
kz_window_get_uri (KzWindow *kz)
{
	GtkWidget *widget = KZ_WINDOW_CURRENT_PAGE(kz);

	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);

	if (KZ_IS_EMBED(widget))
		return kz_embed_get_location(KZ_EMBED(widget));
	else
		return NULL;
}


GtkWidget *
kz_window_get_tab_label (KzWindow *kz, GtkWidget *widget)
{
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	g_return_val_if_fail(GTK_IS_WIDGET(widget), NULL);

	return gtk_notebook_get_tab_label(GTK_NOTEBOOK(kz->notebook),
					  widget);
}


GNode *
kz_window_get_tree (KzWindow *kz)
{
	KzWindowPrivate *priv;
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	priv = KZ_WINDOW_GET_PRIVATE (kz);
	return priv->tab_tree;
}


const KzEmbedEventMouse *
kz_window_get_mouse_event_info (KzWindow *kz)
{
	KzWindowPrivate *priv;
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	priv = KZ_WINDOW_GET_PRIVATE (kz);
	return priv->event;
}



/*****************************************************************************
 *                                                                           *
 *                                Callbacks                                  *
 *                                                                           *
 *****************************************************************************/
static void
cb_profile_global_changed (KzProfile *profile,
			   const gchar *section, const gchar *key,
			   const gchar *old_value,
			   KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	switch (key[0])
	{
	case 'u':/* ui_level */
		if (strcmp(key, "ui_level") == 0)
		{
			kz_window_sync_ui_level(kz);
		}
		if (strcmp(key, "use_proxy") == 0)
		{
			kz_window_sync_proxy(kz);
		}
		break;
	case 'p':
		if (strcmp(key, "proxy_name") == 0)
		{
			kz_window_sync_proxy(kz);
		}
		break;
	default:
		break;
	}
}

static void
cb_profile_proxy_changed (KzProfile *profile,
			  const gchar *section, const gchar *key,
			  const gchar *old_value,
			  KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_window_sync_proxy(kz);
}

static void
cb_profile_gesture_changed (KzProfile *profile,
			   const gchar *section, const gchar *key,
			   const gchar *old_value,
			   KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_window_update_gesture_items(kz);
}


static void
cb_bookmark_bars_insert_child (KzBookmark *bookmark,
			       KzBookmark *child, KzBookmark *sibling,
			       KzWindow *kz)
{
	GtkWidget *bar;
	GList *children;
	gint pos;

	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_BOOKMARK(sibling));
	g_return_if_fail(KZ_IS_WINDOW(kz));

	bar = kz_bookmark_bar_new(kz, child);
	gtk_box_pack_start(GTK_BOX(kz->bookmark_bars_area), 
			   bar, FALSE, FALSE, 0);    
	gtk_widget_show(bar);

	/* reorder */
	children = kz_bookmark_get_children(bookmark);
	pos = g_list_index(children, child);
	gtk_box_reorder_child(GTK_BOX(kz->bookmark_bars_area),
			      bar, pos);
	g_list_free(children);
}


static void
cb_bookmark_bars_remove_child (KzBookmark *bookmark, KzBookmark *child,
			       KzWindow *kz)
{
	GList *node;

	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_WINDOW(kz));

	for (node = kz->bookmark_bars;
	     node;
	     node = g_list_next(node))
	{
		if (KZ_BOOKMARK(KZ_BOOKMARK_BAR(node->data)->folder) == child)
		{
			gtk_widget_destroy(node->data);
			break;
		}
	}
}


static GtkWidget *
find_bookmark_bar (KzWindow *kz, KzBookmark *folder)
{
	GList *node;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);
	g_return_val_if_fail(KZ_IS_BOOKMARK(folder), NULL);

	for (node = kz->bookmark_bars;
	     node;
	     node = g_list_next(node))
	{
		KzBookmarkBar *bar;

		if (!KZ_IS_BOOKMARK_BAR(node->data))
		{
			g_warning("KzWindow: find_bookmark_bar: "
				  "Invalid bookmark bar!: %p", node->data);
			continue;
		}

		bar = KZ_BOOKMARK_BAR(node->data);
		if (folder == bar->folder)
			return GTK_WIDGET(bar);
	}

	return NULL;
}


static void
cb_bookmark_bars_reordered (KzBookmark *bookmark, KzWindow *kz)
{
	GList *children, *node;
	gint pos = 0;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	children = kz_bookmark_get_children(bookmark);
	for (node = children; node; node = g_list_next(node))
	{
		GtkWidget *bar;

		bar = find_bookmark_bar(kz, node->data);
		if (!bar)
		{
			g_warning("KzWindow: reorder bookmark bars: "
				  "bookmark bar for %p is not exist!",
				  node->data);
			continue; /* create? */
		}

		gtk_box_reorder_child(GTK_BOX(kz->bookmark_bars_area),
				      bar, pos);
		pos++;
	}
	g_list_free(children);
}


static void
cb_smartbookmark_insert_child (KzBookmark *bookmark,
			       KzBookmark *child, KzBookmark *sibling,
			       KzWindow *kz)
{
	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_actions_insert_smartbookmark(kz, bookmark, child, sibling);
}


static void
cb_smartbookmark_remove_child (KzBookmark *bookmark, KzBookmark *child,
			       KzWindow *kz)
{
	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_actions_remove_smartbookmark(kz, bookmark, child);	
}


static void
cb_smartbookmark_reordered (KzBookmark *bookmark, KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_actions_update_smartbookmarks(kz, bookmark);	
}



static void
cb_menu_merge_add_widget (GtkUIManager *merge, GtkWidget *widget, GtkBox *box)
{
	gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
}


static gboolean
idle_focus_location_entry(gpointer data)
{
	KzWindow *kz = KZ_WINDOW(data);
	GtkAction *action;
	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);
	action = gtk_action_group_get_action(kz->actions,
					     "FocusLocationEntry");
	if(action)
		gtk_action_activate(action);
	return FALSE;
}


static void
cb_clipboard_get_text(GtkClipboard *clipboard, const gchar *text, gpointer data)
{
	char **received_text = data;
	*received_text = g_strdup(text);
}


static void
cb_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page,
			 guint page_num, KzWindow *kz)
{
	KzEmbed *kzembed = KZ_EMBED(KZ_WINDOW_NTH_PAGE(kz, page_num));
	KzEmbed *cur = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz));
	gchar *title;
	const gchar *location;
	GtkAction *action;
	KzTabLabel *tab;
	gboolean focus;
	KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);

	g_return_if_fail(KZ_IS_EMBED(cur));
	g_return_if_fail(KZ_IS_EMBED(kzembed));

	kz_window_unset_cur_embed_callbacks(kz, cur);
	kz_window_set_cur_embed_callbacks(kz, kzembed);

	title = kz_embed_ensure_title(kzembed);
	location = kz_embed_get_location(kzembed);
	if (title)
	{
		gtk_window_set_title(GTK_WINDOW(kz), title);
		g_free(title);
	}
	else
	{
		gtk_window_set_title(GTK_WINDOW(kz), _("Kazehakase"));
	}

	action = gtk_action_group_get_action(kz->actions, "LocationEntry");
	if (KZ_IS_ENTRY_ACTION(action))
	{
		GtkClipboard *clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
		GObject *owner = gtk_clipboard_get_owner(clipboard);
		gchar *text = NULL;
		if (GTK_IS_ENTRY(owner)) 
                {
			gtk_clipboard_request_text(clipboard,
						   cb_clipboard_get_text,
						   (gpointer)&text);
		}
		if (location)
			kz_entry_action_set_text(KZ_ENTRY_ACTION(action),
						  location);
		else
			kz_entry_action_set_text(KZ_ENTRY_ACTION(action), "");

		if (text) 
                {
			gtk_clipboard_set_text(clipboard, text, -1);
			g_free(text);
		}
	}
	
	action = gtk_action_group_get_action(kz->actions, "Zoom");
	if (KZ_IS_ZOOM_ACTION(action))
	{
		gint ratio;
		ratio = kz_embed_zoom_get(kzembed);
		kz_zoom_action_set_ratio(KZ_ZOOM_ACTION(action), ratio);
	}

	tab = KZ_TAB_LABEL(kz_window_get_tab_label(kz, GTK_WIDGET(kzembed)));
	g_return_if_fail(tab);

	if(kz_tab_label_get_state(tab) == KZ_TAB_LABEL_STATE_LOADED)
	{
		kz_tab_label_set_state(tab, KZ_TAB_LABEL_STATE_NORMAL);
		if((!location || *location == 0 ) &&
		   KZ_CONF_GET("Tab","focus_loc_ent_new", focus, BOOL) &&
		   focus)
		{
			g_idle_add(idle_focus_location_entry, kz);
		}
	}

	kz_actions_set_sensitive(kz, kzembed);
	kz_actions_set_tab_sensitive(kz, kzembed);

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

	/* set current_position in history */
	if (!kz->is_closing_all && !is_restoring_session)
	{
		kz_bookmark_set_current(kz->tabs, page_num);
		kz_window_store_session(kz);
	}
}

static void
cb_notebook_switch_page_after (GtkNotebook *notebook, GtkNotebookPage *page,
			       guint page_num, KzWindow *kz)
{
	kz_feed_info_change_state(KZ_FEED_INFO(kz->feed_info));
}


static void
search_found(GtkWidget* widget, KzWindow *kz)
{
	static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
	static const GdkColor black = { 0, 0x0000, 0x0000, 0x0000 };

	g_return_if_fail(KZ_IS_WINDOW(kz));

	if(kz->statusbar) {
		KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);
		gtk_statusbar_pop(GTK_STATUSBAR(kz->statusbar),
				  priv->status_search);
	}

	gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &white);
	gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &black);
}

static void
search_not_found(GtkWidget* widget, KzWindow *kz)
{
	static const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
	static const GdkColor red = { 0, 0xffff, 0x6666, 0x6666 };
	const gchar *search_word;
	gchar *message;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	search_word = gtk_entry_get_text(GTK_ENTRY(widget));
	message = g_strdup_printf(_("%s not found"), search_word);
	if(kz->statusbar) 
	{
		KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);
		gtk_statusbar_pop(GTK_STATUSBAR(kz->statusbar),
				  priv->status_search);
		gtk_statusbar_push(GTK_STATUSBAR(kz->statusbar),
				   priv->status_search,
				   message);
	}
	g_free(message);

	gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &red);
	gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &white);
}

static void
cb_find_keyword (GtkWidget *widget, KzWindow *kz)
{
	KzEmbed *embed = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz));
	GtkToggleButton *toggle = GTK_TOGGLE_BUTTON(kz->find_direction);
	const gchar *text = gtk_entry_get_text(GTK_ENTRY(widget));
	gboolean back = gtk_toggle_button_get_active(toggle);
	KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);

	if (!embed) return;

	if (!text || !(*text))
	{
		search_found(widget, kz);
		return;
	}

	priv->did_find = kz_embed_find(embed, text, back);
	if(priv->did_find)
		search_found(widget, kz);
	else
		search_not_found(widget, kz);
}

static gboolean
cb_find_key_release (GtkWidget *widget, GdkEventKey *event, KzWindow *kz)
{
	KzEmbed *embed ;
	GtkToggleButton *toggle;
	const gchar *text;
	KzWindowPrivate *priv;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	embed = KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz));
	if (!embed) return FALSE;

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	toggle = GTK_TOGGLE_BUTTON(kz->find_direction);
	text = gtk_entry_get_text(GTK_ENTRY(widget));
	if (!text || !(*text))
	{
		search_found(widget,kz);
		return FALSE;
	}

	if (event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter)
	{
		if (event->state & GDK_SHIFT_MASK)
		{
			gboolean back;
			back = !gtk_toggle_button_get_active(toggle);
			priv->did_find
				= kz_embed_find(embed, text, back);
		}
	}
	else
	{
		if (!(event->state & GDK_SHIFT_MASK))
		{
			gboolean back;
			back = gtk_toggle_button_get_active(toggle);
			priv->did_find
				= kz_embed_incremental_search(embed, text, back);
		}
	}

	if(priv->did_find)
		search_found(widget, kz);
	else
		search_not_found(widget, kz);
	return FALSE;
}

#if 0
static void
cb_find_grab_focus (GtkWidget *widget, KzWindow *kz)
{
	KzEmbed *embed ;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	g_warning("grab");
}
#endif

static void
cb_find_direction_toggle (GtkToggleButton *button, KzWindow *kz)
{
	GtkArrow *arrow;
	gboolean active;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	arrow = GTK_ARROW(GTK_BIN(button)->child);
	g_return_if_fail(GTK_IS_ARROW(arrow));

	active = gtk_toggle_button_get_active(button);
	if (active)
		gtk_arrow_set(arrow, GTK_ARROW_UP, GTK_SHADOW_NONE);
	else
		gtk_arrow_set(arrow, GTK_ARROW_DOWN, GTK_SHADOW_NONE);
}


static void
cb_gesture_stack_motion (KzGesture *gesture, KzGestureMotion motion,
			 KzWindow *kz)
{
	const gchar *label;
	gchar buf1[256], buf2[256];
	KzWindowPrivate *priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_gesture_create_gesture_string(gesture, buf1, G_N_ELEMENTS(buf1));
	label = kz_gesture_get_matched_label(gesture);
	if (label)
		g_snprintf(buf2, G_N_ELEMENTS(buf2),
			   _("Gesture: %s(Action: %s)"), buf1, label);
	else
		g_snprintf(buf2, G_N_ELEMENTS(buf2),
			   _("Gesture: %s"), buf1);

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	gtk_statusbar_pop(GTK_STATUSBAR(kz->statusbar), priv->status_gesture);
	gtk_statusbar_push(GTK_STATUSBAR(kz->statusbar),
			   priv->status_gesture, buf2);
}


static gboolean
handle_scroll_event (KzWindow *kz, GdkScrollDirection direction)
{
	GtkAction *action;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	switch (direction) {
	case GDK_SCROLL_UP:
	case GDK_SCROLL_LEFT:
		action = gtk_action_group_get_action(kz->actions, "PrevTab");
		gtk_action_activate(action);
		return TRUE;
		break;
	case GDK_SCROLL_DOWN:
	case GDK_SCROLL_RIGHT:
		action = gtk_action_group_get_action(kz->actions, "NextTab");
		gtk_action_activate(action);
		return TRUE;
		break;
	default:
		g_warning ("Invalid scroll direction!");
		break;
	}

	return FALSE;
}

static gboolean
cb_notebook_scroll_event (GtkWidget *widget, GdkEventScroll *event,
			  KzWindow *kz)
{
	return handle_scroll_event (kz, event->direction);
}


static void
cb_sidebar_map (GtkWidget *widget, GtkToggleAction *action)
{
	KzWindowPrivate *priv;
	g_return_if_fail(GTK_IS_TOGGLE_ACTION(action));

	priv = KZ_WINDOW_GET_PRIVATE (KZ_SIDEBAR(widget)->kz);
	priv->sidebar_was_shown = TRUE;

	gtk_toggle_action_set_active(action, TRUE);
}


static void
cb_sidebar_unmap (GtkWidget *widget, GtkToggleAction *action)
{
	g_return_if_fail(GTK_IS_TOGGLE_ACTION(action));

	gtk_toggle_action_set_active(action, FALSE);
}


/*****************************************************************************
 *                                                                           *
 *                        Callbacks for Embed                                *
 *                                                                           *
 *****************************************************************************/
static void
cb_embed_title_changed (KzEmbed *embed, KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	if (embed == KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)))
	{
		const gchar *title
			= kz_embed_get_title(KZ_EMBED(embed));
		gtk_window_set_title(GTK_WINDOW(kz), (gchar *) title);
	}
}


static void
cb_embed_location_changed (KzEmbed *embed, KzWindow *kz)
{
	const char *newLocation;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	newLocation = kz_embed_get_location(KZ_EMBED(embed));
	if (newLocation)
	{
		GtkAction *action
			= gtk_action_group_get_action(kz->actions,
						      "LocationEntry");
		if (embed == KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz))
		    && KZ_IS_ENTRY_ACTION(action))
		{
			kz_entry_action_set_text(KZ_ENTRY_ACTION(action),
						 newLocation);
		}
	}
	else
	{
		/* g_print("failed to get location!\n"); */
	}

	kz_actions_set_sensitive(kz, embed);
}


static void
cb_embed_link_message (KzEmbed *embed, KzWindow *kz)
{
	gchar *newMessage;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	newMessage = kz_embed_get_link_message(KZ_EMBED(embed));
	if(kz->statusbar) 
	{
		KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);
		gtk_statusbar_pop(GTK_STATUSBAR(kz->statusbar),
				  priv->status_link_message);
		gtk_statusbar_push(GTK_STATUSBAR(kz->statusbar),
				   priv->status_link_message, newMessage);
	}

	g_free(newMessage);
}


static void
cb_embed_load_started (KzEmbed *embed, KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_actions_set_sensitive(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));

	kz_feed_info_change_state(KZ_FEED_INFO(kz->feed_info));
}


static void
cb_embed_load_finished (KzEmbed *embed, KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	kz_actions_set_sensitive(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));

	kz_feed_info_change_state(KZ_FEED_INFO(kz->feed_info));
}


static void
cb_embed_new_window (KzEmbed *embed, KzEmbed **newEmbed,
		     KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	*newEmbed = KZ_EMBED(kz_window_open_new_tab(kz, NULL));
	gtk_widget_show (GTK_WIDGET(*newEmbed));
}


static void
cb_embed_close_tab (GtkObject *obj, KzWindow *kz)
{
	KzEmbed *kzembed;
	GNode *node, *child;
	KzWindowPrivate *priv;

	g_return_if_fail(KZ_IS_WINDOW(kz));
	priv = KZ_WINDOW_GET_PRIVATE (kz);
	g_return_if_fail(priv->tab_tree);

	kzembed = KZ_EMBED(obj);

	kz_window_unset_cur_embed_callbacks(kz, kzembed);
	kz_window_unset_embed_callbacks(kz, kzembed);

	priv->view_hist = g_list_remove(priv->view_hist, kzembed);
	priv->open_hist = g_list_remove(priv->open_hist, kzembed);

	gtk_statusbar_pop(GTK_STATUSBAR(kz->statusbar),
			  priv->status_link_message);

	node = g_node_find(priv->tab_tree,
			   G_IN_ORDER, G_TRAVERSE_ALL, kzembed);
	/* probably destroy handler is already called */
	if (!node) return;

	/* 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;
	}
	
	/* FIXME! should move to specifing tab like kz_window_close_tab() */
	g_node_destroy(node);
	gtk_widget_destroy(GTK_WIDGET(kzembed));
	g_signal_emit(kz, kz_window_signals[REMOVE_TAB_SIGNAL],
		      0, kzembed);
}


static gint
cb_embed_dom_key_down (KzEmbed *embed, KzEmbedEventKey *event, KzWindow *kz)
{

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	kz_actions_set_sensitive(kz, embed);

	return FALSE;
}


static gint
cb_embed_dom_key_up (KzEmbed *embed, KzEmbedEventKey *event, KzWindow *kz)
{
	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	kz_actions_set_sensitive(kz, embed);

	return FALSE;
}


#warning FIXME
static gint
cb_embed_dom_mouse_click (KzEmbed *embed, KzEmbedEventMouse *event,
			  KzWindow *kz)
{
	gint button;
	guint32 time;
     	glong type;
	KzTabLabel *kztab = NULL;
	GtkWidget *widget = KZ_WINDOW_CURRENT_PAGE(kz);

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	time = GDK_CURRENT_TIME;
	
	type = event->cinfo.context;
	button = event->button;

	switch (button) {
	case 0:
		if ((type & KZ_CONTEXT_LINK) && event->cinfo.link)
		{
			const gchar *location;

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

			/* with Shift key or in locked tab, open new tab */
			if ((event->modifier & KZ_SHIFT_KEY) ||
			    (kztab && kz_tab_label_get_lock(kztab)))
			{
				kz_window_open_new_tab_with_parent
					(kz, event->cinfo.link, GTK_WIDGET(embed));
				return TRUE;
			}
			location = kz_embed_get_location(KZ_EMBED(embed));
			if (!strncmp(location, "history-search:", 15))
			{
				kz_embed_load_url(KZ_EMBED(embed), 
						  event->cinfo.link);
			}
		}
		break;
	case 1:
		if ((type & KZ_CONTEXT_LINK) && event->cinfo.link)
		{
			gboolean focus_mid_click_link = FALSE;
			KZ_CONF_GET("Tab", "focus_mid_click_link", focus_mid_click_link, BOOL);

			widget = kz_window_open_new_tab_with_parent(kz, event->cinfo.link,
							   	    GTK_WIDGET(embed));

			if(((event->modifier & KZ_SHIFT_KEY) != 0) ^ focus_mid_click_link)
			{
				gtk_notebook_set_current_page(GTK_NOTEBOOK(kz->notebook),
							      gtk_notebook_page_num(GTK_NOTEBOOK(kz->notebook), widget));
			}
		}
		else if (!(type & KZ_CONTEXT_INPUT))
		{
			GtkAction *action;

			action = gtk_action_group_get_action(kz->actions,
							     "OpenSelection");

			/* what about uri list? */
			if (action)
				gtk_action_activate(action);
		}
		break;
	default:
		break;
	}

	return FALSE;
}


static gint
cb_embed_dom_mouse_dbl_click (KzEmbed *embed, KzEmbedEventMouse *event,
			      KzWindow *kz)
{
#if 0
     	KzEmbedEventMouse *info;
	gint button;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

     	type = kz_embed_get_mouse_event_info(KZ_MOZ_EMBED(embed), event, &info);
	button = info->button;
	kz_embed_event_free(info);
	switch (button) {
	 case 0:
		 gtk_notebook_prev_page(GTK_NOTEBOOK(kz->notebook));
		 break;
	 case 2:	
		 gtk_notebook_next_page(GTK_NOTEBOOK(kz->notebook));
		 break;
	}
#endif

	return FALSE;
}


static gint
cb_embed_dom_mouse_down (KzEmbed *kzembed, KzEmbedEventMouse *event,
			 KzWindow *kz)
{
	gint button;
     	glong type;
	KzWindowPrivate *priv;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	priv = KZ_WINDOW_GET_PRIVATE (kz);
	type = event->cinfo.context;
	button = event->button;
	if (priv->event)
		kz_embed_event_free((KzEmbedEvent *) priv->event);
	priv->event = (KzEmbedEventMouse *) kz_embed_event_copy((KzEmbedEvent *) event);

	switch (button)
	{
	case 0:
		break;
	case 1:
	{
		gchar *modifier_name = NULL;
		gchar *tmp_name = NULL;
		tmp_name = KZ_CONF_GET_STR("Global", "autoscroll_modifier");

		if(tmp_name)
		{
			modifier_name = g_ascii_strdown(tmp_name, -1);
			g_free(tmp_name);
		}

		if(modifier_name) 
		{
			gint i;
			gint modifier_id = 2;
			for (i = 0; i < G_N_ELEMENTS(modifier_map); i++)
			{
				if(!strcmp(modifier_name, modifier_map[i].name))
				{
					modifier_id = modifier_map[i].id;
					break;
				}
			}

			if((event->modifier & modifier_id) && 
			   (!((KzEmbedEvent*)event)->link))
			{
				GtkAction *action;
				action = gtk_action_group_get_action(kz->actions,
								     "AutoScrollMode");
				if (action)
					gtk_action_activate(action);
			}
			g_free(modifier_name);
		}
		else
		{
			if (!((KzEmbedEvent*)event)->link)
			{
				GtkAction *action;
				action = gtk_action_group_get_action(kz->actions,
								     "AutoScrollMode");
				if (action)
					gtk_action_activate(action);
			}
			break;
		}
		break;
	}
	case 2:
	{
		static GdkCursor *cursor = NULL;
		gboolean use_gesture = TRUE;
		gint x, y, win_x, win_y, win_x_pos, win_y_pos;

		priv->is_button3_pressed = TRUE;
		priv->is_button3_scrolled = FALSE;

		priv->is_gesture = FALSE;
		KZ_CONF_GET("Gesture", "use_gesture", use_gesture, BOOL);
		if (!use_gesture)
		{
			gdk_pointer_grab (GTK_WIDGET(kz)->window,
					  FALSE,
					  GDK_BUTTON_RELEASE_MASK |
					  GDK_BUTTON_PRESS_MASK,
					  NULL,	cursor, gtk_get_current_event_time ());
			break;
		}
		/* start gesture */

		gtk_widget_get_pointer (GTK_WIDGET(kzembed), &x, &y);

		priv->start_x = x;
		priv->start_y = y;

		gdk_window_get_root_origin (GTK_WIDGET(kzembed)->window,
					    &win_x, &win_y);
		gdk_window_get_position (GTK_WIDGET(kzembed)->window,
					 &win_x_pos, &win_y_pos);

		gtk_widget_get_pointer (GTK_WIDGET(kzembed),
					&x, &y);
		kz_gesture_start(priv->gesture, 0, x, y);

		if (!cursor) cursor = gdk_cursor_new (GDK_HAND1);
		gdk_pointer_grab (GTK_WIDGET(kz)->window,
				  FALSE,
				  GDK_POINTER_MOTION_MASK |
				  GDK_BUTTON_RELEASE_MASK |
				  GDK_BUTTON_PRESS_MASK,
				  NULL, cursor, gtk_get_current_event_time ());
		gtk_statusbar_push(GTK_STATUSBAR(kz->statusbar),
				   priv->status_gesture, _("Gesture:"));
		break;
	}
	default:
		break;
	}

	kz_actions_set_selection_sensitive(kz, kzembed);

	return FALSE;
}


static gint
cb_embed_dom_mouse_up (KzEmbed *embed, KzEmbedEventMouse *event,
		       KzWindow *kz)
{
	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	kz_actions_set_selection_sensitive(kz, embed);

	return FALSE;
}

static gint
cb_embed_dom_mouse_over (KzEmbed *embed, KzEmbedEventMouse *event,
		         KzWindow *kz)
{
	static glong previous_event_context = KZ_CONTEXT_NONE;
	glong type;
	gboolean popup_thumbnail;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	KZ_CONF_GET("Global", "popup_thumbnail", popup_thumbnail, BOOL);
	if (!popup_thumbnail) return FALSE;

	type = event->cinfo.context;
	if ((type & KZ_CONTEXT_LINK) && event->cinfo.link)
	{
		GtkWidget *widget = KZ_WINDOW_CURRENT_PAGE(kz);
		GdkDisplay *display = gtk_widget_get_display (widget);
		gint x, y;

		display = gtk_widget_get_display (widget);
		gdk_display_get_pointer (display, NULL, &x, &y, NULL);

		kz_popup_preview_start(kz->popup, event->cinfo.link, event->cinfo.img, x, y);
	}
	else if(!(type & KZ_CONTEXT_LINK) && (previous_event_context & KZ_CONTEXT_LINK))
	{
		kz_popup_preview_reset(kz->popup);
	}

	previous_event_context = type;
	kz_actions_set_selection_sensitive(kz, embed);

	return FALSE;
}

static gboolean
cb_window_motion_notify_event (GtkWidget *widget,
			       GdkEventMotion *event,
			       KzEmbed *kzembed)
{
	KzWindow *kz;
	gint x, y;
	/* GdkModifierType state; */
	KzWindowPrivate *priv;

	g_return_val_if_fail(KZ_IS_EMBED(kzembed), FALSE);

	kz = KZ_WINDOW(widget);
	priv = KZ_WINDOW_GET_PRIVATE (kz);

	/*gdk_window_get_pointer (GTK_WIDGET(kzembed)->window, &x, &y, &state);*/
	gtk_widget_get_pointer (GTK_WIDGET(kzembed), &x, &y);

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

	if (kz_gesture_is_started(priv->gesture))
	{
		/* if(state == GDK_BUTTON3_MASK) */
		kz_gesture_update_position(priv->gesture, x, y);
		/*
		else
		{
			if (gdk_pointer_is_grabbed ())
			{
				gdk_pointer_ungrab (gtk_get_current_event_time ());
			}
			kz_gesture_perform(priv->gesture);
			gtk_statusbar_push(GTK_STATUSBAR(KZ_WINDOW(widget)->statusbar),
				   priv->status_gesture, "");
			priv->start_x = 0;
			priv->start_y = 0;
			priv->is_gesture = FALSE;

			if (priv->event)
				kz_embed_event_free((KzEmbedEvent *) priv->event);
			priv->event = NULL;
		}
		*/
	}

	kz_actions_set_selection_sensitive(kz, kzembed);

	return FALSE;
}


static gboolean
cb_window_button_release_event (GtkWidget *widget,
				GdkEventButton *event,
				KzEmbed *kzembed)
{
	KzWindow *kz;
	gboolean retval = FALSE;
	KzWindowPrivate *priv;

	g_return_val_if_fail(KZ_IS_EMBED(kzembed), FALSE);

	kz = KZ_WINDOW(widget);
	priv = KZ_WINDOW_GET_PRIVATE (kz);

        if (gdk_pointer_is_grabbed ())
        {
                gdk_pointer_ungrab (gtk_get_current_event_time ());
        }

	if (kz_gesture_is_started(priv->gesture))
	{
		if (event->button == 3 && !priv->is_button3_scrolled)
			kz_gesture_perform(priv->gesture);
		else
			kz_gesture_cancel(priv->gesture);
		gtk_statusbar_pop(GTK_STATUSBAR(KZ_WINDOW(widget)->statusbar),
				  priv->status_gesture);
	}

	if (!priv->is_gesture &&
	    !priv->is_button3_scrolled &&
	    event->button == 3)
	{
		kz_actions_popup_menu_modal (kz, event->button, event->time);
		retval = TRUE;
	}

	if (event->button == 3)
	{
		if (priv->is_button3_scrolled)
			retval = TRUE;
		priv->is_button3_pressed = FALSE;
		priv->is_button3_scrolled = FALSE;
	}
	
	priv->start_x = 0;
	priv->start_y = 0;
	priv->is_gesture = FALSE;

	if (priv->event)
		kz_embed_event_free((KzEmbedEvent *) priv->event);
	priv->event = NULL;

	return retval;
}

static gboolean
cb_window_scroll_event (GtkWidget *widget,
			GdkEventScroll *event,
			KzEmbed *kzembed)
{
	KzWindow *kz;
	gboolean retval = FALSE;
	KzWindowPrivate *priv;

	g_return_val_if_fail(KZ_IS_EMBED(kzembed), FALSE);

	kz = KZ_WINDOW(widget);
	priv = KZ_WINDOW_GET_PRIVATE (kz);

	if (priv->is_button3_pressed)
	{
		retval = handle_scroll_event (kz, event->direction);
		priv->is_button3_scrolled = retval;
	}
	
	return retval;
}

static void
cb_notebook_drag_data_received (GtkWidget *widget,
				GdkDragContext *drag_context,
				gint x, gint y,
				GtkSelectionData *data,
				guint info,
				guint time,
				KzWindow *kz)
{
	g_return_if_fail(KZ_IS_WINDOW(kz));

	switch (info)
	{
	case TARGET_KAZEHAKASE_TAB:
	{
		GtkWidget *src_widget;
		KzWindow *src_kz;
		KzTabLabel *src_kztab;

		src_widget = gtk_drag_get_source_widget(drag_context);

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

		src_kz = KZ_WINDOW(kz_window_get_from_tab(GTK_WIDGET(src_kztab->kzembed)));
		if (kz == src_kz)
		{
			gint n;

			n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(kz->notebook));
			kz_window_reorder_tab(kz, GTK_WIDGET(src_kztab->kzembed),
					      n - 1);
			kz_actions_set_sensitive
				(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
			kz_actions_set_tab_sensitive
				(kz, KZ_EMBED(KZ_WINDOW_CURRENT_PAGE(kz)));
		}
		else
		{
			kz_window_move_tab(kz, GTK_WIDGET(src_kztab->kzembed));
		}

		break;
	}

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

		if (data->length < 0) return;
		string = g_alloca (data->length + 1);

		memcpy (string, data->data, data->length);
		string[data->length] = '\0';

		strings = g_strsplit(string, "\n", 2);
		kz_window_open_new_tab(kz, strings[0]);
		g_strfreev(strings);
		break;

	}
	default:
		break;
	}
}

static gboolean 
cb_focus_out_event            (GtkWidget       *widget,
			       GdkEventFocus   *event,
			       KzWindow        *kz)
{
	KzWindowPrivate *priv = KZ_WINDOW_GET_PRIVATE (kz);
	if(!event->in) 
	{
		/* focus is out */
		if (kz_gesture_is_started(priv->gesture))
		{
			kz_gesture_cancel(priv->gesture);
			if (gdk_pointer_is_grabbed ())
			{
				gdk_pointer_ungrab (gtk_get_current_event_time ());
			}
			gtk_statusbar_pop(GTK_STATUSBAR(KZ_WINDOW(widget)->statusbar),
					  priv->status_gesture);
		}
	}
	return FALSE;
}

void
kz_window_append_closed_tab (KzWindow *kz, KzBookmark *bookmark)
{
	GList *children;
	KzBookmark *last;

	kz_bookmark_prepend(kz->closed_tabs, bookmark);

	/* removed the oldest one */
	children = kz_bookmark_get_children(kz->closed_tabs);
	if (children)
	{
		last = g_list_nth_data(children, MAX_CLOSED_TABS);
		if (last)
			kz_bookmark_remove(kz->closed_tabs, last);
		g_list_free(children);
	}
}

