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

/*
 *  Copyright (C) 2003-2004 Takuro Ashie
 *  Copyright (C) 2004 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 "kz-thumbnails-view.h"

#include <glib/gi18n.h>
#include "kazehakase.h"
#include "kz-thumbnail.h"
#include "egg-pixbuf-thumbnail.h"
#include "kz-marshalers.h"

typedef struct _KzThumbnailsViewPrivate	KzThumbnailsViewPrivate;
struct _KzThumbnailsViewPrivate
{
	KzBookmarkFolder *folder;
	GList *children;
	KzThumbnailsViewMode mode;
};

#define KZ_THUMBNAILS_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_THUMBNAILS_VIEW, KzThumbnailsViewPrivate))

enum {
	ACTIVATE,
	LAST_SIGNAL
};

#define KZ_THUMBNAIL_BOOKMARK_KEY "KzThumbnail::Bookmark"

static void     dispose       (GObject          *object);
static void 	size_allocate (GtkWidget        *widget,
      			       GtkAllocation    *allocation);

static void 	kz_thumbnails_view_redraw 	 (KzThumbnailsView *view);

static void     connect_bookmark_folder_signals  (KzThumbnailsView *view,
					      	  KzBookmarkFolder *folder);
static void     disconnect_bookmark_folder_signals  (KzThumbnailsView *view,
					      	     KzBookmarkFolder *folder);

static void     insert_bookmark              (KzThumbnailsView *view,
					      KzBookmarkFolder *folder,
				              KzBookmark *child,
					      KzBookmark *sibling);

static gboolean cb_thumbnail_release         (GtkWidget *thumbnail,
					      GdkEventButton *event,
					      KzThumbnailsView *view); 
static gboolean cb_thumbnail_enter_notify    (GtkWidget        *widget,
				    	      GdkEventCrossing *event);
static gboolean cb_thumbnail_leave_notify    (GtkWidget        *widget,
					      GdkEventCrossing *event);

static gint kz_thumbnails_view_signals[LAST_SIGNAL] = {0};

G_DEFINE_TYPE(KzThumbnailsView, kz_thumbnails_view, GTK_TYPE_TABLE)

static void
kz_thumbnails_view_class_init (KzThumbnailsViewClass *klass)
{
	GObjectClass *object_class;
	GtkWidgetClass *widget_class;

	object_class  = G_OBJECT_CLASS(klass);
	widget_class  = GTK_WIDGET_CLASS(klass);

	object_class->dispose  = dispose;

	widget_class->size_allocate = size_allocate;

	klass->activate = NULL;

	kz_thumbnails_view_signals[ACTIVATE]
		= g_signal_new ("activate",
				G_TYPE_FROM_CLASS (klass),
		  		G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
				G_STRUCT_OFFSET (KzThumbnailsViewClass, activate),
				NULL, NULL,
				_kz_marshal_VOID__OBJECT_INT,
				G_TYPE_NONE, 2,
				KZ_TYPE_BOOKMARK, G_TYPE_INT);
	g_type_class_add_private (object_class, sizeof(KzThumbnailsViewPrivate));
}


static void
kz_thumbnails_view_init (KzThumbnailsView *view)
{
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE (view);

	priv->folder   = NULL;
	priv->children = NULL;
	priv->mode     = KZ_THUMBNAILS_VIEW_VERTICAL;

	gtk_table_set_homogeneous(GTK_TABLE(view), FALSE);
	gtk_table_set_col_spacings(GTK_TABLE(view), 4);
	gtk_table_set_row_spacings(GTK_TABLE(view), 4);
}


static void
dispose (GObject *object)
{
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(object);
	KzThumbnailsView *view = KZ_THUMBNAILS_VIEW(object);

	if (priv->folder)
	{
		disconnect_bookmark_folder_signals(view, priv->folder);
		g_object_unref(priv->folder);
		priv->folder = NULL;
	}

	if (G_OBJECT_CLASS(kz_thumbnails_view_parent_class)->dispose)
		G_OBJECT_CLASS (kz_thumbnails_view_parent_class)->dispose(object);
}


static void 	
size_allocate (GtkWidget *widget,
               GtkAllocation *allocation)
{
	GtkAllocation old_allocation;
	gint old_width;
	KzThumbnailsView *view;
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(widget);
	
	view = KZ_THUMBNAILS_VIEW(widget);

	old_allocation = widget->allocation;
	old_width = old_allocation.width;

  	GTK_WIDGET_CLASS(kz_thumbnails_view_parent_class)->size_allocate(widget, allocation);

	if (priv->mode == KZ_THUMBNAILS_VIEW_PLANE &&
	    old_width != allocation->width)
	{
		kz_thumbnails_view_redraw(view);
	}
}

GtkWidget *
kz_thumbnails_view_new (void)
{
	KzThumbnailsView *view;
	view = g_object_new(KZ_TYPE_THUMBNAILS_VIEW,
		            NULL);

	return GTK_WIDGET(view);
}


static void
kz_thumbnails_view_set_thumbnail_at_pos (KzThumbnailsView *view,
					 GtkWidget *thumbnail,
					 gint pos)
{
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE (view);
	switch (priv->mode)
	{
	case KZ_THUMBNAILS_VIEW_PLANE:
	{
		gint xnum, xpos, ypos;
		gint width;
  		width = GTK_WIDGET(view)->allocation.width;

		if (width < EGG_PIXBUF_THUMBNAIL_NORMAL)
			xnum = 1;
		else
			xnum = (width / EGG_PIXBUF_THUMBNAIL_NORMAL);
		ypos = pos / xnum;
		xpos = pos % xnum;	
		gtk_table_attach(GTK_TABLE(view),
				 thumbnail,
				 xpos, xpos + 1,
			 	 ypos, ypos + 1,
				 GTK_SHRINK, GTK_SHRINK,
				 4, 4);
		break;
	}
	case KZ_THUMBNAILS_VIEW_HORIZONTAL:
		gtk_table_attach(GTK_TABLE(view),
			 	 thumbnail,
				 pos, pos + 1,
			 	 0, 1,
				 GTK_SHRINK, GTK_SHRINK,
				 4, 4);
		break;
	case KZ_THUMBNAILS_VIEW_VERTICAL:
	default:
		gtk_table_attach(GTK_TABLE(view),
				 thumbnail,
				 0, 1,
			 	 pos, pos + 1,
				 GTK_SHRINK, GTK_SHRINK,
				 4, 4);
		break;
	}
}


static void 
kz_thumbnails_view_redraw (KzThumbnailsView *view)
{
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE (view);
	GList *node;
	gint i = 0;

	for (node = priv->children; node; node = g_list_next(node))
	{
		GtkWidget *thumb = node->data;

		g_object_ref(thumb);
		gtk_container_remove(GTK_CONTAINER(view), thumb);
		kz_thumbnails_view_set_thumbnail_at_pos(view, thumb, i);		
		g_object_unref(thumb);
		i++;
	}
}


static void
destroy_child_widget (gpointer data, gpointer user_data)
{
	if (GTK_IS_WIDGET(data))
		gtk_widget_destroy(data);
}

static void
each_bookmark (KzBookmark *bookmark, KzThumbnailsView *view)
{
	KzThumbnailsViewPrivate *priv;
	priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(view);

	insert_bookmark(view, priv->folder, bookmark, NULL);
}

void
kz_thumbnails_view_set_folder (KzThumbnailsView *view,
			       KzBookmarkFolder *folder)
{
	KzThumbnailsViewPrivate *priv;
	
	g_return_if_fail(KZ_IS_THUMBNAILS_VIEW(view));
	g_return_if_fail(!folder || kz_bookmark_is_folder(folder));

	priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(view);

	if (priv->folder)
	{
		disconnect_bookmark_folder_signals(view, priv->folder);
		g_object_unref(priv->folder);
		priv->folder = NULL;

		/* remove old widgets */
		g_list_foreach(priv->children,
			       destroy_child_widget, NULL);
		g_list_free(priv->children);
	}

	if (!folder) return;

	priv->folder = g_object_ref(folder);
	connect_bookmark_folder_signals(view, priv->folder);

	kz_bookmark_folder_foreach_child(folder,
					 (GFunc)each_bookmark, view);
}


