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

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

#include "guiutils.h"
#include "cdialog.h"

#include "edvtypes.h"
#include "edvlistcb.h"
#include "cfg.h"
#include "edvdevices.h"
#include "deviceswin.h"
#include "endeavour.h"
#include "edvmount.h"
#include "edvcb.h"
#include "edvop.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "edvcfglist.h"
#include "config.h"

#include "images/icon_search_20x20.xpm"
#include "images/icon_add_20x20.xpm"
#include "images/icon_edit_20x20.xpm"
#include "images/icon_remove_20x20.xpm"
#include "images/icon_close_20x20.xpm"
#include "images/icon_mount_20x20.xpm"
#include "images/icon_unmount_20x20.xpm"
#include "images/icon_eject_20x20.xpm"
#include "images/icon_properties2_20x20.xpm"
#include "images/icon_fsck_20x20.xpm"
#include "images/icon_tools_20x20.xpm"
#include "images/icon_floppy_20x20.xpm"
#include "images/icon_device_misc_48x48.xpm"


static gint EDVDevicesListWinDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
static gint EDVDevicesListWinKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
);
static gint EDVDevicesListWinButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
static void EDVDevicesListWinSelectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
);
static void EDVDevicesListWinUnselectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
);
static void EDVDevicesListWinMountCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinEjectCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinPropertiesCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinFSCKCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinToolsCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinFormatCB(GtkWidget *widget, gpointer data);

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

static void EDVDevicesListWinAddCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinEditCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinRemoveCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinUpCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinDownCB(GtkWidget *widget, gpointer data);
static void EDVDevicesListWinCloseCB(GtkWidget *widget, gpointer data);

static void EDVDevicesListWinUpdateDisplay(
	edv_device_listwin_struct *lw, edv_device_struct *dev_ptr
);
void EDVDevicesListWinSetRow(
	edv_device_listwin_struct *lw, gint row, edv_device_struct *dev_ptr
);
void EDVDevicesListWinFetchValues(edv_device_listwin_struct *lw);

void EDVDevicesListWinReconfiguredNotifyCB(
	edv_device_listwin_struct *lw
);
void EDVDevicesListWinMountNotifyCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr,
	gboolean is_mounted
);
void EDVDevicesListWinDeviceAddedCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr
);
void EDVDevicesListWinDeviceModifiedCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr
);
void EDVDevicesListWinDeviceRemovedCB(
	edv_device_listwin_struct *lw, gint dev_num
);

edv_device_listwin_struct *EDVDevicesListWinNew(
	gpointer core_ptr
);
void EDVDevicesListWinUpdateMenus(
	edv_device_listwin_struct *lw
);
void EDVDevicesListSetBusy(
	edv_device_listwin_struct *lw, gboolean is_busy
);
gboolean EDVDevicesListWinIsMapped(
	edv_device_listwin_struct *lw
);
void EDVDevicesListWinMap(
	edv_device_listwin_struct *lw
);
void EDVDevicesListWinUnmap(
	edv_device_listwin_struct *lw
);
void EDVDevicesListWinDelete(
	edv_device_listwin_struct *lw
);


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


#if defined(PROG_LANGUAGE_SPANISH)
# define LISTWIN_TITLE	"Artefactos"
#elif defined(PROG_LANGUAGE_FRENCH)
# define LISTWIN_TITLE	"D'appareils"
#elif defined(PROG_LANGUAGE_GERMAN)
# define LISTWIN_TITLE	"Vorrichtungen"
#elif defined(PROG_LANGUAGE_ITALIAN)
# define LISTWIN_TITLE	"I Congegni"
#elif defined(PROG_LANGUAGE_DUTCH)
# define LISTWIN_TITLE	"Apparaten"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
# define LISTWIN_TITLE	"Os Artifícios"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
# define LISTWIN_TITLE	"Innretninger"
#else
# define LISTWIN_TITLE	"Devices"
#endif

#define LISTWIN_WIDTH		550
#define LISTWIN_HEIGHT		460


/*
 *	Devices List Window GtkWindow "delete_event" signal callback.
 */
static gint EDVDevicesListWinDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return(TRUE);

	EDVDevicesListWinCloseCB(NULL, lw);

	return(TRUE);
}

/*
 *	Devices List Window "key_press_event" or "key_release_event"
 *	signal callback.
 */
static gint EDVDevicesListWinKeyEventCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	guint keyval, state;
	gboolean press;
	edv_core_struct *core_ptr;
	GtkCList *clist;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if((widget == NULL) || (key == NULL) || (lw == NULL))
	    return(status);

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return(status);

	/* Get event type */
	etype = key->type;

	/* Get other key event values */
	press = (etype == GDK_KEY_PRESS) ? TRUE : FALSE;
	keyval = key->keyval;
	state = key->state;

/* Stop emit of signal */
#define DO_STOP_KEY_SIGNAL_EMIT	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}

	/* Begin checking which widget this event is for */

	/* Devices clist */
	if(widget == lw->devices_clist)
	{
	    gint row;
	    GList *glist;
	    GtkCList *clist = GTK_CLIST(widget);

	    /* Get last selected row */
	    row = EDVCListGetSelectedLast(clist, NULL);

	    /* Handle by key value */
	    switch(keyval)
	    {
	      case GDK_Return:
	      case GDK_KP_Enter:
	      case GDK_ISO_Enter:
	      case GDK_3270_Enter:
		if(press)
		{
		    EDVDevicesListWinEditCB(NULL, lw);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Delete:
		if(press)
		{
		    EDVDevicesListWinRemoveCB(NULL, lw);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_space:
	      case GDK_KP_Space:
		row = clist->focus_row;
		if((row >= 0) && (row < clist->rows) && press)
		{
		    gboolean already_selected = FALSE;

		    /* Check if this row is already selected */
		    glist = clist->selection;
		    while(glist != NULL)
		    {
			if(row == (gint)glist->data)
			{
			    already_selected = TRUE;
			    break;
			}
			glist = g_list_next(glist);
		    }

		    gtk_clist_freeze(clist);
		    if(already_selected)
			gtk_clist_unselect_row(clist, row, 0);
		    else
			gtk_clist_select_row(clist, row, 0);
		    gtk_clist_thaw(clist);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;
	    }
	}

	return(status);
#undef DO_STOP_KEY_SIGNAL_EMIT
}

/*
 *	Devices List Window "button_press_event" or 
 *	"button_release_event" signal callback.
 */
static gint EDVDevicesListWinButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	GtkCList *clist;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if((widget == NULL) || (button == NULL) || (lw == NULL))
	    return(status);

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return(status);

	cfg_list = core_ptr->cfg_list;

	if(clist->clist_window != ((GdkEventAny *)button)->window)
	    return(status);

	/* Get event type */
	etype = button->type;

	/* Begin checking which widget this event is for */

	/* Devices clist? */
	if(widget == lw->devices_clist)
	{
	    gint row, column;
	    GtkWidget *w;

	    /* Find row and column based on given coordinates */
	    if(!gtk_clist_get_selection_info(
		clist, button->x, button->y, &row, &column
	    ))
	    {
		row = -1;
		column = 0;
	    }

	    /* Handle by event type */
	    switch(etype)
	    {
	      case GDK_2BUTTON_PRESS:
		/* Handle by button number */
		switch(button->button)
		{
		  case 1:
		    EDVDevicesListWinEditCB(NULL, lw);
		    break;
		}
		status = TRUE;
		break;

	      case GDK_BUTTON_PRESS:
		/* Handle by button number */
		switch(button->button)
		{
		  case 3:
		    /* Select row before mapping menu? */
		    if(EDV_GET_B(EDV_CFG_PARM_RIGHT_CLICK_MENU_SELECTS) &&
		       (row >= 0) && (row < clist->rows)
		    )
		    {
			gtk_clist_freeze(clist);
			gtk_clist_select_row(clist, row, column);
			gtk_clist_thaw(clist);
		    }

		    /* Get right click menu widget and map it */
		    w = lw->menu;
		    if(w != NULL)
			gtk_menu_popup(
			    GTK_MENU(w), NULL, NULL,
			    NULL, NULL,
			    button->button, button->time
			);
		    break;
		}
		status = TRUE;
		break;
	    }
	}

	return(status);
}

/*
 *	Devices List Window GtkCList "select_row" signal callback.
 */
static void EDVDevicesListWinSelectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
)
{
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

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

	/* Check which clist this event is for */
	if(GTK_WIDGET(clist) == lw->devices_clist)
	{
	    /* Check if selected row is fully visible, if not then
	     * adjust scroll position to try and make it visible.
	     */
	    if(gtk_clist_row_is_visible(clist, row) !=
		GTK_VISIBILITY_FULL
	    )
		gtk_clist_moveto(
		    clist,
		    row, -1,	/* Row, column */
		    0.5, 0.0	/* Row, column */
		);

	    /* Update displayed device structure */
	    if((row >= 0) && (row < core_ptr->total_devices))
		EDVDevicesListWinUpdateDisplay(
		    lw,  core_ptr->device[row]
		);

	    EDVDevicesListWinUpdateMenus(lw);
	}
}

