/* -*- 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 1164 2004-07-05 09:17:13Z ikezoe $
 */

#include "kz-bookmark-item.h"

#include <gtk/gtk.h>

#include "gobject-utils.h"
#include "intl.h"
#include "kazehakase.h"
#include "kz-bookmark-menu.h"
#include "kz-bookmark-editor.h"
#include "kz-icons.h"
#include "kz-actions.h"
#include "gtk-utils.h"
#include "kz-input-event-box.h"

enum {
	PROP_0,
	PROP_BOOKMARK,
	PROP_KZ_WINDOW
};

enum {
	TARGET_KAZEHAKASE_BOOKMARKS,
};

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

static const gint dnd_types_num = G_N_ELEMENTS(dnd_types);

static void     kz_bookmark_item_class_init       (KzBookmarkItemClass *klass);
static void     kz_bookmark_item_init             (KzBookmarkItem *menu);

/* object class methods */
static GObject* kz_bookmark_item_constructor      (GType           type,
						   guint           n_props,
						   GObjectConstructParam *props);
static void     kz_bookmark_item_dispose          (GObject        *object);
static void     kz_bookmark_item_set_property     (GObject        *object,
						   guint           prop_id,
						   const GValue   *value,
						   GParamSpec     *pspec);
static void     kz_bookmark_item_get_property     (GObject        *object,
						   guint           prop_id,
						   GValue         *value,
						   GParamSpec     *pspec);
/* widget class methods */
static void     kz_bookmark_item_drag_data_get    (GtkWidget        *widget,
						   GdkDragContext   *context,
						   GtkSelectionData *seldata,
						   guint             info,
						   guint             time);
static void     kz_bookmark_item_drag_begin       (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);
static void     bookmark_weak_ref                 (gpointer data,
						   GObject *obj);

/* 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_menuitem_destroy               (GtkWidget        *widget,
						   KzBookmark       *item);

/* manage life cycle of both menuitems and bookmarks */
static void     connect_observe_signals           (GtkWidget *widget,
						   KzBookmark *item);


static GtkHBoxClass *parent_class = NULL;


KZ_OBJECT_GET_TYPE(kz_bookmark_item, "KzBookmarkItem", KzBookmarkItem,
		   kz_bookmark_item_class_init, kz_bookmark_item_init,
		   GTK_TYPE_HBOX)

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

	parent_class = g_type_class_peek_parent (klass);

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

	/* GObject signals */
	gobject_class->dispose      = kz_bookmark_item_dispose;
	gobject_class->constructor  = kz_bookmark_item_constructor;
	gobject_class->set_property = kz_bookmark_item_set_property;
	gobject_class->get_property = kz_bookmark_item_get_property;

	widget_class->drag_data_get = kz_bookmark_item_drag_data_get;
	widget_class->drag_begin    = kz_bookmark_item_drag_begin;

	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*
