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

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

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

#include "cfg.h"
#include "edvtypes.h"
#include "edvpixmap.h"
#include "edvhistory.h"
#include "edvhistoryfio.h"
#include "edvmimetypes.h"
#include "browser.h"
#include "endeavour.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcb.h"
#include "edvcfglist.h"
#include "config.h"


/* String Formatting */
gchar *EDVGetObjectSizeStr(edv_core_struct *core_ptr, gulong i);

/* Pixmaps */
void EDVResizePixmap(
	GdkPixmap *pixmap, GdkBitmap *mask,
	gint width, gint height,
	GdkPixmap **pixmap_rtn, GdkBitmap **mask_rtn
);
gboolean EDVLoadPixmap(
	edv_core_struct *core_ptr,
	guint8 **data, const gchar *name,
	GdkPixmap **pixmap_rtn, GdkBitmap **mask_rtn
);
GtkWidget *EDVNewPixmapWidget(
	edv_core_struct *core_ptr,
	guint8 **data, const gchar *name 
);

/* Cursors */
GdkCursor *EDVGetCursor(
	edv_core_struct *core_ptr, gint cursor_code
);

/* GtkCTree Nodes */
gint EDVNodeGetLevel(GtkCTreeNode *node);
GtkCTreeNode *EDVNodeGetParent(GtkCTreeNode *node);
GtkCTreeNode *EDVNodeGetChild(GtkCTreeNode *node);
GtkCTreeNode *EDVNodeGetSibling(GtkCTreeNode *node);
GtkCTreeNode *EDVNodeGetToplevel(GtkCTree *ctree);

/* GtkCList & GtkCTree Utilities */
gint EDVCListGetSelectedLast(
	GtkCList *clist, GtkCListRow **row_ptr_rtn
);
GtkCTreeNode *EDVCTreeGetSelectedLast(
	GtkCTree *ctree, GtkCTreeRow **row_ptr_rtn
);
gboolean EDVNodeGetIndex(
	GtkCTree *ctree, GtkCTreeNode *node,
	gint *row_rtn, gint *column_rtn
);
GtkCTreeNode *EDVNodeGetByCoordinates(
	GtkCTree *ctree, gint x, gint y
);
void EDVScrollCListToPosition(
	GtkCList *clist, gfloat hpos, gfloat vpos
);

/* Endeavour Window Matching */
gboolean EDVMatchWindowFromToplevel(
	edv_core_struct *core_ptr, GtkWidget *toplevel,
	gint *browser_num,
	gint *imbr_num,
	gint *archiver_num,
	gint *recbin_num
);

/* Object Checking */
gboolean EDVIsObjectNameValid(const gchar *name);
gboolean EDVIsObjectHidden(edv_object_struct *obj);

/* Write Protect Utilities */
gboolean EDVCheckWriteProtect(
	edv_core_struct *core_ptr,
	gboolean verbose, GtkWidget *toplevel
);

/* Permission Checking */
gboolean EDVCheckUIDIsOwner(gint effective_uid, gint owner_id);
gboolean EDVCheckIsOwner(edv_core_struct *core_ptr, gint owner_id);
gboolean EDVIsObjectAccessable(
	edv_core_struct *core_ptr, edv_object_struct *obj
);
gboolean EDVIsObjectReadable(
	edv_core_struct *core_ptr, edv_object_struct *obj
);
gboolean EDVIsObjectWriteable(
	edv_core_struct *core_ptr, edv_object_struct *obj
);
gboolean EDVIsObjectExecutable(
	edv_core_struct *core_ptr, edv_object_struct *obj
);

/* Internal Supported Object Type Checking */
gboolean EDVCheckImlibImage(
	edv_core_struct *core_ptr, const gchar *name
);
gboolean EDVCheckEDVArchiverArchive(
	edv_core_struct *core_ptr, const gchar *name
);

/* Object MIME Type Matching Utilities */
gint EDVMatchObjectIcon(
	edv_device_struct **device, gint total_devices,
	edv_mimetype_struct **mimetype, gint total_mimetypes,
	edv_object_type type,	/* One of EDV_OBJECT_TYPE_* */
	const gchar *path,	/* Full path or just name */
	gboolean link_valid,
	edv_permission_flags permissions,	/* Any of EDV_PERMISSION_* */
	gint icon_size,         /* 0 = small, 1 = medium, 2 = large */
	GdkPixmap **pixmap_closed, GdkBitmap **mask_closed,
	GdkPixmap **pixmap_opened, GdkBitmap **mask_opened,
	GdkPixmap **pixmap_extended, GdkBitmap **mask_extended,
	GdkPixmap **pixmap_hidden, GdkBitmap **mask_hidden
);
gint EDVMatchObjectTypeString(
	edv_mimetype_struct **mimetype, gint total_mimetypes,
	edv_object_type type,	/* One of EDV_OBJECT_TYPE_* */
	edv_permission_flags permissions,
	const gchar *path,      /* Full path or just name */
	gchar **type_str
);

/* Message Output */
void EDVMessage(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
);
void EDVMessageInfo(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
);
void EDVMessageWarning(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
);
void EDVMessageError(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
);
void EDVMessageFromFile(
	const gchar *path, const gchar *title,
	gint cdialog_icon,
	GtkWidget *toplevel
);

/* Play Sound */
void EDVBeep(edv_core_struct *core_ptr);
void EDVPlaySoundBeep(edv_core_struct *core_ptr);
void EDVPlaySoundInfo(edv_core_struct *core_ptr);
void EDVPlaySoundQuestion(edv_core_struct *core_ptr);
void EDVPlaySoundWarning(edv_core_struct *core_ptr);
void EDVPlaySoundError(edv_core_struct *core_ptr);
void EDVPlaySoundCompleted(edv_core_struct *core_ptr);

/* Window Centering */
void EDVCenterWindowToWindow(GtkWidget *w1, GtkWidget *w2);
void EDVCenterWindowToWindowOffset(
	GtkWidget *w1, GtkWidget *w2, gint x_offset, gint y_offset
);

/* GtkEntry Setup Utilities */
void EDVEntrySetDND(edv_core_struct *core_ptr, GtkWidget *w);
void EDVEntrySetCompletePath(edv_core_struct *core_ptr, GtkWidget *w);

/* Scrolled Window Setup Utilities */
GtkWidget *EDVScrolledWindowNew(
	GtkPolicyType hscrollbar_policy,
	GtkPolicyType vscrollbar_policy,
	GtkWidget **event_box_rtn,
	GtkWidget **vbox_rtn
);
static gint EDVScrolledWindowEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

/* Copy Object Path Utilities */
void EDVCopyDiskObjectsToDDEPath(
	edv_core_struct *core_ptr,
	edv_object_struct **list, gint total,
	GtkWidget *owner
);
void EDVCopyDiskObjectsToDDEURL(
	edv_core_struct *core_ptr,
	edv_object_struct **list, gint total,
	GtkWidget *owner
);

/* Style Utilities */
GtkRcStyle *EDVCreateRCStyleFromCfg(const cfg_style_struct *style);

/* History Utilities */
void EDVAppendHistory(
	edv_core_struct *core_ptr,
	edv_history_type type,	/* One of EDV_HISTORY_* */
	gulong time_start,	/* Time the operation first started */
	gulong time_end,	/* Time the operation ended */
	gint status,		/* Result of operation */
	const gchar *src_path,	/* Source Object/Operation/Value */
	const gchar *tar_path,	/* Target Object/Operation/Value */
	const gchar *comments	/* Comments */
);


#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 EDV_SCROLLED_WINDOW_DATA_TRANSLATE_CURSOR_KEY	\
	"edv_scrolled_window_translate_cursor"


static gchar *G_STRCAT(gchar *s, const gchar *s2)
{
	if(s != NULL) {
	    if(s2 != NULL) {
		gchar *sr = g_strconcat(s, s2, NULL);
		g_free(s);
		s = sr;
	    }
	} else {
	    if(s2 != NULL)
		s = STRDUP(s2);
	    else
		s = STRDUP("");
	}
	return(s);
}


/*
 *	Returns a statically allocated string describing the size
 *	of the object specified by i in units defined by the
 *	configuration on the given core structure.
 *
 *	The returned string will be no longer than 256 bytes and
 *	never NULL.
 */
gchar *EDVGetObjectSizeStr(edv_core_struct *core_ptr, gulong i)
{
	gint comma_countdown, slen;
	gchar ss[80], *ss_ptr;
	static gchar ts[80], *ts_ptr;


	g_snprintf(ss, sizeof(ss), "%ld", i);
	slen = STRLEN(ss);

	/* 3 digits or less? (no commas needed) */
	if(slen <= 3)
	{
	    strcpy(ts, ss);
	    return(ts);
	}

	ts_ptr = ts;
	ss_ptr = ss;

	/* Initialize comma counter */
	comma_countdown = slen % 3;
	if(comma_countdown <= 0)
	    comma_countdown = 3;

	/* Iterate through size string until end is reached */
	while(*ss_ptr != '\0')
	{
	    /* Reset comma counter and put in a comma? */
	    if(comma_countdown <= 0)
	    {
		*ts_ptr++ = ',';
		comma_countdown = 3;
	    }

	    *ts_ptr++ = *ss_ptr++;
	    comma_countdown--;
	}

	/* Null terminate return string */
	*ts_ptr = '\0';

	return(ts);
}


/*
 *	Resizes the pixmap and mask to the specified new size.
 */
