/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
 *  Copyright (C) 2009 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-web.h"

#include <glib/gi18n.h>
#include "kz-bookmark-folder.h"
#include "kz-embed.h"
#include "kz-marshalers.h"
#include "utils.h"

enum {
    PROP_0,
    PROP_MODULE_NAME,
    PROP_ENGINE_NAME,
    PROP_EMBED,
    PROP_LOCK
};

enum {
    LINK_MESSAGE,
    JS_STATUS,
    LOCATION,
    TITLE,
    PROGRESS,
    NET_START,
    NET_STOP,
    NEW_WINDOW,
    OPEN_URI,
    SIZE_TO,
    DOM_KEY_DOWN,
    DOM_KEY_PRESS,
    DOM_KEY_UP,
    DOM_MOUSE_DOWN,
    DOM_MOUSE_UP,
    DOM_MOUSE_CLICK,
    DOM_MOUSE_DOUBLE_CLICK,
    DOM_MOUSE_OVER,
    DOM_MOUSE_OUT,
    SELECTION,
    LAST_SIGNAL
};

static gint signals[LAST_SIGNAL] = {0};

typedef struct _KzWebPrivate	KzWebPrivate;
struct _KzWebPrivate
{
    gchar *module_name;
    gchar *engine_name;
    GtkWidget *embed;
    gboolean lock;
    gboolean allow_javascript;
    gboolean is_loading;
    gchar *title;
    gchar *location;
    gdouble progress_ratio;
    KzBookmarkFolder *history;
};

#define KZ_WEB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), KZ_TYPE_WEB, KzWebPrivate))

G_DEFINE_TYPE(KzWeb, kz_web, GTK_TYPE_BIN);

static GObject *constructor  (GType type,
                              guint n_props,
                              GObjectConstructParam *props);
static void     dispose      (GObject      *object);
static void     finalize     (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);
static void     size_request (GtkWidget     *widget,
                              GtkRequisition *requisition);
static void     size_allocate(GtkWidget     *widget,
                              GtkAllocation *allocation);


