#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gtk/gtk.h>
#include <unistd.h>

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

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

#include "cfg.h"
#include "cfg_fio.h"
#include "url.h"
#include "edv_types.h"
#include "edv_history.h"
#include "edv_obj.h"
#include "edv_device.h"
#include "edv_devices_list.h"
#include "edv_mime_type.h"
#include "edv_mime_types_list.h"
#include "edv_recycled_obj.h"
#include "edv_recbin_index.h"
#include "browser.h"
#include "browser_dir_tree.h"
#include "browser_op_cb.h"
#include "imbr.h"
#include "imbr_op_cb.h"
#include "archiver.h"
#include "archiver_op_cb.h"
#include "recbin.h"
#include "recbin_contents_list.h"
#include "recbin_op_cb.h"
#include "prop_dlg.h"
#include "find_win.h"
#include "history_win.h"
#include "edv_generic_options_win.h"
#include "edv_generic_options_win_op.h"
#include "options_win.h"
#include "customize_win.h"
#include "run_dlg.h"
#include "devices_list_win.h"
#include "mime_types_list_win.h"
#include "about_dlg.h"
#include "endeavour2.h"
#include "edv_cb.h"
#include "edv_help.h"
#include "edv_op.h"
#include "edv_cfg_list.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"
#include "config.h"

#include "images/icon_trash_32x32.xpm"


/* New/Map Window Nexus */
gint EDVNewWindow(
	edv_core_struct *core,
	const gchar *name,		/* Name of window */
	const gchar *path,		/* Initial object path */
	const gchar *extra,
	GtkWidget *toplevel
);


/* New Windows */
gint EDVNewBrowser(
	edv_core_struct *core,
	const gchar *path
);
gint EDVNewImbr(
	edv_core_struct *core,
	const gchar *path
);
gint EDVNewArchiver(
	edv_core_struct *core,
	const gchar *path,
	const gchar *password
);

gint EDVNewPropertiesDialogPage(
	edv_core_struct *core, edv_object_struct *obj,
	const gchar *page_name,
	GtkWidget *toplevel
);
gint EDVNewPropertiesDialog(
	edv_core_struct *core, edv_object_struct *obj,
	GtkWidget *toplevel
);


/* Running */
void EDVRunDeviceCheck(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
);
void EDVRunDeviceTools(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
);
void EDVRunDeviceFormat(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
);

void EDVRunTerminal(
	edv_core_struct *core,
	const gchar *cmd,
	const gchar *wd,
	GtkWidget *toplevel
);


/* Window Mapping */
void EDVMapRecBinDeskIcon(edv_core_struct *core);
void EDVMapRecBin(edv_core_struct *core);

void EDVMapHistoryListWin(
	edv_core_struct *core, GtkWidget *toplevel
);

void EDVMapOptionsWinPage(
	edv_core_struct *core,
	const gchar *page_name,
	GtkWidget *toplevel
);
void EDVMapOptionsWin(
	edv_core_struct *core, GtkWidget *toplevel
);

void EDVMapCustomizeWin(
	edv_core_struct *core, GtkWidget *toplevel
);

void EDVMapDevicesListWin(
	edv_core_struct *core, GtkWidget *toplevel
);

void EDVMapMIMETypesListWin(
	edv_core_struct *core,
	const gchar *type,
	GtkWidget *toplevel
);

void EDVMapRunDialogCommand(
	edv_core_struct *core,
	const gchar *command,
	const gchar *working_dir,
	GtkWidget *toplevel
);
void EDVMapRunDialog(
	edv_core_struct *core, GtkWidget *toplevel
);

void EDVMapFindWin(edv_core_struct *core, const gchar *location);
void EDVMapBrowserFindWin(
	edv_core_struct *core, edv_browser_struct *browser
);
void EDVMapImbrFindWin(
	edv_core_struct *core, edv_imbr_struct *imbr
);
void EDVMapArchiverFindWin(
	edv_core_struct *core, edv_archiver_struct *archiver
);
void EDVMapRecBinFindWin(
	edv_core_struct *core, edv_recbin_struct *recbin
);


/* About */
void EDVAboutPage(
	edv_core_struct *core,
	const gchar *page_name,
	GtkWidget *toplevel
);
void EDVAbout(
	edv_core_struct *core, GtkWidget *toplevel
);


/* Update Resources & Widgets */
void EDVUpdateRCStyles(edv_core_struct *core);
void EDVUpdateIDPULists(edv_core_struct *core);
void EDVUpdateDevicesPUList(edv_core_struct *core);
void EDVUpdateMIMETypeHintIndices(edv_core_struct *core);
void EDVUpdateOpenWithPUList(edv_core_struct *core);


/* Refresh & Reset */
void EDVRefresh(edv_core_struct *core);
void EDVReset(edv_core_struct *core);


/* Sync */
void EDVSyncDisks(edv_core_struct *core);


/* Clearing */
void EDVClearHistoryEvents(
	edv_core_struct *core, GtkWidget *toplevel
);
void EDVClearLocationsHistory(
	edv_core_struct *core, GtkWidget *toplevel
);
void EDVClearRunHistory(
	edv_core_struct *core, GtkWidget *toplevel
);


/* Network */
void EDVInternetDownloadObject(
	edv_core_struct *core,
	const url_struct *url,			/* Source */
	const gchar *target_path,		/* Target */
	GtkWidget *toplevel
);


/* Recycle Bin Size Check */
void EDVRecycleBinSizeCheck(
	edv_core_struct *core, GtkWidget *toplevel
);


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


/*
 *	Creates a new window.
 *
 *	The name specifies the window to be created, it can be any of
 *	the following:
 *
 *		"file_browser"
 *		"image_browser"
 *		"archiver"
 *		"recycle_bin"
 *		"properties_dialog"
 *		"options_window"
 *		"customize_window"
 *		"devices_window"
 *		"mimetypes_window"
 *		"run_dialog"
 *		"about_dialog"
 *		"help"
 *
 *	The path specifies (for most of the windows) the initial
 *	location (which may be NULL for no initial location).
 *
 *	The extract specifies (for some of the windows) the extra
 *	argument, it depends on the window in question (see code).
 */
gint EDVNewWindow(
	edv_core_struct *core,
	const gchar *name,		/* Name of window */
	const gchar *path,		/* Initial object path */
	const gchar *extra,		/* Topic, page, tab, etc */
	GtkWidget *toplevel
)
{
	gchar *s, *dname;

	if((core == NULL) || STRISEMPTY(name))
	    return(-1);

	/* Make a copy of the window's name */
	dname = STRDUP(name);

	/* Replace all '-' and blank characters in dname with '_' for
	 * easier matching
	 */
	for(s = dname; *s != '\0'; s++)
	{
	    if((*s == '-') || ISBLANK(*s))
		*s = '_';
	}


	/* Begin checking which window we should create by checking
	 * dname
	 *
	 * Note that dname uses '_' characters to replace '-' or
	 * blank characters
	 */

	/* File Browser */
	if(!g_strcasecmp(dname, "file_browser") ||
	   !g_strcasecmp(dname, "filebrowser") ||
	   !g_strcasecmp(dname, "browser")
	)
	{
	    EDVNewBrowser(core, path);
	}
	/* Image Browser */
	else if(!g_strcasecmp(dname, "image_browser") ||
	        !g_strcasecmp(dname, "imagebrowser") ||
	        !g_strcasecmp(dname, "imbr")
	)
	{
	    EDVNewImbr(core, path);
	}
	/* Archiver */
	else if(!g_strcasecmp(dname, "archiver"))
	{
	    EDVNewArchiver(core, path, extra);
	}
	/* Recycle Bin */
	else if(!g_strcasecmp(dname, "recycle_bin") ||
	        !g_strcasecmp(dname, "recyclebin") ||
		!g_strcasecmp(dname, "rec_bin") ||
	        !g_strcasecmp(dname, "recbin")
	)
	{
	    EDVMapRecBin(core);
	}
	/* Properties Dialog */
	else if(!g_strcasecmp(dname, "properties_dialog") ||
		!g_strcasecmp(dname, "properties") ||
		!g_strcasecmp(dname, "prop_win") ||
		!g_strcasecmp(dname, "prop_dlg") ||
		!g_strcasecmp(dname, "propdlg") ||
	        !g_strcasecmp(dname, "propdlg")
	)
	{
	    struct stat lstat_buf;
	    edv_object_struct *obj = EDVObjectNew();
	    if(!STRISEMPTY(path))
	    {
		EDVObjectSetPath(obj, path);
		if(!lstat(path, &lstat_buf))
		    EDVObjectSetStat(obj, &lstat_buf);
	    }
	    EDVNewPropertiesDialogPage(core, obj, extra, NULL);
	    EDVObjectDelete(obj);
	}
	/* Options Window */
	else if(!g_strcasecmp(dname, "options_window") ||
		!g_strcasecmp(dname, "options")
	)
	{
	    EDVMapOptionsWinPage(core, extra, NULL);
	}
	/* Customize Window */
	else if(!g_strcasecmp(dname, "customize_window") ||
		!g_strcasecmp(dname, "customize")
	)
	{
	    EDVMapCustomizeWin(core, NULL);
	}
	/* Devices Window */
	else if(!g_strcasecmp(dname, "devices_window") ||
		!g_strcasecmp(dname, "devices")
	)
	{
	    EDVMapDevicesListWin(core, NULL);
	}
	/* MIME Types Window */
	else if(!g_strcasecmp(dname, "mimetypes_window") ||
		!g_strcasecmp(dname, "mimetypes")
	)
	{
	    EDVMapMIMETypesListWin(
		core,
		extra,
		NULL
	    );
	}
	/* Run Dialog */
	else if(!g_strcasecmp(dname, "run_dialog") ||
		!g_strcasecmp(dname, "run_dlg") ||
		!g_strcasecmp(dname, "rundlg") ||
		!g_strcasecmp(dname, "run")
	)
	{
	    EDVMapRunDialogCommand(
		core,
		((path == NULL) && (extra != NULL)) ?
		    extra : path,
		extra,
		NULL
	    );
	}
	/* Find Window */
	else if(!g_strcasecmp(dname, "find_window") ||
		!g_strcasecmp(dname, "find_win") ||
		!g_strcasecmp(dname, "find_dialog") ||
		!g_strcasecmp(dname, "find_dlg") ||
		!g_strcasecmp(dname, "find")
	)
	{
	    EDVMapFindWin(
		core,
		((path == NULL) && (extra != NULL)) ?
		    extra : path
	    );
	}
	/* About Dialog */
	else if(!g_strcasecmp(dname, "about_dialog") ||
		!g_strcasecmp(dname, "about_dlg") ||
		!g_strcasecmp(dname, "aboutdlg") ||
                !g_strcasecmp(dname, "about")
	)
	{
	    EDVAboutPage(core, extra, NULL);
	}
	/* Help */
	else if(!g_strcasecmp(dname, "help"))
	{
	    EDVHelp(core, extra, NULL);
	}


	g_free(dname);

	return(0);
}

