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

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

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

#include "edvtypes.h"
#include "cfg.h"
#include "edvobj.h"
#include "edvconfirm.h"
#include "edvrecbin.h"
#include "edvrecbinfio.h"
#include "edvrecbinfop.h"
#include "edvdevices.h"
#include "edvmount.h"
#include "fopdlg.h"
#include "browser.h"
#include "browsercb.h"
#include "browseropcb.h"
#include "browsercontents.h"
#include "browserdirtree.h"
#include "browsercontents.h"
#include "endeavour.h"
#include "edvfcreate.h"
#include "edvcb.h"
#include "edvhelp.h"
#include "edvop.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcfglist.h"
#include "config.h"


void EDVBrowserMenuItemCB(GtkWidget *widget, gpointer data);
gint EDVBrowserMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
gint EDVBrowserMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);

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 EDVBrowserMountBarMountCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);
void EDVBrowserMountBarEjectCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);
void EDVBrowserMountBarGoToCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);

gchar *EDVBrowserFindBarLocationCB(gpointer fb, gpointer data);
void EDVBrowserFindBarStartCB(gpointer fb, gpointer data);
void EDVBrowserFindBarEndCB(
	gpointer fb, gint total_matches, gpointer data
);
void EDVBrowserFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
);

void EDVBrowserBarStatusMessageCB(
	gpointer bar, const gchar *message, gpointer data
);
void EDVBrowserBarStatusProgressCB(
	gpointer bar, gfloat progress, gpointer data
);

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

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

static void EDVBrowserOPNewObjectNexus(
	edv_browser_struct *browser,
	edv_object_type type, GtkWidget *toplevel
);
void EDVBrowserOPNewObject(edv_browser_struct *browser);
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 EDVBrowserOPOpenWith(edv_browser_struct *browser);

void EDVBrowserOPGoToParent(edv_browser_struct *browser);
void EDVBrowserOPGoToHome(edv_browser_struct *browser);
void EDVBrowserOPExpand(edv_browser_struct *browser);
void EDVBrowserOPMount(edv_browser_struct *browser);
void EDVBrowserOPEject(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 EDVBrowserOPDelete(edv_browser_struct *browser);
void EDVBrowserOPSelectAll(edv_browser_struct *browser);
void EDVBrowserOPUnselectAll(edv_browser_struct *browser);
void EDVBrowserOPInvertSelection(edv_browser_struct *browser);
void EDVBrowserOPProperties(edv_browser_struct *browser);

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

void EDVBrowserOPDownload(edv_browser_struct *browser);

void EDVBrowserOPRefresh(edv_browser_struct *browser);
void EDVBrowserOPRefreshAll(edv_browser_struct *browser);

void EDVBrowserOPDirectoryTreeOrigin(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)


/*
 *	Menu item activate callback.
 *
 *	The data must be a edv_browser_opid_struct *.
 */
void EDVBrowserMenuItemCB(GtkWidget *widget, gpointer data)
{
	EDVBrowserOPCB(NULL, -1, data);
}

/*
 *	Menu item "enter_notify_event" signal callback.
 *
 *	The data must be a edv_browser_opid_struct *.
 */
gint EDVBrowserMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	EDVBrowserOPEnterCB(NULL, -1, data);
	return(TRUE);
}

/*
 *	Menu item "leave_notify_event" signal callback.
 *
 *	The data must be a edv_browser_opid_struct *.
 */