static void
kz_web_class_init (KzWebClass *klass)
{
	GObjectClass *object_class;
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);

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

    object_class->constructor = constructor;
    object_class->dispose = dispose;
    object_class->finalize = finalize;
    object_class->set_property = set_property;
    object_class->get_property = get_property;

    widget_class->size_allocate = size_allocate;
    widget_class->size_request = size_request;

    signals[LINK_MESSAGE]
        = g_signal_new ("kz-link-message",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__STRING,
                G_TYPE_NONE, 1,
                G_TYPE_STRING);

    signals[JS_STATUS]
        = g_signal_new ("kz-js-status",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0);

    signals[LOCATION]
        = g_signal_new ("kz-location",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__STRING,
                G_TYPE_NONE, 1,
                G_TYPE_STRING);

    signals[TITLE]
        = g_signal_new ("kz-title",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__STRING,
                G_TYPE_NONE, 1,
                G_TYPE_STRING);

    signals[PROGRESS]
        = g_signal_new ("kz-progress",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__DOUBLE,
                G_TYPE_NONE, 1,
                G_TYPE_DOUBLE);

    signals[NET_START]
        = g_signal_new ("kz-net-start",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0);

    signals[NET_STOP]
        = g_signal_new ("kz-net-stop",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0);

    signals[NEW_WINDOW]
        = g_signal_new ("kz-new-window",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__POINTER,
                G_TYPE_NONE, 1, G_TYPE_POINTER);

    signals[OPEN_URI]
        = g_signal_new ("kz-open-uri",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_INT__STRING,
                G_TYPE_INT, 1, G_TYPE_STRING);

    signals[SIZE_TO]
        = g_signal_new ("kz-size-to",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                _kz_marshal_VOID__INT_INT,
                G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);

    signals[DOM_KEY_DOWN]
        = g_signal_new ("kz-dom-key-down",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_KEY_PRESS]
        = g_signal_new ("kz-dom-key-press",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_KEY_UP]
        = g_signal_new ("kz-dom-key-up",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_DOWN]
        = g_signal_new ("kz-dom-mouse-down",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_UP]
        = g_signal_new ("kz-dom-mouse-up",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_CLICK]
        = g_signal_new ("kz-dom-mouse-click",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_DOUBLE_CLICK]
        = g_signal_new ("kz-dom-mouse-double-click",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_OVER]
        = g_signal_new ("kz-dom-mouse-over",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[DOM_MOUSE_OUT]
        = g_signal_new ("kz-dom-mouse-out",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_LAST,
                0,
                NULL, NULL,
                _kz_marshal_BOOLEAN__POINTER,
                G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);

    signals[SELECTION]
        = g_signal_new ("kz-selection",
                KZ_TYPE_WEB,
                G_SIGNAL_RUN_FIRST,
                0,
                NULL, NULL,
                g_cclosure_marshal_VOID__VOID,
                G_TYPE_NONE, 0);
    g_object_class_install_property
        (object_class,
         PROP_MODULE_NAME,
         g_param_spec_string("module-name",
                             _("Module Name"),
                             _("Module Name"),
                             "gecko",
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
    g_object_class_install_property
        (object_class,
         PROP_ENGINE_NAME,
         g_param_spec_string("engine-name",
                             _("Engine Name"),
                             _("Engine Name"),
                             NULL,
                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
    g_object_class_install_property
        (object_class,
         PROP_LOCK,
         g_param_spec_boolean("lock",
                              _("Lock"),
                              _("Lock"),
                              FALSE,
                              G_PARAM_READWRITE));

	g_type_class_add_private(object_class, sizeof(KzWebPrivate));
}

static void
kz_web_init (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    priv->embed = NULL;
    priv->module_name = NULL;
    priv->engine_name = NULL;
    priv->lock = FALSE;
    priv->is_loading = FALSE;
    priv->title = NULL;
    priv->location = NULL;
    priv->history = KZ_BOOKMARK_FOLDER(kz_bookmark_folder_new(_("Tab")));
}

GtkWidget *
kz_web_new (const gchar *module_name, const gchar *engine_name)
{
    return GTK_WIDGET(g_object_new(KZ_TYPE_WEB,
                                   "module-name", module_name,
                                   "engine-name", engine_name,
                                   NULL));
}

static void
cb_kz_link_message (KzEmbed *embed, const gchar *message, KzWeb *web)
{
    g_signal_emit(web, signals[LINK_MESSAGE], 0, message);
}

static void
cb_kz_title (KzEmbed *embed, const gchar *title, KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    g_free(priv->title);
    priv->title = g_strdup(title);

    g_signal_emit(web, signals[TITLE], 0, title);
}

static void
cb_kz_location (KzEmbed *embed, const gchar *location, KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    g_free(priv->location);
    priv->location = g_strdup(location);

    g_signal_emit(web, signals[LOCATION], 0, location);
}

static void
cb_kz_progress (KzEmbed *embed, gdouble ratio, KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    priv->progress_ratio = ratio;

    g_signal_emit(web, signals[PROGRESS], 0, ratio);
}

static void
cb_kz_net_start (KzEmbed *embed, KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    priv->is_loading = TRUE;
    priv->progress_ratio = 0.0;
    g_signal_emit(web, signals[NET_START], 0);
}

static void
cb_kz_net_stop (KzEmbed *embed, KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);
    GList *history = NULL;
    guint current_position = 0;

    priv->is_loading = FALSE;
    priv->progress_ratio = 1.0;

    kz_embed_get_history(KZ_EMBED(priv->embed), &history, &current_position);
    kz_utils_site_list_to_bookmark_folder(history, current_position, priv->history);
    kz_site_list_free(history);

    g_signal_emit(web, signals[NET_STOP], 0);
}

static gboolean
cb_kz_dom_mouse_up (KzEmbed *embed, KzEmbedEventMouse *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_MOUSE_UP], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_mouse_down (KzEmbed *embed, KzEmbedEventMouse *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_MOUSE_DOWN], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_mouse_click (KzEmbed *embed, KzEmbedEventMouse *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_MOUSE_CLICK], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_mouse_over (KzEmbed *embed, KzEmbedEventMouse *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_MOUSE_OVER], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_key_up (KzEmbed *embed, KzEmbedEventKey *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_KEY_UP], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_key_down (KzEmbed *embed, KzEmbedEventKey *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_KEY_DOWN], 0, event, &ret);
    return ret;
}

static gboolean
cb_kz_dom_key_press (KzEmbed *embed, KzEmbedEventKey *event, KzWeb *web)
{
    gboolean ret = FALSE;
    g_signal_emit(web, signals[DOM_KEY_PRESS], 0, event, &ret);
    return ret;
}

static void
cb_kz_new_window (KzEmbed *embed, KzEmbed **new_embed, KzWeb *web)
{
    KzWeb *new_web = NULL;

    g_signal_emit(web, signals[NEW_WINDOW], 0, &new_web);
    *new_embed = KZ_EMBED(KZ_WEB_GET_PRIVATE(new_web)->embed);
}

static void
connect_embed_signals (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

#define CONNECT(name)                              \
    g_signal_connect(priv->embed, #name,           \
                     G_CALLBACK(cb_ ## name), web)
    CONNECT(kz_net_start);
    CONNECT(kz_net_stop);
    CONNECT(kz_title);
    CONNECT(kz_location);
    CONNECT(kz_progress);
    CONNECT(kz_link_message);
    CONNECT(kz_dom_mouse_up);
    CONNECT(kz_dom_mouse_down);
    CONNECT(kz_dom_mouse_click);
    CONNECT(kz_dom_mouse_over);
    CONNECT(kz_dom_key_up);
    CONNECT(kz_dom_key_down);
    CONNECT(kz_dom_key_press);
    CONNECT(kz_new_window);
#undef CONNECT
}

static void
disconnect_embed_signals (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

#define DISCONNECT(name)                                          \
    g_signal_handlers_disconnect_by_func(priv->embed,             \
                                         G_CALLBACK(cb_ ## name), \
                                         web)
    DISCONNECT(kz_net_start);
    DISCONNECT(kz_net_stop);
    DISCONNECT(kz_title);
    DISCONNECT(kz_location);
    DISCONNECT(kz_progress);
    DISCONNECT(kz_link_message);
    DISCONNECT(kz_dom_mouse_up);
    DISCONNECT(kz_dom_mouse_down);
    DISCONNECT(kz_dom_mouse_click);
    DISCONNECT(kz_dom_mouse_over);
    DISCONNECT(kz_dom_key_up);
    DISCONNECT(kz_dom_key_down);
    DISCONNECT(kz_dom_key_press);
    DISCONNECT(kz_new_window);
#undef CONNECT
}

static void
kz_web_set_embed (KzWeb *web, GtkWidget *embed)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    if (priv->embed) {
        disconnect_embed_signals(web);
        kz_embed_copy_page(KZ_EMBED(priv->embed), KZ_EMBED(embed));
        gtk_container_remove(GTK_CONTAINER(web), priv->embed);
    }
    priv->embed = embed;
    if (embed) {
        gtk_widget_show_all(embed);
        gtk_container_add(GTK_CONTAINER(web), embed);
        connect_embed_signals(web);
    }
}

static GObject*
constructor (GType                  type,
             guint                  n_props,
             GObjectConstructParam *props)
{
	GObject *object;
	GObjectClass *klass = G_OBJECT_CLASS(kz_web_parent_class);
    KzWebPrivate *priv;

	object = klass->constructor(type, n_props, props);
    priv = KZ_WEB_GET_PRIVATE(object);
    kz_web_set_embed(KZ_WEB(object),
                     kz_embed_new(priv->module_name, priv->engine_name));

	return object;
}

static void
dispose (GObject *object)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(object);

    if (priv->history) {
        g_object_unref(priv->history);
        priv->history = NULL;
    }

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

static void
finalize (GObject *object)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(object);

    g_free(priv->module_name);
    g_free(priv->engine_name);
    g_free(priv->title);
    g_free(priv->location);

    if (G_OBJECT_CLASS(kz_web_parent_class)->finalize)
        G_OBJECT_CLASS(kz_web_parent_class)->finalize(object);
}

static void
set_property (GObject         *object,
              guint            prop_id,
              const GValue    *value,
              GParamSpec      *pspec)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(object);

    switch (prop_id) {
    case PROP_MODULE_NAME:
        priv->module_name = g_value_dup_string(value);
        break;
    case PROP_ENGINE_NAME:
        priv->engine_name = g_value_dup_string(value);
        break;
    case PROP_LOCK:
        priv->lock = g_value_get_boolean(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)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(object);

    switch (prop_id) {
    case PROP_MODULE_NAME:
        g_value_set_string(value, priv->module_name);
        break;
    case PROP_ENGINE_NAME:
        g_value_set_string(value, priv->engine_name);
        break;
    case PROP_LOCK:
        g_value_set_boolean(value, priv->lock);
        break;
    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
size_request (GtkWidget *widget, GtkRequisition *requisition)
{
    GtkWidget *child;
    GtkRequisition child_requisition;

    child = gtk_bin_get_child(GTK_BIN(widget));

    requisition->width = GTK_CONTAINER(widget)->border_width;

    requisition->height = GTK_CONTAINER(widget)->border_width;

    if (child && gtk_widget_get_visible(child)) {
        gtk_widget_size_request(child, &child_requisition);
        requisition->width += child_requisition.width;
        requisition->height += child_requisition.height;
    }
}

static void
size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
    GtkWidget *child;
    GtkAllocation child_allocation;

    if (!gtk_widget_get_realized(widget))
        return;
    widget->allocation = *allocation;

    if (!gtk_widget_get_has_window(widget)) {
        child_allocation.x = allocation->x + GTK_CONTAINER(widget)->border_width;
        child_allocation.y = allocation->y + GTK_CONTAINER(widget)->border_width;
    } else {
        child_allocation.x = 0;
        child_allocation.y = 0;
    }

    child_allocation.width = MAX(allocation->width - GTK_CONTAINER(widget)->border_width * 2, 0);
    child_allocation.height = MAX(allocation->height - GTK_CONTAINER(widget)->border_width * 2, 0);

    child = gtk_bin_get_child(GTK_BIN(widget));
    if (child)
        gtk_widget_size_allocate(child, &child_allocation);
}

const gchar *
kz_web_get_engine_name (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->engine_name;
}

const gchar *
kz_web_get_module_name (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->module_name;
}

void
kz_web_load_uri (KzWeb *web, const gchar *uri)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);
    KzWeb *target_web = NULL;

    if (kz_web_get_lock(web)) {
        g_signal_emit_by_name(web, 
                              "new-window",
                              &target_web, 0);
    } else {
        target_web = web;
    }

    priv = KZ_WEB_GET_PRIVATE(target_web);
    kz_embed_load_uri(KZ_EMBED(priv->embed), uri);
}

void
kz_web_change_engine (KzWeb *web, const gchar *new_engine_name)
{
    if (!strcmp(kz_web_get_module_name(web), "per_process")) {
    } else {
        GtkWidget *new_embed;
        new_embed = kz_embed_new(new_engine_name, new_engine_name);
        kz_web_set_embed(web, new_embed);
    }
}

const gchar *
kz_web_get_title (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->title;
}

const gchar *
kz_web_get_location (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->location;
}

gdouble
kz_web_get_progress (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->progress_ratio;
}

gboolean
kz_web_is_loading (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->is_loading;
}

gboolean
kz_web_get_lock (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->lock;
}

void
kz_web_set_lock (KzWeb *web, gboolean lock)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    priv->lock = lock;
	kz_bookmark_folder_set_lock(priv->history, lock);
}

