#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include <unistd.h>

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

#include "../guiutils.h"
#include "../cdialog.h"
#include "../pdialog.h"

#include "../edvtypes.h"
#include "../lib/endeavour2.h"

#include "ziptoolio.h"
#include "ziptool.h"
#include "ziptoolcb.h"
#include "config.h"


void ZipToolOPIDDeleteCB(gpointer data, gpointer user_data);

gint ZipToolExposeEventCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);
gint ZipToolButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);

gint ZipToolCloseCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
void ZipToolDestroyCB(GtkObject *object, gpointer data);

gint ZipToolMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
gint ZipToolMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);

void ZipToolOPIDEnterCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void ZipToolOPIDLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data
);
void ZipToolOPIDCB(
	toolbar_item_struct *item, gint id, gpointer data
);

void ZipToolMountCB(GtkWidget *widget, gpointer data);
void ZipToolUnmountCB(GtkWidget *widget, gpointer data);
void ZipToolSpinDownCB(GtkWidget *widget, gpointer data);
void ZipToolEjectCB(GtkWidget *widget, gpointer data);
void ZipToolPasswordCB(GtkWidget *widget, gpointer data);
void ZipToolBrowseCB(GtkWidget *widget, gpointer data);
void ZipToolRefreshCB(GtkWidget *widget, gpointer data);
void ZipToolFSCKCB(GtkWidget *widget, gpointer data);
void ZipToolCloseBtnCB(GtkWidget *widget, gpointer data);
void ZipToolExitCB(GtkWidget *widget, gpointer data);


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


/*
 *	Use Endeavour to mount/unmount devices
 *
 *	This may not work when Endeavour is not running, if in doubt
 *	do not define this
 */
/* #define ZIP_TOOL_USE_ENDEAVOUR_MOUNT */


/*
 *	Zip Tool Window Operation ID delete signal callback.
 */
void ZipToolOPIDDeleteCB(gpointer data, gpointer user_data)
{
	zip_tool_opid_struct *opid = ZIP_TOOL_OPID(data);
	if(opid == NULL)
	    return;

	g_free(opid->name);
	g_free(opid->button_name);
	g_free(opid->tooltip);

	g_free(opid);
}


/*
 *	"expose_event" signal callback.
 */
gint ZipToolExposeEventCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if((widget == NULL) || (expose == NULL) || (zt == NULL))
	    return(status);

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

	/* Handle by widget */
	if(widget == zt->display_event_box)
	{
	    GdkWindow *window = widget->window;
	    if(window != NULL)
	    {
#if 0
		gdk_window_clear(window);
#endif
	    }
	    status = TRUE;
	}

	return(status);
}

/*
 *	"button_press_event" signal callback.
 */
gint ZipToolButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint etype;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if((widget == NULL) || (button == NULL) || (zt == NULL))
	    return(FALSE);

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

	/* Handle by widget */
	if(widget == zt->display_event_box)
	{
	    GtkWidget *w;

	    /* Handle by event type */
	    switch(etype)
	    {
#if 0
	      case GDK_2BUTTON_PRESS:
		/* Handle by button number */
		switch(button->button)
		{
		  case 1:
		    /* Do start */
		    ZipToolStartCB(widget, data);
		    break;
		}
		break;
#endif

	      case GDK_BUTTON_PRESS:
		/* Handle by button number */
		switch(button->button)
		{
		  case 3:
		    /* Get right click menu widget and map it */
		    w = zt->menu;
		    if(w != NULL)
			gtk_menu_popup(
			    GTK_MENU(w), NULL, NULL,
			    NULL, NULL,
			    button->button, button->time
			);
		    break;
		}
		break;
	    }
	}

	return(TRUE);
}


/*
 *      Close callback.
 */
gint ZipToolCloseCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	ZipToolCloseBtnCB(widget, data);

	return(TRUE);
}

/*
 *      Destroy callback.
 */
void ZipToolDestroyCB(GtkObject *object, gpointer data)
{
	return;
}


/*
 *	Menu item enter callback.
 */
