/******************************************************************************
 *
 *    Name: UIDialogLibGTK.c
 *
 *    $Id$
 *
 *    Purpose: A GTK+ implementation of the UI Dialog Library.
 *
 *    Copyright 2013-2018 Citrix Systems, Inc. All rights reserved.
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <gtk/gtk.h>                                
#include <gdk/gdkkeysyms.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <pthread.h>
#include <X11/Xlib.h>
#define __USE_GNU /* For RTLD_DEFAULT. */
#include <dlfcn.h>

#include "UIDialogLib.h"
#include "UIDialogLibGTK.h"

/* Things still TODO */
// TODO State which elements of the structures actually can be updated in the interface


/* Defines */
#define COL_SPACING 12
#define ROW_SPACING 6
#define BUTTON_BOX_SPACING 12
#define BUTTON_BOX_TOP_PADDING 18
#define SPINNER_SPACING 6
#define WINDOW_BORDER_WIDTH 16
#define BORDER_WIDTH 12
#define EXPANDER_HEIGHT 120
#define EXPANDER_SPACING 6
#define EXPANDER_BORDER 6
#define SCROLLER_HEIGHT 400
#define SELECTION_TABLE_HEIGHT 264
#define MIN_DIALOG_WIDTH 500
#define ICON_WIDTH 32
#define LABEL_WIDTH (MIN_DIALOG_WIDTH - (2*WINDOW_BORDER_WIDTH) - COL_SPACING - ICON_WIDTH)
#define MOUSE_HOVER_COLOUR   "#e6f1f7"
#define SELECTED_COLOUR      "#cce3f0"
#define UNSELECTED_COLOUR    "white"
#define SPLASHSCREEN_COLOR   "#FFFFFF"
#define SPLASH_LEARNMORE_URL "https://www.citrix.com/products/workspace-app/download-citrix-workspace-app.html"
/* Globals */
const char   *gicaroot = NULL;
unsigned int  option_bits = 0;
/* Local Globals */
static int             *gargc;
static char          ***gargv;
static pthread_cond_t   ui_init_cond  = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t  ui_init_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t  one_dialog_mutex = PTHREAD_MUTEX_INITIALIZER;
static GThread         *ui_thread = NULL;
static gboolean         ui_thread_running = FALSE;
static const char      *timeout_format = "%d";
/* Colour cache. */

static GdkColor colSelected;
static GdkColor colUnselected;
static GdkColor colHover;
static GdkColor colSplash;

static void (*ld_func)(void);

/* Structure used to pass information to the button callbacks. */
struct buttonResp
{
    UIDIALOG *dialog;
    const char *button_response;
};


struct selectionTable
{
    GtkWidget **table;
    GtkWidget *hover;
    unsigned int num_entries;
    unsigned int selected;
};


/** @brief Callback on an enter-notify-event on an element in the
 *         selectionTable.
 *
 * @param widget    The widget which has just been entered.
 * @param event     The event which triggered this callback.
 * @param pst       The selectionTable which is involved.
 * @return          Always returns FALSE to allow the event to propagate
 *                  further.
 */
static gboolean enter_notify_event_cb(GtkWidget *widget,
                                      GdkEvent  *event,
                                      struct selectionTable *pst)
{
    if (widget != pst->table[pst->selected])
    {
        /* If the selectionTable entry is not selected then give it the hover
           background colour. */
        gtk_widget_modify_bg(GTK_WIDGET(widget), GTK_STATE_NORMAL, &colHover);
    }

    /* If we were previously hovering on an element and that element is not
     * selected. Then reset the elements background colour. */
    if (pst->hover != NULL && pst->hover != pst->table[pst->selected])
        gtk_widget_modify_bg(GTK_WIDGET(pst->hover), GTK_STATE_NORMAL, &colUnselected);

    pst->hover = widget;

    return FALSE;
}


/** @brief Callback on an leave-notify-event on an element in the
 *         selectionTable.
 *
 * @param widget    The widget which has just been entered.
 * @param event     The event which triggered this callback.
 * @param pst       The selectionTable which is involved.
 * @return          Always returns FALSE to allow the event to propagate
 *                  further.
 */
static gboolean leave_notify_event_cb(GtkWidget *widget,
                                      GdkEvent  *event,
                                      struct selectionTable *pst)
{
    if (widget != pst->table[pst->selected])
    {
        /* If the entry is not selected then reset its background colour. */
        gtk_widget_modify_bg(GTK_WIDGET(widget), GTK_STATE_NORMAL, &colUnselected);
    }

    /* If the hover widget is this widget then we no longer have a widget
       which has been entered. */ 
    if (pst->hover == widget)
        pst->hover = NULL;

    return FALSE;
}


/** @brief Callback on a button-press-event on an element in the
 *         selectionTable.
 *
 * @param widget    The widget which has just been pressed.
 * @param event     The event which triggered this callback.
 * @param pst       The selectionTable which is involved.
 * @return          Always returns FALSE to allow the event to propagate
 *                  further.
 */
static gboolean    button_press_event_cb               (GtkWidget *widget,
                                                        GdkEvent  *event,
                                                        struct selectionTable *pst)
{
    /* Change the background of the previously selected entry in the
       selectionTable to MOUSE_HOVER_COLOUR or UNSELECTED_COLOUR depending
       if it is currently entered or not. */
    if (pst->hover == pst->table[pst->selected])
        gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colHover);
    else
        gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colUnselected);

    /* Find which element was pressed. */
    int i;
    for (i = 0; pst->table[i] != NULL; i++)
    {   
        if (pst->table[i] == widget)
            pst->selected = i;
    }   

    /* Set the background of the newly selected entry to SELECTED_COLOUR. */
    gtk_widget_modify_bg(GTK_WIDGET(widget), GTK_STATE_NORMAL, &colSelected);

    return FALSE;
}


/** @brief Callback on a key-press-event on the selectionTable.
 *
 * @param widget    The widget which has just received the key press.
 * @param event     The event which triggered this callback.
 * @param pst       The selectionTable which is involved.
 * @return          I return TRUE if it is an up or down key we have processed
 *                  to stop it propagating. Otherwise return FALSE to allow the
 *                  key press to propagate.
 */
static gboolean    key_press_event_cb               (GtkWidget    *widget,
                                                     GdkEventKey  *event,
                                                     struct selectionTable *pst)
{
#ifdef GDK_KEY_Up 
    if (event->keyval == GDK_KEY_Up)
#else
    if (event->keyval == GDK_Up)
#endif
    {
        if (pst->selected > 0)
        {
            /* If the UP key was pressed and we have not reached the top of the
               table. Then move the selected entry up one. */

            if (pst->hover == pst->table[pst->selected])
                gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colHover);
            else
                gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colUnselected);

            pst->selected--;

            gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colSelected);

            return TRUE;
        }
    }

#ifdef GDK_KEY_Down 
    if (event->keyval == GDK_KEY_Down)
#else
    if (event->keyval == GDK_Down)
#endif
    {
        if (pst->selected < pst->num_entries - 1)
        {
            /* If the DOWN key was pressed and we have not reached the bottom
               of the table. Then move the selected entry down one. */
            if (pst->hover == pst->table[pst->selected])
                gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colHover);
            else
                gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colUnselected);

            pst->selected++;

            gtk_widget_modify_bg(GTK_WIDGET(pst->table[pst->selected]), GTK_STATE_NORMAL, &colSelected);

            return TRUE;
        }
    }

    return FALSE;
}


/* Iterate over the elements extracting any information entered in to the dialog. */
static void harvest_input(struct formsElement **formIter, GList *harvestList)
{
    /* Update the contents of the structures with the contents of the UI */
    for (; *formIter != NULL; formIter++)
    {
        struct formsElement *currentElement = *formIter;

        switch (currentElement->input.type)
        {
            case I_Text:
            {
                char *entry_string;

                /* Update the text entered */
                currentElement->input.inputUnion.textBox.text = strdup(gtk_entry_get_text(GTK_ENTRY(harvestList->data)));

                entry_string = gtk_editable_get_chars(GTK_EDITABLE(harvestList->data), 0 , -1);

                memset(entry_string, 0, strlen(entry_string)); /* Wipe the contents of the string */

                /* Move on the harvest list. */
                harvestList = g_list_next(harvestList);
                break;
            }
            case I_CheckBox:
            {
                /* Update the toggle position */
                currentElement->input.inputUnion.checkBox.select = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(harvestList->data));

                /* Move on the harvest list. */
                harvestList = g_list_next(harvestList);

                break;
            }
            case I_RadioButton:
            {
                int i = 0;

                /* Update the selected Radio Button. */
                while (currentElement->input.inputUnion.radioButton[i])
                {
                    currentElement->input.inputUnion.radioButton[i]->select = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(harvestList->data));
                    i++;

                    /* Move on the harvest list. */
                    harvestList = g_list_next(harvestList);
                }
                break;
            }
            case I_ComboBox:
            {
                int i = 0;
                int active;

                active = gtk_combo_box_get_active(harvestList->data);

                /* Update the selections. */
                while (currentElement->input.inputUnion.comboBox[i])
                {
                    if (i == active)
                        currentElement->input.inputUnion.comboBox[i]->select = TRUE;
                    else
                        currentElement->input.inputUnion.comboBox[i]->select = FALSE;
                    i++;
                }

                /* Move on the harvest list. */
                harvestList = g_list_next(harvestList);

                break;
            }
            case I_MultiComboBox:
            {
                int i = 0;

                /* Update the selections. */
                while (currentElement->input.inputUnion.multiComboBox[i])
                {
                    currentElement->input.inputUnion.multiComboBox[i]->select = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(harvestList->data));
                    i++;

                    /* Move on the harvest list. */
                    harvestList = g_list_next(harvestList);
                }
                break;
            }
            case I_Expander:
            {
                harvest_input(currentElement->input.inputUnion.expander, harvestList);
                break;
            }
            case I_Scroller:
            {
                harvest_input(currentElement->input.inputUnion.scroller, harvestList);
                break;
            }
            case I_SelectionTable:
            {
                /* Get the currently selected element from the selectionTable
                   structure associated with this selectionTable. */
                unsigned int selected = ((struct selectionTable*) harvestList->data)->selected;

                /* Move on the harvest list. */
                harvestList = g_list_next(harvestList);

                struct selectionElement **selIter;
                unsigned int sel_pos;
                for (selIter = currentElement->input.inputUnion.selectionTable,
                     sel_pos = 0;
                     *selIter != NULL; selIter++, sel_pos++)
                {
                    /* Update the passed in formsElement structures to match
                       the new selected element. */
                    if (sel_pos == selected)
                        (*selIter)->select = TRUE;
                    else
                        (*selIter)->select = FALSE;

                    /* Recursively harvest the elements in the selectionTable. */
                    harvest_input((*selIter)->elements, harvestList);
                }
            }
            case I_Button:
            case I_Label:
            case I_LinkButton:
            case I_None:
            default:
                continue; /* We have no input element don't increment along the harvestLists. */
        }
    }
}


