[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

GTK frontend development, source snapshot



Hi everyone

In the last days i started working again on the gtk over directframebuffer frontend for the debian-installer, trying to implement a mechanism that allows the user to jump from a section of the debian-installer to another; basically, if the users clicks in the main menu while has not yet finished configuring a module the frontend keeps track of the desired jump target and act as if the user pressed the back button. on the next call of gtk_go() the FE checks if there is a pending jump and if there is only one question in the question list before jumping; while there is still more than one question in the list the jump doesn't occourrs and the FE act as if a back command was given by the user. This because modules such netcfg didn't seem to be willink to go back once started (once started it needs to complete the configuration of the net and doesn't seem to be glad to be interrupted). I know this mechanism is somehow strange and i'm also sure that there is a way to backup netcfg, but i can't tell it for sure nor correct t until i get rid of some strange bugs that don't occour with the newt FE; those bugs are

-The question list passed to the gtk module needs to have the "prev" pointers setted to NULL before returning from gtk_go(), otherwise the FE crashes (actually i managed to set those ptrs to NULL by hand ath the end of gtk_go() ) -The "info" string passed to gtk_progress_info() and the "val" int passed to gtk_progress_set() point to wrong locations, so the progress bar actually doesn't work.

Those bugs seem (to me, at least) related more generally to the use of the dfb/gtk libraries, as if starting up such set of libraries trashes the memory somewhere.

Actually, anyway, the frontend doesn't seem to perform too bad: yesterday i've been able to setup a working debian installation using only the gtk fe, though it crashed when installing the grub bootloader.

Attached to this email is the source of the gtk.c i'w actually working on; i've tried to write down as much comments as i could to explain what i'm trying to do in the code and i've managed to rewrite the code in a "clean" way.

could someone please help me?

ciao

Attilio



--------------------gtk.c----------------------------


/***********************************************************************
 *
 * cdebconf - An implementation of the Debian Configuration Management
 *            System
 *
 * File: gtk.c
 *
 * Description: gtk UI for cdebconf
 * Some notes on the implementation - optimistic at best.
 *  mbc - just to get this off of the ground, Im' creating a dialog
 *        and calling gtk_main for each question. once I get the tests
 *        running, I'll probably send a delete_event signal in the
 *        next and back button callbacks.
 *
 *        There is some rudimentary attempt at implementing the next
 *        and back functionality.
 *
 * $Id: gtk.c 23328 2004-10-21 15:28:54Z cjwatson $
 *
 * cdebconf is (c) 2000-2001 Randolph Chung and others under the following
 * license.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 ***********************************************************************/
#include "common.h"
#include "template.h"
#include "question.h"
#include "frontend.h"
#include "database.h"
#include "strutl.h"
#include "cdebconf_gtk.h"

#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dlfcn.h>

#include <gtk/gtk.h>

#include <syslog.h>

#define q_get_extended_description(q) question_get_field((q), "", "extended_description")
#define q_get_description(q)  		question_get_field((q), "", "description")
#define q_get_choices(q)		question_get_field((q), "", "choices")
#define q_get_choices_vals(q)		question_get_field((q), NULL, "choices")
#define q_get_indices(q)		question_get_field((q), "", "indices")

/* A struct to let a question handler store appropriate set functions that will be called after
   gtk_main has quit */
struct setter_struct
{
    void (*func) (void*, struct question*);
    void *data;
    struct question *q;
    struct setter_struct *next;
};

typedef int (custom_func_t)(struct frontend*, struct question*, GtkWidget*);


static gboolean delete_event( GtkWidget *widget,
                              GdkEvent  *event,
                              gpointer   data )
{
    /* If you return FALSE in the "delete_event" signal handler,
     * GTK will emit the "destroy" signal. Returning TRUE means
     * you don't want the window to be destroyed.
     * This is useful for popping up 'are you sure you want to quit?'
     * type dialogs. */
	syslog(LOG_DEBUG,"gtk_fe_debug - delete_event occurred\n");


    /* Change TRUE to FALSE and the main window will be destroyed with
     * a "delete_event". */

    return FALSE;
}



void register_setter(void (*func)(void*, struct question*),
		     void *data, struct question *q, struct frontend *obj)
{
    struct setter_struct *s;

    s = malloc(sizeof(struct setter_struct));
    s->func = func;
    s->data = data;
    s->q = q;
    s->next = ((struct frontend_data*)obj->data)->setters;
    ((struct frontend_data*)obj->data)->setters = s;
}

void free_description_data( GtkObject *obj, struct frontend_question_data* data )
{
    free(data);
}


/*

The help is now shown inside the appropriate text view, and nolonger inside a popup window

gboolean show_description (GtkWidget *widget, struct frontend_question_data* data)
{
  struct question *q;
  struct frontend *obj;

  GtkWidget *main_window;
  GtkWidget *dialog;

  obj = data->obj;
  q = data->q;

  main_window = ((struct frontend_data*) obj->data)->window;

dialog = gtk_message_dialog_new (GTK_WINDOW(main_window), GTK_DIALOG_MODAL,
				   GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
				   q_get_extended_description(q));

  gtk_dialog_run (GTK_DIALOG(dialog));
  gtk_widget_destroy (dialog);

    return FALSE;
}
*/

gboolean show_description (GtkWidget *widget, struct frontend_question_data* data)
{


	struct question *q;
	struct frontend *frontend_ptr;
	struct frontend_data *frontend_data_ptr;
 	GtkWidget *view;
  		
	GtkTextBuffer *buffer;

	frontend_ptr=data->obj;
	frontend_data_ptr=frontend_ptr->data;
	view=(GtkWidget*)frontend_data_ptr->info_box;
  	q = data->q;

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view) );

	gtk_text_buffer_set_text (buffer, q_get_extended_description(q), -1);

//syslog(LOG_DEBUG,"gtk_fe_debug - show_description(%s) called\n",q_get_extended_description(q));
	
    return DC_OK;
}


