#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <gtk/gtk.h>
#include <unistd.h>

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

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

#include "fsckmanager.h"
#include "fsckcb.h"
#include "config.h"


gint FSCKManagerButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
gint FSCKManagerDiskIconExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
);

gint FSCKManagerFSCKTOCB(gpointer data);

gint FSCKManagerDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

static void FSCKManagerDoStart(
	fsck_manager_struct *fm, gint dev_num
);
void FSCKManagerStartCB(GtkWidget *widget, gpointer data);
void FSCKManagerStopCB(GtkWidget *widget, gpointer data);
void FSCKManagerClearCB(GtkWidget *widget, gpointer data);
void FSCKManagerCloseBtnCB(GtkWidget *widget, gpointer data);

void FSCKManagerSelectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
);
void FSCKManagerUnselectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	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)

#define KILL(p,s)       (((p) > 0) ? kill((p), (s)) : -1)
#define UNLINK(p)       (((p) != NULL) ? unlink(p) : -1)


/*
 *	FSCK Manager Devices GtkCList "button_press_event" signal
 *	callback.
 */
gint FSCKManagerButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint row, column;
	GtkWidget *w;
	GtkCList *clist;
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return(status);

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

	if(!gtk_clist_get_selection_info(
	    clist, button->x, button->y, &row, &column
	))
	{
	    row = -1;
	    column = 0;
	}

	switch((gint)button->type)
	{
	  case GDK_2BUTTON_PRESS:
	    switch(button->button)
	    {
	      case 1:
		/* Start FSCK */
		FSCKManagerStartCB(widget, data);
		status = TRUE;
		break;
	    }
	    break;

	  case GDK_BUTTON_PRESS:
	    switch(button->button)
	    {
	      case 3:
		gtk_clist_freeze(clist);
		if(!(button->state & GDK_CONTROL_MASK) &&
		   !(button->state & GDK_SHIFT_MASK)
		)
		    gtk_clist_unselect_all(clist);
		clist->focus_row = row;
		gtk_clist_select_row(clist, row, column);
		gtk_clist_thaw(clist);

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

	return(status);
}

/*
 *	Redraws FSCK Manager's disk animation icon.
 */
gint FSCKManagerDiskIconExposeCB(
	GtkWidget *widget, GdkEventExpose *expose, gpointer data
)
{
	gint i, width, height;
	GdkWindow *window;
	GdkPixmap *pixmap, *bg_pixmap;
	GdkGC *gc;
	GtkStateType state;
	GtkStyle *style;
	GtkWidget *w;
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return(FALSE);

	w = fm->disk_anim_da;
	if(w == NULL)
	    return(FALSE);

	gc = fm->gc;
	window = w->window;
	pixmap = fm->disk_anim_pm;
	if((gc == NULL) || (window == NULL) || (pixmap == NULL))
	    return(FALSE);

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


	/* Begin drawing */
	bg_pixmap = style->bg_pixmap[state];
	if(bg_pixmap != NULL)
	{
	    gint tx, ty;

	    gdk_gc_set_tile(gc, bg_pixmap);
	    gdk_gc_set_fill(gc, GDK_TILED);
	    gdk_window_get_position(window, &tx, &ty);
	    gdk_gc_set_ts_origin(gc, -tx, -ty);
	}
	else
	{
	    gdk_gc_set_fill(gc, GDK_SOLID);
	}
	gdk_gc_set_clip_mask(gc, NULL);

	/* Draw background to cover entire pixmap buffer */
	gdk_gc_set_foreground(gc, &style->bg[state]);
	gdk_draw_rectangle(
	    (GdkDrawable *)pixmap, gc, TRUE,
	    0, 0, width, height
	);

	gdk_gc_set_fill(gc, GDK_SOLID);
	gdk_gc_set_ts_origin(gc, 0, 0);

	/* Find which disk animation icon we want to draw */
	i = fm->last_disk_icon_num;
	if((i >= 0) && (i < fm->total_disk_icons))
	{
	    gdk_gc_set_clip_mask(gc, fm->disk_icon_mask[i]);

	    if(fm->disk_icon_pixmap[i] != NULL)
		gdk_draw_pixmap(
		    pixmap, gc,
		    fm->disk_icon_pixmap[i],
		    0, 0,
		    0, 0,
		    width, height
		);
	}

	/* At this point the pixmap buffer has been drawn, now put the
	 * pixmap buffer to the drawing area's window.
	 */
	gdk_draw_pixmap(
	    window, gc,
	    pixmap,
	    0, 0,
	    0, 0,
	    width, height
	);

	return(TRUE);
}

/*
 *	FSCK Manager FSCK timeout callback.
 */
gint FSCKManagerFSCKTOCB(gpointer data)
{
	FILE *fp;
	pid_t p;
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return(FALSE);

/* Resets the stop count, restores progress bar to non-checking state,
 * sets fsck_toid=0, closes the fsck stdout and stderr streams, removes
 * fsck stdout and stderr tempory files, removes the current device from
 * the queued list of devices, and starts another check process for the
 * next queued device (if any)
 */
#define DO_PROCESS_END	{			\
						\
 fm->stop_count = 0;				\
						\
 FSCKManagerSetProgress(fm, 0.0f, FALSE);	\
						\
 fm->fsck_toid = 0;				\
						\
 KILL(fm->fsck_pid, SIGTERM);			\
 fm->fsck_pid = 0;				\
						\
 FClose(fm->stdout_fp);				\
 fm->stdout_fp = NULL;				\
 FClose(fm->stderr_fp);				\
 fm->stderr_fp = NULL;				\
						\
 UNLINK(fm->stdout_path);			\
 g_free(fm->stdout_path);			\
 fm->stdout_path = NULL;			\
						\
 UNLINK(fm->stderr_path);			\
 g_free(fm->stderr_path);			\
 fm->stderr_path = NULL;			\
						\
 /* Another queued device to check? */		\
 if(fm->queued_device_to_check != NULL) {	\
  /* Remove the last queued device from the	\
   * list */					\
  GList *glist = fm->queued_device_to_check;	\
  GList *glist2 = NULL;				\
  if(glist->next != NULL)			\
   glist2 = g_list_copy(glist->next);		\
  fm->queued_device_to_check = glist2;		\
  g_list_free(glist);				\
						\
  /* Check if there is a current device to	\
   * check */					\
  glist = fm->queued_device_to_check;		\
  if(glist != NULL) {				\
   gint dev_num = (gint)gtk_clist_get_row_data(	\
    (GtkCList *)fm->devices_clist,		\
    (gint)glist->data	/* Row index */		\
   );						\
   FSCKManagerDoStart(fm, dev_num);		\
  }						\
  else						\
  {						\
   /* Done checking all devices */		\
   gtk_widget_show_raise(fm->toplevel);		\
   EDVPlaySoundCompleted(fm->ctx);		\
   FSCKManagerAppendMessage(			\
    fm, fm->text_font, NULL, NULL,		\
    "Check done.\n", TRUE			\
   );						\
  }						\
 }						\
						\
 FSCKManagerUpdateMenus(fm);			\
						\
}

	/* Need to stop? */
	if(fm->stop_count > 0)
	{
	    /* Remove all queued devices */
	    g_list_free(fm->queued_device_to_check);
	    fm->queued_device_to_check = NULL;

	    /* Print interrupted message */
	    FSCKManagerAppendMessage(
		fm, fm->text_font, &fm->error_color, NULL,
		"\n*** Check Interrupted! ***\n",
		TRUE
	    );

	    DO_PROCESS_END
	    return(FALSE);
	}


	/* Read from stdout output file */
	fp = fm->stdout_fp;
	if(fp != NULL)
	{
	    gint bytes_read = 1;
	    gchar buf[1024];

	    while(bytes_read > 0)
	    {
		bytes_read = (gint)fread(
		    (char *)buf, sizeof(gchar), sizeof(buf), fp
		);
		if(bytes_read > 0)
		{
		    gint len = bytes_read;
		    if(len >= sizeof(buf))
			len = sizeof(buf) - 1;

		    buf[len] = '\0';

		    FSCKManagerAppendMessage(
			fm,
			fm->text_font,
			NULL, NULL,
			buf, TRUE
		    );
		}
	    }
	}

	/* Read from stderr output file */
	fp = fm->stderr_fp;
	if(fp != NULL)
	{
	    gint bytes_read = 1;
	    gchar buf[1024];

	    while(bytes_read > 0)
	    {
		bytes_read = (gint)fread(
		    (char *)buf, sizeof(gchar), sizeof(buf), fp
		);
		if(bytes_read > 0)
		{
		    gint len = bytes_read;
		    if(len >= sizeof(buf))
			len = sizeof(buf) - 1;

		    buf[len] = '\0';

		    FSCKManagerAppendMessage(
			fm,
			fm->text_font,
			&fm->error_color, NULL,
			buf, TRUE
		    );
		}
	    }
	}


	/* Process no longer exists? */
	p = fm->fsck_pid;
	if(!ExecProcessExists(p))
	{
	    DO_PROCESS_END
	    return(FALSE);
	}

	/* Update progress bar and disk animation icon */
	FSCKManagerSetProgress(fm, -1.0f, FALSE);

	/* Increment icon animation */
	fm->last_disk_icon_num++;
	if(fm->last_disk_icon_num >= fm->total_disk_icons)
	    fm->last_disk_icon_num = 0;
	FSCKManagerDiskIconExposeCB(fm->disk_anim_da, NULL, fm);

	return(TRUE);
#undef DO_PROCESS_END
}


