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

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

#include "guiutils.h"
#include "cdialog.h"
#include "pdialog.h"
#include "fb.h"
#include "progressdialog.h"
#include "toolbar.h"

#include "cfg.h"
#include "edv_types.h"
#include "edv_obj.h"
#include "edv_confirm.h"
#include "edv_recycled_obj.h"
#include "edv_recbin_stat.h"
#include "edv_recbin_delete.h"
#include "edv_device.h"
#include "edv_device_mount.h"
#include "edv_devices_list.h"
#include "edv_mount_bar.h"
#include "edv_find_bar.h"
#include "edv_status_bar.h"
#include "obj_op_dlg.h"
#include "browser.h"
#include "browser_cb.h"
#include "browser_op_cb.h"
#include "browser_contents_list.h"
#include "browser_dir_tree.h"
#include "browser_contents_list.h"
#include "endeavour2.h"
#include "edv_obj_create.h"
#include "edv_obj_op.h"
#include "edv_cb.h"
#include "edv_help.h"
#include "edv_op.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"
#include "edv_cfg_list.h"
#include "config.h"

#include "images/icon_wildcards_32x32.xpm"


void EDVBrowserOPCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void EDVBrowserOPEnterCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void EDVBrowserOPLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data
);

void EDVBrowserOPClose(edv_browser_struct *browser);
void EDVBrowserOPExit(edv_browser_struct *browser);

void EDVBrowserOPSyncDisks(edv_browser_struct *browser);
void EDVBrowserOPRun(edv_browser_struct *browser);
void EDVBrowserOPWriteProtect(edv_browser_struct *browser);
void EDVBrowserOPDeleteMethodRecycle(edv_browser_struct *browser);
void EDVBrowserOPDeleteMethodPurge(edv_browser_struct *browser);

static void EDVBrowserOPNewObjectNexus(
	edv_browser_struct *browser,
	edv_object_type type, GtkWidget *toplevel
);
void EDVBrowserOPNewObject(
	edv_browser_struct *browser, toolbar_item_struct *item
);
void EDVBrowserOPNewFile(edv_browser_struct *browser);
void EDVBrowserOPNewDirectory(edv_browser_struct *browser);
void EDVBrowserOPNewLink(edv_browser_struct *browser);
void EDVBrowserOPNewFifo(edv_browser_struct *browser);
void EDVBrowserOPNewDeviceBlock(edv_browser_struct *browser);
void EDVBrowserOPNewDeviceCharacter(edv_browser_struct *browser);
void EDVBrowserOPNewSocket(edv_browser_struct *browser);

void EDVBrowserOPOpen(edv_browser_struct *browser);
void EDVBrowserOPOpenTo(edv_browser_struct *browser, toolbar_item_struct *item);
void EDVBrowserOPOpenWith(edv_browser_struct *browser);

void EDVBrowserOPCopyPath(edv_browser_struct *browser);
void EDVBrowserOPCopyURL(edv_browser_struct *browser);
void EDVBrowserOPPaste(edv_browser_struct *browser);

static edv_object_struct **EDVBrowserGetSelectedObjects(
	edv_browser_struct *browser, gint *total
);
void EDVBrowserOPMove(edv_browser_struct *browser);
void EDVBrowserOPCopy(edv_browser_struct *browser);
void EDVBrowserOPLink(edv_browser_struct *browser);
void EDVBrowserOPRename(edv_browser_struct *browser);
void EDVBrowserOPChMod(edv_browser_struct *browser);
void EDVBrowserOPChOwn(edv_browser_struct *browser);
void EDVBrowserOPChTime(edv_browser_struct *browser);
void EDVBrowserOPDelete(edv_browser_struct *browser);
void EDVBrowserOPProperties(edv_browser_struct *browser);

void EDVBrowserOPSelectAll(edv_browser_struct *browser);
void EDVBrowserOPUnselectAll(edv_browser_struct *browser);
void EDVBrowserOPInvertSelection(edv_browser_struct *browser);

void EDVBrowserOPDownload(edv_browser_struct *browser);

void EDVBrowserOPRefresh(edv_browser_struct *browser);
void EDVBrowserOPRefreshAll(edv_browser_struct *browser);
void EDVBrowserOPGoToParent(edv_browser_struct *browser);
void EDVBrowserOPGoToHome(edv_browser_struct *browser);
void EDVBrowserOPExpand(edv_browser_struct *browser);

void EDVBrowserContentsFilter(edv_browser_struct *browser);

void EDVBrowserOPDirectoryTreeOrigin(edv_browser_struct *browser);

void EDVBrowserOPMount(edv_browser_struct *browser);
void EDVBrowserOPEject(edv_browser_struct *browser);

void EDVBrowserMIMETypes(edv_browser_struct *browser);


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


/*
 *	Operation ID callback nexus.
 *
 *	The data must be a edv_browser_opid_struct *.
 */
