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

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

#include "../guiutils.h"

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

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

#include "config.h"

#include "../images/icon_run_20x20.xpm"
#include "../images/icon_stop_20x20.xpm"
#include "../images/icon_clear_20x20.xpm"
#include "../images/icon_close_20x20.xpm"

#include "disk_icon_00_48x48.xpm"
#include "disk_icon_01_48x48.xpm"
#include "disk_icon_02_48x48.xpm"


void FSCKManagerSetDevices(
	fsck_manager_struct *fm,
	edv_device_struct **device, gint total_devices
);
void FSCKManagerAppendMessage(
	fsck_manager_struct *fm,
	GdkFont *font, GdkColor *color_fg, GdkColor *color_bg,
	const gchar *text,
	gboolean allow_auto_scroll
);

fsck_manager_struct *FSCKManagerNew(edv_context_struct *ctx);
void FSCKManagerUpdateMenus(fsck_manager_struct *fm);
void FSCKManagerSetBusy(fsck_manager_struct *fm, gboolean is_busy);
void FSCKManagerSetProgress(
	fsck_manager_struct *fm, gfloat value,
	gboolean allow_gtk_iteration
);
void FSCKManagerMap(fsck_manager_struct *fm);
void FSCKManagerUnmap(fsck_manager_struct *fm);
void FSCKManagerDelete(fsck_manager_struct *fm);


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


#define FSCK_MANAGER_WIDTH	640
#define FSCK_MANAGER_HEIGHT	480

#define FSCK_MANAGER_BTN_WIDTH	(100 + (2 * 3))
#define FSCK_MANAGER_BTN_HEIGHT	(30 + (2 * 3))

#define EDV_STATUS_BAR_HEIGHT   26


/*
 *	Loads the given list of devices to the FSCK Manager's
 *	devices listing and devices clist.
 *
 *	The given list of devices should not be referenced again
 *	after this call.
 */
void FSCKManagerSetDevices(
	fsck_manager_struct *fm,
	edv_device_struct **device, gint total_devices
)
{
	gint i, row, column;
	const gchar *cell_text;
	GdkBitmap *mask;
	GdkPixmap *pixmap;
	GtkCList *clist;
	edv_device_struct *dev;
	gchar *strv[4];

#define DO_DEALLOCATE_INPUT_LIST	{	\
 for(i = 0; i < total_devices; i++)		\
  EDVDeviceDelete(device[i]);			\
 g_free(device);				\
}

	if(fm == NULL)
	{
	    DO_DEALLOCATE_INPUT_LIST
	    return;
	}

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

	gtk_clist_freeze(clist);

	/* Clear clist and delete Devices list */
	gtk_clist_clear(clist);
	for(i = 0; i < fm->total_devices; i++)
	    EDVDeviceDelete(fm->device[i]);
	g_free(fm->device);

	/* Assign the specified Devices list */
	fm->device = device;
	fm->total_devices = total_devices;

	/* Update the clist's listing of devices */
	for(i = 0; i < 4; i++)
	    strv[i] = "";
	for(i = 0; i < fm->total_devices; i++)
	{
	    dev = fm->device[i];
	    if(dev == NULL)
		continue;

	    /* Hide this device from general listing? */
	    if(EDV_DEVICE_IS_UNLISTED(dev))
		continue;

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

	    /* Load icon */
	    pixmap = GDK_PIXMAP_NEW_FROM_XPM_FILE(
		&mask,
		dev->small_icon_file[EDV_DEVICE_ICON_STATE_STANDARD]
	    );

	    /* Set name cell */
	    column = 0;
	    cell_text = dev->name;
	    if((cell_text == NULL) && (dev->device_path != NULL))
	    {
		cell_text = strrchr(dev->device_path, DIR_DELIMINATOR);
		if(cell_text != NULL)
		    cell_text++;
		else
		    cell_text = dev->device_path;
	    }
	    if(cell_text == NULL)
		cell_text = "(null)";

	    if(pixmap != NULL)
		gtk_clist_set_pixtext(
		    clist, row, column, cell_text,
		    EDV_LIST_PIXMAP_TEXT_SPACING,
		    pixmap, mask
		);
	    else
		gtk_clist_set_text(
		    clist, row, column, cell_text
		);

	    /* Set device path cell */
	    column = 1;
	    cell_text = dev->device_path;
	    if(cell_text == NULL)
		cell_text = "(null)";

	    gtk_clist_set_text(
		clist, row, column, cell_text
	    );


	    /* Set mount path cell */
	    column = 2;
	    cell_text = dev->mount_path;
	    if(cell_text == NULL)
		cell_text = "(null)";

	    gtk_clist_set_text(
		clist, row, column, cell_text
	    );


	    /* Set fstype cell */
	    column = 3;
	    gtk_clist_set_text(
		clist, row, column,
		EDVDeviceGetFSNameFromType(dev->fs_type)
	    );


	    /* Set row data of this new row to be a gint indicating
	     * the device listing index of the device that this row
	     * is displaying.
	     */
	    gtk_clist_set_row_data(clist, row, (gpointer)i);
	}

	gtk_clist_thaw(clist);

	FSCKManagerUpdateMenus(fm);