/*
 *	Creates a new File Browser.
 *
 *	The new File Browser will be added to the core and mapped.
 *
 *	The path specifies the initial location.
 *
 *      Returns the index or -1 on error.
 */
gint EDVNewBrowser(
	edv_core_struct *core,
	const gchar *path
)
{
	gint i, existing_windows = 0;
	cfg_item_struct *cfg_list;
	edv_browser_struct *browser;

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

	cfg_list = core->cfg_list;

	/* Count existing windows */
	for(i = 0; i < core->total_browsers; i++)
	{
	    if(core->browser[i] != NULL)
		existing_windows++;
	}

	/* Look for available pointer in the array */
	for(i = 0; i < core->total_browsers; i++)
	{
	    if(core->browser[i] == NULL)
		break;
	}
	if(i >= core->total_browsers)
	{
	    /* Allocate more pointers */
	    i = MAX(core->total_browsers, 0);
	    core->total_browsers = i + 1;

	    core->browser = (edv_browser_struct **)g_realloc(
		core->browser,
		core->total_browsers * sizeof(edv_browser_struct *)
	    );
	    if(core->browser == NULL)
	    {
		core->total_browsers = 0;
		return(-1);
	    }
	    core->browser[i] = NULL;
	}

	/* Shift window position in options if there are existing
	 * windows
	 */
	if(existing_windows > 0)
	{
	    gint	offset_x = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_X
			),
			offset_y = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_Y
			),
			x = EDV_GET_I(
			    EDV_CFG_PARM_BROWSER_X
			) + offset_x,
			y = EDV_GET_I(
			    EDV_CFG_PARM_BROWSER_Y
			) + offset_y;
	    EDV_SET_I(EDV_CFG_PARM_BROWSER_X, x);
	    EDV_SET_I(EDV_CFG_PARM_BROWSER_Y, y);
	}

	/* Create new browser */
	core->browser[i] = browser = EDVBrowserNew(core);
	if(browser != NULL)
	{
	    gchar *startup_dir = EDVEvaluatePath(
		NULL,
		!STRISEMPTY(path) ?
		    path : EDV_GET_S(EDV_CFG_PARM_DIR_START_UP)
	    );
	    GtkWidget *toplevel = browser->toplevel;

	    /* If the startup directory is not a directory then replace
	     * it with its parent
	     */
	    if(!ISPATHDIR(startup_dir))
	    {
		gchar *s = g_dirname(startup_dir);
		g_free(startup_dir);
		startup_dir = s;
	    }

	    EDVBrowserSetBusy(browser, TRUE);
	    GUIBlockInput(toplevel, TRUE);

	    /* Need to map it immediatly after creation, before any
	     * timeouts check on it
	     */
	    EDVBrowserMap(browser);

	    /* Is the specified path within the Directory Tree? */
	    if(EDVBrowserIsPathFromDirTreeOrigin(browser, startup_dir))
	    {
		/* Create the toplevels */
		EDVBrowserDirTreeCreateToplevels(browser);

		/* Select the specified path */
		EDVBrowserDirTreeSelectPath(browser, startup_dir);
	    }
	    else
	    {
		/* Set the directory tree origin, create the toplevels,
		 * and select the specified path
		 */
		EDVBrowserDirTreeSetOriginPath(browser, startup_dir);
	    }

	    g_free(startup_dir);

	    GUIBlockInput(toplevel, FALSE);
	    EDVBrowserSetBusy(browser, FALSE);

	    return(i);
	}
	else
	{
	    return(-1);
	}
}

/*
 *	Creates a new Image Browser.
 *
 *	The new Image Browser will be added to the core and mapped.
 *
 *	The path specifies the initial location.
 *
 *	Returns the index or -1 on error.
 */
gint EDVNewImbr(
	edv_core_struct *core,
	const gchar *path
)
{
	gint i, existing_windows = 0;
	cfg_item_struct *cfg_list;
	edv_imbr_struct *imbr;

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

	cfg_list = core->cfg_list;

	/* Count existing windows */
	for(i = 0; i < core->total_imbrs; i++)
	{
	    if(core->imbr[i] != NULL)
		existing_windows++;
	}

	/* Look for available pointer in the array */
	for(i = 0; i < core->total_imbrs; i++)
	{
	    if(core->imbr[i] == NULL)
		break;
	}
	if(i >= core->total_imbrs)
	{
	    /* Allocate more pointers */
	    i = MAX(core->total_imbrs, 0);
	    core->total_imbrs = i + 1;

	    core->imbr = (edv_imbr_struct **)g_realloc(
		core->imbr,
		core->total_imbrs * sizeof(edv_imbr_struct *)
	    );
	    if(core->imbr == NULL)
	    {
		core->total_imbrs = 0;
		return(-1);
	    }
	    core->imbr[i] = NULL;
	}

	/* Shift window position in options if there are existing
	 * windows
	 */
	if(existing_windows > 0)
	{
	    gint	offset_x = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_X
			),
			offset_y = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_Y
			),
			x = EDV_GET_I(
			    EDV_CFG_PARM_IMBR_X
			) + offset_x,
			y = EDV_GET_I(
			    EDV_CFG_PARM_IMBR_Y
			) + offset_y;
	    EDV_SET_I(EDV_CFG_PARM_IMBR_X, x);
	    EDV_SET_I(EDV_CFG_PARM_IMBR_Y, y);
	}

	/* Create new image browser */
	core->imbr[i] = imbr = EDVImbrNew(core);
	if(imbr != NULL)
	{
	    gchar *startup_dir = EDVEvaluatePath(
		NULL,
		!STRISEMPTY(path) ?
		    path : EDV_GET_S(EDV_CFG_PARM_DIR_START_UP)
	    );
	    GtkWidget *toplevel = imbr->toplevel;

	    /* If the startup directory is not a directory then replace
	     * it with its parent
	     */
	    if(!ISPATHDIR(startup_dir))
	    {
		gchar *s = g_dirname(startup_dir);
		g_free(startup_dir);
		startup_dir = s;
	    }

	    EDVImbrSetBusy(imbr, TRUE);
	    GUIBlockInput(toplevel, TRUE);

	    /* Need to map it immediatly after creation, before any
	     * timeouts check on it
	     */
	    EDVImbrMap(imbr);

	    /* Get initial listing of the startup directory */
	    EDVImbrSelectPath(imbr, startup_dir);

	    EDVImbrUpdateMenus(imbr);

	    g_free(startup_dir);

	    GUIBlockInput(toplevel, FALSE);
	    EDVImbrSetBusy(imbr, FALSE);

	    return(i);
	}
	else
	{
	    return(-1);
	}
}

/*
 *	Creates a new Archiver.
 *
 *	The new Archiver will be added to the core and mapped.
 *
 *	The path specifies the archive that is to be opened on the
 *	Archiver after it has been mapped.
 *
 *	The password specifies the password.
 *
 *      Returns the index or -1 on error.
 */
gint EDVNewArchiver(
	edv_core_struct *core,
	const gchar *path,
	const gchar *password
)
{
	gint i, existing_windows = 0;
	cfg_item_struct *cfg_list;
	edv_archiver_struct *archiver;

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

	cfg_list = core->cfg_list;

	/* Count existing windows */
	for(i = 0; i < core->total_archivers; i++)
	{
	    if(core->archiver[i] != NULL)
		existing_windows++;
	}

	/* Look for available pointer in the array */
	for(i = 0; i < core->total_archivers; i++)
	{
	    if(core->archiver[i] == NULL)
		break;
	}
	if(i >= core->total_archivers)
	{
	    /* Allocate more pointers */
	    i = MAX(core->total_archivers, 0);
	    core->total_archivers = i + 1;

	    core->archiver = (edv_archiver_struct **)g_realloc(
		core->archiver,
		core->total_archivers * sizeof(edv_archiver_struct *)
	    );
	    if(core->archiver == NULL)
	    {
		core->total_archivers = 0;
		return(-1);
	    }
	    core->archiver[i] = NULL;
	}

	/* Shift window position in options if there are existing
	 * windows
	 */
	if(existing_windows > 0)
	{
	    const gint	offset_x = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_X
			),
			offset_y = EDV_GET_I(
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_Y
			),
			x = EDV_GET_I(
			    EDV_CFG_PARM_ARCHIVER_X
			) + offset_x,
			y = EDV_GET_I(
			    EDV_CFG_PARM_ARCHIVER_Y
			) + offset_y;
	    EDV_SET_I(EDV_CFG_PARM_ARCHIVER_X, x);
	    EDV_SET_I(EDV_CFG_PARM_ARCHIVER_Y, y);
	}

	/* Create new archiver */
	core->archiver[i] = archiver = EDVArchiverNew(core);
	if(archiver != NULL)
	{
	    /* Need to map it immediatly after creation, before any
	     * timeouts check on it
	     */
	    EDVArchiverMap(archiver);

	    /* If a path is specified then open it as an archive */
	    if(!STRISEMPTY(path))
		EDVArchiverOpenArchive(
		    archiver,
		    path,
		    password
		);

	    EDVArchiverUpdateMenus(archiver);

	    return(i);
	}
	else
	{
	    return(-1);
	}
}