/*
used to cleat the help area
*/
gboolean clear_description (GtkWidget *widget, struct frontend_question_data* data)
{

	struct frontend *frontend_ptr;
	struct frontend_data *frontend_data_ptr;
 	GtkWidget *view;
  		
	GtkTextBuffer *buffer;

	frontend_ptr=data->obj;
	frontend_data_ptr=frontend_ptr->data;
	view=(GtkWidget*)frontend_data_ptr->info_box;

	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view) );

	gtk_text_buffer_set_text (buffer, "", -1);
	
    return DC_OK;
}



GtkWidget*
create_help_button (struct frontend_question_data *data)
{
  GtkWidget *button;

  button = gtk_button_new_from_stock (GTK_STOCK_HELP);
  g_signal_connect (G_OBJECT(button), "clicked",
		    G_CALLBACK(show_description), data);

  return button;
}

gboolean is_first_question (struct question *q)
{
    struct question *crawl;

    crawl = q;

    while (crawl->prev != NULL)
    {
	if (strcmp(crawl->prev->template->type, "note") != 0)
	    return FALSE;
	crawl = crawl->prev;
    }
    return TRUE;
}

static void bool_setter(void *check, struct question *q)
{
question_setvalue(q, (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check)) ? "true" : "false"));
}	

static void entry_setter(void *entry, struct question *q)
{
    question_setvalue(q, gtk_entry_get_text(GTK_ENTRY(entry)));
}

static void combo_setter(void *entry, struct question *q)
{
    char **choices, **choices_translated;
    int i, count;
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);

    count = strgetargc(q_get_choices_vals(q));
    if (count <= 0)
        return /* DC_NOTOK */;
    choices = malloc(sizeof(char *) * count);
    choices_translated = malloc(sizeof(char *) * count);
    tindex = malloc(sizeof(int) * count);
if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count)
        return /* DC_NOTOK */;

    for (i = 0; i < count; i++)
    {
if (strcmp(gtk_entry_get_text(GTK_ENTRY(entry)), choices_translated[i]) == 0)
	    question_setvalue(q, choices[tindex[i]]);

	free(choices[tindex[i]]);
	free(choices_translated[i]);
    }
    free(choices);
    free(choices_translated);
    free(tindex);
}	

static void multi_setter(void *check_container, struct question *q)
{
    gchar *result = NULL;
    gchar *copy = NULL;
    GList *check_list;
    int i, count;
    char **choices, **choices_translated;
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);

check_list = gtk_container_get_children(GTK_CONTAINER(check_container));
    while(check_list)
    {
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check_list->data)))
	{
            count = strgetargc(q_get_choices_vals(q));
            if (count <= 0)
                return /* DC_NOTOK */;
            choices = malloc(sizeof(char *) * count);
            choices_translated = malloc(sizeof(char *) * count);
            tindex = malloc(sizeof(int) * count);
if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count)
                return /* DC_NOTOK */;

	    for (i = 0; i < count; i++)
            {
if (strcmp(gtk_button_get_label(GTK_BUTTON(check_list->data)), choices_translated[i]) == 0)
		{
		    if(result != NULL)
		    {
			copy = g_strdup(result);
			free(result);
			result = g_strconcat(copy, ", ", choices[tindex[i]], NULL);
			free(copy);
		    }
		    else
			result = g_strdup(choices[tindex[i]]);
		}
		free(choices[tindex[i]]);
		free(choices_translated[i]);
            }
            free(choices);
            free(choices_translated);
	    free(tindex);
	}
	check_list = g_list_next(check_list);
    }
    if(!result)
	result = g_strdup("");
    question_setvalue(q, result);
    g_list_free(check_list);
    free(result);
}

void call_setters(struct frontend *obj)
{
    struct setter_struct *s, *p;

    s = ((struct frontend_data*)obj->data)->setters;

  	//syslog(LOG_DEBUG,"gtk_fe_debug - call_setters() called\n");

    while (s != NULL)
    {
        (*s->func)(s->data, s->q);
        p = s;
        s = s->next;
        free(p);
    }
}

void button_single_callback(GtkWidget *button, struct frontend_question_data* data)
{

    struct frontend *obj = data->obj;
    struct frontend_data *fe_data = obj->data;
    struct question *q = data->q;
    char **choices, **choices_translated;
    int i, count;
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);
	

    count = strgetargc(q_get_choices_vals(q));
    if (count <= 0)
        return /* DC_NOTOK */;
    choices = malloc(sizeof(char *) * count);
    choices_translated = malloc(sizeof(char *) * count);
    tindex = malloc(sizeof(int) * count);

syslog(LOG_DEBUG,"gtk_fe_debug - button_single_callback(%s) called \n", gtk_button_get_label(GTK_BUTTON(button)) );

