[gcompris/gcomprixogoo] - Miquel DE IZARRA Added support for editing the content of the missing



commit 9ce88612e358e4d0f7eda5cc03ae68c7363197c2
Author: Bruno Coudoin <bcoudoin src gnome org>
Date:   Mon Feb 16 23:32:03 2009 +0000

    - Miquel DE IZARRA Added support for editing the content of the missing
    
    	- Miquel DE IZARRA Added support for editing the content
    	  of the missing letter activity.
    
    svn path=/trunk/; revision=3722

 ChangeLog                                          |   29 +
 src/boards/missingletter.c                         |  796 ++++++++++++++++++++
 src/gcompris/board_config_wordlist.c               |   12 +-
 src/gcompris/gameutil.c                            |    2 -
 src/gcompris/gc_net.c                              |    3 +-
 src/gcompris/gc_net.h                              |    1 +
 src/gcompris/gcompris.c                            |   45 ++-
 src/missing_letter-activity/Makefile.am            |    2 +-
 src/missing_letter-activity/missingletter.c        |  186 +++---
 src/missing_letter-activity/missingletter.h        |   36 +
 src/missing_letter-activity/missingletter_config.c |  592 +++++++++++++++
 src/readingh-activity/reading.c                    |   17 +-
 12 files changed, 1611 insertions(+), 110 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 3b491c5..07261df 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,34 @@
 2009-06-20  Bruno coudoin  <bruno coudoin free fr>
 
