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

/*
 *  Copyright (C) 2003 Hiroyuki Ikezoe
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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.c 2411 2006-01-15 04:52:25Z kous $
 */

#include "kz-bookmark.h"

#include <string.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include "gobject-utils.h"
#include "glib-utils.h"
#include "kz-marshalers.h"
#include "utils.h"
#include "eggregex.h"
#include "kz-bookmark-file.h"
#include "kz-bookmarks-enum-types.h"

enum {
	INSERT_CHILD_SIGNAL,
	REMOVE_CHILD_SIGNAL,
	MOVE_CHILD_SIGNAL,
	CHILDREN_REORDERED_SIGNAL,
	LAST_SIGNAL
};

enum {
	PROP_0,
	PROP_TYPE,
	PROP_TITLE,
	PROP_DOC_TITLE,
	PROP_ID,
	PROP_LINK,
	PROP_DESCRIPTION,
	PROP_LAST_MODIFIED,
	PROP_LAST_VISITED,
	PROP_ADDED_TIME,
	PROP_INTERVAL,
	PROP_CURRENT_POS,
	PROP_LOCK,
	PROP_AUTO_REFRESH,
	PROP_JAVASCRIPT
};


static void kz_bookmark_class_init   (KzBookmarkClass *klass);
static void kz_bookmark_init         (KzBookmark      *bookmark);
static void kz_bookmark_dispose      (GObject         *object);
static void kz_bookmark_set_property (GObject         *object,
				      guint            prop_id,
				      const GValue    *value,
				      GParamSpec      *pspec);
static void kz_bookmark_get_property (GObject         *object,
				      guint            prop_id,
				      GValue          *value,
				      GParamSpec      *pspec);

static void kz_bookmark_real_insert_child (KzBookmark *bookmark,
					   KzBookmark *child,
					   KzBookmark *sibling);
static void kz_bookmark_real_remove_child (KzBookmark *bookmark,
					   KzBookmark *child);

static GObjectClass *parent_class = NULL;
static gint kz_bookmark_signals[LAST_SIGNAL] = {0};

static GQuark id_quark                = 0;
static GQuark title_quark             = 0;
static GQuark doc_title_quark         = 0;
static GQuark link_quark              = 0;
static GQuark description_quark       = 0;
static GQuark last_mod_quark          = 0;
static GQuark last_visited_quark      = 0;
static GQuark added_time_quark        = 0;
static GQuark parent_quark            = 0;
static GQuark children_quark          = 0;
static GQuark current_pos_quark       = 0;
static GQuark lock_quark              = 0;
static GQuark auto_refresh_quark      = 0;
static GQuark javascript_quark        = 0;


KZ_OBJECT_GET_TYPE(kz_bookmark, "KzBookmark", KzBookmark,
		   kz_bookmark_class_init, kz_bookmark_init,
		   G_TYPE_OBJECT)


