#include <gtk/gtk.h>

#include "../include/string.h"

#include "guiutils.h"

#include "editor.h"
#include "editorfip.h"
#include "editorfipcb.h"
#include "manedit.h"
#include "maneditcb.h"
#include "messages.h"
#include "config.h"

#include "images/manedit_48x48.xpm"
#include "images/manedit_16x16.xpm"
#include "images/mp_viewer_16x16.xpm"

#include "images/icon_close_20x20.xpm"
#include "images/icon_goto_20x20.xpm"
#include "images/icon_search_20x20.xpm"
#include "images/icon_stop_20x20.xpm"


gint EditorFIPAddItem(
	editor_fip_struct *fip,
	const gchar *excerpt,
	gint line_num,
	const gchar *name,
	const gchar *manual_page,
	GtkCTreeNode *found_branch,
	gpointer branch_data,
	gint text_sel_start, gint text_sel_end
);
void EditorFIPItemDelete(editor_fip_item_struct *item);

void EditorFIPSetBusy(editor_fip_struct *fip);
void EditorFIPSetReady(editor_fip_struct *fip);

static void EditorFIPCreateStatusBar(
	editor_fip_struct *fip, GtkWidget *parent
);

editor_fip_struct *EditorFIPNew(gpointer core_ptr, gpointer editor_ptr);
void EditorFIPUpdateMenus(editor_fip_struct *fip);
void EditorFIPSetStatusPosition(
	editor_fip_struct *fip, gint row, gint column
);
void EditorFIPSetStatusMessage(
	editor_fip_struct *fip, const gchar *mesg
);
void EditorFIPSetStatusProgress(
	editor_fip_struct *fip, gfloat percent
);
void EditorFIPReset(editor_fip_struct *fip, gboolean need_unmap);
void EditorFIPMap(editor_fip_struct *fip);
void EditorFIPUnmap(editor_fip_struct *fip);
void EditorFIPDelete(editor_fip_struct *fip);


#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)


#define FIP_DEF_WIDTH	640
#define FIP_DEF_HEIGHT	480

#define FIP_BTN_WIDTH	(100 + (2 * 3))
#define FIP_BTN_HEIGHT	(30 + (2 * 3))


/*
 *	Procedure to add a new item to the results clist on the dialog.
 */
gint EditorFIPAddItem(
	editor_fip_struct *fip,
	const gchar *excerpt,
	gint line_num,
	const gchar *name,
	const gchar *manual_page,
	GtkCTreeNode *found_branch,
	gpointer branch_data,
	gint text_sel_start, gint text_sel_end
)
{
	gint row = -1;
	gchar **text;   
	GtkCList *clist;
	editor_fip_item_struct *item;

	if(fip == NULL)
	    return(row);

	if(!fip->initialized)
	    return(row);

	clist = (GtkCList *)fip->results_clist;
	if(clist == NULL)
	    return(row);

	/* Allocate a new item structure */
	item = EDITOR_FIP_ITEM(g_malloc0(
	    sizeof(editor_fip_item_struct)
	));
	if(item == NULL)
	    return(row);

	item->editor_fip = fip;
	item->clist = clist;
	item->found_branch = found_branch;
	item->branch_data = branch_data;
	item->text_sel_start = text_sel_start;
	item->text_sel_end = text_sel_end;
	item->line_num = line_num;

	/* Allocate text for columns on results clist */
	text = (gchar **)g_malloc0(4 * sizeof(gchar *));
	if(text != NULL)
	{
	    gchar *s;

	    if(line_num < 0)
		s = g_strdup_printf("n/a");
	    else
		s = g_strdup_printf("#%i", line_num + 1);

	    text[0] = g_strdup((excerpt != NULL) ? excerpt : "");
	    text[1] = g_strdup((s != NULL) ? s : "");
	    text[2] = g_strdup((name != NULL) ? name : "");
	    text[3] = g_strdup((manual_page != NULL) ? manual_page : "");

	    g_free(s);

	    row = gtk_clist_append(clist, text);

	    strlistfree(text, 4);
	    text = NULL;
	}

	if(row > -1)
	{
	    /* Set item structure pointer to new row on results clist
	     * and also set the destroy notify callback function */
	    gtk_clist_set_row_data_full(
		clist, (gint)row, (gpointer)item,
		EditorFIPItemDestroyCB
	    );
	}
	else
	{
	    /* Could not add item pointer to row that was not created,
	     * so delete item structure */
	    EditorFIPItemDelete(item);
	}

	return(row);
}


