/*
 *  Copyright (C) 2002 Jorn Baayen
 *
 *  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$
 */

#include "find-dialog.h"
#include "ephy-prefs.h"
#include "ephy-embed.h"
#include <gtk/gtk.h>

#define CONF_FIND_MATCH_CASE "/apps/epiphany/find/match_case"
#define CONF_FIND_AUTOWRAP "/apps/epiphany/find/autowrap"
#define CONF_FIND_WORD "/apps/epiphany/find/word"

static void find_dialog_class_init (FindDialogClass *klass);
static void find_dialog_init (FindDialog *dialog);
static void find_dialog_finalize (GObject *object);

static void
impl_construct (EphyDialog *dialog,
                const EphyDialogProperty *properties,
                const char *file,
                const char *name);
static void
impl_destruct (EphyDialog *dialog);
static void
impl_show (EphyDialog *dialog);


/* Glade callbacks */
void find_close_button_clicked_cb (GtkWidget *button, EphyDialog *dialog);
void find_next_button_clicked_cb (GtkWidget *button, EphyDialog *dialog);
void find_prev_button_clicked_cb (GtkWidget *button, EphyDialog *dialog);
void find_entry_changed_cb  (GtkWidget *editable, EphyDialog *dialog);
void find_check_toggled_cb (GtkWidget *toggle, EphyDialog *dialog);

static GObjectClass *parent_class = NULL;

struct FindDialogPrivate
{
	EmbedFindInfo *properties;
	GtkWidget *window;
	gboolean can_go_prev;
	gboolean can_go_next;
	gboolean constructed;
};

enum
{
	SEARCH,
	LAST_SIGNAL
};

enum
{
	WINDOW_PROP,
	MATCH_CASE_PROP,
	AUTOWRAP_PROP,
	WORD_PROP,
	BACK_BUTTON,
	FORWARD_BUTTON
};

static const
EphyDialogProperty properties [] =
{
	{ WINDOW_PROP, "find_dialog", NULL, PT_NORMAL, NULL },
	{ MATCH_CASE_PROP, "case_check", CONF_FIND_MATCH_CASE, PT_NORMAL, NULL },
	{ AUTOWRAP_PROP, "wrap_check", CONF_FIND_AUTOWRAP, PT_NORMAL, NULL },
	{ WORD_PROP, "find_entry", CONF_FIND_WORD, PT_NORMAL, NULL },
	{ BACK_BUTTON, "back_button", NULL, PT_NORMAL, NULL },
	{ FORWARD_BUTTON, "forward_button", NULL, PT_NORMAL, NULL },
	{ -1, NULL, NULL }
};

static guint find_dialog_signals[LAST_SIGNAL] = { 0 };

GType
find_dialog_get_type (void)
{
        static GType find_dialog_type = 0;

        if (find_dialog_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (FindDialogClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) find_dialog_class_init,
                        NULL,
                        NULL, /* class_data */
                        sizeof (FindDialog),
                        0, /* n_preallocs */
                        (GInstanceInitFunc) find_dialog_init
                };

                find_dialog_type = g_type_register_static (EPHY_EMBED_DIALOG_TYPE,
						           "FindDialog",
						           &our_info, 0);
        }

        return find_dialog_type;

}

static void
find_dialog_class_init (FindDialogClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
	EphyDialogClass *ephy_dialog_class;

        parent_class = g_type_class_peek_parent (klass);
	ephy_dialog_class = EPHY_DIALOG_CLASS (klass);

        object_class->finalize = find_dialog_finalize;

	ephy_dialog_class->construct = impl_construct;
	ephy_dialog_class->destruct = impl_destruct;
	ephy_dialog_class->show = impl_show;

	find_dialog_signals[SEARCH] =
                g_signal_new ("search",
                              G_OBJECT_CLASS_TYPE (object_class),
                              G_SIGNAL_RUN_FIRST,
                              G_STRUCT_OFFSET (FindDialogClass, search),
                              NULL, NULL,
                              g_cclosure_marshal_VOID__VOID,
                              G_TYPE_NONE,
                              0);
}

