Back to index

gcompris  8.2.2
click_on_letter.c
Go to the documentation of this file.
00001 /* gcompris - click_on_letter.c
00002  *
00003  * Copyright (C) 2001 Pascal Georges
00004  *
00005  *   This program is free software; you can redistribute it and/or modify
00006  *   it under the terms of the GNU General Public License as published by
00007  *   the Free Software Foundation; either version 2 of the License, or
00008  *   (at your option) any later version.
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details.
00014  *
00015  *   You should have received a copy of the GNU General Public License
00016  *   along with this program; if not, write to the Free Software
00017  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include <glib.h>
00021 #include <string.h>
00022 
00023 #include "gcompris/gcompris.h"
00024 
00025 
00026 
00027 #define SOUNDLISTFILE PACKAGE
00028 
00029 static GcomprisBoard *gcomprisBoard = NULL;
00030 static gboolean board_paused;
00031 
00032 static void           start_board (GcomprisBoard *agcomprisBoard);
00033 static void           pause_board (gboolean pause);
00034 static void           end_board (void);
00035 static gboolean              is_our_board (GcomprisBoard *gcomprisBoard);
00036 static void           set_level (guint level);
00037 
00038 static int gamewon;
00039 
00040 static void           process_ok(void);
00041 static void           highlight_selected(GnomeCanvasItem *);
00042 static void           game_won(void);
00043 static void           repeat(void);
00044 static void           config_start(GcomprisBoard *agcomprisBoard,
00045                                   GcomprisProfile *aProfile);
00046 static void           config_stop(void);
00047 
00048 
00049 #define VERTICAL_SEPARATION 408
00050 #define HORIZONTAL_SEPARATION 0
00051 
00052 #define NUMBER_OF_SUBLEVELS 3
00053 #define NUMBER_OF_LEVELS 5
00054 #define MAX_NUMBER_OF_LETTERS 4
00055 
00056 #define NOT_OK              0
00057 #define OK           1
00058 #define OK_NO_INIT   2
00059 
00060 #define TEXT_COLOR "white"
00061 
00062 static GnomeCanvasGroup *boardRootItem = NULL;
00063 
00064 static GnomeCanvasItem *l_items[MAX_NUMBER_OF_LETTERS];
00065 static GnomeCanvasItem *buttons[MAX_NUMBER_OF_LETTERS];
00066 static GnomeCanvasItem *selected_button = NULL;
00067 
00068 static GnomeCanvasItem *click_on_letter_create_item(GnomeCanvasGroup *parent);
00069 
00070 static void click_on_letter_destroy_all_items(void);
00071 static void click_on_letter_next_level(void);
00072 static gint item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
00073 static gboolean sounds_are_fine();
00074 
00075 static int right_position;
00076 static int number_of_letters=MAX_NUMBER_OF_LETTERS;
00077 static gchar *right_letter;
00078 
00079 static gchar *alphabet;
00080 
00081 static void sound_played(gchar *file);
00082 
00083 static gboolean uppercase_only;
00084 
00085 /* Description of this plugin */
00086 static BoardPlugin menu_bp =
00087   {
00088     NULL,
00089     NULL,
00090     "Read a letter",
00091     "Learn to recognize letters",
00092     "Pascal Georges pascal.georges1@free.fr>",
00093     NULL,
00094     NULL,
00095     NULL,
00096     NULL,
00097     start_board,
00098     pause_board,
00099     end_board,
00100     is_our_board,
00101     NULL,
00102     NULL,
00103     set_level,
00104     NULL,
00105     repeat,
00106     config_start,
00107     config_stop
00108   };
00109 
00110 /*
00111  * Main entry point mandatory for each Gcompris's game
00112  * ---------------------------------------------------
00113  *
00114  */
00115 
00116 GET_BPLUGIN_INFO(click_on_letter)
00117 
00118 /*
00119  * in : boolean TRUE = PAUSE : FALSE = CONTINUE
00120  *
00121  */
00122      static void pause_board (gboolean pause)
00123 {
00124   if(gcomprisBoard==NULL)
00125     return;
00126 
00127   if(gamewon == TRUE && pause == FALSE) /* the game is won */
00128     {
00129       game_won();
00130     }
00131 
00132   board_paused = pause;
00133 }
00134 
00135 /*
00136  */
00137 static void start_board (GcomprisBoard *agcomprisBoard)
00138 {
00139   GHashTable *config = gc_db_get_board_conf();
00140   int ready;
00141 
00142   board_paused = TRUE;
00143 
00144   gc_locale_set(g_hash_table_lookup( config, "locale_sound"));
00145 
00146   gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
00147 
00148   if (up_init_str && (strcmp(up_init_str, "True")==0))
00149     uppercase_only = TRUE;
00150   else
00151     uppercase_only = FALSE;
00152 
00153   g_hash_table_destroy(config);
00154 
00155   ready = sounds_are_fine();
00156 
00157   gc_sound_pause();
00158 
00159   if (agcomprisBoard!=NULL)
00160     {
00161       gcomprisBoard=agcomprisBoard;
00162       gc_set_background(gnome_canvas_root(gcomprisBoard->canvas),
00163                            "images/scenery4_background.png");
00164       gcomprisBoard->level=1;
00165       gcomprisBoard->maxlevel=NUMBER_OF_LEVELS;
00166       gcomprisBoard->sublevel=1;
00167 
00168       /* Go to next level after this number of 'play' */
00169       gcomprisBoard->number_of_sublevel=NUMBER_OF_SUBLEVELS;
00170 
00171       if(ready)
00172        {
00173          /* Warning, bar buttons are set in click_on_letter_next_level()
00174             to avoid them to appear in the case a dialog is displayed */
00175          gc_score_start(SCORESTYLE_NOTE,
00176                             50,
00177                             50,
00178                             gcomprisBoard->number_of_sublevel);
00179 
00180          if(ready == OK)
00181            click_on_letter_next_level();
00182 
00183          gamewon = FALSE;
00184          pause_board(FALSE);
00185        }
00186     }
00187 
00188 }
00189 /* ======================================= */
00190 static void end_board ()
00191 {
00192   if(gcomprisBoard!=NULL)
00193     {
00194       pause_board(TRUE);
00195       gc_score_end();
00196       click_on_letter_destroy_all_items();
00197     }
00198   gc_locale_reset();
00199   gcomprisBoard = NULL;
00200   gc_sound_resume();
00201 }
00202 
00203 /* ======================================= */
00204 static void set_level (guint level)
00205 {
00206 
00207   if(gcomprisBoard!=NULL)
00208     {
00209       gcomprisBoard->level=level;
00210       gcomprisBoard->sublevel=1;
00211       click_on_letter_next_level();
00212     }
00213 }
00214 /* ======================================= */
00215 static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
00216 {
00217   if (gcomprisBoard)
00218     {
00219       if(g_strcasecmp(gcomprisBoard->type, "click_on_letter")==0)
00220        {
00221          /* Set the plugin entry */
00222          gcomprisBoard->plugin=&menu_bp;
00223 
00224          return TRUE;
00225        }
00226     }
00227   return FALSE;
00228 }
00229 
00230 /* ======================================= */
00231 static void repeat ()
00232 {
00233   if(gcomprisBoard!=NULL)
00234     {
00235       gchar *str1 = NULL;
00236       gchar *right_letter_ogg = NULL;
00237 
00238       str1 = gc_sound_alphabet(right_letter);
00239 
00240       right_letter_ogg = g_strdup_printf("sounds/$LOCALE/alphabet/%s", str1);
00241       g_free(str1);
00242 
00243       if(right_letter_ogg) {
00244        gc_sound_play_ogg_cb(right_letter_ogg, sound_played);
00245       }
00246 
00247       g_free(right_letter_ogg);
00248     }
00249 }
00250 
00251 static gboolean sounds_are_fine()
00252 {
00253   char *letter_str;
00254   char *str2;
00255   GcomprisProperties *properties = gc_prop_get();
00256 
00257   if(!properties->fx)
00258     {
00259       gc_dialog(_("Error: this activity cannot be played with the\nsound effects disabled.\nGo to the configuration dialog to\nenable the sound"), gc_board_stop);
00260       return(NOT_OK);
00261     }
00262 
00263   /* TRANSLATORS: Put here the alphabet in your language */
00264   alphabet=_("abcdefghijklmnopqrstuvwxyz");
00265   g_assert(g_utf8_validate(alphabet, -1, NULL)); // require by all utf8-functions
00266 
00267   gchar *letter = g_new0(gchar, 8);
00268   g_unichar_to_utf8(g_utf8_get_char(alphabet), letter);
00269   letter_str = gc_sound_alphabet(letter);
00270   g_free(letter);
00271 
00272   str2 = gc_file_find_absolute("sounds/$LOCALE/alphabet/%s", letter_str);
00273 
00274   if (!str2)
00275     {
00276       gchar *locale = NULL;
00277 
00278       locale = g_strndup(gc_locale_get(), 2);
00279       gc_locale_reset();
00280       gc_locale_set("en_US");
00281 
00282       str2 = gc_file_find_absolute("sounds/en/alphabet/%s", letter_str);
00283 
00284       if (!str2)
00285        {
00286          gchar *msg = g_strdup_printf( _("Error: this activity requires that you first install\nthe packages with gcompris voices for the locale '%s' or '%s'"),
00287                                    locale, "en");
00288          gc_dialog(msg, gc_board_stop);
00289          g_free(msg);
00290          return (NOT_OK);
00291        }
00292       else
00293        {
00294          gchar *msg = g_strdup_printf( _("Error: this activity requires that you first install\nthe packages with GCompris voices for the locale '%s' ! Fallback to english, sorry!"), locale);
00295          gc_dialog(msg, click_on_letter_next_level);
00296          g_free(msg);
00297          g_free(str2);
00298          return(OK_NO_INIT);
00299        }
00300     }
00301   else
00302     {
00303       g_free(str2);
00304     }
00305 
00306   return(OK);
00307 }
00308 
00309 /*-------------------------------------------------------------------------------*/
00310 /*-------------------------------------------------------------------------------*/
00311 /* set initial values for the next level */
00312 static void
00313 click_on_letter_next_level()
00314 {
00315   /* It must be set it for the warning dialogs */
00316   gc_bar_set(GC_BAR_CONFIG|GC_BAR_LEVEL|GC_BAR_REPEAT);
00317 
00318   gc_bar_set_level(gcomprisBoard);
00319 
00320   click_on_letter_destroy_all_items();
00321   gamewon = FALSE;
00322   selected_button = NULL;
00323   gc_score_set(gcomprisBoard->sublevel);
00324   g_free (right_letter);
00325   /* Try the next level */
00326   gc_sound_play_ogg("sounds/$LOCALE/misc/click_on_letter.ogg", NULL);
00327   click_on_letter_create_item(gnome_canvas_root(gcomprisBoard->canvas));
00328 }
00329 /* ==================================== */
00330 /* Destroy all the items */
00331 static void click_on_letter_destroy_all_items()
00332 {
00333   if(boardRootItem!=NULL)
00334     gtk_object_destroy (GTK_OBJECT(boardRootItem));
00335 
00336   boardRootItem = NULL;
00337 }
00338 /* ==================================== */
00339 static GnomeCanvasItem *click_on_letter_create_item(GnomeCanvasGroup *parent)
00340 {
00341 
00342   int xOffset,yOffset,i,j;
00343   GdkPixbuf *button_pixmap = NULL;
00344 
00345   int length_of_aphabet=g_utf8_strlen (alphabet,-1);
00346 
00347   number_of_letters=gcomprisBoard->level+1;
00348   if (number_of_letters>MAX_NUMBER_OF_LETTERS) number_of_letters=MAX_NUMBER_OF_LETTERS;
00349 
00350   int numbers[number_of_letters];
00351 
00352   gchar *letters[number_of_letters];
00353   g_assert(number_of_letters<=length_of_aphabet); // because we must set unique letter on every "vagon"
00354 
00355   for (i=0;i<number_of_letters;i++){
00356     numbers[i]=((int)(((float)length_of_aphabet)*rand()/(RAND_MAX+1.0)));
00357 
00358     // check that the letter has not been taken yet
00359     for(j=0;j<i;j++){
00360       if (numbers[i]==numbers[j]) {
00361        i--;
00362        continue;
00363       }
00364     }
00365 
00366   }
00367 
00368   for (i=0;i<number_of_letters;i++){
00369     gchar *copy_from=g_utf8_offset_to_pointer(alphabet, numbers[i]);
00370     gchar *copy_to=g_utf8_offset_to_pointer(alphabet, numbers[i]+1);
00371     letters[i]=g_strndup(copy_from,copy_to-copy_from);
00372 
00373     if (uppercase_only)
00374       letters[i]=g_utf8_strup(copy_from,copy_to-copy_from);
00375     else {
00376       switch (gcomprisBoard->level) {
00377       case 1  :
00378       case 2  : letters[i]=g_strndup(copy_from,copy_to-copy_from); break;
00379       case 3  : letters[i]=g_utf8_strup(copy_from,copy_to-copy_from); break;
00380       default :
00381        if ( rand() > (RAND_MAX/2) )
00382          letters[i]=g_strndup(copy_from,copy_to-copy_from);
00383        else
00384          letters[i]=g_utf8_strup(copy_from,copy_to-copy_from);
00385       }
00386     }
00387   }
00388 
00389   /*  */
00390   right_position = ((int)(((float)number_of_letters)*rand()/(RAND_MAX+1.0)));
00391   g_assert(right_position >= 0  && right_position < number_of_letters);
00392   right_letter = g_utf8_strdown(letters[right_position],-1);
00393 
00394   repeat();
00395 
00396 
00397   boardRootItem = GNOME_CANVAS_GROUP(
00398                                  gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
00399                                                      gnome_canvas_group_get_type (),
00400                                                      "x", (double) 0,
00401                                                      "y", (double) 0,
00402                                                      NULL));
00403 
00404 
00405   button_pixmap = gc_pixmap_load("images/wagon-yellow.png");
00406 
00407   yOffset = VERTICAL_SEPARATION;
00408   xOffset = 5;
00409 
00410 
00411   for (i=0; i< number_of_letters; i++) {
00412     buttons[i] = gnome_canvas_item_new (boardRootItem,
00413                                    gnome_canvas_pixbuf_get_type (),
00414                                    "pixbuf",  button_pixmap,
00415                                    "x",  (double) xOffset,
00416                                    "y",  (double) yOffset,
00417                                    NULL);
00418 
00419 
00420     l_items[i] = gnome_canvas_item_new (boardRootItem,
00421                                    gnome_canvas_text_get_type (),
00422                                    "text", letters[i],
00423                                    "font", gc_skin_font_board_huge_bold,
00424                                    "anchor", GTK_ANCHOR_CENTER,
00425                                    "fill_color_rgba", 0x0000ffff,
00426                                    "x",  (double) xOffset + gdk_pixbuf_get_width(button_pixmap)/2,
00427                                    "y",  (double) yOffset + gdk_pixbuf_get_height(button_pixmap)/2 - 5,
00428                                    NULL);
00429 
00430     g_free(letters[i]);
00431     xOffset +=HORIZONTAL_SEPARATION +gdk_pixbuf_get_width(button_pixmap);
00432 
00433     gtk_signal_connect(GTK_OBJECT(l_items[i]), "event", (GtkSignalFunc) item_event, GINT_TO_POINTER(i));
00434     gtk_signal_connect(GTK_OBJECT(buttons[i]), "event",  (GtkSignalFunc) item_event, GINT_TO_POINTER(i));
00435     //  gtk_signal_connect(GTK_OBJECT(buttons[i]), "event", (GtkSignalFunc) gc_item_focus_event, NULL);
00436   }
00437 
00438 
00439   gdk_pixbuf_unref(button_pixmap);
00440 
00441   return NULL;
00442 }
00443 /* ==================================== */
00444 static void game_won()
00445 {
00446   gcomprisBoard->sublevel++;
00447 
00448   if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
00449     /* Try the next level */
00450     gcomprisBoard->sublevel=1;
00451     gcomprisBoard->level++;
00452     if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
00453       gc_bonus_end_display(BOARD_FINISHED_TUXPLANE);
00454       return;
00455     }
00456   }
00457   click_on_letter_next_level();
00458 }
00459 
00460 /* ==================================== */
00461 static gboolean process_ok_timeout() {
00462   gc_bonus_display(gamewon, BONUS_FLOWER);
00463   return FALSE;
00464 }
00465 
00466 static void process_ok() {
00467   // leave time to display the right answer
00468   g_timeout_add(TIME_CLICK_TO_BONUS, process_ok_timeout, NULL);
00469 }
00470 /* ==================================== */
00471 static gint
00472 item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
00473 {
00474   int pos = GPOINTER_TO_INT(data);
00475   double item_x, item_y;
00476   item_x = event->button.x;
00477   item_y = event->button.y;
00478   gnome_canvas_item_w2i(item->parent, &item_x, &item_y);
00479 
00480   if(board_paused)
00481     return FALSE;
00482 
00483   switch (event->type)
00484     {
00485     case GDK_BUTTON_PRESS:
00486       /* We really don't want the user to change his/her mind */
00487       board_paused = TRUE;
00488 
00489       if ( pos == right_position ) {
00490        gamewon = TRUE;
00491       } else {
00492        gamewon = FALSE;
00493       }
00494       highlight_selected(item);
00495       process_ok();
00496       break;
00497 
00498     default:
00499       break;
00500     }
00501   return FALSE;
00502 }
00503 /* ==================================== */
00504 static void highlight_selected(GnomeCanvasItem * item) {
00505   GdkPixbuf *button_pixmap_selected = NULL, *button_pixmap = NULL;
00506   GnomeCanvasItem *button;
00507   int i;
00508 
00509 
00510   /* Replace text item by button item */
00511   button = item;
00512   for (i=0; i<number_of_letters;i++) {
00513     if ( l_items[i] == item ) {
00514       button = buttons[i];
00515     }
00516   }
00517 
00518   if (selected_button != NULL && selected_button != button) {
00519     button_pixmap = gc_pixmap_load("images/wagon-yellow.png");
00520     /* Warning changing the image needs to update pixbuf_ref for the focus usage */
00521     gc_item_focus_free(selected_button, NULL);
00522     gnome_canvas_item_set(selected_button, "pixbuf", button_pixmap, NULL);
00523     gdk_pixbuf_unref(button_pixmap);
00524   }
00525 
00526   if (selected_button != button) {
00527     button_pixmap_selected = gc_pixmap_load("images/wagon-green.png");
00528     /* Warning changing the image needs to update pixbuf_ref for the focus usage */
00529       gc_item_focus_free(button, NULL);
00530     gnome_canvas_item_set(button, "pixbuf", button_pixmap_selected, NULL);
00531     selected_button = button;
00532     gdk_pixbuf_unref(button_pixmap_selected);
00533   }
00534 
00535 }
00536 
00537 
00538 /* ************************************* */
00539 /* *            Configuration          * */
00540 /* ************************************* */
00541 
00542 
00543 /* ======================= */
00544 /* = config_start        = */
00545 /* ======================= */
00546 
00547 static GcomprisProfile *profile_conf;
00548 static GcomprisBoard   *board_conf;
00549 
00550 static GHFunc save_table (gpointer key,
00551                        gpointer value,
00552                        gpointer user_data)
00553 {
00554   gc_db_set_board_conf ( profile_conf,
00555                          board_conf,
00556                          (gchar *) key,
00557                          (gchar *) value);
00558 
00559   return NULL;
00560 }
00561 
00562 static void
00563 conf_ok(GHashTable *table)
00564 {
00565   if (!table){
00566     if (gcomprisBoard)
00567       pause_board(FALSE);
00568 
00569     return;
00570   }
00571 
00572 
00573   g_hash_table_foreach(table, (GHFunc) save_table, NULL);
00574 
00575   board_conf = NULL;
00576   profile_conf = NULL;
00577 
00578   if (gcomprisBoard){
00579     GHashTable *config;
00580     if (profile_conf)
00581       config = gc_db_get_board_conf();
00582     else
00583       config = table;
00584 
00585     gc_locale_reset();
00586     gc_locale_set(g_hash_table_lookup(config, "locale_sound"));
00587 
00588     gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
00589     if (up_init_str)
00590       {
00591        if(strcmp(up_init_str, "True")==0)
00592          uppercase_only = TRUE;
00593        else
00594          uppercase_only = FALSE;
00595       }
00596 
00597     if (profile_conf)
00598       g_hash_table_destroy(config);
00599 
00600     sounds_are_fine();
00601 
00602     click_on_letter_next_level();
00603 
00604     gamewon = FALSE;
00605     pause_board(FALSE);
00606 
00607   }
00608 
00609   board_conf = NULL;
00610   profile_conf = NULL;
00611 }
00612 
00613 static void
00614 config_start(GcomprisBoard *agcomprisBoard,
00615             GcomprisProfile *aProfile)
00616 {
00617   board_conf = agcomprisBoard;
00618   profile_conf = aProfile;
00619 
00620   if (gcomprisBoard)
00621     pause_board(TRUE);
00622 
00623   gchar *label = g_strdup_printf("<b>%s</b> configuration\n for profile <b>%s</b>",
00624                              agcomprisBoard->name,
00625                              aProfile ? aProfile->name : "");
00626 
00627   gc_board_config_window_display(label, conf_ok);
00628 
00629   g_free(label);
00630 
00631   /* init the combo to previously saved value */
00632   GHashTable *config = gc_db_get_conf( profile_conf, board_conf);
00633 
00634   gchar *saved_locale_sound = g_hash_table_lookup( config, "locale_sound");
00635 
00636   gc_board_config_combo_locales_asset( "Select sound locale", saved_locale_sound,
00637                             "sounds/$LOCALE/colors/purple.ogg");
00638 
00639   gboolean up_init = FALSE;
00640 
00641   gchar *up_init_str = g_hash_table_lookup( config, "uppercase_only");
00642 
00643   if (up_init_str && (strcmp(up_init_str, "True")==0))
00644     up_init = TRUE;
00645 
00646   gc_board_config_boolean_box(_("Uppercase only text"),
00647                      "uppercase_only",
00648                      up_init);
00649 
00650   g_hash_table_destroy(config);
00651 }
00652 
00653 
00654 /* ======================= */
00655 /* = config_stop        = */
00656 /* ======================= */
00657 static void
00658 config_stop()
00659 {
00660 }
00661 
00662 static void
00663 sound_played (gchar *file)
00664 {
00665   g_warning ("Sound_played %s\n", file);
00666 }