#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef HAVE_IMLIB
# include <Imlib.h>
#endif
#include <gtk/gtk.h>

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

#include "guiutils.h"
#include "guirgbimg.h"
#include "cdialog.h"
#include "progressdialog.h"
#include "fprompt.h"

#include "cfg.h"
#include "edvtypes.h"
#include "edvdate.h"
#include "edvid.h"
#include "edvobj.h"
#include "edvfop.h"
#include "edvmimetypes.h"
#include "imbr.h"
#include "imbrcb.h"
#include "imbrtlist.h"
#include "endeavour.h"
#include "edvopen.h"
#include "edvcb.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcfglist.h"
#include "config.h"


static void EDVImbrTListFPromptCancelCB(gpointer data);

static guint8 *EDVImbrTListConvertRGBToRGBA(
	const guint8 *rgb, const guint8 *alpha,
	gint width, gint height,
	gint rgb_bpl, gint alpha_bpl
);
static guint8 *EDVImbrTListLoadImageRGBATarga(
	const gchar *path,
	gint *width, gint *height, gint *bpl,
	GdkWindow *window			/* Reference window */
);
guint8 *EDVImbrTListLoadImageRGBA(
	tlist_struct *tlist, const gchar *path,
	gint *width, gint *height, gint *bpl,
	gboolean resize_for_thumb,		/* Resize for tlist thumb */
	gboolean no_enlarge,			/* No enlarge if smaller
						 * than req size */
	GdkWindow *window			/* Reference window */
);

static void EDVImbrTListSetThumbName(
	edv_core_struct *core_ptr, edv_imbr_struct *imbr,
	tlist_struct *tlist, edv_object_struct *obj,
	gint thumb_num
);

static gint EDVImbrTListAppendObject(
	edv_core_struct *core_ptr, edv_imbr_struct *imbr,
	tlist_struct *tlist, edv_object_struct *obj
);

gint EDVImbrTListFindThumbByPath(
	edv_imbr_struct *imbr, const gchar *path
);

void EDVImbrTListResetThumbs(edv_imbr_struct *imbr);
void EDVImbrTListDoUpdate(
	edv_imbr_struct *imbr, const gchar *path,
	gboolean update_status_bar
);

gint EDVImbrTListLoadIterate(
	edv_imbr_struct *imbr, gboolean update_status_bar,
	gboolean no_enlarge
);

void EDVImbrTListDoOpenObject(
	edv_imbr_struct *imbr, gint thumb_num, guint state
);
void EDVImbrTListDoOpenWithObject(
	edv_imbr_struct *imbr, gint thumb_num
);

static void EDVImbrTListFPromptRenameApplyCB(
	gpointer data, const gchar *value
);
void EDVImbrTListDoFPromptRename(
	edv_imbr_struct *imbr, gint thumb_num
);

void EDVImbrTListObjectAddedNotify(
	edv_imbr_struct *imbr, const gchar *path,
	const struct stat *lstat_buf
);
void EDVImbrTListObjectModifiedNotify(
	edv_imbr_struct *imbr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
);
void EDVImbrTListObjectRemovedNotify(
	edv_imbr_struct *imbr, const gchar *path
);

void EDVImbrTListMountNotify(
	edv_imbr_struct *imbr, edv_device_struct *dev_ptr,
	gboolean is_mounted
);


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


/*
 *	All purpose fprompt cancel callback.
 */
static void EDVImbrTListFPromptCancelCB(gpointer data)
{
	gpointer *cb_data = (gpointer *)data;
	if(cb_data == NULL)
	    return;

	g_free(cb_data);
}


/*
 *	Converts the specified RGB and Alpha image data to RGBA.
 */
static guint8 *EDVImbrTListConvertRGBToRGBA(
	const guint8 *rgb, const guint8 *alpha,
	gint width, gint height,
	gint rgb_bpl, gint alpha_bpl
)
{
	gint rgba_bpl;
	guint8 *rgba;

	if((rgb == NULL) || (width <= 0) || (height <= 0))
	    return(NULL);

	/* Calculate bytes per line for all buffers */
	if(rgb_bpl <= 0)
	    rgb_bpl = width * 3;
	if(alpha_bpl <= 0)
	    alpha_bpl = width * 1;
	rgba_bpl = width * 4;

	/* Allocate RGBA data */
	rgba = (guint8 *)g_malloc(rgba_bpl * height);
	if(rgba == NULL)
	    return(NULL);

	/* Alpha channel available? */
	if(alpha != NULL)
	{
	    guint8 *rgba_line, *rgba_line_end, *rgba_ptr, *rgba_end;
	    const guint8 *rgb_line, *rgb_ptr, *alpha_line, *alpha_ptr;

	    for(rgba_line	= rgba,
		rgba_line_end	= rgba_line + (rgba_bpl * height),
		rgb_line	= rgb,
		alpha_line	= alpha;
		rgba_line < rgba_line_end;
		rgba_line	+= rgba_bpl,
		rgb_line	+= rgb_bpl,
		alpha_line	+= alpha_bpl
	    )
	    {
		rgba_ptr	= rgba_line;
		rgba_end	= rgba_ptr + (width * 4);
		rgb_ptr		= rgb_line;
		alpha_ptr	= alpha_line;

		while(rgba_ptr < rgba_end)
		{
		    *rgba_ptr++ = *rgb_ptr++;
		    *rgba_ptr++ = *rgb_ptr++;
		    *rgba_ptr++ = *rgb_ptr++;
		    *rgba_ptr++ = *alpha_ptr++;
		}
	    }
	}
	else
	{
	    guint8 *rgba_line, *rgba_line_end, *rgba_ptr, *rgba_end;
	    const guint8 *rgb_line, *rgb_ptr;

	    for(rgba_line       = rgba,
		rgba_line_end   = rgba_line + (rgba_bpl * height),
		rgb_line        = rgb;
		rgba_line < rgba_line_end;
		rgba_line       += rgba_bpl,
		rgb_line        += rgb_bpl
	    )
	    {
		rgba_ptr        = rgba_line;
		rgba_end        = rgba_ptr + (width * 4);
		rgb_ptr         = rgb_line;

		while(rgba_ptr < rgba_end)
		{
		    *rgba_ptr++ = rgb_ptr[0];
		    *rgba_ptr++ = rgb_ptr[1];
		    *rgba_ptr++ = rgb_ptr[2];
		    *rgba_ptr++ = (
			(rgb_ptr[0] == 0xff) &&
			(rgb_ptr[1] == 0x00) &&
			(rgb_ptr[2] == 0xff)
		    ) ?
			0x00 : 0xff;

		    rgb_ptr += 3;
		}
	    }
	}

	return(rgba);
}