+	From trunk
+	- Miquel DE IZARRA Added support for editing the content
+	  of the missing letter activity.
+
+	* boards/missing_letter/Makefile.am:
+	* boards/missing_letter/board1.xml.in:
+	* boards/missing_letter/board2.xml.in:
+	* boards/missing_letter/board3.xml.in:
+	* boards/missing_letter/board4.xml.in:
+	* src/boards/Makefile.am:
+	* src/boards/missingletter.c: (pause_board), (start_board),
+	(end_board), (set_level), (missing_letter_next_level),
+	(missing_letter_create_item), (game_won), (init_xml),
+	(add_xml_data), (missing_read_xml_file),
+	(missing_destroy_board_list), (conf_ok), (config_start):
+	* src/boards/missingletter_config.c: (new_clicked),
+	(delete_clicked), (valid_entry), (apply_clicked), (up_clicked),
+	(down_clicked), (_save), (save_clicked), (level_changed),
+	(text_changed), (selection_changed), (destroy_conf_data),
+	(configure_colummns), (config_missing_letter):
+	* src/boards/reading.c: (reading_create_item), (get_random_word):
+	* src/gcompris/board_config_wordlist.c: (gc_board_config_wordlist):
+	* src/gcompris/gc_net.c: (gc_net_get_url_from_file),
+	(gc_cache_import_pixmap):
+	* src/gcompris/gc_net.h:
+	* src/gcompris/gcompris.c: (main):
+
+2009-06-20  Bruno coudoin  <bruno coudoin free fr>
+
 	From trunk.
 	Miguel DE IZARRA added support for online wordlist.
 	This save custom data in user_dir and make a content.txt (a md5sum
diff --git a/src/boards/missingletter.c b/src/boards/missingletter.c
new file mode 100644
index 0000000..476044a
--- /dev/null
+++ b/src/boards/missingletter.c
@@ -0,0 +1,796 @@
+/* gcompris - missingletter.c
+ *
+ * Copyright (C) 2001 Pascal Georges
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "gcompris/gcompris.h"
+
+/* libxml includes */
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+
+#define SOUNDLISTFILE PACKAGE
+
+GcomprisBoard *gcomprisBoard_missing = NULL;
+static gboolean board_paused = TRUE;
+
+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 int gamewon;
+static void		 process_ok(void);
+static void		 highlight_selected(GnomeCanvasItem *);
+static void		 game_won(void);
+static void		 config_start(GcomprisBoard *agcomprisBoard,
+					     GcomprisProfile *aProfile);
+static void		 config_stop(void);
+
+/* from missingletter_config.c */
+void config_missing_letter(GcomprisBoardConf *config);
+
+typedef struct _Board Board;
+struct _Board {
+  char  *pixmapfile;
+  char *question;
+  char *answer;
+  char  *l1;
+  char  *l2;
+  char  *l3;
+};
+
+static Board * board;
+
+/* XML */
+static void		 init_xml(void);
+static void		 add_xml_data(xmlDocPtr, xmlNodePtr, GList**);
+gboolean		 missing_read_xml_file(char *fname, GList**);
+void			 missing_destroy_board_list(GList *);
+static void		 destroy_board(Board * board);
+
+/* This is the list of boards */
+static GList *board_list = NULL;
+
+#define VERTICAL_SEPARATION 30
+#define HORIZONTAL_SEPARATION 30
+
+/* ================================================================ */
+static int right_word; // between 1 and 3, indicates which choice is the right one (the player clicks on it
+
+static GnomeCanvasGroup *boardRootItem = NULL;
+
+static GnomeCanvasItem *image_item = NULL;
+static GnomeCanvasItem *l1_item = NULL;
+static GnomeCanvasItem *l2_item = NULL;
+static GnomeCanvasItem *l3_item = NULL;
+static GnomeCanvasItem *text    = NULL;
+static GnomeCanvasItem *text_s  = NULL;
+static GnomeCanvasItem *button1 = NULL, *button2 = NULL, *button3 = NULL, *selected_button = NULL;
+
+static GnomeCanvasItem *missing_letter_create_item(GnomeCanvasGroup *parent);
+static void missing_letter_destroy_all_items(void);
+static void missing_letter_next_level(void);
+static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
+
+/* Description of this plugin */
+static BoardPlugin menu_bp =
+  {
+    NULL,
+    NULL,
+    N_("Reading"),
+    N_("Learn how to read"),
+    "Pascal Georges pascal georges1 free fr>",
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    start_board,
+    pause_board,
+    end_board,
+    is_our_board,
+    NULL,
+    process_ok,
+    set_level,
+    NULL,
+    NULL,
+    config_start,
+    config_stop
+  };
+
+/*
+ * Main entry point mandatory for each Gcompris's game
+ * ---------------------------------------------------
+ *
+ */
+
+GET_BPLUGIN_INFO(missingletter)
+
+/*
+ * in : boolean TRUE = PAUSE : FALSE = CONTINUE
+ *
+ */
+static void pause_board (gboolean pause)
+{
+  if(gcomprisBoard_missing==NULL)
+    return;
+
+  gc_bar_hide(FALSE);
+
+  if(gamewon == TRUE && pause == FALSE) /* the game is won */
+    {
+      game_won();
+    }
+
+  board_paused = pause;
+}
+
+/*
+ */
+static void start_board (GcomprisBoard *agcomprisBoard)
+{
+  GHashTable *config = gc_db_get_board_conf();
+  gchar * filename;
+
+  gc_locale_set(g_hash_table_lookup( config, "locale"));
+
+  g_hash_table_destroy(config);
+
+  if(agcomprisBoard!=NULL)
+    {
+      gcomprisBoard_missing=agcomprisBoard;
+      gc_set_background(gnome_canvas_root(gcomprisBoard_missing->canvas),
+			"opt/missingletter-bg.jpg");
+      gcomprisBoard_missing->level=1;
+
+      /* Calculate the maxlevel based on the available data file for this board */
+      gcomprisBoard_missing->maxlevel = 1;
+      while((filename = gc_file_find_absolute("%s/board%d.xml",
+      	gcomprisBoard_missing->boarddir, ++gcomprisBoard_missing->maxlevel)))
+	  g_free(filename);
+
+      gcomprisBoard_missing->maxlevel--;
+
+      gcomprisBoard_missing->sublevel=1;
+      gcomprisBoard_missing->number_of_sublevel=G_MAXINT;
+
+      init_xml();
+      gc_bar_set(GC_BAR_CONFIG | GC_BAR_LEVEL);
+
+      missing_letter_next_level();
+
+      gamewon = FALSE;
+      pause_board(FALSE);
+    }
+}
+
+static void end_board ()
+{
+
+  if(gcomprisBoard_missing!=NULL)
+    {
+      pause_board(TRUE);
+      gc_score_end();
+      missing_letter_destroy_all_items();
+      missing_destroy_board_list(board_list);
+      board_list = NULL;
+    }
+
+  gc_locale_reset();
+
+  gcomprisBoard_missing = NULL;
+}
+
+static void
+set_level (guint level)
+{
+
+  if(gcomprisBoard_missing!=NULL)
+    {
+      gcomprisBoard_missing->level=level;
+      gcomprisBoard_missing->sublevel=1;
+      init_xml();
+      missing_letter_next_level();
+    }
+}
+
+static gboolean
+is_our_board (GcomprisBoard *gcomprisBoard)
+{
+  if (gcomprisBoard)
+    {
+      if(g_strcasecmp(gcomprisBoard->type, "missingletter")==0)
+	{
+	  /* Set the plugin entry */
+	  gcomprisBoard->plugin=&menu_bp;
+
+	  return TRUE;
+	}
+    }
+  return FALSE;
+}
+
+/*-------------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------------*/
+/* set initial values for the next level */
+static void missing_letter_next_level()
+{
+  gc_bar_set_level(gcomprisBoard_missing);
+
+  missing_letter_destroy_all_items();
+  selected_button = NULL;
+  gamewon = FALSE;
+
+  gc_score_set(gcomprisBoard_missing->sublevel);
+
+  /* Try the next level */
+  missing_letter_create_item(gnome_canvas_root(gcomprisBoard_missing->canvas));
+
+}
+/* ==================================== */
+/* Destroy all the items */
+static void missing_letter_destroy_all_items()
+{
+  if(boardRootItem!=NULL)
+    gtk_object_destroy (GTK_OBJECT(boardRootItem));
+
+  boardRootItem = NULL;
+}
+/* ==================================== */
+static GnomeCanvasItem *missing_letter_create_item(GnomeCanvasGroup *parent)
+{
+  char *buf[3];
+  int xOffset,yOffset,place;
+  double dx1, dy1, dx2, dy2;
+  GdkPixbuf *button_pixmap = NULL;
+  GdkPixbuf *pixmap = NULL;
+
+  /* This are the values of the area in which we must display the image */
+  gint img_area_x = 290;
+  gint img_area_y = 80;
+  gint img_area_w = 440;
+  gint img_area_h = 310;
+
+  /* this is the coordinate of the text to find */
+  gint txt_area_x = 515;
+  gint txt_area_y = 435;
+
+  place = g_random_int_range( 0, 3);
+  g_assert(place >= 0  && place < 3);
+
+  right_word = place+1;
+
+  boardRootItem = GNOME_CANVAS_GROUP(
+				     gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard_missing->canvas),
+							    gnome_canvas_group_get_type (),
+							    "x", (double) 0,
+							    "y", (double) 0,
+							    NULL));
+  button_pixmap = gc_skin_pixmap_load("button.png");
+  /* display the image */
+  board = g_list_nth_data(board_list, gcomprisBoard_missing->sublevel-1);
+  g_assert(board != NULL);
+  pixmap = gc_pixmap_load(board->pixmapfile);
+
+  yOffset = (gcomprisBoard_missing->height - gdk_pixbuf_get_height(button_pixmap) - gdk_pixbuf_get_height(pixmap) - 2*VERTICAL_SEPARATION)/2;
+
+  text_s = gnome_canvas_item_new (boardRootItem,
+				gnome_canvas_text_get_type (),
+				"text", _(board->question),
+				"font", gc_skin_font_board_huge_bold,
+				"x", (double) txt_area_x + 1.0,
+				"y", (double) txt_area_y + 1.0,
+				"anchor", GTK_ANCHOR_CENTER,
+				"fill_color_rgba", gc_skin_get_color("missingletter/shadow"),
+				NULL);
+  text = gnome_canvas_item_new (boardRootItem,
+				gnome_canvas_text_get_type (),
+				"text", _(board->question),
+				"font", gc_skin_font_board_huge_bold,
+				"x", (double) txt_area_x,
+				"y", (double) txt_area_y,
+				"anchor", GTK_ANCHOR_CENTER,
+				"fill_color_rgba", gc_skin_get_color("missingletter/question"),
+				NULL);
+
+  gnome_canvas_item_get_bounds(text, &dx1, &dy1, &dx2, &dy2);
+  yOffset += VERTICAL_SEPARATION + dy2-dy1;
+
+  image_item = gnome_canvas_item_new (boardRootItem,
+				      gnome_canvas_pixbuf_get_type (),
+				      "pixbuf", pixmap,
+				      "x", (double) img_area_x+(img_area_w - gdk_pixbuf_get_width(pixmap))/2,
+				      "y", (double) img_area_y+(img_area_h - gdk_pixbuf_get_height(pixmap))/2,
+				      "width", (double) gdk_pixbuf_get_width(pixmap),
+				      "height", (double) gdk_pixbuf_get_height(pixmap),
+				      "width_set", TRUE,
+				      "height_set", TRUE,
+				      NULL);
+  gdk_pixbuf_unref(pixmap);
+
+  /* display the 3 words */
+  /* the right word is at position 0 : it is swapped with any position depending of place value */
+
+  switch (place) {
+  case 1 :
+    buf[0] = board->l2;
+    buf[1] = board->l1;
+    buf[2] = board->l3;
+    break;
+  case 2 :
+    buf[0] = board->l3;
+    buf[1] = board->l2;
+    buf[2] = board->l1;
+    break;
+  default :
+    buf[0] = board->l1;
+    buf[1] = board->l2;
+    buf[2] = board->l3;
+    break;
+  }
+
+  yOffset = ( gcomprisBoard_missing->height - 3*gdk_pixbuf_get_height(button_pixmap) - 2*VERTICAL_SEPARATION) / 2;
+  xOffset = (img_area_x-gdk_pixbuf_get_width(button_pixmap))/2;
+  button1 = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_pixbuf_get_type (),
+				   "pixbuf",  button_pixmap,
+				   "x",  (double) xOffset,
+				   "y",  (double) yOffset,
+				   NULL);
+  gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[0],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2 + 1.0,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2 + 1.0,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_shadow,
+				   NULL);
+  l1_item = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[0],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_text_button,
+				   NULL);
+
+  yOffset += HORIZONTAL_SEPARATION + gdk_pixbuf_get_height(button_pixmap);
+  button2 = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_pixbuf_get_type (),
+				   "pixbuf",  button_pixmap,
+				   "x",  (double) xOffset,
+				   "y",  (double) yOffset,
+				   NULL);
+  gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[1],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2 + 1.0,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2 + 1.0,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_shadow,
+				   NULL);
+  l2_item = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[1],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_text_button,
+				   NULL);
+
+  yOffset += HORIZONTAL_SEPARATION + gdk_pixbuf_get_height(button_pixmap);
+
+  button3 = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_pixbuf_get_type (),
+				   "pixbuf",  button_pixmap,
+				   "x",  (double) xOffset,
+				   "y",  (double) yOffset,
+				   NULL);
+  gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[2],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2 + 1.0,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2 + 1.0,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_shadow,
+				   NULL);
+  l3_item = gnome_canvas_item_new (boardRootItem,
+				   gnome_canvas_text_get_type (),
+				   "text", buf[2],
+				   "font", gc_skin_font_board_huge_bold,
+				   "x", (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2,
+				   "y", (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2,
+				   "anchor", GTK_ANCHOR_CENTER,
+				   "fill_color_rgba", gc_skin_color_text_button,
+				   NULL);
+
+  gdk_pixbuf_unref(button_pixmap);
+
+  gtk_signal_connect(GTK_OBJECT(l1_item), "event", (GtkSignalFunc) item_event, NULL);
+  gtk_signal_connect(GTK_OBJECT(l2_item), "event", (GtkSignalFunc) item_event, NULL);
+  gtk_signal_connect(GTK_OBJECT(l3_item), "event", (GtkSignalFunc) item_event, NULL);
+
+  gtk_signal_connect(GTK_OBJECT(button1), "event",  (GtkSignalFunc) item_event, NULL);
+  gtk_signal_connect(GTK_OBJECT(button2), "event",  (GtkSignalFunc) item_event, NULL);
+  gtk_signal_connect(GTK_OBJECT(button3), "event",  (GtkSignalFunc) item_event, NULL);
+  return NULL;
+}
+/* ==================================== */
+static void game_won() {
+  gcomprisBoard_missing->sublevel++;
+
+  if(gcomprisBoard_missing->sublevel>gcomprisBoard_missing->number_of_sublevel) {
+    /* Try the next level */
+    gcomprisBoard_missing->sublevel=1;
+    gcomprisBoard_missing->level++;
+    init_xml();
+
+    if(gcomprisBoard_missing->level>gcomprisBoard_missing->maxlevel) {
+	gc_bonus_end_display(GC_BOARD_FINISHED_TUXPLANE);
+	return;
+    }
+  }
+  missing_letter_next_level();
+}
+
+/* ==================================== */
+static gboolean process_ok_timeout() {
+  gc_bonus_display(gamewon, GC_BONUS_FLOWER);
+  return FALSE;
+}
+
+static void process_ok() {
+  if (gamewon) {
+    gnome_canvas_item_set(text,   "text", board->answer, NULL);
+    gnome_canvas_item_set(text_s, "text", board->answer, NULL);
+  }
+  // leave time to display the right answer
+  gc_bar_hide(TRUE);
+  g_timeout_add(TIME_CLICK_TO_BONUS, process_ok_timeout, NULL);
+}
+
+/* ==================================== */
+static gint
+item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
+{
+  double item_x, item_y;
+  GnomeCanvasItem * temp = NULL;
+  item_x = event->button.x;
+  item_y = event->button.y;
+  gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
+
+  if(board_paused)
+    return FALSE;
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+      board_paused = TRUE;
+      temp = item;
+      if (item == l1_item)
+	temp = button1;
+      if (item == l2_item)
+	temp = button2;
+      if (item == l3_item)
+	temp = button3;
+
+      g_assert(temp == button1 || temp == button2 || temp == button3);
+
+      if ( ( temp == button1 && right_word == 1) ||
+	   ( temp == button2 && right_word == 2) ||
+	   ( temp == button3 && right_word == 3) ) {
+	gamewon = TRUE;
+      } else {
+	gamewon = FALSE;
+      }
+      highlight_selected(temp);
+      process_ok();
+      break;
+
+    default:
+      break;
+    }
+  return FALSE;
+}
+/* ==================================== */
+static void highlight_selected(GnomeCanvasItem * item) {
+  GdkPixbuf *button_pixmap_selected = NULL, *button_pixmap = NULL;
+  GnomeCanvasItem *button;
+
+  /* Replace text item by button item */
+  button = item;
+  if ( button == l1_item ) {
+    button = button1;
+  } else if ( item == l2_item ) {
+    button = button2;
+  } else if ( item == l3_item ) {
+    button = button3;
+  }
+
+  if (selected_button != NULL && selected_button != button) {
+  	button_pixmap = gc_skin_pixmap_load("button.png");
+  	gnome_canvas_item_set(selected_button, "pixbuf", button_pixmap, NULL);
+  	gdk_pixbuf_unref(button_pixmap);
+  }
+
+  if (selected_button != button) {
+  	button_pixmap_selected = gc_skin_pixmap_load("button_selected.png");
+  	gnome_canvas_item_set(button, "pixbuf", button_pixmap_selected, NULL);
+  	selected_button = button;
+  	gdk_pixbuf_unref(button_pixmap_selected);
+  }
+
+}
+
+/* ===================================
+ *                XML stuff
+ *                Ref : shapegame.c
+ * ==================================== */
+/* ======  for DEBUG ========  */
+#if 0
+static void dump_xml() {
+  GList *list;
+  g_warning("XML lentgh = %d\n", g_list_length(board_list));
+
+  for(list = board_list; list != NULL; list = list->next) {
+    Board * board = list->data;
+    g_warning("xml = %s %s %s %s %s %s\n", board->pixmapfile, board->answer, board->question, board->l1, board->l2, board->l3);
+  }
+}
+#endif
+
+/* ==================================== */
+static void init_xml()
+{
+  char *filename;
+
+  if(board_list)
+  {
+    missing_destroy_board_list(board_list);
+    board_list = NULL;
+  }
+  filename = gc_file_find_absolute("%s/board%d.xml",
+				   gcomprisBoard_missing->boarddir,
+				   gcomprisBoard_missing->level);
+  missing_read_xml_file(filename, &board_list);
+  gcomprisBoard_missing->number_of_sublevel = g_list_length(board_list);
+  g_free(filename);
+
+  gc_score_end();
+  gc_score_start(SCORESTYLE_NOTE,
+		  50,
+		  gcomprisBoard_missing->height - 50,
+		  gcomprisBoard_missing->number_of_sublevel);
+}
+
+/* ==================================== */
+static void add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GList **list)
+{
+  gchar *pixmapfile = NULL;
+  gchar *question = NULL, *answer = NULL;
+  gchar *l1 = NULL, *l2 = NULL, *l3 = NULL;
+  gchar *data = NULL;
+  Board * board = g_new(Board,1);
+  gboolean found = FALSE;
+
+  xmlnode = xmlnode->xmlChildrenNode;
+
+  xmlnode = xmlnode->next;
+
+  while (xmlnode != NULL && !found) {
+
+    if (!strcmp((char *)xmlnode->name, "pixmapfile"))
+      pixmapfile = (gchar *)xmlNodeListGetString(doc, xmlnode->xmlChildrenNode, 1);
+
+    if (!found && !strcmp((char *)xmlnode->name, "data"))
+      {
+	if(data==NULL)
+	  {
+	    gchar *tmp;
+	    tmp = (gchar *)xmlNodeListGetString(doc, xmlnode->xmlChildrenNode, 1);
+	    data = g_strdup(gettext(tmp));
+	    g_free(tmp);
+	  }
+      }
+    xmlnode = xmlnode->next;
+  }
+
+  gchar **all_answer = g_strsplit(data, "/", 5);
+  g_free(data);
+
+  answer = all_answer[0];
+  question = all_answer[1];
+  l1 = all_answer[2];
+  l2 = all_answer[3];
+  l3 = all_answer[4];
+
+  g_assert(l1 != NULL  && l2 != NULL && l3 != NULL && answer != NULL && question != NULL);
+
+  board->pixmapfile = pixmapfile;
+  board->question = g_strdup(question);
+  board->answer = g_strdup(answer);
+  board->l1 = g_strdup(l1);
+  board->l2 = g_strdup(l2);
+  board->l3 = g_strdup(l3);
+
+  g_strfreev(all_answer);
+
+  *list = g_list_append (*list, board);
+}
+
+
+/* ==================================== */
+/* read an xml file into our memory structures and update our view,
+   dump any old data we have in memory if we can load a new set */
+gboolean missing_read_xml_file(char *fname, GList **list)
+{
+  /* pointer to the new doc */
+  xmlDocPtr doc;
+  xmlNodePtr node;
+
+  g_return_val_if_fail(fname!=NULL,FALSE);
+
+  /* parse the new file and put the result into newdoc */
+  doc = xmlParseFile(fname);
+
+  /* in case something went wrong */
+  if(!doc)
+    return FALSE;
+
+  if(/* if there is no root element */
+     !doc->children ||
+     /* if it doesn't have a name */
+     !doc->children->name ||
+     /* if it isn't a ImageId node */
+     g_strcasecmp((char *)doc->children->name,"missing_letter")!=0) {
+    xmlFreeDoc(doc);
+    return FALSE;
+  }
+
+  for(node = doc->children->children; node != NULL; node = node->next) {
+    if ( g_strcasecmp((gchar *)node->name, "Board") == 0 )
+      add_xml_data(doc, node, list);
+  }
+  xmlFreeDoc(doc);
+  return TRUE;
+}
+
+/* ======================================= */
+void missing_destroy_board_list(GList *list) {
+  Board *board;
+  while(g_list_length(list)>0)
+    {
+      board = g_list_nth_data(list, 0);
+      list = g_list_remove (list, board);
+      destroy_board(board);
+    }
+}
+
+/* ======================================= */
+static void destroy_board(Board * board) {
+  g_free(board->pixmapfile);
+  g_free(board->answer);
+  g_free(board->question);
+  g_free(board->l1);
+  g_free(board->l2);
+  g_free(board->l3);
+  g_free(board);
+}
+
+/* ************************************* */
+/* *            Configuration          * */
+/* ************************************* */
+
+
+/* ======================= */
+/* = config_start        = */
+/* ======================= */
+
+static GcomprisProfile *profile_conf;
+static GcomprisBoard   *board_conf;
+
+/* GHFunc */
+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 GcomprisConfCallback conf_ok(GHashTable *table)
+{
+  if (!table){
+    if (gcomprisBoard_missing)
+      pause_board(FALSE);
+    return NULL;
+  }
+
+  g_hash_table_foreach(table, (GHFunc) save_table, NULL);
+
+  if (gcomprisBoard_missing){
+    gc_locale_reset();
+
+    GHashTable *config;
+
+    if (profile_conf)
+      config = gc_db_get_board_conf();
+    else
+      config = table;
+
+    gc_locale_set(g_hash_table_lookup( config, "locale"));
+
+    if (profile_conf)
+      g_hash_table_destroy(config);
+
+    init_xml();
+
+    missing_letter_next_level();
+
+  }
+
+    board_conf = NULL;
+  profile_conf = NULL;
+  pause_board(FALSE);
+
+  return NULL;
+}
+
+static void
+config_start(GcomprisBoard *agcomprisBoard,
+		    GcomprisProfile *aProfile)
+{
+  board_conf = agcomprisBoard;
+  profile_conf = aProfile;
+
+  if (gcomprisBoard_missing)
+    pause_board(TRUE);
+
+  gchar *label = g_strdup_printf(_("<b>%s</b> configuration\n for profile <b>%s</b>"),
+				 agcomprisBoard->name,
+				 aProfile ? aProfile->name : "");
+  GcomprisBoardConf *bconf;
+  bconf = gc_board_config_window_display( label,
+				 (GcomprisConfCallback )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(bconf, locale);
+  config_missing_letter(bconf);
+}
+
+
+/* ======================= */
+/* = config_stop        = */
+/* ======================= */
+static void
+config_stop()
+{
+}
diff --git a/src/gcompris/board_config_wordlist.c b/src/gcompris/board_config_wordlist.c
index b0609ba..a6364b2 100644
--- a/src/gcompris/board_config_wordlist.c
+++ b/src/gcompris/board_config_wordlist.c
@@ -49,7 +49,7 @@ static void _combo_level_changed(GtkComboBox *combo_level, gpointer user_data)
 	}
 
 	wordsArray = g_malloc0(sizeof(gpointer)*(g_slist_length(lw->words)+1));
