#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#ifdef __MSW__
# include <windows.h>
#else
# include <unistd.h>
# include <fnmatch.h>
# if defined(__SOLARIS__)
#  include <sys/mnttab.h>
#  include <sys/vfstab.h>
# elif defined(__FreeBSD__)
/* #  include <mntent.h> */
# else
#  include <mntent.h>
# endif
#endif
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

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

#include "guiutils.h"
#include "cdialog.h"
#include "pulist.h"
#include "fprompt.h"
#include "fb.h"
#include "config.h"


#include "images/icon_file_20x20.xpm"
#include "images/icon_folder_closed_20x20.xpm"
#include "images/icon_folder_opened_20x20.xpm"
#include "images/icon_folder_parent_20x20.xpm"
#include "images/icon_folder_noaccess_20x20.xpm"
#include "images/icon_folder_home_20x20.xpm"
#include "images/icon_link2_20x20.xpm"
#include "images/icon_pipe_20x20.xpm"
#include "images/icon_socket_20x20.xpm"
#include "images/icon_device_block_20x20.xpm"
#include "images/icon_device_character_20x20.xpm"
#include "images/icon_executable_20x20.xpm"
#include "images/icon_drive_fixed_20x20.xpm"
#include "images/icon_drive_root_20x20.xpm"

#include "images/icon_fb_list_standard_20x20.xpm"
#include "images/icon_fb_list_vertical_20x20.xpm"
#include "images/icon_fb_list_vertical_details_20x20.xpm"
#include "images/icon_rename_20x20.xpm"
#include "images/icon_chmod_20x20.xpm"
#include "images/icon_reload_20x20.xpm"
#include "images/icon_select_20x20.xpm"
#include "images/icon_ok_20x20.xpm"
#include "images/icon_cancel_20x20.xpm"

#include "images/icon_trash_32x32.xpm"


/*
 *	List Formats:
 */
typedef enum {
	FB_LIST_FORMAT_STANDARD,		/* Horizontal */
	FB_LIST_FORMAT_VERTICAL,		/* Vertical */
	FB_LIST_FORMAT_VERTICAL_DETAILS		/* Vertical with details */
} FileBrowserListFormat;

/*
 *	Icon Index Values:
 *
 *	The values represent the index of each icon defined in
 *	FB_ICON_DATA_LIST.
 */
typedef enum {
	FB_ICON_FILE,				/* 0 */
	FB_ICON_FOLDER_CLOSED,
	FB_ICON_FOLDER_OPENED,
	FB_ICON_FOLDER_PARENT,
	FB_ICON_FOLDER_NOACCESS,
	FB_ICON_FOLDER_HOME,
	FB_ICON_LINK,
	FB_ICON_PIPE,
	FB_ICON_SOCKET,
	FB_ICON_DEVICE_BLOCK,
	FB_ICON_DEVICE_CHARACTER,		/* 10 */
	FB_ICON_EXECUTABLE,
	FB_ICON_DRIVE_FIXED,
	FB_ICON_DRIVE_ROOT			/* 13 */
} FileBrowserIconNum;

/*
 *	Icons Data List:
 *
 *	The order of this list must correspond with FileBrowserIconNum.
 *
 *	Each set contains 6 gpointers, and the gpointers in the last
 *	set are all NULL.
 */
#define FB_ICON_DATA_LIST	{	\
	"text/plain",			\
	icon_file_20x20_xpm,		\
	NULL, NULL, NULL, NULL,		\
					\
	"directory",			\
	icon_folder_closed_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"directory",			\
	icon_folder_opened_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"directory",			\
	icon_folder_parent_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"directory",			\
	icon_folder_noaccess_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"directory",			\
	icon_folder_home_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"link",				\
	icon_link2_20x20_xpm,		\
	NULL, NULL, NULL, NULL,		\
					\
	"pipe",				\
	icon_pipe_20x20_xpm,		\
	NULL, NULL, NULL, NULL,		\
					\
	"socket",			\
	icon_socket_20x20_xpm,		\
	NULL, NULL, NULL, NULL,		\
					\
	"device/block",			\
	icon_device_block_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"device/character",		\
	icon_device_character_20x20_xpm,\
	NULL, NULL, NULL, NULL,		\
					\
	"executable",			\
	icon_executable_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"drive/fixed",			\
	icon_drive_fixed_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	"drive/root",			\
	icon_drive_root_20x20_xpm,	\
	NULL, NULL, NULL, NULL,		\
					\
	NULL, NULL,			\
	NULL, NULL, NULL, NULL		\
}

/*
 *	Icon:
 */
typedef struct _FileBrowserIcon		FileBrowserIcon;
struct _FileBrowserIcon {

	GdkPixmap	*pixmap;
	GdkBitmap	*mask;
	gint		width, height;
	gchar		*desc;		/* Used for MIME type */

};
#define FILE_BROWSER_ICON(p)	((FileBrowserIcon *)(p))

/*
 *	Object:
 */
typedef struct _FileBrowserObject		FileBrowserObject;
struct _FileBrowserObject {

	gchar		*name,
			*full_path,
			*displayed_name;	/* Name shown on the list
						 * (may be abbriviated) */
	gint		icon_num;
	gint		x, y,			/* Position and size on list */
			width, height;

	struct stat	lstat_buf;

};
#define FILE_BROWSER_OBJECT(p)	((FileBrowserObject *)(p))

/*
 *	Column:
 *
 *	Used in the FileBrowser's list when displaying things that
 *	need columns.
 */
typedef struct _FileBrowserColumn		FileBrowserColumn;
struct _FileBrowserColumn {

	gint		position;
	gchar		*label;
	gint		label_justify;	/* One of GTK_JUSTIFY_* */
	gulong		flags;

	/* If drag is TRUE then it means the column is being dragged
	 * (resized and drag_position indicates the right or lower edge
	 * of the column
	 */
	gboolean	drag;
	gint		drag_position;
	gint		drag_last_drawn_position;

};
#define FILE_BROWSER_COLUMN(p)	((FileBrowserColumn *)(p))

/*
 *	File Browser:
 */
typedef struct _FileBrowser		FileBrowser;
struct _FileBrowser {

	gboolean	map_state;
	gint		busy_count,
			freeze_count;
	GtkAccelGroup	*accelgrp;

	GdkCursor	*cur_busy,
			*cur_column_hresize,
			*cur_translate;

	GtkWidget	*toplevel,
			*main_vbox,
			*dir_pulist_da,
			*dir_pulist_btn,
			*goto_parent_btn,
			*new_directory_btn,
			*rename_btn,
			*refresh_btn,
			*list_format_standard_tb,
			*list_format_vertical_tb,
			*list_format_vertical_details_tb,
			*list_header_da,	/* List header GtkDrawingArea */
			*list_da,	/* List GtkDrawingArea */
			*list_vsb,	/* List vertical scrollbar */
			*list_hsb,	/* List horizontal scrollbar */
			*list_menu,	/* List right-click menu */
			*entry,		/* Location entry */
			*type_combo,	/* File type combo */
			*ok_btn_label,
			*ok_btn,
			*cancel_btn_label,
			*cancel_btn;

	GdkPixmap	*list_pm;

	pulist_struct	*dir_pulist;
	gchar		*cur_location;	/* Current directory */

	gint		block_loop_level;
	gboolean	user_response;	/* TRUE if user clicked on OK */

	FileBrowserListFormat	list_format;

	FileBrowserColumn	**column;
	gint			total_columns;

	fb_type_struct	cur_type;	/* Current file type */
	gchar 		**selected_path;
	gint		total_selected_paths;

	FileBrowserIcon	**icon;
	gint		total_icons;

	gint		uid, euid;
	gint		gid, egid;
	gchar		*home_path;
	gchar		**drive_path;
	gint		total_drive_paths;

	gint		focus_object;
	GList		*selection,
			*selection_end;
	FileBrowserObject	**object;
	gint			total_objects;
	gint			objects_per_row;	/* For use with
							 * FB_LIST_FORMAT_STANDARD */
	gint			last_single_select_object;

	/* Last pointer event information in the list_da */
	gint		button,
			last_x, last_y;
	gulong		last_button1_release_time;	/* In ms */

};
#define FILE_BROWSER(p)		((FileBrowser *)(p))
static FileBrowser file_browser;


/* Utilities */
static gchar **FileBrowserDNDBufParse(
	const gchar *buf, gint len, gint *n
);
static gchar *FileBrowserDNDBufFormat(
	FileBrowserObject **object, gint total, GList *selection,
	gint *len
);

static gboolean FileBrowserObjectNameFilter(
	const gchar *name, const gchar *full_path,
	const gchar *ext
);

static gchar **FileBrowserGetDrivePaths(gint *n);

static void FileBrowserSetBusy(FileBrowser *fb, gboolean busy);

static void FileBrowserSetListFormat(
	FileBrowser *fb, FileBrowserListFormat list_format
);

static void FileBrowserSetLocation(FileBrowser *fb, const gchar *path);
static gchar *FileBrowserGetLocation(FileBrowser *fb);

static void FileBrowserDirPUListUpdate(FileBrowser *fb);
static void FileBrowserDirPUListDraw(FileBrowser *fb);

static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i);
static FileBrowserIcon *FileBrowserIconAppend(
	FileBrowser *fb, guint8 **data, const gchar *desc
);
static gint FileBrowserMatchIconNumFromPath(
	FileBrowser *fb, const gchar *full_path, const struct stat *lstat_buf
);

static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i);
static void FileBrowserObjectUpdateValues(
	FileBrowser *fb, FileBrowserObject *o
);
static FileBrowserObject *FileBrowserObjectAppend(
	FileBrowser *fb, const gchar *name, const gchar *full_path,
	struct stat *lstat_buf
);
static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter);
static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i);
static gint FileBrowserListObjectVisibility(FileBrowser *fb, gint i);
static void FileBrowserListMoveToObject(
	FileBrowser *fb, gint i, gfloat coeff
);
static gint FileBrowserListSelectCoordinates(
	FileBrowser *fb, gint x, gint y
);
static void FileBrowserListHeaderDraw(FileBrowser *fb);
static void FileBrowserListHeaderQueueDraw(FileBrowser *fb);
static void FileBrowserListDraw(FileBrowser *fb);
static void FileBrowserListQueueDraw(FileBrowser *fb);

static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i);
static FileBrowserColumn *FileBrowserListColumnAppend(
	FileBrowser *fb, const gchar *label, gint width
);
static void FileBrowserListColumnsClear(FileBrowser *fb);

static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb);


/* Callbacks */
static void FileBrowserDirPUListItemDestroyCB(gpointer data);
static void FileBrowserIconDestroyCB(gpointer data);
static void FileBrowserObjectDestroyCB(gpointer data);
static void FileBrowserColumnDestroyCB(gpointer data);

static gboolean FileBrowserDragmotionCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
	gpointer data
);
static void FileBrowserDragDataReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);
static void FileBrowserDragDataGetCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);
static void FileBrowserDragDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
);

static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data);

static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data);
static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data);
static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data);
static void FileBrowserOKCB(GtkWidget *widget, gpointer data);
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data);

static gint FileBrowserDirPUListDAEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data);

static gint FileBrowserListHeaderEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint FileBrowserListEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static void FileBrowserListScrollCB(
	GtkAdjustment *adj, gpointer data
);

static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data);
static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data);
static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data);
static void FileBrowserRenameFPromptCB(
	gpointer data, const gchar *value
);
static void FileBrowserRenameCB(GtkWidget *widget, gpointer data);
static void FileBrowserCHModFPromptCB(
	gpointer data, const gchar *value
);
static void FileBrowserCHModCB(GtkWidget *widget, gpointer data);
static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data);

static void FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data);

static void FileBrowserTypeListChangeCB(
	GtkWidget *widget, gpointer data, GList *glist
);


/* Front Ends */
gint FileBrowserInit(void);
void FileBrowserSetStyle(GtkRcStyle *rc_style);
void FileBrowserSetTransientFor(GtkWidget *w);
gboolean FileBrowserIsQuery(void);
void FileBrowserBreakQuery(void);
void FileBrowserReset(void);
gboolean FileBrowserGetResponse(
	const gchar *title,
	const gchar *ok_label, const gchar *cancel_label,
	const gchar *path,
	fb_type_struct **type, gint total_types,
	gchar ***path_rtn, gint *path_total_rtns,
	fb_type_struct **type_rtn
);
void FileBrowserMap(void);
void FileBrowserUnmap(void);
void FileBrowserShutdown(void);

/* File browser file type extension management */
gint FileBrowserTypeListNew(
	fb_type_struct ***list, gint *total,
	const gchar *ext,	/* Space separated list of extensions */
	const gchar *name	/* Descriptive name */
);
void FileBrowserDeleteTypeList(
	fb_type_struct **t, gint total
);


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

#ifndef ISBLANK
# define ISBLANK(c)	(((c) == ' ') || ((c) == '\t'))
#endif

#define OBJISSEL(fb,n)	((((fb) != NULL) && ((n) >= 0)) ?	\
 ((g_list_find(fb->selection, (gpointer)(n)) != NULL) ? TRUE : FALSE) : FALSE)


#define FB_WIDTH		480
#define FB_HEIGHT		-1

#define FB_TB_BTN_WIDTH		(20 + 5)
#define FB_TB_BTN_HEIGHT	(20 + 5)

#define FB_LOC_LIST_MAX_CHARS	25	/* Max chars for each item in the
					 * the locations list */

#define FB_LIST_ITEM_MAX_CHARS	25	/* Max chars for each item in the
					 * list */

#define FB_LIST_HEADER_WIDTH	-1
#define FB_LIST_HEADER_HEIGHT	(20 + 2)

#define FB_LIST_WIDTH		-1
#define FB_LIST_HEIGHT		150

#define FB_LIST_ICON_WIDTH	20
#define FB_LIST_ICON_HEIGHT	20
#define FB_LIST_ICON_BORDER	2	/* Border between icon and text */

#define FB_DEFAULT_TYPE_STR	"All files (*.*)"

#define FB_NEW_DIRECTORY_NAME	"new_directory"


static gchar *G_STRCAT(gchar *s, const gchar *s2)
{
	if(s != NULL) {
	    if(s2 != NULL) {
		gchar *sr = g_strconcat(s, s2, NULL);
		g_free(s);
		s = sr;
	    }
	} else {
	    if(s2 != NULL)
		s = STRDUP(s2);
	    else
		s = STRDUP("");
	}
	return(s);
}


/*
 *	Returns a list of full paths parsed from the given DND buffer.
 */
static gchar **FileBrowserDNDBufParse(
	const gchar *buf, gint len, gint *n
)
{
	const gchar *buf_end = buf + len;	/* Record end of buffer */
	gchar **strv = NULL;
	gint i, strc = 0;

	if(n != NULL)
	    *n = strc;

	if((buf == NULL) || (len <= 0))
	    return(strv);

	/* Iterate through buffer that contains a list of '\0' character
	 * separated strings
	 */
	while(buf < buf_end)
	{
	    const gchar *s = buf;
	    gint s_len = STRLEN(s);

	    /* Is the protocol prefix of the string one that we want? */
	    if(g_strcasepfx(s, "file://"))
	    {
		/* Seek past protocol "file://" */
		s += STRLEN("file://");

		/* Seek past "username:password@host:port"
		 * too many programs fail to do this!
		 */
		s = (s != NULL) ? strchr(s, '/') : NULL;

		/* Able to seek to full path portion of the string? */
		if(s != NULL)
		{
		    /* Got full path portion, now add it to the list of
		     * return strings
		     */
		    i = strc;
		    strc = i + 1;
		    strv = (gchar **)g_realloc(
			strv, strc * sizeof(gchar *)
		    );
		    if(strv == NULL)
		    {
			strc = 0;
			break;
		    }
		    strv[i] = STRDUP(s);
		}
	    }

	    /* Seek buf to next string.  First seek buf past this string
	     * then seek past any '\0' deliminator(s) until we reach
	     * the next string or the end of the buffer
	     */
	    buf += s_len;
	    while((buf < buf_end) ? (*buf == '\0') : FALSE)
		buf++;
	}

	/* Update total return value */
	if(n != NULL)
	    *n = strc;

	return(strv);
}

/*
 *	Generates a DND buffer based on the list of objects that are
 *	selected based on the given selection.
 */
static gchar *FileBrowserDNDBufFormat(
	FileBrowserObject **object, gint total, GList *selection,
	gint *len
)
{
	gchar *buf = NULL;
	gint i, buf_len = 0;
	GList *glist;
	FileBrowserObject *o;


	/* Iterate through selection */
	for(glist = selection; glist != NULL; glist = g_list_next(glist))
	{
	    i = (gint)glist->data;
	    if((i >= 0) && (i < total))
		o = object[i];
	    else
		o = NULL;
	    if((o != NULL) ? (o->full_path != NULL) : FALSE)
	    {
		gchar *url = g_strdup_printf(
		    "file://%s",
		    o->full_path
		);
		gint url_len = STRLEN(url);
		gint last_buf_len = buf_len;

		/* Increase allocation of buffer for this url string
		 * which we will append to the buffer (include the
		 * '\0' character counted in buf_len)
		 */
		buf_len += url_len + 1;
		buf = (gchar *)g_realloc(buf, buf_len * sizeof(gchar));
		if(buf == NULL)
		{
		    buf_len = 0;
		    break;
		}
		memcpy(
		    buf + last_buf_len,
		    (url != NULL) ? url : "",
		    url_len + 1		/* Include the '\0' character */
		);

		g_free(url);
	    }
	}

	/* Update return value */
	if(len != NULL)
	    *len = buf_len;

	return(buf);
}


/*
 *	Checks if the object name specified by name and full_path
 *	match the extensions given in ext.
 */
static gboolean FileBrowserObjectNameFilter(
	const gchar *name, const gchar *full_path,
	const gchar *ext
)
{
	gchar *st, *st_end, cur_ext[80];
	gint cur_ext_len, name_len;
	gboolean use_regex;


	if((name == NULL) || (full_path == NULL) || (ext == NULL))
	    return(FALSE);

	name_len = STRLEN(name);

	while(ISBLANK(*ext) || (*ext == ','))
	    ext++;

	/* Iterate through each extension in ext */
	while(*ext != '\0')
	{
	    use_regex = FALSE;

	    /* Copy current extension string in ext to cur_ext and
	     * seek ext to next position
	     */
	    cur_ext_len = 0;
	    st = cur_ext;
	    st_end = cur_ext + 79;	/* Set end 1 character premature */
	    while((st < st_end) && !ISBLANK(*ext) && (*ext != ',') &&
		  (*ext != '\0')
	    )
	    {
		/* Use this opportunity to check if there are characters
		 * in the extension string to warrent the use of regex
		 */
		if((*ext == '*') || (*ext == '?'))
		    use_regex = TRUE;

		*st++ = *ext++;
		cur_ext_len++;
	    }
	    *st = '\0';

	    /* Seek ext to next extension string */
	    while(ISBLANK(*ext) || (*ext == ','))
		ext++;

	    /* Check if there is a match */
#ifndef __MSW__
	    if(use_regex)
	    {
		/* Use regex to match */
		if(!fnmatch(cur_ext, name, 0))
		    return(TRUE);
	    }
	    else
#endif
	    {
		/* Check if cur_ext is a postfix of name */
		if(name_len >= cur_ext_len)
		{
		    if(!g_strcasecmp(
			name + name_len - cur_ext_len,
			cur_ext
		    ))
			return(TRUE);
		}
	    }
	}
	return(FALSE);
}


/*
 *	Returns a list of strings describing the drive paths.
 */
static gchar **FileBrowserGetDrivePaths(gint *n)
{
#if defined(__MSW__)
/* Win32 */
	gchar drive_letter = 'a';
	gchar drive_name[10];
	gint i, strc = 0;
	gchar **strv = NULL;
/*	struct stat stat_buf; */

	for(drive_letter = 'a'; drive_letter <= 'g'; drive_letter++)
	{
	    g_snprintf(
		drive_name, sizeof(drive_name),
		"%c:\\",
		drive_letter
	    );
/*
	    if(stat(drive_name, &stat_buf))
		break;
 */
	    i = strc;
	    strc = i + 1;
	    strv = (gchar **)g_realloc(
		strv, strc * sizeof(gchar *)
	    );
	    if(strv == NULL)
	    {
		strc = 0;
		break;
	    }

	    strv[i] = STRDUP(drive_name);
	}

	if(n != NULL)
	    *n = strc;

	return(strv);
#elif defined(__FreeBSD__)
/* FreeBSD */
	if(n != NULL )
	    *n = 0;
	return(NULL);
#else
/* UNIX */
	gint i, strc = 0;
	gchar **strv = NULL;
#ifdef __SOLARIS__
	struct vfstab *vfs_ptr = NULL;
	int mtback;
#else
	struct mntent *mt_ptr;
#endif

	/* Open system devices list file */
#ifdef __SOLARIS__
	FILE *fp = FOpen("/etc/vfstab", "rb");
#else
	FILE *fp = setmntent("/etc/fstab", "rb");
#endif
	if(fp == NULL)
	{
	    if(n != NULL)
		*n = strc;
	    return(strv);
	}

	/* Begin reading system devices list file */
#ifdef __SOLARIS__
	vfs_ptr = (struct vfstab *)g_malloc(sizeof(struct vfstab));
	mtback = getvfsent(fp, vfs_ptr);
	while(mtback != 0)
#else
	mt_ptr = getmntent(fp);
	while(mt_ptr != NULL)
#endif
	{
	    i = strc;
	    strc = i + 1;
	    strv = (gchar **)g_realloc(
		strv, strc * sizeof(gchar *)
	    );
	    if(strv == NULL)
	    {
		strc = 0;
		break;
	    }

	    /* Get mount path as the drive path */
#ifdef __SOLARIS__
	    strv[i] = STRDUP(vfs_ptr->vfs_mountp);
#else
	    strv[i] = STRDUP(mt_ptr->mnt_dir);
#endif

	    /* Read next mount entry */
#ifdef __SOLARIS__
	    mtback = getmntent(fp, vfs_ptr);
#else
	    mt_ptr = getmntent(fp);
#endif
	}


	/* Close system devices list file */
#ifdef __SOLARIS__
	FClose(fp);
	fp = NULL;
	vfs_ptr = NULL;
#else
	endmntent(fp);
	fp = NULL;
#endif

	if(n != NULL)
	    *n = strc;

	return(strv);
#endif
}


/*
 *	Sets the file browser busy or ready.
 */
static void FileBrowserSetBusy(FileBrowser *fb, gboolean busy)
{
	GdkCursor *cursor;
	GtkWidget *w;


	if(fb == NULL)
	    return;

	w = fb->toplevel;
	if(w != NULL)
	{
	    if(busy)
	    {
		/* Increase busy count */
		fb->busy_count++;

		/* If already busy then don't change anything */
		if(fb->busy_count > 1)
		    return;

		cursor = fb->cur_busy;
	    }
	    else
	    {
		/* Reduce busy count */
		fb->busy_count--;
		if(fb->busy_count < 0)
		    fb->busy_count = 0;

		/* If still busy do not change anything */
		if(fb->busy_count > 0)
		    return;

		cursor = NULL;  /* Use default cursor */
	    }

	    /* Update toplevel window's cursor */
	    if(w->window != NULL)
	    {
		gdk_window_set_cursor(w->window, cursor);
		gdk_flush();
	    }
	}
}

/*
 *	Sets the list format and updates the list.
 */
static void FileBrowserSetListFormat(
	FileBrowser *fb, FileBrowserListFormat list_format
)
{
	GtkWidget *w;
	FileBrowserColumn *column;

	if(fb == NULL)
	    return;

	/* Skip if no change in list format */
	if(fb->list_format == list_format)
	    return;

	fb->freeze_count++;

	/* Set new list format */
	fb->list_format = list_format;

	/* Update list format toggle buttons, make sure we freeze so the
	 * toggle callback does not recurse
	 */
#define SET_TB(p,b)				\
{ if((p) != NULL) {				\
 gtk_toggle_button_set_active(			\
  GTK_TOGGLE_BUTTON(p), b			\
 );						\
} }
	switch(list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL:
	    SET_TB(fb->list_format_standard_tb, FALSE);
	    SET_TB(fb->list_format_vertical_tb, TRUE);
	    SET_TB(fb->list_format_vertical_details_tb, FALSE);
	    break;

	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	    SET_TB(fb->list_format_standard_tb, FALSE);
	    SET_TB(fb->list_format_vertical_tb, FALSE);
	    SET_TB(fb->list_format_vertical_details_tb, TRUE);
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    SET_TB(fb->list_format_standard_tb, TRUE);
	    SET_TB(fb->list_format_vertical_tb, FALSE);
	    SET_TB(fb->list_format_vertical_details_tb, FALSE);
	    break;
	}
#undef SET_TB

	/* Begin updating things to recognize the new list format */

	/* Update list columns */
	FileBrowserListColumnsClear(fb);
	switch(list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	    /* Set up list columns */
	    /* Name */
	    column = FileBrowserListColumnAppend(
		fb, "Name", 145
	    );
	    /* Size */
	    column = FileBrowserListColumnAppend(
		fb, "Size", 70
	    );
	    column->label_justify = GTK_JUSTIFY_RIGHT;
	    /* Permissions */
	    column = FileBrowserListColumnAppend(
		fb, "Permissions", 78
	    );
	    /* Last Modified */
	    column = FileBrowserListColumnAppend(
		fb, "Last Modified", 160
	    );

	    /* Show list header */
	    w = fb->list_header_da;
	    if(w != NULL)
		gtk_widget_show(w);
	    break;

	  case FB_LIST_FORMAT_VERTICAL:
	  case FB_LIST_FORMAT_STANDARD:
	    /* Hide list header */
	    w = fb->list_header_da;
	    if(w != NULL)
		gtk_widget_hide(w);
	    break;
	}
	gtk_widget_queue_resize(fb->toplevel);


	/* Update list, this will reget the list of objects and 
	 * map/unmap list widgets to reflect the new list format
	 *
	 * The list will also be redrawn
	 */
	FileBrowserListUpdate(fb, NULL);

	fb->freeze_count--;
}

/*
 *	Sets the new location and updates the list and directory pulist.
 *
 *	The new location must be a directory, if it is not then its
 *	parent will be used instead.
 */