void EDVBrowserOPCB(
	toolbar_item_struct *item, gint id, gpointer data  
)
{
	gint dev_num;
	GtkWidget *toplevel;
	cfg_item_struct *cfg_list;
	edv_device_struct *dev;
	edv_browser_struct *browser;
	edv_core_struct *core;
	edv_browser_opid_struct *opid = EDV_BROWSER_OPID(data);
	if(opid == NULL)
	    return;

	browser = opid->browser;
	if((browser == NULL) || (opid->flags & EDV_OPID_NO_OP))
	    return;

	if(browser->processing || (browser->freeze_count > 0))
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	browser->freeze_count++;

	/* Get last selected device (if any) */
	dev_num = browser->selected_dev_num;
	dev = ((dev_num >= 0) && (dev_num < core->total_devices)) ?
	    core->device[dev_num] : NULL;

	/* Handle by operation id code */
	switch(opid->op)
	{
	  case EDV_BROWSER_OP_NONE:
	  case EDV_BROWSER_OP_SEPARATOR:
	    break;

	  case EDV_BROWSER_OP_CLOSE:
	    EDVBrowserOPClose(browser);
	    break;
	  case EDV_BROWSER_OP_EXIT:
	    EDVBrowserOPExit(browser);
	    break;

	  case EDV_BROWSER_OP_SYNC_DISKS:
	    EDVBrowserOPSyncDisks(browser);
	    break;
	  case EDV_BROWSER_OP_HISTORY:
	    EDVMapHistoryListWin(core, toplevel);
	    break;
	  case EDV_BROWSER_OP_RUN:
	    EDVBrowserOPRun(browser);
	    break;
	  case EDV_BROWSER_OP_RUN_TERMINAL:
	    if(core != NULL)
	    {
		gchar *wd = STRDUP(EDVBrowserCurrentLocation(browser));
		EDVRunTerminal(core, NULL, wd, toplevel);
		g_free(wd);
	    }
	    break;
	  case EDV_BROWSER_OP_WRITE_PROTECT:
	    EDVBrowserOPWriteProtect(browser);
	    break;
	  case EDV_BROWSER_OP_DELETE_METHOD_RECYCLE:
	    EDVBrowserOPDeleteMethodRecycle(browser);
	    break;
	  case EDV_BROWSER_OP_DELETE_METHOD_PURGE:
	    EDVBrowserOPDeleteMethodPurge(browser);
	    break;

	  case EDV_BROWSER_OP_NEW:
	    EDVBrowserOPNewObject(browser, item);
	    break;
	  case EDV_BROWSER_OP_NEW_FILE:
	    EDVBrowserOPNewFile(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_DIRECTORY:
	    EDVBrowserOPNewDirectory(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_LINK:
	    EDVBrowserOPNewLink(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_FIFO:
	    EDVBrowserOPNewFifo(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_DEVICE_BLOCK:
	    EDVBrowserOPNewDeviceBlock(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_DEVICE_CHARACTER:
	    EDVBrowserOPNewDeviceCharacter(browser);
	    break;
	  case EDV_BROWSER_OP_NEW_SOCKET:
	    EDVBrowserOPNewSocket(browser);
	    break;

	  case EDV_BROWSER_OP_OPEN:
	    EDVBrowserOPOpen(browser);
	    break;
	  case EDV_BROWSER_OP_OPEN_TO:
	    EDVBrowserOPOpenTo(browser, item);
	    break;
	  case EDV_BROWSER_OP_OPEN_WITH:
	    EDVBrowserOPOpenWith(browser);
	    break;

	  case EDV_BROWSER_OP_COPY_PATH:
	    EDVBrowserOPCopyPath(browser);
	    break;
	  case EDV_BROWSER_OP_COPY_URL:
	    EDVBrowserOPCopyURL(browser);
	    break;
	  case EDV_BROWSER_OP_PASTE:
	    EDVBrowserOPPaste(browser);
	    break;

	  case EDV_BROWSER_OP_MOVE:
	    EDVBrowserOPMove(browser);
	    break;
	  case EDV_BROWSER_OP_COPY:
	    EDVBrowserOPCopy(browser);
	    break;
	  case EDV_BROWSER_OP_LINK:
	    EDVBrowserOPLink(browser);
	    break;
	  case EDV_BROWSER_OP_RENAME:
	    EDVBrowserOPRename(browser);
	    break;
	  case EDV_BROWSER_OP_CHMOD:
	    EDVBrowserOPChMod(browser);
	    break;
	  case EDV_BROWSER_OP_CHOWN:
	    EDVBrowserOPChOwn(browser);
	    break;
	   case EDV_BROWSER_OP_CHTIME:
	    EDVBrowserOPChTime(browser);
	    break;
	  case EDV_BROWSER_OP_DELETE:
	    EDVBrowserOPDelete(browser);
	    break;
	  case EDV_BROWSER_OP_PROPERTIES:
	    EDVBrowserOPProperties(browser);
	    break;

	  case EDV_BROWSER_OP_SELECT_ALL:
	    EDVBrowserOPSelectAll(browser);
	    break;
	  case EDV_BROWSER_OP_UNSELECT_ALL:
	    EDVBrowserOPUnselectAll(browser);
	    break;
	  case EDV_BROWSER_OP_INVERT_SELECTION:
	    EDVBrowserOPInvertSelection(browser);
	    break;
	  case EDV_BROWSER_OP_FIND:
	    EDVMapBrowserFindWin(core, browser);
	    break;

	  case EDV_BROWSER_OP_DOWNLOAD:
	    EDVBrowserOPDownload(browser);
	    break;

	  case EDV_BROWSER_OP_REFRESH:
	    EDVBrowserOPRefresh(browser);
	    break;
	  case EDV_BROWSER_OP_REFRESH_ALL:
	    EDVBrowserOPRefreshAll(browser);
	    break;
	  case EDV_BROWSER_OP_GOTO_PARENT:
	    EDVBrowserOPGoToParent(browser);
	    break;
	  case EDV_BROWSER_OP_GOTO_HOME:
	    EDVBrowserOPGoToHome(browser);
	    break;
	  case EDV_BROWSER_OP_EXPAND:
	    EDVBrowserOPExpand(browser);
	    break;

	  case EDV_BROWSER_OP_SHOW_TOOL_BAR:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_TOOL_BAR
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_TOOL_BAR, state
		);
		EDVReconfiguredEmit(core);
	    }
	    break;
	  case EDV_BROWSER_OP_SHOW_LOCATION_BAR:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_LOCATION_BAR
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_LOCATION_BAR, state
		);
		EDVReconfiguredEmit(core);
	    }
	    break;
	  case EDV_BROWSER_OP_SHOW_MOUNT_BAR:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_MOUNT_BAR
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_MOUNT_BAR, state
		);
		EDVReconfiguredEmit(core);
	    }
	    break;
	  case EDV_BROWSER_OP_SHOW_FIND_BAR:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_FIND_BAR
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_FIND_BAR, state
		);
		EDVReconfiguredEmit(core);
	    }
	    break;
	  case EDV_BROWSER_OP_SHOW_STATUS_BAR:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_STATUS_BAR
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_STATUS_BAR, state
		);
		EDVReconfiguredEmit(core);
	    }
	    break;

	  case EDV_BROWSER_OP_CONTENTS_LIST_FILTER:
	    EDVBrowserContentsFilter(browser);
	    break;

	  case EDV_BROWSER_OP_SHOW_HIDDEN_OBJECTS:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_HIDDEN
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_HIDDEN, state
		);
		EDVBrowserOPRefresh(browser);
	    }
	    break;
	  case EDV_BROWSER_OP_SHOW_NOACCESS_OBJECTS:
	    if(core != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_NOACCESS
		);
		EDV_SET_B(
		    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_NOACCESS, state
		);
		EDVBrowserOPRefresh(browser);
	    }
	    break;
	  case EDV_BROWSER_OP_DIRECTORY_TREE_ORIGIN:
	    EDVBrowserOPDirectoryTreeOrigin(browser);
	    break;

	  case EDV_BROWSER_OP_MOUNT:
	    EDVBrowserOPMount(browser);
	    break;
	  case EDV_BROWSER_OP_EJECT:
	    EDVBrowserOPEject(browser);
	    break;
	  case EDV_BROWSER_OP_DEVICE_CHECK:
	    EDVRunDeviceCheck(core, dev, toplevel);
	    break;
	  case EDV_BROWSER_OP_DEVICE_TOOLS:
	    EDVRunDeviceTools(core, dev, toplevel);
	    break;
	  case EDV_BROWSER_OP_DEVICE_FORMAT:
	    EDVRunDeviceFormat(core, dev, toplevel);
	    break;

	  case EDV_BROWSER_OP_MIME_TYPES:
	    EDVBrowserMIMETypes(browser);
	    break;
	  case EDV_BROWSER_OP_DEVICES:
	    EDVMapDevicesListWin(core, toplevel);
	    break;

	  case EDV_BROWSER_OP_NEW_BROWSER:
	    EDVNewBrowser(core, NULL);
	    break;
	  case EDV_BROWSER_OP_NEW_IMBR:
	    EDVNewImbr(core, NULL);
	    break;
	  case EDV_BROWSER_OP_NEW_ARCHIVER:
	    EDVNewArchiver(core, NULL, NULL);
	    break;
	  case EDV_BROWSER_OP_RECYCLE_BIN:
	    EDVMapRecBin(core);
	    break;

	  case EDV_BROWSER_OP_OPTIONS:
	    EDVMapOptionsWin(core, toplevel);
	    break;
	  case EDV_BROWSER_OP_CUSTOMIZE:
	    EDVMapCustomizeWin(core, toplevel);
	    break;

	  case EDV_BROWSER_OP_HELP_CONTENTS:
	    EDVHelp(core, "Contents", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_FILE_BROWSER:
	    EDVHelp(core, "File Browser", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_IMAGE_BROWSER:
	    EDVHelp(core, "Image Browser", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_ARCHIVER:
	    EDVHelp(core, "Archiver", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_RECYCLE_BIN:
	    EDVHelp(core, "Recycle Bin", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_KEYS_LIST:
	    EDVHelp(core, "Keys List", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_MIME_TYPES:
	    EDVHelp(core, "MIME Types", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_DEVICES:
	    EDVHelp(core, "Devices", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_COMMON_OPERATIONS:
	    EDVHelp(core, "Common Operations", toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_ABOUT:
	    EDVAbout(core, toplevel);
	    break;
	}

	browser->freeze_count--;
}

/*
 *	Operation ID enter notify callback nexus.
 *
 *	The data must be a edv_browser_opid_struct *.
 */
void EDVBrowserOPEnterCB(
	toolbar_item_struct *item, gint id, gpointer data  
)
{
	const gchar *tooltip;
	edv_browser_opid_struct *opid = EDV_BROWSER_OPID(data);
	edv_browser_struct *browser = (opid != NULL) ? opid->browser : NULL;
	if(browser == NULL)
	    return;

	tooltip = opid->tooltip;
	if(!STRISEMPTY(tooltip))
	    EDVStatusBarMessage(browser->status_bar, tooltip, FALSE);
}

/*
 *	Operation ID leave notify callback nexus.
 */
void EDVBrowserOPLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data
)
{
	edv_browser_opid_struct *opid = EDV_BROWSER_OPID(data);
	edv_browser_struct *browser = (opid != NULL) ? opid->browser : NULL;
	if(browser == NULL)
	    return;

	EDVStatusBarMessage(browser->status_bar, NULL, FALSE);
}


/*
 *	Sync Disks.
 */
void EDVBrowserOPSyncDisks(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVStatusBarMessage(
	    browser->status_bar,
	    "Syncing disks...",
	    TRUE
	);

	EDVBrowserSetBusy(browser, TRUE);

	EDVSyncDisks(browser->core);

	EDVBrowserSetBusy(browser, FALSE);

	EDVStatusBarMessage(
	    browser->status_bar,
	    "Disk sync done",
	    FALSE
	);
	EDVStatusBarProgress(browser->status_bar, 0.0f, FALSE);
}

/*
 *	Run.
 */
void EDVBrowserOPRun(edv_browser_struct *browser)
{
	gchar *cmd;
	GList *glist;
	GtkCList *clist;
	const edv_object_struct *obj;
	edv_core_struct *core;


	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core = browser->core;
	if((clist == NULL) || (core == NULL))
	    return;

	/* Format command to contain the list of selected objects */
	cmd = STRDUP("");
	for(glist = clist->selection; glist != NULL; glist = g_list_next(glist))
	{
	    obj = EDV_OBJECT(gtk_clist_get_row_data(
		clist, (gint)glist->data
	    ));
	    if((obj != NULL) ? !STRISEMPTY(obj->full_path) : FALSE)
	    {
		gchar *s = g_strconcat(cmd, obj->full_path, NULL);
		g_free(cmd);
		cmd = s;

		if(g_list_next(glist) != NULL)
		{
		    s = g_strconcat(cmd, " ", NULL);
		    g_free(cmd);
		    cmd = s;
		}
	    }
	}

	EDVMapRunDialogCommand(
	    core,
	    cmd,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	g_free(cmd);
}

/*
 *	Write Protect.
 */
void EDVBrowserOPWriteProtect(edv_browser_struct *browser)
{
	gboolean write_protect;
	cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	/* Get current write protect state */
	write_protect = EDV_GET_B(EDV_CFG_PARM_WRITE_PROTECT);

	/* Toggle write protect */
	write_protect = !write_protect;

	/* Set new write protect state */
	CFGItemListSetValueI(
	    cfg_list, EDV_CFG_PARM_WRITE_PROTECT,
	    write_protect, FALSE
	);

	/* Emit write protect changed signal */
	EDVWriteProtectChangedEmit(core, write_protect);
}

/*
 *	Delete Method: Recycle.
 */
void EDVBrowserOPDeleteMethodRecycle(edv_browser_struct *browser)
{
	cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	EDV_SET_I(
	    EDV_CFG_PARM_DELETE_METHOD,
	    EDV_DELETE_METHOD_RECYCLE
	);
	EDVReconfiguredEmit(core);
}

/*
 *	Delete Method: Purge.
 */
void EDVBrowserOPDeleteMethodPurge(edv_browser_struct *browser)
{
	cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

	EDV_SET_I(
	    EDV_CFG_PARM_DELETE_METHOD,
	    EDV_DELETE_METHOD_PURGE
	);
	EDVReconfiguredEmit(core);
}


/*
 *	Close.
 */
void EDVBrowserOPClose(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	if(browser->processing)
	    return;

	EDVBrowserSyncConfiguration(browser);
	EDVBrowserUnmap(browser);
}

/*
 *	Exit.
 */
void EDVBrowserOPExit(edv_browser_struct *browser)
{
	edv_core_struct *core;


	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	EDVBrowserSyncConfiguration(browser);
	EDVBrowserUnmap(browser);

	/* Schedual a new pending operation on the core to close all
	 * the windows
	 */
	core->pending_flags |= EDV_CORE_PENDING_CLOSE_ALL_WINDOWS;
}


/*
 *	New Object Nexus.
 */
static void EDVBrowserOPNewObjectNexus(
	edv_browser_struct *browser,
	edv_object_type type, GtkWidget *toplevel
)
{
	gboolean yes_to_all = FALSE;
	gint status;
	const char *obj_type_name;
	gchar *new_path = NULL;
	const gchar *cur_path, *error_msg;
	edv_core_struct *core = browser->core;
	if(core == NULL)
	    return;

#define DO_FREE_LOCALS	{	\
 g_free(new_path);		\
 new_path = NULL;		\
}

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, toplevel))
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Get current location as the path to create the new object
	 * at
	 */
	cur_path = EDVBrowserCurrentLocation(browser);
	if(cur_path == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

	EDVBrowserSetBusy(browser, TRUE);

	/* Create new object by type */
	status = -1;
	obj_type_name = NULL;
	switch(type)
	{
	  case EDV_OBJECT_TYPE_UNKNOWN:
	  case EDV_OBJECT_TYPE_ERROR:
	  case EDV_OBJECT_TYPE_FILE:
	    obj_type_name = "file";
	    status = EDVObjCreateFile(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DIRECTORY:
	    obj_type_name = "directory";
	    status = EDVObjCreateDirectory(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_LINK:
	    obj_type_name = "symbolic link";
	    status = EDVObjCreateLink(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_BLOCK:
	    obj_type_name = "block device";
	    status = EDVObjCreateDeviceBlock(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
	    obj_type_name = "character device";
	    status = EDVObjCreateDeviceCharacter(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_FIFO:
	    obj_type_name = "fifo pipe";
	    status = EDVObjCreateFifo(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_SOCKET:
	    obj_type_name = "socket";
	    status = EDVObjCreateSocket(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	}

	/* Unmap progress dialog since it may have been mapped in
	 * the above operation
	 */
	ProgressDialogBreakQuery(TRUE);
	ProgressDialogSetTransientFor(NULL);

	/* Error creating new object? */
	if(status)
	{
	    error_msg = EDVObjCreateGetError();
	    if(!STRISEMPTY(error_msg))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    "Create Failed",
		    error_msg,
		    NULL,
		    browser->toplevel
		);
	    }

	    EDVStatusBarMessage(browser->status_bar, NULL, FALSE);
	}
	else
	{
	    /* Successfully created new object */
	    gchar *buf;
	    struct stat lstat_buf;

	    /* Get local stats of new object */
	    if(!lstat(new_path, &lstat_buf))
	    {
		gint row;
		GtkCList *clist = (GtkCList *)browser->contents_clist;

		/* Emit a disk object added signal to all of endeavour's
		 * resources
		 */
		EDVObjectAddedEmit(
		    core, new_path, &lstat_buf
		);

		/* Select new row on contents clist that is listing the
		 * new object.
		 */
		row = EDVBrowserContentsFindRowByPath(browser, new_path);
		if((row > -1) && (clist != NULL))
		{
		    gtk_clist_freeze(clist);
		    gtk_clist_unselect_all(clist);
		    gtk_clist_select_row(clist, row, 0);
		    gtk_clist_thaw(clist);
		}
	    }

	    buf = g_strdup_printf(
		"Created new %s",
		obj_type_name
	    );
	    EDVStatusBarMessage(browser->status_bar, buf, FALSE);
	    g_free(buf);
	}

	EDVBrowserSetBusy(browser, FALSE);

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

/*
 *	New Object.
 */
void EDVBrowserOPNewObject(
	edv_browser_struct *browser, toolbar_item_struct *item
)
{
	if(browser == NULL)
	    return;

	EDVMenuButtonMapMenu(
	    browser->new_object_menu,
	    (item != NULL) ? item->w : NULL
	);
}

/*
 *	New File.
 */
void EDVBrowserOPNewFile(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_FILE, browser->toplevel
	);
}

/*
 *	New Directory.
 */
void EDVBrowserOPNewDirectory(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_DIRECTORY, browser->toplevel  
	);
}

/*
 *	New Link.
 */
void EDVBrowserOPNewLink(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_LINK, browser->toplevel  
	);
}

/*
 *	New FIFO Pipe.
 */
void EDVBrowserOPNewFifo(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_FIFO, browser->toplevel  
	);
}

/*
 *	New Block Device.
 */
void EDVBrowserOPNewDeviceBlock(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_DEVICE_BLOCK, browser->toplevel  
	);
}

/*
 *	New Character Device.
 */
void EDVBrowserOPNewDeviceCharacter(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_DEVICE_CHARACTER, browser->toplevel  
	);
}

/*
 *	New Socket.
 */
void EDVBrowserOPNewSocket(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserOPNewObjectNexus(
	    browser, EDV_OBJECT_TYPE_SOCKET, browser->toplevel  
	);
}


/*
 *	Open.
 */
void EDVBrowserOPOpen(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVBrowserContentsOpen(
	    browser,
	    -1, -1,		/* Use selected object(s) */
	    0
	);
}

/*
 *	Open To.
 */
void EDVBrowserOPOpenTo(edv_browser_struct *browser, toolbar_item_struct *item)
{
	if(browser == NULL)
	    return;

	EDVMenuButtonMapMenu(
	    browser->open_to_menu,
	    (item != NULL) ? item->w : NULL
	);
}

/*
 *	Open With.
 */
void EDVBrowserOPOpenWith(edv_browser_struct *browser)
{
	GtkCList *clist;

	if(browser == NULL)
	    return;

	/* Is there an object selected on the Contents List? */
	clist = GTK_CLIST(browser->contents_clist);
	if(clist->selection != NULL)
	{
	    EDVBrowserContentsOpenWith(
		browser,
		-1, -1		/* Use selected object(s) */
	    );
	}
	else
	{
	    /* Since no object was selected on the Contents List then
	     * check if an object is selected on the Directory Tree
	     */
	    GtkCTree *ctree = GTK_CTREE(browser->directory_ctree);
	    GtkCTreeNode *node = EDVCTreeGetSelectedLast(
		ctree, NULL
	    );
	    if(node != NULL)
		EDVBrowserDirTreeDoOpenWithObject(browser, node);
	}
}


/*
 *	Copy Path To DDE.
 */
void EDVBrowserOPCopyPath(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;

	if(browser == NULL)
	    return;

	EDVBrowserSyncData(browser);

	/* Get list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Copy the paths of the selected objects to the dde */
	EDVCopyDiskObjectsToDDEPath(
	    browser->core,
	    sel_object, total_sel_objects,
	    browser->toplevel
	);

	/* Delete selected objects pointer array only */
	g_free(sel_object);
}

/*
 *	Copy Location To DDE.
 */
void EDVBrowserOPCopyURL(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;

	if(browser == NULL)
	    return;

	EDVBrowserSyncData(browser);

	/* Get list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Copy the urls of the selected objects to the dde */
	EDVCopyDiskObjectsToDDEURL(
	    browser->core,
	    sel_object, total_sel_objects,
	    browser->toplevel
	);

	/* Delete selected objects pointer array only */
	g_free(sel_object);
}

/*
 *	Paste.
 */
void EDVBrowserOPPaste(edv_browser_struct *browser)
{
	gboolean yes_to_all = FALSE;
	gint status = -1;
	const gchar *protocol;
	gchar *buf;
	GList *glist, *url_list = NULL;
	GtkWidget *toplevel;
	url_struct *url;
	edv_object_struct *obj = NULL;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, toplevel))
	    return;

	EDVBrowserSyncData(browser);

	/* Get string from clipboard */
	buf = GUIDDEGetString(
	    toplevel,
	    GDK_SELECTION_PRIMARY,
	    GDK_CURRENT_TIME
	);
	if(buf == NULL)
	    return;

	/* Get last selected object from the contents list only
	 * if it is a directory, otherwise get last selected object
	 * from the directory tree
	 */
	/* Contents clist was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    GtkCList *clist = GTK_CLIST(browser->contents_clist);
	    GList *glist = (clist != NULL) ? clist->selection_end : NULL;
	    if(glist != NULL)
	    {
		obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, (gint)glist->data)
		);
		if((obj != NULL) ?
		    (obj->type != EDV_OBJECT_TYPE_DIRECTORY) : TRUE
		)
		    obj = NULL;
	    }
	}
	/* Directory ctree was last selected? */
	if(obj == NULL)
	{
	    GtkCTree *ctree = GTK_CTREE(browser->directory_ctree);
	    GtkCList *clist = GTK_CLIST(ctree);
	    GList *glist = (clist != NULL) ? clist->selection_end : NULL;
	    if(glist != NULL)
	    {
		GtkCTreeNode *node = (GtkCTreeNode *)glist->data;
		obj = EDV_OBJECT(
		    gtk_ctree_node_get_row_data(ctree, node)
		);
		if((obj != NULL) ?
		    (obj->type != EDV_OBJECT_TYPE_DIRECTORY) : TRUE
		)
		    obj = NULL;
	    }
	}
	/* No objects selected? */
	if(obj == NULL)
	{
	    g_free(buf);
	    return;
	}

	/* Decode URL list string into a list of URLs */
	url_list = URLDecodeString(buf);
	g_free(buf);
	buf = NULL;

	/* Make confirmation */
	if(url_list != NULL)
	{
	    url = URL(url_list->data);
	    if(EDVConfirmCopy(
		core, toplevel,
		(url != NULL) ? url->path : NULL, g_list_length(url_list),
		obj->full_path
	    ) != CDIALOG_RESPONSE_YES)
	    {
		g_list_foreach(url_list, (GFunc)URLDelete, NULL);
		g_list_free(url_list);
		return;
	    }
	}

	/* Paste each URL */
	for(glist = url_list; glist != NULL; glist = g_list_next(glist))
	{
	    url = URL(glist->data);
	    if(url == NULL)
		continue;

	    protocol = url->protocol;
	    if(STRISEMPTY(protocol))
		protocol = "file";

	    /* File? */
	    if(!g_strcasecmp(protocol, "file"))
	    {
		const gchar *error_msg;
		gchar *new_path = NULL;
		struct stat lstat_buf;
		status = EDVObjectOPCopy(
		    core,
		    url->path, obj->full_path,
		    &new_path, toplevel,
		    TRUE, TRUE, &yes_to_all
		);
		error_msg = EDVObjectOPGetError();
		if(!STRISEMPTY(error_msg) && (status != -4))
		{
		    EDVPlaySoundError(core);
		    EDVMessageObjectOPError(
			"Copy Object Error",
			error_msg,
			url->path,
			toplevel
		    );
		}

		/* Check if new_path is valid and that no error
		 * occured
		 *
		 * This implies the operation was successful
		 * and that we need to emit an object added signal
		 */
		if((!STRISEMPTY(new_path) && !status) ?
		    !lstat(new_path, &lstat_buf) : FALSE
		)
		    EDVObjectAddedEmit(
			core, new_path, &lstat_buf
		    );

		g_free(new_path);

		/* Skip handling of the rest of the objects on error
		 * (status != 0) and that the error was not a user
		 * response of no (status != -5)
		 */
		if(status && (status != -5))
		    break;
	    }
	    /* Download? */
	    else if(!g_strcasecmp(protocol, "http") ||
		    !g_strcasecmp(protocol, "ftp") ||
		    !g_strcasecmp(protocol, "https")
	    )
	    {
		EDVInternetDownloadObject(
		    core, url, obj->full_path, toplevel
		);
	    }
	}

	/* Unmap progress dialog, it may have been mapped if any
	 * operations occured in the above loop
	 */
	ProgressDialogBreakQuery(TRUE);
	ProgressDialogSetTransientFor(NULL);

	/* Play completed sound on success */
	if(status == 0)
	    EDVPlaySoundCompleted(core);

	g_list_foreach(url_list, (GFunc)URLDelete, NULL);
	g_list_free(url_list);
}


/*
 *	Returns a list of selected objects on the given browser.
 *
 *	Only the returned pointer array (not each object) needs to be
 *	deleted by the calling function.
 */
static edv_object_struct **EDVBrowserGetSelectedObjects(
	edv_browser_struct *browser, gint *total
)
{
	edv_object_struct **sel_object = NULL, *obj;
	gint total_sel_objects = 0;


	if(total != NULL)
	    *total = 0;

	if(browser == NULL)
	    return(sel_object);

	/* Create a list of disk objects, each pointer in the array
	 * will reffer to a shared object structure pointer so it
	 * should not be deleted
	 */

	/* Check which list contains the last selected object, that
	 * will indicate the list to get selected objects from
	 */

	/* Contents clist was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    gint i, row;
	    GList *glist;
	    GtkCList *clist = (GtkCList *)browser->contents_clist;

	    glist = (clist != NULL) ? clist->selection : NULL;
	    while(glist != NULL)
	    {
		row = (gint)glist->data;
		obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, row)
		);
		if(obj != NULL)
		{
		    i = total_sel_objects;
		    total_sel_objects = i + 1;
		    sel_object = (edv_object_struct **)g_realloc(
			sel_object,
			total_sel_objects * sizeof(edv_object_struct *)
		    );
		    if(sel_object == NULL)
		    {
			total_sel_objects = 0;
			break;
		    }

		    sel_object[i] = obj;
		}

		glist = g_list_next(glist);
	    }
	}
	/* Directory ctree was last selected? */
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    gint i;
	    GList *glist;
	    GtkCList *clist = (GtkCList *)browser->directory_ctree;
	    GtkCTree *ctree = (GtkCTree *)clist;
	    GtkCTreeNode *node;

	    glist = (clist != NULL) ? clist->selection : NULL;
	    while(glist != NULL)
	    {
		node = (GtkCTreeNode *)glist->data;
		obj = EDV_OBJECT(
		    gtk_ctree_node_get_row_data(ctree, node)
		);
		if(obj != NULL)
		{
		    i = total_sel_objects;
		    total_sel_objects = i + 1;
		    sel_object = (edv_object_struct **)g_realloc(
			sel_object,
			total_sel_objects * sizeof(edv_object_struct *)
		    );
		    if(sel_object == NULL)
		    {
			total_sel_objects = 0;
			break;
		    }

		    sel_object[i] = obj;
		}

		glist = g_list_next(glist);
	    }
	}

	/* Update returns */
	if(total != NULL)
	    *total = total_sel_objects;

	return(sel_object);
}

/*
 *	Move.
 */
void EDVBrowserOPMove(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;


	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to move */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_MOVE,
	    sel_object, total_sel_objects,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete selected objects pointer array but not each object    
	 * since they are shared 
	 */
	g_free(sel_object);
}

/*
 *	Copy.
 */
void EDVBrowserOPCopy(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to copy */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_COPY,
	    sel_object, total_sel_objects,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete the list of selected objects */
	g_free(sel_object);
}

/*
 *	Link.
 */
void EDVBrowserOPLink(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to link
	 *
	 * Note that linking only allows one selected object, so always
	 * report total as no more than 1
	 */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_LINK,
	    sel_object, MIN(total_sel_objects, 1),
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete the list of selected objects */
	g_free(sel_object);
}

/*
 *	Rename.
 */
void EDVBrowserOPRename(edv_browser_struct *browser)
{
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

/* This will actually be checked again when the rename fprompt map call
 * but we check it first initially anyways
 */
	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Check which list contains the last selected object, that will
	 * indicate the list to map the rename floating prompt for
	 */

	/* Contents clist was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    gint row;
	    GtkCList *clist = (GtkCList *)browser->contents_clist;

	    row = EDVCListGetSelectedLast(clist, NULL);
	    if(row > -1)
		EDVBrowserContentsDoFPromptRename(
		    browser, row, -1
		);
	}
	/* Directory ctree was last selected? */
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    GtkCTreeNode *node;
	    GtkCTree *ctree = (GtkCTree *)browser->directory_ctree;

	    node = EDVCTreeGetSelectedLast(ctree, NULL);
	    if(node != NULL)
		EDVBrowserDirTreeDoFPromptRename(
		    browser, node
		);
	}
}

/*
 *	Change Permissions.
 */
void EDVBrowserOPChMod(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to change permissions */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHMOD,
	    sel_object, total_sel_objects,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete the list of selected objects */
	g_free(sel_object);
}

/*
 *	Change Ownership.
 */
void EDVBrowserOPChOwn(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to change ownership */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHOWN,
	    sel_object, total_sel_objects,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete the list of selected objects */
	g_free(sel_object);
}


/*
 *	Set Time Stamps.
 */
void EDVBrowserOPChTime(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, browser->toplevel))
	    return;

	/* Get the object operations dialog and create it as needed */
	d = browser->obj_op_dlg;
	if(d == NULL)
	    browser->obj_op_dlg = d = EDVObjOpDlgNew(core);
	if(d == NULL)
	    return;

	/* Sync data to ensure current values to operate on */
	EDVBrowserSyncData(browser);

	/* Get the list of selected objects */
	sel_object = EDVBrowserGetSelectedObjects(
	    browser, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

	/* Map the object operations dialog to set time stamps */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHTIME,
	    sel_object, total_sel_objects,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);

	/* Delete the list of selected objects */
	g_free(sel_object);
}