gboolean
kz_web_get_allow_javascript (KzWeb *web)
{
    return KZ_WEB_GET_PRIVATE(web)->allow_javascript;
}

void
kz_web_set_allow_javascript (KzWeb *web, gboolean allow)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    priv->allow_javascript = allow;
	kz_bookmark_folder_set_javascript(priv->history, allow);
}

gchar *
kz_web_ensure_title (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    if (priv->title)
        return g_strdup(priv->title);

    if (priv->location && strlen(priv->location) != 0) {
        if (priv->is_loading)
            return g_strdup_printf(_("Loading %s ..."), priv->location);
        else
            return g_strdup(priv->location);
    }
    if (priv->is_loading)
        return g_strdup(_("Loading..."));

    return g_strdup(_("No title"));
}

static gchar *
get_up_location (KzWeb *web)
{
    gchar *up_location = NULL;
    gchar *pos, *dummy;
    int len; 
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    if (!priv->location)
        return NULL;

    len = strlen(priv->location);
    if (priv->location[len - 1] == '/')
        dummy = g_strndup(priv->location, len - 1);
    else 
        dummy = g_strndup(priv->location, len);
    pos =  strrchr(dummy, '/');
    if (pos)
        up_location = g_strndup(dummy, pos - dummy + 1);
    g_free(dummy);

    return up_location;
}

