#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>

#include <gtk/gtk.h>

#include "../include/fio.h"
#include "guiutils.h"
#include "cdialog.h"

#include "hview.h"
#include "hviewcb.h"
#include "config.h"

#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_edit_48x48.xpm"


gint HViewPositionXYToBuffer(hview_struct *hv, gint x, gint y);
void HViewScrollTo(hview_struct *hv, gint pos, gfloat ycoeff);
GtkVisibility HViewIsBufferPositionVisible(hview_struct *hv, gint i);

gint HViewOpenFile(hview_struct *hv, const gchar *path);
gint HViewSaveFile(hview_struct *hv, const gchar *path);
gint HViewOpenData(hview_struct *hv, const guint8 *data, gint data_len);
void HViewClear(hview_struct *hv);

void HViewSetEditMode(hview_struct *hv, hview_edit_mode edit_mode);
void HViewSetSelected(hview_struct *hv, guint8 v);
void HViewSetPosition(hview_struct *hv, gint pos);
void HViewSelect(
	hview_struct *hv, gint start_pos, gint end_pos
);
void HViewCut(hview_struct *hv);
void HViewCopy(hview_struct *hv);
void HViewPaste(hview_struct *hv);
void HViewInsert(hview_struct *hv);
void HViewDeleteSelected(hview_struct *hv);
gint HViewFind(
	hview_struct *hv, const guint8 *needle, gint needle_len,
	gint start_pos, gboolean case_sensitive
);

gboolean HViewToplevelIsWindow(hview_struct *hv);
GtkWidget *HViewGetToplevelWidget(hview_struct *hv);
GtkDrawingArea *HViewGetViewWidget(hview_struct *hv);
GtkMenu *HViewGetMenuWidget(hview_struct *hv);

hview_struct *HViewNew(GtkWidget *parent);
hview_struct *HViewNewWithToplevel(
	gint width, gint height
);
void HViewShowHeading(hview_struct *hv, gboolean show);
void HViewShowStatusBar(hview_struct *hv, gboolean show);
void HViewSetReadOnly(hview_struct *hv, gboolean read_only);
void HViewSetChangedCB(
	hview_struct *hv,
	void (*changed_cb)(hview_struct *, gpointer),
	gpointer data
);
void HViewUpdate(hview_struct *hv);
void HViewDraw(hview_struct *hv);
void HViewQueueDraw(hview_struct *hv);  
void HViewSetBusy(hview_struct *hv, gboolean is_busy);
void HViewSetStatusMessage(
	hview_struct *hv, const gchar *mesg,
	gboolean allow_gtk_iteration
);
void HViewMap(hview_struct *hv);
void HViewUnmap(hview_struct *hv);
void HViewDelete(hview_struct *hv);


#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 HVIEW_DEF_TITLE			"Hex View"
#define HVIEW_DEF_WIDTH			640
#define HVIEW_DEF_HEIGHT		480
#define HVIEW_STATUS_BAR_HEIGHT		26


/*
 *	Returns the buffer position based on the given x and y
 *	coordinates relative to the hv's view widget.  The current
 *	hv's scroll position will be taken into account.
 */
gint HViewPositionXYToBuffer(hview_struct *hv, gint x, gint y)
{
	GtkAdjustment *adj;

	if(hv == NULL)
	    return(0);

	if(hv->buf_len <= 0)
	    return(0);

	adj = hv->vadj;
	if(adj == NULL)
	    return(0);

	if((hv->cell_width <= 0) || (hv->cell_height <= 0))
	    return(0);

	/* Clicked on address area? */
	if((x >= hv->address_x) &&
	   (x < hv->hex_x)
	)
	{
	    return(
		CLIP(
		    (y + (gint)adj->value) / hv->cell_height * 
		    hv->cells_per_row,
		    0, hv->buf_len - 1
		)
	    );
	}
	/* Clicked on hex area? */
	else if((x >= hv->hex_x) &&
		(x < hv->ascii_x)
	)
	{
	    if(x >= (hv->hex_x + (3 * hv->cell_width * hv->cells_per_row)))
		return(
		    CLIP(
			((y + (gint)adj->value) / hv->cell_height *
			hv->cells_per_row) + (hv->cells_per_row - 1),
			0, hv->buf_len - 1
		    )
		);
	    else
		return(
		    CLIP(
			((y + (gint)adj->value) / hv->cell_height *
			hv->cells_per_row) +
			((x - hv->hex_x) / (3 * hv->cell_width)),
			0, hv->buf_len - 1
		    )
		);
	}
	/* All else assume ascii area */
	else
	{
	    if(x >= (hv->ascii_x + (hv->cell_width * hv->cells_per_row)))
		return(
		    CLIP(
			((y + (gint)adj->value) / hv->cell_height *
			hv->cells_per_row) + (hv->cells_per_row - 1),
			0, hv->buf_len - 1
		    )
		);
	    else
		return(
		    CLIP(
			((y + (gint)adj->value) / hv->cell_height *
			hv->cells_per_row) +
			((x - hv->ascii_x) / hv->cell_width),
			0, hv->buf_len - 1
		    )
		);
	}
}

