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

/*
 *  Copyright (C) 2003 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-menu.c 642 2004-01-25 14:11:57Z ikezoe $
 */

#include "kz-bookmark-menu.h"

#include "intl.h"
#include "kazehakase.h"
#include "kz-bookmark-editor.h"
#include "kz-actions.h"
#include "kz-favicon.h"

#define KZ_BOOKMARK_MENU_BOOKMARK_KEY "KzBookmarkMenu::Bookmark"
#define KZ_BOOKMARK_MENU_WINDOW_KEY   "KzBookmarkMenu::Window"
#define KZ_BOOKMARK_MENU_TOOLTIP_KEY  "KzBookmarkMenu::ToolTips"
#define KZ_BOOKMARK_MENU_EMPTY_KEY    "KzBookmarkMEnu::Empty"
#define KZ_BOOKMARK_MENU_PROXY_KEY    "KzBookmarkMenu::IsFolderProxy"


static void       bookmark_folder_open_all       (KzBookmark     *bookmark,
						  KzWindow       *kz);

static gboolean   cb_menu_item_button_press      (GtkWidget      *widget,
						  GdkEventButton *event,
						  KzWindow       *kz);
static gboolean   cb_menu_item_button_release    (GtkWidget      *widget,
						  GdkEventButton *event,
						  KzWindow       *kz);

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);


GtkWidget *
kz_bookmark_menu_create_menuitem (KzBookmark *bookmark, KzWindow *kz,
				  gboolean is_folder_proxy)
{
	GtkWidget *menu_item;
	const gchar *title = NULL, *desc;
	GtkTooltips *tooltip = NULL;
	GtkWidget *favicon;
	KzFavicon *kzfav;

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

	/* separator */
	if (kz_bookmark_is_separator(bookmark))
	{
		menu_item = gtk_separator_menu_item_new();
		g_object_set_data(G_OBJECT(menu_item),
				  KZ_BOOKMARK_MENU_BOOKMARK_KEY,
				  bookmark);
		g_object_set_data(G_OBJECT(menu_item),
				  KZ_BOOKMARK_MENU_WINDOW_KEY,
				  kz);
		return menu_item;
	}

	if (is_folder_proxy)
		title = kz_bookmark_get_document_title(bookmark);
	if (!title || !* title)
		title = kz_bookmark_get_title(bookmark);
	if (!title)
		title = "";
	desc = kz_bookmark_get_description(bookmark);

	menu_item = gtk_image_menu_item_new_with_label(title);
	g_object_set_data(G_OBJECT(menu_item),
			  KZ_BOOKMARK_MENU_BOOKMARK_KEY,
			  bookmark);
	g_object_set_data(G_OBJECT(menu_item),
			  KZ_BOOKMARK_MENU_WINDOW_KEY,
			  kz);
	g_object_set_data(G_OBJECT(menu_item),
			  KZ_BOOKMARK_MENU_PROXY_KEY,
			  GINT_TO_POINTER(is_folder_proxy));

	g_signal_connect(G_OBJECT(menu_item), "button_press_event",
			 G_CALLBACK(cb_menu_item_button_press),
			 kz);
	g_signal_connect(G_OBJECT(menu_item), "button_release_event",
			 G_CALLBACK(cb_menu_item_button_release),
			 kz);

	/* submenu */
	if (kz_bookmark_is_folder(bookmark) && !is_folder_proxy)
	{
		GtkWidget *submenu;

		submenu = kz_bookmark_menu_create_submenu(bookmark, kz);
		gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
	}
	
	/* favicon */
	kzfav = kz_favicon_get_instance();
	favicon = kz_favicon_get_widget(kzfav,
					kz_bookmark_get_link(bookmark),
					KZ_ICON_SIZE_BOOKMARK_MENU);
	if (favicon)
	{
		gtk_widget_show(favicon);
		gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
					      favicon);
	}
	g_object_unref(kzfav);

	/* tooltips */
	tooltip = gtk_tooltips_new();
	g_object_ref(G_OBJECT(tooltip));
	gtk_object_sink(GTK_OBJECT(tooltip));
	gtk_tooltips_set_tip(tooltip, menu_item, desc, NULL);
	g_object_set_data_full(G_OBJECT(menu_item),
			       KZ_BOOKMARK_MENU_TOOLTIP_KEY,
			       tooltip,
			       (GDestroyNotify) g_object_unref);

	connect_bookmark_signals(menu_item, bookmark);

	return menu_item;
}


