/*
 *  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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "find-dialog.h"
#include "prefs-strings.h"
#include "galeon-embed.h"
#include <gdk/gdkkeysyms.h>

#define CONF_FIND_MATCH_CASE "/apps/galeon/Browsing/Find/match_case"
#define CONF_FIND_AUTOWRAP "/apps/galeon/Browsing/Find/autowrap"
#define CONF_FIND_WORD "/apps/galeon/Browsing/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_show (GaleonDialog *dialog);


/* Glade callbacks */
gboolean find_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, 
				  GaleonDialog *dialog);
void find_close_button_clicked_cb (GtkWidget *button, GaleonDialog *dialog);
void find_next_button_clicked_cb (GtkWidget *button, GaleonDialog *dialog);
void find_prev_button_clicked_cb (GtkWidget *button, GaleonDialog *dialog);
void find_entry_changed_cb  (GtkWidget *editable, GaleonDialog *dialog);
void find_check_toggled_cb (GtkWidget *toggle, GaleonDialog *dialog);

static GObjectClass *parent_class = NULL;

struct FindDialogPrivate
{
        GaleonEmbed *old_embed;
};

enum
{
	MATCH_CASE_PROP,
	AUTOWRAP_PROP,
	WORD_PROP,
	BACK_BUTTON,
	FORWARD_BUTTON
};
 
static const
GaleonDialogProperty properties [] =
{
	{ 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 }
};

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 (GALEON_EMBED_DIALOG_TYPE,
						           "FindDialog",
						           &our_info, 0);
        }

        return find_dialog_type;

}

static void
impl_show (GaleonDialog *dialog)
{
	GALEON_DIALOG_CLASS (parent_class)->show (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 (galeon_dialog_get_control (dialog, WORD_PROP));
}

static void
find_dialog_class_init (FindDialogClass *klass)
{
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
	GaleonDialogClass *galeon_dialog_class;
	
        parent_class = g_type_class_peek_parent (klass);
	galeon_dialog_class = GALEON_DIALOG_CLASS (klass);
	
        object_class->finalize = find_dialog_finalize;

	galeon_dialog_class->show = impl_show;
}

static void
update_navigation_controls (FindDialog *dialog, gboolean prev, gboolean next)
{
	GtkWidget *button;

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), BACK_BUTTON);
	gtk_widget_set_sensitive (button, prev);

	button = galeon_dialog_get_control (GALEON_DIALOG (dialog), FORWARD_BUTTON);
	gtk_widget_set_sensitive (button, next);
}

static void
set_properties (FindDialog *find_dialog)
{
        char *search_string;
	GValue word = {0, };
	GValue match_case = {0, };
	GValue wrap = {0, };
	GaleonDialog *dialog = GALEON_DIALOG (find_dialog);
	gboolean b_match_case;
	gboolean b_wrap;
	GaleonEmbed *embed;

        /* get the search string from the entry field */
	galeon_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 == NULL || search_string[0] == '\0')
        {
		update_navigation_controls (find_dialog, FALSE, FALSE);
		g_free (search_string);
                return;
        }

	galeon_dialog_get_value (dialog, MATCH_CASE_PROP, &match_case);
        b_match_case = g_value_get_boolean (&match_case);
	g_value_unset (&match_case);

	galeon_dialog_get_value (dialog, AUTOWRAP_PROP, &wrap);
        b_wrap = g_value_get_boolean (&wrap);
	g_value_unset (&wrap);

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG(dialog));
	g_return_if_fail (embed != NULL);

        galeon_embed_find_set_properties (embed, search_string,
					b_match_case, b_wrap);

	g_free (search_string);
}

static void
sync_page_change (GaleonEmbed *embed, FindDialog *dialog)
{
	g_return_if_fail (IS_GALEON_EMBED (embed));
	g_return_if_fail (IS_FIND_DIALOG (dialog));

	update_navigation_controls (dialog, TRUE, TRUE);
	set_properties (dialog);
}

static void
unset_old_embed (FindDialog *dialog)
{
	if (dialog->priv->old_embed == NULL) return;

	g_signal_handlers_disconnect_by_func (dialog->priv->old_embed,
					      G_CALLBACK (sync_page_change),
					      dialog);
	g_object_remove_weak_pointer (G_OBJECT (dialog->priv->old_embed),
				      (gpointer *)&dialog->priv->old_embed);
	
	dialog->priv->old_embed = NULL;
}

