#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <unistd.h>		/* For unlink() */

#include "../include/string.h"
#include "../include/strexp.h"
#include "../include/disk.h"

#include "guiutils.h"
#include "cdialog.h"

#include "editor.h"
#include "editorcb.h"
#include "editorop.h"
#include "editorfio.h"

#include "viewer.h"
#include "viewerfio.h"

#include "manedit.h"
#include "maneditop.h"
#include "config.h"


void EditorSyntaxHighlight(
	editor_struct *editor, GtkWidget *w,
	gint cursor_pos, gint start_pos, gint end_pos
);

void EditorDoFetchValues(editor_struct *editor, GtkCTreeNode *branch);
void EditorDoApplyValues(editor_struct *editor, GtkCTreeNode *branch);
void EditorDoClearValues(editor_struct *editor);

void EditorDoPreview(
	editor_struct *editor, GtkCTreeNode *branch,
	gint viewer_num
);

gboolean EditorDoFind(
	editor_struct *editor, GtkText *text,
	gchar *haystack, gchar *needle,
	gint haystack_len, gint start_pos,
	gboolean case_sensitive,
	gboolean move_to,
	gboolean *search_wrapped
);
gboolean EditorDoReplace(
	editor_struct *editor, GtkText *text,
	gchar *haystack, gchar *needle, const gchar *replacement,
	gint haystack_len, gint start_pos,
	gboolean case_sensitive
);
gint EditorDoReplaceAll(
	editor_struct *editor, GtkText *text,
	gchar *needle, const gchar *replacement,
	gboolean case_sensitive
);


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


/*
 *	Performs syntax highlighting on the GtkText widget w.
 *
 *	Any given position input values may be -1. If all are -1
 *	then entire buffer will have syntax highlighting performed on
 *	it.
 *
 *	Avoid making this call if the text widget is frozen.
 */