void EDVResizePixmap(
	GdkPixmap *pixmap, GdkBitmap *mask,
	gint width, gint height,
	GdkPixmap **pixmap_rtn, GdkBitmap **mask_rtn
)
{
	gint old_width, old_height;

	if(pixmap_rtn != NULL)
	    *pixmap_rtn = NULL;
	if(mask_rtn != NULL)
	    *mask_rtn = NULL;

	if((pixmap == NULL) || (width <= 0) || (height <= 0))
	    return;

	gdk_window_get_size(pixmap, &old_width, &old_height);
	if((old_width <= 0) || (old_height <= 0))
	    return;

	/* No change in size? */
	if((old_width == width) && (old_height == height))
	{
	    if(pixmap_rtn != NULL)
	    {
		if(pixmap != NULL)
		    gdk_pixmap_ref(pixmap);
		*pixmap_rtn = pixmap;
	    }
	    if(mask_rtn != NULL)
	    {
		if(mask != NULL)
		    gdk_bitmap_ref(mask);
		*mask_rtn = mask;
	    }
	    return;
	}

	/* Resize pixmap */
	if(pixmap_rtn != NULL)
	{
	    gint sw, sh, sbpl, tbpl;
	    guint8 *tdata, *sdata;
	    GdkGC *gc = GDK_GC_NEW();

	    /* Get source RGBA data from the specified pixmap */
	    sdata = gdk_get_rgba_image(
		pixmap,
		NULL,
		&sw, &sh, &sbpl
	    );
	    if(sdata == NULL)
	    {
		GDK_GC_UNREF(gc)
		return;
	    }

	    /* Create target RGBA data */
	    tbpl = width * 4;
	    tdata = (guint8 *)g_malloc(tbpl * height);
	    if(tdata == NULL)
	    {
		g_free(sdata);
		GDK_GC_UNREF(gc)
		return;
	    }

	    /* Copy/resize source RGBA data to the target RGBA data */
	    GUIImageBufferResize(
		4,
		sdata, sw, sh, sbpl,
		tdata, width, height, tbpl
	    );

	    g_free(sdata);

	    /* Create target pixmap */
	    *pixmap_rtn = GDK_PIXMAP_NEW(width, height);
	    if(*pixmap_rtn == NULL)
	    {
		g_free(tdata);
		GDK_GC_UNREF(gc)
		return;
	    }

	    /* Put resized target RGBA data to the target pixmap */
	    gdk_draw_rgb_32_image(
		*pixmap_rtn, gc,
		0, 0,
		width, height,
		GDK_RGB_DITHER_NORMAL,
		tdata, tbpl
	    );

	    /* Resize mask */
	    if(mask_rtn != NULL)
		*mask_rtn = GUICreateBitmapFromDataRGBA(
		    width, height, tbpl,
		    tdata, 0x80,
		    NULL
		);

	    g_free(tdata);
	    GDK_GC_UNREF(gc)
	}
}

/*
 *	Returns the pixmap and mask matched from the core's list of
 *	edv_pixmaps specified by name.
 *
 *	If no edv_pixmap exists in the list with the specified name
 *	then a new edv_pixmap will be added to the list.
 *
 *	The returned pixmap and mask will have a ref count added to
 *	them.
 *
 *	Returns TRUE on matched pixmap and mask or FALSE on error.
 */
gboolean EDVLoadPixmap(
	edv_core_struct *core_ptr,
	guint8 **data, const gchar *name,
	GdkPixmap **pixmap_rtn, GdkBitmap **mask_rtn 
)
{
	edv_pixmap *p;

	if(pixmap_rtn != NULL)
	    *pixmap_rtn = NULL;
	if(mask_rtn != NULL)
	    *mask_rtn = NULL;

	if((core_ptr == NULL) || (data == NULL) || (name == NULL))
	    return(FALSE);

	/* Look for an edv_pixmap in the given list that matches
	 * the specified name or add a new edv_pixmap to the list if
	 * one does not exist
	 */
	core_ptr->pixmap_list = EDVPixmapListNew(
	    core_ptr->pixmap_list,
	    data, name,
	    &p
	);

	if(p != NULL)
	{
	    /* Set return pixmap and mask and add a ref count to them */
	    if(pixmap_rtn != NULL)
	    {
		if(p->pixmap != NULL)
		    gdk_pixmap_ref(p->pixmap);
		*pixmap_rtn = p->pixmap;
	    }
	    if(mask_rtn != NULL)
	    {
		if(p->mask != NULL)
		    gdk_bitmap_ref(p->mask);
		*mask_rtn = p->mask;
	    }
	    return(TRUE);
	}
	else
	{
	    return(FALSE);
	}
}

/*
 *	Creates a new GtkPixmap from the specified XPM data.
 *
 *	An edv_pixmap from the core's list of pixmaps will be used
 *	if one exists matching the specified name or a new edv_pixmap
 *	will be added to the list if one does not exist.
 */
GtkWidget *EDVNewPixmapWidget(
	edv_core_struct *core_ptr,
	guint8 **data, const gchar *name 
)
{
	GdkPixmap *pixmap, *mask;

	if(EDVLoadPixmap(core_ptr, data, name, &pixmap, &mask))
	{
	    GtkWidget *w = gtk_pixmap_new(pixmap, mask);
	    GDK_PIXMAP_UNREF(pixmap)
	    GDK_BITMAP_UNREF(mask)
	    return(w);
	}
	else
	{
	    return(NULL);
	}
}


/*
 *	Returns the GdkCursor specified by cursor_code or NULL on
 *	error or failed match.
 */
GdkCursor *EDVGetCursor(
	edv_core_struct *core_ptr, gint cursor_code
)
{
	gint i;
	edv_cursor_struct *cur;


	if(core_ptr == NULL)
	    return(NULL);

	for(i = 0; i < core_ptr->total_cursors; i++)
	{
	    cur = core_ptr->cursor[i];
	    if(cur == NULL)
		continue;

	    if(cur->code == cursor_code)
		return(cur->cursor);
	}

	return(NULL);
}

/*
 *	Returns the level of the given node or 0 if the given node
 *	is NULL.
 */
gint EDVNodeGetLevel(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr = (node != NULL) ? GTK_CTREE_ROW(node) : NULL;
	return((row_ptr != NULL) ? row_ptr->level : 0);
}

/*
 *	Returns the parent of the given node or NULL if the given node
 *	does not have a parent.
 */
GtkCTreeNode *EDVNodeGetParent(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr = (node != NULL) ? GTK_CTREE_ROW(node) : NULL;
	return((row_ptr != NULL) ? row_ptr->parent : NULL);
}

/*
 *	Returns the first child of the given node or NULL if the node
 *	does not have any children.
 */
GtkCTreeNode *EDVNodeGetChild(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr = (node != NULL) ? GTK_CTREE_ROW(node) : NULL;
	return((row_ptr != NULL) ? row_ptr->children : NULL);
}

/*
 *      Returns the next sibling of the given node or NULL if the node
 *	does not have a next sibling.
 */
GtkCTreeNode *EDVNodeGetSibling(GtkCTreeNode *node)
{
	GtkCTreeRow *row_ptr = (node != NULL) ? GTK_CTREE_ROW(node) : NULL;
	return((row_ptr != NULL) ? row_ptr->sibling : NULL);
}

/*
 *	Returns the first toplevel node on the given GtkCTree widget
 *	or NULL if the GtkCTree does not have one.
 */
GtkCTreeNode *EDVNodeGetToplevel(GtkCTree *ctree)
{
	/* Here we just return the first node, since it is always
	 * gauranteed to be the toplevel node.
	 */
	return((ctree != NULL) ? gtk_ctree_node_nth(ctree, 0) : NULL);
}


/*
 *	Returns the index of the last selected row on the given clist.
 *
 *	If row_ptr_rtn is not NULL and there exists a selected row then
 *	*row_ptr_rtn will be updated with the pointer to the row
 *	structure.
 *
 *	Returns -1 on error or no row selected, if a valid row index is
 *	returned it can be considered to be valid and allocated.
 */
gint EDVCListGetSelectedLast(
	GtkCList *clist, GtkCListRow **row_ptr_rtn
)
{
	gint row;
	GList *glist;


	if(row_ptr_rtn != NULL)
	    *row_ptr_rtn = NULL;

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

	/* Get last selected row index or -1 if there is none */
	glist = clist->selection_end;
	row = (glist != NULL) ? (gint)glist->data : -1;

	/* Check if we need to get a row pointer and that the selected
	 * row index is in bounds
	 */
	if((row >= 0) && (row < clist->rows) &&
	   (row_ptr_rtn != NULL) && (clist->row_list != NULL)
	)
	{
	    glist = g_list_nth(clist->row_list, row);
	    *row_ptr_rtn = (glist != NULL) ?
		(GtkCListRow *)glist->data : NULL;
	    if(*row_ptr_rtn == NULL)
		row = -1;
	}

	return(row);
}

/*
 *      Returns the pointer of the last selected node on the given ctree.
 *
 *      If row_ptr_rtn is not NULL and there exists a selected node then
 *      *row_ptr_rtn will be updated with the pointer to the node's row
 *	structure.
 *
 *      Returns NULL on error or no node selected, if a valid node is
 *      returned it can be considered to be valid and allocated.
 */
