/*-
 * xglock.c - main file for xglock, the GTK+ gui interface to xlock.
 *
 * Copyright (c) 1998 by An Unknown author & Remi Cohen-Scali
 *
 * See xlock.c for copying information.
 *
 * Revision History:
 * Nov-04: updates by Eric Lassauge <lassauge AT users.sourceforge.net>
 * 	   - Version 3.0 : give a first try at GTK 2.x !
 * Nov-99: updates by Eric Lassauge <lassauge AT users.sourceforge.net>
 *         - Callbacks for 'Cancel' buttons , don't SIGSEGV anymore
 *         - FontSelection dialog updates for GTK 1.2.6
 *         - Drawing of little colored rectangle simplified, now works in
 *           Pseudo 8 bits and doesn't need an 'expose'
 *         - Add 'About' box
 *         - Add handling of "similar to xmlock" RC file
 *         - etc...
 * Jul-98: Continual improvements by Remi Cohen-Scali <remi.cohenscali AT pobox.com>
 * ?? -98: First written by Charles Vidal <cvidal AT ivsweb.com>.
 */

/*-
  XgLock Problems are almost the same as XmLock's ones.
    1. Allowing only one in -inroot.  Need a way to kill it. (a menu ?)
    2. Still missing many options (TO DO).
    3. Need a clean rewritting.
 */

#include <sys/types.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if 0
# define XGLOCK_DEBUG    1
#else
# undef XGLOCK_DEBUG
#endif

#ifdef HAVE_CONFIG_H
# include <config.h>
#else
# define HAVE_UNISTD_H          1
#endif

#if HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_VFORK
#define FORK                            vfork
#define FORK_STR                        "vfork"
#else
#define FORK                            fork
#define FORK_STR                        "fork"
#endif

#include <gdk/gdkx.h>
#include <gtk/gtk.h>

#include "modes.h"

/* Alloc args by chunks of */
#define ARGS_CHUNK                      20
#define MODE_STRING                     "-mode"

#define XLOCK                           "xlock"
#define WINDOW_WIDTH                    160
#define WINDOW_HEIGHT                   100
#define WINDOW_GEOMETRY                 "160x100"

#define XGLOCK_SPACING                  3
#define XGLOCK_TABLE_HOMOGENEOUS        ARG_HOMOGENEOUS
#define XGLOCK_TABLE_SPACING            XGLOCK_SPACING
#define XGLOCK_TABLE_ROW_SPACING        XGLOCK_SPACING
#define XGLOCK_TABLE_COL_SPACING        XGLOCK_SPACING
#define XGLOCK_TABLE_BORDER_WIDTH       XGLOCK_SPACING

#define FRAME_WIDTH                     500
#define FRAME_HEIGHT                    300

#define FONT_DIALOG_WIDTH               600
#define FONT_DIALOG_HEIGHT              400

#define MAX_WIDGET_NUMBER_LEN           25

#include "logo.h"

#ifndef XGLOCKRC_FILE
#define XGLOCKRC_FILE "xglockrc"
#endif

static char numberwidget[MAX_WIDGET_NUMBER_LEN];
static pid_t numberprocess = -1;
static pid_t *launchedprocesses =(pid_t *)NULL;
static int num_launchedprocesses = 0;
static int sz_launchedprocesses = 0;
static int selected_mode = -1;
/*static GtkWidget *font_sel_entry =(GtkWidget *)NULL;*/
static GtkTooltips *tooltips=(GtkTooltips *)NULL;

/* Prototypes */
#if defined( __ANSI_C__ ) || defined( NeedFunctionsPrototypes )
# define __PROTO( name, args )  name args
#else /* ! __ANSI_C__ && ! NeedFunctionsPrototypes */
# define __PROTO( name, args )  name ()
#endif /* ? __ANSI_C__ && ! NeedFunctionsPrototypes */

#define STD_CB_ARGS	(GtkWidget *, gpointer)

static void *__PROTO( secured_malloc, (size_t) );
static void *__PROTO( secured_calloc, (int, size_t) );
static void *__PROTO( secured_realloc, (void *, size_t) );
static gchar *__PROTO( check_quotes, (gchar *str) );

static void __PROTO( mode_list_item_select_cb, STD_CB_ARGS );
static void __PROTO( mode_list_item_unselect_cb, STD_CB_ARGS );
static void __PROTO( compose_xlock_command, (char *) );
static void __PROTO( kill_xlock_cb, STD_CB_ARGS );
static void __PROTO( launch_xlock, STD_CB_ARGS );
static void __PROTO( exit_xglock, STD_CB_ARGS );
static void __PROTO( bool_option_clicked_cb, STD_CB_ARGS );
static void __PROTO( destroy_window, STD_CB_ARGS );

static void __PROTO( color_selection_cancel_cb, STD_CB_ARGS );
static void __PROTO( color_selection_ok_cb, STD_CB_ARGS );
static void __PROTO( color_selection_changed_cb, STD_CB_ARGS );
static void __PROTO( create_color_selection_dialog, STD_CB_ARGS );

/*static void __PROTO( font_select_item_select_cb, STD_CB_ARGS );*/
static void __PROTO( font_select_cancel_cb, STD_CB_ARGS );
static void __PROTO( font_select_ok_cb, STD_CB_ARGS );
static void __PROTO( create_font_selection_dialog, STD_CB_ARGS );

static void __PROTO( file_selection_cancel_cb, STD_CB_ARGS );
static void __PROTO( file_selection_ok_cb, STD_CB_ARGS );
static void __PROTO( create_file_selection_dialog, STD_CB_ARGS );

static void __PROTO( contextual_help_dialog, STD_CB_ARGS );
static void __PROTO( window_help_dialog, STD_CB_ARGS );
static void __PROTO( global_help_dialog, STD_CB_ARGS );
static void __PROTO( about_dialog, STD_CB_ARGS );
static void __PROTO( about_dialog_close_callback, STD_CB_ARGS );

static GtkWidget * __PROTO( create_file_menu, (GtkWidget *) );
static GtkWidget * __PROTO( create_help_menu, (GtkWidget *) );

static void __PROTO( create_fntColorOptions_entries, (GtkWidget *) );
static void __PROTO( create_genOptions_entries, (GtkWidget *) );
static void __PROTO( create_boolOptions_buttons, (GtkWidget *) );

#if GTK_CHECK_VERSION(2,0,0)
/* GTK+ 2.x differences */
#define    gtk_color_selection_set_opacity(colorsel,has_opacity) \
	gtk_color_selection_set_has_opacity_control(colorsel,(gboolean)has_opacity)
#endif

/*
 * secured_malloc
 * --------------
 * Return an allocated string of bytes
 */
static gpointer
secured_malloc(gulong sz)
{
    gpointer allocated =(gpointer)NULL;
    allocated =(gpointer)g_malloc( sz );
    g_assert( allocated );
    return allocated;
}

/*
 * secured_calloc
 * --------------
 * Return an allocated string of bytes
 */
static gpointer
secured_calloc(gulong n,
               gulong sz)
{
    gpointer allocated = NULL;

    allocated =(gpointer )g_malloc( n * sz );
    g_assert( allocated );
    return allocated;
}

/*
 * secured_realloc
 * ---------------
 * Return an allocated string of bytes
 */
static gpointer
secured_realloc(gpointer ptr,
                gulong sz)
{
    gpointer allocated = NULL;

    allocated =(gpointer )g_realloc( ptr, sz );
    g_assert( allocated );
    return allocated;
}

/*
 * compose_xlock_command
 * ---------------------
 * This function scan the string passed as argument to see if
 * it is necessary to suround it with quotes for using it as a command
 * line argument.
 */
static gchar *
check_quotes(gchar *str)
{
    static gchar *req_dbl = "'#&;* ";
    static gchar *req_simple = "\"";
    gint len = strlen( str );
    gint dbl_cspn = strcspn( str, req_dbl );
    gint simple_cspn = strcspn( str, req_simple );
    gchar *quotes = "";

    if ( dbl_cspn != len )
      quotes = "\"";
    if ( simple_cspn != len )
      quotes = "'";
    return quotes;
}