static void
find_update_nav (EphyDialog *dialog)
{
	GtkWidget *forward_button;
	GtkWidget *back_button;

	g_signal_emit (G_OBJECT (dialog), find_dialog_signals[SEARCH], 0);

	if (!FIND_DIALOG(dialog)->priv->constructed) return;

	forward_button = ephy_dialog_get_control (dialog, FORWARD_BUTTON);
        gtk_widget_set_sensitive (forward_button,
				  FIND_DIALOG(dialog)->priv->can_go_next);

	back_button = ephy_dialog_get_control (dialog, BACK_BUTTON);
        gtk_widget_set_sensitive (back_button,
				  FIND_DIALOG(dialog)->priv->can_go_prev);
}

static void
ensure_constructed (FindDialog *dialog)
{
	if (!dialog->priv->constructed)
	{
		ephy_dialog_construct (EPHY_DIALOG(dialog),
				       properties,
				       "epiphany.glade",
				       "find_dialog");
	}
}

static void
find_dialog_init (FindDialog *dialog)
{
	dialog->priv = g_new0 (FindDialogPrivate, 1);

	dialog->priv->properties = NULL;
	dialog->priv->can_go_prev = TRUE;
	dialog->priv->can_go_next = TRUE;
	dialog->priv->constructed = FALSE;

	ensure_constructed (dialog);
}

static void
impl_construct (EphyDialog *dialog,
                const EphyDialogProperty *properties,
                const char *file,
                const char *name)
{
	FIND_DIALOG(dialog)->priv->constructed = TRUE;

	EPHY_DIALOG_CLASS (parent_class)->construct (dialog, properties, file, name);
}

static void
impl_destruct (EphyDialog *dialog)
{
	FIND_DIALOG(dialog)->priv->constructed = FALSE;

	EPHY_DIALOG_CLASS (parent_class)->destruct (dialog);
}

static void
find_get_info (EphyDialog *dialog)
{
        EmbedFindInfo *properties;
        char *search_string;
	GValue word = {0, };
	GValue match_case = {0, };
	GValue wrap = {0, };
	FindDialog *find_dialog = FIND_DIALOG(dialog);

        /* get the search string from the entry field */
	ephy_dialog_get_value (dialog, WORD_PROP, &word);
        search_string = g_strdup(g_value_get_string (&word));
	g_value_unset (&word);

        /* don't do null searches */
        if (search_string[0] == '\0')
        {
		find_dialog->priv->can_go_prev = FALSE;
		find_dialog->priv->can_go_next = FALSE;
                return;
        }

	if (find_dialog->priv->properties != NULL)
	{
		g_free (find_dialog->priv->properties->search_string);
		g_free (find_dialog->priv->properties);
	}

        /* build search structure */
        properties = g_new0 (EmbedFindInfo,1);
        properties->search_string = search_string;

	ephy_dialog_get_value (dialog, MATCH_CASE_PROP, &match_case);
        properties->match_case = g_value_get_boolean (&match_case);

	ephy_dialog_get_value (dialog, AUTOWRAP_PROP, &wrap);
        properties->wrap = g_value_get_boolean (&wrap);

        properties->entire_word = FALSE;
	properties->search_frames = TRUE;

	find_dialog->priv->properties = properties;
}

static void
impl_show (EphyDialog *dialog)
{
	GdkPixbuf *icon;
	FindDialog *find_dialog = FIND_DIALOG(dialog);
	ensure_constructed (find_dialog);

	find_dialog->priv->can_go_prev = TRUE;
	find_dialog->priv->can_go_next = TRUE;
	find_dialog->priv->window = ephy_dialog_get_control (dialog, WINDOW_PROP);
	find_get_info (dialog);
	find_update_nav (dialog);

	/* Focus the text entry.  This will correctly select or leave
	 * unselected the existing text in the entry depending on the
	 * 'gtk-entry-select-on-focus = 0 / 1' setting in user's gtkrc.
	 */
	gtk_widget_grab_focus (ephy_dialog_get_control (dialog, WORD_PROP));

	
	icon = gtk_widget_render_icon (find_dialog->priv->window, 
						      GTK_STOCK_FIND,
						      GTK_ICON_SIZE_MENU,
						      "find_dialog");
	gtk_window_set_icon (GTK_WINDOW(find_dialog->priv->window), icon);
	g_object_unref (icon);
	
	EPHY_DIALOG_CLASS (parent_class)->show (dialog);
}