/*
 *	Creates a new Properties Dialog.
 *
 *	The new dialog will be added to the core and mapped.
 *
 *	The obj specifies the object that is to be displayed on the
 *	dialog. The specified object will not be modified or deleted by
 *	this call.
 *
 *	The page_name specifies the name of the page to switch to
 *	after the dialog has been mapped.
 *
 *	Returns the index of the new Properties Dialog or -1 on
 *	error.
 */
gint EDVNewPropertiesDialogPage(
	edv_core_struct *core, edv_object_struct *obj,
	const gchar *page_name,
	GtkWidget *toplevel
)
{
	gint i, existing_windows = 0;
	edv_propdlg_struct *propdlg;
	cfg_item_struct *cfg_list;

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

	cfg_list = core->cfg_list;

	/* Count existing windows */
	for(i = 0; i < core->total_propdlgs; i++)
	{
	    if(core->propdlg[i] != NULL)
		existing_windows++;
	}

	/* Look for available pointer in the array */
	for(i = 0; i < core->total_propdlgs; i++)
	{
	    if(core->propdlg[i] == NULL)
		break;
	}
	if(i >= core->total_propdlgs)
	{
	    /* Allocate more pointers */
	    i = MAX(core->total_propdlgs, 0);
	    core->total_propdlgs = i + 1;

	    core->propdlg = (edv_propdlg_struct **)g_realloc(
		core->propdlg,
		core->total_propdlgs * sizeof(edv_propdlg_struct *)
	    );
	    if(core->propdlg == NULL)
	    {
		core->total_propdlgs = 0;
		return(-1);
	    }

	    core->propdlg[i] = NULL;
	}

	/* Create new property window */
	core->propdlg[i] = propdlg = EDVPropDlgNew(core, obj);
	if(propdlg != NULL)
	{
	    GtkNotebook *notebook = GTK_NOTEBOOK(propdlg->notebook);

	    /* Select tab? */
	    if((page_name != NULL) && (notebook != NULL))
	    {
		gint	page_num = -1,
			total_pages = g_list_length(notebook->first_tab);

		/* Check which tab is to be switched to by matching
		 * its name
		 */
		if(!g_strcasecmp(page_name, "General"))
		    page_num = 0;
		else if(!g_strcasecmp(page_name, "Details"))
		    page_num = MAX(total_pages - 1, 1);
		else if(!g_strcasecmp(page_name, "Link"))
		    page_num = 1;
		else if(!g_strcasecmp(page_name, "Device"))
		    page_num = 1;
		else if(!g_strcasecmp(page_name, "Device Node"))
		    page_num = (total_pages >= 4) ? 2 : 1;

		/* Matched page to be switched to? */
		if(page_num > -1)
		    gtk_notebook_set_page(notebook, page_num);
	    }

	    /* Shift window position if there are existing windows */
	    if(existing_windows > 0)
	    {
		gint	offset_x = CFGItemListGetValueI(
			    cfg_list,
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_X
			) * existing_windows,
			offset_y = CFGItemListGetValueI(
			    cfg_list,
			    EDV_CFG_PARM_WINDOW_CASCADE_OFFSET_Y
			) * existing_windows;
		EDVCenterWindowToWindowOffset(
		    toplevel, propdlg->toplevel,
		    offset_x, offset_y
		);
	    }
	    else
	    {
		EDVCenterWindowToWindow(
		    toplevel, propdlg->toplevel
		);
	    }

	    EDVPropDlgMap(propdlg);
	    EDVPropDlgResetHasChanges(propdlg, FALSE);
	    EDVPropDlgUpdateMenus(propdlg);

	    return(i);
	}
	else
	{
	    return(-1);
	}
}

gint EDVNewPropertiesDialog(
	edv_core_struct *core, edv_object_struct *obj,
	GtkWidget *toplevel
)
{
	return(
	    EDVNewPropertiesDialogPage(core, obj, NULL, toplevel)
	);
}


/*
 *	Runs the device check command on the given device reference.
 */
void EDVRunDeviceCheck(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
)
{
	pid_t p;
	const gchar *cmd;

	if((core == NULL) || (dev == NULL))
	    return;

	cmd = dev->command_check;
	if(cmd == NULL)
	    return;

	/* Seek past spaces in command string and make sure it is not
	 * empty
	 */
	while(ISBLANK(*cmd))
	    cmd++;
	if(*cmd == '\0')
	    return;

	p = Exec(cmd);
	if(p > 0)
	{
	    dev->last_check_time = (gulong)time(NULL);
	}
	else
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Unable to execute command:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de ejecutar la orden:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour exécuter l'ordre:\n\
\n\
    %s\n",
#endif
		cmd
	    );
	    EDVPlaySoundError(core);
	    EDVMessageError(
#ifdef PROG_LANGUAGE_ENGLISH
"Cannot Run Program",
		    buf,
"An error was encountered while attempting to run the\n\
specified program. Please verify that the command\n\
is specified properly by going to Device->Devices...\n\
and edit the device reference.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"No Puede Correr El Programa",
		    buf,
"Un error se encontró al procurar para correr el\n\
el programa especificado. Verifique por favor que la\n\
orden es especificado apropiadamente yendo\n\
Artefacto->Aartefactos... y redacta la referencia de\n\
artefacto.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Ne Peut Pas Courir Le Programme",
		    buf,
"Une erreur a été rencontrée en tentant pour courir\n\
le programme spécifié. S'il vous plaît vérifier que\n\
l'ordre est convenablement spécifié en allant\n\
Appareil->Appareils... et édite la référence d'appareil.",
#endif
		toplevel
	    );
	    g_free(buf);
	}
}

/*
 *      Runs the device tools command on the given device reference.
 */
void EDVRunDeviceTools(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
)
{
	pid_t p;
	const gchar *cmd;


	if((core == NULL) || (dev == NULL))
	    return;

	cmd = dev->command_tools;
	if(cmd == NULL)
	    return;

	/* Seek past spaces in command string and make sure it is not
	 * empty.
	 */
	while(ISBLANK(*cmd))
	    cmd++;
	if(*cmd == '\0')
	    return;

	p = Exec(cmd);
	if(p == 0)
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Unable to execute command:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de ejecutar la orden:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour exécuter l'ordre:\n\
\n\
    %s\n",
#endif
		cmd
	    );
	    EDVPlaySoundError(core);
	    EDVMessageError(
#ifdef PROG_LANGUAGE_ENGLISH
"Cannot Run Program",
		    buf,
"An error was encountered while attempting to run the\n\
specified program. Please verify that the command\n\
is specified properly by going to Device->Devices...\n\
and edit the device reference.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"No Puede Correr El Programa",
		    buf,
"Un error se encontró al procurar para correr el\n\
el programa especificado. Verifique por favor que la\n\
orden es especificado apropiadamente yendo\n\
Artefacto->Aartefactos... y redacta la referencia de\n\
artefacto.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Ne Peut Pas Courir Le Programme",
		    buf,
"Une erreur a été rencontrée en tentant pour courir\n\
le programme spécifié. S'il vous plaît vérifier que\n\
l'ordre est convenablement spécifié en allant\n\
Appareil->Appareils... et édite la référence d'appareil.",
#endif
		toplevel
	    );
	    g_free(buf);
	}
}

/*
 *      Runs the device format command on the given device reference.
 */
void EDVRunDeviceFormat(
	edv_core_struct *core, edv_device_struct *dev,
	GtkWidget *toplevel
)
{
	pid_t p;
	const gchar *cmd;

	if((core == NULL) || (dev == NULL))
	    return;

	cmd = dev->command_format;
	if(cmd == NULL)
	    return;

	/* Seek past spaces in command string and make sure it is not
	 * empty.
	 */
	while(ISBLANK(*cmd))
	    cmd++;
	if(*cmd == '\0')
	    return;

	p = Exec(cmd);
	if(p == 0)
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Unable to execute command:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de ejecutar la orden:\n\
\n\
    %s\n",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour exécuter l'ordre:\n\
\n\
    %s\n",
#endif
		cmd
	    );
	    EDVPlaySoundError(core);
	    EDVMessageError(
#ifdef PROG_LANGUAGE_ENGLISH
"Cannot Run Program",
		    buf,
"An error was encountered while attempting to run the\n\
specified program. Please verify that the command\n\
is specified properly by going to Device->Devices...\n\
and edit the device reference.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"No Puede Correr El Programa",
		    buf,
"Un error se encontró al procurar para correr el\n\
el programa especificado. Verifique por favor que la\n\
orden es especificado apropiadamente yendo\n\
Artefacto->Aartefactos... y redacta la referencia de\n\
artefacto.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Ne Peut Pas Courir Le Programme",
		    buf,
"Une erreur a été rencontrée en tentant pour courir\n\
le programme spécifié. S'il vous plaît vérifier que\n\
l'ordre est convenablement spécifié en allant\n\
Appareil->Appareils... et édite la référence d'appareil.",
#endif
		toplevel
	    );
	    g_free(buf);
	}
}

/*
 *	Runs the terminal defined in the configuration list.
 *
 *	If cmd is not NULL then the command specified by cmd will be
 *	appended to the terminal run command.
 *
 *	The wd specifies the working directory for the terminal. If this
 *	is NULL then the current working directory will be used.
 */
void EDVRunTerminal(
	edv_core_struct *core,
	const gchar *cmd,
	const gchar *wd,
	GtkWidget *toplevel
)
{
	gchar *cwd;
	const cfg_item_struct *cfg_list;

	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	if(!STRISEMPTY(wd))
	{
	    cwd = EDVGetCWD();
	    EDVSetCWD(wd);
	}
	else
	{
	    cwd = NULL;
	}

	if(!STRISEMPTY(cmd))
	{
	    /* Get terminal command for running a program with a
	     * terminal, format the command, and then run the command
	     */
	    gchar *cmd_src = STRDUP(EDV_GET_S(
		EDV_CFG_PARM_PROG_TERMINAL_RUN
	    ));
	    if(!STRISEMPTY(cmd_src))
	    {
		gchar *cmd_tar = g_strdup_printf(
		    "%s %s",
		    cmd_src,
		    cmd
		);
		Exec(cmd_tar);
		g_free(cmd_tar);
	    }
	    g_free(cmd_src);
	}
	else
	{
	    /* No command specified, just run the terminal */
	    gchar *cmd_src = STRDUP(EDV_GET_S(
		EDV_CFG_PARM_PROG_TERMINAL
	    ));
	    if(!STRISEMPTY(cmd_src))
		Exec(cmd_src);
	    g_free(cmd_src);
	}

	EDVSetCWD(cwd);

	g_free(cwd);
}