/* Free a glist data item. */
static void glist_free(gpointer data, gpointer not_used)
{
    free(data);
}

/* Free a selectionTable. */
static void selectionTable_free(struct selectionTable *data, gpointer not_used)
{
    /* Free all data allocated for this selection table. */
    free(data->table);
    free(data);
}


static void free_and_quit(UIDIALOG *dialog)
{
    /* Free everything we don't need any more. */
    g_list_free(dialog->elementList);
    dialog->elementList = NULL;
    g_list_foreach(dialog->brespList, glist_free, NULL);
    g_list_free(dialog->brespList);
    dialog->brespList = NULL;

    /* Remove the key press callback. */
    if (dialog->key_press_src)
    {
       g_signal_handler_disconnect (dialog->window, dialog->key_press_src);
    }

    /* Remove the standard timeout if there is one. */
    if (dialog->timeout_src)
    {
        g_source_remove(dialog->timeout_src);
        dialog->timeout_src = 0;
    }

    if (dialog->idle_timeout_src)
    {
        g_source_remove(dialog->idle_timeout_src);
        dialog->idle_timeout_src = 0;

        if (dialog->idle_timeout_change_srcs != NULL)
        {
            g_list_foreach(dialog->idle_timeout_change_srcs, glist_free, NULL);
            g_list_free(dialog->idle_timeout_change_srcs);
            dialog->idle_timeout_change_srcs = NULL;
        }
    }

    /* Free up the structures used internally to trace the state of any
       selectionTables. */
    if (dialog->selectionTablesList)
    {
        g_list_foreach(dialog->selectionTablesList, (GFunc) selectionTable_free, NULL);
        g_list_free(dialog->selectionTablesList);
        dialog->selectionTablesList = NULL;
    }

    gtk_main_quit();
}


/** @brief This function is called once UI_DIALOG_DISPLAY* has harvested input.
    It greys out the dialog and displays the spinner. It also clears up any
    memory associated with harvesting input.

    @param dialog[in,out] is a pointer to the opaque UIDIALOG structure.
*/
static void deactivateDialog(UIDIALOG *dialog)
{
    /* Grey out the dialog. */
    gtk_widget_set_sensitive(dialog->win_contents, FALSE);

    /* Show the spinner. */
    if (dialog->spinner)
        gtk_widget_show(dialog->spinner);

    free_and_quit(dialog);
}


/** @brief This is a callback triggered when a key is pressed. We look out for
    for the ESC key and interpret this as a cancel.

    @param widget_unused[in] is unused
    @param event is the event structure for the given key press.
    @param dialog[in,out] is a pointer to the opaque UIDIALOG structure.
*/
static gboolean key_press_cb(GtkWidget *widget_unused,
                  GdkEventKey *event,
                  UIDIALOG  *dialog)
{
/* Annoyingly the Escape key has had its name changed. GDK_Escape is the
   older of the two defines. */
#ifdef GDK_KEY_Escape
    if (event->keyval == GDK_KEY_Escape)
#else
    if (event->keyval == GDK_Escape)
#endif
    {
        harvest_input(dialog->form, g_list_first(dialog->elementList));

        /* The default cancel response is NULL. */
        dialog->response = NULL;

        deactivateDialog(dialog);

        return TRUE;
    }

    return FALSE;
}


/* Callback on clicking the cross to close the dialog window. */
static gboolean window_closing_cb(GtkWidget *not_used_widget, GdkEvent *not_used_event, UIDIALOG *dialog)
{
    /* Is the dialog being closed when it is interactive? */
    if (gtk_widget_is_sensitive(dialog->win_contents) == TRUE)
    {
        /* The default cancel response is NULL. */
        dialog->response = NULL;
        harvest_input(dialog->form, g_list_first(dialog->elementList));
        free_and_quit(dialog);
    }

    /* Remove the close signal. */
    g_signal_handler_disconnect(dialog->window, dialog->close_signal);

    /* NULL the pointers to any widgets as they are about to be destroyed. */
    dialog->win_contents = NULL;
    dialog->window = NULL;
    dialog->spinner = NULL;

    return FALSE; /* Let the window close. */
}


/* Timer callback for dialog timeout. Called every second. */
static gboolean timeout_tick_cb(UIDIALOG *dialog)
{
    gchar* full_string;

    /* Insert count down time into template string */
    full_string = g_strdup_printf(timeout_format, dialog->timeout);

    /* Update the body string in the dialog */
    gtk_label_set_label(GTK_LABEL(dialog->timeout_label), full_string);
    g_free(full_string);

    if (dialog->timeout > 0)
    {
        dialog->timeout--;

        /* Continue timeout */
        return TRUE;
    }
    else
    {
        harvest_input(dialog->form, g_list_first(dialog->elementList));

        /* Set the response to be the timeout response. */
        dialog->response = dialog->timeout_response;

        /* Stop the timeout. We remove this timeout be returning FALSE. */
        dialog->timeout_src = 0;

        deactivateDialog(dialog);

        return FALSE;
    }
}


/** @brief This is a callback triggered when a element in the UI is modified.
    This allows us to reset the idle timeout period whenever a change is made.

    @param widget_unused[in] is unused
    @param dialog[in,out] is a pointer to the opaque UIDIALOG structure.
*/
static void changed_cb(GtkWidget *widget_unused, UIDIALOG *dialog)
{
    /* Reset the idle timeout. */
    dialog->idle_timeout = dialog->idle_timeout_base;
}


/** @brief This function is responsible for setting up another source to reset
    the idle timeout. A source is required for each modifiable element in the
    dialog.

    @param dialog[in,out] is a pointer to the opaque UIDIALOG structure.
    @param wig[in] is the widget that produces the signal.
    @param signal[in] is the signal to monitor.
*/
static void setup_idle_timeout(UIDIALOG *dialog, GtkWidget *wig, char *signal)
{
    if (dialog->idle_timeout_base > 0)
    {
        guint *gp = malloc(sizeof(guint));
        *gp = g_signal_connect(G_OBJECT(wig), signal, G_CALLBACK(changed_cb), dialog);
        dialog->idle_timeout_change_srcs = g_list_append(dialog->idle_timeout_change_srcs, gp);
    }
}


/** @brief This callback is called every second trying to count down the idle
    timeout. However, it may find the idle timeout has been reset since it was
    last triggered.
    @param dialog[in,out] is a pointer to the opaque UIDIALOG structure.

    @return TRUE or FALSE to indicate if the timeout should repeat of cancel
    respectively.
*/
static gboolean idle_timeout_tick_cb(UIDIALOG *dialog)
{
    if (dialog->idle_timeout > 1)
    {
        dialog->idle_timeout--;

        /* Continue idle timeout */
        return TRUE;
    }
    else
    {
        harvest_input(dialog->form, g_list_first(dialog->elementList));

        /* Set the response to be the timeout response. */
        dialog->response = dialog->idle_timeout_response;

        /* Stop the idle timeout. Returning FALSE will do this. */
        dialog->idle_timeout_src = 0;

        deactivateDialog(dialog);

        return FALSE;
    }
}


/* A button has been pressed. Indicate which button has been pressed. */
static void button_clicked_cb(GtkButton *button, struct buttonResp *bresp)
{
    if(!strcmp(bresp->button_response, "LearnMoreId")) {
        //Create a new window displaying info about Citrix Workspace when user clicks on "Learn More"
        FILE *fp;
        char buf[128];
        int ret = snprintf(buf, 128, "xdg-open %s", SPLASH_LEARNMORE_URL);
        if(ret >=0) {
            if (fp = popen(buf, "r"))
                ret = pclose(fp);
        }
    } else {
        harvest_input(bresp->dialog->form, g_list_first(bresp->dialog->elementList));
    }

    /* Set the response the the button that was pressed. */
    bresp->dialog->response = bresp->button_response;

    if(strcmp(bresp->button_response, "LearnMoreId"))
        deactivateDialog(bresp->dialog);
}


/* This is the hook function used to stop a GtkLinkButton opening a url. */
static void gtk2_link_button_hook(GtkLinkButton *button, const gchar *link_, gpointer user_data)
{
    return;
}


static GtkWidget* load_icon(const char *icon)
{
    gchar *icon_path = g_strconcat(gicaroot, icon, NULL);
    GtkWidget *icon_image = gtk_image_new_from_file(icon_path);
    g_free(icon_path);
    return icon_image;
}


/* At present there is only one icon associated with each dialog
   style so there is a simple one to one mapping maintained here. */
static GtkWidget* get_style_icon(enum dialogStyle style)
{
    switch(style)
    {
        case S_Error:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR,
                                            GTK_ICON_SIZE_DIALOG);
        case S_Warning:
        case S_YesNo:
        case S_Security:
        case S_Reconnect:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING,
                                            GTK_ICON_SIZE_DIALOG);
        case S_Information:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
                                            GTK_ICON_SIZE_DIALOG);
        case S_Flash:
            return load_icon("/icons/flash.png");
        case S_Progress:
            return load_icon("/icons/ActivityIndicatorOnWht_16x16.gif");
        case S_SplashScreen:
            return load_icon("/icons/PromoImage.png");
        case S_None:
        default:
            return NULL;
    }
}


/* Converts a base64 character into a number.
   The output is -1 if the input character represents padding.
   Returns 0 if success, 1 otherwise. */
int decodeBase64Char(unsigned char c, /* out */ unsigned char *out_c)
{
    if (out_c == NULL)
    {
        /* Invalid output parameter. */
        return 1;
    }

    if (('A' <= c) && (c <= 'Z'))
    {
        *out_c = c - 'A';
        return 0;
    }
    if (('a' <= c) && (c <= 'z'))
    {
        unsigned char t = 0;
        int rv = decodeBase64Char('Z', &t);
        if (rv) return rv;
        *out_c = (c - 'a') + (t + 1);
        return 0;
    }
    if (('0' <= c) && (c <= '9'))
    {
        unsigned char t = 0;
        int rv = decodeBase64Char('z', &t);
        if (rv) return rv;
        *out_c = (c - '0') + (t + 1);
        return 0;
    }
    if (c == '+')
    {
        unsigned char t = 0;
        int rv = decodeBase64Char('9', &t);
        if (rv) return rv;
        *out_c = t + 1;
        return 0;
    }
    if (c == '/')
    {
        unsigned char t = 0;
        int rv = decodeBase64Char('9', &t);
        if (rv) return rv;
        *out_c = t + 2;
        return 0;
    }
    if (c == '=')
    {
        *out_c = (unsigned char)-1;
        return 0;
    }

    /* Invalid char. */
    return 1;
}