static void
find_dialog_finalize (GObject *object)
{
	FindDialog *dialog;

        g_return_if_fail (object != NULL);
        g_return_if_fail (IS_FIND_DIALOG (object));

	dialog = FIND_DIALOG (object);

        g_return_if_fail (dialog->priv != NULL);

        g_free (dialog->priv);

        G_OBJECT_CLASS (parent_class)->finalize (object);
}

EphyDialog *
find_dialog_new (EphyEmbed *embed)
{
	FindDialog *dialog;

	dialog = FIND_DIALOG (g_object_new (FIND_DIALOG_TYPE,
				     "EphyEmbed", embed,
				     NULL));

	return EPHY_DIALOG(dialog);
}

EphyDialog *
find_dialog_new_with_parent (GtkWidget *window,
			     EphyEmbed *embed)
{
	FindDialog *dialog;

	dialog = FIND_DIALOG (g_object_new (FIND_DIALOG_TYPE,
				     "EphyEmbed", embed,
				     "ParentWindow", window,
				     NULL));

	return EPHY_DIALOG(dialog);
}

gboolean
find_dialog_can_go_next (FindDialog *dialog)
{
	return dialog->priv->can_go_next;
}

gboolean
find_dialog_can_go_prev (FindDialog *dialog)
{
	return dialog->priv->can_go_prev;
}

void
find_dialog_go_next (FindDialog *dialog,
		     gboolean interactive)
{
	gresult result;
	EphyEmbed *embed;

	if (!find_dialog_can_go_next (dialog)) return;

	dialog->priv->properties->backwards = FALSE;
	dialog->priv->properties->interactive = interactive;

	embed = ephy_embed_dialog_get_embed (EPHY_EMBED_DIALOG(dialog));
	g_return_if_fail (embed != NULL);

        result = ephy_embed_find (embed,
				  dialog->priv->properties);

	dialog->priv->can_go_prev = TRUE;
	if (result != G_OK)
	{
		dialog->priv->can_go_next = FALSE;
	}

	find_update_nav (EPHY_DIALOG(dialog));
}

void
find_dialog_go_prev (FindDialog *dialog,
		     gboolean interactive)
{
	gresult result;
	EphyEmbed *embed;

	if (!find_dialog_can_go_prev (dialog)) return;

	dialog->priv->properties->backwards = TRUE;
	dialog->priv->properties->interactive = interactive;

	embed = ephy_embed_dialog_get_embed (EPHY_EMBED_DIALOG(dialog));
	g_return_if_fail (embed != NULL);

	result = ephy_embed_find (embed,
				  dialog->priv->properties);

	dialog->priv->can_go_next = TRUE;
	if (result != G_OK)
	{
		dialog->priv->can_go_prev = FALSE;
	}

	find_update_nav (EPHY_DIALOG(dialog));
}

void
find_close_button_clicked_cb (GtkWidget *button,
			      EphyDialog *dialog)
{
	ephy_dialog_destruct (dialog);
	g_object_unref (dialog);
}

void find_next_button_clicked_cb (GtkWidget *button,
				  EphyDialog *dialog)
{
	find_dialog_go_next (FIND_DIALOG(dialog), TRUE);
}

void
find_prev_button_clicked_cb (GtkWidget *button,
			     EphyDialog *dialog)
{
	find_dialog_go_prev (FIND_DIALOG(dialog), TRUE);
}

void
find_entry_changed_cb  (GtkWidget *editable,
			EphyDialog *dialog)
{
	FindDialog *find_dialog = FIND_DIALOG(dialog);

	find_dialog->priv->can_go_prev = TRUE;
	find_dialog->priv->can_go_next = TRUE;

	find_get_info (dialog);

	find_update_nav (dialog);
}

void
find_check_toggled_cb (GtkWidget *toggle,
                       EphyDialog *dialog)
{
	FindDialog *find_dialog = FIND_DIALOG(dialog);

	find_dialog->priv->can_go_prev = TRUE;
	find_dialog->priv->can_go_next = TRUE;

	find_get_info (dialog);

        find_update_nav (dialog);
}