#include <stdlib.h>
#include <string.h>

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

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

#include "edvtypes.h"
#include "edvdevices.h"
#include "edvmount.h"
#include "endeavour.h"
#include "edvcb.h"
#include "edvop.h"
#include "edvutils.h"
#include "edvutilsgtk.h"
#include "mountbar.h"
#include "config.h"

#include "images/icon_mount_20x20.xpm"
#include "images/icon_unmount_20x20.xpm"
#include "images/icon_eject_20x20.xpm"
#include "images/icon_goto_20x20.xpm"
#include "images/icon_reload_20x20.xpm"



static gint EDVMountBarDeviceExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);
static gint EDVMountBarDeviceFocusEventCB(
	GtkWidget *widget, GdkEventFocus *focus, gpointer data
);
static gint EDVMountBarCrossingCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
static gint EDVMountBarDeviceKeyCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
);
static gint EDVMountBarDeviceButtonCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
static void EDVMountBarMapPUListCB(GtkWidget *widget, gpointer data);

static void EDVMountBarMountCB(GtkWidget *widget, gpointer data);
static void EDVMountBarEjectCB(GtkWidget *widget, gpointer data);
static void EDVMountBarGoToCB(GtkWidget *widget, gpointer data);
static void EDVMountBarRefreshStatsCB(GtkWidget *widget, gpointer data);

static void EDVMountBarSetStats(edv_mountbar_struct *mb);

edv_mountbar_struct *EDVMountBarNew(
	gpointer core_ptr, GtkWidget *parent,
	void (*mount_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*eject_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*goto_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	gpointer client_data
);
void EDVMountBarUpdateMenus(edv_mountbar_struct *mb);
void EDVMountBarMap(edv_mountbar_struct *mb);
void EDVMountBarUnmap(edv_mountbar_struct *mb);
void EDVMountBarDelete(edv_mountbar_struct *mb);


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


/*
 *	Mount Bar device icon and label GtkDrawingArea
 *	"expose_event" signal callback.
 */
static gint EDVMountBarDeviceExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	gint status = FALSE;
	GtkStateType state;
	gint width, height;
	gint icon_width = 0, icon_height = 0;
	GdkGC *gc;
	GdkWindow *window;
	GdkDrawable *drawable;
	GtkStyle *style;
	GtkWidget *w;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return(status);

	w = mb->dev_da;
	if(w == NULL)
	    return(status);

	state = GTK_WIDGET_STATE(w);
	window = w->window;
	style = gtk_widget_get_style(w);
	if((window == NULL) || (style == NULL))
	    return(status);

	drawable = window;
	gdk_window_get_size(drawable, &width, &height);

	/* Create graphic context as needed */
	gc = mb->gc;
	if(gc == NULL)
	    mb->gc = gc = GDK_GC_NEW();


	/* Begin drawing */

	/* Background */
	gdk_draw_rectangle(
	    drawable, style->base_gc[state], TRUE,
	    0, 0, width, height
	);

	/* Icon */
	if(mb->dev_pixmap != NULL)
	{
	    gint	x = 2 + 2,
			y = 2;
	    GdkBitmap *mask = mb->dev_mask;
	    GdkPixmap *pixmap = mb->dev_pixmap;
	    gdk_window_get_size(pixmap, &icon_width, &icon_height);
	    gdk_gc_set_clip_mask(gc, mask);
	    gdk_gc_set_clip_origin(gc, x, y);
	    gdk_draw_pixmap(
		drawable, gc, pixmap,
		0, 0,
		x, y,
		icon_width, icon_height
	    );
	    gdk_gc_set_clip_mask(gc, NULL);
	}

	/* Label */
	if(!STRISEMPTY(mb->dev_text) && (style->font != NULL))
	{
	    gint        x = 2 + 2 + icon_width + 2,
			y = 0;
	    gint lbearing, rbearing, swidth, ascent, descent;
	    const gchar *s = mb->dev_text;
	    GdkFont *font = style->font;
	    gdk_string_extents(
		font, s,
		&lbearing, &rbearing, &swidth, &ascent, &descent
	    );
	    gdk_draw_string(
		drawable, font, style->text_gc[state],
		x + lbearing,
		y + ((height - (font->ascent + font->descent)) / 2) + 
		    font->ascent,
		s
	    );
	}

	/* Draw shadow in frame */
	gtk_draw_shadow(
	    style, drawable, state, GTK_SHADOW_IN,
	    0, 0, width - 1, height - 1
	);

	/* Draw focus if widget is focused */
	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
	    );

	status = TRUE;
	return(status);
}

