#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>

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

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

#include "../guiutils.h"
#include "../cdialog.h"
#include "../fb.h"
#include "../pdialog.h"
#include "../lib/endeavour2.h"
#include "../hview.h"
#include "config.h"

#include "../images/icon_edit_48x48.xpm"
#include "../images/icon_new_20x20.xpm"
#include "../images/icon_open_20x20.xpm"
#include "../images/icon_save_20x20.xpm"
#include "../images/icon_save_as_20x20.xpm"
#include "../images/icon_exit_20x20.xpm"
#include "../images/icon_cut_20x20.xpm"
#include "../images/icon_copy_20x20.xpm"
#include "../images/icon_paste_20x20.xpm"
#include "../images/icon_add_20x20.xpm"
#include "../images/icon_cancel_20x20.xpm"
#include "../images/icon_search_20x20.xpm"
#include "../images/icon_about_20x20.xpm"


/*
 *	Hex Editor:
 */
typedef struct {

	GtkWidget	*toplevel;
	GtkAccelGroup	*accelgrp;

	GtkWidget	*main_vbox,
			*menu_bar_handle,
			*cut_mi,
			*copy_mi,
			*delete_mi,
			*edit_mode;

	hview_struct	*hv;

	gchar		*filename;

	guint8		*last_find_needle;
	gint		last_find_needle_len;

	edv_context_struct *ctx;

} hedit_struct;
#define HEDIT(p)	((hedit_struct *)(p))


static void HViewSignalCB(int s);
static void HEditHViewChangedCB(hview_struct *hv, gpointer data);
static void HEditDNDDragReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);
static void HEditNewCB(GtkWidget *widget, gpointer data);
static void HEditOpenCB(GtkWidget *widget, gpointer data);
static void HEditSaveCB(GtkWidget *widget, gpointer data);
static void HEditSaveAsCB(GtkWidget *widget, gpointer data);
static void HEditExitCB(GtkWidget *widget, gpointer data);
static void HEditCutCB(GtkWidget *widget, gpointer data);
static void HEditCopyCB(GtkWidget *widget, gpointer data);
static void HEditPasteCB(GtkWidget *widget, gpointer data);
static void HEditInsertCB(GtkWidget *widget, gpointer data);
static void HEditDeleteCB(GtkWidget *widget, gpointer data);
static void HEditFindCB(GtkWidget *widget, gpointer data);
static void HEditFindNextCB(GtkWidget *widget, gpointer data);
static void HEditEditModeCB(GtkWidget *widget, gpointer data);
static void HEditCalculateSelectedCB(GtkWidget *widget, gpointer data);
static void HEditAboutCB(GtkWidget *widget, gpointer data);

static void HEditBuildMenuBar(hedit_struct *he, GtkWidget *parent);
static void HEditUpdate(hedit_struct *he);


#define HEDIT_WIDTH		640
#define HEDIT_HEIGHT		480


#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 DO_CHECK_CHANGES_AND_QUERY_USER(_he_,_hv_) {	\
 /* Check if current data has modifications and		\
  * if so, query user to save first			\
  */							\
 if(((_hv_) != NULL) ? (_hv_)->modified : FALSE) {	\
  gint response;					\
							\
  EDVPlaySoundQuestion((_he_)->ctx);			\
  CDialogSetTransientFor((_he_)->toplevel);		\
  response = CDialogGetResponse(			\
"Save Changes?",					\
"The data contains changes that have not been\n\
saved, do you want to save those changes?", \
"There are changes made to the data that have\n\
not been saved. If you want to save those changes,\n\
then click on \"Yes\". If you want to discard\n\
those changes then click on \"No\". If you do not\n\
want to continue with this operation then click\n\
on \"Cancel\".",					\
   CDIALOG_ICON_WARNING,				\
   CDIALOG_BTNFLAG_YES |				\
   CDIALOG_BTNFLAG_NO |					\
   CDIALOG_BTNFLAG_CANCEL |				\
   CDIALOG_BTNFLAG_HELP,				\
   CDIALOG_BTNFLAG_YES					\
  );							\
  CDialogSetTransientFor(NULL);				\
  switch(response) {					\
   case CDIALOG_RESPONSE_YES_TO_ALL:			\
   case CDIALOG_RESPONSE_YES:				\
    HEditSaveCB(NULL, data);				\
    /* Check if changes were not saved? */		\
    if((_hv_)->modified)				\
     return;						\
    break;						\
							\
   case CDIALOG_RESPONSE_NO:				\
    break;						\
							\
   default:						\
    return;						\
    break;						\
  }							\
 }							\
}


/*
 *	UNIX signal handler.
 */
static void HViewSignalCB(int s)
{
	switch(s)
	{
	  case SIGINT:
	  case SIGTERM:
	  case SIGSEGV:
	    exit(1);
	    break;
	}
}

/*
 *	Hex Editor toplevel GtkWindow "delete_event" callback.
 */
static gint HEditCloseCB(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	HEditExitCB(widget, data);
	return(TRUE);
}


/*
 *	Hex View changed callback.
 */
static void HEditHViewChangedCB(hview_struct *hv, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HEditUpdate(he);
}

/*
 *	Hex View "drag_data_received" callback.
 *
 *	Opens the object specified by the url in the drag data.
 */