/*
 *	FSCK Manager toplevel GtkWindow "delete_event" signal callback.
 */
gint FSCKManagerDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	FSCKManagerCloseBtnCB(widget, data);

	return(TRUE);
}


/*
 *	Sets up the fsck manager window to start checking the given
 *	device.  The fsck program will be runned and timeout callback
 *	set.
 *
 *	Note that his function still needs work to be truely
 *	iterateable.
 */
static void FSCKManagerDoStart(
	fsck_manager_struct *fm, gint dev_num
)
{
	const gchar *fs_type_name, *fsck_prog_pfx;
	gboolean bad_blocks = FALSE, verbose = FALSE;
	GtkWidget *w;
	pid_t p;
	const gchar *tmp_dir;
	gchar *cmd, *fsck_prog, *startup_message;
	edv_device_struct *dev_ptr;


	if(fm == NULL)
	    return;

	/* Get fsck parameters */
	w = fm->bad_blocks_check;
	if(w != NULL)
	    bad_blocks = GTK_TOGGLE_BUTTON(w)->active;
	w = fm->verbose_check;
	if(w != NULL)
	    verbose = GTK_TOGGLE_BUTTON(w)->active;

	/* Get pointer to device */
	if((dev_num < 0) || (dev_num >= fm->total_devices))
	    return;
	dev_ptr = fm->device[dev_num];
	if(dev_ptr == NULL)
	    return;

	/* Get conical name of file system */
	fs_type_name = EDVDeviceGetFSStringFromNumber(dev_ptr->fs_type);


	/* Check if this device is already mounted, in which case we
	 * cannot use fsck on it
	 */
	if(dev_ptr->is_mounted)
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Device `%s' is currently mounted.\n\
\n\
You must unmount the device before checking.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"El artefacto `%s' se monta actualmente.\n\
\n\
Usted debe unmount el artefacto primero antes de verificar.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"`%s' d'appareil est actuellement monté.\n\
\n\
Vous devez unmount l'appareil premièrement avant de vérifiant.",
#endif
		dev_ptr->device_path
	    );
	    EDVPlaySoundWarning(fm->ctx);
	    CDialogSetTransientFor(fm->toplevel);
	    CDialogGetResponse(
#ifdef PROG_LANGUAGE_ENGLISH
		"Check Failed",
		buf,
"The device must be unmounted before it can be\n\
checked, this is to prevent errors due to read/writes\n\
that may occure during the checking process.\n\
Use Endeavour or another application to unmount\n\
the device first.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
		"El Cheque Falló",
		buf,
"El artefacto se debe unmounted antes se puede verificar,\n\
esto deberá prevenir los errores debido a lectura/escritura\n\
que pueden occure durante el proceso que verifica. Use la\n\
Endeavour otra aplicación al unmount el artefacto primero.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
		"Le Chèque A Echoué",
		buf,
"L'appareil doit être unmounted avant d'il peut être\n\
vérifié, ceci sera obligé à empêcher des erreurs grâce aux\n\
lecture/écriture qui peut occure pendant le procédé qui\n\
vérifiant. Utilise Endeavour ou un autre a la premierement.",
#endif
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	    return;
	}


	/* Check if there is already an fsck process running, in which
	 * case we will increment the stop count so the next call to
	 * the timeout callback stops it.
	 */
	p = fm->fsck_pid;
	if(p != 0)
	{
	    if(ExecProcessExists(p))
	    {
		fm->stop_count++;
		return;
	    }
	}

	/* If there is already an existing fsck process timeout, then
	 * we will increment the stop count so the next call to the
	 * timeout callback stops it
	 */
	if(fm->fsck_toid > 0)
	{
	    fm->stop_count++;
	    return;
	}

	cmd = NULL;
	fsck_prog = NULL;
	fsck_prog_pfx = "/sbin/fsck";

	/* Format command string by handling of the fs type */
	switch(dev_ptr->fs_type)
	{
	  case EDV_FS_TYPE_MSDOS:
	  case EDV_FS_TYPE_VFAT:
	    fsck_prog = g_strdup_printf(
		"%s.%s",
		fsck_prog_pfx, "msdos"
	    );
	    cmd = g_strdup_printf(
		"%s -a %s %s %s",
		fsck_prog,
		verbose ? "-v" : "",
		bad_blocks ? "-t" : "",
		dev_ptr->device_path
	    );
	    break;

	  case EDV_FS_TYPE_EXT2_OLD:
	  case EDV_FS_TYPE_EXT2:
	    fsck_prog = g_strdup_printf(
		"%s.%s",
		fsck_prog_pfx, "ext2"
	    );
	    cmd = g_strdup_printf(
		"%s -p %s %s -f %s",
		fsck_prog,
		verbose ? "-v" : "",
		bad_blocks ? "-c" : "",
		dev_ptr->device_path
	    );
	    break;

	  case EDV_FS_TYPE_MINIX_ORIG:
	  case EDV_FS_TYPE_MINIX:
	    fsck_prog = g_strdup_printf(
		"%s.%s",
		fsck_prog_pfx, "minix"
	    );
	    cmd = g_strdup_printf(
		"%s -a -v -f %s",
		fsck_prog, dev_ptr->device_path
	    );
	    break;

	}
	/* Unable to generate command? */
	if(cmd == NULL)
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"This program does not currently support the\n\
file system `%s'.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Este programa no sostiene actualmente el\n\
sistema del archivo `%s'",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Ce programme ne soutient pas actuellement le\n\
`%s' de système de fichier",
#endif
		fs_type_name
	    );
	    EDVPlaySoundWarning(fm->ctx);
	    CDialogSetTransientFor(fm->toplevel);
	    CDialogGetResponse(
#ifdef PROG_LANGUAGE_ENGLISH
		"Check Failed",
		buf,
"The device that you are trying to check has a\n\
file system type that this program does not\n\
currently recognize and is unable to issue the\n\
appropriate fsck command to check that device.",
#endif
#ifdef PROG_LANGUAGE_SPANISH
		"El Cheque Falló",
		buf,
"El artefacto que usted tratan de verificar tiene\n\
un tipo de sistema de archivo que este programa no\n\
reconoce actualmente y es incapaz de publicar la\n\
orden apropiada de fsck para verificar ese\n\
artefacto.",
#endif
#ifdef PROG_LANGUAGE_FRENCH
		"Le Chèque A Echoué",
		buf,
"L'appareil que vous essayez vérifier a un type de\n\
système de fichier que ce programme ne reconnaît\n\
pas actuellement et est incapable pour distribuer\n\
l'ordre de fsck correspondant pour vérifier cet\n\
appareil.",
#endif
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);

	    g_free(cmd);
	    g_free(fsck_prog);

	    return;
	}

startup_message = g_strdup_printf(
"-------------------------------------------------\n\
Checking device %s...\n",
 dev_ptr->device_path
);
FSCKManagerAppendMessage(
 fm, fm->text_font, NULL, NULL,
 startup_message,
 TRUE
);
g_free(startup_message);
startup_message = NULL;


	/* Generate tempory names for stdout and stderr paths */
	tmp_dir = g_getenv("TMPDIR");
	if(STRISEMPTY(tmp_dir))
#ifdef P_tmpdir
	    tmp_dir = P_tmpdir;
#else
	    tmp_dir = "/tmp";   
#endif

	g_free(fm->stdout_path);
	fm->stdout_path = STRDUP(PrefixPaths(tmp_dir, "fsckXXXXXX"));
	mkstemp(fm->stdout_path);

	g_free(fm->stderr_path);
	fm->stderr_path = STRDUP(PrefixPaths(tmp_dir, "fsckXXXXXX"));
	mkstemp(fm->stderr_path);

	/* Execute */
	fm->fsck_pid = p = ExecOE(
	    cmd,
	    fm->stdout_path,
	    fm->stderr_path
	);
	if(p == 0)
	{
	    gchar *buf = g_strdup_printf(
#ifdef PROG_LANGUAGE_ENGLISH
"Unable to execute the fsck command:\n\
\n\
    %s",
#endif
#ifdef PROG_LANGUAGE_SPANISH
"Incapaz de ejecutar la orden de fsck:\n\
\n\
    %s",
#endif
#ifdef PROG_LANGUAGE_FRENCH
"Incapable pour exécuter l'ordre de fsck:\n\
\n\
    %s",
#endif
		cmd
	    );
	    EDVPlaySoundError(fm->ctx);
	    CDialogSetTransientFor(fm->toplevel);
	    CDialogGetResponse(
#ifdef PROG_LANGUAGE_ENGLISH
		"Check Failed",
		buf,
		NULL,
#endif
#ifdef PROG_LANGUAGE_SPANISH
		"El Cheque Falló",
		buf,
		NULL
#endif
#ifdef PROG_LANGUAGE_FRENCH
		"Le Chèque A Echoué",
		buf,
		NULL,
#endif
		CDIALOG_ICON_ERROR,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);

	    /* Execute failed */
	    UNLINK(fm->stdout_path);
	    g_free(fm->stdout_path);
	    fm->stdout_path = NULL;

	    UNLINK(fm->stderr_path);
	    g_free(fm->stderr_path);
	    fm->stderr_path = NULL;

	    g_free(cmd);
	    g_free(fsck_prog);

	    return;
	}

	/* Open stdout and stderr paths */
	FClose(fm->stdout_fp);
	fm->stdout_fp = FOpen(fm->stdout_path, "rb");

	FClose(fm->stderr_fp);
	fm->stderr_fp = FOpen(fm->stderr_path, "rb");


	/* Schedual timeout callback to monitor this process */
	fm->fsck_toid = gtk_timeout_add(
	    50l,		/* ms */
	    FSCKManagerFSCKTOCB, fm
	);



	FSCKManagerUpdateMenus(fm);

	g_free(cmd);
	g_free(fsck_prog);
}


/*
 *      Start callback.
 */
void FSCKManagerStartCB(GtkWidget *widget, gpointer data)
{
	gint dev_num;
	GList *glist;
	GtkCList *clist;
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

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

	/* Process already running? */
	if(fm->fsck_pid != 0)
	    return;

	/* Update the mount states of each device */
	EDVDevicesListUpdateMountStates(
	    fm->device, fm->total_devices
	);

	/* Clear output */
	FSCKManagerClearCB(fm->clear_btn, fm);

	/* Clear old queued devices list to check */
	if(fm->queued_device_to_check != NULL)
	{
	    g_list_free(fm->queued_device_to_check);
	    fm->queued_device_to_check = NULL;
	}

	/* Copy list of selected rows on the devices clist as a list
	 * of queued devices to check.
	 */
	if(clist->selection != NULL)
	    fm->queued_device_to_check = g_list_copy(clist->selection);

	/* Start checking the first queued device */
	glist = fm->queued_device_to_check;
	if(glist != NULL)
	{
	    dev_num = (gint)gtk_clist_get_row_data(
		clist,
		(gint)glist->data	/* Row index */
	    );
	    FSCKManagerDoStart(fm, dev_num);
	}
}

/*
 *	Stop callback.
 */
void FSCKManagerStopCB(GtkWidget *widget, gpointer data)
{
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

	fm->stop_count++;
}

/*
 *	Clear callback.
 */
void FSCKManagerClearCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *w;
	GtkEditable *editable;
	GtkText *text;
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

	w = fm->output_text;
	if(w == NULL)
	    return;

	editable = (GtkEditable *)w;
	text = (GtkText *)w;

	gtk_editable_delete_text(editable, 0, -1);
}

/*
 *	Close button callback.
 */
void FSCKManagerCloseBtnCB(GtkWidget *widget, gpointer data)
{
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

	FSCKManagerUnmap(fm);
}


/*
 *	Devices clist "select_row" signal callback.
 */
void FSCKManagerSelectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
)
{
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

	/* Check which clist this event is for */
	if(GTK_WIDGET(clist) == fm->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, column,        /* Row, column */
		    0.5, 0.0            /* Row, column */
		);

	    FSCKManagerUpdateMenus(fm);
	}
}

/*
 *      Devices clist "unselect_row" signal callback.
 */
void FSCKManagerUnselectRowCB(
	GtkCList *clist, gint row, gint column, GdkEvent *event,
	gpointer data
)
{
	fsck_manager_struct *fm = FSCK_MANAGER(data);
	if(fm == NULL)
	    return;

	/* Check which clist this event is for */
	if(GTK_WIDGET(clist) == fm->devices_clist)
	{

	    FSCKManagerUpdateMenus(fm);
	}
}