/*
 *	Deletes the results item structure and all its allocated
 *	resources.
 */
void EditorFIPItemDelete(editor_fip_item_struct *item)
{
	if(item == NULL)
	    return;

	g_free(item);
}

/*
 *      Marks dialog as busy.
 */
void EditorFIPSetBusy(editor_fip_struct *fip)
{
	GtkWidget *w;
	GdkCursor *cur;
	medit_core_struct *core_ptr;

	if(fip == NULL)
	    return;  

	core_ptr = (medit_core_struct *)fip->core_ptr;
	if(core_ptr == NULL)
	    return;

	w = fip->toplevel;
	if(w == NULL)
	    return;

	cur = core_ptr->cursors_list.busy;
	if(cur == NULL)
	    return;

	if(GTK_WIDGET_NO_WINDOW(w))
	    return;

	gdk_window_set_cursor(w->window, cur);
	gdk_flush(); 
}       

/*
 *      Marks dialog as ready (opposite affect of EditorFIPSetBusy()).
 */
void EditorFIPSetReady(editor_fip_struct *fip)
{
	GtkWidget *w;

	if(fip == NULL)
	    return;

	w = fip->toplevel;
	if(w == NULL)
	    return;

	if(GTK_WIDGET_NO_WINDOW(w))
	    return;

	gdk_window_set_cursor(w->window, NULL);
	gdk_flush();
}


/*
 *      Creates the status bar for the dialog, this function should be
 *      called by EditorFIPNew().
 *
 *      Given parent is assumed to be a vbox.
 */
static void EditorFIPCreateStatusBar(
	editor_fip_struct *fip, GtkWidget *parent
)
{
	gboolean show_tooltips = TRUE;
	GtkWidget *w, *parent2, *parent3;
	GtkAdjustment *adj;


	/* Main hbox for status bar and window map buttons */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent = w;

	/* Handle for status bar */
	w = gtk_handle_box_new();
	fip->status_bar_dock = w;
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Toplevel hbox inside dock */
	w = gtk_hbox_new(FALSE, 0);
	fip->status_bar_toplevel = w;
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;

	/* Main frame in toplevel */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, -1, 25);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;
 
	/* Table in main frame */
	w = gtk_table_new(1, 2, FALSE);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;


	/* Progress bar */
	adj = (GtkAdjustment *)gtk_adjustment_new(0, 1, 100, 0, 0, 0);
	w = gtk_progress_bar_new_with_adjustment(adj);
	fip->status_bar_progress = w;
	gtk_widget_set_usize(w, 100, -1);
	gtk_progress_bar_set_orientation(
	    GTK_PROGRESS_BAR(w), GTK_PROGRESS_LEFT_TO_RIGHT
	);
	gtk_progress_bar_set_bar_style(
	    GTK_PROGRESS_BAR(w), GTK_PROGRESS_CONTINUOUS
	);
	gtk_progress_set_activity_mode(
	    GTK_PROGRESS(w), FALSE
	);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 0, 1,
	    0,
	    GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	gtk_widget_show(w);
	
	
	/* Label */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_container_border_width(GTK_CONTAINER(w), 1);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    1, 2, 0, 1,
	    GTK_SHRINK | GTK_EXPAND | GTK_FILL,
	    GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	gtk_widget_show(w);
	parent3 = w;
	    
	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_show(w);
	parent3 = w;
	  
	w = gtk_label_new("");
	fip->status_bar_label = w;  
	gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 2);
	gtk_widget_show(w);


	/* Handle bar for window map buttons */
	w = gtk_handle_box_new();