static void
kz_bookmark_class_init (KzBookmarkClass *klass)
{
	GObjectClass *object_class;

	parent_class = g_type_class_peek_parent (klass);
	object_class = (GObjectClass *) klass;

	object_class->dispose      = kz_bookmark_dispose;
	object_class->set_property = kz_bookmark_set_property;
	object_class->get_property = kz_bookmark_get_property;

	klass->insert_child       = kz_bookmark_real_insert_child;
	klass->remove_child       = kz_bookmark_real_remove_child;
	klass->move_child         = NULL; /* kz_bookmark_real_move_child; */
	klass->children_reordered = NULL;

	g_object_class_install_property(
		object_class,
		PROP_TYPE,
		g_param_spec_enum (
			"type",
			_("Type"),
			_("Type of the bookmark"),
			KZ_TYPE_BOOKMARK_TYPE,
			KZ_BOOKMARK_NORMAL,
			G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_ID,
		 g_param_spec_string(
			 "id",
			 _("ID"),
			 _("The ID of the bookmark"),
			 NULL,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_TITLE,
		 g_param_spec_string(
			 "title",
			 _("Title"),
			 _("The title of the bookmark"),
			 NULL,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_DOC_TITLE,
		 g_param_spec_string(
			 "document-title",
			 _("Original document title"),
			 _("The original document title of the link"),
			 NULL,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_LINK,
		 g_param_spec_string(
			 "link",
			 _("Link"),
			 _("The URI of the link"),
			 NULL,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_DESCRIPTION,
		 g_param_spec_string(
			 "description",
			 _("Description"),
			 _("The description of the bookmark"),
			 NULL,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_LAST_MODIFIED,
		 g_param_spec_uint(
			 "last-modified",
			 _("Last Modified"),
			 _("Last modification time of the link"),
			 0,
			 G_MAXUINT,
			 0,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_LAST_VISITED,
		 g_param_spec_uint(
			 "last-visited",
			 _("Last Visited Time"),
			 _("The time of the user's last visit to the link"),
			 0,
			 G_MAXUINT,
			 0,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_ADDED_TIME,
		 g_param_spec_uint(
			 "added-time",
			 _("Added Time"),
			 _("The time of the added the bookmark"),
			 0,
			 G_MAXUINT,
			 0,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		 PROP_CURRENT_POS,
		 g_param_spec_uint(
			 "current",
			 _("Current Position"),
			 _("Current position in history"),
			 0,
			 G_MAXUINT,
			 0,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		PROP_LOCK,
		g_param_spec_boolean(
			"lock",
			_("Lock state"),
			_("Lock state for tab"),
			FALSE,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		PROP_AUTO_REFRESH,
		g_param_spec_boolean(
			 "auto-refresh",
			 _("Auto Refresh state"),
			 _("Auto Refresh state for tab"),
			 FALSE,
			 G_PARAM_READWRITE));

	g_object_class_install_property(
		object_class,
		PROP_JAVASCRIPT,
		g_param_spec_boolean(
			 "javascript",
			 _("Javascript state"),
			 _("Javascript state for tab"),
			 FALSE,
			 G_PARAM_READWRITE));

	kz_bookmark_signals[INSERT_CHILD_SIGNAL]
		= g_signal_new ("insert-child",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzBookmarkClass, insert_child),
				NULL, NULL,
				_kz_marshal_VOID__OBJECT_OBJECT,
				G_TYPE_NONE, 2,
				KZ_TYPE_BOOKMARK, KZ_TYPE_BOOKMARK);

	kz_bookmark_signals[REMOVE_CHILD_SIGNAL]
		= g_signal_new ("remove-child",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzBookmarkClass, remove_child),
				NULL, NULL,
				g_cclosure_marshal_VOID__OBJECT,
				G_TYPE_NONE, 1,
				KZ_TYPE_BOOKMARK);

	kz_bookmark_signals[MOVE_CHILD_SIGNAL]
		= g_signal_new ("move-child",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_LAST,
				G_STRUCT_OFFSET (KzBookmarkClass, move_child),
				NULL, NULL,
				_kz_marshal_VOID__OBJECT_OBJECT,
				G_TYPE_NONE, 2,
				KZ_TYPE_BOOKMARK, KZ_TYPE_BOOKMARK);

	kz_bookmark_signals[CHILDREN_REORDERED_SIGNAL]
		= g_signal_new ("children-reordered",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST,
				G_STRUCT_OFFSET (KzBookmarkClass,
						 children_reordered),
				NULL, NULL,
				g_cclosure_marshal_VOID__VOID,
				G_TYPE_NONE, 0);

	/* FIXME! add other properties */
	/*
	 * candidates:
	 * 
	 *   * ID
	 *   * Last-Accessed time
	 *   * frequency
	 *   * ......
	 */

	id_quark           = g_quark_from_string("KzBookmark::ID");
	title_quark        = g_quark_from_string("KzBookmark::Title");
	doc_title_quark    = g_quark_from_string("KzBookmark::DocumentTitle");
	link_quark         = g_quark_from_string("KzBookmark::Link");
	description_quark  = g_quark_from_string("KzBookmark::Description");
	last_mod_quark     = g_quark_from_string("KzBookmark::LastModified");
	last_visited_quark = g_quark_from_string("KzBookmark::LastVisited");
	added_time_quark   = g_quark_from_string("KzBookmark::AddedTime");
	parent_quark       = g_quark_from_string("KzBookmark::Parent");
	children_quark     = g_quark_from_string("KzBookmark::Children");
	current_pos_quark  = g_quark_from_string("KzBookmark::Current");
	lock_quark         = g_quark_from_string("KzBookmark::Lock");
	auto_refresh_quark = g_quark_from_string("KzBookmark::AutoRefresh");
	javascript_quark   = g_quark_from_string("KzBookmark::Javascript");
}

static void
kz_bookmark_init (KzBookmark *bookmark)
{
}


static void
kz_bookmark_dispose (GObject *object)
{
	KzBookmark *bookmark;
	
	bookmark = KZ_BOOKMARK(object);

	kz_bookmark_remove_all(bookmark);

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


#define CHANGE_STR(obj, quark, value) \
{ \
	g_object_set_qdata_full((obj), (quark), (value), \
				(GDestroyNotify) g_free); \
}

static void
kz_bookmark_set_property (GObject *object,
			  guint prop_id,
			  const GValue *value,
			  GParamSpec *pspec)
{
	KzBookmark *bookmark = KZ_BOOKMARK(object);

	switch (prop_id) {
	case PROP_TYPE:
		bookmark->type = g_value_get_enum(value);
		break;
	case PROP_ID:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		CHANGE_STR(object, id_quark, g_value_dup_string(value));
		break;
	case PROP_TITLE:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		CHANGE_STR(object, title_quark, g_value_dup_string(value));
		break;
	case PROP_DOC_TITLE:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		CHANGE_STR(object, doc_title_quark, g_value_dup_string(value));
		break;
	case PROP_LINK:
		g_return_if_fail(!kz_bookmark_is_pure_folder(bookmark));
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		CHANGE_STR(object, link_quark, g_value_dup_string(value));
		break;
	case PROP_DESCRIPTION:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		CHANGE_STR(object, description_quark,
			   g_value_dup_string(value));
		break;
	case PROP_LAST_MODIFIED:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		g_object_set_qdata(object, last_mod_quark,
				   GUINT_TO_POINTER(g_value_get_uint(value)));
		break;
	case PROP_LAST_VISITED:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		g_object_set_qdata(object, last_visited_quark,
				   GUINT_TO_POINTER(g_value_get_uint(value)));
		break;
	case PROP_ADDED_TIME:
		g_return_if_fail(!kz_bookmark_is_separator(bookmark));
		g_object_set_qdata(object, added_time_quark,
				   GUINT_TO_POINTER(g_value_get_uint(value)));
		break;
	case PROP_CURRENT_POS:
		g_return_if_fail(kz_bookmark_is_folder(bookmark));
		g_object_set_qdata(object, current_pos_quark,
				   GUINT_TO_POINTER(g_value_get_uint(value)));
		break;
	case PROP_LOCK:
		g_return_if_fail(kz_bookmark_is_folder(bookmark));
		g_object_set_qdata(object, lock_quark,
				   GINT_TO_POINTER(g_value_get_boolean(value)));
		break;
	case PROP_AUTO_REFRESH:
		g_return_if_fail(kz_bookmark_is_folder(bookmark));
		g_object_set_qdata(object, auto_refresh_quark,
				   GINT_TO_POINTER(g_value_get_boolean(value)));
		break;
	case PROP_JAVASCRIPT:
		g_return_if_fail(kz_bookmark_is_folder(bookmark));
		g_object_set_qdata(object, javascript_quark,
				   GINT_TO_POINTER(g_value_get_boolean(value)));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


static void
kz_bookmark_get_property (GObject *object,
		     guint prop_id,
		     GValue *value,
		     GParamSpec *pspec)
{
	KzBookmark *bookmark = KZ_BOOKMARK(object);
	gchar *str;
	guint time;

	switch (prop_id) {
	case PROP_TYPE:
		g_value_set_enum(value, bookmark->type);
		break;
	case PROP_ID:
		str = g_object_get_qdata(object, id_quark);
		g_value_set_string(value, str);
		break;
	case PROP_TITLE:
		str = g_object_get_qdata(object, title_quark);
		g_value_set_string(value, str);
		break;
	case PROP_DOC_TITLE:
		str = g_object_get_qdata(object, doc_title_quark);
		g_value_set_string(value, str);
		break;
	case PROP_LINK:
		str = g_object_get_qdata(object, link_quark);
		g_value_set_string(value, str);
		break;
	case PROP_DESCRIPTION:
		str = g_object_get_qdata(object, description_quark);
		g_value_set_string(value, str);
		break;
	case PROP_LAST_MODIFIED:
		time = GPOINTER_TO_UINT(g_object_get_qdata(object,
							   last_mod_quark));
		g_value_set_uint(value, time);
		break;
	case PROP_LAST_VISITED:
		time = GPOINTER_TO_UINT(g_object_get_qdata(object,
							   last_visited_quark));
		g_value_set_uint(value, time);
		break;
	case PROP_ADDED_TIME:
		time = GPOINTER_TO_UINT(g_object_get_qdata(object,
							   added_time_quark));
		g_value_set_uint(value, time);
		break;
	case PROP_CURRENT_POS:
		time = GPOINTER_TO_UINT(g_object_get_qdata(object,
							   current_pos_quark));
		g_value_set_uint(value, time);
		break;
	case PROP_LOCK:
		time = GPOINTER_TO_INT(g_object_get_qdata(object,
							  lock_quark));
		g_value_set_boolean(value, time);
		break;
	case PROP_AUTO_REFRESH:
		time = GPOINTER_TO_INT(g_object_get_qdata(object,
							  auto_refresh_quark));
		g_value_set_boolean(value, time);
		break;
	case PROP_JAVASCRIPT:
		time = GPOINTER_TO_INT(g_object_get_qdata(object,
							  javascript_quark));
		g_value_set_boolean(value, time);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}


KzBookmark *
kz_bookmark_new (void)
{
	KzBookmark *bookmark = g_object_new(KZ_TYPE_BOOKMARK,
					    NULL);
	return bookmark;
}


KzBookmark *
kz_bookmark_pure_folder_new (void)
{
	KzBookmark *bookmark;

	bookmark = g_object_new(KZ_TYPE_BOOKMARK,
			        "type", KZ_BOOKMARK_PURE_FOLDER,
			        NULL);
	return bookmark;
}


KzBookmark *
kz_bookmark_separator_new (void)
{
	KzBookmark *bookmark;

	bookmark = g_object_new(KZ_TYPE_BOOKMARK,
			        "title", "-----",
			        "type",  KZ_BOOKMARK_SEPARATOR,
				NULL);
	return bookmark;
}


KzBookmark *
kz_bookmark_new_with_attrs (const gchar *title,
			    const gchar *uri,
			    const gchar *description)
{
	KzBookmark *bookmark;

	bookmark = g_object_new(KZ_TYPE_BOOKMARK,
			        "title",       title,
				"link",        uri,
				"description", description,
				NULL);
	return bookmark;
}


const gchar *
kz_bookmark_get_id (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), id_quark);
}


const gchar *
kz_bookmark_get_title (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), title_quark);
}


const gchar *
kz_bookmark_get_document_title (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), doc_title_quark);
}


const gchar *
kz_bookmark_get_link (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), link_quark);
}


const gchar *
kz_bookmark_get_description (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);
	return g_object_get_qdata(G_OBJECT(bookmark), description_quark);
}


guint
kz_bookmark_get_last_modified (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), 0);
	return GPOINTER_TO_UINT(g_object_get_qdata(G_OBJECT(bookmark),
						   last_mod_quark));
}

guint
kz_bookmark_get_last_visited (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), 0);
	return GPOINTER_TO_UINT(g_object_get_qdata(G_OBJECT(bookmark),
						   last_visited_quark));
}