/* Decodes the given base 64 data array.
   Upon successful operation, out_decoded contains the decoded data
   and in_out_decoded_size contains the actual size of the output.
   Returns 0 if success, 1 otherwise. */
static int decodeBase64Array(const unsigned char     *base64,
                             size_t                   base64_size,
                             /* out */ unsigned char *out_decoded,
                             /* in|out */ size_t     *in_out_decoded_size)
{
    size_t i = 0;
    size_t o = 0;
    int rv = 0;
    unsigned char b1 = 0;
    unsigned char b2 = 0;
    unsigned char b3 = 0;
    unsigned char b4 = 0;
    int b3padded = 0;
    int b4padded = 0;

    if (base64 == NULL
            || base64_size == 0
            || out_decoded == NULL
            || in_out_decoded_size == NULL)
    {
        /* Invalid input parameters. */
        return 1;
    }

    if (base64_size % 4 != 0)
    {
        /* The input data length was not a multiple of 4. */
        return 1;
    }

    if (*in_out_decoded_size < base64_size)
    {
        /* The output buffer is not large enough. */
        return 1;
    }

    while (i < base64_size)
    {
        /* Convert a chunk of 4 chars into 3 data bytes. */
        rv = decodeBase64Char(base64[i++], &b1);
        if (rv) return rv;
        rv = decodeBase64Char(base64[i++], &b2);
        if (rv) return rv;
        rv = decodeBase64Char(base64[i++], &b3);
        if (rv) return rv;
        rv = decodeBase64Char(base64[i++], &b4);
        if (rv) return rv;

        /* b1 and b2 cannot be padding. */
        if (b1 == (unsigned char)-1 || b2 == (unsigned char)-1)
        {
            /* Non-trailing equals characters found. */
            return 1;
        }

        out_decoded[o++] = (b1 << 2) | (b2 >> 4);

        b3padded = b3 == (unsigned char)-1;
        b4padded = b4 == (unsigned char)-1;

        if (b3padded || b4padded)
        {
            /* There is padding; check it forms the very last characters only. */
            if (b3padded && !b4padded)
            {
                /* Invalid padding. */
                return 1;
            }

            if (i != base64_size)
            {
                /* Invalid padding. */
                return 1;
            }
        }

        if (!b3padded)
        {
            /* b3 is not padding. */
            out_decoded[o++] = (b2 << 4) | (b3 >> 2);

            if (!b4padded)
            {
                /* b4 is not padding. */
                out_decoded[o++] = (b3 << 6) | b4;
            }
        }
    }

    /* Set the output size. */
    *in_out_decoded_size = o;

    return 0;
}


/* Creates and returns a GtkImage from the given binary data.
   The binary data must be formatted as a Data URL (aka a Data URI Scheme):

   data:[<mimetype>][;base64],<data>

   Example: data:image/gif;base64,SGVsbG8lMkMlMjBXb3JsZCE=

   References:
   https://en.wikipedia.org/wiki/Data_URI_scheme
   https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs

   The binary data must be null-terminated. */
static GtkWidget *gtk_image_new_from_binary_data(const char *binary_data)
{
    size_t i = 0;
    size_t j = 0;

    const char *data_header = "data:";
    const size_t data_header_size = 5;
    int data_header_found = 0;

    int mime_type_found = 0;

    const char *base64_tag = "base64,";
    const size_t base64_tag_size = 7;
    int base64_tag_found =  0;

    size_t binary_data_size = 0;
    unsigned char *data = NULL;
    size_t data_size = 0;

    GdkPixbufLoader *pix_buf_loader = NULL;
    GdkPixbuf *pix_buf = NULL;
    GtkWidget *image = NULL;
    gboolean rv = FALSE;

    /* Check that the input binary data is valid. */
    if (binary_data == NULL)
    {
        return NULL;
    }

    /* Skip any white space before the next field.*/
    while (binary_data[i] == ' ')
    {
        i++;
    }

    /* Look for the "data:" header. */
    data_header_found = strncmp(&(binary_data[i]), data_header, data_header_size) == 0;
    if (!data_header_found)
    {
        /* No "data:" header. */
        return NULL;
    }
    i += data_header_size;

    /* Skip any white space before the next field.*/
    while (binary_data[i] == ' ')
    {
        i++;
    }

    /* Get the mime type (optional). */
    j = i;
    while (binary_data[i] != '\0')
    {
        if (binary_data[i] == ';')
        {
            mime_type_found = 1;
            break;
        }
        i++;
    }

    /* Create a GTK pixel buffer loader. */
    if (mime_type_found)
    {
        char *mime_type = NULL;
        size_t mime_type_size = 0;

        /* Use the found mime type. */
        mime_type_size = i - j;
        mime_type = (char*)malloc(mime_type_size + 1);
        strncpy(mime_type, &(binary_data[j]), mime_type_size);
        mime_type[mime_type_size] = '\0';

        /* Create a loader for the specific found mime type. */
        pix_buf_loader = gdk_pixbuf_loader_new_with_mime_type(mime_type, NULL);

        /* Cleanup. */
        free(mime_type);
        mime_type = NULL;

        /* Move the parsing index past the mime type. */
        i++;
    }
    else
    {
        /* No mime type found. Create a generic loader. */
        pix_buf_loader = gdk_pixbuf_loader_new();

        /* Restore the parsing index. */
        i = j;
    }
    if (pix_buf_loader == NULL)
    {
        /* An error has occurred creating the pixel buffer loader. */

        return NULL;
    }

    /* Skip any white space before the next field.*/
    while (binary_data[i] == ' ')
    {
        i++;
    }

    /* Get the base64 enconding tag (optional). */
    base64_tag_found = strncmp(&(binary_data[i]), base64_tag, base64_tag_size) == 0;
    if (base64_tag_found)
    {
        /* Move the parsing index past the base64 encoding tag. */
        i += base64_tag_size;
    }

    /* Skip any white space before the next field.*/
    while (binary_data[i] == ' ')
    {
        i++;
    }

    /* Get the actual image data. */
    j = i;
    while (binary_data[i] != '\0'
           && binary_data[i] != ' ')
    {
        i++;
    }
    binary_data_size = i - j;
    if (base64_tag_found)
    {
        int rv = 0;

        /* The image data has to be decoded from base64. */
        data = (unsigned char*)malloc(binary_data_size);
        data_size = binary_data_size;
        rv = decodeBase64Array((unsigned char*)&(binary_data[j]), binary_data_size, data, &data_size);
        if (rv)
        {
            /* An error occurred decoding the image data from base64. */

            /* Cleanup. */
            free(data);
            data = NULL;
            gdk_pixbuf_loader_close(pix_buf_loader, NULL);

            return NULL;
        }
    }
    else
    {
        /* The binary data is plain, no need to decode it. */
        data = (unsigned char*)&(binary_data[j]);
        data_size = binary_data_size;
    }

    /* Write the data into a pixel buffer. */
    rv = gdk_pixbuf_loader_write(pix_buf_loader, data, data_size, NULL);
    if (rv == FALSE)
    {
        /* An error has occurred writing the image data into the pixel buffer. */

        /* Cleanup. */
        if (base64_tag_found)
        {
            free(data);
            data = NULL;
        }
        gdk_pixbuf_loader_close(pix_buf_loader, NULL);

        return NULL;
    }

    /* Get the pixel buffer. */
    pix_buf = gdk_pixbuf_loader_get_pixbuf(pix_buf_loader);
    if (pix_buf == NULL)
    {
        /* No pixel buffer available. */

        /* Cleanup. */
        if (base64_tag_found)
        {
            free(data);
            data = NULL;
        }
        gdk_pixbuf_loader_close(pix_buf_loader, NULL);

        return NULL;
    }

    /* Create a new GtkImage from the pixel buffer. */
    image = gtk_image_new_from_pixbuf(pix_buf);

    /* Cleanup. */
    if (base64_tag_found)
    {
        free(data);
        data = NULL;
    }
    gdk_pixbuf_loader_close(pix_buf_loader, NULL);

    /* Return the image (it can be null). */
    return image;
}


/* Get label icons. */
static GtkWidget* get_label_icon(struct label *label)
{
    if (label == NULL)
    {
        return NULL;
    }

    switch(label->type)
    {
        /* Use GTK_ICON_SIZE_BUTTON as the GTK documentation states it should
         * be approx 16px. */
        case L_Information:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
                                            GTK_ICON_SIZE_BUTTON);
        case L_Warning:
        case L_Unsecure:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_WARNING,
                                            GTK_ICON_SIZE_BUTTON);
        case L_Error:
            return gtk_image_new_from_stock(GTK_STOCK_DIALOG_ERROR,
                                            GTK_ICON_SIZE_BUTTON);
        case L_Confirmation:
            return gtk_image_new_from_stock(GTK_STOCK_YES,
                                            GTK_ICON_SIZE_BUTTON);
        case L_Secure:
            return load_icon("/icons/padlock_h32bit_16.png");

        case L_Image:
            return gtk_image_new_from_binary_data(label->binaryData);

        default:
            return NULL;
    }
}