/*
 *	Called by EDVImbrTListLoadImageRGBA() to load a targa image.
 */
static guint8 *EDVImbrTListLoadImageRGBATarga(
	const gchar *path,
	gint *width, gint *height, gint *bpl,
	GdkWindow *window       /* Reference window */
)
{
	return(TgaReadFromFileFastRGBA(
	    path, width, height,
	    0xff000000	/* Transparent pixel rgba */
	));
}

/* 
 *	Returns a dynamically allocated image buffer loaded from the image
 *	file specified by path, the calling function must deallocate this
 *	buffer.
 *
 *	Can return NULL on error.
 */
guint8 *EDVImbrTListLoadImageRGBA(
	tlist_struct *tlist, const gchar *path,
	gint *width, gint *height, gint *bpl,
	gboolean resize_for_thumb,		/* Resize for tlist thumb */
	gboolean no_enlarge,			/* No enlarge if smaller
						 * than req size */
 	GdkWindow *window			/* Reference window */
)
{
#ifdef HAVE_IMLIB
	const gchar *name;
	gint swidth, sheight, twidth, theight;
	guint8 *rgba = NULL;
	ImlibImage *imlib_image;

	/* Reset returns */
	if(width != NULL)
	    *width = 0;
	if(height != NULL)
	    *height = 0;
	if(bpl != NULL)
	    *bpl = 0;

	if(STRISEMPTY(path))
	    return(NULL);

	name = EDVGetPathName(path);

	/* Begin checking extension to see if we should use a native
	 * image format loader instead of Imlib, since native loaders
	 * may be more efficient or provide more features
	 */

	/* Targa */
	if(EDVIsExtension(name, ".tga .targa"))
	{
	    rgba = EDVImbrTListLoadImageRGBATarga(
		path, width, height, bpl, window
	    );
	}
/* Add other image types that we should load alternatively (without
 * Imlib) here and make sure they return() the RGBA data pointer and do
 * not continue
 */
	/* Loaded image with native loaders? */
	if(rgba != NULL)
	{
	    return(rgba);
	}


	/* Load Imlib image */
	imlib_image = (imlib_handle != NULL) ?
	    Imlib_load_image(imlib_handle, (char *)path) : NULL;
	if(imlib_image == NULL)
	    return(NULL);

	/* Need to realize changes */
	Imlib_changed_image(imlib_handle, imlib_image);

	/* Get size of image */
	swidth = imlib_image->rgb_width;
	sheight = imlib_image->rgb_height;

	/* Need to resize for tlist thumb? */
	if(resize_for_thumb)
	{
	    TListQueryThumbPixmapSize(
		tlist, swidth, sheight, &twidth, &theight
	    );
	    /* Check if we do not want to enlarge if the image is
	     * smaller than the thumb
	     */
	    if(no_enlarge)
	    {
		if(swidth < twidth)
		    twidth = swidth;
		if(sheight < theight)
		    theight = sheight;
	    }
	}
	else
	{
	    twidth = swidth;
	    theight = sheight;
	}
	/* At this point swidth and sheight are the size of the
	 * image and twidth and theight are the size of the
	 * image needed to fit on the tlist thumb
	 */

	if((swidth <= 0) || (sheight <= 0) ||
	   (twidth <= 0) || (theight <= 0)
	)
	{
	    Imlib_destroy_image(imlib_handle, imlib_image);
	    return(NULL);
	}

	/* Need to resize? */
	if((swidth != twidth) || (sheight != theight))
	{
	    gint	t_rgb_bpl = twidth * 3,
			t_alpha_bpl = twidth * 1;

	    /* Allocate resized image buffers */
	    guint8	*t_rgb = (imlib_image->rgb_data != NULL) ?
		(guint8 *)g_malloc(t_rgb_bpl * theight) : NULL,
			*t_alpha = (imlib_image->alpha_data != NULL) ?
		(guint8 *)g_malloc(t_alpha_bpl * theight) : NULL;

	    if(t_rgb != NULL)
		GUIImageBufferResize(
		    3,
		    imlib_image->rgb_data,
		    swidth, sheight, -1,
		    t_rgb,
		    twidth, theight, -1
		);
	    if(t_alpha != NULL)
		GUIImageBufferResize(
		    1,
		    imlib_image->alpha_data,
		    swidth, sheight, -1,
		    t_alpha,
		    twidth, theight, -1
		);

	    /* Create RGBA data from resized thumb image data */
	    rgba = EDVImbrTListConvertRGBToRGBA(
		t_rgb, t_alpha,
		twidth, theight,
		-1, -1		/* Autocalculate bpl values */
	    );

	    g_free(t_rgb);
	    g_free(t_alpha);

	    /* Update image size */
	    swidth = twidth;
	    sheight = theight;
	}
	else
	{
	    /* Create RGBA data from the Imlib's RGB and Alpha data */
	    rgba = EDVImbrTListConvertRGBToRGBA(
		imlib_image->rgb_data, imlib_image->alpha_data,
		swidth, sheight,
		-1, -1		/* Autocalculate bpl values */
	    );
	}

	/* Destroy Imlib image */
	Imlib_destroy_image(imlib_handle, imlib_image);

	/* Update returns */
	if(width != NULL)
	    *width = swidth;
	if(height != NULL)
	    *height = sheight;
	if(bpl != NULL)
	    *bpl = swidth * 4;

	return(rgba);
#else
	return(NULL);
#endif	/* HAVE_IMLIB */
}



/*
 *      Sets the text for the given thumb.
 */
