#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#if defined(HAVE_REGEX)
# include <regex.h>
#else
# include <fnmatch.h>
#endif
#include <gtk/gtk.h>

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

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

#include "edv_types.h"
#include "edv_date.h"
#include "cfg.h"
#include "edv_id.h"
#include "edv_obj.h"
#include "edv_obj_op.h"
#include "edv_mime_type.h"
#include "browser.h"
#include "browser_cb.h"
#include "browser_dir_tree.h"
#include "browser_contents_list.h"
#include "endeavour2.h"
#include "edv_open.h"
#include "edv_cb.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"

#include "edv_cfg_list.h"
#include "config.h"


static edv_object_struct *EDVBrowserNewErrorObject(
	const gchar *path
);

/* Callbacks */
static void EDVBrowserContentsFPromptCancelCB(gpointer data);

/* Column Setting */
static void EDVBrowserContentsResetColumns(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist
);

/* Cell Setting */
static void EDVBrowserContentsSetCellName(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellSize(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	gboolean hide_dir_size, gboolean hide_link_size
);
static void EDVBrowserContentsSetCellType(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellPermissions(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column, gboolean hide_link_permissions
);
static void EDVBrowserContentsSetCellOwner(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellGroup(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellDateAccess(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
);
static void EDVBrowserContentsSetCellDateModified(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
);
static void EDVBrowserContentsSetCellDateChanged(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
);
static void EDVBrowserContentsSetCellHardLinks(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellLinkedTo(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellDevice(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellIndex(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellDeviceType(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellBlockSize(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetCellBlocks(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
);
static void EDVBrowserContentsSetRow(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row
);

static gint EDVBrowserContentsAppendObject(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist,
	edv_object_struct *obj
);
static void EDVBrowserContentsAppendListing(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist,
	const gchar *path,
	const gboolean update_status_bar
);

/* Selection */
GList *EDVBrowserContentsGetSelectedPaths(edv_browser_struct *browser);

/* Finding */
gint EDVBrowserContentsFindRowByIndex(
	edv_browser_struct *browser,
	const gulong device_index, const gulong index
);
gint EDVBrowserContentsFindRowByPath(
	edv_browser_struct *browser, const gchar *path
);

/* Realize Listing */
void EDVBrowserContentsRealizeListing(edv_browser_struct *browser);

/* Get Listing */
void EDVBrowserContentsGetListing(
	edv_browser_struct *browser, const gchar *path,
	const gboolean update_status_bar
);
void EDVBrowserContentsClear(edv_browser_struct *browser);

/* Opening */
void EDVBrowserContentsOpen(
	edv_browser_struct *browser,
	const gint row, const gint column,
	const guint state		/* Modifier keys */
);
void EDVBrowserContentsOpenWith(
	edv_browser_struct *browser, const gint row, const gint column
);

/* Renaming */
static void EDVBrowserContentsFPromptRenameApplyCB(
	gpointer data, const gchar *value
);
void EDVBrowserContentsDoFPromptRename(
	edv_browser_struct *browser, const gint row, const gint column
);

/* Object Callbacks */
void EDVBrowserContentsObjectAddedNotify(
	edv_browser_struct *browser, const gchar *path,
	const struct stat *lstat_buf
);
void EDVBrowserContentsObjectModifiedNotify(
	edv_browser_struct *browser, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
);
void EDVBrowserContentsObjectRemovedNotify(
	edv_browser_struct *browser, const gchar *path
);

/* Mount Callbacks */
void EDVBrowserContentsMountNotify(
	edv_browser_struct *browser, edv_device_struct *dev,
	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)


/*
 *	Creates a new error object.
 *
 *	The path specifies the full path to the error object.
 */
static edv_object_struct *EDVBrowserNewErrorObject(
	const gchar *path
)
{
	edv_object_struct *obj = EDVObjectNew();
	if(obj == NULL)
	    return(NULL);

	obj->type = EDV_OBJECT_TYPE_ERROR;
	EDVObjectSetPath(obj, path);
	EDVObjectUpdateLinkFlags(obj);

	return(obj);
}


/*
 *	FPrompt cancel callback.
 */
static void EDVBrowserContentsFPromptCancelCB(gpointer data)
{
	gpointer *cb_data = (gpointer *)data;
	if(cb_data == NULL)
	    return;

	g_free(cb_data);
}


/*
 *	Resets the Contents List's column headings to the names and 
 *	ordering specified by the configuration. 
 *
 *	All inputs assumed valid.
 */
static void EDVBrowserContentsResetColumns(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist
)
{
	gint i, width;
	const gchar *title = NULL;
	GList *glist;
	GtkJustification justify = GTK_JUSTIFY_LEFT;
	GtkRcStyle *lists_rcstyle = core->lists_rcstyle;
	cfg_intlist_struct	*column_types_intlist,
				*column_width_intlist;
	const cfg_item_struct *cfg_list = core->cfg_list;
	edv_browser_column_type column_type;

	/* Get the column types mapping */
	column_types_intlist = EDV_GET_INTLIST(
	    EDV_CFG_PARM_BROWSER_CONTENTS_COLUMN
	);
	if(column_types_intlist == NULL)
	    return;

	/* Get the column widths */
	column_width_intlist = EDV_GET_INTLIST(
	    EDV_CFG_PARM_BROWSER_CONTENTS_COLUMN_WIDTH
	);
	if(column_width_intlist == NULL)
	    return;


	/* Update the clist's column settings */
	gtk_clist_column_titles_active(clist);
	gtk_clist_column_titles_show(clist);
	gtk_clist_set_auto_sort(clist, FALSE);
	gtk_clist_set_sort_type(clist, GTK_SORT_DESCENDING);

#if 0
/* Already set */
	/* Change clist selection mode to GTK_SELECTION_EXTENDED
	 *
	 * The selection mode can change whenever the contents list is
	 * updated
	 */
	gtk_clist_set_selection_mode(clist, GTK_SELECTION_EXTENDED);
#endif

	/* Iterate through each column */
	for(i = 0, glist = column_types_intlist->list;
	    glist != NULL;
	    i++, glist = g_list_next(glist)
	)
	{
	    column_type = (edv_browser_column_type)glist->data;

	    /* Get the width for this column type */
	    width = (gint)g_list_nth_data(
		column_width_intlist->list,
		(guint)column_type
	    );

	    /* Get column title and justification  based on the
	     * column type
	     */
	    switch(column_type)
	    {
	      case EDV_BROWSER_COLUMN_TYPE_NAME:
		title = "Name";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_SIZE:
		title = "Size";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_TYPE:
		title = "Type";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_PERMISSIONS:
		title = "Permissions";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_OWNER:
		title = "Owner";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_GROUP:
		title = "Group";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_ACCESS:
		title = "Date Access";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_MODIFIED:
		title = "Date Modified";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_CHANGED:
		title = "Date Changed";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_HARD_LINKS:
		title = "Hard Links";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_LINKED_TO:
		title = "Linked To";
		justify = GTK_JUSTIFY_LEFT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DEVICE_INDEX:
		title = "Device Index";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_INDEX:
		title = "Index";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DEVICE_TYPE:
		title = "Device Type";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_BLOCK_SIZE:
		title = "Block Size";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	      case EDV_BROWSER_COLUMN_TYPE_BLOCKS:
		title = "Blocks";
		justify = GTK_JUSTIFY_RIGHT;
		break;
	    }

	    gtk_clist_set_column_visibility(clist, i, TRUE);
	    gtk_clist_set_column_auto_resize(
		clist, i, FALSE
	    );
	    gtk_clist_set_column_title(clist, i, title);
	    gtk_clist_set_column_width(clist, i, width);
	    gtk_clist_set_column_justification(clist, i, justify);
	}
	/* Reset the rest of the columns */
	for(; i < clist->columns; i++)
	    gtk_clist_set_column_visibility(clist, i, FALSE);

	/* Set RC style after column headings have been mapped */
	if(lists_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(
		GTK_WIDGET(clist), lists_rcstyle
	    );
}

/*
 *	Sets the object name for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellName(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gchar *name = obj->name;
	GdkPixmap *pixmap, *pixmap_exp, *pixmap_ext, *pixmap_hid;
	GdkBitmap *mask, *mask_exp, *mask_ext, *mask_hid;
	GtkStyle	**cell_styles = browser->cell_style,
			*style = NULL;

	/* Get icon */
	EDVMatchObjectIcon(
	    core->device, core->total_devices,
	    core->mimetype, core->total_mimetypes,
	    obj->type,
	    obj->full_path,
	    EDV_OBJECT_IS_LINK_VALID(obj),
	    obj->permissions,
	    0,			/* Small icons */
	    &pixmap, &mask,
	    &pixmap_exp, &mask_exp,
	    &pixmap_ext, &mask_ext,
	    &pixmap_hid, &mask_hid
	);
	/* Get style and alternate icon based on any type-specific
	 * conditions
	 */
	switch(obj->type)
	{
	  case EDV_OBJECT_TYPE_UNKNOWN:
	    break;
	  case EDV_OBJECT_TYPE_FILE:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL)
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    break;
	  case EDV_OBJECT_TYPE_DIRECTORY:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL)
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    /* Special directory notations? */
	    if(!strcmp(name, ".."))
	    {
		if(browser->folder_parent_pixmap != NULL)
		{
		    pixmap = browser->folder_parent_pixmap;
		    mask = browser->folder_parent_mask;
		}
	    }
#if 0
	    /* Home directory? */
	    if((!STRISEMPTY(obj->full_path) &&
		!STRISEMPTY(core->home_dir)) ?
		!strcmp(obj->full_path, core->home_dir) : FALSE
	    )
	    {
		if(browser->folder_home_pixmap != NULL)
		{
		    pixmap = browser->folder_home_pixmap;
		    mask = browser->folder_home_mask;
		}
	    }
#endif
	    /* Directory not accessable? */
	    if(!EDVIsObjectAccessable(core, obj))
	    {
		if(browser->folder_noaccess_pixmap != NULL)
		{
		    pixmap = browser->folder_noaccess_pixmap;
		    mask = browser->folder_noaccess_mask;
		}
		if(style == NULL)
		    style = cell_styles[
			EDV_BROWSER_CELL_STYLE_NO_ACCESS
		    ];
	    }
	    break;
	  case EDV_OBJECT_TYPE_LINK:
	    /* Dangling? */
	    if(!EDV_OBJECT_IS_LINK_VALID(obj))
	    {
		if(pixmap_ext != NULL)
		{
		    pixmap = pixmap_ext;
		    mask = mask_ext;
		}
		if(style == NULL)
		    style = cell_styles[
		        EDV_BROWSER_CELL_STYLE_DANGLING_LINK
		    ];
	    }
	    /* Infinately recursive? */
	    if(EDV_OBJECT_IS_LINK_TAR_GRAND_PARENT(obj))
	    {
		if(style == NULL)
		    style = cell_styles[
			EDV_BROWSER_CELL_STYLE_RECURSIVE_LINK
		    ];
	    }
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_BLOCK:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL)
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    break;
	  case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL)
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    break;
	  case EDV_OBJECT_TYPE_FIFO:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL) 
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    break;
	  case EDV_OBJECT_TYPE_SOCKET:
	    /* Hidden */
	    if(EDVIsObjectHidden(obj))
	    {
		if(pixmap_hid != NULL)
		{
		    pixmap = pixmap_hid;
		    mask = mask_hid;
		}
	    }
	    break;
	  case EDV_OBJECT_TYPE_ERROR:
	    break;
	}

	/* Set cell */
	if(pixmap != NULL)
	     gtk_clist_set_pixtext(
		clist, row, column,
		(name != NULL) ? name : "(null)",
		EDV_LIST_PIXMAP_TEXT_SPACING,
		pixmap, mask
	    );
	else
	    gtk_clist_set_text(
		clist, row, column,
		(name != NULL) ? name : "(null)"
	    );
	gtk_clist_set_cell_style(clist, row, column, style);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object size for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellSize(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	gboolean hide_dir_size, gboolean hide_link_size
)
{
	const gint border_minor = 2;
	const gchar *s;
	const edv_object_type type = obj->type;

	if(hide_dir_size && (type == EDV_OBJECT_TYPE_DIRECTORY))
	    s = "";
	else if(hide_link_size && (type == EDV_OBJECT_TYPE_LINK))
	    s = "";
	else if((type == EDV_OBJECT_TYPE_DEVICE_BLOCK) ||
		(type == EDV_OBJECT_TYPE_DEVICE_CHARACTER)
	)
	    s = "";
	else
	    s = EDVGetObjectSizeStr(core, obj->size);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, s
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *	Sets the object type for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellType(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	gchar *type_str;

	/* Get MIME Type type string for the given object */
	EDVMatchObjectTypeString(
	    core->mimetype, core->total_mimetypes,
	    obj->type, obj->permissions,
	    obj->full_path,
	    &type_str
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, (type_str != NULL) ? type_str : ""
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object permissions for the specified cell on the given
 *	clist.
 */
static void EDVBrowserContentsSetCellPermissions(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column, gboolean hide_link_permissions
)
{
	edv_permission_flags permissions = obj->permissions;
	gchar s[80];

	if(hide_link_permissions && (obj->type == EDV_OBJECT_TYPE_LINK))
	    *s = '\0';
	else
	    g_snprintf(
		s, sizeof(s),
		"%c%c%c%c%c%c%c%c%c",
	    (permissions & EDV_PERMISSION_UREAD) ? 'r' : '-',
	    (permissions & EDV_PERMISSION_UWRITE) ? 'w' : '-',
	    (permissions & EDV_PERMISSION_SETUID) ?
		'S' :
		((permissions & EDV_PERMISSION_UEXECUTE) ? 'x' : '-'),
	    (permissions & EDV_PERMISSION_GREAD) ? 'r' : '-',
	    (permissions & EDV_PERMISSION_GWRITE) ? 'w' : '-',
	    (permissions & EDV_PERMISSION_SETGID) ?
		'G' :
		((permissions & EDV_PERMISSION_GEXECUTE) ? 'x' : '-'),
	    (permissions & EDV_PERMISSION_AREAD) ? 'r' : '-',
	    (permissions & EDV_PERMISSION_AWRITE) ? 'w' : '-',
	    (permissions & EDV_PERMISSION_STICKY) ?
		'T' :
		((permissions & EDV_PERMISSION_AEXECUTE) ? 'x' : '-')
	    );

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, s
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object owner for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellOwner(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	/* Get owner name from object's user id */
	const gchar *owner_name = EDVUIDGetNameFromUID(
	    core->uid, core->total_uids,
	    obj->owner_id, NULL
	);
	if(owner_name == NULL)
	    owner_name = "";

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, owner_name
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object group for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellGroup(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	/* Get group name from object's group id */
	const gchar *group_name = EDVGIDGetNameFromGID(
	    core->gid, core->total_gids,
	    obj->group_id, NULL
	);
	if(group_name == NULL)
	    group_name = "";

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, group_name
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *	Sets the object access date for the specified cell on the given
 *	clist.
 */
static void EDVBrowserContentsSetCellDateAccess(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
)
{
	const gchar *s;

	/* Get date string for last access date */
	const gulong t = obj->access_time;
	if(t > 0)
	    s = EDVDateFormatString(
		t, date_format, date_relativity
	    );
	else
	    s = "";

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, s
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object modified date for the specified cell on the given
 *      clist.
 */
static void EDVBrowserContentsSetCellDateModified(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
)
{
	const gchar *s;

	/* Get date string for last modify date */
	const gulong t = obj->modify_time;
	if(t > 0)
	    s = EDVDateFormatString(
		t, date_format, date_relativity
	    );
	else
	    s = "";

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, s
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object change date for the specified cell on the given
 *      clist.
 */
static void EDVBrowserContentsSetCellDateChanged(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column,
	edv_date_relativity date_relativity, const gchar *date_format
)
{
	const gchar *s;

	/* Get date string for last change date */
	const gulong t = obj->change_time;
	if(t > 0)
	    s = EDVDateFormatString(
		t, date_format, date_relativity
	    );
	else
	    s = "";

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, s
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *      Sets the object hard links for the specified cell on the given
 *      clist.
 */
static void EDVBrowserContentsSetCellHardLinks(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	gchar num_str[80];

	/* Get string for number of hard links */
	g_snprintf(
	    num_str, sizeof(num_str),
	    "%i",
	    obj->hard_links
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *	Sets the object link destination for the specified cell on the
 *	given clist.
 */
static void EDVBrowserContentsSetCellLinkedTo(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gchar	*path = (obj->full_path != NULL) ?
			    obj->full_path : "",
			*link_value = (obj->link_value != NULL) ?
			    obj->link_value : "";
	GdkBitmap	*mask = NULL,
			*mask_ext = NULL,
			*mask_hid = NULL;
	GdkPixmap	*pixmap = NULL,
			*pixmap_ext = NULL,
			*pixmap_hid = NULL;
	GtkStyle	*style = NULL,
			**cell_styles = browser->cell_style;

	/* Begin checking for which GtkStyle to be used */

	/* Dangling link?
	 *
	 * Note that we use the no access style for the link
	 * destination of a dangling link. The actual dangling link
	 * style is only used for the name of a dangling link
	 */
	if(!EDV_OBJECT_IS_LINK_VALID(obj))
	{
	    if(style == NULL)
		style = cell_styles[EDV_BROWSER_CELL_STYLE_NO_ACCESS];
	}
	if(EDV_OBJECT_IS_LINK_TAR_GRAND_PARENT(obj))
	{
	    if(style == NULL)
		style = cell_styles[EDV_BROWSER_CELL_STYLE_RECURSIVE_LINK];
	}

	/* Get link value */
	link_value = (obj->link_value != NULL) ? obj->link_value : "";
	if(EDV_OBJECT_IS_LINK_VALID(obj))
	{
	    struct stat lstat_buf;
	    gchar	*parent = g_dirname(path),
			*dest_path = EDVEvaluatePath(parent, link_value);

	    if((dest_path != NULL) ?
		!lstat((const char *)dest_path, &lstat_buf) : FALSE
	    )
	    {
		edv_object_struct *dest_obj = EDVObjectNew();
		EDVObjectSetPath(dest_obj, dest_path);
		EDVObjectSetStat(dest_obj, &lstat_buf);
		EDVObjectUpdateLinkFlags(dest_obj);

		/* Get icon for the link destination */
		EDVMatchObjectIcon(
		    core->device, core->total_devices,
		    core->mimetype, core->total_mimetypes,
		    dest_obj->type,
		    dest_obj->full_path,
		    EDV_OBJECT_IS_LINK_VALID(dest_obj),
		    dest_obj->permissions,
		    0,				/* Small icons */
		    &pixmap, &mask,
		    NULL, NULL,
		    &pixmap_ext, &mask_ext,
		    &pixmap_hid, &mask_hid
		);
		/* Checking if an alternate GtkStyle and icon should
		 * be used based on certain object types
		 */
		switch(dest_obj->type)
		{
		  case EDV_OBJECT_TYPE_UNKNOWN:
		    break;
		  case EDV_OBJECT_TYPE_FILE:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    break;
		  case EDV_OBJECT_TYPE_DIRECTORY:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    /* Special directory notations? */
		    if(!STRISEMPTY(dest_obj->name) ?
			!strcmp(dest_obj->name, "..") : FALSE
		    )
		    {
			if(browser->folder_parent_pixmap != NULL)
			{
			    pixmap = browser->folder_parent_pixmap;
			    mask = browser->folder_parent_mask;
			}
		    }
#if 0
		    /* Home directory? */
		    if((!STRISEMPTY(dest_obj->full_path) &&
			!STRISEMPTY(core->home_dir)) ?
			!strcmp(dest_obj->full_path, core->home_dir) : FALSE
		       )
		    {
			if(browser->folder_home_pixmap != NULL)
			{
			    pixmap = browser->folder_home_pixmap;
			    mask = browser->folder_home_mask;
			}
		    }
#endif
		    /* Directory not accessable? */
		    if(!EDVIsObjectAccessable(core, obj))
		    {
			if(browser->folder_noaccess_pixmap != NULL)
			{
			    pixmap = browser->folder_noaccess_pixmap;
			    mask = browser->folder_noaccess_mask;
			}
			if(style == NULL)
			    style = cell_styles[
				EDV_BROWSER_CELL_STYLE_NO_ACCESS
			    ];
		    }
		    break;
		  case EDV_OBJECT_TYPE_LINK:
		    if(EDV_OBJECT_IS_LINK_TAR_GRAND_PARENT(dest_obj))
		    {
			if(style == NULL)
			    style = cell_styles[
				EDV_BROWSER_CELL_STYLE_RECURSIVE_LINK
			    ];
		    }
		    break;
		  case EDV_OBJECT_TYPE_DEVICE_BLOCK:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    break;
		  case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    break;
		  case EDV_OBJECT_TYPE_FIFO:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    break;
		  case EDV_OBJECT_TYPE_SOCKET:
		    /* Hidden */
		    if(EDVIsObjectHidden(obj))
		    {
			if(pixmap_hid != NULL)
			{
			    pixmap = pixmap_hid;
			    mask = mask_hid;
			}
		    }
		    break;
		  case EDV_OBJECT_TYPE_ERROR:
		    break;
		}

		EDVObjectDelete(dest_obj);
	    }
	    g_free(dest_path);
	    g_free(parent);
	}

	/* Set cell */
	if(pixmap != NULL)
	    gtk_clist_set_pixtext(
		clist, row, column,
		link_value,
		EDV_LIST_PIXMAP_TEXT_SPACING,
		pixmap, mask
	    );
	else
	    gtk_clist_set_text(
		clist, row, column, link_value
	    );
	gtk_clist_set_cell_style(clist, row, column, style);
	gtk_clist_set_shift(
	    clist, row, column, 0, 0
	);
}

/*
 *	Sets the object's device index or the specified cell on
 *	the given clist.
 */
static void EDVBrowserContentsSetCellDevice(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	gchar num_str[80];

	/* Get string for device */
	g_snprintf(
	    num_str, sizeof(num_str),
	    "%ld",
	    obj->device_index
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *	Sets the object's index for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellIndex(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	gchar num_str[80];

	g_snprintf(
	    num_str, sizeof(num_str),
	    "%ld",
	    obj->index
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *      Sets the object device type for the specified cell on the given
 *	clist.
 */
static void EDVBrowserContentsSetCellDeviceType(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	const edv_object_type type = obj->type;
	gint major, minor;
	gchar num_str[2 * 80];

	/* Is object a block or character device? */
	if((type == EDV_OBJECT_TYPE_DEVICE_BLOCK) ||
	   (type == EDV_OBJECT_TYPE_DEVICE_CHARACTER)
	)
	{
	    EDVGetDeviceNumbers(
		(dev_t)obj->device_type, &major, &minor
	    );
	    g_snprintf(
		num_str, sizeof(num_str),
		"%i, %i",
		major, minor
	    );
	}
	else
	{
	    *num_str = '\0';
	}

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *      Sets the object block size for the specified cell on the given
 *	clist.
 */
static void EDVBrowserContentsSetCellBlockSize(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	gchar num_str[80];

	/* Get string for block size */
	g_snprintf(
	    num_str, sizeof(num_str),
	    "%ld",
	    obj->block_size
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *      Sets the object blocks for the specified cell on the given clist.
 */
static void EDVBrowserContentsSetCellBlocks(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row, const gint column
)
{
	const gint border_minor = 2;
	gchar num_str[80];

	/* Get string for number of blocks */
	g_snprintf(
	    num_str, sizeof(num_str),
	    "%ld",
	    obj->blocks
	);

	/* Set cell */
	gtk_clist_set_text(
	    clist, row, column, num_str
	);
	gtk_clist_set_cell_style(clist, row, column, NULL);
	gtk_clist_set_shift(
	    clist, row, column, 0, -border_minor
	);
}

/*
 *	Sets the Contents List's row to the values of the object.
 *
 *	The clist specifies the Contents List.
 *
 *	The obj specifies the object.
 *
 *	The row specifies the row.
 *
 *	All inputs assumed valid.
 */
static void EDVBrowserContentsSetRow(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist, edv_object_struct *obj,
	const gint row
)
{
	gint i;
	gboolean hide_dir_size, hide_link_size, hide_link_permissions;
	const gchar *date_format;
	GList *glist;
	edv_date_relativity date_relativity;
	edv_browser_column_type column_type;
	cfg_intlist_struct *column_types_intlist;
	const cfg_item_struct *cfg_list = core->cfg_list;

	/* Get the column types mapping */
	column_types_intlist = EDV_GET_INTLIST(
	    EDV_CFG_PARM_BROWSER_CONTENTS_COLUMN
	);
	if(column_types_intlist == NULL)
	    return;

	/* Get the display options */
	date_relativity = (edv_date_relativity)EDV_GET_I(
	    EDV_CFG_PARM_DATE_RELATIVITY
	);
	date_format = EDV_GET_S(EDV_CFG_PARM_DATE_FORMAT);
	hide_dir_size = EDV_GET_B(EDV_CFG_PARM_BROWSER_CONTENTS_HIDE_DIR_SIZE);
	hide_link_size = EDV_GET_B(EDV_CFG_PARM_BROWSER_CONTENTS_HIDE_LINK_SIZE);
	hide_link_permissions = EDV_GET_B(EDV_CFG_PARM_BROWSER_CONTENTS_HIDE_LINK_PERMISSIONS);

	/* Iterate through each column */
	for(i = 0, glist = column_types_intlist->list;
	    glist != NULL;
	    i++, glist = g_list_next(glist)
	)
	{
	    column_type = (edv_browser_column_type)glist->data;
	    switch(column_type)
	    {
	      case EDV_BROWSER_COLUMN_TYPE_NAME:
		EDVBrowserContentsSetCellName(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_SIZE:
		EDVBrowserContentsSetCellSize(
		    core, browser, clist, obj,
		    row, i, hide_dir_size, hide_link_size
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_TYPE:
		EDVBrowserContentsSetCellType(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_PERMISSIONS:
		EDVBrowserContentsSetCellPermissions(
		    core, browser, clist, obj,
		    row, i, hide_link_permissions
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_OWNER:
		EDVBrowserContentsSetCellOwner(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_GROUP:
		EDVBrowserContentsSetCellGroup(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_ACCESS:
		EDVBrowserContentsSetCellDateAccess(
		    core, browser, clist, obj,
		    row, i, date_relativity, date_format
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_MODIFIED:
		EDVBrowserContentsSetCellDateModified(
		    core, browser, clist, obj,
		    row, i, date_relativity, date_format
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DATE_CHANGED:
		EDVBrowserContentsSetCellDateChanged(
		    core, browser, clist, obj,
		    row, i, date_relativity, date_format
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_HARD_LINKS:
		EDVBrowserContentsSetCellHardLinks(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_LINKED_TO:
		EDVBrowserContentsSetCellLinkedTo(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DEVICE_INDEX:
		EDVBrowserContentsSetCellDevice(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_INDEX:
		EDVBrowserContentsSetCellIndex(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_DEVICE_TYPE:
		EDVBrowserContentsSetCellDeviceType(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_BLOCK_SIZE:
		EDVBrowserContentsSetCellBlockSize(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	      case EDV_BROWSER_COLUMN_TYPE_BLOCKS:
		EDVBrowserContentsSetCellBlocks(
		    core, browser, clist, obj,
		    row, i
		);
		break;
	    }
	}
}

/*
 *	Appends the object to the Contents List.
 *
 *	The clist specifies the Contents List.
 *
 *	The obj specifies the object who's values will be used to
 *	append a new row on the Contents List and will be transfered
 *	to the Content List's row data. The obj should not be
 *	referenced again after this call.
 *
 *	Returns the new row index or -1 on error.
 *
 *	All inputs assumed valid.
 */
static gint EDVBrowserContentsAppendObject(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist,
	edv_object_struct *obj
)
{
	const gint columns = MAX(clist->columns, 1);
	gint i, new_row;
	gchar **strv;

	/* Allocate the row cell values */
	strv = (gchar **)g_malloc0(columns * sizeof(gchar *));
	for(i = 0; i < columns; i++)
	    strv[i] = "";

	/* Append the new row */
	new_row = gtk_clist_append(clist, strv);

	/* Delete the row cell values */
	g_free(strv);

	/* Failed to append row? */
	if(new_row < 0)
	{
	    EDVObjectDelete(obj);
	    return(-1);
	}

	/* Set the new row's values */
	EDVBrowserContentsSetRow(
	    core, browser, clist,
	    obj,
	    new_row
	);

	/* Set this object as the new row's data */
	gtk_clist_set_row_data_full(
	    clist, new_row,
	    obj, EDVBrowserContentsItemDestroyCB
	);

	return(new_row);
}

/*
 *	Appends the objects from a specified location to the Contents
 *	List.
 *
 *	The clist specifies the Contents List.
 *
 *	The path specifies the full path to the location.
 *
 *	If update_status_bar is TRUE then the status bar will be
 *	updated during this operation.
 *
 *	All inputs assumed valid.
 */
static void EDVBrowserContentsAppendListing(
	edv_core_struct *core, edv_browser_struct *browser,
	GtkCList *clist,
	const gchar *path,
	const gboolean update_status_bar
)
{
#ifdef HAVE_REGEX
	regex_t *regex_filter;
#endif
	gboolean hide_object_hidden, hide_object_noaccess;
	const gchar *filter = browser->contents_list_filter;
	gint nobjs;
	gchar **names_list;
	const cfg_item_struct *cfg_list = core->cfg_list;
	edv_status_bar_struct *sb = browser->status_bar;

	hide_object_hidden = !EDV_GET_B(
	    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_HIDDEN
	);
	hide_object_noaccess = !EDV_GET_B(
	    EDV_CFG_PARM_BROWSER_SHOW_OBJECT_NOACCESS
	);

#if defined(HAVE_REGEX)
	/* Compile the regex search criteria */
	if(STRISEMPTY(filter) ?
	    FALSE : strcmp(filter, "*")
	)
	{
	    regex_filter = (regex_t *)g_malloc(sizeof(regex_t));
	    if(regcomp(
		regex_filter,
		filter,
#ifdef REG_EXTENDED
		REG_EXTENDED |		/* Use POSIX extended regex */
#endif
		REG_NOSUB		/* Do not report subpattern matches */
	    ))
	    {
		g_free(regex_filter);
		regex_filter = NULL;
	    }
	}
	else
	{
	    regex_filter = NULL;
	}
#else
	if(STRISEMPTY(filter) ?
	    TRUE : !strcmp(filter, "*")
	)
	    filter = NULL;
#endif

	/* Get directory entries from the location specified by path */
	names_list = GetDirEntNames2(path, &nobjs);
	if(names_list != NULL)
	{
	    /* Sort the directory contents alphabetically by name */
	    names_list = StringQSort(names_list, nobjs);
	    if(names_list != NULL)
	    {
		struct stat stat_buf;
		gint	i,
			nobjs_loaded = 0, last_progress_percent = -1;
		const gchar *name;
		gchar *full_path = NULL;
		edv_object_struct *obj;
#define UPDATE_PROGRESS	{				\
 if(update_status_bar && (nobjs > 0)) {			\
  const gint progress_percent = nobjs_loaded * 100 / nobjs; \
  if(progress_percent > last_progress_percent) {	\
   EDVStatusBarProgress(				\
    sb,							\
    (gfloat)progress_percent / 100.0f,			\
    TRUE						\
   );							\
   last_progress_percent = progress_percent;		\
  }							\
 }							\
}
		/* Iterate through the directory contents and pick
		 * out just the directories for this first iteration
		 */
		for(i = 0; i < nobjs; i++)
		{
#define FREE_AND_CONTINUE	{	\
 g_free(full_path);			\
 full_path = NULL;			\
					\
 /* Do not delete names_list[i] on this	\
  * first iteration			\
  */					\
					\
 continue;				\
}

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

		    /* Skip special dir notations */
		    if(!strcmp(name, "."))
		    {
			nobjs_loaded++;
			UPDATE_PROGRESS
			FREE_AND_CONTINUE
		    }

		    /* Complete a full path to this object */
		    full_path = STRDUP(PrefixPaths(path, name));
		    if(full_path == NULL)
		    {
			g_free(names_list[i]);
			names_list[i] = NULL;
			FREE_AND_CONTINUE
		    }

		    /* Get the destination object's stats */
		    if(stat((const char *)full_path, &stat_buf))
		    {
			/* Failed to get the statistics of this
			 * object but do not add it as an error
			 * object on this directory iteration, it
			 * will be added later in the second
			 * iteration
			 */
			FREE_AND_CONTINUE
		    }

#ifdef S_ISDIR
		    /* Skip if the destination is not a directory */
		    if(!S_ISDIR(stat_buf.st_mode))
			FREE_AND_CONTINUE
#endif

		    /* Get this directory's local stats */
		    if(lstat((const char *)full_path, &stat_buf))
		    {
			/* Unable to get the directory's local stats
			 * so add it as an error object
			 */
			obj = EDVBrowserNewErrorObject(full_path);
			if(obj != NULL)
			    EDVBrowserContentsAppendObject(
				core, browser, clist,
				obj
			    );
			nobjs_loaded++;
			UPDATE_PROGRESS
			g_free(names_list[i]);
			names_list[i] = NULL;
			FREE_AND_CONTINUE
		    }

		    /* Create a new object, set its values from the
		     * stats, and append it with a new row on the
		     * Contents List
		     */
		    obj = EDVObjectNew();
		    if(obj != NULL)
		    {
			EDVObjectSetPath(obj, full_path);
			EDVObjectSetStat(obj, &stat_buf);
			EDVObjectUpdateLinkFlags(obj);

			/* Skip hidden or no access objects? */
			if((hide_object_hidden ?
			       EDVIsObjectHidden(obj) : FALSE) ||
			   (hide_object_noaccess ?
			       !EDVIsObjectAccessable(core, obj) : FALSE)
			)
			{
			    EDVObjectDelete(obj);
			}
			else
			{
			    /* Append/transfer this object to the listing */
			    EDVBrowserContentsAppendObject(
				core, browser, clist,
				obj
			    );
			}
		    }

		    nobjs_loaded++;
		    UPDATE_PROGRESS
		    g_free(names_list[i]);
		    names_list[i] = NULL;
		    FREE_AND_CONTINUE
#undef FREE_AND_CONTINUE
		}

		/* Now iterate through all other objects, skipping
		 * directories
		 *
		 * This iteration will delete all strings in names_list
		 *
		 * Do not reset last_progress_percent, leave it as the
		 * last value from the previous loop
		 */
		full_path = NULL;
		for(i = 0; i < nobjs; i++)
		{
#define FREE_AND_CONTINUE	{	\
 g_free(full_path);			\
 full_path = NULL;			\
					\
 g_free(names_list[i]);			\
 names_list[i] = NULL;			\
					\
 continue;				\
}
		    name = names_list[i];
		    if(name == NULL)
			FREE_AND_CONTINUE

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

		    /* Filter check */
#if defined(HAVE_REGEX)
		    if(regex_filter != NULL)
		    {
			if(regexec(
			    regex_filter,
			    name,
			    0, NULL,
			    0
			) == REG_NOMATCH)
			{
			    nobjs_loaded++;
			    UPDATE_PROGRESS
			    FREE_AND_CONTINUE
			}
		    }
#else
		    if(filter != NULL)
		    {
			if(fnmatch(filter, name, 0) == FNM_NOMATCH)
			{
			    nobjs_loaded++;
			    UPDATE_PROGRESS
			    FREE_AND_CONTINUE
			}
		    }
#endif

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

		    /* Get the object's local stats */
		    if(lstat((const char *)full_path, &stat_buf))
		    {
			/* Unable to get the object's local stats
			 * so add it as an error object
			 */
			obj = EDVBrowserNewErrorObject(full_path);
			if(obj != NULL)
			    EDVBrowserContentsAppendObject(
				core, browser, clist,
				obj                  
			    );
			nobjs_loaded++;
			UPDATE_PROGRESS
			FREE_AND_CONTINUE
		    }

		    /* Create a new object, set its values from the
		     * stats, and append it with a new row on the
		     * Contents List
		     */
		    obj = EDVObjectNew();
		    if(obj != NULL)
		    {
			EDVObjectSetPath(obj, full_path);
			EDVObjectSetStat(obj, &stat_buf);
			EDVObjectUpdateLinkFlags(obj);

			/* Skip hidden objects */
			if((hide_object_hidden ?
			       EDVIsObjectHidden(obj) : FALSE)
/*                         ||
			   (hide_object_noaccess ?
			        !EDVIsObjectAccessable(core, obj) : FALSE)
 */
			)
			{
			    EDVObjectDelete(obj);
			}
			else
			{
			    /* Append/transfer this object to the listing */
			    EDVBrowserContentsAppendObject(
				core, browser, clist,
				obj
			    );
			}
		    }

		    nobjs_loaded++;
		    UPDATE_PROGRESS
		    FREE_AND_CONTINUE
#undef FREE_AND_CONTINUE
		}

		/* At this point all strings in names_list have been deleted */
#undef UPDATE_PROGRESS
	    }

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

#ifdef HAVE_REGEX
	if(regex_filter != NULL)
	{
	    regfree(regex_filter);
	    g_free(regex_filter);
	}
#endif
}


/* 
 *	Returns a list of full paths describing the selected items.
 *
 *	The calling function must delete the list.
 */
GList *EDVBrowserContentsGetSelectedPaths(edv_browser_struct *browser)
{
	GList *glist, *paths_list = NULL;
	edv_object_struct *obj;
	GtkCList *clist = (browser != NULL) ?
	    (GtkCList *)browser->contents_clist : NULL;
	if(clist == NULL)
	    return(paths_list);

	for(glist = clist->selection;
	    glist != NULL;
	    glist = g_list_next(glist)
	)
	{
	    obj = EDV_OBJECT(gtk_clist_get_row_data(
		clist, (gint)glist->data
	    ));
	    if((obj != NULL) ? STRISEMPTY(obj->full_path) : TRUE)
		continue;

	    paths_list = g_list_append(
		paths_list,
		STRDUP(obj->full_path)
	    );
	}

	return(paths_list);
}


/*
 *	Finds the row who's object matches the location index.
 *
 *	The device_index specifies the index of the device that the
 *	object resides on.
 *
 *	The index specifies the index on the device that the object
 *	resides at.
 *
 *	Returns the matched row or -1 on error.
 */
gint EDVBrowserContentsFindRowByIndex(
	edv_browser_struct *browser, 
	const gulong device_index, const gulong index
)
{
	gint i;
	GtkCList *clist;
	edv_object_struct *obj;

	if(browser == NULL)
	    return(-1);

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

	/* Iterate through all rows */
	for(i = 0; i < clist->rows; i++)
	{
	    obj = EDV_OBJECT(gtk_clist_get_row_data(clist, i));
	    if(obj == NULL)
		continue;

	    if((obj->device_index == device_index) &&
	       (obj->index == index)
	    )
		return(i);
	}

	return(-1);
}

/*
 *	Finds the row who's object matches the path.
 *
 *	The path specifies the full path of the object.
 *
 *	Returns the matched row or -1 on error.
 */
gint EDVBrowserContentsFindRowByPath(
	edv_browser_struct *browser, const gchar *path
)
{
	gint i;
	GtkCList *clist;
	edv_object_struct *obj;

	if((browser == NULL) || STRISEMPTY(path))
	    return(-1);

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

	/* Iterate through all rows */
	for(i = 0; i < clist->rows; i++)
	{
	    obj = EDV_OBJECT(gtk_clist_get_row_data(clist, i));
	    if((obj != NULL) ? STRISEMPTY(obj->full_path) : TRUE)
		continue;

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

	return(-1);
}

/*
 *	Updates all rows on the Contents List with each row's
 *	associated object.
 *
 *	This should be called when MIME Types or Devices have been
 *	added/modified/removed or after reconfiguration.
 *
 *	This function should not be used to get a new listing, use
 *	EDVBrowserContentsGetListing() instead.
 */
void EDVBrowserContentsRealizeListing(edv_browser_struct *browser)
{
	gint i;
	GtkWidget *w;
	GtkCList *clist;
	edv_object_struct *obj;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	w = browser->contents_clist;
	core = browser->core;
	if((w == NULL) || (core == NULL))
	    return;

	clist = GTK_CLIST(w);

	gtk_clist_freeze(clist);

	/* Reset the columns */
	EDVBrowserContentsResetColumns(core, browser, clist);

	/* Update the rows */
	for(i = 0; i < clist->rows; i++)
	{
	    obj = EDV_OBJECT(gtk_clist_get_row_data(clist, i));
	    if(obj == NULL)
		continue;

	    EDVBrowserContentsSetRow(
		core, browser, clist,
		obj,
		i			/* Row */
	    );
	}

	gtk_clist_thaw(clist);
}


/*
 *	Clears the Contents List, resets the columns, and gets a new
 *	listing of objects.
 *
 *	The path specifies the full path to the location.
 *
 *	If update_status_bar is TRUE then the status bar will be updated
 *	during this operation.
 */
void EDVBrowserContentsGetListing(
	edv_browser_struct *browser, const gchar *path,
	const gboolean update_status_bar
)
{
	GtkWidget *w;
	GtkCList *clist;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

	w = browser->contents_clist;
	core = browser->core;
	if((w == NULL) || (core == NULL))
	    return;

	clist = GTK_CLIST(w);

	/* Report the initial progress? */
	if(update_status_bar)
	{
	    EDVStatusBarMessage(
		browser->status_bar,
		"Loading directory contents...",
		FALSE
	    );
	    EDVStatusBarProgress(
		browser->status_bar, 0.0f, TRUE
	    );
	}

	gtk_clist_freeze(clist);

	/* Clear the listing */
	EDVBrowserContentsClear(browser);

	/* Reset the columns */
	EDVBrowserContentsResetColumns(core, browser, clist);

	/* Append the list of objects to the listing */
	if(!STRISEMPTY(path))
	    EDVBrowserContentsAppendListing(
		core, browser, clist,
		path,				/* Location */
		update_status_bar
	    );

	gtk_clist_thaw(clist);

	/* Report the final progress? */
	if(update_status_bar)
	{
	    EDVStatusBarMessage(
		browser->status_bar,
		"Directory contents loaded",
		FALSE
	    );
	    EDVStatusBarProgress(
		browser->status_bar, 0.0f, TRUE
	    );
	}
}

/*
 *	Clears the Contents List.
 */
void EDVBrowserContentsClear(edv_browser_struct *browser)
{
	GtkCList *clist;

	if(browser == NULL)
	    return;

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

	gtk_clist_freeze(clist);
	gtk_clist_clear(clist);
	gtk_clist_thaw(clist);
}


/*
 *	Opens the object.
 *
 *	The row and column specify the object displayed on the
 *	Contents List. If row and column are -1 then the selected
 *	object(s) will be used instead.
 *
 *	The state specifies the current modifier keys.
 */
void EDVBrowserContentsOpen(
	edv_browser_struct *browser,
	const gint row, const gint column,
	const guint state		/* Modifier keys */
)
{
	GList *paths_list;
	GtkWidget *toplevel;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

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

	/* Use selected object paths? */
	if(row < 0)
	{
	    paths_list = EDVBrowserContentsGetSelectedPaths(browser);
	}
	else
	{
	    GtkCList *clist = GTK_CLIST(browser->contents_clist);
	    edv_object_struct *obj = EDV_OBJECT(
		gtk_clist_get_row_data(clist, row)
	    );
	    if(obj == NULL)
		return;

	    paths_list = g_list_append( 
		NULL,
		STRDUP(obj->full_path)
	    );
	}

	/* Check if there is only one path and it refers to a directory */
	if(g_list_length(paths_list) == 1)
	{
	    struct stat stat_buf;
	    gchar *path = EDVEvaluatePath(
		NULL, (gchar *)paths_list->data
	    );
	    if((path != NULL) ? stat(path, &stat_buf) : TRUE)
	    {
		g_list_foreach(paths_list, (GFunc)g_free, NULL);
		g_list_free(paths_list);
		g_free(path);
		return;
	    }
#ifdef S_ISDIR
	    if(S_ISDIR(stat_buf.st_mode))
	    {
		/* Change directory and return */
		EDVBrowserGotoDirectoryCB(browser, path);

		g_list_foreach(paths_list, (GFunc)g_free, NULL);
		g_list_free(paths_list);
		g_free(path);

		return;
	    }
#endif
	}

	if(paths_list != NULL)
	{
	    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";

	    EDVBrowserSetBusy(browser, TRUE);
	    EDVOpen(
		core,
		paths_list,		/* Path */
		command_name,		/* Command Name */
		toplevel,		/* Toplevel */
		TRUE,			/* Verbose */
		&stdout_path_rtn,
		&stderr_path_rtn
	    );
	    EDVBrowserSetBusy(browser, FALSE);

	    g_free(stdout_path_rtn);
	    g_free(stderr_path_rtn);
	}

	g_list_foreach(paths_list, (GFunc)g_free, NULL);
	g_list_free(paths_list);
}

/*
 *	Maps the Open With list to open the object.
 *
 *	The row and column specify the object displayed on the
 *	Contents List. If row and column are -1 then the selected
 *	object(s) will be used instead.
 */
void EDVBrowserContentsOpenWith(
	edv_browser_struct *browser,
	const gint row, const gint column
)
{
	GList *paths_list;
	GtkWidget *toplevel;
	edv_core_struct *core;

	if(browser == NULL)
	    return;

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

	/* Use selected object paths? */
	if(row < 0)
	{
	    /* Get list of selected object paths */
	    paths_list = EDVBrowserContentsGetSelectedPaths(browser);
	}
	else
	{   
	    GtkCList *clist = GTK_CLIST(browser->contents_clist);
	    edv_object_struct *obj = EDV_OBJECT(
		gtk_clist_get_row_data(clist, row)
	    );
	    if(obj == NULL)
		return;

	    paths_list = g_list_append(
		NULL,
		STRDUP(obj->full_path)
	    );
	}

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

	    EDVOpenWith(
		core,
		paths_list,		/* Paths List */
		NULL,			/* Command Name */
		toplevel,		/* Toplevel */
		TRUE,			/* Verbose */
		&stdout_path_rtn,
		&stderr_path_rtn
	    );

	    g_free(stdout_path_rtn);
	    g_free(stderr_path_rtn);
	}

	g_list_foreach(paths_list, (GFunc)g_free, NULL);
	g_list_free(paths_list);
}


/*
 *      FPrompt apply callback.
 */
static void EDVBrowserContentsFPromptRenameApplyCB(
	gpointer data, const gchar *value
)
{
	gint row;
	GtkCList *clist;
	edv_browser_struct *browser;
	gpointer *cb_data = (gpointer *)data;
	if(cb_data == NULL)
	    return;

	/* Get callback data */
	browser = EDV_BROWSER(cb_data[0]);
	clist = (GtkCList *)cb_data[1];
	row = (gint)cb_data[2];

	/* Inputs valid? */
	if((browser != NULL) && (clist != NULL) && (row >= 0) &&
	   (value != NULL)
	)
	{
	    GtkWidget *toplevel = browser->toplevel;
	    edv_core_struct *core = browser->core;
	    edv_object_struct *obj = EDV_OBJECT(
		gtk_clist_get_row_data(clist, row)
	    );

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

		/* Rename */
		EDVObjectOPRename(
		    core,
		    old_full_path, value,
		    &modified_paths_list,
		    toplevel,
		    FALSE,			/* Do not show progress */
		    TRUE,			/* Interactive */
		    &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_msg = EDVObjectOPGetError();
		if(!STRISEMPTY(error_msg))
		{
		    EDVPlaySoundError(core);
		    EDVMessageObjectOPError(
			"Rename Object Error",
			error_msg,
			old_full_path,
			toplevel
		    );
		}

		/* Report the modified objects */
		if(modified_paths_list != NULL)
		{
		    struct stat lstat_buf;
		    const gchar *modified_path;
		    GList *glist;

		    for(glist = modified_paths_list;
			glist != NULL;
			glist = g_list_next(glist)
		    )
		    {
			modified_path = (const gchar *)glist->data;
			if(modified_path == NULL)
			    continue;

			if(!lstat(modified_path, &lstat_buf))
			{
			    gchar *s = g_strdup_printf(
				"Object \"%s\" renamed to \"%s\"",
				g_basename(old_full_path),
				g_basename(modified_path)
			    );
			    EDVStatusBarMessage(browser->status_bar, s, FALSE);
			    g_free(s);

			    EDVObjectModifiedEmit(
				core,
				old_full_path,
				modified_path,
				&lstat_buf
			    );
			}
		    }

		    g_list_foreach(modified_paths_list, (GFunc)g_free, NULL);
		    g_list_free(modified_paths_list);
		}
		else
		{   
		    /* Did not get the modified object path so this
		     * implies failure
		     */
		    EDVStatusBarMessage(
			browser->status_bar,
			"Rename object failed",
			FALSE
		    );
		}

		g_free(old_full_path);
	    }
	}

	g_free(cb_data);
}

/*
 *	Maps the FPrompt over the File Browser contents list item
 *	to rename the object.
 *
 *      If the column is -1 then it implies any column is valid.
 */
void EDVBrowserContentsDoFPromptRename(
	edv_browser_struct *browser, const gint row, const gint column
)
{
	gint cx, cy, px, py, pwidth, pheight, name_column;
	cfg_intlist_struct *column_type_intlist;
	const cfg_item_struct *cfg_list;
	edv_object_struct *obj;
	edv_core_struct *core;
	GtkWidget *w;
	GtkCList *clist;

	if((browser == NULL) || (row < 0) || FPromptIsQuery())
	    return;

	w = browser->contents_clist;
	core = browser->core;
	if((w == NULL) || (core == NULL))
	    return;

	clist = GTK_CLIST(w);
	cfg_list = core->cfg_list;

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

	EDVBrowserSyncData(browser);

	/* Find which column is displaying the name */
	name_column = -1;
	column_type_intlist = EDV_GET_INTLIST(
	    EDV_CFG_PARM_BROWSER_CONTENTS_COLUMN
	);
	if(column_type_intlist != NULL)
	{
	    gint i = 0;
	    GList *glist = column_type_intlist->list;
	    while(glist != NULL)
	    {
		if((edv_browser_column_type)glist->data ==
		    EDV_BROWSER_COLUMN_TYPE_NAME
		)
		{
		    name_column = i;
		    break;
		}
		i++;
		glist = g_list_next(glist);
	    }
	}
	/* No column displaying name? */
	if(name_column < 0)
	    return;

	/* Make sure given row index is in bounds */
	if((row < 0) || (row >= clist->rows))
	    return;

	/* Get clist cell geometry */
	if(!gtk_clist_get_cell_geometry(
	    clist, name_column, row,
	    &cx, &cy, &pwidth, &pheight
	))
	    return;

	/* Get root window relative coordinates */
	px = 0;
	py = 0;
	gdk_window_get_deskrelative_origin(
	    clist->clist_window, &px, &py
	);
	px += cx + 0;
	py += cy - 2;		/* Move up a little */

	/* Get object from the specified row on the Contents List */
	obj = EDV_OBJECT(gtk_clist_get_row_data(clist, row));
	if(obj == NULL)
	    return;

	/* Check if the object name is a special notation that cannot
	 * be renamed
	 */
	if(!STRISEMPTY(obj->name))
	{
	    const gchar *name = obj->name;
	    if(!strcmp(name, ".") || !strcmp(name, "..") ||
	       !strcmp(name, "/")
	    )
		return;
	}

	/* Is the clicked on column the name column or given column is -1
	 * implying any column is acceptable?
	 */
	if((column == name_column) || (column < 0))
	{
	    gpointer *cb_data = (gpointer *)g_malloc0(
		3 * sizeof(gpointer)
	    );
	    gchar *value = STRDUP(obj->name);

	    /* Set up callback data */
	    if(cb_data != NULL)
	    {
		cb_data[0] = browser;
		cb_data[1] = clist;
		cb_data[2] = (gpointer)row;
	    }

	    /* Map floating prompt to change values */
	    FPromptSetTransientFor(browser->toplevel);
	    FPromptSetPosition(px, py);
	    FPromptMapQuery(
		NULL,			/* No label */
		value,
		NULL,			/* No tooltip message */
		FPROMPT_MAP_NO_MOVE,	/* Map code */
		pwidth, -1,		/* Width and height */
		0,			/* No buttons */
		cb_data,		/* Callback data */
		NULL,			/* No browse callback */
		EDVBrowserContentsFPromptRenameApplyCB,
		EDVBrowserContentsFPromptCancelCB
	    );

	    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 EDVBrowserContentsObjectAddedNotify(
	edv_browser_struct *browser, const gchar *path,
	const struct stat *lstat_buf
)
{
	gint row;
	const gchar *cur_path;
	gchar *parent_path;
	GtkWidget *w;
	GtkCList *clist;
	edv_core_struct *core;

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

	cur_path = EDVBrowserCurrentLocation(browser);
	w = browser->contents_clist;
	core = browser->core;
	if((cur_path == NULL) || (w == NULL) || (core == NULL))
	    return;

	clist = GTK_CLIST(w);

	parent_path = g_dirname(path);
	if(parent_path == NULL)
	    return;

	/* Skip if the current location different from the parent
	 * directory of the added object
	 */
	if(strcmp(cur_path, parent_path))
	{
	    g_free(parent_path);
	    return;
	}

	/* Check if the added object is already listed */
	row = EDVBrowserContentsFindRowByIndex(
	    browser,
	    (gulong)lstat_buf->st_dev,
	    (gulong)lstat_buf->st_ino
	);
	if(row > -1)
	{
	    /* The object is already listed, update it */
	    edv_object_struct *obj = EDV_OBJECT(
		gtk_clist_get_row_data(clist, row)
	    );
	    if(obj != NULL)
	    {
		EDVObjectSetPath(obj, path);
		EDVObjectSetStat(obj, lstat_buf);
		EDVObjectUpdateLinkFlags(obj);

		/* Update the Contents List row with the new values from
		 * this object
		 */
		EDVBrowserContentsSetRow(
		    core, browser, clist,
		    obj,
		    row
		);
	    }
	}
	else
	{
	    /* Create a new object */
	    edv_object_struct *obj = EDVObjectNew();
	    if(obj != NULL)
	    {
		gint new_row;

		EDVObjectSetPath(obj, path);
		EDVObjectSetStat(obj, lstat_buf);
		EDVObjectUpdateLinkFlags(obj);

		/* Append/transfer this object to the listing */
		new_row = EDVBrowserContentsAppendObject(
		    core, browser, clist, obj
		);
	    }
	}

	g_free(parent_path);
}

/*
 *      This should be called whenever a object has been modified, it will
 *      search for the object and then reupdate the matching row.
 *
 *      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 EDVBrowserContentsObjectModifiedNotify(
	edv_browser_struct *browser, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
)
{
	const gchar *cur_path;
	gchar *parent_path;
	GtkWidget *w;
	GtkCList *clist;
	edv_core_struct *core;

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

	if(new_path == NULL)
	    new_path = path;

	cur_path = EDVBrowserCurrentLocation(browser);
	w = browser->contents_clist;
	core = browser->core;
	if((cur_path == NULL) || (w == NULL) || (core == NULL))
	    return;

	clist = GTK_CLIST(w);

	parent_path = g_dirname(path);
	if(parent_path == NULL)
	    return;

	/* Is the new modified path the same as the current location? */
	if(!strcmp(new_path, cur_path))
	{
	    /* Reget the listing */
	    gtk_clist_freeze(clist);
	    EDVBrowserContentsGetListing(browser, new_path, TRUE);
	    gtk_clist_thaw(clist);
	}
	/* Is the modified path's parent directory the same as the
	 * current location?
	 */
	else if(!strcmp(parent_path, cur_path))
	{
	    /* Update the existing object */
	    const gint row = EDVBrowserContentsFindRowByIndex(
		browser,
		(gulong)lstat_buf->st_dev,
		(gulong)lstat_buf->st_ino
	    );
	    if(row > -1)
	    {
		/* Update this object */
		edv_object_struct *obj = EDV_OBJECT(
		    gtk_clist_get_row_data(clist, row)
		);
		if(obj != NULL)
		{
		    EDVObjectSetPath(obj, new_path);	/* Use new_path */
		    EDVObjectSetStat(obj, lstat_buf);
		    EDVObjectUpdateLinkFlags(obj);

		    /* Update the Contents List row with the new values
		     * from this object
		     */
		    EDVBrowserContentsSetRow(
			core, browser, clist,
			obj,
			row
		    );
		}
	    }
	}

	g_free(parent_path);
}

/*
 *      This should be called whenever a object has been removed, it will
 *      search for the object and then remove the matching row.
 *
 *      The given path must be an absolute path to the object.
 */
void EDVBrowserContentsObjectRemovedNotify(
	edv_browser_struct *browser, const gchar *path
)
{
	const gchar *cur_path;
	gchar *parent_path;
	GtkWidget *w;
	GtkCList *clist;

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

	cur_path = EDVBrowserCurrentLocation(browser);
	w = browser->contents_clist;
	if((cur_path == NULL) || (w == NULL))
	    return;

	clist = GTK_CLIST(w);

	parent_path = g_dirname(path);
	if(parent_path == NULL)
	    return;

	/* Is the removed path the same as the current location? */
	if(!strcmp(path, cur_path))
	{
	    /* Clear the list */
	    EDVBrowserContentsClear(browser);
	}
	/* Is the removed path's parent directory the same as the
	 * current location?
	 */
	else if(!strcmp(parent_path, cur_path))
	{
	    /* Remove the existing object */
	    const gint row = EDVBrowserContentsFindRowByPath(
		browser,
		path
	    );
	    if(row > -1)
		gtk_clist_remove(clist, row);
	}

	g_free(parent_path);
}


/*
 *      This should be called whenever a device has been mounted or
 *	unmounted.
 */
void EDVBrowserContentsMountNotify(
	edv_browser_struct *browser, edv_device_struct *dev,
	gboolean is_mounted
)
{
	gchar *cur_path = NULL, *mount_path = NULL;
	GtkCList *clist;

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

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

	/* Get the current location and mount path */
	cur_path = STRDUP(EDVBrowserCurrentLocation(browser));
	mount_path = STRDUP(dev->mount_path);
	if((cur_path == NULL) || (mount_path == NULL))
	{
	    g_free(cur_path);
	    g_free(mount_path);
	    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 List needs to be updated
	 */
	if(!strcmp(cur_path, mount_path))
	{
	    /* Reget the listing */
	    gtk_clist_freeze(clist);
	    EDVBrowserContentsGetListing(browser, cur_path, TRUE);
	    gtk_clist_thaw(clist);
	}

	g_free(cur_path);
	g_free(mount_path);
}