/* This function is responsible for creating the overall layout and style of the dialog. */
static GtkWidget *style_dialog(UIDIALOG *dialog, enum dialogStyle style, int content_height)
{
    GtkWidget *icon_image = NULL;
    GtkWidget *content_table;
    GtkWidget *layout_table;     /* Table used to layout the dialog. */
    GdkGeometry geom;

    /* Set the dialog width hints. */
    geom.min_width = MIN_DIALOG_WIDTH;
    geom.min_height = -1;
    geom.max_width = -1;
    geom.max_height = -1;
    gtk_window_set_geometry_hints(GTK_WINDOW(dialog->window), dialog->window, &geom, GDK_HINT_MIN_SIZE);

    /* Set Window icon. */
    {
        gchar *icon_path = g_strconcat(gicaroot, "/icons/receiver.png", NULL);
        gtk_window_set_icon_from_file(GTK_WINDOW(dialog->window), icon_path, NULL);
        g_free(icon_path);
    }

    /* Create the contents table. */
    content_table = gtk_table_new(content_height, 2, FALSE);

    /* Create the layout table. */
    if (dialog->timeout > 0)
        layout_table = gtk_table_new(2, 2, FALSE);
    else {
        if(style == S_SplashScreen)
            layout_table = gtk_table_new(content_height, 2, FALSE);
        else
            layout_table = gtk_table_new(1, 2, FALSE);
    }

    /* Set content table spacing. */
    gtk_table_set_col_spacings(GTK_TABLE(content_table), COL_SPACING);
    gtk_table_set_row_spacings(GTK_TABLE(content_table), ROW_SPACING);

    /* Set the layout table row spacing. */
    gtk_table_set_row_spacings(GTK_TABLE(layout_table), ROW_SPACING);

    icon_image = get_style_icon(style);

    if (icon_image != NULL)
    {
        /* Set the layout table spacing as we have an icon. */
        gtk_table_set_col_spacings(GTK_TABLE(layout_table), COL_SPACING);

        if(style == S_SplashScreen)
            gtk_table_attach(GTK_TABLE(layout_table), icon_image, 0, 2, 2, 4, GTK_SHRINK, GTK_EXPAND|GTK_FILL, 0, 0);
        else    
        /* Attach and align the icon image in the top left. */
        gtk_table_attach(GTK_TABLE(layout_table), icon_image, 0, 1, 0, 1, GTK_SHRINK, GTK_EXPAND|GTK_FILL, 0, 0);
        
        if (style == S_Progress)
            gtk_misc_set_alignment(GTK_MISC(icon_image), 0.5, 0.5);
        else
            gtk_misc_set_alignment(GTK_MISC(icon_image), 0.5, 0);
    }

    if(style != S_SplashScreen)
        /* Attach the content table. */
        gtk_table_attach_defaults(GTK_TABLE(layout_table), content_table, 1, 2, 0, 1);
    else {
        gtk_table_attach_defaults(GTK_TABLE(layout_table), content_table, 0, 2, 0, content_height);
    }

    /* Add the layout_table to the dialog window */
    gtk_container_add(GTK_CONTAINER(dialog->window), layout_table);
    dialog->win_contents = layout_table;

    if (dialog->timeout > 0)
    {
        /* Add timeout label. */
        dialog->timeout_label = gtk_label_new("");
        gtk_misc_set_alignment(GTK_MISC(dialog->timeout_label), 0, 0);
        gtk_table_attach_defaults(GTK_TABLE(layout_table), dialog->timeout_label, 0, 2, 1, 2);
    }

    /* Don't allow the dialog to be resized. */
    gtk_window_set_resizable(GTK_WINDOW(dialog->window), FALSE);
    
    /* Set the border width for the whole window. */
    gtk_container_set_border_width(GTK_CONTAINER(dialog->win_contents), WINDOW_BORDER_WIDTH);

    if(style != S_SplashScreen)
        return content_table;
    else 
        return layout_table;
}


/* Convert the mnemonic string format given in the UI Dialog Library interface
 * to the style expected by GTK+. In the UI Dialog Library interface mnemonics
 * are represented as a character pointer to a string and a character pointer
 * to the mnemonic character within the string or NULL if there is no mnemonic.
 * Whereas GTK pre-seeds the mnemonic character within the string with an '_'.
 * Any real '_'s in the string are represented as double '_'s. */ 
static char *mnemonic_convert(const char *text, const char *mnemonic)
{
    /* GTK+ represent mnemonics as the character with an underscore
       preseeding it. A double underscore means a single underscore. */
    int oristring_len, newstring_len, i;
    int underscore_count = 0;
    const char * iter = text;
    char * newstring;

    oristring_len = strlen(text);

    /* Count the number of underscores in the original string. */
    while (*iter != '\0')
    {
        if (*iter == '_')
        {
            underscore_count++;
        }
        iter++;
    }

    /* Add one for the '\0' terminator. */
    newstring_len = sizeof(char) * (oristring_len + underscore_count + 1);

    /* Add one for the extra underscore for the mnemonic. */
    if (mnemonic != NULL)
        newstring_len++;

    newstring = malloc(newstring_len);
    
    /* Iterate over the old string creating the mnemonic formatted string. */
    for (i = 0, iter = text;
         i < newstring_len;
         i++, iter++)
    {
        if (iter == mnemonic || *iter == '_')
        {
            /* Add the mnemonic underscore or double up the underscore. */
            newstring[i++] = '_'; 
        }

        newstring[i] = *iter;
    }

    return newstring;
}


/* Wraps the given text at about wrap_size (in chars).
 * The input text must be null-terminated.
 * The text is wrapped on word boundaries (i.e. without splitting any word),
 * so it's possible that the actual wrap width gets larger than the given one.
 * This function doesn't do any fancy things like trimming any white space
 * that comes after a wrap point, for example.
 */
static char *wrap_text(const char *text, size_t wrap_size)
{
    size_t text_len = strlen(text);
    char *wrapped_text = strdup(text);

    size_t wrap_index = wrap_size - 1;

    const char *wrap_chars = " ;/?:@=&<>#%{}|\\^~[]";
    const size_t wrap_chars_len = 20;

    size_t i = 0;

    while (wrap_index < text_len)
    {
        int valid_wrap_char = 0;
        for (i = 0; i < wrap_chars_len; i++)
        {
            if (wrapped_text[wrap_index] == wrap_chars[i])
            {
                valid_wrap_char = 1;
                break;
            }
        }
        if (valid_wrap_char)
        {
            wrapped_text[wrap_index] = '\n';
            wrap_index += wrap_size;
        }
        else
        {
            wrap_index++;
        }
    }

    return wrapped_text;
}

/* This is the UI thread. */
static gpointer start_ui_thread(gpointer not_used)
{
    /* Any toolkit specific setup if you need it. */
    gtk_init(gargc, gargv);

    /* Get hold of the GDK lock. This is released by the gtk mainloop as it iterates. */
    gdk_threads_enter();
    gtk_main();
    gdk_threads_leave();
    
    return 0;
}


/** @brief create_button creates a button.
 *
 * @param button    The button structure which to create the button from.
 * @param dialog    The UIDIALOG structure storing all the dialog information.
 * @return          The newly create button as a GtkWidget.
 */
static GtkWidget *create_button(const struct button* button, UIDIALOG *dialog)
{
    GtkWidget *new_button;
    struct buttonResp *bresp;
    
    /* Add a mnemonic to the button if one is given. */
    if (button->mnemonic != NULL)
    {
        char * newstring = mnemonic_convert(button->text, button->mnemonic);
        new_button = gtk_button_new_with_mnemonic(newstring);
        free(newstring);
    }
    else
    {
        new_button = gtk_button_new_with_label(button->text);
    }

    /* Allocate and initialise the button response. */
    bresp = malloc(sizeof(struct buttonResp));
    bresp->dialog = dialog;
    bresp->button_response = button->id;

    /* Store all of these bresp structures up so they can be freed. */
    dialog->brespList = g_list_append(dialog->brespList, bresp);

    g_signal_connect(G_OBJECT(new_button), "clicked", G_CALLBACK(button_clicked_cb), (gpointer) bresp);

    return new_button;
}


/* Create a GtkLabel widget from a struct label. */
static GtkWidget *createLabel(struct label *label, GtkWidget *new_input, GtkWidget **attachPoint, BOOLEAN wrap_label, enum dialogStyle style)
{
    GtkWidget *new_label = NULL;
    GtkWidget *label_table = NULL;

    if (label->text != NULL && label->mnemonic != NULL && new_input != NULL)
    {
        char * newstring = mnemonic_convert(label->text, label->mnemonic);

        new_label = gtk_label_new_with_mnemonic(newstring);
        free(newstring);

        /* Set the widget the mnemonic activates to be the input element
           corresponding with the label. */
        gtk_label_set_mnemonic_widget(GTK_LABEL(new_label), new_input);
    }
    else
    {
        /* If label->text is NULL, an empty label widget is created. */
        new_label = gtk_label_new(label->text);
    }

    if(style != S_SplashScreen)
    /* Align labels to the left so they line up. */
        gtk_misc_set_alignment(GTK_MISC(new_label), 0, 1);
    else { 
        gtk_misc_set_alignment(GTK_MISC(new_label), 0.7, 1);
        gtk_label_set_justify(GTK_LABEL(new_label), GTK_JUSTIFY_CENTER);
    }

    switch (label->type)
    {
        /* If this is a heading label give it a name ending in "_title"
           or "_title2" matches with a rule in our resources.gtkrc file. */
        case L_Heading:
            gtk_widget_set_name(GTK_WIDGET(new_label), "label_title");

            /* Adding some padding above and below to make it look more
               like an HTML heading. */
            gtk_misc_set_padding(GTK_MISC(new_label), 0, 5);
            break;
        case L_Heading2:
            gtk_widget_set_name(GTK_WIDGET(new_label), "label_title2");

            /* Adding some padding above and below to make it look more
               like an HTML heading 2. */
            gtk_misc_set_padding(GTK_MISC(new_label), 0, 5);
            break;
        case L_Error:
            gtk_widget_set_name(GTK_WIDGET(new_label), "label_error");
            break;
        case L_Information:
        case L_Secure:
        case L_Unsecure:       
            gtk_widget_set_name(GTK_WIDGET(new_label), "label_smallprint");
            break;
        default:
            gtk_widget_set_name(GTK_WIDGET(new_label), "label_body");
    }

    /* Add the icon if we need to. */
    {
        GtkWidget *icon_image = get_label_icon(label);

        if (icon_image != NULL)
        {
            label_table = gtk_table_new(1, 2, FALSE);
            gtk_table_set_col_spacings(GTK_TABLE(label_table), COL_SPACING);
            gtk_table_attach(GTK_TABLE(label_table), icon_image, 0, 1, 0, 1, GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
            gtk_table_attach(GTK_TABLE(label_table), new_label, 1, 2, 0, 1, GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0);
            *attachPoint = label_table;
        }
        else
        {
            *attachPoint = new_label;
        }
    }

    if (wrap_label)
    {
        /* Enable wrapping. */
        gtk_label_set_line_wrap(GTK_LABEL(new_label), TRUE);

        if (label->text != NULL && new_input == NULL)
        {
            /* If there is text and the label is not associated with a widget then
               encourage the label width to not extend a MIN_DIALOG_WIDTH dialog. */
            gtk_widget_set_size_request(new_label, LABEL_WIDTH, -1);
            // TODO Make LABEL_WIDTH dependant on the style as the calculation
            // currently assumes there is an icon but for now the above works well.
        }
    }

    return new_label;
}


/** @brief create_button_box creates a empty button box ready to add buttons to.
 * @param dialog            UIDIALOG structure which stores all the dialog info.
 * @param button_box[out]   A pointer to the GtkButtonBox.
 * @return                  Returns a pointer to the container widget for the
 *                          button box and other formatting widgets.
 */
static GtkWidget *create_button_box(UIDIALOG *dialog, GtkWidget **button_box)
{
    GtkWidget *top_padding = gtk_alignment_new(0.5,0.5,1,1);
    GtkWidget *spinner_table = gtk_table_new(1, 2, FALSE);

    /* Use an alightment widget to hold a table of two which holds the
       spinner and the button box. */

    /* Create the button box. */
    *button_box = gtk_hbutton_box_new();
    gtk_box_set_spacing(GTK_BOX(*button_box), BUTTON_BOX_SPACING);

    gtk_button_box_set_layout(GTK_BUTTON_BOX(*button_box), GTK_BUTTONBOX_END);

    /* Align the table holding the spinner and button box. */
    gtk_alignment_set_padding(GTK_ALIGNMENT(top_padding), BUTTON_BOX_TOP_PADDING, 0, 0, 0);

    gtk_table_attach_defaults(GTK_TABLE(spinner_table), *button_box, 1, 2, 0, 1);
    gtk_container_add(GTK_CONTAINER(top_padding), spinner_table);

    if (dialog->spinner != NULL)
    {
        /* Only want a spinner on the last button box. */
        gtk_widget_destroy(dialog->spinner);
        dialog->spinner = NULL;
    }

    /* Get the spinner icon. */
    gchar *icon_path = g_strconcat(gicaroot, "/icons/ActivityIndicatorOnWht_16x16.gif", NULL);
    dialog->spinner = gtk_image_new_from_file(icon_path); 
    g_free(icon_path);

    /* Align spinner image to the right hand side of its allocated area. */
    gtk_misc_set_alignment(GTK_MISC(dialog->spinner), 1, 0.5);

    /* Add SPINNER_SPACING padding between the button box and the spinner. */
    gtk_misc_set_padding(GTK_MISC(dialog->spinner), SPINNER_SPACING, 0);

    gtk_table_attach_defaults(GTK_TABLE(spinner_table), dialog->spinner, 0, 1, 0, 1);

    return top_padding;
}


/** @brief create_scroll_window() creates a scroll window with inbuilt border
 *         padding.
 * @param contents[in]  The widget the GtkScrollWindow will contain.
 * @param border[in]    The between the GtkScrollWindow and its contents in pixels.
 * @return              The create GtkScrollWindow.
 */
static GtkWidget *create_scroll_window(GtkWidget *contents, unsigned int border)
{
    GtkWidget *scroll_view;
    GtkWidget *padding_container;

    /* Put the contents in an event box just to allow us to add padding. */
    padding_container = gtk_event_box_new();
    gtk_container_add(GTK_CONTAINER(padding_container), contents);
    gtk_container_set_border_width(GTK_CONTAINER(padding_container), border);

    /* Put the contents within a GtkScrollView. */
    scroll_view = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_view), padding_container);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

    return scroll_view;
}