#undef DO_DEALLOCATE_INPUT_LIST
}

/*
 *	Appends the given text to the output text on the fsck
 *	manager window.
 */
void FSCKManagerAppendMessage(
	fsck_manager_struct *fm,
	GdkFont *font, GdkColor *color_fg, GdkColor *color_bg,
	const gchar *text,
	gboolean allow_auto_scroll
)
{
	GtkWidget *w;
	GtkEditable *editable;
	GtkText *text_w;


	if((fm == NULL) || (text == NULL))
	    return;

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

	editable = GTK_EDITABLE(w);
	text_w = GTK_TEXT(w);

	gtk_text_insert(
	    text_w, font, color_fg, color_bg, text, -1
	);

	if(allow_auto_scroll)
	{
	    GtkAdjustment *adj = text_w->vadj;

	    /* Need to scroll down to show text not visible? */
	    if(((adj->upper - adj->lower) > adj->page_size) &&
	       ((adj->value + adj->page_size) < adj->upper)
	    )
	    {
		adj->value = adj->upper - adj->page_size;
		if(adj->value < adj->lower)
		    adj->value = adj->lower;
		gtk_signal_emit_by_name(
		    GTK_OBJECT(adj), "value_changed"
		);
	    }
	}
}


/*
 *	Creates a new FSCK Manager.
 */
fsck_manager_struct *FSCKManagerNew(edv_context_struct *ctx)
{
	const gint	border_major = 5,
			border_minor = 2;
	gint i;
	GdkColor *c;
	GdkColormap *colormap;
	GtkAdjustment *adj;
	GdkWindow *window;
	GtkStyle *style;
	GtkWidget	*w, *parent, *parent2, *parent3, *parent4,
			*parent5, *main_vbox;
	GtkAccelGroup *accelgrp;
	GtkEditable *editable;
	GtkText *text;
	GtkCList *clist;
	gchar *heading[4];
	fsck_manager_struct *fm = FSCK_MANAGER(g_malloc0(
	    sizeof(fsck_manager_struct)
	));
	if(fm == NULL)
	    return(NULL);

	/* Reset values */
	fm->map_state = FALSE;
	fm->ctx = ctx;
	fm->busy_count = 0;
	fm->freeze_count = 0;
	fm->queued_device_to_check = NULL;
	fm->fsck_toid = 0;
	fm->fsck_pid = 0;
	fm->stdout_fp = NULL;
	fm->stderr_fp = NULL;
	fm->stdout_path = NULL;
	fm->stderr_path = NULL;
	fm->stop_count = 0;
	fm->progress_pos_last = -1.0f;
	fm->device = NULL;
	fm->total_devices = 0;

	/* Load cursors */
	fm->busy_cur = gdk_cursor_new(GDK_WATCH);
	fm->text_cur = gdk_cursor_new(GDK_XTERM);

	/* Keyboard accelerator group */
	fm->accelgrp = accelgrp = gtk_accel_group_new();

	/* Load fonts */
	fm->text_font = gdk_font_load(
	    PROG_GTK_FONT_NAME_TEXT_EDITABLE
	);

	/* Toplevel */
	fm->toplevel = w = gtk_window_new(GTK_WINDOW_DIALOG);
	gtk_widget_set_usize(w, FSCK_MANAGER_WIDTH, FSCK_MANAGER_HEIGHT);
	gtk_window_set_title(GTK_WINDOW(w), PROG_NAME);
	gtk_window_set_wmclass(                        
	    GTK_WINDOW(w), "fsck", PROG_NAME
	);
	gtk_widget_realize(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 **)disk_icon_00_48x48_xpm);
	}
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(FSCKManagerDeleteEventCB), fm
	);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_window_add_accel_group(GTK_WINDOW(w), accelgrp);
	parent = w;

	/* Main vbox */
	fm->main_vbox = main_vbox = w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;

	/* Hbox for devices clist, start and stop buttons */
	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_widget_show(w);
	parent2 = w;

	/* Vbox for devices clist and fsck parameters */
	w = gtk_vbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent3 = 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(parent3), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Devices clist */