/*
 *	Instructs the hw to scroll to the buffer position pos and
 *	ycoeff.  This function does not update the buf_pos.
 */
void HViewScrollTo(
	hview_struct *hv, gint pos, gfloat ycoeff
)
{
	gint ww, wh;
	GtkAdjustment *adj;
	GtkWidget *w = (hv != NULL) ? hv->view_da : NULL;
	if(w == NULL)
	    return;

	gdk_window_get_size(w->window, &ww, &wh);

	adj = hv->vadj;
	if(adj == NULL)
	    return;

	if((hv->cell_width <= 0) || (hv->cell_height <= 0) ||
	   (hv->cells_per_row <= 0)
	)
	    return;

	if(pos >= hv->buf_len)
	    pos = hv->buf_len - 1;
	if(pos < 0)
	    pos = 0;

	adj->value = ((pos / hv->cells_per_row) * hv->cell_height) -
	    (ycoeff * MAX(adj->page_size - hv->cell_height, 0));
	if(adj->value > (adj->upper - adj->page_size))
	    adj->value = adj->upper - adj->page_size;
	if(adj->value < adj->lower)
	    adj->value = adj->lower;

	gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
}

/*
 *	Returns one of GTK_VISIBILITY_* depending on if the given
 *	buffer position is is visible or not.
 */
GtkVisibility HViewIsBufferPositionVisible(hview_struct *hv, gint i)
{
	gint y, ww, wh;
	GtkAdjustment *adj;
	GtkWidget *w;

	if(hv == NULL)
	    return(GTK_VISIBILITY_NONE);

	w = hv->view_da;
	if(w == NULL)
	    return(GTK_VISIBILITY_NONE);
	gdk_window_get_size(w->window, &ww, &wh);

	adj = hv->vadj;
	if(adj == NULL)
	    return(GTK_VISIBILITY_NONE);

	if((hv->cell_width <= 0) || (hv->cell_height <= 0) ||
	   (hv->cells_per_row <= 0)
	)
	    return(GTK_VISIBILITY_NONE);

	y = (i / hv->cells_per_row * hv->cell_height) - (gint)adj->value;
	if((y + hv->cell_height) < 0)
	    return(GTK_VISIBILITY_NONE);
	else if(y < 0)
	    return(GTK_VISIBILITY_PARTIAL);
	else if((y + hv->cell_height) < wh)
	    return(GTK_VISIBILITY_FULL);
	else if(y < wh)
	    return(GTK_VISIBILITY_PARTIAL);
	else
	    return(GTK_VISIBILITY_NONE);
}

/*
 *	Opens the file specified by path and loads it into the buffer.
 *
 *	This function will automatically call HViewClear().
 *
 *	Returns 0 on success or non-zero on error.
 */
gint HViewOpenFile(hview_struct *hv, const gchar *path)
{
	FILE *fp;
	struct stat stat_buf;
	GtkAdjustment *adj;
	GtkWidget *w;

	if((hv == NULL) || STRISEMPTY(path))
	    return(-1);

	/* Clear buffer and reset scroll position */
	HViewClear(hv);

	/* Open file for reading */
	fp = FOpen(path, "rb");
	if(fp != NULL)
	{
	    if(!fstat(fileno(fp), &stat_buf))
	    {
		hv->buf_len = stat_buf.st_size;
		if(hv->buf_len > 0)
		    hv->buf = (guint8 *)g_malloc(hv->buf_len * sizeof(guint8));
		if(hv->buf == NULL)
		{
		    hv->buf_len = 0;
		}
		else
		{
		    fread(hv->buf, sizeof(guint8), hv->buf_len, fp);
		}
	    }
	    FClose(fp);
	}

	/* Update adjustment */
	w = hv->view_da;
	adj = hv->vadj;
	if((w != NULL) && (adj != NULL) &&
	   (hv->cells_per_row > 0) && (hv->cell_height > 0)
	)
	{
	    const gint height = w->allocation.height;

	    adj->lower = 0.0f;
	    adj->upper = (gfloat)(
		(hv->buf_len / hv->cells_per_row * hv->cell_height) +
		((hv->buf_len % hv->cells_per_row) ? hv->cell_height : 0)
	    );
	    adj->page_size = (gfloat)height;
	    adj->step_increment = hv->cell_height;
	    adj->page_increment = adj->page_size / 2.0f;

	    adj->value = adj->lower;

	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
	}

	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);

	return(0);
}

/*
 *	Saves the data to the file specified by path.
 *
 *      Returns 0 on success or non-zero on error.
 */
gint HViewSaveFile(hview_struct *hv, const gchar *path)
{
	FILE *fp;

	if((hv == NULL) || STRISEMPTY(path))
	    return(-1);

	/* Open file for writing */
	fp = FOpen(path, "wb");
	if(fp != NULL)
	{
	    gint i;

	    for(i = 0; i < hv->buf_len; i++)
	    {
		if(fputc((gint)hv->buf[i], fp) == EOF)
		    break;
	    }

	    FClose(fp);

	    /* Write error? */
	    if(i < hv->buf_len)
		return(-1);
	}

	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);

	return(0);
}