gint ZipToolMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	zip_tool_struct *zt;
	zip_tool_opid_struct *opid_ptr = ZIP_TOOL_OPID(data);
	if(opid_ptr == NULL)
	    return(FALSE);

	zt = ZIP_TOOL(opid_ptr->zt);
	if(zt == NULL)
	    return(FALSE);

	ZipToolStatusMessage(zt, opid_ptr->tooltip, FALSE);

	return(TRUE);
}

/*
 *	Menu item leave callback.
 */
gint ZipToolMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	zip_tool_struct *zt;
	zip_tool_opid_struct *opid_ptr = ZIP_TOOL_OPID(data);
	if(opid_ptr == NULL)
	    return(FALSE);

	zt = ZIP_TOOL(opid_ptr->zt);
	if(zt == NULL)
	    return(FALSE);

	ZipToolStatusMessage(zt, NULL, FALSE);

	return(TRUE);
}

/*
 *	Operation ID enter callback.
 */
void ZipToolOPIDEnterCB(
	toolbar_item_struct *item, gint id, gpointer data
)
{
	zip_tool_opid_struct *opid_ptr = ZIP_TOOL_OPID(data);
	if(opid_ptr == NULL)
	    return;

	if(opid_ptr->func_enter_cb != NULL)
	    opid_ptr->func_enter_cb(NULL, NULL, opid_ptr);
}

/*
 *      Operation ID leave callback.
 */
void ZipToolOPIDLeaveCB(
	toolbar_item_struct *item, gint id, gpointer data
)
{
	zip_tool_opid_struct *opid_ptr = ZIP_TOOL_OPID(data);
	if(opid_ptr == NULL)
	    return;

	if(opid_ptr->func_leave_cb != NULL)
	    opid_ptr->func_leave_cb(NULL, NULL, opid_ptr);
}

/*
 *	Operation id callback.
 */
void ZipToolOPIDCB(
	toolbar_item_struct *item, gint id, gpointer data
)
{
	zip_tool_opid_struct *opid_ptr = ZIP_TOOL_OPID(data);
	if(opid_ptr == NULL)
	    return;

	if(opid_ptr->func_cb != NULL)
	    opid_ptr->func_cb(NULL, opid_ptr->zt);
}


/*
 *	Mount callback.
 */
void ZipToolMountCB(GtkWidget *widget, gpointer data)
{
#ifdef ZIP_TOOL_USE_ENDEAVOUR_MOUNT
	gboolean got_error = FALSE;
	const gchar *cmd, *tmp_dir;
	gchar *stderr_path;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	cmd = dev_ptr->command_mount;
	if(STRISEMPTY(cmd))
	{
	    EDVPlaySoundWarning(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
		"Mount Failed",
"There is no mount command defined for this device",
"You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    return;
	}

	ZipToolSetBusy(zt, TRUE);

	/* Get temp dir */
	tmp_dir = g_getenv("TMPDIR");
	if(STRISEMPTY(tmp_dir))
#ifdef P_tmpdir
	    tmp_dir = P_tmpdir;
#else
	    tmp_dir = "/tmp";   
#endif

	stderr_path = STRDUP(PrefixPaths(tmp_dir, "ziptoolXXXXXX"));
	mkstemp(stderr_path);
	if(ExecBOE(cmd, NULL, stderr_path) != 0)
	{
	    struct stat stat_buf;

	    if(!stat(stderr_path, &stat_buf))
	    {
		if(stat_buf.st_size > 0)
		{
		    EDVPlaySoundWarning(zt->ctx);
		    CDialogSetTransientFor(zt->toplevel);
		    CDialogGetResponseFile(
			"Mount Warning",
			stderr_path,
			NULL,
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_OK,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		}
	    }
	}
	else
	{
	    gchar *buf = g_strdup_printf(
"Unable to execute unmount command:\n\
\n\
    %s",
		cmd
	    );
	    EDVPlaySoundError(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
		"Mount Failed",
		cmd,
		NULL,
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	    got_error = TRUE;
	}
	unlink(stderr_path);
	g_free(stderr_path);

	ZipToolRefreshDevice(zt, dev_ptr);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error mounting device" : "Device mounted",
	    FALSE
	);
	ZipToolSetBusy(zt, FALSE);
#else   /* ZIP_TOOL_USE_ENDEAVOUR_MOUNT */
	gboolean got_error = FALSE;
	gint status;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);

	ZipToolStatusMessage(
	    zt, "Mounting device...", TRUE
	);
	status = ZipToolMount(dev_ptr);
	if(status != 0)
	{
	    const gchar *buf = ZipToolLastError();
	    if(!STRISEMPTY(buf))
	    {
		EDVPlaySoundError(zt->ctx);
		CDialogSetTransientFor(zt->toplevel);
		CDialogGetResponse(
"Error Mounting Device",
buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		    CDIALOG_ICON_ERROR,
		    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		    CDIALOG_BTNFLAG_OK
		);
		CDialogSetTransientFor(NULL);
	    }
	    got_error = TRUE;
	}

	ZipToolRefreshDevice(zt, dev_ptr);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error mounting device" : "Device mounted",
	    FALSE
	);
	if(!got_error)
	    EDVNotifyQueueObjectMounted(zt->ctx, dev_ptr->mount_path);
	EDVContextSync(zt->ctx);
	ZipToolSetBusy(zt, FALSE);
#endif  /* !ZIP_TOOL_USE_ENDEAVOUR_MOUNT */
}