GtkCTreeNode *EDVCTreeGetSelectedLast(
	GtkCTree *ctree, GtkCTreeRow **row_ptr_rtn
)
{
	GList *glist;
	GtkCList *clist;
	GtkCTreeNode *node;


	if(row_ptr_rtn != NULL)
	    *row_ptr_rtn = NULL;

	if(ctree == NULL)
	    return(NULL);

	clist = GTK_CLIST(ctree);

	/* Get last selected node or -1 if there is none */
	glist = clist->selection_end;
	node = (glist != NULL) ? (GtkCTreeNode *)glist->data : NULL;

	/* Check if we need to get a row pointer and that there is a
	 * selected node
	 */
	if((node != NULL) && (row_ptr_rtn != NULL))
	{
	    *row_ptr_rtn = GTK_CTREE_ROW(node);
	    if(*row_ptr_rtn == NULL)
		node = NULL;
	}

	return(node);
}


/*
 *	Returns the column and row indexes of the given GtkCTreeNode node
 *	relative to the given GtkCTree widget.
 *
 *	Returns TRUE if the indexes are found or FALSE on failed match.
 */
gboolean EDVNodeGetIndex(
	GtkCTree *ctree, GtkCTreeNode *node,
	gint *row_rtn, gint *column_rtn
)
{
	gint row;
	const GList *glist;
	GtkCList *clist;
	const GtkCTreeRow *row_ptr;


	/* Reset returns */
	if(row_rtn != NULL)
	    *row_rtn = -1;
	if(column_rtn != NULL)
	    *column_rtn = -1;

	if((ctree == NULL) || (node == NULL))
	    return(FALSE);

	clist = GTK_CLIST(ctree);

	/* Get the row pointer the given node */
	row_ptr = GTK_CTREE_ROW(node);
	if(row_ptr == NULL)
	    return(FALSE);

	/* Count rows until we encounter the given node's row */
	row = 0;
	glist = clist->row_list;
	while(glist != NULL)
	{
	    if(row_ptr == (const GtkCTreeRow *)glist->data)
		break;

	    row++;
	    glist = g_list_next(glist);
	}
	/* Failed to find row? */
	if(glist == NULL)
	    return(FALSE);

	/* Update returns */
	if(row_rtn != NULL)
	    *row_rtn = row;
	/* Column is always the ctree column */
	if(column_rtn != NULL)
	    *column_rtn = ctree->tree_column;

	return(TRUE);
}

/*
 *	Returns the tree node that matches the given coordinates on the
 *	given GtkCTree widget.
 */
GtkCTreeNode *EDVNodeGetByCoordinates(
	GtkCTree *ctree, gint x, gint y
)
{
	gint row, column;
	GtkCList *clist;


	if(ctree == NULL)
	    return(NULL);

	clist = GTK_CLIST(ctree);

	/* Find row and column based on given coordinates */
	if(!gtk_clist_get_selection_info(
	    clist, x, y, &row, &column
	))
	{
	    row = clist->rows;
	    column = 0;
	}
	if(row < 0)
	    row = 0;

	/* Get tree node matching the calculated row */
	return(gtk_ctree_node_nth(ctree, row));
}

/*
 *	Scroll the given clist to the given position.
 *
 *	A "value_changed" signal will be emitted to each of the clist's
 *	horizontal and vertical GtkAdjustments.
 */
void EDVScrollCListToPosition(
	GtkCList *clist, gfloat hpos, gfloat vpos
)
{
	GtkAdjustment *adj;

	if(clist == NULL)
	    return;

	adj = clist->hadjustment;
	if(adj != NULL)
	{
	    if(hpos > (adj->upper - adj->page_size))
		hpos = adj->upper - adj->page_size;
	    if(hpos < adj->lower)
		hpos = adj->lower;
	    gtk_adjustment_set_value(adj, hpos);
	}

	adj = clist->vadjustment;
	if(adj != NULL)
	{
	    if(vpos > (adj->upper - adj->page_size))
		vpos = adj->upper - adj->page_size;
	    if(vpos < adj->lower)
		vpos = adj->lower;
	    gtk_adjustment_set_value(adj, vpos);
	}
}


/*
 *	Checks which window the given toplevel GtkWindow belongs to.
 *
 *	Returns TRUE on match or FALSE on failed match.
 */
gboolean EDVMatchWindowFromToplevel(
	edv_core_struct *core_ptr, GtkWidget *toplevel,
	gint *browser_num,
	gint *imbr_num,
	gint *archiver_num,
	gint *recbin_num
)
{
	gint i;
	const edv_browser_struct *browser;
	const edv_imbr_struct *imbr;
	const edv_archiver_struct *archiver;
	const edv_recbin_struct *recbin;

	if(browser_num != NULL)
	    *browser_num = -1;
	if(imbr_num != NULL)
	    *imbr_num = -1;
	if(archiver_num != NULL)
	    *archiver_num = -1;
	if(recbin_num != NULL)
	    *recbin_num = -1;

	if((core_ptr == NULL) || (toplevel == NULL))
	    return(FALSE);

	/* Begin checking which window's toplevel matches the given
	 * toplevel
	 */

	/* File Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	{
	    browser = core_ptr->browser[i];
	    if(browser == NULL)
		continue;

	    if(browser->toplevel == toplevel)
	    {
		if(browser_num != NULL)
		    *browser_num = i;
		return(TRUE);
	    }
	}

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	{
	    imbr = core_ptr->imbr[i];
	    if(imbr == NULL)
		continue;

	    if(imbr->toplevel == toplevel)
	    {
		if(imbr_num != NULL)
		    *imbr_num = i;
		return(TRUE);
	    }
	}

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	{
	    archiver = core_ptr->archiver[i];
	    if(archiver == NULL)
		continue;

	    if(archiver->toplevel == toplevel)
	    {
		if(archiver_num != NULL)
		    *archiver_num = i;
		return(TRUE);
	    }
	}

	/* Recycle Bin */
	recbin = core_ptr->recbin;
	if(recbin != NULL)
	{
	    if(recbin->toplevel == toplevel)
	    {
		if(recbin_num != NULL)
		    *recbin_num = i;
		return(TRUE);
	    }
	}

	return(FALSE);
}


/*
 *	Returns TRUE if the given disk object name has a valid notation.
 */
gboolean EDVIsObjectNameValid(const gchar *name)
{
	if(STRISEMPTY(name))
	    return(FALSE);

	/* No special notations allowed */
	if(!strcmp(name, ".."))
	    return(FALSE);
	if(!strcmp(name, "."))
	    return(FALSE);
	if(!strcmp(name, "?"))
	    return(FALSE);
	if(!strcmp(name, "*"))
	    return(FALSE);

	/* No blanks allowed as first char */
	if(*name == ' ')
	    return(FALSE);
	if(*name == '\t')
	    return(FALSE);

	/* No deliminators allowed */
	if(strchr(name, G_DIR_SEPARATOR) != NULL)
	    return(FALSE);

	return(TRUE);
}

/*
 *      Returns TRUE if the object is `hidden' by checking the given name
 *      of the object and testing if the first character if the name is
 *      a '.' character.
 */
gboolean EDVIsObjectHidden(edv_object_struct *obj)
{
	const gchar *name = (obj != NULL) ? obj->name : NULL;
	if(STRISEMPTY(name))
	    return(FALSE);

	/* Check if the first character of the name is a '.' */
	if(*name == '.')
	{
	    /* Special directory notations should be excluded */
	    if(!g_strcasecmp(name, "..") || !g_strcasecmp(name, "."))
		return(FALSE);
	    else
		return(TRUE);
	}
	else
	{
	    return(FALSE);
	}
}

/*
 *	Returns TRUE if the write protect is enabled on the given core
 *	structure's configuration list.
 *
 *	If verbose is set to TRUE and write protect is enabled then a 
 *	standard message will be printed to the user indicating that write
 *	protect is enabled and how to disable it.
 */
gboolean EDVCheckWriteProtect(
	edv_core_struct *core_ptr,
	gboolean verbose, GtkWidget *toplevel
)
{
	gboolean write_protect;

	if(core_ptr == NULL)
	    return(FALSE);

	/* Get configuration */
	write_protect = CFGItemListGetValueI(
	    core_ptr->cfg_list, EDV_CFG_PARM_WRITE_PROTECT
	);
	/* Is write protect enabled and verbose specified? */
	if(write_protect && verbose)
	{
	    EDVPlaySoundWarning(core_ptr);
	    EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Escriba La Operación Negada",
"La operación especificada no se permite porque escribe\n\
protege está en.\n",
"Si usted desea realizar la operación de thr entonces\n\
usted debe girar escribe lejos protege yendo a\n\
Escenarios->Escribe Protege.\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"L'Opération D'écriture Niée",
"L'opération spécifiée n'est pas permise parce que protége\n\
en écriture est sur.\n",
"Si vous souhaitez exécuter l'opération de thr alors vous\n\
devez tourner de protége en écriture en allant aux\n\
Montages->Protége En écriture.\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Schreiben Sie Betrieb",
"Verweigert wird, der. Der angegebene betrieb ist nicht\n\
zugelassen, weil schreibgeschützt auf ist.\n",
"Wenn sie wünschen, thr betrieb durchzuführen, dann\n\
müssen sie schreibgeschützt durch gehen zu\n\
Settings->Schreibgeschützt Ausschalten.\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Scrivere L'Operazione Negata",
"L'operazione specificata non è permessa perché scrive\n\
protegge è su.\n",
"Se lei desidera eseguire l'operazione di thr poi lei\n\
deve spegnere scrive protegge da andare ai\n\
Montaggi->Scrive Protegge.\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrijf Werking Ontkende",
"De gespecificeerde werking is niet toegestaan omdat\n\
schrijft op beschermt, is.\n",
"Indien u wenst thr werking te verrichten dan beschermt\n\
u door gaan aan Settings->moet uitschakelen schrijft,\n\
beschermt, Schrijft.\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Escreva Operação Negado",
"A operação especificada não é permitida porque escreve\n\
protege está em.\n",
"Se deseja executar operação de thr então você deve\n\
desligar deve escrever protege por ir a\n\
Settings->Escreve Protege.\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Skriv Nektet Drift",
"Den spesifiserte driften tillater ikke fordi\n\
skrivebeskyttet er på.\n",
"Om de ønsker gjennomføre thr drift skrur da de av\n\
skrivebeskyttet ved å dra til Innstillinger->Skrivebeskyttet.\n",
#else
"Write Operation Denied",
"The specified operation is not permitted because\n\
Write Protect is on.\n",
"If you wish to perform this operation then you must turn\n\
off Write Protect by going to Settings->Write Protect.\n",
#endif
		toplevel
	    );
	}

	return(write_protect);
}