/*
 *	Delete.
 */
void EDVBrowserOPDelete(edv_browser_struct *browser)
{
	struct stat lstat_buf;
	gboolean yes_to_all = FALSE;
	gint status, npaths, objects_deleted = 0;
	const gchar *path, *error_msg;
	GList *glist, *paths_list = NULL, *index_list;
	GtkWidget *toplevel;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

#define DO_FREE_LOCALS	{			\
 if(paths_list != NULL)	{			\
  g_list_foreach(				\
   paths_list, (GFunc)g_free, NULL		\
  );						\
  g_list_free(paths_list);			\
 }						\
}

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, toplevel))
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Contents List was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    GtkCList *clist = GTK_CLIST(browser->contents_clist);
	    gint row;
	    edv_object_struct *obj;

	    for(glist = clist->selection;
		glist != NULL;
		glist = g_list_next(glist)
	    )
	    {
		row = (gint)glist->data;
		obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, row)
		);
		if((obj != NULL) ? (obj->full_path != NULL) : FALSE)
		    paths_list = g_list_append(
			paths_list,
			STRDUP(obj->full_path)
		    );
	    }
	}
	/* Directory Tree was last selected? */
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    GtkCTree *ctree = GTK_CTREE(browser->directory_ctree);
	    GtkCList *clist = GTK_CLIST(ctree);
	    GtkCTreeNode *node;
	    edv_object_struct *obj;

	    for(glist = clist->selection;
		glist != NULL;
		glist = g_list_next(glist)
	    )
	    {
		node = (GtkCTreeNode *)glist->data;
		obj = EDV_OBJECT(
		    gtk_ctree_node_get_row_data(ctree, node)
		);
		if((obj != NULL) ? (obj->full_path != NULL) : FALSE)
		    paths_list = g_list_append(
			paths_list,
			STRDUP(obj->full_path)
		    );
	    }
	}

	/* No objects selected? */
	if(paths_list == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

	npaths = g_list_length(paths_list);

	/* Confirm delete */
	status = EDVConfirmDelete(
	    core, toplevel,
	    (npaths == 1) ? (const gchar *)paths_list->data : NULL,
	    npaths
	);
	switch(status)
	{
	  case CDIALOG_RESPONSE_YES_TO_ALL:
	    yes_to_all = TRUE;
	  case CDIALOG_RESPONSE_YES:
	  case CDIALOG_RESPONSE_OK:
	    break;

	  default:
	    DO_FREE_LOCALS
	    return;
	    break;
	}


	EDVBrowserSetBusy(browser, TRUE);


	/* Iterate through the list of object paths */
	status = 0;
	for(glist = paths_list;
	    glist != NULL;
	    glist = g_list_next(glist)
	)
	{
	    path = (const gchar *)glist->data;
	    if(path == NULL)
		continue;

	    /* Delete the object */
	    index_list = NULL;
	    status = EDVRecBinDeleteObject(
		core, path,
		&index_list,
		toplevel,
		TRUE, TRUE, &yes_to_all
	    );

	    /* Check for and report any errors */
	    error_msg = EDVRecBinDeleteObjectGetError();
	    if(!STRISEMPTY(error_msg))
	    {
		EDVPlaySoundError(core);
		EDVMessageObjectOPError(
		    "Delete Object Error",
		    error_msg,
		    path,
		    toplevel
		);
	    }

	    /* Check if the object still exists */
	    if(lstat(path, &lstat_buf))
	    {
#ifdef ENOENT
		/* Report the object being removed */
		const gint error_code = (gint)errno;
		if(error_code == ENOENT)
		    EDVObjectRemovedEmit(core, path);
#endif
	    }

	    /* Any objects deleted? */
	    if(index_list != NULL)
	    {
		guint index;
		GList *glist;

		for(glist = index_list; glist != NULL; glist = g_list_next(glist))
		{
		    index = (guint)glist->data;
		    if(index != 0)
			EDVRecycledObjectAddedEmit(core, index);

		    objects_deleted++;
		}
	    }

	    /* Delete the recycle objects index list */
	    g_list_free(index_list);

	    /* User aborted? */
	    if(status == -4)
		break;
	}

	/* Update status bar */
	if(TRUE)
	{
	    gchar *s;
	    if(status == -4)
		s = STRDUP(
		    "Delete operation canceled"
		);
	    else if(objects_deleted > 0)
		s = g_strdup_printf(
		    "Deleted %i %s",
		    objects_deleted,
		    (objects_deleted == 1) ? "object" : "objects"
		);
	    else
		s = g_strdup_printf(
		    "Unable to delete %s",
		    (npaths == 1) ? "object" : "objects"
		);
	    EDVStatusBarMessage(browser->status_bar, s, FALSE);
	    g_free(s);
	}

	EDVBrowserSetBusy(browser, FALSE);

	/* Unmap progress dialog, it may have been mapped if any
	 * operations occured in the above loop
	 */
	ProgressDialogBreakQuery(TRUE);
	ProgressDialogSetTransientFor(NULL);

	/* Play completed sound on success */
	if(status == 0)
	    EDVPlaySoundCompleted(core);

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

/*
 *	Properties.
 */
void EDVBrowserOPProperties(edv_browser_struct *browser)
{
	edv_object_struct *obj = NULL;

	if(browser == NULL)
	    return;

	EDVBrowserSyncData(browser);

	/* Get object from last selected directory tree item or
	 * contents list item
	 */
	/* Contents clist was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    GtkCList *clist = GTK_CLIST(browser->contents_clist);
	    GList *glist = (clist != NULL) ? clist->selection_end : NULL;
	    if(glist != NULL)
		obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, (gint)glist->data)
		);
	}
	/* Directory ctree was last selected? */
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    GtkCTree *ctree = GTK_CTREE(browser->directory_ctree);
	    GtkCList *clist = GTK_CLIST(ctree);
	    GList *glist = (clist != NULL) ? clist->selection_end : NULL;
	    if(glist != NULL)
	    {
		GtkCTreeNode *node = (GtkCTreeNode *)glist->data;
		obj = EDV_OBJECT(
		    gtk_ctree_node_get_row_data(ctree, node)
		);
	    }
	}
	/* No objects selected? */
	if(obj == NULL)
	    return;

	/* Create a new Properties Dialog */
	EDVNewPropertiesDialog(
	    browser->core,
	    obj,
	    browser->toplevel
	);
}


