[gcompris] Added support for multigraph in click_on_letter



commit 99e47abd99dea711d251cd2dfcb09dfc92be11c9
Author: Bruno Coudoin <bruno coudoin gcompris net>
Date:   Wed Feb 12 18:38:26 2014 +0100

    Added support for multigraph in click_on_letter
    
    The impact for translators is that the alphabet now is:
    a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z
    
    instead of:
    abcdefghijklmnopqrstuvwxyz
    
    and in te destop file for le levels, letters get separated by blank
    spaces like this:
    Questions=a e i o u y
    Answers=a e i o u y c s
    
    Added a translator comment to the relevant string.
    
    This meant a complete rewrite of the data structures, both for the
    string in the .po catalog and the desktop file. Using gchar** instead
    of gchar* for the alphabet, and for the questions/anwsers use GSList
    for dealing with the dynamic length without running into memory
    allocation troubles.
    
    Improved shuffling algo where it doesn't need any reordering just to
    get at some data structure.
    
    Switched the question and answer columns in the config, because the
    order was confusing (having the answers before the questions).

 src/click_on_letter-activity/click_on_letter.c     |  704 +++++++++++++-------
 .../resources/click_on_letter/default-en.desktop   |   45 +-
 2 files changed, 471 insertions(+), 278 deletions(-)
---
diff --git a/src/click_on_letter-activity/click_on_letter.c b/src/click_on_letter-activity/click_on_letter.c
index c41e3bb..f36e2cf 100644
--- a/src/click_on_letter-activity/click_on_letter.c
+++ b/src/click_on_letter-activity/click_on_letter.c
@@ -19,12 +19,11 @@
 
 #include <glib.h>
 #include <glib/gstdio.h>
+#include <glib/gi18n.h>
 #include <string.h>
 #include <stdlib.h> /* atoi */
 #include "gcompris/gcompris.h"
 
-
-
 #define SOUNDLISTFILE PACKAGE
 
 static GcomprisBoard *gcomprisBoard = NULL;
@@ -44,21 +43,11 @@ static void          process_ok(void);
 static void             highlight_selected(GooCanvasItem *);
 static void             game_won(void);
 static void             repeat(void);
+
 static void             config_start(GcomprisBoard *agcomprisBoard,
                                      GcomprisProfile *aProfile);
 static void             config_stop(void);
-
-/* The data structure of a level */
-typedef struct
-{
-  guint  level;
-  gchar *questions;
-  gchar *answers;
-} Level;
-static GArray *levels = NULL;
-static gchar **questions;
-static gchar **answers;
-
+static gchar             *levels_to_desktop(void);
 static void             load_datafile();
 static void             clear_levels();
 
@@ -80,6 +69,35 @@ enum
 #define MAX_N_LETTER_LINE 4
 #define MAX_N_ANSWER      (N_LETTER_PER_LINE * MAX_N_LETTER_LINE)
 
+
+static gboolean uppercase_only;
+
+/* length of the alphabet*/
+static guint alphlen;
+/* alphabet storage*/
+static gchar **letterlist=NULL;
+
+/* The data structure of a level */
+typedef struct
+{
+  guint  level;
+  GSList *questions;
+  GSList *answers;
+} Level;
+
+static GArray *levels = NULL;
+static gchar *right_letter = NULL;
+
+/* For multigraph keyboard input*/
+static gchar *answerletter = "";
+
+static void              create_levels_from_alphabet(void);
+static void              get_alphabet(void);
+
+static GooCanvasItem    *click_on_letter_create_item(GooCanvasItem *parent);
+static void             click_on_letter_destroy_all_items(void);
+static void             click_on_letter_next_level(void);
+
 #define NOT_OK         0
 #define OK             1
 #define OK_NO_INIT     2
@@ -95,22 +113,10 @@ static RsvgDimensionData carriage_svg_dimension;
 static RsvgHandle *cloud_svg_handle;
 static RsvgDimensionData cloud_svg_dimension;
 
-static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent);
-
-static void click_on_letter_destroy_all_items(void);
-static void click_on_letter_next_level(void);
-static gint item_event(GooCanvasItem *item, GooCanvasItem *target,
-                      GdkEvent *event, gpointer data);
-static guint sounds_are_fine();
-
-static int n_answer;
-static gchar *right_letter = NULL;
-
-static gchar *alphabet;
-
-static void sound_played(gchar *file);
-
-static gboolean uppercase_only;
+static gint             item_event(GooCanvasItem *item, GooCanvasItem *target,
+                                   GdkEvent *event, gpointer data);
+static guint            sounds_are_fine();
+static void             sound_played(gchar *file);
 
 /* Description of this plugin */
 static BoardPlugin menu_bp =
@@ -149,7 +155,7 @@ GET_BPLUGIN_INFO(click_on_letter)
  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
  *
  */
-     static void pause_board (gboolean pause)
+static void pause_board (gboolean pause)
 {
   if(gcomprisBoard==NULL)
     return;
@@ -171,6 +177,8 @@ static void start_board (GcomprisBoard *agcomprisBoard)
 
   board_paused = TRUE;
 
+  get_alphabet(); /* read and init letters */
+
   gc_locale_set(g_hash_table_lookup( config, "locale_sound"));
 
   g_hash_table_destroy(config);
@@ -183,10 +191,7 @@ static void start_board (GcomprisBoard *agcomprisBoard)
     {
       gcomprisBoard=agcomprisBoard;
 
-      if ( gcomprisBoard->mode && g_ascii_strcasecmp(gcomprisBoard->mode, "uppercase")==0 )
-       uppercase_only = TRUE;
-      else
-       uppercase_only = FALSE;
+      uppercase_only =  ( gcomprisBoard->mode && g_ascii_strcasecmp(gcomprisBoard->mode, "uppercase")==0 );
 
       gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas),
                              "click_on_letter/background.svgz");