-	
+
 	for(i=0, list = lw->words; list; list=list->next)
 	{
 		wordsArray[i]=(gchar*)list->data;
@@ -159,7 +159,7 @@ static void _button_clicked(GtkWidget *w, gpointer data)
 static void _destroy(GtkWidget *w, gpointer data)
 {
 	user_param_type_wordlist *u = (user_param_type_wordlist*)data;
-	
+
 	gc_wordlist_free(u->wordlist);
 	g_free(u);
 }
@@ -177,7 +177,7 @@ GtkWidget *gc_board_config_wordlist(GcomprisBoardConf *config, const gchar *file
 	const gchar *locale;
 
 	/* frame */
-	frame = gtk_frame_new("Change wordlist");
+	frame = gtk_frame_new(_("Configure the list of words"));
 	gtk_widget_show(frame);
 	gtk_box_pack_start(GTK_BOX(config->main_conf_box), frame, FALSE, FALSE, 8);
 
@@ -206,7 +206,7 @@ GtkWidget *gc_board_config_wordlist(GcomprisBoardConf *config, const gchar *file
 
 	gtk_widget_show(combo_lang);
 	hbox = gtk_hbox_new(FALSE, 8);
-	label = gtk_label_new(_("Choice language"));
+	label = gtk_label_new(_("Choice of the language"));
 	gtk_widget_show(label);
 	gtk_widget_show(hbox);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 8);
@@ -218,7 +218,7 @@ GtkWidget *gc_board_config_wordlist(GcomprisBoardConf *config, const gchar *file
 
 	gtk_widget_show(combo_level);
 	hbox = gtk_hbox_new(FALSE, 8);
-	label = gtk_label_new(_("Choice level"));
+	label = gtk_label_new(_("Choice of the level"));
 	gtk_widget_show(label);
 	gtk_widget_show(hbox);
 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 8);
@@ -243,7 +243,7 @@ GtkWidget *gc_board_config_wordlist(GcomprisBoardConf *config, const gchar *file
 	gtk_widget_show(hbox);
 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 8);
 
-	GtkWidget * b_default = gtk_button_new_with_label(_("Return to default"));
+	GtkWidget * b_default = gtk_button_new_with_label(_("Back to default"));
 	gtk_widget_show(b_default);
 	gtk_box_pack_start(GTK_BOX(hbox), b_default, FALSE, FALSE, 8);
 
diff --git a/src/gcompris/gameutil.c b/src/gcompris/gameutil.c
index 465ce04..d7a6edb 100644
--- a/src/gcompris/gameutil.c
+++ b/src/gcompris/gameutil.c
@@ -627,8 +627,6 @@ gc_file_find_absolute(const gchar *format, ...)
 		  g_free(filename2);
 		  goto FOUND;
 		}
-	      g_free(absolute_filename);
-
 	      g_free(filename2);
 
 	    }