/*
 *	Unmount callback.
 */
void ZipToolUnmountCB(GtkWidget *widget, gpointer data)
{
#ifdef ZIP_TOOL_USE_ENDEAVOUR_MOUNT
	gboolean got_error = FALSE;
	const gchar *cmd, *tmp_dir;
	gchar *stderr_path;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	cmd = dev_ptr->command_unmount;
	if(STRISEMPTY(cmd))
	{
	    EDVPlaySoundWarning(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
		"Unmount Failed",
"There is no unmount command defined for this device",
"You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    return;
	}

	ZipToolSetBusy(zt, TRUE);

	ZipToolStatusMessage(
	    zt, "Unmounting device...", TRUE
	);

	/* Get temp dir */       
	tmp_dir = g_getenv("TMPDIR");
	if(STRISEMPTY(tmp_dir))      
#ifdef P_tmpdir
	    tmp_dir = P_tmpdir;
#else
	    tmp_dir = "/tmp";
#endif

	stderr_path = STRDUP(PrefixPaths(tmp_dir, "ziptoolXXXXXX"));
	mkstemp(stderr_path);                                       
	if(ExecBOE(cmd, NULL, stderr_path) != 0)
	{
	    struct stat stat_buf;

	    if(!stat(stderr_path, &stat_buf))
	    {
		if(stat_buf.st_size > 0)
		{
		    EDVPlaySoundWarning(zt->ctx);
		    CDialogSetTransientFor(zt->toplevel);
		    CDialogGetResponseFile(
			"Unmount Warning",
			stderr_path,
			NULL,
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_OK,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		}
	    }
	}
	else
	{
	    gchar *buf = g_strdup_printf(
"Unable to execute unmount command:\n\
\n\
    %s",
		cmd
	    );
	    EDVPlaySoundError(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
		"Unmount Error",
		cmd,
		NULL,
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	    got_error = TRUE;
	}
	unlink(stderr_path);
	g_free(stderr_path);

	ZipToolRefreshDevice(zt, dev_ptr);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error unmounting device" : "Device unmounted",
	    FALSE
	);
	ZipToolSetBusy(zt, FALSE);
#else	/* ZIP_TOOL_USE_ENDEAVOUR_MOUNT */
	gboolean got_error = FALSE;
	gint status;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);

	ZipToolStatusMessage(
	    zt, "Unmounting device...", TRUE
	);
	status = ZipToolUnmount(dev_ptr);
	if(status != 0)
	{
	    const gchar *buf = ZipToolLastError();
	    if(!STRISEMPTY(buf))
	    {
		EDVPlaySoundError(zt->ctx);
		CDialogSetTransientFor(zt->toplevel);
		CDialogGetResponse(
"Error Unmounting Device",
buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		    CDIALOG_ICON_ERROR,
		    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		    CDIALOG_BTNFLAG_OK
		);
		CDialogSetTransientFor(NULL);
	    }
	    got_error = TRUE;
	}

	ZipToolRefreshDevice(zt, dev_ptr);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error unmounting device" : "Device unmounted",
	    FALSE
	);
	if(!got_error)
	    EDVNotifyQueueObjectUnmounted(zt->ctx, dev_ptr->mount_path);
	EDVContextSync(zt->ctx);
	ZipToolSetBusy(zt, FALSE);
#endif	/* !ZIP_TOOL_USE_ENDEAVOUR_MOUNT */
}