@@ -232,7 +237,7 @@ static void start_board (GcomprisBoard *agcomprisBoard)
 }
 
 static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str) {
-  gint length_passed, i;
+  gint length_passed, length_right, i;
 
   if(!gcomprisBoard)
     return FALSE;
@@ -248,23 +253,39 @@ static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str) {
     string_passed = preedit_str;
 
   length_passed = g_utf8_strlen(string_passed, -1);
+  length_right = g_utf8_strlen(answerletter, -1);
 
   for (i=0; i < length_passed; i++){
     gunichar ckey = \
       g_unichar_tolower( g_utf8_get_char (string_passed) );
     gunichar cright = \
-      g_unichar_tolower( g_utf8_get_char (right_letter) );
+      g_unichar_tolower( g_utf8_get_char (answerletter) );
 
-    if (ckey == cright){
+    if (ckey == cright && length_passed == length_right){
+      gc_sound_play_ogg ("sounds/flip.wav", NULL);
       gamewon = TRUE;
       process_ok();
       gc_im_reset();
       return TRUE;
     }
-
+    else if(ckey != cright)
+    {
+        gc_sound_play_ogg ("sounds/crash.wav", NULL);
+
+        /*
+         * Todo: It would be nice to add some color here to help the child
+        if(g_utf8_strlen(answerletter, -1) != g_utf8_strlen(right_letter, -1))
+            printf("Some more help!\n");
+         */
+        return FALSE;
+    }
+    else
+    {
+        gc_sound_play_ogg ("sounds/flip.wav", NULL);
+        ++answerletter;
+    }
     string_passed = g_utf8_next_char (string_passed);
   }
-
   return TRUE;
 }
 
@@ -276,9 +297,6 @@ static void end_board ()
       pause_board(TRUE);
       gc_score_end();
       click_on_letter_destroy_all_items();
-      g_strfreev(answers);
-      g_strfreev(questions);
-
       g_object_unref(carriage_svg_handle);
       g_object_unref(cloud_svg_handle);
       clear_levels();
@@ -286,12 +304,12 @@ static void end_board ()
   gc_locale_set( NULL );
   gcomprisBoard = NULL;
   gc_sound_bg_resume();
+  g_strfreev(letterlist);
 }
 
 /* ======================================= */
 static void set_level (guint level)
 {
-
   if(gcomprisBoard!=NULL)
     {
       gcomprisBoard->level=level;
@@ -339,7 +357,7 @@ static gboolean _repeat ()
 
   if(right_letter_ogg) {
 
-    /* Let's check the file exist to be abble to return FALSE */
+    /* Let's check the file exist to be able to return FALSE */
     gchar *absolute_file = gc_file_find_absolute(right_letter_ogg, NULL);
     if (absolute_file)
       {
@@ -362,15 +380,6 @@ static void repeat ()
     }
 }
 
-static gchar *
-get_alphabet()
-{
-  gchar *alphabet = gettext( _("abcdefghijklmnopqrstuvwxyz") );
-  /* TRANSLATORS: Put here the alphabet in your language */
-  g_assert(g_utf8_validate(alphabet, -1, NULL)); // require by all utf8-functions
-  return alphabet;
-}
-
 static guint sounds_are_fine()
 {
   char *letter_str;
@@ -390,10 +399,8 @@ static guint sounds_are_fine()
       return(OK_NO_INIT);
     }
 
-  alphabet = get_alphabet();
-
   gchar *letter = g_new0(gchar, 8);
-  g_unichar_to_utf8(g_utf8_get_char(alphabet), letter);
+  g_unichar_to_utf8(g_utf8_get_char(letterlist[1]), letter);
   letter_str = gc_sound_alphabet(letter);
   g_free(letter);
 
@@ -402,7 +409,7 @@ static guint sounds_are_fine()
 
   if (!str2)
     {
-      gchar *msg2 = g_strdup_printf( _("Error: this activity requires that you first install\nthe packages 
with GCompris voices for the %s locale."),
+      gchar *msg2 = g_strdup_printf(_("Error: this activity requires that you first install\nthe packages 
with GCompris voices for the %s locale."),
                                     gc_locale_get_name( gc_locale_get() ) );
       gchar *msg = g_strconcat(msg2, "\n", text_mode_str, NULL);
       g_free(msg2);
@@ -440,82 +447,98 @@ static void click_on_letter_destroy_all_items()
     goo_canvas_item_remove(boardRootItem);
 
   boardRootItem = NULL;
-
-  g_free (right_letter);
   right_letter = NULL;
 }
 
 /*
- * given an utf8 string, return an array of pointers to string
- * where each string is a char from the original string.
- * All the char in the returned array are shuffled
- * free the result with g_strfreev()
+ * Helper function for randomising letter list and answers
  */
-static gchar
-**shuffle_utf8(gchar *string)
+static void make_random_indices(guint *indices, guint length)
 {
-  guint n_letters = g_utf8_strlen (string, -1);
-  gchar **result = (gchar **) g_new( gpointer, n_letters + 1);
   /* Randomize the list, create a random index first */
-  int random[n_letters];
-  int i;
-  for ( i = 0 ; i < n_letters ; i++) {
-    random[i] = i;
+  guint i;
+  for ( i = 0 ; i < length ; i++) {
+    indices[i] = i;
   }
 
-  for ( i = 0 ; i < n_letters ; i++) {
-    int swap_index = g_random_int_range(0, n_letters);
-    int save = random[i];
-    random[i] = random[swap_index];
-    random[swap_index] = save;
+  for ( i = 0 ; i < length ; i++) {
+    int swap_index = g_random_int_range(0, length);
+    int save = indices[i];
+    indices[i] = indices[swap_index];
+    indices[swap_index] = save;
+  }
+}
+
+
+/* Helper function for creating levels.
+ * For selecting questions and randomizing the order of the answers,
+ * only pointers to elements in the letterlist will be manipulated.
+ * Avoids having to copy array elements
+ */
+static void
+shuffle_pointers(gchar **pointers, guint length)
+{
+  /* Randomize the list, create a random index first */
+  guint random[length];
+  make_random_indices(random,length);
+  guint i;
+  /* Now use the index to swap pointer */
+  for ( i = 0 ; i < length-1; i++) {
+    char *savearray = pointers[random[i]];
+    pointers[random[i]] = pointers[random[i+1]];
+    pointers[random[i+1]] = savearray;
   }
+}
 
-  /* Now use the index to swap letter */
-  for ( i = 0 ; i < n_letters ; i++) {
-    gchar *copy_from = g_utf8_offset_to_pointer(string, random[i]);
-    gchar *copy_to = g_utf8_offset_to_pointer(string, random[i] + 1);
-    result[i] = g_strndup(copy_from, copy_to - copy_from);
+
+/*
+ * Helper function to randomize a level
+ * Todo - it would be nice to change to change to a more efficient
+ * data structure.  GPtrArray?
+ */
+static GSList *randomize_list(GSList *list)
+{
+  guint length = g_slist_length (list);
+  guint indices[length];
+  make_random_indices(indices, length);
+  GSList *result = NULL;
+  guint i;
+  for (i=0; i<length;++i)
+  {
+      result = g_slist_append (result, g_slist_nth_data(list,indices[i]));
   }
-  result[i] = NULL;
   return result;
 }
 
+
 /* ==================================== */
 static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent)
 {
-
   int xOffset, yOffset, i;
+  Level *level = &g_array_index (levels, Level, gcomprisBoard->level - 1);
 
   if (gcomprisBoard->sublevel == 1)
     {
-      Level *level = &g_array_index (levels, Level, gcomprisBoard->level - 1);
-      n_answer = g_utf8_strlen (level->answers, -1);
-      g_assert( n_answer <= MAX_N_ANSWER );
+      guint n_answer = g_slist_length(level->answers);
+      guint n_questions = g_slist_length(level->questions);
+      g_assert(0 < n_answer && n_answer <= MAX_N_ANSWER );
+      g_assert( n_answer >=  n_questions);
+      g_message("New level: %d, Sublevels: %d",gcomprisBoard->level - 1,n_questions);
 
-      if ( uppercase_only )
-       {
-         gchar *answers_up = g_utf8_strup( level->answers, -1 );
-         gchar *questions_up = g_utf8_strup( level->questions, -1 );
-         answers = shuffle_utf8(answers_up);
-         questions = shuffle_utf8(questions_up);
-         g_free(answers_up);
-         g_free(questions_up);
-       }
-      else
-       {
-         answers = shuffle_utf8(level->answers);
-         questions = shuffle_utf8(level->questions);
-       }
+      /* Randomize questions and answers each time a level is called*/
+      level->questions = randomize_list(level->questions);
+      level->answers = randomize_list(level->answers);
 
       /* Go to next level after this number of 'play' */
-      gcomprisBoard->number_of_sublevel = g_utf8_strlen (level->questions, -1);
+          gcomprisBoard->number_of_sublevel = n_questions;
     }
-  right_letter = g_utf8_strdown(questions[gcomprisBoard->sublevel - 1], -1);
-
+  right_letter =  g_slist_nth_data(level->questions,gcomprisBoard->sublevel - 1);
+  /* Display in uppercase? */
+  if(uppercase_only) right_letter=g_utf8_strup(right_letter,-1);
+  answerletter = right_letter;
 
   boardRootItem = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
                                        NULL);
-
   if ( ! _repeat() )
   {
     /* Sound was not played, display the letter to find instead */
@@ -535,7 +558,7 @@ static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent)
                         "radius-y", (double) 10,
                         NULL);
     goo_canvas_text_new (boardRootItem,
-                        questions[gcomprisBoard->sublevel - 1],
+                        right_letter,
                         (double) x + width / 2,
                         (double) y + height / 2,
                         -1,
@@ -554,7 +577,9 @@ static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent)
   RsvgHandle *svg_handle= carriage_svg_handle;
   RsvgDimensionData svg_dimension = carriage_svg_dimension;
 
-  for (i = 0; i< n_answer; i++) {
+  GSList *answerpointer = level->answers;
+
+  for (i = 0; answerpointer; i++) {
 
     if ( i > 0 && i % N_LETTER_PER_LINE == 0 )
       {
@@ -576,10 +601,13 @@ static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent)
                               xOffset,
                               yOffset);
 
+    gchar *answer = (gchar *) answerpointer->data;
+    /* Display in uppercase? */
+    if(uppercase_only) answer=g_utf8_strup(answer,-1);
 
     GooCanvasItem *text_item = \
       goo_canvas_text_new (boardRootItem,
-                          answers[i],
+                          answer,
                           (double) xOffset + svg_dimension.width / 2 + text_gap_x,
                           (double) yOffset + svg_dimension.height / 2 + text_gap_y,
                           -1,
@@ -591,15 +619,18 @@ static GooCanvasItem *click_on_letter_create_item(GooCanvasItem *parent)
     xOffset += HORIZONTAL_SEPARATION + svg_dimension.width;
 
     g_signal_connect(text_item, "button_press_event",
-                    (GCallback) item_event, answers[i]);
+                    (GCallback) item_event, answer);
     g_signal_connect(button_item, "button_press_event",
-                    (GCallback) item_event, answers[i]);
+                    (GCallback) item_event, answer);
     gc_item_focus_init(text_item, button_item);
     gc_item_focus_init(button_item, NULL);
     g_object_set_data(G_OBJECT(button_item), "button_item", button_item);
     g_object_set_data(G_OBJECT(text_item), "button_item", button_item);
-  }
 
+    /* Move to next letter */
+    answerpointer = g_slist_next(answerpointer);
+  }
+  g_slist_free (answerpointer);
   return NULL;
 }
 /* ==================================== */
@@ -612,7 +643,9 @@ static void game_won()
     gcomprisBoard->sublevel=1;
     gcomprisBoard->level++;
     if(gcomprisBoard->level > gcomprisBoard->maxlevel)
+    {
       gcomprisBoard->level = gcomprisBoard->maxlevel;
+    }
   }
   click_on_letter_next_level();
 }
@@ -635,7 +668,7 @@ item_event(GooCanvasItem *item, GooCanvasItem *target,
   if(board_paused)
     return FALSE;
 
-  gchar *answer = g_utf8_strdown ( (gchar*)data , -1 );
+  gchar *answer = (gchar*)data;
 
   switch (event->type)
     {
@@ -655,8 +688,6 @@ item_event(GooCanvasItem *item, GooCanvasItem *target,
     default:
       break;
     }
-
-  g_free(answer);
   return FALSE;
 }
 /* ==================================== */
@@ -676,11 +707,52 @@ static void highlight_selected(GooCanvasItem * item) {
 
 }
 
+
 /*
- * Management of Data File (Desktop style)
+ * Helper function to display contents of questions/answers
+ * in tree model and desktop file
+ */
+static gchar *list_to_string(GSList *list)
+{
+  gchar *result ="";
+  if(list != NULL)
+  {
+      GSList *temppointer = (GSList *)list;
+      result = g_strdup_printf("%s", (gchar *) temppointer->data);
+      while ((temppointer = g_slist_next(temppointer)))
+      {
+          result = g_strdup_printf("%s %s",result, (gchar *) temppointer->data);
+      }
+      g_slist_free (temppointer);
+
+      /* Display in uppercase? */
+      if(uppercase_only) result=g_utf8_strup(result,-1);
+  }
+  return result;
+}
+
+/*
+ * Helper function to get contents of questions/answers
+ * from tree model or desktop file
  */
+static GSList *string_to_list(gchar *string)
+{
+  gchar **strings = g_strsplit (string," ",-1);
+  GSList *result = NULL;
+  guint i = -1;
+  while (strings[++i])
+  {
+      g_strstrip(strings[i]);
+      if(g_utf8_strlen(strings[i],-1) > 0 && g_utf8_validate(strings[i], -1, NULL))
+        result = g_slist_append (result, strings[i]);
+  }
+  return result;
+}
 
-static void load_desktop_datafile(gchar *filename)
+/*
+ * Management of Data File (Desktop style)
+ */
+static gboolean load_desktop_datafile(gchar *filename)
 {
   GKeyFile *keyfile = g_key_file_new ();
   GError *error = NULL;
@@ -690,98 +762,172 @@ static void load_desktop_datafile(gchar *filename)
                                    &error)  ) {
     if (error)
       g_error ("%s", error->message);
-    return;
+    return FALSE;
   }
 
   gsize n_level;
   gchar **groups = g_key_file_get_groups (keyfile, &n_level);
+
+  if (!groups[0])
+  {
+      g_warning ("Desktop file contains no levels");
+      return FALSE;
+  }
+
+  gchar *questions ="";
+  gchar *answers ="";
   int i;
   for (i=0; i<n_level; i++)
-    {
+  {
       Level level;
       level.level = i + 1;
-      level.questions = g_key_file_get_string (keyfile, groups[i],
-                                             "Questions", NULL);
-      level.answers = g_key_file_get_string (keyfile, groups[i],
-                                           "Answers", NULL);
-      g_array_append_vals (levels, &level, 1);
-    }
+      level.questions=NULL;
+      level.answers=NULL;
+      error = NULL;
+      questions = g_key_file_get_string(keyfile, groups[i], "Questions", &error);
+      if (error)
+      {
+        g_warning ("%s", error->message);
+        break;
+      }
+      error = NULL;
+      answers = g_key_file_get_string(keyfile, groups[i], "Answers", &error);
+      if (error)
+      {
+        g_warning ("%s", error->message);
+        break;
+      }
+      if(!g_utf8_validate(questions, -1, NULL) || !g_utf8_validate(answers, -1, NULL))
+      {
+          g_warning ("Level %d contains garbage. Q: %s - A: %s",i+1,questions, answers);
+          break;
+      }
+
+      if(questions && answers)
+      {
+        level.questions=string_to_list(questions);
+        level.answers=string_to_list(answers);
+        g_array_append_vals (levels, &level, 1);
+      }
+      else
+      {
+          g_warning ("Error qetting questions and answers for level %d",i+1);
+          break;
+      }
+  }
 
+  g_free(questions);
+  g_free(answers);
   g_strfreev(groups);
   gcomprisBoard->maxlevel = n_level;
+  return (i>0);
 }
 