gint EDVBrowserMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	EDVBrowserOPLeaveCB(NULL, -1, data);
	return(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;
	cfg_item_struct *cfg_list;
	edv_device_struct *dev_ptr;
	edv_browser_struct *browser;
	edv_core_struct *core_ptr;
	edv_browser_opid_struct *opid = EDV_BROWSER_OPID(data);
	if(opid == NULL)
	    return;

	browser = EDV_BROWSER(opid->browser);
	if(browser == NULL)
	    return;

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

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	cfg_list = core_ptr->cfg_list;

	browser->freeze_count++;

	/* Get last selected device (if any) */
	dev_num = browser->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = 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_WRITE_PROTECT:
	    EDVBrowserOPWriteProtect(browser);
	    break;

	  case EDV_BROWSER_OP_RUN:
	    EDVBrowserOPRun(browser);
	    break;

	  case EDV_BROWSER_OP_RUN_TERMINAL:
	    EDVRunTerminal(core_ptr, NULL, browser->toplevel);
	    break;


	  case EDV_BROWSER_OP_NEW:
	    EDVBrowserOPNewObject(browser);
	    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_WITH:
	    EDVBrowserOPOpenWith(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_MOUNT:
	    EDVBrowserOPMount(browser);
	    break;

	  case EDV_BROWSER_OP_EJECT:
	    EDVBrowserOPEject(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_DELETE:
	    EDVBrowserOPDelete(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_PROPERTIES:
	    EDVBrowserOPProperties(browser);
	    break;

	  case EDV_BROWSER_OP_FIND:
	    EDVMapBrowserFindWin(core_ptr, browser);
	    break;

	  case EDV_BROWSER_OP_HISTORY:
	    EDVMapHistoryListWin(core_ptr, browser->toplevel);
	    break;

	  case EDV_BROWSER_OP_COPY_PATH:
	    EDVBrowserOPCopyPath(browser);
	    break;

	  case EDV_BROWSER_OP_COPY_URL:
	    EDVBrowserOPCopyURL(browser);
	    break;


	  case EDV_BROWSER_OP_REFRESH:
	    EDVBrowserOPRefresh(browser);
	    break;

	  case EDV_BROWSER_OP_REFRESH_ALL:
	    EDVBrowserOPRefreshAll(browser);
	    break;


	  case EDV_BROWSER_OP_DOWNLOAD:
	    EDVBrowserOPDownload(browser);
	    break;


	  case EDV_BROWSER_OP_SHOW_TOOL_BAR:
	    if(core_ptr != 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_ptr);
	    }
	    break;

	  case EDV_BROWSER_OP_SHOW_LOCATION_BAR:
	    if(core_ptr != 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_ptr);
	    }
	    break;

	  case EDV_BROWSER_OP_SHOW_MOUNT_BAR:
	    if(core_ptr != 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_ptr);
	    }
	    break;

	  case EDV_BROWSER_OP_SHOW_FIND_BAR:
	    if(core_ptr != 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_ptr);
	    }
	    break;

	  case EDV_BROWSER_OP_SHOW_STATUS_BAR:
	    if(core_ptr != 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_ptr);
	    }
	    break;

	  case EDV_BROWSER_OP_SHOW_HIDDEN_OBJECTS:
	    if(core_ptr != 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_ptr != 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_MIME_TYPES:
	    EDVMapMIMETypesListWin(core_ptr, browser->toplevel);
	    break;

	  case EDV_BROWSER_OP_DEVICES:
	    EDVMapDevicesListWin(core_ptr, browser->toplevel);
	    break;


	  case EDV_BROWSER_OP_NEW_BROWSER:
	    EDVNewBrowser(core_ptr);
	    break;

	  case EDV_BROWSER_OP_NEW_IMBR:
	    EDVNewImbr(core_ptr);
	    break;

	  case EDV_BROWSER_OP_NEW_ARCHIVER:
	    EDVNewArchiver(core_ptr);
	    break;

	  case EDV_BROWSER_OP_RECYCLE_BIN:
	    EDVMapRecBin(core_ptr);
	    break;

	  case EDV_BROWSER_OP_OPTIONS:
	    EDVMapOptionsWin(core_ptr, browser->toplevel);
	    break;

	  case EDV_BROWSER_OP_CUSTOMIZE:
	    EDVMapCustomizeWin(core_ptr, browser->toplevel);
	    break;


	  case EDV_BROWSER_OP_DEVICE_CHECK:
	    EDVRunDeviceCheck(core_ptr, dev_ptr, browser->toplevel);
	    break;

	  case EDV_BROWSER_OP_DEVICE_TOOLS:
	    EDVRunDeviceTools(core_ptr, dev_ptr, browser->toplevel);
	    break;

	  case EDV_BROWSER_OP_DEVICE_FORMAT:
	    EDVRunDeviceFormat(core_ptr, dev_ptr, browser->toplevel);
	    break;


	  case EDV_BROWSER_OP_HELP_ABOUT:
	    EDVAbout(core_ptr, browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_CONTENTS:
	    EDVHelp(core_ptr, "Contents", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_FILE_BROWSER:
	    EDVHelp(core_ptr, "File Browser", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_IMAGE_BROWSER:
	    EDVHelp(core_ptr, "Image Browser", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_ARCHIVER:
	    EDVHelp(core_ptr, "Archiver", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_RECYCLE_BIN:
	    EDVHelp(core_ptr, "Recycle Bin", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_KEYS_LIST:
	    EDVHelp(core_ptr, "Keys List", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_MIME_TYPES:
	    EDVHelp(core_ptr, "MIME Types", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_DEVICES:
	    EDVHelp(core_ptr, "Devices", browser->toplevel);
	    break;
	  case EDV_BROWSER_OP_HELP_COMMON_OPERATIONS:
	    EDVHelp(core_ptr, "Common Operations", browser->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 = EDV_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 = EDV_BROWSER(
	    (opid != NULL) ? opid->browser : NULL 
	);
	if(browser == NULL)
	    return;

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


/*
 *	Mount Bar mount callback.
 */
void EDVBrowserMountBarMountCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
)
{
	gboolean original_mount_state;
	gint status;
	GtkWidget *toplevel;
	edv_core_struct *core_ptr;
	edv_browser_struct *browser = EDV_BROWSER(data);
	if((browser == NULL) || (dev_ptr == NULL))
	    return;

	if(browser->processing)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = dev_ptr->is_mounted;

	/* Unmount or mount? */
	if(dev_ptr->is_mounted)
	    status = EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	else
	    status = EDVMountDoMount(core_ptr, dev_ptr, toplevel);

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

	EDVBrowserSetBusy(browser, FALSE);

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

/*
 *	Mount bar eject callback.
 */
void EDVBrowserMountBarEjectCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
)
{
	gboolean original_mount_state;
	gint status;
	GtkWidget *toplevel;
	edv_core_struct *core_ptr;
	edv_browser_struct *browser = EDV_BROWSER(data);
	if((browser == NULL) || (dev_ptr == NULL))
	    return;

	if(browser->processing)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = dev_ptr->is_mounted;

	/* Need to unmount first? */
	if(dev_ptr->is_mounted)
	    status = EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	/* Now eject */
	status = EDVMountDoEject(core_ptr, dev_ptr, toplevel);

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

	EDVBrowserSetBusy(browser, FALSE);

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

/*
 *	Mount bar go to mount path callback.
 */
extern void EDVBrowserMountBarGoToCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
)
{
	gchar *path;
	edv_browser_struct *browser = EDV_BROWSER(data);
	if((browser == NULL) || (dev_ptr == NULL))
	    return;

	if(browser->processing)
	    return;

	path = STRDUP(dev_ptr->mount_path);

	EDVBrowserSetBusy(browser, TRUE);

	/* Go to mount path */
	EDVBrowserDirTreeDoSelectPath(browser, path);

	EDVBrowserSetBusy(browser, FALSE);

	g_free(path);
}


/*
 *	Find Bar get current location callback.
 */
gchar *EDVBrowserFindBarLocationCB(gpointer fb, gpointer data)
{
	return(EDVBrowserCurrentLocation(
	    EDV_BROWSER(data)
	));
}

/*
 *	Find Bar start find callback.
 */
void EDVBrowserFindBarStartCB(gpointer fb, gpointer data)
{
	GtkCList *clist;
	edv_browser_struct *browser = EDV_BROWSER(data);
	if(browser == NULL)
	    return;

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

	EDVBrowserSetBusy(browser, TRUE);

	gtk_clist_freeze(clist);
	gtk_clist_unselect_all(clist);
	gtk_clist_thaw(clist);

	EDVBrowserUpdateMenus(browser);
}

/*
 *	Find Bar end find callback.
 */
void EDVBrowserFindBarEndCB(
	gpointer fb, gint total_matches, gpointer data
)
{
	edv_browser_struct *browser = EDV_BROWSER(data);
	if(browser == NULL)
	    return;

	EDVBrowserSetBusy(browser, FALSE);
}

/*
 *	Find Bar match callback.
 */
void EDVBrowserFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
)
{
	gint row;
	GtkCList *clist;
	edv_browser_struct *browser = EDV_BROWSER(data);
	if((path == NULL) || (lstat_buf == NULL) || (browser == NULL))
	    return;

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

	row = EDVBrowserContentsFindRowByPath(browser, path);
	if((row >= 0) && (row < clist->rows))
	    gtk_clist_select_row(clist, row, 0);
}


/*
 *	Status Bar set message callback.
 */
void EDVBrowserBarStatusMessageCB(
	gpointer bar, const gchar *message, gpointer data
)
{
	edv_browser_struct *browser = EDV_BROWSER(data);
	if(browser == NULL)
	    return;

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

/*
 *	Status Bar set progress callback.
 */
void EDVBrowserBarStatusProgressCB(
	gpointer bar, gfloat progress, gpointer data
)
{
	edv_browser_struct *browser = EDV_BROWSER(data);
	if(browser == NULL)
	    return;

	EDVStatusBarProgress(browser->status_bar, progress, FALSE);
}


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

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

	EDVBrowserSetBusy(browser, TRUE);

	EDVSyncDisks(EDV_CORE(browser->core_ptr));

	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_ptr;


	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core_ptr = EDV_CORE(browser->core_ptr);
	if((clist == NULL) || (core_ptr == 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_ptr,
	    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_ptr;

	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	cfg_list = core_ptr->cfg_list;

	/* Get current write protect state */
	write_protect = (gboolean)CFGItemListGetValueI(
	    cfg_list, 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_ptr, write_protect);
}


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

	if(browser->processing)
	    return;

	EDVBrowserSyncConfiguration(browser);
	EDVBrowserUnmap(browser);
}

/*
 *      Close all windows.
 */
void EDVBrowserOPExit(edv_browser_struct *browser)
{
	edv_core_struct *core_ptr;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	EDVBrowserSyncConfiguration(browser);
	EDVBrowserUnmap(browser);

	/* Mark need_close_all_windows on the core structure to TRUE,
	 * this will be checked during the management timeout, in which
	 * case all windows will be deleted
	 */
	core_ptr->need_close_all_windows = TRUE;
}


/*
 *	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_mesg;
	edv_core_struct *core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core_ptr, 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_FILE:
	    obj_type_name = "file";
	    status = EDVFCreateFile(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DIRECTORY:
	    obj_type_name = "directory";
	    status = EDVFCreateDirectory(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_LINK:
	    obj_type_name = "symbolic link";
	    status = EDVFCreateLink(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_BLOCK:
	    obj_type_name = "block device";
	    status = EDVFCreateDeviceBlock(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
	    obj_type_name = "character device";
	    status = EDVFCreateDeviceCharacter(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_FIFO:
	    obj_type_name = "fifo pipe";
	    status = EDVFCreateFifo(
		core_ptr, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_SOCKET:
	    obj_type_name = "socket";
	    status = EDVFCreateSocket(
		core_ptr, 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_mesg = EDVFCreateGetError();
	    if(!STRISEMPTY(error_mesg))
	    {
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
		    "Create Failed",
		    error_mesg,
		    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_ptr, 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)
{
	GtkWidget *w;

	if(browser == NULL)
	    return;

	w = browser->new_object_submenu;
	if(w != NULL)
	    gtk_menu_popup(
		GTK_MENU(w), NULL, NULL,
		NULL, NULL,
		1, GDK_CURRENT_TIME
	    );
}

/*
 *	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)
{
	gint row;
	GtkCList *clist;


	if(browser == NULL)
	    return;

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

	row = EDVCListGetSelectedLast(clist, NULL);
	if(row > -1)
	    EDVBrowserContentsDoOpenObject(
		browser, row, 0, 0
	    );
}

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

	if(browser == NULL)
	    return;

	clist = GTK_CLIST(browser->contents_clist);

	row = EDVCListGetSelectedLast(clist, NULL);
	if(row > -1)
	{
	    EDVBrowserContentsDoOpenWithObject(
		browser, row, 0
	    );
	}
	else
	{
	    /* No row on the contents clist was selected, so do
	     * open with for the directory ctree
	     */
	    GtkCTree *ctree = (GtkCTree *)browser->directory_ctree;
	    GtkCTreeNode *node = EDVCTreeGetSelectedLast(
		ctree, NULL
	    );
	    if(node != NULL)
		EDVBrowserDirTreeDoOpenWithObject(browser, node);
	}
}


/*
 *	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_ptr;
	GtkCTree *ctree = (GtkCTree *)((browser != NULL) ?  
	    browser->directory_ctree : NULL  
	);  
	if(ctree == NULL)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);

	/* Get the selected node */
	node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	if((node == NULL) || (row_ptr == NULL))
	{
	    /* No node selected */
	    EDVPlaySoundBeep(core_ptr);
	    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_ptr);
	    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 */
		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_ptr;
	GtkCTree *ctree = (GtkCTree *)((browser != NULL) ?  
	    browser->directory_ctree : NULL  
	);  
	if(ctree == NULL)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	/* Get home directory */
	path = STRDUP(core_ptr->home_dir);
	if(STRISEMPTY(path))
	{
	    EDVPlaySoundWarning(core_ptr);
	    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);

	/* If the home prefix is not the same as the current Directory
	 * Tree Origin then the Directory Tree Origin needs to be
	 * updated first
	 */
	if((browser->directory_ctree_origin_path != NULL) ?
	    !strpfx(path, browser->directory_ctree_origin_path) : TRUE
	)
	    EDVBrowserDirTreeSetOriginPath(browser, path);

	/* Go to the home directory */
	EDVBrowserDirTreeDoSelectPath(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);
}


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

	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = dev_ptr->is_mounted;

	/* Unmount or mount? */
	if(dev_ptr->is_mounted)
	    status = EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	else
	    status = EDVMountDoMount(core_ptr, dev_ptr, toplevel);

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

	EDVBrowserSetBusy(browser, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVMountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core_ptr);
		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_ptr, dev_num, dev_ptr, dev_ptr->is_mounted);
	}
}

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


	if(browser == NULL)
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	EDVBrowserSetBusy(browser, TRUE);

	/* Record original mount state */
	original_mount_state = dev_ptr->is_mounted;

	/* Need to unmount first? */
	if(dev_ptr->is_mounted)
	    status = EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	/* So now eject */
	status = EDVMountDoEject(core_ptr, dev_ptr, toplevel);

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

	EDVBrowserSetBusy(browser, FALSE);

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


/*
 *	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_ptr;
	edv_fopdlg_struct *d;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Get pointer to file operation dialog and create it as needed */
	d = browser->fopdlg;
	if(d == NULL)
	    browser->fopdlg = d = EDVFOPDlgNew(core_ptr);
	if(d == NULL)
	    return;

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

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_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_ptr;
	edv_fopdlg_struct *d;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Get pointer to file operation dialog and create it as needed */
	d = browser->fopdlg;
	if(d == NULL)
	    browser->fopdlg = d = EDVFOPDlgNew(core_ptr);
	if(d == NULL)
	    return;

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

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_COPY,
	    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);
}

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


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Get pointer to file operation dialog and create it as needed */
	d = browser->fopdlg;
	if(d == NULL)
	    browser->fopdlg = d = EDVFOPDlgNew(core_ptr);
	if(d == NULL)
	    return;

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

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

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

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

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


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == 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_ptr, 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_ptr;
	edv_fopdlg_struct *d;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Get pointer to file operation dialog and create it as needed */
	d = browser->fopdlg;
	if(d == NULL)
	    browser->fopdlg = d = EDVFOPDlgNew(core_ptr);
	if(d == NULL)
	    return;

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

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_CHMOD,
	    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);
}