#ifdef PROG_LANGUAGE_ENGLISH
	heading[0] = "Name";
	heading[1] = "Device Path";
	heading[2] = "Mount Path";
	heading[3] = "File System";
#endif
#ifdef PROG_LANGUAGE_SPANISH
	heading[0] = "El Nombre";
	heading[1] = "El Sendero Del Artefacto";
	heading[2] = "Monte Sendero";
	heading[3] = "Archive Sistema";
#endif
#ifdef PROG_LANGUAGE_FRENCH
	heading[0] = "Nom";
	heading[1] = "Composant";
	heading[2] = "Répertoire de montage";
	heading[3] = "Système de fichier";
#endif
	fm->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_BUTTON_PRESS_MASK
	);
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(FSCKManagerButtonPressEventCB), fm
	);
	gtk_widget_set_usize(w, -1, 150);
	gtk_container_add(GTK_CONTAINER(parent4), w);
	gtk_widget_realize(w);
	gtk_clist_set_selection_mode(clist, GTK_SELECTION_EXTENDED);
	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(FSCKManagerSelectRowCB), fm
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "unselect_row",
	    GTK_SIGNAL_FUNC(FSCKManagerUnselectRowCB), fm
	);
	gtk_widget_show(w);


	/* Devices clist menu */
	if(TRUE)
	{
#define DO_ADD_MENU_ITEM_LABEL	{		\
 w = GUIMenuItemCreate(				\
  menu, GUI_MENU_ITEM_TYPE_LABEL, accel_group,	\
  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					\
 );						\
}

	    GtkWidget *menu = GUIMenuCreate();
	    guint8 **icon;
	    const gchar *label;
	    guint accel_key, accel_mods;
	    GtkAccelGroup *accel_group = NULL;
	    gpointer mclient_data = fm;
	    void (*func_cb)(GtkWidget *w, gpointer);


	    icon = (guint8 **)icon_run_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
	    label = "Start";
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    label = "Comienzo";
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    label = "Démarrer";
#endif
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = FSCKManagerStartCB;
	    DO_ADD_MENU_ITEM_LABEL
	    fm->devices_start_mi = w;

	    icon = (guint8 **)icon_stop_20x20_xpm;
#ifdef PROG_LANGUAGE_ENGLISH
	    label = "Stop";
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    label = "Parada";
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    label = "Arrêt";
#endif
	    accel_key = 0;
	    accel_mods = 0;
	    func_cb = FSCKManagerStopCB;
	    DO_ADD_MENU_ITEM_LABEL
	    fm->devices_stop_mi = w;




	    fm->devices_menu = menu;
#undef DO_ADD_MENU_ITEM_LABEL
#undef DO_ADD_MENU_SEP
	}





	/* Frame for fsck parameters */
	w = gtk_frame_new(
#ifdef PROG_LANGUAGE_ENGLISH
	    "Options"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Opciones"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Options"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_ETCHED_IN);
	gtk_widget_show(w);
	parent4 = w;

	w = gtk_vbox_new(FALSE, border_major);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_container_add(GTK_CONTAINER(parent4), w);
	gtk_widget_show(w);
	parent4 = w;

	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent5 = w;

	/* Check for bad blocks */
	fm->bad_blocks_check = w = gtk_check_button_new_with_label(
#ifdef PROG_LANGUAGE_ENGLISH
	    "Check Bad Blocks"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Verifique Bloques Malos"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Vérifier Mauvais Blocs"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Check for bad blocks (this may increase the checking time)"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "El cheque para bloques malos (esto puede aumentar el \
tiempo que verifica)"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "La vérifications des mauvais blocs augemente le temps de la vérification"

#endif
	);
	gtk_widget_show(w);
	GTK_TOGGLE_BUTTON(w)->active = TRUE;

	/* Verbose */
	fm->verbose_check = w = gtk_check_button_new_with_label(
#ifdef PROG_LANGUAGE_ENGLISH
	    "Verbose"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Detallado"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Détails"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent5), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Be verbose"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Sea detallado"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Plus d'information sur le compte rendu"