diff --git a/src/gcompris/gc_net.c b/src/gcompris/gc_net.c
index ecc7b73..106ec1c 100644
--- a/src/gcompris/gc_net.c
+++ b/src/gcompris/gc_net.c
@@ -187,7 +187,8 @@ gc_net_get_url_from_file(const gchar *format, ...)
 
   return cache;
 #endif
-  }
+}
+
 
 #if 0
 /** return a glist with the content of the files in the given directory
diff --git a/src/gcompris/gc_net.h b/src/gcompris/gc_net.h
index c377748..1b162cd 100644
--- a/src/gcompris/gc_net.h
+++ b/src/gcompris/gc_net.h
@@ -38,6 +38,7 @@ void gc_net_destroy();
 
 void gc_cache_init(void);
 void gc_cache_add(gchar *filename);
+gchar* gc_cache_import_pixmap(gchar *filename, gchar *boarddir, gint width, gint height);
 void gc_cache_remove(gchar *filename);
 void gc_cache_save(void);
 void gc_cache_destroy(void);
diff --git a/src/gcompris/gcompris.c b/src/gcompris/gcompris.c
index efbe124..5c1ec19 100644
--- a/src/gcompris/gcompris.c
+++ b/src/gcompris/gcompris.c
@@ -136,6 +136,9 @@ static gchar *popt_user_dir	   = NULL;
 static gint  popt_experimental     = FALSE;
 static gint  popt_no_quit	   = FALSE;
 static gint  popt_no_config        = FALSE;
+static gchar *popt_server          = NULL;
+static gint  *popt_web_only        = NULL;
+static gchar *popt_cache_dir       = NULL;
 static gchar *popt_drag_mode       = NULL;
 static gchar *sugarBundleId        = NULL;
 static gchar *sugarActivityId      = NULL;
@@ -221,6 +224,16 @@ static GOptionEntry options[] = {
   {"disable-config",'\0', 0, G_OPTION_ARG_NONE, &popt_no_config,
    N_("Disable the config button"), NULL},
 
+  {"server", '\0', 0, G_OPTION_ARG_STRING, &popt_server,
+   N_("GCompris will get images, sounds and activity data from this server if not found locally."), NULL},
+
+  {"web-only", '\0', 0, G_OPTION_ARG_NONE, &popt_web_only,
+   N_("Only when --server is provided, disable check for local resource first."
+      " Data are always taken from the web server."), NULL},
+
+  {"cache-dir", '\0', 0, G_OPTION_ARG_STRING, &popt_cache_dir,
+   N_("In server mode, specify the cache directory used to avoid useless downloads."), NULL},
+
   {"drag-mode", 'g', 0, G_OPTION_ARG_STRING, &popt_drag_mode,
    N_("Global drag and drop mode: normal, 2clicks, both. Default mode is normal."), NULL},
 
@@ -1602,7 +1615,7 @@ main (int argc, char *argv[])
 	{
 	  if (g_access(properties->database, R_OK)==-1)
 	    {
-	      printf("%s exists but is not readable or writable", properties->database);
+	      printf(_("%s exists but is not readable or writable"), properties->database);
 	      exit(0);
 	    }
 	}
@@ -1660,6 +1673,33 @@ main (int argc, char *argv[])
       properties->reread_menu = TRUE;
   }
 
+  if (popt_server){
+#ifdef USE_GNET
+      properties->server = g_strdup(popt_server);
+      printf("   Server '%s'\n", properties->server);
+#else
+      printf(_("The --server option cannot be used because"
+	       "GCompris has been compiled without network support!"));
+      exit(1);
+#endif
+  }
+
+  if(popt_web_only) {
+    g_free(properties->package_data_dir);
+    properties->package_data_dir = g_strdup("");
+
+    g_free(properties->system_icon_dir);
+    properties->system_icon_dir = g_strdup("");
+  }
+
+  if (popt_server){
+    if(popt_cache_dir)
+      properties->cache_dir = g_strdup(popt_cache_dir);
+    else
+      properties->cache_dir = g_build_filename(g_get_user_cache_dir(), "gcompris", NULL);
+    printf("   Cache dir '%s'\n",properties->cache_dir);
+  }
+
   if (popt_drag_mode){
     if (strcmp(popt_drag_mode, "default") == 0)
       properties->drag_mode = GC_DRAG_MODE_GRAB;
@@ -1692,7 +1732,8 @@ main (int argc, char *argv[])
 
     if(properties->profile == NULL)
       {
-	printf("ERROR: Profile '%s' is not found. Run 'gcompris --profile-list' to list available ones\n",
+	printf(_("ERROR: Profile '%s' is not found."
+		 " Run 'gcompris --profile-list' to list available ones\n"),
 	       popt_profile);
 	exit(1);
       }
diff --git a/src/missing_letter-activity/Makefile.am b/src/missing_letter-activity/Makefile.am
index e59e8d1..5c1cdec 100644
--- a/src/missing_letter-activity/Makefile.am
+++ b/src/missing_letter-activity/Makefile.am
@@ -25,7 +25,7 @@ INCLUDES = 	-I$(top_srcdir)/src \
 
 libmissingletter_la_LDFLAGS = $(shared) $(no_undefined) -module -avoid-version $(gc_libs)
 libmissingletter_la_LIBADD =
-libmissingletter_la_SOURCES = missingletter.c
+libmissingletter_la_SOURCES = missingletter.c missingletter_config.c missingletter.h
 
 xmldir = $(pkgdatadir)/@PACKAGE_DATA_DIR@
 
diff --git a/src/missing_letter-activity/missingletter.c b/src/missing_letter-activity/missingletter.c
index 80a3f79..cb9bfaf 100644
--- a/src/missing_letter-activity/missingletter.c
+++ b/src/missing_letter-activity/missingletter.c
@@ -24,18 +24,11 @@
 #include <libxml/tree.h>
 #include <libxml/parser.h>
 
-#define SOUNDLISTFILE PACKAGE
+#include "missingletter.h"
 
-#define MAX_PROPOSAL 6
-typedef struct _Board {
-  gchar *pixmapfile;
-  gchar *question;
-  gchar *answer;
-  gchar *text[MAX_PROPOSAL + 1];
-  guint solution;
-} Board;
+#define SOUNDLISTFILE PACKAGE
 
-static GcomprisBoard *gcomprisBoard = NULL;
+GcomprisBoard *gcomprisBoard_missing = NULL;
 static gboolean board_paused = TRUE;
 
 static void		 start_board (GcomprisBoard *agcomprisBoard);
@@ -43,6 +36,7 @@ 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 int gamewon;
 static void		 process_ok(gchar *answer);
 static void		 highlight_selected(GooCanvasItem *);
 static void		 game_won(void);
@@ -50,15 +44,14 @@ static void		 config_start(GcomprisBoard *agcomprisBoard,
 					     GcomprisProfile *aProfile);
 static void		 config_stop(void);
 
-static int gamewon;
+/* from missingletter_config.c */
+void config_missing_letter(GcomprisBoardConf *config);
 
 /* XML */
