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

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

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

#include "cfg.h"
#include "edv_types.h"
#include "edv_obj.h"
#include "edv_confirm.h"
#include "edv_recycled_obj.h"
#include "edv_recbin_stat.h"
#include "edv_recbin_delete.h"
#include "edv_device.h"
#include "edv_devices_list.h"
#include "edv_device_mount.h"
#include "edv_mount_bar.h"
#include "edv_find_bar.h"
#include "edv_status_bar.h"
#include "obj_op_dlg.h"
#include "imbr.h"
#include "imbr_cb.h"
#include "imbr_op_cb.h"
#include "imbr_tlist.h"
#include "presentation_mode.h"
#include "endeavour2.h"
#include "edv_obj_create.h"
#include "edv_obj_op.h"
#include "edv_cb.h"
#include "edv_help.h"
#include "edv_op.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"
#include "edv_cfg_list.h"
#include "config.h"

#include "images/icon_wildcards_32x32.xpm"


void 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 EDVImbrOPClose(edv_imbr_struct *imbr);
void EDVImbrOPExit(edv_imbr_struct *imbr);

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

static void EDVImbrOPNewObjectNexus(
	edv_imbr_struct *imbr,
	edv_object_type type, GtkWidget *toplevel
);
void EDVImbrOPNewObject(
	edv_imbr_struct *imbr, toolbar_item_struct *item
);
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 EDVImbrOPOpenTo(edv_imbr_struct *imbr, toolbar_item_struct *item);
void EDVImbrOPOpenWith(edv_imbr_struct *imbr);