/*
 *	Mount Bar device icon and label GtkDrawingArea
 *	"focus_in_event" or "focus_out_event" signal callback.
 */
static gint EDVMountBarDeviceFocusEventCB(
	GtkWidget *widget, GdkEventFocus *focus, gpointer data
)
{
	gint status = FALSE;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if((widget == NULL) || (focus == NULL) || (mb == NULL))
	    return(status);

	if(focus->in && !GTK_WIDGET_HAS_FOCUS(widget))
	{
	    GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
	    EDVMountBarDeviceExposeCB(widget, NULL, mb);
	    status = TRUE;
	}
	else if(!focus->in && GTK_WIDGET_HAS_FOCUS(widget))
	{   
	    GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
	    EDVMountBarDeviceExposeCB(widget, NULL, mb);
	    status = TRUE;
	}

	return(status);
}

/*
 *	Mount Bar GtkWidget "enter_notify_event" or
 *	"leave_notify_event" signal callback.
 */
static gint EDVMountBarCrossingCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	const gchar *mesg = NULL;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if((widget == NULL) || (crossing == NULL) || (mb == NULL))
	    return(status);

	etype = (gint)crossing->type;

	/* Handle by event type */
	switch(etype)
	{
	  case GDK_ENTER_NOTIFY:
	    if(mb->mount_btn == widget)
		mesg =
#if defined(PROG_LANGUAGE_SPANISH)
"Mount/unmount dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Mount/unmount appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Mount/unmount vorrichtung"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Mount/unmount congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Mount/unmount apparaat"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Mount/unmount artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Mount/unmount innretning"
#else
"Mount/unmount device"
#endif
		;
	    else if(mb->eject_btn == widget)
		mesg =
#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 vorrichtung aus"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Espellere la stampa dal congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Stoot media van apparaat uit"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Expulse imprensa de artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Kast ut medier fra innretning"
#else
"Eject media from device"
#endif
		;
	    else if(mb->goto_btn == widget)
		mesg =
#if defined(PROG_LANGUAGE_SPANISH)
"Vaya a montar sendero"
#elif defined(PROG_LANGUAGE_FRENCH)
"Aller monter le sentier"
#elif defined(PROG_LANGUAGE_GERMAN)
"Gehen sie, um pfad aufzustellen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Andare montare il sentiero"
#elif defined(PROG_LANGUAGE_DUTCH)
"Ga om pad te bestijgen"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Vá montar caminho"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Dra montere sti"
#else
"Go to mount path"
#endif
		;
	    else if(mb->refresh_stats_btn == widget)
		mesg =
#if defined(PROG_LANGUAGE_SPANISH)
"Refresque toda estadística de dispositivo"
#elif defined(PROG_LANGUAGE_FRENCH)
"Rafraîchir toute statistique d'appareil"
#elif defined(PROG_LANGUAGE_GERMAN)
"Erfrischen sie alle gerätestatistik"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Rinfrescare tutto la statistica di congegno"
#elif defined(PROG_LANGUAGE_DUTCH)
"Verfris alle apparaat statistieken"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Refresque-se toda estatística de artifício"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Forfrisk all innretnings statistikk"
#else
"Refresh all device statistics"
#endif
		;
	    else if(mb->capacity_pbar == widget)
	    { /* Ignore */ }
	    break;
	}

	if(mb->status_message_cb != NULL)
	    mb->status_message_cb(
		mb,			/* Mount Bar */
		mesg,			/* Message */
		mb->client_data		/* Data */
	    );

	return(status);
}


/*
 *	Mount Bar device icon and label GtkDrawingArea
 *	"key_press_event" or "key_release_event" signal callback.
 */
static gint EDVMountBarDeviceKeyCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	guint keyval, state;
	gboolean press;
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if((key == NULL) || (mb == NULL))
	    return(status);

	core_ptr = EDV_CORE(mb->core_ptr);
	if(core_ptr == NULL)
	    return(status);

	etype = key->type;

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

#define DO_STOP_KEY_SIGNAL_EMIT	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}
	if(TRUE)
	{
	    /* Handle by key value */
	    switch(keyval)
	    {
	      case GDK_Return:
	      case GDK_KP_Enter:
	      case GDK_ISO_Enter:
	      case GDK_3270_Enter:
	      case GDK_space:
	      case GDK_KP_Space:
		if(press)
		{
		    EDVMountBarMountCB(NULL, mb);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Up:
	      case GDK_KP_Up:
	      case GDK_Page_Up:
	      case GDK_KP_Page_Up:
		if(press)
		{
		    gint i = mb->selected_dev_num;
		    edv_device_struct *dev_ptr;

		    /* Current selection in bounds? */
		    if((i >= 0) && (i < core_ptr->total_devices))
		    {
			/* Check previous device and see if it is selectable.
			 * Iterate on to previous device if the current is
			 * not selectable.
			 */
			for(i--; i >= 0; i--)
			{
			    dev_ptr = core_ptr->device[i];
			    if(dev_ptr == NULL)
				continue;
			    if(dev_ptr->unlisted)
				continue;
			    break;	/* This device is selectable */
			}
			/* Found a selectable device index? */
			if(i >= 0)
			    mb->selected_dev_num = i;
		    }
		    else
		    {
			/* Current selection is out of bounds, so select
			 * the first valid device
			 */
			for(i = 0; i < core_ptr->total_devices; i++)
			{
			    dev_ptr = core_ptr->device[i];
			    if(dev_ptr == NULL)
				continue;
			    if(dev_ptr->unlisted)
				continue;

			    mb->selected_dev_num = i;
			}
		    }
		    EDVMountBarUpdateMenus(mb);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Down:
	      case GDK_KP_Down:
	      case GDK_Page_Down:
	      case GDK_KP_Page_Down:
		if(press)
		{
		    gint i = mb->selected_dev_num;
		    edv_device_struct *dev_ptr;

		    /* Current selection in bounds? */
		    if((i >= 0) && (i < core_ptr->total_devices))
		    {
			/* Check next device and see if it is selectable.
			 * Iterate on to next device if the current is
			 * not selectable.
			 */
			for(i++; i < core_ptr->total_devices; i++)
			{
			    dev_ptr = core_ptr->device[i];
			    if(dev_ptr == NULL)
				continue;
			    if(dev_ptr->unlisted)
				continue;
			    break;	/* This device is selectable */
			}
			/* Found a selectable device index? */
			if(i < core_ptr->total_devices)
			    mb->selected_dev_num = i;
		    }
		    else
		    {
			/* Current selection is out of bounds, so select
			 * the first valid device.
			 */
			for(i = 0; i < core_ptr->total_devices; i++)
			{
			    dev_ptr = core_ptr->device[i];
			    if(dev_ptr == NULL)
				continue;
			    if(dev_ptr->unlisted)
				continue;

			    mb->selected_dev_num = i;
			}
		    }
		    EDVMountBarUpdateMenus(mb);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_Home:
		if(press)
		{
		    gint i;
		    edv_device_struct *dev_ptr;

		    /* Look for the first device that is selectable */
		    for(i = 0; i < core_ptr->total_devices; i++)
		    {
			dev_ptr = core_ptr->device[i];
			if(dev_ptr == NULL)
			    continue;
			if(dev_ptr->unlisted)
			    continue;
			break;		/* This device is selectable */
		    }
		    /* Found a selectable device index? */
		    if(i < core_ptr->total_devices)
		    {
			mb->selected_dev_num = i;
			EDVMountBarUpdateMenus(mb);
		    }
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;

	      case GDK_End:
		if(press)
		{
		    gint i;
		    edv_device_struct *dev_ptr;

		    /* Look for the last device that is selectable */
		    for(i = core_ptr->total_devices - 1; i >= 0; i--)
		    {
			dev_ptr = core_ptr->device[i];
			if(dev_ptr == NULL)
			    continue;
			if(dev_ptr->unlisted)
			    continue;
			break;          /* This device is selectable */
		    }
		    /* Found a selectable device index? */
		    if(i >= 0)
		    {
			mb->selected_dev_num = i;
			EDVMountBarUpdateMenus(mb);
		    }
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		break;
	    }
	}

	return(status);
}


/*
 *	Mount Bar device icon and label GtkDrawingArea
 *	"button_press_event" or "button_release_event" signal callback.
 */
static gint EDVMountBarDeviceButtonCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if((button == NULL) || (mb == NULL))
	    return(status);

	etype = (gint)button->type;
	switch(etype)
	{
	  case GDK_BUTTON_PRESS:
	    if(!GTK_WIDGET_HAS_FOCUS(widget))
		gtk_widget_grab_focus(widget);
	    switch(button->button)
	    {
	      case 1:
		EDVMountBarMapPUListCB(mb->map_btn, mb);
		status = TRUE;
		break;
	    }
	    break;

	}

	return(status);
}

/*
 *	Mount Bar map Devices Popup List callback.
 */
static void EDVMountBarMapPUListCB(GtkWidget *widget, gpointer data)
{
	const gchar *value;
	gint dev_num;
	edv_device_struct *dev_ptr;
	pulist_struct *pulist;
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return;

	core_ptr = EDV_CORE(mb->core_ptr);
	pulist = (core_ptr != NULL) ? core_ptr->devices_pulist : NULL;
	if(pulist == NULL)
	    return;
	if(PUListIsQuery(pulist))
	    return;

	/* Block input and get value */
	value = PUListMapQuery(
	    pulist, mb->dev_text,
	    10,
	    PULIST_RELATIVE_BELOW,
	    mb->dev_da, widget
	);
	/* User canceled? */
	if(value == NULL)
	    return;

	/* Get device index number from the selected value */
	dev_num = (gint)PUListGetDataFromValue(pulist, value);
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;

	/* Got valid device? */
	if(dev_ptr != NULL)
	{
	    /* Set new selected device on the mount bar structure */
	    mb->selected_dev_num = dev_num;

	    EDVMountBarUpdateMenus(mb);
	}
}

/*
 *	Mount Bar mount/unmount callback.
 */
static void EDVMountBarMountCB(GtkWidget *widget, gpointer data)
{
	gint dev_num;
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return;

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

	dev_num = mb->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	{
	    edv_device_struct *dev_ptr = core_ptr->device[dev_num];
	    if((dev_ptr != NULL) && (mb->mount_cb != NULL))
		mb->mount_cb(
		    mb,			/* Mount Bar */
		    dev_num,		/* Device Number */
		    dev_ptr,		/* Device */
		    mb->client_data	/* Data */
		);
	}
}

/*
 *	Mount Bar eject callback.
 */
static void EDVMountBarEjectCB(GtkWidget *widget, gpointer data)
{
	gint dev_num;
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return;

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

	dev_num = mb->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	{
	    edv_device_struct *dev_ptr = core_ptr->device[dev_num];
	    if((dev_ptr != NULL) && (mb->eject_cb != NULL))
		mb->eject_cb(
		    mb,			/* Mount Bar */
		    dev_num,		/* Device Number */
		    dev_ptr,		/* Device */
		    mb->client_data	/* Data */
		);
	}
}

/*
 *	Mount Bar go to mount path callback.
 */
static void EDVMountBarGoToCB(GtkWidget *widget, gpointer data)
{
	gint dev_num;
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return;

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

	dev_num = mb->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	{
	    edv_device_struct *dev_ptr = core_ptr->device[dev_num];
	    if((dev_ptr != NULL) && (mb->goto_cb != NULL))
		mb->goto_cb(
		    mb,			/* Mount Bar */
		    dev_num,		/* Device Number */
		    dev_ptr,		/* Device */
		    mb->client_data	/* Data */
		);
	}
}

/*
 *	Mount Bar refresh stats callback.
 *
 *	Updates the device stats on the core structure.
 */
static void EDVMountBarRefreshStatsCB(GtkWidget *widget, gpointer data)
{
	edv_core_struct *core_ptr;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(data);
	if(mb == NULL)
	    return;

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

	/* Refresh all device mount states and device stats, then get
	 * device path or mount path that matches the given disk
	 * object's path
	 */
	EDVDevicesListUpdateMountStates(
	    core_ptr->device, core_ptr->total_devices
	);
	EDVDevicesListUpdateStats(
	    core_ptr->device, core_ptr->total_devices
	);

	EDVMountBarUpdateMenus(mb);
}

/*
 *	Sets the stats label of the given mount bar to reflect the values
 *	of the selected device.
 *
 *	The devices list on the core structure is not updated in this call.
 */
static void EDVMountBarSetStats(edv_mountbar_struct *mb)
{
	const gchar *cstrptr;
	gint dev_num;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;
	GtkWidget *w;


	if(mb == NULL)
	    return;

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

	/* Get selected device index number and pointer */
	dev_num = mb->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;

	/* Begin updating capacity progress bar */
	w = mb->capacity_pbar;
	if((dev_ptr != NULL) && (w != NULL))
	{
	    gulong used = dev_ptr->blocks_total - dev_ptr->blocks_free;
	    GtkProgress *pr = GTK_PROGRESS(w);
	    GtkProgressBar *pb = GTK_PROGRESS_BAR(w);

	    gtk_progress_set_activity_mode(pr, FALSE);
	    gtk_progress_set_format_string(pr, "%p%%");
	    gtk_progress_set_show_text(pr, FALSE);

	    gtk_progress_bar_update(
		pb,
		(dev_ptr->blocks_total > 0) ?
		    ((gfloat)used / (gfloat)dev_ptr->blocks_total) :
		    0.0f
	    );
	}

	/* Begin updating stats label */
	w = mb->stats_label;
	if((dev_ptr != NULL) && (w != NULL))
	{
	    gulong used = dev_ptr->blocks_total - dev_ptr->blocks_free;
	    gchar text[256];


	    if(dev_ptr->is_mounted)
	    {
		gchar	used_s[256], total_s[256], free_avail_s[256],
			free_s[256];

		strcpy(
		    used_s,
		    EDVGetObjectSizeStr(core_ptr, used)
		);
		strcpy(
		    total_s,
		    EDVGetObjectSizeStr(core_ptr, dev_ptr->blocks_total)
		);
		strcpy(
		    free_avail_s,
		    EDVGetObjectSizeStr(core_ptr, dev_ptr->blocks_available)
		);
		strcpy(
		    free_s,
		    EDVGetObjectSizeStr(core_ptr, dev_ptr->blocks_free)
		);

#ifdef PROG_LANGUAGE_ENGLISH
		cstrptr = "Used: %s kb  Total: %s kb  %.0f%%  \
Free & Available: %s kb  Free: %s kb";
#endif
#ifdef PROG_LANGUAGE_SPANISH
		cstrptr = "Usado: %s kb  El Suma: %s kb %.0f%%  \
Free & Available: %s kb  Free: %s kb";
#endif
#ifdef PROG_LANGUAGE_FRENCH
		cstrptr = "Utilisé: %s kb  Total: %s kb  %.0f%%  \
Free & Available: %s kb  Free: %s kb";
#endif
		if(dev_ptr->blocks_total > 0)
		{
		    g_snprintf(
			text, sizeof(text),
			cstrptr,
			used_s,
			total_s,
			(gfloat)used * 100.0f /
			    (gfloat)dev_ptr->blocks_total,
			free_avail_s,
			free_s
		    );
		}
		else
		{
#ifdef PROG_LANGUAGE_ENGLISH
		    cstrptr = "Undefined Capacity";
#endif
#ifdef PROG_LANGUAGE_SPANISH
		    cstrptr = "La Capacidad Indefinida";
#endif
#ifdef PROG_LANGUAGE_FRENCH
		    cstrptr = "Capacité Non Définie";
#endif
		    strcpy(text, cstrptr);
		}
	    }
	    else
	    {
#ifdef PROG_LANGUAGE_ENGLISH
		cstrptr = "Device Not Mounted";
#endif
#ifdef PROG_LANGUAGE_SPANISH
		cstrptr = "El Artefacto No Montó";
#endif
#ifdef PROG_LANGUAGE_FRENCH
		cstrptr = "L'appareil n'A Pas Monté";
#endif
		strcpy(text, cstrptr);
	    }

	    gtk_label_set_text(GTK_LABEL(w), text);
	}
}


/*
 *	Create a new mount bar.
 */
edv_mountbar_struct *EDVMountBarNew(
	gpointer core_ptr, GtkWidget *parent,
	void (*mount_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*eject_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*goto_cb)(gpointer, gint, edv_device_struct *, gpointer),
	void (*status_message_cb)(gpointer, const gchar *, gpointer),
	gpointer client_data
)
{
	const gint border_minor = 2;
	GtkAdjustment *adj;
	GtkWidget *w, *parent2;
	edv_mountbar_struct *mb = EDV_MOUNTBAR(
	    g_malloc0(sizeof(edv_mountbar_struct))
	);
	if(mb == NULL)
	    return(mb);

	/* Reset values */
	mb->initialized = TRUE;
	mb->map_state = FALSE;
	mb->core_ptr = core_ptr;
	mb->last_mount_btn_state = -1;
	mb->dev_text = NULL;
	mb->dev_pixmap = NULL;
	mb->dev_mask = NULL;
	mb->selected_dev_num = 0;
	mb->mount_cb = mount_cb;
	mb->eject_cb = eject_cb;
	mb->goto_cb = goto_cb;
	mb->status_message_cb = status_message_cb;
	mb->client_data = client_data;

	mb->gc = NULL;


	/* Set first device from the core structure as the initially
	 * selected device. This first device is not always device
	 * index 0, but rather the first one that is not marked as
	 * unlisted
	 *
	 */
	if(core_ptr != NULL)
	{
	    gint i;
	    edv_device_struct *dev_ptr;
	    edv_core_struct *c_ptr = EDV_CORE(core_ptr);

	    for(i = 0; i < c_ptr->total_devices; i++)
	    {
		dev_ptr = c_ptr->device[i];
		if(dev_ptr == NULL)
		    continue;
		if(dev_ptr->unlisted)
		    continue;

		mb->selected_dev_num = i;
		break;
	    }
	}

	/* Create toplevel */
	mb->toplevel = w = gtk_hbox_new(FALSE, border_minor);
	gtk_container_border_width(GTK_CONTAINER(w), 2);
	gtk_container_add(GTK_CONTAINER(parent), w);
	parent = w;


	/* Hbox for device icon and label drawing area and map button */
	w = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Device drawing area */
	mb->dev_da = w = gtk_drawing_area_new();
	gtk_widget_set_usize(w, 200, 20 + (2 * 2));
	gtk_drawing_area_size(GTK_DRAWING_AREA(w), 200, 20 + (2 * 2));
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_FOCUS);
	gtk_widget_add_events(
	    w,
	    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(EDVMountBarDeviceExposeCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_in_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceFocusEventCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "focus_out_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceFocusEventCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_press_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceKeyCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "key_release_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceKeyCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceButtonCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "button_release_event",
	    GTK_SIGNAL_FUNC(EDVMountBarDeviceButtonCB), mb
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);

	/* Popup list map button */
	mb->map_btn = w = PUListNewMapButtonArrow(
	    GTK_ARROW_DOWN, GTK_SHADOW_OUT,
	    EDVMountBarMapPUListCB, mb
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);



	/* Mount/unmount button */
	mb->mount_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_mount_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVMountBarMountCB), mb
	);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Mount/unmount device"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "El monte/artefacto de unmount"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Appareil de monte/unmount"
#endif
	);
	gtk_widget_show(w);

	/* Eject button */
	mb->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(parent), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVMountBarEjectCB), mb
	);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Eject media from device"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Expulse medios del artefacto"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Ejecter le presse de l'appareil"