static void
sync_embed (FindDialog *dialog, GParamSpec *pspec, gpointer data)
{
	GaleonEmbed *embed;

	unset_old_embed (dialog);

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG (dialog));
	g_return_if_fail (IS_GALEON_EMBED (embed));
	dialog->priv->old_embed = embed;

	g_signal_connect (G_OBJECT (embed), "ge_location",
			  G_CALLBACK (sync_page_change), dialog);

	g_object_add_weak_pointer (G_OBJECT (embed),
				   (gpointer *)&dialog->priv->old_embed);

	update_navigation_controls (dialog, TRUE, TRUE);
	set_properties (dialog);
}

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

	dialog->priv->old_embed = NULL;

	galeon_dialog_construct (GALEON_DIALOG(dialog),
				 properties,
				 "galeon.glade", 
				 "find_dialog");

	g_signal_connect_object (dialog, "notify::GaleonEmbed",
				 G_CALLBACK (sync_embed), NULL, 0);
	update_navigation_controls (dialog, TRUE, TRUE);
}

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_signal_handlers_disconnect_by_func (dialog, G_CALLBACK (sync_embed), NULL);
	unset_old_embed (FIND_DIALOG (dialog));

        g_return_if_fail (dialog->priv != NULL);

        g_free (dialog->priv);

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

GaleonDialog *
find_dialog_new (GaleonEmbed *embed)
{
	FindDialog *dialog;
	
	dialog = FIND_DIALOG (g_object_new (FIND_DIALOG_TYPE, 
				     "GaleonEmbed", embed,
				     NULL));

	return GALEON_DIALOG(dialog);
}

GaleonDialog *
find_dialog_new_with_parent (GtkWidget *window,
			     GaleonEmbed *embed)
{
	FindDialog *dialog;
	
	dialog = FIND_DIALOG (g_object_new (FIND_DIALOG_TYPE, 
				     "GaleonEmbed", embed,
				     "ParentWindow", window, 
				     NULL));

	return GALEON_DIALOG(dialog);
}

static void
find_dialog_go_next (FindDialog *dialog)
{
	gresult result;
	GaleonEmbed *embed;

	g_return_if_fail (IS_FIND_DIALOG (dialog));

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG(dialog));
	g_return_if_fail (embed != NULL);

	set_properties (dialog);
        result = galeon_embed_find_next (embed, FALSE);

	if (result == G_OK)
	{
		update_navigation_controls (dialog, TRUE, TRUE);
	}
	else
	{
		update_navigation_controls (dialog, TRUE, FALSE);
	}
}

static void
find_dialog_go_prev (FindDialog *dialog)
{
	gresult result;
	GaleonEmbed *embed;

	g_return_if_fail (IS_FIND_DIALOG (dialog));

	embed = galeon_embed_dialog_get_embed (GALEON_EMBED_DIALOG(dialog));
	g_return_if_fail (embed != NULL);

	set_properties (dialog);
	result = galeon_embed_find_next (embed, TRUE);

	if (result == G_OK)
	{
		update_navigation_controls (dialog, TRUE, TRUE);
	}
	else
	{
		update_navigation_controls (dialog, FALSE, TRUE);
	}
}

void
find_close_button_clicked_cb (GtkWidget *button, 
			      GaleonDialog *dialog) 
{
	galeon_dialog_destruct (GALEON_DIALOG (dialog));
	g_object_unref (dialog);
}

gboolean
find_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, GaleonDialog *dialog)
{
	if( event->keyval == GDK_Escape )
	{
		galeon_dialog_destruct (GALEON_DIALOG (dialog));
		g_object_unref (dialog);
		return TRUE;
	}
	return FALSE;
}

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

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

void
find_entry_changed_cb  (GtkWidget *editable, 
			GaleonDialog *dialog)
{
	FindDialog *find_dialog = FIND_DIALOG(dialog);
	update_navigation_controls (find_dialog, TRUE, TRUE);
}

void 
find_check_toggled_cb (GtkWidget *toggle,
                       GaleonDialog *dialog)
{
	FindDialog *find_dialog = FIND_DIALOG(dialog);
	update_navigation_controls (find_dialog, TRUE, TRUE);
}
