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

/*
 *  Copyright (C) 2003-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.
 *
 *  $Id: kz-bookmark-tab-menu.c 2819 2007-01-20 05:09:01Z kous $
 */

#include "kz-bookmark-tab-menu.h"

#include <glib/gi18n.h>

#include "kazehakase.h"
#include "kz-actions.h"
#include "kz-tab-label.h"
#include "kz-favicon.h"
#include "utils.h"
#include "kz-icons.h"

#define KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY "KzBookmarkTabMenu::Bookmark"
#define KZ_BOOKMARK_TAB_MENU_WINDOW_KEY   "KzBookmarkTabMenu::Window"
#define KZ_BOOKMARK_TAB_MENU_TOOLTIP_KEY  "KzBookmarkTabMenu::ToolTips"
#define KZ_BOOKMARK_TAB_MENU_EMPTY_KEY    "KzBookmarkTabMEnu::Empty"

static void       connect_bookmark_signals       (GtkWidget      *widget,
						  KzBookmark     *item);
static void       disconnect_bookmark_signals    (GtkWidget      *widget,
						  KzBookmark     *item);
static void       disconnect_menuitem_signals    (GtkWidget      *widget,
						  KzBookmark     *item);

static void       connect_folder_signals         (KzBookmark     *folder,
						  GtkMenuShell   *shell);
static void       disconnect_folder_signals      (KzBookmark     *folder,
						  GtkMenuShell   *shell);
static void       disconnect_menu_signals        (KzBookmark     *folder,
						  GtkMenuShell   *shell);

static void       cb_tab_menu_item_activate      (GtkWidget      *widget,
						  KzWindow       *kz);


GtkWidget *
kz_bookmark_menu_create_tab_menuitem (KzBookmark *bookmark, KzWindow *kz)
{
	GtkWidget *menu_item;
	const gchar *title = NULL, *desc;
	gchar *tooltip_text = NULL;
	GtkTooltips *tooltip = NULL;
	GtkWidget *favicon;
	KzBookmark *cur_page;
	gint cur_pos;
	GList *pages;
	KzFavicon *kzfav = KZ_GET_FAVICON;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	g_return_val_if_fail(kz_bookmark_is_folder(bookmark), NULL);
	g_return_val_if_fail(KZ_IS_WINDOW(kz), NULL);

	cur_pos = kz_bookmark_get_current(bookmark);

	pages = kz_bookmark_get_children(bookmark);
	cur_page = KZ_BOOKMARK(g_list_nth_data(pages, cur_pos));
	g_list_free(pages);
	
	if (!KZ_IS_BOOKMARK(cur_page)) return NULL;
	
	title = kz_bookmark_get_title(cur_page);
	if (!title)
		title = "";

	desc = kz_bookmark_get_description(cur_page);
	if (desc)
		tooltip_text = remove_tag(desc, strlen(desc));
	
	menu_item = gtk_image_menu_item_new_with_label(title);
	g_object_set_data(G_OBJECT(menu_item),
			  KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY,
			  bookmark);
	g_object_set_data(G_OBJECT(menu_item),
			  KZ_BOOKMARK_TAB_MENU_WINDOW_KEY,
			  kz);

	g_signal_connect(menu_item, "activate",
			 G_CALLBACK(cb_tab_menu_item_activate),
			 kz);

	/* favicon */
	favicon = kz_favicon_get_widget(kzfav,
					kz_bookmark_get_link(cur_page),
					KZ_ICON_SIZE_BOOKMARK_MENU);
	if (favicon)
	{
		gtk_widget_show(favicon);
		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
					      favicon);
	}

	/* tooltips */
	tooltip = gtk_tooltips_new();
	g_object_ref(G_OBJECT(tooltip));
	g_object_ref_sink(G_OBJECT(tooltip));
	if (tooltip_text)
	{
		gtk_tooltips_set_tip(tooltip, menu_item, tooltip_text, NULL);
		g_free(tooltip_text);
	}
	g_object_set_data_full(G_OBJECT(menu_item),
			       KZ_BOOKMARK_TAB_MENU_TOOLTIP_KEY,
			       tooltip,
			       (GDestroyNotify) g_object_unref);
	connect_bookmark_signals(menu_item, cur_page);

	return menu_item;
}