/*
 *	Maps the Recycle Bin Desktop Icon, creating it as needed.
 */
void EDVMapRecBinDeskIcon(edv_core_struct *core)
{
	if(core == NULL)
	    return;

	if(core->recbin_deskicon == NULL)
	    core->recbin_deskicon = EDVRecBinDeskIconNew(core);
	EDVRecBinDeskIconMap(core->recbin_deskicon);
	EDVRecBinDeskIconUpdate(core->recbin_deskicon);
}

/*
 *	Creates the recycle bin window as needed and maps it.
 */
void EDVMapRecBin(edv_core_struct *core)
{
	GtkWidget *toplevel;
	GtkCList *clist;
	const cfg_item_struct *cfg_list;
	edv_recbin_struct *recbin;

	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Get pointer to Recycle Bin window, create it as needed */
	recbin = core->recbin;
	if(recbin == NULL)
	    core->recbin = recbin = EDVRecBinNew(core);
	if(recbin == NULL)
	    return;

	toplevel = recbin->toplevel;

	/* Need to map it immediatly after it is created so that it does
	 * not get deleted in the timeout callback when the recycle bin
	 * is updated further below
	 */
	EDVRecBinMap(recbin);

	EDVRecBinSetBusy(recbin, TRUE);
	GUIBlockInput(toplevel, TRUE);

	clist = (GtkCList *)recbin->contents_clist;
	if(clist != NULL)
	{
	    gtk_clist_freeze(clist);
	    EDVRecBinContentsGetListing(
		recbin,
		EDV_GET_B(EDV_CFG_PARM_LISTS_ANIMATE_UPDATES)
	    );
	    gtk_clist_thaw(clist);
	}

	GUIBlockInput(toplevel, FALSE);
	EDVRecBinSetBusy(recbin, FALSE);

	EDVRecBinUpdateMenus(recbin);
}

/*
 *      Creates the history list window as needed and maps it.
 */
void EDVMapHistoryListWin(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	edv_history_win_struct *lw;


	if(core == NULL)
	    return;

	/* Get pointer to history list window, create it as needed */
	lw = core->history_listwin;
	if(lw == NULL)
	    core->history_listwin = lw = EDVHistoryWinNew(
		core
	    );
	if(lw == NULL)
	    return;

	EDVCenterWindowToWindow(toplevel, lw->toplevel);

	EDVHistoryWinSetBusy(lw, TRUE);

	EDVHistoryWinMap(lw);

	EDVHistoryWinUpdateList(lw);
	EDVHistoryWinUpdateMenus(lw);

	EDVHistoryWinSetBusy(lw, FALSE);
}


/*
 *	Maps the Options Window (creating it as needed) and switches
 *	to the page specified by page_name.
 */
void EDVMapOptionsWinPage(
	edv_core_struct *core,
	const gchar *page_name,
	GtkWidget *toplevel
)
{
	edv_gen_opt_win_struct *optwin;

	if(core == NULL)
	    return;

	optwin = core->options_window;
	if(optwin == NULL)
	    core->options_window = optwin = EDVCreateOptionsWindow(
		core
	    );
	if(optwin == NULL)
	    return;

	EDVCenterWindowToWindow(
	    toplevel, optwin->toplevel
	);

	EDVGenOptWinGetValues(optwin);

	if(!STRISEMPTY(page_name))
	    EDVOptionsWindowSelectTab(optwin, page_name);

	EDVGenOptWinMap(optwin);
	EDVGenOptWinResetHasChanges(optwin, FALSE);
	EDVGenOptWinUpdateMenus(optwin);
}

/*
 *	Creates the options window as needed and maps it.
 */
void EDVMapOptionsWin(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	EDVMapOptionsWinPage(core, NULL, toplevel);
}

/*
 *      Creates the options window as needed and maps it.
 */
void EDVMapCustomizeWin(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	gint	browser_num,
		imbr_num,
		archiver_num,
		recbin_num;
	const gchar *page_name;
	edv_gen_opt_win_struct *optwin;
	if(core == NULL)
	    return;


	/* Get Customize Window, create it as needed */
	optwin = core->customize_window;
	if(optwin == NULL)
	    core->customize_window = optwin = EDVCustomizeWindowNew(
		core
	    );
	if(optwin == NULL)
	    return;

	/* Center Customize Window */
	EDVCenterWindowToWindow(toplevel, optwin->toplevel);

	/* Get values from configuration and set to Customize Window's
	 * widgets
	 */
	EDVGenOptWinGetValues(optwin);

	/* Match the given toplevel with the corresponding window */
	if(EDVMatchWindowFromToplevel(
	    core,
	    toplevel,
	    &browser_num,
	    &imbr_num,
	    &archiver_num,
	    &recbin_num
	))
	{
	    if(browser_num > -1)
		page_name = "File Browser";
	    else if(imbr_num > -1)
		page_name = "Image Browser";
	    else if(archiver_num > -1)
		page_name = "Archiver";
	    else if(recbin_num > -1)
		page_name = "Recycle Bin";
	    else
		page_name = NULL;
	}
	else
	{
	    page_name = NULL;
	}

	/* Switch page */
	EDVCustomizeWindowSelectTab(optwin, page_name);

	EDVGenOptWinMap(optwin);
	EDVGenOptWinResetHasChanges(optwin, FALSE);
	EDVGenOptWinUpdateMenus(optwin);
}

/*
 *      Creates the devices list window as needed and maps it.
 */