static void FileBrowserSetLocation(FileBrowser *fb, const gchar *path)
{
	gchar *new_location, *new_name;
	GtkWidget *w;

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

	if(path == fb->cur_location)
	    return;

	/* Make a copy of the given path which will be modified and
	 * checked before (if) it is finally accepted
	 */
	new_location = STRDUP(path);
	new_name = NULL;


	/* Special notation checks */

	/* Current location? */
	if(!strcmp(new_location, "."))
	{
	    g_free(new_location);
#ifdef __MSW__
	    new_location = (fb->cur_location != NULL) ?
		STRDUP(fb->cur_location) : STRDUP("c:");
#else
	    new_location = (fb->cur_location != NULL) ?
		STRDUP(fb->cur_location) : STRDUP("/");
#endif
	}

	/* Parent? */
	if(!strcmp(new_location, ".."))
	{
	    g_free(new_location);
#ifdef __MSW__
	    new_location = (fb->cur_location != NULL) ?
		g_dirname(fb->cur_location) : STRDUP("c:");
#else
	    new_location = (fb->cur_location != NULL) ?
		g_dirname(fb->cur_location) : STRDUP("/");
#endif
	}

	/* Home directory prefix? */
	if(*new_location == '~')
	{
	    /* Prefix the home directory to the new location path */
	    const gchar *home = fb->home_path;
	    gchar *s = g_strdup_printf(
		"%s%s",
		(home != NULL) ? home : "",
		new_location + 1
	    );
	    g_free(new_location);
	    new_location = s;
	}

	/* At this point we need to check if the new location is an
	 * absolute path, if it is not then the current working dir
	 * must be prefixed to it
	 */
	if(!g_path_is_absolute(new_location))
	{
	    gchar *s, cwd[PATH_MAX];
	    if(getcwd(cwd, sizeof(cwd)) != NULL)
		cwd[sizeof(cwd) - 1] = '\0';
	    else
		strcpy(cwd, "/");
	    s = STRDUP(PrefixPaths(cwd, new_location));
	    g_free(new_location);
	    new_location = s;
	}

	/* Make sure the new location is a directory, if it is not then
	 * get its parent instead
	 */
	if(!ISPATHDIR(new_location))
	{
	    gchar *parent = g_dirname(new_location);
	    const gchar *name = strrchr(new_location, G_DIR_SEPARATOR);

	    /* Record that we have an object name */
	    g_free(new_name);
	    new_name = (name != NULL) ? STRDUP(name + 1) : NULL;

	    /* Record parent directory */
	    g_free(new_location);
	    new_location = parent;
	}

	/* Strip tailing deliminator */
	if(new_location != NULL)
	{
#ifdef __MSW__
	    gchar *s = strrchr(new_location, G_DIR_SEPARATOR);
	    gint prefix_len = STRLEN("x:\\");
#else
	    gchar *s = strrchr(new_location, G_DIR_SEPARATOR);
	    gint prefix_len = STRLEN("/");
#endif
	    while(s >= (new_location + prefix_len))
	    {
		if((s[0] == G_DIR_SEPARATOR) &&
		   (s[1] == '\0')
		)
		    *s = '\0';
		else
		    break;
		s--;
	    }
	}

	/* No change in the location? */
	if((fb->cur_location != NULL) ?
	    !strcmp(fb->cur_location, new_location) : FALSE
	)
	{
	    /* No change in location, but check if we still need to
	     * update the lists if the lists were reset/cleared
	     */
	    if(fb->total_objects > 0)
	    {
		/* Lists were not cleared, so no need to update */
		g_free(new_location);
		g_free(new_name);
		return;
	    }
	}

	/* Accept and set new location */
	g_free(fb->cur_location);
	fb->cur_location = new_location;
	new_location = NULL;

	/* Update file name entry */
	w = fb->entry;
	if(w != NULL)
	{
	    gtk_entry_set_text(
		GTK_ENTRY(w),
		(!STRISEMPTY(new_name)) ? new_name : ""
	    );
	    gtk_entry_set_position(GTK_ENTRY(w), -1);
	}

	/* Get new listing of objects due to location change */
	FileBrowserListUpdate(fb, NULL);

	/* Update paths in the directory popup list */
	FileBrowserDirPUListUpdate(fb);

	/* Redraw */
	FileBrowserDirPUListDraw(fb);

	g_free(new_location);
	g_free(new_name);
}

/*
 *	Returns the current location, never returns NULL.
 */
static gchar *FileBrowserGetLocation(FileBrowser *fb)
{
#ifdef __MSW__
	return((fb != NULL) ? fb->cur_location : "c:\\");
#else
	return((fb != NULL) ? fb->cur_location : "/");
#endif
}


/*
 *	DND "drag_motion" signal callback.
 */
static gboolean FileBrowserDragmotionCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
	gpointer data
)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if((dc == NULL) || (fb == NULL))
	    return(FALSE);

	if(dc->actions & GDK_ACTION_COPY)
	    gdk_drag_status(dc, GDK_ACTION_COPY, t);
	else
	    gdk_drag_status(dc, 0, t);

	return(TRUE);
}

/*
 *	DND "drag_data_received" signal callback.
 */
static void FileBrowserDragDataReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if((widget == NULL) || (dc == NULL) || (fb == NULL))
	    return;

	if(selection_data == NULL)
	    return;

	/* Make sure the DND info is one that we want */
	if((info == 0) || (info == 1) || (info == 2))
	{
	    /* Handle by destination widget */
	    /* List */
	    if(widget == fb->list_da)
	    {
		/* When dropping to the list, first check if there is
		 * exactly one object and if so then check if it leads
		 * to a directory, in which case we will set the new
		 * location.  Otherwise we just set it to the file
		 * name entry.
		 */
		gint i, strc;
		gchar **strv = FileBrowserDNDBufParse(
		    (const gchar *)selection_data->data,
		    selection_data->length,
		    &strc
		);
		const gchar *new_location = (strc == 1) ? strv[0] : NULL;
		if((new_location != NULL) ? ISPATHDIR(new_location) : FALSE)
		{
		    /* Exactly one object dropped and it leads to a
		     * directory so go to the new location
		     */
		    FileBrowserSetLocation(fb, new_location);
		}
		else
		{
		    /* Not one object or the one object does not lead to
		     * a directory, so just set the dropped objects to
		     * the file name entry.
		     */
		    GtkEntry *entry = GTK_ENTRY(fb->entry);
		    gchar *s = NULL;
		    for(i = 0; i < strc; i++)
		    {
			s = G_STRCAT(s, strv[i]);
			if((i + 1) < strc)
			    s = G_STRCAT(s, ",");
		    }
		    gtk_entry_set_text(entry, s);
		    gtk_entry_set_position(entry, -1);
		    g_free(s);
		}
		for(i = 0; i < strc; i++)
		    g_free(strv[i]);
		g_free(strv);
	    }
	    /* File name entry */
	    else if(widget == fb->entry)
	    {
		GtkEntry *entry = GTK_ENTRY(widget);
		gchar *s = NULL;
		gint i, strc;
		gchar **strv = FileBrowserDNDBufParse(
		    (const gchar *)selection_data->data,
		    selection_data->length,
		    &strc
		);
		for(i = 0; i < strc; i++)
		{
		    s = G_STRCAT(s, strv[i]);
		    if((i + 1) < strc)
			s = G_STRCAT(s, ",");
		    g_free(strv[i]);
		}
		g_free(strv);
		gtk_entry_set_text(entry, s);
		gtk_entry_set_position(entry, -1);
		g_free(s);
	    }
	}
}

/*
 *	DND "drag_data_get" signal callback.
 */
static void FileBrowserDragDataGetCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	gboolean data_sent = FALSE;
	FileBrowser *fb = FILE_BROWSER(data);
	if((widget == NULL) || (fb == NULL))
	    return;

	/* Make sure the DND info is one that we support */
	if((info == 0) || (info == 1) || (info == 2))
	{
	    /* Handle by destination widget */
	    /* List */
	    if(widget == fb->list_da)
	    {
		gint buf_len;
		gchar *buf = FileBrowserDNDBufFormat(
		    fb->object, fb->total_objects, fb->selection,
		    &buf_len
		);
		if(buf != NULL)
		{
		    /* Send out DND data */
		    gtk_selection_data_set(
			selection_data,
			GDK_SELECTION_TYPE_STRING,
			8,	/* 8 bits per character */
			buf, buf_len
		    );
		    data_sent = TRUE;
		    g_free(buf);
		}
	    }
	}

	/* Failed to send out DND data? */
	if(!data_sent)
	{
	    /* Send a response indicating error */
	    const gchar *s = "Error";
	    gtk_selection_data_set(
		selection_data,
		GDK_SELECTION_TYPE_STRING,
		8,		/* 8 bits per character */
		s,
		STRLEN(s) + 1	/* Include the '\0' character */
	    );
	    data_sent = TRUE;
	}
}

/*
 *      DND "drag_data_delete" signal callback.
 */
static void FileBrowserDragDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if((widget == NULL) || (fb == NULL))
	    return;

	/* Typical this means that some objects have been deleted
	 * so we need to refresh.
	 */
	FileBrowserRefreshCB(fb->refresh_btn, fb);
}

/*
 *	List format toggle callback.
 */
static void FileBrowserListFormatToggleCB(GtkWidget *widget, gpointer data)
{
	gint list_format = -1;
	GtkToggleButton *tb = GTK_TOGGLE_BUTTON(widget);
	FileBrowser *fb = FILE_BROWSER(data);
	if((widget == NULL) || (fb == NULL))
	    return;

	/* If format toggling is frozen then do not do anything or
	 * else recursing may occure
	 */
	if(fb->freeze_count > 0)
	    return;

	fb->freeze_count++;

	/* Do not let toggle button become untoggled when toggling
	 * was not frozen
	 */
	if(!tb->active)
	{
	    gtk_toggle_button_set_active(tb, TRUE);
	    fb->freeze_count--;
	    return;
	}

	/* Begin checking which toggle button it was so we know which
	 * list format to set to
	 */
	/* Standard */
	if(widget == fb->list_format_standard_tb)
	    list_format = FB_LIST_FORMAT_STANDARD;
	/* Vertical */
	else if(widget == fb->list_format_vertical_tb)
	    list_format = FB_LIST_FORMAT_VERTICAL;
	/* Vertical with details */
	else if(widget == fb->list_format_vertical_details_tb)
	    list_format = FB_LIST_FORMAT_VERTICAL_DETAILS;

	/* Did we find out which list format to use? */
	if(list_format > -1)
	    FileBrowserSetListFormat(fb, list_format);

	fb->freeze_count--;
}

/*
 *	Go to parent callback.
 */
static void FileBrowserGoToParentCB(GtkWidget *widget, gpointer data)
{
	const gchar *cur_location;
	gchar *parent;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	/* Get current location, then get its parent */
	cur_location = FileBrowserGetLocation(fb);
	parent = (cur_location != NULL) ?
	    g_dirname(cur_location) : NULL;

	/* Go to the parent of the current location */
	if(parent != NULL)
	    FileBrowserSetLocation(fb, parent);

	g_free(parent);
}

/*
 *	New Directory callback.
 */
static void FileBrowserNewDirectoryCB(GtkWidget *widget, gpointer data)
{
	mode_t m;
	gchar *name, *full_path;
	const gchar *cur_location;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

#if defined(PROG_LANGUAGE_SPANISH)
# define TITLE_MKDIR_FAILED     "Cree Guía Nueva Fallada"
#elif defined(PROG_LANGUAGE_FRENCH)
# define TITLE_MKDIR_FAILED     "Créer Le Nouvel Annuaire Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
# define TITLE_MKDIR_FAILED     "Schaffen Sie Neues Versagten Verzeichnis"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define TITLE_MKDIR_FAILED     "Creare L'Elenco Nuovo Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
# define TITLE_MKDIR_FAILED     "Creëer Nieuwe Gids Verzuimde"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define TITLE_MKDIR_FAILED     "Crie Novo Guia Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define TITLE_MKDIR_FAILED     "Skap New Directory Failed"
#else
# define TITLE_MKDIR_FAILED	"Create New Directory Failed"
#endif

#define MESSAGE_MKDIR_FAILED(s)        {        \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                            \
  TITLE_MKDIR_FAILED,				\
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

	/* Get current location */
	cur_location = FileBrowserGetLocation(fb);
	if(cur_location == NULL)
	    return;


	FileBrowserSetBusy(fb, TRUE);

	/* Generate new name and full path */
	name = STRDUP(FB_NEW_DIRECTORY_NAME);
	full_path = STRDUP(PrefixPaths(cur_location, name));

#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
	m =	S_IRUSR | S_IWUSR | S_IXUSR |
		S_IRGRP | S_IWGRP | S_IXGRP |
		S_IROTH | S_IWOTH | S_IXOTH;
#else
	m = 0;
#endif
	/* Create new directory */
#ifdef __MSW__
	if(mkdir(full_path))
#else
	if(mkdir(full_path, m))
#endif
	{
	    gint i;
	    gboolean created = FALSE;

	    /* Failed to create, try creating it under a different
	     * name.
	     */
	    for(i = 2; i < 100; i++)
	    {
		/* Regenerate new name and full path */
		g_free(name);
		g_free(full_path);
		name = g_strdup_printf(
		    "%s%i",
		    FB_NEW_DIRECTORY_NAME, i
		);
		full_path = STRDUP(PrefixPaths(cur_location, name));

		/* Try to create new directory, if success then
		 * break out of the loop.
		 */
#ifdef __MSW__
		if(!mkdir(full_path))
#else
		if(!mkdir(full_path, m))
#endif
		{
		    created = TRUE;
		    break;
		}

		/* Error creating directory and it was not because it
		 * already exists?
		 */
		if(errno != EEXIST)
		{
		    gchar *buf;

#if defined(PROG_LANGUAGE_SPANISH)
buf = STRDUP("Incapaz de crear guía nueva.");
#elif defined(PROG_LANGUAGE_FRENCH)
buf = STRDUP("Incapable pour créer le nouvel annuaire.");
#elif defined(PROG_LANGUAGE_GERMAN)
buf = STRDUP("Unfähig, neues Verzeichnis zu schaffen.");
#elif defined(PROG_LANGUAGE_ITALIAN)
buf = STRDUP("Incapace per creare l'elenco nuovo.");
#elif defined(PROG_LANGUAGE_DUTCH)
buf = STRDUP("Onbekwaam nieuwe gids te creëren.");
#elif defined(PROG_LANGUAGE_PORTUGUESE)
buf = STRDUP("Incapaz de criar novo guia.");
#elif defined(PROG_LANGUAGE_NORWEGIAN)
buf = STRDUP("Maktesløs skape ny katalog.");
#else
		    switch(errno)
		    {
		      case EACCES:
			buf = STRDUP(
"You do not have sufficient permission to create a\n\
new directory at this location."
			);
			break;

		      case ENAMETOOLONG:
			buf = STRDUP(
"The name for the new directory is too long."
			);
			break;

		      case ENOENT:
			buf = STRDUP(
"A compoent of the current location is a dangling symbolic link."
			);
			break;

		      case ENOMEM:
			buf = STRDUP(
"The system is out of memory."
			);
			break;

		      case EROFS:
			buf = STRDUP(
"The current location is at a read-only filesystem."
			);
			break;
#ifdef ELOOP
		      case ELOOP:
			buf = STRDUP(
"Too many symbolic links were encountered at this location."
			);
			break;
#endif
		      case ENOSPC:
			buf = STRDUP(
"The device is out of free space."
			);
			break;

		      default:
			buf = STRDUP(
"Unable to create new directory."
			);
			break;
		    }
#endif
		    MESSAGE_MKDIR_FAILED(buf);
		    g_free(buf);
		    break;
		}
	    }
	    /* Failed to create new directory? */
	    if(!created)
	    {
		g_free(name);
		g_free(full_path);
		FileBrowserSetBusy(fb, FALSE);
		return;
	    }
	}

	/* Update list */
	FileBrowserListUpdate(fb, NULL);

	/* Select object */
	if(name != NULL)
	{
	    gint i;
	    const FileBrowserObject *o;
	    for(i = 0; i < fb->total_objects; i++)
	    {
		o = fb->object[i];
		if((o != NULL) ? (o->name == NULL) : TRUE)
		    continue;

		if(!strcmp(o->name, name))
		{
		    /* Select this object */
		    fb->focus_object = i;
		    fb->selection = g_list_append(
			fb->selection, (gpointer)i
		    );
		    fb->selection_end = g_list_last(fb->selection);

		    /* Scroll to this object */
		    FileBrowserListMoveToObject(fb, i, 0.5f);

		    break;
		}
	    }
	}
	FileBrowserListQueueDraw(fb);

	/* Update selected objects on entry */
	FileBrowserEntrySetSelectedObjects(fb);

	FileBrowserSetBusy(fb, FALSE);

	g_free(name);
	g_free(full_path);
#undef MESSAGE_MKDIR_FAILED
#undef TITLE_MKDIR_FAILED
}

/*
 *	Refresh callback.
 */
static void FileBrowserRefreshCB(GtkWidget *widget, gpointer data)
{
	gint last_scroll_x = 0, last_scroll_y = 0;
	GtkWidget *w;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	FileBrowserSetBusy(fb, TRUE);

	/* Record last scroll position */
	w = fb->list_hsb;
	if(w != NULL)
	    last_scroll_x = (gint)GTK_ADJUSTMENT_GET_VALUE(
		GTK_RANGE(w)->adjustment
	    );
	w = fb->list_vsb;
	if(w != NULL)
	    last_scroll_y = (gint)GTK_ADJUSTMENT_GET_VALUE(
		GTK_RANGE(w)->adjustment  
	    );


	/* Begin refreshing */

	/* Directory popup list */
	FileBrowserDirPUListUpdate(fb);

	/* List */
	FileBrowserListUpdate(fb, NULL);


	/* Restore scroll position */
	w = fb->list_hsb;
	if(w != NULL)
	{
	    GtkRange *range = GTK_RANGE(w);
	    GtkAdjustment *adj = range->adjustment;
	    if(adj != NULL)
		gtk_adjustment_set_value(
		    adj,
		    CLIP(
			(gfloat)last_scroll_x,
			adj->lower,
			MAX(adj->upper - adj->page_size, adj->lower)
		    )
		);
	}
	w = fb->list_vsb;
	if(w != NULL)
	{
	    GtkRange *range = GTK_RANGE(w);
	    GtkAdjustment *adj = range->adjustment;
	    if(adj != NULL)
		gtk_adjustment_set_value(
		    adj,
		    CLIP(
			(gfloat)last_scroll_y,
			adj->lower,
			MAX(adj->upper - adj->page_size, adj->lower)
		    )
		);
	}

	FileBrowserSetBusy(fb, FALSE);
}


/*
 *	Updates the directory popup list based on the current location.
 */
static void FileBrowserDirPUListUpdate(FileBrowser *fb)
{
	const gchar *cur_location;
	pulist_struct *pulist;
	const FileBrowserIcon *icon;
	struct stat stat_buf;
	GtkDestroyNotify destroy_cb = FileBrowserDirPUListItemDestroyCB;

	if(fb == NULL)
	    return;

	pulist = fb->dir_pulist;
	if(pulist == NULL)
	    return;

	FileBrowserSetBusy(fb, TRUE);

	/* Delete any existing items in the list */
	PUListClear(pulist);

#define GET_ICON(p)	{		\
 if(stat((p), &stat_buf))		\
  icon = NULL;				\
 else					\
  icon = FileBrowserGetIcon(		\
   fb,					\
   FileBrowserMatchIconNumFromPath(	\
    fb, (p), &stat_buf			\
   )					\
  );					\
}

/* Takes string s and shortens it, allocating a new string s2
 * that is a shortened (as needed) version of s.  The string s2
 * must be deallocated.
 */
#define ALLOC_STRING_SHORTENED		\
{ if(s != NULL) {			\
 const gint len = STRLEN(s),		\
	    max_characters = FB_LOC_LIST_MAX_CHARS;\
					\
 /* Length of s is too long? */		\
 if(len > max_characters)		\
  s2 = g_strdup_printf(			\
   "...%s",				\
   &s[len - max_characters + 3]		\
  );					\
 else					\
  s2 = STRDUP(s);			\
} else {				\
 s2 = NULL;				\
} }

	/* Get current location and set items for it and each parent
	 * location.
	 */
	cur_location = FileBrowserGetLocation(fb);
	if(cur_location != NULL)
	{
	    gint i, icon_num;
	    gchar	*s = STRDUP(cur_location),
			*sd = strrchr(s, G_DIR_SEPARATOR),
			*s2;

	    /* Current location */
	    if(stat(s, &stat_buf))
		icon_num = -1;
	    else
		icon_num = FileBrowserMatchIconNumFromPath(
		    fb, s, &stat_buf
		);
	    if(icon_num == FB_ICON_FOLDER_CLOSED)
		icon_num = FB_ICON_FOLDER_OPENED;
	    icon = FileBrowserGetIcon(fb, icon_num);
	    ALLOC_STRING_SHORTENED
	    if(icon != NULL)
		i = PUListAddItemPixText(
		    pulist, s2, icon->pixmap, icon->mask
		);
	    else
		i = PUListAddItem(pulist, s2);
	    g_free(s2);
	    PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb);

	    /* Parent locations */
#ifdef __MSW__
	    while(sd > (s + 3))
#else
	    while(sd > s)
#endif
	    {
		*sd = '\0';

		GET_ICON(s);
		ALLOC_STRING_SHORTENED
		if(icon != NULL)
		    i = PUListAddItemPixText(
			pulist, s2, icon->pixmap, icon->mask
		    );
		else
		    i = PUListAddItem(pulist, s2);
		g_free(s2);
		PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb);

		sd = strrchr(s, G_DIR_SEPARATOR);
	    }

	    /* Toplevel */
#ifdef __MSW__
	    /* On Win32, use s as is from the above parent fetching loop */
	    sd = strchr(s, G_DIR_SEPARATOR);
	    if((sd != NULL) ? (*(sd + 1) != '\0') : FALSE)
	    {
		*(sd + 1) = '\0';
		GET_ICON(s);
		ALLOC_STRING_SHORTENED
		if(icon != NULL)
		    i = PUListAddItemPixText(
			pulist, s2, icon->pixmap, icon->mask
		    );
		else
		    i = PUListAddItem(pulist, s2);
		g_free(s2);
		PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb);
	    }
	    g_free(s);
#else
	    g_free(s);
	    s = STRDUP("/");
	    GET_ICON(s);
	    ALLOC_STRING_SHORTENED
	    if(icon != NULL)
		i = PUListAddItemPixText(
		    pulist, s2, icon->pixmap, icon->mask
		);
	    else
		i = PUListAddItem(pulist, s2);
	    g_free(s2);
	    PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb);
	    g_free(s);
#endif
	}

#ifndef __MSW__
	/* Home */
	if(fb->home_path != NULL)
	{
	    const gchar *s = fb->home_path;
	    gint i;
	    gchar *s2;
	    GET_ICON(s);
	    ALLOC_STRING_SHORTENED
	    if(icon != NULL)
		i = PUListAddItemPixText(
		    pulist, s2, icon->pixmap, icon->mask
		);
	    else
		i = PUListAddItem(pulist, s2);
	    g_free(s2);
	    PUListSetItemDataFull(pulist, i, STRDUP(s), destroy_cb);
	}
#endif

	/* Drives */
	if(fb->total_drive_paths > 0)
	{
	    gint i, i2;
	    const gchar *s;
	    gchar *s2;

	    for(i = 0; i < fb->total_drive_paths; i++)
	    {
		s = fb->drive_path[i];
		if(s == NULL)
		    continue;

		/* Ignore toplevel */
		if(!strcmp(s, "/"))
		    continue;

#if !defined(__MSW__)
		/* Ignore drives that do not have absolute paths */
		if(!g_path_is_absolute(s))
		    continue;
#endif

#if defined(__MSW__)
		icon = FileBrowserGetIcon(fb, FB_ICON_DRIVE_FIXED);
#else
		GET_ICON(s);
#endif
		ALLOC_STRING_SHORTENED
		if(icon != NULL)
		    i2 = PUListAddItemPixText(
			pulist, s2, icon->pixmap, icon->mask
		    );
		else
		    i2 = PUListAddItem(
			pulist, s2
		    );
		g_free(s2);
		PUListSetItemDataFull(pulist, i2, STRDUP(s), destroy_cb);
	    }


	}

/* Other things to be added to the popup list, like mounted drives? */





	FileBrowserSetBusy(fb, FALSE);

#undef GET_ICON
#undef ALLOC_STRING_SHORTENED
}

/*
 *	Redraws the directory popup list's drawing area.
 */
static void FileBrowserDirPUListDraw(FileBrowser *fb)
{
	gint state, width, height;
	GdkFont *font;
	GdkDrawable *drawable;
	GdkWindow *window;
	GdkGC *gc;
	GtkStyle *style;
	GtkWidget *w;
	const gchar *cur_location;


	if(fb == NULL)
	    return;

	w = fb->dir_pulist_da;
	window = (w != NULL) ? w->window : NULL;
	if(window == NULL)
	    return;

	drawable = (GdkDrawable *)fb->list_pm;
	if(drawable == NULL)
	    drawable = (GdkDrawable *)window;

	state = GTK_WIDGET_STATE(w);
	style = gtk_widget_get_style(w);
	gdk_window_get_size(window, &width, &height);
	if((style == NULL) || (width <= 0) || (height <= 0))
	    return;

	font = style->font;


	/* Draw background */
	gdk_draw_rectangle(
	    drawable, style->base_gc[state], TRUE,
	    0, 0, width, height
	);
#if 0
	if(style->bg_pixmap[state] != NULL)
	    gtk_style_apply_default_background(
		style, drawable, FALSE, state,
		NULL,
		0, 0, width, height
	    );
#endif

	/* Draw current location */
	cur_location = FileBrowserGetLocation(fb);
	if(cur_location != NULL)
	{
	    gint x = 2;
	    gint icon_num;
	    const FileBrowserIcon *icon;
	    struct stat stat_buf;

	    /* Match icon */
	    if(!stat(cur_location, &stat_buf))
		icon_num = FileBrowserMatchIconNumFromPath(
		    fb, cur_location, &stat_buf
		);
	    else
		icon_num = -1;
	    /* If regular folder then get the opened folder */
	    if(icon_num == FB_ICON_FOLDER_CLOSED)
		icon_num = FB_ICON_FOLDER_OPENED;
	    icon = FileBrowserGetIcon(fb, icon_num);

	    /* Draw icon */
	    gc = style->fg_gc[state];
	    if(icon != NULL)
	    {
		gint	cx = x,
			cy = (height / 2) - (icon->height / 2);

		gdk_gc_set_clip_mask(gc, icon->mask);
		gdk_gc_set_clip_origin(gc, cx, cy);
		gdk_draw_pixmap(
		    drawable, gc, icon->pixmap,
		    0, 0, cx, cy, icon->width, icon->height
		);
		gdk_gc_set_clip_mask(gc, NULL);

		x += icon->width + FB_LIST_ICON_BORDER;
	    }

	    if(TRUE)
	    {
		gchar *s;
		GdkTextBounds b;
		const gint	len = STRLEN(cur_location),
				max_characters = FB_LOC_LIST_MAX_CHARS;
		GdkGC *gc_text = style->text_gc[state];

		if(len > max_characters)
		    s = g_strdup_printf(
			"...%s",
			&cur_location[len - max_characters + 3]
		    );
		else
		    s = STRDUP(cur_location);

		gdk_string_bounds(font, s, &b);
		gdk_draw_string(
		    drawable, font, gc_text,
		    x + b.lbearing,
		    ((height - (font->ascent + font->descent)) / 2) +
			font->ascent,
		    s
		);
		g_free(s);
	    }
	}


	/* Draw focus rectangle if widget is in focus */
	if(GTK_WIDGET_HAS_FOCUS(w) && GTK_WIDGET_SENSITIVE(w))
	    gtk_draw_focus(
		style, drawable,
		0, 0, width - 1, height - 1
	    );

	/* Send drawable to window if drawable is not the window */
	if(drawable != window)
	    gdk_draw_pixmap(
		window, style->fg_gc[state], drawable,
		0, 0, 0, 0, width, height
	    );
}

/*
 *	Returns the icon at index i or NULL on error.
 */
static FileBrowserIcon *FileBrowserGetIcon(FileBrowser *fb, gint i)
{
	if(fb == NULL)
	    return(NULL);

	if((i < 0) || (i >= fb->total_icons))
	    return(NULL);
	else
	    return(fb->icon[i]);
}

/*
 *	Appends an icon to the list.
 */
static FileBrowserIcon *FileBrowserIconAppend(
	FileBrowser *fb, guint8 **data, const gchar *desc
)
{
	gint i;
	FileBrowserIcon *icon;

	if((fb == NULL) || (data == NULL))
	    return(NULL);

	i = MAX(fb->total_icons, 0);
	fb->total_icons = i + 1;
	fb->icon = (FileBrowserIcon **)g_realloc(
	    fb->icon, fb->total_icons * sizeof(FileBrowserIcon *)
	);
	if(fb->icon == NULL)
	{
	    fb->total_icons = 0;
	    return(NULL);
	}

	fb->icon[i] = icon = FILE_BROWSER_ICON(g_malloc0(
	    sizeof(FileBrowserIcon)
	));
	if(icon != NULL)
	{
	    icon->pixmap = GDK_PIXMAP_NEW_FROM_XPM_DATA(&icon->mask, data);
	    gdk_window_get_size(icon->pixmap, &icon->width, &icon->height);
	    icon->desc = STRDUP(desc);
	}
	return(icon);
}