guint
kz_bookmark_get_added_time (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), 0);
	return GPOINTER_TO_UINT(g_object_get_qdata(G_OBJECT(bookmark),
						   added_time_quark));
}


gboolean
kz_bookmark_is_editable (KzBookmark *bookmark)
{
	KzBookmark *parent_file;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	parent_file = kz_bookmark_get_parent_file(bookmark);

	if (!parent_file) return FALSE;

	if (!kz_bookmark_file_is_editable(KZ_BOOKMARK_FILE(parent_file)))
		return FALSE;

	return TRUE;
}


void
kz_bookmark_set_id (KzBookmark *bookmark,
		    const gchar *id)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "id", id, NULL);
}


void
kz_bookmark_set_title (KzBookmark *bookmark,
		       const gchar *title)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "title", title, NULL);
}


void
kz_bookmark_set_document_title (KzBookmark *bookmark,
				const gchar *doc_title)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "document-title", doc_title, NULL);
}


void
kz_bookmark_set_link (KzBookmark *bookmark,
		      const gchar *uri)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "link", uri, NULL);
}


void
kz_bookmark_set_description (KzBookmark *bookmark,
			     const gchar *description)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "description", description, NULL);
}


void
kz_bookmark_set_last_modified (KzBookmark *bookmark,
			       guint time)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "last-modified", time, NULL);
}