kz_bookmark_item_constructor (GType                  type,
			      guint                  n_props,
			      GObjectConstructParam *props)
{
	KzBookmarkItem *menu;
	GtkWidget *button, *image, *eventbox;
	GtkBox *hbox;
	GtkRcStyle *style;
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(parent_class);
	KzBookmarkState state;
	gboolean is_file, is_folder;

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

	hbox = GTK_BOX(object);
	menu = KZ_BOOKMARK_ITEM(object);
	is_file = kz_bookmark_is_file(menu->bookmark);
	is_folder = kz_bookmark_is_folder(menu->bookmark);
	
	gtk_container_set_border_width(GTK_CONTAINER(menu), 2);

	/* button */
	menu->update_button = button = gtk_button_new();
	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	gtk_box_pack_start(hbox, button, FALSE, FALSE, 0);
	if (is_file)
	{
		g_signal_connect(G_OBJECT(button), "clicked", 
				 G_CALLBACK(cb_update_button_clicked),
				 menu);
		menu->image = image
			= gtk_image_new_from_stock(KZ_STOCK_GREEN,
						   KZ_ICON_SIZE_BOOKMARK_MENU);
	}
	else
	{
		g_signal_connect(G_OBJECT(button), "button_release_event",
			 	 G_CALLBACK(cb_bookmark_menu_release), menu);
		if(is_folder)
		{
			menu->image = image
				= gtk_image_new_from_stock(KZ_STOCK_FOLDER,
							   KZ_ICON_SIZE_BOOKMARK_MENU);
		}
		else
		{
			KzFavicon *kzfav;
			GtkWidget *favicon = NULL;

			/* favicon */
			kzfav = kz_favicon_get_instance();
			favicon	= kz_favicon_get_widget(kzfav,
							kz_bookmark_get_link(menu->bookmark),
							KZ_ICON_SIZE_BOOKMARK_MENU);
			if (favicon)
			{
				menu->image = image = favicon;
			}
			else
			{
				menu->image = image
					= gtk_image_new_from_stock(KZ_STOCK_BOOKMARK,
								   KZ_ICON_SIZE_BOOKMARK_MENU);
			}
			g_object_unref(kzfav);
		}
	}
	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);

	g_signal_connect(G_OBJECT(menu->bookmark),
			 "load_start", 
			 G_CALLBACK(cb_bookmark_load_start),
			 menu);
	g_signal_connect(G_OBJECT(menu->bookmark),
			 "load_completed", 
			 G_CALLBACK(cb_bookmark_load_stop),
			 menu);
	g_signal_connect(G_OBJECT(menu->bookmark),
			 "error", 
			 G_CALLBACK(cb_bookmark_load_error),
			 menu);

	state = kz_bookmark_get_state(menu->bookmark);
	if (state == KZ_BOOKMARK_STATE_LOADING)
		cb_bookmark_load_start(menu->bookmark, menu);

	/* label */
	eventbox = kz_input_event_box_new();
	menu->eventbox = eventbox;
	gtk_box_pack_start(hbox, eventbox, FALSE, FALSE, 0);
	g_signal_connect(G_OBJECT(eventbox), "button_release_event",
			 G_CALLBACK(cb_bookmark_menu_release), menu);
	gtk_widget_show (eventbox);

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

	gtk_drag_source_set(GTK_WIDGET(menu),
			    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(menu);

	connect_observe_signals(GTK_WIDGET(menu), menu->bookmark);

	return object;
}


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

	g_return_if_fail (KZ_IS_BOOKMARK_ITEM(object));

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

	menu = KZ_BOOKMARK_ITEM(object);

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

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

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


static void
kz_bookmark_item_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
kz_bookmark_item_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;
	}
}


GtkWidget *
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 = KZ_BOOKMARK_ITEM(g_object_new(KZ_TYPE_BOOKMARK_ITEM,
					     "kz_window", kz,
					     "bookmark",  bookmark,
					     NULL));

	return GTK_WIDGET(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)
		g_object_unref(menu->sub_menu);
	menu->sub_menu = submenu;
	g_object_ref(G_OBJECT(menu->sub_menu));
	gtk_object_sink(GTK_OBJECT(menu->sub_menu));
}


/*****************************************************************************
 *                                                                           *
 *                                Callbacks                                  *
 *                                                                           *
 *****************************************************************************/
static void 
cb_bookmark_load_start (KzBookmark *bookmark, KzBookmarkItem *menu)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	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)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(menu));

	kz_bookmark_item_reset_submenu(menu);

	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(bookmark));
	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 void
bookmark_weak_ref (gpointer data, GObject *obj)
{
	KzBookmark *item = KZ_BOOKMARK(obj);
	GtkWidget *widget = data;

	if (!widget) return;

	g_signal_handlers_disconnect_by_func(GTK_OBJECT(widget),
					     G_CALLBACK(cb_menuitem_destroy),
					     item);
}


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_load_start(menu->bookmark);
}


static void
cb_menuitem_destroy(GtkWidget *widget, KzBookmark *item)
{
	g_signal_handlers_disconnect_by_func(G_OBJECT(item),
					     G_CALLBACK(cb_notify_title),
					     widget);
	g_object_weak_unref(G_OBJECT(item), bookmark_weak_ref, widget);
}


static void
kz_bookmark_item_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, "dummy", strlen("dummy"));
		break;
	}
}


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

	g_return_if_fail(KZ_IS_BOOKMARK_ITEM(widget));

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

	if (kz_bookmark_is_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_drag_source_set_icon_stock(widget, stock_id);
}


static void
connect_observe_signals (GtkWidget *widget, KzBookmark *item)
{
	g_signal_connect(G_OBJECT(item), "notify::title",
			 G_CALLBACK(cb_notify_title), widget);
	g_object_weak_ref(G_OBJECT(item), bookmark_weak_ref, widget);

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