/*
 * compose_xlock_command
 * ---------------------
 * This function scan all the selected options and build an according
 * command line for running xlock.
 */
static void
compose_xlock_command(gchar ***cmd_argv_p,
                      gulong  *cmd_argv_sz,
                      gulong  *cmd_argc)
{
    gint        i;
    gulong      n = *cmd_argc;
    gulong      sz = *cmd_argv_sz;
    gchar     **cmd_argv = *cmd_argv_p;

#ifdef XGLOCK_DEBUG
# define CHECK_REALLOC(num, size, need)                                                 \
    (void) fprintf(stderr,                                                              \
            "check realloc: current=%ld need=%d size=%ld\n",                            \
            num, need, size );                                                          \
    if (size <= num+need) {                                                             \
        (void)  fprintf(stderr,                                                         \
                "must realloc: size <= num+need is %s\n",                               \
                size<=num+need?"TRUE":"FALSE");                                         \
        cmd_argv =(char **)secured_realloc((gpointer)cmd_argv,                          \
                                           (gulong)(size+ARGS_CHUNK)*sizeof(char **));  \
        size+= ARGS_CHUNK;                                                              \
    }

#else /* ! XGLOCK_DEBUG */
# define CHECK_REALLOC(num, size, need)                                                 \
    if (size <= num+need) {                                                             \
        cmd_argv =(char **)secured_realloc((gpointer)cmd_argv,                          \
                                           (gulong)(size+ARGS_CHUNK)*sizeof(char **));  \
        size+= ARGS_CHUNK;                                                              \
    }

#endif /* ? XGLOCK_DEBUG */

    for (i = 0; i < (gint) nb_boolOpt; i++)
      if (BoolOpt[i].value) {

          /* Check dynamic tab size */
          CHECK_REALLOC( n, sz, 1 );

          /* Add the arg */
          cmd_argv[n] = (gchar *) secured_malloc( strlen( BoolOpt[i].cmdarg )+2 );
          (void) sprintf( cmd_argv[n], "-%s", BoolOpt[i].cmdarg );
          n++;
      }

    /* General options */
    for (i = 0; i < (gint) nb_genOpt; i++)
    {
        gchar *val;

        if ( !( GTK_IS_ENTRY( generalOpt[i].text_widget ) ) ) continue;

        val = (gchar *)gtk_entry_get_text( GTK_ENTRY( generalOpt[i].text_widget ));
        if ( strlen( val ) > 0 )
        {
            gint j;
            gchar *quotes;

            for ( j = 0; j < (gint) nb_defaultedOptions; j++ )
              if ( !strcmp( generalOpt[i].cmdarg, defaulted_options[j] ) )
                break;
            if ( j < (gint) nb_defaultedOptions )
            {
		gint defval = 0;
		gfloat defflt = 1.0;
		gboolean flt = False;

                switch ( j )
                {
                  case DEF_DELAY:
                    defval = LockProcs[selected_mode].def_delay; break;
                  case DEF_BATCHCOUNT:
                    defval = LockProcs[selected_mode].def_batchcount; break;
                  case DEF_CYCLES:
                    defval = LockProcs[selected_mode].def_cycles; break;
                  case DEF_SIZE:
                    defval = LockProcs[selected_mode].def_size; break;
                  case DEF_SATURATION:
		    flt = True;
                    defflt = LockProcs[selected_mode].def_saturation; break;
                }
		if (flt) {
                  if ( defflt == atoi( val ) )
                  {
#ifdef XGLOCK_DEBUG
                    (void) fprintf(stderr,
                            "Option %s has its default value (%g): discarded\n",
                            generalOpt[i].cmdarg, defflt);
#endif
                    continue;
                  }
		} else {
                  if ( defval == atoi( val ) )
                  {
#ifdef XGLOCK_DEBUG
                    (void) fprintf(stderr,
                            "Option %s has its default value (%d): discarded\n",
                            generalOpt[i].cmdarg, defval);
#endif
                    continue;
                  }
                }
            }

            /* Check dyntab size */
            CHECK_REALLOC( n, sz, 2 );

            cmd_argv[n] = (gchar*) secured_malloc( strlen( generalOpt[i].cmdarg )+2 );
            (void) sprintf( cmd_argv[n], "-%s", generalOpt[i].cmdarg );
            n++;

            quotes = check_quotes( val );

            cmd_argv[n] = (gchar *) secured_malloc( strlen( val )+3 );
            (void) sprintf( cmd_argv[n], "%s%s%s", quotes, val, quotes);
            n++;
        }
    }

    /* font and color options */
    for (i = 0; i < (gint) nb_fntColorOpt; i++)
    {
        switch ( fntcolorOpt[i].type )
        {
            /* Handle color parameter */
          case TFNTCOL_COLOR:
          {
              gchar *val = (gchar *)gtk_entry_get_text( GTK_ENTRY( fntcolorOpt[i].entry ) );
              gushort R, G, B;
              gdouble T;
              gchar   opt[50];

              if ( strlen( val ) > 0 )
              {
                  CHECK_REALLOC( n, sz, 2 );

                  cmd_argv[n] = (gchar*) secured_malloc( strlen( fntcolorOpt[i].cmdarg )+2 );
                  (void) sprintf( cmd_argv[n], "-%s", fntcolorOpt[i].cmdarg );
                  n++;

                  (void) sscanf( val, "#%4hx%4hx%4hx (%lf)", &R, &G, &B, &T );
                  (void) sprintf( opt, "#%04x%04x%04x", R, G, B );

                  cmd_argv[n] = (gchar*) secured_malloc( strlen( opt )+3 );
                  (void) sprintf( cmd_argv[n], "\"%s\"", opt );
                  n++;
              }
              break;
          }

            /* Handle font or file parameters */
          case TFNTCOL_FONT:
          case TFNTCOL_FILE:
          {
              gchar *val = (gchar *)gtk_entry_get_text( GTK_ENTRY( fntcolorOpt[i].entry ) );
              gchar *quotes;

              if (strlen( val ) > 0)
              {
                  CHECK_REALLOC( n, sz, 2 );

                  cmd_argv[n] = (gchar *) secured_malloc( strlen( fntcolorOpt[i].cmdarg )+2 );
                  (void) sprintf( cmd_argv[n], "-%s", fntcolorOpt[i].cmdarg );
                  n++;

                  quotes = check_quotes( val );

                  cmd_argv[n] = (gchar *) secured_malloc( strlen( val )+3 );
                  (void) sprintf( cmd_argv[n], "%s%s%s", quotes, val, quotes);
                  n++;
              }
              break;
          }
        }
    }

    /* Add a NULL ptr */
    CHECK_REALLOC( n, sz, 1 );
    cmd_argv[n++] =(char *)NULL;

#undef CHECK_REALLOC

    /* Return argv, argc, argsz */
    *cmd_argc = n;
    *cmd_argv_sz = sz;
    *cmd_argv_p = cmd_argv;
}

/*
 * kill_xlock_cb
 * -------------
 * Callback associated wih the Kill xlock file menu entry
 */
static void
kill_xlock_cb(GtkWidget *w,
              gpointer udata)
{
    guint i;
    gint dummy = 0;

    for ( i = 0;  i < (guint) num_launchedprocesses; i++ )
      if ( launchedprocesses[i] != -1 ) {
          (void) kill( launchedprocesses[i], SIGKILL );
          (void) wait( &dummy );
          launchedprocesses[i] = -1;
      }
    num_launchedprocesses = 0;
}

/*
 * launch_xlock
 * ------------
 * This callback launch the xlock command accordingly to the selected
 * modes and options. The xlock process is launched in root window, in
 * a window or in normal mode.
 */