static void EDVImbrTListSetThumbName(
	edv_core_struct *core_ptr, edv_imbr_struct *imbr,
	tlist_struct *tlist, edv_object_struct *obj,
	gint thumb_num
)
{
	edv_object_type type = obj->type;
	const gchar *name = obj->name;

	if(name == NULL)
	    name = "(null)";

	/* Check if this not an image file supported by Imlib */
	if(!EDVCheckImlibImage(core_ptr, name))
	{
	    /* Probably not an image, so set icon */
	    GdkPixmap *pixmap, *pixmap_hid;
	    GdkBitmap *mask, *mask_hid;

	    /* Match icon with MIME Type */
	    EDVMatchObjectIcon(
		core_ptr->device, core_ptr->total_devices,
		core_ptr->mimetype, core_ptr->total_mimetypes,
		type,
		obj->full_path,
		obj->link_valid, obj->permissions,
		1,                  /* Medium icons */
		&pixmap, &mask,
		NULL, NULL,
		NULL, NULL,
		&pixmap_hid, &mask_hid
	    );
	    /* Check if an alternate icon state should be used */
	    switch(type)
	    {
	      case EDV_OBJECT_TYPE_UNKNOWN:
		break;
	      case EDV_OBJECT_TYPE_FILE:
		/* Hidden */
		if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
		break;
	      case EDV_OBJECT_TYPE_DIRECTORY:
                /* Hidden */
                if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
                {
                    pixmap = pixmap_hid;
                    mask = mask_hid;
                }
		/* Special directory notations? */
		if(!strcmp(name, ".."))
		{
		    pixmap = imbr->folder_parent_pixmap;
		    mask = imbr->folder_parent_mask;
		}
		/* Home directory? */
		if((!STRISEMPTY(obj->full_path) &&
		    !STRISEMPTY(core_ptr->home_dir)) ?
		    !strcmp(obj->full_path, core_ptr->home_dir) : FALSE
		)
		{
		    pixmap = imbr->folder_home_pixmap;
		    mask = imbr->folder_home_mask;
		}
		/* Directory not accessable? */
		if(!EDVIsObjectAccessable(core_ptr, obj))
		{
		    pixmap = imbr->folder_noaccess_pixmap;
		    mask = imbr->folder_noaccess_mask;
		}
		break;
	      case EDV_OBJECT_TYPE_LINK:
		break;
              case EDV_OBJECT_TYPE_DEVICE_BLOCK:
                /* Hidden */
                if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
                {
                    pixmap = pixmap_hid;
                    mask = mask_hid;
                }
		break;
              case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
                /* Hidden */
                if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
                {
                    pixmap = pixmap_hid;
                    mask = mask_hid;
                }
                break;
              case EDV_OBJECT_TYPE_FIFO:
                /* Hidden */
                if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
                {
                    pixmap = pixmap_hid;
                    mask = mask_hid;
                }
                break;
              case EDV_OBJECT_TYPE_SOCKET:
                /* Hidden */
                if(EDVIsObjectHidden(obj) && (pixmap_hid != NULL))
                {
                    pixmap = pixmap_hid;
                    mask = mask_hid;
                }
                break;
 	    }

	    /* Set thumb's pixmap */
	    TListSetPixmap(tlist, thumb_num, pixmap, mask);

	    /* Mark it as loaded so no pixmap gets loaded over it
	     * during any incidental calls to the loading process
	     */
	    TListSetLoadState(tlist, thumb_num, TLIST_LOAD_STATE_LOADED);
	}
	else
	{
	    /* Is an image, so do not set any pixmap on it and leave
	     * its load state as is
	     */
	}

	/* Set thumb's text */
	TListSetText(tlist, thumb_num, name);
}

/*
 *	Appends the disk object to the thumbs list. The given object will
 *	be transfered to the new thumb and should not be referenced again
 *	after this call.
 *
 *	Returns the thumb number that it was appended to or -1 on failure.
 */
static gint EDVImbrTListAppendObject(
	edv_core_struct *core_ptr, edv_imbr_struct *imbr,
	tlist_struct *tlist, edv_object_struct *obj
)
{
	gint thumb_num;

	if((core_ptr == NULL) || (imbr == NULL) ||
	   (tlist == NULL) || (obj == NULL)
	)
	{
	    EDVObjectDelete(obj);
	    return(-1);
	}

	/* Append a new Thumb */
	thumb_num = TListAppend(tlist, "");
	if(thumb_num < 0)
	{
	    EDVObjectDelete(obj);
	    return(-1);
	}

	/* Set new Thumb values */
	TListSetLoadState(tlist, thumb_num, TLIST_LOAD_STATE_NOT_LOADED);
	TListSetThumbDataFull(tlist, thumb_num, obj, EDVImbrTListItemDestroyCB);

	EDVImbrTListSetThumbName(
	    core_ptr, imbr, tlist, obj, thumb_num
	);

	return(thumb_num);
}

/*
 *	Returns the thumb index number who's disk object data structure's
 *	full path matches the given path.
 *
 *	Can return -1 on error or failed match.
 */
gint EDVImbrTListFindThumbByPath(
	edv_imbr_struct *imbr, const gchar *path
)
{
	gint i;
	tlist_struct *tlist;
	edv_object_struct *object;


	if((imbr == NULL) || (path == NULL))
	    return(-1);

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

	/* Iterate through all thumbs */
	for(i = 0; i < tlist->total_thumbs; i++)
	{
	    object = (edv_object_struct *)TListGetThumbData(tlist, i);
	    if(object == NULL)
		continue;

	    /* Full path not specified on disk object structure? */
	    if(object->full_path == NULL)
		continue;

	    /* Full paths match? */
	    if(!strcmp(object->full_path, path))
		return(i);
	}

	return(-1);
}

/*
 *      Updates all thumbs on the thumbs list by getting the thumb data
 *      and updating the thumb list's thumbs.
 *
 *      This is designed to be called whenever the displayed values
 *      of each thumb need to be set again from internal data, usually
 *      when a MIME Type has been added/modified/removed. This function
 *      should not be used to `refresh' the list (get new values of
 *      disk object structures), use EDVImbrTListDoUpdate()
 *      instead.
 */
void EDVImbrTListResetThumbs(edv_imbr_struct *imbr)
{
	gint i;
	tlist_thumb_struct *thumb;
	tlist_struct *tlist;
	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;

	for(i = 0; i < tlist->total_thumbs; i++)
	{
	    thumb = tlist->thumb[i];
	    if(thumb == NULL)
		continue;

	    obj = EDV_OBJECT(thumb->data);
	    if(obj != NULL)
		EDVImbrTListSetThumbName(
		    core_ptr, imbr, tlist, obj, i
		);
	}
}


/*
 *	Deletes all Thumbs in the Image Browser's Thumbs List and loads
 *	the thumbs (but not each image) from the specified directory
 *	path.
 */