/*
 *	Devices List Window GtkCList "unselect_row" signal callback.
 */
static void EDVDevicesListWinUnselectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
)
{
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

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

	/* Check which clist this event is for */
	if(GTK_WIDGET(clist) == lw->devices_clist)
	{
	    EDVDevicesListWinUpdateDisplay(lw, NULL);
	    EDVDevicesListWinUpdateMenus(lw);
	}
}

/*
 *	Device List Window mount/unmunt callback.
 */
static void EDVDevicesListWinMountCB(GtkWidget *widget, gpointer data)
{
	gboolean original_mount_state;
	gint status, row, dev_num;
	GtkWidget *toplevel;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	toplevel = lw->toplevel;
	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	original_mount_state = dev_ptr->is_mounted;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Unmount or mount? */
	if(dev_ptr->is_mounted)
	    status = EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	else
	    status = EDVMountDoMount(core_ptr, dev_ptr, toplevel);

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

	EDVDevicesListSetBusy(lw, FALSE);

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

/* Warning, some widgets on this devices list may be invalid at this
 * point if fetched earlier in this function
 */
	}
}

/*
 *	Devices List Window eject callback.
 */
static void EDVDevicesListWinEjectCB(GtkWidget *widget, gpointer data)
{
	gboolean original_mount_state;
	gint status, row, dev_num;
	GtkWidget *toplevel;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	toplevel = lw->toplevel;
	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	original_mount_state = dev_ptr->is_mounted;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Need to unmount first? */
	if(dev_ptr->is_mounted)
	    EDVMountDoUnmount(core_ptr, dev_ptr, toplevel);
	/* So now eject */
	status = EDVMountDoEject(core_ptr, dev_ptr, toplevel);

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

	EDVDevicesListSetBusy(lw, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVMountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core_ptr);
		EDVMessageError(
		    "Eject Failed",
		    last_error,
		    NULL,
		    toplevel
		);
	    }
	}
	else
	{
	    /* Report eject (unmount) signal to all of endeavour's
	     * resources
	     */
	    EDVObjectMountEmit(core_ptr, dev_num, dev_ptr, dev_ptr->is_mounted);
	}
}

/*
 *	Device List Window properties callback.
 */
