#include <stdlib.h>
#include <string.h>

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "guiutils.h"
#include "pulist.h"

#include "cfg.h"
#include "edv_types.h"
#include "edv_obj.h"
#include "edv_find_bar.h"
#include "endeavour2.h"
#include "edv_find.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"
#include "edv_cfg_list.h"
#include "config.h"

#include "images/icon_stop_20x20.xpm"


static gint EDVFindBarProgressCB(
	const gchar *path, gfloat progress, gpointer data
);
static void EDVFindBarMatchExcerptCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
);
static void EDVFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf, gpointer data
);


static void EDVFindBarFindCB(GtkWidget *widget, gpointer data);
static void EDVFindBarStopCB(GtkWidget *widget, gpointer data);
static gint EDVFindBarCrossingCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);

void EDVFindBarSetSearch(
	edv_findbar_struct *fb, const gchar *s,
	gboolean record_history
);
edv_findbar_operation EDVFindBarCurrentFindOP(edv_findbar_struct *fb);

edv_findbar_struct *EDVFindBarNew(
	gpointer core, GtkWidget *parent,
	edv_findbar_role role,
	const gchar *(*get_location_cb)(
		edv_findbar_struct *, gpointer
	),
	void (*start_cb)(
		edv_findbar_struct *, gpointer
	),
	void (*end_cb)(
		edv_findbar_struct *, gint, gpointer
	),
	void (*match_cb)(
	    const gchar *, const struct stat *,
	    const gchar *, gint,
	    gpointer
	),
	void (*status_message_cb)(const gchar *, gpointer),
	void (*status_progress_cb)(gfloat, gpointer),      
	gpointer data
);
void EDVFindBarUpdateMenus(edv_findbar_struct *fb);
void EDVFindBarMap(edv_findbar_struct *fb);
void EDVFindBarUnmap(edv_findbar_struct *fb);
void EDVFindBarDelete(edv_findbar_struct *fb);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Progress callback, used for EDVFindObjectByName() and similar
 *	functions.
 */
static gint EDVFindBarProgressCB(
	const gchar *path, gfloat progress, gpointer data
)
{
	edv_findbar_struct *fb = EDV_FINDBAR(data);
	if(fb == NULL)
	    return(0);

	if(fb->stop_count > 0)
	    return(1);

	if(fb->status_message_cb != NULL)
	    fb->status_message_cb(path, fb->data);
	if(fb->status_progress_cb != NULL)
	    fb->status_progress_cb(progress, fb->data);

	while(gtk_events_pending() > 0)
	    gtk_main_iteration();

	return(0);
}

/*
 *      Got match callback, used for EDVFindObjectByName() and similar
 *      functions.
 */
static void EDVFindBarMatchExcerptCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
)
{
	edv_findbar_struct *fb = EDV_FINDBAR(data);
	if(fb == NULL)
	    return;

	if(fb->match_cb != NULL)
	    fb->match_cb(
		path,			/* Path */
		lstat_buf,		/* Stats */
		excerpt,		/* Excerpt */
		line_index,		/* Line Index */
		fb->data		/* Data */
	    );

	while(gtk_events_pending() > 0)
	    gtk_main_iteration();
}

static void EDVFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf, gpointer data
)
{
	EDVFindBarMatchExcerptCB(path, lstat_buf, NULL, -1, data);
}

/*
 *	Find "activate" signal callback.
 *
 *	This starts the search process.
 */