void EDVImbrTListDoUpdate(
	edv_imbr_struct *imbr, const gchar *path,
	gboolean update_status_bar
)
{
	gboolean	hide_object_hidden,
			hide_object_noaccess,
			hide_object_nonimages;
	gint strc;
	gchar **strv;
	const cfg_item_struct *cfg_list;
	tlist_struct *tlist;
	edv_core_struct *core_ptr;

	if((imbr == NULL) || STRISEMPTY(path))
	    return;

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

	cfg_list = core_ptr->cfg_list;

	hide_object_hidden = !EDV_GET_B(
	    EDV_CFG_PARM_IMBR_SHOW_OBJECT_HIDDEN
	);
	hide_object_noaccess = !EDV_GET_B(
	    EDV_CFG_PARM_IMBR_SHOW_OBJECT_NOACCESS
	);
	hide_object_nonimages = !EDV_GET_B(
	    EDV_CFG_PARM_IMBR_SHOW_OBJECT_NONIMAGE
	);


	/* Begin clearing the thumbs list and getting new listing */

	TListFreeze(tlist);

	TListClear(tlist);

	/* Get listing of contents in the directory specified by path */
	strv = GetDirEntNames2(path, &strc);
	if(strv != NULL)
	{
	    gint i, stat_result, objects_loaded = 0;
	    gint last_progress_percent, progress_percent;
	    const gchar *name;
	    gchar *full_path;
	    edv_object_struct *obj;
	    struct stat stat_buf, lstat_buf;


	    /* Sort strings */
	    strv = StringQSort(strv, strc);
	    if(strv != NULL)
	    {
		/* Iterate through directory entry names and pick out
		 * just the directories for this first iteration
		 */
		last_progress_percent = -1;     /* None/undefined */
		progress_percent = 0;
		full_path = NULL;
		for(i = 0; i < strc; i++)
		{
#define FREE_AND_CONTINUE	{	\
 g_free(full_path);			\
 full_path = NULL;			\
					\
 /* Do not delete strv[i] on this first	\
  * iteration pass			\
  */					\
					\
 continue;				\
}

		    name = strv[i];
		    if(name == NULL)
			FREE_AND_CONTINUE

		    /* Skip special directory notations */
		    if(!strcmp(name, "."))
			FREE_AND_CONTINUE

		    /* Get full path to this object */
		    full_path = STRDUP(PrefixPaths(path, name));
		    if(full_path == NULL)
			FREE_AND_CONTINUE

		    /* Get this object's destination statistics */
		    if(stat((char *)full_path, &stat_buf))
			FREE_AND_CONTINUE
		    /* Skip if destination is not a directory */
		    if(!S_ISDIR(stat_buf.st_mode))
			FREE_AND_CONTINUE

		    /* Object's destination is a directory, now get
		     * the object's local stats
		     */
		    lstat((char *)full_path, &lstat_buf);

		    /* Create a new object */
		    obj = EDVObjectNew();
		    if(obj != NULL)
		    {
			gint thumb_num;

			EDVObjectSetPath(obj, full_path);
			EDVObjectSetStat(obj, &lstat_buf);
			obj->link_valid = TRUE;

			/* Begin filter checks */
			if((hide_object_hidden ?
			    EDVIsObjectHidden(obj) : FALSE) ||
			   (hide_object_noaccess ?
			    !EDVIsObjectAccessable(core_ptr, obj) : FALSE)
			)
			{
			    EDVObjectDelete(obj);
			    obj = NULL;
			    thumb_num = -1;
			}
			else
			{
			    /* Append thumb using this object, the
			     * object will be transfered
			     */
			    thumb_num = EDVImbrTListAppendObject(
				core_ptr, imbr, tlist, obj
			    );
			    obj = NULL;

			    objects_loaded++;	/* Count this as an object loaded */
			}
		    }

#if 0
/* Do not update progress when getting listing of thumbs as disk objects,
 * since progress should only be updated when each thumb's image is loaded.
 */
		    /* Update progress? */
		    if(update_status_bar && (strc > 0))
		    {
			progress_percent = objects_loaded * 100 / strc;
			if(progress_percent > last_progress_percent)
			{
			    EDVStatusBarProgress(
				imbr->status_bar,
				(gfloat)objects_loaded / (gfloat)strc,
				TRUE
			    );
			    progress_percent = last_progress_percent;
			}
		    }
#endif

		    FREE_AND_CONTINUE
#undef FREE_AND_CONTINUE
		}

		/* Now iterate through all other objects, skipping
		 * directories
		 *
		 * Note that this iteration will also delete all the
		 * strings in strv
		 */
		full_path = NULL;
		for(i = 0; i < strc; i++)
		{
#define FREE_AND_CONTINUE	{	\
 g_free(full_path);			\
 full_path = NULL;			\
					\
 g_free(strv[i]);			\
 strv[i] = NULL;			\
					\
 continue;				\
}
		    name = strv[i];
		    if(name == NULL)
			FREE_AND_CONTINUE

		    /* Skip special directory notations */
		    if(!strcmp(name, "."))
			FREE_AND_CONTINUE

		    /* Get full path to this object */
		    full_path = STRDUP(PrefixPaths(path, name));
		    if(full_path == NULL)
			FREE_AND_CONTINUE

		    /* Get local stats of this object */
		    if(lstat((char *)full_path, &lstat_buf))
			FREE_AND_CONTINUE

		    /* Get destination stats of this object */
		    stat_result = stat((char *)full_path, &stat_buf);
		    if(!stat_result)
		    {
			/* Check if object destination is a directory */
			if(S_ISDIR(stat_buf.st_mode))
			    FREE_AND_CONTINUE
		    }

		    /* Create new object */
		    obj = EDVObjectNew();
		    if(obj != NULL)
		    {
			gint thumb_num;

			EDVObjectSetPath(obj, full_path);
			EDVObjectSetStat(obj, &lstat_buf);
			obj->link_valid = stat_result ? FALSE : TRUE;

			/* Begin filter checks */
			if((hide_object_hidden ?
			    EDVIsObjectHidden(obj) : FALSE) ||
/*
			   (hide_object_noaccess ?
			    !EDVIsObjectAccessable(core_ptr, obj) : FALSE) ||
 */
			   (hide_object_nonimages ?
			    !EDVCheckImlibImage(core_ptr, name) : FALSE)
			)
			{
			    EDVObjectDelete(obj);
			    obj = NULL;
			    thumb_num = -1;
			}
			else
			{
			    /* Append thumb using this object, the
			     * object will be transfered
			     */
			    thumb_num = EDVImbrTListAppendObject(
				core_ptr, imbr, tlist, obj
			    );
			    obj = NULL;

			    objects_loaded++;	/* Count this as an object loaded */
			}
		    }

#if 0
/* Do not update progress when getting listing of thumbs as disk objects,
 * since progress should only be updated when each thumb's image is loaded.
 */
		    /* Update progress? */
		    if(update_status_bar && (strc > 0))
		    {
			progress_percent = objects_loaded * 100 / strc;
			if(progress_percent > last_progress_percent)
			{
			    EDVStatusBarProgress(
				imbr->status_bar,
				(gfloat)objects_loaded / (gfloat)strc,
				TRUE
			    );
			    progress_percent = last_progress_percent;
			}
		    }
#endif

		    FREE_AND_CONTINUE
#undef FREE_AND_CONTINUE
		}

		/* At this point all the strings in strv have been
		 * deleted
		 */
	    }

	    /* Delete the string pointer array, each string should
	     * already be deleted
	     */
	    g_free(strv);
	    strv = NULL;
	}

	TListThaw(tlist);
}