static void EDVDevicesListWinPropertiesCB(GtkWidget *widget, gpointer data)
{
	gint row, dev_num;
	const gchar *mount_path;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get mount path */
	mount_path = dev_ptr->mount_path;
	if(!STRISEMPTY(mount_path))
	{
	    struct stat lstat_buf;

	    /* Check if mount path exists locally */
	    if(!lstat(mount_path, &lstat_buf))
	    {
		edv_object_struct *obj = EDVObjectNew();
		if(obj != NULL)
		{
		    EDVObjectSetPath(obj, mount_path);
		    EDVObjectSetStat(obj, &lstat_buf);
		    EDVObjectValidateLink(obj);

		    /* Create a new Properties Dialog */
		    EDVNewPropertiesDialogPage(
			core_ptr,
			obj,		/* Object */
			"Device",	/* Page Name */
			lw->toplevel	/* Toplevel */
		    );
		}
		EDVObjectDelete(obj);
	    }
	}

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Device List Window fsck callback.
 */
static void EDVDevicesListWinFSCKCB(GtkWidget *widget, gpointer data)
{
	const gchar *cmd;
	gint row, dev_num;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get fsck command */
	cmd = dev_ptr->command_check;
	if(!STRISEMPTY(cmd))
	    Exec(cmd);

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Device List Window tools callback.
 */
static void EDVDevicesListWinToolsCB(GtkWidget *widget, gpointer data)
{
	const gchar *cmd;
	gint row, dev_num;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get tools command */
	cmd = dev_ptr->command_tools;
	if(!STRISEMPTY(cmd))
	    Exec(cmd);

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Device List Window format callback.
 */
static void EDVDevicesListWinFormatCB(GtkWidget *widget, gpointer data)
{
	const gchar *cmd;
	gint row, dev_num;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get selected row and device number index as the row index */
	dev_num = row = EDVCListGetSelectedLast(clist, NULL);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get format command */
	cmd = dev_ptr->command_format;
	if(!STRISEMPTY(cmd))
	    Exec(cmd);

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Device List Window find callback.
 */
static void EDVDevicesListWinFindCB(
	GtkWidget *widget, gpointer data
)
{
	gint find_itterations = 0;
	gint row, column, sel_row;
	const gchar *find_str;
	GtkEntry *entry;
	GtkCList *clist;

	gchar *cell_text;
	guint8 spacing;
	GdkPixmap *pixmap;
	GdkBitmap *mask;

	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	entry = (GtkEntry *)lw->find_entry;
	clist = (GtkCList *)lw->devices_clist;
	if((entry == NULL) || (clist == NULL))
	    return;

	/* Get string to match for from entry */
	find_str = gtk_entry_get_text(entry);
	if(STRISEMPTY(find_str))
	    return;

	/* Get selected row + 1 or 0 if none */
	sel_row = EDVCListGetSelectedLast(clist, NULL);
	if(sel_row < 0)
	    sel_row = 0;
	else
	    sel_row++;

	/* Iterate through rows, checking each Type column to see if
	 * there are any partial patches
	 */
	do
	{
	    /* Iterate from selected row to end */
	    for(row = sel_row; row < clist->rows; row++)
	    {
		/* Iterate through all cells on this row */
		for(column = 0; column < clist->columns; column++)
		{
		    cell_text = NULL;
		    switch(gtk_clist_get_cell_type(clist, row, column))
		    {
		      case GTK_CELL_TEXT:
			gtk_clist_get_text(clist, row, column, &cell_text);
			break;
		      case GTK_CELL_PIXTEXT:
			gtk_clist_get_pixtext(
			    clist, row, column, &cell_text,
			    &spacing, &pixmap, &mask
			);
			break;
		      case GTK_CELL_PIXMAP:
		      case GTK_CELL_WIDGET:
		      case GTK_CELL_EMPTY:
			break;
		    }
		    /* Got cell text? */
		    if(!STRISEMPTY(cell_text))
		    {
			/* Find string found inside cell text string? */
			if(strcasestr(cell_text, find_str))
			    break;
		    }
		}
		/* If column itteration broke before all columns were
		 * iterated through then that implies a match was made.
		 */
		if(column < clist->columns)
		    break;
	    }
	    /* Got match? */
	    if(row < clist->rows)
		break;
	    else
		find_itterations++;

	    /* Reset sel_row to 0 so that find starts at beginning */
	    sel_row = 0;

	} while(find_itterations < 2);

	/* Got match? */
	if(row < clist->rows)
	{
	    /* Select new matched row */
	    gtk_clist_select_row(clist, row, 0);
	}
}

/*
 *	Devices List Window add callback.
 */
static void EDVDevicesListWinAddCB(
	GtkWidget *widget, gpointer data
)
{
	gint row, dev_num;
	edv_device_struct *dev_ptr;
	GtkCList *clist;
	edv_core_struct *core_ptr;
	edv_device_editwin_struct *ew;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get last selected row */
	row = EDVCListGetSelectedLast(clist, NULL);

	/* Get device number index as the selected row's index */
	dev_num = row;

	/* Create a new device at the selected position specified by
	 * dev_num relative to the core structure
	 */
	if((dev_num < 0) || (dev_num >= core_ptr->total_devices))
	{
	    /* Append */

	    /* Allocate more pointers */
	    gint n = MAX(core_ptr->total_devices, 0);
	    core_ptr->total_devices = n + 1;
	    core_ptr->device = (edv_device_struct **)g_realloc(
		core_ptr->device,
		core_ptr->total_devices * sizeof(edv_device_struct *)
	    );
	    if(core_ptr->device == NULL)
	    {
		core_ptr->total_devices = 0;
		EDVDevicesListSetBusy(lw, FALSE);
		return;
	    }

	    dev_num = n;
	}
	else
	{
	    /* Insert */
	    gint i, n;

	    /* Allocate more pointers */
	    n = MAX(core_ptr->total_devices, 0);
	    core_ptr->total_devices = n + 1;
	    core_ptr->device = (edv_device_struct **)g_realloc(
		core_ptr->device,
		core_ptr->total_devices * sizeof(edv_device_struct *)
	    );
	    if(core_ptr->device == NULL)
	    {
		core_ptr->total_devices = 0;
		EDVDevicesListSetBusy(lw, FALSE);
		return;
	    }

	    /* Shift pointers */
	    for(i = n; i > dev_num; i--)
		core_ptr->device[i] = core_ptr->device[i - 1];
	}
	/* Create new device structure */
	core_ptr->device[dev_num] = dev_ptr = EDVDeviceNew(
	    EDV_FS_TYPE_EMPTY,
	    "New Device",
	    "/dev/null",
	    "/mnt"
	);


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


	/* Send device added signal to all of endeavour's resources */
	EDVDeviceAddedEmit(core_ptr, dev_num, dev_ptr);


	/* Map devices edit window, creating it as needed */
	ew = lw->editwin;
	if(ew == NULL)
	    lw->editwin = ew = EDVDevicesEditWinNew(
		core_ptr, lw
	    );
	if(ew != NULL)
	{
	    EDVCenterWindowToWindow(lw->toplevel, ew->toplevel);
	    EDVDevicesEditWinMap(ew);
	    EDVDevicesEditWinFetchValues(ew, dev_num);
	    EDVDevicesEditWinResetHasChanges(ew, FALSE);
	}

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Devices List Window edit callback.
 */
static void EDVDevicesListWinEditCB(
	GtkWidget *widget, gpointer data
)
{
	gint row, dev_num;
	GtkWidget *toplevel;
	GtkCList *clist;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	edv_device_editwin_struct *ew;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	toplevel = lw->toplevel;
	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get last selected row */
	row = EDVCListGetSelectedLast(clist, NULL);

	/* Get device number index as the selected row's index */
	dev_num = row;

	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	/* Check if this device is internally created by this program
	 * which means it cannot be edited
	 */
	if(dev_ptr->internal)
	{
	    EDVPlaySoundWarning(core_ptr);
	    EDVMessageWarning(
"Edit Failed",
"This device is created and managed internally by this\n\
program and may not be edited.",
		NULL,
		toplevel
	    );
	    return;
	}


	EDVDevicesListSetBusy(lw, TRUE);

	/* Map device edit window, creating it as needed */
	ew = lw->editwin;
	if(ew == NULL)
	    lw->editwin = ew = EDVDevicesEditWinNew(
		core_ptr, lw
	    );
	if(ew != NULL)
	{
	    EDVCenterWindowToWindow(lw->toplevel, ew->toplevel);
	    EDVDevicesEditWinMap(ew);
	    EDVDevicesEditWinFetchValues(ew, dev_num);
	    EDVDevicesEditWinResetHasChanges(ew, FALSE);
	}

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Devices List Window remove callback.
 */
static void EDVDevicesListWinRemoveCB(
	GtkWidget *widget, gpointer data
)
{
	gint row, dev_num;
	edv_device_struct *dev_ptr;
	GtkWidget *toplevel;
	GtkCList *clist;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	toplevel = lw->toplevel;
	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	/* Get last selected row */
	row = EDVCListGetSelectedLast(clist, NULL);

	/* Get device number index as the selected row's index */
	dev_num = row;

	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;
	if(dev_ptr == NULL)
	    return;

	/* Check if this device is internally allocated, in which case
	 * it cannot be removed
	 */
	if(dev_ptr->internal)
	{
	    EDVPlaySoundWarning(core_ptr);
	    EDVMessageWarning(
"Remove Failed",
"This device is created and managed internally by this\n\
program and may not be removed.",
		NULL,
		toplevel
	    );
	    return;
	}


	/* Confirm removal of device */
	if(TRUE)
	{
	    gint status;
	    gchar *buf = g_strdup_printf(
"Are you sure you want to remove the device \"%s\"?",
		dev_ptr->device_path
	    );

	    EDVPlaySoundQuestion(core_ptr);
	    CDialogSetTransientFor(toplevel);
	    status = CDialogGetResponse(
		"Confirm Remove",
		buf,
		NULL,
		CDIALOG_ICON_QUESTION,
		CDIALOG_BTNFLAG_YES | CDIALOG_BTNFLAG_NO,
		CDIALOG_BTNFLAG_NO
	    );
	    CDialogSetTransientFor(NULL);

	    g_free(buf);

	    switch(status)
	    {
	      case CDIALOG_RESPONSE_YES_TO_ALL:
	      case CDIALOG_RESPONSE_YES:
	      case CDIALOG_RESPONSE_OK:
		break;

	      default:
		return;
		break;
	    }
	}


	/* Begin deleting device from core structure's list of devices */

	EDVDevicesListSetBusy(lw, TRUE);

	/* Delete device structure */
	EDVDeviceDelete(core_ptr->device[dev_num]);
	core_ptr->device[dev_num] = dev_ptr = NULL;

	/* Reduce total */
	core_ptr->total_devices--;
	if(core_ptr->total_devices < 0)
	    core_ptr->total_devices = 0;

	/* Shift pointers and reallocate if there are still other devices
	 * in the list or else deallocate the list
	 */
	if(core_ptr->total_devices > 0)
	{
	    gint i;

	    /* Shift pointers */
	    for(i = dev_num; i < core_ptr->total_devices; i++)
		core_ptr->device[i] = core_ptr->device[i + 1];

	    /* Reduce pointer array allocation */
	    core_ptr->device = (edv_device_struct **)g_realloc(
		core_ptr->device,
		core_ptr->total_devices * sizeof(edv_device_struct *)
	    );
	    if(core_ptr->device == NULL)
	    {
		core_ptr->total_devices = 0;
	    }
	}
	else
	{
	    g_free(core_ptr->device);
	    core_ptr->device = NULL;
	}

	/* Send device removed signal to all of endeavour's resources */
	EDVDeviceRemovedEmit(core_ptr, dev_num);

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Devices List Window shift up callback.
 */
static void EDVDevicesListWinUpCB(
	GtkWidget *widget, gpointer data
)
{
	gint row;
	GtkCList *clist;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get last selected row */
	row = EDVCListGetSelectedLast(clist, NULL);

	/* Can shift selected row up? */
	if((row > 0) && (row < core_ptr->total_devices) &&
	   (core_ptr->total_devices == clist->rows)
	)
	{
	    edv_device_struct *tmp_dev_ptr;

	    /* Shift clist rows */
	    gtk_clist_freeze(clist);
	    gtk_clist_swap_rows(clist, row, row - 1);
	    gtk_clist_thaw(clist);

	    /* Shift device structure pointers on the core structure's
	     * devices list.
	     */
	    tmp_dev_ptr = core_ptr->device[row - 1];
	    core_ptr->device[row - 1] = core_ptr->device[row];
	    core_ptr->device[row] = tmp_dev_ptr;

	    /* Emit device modified signal for both devices involved in
	     * shift.
	     */
	    EDVDeviceModifiedEmit(
		core_ptr,
		row - 1,
		core_ptr->device[row - 1]
	    );
	    EDVDeviceModifiedEmit(
		core_ptr,
		row,
		core_ptr->device[row]
	    );

	    EDVDevicesListWinUpdateMenus(lw);
	}

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Devices List Window shift down callback.
 */
static void EDVDevicesListWinDownCB(
	GtkWidget *widget, gpointer data
)
{
	gint row;
	GtkCList *clist;
	edv_core_struct *core_ptr;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	EDVDevicesListSetBusy(lw, TRUE);

	/* Get last selected row */
	row = EDVCListGetSelectedLast(clist, NULL);

	/* Can shift selected row down? */
	if((row > -1) && (row < (clist->rows - 1)) &&
	   (core_ptr->total_devices == clist->rows)
	)
	{
	    edv_device_struct *tmp_dev_ptr;

	    /* Shift clist rows */
	    gtk_clist_freeze(clist);
	    gtk_clist_swap_rows(clist, row, row + 1);
	    gtk_clist_thaw(clist);

	    /* Shift device structure pointers on the core structure's
	     * devices list.
	     */
	    tmp_dev_ptr = core_ptr->device[row + 1];
	    core_ptr->device[row + 1] = core_ptr->device[row];
	    core_ptr->device[row] = tmp_dev_ptr;

	    /* Emit device modified signal for both devices involved in
	     * shift.
	     */
	    EDVDeviceModifiedEmit(
		core_ptr,
		row + 1,
		core_ptr->device[row + 1]
	    );
	    EDVDeviceModifiedEmit(
		core_ptr,
		row,
		core_ptr->device[row]
	    );

	    EDVDevicesListWinUpdateMenus(lw);
	}

	EDVDevicesListSetBusy(lw, FALSE);
}

/*
 *	Device list window close button callback.
 */
static void EDVDevicesListWinCloseCB(
	GtkWidget *widget, gpointer data
)
{
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(data);
	if(lw == NULL)
	    return;

	EDVDevicesListWinUnmap(lw);
}



/*
 *	Updates the displayed device on the given list window.
 *
 *	This will destroy the display_client widget and create a
 *	new one if dev_ptr is not NULL.
 */
static void EDVDevicesListWinUpdateDisplay(
	edv_device_listwin_struct *lw, edv_device_struct *dev_ptr
)
{
	const gint	border_major = 5,
			border_minor = 2;
	GtkRcStyle *rcstyle, *standard_rcstyle;
	GtkWidget *w, *parent, *parent2, *parent3, *parent4;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core_ptr;

	if(lw == NULL)
	    return;

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

	cfg_list = core_ptr->cfg_list;
	standard_rcstyle = core_ptr->standard_rcstyle;

	/* Destroy display widgets */
	if(lw->display_client != NULL)
	{
	    GTK_WIDGET_DESTROY(lw->dev_icon_pm)
	    lw->dev_icon_pm = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_name_label)
	    lw->dev_name_label = NULL;

	    GTK_WIDGET_DESTROY(lw->dev_device_path_label)
	    lw->dev_device_path_label = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_mount_path_label)
	    lw->dev_mount_path_label = NULL;

	    GTK_WIDGET_DESTROY(lw->dev_mount_btn)
	    lw->dev_mount_btn = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_eject_btn)
	    lw->dev_eject_btn = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_properties_btn)
	    lw->dev_properties_btn = NULL;

	    GTK_WIDGET_DESTROY(lw->dev_fsck_btn)
	    lw->dev_fsck_btn = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_tools_btn)
	    lw->dev_tools_btn = NULL;
	    GTK_WIDGET_DESTROY(lw->dev_format_btn)
	    lw->dev_format_btn = NULL;

	    PieChartDelete(lw->dev_pc);
	    lw->dev_pc = NULL;

	    GTK_WIDGET_DESTROY(lw->display_client)
	    lw->display_client = NULL;
	}

	/* If device is not available then do not recreate the
	 * display widgets
	 */
	if(dev_ptr == NULL)
	    return;

	/* Realize device as needed */
	EDVDeviceRealize(dev_ptr, FALSE);


	/* Get parent to create display client widget on */
	parent = lw->display_parent;
	if(parent == NULL)
	    return;

	/* Create new client hbox for multiple columns */
	lw->display_client = w = gtk_hbox_new(FALSE, border_major);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;


	/* Left column vbox */
	w = gtk_vbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent3 = w;


	/* HBox for icon and name line */
	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Create icon? */
	if(TRUE)
	{
	    GdkPixmap *pixmap;
	    GdkBitmap *mask;

#if 0
	    /* First try to get large sized pixmap, use standard state
	     * always
	     */
	    pixmap = dev_ptr->large_pixmap[EDV_DEVICE_ICON_STATE_STANDARD];
	    mask = dev_ptr->large_mask[EDV_DEVICE_ICON_STATE_STANDARD];
	    /* No large? Then try medium */
	    if(pixmap == NULL)
#endif
	    if(TRUE)
	    {
		pixmap = dev_ptr->medium_pixmap[EDV_DEVICE_ICON_STATE_STANDARD];
		mask = dev_ptr->medium_mask[EDV_DEVICE_ICON_STATE_STANDARD];
	    }
	    /* No medium? Then try small */
	    if(pixmap == NULL)
	    {
		pixmap = dev_ptr->small_pixmap[EDV_DEVICE_ICON_STATE_STANDARD];
		mask = dev_ptr->small_mask[EDV_DEVICE_ICON_STATE_STANDARD];
	    }

	    /* Got pixmap and mask pair? */
	    if(pixmap != NULL)
	    {
		lw->dev_icon_pm = w = gtk_pixmap_new(pixmap, mask);
		gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
		gtk_widget_show(w);
	    }
	}
	/* Name label (or device path if name is empty) */
	if(!STRISEMPTY(dev_ptr->name))
	{
	    lw->dev_name_label = w = gtk_label_new(dev_ptr->name);
	}
	else
	{
	    const gchar	*dev_path = dev_ptr->device_path,
			*dev_name = EDVGetPathName(dev_path);
	    if(dev_name == NULL)
		dev_name = "(null)";
	    lw->dev_name_label = w = gtk_label_new(dev_name);
	}
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	rcstyle = gtk_rc_style_new();
	rcstyle->font_name = STRDUP(
"-adobe-helvetica-bold-r-normal-*-14-*-*-*-*-*-iso8859-1"
	);
	gtk_widget_modify_style(w, rcstyle);
	gtk_widget_show(w);
	GTK_RC_STYLE_UNREF(rcstyle);


	/* HBox for device path line */
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Device path label */
	rcstyle = gtk_rc_style_new();
	rcstyle->font_name = STRDUP(
"-adobe-helvetica-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"
	);
	w = gtk_label_new(
#if defined(PROG_LANGUAGE_SPANISH)
"El Sendero Del Artefacto: "
#elif defined(PROG_LANGUAGE_FRENCH)
"Sentier D'appareil: "
#else
"Device Path: "
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_modify_style(w, rcstyle);
	gtk_widget_show(w);
	GTK_RC_STYLE_UNREF(rcstyle);

	/* Device path */
	if(!STRISEMPTY(dev_ptr->device_path))
	{
	    rcstyle = gtk_rc_style_new();
	    rcstyle->font_name = STRDUP(
"-adobe-courier-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"
	    );
	    lw->dev_device_path_label = w = gtk_label_new(
		dev_ptr->device_path
	    );
	    gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	    gtk_widget_modify_style(w, rcstyle);
	    gtk_widget_show(w);
	    GTK_RC_STYLE_UNREF(rcstyle);
	}


	/* HBox for mount path line */
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Mount path label */
	rcstyle = gtk_rc_style_new();
	rcstyle->font_name = STRDUP(
"-adobe-helvetica-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"
	);
	w = gtk_label_new(
#if defined(PROG_LANGUAGE_SPANISH)
"Monte Sendero: "
#elif defined(PROG_LANGUAGE_FRENCH)
"Monter Le Sentier: "
#else
"Mount Path: "
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_modify_style(w, rcstyle);
	gtk_widget_show(w);
	GTK_RC_STYLE_UNREF(rcstyle);

	/* Mount path */
	if(!STRISEMPTY(dev_ptr->mount_path))
	{
	    rcstyle = gtk_rc_style_new();
	    rcstyle->font_name = STRDUP(
"-adobe-courier-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"
	    );
	    lw->dev_mount_path_label = w = gtk_label_new(
		dev_ptr->mount_path
	    );
	    gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	    gtk_widget_modify_style(w, rcstyle);
	    gtk_widget_show(w);
	    GTK_RC_STYLE_UNREF(rcstyle);
	}

	/* HBox for mount, eject, and properties buttons */
	w = gtk_hbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Mount/unmount button */
	lw->dev_mount_btn = w = GUIButtonPixmap(
	    (guint8 **)(dev_ptr->is_mounted ?
		icon_unmount_20x20_xpm : icon_mount_20x20_xpm
	    )
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinMountCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Monte o de dispositivo de monte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Monter ou de l'appareil de mont"
#elif defined(PROG_LANGUAGE_GERMAN)
"Untersatz oder ab untersatz vorrichtung"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Montare o via dal congegno di monte"
#elif defined(PROG_LANGUAGE_DUTCH)
"Berg of van berg apparaat"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Monte ou fora monte artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Fotstykke eller av fotstykkeinnretning"
#else
"Mount/unmount device"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(w, !dev_ptr->no_unmount)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);

	/* Eject button */
	lw->dev_eject_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_eject_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinEjectCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Expulse medios del dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Ejecter la presse de l'appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Werfen sie medien von der vorrichtung aus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Espellere la stampa dal congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Stoot media van het apparaat uit"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Expulse imprensa do artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kast ut medier fra innretningen"
#else
"Eject media from the device"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(
	    w,
	    !STRISEMPTY(dev_ptr->command_eject) ? TRUE : FALSE
	)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);

	/* Properties button */
	lw->dev_properties_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_properties2_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinPropertiesCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Modifique las propiedades de sendero de monte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Modifier les propriétés de sentier de mont"
#elif defined(PROG_LANGUAGE_GERMAN)
"Modifizieren sie untersatz pfad eigentümer"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Modificare le proprietì di sentiero di monte"
#elif defined(PROG_LANGUAGE_DUTCH)
"Wijziig berg pad eigendommen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Modifique propriedades de caminho de monte"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Modifiser fotstykkestieiendomer"
#else
"Modify mount path properties"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(
	    w,
	    !STRISEMPTY(dev_ptr->mount_path) ? TRUE : FALSE
	)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);

	/* FSCK button */
	lw->dev_fsck_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_fsck_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinFSCKCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Verifique el sistema del archivo de dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Vérifier le système de classement de l'appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Prüfen sie das dateisystem der vorrichtung"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Controllare il sistema di file del congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Controleer het dossier van het apparaat systeem"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Verifique o sistema de arquivo do artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Sjekk innretningens arkivsystem"
#else
"Check the device's file system"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(
	    w,
	    !STRISEMPTY(dev_ptr->command_check) ? TRUE : FALSE
	)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);

	/* Tools button */
	lw->dev_tools_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_tools_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinToolsCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Corra el programa de instrumentos de dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Courir le programme d'outils de l'appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Laufen sie die werkzeuge der vorrichtung programmieren"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Ha correto il programma di attrezzi del congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Loop de werktuigen van het apparaat programma"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Corra o programa de ferramentas do artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kjør innretningens redskapprogram"
#else
"Run the device's tools program"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(
	    w,
	    !STRISEMPTY(dev_ptr->command_tools) ? TRUE : FALSE
	)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);

	/* Format button */
	lw->dev_format_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_floppy_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinFormatCB), lw
	);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"Formatear los medios en el dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Met en format la presse dans l'appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Formatieren sie die medien in der vorrichtung"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Il formato la stampa nel congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Formatteer de media in het apparaat"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O formato a imprensa no artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Formater mediene i innretningen"