#endif
	);
	gtk_widget_show(w);

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

	/* Go to button */
	mb->goto_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_goto_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVMountBarGoToCB), mb
	);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Go to mount path"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Vaya a montar sendero"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Aller monter le sentier"
#endif
	);
	gtk_widget_show(w);

	/* Refresh stats button */
	mb->refresh_stats_btn = w = GUIButtonPixmap(
	    (guint8 **)icon_reload_20x20_xpm
	);
	gtk_button_set_relief(GTK_BUTTON(w), GTK_RELIEF_NONE);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_signal_connect(
	    GTK_OBJECT(w), "enter_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "leave_notify_event",
	    GTK_SIGNAL_FUNC(EDVMountBarCrossingCB), mb
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(EDVMountBarRefreshStatsCB), mb
	);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Refresh all device statistics"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Refresque toda estadística de artefacto"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Rafraîchir toute statistique d'appareil"
#endif
	);
	gtk_widget_show(w);


	/* Capacity progress bar */
	adj = (GtkAdjustment *)gtk_adjustment_new(0, 1, 100, 0, 5, 5);
	mb->capacity_pbar = w = gtk_progress_bar_new_with_adjustment(adj);
	gtk_widget_set_usize(w, 100, -1);
	gtk_progress_bar_set_orientation(
	    GTK_PROGRESS_BAR(w), GTK_PROGRESS_LEFT_TO_RIGHT
	);
	gtk_progress_bar_set_bar_style(
	    GTK_PROGRESS_BAR(w), GTK_PROGRESS_CONTINUOUS
	);
	gtk_progress_set_activity_mode(
	    GTK_PROGRESS(w), FALSE
	);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);


	/* Frame for stats label */
	w = gtk_frame_new(NULL);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent2 = w;

	w = gtk_hbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;

	/* Stats label */
	mb->stats_label = w = gtk_label_new("");
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, border_minor);
	gtk_widget_show(w);


	EDVMountBarUpdateMenus(mb);

	return(mb);
}


