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

/*
 *  Copyright (C) 2003 Hiroyuki Ikezoe
 *
 *  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.
 */

#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "kazehakase.h"
#include "gobject-utils.h"
#include "kz-bookmark-bar.h"
#include "kz-bookmark-item.h"
#include "kz-bookmark.h"
#include "intl.h"
#include "utils.h"

enum {
	PROP_0,
	PROP_KZ_WINDOW,
	PROP_BOOKMARK_LIST
};

enum {
	TARGET_NETSCAPE_URL,
	TARGET_TEXT_URI_LIST
};

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

static guint n_url_drag_types = G_N_ELEMENTS (url_drag_types);

static void     kz_bookmark_bar_class_init   (KzBookmarkBarClass *klass);
static void     kz_bookmark_bar_init         (KzBookmarkBar *kzbkmkbar);
static GObject* kz_bookmark_bar_constructor  (GType           type,
					      guint           n_props,
					      GObjectConstructParam *props);
static void     kz_bookmark_bar_dispose      (GObject *object);
static void     kz_bookmark_bar_set_property (GObject       *object,
					      guint          prop_id,
					      const GValue  *value,
					      GParamSpec    *pspec);
static void     kz_bookmark_bar_get_property (GObject       *object,
					      guint          prop_id,
					      GValue        *value,
					      GParamSpec    *pspec);

static void     kz_bookmark_bar_refresh_all  (KzBookmarkBar *kzbkmkbar);

static void     kz_bookmark_bar_connect_signal    (KzBookmarkBar *bar);
static void     kz_bookmark_bar_disconnect_signal (KzBookmarkBar *bar);

static void     kz_bookmark_bar_drag_data_received (GtkWidget *widget,
						    GdkDragContext *drag_context,
						    gint x, gint y,
						    GtkSelectionData *data,
						    guint info,
						    guint time);

static void     cb_bookmark_list_updated         (KzBookmark *bookmarklist,
						  KzBookmarkBar *bar);
static void     cb_bookmark_list_remove_child    (KzBookmark *bookmarklist,
						  KzBookmark *child,
						  KzBookmarkBar *bar);
static void     cb_bookmark_list_insert_child  	 (KzBookmark *bookmarklist,
						  KzBookmark *child, KzBookmark *sibling,
						  KzBookmarkBar *bar);

static GtkToolbarClass *parent_class = NULL;


KZ_OBJECT_GET_TYPE(kz_bookmark_bar, "KzBookmarkBar", KzBookmarkBar,
		   kz_bookmark_bar_class_init, kz_bookmark_bar_init,
		   GTK_TYPE_TOOLBAR)