/*
 *	Checks if the given effective user id owns the object who's
 *	owner is the owner_id.
 */
gboolean EDVCheckUIDIsOwner(gint effective_uid, gint owner_id)
{
	return(
	   (effective_uid == 0) ||
	   (effective_uid == owner_id)
	);
}

/*
 *	Checks if the effective user id of this process owns the object
 *	sho's owner is the owner_id.
 */
gboolean EDVCheckIsOwner(edv_core_struct *core_ptr, gint owner_id)
{
	if(core_ptr == NULL)
	    return(FALSE);
	else
	    return(EDVCheckUIDIsOwner(core_ptr->effective_user_id, owner_id));
}

/*
 *	Checks if the user or group ID of the process can access the
 *	specified directory object.
 *
 *	Access is defined as the directory object being executable,
 *	since any directory set x means it is accessable.
 */
gboolean EDVIsObjectAccessable(
	edv_core_struct *core_ptr, edv_object_struct *obj
)
{
	gint euid, eguid;
	edv_permission_flags p;

	if((core_ptr == NULL) || (obj == NULL))
	    return(FALSE);

	euid = core_ptr->effective_user_id;
	eguid = core_ptr->effective_group_id;
	p = obj->permissions;

	/* Sticky and at least one catagory has execute permission? */
	if((p & EDV_PERMISSION_STICKY) &&
	   (p & (EDV_PERMISSION_UEXECUTE | EDV_PERMISSION_GEXECUTE |
		 EDV_PERMISSION_AEXECUTE))
	)
	    return(TRUE);	

	/* Anyone can execute? */
	if(p & EDV_PERMISSION_AEXECUTE)
	    return(TRUE);

	/* Group can execute and is member of group? */
	if((p & EDV_PERMISSION_GEXECUTE) &&
	   (eguid == obj->group_id)
	)
	    return(TRUE);

	/* Owner can execute, is root, or is the owner? */
	if((p & EDV_PERMISSION_UEXECUTE) &&
	   ((euid == 0) || (euid == obj->owner_id))
	)
	    return(TRUE);

	return(FALSE);
}

/*
 *      Checks if the user or group ID of the process can read the
 *	specified object.
 */
gboolean EDVIsObjectReadable(
	edv_core_struct *core_ptr, edv_object_struct *obj
)
{
	gint euid, eguid;
	edv_permission_flags p;

	if((core_ptr == NULL) || (obj == NULL))
	    return(FALSE);

	euid = core_ptr->effective_user_id;
	eguid = core_ptr->effective_group_id;
	p = obj->permissions;

	/* Sticky and at least one catagory has read permission? */
	if((p & EDV_PERMISSION_STICKY) &&
	   (p & (EDV_PERMISSION_UREAD | EDV_PERMISSION_GREAD |
		 EDV_PERMISSION_AREAD))
	)
	    return(TRUE);

	/* Anyone can read? */
	if(p & EDV_PERMISSION_AREAD)
	    return(TRUE);

	/* Group can read and is member of group? */
	if((p & EDV_PERMISSION_GREAD) &&
	   (eguid <= obj->group_id)
	)
	    return(TRUE);

	/* Owner can read and is a owner? */
	if((p & EDV_PERMISSION_UREAD) &&
	   ((euid == 0) || (euid == obj->owner_id))
	)
	    return(TRUE);

	return(FALSE);
}

/*
 *      Checks if the user or group ID of the process can write the
 *	specified object.
 */
gboolean EDVIsObjectWriteable(
	edv_core_struct *core_ptr, edv_object_struct *obj
)
{
	gint euid, eguid;
	edv_permission_flags p;

	if((core_ptr == NULL) || (obj == NULL))
	    return(FALSE);

	euid = core_ptr->effective_user_id;
	eguid = core_ptr->effective_group_id;
	p = obj->permissions;

	/* Sticky and at least one catagory has write permission? */
	if((p & EDV_PERMISSION_STICKY) &&
	   (p & (EDV_PERMISSION_UWRITE | EDV_PERMISSION_GWRITE |
		 EDV_PERMISSION_AWRITE))
	)
	    return(TRUE);

	/* Anyone can write? */
	if(p & EDV_PERMISSION_AWRITE)
	    return(TRUE);

	/* Group can write and is member of group? */
	if((p & EDV_PERMISSION_GWRITE) &&
	   (eguid <= obj->group_id)
	)
	    return(TRUE);

	/* Owner can write and is a owner? */
	if((p & EDV_PERMISSION_UWRITE) &&
	   ((euid == 0) || (euid == obj->owner_id))
	)
	    return(TRUE);

	return(FALSE);
}

/*
 *	Checks if the user or group ID of the process can execute the
 *	specified object.
 */
gboolean EDVIsObjectExecutable(
	edv_core_struct *core_ptr, edv_object_struct *obj
)
{
	gint euid, eguid;
	edv_permission_flags p;

	if((core_ptr == NULL) || (obj == NULL))
	    return(FALSE);

	euid = core_ptr->effective_user_id;
	eguid = core_ptr->effective_group_id;
	p = obj->permissions;

	/* Sticky and at least one catagory has execute permission? */
	if((p & EDV_PERMISSION_STICKY) &&
	   (p & (EDV_PERMISSION_UEXECUTE | EDV_PERMISSION_GEXECUTE |
		 EDV_PERMISSION_AEXECUTE))
	)
	    return(TRUE);

	/* Anyone can execute? */
	if(p & EDV_PERMISSION_AEXECUTE)
	    return(TRUE);

	/* Group can execute and is member of group? */
	if((p & EDV_PERMISSION_GEXECUTE) &&
	   (eguid <= obj->group_id)
	)
	    return(TRUE);

	/* Owner can execute and is a owner? */
	if((p & EDV_PERMISSION_UEXECUTE) &&
	   ((euid == 0) || (euid == obj->owner_id))
	)
	    return(TRUE);

	return(FALSE);
}


/*
 *	Returns TRUE if the object specified by path is supported by
 *	Imlib.
 */
gboolean EDVCheckImlibImage(
	edv_core_struct *core_ptr, const gchar *name
)
{
	gint i;
	const gchar *ext_list[] = IMLIB_IMAGE_FILE_EXTENSIONS;

	if((core_ptr == NULL) || STRISEMPTY(name))
	    return(FALSE);

	/* Check if this is an image file by checking the extension
	 * portion of the name
	 */
	for(i = 0; ext_list[i] != NULL; i += 2)
	{
	    if(EDVIsExtension(name, ext_list[i]))
		return(TRUE);
	}

	return(FALSE);
}

/*
 *      Returns TRUE if the object specified by path is an archive
 *      supported by Endeavour's archiver.
 */
gboolean EDVCheckEDVArchiverArchive(
	edv_core_struct *core_ptr, const gchar *name
)
{
	gint i;
	const gchar *ext_list[] = EDV_ARCHIVER_ARCHIVE_FILE_EXTENSIONS;

	if((core_ptr == NULL) || STRISEMPTY(name))
	    return(FALSE);

	/* Check if this is an image file by checking the extension
	 * portion of the name
	 */
	for(i = 0; ext_list[i] != NULL; i += 2)
	{
	    if(EDVIsExtension(name, ext_list[i]))
		return(TRUE);
	}

	return(FALSE);
}


/*
 *	Fetches the pixmap and mask pairs that best matches the given
 *	information. Devices and MIME Types will be realized as needed
 *	during this call.
 *
 *	Any of the given information may be NULL or 0.
 *
 *	The returned pixmap and mask pairs do not have any refcounts
 *	added to them.
 *
 *	Returns 0 on success or -1 on failed match.
 */
