/* gcompris - wordsgame.c
 *
 * Copyright (C) 2000, 2008 Bruno Coudoin
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, see .
 */
#include 
#include "gcompris/gcompris.h"
#include "gcompris/gameutil.h"
#define SOUNDLISTFILE PACKAGE
#define MAXWORDSLENGTH 50
static GcomprisWordlist *gc_wordlist = NULL;
#if GLIB_CHECK_VERSION(2, 31, 0)
static GMutex items_lock; /* No init needed for static GMutexes */
#else
GStaticMutex items_lock = G_STATIC_MUTEX_INIT;
#endif
/*
  word - word to type
  overword - part of word allready typed
  count - number of allready typed letters in word
  pos - pointer to current position in word
  letter - current expected letter to type
*/
typedef struct {
  GooCanvasItem *rootitem;
  GooCanvasItem *overwriteItem;
  gchar *word;
  gchar *overword;
  gint  count;
  gchar *pos;
  gchar *letter;
} LettersItem;
/*
  items - array of displayed items
  items2del - array of items where moved offscreen
  item_on_focus -  item on focus in array items. NULL - not set.
*/
static GPtrArray 	*items=NULL;
static GPtrArray 	*items2del=NULL;
static LettersItem 	*item_on_focus=NULL;
static GcomprisBoard *gcomprisBoard = NULL;
static gint dummy_id = 0;
static gint drop_items_id = 0;
static gboolean uppercase_only;
static void		 start_board (GcomprisBoard *agcomprisBoard);
static void		 pause_board (gboolean pause);
static void 		 end_board (void);
static gboolean		 is_our_board (GcomprisBoard *gcomprisBoard);
static void		 set_level (guint level);
static gint		 key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
static GooCanvasItem	 *wordsgame_create_item(GooCanvasItem *parent);
static gint		 wordsgame_drop_items (GtkWidget *widget, gpointer data);
static gint		 wordsgame_move_items (GtkWidget *widget, gpointer data);
static void		 wordsgame_destroy_item(LettersItem *item);
static gboolean		 wordsgame_delete_items(gpointer user_data);
static void		 wordsgame_destroy_all_items(void);
static void		 wordsgame_next_level(void);
static void		 wordsgame_add_new_item(void);
static void		 wordsgame_config_start(GcomprisBoard *agcomprisBoard,
					     GcomprisProfile *aProfile);
static void		 wordsgame_config_stop(void);
static void		 player_win(LettersItem *item);
static void		 player_loose(void);
#define MAX_FALLSPEED  7000
#define MAX_SPEED  150
#define MIN_FALLSPEED  3000
#define MIN_SPEED  50
#define DEFAULT_FALLSPEED  7000
#define DEFAULT_SPEED  150
#define INCREMENT_FALLSPEED  1000
#define INCREMENT_SPEED  10
static  guint32              fallSpeed = 0;
static  double               speed = 0.0;
static GooCanvasItem *preedit_text = NULL;
/* Description of this plugin */
static BoardPlugin menu_bp =
  {
    NULL,
    NULL,
    N_("Falling Words"),
    N_("Type the falling words before they reach the ground"),
    "Bruno Coudoin ",
    NULL,
    NULL,
    NULL,
    NULL,
    start_board,
    pause_board,
    end_board,
    is_our_board,
    key_press,
    NULL,
    set_level,
    NULL,
    NULL,
    wordsgame_config_start,
    wordsgame_config_stop
  };
/*
 * Main entry point mandatory for each Gcompris's game
 * ---------------------------------------------------
 *
 */
GET_BPLUGIN_INFO(wordsgame)
/*
 * in : boolean TRUE = PAUSE : FALSE = UNPAUSE
 *
 */