void
kz_bookmark_set_last_visited (KzBookmark *bookmark,
			      guint time)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "last-visited", time, NULL);
}

void
kz_bookmark_set_added_time (KzBookmark *bookmark,
		            guint time)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_object_set(G_OBJECT(bookmark), "added-time", time, NULL);
}

gboolean
kz_bookmark_is_separator (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	if (bookmark->type == KZ_BOOKMARK_SEPARATOR)
		return TRUE;
	else
		return FALSE;
}


gboolean
kz_bookmark_is_folder(KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	if (bookmark->type == KZ_BOOKMARK_FOLDER ||
	    bookmark->type == KZ_BOOKMARK_PURE_FOLDER)
	{
		return TRUE;
	}

	return FALSE;
}


gboolean
kz_bookmark_is_pure_folder (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	if (bookmark->type == KZ_BOOKMARK_PURE_FOLDER)
		return TRUE;
	else
		return FALSE;
}


gboolean
kz_bookmark_get_folded (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	if (!kz_bookmark_is_folder(bookmark)) return TRUE;

	return bookmark->flags & KZ_BOOKMARK_FOLDED_FLAG;
}


void
kz_bookmark_set_folded (KzBookmark  *bookmark, gboolean folded)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	if (folded)
	{
		bookmark->flags |= KZ_BOOKMARK_FOLDED_FLAG;
	}
	else
	{
		bookmark->flags &= ~KZ_BOOKMARK_FOLDED_FLAG;
	}
}