static void EDVFindBarFindCB(GtkWidget *widget, gpointer data)
{
	gboolean case_sensitive = FALSE;
	edv_findbar_operation operation;
	const gchar *s;
	gchar *location, *search_string;
	gint total_matches;
	GtkWidget *w;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core;
	edv_findbar_struct *fb = EDV_FINDBAR(data);
	if(fb == NULL)
	    return;

	if(fb->freeze_count > 0)
	    return;

	core = fb->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;


	/* Reset values for upcomming find operation */
	fb->freeze_count++;
	fb->stop_count = 0;
	EDVFindBarUpdateMenus(fb);


	/* Get find operation */
	operation = EDVFindBarCurrentFindOP(fb);

	/* Get copy of location */
	s = NULL;
	switch(fb->role)
	{
	  case EDV_FINDBAR_ROLE_DISK_OBJECT:
	  case EDV_FINDBAR_ROLE_ARCHIVE_OBJECT:
	    if(fb->get_location_cb != NULL)
		s = fb->get_location_cb(fb, fb->data);
	    break;
	  case EDV_FINDBAR_ROLE_RECYCLED_OBJECT:
	    s = EDV_GET_S(EDV_CFG_PARM_FILE_RECYCLE_BIN_INDEX);
	    break;
	}
	location = STRDUP((s != NULL) ? s : "/");

	/* Get copy of search string */
	s = NULL;
	w = fb->search_combo;
	if(w != NULL)
	{
	    GtkCombo *combo = GTK_COMBO(w);
	    GtkEntry *entry = GTK_ENTRY(combo->entry);
	    s = gtk_entry_get_text(entry);
	}
	search_string = STRDUP((s != NULL) ? s : "");
	/* No wild cards in search string? */
	if((strchr(search_string, '*') == NULL) &&
	   (strchr(search_string, '?') == NULL) &&
	   (operation == EDV_FINDBAR_FIND_OBJECT_NAME)
	)
	{
	    /* Tack on two '*' characters to the beginning and end of
	     * the search string.
	     */
	    gchar *strptr = g_strdup_printf("*%s*", search_string);
	    g_free(search_string);
	    search_string = strptr;
	}
	/* Record search string history */
	EDVFindBarSetSearch(fb, search_string, TRUE);

	/* Case sensitive */
	case_sensitive = GTK_TOGGLE_BUTTON_GET_ACTIVE(
	    fb->case_sensitive_check
	);


	/* Call start find callback to notify about start of find */
	if(fb->start_cb != NULL)
	    fb->start_cb(fb, fb->data);


	/* Perform find procedure by find operation code */
	total_matches = 0;
	switch(operation)
	{
	  case EDV_FINDBAR_FIND_OBJECT_NAME:
	    switch(fb->role)
	    {
	      case EDV_FINDBAR_ROLE_DISK_OBJECT:
		total_matches = EDVFindObjectByName(
		    location, search_string,
		    FALSE,			/* Do not recurse */
		    FALSE,			/* Do not follow links */
		    case_sensitive,
		    fb,
		    EDVFindBarProgressCB,
		    EDVFindBarMatchCB
		);
		break;

	      case EDV_FINDBAR_ROLE_RECYCLED_OBJECT:
		total_matches = EDVFindRecycledObjectByName(
		    location,		/* Recycled objects index file */
 		    search_string,
		    case_sensitive,
		    fb,
		    EDVFindBarProgressCB,
		    EDVFindBarMatchCB
		);
		break;

	      case EDV_FINDBAR_ROLE_ARCHIVE_OBJECT:
		total_matches = EDVFindArchiveObjectByName(
		    core,
		    location,		/* Archive */
		    search_string,
		    case_sensitive,
		    fb,
		    EDVFindBarProgressCB,
		    EDVFindBarMatchCB
		);
		break;
	    }
	    break;

	  case EDV_FINDBAR_FIND_OBJECT_CONTENT:
	    switch(fb->role)
	    {
	      case EDV_FINDBAR_ROLE_DISK_OBJECT:
		total_matches = EDVFindObjectByContent(
		    location, search_string,
		    FALSE,			/* Do not recurse */
		    FALSE,			/* Do not follow links */
		    case_sensitive,		/* Case sensitive */
		    fb,
		    EDVFindBarProgressCB,
		    EDVFindBarMatchExcerptCB
		);
		break;

	      case EDV_FINDBAR_ROLE_RECYCLED_OBJECT:
		total_matches = EDVFindRecycledObjectByContent(
		    location,		/* Recycled objects index file */
		    search_string,
		    case_sensitive,
		    fb,
		    EDVFindBarProgressCB,
		    EDVFindBarMatchExcerptCB
		);
		break;

	      case EDV_FINDBAR_ROLE_ARCHIVE_OBJECT:
		/* Finding archive object by content is not supported yet */
		total_matches = 0;
		break;
	    }
	    break;
	}


	/* Tally */
	if(fb->status_progress_cb != NULL)
	    fb->status_progress_cb(0.0f, fb->data);

	if(total_matches > 0)
	{
	    gchar *buf = g_strdup_printf(
		"Matched %i object%s",
		total_matches,
		(total_matches == 1) ? "" : "s"
	    );
	    if(fb->status_message_cb != NULL)
		fb->status_message_cb(
		    buf,
		    fb->data
		);
	    g_free(buf);
	}
	else
	{
	    if(fb->status_message_cb != NULL)
		fb->status_message_cb(
		    "No objects found",
		    fb->data
		);
	}


	fb->freeze_count--;

	EDVFindBarUpdateMenus(fb);


	/* Call end find callback to notify about end of find */
	if(fb->end_cb != NULL)
	    fb->end_cb(fb, total_matches, fb->data);


	g_free(location);
	g_free(search_string);
}