static void pause_board (gboolean pause)
{
  if(gcomprisBoard==NULL)
    return;
  if(pause)
    {
      if (dummy_id) {
	g_source_remove (dummy_id);
	dummy_id = 0;
      }
      if (drop_items_id) {
	g_source_remove (drop_items_id);
	drop_items_id = 0;
      }
    }
  else
    {
      if(!drop_items_id) {
	drop_items_id = g_timeout_add (0,
				       (GSourceFunc) wordsgame_drop_items, NULL);
      }
      if(!dummy_id) {
	dummy_id = g_timeout_add (10, (GSourceFunc) wordsgame_move_items, NULL);
      }
    }
}
/*
 */
static void start_board (GcomprisBoard *agcomprisBoard)
{
  if(agcomprisBoard!=NULL)
    {
      gcomprisBoard=agcomprisBoard;
      GHashTable *config = gc_db_get_board_conf();
      gc_locale_set(g_hash_table_lookup( config, "locale"));
      gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
      if (up_init_str && (strcmp(up_init_str, "True")==0))
	uppercase_only = TRUE;
      else
	uppercase_only = FALSE;
      g_hash_table_destroy(config);
      /* disable im_context */
      //gcomprisBoard->disable_im_context = TRUE;
      gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
			"wordsgame/scenery_background.png");
      gcomprisBoard->level = 1;
      gcomprisBoard->maxlevel = 6;
      gcomprisBoard->sublevel = 0;
      gc_bar_set(GC_BAR_LEVEL|GC_BAR_CONFIG);
      /* Default speed */
      speed=DEFAULT_SPEED;
      fallSpeed=DEFAULT_FALLSPEED;
      gc_wordlist = gc_wordlist_get_from_file("wordsgame/default-$LOCALE.xml");
      if(!gc_wordlist)
	{
	  /* Fallback to english */
	  gc_wordlist = gc_wordlist_get_from_file("wordsgame/default-en.xml");
	  if(!gc_wordlist)
	    {
	      gcomprisBoard = NULL;
	      gc_dialog(_("Error: We can't find\na list of words to play this game.\n"), gc_board_end);
	      return;
	    }
	}
      wordsgame_next_level();
    }
}
static void
end_board ()
{
  if(gcomprisBoard!=NULL)
    {
      pause_board(TRUE);
      gc_score_end();
#if GLIB_CHECK_VERSION(2, 31, 0)
      g_mutex_lock (&items_lock);
#else
      g_static_mutex_lock (&items_lock);
#endif
      wordsgame_destroy_all_items();
#if GLIB_CHECK_VERSION(2, 31, 0)
      g_mutex_unlock (&items_lock);
#else
      g_static_mutex_unlock (&items_lock);
#endif
      if (preedit_text){
	goo_canvas_item_remove(preedit_text);
	preedit_text=NULL;
      }
      gc_im_reset();
      gcomprisBoard = NULL;
      if (gc_wordlist != NULL){
	gc_wordlist_free(gc_wordlist);
	gc_wordlist = NULL;
      }
    }
  gc_locale_set( NULL );
}
static void
set_level (guint level)
{
  if(gcomprisBoard!=NULL)
    {
      gcomprisBoard->level=level;
      wordsgame_next_level();
    }
}
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
{
  gchar *letter;
  gint i;
  LettersItem *item;
  gchar *str;
  gunichar unichar_letter;
  gint retval = TRUE;
  if(!gcomprisBoard)
    return FALSE;
  if (keyval){
    g_warning("keyval %d", keyval);
    return TRUE;
  }
  if (preedit_str){
    g_warning("preedit_str %s", preedit_str);
    /* show the preedit string on bottom of the window */
    GcomprisProperties	*properties = gc_prop_get ();
    gchar *text;
    PangoAttrList *attrs;
    gint cursor_pos;
    gtk_im_context_get_preedit_string (properties->context,
				       &text,
				       &attrs,
				       &cursor_pos);
    if (!preedit_text)
      preedit_text = \
	goo_canvas_text_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
			     "",
			     BOARDWIDTH/2,
			     BOARDHEIGHT - 100,
			     -1,
			     GTK_ANCHOR_N,
			     "font", gc_skin_font_board_huge_bold,
			     //"fill_color_rgba", 0xba00ffff,
			     NULL);
    g_object_set (preedit_text,
		  "text", text,
		  "attributes", attrs,
		  NULL);
    return TRUE;
  }
  /* commit str */
  g_warning("commit_str %s", commit_str);
  str = commit_str;
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_lock (&items_lock);
#else
  g_static_mutex_lock (&items_lock);