/*
 *	Returns the icon index appropriate for the object specified by
 *	full_path and lstat_buf.
 */
static gint FileBrowserMatchIconNumFromPath(
	FileBrowser *fb, const gchar *full_path, const struct stat *lstat_buf
)
{
	gint i, uid, gid;
	const gchar *ext;
	mode_t m;

	if(fb == NULL)
	    return(FB_ICON_FILE);

	if(full_path != NULL)
	{
	    const gchar *s;

#ifndef __MSW__
	    /* Home directory? */
	    s = fb->home_path;
	    if((s != NULL) ? !strcmp(s, full_path) : FALSE)
		return(FB_ICON_FOLDER_HOME);

	    /* Toplevel? */
	    if(!strcmp(full_path, "/"))
		return(FB_ICON_DRIVE_ROOT);
#endif
	    /* Drive path? */
	    for(i = 0; i < fb->total_drive_paths; i++)
	    {
		s = fb->drive_path[i];
		if((s != NULL) ? !strcmp(s, full_path) : FALSE)
		    return(FB_ICON_DRIVE_FIXED);
	    }

	    /* Get extension (if any) */
	    ext = strrchr(full_path, '.');
	}
	else
	{
	    ext = NULL;
	}

	/* Get object's statistics */
	if(lstat_buf != NULL)
	{
	    m = lstat_buf->st_mode;
	    uid = lstat_buf->st_uid;
	    gid = lstat_buf->st_gid;
	}
	else
	{
	    m = 0;
	    uid = 0;
	    gid = 0;
	}

	/* Directory? */
	if(S_ISDIR(m))
	{
#ifdef __MSW__
	    return(FB_ICON_FOLDER_CLOSED);
#else
	    /* Accessable? */
	    if(fb->euid != 0)
	    {
		/* For non-root process id's we should check if the
		 * process or the process' group owns the object and
		 * respectively see if the object (the directory) is
		 * executable (accessable) and return the appropriate
		 * icon number.
		 */
		/* This process owns object? */
		if(fb->euid == uid)
		    return((m & S_IXUSR) ?
			FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
		    );
		/* This process' group id owns object? */
		else if(fb->egid == gid)
		    return((m & S_IXGRP) ?
			FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
		    );
		/* Anonymous */
		else
		    return((m & S_IXOTH) ?
			FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
		    );
	    }
	    else
	    {
		/* Root always owns the object, so check if owner has
		 * access.
		 */
		return((m & S_IXUSR) ?
			FB_ICON_FOLDER_CLOSED : FB_ICON_FOLDER_NOACCESS
		);
	    }
#endif
	}

#ifdef __MSW__
	if(ext != NULL)
	{
	    if(!g_strcasecmp(ext, ".exe") ||
	       !g_strcasecmp(ext, ".com") ||
	       !g_strcasecmp(ext, ".bat")
	    )
		return(FB_ICON_EXECUTABLE);
	    else
		return(FB_ICON_FILE);
	}
	else
	{
	    return(FB_ICON_FILE);
	}
#else
	if(S_ISLNK(m))
	    return(FB_ICON_LINK);
	else if(S_ISFIFO(m))
	    return(FB_ICON_PIPE);
	else if(S_ISSOCK(m))
	    return(FB_ICON_SOCKET);
	else if(S_ISBLK(m))
	    return(FB_ICON_DEVICE_BLOCK);
	else if(S_ISCHR(m))
	    return(FB_ICON_DEVICE_CHARACTER);
	else if((m & S_IXUSR) || (m & S_IXGRP) || (m & S_IXOTH))
	    return(FB_ICON_EXECUTABLE);
	else
	    return(FB_ICON_FILE);
#endif
}


/*
 *      Returns the object at index i or NULL on error.
 */
static FileBrowserObject *FileBrowserGetObject(FileBrowser *fb, gint i)
{
	if(fb == NULL)
	    return(NULL);

	if((i < 0) || (i >= fb->total_objects))
	    return(NULL);
	else
	    return(fb->object[i]);
}

/*
 *	Updates the Object's values.
 *
 *	The name and lstat_buf must be updated prior to this call since
 *	the information used to update the other values are based on
 *	them.
 *
 *	The updated values are; icon_num, width, and height.
 */
static void FileBrowserObjectUpdateValues(
	FileBrowser *fb, FileBrowserObject *o
)
{
	gint icon_num;
	GdkFont *font;
	GtkStyle *style;
	GtkWidget *w;
	FileBrowserIcon *icon;

	if((fb == NULL) || (o == NULL))
	    return;

	/* Get the list widget's style and font */
	w = fb->list_da;
	style = (w != NULL) ? gtk_widget_get_style(w) : NULL;
	font = (style != NULL) ? style->font : NULL;

	/* Get icon number based on the object's path and statistics */
	o->icon_num = icon_num = FileBrowserMatchIconNumFromPath(
	    fb, o->full_path, &o->lstat_buf
	);

	/* Get icon */
	icon = FileBrowserGetIcon(fb, icon_num);


	/* Set displayed_name from name, abbreviate it as needed */
	if(o->name != NULL)
	{
	    const gchar *name = o->name;
	    const gint	len = STRLEN(name),
			max_chars = FB_LIST_ITEM_MAX_CHARS;

	    g_free(o->displayed_name);
	    if((len > max_chars) && (len > 3))
		o->displayed_name = g_strdup_printf(
		    "...%s",
		    name + (len - max_chars + 3)
		);
	    else
		o->displayed_name = STRDUP(name);
	}
	else
	{
	    g_free(o->displayed_name);
	    o->displayed_name = STRDUP("");
	}


	/* Update size */
	if((o->displayed_name != NULL) && (font != NULL))
	{
	    const gchar *name = o->displayed_name;
	    const gint font_height = font->ascent + font->descent;
	    GdkTextBounds b;
	    gdk_string_bounds(font, name, &b);
	    if(icon != NULL)
	    {
		o->width = MAX(
		    icon->width + FB_LIST_ICON_BORDER + 2 + b.width,
		    1
		);
		o->height = MAX(
		    icon->height,
		    font_height
		);
	    }
	    else
	    {
		o->width = MAX(
		    b.width,
		    1
		);
		o->height = MAX(
		    font_height,
		    1
		);
	    }
	}
	else
	{
	    if(icon != NULL)
	    {
		o->width = icon->width;
		o->height = icon->height;
	    }
	    else
	    {
		o->width = 1;
		o->height = 1;
	    }
	}
}

/*
 *	Appends an object to the list.
 *
 *	Returns the new object or NULL on error.  The calling function
 *	needs to calculate the position.
 */
static FileBrowserObject *FileBrowserObjectAppend(
	FileBrowser *fb, const gchar *name, const gchar *full_path,
	struct stat *lstat_buf
)
{
	gint i;
	FileBrowserObject *o;

	if((fb == NULL) || (full_path == NULL))
	    return(NULL);

	/* Allocate one more pointer for the array */
	i = MAX(fb->total_objects, 0);
	fb->total_objects = i + 1;
	fb->object = (FileBrowserObject **)g_realloc(
	    fb->object, fb->total_objects * sizeof(FileBrowserObject *)
	);
	if(fb->object == NULL)
	{
	    fb->total_objects = 0;
	    return(NULL);
	}

	/* Create a new Object */
	fb->object[i] = o = FILE_BROWSER_OBJECT(g_malloc0(
	    sizeof(FileBrowserObject)
	));
	if(o != NULL)
	{
	    o->name = STRDUP(name);
	    o->full_path = STRDUP(full_path);
	    if(lstat_buf != NULL)
		memcpy(&o->lstat_buf, lstat_buf, sizeof(struct stat));
	    else
		memset(&o->lstat_buf, 0x00, sizeof(struct stat));

	    /* Update values */
	    FileBrowserObjectUpdateValues(fb, o);
	}

	return(o);
}

/*
 *	Updates the list.
 */
static void FileBrowserListUpdate(FileBrowser *fb, const gchar *filter)
{
	const gint	border_major = 5,
			border_minor = 2;
	gint i, width, height;
	gboolean match_all_files;
	gboolean need_resize = FALSE;
	gint	list_x_max = 0, list_y_max = 0,
		list_x_inc = 0, list_x_page_inc = 0,
		list_y_inc = 0,	list_y_page_inc = 0;
	const gchar *cur_location, *ext;
	GdkWindow *window;
	GtkWidget *w;

	if(fb == NULL)
	    return;

	FileBrowserSetBusy(fb, TRUE);

	/* Get size of list */
	w = fb->list_da;
	window = (w != NULL) ? w->window : NULL;
	if(window != NULL)
	{
	    gdk_window_get_size(window, &width, &height);
	}
	else
	{
	    width = 0;
	    height = 0;
	}

	/* Get extension specified in the current file type */
	ext = (filter != NULL) ? filter : fb->cur_type.ext;
	if(ext != NULL)
	    match_all_files = (!strcmp(ext, "*.*") || !strcmp(ext, "*")) ? TRUE : FALSE;
	else
	    match_all_files = TRUE;

	/* Get listing of contents in current location */

	/* Delete current objects list and the selection */

	/* Unselect all */
	fb->focus_object = -1;
	g_list_free(fb->selection);
	fb->selection = fb->selection_end = NULL;

	/* Delete all objects */
	for(i = 0; i < fb->total_objects; i++)
	    FileBrowserObjectDestroyCB(fb->object[i]);
	g_free(fb->object);
	fb->object = NULL;
	fb->total_objects = 0;	

	/* Reset objects per row */
	fb->objects_per_row = 0;


	/* Get new objects */
	cur_location = FileBrowserGetLocation(fb);
	if(cur_location != NULL)
	{
	    gint strc, objects_per_row = 0;
	    gchar **strv = GetDirEntNames2(cur_location, &strc);
	    if(strv != NULL)
	    {
		gchar *s, *full_path;
		struct stat lstat_buf;
		FileBrowserObject *o = NULL;
		gint	border_x = border_minor,
			border_y = border_minor,
			cur_x = border_x,
			cur_y = border_y,
			longest_width = 0,
			last_longest_width = longest_width;

		StringQSort(strv, strc);

/* Sets the x and y position of the object o.  Uses cur_x and cur_y to
 * keep track of the last position
 */
#define SET_OBJECT_POSITION	{		\
 if(o != NULL) {				\
  switch(fb->list_format) {			\
   case FB_LIST_FORMAT_VERTICAL_DETAILS:	\
   case FB_LIST_FORMAT_VERTICAL:		\
    if(longest_width < o->width)                \
    {                                           \
     last_longest_width = longest_width =       \
      o->width;                                 \
    }                                           \
    o->x = cur_x;				\
    o->y = cur_y;				\
    cur_y += o->height + 1;			\
    break;					\
   case FB_LIST_FORMAT_STANDARD:		\
    if(longest_width < o->width)		\
    {						\
     /* Note that last_longest_width gets	\
      * updated with longest_width but is not	\
      * reset to 0 when we go to a new column,	\
      * this is so that last_longest_width can	\
      * be used in case longest_width was reset	\
      * before the next column but when we get	\
      * to the next column there are no objects	\
      * and longest_width = 0 is not really	\
      * accurate anymore.			\
      */					\
     last_longest_width = longest_width =	\
      o->width;					\
    }						\
    o->x = cur_x;                               \
    o->y = cur_y;                               \
    cur_y += o->height + 1;			\
    objects_per_row++;				\
    /* Need to increment "column" when next	\
     * object height would go off the list	\
     *						\
     * Also keep objects per row at no more	\
     * than 7 objects				\
     */						\
    if(((cur_y + o->height) > height) ||	\
       (objects_per_row >= 7)			\
    ) {						\
     if(objects_per_row > fb->objects_per_row)	\
      fb->objects_per_row = objects_per_row;	\
     objects_per_row = 0;			\
     cur_y = border_y;				\
     cur_x += longest_width + border_major;	\
     longest_width = 0;				\
    }						\
    break;					\
  }						\
 }						\
}

		/* We will make several passes through the strv array
		 * to load objects in proper order, directories first
		 * and all other types of objects afterwards.  Each
		 * string in strv is deleted as the object is added
		 * or determined to be filtered from the list.
		 */

		/* Add directories */
		for(i = 0; i < strc; i++)
		{
		    s = strv[i];
		    if(s == NULL)
			continue;

		    /* Skip special directory notations */
		    if(!strcmp(s, ".") ||
		       !strcmp(s, "..")
		    )
		    {
			g_free(strv[i]);
			strv[i] = s = NULL;
			continue;
		    }

		    /* Generate full path to object */
		    full_path = STRDUP(PrefixPaths(cur_location, s));

		    /* Skip objects that are not or do not go to a directory */
		    if(!ISPATHDIR(full_path))
		    {
			g_free(full_path);
			/* Do not delete string from strv, let
			 * subsequent loops through strv handle it
			 */
			continue;
		    }

		    /* Get local stats of object and append object to
		     * the list
		     */
#ifdef __MSW__
		    if(stat(full_path, &lstat_buf))
#else
		    if(lstat(full_path, &lstat_buf))
#endif
			memset(&lstat_buf, 0x00, sizeof(struct stat));
		    o = FileBrowserObjectAppend(fb, s, full_path, &lstat_buf);
		    SET_OBJECT_POSITION

		    /* Delete full path, it is no longer needed */
		    g_free(full_path);

		    /* Delete this name so it dosen't get messed with on
		     * the next loop.
		     */
		    g_free(strv[i]);
		    strv[i] = s = NULL;
		}

		/* Add all other objects */
		for(i = 0; i < strc; i++)
		{
		    s = strv[i];
		    if(s == NULL)
			continue;

		    /* Generate full path to object */
		    full_path = STRDUP(PrefixPaths(cur_location, s));

		    /* Check if this object name gets filtered */
		    if(match_all_files ? FALSE : !FileBrowserObjectNameFilter(
			s, full_path, ext
		    ))
		    {
			g_free(full_path);
			g_free(s);
			strv[i] = s = NULL;
			continue;
		    }

		    /* Get local stats of object and append object to
		     * the list
		     */
#ifdef __MSW__
		    if(stat(full_path, &lstat_buf))
#else
		    if(lstat(full_path, &lstat_buf))
#endif
			memset(&lstat_buf, 0x00, sizeof(struct stat));
		    o = FileBrowserObjectAppend(fb, s, full_path, &lstat_buf);
		    SET_OBJECT_POSITION

		    /* Delete full path, it is no longer needed */
		    g_free(full_path);

		    g_free(s);
		    strv[i] = s = NULL;
		}

		/* No more loops, delete string list strv.  All strings
		 * remaining in strv should have been deleted in the
		 * last loop above.
		 */
		g_free(strv);

		/* Use last object to set list bounds, these bounds will
		 * be used in updating of the scrollbar adjustments.
		 */
		if(o != NULL)
		{
		    list_x_max = o->x + last_longest_width + border_x;
		    list_y_max = o->y + o->height + border_y;
		    list_x_inc = (gint)(width * 0.25);
		    list_x_page_inc = (gint)(width * 0.5);
		    list_y_inc = o->height;
		    list_y_page_inc = (gint)(height * 0.5);
		}
#undef SET_OBJECT_POSITION
	    }
	}


	/* Update widgets based on list format */
	switch(fb->list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    w = fb->list_hsb;
	    if((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE)
	    {
		gtk_widget_hide(w);
		need_resize = TRUE;
	    }
	    w = fb->list_vsb;
	    if(w != NULL)
	    {
		GtkRange *range = GTK_RANGE(w);
		GtkAdjustment *adj = range->adjustment;
		if(adj != NULL)
		{
		    adj->lower = 0.0f;
		    adj->upper = (gfloat)list_y_max;
		    adj->value = adj->lower;
		    adj->step_increment = (gfloat)list_y_inc;
		    adj->page_increment = (gfloat)list_y_page_inc;
		    adj->page_size = (gfloat)height;
		    gtk_signal_emit_by_name(
			GTK_OBJECT(adj), "changed"
		    );
		    gtk_signal_emit_by_name(
			GTK_OBJECT(adj), "value_changed"
		    );

		    /* If content size is larger than visible size then
		     * map the scrollbar, otherwise unmap the scrollbar
		     * since it would not be needed.
		     */
		    if((adj->upper - adj->lower) > adj->page_size)
		    {
			if(!GTK_WIDGET_MAPPED(w))
			{
			    gtk_widget_show(w);
			    need_resize = TRUE;
			}
		    }
		    else
		    {
			if(GTK_WIDGET_MAPPED(w))
			{
			    gtk_widget_hide(w);
			    need_resize = TRUE;
			}
		    }
		}
	    }
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    w = fb->list_vsb;
	    if((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE)
	    {
		gtk_widget_hide(w);
		need_resize = TRUE;
	    }
	    w = fb->list_hsb;
	    if(w != NULL)
	    {
		GtkRange *range = GTK_RANGE(w);
		GtkAdjustment *adj = range->adjustment;
		if(!GTK_WIDGET_MAPPED(w))
		{
		    gtk_widget_show(w);
		    need_resize = TRUE;
		}
		if(adj != NULL)
		{
		    adj->lower = 0.0f;
		    adj->upper = (gfloat)list_x_max;
		    adj->value = adj->lower;
		    adj->step_increment = (gfloat)list_x_inc;
		    adj->page_increment = (gfloat)list_x_page_inc;
		    adj->page_size = (gfloat)width;
		    gtk_signal_emit_by_name(
			GTK_OBJECT(adj), "changed"
		    );
		    gtk_signal_emit_by_name(
			GTK_OBJECT(adj), "value_changed"
		    );

		    /* If content size is larger than visible size then
		     * map the scrollbar, otherwise unmap the scrollbar
		     * since it would not be needed
		     */
		    if((adj->upper - adj->lower) > adj->page_size)
		    {
			if(!GTK_WIDGET_MAPPED(w))
			{
			    gtk_widget_show(w);
			    need_resize = TRUE;
			}
		    }
		    else
		    {
			if(GTK_WIDGET_MAPPED(w))
			{
			    gtk_widget_hide(w);
			    need_resize = TRUE;
			}
		    }
		}
	    }
	    break;
	}

	/* Need to resize due to widgets being mapped or unmapped? */
	if(need_resize)
	    gtk_widget_queue_resize(fb->toplevel);

	FileBrowserSetBusy(fb, FALSE);
}


/*
 *	Sets the DND icon based on the object i.
 */
static void FileBrowserListObjectSetDNDIcon(FileBrowser *fb, gint i)
{
	FileBrowserIcon *icon;
	FileBrowserObject *o = FileBrowserGetObject(fb, i);
	if(fb == NULL)
	    return;

	/* Get object's icon (if any) */
	icon = FileBrowserGetIcon(fb, o->icon_num);
	if(icon == NULL)
	    return;

	/* Set new DND icon if it has a pixmap */
	if(icon->pixmap != NULL)
	    GUIDNDSetDragIcon(
		icon->pixmap, icon->mask,
		icon->width / 2, icon->height / 2
	    );
}

/*
 *	Returns one of GTK_VISIBILITY_* based on the visibility of
 *	object i.
 */
static gint FileBrowserListObjectVisibility(FileBrowser *fb, gint i)
{
	gint scroll_x = 0, scroll_y = 0;
	gint x, y, width, height;
	GdkWindow *window;
	GtkWidget *w;
	FileBrowserObject *o;

	if(fb == NULL)
	    return(GTK_VISIBILITY_NONE);

	/* Get list's GdkWindow and its size, making sure it exists and
	 * its size is positive
	 */
	w = fb->list_da;
	window = (w != NULL) ? w->window : NULL;
	if(window == NULL)
	    return(GTK_VISIBILITY_NONE);
	gdk_window_get_size(window, &width, &height);
	if((width <= 0) || (height <= 0))
	    return(GTK_VISIBILITY_NONE);

	/* Get object */
	o = FileBrowserGetObject(fb, i);
	if(o == NULL)
	    return(GTK_VISIBILITY_NONE);

	/* Get current scroll position */
	if(fb->list_hsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_hsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
	}
	if(fb->list_vsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_vsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
	}

	/* Check visibility by list display format */
	switch(fb->list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    y = o->y - scroll_y;
	    if(((y + o->height) <= 0) || (y >= height))
		return(GTK_VISIBILITY_NONE);
	    else if((y < 0) || ((y + o->height) > height))
		return(GTK_VISIBILITY_PARTIAL);
	    else
		return(GTK_VISIBILITY_FULL);
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    x = o->x - scroll_x;
	    if(((x + o->width) <= 0) || (x >= width))
		return(GTK_VISIBILITY_NONE);
	    else if((x < 0) || ((x + o->width) > width))
		return(GTK_VISIBILITY_PARTIAL);
	    else
		return(GTK_VISIBILITY_FULL);
	    break;
	}

	return(GTK_VISIBILITY_NONE);
}

/*
 *	Scrolls to the object i, using the given coefficient as an
 *	offset within the list window for the final scroll position.
 */
static void FileBrowserListMoveToObject(
	FileBrowser *fb, gint i, gfloat coeff
)
{
	gint x, y, width, height;
	GdkWindow *window;
	GtkWidget *w;
	FileBrowserObject *o;

	if(fb == NULL)
	    return;

	/* Get list's GdkWindow and its size, make sure that it exists
	 * and its size is positive
	 */
	w = fb->list_da;
	window = (w != NULL) ? w->window : NULL;
	if(window == NULL)
	    return;
	gdk_window_get_size(window, &width, &height);
	if((width <= 0) || (height <= 0))
	    return;

	/* Get object */
	o = FileBrowserGetObject(fb, i);
	if(o == NULL)
	    return;

	/* Move to object depending on list display format */
	switch(fb->list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    y = o->y + (o->height * coeff);
	    y -= (gint)(height * CLIP(coeff, 0.0f, 1.0f));
	    if(fb->list_vsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_vsb);
		GtkAdjustment *adj = range->adjustment;
		if(adj != NULL)
		    gtk_adjustment_set_value(
			adj,
			CLIP(
			    y,
			    adj->lower,
			    MAX(adj->upper - adj->page_size, adj->lower)
			)
		    );
	    }
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    x = o->x + (o->width * coeff);
	    x -= (gint)(width * CLIP(coeff, 0.0f, 1.0f));
	    if(fb->list_hsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_hsb);
		GtkAdjustment *adj = range->adjustment;
		if(adj != NULL)
		    gtk_adjustment_set_value(
			adj,
			CLIP(
			    x,
			    adj->lower,
			    MAX(adj->upper - adj->page_size, adj->lower)
			)
		    );
	    }
	    break;
	}
}

/*
 *	Returns the object index number that is found at the given
 *	coordinates or -1 on failed match.
 */
static gint FileBrowserListSelectCoordinates(
	FileBrowser *fb, gint x, gint y
)
{
	gint scroll_x = 0, scroll_y = 0;

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

	/* Get scroll position */
	if(fb->list_hsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_hsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
	}
	if(fb->list_vsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_vsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
	}

	/* Find object by list display format */
	switch(fb->list_format)
	{
	    gint i, cx, cy;
	    FileBrowserObject *o;

	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    for(i = 0; i < fb->total_objects; i++)
	    {
		o = fb->object[i];
		if(o == NULL)
		    continue;

		if((o->width <= 0) || (o->height <= 0))
		    continue;

		/* Calculate object position with scrolled adjust */
		cx = o->x;
		cy = o->y - scroll_y;

		/* In bounds? */
		if((x >= cx) && (x < (cx + o->width)) &&
		   (y >= cy) && (y < (cy + o->height))
		)
		    return(i);
	    }
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    for(i = 0; i < fb->total_objects; i++)
	    {
		o = fb->object[i];
		if(o == NULL)
		    continue;

		if((o->width <= 0) || (o->height <= 0))
		    continue;

		/* Calculate object position with scrolled adjust */
		cx = o->x - scroll_x;
		cy = o->y;

		/* In bounds? */
		if((x >= cx) && (x < (cx + o->width)) &&
		   (y >= cy) && (y < (cy + o->height))
		)
		    return(i);
	    }
	    break;
	}

	return(-1);
}

/*
 *	Redraws the File Browser's list header.
 */
static void FileBrowserListHeaderDraw(FileBrowser *fb)
{
	gint width, height, font_height;
	GdkFont *font;
	GdkDrawable *drawable;
	GdkWindow *window;
	GdkGC *gc;
	GtkStateType state;
	GtkStyle *style;
	GtkWidget *w = (fb != NULL) ? fb->list_header_da : NULL;
	if(w == NULL)
	    return;

	if(!GTK_WIDGET_VISIBLE(w))
	    return;

	window = w->window;
	if(window == NULL)
	    return;

	drawable = (GdkDrawable *)fb->list_pm;
	if(drawable == NULL)
	    drawable = window;

	state = GTK_WIDGET_STATE(w);
	style = gtk_widget_get_style(w);
	gdk_window_get_size(window, &width, &height);
	if((style == NULL) || (width <= 0) || (height <= 0))
	    return;

	font = style->font;
	if(font == NULL)
	    return;

	font_height = font->ascent + font->descent;


	/* Draw background */
	gc = style->bg_gc[state];
#if 0
	bg_pixmap = style->bg_pixmap[state];
	if(bg_pixmap != NULL)
	{
	    gdk_gc_set_fill(gc, GDK_TILED);
	    gdk_gc_set_tile(gc, bg_pixmap);
	    gdk_gc_set_ts_origin(gc, 0, 0);
	}
	else
	{
	    gdk_gc_set_fill(gc, GDK_SOLID);
	    gdk_gc_set_foreground(gc, &style->bg[state]);
	}
#endif
	gdk_draw_rectangle(
	    drawable, gc, TRUE,
	    0, 0, width, height
	);
#if 0
	gdk_gc_set_fill(gc, GDK_SOLID);
	gdk_gc_set_tile(gc, NULL);
#endif


	/* Draw frame around entire header */
	gtk_draw_box(
	    style, drawable, state,
	    GTK_SHADOW_OUT,
	    0, 0, width, height
	);

	/* Any column headings to draw? */
	if(fb->total_columns > 0)
	{
	    gint i, last_column_position = 0;
	    GdkRectangle rect;
	    FileBrowserColumn *column;
	    GdkGC *gc_text;

	    /* Draw each column heading */
	    for(i = 0; i < fb->total_columns; i++)
	    {
		column = fb->column[i];
		if(column == NULL)
		    continue;

		rect.x = last_column_position;
		rect.y = 0;
		rect.width = MAX(column->position - last_column_position, 0);
		rect.height = height;

		gtk_draw_box(
		    style, drawable, state,
		    GTK_SHADOW_OUT,
		    rect.x, rect.y, rect.width, rect.height
		);

		gc_text = (column->flags & GTK_SENSITIVE) ?
		    style->text_gc[state] : style->text_gc[GTK_STATE_INSENSITIVE];

		if((column->label != NULL) ? (*column->label != '\0') : FALSE)
		{
		    const gchar *label = column->label;
		    GdkTextBounds b;
		    gdk_string_bounds(font, label, &b);
		    gdk_gc_set_clip_origin(gc_text, 0, 0);
		    gdk_gc_set_clip_rectangle(gc_text, &rect);
		    switch(column->label_justify)
		    {
		      case GTK_JUSTIFY_CENTER:
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + (rect.width / 2) - (b.width / 2) + b.lbearing,
			    rect.y + (rect.height / 2) -
				(font_height / 2) + font->ascent,
			    label
			);
			break;

		      case GTK_JUSTIFY_RIGHT:
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + rect.width - b.width - 5 + b.lbearing,
			    rect.y + (rect.height / 2) -
				(font_height / 2) + font->ascent,
			    label
			);
			break;

		      default:	/* GTK_JUSTIFY_LEFT */
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + 5 + b.lbearing,
			    rect.y + (rect.height / 2) -
				(font_height / 2) + font->ascent,
			    label
			);
			break;
		    }
		    gdk_gc_set_clip_rectangle(gc_text, NULL);
		}

		if((column->flags & GTK_SENSITIVE) &&
		   (column->flags & GTK_HAS_FOCUS) &&
		   GTK_WIDGET_HAS_FOCUS(w) &&
		   GTK_WIDGET_SENSITIVE(w)
		)
		    gdk_draw_rectangle(
			drawable, style->fg_gc[state], FALSE,
			rect.x, rect.y, rect.width - 1, rect.height - 1
		    );

		last_column_position = column->position;
	    }
	}

	/* Send drawable to window if drawable is not the window */
	if(drawable != (GdkDrawable *)window)
	    gdk_draw_pixmap(
		window, style->fg_gc[state], drawable,
		0, 0, 0, 0, width, height
	    );
}

