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

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

#include "guiutils.h"
#include "cdialog.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 "imbr.h"
#include "imbrcb.h"
#include "imbropcb.h"
#include "imbrtlist.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 EDVImbrMenuItemCB(GtkWidget *widget, gpointer data);
gint EDVImbrMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
gint EDVImbrMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);

void EDVImbrOPCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void EDVImbrOPEnterCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void EDVImbrOPLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data
);

void EDVImbrMountBarMountCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);
void EDVImbrMountBarEjectCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);
void EDVImbrMountBarGoToCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
);

gchar *EDVImbrFindBarLocationCB(gpointer fb, gpointer data);
void EDVImbrFindBarStartCB(gpointer fb, gpointer data);
void EDVImbrFindBarEndCB(
	gpointer fb, gint total_matches, gpointer data
);
void EDVImbrFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
);

void EDVImbrBarStatusMessageCB(
	gpointer bar, const gchar *message, gpointer data
);
void EDVImbrBarStatusProgressCB(
	gpointer bar, gfloat progress, gpointer data
);

void EDVImbrOPSyncDisks(edv_imbr_struct *imbr);
void EDVImbrOPRun(edv_imbr_struct *imbr);
void EDVImbrOPWriteProtect(edv_imbr_struct *imbr);

void EDVImbrOPClose(edv_imbr_struct *imbr);
void EDVImbrOPExit(edv_imbr_struct *imbr);

static void EDVImbrOPNewObjectNexus(
	edv_imbr_struct *imbr,
	edv_object_type type, GtkWidget *toplevel
);
void EDVImbrOPNewObject(edv_imbr_struct *imbr);
void EDVImbrOPNewFile(edv_imbr_struct *imbr);
void EDVImbrOPNewDirectory(edv_imbr_struct *imbr);
void EDVImbrOPNewLink(edv_imbr_struct *imbr);
void EDVImbrOPNewFifo(edv_imbr_struct *imbr);
void EDVImbrOPNewDeviceBlock(edv_imbr_struct *imbr);
void EDVImbrOPNewDeviceCharacter(edv_imbr_struct *imbr);
void EDVImbrOPNewSocket(edv_imbr_struct *imbr);

void EDVImbrOPOpen(edv_imbr_struct *imbr);
void EDVImbrOPOpenWith(edv_imbr_struct *imbr);

static edv_object_struct **EDVImbrGetSelectedObjects(
	edv_imbr_struct *imbr, gint *total
);
void EDVImbrOPMove(edv_imbr_struct *imbr);
void EDVImbrOPCopy(edv_imbr_struct *imbr);
void EDVImbrOPLink(edv_imbr_struct *imbr);
void EDVImbrOPRename(edv_imbr_struct *imbr);
void EDVImbrOPChMod(edv_imbr_struct *imbr);
void EDVImbrOPChown(edv_imbr_struct *imbr);
void EDVImbrOPDelete(edv_imbr_struct *imbr);
void EDVImbrOPProperties(edv_imbr_struct *imbr);

void EDVImbrOPCopyPath(edv_imbr_struct *imbr);
void EDVImbrOPCopyURL(edv_imbr_struct *imbr);

void EDVImbrOPGoToParent(edv_imbr_struct *imbr);
void EDVImbrOPGoToHome(edv_imbr_struct *imbr);
void EDVImbrOPMount(edv_imbr_struct *imbr);
void EDVImbrOPEject(edv_imbr_struct *imbr);
void EDVImbrOPSelectAll(edv_imbr_struct *imbr);
void EDVImbrOPUnselectAll(edv_imbr_struct *imbr);
void EDVImbrOPInvertSelection(edv_imbr_struct *imbr);

void EDVImbrOpStop(edv_imbr_struct *imbr);
void EDVImbrOpContinue(edv_imbr_struct *imbr);
void EDVImbrOPRefresh(edv_imbr_struct *imbr);
void EDVImbrOPRefreshAll(edv_imbr_struct *imbr);

void EDVImbrOPDownload(edv_imbr_struct *imbr);


#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_imbr_opid_struct *.
 */
void EDVImbrMenuItemCB(GtkWidget *widget, gpointer data)
{
	EDVImbrOPCB(NULL, -1, data);
}

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