#else
"Format the media in the device"
#endif
	);
	GTK_WIDGET_SET_SENSITIVE(
	    w,
	    !STRISEMPTY(dev_ptr->command_format) ? TRUE : FALSE
	)
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	gtk_widget_show(w);


	/* Right column vbox */
	w = gtk_vbox_new(TRUE, border_major);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;

	/* Create the Usage Pie Chart if mounted */
	if(dev_ptr->is_mounted)
	{
	    const gulong	adj_division = 10l,
				total_kb = dev_ptr->blocks_total,
				free_kb = dev_ptr->blocks_free,
				available_kb = dev_ptr->blocks_available,
				used_kb = MAX(total_kb - free_kb, 0l);
	    gchar *ss_kb, *ss_mb, *text, *text2;
	    GdkColor c;
	    GtkAdjustment *adj;
	    pie_chart_struct *pc;

	    adj = (GtkAdjustment *)gtk_adjustment_new(
	        0.0f, 0.0f,
	        (gfloat)(total_kb / adj_division),
	        0.0f, 0.0f, 0.0f
	    );
	    GDK_COLOR_SET_COEFF(&c, 0.0f, 0.0f, 1.0f)
	    ss_kb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, total_kb)
	    );
	    ss_mb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, total_kb / 1000l)
	    );
	    text = g_strdup_printf(
	        "%s: %s kb (%s mb)",
#if defined(PROG_LANGUAGE_SPANISH)
"La Capacidad Total"
#elif defined(PROG_LANGUAGE_FRENCH)
"Capacite' Totale"
#elif defined(PROG_LANGUAGE_GERMAN)
"Gesamte Kapazität"
#elif defined(PROG_LANGUAGE_ITALIAN)
"La Capacità Totale"
#elif defined(PROG_LANGUAGE_DUTCH)
"Totale Capaciteit"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Capacidade Total"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Total Kapasitet"
#else
"Total Capacity"
#endif
		, ss_kb, ss_mb
	    );
	    g_free(ss_kb);
	    g_free(ss_mb);

	    ss_kb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, dev_ptr->blocks_free)
	    );
	    ss_mb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, dev_ptr->blocks_free / 1000)
	    );
	    text2 = g_strdup_printf(
	        "%s kb (%s mb)",
		ss_kb, ss_mb
	    );
	    g_free(ss_kb);
	    g_free(ss_mb);

	    lw->dev_pc = pc = PieChartNew(
	        adj, &c, 110, 70,
	        NULL, text,
#if defined(PROG_LANGUAGE_SPANISH)
"Libre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Libre"
#elif defined(PROG_LANGUAGE_GERMAN)
"Frei"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Libero"
#elif defined(PROG_LANGUAGE_DUTCH)
"Vrij"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Livre"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Fri"
#else
"Free"
#endif
		":", text2
	    );
	    GTK_OBJECT_UNREF(adj);
	    g_free(text);
	    g_free(text2);
	    w = pc->toplevel;
	    gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
	    gtk_widget_show(w);

	    GDK_COLOR_SET_COEFF(&c, 0.0f, 1.0f, 1.0f)
	    adj = (GtkAdjustment *)gtk_adjustment_new(
	        0.0f, 0.0f,
	        (gfloat)(available_kb / adj_division),
	        0.0f, 0.0f, 0.0f
	    );
	    ss_kb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, available_kb)
	    );
	    ss_mb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, available_kb / 1000l)
	    );
	    text = g_strdup_printf(
	        "%s kb (%s mb)",
		ss_kb, ss_mb
	    );
	    g_free(ss_kb);
	    g_free(ss_mb);
	    PieChartValueAdd(
		pc, adj, &c,
#if defined(PROG_LANGUAGE_SPANISH)
"Libre & Disponible"
#elif defined(PROG_LANGUAGE_FRENCH)
"Libre & Disponible"
#elif defined(PROG_LANGUAGE_GERMAN)
"Frei & Verfügbar"
#elif defined(PROG_LANGUAGE_ITALIAN)
"ibero & Disponibile"
#elif defined(PROG_LANGUAGE_DUTCH)
"Vrij & Verkrijgbare"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Livre & Disponível"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Fri & Tilgjengelig"
#else
"Free & Available"
#endif
		":", text
	    );
	    GTK_OBJECT_UNREF(adj);
	    g_free(text);

	    GDK_COLOR_SET_COEFF(&c, 1.0f, 0.0f, 1.0f);
	    adj = (GtkAdjustment *)gtk_adjustment_new(
	        0.0f, 0.0f,
	        (gfloat)(used_kb / adj_division),
	        0.0f, 0.0f, 0.0f
	    );
	    ss_kb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, used_kb)
	    );
	    ss_mb = STRDUP(
		EDVGetObjectSizeStr(core_ptr, used_kb / 1000l)
	    );
	    text = g_strdup_printf(
	        "%s kb (%s mb)",
		ss_kb, ss_mb
	    );
	    g_free(ss_kb);   
	    g_free(ss_mb);
 	    PieChartValueAdd(
	        pc, adj, &c,
#if defined(PROG_LANGUAGE_SPANISH)
"Utilizado"
#elif defined(PROG_LANGUAGE_FRENCH)
"Utilise'"
#elif defined(PROG_LANGUAGE_GERMAN)
"Benutzt"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Usato"
#elif defined(PROG_LANGUAGE_DUTCH)
"Gebruikt"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Usado"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Brukt"
#else
"Used"
#endif
		":", text
	    );
	    GTK_OBJECT_UNREF(adj);
	    g_free(text);

	    if(standard_rcstyle != NULL)
		gtk_widget_modify_style_recursive(
		    pc->toplevel, standard_rcstyle
		);
	}
	else
	{
	    w = gtk_label_new("(Device Not Mounted)");
	    gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, FALSE, 0);
	    gtk_widget_show(w);
	}
}