/*
 *	Loads the Image Browser Thumbs List's next unloaded thumb.
 *
 *	The next unloaded thumb will be the next thumb from the
 *	current scroll position. If there are no unloaded thumbs there
 *	then a second pass will check if there is an unloaded thumb
 *	starting from the top.
 *
 *	Returns:
 *
 *	0	All thumbs loaded
 *	1	Thumb loaded successfully but still might be more thumbs
 *		that need to be loaded
 *	-1	General error
 *	-2	Ambiguous, corrupt image, or unsupported format
 *	-3	Systems error
 */
gint EDVImbrTListLoadIterate(
	edv_imbr_struct *imbr, gboolean update_status_bar,
	gboolean no_enlarge
)
{
	gint starting_thumb_num, thumb_num, thumb_to_load_num = -1;
	gint thumbs_already_loaded = 0;
	GtkWidget *toplevel;
	edv_object_struct *obj;
	edv_status_bar_struct *sb = (imbr != NULL) ? imbr->status_bar : NULL;
	tlist_thumb_struct *thumb, *thumb_to_load = NULL;
	edv_core_struct *core_ptr;
	tlist_struct *tlist = (imbr != NULL) ? imbr->tlist : NULL;

#define DO_RESET_PROGRESS	{		\
 if(update_status_bar) {			\
  EDVStatusBarProgress(sb, 0.0f, FALSE);	\
  EDVStatusBarMessage(sb, NULL, FALSE);		\
 }						\
}

	if(tlist == NULL)
	{
	    DO_RESET_PROGRESS
	    return(-1);
	}

	toplevel = imbr->toplevel;
	core_ptr = EDV_CORE(imbr->core_ptr);
	if((toplevel == NULL) || (core_ptr == NULL))
	{
	    DO_RESET_PROGRESS
	    return(-1);
	}

	/* Count number of thumbs already loaded */
	if(update_status_bar)
	{
	    for(thumb_num = 0;
		thumb_num < tlist->total_thumbs;
		thumb_num++
	    )
	    {
		thumb = tlist->thumb[thumb_num];
		if(thumb == NULL)
		{
		    thumbs_already_loaded++;	/* Count NULL's as loaded */
		    continue;
		}

		/* This thumb loaded? */
		if(thumb->load_state != TLIST_LOAD_STATE_NOT_LOADED)
		    thumbs_already_loaded++;
	    }
	}

	/* Calculate first starting thumb at scroll position */
	TListGetSelection(tlist, 0, 0, &starting_thumb_num, NULL, NULL);

	/* Look for thumb to load starting from the scroll position
	 * to the end of the list
	 */
	if(starting_thumb_num > -1)
	{
	    for(thumb_num = starting_thumb_num;
		thumb_num < tlist->total_thumbs;
		thumb_num++
	    )
	    {
		thumb = tlist->thumb[thumb_num];
		if(thumb == NULL)
		    continue;

		if(thumb->load_state == TLIST_LOAD_STATE_NOT_LOADED)
		{
		    thumb_to_load = thumb;
		    thumb_to_load_num = thumb_num;
		    break;
		}
	    }
	}
	/* If no thumbs needed to be loaded starting from
	 * starting_thumb_num then start loading from thumb 0
	 */
	if((thumb_to_load == NULL) &&
	   (starting_thumb_num <= tlist->total_thumbs)
	)
	{
	    for(thumb_num = 0; thumb_num < starting_thumb_num; thumb_num++)
	    {
		thumb = tlist->thumb[thumb_num];
		if(thumb == NULL)
		    continue;

		if(thumb->load_state == TLIST_LOAD_STATE_NOT_LOADED)
		{
		    thumb_to_load = thumb;
		    thumb_to_load_num = thumb_num;
		    break;
		}
	    }
	}

	/* No more thumbs need to be loaded? */
	if((thumb_to_load == NULL) || (thumb_to_load_num < 0))
	{
	    if(update_status_bar)
	    {
		EDVStatusBarProgress(sb, 0.0f, FALSE);
		EDVStatusBarMessage(
		    sb,
		    "Loading done",
		    FALSE
		);
	    }
	    return(0);
	}

	/* Get object from thumb data */
	obj = EDV_OBJECT(thumb_to_load->data);
	if((obj != NULL) ? STRISEMPTY(obj->full_path) : TRUE)
	{
	    thumb_to_load->load_state = TLIST_LOAD_STATE_FAILED;
	    DO_RESET_PROGRESS
	    return(-1);
	}

	/* Load image for this thumb */
	if(TRUE)
	{
	    gint width, height, bpl;
	    guint8 *data_rgba;
	    const gchar	*name = obj->name,
			*full_path = obj->full_path;
	    GdkWindow *window = toplevel->window;

	    if(update_status_bar)
	    {
		/* Format status bar message */
		gchar *buf = g_strdup_printf(
		    "Loading \"%s\" (%ld byte%s)",
		    name, obj->size,
		    (obj->size == 1) ? "" : "s"
		);

		/* Set status bar message */
		EDVStatusBarMessage(sb, buf, FALSE);
		g_free(buf);

		/* Set status bar progress */
		if(tlist->total_thumbs > 0)
		    EDVStatusBarProgress(
			sb,
			(gfloat)thumbs_already_loaded /
			    (gfloat)tlist->total_thumbs,
			FALSE
		    );
	    }

/* Check if image is cached */

	    /* Load image to RGBA data */
	    data_rgba = EDVImbrTListLoadImageRGBA(
		tlist, full_path,
		&width, &height, &bpl,
		TRUE,			/* Resize for tlist thumb */
		no_enlarge,		/* Do not enlarge? */
		window
	    );
	    if(data_rgba == NULL)
	    {
		/* Failed to load image for this thumb, mark the thumb's
		 * load state as failed and set the badimage icon for
		 * this thumb's pixmap
		 */
		thumb_to_load->load_state = TLIST_LOAD_STATE_FAILED;
		TListSetPixmap(
		    tlist, thumb_to_load_num,
		    imbr->file_badimage_pixmap,
		    imbr->file_badimage_mask
		);

		DO_RESET_PROGRESS
		return(-1);
	    }

	    /* Update load state on thumb */
	    thumb_to_load->load_state = TLIST_LOAD_STATE_LOADED;

	    /* Set image to thumb */
	    TListSetRGBA(
		tlist, thumb_to_load_num,
		width, height, bpl,
		GDK_RGB_DITHER_NORMAL,
		data_rgba,
		no_enlarge
	    );

	    /* Delete image RGBA data */
	    g_free(data_rgba);

	    /* Report one thumb loaded */
	    return(1);
	}

	return(0);
}