static void
kz_bookmark_bar_class_init (KzBookmarkBarClass *klass)
{
	GObjectClass *gobject_class;
	GtkWidgetClass *widget_class;

	parent_class = g_type_class_peek_parent (klass);

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

	/* GObject class */
	gobject_class->constructor  = kz_bookmark_bar_constructor;
	gobject_class->dispose      = kz_bookmark_bar_dispose;
	gobject_class->set_property = kz_bookmark_bar_set_property;
	gobject_class->get_property = kz_bookmark_bar_get_property;

	widget_class->drag_data_received   = kz_bookmark_bar_drag_data_received;

	g_object_class_install_property
		(gobject_class,
		 PROP_KZ_WINDOW,
		 g_param_spec_object ("kz-window",
				      _("KzWindow"),
				      _("The parent kazehakase window"),
				      KZ_TYPE_WINDOW,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property
		(gobject_class,
		 PROP_BOOKMARK_LIST,
		 g_param_spec_object ("bookmark-list",
				      _("BookmarkList"),
				      _("Bookmarks list to show"),
				      KZ_TYPE_BOOKMARK,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT_ONLY));
}


static void
kz_bookmark_bar_init (KzBookmarkBar *bar)
{
	bar->kz           = NULL;
	bar->bookmarklist = NULL;

	gtk_drag_dest_set(GTK_WIDGET(bar), 
			  GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | 
			  GTK_DEST_DEFAULT_DROP,
                          url_drag_types,n_url_drag_types,
			  GDK_ACTION_MOVE);
}


static void
kz_bookmark_bar_dispose (GObject *object)
{
	KzBookmarkBar *bar = KZ_BOOKMARK_BAR(object);

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

	if (bar->bookmarklist)
	{
		kz_bookmark_bar_disconnect_signal(bar);
		g_object_unref(bar->bookmarklist);
		bar->bookmarklist = NULL;
	}

	if (bar->kz)
	{
		g_object_unref(bar->kz);
		bar->kz = NULL;
	}
}


static GObject*
kz_bookmark_bar_constructor (GType                  type,
			     guint                  n_props,
			     GObjectConstructParam *props)
{
	KzBookmarkBar *bar;

	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(parent_class);

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

	bar = KZ_BOOKMARK_BAR(object);

	kz_bookmark_bar_refresh_all(bar);

	return object;
}

static void
kz_bookmark_bar_set_property (GObject         *object,
			      guint            prop_id,
			      const GValue    *value,
			      GParamSpec      *pspec)
{
	KzBookmarkBar *bar = KZ_BOOKMARK_BAR(object);
  
	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		bar->kz = g_object_ref(g_value_get_object(value));
		break;
	case PROP_BOOKMARK_LIST:
		bar->bookmarklist = g_object_ref(g_value_get_object(value));
		kz_bookmark_bar_connect_signal(bar);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


static void
kz_bookmark_bar_get_property (GObject         *object,
			      guint            prop_id,
			      GValue          *value,
			      GParamSpec      *pspec)
{
	KzBookmarkBar *bar = KZ_BOOKMARK_BAR(object);

	switch (prop_id)
	{
	case PROP_KZ_WINDOW:
		g_value_set_object(value, bar->kz);
		break;
	case PROP_BOOKMARK_LIST:
		g_value_set_object(value, bar->bookmarklist);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}


GtkWidget *
kz_bookmark_bar_new (KzWindow *kz)
{
	GObject *obj;
	KzBookmark *bookmarklist = kz_bookmark_list_get_instance();
	
	obj = g_object_new(KZ_TYPE_BOOKMARK_BAR,
			   "kz-window", kz,
			   "bookmark-list", bookmarklist,
			   NULL);

	g_object_unref(bookmarklist);

	return GTK_WIDGET(obj);
}


static void
kz_bookmark_bar_refresh_all (KzBookmarkBar *bar)
{
	GList *children;
	GList *node;

        /*
	 * Taken from Gtk+-2.2.2 (gtk/gtktoolbar.c)
	 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
	 * GtkToolbar copyright (C) Federico Mena
	 */ 
	children = GTK_TOOLBAR(bar)->children;
	for (; children; children = children->next)
	{
		GtkToolbarChild *child;

		child = children->data;

		if (child->type != GTK_TOOLBAR_CHILD_SPACE)
		{
			g_object_ref (child->widget);
			gtk_widget_unparent (child->widget);
			gtk_widget_destroy (child->widget);
			g_object_unref (child->widget);
		}
		
		g_free (child);
	}
	g_list_free (GTK_TOOLBAR(bar)->children);
	GTK_TOOLBAR(bar)->children = NULL;

	children = kz_bookmark_get_children(bar->bookmarklist);
	for (node = children; node; node = g_list_next(node))
	{
		GtkWidget *menu;
		KzBookmark *bookmark = node->data;
		menu = kz_bookmark_item_new(bar->kz, bookmark);
		gtk_toolbar_append_widget(GTK_TOOLBAR(bar),
					  menu, NULL, NULL);
		gtk_widget_show(menu);
	}
	g_list_free(children);

	gtk_widget_queue_resize(GTK_WIDGET(bar));
}


static void
kz_bookmark_bar_connect_signal (KzBookmarkBar *bar)
{
	g_signal_connect(G_OBJECT(bar->bookmarklist), "load_completed",
			 G_CALLBACK(cb_bookmark_list_updated),
			 bar);
	g_signal_connect(G_OBJECT(bar->bookmarklist), "insert-child",
			 G_CALLBACK(cb_bookmark_list_insert_child),
			 bar);
	g_signal_connect(G_OBJECT(bar->bookmarklist), "remove-child",
			 G_CALLBACK(cb_bookmark_list_remove_child),
			 bar);	
}


static void
kz_bookmark_bar_disconnect_signal (KzBookmarkBar *bar)
{
	g_signal_handlers_disconnect_by_func
		(G_OBJECT(bar->bookmarklist),
		 G_CALLBACK(cb_bookmark_list_updated), bar);
	g_signal_handlers_disconnect_by_func
		(G_OBJECT(bar->bookmarklist),
		 G_CALLBACK(cb_bookmark_list_insert_child), bar);
	g_signal_handlers_disconnect_by_func
		(G_OBJECT(bar->bookmarklist),
		 G_CALLBACK(cb_bookmark_list_remove_child), bar);
}


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

	GtkToolbarChild *child = (GtkToolbarChild*)a;
	KzBookmarkItem *menu = KZ_BOOKMARK_ITEM(child->widget);
	bookmark1 = menu->bookmark;

	return bookmark1 - bookmark2;
}


static void
kz_bookmark_bar_drag_data_received (GtkWidget *widget,
				    GdkDragContext *drag_context,
				    gint x, gint y,
				    GtkSelectionData *data,
				    guint info,
				    guint time)
{
	KzBookmarkBar *bar;
	KzBookmark *bookmark;
	gchar *string, **strings;

	g_return_if_fail(KZ_BOOKMARK_BAR(widget));
	bar = KZ_BOOKMARK_BAR(widget);

	switch (info)
	{
	 case TARGET_NETSCAPE_URL:
	 case TARGET_TEXT_URI_LIST:
		if (data->length < 0) return;
		string = g_alloca (data->length + 1);

		memcpy (string, data->data, data->length);
		string[data->length] = '\0';

		strings = g_strsplit(data->data, "\n", 2);

		/* Create New KzBookmark */
		bookmark = kz_bookmark_file_new(strings[0],
						strings[1],
						NULL);
		bookmark->flags |= KZ_BOOKMARK_FOLDER;
		kz_bookmark_append(bar->bookmarklist, bookmark);
		g_strfreev(strings);
		g_object_unref(bookmark);
		kz_bookmark_save(bar->bookmarklist);
		break;
	 default:
		break;
	}
}


static void
cb_bookmark_list_updated (KzBookmark *bookmarklist, KzBookmarkBar *bar)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmarklist));
	g_return_if_fail(KZ_IS_BOOKMARK_BAR(bar));

	kz_bookmark_bar_refresh_all(bar);
}


static void
cb_bookmark_list_insert_child  (KzBookmark *bookmarklist,
				KzBookmark *child, KzBookmark *sibling,
				KzBookmarkBar *bar)
{
	GtkWidget *menu;
	g_return_if_fail(KZ_IS_BOOKMARK(bookmarklist));
	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_BOOKMARK_BAR(bar));

	menu = kz_bookmark_item_new(bar->kz, child);
	gtk_toolbar_append_widget(GTK_TOOLBAR(bar),
				  menu, NULL, NULL);
	gtk_widget_show(menu);
}


static void
cb_bookmark_list_remove_child  (KzBookmark *bookmarklist,
				KzBookmark *child,
				KzBookmarkBar *bar)
{
	GList *list, *node;
	
	g_return_if_fail(KZ_IS_BOOKMARK(bookmarklist));
	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(KZ_IS_BOOKMARK_BAR(bar));

	list = GTK_TOOLBAR(bar)->children;
	node = g_list_find_custom(list, child, find_menu_item);
	g_return_if_fail(node);

	gtk_widget_destroy(((GtkToolbarChild*)node->data)->widget);
}