/*
 *	Queues a redraw of the File Browser's list header.
 */
static void FileBrowserListHeaderQueueDraw(FileBrowser *fb)
{
	GtkWidget *w = (fb != NULL) ? fb->list_header_da : NULL;
	if(w != NULL)
	    gtk_widget_queue_draw(w);
}

/*
 *	Redraws the File Browser's list.
 */
static void FileBrowserListDraw(FileBrowser *fb)
{
	gint width, height, font_height;
	gint scroll_x = 0, scroll_y = 0;
	GdkFont *font;
	GdkDrawable *drawable;
	GdkWindow *window;
	GdkGC *gc;
	GtkStateType state;
	GtkStyle *style;
	GtkWidget *w = (fb != NULL) ? fb->list_da : NULL;
	if(w == NULL)
	    return;

	if(!GTK_WIDGET_VISIBLE(w))
	    return;

	window = w->window;
	if(window == NULL)
	    return;

	drawable = (GdkDrawable *)fb->list_pm;
	if(drawable == NULL)
	    drawable = window;

	state = GTK_WIDGET_STATE(w);
	style = gtk_widget_get_style(w);
	gdk_window_get_size(window, &width, &height);
	if((style == NULL) || (width <= 0) || (height <= 0))
	    return;

	font = style->font;
	if(font == NULL)
	    return;

	font_height = font->ascent + font->descent;


	/* Draw background */
	gc = style->base_gc[state];
	gdk_draw_rectangle(
	    drawable, gc, TRUE,
	    0, 0, width, height
	);
#if 0
	if(style->bg_pixmap[state] != NULL)
	    gtk_style_apply_default_background(
		style, drawable, FALSE, state,
		NULL,
		0, 0, width, height
	    );
#endif

	/* Get scroll position */
	if(fb->list_hsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_hsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_x = (gint)((adj != NULL) ? adj->value : 0.0f);
	}
	if(fb->list_vsb != NULL)
	{
	    GtkRange *range = GTK_RANGE(fb->list_vsb);
	    GtkAdjustment *adj = range->adjustment;
	    scroll_y = (gint)((adj != NULL) ? adj->value : 0.0f);
	}

	/* Begin drawing objects by list display format */
	switch(fb->list_format)
	{
	    gboolean o_is_selected;
	    GdkTextBounds b;
	    gint i, x, y, icon_width;
	    GdkGC *gc_text;
	    const FileBrowserObject *o;
	    const FileBrowserIcon *icon;

	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	    /* Make sure we have enough columns to draw for this
	     * list display format
	     */
	    if(fb->total_columns >= 4)
	    {
		mode_t m;
		const struct stat *lstat_buf;
		GdkRectangle rect;
		const FileBrowserColumn *column[4];

		/* Get pointer to all columns */
		for(i = 0; i < 4; i++)
		    column[i] = fb->column[i];

		/* Iterate through objects */
		for(i = 0; i < fb->total_objects; i++)
		{
		    o = fb->object[i];
		    if(o == NULL)
			continue;

		    if((o->width <= 0) || (o->height <= 0))
			continue;

		    /* Object off screen? */
		    x = o->x;
		    y = o->y - scroll_y;
		    if(((y + o->height) < 0) || (y >= height))
			continue;

		    /* Get object values */
		    lstat_buf = &o->lstat_buf;
		    m = lstat_buf->st_mode;

		    /* Get rectangle for the limits of the object's
		     * name area
		     */
		    rect.x = x - 0;
		    rect.y = y;
		    rect.width = MIN(o->width, column[0]->position - 0);
		    rect.height = o->height;

		    /* Is object selected? */
		    o_is_selected = OBJISSEL(fb, i);
		    if(o_is_selected)
		    {
			gc = style->fg_gc[GTK_STATE_SELECTED];
			gc_text = style->text_gc[GTK_STATE_SELECTED];
			gdk_draw_rectangle(
			    drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
			    rect.x, rect.y, rect.width, rect.height
			);
		    }
		    else
		    {
			gc = style->fg_gc[state];
			gc_text = style->text_gc[state];
		    }

		    /* Draw Icon */
		    icon = FileBrowserGetIcon(fb, o->icon_num);
		    if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
		    {
			GdkBitmap *mask = icon->mask;
			GdkPixmap *pixmap = icon->pixmap;
			gdk_gc_set_clip_origin(gc, x, y);
			gdk_gc_set_clip_mask(gc, mask);
			gdk_draw_pixmap(
			    drawable, gc, pixmap,
			    0, 0, x, y, icon->width, icon->height
			);
			gdk_gc_set_clip_mask(gc, NULL);
			icon_width = icon->width;
		    }
		    else
		    {
			icon_width = 0;
		    }

		    /* Set up clip rectangle for drawing of the
		     * object's name cell
		     */
		    gdk_gc_set_clip_origin(gc, 0, 0);
		    gdk_gc_set_clip_rectangle(gc, &rect);
		    gdk_gc_set_clip_origin(gc_text, 0, 0);
		    gdk_gc_set_clip_rectangle(gc_text, &rect);

		    /* Draw Name */
		    if(o->displayed_name != NULL)
		    {
			const gchar *s = o->displayed_name;
			gdk_string_bounds(font, s, &b);
			gdk_draw_string(
			    drawable, font, gc_text,
			    x + icon_width + FB_LIST_ICON_BORDER + b.lbearing,
			    y + (o->height / 2) -
				(font_height / 2) + font->ascent,
			    s
			);
		    }
		    else
		    {
			/* No name, but we still need to have font extents
			 * for use with drawing the subsequent cells below
			 */
			gdk_string_bounds(font, "X", &b);
		    }

		    /* If the object was selected it means we drew the
		     * icon and name with the selected gc's. We should
		     * now restore the gc's and use the current state
		     * gc's for drawing subsequent values
		     */
		    if(o_is_selected)
		    {
			gdk_gc_set_clip_rectangle(gc, NULL);
			gdk_gc_set_clip_rectangle(gc_text, NULL);
			gc = style->fg_gc[state];
			gc_text = style->text_gc[state];
		    }

		    /* Draw Size */
		    if(S_ISREG(m))
		    {
			gchar s[80];
			g_snprintf(
			    s, sizeof(s),
			    "%ld",
			    lstat_buf->st_size
			);
			gdk_string_bounds(font, s, &b);
			rect.x = column[0]->position;
			rect.width = column[1]->position - column[0]->position;
			if(rect.width > 0)
			    gdk_gc_set_clip_rectangle(gc_text, &rect);
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + rect.width - b.width - 2 + b.lbearing,
			    y + (o->height / 2) -
				((font->ascent + font->descent) / 2) +
				font->ascent,
			    s
			);
		    }

		    /* Draw Permissions */
#ifdef S_ISLNK
		    if(!S_ISLNK(m))
#else
		    if(TRUE)
#endif
		    {
			gchar s[80];
#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
			g_snprintf(
			    s, sizeof(s),
			    "%c%c%c%c%c%c%c%c%c",
			    (m & S_IRUSR) ? 'r' : '-',
			    (m & S_IWUSR) ? 'w' : '-',
			    (m & S_ISUID) ? 'S' :
				((m & S_IXUSR) ? 'x' : '-'),
			    (m & S_IRGRP) ? 'r' : '-',
			    (m & S_IWGRP) ? 'w' : '-',
			    (m & S_ISGID) ? 'G' :
				((m & S_IXGRP) ? 'x' : '-'),
			    (m & S_IROTH) ? 'r' : '-',
			    (m & S_IWOTH) ? 'w' : '-',
			    (m & S_ISVTX) ? 'T' :
				((m & S_IXOTH) ? 'x' : '-')
			);
#else
			strcpy(s, "rwxrwxrwx");
#endif
			gdk_string_bounds(font, s, &b);
			rect.x = column[1]->position;
			rect.width = column[2]->position - column[1]->position;
			if(rect.width > 0)
			    gdk_gc_set_clip_rectangle(gc_text, &rect);
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + 2 + b.lbearing,
			    y + (o->height / 2) -
				((font->ascent + font->descent) / 2) +
				font->ascent,
			    s
			);
		    }

		    /* Draw Last Modified Date */
		    if(S_ISREG(m))
		    {
			time_t mtime = lstat_buf->st_mtime;
			gchar *s = STRDUP((mtime > 0) ? ctime(&mtime) : "*undefined*");
			gchar *s2 = strchr(s, '\n');
			if(s2 == NULL)
			    s2 = strchr(s, '\r');
			if(s2 != NULL)
			    *s2 = '\0';
			gdk_string_bounds(font, s, &b);
			rect.x = column[2]->position;
			rect.width = column[3]->position - column[2]->position;
			if(rect.width > 0)
			    gdk_gc_set_clip_rectangle(gc_text, &rect);
			gdk_draw_string(
			    drawable, font, gc_text,
			    rect.x + 2 + b.lbearing,
			    y + (o->height / 2) -
				((font->ascent + font->descent) / 2) +
				font->ascent,
			    s
			);
			g_free(s);
		    }

		    /* Restore gc */
		    gdk_gc_set_clip_rectangle(gc_text, NULL);
		    gdk_gc_set_clip_rectangle(gc, NULL);

		    /* Draw focus rectangle around object? */
		    if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
		    {
			GdkGCValues gcv;
			gdk_gc_get_values(gc, &gcv);
			gdk_gc_set_function(gc, GDK_INVERT);
			rect.x = x - 0;
			rect.width = MIN(o->width, column[0]->position - 0);
			gdk_draw_rectangle(
			    drawable, gc, FALSE,
			    rect.x, rect.y,
			    rect.width - 1, rect.height - 1
			);
			gdk_gc_set_function(gc, gcv.function);
		    }
		}
	    }
	    break;

	  case FB_LIST_FORMAT_VERTICAL:
	    for(i = 0; i < fb->total_objects; i++)
	    {
		o = fb->object[i];
		if(o == NULL)
		    continue;

		if((o->width <= 0) || (o->height <= 0))
		    continue;

		/* Object off screen? */
		x = o->x;
		y = o->y - scroll_y;
		if(((y + o->height) < 0) || (y >= height))
		    continue;

		/* Is object selected? */
		o_is_selected = OBJISSEL(fb, i);
		if(o_is_selected)
		{
		    gc = style->fg_gc[GTK_STATE_SELECTED];
		    gc_text = style->text_gc[GTK_STATE_SELECTED];
		    gdk_draw_rectangle(
			drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
			x, y, o->width, o->height
		    );
		}
		else
		{
		    gc = style->fg_gc[state];
		    gc_text = style->text_gc[state];
		}

		/* Draw icon */
		icon = FileBrowserGetIcon(fb, o->icon_num);
		if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
		{
		    GdkBitmap *mask = icon->mask;
		    GdkPixmap *pixmap = icon->pixmap;
		    gdk_gc_set_clip_origin(gc, x, y);
		    gdk_gc_set_clip_mask(gc, mask);
		    gdk_draw_pixmap(
			drawable, gc, pixmap,
			0, 0, x, y, icon->width, icon->height
		    );
		    gdk_gc_set_clip_mask(gc, NULL);
		    icon_width = icon->width;
		}
		else
		{
		    icon_width = 0;
		}

		/* Draw name */
		if(o->displayed_name != NULL)
		{
		    const gchar *s = o->displayed_name;
		    gdk_string_bounds(font, s, &b);
		    gdk_draw_string(
			drawable, font, gc_text,
			x + icon_width + FB_LIST_ICON_BORDER + b.lbearing,
			y + (o->height / 2) -
			    (font_height / 2) + font->ascent,
			s
		    );
		}

		/* Draw focus rectangle around object? */
		if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
		{
		    GdkGCValues gcv;
		    gdk_gc_get_values(gc, &gcv);
		    gdk_gc_set_function(gc, GDK_INVERT);
		    gdk_draw_rectangle(
			drawable, gc, FALSE,
			x, y, o->width - 1, o->height - 1
		    );
		    gdk_gc_set_function(gc, gcv.function);
		}
	    }
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    for(i = 0; i < fb->total_objects; i++)
	    {
		o = fb->object[i];
		if(o == NULL)
		    continue;

		if((o->width <= 0) || (o->height <= 0))
		    continue;

		/* Object off screen? */
		x = o->x - scroll_x;
		y = o->y;
		if(((x + o->width) < 0) || (x >= width))
		    continue;

		/* Is object selected? */
		o_is_selected = OBJISSEL(fb, i);
		if(o_is_selected)
		{
		    gc = style->fg_gc[GTK_STATE_SELECTED];
		    gc_text = style->text_gc[GTK_STATE_SELECTED];
		    gdk_draw_rectangle(
			drawable, style->bg_gc[GTK_STATE_SELECTED], TRUE,
			x, y, o->width, o->height
		    );
		}
		else
		{
		    gc = style->fg_gc[state];
		    gc_text = style->text_gc[state];
		}

		/* Draw icon */
		icon = FileBrowserGetIcon(fb, o->icon_num);
		if((icon != NULL) ? (icon->pixmap != NULL) : FALSE)
		{
		    GdkBitmap *mask = icon->mask;
		    GdkPixmap *pixmap = icon->pixmap;
		    gdk_gc_set_clip_origin(gc, x, y);
		    gdk_gc_set_clip_mask(gc, mask);
		    gdk_draw_pixmap(
			drawable, gc, pixmap,
			0, 0, x, y, icon->width, icon->height
		    );
		    gdk_gc_set_clip_mask(gc, NULL);
		    icon_width = icon->width;
		}
		else
		{
		    icon_width = 0;
		}

		/* Draw name */
		if(o->displayed_name != NULL)
		{
		    const gchar *s = o->displayed_name;
		    gdk_string_bounds(font, s, &b);
		    gdk_draw_string(
			drawable, font, gc_text,
			x + icon_width + FB_LIST_ICON_BORDER + b.lbearing,
			y + (o->height / 2) -
			    (font_height / 2) + font->ascent,
			s
		    );
		}

		/* Draw focus rectangle around object? */
		if((i == fb->focus_object) && GTK_WIDGET_HAS_FOCUS(w))
		{
		    GdkGCValues gcv;
		    gdk_gc_get_values(gc, &gcv);
		    gdk_gc_set_function(gc, GDK_INVERT);
		    gdk_draw_rectangle(
			drawable, gc, FALSE,
			x, y, o->width - 1, o->height - 1
		    );
		    gdk_gc_set_function(gc, gcv.function);
		}
	    }
	    break;
	}

#if 0
	/* Draw focus rectangle if widget is in focus */
	if(GTK_WIDGET_HAS_FOCUS(w) && (state != GTK_STATE_INSENSITIVE))
	    gtk_draw_focus(
		style, drawable,
		0, 0, width - 1, height - 1
	    );
#endif

	/* Send drawable to window if drawable is not the window */
	if(drawable != (GdkDrawable *)window)
	    gdk_draw_pixmap(
		window, style->fg_gc[state], drawable,
		0, 0, 0, 0, width, height
	    );
}

/*
 *	Queues a redraw of the File Browser's list.
 */
static void FileBrowserListQueueDraw(FileBrowser *fb)
{
	GtkWidget *w = (fb != NULL) ? fb->list_da : NULL;
	if(w != NULL)
	    gtk_widget_queue_draw(w);
}


/*
 *	Returns the list column at index i.
 */
static FileBrowserColumn *FileBrowserListGetColumn(FileBrowser *fb, gint i)
{
	if(fb == NULL)
	    return(NULL);
	if((i < 0) || (i >= fb->total_columns))
	    return(NULL);
	else
	    return(fb->column[i]);
}

/*
 *	Appends a new list column.
 */
static FileBrowserColumn *FileBrowserListColumnAppend(
	FileBrowser *fb, const gchar *label, gint width
)
{
	gint i;
	FileBrowserColumn *column, *column_prev;

	if(fb == NULL)
	    return(NULL);

	i = fb->total_columns;
	fb->total_columns = i + 1;
	fb->column = (FileBrowserColumn **)g_realloc(
	    fb->column, fb->total_columns * sizeof(FileBrowserColumn *)
	);
	if(fb->column == NULL)
	{
	    fb->total_columns = 0;
	    return(NULL);
	}

	fb->column[i] = column = FILE_BROWSER_COLUMN(g_malloc0(
	    sizeof(FileBrowserColumn)
	));
	if(column == NULL)
	    return(NULL);

	column->label = STRDUP(label);
	column_prev = FileBrowserListGetColumn(fb, i - 1);
	column->position = ((column_prev != NULL) ?
	    column_prev->position : 0) + width;
	column->label_justify = GTK_JUSTIFY_LEFT;
	column->flags = GTK_SENSITIVE | GTK_CAN_FOCUS |
			GTK_CAN_DEFAULT;

	column->drag = FALSE;
	column->drag_position = column->position;
	column->drag_last_drawn_position = column->drag_position;

	return(column);
}

/*
 *	Deletes all list columns.
 */
static void FileBrowserListColumnsClear(FileBrowser *fb)
{
	gint i;

	if(fb == NULL)
	    return;

	for(i = 0; i < fb->total_columns; i++)
	    FileBrowserColumnDestroyCB(fb->column[i]);
	g_free(fb->column);
	fb->column = NULL;
	fb->total_columns = 0;
}


/*
 *	Updates the entry with the selected objects, if there are no
 *	selected objects then the entry is blanked.
 */
static void FileBrowserEntrySetSelectedObjects(FileBrowser *fb)
{
	gchar *s;
	GList *glist;
	const FileBrowserObject *o;
	GtkEntry *entry = (fb != NULL) ? (GtkEntry *)fb->entry : NULL;
	if(entry == NULL)
	    return;

	/* Iterate through selected objects and generate a string s to
	 * be set as the new entry value
	 */
	s = STRDUP("");
	for(glist = fb->selection;
	    glist != NULL;
	    glist = g_list_next(glist)
	)
	{
	    o = FileBrowserGetObject(fb, (gint)glist->data);
	    if((o != NULL) ? (o->name != NULL) : FALSE)
	    {
		s = strinsstr(s, -1, o->name);

		/* Add deliminator to string if there is another
		 * object after this one
		 */
		if(g_list_next(glist) != NULL)
		    s = strinschr(s, -1, ',');
	    }
	}

	/* Update entry value if there were objects or else clear it */
	gtk_entry_set_text(entry, (s != NULL) ? s : "");
	gtk_entry_set_position(entry, -1);

	g_free(s);
}


/*
 *	Directory popup list item destroy callback.
 */
static void FileBrowserDirPUListItemDestroyCB(gpointer data)
{
	gchar *full_path = (gchar *)data;
	if(full_path == NULL)
	    return;

	g_free(full_path);
}

/*
 *	File browser icon destroy callback.
 */
static void FileBrowserIconDestroyCB(gpointer data)
{
	FileBrowserIcon *icon = FILE_BROWSER_ICON(data);
	if(icon == NULL)
	    return;

	if(icon->pixmap != NULL)
	    gdk_pixmap_unref(icon->pixmap);
	if(icon->mask != NULL)
	    gdk_bitmap_unref(icon->mask);
	g_free(icon->desc);
	g_free(icon);
}

/*
 *	File browser object destroy callback.
 */
static void FileBrowserObjectDestroyCB(gpointer data)
{
	FileBrowserObject *o = FILE_BROWSER_OBJECT(data);
	if(o == NULL)
	    return;

	g_free(o->displayed_name);
	g_free(o->full_path);
	g_free(o->name);
	g_free(o);
}

/*
 *      File browser list column destroy callback.
 */
static void FileBrowserColumnDestroyCB(gpointer data)
{
	FileBrowserColumn *column = FILE_BROWSER_COLUMN(data);
	if(column == NULL)
	    return;

	g_free(column->label);
	g_free(column);
}

/*
 *	File browser ok button signal callback.
 */
static void FileBrowserOKCB(GtkWidget *widget, gpointer data)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	if(FPromptIsQuery())
	{
	    FPromptBreakQuery();
	}
	else if(PUListIsQuery(fb->dir_pulist))
	{
	    PUListBreakQuery(fb->dir_pulist);
	}
	else if(CDialogIsQuery())
	{

	}
	else
	{
	    gint i;
	    GtkWidget *w;

	    /* Mark that user response was OK */
	    fb->user_response = TRUE;

	    /* Delete previous list of selected paths */
	    for(i = 0; i < fb->total_selected_paths; i++)
		g_free(fb->selected_path[i]);
	    g_free(fb->selected_path);
	    fb->selected_path = NULL;
	    fb->total_selected_paths = 0;

	    /* Get value in entry and explode it to the list of
	     * return paths
	     */
	    w = fb->entry;
	    if(w != NULL)
	    {
		const gchar *s = gtk_entry_get_text(GTK_ENTRY(w));
		if((s != NULL) ? (*s != '\0') : FALSE)
		{
		    /* Explode the entry's string value at all ','
		     * characters and then prefix the current location
		     * to each exploded string
		     */
		    const gchar *name;
		    gint n, strc;
		    gchar **strv = (gchar **)strchrexp(
			(const char *)s, ',', (int *)&strc
		    );
		    const gchar *cur_location = FileBrowserGetLocation(fb);
		    for(i = 0; i < strc; i++)
		    {
			name = strv[i];
			if(name == NULL)
			    continue;

			/* Append a new path to the selected_path
			 * array, the new path will be either name
			 * (if name is an absolute path) or the
			 * cur_location prefixed to name (if name is
			 * not an absolute path).
			 */
			n = MAX(fb->total_selected_paths, 0);
			fb->total_selected_paths = n + 1;
			fb->selected_path = (gchar **)g_realloc(
			    fb->selected_path,
			    fb->total_selected_paths * sizeof(gchar *)
			);
			if(fb->selected_path == NULL)
			{
			    fb->total_selected_paths = 0;
			}
			else
			{
			    /* Check if name is an absolute path, if it
			     * is then copy it to the selected_path.
			     * Otherwise copy the cur_location prefixed
			     * to name.
			     */
			    gchar *full_path;
			    if(g_path_is_absolute(name))
				full_path = STRDUP(name);
			    else
				full_path = STRDUP(
				    PrefixPaths(cur_location, name)
				);
			    fb->selected_path[n] = full_path;
			}

			g_free(strv[i]);
		    }
		    g_free(strv);
	        }
	        else
	        {
		    /* File name entry is empty, so set the current
		     * location as the return path.
		     */
		    const gchar *cur_location = FileBrowserGetLocation(fb);
		    gint n = MAX(fb->total_selected_paths, 0);
		    fb->total_selected_paths = n + 1;
		    fb->selected_path = (gchar **)g_realloc(
		        fb->selected_path,
		        fb->total_selected_paths * sizeof(gchar *)
		    );
		    if(fb->selected_path == NULL)
		    {
			fb->total_selected_paths = 0;
		    }
		    else
		    {
		        fb->selected_path[n] = STRDUP(cur_location);
		    }
	        }
	    }

	    /* Unmap */
	    FileBrowserUnmap();

	    /* Break out of blocking loop */
	    if(fb->block_loop_level > 0)
	    {
		gtk_main_quit();
		fb->block_loop_level--;
	    }
	}
}

/*
 *	File browser cancel button signal callback.
 */
static void FileBrowserCancelCB(GtkWidget *widget, gpointer data)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	if(FPromptIsQuery())
	{
	    FPromptBreakQuery();
	}
	else if(PUListIsQuery(fb->dir_pulist))
	{
	    PUListBreakQuery(fb->dir_pulist);
	}
	else if(CDialogIsQuery())
	{

	}
	else
	{
	    /* Unmap */
	    FileBrowserUnmap();

	    /* Break out of blocking loop */
	    if(fb->block_loop_level > 0)
	    {
		gtk_main_quit();
		fb->block_loop_level--;
	    }
	}
}           

/*
 *      File browser toplevel "delete_event" signal callback.
 */
static gint FileBrowserDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	FileBrowserCancelCB(widget, data);
	return(TRUE);
}


/*
 *	Directory popup list drawing area event callback.
 */
static gint FileBrowserDirPUListDAEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	gboolean key_press;
	GdkEventConfigure *configure;
	GdkEventExpose *expose;
	GdkEventFocus *focus;
	GdkEventKey *key;
	GdkEventButton *button;
	GtkWidget *w;
	FileBrowser *fb = FILE_BROWSER(data);
	if((event == NULL) || (fb == NULL))
	    return(status);

	w = fb->dir_pulist_da;
	if(w == NULL)
	    return(status);

	switch((gint)event->type)
	{
	  case GDK_CONFIGURE:
	    configure = (GdkEventConfigure *)event;
	    status = TRUE;
	    break;

	  case GDK_EXPOSE:
	    expose = (GdkEventExpose *)event;
	    FileBrowserDirPUListDraw(fb);
	    status = TRUE;
	    break;

	  case GDK_FOCUS_CHANGE:
	    focus = (GdkEventFocus *)event;
	    if(focus->in)
		GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
	    else
		GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
	    FileBrowserDirPUListDraw(fb);
	    status = TRUE;
	    break;

	  case GDK_KEY_PRESS:
	  case GDK_KEY_RELEASE:
	    key = (GdkEventKey *)event;
	    key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define STOP_KEY_SIGNAL_EMIT	{		\
if(widget != NULL)				\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  key_press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}
	    switch(key->keyval)
	    {
	      case GDK_Return:
	      case GDK_KP_Enter:
	      case GDK_space:
	      case GDK_Up:
	      case GDK_KP_Up:
	      case GDK_Down:
	      case GDK_KP_Down:
		if(key_press)
		    FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb);
		STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;
	    }
#undef STOP_KEY_SIGNAL_EMIT
	    break;

	  case GDK_BUTTON_PRESS:
	    button = (GdkEventButton *)event;
	    if(!GTK_WIDGET_HAS_FOCUS(w))
		gtk_widget_grab_focus(w);

	    switch(button->button)
	    {
	      case 1:
		FileBrowserDirPUListMapCB(fb->dir_pulist_btn, fb);
		break;
	    }
	    status = TRUE;
	    break;

	  case GDK_BUTTON_RELEASE:
	    button = (GdkEventButton *)event;

	    status = TRUE;
	    break;

	}

	return(status);
}

/*
 *	Directory popup list button callback.
 */
static void FileBrowserDirPUListMapCB(GtkWidget *widget, gpointer data)
{
	const gchar *v;
	pulist_struct *pulist;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	pulist = fb->dir_pulist;
	if(pulist == NULL)
	    return;

	/* Map popup list to query for new location */
	v = PUListMapQuery(
	    pulist,
	    NULL,		/* Initial value */
	    -1,			/* Lines visible */
	    PULIST_RELATIVE_BELOW,
	    fb->dir_pulist_da,
	    widget
	);

	/* Got new location? */
	if(v != NULL)
	{
	    const gchar *full_path = (const gchar *)PUListGetDataFromValue(
		pulist, v
	    );

	    /* Go to new location */
	    if(full_path != NULL)
		FileBrowserSetLocation(fb, full_path);
	}
}


/*
 *	List header event callback.
 */
static gint FileBrowserListHeaderEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	GdkEventConfigure *configure;
	GdkEventExpose *expose;
	GdkEventFocus *focus;
	GdkEventButton *button;
	GdkEventMotion *motion;
	GtkWidget *w;
	FileBrowser *fb = FILE_BROWSER(data);
	if((event == NULL) || (fb == NULL))
	    return(status);

	w = fb->list_header_da;
	if(w == NULL)
	    return(status);

	/* List widget must also be valid */
	if(fb->list_da == NULL)
	    return(status);