/*
 *	Select All.
 */
void EDVBrowserOPSelectAll(edv_browser_struct *browser)
{
	edv_core_struct *core;
	GtkCList *clist;

	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core = browser->core;
	if((clist == NULL) || (core == NULL))
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Select all rows on clist */
	gtk_clist_freeze(clist);
	gtk_clist_select_all(clist);
	gtk_clist_thaw(clist);

	/* Mark node as unselected on directory ctree */
	browser->directory_ctree_selected_node = NULL;

	/* Assume highest row index as the last selected row */
	browser->contents_clist_selected_row = clist->rows - 1;

	EDVStatusBarMessage(
	    browser->status_bar, "All objects selected", FALSE
	);

	EDVBrowserSetBusy(browser, FALSE);
}

/*
 *	Unselect All.
 */
void EDVBrowserOPUnselectAll(edv_browser_struct *browser)
{
	edv_core_struct *core;
	GtkCList *clist;

	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core = browser->core;
	if((clist == NULL) || (core == NULL))
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Unselect all rows on clist */
	gtk_clist_freeze(clist);
	gtk_clist_unselect_all(clist);
	gtk_clist_thaw(clist);

	/* Mark node as unselected on directory ctree */
	browser->directory_ctree_selected_node = NULL;

	/* Mark contents clist's row as unselected */
	browser->contents_clist_selected_row = -1;

	EDVStatusBarMessage(
	    browser->status_bar, "All objects unselected", FALSE
	);

	EDVBrowserSetBusy(browser, FALSE);
}