/*
 *	Procedure to open the object specified by thumb_num
 */
void EDVImbrTListDoOpenObject(
	edv_imbr_struct *imbr, gint thumb_num, guint state
)
{
	const gchar *name;
	gchar *full_path;
	struct stat stat_buf;
	const edv_object_struct *obj;
	edv_core_struct *core_ptr;
	tlist_struct *tlist = (imbr != NULL) ? imbr->tlist : NULL;
	if(tlist == NULL)
	    return;

	obj = EDV_OBJECT(TListGetThumbData(tlist, thumb_num));
	core_ptr = EDV_CORE(imbr->core_ptr);
	if((obj == NULL) || (core_ptr == NULL))
	    return;

	/* Check name of object to check for special notations */
	name = obj->name;
	if(!STRISEMPTY(name))
	{
	    if(!strcmp(name, "."))
	    {
		full_path = NULL;
	    }
	    else if(!strcmp(name, ".."))
	    {
		full_path = g_dirname(
		    EDVImbrCurrentLocation(imbr)
		);
	    }
	    else if(!STRISEMPTY(core_ptr->home_dir) ?
		!strcmp(name, "~") : FALSE
	    )
	    {
		full_path = STRDUP(core_ptr->home_dir);
	    }      
	    else
	    {
		full_path = STRDUP(obj->full_path);
	    }
	}
	else
	{
	    /* No name available, get copy of full path */
	    full_path = STRDUP(obj->full_path);
	}
	if(full_path == NULL)
	    return;

	/* Get stats of destination object, the object structure may
	 * have that information already but we want to ensure we have
	 * the most up to date information
	 */
	if(stat(full_path, &stat_buf))
	{
	    g_free(full_path);
	    return;
	}

	/* Is destination a directory? */
	if(S_ISDIR(stat_buf.st_mode))
	{
	    /* Change directory */
	    EDVImbrSetBusy(imbr, TRUE);
	    EDVImbrSelectPath(imbr, full_path);
	    EDVImbrUpdateMenus(imbr);
	    EDVImbrSetBusy(imbr, FALSE);
	}
	else
	{
	    gchar	*stdout_path_rtn = NULL,
			*stderr_path_rtn = NULL;
	    const gchar *command_name = NULL;

	    if(state & GDK_CONTROL_MASK)
		command_name = "edit";
	    else if(state & GDK_SHIFT_MASK)
		command_name = "edit";

	    EDVImbrSetBusy(imbr, TRUE);
	    EDVOpenObjectPath(
		core_ptr, full_path,
		command_name,		/* Command name */
		imbr->toplevel, TRUE,
		&stdout_path_rtn, &stderr_path_rtn
	    );
	    EDVImbrSetBusy(imbr, FALSE);

	    g_free(stdout_path_rtn);
	    g_free(stderr_path_rtn);
	}

	/* Delete copy of full path */
	g_free(full_path);
}

/*
 *      Procedure to `open with' the object specified by thumb_num
 */
void EDVImbrTListDoOpenWithObject(
	edv_imbr_struct *imbr, gint thumb_num
)
{
	tlist_struct *tlist;
	edv_object_struct *object;
	const gchar *name;
	gchar *full_path;
	struct stat stat_buf;


	if(imbr == NULL)
	    return;

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


	/* Get object from row data */
	object = (edv_object_struct *)TListGetThumbData(tlist, thumb_num);
	if(object == NULL)
	    return;

	/* Check name of object for special notations */
	name = object->name;
	if(name != NULL)
	{
	    if(!strcmp(name, "."))
	    {
		full_path = NULL;
	    }
	    else if(!strcmp(name, ".."))
	    {
		full_path = g_dirname(
		    EDVImbrCurrentLocation(imbr)
		);
	    }
	    else
	    {
		full_path = STRDUP(object->full_path);
	    }
	}
	else
	{
	    /* No name available, get copy of full path */
	    full_path = STRDUP(object->full_path);
	}
	if(full_path == NULL)
	    return;

	/* Get stats of destination object, the object structure may
	 * have that information already but we want to ensure we have
	 * the most up to date information
	 */
	if(stat(full_path, &stat_buf))
	{
	    g_free(full_path);
	    return;
	}

	if(TRUE)
	{
	    gchar       *stdout_path_rtn = NULL,
			*stderr_path_rtn = NULL;

	    EDVOpenWithObjectPath(
		EDV_CORE(imbr->core_ptr), full_path,
		NULL,                   /* Command name */
		imbr->toplevel, TRUE,
		&stdout_path_rtn, &stderr_path_rtn
	    );

	    g_free(stdout_path_rtn);
	    g_free(stderr_path_rtn);
	}

	/* Delete copy of full path */
	g_free(full_path);
}


/*
 *      FPrompt apply callback, set in EDVImbrTListDoFPromptRename().
 */