static void
launch_xlock(GtkWidget *w,
             gpointer udata )
{
#ifdef XGLOCK_DEBUG
    gint i;
#endif
    gchar  *launch_opt =(char *)udata;
    gulong  args_size = 0,
            args_cnt = 0;
    gchar **cmd_argv =(char **)NULL;

    if ( selected_mode >= 0 && selected_mode < (gint) nb_mode )
    {
        /* Alloc argv table */
        args_size = ARGS_CHUNK;
        args_cnt = 0;
        cmd_argv =(char **)secured_calloc( ARGS_CHUNK, sizeof(char *) );

        /* First arg is xlock itself */
        cmd_argv[args_cnt] = (gchar *) secured_malloc( strlen( XLOCK )+1 );
        (void) strcpy(cmd_argv[args_cnt], XLOCK);
        args_cnt++;

        /* If launch in window/root */
        if ( launch_opt )
        {
            cmd_argv[args_cnt] = (gchar *) secured_malloc( strlen( launch_opt )+1 );
            (void) strcpy(cmd_argv[args_cnt], launch_opt);
            args_cnt++;
        }

        /* Mode arg */
        cmd_argv[args_cnt] = (gchar *) secured_malloc( strlen( MODE_STRING )+1 );
        (void) strcpy(cmd_argv[args_cnt], MODE_STRING);
        args_cnt++;

        /* Mode name */
        cmd_argv[args_cnt] = (gchar *) secured_malloc( strlen( LockProcs[selected_mode].cmdline_arg )+1 );
        (void) strcpy(cmd_argv[args_cnt], LockProcs[selected_mode].cmdline_arg);
        args_cnt++;

        /* Add other user selected args */
        compose_xlock_command( &cmd_argv, &args_size, &args_cnt );

#ifdef XGLOCK_DEBUG
        (void) fprintf( stderr, "Running command: " );
        for ( i = 0; i < args_cnt; i++ )
          (void) fprintf( stderr, "%s ", cmd_argv[i] ? cmd_argv[i] : "\n" );
#endif
        /* Allocate the launched pid_t dyntab */
        if ( !launchedprocesses )
        {
            launchedprocesses = (pid_t *) secured_calloc( 10, sizeof( pid_t ) );
            sz_launchedprocesses = 10;
        }
        /* Grow dyntab */
        if ( num_launchedprocesses == sz_launchedprocesses )
        {
            launchedprocesses = (pid_t *) secured_realloc(launchedprocesses,
                                                (sz_launchedprocesses+10)*sizeof( pid_t ) );
            sz_launchedprocesses+= 10;
        }
        /* Fork process to run xlock */
        if ( (launchedprocesses[num_launchedprocesses] = FORK() ) == -1 )
        {
            perror( FORK_STR );
            (void) fprintf(stderr,
                    "Cannot " FORK_STR " process for xlock !\n" );
        }
        /* Child process */
        else if ( launchedprocesses[num_launchedprocesses] == 0 )
          (void) execvp( XLOCK, cmd_argv );
        else
          num_launchedprocesses++;
    }

    /* Free resources */
    while ( args_cnt-- )
      free( cmd_argv[args_cnt] );
    free( cmd_argv );
    cmd_argv =(char **)NULL;
}

/*
 * exit_xglock
 * -----------
 * This callback terminates the xglock process. It kills the root
 * widget and break the gtk main loop.
 */
static void
exit_xglock(GtkWidget *w,
            gpointer udata )
{
    GtkWidget *root =(GtkWidget *)udata;
    guint i;
    gint dummy = 0;

    /* Cleanup launched processes */
    for ( i = 0;  i < (guint) num_launchedprocesses; i++ )
      if ( launchedprocesses[i] != -1 ) {
          (void) kill( launchedprocesses[i], SIGKILL );
          (void) wait( &dummy );
          launchedprocesses[i] = -1;
      }
    num_launchedprocesses = 0;

    if ( numberprocess != -1 ) {
        (void) kill( numberprocess, SIGKILL );
        (void) wait( &dummy );
        numberprocess = -1;
    }

    gtk_main_quit();
    gtk_widget_destroy(root);
}

/*
 * destroy_window
 * --------------
 * Callback for destroying window
 */
static void
destroy_window(GtkWidget * widget,
               gpointer udata)
{
    gtk_widget_destroy( (GtkWidget *)udata );
}

/*
 * bool_option_clicked_cb
 * ----------------------
 * This callback is called when a check button is clicked. It's
 * state is then saved in the structure.
 */
static void
bool_option_clicked_cb(GtkWidget *w,
                       gpointer udata )
{
    guint optnr =(guint)udata;

    if ( !( GTK_IS_CHECK_BUTTON(w) )) return;

    BoolOpt[optnr].value = !BoolOpt[optnr].value;
#ifdef XGLOCK_DEBUG
    (void) fprintf(stderr,
            "Option #%ud %s is %s\n",
            optnr, BoolOpt[optnr].cmdarg,
            BoolOpt[optnr].value ? "ACTIVE":"INACTIVE" );
#endif
}

/* ================================================================= */
/*                                                                   */
/*                      List items' callbacks                        */
/*                                                                   */
/* ================================================================= */

/*
 * mode_list_item_select_cb
 * ------------------------
 * Callback called when a mode list item is selected in the list.
 * It fork the process and exec xlock in the specified mode.
 */
static void
mode_list_item_select_cb(GtkWidget *w,
                         gpointer mode_nr )
{
    gint mode =(gint)mode_nr;
    gint i;

    if (w->state != GTK_STATE_SELECTED ) return;

    if ( mode < 0 || mode >= (gint) nb_mode ) return;

    selected_mode = mode;

    for ( i = 0; i < (gint) nb_defaultedOptions; i++ )
    {
        gchar temp[30];
        gint defval = 0, j;
        gfloat defflt = 1.0;
	gboolean flt = False;

        for ( j = 0; j < (gint) nb_mode; j++ )
          if ( !strcmp( generalOpt[j].cmdarg, defaulted_options[i] ) )
            break;
        if ( j == (gint) nb_mode ) break;
        switch ( i ) {
          case DEF_DELAY:
            defval = LockProcs[selected_mode].def_delay; break;
          case DEF_BATCHCOUNT:
            defval = LockProcs[selected_mode].def_batchcount; break;
          case DEF_CYCLES:
            defval = LockProcs[selected_mode].def_cycles; break;
          case DEF_SIZE:
            defval = LockProcs[selected_mode].def_size; break;
          case DEF_SATURATION:
	    flt = True;
            defflt = LockProcs[selected_mode].def_saturation; break;
        }
	if (flt)
        	(void) sprintf(temp, "%g", defflt );
	else
        	(void) sprintf(temp, "%d", defval );
        gtk_entry_set_text(GTK_ENTRY(generalOpt[j].text_widget), temp);
    }

    if ( ( numberprocess = FORK() ) == -1 )
      (void) fprintf( stderr, "Fork error !\n" );
    else if ( numberprocess == 0 ) {
        (void) execlp(XLOCK, XLOCK, "-parent", numberwidget,
                      MODE_STRING, LockProcs[mode].cmdline_arg, "-geometry", WINDOW_GEOMETRY, "-delay", "100000",
                      "-nolock", "-inwindow", "+install", 0);
    }
}

/*
 * mode_list_item_unselect_cb
 * --------------------------
 * Callback called when a mode list item is deselected in the list.
 * It kills the child process forked to display preview in the
 * preview window.
 */
static void
mode_list_item_unselect_cb(GtkWidget *w,
                           gpointer udata )
{
    gint dummy = 0;

    if ( w->state == GTK_STATE_SELECTED ) return;

    selected_mode = -1;

    if ( numberprocess != -1 ) {
        (void) kill( numberprocess, SIGKILL );
        (void) wait( &dummy );
        numberprocess = -1;
    }
}

/* ================================================================= */
/*                                                                   */
/*                      Color selection dialog                       */
/*                                                                   */
/* ================================================================= */

/*
 * color_selection_cancel_cb
 * -------------------------
 * Cancel callback for color selection dialog
 */