if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count) return /* DC_NOTOK */;
    for (i = 0; i < count; i++)
    	{
if (strcmp(gtk_button_get_label(GTK_BUTTON(button)), choices_translated[i]) == 0)
			{
			/*
			Here we set up the target for the programmed jump
			*/
			if( (*fe_data).number_of_questions > 1 )
				{
				strcpy(fe_data->jump_target,choices[tindex[i]]);
syslog(LOG_DEBUG,"gtk_fe_debug - jump programmed, target: \"%s\" \n", fe_data->jump_target );
				}
			question_setvalue(q, choices[tindex[i]]);
			}
		free(choices[tindex[i]]);
		free(choices_translated[i]);
    	}
    	
    free(choices);
    free(choices_translated);
    free(tindex);
    ((struct frontend_data*)obj->data)->button_val = DC_OK;
    free(data);

    gtk_main_quit();
}

void check_toggled_callback (GtkWidget *toggle, gpointer data)
{
	struct question *q = (struct question*)data;
	gboolean value;

	syslog(LOG_DEBUG,"gtk_fe_debug - check_toggled_callback() called - \n");
	value = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(toggle));
	bool_setter (toggle, q);
}

void boolean_single_callback(GtkWidget *button, struct frontend_question_data* data )
{
    struct frontend *obj = data->obj;
    struct question *q = data->q;
    char *ret;

   	syslog(LOG_DEBUG,"gtk_fe_debug - boolean_single_callback() called\n");
    ret = (char*) gtk_object_get_user_data(GTK_OBJECT(button));
    question_setvalue(q, ret);
    free(ret);

    ((struct frontend_data*)obj->data)->button_val = DC_OK;;

    gtk_main_quit();
}

void exit_button_callback(GtkWidget *button, struct frontend* obj)
{
    int value;
    void *ret;

    ret = gtk_object_get_user_data(GTK_OBJECT(button));
    value = *(int*) ret;

syslog(LOG_DEBUG,"gtk_fe_debug - exit_button_callback() called, value: %d \n",value);

    ((struct frontend_data*)obj->data)->button_val = value;

    gtk_main_quit();
}


static const char *
get_text(struct frontend *obj, const char *template, const char *fallback )
{
	        struct question *q = obj->qdb->methods.get(obj->qdb, template);
		        return q ? q_get_description(q) : fallback;
}

gboolean need_continue_button(struct frontend *obj)
{
    if (obj->questions->next == NULL)
    {
	if (strcmp(obj->questions->template->type, "boolean") == 0)
	    return FALSE;
	else if (strcmp(obj->questions->template->type, "select") == 0)
	    return FALSE;
    }
    return TRUE;
}

gboolean need_back_button(struct frontend *obj)
{
    if (obj->questions->next == NULL)
    {
	if (strcmp(obj->questions->template->type, "boolean") == 0)
	    return FALSE;
    }
    return TRUE;
}

static int
gtkhandler_boolean_single(struct frontend *obj, struct question *q,
			  GtkWidget *qbox)
{
  GtkWidget *hbox;
  GtkWidget *check_button;
  GtkWidget *help_button;
  struct frontend_question_data *data;
  const char *defval = question_getvalue(q, "");

  syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_boolean_single() called\n");

  data = NEW(struct frontend_question_data);
  data->obj = obj;
  data->q = q;

  hbox = gtk_hbox_new (FALSE, 5);
  gtk_box_pack_start (GTK_BOX(qbox), hbox, TRUE, TRUE, 5);

  check_button = gtk_check_button_new_with_label (q_get_description (q));
  g_signal_connect (G_OBJECT(check_button), "toggled",
		    G_CALLBACK(check_toggled_callback),
		    q);
  gtk_box_pack_start (GTK_BOX(hbox), check_button, TRUE, TRUE, 5);

  help_button = create_help_button (data);
  gtk_box_pack_start (GTK_BOX(hbox), help_button, FALSE, FALSE, 3);

  /* FIXME: sensitive to the druid button
     if (obj->methods.can_go_back(obj, q) == FALSE)
     {
     gtk_widget_set_sensitive(back_button, FALSE);
     }
  */

  if (strcmp (defval, "true") == 0)
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(check_button), TRUE);

  return DC_OK;
}

static int gtkhandler_boolean_multiple(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *check;
    struct frontend_question_data *data;
    const char *defval = question_getvalue(q, "");

	 syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_boolean_multiple() called\n");

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;
	
    check = gtk_check_button_new_with_label(q_get_description(q));
    if (strcmp(defval, "true") == 0)
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
    else
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
g_signal_connect (G_OBJECT(check), "enter", G_CALLBACK (show_description), data); g_signal_connect (G_OBJECT(check), "grab-focus", G_CALLBACK (show_description), data); g_signal_connect (G_OBJECT(check), "destroy", G_CALLBACK (free_description_data), data);

    frame = gtk_frame_new(NULL);
    gtk_container_add(GTK_CONTAINER (frame), check);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    if (is_first_question(q))
	gtk_widget_grab_focus(check);
	
    register_setter(bool_setter, check, q, obj);

    return DC_OK;
}

static int gtkhandler_boolean(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
	 syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_boolean() called\n");
    if (q->next == NULL && q->prev == NULL)
        return gtkhandler_boolean_single(obj, q, qbox);
    else
        return gtkhandler_boolean_multiple(obj, q, qbox);
}

static int gtkhandler_multiselect(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *check_container, *check;
    char **choices, **choices_translated, **defvals;
    int i, j, count, defcount;
    struct frontend_question_data *data;
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;
    count = strgetargc(q_get_choices_vals(q));
    if (count <= 0)
        return DC_NOTOK;

    choices = malloc(sizeof(char *) * count);
    choices_translated = malloc(sizeof(char *) * count);
    tindex = malloc(sizeof(int) * count);
if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count)
        return DC_NOTOK;

    defvals = malloc(sizeof(char *) * count);

    /*
    Here code needs to be fixed, since defaul values are passed incorrectly
    */
    defcount = strchoicesplit(question_getvalue(q, ""), defvals, count);
	syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_multiselect() 0 called\n");
	for(i=0; i<defcount; i++)
syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_multiselect() %d)=%s\n",i,defvals[i]);
    //if (defcount <= 0) return DC_NOTOK;	//gtk_fe_debug - fa bloccare

	syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_multiselect() 1 called\n");

    check_container = gtk_vbox_new (FALSE, 0);

g_signal_connect (G_OBJECT(check_container), "destroy", G_CALLBACK (free_description_data), data);

    for (i = 0; i < count; i++)
    	{
		check = gtk_check_button_new_with_label(choices_translated[i]);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), FALSE);
		for (j = 0; j < defcount; j++)
   	    	{
//if (strcmp(choices[tindex[i]], defvals[j]) == 0) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
        	}
g_signal_connect (G_OBJECT(check), "enter", G_CALLBACK (show_description), data); gtk_box_pack_start(GTK_BOX(check_container), check, FALSE, FALSE, 0);

		if (is_first_question(q) && (i == 0) )	gtk_widget_grab_focus(check);

		free(choices[tindex[i]]);
        free(choices_translated[i]);
    	}
    	
    free(choices);
    free(choices_translated);
    free(tindex);
    for (j = 0; j < defcount; j++)
        free(defvals[j]);
    free(defvals);

    frame = gtk_frame_new(q_get_description(q));
    gtk_container_add(GTK_CONTAINER (frame), check_container);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    register_setter(multi_setter, check_container, q, obj);

    return DC_OK;
}

static int gtkhandler_note(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *label;
	
		 syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_note() called\n");
	
    label = gtk_label_new (q_get_extended_description(q));
    gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
    gtk_label_set_line_wrap(GTK_LABEL (label), TRUE);

    frame = gtk_frame_new(q_get_description(q));
    gtk_container_add(GTK_CONTAINER (frame), label);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);
	
    return DC_OK;
}




static int gtkhandler_text(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    /* FIXME: text probably shouldn't be quite so interactive as note */
    return gtkhandler_note(obj, q, qbox);
}

static int gtkhandler_password(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *entry;
    struct frontend_question_data *data;
	 syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_password() called\n");
	
    entry = gtk_entry_new ();
    gtk_entry_set_max_length (GTK_ENTRY (entry), 50);
    gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
    gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
    frame = gtk_frame_new(q_get_description(q));
    gtk_container_add(GTK_CONTAINER (frame), entry);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    if (is_first_question(q))
	gtk_widget_grab_focus(entry);

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;

g_signal_connect (G_OBJECT(entry), "destroy", G_CALLBACK (free_description_data), data);
    /* TODO: showing description on keyboard focus grab makes it impossible
     * to enter a password.
     */
/* g_signal_connect (G_OBJECT(entry), "grab-focus", G_CALLBACK (show_description), data); */
	
    register_setter(entry_setter, entry, q, obj);

    return DC_OK;
}

static int gtkhandler_select_single(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *button, *button_box;
    char **choices, **choices_translated;
    int i, count;
    struct frontend_question_data *data;
    const char *defval = question_getvalue(q, "");
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);

	//syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_select_single() called\n");

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;

    count = strgetargc(q_get_choices_vals(q));
    if (count <= 0)
        return DC_NOTOK;
    choices = malloc(sizeof(char *) * count);
    choices_translated = malloc(sizeof(char *) * count);
    tindex = malloc(sizeof(int) * count);
if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count)
        return DC_NOTOK;

    button_box = gtk_vbutton_box_new();

    frame = gtk_frame_new(q_get_description(q));
    gtk_container_add(GTK_CONTAINER (frame), button_box);

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    for (i = 0; i < count; i++)
    	{
	    button = gtk_button_new_with_label(choices_translated[i]);
		gtk_object_set_user_data(GTK_OBJECT(button), choices[tindex[i]]);
g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK (button_single_callback), data);
		
		/*
the following lines of code were previously introduced to give the user help about the main-menu, but since the help is just a "This is the main menu for the debian installer" i don't think this can be useful g_signal_connect (G_OBJECT(button), "enter", G_CALLBACK (show_description), data); g_signal_connect (G_OBJECT(button), "leave", G_CALLBACK (clear_description), data );
		*/
		gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 5);
		if (defval && strcmp(choices[tindex[i]], defval) == 0)
			{
		    GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
		    gtk_widget_grab_focus(button);
		    gtk_widget_grab_default(button);
			}
		free(choices[tindex[i]]);
   		}
    free(choices);
    free(choices_translated);
    free(tindex);

    return DC_OK;
}