/*      editor->window_map_dock = w; */
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;
	
	/* Main frame in toplevel */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, -1, 25);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;
	  
	/* Hbox for window map buttons */
	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;
   
	/* Window map buttons */
	w = (GtkWidget *)GUIButtonPixmap(
	    (guint8 **)manedit_16x16_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_set_usize(w, 25, -1);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(MEditEditorNewCB),
	    (gpointer)fip->core_ptr
	);
	if(show_tooltips)
	    GUISetWidgetTip(w, MEDIT_SB_TT_NEW_EDITOR);
	gtk_widget_show(w);

	w = (GtkWidget *)GUIButtonPixmap(
	    (guint8 **)mp_viewer_16x16_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_set_usize(w, 25, -1);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(MEditViewerNewCB),
	    (gpointer)fip->core_ptr
	);
	if(show_tooltips)
	    GUISetWidgetTip(w, MEDIT_SB_TT_NEW_VIEWER);
	gtk_widget_show(w);
}



/*
 *	Creates a new dialog.
 */
editor_fip_struct *EditorFIPNew(gpointer core_ptr, gpointer editor_ptr)
{
	GtkWidget *w, *fw, *menu, *left_vbox, *right_vbox;
	GtkWidget *parent, *parent2, *parent3, *parent4;
	GtkWidget *scroll_parent;
	GSList *gslist;
	GtkCList *clist;
	gpointer combo_rtn;
	guint accel_key, accel_mods;
	gpointer accel_group;  
	const gchar *label;
	guint8 **icon;
	gpointer mclient_data;
	void (*func_cb)(GtkWidget *w, gpointer);
	gchar *heading[4];
	medit_cursors_list_struct *cursors_list;
	editor_fip_struct *fip = EDITOR_FIP(g_malloc0(
	    sizeof(editor_fip_struct)
	));
	if(fip == NULL)
	    return(NULL);

	/* Get pointers to resources on core structure */
	if(core_ptr != NULL)
	    cursors_list = &((medit_core_struct *)core_ptr)->cursors_list;
	else
	    cursors_list = NULL;

	/* Reset values */
	fip->initialized = TRUE;
	fip->map_state = FALSE;
	fip->processing = FALSE;

	fip->core_ptr = core_ptr;
	fip->editor_ptr = editor_ptr;


#define DO_ADD_MENU_ITEM_LABEL  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_LABEL, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
}
#define DO_ADD_MENU_ITEM_SUBMENU        \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SUBMENU, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
 if(w != NULL) \
  GUIMenuItemSetSubMenu(w, submenu); \
}
#define DO_ADD_MENU_ITEM_CHECK  \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_CHECK, accel_group, \
  icon, label, accel_key, accel_mods, (gpointer *)&fw, \
  mclient_data, func_cb \
 ); \
}
#define DO_ADD_MENU_SEP \
{ \
 w = GUIMenuItemCreate( \
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL, \
  NULL, NULL, 0, 0, NULL, \
  NULL, NULL \
 ); \
}

	/* Toplevel */  
	w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	fip->toplevel = w;
/*
	gtk_widget_set_events(w,
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EditorFIPKeyEventCB),
	    (gpointer)editor
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EditorFIPKeyEventCB),
	    (gpointer)editor
	);
 */
	gtk_widget_realize(w);   
	GUISetWMIcon(w->window, (guint8 **)manedit_48x48_xpm);
	gtk_widget_set_usize(
	    w,
	    FIP_DEF_WIDTH,
	    -1
	);
	gtk_window_set_policy(
	    GTK_WINDOW(w),
	    TRUE, TRUE, FALSE
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "destroy",
	    GTK_SIGNAL_FUNC(EditorFIPDestroyCB),
	    (gpointer)fip
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(EditorFIPCloseCB),
	    (gpointer)fip
	);
	parent = w;

	/* Main vbox */
	w = gtk_vbox_new(FALSE, 0);
	fip->main_vbox = w;
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;


	/* Hbox to hold left and right columns of vboxes */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Left side vbox, this will hold most of the find and results
	 * widgets.
	 */
	w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	left_vbox = w;

	/* Right side vbox, this holds the find and close buttons */
	w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