static void
color_selection_cancel_cb(GtkWidget *w,
                          gpointer udata)
{
    struct_option_fntcol_callback *cbs_ptr =(struct_option_fntcol_callback *)udata;
    gtk_widget_hide(cbs_ptr->fntcol_dialog);
    gtk_widget_destroy(cbs_ptr->fntcol_dialog);
    (void) g_free(udata);
}

/*
 * color_selection_ok_cb
 * ---------------------
 * Ok callback for color selection dialog
 */
static void
color_selection_ok_cb(GtkWidget *w,
                      gpointer udata)
{
    struct_option_fntcol_callback *cbs_ptr =(struct_option_fntcol_callback *)udata;
    GtkColorSelection 		  *colorsel;
    GdkColor 	       		   gdkcol_bg;
#if (!(GTK_CHECK_VERSION(2,0,0)))
    gdouble            		   colors[4];
#endif
    gchar              		   colorstr[30];

    colorsel = GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel );

#if GTK_CHECK_VERSION(2,0,0)
    gtk_color_selection_get_current_color(colorsel, &gdkcol_bg);
    gtk_color_selection_set_current_color(colorsel, &gdkcol_bg);
    gtk_color_selection_get_current_alpha(colorsel);

    (void) sprintf(colorstr, "#%04x%04x%04x (%3.2f)",
            gdkcol_bg.red,
            gdkcol_bg.green,
            gdkcol_bg.blue,
	    (gdouble)(gtk_color_selection_get_current_alpha(colorsel)/65535.0));
#else
    gtk_color_selection_get_color(colorsel, colors);
    gtk_color_selection_set_color(colorsel, colors);

    (void) sprintf(colorstr, "#%04x%04x%04x (%3.2f)",
            (gushort)(colors[0] * 0xffff),
            (gushort)(colors[1] * 0xffff),
            (gushort)(colors[2] * 0xffff),
            colors[3]);

    gdkcol_bg.red = (gushort)(colors[0] * 0xffff);
    gdkcol_bg.green = (gushort)(colors[1] * 0xffff);
    gdkcol_bg.blue = (gushort)(colors[2] * 0xffff);
#endif
    gdk_color_alloc(gdkcolormap, &gdkcol_bg);

#ifdef XGLOCK_DEBUG
    (void) fprintf(stderr,
            "Parsed bg color: color=#%04x%04x%04x pixel=%lx\n",
            gdkcol_bg.red,
            gdkcol_bg.green,
            gdkcol_bg.blue,
            gdkcol_bg.pixel);
#endif
    gdk_window_set_background(GTK_WIDGET(cbs_ptr->drawing_area)->window, &gdkcol_bg);
    gdk_window_clear(GTK_WIDGET(cbs_ptr->drawing_area)->window);
    gdk_flush();

    gtk_entry_set_text( GTK_ENTRY(cbs_ptr->entry), colorstr );
    gtk_widget_hide( cbs_ptr->fntcol_dialog );
    gtk_widget_destroy(cbs_ptr->fntcol_dialog);
    (void) g_free(udata);
}

/*
 * color_selection_changed_cb
 * --------------------------
 * Color changed callback for color selection dialog (update the current color)
 */
static void
color_selection_changed_cb(GtkWidget *w,
                           gpointer udata)
{
    GtkWidget		 *colorsel_dialog =(GtkWidget *)udata;
    GtkColorSelection 	 *colorsel;
#if GTK_CHECK_VERSION(2,0,0)
    GdkColor		  gdkcol_bg;
#else
    gdouble            	  colors[4];
#endif

    colorsel = GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(colorsel_dialog)->colorsel );
#if GTK_CHECK_VERSION(2,0,0)
    gtk_color_selection_get_current_color(colorsel, &gdkcol_bg);
#else
    gtk_color_selection_get_color(colorsel, colors);
#endif
}

/*
 * create_color_selection_dialog
 * -----------------------------
 * Create a color selection dialog. Is the callback for a color option
 * choice button
 */
static void
create_color_selection_dialog(GtkWidget *w,
                              gpointer udata)
{
    struct_option_fntcol	  *optentry =(struct_option_fntcol *)udata;
#if GTK_CHECK_VERSION(2,0,0)
    GdkColor		  	   gdkcol_bg;
#endif
    gdouble    		  	   colors[4];
    gchar      		 	  *val;
    struct_option_fntcol_callback *cbs_ptr;

    cbs_ptr =(struct_option_fntcol_callback *)secured_malloc(sizeof(struct_option_fntcol_callback));

    gtk_preview_set_install_cmap(TRUE);
    gtk_widget_push_visual(gtk_preview_get_visual());
    gtk_widget_push_colormap(gtk_preview_get_cmap());

    cbs_ptr->fntcol_dialog = gtk_color_selection_dialog_new("color selection dialog");
    cbs_ptr->entry = optentry->entry;
    cbs_ptr->drawing_area = optentry->drawing_area;

    val = (gchar *)gtk_entry_get_text( GTK_ENTRY(cbs_ptr->entry) );
    if ( strlen( val ) > 0 )
    {
        gushort R, G, B;

        (void) sscanf( val, "#%04hx%04hx%04hx (%lf)", &R, &G, &B, &colors[3]);
        gtk_color_selection_set_opacity(GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
                                        colors[3] < 1.0);
#if GTK_CHECK_VERSION(2,0,0)
        if (colors[3] < 1.0)
		gtk_color_selection_set_current_alpha(
				GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
				(guint16)(colors[3] * 65535.0));
	gdkcol_bg.red   = R;
	gdkcol_bg.green = G;
	gdkcol_bg.blue  = B;
        gtk_color_selection_set_current_color(
			GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
			&gdkcol_bg);
#else
        colors[0] = R /(gdouble)0xffff;
        colors[1] = G /(gdouble)0xffff;
        colors[2] = B /(gdouble)0xffff;
        gtk_color_selection_set_color(GTK_COLOR_SELECTION( GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
                                      colors);
#endif
    }

    gtk_color_selection_set_opacity(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
                                    TRUE);

    gtk_color_selection_set_update_policy(GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
                                          GTK_UPDATE_CONTINUOUS);

    gtk_window_position(GTK_WINDOW(cbs_ptr->fntcol_dialog), GTK_WIN_POS_MOUSE);
    gtk_signal_connect(GTK_OBJECT(cbs_ptr->fntcol_dialog), "destroy",
                       (GtkSignalFunc) destroy_window,
                       (gpointer) cbs_ptr->fntcol_dialog);

    gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->colorsel),
                       "color_changed",
                       (GtkSignalFunc) color_selection_changed_cb,
                       (gpointer) cbs_ptr->fntcol_dialog);

    gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->ok_button),
                       "clicked",
                       (GtkSignalFunc) color_selection_ok_cb,
                       (gpointer) cbs_ptr);

    gtk_signal_connect(GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->cancel_button),
                       "clicked",
                       (GtkSignalFunc) color_selection_cancel_cb,
                       (gpointer) cbs_ptr);

    gtk_widget_pop_colormap();
    gtk_widget_pop_visual();
    gtk_widget_show(cbs_ptr->fntcol_dialog);
}

/* ================================================================= */
/*                                                                   */
/*                       Font selection dialog                       */
/*                                                                   */
/* ================================================================= */

/*
 * font_select_cancel_cb
 * ---------------------
 * Font dialog cancel button callback
 */
static void
font_select_cancel_cb(GtkWidget *w,
                      gpointer udata)
{
    struct_option_fntcol_callback *cbs_ptr = (struct_option_fntcol_callback *)udata;
    gtk_widget_hide(cbs_ptr->fntcol_dialog);
    gtk_widget_destroy(cbs_ptr->fntcol_dialog);
    (void) g_free(udata);
}

/*
 * font_select_ok_cb
 * -----------------
 * Font dialog ok button callback
 */