static void HEditDNDDragReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	GtkWidget *w, *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if((widget == NULL) || (dc == NULL) || (he == NULL))
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	DO_CHECK_CHANGES_AND_QUERY_USER(he, hv)

	HViewSetBusy(hv, TRUE);

	/* Get hv's view drawing area widget */
	w = hv->view_da;

	/* View widget the same as the callback widget? */
	if(w == widget)
	{
	    /* Check if the DND target type is one that we support */
	    if((info == HEDIT_DND_TYPE_INFO_TEXT_PLAIN) ||
	       (info == HEDIT_DND_TYPE_INFO_TEXT_URI_LIST) ||
	       (info == HEDIT_DND_TYPE_INFO_STRING)
	    )
	    {
		const gchar	*s,
				*url = (const gchar *)selection_data->data;
		gchar *path;


		/* The DND data should be a URL string, we need to parse
		 * it and get the absolute path portion of that URL
		 * string
		 */
		s = strstr(url, "://");
		if(s != NULL)
		    s += STRLEN("://");
		else
		    s = url;

		s = strchr(s, '/');
		path = STRDUP(s);

		/* Got absolute path to object to be loaded? */
		if(path != NULL)
		{
		    g_free(he->filename);
		    he->filename = STRDUP(path);
		    HViewOpenFile(hv, he->filename);

		    g_free(path);
		}
	    }
	}

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	New callback.
 */
static void HEditNewCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	DO_CHECK_CHANGES_AND_QUERY_USER(he, hv)

	HViewSetBusy(hv, TRUE);

	HViewClear(hv);

	hv->modified = FALSE;

	g_free(he->filename);
	he->filename = NULL;

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	Open callback.
 */
static void HEditOpenCB(GtkWidget *widget, gpointer data)
{
	gboolean status;
	fb_type_struct **ftype = NULL, *ftype_rtn = NULL;
	gint total_ftypes = 0;
	gchar **path_rtn = NULL;
	gint total_path_rtns = 0;
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if((he == NULL) || FileBrowserIsQuery())
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	DO_CHECK_CHANGES_AND_QUERY_USER(he, hv)

	HViewSetBusy(hv, TRUE);

	/* Create file types list */
	FileBrowserTypeListNew(
	    &ftype, &total_ftypes,
	    "*.*", "All files"
	);

	/* Query user for file to open */
	FileBrowserSetTransientFor(toplevel);
	status = FileBrowserGetResponse(
	    "Open File",
	    "Open", "Cancel",
	    NULL,		/* Use last path */
	    ftype, total_ftypes,
	    &path_rtn, &total_path_rtns,
	    &ftype_rtn
	);
	FileBrowserSetTransientFor(NULL);

	/* Got user response? */
	if(status)
	{
	    const gchar *new_path = (total_path_rtns > 0) ?
		path_rtn[0] : NULL;

	    g_free(he->filename);
	    he->filename = STRDUP(new_path);
	    HViewOpenFile(hv, he->filename);
	}

	/* Delete file types list */
	FileBrowserDeleteTypeList(ftype, total_ftypes);

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	Save callback.
 */
static void HEditSaveCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	/* If there is no filename to save to then call save as callback
	 * so user can be queried for a file name
	 */
	if(he->filename == NULL)
	{
	    HEditSaveAsCB(widget, data);
	    return;
	}

	HViewSetBusy(hv, TRUE);

	/* Save data to file */
	if(HViewSaveFile(hv, he->filename))
	{
	    /* Error occured while saving file */
	    gchar *buf = g_strdup_printf(
"Error occured while saving file:\n\n    %s\n",
		he->filename
	    );
	    EDVPlaySoundError(he->ctx);
	    CDialogSetTransientFor(toplevel);
	    CDialogGetResponse(
		"Save Error",
		buf,
"Please check if you have sufficient permission to\n\
save to the specified file and that the device capacity\n\
has not been exceeded.",
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	}
	else
	{
	    /* File saved successfully */
	    EDVNotifyQueueObjectModified(
		he->ctx, he->filename, NULL
	    );
	    EDVContextSync(he->ctx);

	    hv->modified = FALSE;
	}

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	Save as callback.
 */
static void HEditSaveAsCB(GtkWidget *widget, gpointer data)
{
	gboolean status;
	fb_type_struct **ftype = NULL, *ftype_rtn = NULL;
	gint total_ftypes = 0;
	gchar **path_rtn = NULL;
	gint total_path_rtns = 0;
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if((he == NULL) || FileBrowserIsQuery())
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	HViewSetBusy(hv, TRUE);

	/* Create file types list */
	FileBrowserTypeListNew(
	    &ftype, &total_ftypes,
	    "*.*", "All files"
	);

	/* Query user for file to save */
	FileBrowserSetTransientFor(toplevel);
	status = FileBrowserGetResponse(
	    "Save File As",
	    "Save", "Cancel",
	    NULL,               /* Use last path */
	    ftype, total_ftypes,
	    &path_rtn, &total_path_rtns,
	    &ftype_rtn
	);
	FileBrowserSetTransientFor(NULL);

	/* Got user response? */
	if(status)
	{
	    gboolean file_exists = FALSE;
	    const gchar *new_path = (total_path_rtns > 0) ?
		path_rtn[0] : NULL;

	    if(new_path != NULL)
	    {
		gboolean allow_save = TRUE;

		/* Check if file exists */
		file_exists = access(new_path, F_OK) ? FALSE : TRUE;
		if(file_exists)
		{
		    gint response;
		    gchar *buf = g_strdup_printf(
"Overwrite existing file:\n\n    %s\n",
			new_path
		    );
		    EDVPlaySoundWarning(he->ctx);
		    CDialogSetTransientFor(toplevel);
		    response = CDialogGetResponse(
			"Confirm Overwrite",
			buf,
			NULL,
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
			CDIALOG_BTNFLAG_YES
		    );
		    CDialogSetTransientFor(NULL);
		    g_free(buf);
		    switch(response)
		    {
		      case CDIALOG_RESPONSE_YES_TO_ALL:
		      case CDIALOG_RESPONSE_YES:
			break;

		      default:
			allow_save = FALSE;
			break;
		    }
		}

		/* File not exist or user permits ovewrite? */
		if(allow_save)
		{
		    /* Update filename */
		    g_free(he->filename);
		    he->filename = STRDUP(new_path);

		    /* Save data to new file name */
		    if(HViewSaveFile(hv, he->filename))
		    {
			/* Error occured while saving file */
			gchar *buf = g_strdup_printf(
"Error occured while saving file:\n\n    %s\n",
			    he->filename
			);
			EDVPlaySoundError(he->ctx);
			CDialogSetTransientFor(toplevel);
			CDialogGetResponse(
			    "Save Error",
			    buf,
"Please check if you have sufficient permission to\n\
save to the specified file and that the disk capacity\n\
has not been exceeded.",
			    CDIALOG_ICON_ERROR,
			    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			    CDIALOG_BTNFLAG_OK
			);
			CDialogSetTransientFor(NULL);
			g_free(buf);
		    }
		    else
		    {
			/* File saved successfully */
			if(file_exists)
			    EDVNotifyQueueObjectModified(
				he->ctx, he->filename, NULL
			    );
			else
			    EDVNotifyQueueObjectAdded(
				he->ctx, he->filename
			    );
			EDVContextSync(he->ctx);

			hv->modified = FALSE;
		    }
		}
	    }
	}	/* Got user response? */

	/* Delete file types list */
	FileBrowserDeleteTypeList(ftype, total_ftypes);

	/* Reset due to possible file related change */
	FileBrowserReset();

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	Exit callback.
 */
static void HEditExitCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	DO_CHECK_CHANGES_AND_QUERY_USER(he, hv)

	gtk_main_quit();
}

/*
 *	Cut callback.
 */
static void HEditCutCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HViewCut(he->hv);
}

/*
 *      Copy callback.
 */
static void HEditCopyCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HViewCopy(he->hv);
}