#endif
  for (i=0; i < g_utf8_strlen(commit_str,-1); i++){
    unichar_letter = g_utf8_get_char(str);
    str = g_utf8_next_char(str);
    if(!g_unichar_isalnum (unichar_letter)){
      player_loose();
      retval = FALSE;
      break;
    }
    letter = g_new0(gchar,6);
    g_unichar_to_utf8 (unichar_letter, letter);
    /* Force entered letter to the casing we expect
     * Children is to small to manage the caps lock key for now
     */
    if (uppercase_only)
      {
	gchar *old = letter;
	letter = g_utf8_strup(old, -1);
	g_free(old);
      }
    else
      {
	gchar *old = letter;
	letter = g_utf8_strdown(old, -1);
	g_free(old);
      }
    if(item_on_focus==NULL)
      {
	for (i=0;ilen;i++)
	  {
	    item=g_ptr_array_index(items,i);
	    g_assert (item!=NULL);
	    if (strcmp(item->letter,letter)==0)
	      {
		item_on_focus=item;
		break;
	      }
	  }
      }
    if(item_on_focus!=NULL)
      {
	if(strcmp(item_on_focus->letter, letter)==0)
	  {
	    gchar *tmpstr;
	    item_on_focus->count++;
	    g_free(item_on_focus->overword);
	    tmpstr = g_utf8_strndup(item_on_focus->word,
				    item_on_focus->count);
	    /* Add the ZERO WIDTH JOINER to force joined char in Arabic and Hangul
	     *  http://en.wikipedia.org/wiki/Zero-width_joiner
	     */
	    item_on_focus->overword = g_strdup_printf("%s%lc", tmpstr, 0x200D);
	    g_free(tmpstr);
	    g_object_set (item_on_focus->overwriteItem,
			  "text", item_on_focus->overword,
			  NULL);
	    if (item_on_focus->countword,-1))
	      {
		g_free(item_on_focus->letter);
		item_on_focus->letter=g_utf8_strndup(item_on_focus->pos,1);
		item_on_focus->pos=g_utf8_find_next_char(item_on_focus->pos,NULL);
	      }
	    else
	      {
		player_win(item_on_focus);
		item_on_focus=NULL;
	      }
	  }
	else
	  {
	    /* It is a loose : unselect the word and defocus */
	    g_free(item_on_focus->overword);
	    item_on_focus->overword=g_strdup(" ");
	    item_on_focus->count=0;
	    g_free(item_on_focus->letter);
	    item_on_focus->letter=g_utf8_strndup(item_on_focus->word,1);
	    item_on_focus->pos=g_utf8_find_next_char(item_on_focus->word,NULL);
	    g_object_set (item_on_focus->overwriteItem,
			  "text", item_on_focus->overword,
			  NULL);
	    item_on_focus=NULL;
	    g_free(letter);
	    player_loose();
	    break;
	  }
      }
    else
      {
	/* Anyway kid you clicked on the wrong key */
	player_loose();
	g_free(letter);
	break;
      }
    g_free(letter);
  }
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_unlock (&items_lock);
#else
  g_static_mutex_unlock (&items_lock);
