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

/*
 *  Copyright (C) 2003-2004 Hiroyuki Ikezoe
 *  Copyright (C) 2003 Takuro Ashie <ashie@homa.ne.jp>
 *
 *  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-item.c 2714 2006-12-31 01:59:41Z ikezoe $
 */

#include "kz-bookmark-item.h"

#include <gtk/gtk.h>
#include <glib/gi18n.h>

#include "kazehakase.h"
#include "kz-bookmark-file.h"
#include "kz-bookmark-menu.h"
#include "kz-bookmark-menu-item.h"
#include "kz-bookmark-folder-menu-item.h"
#include "kz-bookmark-editor.h"
#include "kz-icons.h"
#include "kz-actions.h"
#include "gtk-utils.h"

enum {
	PROP_0,
	PROP_BOOKMARK,
	PROP_KZ_WINDOW
};

enum {
	TARGET_KAZEHAKASE_BOOKMARKS,
	TARGET_NETSCAPE_URL,
	TARGET_TEXT_URI_LIST
};

static GtkTargetEntry dnd_types[] = {
	{"_KAZEHAKASE_BOOKMARKS", 0, TARGET_KAZEHAKASE_BOOKMARKS},
};

static GtkTargetEntry url_drag_types [] = 
{
	{"_KAZEHAKASE_BOOKMARKS", 0, TARGET_KAZEHAKASE_BOOKMARKS},
        { "_NETSCAPE_URL",        0, TARGET_NETSCAPE_URL},
	{ "text/uri-list",        0, TARGET_TEXT_URI_LIST}
};

static const gint dnd_types_num = G_N_ELEMENTS(dnd_types);
static const gint n_url_drag_types = G_N_ELEMENTS(url_drag_types);

/* object class methods */
static GObject *constructor      (GType           type,
                                  guint           n_props,
                                  GObjectConstructParam *props);
static void     dispose          (GObject        *object);
static void     set_property     (GObject        *object,
                                  guint           prop_id,
                                  const GValue   *value,
                                  GParamSpec     *pspec);
static void     get_property     (GObject        *object,
                                  guint           prop_id,
                                  GValue         *value,
                                  GParamSpec     *pspec);
/* GtkToolItem class methods */
static gboolean create_menu_proxy (GtkToolItem     *item);

/* widget class methods */
static void    drag_data_get    (GtkWidget        *widget,
                                 GdkDragContext   *context,
                                 GtkSelectionData *seldata,
                                 guint             info,
                                 guint             time);
static void    drag_begin       (GtkWidget        *widget,
                                 GdkDragContext   *context);
static void    drag_end         (GtkWidget        *widget,
                                 GdkDragContext   *context);

/* private methods */
static void     kz_bookmark_item_reset_submenu    (KzBookmarkItem *menu);

/* callback for bookmark objects */
static void     cb_bookmark_load_start            (KzBookmark *bookmark,
						   KzBookmarkItem *menu);
static void     cb_bookmark_load_stop             (KzBookmark *bookmark,
						   KzBookmarkItem *menu);
static void     cb_bookmark_load_error            (KzBookmark *bookmark,
						   const gchar *error,
						   KzBookmarkItem *menu);

/* callbacks for child widgets */
static gboolean cb_bookmark_menu_release          (GtkWidget        *widget,
						   GdkEventButton   *event,
						   KzBookmarkItem   *menu);
static void     cb_update_button_clicked          (GtkWidget        *widget,
						   KzBookmarkItem   *menu);

static void	cb_notify_title 		  (GObject *object, GParamSpec *pspec,
						   KzBookmarkItem *menu);

static void     cb_drag_data_received             (GtkWidget        *widget,
						   GdkDragContext   *context,
						   gint              x,
						   gint              y,
			                           GtkSelectionData *selection_data,
						   guint             info,
						   guint             time,
						   gpointer          data);
G_DEFINE_TYPE(KzBookmarkItem, kz_bookmark_item, GTK_TYPE_TOOL_ITEM)