/*
 *	Invert Selection.
 */
void EDVBrowserOPInvertSelection(edv_browser_struct *browser)
{
	edv_core_struct *core;
	GtkCList *clist;
	GList *glist, *selection;
	gint row, total_rows;

	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core = browser->core;
	if((clist == NULL) || (core == NULL))
	    return;

	EDVBrowserSetBusy(browser, TRUE);
	gtk_clist_freeze(clist);

	/* Get copy of selected rows list from clist */
	selection = (clist->selection != NULL) ?
	    g_list_copy(clist->selection) : NULL;

	for(row = 0, total_rows = clist->rows;
	    row < total_rows;
	    row++
	)
	{
	    for(glist = selection;
	        glist != NULL;
	        glist = g_list_next(glist)
	    )
	    {
		if(row == (gint)glist->data)
		{
		    gtk_clist_unselect_row(clist, row, 0);
		    break;
		}
	    }
	    /* Row not selected? */
	    if(glist == NULL)
		gtk_clist_select_row(clist, row, 0);
	}

	g_list_free(selection);

	gtk_clist_thaw(clist);
	EDVStatusBarMessage(
	    browser->status_bar, "Selection inverted", FALSE
	);
	EDVBrowserSetBusy(browser, FALSE);
}


/*
 *	Download.
 */