#define DRAW_DRAG_LINE(x)					\
{ if(column != NULL) {						\
 gint line_p = (gint)(x);					\
 GdkWindow *window1, *window2;					\
 GdkGC *gc;							\
 GtkStyle *style;						\
								\
 window1 = w->window;						\
 window2 = fb->list_da->window;					\
 style = gtk_widget_get_style(w);				\
 gc = (style != NULL) ?						\
  style->fg_gc[GTK_WIDGET_STATE(w)] : NULL;			\
								\
 if((window1 != NULL) && (window2 != NULL) && (gc != NULL)) {	\
  GdkGCValues gcv;						\
  gint width, height;						\
								\
  gdk_gc_get_values(gc, &gcv);					\
  gdk_gc_set_function(gc, GDK_INVERT);				\
								\
  /* Draw drag line on window1 */				\
  gdk_window_get_size(window1, &width, &height);		\
  gdk_draw_line(window1, gc, line_p, 0, line_p, height);	\
								\
  /* Draw drag line on window2 */                               \
  gdk_window_get_size(window2, &width, &height);                \
  gdk_draw_line(window2, gc, line_p, 0, line_p, height);        \
								\
  gdk_gc_set_function(gc, gcv.function);			\
 }								\
} }


	switch((gint)event->type)
	{
	  case GDK_CONFIGURE:
	    configure = (GdkEventConfigure *)event;
	    status = TRUE;
	    break;

	  case GDK_EXPOSE:
	    expose = (GdkEventExpose *)event;
	    FileBrowserListHeaderDraw(fb);
	    status = TRUE;
	    break;

	  case GDK_FOCUS_CHANGE:
	    focus = (GdkEventFocus *)event;
	    if(focus->in && !GTK_WIDGET_HAS_FOCUS(w))
	    {
		GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
		FileBrowserListHeaderQueueDraw(fb);
	    }
	    else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w))
	    {
		GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
		FileBrowserListHeaderQueueDraw(fb);
	    }
	    status = TRUE;
	    break;

	  case GDK_BUTTON_PRESS:
	    button = (GdkEventButton *)event;
	    if(!GTK_WIDGET_HAS_FOCUS(w))
		gtk_widget_grab_focus(w);
	    if(fb->total_columns > 0)
	    {
		gint i, cp, tolor = 3;
		gint p = (gint)button->x;
		FileBrowserColumn *column;

#define COLUMN_POSITION(p)	(((p) != NULL) ? (p)->position : 0)

		/* Iterate through all columns to update focus and
		 * reset drag state.
		 */
		for(i = 0; i < fb->total_columns; i++)
		{
		    column = fb->column[i];
		    if(column == NULL)
			continue;

		    /* Get left edge column position */
		    cp = ((i - 1) >= 0) ?
			COLUMN_POSITION(fb->column[i - 1]) : 0;

		    if((p >= (cp + tolor)) && (p < (column->position - tolor)))
			column->flags |= GTK_HAS_FOCUS;
		    else
			column->flags &= ~GTK_HAS_FOCUS;

		    column->drag = FALSE;
		}
		FileBrowserListHeaderQueueDraw(fb);

		/* Iterate through all columns, checking for one
		 * where the pointer is over its resizing area.
		 */
		for(i = fb->total_columns - 1; i >= 0; i--)
		{
		    column = fb->column[i];
		    if(column == NULL)
			continue;

		    cp = column->position;
		    if((p >= (cp - tolor)) && (p < (cp + (2 * tolor))))
		    {
			/* Update column drag values */
			column->drag = TRUE;
			column->drag_position = cp;

			/* Draw drag line on list header and list */
			DRAW_DRAG_LINE(cp);
			column->drag_last_drawn_position =
			    column->drag_position;

			break;
		    }
		}
#undef COLUMN_POSITION
	    }
	    status = TRUE;
	    break;

	  case GDK_BUTTON_RELEASE:
	    button = (GdkEventButton *)event;
	    if(fb->total_columns > 0)
	    {
		gint i, pos_shift_delta = 0;
		FileBrowserColumn *column;

		/* Iterate through all columns, checking for one that
		 * is being dragged and set that new column's
		 * position
		 */
		for(i = 0; i < fb->total_columns; i++)
		{
		    column = fb->column[i];
		    if(column == NULL)
			continue;

		    /* This column being dragged? */
		    if(column->drag)
		    {
			column->drag = FALSE;
			pos_shift_delta = column->drag_position -
			    column->position;
			column->position = column->drag_position;
		    }
		    else
		    {
			column->position += pos_shift_delta;
		    }
		}

		/* Redraw the list header and the list */
		FileBrowserListHeaderQueueDraw(fb);
		FileBrowserListQueueDraw(fb);
	    }
	    status = TRUE;
	    break;

	  case GDK_MOTION_NOTIFY:
	    motion = (GdkEventMotion *)event;
	    if(fb->total_columns > 0)
	    {
		gint i, tolor = 3, left_column_pos = 0;
		GdkCursor *cursor = NULL;
		gint p = (gint)motion->x;
		FileBrowserColumn *column;


		/* Iterate through all columns and check if one is
		 * being dragged (resized) in which case it will be
		 * handled accordingly.  Also if a pointer has
		 * moved into the dragging area of a column then
		 * the new cursor will be specified.
		 */
		for(i = 0; i < fb->total_columns; i++)
		{
		    column = fb->column[i];
		    if(column == NULL)
			continue;

		    /* This column being dragged? */
		    if(column->drag)
		    {
			column->drag_position = CLIP(
			    p, left_column_pos, w->allocation.width
			);

			/* Draw drag line on list header and list */
			DRAW_DRAG_LINE(column->drag_last_drawn_position);
			DRAW_DRAG_LINE(column->drag_position);
			column->drag_last_drawn_position =
			    column->drag_position;

			/* Update cursor just in case */
			cursor = fb->cur_column_hresize;

			/* No need to handle other columns after
			 * this one since it should be the only
			 * being dragged.
			 */
			break;
		    }
		    else
		    {
			/* Column not being dragged, check if the
			 * pointer has moved into the dragging
			 * area of this column.
			 */
			gint cp = column->position;
			if((p >= (cp - tolor)) && (p < (cp + (2 * tolor))))
			{
			    cursor = fb->cur_column_hresize;
			}

			left_column_pos = column->position;
		    }
		}
		gdk_window_set_cursor(w->window, cursor);
		gdk_flush();
	    }
	    status = TRUE;
	    break;

	  case GDK_LEAVE_NOTIFY:
	    gdk_window_set_cursor(w->window, NULL);
	    gdk_flush();
	    status = TRUE;
	    break;
	}

#undef DRAW_DRAG_LINE

	return(status);
}

/*
 *	List event callback.
 */
static gint FileBrowserListEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	gint status = FALSE;
	gboolean key_press;
	GdkEventConfigure *configure;
	GdkEventExpose *expose;
	GdkEventFocus *focus;
	GdkEventKey *key;
	GdkEventButton *button;
	GdkEventMotion *motion;
	GtkWidget *w;
	FileBrowser *fb = FILE_BROWSER(data);
	if((event == NULL) || (fb == NULL))
	    return(status);

	w = fb->list_da;
	if(w == NULL)
	    return(status);

	switch((gint)event->type)
	{
	  case GDK_CONFIGURE:
	    configure = (GdkEventConfigure *)event;
	    GDK_PIXMAP_UNREF(fb->list_pm)
	    if((configure->width > 0) && (configure->height > 0))
		fb->list_pm = GDK_PIXMAP_NEW(
		    configure->width, configure->height
		);
	    else
		fb->list_pm = NULL;
	    status = TRUE;
	    break;

	  case GDK_EXPOSE:
	    expose = (GdkEventExpose *)event;
	    FileBrowserListDraw(fb);
	    status = TRUE;
	    break;

	  case GDK_FOCUS_CHANGE:
	    focus = (GdkEventFocus *)event;
	    if(focus->in && !GTK_WIDGET_HAS_FOCUS(w))
	    {
		GTK_WIDGET_SET_FLAGS(w, GTK_HAS_FOCUS);
		FileBrowserListQueueDraw(fb);
	    }
	    else if(!focus->in && GTK_WIDGET_HAS_FOCUS(w))
	    {
		GTK_WIDGET_UNSET_FLAGS(w, GTK_HAS_FOCUS);
		FileBrowserListQueueDraw(fb);
	    }
	    status = TRUE;
	    break;

	  case GDK_KEY_PRESS:
	  case GDK_KEY_RELEASE:
	    key = (GdkEventKey *)event;
	    key_press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;
#define STOP_KEY_SIGNAL_EMIT	{		\
 if(widget != NULL)				\
  gtk_signal_emit_stop_by_name(			\
   GTK_OBJECT(widget),				\
   key_press ?					\
    "key_press_event" : "key_release_event"	\
  );						\
}
	    /* First handle key event by list format */
	    switch(fb->list_format)
	    {
	      /* Vertical List Format */
	      case FB_LIST_FORMAT_VERTICAL_DETAILS:
	      case FB_LIST_FORMAT_VERTICAL:
		switch(key->keyval)
		{
		  case GDK_Up:		/* Change Focus Up */
		  case GDK_KP_Up:
		  case GDK_Left:
		  case GDK_KP_Left:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->value - adj->step_increment,
					adj->lower
				    )
				);
			}
			else if(fb->focus_object > 0)
			{
			    gint	n = fb->focus_object,
					i = n - 1;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				if(!OBJISSEL(fb, n))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)n
				    );
				if(!OBJISSEL(fb, i))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)i
				    );
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT		    
		    status = TRUE;
		    break;

		  case GDK_Down:	/* Change Focus Down */
		  case GDK_KP_Down:
		  case GDK_Right:
		  case GDK_KP_Right:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MIN(
					adj->value + adj->step_increment,
					MAX(adj->upper - adj->page_size, adj->lower)
				    )
				);
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    gint	n = fb->focus_object,
					i = n + 1;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				if(!OBJISSEL(fb, n))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)n
				    );
				if(!OBJISSEL(fb, i))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)i
				    );
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		    break;

		  case GDK_Page_Up:	/* Page Focus Up */
		  case GDK_KP_Page_Up:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->value - adj->page_increment,
					adj->lower
				    )
			        );
			}
			else
			{
			    const FileBrowserObject *o = (fb->total_objects > 0) ?
				fb->object[0] : NULL;
			    gint i, n = fb->focus_object,
				row_height = (o != NULL) ?
				    (o->height + 1) : 0;
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;

			    if((row_height > 0) && (adj != NULL))
			    {
				fb->focus_object = i = MAX(
				    fb->focus_object -
				    (adj->page_increment / row_height),
				    0
				);

				/* Select? */
				if((key->state & GDK_SHIFT_MASK) &&
				   (fb->total_objects > 0)
				)
				{
				    gint j;
				    for(j = MIN(n, fb->total_objects - 1); j >= i; j--)
				    {
					if(!OBJISSEL(fb, j))
					    fb->selection = g_list_append(
						fb->selection, (gpointer)j
					    );
				    }
				    fb->selection_end = g_list_last(fb->selection);
				    FileBrowserEntrySetSelectedObjects(fb);
				}

				/* Scroll if focus object is not fully visible */
				if(FileBrowserListObjectVisibility(fb, i) !=
				    GTK_VISIBILITY_FULL
				)
				    FileBrowserListMoveToObject(fb, i, 0.0f);
				else
				    FileBrowserListQueueDraw(fb);
			    }
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_Page_Down:	/* Page Focus Down */
		  case GDK_KP_Page_Down:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MIN(
					adj->value + adj->page_increment,
					MAX(adj->upper - adj->page_size, adj->lower)
				    )
			        );
			}
			else
			{
			    const FileBrowserObject *o = (fb->total_objects > 0) ?
				fb->object[0] : NULL;
			    gint i, n = fb->focus_object,
				row_height = (o != NULL) ?
				    (o->height + 1) : 0;
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;

			    if((row_height > 0) && (adj != NULL))
			    {
				fb->focus_object = i = MIN(
				    fb->focus_object +
				    (adj->page_increment / row_height),
				    fb->total_objects - 1
				);

				/* Select? */
				if((key->state & GDK_SHIFT_MASK) &&
				   (fb->total_objects > 0)
				)
				{
				    gint j;
				    for(j = MAX(n, 0); j <= i; j++)
				    {
					if(!OBJISSEL(fb, j))
					    fb->selection = g_list_append(
						fb->selection, (gpointer)j
					    );
				    }
				    fb->selection_end = g_list_last(fb->selection);
				    FileBrowserEntrySetSelectedObjects(fb);
				}

				/* Scroll if focus object is not fully visible */
				if(FileBrowserListObjectVisibility(fb, i) !=
				    GTK_VISIBILITY_FULL
				)
				    FileBrowserListMoveToObject(fb, i, 1.0f);
				else
				    FileBrowserListQueueDraw(fb);
			    }
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_Home:	/* Focus Beginning */
		  case GDK_KP_Home:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    adj->lower
				);
			}
			else if(fb->focus_object > 0)
			{
			    gint	i = 0,
					n = fb->focus_object;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MIN(n, fb->total_objects - 1); j >= i; j--)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_End:		/* Focus End */
		  case GDK_KP_End:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_vsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->upper - adj->page_size,
					adj->lower
				    )
			        );
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    gint	i = MAX(fb->total_objects - 1, 0),
					n = fb->focus_object;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MAX(n, 0); j <= i; j++)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 1.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;
		}
		break;

	      /* Horizontal List Format */
	      case FB_LIST_FORMAT_STANDARD:
		switch(key->keyval)
		{
		  case GDK_Up:		/* Focus Up */
		  case GDK_KP_Up:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    /* No vertical scrolling */
			}
			else if(fb->focus_object > 0)
			{
			    const gint	n = fb->focus_object,
					i = n - 1;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				if(!OBJISSEL(fb, n))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)n
				    );
				if(!OBJISSEL(fb, i))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)i
				    );
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		    break;

		  case GDK_Down:	/* Focus Down */
		  case GDK_KP_Down:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    /* No vertical scrolling */
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    const gint	n = MAX(fb->focus_object, 0),
					i = n + 1;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				if(!OBJISSEL(fb, n))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)n
				    );
				if(!OBJISSEL(fb, i))
				    fb->selection = g_list_append(
					fb->selection, (gpointer)i
				    );
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		    break;

		  case GDK_Left:	/* Focus Left */
		  case GDK_KP_Left:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->value - adj->step_increment,
					adj->lower
				    )
				);
			}
			else if(fb->focus_object > 0)
			{
			    const gint	n = fb->focus_object,
					i = MAX(
			fb->focus_object - fb->objects_per_row, 0
					);

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MIN(n, fb->total_objects - 1); j >= i; j--)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
			        GTK_VISIBILITY_FULL
			    )
			        FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
			        FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		    break;

		  case GDK_Right:	/* Focus Right */
		  case GDK_KP_Right:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
				range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MIN(
					adj->value + adj->step_increment,
					MAX(adj->upper - adj->page_size, adj->lower)
				    )
				);
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    const gint	n = MAX(fb->focus_object, 0),
					i = MIN(
					    n + fb->objects_per_row,
					    fb->total_objects - 1
					);

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = n; j <= i; j++)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
			        GTK_VISIBILITY_FULL
			    )
			        FileBrowserListMoveToObject(fb, i, 0.5f);
			    else
			        FileBrowserListQueueDraw(fb);
			}
		    }
		    STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		    break;

		  case GDK_Page_Up:	/* Page Focus Left */
		  case GDK_KP_Page_Up:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->value - adj->page_increment,
					adj->lower
				    )
			        );
			}
			else if(fb->focus_object > 0)
			{
			    gint        n = fb->focus_object,
					i = MAX(
			fb->focus_object - fb->objects_per_row, 0
					);

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MIN(n, fb->total_objects - 1); j >= i; j--)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_Page_Down:	/* Page Focus Right */
		  case GDK_KP_Page_Down:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MIN(
					adj->value + adj->page_increment,
					MAX(adj->upper - adj->page_size, adj->lower)
				    )
			        );
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    gint        n = fb->focus_object,
					i = MIN(
			    fb->focus_object + fb->objects_per_row,
			    fb->total_objects - 1
					);

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MAX(n, 0); j <= i; j++)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 1.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_Home:	/* Focus Beginning */
		  case GDK_KP_Home:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    adj->lower
				);
			}
			else if(fb->focus_object > 0)
			{
			    gint        i = 0,
					n = fb->focus_object;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MIN(n, fb->total_objects - 1); j >= i; j--)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 0.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;

		  case GDK_End:		/* Focus End */
		  case GDK_KP_End:
		    if(key_press)
		    {
			/* Scroll only? */
			if(key->state & GDK_CONTROL_MASK)
			{
			    GtkRange *range = (GtkRange *)fb->list_hsb;
			    GtkAdjustment *adj = (range != NULL) ?
			        range->adjustment : NULL;
			    if(adj != NULL)
				gtk_adjustment_set_value(
				    adj,
				    MAX(
					adj->upper - adj->page_size,
					adj->lower
				    )
			        );
			}
			else if(fb->focus_object < (fb->total_objects - 1))
			{
			    gint        i = MAX(fb->total_objects - 1, 0),
					n = fb->focus_object;

			    fb->focus_object = i;

			    /* Select? */
			    if((key->state & GDK_SHIFT_MASK) &&
			       (fb->total_objects > 0)
			    )
			    {
				gint j;
				for(j = MAX(n, 0); j <= i; j++)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				}
				fb->selection_end = g_list_last(fb->selection);
				FileBrowserEntrySetSelectedObjects(fb);
			    }

			    /* Scroll if focus object is not fully visible */
			    if(FileBrowserListObjectVisibility(fb, i) !=
				GTK_VISIBILITY_FULL
			    )
				FileBrowserListMoveToObject(fb, i, 1.0f);
			    else
				FileBrowserListQueueDraw(fb);
			}
		    }
		    status = TRUE;
		    break;
		}
		break;
	    }
	    /* If the key event was not handled above then we
	     * handle it generally independent of the list
	     * format here
	     */
	    if(!status)
	    {
		switch(key->keyval)
		{
		  case GDK_Return:	/* Enter */
		  case GDK_KP_Enter:
		  case GDK_3270_Enter:
		  case GDK_ISO_Enter:
		    if(key_press)
			FileBrowserEntryEnterCB(fb->entry, fb);
		    status = TRUE;
		    break;

		  case GDK_space:	/* Toggle Select */
		    if(key_press)
		    {
			gint i = fb->focus_object;
			if(OBJISSEL(fb, i))
			    fb->selection = g_list_remove(
				fb->selection, (gpointer)i
			    );
			else
			    fb->selection = g_list_append(
				fb->selection, (gpointer)i
			    );
			fb->selection_end = g_list_last(fb->selection);
			FileBrowserListQueueDraw(fb);
			FileBrowserEntrySetSelectedObjects(fb);
		    }
		    status = TRUE;
		    break;

		  case GDK_BackSpace:
		    if(key_press)
			FileBrowserGoToParentCB(NULL, fb);
		    status = TRUE;
		    break;

		  case GDK_F5:	/* Refresh */
		    if(key_press)
			FileBrowserRefreshCB(NULL, fb);
		    status = TRUE;
		    break;

		  case GDK_Insert:	/* New Directory */
#if 0
/* Some other accelerator seems to catch this even with this widget in
 * focus so we don't need to respond to it.
 */
		    if(key_press)
			FileBrowserNewDirectoryCB(NULL, fb);
#endif
		    status = TRUE;
		    break;

		  case GDK_F2:		/* Rename */
		    if(key_press)
			FileBrowserRenameCB(NULL, fb);
		    status = TRUE;
		    break;

		  case GDK_Delete:	/* Delete */
		    if(key_press)
			FileBrowserDeleteCB(NULL, fb);
		    status = TRUE;
		    break;
#if 0
/* Some other accelerator seems to catch this even with this widget in
 * focus so we don't need to respond to it.
 */
		  case 'a':		/* Select All */
		    if(key_press && (key->state & GDK_CONTROL_MASK))
			FileBrowserSelectAllCB(NULL, fb);
		    status = TRUE;
		    break;

		  case 'u':		/* Unelect All */
		    if(key_press && (key->state & GDK_CONTROL_MASK))
			FileBrowserUnselectAllCB(NULL, fb);
		    status = TRUE;
		    break;

		  case 'i':		/* Invert Selection */
		    if(key_press && (key->state & GDK_CONTROL_MASK))
			FileBrowserInvertSelectionCB(NULL, fb);
		    status = TRUE;
		    break;
#endif
		}
	    }
#undef STOP_KEY_SIGNAL_EMIT
	    break;

	  case GDK_BUTTON_PRESS:
	    button = (GdkEventButton *)event;
	    /* Unmap floating prompt as needed */
	    if(FPromptIsQuery())
		FPromptBreakQuery();
	    /* Grab focus as needed */
	    if(!GTK_WIDGET_HAS_FOCUS(w))
		gtk_widget_grab_focus(w);
	    /* Handle by button number */
	    switch(button->button)
	    {
	      case GDK_BUTTON1:		/* Select */
		/* Select one item at a time? */
		if(button->state & GDK_CONTROL_MASK)
		{
		    gint i = FileBrowserListSelectCoordinates(
			fb, (gint)button->x, (gint)button->y
		    );
		    if(i > -1)
		    {
			/* If new object is already selected then unselect
			 * it, otherwise add it to the selection
			 */
			fb->last_single_select_object = -1;
			fb->focus_object = i;
			if(OBJISSEL(fb, i))
			{
			    fb->selection = g_list_remove(
				fb->selection, (gpointer)i
			    );
			    fb->selection_end = g_list_last(fb->selection);
			}
			else
			{
			    fb->selection = g_list_append(
				fb->selection, (gpointer)i
			    );
			    fb->selection_end = g_list_last(fb->selection);

			    /* Update DND icon based on the newly selected
			     * object.
			     */
			    FileBrowserListObjectSetDNDIcon(fb, i);
			}

			/* If matched object is not fully visibile then
			 * scroll so that it is in the center of the
			 * list.
			 */
			if(FileBrowserListObjectVisibility(fb, i) !=
			    GTK_VISIBILITY_FULL
			)
			    FileBrowserListMoveToObject(fb, i, 0.5f);
		    }
		}
		/* Select a group of items */
		else if(button->state & GDK_SHIFT_MASK)
		{
		    gint i = FileBrowserListSelectCoordinates(
			fb, (gint)button->x, (gint)button->y
		    );
		    if(i > -1)
		    {
			/* Select all objects between the last selected
			 * object and the current one
			 */
			fb->last_single_select_object = -1;
			fb->focus_object = i;
			if(fb->selection_end != NULL)
			{
			    gint j = i, n = (gint)fb->selection_end->data;
			    if(j > n)
			    {
				while(j > n)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				    j--;
				}
				fb->selection_end = g_list_last(fb->selection);
			    }
			    else if(j < n)
			    {
				while(j < n)
				{
				    if(!OBJISSEL(fb, j))
					fb->selection = g_list_append(
					    fb->selection, (gpointer)j
					);
				    j++;
				}
				fb->selection_end = g_list_last(fb->selection);
			    }
			}
			else
			{
			    /* No previously selected object, so just select
			     * this one.
			     */
			    fb->selection = g_list_append(
				fb->selection, (gpointer)i
			    );
			    fb->selection_end = g_list_last(fb->selection);
			}

			/* Update DND icon based on the newly selected
			 * object.
			 */
			FileBrowserListObjectSetDNDIcon(fb, i);

			/* If matched object is not fully visibile then
			 * scroll so that it is in the center of the
			 * list
			 */
			if(FileBrowserListObjectVisibility(fb, i) !=
			    GTK_VISIBILITY_FULL
			)
			    FileBrowserListMoveToObject(fb, i, 0.5f);
		    }
		}
		/* Single select */
		else
		{
		    gint i = FileBrowserListSelectCoordinates(
			fb, (gint)button->x, (gint)button->y
		    );
		    if(i > -1)
		    {
			/* If the last single selected object (if any)
			 * is the same as the newly selected object
			 * then update last_single_select_object
			 */
			if((g_list_length(fb->selection) == 1) ?
			    ((gint)fb->selection->data == i) : FALSE
			)
			    fb->last_single_select_object = i;
			else
			    fb->last_single_select_object = -1;

			/* Unselect all */
			fb->focus_object = -1;
			if(fb->selection != NULL)
			    g_list_free(fb->selection);
			fb->selection = fb->selection_end = NULL;

			/* Select this object */
			fb->focus_object = i;
			fb->selection = g_list_append(
			    fb->selection, (gpointer)i
			);
			fb->selection_end = g_list_last(fb->selection);

			/* Update DND icon based on the newly selected
			 * object
			 */
			FileBrowserListObjectSetDNDIcon(fb, i);

			/* If matched object is not fully visibile then
			 * scroll so that it is in the center of the
			 * list
			 */
			if(FileBrowserListObjectVisibility(fb, i) !=
			    GTK_VISIBILITY_FULL
			)
			    FileBrowserListMoveToObject(fb, i, 0.5f);
		    }
		    else
		    {
			/* Unselect all */
			fb->focus_object = -1;
			if(fb->selection != NULL)
			    g_list_free(fb->selection);
			fb->selection = fb->selection_end = NULL;
		    }
		}
		/* Update entry with the new list of selected
		 * objects
		 */
		FileBrowserEntrySetSelectedObjects(fb);
		break;

	      case GDK_BUTTON2:		/* Scroll */
		/* Start scrolling */
		gdk_pointer_grab(
		    button->window,
		    FALSE,
		    GDK_BUTTON_PRESS_MASK |
			GDK_BUTTON_RELEASE_MASK |
			GDK_POINTER_MOTION_MASK |
			GDK_POINTER_MOTION_HINT_MASK,
		    NULL,
		    fb->cur_translate,
		    button->time
		);
		break;

	      case GDK_BUTTON3:		/* Right-click menu */
		/* Right-click menu */
		if(fb->list_menu != NULL)
		{
		    GtkMenu *menu = GTK_MENU(fb->list_menu);
		    gint i = FileBrowserListSelectCoordinates(
			fb, (gint)button->x, (gint)button->y
		    );
		    /* If ctrl or shift modifier keys are held then do
		     * not modify selection before mapping of menu
		     */
		    if((button->state & GDK_CONTROL_MASK) ||
		       (button->state & GDK_SHIFT_MASK)
		    )
		    {
			/* Do not modify selection, just update focus
			 * object
			 */
			fb->focus_object = i;
		    }
		    else if(i > -1)
		    {
			/* Unselect all objects */
			fb->focus_object = -1;
			if(fb->selection != NULL)
			    g_list_free(fb->selection);
			fb->selection = fb->selection_end = NULL;

			/* Select this object */
			fb->focus_object = i;
			fb->selection = g_list_append(
			    fb->selection, (gpointer)i
			);
			fb->selection_end = g_list_last(fb->selection);

			/* Update DND icon based on the newly selected
			 * object
			 */
			FileBrowserListObjectSetDNDIcon(fb, i);
		    }

		    /* Map menu */
		    gtk_menu_popup(
			menu, NULL, NULL,
			NULL, NULL,
			button->button, button->time
		    );
		}
		break;
	    }
	    fb->button = button->button;
	    fb->last_x = (gint)button->x;
	    fb->last_y = (gint)button->y;
	    FileBrowserListQueueDraw(fb);
	    status = TRUE;
	    break;

	  case GDK_BUTTON_RELEASE:
	    button = (GdkEventButton *)event;
	    /* Handle by button number */
	    switch(button->button)
	    {
	      case GDK_BUTTON1:
		if(!(button->state & GDK_CONTROL_MASK) &&
		   !(button->state & GDK_SHIFT_MASK)
		)
		{
		    gulong	double_click_int = 800,
				last_time = fb->last_button1_release_time,
				cur_time = (gulong)button->time,
				dt = (
			(last_time > 0) && (cur_time > last_time)
				) ? (cur_time - last_time) : 0l;
#if 0
		    /* Slow double click? */
		    if((dt > double_click_int) &&
		       (dt <= (2 * double_click_int))
		       (dt != 0)
		    )
		    {
			/* Last single selected object did not change? */
			if(fb->last_single_select_object > -1)
			{
			    /* Call rename callback */
			    FileBrowserRenameCB(NULL, fb);
			}
		    }
#endif
		    /* Double click? */
		    if((dt <= double_click_int) && (dt != 0))
		    {
			/* Last single selected object did not change? */
			if(fb->last_single_select_object > -1)
			{
			    /* Call entry enter callback */
			    FileBrowserEntryEnterCB(fb->entry, fb);
			}
		    }
		}
		fb->last_button1_release_time = (gulong)button->time;
		break;

	      case GDK_BUTTON2:
		/* Stop scrolling as needed */
		if(gdk_pointer_is_grabbed())
		    gdk_pointer_ungrab(button->time);
		break;
	    }
	    fb->button = 0;
	    status = TRUE;
	    break;

	  case GDK_MOTION_NOTIFY:
	    motion = (GdkEventMotion *)event;