void EDVMapDevicesListWin(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	edv_devices_list_win_struct *lw;


	if(core == NULL)
	    return;

	/* Get pointer to devices list window, create it as needed */
	lw = core->device_listwin;
	if(lw == NULL)
	    core->device_listwin = lw = EDVDevicesListWinNew(
		core
	    );
	if(lw == NULL)
	    return;

	EDVCenterWindowToWindow(toplevel, lw->toplevel);
	EDVDevicesListSetBusy(lw, TRUE);
	EDVDevicesListWinMap(lw);
	EDVDevicesListWinFetchValues(lw);
	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Creates the MIME Types list window as needed and maps it.
 *
 *	If type is not NULL then the MIME Type in the list that matches
 *	the type will be selected.
 */
void EDVMapMIMETypesListWin(
	edv_core_struct *core,
	const gchar *type,
	GtkWidget *toplevel
)
{
	edv_mime_types_list_win_struct *lw;

	if(core == NULL)
	    return;

	/* Get pointer to MIME Types list window, create it as needed */
	lw = core->mimetype_listwin;
	if(lw == NULL)
	    core->mimetype_listwin = lw = EDVMimeTypesListWinNew(
		core
	    );
	if(lw == NULL)
	    return;

	EDVCenterWindowToWindow(toplevel, lw->toplevel);
	EDVMimeTypesListWinSetBusy(lw, TRUE);
	EDVMimeTypesListWinMap(lw);
	EDVMimeTypesListWinFetchValues(lw);
	if(type != NULL)
	    EDVMimeTypesListWinSelectByType(lw, type);
	EDVMimeTypesListWinSetBusy(lw, FALSE);

}

/*
 *	Maps the Run Dialog (creating it as needed) with the specified
 *	command and working directory.
 */
void EDVMapRunDialogCommand(
	edv_core_struct *core,
	const gchar *command,
	const gchar *working_dir,
	GtkWidget *toplevel
)
{
	edv_run_dlg_struct *dlg;


	if(core == NULL)
	    return;

	/* Create run dialog as needed */
	dlg = core->run_dlg;
	if(dlg == NULL)
	{
	    core->run_dlg = dlg = EDVRunDlgNew(core);
	    EDVRunDlgFetchValues(dlg);
	}
	if(dlg == NULL)
	    return;

	/* Set command as needed */
	if(!STRISEMPTY(command))
	    EDVRunDlgSetCommand(dlg, command);

	/* Set working directory as needed */
	if(!STRISEMPTY(working_dir))
	    EDVRunDlgSetWorkingDir(dlg, working_dir);

	EDVRunDlgMap(dlg);
}

void EDVMapRunDialog(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	EDVMapRunDialogCommand(core, NULL, NULL, NULL);
}


/*
 *	Maps the Find Window.
 */
void EDVMapFindWin(edv_core_struct *core, const gchar *location)
{
	edv_find_win_struct *fw;

	if(core == NULL)
	    return;

	/* Get the Find Window, create it as needed */
	fw = core->find_win;
	if(fw == NULL)
	    core->find_win = fw = EDVFindWinNew(core);
	if(fw == NULL)
	    return;

	EDVFindWinSetReferenceWindow(
	    fw,
	    -1,
	    -1,
	    -1,
	    -1
	);

	if(!STRISEMPTY(location))
	    EDVFindWinSetLocation(fw, location, FALSE);

	EDVFindWinUpdateMenus(fw);
	EDVFindWinMap(fw);
}

/*
 *	Maps the find window relative to the given browser.
 */
void EDVMapBrowserFindWin(
	edv_core_struct *core, edv_browser_struct *browser
)
{
	gint i, browser_num = -1;
	edv_find_win_struct *fw;

	if((core == NULL) || (browser == NULL))
	    return;

	/* Get the Find Window, create it as needed */
	fw = core->find_win;
	if(fw == NULL)
	    core->find_win = fw = EDVFindWinNew(core);
	if(fw == NULL)
	    return;

	/* Get this File Browser's index */
	for(i = 0; i < core->total_browsers; i++)
	{
	    if(core->browser[i] == browser)
	    {
		browser_num = i;
		break;
	    }
	}

	/* Set the File Browser as the Find Window's reference */
	EDVFindWinSetReferenceWindow(
	    fw,
	    browser_num,
	    -1,
	    -1,
	    -1
	);

	/* Set initial location of find window to match the current
	 * location of the given browser.
	 */
	EDVFindWinSetLocation(
	    fw,
	    EDVBrowserCurrentLocation(browser),
	    FALSE
	);

	EDVCenterWindowToWindow(browser->toplevel, fw->toplevel);
	EDVFindWinUpdateMenus(fw);
	EDVFindWinMap(fw);
}

/*
 *      Maps the find window relative to the given image browser.
 */
void EDVMapImbrFindWin(
	edv_core_struct *core, edv_imbr_struct *imbr
)
{
	gint i, imbr_num = -1;
	edv_find_win_struct *fw;

	if((core == NULL) || (imbr == NULL))
	    return;

	/* Get the Find Window, create it as needed */
	fw = core->find_win;
	if(fw == NULL)
	    core->find_win = fw = EDVFindWinNew(core);
	if(fw == NULL)
	    return;

	/* Get this Image Browser's index */
	for(i = 0; i < core->total_imbrs; i++)
	{
	    if(core->imbr[i] == imbr)
	    {
		imbr_num = i;
		break;
	    }
	}

	/* Set the Image Browser as the Find Window's reference */
	EDVFindWinSetReferenceWindow(
	    fw,
	    -1,
	    imbr_num,
	    -1,
	    -1
	);

	/* Set initial location of find window to match the current
	 * location of the given image browser.
	 */
	EDVFindWinSetLocation(
	    fw,
	    EDVImbrCurrentLocation(imbr),
	    FALSE
	);

	EDVCenterWindowToWindow(imbr->toplevel, fw->toplevel);
	EDVFindWinUpdateMenus(fw);
	EDVFindWinMap(fw);
}

/*
 *	Maps the find window relative to the given archiver.
 */
void EDVMapArchiverFindWin(
	edv_core_struct *core, edv_archiver_struct *archiver
)
{
	gint i, archiver_num = -1;
	edv_find_win_struct *fw;

	if((core == NULL) || (archiver == NULL))
	    return;

	/* Get the Find Window, create it as needed */
	fw = core->find_win;
	if(fw == NULL)
	    core->find_win = fw = EDVFindWinNew(core);
	if(fw == NULL)
	    return;

	/* Get this Archiver's index */
	for(i = 0; i < core->total_archivers; i++)
	{
	    if(core->archiver[i] == archiver)
	    {
		archiver_num = i;
		break;
	    }
	}

	/* Set the Archiver as the Find Window's reference */
	EDVFindWinSetReferenceWindow(
	    fw,
	    -1,
	    -1,
	    -1,
	    archiver_num
	);

	/* Set initial location of find window to match the current
	 * location of the given archiver.
	 */
	EDVFindWinSetLocation(
	    fw,
	    EDVArchiverCurrentLocation(archiver),
	    FALSE
	);

	EDVCenterWindowToWindow(archiver->toplevel, fw->toplevel);
	EDVFindWinUpdateMenus(fw);
	EDVFindWinMap(fw);
}

/*
 *      Maps the find window relative to the given recycle bin.
 */
void EDVMapRecBinFindWin(
	edv_core_struct *core, edv_recbin_struct *recbin
)
{
	const gchar *recycled_index_file;
	gchar *recycled_dir;
	const cfg_item_struct *cfg_list;
	edv_find_win_struct *fw;

	if((core == NULL) || (recbin == NULL))
	    return;

	cfg_list = core->cfg_list;

	/* Get the Find Window, create it as needed */
	fw = core->find_win;
	if(fw == NULL)
	    core->find_win = fw = EDVFindWinNew(core);
	if(fw == NULL)
	    return;

	/* Set the Recycle Bin as the Find Window's reference */
	EDVFindWinSetReferenceWindow(
	    fw,
	    -1,
	    -1,
	    0,		/* First and only recycle bin */
	    -1
	);

	/* Get the path to the Recycled Objects Index File */
	recycled_index_file = EDV_GET_S(
	    EDV_CFG_PARM_FILE_RECYCLE_BIN_INDEX
	);

	/* Get the path to the Recycled Objects Directory */
	recycled_dir = (recycled_index_file != NULL) ?
	    g_dirname(recycled_index_file) : NULL;

	/* Set initial location of find window to be the Recycled
	 * Objects Directory
	 */
	if(recycled_dir != NULL)
	    EDVFindWinSetLocation(fw, recycled_dir, FALSE);

	EDVCenterWindowToWindow(recbin->toplevel, fw->toplevel);
	EDVFindWinUpdateMenus(fw);
	EDVFindWinMap(fw);

	g_free(recycled_dir);
}


/*
 *	Maps the About dialog and switches to the page specified by
 *	page_name.
 */
void EDVAboutPage(
	edv_core_struct *core,
	const gchar *page_name,
	GtkWidget *toplevel
)
{
	GtkNotebook *notebook;
	about_dlg_struct *d;

	if(core == NULL)
	    return;

	/* Get the About Dialog (create it as needed) */
	d = core->about_dlg;
	if(d == NULL)
	    core->about_dlg = d = AboutDlgNew(core);
	if(d == NULL)
	    return;

	/* Select tab */
	notebook = GTK_NOTEBOOK(d->notebook);
	if(!STRISEMPTY(page_name) && (notebook != NULL))
	{
	    gint	page_num = -1;
/*			total_pages = g_list_length(notebook->first_tab); */

	    /* Check which tab is to be switched to by matching
	     * its name
	     */
	    if(!g_strcasecmp(page_name, "Credits"))
		page_num = 0;
	    else if(!g_strcasecmp(page_name, "Copyright"))
		page_num = 1;
	    else if(!g_strcasecmp(page_name, "Version"))
		page_num = 2;

	    /* Matched page to be switched to? */
	    if(page_num > -1)
		gtk_notebook_set_page(notebook, page_num);
	}

	/* Map dialog */
	AboutDlgMap(d);
}

void EDVAbout(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	EDVAboutPage(core, NULL, toplevel);
}


/*
 *	Updates the RC styles.
 */
void EDVUpdateRCStyles(edv_core_struct *core)
{
	const cfg_item_struct *cfg_list;

	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Override GTK style? */
	if(CFGItemListGetValueI(
	    cfg_list, EDV_CFG_PARM_GTK_STYLE_OVERRIDE
	))
	{
	    GtkRcStyle *rcstyle = EDVCreateRCStyleFromCfg(
		CFGItemListGetValueStyle(
		    cfg_list, EDV_CFG_PARM_STYLE_STANDARD
		)
	    );
	    GTK_RC_STYLE_UNREF(core->standard_rcstyle)
	    core->standard_rcstyle = rcstyle;

	    rcstyle = EDVCreateRCStyleFromCfg(
		CFGItemListGetValueStyle(
		    cfg_list, EDV_CFG_PARM_STYLE_LISTS
		)
	    );
	    GTK_RC_STYLE_UNREF(core->lists_rcstyle)
	    core->lists_rcstyle = rcstyle;
	}
	else
	{
	    gboolean style_defined;
	    GtkStateType state;
	    GtkRcStyle *rcstyle;
	    const gchar	*font_name,
			*fg_color_name[5],
			*bg_color_name[5],
			*text_color_name[5],
			*base_color_name[5],
			*bg_pixmap_path[5];

	    font_name = core->font_name;

	    state = GTK_STATE_NORMAL;
	    fg_color_name[state] = core->fg_color_name;
	    bg_color_name[state] = core->bg_color_name;
	    text_color_name[state] = core->fg_color_name;
	    base_color_name[state] = core->bg_color_name;
	    bg_pixmap_path[state] = core->bg_pixmap_path;

	    state = GTK_STATE_ACTIVE;
	    fg_color_name[state] = core->fg_color_name;
	    bg_color_name[state] = core->bg_color_name;
	    text_color_name[state] = core->fg_color_name;
	    base_color_name[state] = core->bg_color_name;
	    bg_pixmap_path[state] = core->bg_pixmap_path;

	    state = GTK_STATE_PRELIGHT;
	    fg_color_name[state] = core->fg_color_name;
	    bg_color_name[state] = core->bg_color_name;
	    text_color_name[state] = core->fg_color_name;
	    base_color_name[state] = core->bg_color_name;
	    bg_pixmap_path[state] = core->bg_pixmap_path;

	    state = GTK_STATE_SELECTED;
	    fg_color_name[state] = core->sfg_color_name;
	    bg_color_name[state] = core->sbg_color_name;
	    text_color_name[state] = core->sfg_color_name;
	    base_color_name[state] = core->sbg_color_name;
	    bg_pixmap_path[state] = core->sbg_pixmap_path;

	    state = GTK_STATE_INSENSITIVE;
	    fg_color_name[state] = core->fg_color_name;
	    bg_color_name[state] = core->bg_color_name;
	    text_color_name[state] = core->fg_color_name;
	    base_color_name[state] = core->bg_color_name;
	    bg_pixmap_path[state] = core->bg_pixmap_path;

#define SET_RCSTYLE(_s_)	{			\
 gint i;						\
 const gchar *s;					\
							\
 style_defined = FALSE;					\
							\
 s = font_name;						\
 if(!STRISEMPTY(s)) {					\
  (_s_)->font_name = STRDUP(s);				\
  style_defined = TRUE;					\
 }							\
							\
 for(i = 0; i < 5; i++) {				\
  s = fg_color_name[i];					\
  if(!STRISEMPTY(s)) {					\
   (_s_)->color_flags[i] |= GTK_RC_FG;			\
   gdk_color_parse(s, &((_s_)->fg[i]));			\
   style_defined = TRUE;				\
  }							\
  s = bg_color_name[i];					\
  if(!STRISEMPTY(s)) {					\
   (_s_)->color_flags[i] |= GTK_RC_BG;			\
   gdk_color_parse(s, &((_s_)->bg[i]));			\
   style_defined = TRUE;				\
  }                                                     \
  s = text_color_name[i];				\
  if(!STRISEMPTY(s)) {					\
   (_s_)->color_flags[i] |= GTK_RC_TEXT;		\
   gdk_color_parse(s, &((_s_)->text[i]));		\
   style_defined = TRUE;				\
  }							\
  s = base_color_name[i];				\
  if(!STRISEMPTY(s)) {					\
   (_s_)->color_flags[i] |= GTK_RC_BASE;		\
   gdk_color_parse(s, &((_s_)->base[i]));		\
   style_defined = TRUE;				\
  }							\
							\
  s = bg_pixmap_path[i];				\
  if(!STRISEMPTY(s)) {					\
   (_s_)->bg_pixmap_name[i] = STRDUP(s);		\
   style_defined = TRUE;				\
  }							\
 }							\
}

	    /* Standard RC style */
	    rcstyle = gtk_rc_style_new();
	    SET_RCSTYLE(rcstyle)
	    GTK_RC_STYLE_UNREF(core->standard_rcstyle)
	    if(style_defined)
	    {
		core->standard_rcstyle = rcstyle;
	    }
	    else
	    {
		GTK_RC_STYLE_UNREF(rcstyle)
		core->standard_rcstyle = NULL;
	    }

	    /* Lists RC style */
	    rcstyle = gtk_rc_style_new();
	    SET_RCSTYLE(rcstyle)
	    GTK_RC_STYLE_UNREF(core->lists_rcstyle)
	    if(style_defined)
	    {
		core->lists_rcstyle = rcstyle;
	    }
	    else
	    {
		GTK_RC_STYLE_UNREF(rcstyle)
		core->lists_rcstyle = NULL;
	    }

#undef SET_RCSTYLE
	}
}