gint EDVMatchObjectIcon(
	edv_device_struct **device, gint total_devices,
	edv_mimetype_struct **mimetype, gint total_mimetypes,
	edv_object_type type,	/* One of EDV_OBJECT_TYPE_* */
	const gchar *path,	/* Full path or name */
	gboolean link_valid,
	edv_permission_flags permissions,	/* Any of EDV_PERMISSION_ */
	gint icon_size,		/* 0 = small, 1 = medium, 2 = large */
	GdkPixmap **pixmap_closed, GdkBitmap **mask_closed,
	GdkPixmap **pixmap_opened, GdkBitmap **mask_opened,
	GdkPixmap **pixmap_extended, GdkBitmap **mask_extended,
	GdkPixmap **pixmap_hidden, GdkBitmap **mask_hidden
)
{
	gboolean got_match = FALSE;
	const gboolean is_file = (type == EDV_OBJECT_TYPE_FILE) ? TRUE : FALSE;
	gint i;
	GdkPixmap *pixmap[EDV_MIMETYPE_TOTAL_ICON_STATES];
	GdkBitmap *mask[EDV_MIMETYPE_TOTAL_ICON_STATES];

	/* Reset local icon lists */
	memset(pixmap, 0x00, EDV_MIMETYPE_TOTAL_ICON_STATES * sizeof(GdkPixmap *));
	memset(mask, 0x00, EDV_MIMETYPE_TOTAL_ICON_STATES * sizeof(GdkBitmap *));

	/* Reset returns */
	if(pixmap_closed != NULL)
	    *pixmap_closed = NULL;
	if(mask_closed != NULL)
	    *mask_closed = NULL;

	if(pixmap_opened != NULL)
	    *pixmap_opened = NULL;
	if(mask_opened != NULL)
	    *mask_opened = NULL;

	if(pixmap_extended != NULL)
	    *pixmap_extended = NULL;
	if(mask_extended != NULL)
	    *mask_extended = NULL;

	if(pixmap_hidden != NULL)
	    *pixmap_hidden = NULL;
	if(mask_hidden != NULL)
	    *mask_hidden = NULL;

/* Sets the pixmap & mask pairs for the MIME Type and realizes it
 *
 * If one or more pixmaps were obtained than got_match will be set to
 * TRUE
 */
#define GET_MIMETYPE_ICONS(_m_)	{		\
 gint n;					\
 const gint nstates = EDV_MIMETYPE_TOTAL_ICON_STATES; \
						\
 /* Realize MIME Type first (as needed) to	\
  * ensire that the icons are loaded		\
  */						\
 EDVMimeTypeRealize((_m_), FALSE);		\
						\
 /* Get icons by the requested size */		\
 switch(icon_size) {				\
  case 0:	/* Small */			\
   for(n = 0; n < nstates; n++) {		\
    pixmap[n] = (_m_)->small_pixmap[n];		\
    mask[n] = (_m_)->small_mask[n];		\
   }						\
   break;					\
  case 1:	/* Medium */			\
   for(n = 0; n < nstates; n++) {		\
    pixmap[n] = (_m_)->medium_pixmap[n];	\
    mask[n] = (_m_)->medium_mask[n];		\
   }						\
   break;					\
  case 2:	/* Large */			\
   for(n = 0; n < nstates; n++) {		\
    pixmap[n] = (_m_)->large_pixmap[n];		\
    mask[n] = (_m_)->large_mask[n];		\
   }						\
   break;					\
 }						\
						\
 /* Check if we got a valid pixmap, if we did	\
  * then set got_match to TRUE */		\
 for(n = 0; n < nstates; n++) {			\
  if(pixmap[n] != NULL) {			\
   got_match = TRUE;				\
   break;					\
  }						\
 }						\
}

	/* Begin checking if the object matches a device or MIME Type */

	/* First check if the object is a link */
	if(type == EDV_OBJECT_TYPE_LINK)
	{
	    const gchar *mime_type_str = EDV_MIMETYPE_STR_LINK;
	    edv_mimetype_struct *m;

	    /* Object is a link, now iterate through MIME Types list and
	     * find the MIME Type of class EDV_MIMETYPE_CLASS_SYSTEM
	     * and use its icons
	     */
	    for(i = 0; i < total_mimetypes; i++)
	    {
		m = mimetype[i];
		if(m == NULL)
		    continue;

		/* Only handle if MIME Type class is a systems object */
		if((m->mt_class == EDV_MIMETYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, mime_type_str))
		    {
			GET_MIMETYPE_ICONS(m);
			got_match = TRUE;
			break;
		    }
		}
	    }
	}

	/* At this point got_match might be set to TRUE at any time to
	 * indicate that a match was made
	 */

	/* Check devices if device list is given and path is an
	 * absolute path
	 */
	if((device != NULL) &&
	   ((path != NULL) ? g_path_is_absolute(path) : FALSE) &&
	   !got_match
	)
	{
	    edv_device_struct *d;

	    /* Iterate through devices */
	    for(i = 0; i < total_devices; i++)
	    {
		d = device[i];
		if(d == NULL)
		    continue;

		if(STRISEMPTY(d->mount_path))
		    continue;

		/* Specified path matches device's mount path? */
		if(!strcmp(d->mount_path, path))
		{
		    gint n;
		    const gint nstates = MIN(
			EDV_DEVICE_TOTAL_ICON_STATES,
			EDV_MIMETYPE_TOTAL_ICON_STATES
		    );

		    /* Realize this device first to ensure that the
		     * icons are loaded
		     */
		    EDVDeviceRealize(d, FALSE);

		    /* Get icons from device */
		    switch(icon_size)
		    {
		      case 0:	/* Small */
			for(n = 0; n < nstates; n++)
			{
			    pixmap[n] = d->small_pixmap[n];
			    mask[n] = d->small_mask[n];
			}
			break;
		      case 1:	/* Medium */
			for(n = 0; n < nstates; n++)
			{
			    pixmap[n] = d->medium_pixmap[n];
			    mask[n] = d->medium_mask[n];
			}
			break;
		      case 2:	/* Large */
			for(n = 0; n < nstates; n++)
			{
			    pixmap[n] = d->large_pixmap[n];
			    mask[n] = d->large_mask[n];
			}
			break;
		    }
		    /* Got match? */
		    for(n = 0; n < nstates; n++)
		    {
			if(pixmap[n] != NULL)
			{
			    got_match = TRUE;
			    break;
			}
		    }
		    break;
		}
	    }	/* Iterate through devices */
	}

	/* Check MIME Types */
	if((mimetype != NULL) && !got_match)
	{
	    const gchar	*value,
			*name = EDVGetPathName(path);
	    edv_mimetype_struct *m;

	    /* Iterate through MIME Types list */
	    for(i = 0; i < total_mimetypes; i++)
	    {
		m = mimetype[i];
		if(m == NULL)
		    continue;

		value = m->value;
		if(STRISEMPTY(value))
		    continue;

		/* Handle by MIME Type class */
		switch(m->mt_class)
		{
		  case EDV_MIMETYPE_CLASS_SYSTEM:
		    break;
		  case EDV_MIMETYPE_CLASS_UNIQUE:
		  case EDV_MIMETYPE_CLASS_PROGRAM:
		    if((path != NULL) ? g_path_is_absolute(path) : FALSE)
		    {
			if(!strcmp(value, path))
			    GET_MIMETYPE_ICONS(m);
		    }
		    break;
		  case EDV_MIMETYPE_CLASS_FORMAT:
		    if((name != NULL) && !got_match && is_file)
		    {
			if(EDVIsExtension(name, value))
			    GET_MIMETYPE_ICONS(m);
		    }
		    break;
		}

		/* Do not stop iterating through MIME Types if got_match
		 * is TRUE, because there might be another MIME type that
		 * has a more specific icon further in the list
		 */

	    }	/* Iterate through MIME Types list */
	}



	/* If still did not get match, then use the basic system MIME
	 * types for the specified object
	 */
	if(!got_match)
	{
	    const gchar *mime_type_str = "";
	    edv_mimetype_struct *m;

	    /* Get MIME Type type string used for matching of system
	     * object type
	     */
	    switch(type)
	    {
	      case EDV_OBJECT_TYPE_UNKNOWN:
		break;

	      case EDV_OBJECT_TYPE_FILE:
		/* Check the file's permissions allow execution, in
		 * which case we use the file/executable MIME Type
		 * instead of file/regular
		 */
		if(permissions & (EDV_PERMISSION_UEXECUTE |
		    EDV_PERMISSION_GEXECUTE | EDV_PERMISSION_AEXECUTE)
		)
		    mime_type_str = EDV_MIMETYPE_STR_FILE_EXECUTABLE;
		else
		    mime_type_str = EDV_MIMETYPE_STR_FILE;
		break;

	      case EDV_OBJECT_TYPE_DIRECTORY:
		mime_type_str = EDV_MIMETYPE_STR_DIRECTORY;
		break;

	      case EDV_OBJECT_TYPE_LINK:
		mime_type_str = EDV_MIMETYPE_STR_LINK;
		break;

	      case EDV_OBJECT_TYPE_DEVICE_BLOCK:
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_BLOCK;
		break;

	      case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_CHARACTER;
		break;

	      case EDV_OBJECT_TYPE_FIFO:
		mime_type_str = EDV_MIMETYPE_STR_FIFO;
		break;

	      case EDV_OBJECT_TYPE_SOCKET:
		mime_type_str = EDV_MIMETYPE_STR_SOCKET;
		break;
	    }

	    /* Iterate through MIME Types of class system */
	    for(i = 0; i < total_mimetypes; i++)
	    {
		m = mimetype[i];
		if(m == NULL)
		    continue;

		/* Only handle if MIME Type class is a systems object */
		if((m->mt_class == EDV_MIMETYPE_CLASS_SYSTEM) &&
		   !STRISEMPTY(m->type)
		)
		{
		    if(!strcmp(m->type, mime_type_str))
		    {
			GET_MIMETYPE_ICONS(m);
			got_match = TRUE;
			break;
		    }
		}
	    }

	    /* If we still didn't get match, then all else return the
	     * file icons
	     */
/* TODO */
	}