/*
 *	Stop callback.
 */
static void EDVFindBarStopCB(GtkWidget *widget, gpointer data)
{
	edv_findbar_struct *fb = EDV_FINDBAR(data);
	if(fb == NULL)
	    return;

	fb->stop_count++;
}

/*
 *      find bar widget "enter_notify_event" or "leave_notify_event"
 *      signal callback.
 */
static gint EDVFindBarCrossingCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	GtkWidget *w;
	const gchar *mesg = NULL;
	edv_findbar_struct *fb = EDV_FINDBAR(data);
	if((widget == NULL) || (crossing == NULL) || (fb == NULL))
	    return(status);

	/* Get event type */
	etype = (gint)crossing->type;
	w = widget;

	/* Handle by event type */
	switch(etype)
	{
	  case GDK_ENTER_NOTIFY:
	    if(w == fb->case_sensitive_check)
		mesg =
#if defined(PROG_LANGUAGE_SPANISH)
"Especifica si los iguales de cuerda no deben ser el casos sensibles ni"
#elif defined(PROG_LANGUAGE_FRENCH)
"Spécifie si les allumettes de ficelle doivent être des cas sensibles ou pas"
#elif defined(PROG_LANGUAGE_GERMAN)
"Gibt an, wenn Schnur Gegenstücke Fall empfindlich oder nicht sein sollen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Non specifica se i fiammiferi di cordicella dovrebbero essere dei casi sensibili o"
#elif defined(PROG_LANGUAGE_DUTCH)
"Specificeert indien koord stellen geval gevoelig of niet zouden moeten zijn"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Especifica se partidas de barbante devem ser caso sensível ou não"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Spesifiserer om snorkamper er tilfelle sensitiv eller ikke"
#else
"Specifies if string matches should be case sensitive or not"
#endif
		;
	    else if(w == fb->stop_btn)
		mesg =
#if defined(PROG_LANGUAGE_SPANISH)
"Especifica si el proceso de la búsqueda debe recurse en subdirectorios"
#elif defined(PROG_LANGUAGE_FRENCH)
"Spécifie si le procédé de recherche devrait recurse dans subdirectories"
#elif defined(PROG_LANGUAGE_GERMAN)
"Gibt an, wenn das suche verfahren recurse in unterverzeichnisse soll"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Specifica se il processo di ricerca dovrebbe il recurse nelle directory secondarie"
#elif defined(PROG_LANGUAGE_DUTCH)
"Specificeert indien het zoektocht proces recurse in subdirectories zal"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Especifica se o processo de busca devia recurse em subdirectories"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Spesifiserer om letingssom prosessen skal recurse inn i subdirectories"
#else
"Specifies if the search process should recurse into subdirectories"
#endif
		;
	    break;
	}

	if(fb->status_message_cb != NULL)
	    fb->status_message_cb(mesg, fb->data);

	return(status);
}