void
kz_web_go_up (KzWeb *web)
{
    gchar *location;

    location = get_up_location(web);
    kz_web_load_uri(web, location);
    g_free(location);
}

gboolean
kz_web_can_go_up (KzWeb *web)
{
    gboolean ret;
    gchar *location;

    location = get_up_location(web);
    if (!location)
        return FALSE;

    /* stupid! */
    if (strcmp(location, "http://") &&
        strcmp(location, "https://")  &&
        strcmp(location, "ftp://")  &&
        strcmp(location, "file://"))
        ret = TRUE;
    else
        ret = FALSE;
    g_free(location);

    return ret;
}

#define DEFINE_SIMPLE_METHOD(method)                \
void                                                \
kz_web_ ## method (KzWeb *web)                      \
{                                                   \
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);   \
    kz_embed_ ## method (KZ_EMBED(priv->embed));    \
}

DEFINE_SIMPLE_METHOD(copy_selection)
DEFINE_SIMPLE_METHOD(cut_selection)
DEFINE_SIMPLE_METHOD(paste)
DEFINE_SIMPLE_METHOD(select_all)
DEFINE_SIMPLE_METHOD(stop_load)
DEFINE_SIMPLE_METHOD(go_back)
DEFINE_SIMPLE_METHOD(go_forward)
DEFINE_SIMPLE_METHOD(print)
DEFINE_SIMPLE_METHOD(print_preview)
DEFINE_SIMPLE_METHOD(page_up)
DEFINE_SIMPLE_METHOD(page_down)
DEFINE_SIMPLE_METHOD(show_page_certificate)