/** @brief The populateContents function is used to iterate over list of
           formsElement structures and convert that into in this case
           GTK+ UI. This function can be called recursively in certain cases
           for elements such as expanders.
    @param form[in,out] is an array of pointers to formsElement structures to
                        convert to UI. Once the UI is displayed these
                        structures are updated to reflect the final state of
                        the UI.
    @param content_table[in] this is a 2 column by n row table, where n is the
                             number of elements in the form list. This is where
                             all the UI widgets are inserted.
    @param button_group[in] this is a pointer to the start of the button group
                            within the form list.
    @param button_group_size[in] this is the number of buttons in the button
                                  group.
    @param dialog[in|out] this is a handle to the opaque dialog structure used
                          by the UIDialogLib to store implementation specific
                          information related to a dialog.
    @param underexapnder[in] states if populateContents is being called to
                             fill in a table under and expander or not.
*/
static void populateContents(struct formsElement **form, GtkWidget *content_table, struct formsElement **button_group, int button_group_size, UIDIALOG *dialog, BOOLEAN underexpander, enum dialogStyle style)
{
    int element_pos;                            /* Iterator used for iterating over the elements. */
    struct formsElement **iter = form;          /* Pointer used for iterating over the passed in form structures. */
    GtkWidget *button_box = NULL;               /* A button box to render the last set of buttons. */
    int front_attach;
    int rear_attach;
    GtkWidget *focusWidget = NULL;      /* The widget to take initial focus. */
    GtkButton *defaultButton = NULL;    /* The button to receive the default key presss. */

    /* Calculate the number of widgets displayed in the content area. */
    if (button_group != NULL)
    {
        GtkWidget *top_padding = create_button_box(dialog, &button_box);

        /* Attach the button box to the table. */
        gtk_table_attach_defaults(GTK_TABLE(content_table), top_padding, 0, 2, button_group - form, 1 + button_group - form);
    }

    /* Iterate over the elements creating their widgets excluding the button box. */
    for (element_pos = 0, iter = form; *iter != NULL; element_pos++, iter++)
    {
        struct formsElement *currentElement = *iter;
        GtkWidget *new_input = NULL;
        

        /* Create the input element */
        switch (currentElement->input.type)
        {
            case I_Text:
            {
                /* Create and add the entry to the dialog. */
                new_input = gtk_entry_new();
                
                if (currentElement->input.inputUnion.textBox.text != NULL)
                {
                    /* An initial text has been provided. Fill the entry with it. */
                    gtk_entry_set_text(GTK_ENTRY(new_input), currentElement->input.inputUnion.textBox.text);
                }
                
                if (currentElement->input.inputUnion.textBox.secret)
                {
                    /* Set visibility so that the hidden character displays instead of the contents. */
                    gtk_entry_set_visibility(GTK_ENTRY(new_input), FALSE);
                }

                if (currentElement->input.inputUnion.textBox.readOnly)
                {
                    /* Set the entry so that it doesn't get focus and mark it as non-editable */
                    g_object_set(G_OBJECT(new_input), "editable", FALSE, "can-focus", FALSE, NULL);
                }

                /* Activate the default action when Enter is pressed. */
                gtk_entry_set_activates_default(GTK_ENTRY(new_input), TRUE);

                /* Restart the idle timeout if we have one. */
                setup_idle_timeout(dialog, new_input, "changed");

                /* Allow this element to be harvested. */
                dialog->elementList = g_list_append(dialog->elementList, new_input);
                break;
            }
            case I_CheckBox:
            {
                if (currentElement->input.inputUnion.checkBox.text != NULL)
                {
                    char * newstring = mnemonic_convert(currentElement->input.inputUnion.checkBox.text, currentElement->input.inputUnion.checkBox.mnemonic);

                    new_input = gtk_check_button_new_with_mnemonic(newstring);
                    free(newstring);
                }
                else
                {
                    /* I expect this case will never be used? */
                    new_input = gtk_check_button_new();
                }

                /* Set the toggle state to that expressed in the structures. */
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(new_input), currentElement->input.inputUnion.checkBox.select);

                /* Restart the idle timeout if we have one. */
                setup_idle_timeout(dialog, new_input, "toggled");

                /* Allow this element to be harvested. */
                dialog->elementList = g_list_append(dialog->elementList, new_input);
                break;
            }
            case I_RadioButton:
            {
                GtkWidget *radio = NULL;                
                struct selection **radioIter = currentElement->input.inputUnion.radioButton;
                int radioCount = 0;
                int i;
                
                /* Count the size of the radio button group. */
                while (*radioIter != NULL)
                {
                    radioCount++;
                    radioIter++;
                }

                /* Create and attach the table to store radioButtons. */
                new_input = gtk_table_new(radioCount, 1, FALSE);

                /* Populate the radio button table with the radio buttons. */
                for (i = 0; i < radioCount; i++)
                {
                    char * newstring = mnemonic_convert(currentElement->input.inputUnion.radioButton[i]->text, currentElement->input.inputUnion.radioButton[i]->mnemonic);

                    /* Create the first radio button with NULL group info then
                     * chain the radio buttons off the previous buttons group
                     * information. */
                    if (i == 0)
                        radio = gtk_radio_button_new_with_mnemonic(NULL, newstring);
                    else
                        radio = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(radio), newstring);

                    free(newstring);

                    /* Set the toggle state to that expressed in the structures. */
                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), currentElement->input.inputUnion.radioButton[i]->select);

                    /* Attach the new radio button to the bottom of the radio button table. */
                    gtk_table_attach_defaults(GTK_TABLE(new_input), radio, 0, 1, i, i + 1);

                    /* Restart the idle timeout if we have one. */
                    setup_idle_timeout(dialog, radio, "toggled");

                    /* Allow this element to be harvested. */
                    dialog->elementList = g_list_append(dialog->elementList, radio);
                }

                break;
            }
            case I_ComboBox:
            {
                GtkTreeIter iter;
                GtkListStore* store = gtk_list_store_new(1, G_TYPE_STRING);
                GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
                int i = 0;

                new_input = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));

                /* Add all of the options. */
                while (currentElement->input.inputUnion.comboBox[i])
                {
                    gtk_list_store_append(store, &iter);
                    gtk_list_store_set(store, &iter, 0, currentElement->input.inputUnion.comboBox[i]->text, -1);

                    if (currentElement->input.inputUnion.comboBox[i]->select)
                        gtk_combo_box_set_active(GTK_COMBO_BOX(new_input), i);

                    i++;
                }

                gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(new_input), renderer, TRUE);
                gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(new_input), renderer, "text", 0, NULL);

                /* Restart the idle timeout if we have one. */
                setup_idle_timeout(dialog, new_input, "changed");

                /* Allow this element to be harvested. */
                dialog->elementList = g_list_append(dialog->elementList, new_input);
                break;
            }
            case I_MultiComboBox:
            {
                /* Implement with a block of check boxes as GTK+ for a multi combo box is limited. */
                GtkWidget *multi = NULL;                
                struct selection **multiIter = currentElement->input.inputUnion.multiComboBox;
                int multiCount = 0;
                int i;
                
                /* Count the number of selections. */
                while (*multiIter != NULL)
                {
                    multiCount++;
                    multiIter++;
                }

                /* Create and attach the table to store multiComboBoxs. */
                new_input = gtk_table_new(multiCount, 1, FALSE);

                /* Populate the multi button table with the check boxes. */
                for (i = 0; i < multiCount; i++)
                {
                    char * newstring = mnemonic_convert(currentElement->input.inputUnion.multiComboBox[i]->text,
                                                        currentElement->input.inputUnion.multiComboBox[i]->mnemonic);

                    multi = gtk_check_button_new_with_mnemonic(newstring);

                    free(newstring);

                    /* Set the toggle state to that expressed in the structures. */
                    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(multi), currentElement->input.inputUnion.multiComboBox[i]->select);

                    /* Attach the new multi button to the bottom of the multi button table. */
                    gtk_table_attach_defaults(GTK_TABLE(new_input), multi, 0, 1, i, i + 1);

                    /* Restart the idle timeout if we have one. */
                    setup_idle_timeout(dialog, multi, "toggled");

                    /* Allow this element to be harvested. */
                    dialog->elementList = g_list_append(dialog->elementList, multi);
                }

                break;
            }
            case I_Button:
            {
                new_input = create_button(&currentElement->input.inputUnion.button, dialog);
                break;
            }
            case I_Expander:
            {
                GtkWidget *new_label;
                GtkWidget *new_table;
                GtkWidget *scroll_view;

                /* Count the number of elements and create the table to store them. */
                {
                    struct formsElement **expIter = currentElement->input.inputUnion.expander;          /* Pointer used for iterating over the passed in form structures. */
                    unsigned int exp_elements = 0;
                 
                    while (*expIter != NULL)
                    {
                        exp_elements++;
                        expIter++;
                    }

                    new_table = gtk_table_new(exp_elements, 2, FALSE);

                    /* Set the column spacings. Not doing any row spacing at
                       moment as it causes the vertical scroll bar to appear. */
                    gtk_table_set_col_spacings(GTK_TABLE(new_table), COL_SPACING);
                }
    
                /* Create the expander the label to go with it and put them together. */
                new_input = gtk_expander_new(NULL);
                createLabel(&currentElement->label, new_input, &new_label, FALSE, style);
                gtk_expander_set_label_widget(GTK_EXPANDER(new_input), new_label);

                /* Set the space between the expander label and its child. */
                gtk_expander_set_spacing(GTK_EXPANDER(new_input), EXPANDER_SPACING);

                /* Put the expander contents within a GtkScrollView. */
                scroll_view = create_scroll_window(new_table, EXPANDER_BORDER);

                /* Make sure the expander has some height to show some information
                   otherwise it will collapse to nothing. */
                gtk_widget_set_size_request(GTK_WIDGET(scroll_view), -1, EXPANDER_HEIGHT);

                gtk_container_add(GTK_CONTAINER(new_input), scroll_view);

                /* Populate the expanders table. */
                populateContents(currentElement->input.inputUnion.expander, new_table, NULL, 0, dialog, TRUE, style);

                break;
            }
            case I_Scroller:
            {
                GtkWidget *new_table;
                GtkWidget *scroll_view;

                /* Count the number of elements and create the table to store them. */
                {
                    struct formsElement **expIter = currentElement->input.inputUnion.scroller;          /* Pointer used for iterating over the passed in form structures. */
                    unsigned int exp_elements = 0;
                 
                    while (*expIter != NULL)
                    {
                        exp_elements++;
                        expIter++;
                    }

                    new_table = gtk_table_new(exp_elements, 2, FALSE);

                    /* Set the column spacings. Not doing any row spacing at
                       moment as it causes the vertical scroll bar to appear. */
                    gtk_table_set_col_spacings(GTK_TABLE(new_table), COL_SPACING);
                }
    
                /* Put the contents within a GtkScrollView. */
                scroll_view = create_scroll_window(new_table, BORDER_WIDTH);

                /* Make sure the scroll window has some height to show some information
                   otherwise it will collapse to nothing. */
                gtk_widget_set_size_request(GTK_WIDGET(scroll_view), -1, SCROLLER_HEIGHT);

                /* Populate the scrollers table. */
                populateContents(currentElement->input.inputUnion.scroller, new_table, NULL, 0, dialog, TRUE, style);

                new_input = scroll_view;

                break;
            }
            case I_SelectionTable:
            {
/*
    selectionTable in tree form:

              GtkScrolledWindow (scroll_view)
                            |
                       GtkViewport
                            |
                GtkTable (2x1 focus_table)
                    |              |
GtkEventBox (focus_eventbox)  GtkTable (1xelements selection_table)
                                    |         |||          |
                    GtkEventBox for element 1 ... GtkEventBox for element n
                                |
                          GtkAlignment
                                |
                   GtkTable (2xelements element table)
                        |                |
              GtkWidget (label)    GtkWidget (input element)
                    ...
              GtkWidget (label n)    GtkWidget (input element n)
*/

                GtkWidget *selection_table;
                GtkWidget *focus_eventbox;
                GtkWidget *focus_table;
                GtkWidget *scroll_view;
                struct selectionTable *pst;
                /* Pointer used for iterating over the selectionElement structures. */
                struct selectionElement **selIter = currentElement->input.inputUnion.selectionTable;
                unsigned int sel_elements = 0;
                unsigned int sel_pos;

                pst = calloc(1, sizeof (struct selectionTable));
                /* Store this structure so that we can harvest and free the
                   selectionTable easily later. */
                dialog->selectionTablesList = g_list_append(dialog->selectionTablesList, pst);
                dialog->elementList = g_list_append(dialog->elementList, pst);
                gboolean elementSelected = FALSE;

                /* Count the number of elements and create the table to store them. */
                {
                    while (*selIter != NULL)
                    {
                        sel_elements++;
                        selIter++;
                    }

                    selection_table = gtk_table_new(sel_elements, 1, FALSE);

                    pst->num_entries = sel_elements;
                    pst->table = calloc(sel_elements + 1, sizeof(GtkWidget*));

                    /* Set the table spacing. */
                    gtk_table_set_col_spacings(GTK_TABLE(selection_table), COL_SPACING);
                }

                /* Put the selectionTable contents within a GtkScrollView. */
                scroll_view = gtk_scrolled_window_new(NULL, NULL);
                new_input = scroll_view;

                /* Create what I have called a focus table. Essentially add an
                   invisible (empty) event box to the left of the selection
                   table to actually take the focus. */
                focus_table = gtk_table_new(2, 1, FALSE);
                focus_eventbox = gtk_event_box_new();
                gtk_widget_set_can_focus(focus_eventbox, TRUE);

                if (currentElement->input.initialFocus == TRUE)
                    focusWidget = focus_eventbox;

                g_signal_connect(G_OBJECT(focus_eventbox), "key-press-event",
                                 G_CALLBACK(key_press_event_cb), pst);

                gtk_table_attach(GTK_TABLE(focus_table), focus_eventbox, 0, 1, 0, 1,
                                 GTK_SHRINK|GTK_FILL, GTK_SHRINK|GTK_FILL, 0, 0);
                gtk_table_attach_defaults(GTK_TABLE(focus_table), selection_table, 0, 1, 1, 2);

                gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_view), focus_table);
                gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_view),
                                               GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

                /* Make sure the selectionTable has some height to show some information
                   otherwise it will collapse to nothing. */
                gtk_widget_set_size_request(GTK_WIDGET(scroll_view), -1, SELECTION_TABLE_HEIGHT);

                for (selIter = currentElement->input.inputUnion.selectionTable,
                     sel_pos = 0;
                     *selIter != NULL; selIter++, sel_pos++)
                {
                    /* The elements are within a selectionTable.
                       Put an event box under each row of the selectionTable.
                       event box is used to detect mouse click and enter/leave
                       events. The background of the element in the
                       selectionTable is set on the event box.
                           Each row contains a table filled with forms data for
                       the row. Below the table is called evttable. See
                       selectionTable case in populateContents for diagram. */

                    GtkWidget *evtbox;
                    GtkWidget *evtalign;
                    GtkWidget *evttable;

                    evtbox = gtk_event_box_new();
                    evtalign = gtk_alignment_new(0.5, 0.5, 1, 1);

                    pst->table[sel_pos] = evtbox;

                    /* Set the initial background colour of the element. */
                    if ((*selIter)->select)
                    {
                        if (!elementSelected)
                        {
                            pst->selected = sel_pos;
                            gtk_widget_modify_bg(GTK_WIDGET(evtbox), GTK_STATE_NORMAL, &colSelected);
                            elementSelected = TRUE;
                        }
                        else
                        {
                            fprintf(stderr, "Warning. Multiple entries in the I_SelectionTable are selected.\n");
                            gtk_widget_modify_bg(GTK_WIDGET(evtbox), GTK_STATE_NORMAL, &colUnselected);
                        }
                    }
                    else
                    {
                        gtk_widget_modify_bg(GTK_WIDGET(evtbox), GTK_STATE_NORMAL, &colUnselected);
                    }

                    gtk_container_set_border_width(GTK_CONTAINER(evtalign), BORDER_WIDTH);

                    g_signal_connect(G_OBJECT(evtbox), "enter-notify-event",
                                     G_CALLBACK(enter_notify_event_cb), pst);
                    g_signal_connect(G_OBJECT(evtbox), "leave-notify-event",
                                     G_CALLBACK(leave_notify_event_cb), pst);
                    g_signal_connect(G_OBJECT(evtbox), "button-press-event",
                                     G_CALLBACK(button_press_event_cb), pst);

                    gtk_table_attach_defaults(GTK_TABLE(selection_table), evtbox,
                                              0, 1, sel_pos, 1 + sel_pos);
                    gtk_container_add(GTK_CONTAINER(evtbox), evtalign);

                    {
                        /* Pointer used for iterating over this rows form structures. */
                        struct formsElement **eleIter = currentElement->input.
                                                        inputUnion.selectionTable[sel_pos]->elements;
                        unsigned int ele_elements = 0;

                        while (*eleIter != NULL)
                        {
                            ele_elements++;
                            eleIter++;
                        }

                        evttable = gtk_table_new(ele_elements, 2, FALSE);
                    }

                    gtk_container_add(GTK_CONTAINER(evtalign), evttable);

                    /* Populate the selection table's table. */
                    populateContents(currentElement->input.inputUnion.selectionTable[sel_pos]->elements,
                                     evttable, NULL, 0, dialog, TRUE, style);
                }

                if (!elementSelected)
                {
                    /* No selection has been made.
                       Belt and braces. default to the first element. */
                    gtk_widget_modify_bg(GTK_WIDGET(pst->table[0]), GTK_STATE_NORMAL, &colSelected);
                }

                break;
            }
            case I_Label:
            {
                createLabel(&currentElement->input.inputUnion.label, NULL, &new_input, TRUE, style);
                break;
            }
            case I_LinkButton:
            {
                /* Note at present we do not do anything with the mnemonic position.
                   This is because there appears to be no way to set a mnemonic
                   for a link button in GTK+ at present. */

                struct buttonResp *bresp;

                /* Wrapping the hyperlink text, so it won't make the dialog unnecessarily large. */
                char *wrapped_text = wrap_text(currentElement->input.inputUnion.linkButton.text, 50);

                /* We are using the empty string as the URL because we don't
                   actually ever want to redirect to a site. */
                new_input = gtk_link_button_new_with_label("", wrapped_text);

                /* Release the wrapped text. */
                free(wrapped_text);

                /* Align link buttons to the left. */
                gtk_button_set_alignment(GTK_BUTTON(new_input), 0.0, 0.5);

                /* We need to stop the link redirecting to open a site so hook it.
                   Currently this is GTK2 only, we cannot quite move to GTK3+
                   yet but bare this in mind.
                   This would be the GTK3+ way:

                   gboolean activate_link_cb(GtkLinkButton *button, gpointer user_data)
                   {
                       return TRUE;
                   }

                   g_signal_connect(G_OBJECT(new_input), "activate-link", G_CALLBACK(activate_link_cb), NULL); 
                 */
                gtk_link_button_set_uri_hook(gtk2_link_button_hook, NULL, NULL);

                /* Allocate and initialise the button response. */
                bresp = malloc(sizeof(struct buttonResp));
                bresp->dialog = dialog;
                bresp->button_response = currentElement->input.inputUnion.linkButton.id;

                /* Store all of these bresp structures up so they can be freed. */
                dialog->brespList = g_list_append(dialog->brespList, bresp);

                g_signal_connect(G_OBJECT(new_input), "clicked", G_CALLBACK(button_clicked_cb), (gpointer) bresp);
                break;
            }
            case I_ButtonBox:
            {
                const struct button **bp;
                GtkWidget *bbox;
                GtkWidget *button;
                const struct button *buttonFocus = currentElement->input.inputUnion.buttonBox.focus;
                GtkWidget *buttonFocusWidget = NULL;

                new_input = create_button_box(dialog, &bbox);
                int i = 0;
                
                /* Create and add all primary buttons to the button box. */
                for (bp = currentElement->input.inputUnion.buttonBox.primary;
                     bp != NULL && *bp != NULL; bp++, i++)
                {
                    button = create_button(*bp, dialog);
                    gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);

                    if (*bp == buttonFocus)
                        buttonFocusWidget = button;

                    /* Setup the default widget. dialog->response is the
                       default until the dailog is rendered. */
                    if (!strcmp((*bp)->id, dialog->response))
                        defaultButton = GTK_BUTTON(button);
                }

                /* Create and add all secondary buttons to the button box. */
                bp = currentElement->input.inputUnion.buttonBox.secondary;

                if (bp != NULL)
                {
                    for (; *bp != NULL; bp++, i++)
                    {
                        button = create_button(*bp, dialog);
                        gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
                        gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox), button, TRUE);

                        if (*bp == buttonFocus)
                            buttonFocusWidget = button;

                        /* Setup the default widget. dialog->response is the
                           default until the dailog is rendered. */
                        if (!strcmp((*bp)->id, dialog->response))
                            defaultButton = GTK_BUTTON(button);
                    }

                    if (dialog->spinner != NULL)
                    {
                        /* Spinner looks silly and moves things around when
                           there are secondary buttons. At present it is not
                           important for any dialogs with secondary buttons so
                           remove it. */
                        gtk_widget_destroy(dialog->spinner);
                        dialog->spinner = NULL;
                    }
                }

                if (buttonFocusWidget && currentElement->input.initialFocus == TRUE)
                {
                    /* We have a focus button and the button box has focus.
                       Hand over that button to be given focus later. */
                    focusWidget = buttonFocusWidget;
                }

                break;
            }
            case I_None:
            default:
                break;
        }


        if((currentElement->label.text != NULL || currentElement->label.binaryData != NULL)
                && currentElement->input.type != I_Expander)
        {
            front_attach = 1;
            rear_attach = 2;
        }
        else
        {
            front_attach = 0;
            rear_attach = 2;
        }

        /* Add the widgets we just created to the table or the button box. */
        if (new_input != NULL)
        {
            if (button_group_size == 0 || iter < button_group) /* Before the button group */
            {
                gtk_table_attach_defaults(GTK_TABLE(content_table), new_input, front_attach, rear_attach, element_pos, 1 + element_pos);
            }
            else if (iter >= button_group + button_group_size)
            {
                /* Take into account the button box groups elements. */
                gtk_table_attach_defaults(GTK_TABLE(content_table),
                                          new_input, front_attach, rear_attach,
                                          element_pos - button_group_size + 1,
                                          1 + element_pos - button_group_size + 1);
            }
            else /* In the button group */
            {
                gtk_box_pack_start(GTK_BOX(button_box), new_input, FALSE, FALSE, 0);

                /* Setup the default widget. dialog->response is the default until the dailog is rendered. */
                if (!strcmp(currentElement->input.inputUnion.button.id, dialog->response))
                    defaultButton = GTK_BUTTON(new_input);
            }

            /* Add the tooltip. */
            if (currentElement->input.assistiveText != NULL)
                gtk_widget_set_tooltip_text(new_input, currentElement->input.assistiveText);
            else /* GtkLinkButton sets the tooltip to the uri. Disable tooltip we have not set. */
                gtk_widget_set_has_tooltip(new_input, FALSE);

            /* Set the initial focus on this widget. */
            if (currentElement->input.initialFocus == TRUE)
            {
                /* If we have been given a specifc widget to focus on then use that. */
                if (focusWidget)
                    g_object_set(G_OBJECT(focusWidget), "is-focus", TRUE, NULL);
                /* Otherwise use the new_input widget. */
                else
                    g_object_set(G_OBJECT(new_input), "is-focus", TRUE, NULL);
            }
        }


        if(currentElement->input.type != I_None)
        {
            front_attach = 0;
            rear_attach = 1;
        }
        else
        {
            front_attach = 0;
            rear_attach = 2;
        }


        /* Create the label associated with this element. */
        if((currentElement->label.text != NULL || currentElement->label.binaryData != NULL)
                && currentElement->input.type != I_Expander)
        {
            GtkWidget *attach_label;
            GtkWidget *new_label = createLabel(&currentElement->label, new_input, &attach_label, !underexpander, style);

            if (currentElement->input.type == I_Label)
                gtk_misc_set_alignment(GTK_MISC(new_label), 0, 0);

            if (currentElement->input.type == I_ButtonBox)
            {
                /* Add padding the same padding to the top of the label as is
                   given to the button box itself. */
                GtkWidget *align;

                align = gtk_alignment_new(0.5, 0.5, 1, 1);
                
                gtk_container_add(GTK_CONTAINER(align), attach_label);
                gtk_alignment_set_padding(GTK_ALIGNMENT(align), BUTTON_BOX_TOP_PADDING, 0, 0, 0);
                attach_label = align;
            }

            /* Attach the label. */
            gtk_table_attach(GTK_TABLE(content_table), attach_label,
                             front_attach, rear_attach, element_pos,
                             1 + element_pos, GTK_FILL, GTK_FILL, 0, 0);
        }
    }

    if (defaultButton)
    {
        gtk_widget_set_can_default(GTK_WIDGET(defaultButton), TRUE);
        gtk_widget_grab_default(GTK_WIDGET(defaultButton));
    }
}