/*
 *      Sets the search combo of the given find bar to the value of
 *      the string s.
 *
 *      If record_history is TRUE then the current value will be recorded
 *      on the combo's list before setting of the new value.
 *      Duplicate values will not be recorded.
 */
void EDVFindBarSetSearch(
	edv_findbar_struct *fb, const gchar *s,
	gboolean record_history
)
{
	GtkCombo *combo;
	gchar *new_s;


	if((fb == NULL) || (s == NULL))
	    return;

	combo = (GtkCombo *)fb->search_combo;
	if(combo == NULL)
	    return;

	/* Copy given path as the new path */
	new_s = STRDUP(s);

	/* Check for no change in value */
	if(new_s != NULL)
	{
	    const gchar *s = gtk_entry_get_text(GTK_ENTRY(combo->entry));
	    if(s != NULL)
	    {
		/* No change in value? */
		if(!strcmp(s, new_s))
		{
		    g_free(new_s);
		    new_s = NULL;
		    return;
		}
	    }
	}

	/* Get old value from combo's entry and record it on the list */
	if(record_history)
	{
	    gchar *old_s = STRDUP(
		gtk_entry_get_text(GTK_ENTRY(combo->entry))
	    );
	    if(old_s != NULL)
	    {
		GUIComboAddItem(GTK_WIDGET(combo), old_s);
		g_free(old_s);
	    }
	}

	/* Set new value on entry and deallocate coppied new_s */
	if(new_s != NULL)
	{
	    gtk_entry_set_text(GTK_ENTRY(combo->entry), new_s);
	    g_free(new_s);
	}
}


/*
 *      Returns the current find operation code, one of EDV_FINDBAR_FIND_*.
 */
edv_findbar_operation EDVFindBarCurrentFindOP(edv_findbar_struct *fb)
{
	pulist_struct *pulist;

	if(fb == NULL)
	    return(EDV_FINDBAR_FIND_OBJECT_NAME);

	pulist = PUListBoxGetPUList(fb->find_op_pulistbox);
	if(pulist == NULL)
	    return(EDV_FINDBAR_FIND_OBJECT_NAME);

	return((edv_findbar_operation)PUListGetItemData(
	    pulist,
	    PUListGetSelectedLast(pulist)
	));
}



/*
 *	Create a new find bar.
 */