void
kz_bookmark_menu_append_tab_menuitems (GtkMenuShell *shell, KzWindow *kz, 
				       KzBookmark *folder)
{
	KzBookmark *prev_folder;
	GList *children, *node;

	g_return_if_fail(GTK_IS_MENU_SHELL(shell));
	g_return_if_fail(KZ_BOOKMARK(folder));
	g_return_if_fail(kz_bookmark_is_folder(folder));

	prev_folder = g_object_get_data(G_OBJECT(shell),
					KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY);
	if (prev_folder)
	{
		disconnect_folder_signals(prev_folder, shell);
		disconnect_menu_signals(prev_folder, shell);
	}

	children = kz_bookmark_get_children(folder);

	/* empty label */
	{
		GtkWidget *widget;

		widget = gtk_image_menu_item_new_with_label(_("Empty Folder"));
		gtk_menu_shell_append(shell, widget);
		if (!children)
			gtk_widget_show(widget);
		gtk_widget_set_sensitive(widget, FALSE);
		g_object_set_data(G_OBJECT(widget),
				  KZ_BOOKMARK_TAB_MENU_WINDOW_KEY,
				  kz);
		g_object_set_data(G_OBJECT(shell),
				  KZ_BOOKMARK_TAB_MENU_EMPTY_KEY,
				  widget);
	}

	/* tabs */
	for (node = children; node; node = g_list_next(node))
	{
		KzBookmark *tab = node->data;
		GtkWidget *widget;

		widget = kz_bookmark_menu_create_tab_menuitem(tab, kz);
		gtk_menu_shell_append(shell, widget);
		gtk_widget_show(widget);
	}

	g_list_free(children);

	connect_folder_signals(folder, shell);

	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY,
			  folder);
	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_TAB_MENU_WINDOW_KEY,
			  kz);
}


void
kz_bookmark_menu_remove_tab_menuitems (GtkMenuShell *shell, KzWindow *kz)
{
	KzBookmark *folder;
	GList *children, *node;

	g_return_if_fail(GTK_IS_MENU_SHELL(shell));

	folder = g_object_get_data(G_OBJECT(shell),
				   KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY);
	if (KZ_IS_BOOKMARK(folder))
	{
		disconnect_folder_signals(folder, shell);
		disconnect_menu_signals(folder, shell);
	}

	children = g_list_copy(shell->children);

	for (node = children; node; node = g_list_next(node))
	{
		GtkWidget *menuitem = node->data;
		KzWindow *kz;

		kz = g_object_get_data(G_OBJECT(menuitem),
				       KZ_BOOKMARK_TAB_MENU_WINDOW_KEY);
		if (kz)
			gtk_widget_destroy(menuitem);
	}

	g_list_free(children);

	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY,
			  NULL);
	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_TAB_MENU_WINDOW_KEY,
			  NULL);
}


/*****************************************************************************
 *                                                                           *
 *                                Callbacks                                  *
 *                                                                           *
 *****************************************************************************/

static gint
find_menu_item (gconstpointer a, gconstpointer b)
{
	const KzBookmark *bookmark1, *bookmark2 = b;

	bookmark1 = g_object_get_data(G_OBJECT(a),
				      KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY);

	return bookmark1 - bookmark2;
}


static void
cb_notify_title (GObject *object, GParamSpec *pspec,
		 GtkWidget *widget)
{
	const gchar *title;

	title = kz_bookmark_get_title(KZ_BOOKMARK(object));
	if (GTK_IS_LABEL(GTK_BIN(widget)->child))
		gtk_label_set_text(GTK_LABEL(GTK_BIN(widget)->child), title);
}