/*
 *	Sets the row on the device clist on the given list window to the
 *	values specified by the given device structure.
 */
void EDVDevicesListWinSetRow(
	edv_device_listwin_struct *lw, gint row, edv_device_struct *dev_ptr
)
{
	GtkCList *clist;
	edv_core_struct *core_ptr;

	GdkPixmap *pixmap = NULL;
	GdkBitmap *mask = NULL;


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

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	/* Realize device structure so we can have use of its
	 * icon pixmap and mask pairs.
	 */
	EDVDeviceRealize(dev_ptr, FALSE);


	/* Get small icon pixmap and mask pair for this row */
	pixmap = dev_ptr->small_pixmap[
	    EDV_DEVICE_ICON_STATE_STANDARD
	];
	mask = dev_ptr->small_mask[
	    EDV_DEVICE_ICON_STATE_STANDARD
	];


	/* Given row index in bounds? */
	if((row >= 0) && (row < clist->rows))
	{
	    /* Set column 0 as name (or device path if name is not given) */
	    if(clist->columns > 0)
	    {
		const gchar *cell_text = dev_ptr->name;
		if((cell_text == NULL) && (dev_ptr->device_path != NULL))
		    cell_text = EDVGetPathName(dev_ptr->device_path);
		if(cell_text == NULL)
		    cell_text = "(null)";

		if(pixmap != NULL)
		    gtk_clist_set_pixtext(
			clist, row, 0, cell_text,
			EDV_LIST_PIXMAP_TEXT_SPACING,
			pixmap, mask
		    );
		else
		    gtk_clist_set_text(
			clist, row, 0, cell_text
		    );
	    }
	    /* Set column 1 as device path */
	    if(clist->columns > 1)
	    {
		const gchar *cell_text = dev_ptr->device_path;
		if(cell_text == NULL)
		    cell_text = "(null)";

		gtk_clist_set_text(
		    clist, row, 1, cell_text
		);
	    }
	    /* Set column 2 as mount path */
	    if(clist->columns > 2)
	    {
		const gchar *cell_text = dev_ptr->mount_path;
		if(cell_text == NULL)
		    cell_text = "(null)";

		gtk_clist_set_text(
		    clist, row, 2, cell_text
		);
	    }
	    /* Set column 3 as fs type */
	    if(clist->columns > 3)
	    {
		gtk_clist_set_text(
		    clist, row, 3,
		    EDVDeviceGetFSStringFromNumber(dev_ptr->fs_type)
		);
	    }

	}
}