edv_findbar_struct *EDVFindBarNew(
	gpointer core, GtkWidget *parent,
	edv_findbar_role role,
	const gchar *(*get_location_cb)(
		edv_findbar_struct *, gpointer
	),
	void (*start_cb)(
		edv_findbar_struct *, gpointer
	),
	void (*end_cb)(
		edv_findbar_struct *, gint, gpointer 
	),
	void (*match_cb)(
	    const gchar *, const struct stat *,
	    const gchar *, gint,
	    gpointer
	),
	void (*status_message_cb)(const gchar *, gpointer),
	void (*status_progress_cb)(gfloat, gpointer),
	gpointer data
)
{
	const gint border_minor = 2;
	gint i;
	gpointer combo_rtn;
	GList *glist;
	GtkWidget *w, *parent2, *parent3;
	GtkCombo *combo;
	pulist_struct *pulist;
	pulistbox_struct *pulistbox;
	edv_findbar_struct *fb = EDV_FINDBAR(
	    g_malloc0(sizeof(edv_findbar_struct))
	);
	if(fb == NULL)
	    return(fb);

	fb->freeze_count = 0;
	fb->stop_count = 0;
	fb->core = core;
	fb->role = role;
	fb->get_location_cb = get_location_cb;
	fb->start_cb = start_cb;
	fb->end_cb = end_cb;
	fb->match_cb = match_cb;
	fb->status_message_cb = status_message_cb;
	fb->status_progress_cb = status_progress_cb;
	fb->data = data;

	/* Create the toplevel GtkHBox */
	fb->toplevel = w = gtk_hbox_new(FALSE, border_minor);
	gtk_container_border_width(GTK_CONTAINER(w), 2);
	gtk_container_add(GTK_CONTAINER(parent), w);
	parent2 = w;

	/* GtkHBox for the find operation */
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;

	w = gtk_label_new(
#if defined(PROG_LANGUAGE_SPANISH)
"Buscar"
#elif defined(PROG_LANGUAGE_FRENCH)
"Découverte"
#elif defined(PROG_LANGUAGE_GERMAN)
"Fund"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Trovare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Vondst"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ache"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Funn"
#else
"Find"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	fb->find_op_pulistbox = pulistbox = PUListBoxNew(
	    parent3,
	    150, -1
	);
	pulist = PUListBoxGetPUList(pulistbox);
	PUListBoxMap(pulistbox);
#if defined(PROG_LANGUAGE_SPANISH)
	i = PUListAddItem(pulist, "Por Nombre de archivo");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Por Contenido");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_FRENCH)
	i = PUListAddItem(pulist, "Nom D'Objet");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Contenu D'Objet");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_GERMAN)
	i = PUListAddItem(pulist, "Wenden Sie Gegen Namen Ein");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Wenden Sie Gegen Inhalt Ein");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_ITALIAN)
	i = PUListAddItem(pulist, "Il Nome Di Oggetto");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Il Contenuto Di Oggetto");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_DUTCH)
	i = PUListAddItem(pulist, "Heb Bezwaar Naam");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Heb Bezwaar Inhoud");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	i = PUListAddItem(pulist, "O Nome De Objeto");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "O Conteúdo De Objeto");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	i = PUListAddItem(pulist, "Protester Navn");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Protester Innhold");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#else
	i = PUListAddItem(pulist, "Object Name");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_NAME);
	i = PUListAddItem(pulist, "Object Content");
	PUListSetItemData(pulist, i, (gpointer)EDV_FINDBAR_FIND_OBJECT_CONTENT);
#endif
	PUListBoxSetLinesVisible(pulistbox, i + 1);

	/* Search string combo */
	glist = NULL;
	w = GUIComboCreate(
#if defined(PROG_LANGUAGE_SPANISH)
"Palabra"
#elif defined(PROG_LANGUAGE_FRENCH)
"Egaler"
#elif defined(PROG_LANGUAGE_GERMAN)
"Anpassend"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Uguagliare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Passend"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Combinar"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Passende"
#else
"Matching"
#endif
	    ":",
	    "",			/* No initial value */
	    glist,		/* List */
	    25,			/* Max items */
	    &combo_rtn,
	    fb,
	    EDVFindBarFindCB,
	    NULL
	);
	fb->search_combo = (GtkWidget *)combo_rtn;
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);

	w = fb->search_combo;
	combo = GTK_COMBO(w);
	gtk_combo_set_case_sensitive(combo, TRUE);

	w = combo->entry;
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
	EDVEntrySetDND(core, w);
#if 0
	if(search_string != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), search_string);