/*
 *	Updates the Users & Groups Popup Lists.
 *
 *	If the Popup Lists do not exist then they will be created.
 */
void EDVUpdateIDPULists(edv_core_struct *core)
{
	gint i, n;
	GtkRcStyle *rcstyle;
	GtkWidget *w;
	const edv_uid_struct *uid;
	const edv_gid_struct *gid;
	pulist_struct *pulist;

	if(core == NULL)
	    return;

	/* Get the Users popup list, create it as needed */
	pulist = core->users_pulist;
	if(pulist == NULL)
	    core->users_pulist = pulist = PUListNew();
	if(pulist == NULL)
	    return;

	/* Update style */
	rcstyle = core->lists_rcstyle;
	w = PUListGetCList(pulist);
	if((w != NULL) && (rcstyle != NULL))
	    gtk_widget_modify_style(w, rcstyle);

	/* Delete all existing items from the Popup List */
	PUListClear(pulist);

	/* Add Users to the Popup List */
	for(i = 0; i < core->total_uids; i++)
	{
	    uid = core->uid[i];
	    if((uid != NULL) ? STRISEMPTY(uid->name) : TRUE)
		continue;

	    n = PUListAddItem(pulist, uid->name);
	    if(n < 0)
		continue;

	    PUListSetItemData(pulist, n, (gpointer)i);
	}


	/* Get the Groups popup list, create it as needed */
	pulist = core->groups_pulist;
	if(pulist == NULL)
	    core->groups_pulist = pulist = PUListNew();
	if(pulist == NULL)
	    return;

	/* Update style */
	rcstyle = core->lists_rcstyle;
	w = PUListGetCList(pulist);
	if((w != NULL) && (rcstyle != NULL))
	    gtk_widget_modify_style(w, rcstyle);

	/* Delete all existing items from the Popup List */
	PUListClear(pulist);

	/* Add Groups to the Popup List */
	for(i = 0; i < core->total_gids; i++)
	{
	    gid = core->gid[i];
	    if((gid != NULL) ? STRISEMPTY(gid->name) : TRUE)
		continue;

	    n = PUListAddItem(pulist, gid->name);
	    if(n < 0)
		continue;

	    PUListSetItemData(pulist, n, (gpointer)i);
	}
}

/*
 *	Updates the Devices Popup List.
 *
 *	If the Popup List does not exist then it will be created.
 */
void EDVUpdateDevicesPUList(edv_core_struct *core)
{
	gint i, n;
	const gchar *dev_name;
	GtkRcStyle *rcstyle;
	GtkWidget *w;
	edv_device_icon_state state = EDV_DEVICE_ICON_STATE_STANDARD;
	edv_device_struct *dev;
	pulist_struct *pulist;

	if(core == NULL)
	    return;

	/* Get the Devices popup list, create it as needed */
	pulist = core->devices_pulist;
	if(pulist == NULL)
	    core->devices_pulist = pulist = PUListNew();
	if(pulist == NULL)
	    return;

	/* Update style */
	rcstyle = core->lists_rcstyle;
	w = PUListGetCList(pulist);
	if((w != NULL) && (rcstyle != NULL))
	    gtk_widget_modify_style(w, rcstyle);

	/* Delete all existing items from the Popup List */
	PUListClear(pulist);

	/* Add Devices to the Popup List */
	for(i = 0; i < core->total_devices; i++)
	{
	    dev = core->device[i];
	    if(dev == NULL)
		continue;

	    /* Skip devices who are marked as unlisted, in which case 
	     * they should not be listed on the devices popup list
	     */
	    if(EDV_DEVICE_IS_UNLISTED(dev))
		continue;

	    /* Realize device, loading icons as needed */
	    EDVDeviceRealize(dev, FALSE);

	    /* Get device name */
	    dev_name = dev->name;
	    if((dev_name == NULL) && (dev->device_path != NULL))
		dev_name = g_basename(dev->device_path);
	    if(dev_name == NULL)
		dev_name = "(null)";

	    /* Add new item to devices popup list */
	    n = PUListAddItemPixText(
		pulist, dev_name,
		dev->small_pixmap[state], dev->small_mask[state]
	    );
	    if(n < 0)
		continue;

	    PUListSetItemData(pulist, n, (gpointer)i);
	}
}

/*
 *	Updates the MIME Type class hint indices.
 */
void EDVUpdateMIMETypeHintIndices(edv_core_struct *core)
{
	gint i;
	edv_mime_type_struct *mt_ptr;

	if(core == NULL)
	    return;

	/* Reset all indexes for all MIME Type classes */
	core->mimetype_system_index_first = -1;
	core->mimetype_system_index_last = -1;
	core->mimetype_format_index_first = -1;
	core->mimetype_format_index_last = -1;
	core->mimetype_program_index_first = -1;
	core->mimetype_program_index_last = -1;
	core->mimetype_unique_index_first = -1;
	core->mimetype_unique_index_last = -1;

	/* Iterate through all MIME Types and update each class hint
	 * index
	 */
	for(i = 0; i < core->total_mimetypes; i++)
	{
	    mt_ptr = core->mimetype[i];
	    if(mt_ptr == NULL)
		continue;

	    switch(mt_ptr->mt_class)
	    {
	      case EDV_MIME_TYPE_CLASS_SYSTEM:
		if(core->mimetype_system_index_first < 0)
		    core->mimetype_system_index_first = i;
		core->mimetype_system_index_last = i;
		break;

	      case EDV_MIME_TYPE_CLASS_FORMAT:
		if(core->mimetype_format_index_first < 0)
		    core->mimetype_format_index_first = i;
		core->mimetype_format_index_last = i;
		break;

	      case EDV_MIME_TYPE_CLASS_PROGRAM:
		if(core->mimetype_program_index_first < 0)
		    core->mimetype_program_index_first = i;
		core->mimetype_program_index_last = i;
		break;

	      case EDV_MIME_TYPE_CLASS_UNIQUE:
		if(core->mimetype_unique_index_first < 0)
		    core->mimetype_unique_index_first = i;
		core->mimetype_unique_index_last = i;
		break;
	    }
	}
}

/*
 *	Updates the Open With Popup List.
 *
 *	If the Popup List does not exist then it will be created.
 */
void EDVUpdateOpenWithPUList(edv_core_struct *core)
{
	gint i, n;
	GtkRcStyle *rcstyle;
	GtkWidget *w;
	edv_mime_type_icon_state state = EDV_MIME_TYPE_ICON_STATE_STANDARD;
	edv_mime_type_struct *mt_ptr;
	pulist_struct *pulist;

	if(core == NULL)
	    return;

	/* Get the Open With popup list, create it as needed */
	pulist = core->open_with_pulist;
	if(pulist == NULL)
	    core->open_with_pulist = pulist = PUListNew();
	if(pulist == NULL)
	    return;

	/* Update style */
	rcstyle = core->lists_rcstyle;
	w = PUListGetCList(pulist);
	if((w != NULL) && (rcstyle != NULL))
	    gtk_widget_modify_style(w, rcstyle);

	/* Delete all existing items from the Popup List */
	PUListClear(pulist);

	/* Add MIME Types of class application to the Popup List */
	for(i = 0; i < core->total_mimetypes; i++)
	{
	    mt_ptr = core->mimetype[i];
	    if(mt_ptr == NULL)
		continue;

	    if(STRISEMPTY(mt_ptr->type))
		continue;

	    /* Skip MIME Type if it is not of class application */
	    if(mt_ptr->mt_class != EDV_MIME_TYPE_CLASS_PROGRAM)
		continue;

	    /* Realize MIME Type, loading icons as needed */
	    EDVMimeTypeRealize(mt_ptr, FALSE);

	    /* Add new item to the Popup List */
	    n = PUListAddItemPixText(
		pulist,
		(mt_ptr->description != NULL) ?
		    mt_ptr->description : mt_ptr->type,
		mt_ptr->small_pixmap[state], mt_ptr->small_mask[state]
	    );
	    if(n < 0)
		continue;

	    PUListSetItemData(pulist, n, (gpointer)i);
	}
}


/*
 *	Refreshes all of Endeavour's resources.
 */
void EDVRefresh(edv_core_struct *core)
{
	gint i;
	edv_browser_struct *browser;
	edv_imbr_struct *imbr;
	edv_archiver_struct *archiver;
	edv_recbin_struct *recbin;
	edv_devices_list_win_struct *device_list;
	edv_mime_types_list_win_struct *mimetype_list;
	edv_gen_opt_win_struct *optwin;

	if(core == NULL)
	    return;

	/* Update the Device mount states and stats */
	EDVDevicesListUpdateMountStates(
	    core->device, core->total_devices
	);
	EDVDevicesListUpdateStats(
	    core->device, core->total_devices
	);

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

	    EDVBrowserOPRefresh(browser);
	}

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

	    EDVImbrOPRefresh(imbr);
	}

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

	    EDVArchiverOPRefresh(archiver);
	}

	/* Refresh the Recycle Bin */
	recbin = core->recbin;
	if(recbin != NULL)
	{
	    EDVRecBinOPRefresh(recbin);
	}

	/* Devices List */
	device_list = core->device_listwin;
	if(EDVDevicesListWinIsMapped(device_list))
	{
	    EDVDevicesListWinFetchValues(device_list);
	}

	/* MIME Types List */
	mimetype_list = core->mimetype_listwin;
	if(EDVMimeTypesListWinIsMapped(mimetype_list))
	{
	    EDVMimeTypesListWinFetchValues(mimetype_list);
	}

	/* Options Window */
	optwin = core->options_window;
	if(EDVGenOptWinIsMapped(optwin))
	{
	    EDVGenOptWinGetValues(optwin);
	}

	/* Customize Window */
	optwin = core->customize_window;
	if(EDVGenOptWinIsMapped(optwin))
	{
	    EDVGenOptWinGetValues(optwin);
	}   


}