-static gboolean		 read_xml_file(char *fname);
 static void		 init_xml(guint level);
-static void		 add_xml_data(xmlDocPtr, xmlNodePtr, GNode *);
-static void		 parse_doc(xmlDocPtr doc);
-static gboolean		 read_xml_file(char *fname);
-static void		 destroy_board_list();
+static void		 add_xml_data(xmlDocPtr, xmlNodePtr, GList **list);
+gboolean		 missing_read_xml_file(char *fname, GList **list);
+void			 missing_destroy_board_list(GList *);
 static void		 destroy_board(Board * board);
 
 /* This is the list of boards */
@@ -122,7 +115,7 @@ GET_BPLUGIN_INFO(missingletter)
  */
 static void pause_board (gboolean pause)
 {
-  if(gcomprisBoard==NULL)
+  if(gcomprisBoard_missing==NULL)
     return;
 
   gc_bar_hide(FALSE);
@@ -135,12 +128,31 @@ static void pause_board (gboolean pause)
   board_paused = pause;
 }
 
+static void _init(GcomprisBoard *agcomprisBoard)
+{
+  gchar * filename;
+  gcomprisBoard_missing->level=1;
+
+  /* Calculate the maxlevel based on the available data file for this board */
+  gcomprisBoard_missing->maxlevel = 1;
+  while((filename = gc_file_find_absolute("%s/board%d.xml",
+					  gcomprisBoard_missing->boarddir,
+					  ++gcomprisBoard_missing->maxlevel)))
+    g_free(filename);
+
+  gcomprisBoard_missing->maxlevel--;
+
+  gcomprisBoard_missing->sublevel=1;
+  gcomprisBoard_missing->number_of_sublevel=G_MAXINT;
+
+  init_xml(gcomprisBoard_missing->level);
+}
+
 /*
  */
 static void
 start_board (GcomprisBoard *agcomprisBoard)
 {
-  gchar *filename = NULL;
   GHashTable *config = gc_db_get_board_conf();
 
   gc_locale_set(g_hash_table_lookup( config, "locale"));
@@ -149,27 +161,10 @@ start_board (GcomprisBoard *agcomprisBoard)
 
   if(agcomprisBoard!=NULL)
     {
-      gcomprisBoard=agcomprisBoard;
-      gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
+      gcomprisBoard_missing=agcomprisBoard;
+      gc_set_background(goo_canvas_get_root_item(gcomprisBoard_missing->canvas),
 			"missing_letter/missingletter-bg.jpg");
-      gcomprisBoard->level=1;
-      gcomprisBoard->sublevel=1;
-
-      /* Calculate the maxlevel based on the available data file for this board */
-      gcomprisBoard->maxlevel=1;
-
-      while( (filename = gc_file_find_absolute("%s/board%d.xml",
-					       gcomprisBoard->boarddir,
-					       gcomprisBoard->maxlevel++,
-					       NULL)) )
-	{
-	  g_free(filename);
-
-	}
-      g_free(filename);
-
-      gcomprisBoard->maxlevel -= 2;
-
+       _init(agcomprisBoard);
       gc_bar_set(GC_BAR_CONFIG | GC_BAR_LEVEL);
       gc_bar_location(10, -1, 0.9);
 
@@ -183,27 +178,29 @@ start_board (GcomprisBoard *agcomprisBoard)
 static void end_board ()
 {
 
-  if(gcomprisBoard!=NULL)
+  if(gcomprisBoard_missing!=NULL)
     {
       pause_board(TRUE);
       gc_score_end();
       missing_letter_destroy_all_items();
-      destroy_board_list();
+      missing_destroy_board_list(board_list);
+      board_list = NULL;
     }
 
   gc_locale_reset();
 
-  gcomprisBoard = NULL;
+  gcomprisBoard_missing = NULL;
 }
 
 static void
 set_level (guint level)
 {
 
-  if(gcomprisBoard!=NULL)
+  if(gcomprisBoard_missing!=NULL)
     {
-      gcomprisBoard->level=level;
-      gcomprisBoard->sublevel=1;
+      gcomprisBoard_missing->level=level;
+      gcomprisBoard_missing->sublevel=1;
+      init_xml(gcomprisBoard_missing->level);
       missing_letter_next_level();
     }
 }
@@ -230,28 +227,26 @@ is_our_board (GcomprisBoard *gcomprisBoard)
 static void
 missing_letter_next_level()
 {
-  gc_bar_set_level(gcomprisBoard);
+  gc_bar_set_level(gcomprisBoard_missing);
 
   missing_letter_destroy_all_items();
   selected_button = NULL;
   gamewon = FALSE;
 
-  destroy_board_list();
-  init_xml(gcomprisBoard->level);
-
-  gcomprisBoard->number_of_sublevel = g_list_length(board_list);
+  init_xml(gcomprisBoard_missing->level);
+  gcomprisBoard_missing->number_of_sublevel = g_list_length(board_list);
 
   gc_score_end();
   gc_score_start(SCORESTYLE_NOTE,
 		 BOARDWIDTH - 195,
 		 BOARDHEIGHT - 30,
-		 gcomprisBoard->number_of_sublevel);
+		 gcomprisBoard_missing->number_of_sublevel);
 
 
-  gc_score_set(gcomprisBoard->sublevel);
+  gc_score_set(gcomprisBoard_missing->sublevel);
 
   /* Try the next level */
-  missing_letter_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
+  missing_letter_create_item(goo_canvas_get_root_item(gcomprisBoard_missing->canvas));
 }
 
 static void
@@ -261,10 +256,10 @@ missing_letter_next_sublevel()
   selected_button = NULL;
   gamewon = FALSE;
 
-  gc_score_set(gcomprisBoard->sublevel);
+  gc_score_set(gcomprisBoard_missing->sublevel);
 
   /* Try the next level */
-  missing_letter_create_item(goo_canvas_get_root_item(gcomprisBoard->canvas));
+  missing_letter_create_item(goo_canvas_get_root_item(gcomprisBoard_missing->canvas));
 }
 
 /* ==================================== */
@@ -299,12 +294,12 @@ missing_letter_create_item(GooCanvasItem *parent)
   guint vertical_separation;
   gint i;
 
-  board_number = gcomprisBoard->sublevel-1;
+  board_number = gcomprisBoard_missing->sublevel-1;
 
   g_assert(board_number >= 0  && board_number < g_list_length(board_list));
 
   boardRootItem = \
-    goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
+    goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard_missing->canvas),
 			  NULL);
 
   button_pixmap = gc_pixmap_load("missing_letter/button.png");
@@ -417,15 +412,15 @@ missing_letter_create_item(GooCanvasItem *parent)
 
 /* ==================================== */
 static void game_won() {
-  gcomprisBoard->sublevel++;
+  gcomprisBoard_missing->sublevel++;
 
-  if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel)
+  if(gcomprisBoard_missing->sublevel>gcomprisBoard_missing->number_of_sublevel)
     {
       /* Try the next level */
-      gcomprisBoard->sublevel=1;
-      gcomprisBoard->level++;
-      if(gcomprisBoard->level>gcomprisBoard->maxlevel)
-	gcomprisBoard->level = gcomprisBoard->maxlevel;
+      gcomprisBoard_missing->sublevel=1;
+      gcomprisBoard_missing->level++;
+      if(gcomprisBoard_missing->level>gcomprisBoard_missing->maxlevel)
+	gcomprisBoard_missing->level = gcomprisBoard_missing->maxlevel;
 
       missing_letter_next_level();
     }
@@ -526,17 +521,22 @@ init_xml(guint level)
 {
   gchar *filename;
 
+  if(board_list)
+    {
+      missing_destroy_board_list(board_list);
+      board_list = NULL;
+    }
   filename = gc_file_find_absolute("%s/board%d.xml",
-				   gcomprisBoard->boarddir,
+				   gcomprisBoard_missing->boarddir,
 				   level);
-
-  g_assert(read_xml_file(filename)== TRUE);
+  missing_read_xml_file(filename, &board_list);
+  gcomprisBoard_missing->number_of_sublevel = g_list_length(board_list);
   g_free(filename);
 }
 
 /* ==================================== */
 static void