#undef GET_MIMETYPE_ICONS


	/* Update returns */
	if(pixmap_closed != NULL)
	    *pixmap_closed = pixmap[EDV_MIMETYPE_ICON_STATE_STANDARD];
	if(mask_closed != NULL)
	    *mask_closed = mask[EDV_MIMETYPE_ICON_STATE_STANDARD];

	if(pixmap_opened != NULL)
	    *pixmap_opened = pixmap[EDV_MIMETYPE_ICON_STATE_SELECTED];
	if(mask_opened != NULL)
	    *mask_opened = mask[EDV_MIMETYPE_ICON_STATE_SELECTED];

	if(pixmap_extended != NULL)
	    *pixmap_extended = pixmap[EDV_MIMETYPE_ICON_STATE_EXTENDED];
	if(mask_extended != NULL)
	    *mask_extended = mask[EDV_MIMETYPE_ICON_STATE_EXTENDED];

	if(pixmap_hidden != NULL)
	    *pixmap_hidden = pixmap[EDV_MIMETYPE_ICON_STATE_HIDDEN];
	if(mask_hidden != NULL)
	    *mask_hidden = mask[EDV_MIMETYPE_ICON_STATE_HIDDEN];

	return(0);
}

/*
 *      Fetches the MIME Type type string that best matches the given
 *      information.
 *
 *      Any of the given information may be NULL or 0.
 *
 *      The returned MIME Type string must not be modified or
 *	deallocated.
 *
 *      Returns 0 on success or -1 on failed match.
 */
gint EDVMatchObjectTypeString(
	edv_mimetype_struct **mimetype, gint total_mimetypes,
	edv_object_type type,	/* One of EDV_OBJECT_TYPE_* */
	edv_permission_flags permissions,
	const gchar *path,	/* Full path or just name */
	gchar **type_str
)
{
	gboolean got_match = FALSE;
	gint i;
	gchar *ltype_str = NULL;
	const gchar *name, *ext = NULL;
	edv_mimetype_struct *m;

	if(type_str != NULL)
	    *type_str = NULL;

	name = EDVGetPathName(path);

	/* Parse extension from the specified name */
	if(name != NULL)
	{
	    const gchar *s = name;

	    /* Seek past first '.' character in the case of hidden
	     * files
	     */
 	    if(*s == '.')
		s++;

	    /* Seek to extension (if any), include the '.' as the first
	     * character if an extension is found
	     */
	    ext = strchr(s, '.');
	    if(ext != NULL)
	    {
		/* Extension must have atleast one character after it */
		if(*(ext + 1) == '\0')
		    ext = NULL;
	    }
	}


	/* Check MIME Types first */
	if(mimetype != NULL)
	{
	    const gchar *value;

	    for(i = 0; i < total_mimetypes; i++)
	    {
		m = mimetype[i];
		if(m == NULL)
		    continue;

		value = m->value;
		if(STRISEMPTY(value))
		    continue;

		/* Handle by MIME Type class */
		switch(m->mt_class)
		{
		  case EDV_MIMETYPE_CLASS_SYSTEM:
		    break;
		  case EDV_MIMETYPE_CLASS_UNIQUE:
		  case EDV_MIMETYPE_CLASS_PROGRAM:
		    if((path != NULL) ? g_path_is_absolute(path) : FALSE)
		    {
			if(!strcmp(value, path))
			{
			    ltype_str = m->type;
			    if(ltype_str != NULL)
				got_match = TRUE;
			}
		    }
		    break;
		  case EDV_MIMETYPE_CLASS_FORMAT:
		    if((name != NULL) && !got_match)
		    {
			if(EDVIsExtension(name, value))
			{
			    ltype_str = m->type;
			    if(ltype_str != NULL)
				got_match = TRUE;
			}
		    }
		    break;
		}
		/* Do not stop iterating through MIME Types if got_match
		 * is TRUE, because there might be another MIME type that
		 * has a more specific icon further in the list
		 */
	    }
	}

	/* If still did not get match then use basic system MIME types
	 * for the given object
	 */
	if(!got_match)
	{
	    const gchar *mime_type_str = "";

	    /* Get MIME Type type string used for matching of system
	     * object type
	     */
	    switch(type)
	    {
	      case EDV_OBJECT_TYPE_UNKNOWN:
		break;

	      case EDV_OBJECT_TYPE_FILE:
		/* Check the file's permissions allow execution, in which
		 * case we use the file/executable MIME Type instead of
		 * file/regular
		 */
		if(permissions & (EDV_PERMISSION_UEXECUTE |
		    EDV_PERMISSION_GEXECUTE | EDV_PERMISSION_AEXECUTE)
		)
		    mime_type_str = EDV_MIMETYPE_STR_FILE_EXECUTABLE;
		else
		    mime_type_str = EDV_MIMETYPE_STR_FILE;
		break;

	      case EDV_OBJECT_TYPE_DIRECTORY:
		mime_type_str = EDV_MIMETYPE_STR_DIRECTORY;
		break;

	      case EDV_OBJECT_TYPE_LINK:
		mime_type_str = EDV_MIMETYPE_STR_LINK;
		break;

	      case EDV_OBJECT_TYPE_DEVICE_BLOCK:
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_BLOCK;
		break;

	      case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
		mime_type_str = EDV_MIMETYPE_STR_DEVICE_CHARACTER;
		break;

	      case EDV_OBJECT_TYPE_FIFO:
		mime_type_str = EDV_MIMETYPE_STR_FIFO;
		break;

	      case EDV_OBJECT_TYPE_SOCKET:
		mime_type_str = EDV_MIMETYPE_STR_SOCKET;
		break;
	    }

	    ltype_str = (gchar *)mime_type_str;
	}

	/* Update returns */
	if(type_str != NULL)
	    *type_str = ltype_str;

	return(0);
}


/*
 *	Displays the specified message on the CDialog.
 */
void EDVMessage(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
)
{
	EDVMessageInfo(title, message, details, toplevel);
}
void EDVMessageInfo(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel
)
{
	CDialogSetTransientFor(toplevel);
	CDialogGetResponse(
	    title, message, details,
	    CDIALOG_ICON_INFO,
	    CDIALOG_BTNFLAG_OK |
	    ((details != NULL) ? CDIALOG_BTNFLAG_HELP : 0),
	    CDIALOG_BTNFLAG_OK
	);
	CDialogSetTransientFor(NULL);
}
void EDVMessageWarning(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel 
)
{
	CDialogSetTransientFor(toplevel);
	CDialogGetResponse(
	    title, message, details,
	    CDIALOG_ICON_WARNING,
	    CDIALOG_BTNFLAG_OK |
	    ((details != NULL) ? CDIALOG_BTNFLAG_HELP : 0),
	    CDIALOG_BTNFLAG_OK
	);
	CDialogSetTransientFor(NULL);                           
}
void EDVMessageError(
	const gchar *title, const gchar *message, const gchar *details,
	GtkWidget *toplevel                                            
)
{
	CDialogSetTransientFor(toplevel);
	CDialogGetResponse(
	    title, message, details,
	    CDIALOG_ICON_ERROR,
	    CDIALOG_BTNFLAG_OK |
	    ((details != NULL) ? CDIALOG_BTNFLAG_HELP : 0),
	    CDIALOG_BTNFLAG_OK  
	);
	CDialogSetTransientFor(NULL);                           
}

/*
 *	Displays the contents of the specified file on the CDialog.
 *
 *	If the file does not exist or cannot be opened then nothing will
 *	be displayed.
 */
void EDVMessageFromFile(
	const gchar *path, const gchar *title,
	gint cdialog_icon,
	GtkWidget *toplevel
)
{
	const gint max_filesize = 80 * 100;
	gchar *buf;
	gint buf_len, bytes_read;
	struct stat stat_buf;
	FILE *fp = !STRISEMPTY(path) ? FOpen(path, "rb") : NULL;
	if(fp == NULL)
	    return;

	if(fstat(fileno(fp), &stat_buf))
	{
	    FClose(fp);
	    return;
	}

	/* Get size of file and limit it to max_filesize */
	buf_len = MIN((gint)stat_buf.st_size, max_filesize);
	if(buf_len < 0)
	    buf_len = 0;
	buf = (gchar *)g_malloc((buf_len + 1) * sizeof(gchar));
	if(buf == NULL)
	{
	    FClose(fp);
	    return;
	}

	/* Read file */
	bytes_read = (gint)fread(buf, sizeof(gchar), (size_t)buf_len, fp);

	/* Close file */
	FClose(fp);

	/* Error reading file? */
	if(bytes_read < 0)
	{
	    g_free(buf);
	    return;
	}

	/* Null terminate */
	if(bytes_read <= buf_len)
	    buf[bytes_read] = '\0';
	else
	    buf[buf_len] = '\0';

	/* File contents empty? */
	if(*buf == '\0')
	{
	    g_free(buf);
	    return;
	}

	/* Display file contents on the CDialog */
	CDialogSetTransientFor(toplevel);
	CDialogGetResponse(
	    title, buf, NULL,
	    cdialog_icon,
	    CDIALOG_BTNFLAG_OK,
	    CDIALOG_BTNFLAG_OK
	);
	CDialogSetTransientFor(NULL);

	g_free(buf);
}


/*
 *	Plays the "Beep" sound.
 */
void EDVBeep(edv_core_struct *core_ptr)
{
	EDVPlaySoundBeep(core_ptr);
}
void EDVPlaySoundBeep(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{
	    gdk_beep();
	}
	else
	{
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_BEEP
	    );
	    if(!STRISEMPTY(s))
	    {
	        gchar *cmd = g_strdup_printf("%s &", s);
	        EDVSystem(cmd);
	        g_free(cmd);
	    }
	}
}