GtkWidget *
kz_bookmark_menu_create_submenu (KzBookmark *folder, KzWindow *kz)
{
	GtkWidget *submenu, *menu_item;
	const gchar *link, *title;

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

	/* create submenu  */
	submenu = gtk_menu_new();

	/* channel link */
	link  = kz_bookmark_get_link(folder);
	title = kz_bookmark_get_document_title(folder);
	if (!title || !* title) title = kz_bookmark_get_title(folder);

	menu_item = kz_bookmark_menu_create_menuitem(folder, kz, TRUE);
	if (menu_item)
	{
		gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menu_item);
		gtk_widget_show(menu_item);
	}

	/* separator */
	menu_item = gtk_separator_menu_item_new();
	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menu_item);
	gtk_widget_show(menu_item);

	/* children */
	kz_boommark_menu_append_menuitems(GTK_MENU_SHELL(submenu), kz, folder);

	return submenu;
}


void
kz_boommark_menu_append_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_MENU_BOOKMARK_KEY);
	if (prev_folder)
	{
		disconnect_folder_signals(folder, shell);
		disconnect_menu_signals(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_MENU_WINDOW_KEY,
				  kz);
		g_object_set_data(G_OBJECT(shell),
				  KZ_BOOKMARK_MENU_EMPTY_KEY,
				  widget);
	}


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

		widget = kz_bookmark_menu_create_menuitem(bookmark, kz,
							  FALSE);
		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_MENU_BOOKMARK_KEY,
			  folder);
	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_MENU_WINDOW_KEY,
			  kz);
}


void
kz_boommark_menu_remove_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_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;
		gpointer p;
		gboolean is_folder_proxy;

		kz = g_object_get_data(G_OBJECT(menuitem),
				       KZ_BOOKMARK_MENU_WINDOW_KEY);
		p = g_object_get_data(G_OBJECT(menuitem),
				      KZ_BOOKMARK_MENU_PROXY_KEY);
		is_folder_proxy = GPOINTER_TO_INT(p);
		if (kz && !is_folder_proxy)
			gtk_widget_destroy(menuitem);
	}

	g_list_free(children);

	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_MENU_BOOKMARK_KEY,
			  NULL);
	g_object_set_data(G_OBJECT(shell),
			  KZ_BOOKMARK_MENU_WINDOW_KEY,
			  NULL);
}


static void
bookmark_folder_open_all (KzBookmark *bookmark, KzWindow *kz)
{
	GList *node, *items;
	KzBookmark *item = NULL;
	GtkWidget *parent = NULL;
	const gchar *channel_uri;

	channel_uri = kz_bookmark_get_link(bookmark);
	if (channel_uri)
		parent = kz_window_open_new_tab(kz, channel_uri);

	items = kz_bookmark_get_children(bookmark);
	for (node = items; node; node = g_list_next(node))
	{
		const gchar *uri;

		item = node->data;
		uri = kz_bookmark_get_link(item);
		if (uri)
			kz_window_open_new_tab_with_parent(kz, uri, parent);
	}
	g_list_free(items);
}


/*****************************************************************************
 *                                                                           *
 *                                Callbacks                                  *
 *                                                                           *
 *****************************************************************************/
#if 0
static void 
cb_bookmark_load_start (KzBookmark *meta, KzBookmarkMenu *bkmkmenu)
{
	g_return_if_fail(KZ_IS_BOOKMARK(meta));
	g_return_if_fail(KZ_IS_BOOKMARK_MENU(bkmkmenu));

	gtk_image_set_from_stock(GTK_IMAGE(bkmkmenu->image),
				 KZ_STOCK_RED,
				 KZ_ICON_SIZE_BOOKMARK_MENU);
}