void EDVImbrOPCopyPath(edv_imbr_struct *imbr);
void EDVImbrOPCopyURL(edv_imbr_struct *imbr);
void EDVImbrOPPaste(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 EDVImbrOPChTime(edv_imbr_struct *imbr);
void EDVImbrOPDelete(edv_imbr_struct *imbr);
void EDVImbrOPProperties(edv_imbr_struct *imbr);

void EDVImbrOPSelectAll(edv_imbr_struct *imbr);
void EDVImbrOPUnselectAll(edv_imbr_struct *imbr);
void EDVImbrOPInvertSelection(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 EDVImbrOPDownload(edv_imbr_struct *imbr);

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

void EDVImbrThumbsListFilter(edv_imbr_struct *imbr);

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


/*
 *	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;
	GtkWidget *toplevel;
	cfg_item_struct *cfg_list;
	edv_device_struct *dev;
	edv_imbr_struct *imbr;
	edv_core_struct *core;
	edv_imbr_opid_struct *opid = EDV_IMBR_OPID(data);
	if(opid == NULL)
	    return;

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

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

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

	cfg_list = core->cfg_list;

	/* Get last selected device (if any) */
	dev_num = imbr->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core->total_devices))
	    dev = core->device[dev_num];
	else
	    dev = 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_HISTORY:
	    EDVMapHistoryListWin(core, toplevel);
	    break;
	  case EDV_IMBR_OP_RUN:
	    EDVImbrOPRun(imbr);
	    break;
	  case EDV_IMBR_OP_RUN_TERMINAL:
	    if(core != NULL)
	    {
		gchar *wd = STRDUP(EDVImbrCurrentLocation(imbr));
		EDVRunTerminal(core, NULL, wd, toplevel);
		g_free(wd);
	    }
	    break;
	  case EDV_IMBR_OP_WRITE_PROTECT:
	    EDVImbrOPWriteProtect(imbr);
	    break;
	  case EDV_IMBR_OP_DELETE_METHOD_RECYCLE:
	    EDVImbrOPDeleteMethodRecycle(imbr);
	    break;
	  case EDV_IMBR_OP_DELETE_METHOD_PURGE:
	    EDVImbrOPDeleteMethodPurge(imbr);
	    break;

	  case EDV_IMBR_OP_NEW:
	    EDVImbrOPNewObject(imbr, item);
	    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_TO:
	    EDVImbrOPOpenTo(imbr, item);
	    break;
	  case EDV_IMBR_OP_OPEN_WITH:
	    EDVImbrOPOpenWith(imbr);
	    break;

	  case EDV_IMBR_OP_COPY_PATH:
	    EDVImbrOPCopyPath(imbr);
	    break;
	  case EDV_IMBR_OP_COPY_URL:
	    EDVImbrOPCopyURL(imbr);
	    break;
	  case EDV_IMBR_OP_PASTE:
	    EDVImbrOPPaste(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_CHTIME:
	    EDVImbrOPChTime(imbr);
	    break;
	  case EDV_IMBR_OP_DELETE:
	    EDVImbrOPDelete(imbr);
	    break;
	  case EDV_IMBR_OP_PROPERTIES:
	    EDVImbrOPProperties(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_FIND:
	    EDVMapImbrFindWin(core, imbr);
	    break;

	  case EDV_IMBR_OP_DOWNLOAD:
	    EDVImbrOPDownload(imbr);
	    break;

	  case EDV_IMBR_OP_REFRESH:
	    EDVImbrOPRefresh(imbr);
	    break;
	  case EDV_IMBR_OP_REFRESH_ALL:
	    EDVImbrOPRefreshAll(imbr);
	    break;
	  case EDV_IMBR_OP_GOTO_PARENT:
	    EDVImbrOPGoToParent(imbr);
	    break;
	  case EDV_IMBR_OP_GOTO_HOME:
	    EDVImbrOPGoToHome(imbr);
	    break;
	  case EDV_IMBR_OP_STOP:
	    EDVImbrOpStop(imbr);
	    break;
	  case EDV_IMBR_OP_CONTINUE:
	    EDVImbrOpContinue(imbr);
	    break;
	  case EDV_IMBR_OP_PRESENTATION_MODE:
	    EDVImbrOpPresentationMode(imbr);
	    break;

	  case EDV_IMBR_OP_SHOW_TOOL_BAR:
	    if(core != 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);
	    }
	    break;
	  case EDV_IMBR_OP_SHOW_LOCATION_BAR:
	    if(core != 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);
	    }
	    break;
	  case EDV_IMBR_OP_SHOW_MOUNT_BAR:
	    if(core != 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);
	    }
	    break;
	  case EDV_IMBR_OP_SHOW_FIND_BAR:
	    if(core != 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);
	    }
	    break;
	  case EDV_IMBR_OP_SHOW_STATUS_BAR:
	    if(core != 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);
	    }
	    break;

	  case EDV_IMBR_OP_THUMBS_LIST_FILTER:
	    EDVImbrThumbsListFilter(imbr);
	    break;

	  case EDV_IMBR_OP_SHOW_HIDDEN_OBJECTS:
	    if(core != 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 != 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 != 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_MOUNT:
	    EDVImbrOPMount(imbr);
	    break;
	  case EDV_IMBR_OP_EJECT:
	    EDVImbrOPEject(imbr);
	    break;
	  case EDV_IMBR_OP_DEVICE_CHECK:
	    EDVRunDeviceCheck(core, dev, toplevel);
	    break;
	  case EDV_IMBR_OP_DEVICE_TOOLS:
	    EDVRunDeviceTools(core, dev, toplevel);
	    break;
	  case EDV_IMBR_OP_DEVICE_FORMAT:
	    EDVRunDeviceFormat(core, dev, toplevel);
	    break;

	  case EDV_IMBR_OP_MIME_TYPES:
	    EDVImbrMIMETypes(imbr);
	    break;
	  case EDV_IMBR_OP_DEVICES:
	    EDVMapDevicesListWin(core, toplevel);
	    break;

	  case EDV_IMBR_OP_NEW_BROWSER:
	    EDVNewBrowser(core, NULL);
	    break;
	  case EDV_IMBR_OP_NEW_IMBR:
	    EDVNewImbr(core, NULL);
	    break;
	  case EDV_IMBR_OP_NEW_ARCHIVER:
	    EDVNewArchiver(core, NULL, NULL);
	    break;
	  case EDV_IMBR_OP_RECYCLE_BIN:
	    EDVMapRecBin(core);
	    break;

	  case EDV_IMBR_OP_OPTIONS:
	    EDVMapOptionsWin(core, toplevel);
	    break;
	  case EDV_IMBR_OP_CUSTOMIZE:
	    EDVMapCustomizeWin(core, toplevel);
	    break;

	  case EDV_IMBR_OP_HELP_CONTENTS:
	    EDVHelp(core, "Contents", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_FILE_BROWSER:
	    EDVHelp(core, "File Browser", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_IMAGE_BROWSER:
	    EDVHelp(core, "Image Browser", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_ARCHIVER:
	    EDVHelp(core, "Archiver", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_RECYCLE_BIN:
	    EDVHelp(core, "Recycle Bin", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_KEYS_LIST:
	    EDVHelp(core, "Keys List", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_MIME_TYPES:
	    EDVHelp(core, "MIME Types", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_DEVICES:
	    EDVHelp(core, "Devices", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_COMMON_OPERATIONS:
	    EDVHelp(core, "Common Operations", toplevel);
	    break;
	  case EDV_IMBR_OP_HELP_ABOUT:
	    EDVAbout(core, 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 = (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 = (opid != NULL) ? opid->imbr : NULL;
	if(imbr == NULL)
	    return;

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


/*
 *	Close.
 */
void EDVImbrOPClose(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	EDVImbrSyncConfiguration(imbr);
	EDVImbrUnmap(imbr);
}

/*
 *	Exit.
 */
void EDVImbrOPExit(edv_imbr_struct *imbr)
{
	edv_core_struct *core;


	if(imbr == NULL)
	    return;

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

	EDVImbrSyncConfiguration(imbr);
	EDVImbrUnmap(imbr);

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


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

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

	EDVSyncDisks(imbr->core);

	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;
	GtkWidget *toplevel;
	tlist_struct *tlist;
	const edv_object_struct *obj;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;
	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == 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,
	    cmd,
	    EDVImbrCurrentLocation(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;


	if(imbr == NULL)
	    return;

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

	cfg_list = core->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, write_protect);
}

/*
 *	Delete Method: Recycle
 */
void EDVImbrOPDeleteMethodRecycle(edv_imbr_struct *imbr)
{
	cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

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

	cfg_list = core->cfg_list;

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

/*
 *	Delete Method: Purge
 */
void EDVImbrOPDeleteMethodPurge(edv_imbr_struct *imbr)
{
	cfg_item_struct *cfg_list;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

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

	cfg_list = core->cfg_list;

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


/*
 *	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_msg;
	edv_core_struct *core = imbr->core;
	if(core == NULL)
	    return;
	
#define DO_FREE_LOCALS	{	\
 g_free(new_path);		\
 new_path = NULL;		\
}

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

	/* Get current location as the path to create the new object
	 * at
	 */
	cur_path = 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_ERROR:
	  case EDV_OBJECT_TYPE_FILE:
	    obj_type_name = "file";
	    status = EDVObjCreateFile(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DIRECTORY:
	    obj_type_name = "directory";
	    status = EDVObjCreateDirectory(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_LINK:
	    obj_type_name = "symbolic link";
	    status = EDVObjCreateLink(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_BLOCK:
	    obj_type_name = "block device";
	    status = EDVObjCreateDeviceBlock(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
	    obj_type_name = "character device";
	    status = EDVObjCreateDeviceCharacter(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_FIFO:
	    obj_type_name = "fifo pipe";
	    status = EDVObjCreateFifo(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	  case EDV_OBJECT_TYPE_SOCKET:
	    obj_type_name = "socket";
	    status = EDVObjCreateSocket(
		core, cur_path, &new_path,
		toplevel, TRUE, TRUE, &yes_to_all
	    );
	    break;
	}

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

	/* Error creating new object? */
	if(status)
	{
	    error_msg = EDVObjCreateGetError();
	    if(!STRISEMPTY(error_msg))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    "Create Failed",
		    error_msg,
		    NULL,
		    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, 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, toolbar_item_struct *item
)
{
	if(imbr == NULL)
	    return;

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

/*
 *	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)
{
	if(imbr == NULL)
	    return;

	EDVImbrTListOpen(
	    imbr,
	    -1,				/* All selected object(s) */
	    0				/* Modifier keys */
	);
}

/*
 *	Open To.
 */
void EDVImbrOPOpenTo(edv_imbr_struct *imbr, toolbar_item_struct *item)
{
	if(imbr == NULL)
	    return;

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

/*
 *	Open With.
 */
void EDVImbrOPOpenWith(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	EDVImbrTListOpenWith(
	    imbr,
	    -1				/* All selected object(s) */
	);
}


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

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;

	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(
	    imbr->core,
	    sel_object, total_sel_objects,
	    toplevel
	);

	g_free(sel_object);
}

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

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;

	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(
	    imbr->core,
	    sel_object, total_sel_objects,
	    toplevel
	);

	g_free(sel_object);
}

/*
 *	Paste.
 */
void EDVImbrOPPaste(edv_imbr_struct *imbr)
{
	gboolean yes_to_all = FALSE;
	gint status = -1;
	const gchar *protocol;
	gchar *buf, *cur_location;
	GList *glist, *url_list = NULL;
	GtkWidget *toplevel;
	url_struct *url;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

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

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

	EDVImbrSyncData(imbr);

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

	/* Get current location */
	cur_location = STRDUP(EDVImbrCurrentLocation(imbr));

	if((buf == NULL) || (cur_location == NULL))
	{
	    g_free(cur_location);
	    g_free(buf);
	    return;
	}

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

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

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

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

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

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

		g_free(new_path);

		/* Skip handling of the rest of the objects on error
		 * (status != 0) and that the error was not a user
		 * response of no (status != -5)
		 */
		if(status && (status != -5))
		    break;
	    }
	    /* Download? */
	    else if(!g_strcasecmp(protocol, "http") ||
		    !g_strcasecmp(protocol, "ftp") ||
		    !g_strcasecmp(protocol, "https")
	    )
	    {
		EDVInternetDownloadObject(
		    core, url, cur_location, toplevel
		);
	    }
	}
	 
	/* Unmap progress dialog, it may have been mapped if any
	 * operations occured in the above loop
	 */
	ProgressDialogBreakQuery(TRUE);
	ProgressDialogSetTransientFor(NULL);

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

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


/*
 *      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(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;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

	/* Get the object operations dialog and create it as needed */
	d = imbr->obj_op_dlg;
	if(d == NULL)
	    imbr->obj_op_dlg = d = EDVObjOpDlgNew(core);
	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 the object operations dialog to move */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_MOVE,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    toplevel
	);

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

/*
 *	Copy.
 */
void EDVImbrOPCopy(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

	/* Get the object operations dialog and create it as needed */
	d = imbr->obj_op_dlg;
	if(d == NULL)
	    imbr->obj_op_dlg = d = EDVObjOpDlgNew(core);
	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 the object operations dialog to copy */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_COPY,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    toplevel
	);

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

/*
 *	Link.
 */
void EDVImbrOPLink(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

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

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

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

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

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

/*
 *	Rename.
 */
void EDVImbrOPRename(edv_imbr_struct *imbr)
{
	GtkWidget *toplevel;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

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

	/* Check and warn if write protect is enabled */
	if(EDVCheckWriteProtect(core, TRUE, 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;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

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

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

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

	/* Map the object operations dialog to change permissions */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHMOD,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    toplevel
	);

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

/*
 *	Change Ownership.
 */
void EDVImbrOPChOwn(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

	/* Get the object operations dialog and create it as needed */
	d = imbr->obj_op_dlg;
	if(d == NULL)
	    imbr->obj_op_dlg = d = EDVObjOpDlgNew(core);
	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 the object operations dialog to change ownership */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHOWN,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    toplevel
	);

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

/*
 *	Set Time Stamps.
 */
void EDVImbrOPChTime(edv_imbr_struct *imbr)
{
	edv_object_struct **sel_object;
	gint total_sel_objects;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_obj_op_dlg_struct *d;

	if(imbr == NULL)
	    return;

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

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

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

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

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

	/* Map the object operations dialog to set time stamps */
	EDVObjOpDlgMapValues(
	    d, EDV_OBJ_OP_DLG_OP_CHTIME,
	    sel_object, total_sel_objects,
	    EDVImbrCurrentLocation(imbr),
	    toplevel
	);

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

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

	if(imbr == NULL)
	    return;

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

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

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


	/* 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)
	{
	    tlist_struct *tlist = imbr->tlist;
	    gint thumb_num;
	    edv_object_struct *obj;

	    for(glist = tlist->selection;
		glist != NULL;
	        glist = g_list_next(glist)
	    )
	    {
		thumb_num = (gint)glist->data;
		obj = EDV_OBJECT(
		    TListGetThumbData(tlist, thumb_num)
		);
		if((obj != NULL) ? (obj->full_path != NULL) : FALSE)
		    paths_list = g_list_append(
			paths_list,
			STRDUP(obj->full_path)
		    );
	    }
	}

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

	npaths = g_list_length(paths_list);

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

	  default:
	    DO_FREE_LOCALS
	    return;
	    break;
	}


	EDVImbrSetBusy(imbr, TRUE);

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

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

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

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

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

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

		    objects_deleted++;
		}
	    }

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

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

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

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

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

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

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;

	EDVImbrSyncData(imbr);

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


/*
 *	Select All.
 */
void EDVImbrOPSelectAll(edv_imbr_struct *imbr)
{
	edv_core_struct *core;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

	core = imbr->core;
	if(core == 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;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

	core = imbr->core;
	if(core == 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;
	GList *glist, *selection;
	gint thumb, total_thumbs;
	tlist_struct *tlist;


	if(imbr == NULL)
	    return;

	core = imbr->core;
	if(core == 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);
}


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

	if(imbr == NULL)
	    return;

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

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

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

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

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

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

	g_free(parent_path);

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

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

	if(imbr == NULL)
	    return;

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

	/* Get home directory */
	home_dir = STRDUP(core->home_dir);
	if(STRISEMPTY(home_dir))
	{
	    EDVPlaySoundWarning(core);
	    EDVMessageWarning(
#if defined(PROG_LANGUAGE_SPANISH)
"Vaya A En Casa Guía Fallada",
"Incapaz de encontrar la guía de hogar, cerciórese que el\n\
ambiente HOME variable se pone.",
		NULL,
#elif defined(PROG_LANGUAGE_FRENCH)
"Aller au répertoire d'accueil échoué",
"Impossible de trouver le répertoire d'accueil, s'assurer que\n\
la variable d''environnement HOME est définie.",
		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.",
		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.",
		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.",
		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.",
		NULL,
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Dra Til Hjem Sviktet Katalog",
"Maktesløs finne hjemkatalogen, sjekker at miljøet variabel\n\
HOME setter.",
		NULL,
#else
"Go To Home Directory Failed",
"Unable to find the home directory, make sure that the\n\
environment variable HOME is set.",
		NULL,
#endif
		toplevel
	    );
	    g_free(home_dir);
	    return;
	}

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

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

	g_free(home_dir);

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

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


	if(imbr == NULL)
	    return;

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

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

	EDVImbrSetBusy(imbr, TRUE);

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

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

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

	EDVImbrSetBusy(imbr, FALSE);

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

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

	if(imbr == NULL)
	    return;

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

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

	EDVImbrSetBusy(imbr, TRUE);

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

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

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

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

	EDVImbrSetBusy(imbr, FALSE);

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


/*
 *	Download.
 */
void EDVImbrOPDownload(edv_imbr_struct *imbr)
{
	GtkWidget *toplevel;

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;

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


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

	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;

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

	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));

	/* Refresh toplevel */
	w = 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(toplevel, FALSE);
	EDVImbrSetBusy(imbr, FALSE);

	g_free(cur_path);
}

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

	if(imbr == NULL)
	    return;

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

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

	/* Refresh Image Browser */
	EDVImbrOPRefresh(imbr);
}

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

/*
 *	Presentation mode.
 */
void EDVImbrOpPresentationMode(edv_imbr_struct *imbr)
{
	if(imbr == NULL)
	    return;

	/* Need to notify the loading process to stop before we ente
	 * presentation mode
	 */
	if(imbr->loading_tocb > 0)
	    imbr->stop_count++;

	/* Switch to presentation mode */
	EDVPresentationModeEnterFromImbr(
	    imbr->core,
	    imbr
	);
}


/*
 *	Thumbs list filter.
 */
void EDVImbrThumbsListFilter(edv_imbr_struct *imbr)
{
	gchar **strv;
	gint strc;
	GtkWidget *toplevel;

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

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

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

	    EDVImbrOPRefresh(imbr);
	}

	PDialogDeleteAllPrompts();

	EDVImbrSetBusy(imbr, FALSE);
}

/*
 *      MIME Types.
 */
void EDVImbrMIMETypes(edv_imbr_struct *imbr)
{
	gchar *type_str = NULL;
	gint i;
	GList *glist;
	GtkWidget *toplevel;
	tlist_struct *tlist;
	edv_core_struct *core;
	if(imbr == NULL)
	    return;

	toplevel = imbr->toplevel;
	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return;

	glist = tlist->selection_end;
	i = (glist != NULL) ? (gint)glist->data : -1;
	if(i > -1)
	{
	    edv_object_struct *obj = EDV_OBJECT(
		TListGetThumbData(tlist, i)
	    );
	    if(obj != NULL)
		EDVMatchObjectTypeString(
		    core->mimetype, core->total_mimetypes,
		    obj->type,
		    obj->permissions,
		    obj->full_path,
		    &type_str
	    );
	}

	EDVMapMIMETypesListWin(core, type_str, toplevel);
}