static void
font_select_ok_cb(GtkWidget *w,
                  gpointer udata)
{
    struct_option_fntcol_callback *cbs_ptr = (struct_option_fntcol_callback *)udata;
    gtk_widget_hide(cbs_ptr->fntcol_dialog);
    gtk_entry_set_text(GTK_ENTRY(cbs_ptr->entry),
                       gtk_font_selection_dialog_get_font_name(GTK_FONT_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)));
    gtk_widget_destroy(cbs_ptr->fntcol_dialog);
    (void) g_free(udata);
}

/*
 * create_font_selection_dialog
 * ----------------------------
 * Create a font selection dialog for font parameters
 */
static void
create_font_selection_dialog(GtkWidget *w,
                             gpointer udata)
{
    struct_option_fntcol 	  *optentry =(struct_option_fntcol *)udata;
    struct_option_fntcol_callback *cbs_ptr;
    gchar 			  *fontname;

    cbs_ptr =(struct_option_fntcol_callback *)secured_malloc( sizeof(struct_option_fntcol_callback) );
    cbs_ptr->fntcol_dialog =(GtkWidget *)NULL;
    cbs_ptr->entry = optentry->entry;
    cbs_ptr->drawing_area =(GtkWidget *)NULL;

    cbs_ptr->fntcol_dialog = gtk_font_selection_dialog_new("Xglock Font Selection Dialog");
    fontname = (gchar *)gtk_entry_get_text(GTK_ENTRY(optentry->entry));
    if ( fontname && strlen(fontname) )
      gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(cbs_ptr->fntcol_dialog), fontname);
    gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->ok_button), "clicked",
                       GTK_SIGNAL_FUNC(font_select_ok_cb),
                       (gpointer) cbs_ptr);
    gtk_signal_connect(GTK_OBJECT(GTK_FONT_SELECTION_DIALOG(cbs_ptr->fntcol_dialog)->cancel_button), "clicked",
                       GTK_SIGNAL_FUNC(font_select_cancel_cb),
                       (gpointer) cbs_ptr);
    gtk_widget_show(cbs_ptr->fntcol_dialog);
}

/* ================================================================= */
/*                                                                   */
/*                       File selection dialog                       */
/*                                                                   */
/* ================================================================= */

/*
 * file_selection_cancel_cb
 * ------------------------
 * File selector Cancel callback
 */
static void
file_selection_cancel_cb(GtkWidget *w,
                         gpointer udata)
{
    struct_option_fntcol_callback *cbs_ptr= (struct_option_fntcol_callback *)udata;
    gtk_widget_hide( cbs_ptr->fntcol_dialog );
    gtk_widget_destroy( cbs_ptr->fntcol_dialog );
    (void) g_free(udata);
}

/*
 * file_selection_ok_cb
 * --------------------
 * File selector OK callback
 */
static void
file_selection_ok_cb(GtkWidget *w,
                     gpointer udata)
{
    struct stat  		   stbuf;
    struct_option_fntcol_callback *cbs_ptr= (struct_option_fntcol_callback *)udata;
    gchar       		  *filename;

    /* Be sure file exist. We could be more paranoia */
    filename = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(cbs_ptr->fntcol_dialog));
    if ( stat(filename, &stbuf) != -1 )
    {
        gtk_widget_hide(cbs_ptr->fntcol_dialog);
        gtk_entry_set_text(GTK_ENTRY(cbs_ptr->entry), filename);
        gtk_widget_destroy(cbs_ptr->fntcol_dialog);
        (void) g_free(udata);
    }
}

/*
 * create_file_selection_dialog
 * ----------------------------
 * Create a file selection dialog for file path parameters (
 */
static void
create_file_selection_dialog(GtkWidget *w,
                             struct_option_fntcol *optentry )
{
    struct_option_fntcol_callback *cbs_ptr;

    cbs_ptr =(struct_option_fntcol_callback *)secured_malloc( sizeof(struct_option_fntcol_callback) );
    cbs_ptr->fntcol_dialog = gtk_file_selection_new("file selection dialog");
    cbs_ptr->entry = optentry->entry;
    cbs_ptr->drawing_area =(GtkWidget *)NULL;
    gtk_window_position(GTK_WINDOW(cbs_ptr->fntcol_dialog), GTK_WIN_POS_MOUSE);
    gtk_signal_connect(GTK_OBJECT(cbs_ptr->fntcol_dialog), "destroy",
                       (GtkSignalFunc) destroy_window,
                       (gpointer) cbs_ptr->fntcol_dialog);
    gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(cbs_ptr->fntcol_dialog)->ok_button),
                       "clicked", (GtkSignalFunc) file_selection_ok_cb,
                       (gpointer) cbs_ptr);
    gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(cbs_ptr->fntcol_dialog)->cancel_button),
                       "clicked", (GtkSignalFunc) file_selection_cancel_cb,
                       (gpointer) cbs_ptr);
    gtk_widget_show(cbs_ptr->fntcol_dialog);
}

/* ================================================================= */
/*                                                                   */
/*                        Help dialogs routines                      */
/*                                                                   */
/* ================================================================= */

static void
contextual_help_dialog(GtkWidget *w,
                       gpointer udata )
{
    (void) fprintf(stderr, "contextual_help_dialog !\n");
}

static void
window_help_dialog(GtkWidget *w,
                   gpointer udata )
{
    (void) fprintf(stderr, "window_help_dialog !\n");
}

static void
global_help_dialog(GtkWidget *w,
                   gpointer udata )
{
    (void) fprintf(stderr, "global_help_dialog !\n");
}

static void about_dialog_close_callback(GtkWidget *widget, gpointer   data)
{
   gtk_widget_destroy(GTK_WIDGET (data));
}

static void
about_dialog(GtkWidget *w,
             gpointer udata )
{
  GtkWidget *xdlg;
  GtkWidget *xlabel;
  GtkWidget *xbutton;
  GtkWidget *xlogo_box;
  GtkWidget *xpreview;
  GtkWidget *xframe,*xframe2;
  GtkWidget *xvbox;
  GtkWidget *xhbox;
  char *text;
  guchar *temp,*temp2;
  guchar *datapointer;
  gint y,x;
  xdlg = gtk_dialog_new();
  gtk_window_set_title(GTK_WINDOW(xdlg), "About");
  gtk_window_position(GTK_WINDOW(xdlg), GTK_WIN_POS_CENTER);
  gtk_signal_connect(GTK_OBJECT(xdlg), "destroy",
                     (GtkSignalFunc)about_dialog_close_callback,
		     xdlg);

  xbutton = gtk_button_new_with_label("Ok");
  GTK_WIDGET_SET_FLAGS(xbutton, GTK_CAN_DEFAULT);
  gtk_signal_connect(GTK_OBJECT(xbutton), "clicked",
                     (GtkSignalFunc)about_dialog_close_callback,
		     xdlg);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(xdlg)->action_area),
		     xbutton, TRUE, TRUE, 0);
  gtk_widget_grab_default(xbutton);
  gtk_widget_show(xbutton);
  gtk_tooltips_set_tip(tooltips,xbutton,"Click here to close the information box.",(gchar *)NULL);

  xframe = gtk_frame_new(NULL);
  gtk_frame_set_shadow_type(GTK_FRAME(xframe), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width(GTK_CONTAINER(xframe), 10);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(xdlg)->vbox), xframe, TRUE, TRUE, 0);
  xvbox = gtk_vbox_new(FALSE, 5);
  gtk_container_border_width(GTK_CONTAINER(xvbox), 10);
  gtk_container_add(GTK_CONTAINER(xframe), xvbox);

  /*  The logo frame & drawing area  */
  xhbox = gtk_hbox_new (FALSE, 5);
  gtk_box_pack_start (GTK_BOX (xvbox), xhbox, FALSE, TRUE, 0);

  xlogo_box = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (xhbox), xlogo_box, FALSE, FALSE, 0);

  xframe2 = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (xframe2), GTK_SHADOW_IN);
  gtk_box_pack_start (GTK_BOX (xlogo_box), xframe2, FALSE, FALSE, 0);

  xpreview = gtk_preview_new (GTK_PREVIEW_COLOR);
  gtk_preview_size (GTK_PREVIEW (xpreview), logo_width, logo_height);
  temp = g_new (guchar,logo_width * 3);
  datapointer=(guchar *)header_data;
  for (y = 0; y < (gint) logo_height; y++)
  {
    temp2=temp;
    for (x = 0; x< (gint) logo_width; x++)
    {
      HEADER_PIXEL(datapointer,temp2);
      temp2+=3;
    }
    gtk_preview_draw_row (GTK_PREVIEW (xpreview), temp,
			  0, y, logo_width);
  }
  g_free(temp);
  gtk_container_add (GTK_CONTAINER (xframe2), xpreview);
  gtk_widget_show (xpreview);
  gtk_widget_show (xframe2);
  gtk_widget_show (xlogo_box);
  gtk_widget_show (xhbox);

  xhbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(xvbox), xhbox, TRUE, TRUE, 0);
  text = "\nFirst written by Charles Vidal <cvidal@ivsweb.com>.\n"