/*	gtk_widget_set_usize(w, 120, -1); */
	gtk_widget_show(w);
	right_vbox = w;



	/* Create widgets for left side vbox */
	parent2 = left_vbox;

	/* Match all or match any radio buttons vbox */
	w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
	gtk_widget_show(w);
	parent3 = w;

	/* Hbox for match all radio button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Reset gslist and create match all radio button */
	gslist = NULL;
	w = gtk_radio_button_new_with_label(gslist, "Match All Occurances");
	fip->match_all_radio = w;
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 2);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), TRUE);
	gtk_widget_show(w);
	gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));

	/* Hbox for match any radio button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Match any radio button */
	w = gtk_radio_button_new_with_label(gslist, "Match Any Occurance");
	fip->match_any_radio = w;
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 2);  
	gtk_widget_show(w);
	gslist = gtk_radio_button_group(GTK_RADIO_BUTTON(w));

	/* Hbox for match phrase radio button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Match phrase radio button */
	w = gtk_radio_button_new_with_label(gslist, "Match Phrase Occurance");
	fip->match_phrase_radio = w;
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 2);
	gtk_widget_show(w);


	/* Hbox for the find combo */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
	gtk_widget_show(w);
	parent3 = w;

	/* Find combo */
	w = (GtkWidget *)GUIComboCreate(
	    "Of:",		/* Label */
	    NULL,               /* Initial text value */
	    NULL,               /* Initial glist of items */
	    20,                 /* Max items */
	    &combo_rtn,         /* Combo widget return */
	    (gpointer )fip,
	    EditorFIPFindActivateCB,
	    NULL
	);
	fip->find_combo = (GtkWidget *)combo_rtn;
	gtk_combo_set_case_sensitive(
	    (GtkCombo *)combo_rtn,
	    TRUE
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 2);
	gtk_widget_show(w);


	/* Hbox for additional options */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
	gtk_widget_show(w);
	parent3 = w;


	/* Case sensitive toggle */
	w = gtk_check_button_new_with_label(
	    "Case Sensitive"
	);
	fip->case_sensitive_toggle = w;
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 2);
	gtk_toggle_button_set_active(
	    GTK_TOGGLE_BUTTON(w), FALSE
	);
	gtk_widget_show(w);



	/* Horizontal rule */
	w = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* Hbox for results clist */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 2);
	gtk_widget_show(w);
	parent3 = w;

	/* Scrolled window, parent for results clist */
	w = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(
	    GTK_SCROLLED_WINDOW(w),   
	    GTK_POLICY_AUTOMATIC,
	    GTK_POLICY_AUTOMATIC
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 5);
	gtk_widget_show(w);
	scroll_parent = w;

	/* Results clist */
	heading[0] = "Excerpt";
	heading[1] = "Line";
	heading[2] = "Name";
	heading[3] = "Manual Page";
	w = gtk_clist_new_with_titles(4, heading);
	fip->results_clist = w;
	clist = (GtkCList *)w;
	if(!GTK_WIDGET_NO_WINDOW(w)) 
	{
	    gtk_widget_set_events(
		w,
		GDK_BUTTON_PRESS_MASK
	    );
	    gtk_signal_connect_after(
		GTK_OBJECT(w), "button_press_event",
		GTK_SIGNAL_FUNC(EditorFIPMenuMapCB),
		(gpointer)fip
	    );
	}
	gtk_clist_set_selection_mode(GTK_CLIST(w), GTK_SELECTION_SINGLE);
	gtk_clist_set_sort_type(GTK_CLIST(w), GTK_SORT_ASCENDING);
	gtk_clist_set_sort_column(GTK_CLIST(w), 3);	/* By manual page name */
	gtk_clist_set_row_height(GTK_CLIST(w), MEDIT_LIST_ROW_SPACING);