#define STOP_SIGNAL_EMIT	{			\
 gtk_signal_emit_stop_by_name(                          \
  GTK_OBJECT(widget), "motion_notify_event"		\
 );							\
}
	    switch(fb->button)
	    {
		gint dx, dy;
		GtkAdjustment *adj;
		GtkRange *range;

	      case GDK_BUTTON1:
		if(motion->is_hint)
		{
		    gint x, y;
		    GdkModifierType mask;
		    gdk_window_get_pointer(
			motion->window, &x, &y, &mask
		    );
		}
		break;

	      case GDK_BUTTON2:
		if(motion->is_hint)
		{
		    gint x, y;
		    GdkModifierType mask;
		    gdk_window_get_pointer(
			motion->window, &x, &y, &mask
		    );
		    dx = x - fb->last_x;
		    dy = y - fb->last_y;
		    fb->last_x = x;
		    fb->last_y = y;
		}
		else
		{
		    dx = (gint)(motion->x - fb->last_x);
		    dy = (gint)(motion->y - fb->last_y);
		    fb->last_x = (gint)motion->x;
		    fb->last_y = (gint)motion->y;
		}

		range = (GtkRange *)fb->list_hsb;
		adj = (range != NULL) ? range->adjustment : NULL;
		if((adj != NULL) && (dx != 0) &&
		   (fb->list_format == FB_LIST_FORMAT_STANDARD)
		)
		{
		    if((adj->upper - adj->page_size) > adj->lower)
			gtk_adjustment_set_value(
			    adj,
			    CLIP(
				adj->value - dx,
				adj->lower, adj->upper - adj->page_size
			    )
			);
		}

		range = (GtkRange *)fb->list_vsb;
		adj = (range != NULL) ? range->adjustment : NULL;
		if((adj != NULL) && (dy != 0) &&
		   ((fb->list_format == FB_LIST_FORMAT_VERTICAL) ||
		    (fb->list_format == FB_LIST_FORMAT_VERTICAL_DETAILS)
		   )
		)
		{
		    if((adj->upper - adj->page_size) > adj->lower)
			gtk_adjustment_set_value(
			    adj,
			    CLIP(
				adj->value - dy,
				adj->lower, adj->upper - adj->page_size
			    )
			);
		}
		STOP_SIGNAL_EMIT
		status = TRUE;
		break;
	    }
#undef STOP_SIGNAL_EMIT
	    break;
	}

	return(status);
}

/*
 *	List scrollbar GtkAdjustment "value_changed" signal callback.
 */
static void FileBrowserListScrollCB(
	GtkAdjustment *adj, gpointer data
)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	FileBrowserListQueueDraw(fb);
}

/*
 *	Select all callback.
 */
static void FileBrowserSelectAllCB(GtkWidget *widget, gpointer data)
{
	gint i;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	if(fb->selection != NULL)
	    g_list_free(fb->selection);
	fb->selection = NULL;
	for(i = 0; i < fb->total_objects; i++)
	    fb->selection = g_list_append(
		fb->selection, (gpointer)i
	    );
	fb->selection_end = g_list_last(fb->selection);
	FileBrowserListQueueDraw(fb);
	FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *	Unselect all callback.
 */
static void FileBrowserUnselectAllCB(GtkWidget *widget, gpointer data)
{
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	if(fb->selection != NULL)
	    g_list_free(fb->selection);
	fb->selection = fb->selection_end = NULL;
	FileBrowserListQueueDraw(fb);
	FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *	Invert selection callback.
 */
static void FileBrowserInvertSelectionCB(GtkWidget *widget, gpointer data)
{
	gint i;
	GList *glist = NULL;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	for(i = 0; i < fb->total_objects; i++)
	{
	    if(!OBJISSEL(fb, i))
		glist = g_list_append(glist, (gpointer)i);
	}
	if(fb->selection != NULL)
	    g_list_free(fb->selection);
	fb->selection = glist;
	fb->selection_end = g_list_last(fb->selection);
	FileBrowserListQueueDraw(fb);
	FileBrowserEntrySetSelectedObjects(fb);
}

/*
 *	Used by FileBrowserRenameCB() as the apply callback for
 *	the floating prompt.
 */
static void FileBrowserRenameFPromptCB(
	gpointer data, const gchar *value
)
{
	gint i;
	GList *glist;
	FileBrowserObject *o;
	FileBrowser *fb = FILE_BROWSER(data);
	if((fb == NULL) || STRISEMPTY(value))
	    return;

#if defined(PROG_LANGUAGE_SPANISH)
# define TITLE_RENAME_FAILED     "Reagrupe Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
# define TITLE_RENAME_FAILED     "Renommer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
# define TITLE_RENAME_FAILED     "Benennen Sie Versagt Um"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define TITLE_RENAME_FAILED     "Rinominare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
# define TITLE_RENAME_FAILED     "Herdoop Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define TITLE_RENAME_FAILED     "O Rename Fracassou"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define TITLE_RENAME_FAILED     "Ombenevn Failed"
#else
# define TITLE_RENAME_FAILED     "Rename Failed"
#endif

#define MESSAGE_RENAME_FAILED(s)	{	\
 CDialogSetTransientFor(fb->toplevel);		\
 CDialogGetResponse(				\
  TITLE_RENAME_FAILED,				\
  (s), NULL,					\
  CDIALOG_ICON_WARNING,				\
  CDIALOG_BTNFLAG_OK,				\
  CDIALOG_BTNFLAG_OK				\
 );						\
 CDialogSetTransientFor(NULL);			\
}

	/* No directory deliminators may exist in the new value */
	if(strchr(value, G_DIR_SEPARATOR) != NULL)
	{
	    gchar *buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"El nombre nuevo \"%s\" contiene deliminators de\n\
guía de '%c' que no se permiten en un nombre de objeto."
#elif defined(PROG_LANGUAGE_FRENCH)
"Le nouveau \"%s\" de nom contient deliminators d'annuaire\n\
de '%c' qui ne sont pas permis dans un nom de l'objet."
#elif defined(PROG_LANGUAGE_GERMAN)
"Der neu \"%s\" enthält '%c' Verzeichnis deliminators, das\n\
im Namen eines Objekts nicht erlaubt wird."
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lo \"%s\" di nome nuovo contiene il deliminators\n\
di elenco di '%c' che non sono lasciati in un nome dell'oggetto."
#elif defined(PROG_LANGUAGE_DUTCH)
"De nieuwe naam \"%s\" bevat '%c' gids deliminators,\n\
die welk in de naam van een voorwerp niet toegestaan is."
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O novo \"%s\" de nome contem deliminators de guia\n\
de '%c' que nao são permitidos num nome do objeto."
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Den nye navne \"%s\" inneholder '%c' katalog deliminators\n\
som ikke tillater i et objekts navn."
#else
"The new name \"%s\" contains '%c' directory\n\
deliminators which are not allowed in an object's name"
#endif
		,
		value, G_DIR_SEPARATOR
	    );
	    MESSAGE_RENAME_FAILED(buf);
	    g_free(buf);
	    return;
	}

	/* Get last selected object */
	glist = fb->selection_end;
	i = (glist != NULL) ? (gint)glist->data : -1;
	o = FileBrowserGetObject(fb, i);
	if((o != NULL) ? (o->full_path != NULL) : FALSE)
	{
	    const gchar *cur_location = FileBrowserGetLocation(fb);
	    gchar	*old_full_path = STRDUP(o->full_path),
			*new_full_path = STRDUP(
			    PrefixPaths(cur_location, value)
			);
	    struct stat lstat_buf;

	    /* New name and old name the same? */
	    if(!strcmp(o->name, value))
	    {

	    }
	    /* Make that the new name does not refer to an object that
	     * already exists
	     */
#if defined(__MSW__)
	    else if(stat(new_full_path, &lstat_buf) == 0)
#else
	    else if(lstat(new_full_path, &lstat_buf) == 0)
#endif
	    {
		gchar *buf = g_strdup_printf(
"An object by the name of \"%s\" already exists.",
		    value
		);
		MESSAGE_RENAME_FAILED(buf);
		g_free(buf);
	    }
	    /* Rename */
	    else if(rename(old_full_path, new_full_path))
	    {
		gchar *buf;

#if defined(PROG_LANGUAGE_SPANISH)
buf = g_strdup_printf(
 "Incapaz de reagrupar \"%s\" a \"%s\".",
 o->name, value
);
#elif defined(PROG_LANGUAGE_FRENCH)
buf = g_strdup_printf(
 "Incapable pour renommer \"%s\" à \"%s\".",
 o->name, value
);
#elif defined(PROG_LANGUAGE_GERMAN)
buf = g_strdup_printf(
 "Unfähig, \"%s\" zu \"%s\" umzubenennen.",
 o->name, value
);
#elif defined(PROG_LANGUAGE_ITALIAN)
buf = g_strdup_printf(
 "Incapace per rinominare \"%s\" a \"%s\".",
 o->name, value
);
#elif defined(PROG_LANGUAGE_DUTCH)
buf = g_strdup_printf(
 "Onbekwaam \"%s\" aan \"%s\" te herdopen.",
 o->name, value
);
#elif defined(PROG_LANGUAGE_PORTUGUESE)
buf = g_strdup_printf(
 "Incapaz a \"%s\" de rename a \"%s\".",
 o->name, value
);
#elif defined(PROG_LANGUAGE_NORWEGIAN)
buf = g_strdup_printf(
 "Maktesløs ombenevne \"%s\" til \"%s\".",
 o->name, value
);
#else
		switch(errno)
		{
		  case EISDIR:
		    buf = g_strdup_printf(
"\"%s\" is an existing directory, but \"%s\" is not a directory.",
			value, o->name
		    );
		    break;

		  case ENOTEMPTY:
		  case EEXIST:
		    buf = g_strdup_printf(
"\"%s\" is a non-empty directory.",
			value
		    );
		    break;

		  case EBUSY:
		    buf = g_strdup_printf(
"\"%s\" or \"%s\" is a directory that is currently in use.",
			o->name, value
		    );
		    break;

		  case EINVAL:
		    buf = g_strdup_printf(
"\"%s\" contains a path prefix of \"%s\" or an attempt\n\
was made to make a directory a subdirectory of itself.",
			value, o->name
		    );
		    break;

		  case EMLINK:
		    buf = g_strdup_printf(
"\"%s\" already has the maximum number of links\n\
to it, or it was a directory and the directory containing\n\
\"%s\" has reached the maximum number of links.",
			o->name, value
		    );
		    break;

		  case ENOTDIR:
		    buf = g_strdup_printf(
"A compoent in \"%s\"\n\
is not a directory or \"%s\" is a directory and\n\
\"%s\" exists but is not a directory.",
			old_full_path,
			o->name, value
		    );
		    break;

		  case EACCES:
		    buf = g_strdup_printf(
"You do not have sufficient permission to rename\n\
\"%s\" to \"%s\".",
			o->name, value
		    );
		    break;

		  case ENAMETOOLONG:
		    buf = g_strdup_printf(
"The name \"%s\" is too long.",
			value
		    );
		    break;

		  case ENOENT:
		    buf = g_strdup_printf(
"One or more directory compoents of \"%s\" or \"%s\"\n\
is a dangling symbolic link.",
			value, o->name
		    );
		    break;

		  case ENOMEM:
		    buf = STRDUP("The system is out of memory.");
		    break;

		  case EROFS:
		    buf = g_strdup_printf(
"The object \"%s\" is on a read-only filesystem\n\
and cannot be renamed.",
			o->name
		    );
		    break;
#ifdef ELOOP
		  case ELOOP:
		    buf = g_strdup_printf(
"Too many symbolic links were encountered when trying to\n\
resolve \"%s\" or \"%s\".",
			value, o->name
		    );
		    break;
#endif
		  case ENOSPC:
		    buf = g_strdup_printf(
"The device that \"%s\" is on is out of free space.",
			o->name
		    );
		    break;

		  default:
		    buf = g_strdup_printf(
"Unable to rename \"%s\" to \"%s\".",
			o->name, value
		    );
		    break;
		}
#endif
		MESSAGE_RENAME_FAILED(buf);
		g_free(buf);
	    }
	    else
	    {
		/* Rename successful */

		/* Update list */
		FileBrowserListUpdate(fb, NULL);

		/* Reselect object */
		for(i = 0; i < fb->total_objects; i++)
		{
		    o = fb->object[i];
		    if((o != NULL) ? (o->full_path == NULL) : TRUE)
			continue;

		    if(!strcmp(o->full_path, new_full_path))
		    {
			/* Select this object */
			fb->focus_object = i;
			fb->selection = g_list_append(
			    fb->selection, (gpointer)i
			);
			fb->selection_end = g_list_last(fb->selection);

			/* Scroll to this object */
			FileBrowserListMoveToObject(fb, i, 0.5f);

			break;
		    }
		}
		FileBrowserListQueueDraw(fb);

		/* Update selected objects on entry */
		FileBrowserEntrySetSelectedObjects(fb);
	    }

	    g_free(old_full_path);
	    g_free(new_full_path);
	}

#undef MESSAGE_RENAME_FAILED
#undef TITLE_RENAME_FAILED
}

/*
 *	Rename callback.
 */
static void FileBrowserRenameCB(GtkWidget *widget, gpointer data)
{
	gint i, x, y, width, height;
	const GList *glist;
	const FileBrowserObject *o;
	FileBrowser *fb = FILE_BROWSER(data);
	if((fb == NULL) || FPromptIsQuery())
	    return;

	/* Get last selected object */
	glist = fb->selection_end;
	i = (glist != NULL) ? (gint)glist->data : -1;
	o = FileBrowserGetObject(fb, i);
	if(o == NULL)
	    return;

	/* Get fprompt geometry based on list display format */
	x = o->x;
	y = o->y;
	width = MAX(o->width + 4, 100);
	height = -1;
	switch(fb->list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    if(fb->list_vsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_vsb);
		GtkAdjustment *adj = range->adjustment;
		y -= (gint)((adj != NULL) ? adj->value : 0.0f);
	    }
	    break;
	  case FB_LIST_FORMAT_STANDARD:
	    if(fb->list_hsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_hsb);
		GtkAdjustment *adj = range->adjustment;
		x -= (gint)((adj != NULL) ? adj->value : 0.0f);
	    }
	    break;
	}

	/* Get root window relative coordinates of the object for
	 * placement of the floating prompt.
	 */
	if(fb->list_da != NULL)
	{
	    GtkWidget *w = fb->list_da;
	    GdkWindow *window = w->window;
	    if(window != NULL)
	    {
		gint rx, ry;
		gdk_window_get_deskrelative_origin(
		    window, &rx, &ry
		);
		x += rx;
		y += ry;
	    }
	}
	/* At this point x and y should now be the root window
	 * relative position for the floating prompt
	 */
	FPromptSetTransientFor(fb->toplevel);
	FPromptSetPosition(x - 2, y - 2);
	FPromptMapQuery(
	    NULL,			/* No label */
	    o->name,
	    NULL,			/* No tooltip */
	    FPROMPT_MAP_NO_MOVE,	/* Map code */
	    width, height,		/* Width and height */
	    0,				/* No buttons */
	    fb,				/* Data */
	    NULL,			/* No browse callback */
	    FileBrowserRenameFPromptCB,
	    NULL			/* No cancel callback */
	);
}

/*
 *	CHMod fprompt callback.
 */
static void FileBrowserCHModFPromptCB(
	gpointer data, const gchar *value
)
{
	gint i;
	GList *glist;
	FileBrowserObject *o;
	FileBrowser *fb = FILE_BROWSER(data);
	if((fb == NULL) || STRISEMPTY(value))
	    return;

#if defined(PROG_LANGUAGE_SPANISH)
# define TITLE_CHMOD_FAILED     "Cambie El Modo Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
# define TITLE_CHMOD_FAILED     "Changer Le Mode A échoué"
#elif defined(PROG_LANGUAGE_GERMAN)
# define TITLE_CHMOD_FAILED     "Ändern Sie Versagten Modus"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define TITLE_CHMOD_FAILED     "Cambiare Il Modo Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
# define TITLE_CHMOD_FAILED     "Verandeer Modus Verzuimde"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define TITLE_CHMOD_FAILED     "Mude Modo Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define TITLE_CHMOD_FAILED     "Forandr Sviktet Modus"
#else
# define TITLE_CHMOD_FAILED     "Change Mode Failed"
#endif

#define MESSAGE_CHMOD_FAILED(s)		{	\
 CDialogSetTransientFor(fb->toplevel);		\
 CDialogGetResponse(                            \
  TITLE_CHMOD_FAILED,                           \
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

	/* Get last selected object */
	glist = fb->selection_end;
	i = (glist != NULL) ? (gint)glist->data : -1;
	o = FileBrowserGetObject(fb, i);
	if((o != NULL) ? (o->full_path != NULL) : FALSE)
	{
#if defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
	    mode_t m = 0;
	    const gchar *s = value;

	    /* Begin parsing value to obtain new mode value */
	    while(ISBLANK(*s))
		s++;

	    /* User */
	    /* Read */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'r')
		    m |= S_IRUSR;
		s++;
	    }
	    /* Write */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'w')
		    m |= S_IWUSR;
		s++;
	    }
	    /* Execute */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'x')
		    m |= S_IXUSR;
		else if(tolower(*s) == 's')
		    m |= S_ISUID;
		s++;
	    }
	    /* Group */
	    /* Read */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'r')
		    m |= S_IRGRP;
		s++;
	    }
	    /* Write */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'w')
		    m |= S_IWGRP;
		s++;
	    }
	    /* Execute */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'x')
		    m |= S_IXGRP;
		else if(tolower(*s) == 'g')
		    m |= S_ISGID;
		s++;
	    }
	    /* Other */
	    /* Read */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'r')
		    m |= S_IROTH;
		s++;
	    }
	    /* Write */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'w')
		    m |= S_IWOTH;
		s++;
	    }
	    /* Execute */
	    if(*s != '\0')
	    {
		if(tolower(*s) == 'x')
		    m |= S_IXOTH;
		else if(tolower(*s) == 't')
		    m |= S_ISVTX;
		s++;
	    }

	    /* CHMod */
	    if(chmod(o->full_path, m))
	    {
		gchar *buf;

#if defined(PROG_LANGUAGE_SPANISH)
 buf = g_strdup_printf(
  "Incapaz de cambiar el modo de \"%s\".",
  o->name
 );
#elif defined(PROG_LANGUAGE_FRENCH)
 buf = g_strdup_printf(
  "Incapable pour changer le mode de \"%s\".",
  o->name
 );
#elif defined(PROG_LANGUAGE_GERMAN)
 buf = g_strdup_printf(
  "Unfähig, den modus von \"%s\" zu ändern.",
  o->name
 );
#elif defined(PROG_LANGUAGE_ITALIAN)
 buf = g_strdup_printf(
  "Incapace per cambiare il modo di \"%s\".",
  o->name
 );
#elif defined(PROG_LANGUAGE_DUTCH)
 buf = g_strdup_printf(
  "Onbekwame de modus van \"%s\" te veranderen.",
  o->name
 );
#elif defined(PROG_LANGUAGE_PORTUGUESE)
 buf = g_strdup_printf(
  "Incapaz de mudar o modo de \"%s\".",
  o->name
 );
#elif defined(PROG_LANGUAGE_NORWEGIAN)
 buf = g_strdup_printf(
  "Maktesløs forandre modus av \"%s\".",
  o->name
 );
#else
		switch(errno)
		{
		  case EPERM:
		    buf = g_strdup_printf(
"You do not have sufficient permission to change the permissions\n\
of \"%s\".",
			o->name
		    );
		    break;

		  case EROFS:
		    buf = g_strdup_printf(
"The object \"%s\" is on a read-only filesystem\n\
and its permissions cannot be changed.",
			o->name
		    );
		    break;

		  case ENAMETOOLONG:
		    buf = g_strdup_printf(
"The name \"%s\" is too long.",
			o->name
		    );
		    break;

		  case ENOMEM:
		    buf = STRDUP(
"The system is out of memory."
		    );
		    break;

		  case ENOTDIR:
		    buf = STRDUP(
"A compoent of the path prefix is not a directory."
		    );
		    break;

		  case EACCES:
		    buf = STRDUP(
"Search permission is denied on a compoent of the path prefix."
		    );
		    break;

#ifdef ELOOP
		  case ELOOP:
		    buf = g_strdup_printf(
"Too many symbolic links were encountered when trying to\n\
resolve \"%s\".",
			o->name
		    );
		    break;
#endif

		  default:
		    buf = g_strdup_printf(
"Unable to change the mode of \"%s\".",
			o->name
		    );
		    break;
		}
#endif
		MESSAGE_CHMOD_FAILED(buf);
		g_free(buf);
	    }
	    else
	    {
		/* Statistics */
#ifdef __MSW__
		if(stat(o->full_path, &o->lstat_buf))
#else
		if(lstat(o->full_path, &o->lstat_buf))
#endif
		    memset(&o->lstat_buf, 0x00, sizeof(struct stat));

		/* Update values */
		FileBrowserObjectUpdateValues(fb, o);
	    }
#endif
	}

	/* Need to redraw and update the value in the entry
	 * since one of the selected object's names may have
	 * changed
	 */
	FileBrowserListQueueDraw(fb);
	FileBrowserEntrySetSelectedObjects(fb);

#undef MESSAGE_RENAME_FAILED
#undef TITLE_RENAME_FAILED
}

/*
 *	CHMod callback.
 */