/*
 *	Regets the Devices List Window's list of devices from the core
 *	structure.
 */
void EDVDevicesListWinFetchValues(edv_device_listwin_struct *lw)
{
	gint i, row;
	gchar **strv;
	GtkCList *clist;
	edv_core_struct *core_ptr;
	edv_device_struct *dev_ptr;


	if(lw == NULL)
	    return;

	clist = (GtkCList *)lw->devices_clist;
	core_ptr = EDV_CORE(lw->core_ptr);
	if((clist == NULL) || (core_ptr == NULL))
	    return;

	if(clist->columns < 1)
	    return;

	/* Allocate clist row cell values */
	strv = (gchar **)g_malloc(clist->columns * sizeof(gchar *));
	for(i = 0; i < clist->columns; i++)
	    strv[i] = "";


	/* Begin updating */

	gtk_clist_freeze(clist);

	/* Remove existing rows on clist and clear display */
	gtk_clist_clear(clist);
	EDVDevicesListWinUpdateDisplay(lw, NULL);

	/* Iterate through all devices on the core structure */
	for(i = 0; i < core_ptr->total_devices; i++)
	{
	    dev_ptr = core_ptr->device[i];
	    if(dev_ptr == NULL)
		continue;

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

	    /* Set new clist row values with the current device 
	     * structure's values.
	     */
	    EDVDevicesListWinSetRow(lw, row, dev_ptr);
	}

	gtk_clist_thaw(clist);

	g_free(strv);

	EDVDevicesListWinUpdateMenus(lw);
}


/*
 *	Reconfigured notify callback.
 */
void EDVDevicesListWinReconfiguredNotifyCB(
	edv_device_listwin_struct *lw
)
{
	GtkRcStyle *standard_rcstyle, *lists_rcstyle;
	GtkWidget *w;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core_ptr;

	if(lw == NULL)
	    return;

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

	cfg_list = core_ptr->cfg_list;
	standard_rcstyle = core_ptr->standard_rcstyle;
	lists_rcstyle = core_ptr->lists_rcstyle;

	/* Update RC styles */
	w = lw->toplevel;
	if((w != NULL) && (standard_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	w = lw->devices_clist;
	if((w != NULL) && (lists_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, lists_rcstyle);

	EDVDevicesListWinUpdateMenus(lw);
}

/*
 *      Called whenever a device has been mounted or unmounted.
 */
void EDVDevicesListWinMountNotifyCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr,
	gboolean is_mounted
)
{
	/* Just forward this to the device modified callback */
	EDVDevicesListWinDeviceModifiedCB(
	    lw, dev_num, dev_ptr
	);
}

/*
 *	Called whenever a new device has been added.
 */
void EDVDevicesListWinDeviceAddedCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr
)
{
	gint row, new_row;
	gchar **strv;
	GtkCList *clist;
	edv_core_struct *core_ptr;


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

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	/* The row to create a new device entry on should correspond to
	 * the given device index number since the clist should be synced
	 * with the devices list.
	 */
	row = dev_num;

	/* Insert a new row on the clist to indicate this new device
	 * structure.
	 */
	strv = (gchar **)g_malloc(clist->columns * sizeof(gchar *));
	if(strv != NULL)
	{
	    gint i;
	    for(i = 0; i < clist->columns; i++)
		strv[i] = g_strdup("");
	}
	if(row >= clist->rows)
	    new_row = gtk_clist_append(clist, strv);
	else
	    new_row = gtk_clist_insert(clist, row, strv);
	if(strv != NULL)
	{
	    gint i;
	    for(i = 0; i < clist->columns; i++)
		g_free(strv[i]);
	    g_free(strv);
	    strv = NULL;
	}
	/* Failed to insert new row on clist? */
	if(new_row < 0)
	    return;

	/* Update clist row text with values from the new device */
	EDVDevicesListWinSetRow(lw, new_row, dev_ptr);

	EDVDevicesListWinUpdateMenus(lw);

	/* Select new row on clist */
	gtk_clist_select_row(clist, new_row, 0);
}

/*
 *	Notifies the given Devices List Window that the given device
 *	has been modified.
 */
void EDVDevicesListWinDeviceModifiedCB(
	edv_device_listwin_struct *lw,
	gint dev_num, edv_device_struct *dev_ptr
)
{
	GtkCList *clist;
	edv_core_struct *core_ptr;


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

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	/* Check if given device index number is in bounds */
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices) &&
	   (dev_num < clist->rows)
	)
	{
	    gint sel_row = EDVCListGetSelectedLast(clist, NULL);

	    /* Update this row with the values from the given device */
	    EDVDevicesListWinSetRow(lw, dev_num, dev_ptr);

	    /* This row currently selected? */
	    if(dev_num == sel_row)
	    {
		/* Update displayed device info */
		EDVDevicesListWinUpdateDisplay(lw, dev_ptr);
	    }
	}

	EDVDevicesListWinUpdateMenus(lw);
}

/*
 *      Notifies the given Devices List Window that the given device
 *      has been removed.
 */
void EDVDevicesListWinDeviceRemovedCB(
	edv_device_listwin_struct *lw, gint dev_num
)
{
	gint row;
	GtkCList *clist;
	edv_core_struct *core_ptr;


	if(lw == NULL)
	    return;

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

	clist = (GtkCList *)lw->devices_clist;
	if(clist == NULL)
	    return;

	/* The row to remove should correspond to the device entry index
	 * since the clist should be synced with the devices list.
	 */
	row = dev_num;

	/* Delete row from clist */
	if((row >= 0) && (row < clist->rows))
	    gtk_clist_remove(clist, row);

	EDVDevicesListWinUpdateMenus(lw);


	/* Forward this signal to the devices edit window */
	EDVDevicesEditWinDeviceRemovedCB(lw->editwin, dev_num);
}


/*
 *	Creates a new Devices List Window.
 */