/*	gtk_clist_set_auto_sort(GTK_CLIST(w), TRUE); */
	gtk_container_add(GTK_CONTAINER(scroll_parent), w);
	gtk_widget_set_usize(w, -1, 240);
	gtk_clist_set_column_width(GTK_CLIST(w), 0, 200);
	gtk_clist_set_column_width(GTK_CLIST(w), 1, 60);
	gtk_clist_set_column_width(GTK_CLIST(w), 2, 80); 
	gtk_clist_set_column_width(GTK_CLIST(w), 3, 100); 
	gtk_clist_set_shadow_type(GTK_CLIST(w), GTK_SHADOW_IN);
	gtk_signal_connect(
	    GTK_OBJECT(w), "select_row",
	    GTK_SIGNAL_FUNC(EditorFIPResultsListSelectCB),
	    (gpointer)fip
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "unselect_row",
	    GTK_SIGNAL_FUNC(EditorFIPResultsListUnselectCB),
	    (gpointer)fip
	);
	gtk_signal_connect( 
	    GTK_OBJECT(w), "click_column",
	    GTK_SIGNAL_FUNC(EditorFIPResultsListColumnClickCB),
	    (gpointer)fip
	);
	gtk_widget_show(w);


	/* Results clist right click menu */
	menu = (GtkWidget *)GUIMenuCreate();
	fip->results_menu = menu;
	accel_group = NULL;
	mclient_data = (gpointer )fip;
 
	if(menu != NULL)
	{
	    icon = (guint8 **)icon_goto_20x20_xpm;
	    label = "Goto";
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EditorFIPGotoCB;
	    DO_ADD_MENU_ITEM_LABEL
	    fip->goto_mi = w;
	}



	/* Begin creating buttons on right side vbox */
	parent2 = right_vbox;

	/* Vbox to group find and clear buttons */
	w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 10);
	gtk_widget_show(w);
	parent3 = w;

	/* Hbox for find button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Find button */
	w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_search_20x20_xpm, "Search", NULL
	);
	fip->find_btn = w;
	gtk_widget_set_usize(w, FIP_BTN_WIDTH, FIP_BTN_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EditorFIPFindCB),
	    (gpointer)fip
	);
	gtk_widget_show(w);

	/* Hbox for stop button */ 
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Stop button */
	w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_stop_20x20_xpm, "Stop", NULL
	);
	fip->stop_btn = w;
	gtk_widget_set_usize(w, FIP_BTN_WIDTH, FIP_BTN_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EditorFIPStopCB), 
	    (gpointer)fip
	);
	gtk_widget_show(w);

	/* Hbox for clear button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Clear button */
	w = GUIButtonPixmapLabelH(
	    NULL, "Clear", NULL
	);
	fip->clear_btn = w;
	gtk_widget_set_usize(w, FIP_BTN_WIDTH, FIP_BTN_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EditorFIPClearCB),
	    (gpointer)fip
	);
	gtk_widget_show(w);


	/* Vbox for grouping just the close button */
	w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 10);
	gtk_widget_show(w);
	parent3 = w;

	/* Hbox for close button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Close button */ 
	w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_close_20x20_xpm, "Close", NULL
	);
	fip->close_btn = w;
	gtk_widget_set_usize(w, FIP_BTN_WIDTH, FIP_BTN_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EditorFIPCloseMCB),
	    (gpointer)fip
	);
	gtk_widget_show(w);




	/* Goto button and other stuff near bottom */
	parent2 = fip->main_vbox;

	/* Horizontal rule */ 
	w = gtk_hseparator_new();   
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Hbox to hold buttons homogeniously */
	w = gtk_hbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 5);
	gtk_widget_show(w);
	parent3 = w;

	/* Goto button */
	w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_goto_20x20_xpm, "Goto", NULL
	);
	fip->goto_btn = w;
	gtk_widget_set_usize(w, FIP_BTN_WIDTH, FIP_BTN_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EditorFIPGotoCB),
	    (gpointer)fip
	);
	gtk_widget_show(w);







	/* Create status bar */
	EditorFIPCreateStatusBar(fip, fip->main_vbox);




#undef DO_ADD_MENU_ITEM_LABEL  
#undef DO_ADD_MENU_ITEM_SUBMENU
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP

	/* Reset values to defaults */
	EditorFIPReset(fip, FALSE);

	/* Update menus */
	EditorFIPUpdateMenus(fip);

	return(fip);
}


/*
 *	Updates menus on dialog.
 */