static void FileBrowserCHModCB(GtkWidget *widget, gpointer data)
{
	gint i, x, y, width, height;
	mode_t m;
	gchar *value;
	const GList *glist;
	const FileBrowserObject *o;
	const FileBrowserColumn *column, *column2;
	FileBrowser *fb = FILE_BROWSER(data);
	if((fb == NULL) || FPromptIsQuery())
	    return;

	/* Get last selected object */
	glist = fb->selection_end;
	i = (glist != NULL) ? (gint)glist->data : -1;
	o = FileBrowserGetObject(fb, i);
	if(o == NULL)
	    return;

	m = o->lstat_buf.st_mode;

#ifdef S_ISLNK
	/* Cannot chmod links */
	if(S_ISLNK(m))
	{
	    CDialogSetTransientFor(fb->toplevel);
	    CDialogGetResponse(
"Change Mode Failed",
"You cannot change the mode of symbolic link objects",
		NULL,
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    return;
	}
#endif

#if !defined(S_IRUSR) || !defined(S_IWUSR) || !defined(S_IXUSR)
	/* Since permissions not supported we cannot chmod */
	if(TRUE)
	{
	    CDialogSetTransientFor(fb->toplevel);
	    CDialogGetResponse(
"Change Mode Failed",
"Changing the mode of objects is not supported on this system.",
		NULL,
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    return;
	}
#else
	/* Format current value */
	value = g_strdup_printf(
	    "%c%c%c%c%c%c%c%c%c",
	    (m & S_IRUSR) ? 'r' : '-',
	    (m & S_IWUSR) ? 'w' : '-',
	    (m & S_ISUID) ? 'S' :
		((m & S_IXUSR) ? 'x' : '-'),
	    (m & S_IRGRP) ? 'r' : '-',
	    (m & S_IWGRP) ? 'w' : '-',
	    (m & S_ISGID) ? 'G' :
		((m & S_IXGRP) ? 'x' : '-'),
	    (m & S_IROTH) ? 'r' : '-',
	    (m & S_IWOTH) ? 'w' : '-',
	    (m & S_ISVTX) ? 'T' :
		((m & S_IXOTH) ? 'x' : '-')
	);
#endif

	/* Get fprompt geometry based on list display format */
	x = o->x;
	y = o->y;
	width = MAX(o->width + 4, 80);
	height = -1;
	switch(fb->list_format)
	{
	  case FB_LIST_FORMAT_VERTICAL_DETAILS:
	  case FB_LIST_FORMAT_VERTICAL:
	    if(fb->list_vsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_vsb);
		GtkAdjustment *adj = range->adjustment;
		y -= (gint)((adj != NULL) ? adj->value : 0.0f);
	    }
	    column = FileBrowserListGetColumn(fb, 1);
	    if(column != NULL)
		x += column->position;
	    column2 = FileBrowserListGetColumn(fb, 2);
	    if((column != NULL) && (column2 != NULL))
		width = MAX(column2->position - column->position + 4, 80);
	    break;

	  case FB_LIST_FORMAT_STANDARD:
	    if(fb->list_hsb != NULL)
	    {
		GtkRange *range = GTK_RANGE(fb->list_hsb);
		GtkAdjustment *adj = range->adjustment;
		x -= (gint)((adj != NULL) ? adj->value : 0.0f);
	    }
	    break;
	}
	/* Get root window relative coordinates of the object for
	 * placement of the floating prompt
	 */
	if(fb->list_da != NULL)
	{
	    GtkWidget *w = fb->list_da;
	    GdkWindow *window = w->window;
	    if(window != NULL)
	    {
		gint rx, ry;
		gdk_window_get_deskrelative_origin(
		    window, &rx, &ry
		);
		x += rx;
		y += ry;
	    }
	}
	/* At this point x and y should now be the root window
	 * relative position for the floating prompt
	 */
	FPromptSetTransientFor(fb->toplevel);
	FPromptSetPosition(x - 2, y - 2);
	FPromptMapQuery(
	    NULL,                       /* No label */
	    value,
	    NULL,                       /* No tooltip */
	    FPROMPT_MAP_NO_MOVE,        /* Map code */
	    width, height,		/* Width and height */
	    0,                          /* No buttons */
	    fb,                         /* Data */
	    NULL,                       /* No browse callback */
	    FileBrowserCHModFPromptCB,
	    NULL                        /* No cancel callback */
	);

	g_free(value);
}

/*
 *	List object delete callback.
 */
static void FileBrowserDeleteCB(GtkWidget *widget, gpointer data)
{
	gint objects_deleted = 0;
	gint i, response;
	gchar *buf;
	GList *glist;
	FileBrowserObject *o;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

#if defined(PROG_LANGUAGE_SPANISH)
# define TITLE_DELETE_FAILED	"Borre Fallado"
#elif defined(PROG_LANGUAGE_FRENCH)
# define TITLE_DELETE_FAILED	"Effacer Echoué"
#elif defined(PROG_LANGUAGE_GERMAN)
# define TITLE_DELETE_FAILED	"Löschen Sie Versagt"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define TITLE_DELETE_FAILED	"Cancellare Fallito"
#elif defined(PROG_LANGUAGE_DUTCH)
# define TITLE_DELETE_FAILED	"Schrap Geverzuimenene"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define TITLE_DELETE_FAILED	"Anule Fracassado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define TITLE_DELETE_FAILED	"Stryk Failed"
#else
# define TITLE_DELETE_FAILED	"Delete Failed"
#endif

#define MESSAGE_DELETE_FAILED(s)        {       \
 CDialogSetTransientFor(fb->toplevel);          \
 CDialogGetResponse(                            \
  TITLE_DELETE_FAILED,                          \
  (s), NULL,                                    \
  CDIALOG_ICON_WARNING,                         \
  CDIALOG_BTNFLAG_OK,                           \
  CDIALOG_BTNFLAG_OK                            \
 );                                             \
 CDialogSetTransientFor(NULL);                  \
}

	if(CDialogIsQuery())
	    return;

	/* Get selection */
	glist = fb->selection;
	if(glist == NULL)
	    return;

	FileBrowserSetBusy(fb, TRUE);

	/* Format confirmation message, if only one object is selected
	 * then confirm with its name. Otherwise confirm the number of
	 * objects to be deleted.
	 */
	i = g_list_length(glist);
	if(i == 1)
	{
	    i = (gint)glist->data;
	    o = FileBrowserGetObject(fb, i);
	    if((o != NULL) ? (o->name != NULL) : FALSE)
		buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"¿Usted está seguro que usted quiere borrar \"%s\"?"
#elif defined(PROG_LANGUAGE_FRENCH)
"Etes-vous sûr que vous voulez effacer \"%s\"?"
#elif defined(PROG_LANGUAGE_GERMAN)
"Sind sie sicher sie \"%s\" wollen löschen?"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lei sono sicuro che lei vuole cancellare \"%s\"?"
#elif defined(PROG_LANGUAGE_DUTCH)
"Bent u zeker u \"%s\" wil schrappen?"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Estão seguro quer anular \"%s\"?"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Er De sikker De stryker \"%s\"?"
#else
"Are you sure you want to delete \"%s\"?"
#endif
		    , o->name
		);
	    else
		buf = STRDUP(
"Are you sure you want to delete this (unnamed) object?"
		);
	}
	else
	{
	    buf = g_strdup_printf(
#if defined(PROG_LANGUAGE_SPANISH)
"¿Borra %i objetos escogidos?"
#elif defined(PROG_LANGUAGE_FRENCH)
"Efface %i objets choisis?"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie %i ausgewählte Objekte?"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancella %i oggetti scelti?"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap %i geselecteerde voorwerpen?"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule %i objetos selecionados?"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk %i valgte ut objekt?"
#else
"Delete %i selected objects?\n"
#endif
		, i
	    );
	}

	/* Confirm delete */
	CDialogSetTransientFor(fb->toplevel);
	response = CDialogGetResponseIconData(
#if defined(PROG_LANGUAGE_SPANISH)
"Confirme Borre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Confirmer Effacer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Bestätigen Sie Löscht"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Confermare Cancellare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Bevestiig Schrappet"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Confirme Anula"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Bekreft Delete"
#else
"Confirm Delete"
#endif
	    , buf, NULL,
	    (guint8 **)icon_trash_32x32_xpm,
	    CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
	    CDIALOG_BTNFLAG_NO
	);
	CDialogSetTransientFor(NULL);
	g_free(buf);
	if(response != CDIALOG_RESPONSE_YES)
	{
	    FileBrowserSetBusy(fb, FALSE);
	    return;
	}

	/* Iterate through selected objects */
	while(glist != NULL)
	{
	    i = (gint)glist->data;
	    o = FileBrowserGetObject(fb, i);
	    if((o != NULL) ? (o->full_path != NULL) : FALSE)
	    {
		const gchar *full_path = o->full_path;
		if(ISLPATHDIR(full_path))
		{
		    /* Delete directory */
		    if(rmdir(full_path))
		    {
			const gchar *name = o->name;
#if defined(PROG_LANGUAGE_SPANISH)
buf = g_strdup_printf(
 "Incapaz de borrar guía \"%s\".",
 name
);
#elif defined(PROG_LANGUAGE_FRENCH)
buf = g_strdup_printf(
 "Incapable pour effacer \"%s\" d'annuaire.",
 name
);
#elif defined(PROG_LANGUAGE_GERMAN)
buf = g_strdup_printf(
 "Unfähig, Verzeichnis \"%s\" zu löschen.",
 name
);
#elif defined(PROG_LANGUAGE_ITALIAN)
buf = g_strdup_printf(
 "Incapace per cancellare \"%s\" di elenco.",
 name
);
#elif defined(PROG_LANGUAGE_DUTCH)
buf = g_strdup_printf(
 "Onbekwaam gids \"%s\" te schrappen.",
 name
);
#elif defined(PROG_LANGUAGE_PORTUGUESE)
buf = g_strdup_printf(
 "Incapaz de anular \"%s\" de guia.",
 name
);
#elif defined(PROG_LANGUAGE_NORWEGIAN)
buf = g_strdup_printf(
 "Maktesløs stryke katalog \"%s\".",
 name
);
#else
			switch(errno)
			{
			  case EPERM:
			    buf = g_strdup_printf(
"The filesystem that the directory \"%s\" is on does\n\
not permit the removal of directory objects.",
				name
			    );
			    break;

			  case EACCES:
			    buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
				name
			    );
			    break;

			  case ENAMETOOLONG:
			    buf = g_strdup_printf(
"The name of \"%s\" is too long.",
				name
			    );
			    break;

			  case ENOENT:
			    buf = g_strdup_printf(
"A directory compoent of \"%s\" does not exist or\n\
is a dangling symbolic link.",
				name
			    );
			    break;

			  case ENOTEMPTY:
			    buf = g_strdup_printf(
"The directory \"%s\" is not empty, you must\n\
make sure that the directory is empty before you can delete it.",
				name
			    );
			    break;

			  case EBUSY:
			    buf = g_strdup_printf(
"The directory \"%s\" is currently in use and\n\
cannot be deleted.",
				name
			    );
			    break;

			  case ENOMEM:
			    buf = STRDUP(
"The system is out of memory."
			    );
			    break;

			  case EROFS:
			    buf = g_strdup_printf(
"\"%s\" is located on a read-only filesystem.",
				name
			    );
			    break;
#ifdef ELOOP
			  case ELOOP:
			    buf = g_strdup_printf(
"Too many symbolic links were encountered while resolving\n\
\"%s\".",
				name
			    );
			    break;
#endif
			  default:
			    buf = g_strdup_printf(
"Unable to delete directory \"%s\".",
				name
			    );
			    break;
			}
#endif
			MESSAGE_DELETE_FAILED(buf);
			g_free(buf);
		    }
		    else
		    {
			objects_deleted++;
		    }
		}
		else
		{
		    /* Delete object */
		    if(unlink((const char *)full_path))
		    {
			const gchar *name = o->name;
#if defined(PROG_LANGUAGE_SPANISH)
buf = g_strdup_printf(
 "Incapaz de borrar \"%s\".",
 name
);
#elif defined(PROG_LANGUAGE_FRENCH)
buf = g_strdup_printf(
 "Incapable pour effacer \"%s\".",
 name
);
#elif defined(PROG_LANGUAGE_GERMAN)
buf = g_strdup_printf(
 "Unfähig, \"%s\" zu löschen.",
 name
);
#elif defined(PROG_LANGUAGE_ITALIAN)
buf = g_strdup_printf(
 "Incapace per cancellare \"%s\".",
 name
);
#elif defined(PROG_LANGUAGE_DUTCH)
buf = g_strdup_printf(
 "Onbekwaam \"%s\" te schrappen.",
 name
);
#elif defined(PROG_LANGUAGE_PORTUGUESE)
buf = g_strdup_printf(
 "Incapaz de anular \"%s\".",
 name
);
#elif defined(PROG_LANGUAGE_NORWEGIAN)
buf = g_strdup_printf(
 "Maktesløs stryke \"%s\".",
 name
);
#else
			switch(errno)
			{
			  case EACCES:
			    buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
				name
			    );
			    break;

			  case EPERM:
			    buf = g_strdup_printf(
"You do not have sufficient permission to delete \"%s\".",
				name
			    );
			    break;

			  case ENAMETOOLONG:
			    buf = g_strdup_printf(
"The name of \"%s\" is too long.",
				name
			    );
			    break;

			  case ENOENT:
			    buf = g_strdup_printf(
"A directory compoent of \"%s\" does not exist or\n\
is a dangling symbolic link.",
				name
			    );
			    break;

			  case ENOTDIR:
			    buf = g_strdup_printf(
"A directory compoent of \"%s\" is not a directory.",
				name
			    );
			    break;

			  case ENOMEM:
			    buf = STRDUP(
"The system is out of memory."
			    );
			    break;

			  case EROFS:
			    buf = g_strdup_printf(
"\"%s\" is located on a read-only filesystem.",
				name
			    );
			    break;
#ifdef ELOOP
			  case ELOOP:
			    buf = g_strdup_printf(
"Too many symbolic links were encountered while resolving\n\
\"%s\".",
				name
			    );
			    break;
#endif
			  case EIO:
			    buf = g_strdup_printf(
"An I/O error occured while attempting to delete \"%s\".",
				name
			    );
			    break;

			  default:
			    buf = g_strdup_printf(
"Unable to delete \"%s\".",
				name
			    );
			    break;
			}
#endif
			MESSAGE_DELETE_FAILED(buf);
			g_free(buf);
		    }
		    else
		    {
			objects_deleted++;
		    }
		}
	    }

	    glist = g_list_next(glist);
	}

	/* Need to refresh everything due to delete */
	FileBrowserRefreshCB(fb->refresh_btn, fb);

	FileBrowserSetBusy(fb, FALSE);

#undef MESSAGE_DELETE_FAILED
#undef TITLE_DELETE_FAILED
}

/*
 *	Entry enter callback.
 *
 *	This function is slightly different from the OK callback
 *	since it will switch to a directory if the single value
 *	is a directory.
 *
 *	This function will call the OK callback if there are
 *	multiple values specified (when it sees a ',' deliminator)
 *	or the single object is specified and (if it actually exists)
 *	does not lead to a directory.
 */
static void FileBrowserEntryEnterCB(GtkWidget *widget, gpointer data)
{
	gchar *s;
	GtkWidget *w;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	w = fb->entry;
	if(w == NULL)
	    return;

	/* Get file name entry value */
	s = gtk_entry_get_text(GTK_ENTRY(w));
	if((s != NULL) ? (*s == '\0') : TRUE)
	    return;

	s = STRDUP(s);	/* Make a copy of s */

	/* Filter specified? */
	if((strchr(s, '*') != NULL) || (strchr(s, '?') != NULL))
	{
	    /* Reget listing with the newly specified filter */
	    FileBrowserListUpdate(fb, s);
	}
	/* Multiple objects specified? */
	else if(strchr(s, ','))
	{
	    /* Multiple objects selected, just call the OK callback */
	    FileBrowserOKCB(fb->ok_btn, fb);
	}
	/* Current directory specified? */
	else if(!strcmp(s, "."))
	{
	    /* Do nothing */
	}
	/* Parent directory specified? */
	else if(!strcmp(s, ".."))
	{
	    /* Go to parent location */
	    const gchar *cur_location = FileBrowserGetLocation(fb);
	    gchar *parent = (cur_location != NULL) ?
		g_dirname(cur_location) : NULL;
	    FileBrowserSetLocation(fb, parent);
	    g_free(parent);
	}
	/* Home directory specified? */
	else if(*s == '~')
	{
	    /* Go to home directory */
	    FileBrowserSetLocation(fb, s);
	}
	else
	{
	    /* All else assume single object specified, it may or may
	     * not actually exist
	     */
	    gchar *full_path;
	    struct stat stat_buf;

	    /* Check if the object specified is specified as a full
	     * path and if it is not then prefix the current location
	     * to it
	     */
	    if(g_path_is_absolute(s))
	    {
		full_path = STRDUP(s);
	    }
	    else
	    {
		const gchar *cur_location = FileBrowserGetLocation(fb);
		full_path = STRDUP(PrefixPaths(cur_location, s));
	    }

	    /* Check if the object specified by full_path exists and if
	     * it leads to a directory.
	     */
	    if(stat(full_path, &stat_buf))
	    {
		/* Cannot stat object (note that it may exist locally
		 * as a symbolic link), the user may want to save as a
		 * new object, so go ahead and call the OK callback on
		 * this.
		 */
		FileBrowserOKCB(fb->ok_btn, fb);
	    }
	    else
	    {
		/* If the object specified by full_path is a directory
		 * then go to that directory, otherwise call the OK
		 * callback.
		 */
		if(S_ISDIR(stat_buf.st_mode))
		    FileBrowserSetLocation(fb, full_path);
		else
		    FileBrowserOKCB(fb->ok_btn, fb);
	    }

	    g_free(full_path);
	}

	g_free(s);
}

/*
 *	File type list selected value changed callback.
 */
static void FileBrowserTypeListChangeCB(
	GtkWidget *widget, gpointer data, GList *glist
)
{
	const gchar *s, *ext;
	gchar *s2;
	GtkEntry *entry;
	GtkCombo *combo;
	fb_type_struct *t;
	FileBrowser *fb = FILE_BROWSER(data);
	if(fb == NULL)
	    return;

	combo = (GtkCombo *)fb->type_combo;
	if(combo == NULL)
	    return;

	entry = GTK_ENTRY(combo->entry);

	/* Get selected file type value */
	s = gtk_entry_get_text(entry);
	if((s != NULL) ? (*s == '\0') : TRUE)
	    return;

	/* Parse the selected file type and store the values in the
	 * current file type structure.
	 *
	 * The format of s will be:
	 *
	 *	"desc(ext)"
	 *
	 *	"Description of Format (*.ext1 *.ext2)"
	 */
	t = &fb->cur_type;

	/* Get name */
	g_free(t->name);
	t->name = STRDUP(s);
	s2 = strrchr(t->name, '(');
	if(s2 != NULL)
	{
	    if(s2 > t->name)
		s2--;
	    while((s2 > t->name) && ISBLANK(*s2))
		s2--;
	    *(s2 + 1) = '\0';
	}

	/* Get extensions list */
	g_free(t->ext);
	t->ext = NULL;

	ext = strrchr(s, '(');
	if(ext != NULL)
	{
	    ext++;
	    while(ISBLANK(*ext))
		ext++;

	    t->ext = STRDUP(ext);
	    s2 = strchr(t->ext, ')');
	    if(s2 != NULL)
	    {
		if(s2 > t->ext)
		    s2--;
		while((s2 > t->ext) && ISBLANK(*s2))
		    s2--;
		*(s2 + 1) = '\0';
	    }
	}

	/* Update values that depend on file type changes only if the
	 * file type change freeze count is 0
	 */
	if(fb->freeze_count > 0)
	    return;

	fb->freeze_count++;

	/* Need to update list due to type change */
	FileBrowserListUpdate(fb, NULL);

	fb->freeze_count--;
}


/*
 *	Initializes the file browser.
 */
gint FileBrowserInit(void)
{
	const gint	border_major = 5,
			border_minor = 2;
	gpointer w_ptr1, w_ptr2;
	GList *glist;
	GdkWindow *window;
	GtkAdjustment *adj;
	GtkAccelGroup *accelgrp;
	GtkWidget *w, *parent, *parent2, *parent3, *parent4, *parent5;
	GtkEntry *entry;
	GtkCombo *combo;
	pulist_struct *pulist;
	FileBrowser *fb = &file_browser;

	/* Reset values */
	fb->map_state = FALSE;
	fb->busy_count = 0;
	fb->freeze_count = 0;
	fb->accelgrp = accelgrp = gtk_accel_group_new();
	fb->cur_busy = gdk_cursor_new(GDK_WATCH);
	fb->cur_column_hresize = gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW);
	fb->cur_translate = gdk_cursor_new(GDK_FLEUR);
	fb->list_pm = NULL;
	fb->cur_location = NULL;
	fb->block_loop_level = 0;
	fb->user_response = FALSE;
	fb->list_format = FB_LIST_FORMAT_STANDARD;
	fb->column = NULL;
	fb->total_columns = 0;
	memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));
	fb->selected_path = NULL;
	fb->total_selected_paths = 0;
	fb->icon = NULL;
	fb->total_icons = 0;
#ifdef __MSW__
	fb->uid = 0;
	fb->euid = 0;
	fb->gid = 0;
	fb->egid = 0;
#else
	fb->uid = getuid();
	fb->euid = geteuid();
	fb->gid = getgid();
	fb->egid = getegid();
#endif
#ifdef __MSW__
	fb->home_path = STRDUP("c:\\");
#else
	fb->home_path = STRDUP(g_getenv("HOME"));
#endif
	fb->drive_path = NULL;
	fb->total_drive_paths = 0;
	fb->focus_object = -1;
	fb->selection = fb->selection_end = NULL;
	fb->object = NULL;
	fb->total_objects = 0;
	fb->objects_per_row = 0;
	fb->last_single_select_object = -1;
	fb->button = 0;
	fb->last_x = 0;
	fb->last_y = 0;
	fb->last_button1_release_time = 0l;

	/* Toplevel */
	fb->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG);
	gtk_widget_set_usize(w, FB_WIDTH, FB_HEIGHT);
	gtk_window_set_title(GTK_WINDOW(w), "Select File");
#ifdef PROG_NAME
	gtk_window_set_wmclass(
	    GTK_WINDOW(w), "fileselection", PROG_NAME
	);
#endif
	gtk_widget_realize(w);
	window = w->window;
	if(window != NULL)
	{
	    GdkGeometry geo;

	    gdk_window_set_decorations(
		window,
		GDK_DECOR_BORDER | GDK_DECOR_TITLE | GDK_DECOR_MENU |
		GDK_DECOR_MINIMIZE
	    );
	    gdk_window_set_functions(
		window,
		GDK_FUNC_MOVE | GDK_FUNC_MINIMIZE | GDK_FUNC_CLOSE
	    );

	    geo.min_width = 100;
	    geo.min_height = 70;
	    geo.max_width = gdk_screen_width() - 10;
	    geo.max_height = gdk_screen_height() - 10;
	    geo.base_width = 0;
	    geo.base_height = 0;
	    geo.width_inc = 1;
	    geo.height_inc = 1;
	    geo.min_aspect = 1.3f;
	    geo.max_aspect = 1.3f;
	    gdk_window_set_geometry_hints(
		window, &geo,
		GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE |
		GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC
	    );
	}
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(FileBrowserDeleteEventCB), fb
	);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp);
	parent = w;

	/* Main vbox */
	fb->main_vbox = w = gtk_vbox_new(FALSE, border_major);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;


	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;


	/* Hbox for directory popup list label and drawing area*/
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent3 = w;

	w = gtk_label_new(
#if defined(PROG_LANGUAGE_SPANISH)
"Ubicación:"
#elif defined(PROG_LANGUAGE_FRENCH)
"Emplacement:"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ort:"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Posizione:"
#elif defined(PROG_LANGUAGE_DUTCH)
"Plaats:"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Localidade:"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Plassering:"
#else
"Location:"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Hbox for Directory popup list and map button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Directory popup list frame and drawing area */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent5 = w;
	/* Drawing area */
	fb->dir_pulist_da = w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, -1, 20 + (2 * 0));
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
	    GDK_FOCUS_CHANGE_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_in_event",
	    GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_out_event",
	    GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(FileBrowserDirPUListDAEventCB), fb
	);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);

	/* Directory popup list button */
	fb->dir_pulist_btn = w = PUListNewMapButtonArrow(
	    GTK_ARROW_DOWN, GTK_SHADOW_OUT,
	    FileBrowserDirPUListMapCB, fb
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Directory popup list */
	fb->dir_pulist = pulist = PUListNew();

	/* Go To Parent */
	fb->goto_parent_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_folder_parent_20x20_xpm
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserGoToParentCB), fb
	);
#if 0
	gtk_accel_group_add(
	    accelgrp, GDK_BackSpace, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
#endif
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"El Padre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Le Parent"
#elif defined(PROG_LANGUAGE_GERMAN)
"Elternteil"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il Genitore"
#elif defined(PROG_LANGUAGE_DUTCH)
"Ouder"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Pai"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Mor Eller Far"
#else
"Parent"
#endif
	);
	gtk_widget_show(w);

	/* New Directory */
	fb->new_directory_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_folder_closed_20x20_xpm
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserNewDirectoryCB), fb
	);
#if 0
	gtk_accel_group_add(
	    accelgrp, GDK_Insert, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
#endif
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"La Guía Nueva"
#elif defined(PROG_LANGUAGE_FRENCH)
"Nouvel Annuaire"
#elif defined(PROG_LANGUAGE_GERMAN)
"Neues Verzeichnis"
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Elenco Nuovo"
#elif defined(PROG_LANGUAGE_DUTCH)
"Nieuwe Gids"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Novo Guia"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Ny Directory"
#else
"New Directory"
#endif
	);
	gtk_widget_show(w);

	/* Rename */
	fb->rename_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_rename_20x20_xpm
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserRenameCB), fb
	);
#if 0
	gtk_accel_group_add(
	    accelgrp, GDK_F2, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
#endif
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Reagrupe"
#elif defined(PROG_LANGUAGE_FRENCH)
"Renommer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Benennen Sie Um"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinominare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Herdoop"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mude Nome"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Ombenevn"
#else
"Rename"
#endif
	);
	gtk_widget_show(w);

	/* Refresh */
	fb->refresh_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_reload_20x20_xpm
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserRefreshCB), fb
	);
#if 0
	gtk_accel_group_add(
	    accelgrp, GDK_F5, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
#endif
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Refresque"
#elif defined(PROG_LANGUAGE_FRENCH)
"Rafraîchir"
#elif defined(PROG_LANGUAGE_GERMAN)
"Erfrischen Sie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinfrescare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verfris"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Refresque-se"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forfrisk"
#else
"Refresh"
#endif
	);
	gtk_widget_show(w);


	/* Hbox for list format buttons */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;

	fb->list_format_standard_tb = w = GUIToggleButtonPixmap(
	    (guint8 **)icon_fb_list_standard_20x20_xpm
	);
	gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "toggled",
	    GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Listar De Estándar"
#elif defined(PROG_LANGUAGE_FRENCH)
"Enumérer De Norme"
#elif defined(PROG_LANGUAGE_GERMAN)
"Standard Aufführen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Elencare Di Norma"
#elif defined(PROG_LANGUAGE_DUTCH)
"Standaard Opsommen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Alistamento De Padrão"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Normal Listing"
#else
"Standard Listing"
#endif
	);
	gtk_widget_show(w);

	fb->list_format_vertical_tb = w = GUIToggleButtonPixmap(
	    (guint8 **)icon_fb_list_vertical_20x20_xpm
	);
	gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "toggled",
	    GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Listar Detallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Enumérer Détaillé"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ausführliches Aufführen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Elencare Dettagliato"
#elif defined(PROG_LANGUAGE_DUTCH)
"Gedetailleerd Opsommen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Alistamento De Detailed"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Detaljert Listing"
#else
"Detailed Listing"
#endif
	);
/* Don't offer the use of the vertical list anymore */
/*	gtk_widget_show(w); */

	fb->list_format_vertical_details_tb = w = GUIToggleButtonPixmap(
	    (guint8 **)icon_fb_list_vertical_details_20x20_xpm
	);
	gtk_widget_set_usize(w, FB_TB_BTN_WIDTH, FB_TB_BTN_HEIGHT);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "toggled",
	    GTK_SIGNAL_FUNC(FileBrowserListFormatToggleCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Listar Detallado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Enumérer Détaillé"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ausführliches Aufführen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Elencare Dettagliato"
#elif defined(PROG_LANGUAGE_DUTCH)
"Gedetailleerd Opsommen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Alistamento De Detailed"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Detaljert Listing"
#else
"Detailed Listing"
#endif
	);
	gtk_widget_show(w);



	/* Table for list */
	w = gtk_table_new(2, 2, FALSE);
	gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Frame and vbox with in for parenting the list header and
	 * the list.
	 */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 0, 1,
	    GTK_FILL | GTK_SHRINK | GTK_EXPAND,
	    GTK_FILL | GTK_SHRINK | GTK_EXPAND,
	    0, 0
	);
	gtk_widget_show(w);
	parent3 = w;
	w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_show(w);
	parent3 = w;


	/* List header */
	fb->list_header_da = w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, FB_LIST_HEADER_WIDTH, FB_LIST_HEADER_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
	    GDK_FOCUS_CHANGE_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK |
	    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "configure_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_in_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_out_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(FileBrowserListHeaderEventCB), fb
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* List */
	fb->list_da = w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, FB_LIST_WIDTH, FB_LIST_HEIGHT);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
	gtk_widget_add_events(
	    w,
	    GDK_STRUCTURE_MASK | GDK_EXPOSURE_MASK |
	    GDK_FOCUS_CHANGE_MASK |
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "configure_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_in_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_out_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(FileBrowserListEventCB), fb
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	if(w != NULL)
	{
	    const GtkTargetEntry dnd_tar_types[] = {
		{"text/plain",          0,      0},
		{"text/uri-list",       0,      1},
		{"STRING",              0,      2}
	    };
	    const GtkTargetEntry dnd_src_types[] = {
		{"text/plain",		0,	0},
		{"text/uri-list",	0,	1},
		{"STRING",		0,	2}
	    };
	    GUIDNDSetTar(
		w,
		dnd_tar_types,
		sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
		GDK_ACTION_COPY,                /* Actions */
		GDK_ACTION_COPY,                /* Default action if same */
		GDK_ACTION_COPY,                /* Default action */
		FileBrowserDragDataReceivedCB,
		fb
	    );
	    gtk_signal_connect_after(
		GTK_OBJECT(w), "drag_motion",
		GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb
	    );
	    GUIDNDSetSrc(
		w,
		dnd_src_types,
		sizeof(dnd_src_types) / sizeof(GtkTargetEntry),
		GDK_ACTION_COPY | GDK_ACTION_MOVE |
		    GDK_ACTION_LINK,		/* Actions */
		GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,	/* Buttons */
		NULL,
		FileBrowserDragDataGetCB,
		FileBrowserDragDataDeleteCB,
		NULL,
		fb
	    );
	}

	/* List vertical scrollbar */
	adj = (GtkAdjustment *)gtk_adjustment_new(
	    0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
	);
	fb->list_vsb = w = gtk_vscrollbar_new(adj);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    1, 2, 0, 1,
	    0,
	    GTK_FILL | GTK_SHRINK | GTK_EXPAND,
	    0, 0
	);
	gtk_signal_connect(
	    GTK_OBJECT(adj), "value_changed",
	    GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb
	);

	/* List horizontal scrollbar */
	adj = (GtkAdjustment *)gtk_adjustment_new(
	    0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
	);
	fb->list_hsb = w = gtk_hscrollbar_new(adj);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 1, 2,
	    GTK_FILL | GTK_SHRINK | GTK_EXPAND,
	    0,
	    0, 0
	);
	gtk_signal_connect(
	    GTK_OBJECT(adj), "value_changed",
	    GTK_SIGNAL_FUNC(FileBrowserListScrollCB), fb
	);

	/* List right-click menu */
	if(TRUE)
	{
	    GtkWidget *menu = (GtkWidget *)GUIMenuCreate();
	    guint8 **icon;
	    const gchar *label;
	    guint accel_key, accel_mods;
	    gpointer mclient_data = fb;
	    void (*func_cb)(GtkWidget *w, gpointer);

#define DO_ADD_MENU_ITEM_LABEL	{		\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp,	\
  icon, label, accel_key, accel_mods, &w_ptr1,	\
  mclient_data, func_cb				\
 );						\
}
#define DO_ADD_MENU_ITEM_CHECK	{		\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_CHECK, accelgrp,	\
  icon, label, accel_key, accel_mods, &w_ptr1,	\
  mclient_data, func_cb				\
 );						\
}
#define DO_ADD_MENU_SEP	{			\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_SEPARATOR, NULL,	\
  NULL, NULL, 0, 0, NULL,			\
  NULL, NULL					\
 );						\
}

	    icon = (guint8 **)icon_select_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Selecto"