-static void create_level_from_alphabet(gchar *alphabet)
+/*
+ * Reads multigraph characters from PO file into array.
+ */
+static void
+get_alphabet()
 {
-  guint alphabet_len = g_utf8_strlen (alphabet, -1);
+  g_message("Getting alphabet");
+  gchar *alphabet = _("a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z");
+  /* TRANSLATORS: Put here the alphabet in your language, letters separated by: /
+   * Supports multigraphs, e.g. /sh/ or /sch/ gets treated as one letter */
+  g_assert(g_utf8_validate(alphabet, -1, NULL)); // require by all utf8-functions
+
+  /* fill letter storage */
+  letterlist = g_strsplit (alphabet,"/",-1);
+  guint i =-1;
+  while (letterlist[++i])
+  {
+      ;
+  }
+  alphlen=i;
+}
+
 
+/*
+ * Read random letters from letterlist and create the levels
+ */
+static void create_levels_from_alphabet()
+{
   guint level_i = 0;
-  guint i = 0;
-  while ( TRUE )
-    {
+  guint n_questions = 0;
+  while ( n_questions < alphlen && n_questions < MAX_N_ANSWER)
+  {
       Level level;
       level.level = ++level_i;
+      n_questions = level_i + 5;
 
-      guint n_questions = level_i + 3;
+      /* Make sure levels fit on screen */
+      n_questions = (n_questions > MAX_N_ANSWER) ? MAX_N_ANSWER : n_questions;
+      n_questions = (n_questions > alphlen) ? alphlen : n_questions;
 
-      if ( i + n_questions > alphabet_len)
-       {
-         n_questions = MAX_N_ANSWER;
-         i = alphabet_len - n_questions;
-       }
-      gchar *copy_from = g_utf8_offset_to_pointer(alphabet, i);
-      gchar *copy_to = g_utf8_offset_to_pointer(alphabet, i + n_questions);
+      /* get random pointers to multigraphs */
+      guint j;
+      gchar *randomlist[alphlen];
+      for (j=0; j<alphlen ; ++j)
+      {
+            randomlist[j]=letterlist[j];
+      }
+      shuffle_pointers(randomlist, alphlen);
 
-      i += n_questions;
+      level.questions=NULL;
+      level.answers=NULL;
 
-      level.questions = g_strndup(copy_from, copy_to - copy_from);
-      level.answers = g_strdup(level.questions);
+      for(j=0;j<n_questions;++j)
+      {
+        level.questions=g_slist_append (level.questions,randomlist[j]);
+        level.answers=g_slist_append (level.answers,randomlist[j]);
+      }
+      /* add the new level to the game */
       g_array_append_vals (levels, &level, 1);
-
-      if ( i >= alphabet_len)
-       break;
-    }
-
+      g_message("Click_on_letter: Created %d questions for level %d\n",n_questions,level_i);
+  }
   gcomprisBoard->maxlevel = level_i;
-
 }
 
 /*
  * Load a desktop style data file and create the levels array
  */
 static void load_datafile() {
-  gchar *filename = gc_file_find_absolute("click_on_letter/default-$LOCALE.desktop");
 
-  clear_levels();
+    clear_levels();
 
-  // Reset the alphabet to match the current locale
-  alphabet = get_alphabet();
+    /* Reset the alphabet to match the current locale */
+    get_alphabet();
 
-  /* create level array */
-  levels = g_array_sized_new (FALSE, FALSE, sizeof (Level), 10);
+    /* create level array */
+    levels = g_array_sized_new (FALSE, FALSE, sizeof (Level), 10);
 
-  if ( filename )
+    gboolean fileloaded = FALSE;
+    gchar *filename = "";
+    /* Try to get special file for uppercase mode */
+    if(uppercase_only)
     {
-      load_desktop_datafile(filename);
+        filename = gc_file_find_absolute("click_on_letter/upper-$LOCALE.desktop");
+        if ( filename )
+        {
+          fileloaded = load_desktop_datafile(filename);
+        }
     }
-  else if ( ! filename && alphabet[0] == 'a')
+    /* If no uppercase file, not not in uppercase mode: try to get normal file */
+    if(!fileloaded)
     {
-      /* This is a LATIN based language, let's fallback to english */
-      filename = gc_file_find_absolute("click_on_letter/default-en.desktop");
+      filename = gc_file_find_absolute("click_on_letter/default-$LOCALE.desktop");
 
-      if( filename )
-       load_desktop_datafile(filename);
-      else
-       // Should not happens but in case let's create the level for LATIN
-       create_level_from_alphabet(alphabet);
+      if ( filename )
+      {
+          fileloaded = load_desktop_datafile(filename);
+      }
     }
-  else
+    if (!fileloaded)
     {
-      // No data file and no latin character set
-      create_level_from_alphabet(alphabet);
+      /* No valid data file for the locale:
+       * create random levels from alphabet in PO file */
+      create_levels_from_alphabet();
     }
-
-  g_free(filename);
+    g_free(filename);
 }
 
 /**
@@ -796,8 +942,8 @@ static gchar *levels_to_desktop() {
     {
       Level level = g_array_index (levels, Level, i);
       gchar *group = g_strdup_printf("%d", level.level);
-      g_key_file_set_string(keyfile, group, "Questions", level.questions);
-      g_key_file_set_string(keyfile, group, "Answers", level.answers);
+      g_key_file_set_string(keyfile, group, "Questions", list_to_string(level.questions));
+      g_key_file_set_string(keyfile, group, "Answers", list_to_string(level.answers));
       g_free(group);
     }
 
@@ -829,19 +975,26 @@ static GHFunc save_table (gpointer key,
   return NULL;
 }
 
+
+
 static gboolean _save_level_from_model(GtkTreeModel *model, GtkTreePath *path,
                                       GtkTreeIter *iter, gpointer data)
 {
   Level level;
+  gchar *answers = "";
+  gchar *questions = "";
 
   gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
                       LEVEL_COLUMN, &level.level,
-                      ANSWER_COLUMN, &level.answers,
-                      QUESTION_COLUMN, &level.questions,
+                      ANSWER_COLUMN, &answers,
+                      QUESTION_COLUMN, &questions,
                       -1);
+  level.answers = string_to_list(answers);
+  level.questions = string_to_list(questions);
   g_array_append_vals (levels, &level, 1);
   gcomprisBoard->maxlevel = level.level;
-
+  g_free(questions);
+  g_free(answers);
   return FALSE;
 }
 
@@ -850,7 +1003,6 @@ static void create_levels_from_config_model()
 {
   GtkTreeIter iter;
   gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter );
-
   clear_levels();
   levels = g_array_sized_new (FALSE, FALSE, sizeof (Level), 10);
   gtk_tree_model_foreach(GTK_TREE_MODEL(model), _save_level_from_model, NULL);
@@ -863,14 +1015,15 @@ load_model_from_levels(GtkListStore *model)
 
   gtk_list_store_clear(model);
   guint i;
+
   for ( i = 0; i < levels->len; i++)
     {
       Level level = g_array_index (levels, Level, i);
       gtk_list_store_append(model, &iter);
       gtk_list_store_set(model, &iter,
                         LEVEL_COLUMN, level.level,
-                        ANSWER_COLUMN, level.answers,
-                        QUESTION_COLUMN, level.questions,
+                        ANSWER_COLUMN, list_to_string(level.answers),
+                        QUESTION_COLUMN, list_to_string(level.questions),
                         -1);
     }
 }
@@ -881,18 +1034,11 @@ clear_levels()
   if ( ! levels )
     return;
 
-  gint i = 0;
-  for (i = 0; i < levels->len; i++)
-    {
-      Level level = g_array_index (levels, Level, i);
-      g_free(level.answers);
-      g_free(level.questions);
-    }
-
   g_array_free(levels, TRUE);
   levels = NULL;
 }
 
+
 static gboolean
 valid_entry(Level *level)
 {
@@ -900,63 +1046,67 @@ valid_entry(Level *level)
   gchar *error;
   GtkWidget *dialog;
 
-  g_assert(level->questions);
-  g_assert(level->answers);
+  GSList *questionpointer = NULL;
+  GSList *answerpointer = NULL;
 
-  if ( strlen(level->questions) == 0 )
-    {
+  if ((level->questions == NULL)
+          || ((level->questions)->data == NULL)
+          || g_strcmp0 ("",(level->questions)->data)==0
+          || g_slist_length(level->questions) < 1)
+  {
       error = g_strdup (_("Questions cannot be empty.") );
       goto error;
-    }
-
-  /* Now check all chars in questions are in answers */
-  guint n_answers = g_utf8_strlen (level->answers, -1);
-  guint n_questions = g_utf8_strlen (level->questions, -1);
+  }
 