void EditorFIPUpdateMenus(editor_fip_struct *fip)
{
	static gboolean reenterant = FALSE;
	gboolean sensitivity;
	GtkWidget *w, *menu;

	if(fip == NULL)
	    return;

	if(!fip->initialized)
	    return;

	/* Okay to handle if processing */

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

#define SET_WIDGET_SENSITIVITY		{	\
 if(w != NULL)					\
  gtk_widget_set_sensitive(w, sensitivity);	\
}
#define SET_CHECK_MENU_ITEM_STATE	{	\
 GUIMenuItemSetCheck(w, state, TRUE);		\
}

	/* Results clist pop up menu */
	menu = fip->results_menu;
	if(menu != NULL)
	{
	    w = fip->goto_mi;
	    sensitivity = ((fip->selected_result < 0) ? FALSE : TRUE);
	    if(sensitivity)
		sensitivity = ((fip->processing) ? FALSE : TRUE);
	    SET_WIDGET_SENSITIVITY
	}


	/* Match options radio buttons */
	w = fip->match_all_radio;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

	w = fip->match_any_radio;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

	w = fip->match_phrase_radio;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

	w = fip->case_sensitive_toggle;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY


	/* Find combo */
	w = fip->find_combo;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY


	/* Buttons */
	w = fip->find_btn;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

	w = fip->stop_btn;
	sensitivity = ((fip->processing) ? TRUE : FALSE);
	SET_WIDGET_SENSITIVITY

	w = fip->clear_btn;
	sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

	w = fip->close_btn;
	sensitivity = ((fip->processing) ? FALSE : TRUE);;
	SET_WIDGET_SENSITIVITY

	w = fip->goto_btn;
	sensitivity = ((fip->selected_result < 0) ? FALSE : TRUE);
	if(sensitivity)
	    sensitivity = ((fip->processing) ? FALSE : TRUE);
	SET_WIDGET_SENSITIVITY

#undef SET_WIDGET_SENSITIVITY
#undef SET_CHECK_MENU_ITEM_STATE

	reenterant = FALSE;
}

/*
 *	Updates status message.
 */
void EditorFIPSetStatusMessage(
	editor_fip_struct *fip, const gchar *mesg
)
{
	GtkWidget *w;

	if(fip == NULL)
	    return;

	w = fip->status_bar_label;
	if(w == NULL)
	    return;
 
	gtk_label_set_text(
	    GTK_LABEL(w),
	    (mesg == NULL) ? "" : mesg
	);

	/* Itterate through GTK main function until all events are handled
	 * so that we ensure this label text is updated
	 */
	while(gtk_events_pending() > 0)
	    gtk_main_iteration();
}

/*
 *	Updates the progress bar on the dialog.
 */
void EditorFIPSetStatusProgress(
	editor_fip_struct *fip, gfloat percent
)
{
	GtkWidget *w;
	gfloat p;

	if(fip == NULL)
	    return;

	w = fip->status_bar_progress;
	if(w == NULL)
	    return;  

	/* Negative? Implies just do activity */        
	if(percent < 0.0)
	{
	    GtkAdjustment *adj = GTK_PROGRESS(w)->adjustment;
	    percent = gtk_progress_get_value(GTK_PROGRESS(w)) + 1;

	    if(percent > adj->upper)
		percent = adj->lower;

	    gtk_progress_set_activity_mode(
		GTK_PROGRESS(w), TRUE
	    );
	    gtk_progress_set_value(GTK_PROGRESS(w), percent);

	    /* Itterate through GTK main function until all events are
	     * handled so that we ensure this progress bar setting is
	     * updated.
	     */
	    while(gtk_events_pending() > 0)
		gtk_main_iteration();

	    return;
	}

	if(percent > 1.0)
	    percent = 1.0; 
	    
	gtk_progress_set_activity_mode(
	    GTK_PROGRESS(w), FALSE
	);
	gtk_progress_set_format_string(
	    GTK_PROGRESS(w), "%p%%"
	);
	gtk_progress_set_show_text(
	    GTK_PROGRESS(w), (percent > 0) ? TRUE : FALSE
	);
	
	p = fip->status_bar_progress_pos_last;
	if(p > percent)
	    p = percent;

	while(1)
	{
	    if(p > percent)
		p = percent;

	    gtk_progress_bar_update(
		GTK_PROGRESS_BAR(w), p
	    );   

	    /* Itterate through GTK main function until all events are
	     * handled so that we ensure this progress bar setting is
	     * updated.
	     */
	    while(gtk_events_pending() > 0)
		gtk_main_iteration();
 
	    if(p < percent)
		p += (gfloat)0.05;
	    else
		break;
	}
	 
	fip->status_bar_progress_pos_last = percent;
}

/*
 *      Resets all values to defaults on dialog.
 */