#endif
  return retval;
}
static gboolean
is_our_board (GcomprisBoard *gcomprisBoard)
{
  if (gcomprisBoard)
    {
      if(g_ascii_strcasecmp(gcomprisBoard->type, "wordsgame")==0)
	{
	  /* Set the plugin entry */
	  gcomprisBoard->plugin=&menu_bp;
	  return TRUE;
	}
    }
  return FALSE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* Called with items_lock locked */
static void wordsgame_next_level_unlocked()
{
  gcomprisBoard->number_of_sublevel = 10 +
    ((gcomprisBoard->level-1) * 5);
  gc_score_start(SCORESTYLE_NOTE,
		 BOARDWIDTH - 195,
		 BOARDHEIGHT - 30,
		 gcomprisBoard->number_of_sublevel);
  gc_bar_set_level(gcomprisBoard);
  gc_score_set(gcomprisBoard->sublevel);
  wordsgame_destroy_all_items();
  if (preedit_text){
    goo_canvas_item_remove(preedit_text);
    preedit_text=NULL;
  }
  gc_im_reset();
  items=g_ptr_array_new();
  items2del=g_ptr_array_new();
  /* Increase speed only after 5 levels */
  if(gcomprisBoard->level > 5)
    {
      gint temp = fallSpeed-gcomprisBoard->level*200;
      if (temp > MIN_FALLSPEED)	fallSpeed=temp;
    }
  pause_board(FALSE);
}
/* set initial values for the next level */
static void wordsgame_next_level()
{
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_lock (&items_lock);
#else
  g_static_mutex_lock (&items_lock);
#endif
  wordsgame_next_level_unlocked();
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_unlock (&items_lock);
#else
  g_static_mutex_unlock (&items_lock);
#endif
}
/* Called with items_lock locked */
static void wordsgame_move_item(LettersItem *item)
{
  GooCanvasBounds bounds;
  goo_canvas_item_translate(item->rootitem, 0, 2.0);
  goo_canvas_item_get_bounds (item->rootitem,
			      &bounds);
  if(bounds.y1>BOARDHEIGHT) {
    if (item == item_on_focus)
      item_on_focus = NULL;
    g_ptr_array_remove (items, item);
    g_ptr_array_add (items2del, item);
    g_timeout_add (100,(GSourceFunc) wordsgame_delete_items, NULL);
    player_loose();
  }
}
/*
 * This does the moves of the game items on the play canvas
 *
 */
static gint wordsgame_move_items (GtkWidget *widget, gpointer data)
{
  g_assert (items!=NULL);
  gint i;
  LettersItem *item;
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_lock (&items_lock);
#else
  g_static_mutex_lock (&items_lock);
#endif
  for (i=items->len-1;i>=0;i--)
    {
      item=g_ptr_array_index(items,i);
      wordsgame_move_item(item);
    }
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_unlock (&items_lock);
#else
  g_static_mutex_unlock (&items_lock);
#endif
  dummy_id = g_timeout_add (gc_timing (speed, items->len),
          (GSourceFunc) wordsgame_move_items, NULL);
  return (FALSE);
}
static void wordsgame_destroy_item(LettersItem *item)
{
  /* The items are freed by player_win */
  goo_canvas_item_remove(item->rootitem);
  g_free(item->word);
  g_free(item->overword);
  g_free(item->letter);
  g_free(item);
}
/* Destroy items that falls out of the canvas */
static gboolean wordsgame_delete_items(gpointer user_data)
{
  LettersItem *item;
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_lock (&items_lock);
#else
  g_static_mutex_lock (&items_lock);
#endif
  /* items2del may be NULL, as we can get called after
     wordsgame_destroy_all_items() has been called (since we get called
     as a timeout handler). */
  if (items2del!=NULL){
    while (items2del->len>0)
      {
        item = g_ptr_array_index(items2del,0);
        g_ptr_array_remove_index_fast(items2del,0);
        wordsgame_destroy_item(item);
      }
  }
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_unlock (&items_lock);
#else
  g_static_mutex_unlock (&items_lock);
#endif
  return (FALSE);
}
/* Destroy all the items, called with items_lock locked */
static void wordsgame_destroy_all_items()
{
  LettersItem *item;
  if (items!=NULL){
    while (items->len>0)
      {
        item = g_ptr_array_index(items,0);
        g_ptr_array_remove_index_fast(items,0);
        wordsgame_destroy_item(item);
      }
    g_ptr_array_free (items, TRUE);
    items=NULL;
  }
  if (items2del!=NULL){
    while (items2del->len>0)
      {
        item = g_ptr_array_index(items2del,0);
        g_ptr_array_remove_index_fast(items2del,0);
        wordsgame_destroy_item(item);
      }
    g_ptr_array_free (items2del, TRUE);
    items2del=NULL;
  }
}
static GooCanvasItem *wordsgame_create_item(GooCanvasItem *parent)
{
  LettersItem *item;
  gchar *word = gc_wordlist_random_word_get(gc_wordlist, gcomprisBoard->level);
  GtkAnchorType direction_anchor = GTK_ANCHOR_NW;
  if(!word)
    /* Should display the dialog box here */
    return NULL;
  if (uppercase_only)
    {
      gchar *old = word;
      word = g_utf8_strup(old, -1);
      g_free(old);
    }
  // create and init item
  item = g_new(LettersItem,1);
  item->word = word;
  item->overword=g_strdup("");
  item->count=0;
  item->letter=g_utf8_strndup(item->word,1);
  item->pos=g_utf8_find_next_char(item->word,NULL);
  if (pango_unichar_direction(g_utf8_get_char(item->word)))
    direction_anchor = GTK_ANCHOR_NE;
  item->rootitem = goo_canvas_group_new (parent, NULL);
  goo_canvas_item_translate(item->rootitem, 0, -12);
  /* To 'erase' words, I create 2 times the text item. One is empty now */
  /* It will be filled each time the user enters the right key         */
  goo_canvas_text_new (item->rootitem,
			 item->word,
			 (double) 0,
			 (double) 0,
			 -1,
			 direction_anchor,
			 "font", gc_skin_font_board_huge_bold,
			 "fill_color_rgba", 0xffffffff,
			 NULL);
  item->overwriteItem = \
    goo_canvas_text_new (item->rootitem,
			 item->overword,
			 (double) 0,
			 (double) 0,
			 -1,
			 direction_anchor,
			 "font", gc_skin_font_board_huge_bold,
			 "fill-color_rgba", 0x000000ff,
			 NULL);
  /*set right x position */
  GooCanvasBounds bounds;
  goo_canvas_item_get_bounds    (item->rootitem,
				 &bounds);
 if(direction_anchor == GTK_ANCHOR_NW)
   goo_canvas_item_translate (item->rootitem,
			      (g_random_int()%(BOARDWIDTH-(gint)(bounds.x2))),
			      0);
  else
   {
      double new_x = (double)( g_random_int()%BOARDWIDTH);
      if ( new_x < -bounds.x1 )
	new_x -=  bounds.x1;
      goo_canvas_item_translate (item->rootitem,
				 new_x ,(double) 0);
   }
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_lock (&items_lock);
#else
  g_static_mutex_lock (&items_lock);
#endif
  g_ptr_array_add(items, item);
#if GLIB_CHECK_VERSION(2, 31, 0)
  g_mutex_unlock (&items_lock);
#else
  g_static_mutex_unlock (&items_lock);
#endif
  return (item->rootitem);
}
static void wordsgame_add_new_item()
{
  g_assert(gcomprisBoard->canvas!=NULL);
  wordsgame_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
}
/*
 * This is called on a low frequency and is used to drop new items
 *
 */
static gint wordsgame_drop_items (GtkWidget *widget, gpointer data)
{
  gc_sound_play_ogg ("sounds/level.wav", NULL);
  wordsgame_add_new_item();
  g_source_remove(drop_items_id);
  drop_items_id = g_timeout_add (fallSpeed,(GSourceFunc) wordsgame_drop_items, NULL);
  return (FALSE);
}
/* Called with items_lock locked */
static void player_win(LettersItem *item)
{
  gc_sound_play_ogg ("sounds/flip.wav", NULL);
  g_assert(gcomprisBoard!=NULL);
  gcomprisBoard->sublevel++;
  gc_score_set(gcomprisBoard->sublevel);
  g_ptr_array_remove(items,item);
  g_ptr_array_add(items2del,item);
  g_object_set (item->rootitem, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
  g_timeout_add (500,(GSourceFunc) wordsgame_delete_items, NULL);
  if(gcomprisBoard->sublevel > gcomprisBoard->number_of_sublevel)
    {
      /* Try the next level */
      gcomprisBoard->level++;
      gcomprisBoard->sublevel = 0;
      if(gcomprisBoard->level>gcomprisBoard->maxlevel)
	gcomprisBoard->level = gcomprisBoard->maxlevel;
      wordsgame_next_level_unlocked();
      gc_sound_play_ogg ("sounds/bonus.wav", NULL);
    }
  else
    {
      /* Drop a new item now to speed up the game */
      if(items->len==0)
        {
	  if ((fallSpeed-=INCREMENT_FALLSPEED) < MIN_FALLSPEED) fallSpeed+=INCREMENT_FALLSPEED;
	  if ((speed-=INCREMENT_SPEED) < MIN_SPEED) speed+=INCREMENT_SPEED;
          if (drop_items_id) {
            /* Remove pending new item creation to sync the falls */
            g_source_remove (drop_items_id);
            drop_items_id = 0;
          }
          if(!drop_items_id) {
            drop_items_id = g_timeout_add (0,
					   (GSourceFunc) wordsgame_drop_items,
					   NULL);
          }
        }
    }
}
static void player_loose()
{
  gc_sound_play_ogg ("sounds/crash.wav", NULL);
}
/* ************************************* */
/* *            Configuration          * */
/* ************************************* */
/* ======================= */
/* = config_start        = */
/* ======================= */
static GcomprisProfile *profile_conf;
static GcomprisBoard   *board_conf;
static void save_table (gpointer key,
			gpointer value,
			gpointer user_data)
{
  gc_db_set_board_conf ( profile_conf,
			    board_conf,
			    (gchar *) key,
			    (gchar *) value);
}
static gboolean
conf_ok(GHashTable *table)
{
  if (!table){
    if (gcomprisBoard)
      pause_board(FALSE);
    return TRUE;
  }
  g_hash_table_foreach(table, (GHFunc) save_table, NULL);
  if (gcomprisBoard){
    GHashTable *config;
    if (profile_conf)
      config = gc_db_get_board_conf();
    else
      config = table;
    gc_locale_set(g_hash_table_lookup( config, "locale"));
    gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
    if (up_init_str)
      {
	if(strcmp(up_init_str, "True")==0)
	  uppercase_only = TRUE;
	else
	  uppercase_only = FALSE;
      }
    if (profile_conf)
      g_hash_table_destroy(config);
    wordsgame_next_level();
    pause_board(FALSE);
  }
  board_conf = NULL;
  profile_conf = NULL;
  return TRUE;
}
static void
wordsgame_config_start(GcomprisBoard *agcomprisBoard,
		       GcomprisProfile *aProfile)
{
  GcomprisBoardConf *conf;
  board_conf = agcomprisBoard;
  profile_conf = aProfile;
  if (gcomprisBoard)
    pause_board(TRUE);
  gchar *label = g_strdup_printf(_("%s configuration\n for profile %s"),
				 agcomprisBoard->name,
				 aProfile? aProfile->name: "");
  conf = gc_board_config_window_display( label,
					 conf_ok);
  g_free(label);
  /* init the combo to previously saved value */
  GHashTable *config = gc_db_get_conf( profile_conf, board_conf);
  gchar *locale = g_hash_table_lookup( config, "locale");
  gc_board_config_combo_locales(conf, locale);
  gc_board_config_wordlist(conf, "wordsgame/default-$LOCALE.xml");
  /* upper case */
  gboolean up_init = FALSE;
  gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
  if (up_init_str && (strcmp(up_init_str, "True")==0))
    up_init = TRUE;
  gc_board_config_boolean_box(conf, _("Uppercase only text"),
			      "uppercase_only",
			      up_init);
}
static void wordsgame_config_stop(void)
{
}