-add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GNode * child)
+add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GList **list)
 {
   Board * board = g_new0(Board,1);
   guint text_index = 0;
@@ -582,6 +582,7 @@ add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GNode * child)
       gc_dialog(_("Data file for this level is not properly formatted."),
 		gc_board_stop);
       g_free(board);
+      *list = NULL;
       return;
     }
 
@@ -609,33 +610,21 @@ add_xml_data(xmlDocPtr doc, xmlNodePtr xmlnode, GNode * child)
 
   /* Insert boards randomly in the list */
   if(g_random_int_range(0, 2))
-    board_list = g_list_append (board_list, board);
+    *list = g_list_append (*list, board);
   else
-    board_list = g_list_prepend (board_list, board);
-}
-
-/* ==================================== */
-static void
-parse_doc(xmlDocPtr doc)
-{
-  xmlNodePtr node;
-
-  for(node = doc->children->children; node != NULL; node = node->next) {
-    if ( g_strcasecmp((gchar *)node->name, "Board") == 0 )
-      add_xml_data(doc, node,NULL);
-  }
-
+    *list = g_list_prepend (*list, board);
 }
 
 /* ==================================== */
 /* read an xml file into our memory structures and update our view,
    dump any old data we have in memory if we can load a new set */
-static gboolean
-read_xml_file(char *fname)
+gboolean missing_read_xml_file(char *fname, GList **list)
 {
   /* pointer to the new doc */
   xmlDocPtr doc;
+  xmlNodePtr node;
 
+  *list = NULL;
   g_return_val_if_fail(fname!=NULL,FALSE);
 
   /* parse the new file and put the result into newdoc */
@@ -649,26 +638,28 @@ read_xml_file(char *fname)
      !doc->children ||
      /* if it doesn't have a name */
      !doc->children->name ||
-     /* if it isn't a ImageId node */
+     /* if it isn't a missing letter node */
      g_strcasecmp((char *)doc->children->name,"missing_letter")!=0) {
     xmlFreeDoc(doc);
     return FALSE;
   }
 
-  parse_doc(doc);
+  for(node = doc->children->children; node != NULL; node = node->next) {
+    if ( g_strcasecmp((gchar *)node->name, "Board") == 0 )
+      add_xml_data(doc, node, list);
+  }
   xmlFreeDoc(doc);
   return TRUE;
 }
 /* ======================================= */
-static void
-destroy_board_list()
+void
+missing_destroy_board_list(GList *list)
 {
   Board *board;
-
-  while(g_list_length(board_list)>0)
+  while(g_list_length(list)>0)
     {
-      board = g_list_nth_data(board_list, 0);
-      board_list = g_list_remove (board_list, board);
+      board = g_list_nth_data(list, 0);
+      list = g_list_remove (list, board);
       destroy_board(board);
     }
 }