/*
 *	Updates all menus and widgets on the mount bar.
 *
 *	This should be called whenever a device has been mounted/unmounted
 *	or when one or more devices have been modified, added, or removed.
 */
void EDVMountBarUpdateMenus(edv_mountbar_struct *mb)
{
	gint dev_num;
	GtkWidget *w;
	edv_device_struct *dev_ptr;
	edv_core_struct *core_ptr;


	if(mb == NULL)
	    return;

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

	/* Get selected device index number and pointer (if any) */
	dev_num = mb->selected_dev_num;
	if((dev_num >= 0) && (dev_num < core_ptr->total_devices))
	    dev_ptr = core_ptr->device[dev_num];
	else
	    dev_ptr = NULL;

	/* Update device icon */
	if(dev_ptr != NULL)
	{
	    gint i;
	    GdkPixmap *pixmap = NULL;
	    GdkBitmap *mask = NULL;


	    EDVDeviceRealize(dev_ptr, FALSE);

	    /* Get device icon pixmap and mask pair from selected device
	     * structure.
	     */
	    for(i = 0; i < EDV_DEVICE_TOTAL_ICON_STATES; i++)
	    {
		pixmap = dev_ptr->small_pixmap[i];
		mask = dev_ptr->small_mask[i];
		if(pixmap != NULL)
		    break;
	    }
	    /* Got device icon pixmap and mask pair? */
	    if(pixmap != NULL)
	    {
		GdkPixmap *old_pixmap = mb->dev_pixmap;
		GdkBitmap *old_mask = mb->dev_mask;

		/* Set new pixmap and mask pair and add a refcount */
		mb->dev_pixmap = pixmap;
		mb->dev_mask = mask;
		if(pixmap != NULL)
		    gdk_pixmap_ref(pixmap);
		if(mask != NULL)
		    gdk_bitmap_ref(mask);

		/* Unref old pixmap and mask pair (if any) */
		if(old_pixmap != NULL)
		    gdk_pixmap_unref(old_pixmap);
		if(old_mask != NULL)
		    gdk_bitmap_unref(old_mask);
	    }
	}

	/* Update device label */
	if(dev_ptr != NULL)
	{
	    const gchar *text = dev_ptr->name;
	    if((text == NULL) && (dev_ptr->device_path != NULL))
		text = EDVGetPathName(dev_ptr->device_path);
	    if(text == NULL)
		text = "(null)";

	    g_free(mb->dev_text);
	    mb->dev_text = STRDUP(text);
	}

	/* Update mount button */
	w = mb->mount_btn;
	if(w != NULL)
	{
	    if(dev_ptr == NULL)
	    {
		/* No device selected, so set insensitive */
		GTK_WIDGET_SET_SENSITIVE(w, FALSE)
	    }
	    else
	    {
		guint8 **icon_data = NULL;


		/* Update sensitivity based on selected device's
		 * no_unmount setting
		 */
		GTK_WIDGET_SET_SENSITIVE(w, !dev_ptr->no_unmount)

		/* Current mount button icon not defined? */
		if(mb->last_mount_btn_state < 0)
		{
		    /* Go ahead and explicitly set icon then */
		    icon_data = dev_ptr->is_mounted ?
			(guint8 **)icon_unmount_20x20_xpm :
			(guint8 **)icon_mount_20x20_xpm;
		}
		/* Set unmount icon? */
		else if(dev_ptr->is_mounted &&
			(mb->last_mount_btn_state == 0)
		)
		{
		    icon_data = (guint8 **)icon_unmount_20x20_xpm;
		}
		/* Set mount icon? */
		else if(!dev_ptr->is_mounted &&
			(mb->last_mount_btn_state == 1)
		)
		{
		    icon_data = (guint8 **)icon_mount_20x20_xpm;
		}
		if(icon_data != NULL)
		    GUIButtonPixmapUpdate(w, icon_data, NULL);

		/* Update last mount button icon state */
		mb->last_mount_btn_state = dev_ptr->is_mounted ? 1 : 0;
	    }
	}

	/* Update eject button */
	w = mb->eject_btn;
	if(w != NULL)
	{
	    if(dev_ptr == NULL)
	    {
		GTK_WIDGET_SET_SENSITIVE(w, FALSE)
	    }
	    else
	    {
		GTK_WIDGET_SET_SENSITIVE(
		    w,
		    !STRISEMPTY(dev_ptr->command_eject)
		)
	    }
	}

	/* Redraw device icon and label */
	EDVMountBarDeviceExposeCB(mb->dev_da, NULL, mb);

	/* Update stats label */
	EDVMountBarSetStats(mb);
}