-  if ( n_answers == 0 )
-    {
-      error = g_strdup( _("Answers cannot be empty.") );
+  if (((level->answers) == NULL)
+          || ((level->answers)->data == NULL)
+          || g_strcmp0 ("",(level->answers)->data)==0
+          || g_slist_length(level->answers) < 1)
+  {
+      error = g_strdup (_("Answers cannot be empty.") );
       goto error;
-    }
+  }
 
-  if ( n_answers > MAX_N_ANSWER )
-    {
-      error = g_strdup_printf( _("Too many characters in the Answer (maximum is %d)."),
+  if ( g_slist_length(level->answers) > MAX_N_ANSWER )
+  {
+      error = g_strdup_printf(_("Too many characters in the Answer (maximum is %d)."),
                                 MAX_N_ANSWER );
       goto error;
-    }
-
-  /* The shuffle is not important, we are using it to get an array of chars */
-  gchar **answers = shuffle_utf8(level->answers);
-  gchar **questions = shuffle_utf8(level->questions);
+  }
 
-  guint a;
-  guint q;
-  for ( q = 0; q < n_questions; q++)
-    {
+  /* Now check all chars in questions are in answers */
+  questionpointer = level->questions;
+  do {
+      gchar *question = (gchar *) questionpointer->data;
       gboolean found = FALSE;
-      for ( a = 0; a < n_answers; a++)
-       {
-         if ( strcmp( answers[a], questions[q] ) == 0 )
-           {
+      answerpointer = level->answers;
+      do {
+          gchar *answer = (gchar *) answerpointer->data;
+         if ( strcmp( answer, question ) == 0 )
              found = TRUE;
-             break;
-           }
-       }
-      if ( ! found )
-       {
-         error = g_strdup ( _("All the characters in Questions must also be in the Answers.") );
-         g_strfreev(questions);
-         g_strfreev(answers);
+      } while (!found && (answerpointer = g_slist_next(answerpointer)));
+      if (! found )
+      {
+         error = g_strdup (_("All the characters in Questions must also be in the Answers.") );
          goto error;
-       }
-    }
-
-  g_strfreev(questions);
-  g_strfreev(answers);
+      }
+  } while ((questionpointer = g_slist_next(questionpointer)));
+  g_slist_free (questionpointer);
+  g_slist_free (answerpointer);
   return TRUE;
 
  error:
+  ;
+ gchar *questions = "";
+ gchar *answers = "";
+ if(((level->questions != NULL) && ((level->questions)->data != NULL)))
+ {
+     questions = list_to_string(level->questions);
+ }
+ if(((level->answers != NULL) && ((level->answers)->data != NULL)))
+ {
+     answers = list_to_string(level->answers);
+ }
   dialog = \
     gtk_message_dialog_new (NULL,
                            GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -964,15 +1114,23 @@ valid_entry(Level *level)
                            GTK_BUTTONS_CLOSE,
                            _("Invalid entry:\n"
                              "At level %d, Questions '%s' / Answers '%s'\n%s"),
-                           level->level, level->questions, level->answers,
+                           level->level, questions, answers,
                            error);
   gtk_dialog_run (GTK_DIALOG (dialog));
   gtk_widget_destroy (dialog);
   g_free(error);
-
+  if(questionpointer)
+        g_slist_free (questionpointer);
+  if(answerpointer)
+        g_slist_free (answerpointer);
   return result;
 }
 