#elif defined(PROG_LANGUAGE_FRENCH)
"Choisir"
#elif defined(PROG_LANGUAGE_GERMAN)
"Erlesen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Scegliere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Uitgezocht"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Selecione"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Utvalgt"
#else
"Select"
#endif
	    ;
	    accel_key = GDK_Return;
	    accel_mods = 0;
	    func_cb = FileBrowserEntryEnterCB;
	    DO_ADD_MENU_ITEM_LABEL
/*	    fb->select_mi = w; */

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_folder_parent_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"El Padre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Le Parent"
#elif defined(PROG_LANGUAGE_GERMAN)
"Elternteil"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il Genitore"
#elif defined(PROG_LANGUAGE_DUTCH)
"Ouder"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Pai"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Mor Eller Far"
#else
"Parent"
#endif
	    ;
	    accel_key = GDK_BackSpace;
	    accel_mods = 0;
	    func_cb = FileBrowserGoToParentCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->goto_parent_mi = w; */

	    icon = (guint8 **)icon_reload_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Refresque"
#elif defined(PROG_LANGUAGE_FRENCH)
"Rafraîchir"
#elif defined(PROG_LANGUAGE_GERMAN)
"Erfrischen Sie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinfrescare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verfris"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Refresque-se"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forfrisk"
#else
"Refresh"
#endif
	    ;
	    accel_key = GDK_F5;
	    accel_mods = 0;
	    func_cb = FileBrowserRefreshCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->refresh_mi = w; */

	    DO_ADD_MENU_SEP

	    icon = (guint8 **)icon_folder_closed_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"La Guía Nueva"
#elif defined(PROG_LANGUAGE_FRENCH)
"Nouvel Annuaire"
#elif defined(PROG_LANGUAGE_GERMAN)
"Neues Verzeichnis"
#elif defined(PROG_LANGUAGE_ITALIAN)
"L'Elenco Nuovo"
#elif defined(PROG_LANGUAGE_DUTCH)
"Nieuwe Gids"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Novo Guia"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Ny Directory"
#else
"New Directory"
#endif
	    ;
	    accel_key = GDK_Insert;
	    accel_mods = 0;
	    func_cb = FileBrowserNewDirectoryCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->new_directory_mi = w; */

	    icon = (guint8 **)icon_rename_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Reagrupe"
#elif defined(PROG_LANGUAGE_FRENCH)
"Renommer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Benennen Sie Um"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinominare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Herdoop"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mude Nome"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Ombenevn"
#else
"Rename"
#endif
	    ;
	    accel_key = GDK_F2;
	    accel_mods = 0;
	    func_cb = FileBrowserRenameCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->rename_mi = w; */

	    icon = (guint8 **)icon_chmod_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Cambie Los Permisos"
#elif defined(PROG_LANGUAGE_FRENCH)
"Changer Des Permissions"
#elif defined(PROG_LANGUAGE_GERMAN)
"Ändern Sie Erlaubnis"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cambiare I Permessi"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verandeer Toestemmingen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mude Permissões"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forandr Permissions"
#else
"Change Permissions"
#endif
	    ;
	    accel_key = GDK_F9;
	    accel_mods = 0;
	    func_cb = FileBrowserCHModCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->chmod_mi = w; */

	    icon = (guint8 **)icon_cancel_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Borre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Effacer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Löschen Sie"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Cancellare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Schrap"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Anule"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Stryk"
#else
"Delete"
#endif
	    ;
	    accel_key = GDK_Delete;
	    accel_mods = 0;
	    func_cb = FileBrowserDeleteCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->delete_mi = w; */

	    DO_ADD_MENU_SEP

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Escoja Todo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Choisir Tout"
#elif defined(PROG_LANGUAGE_GERMAN)
"Wählen Sie Alle Aus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Scegliere Tutto"
#elif defined(PROG_LANGUAGE_DUTCH)
"Selecteer Alle"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Selecione Todo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Utvalgt All"
#else
"Select All"
#endif
	    ;
	    accel_key = 'a';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = FileBrowserSelectAllCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->select_all_mi = w; */

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Rechace Todo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Déssélectionner Tout"
#elif defined(PROG_LANGUAGE_GERMAN)
"Entmarkieren Sie Alle"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Deselezionare Tutto"
#elif defined(PROG_LANGUAGE_DUTCH)
"Heldere Keuze"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O Deselect Todo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Klar Selection"
#else
"Unselect All"
#endif
	    ;
	    accel_key = 'u';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = FileBrowserUnselectAllCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->unselect_all_mi = w; */

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Invierta La Selección"
#elif defined(PROG_LANGUAGE_FRENCH)
"Inverser La Sélection"
#elif defined(PROG_LANGUAGE_GERMAN)
"Invertieren Sie Auswahl"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Invertire La Selezione"
#elif defined(PROG_LANGUAGE_DUTCH)
"Keer Keuze Om"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Inverta Seleção"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Inverter Selection"
#else
"Invert Selection"
#endif
	    ;
	    accel_key = 'i';
	    accel_mods = GDK_CONTROL_MASK;
	    func_cb = FileBrowserInvertSelectionCB;
	    DO_ADD_MENU_ITEM_LABEL
/*          fb->invert_selection_mi = w; */




	    fb->list_menu = menu;

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_ITEM_CHECK
#undef DO_ADD_MENU_SEP
	}


	w = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Vbox for location entry and file type combo */
	w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent3 = w;

	/* File name entry */
	w = GUIPromptBar(
	    NULL,
#if defined(PROG_LANGUAGE_SPANISH)
"Archive Nombre:"
#elif defined(PROG_LANGUAGE_FRENCH)
"Nom De Fichier:"
#elif defined(PROG_LANGUAGE_GERMAN)
"Dateiname:"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Schedare Nome:"
#elif defined(PROG_LANGUAGE_DUTCH)
"Archiveer Naam:"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Arquive Nome:"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Arkiver Name:"
#else
"File Name:"
#endif
	    , &w_ptr1, &w_ptr2
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	fb->entry = w = GTK_WIDGET(w_ptr2);
	entry = GTK_ENTRY(w);
	gtk_signal_connect(
	    GTK_OBJECT(w), "activate",
	    GTK_SIGNAL_FUNC(FileBrowserEntryEnterCB), fb
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Entre el nombre del objeto, usted puede especificar más\
 que un objeto (separa cada nombre con un ',' el carácter)"
#elif defined(PROG_LANGUAGE_FRENCH)
"Entrer le nom de l'objet, vous pouvez spécifier plus\
 qu'un objet (sépare chaque nom avec un ',' le caractère)"
#elif defined(PROG_LANGUAGE_GERMAN)
"Tragen Sie den Namen des Objekts, Sie können angeben\
 mehr als ein Objekt ein (trennen Sie jeden Namen mit einem ',' charakter)"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Entrare il nome dell'oggetto, lei può specificare più\
 di un oggetto (separa ogni nome con un ',' il carattere)"
#elif defined(PROG_LANGUAGE_DUTCH)
"Ga de naam van het voorwerp, u zou kunnen specificeren\
 meer than een voorwerp binnen (scheid elk naam met een ',' teken)"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Entre o nome do objeto, você pode especificar mais de\
 um objeto (separa cada nome com um ',' caráter)"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Gå inn i navnet av objektet, De spesifiserer mere enn\
 et objekt (separere hver navn med et ',' karakter)"
#else
"Enter the name of the object, you may specify more than\
 one object (separate each name with a ',' character)"
#endif
	);
	if(w != NULL)
	{
	    const GtkTargetEntry dnd_tar_types[] = {
		{"text/plain",		0,	0},
		{"text/uri-list",	0,	1},
		{"STRING",		0,	2}
	    };
	    GUIDNDSetTar(
		w,
		dnd_tar_types,
		sizeof(dnd_tar_types) / sizeof(GtkTargetEntry),
		GDK_ACTION_COPY,		/* Actions */
		GDK_ACTION_COPY,		/* Default action if same */
		GDK_ACTION_COPY,		/* Default action */
		FileBrowserDragDataReceivedCB,
		fb
	    );
	    gtk_signal_connect_after(
		GTK_OBJECT(w), "drag_motion",
		GTK_SIGNAL_FUNC(FileBrowserDragmotionCB), fb
	    );
	}

	/* File types combo */
	glist = NULL;
	glist = g_list_append(glist, STRDUP(FB_DEFAULT_TYPE_STR));
	w = GUIComboCreate(
#if defined(PROG_LANGUAGE_SPANISH)
"Tipo Archivo:"
#elif defined(PROG_LANGUAGE_FRENCH)
"Type De Fichier:"
#elif defined(PROG_LANGUAGE_GERMAN)
"Legt Typ Ab:"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Tipo Di File:"
#elif defined(PROG_LANGUAGE_DUTCH)
"Archiveer Type:"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Arquivo Tipo:"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Typen Arkivet:"
#else
"File Type:"
#endif
	    , (const gchar *)glist->data,
	    glist,		/* Initial GList */
	    100,		/* Maximum items */
	    &w_ptr1,		/* GtkCombo return */
	    fb,
	    NULL,
	    FileBrowserTypeListChangeCB
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	g_list_foreach(glist, (GFunc)g_free, NULL);
	g_list_free(glist);
	combo = (GtkCombo *)w_ptr1;
	entry = GTK_ENTRY(combo->entry);
	fb->type_combo = GTK_WIDGET(combo);
	gtk_entry_set_editable(entry, FALSE);
	GUISetWidgetTip(
	    GTK_WIDGET(entry),
#if defined(PROG_LANGUAGE_SPANISH)
"Escoja el tipo del archivo que usted buscan"
#elif defined(PROG_LANGUAGE_FRENCH)
"Choisir le type de fichier que vous cherchez"
#elif defined(PROG_LANGUAGE_GERMAN)
"Wählen Sie die Art Akte, die Sie suchen aus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Scegliere il tipo di file che lei cercano"
#elif defined(PROG_LANGUAGE_DUTCH)
"Selecteer het soort dossier dat u voor kijkt"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Selecione o tipo de arquivo que você procurar"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Velg ut typen arkivet som De leter etter"
#else
"Select the type of file that you are looking for"
#endif
	);

	/* Vbox for ok and cancel buttons */
	w = gtk_vbox_new(TRUE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;


	/* OK button */
	fb->ok_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_ok_20x20_xpm, "OK", &fb->ok_btn_label
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);            
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF,
	    GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserOKCB), fb
	);
#if 0
	gtk_accel_group_add(
	    accelgrp, GDK_Return, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_3270_Enter, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_KP_Enter, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_ISO_Enter, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
#endif
	gtk_widget_show(w);

	/* Cancel button */
	fb->cancel_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_cancel_20x20_xpm, "Cancel", &fb->cancel_btn_label
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH_DEF,
	    GUI_BUTTON_HLABEL_HEIGHT_DEF
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FileBrowserCancelCB), fb
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_widget_show(w);


	/* Load icons, the order is important because the index must
	 * match FB_ICON_* values as indices.
	 */
	if(fb->toplevel != NULL)
	{
	    gint i = 0;
	    gpointer icon_list[] = FB_ICON_DATA_LIST;
	    while(icon_list[i] != NULL)
	    {
		FileBrowserIconAppend(
		    fb,
		    (guint8 **)icon_list[i + 1],	/* XPM data */
		    (const gchar *)icon_list[i + 0]	/* Description */
		);
		i += 6;
	    }
	}

	/* Force set of default list format */
	fb->list_format = -1;
	FileBrowserSetListFormat(fb, FB_LIST_FORMAT_STANDARD);

	return(0);
}

/*
 *	Sets the File Browser's style.
 */
void FileBrowserSetStyle(GtkRcStyle *rc_style)
{
	GtkWidget *w;
	FileBrowser *fb = &file_browser;

	w = fb->toplevel;
	if(w != NULL)
	{
	    if(rc_style != NULL)
	    {
		gtk_widget_modify_style(w, rc_style);
	    }
	    else
	    {
		rc_style = gtk_rc_style_new();
		gtk_widget_modify_style_recursive(w, rc_style);
		GTK_RC_STYLE_UNREF(rc_style)
	    }
	}
}

/*
 *	Sets the File Browser to be a transient for the given
 *	GtkWindow w.
 *
 *	If w is NULL then transient for will be unset.
 */
void FileBrowserSetTransientFor(GtkWidget *w)
{
	FileBrowser *fb = &file_browser;
	if(fb->toplevel != NULL)
	{
	    GtkWidget *toplevel = fb->toplevel;

	    if(w != NULL)
	    {
		if(!GTK_IS_WINDOW(w))
		    return;

/* Since the file browser itself has popup windows that may need to be
 * set modal, we cannot set the file browser itself modal.
		gtk_window_set_modal(
		    GTK_WINDOW(toplevel), TRUE
		);
 */
		gtk_window_set_transient_for(
		    GTK_WINDOW(toplevel), GTK_WINDOW(w)
		);
	    }
	    else
	    {
/*
		gtk_window_set_modal(
		    GTK_WINDOW(toplevel), FALSE
		);
 */
		gtk_window_set_transient_for(
		    GTK_WINDOW(toplevel), NULL
		);
	    }
	}
}

/*
 *      Returns TRUE if currently blocking for query.
 */
gboolean FileBrowserIsQuery(void) 
{
	FileBrowser *fb = &file_browser;

	if(fb->block_loop_level > 0)
	    return(TRUE);
	else
	    return(FALSE);
}

/*
 *      Ends query if any and returns a not available response.
 */
void FileBrowserBreakQuery(void)
{
	FileBrowser *fb = &file_browser;

	fb->user_response = FALSE;

	/* Break out of an additional blocking loops */
	while(fb->block_loop_level > 0)
	{
	    gtk_main_quit();
	    fb->block_loop_level--;
	}
	fb->block_loop_level = 0;
}


/*
 *	Clears the lists and unset the location.
 */
void FileBrowserReset(void)
{
	FileBrowser *fb = &file_browser;
	gint i;
	GtkEntry *entry;
	GtkCombo *combo;

#if 0
	/* Unset location */
	g_free(fb->cur_location);
	fb->cur_location = NULL;
#endif

	/* Unset file name */
	entry = (GtkEntry *)fb->entry;
	if(entry != NULL)
	    gtk_entry_set_text(entry, "");

	/* Clear dir popup list */
	PUListClear(fb->dir_pulist);

	/* Clear file types list */
	combo = (GtkCombo *)fb->type_combo;
	if(combo != NULL)
	{
	    entry = GTK_ENTRY(combo->entry);

	    fb->freeze_count++;

 	    gtk_entry_set_text(entry, "");
	    GUIComboSetList(GTK_WIDGET(combo), NULL);

	    fb->freeze_count--;
	}


	/* Clear objects listing */

	/* Unselect all */
	fb->focus_object = -1;
	g_list_free(fb->selection);
	fb->selection = fb->selection_end = NULL;

	/* Delete all objects */
	for(i = 0; i < fb->total_objects; i++)
	    FileBrowserObjectDestroyCB(fb->object[i]);
	g_free(fb->object);
	fb->object = NULL;
	fb->total_objects = 0;

	/* Reset objects per row */
	fb->objects_per_row = 0;
}

/*
 *	Maps the file browser and sets up the inital values.
 *
 *	Returns TRUE if a path was selected or FALSE if user canceled.
 *
 *	For most values that are set NULL, the value is left unchanged.
 *	All given values are coppied.
 *
 *	If type is set to NULL however, then the type list on the file
 *	browser will be left empty.
 *
 *	All returned pointer values should be considered statically
 *	allocated. The returned pointer for type_rtn may point to
 *	a structure in the input type list.
 */
gboolean FileBrowserGetResponse(
	const gchar *title,
	const gchar *ok_label, const gchar *cancel_label,
	const gchar *path,
	fb_type_struct **type, gint total_types,
	gchar ***path_rtn, gint *path_total_rtns,
	fb_type_struct **type_rtn
)
{
	GtkWidget *w, *toplevel;
	FileBrowser *fb = &file_browser;

#define RESET_RETURNS	{	\
 if(path_rtn != NULL)		\
  *path_rtn = NULL;		\
 if(path_total_rtns != NULL)	\
  *path_total_rtns = 0;		\
				\
 if(type_rtn != NULL)		\
  *type_rtn = NULL;		\
}

	/* Do not handle response if already waiting for a response,
	 * return with a not available response code
	 */
	if(fb->block_loop_level > 0)
	{
	    RESET_RETURNS
	    return(FALSE);
	}


	/* Reset values */
	fb->user_response = FALSE;

	g_free(fb->cur_type.name);
	g_free(fb->cur_type.ext);
	memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));


	/* Reset returns */
	RESET_RETURNS


	toplevel = fb->toplevel;

	/* Reget drive paths */
	strlistfree(fb->drive_path, fb->total_drive_paths);
	fb->drive_path = FileBrowserGetDrivePaths(&fb->total_drive_paths);

	/* Set title */
	if(title != NULL)
	    gtk_window_set_title(GTK_WINDOW(toplevel), title);

	/* Set OK Button label */
	w = fb->ok_btn_label;
	if((ok_label != NULL) && (w != NULL))
	    gtk_label_set_text(GTK_LABEL(w), ok_label);

	/* Set Cancel Button label */
	w = fb->cancel_btn_label;
	if((cancel_label != NULL) && (w != NULL))
	    gtk_label_set_text(GTK_LABEL(w), cancel_label);

	/* Set file types list if a new set of file types is given,
	 * otherwise we keep the current file types list (if any)
	 */
	w = fb->type_combo;
	if((type != NULL) && (total_types > 0) && (w != NULL))
	{
	    GtkCombo *combo = GTK_COMBO(w);
	    GtkEntry *entry = GTK_ENTRY(combo->entry);
	    GList *glist = NULL;
	    const gchar *first_type_s = NULL;
	    gint i;
	    gchar *s;
	    const fb_type_struct *t;

	    /* Freeze when changing file types so other things like
	     * the list do not get modified
	     */
	    fb->freeze_count++;

	    /* Iterate through all given file types and generate the
	     * list
	     */
	    for(i = 0; i < total_types; i++)
	    {
		t = type[i];
		if((t != NULL) ? (t->ext == NULL) : TRUE)
		    continue;

		/* Format list item string */
		if(!STRISEMPTY(t->name))
		    s = g_strdup_printf(
			"%s (%s)", t->name, t->ext
		    );
		else
		    s = STRDUP(t->ext);

		/* Add list item string to the list */
		glist = g_list_append(glist, s);

		/* Record first type which is considered the initially
		 * selected type
		 */
		if(i == 0)
		{
		    fb_type_struct *t_cur = &fb->cur_type;

		    first_type_s = s;

		    g_free(t_cur->name);
		    t_cur->name = STRDUP(t->name);
		    g_free(t_cur->ext);
		    t_cur->ext = STRDUP(t->ext);
		}
	    }

	    /* Set first file type string to the combo's entry */
	    if(first_type_s != NULL)
		gtk_entry_set_text(entry, first_type_s);

	    /* Set file types string list to file type combo */
	    GUIComboSetList(GTK_WIDGET(combo), glist);
	    glist = NULL;
	    first_type_s = NULL;

	    fb->freeze_count--;
	}

	FileBrowserSetBusy(fb, TRUE);

	/* Map File Browser
	 *
	 * This needs to be done now in order to allow the proper
	 * realizing of sizes before other things can be updated
	 */
	FileBrowserMap();

	while(gtk_events_pending() > 0)
	    gtk_main_iteration();

	/* Set initial path (if specified) */
	if(!STRISEMPTY(path))
	{
	    FileBrowserSetLocation(fb, path);
	}
	else
	{
	    /* No path was given, but we still need to check if the
	     * listings need to be updated in case they were
	     * reset/cleared
	     */
	    if(fb->total_objects <= 0)
	    {
		gchar *cur_path = STRDUP(FileBrowserGetLocation(fb));
		FileBrowserSetLocation(fb, cur_path);
		g_free(cur_path);
	    }
	}

	/* If no location was set then set the current location to be
	 * the home directory
	 */
	if(STRISEMPTY(fb->cur_location))
	    FileBrowserSetLocation(fb, "~");

	/* Focus first object as needed */
	if(fb->total_objects > 0)
	{
	    if(fb->focus_object < 0)
	    {
		fb->focus_object = 0;
		gtk_widget_queue_draw(fb->list_da);
	    }
	}

	FileBrowserSetBusy(fb, FALSE);

	/* Block until user response */
	fb->block_loop_level++;
	gtk_main();

	/* Unmap the File Browser just in case it was not unmapped from
	 * any of the callbacks
	 */
	FileBrowserUnmap();

	/* Break out of an additional blocking loops */
	while(fb->block_loop_level > 0)
	{
	    gtk_main_quit();
	    fb->block_loop_level--;
	}
	fb->block_loop_level = 0;


	/* Begin setting returns */

	/* Response path returns */
	if(path_rtn != NULL)
	    *path_rtn = fb->selected_path;
	if(path_total_rtns != NULL)
	    *path_total_rtns = fb->total_selected_paths;

	/* File type */
	if(type_rtn != NULL)
	    *type_rtn = &fb->cur_type;

	return(fb->user_response);
#undef RESET_RETURNS
}


/*
 *	Maps the File Browser.
 */
void FileBrowserMap(void)
{
	FileBrowser *fb = &file_browser;
	GtkWidget *w = fb->toplevel;

	/* Map toplevel */
	gtk_widget_show_raise(w);
	fb->map_state = TRUE;

	/* Grab focus and default for the list */
	w = fb->list_da;
	gtk_widget_grab_focus(w);
	gtk_widget_grab_default(w);
}

/*
 *	Unmaps the File Browser.
 */
void FileBrowserUnmap(void)
{
	FileBrowser *fb = &file_browser;
	GtkWidget *w = fb->toplevel;
	gtk_widget_hide(w);
	fb->map_state = FALSE;
}

/*
 *	Shuts down the File Browser.
 */
void FileBrowserShutdown(void)
{
	gint i;
	FileBrowser *fb = &file_browser;

	/* Begin deleting values */

	/* Current file type */
	g_free(fb->cur_type.name);
	g_free(fb->cur_type.ext);
	memset(&fb->cur_type, 0x00, sizeof(fb_type_struct));

	/* Selected paths */
	for(i = 0; i < fb->total_selected_paths; i++)
	    g_free(fb->selected_path[i]);
	g_free(fb->selected_path);
	fb->selected_path = NULL;
	fb->total_selected_paths = 0;

	/* Selectioned objects list */
	fb->focus_object = -1;
	g_list_free(fb->selection);
	fb->selection = fb->selection_end = NULL;

	/* Objects */
	for(i = 0; i < fb->total_objects; i++)
	    FileBrowserObjectDestroyCB(fb->object[i]);
	g_free(fb->object);
	fb->object = NULL;
	fb->total_objects = 0;
	fb->objects_per_row = 0;

	/* Icons */
	for(i = 0; i < fb->total_icons; i++)
	    FileBrowserIconDestroyCB(fb->icon[i]);
	g_free(fb->icon);
	fb->icon = NULL;
	fb->total_icons = 0;

	/* List columns */
	FileBrowserListColumnsClear(fb);

	/* Drive paths */
	strlistfree(fb->drive_path, fb->total_drive_paths);
	fb->drive_path = NULL;
	fb->total_drive_paths = 0;


	/* Break out of any additional blocking loops */
	while(fb->block_loop_level > 0)
	{
	    gtk_main_quit();
	    fb->block_loop_level--;
	}
	fb->block_loop_level = 0;


	/* Begin destroying widgets */

	PUListDelete(fb->dir_pulist);
	fb->dir_pulist = NULL;

	GTK_WIDGET_DESTROY(fb->list_menu);
	fb->list_menu = NULL;

	GTK_WIDGET_DESTROY(fb->dir_pulist_da);
	fb->dir_pulist_da = NULL;
	GTK_WIDGET_DESTROY(fb->dir_pulist_btn);
	fb->dir_pulist_btn = NULL;

	GTK_WIDGET_DESTROY(fb->goto_parent_btn);
	fb->goto_parent_btn = NULL;
	GTK_WIDGET_DESTROY(fb->new_directory_btn);
	fb->new_directory_btn = NULL;
	GTK_WIDGET_DESTROY(fb->rename_btn);
	fb->rename_btn = NULL;
	GTK_WIDGET_DESTROY(fb->refresh_btn);
	fb->refresh_btn = NULL;

	GTK_WIDGET_DESTROY(fb->list_format_standard_tb);
	fb->list_format_standard_tb = NULL;
	GTK_WIDGET_DESTROY(fb->list_format_vertical_tb);
	fb->list_format_vertical_tb = NULL;
	GTK_WIDGET_DESTROY(fb->list_format_vertical_details_tb);
	fb->list_format_vertical_details_tb = NULL;

	GTK_WIDGET_DESTROY(fb->list_header_da);
	fb->list_header_da = NULL;
	GTK_WIDGET_DESTROY(fb->list_da);
	fb->list_da = NULL;
	GTK_WIDGET_DESTROY(fb->list_vsb);
	fb->list_vsb = NULL;
	GTK_WIDGET_DESTROY(fb->list_hsb);
	fb->list_hsb = NULL;

	GTK_WIDGET_DESTROY(fb->entry);
	fb->entry = NULL;
	GTK_WIDGET_DESTROY(fb->type_combo);
	fb->type_combo = NULL;
	GTK_WIDGET_DESTROY(fb->ok_btn);
	fb->ok_btn = NULL;
	GTK_WIDGET_DESTROY(fb->cancel_btn);
	fb->cancel_btn = NULL;

	GTK_WIDGET_DESTROY(fb->main_vbox);
	fb->main_vbox = NULL;
	GTK_WIDGET_DESTROY(fb->toplevel);
	fb->toplevel = NULL;

	GDK_PIXMAP_UNREF(fb->list_pm)
	fb->list_pm = NULL;

	GDK_CURSOR_DESTROY(fb->cur_busy);
	fb->cur_busy = NULL;
	GDK_CURSOR_DESTROY(fb->cur_column_hresize);
	fb->cur_column_hresize = NULL;
	GDK_CURSOR_DESTROY(fb->cur_translate);
	fb->cur_translate = NULL;

	GTK_ACCEL_GROUP_UNREF(fb->accelgrp)
	fb->accelgrp = NULL;


	g_free(fb->home_path);
	fb->home_path = NULL;

	g_free(fb->cur_location);
	fb->cur_location = NULL;

	memset(fb, 0x00, sizeof(FileBrowser));
}


/*
 *	Convience function to allocate a new file browser file extension
 *	type structure and append it to the given list. The given list
 *	will be modified and the index number of the newly allocated
 *	structure will be returned.
 *
 *	Can return -1 on error.
 */
gint FileBrowserTypeListNew(
	fb_type_struct ***list, gint *total,
	const gchar *ext,	/* Space separated list of extensions */
	const gchar *name	/* Descriptive name */
)
{
	gint n;
	fb_type_struct *fb_type_ptr;


	if((list == NULL) || (total == NULL))
	    return(-1);

	n = MAX(*total, 0);
	*total = n + 1;

	/* Allocate more pointers */
	*list = (fb_type_struct **)g_realloc(
	    *list,
	    (*total) * sizeof(fb_type_struct *)
	);
	if(*list == NULL)
	{
	    *total = 0;
	    return(-1);
	}

	/* Allocate new structure */
	fb_type_ptr = (fb_type_struct *)g_malloc0(sizeof(fb_type_struct));
	(*list)[n] = fb_type_ptr;
	if(fb_type_ptr == NULL)
	{
	    *total = n;
	    return(-1);
	}

	/* Set values */
	fb_type_ptr->ext = STRDUP(ext);
	fb_type_ptr->name = STRDUP(name);

	return(n);
}

/*
 *	Deletes a dynamically allocated file browser file extensions
 *	type list.
 */
void FileBrowserDeleteTypeList(
	fb_type_struct **t, gint total 
)
{
	gint i;
	fb_type_struct *t_ptr;

	for(i = 0; i < total; i++)
	{
	    t_ptr = t[i];
	    if(t_ptr == NULL)
		continue;

	    g_free(t_ptr->ext);
	    g_free(t_ptr->name);
	    g_free(t_ptr);
	}
	g_free(t);
}