#define DEFINE_SET_BOOLEAN_VALUE_METHOD(method)             \
void                                                        \
kz_web_set_ ## method (KzWeb *web, gboolean value)          \
{                                                           \
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);           \
    kz_embed_set_ ## method (KZ_EMBED(priv->embed), value); \
}                                                           \

#define DEFINE_GET_BOOLEAN_VALUE_METHOD(method)             \
gboolean                                                    \
kz_web_ ## method (KzWeb *web)                              \
{                                                           \
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);           \
    return kz_embed_ ## method (KZ_EMBED(priv->embed));     \
}

#define DEFINE_BOOLEAN_METHODS(method)                      \
    DEFINE_SET_BOOLEAN_VALUE_METHOD(method)                 \
    DEFINE_GET_BOOLEAN_VALUE_METHOD(get_ ## method)

DEFINE_GET_BOOLEAN_VALUE_METHOD(can_go_back)
DEFINE_GET_BOOLEAN_VALUE_METHOD(can_go_forward)
DEFINE_GET_BOOLEAN_VALUE_METHOD(can_cut_selection)
DEFINE_GET_BOOLEAN_VALUE_METHOD(can_copy_selection)
DEFINE_GET_BOOLEAN_VALUE_METHOD(can_paste)
DEFINE_GET_BOOLEAN_VALUE_METHOD(selection_is_collapsed)

DEFINE_BOOLEAN_METHODS(allow_images)

guint
kz_web_get_last_modified (KzWeb*web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_last_modified(KZ_EMBED(priv->embed));
}

gboolean
kz_web_get_links (KzWeb *web, GList **list, gboolean selected_only)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_links(KZ_EMBED(priv->embed), list, selected_only);
}

gboolean
kz_web_get_dest_anchors (KzWeb *web, GList **list)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_dest_anchors(KZ_EMBED(priv->embed), list);
}

gboolean
kz_web_set_text_into_textarea (KzWeb *web,
                               gpointer element,
                               const gchar *text)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_set_text_into_textarea(KZ_EMBED(priv->embed), element, text);
}

gchar *
kz_web_get_text_from_textarea(KzWeb *web, gpointer element)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_text_from_textarea(KZ_EMBED(priv->embed), element);
}

void
kz_web_set_encoding (KzWeb *web, const gchar *encoding)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_set_encoding(KZ_EMBED(priv->embed), encoding);
}

void
kz_web_get_encoding (KzWeb *web, gchar **encoding, gboolean *forced)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_get_encoding(KZ_EMBED(priv->embed), encoding, forced);
}

void
kz_web_create_thumbnail (KzWeb *web, EggPixbufThumbnailSize size)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_create_thumbnail(KZ_EMBED(priv->embed), size);
}

gboolean
kz_web_save_with_content (KzWeb *web, const char *raw_filename)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_save_with_content(KZ_EMBED(priv->embed), raw_filename);
}

void
kz_web_zoom (KzWeb *web, gdouble zoom)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_zoom(KZ_EMBED(priv->embed), zoom);
}

gdouble
kz_web_get_zoom_ratio (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_zoom_ratio(KZ_EMBED(priv->embed));
}

void
kz_web_set_text_size (KzWeb *web, gdouble zoom)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_set_text_size(KZ_EMBED(priv->embed), zoom);
}