void EDVBrowserOPDownload(edv_browser_struct *browser)
{
	if(browser == NULL)
	    return;

	EDVInternetDownloadObject(
	    browser->core,
	    NULL,
	    EDVBrowserCurrentLocation(browser),
	    browser->toplevel
	);
}


/*
 *	Refresh.
 */
void EDVBrowserOPRefresh(edv_browser_struct *browser)
{
	gchar *cur_path;
	GtkWidget *w, *toplevel;
	GtkCList *clist;
	GtkCTree *ctree;
	GtkCTreeNode *node;
	GtkCTreeRow *row_ptr;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;

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

	cur_path = STRDUP(EDVBrowserCurrentLocation(browser));

	/* Refresh toplevel */
	w = toplevel;
	if(w != NULL)
	    gtk_widget_queue_resize(w);

	/* Update the Directory Tree */
	clist = (GtkCList *)browser->directory_ctree;
	ctree = (GtkCTree *)clist;

	/* Get selected node */
	node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	if((clist != NULL) && (node != NULL) && (row_ptr != NULL))
	{
	    gboolean was_expanded = row_ptr->expanded;

	    /* Record last scroll position */
	    gfloat last_x = GTK_ADJUSTMENT_GET_VALUE(clist->hadjustment),
		   last_y = GTK_ADJUSTMENT_GET_VALUE(clist->vadjustment);

	    /* Get listing */
	    EDVBrowserDirTreeGetListing(browser, node, TRUE);

	    /* Reget selected node */
	    node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	    if(node != NULL)
	    {
		if(was_expanded)
		    gtk_ctree_expand(ctree, node);
	    }

	    /* Scroll back to original position */
	    EDVScrollCListToPosition(clist, last_x, last_y);
	}

	/* Update contents clist */
	clist = (GtkCList *)browser->contents_clist;
	if((clist != NULL) && (cur_path != NULL))
	{
	    /* Record last scroll position */
	    gfloat last_x = GTK_ADJUSTMENT_GET_VALUE(clist->hadjustment),
		   last_y = GTK_ADJUSTMENT_GET_VALUE(clist->vadjustment);

	    /* Reget listing */
	    EDVBrowserContentsGetListing(
		browser,
		cur_path,
		EDV_GET_B(EDV_CFG_PARM_LISTS_ANIMATE_UPDATES)
	    );

	    /* Scroll back to original position */
	    EDVScrollCListToPosition(clist, last_x, last_y);
	}

	EDVBrowserUpdateMenus(browser);
	EDVStatusBarMessage(
	    browser->status_bar, "Refreshed contents listing", FALSE
	);

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

	g_free(cur_path);
}