#endif
	);
	gtk_widget_show(w);
	GTK_TOGGLE_BUTTON(w)->active = TRUE;



	/* Vbox for buttons */
	w = gtk_vbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;

	/* Hbox for disk animation icon drawing area */
	w = gtk_hbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Disk animation icon drawing area */
	fm->disk_anim_da = w = gtk_drawing_area_new();
	gtk_drawing_area_size(GTK_DRAWING_AREA(w), 48, 48);
	gtk_widget_add_events(w, GDK_EXPOSURE_MASK);
	gtk_signal_connect(
	    GTK_OBJECT(w), "expose_event",
	    GTK_SIGNAL_FUNC(FSCKManagerDiskIconExposeCB), fm
	);
	gtk_box_pack_start(GTK_BOX(parent4), w, TRUE, FALSE, 0);
	gtk_widget_realize(w);
	gtk_widget_show(w);
	/* Graphics context for disk animation icon */
	fm->gc = gdk_gc_new(w->window);
	/* Pixmap buffer for disk animation icon */
	fm->disk_anim_pm = gdk_pixmap_new(w->window, 48, 48, -1);


	/* Vbox for start and stop buttons */
	w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;


	/* Start button */
	fm->start_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_run_20x20_xpm,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Start",
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Comienzo",
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Démarrer",
#endif
	    NULL
	);
	gtk_widget_set_usize(
	    w,
	    FSCK_MANAGER_BTN_WIDTH,
	    FSCK_MANAGER_BTN_HEIGHT
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Start checking the selected device"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "El comienzo verificando el artefacto escogido"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Démarrer le programme de vérification"
#endif
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FSCKManagerStartCB), fm
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Return, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_s, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_s);
	gtk_widget_show(w);

	/* Stop button */
	fm->stop_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_stop_20x20_xpm,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Stop",
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Parada",
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Arrêt",
#endif
	    NULL
	);
	gtk_widget_set_usize(
	    w,
	    FSCK_MANAGER_BTN_WIDTH,
	    FSCK_MANAGER_BTN_HEIGHT
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Stop the current checking process"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Pare la corriente el proceso que verifica"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Arrêter le procédé de vérification en cours"
#endif
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FSCKManagerStopCB), fm
	);
	gtk_accel_group_add(
	    accelgrp, GDK_Escape, 0, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	gtk_accel_group_add(
	    accelgrp, GDK_t, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);                          
	GUIButtonLabelUnderline(w, GDK_t);
	gtk_widget_show(w);



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


	/* Hbox for output text and buttons */
	w = gtk_hbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_widget_show(w);
	parent2 = w;

	/* Frame for output text */
	w = gtk_frame_new(
#ifdef PROG_LANGUAGE_ENGLISH
	    "Results"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Resultados"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Résultats"
#endif
	);
	gtk_box_pack_start(GTK_BOX(parent2), w, TRUE, TRUE, 0);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_ETCHED_IN);
	gtk_widget_show(w);
	parent3 = w;

	/* Table for text and scroll bar widgets */
	w = gtk_table_new(2, 2, FALSE);
	gtk_table_set_row_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_table_set_col_spacing(GTK_TABLE(w), 0, border_minor);
	gtk_container_border_width(GTK_CONTAINER(w), border_major);
	gtk_container_add(GTK_CONTAINER(parent3), w);
	gtk_widget_show(w);
	parent3 = w;

	fm->output_text = w = gtk_text_new(NULL, NULL);
	editable = (GtkEditable *)w;
	text = (GtkText *)w;
	gtk_text_set_editable(text, FALSE);
	gtk_text_set_word_wrap(text, TRUE);
	gtk_table_attach(
	    GTK_TABLE(parent3), w,
	    0, 1, 0, 1,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	fm->colormap = colormap = gtk_widget_get_colormap(w);
	if(colormap != NULL)
	    gdk_colormap_ref(colormap);
	gtk_widget_show(w);

	parent4 = gtk_vscrollbar_new(GTK_TEXT(w)->vadj);
	gtk_table_attach(
	    GTK_TABLE(parent3), parent4,
	    1, 2, 0, 1,
	    GTK_FILL,
	    GTK_EXPAND | GTK_SHRINK | GTK_FILL,
	    0, 0
	);
	gtk_widget_realize(w);
	gdk_window_set_cursor(w->window, fm->text_cur);
	gtk_widget_show(parent4);


	/* Allocate colors */
	c = &fm->error_color;
	c->red = 1.0 * (guint16)-1;
	c->green = 0.0 * (guint16)-1;
	c->blue = 0.0 * (guint16)-1;
	gdk_colormap_alloc_color(colormap, c, TRUE, TRUE);










	/* Vbox for buttons */
	w = gtk_vbox_new(FALSE, border_major);
	gtk_box_pack_start(GTK_BOX(parent2), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent3 = w;

	/* Vbox for clear button */
	w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Clear button */
	fm->clear_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_clear_20x20_xpm,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Clear",
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Claro",
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Effacer",
#endif
	    NULL
	);
	gtk_widget_set_usize(
	    w,
	    FSCK_MANAGER_BTN_WIDTH,
	    FSCK_MANAGER_BTN_HEIGHT
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_start(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Clear results"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Los resultados claros"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Effacer le compte-rendu"
#endif
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FSCKManagerClearCB), fm
	);
	gtk_accel_group_add(
	    accelgrp, GDK_l, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE,
	    GTK_OBJECT(w), "clicked"
	);
	GUIButtonLabelUnderline(w, GDK_l);
	gtk_widget_show(w);

	/* Vbox for close button */
	w = gtk_vbox_new(FALSE, border_minor);
	gtk_box_pack_start(GTK_BOX(parent3), w, TRUE, TRUE, 0);
	gtk_widget_show(w);
	parent4 = w;

	/* Close button */
	fm->close_btn = w = GUIButtonPixmapLabelH(
	    (guint8 **)icon_close_20x20_xpm,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Close",
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Cierre",
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Quitter",
#endif
	    NULL
	);
	gtk_widget_set_usize(
	    w,
	    FSCK_MANAGER_BTN_WIDTH,
	    FSCK_MANAGER_BTN_HEIGHT
	);
	GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT);
	gtk_box_pack_end(GTK_BOX(parent4), w, FALSE, FALSE, 0);
	GUISetWidgetTip(
	    w,
#ifdef PROG_LANGUAGE_ENGLISH
	    "Close this window"
#endif
#ifdef PROG_LANGUAGE_SPANISH
	    "Cierre esta ventana"
#endif
#ifdef PROG_LANGUAGE_FRENCH
	    "Fermer cette fenêtre"
#endif
	);
	gtk_signal_connect(
	    GTK_OBJECT(w), "clicked",
	    GTK_SIGNAL_FUNC(FSCKManagerCloseBtnCB), fm
	);
	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);



	/* Status bar frame */
	w = gtk_frame_new(NULL);
	gtk_widget_set_usize(w, -1, EDV_STATUS_BAR_HEIGHT);
	gtk_frame_set_shadow_type(GTK_FRAME(w), GTK_SHADOW_OUT);
	gtk_container_border_width(GTK_CONTAINER(w), 0);
	gtk_box_pack_start(GTK_BOX(parent), w, FALSE, FALSE, 0);
	gtk_widget_show(w);
	parent2 = w;

	/* Table in main frame */
	w = gtk_table_new(1, 1, FALSE);
	gtk_container_add(GTK_CONTAINER(parent2), w);
	gtk_widget_show(w);
	parent2 = w;

	/* Progress bar */
	adj = (GtkAdjustment *)gtk_adjustment_new(0, 1, 100, 0, 0, 0);
	fm->progress_bar = w = gtk_progress_bar_new_with_adjustment(adj);
	gtk_widget_set_usize(w, -1, 20);
	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_progress_set_show_text(GTK_PROGRESS(w), FALSE);
	gtk_progress_bar_update(GTK_PROGRESS_BAR(w), 0.0);
	gtk_table_attach(
	    GTK_TABLE(parent2), w,
	    0, 1, 0, 1,
	    GTK_SHRINK | GTK_EXPAND | GTK_FILL,
	    GTK_SHRINK,
	    border_minor, border_minor
	);
	gtk_widget_show(w);



	/* Load disk animation icons */
	fm->last_disk_icon_num = 0;

	style = gtk_widget_get_style(fm->disk_anim_da);
	fm->total_disk_icons = 3;
	fm->disk_icon_pixmap = (GdkPixmap **)g_malloc0(
	    fm->total_disk_icons * sizeof(GdkPixmap *)
	);
	fm->disk_icon_mask = (GdkBitmap **)g_malloc0(
	    fm->total_disk_icons * sizeof(GdkBitmap *)
	);

	i = 0;
	fm->disk_icon_pixmap[i] = GDK_PIXMAP_NEW_FROM_XPM_DATA(
	    &fm->disk_icon_mask[i],
	    (guint8 **)disk_icon_00_48x48_xpm
	);
	i = 1;
	fm->disk_icon_pixmap[i] = GDK_PIXMAP_NEW_FROM_XPM_DATA(
	    &fm->disk_icon_mask[i],
	    (guint8 **)disk_icon_01_48x48_xpm
	);
	i = 2;
	fm->disk_icon_pixmap[i] = GDK_PIXMAP_NEW_FROM_XPM_DATA(
	    &fm->disk_icon_mask[i],
	    (guint8 **)disk_icon_02_48x48_xpm
	);





	FSCKManagerUpdateMenus(fm);

	return(fm);
}

/*
 *	Updates widgets to reflect local data values.
 */
void FSCKManagerUpdateMenus(fsck_manager_struct *fm)
{
	gboolean sensitive;
	GtkWidget *w;
	GtkCList *clist;

	if(fm == NULL)
	    return;

#define DO_SET_SENSITIVE	{		\
 if(w != NULL)					\
  gtk_widget_set_sensitive(w, sensitive);	\
}

	sensitive = (fm->fsck_toid > 0) ? FALSE : TRUE;
	w = fm->start_btn;
	DO_SET_SENSITIVE
	w = fm->devices_start_mi;
	DO_SET_SENSITIVE

	sensitive = (fm->fsck_toid > 0) ? TRUE : FALSE;
	w = fm->stop_btn;
	DO_SET_SENSITIVE
	w = fm->devices_stop_mi;
	DO_SET_SENSITIVE


	w = fm->devices_clist;
	clist = (GtkCList *)w;







#undef DO_SET_SENSITIVE
}

/*
 *      Marks the FSCK Manager as busy or ready.
 */
void FSCKManagerSetBusy(fsck_manager_struct *fm, gboolean is_busy)
{
	GdkCursor *cur;
	GtkWidget *w;

	if(fm == NULL)
	    return;

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

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

		cur = fm->busy_cur;
	    }
	    else
	    {
		/* Reduce busy count */
		fm->busy_count--;
		if(fm->busy_count < 0)
		    fm->busy_count = 0;

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

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

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

/*
 *	Updates the progress bar on the FSCK Manager.
 */
void FSCKManagerSetProgress(
	fsck_manager_struct *fm, gfloat value,
	gboolean allow_gtk_iteration
)
{
	GtkWidget *w;
	GtkProgress *progress;

	if(fm == NULL)
	    return;

	w = fm->progress_bar;
	if(w == NULL)
	    return;
	progress = GTK_PROGRESS(w);

	/* Negative? Implies just do activity */
	if(value < 0.0f)
	{
	    GtkAdjustment *adj = progress->adjustment;

	    /* Get new value based on unknown position */
	    value = gtk_progress_get_value(progress) + 1.0;
	    if(value > adj->upper)
		value = adj->lower;

	    /* Update progress widget for `activity mode' (unknown limit)
	     * and set new value.
	     */
	    gtk_progress_set_activity_mode(progress, TRUE);
	    gtk_progress_set_show_text(progress, FALSE);
	    gtk_progress_set_value(progress, value);

	    /* Reset last progress position to -1.0, indicating no last
	     * position since in unknown time limit we don't keep track of
	     * the last position.
	     */
	    fm->progress_pos_last = -1.0;

	    /* Iterate through GTK main function until all events are
	     * handled so that we ensure this progress bar setting is
	     * updated.
	     */
	    while((gtk_events_pending() > 0) && allow_gtk_iteration)
		gtk_main_iteration();

	    return;
	}

	/* Clip value to 0.0, 1.0 */
	if(value > 1.0)
	    value = 1.0;

	/* Reset last progress position if it is greater than the given
	 * value, implying the progress wraped back to the beginning.
	 */
	if(fm->progress_pos_last > value)
	    fm->progress_pos_last = -1.0;

	/* Check if percent did not sufficiently change (use 0.01
	 * increments).
	 */
	if((fm->progress_pos_last > 0.0) &&
	   ((value - fm->progress_pos_last) < 0.01)
	)
	    return;

	/* Update progress */
	gtk_progress_set_activity_mode(progress, FALSE);
	gtk_progress_set_format_string(
	    progress, "%p%%"
	);
	gtk_progress_set_show_text(
	    progress, (value > 0.0) ? TRUE : FALSE
	);
	gtk_progress_bar_update(GTK_PROGRESS_BAR(w), value);

	/* Record last position */
	fm->progress_pos_last = value;

	/* Iterate through GTK main function until all events are
	 * handled so that we ensure this progress bar setting is
	 * updated.
	 */
	while((gtk_events_pending() > 0) && allow_gtk_iteration)
	    gtk_main_iteration();
}

/*
 *	Maps the FSCK Manager.
 */
void FSCKManagerMap(fsck_manager_struct *fm)
{
	GtkWidget *w = (fm != NULL) ? fm->toplevel : NULL;
	if(w == NULL)
	    return;

	gtk_widget_show_raise(w);
	fm->map_state = TRUE;

	/* Grab focus on the Devices List */
	w = fm->devices_clist;
	gtk_widget_grab_focus(w);
}

/*
 *      Unmaps the FSCK Manager.
 */
void FSCKManagerUnmap(fsck_manager_struct *fm)
{
	GtkWidget *w = (fm != NULL) ? fm->toplevel : NULL;
	if(w == NULL)                                     
	    return;

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

/*
 *	Deletes the FSCK Manager.
 */
void FSCKManagerDelete(fsck_manager_struct *fm)
{
	gint i;
	GdkColormap *colormap;

	if(fm == NULL)
	    return;

	colormap = fm->colormap;

	g_list_free(fm->queued_device_to_check);
	fm->queued_device_to_check = NULL;

	GTK_TIMEOUT_REMOVE(fm->fsck_toid);
	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;

	for(i = 0; i < fm->total_disk_icons; i++)
	{
	    GDK_PIXMAP_UNREF(fm->disk_icon_pixmap[i]);
	    GDK_BITMAP_UNREF(fm->disk_icon_mask[i]);
	}
	g_free(fm->disk_icon_pixmap);
	fm->disk_icon_pixmap = NULL;
	g_free(fm->disk_icon_mask);
	fm->disk_icon_mask = NULL;
	fm->total_disk_icons = 0;


	/* Begin destroying widgets */
	GTK_WIDGET_DESTROY(fm->devices_menu);
	GTK_WIDGET_DESTROY(fm->devices_clist);
	GTK_WIDGET_DESTROY(fm->bad_blocks_check);
	GTK_WIDGET_DESTROY(fm->verbose_check);
	GTK_WIDGET_DESTROY(fm->output_text);
	GTK_WIDGET_DESTROY(fm->progress_bar);
	GTK_WIDGET_DESTROY(fm->disk_anim_da);
	GTK_WIDGET_DESTROY(fm->start_btn);
	GTK_WIDGET_DESTROY(fm->stop_btn);
	GTK_WIDGET_DESTROY(fm->clear_btn);
	GTK_WIDGET_DESTROY(fm->close_btn);
	GTK_WIDGET_DESTROY(fm->toplevel);

	GTK_ACCEL_GROUP_UNREF(fm->accelgrp);

	GDK_PIXMAP_UNREF(fm->disk_anim_pm);

	GDK_GC_UNREF(fm->gc);

	GDK_COLORMAP_FREE_COLOR(colormap, &fm->error_color);
	GDK_COLORMAP_UNREF(colormap);

	GDK_CURSOR_DESTROY(fm->busy_cur);
	GDK_CURSOR_DESTROY(fm->text_cur);

	GDK_FONT_UNREF(fm->text_font);

	for(i = 0; i < fm->total_devices; i++)
	    EDVDeviceDelete(fm->device[i]);
	g_free(fm->device);
	fm->device = NULL;
	fm->total_devices = 0;

	g_free(fm);
}