/*****************************************************************************/
/*           Implementations on the UIDialogLib external functions.          */
/*****************************************************************************/

/* Allocate a UIDialog opaque structure */
UIDIALOG *UIDialogAllocate()
{
    return calloc(1, sizeof(UIDIALOG));
}


/* Initialise the UIDialogLib sufficiently to be used with an existing eventloop
 * implementation. UIDialogThreadInit() will need to be subsequently called if
 * the UIDialogLib() is required to run its own eventloop. */

void UIDialogInit(const char *icaroot)
{
    gchar *resource;

    gicaroot = strdup(icaroot);

    /* Load the GTK resources file. */

    resource = g_strconcat(icaroot, "/gtk/resource.gtkrc", NULL);
    gtk_rc_add_default_file(resource);
    g_free(resource);

    /* Setup colour cache. */

    gdk_color_parse(SELECTED_COLOUR, &colSelected);
    gdk_color_parse(UNSELECTED_COLOUR, &colUnselected);
    gdk_color_parse(MOUSE_HOVER_COLOUR, &colHover);
    gdk_color_parse(SPLASHSCREEN_COLOR, &colSplash);

    /* If attached to wfica, get the lurdump diagnostic function. */

    ld_func = dlsym(RTLD_DEFAULT, "Dialogue_active");
}