/*
 *	Coppies the data to the Hex View.
 *
 *	This function will automatically call HViewClear().
 *
 *	Returns 0 on success or non-zero on error.
 */
gint HViewOpenData(hview_struct *hv, const guint8 *data, gint data_len)
{
	GtkAdjustment *adj;
	GtkWidget *w;

	if(hv == NULL)
	    return(-1);

	adj = hv->vadj;
	if(adj == NULL)
	    return(-1);

	/* Clear buffer and reset scroll position */
	HViewClear(hv);

	/* Copy data if any */
	if((data != NULL) && (data_len > 0))
	{
	    hv->buf_len = data_len;
	    hv->buf = (guint8 *)g_malloc(hv->buf_len * sizeof(guint8));
	    if(hv->buf != NULL)
		memcpy(hv->buf, data, hv->buf_len * sizeof(guint8));
	    else
		hv->buf_len = 0;
	}

	/* Update adjustment */
	w = hv->view_da;
	adj = hv->vadj;
	if((w != NULL) && (adj != NULL) &&
	   (hv->cells_per_row > 0) && (hv->cell_height > 0)
	)
	{
            const gint height = w->allocation.height;

	    adj->lower = 0.0f;
	    adj->upper = (gfloat)(
		(hv->buf_len / hv->cells_per_row * hv->cell_height) +
		((hv->buf_len % hv->cells_per_row) ? hv->cell_height : 0)
	    );
	    adj->page_size = (gfloat)height;
	    adj->step_increment = hv->cell_height;
	    adj->page_increment = adj->page_size / 2.0f;

	    adj->value = adj->lower;

	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
	}

	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);

	return(0);
}

/*
 *      Deletes all data in the buffer.
 */
void HViewClear(hview_struct *hv)
{
	gboolean changed;
	GtkAdjustment *adj;
	GtkWidget *w;

	if(hv == NULL)
	    return;

	adj = hv->vadj;
	if(adj == NULL)
	    return;

	/* Clear buffer and reset scroll position */
	g_free(hv->buf);
	hv->buf = NULL;
	hv->buf_pos = 0;
	hv->buf_len = 0;
	hv->buf_sel_start = -1;
	hv->buf_sel_end = -1;
	hv->buf_hex_edit = 0;
	*hv->buf_hex_edit_buf = '\0';
	changed = (hv->modified != FALSE) ? TRUE : FALSE;
	hv->modified = FALSE;

	adj->value = 0.0f;

	/* Update adjustment */
	w = hv->view_da;
	if((w != NULL) && (hv->cells_per_row > 0) && (hv->cell_height > 0))
	{
            const gint height = w->allocation.height;

	    adj->lower = 0.0f;
	    adj->upper = (gfloat)(
		(hv->buf_len / hv->cells_per_row * hv->cell_height) +
		((hv->buf_len % hv->cells_per_row) ? hv->cell_height : 0)
	    );
	    adj->page_size = (gfloat)height;
	    adj->step_increment = hv->cell_height;
	    adj->page_increment = adj->page_size / 2.0f;

	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "value_changed");
	    gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed");
	}

	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);

	if(changed && (hv->changed_cb != NULL))
	    hv->changed_cb(hv, hv->changed_data);
}

/*
 *	Sets the edit mode, where edit_mode can be either
 *	HVIEW_EDIT_MODE_HEX or HVIEW_EDIT_MODE_ASCII.
 */
void HViewSetEditMode(hview_struct *hv, hview_edit_mode edit_mode)
{
	if(hv == NULL)
	    return;

	if(hv->edit_mode != edit_mode)
	{
	    hv->edit_mode = edit_mode;
	    HViewUpdate(hv);
	    HViewSetStatusMessage(hv, NULL, FALSE);
	}
}

/*
 *	Sets all bytes in the selected buffer region to the value of
 *	v.
 *
 *	Note that this function will not redraw the view, the calling
 *	function must call HViewDraw().
 */
void HViewSetSelected(hview_struct *hv, guint8 v)
{
	gint i, n;

	if(hv == NULL)
	    return;

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

	if(!HVIEW_IS_BUF_SELECTED(hv))
	    return;

	/* Get start of selected region i and the length of the
	 * selected region n.
	 */
	if(hv->buf_sel_start < hv->buf_sel_end)
	{
	    i = hv->buf_sel_start;
	    n = hv->buf_sel_end - hv->buf_sel_start + 1;
	}
	else
	{
	    i = hv->buf_sel_end;
	    n = hv->buf_sel_start - hv->buf_sel_end + 1;
	}

	/* Make sure length n at index i does not exceed the length of
	 * the buffer
	 */
	if((i + n) > hv->buf_len)
	    n = hv->buf_len - i;
	if(n <= 0)
	    return;

	/* Set the selected buffer region */
	memset(&hv->buf[i], v, n * sizeof(guint8));
}