static void EDVImbrTListFPromptRenameApplyCB(
	gpointer data, const gchar *value
)
{
	edv_imbr_struct *imbr;
	tlist_struct *tlist;
	gint thumb_num;
	gpointer *cb_data = (gpointer *)data;
	if(cb_data == NULL)
	    return;

	/* Get callback data */
	imbr = EDV_IMBR(cb_data[0]);
	tlist = TLIST(cb_data[1]);
	thumb_num = (gint)cb_data[2];

	/* Inputs valid? */
	if((imbr != NULL) && (tlist != NULL) && (thumb_num >= 0) &&
	   (value != NULL)
	)
	{
	    edv_core_struct *core_ptr = EDV_CORE(imbr->core_ptr);
	    edv_object_struct *object = EDV_OBJECT(
		TListGetThumbData(tlist, thumb_num)
	    );


	    /* Check if the selected object's disk object structure is
	     * valid
	     */
	    if((object != NULL) ? (object->full_path != NULL) : FALSE)
	    {
		gchar *old_full_path = STRDUP(object->full_path);
		gchar *new_obj = NULL;
		const gchar *error_mesg;
		gboolean yes_to_all = FALSE;
		gint status;


		/* Perform rename */
		status = EDVFOPRename(
		    core_ptr, old_full_path, value,
		    &new_obj, imbr->toplevel,
		    FALSE, TRUE,
		    &yes_to_all
		);

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

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

		/* Got new object full path name (implying success)? */
		if((new_obj != NULL) && (old_full_path != NULL))
		{
		    struct stat lstat_buf;
		    const gchar	*new_child = EDVGetPathName(new_obj),
				*old_child = EDVGetPathName(old_full_path);

		    /* Get new local statistics for the renamed object */
		    if(!lstat(new_obj, &lstat_buf))
		    {
			gchar *buf = g_strdup_printf(
			    "Object \"%s\" renamed to \"%s\"",
			    old_child, new_child
			);
			EDVStatusBarMessage(
			    imbr->status_bar, buf, FALSE
			);
			g_free(buf);

			/* Emit object modified signal to all windows */
			EDVObjectModifiedEmit(
			    core_ptr, old_full_path,
			    new_obj, &lstat_buf
			);
		    }
		}
		else
		{
		    /* Did not get new object path new_obj, implying failed */
		    EDVStatusBarMessage(
			imbr->status_bar, "Rename object failed", FALSE
		    );
		}

		/* The disk object structure may now be invalid if the
		 * object modified signal was emitted.
		 */
		object = NULL;


		/* Deallocate coppies of paths */
		g_free(new_obj);
		g_free(old_full_path);
	    }
	}

	/* Deallocate callback data, it is no longer needed */
	g_free(cb_data);
	cb_data = NULL;
}

/*
 *	Procedure to map the floating prompt for renaming of the row
 *	specified by row.
 *
 *      If the column is -1 then it implies any column is valid.
 */
void EDVImbrTListDoFPromptRename(
	edv_imbr_struct *imbr, gint thumb_num
)
{
	gint cx, cy, px, py, pwidth, pheight;
	edv_object_struct *object;
	edv_core_struct *core_ptr;
	tlist_struct *tlist;

	if((imbr == NULL) || (thumb_num < 0) || FPromptIsQuery())
	    return;

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

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

	EDVImbrSyncData(imbr);

	/* Make sure given thumb index is in bounds */
	if((thumb_num < 0) || (thumb_num >= tlist->total_thumbs))
	    return;

	/* Get position of given thumb */
	if(!TListGetPosition(tlist, thumb_num, &cx, &cy))
	    return;

	pwidth = tlist->thumb_width;
	pheight = 20;

	/* Get root window relative coordinates */
	px = 0;
	py = 0;
	if(tlist->list_da != NULL)
	    gdk_window_get_deskrelative_origin(
		tlist->list_da->window, &px, &py
	    );
	px += cx;
	py += cy + tlist->thumb_height - pheight - tlist->thumb_border - 4;

	/* Get disk object structure thumb data */
	object = (edv_object_struct *)TListGetThumbData(tlist, thumb_num);
	if(object == NULL)
	    return;

	/* Check if object name is a special notation that should not be
	 * allowed to be renamed.
	 */
	if(object->name != NULL)
	{
	    const gchar *name = object->name;
	    if(!strcmp(name, ".") || !strcmp(name, "..") ||
	       !strcmp(name, "/")
	    )
		return;
	}

	if(TRUE)
	{
	    gpointer *cb_data = (gpointer *)g_malloc0(
		3 * sizeof(gpointer)
	    );
	    gchar *value = STRDUP(object->name);

	    /* Set up callback data */
	    if(cb_data != NULL)
	    {
		cb_data[0] = imbr;
		cb_data[1] = tlist;
		cb_data[2] = (gpointer)thumb_num;
	    }

	    /* Map floating prompt to change values */
	    FPromptSetTransientFor(imbr->toplevel);
	    FPromptSetPosition(px, py);
	    FPromptMapQuery(
		NULL,			/* No label */
		value,			/* Initial value */
		NULL,			/* No tooltip message */
		FPROMPT_MAP_NO_MOVE,	/* Map code */
		pwidth, -1,		/* Width and height */
		0,			/* Flags */
		(gpointer)cb_data,	/* Callback data */
		NULL,			/* No browse callback */
		EDVImbrTListFPromptRenameApplyCB,
		EDVImbrTListFPromptCancelCB
	    );

	    /* Do not reference cb_data after this call, it will be passed
	     * to the callbacks where it will be deallocated.
	     */
	    cb_data = NULL;

	    /* Delete original value */
	    g_free(value);
	}
}


/*
 *      This should be called whenever a new object has been added, it
 *      will add a new row as needed to represent the new object.
 *
 *      The given path must be an absolute path to the object.
 */