void	     
kz_thumbnails_view_set_mode (KzThumbnailsView *view,
		             KzThumbnailsViewMode mode)
{
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE (view);
	if (priv->mode == mode)
		return;

	priv->mode = mode;

	/* redraw */
	kz_thumbnails_view_redraw(view);
}


static GtkWidget *
find_thumbnail_widget (KzThumbnailsView *view, KzBookmark *bookmark)
{
	GtkWidget *widget;
	gint index;
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE (view);
	
	index = kz_bookmark_folder_get_child_index(KZ_BOOKMARK_FOLDER(priv->folder), bookmark);

	widget = g_list_nth_data(priv->children, index);

	return widget;
}

static void
thumbnail_set_bookmark_property (GtkWidget *thumbnail, KzBookmark *bookmark)
{
	const gchar *uri, *desc;
	
	uri = kz_bookmark_get_link(bookmark);
	if (!uri)
		uri = "about:blank";

	kz_thumbnail_set_thumbnail_from_uri(KZ_THUMBNAIL(thumbnail), uri);
	
	desc = kz_bookmark_get_description(bookmark);
	if (desc)
		gtk_widget_set_tooltip_text(thumbnail, desc);
}


static void
cb_bookmark_notify (GObject *object, GParamSpec *pspec,
		    KzThumbnailsView *view)
{
	KzBookmark *bookmark;
	const gchar *prop;
        GValue value = { 0 };

	g_return_if_fail(KZ_IS_BOOKMARK(object));

	bookmark = KZ_BOOKMARK(object);

	prop = g_param_spec_get_name(pspec);
	g_return_if_fail(prop);

        g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
        g_object_get_property(object, prop, &value);

	if (!strcmp(prop, "link"))
	{
		GtkWidget *thumbnail;

		thumbnail = find_thumbnail_widget(view, bookmark);
		if (!thumbnail) return;

		thumbnail_set_bookmark_property(thumbnail, bookmark);
	}
	g_value_unset(&value);
}