gdouble
kz_web_get_text_size (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_text_size(KZ_EMBED(priv->embed));
}

gchar *
kz_web_get_html_with_contents(KzWeb *web, const gchar *target_dir)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_html_with_contents(KZ_EMBED(priv->embed), target_dir);
}

void
kz_web_set_history (KzWeb *web, const GList *history, guint current_position)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_set_history(KZ_EMBED(priv->embed), history, current_position);
}

void
kz_web_get_history (KzWeb*web, GList **history, guint *current_position)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_get_history(KZ_EMBED(priv->embed), history, current_position);
}

void
kz_web_fine_scroll (KzWeb*web, gint horiz, gint vert)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_fine_scroll(KZ_EMBED(priv->embed), horiz, vert);
}

gboolean
kz_web_can_go_nav_link (KzWeb *web, KzWebNavLink link)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_can_go_nav_link(KZ_EMBED(priv->embed), link);
}

void
kz_web_go_nav_link (KzWeb *web, KzWebNavLink link)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_go_nav_link(KZ_EMBED(priv->embed), link);
}

void
kz_web_append_nav_link (KzWeb *web, KzWebNavLink link, KzNavi *navi)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_append_nav_link(KZ_EMBED(priv->embed), link, navi);
}

void
kz_web_set_nav_link	(KzWeb *web, KzWebNavLink link, KzNavi *navi)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_set_nav_link(KZ_EMBED(priv->embed), link, navi);
}

void
kz_web_set_nth_nav_link (KzWeb *web,
                         KzWebNavLink link,
                         KzNavi *navi,
                         guint nth)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_set_nth_nav_link(KZ_EMBED(priv->embed), link, navi, nth);
}

KzNavi *
kz_web_get_nav_link (KzWeb *web, KzWebNavLink link)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_nav_link(KZ_EMBED(priv->embed), link);
}

KzNavi *
kz_web_get_nth_nav_link (KzWeb *web, KzWebNavLink link, guint nth)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_nth_nav_link(KZ_EMBED(priv->embed), link, nth);
}

GList *
kz_web_get_nav_links (KzWeb *web, KzWebNavLink link)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_nav_links(KZ_EMBED(priv->embed), link);
}

void
kz_web_go_history_index (KzWeb *web, gint index)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_go_history_index(KZ_EMBED(priv->embed), index);
}

gchar *
kz_web_get_body_text (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_body_text(KZ_EMBED(priv->embed));
}

void
kz_web_copy_page (KzWeb *src, KzWeb *dest)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(src);

    kz_embed_copy_page(KZ_EMBED(priv->embed), KZ_EMBED(KZ_WEB_GET_PRIVATE(dest)));
}

gboolean
kz_web_shistory_get_pos (KzWeb *web, gint *pos, gint *count)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_shistory_get_pos(KZ_EMBED(priv->embed), pos, count);
}

void
kz_web_shistory_get_nth	(KzWeb *web, 
                         gint nth,
                         gboolean is_relative,
                         gchar **uri,
                         gchar **title)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_shistory_get_nth(KZ_EMBED(priv->embed), nth, is_relative, uri, title);
}

void
kz_web_reload (KzWeb *web, KzWebReloadFlag flags)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_reload(KZ_EMBED(priv->embed), flags);
}

GList *
kz_web_get_printer_list (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_printer_list(KZ_EMBED(priv->embed));
}

gboolean
kz_web_find (KzWeb *web, const gchar *keyword, gboolean backward)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_find(KZ_EMBED(priv->embed), keyword, backward);
}

gboolean
kz_web_incremental_search (KzWeb *web,
                           const gchar *keyword,
                           gboolean backward)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_incremental_search(KZ_EMBED(priv->embed),
                                       keyword, backward);
}

gchar *
kz_web_get_selection_string (KzWeb *web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    return kz_embed_get_selection_string(KZ_EMBED(priv->embed));
}

void
kz_web_view_current_page_source_in_new (KzWeb *web,
                                        KzWeb *new_web)
{
    KzWebPrivate *priv = KZ_WEB_GET_PRIVATE(web);

    kz_embed_view_current_page_source_in_new(KZ_EMBED(priv->embed),
                                             KZ_EMBED(KZ_WEB_GET_PRIVATE(new_web)->embed));
}

/* 
vi:ts=4:nowrap:ai:expandtab:sw=4
*/