#endif

	/* Case sensitive check */
	fb->case_sensitive_check = w = gtk_check_button_new_with_label(
#if defined(PROG_LANGUAGE_SPANISH)
"Sensible May/min."
#elif defined(PROG_LANGUAGE_FRENCH)
"Reconnaître Sensible"
#elif defined(PROG_LANGUAGE_GERMAN)
"Fall Empfindlich"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Imballare Sensibile"
#elif defined(PROG_LANGUAGE_DUTCH)
"Sluit Gevoelig In"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Caso Sensível"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Tilfelle Sensitiv"
#else
"Case Sensitive"
#endif
	);
	GTK_TOGGLE_BUTTON(w)->active = FALSE;
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVFindBarCrossingCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVFindBarCrossingCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Verifique esto para discriminar Mayusculas y minusculas"
#elif defined(PROG_LANGUAGE_FRENCH)
"Vérifier ceci pour égaler des ficelles reconnaissent sensiblement"
#elif defined(PROG_LANGUAGE_GERMAN)
"Prüfen sie dies, schnüren fall empfindlich anzupassen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Controllare questo per uguagliare le cordicelle imballano sensibilmente"
#elif defined(PROG_LANGUAGE_DUTCH)
"Controleer dit om bij koorden geval gevoelig te passen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Verifique isto combinar caso de barbantes sensivelmente"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Sjekk dette passe snortilfelle sensitivt"
#else
"Check this to match strings case sensitively"
#endif
	);
	gtk_widget_show(w);

	w = gtk_vseparator_new();
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Stop button */
	fb->stop_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_stop_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVFindBarStopCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVFindBarCrossingCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVFindBarCrossingCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Pare el procedimiento actual del hallazgo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Arrêter la procédure actuelle de découverte"
#elif defined(PROG_LANGUAGE_GERMAN)
"Halten Sie das jetzige Fund Verfahren auf"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Fermare la corrente trova la procedura"
#elif defined(PROG_LANGUAGE_DUTCH)
"Stop de huidig vondst procedure"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Pare a corrente achar procedimento"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stans den nåværendee funnprosedyre"
#else
"Stop the current find procedure"
#endif
	);
	gtk_widget_show(w);



	EDVFindBarUpdateMenus(fb);

	return(fb);
}


/*
 *	Updates the Find Bar's widgets to reflect current values.
 */
void EDVFindBarUpdateMenus(edv_findbar_struct *fb)
{
	gboolean sensitive;
	edv_core_struct *core;

	if(fb == NULL)
	    return;

	core = fb->core;
	if(core == NULL)
	    return;

#define DO_SHOW		\
{ if(w != NULL) gtk_widget_show(w); }
#define DO_HIDE		\
{ if(w != NULL) gtk_widget_hide(w); }
#define DO_SET_CHECK_STATE	\
{ GUIMenuItemSetCheck(w, state, FALSE); }

	/* Update buttons */
	sensitive = (fb->freeze_count > 0) ? TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(fb->stop_btn, sensitive)

	sensitive = (fb->freeze_count == 0) ? TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(fb->search_combo, sensitive);
	GTK_WIDGET_SET_SENSITIVE(fb->case_sensitive_check, sensitive);
	GTK_WIDGET_SET_SENSITIVE(
	    PUListBoxGetToplevel(fb->find_op_pulistbox),
	    sensitive
	);

#undef DO_SHOW
#undef DO_HIDE
#undef DO_SET_SENSITIVE
#undef DO_SET_CHECK_STATE
}

/*
 *	Maps the Find Bar.
 */
void EDVFindBarMap(edv_findbar_struct *fb)
{
	GtkWidget *w = (fb != NULL) ? fb->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_show(w);
}

/*
 *	Unmaps the Find Bar.
 */
void EDVFindBarUnmap(edv_findbar_struct *fb)
{
	GtkWidget *w = (fb != NULL) ? fb->toplevel : NULL;
	if(w == NULL)
	    return;   

	gtk_widget_hide(w);
}

/*
 *	Deletes the Find Bar.
 */
void EDVFindBarDelete(edv_findbar_struct *fb)
{
	if(fb == NULL)
	    return;

	PUListBoxDelete(fb->find_op_pulistbox);
	GTK_WIDGET_DESTROY(fb->search_combo);
	GTK_WIDGET_DESTROY(fb->case_sensitive_check);
	GTK_WIDGET_DESTROY(fb->stop_btn);
	GTK_WIDGET_DESTROY(fb->toplevel);
	g_free(fb);
}