static void
cb_notify_desc (GObject *object, GParamSpec *pspec,
		GtkWidget *widget)
{
	GtkTooltips *tooltip;
	const gchar *desc;
	gchar *tooltip_text = NULL;
	
	desc = kz_bookmark_get_description(KZ_BOOKMARK(object));
	if (desc)
		tooltip_text = remove_tag(desc, strlen(desc));
	tooltip = g_object_get_data(G_OBJECT(widget),
				    KZ_BOOKMARK_TAB_MENU_TOOLTIP_KEY);
	if (GTK_IS_TOOLTIPS(tooltip) && tooltip_text)
	{
		gtk_tooltips_set_tip(tooltip, widget, tooltip_text, NULL);
		g_free(tooltip_text);
	}
}


static void
bookmark_weak_ref (gpointer data, GObject *obj)
{
	KzBookmark *item = KZ_BOOKMARK(obj);
	GtkWidget *widget = data;

	if (!widget) return;

	disconnect_menuitem_signals (widget, item);
}


static void
cb_menuitem_destroy(GtkWidget *widget, KzBookmark *item)
{
	disconnect_bookmark_signals(widget, item);
}


static void
connect_bookmark_signals (GtkWidget *widget, KzBookmark *item)
{
	g_signal_connect(item, "notify::title",
			 G_CALLBACK(cb_notify_title),
			 widget);
	g_signal_connect(item, "notify::description",
			 G_CALLBACK(cb_notify_desc),
			 widget);

	g_signal_connect(widget, "destroy",
			 G_CALLBACK(cb_menuitem_destroy), item);
	g_object_weak_ref(G_OBJECT(item), bookmark_weak_ref, widget);
}


static void
disconnect_bookmark_signals (GtkWidget *widget, KzBookmark *item)
{
	g_signal_handlers_disconnect_by_func(item,
					     G_CALLBACK(cb_notify_title),
					     widget);
	g_signal_handlers_disconnect_by_func(item,
					     G_CALLBACK(cb_notify_desc),
					     widget);

	g_object_weak_unref(G_OBJECT(item), bookmark_weak_ref, widget);
}


static void
disconnect_menuitem_signals (GtkWidget *widget, KzBookmark *item)
{
	g_signal_handlers_disconnect_by_func(widget,
					     G_CALLBACK(cb_menuitem_destroy),
					     item);
}


/*****************************************************************************
 *                                                                           *
 *                               for  menu                                   *
 *                                                                           *
 *****************************************************************************/
static void
cb_folder_insert_child (KzBookmark *item,
			KzBookmark *child, KzBookmark *sibling,
			GtkMenuShell *shell)
{
	KzWindow *kz;
	GtkWidget *menuitem, *empty_label;
	GList *list;

	kz = g_object_get_data(G_OBJECT(shell), KZ_BOOKMARK_TAB_MENU_WINDOW_KEY);
	list = shell->children;

	menuitem = kz_bookmark_menu_create_tab_menuitem(child, kz);
	if (!menuitem) return;
	
	gtk_widget_show(menuitem);

	gtk_menu_shell_prepend(shell, menuitem);

	empty_label = g_object_get_data(G_OBJECT(shell),
					KZ_BOOKMARK_TAB_MENU_EMPTY_KEY);
	if (GTK_IS_WIDGET(empty_label))
		gtk_widget_hide(empty_label);
}


static void
cb_folder_remove_child (KzBookmark *item,
			KzBookmark *child, GtkMenuShell *shell)
{
	KzWindow *kz;
	GList *list, *node;
	GtkWidget *empty_label;

	kz = g_object_get_data(G_OBJECT(shell), KZ_BOOKMARK_TAB_MENU_WINDOW_KEY);
	list = shell->children;

	node = g_list_find_custom(list, child, find_menu_item);
	g_return_if_fail(node);
	gtk_widget_destroy(node->data);

	if (kz_bookmark_has_children(item)) return;

	empty_label = g_object_get_data(G_OBJECT(shell),
					KZ_BOOKMARK_TAB_MENU_EMPTY_KEY);
	if (GTK_IS_WIDGET(empty_label))
		gtk_widget_show(empty_label);
}