void EDVImbrTListObjectAddedNotify(
	edv_imbr_struct *imbr, const gchar *path,
	const struct stat *lstat_buf
)
{
	gchar *cur_path, *parent_path;
	gint thumb_num;
	tlist_struct *tlist;
	edv_core_struct *core_ptr;
	edv_object_struct *object;
	gint stat_result;
	struct stat stat_buf;


	if((imbr == NULL) || (path == NULL) || (lstat_buf == NULL))
	    return;

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

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

#define DO_FREE_LOCALS	{	\
 g_free(cur_path);		\
 cur_path = NULL;		\
 g_free(parent_path);		\
 parent_path = NULL;		\
}
	/* Get parent of the given path and current location */
	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));
	parent_path = g_dirname(path);
	if((cur_path == NULL) || (parent_path == NULL))
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Is location of what contents list is displaying is different
	 * from the location of the new object to add?
	 */
	if(strcmp(cur_path, parent_path))
	{
	    DO_FREE_LOCALS
	    return;
	}


	/* Get destination stats */
	stat_result = stat(path, &stat_buf);


	/* Check if the new path of the object to add reffers to an object
	 * that already exists in the list.
	 */
	thumb_num = EDVImbrTListFindThumbByPath(imbr, path);
	if((thumb_num >= 0) && (thumb_num < tlist->total_thumbs))
	{
	    /* The new object to add already exists in the list, so just
	     * update the row.
	     */

	    /* Get disk object structure from matched thumb */
	    object = (edv_object_struct *)TListGetThumbData(tlist, thumb_num);
	    if(object != NULL)
	    {
		/* Update disk object structure */
		EDVObjectSetPath(object, path);
		EDVObjectSetStat(object, lstat_buf);
		object->link_valid = stat_result ? FALSE : TRUE;

		/* Update the thumb's displayed name */
		EDVImbrTListSetThumbName(
		    core_ptr, imbr, tlist, object, thumb_num
		);
	    }
	}
	else
	{
	    /* Create a new disk object structure */
	    object = EDVObjectNew();
	    if(object != NULL)
	    {
		/* Set disk object structure */
		EDVObjectSetPath(object, path);
		EDVObjectSetStat(object, lstat_buf);
		object->link_valid = stat_result ? FALSE : TRUE;

		TListFreeze(tlist);

		/* Append thumb using this disk object structure */
		thumb_num = EDVImbrTListAppendObject(
		    core_ptr, imbr, tlist, object
		);
		/* The object structure is now invalid after passing
		 * it to EDVImbrTListAppendObject().
		 */
		object = NULL;

		TListThaw(tlist);
	    }

	    /* Need to (re)queue loading process so that any newly added
	     * thumb gets it's image loaded.
	     */
	    EDVImbrQueueLoadingProcess(imbr);
	}

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

/*
 *      This should be called whenever a object has been modified, it will
 *      search for the object and then reupdate the matching thumb.
 *
 *      The given path must be an absolute path to the object and must be
 *      the path of the object's original name. The new_path must be an
 *      absolute path to the object at its new name, the new_path may be
 *      NULL if there was no name change.
 */
void EDVImbrTListObjectModifiedNotify(
	edv_imbr_struct *imbr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
)
{
	gchar *cur_path, *parent_path;
	gint thumb_num;
	tlist_struct *tlist;
	edv_core_struct *core_ptr;
	edv_object_struct *object;
	gint stat_result;
	struct stat stat_buf;


	if((imbr == NULL) || (path == NULL) || (lstat_buf == NULL))
	    return;

	if(new_path == NULL)
	    new_path = path;

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

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

#define DO_FREE_LOCALS	{	\
 g_free(cur_path);		\
 cur_path = NULL;		\
 g_free(parent_path);		\
 parent_path = NULL;		\
}
	/* Get parent of the given path and current location */
	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));
	parent_path = g_dirname(new_path);
	if((cur_path == NULL) || (parent_path == NULL))
	{
	    DO_FREE_LOCALS
	    return;
	}


	/* Get destination stats */
	stat_result = stat(new_path, &stat_buf);


	/* Is given path that has been modified the same as the current
	 * location?
	 */
	if(!strcmp(new_path, cur_path))
	{
	    /* Reget listing */
	    EDVImbrTListDoUpdate(imbr, new_path, TRUE);
	}
	else
	{
	    /* Look for a thumb who's disk object full path matches the
	     * old path.
	     */
	    thumb_num = EDVImbrTListFindThumbByPath(imbr, path);
	    if((thumb_num >= 0) && (thumb_num < tlist->total_thumbs))
	    {
		/* Get disk object structure from matched thumb */
		object = (edv_object_struct *)TListGetThumbData(tlist, thumb_num);
		if(object != NULL)
		{
		    /* Update disk object structure */
		    EDVObjectSetPath(object, new_path);		/* Use new_path */
		    EDVObjectSetStat(object, lstat_buf);
		    object->link_valid = stat_result ? FALSE : TRUE;

		    /* Update the thumb's displayed name */
		    EDVImbrTListSetThumbName(
			core_ptr, imbr, tlist, object, thumb_num
		    );
		}
	    }
	}

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}

/*
 *      This should be called whenever a object has been removed, it will
 *      search for the object and then remove the matching thumb.
 *
 *      The given path must be an absolute path to the object.
 */
void EDVImbrTListObjectRemovedNotify(
	edv_imbr_struct *imbr, const gchar *path
)
{
	gchar *cur_path, *parent_path;
	gint thumb_num;
	tlist_struct *tlist;


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

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

#define DO_FREE_LOCALS	{	\
 g_free(cur_path);		\
 cur_path = NULL;		\
 g_free(parent_path);		\
 parent_path = NULL;		\
}
	/* Get parent of the given path and current location */
	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));
	parent_path = g_dirname(path);
	if((cur_path == NULL) || (parent_path == NULL))
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Is given path that has been removed the same as the current
	 * location?
	 */
	if(!strcmp(path, cur_path))
	{
	    /* Clear thumbs list */
	    TListFreeze(tlist);
	    TListClear(tlist);
	    TListThaw(tlist);
	}
	else
	{
	    /* Look for a thumb that matches the given path */
	    thumb_num = EDVImbrTListFindThumbByPath(imbr, path);
	    if((thumb_num >= 0) && (thumb_num < tlist->total_thumbs))
	    {
		/* Remove matched thumb */
		TListFreeze(tlist);
		TListRemove(tlist, thumb_num);
		TListThaw(tlist);;
	    }
	}

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}


/*
 *      This should be called whenever a device has been mounted or
 *	unmounted.
 */
void EDVImbrTListMountNotify(
	edv_imbr_struct *imbr, edv_device_struct *dev_ptr,
	gboolean is_mounted
)
{
	gchar *cur_path, *mount_path;
	tlist_struct *tlist;


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

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

#define DO_FREE_LOCALS	{	\
 g_free(cur_path);		\
 cur_path = NULL;		\
 g_free(mount_path);		\
 mount_path = NULL;		\
}
	/* Get coppies of current location and mount paths */
	cur_path = STRDUP(EDVImbrCurrentLocation(imbr));
	mount_path = STRDUP(dev_ptr->mount_path);
	if((cur_path == NULL) || (mount_path == NULL))
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Need to simplify coppies of current and mount paths */
	EDVSimplifyPath(cur_path);
	EDVSimplifyPath(mount_path);


	/* Check if mount path was the current location, if it was then
	 * the contents clist needs to be updated
	 */
	if(!strcmp(cur_path, mount_path))
	{
	    /* Reget listing */
	    EDVImbrTListDoUpdate(imbr, cur_path, TRUE);
	}

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}