+/*
+ * Used to check questions and answers before applying new config.
+ * Todo: bug with apply: if cell is still in edit mode, new value is ignored.
+ * Can this be fixed or is it a GTK Model thing?
+ */
 static gboolean
 _check_errors(GtkTreeModel *model, GtkTreePath *path,
              GtkTreeIter *iter, gpointer data)
@@ -980,20 +1138,38 @@ _check_errors(GtkTreeModel *model, GtkTreePath *path,
   Level level;
   gboolean *has_error = (gboolean*)data;
 
-  gtk_tree_model_get (model, iter,
+  gchar *answers = "";
+  gchar *questions = "";
+
+  gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
                       LEVEL_COLUMN, &level.level,
-                      QUESTION_COLUMN, &level.questions,
-                      ANSWER_COLUMN, &level.answers,
+                      ANSWER_COLUMN, &answers,
+                      QUESTION_COLUMN, &questions,
                       -1);
 
-  if( ! valid_entry( &level ) )
-    {
-      *has_error = TRUE;
-      /* Don't check more errors */
-      return TRUE;
-    }
+  if(g_utf8_strlen(questions,-1))
+      level.questions = string_to_list(questions);
+  else
+  {
+      level.questions = NULL;
+      level.questions = g_slist_append(level.questions,"");
+  }
+  if(g_utf8_strlen(answers,-1) )
+      level.answers = string_to_list(answers);
+  else
+  {
+      level.answers = NULL;
+      level.answers = g_slist_append(level.answers,"");
+  }
 
-  return FALSE;
+  if(!valid_entry( &level ) )
+  {
+      *has_error = TRUE;
+      // Don't check more errors
+  }
+  g_free(answers);
+  g_free(questions);
+  return *has_error;
 }
 
 