/*
 *	Menu item "leave_notify_event" signal callback.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
gint EDVImbrMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	EDVImbrOPLeaveCB(NULL, -1, data);
	return(TRUE);
}

/*
 *	Operation ID callback nexus.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
void EDVImbrOPCB(
	toolbar_item_struct *item, gint id, gpointer data  
)
{
	gint dev_num;
	cfg_item_struct *cfg_list;
	edv_device_struct *dev_ptr;
	edv_imbr_struct *imbr;
	edv_core_struct *core_ptr;
	edv_imbr_opid_struct *opid = EDV_IMBR_OPID(data);
	if(opid == NULL)
	    return;

	imbr = EDV_IMBR(opid->imbr);
	if(imbr == NULL)
	    return;

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

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

	cfg_list = core_ptr->cfg_list;

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

	imbr->freeze_count++;

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

	  case EDV_IMBR_OP_CLOSE:
	    EDVImbrOPClose(imbr);
	    break;

	  case EDV_IMBR_OP_EXIT:
	    EDVImbrOPExit(imbr);
	    break;


	  case EDV_IMBR_OP_SYNC_DISKS:
	    EDVImbrOPSyncDisks(imbr);
	    break;

	  case EDV_IMBR_OP_RUN:
	    EDVImbrOPRun(imbr);
	    break;

	  case EDV_IMBR_OP_WRITE_PROTECT:
	    EDVImbrOPWriteProtect(imbr);
	    break;

	  case EDV_IMBR_OP_RUN_TERMINAL:
	    EDVRunTerminal(core_ptr, NULL, imbr->toplevel);
	    break;


	  case EDV_IMBR_OP_NEW:
	    EDVImbrOPNewObject(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_FILE:
	    EDVImbrOPNewFile(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_DIRECTORY:
	    EDVImbrOPNewDirectory(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_LINK:
	    EDVImbrOPNewLink(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_FIFO:
	    EDVImbrOPNewFifo(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_DEVICE_BLOCK:
	    EDVImbrOPNewDeviceBlock(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_DEVICE_CHARACTER:
	    EDVImbrOPNewDeviceCharacter(imbr);
	    break;

	  case EDV_IMBR_OP_NEW_SOCKET:
	    EDVImbrOPNewSocket(imbr);
	    break;


	  case EDV_IMBR_OP_OPEN:
	    EDVImbrOPOpen(imbr);
	    break;

	  case EDV_IMBR_OP_OPEN_WITH:
	    EDVImbrOPOpenWith(imbr);
	    break;


	  case EDV_IMBR_OP_GOTO_PARENT:
	    EDVImbrOPGoToParent(imbr);
	    break;

	  case EDV_IMBR_OP_GOTO_HOME:
	    EDVImbrOPGoToHome(imbr);
	    break; 

	  case EDV_IMBR_OP_MOUNT:
	    EDVImbrOPMount(imbr);
	    break;

	  case EDV_IMBR_OP_EJECT:
	    EDVImbrOPEject(imbr);
	    break;

	  case EDV_IMBR_OP_MOVE:
	    EDVImbrOPMove(imbr);
	    break;

	  case EDV_IMBR_OP_COPY:
	    EDVImbrOPCopy(imbr);
	    break;

	  case EDV_IMBR_OP_LINK:
	    EDVImbrOPLink(imbr);
	    break;

	  case EDV_IMBR_OP_RENAME:
	    EDVImbrOPRename(imbr);
	    break;

	  case EDV_IMBR_OP_CHMOD:
	    EDVImbrOPChMod(imbr);
	    break;

	  case EDV_IMBR_OP_CHOWN:
	    EDVImbrOPChown(imbr);
	    break;

	  case EDV_IMBR_OP_DELETE:
	    EDVImbrOPDelete(imbr);
	    break;

	  case EDV_IMBR_OP_SELECT_ALL:
	    EDVImbrOPSelectAll(imbr);
	    break;

	  case EDV_IMBR_OP_UNSELECT_ALL:
	    EDVImbrOPUnselectAll(imbr);
	    break;

	  case EDV_IMBR_OP_INVERT_SELECTION:
	    EDVImbrOPInvertSelection(imbr);
	    break;

	  case EDV_IMBR_OP_PROPERTIES:
	    EDVImbrOPProperties(imbr);
	    break;

	  case EDV_IMBR_OP_FIND:
	    EDVMapImbrFindWin(core_ptr, imbr);
	    break;

	  case EDV_IMBR_OP_HISTORY:
	    EDVMapHistoryListWin(core_ptr, imbr->toplevel);
	    break;

	  case EDV_IMBR_OP_COPY_PATH:
	    EDVImbrOPCopyPath(imbr);
	    break;

	  case EDV_IMBR_OP_COPY_URL:
	    EDVImbrOPCopyURL(imbr);
	    break;


	  case EDV_IMBR_OP_STOP:
	    EDVImbrOpStop(imbr);
	    break;

	  case EDV_IMBR_OP_CONTINUE:
	    EDVImbrOpContinue(imbr);
	    break;

	  case EDV_IMBR_OP_REFRESH:
	    EDVImbrOPRefresh(imbr);
	    break;

	  case EDV_IMBR_OP_REFRESH_ALL:
	    EDVImbrOPRefreshAll(imbr);
	    break;


	  case EDV_IMBR_OP_DOWNLOAD:
	    EDVImbrOPDownload(imbr);
	    break;


	  case EDV_IMBR_OP_SHOW_TOOL_BAR:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_IMBR_SHOW_TOOL_BAR
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_TOOL_BAR, state);
		EDVReconfiguredEmit(core_ptr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_LOCATION_BAR:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_IMBR_SHOW_LOCATION_BAR
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_LOCATION_BAR, state);
		EDVReconfiguredEmit(core_ptr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_MOUNT_BAR:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_IMBR_SHOW_MOUNT_BAR
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_MOUNT_BAR, state);
		EDVReconfiguredEmit(core_ptr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_FIND_BAR:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_IMBR_SHOW_FIND_BAR
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_FIND_BAR, state);
		EDVReconfiguredEmit(core_ptr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_STATUS_BAR:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(
		    EDV_CFG_PARM_IMBR_SHOW_STATUS_BAR
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_STATUS_BAR, state);
		EDVReconfiguredEmit(core_ptr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_HIDDEN_OBJECTS:
	    if(core_ptr != NULL)
	    {
		gboolean state = !EDV_GET_B(           
		    EDV_CFG_PARM_IMBR_SHOW_OBJECT_HIDDEN
		);
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_OBJECT_HIDDEN, state);
		EDVImbrOPRefresh(imbr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_NOACCESS_OBJECTS:
	    if(core_ptr != NULL)               
	    {                                 
		gboolean state = !EDV_GET_B(           
		    EDV_CFG_PARM_IMBR_SHOW_OBJECT_NOACCESS
		);                                      
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_OBJECT_NOACCESS, state);
		EDVImbrOPRefresh(imbr);
	    }
	    break;

	  case EDV_IMBR_OP_SHOW_NONIMAGE_OBJECTS:
	    if(core_ptr != NULL)               
	    {                                 
		gboolean state = !EDV_GET_B(           
		    EDV_CFG_PARM_IMBR_SHOW_OBJECT_NONIMAGE
		);                                      
		EDV_SET_B(EDV_CFG_PARM_IMBR_SHOW_OBJECT_NONIMAGE, state);
		EDVImbrOPRefresh(imbr);
	    }
	    break;

	  case EDV_IMBR_OP_MIME_TYPES:
	    EDVMapMIMETypesListWin(core_ptr, imbr->toplevel);
	    break;

	  case EDV_IMBR_OP_DEVICES:
	    EDVMapDevicesListWin(core_ptr, imbr->toplevel);
	    break;


	  case EDV_IMBR_OP_NEW_BROWSER:
	    EDVNewBrowser(core_ptr);
	    break;

	  case EDV_IMBR_OP_NEW_IMBR:
	    EDVNewImbr(core_ptr);
	    break;

	  case EDV_IMBR_OP_NEW_ARCHIVER:
	    EDVNewArchiver(core_ptr);
	    break;

	  case EDV_IMBR_OP_RECYCLE_BIN:
	    EDVMapRecBin(core_ptr);
	    break;

	  case EDV_IMBR_OP_OPTIONS:
	    EDVMapOptionsWin(core_ptr, imbr->toplevel);
	    break;

	  case EDV_IMBR_OP_CUSTOMIZE:
	    EDVMapCustomizeWin(core_ptr, imbr->toplevel);
	    break;


	  case EDV_IMBR_OP_DEVICE_CHECK:
	    EDVRunDeviceCheck(core_ptr, dev_ptr, imbr->toplevel);
	    break;

	  case EDV_IMBR_OP_DEVICE_TOOLS:
	    EDVRunDeviceTools(core_ptr, dev_ptr, imbr->toplevel);
	    break;

	  case EDV_IMBR_OP_DEVICE_FORMAT:
	    EDVRunDeviceFormat(core_ptr, dev_ptr, imbr->toplevel);
	    break;


	  case EDV_IMBR_OP_HELP_ABOUT:
	    EDVAbout(core_ptr, imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_CONTENTS:
	    EDVHelp(core_ptr, "Contents", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_FILE_BROWSER:
	    EDVHelp(core_ptr, "File Browser", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_IMAGE_BROWSER:
	    EDVHelp(core_ptr, "Image Browser", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_ARCHIVER:
	    EDVHelp(core_ptr, "Archiver", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_RECYCLE_BIN:
	    EDVHelp(core_ptr, "Recycle Bin", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_KEYS_LIST:
	    EDVHelp(core_ptr, "Keys List", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_MIME_TYPES:
	    EDVHelp(core_ptr, "MIME Types", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_DEVICES:
	    EDVHelp(core_ptr, "Devices", imbr->toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_COMMON_OPERATIONS:
	    EDVHelp(core_ptr, "Common Operations", imbr->toplevel);
	    break;
	}

	imbr->freeze_count--;
}

/*
 *	Operation ID enter notify callback nexus.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
void EDVImbrOPEnterCB(
	toolbar_item_struct *item, gint id, gpointer data  
)
{
	const gchar *tooltip;
	edv_imbr_opid_struct *opid = EDV_IMBR_OPID(data);
	edv_imbr_struct *imbr = EDV_IMBR(
	    (opid != NULL) ? opid->imbr : NULL
	);
	if(imbr == NULL)
	    return;

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

/*
 *	Operation ID leave notify callback nexus.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
void EDVImbrOPLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data  
)
{
	edv_imbr_opid_struct *opid = EDV_IMBR_OPID(data);
	edv_imbr_struct *imbr = EDV_IMBR( 
	    (opid != NULL) ? opid->imbr : NULL 
	);
	if(imbr == NULL)
	    return;

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


/*
 *	Find Bar get current location callback.
 */
