/* -*- 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 3822 2009-09-04 00:28:36Z 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-bookmark-utils.h"
#include "kz-icons.h"
#include "kz-notebook.h"
#include "kz-actions.h"
#include "gtk-utils.h"

enum {
	PROP_0,
	PROP_BOOKMARK,
	PROP_KZ_WINDOW
};

enum {
	TARGET_GTK_NOTEBOOK_TAB,
	TARGET_KAZEHAKASE_BOOKMARKS,
	TARGET_NETSCAPE_URL,
	TARGET_TEXT_URI_LIST
};

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

static const GtkTargetEntry url_drag_types [] = 
{
	{"GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, TARGET_GTK_NOTEBOOK_TAB},
	{"_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   = G_OBJECT_CLASS(klass);
	widget_class    = GTK_WIDGET_CLASS(klass);
	tool_item_class = GTK_TOOL_ITEM_CLASS(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);
	g_object_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 = NULL, *file;
        KzBookmarkFolder *parent;
	GtkWidget *src_widget;
	gboolean success = FALSE, moved = FALSE;

	item = KZ_BOOKMARK_ITEM(widget);
	switch (info)
	{
	 case TARGET_GTK_NOTEBOOK_TAB:
	 {
	 	KzWeb *kzweb;
		const gchar *uri, *title;
		src_widget = gtk_drag_get_source_widget(context);
		if (!KZ_IS_NOTEBOOK(src_widget))
			break;
		kzweb = KZ_WEB(kz_notebook_get_dragged_page(KZ_NOTEBOOK(src_widget)));
		uri   = kz_web_get_location(kzweb);
		title = kz_web_get_title(kzweb);
		bookmark = KZ_BOOKMARK(kz_bookmark_new_with_attrs(title, uri, NULL));
	 	break;
	 }
	 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_folder_remove(parent, bookmark);
		moved = TRUE;
	 	break;
	 case TARGET_NETSCAPE_URL:
	 case TARGET_TEXT_URI_LIST:
	 	bookmark = kz_bookmark_create_from_selection_data(selection_data);
		break;
	 default:
		break;
	}

	if (bookmark)
	{
		kz_bookmark_folder_append(KZ_BOOKMARK_FOLDER(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);
		}
		g_object_unref(bookmark);
		success = TRUE;
	}
	gtk_drag_finish(context, success, moved, time);
}