static void
kz_bookmark_real_insert_child (KzBookmark *bookmark,
			       KzBookmark *child,
			       KzBookmark *sibling)
{
	GList *list, *next = NULL;

	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(!sibling || KZ_IS_BOOKMARK(sibling));
	g_return_if_fail(kz_bookmark_is_folder(bookmark));

	list = g_object_get_qdata(G_OBJECT(bookmark), children_quark);
	if (sibling)
		next = g_list_find(list, sibling);

	g_object_ref(child);
	list = g_list_insert_before(list, next, child);
	g_object_set_qdata(G_OBJECT(bookmark), children_quark, list);

	g_object_set_qdata(G_OBJECT(child), parent_quark, bookmark);
}


void
kz_bookmark_insert_before (KzBookmark *bookmark,
			   KzBookmark *child,
			   KzBookmark *sibling)
{
	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	g_signal_emit(bookmark,
		      kz_bookmark_signals[INSERT_CHILD_SIGNAL], 0,
		      child, sibling);
}


void
kz_bookmark_append (KzBookmark *bookmark, KzBookmark *child)
{
	kz_bookmark_insert_before(bookmark, child, NULL);
}


void
kz_bookmark_prepend (KzBookmark *bookmark, KzBookmark *child)
{
	GList *list;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));

	list = g_object_get_qdata(G_OBJECT(bookmark), children_quark);
	if (list)
		kz_bookmark_insert_before(bookmark, child, list->data);
	else
		kz_bookmark_insert_before(bookmark, child, NULL);		
}