gchar *EDVImbrFindBarLocationCB(gpointer fb, gpointer data)
{
	return(EDVImbrCurrentLocation(EDV_IMBR(data)));
}

/*
 *	Mount Bar mount callback.
 */
void EDVImbrMountBarMountCB(
	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_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev_ptr == NULL))
	    return;

	if(imbr->processing)
	    return;

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

	EDVImbrSetBusy(imbr, 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
	);

	EDVImbrSetBusy(imbr, 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,
		    imbr->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 EDVImbrMountBarEjectCB(
	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_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev_ptr == NULL))
	    return;

	if(imbr->processing)
	    return;

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

	EDVImbrSetBusy(imbr, 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
	);

	EDVImbrSetBusy(imbr, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVMountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
		    "Eject Failed",
		    last_error,
		    NULL,
		    imbr->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 EDVImbrMountBarGoToCB(
	gpointer mb, gint dev_num, edv_device_struct *dev_ptr,
	gpointer data
)
{
	gchar *path;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev_ptr == NULL))
	    return;

	if(imbr->processing)
	    return;

	/* Get copy of mount path */
	path = STRDUP(dev_ptr->mount_path);

	EDVImbrSetBusy(imbr, TRUE);

	EDVImbrSelectPath(imbr, path);
	EDVImbrUpdateMenus(imbr);

	EDVImbrSetBusy(imbr, FALSE);

	g_free(path);
}