/*
 *	Maps the Mount Bar.
 */
void EDVMountBarMap(edv_mountbar_struct *mb)
{
        GtkWidget *w = (mb != NULL) ? mb->toplevel : NULL;
        if(w == NULL)
            return;

	gtk_widget_show(w);
	mb->map_state = TRUE;
}

/*
 *	Unmaps the Mount Bar.
 */
void EDVMountBarUnmap(edv_mountbar_struct *mb)
{
	GtkWidget *w = (mb != NULL) ? mb->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_hide(w);
	mb->map_state = FALSE;
}

/*
 *	Deletes the Mount Bar.
 */
void EDVMountBarDelete(edv_mountbar_struct *mb)
{
	if(mb == NULL)
	    return;

	GTK_WIDGET_DESTROY(mb->dev_da)
	mb->dev_da = NULL;
        GTK_WIDGET_DESTROY(mb->map_btn)
	mb->map_btn = NULL;
        GTK_WIDGET_DESTROY(mb->mount_btn)
	mb->mount_btn = NULL;
        GTK_WIDGET_DESTROY(mb->eject_btn)
	mb->eject_btn = NULL;
        GTK_WIDGET_DESTROY(mb->goto_btn)
	mb->goto_btn = NULL;
        GTK_WIDGET_DESTROY(mb->refresh_stats_btn)
	mb->refresh_stats_btn = NULL;
        GTK_WIDGET_DESTROY(mb->capacity_pbar)
	mb->capacity_pbar = NULL;
        GTK_WIDGET_DESTROY(mb->stats_label)
	mb->stats_label = NULL;
        GTK_WIDGET_DESTROY(mb->toplevel)
	mb->toplevel = NULL;

	GDK_PIXMAP_UNREF(mb->dev_pixmap)
	mb->dev_pixmap = NULL;
	GDK_BITMAP_UNREF(mb->dev_mask)
	mb->dev_mask = NULL;

	GDK_GC_UNREF(mb->gc)
	mb->gc = NULL;

	g_free(mb->dev_text);
	mb->dev_text = NULL;

	g_free(mb);
}