/*
 *	Plays the "Info" sound.
 */
void EDVPlaySoundInfo(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{

	}
	else
	{   
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_INFO
	    );
	    if(!STRISEMPTY(s))
	    {
		gchar *cmd = g_strdup_printf("%s &", s);
		EDVSystem(cmd);
		g_free(cmd);
	    }
	}
}

/*
 *	Plays the "Question" sound.
 */
void EDVPlaySoundQuestion(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{

	}
	else
	{   
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_QUESTION
	    );
	    if(!STRISEMPTY(s))
	    {
		gchar *cmd = g_strdup_printf("%s &", s);
		EDVSystem(cmd);
		g_free(cmd);
	    }
	}
}

/*
 *	Plays the "Warning" sound.
 */
void EDVPlaySoundWarning(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{

	}
	else
	{   
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_WARNING
	    );
	    if(!STRISEMPTY(s))
	    {
		gchar *cmd = g_strdup_printf("%s &", s);
		EDVSystem(cmd);
		g_free(cmd);
	    }
	}
}

/*
 *	Plays the "Error" sound.
 */
void EDVPlaySoundError(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{

	}
	else
	{   
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_ERROR
	    );
	    if(!STRISEMPTY(s))
	    {
		gchar *cmd = g_strdup_printf("%s &", s);
		EDVSystem(cmd);
		g_free(cmd);
	    }
	}
}

/*
 *	Plays the "Completed" sound.
 */
void EDVPlaySoundCompleted(edv_core_struct *core_ptr)
{
	const cfg_item_struct *cfg_list = (core_ptr != NULL) ?
	    core_ptr->cfg_list : NULL;
	if(CFGItemListGetValueI(cfg_list, EDV_CFG_PARM_SOUND_USE_SYSTEM))
	{

	}
	else
	{   
	    const gchar *s = CFGItemListGetValueS(
		cfg_list, EDV_CFG_PARM_SOUND_PLAY_COMPLETED
	    );
	    if(!STRISEMPTY(s))
	    {
		gchar *cmd = g_strdup_printf("%s &", s);
		EDVSystem(cmd);
		g_free(cmd);
	    }
	}
}


/*
 *	Moves the GtkWindow w2 to be centered to GtkWindow w1.
 */
void EDVCenterWindowToWindow(GtkWidget *w1, GtkWidget *w2)
{
	EDVCenterWindowToWindowOffset(w1, w2, 0, 0);
}

/*
 *	Moves the GtkWindow w2 to be centered to GtkWindow w1 plus
 *	the given offsets.
 */
void EDVCenterWindowToWindowOffset(
	GtkWidget *w1, GtkWidget *w2, gint x_offset, gint y_offset
)
{
	gint x, y;
	gint width1, height1, width2, height2;
	GdkWindow *window1, *window2;


	if((w1 == NULL) || (w2 == NULL))
	    return;

	if(!GTK_IS_WINDOW(w1) || !GTK_IS_WINDOW(w2))
	    return;

	window1 = w1->window;
	window2 = w2->window;
	if((window1 == NULL) || (window2 == NULL))
	    return;

	gdk_window_get_root_origin(window1, &x, &y);
	gdk_window_get_size(window1, &width1, &height1);
	gdk_window_get_size(window2, &width2, &height2);

	gtk_widget_set_uposition(
	    w2,
	    ((width2 > 0) ?
		(x + (width1 / 2) - (width2 / 2)) : (x + 20)
	    ) + x_offset,
	    ((height2 > 0) ?
		(y + (height1 / 2) - (height2 / 2)) : (y + 20)
	    ) + y_offset
	);
}

/*
 *	Sets up the GtkEntry w to allow it to receive drag and drop.
 */
void EDVEntrySetDND(edv_core_struct *core_ptr, GtkWidget *w)
{
	const GtkTargetEntry dnd_tar_types[] = {
{"text/plain",                          0,      EDV_DND_TYPE_INFO_TEXT_PLAIN},
{"text/uri-list",                       0,      EDV_DND_TYPE_INFO_TEXT_URI_LIST},
{"STRING",                              0,      EDV_DND_TYPE_INFO_STRING}
	};

	if((core_ptr == NULL) || (w == NULL))
	    return;

	if(!GTK_IS_ENTRY(w))
	    return;

	GUIDNDSetTar(
	    w,
	    dnd_tar_types,
	    sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
	    GDK_ACTION_COPY,			/* Actions */
	    GDK_ACTION_COPY,			/* Default action if same */
	    GDK_ACTION_COPY,			/* Default action */
	    EDVEntryDragDataReceivedCB,
	    core_ptr
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "drag_motion",
	    GTK_SIGNAL_FUNC(EDVEntryDragMotionCB), core_ptr
	);
}

/*
 *	Sets up the GtkEntry w to complete path when the TAB key is
 *	pressed.
 */
void EDVEntrySetCompletePath(edv_core_struct *core_ptr, GtkWidget *w)
{
	if((core_ptr == NULL) || (w == NULL))
	    return;

	if(!GTK_IS_ENTRY(w))
	    return;

	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EDVEntryCompletePathCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EDVEntryCompletePathCB), core_ptr
	);
}


/*
 *	Creates a new GtkScrolledWindow.
 *
 *	It will be set up for scrolling.
 */
GtkWidget *EDVScrolledWindowNew(
	GtkPolicyType hscrollbar_policy,
	GtkPolicyType vscrollbar_policy,
	GtkWidget **event_box_rtn,
	GtkWidget **vbox_rtn
)
{
	GdkCursor *translate_cur = gdk_cursor_new(GDK_FLEUR);
	GtkWidget *parent, *w = gtk_scrolled_window_new(NULL, NULL);
	GtkScrolledWindow *sw = GTK_SCROLLED_WINDOW(w);
	gtk_scrolled_window_set_policy(
	    sw, hscrollbar_policy, vscrollbar_policy
	);
	gtk_object_set_data_full(
	    GTK_OBJECT(w),
	    EDV_SCROLLED_WINDOW_DATA_TRANSLATE_CURSOR_KEY,
	    translate_cur,
	    (GtkDestroyNotify)gdk_cursor_destroy
	);
	parent = w;

	w = gtk_event_box_new();
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    GDK_EXPOSURE_MASK | GDK_VISIBILITY_NOTIFY_MASK |
	    GDK_STRUCTURE_MASK | GDK_FOCUS_CHANGE_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EDVScrolledWindowEventCB), sw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EDVScrolledWindowEventCB), sw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(EDVScrolledWindowEventCB), sw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(EDVScrolledWindowEventCB), sw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(EDVScrolledWindowEventCB), sw
	);
	gtk_scrolled_window_add_with_viewport(sw, w);
	gtk_widget_show(w);
	parent = w;

	w = gtk_vbox_new(FALSE, 0);
	if(vbox_rtn != NULL)
	    *vbox_rtn = w;
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);

	return(GTK_WIDGET(sw));
}

/*
 *	GtkScrolledWindow event callback.
 */
static gint EDVScrolledWindowEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	static gboolean do_scroll;
	static gint pointer_x_last, pointer_y_last;
	gboolean press;
	GdkEventFocus *focus;
	GdkEventKey *key;
	GdkEventButton *button;
	GdkEventMotion *motion;
	GtkScrolledWindow *sw = (GtkScrolledWindow *)data;
	if((widget == NULL) || (event == NULL) || (sw == NULL))
	    return(status);

#define ADJ_SET_EMIT(adj,v)	{		\
 gfloat v2 = (v);				\
 if(v2 > ((adj)->upper - (adj)->page_size))	\
  v2 = (adj)->upper - (adj)->page_size;		\
 if(v2 < (adj)->lower)				\
  v2 = (adj)->lower;				\
 gtk_adjustment_set_value((adj), v2);		\
}
#define ADJ_SET_DELTA_EMIT(adj,d)	{	\
 ADJ_SET_EMIT((adj), (adj)->value + (d));	\
}

	switch((gint)event->type)
	{
	  case GDK_FOCUS_CHANGE:
	    focus = (GdkEventFocus *)event;
	    if(focus->in && !GTK_WIDGET_HAS_FOCUS(widget))
		GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
	    else if(!focus->in && GTK_WIDGET_HAS_FOCUS(widget))
		GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
	    status = TRUE;
	    break;

	  case GDK_KEY_PRESS:
	  case GDK_KEY_RELEASE:
	    key = (GdkEventKey *)event;
	    press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define SIGNAL_EMIT_STOP        {               \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget),                           \
  press ?                                       \
   "key_press_event" : "key_release_event"      \
 );                                             \
}
	    switch(key->keyval)
	    {
	      case GDK_Up:
	      case GDK_KP_Up:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_DELTA_EMIT(adj, -adj->step_increment);
		}
		SIGNAL_EMIT_STOP
		status = TRUE;
		break;
	      case GDK_Down:
	      case GDK_KP_Down:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_DELTA_EMIT(adj, adj->step_increment);
		}
		SIGNAL_EMIT_STOP
		status = TRUE;
		break;
	      case '-':
	      case 'b':
	      case 'p':
	      case GDK_Page_Up:
	      case GDK_KP_Page_Up:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_DELTA_EMIT(adj, -adj->page_increment);
		}
		status = TRUE;
		break;
	      case 'n':
	      case GDK_space:
	      case GDK_Page_Down:
	      case GDK_KP_Page_Down:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_DELTA_EMIT(adj, adj->page_increment);
		}
		status = TRUE;
		break;
	      case GDK_Home:
	      case GDK_KP_Home:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_EMIT(adj, adj->lower);
		}
		status = TRUE;
		break;
	      case GDK_End:
	      case GDK_KP_End:
		if(press)
		{
		    GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment(sw);
		    ADJ_SET_EMIT(adj, adj->upper - adj->page_size);
		}
		status = TRUE;
		break;
	    }