static void
cb_folder_reordered (KzBookmark *item, GtkMenuShell *shell)
{
	KzWindow *kz;

	kz = g_object_get_data(G_OBJECT(shell), KZ_BOOKMARK_TAB_MENU_WINDOW_KEY);
	kz_bookmark_menu_remove_tab_menuitems(shell, kz);
	kz_bookmark_menu_append_tab_menuitems(shell, kz, item);
}

static GList *
cb_folder_get_children (KzBookmark *item, GtkMenuShell *shell)
{
	GList *child, *children = NULL, *last_children = NULL;

	for (child = shell->children; child; child = g_list_next(child))
	{
		KzBookmark *bookmark;
		bookmark = g_object_get_data(G_OBJECT(child->data),
					     KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY);
		if (!children)
		{
			children = g_list_append(children, bookmark);
			last_children = children;
		}
		else
		{
			GList *result;
			result = g_list_append(last_children, bookmark);
			last_children = g_list_next(result);
		}
	}

	return children;
}

static gboolean
cb_folder_has_children (KzBookmark *item, GtkMenuShell *shell)
{
	return shell->children != NULL;
}


static void
folder_weak_ref (gpointer data, GObject *obj)
{
	KzBookmark *folder = KZ_BOOKMARK(obj);
	GtkMenuShell *shell = data;

	disconnect_menu_signals(folder, GTK_MENU_SHELL(shell));
}


static void
cb_root_menu_destroy(GtkWidget *widget, KzBookmark *folder)
{
	disconnect_folder_signals(folder, GTK_MENU_SHELL(widget));
}


static void
connect_folder_signals (KzBookmark *folder, GtkMenuShell *shell)
{
	g_signal_connect_after(folder, "insert-child",
			       G_CALLBACK(cb_folder_insert_child),
			       shell);
	g_signal_connect_after(folder, "remove-child",
			       G_CALLBACK(cb_folder_remove_child),
			       shell);
	g_signal_connect_after(folder, "children-reordered",
			       G_CALLBACK(cb_folder_reordered),
			       shell);
	g_signal_connect_after(folder, "get-children",
			       G_CALLBACK(cb_folder_get_children),
			       shell);
	g_signal_connect_after(folder, "has-children",
			       G_CALLBACK(cb_folder_has_children),
			       shell);

	g_signal_connect(shell, "destroy",
			 G_CALLBACK(cb_root_menu_destroy), folder);
	g_object_weak_ref(G_OBJECT(folder), folder_weak_ref, shell);
}


static void
disconnect_folder_signals (KzBookmark *folder, GtkMenuShell *shell)
{
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_folder_insert_child),
		 shell);
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_folder_remove_child),
		 shell);
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_folder_reordered),
		 shell);
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_folder_get_children),
		 shell);
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_folder_has_children),
		 shell);

	g_object_weak_unref(G_OBJECT(folder), folder_weak_ref, shell);
}


static void
disconnect_menu_signals (KzBookmark *folder, GtkMenuShell *shell)
{
	g_signal_handlers_disconnect_by_func
		(shell,
		 G_CALLBACK(cb_root_menu_destroy),
		 folder);
}

/* for closed tab menu */
static void 
cb_tab_menu_item_activate (GtkWidget *widget, KzWindow *kz)
{
	KzBookmark *item;
	GtkWidget *kzembed;
	KzTabLabel *kztab;

	g_return_if_fail(KZ_IS_WINDOW(kz));

	item = g_object_get_data(G_OBJECT(widget),
				 KZ_BOOKMARK_TAB_MENU_BOOKMARK_KEY);
	g_return_if_fail(item);

	kzembed = kz_window_open_new_tab(kz, NULL);
	kztab = KZ_TAB_LABEL(gtk_notebook_get_tab_label(
			     GTK_NOTEBOOK(kz->notebook),
			     kzembed));
	kz_tab_label_set_history(KZ_TAB_LABEL(kztab), item);

	/* FIXME! should use parent bookmark */ 
	kz_bookmark_remove(kz->closed_tabs, item);
}