static void
kz_bookmark_item_class_init (KzBookmarkItemClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;
	GtkToolItemClass *tool_item_class;

	gobject_class   = (GObjectClass *) klass;
	widget_class    = (GtkWidgetClass *) klass;
	tool_item_class = (GtkToolItemClass *) klass;

	/* GObject signals */
	gobject_class->dispose      = dispose;
	gobject_class->constructor  = constructor;
	gobject_class->set_property = set_property;
	gobject_class->get_property = get_property;

	widget_class->drag_data_get = drag_data_get;
	widget_class->drag_begin    = drag_begin;
	widget_class->drag_end      = drag_end;

  	tool_item_class->create_menu_proxy = create_menu_proxy;

	g_object_class_install_property
		(gobject_class,
		 PROP_BOOKMARK,
		 g_param_spec_object ("bookmark",
				      _("Bookmark"),
				      _("The boomark to show"),
				      KZ_TYPE_BOOKMARK,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object ("kz-window",
				      _("KzWindow"),
				      _("The KzWindow to add a home button"),
				      KZ_TYPE_WINDOW,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
}


static void
kz_bookmark_item_init (KzBookmarkItem *menu)
{
	menu->kz            = NULL;
	menu->bookmark      = NULL;
	menu->sub_menu      = NULL;
	menu->update_button = NULL;
	menu->image         = NULL;
}


static GObject*
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	KzBookmarkItem *item;
	GtkWidget *button, *image, *eventbox, *hbox;
	GtkContainer *container;
	GtkRcStyle *style;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(kz_bookmark_item_parent_class);
	KzBookmarkFileState state;
	gboolean is_file, is_folder;

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

	container = GTK_CONTAINER(object);
	hbox = gtk_hbox_new(FALSE, 1);
	item = KZ_BOOKMARK_ITEM(object);
	is_file = KZ_IS_BOOKMARK_FILE(item->bookmark);
	is_folder = kz_bookmark_is_folder(item->bookmark);
	
	gtk_container_set_border_width(container, 2);
	gtk_container_add(container, hbox);
	gtk_widget_show (hbox);

	/* button */
	item->update_button = button = gtk_button_new();
	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
	if (is_file)
	{
		g_signal_connect(button, "clicked", 
				 G_CALLBACK(cb_update_button_clicked),
				 item);
		item->image = image
			= gtk_image_new_from_stock(KZ_STOCK_GREEN,
						   KZ_ICON_SIZE_BOOKMARK_MENU);
	}
	else
	{
		g_signal_connect(button, "button_release_event",
			 	 G_CALLBACK(cb_bookmark_menu_release), item);
		if(is_folder)
		{
			item->image = image
				= gtk_image_new_from_stock(KZ_STOCK_FOLDER,
							   KZ_ICON_SIZE_BOOKMARK_MENU);
			gtk_drag_dest_set(GTK_WIDGET(item), 
					  GTK_DEST_DEFAULT_ALL, 
	               		          url_drag_types, n_url_drag_types,
			  		  GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
			g_signal_connect(item, "drag-data-received", 
					 G_CALLBACK(cb_drag_data_received), NULL);
		}
		else
		{
			KzFavicon *kzfav = KZ_GET_FAVICON;
			GtkWidget *favicon = NULL;

			/* favicon */
			favicon	= kz_favicon_get_widget(kzfav,
							kz_bookmark_get_link(item->bookmark),
							KZ_ICON_SIZE_BOOKMARK_MENU);
			if (favicon)
			{
				item->image = image = favicon;
			}
			else
			{
				item->image = image
					= gtk_image_new_from_stock(KZ_STOCK_BOOKMARK,
								   KZ_ICON_SIZE_BOOKMARK_MENU);
			}
		}
	}

	gtk_container_add(GTK_CONTAINER(button), image);
	gtk_widget_show(image);
	gtk_widget_show(button);
	style = gtk_rc_style_new ();
	style->xthickness = style->ythickness = 0;
	gtk_widget_modify_style (button, style);
	gtk_rc_style_unref (style);

	if (is_file)
	{
		g_signal_connect(item->bookmark,
				 "load_start", 
				 G_CALLBACK(cb_bookmark_load_start),
				 item);
		g_signal_connect(item->bookmark,
				 "load_completed", 
				 G_CALLBACK(cb_bookmark_load_stop),
				 item);
		g_signal_connect(item->bookmark,
				 "error", 
				 G_CALLBACK(cb_bookmark_load_error),
				 item);

		state = kz_bookmark_file_get_state(KZ_BOOKMARK_FILE(item->bookmark));
		if (state == KZ_BOOKMARK_FILE_STATE_LOADING)
			cb_bookmark_load_start(item->bookmark, item);
	}
	/* label */
	eventbox = gtk_event_box_new();
	gtk_event_box_set_visible_window(GTK_EVENT_BOX(eventbox), FALSE);
	item->eventbox = eventbox;
	gtk_box_pack_start(GTK_BOX(hbox), eventbox, FALSE, FALSE, 0);
	g_signal_connect(eventbox, "button_release_event",
			 G_CALLBACK(cb_bookmark_menu_release), item);
	gtk_widget_show (eventbox);

	item->label = gtk_label_new(kz_bookmark_get_title(item->bookmark));
	gtk_container_add(GTK_CONTAINER(eventbox), item->label);
	gtk_widget_show (item->label);

	gtk_drag_source_set(GTK_WIDGET(item),
			    GDK_BUTTON1_MASK |
			    GDK_BUTTON2_MASK |
			    GDK_BUTTON3_MASK,
			    dnd_types,
			    dnd_types_num,
			    GDK_ACTION_ASK  | GDK_ACTION_COPY
			    | GDK_ACTION_MOVE | GDK_ACTION_LINK);
	/* submenu */
	kz_bookmark_item_reset_submenu(item);

	g_signal_connect(item->bookmark, "notify::title",
			 G_CALLBACK(cb_notify_title), item);

	return object;
}


static void
dispose (GObject *object)
{
	KzBookmarkItem *menu;

	if (G_OBJECT_CLASS (kz_bookmark_item_parent_class)->dispose)
		G_OBJECT_CLASS (kz_bookmark_item_parent_class)->dispose(object);

	menu = KZ_BOOKMARK_ITEM(object);

	if (menu->bookmark)
	{
		if (KZ_IS_BOOKMARK_FILE(menu->bookmark))
		{
			g_signal_handlers_disconnect_by_func(
				menu->bookmark,
				cb_bookmark_load_start,
				menu);
			g_signal_handlers_disconnect_by_func(
				menu->bookmark,
				cb_bookmark_load_stop,
				menu);
			g_signal_handlers_disconnect_by_func(
				menu->bookmark,
				cb_bookmark_load_error,
				menu);
		}
		g_signal_handlers_disconnect_by_func(menu->bookmark,
					     G_CALLBACK(cb_notify_title),
					     menu);
		g_object_unref(menu->bookmark);
		menu->bookmark = NULL;
	}

	if (menu->sub_menu)
	{
		gtk_widget_destroy(menu->sub_menu);
		menu->sub_menu = NULL;
	}

	if (menu->kz)
		g_object_unref(menu->kz);
	menu->kz = NULL;
}


static void
set_property (GObject         *object,
			       guint            prop_id,
			       const GValue    *value,
			       GParamSpec      *pspec)
{
	KzBookmarkItem *menu = KZ_BOOKMARK_ITEM(object);
  
	switch (prop_id)
	{
	case PROP_BOOKMARK:
		menu->bookmark = g_object_ref(g_value_get_object(value));
		break;
	case PROP_KZ_WINDOW:
		menu->kz = g_object_ref(g_value_get_object(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
get_property (GObject         *object,
			       guint            prop_id,
			       GValue          *value,
			       GParamSpec      *pspec)
{
	KzBookmarkItem *item = KZ_BOOKMARK_ITEM(object);

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


static gboolean
create_menu_proxy (GtkToolItem *item)
{
	KzBookmarkItem *bookmark_item = KZ_BOOKMARK_ITEM(item);
	GtkWidget *proxy;

	if (kz_bookmark_is_folder(bookmark_item->bookmark))
	{
		proxy = kz_bookmark_folder_menu_item_new(bookmark_item->kz,
							 bookmark_item->bookmark);
	}
	else
	{
		proxy = kz_bookmark_menu_item_new(bookmark_item->kz,
						  bookmark_item->bookmark);
	}
	gtk_tool_item_set_proxy_menu_item(GTK_TOOL_ITEM(item),
					  "bookmark", proxy);
  
	return TRUE;
}

GtkToolItem *
kz_bookmark_item_new (KzWindow *kz, KzBookmark *bookmark)
{
	KzBookmarkItem *menu;

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

	menu = g_object_new(KZ_TYPE_BOOKMARK_ITEM,
			   "kz-window", kz,
			   "bookmark",  bookmark,
			   NULL);

	return GTK_TOOL_ITEM(menu);
}


/*****************************************************************************
 *                                                                           *
 *                            Private methods                                *
 *                                                                           *
 *****************************************************************************/
static void
kz_bookmark_item_reset_submenu (KzBookmarkItem *menu)
{
	GtkWidget *submenu;

	if (!kz_bookmark_is_folder(menu->bookmark))
		return;

	submenu = kz_bookmark_menu_create_submenu(menu->bookmark, menu->kz);
	if (menu->sub_menu)
	{
		gtk_widget_destroy(menu->sub_menu);
	}

	menu->sub_menu = submenu;
}


/*****************************************************************************
 *                                                                           *
 *                                Callbacks                                  *
 *                                                                           *
 *****************************************************************************/
static void 
cb_bookmark_load_start (KzBookmark *bookmark, KzBookmarkItem *menu)
{
	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(menu));

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


static void 
cb_bookmark_load_stop (KzBookmark *bookmark, KzBookmarkItem *menu)
{
	gboolean update = TRUE;
	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(menu));

	kz_bookmark_item_reset_submenu(menu);

	if (KZ_IS_BOOKMARK_FILE(bookmark))
		update = kz_bookmark_file_is_update(KZ_BOOKMARK_FILE(bookmark));

	if (update)
	{
		gtk_image_set_from_stock(GTK_IMAGE(menu->image),
					 KZ_STOCK_YELLOW,
					 KZ_ICON_SIZE_BOOKMARK_MENU);
	}
	else
	{
		gtk_image_set_from_stock(GTK_IMAGE(menu->image),
					 KZ_STOCK_GREEN,
					 KZ_ICON_SIZE_BOOKMARK_MENU);
	}
}


static void 
cb_bookmark_load_error (KzBookmark *bookmark, const gchar *error, KzBookmarkItem *menu)
{
	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(menu));

	gtk_image_set_from_stock(GTK_IMAGE(menu->image),
				 KZ_STOCK_GRAY,
				 KZ_ICON_SIZE_BOOKMARK_MENU);
}


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

	title = kz_bookmark_get_title(KZ_BOOKMARK(object));
	gtk_label_set_text(GTK_LABEL(menu->label), title);
}


static gboolean
cb_bookmark_menu_release (GtkWidget *widget, GdkEventButton *event,
			  KzBookmarkItem *menu)
{
	KzWindow *kz;
	GdkEventButton *event_button;

	kz = menu->kz;

	event_button = (GdkEventButton *)event;

	switch(event_button->button)
	{
	case 1:
		if (kz_bookmark_is_folder(menu->bookmark))
		{
			if (!GTK_IS_MENU(menu->sub_menu))
				return FALSE;

			gtk_menu_popup(GTK_MENU(menu->sub_menu), NULL, NULL, 
				       gtkutil_menu_position_under_widget,
				       GTK_WIDGET(menu),
				       0, event_button->time);
		}
		else
		{
			const gchar *uri = kz_bookmark_get_link(menu->bookmark);
			if (uri)
				kz_window_load_url(kz, uri);
		}
		break;
	case 2:
		if (kz_bookmark_is_folder(menu->bookmark))
		{
			GtkAction *action;
			kz_actions_set_bookmark_for_action(kz, menu->bookmark);
			action = gtk_action_group_get_action(kz->actions,
							     "OpenAllBookmarks");
			if (action)
				gtk_action_activate(action);
			kz_actions_set_bookmark_for_action(kz, NULL);
		}
		else
		{
			const gchar *uri = kz_bookmark_get_link(menu->bookmark);
			if (uri)
				kz_window_open_new_tab(kz, uri);
		}
		break;
	case 3:
		kz_actions_popup_bookmark_menu_modal(kz, menu->bookmark,
						     event->button,
						     event->time);
		break;
	default:
		break;
	}

	return TRUE;
}


static void
cb_update_button_clicked (GtkWidget *widget, KzBookmarkItem *menu)
{
	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(menu));

	kz_bookmark_file_load_start(KZ_BOOKMARK_FILE(menu->bookmark));
}


static void
drag_data_get (GtkWidget *widget,
               GdkDragContext *context,
               GtkSelectionData *seldata,
               guint info,
               guint time)
{
	switch (info)
	{
	case TARGET_KAZEHAKASE_BOOKMARKS:
		gtk_selection_data_set(seldata, seldata->target,
				       8, (const guchar*)"dummy", strlen("dummy"));
		break;
	}
}


static void
drag_begin (GtkWidget *widget, GdkDragContext *context)
{
	KzBookmarkItem *item;
	KzBookmark *bookmark;
	const gchar *stock_id;

	item = KZ_BOOKMARK_ITEM(widget);
	bookmark = item->bookmark;

	if (KZ_IS_BOOKMARK_FILE(bookmark))
		stock_id = KZ_STOCK_REMOTE_BOOKMARK;
	else if (kz_bookmark_is_folder(bookmark))
		stock_id = KZ_STOCK_FOLDER;
	else
		stock_id = KZ_STOCK_BOOKMARK;

	gtk_widget_hide(widget);
	gtk_drag_source_set_icon_stock(widget, stock_id);
}


static void
drag_end (GtkWidget *widget, GdkDragContext *context)
{
	gtk_widget_show(widget);
}


static void
cb_drag_data_received (GtkWidget        *widget,
		       GdkDragContext   *context,
		       gint              x,
		       gint              y,
		       GtkSelectionData *selection_data,
		       guint             info,
		       guint             time,
		       gpointer          data)
{
	KzBookmarkItem *item;
	KzBookmark *bookmark, *parent, *file;
	gchar **strings = NULL; 
	GtkWidget *src_widget;
	const gchar *title;
	gchar *utf8_title = NULL;

	item = KZ_BOOKMARK_ITEM(widget);
	switch (info)
	{
	 case TARGET_KAZEHAKASE_BOOKMARKS:
		src_widget = gtk_drag_get_source_widget(context);
		
		if (!KZ_IS_BOOKMARK_ITEM(src_widget))
			break;

		bookmark = KZ_BOOKMARK_ITEM(src_widget)->bookmark;
		g_object_ref(bookmark);

		parent = kz_bookmark_get_parent(bookmark);
		kz_bookmark_remove(parent, bookmark);

		kz_bookmark_append(item->bookmark, bookmark);
		if (KZ_IS_BOOKMARK_FILE(item->bookmark))
			file = item->bookmark;
		else
			file = kz_bookmark_get_parent_file(item->bookmark);
		
		if (kz_bookmark_file_has_xmlrpc(KZ_BOOKMARK_FILE(file)))
		{
			kz_bookmark_file_xmlrpc_insert(KZ_BOOKMARK_FILE(file),
						       item->bookmark,
						       NULL,
						       bookmark);
		}
		else
		{
			kz_bookmark_file_save(KZ_BOOKMARK_FILE(file));
		}

		gtk_drag_finish(context, TRUE, TRUE, time);
	 	break;
	 case TARGET_NETSCAPE_URL:
	 case TARGET_TEXT_URI_LIST:
		if (selection_data->length < 0) return;
		strings = g_strsplit((const gchar*)selection_data->data, "\n", 2);
		if (!strings) return;
		if (strings[1] != NULL)
		{
			utf8_title = g_locale_to_utf8(strings[1],
						      strlen(strings[1]),
						      NULL,
						      NULL,
						      NULL);
		}
		if (utf8_title && g_utf8_validate(utf8_title, -1, NULL))
			title = utf8_title;
		else 
			title = _("Title");
		
		/* FIXME! we should also add normal bookmark. */
		/* Create New KzBookmark */
		if (strstr(strings[0], "xml") ||
		    strstr(strings[0], "rss") ||
		    strstr(strings[0], "rdf"))
		{
			bookmark = KZ_BOOKMARK(kz_bookmark_file_new(strings[0],
							title,
							NULL));
		}
		else
		{
			bookmark = kz_bookmark_new_with_attrs(title,
							      strings[0],
							      NULL);
		}
		kz_bookmark_append(item->bookmark, bookmark);
		g_strfreev(strings);
		if (utf8_title)
			g_free(utf8_title);
		g_object_unref(bookmark);

		if (KZ_IS_BOOKMARK_FILE(item->bookmark))
			file = item->bookmark;
		else
			file = kz_bookmark_get_parent_file(item->bookmark);
		
		if (kz_bookmark_file_has_xmlrpc(KZ_BOOKMARK_FILE(file)))
		{
			kz_bookmark_file_xmlrpc_insert(KZ_BOOKMARK_FILE(file),
						       item->bookmark,
						       NULL,
						       bookmark);
		}
		else
		{
			kz_bookmark_file_save(KZ_BOOKMARK_FILE(file));
		}
	
		gtk_drag_finish(context, TRUE, FALSE, time);
		break;
	 default:
	 	gtk_drag_finish(context, FALSE, FALSE, time);
		break;
	}
}