/*
 *      Paste callback.
 */
static void HEditPasteCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HViewPaste(he->hv);
}

/*
 *      Insert callback.
 */
static void HEditInsertCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HViewInsert(he->hv);
}

/*
 *      Delete callback.
 */
static void HEditDeleteCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	HViewDeleteSelected(he->hv);
}

/*
 *	Find callback.
 */
static void HEditFindCB(GtkWidget *widget, gpointer data)
{
	gchar **strv, *last_needle = NULL;
	gint strc;
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if((he == NULL) || PDialogIsQuery())
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	HViewSetBusy(hv, TRUE);

	PDialogDeleteAllPrompts();
	if((he->last_find_needle != NULL) &&
	   (he->last_find_needle_len > 0)
	)
	{
	    last_needle = (gchar *)g_malloc(
		(he->last_find_needle_len + 1) * sizeof(gchar)
	    );
	    memcpy(
		last_needle, he->last_find_needle, 
		he->last_find_needle_len * sizeof(gchar)
	    );
	    last_needle[he->last_find_needle_len] = '\0';
	}
 	PDialogAddPrompt(NULL, "Find:", last_needle);
	g_free(last_needle);
	last_needle = NULL;
	PDialogSetSize(320, -1);
	PDialogSetTransientFor(toplevel);
	strv = PDialogGetResponse(
	    "Find:", NULL, NULL,
	    PDIALOG_ICON_SEARCH,
	    "Find", "Cancel",
	    PDIALOG_BTNFLAG_SUBMIT | PDIALOG_BTNFLAG_CANCEL,
	    PDIALOG_BTNFLAG_SUBMIT,
	    &strc
	);
	PDialogSetTransientFor(NULL);

	if((strv != NULL) && (strc > 0))
	{
	    gint i;
	    const gchar *s = strv[0];
	    if(!STRISEMPTY(s))
	    {
		/* Need to convert the user given string from ASCII to
		 * hex and update the last find needle
		 */
		g_free(he->last_find_needle);
		he->last_find_needle = (guint8 *)STRDUP(s);
		he->last_find_needle_len = STRLEN(s);

		/* Do search */
		i = HViewFind(
		    hv,
		    he->last_find_needle, he->last_find_needle_len,
		    hv->buf_pos + 1,	/* Start index */
		    FALSE		/* Not case sensitive */
		);
		/* Got match? */
		if(i > -1)
		{
		    /* Scroll to position */
		    HViewSetPosition(hv, i);
		    HViewSelect(hv, i, i + he->last_find_needle_len - 1);
		    if(HViewIsBufferPositionVisible(hv, i) !=
			GTK_VISIBILITY_FULL
		    )
			HViewScrollTo(hv, i, 0.5);
		}
		else
		{
		    /* No match, then start search again from the
		     * beginning.
		     */
		    i = HViewFind(
			hv,
			he->last_find_needle, he->last_find_needle_len,
			0,		/* Start index */
			FALSE		/* Not case sensitive */
		    );
		    /* Got match the second time? */
		    if(i > -1)
		    {
			/* Scroll to position */
			HViewSetPosition(hv, i);
			HViewSelect(hv, i, i + he->last_find_needle_len - 1);
			if(HViewIsBufferPositionVisible(hv, i) !=
			    GTK_VISIBILITY_FULL
			)
			    HViewScrollTo(hv, i, 0.5);
		    }
		    else
		    {
			/* No match */
			HViewSetStatusMessage(
			    hv,
			    "String not found",
			    FALSE
			);
		    }	/* Got match the second time? */
		}	/* Got match? */
	    }
	}

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *      Find next callback.
 */
static void HEditFindNextCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	if(he->last_find_needle == NULL)
	    return;

	HViewSetBusy(hv, TRUE);

	if(TRUE)
	{
	    /* Do search */
	    gint i = HViewFind(
		hv,
		he->last_find_needle, he->last_find_needle_len,
		hv->buf_pos + 1,	/* Start index */
		FALSE			/* Not case sensitive */
	    );
	    /* Got match? */
	    if(i > -1)
	    {
		/* Scroll to position */
		HViewSetPosition(hv, i);
		HViewSelect(hv, i, i + he->last_find_needle_len - 1);
		if(HViewIsBufferPositionVisible(hv, i) !=
		    GTK_VISIBILITY_FULL
		)
		    HViewScrollTo(hv, i, 0.5);
	    }
	    else
	    {
		/* No match, then start search again from the
		 * beginning.
		 */
		i = HViewFind(
		    hv,
		    he->last_find_needle, he->last_find_needle_len,
		    0,		/* Start index */
		    FALSE	/* Not case sensitive */
		);
		/* Got match the second time? */
		if(i > -1)
		{
		    /* Scroll to position */
		    HViewSetPosition(hv, i);
		    HViewSelect(hv, i, i + he->last_find_needle_len - 1);
		    if(HViewIsBufferPositionVisible(hv, i) !=
			GTK_VISIBILITY_FULL
		    )
			HViewScrollTo(hv, i, 0.5);
		}
		else
		{
		    /* No match */
		    HViewSetStatusMessage(
			hv,
			"String not found",
			FALSE
		    );
		}	/* Got match the second time? */
	    }		/* Got match? */
	}

	HEditUpdate(he);
	HViewSetBusy(hv, FALSE);
}

/*
 *	Change edit mode callback.
 */
static void HEditEditModeCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	if(hv->edit_mode == HVIEW_EDIT_MODE_HEX)
	    HViewSetEditMode(hv, HVIEW_EDIT_MODE_ASCII);
	else
	    HViewSetEditMode(hv, HVIEW_EDIT_MODE_HEX);

	HEditUpdate(he);
}

/*
 *	Calculate selected callback.
 */
static void HEditCalculateSelectedCB(GtkWidget *widget, gpointer data)
{
	gint pos;
	guint8 i8 = 0, i8s = 0;
	guint16 i16 = 0, i16s = 0;
	guint32 i24 = 0, i24s = 0;
	guint32 i32 = 0, i32s = 0;
	guint64 i64 = 0, i64s = 0;
	const guint8 *buf;
	GtkWidget *toplevel;
	hview_struct *hv;
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	toplevel = he->toplevel;
	hv = he->hv;
	if(hv == NULL)
	    return;

	buf = hv->buf;
	if(buf == NULL)
	    return;

	if(CDialogIsQuery())
	    return;

	/* Check if nothing is selected */
	if(HVIEW_IS_BUF_SELECTED(hv))
	{
	    gint	i = hv->buf_sel_start,
			n = hv->buf_sel_end;
	    pos = i;
	    if((i >= 0) && (i < hv->buf_len) &&
	       (n >= 0) && (n < hv->buf_len)
	    )
	    {
		gint len;

		if(i > n)
		{
		    gint t = i;
		    i = n;
		    n = t;
		}

		len = n - i + 1;

		if(len >= sizeof(guint8))
		    i8 = *(guint8 *)(&buf[i]);
		if(len >= sizeof(guint16))
		    i16 = *(guint16 *)(&buf[i]);
		if(len >= sizeof(guint32))
		    i32 = *(guint32 *)(&buf[i]);
		if(len >= sizeof(guint64))
		    i64 = *(guint64 *)(&buf[i]);

		i24 = (i32 & 0x00ffffff);
	    }
	}
	else
	{
	    gint i = hv->buf_pos;
	    pos = i;
	    if((i >= 0) && (i < hv->buf_len))
	    {
		if((i + sizeof(guint8)) <= hv->buf_len)
		    i8 = *(guint8 *)(&buf[i]);
		if((i + sizeof(guint16)) <= hv->buf_len)
		    i16 = *(guint16 *)(&buf[i]);
		if((i + sizeof(guint32)) <= hv->buf_len)
		    i32 = *(guint32 *)(&buf[i]);
		if((i + sizeof(guint64)) <= hv->buf_len)
		    i64 = *(guint64 *)(&buf[i]);

		i24 = (i32 & 0x00ffffff);
	    }
	}

	/* Calculate swapped values */
	if(TRUE)
	{
	    i8s = i8;

	    i16s |= ((i16 & 0x00ff) << 8);
	    i16s |= ((i16 & 0xff00) >> 8);

	    i24s |= ((i24 & 0x000000ff) << 16);
	    i24s |= ((i24 & 0x0000ff00) >> 0);
	    i24s |= ((i24 & 0x00ff0000) >> 16);

	    i32s |= ((i32 & 0x000000ff) << 24);
	    i32s |= ((i32 & 0x0000ff00) << 8);
	    i32s |= ((i32 & 0x00ff0000) >> 8);
	    i32s |= ((i32 & 0xff000000) >> 24);

	    i64s |= ((i64 & 0x00000000000000ffLL) << 56);
	    i64s |= ((i64 & 0x000000000000ff00LL) << 40);
	    i64s |= ((i64 & 0x0000000000ff0000LL) << 24);
	    i64s |= ((i64 & 0x00000000ff000000LL) << 8);
	    i64s |= ((i64 & 0x000000ff00000000LL) >> 8);
	    i64s |= ((i64 & 0x0000ff0000000000LL) >> 24);
	    i64s |= ((i64 & 0x00ff000000000000LL) >> 40);
	    i64s |= ((i64 & 0xff00000000000000LL) >> 56);
	}


	if(TRUE)
	{
	    gchar *buf = g_strdup_printf(
"Position: %i\n\
8 Bit Value: %i  Unsigned: %i  0x%.2X\n\
16 Bit Value: %i  Unsigned: %i  0x%.4X\n\
24 Bit Value: %i  Unsigned: %i  0x%.6X\n\
32 Bit Value: %i  Unsigned: %i  0x%.8X\n\
64 Bit Value: %ld  Unsigned: %ld  0x%.16LX\n\
\n\
Swapped Values:\n\
8 Bit Value: %i  Unsigned: %i  0x%.2X\n\
16 Bit Value: %i  Unsigned: %i  0x%.4X\n\
24 Bit Value: %i  Unsigned: %i  0x%.6X\n\
32 Bit Value: %i  Unsigned: %i  0x%.8X\n\
64 Bit Value: %ld  Unsigned: %ld  0x%.16LX",
		pos,
		(gint8)((gint32)i8 - (0xff / 2) - 1), i8, i8,
		(gint16)((gint32)i16 - (0xffff / 2) - 1), i16, i16,
		(gint32)((gint64)i24 - (0xffffff / 2) - 1), i24, i24,
		(gint32)((gint64)i32 - (0xffffffff / 2) - 1), i32, i32,
		(unsigned long)((gint64)i64 - (0xffffffffffffffffLL / 2) - 1),
		(unsigned long)i64,
		(long long unsigned int)i64,
		(gint8)((gint32)i8s - (0xff / 2) - 1), i8s, i8s,
		(gint16)((gint32)i16s - (0xffff / 2) - 1), i16s, i16s,
		(gint32)((gint64)i24s - (0xffffff / 2) - 1), i24s, i24s,
		(gint32)((gint64)i32s - (0xffffffff / 2) - 1), i32s, i32s,
		(unsigned long)((gint64)i64s - (0xffffffffffffffffLL / 2) - 1),
		(unsigned long)i64s,
		(long long unsigned int)i64s
	    );
	    EDVPlaySoundInfo(he->ctx);
	    CDialogSetTransientFor(toplevel);
	    CDialogGetResponse(
		"Results",
		buf,
		NULL,
		CDIALOG_ICON_INFO,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	}


	HEditUpdate(he);
}

/*
 *	About callback.
 */
static void HEditAboutCB(GtkWidget *widget, gpointer data)
{
	hedit_struct *he = HEDIT(data);
	if(he == NULL)
	    return;

	EDVAbout(he->ctx);
	EDVContextSync(he->ctx);
}


/*
 *	Builds the menu bar.
 */
static void HEditBuildMenuBar(hedit_struct *he, GtkWidget *parent)
{
	guint8 **icon;
	const gchar *label;
	guint accel_key, accel_mods;
	GtkAccelGroup *accelgrp;
	GtkWidget *menu_bar, *menu, *w;
	gpointer mclient_data = he;
	void (*func_cb)(GtkWidget *w, gpointer);


	if((he == NULL) || (parent == NULL))
	    return;

	/* Get keyboard accelerator group */
	accelgrp = he->accelgrp;

	/* Create menu bar */
	menu_bar = GUIMenuBarCreate(NULL);
	if(GTK_IS_BOX(parent))
	    gtk_box_pack_start(GTK_BOX(parent), menu_bar, FALSE, FALSE, 0);
	else
	    gtk_container_add(GTK_CONTAINER(parent), menu_bar);
	gtk_widget_show(menu_bar);

#define DO_ADD_MENU_ITEM_LABEL	{		\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp,	\
  icon, label, accel_key, accel_mods, NULL,	\
  mclient_data, func_cb				\
 );						\
}
#define DO_ADD_MENU_ITEM_CHECK	{		\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_CHECK, accelgrp,	\
  icon, label, accel_key, accel_mods, NULL,	\
  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					\
 );						\
}

	/* Create file menu */
	menu = GUIMenuCreateTearOff();
	if(menu != NULL)
	{
	    icon = (guint8 **)icon_new_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Nuevo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Nouveau"
#elif defined(PROG_LANGUAGE_GERMAN)
"Neu"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Nuovo"
#elif defined(PROG_LANGUAGE_DUTCH)
"Nieuw"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Novo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Ny"
#else
"New"
#endif
	    ;
	    accel_key = 'n';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditNewCB;
	    DO_ADD_MENU_ITEM_LABEL

	    icon = (guint8 **)icon_open_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Abierto..."
#elif defined(PROG_LANGUAGE_FRENCH)
"Ouvrir..."
#elif defined(PROG_LANGUAGE_GERMAN)
"Offen..."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aperto..."
#elif defined(PROG_LANGUAGE_DUTCH)
"Open..."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Aberto..."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Åpen..."
#else
"Open..."
#endif
	    ;
	    accel_key = 'o';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditOpenCB;
	    DO_ADD_MENU_ITEM_LABEL

	    icon = (guint8 **)icon_save_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Salve"
#elif defined(PROG_LANGUAGE_FRENCH)
"Epargner"
#elif defined(PROG_LANGUAGE_GERMAN)
"Sparen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Risparmiare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Red"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Poupe"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Untatt"
#else
"Save"
#endif
	    ;
	    accel_key = 's';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditSaveCB;
	    DO_ADD_MENU_ITEM_LABEL

	    icon = (guint8 **)icon_save_as_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Salve Como..."
#elif defined(PROG_LANGUAGE_FRENCH)
"Epargner Comme..."
#elif defined(PROG_LANGUAGE_GERMAN)
"Außer Als..."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Risparmiare Come..."
#elif defined(PROG_LANGUAGE_DUTCH)
"Behalve Als..."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Poupe Como..."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Untatt As..."
#else
"Save As..."
#endif
	    ;
	    accel_key = 'a';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditSaveAsCB;
	    DO_ADD_MENU_ITEM_LABEL

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_exit_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"La Salida"
#elif defined(PROG_LANGUAGE_FRENCH)
"Sortie"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ausgang"
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Uscita"
#elif defined(PROG_LANGUAGE_DUTCH)
"Uitgang"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"A Saída"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Utgang"
#else
"Exit"
#endif
	    ;
	    accel_key = 'q';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditExitCB;
	    DO_ADD_MENU_ITEM_LABEL

	}
	GUIMenuAddToMenuBar(
	    menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Archivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Fichier"
#elif defined(PROG_LANGUAGE_GERMAN)
"Akte"
#elif defined(PROG_LANGUAGE_ITALIAN)
"File"
#elif defined(PROG_LANGUAGE_DUTCH)
"Dossier"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Arquivo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Arkiv"
#else
"File"
#endif
	    , GUI_MENU_BAR_ALIGN_LEFT
	);

	/* Create edit menu */
	menu = GUIMenuCreateTearOff();
	if(menu != NULL)
	{
	    icon = (guint8 **)icon_cut_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"El Corte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Coupure"
#elif defined(PROG_LANGUAGE_GERMAN)
"Schnitt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il Taglio"
#elif defined(PROG_LANGUAGE_DUTCH)
"Snee"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Corte"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Snitt"
#else
"Cut"
#endif
	    ;
	    accel_key = 'x';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditCutCB;
	    DO_ADD_MENU_ITEM_LABEL
	    he->cut_mi = w;

	    icon = (guint8 **)icon_copy_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"La Copia"
#elif defined(PROG_LANGUAGE_FRENCH)
"Copie"
#elif defined(PROG_LANGUAGE_GERMAN)
"Kopie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"La Copia"
#elif defined(PROG_LANGUAGE_DUTCH)
"Kopie"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"A Cópia"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kopi"
#else
"Copy"
#endif
	    ;
	    accel_key = 'c';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditCopyCB;
	    DO_ADD_MENU_ITEM_LABEL
	    he->copy_mi = w;

	    icon = (guint8 **)icon_paste_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"La Pasta"
#elif defined(PROG_LANGUAGE_FRENCH)
"Pâte"
#elif defined(PROG_LANGUAGE_GERMAN)
"Paste"
#elif defined(PROG_LANGUAGE_ITALIAN)
"La Pasta"
#elif defined(PROG_LANGUAGE_DUTCH)
"Plakmiddel"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"A Pasta"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Masse"
#else
"Paste"
#endif
	    ;
	    accel_key = 'v';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditPasteCB;
	    DO_ADD_MENU_ITEM_LABEL

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_add_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"La Adición"
#elif defined(PROG_LANGUAGE_FRENCH)
"Insertion"
#elif defined(PROG_LANGUAGE_GERMAN)
"Einsatz"
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Inserzione"
#elif defined(PROG_LANGUAGE_DUTCH)
"Tussenvoegsel"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Insira"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Innsetning"
#else
"Insert"
#endif
	    ;
	    accel_key = GDK_Insert;
	    accel_mods = 0;
	    func_cb = HEditInsertCB;
	    DO_ADD_MENU_ITEM_LABEL

	    icon = (guint8 **)icon_cancel_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Borre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk"
#else
"Delete"
#endif
	    ;
	    accel_key = GDK_Delete;
	    accel_mods = 0;
	    func_cb = HEditDeleteCB;
	    DO_ADD_MENU_ITEM_LABEL
	    he->delete_mi = w;

	    DO_ADD_MENU_SEP

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"El Cambio Redacta El Modo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Le Changement Edite Le Mode"
#elif defined(PROG_LANGUAGE_GERMAN)
"Änderung Redigiert Modus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il Cambiamento Redige Il Modo"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verandeer Bewerking Modus"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"A Mudança Edita Modo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forandr Edit Mode"
#else
"Change Edit Mode"
#endif
	    ;
	    accel_key = GDK_F4;
	    accel_mods = 0;
	    func_cb = HEditEditModeCB;
	    DO_ADD_MENU_ITEM_LABEL

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_search_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"El Hallazgo..."
#elif defined(PROG_LANGUAGE_FRENCH)
"Découverte..."
#elif defined(PROG_LANGUAGE_GERMAN)
"Fund..."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Trovare..."
#elif defined(PROG_LANGUAGE_DUTCH)
"Vondst..."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ache..."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Funn..."
#else
"Find..."
#endif
	    ;
	    accel_key = 'f';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = HEditFindCB;
	    DO_ADD_MENU_ITEM_LABEL

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Encuentre Luego"
#elif defined(PROG_LANGUAGE_FRENCH)
"Trouver Prochain"
#elif defined(PROG_LANGUAGE_GERMAN)
"Finden Sie Nächst"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Trovare Vicino"
#elif defined(PROG_LANGUAGE_DUTCH)
"Vind Volgend"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ache Logo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Finn Next"
#else
"Find Next"
#endif
	    ;
	    accel_key = GDK_F6;
	    accel_mods = 0;
	    func_cb = HEditFindNextCB;
	    DO_ADD_MENU_ITEM_LABEL

	}
	GUIMenuAddToMenuBar(
	    menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Redacte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Editer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Redigieren"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Redigere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Bewerking"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Edite"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Rediger"
#else
"Edit"
#endif
	    , GUI_MENU_BAR_ALIGN_LEFT
	);

	/* Create tools menu */
	menu = GUIMenuCreateTearOff();
	if(menu != NULL)
	{
	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Calcule Escogido"
#elif defined(PROG_LANGUAGE_FRENCH)
"Calculer Choisi"
#elif defined(PROG_LANGUAGE_GERMAN)
"Kalkulieren Sie Ausgewählt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Calcolare Scelto"
#elif defined(PROG_LANGUAGE_DUTCH)
"Reken Geselecterenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Calcule Selecionado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kalkuler Valgt Ut"
#else
"Calculate Selected"
#endif
	    ;
	    accel_key = GDK_F9;
	    accel_mods = 0;
	    func_cb = HEditCalculateSelectedCB;
	    DO_ADD_MENU_ITEM_LABEL

	}
	GUIMenuAddToMenuBar(
	    menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Instrumentos"
#elif defined(PROG_LANGUAGE_FRENCH)
"Outils"
#elif defined(PROG_LANGUAGE_GERMAN)
"Werkzeuge"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Attrezzi"
#elif defined(PROG_LANGUAGE_DUTCH)
"Werktuigen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ferramentas"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Redskaper"
#else
"Tools"
#endif
	    , GUI_MENU_BAR_ALIGN_LEFT
	);



	/* Create help menu */
	menu = GUIMenuCreateTearOff();
	if(menu != NULL)
	{
	    icon = (guint8 **)icon_about_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Acerca De"
#elif defined(PROG_LANGUAGE_FRENCH)
"De"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ungefähr"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Circa"
#elif defined(PROG_LANGUAGE_DUTCH)
"Over"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Sobre"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Omtrent"
#else
"About"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HEditAboutCB;
	    DO_ADD_MENU_ITEM_LABEL
	}
	GUIMenuAddToMenuBar(
	    menu_bar, menu,
#if defined(PROG_LANGUAGE_SPANISH)
"Ayuda"
#elif defined(PROG_LANGUAGE_FRENCH)
"Aide"
#elif defined(PROG_LANGUAGE_GERMAN)
"Hilfe"
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Aiuto"
#elif defined(PROG_LANGUAGE_DUTCH)
"Hulp"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ajuda"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Hjelp"
#else
"Help"
#endif
	    , GUI_MENU_BAR_ALIGN_RIGHT
	);





#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP
}

/*
 *	Updates Hex Editor's widgets to reflect current values.
 */
static void HEditUpdate(hedit_struct *he)
{
	gboolean sensitive;
	GtkWidget *w;
	hview_struct *hv;


	if(he == NULL)
	    return;

	hv = he->hv;
	if(hv == NULL)
	    return;

	w = he->toplevel;
	if(w != NULL)
	{
	    const gchar *s = (he->filename != NULL) ?
		strrchr(he->filename, DIR_DELIMINATOR) : NULL;
	    gchar *title = g_strdup_printf(
		PROG_NAME ": %s%s",
		(s != NULL) ? s + 1 :
		    ((he->filename != NULL) ? he->filename : "Untitled"),
		hv->modified ? " (*)" : ""
	    );
	    gtk_window_set_title(GTK_WINDOW(w), title);
	    g_free(title);
	}

	sensitive = ((hv->buf_sel_start >= 0) && (hv->buf_sel_end >= 0)) ?
	    TRUE : FALSE;
#if 0
	w = he->cut_mi;
	if(w != NULL)
	    gtk_widget_set_sensitive(w, sensitive);
	w = he->copy_mi;
	if(w != NULL)
	    gtk_widget_set_sensitive(w, sensitive);
	w = he->delete_mi;
	if(w != NULL)
	    gtk_widget_set_sensitive(w, sensitive);
#endif
}