/*
 * 	Sets the new cursor position.
 */
void HViewSetPosition(hview_struct *hv, gint pos)
{
	if(hv == NULL)
	    return;

	/* Sanitize the specified position */
	if(pos < 0)
	    pos = hv->buf_len - 1;
	if(pos >= hv->buf_len)
	    pos = hv->buf_len - 1;
	if(pos < 0)
	    pos = 0;

	/* Change in position? */
	if(pos != hv->buf_pos)
	{
	    hv->buf_pos = pos;
	    HViewQueueDraw(hv);
	    HViewUpdate(hv);
	    HViewSetStatusMessage(hv, NULL, FALSE);
	}
}

/*
 *	Select the given region.
 *
 *	If either start_pos or end_pos is < 0 then the hv will be
 *	unselected.
 */
void HViewSelect(
	hview_struct *hv, gint start_pos, gint end_pos
)
{
	if(hv == NULL)
	    return;

	/* Unselect? */
	if((start_pos < 0) || (end_pos < 0))
	{
	    hv->buf_sel_start = -1;
	    hv->buf_sel_end = -1;
	}
	else
	{
	    if(start_pos < end_pos)
	    {
		hv->buf_sel_start = start_pos;
		hv->buf_sel_end = end_pos;
	    }
	    else
	    {
		hv->buf_sel_start = end_pos;
		hv->buf_sel_end = start_pos;
	    }

	    if(hv->buf_sel_start >= hv->buf_len)
		hv->buf_sel_start = hv->buf_len - 1;
	    if(hv->buf_sel_end >= hv->buf_len)
		hv->buf_sel_end = hv->buf_len - 1;
	}

	HViewQueueDraw(hv);
	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);
}

/*
 *	Cut selected region and copy it to the clipboard.
 */
void HViewCut(hview_struct *hv)
{
	HViewCutCB(NULL, hv);
}

/*
 *      Copy selected region to the clipboard.
 */
void HViewCopy(hview_struct *hv)
{
	HViewCopyCB(NULL, hv);
}

/*
 *	Paste data from clipboard to cursor position.
 */
void HViewPaste(hview_struct *hv)
{
	HViewPasteCB(NULL, hv);
}

/*
 *	Insert a new byte at the cursor position.
 */
void HViewInsert(hview_struct *hv)
{
	HViewInsertCB(NULL, hv);
}

/*
 *	Delete selected bytes.
 */
void HViewDeleteSelected(hview_struct *hv)
{
	HViewDeleteCB(NULL, hv);
}


/*
 *	Searches for needle in the current buffer at the given
 *	start_pos.
 *
 *	Returns the buffer index if needle is found or -1 if needle
 *	cannot be found at or after the start_pos.
 */
gint HViewFind(
	hview_struct *hv, const guint8 *needle, gint needle_len,
	gint start_pos, gboolean case_sensitive
)
{
	gint i;
	const guint8 *buf;
	gint buf_len;

	if((hv == NULL) || (needle == NULL) || (needle_len <= 0))
	    return(-1);

	buf = hv->buf;
	buf_len = hv->buf_len;
	if((buf == NULL) || (buf_len <= 0))
	    return(-1);

	if(start_pos < 0)
	    start_pos = 0;

	i = start_pos;
	while(i < buf_len)
	{
	    /* First character of needle matches current buffer
	     * position's value?
	     */
	    if(case_sensitive ?
		(buf[i] == *needle) :
		(toupper((gchar)buf[i]) == toupper((gchar)*needle))
	    )
	    {
		gint n, i_rtn = i;

		/* Seek buffer position and needle position to nex
		 * index and compare the buffer with the rest of the
		 * needle.
		 */
		for(n = 1, i++; n < needle_len; n++, i++)
		{
		    if(i >= buf_len)
			break;

		    /* Buffer and needle no longer match? */
		    if(case_sensitive ?
			(buf[i] != needle[n]) :
			(toupper((gchar)buf[i]) != toupper((gchar)needle[n]))
		    )
			break;
		}
		/* Buffer matched needle length? */
		if(n >= needle_len)
		    return(i_rtn);
	    }

	    i++;
	}

	return(-1);
}


/*
 *	Checks if the Hex View's toplevel GtkWidget is a GtkWindow.
 */
gboolean HViewToplevelIsWindow(hview_struct *hv)
{
	GtkWidget *w = (hv != NULL) ? hv->toplevel : NULL;
	return((w != NULL) ? GTK_IS_WINDOW(w) : FALSE);
}

/*
 *	Returns the Hex View's toplevel GtkWidget.
 */
GtkWidget *HViewGetToplevelWidget(hview_struct *hv)
{
	return((hv != NULL) ? hv->toplevel : NULL);
}

/*
 *	Returns the Hex View's view GtkDrawingArea.
 */
GtkDrawingArea *HViewGetViewWidget(hview_struct *hv)
{
	return((hv != NULL) ? (GtkDrawingArea *)hv->view_da : NULL);
}

/*
 *	Returns the Hex View's GtkMenu.
 */