/*
 *	Spin down callback.
 */
void ZipToolSpinDownCB(GtkWidget *widget, gpointer data)
{
	gboolean got_error = FALSE;
	gint status;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);

	ZipToolStatusMessage(
	    zt, "Spinning down device...", TRUE
	);
	status = ZipToolSpinDown(dev_ptr);
	if(status != 0)
	{
	    const gchar *buf = ZipToolLastError();
	    if(!STRISEMPTY(buf))
	    {
		EDVPlaySoundError(zt->ctx);
		CDialogSetTransientFor(zt->toplevel);
		CDialogGetResponse(
"Error Spinning Down Device",
buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		    CDIALOG_ICON_ERROR,
		    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		    CDIALOG_BTNFLAG_OK
		);
		CDialogSetTransientFor(NULL);
	    }
	    got_error = TRUE;
	}

#if 0
/* Do not refresh after spinning down device, because otherwise it
 * would check the device and cause things to spin up again
 */
	ZipToolRefreshDevice(zt, dev_ptr);
#endif
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error spinning down device" : "Device spinned down",
	    FALSE
	);
	ZipToolSetBusy(zt, FALSE);
}

/*
 *	Eject callback.
 */
void ZipToolEjectCB(GtkWidget *widget, gpointer data)
{
	gboolean got_error = FALSE;
	gint status;
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);

	ZipToolStatusMessage(
	    zt, "Ejecting media...", TRUE
	);
	status = ZipToolEject(dev_ptr);
	if(status != 0)
	{
	    const gchar *buf = ZipToolLastError();
	    if(!STRISEMPTY(buf))
	    {
		EDVPlaySoundError(zt->ctx);
		CDialogSetTransientFor(zt->toplevel);
		CDialogGetResponse(
"Error Ejecting Media",
buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		    CDIALOG_ICON_ERROR,
		    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		    CDIALOG_BTNFLAG_OK
		);
		CDialogSetTransientFor(NULL);
	    }
	    got_error = TRUE;
	}

	ZipToolRefreshDevice(zt, dev_ptr);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt,
	    got_error ?
		"Error ejecting media" : "Media ejected",
	    FALSE
	);
	if(!got_error)
	    EDVNotifyQueueObjectUnmounted(zt->ctx, dev_ptr->mount_path);
	EDVContextSync(zt->ctx);
	ZipToolSetBusy(zt, FALSE);
}

/*
 *	Lock/unlock with password callback.
 */