/*
 *	Change Ownership.
 */
void EDVBrowserOPChown(edv_browser_struct *browser)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

	/* Get pointer to file operation dialog and create it as needed */
	d = browser->fopdlg;
	if(d == NULL)
	    browser->fopdlg = d = EDVFOPDlgNew(core_ptr);
	if(d == NULL)
	    return;

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

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_CHOWN,
	    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);
}


/*
 *	Delete.
 */
void EDVBrowserOPDelete(edv_browser_struct *browser)
{
	gboolean yes_to_all = FALSE;
	gint i, status;
	const gchar *path_ptr;
	gchar **object_path = NULL;
	gint total_object_paths = 0, objects_deleted = 0;
	guint *index;
	gint total_indices;
	const gchar *error_mesg;
	edv_core_struct *core_ptr;

	if(browser == NULL)
	    return;

#define DO_FREE_LOCALS	{			\
 strlistfree(object_path, total_object_paths);	\
 object_path = NULL;				\
 total_object_paths = 0;			\
}

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

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

	/* Contents List was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    edv_object_struct *obj;
	    gint row;
	    GtkCList *clist = (GtkCList *)browser->contents_clist;
	    GList *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) ? (obj->full_path != NULL) : FALSE)
		{
		    i = total_object_paths;
		    total_object_paths = i + 1;
		    object_path = (gchar **)g_realloc(
			object_path,
			total_object_paths * sizeof(gchar *)
		    );
		    if(object_path == NULL)
		    {
			total_object_paths = 0;
			break;
		    }
		    else
		    {
			object_path[i] = STRDUP(obj->full_path);
		    }
		}

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

	    GList *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) ? (obj->full_path != NULL) : FALSE)
		{
		    i = total_object_paths;
		    total_object_paths = i + 1;
		    object_path = (gchar **)g_realloc(
			object_path,
			total_object_paths * sizeof(gchar *)
		    );
		    if(object_path == NULL)
		    {
			total_object_paths = 0;
			break;
		    }
		    else
		    {
			object_path[i] = STRDUP(obj->full_path);
		    }
		}

		glist = g_list_next(glist);
	    }
	}

	/* No disk objects selected? */
	if(total_object_paths <= 0)
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Confirm delete */
	status = EDVConfirmDelete(
	    core_ptr, browser->toplevel,
	    (total_object_paths == 1) ? object_path[0] : NULL,
	    total_object_paths
	);
	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 list of disk object paths */
	status = 0;
	for(i = 0; i < total_object_paths; i++)
	{
	    path_ptr = object_path[i];
	    if(path_ptr == NULL)
		continue;

	    index = NULL;
	    total_indices = 0;

	    /* Delete disk object */
	    status = EDVRecBinFOPDelete(
		core_ptr, path_ptr,
		&index, &total_indices,
		browser->toplevel,
		TRUE, TRUE, &yes_to_all
	    );

	    /* Get error message (if any) describing the error that
	     * might have occured in the above operation
	     */  
	    error_mesg = EDVRecBinFOPGetError();
	    if(!STRISEMPTY(error_mesg))
	    {
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
		    "Operation Error",
		    error_mesg,
		    NULL,
		    browser->toplevel
		);
	    }

	    /* Object was delected successfully? */
	    if((index != NULL) && !status)
	    {
		gint n;
		struct stat lstat_buf;

		/* Report recycle object added for all non-zero indexes */
		for(n = 0; n < total_indices; n++)
		{
		    if(index[n] != 0)
			EDVRecycledObjectAddedEmit(core_ptr, index[n]);
		}

		/* Check if disk object no longer exists */
		if(lstat(path_ptr, &lstat_buf))
		{
		    /* Report disk object removed */
		    EDVObjectRemovedEmit(core_ptr, path_ptr);
		}

		/* Increment number of disk objects deleted */
		objects_deleted += total_indices;
	    }

	    /* Delete recycle objects index array */
	    g_free(index);
	    index = NULL;
	    total_indices = 0;

	    /* 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;
	}

	/* Update status bar */
	if(TRUE)
	{
	    gchar *buf;

	    switch(status)
	    {
	      case 0: case -5:
		buf = g_strdup_printf(
		    "Deleted %i object%s",
		    objects_deleted,
		    (objects_deleted == 1) ? "" : "s"
		);
		break;

	      case -4:  /* Cancel */
		buf = g_strdup_printf(
		    "Delete operation canceled"
		);
		break;

	      default:  /* Error */
		buf = g_strdup_printf(
		    "Unable to delete object%s",
		    (total_object_paths == 1) ? "" : "s"
		);
		break;
	    }
	    EDVStatusBarMessage(browser->status_bar, buf, FALSE);
	    g_free(buf);
	}

	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_ptr);

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

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


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	/* Get contents clist */
	clist = (GtkCList *)browser->contents_clist;
	if(clist == 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_ptr;
	GtkCList *clist;


	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	/* Get contents clist */
	clist = (GtkCList *)browser->contents_clist;
	if(clist == 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_ptr;
	GtkCList *clist;
	GList *glist, *selection;
	gint row, total_rows;


	if(browser == NULL)
	    return;

	clist = (GtkCList *)browser->contents_clist;
	core_ptr = EDV_CORE(browser->core_ptr);
	if((clist == NULL) || (core_ptr == 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);
}

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


	if(browser == NULL)
	    return;

	EDVBrowserSyncData(browser);

	/* Get object from last selected directory ctree item or
	 * contents clist item
	 */
	/* Contents clist was last selected? */
	if(browser->contents_clist_selected_row > -1)
	{
	    gint 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)
		);
		break;
	    }
	}
	/* Directory ctree was last selected? */
	else if(browser->directory_ctree_selected_node != NULL)
	{
	    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)
		);
		break;
	    }
	}

	if(obj != NULL)
	    EDVNewPropertiesDialog(
		EDV_CORE(browser->core_ptr),
		obj,
		browser->toplevel
	    );
}