GtkMenu *HViewGetMenuWidget(hview_struct *hv)
{
	return((hv != NULL) ? (GtkMenu *)hv->menu : NULL);
}


/*
 *	Creates a new Hex View.
 */
hview_struct *HViewNew(GtkWidget *parent)
{
	const gint border_minor = 2;
	GdkFont *font;
	GdkColormap *colormap;
	GtkAdjustment *adj;
	GdkWindow *window;
	GtkWidget *w, *parent2, *parent3, *parent4, *parent5;
	hview_struct *hv = HVIEW(
	    g_malloc0(sizeof(hview_struct))
	);
	if(hv == NULL)
	    return(NULL);

	/* Reset values */
	hv->map_state = FALSE;
	hv->freeze_count = 0;
	hv->busy_count = 0;
	hv->dragging_selection = FALSE;
	hv->dragging_scroll = FALSE;
	hv->scroll_toid = 0;
	hv->draw_idle_id = 0;
	hv->buf = NULL;
	hv->buf_pos = 0;
	hv->buf_len = 0;
	hv->buf_sel_start = -1;
	hv->buf_sel_end = -1;
	hv->read_only = FALSE;
	hv->edit_mode = HVIEW_EDIT_MODE_HEX;
	hv->buf_hex_edit = 0;
	*hv->buf_hex_edit_buf = '\0';
	hv->modified = FALSE;
	hv->changed_cb = NULL;
	hv->changed_data = NULL;

	/* Load cursors */
	hv->busy_cur = gdk_cursor_new(GDK_WATCH);
	hv->text_cur = gdk_cursor_new(GDK_XTERM);
	hv->translate_cur = gdk_cursor_new(GDK_FLEUR);

	/* Load fonts */
	hv->font = font = gdk_font_load(
"-adobe-courier-medium-r-normal-*-12-*-*-*-m-*-iso8859-1"
	);
	hv->cell_width = 7;
	hv->cell_height = font->ascent + font->descent;
	hv->cells_per_row = 16;
	hv->address_x = 0;
	hv->hex_x = 90;
	hv->ascii_x = 450;

	/* Toplevel */
	hv->toplevel = w = gtk_vbox_new(FALSE, 0);
	if(GTK_IS_BOX(parent))
	    gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	else
	    gtk_container_add(GTK_CONTAINER(parent), w);
	parent = w;


	/* Table for view */
	w = gtk_table_new(1, 2, 0);
	gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Drawing area frame */
	w = gtk_frame_new(NULL);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 0, 1,
	    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
	    GTK_EXPAND | GTK_FILL | GTK_SHRINK,
	    0, 0
	);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_widget_show(w);
	parent3 = w;

	w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_show(w);
	parent3 = w;

	/* Hbox for column headings */	
	hv->heading_toplevel = w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Address column heading frame and label */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, hv->hex_x - hv->address_x, 22);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_label_new("Address");
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Hex column heading frame and label */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, hv->ascii_x - hv->hex_x, 22);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_label_new("Hex");
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* ASCII column heading frame and label */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, -1, 22);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);
	parent5 = w;
	w = gtk_label_new("ASCII");
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* View drawing area */
	hv->view_da = w = gtk_drawing_area_new();
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "configure_event",
	    GTK_SIGNAL_FUNC(HViewConfigureCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(HViewExposeCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(HViewKeyCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(HViewKeyCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(HViewButtonCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(HViewButtonCB), hv
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(HViewMotionCB), hv
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
	gtk_widget_realize(w);
	window = w->window;
	gtk_widget_show(w);

	/* Do not create view pixmap yet */
	hv->view_pm = NULL;

	/* Create graphics context for view drawing area */
	hv->gc = GDK_GC_NEW();

	hv->colormap = colormap = gdk_window_get_colormap(window);
	if(colormap != NULL)
	    gdk_colormap_ref(colormap);


	/* Create adjustment for vertical scrolling */
	hv->vadj = adj = (GtkAdjustment *)gtk_adjustment_new(
	    0.0, 0.0, 0.0, 0.0, 0.0, 0.0
	);
	gtk_signal_connect(
	    GTK_OBJECT(adj), "value_changed",
	    GTK_SIGNAL_FUNC(HViewValueChangedCB), hv
	);
	/* Vertical scroll bar */
	hv->vscrollbar = w = gtk_vscrollbar_new(adj);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    1, 2, 0, 1,
	    GTK_FILL,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	gtk_widget_show(w);



	/* Frame for status bar */
	hv->status_bar_toplevel = w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, -1, HVIEW_STATUS_BAR_HEIGHT);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Table in status bar frame */
	w = gtk_table_new(1, 1, FALSE);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;

	/* Status label frame */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_container_border_width(GTK_CONTAINER(w), 1);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 0, 1,
	    GTK_SHRINK | GTK_EXPAND | GTK_FILL,
	    GTK_FILL,
	    0, 0
	);
	gtk_widget_show(w);
	parent3 = w;

	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_show(w);
	parent3 = w;

	hv->status_bar_label = w = gtk_label_new("");
	gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_LEFT);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, border_minor);
	gtk_widget_show(w);


	/* Right-click menu */
	if(TRUE)
	{
#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					\
 );						\
}

	    GtkWidget *menu = GUIMenuCreate();
	    guint8 **icon;
	    const gchar *label;
	    gint accel_key;
	    guint accel_mods;
	    GtkAccelGroup *accelgrp = NULL;
	    gpointer mclient_data = hv;
	    void (*func_cb)(GtkWidget *w, gpointer);


	    icon = (guint8 **)icon_cut_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Corte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Coupure"