#if GTK_CHECK_VERSION(2,0,0)
         "Latest GTK+ 2.x by Éric Lassauge <lassauge@users.sourceforge.net>.\n\n"
#else
         "Updates by Eric Lassauge <lassauge@users.sourceforge.net>.\n\n"
#endif
         "GTK+ launcher for xlockmore: the maintained version of xlock.\n"
 	 "http://www.tux.org/~bagleyd/xlockmore.html\n"
	 "Version 3.0\n";

  xlabel = gtk_label_new(text);
  gtk_box_pack_start(GTK_BOX(xhbox), xlabel, TRUE, FALSE, 0);
  gtk_widget_show(xlabel);

  gtk_widget_show(xhbox);

  gtk_widget_show(xvbox);
  gtk_widget_show(xframe);
  gtk_widget_show(xdlg);
}


/* ================================================================= */
/*                                                                   */
/*                     Menu bar creation routines                    */
/*                                                                   */
/* ================================================================= */

/*
 * create_file_menu
 * ----------------
 * Create the file menu attached to file menubar item
 */
static GtkWidget *
create_file_menu(GtkWidget *window)
{
    GtkWidget           *menu;
    GtkWidget           *menu_item;
    GtkAccelGroup       *group;

    menu = gtk_menu_new();
    group = gtk_accel_group_new();
    gtk_menu_set_accel_group (GTK_MENU (menu), group);

    menu_item = gtk_menu_item_new_with_label("Kill xlock");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'K',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) kill_xlock_cb,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    menu_item = gtk_menu_item_new_with_label("Quit");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'Q',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) exit_xglock,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    return menu;
}

/*
 * create_help_menu
 * ----------------
 * Create the help menu attached to help menubar item
 */
static GtkWidget  *
create_help_menu(GtkWidget *window)
{
    GtkWidget           *menu;
    GtkWidget           *menu_item;
    GtkAccelGroup *group;

    menu = gtk_menu_new();
    group = gtk_accel_group_new();
    gtk_menu_set_accel_group (GTK_MENU (menu), group);

    menu_item = gtk_menu_item_new_with_label("Context help");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'C',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) contextual_help_dialog,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    menu_item = gtk_menu_item_new_with_label("Window help");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'W',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) window_help_dialog,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    menu_item = gtk_menu_item_new_with_label("Global help");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'G',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) global_help_dialog,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    menu_item = gtk_menu_item_new_with_label("About XgLock");
    gtk_container_add( GTK_CONTAINER (menu), menu_item);
    gtk_widget_add_accelerator(menu_item,
			"activate",
			group,
			'A',
			(GdkModifierType) 0, GTK_ACCEL_VISIBLE);
    gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
                        (GtkSignalFunc) about_dialog,
                        (gpointer) window);
    gtk_widget_show(menu_item);

    return menu;
}

/* ================================================================= */
/*                                                                   */
/*             Special parameters' dialog creation                   */
/*                                                                   */
/* ================================================================= */

/*
 * create_fntColorOptions_entries
 * ------------------------------
 * Create all entries for font and color options.
 */