static void
kz_bookmark_real_remove_child (KzBookmark *bookmark, KzBookmark *child)
{
	GList *list;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_return_if_fail(KZ_IS_BOOKMARK(child));
	g_return_if_fail(kz_bookmark_is_folder(bookmark));

	list = g_object_get_qdata(G_OBJECT(bookmark), children_quark);
	if (!g_list_find(list, child)) return;

	list = g_list_remove(list, child);
	g_object_unref(child);
	g_object_set_qdata(G_OBJECT(bookmark), children_quark, list);

	g_object_set_qdata(G_OBJECT(child), parent_quark, NULL);	
}


void
kz_bookmark_remove (KzBookmark *bookmark, KzBookmark *child)
{
	g_signal_emit(bookmark,
		      kz_bookmark_signals[REMOVE_CHILD_SIGNAL], 0,
		      child);
}


void
kz_bookmark_remove_all (KzBookmark *bookmark)
{
	GList *children, *node, *prev;

	children = g_object_get_qdata(G_OBJECT(bookmark), children_quark);
	children = g_list_copy(children);
	children = g_list_last(children);

	node = children;
	while (node)
	{
		KzBookmark *child = node->data;
		prev = g_list_previous(node);
		kz_bookmark_remove (bookmark, child);
		node = prev;
	}

	g_list_free(children);
}


KzBookmark *
kz_bookmark_get_parent (KzBookmark *bookmark)
{
	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	return g_object_get_qdata(G_OBJECT(bookmark), parent_quark);
}


KzBookmark *
kz_bookmark_get_parent_file (KzBookmark *bookmark)
{
	KzBookmark *parent = bookmark;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	do
	{
		parent = kz_bookmark_get_parent(parent);
		if (KZ_IS_BOOKMARK(parent) && KZ_IS_BOOKMARK_FILE(parent))
			return parent;
	} while (parent);

	return NULL;
}


GList *
kz_bookmark_get_children (KzBookmark *bookmark)
{
	GList *list;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	list =  g_object_get_qdata(G_OBJECT(bookmark), children_quark);

	if (list)
		return g_list_copy(list);

	return NULL;
}


gboolean
kz_bookmark_has_children (KzBookmark  *bookmark)
{
	GList *list;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), FALSE);

	list =  g_object_get_qdata(G_OBJECT(bookmark), children_quark);

	if (list)
		return TRUE;
	else
		return FALSE;
}


KzBookmark *
kz_bookmark_next (KzBookmark *bookmark)
{
	KzBookmark *parent;
	GList *list, *node;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	parent = kz_bookmark_get_parent(bookmark);
	if (!parent) return NULL;

	list = g_object_get_qdata(G_OBJECT(parent), children_quark);
	node = g_list_find(list, bookmark);
	if (node && (node = g_list_next(node)))
		return node->data;

	return NULL;
}


KzBookmark *
kz_bookmark_prev (KzBookmark  *bookmark)
{
	KzBookmark *parent;
	GList *list, *node;

	g_return_val_if_fail(KZ_IS_BOOKMARK(bookmark), NULL);

	parent = kz_bookmark_get_parent(bookmark);
	if (!parent) return NULL;

	list = g_object_get_qdata(G_OBJECT(parent), children_quark);
	node = g_list_find(list, bookmark);
	if (node && (node = g_list_previous(node)))
		return node->data;

	return NULL;
}