#elif defined(PROG_LANGUAGE_GERMAN)
"Schnitt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Taglio"
#elif defined(PROG_LANGUAGE_DUTCH)
"Snee"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Corte"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Snitt"
#else
"Cut"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewCutCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->cut_mi = w;

	    icon = (guint8 **)icon_copy_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Copia"
#elif defined(PROG_LANGUAGE_FRENCH)
"Copie"
#elif defined(PROG_LANGUAGE_GERMAN)
"Kopie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Copia"
#elif defined(PROG_LANGUAGE_DUTCH)
"Kopie"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Cópia"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kopi"
#else
"Copy"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewCopyCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->copy_mi = w;

	    icon = (guint8 **)icon_paste_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Pasta"
#elif defined(PROG_LANGUAGE_FRENCH)
"Pâte"
#elif defined(PROG_LANGUAGE_GERMAN)
"Paste"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Pasta"
#elif defined(PROG_LANGUAGE_DUTCH)
"Plakmiddel"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Pasta"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Masse"
#else
"Paste"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewPasteCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->paste_mi = w;

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_add_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"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 = 0;
	    accel_mods = 0;
	    func_cb = HViewInsertCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->insert_mi = w;

	    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 = 0;
	    accel_mods = 0;
	    func_cb = HViewDeleteCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->delete_mi = w;

	    DO_ADD_MENU_SEP

	    icon = NULL;
	    label = "Select All";
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewSelectAllCB;
	    DO_ADD_MENU_ITEM_LABEL
	    hv->select_all_mi = w;

	    icon = NULL;
	    label = "Unselect All";
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewUnselectAllCB;
	    DO_ADD_MENU_ITEM_LABEL  
	    hv->unselect_all_mi = w;    

	    DO_ADD_MENU_SEP   
	    hv->edit_mode_msep = w;

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Hex Redacte El Modo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Hex Editer Le Mode"
#elif defined(PROG_LANGUAGE_GERMAN)
"Hex Redigieren Sie Modus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Hex Redigere Il Modo"
#elif defined(PROG_LANGUAGE_DUTCH)
"Hex Redigeer Modus"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Hex Edite Modo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Hex Rediger Modus"
#else
"Hex Edit Mode"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewEditModeHexCB;
	    DO_ADD_MENU_ITEM_CHECK
	    hv->edit_mode_hex_mi = w;

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"ASCII Redacte El Modo"
#elif defined(PROG_LANGUAGE_FRENCH)
"ASCII Editer Le Mode"
#elif defined(PROG_LANGUAGE_GERMAN)
"ASCII Redigieren Sie Modus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"ASCII Redigere Il Modo"
#elif defined(PROG_LANGUAGE_DUTCH)
"ASCII Redigeer Modus"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"ASCII Edite Modo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"ASCII Rediger Modus"
#else
"ASCII Edit Mode"
#endif
	    ;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = HViewEditModeASCIICB;
	    DO_ADD_MENU_ITEM_CHECK
	    hv->edit_mode_ascii_mi = w;


	    hv->menu = menu;

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP
	}


	HViewUpdate(hv);
	HViewSetStatusMessage(hv, NULL, FALSE);

	return(hv);
}

/*
 *	Creates a new Hex View with a toplevel GtkWindow.
 */
hview_struct *HViewNewWithToplevel(
	gint width, gint height
)
{
	hview_struct *hv;
	GdkWindow *window;

	/* Create a toplevel GtkWindow for the Hex View*/
	GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_usize(
	    w,
	    (width > 0) ? width : HVIEW_DEF_WIDTH,
	    (height > 0) ? height : HVIEW_DEF_HEIGHT
	);
	gtk_window_set_title(GTK_WINDOW(w), HVIEW_DEF_TITLE);
#ifdef PROG_NAME
	gtk_window_set_wmclass(
	    GTK_WINDOW(w), "hexview", PROG_NAME
	);
#endif
	gtk_widget_realize(w);                                           
	window = w->window;
	if(window != NULL)
	{
	    GUISetWMIcon(
		window, 
		(guint8 **)icon_edit_48x48_xpm
	    );
	}
	gtk_container_border_width(GTK_CONTAINER(w), 0);

	/* Create Hex View and parent it to the toplevel GtkWindow */
	hv = HViewNew(w);

	gtk_widget_show(hv->toplevel);

	/* Change the Hex View's toplevel to the newly created
	 * toplevel GtkWindow
	 */
	hv->toplevel = w;

	gtk_signal_connect( 
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(HViewDeleteEventCB), hv
	);

	return(hv);
}