void EditorSyntaxHighlight(
	editor_struct *editor, GtkWidget *w,
	gint cursor_pos, gint start_pos, gint end_pos
)
{
	gboolean in_tag, in_sym;
	gint c, orig_cur_pos, cur_pos, text_start, text_end;
	gboolean orig_has_selection;
	gint orig_sel_start = -1, orig_sel_end = -1;
	GtkStyle *style_ptr;
	GtkText *text = (GtkText *)w;
	GtkEditable *editable = (GtkEditable *)w;
	medit_core_struct *core_ptr;
	medit_styles_list_struct *styles_list;
	if((editor == NULL) || (w == NULL))
	    return;

	if(!editor->initialized)
	    return;

	/* No syntax highlighting? */
	if(!editor->syntax_highlighting)
	    return;

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

	styles_list = &core_ptr->styles_list;

	/* Set text bounds */
	text_start = 0;		/* Duh */
	text_end = (gint)gtk_text_get_length(text);

	/* Make sure start and end positions are in bounds */
	if(cursor_pos >= text_end)
	    cursor_pos = text_end - 1;
	if(start_pos >= text_end)
	    start_pos = text_end - 1;
	if(end_pos > text_end)
	    end_pos = text_end;


	/* Use start position as cursor position if cursor position
	 * is invalid and start position is valid.
	 */
	if((cursor_pos < 0) && (start_pos > -1))
	    cursor_pos = start_pos;

	/* Record original cursor position */
	orig_cur_pos = gtk_editable_get_position(editable);

	/* Record original selection */
	orig_has_selection = editable->has_selection;
	if(orig_has_selection)
	{
	    orig_sel_start = editable->selection_start_pos;
	    orig_sel_end = editable->selection_end_pos;
	}

	/* Cursor position defined? */
	if(cursor_pos > -1)
	{
	    /* Set starting cursor position */
	    cur_pos = cursor_pos;

	    /* Itterate backwards untill a deliminator is encountered,
	     * if a deliminator is encountered then it will be colored and
	     * then the style will be updated for regular or tag
	     * text and in_tag will be set accordingly.
	     * If no tags were encountered then regular style will
	     * be set.
	     */
	    in_tag = FALSE;
	    in_sym = FALSE;
gtk_text_freeze(text);
	    while(cur_pos >= text_start)
	    {
		c = GTK_TEXT_INDEX(text, cur_pos);
		/* Is character a tag deliminator start? */
		if(c == '<')
		{
		    style_ptr = styles_list->edit_text_tag_deliminator;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    "<",
			    1
			);
		    }
		    in_tag = TRUE;
		    cur_pos++;	/* Seek past tag */
		    break;
		}
		/* Is character an tag deliminator end? */
		else if(c == '>')
		{
		    style_ptr = styles_list->edit_text_tag_deliminator;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    ">",
			    1
			);
		    }
		    cur_pos++;	/* Seek past tag */
		    break;
		}
		/* Is character a start of a symbol representation? */
		else if(c == '&')
		{
		    style_ptr = styles_list->edit_text_tag_symrep;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    "&",
			    1
			);
		    }
		    in_sym = TRUE;
		    cur_pos++;  /* Seek past symbol rep */
		    break;
		}
		/* Skip symbol representation end ';' when itterating
		 * backwards.
		 */
		else
		{
		    cur_pos--;
		}
	    }
	    /* Make sure current position stays at or above text_start */
	    if(cur_pos < text_start)
		cur_pos = text_start;

	    if(TRUE)
	    {
		/* Itterate to end of text or to a start or end
		 * deliminator after end_pos.
		 */
		while(cur_pos < text_end)
		{
		    c = GTK_TEXT_INDEX(text, cur_pos);
		    /* Is character a tag deliminator start? */
		    if((c == '<') && !in_tag)
		    {
			style_ptr = styles_list->edit_text_tag_deliminator;
			if(style_ptr != NULL)
			{
			    gtk_text_set_point(text, cur_pos);
			    gtk_text_forward_delete(text, 1);
			    gtk_text_insert(
				text,
				style_ptr->font,
				&style_ptr->fg[GTK_STATE_NORMAL],
				NULL,
				"<",
				1
			    );         
			}
			in_tag = TRUE;	/* Mark as in a tag */

			/* Encountered a deliminator after end_pos? */
			if(end_pos > -1)
			{
			    if(cur_pos > end_pos)
				break;
			}
		    }
		    /* Is character a tag deliminator end? */
		    else if((c == '>') && in_tag)
		    {
			style_ptr = styles_list->edit_text_tag_deliminator;
			if(style_ptr != NULL)
			{
			    gtk_text_set_point(text, cur_pos);
			    gtk_text_forward_delete(text, 1);
			    gtk_text_insert(
				text,
				style_ptr->font,
				&style_ptr->fg[GTK_STATE_NORMAL],
				NULL,
				">",
				1  
			    );
			}
			in_tag = FALSE;	/* Mark no longer in a tag */

			/* Encountered a deliminator after end_pos? */
			if(end_pos > -1)
			{
			    if(cur_pos > end_pos)
				break;
			}
		    }
		    /* Is character a symbol representation start? */
		    else if((c == '&') && !in_sym)
		    {
			style_ptr = styles_list->edit_text_tag_symrep;
			if(style_ptr != NULL)
			{
			    gtk_text_set_point(text, cur_pos);
			    gtk_text_forward_delete(text, 1);
			    gtk_text_insert(
				text,
				style_ptr->font,
				&style_ptr->fg[GTK_STATE_NORMAL],
				NULL,
				"&",
				1
			    );
			}
			in_sym = TRUE;  /* Mark as in a sym */

			/* Encountered a deliminator after end_pos? */
			if(end_pos > -1)
			{
			    if(cur_pos > end_pos)
				break;
			}
		    }
		    /* Is character a symbol representation end? */
		    else if((c == ';') && in_sym)
		    {
			style_ptr = styles_list->edit_text_tag_symrep;
			if(style_ptr != NULL)
			{
			    gtk_text_set_point(text, cur_pos);
			    gtk_text_forward_delete(text, 1);
			    gtk_text_insert(
				text,
				style_ptr->font,
				&style_ptr->fg[GTK_STATE_NORMAL],
				NULL,
				";",
				1
			    );
			}
			in_sym = FALSE;  /* Mark as no longer in sym */

			/* Encountered a deliminator after end_pos? */
			if(end_pos > -1)
			{
			    if(cur_pos > end_pos)
				break;
			}
		    }
		    else
		    {
			if(in_tag)
			    style_ptr = styles_list->edit_text_tag_text;
			else if(in_sym)
			    style_ptr = styles_list->edit_text_tag_symrep;
			else
			    style_ptr = styles_list->edit_text_standard;
			if(style_ptr != NULL)
			{
			    gchar tmp_str[2];

			    tmp_str[0] = (char)c;
			    tmp_str[1] = '\0';

			    gtk_text_set_point(text, cur_pos);
			    gtk_text_forward_delete(text, 1);
			    gtk_text_insert(
				text,
				style_ptr->font,
				&style_ptr->fg[GTK_STATE_NORMAL],
				NULL,
				tmp_str,
				1
			    );
			}
		    }

		    /* Increment to next position */
		    cur_pos++;
		}
	    }
gtk_text_thaw(text);
	}
	else
	{
	    /* Highlight entire buffer */

	    /* Set cur_pos at start of text */
	    cur_pos = text_start;

	    /* Reset in tag marker */
	    in_tag = FALSE;
	    in_sym = FALSE;

	    /* Itterate to end of text */
gtk_text_freeze(text);
	    while(cur_pos < text_end)
	    {
		c = GTK_TEXT_INDEX(text, cur_pos);
		/* Is character a tag deliminator start? */
		if((c == '<') && !in_tag)
		{
		    style_ptr = styles_list->edit_text_tag_deliminator;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    "<",
			    1
			);
		    }
		    in_tag = TRUE;	/* Mark as in a tag */
		}
		/* Is character a tag deliminator end? */
		else if((c == '>') && in_tag)
		{
		    style_ptr = styles_list->edit_text_tag_deliminator;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    ">",
			    1
			);
		    }
		    in_tag = FALSE; /* Mark no longer in a tag */
		}
		/* Is character a symbol representation start? */
		else if((c == '&') && !in_sym)
		{
		    style_ptr = styles_list->edit_text_tag_symrep;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1); 
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    "&", 
			    1   
			);   
		    }
		    in_sym = TRUE;	/* Mark as in a sym */
		}
		/* Is character a symbol representation end? */
		else if((c == ';') && in_sym)
		{
		    style_ptr = styles_list->edit_text_tag_symrep;
		    if(style_ptr != NULL)
		    {
			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    ";",
			    1
			);
		    }
		    in_sym = FALSE;	/* Mark no longer in a sym */
		}
		else
		{
		    if(in_tag)
			style_ptr = styles_list->edit_text_tag_text;
		    else if(in_sym)
			style_ptr = styles_list->edit_text_tag_symrep;
		    else
			style_ptr = styles_list->edit_text_standard;
		    if(style_ptr != NULL)
		    {
			gchar tmp_str[2];

			tmp_str[0] = (char)c;
			tmp_str[1] = '\0';

			gtk_text_set_point(text, cur_pos);
			gtk_text_forward_delete(text, 1);
			gtk_text_insert(   
			    text,
			    style_ptr->font,
			    &style_ptr->fg[GTK_STATE_NORMAL],
			    NULL,
			    tmp_str,
			    1
			);
		    }
		}
		cur_pos++;
	    }
gtk_text_thaw(text);
	}

	/* Set back original cursor position */
	gtk_text_set_point(text, orig_cur_pos);
	gtk_editable_set_position(editable, orig_cur_pos);

	/* Set back originally had selection? */
	if(orig_has_selection)
	{
	    gtk_editable_select_region(
		editable, orig_sel_start, orig_sel_end
	    );
	}


}


/*
 *	Procedure to fetch values from the given branch and map
 *	the appropriate editor panel widgets on the editor.
 */
void EditorDoFetchValues(editor_struct *editor, GtkCTreeNode *branch)
{
	gint i;
	gint map_editor_panel_type = -1;
	GtkStyle *style;
	GtkWidget *w;
	editor_item_struct *item;
	medit_core_struct *core_ptr;
	medit_styles_list_struct *styles_list;

	if((editor == NULL) || (branch == NULL))
	    return;

	if(!editor->initialized)
	    return;

	/* Check if already processing */
	if(editor->processing)
	    return;

	/* Get core pointer and subsequently the styles list */
	core_ptr = MEDIT_CORE(editor->core_ptr);
	styles_list = (core_ptr != NULL) ?
	    &core_ptr->styles_list : NULL;


	/* Get pointer to item data from ctree branch node */
	item = EditorBranchGetData(
	    GTK_CTREE(editor->layout_ctree), branch
	);


	/* Map and unmap editor panel widgets */
	if(item != NULL)
	    map_editor_panel_type = item->type;

	for(i = 0; i < MEDIT_EDIT_PANEL_MAX; i++)
	{
	    w = editor->edit_panel_vbox[i];
	    if(w == NULL)
		continue;

	    if(i == map_editor_panel_type)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}


	/* Load data into widgets by the type of item data from
	 * the given branch.
	 */
	switch(map_editor_panel_type)
	{
	  case EditorItemTypeFile:

	    break;

	  case EditorItemTypeHeader:
	    /* Set entry widgets */
	    w = editor->header_name_entry;
	    if(w != NULL)
	    {
		if((item == NULL) ?
		    0 : (item->header_name != NULL)
		)
		    gtk_entry_set_text(
			GTK_ENTRY(w), item->header_name
		    );
	    }

	    w = editor->header_section_number_entry;
	    if(w != NULL)
	    {
		if((item == NULL) ?    
		    0 : (item->header_section_number != NULL)
		)
		    gtk_entry_set_text(
			GTK_ENTRY(w), item->header_section_number
		    );
	    }     

	    w = editor->header_version_entry;
	    if(w != NULL)
	    {
		if((item == NULL) ?
		    0 : (item->header_version != NULL)
		)
		    gtk_entry_set_text(
			GTK_ENTRY(w), item->header_version
		    );
	    }

	    w = editor->header_author_entry;
	    if(w != NULL)
	    {
		if((item == NULL) ?
		    0 : (item->header_author != NULL)
		)
		    gtk_entry_set_text(
			GTK_ENTRY(w), item->header_author
		    );
	    }

	    w = editor->header_catagory_entry;
	    if(w != NULL)
	    {
		if((item == NULL) ?
		    0 : (item->header_catagory != NULL)
		)
		    gtk_entry_set_text(
			GTK_ENTRY(w), item->header_catagory
		    );
	    }

	    /* Get pointer to header text widget */
	    w = editor->header_text;
	    if(w != NULL)
	    {
		gint text_len;

		/* Remove all text */
		text_len = gtk_text_get_length(GTK_TEXT(w));
		gtk_text_set_point(GTK_TEXT(w), 0);
		gtk_text_freeze(GTK_TEXT(w));
		gtk_text_forward_delete(GTK_TEXT(w), text_len);
		gtk_text_thaw(GTK_TEXT(w));

		/* Item data valid? */
		if(item != NULL)
		{
		    gchar *line_ptr;


		    if(styles_list != NULL)
			style = styles_list->edit_text_standard;
		    else
			style = NULL;

		    /* Set lines from item to text widget */
		    gtk_text_freeze(GTK_TEXT(w));
		    for(i = 0; i < item->total_lines; i++)
		    {
			line_ptr = item->line[i];
			if(line_ptr == NULL)
			    continue;

			/* Insert this line */
			gtk_text_insert(
			    GTK_TEXT(w),
			    (style != NULL) ? style->font : NULL,
			    NULL,	/* Foreground color */
			    NULL,	/* Background color */
			    line_ptr,
			    -1
			);

			/* Need to insert a newline character at end */
			gtk_text_insert(
			    GTK_TEXT(w),
			    (style != NULL) ? style->font : NULL,
			    NULL,       /* Foreground color */
			    NULL,       /* Background color */
			    "\n",
			    -1
			);
		    }
		    gtk_text_thaw(GTK_TEXT(w));
		}
	    }
	    break;

	  case EditorItemTypeSection:
	    /* Section name entry */
	    w = editor->section_name_entry;
	    if(w != NULL)
	    {
		gtk_entry_set_text(
		    GTK_ENTRY(w),
		    (item == NULL) ?
			"" : item->section_name
		);
	    }

	    /* Get pointer to section text widget */
	    w = editor->section_text;
	    if(w != NULL)
	    {
		gint text_len;

		/* Remove all text */
		text_len = gtk_text_get_length(GTK_TEXT(w));
		gtk_text_set_point(GTK_TEXT(w), 0);
		gtk_text_freeze(GTK_TEXT(w));
		gtk_text_forward_delete(GTK_TEXT(w), text_len);
		gtk_text_thaw(GTK_TEXT(w));

		/* Item data valid? */  
		if(item != NULL)
		{
		    gchar *line_ptr;


		    if(styles_list != NULL)
			style = styles_list->edit_text_standard;
		    else  
			style = NULL;

		    /* Set lines from item to text widget */
		    gtk_text_freeze(GTK_TEXT(w));
		    for(i = 0; i < item->total_lines; i++)
		    {
			line_ptr = item->line[i];
			if(line_ptr == NULL)
			    continue;

			/* Insert this line */
			gtk_text_insert(
			    GTK_TEXT(w),
			    (style != NULL) ? style->font : NULL,
			    NULL,       /* Foreground color */
			    NULL,       /* Background color */
			    line_ptr,
			    -1
			);

			/* Need to insert a newline character at end */
			gtk_text_insert( 
			    GTK_TEXT(w),
			    (style != NULL) ? style->font : NULL,
			    NULL,       /* Foreground color */
			    NULL,       /* Background color */
			    "\n",
			    -1
			);
		    }
		    gtk_text_thaw(GTK_TEXT(w));

		    /* Syntax highlighting for entire buffer */
		    EditorSyntaxHighlight(
			editor, w,
			-1, -1, -1
		    );
		}
	    }
	    break;
	}

	/* Update menus */
	EditorUpdateMenus(editor);
}


/*
 *      Procedure to apply values to the given branch from the appropriate
 *	widgets depending on the branch data's type.
 */
void EditorDoApplyValues(editor_struct *editor, GtkCTreeNode *branch)
{
	GtkWidget *w;
	editor_item_struct *item;
	medit_pixmaps_list_struct *pixmaps_list;
	medit_core_struct *core_ptr;

	if((editor == NULL) || (branch == NULL))
	    return;

	if(!editor->initialized)
	    return;

	/* Check if already processing */
	if(editor->processing)   
	    return;

	core_ptr = MEDIT_CORE(editor->core_ptr);
	if(core_ptr == NULL)
	    return;

	pixmaps_list = &core_ptr->pixmaps_list;


	/* Get pointer to item data from ctree branch node */
	item = EditorBranchGetData(
	    GTK_CTREE(editor->layout_ctree), branch
	);
	if(item == NULL)
	{
	    /* No data to apply */
	    return;
	}

	/* Check ctree branch node data type */
	switch(item->type)
	{
	  case EditorItemTypeFile:

	    break;
			    
	  case EditorItemTypeHeader:
	    /* Header name entry */
	    w = editor->header_name_entry;
	    if((w != NULL) && (item != NULL))
	    {
		g_free(item->header_name);
		item->header_name = STRDUP(gtk_entry_get_text(GTK_ENTRY(w)));
	    }
	    /* Header section number entry */
	    w = editor->header_section_number_entry;  
	    if((w != NULL) && (item != NULL))
	    {
		g_free(item->header_section_number);
		item->header_section_number = STRDUP(gtk_entry_get_text(GTK_ENTRY(w)));
	    }
	    /* Header version entry */
	    w = editor->header_version_entry;
	    if((w != NULL) && (item != NULL))
	    {
		g_free(item->header_version);
		item->header_version = STRDUP(gtk_entry_get_text(GTK_ENTRY(w)));
	    }
	    /* Header author entry */
	    w = editor->header_author_entry;
	    if((w != NULL) && (item != NULL))
	    {
		g_free(item->header_author);
		item->header_author = STRDUP(gtk_entry_get_text(GTK_ENTRY(w)));
	    }
	    /* Header catagory entry */
	    w = editor->header_catagory_entry;
	    if((w != NULL) && (item != NULL))
	    {
		g_free(item->header_catagory); 
		item->header_catagory = STRDUP(gtk_entry_get_text(GTK_ENTRY(w)));
	    }
	    /* Header text */
	    w = editor->header_text;
	    if((w != NULL) && (item != NULL))
	    {
		gchar *s;

		/* Delete existing lines */
		strlistfree(item->line, item->total_lines);
		item->line = NULL;
		item->total_lines = 0;

		/* Get new lines */
		s = gtk_editable_get_chars(
		    GTK_EDITABLE(w), 0, -1
		);
		item->line = strchrexp(s, '\n', &item->total_lines);
		g_free(s);
	    }
	    break;

	  case EditorItemTypeSection:
	    /* Section name entry */
	    w = editor->section_name_entry;
	    if((w != NULL) && (item != NULL))
	    {
		gchar *s = gtk_entry_get_text(GTK_ENTRY(w));

		g_free(item->section_name);
		item->section_name = STRDUP(s);

		if(s != NULL)
		    gtk_ctree_node_set_pixtext(
			GTK_CTREE(editor->layout_ctree),
			branch,		/* Branch node */
			0,		/* Column */
			s,
			MEDIT_LIST_ICON_TEXT_SPACING,
			pixmaps_list->manpage_section_20x20,
			pixmaps_list->manpage_section_20x20_mask
		    );
	    }

	    /* Section text */
	    w = editor->section_text;
	    if(w != NULL)
	    {
		gchar *s;

		/* Delete existing lines */
		strlistfree(item->line, item->total_lines);
		item->line = NULL;
		item->total_lines = 0;

		/* Get new lines */
		s = gtk_editable_get_chars(GTK_EDITABLE(w), 0, -1);
		item->line = strchrexp(s, '\n', &item->total_lines);
		g_free(s);
	    }
	    break;
	}

	/* Update menus */
	EditorUpdateMenus(editor);
}

/*
 *	Clears all data on the editor's branch item editing widgets but
 *	does not unmap anything.
 */
void EditorDoClearValues(editor_struct *editor)
{
	GtkWidget *w;

	if(editor == NULL)
	    return;

	if(!editor->initialized)
	    return;

	/* Check if already processing */
	if(editor->processing)
	    return;

	/* Clear file branch item data widgets */

	/* Header entry widgets */
	w = editor->header_name_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	w = editor->header_section_number_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	w = editor->header_version_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	w = editor->header_author_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	w = editor->header_catagory_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	/* Header text */
	w = editor->header_text;
	if(w != NULL)
	{
	    gint text_len;

	    /* Remove all text */
	    text_len = gtk_text_get_length(GTK_TEXT(w));
	    gtk_text_set_point(GTK_TEXT(w), 0);
	    gtk_text_freeze(GTK_TEXT(w));
	    gtk_text_forward_delete(GTK_TEXT(w), text_len);
	    gtk_text_thaw(GTK_TEXT(w));
	}


	/* Section name entry */
	w = editor->section_name_entry;
	if(w != NULL)
	    gtk_entry_set_text(GTK_ENTRY(w), "");

	/* Section text */
	w = editor->section_text;
	if(w != NULL)
	{
	    gint text_len;

	    /* Remove all text */
	    text_len = gtk_text_get_length(GTK_TEXT(w));
	    gtk_text_set_point(GTK_TEXT(w), 0);
	    gtk_text_freeze(GTK_TEXT(w));
	    gtk_text_forward_delete(GTK_TEXT(w), text_len);
	    gtk_text_thaw(GTK_TEXT(w));
	}
}


/*
 *	Checks if viewer_num is a valid viewer and sets it up to load
 *	an exported manual page converted to man formatted output.
 */
void EditorDoPreview(
	editor_struct *editor, GtkCTreeNode *branch,  
	gint viewer_num
)
{
	gint i, status, editor_num;
	viewer_struct *v;
	medit_core_struct *core_ptr;
	GtkCTreeNode *trunk;
	editor_item_struct *item;
	GtkCTree *ctree;
	gchar *orig_path, *tmp_path, *prog_tmp_path;


	if(editor == NULL)
	    return;

	if(!editor->initialized)
	    return;

	/* Check if processing */
	if(editor->processing)   
	    return;

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

	ctree = (GtkCTree *)editor->layout_ctree;
	if(ctree == NULL)
	    return;


	/* Find our editor pointer on core */
	for(i = 0; i < core_ptr->total_editors; i++)
	{
	    if(core_ptr->editor[i] == editor)
		break;
	}
	if(i < core_ptr->total_editors)
	    editor_num = i;
	else
	    editor_num = -1;

	/* Check if viewer_num is valid on the core structure */
	v = ((viewer_num >= 0) && (viewer_num < core_ptr->total_viewers)) ?
	    core_ptr->viewer[viewer_num] : NULL;
	if(v == NULL)
	{
	    gchar *s = g_strdup_printf(
"EditorDoPreview(): Viewer number %i is invalid.",
		viewer_num
	    );
	    CDialogGetResponse(
"Non-critical internal error!",
		s,
"The viewer number given to this function is\n\
invalid. Cannot continue with preview.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    g_free(s);
	    return;
	}

	/* Got viewer pointer, now check if the viewer is mapped and
	 * initialized
	 */
	if(!v->initialized || !v->map_state)
	    return;

	/* Set up viewer's values */
	v->editor_num = editor_num;

	/* Get given branch's trunk */
	trunk = EditorItemGetToplevel(editor, branch);
	if(trunk == NULL)
	{
	    CDialogGetResponse(
"Non-critical internal error!",
"EditorDoPreview(): Given branch has no trunk.\n",
"The branch pointer given to this function has\n\
no trunk branch pointer that could be found.\n\
Cannot continue with preview.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    return;
	}

	/* Get trunk item pointer */
	item = EditorBranchGetData(ctree, trunk);
	if(item == NULL)
	{
	    CDialogGetResponse(
"Non-critical internal error!",
"EditorDoPreview(): No item data found for given\n\
branch's trunk branch.\n",
"The branch pointer given to this function has\n\
a trunk branch with no item data that contains\n\
the information of the manual pages. Cannot\n\
continue with preview.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    return;
	}


	/* Create tempory files directory for this program as needed
	 * (return string needs to be deallocated).
	 */
	prog_tmp_path = MEditCreateTempDir(core_ptr);
	if(prog_tmp_path == NULL)
	{
	    CDialogGetResponse(
"Non-critical internal error!",
"EditorDoPreview(): MEditCreateTempDir() did not return a path.",
"Could not obtain program tempory file directory path,\n\
and thus unable to generate tempory files for preview.\n\
Cannot continue with preview.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    return;
	}

	/* Create a tempory file to save trunk to it */
	tmp_path = g_strdup(PrefixPaths(
	    prog_tmp_path, "previewXXXXXX"
	));
	mkstemp(tmp_path);

	/* Here we play a trick, first record the full path name
	 * on the item data and replace it with our tempory file path
	 * so that it gets saved to that. After saving we set back
	 * the file path name on the item data to what it was
	 */
	orig_path = item->full_path;
	item->full_path = tmp_path;
	tmp_path = NULL;

	/* Save branch with an updated filename to the tempory file */
	EditorFileSave(editor, branch);

	/* Clear viewer and load new manual page file */
	ViewerViewTextRecordScrollPositions(v);
	ViewerTextDelete(v, 0, -1);
	ViewerTextInsertPosition(v, 0);
	status = ViewerLoadFile(
	    v, item->full_path, orig_path
	);

	/* Remove tempory saved manual page */
	if(item->full_path != NULL)
	    unlink(item->full_path);


	/* Free tempory path (this was our tmp_path) */
	g_free(item->full_path);

	/* Set back original full file name on item */
	item->full_path = orig_path;
	orig_path = NULL;


	/* Free program tempory files directory path */
	g_free(prog_tmp_path);
	prog_tmp_path = NULL;
}


/*
 *	Procedure to find needle from haystack with respect to
 *	the given text widget.
 *
 *	Both needle and haystack may be modified by this function,
 *	but never deallocated.
 *
 *	Given GtkText widget will be updated and instructed to select
 *	the matched text if a match was made. If case_sensitive is TRUE
 *	then the search will be made case sensitive, and if move_to is
 *	set to TRUE then gtk_editable_set_position() will be called to
 *	scroll to the new matched position.
 *
 *	If haystack_len is -1 then strlen() will be called on haystack
 *	to get the actual length. Given start_pos will be sanitized,
 *	if start_pos is >= haystack_len then start_pos will be set to 0.
 *
 *	Returns TRUE if a match is made.
 *
 *	Pointer to search_wrapped will be set to TRUE if a match
 *	was made after a search wrapped through the entire buffer.
 */
gboolean EditorDoFind(  
	editor_struct *editor, GtkText *text,
	gchar *haystack, gchar *needle,
	gint haystack_len, gint start_pos,
	gboolean case_sensitive,
	gboolean move_to,
	gboolean *search_wrapped
)
{
	gchar *bp, *match_ptr;


	/* Reset search wrapped */
	if(search_wrapped != NULL)
	    (*search_wrapped) = FALSE;

	/* Inputs valid? */
	if((editor == NULL) || (text == NULL) ||
	   (haystack == NULL) || (needle == NULL)
	)
	    return(FALSE);

	/* Need to get haystack length ourself? */
	if(haystack_len < 0)
	    haystack_len = strlen(haystack);

	/* Empty haystack? */
	if(haystack_len < 1)
	    return(FALSE);

	/* Empty needle? */
	if((*needle) == '\0')
	    return(FALSE);

	EditorSetStatusMessage(editor, "Finding...");

	/* Sanitize starting position */
	if(start_pos >= haystack_len)
	    start_pos = 0;
	else if(start_pos < 0)
	    start_pos = 0;


	/* Case insensitive search? */
	if(!case_sensitive)
	{
	    /* Need to modify buffers to upper case */

	    /* Modify ending portion of haystack */
	    bp = (gchar *)(haystack + start_pos);
	    while((*bp) != '\0')
	    {
		(*bp) = toupper(*bp);
		bp++;
	    }

	    /* Modify needle */
	    bp = needle;
	    while((*bp) != '\0')
	    {
		(*bp) = toupper(*bp);
		bp++;
	    }
	}


	/* Begin search from starting position */
	match_ptr = strstr(
	    (gchar *)(haystack + start_pos),
	    needle
	);
	if(match_ptr == NULL)
	{
	    /* Did not get match, so start from beginning of haystack
	     * and search again. First check if this is case insensitive.
	     */
	    if(!case_sensitive)
	    {
		/* Toupper the beginning portion of the buffer */
		bp = haystack;
		while(bp < (gchar *)(haystack + start_pos))
		{
		    (*bp) = toupper(*bp);
		    bp++;
		}
	    }
	    /* Now try match again for the second time starting form the
	     * beginning.
	     */
	    match_ptr = strstr(
		(gchar *)(haystack + 0),
		needle
	    );
	    if(match_ptr != NULL)
	    {
		/* Got match the second time, now mark search wrapped
		 * as true.
		 */
		if(search_wrapped != NULL)
		    (*search_wrapped) = TRUE;
	    }
	}

	EditorSetStatusMessage(editor, "Find done");

	/* Did we get a match? */
	if(match_ptr == NULL)
	{
	    /* Sorry, no match made */
	    return(FALSE);
	}
	else
	{
	    /* Got match */
	    gint match_start_pos = (gint)(match_ptr - haystack);
	    gint needle_len = strlen(needle);

	    if(match_start_pos < 0)
		match_start_pos = 0;

	    /* Now modify the text widget, set new cursor position and
	     * select the matched string.
	     */
	    if(TRUE)
	    {
		GtkEditable *editable = (GtkEditable *)text;

		/* Scroll to new matched position? */
		if(move_to)
		{
		    gtk_text_set_point(text, match_start_pos);
		    gtk_editable_set_position(
			editable, match_start_pos
		    );
		}

		if(needle_len > 0)
		{
		    gtk_editable_select_region(
			editable,
			match_start_pos,
			match_start_pos + needle_len
		    );
		}
	    }

	    return(TRUE);
	}
}


/*
 *	Procedure to replace the `next' occurance of needle in
 *	haystack on the given GtkText widget with replacement.
 *
 *	This function calls EditorDoFind() so the given haystack
 *	and needle may be modified.
 *
 *	This function will freeze and thaw the given GtkText widget but
 *	not set its sensitivity.
 *
 *	If the given GtkText widget already has a selection marked
 *	then no find will be made and the selected text will be
 *	replaced with the replacement.
 *
 *	Returns TRUE if a replace was done and FALSE if no replace
 *	was made or error.
 */
gboolean EditorDoReplace(
	editor_struct *editor, GtkText *text,
	gchar *haystack, gchar *needle, const gchar *replacement,
	gint haystack_len, gint start_pos,
	gboolean case_sensitive
)
{
	GtkEditable *editable;
	gint delete_len, replacement_len;
	medit_core_struct *core_ptr;
	medit_styles_list_struct *styles_list;
	GtkStyle *style_ptr;


	/* Inputs valid? */
	if((editor == NULL) || (text == NULL) ||
	   (haystack == NULL) || (needle == NULL) || (replacement == NULL)
	)
	    return(FALSE);

	/* Get core structure pointer */
	core_ptr = (medit_core_struct *)editor->core_ptr;
	if(core_ptr == NULL)
	    return(FALSE);


	/* Get pointer to styles list structure on core structure */
	styles_list = &core_ptr->styles_list;


	editable = GTK_EDITABLE(text);

	/* Get length of replacement string */
	replacement_len = strlen(replacement);


	/* GtkText already has selection? */
	if(editable->has_selection)
	{
	    /* Text already selected */

	    EditorSetStatusMessage(editor, "Replacing...");

	    /* Set new starting position, overriding the given one */
	    start_pos = editable->selection_start_pos;

	    /* Get length of text to delete, can be 0 */
	    delete_len = MAX(
		editable->selection_end_pos - editable->selection_start_pos,
		0
	    );
	}
	else
	{
	    /* Text not selected, so we need to find first */
	    gboolean search_wrapped;
	    gboolean got_match = EditorDoFind(
		editor, text,
		haystack, needle,
		haystack_len, start_pos,
		case_sensitive,
		TRUE,			/* Scroll to matched position */
		&search_wrapped
	    );

	    EditorSetStatusMessage(editor, "Replacing...");

	    /* Didn't get match? */
	    if(!got_match)
	    {
		/* No match, then that means we have nothing to replace */
		EditorSetStatusMessage(editor, "Replace done");
		return(FALSE);
	    }

	    /* Now hopefully the text widget has its selection and marked */
	    if(!editable->has_selection)
	    {
		/* Something went wrong, supposedly got match but GtkText
		 * widget was not marked as having a selection.
		 */
		EditorSetStatusMessage(editor, "Replace done");
		return(FALSE);
	    }

	    /* Set new starting position, overriding the given one */
	    start_pos = editable->selection_start_pos;

	    /* Get length of text to delete, can be 0 */
	    delete_len = MAX(
		editable->selection_end_pos - editable->selection_start_pos,
		0
	    );
	}

	/* Now we do replace, start_pos and delete_len should be valid
	 * now.
	 */

	/* Any text to delete? */
	if(delete_len > 0)
	{
	    if(use_text_delete)
	    {
		EditorTextDeleteCB(
		    editable, start_pos, start_pos + delete_len,
		    (gpointer)editor
		);
		gtk_text_freeze(text);
		gtk_text_set_point(text, (guint)start_pos);
		gtk_text_forward_delete(text, (guint)delete_len);
		gtk_text_thaw(text);
	    }
	    else
	    {
		gtk_text_freeze(text);
		gtk_editable_delete_text(
		    editable, start_pos, start_pos + delete_len
		);
		gtk_text_thaw(text);
	    }
	}

	/* Insert replacement string? */
	if(replacement_len > 0)
	{
	    GdkFont *font;
	    gint position;


	    /* Select GdkFont from styles list (can be NULL) */
	    style_ptr = styles_list->edit_text_standard;
	    font = ((style_ptr == NULL) ? NULL : style_ptr->font);

	    /* Set insert point and insert replacement text */
	    gtk_text_set_point(text, start_pos);
	    gtk_text_freeze(text);
	    gtk_text_insert(
		text,
		font,
		NULL, NULL,
		replacement,
		replacement_len
	    );
	    gtk_text_thaw(text);

	    /* Call insert callback after inserting text */
	    position = start_pos;
	    EditorTextInsertCB(
		editable, (const gchar *)replacement,
		replacement_len, &position,
		(gpointer)editor
	    );

	    /* Move cursor position after replacement */
	    gtk_text_set_point(text, start_pos + replacement_len);
	    gtk_editable_set_position(
		editable, start_pos + replacement_len
	    );

	    /* Do not select replacement */
	}


	EditorSetStatusMessage(editor, "Replace done");

	return(TRUE);
}


/*
 *      Procedure to replace the all occurances (if any) of needle in
 *      the given GtkText widget's text with replacement.
 *
 *      This function will freeze and thaw the given GtkText widget but
 *      not set its sensitivity.      
 *
 *      This function calls EditorDoFind() so the given haystack
 *      and needle may be modified. 
 *
 *	Returns the number of replacements made or 0 on error or no
 *	matches.
 */
gint EditorDoReplaceAll(
	editor_struct *editor, GtkText *text,
	gchar *needle, const gchar *replacement,
	gboolean case_sensitive
)
{
	GtkEditable *editable;
	gint delete_len, replacement_len;
	medit_core_struct *core_ptr;
	medit_styles_list_struct *styles_list;
	GtkStyle *style_ptr;
	gint orig_pos;
	gint replace_count = 0;


	/* Inputs valid? */
	if((editor == NULL) || (text == NULL) ||
	   (needle == NULL) || (replacement == NULL)
	)
	    return(replace_count);

	/* Get core structure pointer */
	core_ptr = (medit_core_struct *)editor->core_ptr;
	if(core_ptr == NULL)
	    return(replace_count);

	/* Get pointer to styles list structure on core structure */
	styles_list = &core_ptr->styles_list;


	editable = GTK_EDITABLE(text);

	/* Get length of replacement string */
	replacement_len = strlen(replacement);

	/* Record initial position */
	orig_pos = (gint)gtk_editable_get_position(editable);


	if(TRUE)
	{
	    /* Seek back to beginning and do a through search, for each
	     * matched find, replace it with replacement.
	     */
	    gboolean search_wrapped;
	    gboolean got_match;
	    gint start_pos, next_pos = 0;
	    gchar *haystack = NULL;

	    do
	    {
		/* Get latest haystack from text widget */
		g_free((gpointer)haystack);
		haystack = gtk_editable_get_chars(editable, 0, -1);

		/* Do find */
		got_match = EditorDoFind(
		    editor, text,
		    haystack, needle,
		    -1, next_pos,
		    case_sensitive,
		    FALSE,		/* No scroll to matched position */
		    &search_wrapped
		);
		if(!got_match)
		    break;

		/* If the search wrapped then do not perform any more
		 * replacements since it may have matched an embedded
		 * part of needle in the replacement for example.
		 */
		if(search_wrapped)
		    break;

		/* Now hopefully the text widget has its selection and
		 * marked.
		 */
		if(!editable->has_selection)
		{
		    /* Something went wrong, supposedly got match but
		     * GtkText widget was not marked as having a
		     * selection.
		     */
		    break;
		}

		/* Get start of selection as start position */
		start_pos = editable->selection_start_pos;

		/* Set next starting position, being past the current
		 * selected starting position and past what would be
		 * the length of the replacement string.
		 */
		next_pos = start_pos + replacement_len;

		/* Get length of text to delete, can be 0 */
		delete_len = MAX(
		    editable->selection_end_pos -
			editable->selection_start_pos,
		    0
		);

		/* Any text to delete? */
		if(delete_len > 0)
		{
		    if(use_text_delete)
		    {
			EditorTextDeleteCB(
			    editable, start_pos, start_pos + delete_len,
			    (gpointer)editor
			);
			gtk_text_freeze(text);
			gtk_text_set_point(text, (guint)start_pos);
			gtk_text_forward_delete(text, (guint)delete_len);
			gtk_text_thaw(text);
		    }
		    else
		    {
			gtk_text_freeze(text);
			gtk_editable_delete_text(
			    editable, start_pos, start_pos + delete_len
			);
			gtk_text_thaw(text);
		    }
		}

		/* Insert replacement string? */
		if(replacement_len > 0)
		{
		    GdkFont *font;
		    gint position;


		    /* Select GdkFont from styles list (can be NULL) */
		    style_ptr = styles_list->edit_text_standard;
		    font = ((style_ptr == NULL) ? NULL : style_ptr->font);

		    /* Set insert point and insert replacement text */
		    gtk_text_set_point(text, start_pos);
		    gtk_text_freeze(text);
		    gtk_text_insert(
			text,
			font,
			NULL, NULL,
			replacement,
			replacement_len
		    );
		    gtk_text_thaw(text);

		    /* Call insert callback after inserting text */
		    position = start_pos;
		    EditorTextInsertCB(
			editable, (const gchar *)replacement,
			replacement_len, &position,
			(gpointer)editor
		    );

		    /* Move cursor position after replacement */
		    gtk_text_set_point(text, start_pos + replacement_len);
#if 0
		    gtk_editable_set_position(
			editable, start_pos + replacement_len
		    );
#endif
		    /* Do not select replacement */
		}

		replace_count++;

	    } while(1);

	    /* Deallocate haystack as needed */
	    g_free((gpointer)haystack);
	    haystack = NULL;
	}

	/* Set back original position */
	if(orig_pos > (gint)gtk_text_get_length(text))
	    orig_pos = (gint)gtk_text_get_length(text);
	gtk_text_set_point(text, orig_pos);
/*	gtk_editable_set_position(editable, orig_pos); */


	return(replace_count);
}