/*
 *	Refresh All.
 */
void EDVBrowserOPRefreshAll(edv_browser_struct *browser)
{
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	core = browser->core;
	if(core == NULL)
	    return;

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

	/* Refresh File Browser */
	EDVBrowserOPRefresh(browser);
}

/*
 *	Go To Parent.
 */
void EDVBrowserOPGoToParent(edv_browser_struct *browser)
{
	const gchar *path;
	GtkWidget *toplevel;
	GtkCTreeNode *node;
	GtkCTreeRow *row_ptr;
	edv_object_struct *obj;
	edv_core_struct *core;
	GtkCTree *ctree = (GtkCTree *)((browser != NULL) ?
	    browser->directory_ctree : NULL  
	);
	if(ctree == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;

	/* Get the selected node */
	node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	if((node == NULL) || (row_ptr == NULL))
	{
	    /* No node selected */
	    EDVPlaySoundBeep(core);
	    return;
	}

	/* Get the selected node's Object */
	obj = EDV_OBJECT(
	    gtk_ctree_node_get_row_data(ctree, node)
	);
	path = (obj != NULL) ? obj->full_path : NULL;
	if(STRISEMPTY(path))
	    return;

	/* Already at toplevel? */
	if(!strcmp(path, "/"))
	{
	    EDVPlaySoundBeep(core);
	    return;
	}

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

	/* Get the parent node
	 *
	 * If the node does not have a parent then it implies that
	 * we need to update the Directory Tree's Origin path
	 */
	node = row_ptr->parent;
	if(node != NULL)
	{
	    /* Select the parent node */
	    gtk_ctree_select(ctree, row_ptr->parent);
	}
	else
	{
	    gchar *parent_path = g_dirname(path);
	    if(parent_path != NULL)
	    {
		/* Update the Directory Tree Origin to be the parent
		 * and select the parent
		 */
		EDVBrowserDirTreeSetOriginPath(browser, parent_path);
		g_free(parent_path);
	    }
	}

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

/*
 *	Go To Home.
 */
void EDVBrowserOPGoToHome(edv_browser_struct *browser)
{
	gchar *path;
	GtkWidget *toplevel;
	edv_core_struct *core;
	GtkCTree *ctree = (GtkCTree *)((browser != NULL) ?
	    browser->directory_ctree : NULL  
	);
	if(ctree == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	/* Get home directory */
	path = STRDUP(core->home_dir);
	if(STRISEMPTY(path))
	{
	    EDVPlaySoundWarning(core);
	    EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Vaya A En Casa Guía Fallada",
"Incapaz de encontrar la guía de hogar, cerciórese que el\n\
ambiente HOME variable se pone.\n",
		NULL,
#elif defined(PROG_LANGUAGE_FRENCH)
"Aller A L'Annuaire De Maison Echoué",
"Incapable de trouver l'annuaire de maison, s'assure que\n\
l'environnement HOME variable est réglé.\n",
		NULL,
#elif defined(PROG_LANGUAGE_GERMAN)
"Gehen Sie Zu Heim Verzeichnis Hat Versagt",
"Unfähig, das heim verzeichnis zu finden, vergewissert\n\
sich, daß die umwelt veränderliche HOME gesetzt ist.\n",
		NULL,
#elif defined(PROG_LANGUAGE_ITALIAN)
"Andare All'Elenco Di Casa Fallito",
"Incapace per trovare l'elenco di casa, si assicura che\n\
l'ambiente HOME variabile è regolato.\n",
		NULL,
#elif defined(PROG_LANGUAGE_DUTCH)
"Ga Te Huis Gids Verzuimde",
"Onbekwame de huis gids te vinden, vergewist zich ervan dat de\n\
omgeving, die veranderlijke HOME gezet is.\n",
		NULL,
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Vá A Guia De Lar Fracassado",
"Incapaz de achar o guia de lar, assegura-se que o ambiente\n\
HOME variável é posto.\n",
		NULL,
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Dra Til Hjem Sviktet Katalog",
"Maktesløs finne hjemkatalogen, sjekker at miljøet variabel\n\
HOME setter.\n",
		NULL,
#else
"Go To Home Directory Failed",
"Unable to find the home directory, make sure that the\n\
environment variable HOME is set.\n",
		NULL,
#endif
		toplevel
	    );
	    g_free(path);
	    return;
	}

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

	/* Change the Directory Tree origin as needed */
	if(EDVBrowserIsPathFromDirTreeOrigin(browser, path))
	{
	    /* Select the home directory */
	    EDVBrowserDirTreeSelectPath(browser, path);
	}
	else
	{
	    /* Set the Directory Tree origin to the home directory
	     * and select the home directory
	     */
	    EDVBrowserDirTreeSetOriginPath(browser, path);
	}

	g_free(path);

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

/*
 *	Expands/Colapse.
 */
void EDVBrowserOPExpand(edv_browser_struct *browser)
{
	GtkCTree *ctree;
	GtkCTreeNode *node;
	GtkCTreeRow *row_ptr;

	if(browser == NULL)
	    return;

	ctree = (GtkCTree *)browser->directory_ctree;
	if(ctree == NULL)
	    return;

	/* Get selected node */
	node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	if((node == NULL) || (row_ptr == NULL))
	    return;

	/* Collapse or expand node? Note that appropriate callbacks
	 * will be made when expanded or collapsed.
	 */
	if(row_ptr->expanded)
	    gtk_ctree_collapse(ctree, node);
	else
	    gtk_ctree_expand(ctree, node);
}


/*
 *	Sets the contents list filter.
 */
void EDVBrowserContentsFilter(edv_browser_struct *browser)
{
	gchar **strv;
	gint strc;
	GtkWidget *toplevel;

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

	toplevel = browser->toplevel;
	EDVBrowserSetBusy(browser, TRUE);

	PDialogDeleteAllPrompts();
	PDialogSetTransientFor(toplevel);
	PDialogAddPrompt(NULL, "Filter:", browser->contents_list_filter);
	PDialogSetSize(320, -1);
	strv = PDialogGetResponseIconData(
	    "Set Filter",
	    NULL, NULL,
	    (guint8 **)icon_wildcards_32x32_xpm,
	    "Set", "Cancel",
	    PDIALOG_BTNFLAG_SUBMIT | PDIALOG_BTNFLAG_CANCEL,
	    PDIALOG_BTNFLAG_SUBMIT,
	    &strc
	);
	PDialogSetTransientFor(NULL);
	if((strv != NULL) && (strc > 0))
	{
	    if(strc > 0)
	    {
		g_free(browser->contents_list_filter);
		browser->contents_list_filter = STRDUP(strv[0]);
	    }

	    EDVBrowserOPRefresh(browser);
	}

	PDialogDeleteAllPrompts();

	EDVBrowserSetBusy(browser, FALSE);
}


/*
 *	Directory Tree Origin.
 */
void EDVBrowserOPDirectoryTreeOrigin(edv_browser_struct *browser)
{
	gboolean status;
	fb_type_struct **ftype = NULL, *ftype_rtn = NULL;
	gint nftypes = 0;
	gchar **path = NULL;
	gint npaths = 0;
	const gchar *new_origin_path = NULL;
	struct stat stat_buf;
	GtkWidget *toplevel;
	edv_core_struct *core;
	if((browser == NULL) || FileBrowserIsQuery())
	    return;

	toplevel = browser->toplevel;
	core = browser->core;

	/* Create new file types list */
	FileBrowserTypeListNew(
	    &ftype, &nftypes,
	    "*.*", "All Files"
	);
	  
	/* Query user for directory */
	FileBrowserSetTransientFor(toplevel);
	status = FileBrowserGetResponse(
	    "Set Directory Tree Origin",
	    "Set", "Cancel",
	    browser->directory_ctree_origin_path,
	    ftype, nftypes,
	    &path, &npaths,
	    &ftype_rtn
	); 
	FileBrowserSetTransientFor(NULL);

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


	/* Got new directory tree origin path and it exists and it leads
	 * to a directory?
	 */
	new_origin_path = status ?
	    ((npaths > 0) ? path[0] : NULL) : NULL;
	if(STRISEMPTY(new_origin_path))
	    return;
	if(!g_path_is_absolute(new_origin_path))
	{
	    gchar *buf = g_strdup_printf(
"Not an absolute path:\n\
\n\
    %s\n",
		new_origin_path
	    );
	    EDVPlaySoundWarning(core);
	    EDVMessageWarning(
		"Set Directory Tree Origin Failed",
		buf,
		NULL,
		toplevel
	    );
	    g_free(buf);
	    return;
	}
	if(stat(new_origin_path, &stat_buf))
	{
	    const gint error_code = (gint)errno;
	    gchar *buf, *s = STRDUP(g_strerror(error_code));
	    if(s == NULL)
		s = STRDUP("No such directory");
	    *s = toupper(*s);
	    buf = g_strdup_printf(
"%s:\n\
\n\
    %s\n",
		s, new_origin_path
	    );
	    EDVPlaySoundWarning(core);
	    EDVMessageWarning(
		"Set Directory Tree Origin Failed",
		buf,
		NULL,
		toplevel
	    );
	    g_free(buf);
	    g_free(s);
	    return;
	}
#ifdef S_ISDIR
	if(!S_ISDIR(stat_buf.st_mode))
#else
	if(TRUE)
#endif
	{
	    gchar *buf = g_strdup_printf(
"Object is not a directory:\n\
\n\
    %s\n",
		new_origin_path
	    );
	    EDVPlaySoundWarning(core);
	    EDVMessageWarning(
		"Set Directory Tree Origin Failed",
		buf,
		NULL,
		toplevel
	    );
	    g_free(buf);
	    return;
	}

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

	/* Set new Directory Tree Origin path */
	EDVBrowserDirTreeSetOriginPath(browser, new_origin_path);

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


/*
 *	Mount/Unmount.
 */
void EDVBrowserOPMount(edv_browser_struct *browser)
{
	gboolean original_mount_state;
	gint status, dev_num;
	GtkWidget *toplevel;
	edv_device_struct *dev;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	/* Get number of last selected device index (if any) */
	dev_num = browser->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core->total_devices))
	    dev = core->device[dev_num];
	else
	    dev = NULL;
	if(dev == NULL)
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = EDV_DEVICE_IS_MOUNTED(dev);

	/* Unmount or mount? */
	if(EDV_DEVICE_IS_MOUNTED(dev))
	    status = EDVDeviceUnmount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );
	else
	    status = EDVDeviceMount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );

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

	EDVBrowserSetBusy(browser, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVDeviceUnmountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    original_mount_state ?
			"Unmount Failed" : "Mount Failed",
		    last_error,
		    NULL,
		    browser->toplevel
		);
	    }
	}
	else
	{
	    /* Report un mount signal to all of endeavour's resources */
	    EDVObjectMountEmit(core, dev_num, dev, EDV_DEVICE_IS_MOUNTED(dev));
	}
}