/* return current activate tab position */
guint
kz_bookmark_get_current (KzBookmark *bookmark)
{
	if (!kz_bookmark_is_folder(bookmark)) return 0;

	return GPOINTER_TO_UINT(g_object_get_qdata(G_OBJECT(bookmark),
						   current_pos_quark));
}
void
kz_bookmark_set_current (KzBookmark  *bookmark,
			 guint pos)
{
	if (!kz_bookmark_is_folder(bookmark)) return;

	g_object_set(G_OBJECT(bookmark), "current", pos, NULL);
}

gboolean
kz_bookmark_get_lock (KzBookmark  *bookmark)
{
	if (!kz_bookmark_is_folder(bookmark)) return FALSE;

	return GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(bookmark),
						  lock_quark));
}

void
kz_bookmark_set_lock (KzBookmark  *bookmark,
		      gboolean lock)
{
	if (!kz_bookmark_is_folder(bookmark)) return;
	
	g_object_set(G_OBJECT(bookmark), "lock", lock, NULL);
}

gboolean
kz_bookmark_get_auto_refresh (KzBookmark  *bookmark)
{
	if (!kz_bookmark_is_folder(bookmark)) return FALSE;

	return GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(bookmark),
						  auto_refresh_quark));
}

void
kz_bookmark_set_auto_refresh (KzBookmark  *bookmark,
			      gboolean auto_refresh)
{
	if (!kz_bookmark_is_folder(bookmark)) return;

	g_object_set(G_OBJECT(bookmark), "auto-refresh", auto_refresh, NULL);
}

gboolean
kz_bookmark_get_javascript (KzBookmark  *bookmark)
{
	if (!kz_bookmark_is_folder(bookmark)) return FALSE;

	return GPOINTER_TO_INT(g_object_get_qdata(G_OBJECT(bookmark),
						  javascript_quark));
}

void
kz_bookmark_set_javascript (KzBookmark  *bookmark,
			      gboolean javascript)
{
	if (!kz_bookmark_is_folder(bookmark)) return;

	g_object_set(G_OBJECT(bookmark), "javascript", javascript, NULL);
}

static gint 
compare_func (gconstpointer a, gconstpointer b)
{
	guint one = kz_bookmark_get_last_modified((KzBookmark*)a);
	guint two = kz_bookmark_get_last_modified((KzBookmark*)b);
	return two - one;
}


void
kz_bookmark_sort (KzBookmark *bookmark, const gchar *type)
{
	GList *children;

	g_return_if_fail(KZ_IS_BOOKMARK(bookmark));
	g_return_if_fail(kz_bookmark_is_folder(bookmark));

	if (!type) return;

	children = g_object_get_qdata(G_OBJECT(bookmark), children_quark);

	/* FIXME! use hash table */
	if (!strcmp(type, KZ_BOOKMARK_SORT_LAST_MODIFIED))
		children = g_list_sort(children, compare_func);
	else
		return;

	g_object_set_qdata(G_OBJECT(bookmark), children_quark, children);

	g_signal_emit(bookmark,
		      kz_bookmark_signals[CHILDREN_REORDERED_SIGNAL], 0);
}


void
kz_bookmark_regist_sort_func (const gchar *type, GCompareFunc *func)
{
	g_warning("kz_bookmark_regist_sort_func() is not implemented yet.");
}


KzBookmark *
kz_bookmark_find_bookmark_from_uri (KzBookmark *folder, const gchar *key_uri)
{
	GList *children, *node;
	KzBookmark *bookmark = NULL;

	children = kz_bookmark_get_children(folder);

	/* FIXME! Use hash? */
	for (node = children; node; node = g_list_next(node))
	{
		KzBookmark *child = node->data;
		const gchar *uri = kz_bookmark_get_link(child);
		if (uri && key_uri && !g_strcmp(uri, key_uri))
		{
			bookmark = child;	
			break;
		}

		if (kz_bookmark_is_folder(child))
		{
			KzBookmark *find;
			find = kz_bookmark_find_bookmark_from_uri(child,
								  key_uri);
			if (find)
			{
				bookmark = find;
				break;
			}
		}
	}
	g_list_free(children);

	return bookmark;
}