@@ -1005,10 +1181,21 @@ _check_errors(GtkTreeModel *model, GtkTreePath *path,
 gchar *get_user_desktop_file()
 {
   gchar **locale = g_strsplit_set(gc_locale_get(), ".", 2);
-  gchar *filename =
-    gc_file_find_absolute_writeable("%s/default-%s.desktop",
-                                   gcomprisBoard->boarddir,
-                                   locale[0]);
+  gchar *filename = "";
+  if(uppercase_only)
+  {
+      filename =
+            gc_file_find_absolute_writeable("%s/upper-%s.desktop",
+                                            gcomprisBoard->boarddir,
+                                            locale[0]);
+  }
+  else
+  {
+        filename =
+            gc_file_find_absolute_writeable("%s/default-%s.desktop",
+                                            gcomprisBoard->boarddir,
+                                            locale[0]);
+  }
   g_strfreev(locale);
   return filename;
 }
@@ -1029,6 +1216,7 @@ conf_ok(GHashTable *table)
   profile_conf = NULL;
 
   if (gcomprisBoard){
+
     GHashTable *config;
     if (profile_conf)
       config = gc_db_get_board_conf();
@@ -1036,13 +1224,13 @@ conf_ok(GHashTable *table)
       config = table;
 
     gboolean has_error = FALSE;
+
     gtk_tree_model_foreach(GTK_TREE_MODEL(model), _check_errors, &has_error);
 
     /* Tell the config not to close the dialog to let the user fix the issues */
     if (has_error)
       return FALSE;
 
-
     gc_locale_set(g_hash_table_lookup(config, "locale_sound"));
 
     if (profile_conf)
@@ -1220,7 +1408,6 @@ return_to_default(GtkWidget *widget, gpointer data)
 
 }
 
-
 static void cell_edited_callback (GtkCellRendererText *cell,
                           gchar               *path,
                           gchar               *new_text,
@@ -1251,25 +1438,27 @@ static void configure_colummns(GtkTreeView *treeview)
                                                    NULL);
   gtk_tree_view_append_column(treeview, column);
 
-  /* Answer column */
+
+  /* Question column */
   renderer = gtk_cell_renderer_text_new();
   g_object_set(renderer, "editable", TRUE, NULL);
-  g_object_set_data(G_OBJECT(renderer), "my_column_num",  GUINT_TO_POINTER(ANSWER_COLUMN) );
+  g_object_set_data(G_OBJECT(renderer), "my_column_num",  GUINT_TO_POINTER(QUESTION_COLUMN) );
   g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, treeview);