int main(int argc, char *argv[])
{
	gboolean initialized_gtk = FALSE;
	gint i;
	gint		scroll_pos = 0;
	const gchar	*arg,
			*filename = NULL;
	GdkWindow *window;
	GtkWidget *w;
	hedit_struct *he;
	hview_struct *hv;
	edv_context_struct *ctx;


	/* Set up time zone */
	tzset();

	/* Set up signal callbacks */
#ifdef SIGINT
	signal(SIGINT, HViewSignalCB);
#endif
#ifdef SIGTERM
	signal(SIGTERM, HViewSignalCB);
#endif
#ifdef SIGSEGV
	signal(SIGSEGV, HViewSignalCB);
#endif
#ifdef SIGSTOP
	signal(SIGSTOP, HViewSignalCB);
#endif
#ifdef SIGCONT
	signal(SIGCONT, HViewSignalCB);
#endif
#ifdef SIGPIPE
	signal(SIGPIPE, HViewSignalCB);
#endif

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

	    /* Help? */
	    if(!strcasecmp(arg, "--help") ||
	       !strcasecmp(arg, "-help") ||
	       !strcasecmp(arg, "--h") ||
	       !strcasecmp(arg, "-h") ||
	       !strcasecmp(arg, "-?")
	    )
	    {
		g_print(PROG_HELP_MESG);
		return(0);
	    }
	    /* Version? */
	    else if(!strcasecmp(arg, "--version") ||
		    !strcasecmp(arg, "-version")
	    )
	    {
		g_print(
		    "%s %s\n%s",
		    PROG_NAME, PROG_VERSION, PROG_COPYRIGHT
		);
		return(0);
	    }
	    /* Scroll to position */
	    else if(!strcasecmp(arg, "--address") ||
		    !strcasecmp(arg, "-address") ||
		    !strcasecmp(arg, "--a") ||
		    !strcasecmp(arg, "-a")
	    )
	    {
		i++;
		arg = (i < argc) ? argv[i] : NULL;
		if(arg != NULL)
		{
		    scroll_pos = ATOI(arg);
		}
		else
		{
		    g_printerr(
			"%s: Requires argument\n",
			argv[i - 1]
		    );
		}
	    }
	    /* All other non-options assume file to open at start up */ 
	    else if(*arg != '-')
	    {
		filename = arg;
	    }
	}

	/* Set GTK+ locale */
	gtk_set_locale();

	/* Initialize GTK+ as needed */
	if(!initialized_gtk)
	{
	    if(!gtk_init_check(&argc, &argv))
	    {
		g_printerr("Unable to initialize GTK.\n");
		return(1);
	    }
	    initialized_gtk = TRUE;
	}

	/* Initialize GDK RGB buffers system */
	gdk_rgb_init();

	/* Initialize dialogs */
	CDialogInit();
	FileBrowserInit();
	PDialogInit();

	/* Initialize Endeavour context */
	ctx = EDVContextNew();
	EDVContextLoadConfigurationFile(ctx, NULL);


	/* Begin creating hex edit window */
	he = HEDIT(
	    g_malloc0(sizeof(hedit_struct))
	);
	he->accelgrp = gtk_accel_group_new();
	he->ctx = ctx;

	/* Toplevel */
	he->toplevel = w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_usize(w, HEDIT_WIDTH, HEDIT_HEIGHT);
	gtk_window_set_wmclass(
	    GTK_WINDOW(w), "hex_editor", PROG_NAME
	);
	gtk_window_set_policy(
	    GTK_WINDOW(w),
	    TRUE, TRUE, TRUE
	);
	gtk_widget_realize(w);
	window = w->window;
	if(window != NULL)
	{
	    GdkGeometry geo;

	    geo.min_width = 100;
	    geo.min_height = 70;

	    geo.base_width = 0;
	    geo.base_height = 0;

	    geo.width_inc = 1;
	    geo.height_inc = 1;

	    gdk_window_set_geometry_hints(
		window, &geo,
		GDK_HINT_MIN_SIZE |
		GDK_HINT_BASE_SIZE |
		GDK_HINT_RESIZE_INC
	    );

	    gdk_window_set_decorations(
		window,
		GDK_DECOR_BORDER | GDK_DECOR_TITLE | GDK_DECOR_MENU |
		GDK_DECOR_RESIZEH | GDK_DECOR_MINIMIZE |
	        GDK_DECOR_MAXIMIZE
	    );
	    gdk_window_set_functions(
		window,
		GDK_FUNC_MOVE | GDK_FUNC_RESIZE |
		GDK_FUNC_MINIMIZE | GDK_FUNC_MAXIMIZE |
		GDK_FUNC_CLOSE
	    );

	    GUISetWMIcon(window, (guint8 **)icon_edit_48x48_xpm);
	}
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_window_add_accel_group(GTK_WINDOW(w), he->accelgrp);

	he->main_vbox = w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(he->toplevel), w);
	gtk_widget_show(w);

	he->menu_bar_handle = w = gtk_handle_box_new();
	gtk_box_pack_start(GTK_BOX(he->main_vbox), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	HEditBuildMenuBar(he, he->menu_bar_handle);

	he->hv = hv = HViewNew(he->main_vbox);
	gtk_window_apply_args(GTK_WINDOW(he->toplevel), argc, argv);
	HViewSetChangedCB(hv, HEditHViewChangedCB, he);
	HViewMap(hv);
	gtk_signal_connect(
	    GTK_OBJECT(he->toplevel), "delete_event",
	    GTK_SIGNAL_FUNC(HEditCloseCB), he
	);
	/* Set up hv for DND */
	w = hv->view_da;
	if(w != NULL)
	{
	    const GtkTargetEntry dnd_tar_types[] = {
{"text/plain",          0,              HEDIT_DND_TYPE_INFO_TEXT_PLAIN},
{"text/uri-list",       0,              HEDIT_DND_TYPE_INFO_TEXT_URI_LIST},
{"STRING",              0,              HEDIT_DND_TYPE_INFO_STRING}
	    };
	    GUIDNDSetTar(
		w,
		dnd_tar_types,
		sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
		GDK_ACTION_DEFAULT |            /* Drag actions */
		GDK_ACTION_LINK |
		GDK_ACTION_MOVE |
		GDK_ACTION_COPY,
		GDK_ACTION_MOVE,                /* Def act if same */
		GDK_ACTION_COPY,                /* Def act */
		HEditDNDDragReceivedCB,
		he
	    );
	}
	HEditUpdate(he);
	gtk_widget_show(he->toplevel);

	if(filename != NULL)
	{
	    HViewSetBusy(hv, TRUE);

	    g_free(he->filename);
	    he->filename = STRDUP(filename);
	    HViewOpenFile(hv, he->filename);

	    if(scroll_pos > 0)
	    {
		if(scroll_pos >= hv->buf_len)
		    scroll_pos = MAX(hv->buf_len - 1, 0);
		HViewSetPosition(hv, scroll_pos);
		HViewScrollTo(hv, scroll_pos, 0.5f);
	    }

	    HEditUpdate(he);

	    HViewSetBusy(hv, FALSE);
	}


	gtk_main();

	/* Delete hex edit window */
	HViewClear(hv);
	HViewDelete(hv);
	he->hv = hv = NULL;

	gtk_widget_destroy(he->menu_bar_handle);
	he->menu_bar_handle = NULL;
	gtk_widget_destroy(he->toplevel);
	he->toplevel = NULL;

	gtk_accel_group_unref(he->accelgrp);
	he->accelgrp = NULL;

	g_free(he->filename);
	g_free(he->last_find_needle);
	g_free(he);
	he = NULL;


	/* Delete Endeavour context */
	EDVContextSync(ctx);
	EDVContextDelete(ctx);
	ctx = NULL;

	/* Shutdown dialogs */
	CDialogShutdown();
	FileBrowserShutdown();
	PDialogShutdown();

	/* Reset the DND Icon */
	GUIDNDSetDragIcon(NULL, NULL, 0, 0);

	return(0);
}