@@ -715,14 +706,14 @@ static GcomprisConfCallback
 conf_ok(GHashTable *table)
 {
   if (!table){
-    if (gcomprisBoard)
+    if (gcomprisBoard_missing)
       pause_board(FALSE);
     return NULL;
   }
 
   g_hash_table_foreach(table, (GHFunc) save_table, NULL);
 
-  if (gcomprisBoard){
+  if (gcomprisBoard_missing){
     gc_locale_reset();
 
     GHashTable *config;
@@ -737,9 +728,9 @@ conf_ok(GHashTable *table)
     if (profile_conf)
       g_hash_table_destroy(config);
 
-    destroy_board_list();
+    missing_destroy_board_list(board_list);
 
-    init_xml(gcomprisBoard->level);
+    init_xml(gcomprisBoard_missing->level);
 
     missing_letter_next_level();
 
@@ -747,6 +738,7 @@ conf_ok(GHashTable *table)
 
     board_conf = NULL;
   profile_conf = NULL;
+  pause_board(FALSE);
 
   return NULL;
 }
@@ -758,7 +750,7 @@ config_start(GcomprisBoard *agcomprisBoard,
   board_conf = agcomprisBoard;
   profile_conf = aProfile;
 
-  if (gcomprisBoard)
+  if (gcomprisBoard_missing)
     pause_board(TRUE);
 
   gchar *label = g_strdup_printf(_("<b>%s</b> configuration\n for profile <b>%s</b>"),
@@ -776,7 +768,7 @@ config_start(GcomprisBoard *agcomprisBoard,
   gchar *locale = g_hash_table_lookup( config, "locale");
 
   gc_board_config_combo_locales(bconf, locale);
-
+  config_missing_letter(bconf);
 }
 
 
diff --git a/src/missing_letter-activity/missingletter.h b/src/missing_letter-activity/missingletter.h
new file mode 100644
index 0000000..c56d779
--- /dev/null
+++ b/src/missing_letter-activity/missingletter.h
@@ -0,0 +1,36 @@
+/* gcompris - missingletter.h
+ *
+ * Copyright (C) 2009 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MISSINGLETTER_H_
+#define _MISSINGLETTER_H_
+
+extern GcomprisBoard *gcomprisBoard_missing;
+
+gboolean              missing_read_xml_file(char *fname, GList**);
+void                  missing_destroy_board_list(GList *);
+
+#define MAX_PROPOSAL 6
+typedef struct _Board {
+  gchar *pixmapfile;
+  gchar *question;
+  gchar *answer;
+  gchar *text[MAX_PROPOSAL + 1];
+  guint solution;
+} Board;
+
+#endif
diff --git a/src/missing_letter-activity/missingletter_config.c b/src/missing_letter-activity/missingletter_config.c
new file mode 100644
index 0000000..6bfe3d9
--- /dev/null
+++ b/src/missing_letter-activity/missingletter_config.c
@@ -0,0 +1,592 @@
+#include "gcompris/gcompris.h"
+#include <string.h>
+
+#include "missingletter.h"
+
+
+typedef struct
+  {
+    GtkComboBox *combo_level;
+    GtkTreeView *view;
+
+    GtkFileChooserButton *pixmap;
+    GtkEntry *question, *answer, *choice;
+    gboolean changed;
+  } _config_missing;
+
+enum
+  {
+    QUESTION_COLUMN,
+    ANSWER_COLUMN,
+    CHOICE_COLUMN,
+    PIXMAP_COLUMN,
+    PIXBUF_COLUMN,
+    N_COLUMNS
+  };
+
+#define ICON_SIZE 32
+
+static void new_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+
+  ls = GTK_LIST_STORE(gtk_tree_view_get_model(u->view));
+  gtk_list_store_append(ls, &iter);
+  gtk_list_store_set(ls, &iter,
+                     QUESTION_COLUMN, "",
+                     ANSWER_COLUMN, "",
+                     CHOICE_COLUMN, "",
+                     PIXMAP_COLUMN, "",
+                     PIXBUF_COLUMN, NULL,
+                     -1);
+  GtkTreeSelection* sel = gtk_tree_view_get_selection(u->view);
+  gtk_tree_selection_select_iter(sel , &iter);
+}
+
+static void delete_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkTreeSelection *selection = gtk_tree_view_get_selection(u->view);
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+      u->changed = TRUE;
+    }
+}
+
+static gboolean valid_entry(gchar *question, gchar *answer,
+			    gchar *choice, gchar *pixmap)
+{
+  gboolean result=FALSE;
+  gchar **split;
+
+  if(choice && question && answer && pixmap &&
+     strlen(choice)==3 && strlen(question) && strlen(answer)
+     && strchr(question, '_'))
+    {
+      split = g_strsplit(question, "_", 2);
+      if(g_str_has_prefix(answer, split[0]) &&
+         g_str_has_suffix(answer, split[1]) &&
+         answer[strlen(split[0])] == choice[0])
+        result = TRUE;
+      g_strfreev(split);
+    }
+  return result;
+}
+
+static void apply_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  const gchar *question, *answer, *choice;
+  gchar *pixmap, *pixfile;
+  GtkTreeSelection *selection = gtk_tree_view_get_selection(u->view);
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  question = gtk_entry_get_text(u->question);
+  answer = gtk_entry_get_text(u->answer);
+  choice = gtk_entry_get_text(u->choice);
+  pixmap = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(u->pixmap));
+
+  if(valid_entry((gchar*)question, (gchar*) answer, (gchar*)choice, pixmap))
+    {
+      if (gtk_tree_selection_get_selected (selection, &model, &iter))
+        {
+          pixfile = gc_cache_import_pixmap(pixmap, "missingletter", 300, 300);
+          GdkPixbuf *pixbuf =
+	    gdk_pixbuf_new_from_file_at_size(pixmap, ICON_SIZE,
+					     ICON_SIZE, NULL);
+
+          gtk_list_store_set(GTK_LIST_STORE(model),&iter,
+                             QUESTION_COLUMN, question,
+                             ANSWER_COLUMN, answer,
+                             CHOICE_COLUMN, choice,
+                             PIXMAP_COLUMN, pixfile,
+                             PIXBUF_COLUMN, pixbuf,
+                             -1);
+          u->changed = TRUE;
+          g_free(pixfile);
+          g_object_unref(pixbuf);
+        }
+    }
+  g_free(pixmap);
+}
+
+static void up_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkTreeSelection *selection = gtk_tree_view_get_selection(u->view);
+  GtkTreeIter itera, iterb;
+  GtkTreeModel *model;
+  GtkTreePath *tpa, *tpb;
+  gchar *pa, *pb;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &itera))
+    {
+      pa = gtk_tree_model_get_string_from_iter(model, &itera);
+      tpa = gtk_tree_path_new_from_string(pa);
+      tpb = gtk_tree_path_copy(tpa);
+      gtk_tree_path_prev(tpb);
+      pb = gtk_tree_path_to_string(tpb);
+      gtk_tree_model_get_iter_from_string(model, &iterb, pb);
+      gtk_list_store_swap(GTK_LIST_STORE(model), &itera, &iterb);
+      gtk_tree_path_free(tpa);
+      gtk_tree_path_free(tpb);
+      g_free(pa);
+      g_free(pb);
+      u->changed = TRUE;
+    }
+}
+
+static void down_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkTreeSelection *selection = gtk_tree_view_get_selection(u->view);
+  GtkTreeIter itera, iterb;
+  GtkTreeModel *model;
+  GtkTreePath *tpa, *tpb;
+  gchar *pa, *pb;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &itera))
+    {
+      pa = gtk_tree_model_get_string_from_iter(model, &itera);
+      tpa = gtk_tree_path_new_from_string(pa);
+      tpb = gtk_tree_path_copy(tpa);
+      gtk_tree_path_next(tpb);
+      pb = gtk_tree_path_to_string(tpb);
+      if(gtk_tree_model_get_iter_from_string(model, &iterb, pb))
+        gtk_list_store_swap(GTK_LIST_STORE(model), &itera, &iterb);
+      gtk_tree_path_free(tpa);
+      gtk_tree_path_free(tpb);
+      g_free(pa);
+      g_free(pb);
+      u->changed = TRUE;
+    }
+}
+
+static gboolean _save(GtkTreeModel *model, GtkTreePath *path,
+                      GtkTreeIter *iter, gpointer data)
+{
+  gchar *question, *answer, *choice, *pixmap;
+  gchar *tmp = NULL;
+  xmlNodePtr root, node;
+
+  gtk_tree_model_get (model, iter,
+                      QUESTION_COLUMN, &question,
+                      ANSWER_COLUMN, &answer,
+                      CHOICE_COLUMN, &choice,
+                      PIXMAP_COLUMN, &pixmap,
+                      -1);
+  if(valid_entry(question, answer, choice, pixmap))
+    {
+      tmp = g_strdup_printf("%s/%s/%c/%c/%c",
+			    answer, question,
+			    choice[0], choice[1], choice[2]);
+
+      root =(xmlNodePtr)data;
+      node = xmlNewChild(root, NULL, BAD_CAST "Board", NULL);
+      xmlNewChild(node, NULL,BAD_CAST "pixmapfile", BAD_CAST pixmap);
+      xmlNewChild(node, NULL, BAD_CAST "data", BAD_CAST tmp);
+    }
+  g_free(tmp);
+  g_free(question);
+  g_free(answer);
+  g_free(choice);
+  return FALSE;
+}
+
+static void save_clicked(GtkButton *b, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkTreeModel *model;
+  gchar *filename;
+  xmlNodePtr root;
+  xmlDocPtr doc;
+  int level;
+
+  level = gtk_combo_box_get_active(u->combo_level)+1;
+  if(level==0)
+    return;
+  if(! u->changed)
+    return;
+  model = gtk_tree_view_get_model(u->view);
+  doc = xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
+  root = xmlNewNode(NULL, BAD_CAST "missing_letter");
+  xmlDocSetRootElement(doc,root);
+
+  gtk_tree_model_foreach(model, _save, root);
+
+  filename =
+    gc_file_find_absolute_writeable("%s/board%d.xml",
+				    gcomprisBoard_missing->boarddir, level);
+  if(xmlSaveFormatFileEnc(filename, doc, NULL, 1)<0)
+    g_warning("Fail to write %s", filename);
+  g_free(filename);
+  xmlFreeDoc(doc);
+  u->changed = FALSE;
+}
+
+static void level_changed(GtkComboBox *combo, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  GtkListStore *ls;
+  GtkTreeIter iter;
+  gchar *filename;
+  GList *list=NULL, *l;
+  int level, result;
+
+  level = gtk_combo_box_get_active(u->combo_level)+1;
+  if(level==0)
+    return;
+  if(u->changed)
+    {
+      GtkWidget *dialog;
+
+      dialog = gtk_dialog_new_with_buttons("Save changes ?",
+                                           NULL,
+                                           GTK_DIALOG_MODAL,
+                                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                           GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+                                           NULL);
+      result = gtk_dialog_run(GTK_DIALOG(dialog));
+      switch(result)
+        {
+        case GTK_RESPONSE_ACCEPT:
+          save_clicked(NULL, data);
+          break;
+        default:
+          u->changed=FALSE;
+          break;
+        }
+      gtk_widget_destroy (dialog);
+    }
+  ls = GTK_LIST_STORE(gtk_tree_view_get_model(u->view));
+  filename = gc_file_find_absolute("%s/board%d.xml",
+                                   gcomprisBoard_missing->boarddir, level);
+  missing_read_xml_file(filename,&list);
+  g_free(filename);
+  gtk_list_store_clear(ls);
+
+  for(l=list; l; l=l->next)
+    {
+      Board *b = l->data;
+      gchar *pixfile = gc_file_find_absolute(b->pixmapfile);
+      GdkPixbuf *pixbuf;
+      gchar tmp[MAX_PROPOSAL+1];
+      int i = 0;
+
+      pixbuf =
+	gdk_pixbuf_new_from_file_at_size(pixfile, ICON_SIZE, ICON_SIZE,
+					 NULL);
+
+      while(b->text[i])
+	{
+	  tmp[i] = b->text[i][0];
+	  i++;
+	}
+      tmp[i] = '\0';
+
+      gtk_list_store_append(ls, &iter);
+      gtk_list_store_set(ls, &iter,
+                         QUESTION_COLUMN, b->question,
+                         ANSWER_COLUMN, b->answer,
+                         CHOICE_COLUMN, tmp,
+                         PIXMAP_COLUMN, b->pixmapfile,
+                         PIXBUF_COLUMN, pixbuf,
+                         -1);
+      g_free(pixfile);
+      g_object_unref(pixbuf);
+    }
+  missing_destroy_board_list(list);
+}
+
+static void text_changed(GtkWidget *widget, gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+
+  const gchar *question, *answer, *choice;
+
+  question = gtk_entry_get_text(u->question);
+  answer = gtk_entry_get_text(u->answer);
+  choice = gtk_entry_get_text(u->choice);
+
+  if(widget == (GtkWidget*)u->answer)
+    {
+      if(g_str_has_prefix(answer,question))
+        {
+          gtk_entry_set_text(u->question,answer);
+        }
+    }
+  else if(widget ==(GtkWidget*) u->question)
+    {
+      if(strchr(question, '_'))
+        {
+          gchar ** split = g_strsplit(question,"_",2);
+          if(answer[strlen(split[0])]!= choice[0])
+            {
+              gchar *tmp;
+              tmp = g_new0(gchar, 4);
+              tmp[0]= answer[strlen(split[0])];
+              gtk_entry_set_text(u->choice, tmp);
+              g_free(tmp);
+            }
+          g_strfreev(split);
+        }
+    }
+}
+
+void selection_changed (GtkTreeSelection *selection,gpointer data)
+{
+  _config_missing *u = (_config_missing*)data;
+  gchar *question, *answer, *choice, *pixmap, *pixfile;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+    {
+      gtk_tree_model_get (model, &iter,
+                          QUESTION_COLUMN, &question,
+                          ANSWER_COLUMN, &answer,
+                          CHOICE_COLUMN, &choice,
+                          PIXMAP_COLUMN, &pixmap,
+                          -1);
+      gtk_entry_set_text(u->question, question);
+      gtk_entry_set_text(u->answer, answer);
+      gtk_entry_set_text(u->choice, choice);
+      pixfile = gc_file_find_absolute(pixmap);
+      gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(u->pixmap), pixfile);
+
+      g_free(question);
+      g_free(answer);
+      g_free(choice);
+      g_free(pixmap);
+      g_free(pixfile);
+    }
+}
+
+void destroy_conf_data(void *not_used, gpointer *data)
+{
+  g_free(data);
+}
+
+static void configure_colummns(GtkTreeView *treeview)
+{
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+
+  /* pixbuf column */
+  renderer = gtk_cell_renderer_pixbuf_new();
+  column = gtk_tree_view_column_new_with_attributes(_("Picture"),
+                                                    renderer, "pixbuf", PIXBUF_COLUMN, NULL);
+  gtk_tree_view_append_column(treeview, column);
+
+  /* Answer column */
+  renderer = gtk_cell_renderer_text_new();
+  column = gtk_tree_view_column_new_with_attributes(_("Answer"),
+                                                    renderer, "text", ANSWER_COLUMN, NULL);
+  gtk_tree_view_append_column(treeview, column);
+
+  /* Question column */
+  renderer = gtk_cell_renderer_text_new();
+  column = gtk_tree_view_column_new_with_attributes(_("Question"),
+                                                    renderer, "text", QUESTION_COLUMN, NULL);
+  gtk_tree_view_append_column(treeview, column);
+
+  /* Choice column */
+  renderer = gtk_cell_renderer_text_new();
+  column = gtk_tree_view_column_new_with_attributes(_("Choice"),
+                                                    renderer, "text", CHOICE_COLUMN, NULL);
+  gtk_tree_view_append_column(treeview, column);
+#if 0
+  /* pixmap column (debug only)*/
+  renderer = gtk_cell_renderer_text_new();
+  column = gtk_tree_view_column_new_with_attributes("File"),
+                                                    renderer, "text", PIXMAP_COLUMN, NULL);
+  gtk_tree_view_append_column(treeview, column);
+#endif
+}
+
+  void config_missing_letter(GcomprisBoardConf *config)
+  {
+    GtkWidget *frame, *view, *pixmap, *question, *answer, *choice;
+    GtkWidget *level, *vbox, *hbox, *label;
+    GtkWidget *bbox, *button, *table;
+    GtkFileFilter *file_filter;
+    _config_missing *conf_data;
+    int i;
+
+    conf_data = g_new0(_config_missing,1);
+
+    /* frame */
+    frame = gtk_frame_new("");
+    gtk_widget_show(frame);
+    gtk_box_pack_start(GTK_BOX(config->main_conf_box), frame, TRUE, TRUE, 8);
+
+    vbox = gtk_vbox_new(FALSE, 8);
+    gtk_widget_show(vbox);
+    gtk_container_add(GTK_CONTAINER(frame), vbox);
+
+    /* hbox */
+    hbox = gtk_hbox_new(FALSE, 8);
+    gtk_widget_show(hbox);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 8);
+
+    /* combo level */
+    label = gtk_label_new(_("Level:"));
+    gtk_widget_show(label);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 8);
+
+    level = gtk_combo_box_new_text();
+    for(i=1; i< gcomprisBoard_missing->maxlevel; i++)
+      {
+        gchar *tmp;
+        tmp = g_strdup_printf(_("Level %d"), i);
+        gtk_combo_box_append_text(GTK_COMBO_BOX(level), tmp);
+        g_free(tmp);
+      }
+    gtk_widget_show(level);
+    gtk_box_pack_start(GTK_BOX(hbox), level, FALSE, FALSE, 8);
+
+    /* list view */
+    GtkListStore *list = gtk_list_store_new(N_COLUMNS,
+                                            G_TYPE_STRING,   /*Question */
+                                            G_TYPE_STRING,   /* Answer */
+                                            G_TYPE_STRING,   /* Choice */
+                                            G_TYPE_STRING,   /* pixmap */
+                                            GDK_TYPE_PIXBUF  /* pixbuf */
+                                            );
+
+    view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list));
+    configure_colummns(GTK_TREE_VIEW(view));
+    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
+    gtk_tree_view_set_search_column (GTK_TREE_VIEW (view), ANSWER_COLUMN);
+    gtk_widget_set_size_request(view, -1, 200);
+    gtk_widget_show(view);
+
+    GtkScrolledWindow *scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL,NULL));
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+    gtk_widget_show(GTK_WIDGET(scroll));
+    gtk_container_add(GTK_CONTAINER(scroll), view);
+
+    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(scroll), TRUE, TRUE, 10);
+
+    /* button box */
+    bbox = gtk_hbutton_box_new();
+    gtk_widget_show(bbox);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 8);
+    button = gtk_button_new_from_stock(GTK_STOCK_NEW);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(new_clicked), (gpointer) conf_data);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(delete_clicked), (gpointer) conf_data);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_APPLY);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(apply_clicked), (gpointer) conf_data);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(up_clicked), (gpointer) conf_data);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(down_clicked), (gpointer) conf_data);
+
+    button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(bbox), button);
+    g_signal_connect(G_OBJECT(button), "clicked",
+                     G_CALLBACK(save_clicked), (gpointer) conf_data);
+
+    /* table */
+    table = gtk_table_new(2, 4, FALSE);
+    gtk_widget_show(table);
+    gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 8);
+
+    /* answer */
+    label = gtk_label_new(_("Answer"));
+    gtk_widget_show(label);
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+
+    answer = gtk_entry_new();
+    gtk_widget_show(answer);
+    gtk_table_attach_defaults(GTK_TABLE(table), answer, 1, 2, 0, 1);
+
+    /* pixmap */
+    label = gtk_label_new(_("Picture"));
+    gtk_widget_show(label);
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 0, 1);
+
+    pixmap = gtk_file_chooser_button_new(_("Filename:"),
+                                         GTK_FILE_CHOOSER_ACTION_OPEN);
+
+    file_filter = gtk_file_filter_new();
+    gtk_file_filter_add_pixbuf_formats(file_filter);
+    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(pixmap), file_filter);
+    gtk_widget_show(pixmap);
+    gtk_table_attach_defaults(GTK_TABLE(table), pixmap, 3, 4, 0, 1);
+
+    /* question */
+    label = gtk_label_new(_("Question"));
+    gtk_widget_show(label);
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+
+    question = gtk_entry_new();
+    gtk_widget_show(question);
+    gtk_table_attach_defaults(GTK_TABLE(table), question, 1, 2, 1, 2);
+
+    /* choice */
+    label = gtk_label_new(_("Choice"));
+    gtk_widget_show(label);
+    gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 1, 2);
+
+    choice = gtk_entry_new();
+    gtk_entry_set_max_length(GTK_ENTRY(choice), 3);
+    gtk_widget_show(choice);
+    gtk_table_attach_defaults(GTK_TABLE(table), choice, 3, 4, 1, 2);
+
+    conf_data -> combo_level = GTK_COMBO_BOX(level);
+    conf_data -> view = GTK_TREE_VIEW(view);
+    conf_data -> pixmap = GTK_FILE_CHOOSER_BUTTON(pixmap);
+    conf_data -> question = GTK_ENTRY(question);
+    conf_data -> answer = GTK_ENTRY(answer);
+    conf_data -> choice = GTK_ENTRY(choice);
+
+    GtkTreeSelection *selection;
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+    gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+    g_signal_connect(G_OBJECT(selection),
+                     "changed",
+                     G_CALLBACK(selection_changed),
+                     (gpointer) conf_data);
+    g_signal_connect(G_OBJECT(frame), "destroy",
+                     G_CALLBACK(destroy_conf_data), (gpointer) conf_data);
+    g_signal_connect(G_OBJECT(level), "changed",
+                     G_CALLBACK(level_changed), (gpointer) conf_data);
+    g_signal_connect(G_OBJECT(question), "changed",
+                     G_CALLBACK(text_changed), (gpointer) conf_data);
+    g_signal_connect(G_OBJECT(answer), "changed",
+                     G_CALLBACK(text_changed), (gpointer) conf_data);
+
+    gtk_combo_box_set_active(GTK_COMBO_BOX(level), 0);
+  }
+
diff --git a/src/readingh-activity/reading.c b/src/readingh-activity/reading.c
index ac4fafa..8e28a73 100644
--- a/src/readingh-activity/reading.c
+++ b/src/readingh-activity/reading.c
@@ -451,7 +451,15 @@ reading_create_item(GooCanvasItem *parent)
       word = g_strdup(textToFind);
     }
 
-  g_assert(word!=NULL);
+  if(word==NULL)
+  {
+  	gc_dialog(_("Skip this level. Not enough word in the list !"),
+		(DialogBoxCallBack)reading_next_level);
+	gcomprisBoard->level++;
+	if(gcomprisBoard->level>gcomprisBoard->maxlevel)
+	  gcomprisBoard->level = gcomprisBoard->maxlevel;
+	return FALSE;
+  }
 
   if(textToFindIndex>=0)
     textToFindIndex--;
@@ -780,6 +788,7 @@ static gchar *
 get_random_word(const gchar* except)
 {
   gchar *word;
+  int count=0;
 
   word = gc_wordlist_random_word_get(gc_wordlist, gcomprisBoard->level);
 
@@ -787,6 +796,12 @@ get_random_word(const gchar* except)
     while(strcmp(except, word)==0)
       {
 	g_free(word);
+
+	if(count++>100)
+	{
+		word = NULL;
+		break;
+	}
 	word = gc_wordlist_random_word_get(gc_wordlist, gcomprisBoard->level);
       }
 



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