-  column = gtk_tree_view_column_new_with_attributes(_("Answer"),
+  column = gtk_tree_view_column_new_with_attributes(_("Question"),
                                                     renderer,
-                                                   "text", ANSWER_COLUMN,
+                                                   "text", QUESTION_COLUMN,
                                                    NULL);
   gtk_tree_view_append_column(treeview, column);
 
-  /* Question column */
+
+  /* Answer column */
   renderer = gtk_cell_renderer_text_new();
   g_object_set(renderer, "editable", TRUE, NULL);
-  g_object_set_data(G_OBJECT(renderer), "my_column_num",  GUINT_TO_POINTER(QUESTION_COLUMN) );
+  g_object_set_data(G_OBJECT(renderer), "my_column_num",  GUINT_TO_POINTER(ANSWER_COLUMN) );
   g_signal_connect(renderer, "edited", (GCallback) cell_edited_callback, treeview);
-  column = gtk_tree_view_column_new_with_attributes(_("Question"),
+  column = gtk_tree_view_column_new_with_attributes(_("Answer"),
                                                     renderer,
-                                                   "text", QUESTION_COLUMN,
+                                                   "text", ANSWER_COLUMN,
                                                    NULL);
   gtk_tree_view_append_column(treeview, column);
 
@@ -1312,8 +1501,12 @@ config_start(GcomprisBoard *agcomprisBoard,
 
   gc_locale_set( NULL );
 
-  gchar *label = g_strdup_printf(_("<b>%1$s</b> configuration\n for profile <b>%2$s</b>"),
-                                agcomprisBoard->name,
+  /*
+   * TRANSLATORS: %1$s is the board name (click_on_letter),
+   * 2$s is the name of the current user profile
+   */
+  gchar *label = g_strdup_printf(C_("click_on_letter_config","<b>%1$s</b> configuration\n for profile 
<b>%2$s</b>"),
+                                _(agcomprisBoard->name),
                                 aProfile ? aProfile->name : "");
   GcomprisBoardConf *bconf;
   bconf = gc_board_config_window_display(label, conf_ok);
@@ -1325,7 +1518,7 @@ config_start(GcomprisBoard *agcomprisBoard,
 
   gchar *saved_locale_sound = g_hash_table_lookup( config, "locale_sound");
 
-  gc_board_config_combo_locales_asset(bconf, "Select sound locale", saved_locale_sound,
+  gc_board_config_combo_locales_asset(bconf, _("Choose a language"), saved_locale_sound,
                                      "voices/$LOCALE/colors/purple.ogg",
                                      G_CALLBACK (locale_changed));
 
@@ -1366,7 +1559,6 @@ config_start(GcomprisBoard *agcomprisBoard,
   gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
 
   model = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(treeview)));
-
   load_model_from_levels(model);
 
   /* some buttons */
@@ -1424,5 +1616,5 @@ config_stop()
 static void
 sound_played (gchar *file)
 {
-  g_warning ("Sound_played %s\n", file);
+  g_message ("Sound_played %s\n", file);
 }
diff --git a/src/click_on_letter-activity/resources/click_on_letter/default-en.desktop 
b/src/click_on_letter-activity/resources/click_on_letter/default-en.desktop
index 12b7566..f79fa9a 100644
--- a/src/click_on_letter-activity/resources/click_on_letter/default-en.desktop
+++ b/src/click_on_letter-activity/resources/click_on_letter/default-en.desktop
@@ -5,49 +5,50 @@
 
 # Question is a list of letters that will be asked to search for
 # Answer is a list of possible answers the children will have to choose from
+# Questions and answers must be separated by single whitespaces
 # WARNING: It is up to you to put at least all the Question letters in the Answer.
 #          You can also add some confusing letters in the Answer section
 # WARNING: Answer must have at max 18 characters
 [1]
-Questions=aeiouy
-Answers=aeiouy
+Questions=a e i o u y
+Answers=a e i o u y
 
 [2]
-Questions=aeiouy
-Answers=aeiouycs
+Questions=a e i o u y
+Answers=a e i o u y c s
 
 [3]
-Questions=aeiouy
-Answers=aeiouycksvxz
+Questions=a e i o u y
+Answers=a e i o u y c k s v x z
 
 [4]
-Questions=ckpsvxwz
-Answers=ckpsvxwz
+Questions=c k p s v x w z
+Answers=c k p s v x w z
 
 [5]
-Questions=bfglmnqt
-Answers=bfglmnqt
+Questions=b f g l m n q t
+Answers=b f g l m n q t
 
 [6]
-Questions=bdgqpnmu
-Answers=bdgqpnmu
+Questions=b d g q p n m u
+Answers=b d g q p n m u
 
 [7]
-Questions=ilthwvae
-Answers=ilthwvae
+Questions=i l t h w v a e
+Answers=i l t h w v a e
 
 [8]
-Questions=abcdefgh
-Answers=abcdefgh
+Questions=a b c d e f g h
+Answers=a b c d e f g h
 
 [9]
-Questions=ijklmnop
-Answers=ijklmnop
+Questions=i j k l m n o p
+Answers=i j k l m n o p
 
 [10]
-Questions=qrstuvwxyz
-Answers=qrstuvwxyz
+Questions=q r s t u v w x y z
+Answers=q r s t u v w x y z
 
 [11]
-Questions=bcdfghjklmnpqrstvwxz
-Answers=bcdfghjklmnpqrstvwxz
+Questions=b c d f g h j k l m n p q r s t v w x z
+Answers=b c d f g h j k l m n p q r s t v w x z



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