/*
 *	Find Bar start find callback.
 */
void EDVImbrFindBarStartCB(gpointer fb, gpointer data)
{
	tlist_struct *tlist;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	TListFreeze(tlist);
	TListUnselectAll(tlist);
	TListThaw(tlist);

	EDVImbrUpdateMenus(imbr);
}

/*
 *	Find Bar end find callback.
 */
void EDVImbrFindBarEndCB(
	gpointer fb, gint total_matches, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	EDVImbrSetBusy(imbr, FALSE);
}


/*
 *	Find Bar match callback.
 */
void EDVImbrFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
)
{
	gint thumb_num;
	tlist_struct *tlist;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((path == NULL) || (lstat_buf == NULL) || (imbr == NULL))
	    return;

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	thumb_num = EDVImbrTListFindThumbByPath(imbr, path);
	if((thumb_num >= 0) && (thumb_num < tlist->total_thumbs))
	{
	    TListFreeze(tlist);
	    TListSelectThumb(tlist, thumb_num);
	    TListThaw(tlist);
	}
}


/*
 *	Status Bar set message callback.
 */
void EDVImbrBarStatusMessageCB(
	gpointer mb, const gchar *message, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

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

/*
 *	Status Bar set progress callback.
 */
void EDVImbrBarStatusProgressCB(
	gpointer bar, gfloat progress, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

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


/*
 *	Sync Disks.
 */
void EDVImbrOPSyncDisks(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

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

	EDVSyncDisks(EDV_CORE(imbr->core_ptr));

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

/*
 *	Run.
 */
void EDVImbrOPRun(edv_imbr_struct *imbr)
{
	gchar *cmd;
	GList *glist;
	tlist_struct *tlist;
	const edv_object_struct *obj;
	edv_core_struct *core_ptr;


	if(imbr == NULL)
	    return;

	tlist = imbr->tlist;
	core_ptr = EDV_CORE(imbr->core_ptr);
	if((tlist == NULL) || (core_ptr == NULL))
	    return;

	/* Format command to contain the list of selected objects */
	cmd = STRDUP("");
	for(glist = tlist->selection; glist != NULL; glist = g_list_next(glist))
	{
	    obj = EDV_OBJECT(TListGetThumbData(
		tlist, (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,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

	g_free(cmd);
}

/*
 *	Write Protect toggle.
 */
void EDVImbrOPWriteProtect(edv_imbr_struct *imbr)
{
	gboolean write_protect;
	cfg_item_struct *cfg_list;
	edv_core_struct *core_ptr;


	if(imbr == NULL)
	    return;

	core_ptr = EDV_CORE(imbr->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 EDVImbrOPClose(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	EDVImbrSyncConfiguration(imbr);
	EDVImbrUnmap(imbr);
}

/*
 *      Close All Windows.
 */
void EDVImbrOPExit(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;


	if(imbr == NULL)
	    return;

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

	EDVImbrSyncConfiguration(imbr);
	EDVImbrUnmap(imbr);

	/* 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 EDVImbrOPNewObjectNexus(
	edv_imbr_struct *imbr,
	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(imbr->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 = EDVImbrCurrentLocation(imbr);
	if(cur_path == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

	EDVImbrSetBusy(imbr, 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,
		    imbr->toplevel
		);
	    }

	    EDVStatusBarMessage(imbr->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 thumb_num;
		tlist_struct *tlist = imbr->tlist;


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

		/* Select new thumb (if any) who's disk object structure
		 * matches the newly added object path.
		 */
		thumb_num = EDVImbrTListFindThumbByPath(imbr, new_path);
		if((thumb_num > -1) && (tlist != NULL))
		{
		    TListFreeze(tlist);
		    TListUnselectAll(tlist);
		    TListSelectThumb(tlist, thumb_num);
		    TListThaw(tlist);
		}
	    }

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

	EDVImbrSetBusy(imbr, FALSE);

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

/*
 *	New Object.
 */
void EDVImbrOPNewObject(edv_imbr_struct *imbr)
{
	GtkWidget *w;

	if(imbr == NULL)
	    return;

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

/*
 *	New File.
 */
void EDVImbrOPNewFile(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_FILE, imbr->toplevel
	);
}

/*
 *	New Directory.
 */
void EDVImbrOPNewDirectory(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_DIRECTORY, imbr->toplevel
	);
}

/*
 *	New Link.
 */
void EDVImbrOPNewLink(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_LINK, imbr->toplevel
	);
}

/*
 *	New FIFO Pipe.
 */
void EDVImbrOPNewFifo(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_FIFO, imbr->toplevel
	);
}

/*
 *	New Block Device.
 */
void EDVImbrOPNewDeviceBlock(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_DEVICE_BLOCK, imbr->toplevel
	);
}

/*
 *	New Character Device.
 */
void EDVImbrOPNewDeviceCharacter(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_DEVICE_CHARACTER, imbr->toplevel
	);
}

/*
 *	New Socket.
 */
void EDVImbrOPNewSocket(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrOPNewObjectNexus(
	    imbr, EDV_OBJECT_TYPE_SOCKET, imbr->toplevel
	);
}


/*
 *	Open.
 */
void EDVImbrOPOpen(edv_imbr_struct *imbr)
{
	gint thumb_num;
	GList *glist;
	tlist_struct *tlist = (imbr != NULL) ? imbr->tlist : NULL;
	if(tlist == NULL)
	    return;

	glist = tlist->selection_end;
	thumb_num = (glist != NULL) ? (gint)glist->data : -1;
	if(thumb_num > -1)
	    EDVImbrTListDoOpenObject(imbr, thumb_num, 0);
}

/*
 *	Open With.
 */
void EDVImbrOPOpenWith(edv_imbr_struct *imbr)
{
	gint thumb_num;
	GList *glist;
	tlist_struct *tlist = (imbr != NULL) ? imbr->tlist : NULL;
	if(tlist == NULL)
	    return;

	glist = tlist->selection_end;
	thumb_num = (glist != NULL) ? (gint)glist->data : -1;
	if(thumb_num > -1)
	    EDVImbrTListDoOpenWithObject(imbr, thumb_num);
}


/*
 *	Go To Parent.
 */
void EDVImbrOPGoToParent(edv_imbr_struct *imbr)
{
	gchar *parent_path;
	const gchar *current_location;
	edv_core_struct *core_ptr;

	if(imbr == NULL)
	    return;

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

	/* Get the current location */
	current_location = EDVImbrCurrentLocation(imbr);
	if(STRISEMPTY(current_location))
	{
	    EDVPlaySoundBeep(core_ptr);
	    return;
	}

	/* Already at toplevel? */
	if(!strcmp((const char *)current_location, "/"))
	{
	    EDVPlaySoundBeep(core_ptr);
	    return;
	}

	/* Get copy of parent path of the current location */
	parent_path = g_dirname(current_location);
	if(parent_path == NULL)
	{
	    EDVPlaySoundBeep(core_ptr);
	    return;
	}

	EDVImbrSetBusy(imbr, TRUE);

	/* Go to parent directory */
	EDVImbrSelectPath(imbr, parent_path);

	EDVImbrUpdateMenus(imbr);

	g_free(parent_path);

	EDVImbrSetBusy(imbr, FALSE);
}

/*
 *	Go To Home.
 */
void EDVImbrOPGoToHome(edv_imbr_struct *imbr)
{
	gchar *home_dir;
	edv_core_struct *core_ptr;

	if(imbr == NULL)
	    return;

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

	/* Get home directory */
	home_dir = STRDUP(core_ptr->home_dir);
	if(STRISEMPTY(home_dir))
	{
	    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
		imbr->toplevel
	    );
	    g_free(home_dir);
	    return;
	}

	EDVImbrSetBusy(imbr, TRUE);

	/* Go to home directory */
	EDVImbrSelectPath(imbr, home_dir);

	EDVImbrSetBusy(imbr, FALSE);

	EDVImbrUpdateMenus(imbr);

	g_free(home_dir);
}

/*
 *	Mount/Unmount.
 */
void EDVImbrOPMount(edv_imbr_struct *imbr)
{
	gboolean original_mount_state;
	gint status, dev_num;
	GtkWidget *toplevel;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;


	if(imbr == NULL)
	    return;

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

	/* Get number of last selected device index (if any) */
	dev_num = imbr->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;

	EDVImbrSetBusy(imbr, 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
	);

	EDVImbrSetBusy(imbr, 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,
		    imbr->toplevel
		);
	    }
	}
	else
	{
	    /* Report mount signal to all of endeavour's resources */
	    EDVObjectMountEmit(core_ptr, dev_num, dev_ptr, dev_ptr->is_mounted);
	}
}

/*
 *	Eject.
 */
void EDVImbrOPEject(edv_imbr_struct *imbr)
{
	gboolean original_mount_state;
	gint status, dev_num;
	GtkWidget *toplevel;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;

	if(imbr == NULL)
	    return;

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

	/* Get number of last selected device index (if any) */
	dev_num = imbr->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;

	EDVImbrSetBusy(imbr, 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
	);

	EDVImbrSetBusy(imbr, FALSE);

	/* Eject error? */
	if(status)
	{
	    const gchar *last_error = EDVMountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
		    "Eject Failed",
		    last_error,
		    NULL,
		    imbr->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 Image Browser.
 *
 *      Only the returned pointer array (not each object) needs to be
 *      deallocated by the calling function.
 */
static edv_object_struct **EDVImbrGetSelectedObjects(
	edv_imbr_struct *imbr, gint *total
)
{
	edv_object_struct **sel_object = NULL, *obj;
	gint total_sel_objects = 0;


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

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

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

	if(TRUE)
	{
	    gint i, thumb_num;
	    GList *glist;
	    tlist_struct *tlist = imbr->tlist;

	    glist = (tlist != NULL) ? tlist->selection : NULL;
	    while(glist != NULL)
	    {
		thumb_num = (gint)glist->data;
		obj = (edv_object_struct *)TListGetThumbData(tlist, thumb_num);
		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 EDVImbrOPMove(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(imbr == NULL)
	    return;

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

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

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

	/* Sync data to ensure that current values to operate on */
	EDVImbrSyncData(imbr);

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_MOVE,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

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

/*
 *	Copy.
 */
void EDVImbrOPCopy(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(imbr == NULL)
	    return;

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

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

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

	/* Sync data to ensure that current values to operate on */
	EDVImbrSyncData(imbr);

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_COPY,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

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

/*
 *	Link.
 */
void EDVImbrOPLink(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(imbr == NULL)
	    return;

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

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

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

	/* Sync data to ensure that current values to operate on */
	EDVImbrSyncData(imbr);

	/* Get list of selected objects */
	sel_object = EDVImbrGetSelectedObjects(
	    imbr, &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),
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

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

/*
 *	Rename.
 */
void EDVImbrOPRename(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;


	if(imbr == NULL)
	    return;

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

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

	if(TRUE)
	{
	    gint thumb_num;
	    GList *glist;
	    tlist_struct *tlist = imbr->tlist;

	    glist = (tlist != NULL) ? tlist->selection_end : NULL;
	    thumb_num = (glist != NULL) ? (gint)glist->data : -1;
	    if(thumb_num > -1)
		EDVImbrTListDoFPromptRename(imbr, thumb_num);
	}
}

/*
 *	Change Permissions.
 */
void EDVImbrOPChMod(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(imbr == NULL)
	    return;

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

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

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

	/* Sync data to ensure that current values to operate on */
	EDVImbrSyncData(imbr);

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_CHMOD,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

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

/*
 *	Change Ownership.
 */
void EDVImbrOPChown(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	edv_core_struct *core_ptr;
	edv_fopdlg_struct *d;


	if(imbr == NULL)
	    return;

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

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

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

	/* Sync data to ensure that current values to operate on */
	EDVImbrSyncData(imbr);

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

	/* Map dialog */
	EDVFOPDlgMapValues(
	    d, EDV_FOPDLG_OP_CHOWN,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);

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

/*
 *	Delete.
 */
void EDVImbrOPDelete(edv_imbr_struct *imbr)
{
	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(imbr == NULL)
	    return;

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

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

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


	/* 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
	 */
	if(TRUE)
	{
	    gint i, thumb_num;
	    GList *glist;
	    tlist_struct *tlist = imbr->tlist;
	    edv_object_struct *obj;


	    glist = (tlist != NULL) ? tlist->selection : NULL;
	    while(glist != NULL)
	    {
		thumb_num = (gint)glist->data;
		obj = EDV_OBJECT(
		    TListGetThumbData(tlist, thumb_num)
		);
		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;
		    }

		    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, imbr->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;
	}


	EDVImbrSetBusy(imbr, TRUE);

	/* Iterate through objects */
	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,
		imbr->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,
		    imbr->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(imbr->status_bar, buf, FALSE);
	    g_free(buf);
	}

	EDVImbrSetBusy(imbr, 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 EDVImbrOPSelectAll(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

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

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	/* Select all thumbs */
	TListFreeze(tlist);
	TListSelectAll(tlist);
	TListThaw(tlist);

	/* Assume highest thumb index as the last selected thumb */
	imbr->tlist_selected_thumb = tlist->total_thumbs - 1;

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

	EDVImbrSetBusy(imbr, FALSE);

	EDVImbrUpdateMenus(imbr);
}

/*
 *	Unselect All.
 */
void EDVImbrOPUnselectAll(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

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

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	/* Unselect all thumbs */
	TListFreeze(tlist);
	TListUnselectAll(tlist);
	TListThaw(tlist);

	/* Mark that no thumb is selected */
	imbr->tlist_selected_thumb = -1;

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

	EDVImbrSetBusy(imbr, FALSE);

	EDVImbrUpdateMenus(imbr);
}

/*
 *      Invert Selection.
 */
void EDVImbrOPInvertSelection(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;
	GList *glist, *selection;
	gint thumb, total_thumbs;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

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

	/* Get thumbs list */
	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);
	TListFreeze(tlist);

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

	for(thumb = 0, total_thumbs = tlist->total_thumbs;
	    thumb < total_thumbs;
	    thumb++
	)
	{
	    for(glist = selection;
		glist != NULL;
		glist = g_list_next(glist)
	    )
	    {
		if(thumb == (gint)glist->data)
		{
		    TListUnselectThumb(tlist, thumb);
		    break;
		}
	    }
	    /* Thumb not selected? */
	    if(glist == NULL)
		TListSelectThumb(tlist, thumb);
	}

	g_list_free(selection);

	TListThaw(tlist);
	EDVStatusBarMessage(
	    imbr->status_bar, "Selection inverted", FALSE
	);
	EDVImbrSetBusy(imbr, FALSE);
}

/*
 *      Properties.
 */
void EDVImbrOPProperties(edv_imbr_struct *imbr)
{
	edv_object_struct *obj;

	if(imbr == NULL)
	    return;

	EDVImbrSyncData(imbr);

	/* Get object from last selected thumb */
	obj = EDV_OBJECT(TListGetThumbData(
	    imbr->tlist, imbr->tlist_selected_thumb
	));
	if(obj != NULL)
	    EDVNewPropertiesDialog(
		EDV_CORE(imbr->core_ptr),
		obj,
		imbr->toplevel
	    );
}


/*
 *	Copy Path To DDE.
 */
void EDVImbrOPCopyPath(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;

	if(imbr == NULL)
	    return;

	EDVImbrSyncData(imbr);

	/* Create a list of disk objects, each pointer in the array will
	 * reffer to a shared object structure pointer so it should not
	 * be deallocated
	 *
	 * Check which list contains the last selected object, that will
	 * indicate the list to get selected object structures from
	 */
	sel_object = EDVImbrGetSelectedObjects(
	    imbr, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

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

	g_free(sel_object);
}

/*
 *	Copy Location To DDE.
 */
void EDVImbrOPCopyURL(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;

	if(imbr == NULL)
	    return;

	EDVImbrSyncData(imbr);

	/* Create a list of disk objects, each pointer in the array will
	 * reffer to a shared object structure pointer so it should not
	 * be deallocated
	 *
	 * Check which list contains the last selected object, that will
	 * indicate the list to get selected object structures from
	 */
	sel_object = EDVImbrGetSelectedObjects(
	    imbr, &total_sel_objects
	);
	if(sel_object == NULL)
	    return;

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

	g_free(sel_object);
}


/*
 *	Stop.
 */
void EDVImbrOpStop(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	imbr->stop_count++;
}

/*
 *	Continue.
 */
void EDVImbrOpContinue(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	/* Reset loading process timeout callback and initialize
	 * loading values
	 */
	EDVImbrQueueLoadingProcess(imbr);

	EDVImbrUpdateMenus(imbr);
}

/*
 *      Refresh.
 */
void EDVImbrOPRefresh(edv_imbr_struct *imbr)
{
	gchar *cur_path;
	GtkWidget *w;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

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

	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));

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

	/* Update thumbs list by reselecting its path */
	tlist = imbr->tlist;
	if(tlist != NULL)
	{
	    GtkAdjustment *adj;

	    /* Record last scroll position */
	    gfloat last_x = GTK_ADJUSTMENT_GET_VALUE(tlist->hadj),
		   last_y = GTK_ADJUSTMENT_GET_VALUE(tlist->vadj);

	    /* Reget listing and initialize values for the loading
	     * process
	     *
	     * The entire list should be fully obtained after this call
	     * so it is possible to scroll to the old position
	     */
	    EDVImbrSelectPath(imbr, cur_path);

	    /* Scroll back to previous location */
	    adj = tlist->hadj;
	    if(adj != NULL)
	    {
		if(last_x > (adj->upper - adj->page_size))
		    last_x = adj->upper - adj->page_size;
		if(last_x < adj->lower)
		    last_x = adj->lower;
		gtk_adjustment_set_value(adj, last_x);
	    }
	    adj = tlist->vadj;
	    if(adj != NULL)
	    {
		if(last_y > (adj->upper - adj->page_size))
		    last_y = adj->upper - adj->page_size;
		if(last_y < adj->lower)
		    last_y = adj->lower;
		gtk_adjustment_set_value(adj, last_y);
	    }
	}

	EDVImbrUpdateMenus(imbr);
	EDVStatusBarMessage(
	    imbr->status_bar, "Refreshed contents listing", FALSE
	);

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

	g_free(cur_path);
}

/*
 *      Refresh All.
 */
void EDVImbrOPRefreshAll(edv_imbr_struct *imbr)
{
	edv_core_struct *core_ptr;

	if(imbr == NULL)
	    return;

	core_ptr = EDV_CORE(imbr->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 Image Browser */
	EDVImbrOPRefresh(imbr);
}


/*
 *	Download.
 */
void EDVImbrOPDownload(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVInternetDownloadObject(
	    EDV_CORE(imbr->core_ptr),
	    NULL,
	    EDVImbrCurrentLocation(imbr),
	    imbr->toplevel
	);
}