/*
 *	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(
	    EDV_CORE(browser->core_ptr),
	    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(
	    EDV_CORE(browser->core_ptr),
	    sel_object, total_sel_objects,
	    browser->toplevel
	);

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

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

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

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

	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

	cfg_list = core_ptr->cfg_list;

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

	cur_path = STRDUP(EDVBrowserCurrentLocation(browser));

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

	/* Update directory ctree */
	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);

	    gtk_clist_freeze(clist);

	    /* Update list items */
	    EDVBrowserDirTreeDoRefresh(browser, node, TRUE);

	    /* Reget selected node, since the refresh procedure might
	     * have deleted the node
	     */
	    node = EDVCTreeGetSelectedLast(ctree, &row_ptr);
	    if(node != NULL)
	    {
		if(was_expanded)
		    gtk_ctree_expand(ctree, node);
	    }

	    gtk_clist_thaw(clist);

	    /* 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);

	    gtk_clist_freeze(clist);

	    /* Update list items */
	    EDVBrowserContentsDoUpdate(
		browser,
		cur_path,
		EDV_GET_B(EDV_CFG_PARM_LISTS_ANIMATE_UPDATES)
	    );

	    gtk_clist_thaw(clist);

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

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

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

	g_free(cur_path);
}

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

	if(browser == NULL)
	    return;

	core_ptr = EDV_CORE(browser->core_ptr);
	if(core_ptr == NULL)
	    return;

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

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


/*
 *	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_ptr;
	if((browser == NULL) || FileBrowserIsQuery())
	    return;

	toplevel = browser->toplevel;
	core_ptr = EDV_CORE(browser->core_ptr);

	/* 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_ptr);
	    EDVMessageWarning(
		"Set Directory Tree Origin Failed",
		buf,
		NULL,
		toplevel
	    );
	    g_free(buf);
	    return;
	}
	if(stat(new_origin_path, &stat_buf))
	{
	    gchar *buf, *s = STRDUP(strerror(errno));
	    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_ptr);
	    EDVMessageWarning(
		"Set Directory Tree Origin Failed",
		buf,
		NULL,
		toplevel
	    );
	    g_free(buf);
	    g_free(s);
	    return;
	}
	if(!S_ISDIR(stat_buf.st_mode))
	{
	    gchar *buf = g_strdup_printf(
"Object is not a directory:\n\
\n\
    %s\n",
		new_origin_path
	    );
	    EDVPlaySoundWarning(core_ptr);
	    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);
}