void EditorFIPReset(editor_fip_struct *fip, gboolean need_unmap)
{
	static gboolean reenterant = FALSE;
	GtkWidget *w;

	if(fip == NULL)
	    return;

	if(!fip->initialized) 
	    return;

	/* Still processing? */
	if(fip->processing)
	    return;

	if(reenterant)
	    return;
	else
	    reenterant = TRUE;

	/* Reset values */
	fip->processing = FALSE;
	fip->status_bar_progress_pos_last = 0;
	fip->stop_find_count = 0;
	fip->selected_result = -1;

	/* Reset status messages */
	EditorFIPSetStatusMessage(fip, NULL);
	EditorFIPSetStatusProgress(fip, 0.0);


	/* Match all radio button */
	w = fip->match_all_radio;
	if(w != NULL)
	    gtk_toggle_button_set_active(
		GTK_TOGGLE_BUTTON(w), TRUE
	    );

	/* Case sensitive */
	w = fip->case_sensitive_toggle;
	if(w != NULL)
	    gtk_toggle_button_set_active(
		GTK_TOGGLE_BUTTON(w), FALSE
	    );

	/* Remove all items on the results clist, destroy notify
	 * will be called to delete all item data.
	 */
	w = fip->results_clist;
	if(w != NULL)
	    gtk_clist_clear(GTK_CLIST(w));


	/* Unmap as needed */
	if(need_unmap)
	{
	    w = fip->toplevel;
	    if(w != NULL)
		gtk_widget_hide(w);
	    fip->map_state = FALSE;
	}

	reenterant = FALSE;
}

/*
 *      Maps dialog as needed.
 */
void EditorFIPMap(editor_fip_struct *fip)
{
	GtkWidget *w;
	
	if(fip == NULL)
	    return;
	
	if(!fip->initialized)
	    return;
	
	w = fip->toplevel;
	if(w == NULL)
	    return;
	   
	if(!fip->map_state)
	{
	    fip->map_state = TRUE;
	    gtk_widget_show(w);
	}
}

/*
 *      Unmaps dialog as needed.
 */
void EditorFIPUnmap(editor_fip_struct *fip)
{
	GtkWidget *w;
	
	if(fip == NULL)
	    return;
   
	if(!fip->initialized)
	    return;
	
	w = fip->toplevel;
	if(w == NULL)
	    return;
	
	if(fip->map_state)   
	{
	    fip->map_state = FALSE;
	    gtk_widget_hide(w);
	}
}

/*
 *      Destroys the dialog and all its widgets and allocated resources.
 *
 *      The dialog structure itself will also be deleted.
 */
void EditorFIPDelete(editor_fip_struct *fip)
{
	GtkWidget *w;


	if(fip == NULL)
	    return;
	
	if(fip->initialized)
	{
	    /* Still processing? */
	    if(fip->processing)
		return;

	    /* Reset values to defaults, this will cause any loaded data
	     * to be deleted.
	     */
	    EditorFIPReset(fip, TRUE);


#define DO_DESTROY_WIDGET	\
{ \
 if(w != NULL) \
  gtk_widget_destroy(w); \
}

	    w = fip->results_menu;
	    fip->results_menu = NULL;
	    fip->goto_mi = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->results_clist;
	    fip->results_clist = NULL;
	    DO_DESTROY_WIDGET


	    w = fip->find_combo;
	    fip->find_combo = NULL;
	    DO_DESTROY_WIDGET


	    w = fip->find_btn;
	    fip->find_btn = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->clear_btn;
	    fip->clear_btn = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->stop_btn;
	    fip->stop_btn = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->close_btn;
	    fip->close_btn = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->goto_btn;
	    fip->goto_btn = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->match_all_radio;
	    fip->match_all_radio = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->match_any_radio;
	    fip->match_any_radio = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->match_phrase_radio;
	    fip->match_phrase_radio = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->case_sensitive_toggle;
	    fip->case_sensitive_toggle = NULL;
	    DO_DESTROY_WIDGET



	    w = fip->status_bar_toplevel;
	    fip->status_bar_toplevel = NULL;
	    fip->status_bar_progress = NULL;
	    fip->status_bar_label = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->status_bar_dock;
	    fip->status_bar_dock = NULL;
	    DO_DESTROY_WIDGET


	    w = fip->main_vbox;
	    fip->main_vbox = NULL;
	    DO_DESTROY_WIDGET

	    w = fip->toplevel;
	    fip->toplevel = NULL;
	    DO_DESTROY_WIDGET

#undef DO_DESTROY_WIDGET
	}

	g_free(fip);
}