/*
 *	Reloads all of Endeavour's configurations from file and
 *	refreshes resources.
 *
 *	Any unsaved configuration in memory will be discarded.
 *
 *	EDVRefresh() will be automatically called at the end.
 */
void EDVReset(edv_core_struct *core)
{
	gint i;
	const gchar *s;
	gchar hostname[64 + 1];
	cfg_item_struct *cfg_list;

	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Reget the Host's Name */
	if(gethostname((char *)hostname, sizeof(hostname)))
	    *hostname = '\0';
	else
	    hostname[sizeof(hostname) - 1] = '\0';

	/* Reget the User & Group IDs */
	for(i = 0; i < core->total_uids; i++)
	    EDVUIDDelete(core->uid[i]);
	g_free(core->uid);
	core->uid = EDVUIDGetSystem(&core->total_uids);
	for(i = 0; i < core->total_gids; i++)
	    EDVGIDDelete(core->gid[i]);
	g_free(core->gid);
	core->gid = EDVGIDGetSystem(&core->total_gids);

	/* Reget the Effective User ID string */
	s = EDVUIDGetNameFromUID(
	    core->uid, core->total_uids,
	    core->effective_user_id, NULL
	);
	g_free(core->effective_user_id_str);
	core->effective_user_id_str = STRDUP(
	    !STRISEMPTY(s) ? s : "anonymous"
	);

	/* Reget the Effective User ID & Hostname string */
	g_free(core->effective_user_id_host_str);
	core->effective_user_id_host_str = g_strdup_printf(
	    "%s@%s",
	    !STRISEMPTY(s) ? s : "anonymous",
	    !STRISEMPTY(hostname) ? hostname : "unknown"
	);

	/* Reopen the configuration list from the configuration file */
	if(core->cfg_file != NULL)
	    CFGFileOpen(core->cfg_file, cfg_list);

	/* Reopen the Devices list from the Devices file */
	for(i = 0; i < core->total_devices; i++)
	    EDVDeviceDelete(core->device[i]);
	g_free(core->device);
	core->device = NULL;
	core->total_devices = 0;
	EDVDevicesListLoadFromSystem(
	    &core->device, &core->total_devices,
	    NULL, core
	);
	EDVDevicesListFileOpen(
	    EDV_GET_S(EDV_CFG_PARM_FILE_DEVICES),
	    &core->device, &core->total_devices,
	    NULL, core
	);

	/* Reopen the MIME Types list from the MIME Types file */
	for(i = 0; i < core->total_mimetypes; i++)
	    EDVMimeTypeDelete(core->mimetype[i]);
	g_free(core->mimetype);
	core->mimetype = NULL;
	core->total_mimetypes = 0;
	EDVMimeTypesListSystemFileOpen(
	    &core->mimetype, &core->total_mimetypes,
	    -1,			/* Append */
	    NULL, core,
	    NULL, core,
	    TRUE		/* Read only */
	);
	EDVMimeTypesListFileOpen(
	    EDV_GET_S(EDV_CFG_PARM_FILE_MIME_TYPES),
	    &core->mimetype, &core->total_mimetypes,
	    -1,			/* Append */
	    FALSE,		/* Do not update */
	    FALSE,		/* Not only newer */
	    NULL, core,
	    NULL, core,
	    NULL, core,
	    FALSE		/* Not read only */
	);
	EDVMimeTypesListFileOpen(
	    EDV_GET_S(EDV_CFG_PARM_FILE_MIME_TYPES_GLOBAL),
	    &core->mimetype, &core->total_mimetypes,
	    -1,			/* Append */
	    FALSE,		/* Do not update */
	    FALSE,		/* Not only newer */
	    NULL, core,
	    NULL, core,
	    NULL, core,
	    TRUE		/* Read only */
	);
	/* Update the MIME Types class list index hints */
	EDVUpdateMIMETypeHintIndices(core);

	/* Notify all resources about the reset
	 *
	 * This will cause all the windows to update their
	 * customizable attributes based on the just reloaded
	 * configuration
	 */
	EDVReconfiguredEmit(core);

	/* Notify about the updated global write protect state */
	EDVWriteProtectChangedEmit(
	    core, EDV_GET_B(EDV_CFG_PARM_WRITE_PROTECT)
	);

	/* Refresh all the windows */
	EDVRefresh(core);
}


/*
 *	Syncs disks and saves all of Endeavour's configuration in
 *	memory to file.
 */
void EDVSyncDisks(edv_core_struct *core)
{
	gulong time_start = (gulong)time(NULL);
	gboolean object_existed;
	const gchar *s;
	struct stat lstat_buf;
	cfg_item_struct *cfg_list;

	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Begin saving all of Endeavour's configuration in memory to
	 * file
	 */

	/* MIME Types */
	s = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_MIME_TYPES
	);
	if(!STRISEMPTY(s))
	{
	    gchar *path = STRDUP(s);
	    object_existed = access(path, F_OK) ? FALSE : TRUE;
	    EDVMimeTypesListFileSave(
		path, core->mimetype, core->total_mimetypes,
		FALSE,		/* Do not write MIME Types marked read_only */
		NULL, core
	    );
	    if(!lstat(path, &lstat_buf))
	    {
		if(object_existed)
		    EDVObjectModifiedEmit(core, path, NULL, &lstat_buf);
		else
		    EDVObjectAddedEmit(core, path, &lstat_buf);
	    }
	    g_free(path);
	}

	/* Devices */
	s = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_DEVICES
	);
	if(!STRISEMPTY(s))
	{
	    gchar *path = STRDUP(s);
	    object_existed = access(path, F_OK) ? FALSE : TRUE;
	    EDVDeviceListFileSave(
		path, core->device, core->total_devices,
		NULL, core
	    );
	    if(!lstat(path, &lstat_buf))
	    {
		if(object_existed)
		    EDVObjectModifiedEmit(core, path, NULL, &lstat_buf);
		else
		    EDVObjectAddedEmit(core, path, &lstat_buf);
	    }
	    g_free(path);
	}

	/* Configuration */
	if(core->cfg_file != NULL)
	{
	    gchar *path = STRDUP(core->cfg_file);
	    object_existed = access(path, F_OK) ? FALSE : TRUE;
	    CFGFileSave(path, cfg_list);
	    if(!lstat(path, &lstat_buf))
	    {
		if(object_existed)
		    EDVObjectModifiedEmit(core, path, NULL, &lstat_buf);
		else
		    EDVObjectAddedEmit(core, path, &lstat_buf);
	    }
	    g_free(path);
	}

	/* Flush all disk operations */
	sync();

	/* Record history */
	EDVAppendHistory(
	    core,
	    EDV_HISTORY_SYNC_DISKS,
	    time_start, (gulong)time(NULL),
	    0,
	    NULL,		/* Source */
	    NULL,		/* Target */
	    NULL		/* Comment */
	);
}


/*
 *	Clears all history events.
 *
 *	Clears the History List Window and deletes the history events
 *	log file.
 */
void EDVClearHistoryEvents(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	if(core == NULL)
	    return;

	/* Map History List Window */
	EDVMapHistoryListWin(core, toplevel);

	/* Clear history events by using the History List Window
	 * to delet the history events log file, the History List
	 * Window will also be cleared
	 */
	EDVHistoryWinClear(core->history_listwin);
}

/*
 *	Clears the locations history on all windows.
 */
void EDVClearLocationsHistory(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	gint i, response;
	const gchar *path;
	edv_browser_struct *browser;
	edv_imbr_struct *imbr;
	edv_archiver_struct *archiver;
	const cfg_item_struct *cfg_list;


	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Confirm */
	EDVPlaySoundQuestion(core);
	CDialogSetTransientFor(toplevel);
	response = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"Confirme Claro",
"¿Usted está seguro que usted quiere borrar la historia de\n\
ubicaciones en todas las ventanas?\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"Confirmer Clair",
"Etes-vous sûr que vous voulez effacer l'histoire d'emplacements\n\
sur toutes les fenêtres?\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Bestätigen Sie Klar",
"Sind sie sicher sie die orte geschichte auf allen fenstern\n\
wollen löschen?\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Confermare Chiaro",
"Lei sono sicuro che lei vuole cancellare la storia di posizioni\n\
su tutte le finestre?\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Bevestiig Helder",
"ent u zeker u de plaatzen geschiedenis op alle ramen wil\n\
schrappen?\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Confirme Claro",
"Estão seguro quer anular a história de localidades em todas\n\
as janelas?\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Bekreft Klar",
"Er de sikker de stryker plasserings historien på alle vinduer?\n",
#else
"Confirm Clear",
"Are you sure you want to delete the locations history on\n\
all the windows?\n",
#endif
	    NULL,
	    CDIALOG_ICON_QUESTION,
	    CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
	    CDIALOG_BTNFLAG_YES
	);
	CDialogSetTransientFor(NULL);
	if(response != CDIALOG_RESPONSE_YES)
	    return;


	/* Clear File Browser Locations List */
	for(i = 0; i < core->total_browsers; i++)
	{
	    browser = core->browser[i];
	    if(browser == NULL)
		continue;

	    GUIComboSetList(browser->location_combo, NULL);
	}

	/* Delete File Browser Locations History File */
	path = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_BROWSER_LOCATION_HISTORY
	);
	if(!STRISEMPTY(path))
	{
	    if(!unlink((const char *)path))
		EDVObjectRemovedEmit(core, path);
	}


	/* Clear Image Browser Locations List */
	for(i = 0; i < core->total_imbrs; i++)
	{
	    imbr = core->imbr[i];
	    if(imbr == NULL)
		continue;

	    GUIComboSetList(imbr->location_combo, NULL);
	}

	/* Delete Image Browser Locations History File */
	path = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_IMBR_LOCATION_HISTORY
	);
	if(!STRISEMPTY(path))
	{
	    if(!unlink((const char *)path))
		EDVObjectRemovedEmit(core, path);
	}


	/* Clear Archiver Locations List */
	for(i = 0; i < core->total_archivers; i++)
	{
	    archiver = core->archiver[i];
	    if(archiver == NULL)
		continue;

	    GUIComboSetList(archiver->location_combo, NULL);
	}

	/* Delete Image Browser Locations History File */
	path = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_ARCHIVER_LOCATION_HISTORY
	);
	if(!STRISEMPTY(path))
	{
	    if(!unlink((const char *)path))
		EDVObjectRemovedEmit(core, path);
	}

	EDVPlaySoundCompleted(core);
}