void ZipToolPasswordCB(GtkWidget *widget, gpointer data)
{
	gint status;
	gchar **strv;
	gint strc;
	zip_tool_lock_state lock_state;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if((zt == NULL) || CDialogIsQuery() || PDialogIsQuery())
	    return;

	if(zt->device == NULL)
	    return;

	if(zt->device->is_mounted)
	{
	    EDVPlaySoundError(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
"Error Locking/Unlocking Media",
"The device must be unmounted first, before\n\
locking or unlocking the media.",
		NULL,
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    return;
	}

	ZipToolSetBusy(zt, TRUE);

	/* Get current lock state */
	lock_state = ZipToolDeviceIsProtected(zt->device);
	switch(lock_state)
	{
	  case ZIP_TOOL_LOCK_STATE_LOCKED_PASSWORD:
	    /* Already locked with password, prompt for unlock
	     * with password
	     */
	    ZipToolStatusMessage(
		zt, "Unlocking media with password...", TRUE
	    );
	    PDialogDeleteAllPrompts();
	    PDialogAddPromptPassword(
		NULL, "Password:", NULL
	    );
	    PDialogSetSize(300, -1);
	    PDialogSetTransientFor(zt->toplevel);
	    strv = PDialogGetResponse(
		"Unlock Media",
"A password is required to unlock this media,\n\
please enter the password below.",
		NULL,
		PDIALOG_ICON_SECURITY,
		"Unlock", "Cancel",
		PDIALOG_BTNFLAG_SUBMIT | PDIALOG_BTNFLAG_CANCEL,
		PDIALOG_BTNFLAG_SUBMIT,
		&strc
	    );
	    PDialogSetTransientFor(NULL);
	    if((strv != NULL) && (strc >= 1))
	    {
		const gchar *password = strv[0];
		status = ZipToolUnlock(zt->device, password);
		if(status)
		{
		    const gchar *buf =
"General error encountered while unlocking media";
		    switch(status)
		    {
		      case -4:
			buf =
"Bad password";
			break;

		      case -3:
			buf =
"Unable to execute unlock command";
			break;

		      case -2:
			buf =
"Invalid device reference value";
			break;
		    }
		    EDVPlaySoundError(zt->ctx);
		    CDialogSetTransientFor(zt->toplevel);
		    CDialogGetResponse(
			"Error Unlocking Media",
			buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
			CDIALOG_ICON_ERROR,
			CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		    ZipToolStatusMessage(
			zt, "Failed to unlock media", FALSE
		    );
		}
		else
		{
		    ZipToolStatusMessage(
			zt, "Unlocked media", FALSE
		    );
		}
	    }
	    else
	    {
		ZipToolStatusMessage(
		    zt, "Unlock media aborted", FALSE
		);
	    }
	    break;

	  case ZIP_TOOL_LOCK_STATE_LOCKED:
	    /* Locked (without password), just unlock */
	    ZipToolStatusMessage(
		zt, "Unlocking media...", TRUE
	    );
	    status = ZipToolUnlock(zt->device, NULL);
	    if(status)
	    {
		const gchar *buf =
"General error encountered while unlocking media";
		switch(status)
		{
		  case -4:
		    buf =
"Bad password";
		    break;

		  case -3:
		    buf =
"Unable to execute unlock command";
		    break;

		  case -2:
		    buf =
"Invalid device reference value";
		    break;
		}
		EDVPlaySoundError(zt->ctx);
		CDialogSetTransientFor(zt->toplevel);
		CDialogGetResponse(
		    "Error Unlocking Media",
		    buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		    CDIALOG_ICON_ERROR,
		    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		    CDIALOG_BTNFLAG_OK
		);
		CDialogSetTransientFor(NULL);
		ZipToolStatusMessage(
		    zt, "Failed to unlock media", FALSE
		);
	    }
	    else
	    {
		ZipToolStatusMessage(
		    zt, "Unlocked media", FALSE
		);
	    }
	    break;

	  case ZIP_TOOL_LOCK_STATE_UNLOCKED:
	    /* Unlocked, prompt for lock with optional password */
	    ZipToolStatusMessage(
		zt, "Locking media...", TRUE
	    );
	    PDialogDeleteAllPrompts();
	    PDialogAddPromptPassword(
		NULL, "Password:", NULL
	    );
	    PDialogAddPromptPassword(
		NULL, "Confirm:", NULL
	    );
	    PDialogSetSize(300, -1);
	    PDialogSetTransientFor(zt->toplevel);
	    strv = PDialogGetResponse(
		"Lock Media",
"Enter a new password that will be used to\n\
lock this media, or leave it blank to set this\n\
media as read only",
		NULL,
		PDIALOG_ICON_SECURITY,
		"Lock", "Cancel",
		PDIALOG_BTNFLAG_SUBMIT | PDIALOG_BTNFLAG_CANCEL,
		PDIALOG_BTNFLAG_SUBMIT,
		&strc
	    );
	    PDialogSetTransientFor(NULL);
	    if((strv != NULL) && (strc >= 2))
	    {
		const gchar *password = strv[0];
		const gchar *confirm = strv[1];
		if(!STRISEMPTY(password))
		{
		    /* Password given, now make sure password matches
		     * with confirm
		     */
		    if((confirm != NULL) ?
			!strcmp(password, confirm) : FALSE
		    )
			status = ZipToolLock(zt->device, password);
		    else
			status = -100;
		}
		else
		{
		    /* No password, just lock media */
		    status = ZipToolLock(zt->device, NULL);
		}
		if(status)
		{
		    const gchar *buf =
"General error encountered while locking media";
		    switch(status)
		    {
		      case -100:
			buf =
"Passwords do not match";
			break;

		      case -4:
			buf =
"Unable to lock media";
			break;

		      case -3:
			buf =
"Unable to execute lock command";
			break;

		      case -2:
			buf =
"Invalid device reference value";
			break;
		    }
		    EDVPlaySoundError(zt->ctx);
		    CDialogSetTransientFor(zt->toplevel);
		    CDialogGetResponse(
			"Error Locking Media",
			buf,
"Please check to make sure that you have\n\
sufficient permission to run ziptool and access\n\
the device.  Also make sure that the device is\n\
properly defined from Endeavour Mark II.\n\
\n\
You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
			CDIALOG_ICON_ERROR,
			CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		    ZipToolStatusMessage(
			zt, "Failed to lock media", FALSE
		    );
		}
		else
		{
		    ZipToolStatusMessage(
			zt, "Locked media", FALSE
		    );
		}
	    }
	    else
	    {
		ZipToolStatusMessage(
		    zt, "Lock media aborted", FALSE
		);
	    }
	    break;
	}

	ZipToolRefreshDevice(zt, zt->device);
	ZipToolUpdate(zt);
	ZipToolSetBusy(zt, FALSE);
}

/*
 *	Run file browser to browse media callback.
 */
void ZipToolBrowseCB(GtkWidget *widget, gpointer data)
{
	edv_device_struct *dev_ptr;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	dev_ptr = zt->device;
	if(dev_ptr == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);
	EDVWindowBrowserNew(zt->ctx, dev_ptr->mount_path);
	ZipToolSetBusy(zt, FALSE);
}

/*
 *	Refresh calback.
 */
void ZipToolRefreshCB(GtkWidget *widget, gpointer data)
{
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);
	ZipToolStatusMessage(
	    zt, "Refreshing device information...", TRUE
	);
	ZipToolRefreshDevice(zt, zt->device);
	ZipToolUpdate(zt);
	ZipToolStatusMessage(
	    zt, "Refreshed device information", FALSE
	);
	ZipToolSetBusy(zt, FALSE);
}

/*
 *	FSCK callback.
 */
void ZipToolFSCKCB(GtkWidget *widget, gpointer data)
{
	const gchar *cmd;
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	ZipToolSetBusy(zt, TRUE);

	cmd = (zt->device != NULL) ? zt->device->command_check : NULL;
	if((cmd != NULL) ? (*cmd != '\0') : FALSE)
	{
	    Exec(cmd);
	}
	else
	{
	    EDVPlaySoundWarning(zt->ctx);
	    CDialogSetTransientFor(zt->toplevel);
	    CDialogGetResponse(
		"FSCK Program Undefined",
"There is no defined command to run the FSCK program\n\
for this device.",
"To define a command to run the FSCK program for this\n\
device, run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and define the Check Command to run the FSCK program.\n\
Then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	}

	ZipToolSetBusy(zt, FALSE);
}

/*
 *	Close button callback.
 */
void ZipToolCloseBtnCB(GtkWidget *widget, gpointer data)
{
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;

	ZipToolUnmap(zt);
}

/*
 *	Exit callback.
 */
void ZipToolExitCB(GtkWidget *widget, gpointer data)
{
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(zt == NULL)
	    return;
/* Need to work on this */
	ZipToolUnmap(zt);
}