static gboolean UIThreadRunning(gpointer unused)
{
    ui_thread_running = TRUE;

    /* Signal the main thread to indicate GTK+ initialisation is complete. */
    pthread_cond_signal(&ui_init_cond);

    return FALSE; /* The code we were signaled for has been run so remove from
                     the GTK+ mainloop. */
}


/* Initialise the UIDialogLibs own eventloop. */
void UIDialogLoopInit(int *argc, char ***argv)
{
    gargc = argc;
    gargv = argv;

    /* Initialise GTK+ */
    g_thread_init(NULL);
    gdk_threads_init();

    /* Start the ui thread. */
    pthread_mutex_lock(&ui_init_mutex);

    g_idle_add((gboolean(*)(gpointer user_data)) UIThreadRunning, NULL);    
    ui_thread = g_thread_create_full(start_ui_thread, NULL, 0, TRUE, FALSE /* IGNORED */, G_THREAD_PRIORITY_NORMAL /* IGNORED */, NULL);

    /* Wait until GTK+ is initialised. */
    do
    {
        /* We need the loop as g_cond_wait may spuriously return according to
         * documentation. */
        pthread_cond_wait(&ui_init_cond, &ui_init_mutex);
    } while (!ui_thread_running);

    pthread_mutex_unlock(&ui_init_mutex);
}

/* Store any overall options for the library. */

void UIDialogSetOptions(unsigned int options)
{
    option_bits = options;
}

/* Implement the UIDialogDisplayVarg() function from the UI Dialog Library SDK.
 */