#undef SIGNAL_EMIT_STOP
	    break;

	  case GDK_BUTTON_PRESS:
	    button = (GdkEventButton *)event;
	    if(!GTK_WIDGET_HAS_FOCUS(widget))
		gtk_widget_grab_focus(widget);
	    switch(button->button)
	    {
	      case GDK_BUTTON1:
	      case GDK_BUTTON2:
		if(TRUE)
		{
		    gint x, y;
		    GdkModifierType mask;
		    GdkCursor *cursor = (GdkCursor *)GTK_OBJECT_GET_DATA(
			sw,
			EDV_SCROLLED_WINDOW_DATA_TRANSLATE_CURSOR_KEY
		    );

		    gdk_window_get_pointer(
			button->window, &x, &y, &mask
		    );

		    gdk_pointer_grab(
			button->window,
			FALSE,
			GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK |
			    GDK_POINTER_MOTION_MASK |
			    GDK_POINTER_MOTION_HINT_MASK,
			NULL,
			cursor,
			button->time
		    );

		    do_scroll = TRUE;
		    pointer_x_last = x;
		    pointer_y_last = y;
		    status = TRUE;
		}
		break;
	    }
	    break;

	  case GDK_BUTTON_RELEASE:
	    button = (GdkEventButton *)event;
	    switch(button->button)
	    {
	      case GDK_BUTTON1:
	      case GDK_BUTTON2:
		if(TRUE)
		{
		    gdk_pointer_ungrab(button->time);

		    do_scroll = FALSE;
		    pointer_x_last = 0;
		    pointer_y_last = 0;

		    status = TRUE;
		}
		break;
	    }
	    break;

	  case GDK_MOTION_NOTIFY:
	    motion = (GdkEventMotion *)event;
	    if(do_scroll)
	    {
		gint dx, dy;
		GtkAdjustment *hadj = gtk_scrolled_window_get_hadjustment(sw),
			      *vadj = gtk_scrolled_window_get_vadjustment(sw);

		if(motion->is_hint)
		{
		    gint x, y;
		    GdkModifierType mask;
		    gdk_window_get_pointer(
			motion->window, &x, &y, &mask
		    );
		    dx = x - pointer_x_last;
		    dy = y - pointer_y_last;
		}
		else
		{
		    dx = (gint)(motion->x - pointer_x_last);
		    dy = (gint)(motion->y - pointer_y_last);
		}
#define DO_SCROLL(adj,d)	{			\
 if((d) != 0) {						\
  if(((adj)->upper - (adj)->page_size) > (adj)->lower) {\
   gtk_adjustment_set_value(				\
    adj,						\
    CLIP(						\
     (adj)->value - (d),				\
     (adj)->lower, (adj)->upper - (adj)->page_size	\
    )							\
   );							\
  }							\
 }							\
}
		DO_SCROLL(hadj, dx);
		DO_SCROLL(vadj, dy);

#undef DO_SCROLL
		status = TRUE;
	    }
	    break;

	}

#undef ADJ_SET_DELTA_EMIT
#undef ADJ_SET_EMIT

	return(status);
}


/*
 *	Coppies the paths of the given list of disk objects to the
 *	DDE as a space separated list of absolute paths.
 */
void EDVCopyDiskObjectsToDDEPath(
	edv_core_struct *core_ptr,
	edv_object_struct **list, gint total,
	GtkWidget *owner
)
{
	gint i;
	gchar *buf = NULL;
	edv_object_struct *obj;


	if((core_ptr == NULL) || (list == NULL) || (total <= 0))
	    return;


	for(i = 0; i < total; i++)
	{
	    obj = list[i];
	    if(obj == NULL)
		continue;

	    if(obj->full_path == NULL)
		continue;

	    buf = G_STRCAT(buf, obj->full_path);
	    if((i + 1) < total)
		buf = G_STRCAT(buf, " ");
	}

	if(buf != NULL)
	{
	    GUIDDESetString(
		owner,			/* Owner */
		GDK_SELECTION_PRIMARY,	/* Selection */
		GDK_CURRENT_TIME,	/* Time */
		buf			/* Data */
	    );
	    g_free(buf);
	}
}

/*
 *      Coppies the paths of the given list of disk objects to the
 *      DDE as a space separated list of urls (without the localhost
 *	address specified).
 */
void EDVCopyDiskObjectsToDDEURL(
	edv_core_struct *core_ptr,
	edv_object_struct **list, gint total,
	GtkWidget *owner
)
{
	gint i;
	gchar *buf = NULL;
	edv_object_struct *obj;


	if((core_ptr == NULL) || (list == NULL) || (total <= 0))
	    return;


	for(i = 0; i < total; i++)
	{
	    obj = list[i];
	    if(obj == NULL)
		continue;

	    if(obj->full_path == NULL)
		continue;

	    buf = G_STRCAT(buf, "file://");
	    buf = G_STRCAT(buf, obj->full_path);
	    if((i + 1) < total)
		buf = G_STRCAT(buf, " ");
	}

	if(buf != NULL)
	{
	    GUIDDESetString(
		owner,			/* Owner */
		GDK_SELECTION_PRIMARY,	/* Selection */
		GDK_CURRENT_TIME,	/* Time */
		buf			/* Data */
	    );
	    g_free(buf);
	}
}


/*
 *	Creates a new GtkRcStyle from the given cfg_style_struct.
 */
GtkRcStyle *EDVCreateRCStyleFromCfg(const cfg_style_struct *style)
{
	gint i;
	guint src_color_flags;
	GdkColor *tar_c;
	const cfg_color_struct *src_c;
	GtkRcStyle *tar_style;
	const cfg_style_struct *src_style = style;

#define STRDUP2(s)	((STRISEMPTY(s)) ? NULL : g_strdup(s))

	if(src_style == NULL)
	    return(NULL);

	tar_style = gtk_rc_style_new();

	g_free(tar_style->font_name);
	tar_style->font_name = STRDUP2(src_style->font_name);

	for(i = 0; i < CFG_STYLE_STATES; i++)
	{
	    src_color_flags = src_style->color_flags[i];
	    if(src_color_flags & CFG_STYLE_FG)
	    {
		tar_style->color_flags[i] |= GTK_RC_FG;
		src_c = &src_style->fg[i];
		tar_c = &tar_style->fg[i];
		GDK_COLOR_SET_COEFF(
		    tar_c, src_c->r, src_c->g, src_c->b
		)
	    }
	    if(src_color_flags & CFG_STYLE_BG)
	    {
		tar_style->color_flags[i] |= GTK_RC_BG;
		src_c = &src_style->bg[i];
		tar_c = &tar_style->bg[i];
		GDK_COLOR_SET_COEFF(
		    tar_c, src_c->r, src_c->g, src_c->b
		) 
	    }
	    if(src_color_flags & CFG_STYLE_TEXT)
	    {
		tar_style->color_flags[i] |= GTK_RC_TEXT;
		src_c = &src_style->text[i];
		tar_c = &tar_style->text[i];
		GDK_COLOR_SET_COEFF(
		    tar_c, src_c->r, src_c->g, src_c->b
		) 
	    }
	    if(src_color_flags & CFG_STYLE_BASE)
	    {
		tar_style->color_flags[i] |= GTK_RC_BASE;
		src_c = &src_style->base[i];
		tar_c = &tar_style->base[i];
		GDK_COLOR_SET_COEFF(
		    tar_c, src_c->r, src_c->g, src_c->b
		) 
	    }

	    g_free(tar_style->bg_pixmap_name[i]);
	    tar_style->bg_pixmap_name[i] = STRDUP2(
		src_style->bg_pixmap_name[i]
	    );
	}

	return(tar_style);
#undef STRDUP2
}

/*
 *	Appends a new operation history event item to the list on the
 *	given core structure.
 */
void EDVAppendHistory(
	edv_core_struct *core_ptr,
	edv_history_type type,	/* One of EDV_HISTORY_* */
	gulong time_start,	/* Time the operation first started */
	gulong time_end,	/* Time the operation ended */
	gint status,		/* Result of operation */
	const gchar *src_path,	/* Source Object/Operation/Value */
	const gchar *tar_path,	/* Target Object/Operation/Value */
	const gchar *comments	/* Comments */
)
{
	edv_history_struct *h;


	if(core_ptr == NULL)
	    return;

	/* Create new history event */
	h = EDVHistoryNew();
	if(h == NULL)
	    return;

	/* Set values */
	h->type = type;
	h->time_start = time_start;
	h->time_end = time_end;
	h->status = status;
	h->src_path = STRDUP(src_path);
	h->tar_path = STRDUP(tar_path);
	h->comments = STRDUP(comments);

	/* Append to history file */
	EDVHistoryAppendToFile(
	    CFGItemListGetValueS(
		core_ptr->cfg_list, EDV_CFG_PARM_FILE_HISTORY
	    ),
	    h
	);

	/* Emit history event added signal */
	EDVHistoryAddedEmit(core_ptr, -1, h);

	/* Delete history event */
	EDVHistoryDelete(h);
}