static void
create_fntColorOptions_entries(GtkScrolledWindow *window)
{
    gint         i;
    GtkWidget   *box0;
    GtkWidget   *box1;
    GtkWidget   *table;
    GtkWidget   *label;
    GtkWidget   *entry;
    GtkWidget   *button =(GtkWidget *)NULL;
    GdkColor	 gdkcol_bg;
    GdkColor	 gdkcol_fg;


    box0 = gtk_hbox_new(FALSE, 0);
    gtk_scrolled_window_add_with_viewport(window, box0);
    gtk_widget_show(box0);

    table = gtk_table_new(nb_fntColorOpt,6,0);
    gtk_table_set_row_spacings(GTK_TABLE(table),XGLOCK_TABLE_ROW_SPACING);
    gtk_table_set_col_spacings(GTK_TABLE(table),XGLOCK_TABLE_COL_SPACING);
    gtk_container_border_width(GTK_CONTAINER(table),XGLOCK_TABLE_BORDER_WIDTH);
    gtk_box_pack_start(GTK_BOX(box0),table,TRUE,TRUE,XGLOCK_SPACING);
    gtk_widget_show(table);

    for (i = 0; i < (gint) nb_fntColorOpt; i++) {

        label = gtk_label_new(fntcolorOpt[i].label);
        gtk_table_attach(GTK_TABLE(table),label,
                         0,1,i,i+1,
                         (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                         (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                         XGLOCK_TABLE_SPACING,
                         XGLOCK_TABLE_SPACING);
        gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
        gtk_widget_show(label);

        fntcolorOpt[i].entry = entry = gtk_entry_new();
        gtk_entry_set_editable(GTK_ENTRY(entry), False);
        gtk_widget_set_usize(entry, 0, 21);
        gtk_tooltips_set_tip(tooltips, entry, fntcolorOpt[i].desc,(gchar *)NULL);
        gtk_table_attach(GTK_TABLE(table),entry,
                         1,5,i,i+1,
                         (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                         (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                         XGLOCK_TABLE_SPACING,
                         XGLOCK_TABLE_SPACING);
        gtk_widget_show(entry);

        switch ( fntcolorOpt[i].type ) {

          case TFNTCOL_COLOR:
            box1 = gtk_hbox_new(FALSE, 0);
            gtk_container_border_width(GTK_CONTAINER(box1),XGLOCK_TABLE_BORDER_WIDTH);
            gtk_table_attach(GTK_TABLE(table),box1,
                             5,6,i,i+1,
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             XGLOCK_TABLE_SPACING,
                             XGLOCK_TABLE_SPACING);
            gtk_widget_show(box1);

            gdkcol_bg.red = 0x0000;
            gdkcol_bg.green = 0x0000;
            gdkcol_bg.blue = 0x0000;
            gdk_color_alloc(gdkcolormap, &gdkcol_bg);
            gdkcol_bg.red = 0xffff;
            gdkcol_bg.green = 0xffff;
            gdkcol_bg.blue = 0xffff;
            gdk_color_alloc(gdkcolormap, &gdkcol_fg);
            button = gtk_button_new_with_label( "Select color ..." );
            gtk_signal_connect(GTK_OBJECT(button), "clicked",
                               (GtkSignalFunc) create_color_selection_dialog,
                               (gpointer)&fntcolorOpt[i]);
            GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
            gtk_tooltips_set_tip(tooltips, button, fntcolorOpt[i].desc,(gchar *)NULL);
            gtk_box_pack_start(GTK_BOX(box1), button, FALSE, TRUE, 0);
            gtk_widget_show(button);

            fntcolorOpt[i].drawing_area = gtk_drawing_area_new();
            gtk_tooltips_set_tip(tooltips, fntcolorOpt[i].drawing_area, fntcolorOpt[i].desc,(gchar *)NULL);
            gtk_widget_set_usize(fntcolorOpt[i].drawing_area, 10, 21);
            gtk_container_add(GTK_CONTAINER(box1), fntcolorOpt[i].drawing_area);
            gtk_widget_show(fntcolorOpt[i].drawing_area);

            break;

          case TFNTCOL_FONT:
            button = gtk_button_new_with_label( "Select font ..." );
            gtk_signal_connect(GTK_OBJECT(button), "clicked",
                               (GtkSignalFunc) create_font_selection_dialog,
                               (gpointer)&fntcolorOpt[i]);
            GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
            gtk_tooltips_set_tip(tooltips, button, fntcolorOpt[i].desc,(gchar *)NULL);
            gtk_table_attach(GTK_TABLE(table),button,
                             5,6,i,i+1,
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             XGLOCK_TABLE_SPACING,
                             XGLOCK_TABLE_SPACING);
            gtk_widget_show(button);
            break;

          case TFNTCOL_FILE:
            button = gtk_button_new_with_label( "Select file ..." );
            gtk_signal_connect(GTK_OBJECT(button), "clicked",
                               (GtkSignalFunc) create_file_selection_dialog,
                               (gpointer)&fntcolorOpt[i]);
            GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
            gtk_tooltips_set_tip(tooltips, button, fntcolorOpt[i].desc,(gchar *)NULL);
            gtk_table_attach(GTK_TABLE(table),button,
                             5,6,i,i+1,
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             (GtkAttachOptions) (GTK_EXPAND|GTK_FILL),
                             XGLOCK_TABLE_SPACING,
                             XGLOCK_TABLE_SPACING);
            gtk_widget_show(button);
            break;

          default:
            (void) fprintf(stderr,
                    "Program Inconsistency: Unknown parameter type #%d !\n",
                    fntcolorOpt[i].type );
        }

        if ( fntcolorOpt[i].drawing_area && GTK_WIDGET(fntcolorOpt[i].drawing_area)->window )
          gdk_window_set_background(GTK_WIDGET(fntcolorOpt[i].drawing_area)->window,
                                    &gdkcol_bg);
    }
}

/* ================================================================= */
/*                                                                   */
/*                   Create general optrions entries                 */
/*                                                                   */
/* ================================================================= */

/*
 * create_genOptions_entries
 * -------------------------
 * Create all entries for general options (text fields).
 */
static void
create_genOptions_entries(GtkScrolledWindow *window)
{
    gint         i;
/*     gint         j; */
    GtkWidget   *box0;
    GtkWidget   *box1;
    GtkWidget   *box2;
    GtkWidget   *entry;
    GtkWidget   *label;


    box0 = gtk_hbox_new(FALSE, 0);
    gtk_scrolled_window_add_with_viewport(window, box0);
    gtk_widget_show(box0);

    box1 = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(box0), box1);
    gtk_widget_show(box1);

    box2 = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(box0), box2);
    gtk_widget_show(box2);

    for (i = 0; i < (gint) nb_genOpt; i++) {
        label = gtk_label_new(generalOpt[i].label);
        gtk_widget_set_usize(label, 60, 21);
        gtk_box_pack_start(GTK_BOX(box1), label, FALSE, TRUE, 0);
        gtk_widget_show(label);
    }
    for (i = 0; i < (gint) nb_genOpt; i++) {
        entry = gtk_entry_new();
        gtk_widget_set_usize(entry, 350, 21);
        gtk_tooltips_set_tip(tooltips, entry, generalOpt[i].desc,(gchar *)NULL);
        gtk_box_pack_start(GTK_BOX(box2), entry, FALSE, TRUE, 0);
        gtk_widget_show(entry);
        generalOpt[i].text_widget = entry;
    }
}

/* ================================================================= */
/*                                                                   */
/*             Create boolean options check buttons                  */
/*                                                                   */
/* ================================================================= */

/*
 * create_boolOptions_buttons
 * --------------------------
 * Creates all toggles buttons for boolean options
 */
static void
create_boolOptions_buttons(GtkScrolledWindow *parent)
{
    gint         i;
    GtkWidget   *box0;
    GtkWidget   *box1;
    GtkWidget   *box2;
    GtkWidget   *button;


    box0 = gtk_hbox_new(FALSE, 0);
    gtk_scrolled_window_add_with_viewport(parent, box0);
    gtk_widget_show(box0);

    box1 = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(box0), box1);
    gtk_widget_show(box1);

    box2 = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(box0), box2);
    gtk_widget_show(box2);

    for (i = 0; i < (gint) nb_boolOpt; i++) {
        button = gtk_check_button_new_with_label(BoolOpt[i].label);
        gtk_tooltips_set_tip(tooltips, button, BoolOpt[i].desc,(gchar *)NULL);
        gtk_box_pack_start(GTK_BOX(i%2 ? box1 : box2), button, TRUE, TRUE, 0);
        gtk_signal_connect(GTK_OBJECT(button), "clicked",
                           (GtkSignalFunc) bool_option_clicked_cb,
                           (gpointer)i);
        gtk_widget_show(button);
    }
}

/* ================================================================= */
/*                                                                   */
/*                            Main function                          */
/*                                                                   */
/* ================================================================= */

int
main(gint argc, gchar *argv[])
{
    GtkWidget  *xglockroot;
    GdkWindow  *gdkwin;
    Window     *xwin;

    GtkWidget  *box0;
    GtkWidget  *box1;
    GtkWidget  *box2;
    GtkWidget  *box3;
    GtkWidget  *scrolled_win;
    GtkWidget  *list;
    GtkWidget  *list_item;
    GtkWidget  *button;
    GtkWidget  *separator;
    GtkWidget  *menubar;
    GtkWidget  *menuitem;
    GtkWidget  *menu;
    GtkWidget  *previewButton;
    GtkWidget  *notebook;
    GtkWidget  *frame;
    GtkWidget  *label;
    gint        i;
    gchar       temp[100];

    /* Init for some data */
    nb_mode = sizeof (LockProcs) / sizeof (LockProcs[0]);
    nb_boolOpt = sizeof (BoolOpt) / sizeof (BoolOpt[0]);
    nb_genOpt = sizeof (generalOpt) / sizeof (generalOpt[0]);
    nb_fntColorOpt = sizeof (fntcolorOpt) / sizeof (fntcolorOpt[0]);

#ifdef USE_MB
    gtk_set_locale();
#endif
    gtk_init(&argc, &argv);
    gtk_rc_parse(XGLOCKRC_FILE);
    gtk_rc_parse("xglockrc");

/*
    gdkvisual = gdk_visual_get_best();
    gdkcolormap = gdk_colormap_new(gdkvisual, False);
*/
    /* E.Lassauge : now it works in 8bits Pseudo-Color */
    gdkcolormap = gdk_colormap_get_system();

    xglockroot = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_name(xglockroot, "xglock");
    gtk_window_set_title(GTK_WINDOW(xglockroot), "GTK+ XLock Launcher");
    gtk_window_set_wmclass (GTK_WINDOW (xglockroot), "XgLock", "XLock");

    tooltips = gtk_tooltips_new();

    box1 = gtk_vbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(xglockroot), box1);
    gtk_widget_show(box1);

    box0 = gtk_vbox_new(FALSE, 10);
    gtk_container_border_width(GTK_CONTAINER(box0), 10);
    gtk_box_pack_start(GTK_BOX(box1), box0, TRUE, TRUE, 0);
    gtk_widget_show(box0);

    box2 = gtk_hbox_new(FALSE, 10);
    gtk_container_border_width(GTK_CONTAINER(box2), 10);
    gtk_box_pack_start(GTK_BOX(box1), box2, TRUE, TRUE, 0);
    gtk_widget_show(box2);

    /* menu bar */
    menubar = gtk_menu_bar_new();
    gtk_box_pack_start(GTK_BOX(box0), menubar, FALSE, TRUE, 0);
    gtk_widget_show(menubar);

    /* File menu and File menu bar item */
    menu = create_file_menu(xglockroot);
    menuitem = gtk_menu_item_new_with_label("File");
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
    gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
    gtk_widget_show(menuitem);

    /* File menu and File menu bar item */
    menu = create_help_menu(xglockroot);
    menuitem = gtk_menu_item_new_with_label("Help");
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
    gtk_menu_bar_append(GTK_MENU_BAR(menubar), menuitem);
    gtk_widget_show(menuitem);

    /* Third box containing preview button */
    box3 = gtk_hbox_new(FALSE, 10);
    gtk_widget_set_usize(box3, WINDOW_WIDTH+20, WINDOW_HEIGHT );
    gtk_container_border_width(GTK_CONTAINER(box3), 1);
    gtk_box_pack_start(GTK_BOX(box0), box3, TRUE, TRUE, 0);
    gtk_widget_show(box3);

    /* Preview window */