static int gtkhandler_select_multiple(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *combo, *frame;
    GList *items = NULL;
    char **choices, **choices_translated;
    struct frontend_question_data *data;
    int i, count;
    const char *defval = question_getvalue(q, "");
    int *tindex = NULL;
    const gchar *indices = q_get_indices(q);


	GtkWidget *view;
	GtkTextBuffer *buffer;


//syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_select_multiple() called\n");


    count = strgetargc(q_get_choices_vals(q));
    if (count <= 0)
        return DC_NOTOK;
    choices = malloc(sizeof(char *) * count);
    choices_translated = malloc(sizeof(char *) * count);
    tindex = malloc(sizeof(int) * count);
if (strchoicesplitsort(q_get_choices_vals(q), q_get_choices(q), indices, choices, choices_translated, tindex, count) != count)
        return DC_NOTOK;
    free(choices);
    free(tindex);
    if (count <= 0) return DC_NOTOK;

    for (i = 0; i < count; i++)
        /* steal memory */
        {
        items = g_list_append (items, choices_translated[i]);
//syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_select_multiple(\"%s\") \n", choices_translated[i] );
        }
    free(choices_translated);

    combo = gtk_combo_new ();
    gtk_combo_set_popdown_strings (GTK_COMBO (combo), items);
    g_list_free(items);
gtk_editable_set_editable (GTK_EDITABLE(GTK_COMBO(combo)->entry), FALSE);

    if( defval!=NULL )
    	gtk_entry_set_text (GTK_ENTRY(GTK_COMBO(combo)->entry), defval);
    else
    	gtk_entry_set_text (GTK_ENTRY(GTK_COMBO(combo)->entry), "");
    gtk_combo_set_value_in_list (GTK_COMBO (combo), TRUE, FALSE);


	/*
this is just a dirty hack to prevent the description of the disk-partitioner, which is very long, from trashing the screen
	*/
	view = gtk_text_view_new ();
	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
	gtk_text_buffer_set_text (buffer, q_get_description(q), -1);
	gtk_text_view_set_editable ( GTK_TEXT_VIEW(view) , FALSE);
	gtk_text_view_set_cursor_visible ( GTK_TEXT_VIEW(view) , FALSE);
	gtk_text_view_set_wrap_mode ( GTK_TEXT_VIEW(view) , GTK_WRAP_WORD);
	gtk_text_view_set_left_margin( GTK_TEXT_VIEW(view) , 5);
	gtk_text_view_set_right_margin( GTK_TEXT_VIEW(view) , 5);

    frame = gtk_frame_new("");
    gtk_frame_set_label_widget      (frame,view );
syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_select_multiple() frame title: \"%s\" \n", q_get_description(q) );
    gtk_container_add(GTK_CONTAINER (frame), combo);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    if (is_first_question(q))
	gtk_widget_grab_focus(combo);

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;

    g_signal_connect (G_OBJECT(GTK_COMBO(combo)->entry), "destroy",
                      G_CALLBACK (free_description_data), data);

    g_signal_connect (G_OBJECT(GTK_COMBO(combo)->entry), "grab-focus",
                      G_CALLBACK (show_description), data);

    register_setter(combo_setter, GTK_COMBO(combo)->entry, q, obj);

    return DC_OK;
}

static int gtkhandler_select(struct frontend *obj, struct question *q, GtkWidget *qbox)
{

	/*
actually the gtkhandler_select_single is used for the main menu only, other select questions will use gtkhandler_select_multiple; some more work is needed here
	*/	
	
    if (q->prev == NULL)
        return gtkhandler_select_single(obj, q, qbox);
    else
        return gtkhandler_select_multiple(obj, q, qbox);
}

static int gtkhandler_string(struct frontend *obj, struct question *q, GtkWidget *qbox)
{
    GtkWidget *frame, *entry;
    struct frontend_question_data *data;
    const char *defval = question_getvalue(q, "");
	
	syslog(LOG_DEBUG,"gtk_fe_debug - gtkhandler_string() called\n"); 	
	
    entry = gtk_entry_new ();
    if ( defval != NULL )
		gtk_entry_set_text (GTK_ENTRY(entry), defval);
	else
		gtk_entry_set_text (GTK_ENTRY(entry), "");
    gtk_entry_set_max_length (GTK_ENTRY (entry), 50);
    gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
    frame = gtk_frame_new(q_get_description(q));
    gtk_container_add(GTK_CONTAINER (frame), entry);	

    gtk_box_pack_start(GTK_BOX(qbox), frame, FALSE, FALSE, 5);

    if (is_first_question(q))
		gtk_widget_grab_focus(entry);

    data = NEW(struct frontend_question_data);
    data->obj = obj;
    data->q = q;

g_signal_connect (G_OBJECT(entry), "destroy", G_CALLBACK (free_description_data), data); g_signal_connect (G_OBJECT(entry), "grab-focus", G_CALLBACK (show_description), data);

    register_setter(entry_setter, entry, q, obj);

    return DC_OK;
}



struct question_handlers {
    const char *type;
int (*handler)(struct frontend *obj, struct question *q, GtkWidget *questionbox);
} question_handlers[] = {
    { "boolean",        gtkhandler_boolean },
    { "multiselect",    gtkhandler_multiselect },
    { "note",	        gtkhandler_note },
    { "password",	gtkhandler_password },
    { "select",	        gtkhandler_select },
    { "string",	        gtkhandler_string },
    { "error",	        gtkhandler_note },
//  { "custom",         gtkhandler_custom },
    { "text",           gtkhandler_text }
};

void set_window_properties(GtkWidget *window)
{
    gtk_widget_set_size_request (window, 800, 600);
    gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
    gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
    gtk_window_set_decorated (GTK_WINDOW (window), TRUE);
}