/*
 *	Eject.
 */
void EDVBrowserOPEject(edv_browser_struct *browser)
{
	gboolean original_mount_state;
	gint status, dev_num;
	GtkWidget *toplevel;
	edv_device_struct *dev;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	/* Get number of last selected device index (if any) */
	dev_num = browser->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core->total_devices))
	    dev = core->device[dev_num];
	else
	    dev = NULL;
	if(dev == NULL)
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = EDV_DEVICE_IS_MOUNTED(dev);

	/* Need to unmount first? */
	if(EDV_DEVICE_IS_MOUNTED(dev))
	    status = EDVDeviceUnmount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );

	/* Eject */
	status = EDVDeviceEject(
	    core, dev,
	    TRUE, TRUE,
	    toplevel
	);

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

	EDVBrowserSetBusy(browser, FALSE);

	/* Eject error? */
	if(status)
	{
	    const gchar *last_error = EDVDeviceUnmountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    "Eject Failed",
		    last_error,
		    NULL,
		    browser->toplevel
		);
	    }
	}
	else
	{
	    /* Report eject (unmount) signal to all of endeavour's
	     * resources
	     */
	    EDVObjectMountEmit(core, dev_num, dev, EDV_DEVICE_IS_MOUNTED(dev));
	}
}

/*
 *      MIME Types.
 */
void EDVBrowserMIMETypes(edv_browser_struct *browser)
{
	gchar *type_str = NULL;
	GtkWidget *toplevel;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core = browser->core;
	if(core == NULL)
	    return;

	if(browser->contents_clist_selected_row > -1)
	{
	    GtkCList *clist = (GtkCList *)browser->contents_clist;
	    const gint i = EDVCListGetSelectedLast(clist, NULL);
	    if(i > -1)
	    {
		edv_object_struct *obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, i)
		);
		if(obj != NULL)
		    EDVMatchObjectTypeString(
			core->mimetype, core->total_mimetypes,
			obj->type,
			obj->permissions,
			obj->full_path,
			&type_str
		);
	    }
	}
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    GtkCTree *ctree = (GtkCTree *)browser->directory_ctree;
	    GtkCTreeNode *node = EDVCTreeGetSelectedLast(ctree, NULL);
	    if(node != NULL)
	    {
		edv_object_struct *obj = EDV_OBJECT(
		    gtk_ctree_node_get_row_data(ctree, node)
		);
		if(obj != NULL)
		    EDVMatchObjectTypeString(
			core->mimetype, core->total_mimetypes,
			obj->type,
			obj->permissions,
			obj->full_path,
			&type_str
		);
	    }
	}

	EDVMapMIMETypesListWin(core, type_str, toplevel);
}