edv_device_listwin_struct *EDVDevicesListWinNew(
	gpointer core_ptr
)
{
	const gint	border_major = 5,
			border_minor = 2;
	const gchar	*wm_name = NULL,
			*wm_class = NULL;
	gchar *heading[4];
	gpointer mclient_data;
	gpointer entry_rtn;
	GdkWindow *window;
	GtkRcStyle	*standard_rcstyle = NULL,
			*lists_rcstyle = NULL;
	GtkAccelGroup *accelgrp;
	GtkStyle *style;
	GtkWidget *w, *menu, *parent, *parent2, *parent3, *parent4, *parent5;
	GtkEntry *entry;
	GtkCList *clist;
	edv_device_listwin_struct *lw = EDV_DEVICE_LISTWIN(
	    g_malloc0(sizeof(edv_device_listwin_struct))
	);
	if(lw == NULL)
	    return(lw);


	/* Get values from core structure */
	if(core_ptr != NULL)
	{
	    edv_core_struct *c_ptr = EDV_CORE(core_ptr);

	    standard_rcstyle = c_ptr->standard_rcstyle;
	    lists_rcstyle = c_ptr->lists_rcstyle;

	    wm_name = c_ptr->wm_name;
	    wm_class = c_ptr->wm_class;
	}

	lw->accelgrp = accelgrp = gtk_accel_group_new();
	lw->processing = FALSE;
	lw->busy_count = 0;
	lw->freeze_count = 0;
	lw->core_ptr = core_ptr;


	/* Begin creating widgets */

	/* Toplevel */
	lw->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG);
	gtk_widget_set_usize(w, LISTWIN_WIDTH, LISTWIN_HEIGHT);
	gtk_window_set_title(GTK_WINDOW(w), LISTWIN_TITLE);
	if(!STRISEMPTY(wm_name) && !STRISEMPTY(wm_class))
	    gtk_window_set_wmclass(GTK_WINDOW(w), wm_name, wm_class);
	else
	    gtk_window_set_wmclass(
		GTK_WINDOW(w), "deviceslist", PROG_NAME
	    );
	gtk_widget_realize(w);
	style = gtk_widget_get_style(w);
	window = w->window;
	if(window != NULL)
	{
	    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
	    );
	    GUISetWMIcon(window, (guint8 **)icon_device_misc_48x48_xpm);
	}
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinDeleteEventCB), lw
	);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp);
	parent = w;



	/* Main vbox */
	lw->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;


	/* Hbox for find prompt */
	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	w = GUIPromptBar(
	    (guint8 **)icon_search_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"El Hallazgo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Découverte"
#elif defined(PROG_LANGUAGE_GERMAN)
"Fund"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Trovare"
#elif defined(PROG_LANGUAGE_DUTCH)
"Vondst"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Ache"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Funn"
#else
"Find"
#endif
	    ":", NULL, &entry_rtn
	);
	lw->find_entry = (GtkWidget *)entry_rtn;
	entry = (GtkEntry *)entry_rtn;
	gtk_signal_connect(
	    GTK_OBJECT(entry), "activate",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinFindCB), lw
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	EDVEntrySetDND(EDV_CORE(core_ptr), (GtkWidget *)entry_rtn);
	gtk_widget_show(w);


	/* Vbox for devices clist and buttons */
	w = gtk_vbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;


	/* Scrolled window for devices clist */
	w = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(
	    GTK_SCROLLED_WINDOW(w),
	    GTK_POLICY_AUTOMATIC,
	    GTK_POLICY_AUTOMATIC
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent3 = w;

	/* Devices clist */
#if defined(PROG_LANGUAGE_SPANISH)
	heading[0] = "El Nombre";
	heading[1] = "El Sendero Del Artefacto";
	heading[2] = "Monte Sendero";
	heading[3] = "Archive Sistema";
#elif defined(PROG_LANGUAGE_FRENCH)
	heading[0] = "Nom";
	heading[1] = "Sentier D'appareil";
	heading[2] = "Monter Le Sentier";
	heading[3] = "Classer Le Système";
#elif defined(PROG_LANGUAGE_GERMAN)
	heading[0] = "Name";
	heading[1] = "Gerätepfad";
	heading[2] = "Stellen Sie Pfad Auf";
	heading[3] = "Dateisystem";
#elif defined(PROG_LANGUAGE_ITALIAN)
	heading[0] = "Il Nome";
	heading[1] = "Il Sentiero Di Congegno";
	heading[2] = "Montare Il Sentiero";
	heading[3] = "Schedare Il Sistema";
#elif defined(PROG_LANGUAGE_DUTCH)
	heading[0] = "Naam";
	heading[1] = "Apparaat Pad";
	heading[2] = "Bestijg Pad";
	heading[3] = "Archiveer Systeem";
#elif defined(PROG_LANGUAGE_PORTUGUESE)
	heading[0] = "O Nome";
	heading[1] = "O Caminho De Artifício";
	heading[2] = "Monte Caminho";
	heading[3] = "Arquive Sistema";
#elif defined(PROG_LANGUAGE_NORWEGIAN)
	heading[0] = "Navn";
	heading[1] = "Innretning Path";
	heading[2] = "Monter Path";
	heading[3] = "Arkiver System";
#else
	heading[0] = "Name";
	heading[1] = "Device Path";
	heading[2] = "Mount Path";
	heading[3] = "File System";
#endif
	lw->devices_clist = w = gtk_clist_new_with_titles(4, heading);
	clist = GTK_CLIST(w);
	gtk_widget_add_events(
	    w,
	    GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
	    GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
	    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
	    GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EDVCListKeyEventCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EDVCListKeyEventCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(EDVCListButtonEventCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(EDVCListButtonEventCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "motion_notify_event",
	    GTK_SIGNAL_FUNC(EDVCListMotionEventCB), core_ptr
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinKeyEventCB), lw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinKeyEventCB), lw
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinButtonPressEventCB), lw
	);
	gtk_widget_set_usize(w, -1, (gint)(LISTWIN_HEIGHT * 0.38f));
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_realize(w);
	gtk_clist_set_column_width(clist, 0, 130);
	gtk_clist_set_column_width(clist, 1, 130);
	gtk_clist_set_column_width(clist, 2, 130);
	gtk_clist_set_column_width(clist, 3, 50);
	gtk_clist_column_titles_passive(clist);
	gtk_clist_set_row_height(clist, EDV_LIST_ROW_SPACING);
	gtk_clist_set_shadow_type(clist, GTK_SHADOW_IN);
	gtk_signal_connect(
	    GTK_OBJECT(w), "select_row",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinSelectRowCB), lw
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "unselect_row",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinUnselectRowCB), lw
	);
	gtk_widget_show(w);


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

	/* Vbox for add, edit, and remove set of buttons */
	w = gtk_hbox_new(FALSE, 2 * border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Add button */
	lw->add_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_add_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Agregue"
#elif defined(PROG_LANGUAGE_FRENCH)
"Ajouter"
#elif defined(PROG_LANGUAGE_GERMAN)
"Fügen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aggiungere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Toevoeg"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Adicione"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Tilføy"
#else
"Add"
#endif
	    "...", NULL
	);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH, GUI_BUTTON_HLABEL_HEIGHT
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinAddCB), lw
	);
	gtk_accel_group_add(
	    accelgrp, GDK_a, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);                          
	GUIButtonLabelUnderline(w, GDK_a);
	gtk_widget_show(w);

	/* Edit button */
	lw->edit_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_edit_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Redacte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Editer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Redigieren"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Redigere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Bewerking"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Edite"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Rediger"
#else
"Edit"
#endif
	    "...", NULL
	);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH, GUI_BUTTON_HLABEL_HEIGHT
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinEditCB), lw
	);
	gtk_accel_group_add(
	    accelgrp, GDK_e, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);                          
	GUIButtonLabelUnderline(w, GDK_e);
	gtk_widget_show(w);

	/* Remove button */
	lw->remove_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_remove_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Quite"
#elif defined(PROG_LANGUAGE_FRENCH)
"Enlever"
#elif defined(PROG_LANGUAGE_GERMAN)
"Nehmen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Togliere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verwijdeer"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Retire"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Fjern"
#else
"Remove"
#endif
	    , NULL
	);
	gtk_widget_set_usize(
	    w,
	    GUI_BUTTON_HLABEL_WIDTH, GUI_BUTTON_HLABEL_HEIGHT
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinRemoveCB), lw
	);
	gtk_accel_group_add(
	    accelgrp, GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);                          
	GUIButtonLabelUnderline(w, GDK_r);
	gtk_widget_show(w);


	/* Hbox for up and down set of buttons */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Vbox for up button */
	w = gtk_vbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent5 = w;
	/* Up button */
	lw->up_btn = w = gtk_button_new();
	gtk_box_pack_start(GTK_BOX(parent5), w, TRUE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinUpCB), lw
	);
	gtk_widget_show(w);
	parent5 = w;
	/* Arrow */
	w = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_OUT);
	gtk_widget_set_usize(w, 20, 20);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);

	/* Vbox for down button */
	w = gtk_vbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_widget_show(w);
	parent5 = w;
	/* Down button */
	lw->down_btn = w = gtk_button_new();
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinDownCB), lw
	);
	gtk_widget_show(w);
	parent5 = w;
	/* Arrow */
	w = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
	gtk_widget_set_usize(w, 20, 20);
	gtk_container_add(GTK_CONTAINER(parent5), w);
	gtk_widget_show(w);





	/* Horizontal separator */
	w = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* Parent vbox for displaying selected device */
	lw->display_parent = w = gtk_vbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Client vbox that is parented to the display_parent, leave this
	 * NULL, it'll be created when needed.
	 */
	lw->display_client = NULL;


	/* Horizontal separator */
	w = gtk_hseparator_new();
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


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

	/* Close button */
	lw->close_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_close_20x20_xpm,