static void
insert_bookmark (KzThumbnailsView *view,
		 KzBookmarkFolder *folder,
		 KzBookmark *child,
		 KzBookmark *sibling)
{
	GtkWidget *thumbnail;
	gint pos = -1;
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(view);

	thumbnail = kz_thumbnail_new();
	thumbnail_set_bookmark_property(thumbnail, child);

	g_object_set_data(G_OBJECT(thumbnail),
			  KZ_THUMBNAIL_BOOKMARK_KEY,
			  child);
	g_signal_connect(thumbnail, "button_release_event",
			 G_CALLBACK(cb_thumbnail_release), view);
	g_signal_connect(thumbnail, "enter-notify-event",
			 G_CALLBACK(cb_thumbnail_enter_notify), NULL);
	g_signal_connect(thumbnail, "leave-notify-event",
			 G_CALLBACK(cb_thumbnail_leave_notify), NULL);

	if (sibling)
		pos = kz_bookmark_folder_get_child_index(folder, sibling);

	if (pos >= 0)
	{
		GList *node, *pos_node;
		gint i = pos + 1;
		pos_node = g_list_nth(priv->children, pos);

		for (node = pos_node; node; node = g_list_next(node))
		{
			GtkWidget *thumb = node->data;

			g_object_ref(thumb);
			gtk_container_remove(GTK_CONTAINER(view), thumb);

			kz_thumbnails_view_set_thumbnail_at_pos(view, thumb, i);		
			g_object_unref(thumb);
			i++;
		}
	}
	else
	{
		pos = g_list_length(priv->children);
	}
	priv->children = g_list_insert(priv->children,
				       thumbnail,
				       pos);

	gtk_widget_show(thumbnail);

	kz_thumbnails_view_set_thumbnail_at_pos(view, thumbnail, pos);		
	g_signal_connect(child, "notify",
			 G_CALLBACK(cb_bookmark_notify), view);
}


static void
cb_bookmark_folder_insert_child (KzBookmarkFolder *folder,
			  	 KzBookmark *child,
				 KzBookmark *sibling,
				 KzThumbnailsView *view)
{
	insert_bookmark(view, folder, child, sibling);
}


static void
cb_bookmark_folder_remove_child (KzBookmarkFolder *folder,
				 KzBookmark *child,
			  	 KzThumbnailsView *view)
{
	GtkWidget *thumbnail;
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(view);

	g_signal_handlers_disconnect_by_func
		(child,
		 G_CALLBACK(cb_bookmark_notify), view);

	thumbnail = find_thumbnail_widget(view, child);

	if (thumbnail)
	{
		gtk_widget_destroy(thumbnail);
		priv->children = g_list_remove(priv->children, thumbnail);
	}
}


static void
connect_bookmark_folder_signals (KzThumbnailsView *view,
			  	 KzBookmarkFolder *folder)
{
	g_return_if_fail(KZ_IS_BOOKMARK_FOLDER(folder));

	g_signal_connect(folder, "insert-child",
			 G_CALLBACK(cb_bookmark_folder_insert_child), view);
	g_signal_connect(folder, "remove-child",
			 G_CALLBACK(cb_bookmark_folder_remove_child), view);
}


static void
disconnect_bookmark_folder_signals (KzThumbnailsView *view,
			     	    KzBookmarkFolder *folder)
{
	g_return_if_fail(KZ_IS_BOOKMARK_FOLDER(folder));

	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_bookmark_folder_insert_child), view);
	g_signal_handlers_disconnect_by_func
		(folder,
		 G_CALLBACK(cb_bookmark_folder_remove_child), view);
}


static gboolean
cb_thumbnail_release (GtkWidget *thumbnail, GdkEventButton *event,
		      KzThumbnailsView *view)
{
	KzBookmark *bookmark;
	gint index;
	KzThumbnailsViewPrivate *priv = KZ_THUMBNAILS_VIEW_GET_PRIVATE(view);
	
	if (event->button == 3)
		return TRUE;

	bookmark = g_object_get_data(G_OBJECT(thumbnail),
				     KZ_THUMBNAIL_BOOKMARK_KEY);
	index = kz_bookmark_folder_get_child_index(KZ_BOOKMARK_FOLDER(priv->folder), bookmark);

	g_signal_emit(view,
		      kz_thumbnails_view_signals[ACTIVATE],
		      0,
		      bookmark, index);

	return FALSE;
}

static gboolean
cb_thumbnail_enter_notify (GtkWidget *widget,
			   GdkEventCrossing *event)
{
 	gtk_widget_set_state(widget, GTK_STATE_PRELIGHT);
	return FALSE;
}


static gboolean
cb_thumbnail_leave_notify (GtkWidget *widget,
			   GdkEventCrossing *event)
{
	return FALSE;	
}