void set_design_elements(struct frontend *obj, GtkWidget *window)
{
	/*
	a  scheme of the hierarchy of boxes used to store widgets
	
	0) Globalbox
	1) Mainbox
	2) menubox_scroll
	3) menubox
	4) targetbox_scroll
	5) targetbox
	6) actionbox
	7) infobox
	8) probress_bax
	
	0_________________________
	|  _____  |1 ___________  |
	| |2___ | | |4_________ | |
	| ||3  || | ||5        || |
	| ||   || | ||_________|| |
	| ||   || | |___________| |	
	| ||   || |  ___________  |
	| ||   || | |6          | |	
	| ||   || | |___________| |
	| ||   || |  ___________  |
	| ||   || | |7__________| |	
	| ||___|| |  ___________  |
	| |_____| | |8__________| |	
	|_________|_______________|		
				
			*/
	
	
	
    GtkWidget *mainbox, *globalbox;
    GtkWidget *targetbox, *targetbox_scroll;
    GtkWidget *menubox, *menubox_scroll;
    GtkWidget *actionbox, *infobox;
    GtkWidget *button_next, *button_prev;
	GtkWidget *frame;
	GtkWidget *view;
	GtkTextBuffer *buffer;
	GtkWidget *progress_bar;
	
    int *ret_val;

    mainbox = gtk_vbox_new (FALSE, 10);
    gtk_container_set_border_width (GTK_CONTAINER(mainbox), 5);

    /*
This is the set of box and the copntaining viewport where the questions will be displayed
    */
    targetbox = gtk_vbox_new (FALSE, 10);
    ((struct frontend_data*) obj->data)->target_box = targetbox;

    targetbox_scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (targetbox_scroll), targetbox);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (targetbox_scroll),
                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);


    /*
    Here are the back and forward buttons
    */
    actionbox = gtk_hbutton_box_new();
gtk_button_box_set_layout (GTK_BUTTON_BOX(actionbox), GTK_BUTTONBOX_END);
    gtk_box_pack_end (GTK_BOX(mainbox), actionbox, FALSE, FALSE, 5);

    button_prev = gtk_button_new_from_stock (GTK_STOCK_GO_BACK);
    ret_val = NEW(int);
    *ret_val = DC_GOBACK;
    gtk_object_set_user_data (GTK_OBJECT(button_prev), ret_val);
    g_signal_connect (G_OBJECT(button_prev), "clicked",
                      G_CALLBACK(exit_button_callback), obj);


    gtk_box_pack_start (GTK_BOX(actionbox), button_prev, TRUE, TRUE, 2);

    button_next = gtk_button_new_from_stock (GTK_STOCK_GO_FORWARD);
    ret_val = NEW(int);
    *ret_val = DC_OK;
    gtk_object_set_user_data (GTK_OBJECT(button_next), ret_val);
    g_signal_connect (G_OBJECT(button_next), "clicked",
                      G_CALLBACK(exit_button_callback), obj);
    gtk_box_pack_start (GTK_BOX(actionbox), button_next, TRUE, TRUE, 2);
    GTK_WIDGET_SET_FLAGS (button_next, GTK_CAN_DEFAULT);

    ((struct frontend_data*) obj->data)->button_prev = button_prev;
    ((struct frontend_data*) obj->data)->button_next = button_next;


 	/*
Here a frame is created to display extended descriptions about the questions
 	*/
	view = gtk_text_view_new ();
	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
	gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
	gtk_text_view_set_editable ( GTK_TEXT_VIEW(view) , FALSE);
	gtk_text_view_set_cursor_visible ( GTK_TEXT_VIEW(view) , FALSE);
	gtk_text_view_set_wrap_mode ( GTK_TEXT_VIEW(view) , GTK_WRAP_WORD);
	gtk_text_view_set_left_margin( GTK_TEXT_VIEW(view) , 5);
	gtk_text_view_set_right_margin( GTK_TEXT_VIEW(view) , 5);
    ((struct frontend_data*) obj->data)->info_box = view;
    frame = gtk_frame_new("Description");
    gtk_container_add(GTK_CONTAINER (frame), view);
    infobox = gtk_vbox_new (FALSE, 10);
    gtk_box_pack_start(GTK_BOX (infobox), frame, TRUE, TRUE, 5);
    		
	
 	/*
Here is created a progress bar; probably it will be removed from here in the future and placed somewher else
 	*/
    obj->progress_title = NULL;
    obj->progress_min = 0;
    obj->progress_max = 100;
    obj->progress_cur = 0;
    progress_bar = gtk_progress_bar_new ();
    ((struct frontend_data*)obj->data)->progress_bar = progress_bar;
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), "progressbar");


	/*
This is where the main-menu will be displayed, in the left-area of the screen
	*/
	menubox = gtk_vbox_new (FALSE, 10);
    ((struct frontend_data*) obj->data)->menu_box = menubox;
    menubox_scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW (menubox_scroll), menubox);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (menubox_scroll),
                                   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	

	/*
	Final packaging
	*/
gtk_box_pack_start(GTK_BOX (mainbox), targetbox_scroll, TRUE, TRUE, 5);
    gtk_box_pack_start(GTK_BOX (mainbox), infobox, TRUE, FALSE, 5);
	gtk_box_pack_end(GTK_BOX (mainbox), progress_bar, TRUE, FALSE, 5);

    globalbox = gtk_hbox_new (FALSE, 10);
    gtk_box_pack_start(GTK_BOX (globalbox), menubox_scroll, TRUE, TRUE, 5);
    gtk_box_pack_start(GTK_BOX (globalbox), mainbox, TRUE, TRUE, 5);

    gtk_container_add(GTK_CONTAINER(window), globalbox);


}



static int gtk_initialize(struct frontend *obj, struct configuration *conf)
{

	struct frontend_data *fe_data;
    GtkWidget *window;
    int args = 1;
    char **name;

    //FIXME: This can surely be done in a better way
    (char**) name = malloc(2 * sizeof(char*));
    (char*) name[0] = malloc(12 * sizeof(char));
    name[0] = "debian-installer";
    name[1] = NULL;

    obj->data = NEW(struct frontend_data);
    obj->interactive = 1;
	
	
	/*
Here we setup in the frontend structure the datas needed for the mechanism needed to jump across questions
	*/
	fe_data=obj->data;
	strcpy(fe_data->jump_target,"");
	fe_data->number_of_questions=0;
			
	syslog(LOG_DEBUG,"gtk_fe_debug - gtk_initialize() called\n");
	
    gtk_init (&args, &name);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    set_window_properties (window);
    set_design_elements (obj, window);
    ((struct frontend_data*) obj->data)->window = window;
    gtk_widget_show_all(window);

    return DC_OK;
}