/*
 *	Clear the Run Dialog history and deletes the run history log
 *	file.
 */
void EDVClearRunHistory(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	gint response;
	const gchar *path;
	edv_run_dlg_struct *d;
	const cfg_item_struct *cfg_list;


	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Confirm */
	EDVPlaySoundQuestion(core);
	CDialogSetTransientFor(toplevel);
	response = CDialogGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"Confirme Claro",
"¿Usted está seguro que usted quiere borrar el corre la historia?\n",
#elif defined(PROG_LANGUAGE_FRENCH)
"Confirmer Clair",
"Etes-vous sûr que vous voulez effacer l'histoire de course?\n",
#elif defined(PROG_LANGUAGE_GERMAN)
"Bestätigen Sie Klar",
"Sind Sie sicher Sie die Lauf Geschichte wollen löschen?\n",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Confermare Chiaro",
"Lei sono sicuro che lei vuole cancellare il ha corso la storia?\n",
#elif defined(PROG_LANGUAGE_DUTCH)
"Bevestiig Helder",
"Bent u zeker u de tocht geschiedenis wil schrappen?\n",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Confirme Claro",
"Estão seguro quer anular a história de corrida?\n",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Bekreft Klar",
"Er De sikker De stryker løphistorien?\n",
#else
"Confirm Clear",
"Are you sure you want to delete the run history?\n",
#endif
	    NULL,
	    CDIALOG_ICON_QUESTION,
	    CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
	    CDIALOG_BTNFLAG_YES
	);
	CDialogSetTransientFor(NULL);
	if(response != CDIALOG_RESPONSE_YES)
	    return;


	/* Clear Run Dialog Run History */
	d = core->run_dlg;
	if(d != NULL)
	{
	    GUIComboSetList(d->run_combo, NULL);
	}

	/* Delete Run Dialog Run History File */
	path = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_FILE_RUNDLG_HISTORY
	);
	if(!STRISEMPTY(path))
	{
	    if(!unlink((const char *)path))
		EDVObjectRemovedEmit(core, path);
	}

	EDVPlaySoundCompleted(core);
}


/*
 *	Calls the Endeavour Download Front End program to download the
 *	given URL described in dnd_obj to the directory target_path.
 *
 *	If the dnd_obj is not given then the download program will still
 *	be runned but used to query where the user wants to download
 *	to.
 */
void EDVInternetDownloadObject(
	edv_core_struct *core,
	const url_struct *url,			/* Source */
	const gchar *target_path,		/* Target */
	GtkWidget *toplevel
)
{
	gboolean confirm;
	const gchar *prog;
	const cfg_item_struct *cfg_list;

	if((core == NULL) || STRISEMPTY(target_path))
	    return;

	cfg_list = core->cfg_list;

	/* Get configuration */
	confirm = CFGItemListGetValueI(
	    cfg_list, EDV_CFG_PARM_CONFIRM_DOWNLOAD
	);
	prog = CFGItemListGetValueS(
	    cfg_list, EDV_CFG_PARM_PROG_NET_DOWNLOAD
	);

	/* Download Front End program not set? */
	if(STRISEMPTY(prog))
	{
	    EDVPlaySoundError(core);
	    EDVMessageError(
		"No Download Program",
"Download Front End program not set",
"To set the network download program, please go\n\
to Settings->Options->Programs.",
		toplevel
	    );
	}
	else
	{
	    gint p;
	    gchar *url_str, *cmd;

	    /* Format url */
	    if(url == NULL)
		url_str = STRDUP("");
	    else if(url->port > 0)
		url_str = g_strdup_printf(
		    "%s://%s%s%s%s%s:%i%s",
		    (url->protocol != NULL) ? url->protocol : "file",
		    (url->user != NULL) ? url->user : "",
		    (url->password != NULL) ? ":" : "",
		    (url->password != NULL) ? url->password : "",
		    (url->user != NULL) ? "@" : "",
		    (url->host != NULL) ? url->host : "localhost",
		    url->port,
		    url->path
		);
	    else
		url_str = g_strdup_printf(
		    "%s://%s%s%s%s%s%s",
		    (url->protocol != NULL) ? url->protocol : "file",
		    (url->user != NULL) ? url->user : "",
		    (url->password != NULL) ? ":" : "",
		    (url->password != NULL) ? url->password : "",
		    (url->user != NULL) ? "@" : "",
		    (url->host != NULL) ? url->host : "localhost",
		    url->path
		);

	    /* Format command, the program should accept arguments being
	     * the first argument(s) are the URLs to download and the
	     * last argument is the destination path
	     */
	    cmd = g_strdup_printf(
		"%s %s %s \"%s\" \"%s\"",
		prog,
		"-b",
		confirm ? "-c" : "",
		url_str, target_path
	    );

	    /* Execute the download command */
	    p = Exec(cmd);
	    if(p <= 0)
	    {
		gchar *s = g_strdup_printf(
"Unable to execute command:\n\n    %s",
		    cmd
		);
		EDVPlaySoundError(core);
		EDVMessageError(
		    "Download Failed",
		    s,
"Make sure that the network download program is\n\
defined correctly, please go to\n\
Settings->Options->Programs.",
		    toplevel
		);
		g_free(s);
	    }

	    g_free(cmd);
	    g_free(url_str);
	}
}


/*
 *	Tabulates the total size of all objects in the recycled objects
 *	directory and checks if it has exceeded the configuration
 *	specified recycle bin size warn size. If it has exceeded then
 *	the user will be warned.
 */
void EDVRecycleBinSizeCheck(
	edv_core_struct *core, GtkWidget *toplevel
)
{
	const gchar *recycled_index_file;
	const cfg_item_struct *cfg_list;
	edv_recbin_index_struct *rbi_ptr;
	edv_recycled_object_struct *obj;
	gulong recbin_size_warn = 0, total_size = 0;


	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Get path to recycled objects index file */
	recycled_index_file = EDV_GET_S(EDV_CFG_PARM_FILE_RECYCLE_BIN_INDEX);
	if(recycled_index_file == NULL)
	    return;

	/* Get recycle bin size warn */
	recbin_size_warn = EDV_GET_UL(EDV_CFG_PARM_RECBIN_SIZE_WARN);
	/* If the warn size is 0, then this implies no warning should
	 * be given (in which case do not bother to check anything)
	 */
	if(recbin_size_warn == 0)
	    return;

	/* Get recycled objects directory index pointer used to iterate
	 * through all recycled objects
	 */
	rbi_ptr = EDVRecBinIndexOpen(recycled_index_file);
	while(!EDVRecBinIndexNext(rbi_ptr))
	{
	    /* Get recycled object obtained from the recycled objects
	     * directory index and do not modify it
	     */
	    obj = rbi_ptr->obj;
	    if(obj == NULL)
		continue;

	    /* Increase total size of recycled objects by this recycled
	     * object
	     */
	    total_size += obj->size;
	}
	EDVRecBinIndexClose(rbi_ptr);
	rbi_ptr = NULL;


	/* At this point the total size of all recycled objects has been
	 * obtained. Now check if total_size reaches or exceeds the
	 * configuration set size to warn
	 */
	if(total_size >= recbin_size_warn)
	{
	    gchar	*total_size_s = STRDUP(
		EDVGetObjectSizeStr(core, total_size)
			),
			*recbin_size_warn_s = STRDUP(
		EDVGetObjectSizeStr(core, recbin_size_warn)
			),
			*buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"The size of the recycle bin is now %s bytes,\n\
it has exceeded the user specified size of %s bytes\n\
at which this warning is to be shown.\n\
\n\
To purge all the existing recycled objects, first\n\
go to the Recycle Bin and then go to File->Purge All.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"El tamaño del cajón de la recirculación es ahora %s bytes,\n\
ha excedido al usuario el tamaño especificado de\n\
%s bytes en que este advertir deberá ser mostrado.\n\
\n\
Al purge todos los objetos existentes de recycled, van\n\
primero al Cajón de Reciclaje y entonces van a\n\
File->Purge All.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"La taille du recycle l'huche est maintenant %s bytes,\n\
il a dépassé l'utilisateur la taille spécifiée de\n\
%s bytes à qui cet avertissement devrait être montré.\n\
\n\
Purger tout l'existant recyclé objets, premièrement va\n\
au Recycle Huche et alors va File->Purge All.",
#endif
		total_size_s, recbin_size_warn_s
	    );

	    EDVPlaySoundWarning(core);
	    CDialogSetTransientFor(toplevel);
	    CDialogGetResponseIconData(
#ifdef PROG_LANGUAGE_ENGLISH
		"Recycle Bin Size Warning",
#endif
#ifdef PROG_LANGUAGE_SPANISH
		"Advertir De Cajón De Reciclaje",
#endif
#ifdef PROG_LANGUAGE_FRENCH
		"Recycler l'Avertissement d'Huche",
#endif
		buf, NULL,
		(guint8 **)icon_trash_32x32_xpm,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);

	    g_free(buf);
	    g_free(total_size_s);
	    g_free(recbin_size_warn_s);
	}
}