#if GTK_CHECK_VERSION(2,0,0)
    {
    GdkColor color;
    GtkTextBuffer *buffer;

    /* a text view because I need here an X window (Button has no more
     * a window in Gtk2 ! */
    previewButton = gtk_text_view_new();
    gtk_text_view_set_editable(GTK_TEXT_VIEW(previewButton), FALSE);
    gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(previewButton), FALSE);
    gtk_text_view_set_justification(GTK_TEXT_VIEW(previewButton), GTK_JUSTIFY_CENTER);
    gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(previewButton),30);
    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (previewButton));
    gtk_text_buffer_set_text (buffer, "Preview window", -1);
    gdk_color_parse ("red", &color);
    gtk_widget_modify_text( previewButton, GTK_STATE_NORMAL, &color);
    }
#else
    /* button because I need here an X window */
    previewButton = gtk_button_new_with_label( "Preview window" );
#endif
    gtk_widget_set_name(previewButton,"preview_button");
    gtk_widget_set_usize(previewButton, WINDOW_WIDTH, WINDOW_HEIGHT );
    gtk_box_pack_start(GTK_BOX(box3), previewButton, TRUE, FALSE, 10);
    gtk_tooltips_set_tip(tooltips, previewButton, "This is the preview window",(gchar *)NULL);
    gtk_widget_realize(previewButton);
    gtk_widget_show(previewButton);

    /* Getting X window id through GDK */
    gdkwin = GTK_WIDGET(previewButton)->window;
    xwin =(Window *)GDK_WINDOW_XWINDOW( gdkwin );
    (void) sprintf(numberwidget, "%ld",(unsigned long)xwin );

    /* notebook */
    notebook = gtk_notebook_new();
    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
    gtk_box_pack_start(GTK_BOX(box2), notebook, TRUE, TRUE, 0);
    gtk_widget_show(notebook);

    /* list of modes */
    frame = gtk_frame_new("Choice of the mode");
    gtk_container_border_width(GTK_CONTAINER(frame), 10);
    gtk_widget_set_usize(frame, FRAME_WIDTH, FRAME_HEIGHT);

    /* list */
    scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(frame), scrolled_win);
    gtk_widget_show(scrolled_win);

    list = gtk_list_new();
    gtk_list_set_selection_mode(GTK_LIST(list), GTK_SELECTION_SINGLE);
    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_win), list);
    gtk_container_set_focus_vadjustment (GTK_CONTAINER (list),
                                         gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_win)));
    GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (scrolled_win)->vscrollbar, GTK_CAN_FOCUS);
    gtk_tooltips_set_tip(tooltips, list, "Select the mode you want to try",(gchar *)NULL);

    for (i = 0; i < (gint) nb_mode; i++) {
        sprintf(temp, "%s :%s", LockProcs[i].cmdline_arg, LockProcs[i].desc);
        list_item = gtk_list_item_new_with_label(temp);
        gtk_container_add(GTK_CONTAINER(list), list_item);
        gtk_signal_connect (GTK_OBJECT (list_item), "select",
                            (GtkSignalFunc) mode_list_item_select_cb,
                            (gpointer) ((long) i));
        gtk_signal_connect (GTK_OBJECT (list_item), "deselect",
                            (GtkSignalFunc) mode_list_item_unselect_cb,
                            (gpointer) NULL);
        gtk_widget_show(list_item);
    }
    gtk_widget_show( list );

    label = gtk_label_new("Choose the mode");
    gtk_widget_show(frame);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);

    /* Options */

    frame = gtk_frame_new("Boolean Options");
    gtk_container_border_width(GTK_CONTAINER(frame), 10);
    gtk_widget_set_usize(frame, FRAME_WIDTH, FRAME_HEIGHT);
    gtk_widget_show(frame);

    scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(frame), scrolled_win);
    gtk_widget_show(scrolled_win);

    create_boolOptions_buttons(GTK_SCROLLED_WINDOW(scrolled_win));
    label = gtk_label_new("Options");
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);
    /* General Options */

    frame = gtk_frame_new("General Options");
    gtk_container_border_width(GTK_CONTAINER(frame), 10);
    gtk_widget_set_usize(frame, FRAME_WIDTH, FRAME_HEIGHT);
    gtk_widget_show(frame);

    scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(frame), scrolled_win);
    gtk_widget_show(scrolled_win);

    create_genOptions_entries(GTK_SCROLLED_WINDOW(scrolled_win));
    label = gtk_label_new("General Options");
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);
    /* Color Options */

    frame = gtk_frame_new("Color Options");
    gtk_container_border_width(GTK_CONTAINER(frame), 10);
    gtk_widget_set_usize(frame, FRAME_WIDTH, FRAME_HEIGHT );
    gtk_widget_show(frame);

    scrolled_win = gtk_scrolled_window_new(NULL, NULL);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    gtk_container_add(GTK_CONTAINER(frame), scrolled_win);
    gtk_widget_show(scrolled_win);

    create_fntColorOptions_entries(GTK_SCROLLED_WINDOW(scrolled_win));
    label = gtk_label_new("Font & Color Options");
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);

    /* end of notebook */
    separator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(box1), separator, FALSE, TRUE, 0);
    gtk_widget_show(separator);

    /* Last Hbox for buttons */
    box2 = gtk_hbox_new(FALSE, 10);
    gtk_container_border_width(GTK_CONTAINER(box2), 10);
    gtk_box_pack_start(GTK_BOX(box1), box2, FALSE, TRUE, 0);
    gtk_widget_show(box2);

    /* buttons */
    button = gtk_button_new_with_label("Launch");
    gtk_widget_set_usize(button, 60, 30);
    GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
    gtk_tooltips_set_tip(tooltips, button, "Launch xlock in selected mode",(gchar *)NULL);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       (GtkSignalFunc) launch_xlock,
                       (gpointer) NULL);
    gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 4);
    gtk_widget_show(button);

    /* launch in root button */
    button = gtk_button_new_with_label("Launch in root");
    gtk_widget_set_usize(button, 95, 30);
    GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
    gtk_tooltips_set_tip(tooltips, button, "Launch xlock in root window",(gchar *)NULL);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       (GtkSignalFunc) launch_xlock,
                       (gpointer) "-inroot" );
    gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 4);
    gtk_widget_show(button);

    /* launch -- in window -- button */
    button = gtk_button_new_with_label("Launch in window");
    gtk_widget_set_usize(button, 115, 30);
    GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
    gtk_tooltips_set_tip(tooltips, button, "Launch xlock in a window",(gchar *)NULL);
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
                       (GtkSignalFunc) launch_xlock,
                       (gpointer) "-inwindow" );
    gtk_box_pack_start(GTK_BOX(box2), button, FALSE, TRUE, 4);
    gtk_widget_show(button);

    /* exit button */
    button = gtk_button_new_with_label("Exit");
    gtk_widget_set_name(button,"special_button");
    gtk_tooltips_set_tip(tooltips, button, "Exit xglock",(gchar *)NULL);
    gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
                              (GtkSignalFunc) exit_xglock,
                              (gpointer)xglockroot );
    gtk_box_pack_start(GTK_BOX(box2), button, TRUE, TRUE, 4);
    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
    gtk_widget_grab_default(button);
    gtk_widget_show(button);

    gtk_widget_show(xglockroot);

    gtk_main();

    return 0;
}