const char* UIDialogDisplayVarg(UIDIALOG *dialog,
                      struct formsElement **form,
                      const char *window_title,
                      const char *default_response,
                      ...)
{
    struct formsElement **iter = form;          /* Pointer used for iterating over the passed in form structures. */
    struct formsElement **button_group = NULL;  /* Pointer to the start of the button box in the form structures. */
    int content_height = 0;                     /* The number of elements in the content area. */
    int button_group_size = 0;                  /* Number of buttons to be in the button box. */
    gboolean prev_was_button = FALSE;           /* Was the previous element suitable for the button box. */
    UIDIALOG *parent = NULL;
    enum dialogStyle style = S_None;
    const char *wmclass_name = NULL;
    const char *wmclass_class = NULL;

    Window Xwin;
    XWindowAttributes xwattr;
    Display* disp;
    XEvent xev;

    {
        va_list argp;
        enum argType argt;

        va_start(argp, default_response);
        do
        {
            argt = va_arg(argp, int);

            switch (argt)
            {
                case A_LAST_ARG:
                    break;
                case A_STYLE:
                    style = va_arg(argp, enum dialogStyle);
                    break;
                case A_PARENT:
                    parent = va_arg(argp, UIDIALOG *);
                    break;
                case A_TIMEOUT:
                    dialog->timeout = va_arg(argp, unsigned int);
                    break;
                case A_TIMEOUT_RESPONSE:
                    dialog->timeout_response = va_arg(argp, char*);
                    break;
                case A_WMCLASS_NAME:
                    wmclass_name = va_arg(argp, char*);
                    break;
                case A_WMCLASS_CLASS:
                    wmclass_class = va_arg(argp, char*);
                    break;
                case A_IDLE_TIMEOUT:
                    {
                        dialog->idle_timeout_base = va_arg(argp, unsigned int);
                        dialog->idle_timeout = dialog->idle_timeout_base;
                        break;
                    }
                case A_IDLE_TIMEOUT_RESPONSE:
                    {
                        dialog->idle_timeout_response = va_arg(argp, char*);
                        break;
                    }
                default:
                    break;
            }
        } while (argt != A_LAST_ARG);
        va_end(argp);
    }

    /* Initialise and store initial references. */
    dialog->response = default_response;
    dialog->timeout_src = 0;
    dialog->form = form;
    dialog->num_elements = 0;

    /* Get the UI threads lock. */
    if (ui_thread != NULL)
    {
        pthread_mutex_lock(&one_dialog_mutex);
        gdk_threads_enter();
    }


    if (dialog->going_up_or_down == DIALOG_GOING_DOWN)
    {
        /* The dialog has been marked to close before it has even stated going up
         * this can legitimately happen if the open and close are in different
         * threads. */
        dialog->going_up_or_down = DIALOG_DOWN;

        if (ui_thread != NULL)
        {
            gdk_threads_leave();
            pthread_mutex_unlock(&one_dialog_mutex);
        }

        return NULL;
    }
    else
    {
        /* This dialog is going up within this critical section so mark it up. */
        dialog->going_up_or_down = DIALOG_UP;
    }

    /* Create the basic window if there isn't one we made earlier. */
    if (dialog->window == NULL)
    {
        dialog->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

        /* Only set the WM Class name and class hints in a pair. */
        if (wmclass_name && wmclass_class)
            gtk_window_set_wmclass(GTK_WINDOW(dialog->window), wmclass_name, wmclass_class);

        /* Set the callbacks for when the window is closing. */
        dialog->close_signal = g_signal_connect(G_OBJECT(dialog->window), "delete_event", G_CALLBACK(window_closing_cb), dialog);
    }
    else
    {
        /* Clear out the old contents of the window. */
        if (dialog->win_contents != NULL)
        {
            gtk_widget_hide(dialog->win_contents);
            gtk_widget_destroy(dialog->win_contents);

            /* Destroying a container destroys its contents. */
            dialog->win_contents = NULL;
            dialog->spinner = NULL;
        }
    }

    /* Set the dialogs parent. */
    if (parent != NULL)
    {
        gtk_window_set_modal(GTK_WINDOW(dialog->window), TRUE);
        gtk_window_set_transient_for(GTK_WINDOW(dialog->window), GTK_WINDOW(parent->window));

        /* Set dialog position */
        gtk_window_set_position(GTK_WINDOW(dialog->window), GTK_WIN_POS_CENTER_ON_PARENT);

        if (option_bits & UI_DIALOGS_ABOVE)
            gtk_window_set_keep_above(GTK_WINDOW(dialog->window), TRUE);
    }
    else
    {
        /* Set dialog position */
        gtk_window_set_modal(GTK_WINDOW(dialog->window), TRUE);
        gtk_window_set_position(GTK_WINDOW(dialog->window), GTK_WIN_POS_CENTER);

        /* We don't have a parent and we want to be seen
           so set the dialog window to on top. */
        gtk_window_set_keep_above(GTK_WINDOW(dialog->window), TRUE);
    }

    /* Set the dialog title. */
    gtk_window_set_title(GTK_WINDOW(dialog->window), window_title);  

    /* Count the number of elements to create */
    while (*iter != NULL)
    {
        struct formsElement *currentElement = *iter;

        /* Do we want to start the button box?
           Only use buttons without an additional label for the button box.
           At present buttons ignore the label.type . */
        if (currentElement->input.type == I_Button && currentElement->label.text == NULL)
        {
            if (prev_was_button == FALSE)
            {
                /* Make the first button in the last group of buttons as the 
                 * start of the button group. These are the ones in the button
                 * box at the bottom of the dialog. */
                button_group = iter;
                button_group_size = 1;
            }
            else
            {
                button_group_size++;
            }

            prev_was_button = TRUE;
        }
        else
        {
            prev_was_button = FALSE;
        }

        dialog->num_elements++;
        iter++;
    }

    /* Calculate the number of widgets displayed in the content area. */
    if (button_group != NULL)
        content_height = dialog->num_elements - button_group_size + 1; /* Add one for the button box */
    else
        content_height = dialog->num_elements;

    {
        GtkWidget *content_table;                   /* Table used to contain all widgets in the form structures. */

        /* Add the style to the dialog. This adds all the dialog specific icons and
           correctly sizes the dialog and adds any background etc. */
        content_table = style_dialog(dialog, style, content_height);

        /* Generate the contents table which has all the elements described in form. */
        populateContents(form, content_table, button_group, button_group_size, dialog, FALSE, style);
    }
   
    /* Set the color of windows to white */
     if(style == S_SplashScreen)
         gtk_widget_modify_bg(dialog->window, GTK_STATE_NORMAL, &colSplash);
    
    /* Display the dialog. */
    gtk_widget_show_all(dialog->window);

    
    Xwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(dialog->window)->window);
    disp = GDK_WINDOW_XDISPLAY(GTK_WIDGET(dialog->window)->window);
    
    if(Xwin!=None && disp !=None){

        XGetWindowAttributes(disp, Xwin, &xwattr);
        memset(&xev, 0, sizeof(xev));
        xev.type = ClientMessage;
        xev.xclient.display = disp;
        xev.xclient.window = Xwin; 
        xev.xclient.message_type = XInternAtom(disp, "_NET_ACTIVE_WINDOW", False);
        xev.xclient.format = 32;
     
        /*Forces the dialog to come on top. We have used Xlib instead of GTK because 
        display manager does not allow GTK to force dialog box on top of a fullscreen application*/
        XSendEvent(disp, xwattr.screen->root, False,
                       SubstructureNotifyMask | SubstructureRedirectMask,
                       &xev); 
  
        XFlush(disp);
        
    }


    /* We don't want to display the spinner just yet. */
    if (dialog->spinner)
        gtk_widget_hide(dialog->spinner);

    if (dialog->timeout > 0)
    {
        /* Initialise the timeout label. */
        timeout_tick_cb(dialog);

        /* Start the count down timer. */
        dialog->timeout_src = gdk_threads_add_timeout(1000, (GSourceFunc) timeout_tick_cb, dialog);
    }

    if (dialog->idle_timeout_base > 0)
    {
        /* Start the count down timer. */
        dialog->idle_timeout_src = gdk_threads_add_timeout(1000, (GSourceFunc) idle_timeout_tick_cb, dialog);
    }

    /* Monitor for the ESC key being pressed. */
    dialog->key_press_src = g_signal_connect(G_OBJECT(dialog->window), "key_press_event", G_CALLBACK(key_press_cb), dialog);

    /* If running inside wfica, call back for lurdump.
     * The called function runs gtk_main().
     *
     * Otherwise use GTK's recursive mainloop to hold the control flow
     * while the dialog is displayed. This is how gtk_dialog_run() works.
     */

    if (ld_func)
        ld_func();
    else
        gtk_main();

    /* Release the UI threads lock. */
    if (ui_thread != NULL)
    {
        gdk_threads_leave();
        pthread_mutex_unlock(&one_dialog_mutex);
    }
    else
    {
        /* Close the dialog immediately as we have no UI thread to render the
           spinner. */
        UIDialogClose(dialog);
    }

    /* Return the id string of the button that was pressed */
    return dialog->response;
}

/* Implement the UIDialogDisplayWMClass() function from the UI Dialog Library SDK */
const char* UIDialogDisplayWMClass(UIDIALOG *dialog,
                      struct formsElement **form,
                      enum dialogStyle style,
                      UIDIALOG *parent,
                      const char *window_title,
                      const char *default_response,
                      unsigned int timeout,
                      const char *timeout_response,
                      const char *wmclass_name,
                      const char *wmclass_class)
{
    return UIDialogDisplayVarg(dialog, form, window_title, default_response,
                               A_STYLE, style,
                               A_PARENT, parent,
                               A_TIMEOUT, timeout,
                               A_TIMEOUT_RESPONSE, timeout_response,
                               A_WMCLASS_NAME, wmclass_name,
                               A_WMCLASS_CLASS, wmclass_class, A_LAST_ARG);
}

/* Implement the UIDialogDisplay() function from the UI Dialog Library SDK */
const char* UIDialogDisplay(UIDIALOG *dialog,
                            struct formsElement **form,
                            enum dialogStyle style,
                            UIDIALOG *parent,
                            const char *window_title,
                            const char *default_response,
                            unsigned int timeout,
                            const char *timeout_response)
{
    return UIDialogDisplayVarg(dialog, form, window_title, default_response,
                               A_STYLE, style,
                               A_PARENT, parent,
                               A_TIMEOUT, timeout,
                               A_TIMEOUT_RESPONSE, timeout_response, A_LAST_ARG);
}


/* Close a dialog that has been left in the spinning state */
void UIDialogClose(UIDIALOG *dialog)
{
    /* Get the UI threads lock. */
    if (ui_thread != NULL)
        gdk_threads_enter();

    if (dialog->going_up_or_down == DIALOG_UP)
    {
        /* The dialog is UP so take it down. */
        dialog->going_up_or_down = DIALOG_DOWN;
    }
    else
    {
        /* The dialog may be heading up in another thread the close has just
           come first. Say it's going down and not to put it up. */
        dialog->going_up_or_down = DIALOG_GOING_DOWN;
    }

    if (dialog->window != NULL)
    {
        GtkWidget *tmpWin;

        tmpWin = dialog->window;

        /* Do what we do whenever the window is closed. */
        window_closing_cb(NULL, NULL, dialog);

        gtk_widget_hide(tmpWin);
        gtk_widget_destroy(tmpWin);

        /* Destroying a container destroys its contents. */
        dialog->win_contents = NULL;
        dialog->spinner = NULL;
    }

    if (ui_thread != NULL)
        gdk_threads_leave();
}


/* Do whatever closedown stuff you need to do. */    
void UIDialogDeInit()
{
    if (gicaroot)
      free ((char*)gicaroot);

}