/*
 *	Shows or hides the Hex View's Heading.
 */
void HViewShowHeading(hview_struct *hv, gboolean show)
{
	GtkWidget *w;

	if(hv == NULL)
	    return;

	w = hv->heading_toplevel;
	if(w != NULL)
	{
	    if(show)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}
}

/*
 *	Shows or hides the Hex View's Status Bar.
 */
void HViewShowStatusBar(hview_struct *hv, gboolean show)
{
	GtkWidget *w;

	if(hv == NULL)
	    return;

	w = hv->status_bar_toplevel;
	if(w != NULL)
	{
	    if(show)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}
}

/*
 *	Sets the Hex View's as read only or writable.
 */
void HViewSetReadOnly(hview_struct *hv, gboolean read_only)
{
	if(hv == NULL)
	    return;

	if(hv->read_only != read_only)
	{
	    hv->read_only = read_only;
	    HViewQueueDraw(hv);
	    HViewUpdate(hv);
	}
}

/*
 *	Sets the Hex View's changed callback.
 */
void HViewSetChangedCB(
	hview_struct *hv,
	void (*changed_cb)(hview_struct *, gpointer),
	gpointer data
)
{
	if(hv == NULL)
	    return;

	hv->changed_cb = changed_cb;
	hv->changed_data = data;
}

/*
 *	Updates the Hex View's widgets to reflect current values.
 */
void HViewUpdate(hview_struct *hv)
{
	gboolean sensitive;

	if(hv == NULL)
	    return;

	hv->freeze_count++;

	/* Right click menu */

	/* Cut, Copy and Paste */
	sensitive = hv->read_only ?
	    FALSE : HVIEW_IS_BUF_SELECTED(hv);
	GTK_WIDGET_SET_SENSITIVE(hv->cut_mi, sensitive)
	sensitive = (HVIEW_IS_BUF_SELECTED(hv)) ? TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(hv->copy_mi, sensitive)
	sensitive = !hv->read_only;
	GTK_WIDGET_SET_SENSITIVE(hv->paste_mi, sensitive) 

	/* Insert and Delete */
	sensitive = !hv->read_only;
	GTK_WIDGET_SET_SENSITIVE(hv->insert_mi, sensitive) 
	GTK_WIDGET_SET_SENSITIVE(hv->delete_mi, sensitive) 

	/* Select All and Unselect All */
	sensitive = TRUE;
	GTK_WIDGET_SET_SENSITIVE(hv->select_all_mi, sensitive)
	sensitive = HVIEW_IS_BUF_SELECTED(hv);
	GTK_WIDGET_SET_SENSITIVE(hv->unselect_all_mi, sensitive)

	/* Edit mode */
	if(hv->read_only)
	{
	    gtk_widget_hide(hv->edit_mode_msep);
	    gtk_widget_hide(hv->edit_mode_hex_mi);
	    gtk_widget_hide(hv->edit_mode_ascii_mi);
	}
	else
	{
            gtk_widget_show(hv->edit_mode_msep);
            gtk_widget_show(hv->edit_mode_hex_mi);
            gtk_widget_show(hv->edit_mode_ascii_mi);
	}
	gtk_check_menu_item_set_active(
            GTK_CHECK_MENU_ITEM(hv->edit_mode_hex_mi),
            (hv->edit_mode == HVIEW_EDIT_MODE_HEX) ? TRUE : FALSE
        );
	gtk_check_menu_item_set_active(
	    GTK_CHECK_MENU_ITEM(hv->edit_mode_ascii_mi),
	    (hv->edit_mode == HVIEW_EDIT_MODE_ASCII) ? TRUE : FALSE
	);

        hv->freeze_count--;
}

/* 
 *	Redraws the Hex View.
 */
void HViewDraw(hview_struct *hv)
{
	HViewExposeCB(NULL, NULL, hv);
}

/*
 *	Queues a draw for the Hex View.
 */
void HViewQueueDraw(hview_struct *hv)
{
	if(hv == NULL)
	    return;

	/* Already queued? */
	if(hv->draw_idle_id > 0)
	    return;

	/* Queue redraw by adding a idle callback to the draw idle
	 * callback
	 */
	hv->draw_idle_id = gtk_idle_add_priority(
	    GTK_PRIORITY_REDRAW,
	    HViewDrawIdleCB,
	    hv
	);

}


/*
 *	Updates the Hex View's Status Bar message.
 *
 *	If mesg is NULL then the cursor position status message will
 *	be set.
 */