static int gtk_go(struct frontend *obj)
{
    struct frontend_data *data = (struct frontend_data *) obj->data;
    struct question *q = obj->questions;
    struct question *q_last;
    int i,j, number_of_questions=0;
    int ret;
    GtkWidget *questionbox, *menubox;

    if (q == NULL) return DC_OK;

    /*
"number_of_questions" will take count of the question list's lenght, while "q_last" will always point to the last question in the list
    */
    q_last=q;
    number_of_questions=1;
    while(q_last->next!=NULL)
    	{
    	number_of_questions++;
    	q_last=q_last->next;
    	}
syslog(LOG_DEBUG,"gtk_fe_debug - gtk_go(obj->capability=%d ) called, question list's lenght: %d ",obj->capability, number_of_questions);
	data->number_of_questions=number_of_questions;


    /*
    gtk_fe_debug this piece of code impements the "jump" mechanism
    */
	if( strcmp(data->jump_target,"") != 0 )
		{
		if( number_of_questions==1 )
			{
			/*
the first question in the list is always the main-menu, so we need to set it by hand to tell the fronted to jump to the module clicked by the user
			*/
syslog(LOG_DEBUG,"gtk_fe_debug - gtk_go() jumping to \"%s\"\n", data->jump_target );
			q = obj->questions;
			question_setvalue(q, data->jump_target);
			obj->qdb->methods.set(obj->qdb, q);
q->next=NULL;
			strcpy(data->jump_target,"");
			data->number_of_questions=0;
						
			data->button_val = DC_OK;
			
			q_last=obj->questions;
			while(q_last->next!=NULL)
		    	{
		    	q_last=q_last->next;
		   		q_last=q_last->next;
		    	}
			
			return DC_OK;
			}
		else
			{
			data->button_val = DC_GOBACK;
syslog(LOG_DEBUG,"gtk_fe_debug - gtk_go() preparing to jump to \"%s\"\n", data->jump_target ); q=q_last->prev; //gtk_fe_debug+ without this the ASSERT() in frontend.c, line 15, fails!
q->next=NULL;			
			return DC_GOBACK;
			}
		}


    data->setters = NULL;

    questionbox = gtk_vbox_new(FALSE, 5);
	menubox = gtk_vbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(data->target_box), questionbox, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(data->menu_box), menubox, FALSE, FALSE, 5);


	j=0;
    while (q != NULL)
    	{
    	j++;
    	syslog(LOG_DEBUG,"  gtk_fe_debug - question %d: %s",j,q->tag);
        for (i = 0; i < DIM(question_handlers); i++)
        	{
            if (strcmp(q->template->type, question_handlers[i].type) == 0)
            	{

				/*
the first question is always (?) the main menu and must be shown in the left menu
				the other questions are shown in the questionbox
				*/
            	if (j==1)
            		ret = question_handlers[i].handler(obj, q, menubox);
	           	else if (j>1)
	           		ret = question_handlers[i].handler(obj, q, questionbox);

                if (ret != DC_OK)
                	{
syslog(LOG_DEBUG,"gtk_fe_debug - question %d: \"%s\" failed to display!\n",j,q->tag);
                    //return ret;
                	}
            	}
            }
q->prev=NULL; //gtk_fe_debug+ without this the ASSERT() in frontend.c, line 15, fails!
        q = q->next;    	
    	}
	
    q = obj->questions;


	/*
the "back button" will be shown only if there is more than one question to show (if there is just one question the user must click in the left menu)
	*/
if( number_of_questions > 1 && obj->methods.can_go_back(obj, q) ) gtk_widget_set_sensitive (data->button_prev, TRUE );
    else gtk_widget_set_sensitive (data->button_prev, FALSE );

if ( number_of_questions > 1 ) gtk_widget_set_sensitive(GTK_WIDGET(data->button_next), TRUE);
    else gtk_widget_set_sensitive(GTK_WIDGET(data->button_next), FALSE);

    gtk_widget_show_all(data->window);

	gtk_main();

    if (data->button_val == DC_OK)
	    {
		call_setters(obj);

		q = obj->questions;
	
		while (q != NULL)	
			{
		    obj->qdb->methods.set(obj->qdb, q);
		    q = q->next;
			}
		q = obj->questions;
   		}
   		
	else if (data->button_val == DC_GOBACK && q_last->prev != NULL ) 		
	    {
	    q = q_last;
		q = q_last->prev;
	    }
	q->next=NULL;
   	//q->prev=NULL;

	gtk_widget_destroy(questionbox);
	gtk_widget_destroy(menubox);

    if (data->button_val == DC_OK) return DC_OK;
    else if (data->button_val == DC_GOBACK) return DC_GOBACK;
	else return DC_OK;

}

static bool gtk_can_go_back(struct frontend *obj, struct question *q)
{
    return (obj->capability & DCF_CAPB_BACKUP);
}