static void 
cb_bookmark_load_stop (KzBookmark *bookmark, KzBookmarkMenu *menu)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_return_if_fail(KZ_IS_BOOKMARK_MENU(menu));

	kz_bookmark_menu_create_submenu(menu, bookmark, NULL);

	gtk_image_set_from_stock(GTK_IMAGE(menu->image),
				 KZ_STOCK_GREEN,
				 KZ_ICON_SIZE_BOOKMARK_MENU);
}
#endif


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

	bookmark1 = g_object_get_data(G_OBJECT(a),
				      KZ_BOOKMARK_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;

	desc = kz_bookmark_get_description(KZ_BOOKMARK(object));
	tooltip = g_object_get_data(G_OBJECT(widget),
				    KZ_BOOKMARK_MENU_TOOLTIP_KEY);
	if (GTK_IS_TOOLTIPS(tooltip))
		gtk_tooltips_set_tip(tooltip, widget, desc, NULL);
}


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(G_OBJECT(item), "notify::title",
			 G_CALLBACK(cb_notify_title),
			 widget);
	g_signal_connect(G_OBJECT(item), "notify::description",
			 G_CALLBACK(cb_notify_desc),
			 widget);

	g_signal_connect(G_OBJECT(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(G_OBJECT(item),
					     G_CALLBACK(cb_notify_title),
					     widget);
	g_signal_handlers_disconnect_by_func(G_OBJECT(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(GTK_OBJECT(widget),
					     G_CALLBACK(cb_menuitem_destroy),
					     item);
}


static gboolean
cb_menu_item_button_press (GtkWidget *widget, GdkEventButton *event,
			   KzWindow *kz)
{
	KzBookmark *item;
	const gchar *uri;

	g_return_val_if_fail(KZ_IS_WINDOW(kz), FALSE);

	item = g_object_get_data(G_OBJECT(widget),
				 KZ_BOOKMARK_MENU_BOOKMARK_KEY);
	g_return_val_if_fail(item, FALSE);

	uri = kz_bookmark_get_link(item);

	switch (event->button)
	{
	case 1:
		if (uri)
		{
			kz_window_load_url(kz, uri);
		}
		else if (kz_bookmark_is_folder(item))
		{
			GtkWidget *editor;

			editor = kz_bookmark_editor_new(kz_bookmark);
			kz_bookmark_editor_folder_view_select
				(KZ_BOOKMARK_EDITOR(editor), item);
			gtk_widget_show(editor);
		}
		return TRUE;
		break;
	case 2:
		if (kz_bookmark_is_folder(item))
			bookmark_folder_open_all(item, kz);
		else if (uri)
			kz_window_open_new_tab(kz, uri);
		return TRUE;
		break;
	case 3:
		kz_actions_popup_bookmark_menu_modal(kz, item,
						     event->button,
						     event->time);
		/* return TRUE; */
		break;
	default:
		break;
	}

	return FALSE;
}


static gboolean
cb_menu_item_button_release (GtkWidget *widget, GdkEventButton *event,
			     KzWindow *kz)
{
	gboolean keep = FALSE;

	if (event->button == 1)
		return FALSE;

	KZ_CONF_GET("Global", "keep_bookmark_menu", keep, BOOL);
	if (keep)
		return TRUE;
	return FALSE;
}

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

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

	menuitem = kz_bookmark_menu_create_menuitem(child, kz, FALSE);
	gtk_widget_show(menuitem);

	if (sibling)
	{
		node = g_list_find_custom(list, sibling, find_menu_item);
		g_return_if_fail(node);

		n = g_list_position(list, node);
		gtk_menu_shell_insert(shell, menuitem, n);
	}
	else
	{
		gtk_menu_shell_append(shell, menuitem);
	}

	empty_label = g_object_get_data(G_OBJECT(shell),
					KZ_BOOKMARK_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;

	kz = g_object_get_data(G_OBJECT(shell), KZ_BOOKMARK_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))
	{
		GtkWidget *empty_label;

		empty_label = g_object_get_data(G_OBJECT(shell),
						KZ_BOOKMARK_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_MENU_WINDOW_KEY);
	kz_boommark_menu_remove_menuitems(shell, kz);
	kz_boommark_menu_append_menuitems(shell, kz, item);
}


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(G_OBJECT(folder), "insert-child",
			       G_CALLBACK(cb_folder_insert_child),
			       shell);
	g_signal_connect_after(G_OBJECT(folder), "remove-child",
			       G_CALLBACK(cb_folder_remove_child),
			       shell);
	g_signal_connect_after(G_OBJECT(folder), "children-reordered",
			       G_CALLBACK(cb_folder_reordered),
			       shell);

	g_signal_connect(G_OBJECT(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
		(G_OBJECT(folder),
		 G_CALLBACK(cb_folder_insert_child),
		 shell);
	g_signal_handlers_disconnect_by_func
		(G_OBJECT(folder),
		 G_CALLBACK(cb_folder_remove_child),
		 shell);
	g_signal_handlers_disconnect_by_func
		(G_OBJECT(folder),
		 G_CALLBACK(cb_folder_reordered),
		 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
		(GTK_OBJECT(shell),
		 G_CALLBACK(cb_root_menu_destroy),
		 folder);
}