#if defined(PROG_LANGUAGE_SPANISH)
"Cierre"
#elif defined(PROG_LANGUAGE_FRENCH)
"Fin"
#elif defined(PROG_LANGUAGE_GERMAN)
"Nah"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Vicino"
#elif defined(PROG_LANGUAGE_DUTCH)
"Einde"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Próximo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Nær"
#else
"Close"
#endif
	    , NULL
	);
	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_end(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVDevicesListWinCloseCB), lw
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_c);
	gtk_widget_show(w);



	/* Right click menu */
	lw->menu = menu = GUIMenuCreate();
	mclient_data = lw;
	if(menu != NULL)
	{
	    guint accel_key, accel_mods;
	    guint8 **icon;
	    const gchar *label;
	    void (*func_cb)(GtkWidget *, gpointer);

#define DO_ADD_MENU_ITEM_LABEL	{			\
 w = GUIMenuItemCreate(					\
  menu, GUI_MENU_ITEM_TYPE_LABEL, accelgrp,		\
  icon, label, accel_key, accel_mods, NULL,		\
  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_add_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Agregue"
#elif defined(PROG_LANGUAGE_FRENCH)
"Ajouter"
#elif defined(PROG_LANGUAGE_GERMAN)
"Fügen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Aggiungere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Toevoeg"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Adicione"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Tilføy"
#else
"Add"
#endif
		"...";
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EDVDevicesListWinAddCB;
	    DO_ADD_MENU_ITEM_LABEL
	    lw->add_mi = w;

	    icon = (guint8 **)icon_edit_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Redacte"
#elif defined(PROG_LANGUAGE_FRENCH)
"Editer"
#elif defined(PROG_LANGUAGE_GERMAN)
"Redigieren"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Redigere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Bewerking"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Edite"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Rediger"
#else
"Edit"
#endif
		"...";
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EDVDevicesListWinEditCB;
	    DO_ADD_MENU_ITEM_LABEL
	    lw->edit_mi = w;

	    icon = (guint8 **)icon_remove_20x20_xpm;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Quite"
#elif defined(PROG_LANGUAGE_FRENCH)
"Enlever"
#elif defined(PROG_LANGUAGE_GERMAN)
"Nehmen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Togliere"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verwijdeer"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Retire"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Fjern"
#else
"Remove"
#endif
		;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EDVDevicesListWinRemoveCB;
	    DO_ADD_MENU_ITEM_LABEL
	    lw->remove_mi = w;

	    DO_ADD_MENU_SEP

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Cambie Arriba"
#elif defined(PROG_LANGUAGE_FRENCH)
"Changer En Haut"
#elif defined(PROG_LANGUAGE_GERMAN)
"Spostare Su"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Spostare Su"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verplaats Op"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mude Para Cima"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forskyv Opp"
#else
"Shift Up"
#endif
		;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EDVDevicesListWinUpCB;
	    DO_ADD_MENU_ITEM_LABEL
	    lw->up_mi = w;

	    icon = NULL;
	    label =
#if defined(PROG_LANGUAGE_SPANISH)
"Cambie Hacia Abajo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Changer En Bas"
#elif defined(PROG_LANGUAGE_GERMAN)
"Spostare Giù"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Spostare Giù"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verplaats Beneden"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mude Para Baixo"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forskyv Ned"
#else
"Shift Down"
#endif
		;
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = EDVDevicesListWinDownCB;
	    DO_ADD_MENU_ITEM_LABEL
	    lw->down_mi = w;

#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_SEP
	}


	/* Set initial RC styles */
	if(standard_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(
		lw->toplevel, standard_rcstyle
	    );
	if(lists_rcstyle != NULL)
	    gtk_widget_modify_style_recursive(
		lw->devices_clist, lists_rcstyle
	    );



	EDVDevicesListWinUpdateMenus(lw);

	return(lw);
}

/*
 *	Updates menus and other widgets on the given devices list
 *	window to reflect its current data.
 */
void EDVDevicesListWinUpdateMenus(
	edv_device_listwin_struct *lw
)
{
	gboolean sensitive;
	gint sel_row;
	GtkCList *clist;

	if(lw == NULL)
	    return;

	/* Get selected row on clist */
	clist = (GtkCList *)lw->devices_clist;
	sel_row = EDVCListGetSelectedLast(clist, NULL);

	sensitive = (sel_row > -1) ? TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(lw->edit_btn, sensitive)
	GTK_WIDGET_SET_SENSITIVE(lw->remove_btn, sensitive)

	GTK_WIDGET_SET_SENSITIVE(lw->edit_mi, sensitive)
	GTK_WIDGET_SET_SENSITIVE(lw->remove_mi, sensitive)

	sensitive = (sel_row > 0) ? TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(lw->up_btn, sensitive)
	GTK_WIDGET_SET_SENSITIVE(lw->up_mi, sensitive)

	sensitive = ((sel_row > -1) && (sel_row < (clist->rows - 1))) ?
	    TRUE : FALSE;
	GTK_WIDGET_SET_SENSITIVE(lw->down_btn, sensitive)
	GTK_WIDGET_SET_SENSITIVE(lw->down_mi, sensitive)

}

/*
 *	Sets the Devices List Window as busy or ready.
 */
void EDVDevicesListSetBusy(
	edv_device_listwin_struct *lw, gboolean is_busy
)
{
	GdkCursor *cursor;
	GtkWidget *w;
	edv_core_struct *core_ptr;


	if(lw == NULL)
	    return;

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

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

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

		cursor = EDVGetCursor(core_ptr, EDV_CURSOR_CODE_BUSY);
	    }
	    else
	    {
		/* Reduce busy count */
		lw->busy_count--;
		if(lw->busy_count < 0)
		    lw->busy_count = 0;

		/* If still busy do not change anything */
		if(lw->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();
	    }
	}
}

/*
 *	Checks if the Devices List Window is mapped.
 */
gboolean EDVDevicesListWinIsMapped(
	edv_device_listwin_struct *lw
)
{
	GtkWidget *w = (lw != NULL) ? lw->toplevel : NULL;
	return((w != NULL) ? GTK_WIDGET_MAPPED(w) : FALSE);
}

/*
 *	Maps the Devices List Window.
 */
void EDVDevicesListWinMap(
	edv_device_listwin_struct *lw
)
{
	GtkWidget *w = (lw != NULL) ? lw->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_show_raise(w);

	w = lw->devices_clist;
	gtk_widget_grab_focus(w);
}

/*
 *	Unmaps the Devices List Window.
 */
void EDVDevicesListWinUnmap(
	edv_device_listwin_struct *lw
)
{
	GtkWidget *w = (lw != NULL) ? lw->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_hide(w);
}

/*
 *	Deletes the Devices List Window.
 */
void EDVDevicesListWinDelete(
	edv_device_listwin_struct *lw
)
{
	if(lw == NULL)
	    return;

	/* Delete Device Edit window */
	EDVDevicesEditWinDelete(lw->editwin);
	lw->editwin = NULL;

	/* Destroy right click menu */
	GTK_WIDGET_DESTROY(lw->menu)
	lw->menu = NULL;

	/* Begin destroying widgets */
	GTK_WIDGET_DESTROY(lw->dev_icon_pm)
	lw->dev_icon_pm = NULL;
	GTK_WIDGET_DESTROY(lw->dev_name_label)
	lw->dev_name_label = NULL;

	GTK_WIDGET_DESTROY(lw->dev_device_path_label)
	lw->dev_device_path_label = NULL;
	GTK_WIDGET_DESTROY(lw->dev_mount_path_label)
	lw->dev_mount_path_label = NULL;

	GTK_WIDGET_DESTROY(lw->dev_mount_btn)
	lw->dev_mount_btn = NULL;
	GTK_WIDGET_DESTROY(lw->dev_eject_btn)
	lw->dev_eject_btn = NULL;
	GTK_WIDGET_DESTROY(lw->dev_properties_btn)
	lw->dev_properties_btn = NULL;

	GTK_WIDGET_DESTROY(lw->dev_fsck_btn)
	lw->dev_fsck_btn = NULL;
	GTK_WIDGET_DESTROY(lw->dev_tools_btn)
	lw->dev_tools_btn = NULL;
	GTK_WIDGET_DESTROY(lw->dev_format_btn)
	lw->dev_format_btn = NULL;

	PieChartDelete(lw->dev_pc);
	lw->dev_pc = NULL;

	GTK_WIDGET_DESTROY(lw->display_client)
	lw->display_client = NULL;
	GTK_WIDGET_DESTROY(lw->display_parent)
	lw->display_parent = NULL;

	GTK_WIDGET_DESTROY(lw->find_entry)
	lw->find_entry = NULL;
	GTK_WIDGET_DESTROY(lw->devices_clist)
	lw->devices_clist = NULL;

	GTK_WIDGET_DESTROY(lw->add_btn)
	lw->add_btn = NULL;
	GTK_WIDGET_DESTROY(lw->edit_btn)
	lw->edit_btn = NULL;
	GTK_WIDGET_DESTROY(lw->remove_btn)
	lw->remove_btn = NULL;
	GTK_WIDGET_DESTROY(lw->up_btn)
	lw->up_btn = NULL;
	GTK_WIDGET_DESTROY(lw->down_btn)
	lw->down_btn = NULL;
	GTK_WIDGET_DESTROY(lw->close_btn)
	lw->close_btn = NULL;

	GTK_WIDGET_DESTROY(lw->toplevel)
	lw->toplevel = NULL;

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

	g_free(lw);
}