void HViewSetStatusMessage(
	hview_struct *hv, const gchar *mesg,
	gboolean allow_gtk_iteration
)
{
	GtkWidget *w = (hv != NULL) ? hv->status_bar_label : NULL;
	if(w == NULL)
	    return;

	if(mesg == NULL)
	{
	    gint i = hv->buf_pos;
	    gchar *buf;

	    if(hv->buf_hex_edit > 0)
	    {
		buf = g_strdup_printf(
"Address: 0x%.8X - Position: %i - Size: %i -\
 Mode: %s - Hex: 0x%c%c%c",
		    i, i,
		    hv->buf_len,
		    (hv->edit_mode == HVIEW_EDIT_MODE_HEX) ?
			"Hex" : "ASCII",
		    hv->buf_hex_edit_buf[0],
		    (hv->buf_hex_edit > 1) ?
			hv->buf_hex_edit_buf[1] : '_',
		    (hv->buf_hex_edit > 2) ? '_' : '\0'
		);
	    }
	    else
	    {
		if((i >= 0) && (i < hv->buf_len) && (hv->buf != NULL))
		    buf = g_strdup_printf(
"Address: 0x%.8X - Position: %i - Size: %i -\
 Mode: %s - Hex: 0x%.2X - ASCII: %c",
			i, i,
			hv->buf_len,
			(hv->edit_mode == HVIEW_EDIT_MODE_HEX) ?
			    "Hex" : "ASCII",
			hv->buf[i],
			isprint((gchar)hv->buf[i]) ?
			    (gchar)hv->buf[i] : ' '
		    );
		else
		    buf = STRDUP("");
	    }
	    gtk_label_set_text(GTK_LABEL(w), buf);
	    g_free(buf);
	}
	else
	{
	    gtk_label_set_text(GTK_LABEL(w), mesg);
	}

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

/*
 *	Marks the Hex View as busy or ready.
 */
void HViewSetBusy(hview_struct *hv, gboolean is_busy)
{
	GdkCursor *cur;
	GtkWidget *w = (hv != NULL) ? hv->toplevel : NULL;
	if(w == NULL)
	    return;

	/* If the toplevel is not a GtkWindow then set only for the
	 * view GtkDrawingArea
	 */
	if(!GTK_IS_WINDOW(hv))
	{
	    w = hv->view_da;
	    if(w == NULL)
		return;
	}

	if(is_busy)
	{
	    /* Increase busy count */
	    hv->busy_count++;

	    /* If already busy then don't change anything */
	    if(hv->busy_count > 1)
		return;

	    cur = hv->busy_cur;
	}
	else
	{
	    /* Reduce busy count */
	    hv->busy_count--;
	    if(hv->busy_count < 0)
		hv->busy_count = 0;

	    /* If still busy do not change anything */
	    if(hv->busy_count > 0)
		return;

	    cur = NULL;		/* Use default cursor */
	}

	/* Update toplevel window's cursor */
	if(w->window != NULL)
	{
	    gdk_window_set_cursor(w->window, cur);
	    gdk_flush();
	}
}

/*
 *	Maps the Hex View.
 */
void HViewMap(hview_struct *hv)
{
	GtkWidget *w = (hv != NULL) ? hv->toplevel : NULL;
	if(w == NULL)
	    return;

	if(GTK_IS_WINDOW(w))
	    gtk_widget_show_raise(w);
	else
	    gtk_widget_show(w);
	hv->map_state = TRUE;
}

/*
 *	Unmaps the hex view.
 */
void HViewUnmap(hview_struct *hv)
{
	GtkWidget *w = (hv != NULL) ? hv->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_hide(w);
	hv->map_state = FALSE;
}

/*
 *	Deletes the Hex View.
 */
void HViewDelete(hview_struct *hv)
{
	GdkColormap *colormap;

	if(hv == NULL)
	    return;

	if(hv->scroll_toid > 0)
	{
	    gtk_timeout_remove(hv->scroll_toid);
	    hv->scroll_toid = 0;
	}
	if(hv->draw_idle_id > 0)
	{
	    gtk_timeout_remove(hv->draw_idle_id);
	    hv->draw_idle_id = 0;
	}

	colormap = hv->colormap;

	g_free(hv->buf);
	hv->buf = NULL;
	hv->buf_pos = 0;
	hv->buf_len = 0;
	hv->buf_sel_start = -1;
	hv->buf_sel_end = -1;
	hv->buf_hex_edit = 0;
	*hv->buf_hex_edit_buf = '\0';

	GTK_WIDGET_DESTROY(hv->menu)
	GTK_WIDGET_DESTROY(hv->status_bar_label)
	GTK_WIDGET_DESTROY(hv->status_bar_toplevel)
	GTK_WIDGET_DESTROY(hv->vscrollbar)
	GTK_WIDGET_DESTROY(hv->view_da)
	GTK_WIDGET_DESTROY(hv->heading_toplevel)
	GTK_WIDGET_DESTROY(hv->toplevel)

	GDK_PIXMAP_UNREF(hv->view_pm)

	GDK_CURSOR_DESTROY(hv->busy_cur)
	GDK_CURSOR_DESTROY(hv->text_cur)
	GDK_CURSOR_DESTROY(hv->translate_cur)

	GDK_GC_UNREF(hv->gc)

	GDK_COLORMAP_UNREF(hv->colormap)

	GDK_FONT_UNREF(hv->font)

	g_free(hv);
}