static void gtk_progress_start(struct frontend *obj, int min, int max, const char *title)
{
    GtkWidget *progress_bar;

    progress_bar=((struct frontend_data*)obj->data)->progress_bar;

    DELETE(obj->progress_title);
    obj->progress_title=strdup(title);
    obj->progress_min = min;
    obj->progress_max = 100;
    obj->progress_cur = min;

//syslog(LOG_DEBUG,"gtk_fe_debug - gtk_progress_start(min=%d, max=%d, title=%s) called ",min ,max, title );

//gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), "gtk_progress_start() called");

    while (gtk_events_pending ())
	gtk_main_iteration ();
}

static void gtk_progress_set(struct frontend *obj, int val)
{
    gdouble progress;
    GtkWidget *progress_bar;

	/*
	
	The progress bar doesn't work yet: incorrect val int is passed!
	
syslog(LOG_DEBUG,"gtk_fe_debug - gtk_progress_set(val=%d) called\n", val);

    progress_bar=((struct frontend_data*)obj->data)->progress_bar;

    obj->progress_cur = val;
    if ((obj->progress_max - obj->progress_min) > 0)
    	{
        progress = (gdouble)(obj->progress_cur - obj->progress_min) /
                   (gdouble)(obj->progress_max - obj->progress_min);
		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), progress);
	
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), "gtk_progress_set() funziona");
    	}

    while (gtk_events_pending ())	gtk_main_iteration ();
	*/
}

static void gtk_progress_info(struct frontend *obj, const char *info)
{
    GtkWidget *progress_bar;

syslog(LOG_DEBUG,"gtk_fe_debug - gtk_progress_info(%s) called\n", info );

    progress_bar=((struct frontend_data*)obj->data)->progress_bar;
    /*
gtk_progress_info doesn't work yet: it receives an incorrect poiter to the buffer that should contain the string to be diplayed



    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), info);
    */

	while (gtk_events_pending ()) gtk_main_iteration ();
}

static void gtk_progress_stop(struct frontend *obj)
{
    //syslog(LOG_DEBUG,"gtk_fe_debug - gtk_progress_stop() called\n");
gtk_widget_destroy(gtk_widget_get_parent(((struct frontend_data*)obj->data)->progress_bar));
    gtk_widget_show_all(((struct frontend_data*)obj->data)->window);
    while (gtk_events_pending ())
	gtk_main_iteration ();
}

static unsigned long gtk_query_capability(struct frontend *f)
{
	syslog(LOG_DEBUG,"gtk_fe_debug - gtk_query_capability() called\n");
	return DCF_CAPB_BACKUP;
}

static int gtk_add(struct frontend *obj, struct question *q)
{

	return DC_OK;
}

struct frontend_module debconf_frontend_module =
{
    initialize: gtk_initialize,
    go: gtk_go,
//    shutdown: gtk_shutdown,	//gtk_fe_debug +
//    clear: gtk_clear,	//gtk_fe_debug +
//    add: gtk_add,		//gtk_fe_debug +
    can_go_back: gtk_can_go_back,
    progress_start: gtk_progress_start,
    progress_info: gtk_progress_info,
    progress_set: gtk_progress_set,
    progress_stop: gtk_progress_stop,
    query_capability: gtk_query_capability,
};

/*
static int gtk_shutdown(struct frontend *obj) //gtk_fe_debug +
{
	struct question *q = obj->questions;
	syslog(LOG_DEBUG,"gtk_fe_debug - gtk_shutdown() called\n");
	return DC_OK;
}


static int gtk_clear(struct frontend *obj) //gtk_fe_debug +
{
	syslog(LOG_DEBUG,"gtk_fe_debug - gtk_clear() called\n");
	return DC_OK;
}
*/






---------------cdebconf_gtk.c-----------------------

#include "common.h"
#include "template.h"
#include "question.h"
#include "frontend.h"
#include "database.h"
#include "strutl.h"

#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dlfcn.h>

#include <gtk/gtk.h>


/* Use this struct to store data that shall live between the questions */
struct frontend_data
{
  /* Main window of the frontend */
  GtkWidget *window;
  /* Pointer to the box, where question widgets shall be stored in */
  GtkWidget *target_box;
  /* Pointer to the box, where help messages shall be stored in */
  GtkWidget *info_box;
  /* Pointer to the box, where the main menu shall be stored in */
  GtkWidget *menu_box;

  /* Buttons for the druid-like interface */
  GtkWidget *button_next;
  GtkWidget *button_prev;

  /* Pointer to the Progress Bar, when initialized */
  GtkWidget *progress_bar;

  /* Struct to register the Set Functions of the Widgets */
  struct setter_struct *setters;
  /* Value of the button pressed to leave a form */
  int button_val;


/* this counter keeps count of the number of question in the question list during a gtk_go() run */
  int number_of_questions;

  char jump_target[40];

};

/* Embed frontend ans question in this object to pass it through an event handler */
struct frontend_question_data
{
    struct frontend *obj;
    struct question *q;
};

/* Functions registered here will be called after each question run. It is to be used to retrieve the data from the widgets and store it in the question database */
void register_setter(void (*func)(void*, struct question*),
		     void *data, struct question *q, struct frontend *obj);

/* Returns TRUE if the question specified is the first question in a row. To be used to
   determine if an associated widget should grab the focus. */
gboolean is_first_question(struct question *q);

/* Used to free the frontend_question_data struct which is used to transport data via callbacks. Can be used as a callback for the destroy signal of a widget associated
   to the question.*/
void free_description_data( GtkObject *obj, struct frontend_question_data* data );

/* Function which can be used as callback. Shows the description of specified question
   in the description area. Can be used when receiving focus */
gboolean show_description( GtkWidget *widget, struct frontend_question_data* data );



Reply to: