#include <signal.h>
#include <locale.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>

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

#include "guiutils.h"
#include "cdialog.h"
#include "fprompt.h"
#include "csd.h"
#include "fsd.h"
#include "fb.h"
#include "pdialog.h"

#include "editor.h"
#include "editorfio.h"
#include "viewer.h"
#include "viewerfio.h"
#include "pref.h"
#include "preffio.h"
#include "prefop.h"
#include "aboutdialog.h"
#include "manedit.h"
#include "maneditop.h"
#include "config.h"

#include "images/icon_manpage_heading_20x20.xpm"
#include "images/icon_manpage_section_20x20.xpm"
#include "images/icon_folder_closed_20x20.xpm"
#include "images/icon_folder_opened_20x20.xpm"
#include "images/icon_manual_closed_20x20.xpm"
#include "images/icon_manual_opened_20x20.xpm"


static void MEditSignalCB(int s);

static gboolean load_font_to_style(GtkStyle *style_ptr, const gchar *font_name);

medit_core_struct *MEditNew(
	gint argc, gchar **argv,
	gint *status_rtn
);
static gint MEditTOCB(gpointer data);
void MEditDelete(medit_core_struct *core);


static int		segfault_count = 0;

gboolean		need_close_all_windows = FALSE;
medit_core_struct	*medit_core_ptr = NULL;
gboolean		use_text_delete;


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


/*
 *	Signal handler.
 */
void MEditSignalCB(int s)
{
	static gboolean doing_crash_save = FALSE;

	switch(s)
	{
	  case SIGINT: 
	  case SIGTERM:
	    need_close_all_windows = TRUE;
	    break;

	  case SIGSEGV:
	    signal(SIGSEGV, MEditSignalCB);	/* Watch for SIGSEGV again */
	    segfault_count++;			/* Increment SIGSEGV count */
	    /* Print warning of segmentation fault */
	    g_printerr(
		"%s triggered a segmentation fault (%i times)\n",
		PROG_NAME,
		segfault_count
	    );
	    /* Already doing crash save? */
	    if(doing_crash_save)
	    {

	    }
	    else
	    {
		/* Mark and perform emergency save procedure */
		doing_crash_save = TRUE;
		MEditDoEmergencySave(medit_core_ptr);
	    }
	    /* Got 3 or more segfaults? */
	    if(segfault_count >= 3)
	    {
		g_printerr(
		    "%s attempting immediate process exit().\n",
		    PROG_NAME
		);
		exit(1);
	    }
	    else
	    {
		need_close_all_windows = TRUE;
	    }
	    break;

	  case SIGPIPE:
	    signal(SIGPIPE, MEditSignalCB);
	    break;
	}
}


/*
 *	Macro used in MEditNew() to load fonts to styles.
 *
 *	Returns TRUE if the font was not able to be loaded, otherwise
 *	FALSE on success.
 */
static gboolean load_font_to_style(GtkStyle *style_ptr, const gchar *font_name)
{
	GdkFont *new_font = gdk_font_load(font_name);

	/* Could not load new font? */
	if(new_font == NULL)
	    return(TRUE);

	/* Unref current font on style structure */
	if(style_ptr->font != NULL)
	    gdk_font_unref(style_ptr->font);

	/* Set newly loaded font to style */
	style_ptr->font = new_font;

	return(FALSE);
}


/*
 *	Initializes all resources for the core structure.
 */
medit_core_struct *MEditNew(
	gint argc, gchar **argv, 
	gint *status_rtn
)
{
	gboolean startup_viewer = FALSE;
	gint	i,
		editor_num = -1,
		viewer_num = -1;
	gchar *cfg_file;
	const gchar	*s, *arg,
			*filename = NULL;
	GdkColormap *colormap;
	GdkWindow *window = (GdkWindow *)GDK_ROOT_PARENT();
	GtkStyle *style_standard;
	editor_struct *editor = NULL;
	viewer_struct *viewer = NULL;
	medit_cursors_list_struct *cursors_list;
	medit_styles_list_struct *styles_list;
	medit_pixmaps_list_struct *pixmaps_list;
	medit_core_struct *core = MEDIT_CORE(g_malloc0(
	    sizeof(medit_core_struct)
	));
	if(core == NULL)
	{
	    *status_rtn = 3;
	    return(NULL);
	}

	/* Set path to default cfg_file, coppied value */
	s = g_getenv("HOME");
	if(s == NULL)
	    s = "/";
	s = PrefixPaths(s, MEDIT_RCFILE_LOCAL);
	if(s == NULL)
	    s = MEDIT_RCFILE_LOCAL;
	cfg_file = STRDUP(s);


	/* Reset the values */
	core->untitled_count = 0;

	core->string_atom = gdk_atom_intern(
	    "STRING",
	    FALSE
	);

	/* Parse arguments */
	for(i = 1; i < argc; i++)
	{
	    arg = argv[i];
	    if(arg == NULL)
		continue;

	    /* Start up with viewer? */
	    if(!g_strcasecmp(arg, "--viewer") ||
	       !g_strcasecmp(arg, "-viewer") ||
	       !g_strcasecmp(arg, "--view") ||
	       !g_strcasecmp(arg, "-view") ||
	       !g_strcasecmp(arg, "--manview") ||
	       !g_strcasecmp(arg, "-manview")
	    )
	    {
		startup_viewer = TRUE;
	    }
	    /* Configuration file? */
	    else if(!g_strcasecmp(arg, "--config") ||
		    !g_strcasecmp(arg, "-config") ||
		    !g_strcasecmp(arg, "--rcfile") ||
		    !g_strcasecmp(arg, "-rcfile") ||
		    !g_strcasecmp(arg, "-f")
	    )
	    {
		i++;
		arg = (i < argc) ? argv[i] : NULL;
		if(arg != NULL)
		{
		    g_free(cfg_file);
		    cfg_file = STRDUP(arg);
		}
		else
		{
		    g_printerr(
			"%s: Requires argument.\n",
			argv[i - 1]
		    );
		}
	    }
	    /* Filename to load on startup? */
	    else if((*arg != '-') && (*arg != '+'))
	    {
		filename = arg;
	    }
	}


	/* Get the default GTK style */
	style_standard = gtk_widget_get_default_style();


	/* Load the cursors */
	cursors_list = &core->cursors_list;
	if(TRUE)
	{
	    cursors_list->busy = gdk_cursor_new(GDK_WATCH);
	    cursors_list->text = gdk_cursor_new(GDK_XTERM);
	}


	/* Load the styles */
	styles_list = &core->styles_list;
	colormap = gdk_colormap_get_system();
	if((window != NULL) && (style_standard != NULL) &&
	   (colormap != NULL)
	)
	{
	    GtkStyle *style_ptr;
	    GdkColor c;

	    /* Each style must have a loaded GdkFont and may have
	     * allocated GdkColors.
	     */

	    /* Heading 1 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING1);
	    }
	    styles_list->heading1_text = style_ptr;

	    /* Heading 2 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		if(load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING2))
		    load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING3);
	    }
	    styles_list->heading2_text = style_ptr;

	    /* Heading 3 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING3);
	    }
	    styles_list->heading3_text = style_ptr;

	    /* Heading 4 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		if(load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING4))
		    load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING5);
	    }
	    styles_list->heading4_text = style_ptr;

	    /* Heading 5 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING5);
	    }
	    styles_list->heading5_text = style_ptr;

	    /* Heading 6 */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING6);
	    }
	    styles_list->heading6_text = style_ptr;


	    /* Heading 1 reverse */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		c = style_ptr->fg[GTK_STATE_NORMAL];
		style_ptr->fg[GTK_STATE_NORMAL] =
		    style_ptr->bg[GTK_STATE_NORMAL];
		style_ptr->bg[GTK_STATE_NORMAL] = c;

		gdk_color_alloc(colormap, &style_ptr->fg[GTK_STATE_NORMAL]);
		gdk_color_alloc(colormap, &style_ptr->bg[GTK_STATE_NORMAL]);

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING1_REV);
	    }
	    styles_list->heading1_rev_text = style_ptr;

	    /* Heading 2 reverse */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		c = style_ptr->fg[GTK_STATE_NORMAL];
		style_ptr->fg[GTK_STATE_NORMAL] =   
		    style_ptr->bg[GTK_STATE_NORMAL];
		style_ptr->bg[GTK_STATE_NORMAL] = c;

		gdk_color_alloc(colormap, &style_ptr->fg[GTK_STATE_NORMAL]);
		gdk_color_alloc(colormap, &style_ptr->bg[GTK_STATE_NORMAL]);

		if(load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING2_REV))
		    load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING3_REV);
	    }
	    styles_list->heading2_rev_text = style_ptr;

	    /* Heading 3 reverse */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		c = style_ptr->fg[GTK_STATE_NORMAL];
		style_ptr->fg[GTK_STATE_NORMAL] =
		    style_ptr->bg[GTK_STATE_NORMAL];
		style_ptr->bg[GTK_STATE_NORMAL] = c;

		gdk_color_alloc(colormap, &style_ptr->fg[GTK_STATE_NORMAL]);
		gdk_color_alloc(colormap, &style_ptr->bg[GTK_STATE_NORMAL]);

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING3_REV);
	    }
	    styles_list->heading3_rev_text = style_ptr;

	    /* Heading 4 reverse */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		c = style_ptr->fg[GTK_STATE_NORMAL];
		style_ptr->fg[GTK_STATE_NORMAL] =
		    style_ptr->bg[GTK_STATE_NORMAL];
		style_ptr->bg[GTK_STATE_NORMAL] = c;

		gdk_color_alloc(colormap, &style_ptr->fg[GTK_STATE_NORMAL]);
		gdk_color_alloc(colormap, &style_ptr->bg[GTK_STATE_NORMAL]);

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_HEADING4_REV);
	    }
	    styles_list->heading4_rev_text = style_ptr;


	    /* Editing text standard */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_EDIT_TEXT_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_EDIT_STD);
	    }
	    styles_list->edit_text_standard = style_ptr;

	    /* Editing text background */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_EDIT_TEXT_BG, &c);
		style_ptr->base[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_EDIT_STD);
	    }
	    styles_list->edit_text_background = style_ptr;

	    /* Editing text tag deliminator */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_EDIT_TEXT_DELIM_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_EDIT_STD);
	    }
	    styles_list->edit_text_tag_deliminator = style_ptr;

	    /* Editing text XML tag text */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_EDIT_TEXT_TAG_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_EDIT_STD);
	    }
	    styles_list->edit_text_tag_text = style_ptr;

	    /* Editing text XML symbol representations text */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_EDIT_TEXT_SYMREP_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_EDIT_STD);
	    }
	    styles_list->edit_text_tag_symrep = style_ptr;


	    /* Manpage output text standard */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_MANPAGE_TEXT_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_MANPAGE_STD);
	    }
	    styles_list->manpage_text_standard = style_ptr;

	    /* Manpage output text background */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_MANPAGE_TEXT_BG, &c);
		style_ptr->base[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_MANPAGE_STD);
	    }
	    styles_list->manpage_text_background = style_ptr;

	    /* Manpage output text bold */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_MANPAGE_TEXT_BOLD_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_MANPAGE_BOLD);
	    }
	    styles_list->manpage_text_bold = style_ptr;

	    /* Manpage output text underline */
	    style_ptr = gtk_style_copy(style_standard);
	    if(style_ptr != NULL)
	    {
		gdk_color_parse(MEDIT_CSTR_MANPAGE_TEXT_UNDERLINE_FG, &c);
		style_ptr->fg[GTK_STATE_NORMAL] = c;

		load_font_to_style(style_ptr, MEDIT_FONT_NAME_MANPAGE_UNDERLINE);
	    }
	    styles_list->manpage_text_underline = style_ptr;


	}


	/* Load the pixmaps list */
	pixmaps_list = &core->pixmaps_list;
	if((window != NULL) && (style_standard != NULL))
	{
	    GdkPixmap **pixmap;
	    GdkBitmap **mask;
	    guint8 **data;

#define DO_LOAD_PIXMAP {				\
 (*pixmap) = gdk_pixmap_create_from_xpm_d(		\
  window, mask,						\
  &style_standard->bg[GTK_STATE_NORMAL],		\
  (gchar **)data					\
 );							\
}
	    pixmap = &pixmaps_list->folder_closed_20x20;
	    mask = &pixmaps_list->folder_closed_20x20_mask;
	    data = (guint8 **)icon_folder_closed_20x20_xpm;
	    DO_LOAD_PIXMAP

	    pixmap = &pixmaps_list->folder_opened_20x20;
	    mask = &pixmaps_list->folder_opened_20x20_mask;
	    data = (guint8 **)icon_folder_opened_20x20_xpm;
	    DO_LOAD_PIXMAP


	    pixmap = &pixmaps_list->manual_closed_20x20;
	    mask = &pixmaps_list->manual_closed_20x20_mask;
	    data = (guint8 **)icon_manual_closed_20x20_xpm;
	    DO_LOAD_PIXMAP

	    pixmap = &pixmaps_list->manual_opened_20x20;
	    mask = &pixmaps_list->manual_opened_20x20_mask;
	    data = (guint8 **)icon_manual_opened_20x20_xpm;
	    DO_LOAD_PIXMAP


	    pixmap = &pixmaps_list->manpage_heading_20x20;
	    mask = &pixmaps_list->manpage_heading_20x20_mask;
	    data = (guint8 **)icon_manpage_heading_20x20_xpm;
	    DO_LOAD_PIXMAP

	    pixmap = &pixmaps_list->manpage_section_20x20;
	    mask = &pixmaps_list->manpage_section_20x20_mask;
	    data = (guint8 **)icon_manpage_section_20x20_xpm;
	    DO_LOAD_PIXMAP

#undef DO_LOAD_PIXMAP
	}


	/* Create Preferences & Options window */
	core->pref = PrefNew(core);

	/* Open the configuration */
	if(cfg_file != NULL)
	{
	    if(PrefLoadFromFile(core->pref, cfg_file) == 0)
	    {
		/* Apply preferences to realize newly loaded values
		 * onto core structure and other resources
		 */
		PrefDoApply(core->pref);
	    }
	}

	/* Startup with an editor? */
	if(!startup_viewer &&
	   PrefParmGetValueB(core->pref, MEDIT_PREF_PARM_STARTUP_EDITOR)
	)
	    editor = EditorNew(core);
	else
	    editor = NULL;
	if(editor != NULL)
	{
	    if(core->total_editors < 0)
		core->total_editors = 0;

	    editor_num = core->total_editors;
	    core->total_editors++;
	    core->editor = (editor_struct **)g_realloc(
		core->editor,
		core->total_editors * sizeof(editor_struct *)
	    );
	    if(core->editor == NULL)
	    {
		core->total_editors = 0;
		EditorDelete(editor);
		editor = NULL;
	    }
	    else
	    {
		core->editor[editor_num] = editor;
		EditorMap(editor);
		if(filename != NULL)
		    EditorFileOpen(editor, filename, NULL, FALSE);
	    }
	}

	/* Startup with a viewer? */
	if((startup_viewer) ||
	   PrefParmGetValueB(core->pref, MEDIT_PREF_PARM_STARTUP_VIEWER)
	)
	    viewer = ViewerNew(
		core,
		(core->total_editors > 0) ? 0 : -1
	    );
	else
	    viewer = NULL;
	if(viewer != NULL)
	{
	    if(core->total_viewers < 0)
		core->total_viewers = 0;
	      
	    viewer_num = core->total_viewers;
	    core->total_viewers++;
	    core->viewer = (viewer_struct **)g_realloc(
		core->viewer,
		core->total_viewers * sizeof(viewer_struct *)
	    );
	    if(core->viewer == NULL)
	    {
		core->total_viewers = 0;
		ViewerDelete(viewer);
		viewer = NULL;
	    }
	    else
	    {
		core->viewer[viewer_num] = viewer;
		ViewerMap(viewer);

		if(filename != NULL)
		{
		    ViewerSetBusy(viewer);
		    ViewerTextDelete(viewer, 0, -1);
		    ViewerTextInsertPosition(viewer, 0);
		    ViewerOpenFile(viewer, filename, filename);
		    ViewerSetReady(viewer);
		}
	    }
	}

	/* If no windows were created then create a new editor */
	if((editor == NULL) && (viewer == NULL))
	{
	    editor = EditorNew(core);
	    if(editor != NULL)
	    {
		editor_num = MAX(core->total_editors, 0);
		core->total_editors = editor_num + 1;
		core->editor = (editor_struct **)g_realloc(
		    core->editor,
		    core->total_editors * sizeof(editor_struct *)
		);
		if(core->editor == NULL)
		{
		    core->total_editors = 0;
		    EditorDelete(editor);
		    editor = NULL;
		}
		else
		{
		    core->editor[editor_num] = editor;
		    EditorMap(editor);
		    if(filename != NULL)
			EditorFileOpen(editor, filename, NULL, FALSE);
		}
	    }
	}


	/* Call crash check recovery function to prompt and recover any
	 * files saved by an emergency save in a previous run of this
	 * program.
	 */
	MEditDoCrashRecovery(core);


	/* Set the timeout callback */
	core->toid = gtk_timeout_add(
	    250l,
	    MEditTOCB, core
	);


	g_free(cfg_file);

	*status_rtn = 0;

	return(core);
}

/*
 *	Main timeout callback.
 */
static gint MEditTOCB(gpointer data)
{
	gboolean need_break, still_processing;
	gint i, status;
	GtkCTreeNode *branch;
	GtkCTreeRow *branch_row;
	editor_struct *editor;
	viewer_struct *viewer;
	editor_item_struct *item;
	medit_core_struct *core = MEDIT_CORE(data);
	if(core == NULL)
	    return(FALSE);

	/* Need to close all windows? */
	if(need_close_all_windows) 
	{
	    /* Check if any dialogs are in query, break query as needed
	     * and return. Let next call to this function handle rest of
	     * closing all windows
	     */
	    if(CDialogIsQuery())
	    {
		CDialogBreakQuery();
		return(TRUE);
	    }
	    if(FileBrowserIsQuery())
	    {
		FileBrowserBreakQuery();
		return(TRUE);
	    }
	    if(CSDIsQuery())
	    {
		CSDBreakQuery();
		return(TRUE);
	    }
	    if(FSDIsQuery())
	    {
		FSDBreakQuery();
		return(TRUE);
	    }
	    if(PDialogIsQuery())
	    {
		PDialogBreakQuery();
		return(TRUE);
	    }
	    /* All dialogs not in query */


	    /* Reset need_close_all_windows back to FALSE and then check
	     * a few things.
	     */
	    need_close_all_windows = FALSE;

	    /* Check if any windows are processing */
	    still_processing = FALSE;

	    /* Check each editor to see if any are still processing */
	    for(i = 0; i < core->total_editors; i++)
	    {
		editor = core->editor[i];
		if(editor == NULL)
		    continue;
		
		if(editor->freeze_count > 0)
		{
		    still_processing = TRUE;
		    break;
		}
	    }
	    /* Check each viewer to see if any are still processing */
	    for(i = 0; i < core->total_viewers; i++)
	    {
		viewer = core->viewer[i];
		if(viewer == NULL)
		    continue;

		if(viewer->processing)
		{
		    still_processing = TRUE;
		    break;
		}
	    }
	    /* One or more windows still processing? */
	    if(still_processing)
	    {
		/* Return now, next call to timeout won't request to
		 * close all windows.
		 */
		return(TRUE);
	    }


	    /* Go through each editor and check for changes */
	    for(i = 0; i < core->total_editors; i++)
	    {
		editor = core->editor[i];
		if(editor == NULL)
		    continue;

		if(TRUE)
		{
		    need_break = FALSE;
		    branch = EditorItemGetFirstToplevel(editor);
		    while(branch != NULL)
		    {
			item = EditorBranchGetData(
			    GTK_CTREE(editor->layout_ctree),
			    branch
			);
			if(item->has_changes)
			{
			    need_break = TRUE;
			    break;
			}

			branch_row = GTK_CTREE_ROW(branch);
			branch = (branch_row != NULL) ?
			    branch_row->sibling : NULL;
		    }

		    if(need_break)
			break;
		}
	    }
	    if(i < core->total_editors)
	    {
		/* Found some changed data that was not saved */
		CDialogSetTransientFor(NULL);
		status = CDialogGetResponse(
"Save Changes",
"There are unsaved changes on one or more of the editors,\n\
are you sure you want to exit and discard those changes?",
		    NULL,
		    CDIALOG_ICON_WARNING,
		    CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
		    CDIALOG_BTNFLAG_NO
		);
		CDialogSetTransientFor(NULL);
		switch(status)
		{
		  case CDIALOG_RESPONSE_YES:
		  case CDIALOG_RESPONSE_YES_TO_ALL: 
		  case CDIALOG_RESPONSE_OK:
		    need_close_all_windows = TRUE;
		    break;
		}
	    }
	    else
	    {
		/* No changed data found, can continue to close all
		 * windows.
		 */
		need_close_all_windows = TRUE;
	    }

/* Go through other types of windows to check for changes? */


	    /* Definatly sure to close all windows? */
	    if(need_close_all_windows)
	    {
		/* Go through each editor and close all editors
		 * that are initialized by resetting and unmapping them.
		 */
		for(i = 0; i < core->total_editors; i++)
		{
		    editor = core->editor[i];
		    if(editor == NULL)
			continue;

		    EditorRecordPositions(editor);
		    EditorReset(editor, TRUE);
		}
		/* Viewer windows */
		for(i = 0; i < core->total_viewers; i++)
		{
		    viewer = core->viewer[i];
		    if(viewer == NULL)
			continue;
	    
		    ViewerRecordPositions(viewer);
		    ViewerReset(viewer, TRUE);
		}
		/* Add support for resetting and unmapping other types of
		 * windows here.
		 */

	    }
	}


	/* Go through each editor, checking if one or more are
	 * initialized and/or mapped. This is to ensure this program
	 * keeps running while one or more Manual Page Editor editor is
	 * in use, otherwise this program will start shutting down.
	 */
	for(i = 0; i < core->total_editors; i++)
	{
	    editor = core->editor[i];
	    if(editor == NULL)
		continue;

	    /* Is this editor mapped? */
	    if(GTK_WIDGET_MAPPED(editor->toplevel))
		break;
	}
	/* Atleast one editor was initialized and mapped? */
	if(i < core->total_editors)
	{
	    /* Atleast one editor is still in use, keep running */
	}
	else
	{
	    /* All editors were uninitialized and/or unmapped */

	    /* Check if any viewers are initialized and mapped */
	    for(i = 0; i < core->total_viewers; i++)
	    {
		viewer = core->viewer[i];
		if(viewer == NULL)
		    continue;

		/* Is this viewer initialized and mapped? */
		if(GTK_WIDGET_MAPPED(viewer->toplevel))
		    break;
	    }
	    /* Atleast one viewer was initialized and mapped? */
	    if(i < core->total_viewers)
	    {
		/* Atleast one viewer is still in use, keep running */
	    }
	    else
	    {
		/* All viewers were uninitialized and/or unmapped */

		/* Pop the main GTK loop */
		gtk_main_quit();

		/* Do not call this timeout again */
		core->toid = 0;
		return(FALSE);
	    }
	}

	return(TRUE);
}

/*
 *	Deletes all resources on the core structure but does not
 *	delete the structure itself.
 */
void MEditDelete(medit_core_struct *core)
{
	gint i;
	gchar *cfg_file;
	medit_cursors_list_struct *cursors_list;
	medit_pixmaps_list_struct *pixmaps_list;
	medit_styles_list_struct *styles_list;

	if(core == NULL)
	    return;

	/* Remove the timeout callback */
	GTK_TIMEOUT_REMOVE(core->toid);
	core->toid = 0;

	/* Delete all the editors */
	for(i = 0; i < core->total_editors; i++)
	    EditorDelete(core->editor[i]);
	g_free(core->editor);
	core->editor = NULL;
	core->total_editors = 0;

	/* Delete all the viewers */
	for(i = 0; i < core->total_viewers; i++)
	    ViewerDelete(core->viewer[i]);
	g_free(core->viewer);
	core->viewer = NULL;
	core->total_viewers = 0;

	/* Save the confuiguration */
	cfg_file = (char *)PrefParmGetValueP(
	    core->pref, MEDIT_PREF_PARM_LOCATIONS_RCFILE
	);
	if(cfg_file != NULL)
	    cfg_file = STRDUP(cfg_file);
	if(cfg_file != NULL)
	    PrefSaveToFile(core->pref, cfg_file);
	g_free(cfg_file);
	cfg_file = NULL;

	/* Delete the Preferences & Options window */
	PrefDelete(core->pref);
	core->pref = NULL;

	/* Delete the about dialog */
	AboutDialogDelete(core->about);
	core->about = NULL;



	/* Unref all cursors */
	cursors_list = &core->cursors_list;
	gdk_cursor_destroy(cursors_list->busy);
	gdk_cursor_destroy(cursors_list->text);


	/* Unref all pixmaps */
	pixmaps_list = &core->pixmaps_list;

	GDK_PIXMAP_UNREF(pixmaps_list->medit_icon_48x48);
	GDK_BITMAP_UNREF(pixmaps_list->medit_icon_48x48_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->folder_closed_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->folder_closed_20x20_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->folder_opened_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->folder_opened_20x20_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->manual_closed_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->manual_closed_20x20_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->manual_opened_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->manual_opened_20x20_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->manpage_heading_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->manpage_heading_20x20_mask);

	GDK_PIXMAP_UNREF(pixmaps_list->manpage_section_20x20);
	GDK_BITMAP_UNREF(pixmaps_list->manpage_section_20x20_mask);


	/* Unref all styles */
	styles_list = &core->styles_list;

	styles_list->standard = NULL;		/* Came from GTK internal, do
						 * not unref it */
	GTK_STYLE_UNREF(styles_list->heading1_text);
	GTK_STYLE_UNREF(styles_list->heading2_text);
	GTK_STYLE_UNREF(styles_list->heading3_text);
	GTK_STYLE_UNREF(styles_list->heading4_text);
	GTK_STYLE_UNREF(styles_list->heading5_text);
	GTK_STYLE_UNREF(styles_list->heading6_text);

	GTK_STYLE_UNREF(styles_list->heading1_rev_text);
	GTK_STYLE_UNREF(styles_list->heading2_rev_text);
	GTK_STYLE_UNREF(styles_list->heading3_rev_text);
	GTK_STYLE_UNREF(styles_list->heading4_rev_text);

	GTK_STYLE_UNREF(styles_list->edit_text_standard);
	GTK_STYLE_UNREF(styles_list->edit_text_background);
	GTK_STYLE_UNREF(styles_list->edit_text_tag_deliminator);
	GTK_STYLE_UNREF(styles_list->edit_text_tag_text);
	GTK_STYLE_UNREF(styles_list->edit_text_tag_symrep);

	GTK_STYLE_UNREF(styles_list->manpage_text_standard);
	GTK_STYLE_UNREF(styles_list->manpage_text_background);
	GTK_STYLE_UNREF(styles_list->manpage_text_bold);
	GTK_STYLE_UNREF(styles_list->manpage_text_underline);


	g_free(core);
}


int main(int argc, char **argv)
{
	gint i, status;
	const gchar *arg;
	medit_core_struct *core = MEDIT_CORE(g_malloc0(
	    sizeof(medit_core_struct)
	));
	if(core == NULL)
	    return(1);

	segfault_count = 0;
	need_close_all_windows = FALSE;

	/* Set the global core pointer */
	medit_core_ptr = core;

use_text_delete = TRUE;

	/* Set up signals */
	signal(SIGINT, MEditSignalCB);
	signal(SIGTERM, MEditSignalCB);
	signal(SIGSEGV, MEditSignalCB);
	signal(SIGPIPE, MEditSignalCB);


	/* Parse basic arguments */
	for(i = 1; i < argc; i++)
	{
	    arg = argv[i];
	    if(arg == NULL)
		continue;

	    /* Help */
	    if(!g_strcasecmp(arg, "--help") ||
	       !g_strcasecmp(arg, "-help") ||
	       !g_strcasecmp(arg, "--h") ||
	       !g_strcasecmp(arg, "-h")
	    )
	    {
		g_print("%s\n", PROG_USAGE_MESG);
		return(0);
	    }
	    /* Version */
	    else if(!g_strcasecmp(arg, "--version") ||
		    !g_strcasecmp(arg, "-version")
	    )
	    {
		g_print(
		    "%s Version %s\n%s\n",
		    PROG_NAME_FULL, PROG_VERSION,
		    PROG_COPYRIGHT
		);
		return(0);
	    }
	    else
	    {
		/* Leave other arguments alone, they will be handled
		 * later
		 */
	    }
	}


	/* Set up localization code */
	setlocale(LC_ALL, "");

	gtk_set_locale();

	/* Initialize GTK */
	gtk_init(&argc, &argv);

	/* Initialize the dialogs */
	CDialogInit();
	FPromptInit();
	FileBrowserInit();
	CSDInit();
	FSDInit();
	PDialogInit();

	/* Initialize the core */
	core = MEditNew(argc, argv, &status);
	if(status != 0)
	{
	    MEditDelete(core);
	    return(status);
	}

	/* Enter main GTK loop */
	gtk_main();

	/* Delete the core */
	medit_core_ptr = NULL;
	MEditDelete(core);
	core = NULL;

	/* Shutdown the dialogs */
	CDialogShutdown();
	FPromptShutdown();
	FileBrowserShutdown();
	CSDShutdown();
	FSDShutdown();
	PDialogShutdown();

	return(0);
}
